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 == &rtp_muxer) {
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))
1890 stream_pos = parse_date(buf, 0);
1891 else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1892 int prebuffer = strtol(buf, 0, 10);
1893 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1895 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1897 strcpy(input_filename, c->stream->feed_filename);
1899 /* compute position (relative time) */
1900 if (find_info_tag(buf, sizeof(buf), "date", info))
1901 stream_pos = parse_date(buf, 1);
1905 if (input_filename[0] == '\0')
1909 { time_t when = stream_pos / 1000000;
1910 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1915 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1916 buf_size, c->stream->ap_in) < 0) {
1917 http_log("%s not found", input_filename);
1921 av_find_stream_info(c->fmt_in);
1923 /* open each parser */
1924 for(i=0;i<s->nb_streams;i++)
1927 /* choose stream as clock source (we favorize video stream if
1928 present) for packet sending */
1929 c->pts_stream_index = 0;
1930 for(i=0;i<c->stream->nb_streams;i++) {
1931 if (c->pts_stream_index == 0 &&
1932 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1933 c->pts_stream_index = i;
1938 if (c->fmt_in->iformat->read_seek)
1939 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1941 /* set the start time (needed for maxtime and RTP packet timing) */
1942 c->start_time = cur_time;
1943 c->first_pts = AV_NOPTS_VALUE;
1947 /* return the server clock (in us) */
1948 static int64_t get_server_clock(HTTPContext *c)
1950 /* compute current pts value from system time */
1951 return (cur_time - c->start_time) * 1000;
1954 /* return the estimated time at which the current packet must be sent
1956 static int64_t get_packet_send_clock(HTTPContext *c)
1958 int bytes_left, bytes_sent, frame_bytes;
1960 frame_bytes = c->cur_frame_bytes;
1961 if (frame_bytes <= 0)
1964 bytes_left = c->buffer_end - c->buffer_ptr;
1965 bytes_sent = frame_bytes - bytes_left;
1966 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1971 static int http_prepare_data(HTTPContext *c)
1974 AVFormatContext *ctx;
1976 av_freep(&c->pb_buffer);
1978 case HTTPSTATE_SEND_DATA_HEADER:
1979 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1980 av_strlcpy(c->fmt_ctx.author, c->stream->author,
1981 sizeof(c->fmt_ctx.author));
1982 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
1983 sizeof(c->fmt_ctx.comment));
1984 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
1985 sizeof(c->fmt_ctx.copyright));
1986 av_strlcpy(c->fmt_ctx.title, c->stream->title,
1987 sizeof(c->fmt_ctx.title));
1989 /* open output stream by using specified codecs */
1990 c->fmt_ctx.oformat = c->stream->fmt;
1991 c->fmt_ctx.nb_streams = c->stream->nb_streams;
1992 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1995 st = av_mallocz(sizeof(AVStream));
1996 st->codec= avcodec_alloc_context();
1997 c->fmt_ctx.streams[i] = st;
1998 /* if file or feed, then just take streams from FFStream struct */
1999 if (!c->stream->feed ||
2000 c->stream->feed == c->stream)
2001 src = c->stream->streams[i];
2003 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2007 st->codec->frame_number = 0; /* XXX: should be done in
2008 AVStream, not in codec */
2009 /* I'm pretty sure that this is not correct...
2010 * However, without it, we crash
2012 st->codec->coded_frame = &dummy_frame;
2014 c->got_key_frame = 0;
2016 /* prepare header and save header data in a stream */
2017 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2018 /* XXX: potential leak */
2021 c->fmt_ctx.pb.is_streamed = 1;
2023 av_set_parameters(&c->fmt_ctx, NULL);
2024 if (av_write_header(&c->fmt_ctx) < 0)
2027 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2028 c->buffer_ptr = c->pb_buffer;
2029 c->buffer_end = c->pb_buffer + len;
2031 c->state = HTTPSTATE_SEND_DATA;
2032 c->last_packet_sent = 0;
2034 case HTTPSTATE_SEND_DATA:
2035 /* find a new packet */
2039 /* read a packet from the input stream */
2040 if (c->stream->feed)
2041 ffm_set_write_index(c->fmt_in,
2042 c->stream->feed->feed_write_index,
2043 c->stream->feed->feed_size);
2045 if (c->stream->max_time &&
2046 c->stream->max_time + c->start_time - cur_time < 0)
2047 /* We have timed out */
2048 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2051 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2052 if (c->stream->feed && c->stream->feed->feed_opened) {
2053 /* if coming from feed, it means we reached the end of the
2054 ffm file, so must wait for more data */
2055 c->state = HTTPSTATE_WAIT_FEED;
2056 return 1; /* state changed */
2058 if (c->stream->loop) {
2059 av_close_input_file(c->fmt_in);
2061 if (open_input_stream(c, "") < 0)
2066 /* must send trailer now because eof or error */
2067 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2071 /* update first pts if needed */
2072 if (c->first_pts == AV_NOPTS_VALUE) {
2073 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2074 c->start_time = cur_time;
2076 /* send it to the appropriate stream */
2077 if (c->stream->feed) {
2078 /* if coming from a feed, select the right stream */
2079 if (c->switch_pending) {
2080 c->switch_pending = 0;
2081 for(i=0;i<c->stream->nb_streams;i++) {
2082 if (c->switch_feed_streams[i] == pkt.stream_index)
2083 if (pkt.flags & PKT_FLAG_KEY)
2084 do_switch_stream(c, i);
2085 if (c->switch_feed_streams[i] >= 0)
2086 c->switch_pending = 1;
2089 for(i=0;i<c->stream->nb_streams;i++) {
2090 if (c->feed_streams[i] == pkt.stream_index) {
2091 pkt.stream_index = i;
2092 if (pkt.flags & PKT_FLAG_KEY)
2093 c->got_key_frame |= 1 << i;
2094 /* See if we have all the key frames, then
2095 * we start to send. This logic is not quite
2096 * right, but it works for the case of a
2097 * single video stream with one or more
2098 * audio streams (for which every frame is
2099 * typically a key frame).
2101 if (!c->stream->send_on_key ||
2102 ((c->got_key_frame + 1) >> c->stream->nb_streams))
2107 AVCodecContext *codec;
2110 /* specific handling for RTP: we use several
2111 output stream (one for each RTP
2112 connection). XXX: need more abstract handling */
2113 if (c->is_packetized) {
2115 /* compute send time and duration */
2116 st = c->fmt_in->streams[pkt.stream_index];
2117 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2118 if (st->start_time != AV_NOPTS_VALUE)
2119 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2120 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2122 printf("index=%d pts=%0.3f duration=%0.6f\n",
2124 (double)c->cur_pts /
2126 (double)c->cur_frame_duration /
2129 /* find RTP context */
2130 c->packet_stream_index = pkt.stream_index;
2131 ctx = c->rtp_ctx[c->packet_stream_index];
2133 av_free_packet(&pkt);
2136 codec = ctx->streams[0]->codec;
2137 /* only one stream per RTP connection */
2138 pkt.stream_index = 0;
2142 codec = ctx->streams[pkt.stream_index]->codec;
2145 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2146 if (c->is_packetized) {
2147 int max_packet_size;
2148 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2149 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2151 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2152 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2154 ret = url_open_dyn_buf(&ctx->pb);
2157 /* XXX: potential leak */
2160 if (pkt.dts != AV_NOPTS_VALUE)
2161 pkt.dts = av_rescale_q(pkt.dts,
2162 c->fmt_in->streams[pkt.stream_index]->time_base,
2163 ctx->streams[pkt.stream_index]->time_base);
2164 if (pkt.pts != AV_NOPTS_VALUE)
2165 pkt.pts = av_rescale_q(pkt.pts,
2166 c->fmt_in->streams[pkt.stream_index]->time_base,
2167 ctx->streams[pkt.stream_index]->time_base);
2168 if (av_write_frame(ctx, &pkt))
2169 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2171 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2172 c->cur_frame_bytes = len;
2173 c->buffer_ptr = c->pb_buffer;
2174 c->buffer_end = c->pb_buffer + len;
2176 codec->frame_number++;
2180 av_free_packet(&pkt);
2186 case HTTPSTATE_SEND_DATA_TRAILER:
2187 /* last packet test ? */
2188 if (c->last_packet_sent || c->is_packetized)
2191 /* prepare header */
2192 if (url_open_dyn_buf(&ctx->pb) < 0) {
2193 /* XXX: potential leak */
2196 av_write_trailer(ctx);
2197 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2198 c->buffer_ptr = c->pb_buffer;
2199 c->buffer_end = c->pb_buffer + len;
2201 c->last_packet_sent = 1;
2207 /* should convert the format at the same time */
2208 /* send data starting at c->buffer_ptr to the output connection
2209 (either UDP or TCP connection) */
2210 static int http_send_data(HTTPContext *c)
2215 if (c->buffer_ptr >= c->buffer_end) {
2216 ret = http_prepare_data(c);
2220 /* state change requested */
2223 if (c->is_packetized) {
2224 /* RTP data output */
2225 len = c->buffer_end - c->buffer_ptr;
2227 /* fail safe - should never happen */
2229 c->buffer_ptr = c->buffer_end;
2232 len = (c->buffer_ptr[0] << 24) |
2233 (c->buffer_ptr[1] << 16) |
2234 (c->buffer_ptr[2] << 8) |
2236 if (len > (c->buffer_end - c->buffer_ptr))
2238 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2239 /* nothing to send yet: we can wait */
2243 c->data_count += len;
2244 update_datarate(&c->datarate, c->data_count);
2246 c->stream->bytes_served += len;
2248 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2249 /* RTP packets are sent inside the RTSP TCP connection */
2250 ByteIOContext pb1, *pb = &pb1;
2251 int interleaved_index, size;
2253 HTTPContext *rtsp_c;
2256 /* if no RTSP connection left, error */
2259 /* if already sending something, then wait. */
2260 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2262 if (url_open_dyn_buf(pb) < 0)
2264 interleaved_index = c->packet_stream_index * 2;
2265 /* RTCP packets are sent at odd indexes */
2266 if (c->buffer_ptr[1] == 200)
2267 interleaved_index++;
2268 /* write RTSP TCP header */
2270 header[1] = interleaved_index;
2271 header[2] = len >> 8;
2273 put_buffer(pb, header, 4);
2274 /* write RTP packet data */
2276 put_buffer(pb, c->buffer_ptr, len);
2277 size = url_close_dyn_buf(pb, &c->packet_buffer);
2278 /* prepare asynchronous TCP sending */
2279 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2280 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2281 c->buffer_ptr += len;
2283 /* send everything we can NOW */
2284 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2285 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2287 rtsp_c->packet_buffer_ptr += len;
2288 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2289 /* if we could not send all the data, we will
2290 send it later, so a new state is needed to
2291 "lock" the RTSP TCP connection */
2292 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2295 /* all data has been sent */
2296 av_freep(&c->packet_buffer);
2298 /* send RTP packet directly in UDP */
2300 url_write(c->rtp_handles[c->packet_stream_index],
2301 c->buffer_ptr, len);
2302 c->buffer_ptr += len;
2303 /* here we continue as we can send several packets per 10 ms slot */
2306 /* TCP data output */
2307 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2309 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2310 ff_neterrno() != FF_NETERROR(EINTR))
2311 /* error : close connection */
2316 c->buffer_ptr += len;
2318 c->data_count += len;
2319 update_datarate(&c->datarate, c->data_count);
2321 c->stream->bytes_served += len;
2329 static int http_start_receive_data(HTTPContext *c)
2333 if (c->stream->feed_opened)
2336 /* Don't permit writing to this one */
2337 if (c->stream->readonly)
2341 fd = open(c->stream->feed_filename, O_RDWR);
2346 c->stream->feed_write_index = ffm_read_write_index(fd);
2347 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2348 lseek(fd, 0, SEEK_SET);
2350 /* init buffer input */
2351 c->buffer_ptr = c->buffer;
2352 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2353 c->stream->feed_opened = 1;
2357 static int http_receive_data(HTTPContext *c)
2361 if (c->buffer_end > c->buffer_ptr) {
2364 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2366 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2367 ff_neterrno() != FF_NETERROR(EINTR))
2368 /* error : close connection */
2370 } else if (len == 0)
2371 /* end of connection : close it */
2374 c->buffer_ptr += len;
2375 c->data_count += len;
2376 update_datarate(&c->datarate, c->data_count);
2380 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2381 if (c->buffer[0] != 'f' ||
2382 c->buffer[1] != 'm') {
2383 http_log("Feed stream has become desynchronized -- disconnecting\n");
2388 if (c->buffer_ptr >= c->buffer_end) {
2389 FFStream *feed = c->stream;
2390 /* a packet has been received : write it in the store, except
2392 if (c->data_count > FFM_PACKET_SIZE) {
2394 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2395 /* XXX: use llseek or url_seek */
2396 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2397 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2399 feed->feed_write_index += FFM_PACKET_SIZE;
2400 /* update file size */
2401 if (feed->feed_write_index > c->stream->feed_size)
2402 feed->feed_size = feed->feed_write_index;
2404 /* handle wrap around if max file size reached */
2405 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2406 feed->feed_write_index = FFM_PACKET_SIZE;
2409 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2411 /* wake up any waiting connections */
2412 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2413 if (c1->state == HTTPSTATE_WAIT_FEED &&
2414 c1->stream->feed == c->stream->feed)
2415 c1->state = HTTPSTATE_SEND_DATA;
2418 /* We have a header in our hands that contains useful data */
2420 AVInputFormat *fmt_in;
2421 ByteIOContext *pb = &s.pb;
2424 memset(&s, 0, sizeof(s));
2426 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2427 pb->buf_end = c->buffer_end; /* ?? */
2428 pb->is_streamed = 1;
2430 /* use feed output format name to find corresponding input format */
2431 fmt_in = av_find_input_format(feed->fmt->name);
2435 if (fmt_in->priv_data_size > 0) {
2436 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2442 if (fmt_in->read_header(&s, 0) < 0) {
2443 av_freep(&s.priv_data);
2447 /* Now we have the actual streams */
2448 if (s.nb_streams != feed->nb_streams) {
2449 av_freep(&s.priv_data);
2452 for (i = 0; i < s.nb_streams; i++)
2453 memcpy(feed->streams[i]->codec,
2454 s.streams[i]->codec, sizeof(AVCodecContext));
2455 av_freep(&s.priv_data);
2457 c->buffer_ptr = c->buffer;
2462 c->stream->feed_opened = 0;
2467 /********************************************************************/
2470 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2477 switch(error_number) {
2478 case RTSP_STATUS_OK:
2481 case RTSP_STATUS_METHOD:
2482 str = "Method Not Allowed";
2484 case RTSP_STATUS_BANDWIDTH:
2485 str = "Not Enough Bandwidth";
2487 case RTSP_STATUS_SESSION:
2488 str = "Session Not Found";
2490 case RTSP_STATUS_STATE:
2491 str = "Method Not Valid in This State";
2493 case RTSP_STATUS_AGGREGATE:
2494 str = "Aggregate operation not allowed";
2496 case RTSP_STATUS_ONLY_AGGREGATE:
2497 str = "Only aggregate operation allowed";
2499 case RTSP_STATUS_TRANSPORT:
2500 str = "Unsupported transport";
2502 case RTSP_STATUS_INTERNAL:
2503 str = "Internal Server Error";
2505 case RTSP_STATUS_SERVICE:
2506 str = "Service Unavailable";
2508 case RTSP_STATUS_VERSION:
2509 str = "RTSP Version not supported";
2512 str = "Unknown Error";
2516 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2517 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2519 /* output GMT time */
2523 p = buf2 + strlen(p) - 1;
2526 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2529 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2531 rtsp_reply_header(c, error_number);
2532 url_fprintf(c->pb, "\r\n");
2535 static int rtsp_parse_request(HTTPContext *c)
2537 const char *p, *p1, *p2;
2544 RTSPHeader header1, *header = &header1;
2546 c->buffer_ptr[0] = '\0';
2549 get_word(cmd, sizeof(cmd), &p);
2550 get_word(url, sizeof(url), &p);
2551 get_word(protocol, sizeof(protocol), &p);
2553 av_strlcpy(c->method, cmd, sizeof(c->method));
2554 av_strlcpy(c->url, url, sizeof(c->url));
2555 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2558 if (url_open_dyn_buf(c->pb) < 0) {
2559 /* XXX: cannot do more */
2560 c->pb = NULL; /* safety */
2564 /* check version name */
2565 if (strcmp(protocol, "RTSP/1.0") != 0) {
2566 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2570 /* parse each header line */
2571 memset(header, 0, sizeof(RTSPHeader));
2572 /* skip to next line */
2573 while (*p != '\n' && *p != '\0')
2577 while (*p != '\0') {
2578 p1 = strchr(p, '\n');
2582 if (p2 > p && p2[-1] == '\r')
2584 /* skip empty line */
2588 if (len > sizeof(line) - 1)
2589 len = sizeof(line) - 1;
2590 memcpy(line, p, len);
2592 rtsp_parse_line(header, line);
2596 /* handle sequence number */
2597 c->seq = header->seq;
2599 if (!strcmp(cmd, "DESCRIBE"))
2600 rtsp_cmd_describe(c, url);
2601 else if (!strcmp(cmd, "OPTIONS"))
2602 rtsp_cmd_options(c, url);
2603 else if (!strcmp(cmd, "SETUP"))
2604 rtsp_cmd_setup(c, url, header);
2605 else if (!strcmp(cmd, "PLAY"))
2606 rtsp_cmd_play(c, url, header);
2607 else if (!strcmp(cmd, "PAUSE"))
2608 rtsp_cmd_pause(c, url, header);
2609 else if (!strcmp(cmd, "TEARDOWN"))
2610 rtsp_cmd_teardown(c, url, header);
2612 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2615 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2616 c->pb = NULL; /* safety */
2618 /* XXX: cannot do more */
2621 c->buffer_ptr = c->pb_buffer;
2622 c->buffer_end = c->pb_buffer + len;
2623 c->state = RTSPSTATE_SEND_REPLY;
2627 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2628 struct in_addr my_ip)
2630 AVFormatContext *avc;
2631 AVStream avs[MAX_STREAMS];
2634 avc = av_alloc_format_context();
2638 if (stream->title[0] != 0) {
2639 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2641 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2643 avc->nb_streams = stream->nb_streams;
2644 if (stream->is_multicast) {
2645 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2646 inet_ntoa(stream->multicast_ip),
2647 stream->multicast_port, stream->multicast_ttl);
2650 for(i = 0; i < stream->nb_streams; i++) {
2651 avc->streams[i] = &avs[i];
2652 avc->streams[i]->codec = stream->streams[i]->codec;
2654 *pbuffer = av_mallocz(2048);
2655 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2658 return strlen(*pbuffer);
2661 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2663 // rtsp_reply_header(c, RTSP_STATUS_OK);
2664 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2665 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2666 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2667 url_fprintf(c->pb, "\r\n");
2670 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2676 int content_length, len;
2677 struct sockaddr_in my_addr;
2679 /* find which url is asked */
2680 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2685 for(stream = first_stream; stream != NULL; stream = stream->next) {
2686 if (!stream->is_feed && stream->fmt == &rtp_muxer &&
2687 !strcmp(path, stream->filename)) {
2691 /* no stream found */
2692 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2696 /* prepare the media description in sdp format */
2698 /* get the host IP */
2699 len = sizeof(my_addr);
2700 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2701 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2702 if (content_length < 0) {
2703 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2706 rtsp_reply_header(c, RTSP_STATUS_OK);
2707 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2708 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2709 url_fprintf(c->pb, "\r\n");
2710 put_buffer(c->pb, content, content_length);
2713 static HTTPContext *find_rtp_session(const char *session_id)
2717 if (session_id[0] == '\0')
2720 for(c = first_http_ctx; c != NULL; c = c->next) {
2721 if (!strcmp(c->session_id, session_id))
2727 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2729 RTSPTransportField *th;
2732 for(i=0;i<h->nb_transports;i++) {
2733 th = &h->transports[i];
2734 if (th->protocol == protocol)
2740 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2744 int stream_index, port;
2749 RTSPTransportField *th;
2750 struct sockaddr_in dest_addr;
2751 RTSPActionServerSetup setup;
2753 /* find which url is asked */
2754 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2759 /* now check each stream */
2760 for(stream = first_stream; stream != NULL; stream = stream->next) {
2761 if (!stream->is_feed && stream->fmt == &rtp_muxer) {
2762 /* accept aggregate filenames only if single stream */
2763 if (!strcmp(path, stream->filename)) {
2764 if (stream->nb_streams != 1) {
2765 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2772 for(stream_index = 0; stream_index < stream->nb_streams;
2774 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2775 stream->filename, stream_index);
2776 if (!strcmp(path, buf))
2781 /* no stream found */
2782 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2786 /* generate session id if needed */
2787 if (h->session_id[0] == '\0')
2788 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2789 av_random(&random_state), av_random(&random_state));
2791 /* find rtp session, and create it if none found */
2792 rtp_c = find_rtp_session(h->session_id);
2794 /* always prefer UDP */
2795 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2797 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2799 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2804 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2807 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2811 /* open input stream */
2812 if (open_input_stream(rtp_c, "") < 0) {
2813 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2818 /* test if stream is OK (test needed because several SETUP needs
2819 to be done for a given file) */
2820 if (rtp_c->stream != stream) {
2821 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2825 /* test if stream is already set up */
2826 if (rtp_c->rtp_ctx[stream_index]) {
2827 rtsp_reply_error(c, RTSP_STATUS_STATE);
2831 /* check transport */
2832 th = find_transport(h, rtp_c->rtp_protocol);
2833 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2834 th->client_port_min <= 0)) {
2835 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2839 /* setup default options */
2840 setup.transport_option[0] = '\0';
2841 dest_addr = rtp_c->from_addr;
2842 dest_addr.sin_port = htons(th->client_port_min);
2845 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2846 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2850 /* now everything is OK, so we can send the connection parameters */
2851 rtsp_reply_header(c, RTSP_STATUS_OK);
2853 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2855 switch(rtp_c->rtp_protocol) {
2856 case RTSP_PROTOCOL_RTP_UDP:
2857 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2858 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2859 "client_port=%d-%d;server_port=%d-%d",
2860 th->client_port_min, th->client_port_min + 1,
2863 case RTSP_PROTOCOL_RTP_TCP:
2864 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2865 stream_index * 2, stream_index * 2 + 1);
2870 if (setup.transport_option[0] != '\0')
2871 url_fprintf(c->pb, ";%s", setup.transport_option);
2872 url_fprintf(c->pb, "\r\n");
2875 url_fprintf(c->pb, "\r\n");
2879 /* find an rtp connection by using the session ID. Check consistency
2881 static HTTPContext *find_rtp_session_with_url(const char *url,
2882 const char *session_id)
2890 rtp_c = find_rtp_session(session_id);
2894 /* find which url is asked */
2895 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2899 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2900 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2901 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2902 rtp_c->stream->filename, s);
2903 if(!strncmp(path, buf, sizeof(buf))) {
2904 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2911 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2915 rtp_c = find_rtp_session_with_url(url, h->session_id);
2917 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2921 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2922 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2923 rtp_c->state != HTTPSTATE_READY) {
2924 rtsp_reply_error(c, RTSP_STATUS_STATE);
2929 /* XXX: seek in stream */
2930 if (h->range_start != AV_NOPTS_VALUE) {
2931 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2932 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2936 rtp_c->state = HTTPSTATE_SEND_DATA;
2938 /* now everything is OK, so we can send the connection parameters */
2939 rtsp_reply_header(c, RTSP_STATUS_OK);
2941 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2942 url_fprintf(c->pb, "\r\n");
2945 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2949 rtp_c = find_rtp_session_with_url(url, h->session_id);
2951 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2955 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2956 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2957 rtsp_reply_error(c, RTSP_STATUS_STATE);
2961 rtp_c->state = HTTPSTATE_READY;
2962 rtp_c->first_pts = AV_NOPTS_VALUE;
2963 /* now everything is OK, so we can send the connection parameters */
2964 rtsp_reply_header(c, RTSP_STATUS_OK);
2966 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2967 url_fprintf(c->pb, "\r\n");
2970 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
2973 char session_id[32];
2975 rtp_c = find_rtp_session_with_url(url, h->session_id);
2977 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2981 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
2983 /* abort the session */
2984 close_connection(rtp_c);
2986 /* now everything is OK, so we can send the connection parameters */
2987 rtsp_reply_header(c, RTSP_STATUS_OK);
2989 url_fprintf(c->pb, "Session: %s\r\n", session_id);
2990 url_fprintf(c->pb, "\r\n");
2994 /********************************************************************/
2997 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
2998 FFStream *stream, const char *session_id,
2999 enum RTSPProtocol rtp_protocol)
3001 HTTPContext *c = NULL;
3002 const char *proto_str;
3004 /* XXX: should output a warning page when coming
3005 close to the connection limit */
3006 if (nb_connections >= nb_max_connections)
3009 /* add a new connection */
3010 c = av_mallocz(sizeof(HTTPContext));
3015 c->poll_entry = NULL;
3016 c->from_addr = *from_addr;
3017 c->buffer_size = IOBUFFER_INIT_SIZE;
3018 c->buffer = av_malloc(c->buffer_size);
3023 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3024 c->state = HTTPSTATE_READY;
3025 c->is_packetized = 1;
3026 c->rtp_protocol = rtp_protocol;
3028 /* protocol is shown in statistics */
3029 switch(c->rtp_protocol) {
3030 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3031 proto_str = "MCAST";
3033 case RTSP_PROTOCOL_RTP_UDP:
3036 case RTSP_PROTOCOL_RTP_TCP:
3043 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3044 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3046 current_bandwidth += stream->bandwidth;
3048 c->next = first_http_ctx;
3060 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3061 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3063 static int rtp_new_av_stream(HTTPContext *c,
3064 int stream_index, struct sockaddr_in *dest_addr,
3065 HTTPContext *rtsp_c)
3067 AVFormatContext *ctx;
3073 int max_packet_size;
3075 /* now we can open the relevant output stream */
3076 ctx = av_alloc_format_context();
3079 ctx->oformat = &rtp_muxer;
3081 st = av_mallocz(sizeof(AVStream));
3084 st->codec= avcodec_alloc_context();
3085 ctx->nb_streams = 1;
3086 ctx->streams[0] = st;
3088 if (!c->stream->feed ||
3089 c->stream->feed == c->stream)
3090 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3093 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3095 st->priv_data = NULL;
3097 /* build destination RTP address */
3098 ipaddr = inet_ntoa(dest_addr->sin_addr);
3100 switch(c->rtp_protocol) {
3101 case RTSP_PROTOCOL_RTP_UDP:
3102 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3105 /* XXX: also pass as parameter to function ? */
3106 if (c->stream->is_multicast) {
3108 ttl = c->stream->multicast_ttl;
3111 snprintf(ctx->filename, sizeof(ctx->filename),
3112 "rtp://%s:%d?multicast=1&ttl=%d",
3113 ipaddr, ntohs(dest_addr->sin_port), ttl);
3115 snprintf(ctx->filename, sizeof(ctx->filename),
3116 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3119 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3121 c->rtp_handles[stream_index] = h;
3122 max_packet_size = url_get_max_packet_size(h);
3124 case RTSP_PROTOCOL_RTP_TCP:
3127 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3133 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3134 ipaddr, ntohs(dest_addr->sin_port),
3136 c->stream->filename, stream_index, c->protocol);
3138 /* normally, no packets should be output here, but the packet size may be checked */
3139 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3140 /* XXX: close stream */
3143 av_set_parameters(ctx, NULL);
3144 if (av_write_header(ctx) < 0) {
3151 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3154 c->rtp_ctx[stream_index] = ctx;
3158 /********************************************************************/
3159 /* ffserver initialization */
3161 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3165 fst = av_mallocz(sizeof(AVStream));
3168 fst->codec= avcodec_alloc_context();
3169 fst->priv_data = av_mallocz(sizeof(FeedData));
3170 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3171 fst->codec->coded_frame = &dummy_frame;
3172 fst->index = stream->nb_streams;
3173 av_set_pts_info(fst, 33, 1, 90000);
3174 stream->streams[stream->nb_streams++] = fst;
3178 /* return the stream number in the feed */
3179 static int add_av_stream(FFStream *feed, AVStream *st)
3182 AVCodecContext *av, *av1;
3186 for(i=0;i<feed->nb_streams;i++) {
3187 st = feed->streams[i];
3189 if (av1->codec_id == av->codec_id &&
3190 av1->codec_type == av->codec_type &&
3191 av1->bit_rate == av->bit_rate) {
3193 switch(av->codec_type) {
3194 case CODEC_TYPE_AUDIO:
3195 if (av1->channels == av->channels &&
3196 av1->sample_rate == av->sample_rate)
3199 case CODEC_TYPE_VIDEO:
3200 if (av1->width == av->width &&
3201 av1->height == av->height &&
3202 av1->time_base.den == av->time_base.den &&
3203 av1->time_base.num == av->time_base.num &&
3204 av1->gop_size == av->gop_size)
3213 fst = add_av_stream1(feed, av);
3216 return feed->nb_streams - 1;
3221 static void remove_stream(FFStream *stream)
3225 while (*ps != NULL) {
3233 /* specific mpeg4 handling : we extract the raw parameters */
3234 static void extract_mpeg4_header(AVFormatContext *infile)
3236 int mpeg4_count, i, size;
3242 for(i=0;i<infile->nb_streams;i++) {
3243 st = infile->streams[i];
3244 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3245 st->codec->extradata_size == 0) {
3252 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3253 while (mpeg4_count > 0) {
3254 if (av_read_packet(infile, &pkt) < 0)
3256 st = infile->streams[pkt.stream_index];
3257 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3258 st->codec->extradata_size == 0) {
3259 av_freep(&st->codec->extradata);
3260 /* fill extradata with the header */
3261 /* XXX: we make hard suppositions here ! */
3263 while (p < pkt.data + pkt.size - 4) {
3264 /* stop when vop header is found */
3265 if (p[0] == 0x00 && p[1] == 0x00 &&
3266 p[2] == 0x01 && p[3] == 0xb6) {
3267 size = p - pkt.data;
3268 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3269 st->codec->extradata = av_malloc(size);
3270 st->codec->extradata_size = size;
3271 memcpy(st->codec->extradata, pkt.data, size);
3278 av_free_packet(&pkt);
3282 /* compute the needed AVStream for each file */
3283 static void build_file_streams(void)
3285 FFStream *stream, *stream_next;
3286 AVFormatContext *infile;
3289 /* gather all streams */
3290 for(stream = first_stream; stream != NULL; stream = stream_next) {
3291 stream_next = stream->next;
3292 if (stream->stream_type == STREAM_TYPE_LIVE &&
3294 /* the stream comes from a file */
3295 /* try to open the file */
3297 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3298 if (stream->fmt == &rtp_muxer) {
3299 /* specific case : if transport stream output to RTP,
3300 we use a raw transport stream reader */
3301 stream->ap_in->mpeg2ts_raw = 1;
3302 stream->ap_in->mpeg2ts_compute_pcr = 1;
3305 if (av_open_input_file(&infile, stream->feed_filename,
3306 stream->ifmt, 0, stream->ap_in) < 0) {
3307 http_log("%s not found", stream->feed_filename);
3308 /* remove stream (no need to spend more time on it) */
3310 remove_stream(stream);
3312 /* find all the AVStreams inside and reference them in
3314 if (av_find_stream_info(infile) < 0) {
3315 http_log("Could not find codec parameters from '%s'",
3316 stream->feed_filename);
3317 av_close_input_file(infile);
3320 extract_mpeg4_header(infile);
3322 for(i=0;i<infile->nb_streams;i++)
3323 add_av_stream1(stream, infile->streams[i]->codec);
3325 av_close_input_file(infile);
3331 /* compute the needed AVStream for each feed */
3332 static void build_feed_streams(void)
3334 FFStream *stream, *feed;
3337 /* gather all streams */
3338 for(stream = first_stream; stream != NULL; stream = stream->next) {
3339 feed = stream->feed;
3341 if (!stream->is_feed) {
3342 /* we handle a stream coming from a feed */
3343 for(i=0;i<stream->nb_streams;i++)
3344 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3349 /* gather all streams */
3350 for(stream = first_stream; stream != NULL; stream = stream->next) {
3351 feed = stream->feed;
3353 if (stream->is_feed) {
3354 for(i=0;i<stream->nb_streams;i++)
3355 stream->feed_streams[i] = i;
3360 /* create feed files if needed */
3361 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3364 if (url_exist(feed->feed_filename)) {
3365 /* See if it matches */
3369 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3370 /* Now see if it matches */
3371 if (s->nb_streams == feed->nb_streams) {
3373 for(i=0;i<s->nb_streams;i++) {
3375 sf = feed->streams[i];
3378 if (sf->index != ss->index ||
3380 printf("Index & Id do not match for stream %d (%s)\n",
3381 i, feed->feed_filename);
3384 AVCodecContext *ccf, *ccs;
3388 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3390 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3391 printf("Codecs do not match for stream %d\n", i);
3393 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3394 printf("Codec bitrates do not match for stream %d\n", i);
3396 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3397 if (CHECK_CODEC(time_base.den) ||
3398 CHECK_CODEC(time_base.num) ||
3399 CHECK_CODEC(width) ||
3400 CHECK_CODEC(height)) {
3401 printf("Codec width, height and framerate do not match for stream %d\n", i);
3404 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3405 if (CHECK_CODEC(sample_rate) ||
3406 CHECK_CODEC(channels) ||
3407 CHECK_CODEC(frame_size)) {
3408 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3412 printf("Unknown codec type\n");
3420 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3421 feed->feed_filename, s->nb_streams, feed->nb_streams);
3423 av_close_input_file(s);
3425 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3426 feed->feed_filename);
3429 if (feed->readonly) {
3430 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3431 feed->feed_filename);
3434 unlink(feed->feed_filename);
3437 if (!url_exist(feed->feed_filename)) {
3438 AVFormatContext s1, *s = &s1;
3440 if (feed->readonly) {
3441 printf("Unable to create feed file '%s' as it is marked readonly\n",
3442 feed->feed_filename);
3446 /* only write the header of the ffm file */
3447 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3448 fprintf(stderr, "Could not open output feed file '%s'\n",
3449 feed->feed_filename);
3452 s->oformat = feed->fmt;
3453 s->nb_streams = feed->nb_streams;
3454 for(i=0;i<s->nb_streams;i++) {
3456 st = feed->streams[i];
3459 av_set_parameters(s, NULL);
3460 if (av_write_header(s) < 0) {
3461 fprintf(stderr, "Container doesn't supports the required parameters\n");
3464 /* XXX: need better api */
3465 av_freep(&s->priv_data);
3468 /* get feed size and write index */
3469 fd = open(feed->feed_filename, O_RDONLY);
3471 fprintf(stderr, "Could not open output feed file '%s'\n",
3472 feed->feed_filename);
3476 feed->feed_write_index = ffm_read_write_index(fd);
3477 feed->feed_size = lseek(fd, 0, SEEK_END);
3478 /* ensure that we do not wrap before the end of file */
3479 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3480 feed->feed_max_size = feed->feed_size;
3486 /* compute the bandwidth used by each stream */
3487 static void compute_bandwidth(void)
3492 for(stream = first_stream; stream != NULL; stream = stream->next) {
3494 for(i=0;i<stream->nb_streams;i++) {
3495 AVStream *st = stream->streams[i];
3496 switch(st->codec->codec_type) {
3497 case CODEC_TYPE_AUDIO:
3498 case CODEC_TYPE_VIDEO:
3499 bandwidth += st->codec->bit_rate;
3505 stream->bandwidth = (bandwidth + 999) / 1000;
3509 static void get_arg(char *buf, int buf_size, const char **pp)
3516 while (isspace(*p)) p++;
3519 if (*p == '\"' || *p == '\'')
3531 if ((q - buf) < buf_size - 1)
3536 if (quote && *p == quote)
3541 /* add a codec and set the default parameters */
3542 static void add_codec(FFStream *stream, AVCodecContext *av)
3546 /* compute default parameters */
3547 switch(av->codec_type) {
3548 case CODEC_TYPE_AUDIO:
3549 if (av->bit_rate == 0)
3550 av->bit_rate = 64000;
3551 if (av->sample_rate == 0)
3552 av->sample_rate = 22050;
3553 if (av->channels == 0)
3556 case CODEC_TYPE_VIDEO:
3557 if (av->bit_rate == 0)
3558 av->bit_rate = 64000;
3559 if (av->time_base.num == 0){
3560 av->time_base.den = 5;
3561 av->time_base.num = 1;
3563 if (av->width == 0 || av->height == 0) {
3567 /* Bitrate tolerance is less for streaming */
3568 if (av->bit_rate_tolerance == 0)
3569 av->bit_rate_tolerance = av->bit_rate / 4;
3574 if (av->max_qdiff == 0)
3576 av->qcompress = 0.5;
3579 if (!av->nsse_weight)
3580 av->nsse_weight = 8;
3582 av->frame_skip_cmp = FF_CMP_DCTMAX;
3583 av->me_method = ME_EPZS;
3584 av->rc_buffer_aggressivity = 1.0;
3587 av->rc_eq = "tex^qComp";
3588 if (!av->i_quant_factor)
3589 av->i_quant_factor = -0.8;
3590 if (!av->b_quant_factor)
3591 av->b_quant_factor = 1.25;
3592 if (!av->b_quant_offset)
3593 av->b_quant_offset = 1.25;
3594 if (!av->rc_max_rate)
3595 av->rc_max_rate = av->bit_rate * 2;
3597 if (av->rc_max_rate && !av->rc_buffer_size) {
3598 av->rc_buffer_size = av->rc_max_rate;
3607 st = av_mallocz(sizeof(AVStream));
3610 st->codec = avcodec_alloc_context();
3611 stream->streams[stream->nb_streams++] = st;
3612 memcpy(st->codec, av, sizeof(AVCodecContext));
3615 static int opt_audio_codec(const char *arg)
3621 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3626 return CODEC_ID_NONE;
3631 static int opt_video_codec(const char *arg)
3637 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3642 return CODEC_ID_NONE;
3647 /* simplistic plugin support */
3650 static void load_module(const char *filename)
3653 void (*init_func)(void);
3654 dll = dlopen(filename, RTLD_NOW);
3656 fprintf(stderr, "Could not load module '%s' - %s\n",
3657 filename, dlerror());
3661 init_func = dlsym(dll, "ffserver_module_init");
3664 "%s: init function 'ffserver_module_init()' not found\n",
3673 static int parse_ffconfig(const char *filename)
3680 int val, errors, line_num;
3681 FFStream **last_stream, *stream, *redirect;
3682 FFStream **last_feed, *feed;
3683 AVCodecContext audio_enc, video_enc;
3684 int audio_id, video_id;
3686 f = fopen(filename, "r");
3694 first_stream = NULL;
3695 last_stream = &first_stream;
3697 last_feed = &first_feed;
3701 audio_id = CODEC_ID_NONE;
3702 video_id = CODEC_ID_NONE;
3704 if (fgets(line, sizeof(line), f) == NULL)
3710 if (*p == '\0' || *p == '#')
3713 get_arg(cmd, sizeof(cmd), &p);
3715 if (!strcasecmp(cmd, "Port")) {
3716 get_arg(arg, sizeof(arg), &p);
3718 if (val < 1 || val > 65536) {
3719 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3720 filename, line_num, arg);
3723 my_http_addr.sin_port = htons(val);
3724 } else if (!strcasecmp(cmd, "BindAddress")) {
3725 get_arg(arg, sizeof(arg), &p);
3726 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3727 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3728 filename, line_num, arg);
3731 } else if (!strcasecmp(cmd, "NoDaemon")) {
3732 ffserver_daemon = 0;
3733 } else if (!strcasecmp(cmd, "RTSPPort")) {
3734 get_arg(arg, sizeof(arg), &p);
3736 if (val < 1 || val > 65536) {
3737 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3738 filename, line_num, arg);
3741 my_rtsp_addr.sin_port = htons(atoi(arg));
3742 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3743 get_arg(arg, sizeof(arg), &p);
3744 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3745 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3746 filename, line_num, arg);
3749 } else if (!strcasecmp(cmd, "MaxClients")) {
3750 get_arg(arg, sizeof(arg), &p);
3752 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3753 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3754 filename, line_num, arg);
3757 nb_max_connections = val;
3759 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3760 get_arg(arg, sizeof(arg), &p);
3762 if (val < 10 || val > 100000) {
3763 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3764 filename, line_num, arg);
3767 max_bandwidth = val;
3768 } else if (!strcasecmp(cmd, "CustomLog")) {
3769 get_arg(logfilename, sizeof(logfilename), &p);
3770 } else if (!strcasecmp(cmd, "<Feed")) {
3771 /*********************************************/
3772 /* Feed related options */
3774 if (stream || feed) {
3775 fprintf(stderr, "%s:%d: Already in a tag\n",
3776 filename, line_num);
3778 feed = av_mallocz(sizeof(FFStream));
3779 /* add in stream list */
3780 *last_stream = feed;
3781 last_stream = &feed->next;
3782 /* add in feed list */
3784 last_feed = &feed->next_feed;
3786 get_arg(feed->filename, sizeof(feed->filename), &p);
3787 q = strrchr(feed->filename, '>');
3790 feed->fmt = guess_format("ffm", NULL, NULL);
3791 /* defaut feed file */
3792 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3793 "/tmp/%s.ffm", feed->filename);
3794 feed->feed_max_size = 5 * 1024 * 1024;
3796 feed->feed = feed; /* self feeding :-) */
3798 } else if (!strcasecmp(cmd, "Launch")) {
3802 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3804 for (i = 0; i < 62; i++) {
3805 get_arg(arg, sizeof(arg), &p);
3809 feed->child_argv[i] = av_strdup(arg);
3812 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3814 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3816 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3817 inet_ntoa(my_http_addr.sin_addr),
3818 ntohs(my_http_addr.sin_port), feed->filename);
3823 fprintf(stdout, "Launch commandline: ");
3824 for (j = 0; j <= i; j++)
3825 fprintf(stdout, "%s ", feed->child_argv[j]);
3826 fprintf(stdout, "\n");
3829 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3831 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3833 } else if (stream) {
3834 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3836 } else if (!strcasecmp(cmd, "File")) {
3838 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3840 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3841 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3846 get_arg(arg, sizeof(arg), &p);
3848 fsize = strtod(p1, (char **)&p1);
3849 switch(toupper(*p1)) {
3854 fsize *= 1024 * 1024;
3857 fsize *= 1024 * 1024 * 1024;
3860 feed->feed_max_size = (int64_t)fsize;
3862 } else if (!strcasecmp(cmd, "</Feed>")) {
3864 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3865 filename, line_num);
3869 } else if (!strcasecmp(cmd, "<Stream")) {
3870 /*********************************************/
3871 /* Stream related options */
3873 if (stream || feed) {
3874 fprintf(stderr, "%s:%d: Already in a tag\n",
3875 filename, line_num);
3877 stream = av_mallocz(sizeof(FFStream));
3878 *last_stream = stream;
3879 last_stream = &stream->next;
3881 get_arg(stream->filename, sizeof(stream->filename), &p);
3882 q = strrchr(stream->filename, '>');
3885 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3886 memset(&audio_enc, 0, sizeof(AVCodecContext));
3887 memset(&video_enc, 0, sizeof(AVCodecContext));
3888 audio_id = CODEC_ID_NONE;
3889 video_id = CODEC_ID_NONE;
3891 audio_id = stream->fmt->audio_codec;
3892 video_id = stream->fmt->video_codec;
3895 } else if (!strcasecmp(cmd, "Feed")) {
3896 get_arg(arg, sizeof(arg), &p);
3901 while (sfeed != NULL) {
3902 if (!strcmp(sfeed->filename, arg))
3904 sfeed = sfeed->next_feed;
3907 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3908 filename, line_num, arg);
3910 stream->feed = sfeed;
3912 } else if (!strcasecmp(cmd, "Format")) {
3913 get_arg(arg, sizeof(arg), &p);
3914 if (!strcmp(arg, "status")) {
3915 stream->stream_type = STREAM_TYPE_STATUS;
3918 stream->stream_type = STREAM_TYPE_LIVE;
3919 /* jpeg cannot be used here, so use single frame jpeg */
3920 if (!strcmp(arg, "jpeg"))
3921 strcpy(arg, "mjpeg");
3922 stream->fmt = guess_stream_format(arg, NULL, NULL);
3924 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3925 filename, line_num, arg);
3930 audio_id = stream->fmt->audio_codec;
3931 video_id = stream->fmt->video_codec;
3933 } else if (!strcasecmp(cmd, "InputFormat")) {
3934 get_arg(arg, sizeof(arg), &p);
3935 stream->ifmt = av_find_input_format(arg);
3936 if (!stream->ifmt) {
3937 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
3938 filename, line_num, arg);
3940 } else if (!strcasecmp(cmd, "FaviconURL")) {
3941 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3942 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3944 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3945 filename, line_num);
3948 } else if (!strcasecmp(cmd, "Author")) {
3950 get_arg(stream->author, sizeof(stream->author), &p);
3951 } else if (!strcasecmp(cmd, "Comment")) {
3953 get_arg(stream->comment, sizeof(stream->comment), &p);
3954 } else if (!strcasecmp(cmd, "Copyright")) {
3956 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3957 } else if (!strcasecmp(cmd, "Title")) {
3959 get_arg(stream->title, sizeof(stream->title), &p);
3960 } else if (!strcasecmp(cmd, "Preroll")) {
3961 get_arg(arg, sizeof(arg), &p);
3963 stream->prebuffer = atof(arg) * 1000;
3964 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3966 stream->send_on_key = 1;
3967 } else if (!strcasecmp(cmd, "AudioCodec")) {
3968 get_arg(arg, sizeof(arg), &p);
3969 audio_id = opt_audio_codec(arg);
3970 if (audio_id == CODEC_ID_NONE) {
3971 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3972 filename, line_num, arg);
3975 } else if (!strcasecmp(cmd, "VideoCodec")) {
3976 get_arg(arg, sizeof(arg), &p);
3977 video_id = opt_video_codec(arg);
3978 if (video_id == CODEC_ID_NONE) {
3979 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3980 filename, line_num, arg);
3983 } else if (!strcasecmp(cmd, "MaxTime")) {
3984 get_arg(arg, sizeof(arg), &p);
3986 stream->max_time = atof(arg) * 1000;
3987 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3988 get_arg(arg, sizeof(arg), &p);
3990 audio_enc.bit_rate = atoi(arg) * 1000;
3991 } else if (!strcasecmp(cmd, "AudioChannels")) {
3992 get_arg(arg, sizeof(arg), &p);
3994 audio_enc.channels = atoi(arg);
3995 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3996 get_arg(arg, sizeof(arg), &p);
3998 audio_enc.sample_rate = atoi(arg);
3999 } else if (!strcasecmp(cmd, "AudioQuality")) {
4000 get_arg(arg, sizeof(arg), &p);
4002 // audio_enc.quality = atof(arg) * 1000;
4004 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4006 int minrate, maxrate;
4008 get_arg(arg, sizeof(arg), &p);
4010 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4011 video_enc.rc_min_rate = minrate * 1000;
4012 video_enc.rc_max_rate = maxrate * 1000;
4014 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4015 filename, line_num, arg);
4019 } else if (!strcasecmp(cmd, "Debug")) {
4021 get_arg(arg, sizeof(arg), &p);
4022 video_enc.debug = strtol(arg,0,0);
4024 } else if (!strcasecmp(cmd, "Strict")) {
4026 get_arg(arg, sizeof(arg), &p);
4027 video_enc.strict_std_compliance = atoi(arg);
4029 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4031 get_arg(arg, sizeof(arg), &p);
4032 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4034 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4036 get_arg(arg, sizeof(arg), &p);
4037 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4039 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4040 get_arg(arg, sizeof(arg), &p);
4042 video_enc.bit_rate = atoi(arg) * 1000;
4044 } else if (!strcasecmp(cmd, "VideoSize")) {
4045 get_arg(arg, sizeof(arg), &p);
4047 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4048 if ((video_enc.width % 16) != 0 ||
4049 (video_enc.height % 16) != 0) {
4050 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4051 filename, line_num);
4055 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4056 get_arg(arg, sizeof(arg), &p);
4058 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4059 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4061 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4062 get_arg(arg, sizeof(arg), &p);
4064 video_enc.gop_size = atoi(arg);
4065 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4067 video_enc.gop_size = 1;
4068 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4070 video_enc.mb_decision = FF_MB_DECISION_BITS;
4071 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4073 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4074 video_enc.flags |= CODEC_FLAG_4MV;
4076 } else if (!strcasecmp(cmd, "VideoTag")) {
4077 get_arg(arg, sizeof(arg), &p);
4078 if ((strlen(arg) == 4) && stream)
4079 video_enc.codec_tag = ff_get_fourcc(arg);
4080 } else if (!strcasecmp(cmd, "BitExact")) {
4082 video_enc.flags |= CODEC_FLAG_BITEXACT;
4083 } else if (!strcasecmp(cmd, "DctFastint")) {
4085 video_enc.dct_algo = FF_DCT_FASTINT;
4086 } else if (!strcasecmp(cmd, "IdctSimple")) {
4088 video_enc.idct_algo = FF_IDCT_SIMPLE;
4089 } else if (!strcasecmp(cmd, "Qscale")) {
4090 get_arg(arg, sizeof(arg), &p);
4092 video_enc.flags |= CODEC_FLAG_QSCALE;
4093 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4095 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4096 get_arg(arg, sizeof(arg), &p);
4098 video_enc.max_qdiff = atoi(arg);
4099 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4100 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4101 filename, line_num);
4105 } else if (!strcasecmp(cmd, "VideoQMax")) {
4106 get_arg(arg, sizeof(arg), &p);
4108 video_enc.qmax = atoi(arg);
4109 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4110 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4111 filename, line_num);
4115 } else if (!strcasecmp(cmd, "VideoQMin")) {
4116 get_arg(arg, sizeof(arg), &p);
4118 video_enc.qmin = atoi(arg);
4119 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4120 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4121 filename, line_num);
4125 } else if (!strcasecmp(cmd, "LumaElim")) {
4126 get_arg(arg, sizeof(arg), &p);
4128 video_enc.luma_elim_threshold = atoi(arg);
4129 } else if (!strcasecmp(cmd, "ChromaElim")) {
4130 get_arg(arg, sizeof(arg), &p);
4132 video_enc.chroma_elim_threshold = atoi(arg);
4133 } else if (!strcasecmp(cmd, "LumiMask")) {
4134 get_arg(arg, sizeof(arg), &p);
4136 video_enc.lumi_masking = atof(arg);
4137 } else if (!strcasecmp(cmd, "DarkMask")) {
4138 get_arg(arg, sizeof(arg), &p);
4140 video_enc.dark_masking = atof(arg);
4141 } else if (!strcasecmp(cmd, "NoVideo")) {
4142 video_id = CODEC_ID_NONE;
4143 } else if (!strcasecmp(cmd, "NoAudio")) {
4144 audio_id = CODEC_ID_NONE;
4145 } else if (!strcasecmp(cmd, "ACL")) {
4148 get_arg(arg, sizeof(arg), &p);
4149 if (strcasecmp(arg, "allow") == 0)
4150 acl.action = IP_ALLOW;
4151 else if (strcasecmp(arg, "deny") == 0)
4152 acl.action = IP_DENY;
4154 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4155 filename, line_num, arg);
4159 get_arg(arg, sizeof(arg), &p);
4161 if (resolve_host(&acl.first, arg) != 0) {
4162 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4163 filename, line_num, arg);
4166 acl.last = acl.first;
4168 get_arg(arg, sizeof(arg), &p);
4171 if (resolve_host(&acl.last, arg) != 0) {
4172 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4173 filename, line_num, arg);
4179 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4180 IPAddressACL **naclp = 0;
4186 naclp = &stream->acl;
4190 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4191 filename, line_num);
4197 naclp = &(*naclp)->next;
4202 } else if (!strcasecmp(cmd, "RTSPOption")) {
4203 get_arg(arg, sizeof(arg), &p);
4205 av_freep(&stream->rtsp_option);
4206 stream->rtsp_option = av_strdup(arg);
4208 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4209 get_arg(arg, sizeof(arg), &p);
4211 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4212 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4213 filename, line_num, arg);
4216 stream->is_multicast = 1;
4217 stream->loop = 1; /* default is looping */
4219 } else if (!strcasecmp(cmd, "MulticastPort")) {
4220 get_arg(arg, sizeof(arg), &p);
4222 stream->multicast_port = atoi(arg);
4223 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4224 get_arg(arg, sizeof(arg), &p);
4226 stream->multicast_ttl = atoi(arg);
4227 } else if (!strcasecmp(cmd, "NoLoop")) {
4230 } else if (!strcasecmp(cmd, "</Stream>")) {
4232 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4233 filename, line_num);
4236 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4237 if (audio_id != CODEC_ID_NONE) {
4238 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4239 audio_enc.codec_id = audio_id;
4240 add_codec(stream, &audio_enc);
4242 if (video_id != CODEC_ID_NONE) {
4243 video_enc.codec_type = CODEC_TYPE_VIDEO;
4244 video_enc.codec_id = video_id;
4245 add_codec(stream, &video_enc);
4249 } else if (!strcasecmp(cmd, "<Redirect")) {
4250 /*********************************************/
4252 if (stream || feed || redirect) {
4253 fprintf(stderr, "%s:%d: Already in a tag\n",
4254 filename, line_num);
4257 redirect = av_mallocz(sizeof(FFStream));
4258 *last_stream = redirect;
4259 last_stream = &redirect->next;
4261 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4262 q = strrchr(redirect->filename, '>');
4265 redirect->stream_type = STREAM_TYPE_REDIRECT;
4267 } else if (!strcasecmp(cmd, "URL")) {
4269 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4270 } else if (!strcasecmp(cmd, "</Redirect>")) {
4272 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4273 filename, line_num);
4276 if (!redirect->feed_filename[0]) {
4277 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4278 filename, line_num);
4282 } else if (!strcasecmp(cmd, "LoadModule")) {
4283 get_arg(arg, sizeof(arg), &p);
4287 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4288 filename, line_num, arg);
4292 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4293 filename, line_num, cmd);
4305 static void show_banner(void)
4307 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4310 static void show_help(void)
4313 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4314 "Hyper fast multi format Audio/Video streaming server\n"
4316 "-L : print the LICENSE\n"
4318 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4322 static void handle_child_exit(int sig)
4327 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4330 for (feed = first_feed; feed; feed = feed->next) {
4331 if (feed->pid == pid) {
4332 int uptime = time(0) - feed->pid_start;
4335 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4338 /* Turn off any more restarts */
4339 feed->child_argv = 0;
4344 need_to_start_children = 1;
4347 int main(int argc, char **argv)
4349 const char *config_filename;
4351 struct sigaction sigact;
4355 config_filename = "/etc/ffserver.conf";
4357 my_program_name = argv[0];
4358 my_program_dir = getcwd(0, 0);
4359 ffserver_daemon = 1;
4362 c = getopt(argc, argv, "ndLh?f:");
4379 ffserver_daemon = 0;
4382 config_filename = optarg;
4389 putenv("http_proxy"); /* Kill the http_proxy */
4391 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4393 /* address on which the server will handle HTTP connections */
4394 my_http_addr.sin_family = AF_INET;
4395 my_http_addr.sin_port = htons (8080);
4396 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4398 /* address on which the server will handle RTSP connections */
4399 my_rtsp_addr.sin_family = AF_INET;
4400 my_rtsp_addr.sin_port = htons (5454);
4401 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4403 nb_max_connections = 5;
4404 max_bandwidth = 1000;
4405 first_stream = NULL;
4406 logfilename[0] = '\0';
4408 memset(&sigact, 0, sizeof(sigact));
4409 sigact.sa_handler = handle_child_exit;
4410 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4411 sigaction(SIGCHLD, &sigact, 0);
4413 if (parse_ffconfig(config_filename) < 0) {
4414 fprintf(stderr, "Incorrect config file - exiting.\n");
4418 build_file_streams();
4420 build_feed_streams();
4422 compute_bandwidth();
4424 /* put the process in background and detach it from its TTY */
4425 if (ffserver_daemon) {
4432 } else if (pid > 0) {
4440 open("/dev/null", O_RDWR);
4441 if (strcmp(logfilename, "-") != 0) {
4451 signal(SIGPIPE, SIG_IGN);
4453 /* open log file if needed */
4454 if (logfilename[0] != '\0') {
4455 if (!strcmp(logfilename, "-"))
4458 logfile = fopen(logfilename, "w");
4461 if (http_server() < 0) {
4462 fprintf(stderr, "Could not start server\n");