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
21 #define HAVE_AV_CONFIG_H
27 #include <sys/ioctl.h>
31 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
33 #include <sys/types.h>
34 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
47 /* maximum number of simultaneous HTTP connections */
48 #define HTTP_MAX_CONNECTIONS 2000
51 HTTPSTATE_WAIT_REQUEST,
52 HTTPSTATE_SEND_HEADER,
53 HTTPSTATE_SEND_DATA_HEADER,
54 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
55 HTTPSTATE_SEND_DATA_TRAILER,
56 HTTPSTATE_RECEIVE_DATA,
57 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
60 RTSPSTATE_WAIT_REQUEST,
62 RTSPSTATE_SEND_PACKET,
65 const char *http_state[] = {
81 #define IOBUFFER_INIT_SIZE 8192
83 /* coef for exponential mean for bitrate estimation in statistics */
86 /* timeouts are in ms */
87 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
88 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
90 #define SYNC_TIMEOUT (10 * 1000)
93 int64_t count1, count2;
97 /* context associated with one connection */
98 typedef struct HTTPContext {
100 int fd; /* socket file descriptor */
101 struct sockaddr_in from_addr; /* origin */
102 struct pollfd *poll_entry; /* used when polling */
104 uint8_t *buffer_ptr, *buffer_end;
107 struct HTTPContext *next;
108 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
112 /* input format handling */
113 AVFormatContext *fmt_in;
114 long start_time; /* In milliseconds - this wraps fairly often */
115 int64_t first_pts; /* initial pts value */
116 int64_t cur_pts; /* current pts value from the stream in us */
117 int64_t cur_frame_duration; /* duration of the current frame in us */
118 int cur_frame_bytes; /* output frame size, needed to compute
119 the time at which we send each
121 int pts_stream_index; /* stream we choose as clock reference */
122 int64_t cur_clock; /* current clock reference value in us */
123 /* output format handling */
124 struct FFStream *stream;
125 /* -1 is invalid stream */
126 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
127 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
129 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
130 int last_packet_sent; /* true if last data packet was sent */
132 DataRateData datarate;
139 int is_packetized; /* if true, the stream is packetized */
140 int packet_stream_index; /* current stream for output in state machine */
142 /* RTSP state specific */
143 uint8_t *pb_buffer; /* XXX: use that in all the code */
145 int seq; /* RTSP sequence number */
147 /* RTP state specific */
148 enum RTSPProtocol rtp_protocol;
149 char session_id[32]; /* session id */
150 AVFormatContext *rtp_ctx[MAX_STREAMS];
152 /* RTP/UDP specific */
153 URLContext *rtp_handles[MAX_STREAMS];
155 /* RTP/TCP specific */
156 struct HTTPContext *rtsp_c;
157 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
160 static AVFrame dummy_frame;
162 /* each generated stream is described here */
166 STREAM_TYPE_REDIRECT,
169 enum IPAddressAction {
174 typedef struct IPAddressACL {
175 struct IPAddressACL *next;
176 enum IPAddressAction action;
177 /* These are in host order */
178 struct in_addr first;
182 /* description of each stream of the ffserver.conf file */
183 typedef struct FFStream {
184 enum StreamType stream_type;
185 char filename[1024]; /* stream filename */
186 struct FFStream *feed; /* feed we are using (can be null if
188 AVFormatParameters *ap_in; /* input parameters */
189 AVInputFormat *ifmt; /* if non NULL, force input format */
193 int prebuffer; /* Number of millseconds early to start */
194 long max_time; /* Number of milliseconds to run */
196 AVStream *streams[MAX_STREAMS];
197 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
198 char feed_filename[1024]; /* file name of the feed storage, or
199 input file name for a stream */
204 pid_t pid; /* Of ffmpeg process */
205 time_t pid_start; /* Of ffmpeg process */
207 struct FFStream *next;
208 int bandwidth; /* bandwidth, in kbits/s */
211 /* multicast specific */
213 struct in_addr multicast_ip;
214 int multicast_port; /* first port used for multicast */
216 int loop; /* if true, send the stream in loops (only meaningful if file) */
219 int feed_opened; /* true if someone is writing to the feed */
220 int is_feed; /* true if it is a feed */
221 int readonly; /* True if writing is prohibited to the file */
223 int64_t bytes_served;
224 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
225 int64_t feed_write_index; /* current write position in feed (it wraps round) */
226 int64_t feed_size; /* current size of feed */
227 struct FFStream *next_feed;
230 typedef struct FeedData {
231 long long data_count;
232 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
235 struct sockaddr_in my_http_addr;
236 struct sockaddr_in my_rtsp_addr;
238 static char logfilename[1024];
239 static HTTPContext *first_http_ctx;
240 static FFStream *first_feed; /* contains only feeds */
241 static FFStream *first_stream; /* contains all streams, including feeds */
243 static void new_connection(int server_fd, int is_rtsp);
244 static void close_connection(HTTPContext *c);
247 static int handle_connection(HTTPContext *c);
248 static int http_parse_request(HTTPContext *c);
249 static int http_send_data(HTTPContext *c);
250 static void compute_stats(HTTPContext *c);
251 static int open_input_stream(HTTPContext *c, const char *info);
252 static int http_start_receive_data(HTTPContext *c);
253 static int http_receive_data(HTTPContext *c);
256 static int rtsp_parse_request(HTTPContext *c);
257 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
258 static void rtsp_cmd_options(HTTPContext *c, const char *url);
259 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
260 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
261 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
262 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
265 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
266 struct in_addr my_ip);
269 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
270 FFStream *stream, const char *session_id,
271 enum RTSPProtocol rtp_protocol);
272 static int rtp_new_av_stream(HTTPContext *c,
273 int stream_index, struct sockaddr_in *dest_addr,
274 HTTPContext *rtsp_c);
276 static const char *my_program_name;
277 static const char *my_program_dir;
279 static int ffserver_debug;
280 static int ffserver_daemon;
281 static int no_launch;
282 static int need_to_start_children;
284 static int nb_max_connections;
285 static int nb_connections;
287 static int max_bandwidth;
288 static int current_bandwidth;
290 static long cur_time; // Making this global saves on passing it around everywhere
292 static long gettime_ms(void)
296 gettimeofday(&tv,NULL);
297 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
300 static FILE *logfile = NULL;
302 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
308 vfprintf(logfile, fmt, ap);
314 static char *ctime1(char *buf2)
322 p = buf2 + strlen(p) - 1;
328 static void log_connection(HTTPContext *c)
335 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
336 inet_ntoa(c->from_addr.sin_addr),
337 ctime1(buf2), c->method, c->url,
338 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
341 static void update_datarate(DataRateData *drd, int64_t count)
343 if (!drd->time1 && !drd->count1) {
344 drd->time1 = drd->time2 = cur_time;
345 drd->count1 = drd->count2 = count;
347 if (cur_time - drd->time2 > 5000) {
348 drd->time1 = drd->time2;
349 drd->count1 = drd->count2;
350 drd->time2 = cur_time;
356 /* In bytes per second */
357 static int compute_datarate(DataRateData *drd, int64_t count)
359 if (cur_time == drd->time1)
362 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
366 static void start_children(FFStream *feed)
371 for (; feed; feed = feed->next) {
372 if (feed->child_argv && !feed->pid) {
373 feed->pid_start = time(0);
378 fprintf(stderr, "Unable to create children\n");
387 for (i = 3; i < 256; i++) {
391 if (!ffserver_debug) {
392 i = open("/dev/null", O_RDWR);
401 pstrcpy(pathname, sizeof(pathname), my_program_name);
403 slash = strrchr(pathname, '/');
409 strcpy(slash, "ffmpeg");
411 /* This is needed to make relative pathnames work */
412 chdir(my_program_dir);
414 signal(SIGPIPE, SIG_DFL);
416 execvp(pathname, feed->child_argv);
424 /* open a listening socket */
425 static int socket_open_listen(struct sockaddr_in *my_addr)
429 server_fd = socket(AF_INET,SOCK_STREAM,0);
436 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
438 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
440 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
446 if (listen (server_fd, 5) < 0) {
451 fcntl(server_fd, F_SETFL, O_NONBLOCK);
456 /* start all multicast streams */
457 static void start_multicast(void)
462 struct sockaddr_in dest_addr;
463 int default_port, stream_index;
466 for(stream = first_stream; stream != NULL; stream = stream->next) {
467 if (stream->is_multicast) {
468 /* open the RTP connection */
469 snprintf(session_id, sizeof(session_id),
470 "%08x%08x", (int)random(), (int)random());
472 /* choose a port if none given */
473 if (stream->multicast_port == 0) {
474 stream->multicast_port = default_port;
478 dest_addr.sin_family = AF_INET;
479 dest_addr.sin_addr = stream->multicast_ip;
480 dest_addr.sin_port = htons(stream->multicast_port);
482 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
483 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
487 if (open_input_stream(rtp_c, "") < 0) {
488 fprintf(stderr, "Could not open input stream for stream '%s'\n",
493 /* open each RTP stream */
494 for(stream_index = 0; stream_index < stream->nb_streams;
496 dest_addr.sin_port = htons(stream->multicast_port +
498 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
499 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
500 stream->filename, stream_index);
505 /* change state to send data */
506 rtp_c->state = HTTPSTATE_SEND_DATA;
511 /* main loop of the http server */
512 static int http_server(void)
514 int server_fd, ret, rtsp_server_fd, delay, delay1;
515 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
516 HTTPContext *c, *c_next;
518 server_fd = socket_open_listen(&my_http_addr);
522 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
523 if (rtsp_server_fd < 0)
526 http_log("ffserver started.\n");
528 start_children(first_feed);
530 first_http_ctx = NULL;
536 poll_entry = poll_table;
537 poll_entry->fd = server_fd;
538 poll_entry->events = POLLIN;
541 poll_entry->fd = rtsp_server_fd;
542 poll_entry->events = POLLIN;
545 /* wait for events on each HTTP handle */
552 case HTTPSTATE_SEND_HEADER:
553 case RTSPSTATE_SEND_REPLY:
554 case RTSPSTATE_SEND_PACKET:
555 c->poll_entry = poll_entry;
557 poll_entry->events = POLLOUT;
560 case HTTPSTATE_SEND_DATA_HEADER:
561 case HTTPSTATE_SEND_DATA:
562 case HTTPSTATE_SEND_DATA_TRAILER:
563 if (!c->is_packetized) {
564 /* for TCP, we output as much as we can (may need to put a limit) */
565 c->poll_entry = poll_entry;
567 poll_entry->events = POLLOUT;
570 /* when ffserver is doing the timing, we work by
571 looking at which packet need to be sent every
573 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
578 case HTTPSTATE_WAIT_REQUEST:
579 case HTTPSTATE_RECEIVE_DATA:
580 case HTTPSTATE_WAIT_FEED:
581 case RTSPSTATE_WAIT_REQUEST:
582 /* need to catch errors */
583 c->poll_entry = poll_entry;
585 poll_entry->events = POLLIN;/* Maybe this will work */
589 c->poll_entry = NULL;
595 /* wait for an event on one connection. We poll at least every
596 second to handle timeouts */
598 ret = poll(poll_table, poll_entry - poll_table, delay);
599 if (ret < 0 && errno != EAGAIN && errno != EINTR)
603 cur_time = gettime_ms();
605 if (need_to_start_children) {
606 need_to_start_children = 0;
607 start_children(first_feed);
610 /* now handle the events */
611 for(c = first_http_ctx; c != NULL; c = c_next) {
613 if (handle_connection(c) < 0) {
614 /* close and free the connection */
620 poll_entry = poll_table;
621 /* new HTTP connection request ? */
622 if (poll_entry->revents & POLLIN) {
623 new_connection(server_fd, 0);
626 /* new RTSP connection request ? */
627 if (poll_entry->revents & POLLIN) {
628 new_connection(rtsp_server_fd, 1);
633 /* start waiting for a new HTTP/RTSP request */
634 static void start_wait_request(HTTPContext *c, int is_rtsp)
636 c->buffer_ptr = c->buffer;
637 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
640 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
641 c->state = RTSPSTATE_WAIT_REQUEST;
643 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
644 c->state = HTTPSTATE_WAIT_REQUEST;
648 static void new_connection(int server_fd, int is_rtsp)
650 struct sockaddr_in from_addr;
652 HTTPContext *c = NULL;
654 len = sizeof(from_addr);
655 fd = accept(server_fd, (struct sockaddr *)&from_addr,
659 fcntl(fd, F_SETFL, O_NONBLOCK);
661 /* XXX: should output a warning page when coming
662 close to the connection limit */
663 if (nb_connections >= nb_max_connections)
666 /* add a new connection */
667 c = av_mallocz(sizeof(HTTPContext));
672 c->poll_entry = NULL;
673 c->from_addr = from_addr;
674 c->buffer_size = IOBUFFER_INIT_SIZE;
675 c->buffer = av_malloc(c->buffer_size);
679 c->next = first_http_ctx;
683 start_wait_request(c, is_rtsp);
695 static void close_connection(HTTPContext *c)
697 HTTPContext **cp, *c1;
699 AVFormatContext *ctx;
703 /* remove connection from list */
704 cp = &first_http_ctx;
705 while ((*cp) != NULL) {
714 /* remove references, if any (XXX: do it faster) */
715 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
720 /* remove connection associated resources */
724 /* close each frame parser */
725 for(i=0;i<c->fmt_in->nb_streams;i++) {
726 st = c->fmt_in->streams[i];
727 if (st->codec->codec) {
728 avcodec_close(st->codec);
731 av_close_input_file(c->fmt_in);
734 /* free RTP output streams if any */
737 nb_streams = c->stream->nb_streams;
739 for(i=0;i<nb_streams;i++) {
742 av_write_trailer(ctx);
745 h = c->rtp_handles[i];
753 if (!c->last_packet_sent) {
756 if (url_open_dyn_buf(&ctx->pb) >= 0) {
757 av_write_trailer(ctx);
758 url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
763 for(i=0; i<ctx->nb_streams; i++)
764 av_free(ctx->streams[i]) ;
766 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
767 current_bandwidth -= c->stream->bandwidth;
768 av_freep(&c->pb_buffer);
769 av_freep(&c->packet_buffer);
775 static int handle_connection(HTTPContext *c)
780 case HTTPSTATE_WAIT_REQUEST:
781 case RTSPSTATE_WAIT_REQUEST:
783 if ((c->timeout - cur_time) < 0)
785 if (c->poll_entry->revents & (POLLERR | POLLHUP))
788 /* no need to read if no events */
789 if (!(c->poll_entry->revents & POLLIN))
793 len = read(c->fd, c->buffer_ptr, 1);
795 if (errno != EAGAIN && errno != EINTR)
797 } else if (len == 0) {
800 /* search for end of request. */
802 c->buffer_ptr += len;
804 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
805 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
806 /* request found : parse it and reply */
807 if (c->state == HTTPSTATE_WAIT_REQUEST) {
808 ret = http_parse_request(c);
810 ret = rtsp_parse_request(c);
814 } else if (ptr >= c->buffer_end) {
815 /* request too long: cannot do anything */
817 } else goto read_loop;
821 case HTTPSTATE_SEND_HEADER:
822 if (c->poll_entry->revents & (POLLERR | POLLHUP))
825 /* no need to write if no events */
826 if (!(c->poll_entry->revents & POLLOUT))
828 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
830 if (errno != EAGAIN && errno != EINTR) {
831 /* error : close connection */
832 av_freep(&c->pb_buffer);
836 c->buffer_ptr += len;
838 c->stream->bytes_served += len;
839 c->data_count += len;
840 if (c->buffer_ptr >= c->buffer_end) {
841 av_freep(&c->pb_buffer);
846 /* all the buffer was sent : synchronize to the incoming stream */
847 c->state = HTTPSTATE_SEND_DATA_HEADER;
848 c->buffer_ptr = c->buffer_end = c->buffer;
853 case HTTPSTATE_SEND_DATA:
854 case HTTPSTATE_SEND_DATA_HEADER:
855 case HTTPSTATE_SEND_DATA_TRAILER:
856 /* for packetized output, we consider we can always write (the
857 input streams sets the speed). It may be better to verify
858 that we do not rely too much on the kernel queues */
859 if (!c->is_packetized) {
860 if (c->poll_entry->revents & (POLLERR | POLLHUP))
863 /* no need to read if no events */
864 if (!(c->poll_entry->revents & POLLOUT))
867 if (http_send_data(c) < 0)
870 case HTTPSTATE_RECEIVE_DATA:
871 /* no need to read if no events */
872 if (c->poll_entry->revents & (POLLERR | POLLHUP))
874 if (!(c->poll_entry->revents & POLLIN))
876 if (http_receive_data(c) < 0)
879 case HTTPSTATE_WAIT_FEED:
880 /* no need to read if no events */
881 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
884 /* nothing to do, we'll be waken up by incoming feed packets */
887 case RTSPSTATE_SEND_REPLY:
888 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
889 av_freep(&c->pb_buffer);
892 /* no need to write if no events */
893 if (!(c->poll_entry->revents & POLLOUT))
895 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
897 if (errno != EAGAIN && errno != EINTR) {
898 /* error : close connection */
899 av_freep(&c->pb_buffer);
903 c->buffer_ptr += len;
904 c->data_count += len;
905 if (c->buffer_ptr >= c->buffer_end) {
906 /* all the buffer was sent : wait for a new request */
907 av_freep(&c->pb_buffer);
908 start_wait_request(c, 1);
912 case RTSPSTATE_SEND_PACKET:
913 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
914 av_freep(&c->packet_buffer);
917 /* no need to write if no events */
918 if (!(c->poll_entry->revents & POLLOUT))
920 len = write(c->fd, c->packet_buffer_ptr,
921 c->packet_buffer_end - c->packet_buffer_ptr);
923 if (errno != EAGAIN && errno != EINTR) {
924 /* error : close connection */
925 av_freep(&c->packet_buffer);
929 c->packet_buffer_ptr += len;
930 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
931 /* all the buffer was sent : wait for a new request */
932 av_freep(&c->packet_buffer);
933 c->state = RTSPSTATE_WAIT_REQUEST;
937 case HTTPSTATE_READY:
946 static int extract_rates(char *rates, int ratelen, const char *request)
950 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
951 if (strncasecmp(p, "Pragma:", 7) == 0) {
952 const char *q = p + 7;
954 while (*q && *q != '\n' && isspace(*q))
957 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
963 memset(rates, 0xff, ratelen);
966 while (*q && *q != '\n' && *q != ':')
969 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
973 if (stream_no < ratelen && stream_no >= 0) {
974 rates[stream_no] = rate_no;
977 while (*q && *q != '\n' && !isspace(*q))
994 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
997 int best_bitrate = 100000000;
1000 for (i = 0; i < feed->nb_streams; i++) {
1001 AVCodecContext *feed_codec = feed->streams[i]->codec;
1003 if (feed_codec->codec_id != codec->codec_id ||
1004 feed_codec->sample_rate != codec->sample_rate ||
1005 feed_codec->width != codec->width ||
1006 feed_codec->height != codec->height) {
1010 /* Potential stream */
1012 /* We want the fastest stream less than bit_rate, or the slowest
1013 * faster than bit_rate
1016 if (feed_codec->bit_rate <= bit_rate) {
1017 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1018 best_bitrate = feed_codec->bit_rate;
1022 if (feed_codec->bit_rate < best_bitrate) {
1023 best_bitrate = feed_codec->bit_rate;
1032 static int modify_current_stream(HTTPContext *c, char *rates)
1035 FFStream *req = c->stream;
1036 int action_required = 0;
1038 /* Not much we can do for a feed */
1042 for (i = 0; i < req->nb_streams; i++) {
1043 AVCodecContext *codec = req->streams[i]->codec;
1047 c->switch_feed_streams[i] = req->feed_streams[i];
1050 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1053 /* Wants off or slow */
1054 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1056 /* This doesn't work well when it turns off the only stream! */
1057 c->switch_feed_streams[i] = -2;
1058 c->feed_streams[i] = -2;
1063 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1064 action_required = 1;
1067 return action_required;
1071 static void do_switch_stream(HTTPContext *c, int i)
1073 if (c->switch_feed_streams[i] >= 0) {
1075 c->feed_streams[i] = c->switch_feed_streams[i];
1078 /* Now update the stream */
1080 c->switch_feed_streams[i] = -1;
1083 /* XXX: factorize in utils.c ? */
1084 /* XXX: take care with different space meaning */
1085 static void skip_spaces(const char **pp)
1089 while (*p == ' ' || *p == '\t')
1094 static void get_word(char *buf, int buf_size, const char **pp)
1102 while (!isspace(*p) && *p != '\0') {
1103 if ((q - buf) < buf_size - 1)
1112 static int validate_acl(FFStream *stream, HTTPContext *c)
1114 enum IPAddressAction last_action = IP_DENY;
1116 struct in_addr *src = &c->from_addr.sin_addr;
1117 unsigned long src_addr = ntohl(src->s_addr);
1119 for (acl = stream->acl; acl; acl = acl->next) {
1120 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1121 return (acl->action == IP_ALLOW) ? 1 : 0;
1123 last_action = acl->action;
1126 /* Nothing matched, so return not the last action */
1127 return (last_action == IP_DENY) ? 1 : 0;
1130 /* compute the real filename of a file by matching it without its
1131 extensions to all the stream filenames */
1132 static void compute_real_filename(char *filename, int max_size)
1139 /* compute filename by matching without the file extensions */
1140 pstrcpy(file1, sizeof(file1), filename);
1141 p = strrchr(file1, '.');
1144 for(stream = first_stream; stream != NULL; stream = stream->next) {
1145 pstrcpy(file2, sizeof(file2), stream->filename);
1146 p = strrchr(file2, '.');
1149 if (!strcmp(file1, file2)) {
1150 pstrcpy(filename, max_size, stream->filename);
1165 /* parse http request and prepare header */
1166 static int http_parse_request(HTTPContext *c)
1169 enum RedirType redir_type;
1171 char info[1024], *filename;
1175 const char *mime_type;
1179 char *useragent = 0;
1182 get_word(cmd, sizeof(cmd), (const char **)&p);
1183 pstrcpy(c->method, sizeof(c->method), cmd);
1185 if (!strcmp(cmd, "GET"))
1187 else if (!strcmp(cmd, "POST"))
1192 get_word(url, sizeof(url), (const char **)&p);
1193 pstrcpy(c->url, sizeof(c->url), url);
1195 get_word(protocol, sizeof(protocol), (const char **)&p);
1196 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1199 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1202 http_log("New connection: %s %s\n", cmd, url);
1204 /* find the filename and the optional info string in the request */
1211 pstrcpy(info, sizeof(info), p);
1217 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1218 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1220 if (*useragent && *useragent != '\n' && isspace(*useragent))
1224 p = strchr(p, '\n');
1231 redir_type = REDIR_NONE;
1232 if (match_ext(filename, "asx")) {
1233 redir_type = REDIR_ASX;
1234 filename[strlen(filename)-1] = 'f';
1235 } else if (match_ext(filename, "asf") &&
1236 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1237 /* if this isn't WMP or lookalike, return the redirector file */
1238 redir_type = REDIR_ASF;
1239 } else if (match_ext(filename, "rpm,ram")) {
1240 redir_type = REDIR_RAM;
1241 strcpy(filename + strlen(filename)-2, "m");
1242 } else if (match_ext(filename, "rtsp")) {
1243 redir_type = REDIR_RTSP;
1244 compute_real_filename(filename, sizeof(url) - 1);
1245 } else if (match_ext(filename, "sdp")) {
1246 redir_type = REDIR_SDP;
1247 compute_real_filename(filename, sizeof(url) - 1);
1250 stream = first_stream;
1251 while (stream != NULL) {
1252 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1254 stream = stream->next;
1256 if (stream == NULL) {
1257 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1262 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1263 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1265 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1266 c->http_error = 301;
1268 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1269 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1270 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1271 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1272 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1273 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1274 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1276 /* prepare output buffer */
1277 c->buffer_ptr = c->buffer;
1279 c->state = HTTPSTATE_SEND_HEADER;
1283 /* If this is WMP, get the rate information */
1284 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1285 if (modify_current_stream(c, ratebuf)) {
1286 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1287 if (c->switch_feed_streams[i] >= 0)
1288 do_switch_stream(c, i);
1293 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1294 current_bandwidth += stream->bandwidth;
1297 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1298 c->http_error = 200;
1300 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1301 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1302 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1303 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1304 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");
1305 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec.</p>\r\n",
1306 current_bandwidth, max_bandwidth);
1307 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1309 /* prepare output buffer */
1310 c->buffer_ptr = c->buffer;
1312 c->state = HTTPSTATE_SEND_HEADER;
1316 if (redir_type != REDIR_NONE) {
1319 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1320 if (strncasecmp(p, "Host:", 5) == 0) {
1324 p = strchr(p, '\n');
1335 while (isspace(*hostinfo))
1338 eoh = strchr(hostinfo, '\n');
1340 if (eoh[-1] == '\r')
1343 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1344 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1345 hostbuf[eoh - hostinfo] = 0;
1347 c->http_error = 200;
1349 switch(redir_type) {
1351 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1352 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1353 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1354 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1355 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1356 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1357 hostbuf, filename, info);
1358 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1362 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1363 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1364 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1365 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1366 hostbuf, filename, info);
1369 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1370 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1371 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1372 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1373 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1374 hostbuf, filename, info);
1378 char hostname[256], *p;
1379 /* extract only hostname */
1380 pstrcpy(hostname, sizeof(hostname), hostbuf);
1381 p = strrchr(hostname, ':');
1384 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1385 /* XXX: incorrect mime type ? */
1386 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1387 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1388 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1389 hostname, ntohs(my_rtsp_addr.sin_port),
1396 int sdp_data_size, len;
1397 struct sockaddr_in my_addr;
1399 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1400 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1401 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1403 len = sizeof(my_addr);
1404 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1406 /* XXX: should use a dynamic buffer */
1407 sdp_data_size = prepare_sdp_description(stream,
1410 if (sdp_data_size > 0) {
1411 memcpy(q, sdp_data, sdp_data_size);
1423 /* prepare output buffer */
1424 c->buffer_ptr = c->buffer;
1426 c->state = HTTPSTATE_SEND_HEADER;
1432 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1436 stream->conns_served++;
1438 /* XXX: add there authenticate and IP match */
1441 /* if post, it means a feed is being sent */
1442 if (!stream->is_feed) {
1443 /* However it might be a status report from WMP! Lets log the data
1444 * as it might come in handy one day
1449 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1450 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1454 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1455 client_id = strtol(p + 18, 0, 10);
1457 p = strchr(p, '\n');
1465 char *eol = strchr(logline, '\n');
1470 if (eol[-1] == '\r')
1472 http_log("%.*s\n", (int) (eol - logline), logline);
1473 c->suppress_log = 1;
1478 http_log("\nGot request:\n%s\n", c->buffer);
1481 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1484 /* Now we have to find the client_id */
1485 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1486 if (wmpc->wmp_client_id == client_id)
1491 if (modify_current_stream(wmpc, ratebuf)) {
1492 wmpc->switch_pending = 1;
1497 snprintf(msg, sizeof(msg), "POST command not handled");
1501 if (http_start_receive_data(c) < 0) {
1502 snprintf(msg, sizeof(msg), "could not open feed");
1506 c->state = HTTPSTATE_RECEIVE_DATA;
1511 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1512 http_log("\nGot request:\n%s\n", c->buffer);
1516 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1519 /* open input stream */
1520 if (open_input_stream(c, info) < 0) {
1521 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1525 /* prepare http header */
1527 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1528 mime_type = c->stream->fmt->mime_type;
1530 mime_type = "application/x-octet_stream";
1531 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1533 /* for asf, we need extra headers */
1534 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1535 /* Need to allocate a client id */
1537 c->wmp_client_id = random() & 0x7fffffff;
1539 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);
1541 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1542 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1544 /* prepare output buffer */
1546 c->buffer_ptr = c->buffer;
1548 c->state = HTTPSTATE_SEND_HEADER;
1551 c->http_error = 404;
1553 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1554 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1555 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1556 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1557 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1558 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1559 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1561 /* prepare output buffer */
1562 c->buffer_ptr = c->buffer;
1564 c->state = HTTPSTATE_SEND_HEADER;
1568 c->http_error = 200; /* horrible : we use this value to avoid
1569 going to the send data state */
1570 c->state = HTTPSTATE_SEND_HEADER;
1574 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1576 static const char *suffix = " kMGTP";
1579 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1582 url_fprintf(pb, "%"PRId64"%c", count, *s);
1585 static void compute_stats(HTTPContext *c)
1592 ByteIOContext pb1, *pb = &pb1;
1594 if (url_open_dyn_buf(pb) < 0) {
1595 /* XXX: return an error ? */
1596 c->buffer_ptr = c->buffer;
1597 c->buffer_end = c->buffer;
1601 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1602 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1603 url_fprintf(pb, "Pragma: no-cache\r\n");
1604 url_fprintf(pb, "\r\n");
1606 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1607 if (c->stream->feed_filename) {
1608 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1610 url_fprintf(pb, "</HEAD>\n<BODY>");
1611 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1613 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1614 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1615 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");
1616 stream = first_stream;
1617 while (stream != NULL) {
1618 char sfilename[1024];
1621 if (stream->feed != stream) {
1622 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1623 eosf = sfilename + strlen(sfilename);
1624 if (eosf - sfilename >= 4) {
1625 if (strcmp(eosf - 4, ".asf") == 0) {
1626 strcpy(eosf - 4, ".asx");
1627 } else if (strcmp(eosf - 3, ".rm") == 0) {
1628 strcpy(eosf - 3, ".ram");
1629 } else if (stream->fmt == &rtp_muxer) {
1630 /* generate a sample RTSP director if
1631 unicast. Generate an SDP redirector if
1633 eosf = strrchr(sfilename, '.');
1635 eosf = sfilename + strlen(sfilename);
1636 if (stream->is_multicast)
1637 strcpy(eosf, ".sdp");
1639 strcpy(eosf, ".rtsp");
1643 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1644 sfilename, stream->filename);
1645 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1646 stream->conns_served);
1647 fmt_bytecount(pb, stream->bytes_served);
1648 switch(stream->stream_type) {
1649 case STREAM_TYPE_LIVE:
1651 int audio_bit_rate = 0;
1652 int video_bit_rate = 0;
1653 const char *audio_codec_name = "";
1654 const char *video_codec_name = "";
1655 const char *audio_codec_name_extra = "";
1656 const char *video_codec_name_extra = "";
1658 for(i=0;i<stream->nb_streams;i++) {
1659 AVStream *st = stream->streams[i];
1660 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1661 switch(st->codec->codec_type) {
1662 case CODEC_TYPE_AUDIO:
1663 audio_bit_rate += st->codec->bit_rate;
1665 if (*audio_codec_name)
1666 audio_codec_name_extra = "...";
1667 audio_codec_name = codec->name;
1670 case CODEC_TYPE_VIDEO:
1671 video_bit_rate += st->codec->bit_rate;
1673 if (*video_codec_name)
1674 video_codec_name_extra = "...";
1675 video_codec_name = codec->name;
1678 case CODEC_TYPE_DATA:
1679 video_bit_rate += st->codec->bit_rate;
1685 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",
1688 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1689 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1691 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1693 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1695 url_fprintf(pb, "\n");
1699 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1703 stream = stream->next;
1705 url_fprintf(pb, "</TABLE>\n");
1707 stream = first_stream;
1708 while (stream != NULL) {
1709 if (stream->feed == stream) {
1710 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1712 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1714 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1719 /* This is somewhat linux specific I guess */
1720 snprintf(ps_cmd, sizeof(ps_cmd),
1721 "ps -o \"%%cpu,cputime\" --no-headers %d",
1724 pid_stat = popen(ps_cmd, "r");
1729 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1731 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1739 url_fprintf(pb, "<p>");
1741 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");
1743 for (i = 0; i < stream->nb_streams; i++) {
1744 AVStream *st = stream->streams[i];
1745 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1746 const char *type = "unknown";
1747 char parameters[64];
1751 switch(st->codec->codec_type) {
1752 case CODEC_TYPE_AUDIO:
1755 case CODEC_TYPE_VIDEO:
1757 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1758 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1763 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1764 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1766 url_fprintf(pb, "</table>\n");
1769 stream = stream->next;
1775 AVCodecContext *enc;
1779 stream = first_feed;
1780 while (stream != NULL) {
1781 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1782 url_fprintf(pb, "<TABLE>\n");
1783 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1784 for(i=0;i<stream->nb_streams;i++) {
1785 AVStream *st = stream->streams[i];
1786 FeedData *fdata = st->priv_data;
1789 avcodec_string(buf, sizeof(buf), enc);
1790 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1791 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1792 avg /= enc->frame_size;
1793 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1794 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1796 url_fprintf(pb, "</TABLE>\n");
1797 stream = stream->next_feed;
1802 /* connection status */
1803 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1805 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1806 nb_connections, nb_max_connections);
1808 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1809 current_bandwidth, max_bandwidth);
1811 url_fprintf(pb, "<TABLE>\n");
1812 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");
1813 c1 = first_http_ctx;
1815 while (c1 != NULL) {
1821 for (j = 0; j < c1->stream->nb_streams; j++) {
1822 if (!c1->stream->feed) {
1823 bitrate += c1->stream->streams[j]->codec->bit_rate;
1825 if (c1->feed_streams[j] >= 0) {
1826 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1833 p = inet_ntoa(c1->from_addr.sin_addr);
1834 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1836 c1->stream ? c1->stream->filename : "",
1837 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1840 http_state[c1->state]);
1841 fmt_bytecount(pb, bitrate);
1842 url_fprintf(pb, "<td align=right>");
1843 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1844 url_fprintf(pb, "<td align=right>");
1845 fmt_bytecount(pb, c1->data_count);
1846 url_fprintf(pb, "\n");
1849 url_fprintf(pb, "</TABLE>\n");
1854 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1855 url_fprintf(pb, "</BODY>\n</HTML>\n");
1857 len = url_close_dyn_buf(pb, &c->pb_buffer);
1858 c->buffer_ptr = c->pb_buffer;
1859 c->buffer_end = c->pb_buffer + len;
1862 /* check if the parser needs to be opened for stream i */
1863 static void open_parser(AVFormatContext *s, int i)
1865 AVStream *st = s->streams[i];
1868 if (!st->codec->codec) {
1869 codec = avcodec_find_decoder(st->codec->codec_id);
1870 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1871 st->codec->parse_only = 1;
1872 if (avcodec_open(st->codec, codec) < 0) {
1873 st->codec->parse_only = 0;
1879 static int open_input_stream(HTTPContext *c, const char *info)
1882 char input_filename[1024];
1887 /* find file name */
1888 if (c->stream->feed) {
1889 strcpy(input_filename, c->stream->feed->feed_filename);
1890 buf_size = FFM_PACKET_SIZE;
1891 /* compute position (absolute time) */
1892 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1893 stream_pos = parse_date(buf, 0);
1894 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1895 int prebuffer = strtol(buf, 0, 10);
1896 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1898 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1901 strcpy(input_filename, c->stream->feed_filename);
1903 /* compute position (relative time) */
1904 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1905 stream_pos = parse_date(buf, 1);
1910 if (input_filename[0] == '\0')
1914 { time_t when = stream_pos / 1000000;
1915 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1920 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1921 buf_size, c->stream->ap_in) < 0) {
1922 http_log("%s not found", input_filename);
1927 /* open each parser */
1928 for(i=0;i<s->nb_streams;i++)
1931 /* choose stream as clock source (we favorize video stream if
1932 present) for packet sending */
1933 c->pts_stream_index = 0;
1934 for(i=0;i<c->stream->nb_streams;i++) {
1935 if (c->pts_stream_index == 0 &&
1936 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1937 c->pts_stream_index = i;
1942 if (c->fmt_in->iformat->read_seek) {
1943 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1946 /* set the start time (needed for maxtime and RTP packet timing) */
1947 c->start_time = cur_time;
1948 c->first_pts = AV_NOPTS_VALUE;
1952 /* return the server clock (in us) */
1953 static int64_t get_server_clock(HTTPContext *c)
1955 /* compute current pts value from system time */
1956 return (int64_t)(cur_time - c->start_time) * 1000LL;
1959 /* return the estimated time at which the current packet must be sent
1961 static int64_t get_packet_send_clock(HTTPContext *c)
1963 int bytes_left, bytes_sent, frame_bytes;
1965 frame_bytes = c->cur_frame_bytes;
1966 if (frame_bytes <= 0) {
1969 bytes_left = c->buffer_end - c->buffer_ptr;
1970 bytes_sent = frame_bytes - bytes_left;
1971 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1976 static int http_prepare_data(HTTPContext *c)
1979 AVFormatContext *ctx;
1981 av_freep(&c->pb_buffer);
1983 case HTTPSTATE_SEND_DATA_HEADER:
1984 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1985 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
1987 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
1988 c->stream->comment);
1989 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
1990 c->stream->copyright);
1991 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
1994 /* open output stream by using specified codecs */
1995 c->fmt_ctx.oformat = c->stream->fmt;
1996 c->fmt_ctx.nb_streams = c->stream->nb_streams;
1997 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2000 st = av_mallocz(sizeof(AVStream));
2001 st->codec= avcodec_alloc_context();
2002 c->fmt_ctx.streams[i] = st;
2003 /* if file or feed, then just take streams from FFStream struct */
2004 if (!c->stream->feed ||
2005 c->stream->feed == c->stream)
2006 src = c->stream->streams[i];
2008 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2012 st->codec->frame_number = 0; /* XXX: should be done in
2013 AVStream, not in codec */
2014 /* I'm pretty sure that this is not correct...
2015 * However, without it, we crash
2017 st->codec->coded_frame = &dummy_frame;
2019 c->got_key_frame = 0;
2021 /* prepare header and save header data in a stream */
2022 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2023 /* XXX: potential leak */
2026 c->fmt_ctx.pb.is_streamed = 1;
2028 av_set_parameters(&c->fmt_ctx, NULL);
2029 av_write_header(&c->fmt_ctx);
2031 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2032 c->buffer_ptr = c->pb_buffer;
2033 c->buffer_end = c->pb_buffer + len;
2035 c->state = HTTPSTATE_SEND_DATA;
2036 c->last_packet_sent = 0;
2038 case HTTPSTATE_SEND_DATA:
2039 /* find a new packet */
2043 /* read a packet from the input stream */
2044 if (c->stream->feed) {
2045 ffm_set_write_index(c->fmt_in,
2046 c->stream->feed->feed_write_index,
2047 c->stream->feed->feed_size);
2050 if (c->stream->max_time &&
2051 c->stream->max_time + c->start_time - cur_time < 0) {
2052 /* We have timed out */
2053 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2056 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2057 if (c->stream->feed && c->stream->feed->feed_opened) {
2058 /* if coming from feed, it means we reached the end of the
2059 ffm file, so must wait for more data */
2060 c->state = HTTPSTATE_WAIT_FEED;
2061 return 1; /* state changed */
2063 if (c->stream->loop) {
2064 av_close_input_file(c->fmt_in);
2066 if (open_input_stream(c, "") < 0)
2071 /* must send trailer now because eof or error */
2072 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2076 /* update first pts if needed */
2077 if (c->first_pts == AV_NOPTS_VALUE) {
2078 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2079 c->start_time = cur_time;
2081 /* send it to the appropriate stream */
2082 if (c->stream->feed) {
2083 /* if coming from a feed, select the right stream */
2084 if (c->switch_pending) {
2085 c->switch_pending = 0;
2086 for(i=0;i<c->stream->nb_streams;i++) {
2087 if (c->switch_feed_streams[i] == pkt.stream_index) {
2088 if (pkt.flags & PKT_FLAG_KEY) {
2089 do_switch_stream(c, i);
2092 if (c->switch_feed_streams[i] >= 0) {
2093 c->switch_pending = 1;
2097 for(i=0;i<c->stream->nb_streams;i++) {
2098 if (c->feed_streams[i] == pkt.stream_index) {
2099 pkt.stream_index = i;
2100 if (pkt.flags & PKT_FLAG_KEY) {
2101 c->got_key_frame |= 1 << i;
2103 /* See if we have all the key frames, then
2104 * we start to send. This logic is not quite
2105 * right, but it works for the case of a
2106 * single video stream with one or more
2107 * audio streams (for which every frame is
2108 * typically a key frame).
2110 if (!c->stream->send_on_key ||
2111 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2117 AVCodecContext *codec;
2120 /* specific handling for RTP: we use several
2121 output stream (one for each RTP
2122 connection). XXX: need more abstract handling */
2123 if (c->is_packetized) {
2125 /* compute send time and duration */
2126 st = c->fmt_in->streams[pkt.stream_index];
2127 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2128 if (st->start_time != AV_NOPTS_VALUE)
2129 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2130 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2132 printf("index=%d pts=%0.3f duration=%0.6f\n",
2134 (double)c->cur_pts /
2136 (double)c->cur_frame_duration /
2139 /* find RTP context */
2140 c->packet_stream_index = pkt.stream_index;
2141 ctx = c->rtp_ctx[c->packet_stream_index];
2143 av_free_packet(&pkt);
2146 codec = ctx->streams[0]->codec;
2147 /* only one stream per RTP connection */
2148 pkt.stream_index = 0;
2152 codec = ctx->streams[pkt.stream_index]->codec;
2155 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2156 if (c->is_packetized) {
2157 int max_packet_size;
2158 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2159 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2161 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2162 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2164 ret = url_open_dyn_buf(&ctx->pb);
2167 /* XXX: potential leak */
2170 if (av_write_frame(ctx, &pkt)) {
2171 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2174 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2175 c->cur_frame_bytes = len;
2176 c->buffer_ptr = c->pb_buffer;
2177 c->buffer_end = c->pb_buffer + len;
2179 codec->frame_number++;
2183 av_free_packet(&pkt);
2189 case HTTPSTATE_SEND_DATA_TRAILER:
2190 /* last packet test ? */
2191 if (c->last_packet_sent || c->is_packetized)
2194 /* prepare header */
2195 if (url_open_dyn_buf(&ctx->pb) < 0) {
2196 /* XXX: potential leak */
2199 av_write_trailer(ctx);
2200 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2201 c->buffer_ptr = c->pb_buffer;
2202 c->buffer_end = c->pb_buffer + len;
2204 c->last_packet_sent = 1;
2211 #define SHORT_TERM_BANDWIDTH 8000000
2213 /* should convert the format at the same time */
2214 /* send data starting at c->buffer_ptr to the output connection
2215 (either UDP or TCP connection) */
2216 static int http_send_data(HTTPContext *c)
2221 if (c->buffer_ptr >= c->buffer_end) {
2222 ret = http_prepare_data(c);
2225 else if (ret != 0) {
2226 /* state change requested */
2230 if (c->is_packetized) {
2231 /* RTP data output */
2232 len = c->buffer_end - c->buffer_ptr;
2234 /* fail safe - should never happen */
2236 c->buffer_ptr = c->buffer_end;
2239 len = (c->buffer_ptr[0] << 24) |
2240 (c->buffer_ptr[1] << 16) |
2241 (c->buffer_ptr[2] << 8) |
2243 if (len > (c->buffer_end - c->buffer_ptr))
2245 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2246 /* nothing to send yet: we can wait */
2250 c->data_count += len;
2251 update_datarate(&c->datarate, c->data_count);
2253 c->stream->bytes_served += len;
2255 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2256 /* RTP packets are sent inside the RTSP TCP connection */
2257 ByteIOContext pb1, *pb = &pb1;
2258 int interleaved_index, size;
2260 HTTPContext *rtsp_c;
2263 /* if no RTSP connection left, error */
2266 /* if already sending something, then wait. */
2267 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
2270 if (url_open_dyn_buf(pb) < 0)
2272 interleaved_index = c->packet_stream_index * 2;
2273 /* RTCP packets are sent at odd indexes */
2274 if (c->buffer_ptr[1] == 200)
2275 interleaved_index++;
2276 /* write RTSP TCP header */
2278 header[1] = interleaved_index;
2279 header[2] = len >> 8;
2281 put_buffer(pb, header, 4);
2282 /* write RTP packet data */
2284 put_buffer(pb, c->buffer_ptr, len);
2285 size = url_close_dyn_buf(pb, &c->packet_buffer);
2286 /* prepare asynchronous TCP sending */
2287 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2288 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2289 c->buffer_ptr += len;
2291 /* send everything we can NOW */
2292 len = write(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2293 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr);
2295 rtsp_c->packet_buffer_ptr += len;
2297 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2298 /* if we could not send all the data, we will
2299 send it later, so a new state is needed to
2300 "lock" the RTSP TCP connection */
2301 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2304 /* all data has been sent */
2305 av_freep(&c->packet_buffer);
2308 /* send RTP packet directly in UDP */
2310 url_write(c->rtp_handles[c->packet_stream_index],
2311 c->buffer_ptr, len);
2312 c->buffer_ptr += len;
2313 /* here we continue as we can send several packets per 10 ms slot */
2316 /* TCP data output */
2317 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2319 if (errno != EAGAIN && errno != EINTR) {
2320 /* error : close connection */
2326 c->buffer_ptr += len;
2328 c->data_count += len;
2329 update_datarate(&c->datarate, c->data_count);
2331 c->stream->bytes_served += len;
2339 static int http_start_receive_data(HTTPContext *c)
2343 if (c->stream->feed_opened)
2346 /* Don't permit writing to this one */
2347 if (c->stream->readonly)
2351 fd = open(c->stream->feed_filename, O_RDWR);
2356 c->stream->feed_write_index = ffm_read_write_index(fd);
2357 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2358 lseek(fd, 0, SEEK_SET);
2360 /* init buffer input */
2361 c->buffer_ptr = c->buffer;
2362 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2363 c->stream->feed_opened = 1;
2367 static int http_receive_data(HTTPContext *c)
2371 if (c->buffer_end > c->buffer_ptr) {
2374 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2376 if (errno != EAGAIN && errno != EINTR) {
2377 /* error : close connection */
2380 } else if (len == 0) {
2381 /* end of connection : close it */
2384 c->buffer_ptr += len;
2385 c->data_count += len;
2386 update_datarate(&c->datarate, c->data_count);
2390 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2391 if (c->buffer[0] != 'f' ||
2392 c->buffer[1] != 'm') {
2393 http_log("Feed stream has become desynchronized -- disconnecting\n");
2398 if (c->buffer_ptr >= c->buffer_end) {
2399 FFStream *feed = c->stream;
2400 /* a packet has been received : write it in the store, except
2402 if (c->data_count > FFM_PACKET_SIZE) {
2404 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2405 /* XXX: use llseek or url_seek */
2406 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2407 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2409 feed->feed_write_index += FFM_PACKET_SIZE;
2410 /* update file size */
2411 if (feed->feed_write_index > c->stream->feed_size)
2412 feed->feed_size = feed->feed_write_index;
2414 /* handle wrap around if max file size reached */
2415 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2416 feed->feed_write_index = FFM_PACKET_SIZE;
2419 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2421 /* wake up any waiting connections */
2422 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2423 if (c1->state == HTTPSTATE_WAIT_FEED &&
2424 c1->stream->feed == c->stream->feed) {
2425 c1->state = HTTPSTATE_SEND_DATA;
2429 /* We have a header in our hands that contains useful data */
2431 AVInputFormat *fmt_in;
2432 ByteIOContext *pb = &s.pb;
2435 memset(&s, 0, sizeof(s));
2437 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2438 pb->buf_end = c->buffer_end; /* ?? */
2439 pb->is_streamed = 1;
2441 /* use feed output format name to find corresponding input format */
2442 fmt_in = av_find_input_format(feed->fmt->name);
2446 if (fmt_in->priv_data_size > 0) {
2447 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2453 if (fmt_in->read_header(&s, 0) < 0) {
2454 av_freep(&s.priv_data);
2458 /* Now we have the actual streams */
2459 if (s.nb_streams != feed->nb_streams) {
2460 av_freep(&s.priv_data);
2463 for (i = 0; i < s.nb_streams; i++) {
2464 memcpy(feed->streams[i]->codec,
2465 s.streams[i]->codec, sizeof(AVCodecContext));
2467 av_freep(&s.priv_data);
2469 c->buffer_ptr = c->buffer;
2474 c->stream->feed_opened = 0;
2479 /********************************************************************/
2482 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2489 switch(error_number) {
2490 #define DEF(n, c, s) case c: str = s; break;
2491 #include "rtspcodes.h"
2494 str = "Unknown Error";
2498 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2499 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2501 /* output GMT time */
2505 p = buf2 + strlen(p) - 1;
2508 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2511 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2513 rtsp_reply_header(c, error_number);
2514 url_fprintf(c->pb, "\r\n");
2517 static int rtsp_parse_request(HTTPContext *c)
2519 const char *p, *p1, *p2;
2526 RTSPHeader header1, *header = &header1;
2528 c->buffer_ptr[0] = '\0';
2531 get_word(cmd, sizeof(cmd), &p);
2532 get_word(url, sizeof(url), &p);
2533 get_word(protocol, sizeof(protocol), &p);
2535 pstrcpy(c->method, sizeof(c->method), cmd);
2536 pstrcpy(c->url, sizeof(c->url), url);
2537 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2540 if (url_open_dyn_buf(c->pb) < 0) {
2541 /* XXX: cannot do more */
2542 c->pb = NULL; /* safety */
2546 /* check version name */
2547 if (strcmp(protocol, "RTSP/1.0") != 0) {
2548 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2552 /* parse each header line */
2553 memset(header, 0, sizeof(RTSPHeader));
2554 /* skip to next line */
2555 while (*p != '\n' && *p != '\0')
2559 while (*p != '\0') {
2560 p1 = strchr(p, '\n');
2564 if (p2 > p && p2[-1] == '\r')
2566 /* skip empty line */
2570 if (len > sizeof(line) - 1)
2571 len = sizeof(line) - 1;
2572 memcpy(line, p, len);
2574 rtsp_parse_line(header, line);
2578 /* handle sequence number */
2579 c->seq = header->seq;
2581 if (!strcmp(cmd, "DESCRIBE")) {
2582 rtsp_cmd_describe(c, url);
2583 } else if (!strcmp(cmd, "OPTIONS")) {
2584 rtsp_cmd_options(c, url);
2585 } else if (!strcmp(cmd, "SETUP")) {
2586 rtsp_cmd_setup(c, url, header);
2587 } else if (!strcmp(cmd, "PLAY")) {
2588 rtsp_cmd_play(c, url, header);
2589 } else if (!strcmp(cmd, "PAUSE")) {
2590 rtsp_cmd_pause(c, url, header);
2591 } else if (!strcmp(cmd, "TEARDOWN")) {
2592 rtsp_cmd_teardown(c, url, header);
2594 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2597 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2598 c->pb = NULL; /* safety */
2600 /* XXX: cannot do more */
2603 c->buffer_ptr = c->pb_buffer;
2604 c->buffer_end = c->pb_buffer + len;
2605 c->state = RTSPSTATE_SEND_REPLY;
2609 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2611 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2612 struct in_addr my_ip)
2614 ByteIOContext pb1, *pb = &pb1;
2615 int i, payload_type, port, private_payload_type, j;
2616 const char *ipstr, *title, *mediatype;
2619 if (url_open_dyn_buf(pb) < 0)
2622 /* general media info */
2624 url_fprintf(pb, "v=0\n");
2625 ipstr = inet_ntoa(my_ip);
2626 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2627 title = stream->title;
2628 if (title[0] == '\0')
2630 url_fprintf(pb, "s=%s\n", title);
2631 if (stream->comment[0] != '\0')
2632 url_fprintf(pb, "i=%s\n", stream->comment);
2633 if (stream->is_multicast) {
2634 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2636 /* for each stream, we output the necessary info */
2637 private_payload_type = RTP_PT_PRIVATE;
2638 for(i = 0; i < stream->nb_streams; i++) {
2639 st = stream->streams[i];
2640 if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
2641 mediatype = "video";
2643 switch(st->codec->codec_type) {
2644 case CODEC_TYPE_AUDIO:
2645 mediatype = "audio";
2647 case CODEC_TYPE_VIDEO:
2648 mediatype = "video";
2651 mediatype = "application";
2655 /* NOTE: the port indication is not correct in case of
2656 unicast. It is not an issue because RTSP gives it */
2657 payload_type = rtp_get_payload_type(st->codec);
2658 if (payload_type < 0)
2659 payload_type = private_payload_type++;
2660 if (stream->is_multicast) {
2661 port = stream->multicast_port + 2 * i;
2665 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2666 mediatype, port, payload_type);
2667 if (payload_type >= RTP_PT_PRIVATE) {
2668 /* for private payload type, we need to give more info */
2669 switch(st->codec->codec_id) {
2670 case CODEC_ID_MPEG4:
2673 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2674 payload_type, 90000);
2675 /* we must also add the mpeg4 header */
2676 data = st->codec->extradata;
2678 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2679 for(j=0;j<st->codec->extradata_size;j++) {
2680 url_fprintf(pb, "%02x", data[j]);
2682 url_fprintf(pb, "\n");
2687 /* XXX: add other codecs ? */
2691 url_fprintf(pb, "a=control:streamid=%d\n", i);
2693 return url_close_dyn_buf(pb, pbuffer);
2695 url_close_dyn_buf(pb, pbuffer);
2700 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2702 // rtsp_reply_header(c, RTSP_STATUS_OK);
2703 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2704 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2705 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2706 url_fprintf(c->pb, "\r\n");
2709 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2715 int content_length, len;
2716 struct sockaddr_in my_addr;
2718 /* find which url is asked */
2719 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2724 for(stream = first_stream; stream != NULL; stream = stream->next) {
2725 if (!stream->is_feed && stream->fmt == &rtp_muxer &&
2726 !strcmp(path, stream->filename)) {
2730 /* no stream found */
2731 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2735 /* prepare the media description in sdp format */
2737 /* get the host IP */
2738 len = sizeof(my_addr);
2739 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2740 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2741 if (content_length < 0) {
2742 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2745 rtsp_reply_header(c, RTSP_STATUS_OK);
2746 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2747 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2748 url_fprintf(c->pb, "\r\n");
2749 put_buffer(c->pb, content, content_length);
2752 static HTTPContext *find_rtp_session(const char *session_id)
2756 if (session_id[0] == '\0')
2759 for(c = first_http_ctx; c != NULL; c = c->next) {
2760 if (!strcmp(c->session_id, session_id))
2766 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2768 RTSPTransportField *th;
2771 for(i=0;i<h->nb_transports;i++) {
2772 th = &h->transports[i];
2773 if (th->protocol == protocol)
2779 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2783 int stream_index, port;
2788 RTSPTransportField *th;
2789 struct sockaddr_in dest_addr;
2790 RTSPActionServerSetup setup;
2792 /* find which url is asked */
2793 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2798 /* now check each stream */
2799 for(stream = first_stream; stream != NULL; stream = stream->next) {
2800 if (!stream->is_feed && stream->fmt == &rtp_muxer) {
2801 /* accept aggregate filenames only if single stream */
2802 if (!strcmp(path, stream->filename)) {
2803 if (stream->nb_streams != 1) {
2804 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2811 for(stream_index = 0; stream_index < stream->nb_streams;
2813 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2814 stream->filename, stream_index);
2815 if (!strcmp(path, buf))
2820 /* no stream found */
2821 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2825 /* generate session id if needed */
2826 if (h->session_id[0] == '\0') {
2827 snprintf(h->session_id, sizeof(h->session_id),
2828 "%08x%08x", (int)random(), (int)random());
2831 /* find rtp session, and create it if none found */
2832 rtp_c = find_rtp_session(h->session_id);
2834 /* always prefer UDP */
2835 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2837 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2839 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2844 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2847 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2851 /* open input stream */
2852 if (open_input_stream(rtp_c, "") < 0) {
2853 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2858 /* test if stream is OK (test needed because several SETUP needs
2859 to be done for a given file) */
2860 if (rtp_c->stream != stream) {
2861 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2865 /* test if stream is already set up */
2866 if (rtp_c->rtp_ctx[stream_index]) {
2867 rtsp_reply_error(c, RTSP_STATUS_STATE);
2871 /* check transport */
2872 th = find_transport(h, rtp_c->rtp_protocol);
2873 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2874 th->client_port_min <= 0)) {
2875 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2879 /* setup default options */
2880 setup.transport_option[0] = '\0';
2881 dest_addr = rtp_c->from_addr;
2882 dest_addr.sin_port = htons(th->client_port_min);
2884 /* add transport option if needed */
2885 if (ff_rtsp_callback) {
2886 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2887 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2888 (char *)&setup, sizeof(setup),
2889 stream->rtsp_option) < 0) {
2890 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2893 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2897 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2898 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2902 /* now everything is OK, so we can send the connection parameters */
2903 rtsp_reply_header(c, RTSP_STATUS_OK);
2905 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2907 switch(rtp_c->rtp_protocol) {
2908 case RTSP_PROTOCOL_RTP_UDP:
2909 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2910 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2911 "client_port=%d-%d;server_port=%d-%d",
2912 th->client_port_min, th->client_port_min + 1,
2915 case RTSP_PROTOCOL_RTP_TCP:
2916 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2917 stream_index * 2, stream_index * 2 + 1);
2922 if (setup.transport_option[0] != '\0') {
2923 url_fprintf(c->pb, ";%s", setup.transport_option);
2925 url_fprintf(c->pb, "\r\n");
2928 url_fprintf(c->pb, "\r\n");
2932 /* find an rtp connection by using the session ID. Check consistency
2934 static HTTPContext *find_rtp_session_with_url(const char *url,
2935 const char *session_id)
2943 rtp_c = find_rtp_session(session_id);
2947 /* find which url is asked */
2948 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2952 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2953 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2954 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2955 rtp_c->stream->filename, s);
2956 if(!strncmp(path, buf, sizeof(buf))) {
2957 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2964 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2968 rtp_c = find_rtp_session_with_url(url, h->session_id);
2970 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2974 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2975 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2976 rtp_c->state != HTTPSTATE_READY) {
2977 rtsp_reply_error(c, RTSP_STATUS_STATE);
2982 /* XXX: seek in stream */
2983 if (h->range_start != AV_NOPTS_VALUE) {
2984 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2985 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2989 rtp_c->state = HTTPSTATE_SEND_DATA;
2991 /* now everything is OK, so we can send the connection parameters */
2992 rtsp_reply_header(c, RTSP_STATUS_OK);
2994 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2995 url_fprintf(c->pb, "\r\n");
2998 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3002 rtp_c = find_rtp_session_with_url(url, h->session_id);
3004 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3008 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3009 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3010 rtsp_reply_error(c, RTSP_STATUS_STATE);
3014 rtp_c->state = HTTPSTATE_READY;
3015 rtp_c->first_pts = AV_NOPTS_VALUE;
3016 /* now everything is OK, so we can send the connection parameters */
3017 rtsp_reply_header(c, RTSP_STATUS_OK);
3019 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3020 url_fprintf(c->pb, "\r\n");
3023 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3027 rtp_c = find_rtp_session_with_url(url, h->session_id);
3029 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3033 /* abort the session */
3034 close_connection(rtp_c);
3036 if (ff_rtsp_callback) {
3037 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3039 rtp_c->stream->rtsp_option);
3042 /* now everything is OK, so we can send the connection parameters */
3043 rtsp_reply_header(c, RTSP_STATUS_OK);
3045 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3046 url_fprintf(c->pb, "\r\n");
3050 /********************************************************************/
3053 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3054 FFStream *stream, const char *session_id,
3055 enum RTSPProtocol rtp_protocol)
3057 HTTPContext *c = NULL;
3058 const char *proto_str;
3060 /* XXX: should output a warning page when coming
3061 close to the connection limit */
3062 if (nb_connections >= nb_max_connections)
3065 /* add a new connection */
3066 c = av_mallocz(sizeof(HTTPContext));
3071 c->poll_entry = NULL;
3072 c->from_addr = *from_addr;
3073 c->buffer_size = IOBUFFER_INIT_SIZE;
3074 c->buffer = av_malloc(c->buffer_size);
3079 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3080 c->state = HTTPSTATE_READY;
3081 c->is_packetized = 1;
3082 c->rtp_protocol = rtp_protocol;
3084 /* protocol is shown in statistics */
3085 switch(c->rtp_protocol) {
3086 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3087 proto_str = "MCAST";
3089 case RTSP_PROTOCOL_RTP_UDP:
3092 case RTSP_PROTOCOL_RTP_TCP:
3099 pstrcpy(c->protocol, sizeof(c->protocol), "RTP/");
3100 pstrcat(c->protocol, sizeof(c->protocol), proto_str);
3102 current_bandwidth += stream->bandwidth;
3104 c->next = first_http_ctx;
3116 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3117 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3119 static int rtp_new_av_stream(HTTPContext *c,
3120 int stream_index, struct sockaddr_in *dest_addr,
3121 HTTPContext *rtsp_c)
3123 AVFormatContext *ctx;
3129 int max_packet_size;
3131 /* now we can open the relevant output stream */
3132 ctx = av_alloc_format_context();
3135 ctx->oformat = &rtp_muxer;
3137 st = av_mallocz(sizeof(AVStream));
3140 st->codec= avcodec_alloc_context();
3141 ctx->nb_streams = 1;
3142 ctx->streams[0] = st;
3144 if (!c->stream->feed ||
3145 c->stream->feed == c->stream) {
3146 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3149 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3153 /* build destination RTP address */
3154 ipaddr = inet_ntoa(dest_addr->sin_addr);
3156 switch(c->rtp_protocol) {
3157 case RTSP_PROTOCOL_RTP_UDP:
3158 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3161 /* XXX: also pass as parameter to function ? */
3162 if (c->stream->is_multicast) {
3164 ttl = c->stream->multicast_ttl;
3167 snprintf(ctx->filename, sizeof(ctx->filename),
3168 "rtp://%s:%d?multicast=1&ttl=%d",
3169 ipaddr, ntohs(dest_addr->sin_port), ttl);
3171 snprintf(ctx->filename, sizeof(ctx->filename),
3172 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3175 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3177 c->rtp_handles[stream_index] = h;
3178 max_packet_size = url_get_max_packet_size(h);
3180 case RTSP_PROTOCOL_RTP_TCP:
3183 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3189 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3190 ipaddr, ntohs(dest_addr->sin_port),
3192 c->stream->filename, stream_index, c->protocol);
3194 /* normally, no packets should be output here, but the packet size may be checked */
3195 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3196 /* XXX: close stream */
3199 av_set_parameters(ctx, NULL);
3200 if (av_write_header(ctx) < 0) {
3207 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3210 c->rtp_ctx[stream_index] = ctx;
3214 /********************************************************************/
3215 /* ffserver initialization */
3217 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3221 fst = av_mallocz(sizeof(AVStream));
3224 fst->codec= avcodec_alloc_context();
3225 fst->priv_data = av_mallocz(sizeof(FeedData));
3226 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3227 fst->codec->coded_frame = &dummy_frame;
3228 fst->index = stream->nb_streams;
3229 av_set_pts_info(fst, 33, 1, 90000);
3230 stream->streams[stream->nb_streams++] = fst;
3234 /* return the stream number in the feed */
3235 static int add_av_stream(FFStream *feed, AVStream *st)
3238 AVCodecContext *av, *av1;
3242 for(i=0;i<feed->nb_streams;i++) {
3243 st = feed->streams[i];
3245 if (av1->codec_id == av->codec_id &&
3246 av1->codec_type == av->codec_type &&
3247 av1->bit_rate == av->bit_rate) {
3249 switch(av->codec_type) {
3250 case CODEC_TYPE_AUDIO:
3251 if (av1->channels == av->channels &&
3252 av1->sample_rate == av->sample_rate)
3255 case CODEC_TYPE_VIDEO:
3256 if (av1->width == av->width &&
3257 av1->height == av->height &&
3258 av1->time_base.den == av->time_base.den &&
3259 av1->time_base.num == av->time_base.num &&
3260 av1->gop_size == av->gop_size)
3269 fst = add_av_stream1(feed, av);
3272 return feed->nb_streams - 1;
3277 static void remove_stream(FFStream *stream)
3281 while (*ps != NULL) {
3282 if (*ps == stream) {
3290 /* specific mpeg4 handling : we extract the raw parameters */
3291 static void extract_mpeg4_header(AVFormatContext *infile)
3293 int mpeg4_count, i, size;
3299 for(i=0;i<infile->nb_streams;i++) {
3300 st = infile->streams[i];
3301 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3302 st->codec->extradata_size == 0) {
3309 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3310 while (mpeg4_count > 0) {
3311 if (av_read_packet(infile, &pkt) < 0)
3313 st = infile->streams[pkt.stream_index];
3314 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3315 st->codec->extradata_size == 0) {
3316 av_freep(&st->codec->extradata);
3317 /* fill extradata with the header */
3318 /* XXX: we make hard suppositions here ! */
3320 while (p < pkt.data + pkt.size - 4) {
3321 /* stop when vop header is found */
3322 if (p[0] == 0x00 && p[1] == 0x00 &&
3323 p[2] == 0x01 && p[3] == 0xb6) {
3324 size = p - pkt.data;
3325 // av_hex_dump(pkt.data, size);
3326 st->codec->extradata = av_malloc(size);
3327 st->codec->extradata_size = size;
3328 memcpy(st->codec->extradata, pkt.data, size);
3335 av_free_packet(&pkt);
3339 /* compute the needed AVStream for each file */
3340 static void build_file_streams(void)
3342 FFStream *stream, *stream_next;
3343 AVFormatContext *infile;
3346 /* gather all streams */
3347 for(stream = first_stream; stream != NULL; stream = stream_next) {
3348 stream_next = stream->next;
3349 if (stream->stream_type == STREAM_TYPE_LIVE &&
3351 /* the stream comes from a file */
3352 /* try to open the file */
3354 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3355 if (stream->fmt == &rtp_muxer) {
3356 /* specific case : if transport stream output to RTP,
3357 we use a raw transport stream reader */
3358 stream->ap_in->mpeg2ts_raw = 1;
3359 stream->ap_in->mpeg2ts_compute_pcr = 1;
3362 if (av_open_input_file(&infile, stream->feed_filename,
3363 stream->ifmt, 0, stream->ap_in) < 0) {
3364 http_log("%s not found", stream->feed_filename);
3365 /* remove stream (no need to spend more time on it) */
3367 remove_stream(stream);
3369 /* find all the AVStreams inside and reference them in
3371 if (av_find_stream_info(infile) < 0) {
3372 http_log("Could not find codec parameters from '%s'",
3373 stream->feed_filename);
3374 av_close_input_file(infile);
3377 extract_mpeg4_header(infile);
3379 for(i=0;i<infile->nb_streams;i++) {
3380 add_av_stream1(stream, infile->streams[i]->codec);
3382 av_close_input_file(infile);
3388 /* compute the needed AVStream for each feed */
3389 static void build_feed_streams(void)
3391 FFStream *stream, *feed;
3394 /* gather all streams */
3395 for(stream = first_stream; stream != NULL; stream = stream->next) {
3396 feed = stream->feed;
3398 if (!stream->is_feed) {
3399 /* we handle a stream coming from a feed */
3400 for(i=0;i<stream->nb_streams;i++) {
3401 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3407 /* gather all streams */
3408 for(stream = first_stream; stream != NULL; stream = stream->next) {
3409 feed = stream->feed;
3411 if (stream->is_feed) {
3412 for(i=0;i<stream->nb_streams;i++) {
3413 stream->feed_streams[i] = i;
3419 /* create feed files if needed */
3420 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3423 if (url_exist(feed->feed_filename)) {
3424 /* See if it matches */
3428 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3429 /* Now see if it matches */
3430 if (s->nb_streams == feed->nb_streams) {
3432 for(i=0;i<s->nb_streams;i++) {
3434 sf = feed->streams[i];
3437 if (sf->index != ss->index ||
3439 printf("Index & Id do not match for stream %d (%s)\n",
3440 i, feed->feed_filename);
3443 AVCodecContext *ccf, *ccs;
3447 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3449 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3450 printf("Codecs do not match for stream %d\n", i);
3452 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3453 printf("Codec bitrates do not match for stream %d\n", i);
3455 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3456 if (CHECK_CODEC(time_base.den) ||
3457 CHECK_CODEC(time_base.num) ||
3458 CHECK_CODEC(width) ||
3459 CHECK_CODEC(height)) {
3460 printf("Codec width, height and framerate do not match for stream %d\n", i);
3463 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3464 if (CHECK_CODEC(sample_rate) ||
3465 CHECK_CODEC(channels) ||
3466 CHECK_CODEC(frame_size)) {
3467 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3471 printf("Unknown codec type\n");
3480 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3481 feed->feed_filename, s->nb_streams, feed->nb_streams);
3484 av_close_input_file(s);
3486 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3487 feed->feed_filename);
3490 if (feed->readonly) {
3491 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3492 feed->feed_filename);
3495 unlink(feed->feed_filename);
3498 if (!url_exist(feed->feed_filename)) {
3499 AVFormatContext s1, *s = &s1;
3501 if (feed->readonly) {
3502 printf("Unable to create feed file '%s' as it is marked readonly\n",
3503 feed->feed_filename);
3507 /* only write the header of the ffm file */
3508 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3509 fprintf(stderr, "Could not open output feed file '%s'\n",
3510 feed->feed_filename);
3513 s->oformat = feed->fmt;
3514 s->nb_streams = feed->nb_streams;
3515 for(i=0;i<s->nb_streams;i++) {
3517 st = feed->streams[i];
3520 av_set_parameters(s, NULL);
3522 /* XXX: need better api */
3523 av_freep(&s->priv_data);
3526 /* get feed size and write index */
3527 fd = open(feed->feed_filename, O_RDONLY);
3529 fprintf(stderr, "Could not open output feed file '%s'\n",
3530 feed->feed_filename);
3534 feed->feed_write_index = ffm_read_write_index(fd);
3535 feed->feed_size = lseek(fd, 0, SEEK_END);
3536 /* ensure that we do not wrap before the end of file */
3537 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3538 feed->feed_max_size = feed->feed_size;
3544 /* compute the bandwidth used by each stream */
3545 static void compute_bandwidth(void)
3550 for(stream = first_stream; stream != NULL; stream = stream->next) {
3552 for(i=0;i<stream->nb_streams;i++) {
3553 AVStream *st = stream->streams[i];
3554 switch(st->codec->codec_type) {
3555 case CODEC_TYPE_AUDIO:
3556 case CODEC_TYPE_VIDEO:
3557 bandwidth += st->codec->bit_rate;
3563 stream->bandwidth = (bandwidth + 999) / 1000;
3567 static void get_arg(char *buf, int buf_size, const char **pp)
3574 while (isspace(*p)) p++;
3577 if (*p == '\"' || *p == '\'')
3589 if ((q - buf) < buf_size - 1)
3594 if (quote && *p == quote)
3599 /* add a codec and set the default parameters */
3600 static void add_codec(FFStream *stream, AVCodecContext *av)
3604 /* compute default parameters */
3605 switch(av->codec_type) {
3606 case CODEC_TYPE_AUDIO:
3607 if (av->bit_rate == 0)
3608 av->bit_rate = 64000;
3609 if (av->sample_rate == 0)
3610 av->sample_rate = 22050;
3611 if (av->channels == 0)
3614 case CODEC_TYPE_VIDEO:
3615 if (av->bit_rate == 0)
3616 av->bit_rate = 64000;
3617 if (av->time_base.num == 0){
3618 av->time_base.den = 5;
3619 av->time_base.num = 1;
3621 if (av->width == 0 || av->height == 0) {
3625 /* Bitrate tolerance is less for streaming */
3626 if (av->bit_rate_tolerance == 0)
3627 av->bit_rate_tolerance = av->bit_rate / 4;
3632 if (av->max_qdiff == 0)
3634 av->qcompress = 0.5;
3637 if (!av->nsse_weight)
3638 av->nsse_weight = 8;
3640 av->frame_skip_cmp = FF_CMP_DCTMAX;
3641 av->me_method = ME_EPZS;
3642 av->rc_buffer_aggressivity = 1.0;
3645 av->rc_eq = "tex^qComp";
3646 if (!av->i_quant_factor)
3647 av->i_quant_factor = -0.8;
3648 if (!av->b_quant_factor)
3649 av->b_quant_factor = 1.25;
3650 if (!av->b_quant_offset)
3651 av->b_quant_offset = 1.25;
3652 if (!av->rc_max_rate)
3653 av->rc_max_rate = av->bit_rate * 2;
3655 if (av->rc_max_rate && !av->rc_buffer_size) {
3656 av->rc_buffer_size = av->rc_max_rate;
3665 st = av_mallocz(sizeof(AVStream));
3668 st->codec = avcodec_alloc_context();
3669 stream->streams[stream->nb_streams++] = st;
3670 memcpy(st->codec, av, sizeof(AVCodecContext));
3673 static int opt_audio_codec(const char *arg)
3679 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3684 return CODEC_ID_NONE;
3690 static int opt_video_codec(const char *arg)
3696 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3701 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 parse_ffconfig(const char *filename)
3740 int val, errors, line_num;
3741 FFStream **last_stream, *stream, *redirect;
3742 FFStream **last_feed, *feed;
3743 AVCodecContext audio_enc, video_enc;
3744 int audio_id, video_id;
3746 f = fopen(filename, "r");
3754 first_stream = NULL;
3755 last_stream = &first_stream;
3757 last_feed = &first_feed;
3761 audio_id = CODEC_ID_NONE;
3762 video_id = CODEC_ID_NONE;
3764 if (fgets(line, sizeof(line), f) == NULL)
3770 if (*p == '\0' || *p == '#')
3773 get_arg(cmd, sizeof(cmd), &p);
3775 if (!strcasecmp(cmd, "Port")) {
3776 get_arg(arg, sizeof(arg), &p);
3777 my_http_addr.sin_port = htons (atoi(arg));
3778 } else if (!strcasecmp(cmd, "BindAddress")) {
3779 get_arg(arg, sizeof(arg), &p);
3780 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3781 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3782 filename, line_num, arg);
3785 } else if (!strcasecmp(cmd, "NoDaemon")) {
3786 ffserver_daemon = 0;
3787 } else if (!strcasecmp(cmd, "RTSPPort")) {
3788 get_arg(arg, sizeof(arg), &p);
3789 my_rtsp_addr.sin_port = htons (atoi(arg));
3790 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3791 get_arg(arg, sizeof(arg), &p);
3792 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3793 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3794 filename, line_num, arg);
3797 } else if (!strcasecmp(cmd, "MaxClients")) {
3798 get_arg(arg, sizeof(arg), &p);
3800 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3801 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3802 filename, line_num, arg);
3805 nb_max_connections = val;
3807 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3808 get_arg(arg, sizeof(arg), &p);
3810 if (val < 10 || val > 100000) {
3811 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3812 filename, line_num, arg);
3815 max_bandwidth = val;
3817 } else if (!strcasecmp(cmd, "CustomLog")) {
3818 get_arg(logfilename, sizeof(logfilename), &p);
3819 } else if (!strcasecmp(cmd, "<Feed")) {
3820 /*********************************************/
3821 /* Feed related options */
3823 if (stream || feed) {
3824 fprintf(stderr, "%s:%d: Already in a tag\n",
3825 filename, line_num);
3827 feed = av_mallocz(sizeof(FFStream));
3828 /* add in stream list */
3829 *last_stream = feed;
3830 last_stream = &feed->next;
3831 /* add in feed list */
3833 last_feed = &feed->next_feed;
3835 get_arg(feed->filename, sizeof(feed->filename), &p);
3836 q = strrchr(feed->filename, '>');
3839 feed->fmt = guess_format("ffm", NULL, NULL);
3840 /* defaut feed file */
3841 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3842 "/tmp/%s.ffm", feed->filename);
3843 feed->feed_max_size = 5 * 1024 * 1024;
3845 feed->feed = feed; /* self feeding :-) */
3847 } else if (!strcasecmp(cmd, "Launch")) {
3851 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3853 for (i = 0; i < 62; i++) {
3856 get_arg(argbuf, sizeof(argbuf), &p);
3860 feed->child_argv[i] = av_malloc(strlen(argbuf) + 1);
3861 strcpy(feed->child_argv[i], argbuf);
3864 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3866 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3868 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3869 inet_ntoa(my_http_addr.sin_addr),
3870 ntohs(my_http_addr.sin_port), feed->filename);
3875 fprintf(stdout, "Launch commandline: ");
3876 for (j = 0; j <= i; j++)
3877 fprintf(stdout, "%s ", feed->child_argv[j]);
3878 fprintf(stdout, "\n");
3881 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3883 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3885 } else if (stream) {
3886 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3888 } else if (!strcasecmp(cmd, "File")) {
3890 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3891 } else if (stream) {
3892 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3894 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3899 get_arg(arg, sizeof(arg), &p);
3901 fsize = strtod(p1, (char **)&p1);
3902 switch(toupper(*p1)) {
3907 fsize *= 1024 * 1024;
3910 fsize *= 1024 * 1024 * 1024;
3913 feed->feed_max_size = (int64_t)fsize;
3915 } else if (!strcasecmp(cmd, "</Feed>")) {
3917 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3918 filename, line_num);
3922 /* Make sure that we start out clean */
3923 if (unlink(feed->feed_filename) < 0
3924 && errno != ENOENT) {
3925 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3926 filename, line_num, feed->feed_filename, strerror(errno));
3932 } else if (!strcasecmp(cmd, "<Stream")) {
3933 /*********************************************/
3934 /* Stream related options */
3936 if (stream || feed) {
3937 fprintf(stderr, "%s:%d: Already in a tag\n",
3938 filename, line_num);
3940 stream = av_mallocz(sizeof(FFStream));
3941 *last_stream = stream;
3942 last_stream = &stream->next;
3944 get_arg(stream->filename, sizeof(stream->filename), &p);
3945 q = strrchr(stream->filename, '>');
3948 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3949 memset(&audio_enc, 0, sizeof(AVCodecContext));
3950 memset(&video_enc, 0, sizeof(AVCodecContext));
3951 audio_id = CODEC_ID_NONE;
3952 video_id = CODEC_ID_NONE;
3954 audio_id = stream->fmt->audio_codec;
3955 video_id = stream->fmt->video_codec;
3958 } else if (!strcasecmp(cmd, "Feed")) {
3959 get_arg(arg, sizeof(arg), &p);
3964 while (sfeed != NULL) {
3965 if (!strcmp(sfeed->filename, arg))
3967 sfeed = sfeed->next_feed;
3970 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3971 filename, line_num, arg);
3973 stream->feed = sfeed;
3976 } else if (!strcasecmp(cmd, "Format")) {
3977 get_arg(arg, sizeof(arg), &p);
3978 if (!strcmp(arg, "status")) {
3979 stream->stream_type = STREAM_TYPE_STATUS;
3982 stream->stream_type = STREAM_TYPE_LIVE;
3983 /* jpeg cannot be used here, so use single frame jpeg */
3984 if (!strcmp(arg, "jpeg"))
3985 strcpy(arg, "mjpeg");
3986 stream->fmt = guess_stream_format(arg, NULL, NULL);
3988 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3989 filename, line_num, arg);
3994 audio_id = stream->fmt->audio_codec;
3995 video_id = stream->fmt->video_codec;
3997 } else if (!strcasecmp(cmd, "InputFormat")) {
3998 stream->ifmt = av_find_input_format(arg);
3999 if (!stream->ifmt) {
4000 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4001 filename, line_num, arg);
4003 } else if (!strcasecmp(cmd, "FaviconURL")) {
4004 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4005 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4007 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4008 filename, line_num);
4011 } else if (!strcasecmp(cmd, "Author")) {
4013 get_arg(stream->author, sizeof(stream->author), &p);
4015 } else if (!strcasecmp(cmd, "Comment")) {
4017 get_arg(stream->comment, sizeof(stream->comment), &p);
4019 } else if (!strcasecmp(cmd, "Copyright")) {
4021 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4023 } else if (!strcasecmp(cmd, "Title")) {
4025 get_arg(stream->title, sizeof(stream->title), &p);
4027 } else if (!strcasecmp(cmd, "Preroll")) {
4028 get_arg(arg, sizeof(arg), &p);
4030 stream->prebuffer = atof(arg) * 1000;
4032 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4034 stream->send_on_key = 1;
4036 } else if (!strcasecmp(cmd, "AudioCodec")) {
4037 get_arg(arg, sizeof(arg), &p);
4038 audio_id = opt_audio_codec(arg);
4039 if (audio_id == CODEC_ID_NONE) {
4040 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4041 filename, line_num, arg);
4044 } else if (!strcasecmp(cmd, "VideoCodec")) {
4045 get_arg(arg, sizeof(arg), &p);
4046 video_id = opt_video_codec(arg);
4047 if (video_id == CODEC_ID_NONE) {
4048 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4049 filename, line_num, arg);
4052 } else if (!strcasecmp(cmd, "MaxTime")) {
4053 get_arg(arg, sizeof(arg), &p);
4055 stream->max_time = atof(arg) * 1000;
4057 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4058 get_arg(arg, sizeof(arg), &p);
4060 audio_enc.bit_rate = atoi(arg) * 1000;
4062 } else if (!strcasecmp(cmd, "AudioChannels")) {
4063 get_arg(arg, sizeof(arg), &p);
4065 audio_enc.channels = atoi(arg);
4067 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4068 get_arg(arg, sizeof(arg), &p);
4070 audio_enc.sample_rate = atoi(arg);
4072 } else if (!strcasecmp(cmd, "AudioQuality")) {
4073 get_arg(arg, sizeof(arg), &p);
4075 // audio_enc.quality = atof(arg) * 1000;
4077 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4079 int minrate, maxrate;
4081 get_arg(arg, sizeof(arg), &p);
4083 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4084 video_enc.rc_min_rate = minrate * 1000;
4085 video_enc.rc_max_rate = maxrate * 1000;
4087 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4088 filename, line_num, arg);
4092 } else if (!strcasecmp(cmd, "Debug")) {
4094 get_arg(arg, sizeof(arg), &p);
4095 video_enc.debug = strtol(arg,0,0);
4097 } else if (!strcasecmp(cmd, "Strict")) {
4099 get_arg(arg, sizeof(arg), &p);
4100 video_enc.strict_std_compliance = atoi(arg);
4102 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4104 get_arg(arg, sizeof(arg), &p);
4105 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4107 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4109 get_arg(arg, sizeof(arg), &p);
4110 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4112 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4113 get_arg(arg, sizeof(arg), &p);
4115 video_enc.bit_rate = atoi(arg) * 1000;
4117 } else if (!strcasecmp(cmd, "VideoSize")) {
4118 get_arg(arg, sizeof(arg), &p);
4120 parse_image_size(&video_enc.width, &video_enc.height, arg);
4121 if ((video_enc.width % 16) != 0 ||
4122 (video_enc.height % 16) != 0) {
4123 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4124 filename, line_num);
4128 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4129 get_arg(arg, sizeof(arg), &p);
4131 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4132 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4134 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4135 get_arg(arg, sizeof(arg), &p);
4137 video_enc.gop_size = atoi(arg);
4139 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4141 video_enc.gop_size = 1;
4143 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4145 video_enc.mb_decision = FF_MB_DECISION_BITS;
4147 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4149 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4150 video_enc.flags |= CODEC_FLAG_4MV;
4152 } else if (!strcasecmp(cmd, "BitExact")) {
4154 video_enc.flags |= CODEC_FLAG_BITEXACT;
4156 } else if (!strcasecmp(cmd, "DctFastint")) {
4158 video_enc.dct_algo = FF_DCT_FASTINT;
4160 } else if (!strcasecmp(cmd, "IdctSimple")) {
4162 video_enc.idct_algo = FF_IDCT_SIMPLE;
4164 } else if (!strcasecmp(cmd, "Qscale")) {
4165 get_arg(arg, sizeof(arg), &p);
4167 video_enc.flags |= CODEC_FLAG_QSCALE;
4168 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4170 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4171 get_arg(arg, sizeof(arg), &p);
4173 video_enc.max_qdiff = atoi(arg);
4174 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4175 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4176 filename, line_num);
4180 } else if (!strcasecmp(cmd, "VideoQMax")) {
4181 get_arg(arg, sizeof(arg), &p);
4183 video_enc.qmax = atoi(arg);
4184 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4185 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4186 filename, line_num);
4190 } else if (!strcasecmp(cmd, "VideoQMin")) {
4191 get_arg(arg, sizeof(arg), &p);
4193 video_enc.qmin = atoi(arg);
4194 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4195 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4196 filename, line_num);
4200 } else if (!strcasecmp(cmd, "LumaElim")) {
4201 get_arg(arg, sizeof(arg), &p);
4203 video_enc.luma_elim_threshold = atoi(arg);
4205 } else if (!strcasecmp(cmd, "ChromaElim")) {
4206 get_arg(arg, sizeof(arg), &p);
4208 video_enc.chroma_elim_threshold = atoi(arg);
4210 } else if (!strcasecmp(cmd, "LumiMask")) {
4211 get_arg(arg, sizeof(arg), &p);
4213 video_enc.lumi_masking = atof(arg);
4215 } else if (!strcasecmp(cmd, "DarkMask")) {
4216 get_arg(arg, sizeof(arg), &p);
4218 video_enc.dark_masking = atof(arg);
4220 } else if (!strcasecmp(cmd, "NoVideo")) {
4221 video_id = CODEC_ID_NONE;
4222 } else if (!strcasecmp(cmd, "NoAudio")) {
4223 audio_id = CODEC_ID_NONE;
4224 } else if (!strcasecmp(cmd, "ACL")) {
4228 get_arg(arg, sizeof(arg), &p);
4229 if (strcasecmp(arg, "allow") == 0) {
4230 acl.action = IP_ALLOW;
4231 } else if (strcasecmp(arg, "deny") == 0) {
4232 acl.action = IP_DENY;
4234 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4235 filename, line_num, arg);
4239 get_arg(arg, sizeof(arg), &p);
4241 he = gethostbyname(arg);
4243 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4244 filename, line_num, arg);
4247 /* Only take the first */
4248 acl.first.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4249 acl.last = acl.first;
4252 get_arg(arg, sizeof(arg), &p);
4255 he = gethostbyname(arg);
4257 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4258 filename, line_num, arg);
4261 /* Only take the first */
4262 acl.last.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4267 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4268 IPAddressACL **naclp = 0;
4274 naclp = &stream->acl;
4278 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4279 filename, line_num);
4285 naclp = &(*naclp)->next;
4290 } else if (!strcasecmp(cmd, "RTSPOption")) {
4291 get_arg(arg, sizeof(arg), &p);
4293 av_freep(&stream->rtsp_option);
4294 /* XXX: av_strdup ? */
4295 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4296 if (stream->rtsp_option) {
4297 strcpy(stream->rtsp_option, arg);
4300 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4301 get_arg(arg, sizeof(arg), &p);
4303 if (!inet_aton(arg, &stream->multicast_ip)) {
4304 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4305 filename, line_num, arg);
4308 stream->is_multicast = 1;
4309 stream->loop = 1; /* default is looping */
4311 } else if (!strcasecmp(cmd, "MulticastPort")) {
4312 get_arg(arg, sizeof(arg), &p);
4314 stream->multicast_port = atoi(arg);
4316 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4317 get_arg(arg, sizeof(arg), &p);
4319 stream->multicast_ttl = atoi(arg);
4321 } else if (!strcasecmp(cmd, "NoLoop")) {
4325 } else if (!strcasecmp(cmd, "</Stream>")) {
4327 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4328 filename, line_num);
4331 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4332 if (audio_id != CODEC_ID_NONE) {
4333 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4334 audio_enc.codec_id = audio_id;
4335 add_codec(stream, &audio_enc);
4337 if (video_id != CODEC_ID_NONE) {
4338 video_enc.codec_type = CODEC_TYPE_VIDEO;
4339 video_enc.codec_id = video_id;
4340 add_codec(stream, &video_enc);
4344 } else if (!strcasecmp(cmd, "<Redirect")) {
4345 /*********************************************/
4347 if (stream || feed || redirect) {
4348 fprintf(stderr, "%s:%d: Already in a tag\n",
4349 filename, line_num);
4352 redirect = av_mallocz(sizeof(FFStream));
4353 *last_stream = redirect;
4354 last_stream = &redirect->next;
4356 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4357 q = strrchr(redirect->filename, '>');
4360 redirect->stream_type = STREAM_TYPE_REDIRECT;
4362 } else if (!strcasecmp(cmd, "URL")) {
4364 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4366 } else if (!strcasecmp(cmd, "</Redirect>")) {
4368 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4369 filename, line_num);
4372 if (!redirect->feed_filename[0]) {
4373 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4374 filename, line_num);
4378 } else if (!strcasecmp(cmd, "LoadModule")) {
4379 get_arg(arg, sizeof(arg), &p);
4383 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4384 filename, line_num, arg);
4388 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4389 filename, line_num, cmd);
4403 static void write_packet(FFCodec *ffenc,
4404 uint8_t *buf, int size)
4407 AVCodecContext *enc = &ffenc->enc;
4409 mk_header(&hdr, enc, size);
4410 wptr = http_fifo.wptr;
4411 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4412 fifo_write(&http_fifo, buf, size, &wptr);
4413 /* atomic modification of wptr */
4414 http_fifo.wptr = wptr;
4415 ffenc->data_count += size;
4416 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4420 static void show_banner(void)
4422 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4425 static void show_help(void)
4428 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4429 "Hyper fast multi format Audio/Video streaming server\n"
4431 "-L : print the LICENSE\n"
4433 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4437 static void show_license(void)
4441 "FFmpeg is free software; you can redistribute it and/or\n"
4442 "modify it under the terms of the GNU Lesser General Public\n"
4443 "License as published by the Free Software Foundation; either\n"
4444 "version 2.1 of the License, or (at your option) any later version.\n"
4446 "FFmpeg is distributed in the hope that it will be useful,\n"
4447 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4448 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4449 "Lesser General Public License for more details.\n"
4451 "You should have received a copy of the GNU Lesser General Public\n"
4452 "License along with FFmpeg; if not, write to the Free Software\n"
4453 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
4457 static void handle_child_exit(int sig)
4462 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4465 for (feed = first_feed; feed; feed = feed->next) {
4466 if (feed->pid == pid) {
4467 int uptime = time(0) - feed->pid_start;
4470 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4473 /* Turn off any more restarts */
4474 feed->child_argv = 0;
4480 need_to_start_children = 1;
4483 int main(int argc, char **argv)
4485 const char *config_filename;
4487 struct sigaction sigact;
4491 config_filename = "/etc/ffserver.conf";
4493 my_program_name = argv[0];
4494 my_program_dir = getcwd(0, 0);
4495 ffserver_daemon = 1;
4498 c = getopt(argc, argv, "ndLh?f:");
4514 ffserver_daemon = 0;
4517 config_filename = optarg;
4524 putenv("http_proxy"); /* Kill the http_proxy */
4526 srandom(gettime_ms() + (getpid() << 16));
4528 /* address on which the server will handle HTTP connections */
4529 my_http_addr.sin_family = AF_INET;
4530 my_http_addr.sin_port = htons (8080);
4531 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4533 /* address on which the server will handle RTSP connections */
4534 my_rtsp_addr.sin_family = AF_INET;
4535 my_rtsp_addr.sin_port = htons (5454);
4536 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4538 nb_max_connections = 5;
4539 max_bandwidth = 1000;
4540 first_stream = NULL;
4541 logfilename[0] = '\0';
4543 memset(&sigact, 0, sizeof(sigact));
4544 sigact.sa_handler = handle_child_exit;
4545 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4546 sigaction(SIGCHLD, &sigact, 0);
4548 if (parse_ffconfig(config_filename) < 0) {
4549 fprintf(stderr, "Incorrect config file - exiting.\n");
4553 build_file_streams();
4555 build_feed_streams();
4557 compute_bandwidth();
4559 /* put the process in background and detach it from its TTY */
4560 if (ffserver_daemon) {
4567 } else if (pid > 0) {
4575 open("/dev/null", O_RDWR);
4576 if (strcmp(logfilename, "-") != 0) {
4586 signal(SIGPIPE, SIG_IGN);
4588 /* open log file if needed */
4589 if (logfilename[0] != '\0') {
4590 if (!strcmp(logfilename, "-"))
4593 logfile = fopen(logfilename, "w");
4596 if (http_server() < 0) {
4597 fprintf(stderr, "Could not start server\n");