2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #define HAVE_AV_CONFIG_H
27 #include <sys/ioctl.h>
28 #ifdef HAVE_SYS_POLL_H
33 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
35 #include <sys/types.h>
36 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
50 /* maximum number of simultaneous HTTP connections */
51 #define HTTP_MAX_CONNECTIONS 2000
54 HTTPSTATE_WAIT_REQUEST,
55 HTTPSTATE_SEND_HEADER,
56 HTTPSTATE_SEND_DATA_HEADER,
57 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
58 HTTPSTATE_SEND_DATA_TRAILER,
59 HTTPSTATE_RECEIVE_DATA,
60 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
63 RTSPSTATE_WAIT_REQUEST,
65 RTSPSTATE_SEND_PACKET,
68 const char *http_state[] = {
84 #define IOBUFFER_INIT_SIZE 8192
86 /* coef for exponential mean for bitrate estimation in statistics */
89 /* timeouts are in ms */
90 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
91 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
93 #define SYNC_TIMEOUT (10 * 1000)
96 int64_t count1, count2;
100 /* context associated with one connection */
101 typedef struct HTTPContext {
102 enum HTTPState state;
103 int fd; /* socket file descriptor */
104 struct sockaddr_in from_addr; /* origin */
105 struct pollfd *poll_entry; /* used when polling */
107 uint8_t *buffer_ptr, *buffer_end;
110 struct HTTPContext *next;
111 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
115 /* input format handling */
116 AVFormatContext *fmt_in;
117 int64_t start_time; /* In milliseconds - this wraps fairly often */
118 int64_t first_pts; /* initial pts value */
119 int64_t cur_pts; /* current pts value from the stream in us */
120 int64_t cur_frame_duration; /* duration of the current frame in us */
121 int cur_frame_bytes; /* output frame size, needed to compute
122 the time at which we send each
124 int pts_stream_index; /* stream we choose as clock reference */
125 int64_t cur_clock; /* current clock reference value in us */
126 /* output format handling */
127 struct FFStream *stream;
128 /* -1 is invalid stream */
129 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
130 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
132 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
133 int last_packet_sent; /* true if last data packet was sent */
135 DataRateData datarate;
142 int is_packetized; /* if true, the stream is packetized */
143 int packet_stream_index; /* current stream for output in state machine */
145 /* RTSP state specific */
146 uint8_t *pb_buffer; /* XXX: use that in all the code */
148 int seq; /* RTSP sequence number */
150 /* RTP state specific */
151 enum RTSPProtocol rtp_protocol;
152 char session_id[32]; /* session id */
153 AVFormatContext *rtp_ctx[MAX_STREAMS];
155 /* RTP/UDP specific */
156 URLContext *rtp_handles[MAX_STREAMS];
158 /* RTP/TCP specific */
159 struct HTTPContext *rtsp_c;
160 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
163 static AVFrame dummy_frame;
165 /* each generated stream is described here */
169 STREAM_TYPE_REDIRECT,
172 enum IPAddressAction {
177 typedef struct IPAddressACL {
178 struct IPAddressACL *next;
179 enum IPAddressAction action;
180 /* These are in host order */
181 struct in_addr first;
185 /* description of each stream of the ffserver.conf file */
186 typedef struct FFStream {
187 enum StreamType stream_type;
188 char filename[1024]; /* stream filename */
189 struct FFStream *feed; /* feed we are using (can be null if
191 AVFormatParameters *ap_in; /* input parameters */
192 AVInputFormat *ifmt; /* if non NULL, force input format */
196 int prebuffer; /* Number of millseconds early to start */
197 int64_t max_time; /* Number of milliseconds to run */
199 AVStream *streams[MAX_STREAMS];
200 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
201 char feed_filename[1024]; /* file name of the feed storage, or
202 input file name for a stream */
207 pid_t pid; /* Of ffmpeg process */
208 time_t pid_start; /* Of ffmpeg process */
210 struct FFStream *next;
211 int bandwidth; /* bandwidth, in kbits/s */
214 /* multicast specific */
216 struct in_addr multicast_ip;
217 int multicast_port; /* first port used for multicast */
219 int loop; /* if true, send the stream in loops (only meaningful if file) */
222 int feed_opened; /* true if someone is writing to the feed */
223 int is_feed; /* true if it is a feed */
224 int readonly; /* True if writing is prohibited to the file */
226 int64_t bytes_served;
227 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
228 int64_t feed_write_index; /* current write position in feed (it wraps round) */
229 int64_t feed_size; /* current size of feed */
230 struct FFStream *next_feed;
233 typedef struct FeedData {
234 long long data_count;
235 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
238 struct sockaddr_in my_http_addr;
239 struct sockaddr_in my_rtsp_addr;
241 static char logfilename[1024];
242 static HTTPContext *first_http_ctx;
243 static FFStream *first_feed; /* contains only feeds */
244 static FFStream *first_stream; /* contains all streams, including feeds */
246 static void new_connection(int server_fd, int is_rtsp);
247 static void close_connection(HTTPContext *c);
250 static int handle_connection(HTTPContext *c);
251 static int http_parse_request(HTTPContext *c);
252 static int http_send_data(HTTPContext *c);
253 static void compute_stats(HTTPContext *c);
254 static int open_input_stream(HTTPContext *c, const char *info);
255 static int http_start_receive_data(HTTPContext *c);
256 static int http_receive_data(HTTPContext *c);
259 static int rtsp_parse_request(HTTPContext *c);
260 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
261 static void rtsp_cmd_options(HTTPContext *c, const char *url);
262 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
263 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
264 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
265 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
268 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
269 struct in_addr my_ip);
272 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
273 FFStream *stream, const char *session_id,
274 enum RTSPProtocol rtp_protocol);
275 static int rtp_new_av_stream(HTTPContext *c,
276 int stream_index, struct sockaddr_in *dest_addr,
277 HTTPContext *rtsp_c);
279 static const char *my_program_name;
280 static const char *my_program_dir;
282 static int ffserver_debug;
283 static int ffserver_daemon;
284 static int no_launch;
285 static int need_to_start_children;
287 static int nb_max_connections;
288 static int nb_connections;
290 static int max_bandwidth;
291 static int current_bandwidth;
293 static int64_t cur_time; // Making this global saves on passing it around everywhere
295 static AVRandomState random_state;
297 static FILE *logfile = NULL;
299 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
305 vfprintf(logfile, fmt, ap);
311 static char *ctime1(char *buf2)
319 p = buf2 + strlen(p) - 1;
325 static void log_connection(HTTPContext *c)
332 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
333 inet_ntoa(c->from_addr.sin_addr),
334 ctime1(buf2), c->method, c->url,
335 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
338 static void update_datarate(DataRateData *drd, int64_t count)
340 if (!drd->time1 && !drd->count1) {
341 drd->time1 = drd->time2 = cur_time;
342 drd->count1 = drd->count2 = count;
344 if (cur_time - drd->time2 > 5000) {
345 drd->time1 = drd->time2;
346 drd->count1 = drd->count2;
347 drd->time2 = cur_time;
353 /* In bytes per second */
354 static int compute_datarate(DataRateData *drd, int64_t count)
356 if (cur_time == drd->time1)
359 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
363 static void start_children(FFStream *feed)
368 for (; feed; feed = feed->next) {
369 if (feed->child_argv && !feed->pid) {
370 feed->pid_start = time(0);
375 fprintf(stderr, "Unable to create children\n");
384 for (i = 3; i < 256; i++) {
388 if (!ffserver_debug) {
389 i = open("/dev/null", O_RDWR);
398 pstrcpy(pathname, sizeof(pathname), my_program_name);
400 slash = strrchr(pathname, '/');
406 strcpy(slash, "ffmpeg");
408 /* This is needed to make relative pathnames work */
409 chdir(my_program_dir);
411 signal(SIGPIPE, SIG_DFL);
413 execvp(pathname, feed->child_argv);
421 /* open a listening socket */
422 static int socket_open_listen(struct sockaddr_in *my_addr)
426 server_fd = socket(AF_INET,SOCK_STREAM,0);
433 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
435 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
437 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
439 closesocket(server_fd);
443 if (listen (server_fd, 5) < 0) {
445 closesocket(server_fd);
448 fcntl(server_fd, F_SETFL, O_NONBLOCK);
453 /* start all multicast streams */
454 static void start_multicast(void)
459 struct sockaddr_in dest_addr;
460 int default_port, stream_index;
463 for(stream = first_stream; stream != NULL; stream = stream->next) {
464 if (stream->is_multicast) {
465 /* open the RTP connection */
466 snprintf(session_id, sizeof(session_id), "%08x%08x",
467 av_random(&random_state), av_random(&random_state));
469 /* choose a port if none given */
470 if (stream->multicast_port == 0) {
471 stream->multicast_port = default_port;
475 dest_addr.sin_family = AF_INET;
476 dest_addr.sin_addr = stream->multicast_ip;
477 dest_addr.sin_port = htons(stream->multicast_port);
479 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
480 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
484 if (open_input_stream(rtp_c, "") < 0) {
485 fprintf(stderr, "Could not open input stream for stream '%s'\n",
490 /* open each RTP stream */
491 for(stream_index = 0; stream_index < stream->nb_streams;
493 dest_addr.sin_port = htons(stream->multicast_port +
495 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
496 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
497 stream->filename, stream_index);
502 /* change state to send data */
503 rtp_c->state = HTTPSTATE_SEND_DATA;
508 /* main loop of the http server */
509 static int http_server(void)
511 int server_fd, ret, rtsp_server_fd, delay, delay1;
512 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
513 HTTPContext *c, *c_next;
515 server_fd = socket_open_listen(&my_http_addr);
519 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
520 if (rtsp_server_fd < 0)
523 http_log("ffserver started.\n");
525 start_children(first_feed);
527 first_http_ctx = NULL;
533 poll_entry = poll_table;
534 poll_entry->fd = server_fd;
535 poll_entry->events = POLLIN;
538 poll_entry->fd = rtsp_server_fd;
539 poll_entry->events = POLLIN;
542 /* wait for events on each HTTP handle */
549 case HTTPSTATE_SEND_HEADER:
550 case RTSPSTATE_SEND_REPLY:
551 case RTSPSTATE_SEND_PACKET:
552 c->poll_entry = poll_entry;
554 poll_entry->events = POLLOUT;
557 case HTTPSTATE_SEND_DATA_HEADER:
558 case HTTPSTATE_SEND_DATA:
559 case HTTPSTATE_SEND_DATA_TRAILER:
560 if (!c->is_packetized) {
561 /* for TCP, we output as much as we can (may need to put a limit) */
562 c->poll_entry = poll_entry;
564 poll_entry->events = POLLOUT;
567 /* when ffserver is doing the timing, we work by
568 looking at which packet need to be sent every
570 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
575 case HTTPSTATE_WAIT_REQUEST:
576 case HTTPSTATE_RECEIVE_DATA:
577 case HTTPSTATE_WAIT_FEED:
578 case RTSPSTATE_WAIT_REQUEST:
579 /* need to catch errors */
580 c->poll_entry = poll_entry;
582 poll_entry->events = POLLIN;/* Maybe this will work */
586 c->poll_entry = NULL;
592 /* wait for an event on one connection. We poll at least every
593 second to handle timeouts */
595 ret = poll(poll_table, poll_entry - poll_table, delay);
596 if (ret < 0 && errno != EAGAIN && errno != 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);
623 /* new RTSP connection request ? */
624 if (poll_entry->revents & POLLIN) {
625 new_connection(rtsp_server_fd, 1);
630 /* start waiting for a new HTTP/RTSP request */
631 static void start_wait_request(HTTPContext *c, int is_rtsp)
633 c->buffer_ptr = c->buffer;
634 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
637 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
638 c->state = RTSPSTATE_WAIT_REQUEST;
640 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
641 c->state = HTTPSTATE_WAIT_REQUEST;
645 static void new_connection(int server_fd, int is_rtsp)
647 struct sockaddr_in from_addr;
649 HTTPContext *c = NULL;
651 len = sizeof(from_addr);
652 fd = accept(server_fd, (struct sockaddr *)&from_addr,
656 fcntl(fd, F_SETFL, O_NONBLOCK);
658 /* XXX: should output a warning page when coming
659 close to the connection limit */
660 if (nb_connections >= nb_max_connections)
663 /* add a new connection */
664 c = av_mallocz(sizeof(HTTPContext));
669 c->poll_entry = NULL;
670 c->from_addr = from_addr;
671 c->buffer_size = IOBUFFER_INIT_SIZE;
672 c->buffer = av_malloc(c->buffer_size);
676 c->next = first_http_ctx;
680 start_wait_request(c, is_rtsp);
692 static void close_connection(HTTPContext *c)
694 HTTPContext **cp, *c1;
696 AVFormatContext *ctx;
700 /* remove connection from list */
701 cp = &first_http_ctx;
702 while ((*cp) != NULL) {
711 /* remove references, if any (XXX: do it faster) */
712 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
717 /* remove connection associated resources */
721 /* close each frame parser */
722 for(i=0;i<c->fmt_in->nb_streams;i++) {
723 st = c->fmt_in->streams[i];
724 if (st->codec->codec) {
725 avcodec_close(st->codec);
728 av_close_input_file(c->fmt_in);
731 /* free RTP output streams if any */
734 nb_streams = c->stream->nb_streams;
736 for(i=0;i<nb_streams;i++) {
739 av_write_trailer(ctx);
742 h = c->rtp_handles[i];
750 if (!c->last_packet_sent) {
753 if (url_open_dyn_buf(&ctx->pb) >= 0) {
754 av_write_trailer(ctx);
755 url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
760 for(i=0; i<ctx->nb_streams; i++)
761 av_free(ctx->streams[i]) ;
763 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
764 current_bandwidth -= c->stream->bandwidth;
766 /* signal that there is no feed if we are the feeder socket */
767 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
768 c->stream->feed_opened = 0;
772 av_freep(&c->pb_buffer);
773 av_freep(&c->packet_buffer);
779 static int handle_connection(HTTPContext *c)
784 case HTTPSTATE_WAIT_REQUEST:
785 case RTSPSTATE_WAIT_REQUEST:
787 if ((c->timeout - cur_time) < 0)
789 if (c->poll_entry->revents & (POLLERR | POLLHUP))
792 /* no need to read if no events */
793 if (!(c->poll_entry->revents & POLLIN))
797 len = recv(c->fd, c->buffer_ptr, 1, 0);
799 if (errno != EAGAIN && errno != EINTR)
801 } else if (len == 0) {
804 /* search for end of request. */
806 c->buffer_ptr += len;
808 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
809 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
810 /* request found : parse it and reply */
811 if (c->state == HTTPSTATE_WAIT_REQUEST) {
812 ret = http_parse_request(c);
814 ret = rtsp_parse_request(c);
818 } else if (ptr >= c->buffer_end) {
819 /* request too long: cannot do anything */
821 } else goto read_loop;
825 case HTTPSTATE_SEND_HEADER:
826 if (c->poll_entry->revents & (POLLERR | POLLHUP))
829 /* no need to write if no events */
830 if (!(c->poll_entry->revents & POLLOUT))
832 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
834 if (errno != EAGAIN && errno != EINTR) {
835 /* error : close connection */
836 av_freep(&c->pb_buffer);
840 c->buffer_ptr += len;
842 c->stream->bytes_served += len;
843 c->data_count += len;
844 if (c->buffer_ptr >= c->buffer_end) {
845 av_freep(&c->pb_buffer);
850 /* all the buffer was sent : synchronize to the incoming stream */
851 c->state = HTTPSTATE_SEND_DATA_HEADER;
852 c->buffer_ptr = c->buffer_end = c->buffer;
857 case HTTPSTATE_SEND_DATA:
858 case HTTPSTATE_SEND_DATA_HEADER:
859 case HTTPSTATE_SEND_DATA_TRAILER:
860 /* for packetized output, we consider we can always write (the
861 input streams sets the speed). It may be better to verify
862 that we do not rely too much on the kernel queues */
863 if (!c->is_packetized) {
864 if (c->poll_entry->revents & (POLLERR | POLLHUP))
867 /* no need to read if no events */
868 if (!(c->poll_entry->revents & POLLOUT))
871 if (http_send_data(c) < 0)
873 /* close connection if trailer sent */
874 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
877 case HTTPSTATE_RECEIVE_DATA:
878 /* no need to read if no events */
879 if (c->poll_entry->revents & (POLLERR | POLLHUP))
881 if (!(c->poll_entry->revents & POLLIN))
883 if (http_receive_data(c) < 0)
886 case HTTPSTATE_WAIT_FEED:
887 /* no need to read if no events */
888 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
891 /* nothing to do, we'll be waken up by incoming feed packets */
894 case RTSPSTATE_SEND_REPLY:
895 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
896 av_freep(&c->pb_buffer);
899 /* no need to write if no events */
900 if (!(c->poll_entry->revents & POLLOUT))
902 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
904 if (errno != EAGAIN && errno != EINTR) {
905 /* error : close connection */
906 av_freep(&c->pb_buffer);
910 c->buffer_ptr += len;
911 c->data_count += len;
912 if (c->buffer_ptr >= c->buffer_end) {
913 /* all the buffer was sent : wait for a new request */
914 av_freep(&c->pb_buffer);
915 start_wait_request(c, 1);
919 case RTSPSTATE_SEND_PACKET:
920 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
921 av_freep(&c->packet_buffer);
924 /* no need to write if no events */
925 if (!(c->poll_entry->revents & POLLOUT))
927 len = send(c->fd, c->packet_buffer_ptr,
928 c->packet_buffer_end - c->packet_buffer_ptr, 0);
930 if (errno != EAGAIN && errno != EINTR) {
931 /* error : close connection */
932 av_freep(&c->packet_buffer);
936 c->packet_buffer_ptr += len;
937 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
938 /* all the buffer was sent : wait for a new request */
939 av_freep(&c->packet_buffer);
940 c->state = RTSPSTATE_WAIT_REQUEST;
944 case HTTPSTATE_READY:
953 static int extract_rates(char *rates, int ratelen, const char *request)
957 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
958 if (strncasecmp(p, "Pragma:", 7) == 0) {
959 const char *q = p + 7;
961 while (*q && *q != '\n' && isspace(*q))
964 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
970 memset(rates, 0xff, ratelen);
973 while (*q && *q != '\n' && *q != ':')
976 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
980 if (stream_no < ratelen && stream_no >= 0) {
981 rates[stream_no] = rate_no;
984 while (*q && *q != '\n' && !isspace(*q))
1001 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1004 int best_bitrate = 100000000;
1007 for (i = 0; i < feed->nb_streams; i++) {
1008 AVCodecContext *feed_codec = feed->streams[i]->codec;
1010 if (feed_codec->codec_id != codec->codec_id ||
1011 feed_codec->sample_rate != codec->sample_rate ||
1012 feed_codec->width != codec->width ||
1013 feed_codec->height != codec->height) {
1017 /* Potential stream */
1019 /* We want the fastest stream less than bit_rate, or the slowest
1020 * faster than bit_rate
1023 if (feed_codec->bit_rate <= bit_rate) {
1024 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1025 best_bitrate = feed_codec->bit_rate;
1029 if (feed_codec->bit_rate < best_bitrate) {
1030 best_bitrate = feed_codec->bit_rate;
1039 static int modify_current_stream(HTTPContext *c, char *rates)
1042 FFStream *req = c->stream;
1043 int action_required = 0;
1045 /* Not much we can do for a feed */
1049 for (i = 0; i < req->nb_streams; i++) {
1050 AVCodecContext *codec = req->streams[i]->codec;
1054 c->switch_feed_streams[i] = req->feed_streams[i];
1057 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1060 /* Wants off or slow */
1061 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1063 /* This doesn't work well when it turns off the only stream! */
1064 c->switch_feed_streams[i] = -2;
1065 c->feed_streams[i] = -2;
1070 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1071 action_required = 1;
1074 return action_required;
1078 static void do_switch_stream(HTTPContext *c, int i)
1080 if (c->switch_feed_streams[i] >= 0) {
1082 c->feed_streams[i] = c->switch_feed_streams[i];
1085 /* Now update the stream */
1087 c->switch_feed_streams[i] = -1;
1090 /* XXX: factorize in utils.c ? */
1091 /* XXX: take care with different space meaning */
1092 static void skip_spaces(const char **pp)
1096 while (*p == ' ' || *p == '\t')
1101 static void get_word(char *buf, int buf_size, const char **pp)
1109 while (!isspace(*p) && *p != '\0') {
1110 if ((q - buf) < buf_size - 1)
1119 static int validate_acl(FFStream *stream, HTTPContext *c)
1121 enum IPAddressAction last_action = IP_DENY;
1123 struct in_addr *src = &c->from_addr.sin_addr;
1124 unsigned long src_addr = ntohl(src->s_addr);
1126 for (acl = stream->acl; acl; acl = acl->next) {
1127 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1128 return (acl->action == IP_ALLOW) ? 1 : 0;
1130 last_action = acl->action;
1133 /* Nothing matched, so return not the last action */
1134 return (last_action == IP_DENY) ? 1 : 0;
1137 /* compute the real filename of a file by matching it without its
1138 extensions to all the stream filenames */
1139 static void compute_real_filename(char *filename, int max_size)
1146 /* compute filename by matching without the file extensions */
1147 pstrcpy(file1, sizeof(file1), filename);
1148 p = strrchr(file1, '.');
1151 for(stream = first_stream; stream != NULL; stream = stream->next) {
1152 pstrcpy(file2, sizeof(file2), stream->filename);
1153 p = strrchr(file2, '.');
1156 if (!strcmp(file1, file2)) {
1157 pstrcpy(filename, max_size, stream->filename);
1172 /* parse http request and prepare header */
1173 static int http_parse_request(HTTPContext *c)
1176 enum RedirType redir_type;
1178 char info[1024], filename[1024];
1182 const char *mime_type;
1186 char *useragent = 0;
1189 get_word(cmd, sizeof(cmd), (const char **)&p);
1190 pstrcpy(c->method, sizeof(c->method), cmd);
1192 if (!strcmp(cmd, "GET"))
1194 else if (!strcmp(cmd, "POST"))
1199 get_word(url, sizeof(url), (const char **)&p);
1200 pstrcpy(c->url, sizeof(c->url), url);
1202 get_word(protocol, sizeof(protocol), (const char **)&p);
1203 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1206 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1209 http_log("New connection: %s %s\n", cmd, url);
1211 /* find the filename and the optional info string in the request */
1212 p = strchr(url, '?');
1214 pstrcpy(info, sizeof(info), p);
1220 pstrcpy(filename, sizeof(filename)-1, url + ((*url == '/') ? 1 : 0));
1222 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1223 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1225 if (*useragent && *useragent != '\n' && isspace(*useragent))
1229 p = strchr(p, '\n');
1236 redir_type = REDIR_NONE;
1237 if (match_ext(filename, "asx")) {
1238 redir_type = REDIR_ASX;
1239 filename[strlen(filename)-1] = 'f';
1240 } else if (match_ext(filename, "asf") &&
1241 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1242 /* if this isn't WMP or lookalike, return the redirector file */
1243 redir_type = REDIR_ASF;
1244 } else if (match_ext(filename, "rpm,ram")) {
1245 redir_type = REDIR_RAM;
1246 strcpy(filename + strlen(filename)-2, "m");
1247 } else if (match_ext(filename, "rtsp")) {
1248 redir_type = REDIR_RTSP;
1249 compute_real_filename(filename, sizeof(filename) - 1);
1250 } else if (match_ext(filename, "sdp")) {
1251 redir_type = REDIR_SDP;
1252 compute_real_filename(filename, sizeof(filename) - 1);
1255 // "redirect" / request to index.html
1256 if (!strlen(filename))
1257 pstrcpy(filename, sizeof(filename) - 1, "index.html");
1259 stream = first_stream;
1260 while (stream != NULL) {
1261 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1263 stream = stream->next;
1265 if (stream == NULL) {
1266 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1271 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1272 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1274 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1275 c->http_error = 301;
1277 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1278 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1279 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1280 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1281 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1282 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1283 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1285 /* prepare output buffer */
1286 c->buffer_ptr = c->buffer;
1288 c->state = HTTPSTATE_SEND_HEADER;
1292 /* If this is WMP, get the rate information */
1293 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1294 if (modify_current_stream(c, ratebuf)) {
1295 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1296 if (c->switch_feed_streams[i] >= 0)
1297 do_switch_stream(c, i);
1302 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1303 current_bandwidth += stream->bandwidth;
1306 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1307 c->http_error = 200;
1309 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1310 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1311 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1312 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1313 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");
1314 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",
1315 current_bandwidth, max_bandwidth);
1316 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1318 /* prepare output buffer */
1319 c->buffer_ptr = c->buffer;
1321 c->state = HTTPSTATE_SEND_HEADER;
1325 if (redir_type != REDIR_NONE) {
1328 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1329 if (strncasecmp(p, "Host:", 5) == 0) {
1333 p = strchr(p, '\n');
1344 while (isspace(*hostinfo))
1347 eoh = strchr(hostinfo, '\n');
1349 if (eoh[-1] == '\r')
1352 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1353 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1354 hostbuf[eoh - hostinfo] = 0;
1356 c->http_error = 200;
1358 switch(redir_type) {
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1362 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1363 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1364 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1365 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1366 hostbuf, filename, info);
1367 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1370 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1371 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1372 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1373 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1374 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1375 hostbuf, filename, info);
1378 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1379 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1380 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1381 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1382 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1383 hostbuf, filename, info);
1387 char hostname[256], *p;
1388 /* extract only hostname */
1389 pstrcpy(hostname, sizeof(hostname), hostbuf);
1390 p = strrchr(hostname, ':');
1393 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1394 /* XXX: incorrect mime type ? */
1395 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1396 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1397 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1398 hostname, ntohs(my_rtsp_addr.sin_port),
1405 int sdp_data_size, len;
1406 struct sockaddr_in my_addr;
1408 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1410 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1412 len = sizeof(my_addr);
1413 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1415 /* XXX: should use a dynamic buffer */
1416 sdp_data_size = prepare_sdp_description(stream,
1419 if (sdp_data_size > 0) {
1420 memcpy(q, sdp_data, sdp_data_size);
1432 /* prepare output buffer */
1433 c->buffer_ptr = c->buffer;
1435 c->state = HTTPSTATE_SEND_HEADER;
1441 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1445 stream->conns_served++;
1447 /* XXX: add there authenticate and IP match */
1450 /* if post, it means a feed is being sent */
1451 if (!stream->is_feed) {
1452 /* However it might be a status report from WMP! Lets log the data
1453 * as it might come in handy one day
1458 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1459 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1463 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1464 client_id = strtol(p + 18, 0, 10);
1466 p = strchr(p, '\n');
1474 char *eol = strchr(logline, '\n');
1479 if (eol[-1] == '\r')
1481 http_log("%.*s\n", (int) (eol - logline), logline);
1482 c->suppress_log = 1;
1487 http_log("\nGot request:\n%s\n", c->buffer);
1490 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1493 /* Now we have to find the client_id */
1494 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1495 if (wmpc->wmp_client_id == client_id)
1500 if (modify_current_stream(wmpc, ratebuf)) {
1501 wmpc->switch_pending = 1;
1506 snprintf(msg, sizeof(msg), "POST command not handled");
1510 if (http_start_receive_data(c) < 0) {
1511 snprintf(msg, sizeof(msg), "could not open feed");
1515 c->state = HTTPSTATE_RECEIVE_DATA;
1520 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1521 http_log("\nGot request:\n%s\n", c->buffer);
1525 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1528 /* open input stream */
1529 if (open_input_stream(c, info) < 0) {
1530 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1534 /* prepare http header */
1536 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1537 mime_type = c->stream->fmt->mime_type;
1539 mime_type = "application/x-octet_stream";
1540 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1542 /* for asf, we need extra headers */
1543 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1544 /* Need to allocate a client id */
1546 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1548 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);
1550 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1551 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1553 /* prepare output buffer */
1555 c->buffer_ptr = c->buffer;
1557 c->state = HTTPSTATE_SEND_HEADER;
1560 c->http_error = 404;
1562 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1563 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1564 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1565 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1566 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1567 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1568 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1570 /* prepare output buffer */
1571 c->buffer_ptr = c->buffer;
1573 c->state = HTTPSTATE_SEND_HEADER;
1577 c->http_error = 200; /* horrible : we use this value to avoid
1578 going to the send data state */
1579 c->state = HTTPSTATE_SEND_HEADER;
1583 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1585 static const char *suffix = " kMGTP";
1588 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1591 url_fprintf(pb, "%"PRId64"%c", count, *s);
1594 static void compute_stats(HTTPContext *c)
1601 ByteIOContext pb1, *pb = &pb1;
1603 if (url_open_dyn_buf(pb) < 0) {
1604 /* XXX: return an error ? */
1605 c->buffer_ptr = c->buffer;
1606 c->buffer_end = c->buffer;
1610 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1611 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1612 url_fprintf(pb, "Pragma: no-cache\r\n");
1613 url_fprintf(pb, "\r\n");
1615 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1616 if (c->stream->feed_filename) {
1617 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1619 url_fprintf(pb, "</HEAD>\n<BODY>");
1620 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1622 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1623 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1624 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");
1625 stream = first_stream;
1626 while (stream != NULL) {
1627 char sfilename[1024];
1630 if (stream->feed != stream) {
1631 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1632 eosf = sfilename + strlen(sfilename);
1633 if (eosf - sfilename >= 4) {
1634 if (strcmp(eosf - 4, ".asf") == 0) {
1635 strcpy(eosf - 4, ".asx");
1636 } else if (strcmp(eosf - 3, ".rm") == 0) {
1637 strcpy(eosf - 3, ".ram");
1638 } else if (stream->fmt == &rtp_muxer) {
1639 /* generate a sample RTSP director if
1640 unicast. Generate an SDP redirector if
1642 eosf = strrchr(sfilename, '.');
1644 eosf = sfilename + strlen(sfilename);
1645 if (stream->is_multicast)
1646 strcpy(eosf, ".sdp");
1648 strcpy(eosf, ".rtsp");
1652 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1653 sfilename, stream->filename);
1654 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1655 stream->conns_served);
1656 fmt_bytecount(pb, stream->bytes_served);
1657 switch(stream->stream_type) {
1658 case STREAM_TYPE_LIVE:
1660 int audio_bit_rate = 0;
1661 int video_bit_rate = 0;
1662 const char *audio_codec_name = "";
1663 const char *video_codec_name = "";
1664 const char *audio_codec_name_extra = "";
1665 const char *video_codec_name_extra = "";
1667 for(i=0;i<stream->nb_streams;i++) {
1668 AVStream *st = stream->streams[i];
1669 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1670 switch(st->codec->codec_type) {
1671 case CODEC_TYPE_AUDIO:
1672 audio_bit_rate += st->codec->bit_rate;
1674 if (*audio_codec_name)
1675 audio_codec_name_extra = "...";
1676 audio_codec_name = codec->name;
1679 case CODEC_TYPE_VIDEO:
1680 video_bit_rate += st->codec->bit_rate;
1682 if (*video_codec_name)
1683 video_codec_name_extra = "...";
1684 video_codec_name = codec->name;
1687 case CODEC_TYPE_DATA:
1688 video_bit_rate += st->codec->bit_rate;
1694 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",
1697 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1698 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1700 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1702 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1704 url_fprintf(pb, "\n");
1708 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1712 stream = stream->next;
1714 url_fprintf(pb, "</TABLE>\n");
1716 stream = first_stream;
1717 while (stream != NULL) {
1718 if (stream->feed == stream) {
1719 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1721 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1723 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1728 /* This is somewhat linux specific I guess */
1729 snprintf(ps_cmd, sizeof(ps_cmd),
1730 "ps -o \"%%cpu,cputime\" --no-headers %d",
1733 pid_stat = popen(ps_cmd, "r");
1738 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1740 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1748 url_fprintf(pb, "<p>");
1750 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");
1752 for (i = 0; i < stream->nb_streams; i++) {
1753 AVStream *st = stream->streams[i];
1754 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1755 const char *type = "unknown";
1756 char parameters[64];
1760 switch(st->codec->codec_type) {
1761 case CODEC_TYPE_AUDIO:
1764 case CODEC_TYPE_VIDEO:
1766 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1767 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1772 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1773 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1775 url_fprintf(pb, "</table>\n");
1778 stream = stream->next;
1784 AVCodecContext *enc;
1788 stream = first_feed;
1789 while (stream != NULL) {
1790 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1791 url_fprintf(pb, "<TABLE>\n");
1792 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1793 for(i=0;i<stream->nb_streams;i++) {
1794 AVStream *st = stream->streams[i];
1795 FeedData *fdata = st->priv_data;
1798 avcodec_string(buf, sizeof(buf), enc);
1799 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1800 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1801 avg /= enc->frame_size;
1802 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1803 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1805 url_fprintf(pb, "</TABLE>\n");
1806 stream = stream->next_feed;
1811 /* connection status */
1812 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1814 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1815 nb_connections, nb_max_connections);
1817 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1818 current_bandwidth, max_bandwidth);
1820 url_fprintf(pb, "<TABLE>\n");
1821 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");
1822 c1 = first_http_ctx;
1824 while (c1 != NULL) {
1830 for (j = 0; j < c1->stream->nb_streams; j++) {
1831 if (!c1->stream->feed) {
1832 bitrate += c1->stream->streams[j]->codec->bit_rate;
1834 if (c1->feed_streams[j] >= 0) {
1835 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1842 p = inet_ntoa(c1->from_addr.sin_addr);
1843 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1845 c1->stream ? c1->stream->filename : "",
1846 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1849 http_state[c1->state]);
1850 fmt_bytecount(pb, bitrate);
1851 url_fprintf(pb, "<td align=right>");
1852 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1853 url_fprintf(pb, "<td align=right>");
1854 fmt_bytecount(pb, c1->data_count);
1855 url_fprintf(pb, "\n");
1858 url_fprintf(pb, "</TABLE>\n");
1863 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1864 url_fprintf(pb, "</BODY>\n</HTML>\n");
1866 len = url_close_dyn_buf(pb, &c->pb_buffer);
1867 c->buffer_ptr = c->pb_buffer;
1868 c->buffer_end = c->pb_buffer + len;
1871 /* check if the parser needs to be opened for stream i */
1872 static void open_parser(AVFormatContext *s, int i)
1874 AVStream *st = s->streams[i];
1877 if (!st->codec->codec) {
1878 codec = avcodec_find_decoder(st->codec->codec_id);
1879 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1880 st->codec->parse_only = 1;
1881 if (avcodec_open(st->codec, codec) < 0) {
1882 st->codec->parse_only = 0;
1888 static int open_input_stream(HTTPContext *c, const char *info)
1891 char input_filename[1024];
1896 /* find file name */
1897 if (c->stream->feed) {
1898 strcpy(input_filename, c->stream->feed->feed_filename);
1899 buf_size = FFM_PACKET_SIZE;
1900 /* compute position (absolute time) */
1901 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1902 stream_pos = parse_date(buf, 0);
1903 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1904 int prebuffer = strtol(buf, 0, 10);
1905 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1907 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1910 strcpy(input_filename, c->stream->feed_filename);
1912 /* compute position (relative time) */
1913 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1914 stream_pos = parse_date(buf, 1);
1919 if (input_filename[0] == '\0')
1923 { time_t when = stream_pos / 1000000;
1924 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1929 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1930 buf_size, c->stream->ap_in) < 0) {
1931 http_log("%s not found", input_filename);
1936 /* open each parser */
1937 for(i=0;i<s->nb_streams;i++)
1940 /* choose stream as clock source (we favorize video stream if
1941 present) for packet sending */
1942 c->pts_stream_index = 0;
1943 for(i=0;i<c->stream->nb_streams;i++) {
1944 if (c->pts_stream_index == 0 &&
1945 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1946 c->pts_stream_index = i;
1951 if (c->fmt_in->iformat->read_seek) {
1952 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1955 /* set the start time (needed for maxtime and RTP packet timing) */
1956 c->start_time = cur_time;
1957 c->first_pts = AV_NOPTS_VALUE;
1961 /* return the server clock (in us) */
1962 static int64_t get_server_clock(HTTPContext *c)
1964 /* compute current pts value from system time */
1965 return (cur_time - c->start_time) * 1000;
1968 /* return the estimated time at which the current packet must be sent
1970 static int64_t get_packet_send_clock(HTTPContext *c)
1972 int bytes_left, bytes_sent, frame_bytes;
1974 frame_bytes = c->cur_frame_bytes;
1975 if (frame_bytes <= 0) {
1978 bytes_left = c->buffer_end - c->buffer_ptr;
1979 bytes_sent = frame_bytes - bytes_left;
1980 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1985 static int http_prepare_data(HTTPContext *c)
1988 AVFormatContext *ctx;
1990 av_freep(&c->pb_buffer);
1992 case HTTPSTATE_SEND_DATA_HEADER:
1993 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1994 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
1996 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
1997 c->stream->comment);
1998 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
1999 c->stream->copyright);
2000 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2003 /* open output stream by using specified codecs */
2004 c->fmt_ctx.oformat = c->stream->fmt;
2005 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2006 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2009 st = av_mallocz(sizeof(AVStream));
2010 st->codec= avcodec_alloc_context();
2011 c->fmt_ctx.streams[i] = st;
2012 /* if file or feed, then just take streams from FFStream struct */
2013 if (!c->stream->feed ||
2014 c->stream->feed == c->stream)
2015 src = c->stream->streams[i];
2017 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2021 st->codec->frame_number = 0; /* XXX: should be done in
2022 AVStream, not in codec */
2023 /* I'm pretty sure that this is not correct...
2024 * However, without it, we crash
2026 st->codec->coded_frame = &dummy_frame;
2028 c->got_key_frame = 0;
2030 /* prepare header and save header data in a stream */
2031 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2032 /* XXX: potential leak */
2035 c->fmt_ctx.pb.is_streamed = 1;
2037 av_set_parameters(&c->fmt_ctx, NULL);
2038 if (av_write_header(&c->fmt_ctx) < 0)
2041 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2042 c->buffer_ptr = c->pb_buffer;
2043 c->buffer_end = c->pb_buffer + len;
2045 c->state = HTTPSTATE_SEND_DATA;
2046 c->last_packet_sent = 0;
2048 case HTTPSTATE_SEND_DATA:
2049 /* find a new packet */
2053 /* read a packet from the input stream */
2054 if (c->stream->feed) {
2055 ffm_set_write_index(c->fmt_in,
2056 c->stream->feed->feed_write_index,
2057 c->stream->feed->feed_size);
2060 if (c->stream->max_time &&
2061 c->stream->max_time + c->start_time - cur_time < 0) {
2062 /* We have timed out */
2063 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2066 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2067 if (c->stream->feed && c->stream->feed->feed_opened) {
2068 /* if coming from feed, it means we reached the end of the
2069 ffm file, so must wait for more data */
2070 c->state = HTTPSTATE_WAIT_FEED;
2071 return 1; /* state changed */
2073 if (c->stream->loop) {
2074 av_close_input_file(c->fmt_in);
2076 if (open_input_stream(c, "") < 0)
2081 /* must send trailer now because eof or error */
2082 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2086 /* update first pts if needed */
2087 if (c->first_pts == AV_NOPTS_VALUE) {
2088 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2089 c->start_time = cur_time;
2091 /* send it to the appropriate stream */
2092 if (c->stream->feed) {
2093 /* if coming from a feed, select the right stream */
2094 if (c->switch_pending) {
2095 c->switch_pending = 0;
2096 for(i=0;i<c->stream->nb_streams;i++) {
2097 if (c->switch_feed_streams[i] == pkt.stream_index) {
2098 if (pkt.flags & PKT_FLAG_KEY) {
2099 do_switch_stream(c, i);
2102 if (c->switch_feed_streams[i] >= 0) {
2103 c->switch_pending = 1;
2107 for(i=0;i<c->stream->nb_streams;i++) {
2108 if (c->feed_streams[i] == pkt.stream_index) {
2109 pkt.stream_index = i;
2110 if (pkt.flags & PKT_FLAG_KEY) {
2111 c->got_key_frame |= 1 << i;
2113 /* See if we have all the key frames, then
2114 * we start to send. This logic is not quite
2115 * right, but it works for the case of a
2116 * single video stream with one or more
2117 * audio streams (for which every frame is
2118 * typically a key frame).
2120 if (!c->stream->send_on_key ||
2121 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2127 AVCodecContext *codec;
2130 /* specific handling for RTP: we use several
2131 output stream (one for each RTP
2132 connection). XXX: need more abstract handling */
2133 if (c->is_packetized) {
2135 /* compute send time and duration */
2136 st = c->fmt_in->streams[pkt.stream_index];
2137 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2138 if (st->start_time != AV_NOPTS_VALUE)
2139 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2140 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2142 printf("index=%d pts=%0.3f duration=%0.6f\n",
2144 (double)c->cur_pts /
2146 (double)c->cur_frame_duration /
2149 /* find RTP context */
2150 c->packet_stream_index = pkt.stream_index;
2151 ctx = c->rtp_ctx[c->packet_stream_index];
2153 av_free_packet(&pkt);
2156 codec = ctx->streams[0]->codec;
2157 /* only one stream per RTP connection */
2158 pkt.stream_index = 0;
2162 codec = ctx->streams[pkt.stream_index]->codec;
2165 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2166 if (c->is_packetized) {
2167 int max_packet_size;
2168 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2169 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2171 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2172 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2174 ret = url_open_dyn_buf(&ctx->pb);
2177 /* XXX: potential leak */
2180 if (pkt.dts != AV_NOPTS_VALUE)
2181 pkt.dts = av_rescale_q(pkt.dts,
2182 c->fmt_in->streams[pkt.stream_index]->time_base,
2183 ctx->streams[pkt.stream_index]->time_base);
2184 if (pkt.pts != AV_NOPTS_VALUE)
2185 pkt.pts = av_rescale_q(pkt.pts,
2186 c->fmt_in->streams[pkt.stream_index]->time_base,
2187 ctx->streams[pkt.stream_index]->time_base);
2188 if (av_write_frame(ctx, &pkt)) {
2189 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2192 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2193 c->cur_frame_bytes = len;
2194 c->buffer_ptr = c->pb_buffer;
2195 c->buffer_end = c->pb_buffer + len;
2197 codec->frame_number++;
2201 av_free_packet(&pkt);
2207 case HTTPSTATE_SEND_DATA_TRAILER:
2208 /* last packet test ? */
2209 if (c->last_packet_sent || c->is_packetized)
2212 /* prepare header */
2213 if (url_open_dyn_buf(&ctx->pb) < 0) {
2214 /* XXX: potential leak */
2217 av_write_trailer(ctx);
2218 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2219 c->buffer_ptr = c->pb_buffer;
2220 c->buffer_end = c->pb_buffer + len;
2222 c->last_packet_sent = 1;
2229 #define SHORT_TERM_BANDWIDTH 8000000
2231 /* should convert the format at the same time */
2232 /* send data starting at c->buffer_ptr to the output connection
2233 (either UDP or TCP connection) */
2234 static int http_send_data(HTTPContext *c)
2239 if (c->buffer_ptr >= c->buffer_end) {
2240 ret = http_prepare_data(c);
2243 else if (ret != 0) {
2244 /* state change requested */
2248 if (c->is_packetized) {
2249 /* RTP data output */
2250 len = c->buffer_end - c->buffer_ptr;
2252 /* fail safe - should never happen */
2254 c->buffer_ptr = c->buffer_end;
2257 len = (c->buffer_ptr[0] << 24) |
2258 (c->buffer_ptr[1] << 16) |
2259 (c->buffer_ptr[2] << 8) |
2261 if (len > (c->buffer_end - c->buffer_ptr))
2263 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2264 /* nothing to send yet: we can wait */
2268 c->data_count += len;
2269 update_datarate(&c->datarate, c->data_count);
2271 c->stream->bytes_served += len;
2273 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2274 /* RTP packets are sent inside the RTSP TCP connection */
2275 ByteIOContext pb1, *pb = &pb1;
2276 int interleaved_index, size;
2278 HTTPContext *rtsp_c;
2281 /* if no RTSP connection left, error */
2284 /* if already sending something, then wait. */
2285 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
2288 if (url_open_dyn_buf(pb) < 0)
2290 interleaved_index = c->packet_stream_index * 2;
2291 /* RTCP packets are sent at odd indexes */
2292 if (c->buffer_ptr[1] == 200)
2293 interleaved_index++;
2294 /* write RTSP TCP header */
2296 header[1] = interleaved_index;
2297 header[2] = len >> 8;
2299 put_buffer(pb, header, 4);
2300 /* write RTP packet data */
2302 put_buffer(pb, c->buffer_ptr, len);
2303 size = url_close_dyn_buf(pb, &c->packet_buffer);
2304 /* prepare asynchronous TCP sending */
2305 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2306 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2307 c->buffer_ptr += len;
2309 /* send everything we can NOW */
2310 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2311 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2313 rtsp_c->packet_buffer_ptr += len;
2315 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2316 /* if we could not send all the data, we will
2317 send it later, so a new state is needed to
2318 "lock" the RTSP TCP connection */
2319 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2322 /* all data has been sent */
2323 av_freep(&c->packet_buffer);
2326 /* send RTP packet directly in UDP */
2328 url_write(c->rtp_handles[c->packet_stream_index],
2329 c->buffer_ptr, len);
2330 c->buffer_ptr += len;
2331 /* here we continue as we can send several packets per 10 ms slot */
2334 /* TCP data output */
2335 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2337 if (errno != EAGAIN && errno != EINTR) {
2338 /* error : close connection */
2344 c->buffer_ptr += len;
2346 c->data_count += len;
2347 update_datarate(&c->datarate, c->data_count);
2349 c->stream->bytes_served += len;
2357 static int http_start_receive_data(HTTPContext *c)
2361 if (c->stream->feed_opened)
2364 /* Don't permit writing to this one */
2365 if (c->stream->readonly)
2369 fd = open(c->stream->feed_filename, O_RDWR);
2374 c->stream->feed_write_index = ffm_read_write_index(fd);
2375 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2376 lseek(fd, 0, SEEK_SET);
2378 /* init buffer input */
2379 c->buffer_ptr = c->buffer;
2380 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2381 c->stream->feed_opened = 1;
2385 static int http_receive_data(HTTPContext *c)
2389 if (c->buffer_end > c->buffer_ptr) {
2392 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2394 if (errno != EAGAIN && errno != EINTR) {
2395 /* error : close connection */
2398 } else if (len == 0) {
2399 /* end of connection : close it */
2402 c->buffer_ptr += len;
2403 c->data_count += len;
2404 update_datarate(&c->datarate, c->data_count);
2408 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2409 if (c->buffer[0] != 'f' ||
2410 c->buffer[1] != 'm') {
2411 http_log("Feed stream has become desynchronized -- disconnecting\n");
2416 if (c->buffer_ptr >= c->buffer_end) {
2417 FFStream *feed = c->stream;
2418 /* a packet has been received : write it in the store, except
2420 if (c->data_count > FFM_PACKET_SIZE) {
2422 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2423 /* XXX: use llseek or url_seek */
2424 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2425 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2427 feed->feed_write_index += FFM_PACKET_SIZE;
2428 /* update file size */
2429 if (feed->feed_write_index > c->stream->feed_size)
2430 feed->feed_size = feed->feed_write_index;
2432 /* handle wrap around if max file size reached */
2433 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2434 feed->feed_write_index = FFM_PACKET_SIZE;
2437 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2439 /* wake up any waiting connections */
2440 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2441 if (c1->state == HTTPSTATE_WAIT_FEED &&
2442 c1->stream->feed == c->stream->feed) {
2443 c1->state = HTTPSTATE_SEND_DATA;
2447 /* We have a header in our hands that contains useful data */
2449 AVInputFormat *fmt_in;
2450 ByteIOContext *pb = &s.pb;
2453 memset(&s, 0, sizeof(s));
2455 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2456 pb->buf_end = c->buffer_end; /* ?? */
2457 pb->is_streamed = 1;
2459 /* use feed output format name to find corresponding input format */
2460 fmt_in = av_find_input_format(feed->fmt->name);
2464 if (fmt_in->priv_data_size > 0) {
2465 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2471 if (fmt_in->read_header(&s, 0) < 0) {
2472 av_freep(&s.priv_data);
2476 /* Now we have the actual streams */
2477 if (s.nb_streams != feed->nb_streams) {
2478 av_freep(&s.priv_data);
2481 for (i = 0; i < s.nb_streams; i++) {
2482 memcpy(feed->streams[i]->codec,
2483 s.streams[i]->codec, sizeof(AVCodecContext));
2485 av_freep(&s.priv_data);
2487 c->buffer_ptr = c->buffer;
2492 c->stream->feed_opened = 0;
2497 /********************************************************************/
2500 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2507 switch(error_number) {
2508 #define DEF(n, c, s) case c: str = s; break;
2509 #include "rtspcodes.h"
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 pstrcpy(c->method, sizeof(c->method), cmd);
2554 pstrcpy(c->url, sizeof(c->url), url);
2555 pstrcpy(c->protocol, sizeof(c->protocol), 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 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2629 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2630 struct in_addr my_ip)
2632 ByteIOContext pb1, *pb = &pb1;
2633 int i, payload_type, port, private_payload_type, j;
2634 const char *ipstr, *title, *mediatype;
2637 if (url_open_dyn_buf(pb) < 0)
2640 /* general media info */
2642 url_fprintf(pb, "v=0\n");
2643 ipstr = inet_ntoa(my_ip);
2644 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2645 title = stream->title;
2646 if (title[0] == '\0')
2648 url_fprintf(pb, "s=%s\n", title);
2649 if (stream->comment[0] != '\0')
2650 url_fprintf(pb, "i=%s\n", stream->comment);
2651 if (stream->is_multicast) {
2652 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2654 /* for each stream, we output the necessary info */
2655 private_payload_type = RTP_PT_PRIVATE;
2656 for(i = 0; i < stream->nb_streams; i++) {
2657 st = stream->streams[i];
2658 if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
2659 mediatype = "video";
2661 switch(st->codec->codec_type) {
2662 case CODEC_TYPE_AUDIO:
2663 mediatype = "audio";
2665 case CODEC_TYPE_VIDEO:
2666 mediatype = "video";
2669 mediatype = "application";
2673 /* NOTE: the port indication is not correct in case of
2674 unicast. It is not an issue because RTSP gives it */
2675 payload_type = rtp_get_payload_type(st->codec);
2676 if (payload_type < 0)
2677 payload_type = private_payload_type++;
2678 if (stream->is_multicast) {
2679 port = stream->multicast_port + 2 * i;
2683 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2684 mediatype, port, payload_type);
2685 if (payload_type >= RTP_PT_PRIVATE) {
2686 /* for private payload type, we need to give more info */
2687 switch(st->codec->codec_id) {
2688 case CODEC_ID_MPEG4:
2691 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2692 payload_type, 90000);
2693 /* we must also add the mpeg4 header */
2694 data = st->codec->extradata;
2696 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2697 for(j=0;j<st->codec->extradata_size;j++) {
2698 url_fprintf(pb, "%02x", data[j]);
2700 url_fprintf(pb, "\n");
2705 /* XXX: add other codecs ? */
2709 url_fprintf(pb, "a=control:streamid=%d\n", i);
2711 return url_close_dyn_buf(pb, pbuffer);
2713 url_close_dyn_buf(pb, pbuffer);
2718 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2720 // rtsp_reply_header(c, RTSP_STATUS_OK);
2721 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2722 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2723 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2724 url_fprintf(c->pb, "\r\n");
2727 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2733 int content_length, len;
2734 struct sockaddr_in my_addr;
2736 /* find which url is asked */
2737 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2742 for(stream = first_stream; stream != NULL; stream = stream->next) {
2743 if (!stream->is_feed && stream->fmt == &rtp_muxer &&
2744 !strcmp(path, stream->filename)) {
2748 /* no stream found */
2749 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2753 /* prepare the media description in sdp format */
2755 /* get the host IP */
2756 len = sizeof(my_addr);
2757 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2758 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2759 if (content_length < 0) {
2760 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2763 rtsp_reply_header(c, RTSP_STATUS_OK);
2764 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2765 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2766 url_fprintf(c->pb, "\r\n");
2767 put_buffer(c->pb, content, content_length);
2770 static HTTPContext *find_rtp_session(const char *session_id)
2774 if (session_id[0] == '\0')
2777 for(c = first_http_ctx; c != NULL; c = c->next) {
2778 if (!strcmp(c->session_id, session_id))
2784 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2786 RTSPTransportField *th;
2789 for(i=0;i<h->nb_transports;i++) {
2790 th = &h->transports[i];
2791 if (th->protocol == protocol)
2797 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2801 int stream_index, port;
2806 RTSPTransportField *th;
2807 struct sockaddr_in dest_addr;
2808 RTSPActionServerSetup setup;
2810 /* find which url is asked */
2811 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2816 /* now check each stream */
2817 for(stream = first_stream; stream != NULL; stream = stream->next) {
2818 if (!stream->is_feed && stream->fmt == &rtp_muxer) {
2819 /* accept aggregate filenames only if single stream */
2820 if (!strcmp(path, stream->filename)) {
2821 if (stream->nb_streams != 1) {
2822 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2829 for(stream_index = 0; stream_index < stream->nb_streams;
2831 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2832 stream->filename, stream_index);
2833 if (!strcmp(path, buf))
2838 /* no stream found */
2839 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2843 /* generate session id if needed */
2844 if (h->session_id[0] == '\0') {
2845 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2846 av_random(&random_state), av_random(&random_state));
2849 /* find rtp session, and create it if none found */
2850 rtp_c = find_rtp_session(h->session_id);
2852 /* always prefer UDP */
2853 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2855 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2857 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2862 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2865 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2869 /* open input stream */
2870 if (open_input_stream(rtp_c, "") < 0) {
2871 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2876 /* test if stream is OK (test needed because several SETUP needs
2877 to be done for a given file) */
2878 if (rtp_c->stream != stream) {
2879 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2883 /* test if stream is already set up */
2884 if (rtp_c->rtp_ctx[stream_index]) {
2885 rtsp_reply_error(c, RTSP_STATUS_STATE);
2889 /* check transport */
2890 th = find_transport(h, rtp_c->rtp_protocol);
2891 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2892 th->client_port_min <= 0)) {
2893 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2897 /* setup default options */
2898 setup.transport_option[0] = '\0';
2899 dest_addr = rtp_c->from_addr;
2900 dest_addr.sin_port = htons(th->client_port_min);
2902 /* add transport option if needed */
2903 if (ff_rtsp_callback) {
2904 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2905 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2906 (char *)&setup, sizeof(setup),
2907 stream->rtsp_option) < 0) {
2908 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2911 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2915 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2916 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2920 /* now everything is OK, so we can send the connection parameters */
2921 rtsp_reply_header(c, RTSP_STATUS_OK);
2923 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2925 switch(rtp_c->rtp_protocol) {
2926 case RTSP_PROTOCOL_RTP_UDP:
2927 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2928 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2929 "client_port=%d-%d;server_port=%d-%d",
2930 th->client_port_min, th->client_port_min + 1,
2933 case RTSP_PROTOCOL_RTP_TCP:
2934 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2935 stream_index * 2, stream_index * 2 + 1);
2940 if (setup.transport_option[0] != '\0') {
2941 url_fprintf(c->pb, ";%s", setup.transport_option);
2943 url_fprintf(c->pb, "\r\n");
2946 url_fprintf(c->pb, "\r\n");
2950 /* find an rtp connection by using the session ID. Check consistency
2952 static HTTPContext *find_rtp_session_with_url(const char *url,
2953 const char *session_id)
2961 rtp_c = find_rtp_session(session_id);
2965 /* find which url is asked */
2966 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2970 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2971 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2972 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2973 rtp_c->stream->filename, s);
2974 if(!strncmp(path, buf, sizeof(buf))) {
2975 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2982 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2986 rtp_c = find_rtp_session_with_url(url, h->session_id);
2988 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2992 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2993 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2994 rtp_c->state != HTTPSTATE_READY) {
2995 rtsp_reply_error(c, RTSP_STATUS_STATE);
3000 /* XXX: seek in stream */
3001 if (h->range_start != AV_NOPTS_VALUE) {
3002 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3003 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3007 rtp_c->state = HTTPSTATE_SEND_DATA;
3009 /* now everything is OK, so we can send the connection parameters */
3010 rtsp_reply_header(c, RTSP_STATUS_OK);
3012 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3013 url_fprintf(c->pb, "\r\n");
3016 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3020 rtp_c = find_rtp_session_with_url(url, h->session_id);
3022 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3026 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3027 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3028 rtsp_reply_error(c, RTSP_STATUS_STATE);
3032 rtp_c->state = HTTPSTATE_READY;
3033 rtp_c->first_pts = AV_NOPTS_VALUE;
3034 /* now everything is OK, so we can send the connection parameters */
3035 rtsp_reply_header(c, RTSP_STATUS_OK);
3037 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3038 url_fprintf(c->pb, "\r\n");
3041 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3045 rtp_c = find_rtp_session_with_url(url, h->session_id);
3047 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3051 /* abort the session */
3052 close_connection(rtp_c);
3054 if (ff_rtsp_callback) {
3055 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3057 rtp_c->stream->rtsp_option);
3060 /* now everything is OK, so we can send the connection parameters */
3061 rtsp_reply_header(c, RTSP_STATUS_OK);
3063 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3064 url_fprintf(c->pb, "\r\n");
3068 /********************************************************************/
3071 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3072 FFStream *stream, const char *session_id,
3073 enum RTSPProtocol rtp_protocol)
3075 HTTPContext *c = NULL;
3076 const char *proto_str;
3078 /* XXX: should output a warning page when coming
3079 close to the connection limit */
3080 if (nb_connections >= nb_max_connections)
3083 /* add a new connection */
3084 c = av_mallocz(sizeof(HTTPContext));
3089 c->poll_entry = NULL;
3090 c->from_addr = *from_addr;
3091 c->buffer_size = IOBUFFER_INIT_SIZE;
3092 c->buffer = av_malloc(c->buffer_size);
3097 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3098 c->state = HTTPSTATE_READY;
3099 c->is_packetized = 1;
3100 c->rtp_protocol = rtp_protocol;
3102 /* protocol is shown in statistics */
3103 switch(c->rtp_protocol) {
3104 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3105 proto_str = "MCAST";
3107 case RTSP_PROTOCOL_RTP_UDP:
3110 case RTSP_PROTOCOL_RTP_TCP:
3117 pstrcpy(c->protocol, sizeof(c->protocol), "RTP/");
3118 pstrcat(c->protocol, sizeof(c->protocol), proto_str);
3120 current_bandwidth += stream->bandwidth;
3122 c->next = first_http_ctx;
3134 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3135 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3137 static int rtp_new_av_stream(HTTPContext *c,
3138 int stream_index, struct sockaddr_in *dest_addr,
3139 HTTPContext *rtsp_c)
3141 AVFormatContext *ctx;
3147 int max_packet_size;
3149 /* now we can open the relevant output stream */
3150 ctx = av_alloc_format_context();
3153 ctx->oformat = &rtp_muxer;
3155 st = av_mallocz(sizeof(AVStream));
3158 st->codec= avcodec_alloc_context();
3159 ctx->nb_streams = 1;
3160 ctx->streams[0] = st;
3162 if (!c->stream->feed ||
3163 c->stream->feed == c->stream) {
3164 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3167 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3171 /* build destination RTP address */
3172 ipaddr = inet_ntoa(dest_addr->sin_addr);
3174 switch(c->rtp_protocol) {
3175 case RTSP_PROTOCOL_RTP_UDP:
3176 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3179 /* XXX: also pass as parameter to function ? */
3180 if (c->stream->is_multicast) {
3182 ttl = c->stream->multicast_ttl;
3185 snprintf(ctx->filename, sizeof(ctx->filename),
3186 "rtp://%s:%d?multicast=1&ttl=%d",
3187 ipaddr, ntohs(dest_addr->sin_port), ttl);
3189 snprintf(ctx->filename, sizeof(ctx->filename),
3190 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3193 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3195 c->rtp_handles[stream_index] = h;
3196 max_packet_size = url_get_max_packet_size(h);
3198 case RTSP_PROTOCOL_RTP_TCP:
3201 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3207 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3208 ipaddr, ntohs(dest_addr->sin_port),
3210 c->stream->filename, stream_index, c->protocol);
3212 /* normally, no packets should be output here, but the packet size may be checked */
3213 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3214 /* XXX: close stream */
3217 av_set_parameters(ctx, NULL);
3218 if (av_write_header(ctx) < 0) {
3225 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3228 c->rtp_ctx[stream_index] = ctx;
3232 /********************************************************************/
3233 /* ffserver initialization */
3235 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3239 fst = av_mallocz(sizeof(AVStream));
3242 fst->codec= avcodec_alloc_context();
3243 fst->priv_data = av_mallocz(sizeof(FeedData));
3244 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3245 fst->codec->coded_frame = &dummy_frame;
3246 fst->index = stream->nb_streams;
3247 av_set_pts_info(fst, 33, 1, 90000);
3248 stream->streams[stream->nb_streams++] = fst;
3252 /* return the stream number in the feed */
3253 static int add_av_stream(FFStream *feed, AVStream *st)
3256 AVCodecContext *av, *av1;
3260 for(i=0;i<feed->nb_streams;i++) {
3261 st = feed->streams[i];
3263 if (av1->codec_id == av->codec_id &&
3264 av1->codec_type == av->codec_type &&
3265 av1->bit_rate == av->bit_rate) {
3267 switch(av->codec_type) {
3268 case CODEC_TYPE_AUDIO:
3269 if (av1->channels == av->channels &&
3270 av1->sample_rate == av->sample_rate)
3273 case CODEC_TYPE_VIDEO:
3274 if (av1->width == av->width &&
3275 av1->height == av->height &&
3276 av1->time_base.den == av->time_base.den &&
3277 av1->time_base.num == av->time_base.num &&
3278 av1->gop_size == av->gop_size)
3287 fst = add_av_stream1(feed, av);
3290 return feed->nb_streams - 1;
3295 static void remove_stream(FFStream *stream)
3299 while (*ps != NULL) {
3300 if (*ps == stream) {
3308 /* specific mpeg4 handling : we extract the raw parameters */
3309 static void extract_mpeg4_header(AVFormatContext *infile)
3311 int mpeg4_count, i, size;
3317 for(i=0;i<infile->nb_streams;i++) {
3318 st = infile->streams[i];
3319 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3320 st->codec->extradata_size == 0) {
3327 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3328 while (mpeg4_count > 0) {
3329 if (av_read_packet(infile, &pkt) < 0)
3331 st = infile->streams[pkt.stream_index];
3332 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3333 st->codec->extradata_size == 0) {
3334 av_freep(&st->codec->extradata);
3335 /* fill extradata with the header */
3336 /* XXX: we make hard suppositions here ! */
3338 while (p < pkt.data + pkt.size - 4) {
3339 /* stop when vop header is found */
3340 if (p[0] == 0x00 && p[1] == 0x00 &&
3341 p[2] == 0x01 && p[3] == 0xb6) {
3342 size = p - pkt.data;
3343 // av_hex_dump(pkt.data, size);
3344 st->codec->extradata = av_malloc(size);
3345 st->codec->extradata_size = size;
3346 memcpy(st->codec->extradata, pkt.data, size);
3353 av_free_packet(&pkt);
3357 /* compute the needed AVStream for each file */
3358 static void build_file_streams(void)
3360 FFStream *stream, *stream_next;
3361 AVFormatContext *infile;
3364 /* gather all streams */
3365 for(stream = first_stream; stream != NULL; stream = stream_next) {
3366 stream_next = stream->next;
3367 if (stream->stream_type == STREAM_TYPE_LIVE &&
3369 /* the stream comes from a file */
3370 /* try to open the file */
3372 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3373 if (stream->fmt == &rtp_muxer) {
3374 /* specific case : if transport stream output to RTP,
3375 we use a raw transport stream reader */
3376 stream->ap_in->mpeg2ts_raw = 1;
3377 stream->ap_in->mpeg2ts_compute_pcr = 1;
3380 if (av_open_input_file(&infile, stream->feed_filename,
3381 stream->ifmt, 0, stream->ap_in) < 0) {
3382 http_log("%s not found", stream->feed_filename);
3383 /* remove stream (no need to spend more time on it) */
3385 remove_stream(stream);
3387 /* find all the AVStreams inside and reference them in
3389 if (av_find_stream_info(infile) < 0) {
3390 http_log("Could not find codec parameters from '%s'",
3391 stream->feed_filename);
3392 av_close_input_file(infile);
3395 extract_mpeg4_header(infile);
3397 for(i=0;i<infile->nb_streams;i++) {
3398 add_av_stream1(stream, infile->streams[i]->codec);
3400 av_close_input_file(infile);
3406 /* compute the needed AVStream for each feed */
3407 static void build_feed_streams(void)
3409 FFStream *stream, *feed;
3412 /* gather all streams */
3413 for(stream = first_stream; stream != NULL; stream = stream->next) {
3414 feed = stream->feed;
3416 if (!stream->is_feed) {
3417 /* we handle a stream coming from a feed */
3418 for(i=0;i<stream->nb_streams;i++) {
3419 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3425 /* gather all streams */
3426 for(stream = first_stream; stream != NULL; stream = stream->next) {
3427 feed = stream->feed;
3429 if (stream->is_feed) {
3430 for(i=0;i<stream->nb_streams;i++) {
3431 stream->feed_streams[i] = i;
3437 /* create feed files if needed */
3438 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3441 if (url_exist(feed->feed_filename)) {
3442 /* See if it matches */
3446 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3447 /* Now see if it matches */
3448 if (s->nb_streams == feed->nb_streams) {
3450 for(i=0;i<s->nb_streams;i++) {
3452 sf = feed->streams[i];
3455 if (sf->index != ss->index ||
3457 printf("Index & Id do not match for stream %d (%s)\n",
3458 i, feed->feed_filename);
3461 AVCodecContext *ccf, *ccs;
3465 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3467 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3468 printf("Codecs do not match for stream %d\n", i);
3470 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3471 printf("Codec bitrates do not match for stream %d\n", i);
3473 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3474 if (CHECK_CODEC(time_base.den) ||
3475 CHECK_CODEC(time_base.num) ||
3476 CHECK_CODEC(width) ||
3477 CHECK_CODEC(height)) {
3478 printf("Codec width, height and framerate do not match for stream %d\n", i);
3481 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3482 if (CHECK_CODEC(sample_rate) ||
3483 CHECK_CODEC(channels) ||
3484 CHECK_CODEC(frame_size)) {
3485 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3489 printf("Unknown codec type\n");
3498 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3499 feed->feed_filename, s->nb_streams, feed->nb_streams);
3502 av_close_input_file(s);
3504 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3505 feed->feed_filename);
3508 if (feed->readonly) {
3509 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3510 feed->feed_filename);
3513 unlink(feed->feed_filename);
3516 if (!url_exist(feed->feed_filename)) {
3517 AVFormatContext s1, *s = &s1;
3519 if (feed->readonly) {
3520 printf("Unable to create feed file '%s' as it is marked readonly\n",
3521 feed->feed_filename);
3525 /* only write the header of the ffm file */
3526 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3527 fprintf(stderr, "Could not open output feed file '%s'\n",
3528 feed->feed_filename);
3531 s->oformat = feed->fmt;
3532 s->nb_streams = feed->nb_streams;
3533 for(i=0;i<s->nb_streams;i++) {
3535 st = feed->streams[i];
3538 av_set_parameters(s, NULL);
3539 if (av_write_header(s) < 0) {
3540 fprintf(stderr, "Container doesn't supports the required parameters\n");
3543 /* XXX: need better api */
3544 av_freep(&s->priv_data);
3547 /* get feed size and write index */
3548 fd = open(feed->feed_filename, O_RDONLY);
3550 fprintf(stderr, "Could not open output feed file '%s'\n",
3551 feed->feed_filename);
3555 feed->feed_write_index = ffm_read_write_index(fd);
3556 feed->feed_size = lseek(fd, 0, SEEK_END);
3557 /* ensure that we do not wrap before the end of file */
3558 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3559 feed->feed_max_size = feed->feed_size;
3565 /* compute the bandwidth used by each stream */
3566 static void compute_bandwidth(void)
3571 for(stream = first_stream; stream != NULL; stream = stream->next) {
3573 for(i=0;i<stream->nb_streams;i++) {
3574 AVStream *st = stream->streams[i];
3575 switch(st->codec->codec_type) {
3576 case CODEC_TYPE_AUDIO:
3577 case CODEC_TYPE_VIDEO:
3578 bandwidth += st->codec->bit_rate;
3584 stream->bandwidth = (bandwidth + 999) / 1000;
3588 static void get_arg(char *buf, int buf_size, const char **pp)
3595 while (isspace(*p)) p++;
3598 if (*p == '\"' || *p == '\'')
3610 if ((q - buf) < buf_size - 1)
3615 if (quote && *p == quote)
3620 /* add a codec and set the default parameters */
3621 static void add_codec(FFStream *stream, AVCodecContext *av)
3625 /* compute default parameters */
3626 switch(av->codec_type) {
3627 case CODEC_TYPE_AUDIO:
3628 if (av->bit_rate == 0)
3629 av->bit_rate = 64000;
3630 if (av->sample_rate == 0)
3631 av->sample_rate = 22050;
3632 if (av->channels == 0)
3635 case CODEC_TYPE_VIDEO:
3636 if (av->bit_rate == 0)
3637 av->bit_rate = 64000;
3638 if (av->time_base.num == 0){
3639 av->time_base.den = 5;
3640 av->time_base.num = 1;
3642 if (av->width == 0 || av->height == 0) {
3646 /* Bitrate tolerance is less for streaming */
3647 if (av->bit_rate_tolerance == 0)
3648 av->bit_rate_tolerance = av->bit_rate / 4;
3653 if (av->max_qdiff == 0)
3655 av->qcompress = 0.5;
3658 if (!av->nsse_weight)
3659 av->nsse_weight = 8;
3661 av->frame_skip_cmp = FF_CMP_DCTMAX;
3662 av->me_method = ME_EPZS;
3663 av->rc_buffer_aggressivity = 1.0;
3666 av->rc_eq = "tex^qComp";
3667 if (!av->i_quant_factor)
3668 av->i_quant_factor = -0.8;
3669 if (!av->b_quant_factor)
3670 av->b_quant_factor = 1.25;
3671 if (!av->b_quant_offset)
3672 av->b_quant_offset = 1.25;
3673 if (!av->rc_max_rate)
3674 av->rc_max_rate = av->bit_rate * 2;
3676 if (av->rc_max_rate && !av->rc_buffer_size) {
3677 av->rc_buffer_size = av->rc_max_rate;
3686 st = av_mallocz(sizeof(AVStream));
3689 st->codec = avcodec_alloc_context();
3690 stream->streams[stream->nb_streams++] = st;
3691 memcpy(st->codec, av, sizeof(AVCodecContext));
3694 static int opt_audio_codec(const char *arg)
3700 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3705 return CODEC_ID_NONE;
3711 static int opt_video_codec(const char *arg)
3717 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3722 return CODEC_ID_NONE;
3728 /* simplistic plugin support */
3731 static void load_module(const char *filename)
3734 void (*init_func)(void);
3735 dll = dlopen(filename, RTLD_NOW);
3737 fprintf(stderr, "Could not load module '%s' - %s\n",
3738 filename, dlerror());
3742 init_func = dlsym(dll, "ffserver_module_init");
3745 "%s: init function 'ffserver_module_init()' not found\n",
3754 static int parse_ffconfig(const char *filename)
3761 int val, errors, line_num;
3762 FFStream **last_stream, *stream, *redirect;
3763 FFStream **last_feed, *feed;
3764 AVCodecContext audio_enc, video_enc;
3765 int audio_id, video_id;
3767 f = fopen(filename, "r");
3775 first_stream = NULL;
3776 last_stream = &first_stream;
3778 last_feed = &first_feed;
3782 audio_id = CODEC_ID_NONE;
3783 video_id = CODEC_ID_NONE;
3785 if (fgets(line, sizeof(line), f) == NULL)
3791 if (*p == '\0' || *p == '#')
3794 get_arg(cmd, sizeof(cmd), &p);
3796 if (!strcasecmp(cmd, "Port")) {
3797 get_arg(arg, sizeof(arg), &p);
3798 my_http_addr.sin_port = htons (atoi(arg));
3799 } else if (!strcasecmp(cmd, "BindAddress")) {
3800 get_arg(arg, sizeof(arg), &p);
3801 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3802 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3803 filename, line_num, arg);
3806 } else if (!strcasecmp(cmd, "NoDaemon")) {
3807 ffserver_daemon = 0;
3808 } else if (!strcasecmp(cmd, "RTSPPort")) {
3809 get_arg(arg, sizeof(arg), &p);
3810 my_rtsp_addr.sin_port = htons (atoi(arg));
3811 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3812 get_arg(arg, sizeof(arg), &p);
3813 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3814 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3815 filename, line_num, arg);
3818 } else if (!strcasecmp(cmd, "MaxClients")) {
3819 get_arg(arg, sizeof(arg), &p);
3821 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3822 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3823 filename, line_num, arg);
3826 nb_max_connections = val;
3828 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3829 get_arg(arg, sizeof(arg), &p);
3831 if (val < 10 || val > 100000) {
3832 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3833 filename, line_num, arg);
3836 max_bandwidth = val;
3838 } else if (!strcasecmp(cmd, "CustomLog")) {
3839 get_arg(logfilename, sizeof(logfilename), &p);
3840 } else if (!strcasecmp(cmd, "<Feed")) {
3841 /*********************************************/
3842 /* Feed related options */
3844 if (stream || feed) {
3845 fprintf(stderr, "%s:%d: Already in a tag\n",
3846 filename, line_num);
3848 feed = av_mallocz(sizeof(FFStream));
3849 /* add in stream list */
3850 *last_stream = feed;
3851 last_stream = &feed->next;
3852 /* add in feed list */
3854 last_feed = &feed->next_feed;
3856 get_arg(feed->filename, sizeof(feed->filename), &p);
3857 q = strrchr(feed->filename, '>');
3860 feed->fmt = guess_format("ffm", NULL, NULL);
3861 /* defaut feed file */
3862 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3863 "/tmp/%s.ffm", feed->filename);
3864 feed->feed_max_size = 5 * 1024 * 1024;
3866 feed->feed = feed; /* self feeding :-) */
3868 } else if (!strcasecmp(cmd, "Launch")) {
3872 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3874 for (i = 0; i < 62; i++) {
3877 get_arg(argbuf, sizeof(argbuf), &p);
3881 feed->child_argv[i] = av_malloc(strlen(argbuf) + 1);
3882 strcpy(feed->child_argv[i], argbuf);
3885 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3887 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3889 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3890 inet_ntoa(my_http_addr.sin_addr),
3891 ntohs(my_http_addr.sin_port), feed->filename);
3896 fprintf(stdout, "Launch commandline: ");
3897 for (j = 0; j <= i; j++)
3898 fprintf(stdout, "%s ", feed->child_argv[j]);
3899 fprintf(stdout, "\n");
3902 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3904 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3906 } else if (stream) {
3907 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3909 } else if (!strcasecmp(cmd, "File")) {
3911 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3912 } else if (stream) {
3913 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3915 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3920 get_arg(arg, sizeof(arg), &p);
3922 fsize = strtod(p1, (char **)&p1);
3923 switch(toupper(*p1)) {
3928 fsize *= 1024 * 1024;
3931 fsize *= 1024 * 1024 * 1024;
3934 feed->feed_max_size = (int64_t)fsize;
3936 } else if (!strcasecmp(cmd, "</Feed>")) {
3938 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3939 filename, line_num);
3943 /* Make sure that we start out clean */
3944 if (unlink(feed->feed_filename) < 0
3945 && errno != ENOENT) {
3946 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3947 filename, line_num, feed->feed_filename, strerror(errno));
3953 } else if (!strcasecmp(cmd, "<Stream")) {
3954 /*********************************************/
3955 /* Stream related options */
3957 if (stream || feed) {
3958 fprintf(stderr, "%s:%d: Already in a tag\n",
3959 filename, line_num);
3961 stream = av_mallocz(sizeof(FFStream));
3962 *last_stream = stream;
3963 last_stream = &stream->next;
3965 get_arg(stream->filename, sizeof(stream->filename), &p);
3966 q = strrchr(stream->filename, '>');
3969 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3970 memset(&audio_enc, 0, sizeof(AVCodecContext));
3971 memset(&video_enc, 0, sizeof(AVCodecContext));
3972 audio_id = CODEC_ID_NONE;
3973 video_id = CODEC_ID_NONE;
3975 audio_id = stream->fmt->audio_codec;
3976 video_id = stream->fmt->video_codec;
3979 } else if (!strcasecmp(cmd, "Feed")) {
3980 get_arg(arg, sizeof(arg), &p);
3985 while (sfeed != NULL) {
3986 if (!strcmp(sfeed->filename, arg))
3988 sfeed = sfeed->next_feed;
3991 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3992 filename, line_num, arg);
3994 stream->feed = sfeed;
3997 } else if (!strcasecmp(cmd, "Format")) {
3998 get_arg(arg, sizeof(arg), &p);
3999 if (!strcmp(arg, "status")) {
4000 stream->stream_type = STREAM_TYPE_STATUS;
4003 stream->stream_type = STREAM_TYPE_LIVE;
4004 /* jpeg cannot be used here, so use single frame jpeg */
4005 if (!strcmp(arg, "jpeg"))
4006 strcpy(arg, "mjpeg");
4007 stream->fmt = guess_stream_format(arg, NULL, NULL);
4009 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4010 filename, line_num, arg);
4015 audio_id = stream->fmt->audio_codec;
4016 video_id = stream->fmt->video_codec;
4018 } else if (!strcasecmp(cmd, "InputFormat")) {
4019 stream->ifmt = av_find_input_format(arg);
4020 if (!stream->ifmt) {
4021 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4022 filename, line_num, arg);
4024 } else if (!strcasecmp(cmd, "FaviconURL")) {
4025 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4026 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4028 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4029 filename, line_num);
4032 } else if (!strcasecmp(cmd, "Author")) {
4034 get_arg(stream->author, sizeof(stream->author), &p);
4036 } else if (!strcasecmp(cmd, "Comment")) {
4038 get_arg(stream->comment, sizeof(stream->comment), &p);
4040 } else if (!strcasecmp(cmd, "Copyright")) {
4042 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4044 } else if (!strcasecmp(cmd, "Title")) {
4046 get_arg(stream->title, sizeof(stream->title), &p);
4048 } else if (!strcasecmp(cmd, "Preroll")) {
4049 get_arg(arg, sizeof(arg), &p);
4051 stream->prebuffer = atof(arg) * 1000;
4053 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4055 stream->send_on_key = 1;
4057 } else if (!strcasecmp(cmd, "AudioCodec")) {
4058 get_arg(arg, sizeof(arg), &p);
4059 audio_id = opt_audio_codec(arg);
4060 if (audio_id == CODEC_ID_NONE) {
4061 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4062 filename, line_num, arg);
4065 } else if (!strcasecmp(cmd, "VideoCodec")) {
4066 get_arg(arg, sizeof(arg), &p);
4067 video_id = opt_video_codec(arg);
4068 if (video_id == CODEC_ID_NONE) {
4069 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4070 filename, line_num, arg);
4073 } else if (!strcasecmp(cmd, "MaxTime")) {
4074 get_arg(arg, sizeof(arg), &p);
4076 stream->max_time = atof(arg) * 1000;
4078 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4079 get_arg(arg, sizeof(arg), &p);
4081 audio_enc.bit_rate = atoi(arg) * 1000;
4083 } else if (!strcasecmp(cmd, "AudioChannels")) {
4084 get_arg(arg, sizeof(arg), &p);
4086 audio_enc.channels = atoi(arg);
4088 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4089 get_arg(arg, sizeof(arg), &p);
4091 audio_enc.sample_rate = atoi(arg);
4093 } else if (!strcasecmp(cmd, "AudioQuality")) {
4094 get_arg(arg, sizeof(arg), &p);
4096 // audio_enc.quality = atof(arg) * 1000;
4098 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4100 int minrate, maxrate;
4102 get_arg(arg, sizeof(arg), &p);
4104 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4105 video_enc.rc_min_rate = minrate * 1000;
4106 video_enc.rc_max_rate = maxrate * 1000;
4108 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4109 filename, line_num, arg);
4113 } else if (!strcasecmp(cmd, "Debug")) {
4115 get_arg(arg, sizeof(arg), &p);
4116 video_enc.debug = strtol(arg,0,0);
4118 } else if (!strcasecmp(cmd, "Strict")) {
4120 get_arg(arg, sizeof(arg), &p);
4121 video_enc.strict_std_compliance = atoi(arg);
4123 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4125 get_arg(arg, sizeof(arg), &p);
4126 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4128 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4130 get_arg(arg, sizeof(arg), &p);
4131 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4133 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4134 get_arg(arg, sizeof(arg), &p);
4136 video_enc.bit_rate = atoi(arg) * 1000;
4138 } else if (!strcasecmp(cmd, "VideoSize")) {
4139 get_arg(arg, sizeof(arg), &p);
4141 parse_image_size(&video_enc.width, &video_enc.height, arg);
4142 if ((video_enc.width % 16) != 0 ||
4143 (video_enc.height % 16) != 0) {
4144 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4145 filename, line_num);
4149 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4150 get_arg(arg, sizeof(arg), &p);
4152 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4153 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4155 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4156 get_arg(arg, sizeof(arg), &p);
4158 video_enc.gop_size = atoi(arg);
4160 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4162 video_enc.gop_size = 1;
4164 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4166 video_enc.mb_decision = FF_MB_DECISION_BITS;
4168 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4170 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4171 video_enc.flags |= CODEC_FLAG_4MV;
4173 } else if (!strcasecmp(cmd, "VideoTag")) {
4174 get_arg(arg, sizeof(arg), &p);
4175 if ((strlen(arg) == 4) && stream) {
4176 video_enc.codec_tag = ff_get_fourcc(arg);
4178 } else if (!strcasecmp(cmd, "BitExact")) {
4180 video_enc.flags |= CODEC_FLAG_BITEXACT;
4182 } else if (!strcasecmp(cmd, "DctFastint")) {
4184 video_enc.dct_algo = FF_DCT_FASTINT;
4186 } else if (!strcasecmp(cmd, "IdctSimple")) {
4188 video_enc.idct_algo = FF_IDCT_SIMPLE;
4190 } else if (!strcasecmp(cmd, "Qscale")) {
4191 get_arg(arg, sizeof(arg), &p);
4193 video_enc.flags |= CODEC_FLAG_QSCALE;
4194 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4196 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4197 get_arg(arg, sizeof(arg), &p);
4199 video_enc.max_qdiff = atoi(arg);
4200 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4201 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4202 filename, line_num);
4206 } else if (!strcasecmp(cmd, "VideoQMax")) {
4207 get_arg(arg, sizeof(arg), &p);
4209 video_enc.qmax = atoi(arg);
4210 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4211 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4212 filename, line_num);
4216 } else if (!strcasecmp(cmd, "VideoQMin")) {
4217 get_arg(arg, sizeof(arg), &p);
4219 video_enc.qmin = atoi(arg);
4220 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4221 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4222 filename, line_num);
4226 } else if (!strcasecmp(cmd, "LumaElim")) {
4227 get_arg(arg, sizeof(arg), &p);
4229 video_enc.luma_elim_threshold = atoi(arg);
4231 } else if (!strcasecmp(cmd, "ChromaElim")) {
4232 get_arg(arg, sizeof(arg), &p);
4234 video_enc.chroma_elim_threshold = atoi(arg);
4236 } else if (!strcasecmp(cmd, "LumiMask")) {
4237 get_arg(arg, sizeof(arg), &p);
4239 video_enc.lumi_masking = atof(arg);
4241 } else if (!strcasecmp(cmd, "DarkMask")) {
4242 get_arg(arg, sizeof(arg), &p);
4244 video_enc.dark_masking = atof(arg);
4246 } else if (!strcasecmp(cmd, "NoVideo")) {
4247 video_id = CODEC_ID_NONE;
4248 } else if (!strcasecmp(cmd, "NoAudio")) {
4249 audio_id = CODEC_ID_NONE;
4250 } else if (!strcasecmp(cmd, "ACL")) {
4254 get_arg(arg, sizeof(arg), &p);
4255 if (strcasecmp(arg, "allow") == 0) {
4256 acl.action = IP_ALLOW;
4257 } else if (strcasecmp(arg, "deny") == 0) {
4258 acl.action = IP_DENY;
4260 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4261 filename, line_num, arg);
4265 get_arg(arg, sizeof(arg), &p);
4267 he = gethostbyname(arg);
4269 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4270 filename, line_num, arg);
4273 /* Only take the first */
4274 acl.first.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4275 acl.last = acl.first;
4278 get_arg(arg, sizeof(arg), &p);
4281 he = gethostbyname(arg);
4283 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4284 filename, line_num, arg);
4287 /* Only take the first */
4288 acl.last.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4293 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4294 IPAddressACL **naclp = 0;
4300 naclp = &stream->acl;
4304 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4305 filename, line_num);
4311 naclp = &(*naclp)->next;
4316 } else if (!strcasecmp(cmd, "RTSPOption")) {
4317 get_arg(arg, sizeof(arg), &p);
4319 av_freep(&stream->rtsp_option);
4320 /* XXX: av_strdup ? */
4321 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4322 if (stream->rtsp_option) {
4323 strcpy(stream->rtsp_option, arg);
4326 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4327 get_arg(arg, sizeof(arg), &p);
4329 if (!inet_aton(arg, &stream->multicast_ip)) {
4330 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4331 filename, line_num, arg);
4334 stream->is_multicast = 1;
4335 stream->loop = 1; /* default is looping */
4337 } else if (!strcasecmp(cmd, "MulticastPort")) {
4338 get_arg(arg, sizeof(arg), &p);
4340 stream->multicast_port = atoi(arg);
4342 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4343 get_arg(arg, sizeof(arg), &p);
4345 stream->multicast_ttl = atoi(arg);
4347 } else if (!strcasecmp(cmd, "NoLoop")) {
4351 } else if (!strcasecmp(cmd, "</Stream>")) {
4353 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4354 filename, line_num);
4357 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4358 if (audio_id != CODEC_ID_NONE) {
4359 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4360 audio_enc.codec_id = audio_id;
4361 add_codec(stream, &audio_enc);
4363 if (video_id != CODEC_ID_NONE) {
4364 video_enc.codec_type = CODEC_TYPE_VIDEO;
4365 video_enc.codec_id = video_id;
4366 add_codec(stream, &video_enc);
4370 } else if (!strcasecmp(cmd, "<Redirect")) {
4371 /*********************************************/
4373 if (stream || feed || redirect) {
4374 fprintf(stderr, "%s:%d: Already in a tag\n",
4375 filename, line_num);
4378 redirect = av_mallocz(sizeof(FFStream));
4379 *last_stream = redirect;
4380 last_stream = &redirect->next;
4382 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4383 q = strrchr(redirect->filename, '>');
4386 redirect->stream_type = STREAM_TYPE_REDIRECT;
4388 } else if (!strcasecmp(cmd, "URL")) {
4390 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4392 } else if (!strcasecmp(cmd, "</Redirect>")) {
4394 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4395 filename, line_num);
4398 if (!redirect->feed_filename[0]) {
4399 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4400 filename, line_num);
4404 } else if (!strcasecmp(cmd, "LoadModule")) {
4405 get_arg(arg, sizeof(arg), &p);
4409 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4410 filename, line_num, arg);
4414 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4415 filename, line_num, cmd);
4429 static void write_packet(FFCodec *ffenc,
4430 uint8_t *buf, int size)
4433 AVCodecContext *enc = &ffenc->enc;
4435 mk_header(&hdr, enc, size);
4436 wptr = http_fifo.wptr;
4437 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4438 fifo_write(&http_fifo, buf, size, &wptr);
4439 /* atomic modification of wptr */
4440 http_fifo.wptr = wptr;
4441 ffenc->data_count += size;
4442 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4446 static void show_banner(void)
4448 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4451 static void show_help(void)
4454 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4455 "Hyper fast multi format Audio/Video streaming server\n"
4457 "-L : print the LICENSE\n"
4459 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4463 static void show_license(void)
4467 "FFmpeg is free software; you can redistribute it and/or\n"
4468 "modify it under the terms of the GNU Lesser General Public\n"
4469 "License as published by the Free Software Foundation; either\n"
4470 "version 2.1 of the License, or (at your option) any later version.\n"
4472 "FFmpeg is distributed in the hope that it will be useful,\n"
4473 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4474 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4475 "Lesser General Public License for more details.\n"
4477 "You should have received a copy of the GNU Lesser General Public\n"
4478 "License along with FFmpeg; if not, write to the Free Software\n"
4479 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
4483 static void handle_child_exit(int sig)
4488 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4491 for (feed = first_feed; feed; feed = feed->next) {
4492 if (feed->pid == pid) {
4493 int uptime = time(0) - feed->pid_start;
4496 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4499 /* Turn off any more restarts */
4500 feed->child_argv = 0;
4506 need_to_start_children = 1;
4509 int main(int argc, char **argv)
4511 const char *config_filename;
4513 struct sigaction sigact;
4517 config_filename = "/etc/ffserver.conf";
4519 my_program_name = argv[0];
4520 my_program_dir = getcwd(0, 0);
4521 ffserver_daemon = 1;
4524 c = getopt(argc, argv, "ndLh?f:");
4540 ffserver_daemon = 0;
4543 config_filename = optarg;
4550 putenv("http_proxy"); /* Kill the http_proxy */
4552 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4554 /* address on which the server will handle HTTP connections */
4555 my_http_addr.sin_family = AF_INET;
4556 my_http_addr.sin_port = htons (8080);
4557 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4559 /* address on which the server will handle RTSP connections */
4560 my_rtsp_addr.sin_family = AF_INET;
4561 my_rtsp_addr.sin_port = htons (5454);
4562 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4564 nb_max_connections = 5;
4565 max_bandwidth = 1000;
4566 first_stream = NULL;
4567 logfilename[0] = '\0';
4569 memset(&sigact, 0, sizeof(sigact));
4570 sigact.sa_handler = handle_child_exit;
4571 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4572 sigaction(SIGCHLD, &sigact, 0);
4574 if (parse_ffconfig(config_filename) < 0) {
4575 fprintf(stderr, "Incorrect config file - exiting.\n");
4579 build_file_streams();
4581 build_feed_streams();
4583 compute_bandwidth();
4585 /* put the process in background and detach it from its TTY */
4586 if (ffserver_daemon) {
4593 } else if (pid > 0) {
4601 open("/dev/null", O_RDWR);
4602 if (strcmp(logfilename, "-") != 0) {
4612 signal(SIGPIPE, SIG_IGN);
4614 /* open log file if needed */
4615 if (logfilename[0] != '\0') {
4616 if (!strcmp(logfilename, "-"))
4619 logfile = fopen(logfilename, "w");
4622 if (http_server() < 0) {
4623 fprintf(stderr, "Could not start server\n");