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;
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 */
1218 pstrcpy(info, sizeof(info), p);
1224 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1225 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1227 if (*useragent && *useragent != '\n' && isspace(*useragent))
1231 p = strchr(p, '\n');
1238 redir_type = REDIR_NONE;
1239 if (match_ext(filename, "asx")) {
1240 redir_type = REDIR_ASX;
1241 filename[strlen(filename)-1] = 'f';
1242 } else if (match_ext(filename, "asf") &&
1243 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1244 /* if this isn't WMP or lookalike, return the redirector file */
1245 redir_type = REDIR_ASF;
1246 } else if (match_ext(filename, "rpm,ram")) {
1247 redir_type = REDIR_RAM;
1248 strcpy(filename + strlen(filename)-2, "m");
1249 } else if (match_ext(filename, "rtsp")) {
1250 redir_type = REDIR_RTSP;
1251 compute_real_filename(filename, sizeof(url) - 1);
1252 } else if (match_ext(filename, "sdp")) {
1253 redir_type = REDIR_SDP;
1254 compute_real_filename(filename, sizeof(url) - 1);
1257 stream = first_stream;
1258 while (stream != NULL) {
1259 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1261 stream = stream->next;
1263 if (stream == NULL) {
1264 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1269 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1270 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1272 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1273 c->http_error = 301;
1275 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1276 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1277 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1278 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1279 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1280 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1281 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1283 /* prepare output buffer */
1284 c->buffer_ptr = c->buffer;
1286 c->state = HTTPSTATE_SEND_HEADER;
1290 /* If this is WMP, get the rate information */
1291 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1292 if (modify_current_stream(c, ratebuf)) {
1293 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1294 if (c->switch_feed_streams[i] >= 0)
1295 do_switch_stream(c, i);
1300 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1301 current_bandwidth += stream->bandwidth;
1304 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1305 c->http_error = 200;
1307 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1308 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1309 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1310 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1311 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The server is too busy to serve your request at this time.</p>\r\n");
1312 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec.</p>\r\n",
1313 current_bandwidth, max_bandwidth);
1314 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1316 /* prepare output buffer */
1317 c->buffer_ptr = c->buffer;
1319 c->state = HTTPSTATE_SEND_HEADER;
1323 if (redir_type != REDIR_NONE) {
1326 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1327 if (strncasecmp(p, "Host:", 5) == 0) {
1331 p = strchr(p, '\n');
1342 while (isspace(*hostinfo))
1345 eoh = strchr(hostinfo, '\n');
1347 if (eoh[-1] == '\r')
1350 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1351 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1352 hostbuf[eoh - hostinfo] = 0;
1354 c->http_error = 200;
1356 switch(redir_type) {
1358 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1359 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1362 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1363 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1364 hostbuf, filename, info);
1365 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1368 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1369 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1370 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1371 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1372 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1373 hostbuf, filename, info);
1376 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1377 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1378 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1379 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1380 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1381 hostbuf, filename, info);
1385 char hostname[256], *p;
1386 /* extract only hostname */
1387 pstrcpy(hostname, sizeof(hostname), hostbuf);
1388 p = strrchr(hostname, ':');
1391 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1392 /* XXX: incorrect mime type ? */
1393 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1394 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1395 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1396 hostname, ntohs(my_rtsp_addr.sin_port),
1403 int sdp_data_size, len;
1404 struct sockaddr_in my_addr;
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1408 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1410 len = sizeof(my_addr);
1411 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1413 /* XXX: should use a dynamic buffer */
1414 sdp_data_size = prepare_sdp_description(stream,
1417 if (sdp_data_size > 0) {
1418 memcpy(q, sdp_data, sdp_data_size);
1430 /* prepare output buffer */
1431 c->buffer_ptr = c->buffer;
1433 c->state = HTTPSTATE_SEND_HEADER;
1439 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1443 stream->conns_served++;
1445 /* XXX: add there authenticate and IP match */
1448 /* if post, it means a feed is being sent */
1449 if (!stream->is_feed) {
1450 /* However it might be a status report from WMP! Lets log the data
1451 * as it might come in handy one day
1456 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1457 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1461 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1462 client_id = strtol(p + 18, 0, 10);
1464 p = strchr(p, '\n');
1472 char *eol = strchr(logline, '\n');
1477 if (eol[-1] == '\r')
1479 http_log("%.*s\n", (int) (eol - logline), logline);
1480 c->suppress_log = 1;
1485 http_log("\nGot request:\n%s\n", c->buffer);
1488 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1491 /* Now we have to find the client_id */
1492 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1493 if (wmpc->wmp_client_id == client_id)
1498 if (modify_current_stream(wmpc, ratebuf)) {
1499 wmpc->switch_pending = 1;
1504 snprintf(msg, sizeof(msg), "POST command not handled");
1508 if (http_start_receive_data(c) < 0) {
1509 snprintf(msg, sizeof(msg), "could not open feed");
1513 c->state = HTTPSTATE_RECEIVE_DATA;
1518 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1519 http_log("\nGot request:\n%s\n", c->buffer);
1523 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1526 /* open input stream */
1527 if (open_input_stream(c, info) < 0) {
1528 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1532 /* prepare http header */
1534 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1535 mime_type = c->stream->fmt->mime_type;
1537 mime_type = "application/x-octet_stream";
1538 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1540 /* for asf, we need extra headers */
1541 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1542 /* Need to allocate a client id */
1544 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1546 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);
1548 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1549 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1551 /* prepare output buffer */
1553 c->buffer_ptr = c->buffer;
1555 c->state = HTTPSTATE_SEND_HEADER;
1558 c->http_error = 404;
1560 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1561 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1562 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1563 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1564 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1565 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1566 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1568 /* prepare output buffer */
1569 c->buffer_ptr = c->buffer;
1571 c->state = HTTPSTATE_SEND_HEADER;
1575 c->http_error = 200; /* horrible : we use this value to avoid
1576 going to the send data state */
1577 c->state = HTTPSTATE_SEND_HEADER;
1581 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1583 static const char *suffix = " kMGTP";
1586 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1589 url_fprintf(pb, "%"PRId64"%c", count, *s);
1592 static void compute_stats(HTTPContext *c)
1599 ByteIOContext pb1, *pb = &pb1;
1601 if (url_open_dyn_buf(pb) < 0) {
1602 /* XXX: return an error ? */
1603 c->buffer_ptr = c->buffer;
1604 c->buffer_end = c->buffer;
1608 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1609 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1610 url_fprintf(pb, "Pragma: no-cache\r\n");
1611 url_fprintf(pb, "\r\n");
1613 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1614 if (c->stream->feed_filename) {
1615 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1617 url_fprintf(pb, "</HEAD>\n<BODY>");
1618 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1620 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1621 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1622 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");
1623 stream = first_stream;
1624 while (stream != NULL) {
1625 char sfilename[1024];
1628 if (stream->feed != stream) {
1629 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1630 eosf = sfilename + strlen(sfilename);
1631 if (eosf - sfilename >= 4) {
1632 if (strcmp(eosf - 4, ".asf") == 0) {
1633 strcpy(eosf - 4, ".asx");
1634 } else if (strcmp(eosf - 3, ".rm") == 0) {
1635 strcpy(eosf - 3, ".ram");
1636 } else if (stream->fmt == &rtp_muxer) {
1637 /* generate a sample RTSP director if
1638 unicast. Generate an SDP redirector if
1640 eosf = strrchr(sfilename, '.');
1642 eosf = sfilename + strlen(sfilename);
1643 if (stream->is_multicast)
1644 strcpy(eosf, ".sdp");
1646 strcpy(eosf, ".rtsp");
1650 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1651 sfilename, stream->filename);
1652 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1653 stream->conns_served);
1654 fmt_bytecount(pb, stream->bytes_served);
1655 switch(stream->stream_type) {
1656 case STREAM_TYPE_LIVE:
1658 int audio_bit_rate = 0;
1659 int video_bit_rate = 0;
1660 const char *audio_codec_name = "";
1661 const char *video_codec_name = "";
1662 const char *audio_codec_name_extra = "";
1663 const char *video_codec_name_extra = "";
1665 for(i=0;i<stream->nb_streams;i++) {
1666 AVStream *st = stream->streams[i];
1667 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1668 switch(st->codec->codec_type) {
1669 case CODEC_TYPE_AUDIO:
1670 audio_bit_rate += st->codec->bit_rate;
1672 if (*audio_codec_name)
1673 audio_codec_name_extra = "...";
1674 audio_codec_name = codec->name;
1677 case CODEC_TYPE_VIDEO:
1678 video_bit_rate += st->codec->bit_rate;
1680 if (*video_codec_name)
1681 video_codec_name_extra = "...";
1682 video_codec_name = codec->name;
1685 case CODEC_TYPE_DATA:
1686 video_bit_rate += st->codec->bit_rate;
1692 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",
1695 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1696 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1698 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1700 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1702 url_fprintf(pb, "\n");
1706 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1710 stream = stream->next;
1712 url_fprintf(pb, "</TABLE>\n");
1714 stream = first_stream;
1715 while (stream != NULL) {
1716 if (stream->feed == stream) {
1717 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1719 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1721 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1726 /* This is somewhat linux specific I guess */
1727 snprintf(ps_cmd, sizeof(ps_cmd),
1728 "ps -o \"%%cpu,cputime\" --no-headers %d",
1731 pid_stat = popen(ps_cmd, "r");
1736 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1738 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1746 url_fprintf(pb, "<p>");
1748 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");
1750 for (i = 0; i < stream->nb_streams; i++) {
1751 AVStream *st = stream->streams[i];
1752 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1753 const char *type = "unknown";
1754 char parameters[64];
1758 switch(st->codec->codec_type) {
1759 case CODEC_TYPE_AUDIO:
1762 case CODEC_TYPE_VIDEO:
1764 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1765 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1770 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1771 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1773 url_fprintf(pb, "</table>\n");
1776 stream = stream->next;
1782 AVCodecContext *enc;
1786 stream = first_feed;
1787 while (stream != NULL) {
1788 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1789 url_fprintf(pb, "<TABLE>\n");
1790 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1791 for(i=0;i<stream->nb_streams;i++) {
1792 AVStream *st = stream->streams[i];
1793 FeedData *fdata = st->priv_data;
1796 avcodec_string(buf, sizeof(buf), enc);
1797 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1798 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1799 avg /= enc->frame_size;
1800 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1801 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1803 url_fprintf(pb, "</TABLE>\n");
1804 stream = stream->next_feed;
1809 /* connection status */
1810 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1812 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1813 nb_connections, nb_max_connections);
1815 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1816 current_bandwidth, max_bandwidth);
1818 url_fprintf(pb, "<TABLE>\n");
1819 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");
1820 c1 = first_http_ctx;
1822 while (c1 != NULL) {
1828 for (j = 0; j < c1->stream->nb_streams; j++) {
1829 if (!c1->stream->feed) {
1830 bitrate += c1->stream->streams[j]->codec->bit_rate;
1832 if (c1->feed_streams[j] >= 0) {
1833 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1840 p = inet_ntoa(c1->from_addr.sin_addr);
1841 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1843 c1->stream ? c1->stream->filename : "",
1844 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1847 http_state[c1->state]);
1848 fmt_bytecount(pb, bitrate);
1849 url_fprintf(pb, "<td align=right>");
1850 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1851 url_fprintf(pb, "<td align=right>");
1852 fmt_bytecount(pb, c1->data_count);
1853 url_fprintf(pb, "\n");
1856 url_fprintf(pb, "</TABLE>\n");
1861 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1862 url_fprintf(pb, "</BODY>\n</HTML>\n");
1864 len = url_close_dyn_buf(pb, &c->pb_buffer);
1865 c->buffer_ptr = c->pb_buffer;
1866 c->buffer_end = c->pb_buffer + len;
1869 /* check if the parser needs to be opened for stream i */
1870 static void open_parser(AVFormatContext *s, int i)
1872 AVStream *st = s->streams[i];
1875 if (!st->codec->codec) {
1876 codec = avcodec_find_decoder(st->codec->codec_id);
1877 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1878 st->codec->parse_only = 1;
1879 if (avcodec_open(st->codec, codec) < 0) {
1880 st->codec->parse_only = 0;
1886 static int open_input_stream(HTTPContext *c, const char *info)
1889 char input_filename[1024];
1894 /* find file name */
1895 if (c->stream->feed) {
1896 strcpy(input_filename, c->stream->feed->feed_filename);
1897 buf_size = FFM_PACKET_SIZE;
1898 /* compute position (absolute time) */
1899 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1900 stream_pos = parse_date(buf, 0);
1901 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1902 int prebuffer = strtol(buf, 0, 10);
1903 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1905 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1908 strcpy(input_filename, c->stream->feed_filename);
1910 /* compute position (relative time) */
1911 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1912 stream_pos = parse_date(buf, 1);
1917 if (input_filename[0] == '\0')
1921 { time_t when = stream_pos / 1000000;
1922 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1927 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1928 buf_size, c->stream->ap_in) < 0) {
1929 http_log("%s not found", input_filename);
1934 /* open each parser */
1935 for(i=0;i<s->nb_streams;i++)
1938 /* choose stream as clock source (we favorize video stream if
1939 present) for packet sending */
1940 c->pts_stream_index = 0;
1941 for(i=0;i<c->stream->nb_streams;i++) {
1942 if (c->pts_stream_index == 0 &&
1943 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1944 c->pts_stream_index = i;
1949 if (c->fmt_in->iformat->read_seek) {
1950 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1953 /* set the start time (needed for maxtime and RTP packet timing) */
1954 c->start_time = cur_time;
1955 c->first_pts = AV_NOPTS_VALUE;
1959 /* return the server clock (in us) */
1960 static int64_t get_server_clock(HTTPContext *c)
1962 /* compute current pts value from system time */
1963 return (cur_time - c->start_time) * 1000;
1966 /* return the estimated time at which the current packet must be sent
1968 static int64_t get_packet_send_clock(HTTPContext *c)
1970 int bytes_left, bytes_sent, frame_bytes;
1972 frame_bytes = c->cur_frame_bytes;
1973 if (frame_bytes <= 0) {
1976 bytes_left = c->buffer_end - c->buffer_ptr;
1977 bytes_sent = frame_bytes - bytes_left;
1978 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1983 static int http_prepare_data(HTTPContext *c)
1986 AVFormatContext *ctx;
1988 av_freep(&c->pb_buffer);
1990 case HTTPSTATE_SEND_DATA_HEADER:
1991 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1992 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
1994 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
1995 c->stream->comment);
1996 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
1997 c->stream->copyright);
1998 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2001 /* open output stream by using specified codecs */
2002 c->fmt_ctx.oformat = c->stream->fmt;
2003 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2004 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2007 st = av_mallocz(sizeof(AVStream));
2008 st->codec= avcodec_alloc_context();
2009 c->fmt_ctx.streams[i] = st;
2010 /* if file or feed, then just take streams from FFStream struct */
2011 if (!c->stream->feed ||
2012 c->stream->feed == c->stream)
2013 src = c->stream->streams[i];
2015 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2019 st->codec->frame_number = 0; /* XXX: should be done in
2020 AVStream, not in codec */
2021 /* I'm pretty sure that this is not correct...
2022 * However, without it, we crash
2024 st->codec->coded_frame = &dummy_frame;
2026 c->got_key_frame = 0;
2028 /* prepare header and save header data in a stream */
2029 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2030 /* XXX: potential leak */
2033 c->fmt_ctx.pb.is_streamed = 1;
2035 av_set_parameters(&c->fmt_ctx, NULL);
2036 if (av_write_header(&c->fmt_ctx) < 0)
2039 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2040 c->buffer_ptr = c->pb_buffer;
2041 c->buffer_end = c->pb_buffer + len;
2043 c->state = HTTPSTATE_SEND_DATA;
2044 c->last_packet_sent = 0;
2046 case HTTPSTATE_SEND_DATA:
2047 /* find a new packet */
2051 /* read a packet from the input stream */
2052 if (c->stream->feed) {
2053 ffm_set_write_index(c->fmt_in,
2054 c->stream->feed->feed_write_index,
2055 c->stream->feed->feed_size);
2058 if (c->stream->max_time &&
2059 c->stream->max_time + c->start_time - cur_time < 0) {
2060 /* We have timed out */
2061 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2064 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2065 if (c->stream->feed && c->stream->feed->feed_opened) {
2066 /* if coming from feed, it means we reached the end of the
2067 ffm file, so must wait for more data */
2068 c->state = HTTPSTATE_WAIT_FEED;
2069 return 1; /* state changed */
2071 if (c->stream->loop) {
2072 av_close_input_file(c->fmt_in);
2074 if (open_input_stream(c, "") < 0)
2079 /* must send trailer now because eof or error */
2080 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2084 /* update first pts if needed */
2085 if (c->first_pts == AV_NOPTS_VALUE) {
2086 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2087 c->start_time = cur_time;
2089 /* send it to the appropriate stream */
2090 if (c->stream->feed) {
2091 /* if coming from a feed, select the right stream */
2092 if (c->switch_pending) {
2093 c->switch_pending = 0;
2094 for(i=0;i<c->stream->nb_streams;i++) {
2095 if (c->switch_feed_streams[i] == pkt.stream_index) {
2096 if (pkt.flags & PKT_FLAG_KEY) {
2097 do_switch_stream(c, i);
2100 if (c->switch_feed_streams[i] >= 0) {
2101 c->switch_pending = 1;
2105 for(i=0;i<c->stream->nb_streams;i++) {
2106 if (c->feed_streams[i] == pkt.stream_index) {
2107 pkt.stream_index = i;
2108 if (pkt.flags & PKT_FLAG_KEY) {
2109 c->got_key_frame |= 1 << i;
2111 /* See if we have all the key frames, then
2112 * we start to send. This logic is not quite
2113 * right, but it works for the case of a
2114 * single video stream with one or more
2115 * audio streams (for which every frame is
2116 * typically a key frame).
2118 if (!c->stream->send_on_key ||
2119 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2125 AVCodecContext *codec;
2128 /* specific handling for RTP: we use several
2129 output stream (one for each RTP
2130 connection). XXX: need more abstract handling */
2131 if (c->is_packetized) {
2133 /* compute send time and duration */
2134 st = c->fmt_in->streams[pkt.stream_index];
2135 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2136 if (st->start_time != AV_NOPTS_VALUE)
2137 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2138 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2140 printf("index=%d pts=%0.3f duration=%0.6f\n",
2142 (double)c->cur_pts /
2144 (double)c->cur_frame_duration /
2147 /* find RTP context */
2148 c->packet_stream_index = pkt.stream_index;
2149 ctx = c->rtp_ctx[c->packet_stream_index];
2151 av_free_packet(&pkt);
2154 codec = ctx->streams[0]->codec;
2155 /* only one stream per RTP connection */
2156 pkt.stream_index = 0;
2160 codec = ctx->streams[pkt.stream_index]->codec;
2163 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2164 if (c->is_packetized) {
2165 int max_packet_size;
2166 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2167 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2169 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2170 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2172 ret = url_open_dyn_buf(&ctx->pb);
2175 /* XXX: potential leak */
2178 if (pkt.dts != AV_NOPTS_VALUE)
2179 pkt.dts = av_rescale_q(pkt.dts,
2180 c->fmt_in->streams[pkt.stream_index]->time_base,
2181 ctx->streams[pkt.stream_index]->time_base);
2182 if (pkt.pts != AV_NOPTS_VALUE)
2183 pkt.pts = av_rescale_q(pkt.pts,
2184 c->fmt_in->streams[pkt.stream_index]->time_base,
2185 ctx->streams[pkt.stream_index]->time_base);
2186 if (av_write_frame(ctx, &pkt)) {
2187 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2190 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2191 c->cur_frame_bytes = len;
2192 c->buffer_ptr = c->pb_buffer;
2193 c->buffer_end = c->pb_buffer + len;
2195 codec->frame_number++;
2199 av_free_packet(&pkt);
2205 case HTTPSTATE_SEND_DATA_TRAILER:
2206 /* last packet test ? */
2207 if (c->last_packet_sent || c->is_packetized)
2210 /* prepare header */
2211 if (url_open_dyn_buf(&ctx->pb) < 0) {
2212 /* XXX: potential leak */
2215 av_write_trailer(ctx);
2216 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2217 c->buffer_ptr = c->pb_buffer;
2218 c->buffer_end = c->pb_buffer + len;
2220 c->last_packet_sent = 1;
2227 #define SHORT_TERM_BANDWIDTH 8000000
2229 /* should convert the format at the same time */
2230 /* send data starting at c->buffer_ptr to the output connection
2231 (either UDP or TCP connection) */
2232 static int http_send_data(HTTPContext *c)
2237 if (c->buffer_ptr >= c->buffer_end) {
2238 ret = http_prepare_data(c);
2241 else if (ret != 0) {
2242 /* state change requested */
2246 if (c->is_packetized) {
2247 /* RTP data output */
2248 len = c->buffer_end - c->buffer_ptr;
2250 /* fail safe - should never happen */
2252 c->buffer_ptr = c->buffer_end;
2255 len = (c->buffer_ptr[0] << 24) |
2256 (c->buffer_ptr[1] << 16) |
2257 (c->buffer_ptr[2] << 8) |
2259 if (len > (c->buffer_end - c->buffer_ptr))
2261 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2262 /* nothing to send yet: we can wait */
2266 c->data_count += len;
2267 update_datarate(&c->datarate, c->data_count);
2269 c->stream->bytes_served += len;
2271 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2272 /* RTP packets are sent inside the RTSP TCP connection */
2273 ByteIOContext pb1, *pb = &pb1;
2274 int interleaved_index, size;
2276 HTTPContext *rtsp_c;
2279 /* if no RTSP connection left, error */
2282 /* if already sending something, then wait. */
2283 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
2286 if (url_open_dyn_buf(pb) < 0)
2288 interleaved_index = c->packet_stream_index * 2;
2289 /* RTCP packets are sent at odd indexes */
2290 if (c->buffer_ptr[1] == 200)
2291 interleaved_index++;
2292 /* write RTSP TCP header */
2294 header[1] = interleaved_index;
2295 header[2] = len >> 8;
2297 put_buffer(pb, header, 4);
2298 /* write RTP packet data */
2300 put_buffer(pb, c->buffer_ptr, len);
2301 size = url_close_dyn_buf(pb, &c->packet_buffer);
2302 /* prepare asynchronous TCP sending */
2303 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2304 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2305 c->buffer_ptr += len;
2307 /* send everything we can NOW */
2308 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2309 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2311 rtsp_c->packet_buffer_ptr += len;
2313 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2314 /* if we could not send all the data, we will
2315 send it later, so a new state is needed to
2316 "lock" the RTSP TCP connection */
2317 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2320 /* all data has been sent */
2321 av_freep(&c->packet_buffer);
2324 /* send RTP packet directly in UDP */
2326 url_write(c->rtp_handles[c->packet_stream_index],
2327 c->buffer_ptr, len);
2328 c->buffer_ptr += len;
2329 /* here we continue as we can send several packets per 10 ms slot */
2332 /* TCP data output */
2333 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2335 if (errno != EAGAIN && errno != EINTR) {
2336 /* error : close connection */
2342 c->buffer_ptr += len;
2344 c->data_count += len;
2345 update_datarate(&c->datarate, c->data_count);
2347 c->stream->bytes_served += len;
2355 static int http_start_receive_data(HTTPContext *c)
2359 if (c->stream->feed_opened)
2362 /* Don't permit writing to this one */
2363 if (c->stream->readonly)
2367 fd = open(c->stream->feed_filename, O_RDWR);
2372 c->stream->feed_write_index = ffm_read_write_index(fd);
2373 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2374 lseek(fd, 0, SEEK_SET);
2376 /* init buffer input */
2377 c->buffer_ptr = c->buffer;
2378 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2379 c->stream->feed_opened = 1;
2383 static int http_receive_data(HTTPContext *c)
2387 if (c->buffer_end > c->buffer_ptr) {
2390 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2392 if (errno != EAGAIN && errno != EINTR) {
2393 /* error : close connection */
2396 } else if (len == 0) {
2397 /* end of connection : close it */
2400 c->buffer_ptr += len;
2401 c->data_count += len;
2402 update_datarate(&c->datarate, c->data_count);
2406 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2407 if (c->buffer[0] != 'f' ||
2408 c->buffer[1] != 'm') {
2409 http_log("Feed stream has become desynchronized -- disconnecting\n");
2414 if (c->buffer_ptr >= c->buffer_end) {
2415 FFStream *feed = c->stream;
2416 /* a packet has been received : write it in the store, except
2418 if (c->data_count > FFM_PACKET_SIZE) {
2420 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2421 /* XXX: use llseek or url_seek */
2422 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2423 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2425 feed->feed_write_index += FFM_PACKET_SIZE;
2426 /* update file size */
2427 if (feed->feed_write_index > c->stream->feed_size)
2428 feed->feed_size = feed->feed_write_index;
2430 /* handle wrap around if max file size reached */
2431 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2432 feed->feed_write_index = FFM_PACKET_SIZE;
2435 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2437 /* wake up any waiting connections */
2438 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2439 if (c1->state == HTTPSTATE_WAIT_FEED &&
2440 c1->stream->feed == c->stream->feed) {
2441 c1->state = HTTPSTATE_SEND_DATA;
2445 /* We have a header in our hands that contains useful data */
2447 AVInputFormat *fmt_in;
2448 ByteIOContext *pb = &s.pb;
2451 memset(&s, 0, sizeof(s));
2453 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2454 pb->buf_end = c->buffer_end; /* ?? */
2455 pb->is_streamed = 1;
2457 /* use feed output format name to find corresponding input format */
2458 fmt_in = av_find_input_format(feed->fmt->name);
2462 if (fmt_in->priv_data_size > 0) {
2463 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2469 if (fmt_in->read_header(&s, 0) < 0) {
2470 av_freep(&s.priv_data);
2474 /* Now we have the actual streams */
2475 if (s.nb_streams != feed->nb_streams) {
2476 av_freep(&s.priv_data);
2479 for (i = 0; i < s.nb_streams; i++) {
2480 memcpy(feed->streams[i]->codec,
2481 s.streams[i]->codec, sizeof(AVCodecContext));
2483 av_freep(&s.priv_data);
2485 c->buffer_ptr = c->buffer;
2490 c->stream->feed_opened = 0;
2495 /********************************************************************/
2498 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2505 switch(error_number) {
2506 #define DEF(n, c, s) case c: str = s; break;
2507 #include "rtspcodes.h"
2510 str = "Unknown Error";
2514 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2515 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2517 /* output GMT time */
2521 p = buf2 + strlen(p) - 1;
2524 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2527 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2529 rtsp_reply_header(c, error_number);
2530 url_fprintf(c->pb, "\r\n");
2533 static int rtsp_parse_request(HTTPContext *c)
2535 const char *p, *p1, *p2;
2542 RTSPHeader header1, *header = &header1;
2544 c->buffer_ptr[0] = '\0';
2547 get_word(cmd, sizeof(cmd), &p);
2548 get_word(url, sizeof(url), &p);
2549 get_word(protocol, sizeof(protocol), &p);
2551 pstrcpy(c->method, sizeof(c->method), cmd);
2552 pstrcpy(c->url, sizeof(c->url), url);
2553 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2556 if (url_open_dyn_buf(c->pb) < 0) {
2557 /* XXX: cannot do more */
2558 c->pb = NULL; /* safety */
2562 /* check version name */
2563 if (strcmp(protocol, "RTSP/1.0") != 0) {
2564 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2568 /* parse each header line */
2569 memset(header, 0, sizeof(RTSPHeader));
2570 /* skip to next line */
2571 while (*p != '\n' && *p != '\0')
2575 while (*p != '\0') {
2576 p1 = strchr(p, '\n');
2580 if (p2 > p && p2[-1] == '\r')
2582 /* skip empty line */
2586 if (len > sizeof(line) - 1)
2587 len = sizeof(line) - 1;
2588 memcpy(line, p, len);
2590 rtsp_parse_line(header, line);
2594 /* handle sequence number */
2595 c->seq = header->seq;
2597 if (!strcmp(cmd, "DESCRIBE")) {
2598 rtsp_cmd_describe(c, url);
2599 } else if (!strcmp(cmd, "OPTIONS")) {
2600 rtsp_cmd_options(c, url);
2601 } else if (!strcmp(cmd, "SETUP")) {
2602 rtsp_cmd_setup(c, url, header);
2603 } else if (!strcmp(cmd, "PLAY")) {
2604 rtsp_cmd_play(c, url, header);
2605 } else if (!strcmp(cmd, "PAUSE")) {
2606 rtsp_cmd_pause(c, url, header);
2607 } else if (!strcmp(cmd, "TEARDOWN")) {
2608 rtsp_cmd_teardown(c, url, header);
2610 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2613 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2614 c->pb = NULL; /* safety */
2616 /* XXX: cannot do more */
2619 c->buffer_ptr = c->pb_buffer;
2620 c->buffer_end = c->pb_buffer + len;
2621 c->state = RTSPSTATE_SEND_REPLY;
2625 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2627 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2628 struct in_addr my_ip)
2630 ByteIOContext pb1, *pb = &pb1;
2631 int i, payload_type, port, private_payload_type, j;
2632 const char *ipstr, *title, *mediatype;
2635 if (url_open_dyn_buf(pb) < 0)
2638 /* general media info */
2640 url_fprintf(pb, "v=0\n");
2641 ipstr = inet_ntoa(my_ip);
2642 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2643 title = stream->title;
2644 if (title[0] == '\0')
2646 url_fprintf(pb, "s=%s\n", title);
2647 if (stream->comment[0] != '\0')
2648 url_fprintf(pb, "i=%s\n", stream->comment);
2649 if (stream->is_multicast) {
2650 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2652 /* for each stream, we output the necessary info */
2653 private_payload_type = RTP_PT_PRIVATE;
2654 for(i = 0; i < stream->nb_streams; i++) {
2655 st = stream->streams[i];
2656 if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
2657 mediatype = "video";
2659 switch(st->codec->codec_type) {
2660 case CODEC_TYPE_AUDIO:
2661 mediatype = "audio";
2663 case CODEC_TYPE_VIDEO:
2664 mediatype = "video";
2667 mediatype = "application";
2671 /* NOTE: the port indication is not correct in case of
2672 unicast. It is not an issue because RTSP gives it */
2673 payload_type = rtp_get_payload_type(st->codec);
2674 if (payload_type < 0)
2675 payload_type = private_payload_type++;
2676 if (stream->is_multicast) {
2677 port = stream->multicast_port + 2 * i;
2681 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2682 mediatype, port, payload_type);
2683 if (payload_type >= RTP_PT_PRIVATE) {
2684 /* for private payload type, we need to give more info */
2685 switch(st->codec->codec_id) {
2686 case CODEC_ID_MPEG4:
2689 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2690 payload_type, 90000);
2691 /* we must also add the mpeg4 header */
2692 data = st->codec->extradata;
2694 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2695 for(j=0;j<st->codec->extradata_size;j++) {
2696 url_fprintf(pb, "%02x", data[j]);
2698 url_fprintf(pb, "\n");
2703 /* XXX: add other codecs ? */
2707 url_fprintf(pb, "a=control:streamid=%d\n", i);
2709 return url_close_dyn_buf(pb, pbuffer);
2711 url_close_dyn_buf(pb, pbuffer);
2716 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2718 // rtsp_reply_header(c, RTSP_STATUS_OK);
2719 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2720 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2721 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2722 url_fprintf(c->pb, "\r\n");
2725 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2731 int content_length, len;
2732 struct sockaddr_in my_addr;
2734 /* find which url is asked */
2735 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2740 for(stream = first_stream; stream != NULL; stream = stream->next) {
2741 if (!stream->is_feed && stream->fmt == &rtp_muxer &&
2742 !strcmp(path, stream->filename)) {
2746 /* no stream found */
2747 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2751 /* prepare the media description in sdp format */
2753 /* get the host IP */
2754 len = sizeof(my_addr);
2755 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2756 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2757 if (content_length < 0) {
2758 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2761 rtsp_reply_header(c, RTSP_STATUS_OK);
2762 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2763 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2764 url_fprintf(c->pb, "\r\n");
2765 put_buffer(c->pb, content, content_length);
2768 static HTTPContext *find_rtp_session(const char *session_id)
2772 if (session_id[0] == '\0')
2775 for(c = first_http_ctx; c != NULL; c = c->next) {
2776 if (!strcmp(c->session_id, session_id))
2782 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2784 RTSPTransportField *th;
2787 for(i=0;i<h->nb_transports;i++) {
2788 th = &h->transports[i];
2789 if (th->protocol == protocol)
2795 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2799 int stream_index, port;
2804 RTSPTransportField *th;
2805 struct sockaddr_in dest_addr;
2806 RTSPActionServerSetup setup;
2808 /* find which url is asked */
2809 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2814 /* now check each stream */
2815 for(stream = first_stream; stream != NULL; stream = stream->next) {
2816 if (!stream->is_feed && stream->fmt == &rtp_muxer) {
2817 /* accept aggregate filenames only if single stream */
2818 if (!strcmp(path, stream->filename)) {
2819 if (stream->nb_streams != 1) {
2820 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2827 for(stream_index = 0; stream_index < stream->nb_streams;
2829 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2830 stream->filename, stream_index);
2831 if (!strcmp(path, buf))
2836 /* no stream found */
2837 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2841 /* generate session id if needed */
2842 if (h->session_id[0] == '\0') {
2843 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2844 av_random(&random_state), av_random(&random_state));
2847 /* find rtp session, and create it if none found */
2848 rtp_c = find_rtp_session(h->session_id);
2850 /* always prefer UDP */
2851 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2853 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2855 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2860 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2863 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2867 /* open input stream */
2868 if (open_input_stream(rtp_c, "") < 0) {
2869 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2874 /* test if stream is OK (test needed because several SETUP needs
2875 to be done for a given file) */
2876 if (rtp_c->stream != stream) {
2877 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2881 /* test if stream is already set up */
2882 if (rtp_c->rtp_ctx[stream_index]) {
2883 rtsp_reply_error(c, RTSP_STATUS_STATE);
2887 /* check transport */
2888 th = find_transport(h, rtp_c->rtp_protocol);
2889 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2890 th->client_port_min <= 0)) {
2891 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2895 /* setup default options */
2896 setup.transport_option[0] = '\0';
2897 dest_addr = rtp_c->from_addr;
2898 dest_addr.sin_port = htons(th->client_port_min);
2900 /* add transport option if needed */
2901 if (ff_rtsp_callback) {
2902 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2903 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2904 (char *)&setup, sizeof(setup),
2905 stream->rtsp_option) < 0) {
2906 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2909 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2913 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2914 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2918 /* now everything is OK, so we can send the connection parameters */
2919 rtsp_reply_header(c, RTSP_STATUS_OK);
2921 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2923 switch(rtp_c->rtp_protocol) {
2924 case RTSP_PROTOCOL_RTP_UDP:
2925 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2926 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2927 "client_port=%d-%d;server_port=%d-%d",
2928 th->client_port_min, th->client_port_min + 1,
2931 case RTSP_PROTOCOL_RTP_TCP:
2932 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2933 stream_index * 2, stream_index * 2 + 1);
2938 if (setup.transport_option[0] != '\0') {
2939 url_fprintf(c->pb, ";%s", setup.transport_option);
2941 url_fprintf(c->pb, "\r\n");
2944 url_fprintf(c->pb, "\r\n");
2948 /* find an rtp connection by using the session ID. Check consistency
2950 static HTTPContext *find_rtp_session_with_url(const char *url,
2951 const char *session_id)
2959 rtp_c = find_rtp_session(session_id);
2963 /* find which url is asked */
2964 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2968 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2969 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2970 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2971 rtp_c->stream->filename, s);
2972 if(!strncmp(path, buf, sizeof(buf))) {
2973 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2980 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2984 rtp_c = find_rtp_session_with_url(url, h->session_id);
2986 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2990 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2991 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2992 rtp_c->state != HTTPSTATE_READY) {
2993 rtsp_reply_error(c, RTSP_STATUS_STATE);
2998 /* XXX: seek in stream */
2999 if (h->range_start != AV_NOPTS_VALUE) {
3000 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3001 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3005 rtp_c->state = HTTPSTATE_SEND_DATA;
3007 /* now everything is OK, so we can send the connection parameters */
3008 rtsp_reply_header(c, RTSP_STATUS_OK);
3010 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3011 url_fprintf(c->pb, "\r\n");
3014 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3018 rtp_c = find_rtp_session_with_url(url, h->session_id);
3020 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3024 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3025 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3026 rtsp_reply_error(c, RTSP_STATUS_STATE);
3030 rtp_c->state = HTTPSTATE_READY;
3031 rtp_c->first_pts = AV_NOPTS_VALUE;
3032 /* now everything is OK, so we can send the connection parameters */
3033 rtsp_reply_header(c, RTSP_STATUS_OK);
3035 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3036 url_fprintf(c->pb, "\r\n");
3039 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3043 rtp_c = find_rtp_session_with_url(url, h->session_id);
3045 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3049 /* abort the session */
3050 close_connection(rtp_c);
3052 if (ff_rtsp_callback) {
3053 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3055 rtp_c->stream->rtsp_option);
3058 /* now everything is OK, so we can send the connection parameters */
3059 rtsp_reply_header(c, RTSP_STATUS_OK);
3061 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3062 url_fprintf(c->pb, "\r\n");
3066 /********************************************************************/
3069 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3070 FFStream *stream, const char *session_id,
3071 enum RTSPProtocol rtp_protocol)
3073 HTTPContext *c = NULL;
3074 const char *proto_str;
3076 /* XXX: should output a warning page when coming
3077 close to the connection limit */
3078 if (nb_connections >= nb_max_connections)
3081 /* add a new connection */
3082 c = av_mallocz(sizeof(HTTPContext));
3087 c->poll_entry = NULL;
3088 c->from_addr = *from_addr;
3089 c->buffer_size = IOBUFFER_INIT_SIZE;
3090 c->buffer = av_malloc(c->buffer_size);
3095 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3096 c->state = HTTPSTATE_READY;
3097 c->is_packetized = 1;
3098 c->rtp_protocol = rtp_protocol;
3100 /* protocol is shown in statistics */
3101 switch(c->rtp_protocol) {
3102 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3103 proto_str = "MCAST";
3105 case RTSP_PROTOCOL_RTP_UDP:
3108 case RTSP_PROTOCOL_RTP_TCP:
3115 pstrcpy(c->protocol, sizeof(c->protocol), "RTP/");
3116 pstrcat(c->protocol, sizeof(c->protocol), proto_str);
3118 current_bandwidth += stream->bandwidth;
3120 c->next = first_http_ctx;
3132 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3133 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3135 static int rtp_new_av_stream(HTTPContext *c,
3136 int stream_index, struct sockaddr_in *dest_addr,
3137 HTTPContext *rtsp_c)
3139 AVFormatContext *ctx;
3145 int max_packet_size;
3147 /* now we can open the relevant output stream */
3148 ctx = av_alloc_format_context();
3151 ctx->oformat = &rtp_muxer;
3153 st = av_mallocz(sizeof(AVStream));
3156 st->codec= avcodec_alloc_context();
3157 ctx->nb_streams = 1;
3158 ctx->streams[0] = st;
3160 if (!c->stream->feed ||
3161 c->stream->feed == c->stream) {
3162 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3165 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3169 /* build destination RTP address */
3170 ipaddr = inet_ntoa(dest_addr->sin_addr);
3172 switch(c->rtp_protocol) {
3173 case RTSP_PROTOCOL_RTP_UDP:
3174 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3177 /* XXX: also pass as parameter to function ? */
3178 if (c->stream->is_multicast) {
3180 ttl = c->stream->multicast_ttl;
3183 snprintf(ctx->filename, sizeof(ctx->filename),
3184 "rtp://%s:%d?multicast=1&ttl=%d",
3185 ipaddr, ntohs(dest_addr->sin_port), ttl);
3187 snprintf(ctx->filename, sizeof(ctx->filename),
3188 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3191 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3193 c->rtp_handles[stream_index] = h;
3194 max_packet_size = url_get_max_packet_size(h);
3196 case RTSP_PROTOCOL_RTP_TCP:
3199 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3205 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3206 ipaddr, ntohs(dest_addr->sin_port),
3208 c->stream->filename, stream_index, c->protocol);
3210 /* normally, no packets should be output here, but the packet size may be checked */
3211 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3212 /* XXX: close stream */
3215 av_set_parameters(ctx, NULL);
3216 if (av_write_header(ctx) < 0) {
3223 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3226 c->rtp_ctx[stream_index] = ctx;
3230 /********************************************************************/
3231 /* ffserver initialization */
3233 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3237 fst = av_mallocz(sizeof(AVStream));
3240 fst->codec= avcodec_alloc_context();
3241 fst->priv_data = av_mallocz(sizeof(FeedData));
3242 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3243 fst->codec->coded_frame = &dummy_frame;
3244 fst->index = stream->nb_streams;
3245 av_set_pts_info(fst, 33, 1, 90000);
3246 stream->streams[stream->nb_streams++] = fst;
3250 /* return the stream number in the feed */
3251 static int add_av_stream(FFStream *feed, AVStream *st)
3254 AVCodecContext *av, *av1;
3258 for(i=0;i<feed->nb_streams;i++) {
3259 st = feed->streams[i];
3261 if (av1->codec_id == av->codec_id &&
3262 av1->codec_type == av->codec_type &&
3263 av1->bit_rate == av->bit_rate) {
3265 switch(av->codec_type) {
3266 case CODEC_TYPE_AUDIO:
3267 if (av1->channels == av->channels &&
3268 av1->sample_rate == av->sample_rate)
3271 case CODEC_TYPE_VIDEO:
3272 if (av1->width == av->width &&
3273 av1->height == av->height &&
3274 av1->time_base.den == av->time_base.den &&
3275 av1->time_base.num == av->time_base.num &&
3276 av1->gop_size == av->gop_size)
3285 fst = add_av_stream1(feed, av);
3288 return feed->nb_streams - 1;
3293 static void remove_stream(FFStream *stream)
3297 while (*ps != NULL) {
3298 if (*ps == stream) {
3306 /* specific mpeg4 handling : we extract the raw parameters */
3307 static void extract_mpeg4_header(AVFormatContext *infile)
3309 int mpeg4_count, i, size;
3315 for(i=0;i<infile->nb_streams;i++) {
3316 st = infile->streams[i];
3317 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3318 st->codec->extradata_size == 0) {
3325 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3326 while (mpeg4_count > 0) {
3327 if (av_read_packet(infile, &pkt) < 0)
3329 st = infile->streams[pkt.stream_index];
3330 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3331 st->codec->extradata_size == 0) {
3332 av_freep(&st->codec->extradata);
3333 /* fill extradata with the header */
3334 /* XXX: we make hard suppositions here ! */
3336 while (p < pkt.data + pkt.size - 4) {
3337 /* stop when vop header is found */
3338 if (p[0] == 0x00 && p[1] == 0x00 &&
3339 p[2] == 0x01 && p[3] == 0xb6) {
3340 size = p - pkt.data;
3341 // av_hex_dump(pkt.data, size);
3342 st->codec->extradata = av_malloc(size);
3343 st->codec->extradata_size = size;
3344 memcpy(st->codec->extradata, pkt.data, size);
3351 av_free_packet(&pkt);
3355 /* compute the needed AVStream for each file */
3356 static void build_file_streams(void)
3358 FFStream *stream, *stream_next;
3359 AVFormatContext *infile;
3362 /* gather all streams */
3363 for(stream = first_stream; stream != NULL; stream = stream_next) {
3364 stream_next = stream->next;
3365 if (stream->stream_type == STREAM_TYPE_LIVE &&
3367 /* the stream comes from a file */
3368 /* try to open the file */
3370 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3371 if (stream->fmt == &rtp_muxer) {
3372 /* specific case : if transport stream output to RTP,
3373 we use a raw transport stream reader */
3374 stream->ap_in->mpeg2ts_raw = 1;
3375 stream->ap_in->mpeg2ts_compute_pcr = 1;
3378 if (av_open_input_file(&infile, stream->feed_filename,
3379 stream->ifmt, 0, stream->ap_in) < 0) {
3380 http_log("%s not found", stream->feed_filename);
3381 /* remove stream (no need to spend more time on it) */
3383 remove_stream(stream);
3385 /* find all the AVStreams inside and reference them in
3387 if (av_find_stream_info(infile) < 0) {
3388 http_log("Could not find codec parameters from '%s'",
3389 stream->feed_filename);
3390 av_close_input_file(infile);
3393 extract_mpeg4_header(infile);
3395 for(i=0;i<infile->nb_streams;i++) {
3396 add_av_stream1(stream, infile->streams[i]->codec);
3398 av_close_input_file(infile);
3404 /* compute the needed AVStream for each feed */
3405 static void build_feed_streams(void)
3407 FFStream *stream, *feed;
3410 /* gather all streams */
3411 for(stream = first_stream; stream != NULL; stream = stream->next) {
3412 feed = stream->feed;
3414 if (!stream->is_feed) {
3415 /* we handle a stream coming from a feed */
3416 for(i=0;i<stream->nb_streams;i++) {
3417 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3423 /* gather all streams */
3424 for(stream = first_stream; stream != NULL; stream = stream->next) {
3425 feed = stream->feed;
3427 if (stream->is_feed) {
3428 for(i=0;i<stream->nb_streams;i++) {
3429 stream->feed_streams[i] = i;
3435 /* create feed files if needed */
3436 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3439 if (url_exist(feed->feed_filename)) {
3440 /* See if it matches */
3444 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3445 /* Now see if it matches */
3446 if (s->nb_streams == feed->nb_streams) {
3448 for(i=0;i<s->nb_streams;i++) {
3450 sf = feed->streams[i];
3453 if (sf->index != ss->index ||
3455 printf("Index & Id do not match for stream %d (%s)\n",
3456 i, feed->feed_filename);
3459 AVCodecContext *ccf, *ccs;
3463 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3465 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3466 printf("Codecs do not match for stream %d\n", i);
3468 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3469 printf("Codec bitrates do not match for stream %d\n", i);
3471 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3472 if (CHECK_CODEC(time_base.den) ||
3473 CHECK_CODEC(time_base.num) ||
3474 CHECK_CODEC(width) ||
3475 CHECK_CODEC(height)) {
3476 printf("Codec width, height and framerate do not match for stream %d\n", i);
3479 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3480 if (CHECK_CODEC(sample_rate) ||
3481 CHECK_CODEC(channels) ||
3482 CHECK_CODEC(frame_size)) {
3483 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3487 printf("Unknown codec type\n");
3496 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3497 feed->feed_filename, s->nb_streams, feed->nb_streams);
3500 av_close_input_file(s);
3502 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3503 feed->feed_filename);
3506 if (feed->readonly) {
3507 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3508 feed->feed_filename);
3511 unlink(feed->feed_filename);
3514 if (!url_exist(feed->feed_filename)) {
3515 AVFormatContext s1, *s = &s1;
3517 if (feed->readonly) {
3518 printf("Unable to create feed file '%s' as it is marked readonly\n",
3519 feed->feed_filename);
3523 /* only write the header of the ffm file */
3524 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3525 fprintf(stderr, "Could not open output feed file '%s'\n",
3526 feed->feed_filename);
3529 s->oformat = feed->fmt;
3530 s->nb_streams = feed->nb_streams;
3531 for(i=0;i<s->nb_streams;i++) {
3533 st = feed->streams[i];
3536 av_set_parameters(s, NULL);
3537 if (av_write_header(s) < 0) {
3538 fprintf(stderr, "Container doesn't supports the required parameters\n");
3541 /* XXX: need better api */
3542 av_freep(&s->priv_data);
3545 /* get feed size and write index */
3546 fd = open(feed->feed_filename, O_RDONLY);
3548 fprintf(stderr, "Could not open output feed file '%s'\n",
3549 feed->feed_filename);
3553 feed->feed_write_index = ffm_read_write_index(fd);
3554 feed->feed_size = lseek(fd, 0, SEEK_END);
3555 /* ensure that we do not wrap before the end of file */
3556 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3557 feed->feed_max_size = feed->feed_size;
3563 /* compute the bandwidth used by each stream */
3564 static void compute_bandwidth(void)
3569 for(stream = first_stream; stream != NULL; stream = stream->next) {
3571 for(i=0;i<stream->nb_streams;i++) {
3572 AVStream *st = stream->streams[i];
3573 switch(st->codec->codec_type) {
3574 case CODEC_TYPE_AUDIO:
3575 case CODEC_TYPE_VIDEO:
3576 bandwidth += st->codec->bit_rate;
3582 stream->bandwidth = (bandwidth + 999) / 1000;
3586 static void get_arg(char *buf, int buf_size, const char **pp)
3593 while (isspace(*p)) p++;
3596 if (*p == '\"' || *p == '\'')
3608 if ((q - buf) < buf_size - 1)
3613 if (quote && *p == quote)
3618 /* add a codec and set the default parameters */
3619 static void add_codec(FFStream *stream, AVCodecContext *av)
3623 /* compute default parameters */
3624 switch(av->codec_type) {
3625 case CODEC_TYPE_AUDIO:
3626 if (av->bit_rate == 0)
3627 av->bit_rate = 64000;
3628 if (av->sample_rate == 0)
3629 av->sample_rate = 22050;
3630 if (av->channels == 0)
3633 case CODEC_TYPE_VIDEO:
3634 if (av->bit_rate == 0)
3635 av->bit_rate = 64000;
3636 if (av->time_base.num == 0){
3637 av->time_base.den = 5;
3638 av->time_base.num = 1;
3640 if (av->width == 0 || av->height == 0) {
3644 /* Bitrate tolerance is less for streaming */
3645 if (av->bit_rate_tolerance == 0)
3646 av->bit_rate_tolerance = av->bit_rate / 4;
3651 if (av->max_qdiff == 0)
3653 av->qcompress = 0.5;
3656 if (!av->nsse_weight)
3657 av->nsse_weight = 8;
3659 av->frame_skip_cmp = FF_CMP_DCTMAX;
3660 av->me_method = ME_EPZS;
3661 av->rc_buffer_aggressivity = 1.0;
3664 av->rc_eq = "tex^qComp";
3665 if (!av->i_quant_factor)
3666 av->i_quant_factor = -0.8;
3667 if (!av->b_quant_factor)
3668 av->b_quant_factor = 1.25;
3669 if (!av->b_quant_offset)
3670 av->b_quant_offset = 1.25;
3671 if (!av->rc_max_rate)
3672 av->rc_max_rate = av->bit_rate * 2;
3674 if (av->rc_max_rate && !av->rc_buffer_size) {
3675 av->rc_buffer_size = av->rc_max_rate;
3684 st = av_mallocz(sizeof(AVStream));
3687 st->codec = avcodec_alloc_context();
3688 stream->streams[stream->nb_streams++] = st;
3689 memcpy(st->codec, av, sizeof(AVCodecContext));
3692 static int opt_audio_codec(const char *arg)
3698 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3703 return CODEC_ID_NONE;
3709 static int opt_video_codec(const char *arg)
3715 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3720 return CODEC_ID_NONE;
3726 /* simplistic plugin support */
3729 static void load_module(const char *filename)
3732 void (*init_func)(void);
3733 dll = dlopen(filename, RTLD_NOW);
3735 fprintf(stderr, "Could not load module '%s' - %s\n",
3736 filename, dlerror());
3740 init_func = dlsym(dll, "ffserver_module_init");
3743 "%s: init function 'ffserver_module_init()' not found\n",
3752 static int parse_ffconfig(const char *filename)
3759 int val, errors, line_num;
3760 FFStream **last_stream, *stream, *redirect;
3761 FFStream **last_feed, *feed;
3762 AVCodecContext audio_enc, video_enc;
3763 int audio_id, video_id;
3765 f = fopen(filename, "r");
3773 first_stream = NULL;
3774 last_stream = &first_stream;
3776 last_feed = &first_feed;
3780 audio_id = CODEC_ID_NONE;
3781 video_id = CODEC_ID_NONE;
3783 if (fgets(line, sizeof(line), f) == NULL)
3789 if (*p == '\0' || *p == '#')
3792 get_arg(cmd, sizeof(cmd), &p);
3794 if (!strcasecmp(cmd, "Port")) {
3795 get_arg(arg, sizeof(arg), &p);
3796 my_http_addr.sin_port = htons (atoi(arg));
3797 } else if (!strcasecmp(cmd, "BindAddress")) {
3798 get_arg(arg, sizeof(arg), &p);
3799 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3800 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3801 filename, line_num, arg);
3804 } else if (!strcasecmp(cmd, "NoDaemon")) {
3805 ffserver_daemon = 0;
3806 } else if (!strcasecmp(cmd, "RTSPPort")) {
3807 get_arg(arg, sizeof(arg), &p);
3808 my_rtsp_addr.sin_port = htons (atoi(arg));
3809 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3810 get_arg(arg, sizeof(arg), &p);
3811 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3812 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3813 filename, line_num, arg);
3816 } else if (!strcasecmp(cmd, "MaxClients")) {
3817 get_arg(arg, sizeof(arg), &p);
3819 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3820 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3821 filename, line_num, arg);
3824 nb_max_connections = val;
3826 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3827 get_arg(arg, sizeof(arg), &p);
3829 if (val < 10 || val > 100000) {
3830 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3831 filename, line_num, arg);
3834 max_bandwidth = val;
3836 } else if (!strcasecmp(cmd, "CustomLog")) {
3837 get_arg(logfilename, sizeof(logfilename), &p);
3838 } else if (!strcasecmp(cmd, "<Feed")) {
3839 /*********************************************/
3840 /* Feed related options */
3842 if (stream || feed) {
3843 fprintf(stderr, "%s:%d: Already in a tag\n",
3844 filename, line_num);
3846 feed = av_mallocz(sizeof(FFStream));
3847 /* add in stream list */
3848 *last_stream = feed;
3849 last_stream = &feed->next;
3850 /* add in feed list */
3852 last_feed = &feed->next_feed;
3854 get_arg(feed->filename, sizeof(feed->filename), &p);
3855 q = strrchr(feed->filename, '>');
3858 feed->fmt = guess_format("ffm", NULL, NULL);
3859 /* defaut feed file */
3860 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3861 "/tmp/%s.ffm", feed->filename);
3862 feed->feed_max_size = 5 * 1024 * 1024;
3864 feed->feed = feed; /* self feeding :-) */
3866 } else if (!strcasecmp(cmd, "Launch")) {
3870 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3872 for (i = 0; i < 62; i++) {
3875 get_arg(argbuf, sizeof(argbuf), &p);
3879 feed->child_argv[i] = av_malloc(strlen(argbuf) + 1);
3880 strcpy(feed->child_argv[i], argbuf);
3883 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3885 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3887 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3888 inet_ntoa(my_http_addr.sin_addr),
3889 ntohs(my_http_addr.sin_port), feed->filename);
3894 fprintf(stdout, "Launch commandline: ");
3895 for (j = 0; j <= i; j++)
3896 fprintf(stdout, "%s ", feed->child_argv[j]);
3897 fprintf(stdout, "\n");
3900 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3902 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3904 } else if (stream) {
3905 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3907 } else if (!strcasecmp(cmd, "File")) {
3909 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3910 } else if (stream) {
3911 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3913 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3918 get_arg(arg, sizeof(arg), &p);
3920 fsize = strtod(p1, (char **)&p1);
3921 switch(toupper(*p1)) {
3926 fsize *= 1024 * 1024;
3929 fsize *= 1024 * 1024 * 1024;
3932 feed->feed_max_size = (int64_t)fsize;
3934 } else if (!strcasecmp(cmd, "</Feed>")) {
3936 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3937 filename, line_num);
3941 /* Make sure that we start out clean */
3942 if (unlink(feed->feed_filename) < 0
3943 && errno != ENOENT) {
3944 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3945 filename, line_num, feed->feed_filename, strerror(errno));
3951 } else if (!strcasecmp(cmd, "<Stream")) {
3952 /*********************************************/
3953 /* Stream related options */
3955 if (stream || feed) {
3956 fprintf(stderr, "%s:%d: Already in a tag\n",
3957 filename, line_num);
3959 stream = av_mallocz(sizeof(FFStream));
3960 *last_stream = stream;
3961 last_stream = &stream->next;
3963 get_arg(stream->filename, sizeof(stream->filename), &p);
3964 q = strrchr(stream->filename, '>');
3967 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3968 memset(&audio_enc, 0, sizeof(AVCodecContext));
3969 memset(&video_enc, 0, sizeof(AVCodecContext));
3970 audio_id = CODEC_ID_NONE;
3971 video_id = CODEC_ID_NONE;
3973 audio_id = stream->fmt->audio_codec;
3974 video_id = stream->fmt->video_codec;
3977 } else if (!strcasecmp(cmd, "Feed")) {
3978 get_arg(arg, sizeof(arg), &p);
3983 while (sfeed != NULL) {
3984 if (!strcmp(sfeed->filename, arg))
3986 sfeed = sfeed->next_feed;
3989 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3990 filename, line_num, arg);
3992 stream->feed = sfeed;
3995 } else if (!strcasecmp(cmd, "Format")) {
3996 get_arg(arg, sizeof(arg), &p);
3997 if (!strcmp(arg, "status")) {
3998 stream->stream_type = STREAM_TYPE_STATUS;
4001 stream->stream_type = STREAM_TYPE_LIVE;
4002 /* jpeg cannot be used here, so use single frame jpeg */
4003 if (!strcmp(arg, "jpeg"))
4004 strcpy(arg, "mjpeg");
4005 stream->fmt = guess_stream_format(arg, NULL, NULL);
4007 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4008 filename, line_num, arg);
4013 audio_id = stream->fmt->audio_codec;
4014 video_id = stream->fmt->video_codec;
4016 } else if (!strcasecmp(cmd, "InputFormat")) {
4017 stream->ifmt = av_find_input_format(arg);
4018 if (!stream->ifmt) {
4019 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4020 filename, line_num, arg);
4022 } else if (!strcasecmp(cmd, "FaviconURL")) {
4023 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4024 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4026 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4027 filename, line_num);
4030 } else if (!strcasecmp(cmd, "Author")) {
4032 get_arg(stream->author, sizeof(stream->author), &p);
4034 } else if (!strcasecmp(cmd, "Comment")) {
4036 get_arg(stream->comment, sizeof(stream->comment), &p);
4038 } else if (!strcasecmp(cmd, "Copyright")) {
4040 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4042 } else if (!strcasecmp(cmd, "Title")) {
4044 get_arg(stream->title, sizeof(stream->title), &p);
4046 } else if (!strcasecmp(cmd, "Preroll")) {
4047 get_arg(arg, sizeof(arg), &p);
4049 stream->prebuffer = atof(arg) * 1000;
4051 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4053 stream->send_on_key = 1;
4055 } else if (!strcasecmp(cmd, "AudioCodec")) {
4056 get_arg(arg, sizeof(arg), &p);
4057 audio_id = opt_audio_codec(arg);
4058 if (audio_id == CODEC_ID_NONE) {
4059 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4060 filename, line_num, arg);
4063 } else if (!strcasecmp(cmd, "VideoCodec")) {
4064 get_arg(arg, sizeof(arg), &p);
4065 video_id = opt_video_codec(arg);
4066 if (video_id == CODEC_ID_NONE) {
4067 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4068 filename, line_num, arg);
4071 } else if (!strcasecmp(cmd, "MaxTime")) {
4072 get_arg(arg, sizeof(arg), &p);
4074 stream->max_time = atof(arg) * 1000;
4076 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4077 get_arg(arg, sizeof(arg), &p);
4079 audio_enc.bit_rate = atoi(arg) * 1000;
4081 } else if (!strcasecmp(cmd, "AudioChannels")) {
4082 get_arg(arg, sizeof(arg), &p);
4084 audio_enc.channels = atoi(arg);
4086 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4087 get_arg(arg, sizeof(arg), &p);
4089 audio_enc.sample_rate = atoi(arg);
4091 } else if (!strcasecmp(cmd, "AudioQuality")) {
4092 get_arg(arg, sizeof(arg), &p);
4094 // audio_enc.quality = atof(arg) * 1000;
4096 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4098 int minrate, maxrate;
4100 get_arg(arg, sizeof(arg), &p);
4102 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4103 video_enc.rc_min_rate = minrate * 1000;
4104 video_enc.rc_max_rate = maxrate * 1000;
4106 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4107 filename, line_num, arg);
4111 } else if (!strcasecmp(cmd, "Debug")) {
4113 get_arg(arg, sizeof(arg), &p);
4114 video_enc.debug = strtol(arg,0,0);
4116 } else if (!strcasecmp(cmd, "Strict")) {
4118 get_arg(arg, sizeof(arg), &p);
4119 video_enc.strict_std_compliance = atoi(arg);
4121 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4123 get_arg(arg, sizeof(arg), &p);
4124 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4126 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4128 get_arg(arg, sizeof(arg), &p);
4129 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4131 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4132 get_arg(arg, sizeof(arg), &p);
4134 video_enc.bit_rate = atoi(arg) * 1000;
4136 } else if (!strcasecmp(cmd, "VideoSize")) {
4137 get_arg(arg, sizeof(arg), &p);
4139 parse_image_size(&video_enc.width, &video_enc.height, arg);
4140 if ((video_enc.width % 16) != 0 ||
4141 (video_enc.height % 16) != 0) {
4142 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4143 filename, line_num);
4147 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4148 get_arg(arg, sizeof(arg), &p);
4150 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4151 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4153 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4154 get_arg(arg, sizeof(arg), &p);
4156 video_enc.gop_size = atoi(arg);
4158 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4160 video_enc.gop_size = 1;
4162 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4164 video_enc.mb_decision = FF_MB_DECISION_BITS;
4166 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4168 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4169 video_enc.flags |= CODEC_FLAG_4MV;
4171 } else if (!strcasecmp(cmd, "VideoTag")) {
4172 get_arg(arg, sizeof(arg), &p);
4173 if ((strlen(arg) == 4) && stream) {
4174 video_enc.codec_tag = ff_get_fourcc(arg);
4176 } else if (!strcasecmp(cmd, "BitExact")) {
4178 video_enc.flags |= CODEC_FLAG_BITEXACT;
4180 } else if (!strcasecmp(cmd, "DctFastint")) {
4182 video_enc.dct_algo = FF_DCT_FASTINT;
4184 } else if (!strcasecmp(cmd, "IdctSimple")) {
4186 video_enc.idct_algo = FF_IDCT_SIMPLE;
4188 } else if (!strcasecmp(cmd, "Qscale")) {
4189 get_arg(arg, sizeof(arg), &p);
4191 video_enc.flags |= CODEC_FLAG_QSCALE;
4192 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4194 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4195 get_arg(arg, sizeof(arg), &p);
4197 video_enc.max_qdiff = atoi(arg);
4198 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4199 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4200 filename, line_num);
4204 } else if (!strcasecmp(cmd, "VideoQMax")) {
4205 get_arg(arg, sizeof(arg), &p);
4207 video_enc.qmax = atoi(arg);
4208 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4209 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4210 filename, line_num);
4214 } else if (!strcasecmp(cmd, "VideoQMin")) {
4215 get_arg(arg, sizeof(arg), &p);
4217 video_enc.qmin = atoi(arg);
4218 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4219 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4220 filename, line_num);
4224 } else if (!strcasecmp(cmd, "LumaElim")) {
4225 get_arg(arg, sizeof(arg), &p);
4227 video_enc.luma_elim_threshold = atoi(arg);
4229 } else if (!strcasecmp(cmd, "ChromaElim")) {
4230 get_arg(arg, sizeof(arg), &p);
4232 video_enc.chroma_elim_threshold = atoi(arg);
4234 } else if (!strcasecmp(cmd, "LumiMask")) {
4235 get_arg(arg, sizeof(arg), &p);
4237 video_enc.lumi_masking = atof(arg);
4239 } else if (!strcasecmp(cmd, "DarkMask")) {
4240 get_arg(arg, sizeof(arg), &p);
4242 video_enc.dark_masking = atof(arg);
4244 } else if (!strcasecmp(cmd, "NoVideo")) {
4245 video_id = CODEC_ID_NONE;
4246 } else if (!strcasecmp(cmd, "NoAudio")) {
4247 audio_id = CODEC_ID_NONE;
4248 } else if (!strcasecmp(cmd, "ACL")) {
4252 get_arg(arg, sizeof(arg), &p);
4253 if (strcasecmp(arg, "allow") == 0) {
4254 acl.action = IP_ALLOW;
4255 } else if (strcasecmp(arg, "deny") == 0) {
4256 acl.action = IP_DENY;
4258 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4259 filename, line_num, arg);
4263 get_arg(arg, sizeof(arg), &p);
4265 he = gethostbyname(arg);
4267 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4268 filename, line_num, arg);
4271 /* Only take the first */
4272 acl.first.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4273 acl.last = acl.first;
4276 get_arg(arg, sizeof(arg), &p);
4279 he = gethostbyname(arg);
4281 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4282 filename, line_num, arg);
4285 /* Only take the first */
4286 acl.last.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4291 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4292 IPAddressACL **naclp = 0;
4298 naclp = &stream->acl;
4302 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4303 filename, line_num);
4309 naclp = &(*naclp)->next;
4314 } else if (!strcasecmp(cmd, "RTSPOption")) {
4315 get_arg(arg, sizeof(arg), &p);
4317 av_freep(&stream->rtsp_option);
4318 /* XXX: av_strdup ? */
4319 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4320 if (stream->rtsp_option) {
4321 strcpy(stream->rtsp_option, arg);
4324 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4325 get_arg(arg, sizeof(arg), &p);
4327 if (!inet_aton(arg, &stream->multicast_ip)) {
4328 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4329 filename, line_num, arg);
4332 stream->is_multicast = 1;
4333 stream->loop = 1; /* default is looping */
4335 } else if (!strcasecmp(cmd, "MulticastPort")) {
4336 get_arg(arg, sizeof(arg), &p);
4338 stream->multicast_port = atoi(arg);
4340 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4341 get_arg(arg, sizeof(arg), &p);
4343 stream->multicast_ttl = atoi(arg);
4345 } else if (!strcasecmp(cmd, "NoLoop")) {
4349 } else if (!strcasecmp(cmd, "</Stream>")) {
4351 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4352 filename, line_num);
4355 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4356 if (audio_id != CODEC_ID_NONE) {
4357 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4358 audio_enc.codec_id = audio_id;
4359 add_codec(stream, &audio_enc);
4361 if (video_id != CODEC_ID_NONE) {
4362 video_enc.codec_type = CODEC_TYPE_VIDEO;
4363 video_enc.codec_id = video_id;
4364 add_codec(stream, &video_enc);
4368 } else if (!strcasecmp(cmd, "<Redirect")) {
4369 /*********************************************/
4371 if (stream || feed || redirect) {
4372 fprintf(stderr, "%s:%d: Already in a tag\n",
4373 filename, line_num);
4376 redirect = av_mallocz(sizeof(FFStream));
4377 *last_stream = redirect;
4378 last_stream = &redirect->next;
4380 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4381 q = strrchr(redirect->filename, '>');
4384 redirect->stream_type = STREAM_TYPE_REDIRECT;
4386 } else if (!strcasecmp(cmd, "URL")) {
4388 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4390 } else if (!strcasecmp(cmd, "</Redirect>")) {
4392 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4393 filename, line_num);
4396 if (!redirect->feed_filename[0]) {
4397 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4398 filename, line_num);
4402 } else if (!strcasecmp(cmd, "LoadModule")) {
4403 get_arg(arg, sizeof(arg), &p);
4407 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4408 filename, line_num, arg);
4412 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4413 filename, line_num, cmd);
4427 static void write_packet(FFCodec *ffenc,
4428 uint8_t *buf, int size)
4431 AVCodecContext *enc = &ffenc->enc;
4433 mk_header(&hdr, enc, size);
4434 wptr = http_fifo.wptr;
4435 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4436 fifo_write(&http_fifo, buf, size, &wptr);
4437 /* atomic modification of wptr */
4438 http_fifo.wptr = wptr;
4439 ffenc->data_count += size;
4440 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4444 static void show_banner(void)
4446 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4449 static void show_help(void)
4452 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4453 "Hyper fast multi format Audio/Video streaming server\n"
4455 "-L : print the LICENSE\n"
4457 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4461 static void show_license(void)
4465 "FFmpeg is free software; you can redistribute it and/or\n"
4466 "modify it under the terms of the GNU Lesser General Public\n"
4467 "License as published by the Free Software Foundation; either\n"
4468 "version 2.1 of the License, or (at your option) any later version.\n"
4470 "FFmpeg is distributed in the hope that it will be useful,\n"
4471 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4472 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4473 "Lesser General Public License for more details.\n"
4475 "You should have received a copy of the GNU Lesser General Public\n"
4476 "License along with FFmpeg; if not, write to the Free Software\n"
4477 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
4481 static void handle_child_exit(int sig)
4486 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4489 for (feed = first_feed; feed; feed = feed->next) {
4490 if (feed->pid == pid) {
4491 int uptime = time(0) - feed->pid_start;
4494 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4497 /* Turn off any more restarts */
4498 feed->child_argv = 0;
4504 need_to_start_children = 1;
4507 int main(int argc, char **argv)
4509 const char *config_filename;
4511 struct sigaction sigact;
4515 config_filename = "/etc/ffserver.conf";
4517 my_program_name = argv[0];
4518 my_program_dir = getcwd(0, 0);
4519 ffserver_daemon = 1;
4522 c = getopt(argc, argv, "ndLh?f:");
4538 ffserver_daemon = 0;
4541 config_filename = optarg;
4548 putenv("http_proxy"); /* Kill the http_proxy */
4550 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4552 /* address on which the server will handle HTTP connections */
4553 my_http_addr.sin_family = AF_INET;
4554 my_http_addr.sin_port = htons (8080);
4555 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4557 /* address on which the server will handle RTSP connections */
4558 my_rtsp_addr.sin_family = AF_INET;
4559 my_rtsp_addr.sin_port = htons (5454);
4560 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4562 nb_max_connections = 5;
4563 max_bandwidth = 1000;
4564 first_stream = NULL;
4565 logfilename[0] = '\0';
4567 memset(&sigact, 0, sizeof(sigact));
4568 sigact.sa_handler = handle_child_exit;
4569 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4570 sigaction(SIGCHLD, &sigact, 0);
4572 if (parse_ffconfig(config_filename) < 0) {
4573 fprintf(stderr, "Incorrect config file - exiting.\n");
4577 build_file_streams();
4579 build_feed_streams();
4581 compute_bandwidth();
4583 /* put the process in background and detach it from its TTY */
4584 if (ffserver_daemon) {
4591 } else if (pid > 0) {
4599 open("/dev/null", O_RDWR);
4600 if (strcmp(logfilename, "-") != 0) {
4610 signal(SIGPIPE, SIG_IGN);
4612 /* open log file if needed */
4613 if (logfilename[0] != '\0') {
4614 if (!strcmp(logfilename, "-"))
4617 logfile = fopen(logfilename, "w");
4620 if (http_server() < 0) {
4621 fprintf(stderr, "Could not start server\n");