2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
33 #include <sys/ioctl.h>
34 #ifdef HAVE_SYS_POLL_H
39 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
55 /* maximum number of simultaneous HTTP connections */
56 #define HTTP_MAX_CONNECTIONS 2000
59 HTTPSTATE_WAIT_REQUEST,
60 HTTPSTATE_SEND_HEADER,
61 HTTPSTATE_SEND_DATA_HEADER,
62 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
63 HTTPSTATE_SEND_DATA_TRAILER,
64 HTTPSTATE_RECEIVE_DATA,
65 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
68 RTSPSTATE_WAIT_REQUEST,
70 RTSPSTATE_SEND_PACKET,
73 const char *http_state[] = {
89 #define IOBUFFER_INIT_SIZE 8192
91 /* timeouts are in ms */
92 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
93 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
95 #define SYNC_TIMEOUT (10 * 1000)
98 int64_t count1, count2;
102 /* context associated with one connection */
103 typedef struct HTTPContext {
104 enum HTTPState state;
105 int fd; /* socket file descriptor */
106 struct sockaddr_in from_addr; /* origin */
107 struct pollfd *poll_entry; /* used when polling */
109 uint8_t *buffer_ptr, *buffer_end;
112 struct HTTPContext *next;
113 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
117 /* input format handling */
118 AVFormatContext *fmt_in;
119 int64_t start_time; /* In milliseconds - this wraps fairly often */
120 int64_t first_pts; /* initial pts value */
121 int64_t cur_pts; /* current pts value from the stream in us */
122 int64_t cur_frame_duration; /* duration of the current frame in us */
123 int cur_frame_bytes; /* output frame size, needed to compute
124 the time at which we send each
126 int pts_stream_index; /* stream we choose as clock reference */
127 int64_t cur_clock; /* current clock reference value in us */
128 /* output format handling */
129 struct FFStream *stream;
130 /* -1 is invalid stream */
131 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
132 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
134 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
135 int last_packet_sent; /* true if last data packet was sent */
137 DataRateData datarate;
144 int is_packetized; /* if true, the stream is packetized */
145 int packet_stream_index; /* current stream for output in state machine */
147 /* RTSP state specific */
148 uint8_t *pb_buffer; /* XXX: use that in all the code */
150 int seq; /* RTSP sequence number */
152 /* RTP state specific */
153 enum RTSPProtocol rtp_protocol;
154 char session_id[32]; /* session id */
155 AVFormatContext *rtp_ctx[MAX_STREAMS];
157 /* RTP/UDP specific */
158 URLContext *rtp_handles[MAX_STREAMS];
160 /* RTP/TCP specific */
161 struct HTTPContext *rtsp_c;
162 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
165 static AVFrame dummy_frame;
167 /* each generated stream is described here */
171 STREAM_TYPE_REDIRECT,
174 enum IPAddressAction {
179 typedef struct IPAddressACL {
180 struct IPAddressACL *next;
181 enum IPAddressAction action;
182 /* These are in host order */
183 struct in_addr first;
187 /* description of each stream of the ffserver.conf file */
188 typedef struct FFStream {
189 enum StreamType stream_type;
190 char filename[1024]; /* stream filename */
191 struct FFStream *feed; /* feed we are using (can be null if
193 AVFormatParameters *ap_in; /* input parameters */
194 AVInputFormat *ifmt; /* if non NULL, force input format */
198 int prebuffer; /* Number of millseconds early to start */
199 int64_t max_time; /* Number of milliseconds to run */
201 AVStream *streams[MAX_STREAMS];
202 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
203 char feed_filename[1024]; /* file name of the feed storage, or
204 input file name for a stream */
209 pid_t pid; /* Of ffmpeg process */
210 time_t pid_start; /* Of ffmpeg process */
212 struct FFStream *next;
213 int bandwidth; /* bandwidth, in kbits/s */
216 /* multicast specific */
218 struct in_addr multicast_ip;
219 int multicast_port; /* first port used for multicast */
221 int loop; /* if true, send the stream in loops (only meaningful if file) */
224 int feed_opened; /* true if someone is writing to the feed */
225 int is_feed; /* true if it is a feed */
226 int readonly; /* True if writing is prohibited to the file */
228 int64_t bytes_served;
229 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
230 int64_t feed_write_index; /* current write position in feed (it wraps round) */
231 int64_t feed_size; /* current size of feed */
232 struct FFStream *next_feed;
235 typedef struct FeedData {
236 long long data_count;
237 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
240 static struct sockaddr_in my_http_addr;
241 static struct sockaddr_in my_rtsp_addr;
243 static char logfilename[1024];
244 static HTTPContext *first_http_ctx;
245 static FFStream *first_feed; /* contains only feeds */
246 static FFStream *first_stream; /* contains all streams, including feeds */
248 static void new_connection(int server_fd, int is_rtsp);
249 static void close_connection(HTTPContext *c);
252 static int handle_connection(HTTPContext *c);
253 static int http_parse_request(HTTPContext *c);
254 static int http_send_data(HTTPContext *c);
255 static void compute_stats(HTTPContext *c);
256 static int open_input_stream(HTTPContext *c, const char *info);
257 static int http_start_receive_data(HTTPContext *c);
258 static int http_receive_data(HTTPContext *c);
261 static int rtsp_parse_request(HTTPContext *c);
262 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
263 static void rtsp_cmd_options(HTTPContext *c, const char *url);
264 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
265 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
266 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
267 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
270 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
271 struct in_addr my_ip);
274 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
275 FFStream *stream, const char *session_id,
276 enum RTSPProtocol rtp_protocol);
277 static int rtp_new_av_stream(HTTPContext *c,
278 int stream_index, struct sockaddr_in *dest_addr,
279 HTTPContext *rtsp_c);
281 static const char *my_program_name;
282 static const char *my_program_dir;
284 static int ffserver_debug;
285 static int ffserver_daemon;
286 static int no_launch;
287 static int need_to_start_children;
289 static int nb_max_connections;
290 static int nb_connections;
292 static int max_bandwidth;
293 static int current_bandwidth;
295 static int64_t cur_time; // Making this global saves on passing it around everywhere
297 static AVRandomState random_state;
299 static FILE *logfile = NULL;
301 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
307 vfprintf(logfile, fmt, ap);
313 static char *ctime1(char *buf2)
321 p = buf2 + strlen(p) - 1;
327 static void log_connection(HTTPContext *c)
334 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
335 inet_ntoa(c->from_addr.sin_addr),
336 ctime1(buf2), c->method, c->url,
337 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
340 static void update_datarate(DataRateData *drd, int64_t count)
342 if (!drd->time1 && !drd->count1) {
343 drd->time1 = drd->time2 = cur_time;
344 drd->count1 = drd->count2 = count;
345 } else if (cur_time - drd->time2 > 5000) {
346 drd->time1 = drd->time2;
347 drd->count1 = drd->count2;
348 drd->time2 = cur_time;
353 /* In bytes per second */
354 static int compute_datarate(DataRateData *drd, int64_t count)
356 if (cur_time == drd->time1)
359 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
363 static void start_children(FFStream *feed)
368 for (; feed; feed = feed->next) {
369 if (feed->child_argv && !feed->pid) {
370 feed->pid_start = time(0);
375 fprintf(stderr, "Unable to create children\n");
384 for (i = 3; i < 256; i++)
387 if (!ffserver_debug) {
388 i = open("/dev/null", O_RDWR);
397 av_strlcpy(pathname, my_program_name, sizeof(pathname));
399 slash = strrchr(pathname, '/');
404 strcpy(slash, "ffmpeg");
406 /* This is needed to make relative pathnames work */
407 chdir(my_program_dir);
409 signal(SIGPIPE, SIG_DFL);
411 execvp(pathname, feed->child_argv);
419 /* open a listening socket */
420 static int socket_open_listen(struct sockaddr_in *my_addr)
424 server_fd = socket(AF_INET,SOCK_STREAM,0);
431 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
433 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
435 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
437 closesocket(server_fd);
441 if (listen (server_fd, 5) < 0) {
443 closesocket(server_fd);
446 ff_socket_nonblock(server_fd, 1);
451 /* start all multicast streams */
452 static void start_multicast(void)
457 struct sockaddr_in dest_addr;
458 int default_port, stream_index;
461 for(stream = first_stream; stream != NULL; stream = stream->next) {
462 if (stream->is_multicast) {
463 /* open the RTP connection */
464 snprintf(session_id, sizeof(session_id), "%08x%08x",
465 av_random(&random_state), av_random(&random_state));
467 /* choose a port if none given */
468 if (stream->multicast_port == 0) {
469 stream->multicast_port = default_port;
473 dest_addr.sin_family = AF_INET;
474 dest_addr.sin_addr = stream->multicast_ip;
475 dest_addr.sin_port = htons(stream->multicast_port);
477 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
478 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
482 if (open_input_stream(rtp_c, "") < 0) {
483 fprintf(stderr, "Could not open input stream for stream '%s'\n",
488 /* open each RTP stream */
489 for(stream_index = 0; stream_index < stream->nb_streams;
491 dest_addr.sin_port = htons(stream->multicast_port +
493 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
494 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
495 stream->filename, stream_index);
500 /* change state to send data */
501 rtp_c->state = HTTPSTATE_SEND_DATA;
506 /* main loop of the http server */
507 static int http_server(void)
509 int server_fd, ret, rtsp_server_fd, delay, delay1;
510 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
511 HTTPContext *c, *c_next;
513 server_fd = socket_open_listen(&my_http_addr);
517 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
518 if (rtsp_server_fd < 0)
521 http_log("ffserver started.\n");
523 start_children(first_feed);
525 first_http_ctx = NULL;
531 poll_entry = poll_table;
532 poll_entry->fd = server_fd;
533 poll_entry->events = POLLIN;
536 poll_entry->fd = rtsp_server_fd;
537 poll_entry->events = POLLIN;
540 /* wait for events on each HTTP handle */
547 case HTTPSTATE_SEND_HEADER:
548 case RTSPSTATE_SEND_REPLY:
549 case RTSPSTATE_SEND_PACKET:
550 c->poll_entry = poll_entry;
552 poll_entry->events = POLLOUT;
555 case HTTPSTATE_SEND_DATA_HEADER:
556 case HTTPSTATE_SEND_DATA:
557 case HTTPSTATE_SEND_DATA_TRAILER:
558 if (!c->is_packetized) {
559 /* for TCP, we output as much as we can (may need to put a limit) */
560 c->poll_entry = poll_entry;
562 poll_entry->events = POLLOUT;
565 /* when ffserver is doing the timing, we work by
566 looking at which packet need to be sent every
568 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
573 case HTTPSTATE_WAIT_REQUEST:
574 case HTTPSTATE_RECEIVE_DATA:
575 case HTTPSTATE_WAIT_FEED:
576 case RTSPSTATE_WAIT_REQUEST:
577 /* need to catch errors */
578 c->poll_entry = poll_entry;
580 poll_entry->events = POLLIN;/* Maybe this will work */
584 c->poll_entry = NULL;
590 /* wait for an event on one connection. We poll at least every
591 second to handle timeouts */
593 ret = poll(poll_table, poll_entry - poll_table, delay);
594 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
595 ff_neterrno() != FF_NETERROR(EINTR))
599 cur_time = av_gettime() / 1000;
601 if (need_to_start_children) {
602 need_to_start_children = 0;
603 start_children(first_feed);
606 /* now handle the events */
607 for(c = first_http_ctx; c != NULL; c = c_next) {
609 if (handle_connection(c) < 0) {
610 /* close and free the connection */
616 poll_entry = poll_table;
617 /* new HTTP connection request ? */
618 if (poll_entry->revents & POLLIN)
619 new_connection(server_fd, 0);
621 /* new RTSP connection request ? */
622 if (poll_entry->revents & POLLIN)
623 new_connection(rtsp_server_fd, 1);
627 /* start waiting for a new HTTP/RTSP request */
628 static void start_wait_request(HTTPContext *c, int is_rtsp)
630 c->buffer_ptr = c->buffer;
631 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
634 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
635 c->state = RTSPSTATE_WAIT_REQUEST;
637 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
638 c->state = HTTPSTATE_WAIT_REQUEST;
642 static void new_connection(int server_fd, int is_rtsp)
644 struct sockaddr_in from_addr;
646 HTTPContext *c = NULL;
648 len = sizeof(from_addr);
649 fd = accept(server_fd, (struct sockaddr *)&from_addr,
653 ff_socket_nonblock(fd, 1);
655 /* XXX: should output a warning page when coming
656 close to the connection limit */
657 if (nb_connections >= nb_max_connections)
660 /* add a new connection */
661 c = av_mallocz(sizeof(HTTPContext));
666 c->poll_entry = NULL;
667 c->from_addr = from_addr;
668 c->buffer_size = IOBUFFER_INIT_SIZE;
669 c->buffer = av_malloc(c->buffer_size);
673 c->next = first_http_ctx;
677 start_wait_request(c, is_rtsp);
689 static void close_connection(HTTPContext *c)
691 HTTPContext **cp, *c1;
693 AVFormatContext *ctx;
697 /* remove connection from list */
698 cp = &first_http_ctx;
699 while ((*cp) != NULL) {
707 /* remove references, if any (XXX: do it faster) */
708 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
713 /* remove connection associated resources */
717 /* close each frame parser */
718 for(i=0;i<c->fmt_in->nb_streams;i++) {
719 st = c->fmt_in->streams[i];
720 if (st->codec->codec)
721 avcodec_close(st->codec);
723 av_close_input_file(c->fmt_in);
726 /* free RTP output streams if any */
729 nb_streams = c->stream->nb_streams;
731 for(i=0;i<nb_streams;i++) {
734 av_write_trailer(ctx);
737 h = c->rtp_handles[i];
744 if (!c->last_packet_sent) {
747 if (url_open_dyn_buf(&ctx->pb) >= 0) {
748 av_write_trailer(ctx);
749 url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
754 for(i=0; i<ctx->nb_streams; i++)
755 av_free(ctx->streams[i]);
757 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
758 current_bandwidth -= c->stream->bandwidth;
760 /* signal that there is no feed if we are the feeder socket */
761 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
762 c->stream->feed_opened = 0;
766 av_freep(&c->pb_buffer);
767 av_freep(&c->packet_buffer);
773 static int handle_connection(HTTPContext *c)
778 case HTTPSTATE_WAIT_REQUEST:
779 case RTSPSTATE_WAIT_REQUEST:
781 if ((c->timeout - cur_time) < 0)
783 if (c->poll_entry->revents & (POLLERR | POLLHUP))
786 /* no need to read if no events */
787 if (!(c->poll_entry->revents & POLLIN))
791 len = recv(c->fd, c->buffer_ptr, 1, 0);
793 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
794 ff_neterrno() != FF_NETERROR(EINTR))
796 } else if (len == 0) {
799 /* search for end of request. */
801 c->buffer_ptr += len;
803 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
804 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
805 /* request found : parse it and reply */
806 if (c->state == HTTPSTATE_WAIT_REQUEST) {
807 ret = http_parse_request(c);
809 ret = rtsp_parse_request(c);
813 } else if (ptr >= c->buffer_end) {
814 /* request too long: cannot do anything */
816 } else goto read_loop;
820 case HTTPSTATE_SEND_HEADER:
821 if (c->poll_entry->revents & (POLLERR | POLLHUP))
824 /* no need to write if no events */
825 if (!(c->poll_entry->revents & POLLOUT))
827 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
829 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
830 ff_neterrno() != FF_NETERROR(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);
845 /* all the buffer was sent : synchronize to the incoming stream */
846 c->state = HTTPSTATE_SEND_DATA_HEADER;
847 c->buffer_ptr = c->buffer_end = c->buffer;
852 case HTTPSTATE_SEND_DATA:
853 case HTTPSTATE_SEND_DATA_HEADER:
854 case HTTPSTATE_SEND_DATA_TRAILER:
855 /* for packetized output, we consider we can always write (the
856 input streams sets the speed). It may be better to verify
857 that we do not rely too much on the kernel queues */
858 if (!c->is_packetized) {
859 if (c->poll_entry->revents & (POLLERR | POLLHUP))
862 /* no need to read if no events */
863 if (!(c->poll_entry->revents & POLLOUT))
866 if (http_send_data(c) < 0)
868 /* close connection if trailer sent */
869 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
872 case HTTPSTATE_RECEIVE_DATA:
873 /* no need to read if no events */
874 if (c->poll_entry->revents & (POLLERR | POLLHUP))
876 if (!(c->poll_entry->revents & POLLIN))
878 if (http_receive_data(c) < 0)
881 case HTTPSTATE_WAIT_FEED:
882 /* no need to read if no events */
883 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
886 /* nothing to do, we'll be waken up by incoming feed packets */
889 case RTSPSTATE_SEND_REPLY:
890 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
891 av_freep(&c->pb_buffer);
894 /* no need to write if no events */
895 if (!(c->poll_entry->revents & POLLOUT))
897 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
899 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
900 ff_neterrno() != FF_NETERROR(EINTR)) {
901 /* error : close connection */
902 av_freep(&c->pb_buffer);
906 c->buffer_ptr += len;
907 c->data_count += len;
908 if (c->buffer_ptr >= c->buffer_end) {
909 /* all the buffer was sent : wait for a new request */
910 av_freep(&c->pb_buffer);
911 start_wait_request(c, 1);
915 case RTSPSTATE_SEND_PACKET:
916 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
917 av_freep(&c->packet_buffer);
920 /* no need to write if no events */
921 if (!(c->poll_entry->revents & POLLOUT))
923 len = send(c->fd, c->packet_buffer_ptr,
924 c->packet_buffer_end - c->packet_buffer_ptr, 0);
926 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
927 ff_neterrno() != FF_NETERROR(EINTR)) {
928 /* error : close connection */
929 av_freep(&c->packet_buffer);
933 c->packet_buffer_ptr += len;
934 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
935 /* all the buffer was sent : wait for a new request */
936 av_freep(&c->packet_buffer);
937 c->state = RTSPSTATE_WAIT_REQUEST;
941 case HTTPSTATE_READY:
950 static int extract_rates(char *rates, int ratelen, const char *request)
954 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
955 if (strncasecmp(p, "Pragma:", 7) == 0) {
956 const char *q = p + 7;
958 while (*q && *q != '\n' && isspace(*q))
961 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
967 memset(rates, 0xff, ratelen);
970 while (*q && *q != '\n' && *q != ':')
973 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
977 if (stream_no < ratelen && stream_no >= 0)
978 rates[stream_no] = rate_no;
980 while (*q && *q != '\n' && !isspace(*q))
997 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1000 int best_bitrate = 100000000;
1003 for (i = 0; i < feed->nb_streams; i++) {
1004 AVCodecContext *feed_codec = feed->streams[i]->codec;
1006 if (feed_codec->codec_id != codec->codec_id ||
1007 feed_codec->sample_rate != codec->sample_rate ||
1008 feed_codec->width != codec->width ||
1009 feed_codec->height != codec->height)
1012 /* Potential stream */
1014 /* We want the fastest stream less than bit_rate, or the slowest
1015 * faster than bit_rate
1018 if (feed_codec->bit_rate <= bit_rate) {
1019 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1020 best_bitrate = feed_codec->bit_rate;
1024 if (feed_codec->bit_rate < best_bitrate) {
1025 best_bitrate = feed_codec->bit_rate;
1034 static int modify_current_stream(HTTPContext *c, char *rates)
1037 FFStream *req = c->stream;
1038 int action_required = 0;
1040 /* Not much we can do for a feed */
1044 for (i = 0; i < req->nb_streams; i++) {
1045 AVCodecContext *codec = req->streams[i]->codec;
1049 c->switch_feed_streams[i] = req->feed_streams[i];
1052 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1055 /* Wants off or slow */
1056 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1058 /* This doesn't work well when it turns off the only stream! */
1059 c->switch_feed_streams[i] = -2;
1060 c->feed_streams[i] = -2;
1065 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1066 action_required = 1;
1069 return action_required;
1073 static void do_switch_stream(HTTPContext *c, int i)
1075 if (c->switch_feed_streams[i] >= 0) {
1077 c->feed_streams[i] = c->switch_feed_streams[i];
1080 /* Now update the stream */
1082 c->switch_feed_streams[i] = -1;
1085 /* XXX: factorize in utils.c ? */
1086 /* XXX: take care with different space meaning */
1087 static void skip_spaces(const char **pp)
1091 while (*p == ' ' || *p == '\t')
1096 static void get_word(char *buf, int buf_size, const char **pp)
1104 while (!isspace(*p) && *p != '\0') {
1105 if ((q - buf) < buf_size - 1)
1114 static int validate_acl(FFStream *stream, HTTPContext *c)
1116 enum IPAddressAction last_action = IP_DENY;
1118 struct in_addr *src = &c->from_addr.sin_addr;
1119 unsigned long src_addr = src->s_addr;
1121 for (acl = stream->acl; acl; acl = acl->next) {
1122 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1123 return (acl->action == IP_ALLOW) ? 1 : 0;
1124 last_action = acl->action;
1127 /* Nothing matched, so return not the last action */
1128 return (last_action == IP_DENY) ? 1 : 0;
1131 /* compute the real filename of a file by matching it without its
1132 extensions to all the stream filenames */
1133 static void compute_real_filename(char *filename, int max_size)
1140 /* compute filename by matching without the file extensions */
1141 av_strlcpy(file1, filename, sizeof(file1));
1142 p = strrchr(file1, '.');
1145 for(stream = first_stream; stream != NULL; stream = stream->next) {
1146 av_strlcpy(file2, stream->filename, sizeof(file2));
1147 p = strrchr(file2, '.');
1150 if (!strcmp(file1, file2)) {
1151 av_strlcpy(filename, stream->filename, max_size);
1166 /* parse http request and prepare header */
1167 static int http_parse_request(HTTPContext *c)
1170 enum RedirType redir_type;
1172 char info[1024], filename[1024];
1176 const char *mime_type;
1180 char *useragent = 0;
1183 get_word(cmd, sizeof(cmd), (const char **)&p);
1184 av_strlcpy(c->method, cmd, sizeof(c->method));
1186 if (!strcmp(cmd, "GET"))
1188 else if (!strcmp(cmd, "POST"))
1193 get_word(url, sizeof(url), (const char **)&p);
1194 av_strlcpy(c->url, url, sizeof(c->url));
1196 get_word(protocol, sizeof(protocol), (const char **)&p);
1197 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1200 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1203 http_log("New connection: %s %s\n", cmd, url);
1205 /* find the filename and the optional info string in the request */
1206 p = strchr(url, '?');
1208 av_strlcpy(info, p, sizeof(info));
1213 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1215 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1216 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1218 if (*useragent && *useragent != '\n' && isspace(*useragent))
1222 p = strchr(p, '\n');
1229 redir_type = REDIR_NONE;
1230 if (match_ext(filename, "asx")) {
1231 redir_type = REDIR_ASX;
1232 filename[strlen(filename)-1] = 'f';
1233 } else if (match_ext(filename, "asf") &&
1234 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1235 /* if this isn't WMP or lookalike, return the redirector file */
1236 redir_type = REDIR_ASF;
1237 } else if (match_ext(filename, "rpm,ram")) {
1238 redir_type = REDIR_RAM;
1239 strcpy(filename + strlen(filename)-2, "m");
1240 } else if (match_ext(filename, "rtsp")) {
1241 redir_type = REDIR_RTSP;
1242 compute_real_filename(filename, sizeof(filename) - 1);
1243 } else if (match_ext(filename, "sdp")) {
1244 redir_type = REDIR_SDP;
1245 compute_real_filename(filename, sizeof(filename) - 1);
1248 // "redirect" / request to index.html
1249 if (!strlen(filename))
1250 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1252 stream = first_stream;
1253 while (stream != NULL) {
1254 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1256 stream = stream->next;
1258 if (stream == NULL) {
1259 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1264 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1265 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1267 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1268 c->http_error = 301;
1270 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1271 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1272 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1273 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1274 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1275 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1276 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1278 /* prepare output buffer */
1279 c->buffer_ptr = c->buffer;
1281 c->state = HTTPSTATE_SEND_HEADER;
1285 /* If this is WMP, get the rate information */
1286 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1287 if (modify_current_stream(c, ratebuf)) {
1288 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1289 if (c->switch_feed_streams[i] >= 0)
1290 do_switch_stream(c, i);
1295 /* If already streaming this feed, do not let start another feeder. */
1296 if (stream->feed_opened) {
1297 snprintf(msg, sizeof(msg), "This feed is already being received.");
1301 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1302 current_bandwidth += stream->bandwidth;
1304 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1305 c->http_error = 200;
1307 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1308 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1309 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1310 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1311 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");
1312 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",
1313 current_bandwidth, max_bandwidth);
1314 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1316 /* prepare output buffer */
1317 c->buffer_ptr = c->buffer;
1319 c->state = HTTPSTATE_SEND_HEADER;
1323 if (redir_type != REDIR_NONE) {
1326 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1327 if (strncasecmp(p, "Host:", 5) == 0) {
1331 p = strchr(p, '\n');
1342 while (isspace(*hostinfo))
1345 eoh = strchr(hostinfo, '\n');
1347 if (eoh[-1] == '\r')
1350 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1351 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1352 hostbuf[eoh - hostinfo] = 0;
1354 c->http_error = 200;
1356 switch(redir_type) {
1358 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1359 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1362 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1363 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1364 hostbuf, filename, info);
1365 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1368 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1369 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1370 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1371 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1372 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1373 hostbuf, filename, info);
1376 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1377 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1378 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1379 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1380 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1381 hostbuf, filename, info);
1385 char hostname[256], *p;
1386 /* extract only hostname */
1387 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1388 p = strrchr(hostname, ':');
1391 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1392 /* XXX: incorrect mime type ? */
1393 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1394 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1395 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1396 hostname, ntohs(my_rtsp_addr.sin_port),
1403 int sdp_data_size, len;
1404 struct sockaddr_in my_addr;
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1408 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1410 len = sizeof(my_addr);
1411 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1413 /* XXX: should use a dynamic buffer */
1414 sdp_data_size = prepare_sdp_description(stream,
1417 if (sdp_data_size > 0) {
1418 memcpy(q, sdp_data, sdp_data_size);
1430 /* prepare output buffer */
1431 c->buffer_ptr = c->buffer;
1433 c->state = HTTPSTATE_SEND_HEADER;
1439 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1443 stream->conns_served++;
1445 /* XXX: add there authenticate and IP match */
1448 /* if post, it means a feed is being sent */
1449 if (!stream->is_feed) {
1450 /* However it might be a status report from WMP! Lets log the data
1451 * as it might come in handy one day
1456 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1457 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1461 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1462 client_id = strtol(p + 18, 0, 10);
1463 p = strchr(p, '\n');
1471 char *eol = strchr(logline, '\n');
1476 if (eol[-1] == '\r')
1478 http_log("%.*s\n", (int) (eol - logline), logline);
1479 c->suppress_log = 1;
1484 http_log("\nGot request:\n%s\n", c->buffer);
1487 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1490 /* Now we have to find the client_id */
1491 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1492 if (wmpc->wmp_client_id == client_id)
1496 if (wmpc && modify_current_stream(wmpc, ratebuf))
1497 wmpc->switch_pending = 1;
1500 snprintf(msg, sizeof(msg), "POST command not handled");
1504 if (http_start_receive_data(c) < 0) {
1505 snprintf(msg, sizeof(msg), "could not open feed");
1509 c->state = HTTPSTATE_RECEIVE_DATA;
1514 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1515 http_log("\nGot request:\n%s\n", c->buffer);
1518 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1521 /* open input stream */
1522 if (open_input_stream(c, info) < 0) {
1523 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1527 /* prepare http header */
1529 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1530 mime_type = c->stream->fmt->mime_type;
1532 mime_type = "application/x-octet-stream";
1533 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1535 /* for asf, we need extra headers */
1536 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1537 /* Need to allocate a client id */
1539 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1541 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);
1543 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1544 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1546 /* prepare output buffer */
1548 c->buffer_ptr = c->buffer;
1550 c->state = HTTPSTATE_SEND_HEADER;
1553 c->http_error = 404;
1555 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1556 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1557 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1558 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1559 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1560 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1561 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1563 /* prepare output buffer */
1564 c->buffer_ptr = c->buffer;
1566 c->state = HTTPSTATE_SEND_HEADER;
1570 c->http_error = 200; /* horrible : we use this value to avoid
1571 going to the send data state */
1572 c->state = HTTPSTATE_SEND_HEADER;
1576 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1578 static const char *suffix = " kMGTP";
1581 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1583 url_fprintf(pb, "%"PRId64"%c", count, *s);
1586 static void compute_stats(HTTPContext *c)
1593 ByteIOContext pb1, *pb = &pb1;
1595 if (url_open_dyn_buf(pb) < 0) {
1596 /* XXX: return an error ? */
1597 c->buffer_ptr = c->buffer;
1598 c->buffer_end = c->buffer;
1602 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1603 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1604 url_fprintf(pb, "Pragma: no-cache\r\n");
1605 url_fprintf(pb, "\r\n");
1607 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1608 if (c->stream->feed_filename)
1609 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 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
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);
1694 url_fprintf(pb, "\n");
1698 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1702 stream = stream->next;
1704 url_fprintf(pb, "</TABLE>\n");
1706 stream = first_stream;
1707 while (stream != NULL) {
1708 if (stream->feed == stream) {
1709 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1711 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1713 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1718 /* This is somewhat linux specific I guess */
1719 snprintf(ps_cmd, sizeof(ps_cmd),
1720 "ps -o \"%%cpu,cputime\" --no-headers %d",
1723 pid_stat = popen(ps_cmd, "r");
1728 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1730 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1738 url_fprintf(pb, "<p>");
1740 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");
1742 for (i = 0; i < stream->nb_streams; i++) {
1743 AVStream *st = stream->streams[i];
1744 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1745 const char *type = "unknown";
1746 char parameters[64];
1750 switch(st->codec->codec_type) {
1751 case CODEC_TYPE_AUDIO:
1753 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
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;
1824 else if (c1->feed_streams[j] >= 0)
1825 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1830 p = inet_ntoa(c1->from_addr.sin_addr);
1831 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1833 c1->stream ? c1->stream->filename : "",
1834 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1837 http_state[c1->state]);
1838 fmt_bytecount(pb, bitrate);
1839 url_fprintf(pb, "<td align=right>");
1840 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1841 url_fprintf(pb, "<td align=right>");
1842 fmt_bytecount(pb, c1->data_count);
1843 url_fprintf(pb, "\n");
1846 url_fprintf(pb, "</TABLE>\n");
1851 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1852 url_fprintf(pb, "</BODY>\n</HTML>\n");
1854 len = url_close_dyn_buf(pb, &c->pb_buffer);
1855 c->buffer_ptr = c->pb_buffer;
1856 c->buffer_end = c->pb_buffer + len;
1859 /* check if the parser needs to be opened for stream i */
1860 static void open_parser(AVFormatContext *s, int i)
1862 AVStream *st = s->streams[i];
1865 if (!st->codec->codec) {
1866 codec = avcodec_find_decoder(st->codec->codec_id);
1867 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1868 st->codec->parse_only = 1;
1869 if (avcodec_open(st->codec, codec) < 0)
1870 st->codec->parse_only = 0;
1875 static int open_input_stream(HTTPContext *c, const char *info)
1878 char input_filename[1024];
1883 /* find file name */
1884 if (c->stream->feed) {
1885 strcpy(input_filename, c->stream->feed->feed_filename);
1886 buf_size = FFM_PACKET_SIZE;
1887 /* compute position (absolute time) */
1888 if (find_info_tag(buf, sizeof(buf), "date", info))
1889 stream_pos = parse_date(buf, 0);
1890 else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1891 int prebuffer = strtol(buf, 0, 10);
1892 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1894 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1896 strcpy(input_filename, c->stream->feed_filename);
1898 /* compute position (relative time) */
1899 if (find_info_tag(buf, sizeof(buf), "date", info))
1900 stream_pos = parse_date(buf, 1);
1904 if (input_filename[0] == '\0')
1908 { time_t when = stream_pos / 1000000;
1909 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1914 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1915 buf_size, c->stream->ap_in) < 0) {
1916 http_log("%s not found", input_filename);
1921 /* open each parser */
1922 for(i=0;i<s->nb_streams;i++)
1925 /* choose stream as clock source (we favorize video stream if
1926 present) for packet sending */
1927 c->pts_stream_index = 0;
1928 for(i=0;i<c->stream->nb_streams;i++) {
1929 if (c->pts_stream_index == 0 &&
1930 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1931 c->pts_stream_index = i;
1936 if (c->fmt_in->iformat->read_seek)
1937 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1939 /* set the start time (needed for maxtime and RTP packet timing) */
1940 c->start_time = cur_time;
1941 c->first_pts = AV_NOPTS_VALUE;
1945 /* return the server clock (in us) */
1946 static int64_t get_server_clock(HTTPContext *c)
1948 /* compute current pts value from system time */
1949 return (cur_time - c->start_time) * 1000;
1952 /* return the estimated time at which the current packet must be sent
1954 static int64_t get_packet_send_clock(HTTPContext *c)
1956 int bytes_left, bytes_sent, frame_bytes;
1958 frame_bytes = c->cur_frame_bytes;
1959 if (frame_bytes <= 0)
1962 bytes_left = c->buffer_end - c->buffer_ptr;
1963 bytes_sent = frame_bytes - bytes_left;
1964 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1969 static int http_prepare_data(HTTPContext *c)
1972 AVFormatContext *ctx;
1974 av_freep(&c->pb_buffer);
1976 case HTTPSTATE_SEND_DATA_HEADER:
1977 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1978 av_strlcpy(c->fmt_ctx.author, c->stream->author,
1979 sizeof(c->fmt_ctx.author));
1980 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
1981 sizeof(c->fmt_ctx.comment));
1982 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
1983 sizeof(c->fmt_ctx.copyright));
1984 av_strlcpy(c->fmt_ctx.title, c->stream->title,
1985 sizeof(c->fmt_ctx.title));
1987 /* open output stream by using specified codecs */
1988 c->fmt_ctx.oformat = c->stream->fmt;
1989 c->fmt_ctx.nb_streams = c->stream->nb_streams;
1990 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1993 st = av_mallocz(sizeof(AVStream));
1994 st->codec= avcodec_alloc_context();
1995 c->fmt_ctx.streams[i] = st;
1996 /* if file or feed, then just take streams from FFStream struct */
1997 if (!c->stream->feed ||
1998 c->stream->feed == c->stream)
1999 src = c->stream->streams[i];
2001 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2005 st->codec->frame_number = 0; /* XXX: should be done in
2006 AVStream, not in codec */
2007 /* I'm pretty sure that this is not correct...
2008 * However, without it, we crash
2010 st->codec->coded_frame = &dummy_frame;
2012 c->got_key_frame = 0;
2014 /* prepare header and save header data in a stream */
2015 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2016 /* XXX: potential leak */
2019 c->fmt_ctx.pb.is_streamed = 1;
2021 av_set_parameters(&c->fmt_ctx, NULL);
2022 if (av_write_header(&c->fmt_ctx) < 0)
2025 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2026 c->buffer_ptr = c->pb_buffer;
2027 c->buffer_end = c->pb_buffer + len;
2029 c->state = HTTPSTATE_SEND_DATA;
2030 c->last_packet_sent = 0;
2032 case HTTPSTATE_SEND_DATA:
2033 /* find a new packet */
2037 /* read a packet from the input stream */
2038 if (c->stream->feed)
2039 ffm_set_write_index(c->fmt_in,
2040 c->stream->feed->feed_write_index,
2041 c->stream->feed->feed_size);
2043 if (c->stream->max_time &&
2044 c->stream->max_time + c->start_time - cur_time < 0)
2045 /* We have timed out */
2046 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2049 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2050 if (c->stream->feed && c->stream->feed->feed_opened) {
2051 /* if coming from feed, it means we reached the end of the
2052 ffm file, so must wait for more data */
2053 c->state = HTTPSTATE_WAIT_FEED;
2054 return 1; /* state changed */
2056 if (c->stream->loop) {
2057 av_close_input_file(c->fmt_in);
2059 if (open_input_stream(c, "") < 0)
2064 /* must send trailer now because eof or error */
2065 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2069 /* update first pts if needed */
2070 if (c->first_pts == AV_NOPTS_VALUE) {
2071 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2072 c->start_time = cur_time;
2074 /* send it to the appropriate stream */
2075 if (c->stream->feed) {
2076 /* if coming from a feed, select the right stream */
2077 if (c->switch_pending) {
2078 c->switch_pending = 0;
2079 for(i=0;i<c->stream->nb_streams;i++) {
2080 if (c->switch_feed_streams[i] == pkt.stream_index)
2081 if (pkt.flags & PKT_FLAG_KEY)
2082 do_switch_stream(c, i);
2083 if (c->switch_feed_streams[i] >= 0)
2084 c->switch_pending = 1;
2087 for(i=0;i<c->stream->nb_streams;i++) {
2088 if (c->feed_streams[i] == pkt.stream_index) {
2089 pkt.stream_index = i;
2090 if (pkt.flags & PKT_FLAG_KEY)
2091 c->got_key_frame |= 1 << i;
2092 /* See if we have all the key frames, then
2093 * we start to send. This logic is not quite
2094 * right, but it works for the case of a
2095 * single video stream with one or more
2096 * audio streams (for which every frame is
2097 * typically a key frame).
2099 if (!c->stream->send_on_key ||
2100 ((c->got_key_frame + 1) >> c->stream->nb_streams))
2105 AVCodecContext *codec;
2108 /* specific handling for RTP: we use several
2109 output stream (one for each RTP
2110 connection). XXX: need more abstract handling */
2111 if (c->is_packetized) {
2113 /* compute send time and duration */
2114 st = c->fmt_in->streams[pkt.stream_index];
2115 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2116 if (st->start_time != AV_NOPTS_VALUE)
2117 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2118 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2120 printf("index=%d pts=%0.3f duration=%0.6f\n",
2122 (double)c->cur_pts /
2124 (double)c->cur_frame_duration /
2127 /* find RTP context */
2128 c->packet_stream_index = pkt.stream_index;
2129 ctx = c->rtp_ctx[c->packet_stream_index];
2131 av_free_packet(&pkt);
2134 codec = ctx->streams[0]->codec;
2135 /* only one stream per RTP connection */
2136 pkt.stream_index = 0;
2140 codec = ctx->streams[pkt.stream_index]->codec;
2143 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2144 if (c->is_packetized) {
2145 int max_packet_size;
2146 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2147 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2149 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2150 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2152 ret = url_open_dyn_buf(&ctx->pb);
2155 /* XXX: potential leak */
2158 if (pkt.dts != AV_NOPTS_VALUE)
2159 pkt.dts = av_rescale_q(pkt.dts,
2160 c->fmt_in->streams[pkt.stream_index]->time_base,
2161 ctx->streams[pkt.stream_index]->time_base);
2162 if (pkt.pts != AV_NOPTS_VALUE)
2163 pkt.pts = av_rescale_q(pkt.pts,
2164 c->fmt_in->streams[pkt.stream_index]->time_base,
2165 ctx->streams[pkt.stream_index]->time_base);
2166 if (av_write_frame(ctx, &pkt))
2167 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2169 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2170 c->cur_frame_bytes = len;
2171 c->buffer_ptr = c->pb_buffer;
2172 c->buffer_end = c->pb_buffer + len;
2174 codec->frame_number++;
2178 av_free_packet(&pkt);
2184 case HTTPSTATE_SEND_DATA_TRAILER:
2185 /* last packet test ? */
2186 if (c->last_packet_sent || c->is_packetized)
2189 /* prepare header */
2190 if (url_open_dyn_buf(&ctx->pb) < 0) {
2191 /* XXX: potential leak */
2194 av_write_trailer(ctx);
2195 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2196 c->buffer_ptr = c->pb_buffer;
2197 c->buffer_end = c->pb_buffer + len;
2199 c->last_packet_sent = 1;
2205 /* should convert the format at the same time */
2206 /* send data starting at c->buffer_ptr to the output connection
2207 (either UDP or TCP connection) */
2208 static int http_send_data(HTTPContext *c)
2213 if (c->buffer_ptr >= c->buffer_end) {
2214 ret = http_prepare_data(c);
2218 /* state change requested */
2221 if (c->is_packetized) {
2222 /* RTP data output */
2223 len = c->buffer_end - c->buffer_ptr;
2225 /* fail safe - should never happen */
2227 c->buffer_ptr = c->buffer_end;
2230 len = (c->buffer_ptr[0] << 24) |
2231 (c->buffer_ptr[1] << 16) |
2232 (c->buffer_ptr[2] << 8) |
2234 if (len > (c->buffer_end - c->buffer_ptr))
2236 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2237 /* nothing to send yet: we can wait */
2241 c->data_count += len;
2242 update_datarate(&c->datarate, c->data_count);
2244 c->stream->bytes_served += len;
2246 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2247 /* RTP packets are sent inside the RTSP TCP connection */
2248 ByteIOContext pb1, *pb = &pb1;
2249 int interleaved_index, size;
2251 HTTPContext *rtsp_c;
2254 /* if no RTSP connection left, error */
2257 /* if already sending something, then wait. */
2258 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2260 if (url_open_dyn_buf(pb) < 0)
2262 interleaved_index = c->packet_stream_index * 2;
2263 /* RTCP packets are sent at odd indexes */
2264 if (c->buffer_ptr[1] == 200)
2265 interleaved_index++;
2266 /* write RTSP TCP header */
2268 header[1] = interleaved_index;
2269 header[2] = len >> 8;
2271 put_buffer(pb, header, 4);
2272 /* write RTP packet data */
2274 put_buffer(pb, c->buffer_ptr, len);
2275 size = url_close_dyn_buf(pb, &c->packet_buffer);
2276 /* prepare asynchronous TCP sending */
2277 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2278 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2279 c->buffer_ptr += len;
2281 /* send everything we can NOW */
2282 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2283 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2285 rtsp_c->packet_buffer_ptr += len;
2286 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2287 /* if we could not send all the data, we will
2288 send it later, so a new state is needed to
2289 "lock" the RTSP TCP connection */
2290 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2293 /* all data has been sent */
2294 av_freep(&c->packet_buffer);
2296 /* send RTP packet directly in UDP */
2298 url_write(c->rtp_handles[c->packet_stream_index],
2299 c->buffer_ptr, len);
2300 c->buffer_ptr += len;
2301 /* here we continue as we can send several packets per 10 ms slot */
2304 /* TCP data output */
2305 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2307 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2308 ff_neterrno() != FF_NETERROR(EINTR))
2309 /* error : close connection */
2314 c->buffer_ptr += len;
2316 c->data_count += len;
2317 update_datarate(&c->datarate, c->data_count);
2319 c->stream->bytes_served += len;
2327 static int http_start_receive_data(HTTPContext *c)
2331 if (c->stream->feed_opened)
2334 /* Don't permit writing to this one */
2335 if (c->stream->readonly)
2339 fd = open(c->stream->feed_filename, O_RDWR);
2344 c->stream->feed_write_index = ffm_read_write_index(fd);
2345 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2346 lseek(fd, 0, SEEK_SET);
2348 /* init buffer input */
2349 c->buffer_ptr = c->buffer;
2350 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2351 c->stream->feed_opened = 1;
2355 static int http_receive_data(HTTPContext *c)
2359 if (c->buffer_end > c->buffer_ptr) {
2362 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2364 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2365 ff_neterrno() != FF_NETERROR(EINTR))
2366 /* error : close connection */
2368 } else if (len == 0)
2369 /* end of connection : close it */
2372 c->buffer_ptr += len;
2373 c->data_count += len;
2374 update_datarate(&c->datarate, c->data_count);
2378 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2379 if (c->buffer[0] != 'f' ||
2380 c->buffer[1] != 'm') {
2381 http_log("Feed stream has become desynchronized -- disconnecting\n");
2386 if (c->buffer_ptr >= c->buffer_end) {
2387 FFStream *feed = c->stream;
2388 /* a packet has been received : write it in the store, except
2390 if (c->data_count > FFM_PACKET_SIZE) {
2392 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2393 /* XXX: use llseek or url_seek */
2394 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2395 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2397 feed->feed_write_index += FFM_PACKET_SIZE;
2398 /* update file size */
2399 if (feed->feed_write_index > c->stream->feed_size)
2400 feed->feed_size = feed->feed_write_index;
2402 /* handle wrap around if max file size reached */
2403 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2404 feed->feed_write_index = FFM_PACKET_SIZE;
2407 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2409 /* wake up any waiting connections */
2410 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2411 if (c1->state == HTTPSTATE_WAIT_FEED &&
2412 c1->stream->feed == c->stream->feed)
2413 c1->state = HTTPSTATE_SEND_DATA;
2416 /* We have a header in our hands that contains useful data */
2418 AVInputFormat *fmt_in;
2419 ByteIOContext *pb = &s.pb;
2422 memset(&s, 0, sizeof(s));
2424 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2425 pb->buf_end = c->buffer_end; /* ?? */
2426 pb->is_streamed = 1;
2428 /* use feed output format name to find corresponding input format */
2429 fmt_in = av_find_input_format(feed->fmt->name);
2433 if (fmt_in->priv_data_size > 0) {
2434 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2440 if (fmt_in->read_header(&s, 0) < 0) {
2441 av_freep(&s.priv_data);
2445 /* Now we have the actual streams */
2446 if (s.nb_streams != feed->nb_streams) {
2447 av_freep(&s.priv_data);
2450 for (i = 0; i < s.nb_streams; i++)
2451 memcpy(feed->streams[i]->codec,
2452 s.streams[i]->codec, sizeof(AVCodecContext));
2453 av_freep(&s.priv_data);
2455 c->buffer_ptr = c->buffer;
2460 c->stream->feed_opened = 0;
2465 /********************************************************************/
2468 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2475 switch(error_number) {
2476 case RTSP_STATUS_OK:
2479 case RTSP_STATUS_METHOD:
2480 str = "Method Not Allowed";
2482 case RTSP_STATUS_BANDWIDTH:
2483 str = "Not Enough Bandwidth";
2485 case RTSP_STATUS_SESSION:
2486 str = "Session Not Found";
2488 case RTSP_STATUS_STATE:
2489 str = "Method Not Valid in This State";
2491 case RTSP_STATUS_AGGREGATE:
2492 str = "Aggregate operation not allowed";
2494 case RTSP_STATUS_ONLY_AGGREGATE:
2495 str = "Only aggregate operation allowed";
2497 case RTSP_STATUS_TRANSPORT:
2498 str = "Unsupported transport";
2500 case RTSP_STATUS_INTERNAL:
2501 str = "Internal Server Error";
2503 case RTSP_STATUS_SERVICE:
2504 str = "Service Unavailable";
2506 case RTSP_STATUS_VERSION:
2507 str = "RTSP Version not supported";
2510 str = "Unknown Error";
2514 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2515 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2517 /* output GMT time */
2521 p = buf2 + strlen(p) - 1;
2524 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2527 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2529 rtsp_reply_header(c, error_number);
2530 url_fprintf(c->pb, "\r\n");
2533 static int rtsp_parse_request(HTTPContext *c)
2535 const char *p, *p1, *p2;
2542 RTSPHeader header1, *header = &header1;
2544 c->buffer_ptr[0] = '\0';
2547 get_word(cmd, sizeof(cmd), &p);
2548 get_word(url, sizeof(url), &p);
2549 get_word(protocol, sizeof(protocol), &p);
2551 av_strlcpy(c->method, cmd, sizeof(c->method));
2552 av_strlcpy(c->url, url, sizeof(c->url));
2553 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2556 if (url_open_dyn_buf(c->pb) < 0) {
2557 /* XXX: cannot do more */
2558 c->pb = NULL; /* safety */
2562 /* check version name */
2563 if (strcmp(protocol, "RTSP/1.0") != 0) {
2564 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2568 /* parse each header line */
2569 memset(header, 0, sizeof(RTSPHeader));
2570 /* skip to next line */
2571 while (*p != '\n' && *p != '\0')
2575 while (*p != '\0') {
2576 p1 = strchr(p, '\n');
2580 if (p2 > p && p2[-1] == '\r')
2582 /* skip empty line */
2586 if (len > sizeof(line) - 1)
2587 len = sizeof(line) - 1;
2588 memcpy(line, p, len);
2590 rtsp_parse_line(header, line);
2594 /* handle sequence number */
2595 c->seq = header->seq;
2597 if (!strcmp(cmd, "DESCRIBE"))
2598 rtsp_cmd_describe(c, url);
2599 else if (!strcmp(cmd, "OPTIONS"))
2600 rtsp_cmd_options(c, url);
2601 else if (!strcmp(cmd, "SETUP"))
2602 rtsp_cmd_setup(c, url, header);
2603 else if (!strcmp(cmd, "PLAY"))
2604 rtsp_cmd_play(c, url, header);
2605 else if (!strcmp(cmd, "PAUSE"))
2606 rtsp_cmd_pause(c, url, header);
2607 else if (!strcmp(cmd, "TEARDOWN"))
2608 rtsp_cmd_teardown(c, url, header);
2610 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2613 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2614 c->pb = NULL; /* safety */
2616 /* XXX: cannot do more */
2619 c->buffer_ptr = c->pb_buffer;
2620 c->buffer_end = c->pb_buffer + len;
2621 c->state = RTSPSTATE_SEND_REPLY;
2625 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2627 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2628 struct in_addr my_ip)
2630 ByteIOContext pb1, *pb = &pb1;
2631 int i, payload_type, port, private_payload_type, j;
2632 const char *ipstr, *title, *mediatype;
2635 if (url_open_dyn_buf(pb) < 0)
2638 /* general media info */
2640 url_fprintf(pb, "v=0\n");
2641 ipstr = inet_ntoa(my_ip);
2642 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2643 title = stream->title;
2644 if (title[0] == '\0')
2646 url_fprintf(pb, "s=%s\n", title);
2647 if (stream->comment[0] != '\0')
2648 url_fprintf(pb, "i=%s\n", stream->comment);
2649 if (stream->is_multicast)
2650 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2652 /* for each stream, we output the necessary info */
2653 private_payload_type = RTP_PT_PRIVATE;
2654 for(i = 0; i < stream->nb_streams; i++) {
2655 st = stream->streams[i];
2656 if (st->codec->codec_id == CODEC_ID_MPEG2TS)
2657 mediatype = "video";
2659 switch(st->codec->codec_type) {
2660 case CODEC_TYPE_AUDIO:
2661 mediatype = "audio";
2663 case CODEC_TYPE_VIDEO:
2664 mediatype = "video";
2667 mediatype = "application";
2671 /* NOTE: the port indication is not correct in case of
2672 unicast. It is not an issue because RTSP gives it */
2673 payload_type = rtp_get_payload_type(st->codec);
2674 if (payload_type < 0)
2675 payload_type = private_payload_type++;
2676 if (stream->is_multicast)
2677 port = stream->multicast_port + 2 * i;
2681 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2682 mediatype, port, payload_type);
2683 if (payload_type >= RTP_PT_PRIVATE) {
2684 /* for private payload type, we need to give more info */
2685 switch(st->codec->codec_id) {
2686 case CODEC_ID_MPEG4:
2689 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2690 payload_type, 90000);
2691 /* we must also add the mpeg4 header */
2692 data = st->codec->extradata;
2694 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2695 for(j=0;j<st->codec->extradata_size;j++)
2696 url_fprintf(pb, "%02x", data[j]);
2697 url_fprintf(pb, "\n");
2702 /* XXX: add other codecs ? */
2706 url_fprintf(pb, "a=control:streamid=%d\n", i);
2708 return url_close_dyn_buf(pb, pbuffer);
2710 url_close_dyn_buf(pb, pbuffer);
2715 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2717 // rtsp_reply_header(c, RTSP_STATUS_OK);
2718 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2719 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2720 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2721 url_fprintf(c->pb, "\r\n");
2724 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2730 int content_length, len;
2731 struct sockaddr_in my_addr;
2733 /* find which url is asked */
2734 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2739 for(stream = first_stream; stream != NULL; stream = stream->next) {
2740 if (!stream->is_feed && stream->fmt == &rtp_muxer &&
2741 !strcmp(path, stream->filename)) {
2745 /* no stream found */
2746 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2750 /* prepare the media description in sdp format */
2752 /* get the host IP */
2753 len = sizeof(my_addr);
2754 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2755 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2756 if (content_length < 0) {
2757 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2760 rtsp_reply_header(c, RTSP_STATUS_OK);
2761 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2762 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2763 url_fprintf(c->pb, "\r\n");
2764 put_buffer(c->pb, content, content_length);
2767 static HTTPContext *find_rtp_session(const char *session_id)
2771 if (session_id[0] == '\0')
2774 for(c = first_http_ctx; c != NULL; c = c->next) {
2775 if (!strcmp(c->session_id, session_id))
2781 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2783 RTSPTransportField *th;
2786 for(i=0;i<h->nb_transports;i++) {
2787 th = &h->transports[i];
2788 if (th->protocol == protocol)
2794 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2798 int stream_index, port;
2803 RTSPTransportField *th;
2804 struct sockaddr_in dest_addr;
2805 RTSPActionServerSetup setup;
2807 /* find which url is asked */
2808 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2813 /* now check each stream */
2814 for(stream = first_stream; stream != NULL; stream = stream->next) {
2815 if (!stream->is_feed && stream->fmt == &rtp_muxer) {
2816 /* accept aggregate filenames only if single stream */
2817 if (!strcmp(path, stream->filename)) {
2818 if (stream->nb_streams != 1) {
2819 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2826 for(stream_index = 0; stream_index < stream->nb_streams;
2828 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2829 stream->filename, stream_index);
2830 if (!strcmp(path, buf))
2835 /* no stream found */
2836 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2840 /* generate session id if needed */
2841 if (h->session_id[0] == '\0')
2842 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2843 av_random(&random_state), av_random(&random_state));
2845 /* find rtp session, and create it if none found */
2846 rtp_c = find_rtp_session(h->session_id);
2848 /* always prefer UDP */
2849 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2851 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2853 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2858 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2861 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2865 /* open input stream */
2866 if (open_input_stream(rtp_c, "") < 0) {
2867 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2872 /* test if stream is OK (test needed because several SETUP needs
2873 to be done for a given file) */
2874 if (rtp_c->stream != stream) {
2875 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2879 /* test if stream is already set up */
2880 if (rtp_c->rtp_ctx[stream_index]) {
2881 rtsp_reply_error(c, RTSP_STATUS_STATE);
2885 /* check transport */
2886 th = find_transport(h, rtp_c->rtp_protocol);
2887 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2888 th->client_port_min <= 0)) {
2889 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2893 /* setup default options */
2894 setup.transport_option[0] = '\0';
2895 dest_addr = rtp_c->from_addr;
2896 dest_addr.sin_port = htons(th->client_port_min);
2899 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2900 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2904 /* now everything is OK, so we can send the connection parameters */
2905 rtsp_reply_header(c, RTSP_STATUS_OK);
2907 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2909 switch(rtp_c->rtp_protocol) {
2910 case RTSP_PROTOCOL_RTP_UDP:
2911 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2912 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2913 "client_port=%d-%d;server_port=%d-%d",
2914 th->client_port_min, th->client_port_min + 1,
2917 case RTSP_PROTOCOL_RTP_TCP:
2918 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2919 stream_index * 2, stream_index * 2 + 1);
2924 if (setup.transport_option[0] != '\0')
2925 url_fprintf(c->pb, ";%s", setup.transport_option);
2926 url_fprintf(c->pb, "\r\n");
2929 url_fprintf(c->pb, "\r\n");
2933 /* find an rtp connection by using the session ID. Check consistency
2935 static HTTPContext *find_rtp_session_with_url(const char *url,
2936 const char *session_id)
2944 rtp_c = find_rtp_session(session_id);
2948 /* find which url is asked */
2949 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2953 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2954 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2955 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2956 rtp_c->stream->filename, s);
2957 if(!strncmp(path, buf, sizeof(buf))) {
2958 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2965 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2969 rtp_c = find_rtp_session_with_url(url, h->session_id);
2971 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2975 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2976 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2977 rtp_c->state != HTTPSTATE_READY) {
2978 rtsp_reply_error(c, RTSP_STATUS_STATE);
2983 /* XXX: seek in stream */
2984 if (h->range_start != AV_NOPTS_VALUE) {
2985 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2986 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2990 rtp_c->state = HTTPSTATE_SEND_DATA;
2992 /* now everything is OK, so we can send the connection parameters */
2993 rtsp_reply_header(c, RTSP_STATUS_OK);
2995 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2996 url_fprintf(c->pb, "\r\n");
2999 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3003 rtp_c = find_rtp_session_with_url(url, h->session_id);
3005 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3009 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3010 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3011 rtsp_reply_error(c, RTSP_STATUS_STATE);
3015 rtp_c->state = HTTPSTATE_READY;
3016 rtp_c->first_pts = AV_NOPTS_VALUE;
3017 /* now everything is OK, so we can send the connection parameters */
3018 rtsp_reply_header(c, RTSP_STATUS_OK);
3020 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3021 url_fprintf(c->pb, "\r\n");
3024 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3027 char session_id[32];
3029 rtp_c = find_rtp_session_with_url(url, h->session_id);
3031 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3035 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3037 /* abort the session */
3038 close_connection(rtp_c);
3040 /* now everything is OK, so we can send the connection parameters */
3041 rtsp_reply_header(c, RTSP_STATUS_OK);
3043 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3044 url_fprintf(c->pb, "\r\n");
3048 /********************************************************************/
3051 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3052 FFStream *stream, const char *session_id,
3053 enum RTSPProtocol rtp_protocol)
3055 HTTPContext *c = NULL;
3056 const char *proto_str;
3058 /* XXX: should output a warning page when coming
3059 close to the connection limit */
3060 if (nb_connections >= nb_max_connections)
3063 /* add a new connection */
3064 c = av_mallocz(sizeof(HTTPContext));
3069 c->poll_entry = NULL;
3070 c->from_addr = *from_addr;
3071 c->buffer_size = IOBUFFER_INIT_SIZE;
3072 c->buffer = av_malloc(c->buffer_size);
3077 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3078 c->state = HTTPSTATE_READY;
3079 c->is_packetized = 1;
3080 c->rtp_protocol = rtp_protocol;
3082 /* protocol is shown in statistics */
3083 switch(c->rtp_protocol) {
3084 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3085 proto_str = "MCAST";
3087 case RTSP_PROTOCOL_RTP_UDP:
3090 case RTSP_PROTOCOL_RTP_TCP:
3097 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3098 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3100 current_bandwidth += stream->bandwidth;
3102 c->next = first_http_ctx;
3114 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3115 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3117 static int rtp_new_av_stream(HTTPContext *c,
3118 int stream_index, struct sockaddr_in *dest_addr,
3119 HTTPContext *rtsp_c)
3121 AVFormatContext *ctx;
3127 int max_packet_size;
3129 /* now we can open the relevant output stream */
3130 ctx = av_alloc_format_context();
3133 ctx->oformat = &rtp_muxer;
3135 st = av_mallocz(sizeof(AVStream));
3138 st->codec= avcodec_alloc_context();
3139 ctx->nb_streams = 1;
3140 ctx->streams[0] = st;
3142 if (!c->stream->feed ||
3143 c->stream->feed == c->stream)
3144 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3147 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3149 st->priv_data = NULL;
3151 /* build destination RTP address */
3152 ipaddr = inet_ntoa(dest_addr->sin_addr);
3154 switch(c->rtp_protocol) {
3155 case RTSP_PROTOCOL_RTP_UDP:
3156 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3159 /* XXX: also pass as parameter to function ? */
3160 if (c->stream->is_multicast) {
3162 ttl = c->stream->multicast_ttl;
3165 snprintf(ctx->filename, sizeof(ctx->filename),
3166 "rtp://%s:%d?multicast=1&ttl=%d",
3167 ipaddr, ntohs(dest_addr->sin_port), ttl);
3169 snprintf(ctx->filename, sizeof(ctx->filename),
3170 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3173 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3175 c->rtp_handles[stream_index] = h;
3176 max_packet_size = url_get_max_packet_size(h);
3178 case RTSP_PROTOCOL_RTP_TCP:
3181 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3187 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3188 ipaddr, ntohs(dest_addr->sin_port),
3190 c->stream->filename, stream_index, c->protocol);
3192 /* normally, no packets should be output here, but the packet size may be checked */
3193 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3194 /* XXX: close stream */
3197 av_set_parameters(ctx, NULL);
3198 if (av_write_header(ctx) < 0) {
3205 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3208 c->rtp_ctx[stream_index] = ctx;
3212 /********************************************************************/
3213 /* ffserver initialization */
3215 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3219 fst = av_mallocz(sizeof(AVStream));
3222 fst->codec= avcodec_alloc_context();
3223 fst->priv_data = av_mallocz(sizeof(FeedData));
3224 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3225 fst->codec->coded_frame = &dummy_frame;
3226 fst->index = stream->nb_streams;
3227 av_set_pts_info(fst, 33, 1, 90000);
3228 stream->streams[stream->nb_streams++] = fst;
3232 /* return the stream number in the feed */
3233 static int add_av_stream(FFStream *feed, AVStream *st)
3236 AVCodecContext *av, *av1;
3240 for(i=0;i<feed->nb_streams;i++) {
3241 st = feed->streams[i];
3243 if (av1->codec_id == av->codec_id &&
3244 av1->codec_type == av->codec_type &&
3245 av1->bit_rate == av->bit_rate) {
3247 switch(av->codec_type) {
3248 case CODEC_TYPE_AUDIO:
3249 if (av1->channels == av->channels &&
3250 av1->sample_rate == av->sample_rate)
3253 case CODEC_TYPE_VIDEO:
3254 if (av1->width == av->width &&
3255 av1->height == av->height &&
3256 av1->time_base.den == av->time_base.den &&
3257 av1->time_base.num == av->time_base.num &&
3258 av1->gop_size == av->gop_size)
3267 fst = add_av_stream1(feed, av);
3270 return feed->nb_streams - 1;
3275 static void remove_stream(FFStream *stream)
3279 while (*ps != NULL) {
3287 /* specific mpeg4 handling : we extract the raw parameters */
3288 static void extract_mpeg4_header(AVFormatContext *infile)
3290 int mpeg4_count, i, size;
3296 for(i=0;i<infile->nb_streams;i++) {
3297 st = infile->streams[i];
3298 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3299 st->codec->extradata_size == 0) {
3306 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3307 while (mpeg4_count > 0) {
3308 if (av_read_packet(infile, &pkt) < 0)
3310 st = infile->streams[pkt.stream_index];
3311 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3312 st->codec->extradata_size == 0) {
3313 av_freep(&st->codec->extradata);
3314 /* fill extradata with the header */
3315 /* XXX: we make hard suppositions here ! */
3317 while (p < pkt.data + pkt.size - 4) {
3318 /* stop when vop header is found */
3319 if (p[0] == 0x00 && p[1] == 0x00 &&
3320 p[2] == 0x01 && p[3] == 0xb6) {
3321 size = p - pkt.data;
3322 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3323 st->codec->extradata = av_malloc(size);
3324 st->codec->extradata_size = size;
3325 memcpy(st->codec->extradata, pkt.data, size);
3332 av_free_packet(&pkt);
3336 /* compute the needed AVStream for each file */
3337 static void build_file_streams(void)
3339 FFStream *stream, *stream_next;
3340 AVFormatContext *infile;
3343 /* gather all streams */
3344 for(stream = first_stream; stream != NULL; stream = stream_next) {
3345 stream_next = stream->next;
3346 if (stream->stream_type == STREAM_TYPE_LIVE &&
3348 /* the stream comes from a file */
3349 /* try to open the file */
3351 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3352 if (stream->fmt == &rtp_muxer) {
3353 /* specific case : if transport stream output to RTP,
3354 we use a raw transport stream reader */
3355 stream->ap_in->mpeg2ts_raw = 1;
3356 stream->ap_in->mpeg2ts_compute_pcr = 1;
3359 if (av_open_input_file(&infile, stream->feed_filename,
3360 stream->ifmt, 0, stream->ap_in) < 0) {
3361 http_log("%s not found", stream->feed_filename);
3362 /* remove stream (no need to spend more time on it) */
3364 remove_stream(stream);
3366 /* find all the AVStreams inside and reference them in
3368 if (av_find_stream_info(infile) < 0) {
3369 http_log("Could not find codec parameters from '%s'",
3370 stream->feed_filename);
3371 av_close_input_file(infile);
3374 extract_mpeg4_header(infile);
3376 for(i=0;i<infile->nb_streams;i++)
3377 add_av_stream1(stream, infile->streams[i]->codec);
3379 av_close_input_file(infile);
3385 /* compute the needed AVStream for each feed */
3386 static void build_feed_streams(void)
3388 FFStream *stream, *feed;
3391 /* gather all streams */
3392 for(stream = first_stream; stream != NULL; stream = stream->next) {
3393 feed = stream->feed;
3395 if (!stream->is_feed) {
3396 /* we handle a stream coming from a feed */
3397 for(i=0;i<stream->nb_streams;i++)
3398 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3403 /* gather all streams */
3404 for(stream = first_stream; stream != NULL; stream = stream->next) {
3405 feed = stream->feed;
3407 if (stream->is_feed) {
3408 for(i=0;i<stream->nb_streams;i++)
3409 stream->feed_streams[i] = i;
3414 /* create feed files if needed */
3415 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3418 if (url_exist(feed->feed_filename)) {
3419 /* See if it matches */
3423 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3424 /* Now see if it matches */
3425 if (s->nb_streams == feed->nb_streams) {
3427 for(i=0;i<s->nb_streams;i++) {
3429 sf = feed->streams[i];
3432 if (sf->index != ss->index ||
3434 printf("Index & Id do not match for stream %d (%s)\n",
3435 i, feed->feed_filename);
3438 AVCodecContext *ccf, *ccs;
3442 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3444 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3445 printf("Codecs do not match for stream %d\n", i);
3447 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3448 printf("Codec bitrates do not match for stream %d\n", i);
3450 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3451 if (CHECK_CODEC(time_base.den) ||
3452 CHECK_CODEC(time_base.num) ||
3453 CHECK_CODEC(width) ||
3454 CHECK_CODEC(height)) {
3455 printf("Codec width, height and framerate do not match for stream %d\n", i);
3458 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3459 if (CHECK_CODEC(sample_rate) ||
3460 CHECK_CODEC(channels) ||
3461 CHECK_CODEC(frame_size)) {
3462 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3466 printf("Unknown codec type\n");
3474 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3475 feed->feed_filename, s->nb_streams, feed->nb_streams);
3477 av_close_input_file(s);
3479 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3480 feed->feed_filename);
3483 if (feed->readonly) {
3484 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3485 feed->feed_filename);
3488 unlink(feed->feed_filename);
3491 if (!url_exist(feed->feed_filename)) {
3492 AVFormatContext s1, *s = &s1;
3494 if (feed->readonly) {
3495 printf("Unable to create feed file '%s' as it is marked readonly\n",
3496 feed->feed_filename);
3500 /* only write the header of the ffm file */
3501 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3502 fprintf(stderr, "Could not open output feed file '%s'\n",
3503 feed->feed_filename);
3506 s->oformat = feed->fmt;
3507 s->nb_streams = feed->nb_streams;
3508 for(i=0;i<s->nb_streams;i++) {
3510 st = feed->streams[i];
3513 av_set_parameters(s, NULL);
3514 if (av_write_header(s) < 0) {
3515 fprintf(stderr, "Container doesn't supports the required parameters\n");
3518 /* XXX: need better api */
3519 av_freep(&s->priv_data);
3522 /* get feed size and write index */
3523 fd = open(feed->feed_filename, O_RDONLY);
3525 fprintf(stderr, "Could not open output feed file '%s'\n",
3526 feed->feed_filename);
3530 feed->feed_write_index = ffm_read_write_index(fd);
3531 feed->feed_size = lseek(fd, 0, SEEK_END);
3532 /* ensure that we do not wrap before the end of file */
3533 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3534 feed->feed_max_size = feed->feed_size;
3540 /* compute the bandwidth used by each stream */
3541 static void compute_bandwidth(void)
3546 for(stream = first_stream; stream != NULL; stream = stream->next) {
3548 for(i=0;i<stream->nb_streams;i++) {
3549 AVStream *st = stream->streams[i];
3550 switch(st->codec->codec_type) {
3551 case CODEC_TYPE_AUDIO:
3552 case CODEC_TYPE_VIDEO:
3553 bandwidth += st->codec->bit_rate;
3559 stream->bandwidth = (bandwidth + 999) / 1000;
3563 static void get_arg(char *buf, int buf_size, const char **pp)
3570 while (isspace(*p)) p++;
3573 if (*p == '\"' || *p == '\'')
3585 if ((q - buf) < buf_size - 1)
3590 if (quote && *p == quote)
3595 /* add a codec and set the default parameters */
3596 static void add_codec(FFStream *stream, AVCodecContext *av)
3600 /* compute default parameters */
3601 switch(av->codec_type) {
3602 case CODEC_TYPE_AUDIO:
3603 if (av->bit_rate == 0)
3604 av->bit_rate = 64000;
3605 if (av->sample_rate == 0)
3606 av->sample_rate = 22050;
3607 if (av->channels == 0)
3610 case CODEC_TYPE_VIDEO:
3611 if (av->bit_rate == 0)
3612 av->bit_rate = 64000;
3613 if (av->time_base.num == 0){
3614 av->time_base.den = 5;
3615 av->time_base.num = 1;
3617 if (av->width == 0 || av->height == 0) {
3621 /* Bitrate tolerance is less for streaming */
3622 if (av->bit_rate_tolerance == 0)
3623 av->bit_rate_tolerance = av->bit_rate / 4;
3628 if (av->max_qdiff == 0)
3630 av->qcompress = 0.5;
3633 if (!av->nsse_weight)
3634 av->nsse_weight = 8;
3636 av->frame_skip_cmp = FF_CMP_DCTMAX;
3637 av->me_method = ME_EPZS;
3638 av->rc_buffer_aggressivity = 1.0;
3641 av->rc_eq = "tex^qComp";
3642 if (!av->i_quant_factor)
3643 av->i_quant_factor = -0.8;
3644 if (!av->b_quant_factor)
3645 av->b_quant_factor = 1.25;
3646 if (!av->b_quant_offset)
3647 av->b_quant_offset = 1.25;
3648 if (!av->rc_max_rate)
3649 av->rc_max_rate = av->bit_rate * 2;
3651 if (av->rc_max_rate && !av->rc_buffer_size) {
3652 av->rc_buffer_size = av->rc_max_rate;
3661 st = av_mallocz(sizeof(AVStream));
3664 st->codec = avcodec_alloc_context();
3665 stream->streams[stream->nb_streams++] = st;
3666 memcpy(st->codec, av, sizeof(AVCodecContext));
3669 static int opt_audio_codec(const char *arg)
3675 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3680 return CODEC_ID_NONE;
3685 static int opt_video_codec(const char *arg)
3691 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3696 return CODEC_ID_NONE;
3701 /* simplistic plugin support */
3704 static void load_module(const char *filename)
3707 void (*init_func)(void);
3708 dll = dlopen(filename, RTLD_NOW);
3710 fprintf(stderr, "Could not load module '%s' - %s\n",
3711 filename, dlerror());
3715 init_func = dlsym(dll, "ffserver_module_init");
3718 "%s: init function 'ffserver_module_init()' not found\n",
3727 static int parse_ffconfig(const char *filename)
3734 int val, errors, line_num;
3735 FFStream **last_stream, *stream, *redirect;
3736 FFStream **last_feed, *feed;
3737 AVCodecContext audio_enc, video_enc;
3738 int audio_id, video_id;
3740 f = fopen(filename, "r");
3748 first_stream = NULL;
3749 last_stream = &first_stream;
3751 last_feed = &first_feed;
3755 audio_id = CODEC_ID_NONE;
3756 video_id = CODEC_ID_NONE;
3758 if (fgets(line, sizeof(line), f) == NULL)
3764 if (*p == '\0' || *p == '#')
3767 get_arg(cmd, sizeof(cmd), &p);
3769 if (!strcasecmp(cmd, "Port")) {
3770 get_arg(arg, sizeof(arg), &p);
3772 if (val < 1 || val > 65536) {
3773 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3774 filename, line_num, arg);
3777 my_http_addr.sin_port = htons(val);
3778 } else if (!strcasecmp(cmd, "BindAddress")) {
3779 get_arg(arg, sizeof(arg), &p);
3780 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3781 fprintf(stderr, "%s:%d: Invalid host/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);
3790 if (val < 1 || val > 65536) {
3791 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3792 filename, line_num, arg);
3795 my_rtsp_addr.sin_port = htons(atoi(arg));
3796 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3797 get_arg(arg, sizeof(arg), &p);
3798 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3799 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3800 filename, line_num, arg);
3803 } else if (!strcasecmp(cmd, "MaxClients")) {
3804 get_arg(arg, sizeof(arg), &p);
3806 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3807 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3808 filename, line_num, arg);
3811 nb_max_connections = val;
3813 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3814 get_arg(arg, sizeof(arg), &p);
3816 if (val < 10 || val > 100000) {
3817 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3818 filename, line_num, arg);
3821 max_bandwidth = val;
3822 } else if (!strcasecmp(cmd, "CustomLog")) {
3823 get_arg(logfilename, sizeof(logfilename), &p);
3824 } else if (!strcasecmp(cmd, "<Feed")) {
3825 /*********************************************/
3826 /* Feed related options */
3828 if (stream || feed) {
3829 fprintf(stderr, "%s:%d: Already in a tag\n",
3830 filename, line_num);
3832 feed = av_mallocz(sizeof(FFStream));
3833 /* add in stream list */
3834 *last_stream = feed;
3835 last_stream = &feed->next;
3836 /* add in feed list */
3838 last_feed = &feed->next_feed;
3840 get_arg(feed->filename, sizeof(feed->filename), &p);
3841 q = strrchr(feed->filename, '>');
3844 feed->fmt = guess_format("ffm", NULL, NULL);
3845 /* defaut feed file */
3846 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3847 "/tmp/%s.ffm", feed->filename);
3848 feed->feed_max_size = 5 * 1024 * 1024;
3850 feed->feed = feed; /* self feeding :-) */
3852 } else if (!strcasecmp(cmd, "Launch")) {
3856 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3858 for (i = 0; i < 62; i++) {
3859 get_arg(arg, sizeof(arg), &p);
3863 feed->child_argv[i] = av_strdup(arg);
3866 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3868 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3870 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3871 inet_ntoa(my_http_addr.sin_addr),
3872 ntohs(my_http_addr.sin_port), feed->filename);
3877 fprintf(stdout, "Launch commandline: ");
3878 for (j = 0; j <= i; j++)
3879 fprintf(stdout, "%s ", feed->child_argv[j]);
3880 fprintf(stdout, "\n");
3883 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3885 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3887 } else if (stream) {
3888 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3890 } else if (!strcasecmp(cmd, "File")) {
3892 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3894 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3895 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3900 get_arg(arg, sizeof(arg), &p);
3902 fsize = strtod(p1, (char **)&p1);
3903 switch(toupper(*p1)) {
3908 fsize *= 1024 * 1024;
3911 fsize *= 1024 * 1024 * 1024;
3914 feed->feed_max_size = (int64_t)fsize;
3916 } else if (!strcasecmp(cmd, "</Feed>")) {
3918 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3919 filename, line_num);
3923 } else if (!strcasecmp(cmd, "<Stream")) {
3924 /*********************************************/
3925 /* Stream related options */
3927 if (stream || feed) {
3928 fprintf(stderr, "%s:%d: Already in a tag\n",
3929 filename, line_num);
3931 stream = av_mallocz(sizeof(FFStream));
3932 *last_stream = stream;
3933 last_stream = &stream->next;
3935 get_arg(stream->filename, sizeof(stream->filename), &p);
3936 q = strrchr(stream->filename, '>');
3939 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3940 memset(&audio_enc, 0, sizeof(AVCodecContext));
3941 memset(&video_enc, 0, sizeof(AVCodecContext));
3942 audio_id = CODEC_ID_NONE;
3943 video_id = CODEC_ID_NONE;
3945 audio_id = stream->fmt->audio_codec;
3946 video_id = stream->fmt->video_codec;
3949 } else if (!strcasecmp(cmd, "Feed")) {
3950 get_arg(arg, sizeof(arg), &p);
3955 while (sfeed != NULL) {
3956 if (!strcmp(sfeed->filename, arg))
3958 sfeed = sfeed->next_feed;
3961 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3962 filename, line_num, arg);
3964 stream->feed = sfeed;
3966 } else if (!strcasecmp(cmd, "Format")) {
3967 get_arg(arg, sizeof(arg), &p);
3968 if (!strcmp(arg, "status")) {
3969 stream->stream_type = STREAM_TYPE_STATUS;
3972 stream->stream_type = STREAM_TYPE_LIVE;
3973 /* jpeg cannot be used here, so use single frame jpeg */
3974 if (!strcmp(arg, "jpeg"))
3975 strcpy(arg, "mjpeg");
3976 stream->fmt = guess_stream_format(arg, NULL, NULL);
3978 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3979 filename, line_num, arg);
3984 audio_id = stream->fmt->audio_codec;
3985 video_id = stream->fmt->video_codec;
3987 } else if (!strcasecmp(cmd, "InputFormat")) {
3988 get_arg(arg, sizeof(arg), &p);
3989 stream->ifmt = av_find_input_format(arg);
3990 if (!stream->ifmt) {
3991 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
3992 filename, line_num, arg);
3994 } else if (!strcasecmp(cmd, "FaviconURL")) {
3995 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3996 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3998 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3999 filename, line_num);
4002 } else if (!strcasecmp(cmd, "Author")) {
4004 get_arg(stream->author, sizeof(stream->author), &p);
4005 } else if (!strcasecmp(cmd, "Comment")) {
4007 get_arg(stream->comment, sizeof(stream->comment), &p);
4008 } else if (!strcasecmp(cmd, "Copyright")) {
4010 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4011 } else if (!strcasecmp(cmd, "Title")) {
4013 get_arg(stream->title, sizeof(stream->title), &p);
4014 } else if (!strcasecmp(cmd, "Preroll")) {
4015 get_arg(arg, sizeof(arg), &p);
4017 stream->prebuffer = atof(arg) * 1000;
4018 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4020 stream->send_on_key = 1;
4021 } else if (!strcasecmp(cmd, "AudioCodec")) {
4022 get_arg(arg, sizeof(arg), &p);
4023 audio_id = opt_audio_codec(arg);
4024 if (audio_id == CODEC_ID_NONE) {
4025 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4026 filename, line_num, arg);
4029 } else if (!strcasecmp(cmd, "VideoCodec")) {
4030 get_arg(arg, sizeof(arg), &p);
4031 video_id = opt_video_codec(arg);
4032 if (video_id == CODEC_ID_NONE) {
4033 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4034 filename, line_num, arg);
4037 } else if (!strcasecmp(cmd, "MaxTime")) {
4038 get_arg(arg, sizeof(arg), &p);
4040 stream->max_time = atof(arg) * 1000;
4041 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4042 get_arg(arg, sizeof(arg), &p);
4044 audio_enc.bit_rate = atoi(arg) * 1000;
4045 } else if (!strcasecmp(cmd, "AudioChannels")) {
4046 get_arg(arg, sizeof(arg), &p);
4048 audio_enc.channels = atoi(arg);
4049 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4050 get_arg(arg, sizeof(arg), &p);
4052 audio_enc.sample_rate = atoi(arg);
4053 } else if (!strcasecmp(cmd, "AudioQuality")) {
4054 get_arg(arg, sizeof(arg), &p);
4056 // audio_enc.quality = atof(arg) * 1000;
4058 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4060 int minrate, maxrate;
4062 get_arg(arg, sizeof(arg), &p);
4064 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4065 video_enc.rc_min_rate = minrate * 1000;
4066 video_enc.rc_max_rate = maxrate * 1000;
4068 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4069 filename, line_num, arg);
4073 } else if (!strcasecmp(cmd, "Debug")) {
4075 get_arg(arg, sizeof(arg), &p);
4076 video_enc.debug = strtol(arg,0,0);
4078 } else if (!strcasecmp(cmd, "Strict")) {
4080 get_arg(arg, sizeof(arg), &p);
4081 video_enc.strict_std_compliance = atoi(arg);
4083 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4085 get_arg(arg, sizeof(arg), &p);
4086 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4088 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4090 get_arg(arg, sizeof(arg), &p);
4091 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4093 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4094 get_arg(arg, sizeof(arg), &p);
4096 video_enc.bit_rate = atoi(arg) * 1000;
4098 } else if (!strcasecmp(cmd, "VideoSize")) {
4099 get_arg(arg, sizeof(arg), &p);
4101 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4102 if ((video_enc.width % 16) != 0 ||
4103 (video_enc.height % 16) != 0) {
4104 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4105 filename, line_num);
4109 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4110 get_arg(arg, sizeof(arg), &p);
4112 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4113 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4115 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4116 get_arg(arg, sizeof(arg), &p);
4118 video_enc.gop_size = atoi(arg);
4119 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4121 video_enc.gop_size = 1;
4122 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4124 video_enc.mb_decision = FF_MB_DECISION_BITS;
4125 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4127 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4128 video_enc.flags |= CODEC_FLAG_4MV;
4130 } else if (!strcasecmp(cmd, "VideoTag")) {
4131 get_arg(arg, sizeof(arg), &p);
4132 if ((strlen(arg) == 4) && stream)
4133 video_enc.codec_tag = ff_get_fourcc(arg);
4134 } else if (!strcasecmp(cmd, "BitExact")) {
4136 video_enc.flags |= CODEC_FLAG_BITEXACT;
4137 } else if (!strcasecmp(cmd, "DctFastint")) {
4139 video_enc.dct_algo = FF_DCT_FASTINT;
4140 } else if (!strcasecmp(cmd, "IdctSimple")) {
4142 video_enc.idct_algo = FF_IDCT_SIMPLE;
4143 } else if (!strcasecmp(cmd, "Qscale")) {
4144 get_arg(arg, sizeof(arg), &p);
4146 video_enc.flags |= CODEC_FLAG_QSCALE;
4147 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4149 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4150 get_arg(arg, sizeof(arg), &p);
4152 video_enc.max_qdiff = atoi(arg);
4153 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4154 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4155 filename, line_num);
4159 } else if (!strcasecmp(cmd, "VideoQMax")) {
4160 get_arg(arg, sizeof(arg), &p);
4162 video_enc.qmax = atoi(arg);
4163 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4164 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4165 filename, line_num);
4169 } else if (!strcasecmp(cmd, "VideoQMin")) {
4170 get_arg(arg, sizeof(arg), &p);
4172 video_enc.qmin = atoi(arg);
4173 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4174 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4175 filename, line_num);
4179 } else if (!strcasecmp(cmd, "LumaElim")) {
4180 get_arg(arg, sizeof(arg), &p);
4182 video_enc.luma_elim_threshold = atoi(arg);
4183 } else if (!strcasecmp(cmd, "ChromaElim")) {
4184 get_arg(arg, sizeof(arg), &p);
4186 video_enc.chroma_elim_threshold = atoi(arg);
4187 } else if (!strcasecmp(cmd, "LumiMask")) {
4188 get_arg(arg, sizeof(arg), &p);
4190 video_enc.lumi_masking = atof(arg);
4191 } else if (!strcasecmp(cmd, "DarkMask")) {
4192 get_arg(arg, sizeof(arg), &p);
4194 video_enc.dark_masking = atof(arg);
4195 } else if (!strcasecmp(cmd, "NoVideo")) {
4196 video_id = CODEC_ID_NONE;
4197 } else if (!strcasecmp(cmd, "NoAudio")) {
4198 audio_id = CODEC_ID_NONE;
4199 } else if (!strcasecmp(cmd, "ACL")) {
4202 get_arg(arg, sizeof(arg), &p);
4203 if (strcasecmp(arg, "allow") == 0)
4204 acl.action = IP_ALLOW;
4205 else if (strcasecmp(arg, "deny") == 0)
4206 acl.action = IP_DENY;
4208 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4209 filename, line_num, arg);
4213 get_arg(arg, sizeof(arg), &p);
4215 if (resolve_host(&acl.first, arg) != 0) {
4216 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4217 filename, line_num, arg);
4220 acl.last = acl.first;
4222 get_arg(arg, sizeof(arg), &p);
4225 if (resolve_host(&acl.last, arg) != 0) {
4226 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4227 filename, line_num, arg);
4233 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4234 IPAddressACL **naclp = 0;
4240 naclp = &stream->acl;
4244 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4245 filename, line_num);
4251 naclp = &(*naclp)->next;
4256 } else if (!strcasecmp(cmd, "RTSPOption")) {
4257 get_arg(arg, sizeof(arg), &p);
4259 av_freep(&stream->rtsp_option);
4260 stream->rtsp_option = av_strdup(arg);
4262 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4263 get_arg(arg, sizeof(arg), &p);
4265 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4266 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4267 filename, line_num, arg);
4270 stream->is_multicast = 1;
4271 stream->loop = 1; /* default is looping */
4273 } else if (!strcasecmp(cmd, "MulticastPort")) {
4274 get_arg(arg, sizeof(arg), &p);
4276 stream->multicast_port = atoi(arg);
4277 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4278 get_arg(arg, sizeof(arg), &p);
4280 stream->multicast_ttl = atoi(arg);
4281 } else if (!strcasecmp(cmd, "NoLoop")) {
4284 } else if (!strcasecmp(cmd, "</Stream>")) {
4286 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4287 filename, line_num);
4290 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4291 if (audio_id != CODEC_ID_NONE) {
4292 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4293 audio_enc.codec_id = audio_id;
4294 add_codec(stream, &audio_enc);
4296 if (video_id != CODEC_ID_NONE) {
4297 video_enc.codec_type = CODEC_TYPE_VIDEO;
4298 video_enc.codec_id = video_id;
4299 add_codec(stream, &video_enc);
4303 } else if (!strcasecmp(cmd, "<Redirect")) {
4304 /*********************************************/
4306 if (stream || feed || redirect) {
4307 fprintf(stderr, "%s:%d: Already in a tag\n",
4308 filename, line_num);
4311 redirect = av_mallocz(sizeof(FFStream));
4312 *last_stream = redirect;
4313 last_stream = &redirect->next;
4315 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4316 q = strrchr(redirect->filename, '>');
4319 redirect->stream_type = STREAM_TYPE_REDIRECT;
4321 } else if (!strcasecmp(cmd, "URL")) {
4323 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4324 } else if (!strcasecmp(cmd, "</Redirect>")) {
4326 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4327 filename, line_num);
4330 if (!redirect->feed_filename[0]) {
4331 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4332 filename, line_num);
4336 } else if (!strcasecmp(cmd, "LoadModule")) {
4337 get_arg(arg, sizeof(arg), &p);
4341 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4342 filename, line_num, arg);
4346 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4347 filename, line_num, cmd);
4359 static void show_banner(void)
4361 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4364 static void show_help(void)
4367 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4368 "Hyper fast multi format Audio/Video streaming server\n"
4370 "-L : print the LICENSE\n"
4372 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4376 static void show_license(void)
4380 "FFmpeg is free software; you can redistribute it and/or\n"
4381 "modify it under the terms of the GNU Lesser General Public\n"
4382 "License as published by the Free Software Foundation; either\n"
4383 "version 2.1 of the License, or (at your option) any later version.\n"
4385 "FFmpeg is distributed in the hope that it will be useful,\n"
4386 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4387 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4388 "Lesser General Public License for more details.\n"
4390 "You should have received a copy of the GNU Lesser General Public\n"
4391 "License along with FFmpeg; if not, write to the Free Software\n"
4392 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
4396 static void handle_child_exit(int sig)
4401 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4404 for (feed = first_feed; feed; feed = feed->next) {
4405 if (feed->pid == pid) {
4406 int uptime = time(0) - feed->pid_start;
4409 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4412 /* Turn off any more restarts */
4413 feed->child_argv = 0;
4418 need_to_start_children = 1;
4421 int main(int argc, char **argv)
4423 const char *config_filename;
4425 struct sigaction sigact;
4429 config_filename = "/etc/ffserver.conf";
4431 my_program_name = argv[0];
4432 my_program_dir = getcwd(0, 0);
4433 ffserver_daemon = 1;
4436 c = getopt(argc, argv, "ndLh?f:");
4452 ffserver_daemon = 0;
4455 config_filename = optarg;
4462 putenv("http_proxy"); /* Kill the http_proxy */
4464 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4466 /* address on which the server will handle HTTP connections */
4467 my_http_addr.sin_family = AF_INET;
4468 my_http_addr.sin_port = htons (8080);
4469 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4471 /* address on which the server will handle RTSP connections */
4472 my_rtsp_addr.sin_family = AF_INET;
4473 my_rtsp_addr.sin_port = htons (5454);
4474 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4476 nb_max_connections = 5;
4477 max_bandwidth = 1000;
4478 first_stream = NULL;
4479 logfilename[0] = '\0';
4481 memset(&sigact, 0, sizeof(sigact));
4482 sigact.sa_handler = handle_child_exit;
4483 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4484 sigaction(SIGCHLD, &sigact, 0);
4486 if (parse_ffconfig(config_filename) < 0) {
4487 fprintf(stderr, "Incorrect config file - exiting.\n");
4491 build_file_streams();
4493 build_feed_streams();
4495 compute_bandwidth();
4497 /* put the process in background and detach it from its TTY */
4498 if (ffserver_daemon) {
4505 } else if (pid > 0) {
4513 open("/dev/null", O_RDWR);
4514 if (strcmp(logfilename, "-") != 0) {
4524 signal(SIGPIPE, SIG_IGN);
4526 /* open log file if needed */
4527 if (logfilename[0] != '\0') {
4528 if (!strcmp(logfilename, "-"))
4531 logfile = fopen(logfilename, "w");
4534 if (http_server() < 0) {
4535 fprintf(stderr, "Could not start server\n");