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
56 /* maximum number of simultaneous HTTP connections */
57 #define HTTP_MAX_CONNECTIONS 2000
60 HTTPSTATE_WAIT_REQUEST,
61 HTTPSTATE_SEND_HEADER,
62 HTTPSTATE_SEND_DATA_HEADER,
63 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
64 HTTPSTATE_SEND_DATA_TRAILER,
65 HTTPSTATE_RECEIVE_DATA,
66 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
69 RTSPSTATE_WAIT_REQUEST,
71 RTSPSTATE_SEND_PACKET,
74 const char *http_state[] = {
90 #define IOBUFFER_INIT_SIZE 8192
92 /* timeouts are in ms */
93 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
94 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
96 #define SYNC_TIMEOUT (10 * 1000)
99 int64_t count1, count2;
100 int64_t time1, time2;
103 /* context associated with one connection */
104 typedef struct HTTPContext {
105 enum HTTPState state;
106 int fd; /* socket file descriptor */
107 struct sockaddr_in from_addr; /* origin */
108 struct pollfd *poll_entry; /* used when polling */
110 uint8_t *buffer_ptr, *buffer_end;
113 struct HTTPContext *next;
114 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
118 /* input format handling */
119 AVFormatContext *fmt_in;
120 int64_t start_time; /* In milliseconds - this wraps fairly often */
121 int64_t first_pts; /* initial pts value */
122 int64_t cur_pts; /* current pts value from the stream in us */
123 int64_t cur_frame_duration; /* duration of the current frame in us */
124 int cur_frame_bytes; /* output frame size, needed to compute
125 the time at which we send each
127 int pts_stream_index; /* stream we choose as clock reference */
128 int64_t cur_clock; /* current clock reference value in us */
129 /* output format handling */
130 struct FFStream *stream;
131 /* -1 is invalid stream */
132 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
133 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
135 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
136 int last_packet_sent; /* true if last data packet was sent */
138 DataRateData datarate;
145 int is_packetized; /* if true, the stream is packetized */
146 int packet_stream_index; /* current stream for output in state machine */
148 /* RTSP state specific */
149 uint8_t *pb_buffer; /* XXX: use that in all the code */
151 int seq; /* RTSP sequence number */
153 /* RTP state specific */
154 enum RTSPProtocol rtp_protocol;
155 char session_id[32]; /* session id */
156 AVFormatContext *rtp_ctx[MAX_STREAMS];
158 /* RTP/UDP specific */
159 URLContext *rtp_handles[MAX_STREAMS];
161 /* RTP/TCP specific */
162 struct HTTPContext *rtsp_c;
163 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
166 static AVFrame dummy_frame;
168 /* each generated stream is described here */
172 STREAM_TYPE_REDIRECT,
175 enum IPAddressAction {
180 typedef struct IPAddressACL {
181 struct IPAddressACL *next;
182 enum IPAddressAction action;
183 /* These are in host order */
184 struct in_addr first;
188 /* description of each stream of the ffserver.conf file */
189 typedef struct FFStream {
190 enum StreamType stream_type;
191 char filename[1024]; /* stream filename */
192 struct FFStream *feed; /* feed we are using (can be null if
194 AVFormatParameters *ap_in; /* input parameters */
195 AVInputFormat *ifmt; /* if non NULL, force input format */
199 int prebuffer; /* Number of millseconds early to start */
200 int64_t max_time; /* Number of milliseconds to run */
202 AVStream *streams[MAX_STREAMS];
203 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
204 char feed_filename[1024]; /* file name of the feed storage, or
205 input file name for a stream */
210 pid_t pid; /* Of ffmpeg process */
211 time_t pid_start; /* Of ffmpeg process */
213 struct FFStream *next;
214 int bandwidth; /* bandwidth, in kbits/s */
217 /* multicast specific */
219 struct in_addr multicast_ip;
220 int multicast_port; /* first port used for multicast */
222 int loop; /* if true, send the stream in loops (only meaningful if file) */
225 int feed_opened; /* true if someone is writing to the feed */
226 int is_feed; /* true if it is a feed */
227 int readonly; /* True if writing is prohibited to the file */
229 int64_t bytes_served;
230 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
231 int64_t feed_write_index; /* current write position in feed (it wraps round) */
232 int64_t feed_size; /* current size of feed */
233 struct FFStream *next_feed;
236 typedef struct FeedData {
237 long long data_count;
238 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
241 static struct sockaddr_in my_http_addr;
242 static struct sockaddr_in my_rtsp_addr;
244 static char logfilename[1024];
245 static HTTPContext *first_http_ctx;
246 static FFStream *first_feed; /* contains only feeds */
247 static FFStream *first_stream; /* contains all streams, including feeds */
249 static void new_connection(int server_fd, int is_rtsp);
250 static void close_connection(HTTPContext *c);
253 static int handle_connection(HTTPContext *c);
254 static int http_parse_request(HTTPContext *c);
255 static int http_send_data(HTTPContext *c);
256 static void compute_stats(HTTPContext *c);
257 static int open_input_stream(HTTPContext *c, const char *info);
258 static int http_start_receive_data(HTTPContext *c);
259 static int http_receive_data(HTTPContext *c);
262 static int rtsp_parse_request(HTTPContext *c);
263 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
264 static void rtsp_cmd_options(HTTPContext *c, const char *url);
265 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
266 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
267 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
268 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
271 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
272 struct in_addr my_ip);
275 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
276 FFStream *stream, const char *session_id,
277 enum RTSPProtocol rtp_protocol);
278 static int rtp_new_av_stream(HTTPContext *c,
279 int stream_index, struct sockaddr_in *dest_addr,
280 HTTPContext *rtsp_c);
282 static const char *my_program_name;
283 static const char *my_program_dir;
285 static int ffserver_debug;
286 static int ffserver_daemon;
287 static int no_launch;
288 static int need_to_start_children;
290 static int nb_max_connections;
291 static int nb_connections;
293 static int max_bandwidth;
294 static int current_bandwidth;
296 static int64_t cur_time; // Making this global saves on passing it around everywhere
298 static AVRandomState random_state;
300 static FILE *logfile = NULL;
302 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
308 vfprintf(logfile, fmt, ap);
314 static char *ctime1(char *buf2)
322 p = buf2 + strlen(p) - 1;
328 static void log_connection(HTTPContext *c)
335 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
336 inet_ntoa(c->from_addr.sin_addr),
337 ctime1(buf2), c->method, c->url,
338 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
341 static void update_datarate(DataRateData *drd, int64_t count)
343 if (!drd->time1 && !drd->count1) {
344 drd->time1 = drd->time2 = cur_time;
345 drd->count1 = drd->count2 = count;
346 } else if (cur_time - drd->time2 > 5000) {
347 drd->time1 = drd->time2;
348 drd->count1 = drd->count2;
349 drd->time2 = cur_time;
354 /* In bytes per second */
355 static int compute_datarate(DataRateData *drd, int64_t count)
357 if (cur_time == drd->time1)
360 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
364 static void start_children(FFStream *feed)
369 for (; feed; feed = feed->next) {
370 if (feed->child_argv && !feed->pid) {
371 feed->pid_start = time(0);
376 fprintf(stderr, "Unable to create children\n");
385 for (i = 3; i < 256; i++)
388 if (!ffserver_debug) {
389 i = open("/dev/null", O_RDWR);
398 av_strlcpy(pathname, my_program_name, sizeof(pathname));
400 slash = strrchr(pathname, '/');
405 strcpy(slash, "ffmpeg");
407 /* This is needed to make relative pathnames work */
408 chdir(my_program_dir);
410 signal(SIGPIPE, SIG_DFL);
412 execvp(pathname, feed->child_argv);
420 /* open a listening socket */
421 static int socket_open_listen(struct sockaddr_in *my_addr)
425 server_fd = socket(AF_INET,SOCK_STREAM,0);
432 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
434 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
436 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
438 closesocket(server_fd);
442 if (listen (server_fd, 5) < 0) {
444 closesocket(server_fd);
447 ff_socket_nonblock(server_fd, 1);
452 /* start all multicast streams */
453 static void start_multicast(void)
458 struct sockaddr_in dest_addr;
459 int default_port, stream_index;
462 for(stream = first_stream; stream != NULL; stream = stream->next) {
463 if (stream->is_multicast) {
464 /* open the RTP connection */
465 snprintf(session_id, sizeof(session_id), "%08x%08x",
466 av_random(&random_state), av_random(&random_state));
468 /* choose a port if none given */
469 if (stream->multicast_port == 0) {
470 stream->multicast_port = default_port;
474 dest_addr.sin_family = AF_INET;
475 dest_addr.sin_addr = stream->multicast_ip;
476 dest_addr.sin_port = htons(stream->multicast_port);
478 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
479 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
483 if (open_input_stream(rtp_c, "") < 0) {
484 fprintf(stderr, "Could not open input stream for stream '%s'\n",
489 /* open each RTP stream */
490 for(stream_index = 0; stream_index < stream->nb_streams;
492 dest_addr.sin_port = htons(stream->multicast_port +
494 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
495 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
496 stream->filename, stream_index);
501 /* change state to send data */
502 rtp_c->state = HTTPSTATE_SEND_DATA;
507 /* main loop of the http server */
508 static int http_server(void)
510 int server_fd, ret, rtsp_server_fd, delay, delay1;
511 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
512 HTTPContext *c, *c_next;
514 server_fd = socket_open_listen(&my_http_addr);
518 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
519 if (rtsp_server_fd < 0)
522 http_log("ffserver started.\n");
524 start_children(first_feed);
526 first_http_ctx = NULL;
532 poll_entry = poll_table;
533 poll_entry->fd = server_fd;
534 poll_entry->events = POLLIN;
537 poll_entry->fd = rtsp_server_fd;
538 poll_entry->events = POLLIN;
541 /* wait for events on each HTTP handle */
548 case HTTPSTATE_SEND_HEADER:
549 case RTSPSTATE_SEND_REPLY:
550 case RTSPSTATE_SEND_PACKET:
551 c->poll_entry = poll_entry;
553 poll_entry->events = POLLOUT;
556 case HTTPSTATE_SEND_DATA_HEADER:
557 case HTTPSTATE_SEND_DATA:
558 case HTTPSTATE_SEND_DATA_TRAILER:
559 if (!c->is_packetized) {
560 /* for TCP, we output as much as we can (may need to put a limit) */
561 c->poll_entry = poll_entry;
563 poll_entry->events = POLLOUT;
566 /* when ffserver is doing the timing, we work by
567 looking at which packet need to be sent every
569 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
574 case HTTPSTATE_WAIT_REQUEST:
575 case HTTPSTATE_RECEIVE_DATA:
576 case HTTPSTATE_WAIT_FEED:
577 case RTSPSTATE_WAIT_REQUEST:
578 /* need to catch errors */
579 c->poll_entry = poll_entry;
581 poll_entry->events = POLLIN;/* Maybe this will work */
585 c->poll_entry = NULL;
591 /* wait for an event on one connection. We poll at least every
592 second to handle timeouts */
594 ret = poll(poll_table, poll_entry - poll_table, delay);
595 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
596 ff_neterrno() != FF_NETERROR(EINTR))
600 cur_time = av_gettime() / 1000;
602 if (need_to_start_children) {
603 need_to_start_children = 0;
604 start_children(first_feed);
607 /* now handle the events */
608 for(c = first_http_ctx; c != NULL; c = c_next) {
610 if (handle_connection(c) < 0) {
611 /* close and free the connection */
617 poll_entry = poll_table;
618 /* new HTTP connection request ? */
619 if (poll_entry->revents & POLLIN)
620 new_connection(server_fd, 0);
622 /* new RTSP connection request ? */
623 if (poll_entry->revents & POLLIN)
624 new_connection(rtsp_server_fd, 1);
628 /* start waiting for a new HTTP/RTSP request */
629 static void start_wait_request(HTTPContext *c, int is_rtsp)
631 c->buffer_ptr = c->buffer;
632 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
635 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
636 c->state = RTSPSTATE_WAIT_REQUEST;
638 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
639 c->state = HTTPSTATE_WAIT_REQUEST;
643 static void new_connection(int server_fd, int is_rtsp)
645 struct sockaddr_in from_addr;
647 HTTPContext *c = NULL;
649 len = sizeof(from_addr);
650 fd = accept(server_fd, (struct sockaddr *)&from_addr,
654 ff_socket_nonblock(fd, 1);
656 /* XXX: should output a warning page when coming
657 close to the connection limit */
658 if (nb_connections >= nb_max_connections)
661 /* add a new connection */
662 c = av_mallocz(sizeof(HTTPContext));
667 c->poll_entry = NULL;
668 c->from_addr = from_addr;
669 c->buffer_size = IOBUFFER_INIT_SIZE;
670 c->buffer = av_malloc(c->buffer_size);
674 c->next = first_http_ctx;
678 start_wait_request(c, is_rtsp);
690 static void close_connection(HTTPContext *c)
692 HTTPContext **cp, *c1;
694 AVFormatContext *ctx;
698 /* remove connection from list */
699 cp = &first_http_ctx;
700 while ((*cp) != NULL) {
708 /* remove references, if any (XXX: do it faster) */
709 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
714 /* remove connection associated resources */
718 /* close each frame parser */
719 for(i=0;i<c->fmt_in->nb_streams;i++) {
720 st = c->fmt_in->streams[i];
721 if (st->codec->codec)
722 avcodec_close(st->codec);
724 av_close_input_file(c->fmt_in);
727 /* free RTP output streams if any */
730 nb_streams = c->stream->nb_streams;
732 for(i=0;i<nb_streams;i++) {
735 av_write_trailer(ctx);
738 h = c->rtp_handles[i];
745 if (!c->last_packet_sent) {
748 if (url_open_dyn_buf(&ctx->pb) >= 0) {
749 av_write_trailer(ctx);
750 url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
755 for(i=0; i<ctx->nb_streams; i++)
756 av_free(ctx->streams[i]);
758 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
759 current_bandwidth -= c->stream->bandwidth;
761 /* signal that there is no feed if we are the feeder socket */
762 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
763 c->stream->feed_opened = 0;
767 av_freep(&c->pb_buffer);
768 av_freep(&c->packet_buffer);
774 static int handle_connection(HTTPContext *c)
779 case HTTPSTATE_WAIT_REQUEST:
780 case RTSPSTATE_WAIT_REQUEST:
782 if ((c->timeout - cur_time) < 0)
784 if (c->poll_entry->revents & (POLLERR | POLLHUP))
787 /* no need to read if no events */
788 if (!(c->poll_entry->revents & POLLIN))
792 len = recv(c->fd, c->buffer_ptr, 1, 0);
794 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
795 ff_neterrno() != FF_NETERROR(EINTR))
797 } else if (len == 0) {
800 /* search for end of request. */
802 c->buffer_ptr += len;
804 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
805 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
806 /* request found : parse it and reply */
807 if (c->state == HTTPSTATE_WAIT_REQUEST) {
808 ret = http_parse_request(c);
810 ret = rtsp_parse_request(c);
814 } else if (ptr >= c->buffer_end) {
815 /* request too long: cannot do anything */
817 } else goto read_loop;
821 case HTTPSTATE_SEND_HEADER:
822 if (c->poll_entry->revents & (POLLERR | POLLHUP))
825 /* no need to write if no events */
826 if (!(c->poll_entry->revents & POLLOUT))
828 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
830 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
831 ff_neterrno() != FF_NETERROR(EINTR)) {
832 /* error : close connection */
833 av_freep(&c->pb_buffer);
837 c->buffer_ptr += len;
839 c->stream->bytes_served += len;
840 c->data_count += len;
841 if (c->buffer_ptr >= c->buffer_end) {
842 av_freep(&c->pb_buffer);
846 /* all the buffer was sent : synchronize to the incoming stream */
847 c->state = HTTPSTATE_SEND_DATA_HEADER;
848 c->buffer_ptr = c->buffer_end = c->buffer;
853 case HTTPSTATE_SEND_DATA:
854 case HTTPSTATE_SEND_DATA_HEADER:
855 case HTTPSTATE_SEND_DATA_TRAILER:
856 /* for packetized output, we consider we can always write (the
857 input streams sets the speed). It may be better to verify
858 that we do not rely too much on the kernel queues */
859 if (!c->is_packetized) {
860 if (c->poll_entry->revents & (POLLERR | POLLHUP))
863 /* no need to read if no events */
864 if (!(c->poll_entry->revents & POLLOUT))
867 if (http_send_data(c) < 0)
869 /* close connection if trailer sent */
870 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
873 case HTTPSTATE_RECEIVE_DATA:
874 /* no need to read if no events */
875 if (c->poll_entry->revents & (POLLERR | POLLHUP))
877 if (!(c->poll_entry->revents & POLLIN))
879 if (http_receive_data(c) < 0)
882 case HTTPSTATE_WAIT_FEED:
883 /* no need to read if no events */
884 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
887 /* nothing to do, we'll be waken up by incoming feed packets */
890 case RTSPSTATE_SEND_REPLY:
891 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
892 av_freep(&c->pb_buffer);
895 /* no need to write if no events */
896 if (!(c->poll_entry->revents & POLLOUT))
898 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
900 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
901 ff_neterrno() != FF_NETERROR(EINTR)) {
902 /* error : close connection */
903 av_freep(&c->pb_buffer);
907 c->buffer_ptr += len;
908 c->data_count += len;
909 if (c->buffer_ptr >= c->buffer_end) {
910 /* all the buffer was sent : wait for a new request */
911 av_freep(&c->pb_buffer);
912 start_wait_request(c, 1);
916 case RTSPSTATE_SEND_PACKET:
917 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
918 av_freep(&c->packet_buffer);
921 /* no need to write if no events */
922 if (!(c->poll_entry->revents & POLLOUT))
924 len = send(c->fd, c->packet_buffer_ptr,
925 c->packet_buffer_end - c->packet_buffer_ptr, 0);
927 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
928 ff_neterrno() != FF_NETERROR(EINTR)) {
929 /* error : close connection */
930 av_freep(&c->packet_buffer);
934 c->packet_buffer_ptr += len;
935 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
936 /* all the buffer was sent : wait for a new request */
937 av_freep(&c->packet_buffer);
938 c->state = RTSPSTATE_WAIT_REQUEST;
942 case HTTPSTATE_READY:
951 static int extract_rates(char *rates, int ratelen, const char *request)
955 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
956 if (strncasecmp(p, "Pragma:", 7) == 0) {
957 const char *q = p + 7;
959 while (*q && *q != '\n' && isspace(*q))
962 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
968 memset(rates, 0xff, ratelen);
971 while (*q && *q != '\n' && *q != ':')
974 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
978 if (stream_no < ratelen && stream_no >= 0)
979 rates[stream_no] = rate_no;
981 while (*q && *q != '\n' && !isspace(*q))
998 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1001 int best_bitrate = 100000000;
1004 for (i = 0; i < feed->nb_streams; i++) {
1005 AVCodecContext *feed_codec = feed->streams[i]->codec;
1007 if (feed_codec->codec_id != codec->codec_id ||
1008 feed_codec->sample_rate != codec->sample_rate ||
1009 feed_codec->width != codec->width ||
1010 feed_codec->height != codec->height)
1013 /* Potential stream */
1015 /* We want the fastest stream less than bit_rate, or the slowest
1016 * faster than bit_rate
1019 if (feed_codec->bit_rate <= bit_rate) {
1020 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1021 best_bitrate = feed_codec->bit_rate;
1025 if (feed_codec->bit_rate < best_bitrate) {
1026 best_bitrate = feed_codec->bit_rate;
1035 static int modify_current_stream(HTTPContext *c, char *rates)
1038 FFStream *req = c->stream;
1039 int action_required = 0;
1041 /* Not much we can do for a feed */
1045 for (i = 0; i < req->nb_streams; i++) {
1046 AVCodecContext *codec = req->streams[i]->codec;
1050 c->switch_feed_streams[i] = req->feed_streams[i];
1053 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1056 /* Wants off or slow */
1057 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1059 /* This doesn't work well when it turns off the only stream! */
1060 c->switch_feed_streams[i] = -2;
1061 c->feed_streams[i] = -2;
1066 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1067 action_required = 1;
1070 return action_required;
1074 static void do_switch_stream(HTTPContext *c, int i)
1076 if (c->switch_feed_streams[i] >= 0) {
1078 c->feed_streams[i] = c->switch_feed_streams[i];
1081 /* Now update the stream */
1083 c->switch_feed_streams[i] = -1;
1086 /* XXX: factorize in utils.c ? */
1087 /* XXX: take care with different space meaning */
1088 static void skip_spaces(const char **pp)
1092 while (*p == ' ' || *p == '\t')
1097 static void get_word(char *buf, int buf_size, const char **pp)
1105 while (!isspace(*p) && *p != '\0') {
1106 if ((q - buf) < buf_size - 1)
1115 static int validate_acl(FFStream *stream, HTTPContext *c)
1117 enum IPAddressAction last_action = IP_DENY;
1119 struct in_addr *src = &c->from_addr.sin_addr;
1120 unsigned long src_addr = src->s_addr;
1122 for (acl = stream->acl; acl; acl = acl->next) {
1123 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1124 return (acl->action == IP_ALLOW) ? 1 : 0;
1125 last_action = acl->action;
1128 /* Nothing matched, so return not the last action */
1129 return (last_action == IP_DENY) ? 1 : 0;
1132 /* compute the real filename of a file by matching it without its
1133 extensions to all the stream filenames */
1134 static void compute_real_filename(char *filename, int max_size)
1141 /* compute filename by matching without the file extensions */
1142 av_strlcpy(file1, filename, sizeof(file1));
1143 p = strrchr(file1, '.');
1146 for(stream = first_stream; stream != NULL; stream = stream->next) {
1147 av_strlcpy(file2, stream->filename, sizeof(file2));
1148 p = strrchr(file2, '.');
1151 if (!strcmp(file1, file2)) {
1152 av_strlcpy(filename, stream->filename, max_size);
1167 /* parse http request and prepare header */
1168 static int http_parse_request(HTTPContext *c)
1171 enum RedirType redir_type;
1173 char info[1024], filename[1024];
1177 const char *mime_type;
1181 char *useragent = 0;
1184 get_word(cmd, sizeof(cmd), (const char **)&p);
1185 av_strlcpy(c->method, cmd, sizeof(c->method));
1187 if (!strcmp(cmd, "GET"))
1189 else if (!strcmp(cmd, "POST"))
1194 get_word(url, sizeof(url), (const char **)&p);
1195 av_strlcpy(c->url, url, sizeof(c->url));
1197 get_word(protocol, sizeof(protocol), (const char **)&p);
1198 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1201 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1204 http_log("New connection: %s %s\n", cmd, url);
1206 /* find the filename and the optional info string in the request */
1207 p = strchr(url, '?');
1209 av_strlcpy(info, p, sizeof(info));
1214 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1216 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1217 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1219 if (*useragent && *useragent != '\n' && isspace(*useragent))
1223 p = strchr(p, '\n');
1230 redir_type = REDIR_NONE;
1231 if (match_ext(filename, "asx")) {
1232 redir_type = REDIR_ASX;
1233 filename[strlen(filename)-1] = 'f';
1234 } else if (match_ext(filename, "asf") &&
1235 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1236 /* if this isn't WMP or lookalike, return the redirector file */
1237 redir_type = REDIR_ASF;
1238 } else if (match_ext(filename, "rpm,ram")) {
1239 redir_type = REDIR_RAM;
1240 strcpy(filename + strlen(filename)-2, "m");
1241 } else if (match_ext(filename, "rtsp")) {
1242 redir_type = REDIR_RTSP;
1243 compute_real_filename(filename, sizeof(filename) - 1);
1244 } else if (match_ext(filename, "sdp")) {
1245 redir_type = REDIR_SDP;
1246 compute_real_filename(filename, sizeof(filename) - 1);
1249 // "redirect" / request to index.html
1250 if (!strlen(filename))
1251 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1253 stream = first_stream;
1254 while (stream != NULL) {
1255 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1257 stream = stream->next;
1259 if (stream == NULL) {
1260 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1265 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1266 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1268 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1269 c->http_error = 301;
1271 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1272 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1273 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1274 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1275 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1276 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1277 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1279 /* prepare output buffer */
1280 c->buffer_ptr = c->buffer;
1282 c->state = HTTPSTATE_SEND_HEADER;
1286 /* If this is WMP, get the rate information */
1287 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1288 if (modify_current_stream(c, ratebuf)) {
1289 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1290 if (c->switch_feed_streams[i] >= 0)
1291 do_switch_stream(c, i);
1296 /* If already streaming this feed, do not let start another feeder. */
1297 if (stream->feed_opened) {
1298 snprintf(msg, sizeof(msg), "This feed is already being received.");
1302 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1303 current_bandwidth += stream->bandwidth;
1305 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1306 c->http_error = 200;
1308 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1309 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1310 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1311 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1312 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");
1313 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",
1314 current_bandwidth, max_bandwidth);
1315 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1317 /* prepare output buffer */
1318 c->buffer_ptr = c->buffer;
1320 c->state = HTTPSTATE_SEND_HEADER;
1324 if (redir_type != REDIR_NONE) {
1327 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1328 if (strncasecmp(p, "Host:", 5) == 0) {
1332 p = strchr(p, '\n');
1343 while (isspace(*hostinfo))
1346 eoh = strchr(hostinfo, '\n');
1348 if (eoh[-1] == '\r')
1351 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1352 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1353 hostbuf[eoh - hostinfo] = 0;
1355 c->http_error = 200;
1357 switch(redir_type) {
1359 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1362 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1363 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1364 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1365 hostbuf, filename, info);
1366 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1369 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1370 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1371 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1372 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1373 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1374 hostbuf, filename, info);
1377 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1378 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1379 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1380 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1381 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1382 hostbuf, filename, info);
1386 char hostname[256], *p;
1387 /* extract only hostname */
1388 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1389 p = strrchr(hostname, ':');
1392 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1393 /* XXX: incorrect mime type ? */
1394 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1395 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1396 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1397 hostname, ntohs(my_rtsp_addr.sin_port),
1404 int sdp_data_size, len;
1405 struct sockaddr_in my_addr;
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1408 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1411 len = sizeof(my_addr);
1412 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1414 /* XXX: should use a dynamic buffer */
1415 sdp_data_size = prepare_sdp_description(stream,
1418 if (sdp_data_size > 0) {
1419 memcpy(q, sdp_data, sdp_data_size);
1431 /* prepare output buffer */
1432 c->buffer_ptr = c->buffer;
1434 c->state = HTTPSTATE_SEND_HEADER;
1440 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1444 stream->conns_served++;
1446 /* XXX: add there authenticate and IP match */
1449 /* if post, it means a feed is being sent */
1450 if (!stream->is_feed) {
1451 /* However it might be a status report from WMP! Lets log the data
1452 * as it might come in handy one day
1457 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1458 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1462 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1463 client_id = strtol(p + 18, 0, 10);
1464 p = strchr(p, '\n');
1472 char *eol = strchr(logline, '\n');
1477 if (eol[-1] == '\r')
1479 http_log("%.*s\n", (int) (eol - logline), logline);
1480 c->suppress_log = 1;
1485 http_log("\nGot request:\n%s\n", c->buffer);
1488 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1491 /* Now we have to find the client_id */
1492 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1493 if (wmpc->wmp_client_id == client_id)
1497 if (wmpc && modify_current_stream(wmpc, ratebuf))
1498 wmpc->switch_pending = 1;
1501 snprintf(msg, sizeof(msg), "POST command not handled");
1505 if (http_start_receive_data(c) < 0) {
1506 snprintf(msg, sizeof(msg), "could not open feed");
1510 c->state = HTTPSTATE_RECEIVE_DATA;
1515 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1516 http_log("\nGot request:\n%s\n", c->buffer);
1519 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1522 /* open input stream */
1523 if (open_input_stream(c, info) < 0) {
1524 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1528 /* prepare http header */
1530 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1531 mime_type = c->stream->fmt->mime_type;
1533 mime_type = "application/x-octet-stream";
1534 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1536 /* for asf, we need extra headers */
1537 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1538 /* Need to allocate a client id */
1540 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1542 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);
1544 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1545 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1547 /* prepare output buffer */
1549 c->buffer_ptr = c->buffer;
1551 c->state = HTTPSTATE_SEND_HEADER;
1554 c->http_error = 404;
1556 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1557 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1558 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1559 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1560 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1561 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1562 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1564 /* prepare output buffer */
1565 c->buffer_ptr = c->buffer;
1567 c->state = HTTPSTATE_SEND_HEADER;
1571 c->http_error = 200; /* horrible : we use this value to avoid
1572 going to the send data state */
1573 c->state = HTTPSTATE_SEND_HEADER;
1577 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1579 static const char *suffix = " kMGTP";
1582 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1584 url_fprintf(pb, "%"PRId64"%c", count, *s);
1587 static void compute_stats(HTTPContext *c)
1594 ByteIOContext pb1, *pb = &pb1;
1596 if (url_open_dyn_buf(pb) < 0) {
1597 /* XXX: return an error ? */
1598 c->buffer_ptr = c->buffer;
1599 c->buffer_end = c->buffer;
1603 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1604 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1605 url_fprintf(pb, "Pragma: no-cache\r\n");
1606 url_fprintf(pb, "\r\n");
1608 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1609 if (c->stream->feed_filename)
1610 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1611 url_fprintf(pb, "</HEAD>\n<BODY>");
1612 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1614 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1615 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1616 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");
1617 stream = first_stream;
1618 while (stream != NULL) {
1619 char sfilename[1024];
1622 if (stream->feed != stream) {
1623 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1624 eosf = sfilename + strlen(sfilename);
1625 if (eosf - sfilename >= 4) {
1626 if (strcmp(eosf - 4, ".asf") == 0)
1627 strcpy(eosf - 4, ".asx");
1628 else if (strcmp(eosf - 3, ".rm") == 0)
1629 strcpy(eosf - 3, ".ram");
1630 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1631 /* generate a sample RTSP director if
1632 unicast. Generate an SDP redirector if
1634 eosf = strrchr(sfilename, '.');
1636 eosf = sfilename + strlen(sfilename);
1637 if (stream->is_multicast)
1638 strcpy(eosf, ".sdp");
1640 strcpy(eosf, ".rtsp");
1644 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1645 sfilename, stream->filename);
1646 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1647 stream->conns_served);
1648 fmt_bytecount(pb, stream->bytes_served);
1649 switch(stream->stream_type) {
1650 case STREAM_TYPE_LIVE:
1652 int audio_bit_rate = 0;
1653 int video_bit_rate = 0;
1654 const char *audio_codec_name = "";
1655 const char *video_codec_name = "";
1656 const char *audio_codec_name_extra = "";
1657 const char *video_codec_name_extra = "";
1659 for(i=0;i<stream->nb_streams;i++) {
1660 AVStream *st = stream->streams[i];
1661 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1662 switch(st->codec->codec_type) {
1663 case CODEC_TYPE_AUDIO:
1664 audio_bit_rate += st->codec->bit_rate;
1666 if (*audio_codec_name)
1667 audio_codec_name_extra = "...";
1668 audio_codec_name = codec->name;
1671 case CODEC_TYPE_VIDEO:
1672 video_bit_rate += st->codec->bit_rate;
1674 if (*video_codec_name)
1675 video_codec_name_extra = "...";
1676 video_codec_name = codec->name;
1679 case CODEC_TYPE_DATA:
1680 video_bit_rate += st->codec->bit_rate;
1686 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",
1689 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1690 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1692 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1694 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1695 url_fprintf(pb, "\n");
1699 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1703 stream = stream->next;
1705 url_fprintf(pb, "</TABLE>\n");
1707 stream = first_stream;
1708 while (stream != NULL) {
1709 if (stream->feed == stream) {
1710 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1712 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1714 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1719 /* This is somewhat linux specific I guess */
1720 snprintf(ps_cmd, sizeof(ps_cmd),
1721 "ps -o \"%%cpu,cputime\" --no-headers %d",
1724 pid_stat = popen(ps_cmd, "r");
1729 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1731 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1739 url_fprintf(pb, "<p>");
1741 url_fprintf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
1743 for (i = 0; i < stream->nb_streams; i++) {
1744 AVStream *st = stream->streams[i];
1745 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1746 const char *type = "unknown";
1747 char parameters[64];
1751 switch(st->codec->codec_type) {
1752 case CODEC_TYPE_AUDIO:
1754 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1756 case CODEC_TYPE_VIDEO:
1758 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1759 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1764 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1765 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1767 url_fprintf(pb, "</table>\n");
1770 stream = stream->next;
1776 AVCodecContext *enc;
1780 stream = first_feed;
1781 while (stream != NULL) {
1782 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1783 url_fprintf(pb, "<TABLE>\n");
1784 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1785 for(i=0;i<stream->nb_streams;i++) {
1786 AVStream *st = stream->streams[i];
1787 FeedData *fdata = st->priv_data;
1790 avcodec_string(buf, sizeof(buf), enc);
1791 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1792 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1793 avg /= enc->frame_size;
1794 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1795 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1797 url_fprintf(pb, "</TABLE>\n");
1798 stream = stream->next_feed;
1803 /* connection status */
1804 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1806 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1807 nb_connections, nb_max_connections);
1809 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1810 current_bandwidth, max_bandwidth);
1812 url_fprintf(pb, "<TABLE>\n");
1813 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");
1814 c1 = first_http_ctx;
1816 while (c1 != NULL) {
1822 for (j = 0; j < c1->stream->nb_streams; j++) {
1823 if (!c1->stream->feed)
1824 bitrate += c1->stream->streams[j]->codec->bit_rate;
1825 else if (c1->feed_streams[j] >= 0)
1826 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1831 p = inet_ntoa(c1->from_addr.sin_addr);
1832 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1834 c1->stream ? c1->stream->filename : "",
1835 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1838 http_state[c1->state]);
1839 fmt_bytecount(pb, bitrate);
1840 url_fprintf(pb, "<td align=right>");
1841 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1842 url_fprintf(pb, "<td align=right>");
1843 fmt_bytecount(pb, c1->data_count);
1844 url_fprintf(pb, "\n");
1847 url_fprintf(pb, "</TABLE>\n");
1852 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1853 url_fprintf(pb, "</BODY>\n</HTML>\n");
1855 len = url_close_dyn_buf(pb, &c->pb_buffer);
1856 c->buffer_ptr = c->pb_buffer;
1857 c->buffer_end = c->pb_buffer + len;
1860 /* check if the parser needs to be opened for stream i */
1861 static void open_parser(AVFormatContext *s, int i)
1863 AVStream *st = s->streams[i];
1866 if (!st->codec->codec) {
1867 codec = avcodec_find_decoder(st->codec->codec_id);
1868 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1869 st->codec->parse_only = 1;
1870 if (avcodec_open(st->codec, codec) < 0)
1871 st->codec->parse_only = 0;
1876 static int open_input_stream(HTTPContext *c, const char *info)
1879 char input_filename[1024];
1884 /* find file name */
1885 if (c->stream->feed) {
1886 strcpy(input_filename, c->stream->feed->feed_filename);
1887 buf_size = FFM_PACKET_SIZE;
1888 /* compute position (absolute time) */
1889 if (find_info_tag(buf, sizeof(buf), "date", info))
1891 stream_pos = parse_date(buf, 0);
1892 if (stream_pos == INT64_MIN)
1895 else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1896 int prebuffer = strtol(buf, 0, 10);
1897 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1899 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1901 strcpy(input_filename, c->stream->feed_filename);
1903 /* compute position (relative time) */
1904 if (find_info_tag(buf, sizeof(buf), "date", info))
1906 stream_pos = parse_date(buf, 1);
1907 if (stream_pos == INT64_MIN)
1913 if (input_filename[0] == '\0')
1917 { time_t when = stream_pos / 1000000;
1918 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1923 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1924 buf_size, c->stream->ap_in) < 0) {
1925 http_log("%s not found", input_filename);
1928 s->flags |= AVFMT_FLAG_GENPTS;
1930 av_find_stream_info(c->fmt_in);
1932 /* open each parser */
1933 for(i=0;i<s->nb_streams;i++)
1936 /* choose stream as clock source (we favorize video stream if
1937 present) for packet sending */
1938 c->pts_stream_index = 0;
1939 for(i=0;i<c->stream->nb_streams;i++) {
1940 if (c->pts_stream_index == 0 &&
1941 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1942 c->pts_stream_index = i;
1947 if (c->fmt_in->iformat->read_seek)
1948 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1950 /* set the start time (needed for maxtime and RTP packet timing) */
1951 c->start_time = cur_time;
1952 c->first_pts = AV_NOPTS_VALUE;
1956 /* return the server clock (in us) */
1957 static int64_t get_server_clock(HTTPContext *c)
1959 /* compute current pts value from system time */
1960 return (cur_time - c->start_time) * 1000;
1963 /* return the estimated time at which the current packet must be sent
1965 static int64_t get_packet_send_clock(HTTPContext *c)
1967 int bytes_left, bytes_sent, frame_bytes;
1969 frame_bytes = c->cur_frame_bytes;
1970 if (frame_bytes <= 0)
1973 bytes_left = c->buffer_end - c->buffer_ptr;
1974 bytes_sent = frame_bytes - bytes_left;
1975 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1980 static int http_prepare_data(HTTPContext *c)
1983 AVFormatContext *ctx;
1985 av_freep(&c->pb_buffer);
1987 case HTTPSTATE_SEND_DATA_HEADER:
1988 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1989 av_strlcpy(c->fmt_ctx.author, c->stream->author,
1990 sizeof(c->fmt_ctx.author));
1991 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
1992 sizeof(c->fmt_ctx.comment));
1993 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
1994 sizeof(c->fmt_ctx.copyright));
1995 av_strlcpy(c->fmt_ctx.title, c->stream->title,
1996 sizeof(c->fmt_ctx.title));
1998 /* open output stream by using specified codecs */
1999 c->fmt_ctx.oformat = c->stream->fmt;
2000 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2001 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2004 st = av_mallocz(sizeof(AVStream));
2005 st->codec= avcodec_alloc_context();
2006 c->fmt_ctx.streams[i] = st;
2007 /* if file or feed, then just take streams from FFStream struct */
2008 if (!c->stream->feed ||
2009 c->stream->feed == c->stream)
2010 src = c->stream->streams[i];
2012 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2016 st->codec->frame_number = 0; /* XXX: should be done in
2017 AVStream, not in codec */
2018 /* I'm pretty sure that this is not correct...
2019 * However, without it, we crash
2021 st->codec->coded_frame = &dummy_frame;
2023 c->got_key_frame = 0;
2025 /* prepare header and save header data in a stream */
2026 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2027 /* XXX: potential leak */
2030 c->fmt_ctx.pb.is_streamed = 1;
2032 av_set_parameters(&c->fmt_ctx, NULL);
2033 if (av_write_header(&c->fmt_ctx) < 0)
2036 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2037 c->buffer_ptr = c->pb_buffer;
2038 c->buffer_end = c->pb_buffer + len;
2040 c->state = HTTPSTATE_SEND_DATA;
2041 c->last_packet_sent = 0;
2043 case HTTPSTATE_SEND_DATA:
2044 /* find a new packet */
2048 /* read a packet from the input stream */
2049 if (c->stream->feed)
2050 ffm_set_write_index(c->fmt_in,
2051 c->stream->feed->feed_write_index,
2052 c->stream->feed->feed_size);
2054 if (c->stream->max_time &&
2055 c->stream->max_time + c->start_time - cur_time < 0)
2056 /* We have timed out */
2057 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2060 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2061 if (c->stream->feed && c->stream->feed->feed_opened) {
2062 /* if coming from feed, it means we reached the end of the
2063 ffm file, so must wait for more data */
2064 c->state = HTTPSTATE_WAIT_FEED;
2065 return 1; /* state changed */
2067 if (c->stream->loop) {
2068 av_close_input_file(c->fmt_in);
2070 if (open_input_stream(c, "") < 0)
2075 /* must send trailer now because eof or error */
2076 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2080 /* update first pts if needed */
2081 if (c->first_pts == AV_NOPTS_VALUE) {
2082 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2083 c->start_time = cur_time;
2085 /* send it to the appropriate stream */
2086 if (c->stream->feed) {
2087 /* if coming from a feed, select the right stream */
2088 if (c->switch_pending) {
2089 c->switch_pending = 0;
2090 for(i=0;i<c->stream->nb_streams;i++) {
2091 if (c->switch_feed_streams[i] == pkt.stream_index)
2092 if (pkt.flags & PKT_FLAG_KEY)
2093 do_switch_stream(c, i);
2094 if (c->switch_feed_streams[i] >= 0)
2095 c->switch_pending = 1;
2098 for(i=0;i<c->stream->nb_streams;i++) {
2099 if (c->feed_streams[i] == pkt.stream_index) {
2100 pkt.stream_index = i;
2101 if (pkt.flags & PKT_FLAG_KEY)
2102 c->got_key_frame |= 1 << i;
2103 /* See if we have all the key frames, then
2104 * we start to send. This logic is not quite
2105 * right, but it works for the case of a
2106 * single video stream with one or more
2107 * audio streams (for which every frame is
2108 * typically a key frame).
2110 if (!c->stream->send_on_key ||
2111 ((c->got_key_frame + 1) >> c->stream->nb_streams))
2116 AVCodecContext *codec;
2119 /* specific handling for RTP: we use several
2120 output stream (one for each RTP
2121 connection). XXX: need more abstract handling */
2122 if (c->is_packetized) {
2124 /* compute send time and duration */
2125 st = c->fmt_in->streams[pkt.stream_index];
2126 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2127 if (st->start_time != AV_NOPTS_VALUE)
2128 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2129 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2131 printf("index=%d pts=%0.3f duration=%0.6f\n",
2133 (double)c->cur_pts /
2135 (double)c->cur_frame_duration /
2138 /* find RTP context */
2139 c->packet_stream_index = pkt.stream_index;
2140 ctx = c->rtp_ctx[c->packet_stream_index];
2142 av_free_packet(&pkt);
2145 codec = ctx->streams[0]->codec;
2146 /* only one stream per RTP connection */
2147 pkt.stream_index = 0;
2151 codec = ctx->streams[pkt.stream_index]->codec;
2154 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2155 if (c->is_packetized) {
2156 int max_packet_size;
2157 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2158 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2160 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2161 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2163 ret = url_open_dyn_buf(&ctx->pb);
2166 /* XXX: potential leak */
2169 if (pkt.dts != AV_NOPTS_VALUE)
2170 pkt.dts = av_rescale_q(pkt.dts,
2171 c->fmt_in->streams[pkt.stream_index]->time_base,
2172 ctx->streams[pkt.stream_index]->time_base);
2173 if (pkt.pts != AV_NOPTS_VALUE)
2174 pkt.pts = av_rescale_q(pkt.pts,
2175 c->fmt_in->streams[pkt.stream_index]->time_base,
2176 ctx->streams[pkt.stream_index]->time_base);
2177 if (av_write_frame(ctx, &pkt))
2178 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2180 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2181 c->cur_frame_bytes = len;
2182 c->buffer_ptr = c->pb_buffer;
2183 c->buffer_end = c->pb_buffer + len;
2185 codec->frame_number++;
2189 av_free_packet(&pkt);
2195 case HTTPSTATE_SEND_DATA_TRAILER:
2196 /* last packet test ? */
2197 if (c->last_packet_sent || c->is_packetized)
2200 /* prepare header */
2201 if (url_open_dyn_buf(&ctx->pb) < 0) {
2202 /* XXX: potential leak */
2205 av_write_trailer(ctx);
2206 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2207 c->buffer_ptr = c->pb_buffer;
2208 c->buffer_end = c->pb_buffer + len;
2210 c->last_packet_sent = 1;
2216 /* should convert the format at the same time */
2217 /* send data starting at c->buffer_ptr to the output connection
2218 (either UDP or TCP connection) */
2219 static int http_send_data(HTTPContext *c)
2224 if (c->buffer_ptr >= c->buffer_end) {
2225 ret = http_prepare_data(c);
2229 /* state change requested */
2232 if (c->is_packetized) {
2233 /* RTP data output */
2234 len = c->buffer_end - c->buffer_ptr;
2236 /* fail safe - should never happen */
2238 c->buffer_ptr = c->buffer_end;
2241 len = (c->buffer_ptr[0] << 24) |
2242 (c->buffer_ptr[1] << 16) |
2243 (c->buffer_ptr[2] << 8) |
2245 if (len > (c->buffer_end - c->buffer_ptr))
2247 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2248 /* nothing to send yet: we can wait */
2252 c->data_count += len;
2253 update_datarate(&c->datarate, c->data_count);
2255 c->stream->bytes_served += len;
2257 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2258 /* RTP packets are sent inside the RTSP TCP connection */
2259 ByteIOContext pb1, *pb = &pb1;
2260 int interleaved_index, size;
2262 HTTPContext *rtsp_c;
2265 /* if no RTSP connection left, error */
2268 /* if already sending something, then wait. */
2269 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2271 if (url_open_dyn_buf(pb) < 0)
2273 interleaved_index = c->packet_stream_index * 2;
2274 /* RTCP packets are sent at odd indexes */
2275 if (c->buffer_ptr[1] == 200)
2276 interleaved_index++;
2277 /* write RTSP TCP header */
2279 header[1] = interleaved_index;
2280 header[2] = len >> 8;
2282 put_buffer(pb, header, 4);
2283 /* write RTP packet data */
2285 put_buffer(pb, c->buffer_ptr, len);
2286 size = url_close_dyn_buf(pb, &c->packet_buffer);
2287 /* prepare asynchronous TCP sending */
2288 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2289 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2290 c->buffer_ptr += len;
2292 /* send everything we can NOW */
2293 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2294 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2296 rtsp_c->packet_buffer_ptr += len;
2297 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2298 /* if we could not send all the data, we will
2299 send it later, so a new state is needed to
2300 "lock" the RTSP TCP connection */
2301 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2304 /* all data has been sent */
2305 av_freep(&c->packet_buffer);
2307 /* send RTP packet directly in UDP */
2309 url_write(c->rtp_handles[c->packet_stream_index],
2310 c->buffer_ptr, len);
2311 c->buffer_ptr += len;
2312 /* here we continue as we can send several packets per 10 ms slot */
2315 /* TCP data output */
2316 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2318 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2319 ff_neterrno() != FF_NETERROR(EINTR))
2320 /* error : close connection */
2325 c->buffer_ptr += len;
2327 c->data_count += len;
2328 update_datarate(&c->datarate, c->data_count);
2330 c->stream->bytes_served += len;
2338 static int http_start_receive_data(HTTPContext *c)
2342 if (c->stream->feed_opened)
2345 /* Don't permit writing to this one */
2346 if (c->stream->readonly)
2350 fd = open(c->stream->feed_filename, O_RDWR);
2355 c->stream->feed_write_index = ffm_read_write_index(fd);
2356 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2357 lseek(fd, 0, SEEK_SET);
2359 /* init buffer input */
2360 c->buffer_ptr = c->buffer;
2361 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2362 c->stream->feed_opened = 1;
2366 static int http_receive_data(HTTPContext *c)
2370 if (c->buffer_end > c->buffer_ptr) {
2373 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2375 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2376 ff_neterrno() != FF_NETERROR(EINTR))
2377 /* error : close connection */
2379 } else if (len == 0)
2380 /* end of connection : close it */
2383 c->buffer_ptr += len;
2384 c->data_count += len;
2385 update_datarate(&c->datarate, c->data_count);
2389 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2390 if (c->buffer[0] != 'f' ||
2391 c->buffer[1] != 'm') {
2392 http_log("Feed stream has become desynchronized -- disconnecting\n");
2397 if (c->buffer_ptr >= c->buffer_end) {
2398 FFStream *feed = c->stream;
2399 /* a packet has been received : write it in the store, except
2401 if (c->data_count > FFM_PACKET_SIZE) {
2403 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2404 /* XXX: use llseek or url_seek */
2405 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2406 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2408 feed->feed_write_index += FFM_PACKET_SIZE;
2409 /* update file size */
2410 if (feed->feed_write_index > c->stream->feed_size)
2411 feed->feed_size = feed->feed_write_index;
2413 /* handle wrap around if max file size reached */
2414 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2415 feed->feed_write_index = FFM_PACKET_SIZE;
2418 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2420 /* wake up any waiting connections */
2421 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2422 if (c1->state == HTTPSTATE_WAIT_FEED &&
2423 c1->stream->feed == c->stream->feed)
2424 c1->state = HTTPSTATE_SEND_DATA;
2427 /* We have a header in our hands that contains useful data */
2429 AVInputFormat *fmt_in;
2430 ByteIOContext *pb = &s.pb;
2433 memset(&s, 0, sizeof(s));
2435 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2436 pb->buf_end = c->buffer_end; /* ?? */
2437 pb->is_streamed = 1;
2439 /* use feed output format name to find corresponding input format */
2440 fmt_in = av_find_input_format(feed->fmt->name);
2444 if (fmt_in->priv_data_size > 0) {
2445 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2451 if (fmt_in->read_header(&s, 0) < 0) {
2452 av_freep(&s.priv_data);
2456 /* Now we have the actual streams */
2457 if (s.nb_streams != feed->nb_streams) {
2458 av_freep(&s.priv_data);
2461 for (i = 0; i < s.nb_streams; i++)
2462 memcpy(feed->streams[i]->codec,
2463 s.streams[i]->codec, sizeof(AVCodecContext));
2464 av_freep(&s.priv_data);
2466 c->buffer_ptr = c->buffer;
2471 c->stream->feed_opened = 0;
2476 /********************************************************************/
2479 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2486 switch(error_number) {
2487 case RTSP_STATUS_OK:
2490 case RTSP_STATUS_METHOD:
2491 str = "Method Not Allowed";
2493 case RTSP_STATUS_BANDWIDTH:
2494 str = "Not Enough Bandwidth";
2496 case RTSP_STATUS_SESSION:
2497 str = "Session Not Found";
2499 case RTSP_STATUS_STATE:
2500 str = "Method Not Valid in This State";
2502 case RTSP_STATUS_AGGREGATE:
2503 str = "Aggregate operation not allowed";
2505 case RTSP_STATUS_ONLY_AGGREGATE:
2506 str = "Only aggregate operation allowed";
2508 case RTSP_STATUS_TRANSPORT:
2509 str = "Unsupported transport";
2511 case RTSP_STATUS_INTERNAL:
2512 str = "Internal Server Error";
2514 case RTSP_STATUS_SERVICE:
2515 str = "Service Unavailable";
2517 case RTSP_STATUS_VERSION:
2518 str = "RTSP Version not supported";
2521 str = "Unknown Error";
2525 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2526 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2528 /* output GMT time */
2532 p = buf2 + strlen(p) - 1;
2535 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2538 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2540 rtsp_reply_header(c, error_number);
2541 url_fprintf(c->pb, "\r\n");
2544 static int rtsp_parse_request(HTTPContext *c)
2546 const char *p, *p1, *p2;
2553 RTSPHeader header1, *header = &header1;
2555 c->buffer_ptr[0] = '\0';
2558 get_word(cmd, sizeof(cmd), &p);
2559 get_word(url, sizeof(url), &p);
2560 get_word(protocol, sizeof(protocol), &p);
2562 av_strlcpy(c->method, cmd, sizeof(c->method));
2563 av_strlcpy(c->url, url, sizeof(c->url));
2564 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2567 if (url_open_dyn_buf(c->pb) < 0) {
2568 /* XXX: cannot do more */
2569 c->pb = NULL; /* safety */
2573 /* check version name */
2574 if (strcmp(protocol, "RTSP/1.0") != 0) {
2575 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2579 /* parse each header line */
2580 memset(header, 0, sizeof(RTSPHeader));
2581 /* skip to next line */
2582 while (*p != '\n' && *p != '\0')
2586 while (*p != '\0') {
2587 p1 = strchr(p, '\n');
2591 if (p2 > p && p2[-1] == '\r')
2593 /* skip empty line */
2597 if (len > sizeof(line) - 1)
2598 len = sizeof(line) - 1;
2599 memcpy(line, p, len);
2601 rtsp_parse_line(header, line);
2605 /* handle sequence number */
2606 c->seq = header->seq;
2608 if (!strcmp(cmd, "DESCRIBE"))
2609 rtsp_cmd_describe(c, url);
2610 else if (!strcmp(cmd, "OPTIONS"))
2611 rtsp_cmd_options(c, url);
2612 else if (!strcmp(cmd, "SETUP"))
2613 rtsp_cmd_setup(c, url, header);
2614 else if (!strcmp(cmd, "PLAY"))
2615 rtsp_cmd_play(c, url, header);
2616 else if (!strcmp(cmd, "PAUSE"))
2617 rtsp_cmd_pause(c, url, header);
2618 else if (!strcmp(cmd, "TEARDOWN"))
2619 rtsp_cmd_teardown(c, url, header);
2621 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2624 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2625 c->pb = NULL; /* safety */
2627 /* XXX: cannot do more */
2630 c->buffer_ptr = c->pb_buffer;
2631 c->buffer_end = c->pb_buffer + len;
2632 c->state = RTSPSTATE_SEND_REPLY;
2636 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2637 struct in_addr my_ip)
2639 AVFormatContext *avc;
2640 AVStream avs[MAX_STREAMS];
2643 avc = av_alloc_format_context();
2647 if (stream->title[0] != 0) {
2648 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2650 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2652 avc->nb_streams = stream->nb_streams;
2653 if (stream->is_multicast) {
2654 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2655 inet_ntoa(stream->multicast_ip),
2656 stream->multicast_port, stream->multicast_ttl);
2659 for(i = 0; i < stream->nb_streams; i++) {
2660 avc->streams[i] = &avs[i];
2661 avc->streams[i]->codec = stream->streams[i]->codec;
2663 *pbuffer = av_mallocz(2048);
2664 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2667 return strlen(*pbuffer);
2670 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2672 // rtsp_reply_header(c, RTSP_STATUS_OK);
2673 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2674 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2675 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2676 url_fprintf(c->pb, "\r\n");
2679 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2685 int content_length, len;
2686 struct sockaddr_in my_addr;
2688 /* find which url is asked */
2689 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2694 for(stream = first_stream; stream != NULL; stream = stream->next) {
2695 if (!stream->is_feed &&
2696 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2697 !strcmp(path, stream->filename)) {
2701 /* no stream found */
2702 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2706 /* prepare the media description in sdp format */
2708 /* get the host IP */
2709 len = sizeof(my_addr);
2710 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2711 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2712 if (content_length < 0) {
2713 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2716 rtsp_reply_header(c, RTSP_STATUS_OK);
2717 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2718 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2719 url_fprintf(c->pb, "\r\n");
2720 put_buffer(c->pb, content, content_length);
2723 static HTTPContext *find_rtp_session(const char *session_id)
2727 if (session_id[0] == '\0')
2730 for(c = first_http_ctx; c != NULL; c = c->next) {
2731 if (!strcmp(c->session_id, session_id))
2737 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2739 RTSPTransportField *th;
2742 for(i=0;i<h->nb_transports;i++) {
2743 th = &h->transports[i];
2744 if (th->protocol == protocol)
2750 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2754 int stream_index, port;
2759 RTSPTransportField *th;
2760 struct sockaddr_in dest_addr;
2761 RTSPActionServerSetup setup;
2763 /* find which url is asked */
2764 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2769 /* now check each stream */
2770 for(stream = first_stream; stream != NULL; stream = stream->next) {
2771 if (!stream->is_feed &&
2772 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2773 /* accept aggregate filenames only if single stream */
2774 if (!strcmp(path, stream->filename)) {
2775 if (stream->nb_streams != 1) {
2776 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2783 for(stream_index = 0; stream_index < stream->nb_streams;
2785 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2786 stream->filename, stream_index);
2787 if (!strcmp(path, buf))
2792 /* no stream found */
2793 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2797 /* generate session id if needed */
2798 if (h->session_id[0] == '\0')
2799 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2800 av_random(&random_state), av_random(&random_state));
2802 /* find rtp session, and create it if none found */
2803 rtp_c = find_rtp_session(h->session_id);
2805 /* always prefer UDP */
2806 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2808 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2810 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2815 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2818 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2822 /* open input stream */
2823 if (open_input_stream(rtp_c, "") < 0) {
2824 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2829 /* test if stream is OK (test needed because several SETUP needs
2830 to be done for a given file) */
2831 if (rtp_c->stream != stream) {
2832 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2836 /* test if stream is already set up */
2837 if (rtp_c->rtp_ctx[stream_index]) {
2838 rtsp_reply_error(c, RTSP_STATUS_STATE);
2842 /* check transport */
2843 th = find_transport(h, rtp_c->rtp_protocol);
2844 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2845 th->client_port_min <= 0)) {
2846 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2850 /* setup default options */
2851 setup.transport_option[0] = '\0';
2852 dest_addr = rtp_c->from_addr;
2853 dest_addr.sin_port = htons(th->client_port_min);
2856 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2857 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2861 /* now everything is OK, so we can send the connection parameters */
2862 rtsp_reply_header(c, RTSP_STATUS_OK);
2864 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2866 switch(rtp_c->rtp_protocol) {
2867 case RTSP_PROTOCOL_RTP_UDP:
2868 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2869 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2870 "client_port=%d-%d;server_port=%d-%d",
2871 th->client_port_min, th->client_port_min + 1,
2874 case RTSP_PROTOCOL_RTP_TCP:
2875 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2876 stream_index * 2, stream_index * 2 + 1);
2881 if (setup.transport_option[0] != '\0')
2882 url_fprintf(c->pb, ";%s", setup.transport_option);
2883 url_fprintf(c->pb, "\r\n");
2886 url_fprintf(c->pb, "\r\n");
2890 /* find an rtp connection by using the session ID. Check consistency
2892 static HTTPContext *find_rtp_session_with_url(const char *url,
2893 const char *session_id)
2901 rtp_c = find_rtp_session(session_id);
2905 /* find which url is asked */
2906 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2910 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2911 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2912 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2913 rtp_c->stream->filename, s);
2914 if(!strncmp(path, buf, sizeof(buf))) {
2915 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2922 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2926 rtp_c = find_rtp_session_with_url(url, h->session_id);
2928 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2932 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2933 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2934 rtp_c->state != HTTPSTATE_READY) {
2935 rtsp_reply_error(c, RTSP_STATUS_STATE);
2940 /* XXX: seek in stream */
2941 if (h->range_start != AV_NOPTS_VALUE) {
2942 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2943 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2947 rtp_c->state = HTTPSTATE_SEND_DATA;
2949 /* now everything is OK, so we can send the connection parameters */
2950 rtsp_reply_header(c, RTSP_STATUS_OK);
2952 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2953 url_fprintf(c->pb, "\r\n");
2956 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2960 rtp_c = find_rtp_session_with_url(url, h->session_id);
2962 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2966 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2967 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2968 rtsp_reply_error(c, RTSP_STATUS_STATE);
2972 rtp_c->state = HTTPSTATE_READY;
2973 rtp_c->first_pts = AV_NOPTS_VALUE;
2974 /* now everything is OK, so we can send the connection parameters */
2975 rtsp_reply_header(c, RTSP_STATUS_OK);
2977 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2978 url_fprintf(c->pb, "\r\n");
2981 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
2984 char session_id[32];
2986 rtp_c = find_rtp_session_with_url(url, h->session_id);
2988 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2992 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
2994 /* abort the session */
2995 close_connection(rtp_c);
2997 /* now everything is OK, so we can send the connection parameters */
2998 rtsp_reply_header(c, RTSP_STATUS_OK);
3000 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3001 url_fprintf(c->pb, "\r\n");
3005 /********************************************************************/
3008 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3009 FFStream *stream, const char *session_id,
3010 enum RTSPProtocol rtp_protocol)
3012 HTTPContext *c = NULL;
3013 const char *proto_str;
3015 /* XXX: should output a warning page when coming
3016 close to the connection limit */
3017 if (nb_connections >= nb_max_connections)
3020 /* add a new connection */
3021 c = av_mallocz(sizeof(HTTPContext));
3026 c->poll_entry = NULL;
3027 c->from_addr = *from_addr;
3028 c->buffer_size = IOBUFFER_INIT_SIZE;
3029 c->buffer = av_malloc(c->buffer_size);
3034 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3035 c->state = HTTPSTATE_READY;
3036 c->is_packetized = 1;
3037 c->rtp_protocol = rtp_protocol;
3039 /* protocol is shown in statistics */
3040 switch(c->rtp_protocol) {
3041 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3042 proto_str = "MCAST";
3044 case RTSP_PROTOCOL_RTP_UDP:
3047 case RTSP_PROTOCOL_RTP_TCP:
3054 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3055 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3057 current_bandwidth += stream->bandwidth;
3059 c->next = first_http_ctx;
3071 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3072 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3074 static int rtp_new_av_stream(HTTPContext *c,
3075 int stream_index, struct sockaddr_in *dest_addr,
3076 HTTPContext *rtsp_c)
3078 AVFormatContext *ctx;
3084 int max_packet_size;
3086 /* now we can open the relevant output stream */
3087 ctx = av_alloc_format_context();
3090 ctx->oformat = guess_format("rtp", NULL, NULL);
3092 st = av_mallocz(sizeof(AVStream));
3095 st->codec= avcodec_alloc_context();
3096 ctx->nb_streams = 1;
3097 ctx->streams[0] = st;
3099 if (!c->stream->feed ||
3100 c->stream->feed == c->stream)
3101 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3104 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3106 st->priv_data = NULL;
3108 /* build destination RTP address */
3109 ipaddr = inet_ntoa(dest_addr->sin_addr);
3111 switch(c->rtp_protocol) {
3112 case RTSP_PROTOCOL_RTP_UDP:
3113 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3116 /* XXX: also pass as parameter to function ? */
3117 if (c->stream->is_multicast) {
3119 ttl = c->stream->multicast_ttl;
3122 snprintf(ctx->filename, sizeof(ctx->filename),
3123 "rtp://%s:%d?multicast=1&ttl=%d",
3124 ipaddr, ntohs(dest_addr->sin_port), ttl);
3126 snprintf(ctx->filename, sizeof(ctx->filename),
3127 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3130 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3132 c->rtp_handles[stream_index] = h;
3133 max_packet_size = url_get_max_packet_size(h);
3135 case RTSP_PROTOCOL_RTP_TCP:
3138 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3144 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3145 ipaddr, ntohs(dest_addr->sin_port),
3147 c->stream->filename, stream_index, c->protocol);
3149 /* normally, no packets should be output here, but the packet size may be checked */
3150 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3151 /* XXX: close stream */
3154 av_set_parameters(ctx, NULL);
3155 if (av_write_header(ctx) < 0) {
3162 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3165 c->rtp_ctx[stream_index] = ctx;
3169 /********************************************************************/
3170 /* ffserver initialization */
3172 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3176 fst = av_mallocz(sizeof(AVStream));
3179 fst->codec= avcodec_alloc_context();
3180 fst->priv_data = av_mallocz(sizeof(FeedData));
3181 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3182 fst->codec->coded_frame = &dummy_frame;
3183 fst->index = stream->nb_streams;
3184 av_set_pts_info(fst, 33, 1, 90000);
3185 stream->streams[stream->nb_streams++] = fst;
3189 /* return the stream number in the feed */
3190 static int add_av_stream(FFStream *feed, AVStream *st)
3193 AVCodecContext *av, *av1;
3197 for(i=0;i<feed->nb_streams;i++) {
3198 st = feed->streams[i];
3200 if (av1->codec_id == av->codec_id &&
3201 av1->codec_type == av->codec_type &&
3202 av1->bit_rate == av->bit_rate) {
3204 switch(av->codec_type) {
3205 case CODEC_TYPE_AUDIO:
3206 if (av1->channels == av->channels &&
3207 av1->sample_rate == av->sample_rate)
3210 case CODEC_TYPE_VIDEO:
3211 if (av1->width == av->width &&
3212 av1->height == av->height &&
3213 av1->time_base.den == av->time_base.den &&
3214 av1->time_base.num == av->time_base.num &&
3215 av1->gop_size == av->gop_size)
3224 fst = add_av_stream1(feed, av);
3227 return feed->nb_streams - 1;
3232 static void remove_stream(FFStream *stream)
3236 while (*ps != NULL) {
3244 /* specific mpeg4 handling : we extract the raw parameters */
3245 static void extract_mpeg4_header(AVFormatContext *infile)
3247 int mpeg4_count, i, size;
3253 for(i=0;i<infile->nb_streams;i++) {
3254 st = infile->streams[i];
3255 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3256 st->codec->extradata_size == 0) {
3263 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3264 while (mpeg4_count > 0) {
3265 if (av_read_packet(infile, &pkt) < 0)
3267 st = infile->streams[pkt.stream_index];
3268 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3269 st->codec->extradata_size == 0) {
3270 av_freep(&st->codec->extradata);
3271 /* fill extradata with the header */
3272 /* XXX: we make hard suppositions here ! */
3274 while (p < pkt.data + pkt.size - 4) {
3275 /* stop when vop header is found */
3276 if (p[0] == 0x00 && p[1] == 0x00 &&
3277 p[2] == 0x01 && p[3] == 0xb6) {
3278 size = p - pkt.data;
3279 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3280 st->codec->extradata = av_malloc(size);
3281 st->codec->extradata_size = size;
3282 memcpy(st->codec->extradata, pkt.data, size);
3289 av_free_packet(&pkt);
3293 /* compute the needed AVStream for each file */
3294 static void build_file_streams(void)
3296 FFStream *stream, *stream_next;
3297 AVFormatContext *infile;
3300 /* gather all streams */
3301 for(stream = first_stream; stream != NULL; stream = stream_next) {
3302 stream_next = stream->next;
3303 if (stream->stream_type == STREAM_TYPE_LIVE &&
3305 /* the stream comes from a file */
3306 /* try to open the file */
3308 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3309 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3310 /* specific case : if transport stream output to RTP,
3311 we use a raw transport stream reader */
3312 stream->ap_in->mpeg2ts_raw = 1;
3313 stream->ap_in->mpeg2ts_compute_pcr = 1;
3316 if (av_open_input_file(&infile, stream->feed_filename,
3317 stream->ifmt, 0, stream->ap_in) < 0) {
3318 http_log("%s not found", stream->feed_filename);
3319 /* remove stream (no need to spend more time on it) */
3321 remove_stream(stream);
3323 /* find all the AVStreams inside and reference them in
3325 if (av_find_stream_info(infile) < 0) {
3326 http_log("Could not find codec parameters from '%s'",
3327 stream->feed_filename);
3328 av_close_input_file(infile);
3331 extract_mpeg4_header(infile);
3333 for(i=0;i<infile->nb_streams;i++)
3334 add_av_stream1(stream, infile->streams[i]->codec);
3336 av_close_input_file(infile);
3342 /* compute the needed AVStream for each feed */
3343 static void build_feed_streams(void)
3345 FFStream *stream, *feed;
3348 /* gather all streams */
3349 for(stream = first_stream; stream != NULL; stream = stream->next) {
3350 feed = stream->feed;
3352 if (!stream->is_feed) {
3353 /* we handle a stream coming from a feed */
3354 for(i=0;i<stream->nb_streams;i++)
3355 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3360 /* gather all streams */
3361 for(stream = first_stream; stream != NULL; stream = stream->next) {
3362 feed = stream->feed;
3364 if (stream->is_feed) {
3365 for(i=0;i<stream->nb_streams;i++)
3366 stream->feed_streams[i] = i;
3371 /* create feed files if needed */
3372 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3375 if (url_exist(feed->feed_filename)) {
3376 /* See if it matches */
3380 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3381 /* Now see if it matches */
3382 if (s->nb_streams == feed->nb_streams) {
3384 for(i=0;i<s->nb_streams;i++) {
3386 sf = feed->streams[i];
3389 if (sf->index != ss->index ||
3391 printf("Index & Id do not match for stream %d (%s)\n",
3392 i, feed->feed_filename);
3395 AVCodecContext *ccf, *ccs;
3399 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3401 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3402 printf("Codecs do not match for stream %d\n", i);
3404 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3405 printf("Codec bitrates do not match for stream %d\n", i);
3407 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3408 if (CHECK_CODEC(time_base.den) ||
3409 CHECK_CODEC(time_base.num) ||
3410 CHECK_CODEC(width) ||
3411 CHECK_CODEC(height)) {
3412 printf("Codec width, height and framerate do not match for stream %d\n", i);
3415 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3416 if (CHECK_CODEC(sample_rate) ||
3417 CHECK_CODEC(channels) ||
3418 CHECK_CODEC(frame_size)) {
3419 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3423 printf("Unknown codec type\n");
3431 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3432 feed->feed_filename, s->nb_streams, feed->nb_streams);
3434 av_close_input_file(s);
3436 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3437 feed->feed_filename);
3440 if (feed->readonly) {
3441 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3442 feed->feed_filename);
3445 unlink(feed->feed_filename);
3448 if (!url_exist(feed->feed_filename)) {
3449 AVFormatContext s1, *s = &s1;
3451 if (feed->readonly) {
3452 printf("Unable to create feed file '%s' as it is marked readonly\n",
3453 feed->feed_filename);
3457 /* only write the header of the ffm file */
3458 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3459 fprintf(stderr, "Could not open output feed file '%s'\n",
3460 feed->feed_filename);
3463 s->oformat = feed->fmt;
3464 s->nb_streams = feed->nb_streams;
3465 for(i=0;i<s->nb_streams;i++) {
3467 st = feed->streams[i];
3470 av_set_parameters(s, NULL);
3471 if (av_write_header(s) < 0) {
3472 fprintf(stderr, "Container doesn't supports the required parameters\n");
3475 /* XXX: need better api */
3476 av_freep(&s->priv_data);
3479 /* get feed size and write index */
3480 fd = open(feed->feed_filename, O_RDONLY);
3482 fprintf(stderr, "Could not open output feed file '%s'\n",
3483 feed->feed_filename);
3487 feed->feed_write_index = ffm_read_write_index(fd);
3488 feed->feed_size = lseek(fd, 0, SEEK_END);
3489 /* ensure that we do not wrap before the end of file */
3490 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3491 feed->feed_max_size = feed->feed_size;
3497 /* compute the bandwidth used by each stream */
3498 static void compute_bandwidth(void)
3503 for(stream = first_stream; stream != NULL; stream = stream->next) {
3505 for(i=0;i<stream->nb_streams;i++) {
3506 AVStream *st = stream->streams[i];
3507 switch(st->codec->codec_type) {
3508 case CODEC_TYPE_AUDIO:
3509 case CODEC_TYPE_VIDEO:
3510 bandwidth += st->codec->bit_rate;
3516 stream->bandwidth = (bandwidth + 999) / 1000;
3520 static void get_arg(char *buf, int buf_size, const char **pp)
3527 while (isspace(*p)) p++;
3530 if (*p == '\"' || *p == '\'')
3542 if ((q - buf) < buf_size - 1)
3547 if (quote && *p == quote)
3552 /* add a codec and set the default parameters */
3553 static void add_codec(FFStream *stream, AVCodecContext *av)
3557 /* compute default parameters */
3558 switch(av->codec_type) {
3559 case CODEC_TYPE_AUDIO:
3560 if (av->bit_rate == 0)
3561 av->bit_rate = 64000;
3562 if (av->sample_rate == 0)
3563 av->sample_rate = 22050;
3564 if (av->channels == 0)
3567 case CODEC_TYPE_VIDEO:
3568 if (av->bit_rate == 0)
3569 av->bit_rate = 64000;
3570 if (av->time_base.num == 0){
3571 av->time_base.den = 5;
3572 av->time_base.num = 1;
3574 if (av->width == 0 || av->height == 0) {
3578 /* Bitrate tolerance is less for streaming */
3579 if (av->bit_rate_tolerance == 0)
3580 av->bit_rate_tolerance = av->bit_rate / 4;
3585 if (av->max_qdiff == 0)
3587 av->qcompress = 0.5;
3590 if (!av->nsse_weight)
3591 av->nsse_weight = 8;
3593 av->frame_skip_cmp = FF_CMP_DCTMAX;
3594 av->me_method = ME_EPZS;
3595 av->rc_buffer_aggressivity = 1.0;
3598 av->rc_eq = "tex^qComp";
3599 if (!av->i_quant_factor)
3600 av->i_quant_factor = -0.8;
3601 if (!av->b_quant_factor)
3602 av->b_quant_factor = 1.25;
3603 if (!av->b_quant_offset)
3604 av->b_quant_offset = 1.25;
3605 if (!av->rc_max_rate)
3606 av->rc_max_rate = av->bit_rate * 2;
3608 if (av->rc_max_rate && !av->rc_buffer_size) {
3609 av->rc_buffer_size = av->rc_max_rate;
3618 st = av_mallocz(sizeof(AVStream));
3621 st->codec = avcodec_alloc_context();
3622 stream->streams[stream->nb_streams++] = st;
3623 memcpy(st->codec, av, sizeof(AVCodecContext));
3626 static int opt_audio_codec(const char *arg)
3632 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3637 return CODEC_ID_NONE;
3642 static int opt_video_codec(const char *arg)
3648 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3653 return CODEC_ID_NONE;
3658 /* simplistic plugin support */
3661 static void load_module(const char *filename)
3664 void (*init_func)(void);
3665 dll = dlopen(filename, RTLD_NOW);
3667 fprintf(stderr, "Could not load module '%s' - %s\n",
3668 filename, dlerror());
3672 init_func = dlsym(dll, "ffserver_module_init");
3675 "%s: init function 'ffserver_module_init()' not found\n",
3684 static int parse_ffconfig(const char *filename)
3691 int val, errors, line_num;
3692 FFStream **last_stream, *stream, *redirect;
3693 FFStream **last_feed, *feed;
3694 AVCodecContext audio_enc, video_enc;
3695 int audio_id, video_id;
3697 f = fopen(filename, "r");
3705 first_stream = NULL;
3706 last_stream = &first_stream;
3708 last_feed = &first_feed;
3712 audio_id = CODEC_ID_NONE;
3713 video_id = CODEC_ID_NONE;
3715 if (fgets(line, sizeof(line), f) == NULL)
3721 if (*p == '\0' || *p == '#')
3724 get_arg(cmd, sizeof(cmd), &p);
3726 if (!strcasecmp(cmd, "Port")) {
3727 get_arg(arg, sizeof(arg), &p);
3729 if (val < 1 || val > 65536) {
3730 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3731 filename, line_num, arg);
3734 my_http_addr.sin_port = htons(val);
3735 } else if (!strcasecmp(cmd, "BindAddress")) {
3736 get_arg(arg, sizeof(arg), &p);
3737 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3738 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3739 filename, line_num, arg);
3742 } else if (!strcasecmp(cmd, "NoDaemon")) {
3743 ffserver_daemon = 0;
3744 } else if (!strcasecmp(cmd, "RTSPPort")) {
3745 get_arg(arg, sizeof(arg), &p);
3747 if (val < 1 || val > 65536) {
3748 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3749 filename, line_num, arg);
3752 my_rtsp_addr.sin_port = htons(atoi(arg));
3753 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3754 get_arg(arg, sizeof(arg), &p);
3755 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3756 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3757 filename, line_num, arg);
3760 } else if (!strcasecmp(cmd, "MaxClients")) {
3761 get_arg(arg, sizeof(arg), &p);
3763 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3764 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3765 filename, line_num, arg);
3768 nb_max_connections = val;
3770 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3771 get_arg(arg, sizeof(arg), &p);
3773 if (val < 10 || val > 100000) {
3774 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3775 filename, line_num, arg);
3778 max_bandwidth = val;
3779 } else if (!strcasecmp(cmd, "CustomLog")) {
3780 get_arg(logfilename, sizeof(logfilename), &p);
3781 } else if (!strcasecmp(cmd, "<Feed")) {
3782 /*********************************************/
3783 /* Feed related options */
3785 if (stream || feed) {
3786 fprintf(stderr, "%s:%d: Already in a tag\n",
3787 filename, line_num);
3789 feed = av_mallocz(sizeof(FFStream));
3790 /* add in stream list */
3791 *last_stream = feed;
3792 last_stream = &feed->next;
3793 /* add in feed list */
3795 last_feed = &feed->next_feed;
3797 get_arg(feed->filename, sizeof(feed->filename), &p);
3798 q = strrchr(feed->filename, '>');
3801 feed->fmt = guess_format("ffm", NULL, NULL);
3802 /* defaut feed file */
3803 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3804 "/tmp/%s.ffm", feed->filename);
3805 feed->feed_max_size = 5 * 1024 * 1024;
3807 feed->feed = feed; /* self feeding :-) */
3809 } else if (!strcasecmp(cmd, "Launch")) {
3813 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3815 for (i = 0; i < 62; i++) {
3816 get_arg(arg, sizeof(arg), &p);
3820 feed->child_argv[i] = av_strdup(arg);
3823 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3825 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3827 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3828 inet_ntoa(my_http_addr.sin_addr),
3829 ntohs(my_http_addr.sin_port), feed->filename);
3834 fprintf(stdout, "Launch commandline: ");
3835 for (j = 0; j <= i; j++)
3836 fprintf(stdout, "%s ", feed->child_argv[j]);
3837 fprintf(stdout, "\n");
3840 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3842 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3844 } else if (stream) {
3845 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3847 } else if (!strcasecmp(cmd, "File")) {
3849 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3851 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3852 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3857 get_arg(arg, sizeof(arg), &p);
3859 fsize = strtod(p1, (char **)&p1);
3860 switch(toupper(*p1)) {
3865 fsize *= 1024 * 1024;
3868 fsize *= 1024 * 1024 * 1024;
3871 feed->feed_max_size = (int64_t)fsize;
3873 } else if (!strcasecmp(cmd, "</Feed>")) {
3875 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3876 filename, line_num);
3880 } else if (!strcasecmp(cmd, "<Stream")) {
3881 /*********************************************/
3882 /* Stream related options */
3884 if (stream || feed) {
3885 fprintf(stderr, "%s:%d: Already in a tag\n",
3886 filename, line_num);
3888 stream = av_mallocz(sizeof(FFStream));
3889 *last_stream = stream;
3890 last_stream = &stream->next;
3892 get_arg(stream->filename, sizeof(stream->filename), &p);
3893 q = strrchr(stream->filename, '>');
3896 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3897 memset(&audio_enc, 0, sizeof(AVCodecContext));
3898 memset(&video_enc, 0, sizeof(AVCodecContext));
3899 audio_id = CODEC_ID_NONE;
3900 video_id = CODEC_ID_NONE;
3902 audio_id = stream->fmt->audio_codec;
3903 video_id = stream->fmt->video_codec;
3906 } else if (!strcasecmp(cmd, "Feed")) {
3907 get_arg(arg, sizeof(arg), &p);
3912 while (sfeed != NULL) {
3913 if (!strcmp(sfeed->filename, arg))
3915 sfeed = sfeed->next_feed;
3918 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3919 filename, line_num, arg);
3921 stream->feed = sfeed;
3923 } else if (!strcasecmp(cmd, "Format")) {
3924 get_arg(arg, sizeof(arg), &p);
3925 if (!strcmp(arg, "status")) {
3926 stream->stream_type = STREAM_TYPE_STATUS;
3929 stream->stream_type = STREAM_TYPE_LIVE;
3930 /* jpeg cannot be used here, so use single frame jpeg */
3931 if (!strcmp(arg, "jpeg"))
3932 strcpy(arg, "mjpeg");
3933 stream->fmt = guess_stream_format(arg, NULL, NULL);
3935 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3936 filename, line_num, arg);
3941 audio_id = stream->fmt->audio_codec;
3942 video_id = stream->fmt->video_codec;
3944 } else if (!strcasecmp(cmd, "InputFormat")) {
3945 get_arg(arg, sizeof(arg), &p);
3946 stream->ifmt = av_find_input_format(arg);
3947 if (!stream->ifmt) {
3948 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
3949 filename, line_num, arg);
3951 } else if (!strcasecmp(cmd, "FaviconURL")) {
3952 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3953 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3955 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3956 filename, line_num);
3959 } else if (!strcasecmp(cmd, "Author")) {
3961 get_arg(stream->author, sizeof(stream->author), &p);
3962 } else if (!strcasecmp(cmd, "Comment")) {
3964 get_arg(stream->comment, sizeof(stream->comment), &p);
3965 } else if (!strcasecmp(cmd, "Copyright")) {
3967 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3968 } else if (!strcasecmp(cmd, "Title")) {
3970 get_arg(stream->title, sizeof(stream->title), &p);
3971 } else if (!strcasecmp(cmd, "Preroll")) {
3972 get_arg(arg, sizeof(arg), &p);
3974 stream->prebuffer = atof(arg) * 1000;
3975 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3977 stream->send_on_key = 1;
3978 } else if (!strcasecmp(cmd, "AudioCodec")) {
3979 get_arg(arg, sizeof(arg), &p);
3980 audio_id = opt_audio_codec(arg);
3981 if (audio_id == CODEC_ID_NONE) {
3982 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3983 filename, line_num, arg);
3986 } else if (!strcasecmp(cmd, "VideoCodec")) {
3987 get_arg(arg, sizeof(arg), &p);
3988 video_id = opt_video_codec(arg);
3989 if (video_id == CODEC_ID_NONE) {
3990 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3991 filename, line_num, arg);
3994 } else if (!strcasecmp(cmd, "MaxTime")) {
3995 get_arg(arg, sizeof(arg), &p);
3997 stream->max_time = atof(arg) * 1000;
3998 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3999 get_arg(arg, sizeof(arg), &p);
4001 audio_enc.bit_rate = atoi(arg) * 1000;
4002 } else if (!strcasecmp(cmd, "AudioChannels")) {
4003 get_arg(arg, sizeof(arg), &p);
4005 audio_enc.channels = atoi(arg);
4006 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4007 get_arg(arg, sizeof(arg), &p);
4009 audio_enc.sample_rate = atoi(arg);
4010 } else if (!strcasecmp(cmd, "AudioQuality")) {
4011 get_arg(arg, sizeof(arg), &p);
4013 // audio_enc.quality = atof(arg) * 1000;
4015 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4017 int minrate, maxrate;
4019 get_arg(arg, sizeof(arg), &p);
4021 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4022 video_enc.rc_min_rate = minrate * 1000;
4023 video_enc.rc_max_rate = maxrate * 1000;
4025 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4026 filename, line_num, arg);
4030 } else if (!strcasecmp(cmd, "Debug")) {
4032 get_arg(arg, sizeof(arg), &p);
4033 video_enc.debug = strtol(arg,0,0);
4035 } else if (!strcasecmp(cmd, "Strict")) {
4037 get_arg(arg, sizeof(arg), &p);
4038 video_enc.strict_std_compliance = atoi(arg);
4040 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4042 get_arg(arg, sizeof(arg), &p);
4043 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4045 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4047 get_arg(arg, sizeof(arg), &p);
4048 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4050 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4051 get_arg(arg, sizeof(arg), &p);
4053 video_enc.bit_rate = atoi(arg) * 1000;
4055 } else if (!strcasecmp(cmd, "VideoSize")) {
4056 get_arg(arg, sizeof(arg), &p);
4058 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4059 if ((video_enc.width % 16) != 0 ||
4060 (video_enc.height % 16) != 0) {
4061 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4062 filename, line_num);
4066 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4067 get_arg(arg, sizeof(arg), &p);
4069 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4070 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4072 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4073 get_arg(arg, sizeof(arg), &p);
4075 video_enc.gop_size = atoi(arg);
4076 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4078 video_enc.gop_size = 1;
4079 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4081 video_enc.mb_decision = FF_MB_DECISION_BITS;
4082 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4084 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4085 video_enc.flags |= CODEC_FLAG_4MV;
4087 } else if (!strcasecmp(cmd, "VideoTag")) {
4088 get_arg(arg, sizeof(arg), &p);
4089 if ((strlen(arg) == 4) && stream)
4090 video_enc.codec_tag = ff_get_fourcc(arg);
4091 } else if (!strcasecmp(cmd, "BitExact")) {
4093 video_enc.flags |= CODEC_FLAG_BITEXACT;
4094 } else if (!strcasecmp(cmd, "DctFastint")) {
4096 video_enc.dct_algo = FF_DCT_FASTINT;
4097 } else if (!strcasecmp(cmd, "IdctSimple")) {
4099 video_enc.idct_algo = FF_IDCT_SIMPLE;
4100 } else if (!strcasecmp(cmd, "Qscale")) {
4101 get_arg(arg, sizeof(arg), &p);
4103 video_enc.flags |= CODEC_FLAG_QSCALE;
4104 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4106 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4107 get_arg(arg, sizeof(arg), &p);
4109 video_enc.max_qdiff = atoi(arg);
4110 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4111 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4112 filename, line_num);
4116 } else if (!strcasecmp(cmd, "VideoQMax")) {
4117 get_arg(arg, sizeof(arg), &p);
4119 video_enc.qmax = atoi(arg);
4120 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4121 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4122 filename, line_num);
4126 } else if (!strcasecmp(cmd, "VideoQMin")) {
4127 get_arg(arg, sizeof(arg), &p);
4129 video_enc.qmin = atoi(arg);
4130 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4131 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4132 filename, line_num);
4136 } else if (!strcasecmp(cmd, "LumaElim")) {
4137 get_arg(arg, sizeof(arg), &p);
4139 video_enc.luma_elim_threshold = atoi(arg);
4140 } else if (!strcasecmp(cmd, "ChromaElim")) {
4141 get_arg(arg, sizeof(arg), &p);
4143 video_enc.chroma_elim_threshold = atoi(arg);
4144 } else if (!strcasecmp(cmd, "LumiMask")) {
4145 get_arg(arg, sizeof(arg), &p);
4147 video_enc.lumi_masking = atof(arg);
4148 } else if (!strcasecmp(cmd, "DarkMask")) {
4149 get_arg(arg, sizeof(arg), &p);
4151 video_enc.dark_masking = atof(arg);
4152 } else if (!strcasecmp(cmd, "NoVideo")) {
4153 video_id = CODEC_ID_NONE;
4154 } else if (!strcasecmp(cmd, "NoAudio")) {
4155 audio_id = CODEC_ID_NONE;
4156 } else if (!strcasecmp(cmd, "ACL")) {
4159 get_arg(arg, sizeof(arg), &p);
4160 if (strcasecmp(arg, "allow") == 0)
4161 acl.action = IP_ALLOW;
4162 else if (strcasecmp(arg, "deny") == 0)
4163 acl.action = IP_DENY;
4165 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4166 filename, line_num, arg);
4170 get_arg(arg, sizeof(arg), &p);
4172 if (resolve_host(&acl.first, arg) != 0) {
4173 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4174 filename, line_num, arg);
4177 acl.last = acl.first;
4179 get_arg(arg, sizeof(arg), &p);
4182 if (resolve_host(&acl.last, arg) != 0) {
4183 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4184 filename, line_num, arg);
4190 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4191 IPAddressACL **naclp = 0;
4197 naclp = &stream->acl;
4201 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4202 filename, line_num);
4208 naclp = &(*naclp)->next;
4213 } else if (!strcasecmp(cmd, "RTSPOption")) {
4214 get_arg(arg, sizeof(arg), &p);
4216 av_freep(&stream->rtsp_option);
4217 stream->rtsp_option = av_strdup(arg);
4219 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4220 get_arg(arg, sizeof(arg), &p);
4222 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4223 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4224 filename, line_num, arg);
4227 stream->is_multicast = 1;
4228 stream->loop = 1; /* default is looping */
4230 } else if (!strcasecmp(cmd, "MulticastPort")) {
4231 get_arg(arg, sizeof(arg), &p);
4233 stream->multicast_port = atoi(arg);
4234 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4235 get_arg(arg, sizeof(arg), &p);
4237 stream->multicast_ttl = atoi(arg);
4238 } else if (!strcasecmp(cmd, "NoLoop")) {
4241 } else if (!strcasecmp(cmd, "</Stream>")) {
4243 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4244 filename, line_num);
4247 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4248 if (audio_id != CODEC_ID_NONE) {
4249 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4250 audio_enc.codec_id = audio_id;
4251 add_codec(stream, &audio_enc);
4253 if (video_id != CODEC_ID_NONE) {
4254 video_enc.codec_type = CODEC_TYPE_VIDEO;
4255 video_enc.codec_id = video_id;
4256 add_codec(stream, &video_enc);
4260 } else if (!strcasecmp(cmd, "<Redirect")) {
4261 /*********************************************/
4263 if (stream || feed || redirect) {
4264 fprintf(stderr, "%s:%d: Already in a tag\n",
4265 filename, line_num);
4268 redirect = av_mallocz(sizeof(FFStream));
4269 *last_stream = redirect;
4270 last_stream = &redirect->next;
4272 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4273 q = strrchr(redirect->filename, '>');
4276 redirect->stream_type = STREAM_TYPE_REDIRECT;
4278 } else if (!strcasecmp(cmd, "URL")) {
4280 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4281 } else if (!strcasecmp(cmd, "</Redirect>")) {
4283 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4284 filename, line_num);
4287 if (!redirect->feed_filename[0]) {
4288 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4289 filename, line_num);
4293 } else if (!strcasecmp(cmd, "LoadModule")) {
4294 get_arg(arg, sizeof(arg), &p);
4298 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4299 filename, line_num, arg);
4303 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4304 filename, line_num, cmd);
4316 static void show_banner(void)
4318 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4321 static void show_help(void)
4324 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4325 "Hyper fast multi format Audio/Video streaming server\n"
4327 "-L : print the LICENSE\n"
4329 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4333 static void handle_child_exit(int sig)
4338 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4341 for (feed = first_feed; feed; feed = feed->next) {
4342 if (feed->pid == pid) {
4343 int uptime = time(0) - feed->pid_start;
4346 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4349 /* Turn off any more restarts */
4350 feed->child_argv = 0;
4355 need_to_start_children = 1;
4358 int main(int argc, char **argv)
4360 const char *config_filename;
4362 struct sigaction sigact;
4366 config_filename = "/etc/ffserver.conf";
4368 my_program_name = argv[0];
4369 my_program_dir = getcwd(0, 0);
4370 ffserver_daemon = 1;
4373 c = getopt(argc, argv, "ndLh?f:");
4390 ffserver_daemon = 0;
4393 config_filename = optarg;
4400 putenv("http_proxy"); /* Kill the http_proxy */
4402 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4404 /* address on which the server will handle HTTP connections */
4405 my_http_addr.sin_family = AF_INET;
4406 my_http_addr.sin_port = htons (8080);
4407 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4409 /* address on which the server will handle RTSP connections */
4410 my_rtsp_addr.sin_family = AF_INET;
4411 my_rtsp_addr.sin_port = htons (5454);
4412 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4414 nb_max_connections = 5;
4415 max_bandwidth = 1000;
4416 first_stream = NULL;
4417 logfilename[0] = '\0';
4419 memset(&sigact, 0, sizeof(sigact));
4420 sigact.sa_handler = handle_child_exit;
4421 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4422 sigaction(SIGCHLD, &sigact, 0);
4424 if (parse_ffconfig(config_filename) < 0) {
4425 fprintf(stderr, "Incorrect config file - exiting.\n");
4429 build_file_streams();
4431 build_feed_streams();
4433 compute_bandwidth();
4435 /* put the process in background and detach it from its TTY */
4436 if (ffserver_daemon) {
4443 } else if (pid > 0) {
4451 open("/dev/null", O_RDWR);
4452 if (strcmp(logfilename, "-") != 0) {
4462 signal(SIGPIPE, SIG_IGN);
4464 /* open log file if needed */
4465 if (logfilename[0] != '\0') {
4466 if (!strcmp(logfilename, "-"))
4469 logfile = fopen(logfilename, "w");
4472 if (http_server() < 0) {
4473 fprintf(stderr, "Could not start server\n");