2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
28 #include "libavutil/random.h"
29 #include "libavutil/avstring.h"
30 #include "libavformat/avformat.h"
31 #include "libavformat/network.h"
32 #include "libavformat/os_support.h"
33 #include "libavformat/rtp.h"
34 #include "libavformat/rtsp.h"
35 #include "libavcodec/opt.h"
39 #include <sys/ioctl.h>
45 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
57 const char program_name[] = "FFserver";
58 const int program_birth_year = 2000;
60 static const OptionDef options[];
62 /* maximum number of simultaneous HTTP connections */
63 #define HTTP_MAX_CONNECTIONS 2000
66 HTTPSTATE_WAIT_REQUEST,
67 HTTPSTATE_SEND_HEADER,
68 HTTPSTATE_SEND_DATA_HEADER,
69 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
70 HTTPSTATE_SEND_DATA_TRAILER,
71 HTTPSTATE_RECEIVE_DATA,
72 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
75 RTSPSTATE_WAIT_REQUEST,
77 RTSPSTATE_SEND_PACKET,
80 static const char *http_state[] = {
96 #define IOBUFFER_INIT_SIZE 8192
98 /* timeouts are in ms */
99 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
100 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
102 #define SYNC_TIMEOUT (10 * 1000)
105 int64_t count1, count2;
106 int64_t time1, time2;
109 /* context associated with one connection */
110 typedef struct HTTPContext {
111 enum HTTPState state;
112 int fd; /* socket file descriptor */
113 struct sockaddr_in from_addr; /* origin */
114 struct pollfd *poll_entry; /* used when polling */
116 uint8_t *buffer_ptr, *buffer_end;
119 struct HTTPContext *next;
120 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
124 /* input format handling */
125 AVFormatContext *fmt_in;
126 int64_t start_time; /* In milliseconds - this wraps fairly often */
127 int64_t first_pts; /* initial pts value */
128 int64_t cur_pts; /* current pts value from the stream in us */
129 int64_t cur_frame_duration; /* duration of the current frame in us */
130 int cur_frame_bytes; /* output frame size, needed to compute
131 the time at which we send each
133 int pts_stream_index; /* stream we choose as clock reference */
134 int64_t cur_clock; /* current clock reference value in us */
135 /* output format handling */
136 struct FFStream *stream;
137 /* -1 is invalid stream */
138 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
139 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
141 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
142 int last_packet_sent; /* true if last data packet was sent */
144 DataRateData datarate;
151 int is_packetized; /* if true, the stream is packetized */
152 int packet_stream_index; /* current stream for output in state machine */
154 /* RTSP state specific */
155 uint8_t *pb_buffer; /* XXX: use that in all the code */
157 int seq; /* RTSP sequence number */
159 /* RTP state specific */
160 enum RTSPProtocol rtp_protocol;
161 char session_id[32]; /* session id */
162 AVFormatContext *rtp_ctx[MAX_STREAMS];
164 /* RTP/UDP specific */
165 URLContext *rtp_handles[MAX_STREAMS];
167 /* RTP/TCP specific */
168 struct HTTPContext *rtsp_c;
169 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
172 /* each generated stream is described here */
176 STREAM_TYPE_REDIRECT,
179 enum IPAddressAction {
184 typedef struct IPAddressACL {
185 struct IPAddressACL *next;
186 enum IPAddressAction action;
187 /* These are in host order */
188 struct in_addr first;
192 /* description of each stream of the ffserver.conf file */
193 typedef struct FFStream {
194 enum StreamType stream_type;
195 char filename[1024]; /* stream filename */
196 struct FFStream *feed; /* feed we are using (can be null if
198 AVFormatParameters *ap_in; /* input parameters */
199 AVInputFormat *ifmt; /* if non NULL, force input format */
203 int prebuffer; /* Number of millseconds early to start */
204 int64_t max_time; /* Number of milliseconds to run */
206 AVStream *streams[MAX_STREAMS];
207 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
208 char feed_filename[1024]; /* file name of the feed storage, or
209 input file name for a stream */
214 pid_t pid; /* Of ffmpeg process */
215 time_t pid_start; /* Of ffmpeg process */
217 struct FFStream *next;
218 unsigned bandwidth; /* bandwidth, in kbits/s */
221 /* multicast specific */
223 struct in_addr multicast_ip;
224 int multicast_port; /* first port used for multicast */
226 int loop; /* if true, send the stream in loops (only meaningful if file) */
229 int feed_opened; /* true if someone is writing to the feed */
230 int is_feed; /* true if it is a feed */
231 int readonly; /* True if writing is prohibited to the file */
233 int64_t bytes_served;
234 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
235 int64_t feed_write_index; /* current write position in feed (it wraps around) */
236 int64_t feed_size; /* current size of feed */
237 struct FFStream *next_feed;
240 typedef struct FeedData {
241 long long data_count;
242 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
245 static struct sockaddr_in my_http_addr;
246 static struct sockaddr_in my_rtsp_addr;
248 static char logfilename[1024];
249 static HTTPContext *first_http_ctx;
250 static FFStream *first_feed; /* contains only feeds */
251 static FFStream *first_stream; /* contains all streams, including feeds */
253 static void new_connection(int server_fd, int is_rtsp);
254 static void close_connection(HTTPContext *c);
257 static int handle_connection(HTTPContext *c);
258 static int http_parse_request(HTTPContext *c);
259 static int http_send_data(HTTPContext *c);
260 static void compute_status(HTTPContext *c);
261 static int open_input_stream(HTTPContext *c, const char *info);
262 static int http_start_receive_data(HTTPContext *c);
263 static int http_receive_data(HTTPContext *c);
266 static int rtsp_parse_request(HTTPContext *c);
267 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
268 static void rtsp_cmd_options(HTTPContext *c, const char *url);
269 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
270 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
271 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
275 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
276 struct in_addr my_ip);
279 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
280 FFStream *stream, const char *session_id,
281 enum RTSPProtocol rtp_protocol);
282 static int rtp_new_av_stream(HTTPContext *c,
283 int stream_index, struct sockaddr_in *dest_addr,
284 HTTPContext *rtsp_c);
286 static const char *my_program_name;
287 static const char *my_program_dir;
289 static const char *config_filename;
290 static int ffserver_debug;
291 static int ffserver_daemon;
292 static int no_launch;
293 static int need_to_start_children;
295 static int nb_max_connections = 5;
296 static int nb_connections;
298 static uint64_t max_bandwidth = 1000;
299 static uint64_t current_bandwidth;
301 static int64_t cur_time; // Making this global saves on passing it around everywhere
303 static AVRandomState random_state;
305 static FILE *logfile = NULL;
307 static char *ctime1(char *buf2)
315 p = buf2 + strlen(p) - 1;
321 static void http_vlog(const char *fmt, va_list vargs)
323 static int print_prefix = 1;
328 fprintf(logfile, "%s ", buf);
330 print_prefix = strstr(fmt, "\n") != NULL;
331 vfprintf(logfile, fmt, vargs);
336 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
339 va_start(vargs, fmt);
340 http_vlog(fmt, vargs);
344 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
346 static int print_prefix = 1;
347 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
348 if (level > av_log_level)
350 if (print_prefix && avc)
351 http_log("[%s @ %p]", avc->item_name(ptr), avc);
352 print_prefix = strstr(fmt, "\n") != NULL;
353 http_vlog(fmt, vargs);
356 static void log_connection(HTTPContext *c)
361 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
362 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
363 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
366 static void update_datarate(DataRateData *drd, int64_t count)
368 if (!drd->time1 && !drd->count1) {
369 drd->time1 = drd->time2 = cur_time;
370 drd->count1 = drd->count2 = count;
371 } else if (cur_time - drd->time2 > 5000) {
372 drd->time1 = drd->time2;
373 drd->count1 = drd->count2;
374 drd->time2 = cur_time;
379 /* In bytes per second */
380 static int compute_datarate(DataRateData *drd, int64_t count)
382 if (cur_time == drd->time1)
385 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
389 static void start_children(FFStream *feed)
394 for (; feed; feed = feed->next) {
395 if (feed->child_argv && !feed->pid) {
396 feed->pid_start = time(0);
401 http_log("Unable to create children\n");
410 for (i = 3; i < 256; i++)
413 if (!ffserver_debug) {
414 i = open("/dev/null", O_RDWR);
423 av_strlcpy(pathname, my_program_name, sizeof(pathname));
425 slash = strrchr(pathname, '/');
430 strcpy(slash, "ffmpeg");
432 /* This is needed to make relative pathnames work */
433 chdir(my_program_dir);
435 signal(SIGPIPE, SIG_DFL);
437 execvp(pathname, feed->child_argv);
445 /* open a listening socket */
446 static int socket_open_listen(struct sockaddr_in *my_addr)
450 server_fd = socket(AF_INET,SOCK_STREAM,0);
457 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
459 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
461 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
463 closesocket(server_fd);
467 if (listen (server_fd, 5) < 0) {
469 closesocket(server_fd);
472 ff_socket_nonblock(server_fd, 1);
477 /* start all multicast streams */
478 static void start_multicast(void)
483 struct sockaddr_in dest_addr;
484 int default_port, stream_index;
487 for(stream = first_stream; stream != NULL; stream = stream->next) {
488 if (stream->is_multicast) {
489 /* open the RTP connection */
490 snprintf(session_id, sizeof(session_id), "%08x%08x",
491 av_random(&random_state), av_random(&random_state));
493 /* choose a port if none given */
494 if (stream->multicast_port == 0) {
495 stream->multicast_port = default_port;
499 dest_addr.sin_family = AF_INET;
500 dest_addr.sin_addr = stream->multicast_ip;
501 dest_addr.sin_port = htons(stream->multicast_port);
503 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
504 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
508 if (open_input_stream(rtp_c, "") < 0) {
509 http_log("Could not open input stream for stream '%s'\n",
514 /* open each RTP stream */
515 for(stream_index = 0; stream_index < stream->nb_streams;
517 dest_addr.sin_port = htons(stream->multicast_port +
519 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
520 http_log("Could not open output stream '%s/streamid=%d'\n",
521 stream->filename, stream_index);
526 /* change state to send data */
527 rtp_c->state = HTTPSTATE_SEND_DATA;
532 /* main loop of the http server */
533 static int http_server(void)
535 int server_fd = 0, rtsp_server_fd = 0;
536 int ret, delay, delay1;
537 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
538 HTTPContext *c, *c_next;
540 if (my_http_addr.sin_port) {
541 server_fd = socket_open_listen(&my_http_addr);
546 if (my_rtsp_addr.sin_port) {
547 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
548 if (rtsp_server_fd < 0)
552 if (!rtsp_server_fd && !server_fd) {
553 http_log("HTTP and RTSP disabled.\n");
557 http_log("ffserver started.\n");
559 start_children(first_feed);
561 first_http_ctx = NULL;
567 poll_entry = poll_table;
569 poll_entry->fd = server_fd;
570 poll_entry->events = POLLIN;
573 if (rtsp_server_fd) {
574 poll_entry->fd = rtsp_server_fd;
575 poll_entry->events = POLLIN;
579 /* wait for events on each HTTP handle */
586 case HTTPSTATE_SEND_HEADER:
587 case RTSPSTATE_SEND_REPLY:
588 case RTSPSTATE_SEND_PACKET:
589 c->poll_entry = poll_entry;
591 poll_entry->events = POLLOUT;
594 case HTTPSTATE_SEND_DATA_HEADER:
595 case HTTPSTATE_SEND_DATA:
596 case HTTPSTATE_SEND_DATA_TRAILER:
597 if (!c->is_packetized) {
598 /* for TCP, we output as much as we can (may need to put a limit) */
599 c->poll_entry = poll_entry;
601 poll_entry->events = POLLOUT;
604 /* when ffserver is doing the timing, we work by
605 looking at which packet need to be sent every
607 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
612 case HTTPSTATE_WAIT_REQUEST:
613 case HTTPSTATE_RECEIVE_DATA:
614 case HTTPSTATE_WAIT_FEED:
615 case RTSPSTATE_WAIT_REQUEST:
616 /* need to catch errors */
617 c->poll_entry = poll_entry;
619 poll_entry->events = POLLIN;/* Maybe this will work */
623 c->poll_entry = NULL;
629 /* wait for an event on one connection. We poll at least every
630 second to handle timeouts */
632 ret = poll(poll_table, poll_entry - poll_table, delay);
633 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
634 ff_neterrno() != FF_NETERROR(EINTR))
638 cur_time = av_gettime() / 1000;
640 if (need_to_start_children) {
641 need_to_start_children = 0;
642 start_children(first_feed);
645 /* now handle the events */
646 for(c = first_http_ctx; c != NULL; c = c_next) {
648 if (handle_connection(c) < 0) {
649 /* close and free the connection */
655 poll_entry = poll_table;
657 /* new HTTP connection request ? */
658 if (poll_entry->revents & POLLIN)
659 new_connection(server_fd, 0);
662 if (rtsp_server_fd) {
663 /* new RTSP connection request ? */
664 if (poll_entry->revents & POLLIN)
665 new_connection(rtsp_server_fd, 1);
670 /* start waiting for a new HTTP/RTSP request */
671 static void start_wait_request(HTTPContext *c, int is_rtsp)
673 c->buffer_ptr = c->buffer;
674 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
677 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
678 c->state = RTSPSTATE_WAIT_REQUEST;
680 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
681 c->state = HTTPSTATE_WAIT_REQUEST;
685 static void new_connection(int server_fd, int is_rtsp)
687 struct sockaddr_in from_addr;
689 HTTPContext *c = NULL;
691 len = sizeof(from_addr);
692 fd = accept(server_fd, (struct sockaddr *)&from_addr,
695 http_log("error during accept %s\n", strerror(errno));
698 ff_socket_nonblock(fd, 1);
700 /* XXX: should output a warning page when coming
701 close to the connection limit */
702 if (nb_connections >= nb_max_connections)
705 /* add a new connection */
706 c = av_mallocz(sizeof(HTTPContext));
711 c->poll_entry = NULL;
712 c->from_addr = from_addr;
713 c->buffer_size = IOBUFFER_INIT_SIZE;
714 c->buffer = av_malloc(c->buffer_size);
718 c->next = first_http_ctx;
722 start_wait_request(c, is_rtsp);
734 static void close_connection(HTTPContext *c)
736 HTTPContext **cp, *c1;
738 AVFormatContext *ctx;
742 /* remove connection from list */
743 cp = &first_http_ctx;
744 while ((*cp) != NULL) {
752 /* remove references, if any (XXX: do it faster) */
753 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
758 /* remove connection associated resources */
762 /* close each frame parser */
763 for(i=0;i<c->fmt_in->nb_streams;i++) {
764 st = c->fmt_in->streams[i];
765 if (st->codec->codec)
766 avcodec_close(st->codec);
768 av_close_input_file(c->fmt_in);
771 /* free RTP output streams if any */
774 nb_streams = c->stream->nb_streams;
776 for(i=0;i<nb_streams;i++) {
779 av_write_trailer(ctx);
782 h = c->rtp_handles[i];
789 if (!c->last_packet_sent) {
792 if (url_open_dyn_buf(&ctx->pb) >= 0) {
793 av_write_trailer(ctx);
794 av_freep(&c->pb_buffer);
795 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
800 for(i=0; i<ctx->nb_streams; i++)
801 av_free(ctx->streams[i]);
803 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
804 current_bandwidth -= c->stream->bandwidth;
806 /* signal that there is no feed if we are the feeder socket */
807 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
808 c->stream->feed_opened = 0;
812 av_freep(&c->pb_buffer);
813 av_freep(&c->packet_buffer);
819 static int handle_connection(HTTPContext *c)
824 case HTTPSTATE_WAIT_REQUEST:
825 case RTSPSTATE_WAIT_REQUEST:
827 if ((c->timeout - cur_time) < 0)
829 if (c->poll_entry->revents & (POLLERR | POLLHUP))
832 /* no need to read if no events */
833 if (!(c->poll_entry->revents & POLLIN))
837 len = recv(c->fd, c->buffer_ptr, 1, 0);
839 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
840 ff_neterrno() != FF_NETERROR(EINTR))
842 } else if (len == 0) {
845 /* search for end of request. */
847 c->buffer_ptr += len;
849 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
850 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
851 /* request found : parse it and reply */
852 if (c->state == HTTPSTATE_WAIT_REQUEST) {
853 ret = http_parse_request(c);
855 ret = rtsp_parse_request(c);
859 } else if (ptr >= c->buffer_end) {
860 /* request too long: cannot do anything */
862 } else goto read_loop;
866 case HTTPSTATE_SEND_HEADER:
867 if (c->poll_entry->revents & (POLLERR | POLLHUP))
870 /* no need to write if no events */
871 if (!(c->poll_entry->revents & POLLOUT))
873 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
875 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
876 ff_neterrno() != FF_NETERROR(EINTR)) {
877 /* error : close connection */
878 av_freep(&c->pb_buffer);
882 c->buffer_ptr += len;
884 c->stream->bytes_served += len;
885 c->data_count += len;
886 if (c->buffer_ptr >= c->buffer_end) {
887 av_freep(&c->pb_buffer);
891 /* all the buffer was sent : synchronize to the incoming stream */
892 c->state = HTTPSTATE_SEND_DATA_HEADER;
893 c->buffer_ptr = c->buffer_end = c->buffer;
898 case HTTPSTATE_SEND_DATA:
899 case HTTPSTATE_SEND_DATA_HEADER:
900 case HTTPSTATE_SEND_DATA_TRAILER:
901 /* for packetized output, we consider we can always write (the
902 input streams sets the speed). It may be better to verify
903 that we do not rely too much on the kernel queues */
904 if (!c->is_packetized) {
905 if (c->poll_entry->revents & (POLLERR | POLLHUP))
908 /* no need to read if no events */
909 if (!(c->poll_entry->revents & POLLOUT))
912 if (http_send_data(c) < 0)
914 /* close connection if trailer sent */
915 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
918 case HTTPSTATE_RECEIVE_DATA:
919 /* no need to read if no events */
920 if (c->poll_entry->revents & (POLLERR | POLLHUP))
922 if (!(c->poll_entry->revents & POLLIN))
924 if (http_receive_data(c) < 0)
927 case HTTPSTATE_WAIT_FEED:
928 /* no need to read if no events */
929 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
932 /* nothing to do, we'll be waken up by incoming feed packets */
935 case RTSPSTATE_SEND_REPLY:
936 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
937 av_freep(&c->pb_buffer);
940 /* no need to write if no events */
941 if (!(c->poll_entry->revents & POLLOUT))
943 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
945 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
946 ff_neterrno() != FF_NETERROR(EINTR)) {
947 /* error : close connection */
948 av_freep(&c->pb_buffer);
952 c->buffer_ptr += len;
953 c->data_count += len;
954 if (c->buffer_ptr >= c->buffer_end) {
955 /* all the buffer was sent : wait for a new request */
956 av_freep(&c->pb_buffer);
957 start_wait_request(c, 1);
961 case RTSPSTATE_SEND_PACKET:
962 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
963 av_freep(&c->packet_buffer);
966 /* no need to write if no events */
967 if (!(c->poll_entry->revents & POLLOUT))
969 len = send(c->fd, c->packet_buffer_ptr,
970 c->packet_buffer_end - c->packet_buffer_ptr, 0);
972 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
973 ff_neterrno() != FF_NETERROR(EINTR)) {
974 /* error : close connection */
975 av_freep(&c->packet_buffer);
979 c->packet_buffer_ptr += len;
980 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
981 /* all the buffer was sent : wait for a new request */
982 av_freep(&c->packet_buffer);
983 c->state = RTSPSTATE_WAIT_REQUEST;
987 case HTTPSTATE_READY:
996 static int extract_rates(char *rates, int ratelen, const char *request)
1000 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1001 if (strncasecmp(p, "Pragma:", 7) == 0) {
1002 const char *q = p + 7;
1004 while (*q && *q != '\n' && isspace(*q))
1007 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1013 memset(rates, 0xff, ratelen);
1016 while (*q && *q != '\n' && *q != ':')
1019 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1023 if (stream_no < ratelen && stream_no >= 0)
1024 rates[stream_no] = rate_no;
1026 while (*q && *q != '\n' && !isspace(*q))
1033 p = strchr(p, '\n');
1043 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1046 int best_bitrate = 100000000;
1049 for (i = 0; i < feed->nb_streams; i++) {
1050 AVCodecContext *feed_codec = feed->streams[i]->codec;
1052 if (feed_codec->codec_id != codec->codec_id ||
1053 feed_codec->sample_rate != codec->sample_rate ||
1054 feed_codec->width != codec->width ||
1055 feed_codec->height != codec->height)
1058 /* Potential stream */
1060 /* We want the fastest stream less than bit_rate, or the slowest
1061 * faster than bit_rate
1064 if (feed_codec->bit_rate <= bit_rate) {
1065 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1066 best_bitrate = feed_codec->bit_rate;
1070 if (feed_codec->bit_rate < best_bitrate) {
1071 best_bitrate = feed_codec->bit_rate;
1080 static int modify_current_stream(HTTPContext *c, char *rates)
1083 FFStream *req = c->stream;
1084 int action_required = 0;
1086 /* Not much we can do for a feed */
1090 for (i = 0; i < req->nb_streams; i++) {
1091 AVCodecContext *codec = req->streams[i]->codec;
1095 c->switch_feed_streams[i] = req->feed_streams[i];
1098 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1101 /* Wants off or slow */
1102 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1104 /* This doesn't work well when it turns off the only stream! */
1105 c->switch_feed_streams[i] = -2;
1106 c->feed_streams[i] = -2;
1111 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1112 action_required = 1;
1115 return action_required;
1119 static void do_switch_stream(HTTPContext *c, int i)
1121 if (c->switch_feed_streams[i] >= 0) {
1123 c->feed_streams[i] = c->switch_feed_streams[i];
1126 /* Now update the stream */
1128 c->switch_feed_streams[i] = -1;
1131 /* XXX: factorize in utils.c ? */
1132 /* XXX: take care with different space meaning */
1133 static void skip_spaces(const char **pp)
1137 while (*p == ' ' || *p == '\t')
1142 static void get_word(char *buf, int buf_size, const char **pp)
1150 while (!isspace(*p) && *p != '\0') {
1151 if ((q - buf) < buf_size - 1)
1160 static int validate_acl(FFStream *stream, HTTPContext *c)
1162 enum IPAddressAction last_action = IP_DENY;
1164 struct in_addr *src = &c->from_addr.sin_addr;
1165 unsigned long src_addr = src->s_addr;
1167 for (acl = stream->acl; acl; acl = acl->next) {
1168 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1169 return (acl->action == IP_ALLOW) ? 1 : 0;
1170 last_action = acl->action;
1173 /* Nothing matched, so return not the last action */
1174 return (last_action == IP_DENY) ? 1 : 0;
1177 /* compute the real filename of a file by matching it without its
1178 extensions to all the stream filenames */
1179 static void compute_real_filename(char *filename, int max_size)
1186 /* compute filename by matching without the file extensions */
1187 av_strlcpy(file1, filename, sizeof(file1));
1188 p = strrchr(file1, '.');
1191 for(stream = first_stream; stream != NULL; stream = stream->next) {
1192 av_strlcpy(file2, stream->filename, sizeof(file2));
1193 p = strrchr(file2, '.');
1196 if (!strcmp(file1, file2)) {
1197 av_strlcpy(filename, stream->filename, max_size);
1212 /* parse http request and prepare header */
1213 static int http_parse_request(HTTPContext *c)
1216 enum RedirType redir_type;
1218 char info[1024], filename[1024];
1222 const char *mime_type;
1226 char *useragent = 0;
1229 get_word(cmd, sizeof(cmd), (const char **)&p);
1230 av_strlcpy(c->method, cmd, sizeof(c->method));
1232 if (!strcmp(cmd, "GET"))
1234 else if (!strcmp(cmd, "POST"))
1239 get_word(url, sizeof(url), (const char **)&p);
1240 av_strlcpy(c->url, url, sizeof(c->url));
1242 get_word(protocol, sizeof(protocol), (const char **)&p);
1243 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1246 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1249 http_log("New connection: %s %s\n", cmd, url);
1251 /* find the filename and the optional info string in the request */
1252 p = strchr(url, '?');
1254 av_strlcpy(info, p, sizeof(info));
1259 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1261 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1262 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1264 if (*useragent && *useragent != '\n' && isspace(*useragent))
1268 p = strchr(p, '\n');
1275 redir_type = REDIR_NONE;
1276 if (match_ext(filename, "asx")) {
1277 redir_type = REDIR_ASX;
1278 filename[strlen(filename)-1] = 'f';
1279 } else if (match_ext(filename, "asf") &&
1280 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1281 /* if this isn't WMP or lookalike, return the redirector file */
1282 redir_type = REDIR_ASF;
1283 } else if (match_ext(filename, "rpm,ram")) {
1284 redir_type = REDIR_RAM;
1285 strcpy(filename + strlen(filename)-2, "m");
1286 } else if (match_ext(filename, "rtsp")) {
1287 redir_type = REDIR_RTSP;
1288 compute_real_filename(filename, sizeof(filename) - 1);
1289 } else if (match_ext(filename, "sdp")) {
1290 redir_type = REDIR_SDP;
1291 compute_real_filename(filename, sizeof(filename) - 1);
1294 // "redirect" / request to index.html
1295 if (!strlen(filename))
1296 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1298 stream = first_stream;
1299 while (stream != NULL) {
1300 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1302 stream = stream->next;
1304 if (stream == NULL) {
1305 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1310 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1311 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1313 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1314 c->http_error = 301;
1316 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1317 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1318 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1319 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1320 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1321 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1324 /* prepare output buffer */
1325 c->buffer_ptr = c->buffer;
1327 c->state = HTTPSTATE_SEND_HEADER;
1331 /* If this is WMP, get the rate information */
1332 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1333 if (modify_current_stream(c, ratebuf)) {
1334 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1335 if (c->switch_feed_streams[i] >= 0)
1336 do_switch_stream(c, i);
1341 /* If already streaming this feed, do not let start another feeder. */
1342 if (stream->feed_opened) {
1343 snprintf(msg, sizeof(msg), "This feed is already being received.");
1347 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1348 current_bandwidth += stream->bandwidth;
1350 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1351 c->http_error = 200;
1353 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1354 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1355 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1356 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1357 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The server is too busy to serve your request at this time.</p>\r\n");
1358 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The bandwidth being served (including your stream) is %lldkbit/sec, and this exceeds the limit of %lldkbit/sec.</p>\r\n",
1359 current_bandwidth, max_bandwidth);
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1362 /* prepare output buffer */
1363 c->buffer_ptr = c->buffer;
1365 c->state = HTTPSTATE_SEND_HEADER;
1369 if (redir_type != REDIR_NONE) {
1372 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1373 if (strncasecmp(p, "Host:", 5) == 0) {
1377 p = strchr(p, '\n');
1388 while (isspace(*hostinfo))
1391 eoh = strchr(hostinfo, '\n');
1393 if (eoh[-1] == '\r')
1396 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1397 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1398 hostbuf[eoh - hostinfo] = 0;
1400 c->http_error = 200;
1402 switch(redir_type) {
1404 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1405 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1408 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1410 hostbuf, filename, info);
1411 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1414 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1415 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1416 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1417 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1418 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1419 hostbuf, filename, info);
1422 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1425 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1426 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1427 hostbuf, filename, info);
1431 char hostname[256], *p;
1432 /* extract only hostname */
1433 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1434 p = strrchr(hostname, ':');
1437 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1438 /* XXX: incorrect mime type ? */
1439 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1440 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1441 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1442 hostname, ntohs(my_rtsp_addr.sin_port),
1449 int sdp_data_size, len;
1450 struct sockaddr_in my_addr;
1452 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1453 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1454 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1456 len = sizeof(my_addr);
1457 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1459 /* XXX: should use a dynamic buffer */
1460 sdp_data_size = prepare_sdp_description(stream,
1463 if (sdp_data_size > 0) {
1464 memcpy(q, sdp_data, sdp_data_size);
1476 /* prepare output buffer */
1477 c->buffer_ptr = c->buffer;
1479 c->state = HTTPSTATE_SEND_HEADER;
1485 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1489 stream->conns_served++;
1491 /* XXX: add there authenticate and IP match */
1494 /* if post, it means a feed is being sent */
1495 if (!stream->is_feed) {
1496 /* However it might be a status report from WMP! Lets log the data
1497 * as it might come in handy one day
1502 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1503 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1507 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1508 client_id = strtol(p + 18, 0, 10);
1509 p = strchr(p, '\n');
1517 char *eol = strchr(logline, '\n');
1522 if (eol[-1] == '\r')
1524 http_log("%.*s\n", (int) (eol - logline), logline);
1525 c->suppress_log = 1;
1530 http_log("\nGot request:\n%s\n", c->buffer);
1533 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1536 /* Now we have to find the client_id */
1537 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1538 if (wmpc->wmp_client_id == client_id)
1542 if (wmpc && modify_current_stream(wmpc, ratebuf))
1543 wmpc->switch_pending = 1;
1546 snprintf(msg, sizeof(msg), "POST command not handled");
1550 if (http_start_receive_data(c) < 0) {
1551 snprintf(msg, sizeof(msg), "could not open feed");
1555 c->state = HTTPSTATE_RECEIVE_DATA;
1560 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1561 http_log("\nGot request:\n%s\n", c->buffer);
1564 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1567 /* open input stream */
1568 if (open_input_stream(c, info) < 0) {
1569 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1573 /* prepare http header */
1575 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1576 mime_type = c->stream->fmt->mime_type;
1578 mime_type = "application/x-octet-stream";
1579 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1581 /* for asf, we need extra headers */
1582 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1583 /* Need to allocate a client id */
1585 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1587 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
1589 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1590 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1592 /* prepare output buffer */
1594 c->buffer_ptr = c->buffer;
1596 c->state = HTTPSTATE_SEND_HEADER;
1599 c->http_error = 404;
1601 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1602 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1603 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1604 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1605 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1606 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1607 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1609 /* prepare output buffer */
1610 c->buffer_ptr = c->buffer;
1612 c->state = HTTPSTATE_SEND_HEADER;
1616 c->http_error = 200; /* horrible : we use this value to avoid
1617 going to the send data state */
1618 c->state = HTTPSTATE_SEND_HEADER;
1622 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1624 static const char *suffix = " kMGTP";
1627 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1629 url_fprintf(pb, "%"PRId64"%c", count, *s);
1632 static void compute_status(HTTPContext *c)
1641 if (url_open_dyn_buf(&pb) < 0) {
1642 /* XXX: return an error ? */
1643 c->buffer_ptr = c->buffer;
1644 c->buffer_end = c->buffer;
1648 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1649 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1650 url_fprintf(pb, "Pragma: no-cache\r\n");
1651 url_fprintf(pb, "\r\n");
1653 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1654 if (c->stream->feed_filename[0])
1655 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1656 url_fprintf(pb, "</HEAD>\n<BODY>");
1657 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1659 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1660 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1661 url_fprintf(pb, "<TR><Th valign=top>Path<th align=left>Served<br>Conns<Th><br>bytes<Th valign=top>Format<Th>Bit rate<br>kbits/s<Th align=left>Video<br>kbits/s<th><br>Codec<Th align=left>Audio<br>kbits/s<th><br>Codec<Th align=left valign=top>Feed\n");
1662 stream = first_stream;
1663 while (stream != NULL) {
1664 char sfilename[1024];
1667 if (stream->feed != stream) {
1668 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1669 eosf = sfilename + strlen(sfilename);
1670 if (eosf - sfilename >= 4) {
1671 if (strcmp(eosf - 4, ".asf") == 0)
1672 strcpy(eosf - 4, ".asx");
1673 else if (strcmp(eosf - 3, ".rm") == 0)
1674 strcpy(eosf - 3, ".ram");
1675 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1676 /* generate a sample RTSP director if
1677 unicast. Generate an SDP redirector if
1679 eosf = strrchr(sfilename, '.');
1681 eosf = sfilename + strlen(sfilename);
1682 if (stream->is_multicast)
1683 strcpy(eosf, ".sdp");
1685 strcpy(eosf, ".rtsp");
1689 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1690 sfilename, stream->filename);
1691 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1692 stream->conns_served);
1693 fmt_bytecount(pb, stream->bytes_served);
1694 switch(stream->stream_type) {
1695 case STREAM_TYPE_LIVE:
1697 int audio_bit_rate = 0;
1698 int video_bit_rate = 0;
1699 const char *audio_codec_name = "";
1700 const char *video_codec_name = "";
1701 const char *audio_codec_name_extra = "";
1702 const char *video_codec_name_extra = "";
1704 for(i=0;i<stream->nb_streams;i++) {
1705 AVStream *st = stream->streams[i];
1706 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1707 switch(st->codec->codec_type) {
1708 case CODEC_TYPE_AUDIO:
1709 audio_bit_rate += st->codec->bit_rate;
1711 if (*audio_codec_name)
1712 audio_codec_name_extra = "...";
1713 audio_codec_name = codec->name;
1716 case CODEC_TYPE_VIDEO:
1717 video_bit_rate += st->codec->bit_rate;
1719 if (*video_codec_name)
1720 video_codec_name_extra = "...";
1721 video_codec_name = codec->name;
1724 case CODEC_TYPE_DATA:
1725 video_bit_rate += st->codec->bit_rate;
1731 url_fprintf(pb, "<TD align=center> %s <TD align=right> %d <TD align=right> %d <TD> %s %s <TD align=right> %d <TD> %s %s",
1734 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1735 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1737 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1739 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1740 url_fprintf(pb, "\n");
1744 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1748 stream = stream->next;
1750 url_fprintf(pb, "</TABLE>\n");
1752 stream = first_stream;
1753 while (stream != NULL) {
1754 if (stream->feed == stream) {
1755 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1757 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1759 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1764 /* This is somewhat linux specific I guess */
1765 snprintf(ps_cmd, sizeof(ps_cmd),
1766 "ps -o \"%%cpu,cputime\" --no-headers %d",
1769 pid_stat = popen(ps_cmd, "r");
1774 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1776 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1784 url_fprintf(pb, "<p>");
1786 url_fprintf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
1788 for (i = 0; i < stream->nb_streams; i++) {
1789 AVStream *st = stream->streams[i];
1790 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1791 const char *type = "unknown";
1792 char parameters[64];
1796 switch(st->codec->codec_type) {
1797 case CODEC_TYPE_AUDIO:
1799 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1801 case CODEC_TYPE_VIDEO:
1803 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1804 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1809 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1810 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1812 url_fprintf(pb, "</table>\n");
1815 stream = stream->next;
1821 AVCodecContext *enc;
1825 stream = first_feed;
1826 while (stream != NULL) {
1827 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1828 url_fprintf(pb, "<TABLE>\n");
1829 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1830 for(i=0;i<stream->nb_streams;i++) {
1831 AVStream *st = stream->streams[i];
1832 FeedData *fdata = st->priv_data;
1835 avcodec_string(buf, sizeof(buf), enc);
1836 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1837 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1838 avg /= enc->frame_size;
1839 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1840 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1842 url_fprintf(pb, "</TABLE>\n");
1843 stream = stream->next_feed;
1848 /* connection status */
1849 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1851 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1852 nb_connections, nb_max_connections);
1854 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1855 current_bandwidth, max_bandwidth);
1857 url_fprintf(pb, "<TABLE>\n");
1858 url_fprintf(pb, "<TR><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
1859 c1 = first_http_ctx;
1861 while (c1 != NULL) {
1867 for (j = 0; j < c1->stream->nb_streams; j++) {
1868 if (!c1->stream->feed)
1869 bitrate += c1->stream->streams[j]->codec->bit_rate;
1870 else if (c1->feed_streams[j] >= 0)
1871 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1876 p = inet_ntoa(c1->from_addr.sin_addr);
1877 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1879 c1->stream ? c1->stream->filename : "",
1880 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1883 http_state[c1->state]);
1884 fmt_bytecount(pb, bitrate);
1885 url_fprintf(pb, "<td align=right>");
1886 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1887 url_fprintf(pb, "<td align=right>");
1888 fmt_bytecount(pb, c1->data_count);
1889 url_fprintf(pb, "\n");
1892 url_fprintf(pb, "</TABLE>\n");
1897 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1898 url_fprintf(pb, "</BODY>\n</HTML>\n");
1900 len = url_close_dyn_buf(pb, &c->pb_buffer);
1901 c->buffer_ptr = c->pb_buffer;
1902 c->buffer_end = c->pb_buffer + len;
1905 /* check if the parser needs to be opened for stream i */
1906 static void open_parser(AVFormatContext *s, int i)
1908 AVStream *st = s->streams[i];
1911 if (!st->codec->codec) {
1912 codec = avcodec_find_decoder(st->codec->codec_id);
1913 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1914 st->codec->parse_only = 1;
1915 if (avcodec_open(st->codec, codec) < 0)
1916 st->codec->parse_only = 0;
1921 static int open_input_stream(HTTPContext *c, const char *info)
1924 char input_filename[1024];
1926 int buf_size, i, ret;
1929 /* find file name */
1930 if (c->stream->feed) {
1931 strcpy(input_filename, c->stream->feed->feed_filename);
1932 buf_size = FFM_PACKET_SIZE;
1933 /* compute position (absolute time) */
1934 if (find_info_tag(buf, sizeof(buf), "date", info))
1936 stream_pos = parse_date(buf, 0);
1937 if (stream_pos == INT64_MIN)
1940 else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1941 int prebuffer = strtol(buf, 0, 10);
1942 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1944 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1946 strcpy(input_filename, c->stream->feed_filename);
1948 /* compute position (relative time) */
1949 if (find_info_tag(buf, sizeof(buf), "date", info))
1951 stream_pos = parse_date(buf, 1);
1952 if (stream_pos == INT64_MIN)
1958 if (input_filename[0] == '\0')
1962 { time_t when = stream_pos / 1000000;
1963 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1968 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1969 buf_size, c->stream->ap_in)) < 0) {
1970 http_log("could not open %s: %d\n", input_filename, ret);
1973 s->flags |= AVFMT_FLAG_GENPTS;
1975 av_find_stream_info(c->fmt_in);
1977 /* open each parser */
1978 for(i=0;i<s->nb_streams;i++)
1981 /* choose stream as clock source (we favorize video stream if
1982 present) for packet sending */
1983 c->pts_stream_index = 0;
1984 for(i=0;i<c->stream->nb_streams;i++) {
1985 if (c->pts_stream_index == 0 &&
1986 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1987 c->pts_stream_index = i;
1992 if (c->fmt_in->iformat->read_seek)
1993 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1995 /* set the start time (needed for maxtime and RTP packet timing) */
1996 c->start_time = cur_time;
1997 c->first_pts = AV_NOPTS_VALUE;
2001 /* return the server clock (in us) */
2002 static int64_t get_server_clock(HTTPContext *c)
2004 /* compute current pts value from system time */
2005 return (cur_time - c->start_time) * 1000;
2008 /* return the estimated time at which the current packet must be sent
2010 static int64_t get_packet_send_clock(HTTPContext *c)
2012 int bytes_left, bytes_sent, frame_bytes;
2014 frame_bytes = c->cur_frame_bytes;
2015 if (frame_bytes <= 0)
2018 bytes_left = c->buffer_end - c->buffer_ptr;
2019 bytes_sent = frame_bytes - bytes_left;
2020 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2025 static int http_prepare_data(HTTPContext *c)
2028 AVFormatContext *ctx;
2030 av_freep(&c->pb_buffer);
2032 case HTTPSTATE_SEND_DATA_HEADER:
2033 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2034 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2035 sizeof(c->fmt_ctx.author));
2036 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2037 sizeof(c->fmt_ctx.comment));
2038 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2039 sizeof(c->fmt_ctx.copyright));
2040 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2041 sizeof(c->fmt_ctx.title));
2043 /* open output stream by using specified codecs */
2044 c->fmt_ctx.oformat = c->stream->fmt;
2045 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2046 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2049 st = av_mallocz(sizeof(AVStream));
2050 st->codec= avcodec_alloc_context();
2051 c->fmt_ctx.streams[i] = st;
2052 /* if file or feed, then just take streams from FFStream struct */
2053 if (!c->stream->feed ||
2054 c->stream->feed == c->stream)
2055 src = c->stream->streams[i];
2057 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2061 st->codec->frame_number = 0; /* XXX: should be done in
2062 AVStream, not in codec */
2064 c->got_key_frame = 0;
2066 /* prepare header and save header data in a stream */
2067 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2068 /* XXX: potential leak */
2071 c->fmt_ctx.pb->is_streamed = 1;
2074 * HACK to avoid mpeg ps muxer to spit many underflow errors
2075 * Default value from FFmpeg
2076 * Try to set it use configuration option
2078 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2079 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2081 av_set_parameters(&c->fmt_ctx, NULL);
2082 if (av_write_header(&c->fmt_ctx) < 0) {
2083 http_log("Error writing output header\n");
2087 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2088 c->buffer_ptr = c->pb_buffer;
2089 c->buffer_end = c->pb_buffer + len;
2091 c->state = HTTPSTATE_SEND_DATA;
2092 c->last_packet_sent = 0;
2094 case HTTPSTATE_SEND_DATA:
2095 /* find a new packet */
2096 /* read a packet from the input stream */
2097 if (c->stream->feed)
2098 ffm_set_write_index(c->fmt_in,
2099 c->stream->feed->feed_write_index,
2100 c->stream->feed->feed_size);
2102 if (c->stream->max_time &&
2103 c->stream->max_time + c->start_time - cur_time < 0)
2104 /* We have timed out */
2105 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2109 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2110 if (c->stream->feed && c->stream->feed->feed_opened) {
2111 /* if coming from feed, it means we reached the end of the
2112 ffm file, so must wait for more data */
2113 c->state = HTTPSTATE_WAIT_FEED;
2114 return 1; /* state changed */
2116 if (c->stream->loop) {
2117 av_close_input_file(c->fmt_in);
2119 if (open_input_stream(c, "") < 0)
2124 /* must send trailer now because eof or error */
2125 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2129 int source_index = pkt.stream_index;
2130 /* update first pts if needed */
2131 if (c->first_pts == AV_NOPTS_VALUE) {
2132 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2133 c->start_time = cur_time;
2135 /* send it to the appropriate stream */
2136 if (c->stream->feed) {
2137 /* if coming from a feed, select the right stream */
2138 if (c->switch_pending) {
2139 c->switch_pending = 0;
2140 for(i=0;i<c->stream->nb_streams;i++) {
2141 if (c->switch_feed_streams[i] == pkt.stream_index)
2142 if (pkt.flags & PKT_FLAG_KEY)
2143 do_switch_stream(c, i);
2144 if (c->switch_feed_streams[i] >= 0)
2145 c->switch_pending = 1;
2148 for(i=0;i<c->stream->nb_streams;i++) {
2149 if (c->feed_streams[i] == pkt.stream_index) {
2150 AVStream *st = c->fmt_in->streams[source_index];
2151 pkt.stream_index = i;
2152 if (pkt.flags & PKT_FLAG_KEY &&
2153 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2154 c->stream->nb_streams == 1))
2155 c->got_key_frame = 1;
2156 if (!c->stream->send_on_key || c->got_key_frame)
2161 AVCodecContext *codec;
2164 /* specific handling for RTP: we use several
2165 output stream (one for each RTP
2166 connection). XXX: need more abstract handling */
2167 if (c->is_packetized) {
2169 /* compute send time and duration */
2170 st = c->fmt_in->streams[pkt.stream_index];
2171 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2172 if (st->start_time != AV_NOPTS_VALUE)
2173 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2174 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2176 printf("index=%d pts=%0.3f duration=%0.6f\n",
2178 (double)c->cur_pts /
2180 (double)c->cur_frame_duration /
2183 /* find RTP context */
2184 c->packet_stream_index = pkt.stream_index;
2185 ctx = c->rtp_ctx[c->packet_stream_index];
2187 av_free_packet(&pkt);
2190 codec = ctx->streams[0]->codec;
2191 /* only one stream per RTP connection */
2192 pkt.stream_index = 0;
2196 codec = ctx->streams[pkt.stream_index]->codec;
2199 if (c->is_packetized) {
2200 int max_packet_size;
2201 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2202 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2204 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2205 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2207 ret = url_open_dyn_buf(&ctx->pb);
2210 /* XXX: potential leak */
2213 c->fmt_ctx.pb->is_streamed = 1;
2214 if (pkt.dts != AV_NOPTS_VALUE)
2215 pkt.dts = av_rescale_q(pkt.dts,
2216 c->fmt_in->streams[source_index]->time_base,
2217 ctx->streams[pkt.stream_index]->time_base);
2218 if (pkt.pts != AV_NOPTS_VALUE)
2219 pkt.pts = av_rescale_q(pkt.pts,
2220 c->fmt_in->streams[source_index]->time_base,
2221 ctx->streams[pkt.stream_index]->time_base);
2222 pkt.duration = av_rescale_q(pkt.duration,
2223 c->fmt_in->streams[source_index]->time_base,
2224 ctx->streams[pkt.stream_index]->time_base);
2225 if (av_write_frame(ctx, &pkt) < 0) {
2226 http_log("Error writing frame to output\n");
2227 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2231 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2232 c->cur_frame_bytes = len;
2233 c->buffer_ptr = c->pb_buffer;
2234 c->buffer_end = c->pb_buffer + len;
2236 codec->frame_number++;
2238 av_free_packet(&pkt);
2242 av_free_packet(&pkt);
2247 case HTTPSTATE_SEND_DATA_TRAILER:
2248 /* last packet test ? */
2249 if (c->last_packet_sent || c->is_packetized)
2252 /* prepare header */
2253 if (url_open_dyn_buf(&ctx->pb) < 0) {
2254 /* XXX: potential leak */
2257 c->fmt_ctx.pb->is_streamed = 1;
2258 av_write_trailer(ctx);
2259 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2260 c->buffer_ptr = c->pb_buffer;
2261 c->buffer_end = c->pb_buffer + len;
2263 c->last_packet_sent = 1;
2269 /* should convert the format at the same time */
2270 /* send data starting at c->buffer_ptr to the output connection
2271 (either UDP or TCP connection) */
2272 static int http_send_data(HTTPContext *c)
2277 if (c->buffer_ptr >= c->buffer_end) {
2278 ret = http_prepare_data(c);
2282 /* state change requested */
2285 if (c->is_packetized) {
2286 /* RTP data output */
2287 len = c->buffer_end - c->buffer_ptr;
2289 /* fail safe - should never happen */
2291 c->buffer_ptr = c->buffer_end;
2294 len = (c->buffer_ptr[0] << 24) |
2295 (c->buffer_ptr[1] << 16) |
2296 (c->buffer_ptr[2] << 8) |
2298 if (len > (c->buffer_end - c->buffer_ptr))
2300 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2301 /* nothing to send yet: we can wait */
2305 c->data_count += len;
2306 update_datarate(&c->datarate, c->data_count);
2308 c->stream->bytes_served += len;
2310 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2311 /* RTP packets are sent inside the RTSP TCP connection */
2313 int interleaved_index, size;
2315 HTTPContext *rtsp_c;
2318 /* if no RTSP connection left, error */
2321 /* if already sending something, then wait. */
2322 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2324 if (url_open_dyn_buf(&pb) < 0)
2326 interleaved_index = c->packet_stream_index * 2;
2327 /* RTCP packets are sent at odd indexes */
2328 if (c->buffer_ptr[1] == 200)
2329 interleaved_index++;
2330 /* write RTSP TCP header */
2332 header[1] = interleaved_index;
2333 header[2] = len >> 8;
2335 put_buffer(pb, header, 4);
2336 /* write RTP packet data */
2338 put_buffer(pb, c->buffer_ptr, len);
2339 size = url_close_dyn_buf(pb, &c->packet_buffer);
2340 /* prepare asynchronous TCP sending */
2341 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2342 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2343 c->buffer_ptr += len;
2345 /* send everything we can NOW */
2346 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2347 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2349 rtsp_c->packet_buffer_ptr += len;
2350 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2351 /* if we could not send all the data, we will
2352 send it later, so a new state is needed to
2353 "lock" the RTSP TCP connection */
2354 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2357 /* all data has been sent */
2358 av_freep(&c->packet_buffer);
2360 /* send RTP packet directly in UDP */
2362 url_write(c->rtp_handles[c->packet_stream_index],
2363 c->buffer_ptr, len);
2364 c->buffer_ptr += len;
2365 /* here we continue as we can send several packets per 10 ms slot */
2368 /* TCP data output */
2369 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2371 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2372 ff_neterrno() != FF_NETERROR(EINTR))
2373 /* error : close connection */
2378 c->buffer_ptr += len;
2380 c->data_count += len;
2381 update_datarate(&c->datarate, c->data_count);
2383 c->stream->bytes_served += len;
2391 static int http_start_receive_data(HTTPContext *c)
2395 if (c->stream->feed_opened)
2398 /* Don't permit writing to this one */
2399 if (c->stream->readonly)
2403 fd = open(c->stream->feed_filename, O_RDWR);
2405 http_log("Error opening feeder file: %s\n", strerror(errno));
2410 c->stream->feed_write_index = ffm_read_write_index(fd);
2411 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2412 lseek(fd, 0, SEEK_SET);
2414 /* init buffer input */
2415 c->buffer_ptr = c->buffer;
2416 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2417 c->stream->feed_opened = 1;
2421 static int http_receive_data(HTTPContext *c)
2425 if (c->buffer_end > c->buffer_ptr) {
2428 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2430 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2431 ff_neterrno() != FF_NETERROR(EINTR))
2432 /* error : close connection */
2434 } else if (len == 0)
2435 /* end of connection : close it */
2438 c->buffer_ptr += len;
2439 c->data_count += len;
2440 update_datarate(&c->datarate, c->data_count);
2444 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2445 if (c->buffer[0] != 'f' ||
2446 c->buffer[1] != 'm') {
2447 http_log("Feed stream has become desynchronized -- disconnecting\n");
2452 if (c->buffer_ptr >= c->buffer_end) {
2453 FFStream *feed = c->stream;
2454 /* a packet has been received : write it in the store, except
2456 if (c->data_count > FFM_PACKET_SIZE) {
2458 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2459 /* XXX: use llseek or url_seek */
2460 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2461 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2462 http_log("Error writing to feed file: %s\n", strerror(errno));
2466 feed->feed_write_index += FFM_PACKET_SIZE;
2467 /* update file size */
2468 if (feed->feed_write_index > c->stream->feed_size)
2469 feed->feed_size = feed->feed_write_index;
2471 /* handle wrap around if max file size reached */
2472 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2473 feed->feed_write_index = FFM_PACKET_SIZE;
2476 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2478 /* wake up any waiting connections */
2479 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2480 if (c1->state == HTTPSTATE_WAIT_FEED &&
2481 c1->stream->feed == c->stream->feed)
2482 c1->state = HTTPSTATE_SEND_DATA;
2485 /* We have a header in our hands that contains useful data */
2487 AVInputFormat *fmt_in;
2490 memset(&s, 0, sizeof(s));
2492 url_open_buf(&s.pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2493 s.pb->is_streamed = 1;
2495 /* use feed output format name to find corresponding input format */
2496 fmt_in = av_find_input_format(feed->fmt->name);
2500 if (fmt_in->priv_data_size > 0) {
2501 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2507 if (fmt_in->read_header(&s, 0) < 0) {
2508 av_freep(&s.priv_data);
2512 /* Now we have the actual streams */
2513 if (s.nb_streams != feed->nb_streams) {
2514 av_freep(&s.priv_data);
2517 for (i = 0; i < s.nb_streams; i++)
2518 memcpy(feed->streams[i]->codec,
2519 s.streams[i]->codec, sizeof(AVCodecContext));
2520 av_freep(&s.priv_data);
2522 c->buffer_ptr = c->buffer;
2527 c->stream->feed_opened = 0;
2529 /* wake up any waiting connections to stop waiting for feed */
2530 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2531 if (c1->state == HTTPSTATE_WAIT_FEED &&
2532 c1->stream->feed == c->stream->feed)
2533 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2538 /********************************************************************/
2541 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2548 switch(error_number) {
2549 case RTSP_STATUS_OK:
2552 case RTSP_STATUS_METHOD:
2553 str = "Method Not Allowed";
2555 case RTSP_STATUS_BANDWIDTH:
2556 str = "Not Enough Bandwidth";
2558 case RTSP_STATUS_SESSION:
2559 str = "Session Not Found";
2561 case RTSP_STATUS_STATE:
2562 str = "Method Not Valid in This State";
2564 case RTSP_STATUS_AGGREGATE:
2565 str = "Aggregate operation not allowed";
2567 case RTSP_STATUS_ONLY_AGGREGATE:
2568 str = "Only aggregate operation allowed";
2570 case RTSP_STATUS_TRANSPORT:
2571 str = "Unsupported transport";
2573 case RTSP_STATUS_INTERNAL:
2574 str = "Internal Server Error";
2576 case RTSP_STATUS_SERVICE:
2577 str = "Service Unavailable";
2579 case RTSP_STATUS_VERSION:
2580 str = "RTSP Version not supported";
2583 str = "Unknown Error";
2587 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2588 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2590 /* output GMT time */
2594 p = buf2 + strlen(p) - 1;
2597 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2600 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2602 rtsp_reply_header(c, error_number);
2603 url_fprintf(c->pb, "\r\n");
2606 static int rtsp_parse_request(HTTPContext *c)
2608 const char *p, *p1, *p2;
2614 RTSPHeader header1, *header = &header1;
2616 c->buffer_ptr[0] = '\0';
2619 get_word(cmd, sizeof(cmd), &p);
2620 get_word(url, sizeof(url), &p);
2621 get_word(protocol, sizeof(protocol), &p);
2623 av_strlcpy(c->method, cmd, sizeof(c->method));
2624 av_strlcpy(c->url, url, sizeof(c->url));
2625 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2627 if (url_open_dyn_buf(&c->pb) < 0) {
2628 /* XXX: cannot do more */
2629 c->pb = NULL; /* safety */
2633 /* check version name */
2634 if (strcmp(protocol, "RTSP/1.0") != 0) {
2635 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2639 /* parse each header line */
2640 memset(header, 0, sizeof(RTSPHeader));
2641 /* skip to next line */
2642 while (*p != '\n' && *p != '\0')
2646 while (*p != '\0') {
2647 p1 = strchr(p, '\n');
2651 if (p2 > p && p2[-1] == '\r')
2653 /* skip empty line */
2657 if (len > sizeof(line) - 1)
2658 len = sizeof(line) - 1;
2659 memcpy(line, p, len);
2661 rtsp_parse_line(header, line);
2665 /* handle sequence number */
2666 c->seq = header->seq;
2668 if (!strcmp(cmd, "DESCRIBE"))
2669 rtsp_cmd_describe(c, url);
2670 else if (!strcmp(cmd, "OPTIONS"))
2671 rtsp_cmd_options(c, url);
2672 else if (!strcmp(cmd, "SETUP"))
2673 rtsp_cmd_setup(c, url, header);
2674 else if (!strcmp(cmd, "PLAY"))
2675 rtsp_cmd_play(c, url, header);
2676 else if (!strcmp(cmd, "PAUSE"))
2677 rtsp_cmd_pause(c, url, header);
2678 else if (!strcmp(cmd, "TEARDOWN"))
2679 rtsp_cmd_teardown(c, url, header);
2681 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2684 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2685 c->pb = NULL; /* safety */
2687 /* XXX: cannot do more */
2690 c->buffer_ptr = c->pb_buffer;
2691 c->buffer_end = c->pb_buffer + len;
2692 c->state = RTSPSTATE_SEND_REPLY;
2696 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2697 struct in_addr my_ip)
2699 AVFormatContext *avc;
2700 AVStream avs[MAX_STREAMS];
2703 avc = av_alloc_format_context();
2707 if (stream->title[0] != 0) {
2708 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2710 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2712 avc->nb_streams = stream->nb_streams;
2713 if (stream->is_multicast) {
2714 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2715 inet_ntoa(stream->multicast_ip),
2716 stream->multicast_port, stream->multicast_ttl);
2719 for(i = 0; i < stream->nb_streams; i++) {
2720 avc->streams[i] = &avs[i];
2721 avc->streams[i]->codec = stream->streams[i]->codec;
2723 *pbuffer = av_mallocz(2048);
2724 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2727 return strlen(*pbuffer);
2730 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2732 // rtsp_reply_header(c, RTSP_STATUS_OK);
2733 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2734 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2735 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2736 url_fprintf(c->pb, "\r\n");
2739 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2745 int content_length, len;
2746 struct sockaddr_in my_addr;
2748 /* find which url is asked */
2749 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2754 for(stream = first_stream; stream != NULL; stream = stream->next) {
2755 if (!stream->is_feed &&
2756 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2757 !strcmp(path, stream->filename)) {
2761 /* no stream found */
2762 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2766 /* prepare the media description in sdp format */
2768 /* get the host IP */
2769 len = sizeof(my_addr);
2770 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2771 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2772 if (content_length < 0) {
2773 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2776 rtsp_reply_header(c, RTSP_STATUS_OK);
2777 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2778 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2779 url_fprintf(c->pb, "\r\n");
2780 put_buffer(c->pb, content, content_length);
2783 static HTTPContext *find_rtp_session(const char *session_id)
2787 if (session_id[0] == '\0')
2790 for(c = first_http_ctx; c != NULL; c = c->next) {
2791 if (!strcmp(c->session_id, session_id))
2797 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2799 RTSPTransportField *th;
2802 for(i=0;i<h->nb_transports;i++) {
2803 th = &h->transports[i];
2804 if (th->protocol == protocol)
2810 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2814 int stream_index, port;
2819 RTSPTransportField *th;
2820 struct sockaddr_in dest_addr;
2821 RTSPActionServerSetup setup;
2823 /* find which url is asked */
2824 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2829 /* now check each stream */
2830 for(stream = first_stream; stream != NULL; stream = stream->next) {
2831 if (!stream->is_feed &&
2832 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2833 /* accept aggregate filenames only if single stream */
2834 if (!strcmp(path, stream->filename)) {
2835 if (stream->nb_streams != 1) {
2836 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2843 for(stream_index = 0; stream_index < stream->nb_streams;
2845 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2846 stream->filename, stream_index);
2847 if (!strcmp(path, buf))
2852 /* no stream found */
2853 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2857 /* generate session id if needed */
2858 if (h->session_id[0] == '\0')
2859 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2860 av_random(&random_state), av_random(&random_state));
2862 /* find rtp session, and create it if none found */
2863 rtp_c = find_rtp_session(h->session_id);
2865 /* always prefer UDP */
2866 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2868 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2870 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2875 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2878 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2882 /* open input stream */
2883 if (open_input_stream(rtp_c, "") < 0) {
2884 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2889 /* test if stream is OK (test needed because several SETUP needs
2890 to be done for a given file) */
2891 if (rtp_c->stream != stream) {
2892 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2896 /* test if stream is already set up */
2897 if (rtp_c->rtp_ctx[stream_index]) {
2898 rtsp_reply_error(c, RTSP_STATUS_STATE);
2902 /* check transport */
2903 th = find_transport(h, rtp_c->rtp_protocol);
2904 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2905 th->client_port_min <= 0)) {
2906 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2910 /* setup default options */
2911 setup.transport_option[0] = '\0';
2912 dest_addr = rtp_c->from_addr;
2913 dest_addr.sin_port = htons(th->client_port_min);
2916 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2917 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2921 /* now everything is OK, so we can send the connection parameters */
2922 rtsp_reply_header(c, RTSP_STATUS_OK);
2924 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2926 switch(rtp_c->rtp_protocol) {
2927 case RTSP_PROTOCOL_RTP_UDP:
2928 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2929 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2930 "client_port=%d-%d;server_port=%d-%d",
2931 th->client_port_min, th->client_port_min + 1,
2934 case RTSP_PROTOCOL_RTP_TCP:
2935 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2936 stream_index * 2, stream_index * 2 + 1);
2941 if (setup.transport_option[0] != '\0')
2942 url_fprintf(c->pb, ";%s", setup.transport_option);
2943 url_fprintf(c->pb, "\r\n");
2946 url_fprintf(c->pb, "\r\n");
2950 /* find an rtp connection by using the session ID. Check consistency
2952 static HTTPContext *find_rtp_session_with_url(const char *url,
2953 const char *session_id)
2961 rtp_c = find_rtp_session(session_id);
2965 /* find which url is asked */
2966 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2970 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2971 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2972 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2973 rtp_c->stream->filename, s);
2974 if(!strncmp(path, buf, sizeof(buf))) {
2975 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2982 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2986 rtp_c = find_rtp_session_with_url(url, h->session_id);
2988 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2992 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2993 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2994 rtp_c->state != HTTPSTATE_READY) {
2995 rtsp_reply_error(c, RTSP_STATUS_STATE);
3000 /* XXX: seek in stream */
3001 if (h->range_start != AV_NOPTS_VALUE) {
3002 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3003 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3007 rtp_c->state = HTTPSTATE_SEND_DATA;
3009 /* now everything is OK, so we can send the connection parameters */
3010 rtsp_reply_header(c, RTSP_STATUS_OK);
3012 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3013 url_fprintf(c->pb, "\r\n");
3016 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3020 rtp_c = find_rtp_session_with_url(url, h->session_id);
3022 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3026 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3027 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3028 rtsp_reply_error(c, RTSP_STATUS_STATE);
3032 rtp_c->state = HTTPSTATE_READY;
3033 rtp_c->first_pts = AV_NOPTS_VALUE;
3034 /* now everything is OK, so we can send the connection parameters */
3035 rtsp_reply_header(c, RTSP_STATUS_OK);
3037 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3038 url_fprintf(c->pb, "\r\n");
3041 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3044 char session_id[32];
3046 rtp_c = find_rtp_session_with_url(url, h->session_id);
3048 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3052 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3054 /* abort the session */
3055 close_connection(rtp_c);
3057 /* now everything is OK, so we can send the connection parameters */
3058 rtsp_reply_header(c, RTSP_STATUS_OK);
3060 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3061 url_fprintf(c->pb, "\r\n");
3065 /********************************************************************/
3068 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3069 FFStream *stream, const char *session_id,
3070 enum RTSPProtocol rtp_protocol)
3072 HTTPContext *c = NULL;
3073 const char *proto_str;
3075 /* XXX: should output a warning page when coming
3076 close to the connection limit */
3077 if (nb_connections >= nb_max_connections)
3080 /* add a new connection */
3081 c = av_mallocz(sizeof(HTTPContext));
3086 c->poll_entry = NULL;
3087 c->from_addr = *from_addr;
3088 c->buffer_size = IOBUFFER_INIT_SIZE;
3089 c->buffer = av_malloc(c->buffer_size);
3094 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3095 c->state = HTTPSTATE_READY;
3096 c->is_packetized = 1;
3097 c->rtp_protocol = rtp_protocol;
3099 /* protocol is shown in statistics */
3100 switch(c->rtp_protocol) {
3101 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3102 proto_str = "MCAST";
3104 case RTSP_PROTOCOL_RTP_UDP:
3107 case RTSP_PROTOCOL_RTP_TCP:
3114 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3115 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3117 current_bandwidth += stream->bandwidth;
3119 c->next = first_http_ctx;
3131 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3132 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3134 static int rtp_new_av_stream(HTTPContext *c,
3135 int stream_index, struct sockaddr_in *dest_addr,
3136 HTTPContext *rtsp_c)
3138 AVFormatContext *ctx;
3141 URLContext *h = NULL;
3144 int max_packet_size;
3146 /* now we can open the relevant output stream */
3147 ctx = av_alloc_format_context();
3150 ctx->oformat = guess_format("rtp", NULL, NULL);
3152 st = av_mallocz(sizeof(AVStream));
3155 st->codec= avcodec_alloc_context();
3156 ctx->nb_streams = 1;
3157 ctx->streams[0] = st;
3159 if (!c->stream->feed ||
3160 c->stream->feed == c->stream)
3161 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3164 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3166 st->priv_data = NULL;
3168 /* build destination RTP address */
3169 ipaddr = inet_ntoa(dest_addr->sin_addr);
3171 switch(c->rtp_protocol) {
3172 case RTSP_PROTOCOL_RTP_UDP:
3173 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3176 /* XXX: also pass as parameter to function ? */
3177 if (c->stream->is_multicast) {
3179 ttl = c->stream->multicast_ttl;
3182 snprintf(ctx->filename, sizeof(ctx->filename),
3183 "rtp://%s:%d?multicast=1&ttl=%d",
3184 ipaddr, ntohs(dest_addr->sin_port), ttl);
3186 snprintf(ctx->filename, sizeof(ctx->filename),
3187 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3190 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3192 c->rtp_handles[stream_index] = h;
3193 max_packet_size = url_get_max_packet_size(h);
3195 case RTSP_PROTOCOL_RTP_TCP:
3198 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3204 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3205 ipaddr, ntohs(dest_addr->sin_port),
3207 c->stream->filename, stream_index, c->protocol);
3209 /* normally, no packets should be output here, but the packet size may be checked */
3210 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3211 /* XXX: close stream */
3214 av_set_parameters(ctx, NULL);
3215 if (av_write_header(ctx) < 0) {
3222 url_close_dyn_buf(ctx->pb, &dummy_buf);
3225 c->rtp_ctx[stream_index] = ctx;
3229 /********************************************************************/
3230 /* ffserver initialization */
3232 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3236 fst = av_mallocz(sizeof(AVStream));
3239 fst->codec= avcodec_alloc_context();
3240 fst->priv_data = av_mallocz(sizeof(FeedData));
3241 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3242 fst->index = stream->nb_streams;
3243 av_set_pts_info(fst, 33, 1, 90000);
3244 stream->streams[stream->nb_streams++] = fst;
3248 /* return the stream number in the feed */
3249 static int add_av_stream(FFStream *feed, AVStream *st)
3252 AVCodecContext *av, *av1;
3256 for(i=0;i<feed->nb_streams;i++) {
3257 st = feed->streams[i];
3259 if (av1->codec_id == av->codec_id &&
3260 av1->codec_type == av->codec_type &&
3261 av1->bit_rate == av->bit_rate) {
3263 switch(av->codec_type) {
3264 case CODEC_TYPE_AUDIO:
3265 if (av1->channels == av->channels &&
3266 av1->sample_rate == av->sample_rate)
3269 case CODEC_TYPE_VIDEO:
3270 if (av1->width == av->width &&
3271 av1->height == av->height &&
3272 av1->time_base.den == av->time_base.den &&
3273 av1->time_base.num == av->time_base.num &&
3274 av1->gop_size == av->gop_size)
3283 fst = add_av_stream1(feed, av);
3286 return feed->nb_streams - 1;
3291 static void remove_stream(FFStream *stream)
3295 while (*ps != NULL) {
3303 /* specific mpeg4 handling : we extract the raw parameters */
3304 static void extract_mpeg4_header(AVFormatContext *infile)
3306 int mpeg4_count, i, size;
3312 for(i=0;i<infile->nb_streams;i++) {
3313 st = infile->streams[i];
3314 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3315 st->codec->extradata_size == 0) {
3322 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3323 while (mpeg4_count > 0) {
3324 if (av_read_packet(infile, &pkt) < 0)
3326 st = infile->streams[pkt.stream_index];
3327 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3328 st->codec->extradata_size == 0) {
3329 av_freep(&st->codec->extradata);
3330 /* fill extradata with the header */
3331 /* XXX: we make hard suppositions here ! */
3333 while (p < pkt.data + pkt.size - 4) {
3334 /* stop when vop header is found */
3335 if (p[0] == 0x00 && p[1] == 0x00 &&
3336 p[2] == 0x01 && p[3] == 0xb6) {
3337 size = p - pkt.data;
3338 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3339 st->codec->extradata = av_malloc(size);
3340 st->codec->extradata_size = size;
3341 memcpy(st->codec->extradata, pkt.data, size);
3348 av_free_packet(&pkt);
3352 /* compute the needed AVStream for each file */
3353 static void build_file_streams(void)
3355 FFStream *stream, *stream_next;
3356 AVFormatContext *infile;
3359 /* gather all streams */
3360 for(stream = first_stream; stream != NULL; stream = stream_next) {
3361 stream_next = stream->next;
3362 if (stream->stream_type == STREAM_TYPE_LIVE &&
3364 /* the stream comes from a file */
3365 /* try to open the file */
3367 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3368 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3369 /* specific case : if transport stream output to RTP,
3370 we use a raw transport stream reader */
3371 stream->ap_in->mpeg2ts_raw = 1;
3372 stream->ap_in->mpeg2ts_compute_pcr = 1;
3375 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3376 stream->ifmt, 0, stream->ap_in)) < 0) {
3377 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3378 /* remove stream (no need to spend more time on it) */
3380 remove_stream(stream);
3382 /* find all the AVStreams inside and reference them in
3384 if (av_find_stream_info(infile) < 0) {
3385 http_log("Could not find codec parameters from '%s'\n",
3386 stream->feed_filename);
3387 av_close_input_file(infile);
3390 extract_mpeg4_header(infile);
3392 for(i=0;i<infile->nb_streams;i++)
3393 add_av_stream1(stream, infile->streams[i]->codec);
3395 av_close_input_file(infile);
3401 /* compute the needed AVStream for each feed */
3402 static void build_feed_streams(void)
3404 FFStream *stream, *feed;
3407 /* gather all streams */
3408 for(stream = first_stream; stream != NULL; stream = stream->next) {
3409 feed = stream->feed;
3411 if (!stream->is_feed) {
3412 /* we handle a stream coming from a feed */
3413 for(i=0;i<stream->nb_streams;i++)
3414 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3419 /* gather all streams */
3420 for(stream = first_stream; stream != NULL; stream = stream->next) {
3421 feed = stream->feed;
3423 if (stream->is_feed) {
3424 for(i=0;i<stream->nb_streams;i++)
3425 stream->feed_streams[i] = i;
3430 /* create feed files if needed */
3431 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3434 if (url_exist(feed->feed_filename)) {
3435 /* See if it matches */
3439 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3440 /* Now see if it matches */
3441 if (s->nb_streams == feed->nb_streams) {
3443 for(i=0;i<s->nb_streams;i++) {
3445 sf = feed->streams[i];
3448 if (sf->index != ss->index ||
3450 printf("Index & Id do not match for stream %d (%s)\n",
3451 i, feed->feed_filename);
3454 AVCodecContext *ccf, *ccs;
3458 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3460 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3461 printf("Codecs do not match for stream %d\n", i);
3463 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3464 printf("Codec bitrates do not match for stream %d\n", i);
3466 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3467 if (CHECK_CODEC(time_base.den) ||
3468 CHECK_CODEC(time_base.num) ||
3469 CHECK_CODEC(width) ||
3470 CHECK_CODEC(height)) {
3471 printf("Codec width, height and framerate do not match for stream %d\n", i);
3474 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3475 if (CHECK_CODEC(sample_rate) ||
3476 CHECK_CODEC(channels) ||
3477 CHECK_CODEC(frame_size)) {
3478 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3482 printf("Unknown codec type\n");
3490 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3491 feed->feed_filename, s->nb_streams, feed->nb_streams);
3493 av_close_input_file(s);
3495 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3496 feed->feed_filename);
3499 if (feed->readonly) {
3500 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3501 feed->feed_filename);
3504 unlink(feed->feed_filename);
3507 if (!url_exist(feed->feed_filename)) {
3508 AVFormatContext s1, *s = &s1;
3510 if (feed->readonly) {
3511 printf("Unable to create feed file '%s' as it is marked readonly\n",
3512 feed->feed_filename);
3516 /* only write the header of the ffm file */
3517 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3518 http_log("Could not open output feed file '%s'\n",
3519 feed->feed_filename);
3522 s->oformat = feed->fmt;
3523 s->nb_streams = feed->nb_streams;
3524 for(i=0;i<s->nb_streams;i++) {
3526 st = feed->streams[i];
3529 av_set_parameters(s, NULL);
3530 if (av_write_header(s) < 0) {
3531 http_log("Container doesn't supports the required parameters\n");
3534 /* XXX: need better api */
3535 av_freep(&s->priv_data);
3538 /* get feed size and write index */
3539 fd = open(feed->feed_filename, O_RDONLY);
3541 http_log("Could not open output feed file '%s'\n",
3542 feed->feed_filename);
3546 feed->feed_write_index = ffm_read_write_index(fd);
3547 feed->feed_size = lseek(fd, 0, SEEK_END);
3548 /* ensure that we do not wrap before the end of file */
3549 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3550 feed->feed_max_size = feed->feed_size;
3556 /* compute the bandwidth used by each stream */
3557 static void compute_bandwidth(void)
3563 for(stream = first_stream; stream != NULL; stream = stream->next) {
3565 for(i=0;i<stream->nb_streams;i++) {
3566 AVStream *st = stream->streams[i];
3567 switch(st->codec->codec_type) {
3568 case CODEC_TYPE_AUDIO:
3569 case CODEC_TYPE_VIDEO:
3570 bandwidth += st->codec->bit_rate;
3576 stream->bandwidth = (bandwidth + 999) / 1000;
3580 static void get_arg(char *buf, int buf_size, const char **pp)
3587 while (isspace(*p)) p++;
3590 if (*p == '\"' || *p == '\'')
3602 if ((q - buf) < buf_size - 1)
3607 if (quote && *p == quote)
3612 /* add a codec and set the default parameters */
3613 static void add_codec(FFStream *stream, AVCodecContext *av)
3617 /* compute default parameters */
3618 switch(av->codec_type) {
3619 case CODEC_TYPE_AUDIO:
3620 if (av->bit_rate == 0)
3621 av->bit_rate = 64000;
3622 if (av->sample_rate == 0)
3623 av->sample_rate = 22050;
3624 if (av->channels == 0)
3627 case CODEC_TYPE_VIDEO:
3628 if (av->bit_rate == 0)
3629 av->bit_rate = 64000;
3630 if (av->time_base.num == 0){
3631 av->time_base.den = 5;
3632 av->time_base.num = 1;
3634 if (av->width == 0 || av->height == 0) {
3638 /* Bitrate tolerance is less for streaming */
3639 if (av->bit_rate_tolerance == 0)
3640 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3641 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3646 if (av->max_qdiff == 0)
3648 av->qcompress = 0.5;
3651 if (!av->nsse_weight)
3652 av->nsse_weight = 8;
3654 av->frame_skip_cmp = FF_CMP_DCTMAX;
3655 av->me_method = ME_EPZS;
3656 av->rc_buffer_aggressivity = 1.0;
3659 av->rc_eq = "tex^qComp";
3660 if (!av->i_quant_factor)
3661 av->i_quant_factor = -0.8;
3662 if (!av->b_quant_factor)
3663 av->b_quant_factor = 1.25;
3664 if (!av->b_quant_offset)
3665 av->b_quant_offset = 1.25;
3666 if (!av->rc_max_rate)
3667 av->rc_max_rate = av->bit_rate * 2;
3669 if (av->rc_max_rate && !av->rc_buffer_size) {
3670 av->rc_buffer_size = av->rc_max_rate;
3679 st = av_mallocz(sizeof(AVStream));
3682 st->codec = avcodec_alloc_context();
3683 stream->streams[stream->nb_streams++] = st;
3684 memcpy(st->codec, av, sizeof(AVCodecContext));
3687 static int opt_audio_codec(const char *arg)
3689 AVCodec *p= avcodec_find_encoder_by_name(arg);
3691 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3692 return CODEC_ID_NONE;
3697 static int opt_video_codec(const char *arg)
3699 AVCodec *p= avcodec_find_encoder_by_name(arg);
3701 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3702 return CODEC_ID_NONE;
3707 /* simplistic plugin support */
3710 static void load_module(const char *filename)
3713 void (*init_func)(void);
3714 dll = dlopen(filename, RTLD_NOW);
3716 fprintf(stderr, "Could not load module '%s' - %s\n",
3717 filename, dlerror());
3721 init_func = dlsym(dll, "ffserver_module_init");
3724 "%s: init function 'ffserver_module_init()' not found\n",
3733 static int opt_default(const char *opt, const char *arg,
3734 AVCodecContext *avctx, int type)
3736 const AVOption *o = NULL;
3737 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3739 o = av_set_string(avctx, opt, arg);
3745 static int parse_ffconfig(const char *filename)
3752 int val, errors, line_num;
3753 FFStream **last_stream, *stream, *redirect;
3754 FFStream **last_feed, *feed;
3755 AVCodecContext audio_enc, video_enc;
3756 int audio_id, video_id;
3758 f = fopen(filename, "r");
3766 first_stream = NULL;
3767 last_stream = &first_stream;
3769 last_feed = &first_feed;
3773 audio_id = CODEC_ID_NONE;
3774 video_id = CODEC_ID_NONE;
3776 if (fgets(line, sizeof(line), f) == NULL)
3782 if (*p == '\0' || *p == '#')
3785 get_arg(cmd, sizeof(cmd), &p);
3787 if (!strcasecmp(cmd, "Port")) {
3788 get_arg(arg, sizeof(arg), &p);
3790 if (val < 1 || val > 65536) {
3791 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3792 filename, line_num, arg);
3795 my_http_addr.sin_port = htons(val);
3796 } else if (!strcasecmp(cmd, "BindAddress")) {
3797 get_arg(arg, sizeof(arg), &p);
3798 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3799 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3800 filename, line_num, arg);
3803 } else if (!strcasecmp(cmd, "NoDaemon")) {
3804 ffserver_daemon = 0;
3805 } else if (!strcasecmp(cmd, "RTSPPort")) {
3806 get_arg(arg, sizeof(arg), &p);
3808 if (val < 1 || val > 65536) {
3809 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3810 filename, line_num, arg);
3813 my_rtsp_addr.sin_port = htons(atoi(arg));
3814 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3815 get_arg(arg, sizeof(arg), &p);
3816 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3817 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3818 filename, line_num, arg);
3821 } else if (!strcasecmp(cmd, "MaxClients")) {
3822 get_arg(arg, sizeof(arg), &p);
3824 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3825 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3826 filename, line_num, arg);
3829 nb_max_connections = val;
3831 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3833 get_arg(arg, sizeof(arg), &p);
3835 if (llval < 10 || llval > 10000000) {
3836 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3837 filename, line_num, arg);
3840 max_bandwidth = llval;
3841 } else if (!strcasecmp(cmd, "CustomLog")) {
3842 if (!ffserver_debug)
3843 get_arg(logfilename, sizeof(logfilename), &p);
3844 } else if (!strcasecmp(cmd, "<Feed")) {
3845 /*********************************************/
3846 /* Feed related options */
3848 if (stream || feed) {
3849 fprintf(stderr, "%s:%d: Already in a tag\n",
3850 filename, line_num);
3852 feed = av_mallocz(sizeof(FFStream));
3853 /* add in stream list */
3854 *last_stream = feed;
3855 last_stream = &feed->next;
3856 /* add in feed list */
3858 last_feed = &feed->next_feed;
3860 get_arg(feed->filename, sizeof(feed->filename), &p);
3861 q = strrchr(feed->filename, '>');
3864 feed->fmt = guess_format("ffm", NULL, NULL);
3865 /* defaut feed file */
3866 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3867 "/tmp/%s.ffm", feed->filename);
3868 feed->feed_max_size = 5 * 1024 * 1024;
3870 feed->feed = feed; /* self feeding :-) */
3872 } else if (!strcasecmp(cmd, "Launch")) {
3876 feed->child_argv = av_mallocz(64 * sizeof(char *));
3878 for (i = 0; i < 62; i++) {
3879 get_arg(arg, sizeof(arg), &p);
3883 feed->child_argv[i] = av_strdup(arg);
3886 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3888 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3890 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3891 inet_ntoa(my_http_addr.sin_addr),
3892 ntohs(my_http_addr.sin_port), feed->filename);
3897 fprintf(stdout, "Launch commandline: ");
3898 for (j = 0; j <= i; j++)
3899 fprintf(stdout, "%s ", feed->child_argv[j]);
3900 fprintf(stdout, "\n");
3903 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3905 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3907 } else if (stream) {
3908 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3910 } else if (!strcasecmp(cmd, "File")) {
3912 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3914 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3915 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3920 get_arg(arg, sizeof(arg), &p);
3922 fsize = strtod(p1, &p1);
3923 switch(toupper(*p1)) {
3928 fsize *= 1024 * 1024;
3931 fsize *= 1024 * 1024 * 1024;
3934 feed->feed_max_size = (int64_t)fsize;
3936 } else if (!strcasecmp(cmd, "</Feed>")) {
3938 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3939 filename, line_num);
3943 } else if (!strcasecmp(cmd, "<Stream")) {
3944 /*********************************************/
3945 /* Stream related options */
3947 if (stream || feed) {
3948 fprintf(stderr, "%s:%d: Already in a tag\n",
3949 filename, line_num);
3951 const AVClass *class;
3952 stream = av_mallocz(sizeof(FFStream));
3953 *last_stream = stream;
3954 last_stream = &stream->next;
3956 get_arg(stream->filename, sizeof(stream->filename), &p);
3957 q = strrchr(stream->filename, '>');
3960 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3961 /* fetch avclass so AVOption works
3962 * FIXME try to use avcodec_get_context_defaults2
3963 * without changing defaults too much */
3964 avcodec_get_context_defaults(&video_enc);
3965 class = video_enc.av_class;
3966 memset(&audio_enc, 0, sizeof(AVCodecContext));
3967 memset(&video_enc, 0, sizeof(AVCodecContext));
3968 audio_enc.av_class = class;
3969 video_enc.av_class = class;
3970 audio_id = CODEC_ID_NONE;
3971 video_id = CODEC_ID_NONE;
3973 audio_id = stream->fmt->audio_codec;
3974 video_id = stream->fmt->video_codec;
3977 } else if (!strcasecmp(cmd, "Feed")) {
3978 get_arg(arg, sizeof(arg), &p);
3983 while (sfeed != NULL) {
3984 if (!strcmp(sfeed->filename, arg))
3986 sfeed = sfeed->next_feed;
3989 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3990 filename, line_num, arg);
3992 stream->feed = sfeed;
3994 } else if (!strcasecmp(cmd, "Format")) {
3995 get_arg(arg, sizeof(arg), &p);
3997 if (!strcmp(arg, "status")) {
3998 stream->stream_type = STREAM_TYPE_STATUS;
4001 stream->stream_type = STREAM_TYPE_LIVE;
4002 /* jpeg cannot be used here, so use single frame jpeg */
4003 if (!strcmp(arg, "jpeg"))
4004 strcpy(arg, "mjpeg");
4005 stream->fmt = guess_stream_format(arg, NULL, NULL);
4007 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4008 filename, line_num, arg);
4013 audio_id = stream->fmt->audio_codec;
4014 video_id = stream->fmt->video_codec;
4017 } else if (!strcasecmp(cmd, "InputFormat")) {
4018 get_arg(arg, sizeof(arg), &p);
4019 stream->ifmt = av_find_input_format(arg);
4020 if (!stream->ifmt) {
4021 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4022 filename, line_num, arg);
4024 } else if (!strcasecmp(cmd, "FaviconURL")) {
4025 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4026 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4028 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4029 filename, line_num);
4032 } else if (!strcasecmp(cmd, "Author")) {
4034 get_arg(stream->author, sizeof(stream->author), &p);
4035 } else if (!strcasecmp(cmd, "Comment")) {
4037 get_arg(stream->comment, sizeof(stream->comment), &p);
4038 } else if (!strcasecmp(cmd, "Copyright")) {
4040 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4041 } else if (!strcasecmp(cmd, "Title")) {
4043 get_arg(stream->title, sizeof(stream->title), &p);
4044 } else if (!strcasecmp(cmd, "Preroll")) {
4045 get_arg(arg, sizeof(arg), &p);
4047 stream->prebuffer = atof(arg) * 1000;
4048 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4050 stream->send_on_key = 1;
4051 } else if (!strcasecmp(cmd, "AudioCodec")) {
4052 get_arg(arg, sizeof(arg), &p);
4053 audio_id = opt_audio_codec(arg);
4054 if (audio_id == CODEC_ID_NONE) {
4055 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4056 filename, line_num, arg);
4059 } else if (!strcasecmp(cmd, "VideoCodec")) {
4060 get_arg(arg, sizeof(arg), &p);
4061 video_id = opt_video_codec(arg);
4062 if (video_id == CODEC_ID_NONE) {
4063 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4064 filename, line_num, arg);
4067 } else if (!strcasecmp(cmd, "MaxTime")) {
4068 get_arg(arg, sizeof(arg), &p);
4070 stream->max_time = atof(arg) * 1000;
4071 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4072 get_arg(arg, sizeof(arg), &p);
4074 audio_enc.bit_rate = atoi(arg) * 1000;
4075 } else if (!strcasecmp(cmd, "AudioChannels")) {
4076 get_arg(arg, sizeof(arg), &p);
4078 audio_enc.channels = atoi(arg);
4079 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4080 get_arg(arg, sizeof(arg), &p);
4082 audio_enc.sample_rate = atoi(arg);
4083 } else if (!strcasecmp(cmd, "AudioQuality")) {
4084 get_arg(arg, sizeof(arg), &p);
4086 // audio_enc.quality = atof(arg) * 1000;
4088 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4090 int minrate, maxrate;
4092 get_arg(arg, sizeof(arg), &p);
4094 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4095 video_enc.rc_min_rate = minrate * 1000;
4096 video_enc.rc_max_rate = maxrate * 1000;
4098 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4099 filename, line_num, arg);
4103 } else if (!strcasecmp(cmd, "Debug")) {
4105 get_arg(arg, sizeof(arg), &p);
4106 video_enc.debug = strtol(arg,0,0);
4108 } else if (!strcasecmp(cmd, "Strict")) {
4110 get_arg(arg, sizeof(arg), &p);
4111 video_enc.strict_std_compliance = atoi(arg);
4113 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4115 get_arg(arg, sizeof(arg), &p);
4116 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4118 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4120 get_arg(arg, sizeof(arg), &p);
4121 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4123 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4124 get_arg(arg, sizeof(arg), &p);
4126 video_enc.bit_rate = atoi(arg) * 1000;
4128 } else if (!strcasecmp(cmd, "VideoSize")) {
4129 get_arg(arg, sizeof(arg), &p);
4131 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4132 if ((video_enc.width % 16) != 0 ||
4133 (video_enc.height % 16) != 0) {
4134 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4135 filename, line_num);
4139 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4140 get_arg(arg, sizeof(arg), &p);
4142 AVRational frame_rate;
4143 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4144 fprintf(stderr, "Incorrect frame rate\n");
4147 video_enc.time_base.num = frame_rate.den;
4148 video_enc.time_base.den = frame_rate.num;
4151 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4152 get_arg(arg, sizeof(arg), &p);
4154 video_enc.gop_size = atoi(arg);
4155 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4157 video_enc.gop_size = 1;
4158 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4160 video_enc.mb_decision = FF_MB_DECISION_BITS;
4161 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4163 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4164 video_enc.flags |= CODEC_FLAG_4MV;
4166 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4167 !strcasecmp(cmd, "AVOptionAudio")) {
4169 AVCodecContext *avctx;
4171 get_arg(arg, sizeof(arg), &p);
4172 get_arg(arg2, sizeof(arg2), &p);
4173 if (!strcasecmp(cmd, "AVOptionVideo")) {
4175 type = AV_OPT_FLAG_VIDEO_PARAM;
4178 type = AV_OPT_FLAG_AUDIO_PARAM;
4180 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4181 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4184 } else if (!strcasecmp(cmd, "VideoTag")) {
4185 get_arg(arg, sizeof(arg), &p);
4186 if ((strlen(arg) == 4) && stream)
4187 video_enc.codec_tag = ff_get_fourcc(arg);
4188 } else if (!strcasecmp(cmd, "BitExact")) {
4190 video_enc.flags |= CODEC_FLAG_BITEXACT;
4191 } else if (!strcasecmp(cmd, "DctFastint")) {
4193 video_enc.dct_algo = FF_DCT_FASTINT;
4194 } else if (!strcasecmp(cmd, "IdctSimple")) {
4196 video_enc.idct_algo = FF_IDCT_SIMPLE;
4197 } else if (!strcasecmp(cmd, "Qscale")) {
4198 get_arg(arg, sizeof(arg), &p);
4200 video_enc.flags |= CODEC_FLAG_QSCALE;
4201 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4203 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4204 get_arg(arg, sizeof(arg), &p);
4206 video_enc.max_qdiff = atoi(arg);
4207 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4208 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4209 filename, line_num);
4213 } else if (!strcasecmp(cmd, "VideoQMax")) {
4214 get_arg(arg, sizeof(arg), &p);
4216 video_enc.qmax = atoi(arg);
4217 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4218 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4219 filename, line_num);
4223 } else if (!strcasecmp(cmd, "VideoQMin")) {
4224 get_arg(arg, sizeof(arg), &p);
4226 video_enc.qmin = atoi(arg);
4227 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4228 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4229 filename, line_num);
4233 } else if (!strcasecmp(cmd, "LumaElim")) {
4234 get_arg(arg, sizeof(arg), &p);
4236 video_enc.luma_elim_threshold = atoi(arg);
4237 } else if (!strcasecmp(cmd, "ChromaElim")) {
4238 get_arg(arg, sizeof(arg), &p);
4240 video_enc.chroma_elim_threshold = atoi(arg);
4241 } else if (!strcasecmp(cmd, "LumiMask")) {
4242 get_arg(arg, sizeof(arg), &p);
4244 video_enc.lumi_masking = atof(arg);
4245 } else if (!strcasecmp(cmd, "DarkMask")) {
4246 get_arg(arg, sizeof(arg), &p);
4248 video_enc.dark_masking = atof(arg);
4249 } else if (!strcasecmp(cmd, "NoVideo")) {
4250 video_id = CODEC_ID_NONE;
4251 } else if (!strcasecmp(cmd, "NoAudio")) {
4252 audio_id = CODEC_ID_NONE;
4253 } else if (!strcasecmp(cmd, "ACL")) {
4256 get_arg(arg, sizeof(arg), &p);
4257 if (strcasecmp(arg, "allow") == 0)
4258 acl.action = IP_ALLOW;
4259 else if (strcasecmp(arg, "deny") == 0)
4260 acl.action = IP_DENY;
4262 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4263 filename, line_num, arg);
4267 get_arg(arg, sizeof(arg), &p);
4269 if (resolve_host(&acl.first, arg) != 0) {
4270 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4271 filename, line_num, arg);
4274 acl.last = acl.first;
4276 get_arg(arg, sizeof(arg), &p);
4279 if (resolve_host(&acl.last, arg) != 0) {
4280 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4281 filename, line_num, arg);
4287 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4288 IPAddressACL **naclp = 0;
4294 naclp = &stream->acl;
4298 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4299 filename, line_num);
4305 naclp = &(*naclp)->next;
4310 } else if (!strcasecmp(cmd, "RTSPOption")) {
4311 get_arg(arg, sizeof(arg), &p);
4313 av_freep(&stream->rtsp_option);
4314 stream->rtsp_option = av_strdup(arg);
4316 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4317 get_arg(arg, sizeof(arg), &p);
4319 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4320 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4321 filename, line_num, arg);
4324 stream->is_multicast = 1;
4325 stream->loop = 1; /* default is looping */
4327 } else if (!strcasecmp(cmd, "MulticastPort")) {
4328 get_arg(arg, sizeof(arg), &p);
4330 stream->multicast_port = atoi(arg);
4331 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4332 get_arg(arg, sizeof(arg), &p);
4334 stream->multicast_ttl = atoi(arg);
4335 } else if (!strcasecmp(cmd, "NoLoop")) {
4338 } else if (!strcasecmp(cmd, "</Stream>")) {
4340 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4341 filename, line_num);
4344 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4345 if (audio_id != CODEC_ID_NONE) {
4346 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4347 audio_enc.codec_id = audio_id;
4348 add_codec(stream, &audio_enc);
4350 if (video_id != CODEC_ID_NONE) {
4351 video_enc.codec_type = CODEC_TYPE_VIDEO;
4352 video_enc.codec_id = video_id;
4353 add_codec(stream, &video_enc);
4358 } else if (!strcasecmp(cmd, "<Redirect")) {
4359 /*********************************************/
4361 if (stream || feed || redirect) {
4362 fprintf(stderr, "%s:%d: Already in a tag\n",
4363 filename, line_num);
4366 redirect = av_mallocz(sizeof(FFStream));
4367 *last_stream = redirect;
4368 last_stream = &redirect->next;
4370 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4371 q = strrchr(redirect->filename, '>');
4374 redirect->stream_type = STREAM_TYPE_REDIRECT;
4376 } else if (!strcasecmp(cmd, "URL")) {
4378 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4379 } else if (!strcasecmp(cmd, "</Redirect>")) {
4381 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4382 filename, line_num);
4385 if (!redirect->feed_filename[0]) {
4386 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4387 filename, line_num);
4392 } else if (!strcasecmp(cmd, "LoadModule")) {
4393 get_arg(arg, sizeof(arg), &p);
4397 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4398 filename, line_num, arg);
4402 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4403 filename, line_num, cmd);
4415 static void handle_child_exit(int sig)
4420 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4423 for (feed = first_feed; feed; feed = feed->next) {
4424 if (feed->pid == pid) {
4425 int uptime = time(0) - feed->pid_start;
4428 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4431 /* Turn off any more restarts */
4432 feed->child_argv = 0;
4437 need_to_start_children = 1;
4440 static void opt_debug()
4443 ffserver_daemon = 0;
4444 logfilename[0] = '-';
4447 static void opt_show_help(void)
4449 printf("usage: ffserver [options]\n"
4450 "Hyper fast multi format Audio/Video streaming server\n");
4452 show_help_options(options, "Main options:\n", 0, 0);
4455 static const OptionDef options[] = {
4456 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4457 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4458 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4459 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4460 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4461 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4462 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4466 int main(int argc, char **argv)
4468 struct sigaction sigact;
4474 config_filename = "/etc/ffserver.conf";
4476 my_program_name = argv[0];
4477 my_program_dir = getcwd(0, 0);
4478 ffserver_daemon = 1;
4480 parse_options(argc, argv, options, NULL);
4482 unsetenv("http_proxy"); /* Kill the http_proxy */
4484 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4486 memset(&sigact, 0, sizeof(sigact));
4487 sigact.sa_handler = handle_child_exit;
4488 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4489 sigaction(SIGCHLD, &sigact, 0);
4491 if (parse_ffconfig(config_filename) < 0) {
4492 fprintf(stderr, "Incorrect config file - exiting.\n");
4496 build_file_streams();
4498 build_feed_streams();
4500 compute_bandwidth();
4502 /* put the process in background and detach it from its TTY */
4503 if (ffserver_daemon) {
4510 } else if (pid > 0) {
4518 open("/dev/null", O_RDWR);
4519 if (strcmp(logfilename, "-") != 0) {
4529 signal(SIGPIPE, SIG_IGN);
4531 /* open log file if needed */
4532 if (logfilename[0] != '\0') {
4533 if (!strcmp(logfilename, "-"))
4536 logfile = fopen(logfilename, "a");
4537 av_log_set_callback(http_av_log);
4540 if (http_server() < 0) {
4541 http_log("Could not start server\n");