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 already streaming this feed, dont let start an another feeder */
1303 if (stream->feed_opened) {
1304 snprintf(msg, sizeof(msg), "This feed is already being received.");
1308 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1309 current_bandwidth += stream->bandwidth;
1312 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1313 c->http_error = 200;
1315 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1316 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1317 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1318 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1319 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");
1320 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",
1321 current_bandwidth, max_bandwidth);
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1324 /* prepare output buffer */
1325 c->buffer_ptr = c->buffer;
1327 c->state = HTTPSTATE_SEND_HEADER;
1331 if (redir_type != REDIR_NONE) {
1334 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1335 if (strncasecmp(p, "Host:", 5) == 0) {
1339 p = strchr(p, '\n');
1350 while (isspace(*hostinfo))
1353 eoh = strchr(hostinfo, '\n');
1355 if (eoh[-1] == '\r')
1358 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1359 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1360 hostbuf[eoh - hostinfo] = 0;
1362 c->http_error = 200;
1364 switch(redir_type) {
1366 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1367 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1368 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1369 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1370 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1371 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1372 hostbuf, filename, info);
1373 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1376 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1377 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1378 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1379 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1380 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1381 hostbuf, filename, info);
1384 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1385 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1386 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1387 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1388 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1389 hostbuf, filename, info);
1393 char hostname[256], *p;
1394 /* extract only hostname */
1395 pstrcpy(hostname, sizeof(hostname), hostbuf);
1396 p = strrchr(hostname, ':');
1399 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1400 /* XXX: incorrect mime type ? */
1401 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1402 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1403 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1404 hostname, ntohs(my_rtsp_addr.sin_port),
1411 int sdp_data_size, len;
1412 struct sockaddr_in my_addr;
1414 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1415 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1416 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1418 len = sizeof(my_addr);
1419 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1421 /* XXX: should use a dynamic buffer */
1422 sdp_data_size = prepare_sdp_description(stream,
1425 if (sdp_data_size > 0) {
1426 memcpy(q, sdp_data, sdp_data_size);
1438 /* prepare output buffer */
1439 c->buffer_ptr = c->buffer;
1441 c->state = HTTPSTATE_SEND_HEADER;
1447 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1451 stream->conns_served++;
1453 /* XXX: add there authenticate and IP match */
1456 /* if post, it means a feed is being sent */
1457 if (!stream->is_feed) {
1458 /* However it might be a status report from WMP! Lets log the data
1459 * as it might come in handy one day
1464 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1465 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1469 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1470 client_id = strtol(p + 18, 0, 10);
1472 p = strchr(p, '\n');
1480 char *eol = strchr(logline, '\n');
1485 if (eol[-1] == '\r')
1487 http_log("%.*s\n", (int) (eol - logline), logline);
1488 c->suppress_log = 1;
1493 http_log("\nGot request:\n%s\n", c->buffer);
1496 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1499 /* Now we have to find the client_id */
1500 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1501 if (wmpc->wmp_client_id == client_id)
1506 if (modify_current_stream(wmpc, ratebuf)) {
1507 wmpc->switch_pending = 1;
1512 snprintf(msg, sizeof(msg), "POST command not handled");
1516 if (http_start_receive_data(c) < 0) {
1517 snprintf(msg, sizeof(msg), "could not open feed");
1521 c->state = HTTPSTATE_RECEIVE_DATA;
1526 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1527 http_log("\nGot request:\n%s\n", c->buffer);
1531 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1534 /* open input stream */
1535 if (open_input_stream(c, info) < 0) {
1536 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1540 /* prepare http header */
1542 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1543 mime_type = c->stream->fmt->mime_type;
1545 mime_type = "application/x-octet_stream";
1546 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1548 /* for asf, we need extra headers */
1549 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1550 /* Need to allocate a client id */
1552 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1554 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);
1556 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1557 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1559 /* prepare output buffer */
1561 c->buffer_ptr = c->buffer;
1563 c->state = HTTPSTATE_SEND_HEADER;
1566 c->http_error = 404;
1568 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1569 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1570 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1571 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1572 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1573 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1574 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1576 /* prepare output buffer */
1577 c->buffer_ptr = c->buffer;
1579 c->state = HTTPSTATE_SEND_HEADER;
1583 c->http_error = 200; /* horrible : we use this value to avoid
1584 going to the send data state */
1585 c->state = HTTPSTATE_SEND_HEADER;
1589 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1591 static const char *suffix = " kMGTP";
1594 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1597 url_fprintf(pb, "%"PRId64"%c", count, *s);
1600 static void compute_stats(HTTPContext *c)
1607 ByteIOContext pb1, *pb = &pb1;
1609 if (url_open_dyn_buf(pb) < 0) {
1610 /* XXX: return an error ? */
1611 c->buffer_ptr = c->buffer;
1612 c->buffer_end = c->buffer;
1616 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1617 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1618 url_fprintf(pb, "Pragma: no-cache\r\n");
1619 url_fprintf(pb, "\r\n");
1621 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1622 if (c->stream->feed_filename) {
1623 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1625 url_fprintf(pb, "</HEAD>\n<BODY>");
1626 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1628 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1629 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1630 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");
1631 stream = first_stream;
1632 while (stream != NULL) {
1633 char sfilename[1024];
1636 if (stream->feed != stream) {
1637 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1638 eosf = sfilename + strlen(sfilename);
1639 if (eosf - sfilename >= 4) {
1640 if (strcmp(eosf - 4, ".asf") == 0) {
1641 strcpy(eosf - 4, ".asx");
1642 } else if (strcmp(eosf - 3, ".rm") == 0) {
1643 strcpy(eosf - 3, ".ram");
1644 } else if (stream->fmt == &rtp_muxer) {
1645 /* generate a sample RTSP director if
1646 unicast. Generate an SDP redirector if
1648 eosf = strrchr(sfilename, '.');
1650 eosf = sfilename + strlen(sfilename);
1651 if (stream->is_multicast)
1652 strcpy(eosf, ".sdp");
1654 strcpy(eosf, ".rtsp");
1658 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1659 sfilename, stream->filename);
1660 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1661 stream->conns_served);
1662 fmt_bytecount(pb, stream->bytes_served);
1663 switch(stream->stream_type) {
1664 case STREAM_TYPE_LIVE:
1666 int audio_bit_rate = 0;
1667 int video_bit_rate = 0;
1668 const char *audio_codec_name = "";
1669 const char *video_codec_name = "";
1670 const char *audio_codec_name_extra = "";
1671 const char *video_codec_name_extra = "";
1673 for(i=0;i<stream->nb_streams;i++) {
1674 AVStream *st = stream->streams[i];
1675 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1676 switch(st->codec->codec_type) {
1677 case CODEC_TYPE_AUDIO:
1678 audio_bit_rate += st->codec->bit_rate;
1680 if (*audio_codec_name)
1681 audio_codec_name_extra = "...";
1682 audio_codec_name = codec->name;
1685 case CODEC_TYPE_VIDEO:
1686 video_bit_rate += st->codec->bit_rate;
1688 if (*video_codec_name)
1689 video_codec_name_extra = "...";
1690 video_codec_name = codec->name;
1693 case CODEC_TYPE_DATA:
1694 video_bit_rate += st->codec->bit_rate;
1700 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",
1703 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1704 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1706 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1708 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1710 url_fprintf(pb, "\n");
1714 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1718 stream = stream->next;
1720 url_fprintf(pb, "</TABLE>\n");
1722 stream = first_stream;
1723 while (stream != NULL) {
1724 if (stream->feed == stream) {
1725 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1727 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1729 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1734 /* This is somewhat linux specific I guess */
1735 snprintf(ps_cmd, sizeof(ps_cmd),
1736 "ps -o \"%%cpu,cputime\" --no-headers %d",
1739 pid_stat = popen(ps_cmd, "r");
1744 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1746 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1754 url_fprintf(pb, "<p>");
1756 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");
1758 for (i = 0; i < stream->nb_streams; i++) {
1759 AVStream *st = stream->streams[i];
1760 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1761 const char *type = "unknown";
1762 char parameters[64];
1766 switch(st->codec->codec_type) {
1767 case CODEC_TYPE_AUDIO:
1769 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1771 case CODEC_TYPE_VIDEO:
1773 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1774 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1779 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1780 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1782 url_fprintf(pb, "</table>\n");
1785 stream = stream->next;
1791 AVCodecContext *enc;
1795 stream = first_feed;
1796 while (stream != NULL) {
1797 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1798 url_fprintf(pb, "<TABLE>\n");
1799 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1800 for(i=0;i<stream->nb_streams;i++) {
1801 AVStream *st = stream->streams[i];
1802 FeedData *fdata = st->priv_data;
1805 avcodec_string(buf, sizeof(buf), enc);
1806 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1807 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1808 avg /= enc->frame_size;
1809 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1810 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1812 url_fprintf(pb, "</TABLE>\n");
1813 stream = stream->next_feed;
1818 /* connection status */
1819 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1821 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1822 nb_connections, nb_max_connections);
1824 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1825 current_bandwidth, max_bandwidth);
1827 url_fprintf(pb, "<TABLE>\n");
1828 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");
1829 c1 = first_http_ctx;
1831 while (c1 != NULL) {
1837 for (j = 0; j < c1->stream->nb_streams; j++) {
1838 if (!c1->stream->feed) {
1839 bitrate += c1->stream->streams[j]->codec->bit_rate;
1841 if (c1->feed_streams[j] >= 0) {
1842 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1849 p = inet_ntoa(c1->from_addr.sin_addr);
1850 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1852 c1->stream ? c1->stream->filename : "",
1853 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1856 http_state[c1->state]);
1857 fmt_bytecount(pb, bitrate);
1858 url_fprintf(pb, "<td align=right>");
1859 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1860 url_fprintf(pb, "<td align=right>");
1861 fmt_bytecount(pb, c1->data_count);
1862 url_fprintf(pb, "\n");
1865 url_fprintf(pb, "</TABLE>\n");
1870 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1871 url_fprintf(pb, "</BODY>\n</HTML>\n");
1873 len = url_close_dyn_buf(pb, &c->pb_buffer);
1874 c->buffer_ptr = c->pb_buffer;
1875 c->buffer_end = c->pb_buffer + len;
1878 /* check if the parser needs to be opened for stream i */
1879 static void open_parser(AVFormatContext *s, int i)
1881 AVStream *st = s->streams[i];
1884 if (!st->codec->codec) {
1885 codec = avcodec_find_decoder(st->codec->codec_id);
1886 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1887 st->codec->parse_only = 1;
1888 if (avcodec_open(st->codec, codec) < 0) {
1889 st->codec->parse_only = 0;
1895 static int open_input_stream(HTTPContext *c, const char *info)
1898 char input_filename[1024];
1903 /* find file name */
1904 if (c->stream->feed) {
1905 strcpy(input_filename, c->stream->feed->feed_filename);
1906 buf_size = FFM_PACKET_SIZE;
1907 /* compute position (absolute time) */
1908 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1909 stream_pos = parse_date(buf, 0);
1910 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1911 int prebuffer = strtol(buf, 0, 10);
1912 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1914 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1917 strcpy(input_filename, c->stream->feed_filename);
1919 /* compute position (relative time) */
1920 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1921 stream_pos = parse_date(buf, 1);
1926 if (input_filename[0] == '\0')
1930 { time_t when = stream_pos / 1000000;
1931 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1936 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1937 buf_size, c->stream->ap_in) < 0) {
1938 http_log("%s not found", input_filename);
1943 /* open each parser */
1944 for(i=0;i<s->nb_streams;i++)
1947 /* choose stream as clock source (we favorize video stream if
1948 present) for packet sending */
1949 c->pts_stream_index = 0;
1950 for(i=0;i<c->stream->nb_streams;i++) {
1951 if (c->pts_stream_index == 0 &&
1952 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1953 c->pts_stream_index = i;
1958 if (c->fmt_in->iformat->read_seek) {
1959 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1962 /* set the start time (needed for maxtime and RTP packet timing) */
1963 c->start_time = cur_time;
1964 c->first_pts = AV_NOPTS_VALUE;
1968 /* return the server clock (in us) */
1969 static int64_t get_server_clock(HTTPContext *c)
1971 /* compute current pts value from system time */
1972 return (cur_time - c->start_time) * 1000;
1975 /* return the estimated time at which the current packet must be sent
1977 static int64_t get_packet_send_clock(HTTPContext *c)
1979 int bytes_left, bytes_sent, frame_bytes;
1981 frame_bytes = c->cur_frame_bytes;
1982 if (frame_bytes <= 0) {
1985 bytes_left = c->buffer_end - c->buffer_ptr;
1986 bytes_sent = frame_bytes - bytes_left;
1987 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1992 static int http_prepare_data(HTTPContext *c)
1995 AVFormatContext *ctx;
1997 av_freep(&c->pb_buffer);
1999 case HTTPSTATE_SEND_DATA_HEADER:
2000 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2001 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
2003 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
2004 c->stream->comment);
2005 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
2006 c->stream->copyright);
2007 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2010 /* open output stream by using specified codecs */
2011 c->fmt_ctx.oformat = c->stream->fmt;
2012 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2013 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2016 st = av_mallocz(sizeof(AVStream));
2017 st->codec= avcodec_alloc_context();
2018 c->fmt_ctx.streams[i] = st;
2019 /* if file or feed, then just take streams from FFStream struct */
2020 if (!c->stream->feed ||
2021 c->stream->feed == c->stream)
2022 src = c->stream->streams[i];
2024 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2028 st->codec->frame_number = 0; /* XXX: should be done in
2029 AVStream, not in codec */
2030 /* I'm pretty sure that this is not correct...
2031 * However, without it, we crash
2033 st->codec->coded_frame = &dummy_frame;
2035 c->got_key_frame = 0;
2037 /* prepare header and save header data in a stream */
2038 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2039 /* XXX: potential leak */
2042 c->fmt_ctx.pb.is_streamed = 1;
2044 av_set_parameters(&c->fmt_ctx, NULL);
2045 if (av_write_header(&c->fmt_ctx) < 0)
2048 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2049 c->buffer_ptr = c->pb_buffer;
2050 c->buffer_end = c->pb_buffer + len;
2052 c->state = HTTPSTATE_SEND_DATA;
2053 c->last_packet_sent = 0;
2055 case HTTPSTATE_SEND_DATA:
2056 /* find a new packet */
2060 /* read a packet from the input stream */
2061 if (c->stream->feed) {
2062 ffm_set_write_index(c->fmt_in,
2063 c->stream->feed->feed_write_index,
2064 c->stream->feed->feed_size);
2067 if (c->stream->max_time &&
2068 c->stream->max_time + c->start_time - cur_time < 0) {
2069 /* We have timed out */
2070 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2073 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2074 if (c->stream->feed && c->stream->feed->feed_opened) {
2075 /* if coming from feed, it means we reached the end of the
2076 ffm file, so must wait for more data */
2077 c->state = HTTPSTATE_WAIT_FEED;
2078 return 1; /* state changed */
2080 if (c->stream->loop) {
2081 av_close_input_file(c->fmt_in);
2083 if (open_input_stream(c, "") < 0)
2088 /* must send trailer now because eof or error */
2089 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2093 /* update first pts if needed */
2094 if (c->first_pts == AV_NOPTS_VALUE) {
2095 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2096 c->start_time = cur_time;
2098 /* send it to the appropriate stream */
2099 if (c->stream->feed) {
2100 /* if coming from a feed, select the right stream */
2101 if (c->switch_pending) {
2102 c->switch_pending = 0;
2103 for(i=0;i<c->stream->nb_streams;i++) {
2104 if (c->switch_feed_streams[i] == pkt.stream_index) {
2105 if (pkt.flags & PKT_FLAG_KEY) {
2106 do_switch_stream(c, i);
2109 if (c->switch_feed_streams[i] >= 0) {
2110 c->switch_pending = 1;
2114 for(i=0;i<c->stream->nb_streams;i++) {
2115 if (c->feed_streams[i] == pkt.stream_index) {
2116 pkt.stream_index = i;
2117 if (pkt.flags & PKT_FLAG_KEY) {
2118 c->got_key_frame |= 1 << i;
2120 /* See if we have all the key frames, then
2121 * we start to send. This logic is not quite
2122 * right, but it works for the case of a
2123 * single video stream with one or more
2124 * audio streams (for which every frame is
2125 * typically a key frame).
2127 if (!c->stream->send_on_key ||
2128 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2134 AVCodecContext *codec;
2137 /* specific handling for RTP: we use several
2138 output stream (one for each RTP
2139 connection). XXX: need more abstract handling */
2140 if (c->is_packetized) {
2142 /* compute send time and duration */
2143 st = c->fmt_in->streams[pkt.stream_index];
2144 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2145 if (st->start_time != AV_NOPTS_VALUE)
2146 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2147 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2149 printf("index=%d pts=%0.3f duration=%0.6f\n",
2151 (double)c->cur_pts /
2153 (double)c->cur_frame_duration /
2156 /* find RTP context */
2157 c->packet_stream_index = pkt.stream_index;
2158 ctx = c->rtp_ctx[c->packet_stream_index];
2160 av_free_packet(&pkt);
2163 codec = ctx->streams[0]->codec;
2164 /* only one stream per RTP connection */
2165 pkt.stream_index = 0;
2169 codec = ctx->streams[pkt.stream_index]->codec;
2172 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2173 if (c->is_packetized) {
2174 int max_packet_size;
2175 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2176 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2178 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2179 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2181 ret = url_open_dyn_buf(&ctx->pb);
2184 /* XXX: potential leak */
2187 if (pkt.dts != AV_NOPTS_VALUE)
2188 pkt.dts = av_rescale_q(pkt.dts,
2189 c->fmt_in->streams[pkt.stream_index]->time_base,
2190 ctx->streams[pkt.stream_index]->time_base);
2191 if (pkt.pts != AV_NOPTS_VALUE)
2192 pkt.pts = av_rescale_q(pkt.pts,
2193 c->fmt_in->streams[pkt.stream_index]->time_base,
2194 ctx->streams[pkt.stream_index]->time_base);
2195 if (av_write_frame(ctx, &pkt)) {
2196 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2199 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2200 c->cur_frame_bytes = len;
2201 c->buffer_ptr = c->pb_buffer;
2202 c->buffer_end = c->pb_buffer + len;
2204 codec->frame_number++;
2208 av_free_packet(&pkt);
2214 case HTTPSTATE_SEND_DATA_TRAILER:
2215 /* last packet test ? */
2216 if (c->last_packet_sent || c->is_packetized)
2219 /* prepare header */
2220 if (url_open_dyn_buf(&ctx->pb) < 0) {
2221 /* XXX: potential leak */
2224 av_write_trailer(ctx);
2225 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2226 c->buffer_ptr = c->pb_buffer;
2227 c->buffer_end = c->pb_buffer + len;
2229 c->last_packet_sent = 1;
2236 #define SHORT_TERM_BANDWIDTH 8000000
2238 /* should convert the format at the same time */
2239 /* send data starting at c->buffer_ptr to the output connection
2240 (either UDP or TCP connection) */
2241 static int http_send_data(HTTPContext *c)
2246 if (c->buffer_ptr >= c->buffer_end) {
2247 ret = http_prepare_data(c);
2250 else if (ret != 0) {
2251 /* state change requested */
2255 if (c->is_packetized) {
2256 /* RTP data output */
2257 len = c->buffer_end - c->buffer_ptr;
2259 /* fail safe - should never happen */
2261 c->buffer_ptr = c->buffer_end;
2264 len = (c->buffer_ptr[0] << 24) |
2265 (c->buffer_ptr[1] << 16) |
2266 (c->buffer_ptr[2] << 8) |
2268 if (len > (c->buffer_end - c->buffer_ptr))
2270 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2271 /* nothing to send yet: we can wait */
2275 c->data_count += len;
2276 update_datarate(&c->datarate, c->data_count);
2278 c->stream->bytes_served += len;
2280 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2281 /* RTP packets are sent inside the RTSP TCP connection */
2282 ByteIOContext pb1, *pb = &pb1;
2283 int interleaved_index, size;
2285 HTTPContext *rtsp_c;
2288 /* if no RTSP connection left, error */
2291 /* if already sending something, then wait. */
2292 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
2295 if (url_open_dyn_buf(pb) < 0)
2297 interleaved_index = c->packet_stream_index * 2;
2298 /* RTCP packets are sent at odd indexes */
2299 if (c->buffer_ptr[1] == 200)
2300 interleaved_index++;
2301 /* write RTSP TCP header */
2303 header[1] = interleaved_index;
2304 header[2] = len >> 8;
2306 put_buffer(pb, header, 4);
2307 /* write RTP packet data */
2309 put_buffer(pb, c->buffer_ptr, len);
2310 size = url_close_dyn_buf(pb, &c->packet_buffer);
2311 /* prepare asynchronous TCP sending */
2312 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2313 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2314 c->buffer_ptr += len;
2316 /* send everything we can NOW */
2317 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2318 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2320 rtsp_c->packet_buffer_ptr += len;
2322 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2323 /* if we could not send all the data, we will
2324 send it later, so a new state is needed to
2325 "lock" the RTSP TCP connection */
2326 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2329 /* all data has been sent */
2330 av_freep(&c->packet_buffer);
2333 /* send RTP packet directly in UDP */
2335 url_write(c->rtp_handles[c->packet_stream_index],
2336 c->buffer_ptr, len);
2337 c->buffer_ptr += len;
2338 /* here we continue as we can send several packets per 10 ms slot */
2341 /* TCP data output */
2342 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2344 if (errno != EAGAIN && errno != EINTR) {
2345 /* error : close connection */
2351 c->buffer_ptr += len;
2353 c->data_count += len;
2354 update_datarate(&c->datarate, c->data_count);
2356 c->stream->bytes_served += len;
2364 static int http_start_receive_data(HTTPContext *c)
2368 if (c->stream->feed_opened)
2371 /* Don't permit writing to this one */
2372 if (c->stream->readonly)
2376 fd = open(c->stream->feed_filename, O_RDWR);
2381 c->stream->feed_write_index = ffm_read_write_index(fd);
2382 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2383 lseek(fd, 0, SEEK_SET);
2385 /* init buffer input */
2386 c->buffer_ptr = c->buffer;
2387 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2388 c->stream->feed_opened = 1;
2392 static int http_receive_data(HTTPContext *c)
2396 if (c->buffer_end > c->buffer_ptr) {
2399 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2401 if (errno != EAGAIN && errno != EINTR) {
2402 /* error : close connection */
2405 } else if (len == 0) {
2406 /* end of connection : close it */
2409 c->buffer_ptr += len;
2410 c->data_count += len;
2411 update_datarate(&c->datarate, c->data_count);
2415 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2416 if (c->buffer[0] != 'f' ||
2417 c->buffer[1] != 'm') {
2418 http_log("Feed stream has become desynchronized -- disconnecting\n");
2423 if (c->buffer_ptr >= c->buffer_end) {
2424 FFStream *feed = c->stream;
2425 /* a packet has been received : write it in the store, except
2427 if (c->data_count > FFM_PACKET_SIZE) {
2429 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2430 /* XXX: use llseek or url_seek */
2431 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2432 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2434 feed->feed_write_index += FFM_PACKET_SIZE;
2435 /* update file size */
2436 if (feed->feed_write_index > c->stream->feed_size)
2437 feed->feed_size = feed->feed_write_index;
2439 /* handle wrap around if max file size reached */
2440 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2441 feed->feed_write_index = FFM_PACKET_SIZE;
2444 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2446 /* wake up any waiting connections */
2447 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2448 if (c1->state == HTTPSTATE_WAIT_FEED &&
2449 c1->stream->feed == c->stream->feed) {
2450 c1->state = HTTPSTATE_SEND_DATA;
2454 /* We have a header in our hands that contains useful data */
2456 AVInputFormat *fmt_in;
2457 ByteIOContext *pb = &s.pb;
2460 memset(&s, 0, sizeof(s));
2462 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2463 pb->buf_end = c->buffer_end; /* ?? */
2464 pb->is_streamed = 1;
2466 /* use feed output format name to find corresponding input format */
2467 fmt_in = av_find_input_format(feed->fmt->name);
2471 if (fmt_in->priv_data_size > 0) {
2472 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2478 if (fmt_in->read_header(&s, 0) < 0) {
2479 av_freep(&s.priv_data);
2483 /* Now we have the actual streams */
2484 if (s.nb_streams != feed->nb_streams) {
2485 av_freep(&s.priv_data);
2488 for (i = 0; i < s.nb_streams; i++) {
2489 memcpy(feed->streams[i]->codec,
2490 s.streams[i]->codec, sizeof(AVCodecContext));
2492 av_freep(&s.priv_data);
2494 c->buffer_ptr = c->buffer;
2499 c->stream->feed_opened = 0;
2504 /********************************************************************/
2507 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2514 switch(error_number) {
2515 #define DEF(n, c, s) case c: str = s; break;
2516 #include "rtspcodes.h"
2519 str = "Unknown Error";
2523 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2524 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2526 /* output GMT time */
2530 p = buf2 + strlen(p) - 1;
2533 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2536 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2538 rtsp_reply_header(c, error_number);
2539 url_fprintf(c->pb, "\r\n");
2542 static int rtsp_parse_request(HTTPContext *c)
2544 const char *p, *p1, *p2;
2551 RTSPHeader header1, *header = &header1;
2553 c->buffer_ptr[0] = '\0';
2556 get_word(cmd, sizeof(cmd), &p);
2557 get_word(url, sizeof(url), &p);
2558 get_word(protocol, sizeof(protocol), &p);
2560 pstrcpy(c->method, sizeof(c->method), cmd);
2561 pstrcpy(c->url, sizeof(c->url), url);
2562 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2565 if (url_open_dyn_buf(c->pb) < 0) {
2566 /* XXX: cannot do more */
2567 c->pb = NULL; /* safety */
2571 /* check version name */
2572 if (strcmp(protocol, "RTSP/1.0") != 0) {
2573 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2577 /* parse each header line */
2578 memset(header, 0, sizeof(RTSPHeader));
2579 /* skip to next line */
2580 while (*p != '\n' && *p != '\0')
2584 while (*p != '\0') {
2585 p1 = strchr(p, '\n');
2589 if (p2 > p && p2[-1] == '\r')
2591 /* skip empty line */
2595 if (len > sizeof(line) - 1)
2596 len = sizeof(line) - 1;
2597 memcpy(line, p, len);
2599 rtsp_parse_line(header, line);
2603 /* handle sequence number */
2604 c->seq = header->seq;
2606 if (!strcmp(cmd, "DESCRIBE")) {
2607 rtsp_cmd_describe(c, url);
2608 } else if (!strcmp(cmd, "OPTIONS")) {
2609 rtsp_cmd_options(c, url);
2610 } else if (!strcmp(cmd, "SETUP")) {
2611 rtsp_cmd_setup(c, url, header);
2612 } else if (!strcmp(cmd, "PLAY")) {
2613 rtsp_cmd_play(c, url, header);
2614 } else if (!strcmp(cmd, "PAUSE")) {
2615 rtsp_cmd_pause(c, url, header);
2616 } else if (!strcmp(cmd, "TEARDOWN")) {
2617 rtsp_cmd_teardown(c, url, header);
2619 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2622 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2623 c->pb = NULL; /* safety */
2625 /* XXX: cannot do more */
2628 c->buffer_ptr = c->pb_buffer;
2629 c->buffer_end = c->pb_buffer + len;
2630 c->state = RTSPSTATE_SEND_REPLY;
2634 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2636 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2637 struct in_addr my_ip)
2639 ByteIOContext pb1, *pb = &pb1;
2640 int i, payload_type, port, private_payload_type, j;
2641 const char *ipstr, *title, *mediatype;
2644 if (url_open_dyn_buf(pb) < 0)
2647 /* general media info */
2649 url_fprintf(pb, "v=0\n");
2650 ipstr = inet_ntoa(my_ip);
2651 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2652 title = stream->title;
2653 if (title[0] == '\0')
2655 url_fprintf(pb, "s=%s\n", title);
2656 if (stream->comment[0] != '\0')
2657 url_fprintf(pb, "i=%s\n", stream->comment);
2658 if (stream->is_multicast) {
2659 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2661 /* for each stream, we output the necessary info */
2662 private_payload_type = RTP_PT_PRIVATE;
2663 for(i = 0; i < stream->nb_streams; i++) {
2664 st = stream->streams[i];
2665 if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
2666 mediatype = "video";
2668 switch(st->codec->codec_type) {
2669 case CODEC_TYPE_AUDIO:
2670 mediatype = "audio";
2672 case CODEC_TYPE_VIDEO:
2673 mediatype = "video";
2676 mediatype = "application";
2680 /* NOTE: the port indication is not correct in case of
2681 unicast. It is not an issue because RTSP gives it */
2682 payload_type = rtp_get_payload_type(st->codec);
2683 if (payload_type < 0)
2684 payload_type = private_payload_type++;
2685 if (stream->is_multicast) {
2686 port = stream->multicast_port + 2 * i;
2690 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2691 mediatype, port, payload_type);
2692 if (payload_type >= RTP_PT_PRIVATE) {
2693 /* for private payload type, we need to give more info */
2694 switch(st->codec->codec_id) {
2695 case CODEC_ID_MPEG4:
2698 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2699 payload_type, 90000);
2700 /* we must also add the mpeg4 header */
2701 data = st->codec->extradata;
2703 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2704 for(j=0;j<st->codec->extradata_size;j++) {
2705 url_fprintf(pb, "%02x", data[j]);
2707 url_fprintf(pb, "\n");
2712 /* XXX: add other codecs ? */
2716 url_fprintf(pb, "a=control:streamid=%d\n", i);
2718 return url_close_dyn_buf(pb, pbuffer);
2720 url_close_dyn_buf(pb, pbuffer);
2725 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2727 // rtsp_reply_header(c, RTSP_STATUS_OK);
2728 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2729 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2730 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2731 url_fprintf(c->pb, "\r\n");
2734 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2740 int content_length, len;
2741 struct sockaddr_in my_addr;
2743 /* find which url is asked */
2744 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2749 for(stream = first_stream; stream != NULL; stream = stream->next) {
2750 if (!stream->is_feed && stream->fmt == &rtp_muxer &&
2751 !strcmp(path, stream->filename)) {
2755 /* no stream found */
2756 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2760 /* prepare the media description in sdp format */
2762 /* get the host IP */
2763 len = sizeof(my_addr);
2764 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2765 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2766 if (content_length < 0) {
2767 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2770 rtsp_reply_header(c, RTSP_STATUS_OK);
2771 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2772 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2773 url_fprintf(c->pb, "\r\n");
2774 put_buffer(c->pb, content, content_length);
2777 static HTTPContext *find_rtp_session(const char *session_id)
2781 if (session_id[0] == '\0')
2784 for(c = first_http_ctx; c != NULL; c = c->next) {
2785 if (!strcmp(c->session_id, session_id))
2791 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2793 RTSPTransportField *th;
2796 for(i=0;i<h->nb_transports;i++) {
2797 th = &h->transports[i];
2798 if (th->protocol == protocol)
2804 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2808 int stream_index, port;
2813 RTSPTransportField *th;
2814 struct sockaddr_in dest_addr;
2815 RTSPActionServerSetup setup;
2817 /* find which url is asked */
2818 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2823 /* now check each stream */
2824 for(stream = first_stream; stream != NULL; stream = stream->next) {
2825 if (!stream->is_feed && stream->fmt == &rtp_muxer) {
2826 /* accept aggregate filenames only if single stream */
2827 if (!strcmp(path, stream->filename)) {
2828 if (stream->nb_streams != 1) {
2829 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2836 for(stream_index = 0; stream_index < stream->nb_streams;
2838 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2839 stream->filename, stream_index);
2840 if (!strcmp(path, buf))
2845 /* no stream found */
2846 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2850 /* generate session id if needed */
2851 if (h->session_id[0] == '\0') {
2852 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2853 av_random(&random_state), av_random(&random_state));
2856 /* find rtp session, and create it if none found */
2857 rtp_c = find_rtp_session(h->session_id);
2859 /* always prefer UDP */
2860 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2862 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2864 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2869 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2872 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2876 /* open input stream */
2877 if (open_input_stream(rtp_c, "") < 0) {
2878 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2883 /* test if stream is OK (test needed because several SETUP needs
2884 to be done for a given file) */
2885 if (rtp_c->stream != stream) {
2886 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2890 /* test if stream is already set up */
2891 if (rtp_c->rtp_ctx[stream_index]) {
2892 rtsp_reply_error(c, RTSP_STATUS_STATE);
2896 /* check transport */
2897 th = find_transport(h, rtp_c->rtp_protocol);
2898 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2899 th->client_port_min <= 0)) {
2900 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2904 /* setup default options */
2905 setup.transport_option[0] = '\0';
2906 dest_addr = rtp_c->from_addr;
2907 dest_addr.sin_port = htons(th->client_port_min);
2909 /* add transport option if needed */
2910 if (ff_rtsp_callback) {
2911 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2912 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2913 (char *)&setup, sizeof(setup),
2914 stream->rtsp_option) < 0) {
2915 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2918 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2922 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2923 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2927 /* now everything is OK, so we can send the connection parameters */
2928 rtsp_reply_header(c, RTSP_STATUS_OK);
2930 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2932 switch(rtp_c->rtp_protocol) {
2933 case RTSP_PROTOCOL_RTP_UDP:
2934 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2935 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2936 "client_port=%d-%d;server_port=%d-%d",
2937 th->client_port_min, th->client_port_min + 1,
2940 case RTSP_PROTOCOL_RTP_TCP:
2941 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2942 stream_index * 2, stream_index * 2 + 1);
2947 if (setup.transport_option[0] != '\0') {
2948 url_fprintf(c->pb, ";%s", setup.transport_option);
2950 url_fprintf(c->pb, "\r\n");
2953 url_fprintf(c->pb, "\r\n");
2957 /* find an rtp connection by using the session ID. Check consistency
2959 static HTTPContext *find_rtp_session_with_url(const char *url,
2960 const char *session_id)
2968 rtp_c = find_rtp_session(session_id);
2972 /* find which url is asked */
2973 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2977 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2978 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2979 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2980 rtp_c->stream->filename, s);
2981 if(!strncmp(path, buf, sizeof(buf))) {
2982 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2989 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2993 rtp_c = find_rtp_session_with_url(url, h->session_id);
2995 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2999 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3000 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3001 rtp_c->state != HTTPSTATE_READY) {
3002 rtsp_reply_error(c, RTSP_STATUS_STATE);
3007 /* XXX: seek in stream */
3008 if (h->range_start != AV_NOPTS_VALUE) {
3009 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3010 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3014 rtp_c->state = HTTPSTATE_SEND_DATA;
3016 /* now everything is OK, so we can send the connection parameters */
3017 rtsp_reply_header(c, RTSP_STATUS_OK);
3019 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3020 url_fprintf(c->pb, "\r\n");
3023 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3027 rtp_c = find_rtp_session_with_url(url, h->session_id);
3029 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3033 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3034 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3035 rtsp_reply_error(c, RTSP_STATUS_STATE);
3039 rtp_c->state = HTTPSTATE_READY;
3040 rtp_c->first_pts = AV_NOPTS_VALUE;
3041 /* now everything is OK, so we can send the connection parameters */
3042 rtsp_reply_header(c, RTSP_STATUS_OK);
3044 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3045 url_fprintf(c->pb, "\r\n");
3048 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3052 rtp_c = find_rtp_session_with_url(url, h->session_id);
3054 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3058 /* abort the session */
3059 close_connection(rtp_c);
3061 if (ff_rtsp_callback) {
3062 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3064 rtp_c->stream->rtsp_option);
3067 /* now everything is OK, so we can send the connection parameters */
3068 rtsp_reply_header(c, RTSP_STATUS_OK);
3070 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3071 url_fprintf(c->pb, "\r\n");
3075 /********************************************************************/
3078 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3079 FFStream *stream, const char *session_id,
3080 enum RTSPProtocol rtp_protocol)
3082 HTTPContext *c = NULL;
3083 const char *proto_str;
3085 /* XXX: should output a warning page when coming
3086 close to the connection limit */
3087 if (nb_connections >= nb_max_connections)
3090 /* add a new connection */
3091 c = av_mallocz(sizeof(HTTPContext));
3096 c->poll_entry = NULL;
3097 c->from_addr = *from_addr;
3098 c->buffer_size = IOBUFFER_INIT_SIZE;
3099 c->buffer = av_malloc(c->buffer_size);
3104 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3105 c->state = HTTPSTATE_READY;
3106 c->is_packetized = 1;
3107 c->rtp_protocol = rtp_protocol;
3109 /* protocol is shown in statistics */
3110 switch(c->rtp_protocol) {
3111 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3112 proto_str = "MCAST";
3114 case RTSP_PROTOCOL_RTP_UDP:
3117 case RTSP_PROTOCOL_RTP_TCP:
3124 pstrcpy(c->protocol, sizeof(c->protocol), "RTP/");
3125 pstrcat(c->protocol, sizeof(c->protocol), proto_str);
3127 current_bandwidth += stream->bandwidth;
3129 c->next = first_http_ctx;
3141 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3142 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3144 static int rtp_new_av_stream(HTTPContext *c,
3145 int stream_index, struct sockaddr_in *dest_addr,
3146 HTTPContext *rtsp_c)
3148 AVFormatContext *ctx;
3154 int max_packet_size;
3156 /* now we can open the relevant output stream */
3157 ctx = av_alloc_format_context();
3160 ctx->oformat = &rtp_muxer;
3162 st = av_mallocz(sizeof(AVStream));
3165 st->codec= avcodec_alloc_context();
3166 ctx->nb_streams = 1;
3167 ctx->streams[0] = st;
3169 if (!c->stream->feed ||
3170 c->stream->feed == c->stream) {
3171 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3174 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3178 /* build destination RTP address */
3179 ipaddr = inet_ntoa(dest_addr->sin_addr);
3181 switch(c->rtp_protocol) {
3182 case RTSP_PROTOCOL_RTP_UDP:
3183 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3186 /* XXX: also pass as parameter to function ? */
3187 if (c->stream->is_multicast) {
3189 ttl = c->stream->multicast_ttl;
3192 snprintf(ctx->filename, sizeof(ctx->filename),
3193 "rtp://%s:%d?multicast=1&ttl=%d",
3194 ipaddr, ntohs(dest_addr->sin_port), ttl);
3196 snprintf(ctx->filename, sizeof(ctx->filename),
3197 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3200 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3202 c->rtp_handles[stream_index] = h;
3203 max_packet_size = url_get_max_packet_size(h);
3205 case RTSP_PROTOCOL_RTP_TCP:
3208 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3214 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3215 ipaddr, ntohs(dest_addr->sin_port),
3217 c->stream->filename, stream_index, c->protocol);
3219 /* normally, no packets should be output here, but the packet size may be checked */
3220 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3221 /* XXX: close stream */
3224 av_set_parameters(ctx, NULL);
3225 if (av_write_header(ctx) < 0) {
3232 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3235 c->rtp_ctx[stream_index] = ctx;
3239 /********************************************************************/
3240 /* ffserver initialization */
3242 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3246 fst = av_mallocz(sizeof(AVStream));
3249 fst->codec= avcodec_alloc_context();
3250 fst->priv_data = av_mallocz(sizeof(FeedData));
3251 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3252 fst->codec->coded_frame = &dummy_frame;
3253 fst->index = stream->nb_streams;
3254 av_set_pts_info(fst, 33, 1, 90000);
3255 stream->streams[stream->nb_streams++] = fst;
3259 /* return the stream number in the feed */
3260 static int add_av_stream(FFStream *feed, AVStream *st)
3263 AVCodecContext *av, *av1;
3267 for(i=0;i<feed->nb_streams;i++) {
3268 st = feed->streams[i];
3270 if (av1->codec_id == av->codec_id &&
3271 av1->codec_type == av->codec_type &&
3272 av1->bit_rate == av->bit_rate) {
3274 switch(av->codec_type) {
3275 case CODEC_TYPE_AUDIO:
3276 if (av1->channels == av->channels &&
3277 av1->sample_rate == av->sample_rate)
3280 case CODEC_TYPE_VIDEO:
3281 if (av1->width == av->width &&
3282 av1->height == av->height &&
3283 av1->time_base.den == av->time_base.den &&
3284 av1->time_base.num == av->time_base.num &&
3285 av1->gop_size == av->gop_size)
3294 fst = add_av_stream1(feed, av);
3297 return feed->nb_streams - 1;
3302 static void remove_stream(FFStream *stream)
3306 while (*ps != NULL) {
3307 if (*ps == stream) {
3315 /* specific mpeg4 handling : we extract the raw parameters */
3316 static void extract_mpeg4_header(AVFormatContext *infile)
3318 int mpeg4_count, i, size;
3324 for(i=0;i<infile->nb_streams;i++) {
3325 st = infile->streams[i];
3326 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3327 st->codec->extradata_size == 0) {
3334 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3335 while (mpeg4_count > 0) {
3336 if (av_read_packet(infile, &pkt) < 0)
3338 st = infile->streams[pkt.stream_index];
3339 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3340 st->codec->extradata_size == 0) {
3341 av_freep(&st->codec->extradata);
3342 /* fill extradata with the header */
3343 /* XXX: we make hard suppositions here ! */
3345 while (p < pkt.data + pkt.size - 4) {
3346 /* stop when vop header is found */
3347 if (p[0] == 0x00 && p[1] == 0x00 &&
3348 p[2] == 0x01 && p[3] == 0xb6) {
3349 size = p - pkt.data;
3350 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3351 st->codec->extradata = av_malloc(size);
3352 st->codec->extradata_size = size;
3353 memcpy(st->codec->extradata, pkt.data, size);
3360 av_free_packet(&pkt);
3364 /* compute the needed AVStream for each file */
3365 static void build_file_streams(void)
3367 FFStream *stream, *stream_next;
3368 AVFormatContext *infile;
3371 /* gather all streams */
3372 for(stream = first_stream; stream != NULL; stream = stream_next) {
3373 stream_next = stream->next;
3374 if (stream->stream_type == STREAM_TYPE_LIVE &&
3376 /* the stream comes from a file */
3377 /* try to open the file */
3379 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3380 if (stream->fmt == &rtp_muxer) {
3381 /* specific case : if transport stream output to RTP,
3382 we use a raw transport stream reader */
3383 stream->ap_in->mpeg2ts_raw = 1;
3384 stream->ap_in->mpeg2ts_compute_pcr = 1;
3387 if (av_open_input_file(&infile, stream->feed_filename,
3388 stream->ifmt, 0, stream->ap_in) < 0) {
3389 http_log("%s not found", stream->feed_filename);
3390 /* remove stream (no need to spend more time on it) */
3392 remove_stream(stream);
3394 /* find all the AVStreams inside and reference them in
3396 if (av_find_stream_info(infile) < 0) {
3397 http_log("Could not find codec parameters from '%s'",
3398 stream->feed_filename);
3399 av_close_input_file(infile);
3402 extract_mpeg4_header(infile);
3404 for(i=0;i<infile->nb_streams;i++) {
3405 add_av_stream1(stream, infile->streams[i]->codec);
3407 av_close_input_file(infile);
3413 /* compute the needed AVStream for each feed */
3414 static void build_feed_streams(void)
3416 FFStream *stream, *feed;
3419 /* gather all streams */
3420 for(stream = first_stream; stream != NULL; stream = stream->next) {
3421 feed = stream->feed;
3423 if (!stream->is_feed) {
3424 /* we handle a stream coming from a feed */
3425 for(i=0;i<stream->nb_streams;i++) {
3426 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3432 /* gather all streams */
3433 for(stream = first_stream; stream != NULL; stream = stream->next) {
3434 feed = stream->feed;
3436 if (stream->is_feed) {
3437 for(i=0;i<stream->nb_streams;i++) {
3438 stream->feed_streams[i] = i;
3444 /* create feed files if needed */
3445 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3448 if (url_exist(feed->feed_filename)) {
3449 /* See if it matches */
3453 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3454 /* Now see if it matches */
3455 if (s->nb_streams == feed->nb_streams) {
3457 for(i=0;i<s->nb_streams;i++) {
3459 sf = feed->streams[i];
3462 if (sf->index != ss->index ||
3464 printf("Index & Id do not match for stream %d (%s)\n",
3465 i, feed->feed_filename);
3468 AVCodecContext *ccf, *ccs;
3472 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3474 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3475 printf("Codecs do not match for stream %d\n", i);
3477 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3478 printf("Codec bitrates do not match for stream %d\n", i);
3480 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3481 if (CHECK_CODEC(time_base.den) ||
3482 CHECK_CODEC(time_base.num) ||
3483 CHECK_CODEC(width) ||
3484 CHECK_CODEC(height)) {
3485 printf("Codec width, height and framerate do not match for stream %d\n", i);
3488 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3489 if (CHECK_CODEC(sample_rate) ||
3490 CHECK_CODEC(channels) ||
3491 CHECK_CODEC(frame_size)) {
3492 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3496 printf("Unknown codec type\n");
3505 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3506 feed->feed_filename, s->nb_streams, feed->nb_streams);
3509 av_close_input_file(s);
3511 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3512 feed->feed_filename);
3515 if (feed->readonly) {
3516 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3517 feed->feed_filename);
3520 unlink(feed->feed_filename);
3523 if (!url_exist(feed->feed_filename)) {
3524 AVFormatContext s1, *s = &s1;
3526 if (feed->readonly) {
3527 printf("Unable to create feed file '%s' as it is marked readonly\n",
3528 feed->feed_filename);
3532 /* only write the header of the ffm file */
3533 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3534 fprintf(stderr, "Could not open output feed file '%s'\n",
3535 feed->feed_filename);
3538 s->oformat = feed->fmt;
3539 s->nb_streams = feed->nb_streams;
3540 for(i=0;i<s->nb_streams;i++) {
3542 st = feed->streams[i];
3545 av_set_parameters(s, NULL);
3546 if (av_write_header(s) < 0) {
3547 fprintf(stderr, "Container doesn't supports the required parameters\n");
3550 /* XXX: need better api */
3551 av_freep(&s->priv_data);
3554 /* get feed size and write index */
3555 fd = open(feed->feed_filename, O_RDONLY);
3557 fprintf(stderr, "Could not open output feed file '%s'\n",
3558 feed->feed_filename);
3562 feed->feed_write_index = ffm_read_write_index(fd);
3563 feed->feed_size = lseek(fd, 0, SEEK_END);
3564 /* ensure that we do not wrap before the end of file */
3565 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3566 feed->feed_max_size = feed->feed_size;
3572 /* compute the bandwidth used by each stream */
3573 static void compute_bandwidth(void)
3578 for(stream = first_stream; stream != NULL; stream = stream->next) {
3580 for(i=0;i<stream->nb_streams;i++) {
3581 AVStream *st = stream->streams[i];
3582 switch(st->codec->codec_type) {
3583 case CODEC_TYPE_AUDIO:
3584 case CODEC_TYPE_VIDEO:
3585 bandwidth += st->codec->bit_rate;
3591 stream->bandwidth = (bandwidth + 999) / 1000;
3595 static void get_arg(char *buf, int buf_size, const char **pp)
3602 while (isspace(*p)) p++;
3605 if (*p == '\"' || *p == '\'')
3617 if ((q - buf) < buf_size - 1)
3622 if (quote && *p == quote)
3627 /* add a codec and set the default parameters */
3628 static void add_codec(FFStream *stream, AVCodecContext *av)
3632 /* compute default parameters */
3633 switch(av->codec_type) {
3634 case CODEC_TYPE_AUDIO:
3635 if (av->bit_rate == 0)
3636 av->bit_rate = 64000;
3637 if (av->sample_rate == 0)
3638 av->sample_rate = 22050;
3639 if (av->channels == 0)
3642 case CODEC_TYPE_VIDEO:
3643 if (av->bit_rate == 0)
3644 av->bit_rate = 64000;
3645 if (av->time_base.num == 0){
3646 av->time_base.den = 5;
3647 av->time_base.num = 1;
3649 if (av->width == 0 || av->height == 0) {
3653 /* Bitrate tolerance is less for streaming */
3654 if (av->bit_rate_tolerance == 0)
3655 av->bit_rate_tolerance = av->bit_rate / 4;
3660 if (av->max_qdiff == 0)
3662 av->qcompress = 0.5;
3665 if (!av->nsse_weight)
3666 av->nsse_weight = 8;
3668 av->frame_skip_cmp = FF_CMP_DCTMAX;
3669 av->me_method = ME_EPZS;
3670 av->rc_buffer_aggressivity = 1.0;
3673 av->rc_eq = "tex^qComp";
3674 if (!av->i_quant_factor)
3675 av->i_quant_factor = -0.8;
3676 if (!av->b_quant_factor)
3677 av->b_quant_factor = 1.25;
3678 if (!av->b_quant_offset)
3679 av->b_quant_offset = 1.25;
3680 if (!av->rc_max_rate)
3681 av->rc_max_rate = av->bit_rate * 2;
3683 if (av->rc_max_rate && !av->rc_buffer_size) {
3684 av->rc_buffer_size = av->rc_max_rate;
3693 st = av_mallocz(sizeof(AVStream));
3696 st->codec = avcodec_alloc_context();
3697 stream->streams[stream->nb_streams++] = st;
3698 memcpy(st->codec, av, sizeof(AVCodecContext));
3701 static int opt_audio_codec(const char *arg)
3707 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3712 return CODEC_ID_NONE;
3718 static int opt_video_codec(const char *arg)
3724 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3729 return CODEC_ID_NONE;
3735 /* simplistic plugin support */
3738 static void load_module(const char *filename)
3741 void (*init_func)(void);
3742 dll = dlopen(filename, RTLD_NOW);
3744 fprintf(stderr, "Could not load module '%s' - %s\n",
3745 filename, dlerror());
3749 init_func = dlsym(dll, "ffserver_module_init");
3752 "%s: init function 'ffserver_module_init()' not found\n",
3761 static int parse_ffconfig(const char *filename)
3768 int val, errors, line_num;
3769 FFStream **last_stream, *stream, *redirect;
3770 FFStream **last_feed, *feed;
3771 AVCodecContext audio_enc, video_enc;
3772 int audio_id, video_id;
3774 f = fopen(filename, "r");
3782 first_stream = NULL;
3783 last_stream = &first_stream;
3785 last_feed = &first_feed;
3789 audio_id = CODEC_ID_NONE;
3790 video_id = CODEC_ID_NONE;
3792 if (fgets(line, sizeof(line), f) == NULL)
3798 if (*p == '\0' || *p == '#')
3801 get_arg(cmd, sizeof(cmd), &p);
3803 if (!strcasecmp(cmd, "Port")) {
3804 get_arg(arg, sizeof(arg), &p);
3805 my_http_addr.sin_port = htons (atoi(arg));
3806 } else if (!strcasecmp(cmd, "BindAddress")) {
3807 get_arg(arg, sizeof(arg), &p);
3808 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3809 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3810 filename, line_num, arg);
3813 } else if (!strcasecmp(cmd, "NoDaemon")) {
3814 ffserver_daemon = 0;
3815 } else if (!strcasecmp(cmd, "RTSPPort")) {
3816 get_arg(arg, sizeof(arg), &p);
3817 my_rtsp_addr.sin_port = htons (atoi(arg));
3818 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3819 get_arg(arg, sizeof(arg), &p);
3820 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3821 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3822 filename, line_num, arg);
3825 } else if (!strcasecmp(cmd, "MaxClients")) {
3826 get_arg(arg, sizeof(arg), &p);
3828 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3829 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3830 filename, line_num, arg);
3833 nb_max_connections = val;
3835 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3836 get_arg(arg, sizeof(arg), &p);
3838 if (val < 10 || val > 100000) {
3839 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3840 filename, line_num, arg);
3843 max_bandwidth = val;
3845 } else if (!strcasecmp(cmd, "CustomLog")) {
3846 get_arg(logfilename, sizeof(logfilename), &p);
3847 } else if (!strcasecmp(cmd, "<Feed")) {
3848 /*********************************************/
3849 /* Feed related options */
3851 if (stream || feed) {
3852 fprintf(stderr, "%s:%d: Already in a tag\n",
3853 filename, line_num);
3855 feed = av_mallocz(sizeof(FFStream));
3856 /* add in stream list */
3857 *last_stream = feed;
3858 last_stream = &feed->next;
3859 /* add in feed list */
3861 last_feed = &feed->next_feed;
3863 get_arg(feed->filename, sizeof(feed->filename), &p);
3864 q = strrchr(feed->filename, '>');
3867 feed->fmt = guess_format("ffm", NULL, NULL);
3868 /* defaut feed file */
3869 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3870 "/tmp/%s.ffm", feed->filename);
3871 feed->feed_max_size = 5 * 1024 * 1024;
3873 feed->feed = feed; /* self feeding :-) */
3875 } else if (!strcasecmp(cmd, "Launch")) {
3879 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3881 for (i = 0; i < 62; i++) {
3884 get_arg(argbuf, sizeof(argbuf), &p);
3888 feed->child_argv[i] = av_malloc(strlen(argbuf) + 1);
3889 strcpy(feed->child_argv[i], argbuf);
3892 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3894 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3896 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3897 inet_ntoa(my_http_addr.sin_addr),
3898 ntohs(my_http_addr.sin_port), feed->filename);
3903 fprintf(stdout, "Launch commandline: ");
3904 for (j = 0; j <= i; j++)
3905 fprintf(stdout, "%s ", feed->child_argv[j]);
3906 fprintf(stdout, "\n");
3909 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3911 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3913 } else if (stream) {
3914 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3916 } else if (!strcasecmp(cmd, "File")) {
3918 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3919 } else if (stream) {
3920 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3922 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3927 get_arg(arg, sizeof(arg), &p);
3929 fsize = strtod(p1, (char **)&p1);
3930 switch(toupper(*p1)) {
3935 fsize *= 1024 * 1024;
3938 fsize *= 1024 * 1024 * 1024;
3941 feed->feed_max_size = (int64_t)fsize;
3943 } else if (!strcasecmp(cmd, "</Feed>")) {
3945 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3946 filename, line_num);
3950 /* Make sure that we start out clean */
3951 if (unlink(feed->feed_filename) < 0
3952 && errno != ENOENT) {
3953 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3954 filename, line_num, feed->feed_filename, strerror(errno));
3960 } else if (!strcasecmp(cmd, "<Stream")) {
3961 /*********************************************/
3962 /* Stream related options */
3964 if (stream || feed) {
3965 fprintf(stderr, "%s:%d: Already in a tag\n",
3966 filename, line_num);
3968 stream = av_mallocz(sizeof(FFStream));
3969 *last_stream = stream;
3970 last_stream = &stream->next;
3972 get_arg(stream->filename, sizeof(stream->filename), &p);
3973 q = strrchr(stream->filename, '>');
3976 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3977 memset(&audio_enc, 0, sizeof(AVCodecContext));
3978 memset(&video_enc, 0, sizeof(AVCodecContext));
3979 audio_id = CODEC_ID_NONE;
3980 video_id = CODEC_ID_NONE;
3982 audio_id = stream->fmt->audio_codec;
3983 video_id = stream->fmt->video_codec;
3986 } else if (!strcasecmp(cmd, "Feed")) {
3987 get_arg(arg, sizeof(arg), &p);
3992 while (sfeed != NULL) {
3993 if (!strcmp(sfeed->filename, arg))
3995 sfeed = sfeed->next_feed;
3998 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3999 filename, line_num, arg);
4001 stream->feed = sfeed;
4004 } else if (!strcasecmp(cmd, "Format")) {
4005 get_arg(arg, sizeof(arg), &p);
4006 if (!strcmp(arg, "status")) {
4007 stream->stream_type = STREAM_TYPE_STATUS;
4010 stream->stream_type = STREAM_TYPE_LIVE;
4011 /* jpeg cannot be used here, so use single frame jpeg */
4012 if (!strcmp(arg, "jpeg"))
4013 strcpy(arg, "mjpeg");
4014 stream->fmt = guess_stream_format(arg, NULL, NULL);
4016 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4017 filename, line_num, arg);
4022 audio_id = stream->fmt->audio_codec;
4023 video_id = stream->fmt->video_codec;
4025 } else if (!strcasecmp(cmd, "InputFormat")) {
4026 stream->ifmt = av_find_input_format(arg);
4027 if (!stream->ifmt) {
4028 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4029 filename, line_num, arg);
4031 } else if (!strcasecmp(cmd, "FaviconURL")) {
4032 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4033 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4035 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4036 filename, line_num);
4039 } else if (!strcasecmp(cmd, "Author")) {
4041 get_arg(stream->author, sizeof(stream->author), &p);
4043 } else if (!strcasecmp(cmd, "Comment")) {
4045 get_arg(stream->comment, sizeof(stream->comment), &p);
4047 } else if (!strcasecmp(cmd, "Copyright")) {
4049 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4051 } else if (!strcasecmp(cmd, "Title")) {
4053 get_arg(stream->title, sizeof(stream->title), &p);
4055 } else if (!strcasecmp(cmd, "Preroll")) {
4056 get_arg(arg, sizeof(arg), &p);
4058 stream->prebuffer = atof(arg) * 1000;
4060 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4062 stream->send_on_key = 1;
4064 } else if (!strcasecmp(cmd, "AudioCodec")) {
4065 get_arg(arg, sizeof(arg), &p);
4066 audio_id = opt_audio_codec(arg);
4067 if (audio_id == CODEC_ID_NONE) {
4068 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4069 filename, line_num, arg);
4072 } else if (!strcasecmp(cmd, "VideoCodec")) {
4073 get_arg(arg, sizeof(arg), &p);
4074 video_id = opt_video_codec(arg);
4075 if (video_id == CODEC_ID_NONE) {
4076 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4077 filename, line_num, arg);
4080 } else if (!strcasecmp(cmd, "MaxTime")) {
4081 get_arg(arg, sizeof(arg), &p);
4083 stream->max_time = atof(arg) * 1000;
4085 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4086 get_arg(arg, sizeof(arg), &p);
4088 audio_enc.bit_rate = atoi(arg) * 1000;
4090 } else if (!strcasecmp(cmd, "AudioChannels")) {
4091 get_arg(arg, sizeof(arg), &p);
4093 audio_enc.channels = atoi(arg);
4095 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4096 get_arg(arg, sizeof(arg), &p);
4098 audio_enc.sample_rate = atoi(arg);
4100 } else if (!strcasecmp(cmd, "AudioQuality")) {
4101 get_arg(arg, sizeof(arg), &p);
4103 // audio_enc.quality = atof(arg) * 1000;
4105 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4107 int minrate, maxrate;
4109 get_arg(arg, sizeof(arg), &p);
4111 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4112 video_enc.rc_min_rate = minrate * 1000;
4113 video_enc.rc_max_rate = maxrate * 1000;
4115 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4116 filename, line_num, arg);
4120 } else if (!strcasecmp(cmd, "Debug")) {
4122 get_arg(arg, sizeof(arg), &p);
4123 video_enc.debug = strtol(arg,0,0);
4125 } else if (!strcasecmp(cmd, "Strict")) {
4127 get_arg(arg, sizeof(arg), &p);
4128 video_enc.strict_std_compliance = atoi(arg);
4130 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4132 get_arg(arg, sizeof(arg), &p);
4133 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4135 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4137 get_arg(arg, sizeof(arg), &p);
4138 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4140 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4141 get_arg(arg, sizeof(arg), &p);
4143 video_enc.bit_rate = atoi(arg) * 1000;
4145 } else if (!strcasecmp(cmd, "VideoSize")) {
4146 get_arg(arg, sizeof(arg), &p);
4148 parse_image_size(&video_enc.width, &video_enc.height, arg);
4149 if ((video_enc.width % 16) != 0 ||
4150 (video_enc.height % 16) != 0) {
4151 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4152 filename, line_num);
4156 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4157 get_arg(arg, sizeof(arg), &p);
4159 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4160 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4162 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4163 get_arg(arg, sizeof(arg), &p);
4165 video_enc.gop_size = atoi(arg);
4167 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4169 video_enc.gop_size = 1;
4171 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4173 video_enc.mb_decision = FF_MB_DECISION_BITS;
4175 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4177 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4178 video_enc.flags |= CODEC_FLAG_4MV;
4180 } else if (!strcasecmp(cmd, "VideoTag")) {
4181 get_arg(arg, sizeof(arg), &p);
4182 if ((strlen(arg) == 4) && stream) {
4183 video_enc.codec_tag = ff_get_fourcc(arg);
4185 } else if (!strcasecmp(cmd, "BitExact")) {
4187 video_enc.flags |= CODEC_FLAG_BITEXACT;
4189 } else if (!strcasecmp(cmd, "DctFastint")) {
4191 video_enc.dct_algo = FF_DCT_FASTINT;
4193 } else if (!strcasecmp(cmd, "IdctSimple")) {
4195 video_enc.idct_algo = FF_IDCT_SIMPLE;
4197 } else if (!strcasecmp(cmd, "Qscale")) {
4198 get_arg(arg, sizeof(arg), &p);
4200 video_enc.flags |= CODEC_FLAG_QSCALE;
4201 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4203 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4204 get_arg(arg, sizeof(arg), &p);
4206 video_enc.max_qdiff = atoi(arg);
4207 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4208 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4209 filename, line_num);
4213 } else if (!strcasecmp(cmd, "VideoQMax")) {
4214 get_arg(arg, sizeof(arg), &p);
4216 video_enc.qmax = atoi(arg);
4217 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4218 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4219 filename, line_num);
4223 } else if (!strcasecmp(cmd, "VideoQMin")) {
4224 get_arg(arg, sizeof(arg), &p);
4226 video_enc.qmin = atoi(arg);
4227 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4228 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4229 filename, line_num);
4233 } else if (!strcasecmp(cmd, "LumaElim")) {
4234 get_arg(arg, sizeof(arg), &p);
4236 video_enc.luma_elim_threshold = atoi(arg);
4238 } else if (!strcasecmp(cmd, "ChromaElim")) {
4239 get_arg(arg, sizeof(arg), &p);
4241 video_enc.chroma_elim_threshold = atoi(arg);
4243 } else if (!strcasecmp(cmd, "LumiMask")) {
4244 get_arg(arg, sizeof(arg), &p);
4246 video_enc.lumi_masking = atof(arg);
4248 } else if (!strcasecmp(cmd, "DarkMask")) {
4249 get_arg(arg, sizeof(arg), &p);
4251 video_enc.dark_masking = atof(arg);
4253 } else if (!strcasecmp(cmd, "NoVideo")) {
4254 video_id = CODEC_ID_NONE;
4255 } else if (!strcasecmp(cmd, "NoAudio")) {
4256 audio_id = CODEC_ID_NONE;
4257 } else if (!strcasecmp(cmd, "ACL")) {
4261 get_arg(arg, sizeof(arg), &p);
4262 if (strcasecmp(arg, "allow") == 0) {
4263 acl.action = IP_ALLOW;
4264 } else if (strcasecmp(arg, "deny") == 0) {
4265 acl.action = IP_DENY;
4267 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4268 filename, line_num, arg);
4272 get_arg(arg, sizeof(arg), &p);
4274 he = gethostbyname(arg);
4276 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4277 filename, line_num, arg);
4280 /* Only take the first */
4281 acl.first.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4282 acl.last = acl.first;
4285 get_arg(arg, sizeof(arg), &p);
4288 he = gethostbyname(arg);
4290 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4291 filename, line_num, arg);
4294 /* Only take the first */
4295 acl.last.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4300 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4301 IPAddressACL **naclp = 0;
4307 naclp = &stream->acl;
4311 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4312 filename, line_num);
4318 naclp = &(*naclp)->next;
4323 } else if (!strcasecmp(cmd, "RTSPOption")) {
4324 get_arg(arg, sizeof(arg), &p);
4326 av_freep(&stream->rtsp_option);
4327 /* XXX: av_strdup ? */
4328 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4329 if (stream->rtsp_option) {
4330 strcpy(stream->rtsp_option, arg);
4333 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4334 get_arg(arg, sizeof(arg), &p);
4336 if (!inet_aton(arg, &stream->multicast_ip)) {
4337 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4338 filename, line_num, arg);
4341 stream->is_multicast = 1;
4342 stream->loop = 1; /* default is looping */
4344 } else if (!strcasecmp(cmd, "MulticastPort")) {
4345 get_arg(arg, sizeof(arg), &p);
4347 stream->multicast_port = atoi(arg);
4349 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4350 get_arg(arg, sizeof(arg), &p);
4352 stream->multicast_ttl = atoi(arg);
4354 } else if (!strcasecmp(cmd, "NoLoop")) {
4358 } else if (!strcasecmp(cmd, "</Stream>")) {
4360 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4361 filename, line_num);
4364 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4365 if (audio_id != CODEC_ID_NONE) {
4366 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4367 audio_enc.codec_id = audio_id;
4368 add_codec(stream, &audio_enc);
4370 if (video_id != CODEC_ID_NONE) {
4371 video_enc.codec_type = CODEC_TYPE_VIDEO;
4372 video_enc.codec_id = video_id;
4373 add_codec(stream, &video_enc);
4377 } else if (!strcasecmp(cmd, "<Redirect")) {
4378 /*********************************************/
4380 if (stream || feed || redirect) {
4381 fprintf(stderr, "%s:%d: Already in a tag\n",
4382 filename, line_num);
4385 redirect = av_mallocz(sizeof(FFStream));
4386 *last_stream = redirect;
4387 last_stream = &redirect->next;
4389 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4390 q = strrchr(redirect->filename, '>');
4393 redirect->stream_type = STREAM_TYPE_REDIRECT;
4395 } else if (!strcasecmp(cmd, "URL")) {
4397 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4399 } else if (!strcasecmp(cmd, "</Redirect>")) {
4401 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4402 filename, line_num);
4405 if (!redirect->feed_filename[0]) {
4406 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4407 filename, line_num);
4411 } else if (!strcasecmp(cmd, "LoadModule")) {
4412 get_arg(arg, sizeof(arg), &p);
4416 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4417 filename, line_num, arg);
4421 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4422 filename, line_num, cmd);
4436 static void write_packet(FFCodec *ffenc,
4437 uint8_t *buf, int size)
4440 AVCodecContext *enc = &ffenc->enc;
4442 mk_header(&hdr, enc, size);
4443 wptr = http_fifo.wptr;
4444 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4445 fifo_write(&http_fifo, buf, size, &wptr);
4446 /* atomic modification of wptr */
4447 http_fifo.wptr = wptr;
4448 ffenc->data_count += size;
4449 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4453 static void show_banner(void)
4455 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4458 static void show_help(void)
4461 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4462 "Hyper fast multi format Audio/Video streaming server\n"
4464 "-L : print the LICENSE\n"
4466 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4470 static void show_license(void)
4474 "FFmpeg is free software; you can redistribute it and/or\n"
4475 "modify it under the terms of the GNU Lesser General Public\n"
4476 "License as published by the Free Software Foundation; either\n"
4477 "version 2.1 of the License, or (at your option) any later version.\n"
4479 "FFmpeg is distributed in the hope that it will be useful,\n"
4480 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4481 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4482 "Lesser General Public License for more details.\n"
4484 "You should have received a copy of the GNU Lesser General Public\n"
4485 "License along with FFmpeg; if not, write to the Free Software\n"
4486 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
4490 static void handle_child_exit(int sig)
4495 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4498 for (feed = first_feed; feed; feed = feed->next) {
4499 if (feed->pid == pid) {
4500 int uptime = time(0) - feed->pid_start;
4503 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4506 /* Turn off any more restarts */
4507 feed->child_argv = 0;
4513 need_to_start_children = 1;
4516 int main(int argc, char **argv)
4518 const char *config_filename;
4520 struct sigaction sigact;
4524 config_filename = "/etc/ffserver.conf";
4526 my_program_name = argv[0];
4527 my_program_dir = getcwd(0, 0);
4528 ffserver_daemon = 1;
4531 c = getopt(argc, argv, "ndLh?f:");
4547 ffserver_daemon = 0;
4550 config_filename = optarg;
4557 putenv("http_proxy"); /* Kill the http_proxy */
4559 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4561 /* address on which the server will handle HTTP connections */
4562 my_http_addr.sin_family = AF_INET;
4563 my_http_addr.sin_port = htons (8080);
4564 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4566 /* address on which the server will handle RTSP connections */
4567 my_rtsp_addr.sin_family = AF_INET;
4568 my_rtsp_addr.sin_port = htons (5454);
4569 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4571 nb_max_connections = 5;
4572 max_bandwidth = 1000;
4573 first_stream = NULL;
4574 logfilename[0] = '\0';
4576 memset(&sigact, 0, sizeof(sigact));
4577 sigact.sa_handler = handle_child_exit;
4578 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4579 sigaction(SIGCHLD, &sigact, 0);
4581 if (parse_ffconfig(config_filename) < 0) {
4582 fprintf(stderr, "Incorrect config file - exiting.\n");
4586 build_file_streams();
4588 build_feed_streams();
4590 compute_bandwidth();
4592 /* put the process in background and detach it from its TTY */
4593 if (ffserver_daemon) {
4600 } else if (pid > 0) {
4608 open("/dev/null", O_RDWR);
4609 if (strcmp(logfilename, "-") != 0) {
4619 signal(SIGPIPE, SIG_IGN);
4621 /* open log file if needed */
4622 if (logfilename[0] != '\0') {
4623 if (!strcmp(logfilename, "-"))
4626 logfile = fopen(logfilename, "w");
4629 if (http_server() < 0) {
4630 fprintf(stderr, "Could not start server\n");