2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #define HAVE_AV_CONFIG_H
25 #include <sys/ioctl.h>
29 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
38 #ifdef CONFIG_HAVE_DLFCN
44 /* maximum number of simultaneous HTTP connections */
45 #define HTTP_MAX_CONNECTIONS 2000
48 HTTPSTATE_WAIT_REQUEST,
49 HTTPSTATE_SEND_HEADER,
50 HTTPSTATE_SEND_DATA_HEADER,
51 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
52 HTTPSTATE_SEND_DATA_TRAILER,
53 HTTPSTATE_RECEIVE_DATA,
54 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
57 RTSPSTATE_WAIT_REQUEST,
59 RTSPSTATE_SEND_PACKET,
62 const char *http_state[] = {
78 #define IOBUFFER_INIT_SIZE 8192
80 /* coef for exponential mean for bitrate estimation in statistics */
83 /* timeouts are in ms */
84 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
85 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
87 #define SYNC_TIMEOUT (10 * 1000)
90 int64_t count1, count2;
94 /* context associated with one connection */
95 typedef struct HTTPContext {
97 int fd; /* socket file descriptor */
98 struct sockaddr_in from_addr; /* origin */
99 struct pollfd *poll_entry; /* used when polling */
101 uint8_t *buffer_ptr, *buffer_end;
104 struct HTTPContext *next;
105 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
109 /* input format handling */
110 AVFormatContext *fmt_in;
111 long start_time; /* In milliseconds - this wraps fairly often */
112 int64_t first_pts; /* initial pts value */
113 int64_t cur_pts; /* current pts value from the stream in us */
114 int64_t cur_frame_duration; /* duration of the current frame in us */
115 int cur_frame_bytes; /* output frame size, needed to compute
116 the time at which we send each
118 int pts_stream_index; /* stream we choose as clock reference */
119 int64_t cur_clock; /* current clock reference value in us */
120 /* output format handling */
121 struct FFStream *stream;
122 /* -1 is invalid stream */
123 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
124 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
126 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
127 int last_packet_sent; /* true if last data packet was sent */
129 DataRateData datarate;
136 int is_packetized; /* if true, the stream is packetized */
137 int packet_stream_index; /* current stream for output in state machine */
139 /* RTSP state specific */
140 uint8_t *pb_buffer; /* XXX: use that in all the code */
142 int seq; /* RTSP sequence number */
144 /* RTP state specific */
145 enum RTSPProtocol rtp_protocol;
146 char session_id[32]; /* session id */
147 AVFormatContext *rtp_ctx[MAX_STREAMS];
149 /* RTP/UDP specific */
150 URLContext *rtp_handles[MAX_STREAMS];
152 /* RTP/TCP specific */
153 struct HTTPContext *rtsp_c;
154 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
157 static AVFrame dummy_frame;
159 /* each generated stream is described here */
163 STREAM_TYPE_REDIRECT,
166 enum IPAddressAction {
171 typedef struct IPAddressACL {
172 struct IPAddressACL *next;
173 enum IPAddressAction action;
174 /* These are in host order */
175 struct in_addr first;
179 /* description of each stream of the ffserver.conf file */
180 typedef struct FFStream {
181 enum StreamType stream_type;
182 char filename[1024]; /* stream filename */
183 struct FFStream *feed; /* feed we are using (can be null if
185 AVFormatParameters *ap_in; /* input parameters */
186 AVInputFormat *ifmt; /* if non NULL, force input format */
190 int prebuffer; /* Number of millseconds early to start */
191 long max_time; /* Number of milliseconds to run */
193 AVStream *streams[MAX_STREAMS];
194 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
195 char feed_filename[1024]; /* file name of the feed storage, or
196 input file name for a stream */
201 pid_t pid; /* Of ffmpeg process */
202 time_t pid_start; /* Of ffmpeg process */
204 struct FFStream *next;
205 int bandwidth; /* bandwidth, in kbits/s */
208 /* multicast specific */
210 struct in_addr multicast_ip;
211 int multicast_port; /* first port used for multicast */
213 int loop; /* if true, send the stream in loops (only meaningful if file) */
216 int feed_opened; /* true if someone is writing to the feed */
217 int is_feed; /* true if it is a feed */
218 int readonly; /* True if writing is prohibited to the file */
220 int64_t bytes_served;
221 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
222 int64_t feed_write_index; /* current write position in feed (it wraps round) */
223 int64_t feed_size; /* current size of feed */
224 struct FFStream *next_feed;
227 typedef struct FeedData {
228 long long data_count;
229 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
232 struct sockaddr_in my_http_addr;
233 struct sockaddr_in my_rtsp_addr;
235 static char logfilename[1024];
236 static HTTPContext *first_http_ctx;
237 static FFStream *first_feed; /* contains only feeds */
238 static FFStream *first_stream; /* contains all streams, including feeds */
240 static void new_connection(int server_fd, int is_rtsp);
241 static void close_connection(HTTPContext *c);
244 static int handle_connection(HTTPContext *c);
245 static int http_parse_request(HTTPContext *c);
246 static int http_send_data(HTTPContext *c);
247 static void compute_stats(HTTPContext *c);
248 static int open_input_stream(HTTPContext *c, const char *info);
249 static int http_start_receive_data(HTTPContext *c);
250 static int http_receive_data(HTTPContext *c);
253 static int rtsp_parse_request(HTTPContext *c);
254 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
255 static void rtsp_cmd_options(HTTPContext *c, const char *url);
256 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
257 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
258 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
259 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
262 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
263 struct in_addr my_ip);
266 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
267 FFStream *stream, const char *session_id,
268 enum RTSPProtocol rtp_protocol);
269 static int rtp_new_av_stream(HTTPContext *c,
270 int stream_index, struct sockaddr_in *dest_addr,
271 HTTPContext *rtsp_c);
273 static const char *my_program_name;
274 static const char *my_program_dir;
276 static int ffserver_debug;
277 static int ffserver_daemon;
278 static int no_launch;
279 static int need_to_start_children;
281 static int nb_max_connections;
282 static int nb_connections;
284 static int max_bandwidth;
285 static int current_bandwidth;
287 static long cur_time; // Making this global saves on passing it around everywhere
289 static long gettime_ms(void)
293 gettimeofday(&tv,NULL);
294 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
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));
443 if (listen (server_fd, 5) < 0) {
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),
467 "%08x%08x", (int)random(), (int)random());
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 = gettime_ms();
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;
765 av_freep(&c->pb_buffer);
766 av_freep(&c->packet_buffer);
772 static int handle_connection(HTTPContext *c)
777 case HTTPSTATE_WAIT_REQUEST:
778 case RTSPSTATE_WAIT_REQUEST:
780 if ((c->timeout - cur_time) < 0)
782 if (c->poll_entry->revents & (POLLERR | POLLHUP))
785 /* no need to read if no events */
786 if (!(c->poll_entry->revents & POLLIN))
790 len = read(c->fd, c->buffer_ptr, 1);
792 if (errno != EAGAIN && errno != EINTR)
794 } else if (len == 0) {
797 /* search for end of request. */
799 c->buffer_ptr += len;
801 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
802 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
803 /* request found : parse it and reply */
804 if (c->state == HTTPSTATE_WAIT_REQUEST) {
805 ret = http_parse_request(c);
807 ret = rtsp_parse_request(c);
811 } else if (ptr >= c->buffer_end) {
812 /* request too long: cannot do anything */
814 } else goto read_loop;
818 case HTTPSTATE_SEND_HEADER:
819 if (c->poll_entry->revents & (POLLERR | POLLHUP))
822 /* no need to write if no events */
823 if (!(c->poll_entry->revents & POLLOUT))
825 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
827 if (errno != EAGAIN && errno != EINTR) {
828 /* error : close connection */
829 av_freep(&c->pb_buffer);
833 c->buffer_ptr += len;
835 c->stream->bytes_served += len;
836 c->data_count += len;
837 if (c->buffer_ptr >= c->buffer_end) {
838 av_freep(&c->pb_buffer);
843 /* all the buffer was sent : synchronize to the incoming stream */
844 c->state = HTTPSTATE_SEND_DATA_HEADER;
845 c->buffer_ptr = c->buffer_end = c->buffer;
850 case HTTPSTATE_SEND_DATA:
851 case HTTPSTATE_SEND_DATA_HEADER:
852 case HTTPSTATE_SEND_DATA_TRAILER:
853 /* for packetized output, we consider we can always write (the
854 input streams sets the speed). It may be better to verify
855 that we do not rely too much on the kernel queues */
856 if (!c->is_packetized) {
857 if (c->poll_entry->revents & (POLLERR | POLLHUP))
860 /* no need to read if no events */
861 if (!(c->poll_entry->revents & POLLOUT))
864 if (http_send_data(c) < 0)
867 case HTTPSTATE_RECEIVE_DATA:
868 /* no need to read if no events */
869 if (c->poll_entry->revents & (POLLERR | POLLHUP))
871 if (!(c->poll_entry->revents & POLLIN))
873 if (http_receive_data(c) < 0)
876 case HTTPSTATE_WAIT_FEED:
877 /* no need to read if no events */
878 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
881 /* nothing to do, we'll be waken up by incoming feed packets */
884 case RTSPSTATE_SEND_REPLY:
885 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
886 av_freep(&c->pb_buffer);
889 /* no need to write if no events */
890 if (!(c->poll_entry->revents & POLLOUT))
892 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
894 if (errno != EAGAIN && errno != EINTR) {
895 /* error : close connection */
896 av_freep(&c->pb_buffer);
900 c->buffer_ptr += len;
901 c->data_count += len;
902 if (c->buffer_ptr >= c->buffer_end) {
903 /* all the buffer was sent : wait for a new request */
904 av_freep(&c->pb_buffer);
905 start_wait_request(c, 1);
909 case RTSPSTATE_SEND_PACKET:
910 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
911 av_freep(&c->packet_buffer);
914 /* no need to write if no events */
915 if (!(c->poll_entry->revents & POLLOUT))
917 len = write(c->fd, c->packet_buffer_ptr,
918 c->packet_buffer_end - c->packet_buffer_ptr);
920 if (errno != EAGAIN && errno != EINTR) {
921 /* error : close connection */
922 av_freep(&c->packet_buffer);
926 c->packet_buffer_ptr += len;
927 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
928 /* all the buffer was sent : wait for a new request */
929 av_freep(&c->packet_buffer);
930 c->state = RTSPSTATE_WAIT_REQUEST;
934 case HTTPSTATE_READY:
943 static int extract_rates(char *rates, int ratelen, const char *request)
947 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
948 if (strncasecmp(p, "Pragma:", 7) == 0) {
949 const char *q = p + 7;
951 while (*q && *q != '\n' && isspace(*q))
954 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
960 memset(rates, 0xff, ratelen);
963 while (*q && *q != '\n' && *q != ':')
966 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
970 if (stream_no < ratelen && stream_no >= 0) {
971 rates[stream_no] = rate_no;
974 while (*q && *q != '\n' && !isspace(*q))
991 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
994 int best_bitrate = 100000000;
997 for (i = 0; i < feed->nb_streams; i++) {
998 AVCodecContext *feed_codec = feed->streams[i]->codec;
1000 if (feed_codec->codec_id != codec->codec_id ||
1001 feed_codec->sample_rate != codec->sample_rate ||
1002 feed_codec->width != codec->width ||
1003 feed_codec->height != codec->height) {
1007 /* Potential stream */
1009 /* We want the fastest stream less than bit_rate, or the slowest
1010 * faster than bit_rate
1013 if (feed_codec->bit_rate <= bit_rate) {
1014 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1015 best_bitrate = feed_codec->bit_rate;
1019 if (feed_codec->bit_rate < best_bitrate) {
1020 best_bitrate = feed_codec->bit_rate;
1029 static int modify_current_stream(HTTPContext *c, char *rates)
1032 FFStream *req = c->stream;
1033 int action_required = 0;
1035 /* Not much we can do for a feed */
1039 for (i = 0; i < req->nb_streams; i++) {
1040 AVCodecContext *codec = req->streams[i]->codec;
1044 c->switch_feed_streams[i] = req->feed_streams[i];
1047 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1050 /* Wants off or slow */
1051 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1053 /* This doesn't work well when it turns off the only stream! */
1054 c->switch_feed_streams[i] = -2;
1055 c->feed_streams[i] = -2;
1060 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1061 action_required = 1;
1064 return action_required;
1068 static void do_switch_stream(HTTPContext *c, int i)
1070 if (c->switch_feed_streams[i] >= 0) {
1072 c->feed_streams[i] = c->switch_feed_streams[i];
1075 /* Now update the stream */
1077 c->switch_feed_streams[i] = -1;
1080 /* XXX: factorize in utils.c ? */
1081 /* XXX: take care with different space meaning */
1082 static void skip_spaces(const char **pp)
1086 while (*p == ' ' || *p == '\t')
1091 static void get_word(char *buf, int buf_size, const char **pp)
1099 while (!isspace(*p) && *p != '\0') {
1100 if ((q - buf) < buf_size - 1)
1109 static int validate_acl(FFStream *stream, HTTPContext *c)
1111 enum IPAddressAction last_action = IP_DENY;
1113 struct in_addr *src = &c->from_addr.sin_addr;
1114 unsigned long src_addr = ntohl(src->s_addr);
1116 for (acl = stream->acl; acl; acl = acl->next) {
1117 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1118 return (acl->action == IP_ALLOW) ? 1 : 0;
1120 last_action = acl->action;
1123 /* Nothing matched, so return not the last action */
1124 return (last_action == IP_DENY) ? 1 : 0;
1127 /* compute the real filename of a file by matching it without its
1128 extensions to all the stream filenames */
1129 static void compute_real_filename(char *filename, int max_size)
1136 /* compute filename by matching without the file extensions */
1137 pstrcpy(file1, sizeof(file1), filename);
1138 p = strrchr(file1, '.');
1141 for(stream = first_stream; stream != NULL; stream = stream->next) {
1142 pstrcpy(file2, sizeof(file2), stream->filename);
1143 p = strrchr(file2, '.');
1146 if (!strcmp(file1, file2)) {
1147 pstrcpy(filename, max_size, stream->filename);
1162 /* parse http request and prepare header */
1163 static int http_parse_request(HTTPContext *c)
1166 enum RedirType redir_type;
1168 char info[1024], *filename;
1172 const char *mime_type;
1176 char *useragent = 0;
1179 get_word(cmd, sizeof(cmd), (const char **)&p);
1180 pstrcpy(c->method, sizeof(c->method), cmd);
1182 if (!strcmp(cmd, "GET"))
1184 else if (!strcmp(cmd, "POST"))
1189 get_word(url, sizeof(url), (const char **)&p);
1190 pstrcpy(c->url, sizeof(c->url), url);
1192 get_word(protocol, sizeof(protocol), (const char **)&p);
1193 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1196 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1199 http_log("New connection: %s %s\n", cmd, url);
1201 /* find the filename and the optional info string in the request */
1208 pstrcpy(info, sizeof(info), p);
1214 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1215 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1217 if (*useragent && *useragent != '\n' && isspace(*useragent))
1221 p = strchr(p, '\n');
1228 redir_type = REDIR_NONE;
1229 if (match_ext(filename, "asx")) {
1230 redir_type = REDIR_ASX;
1231 filename[strlen(filename)-1] = 'f';
1232 } else if (match_ext(filename, "asf") &&
1233 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1234 /* if this isn't WMP or lookalike, return the redirector file */
1235 redir_type = REDIR_ASF;
1236 } else if (match_ext(filename, "rpm,ram")) {
1237 redir_type = REDIR_RAM;
1238 strcpy(filename + strlen(filename)-2, "m");
1239 } else if (match_ext(filename, "rtsp")) {
1240 redir_type = REDIR_RTSP;
1241 compute_real_filename(filename, sizeof(url) - 1);
1242 } else if (match_ext(filename, "sdp")) {
1243 redir_type = REDIR_SDP;
1244 compute_real_filename(filename, sizeof(url) - 1);
1247 stream = first_stream;
1248 while (stream != NULL) {
1249 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1251 stream = stream->next;
1253 if (stream == NULL) {
1254 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1259 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1260 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1262 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1263 c->http_error = 301;
1265 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1266 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1267 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1268 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1269 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1270 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1271 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1273 /* prepare output buffer */
1274 c->buffer_ptr = c->buffer;
1276 c->state = HTTPSTATE_SEND_HEADER;
1280 /* If this is WMP, get the rate information */
1281 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1282 if (modify_current_stream(c, ratebuf)) {
1283 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1284 if (c->switch_feed_streams[i] >= 0)
1285 do_switch_stream(c, i);
1290 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1291 current_bandwidth += stream->bandwidth;
1294 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1295 c->http_error = 200;
1297 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1298 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1299 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1300 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1301 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");
1302 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",
1303 current_bandwidth, max_bandwidth);
1304 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1306 /* prepare output buffer */
1307 c->buffer_ptr = c->buffer;
1309 c->state = HTTPSTATE_SEND_HEADER;
1313 if (redir_type != REDIR_NONE) {
1316 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1317 if (strncasecmp(p, "Host:", 5) == 0) {
1321 p = strchr(p, '\n');
1332 while (isspace(*hostinfo))
1335 eoh = strchr(hostinfo, '\n');
1337 if (eoh[-1] == '\r')
1340 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1341 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1342 hostbuf[eoh - hostinfo] = 0;
1344 c->http_error = 200;
1346 switch(redir_type) {
1348 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1349 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1350 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1351 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1352 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1353 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1354 hostbuf, filename, info);
1355 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1358 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1359 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\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, "# Autogenerated by ffserver\r\n");
1362 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1363 hostbuf, filename, info);
1366 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1367 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1368 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1369 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1370 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1371 hostbuf, filename, info);
1375 char hostname[256], *p;
1376 /* extract only hostname */
1377 pstrcpy(hostname, sizeof(hostname), hostbuf);
1378 p = strrchr(hostname, ':');
1381 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1382 /* XXX: incorrect mime type ? */
1383 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1384 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1385 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1386 hostname, ntohs(my_rtsp_addr.sin_port),
1393 int sdp_data_size, len;
1394 struct sockaddr_in my_addr;
1396 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1397 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1398 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1400 len = sizeof(my_addr);
1401 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1403 /* XXX: should use a dynamic buffer */
1404 sdp_data_size = prepare_sdp_description(stream,
1407 if (sdp_data_size > 0) {
1408 memcpy(q, sdp_data, sdp_data_size);
1420 /* prepare output buffer */
1421 c->buffer_ptr = c->buffer;
1423 c->state = HTTPSTATE_SEND_HEADER;
1429 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1433 stream->conns_served++;
1435 /* XXX: add there authenticate and IP match */
1438 /* if post, it means a feed is being sent */
1439 if (!stream->is_feed) {
1440 /* However it might be a status report from WMP! Lets log the data
1441 * as it might come in handy one day
1446 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1447 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1451 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1452 client_id = strtol(p + 18, 0, 10);
1454 p = strchr(p, '\n');
1462 char *eol = strchr(logline, '\n');
1467 if (eol[-1] == '\r')
1469 http_log("%.*s\n", (int) (eol - logline), logline);
1470 c->suppress_log = 1;
1475 http_log("\nGot request:\n%s\n", c->buffer);
1478 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1481 /* Now we have to find the client_id */
1482 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1483 if (wmpc->wmp_client_id == client_id)
1488 if (modify_current_stream(wmpc, ratebuf)) {
1489 wmpc->switch_pending = 1;
1494 snprintf(msg, sizeof(msg), "POST command not handled");
1498 if (http_start_receive_data(c) < 0) {
1499 snprintf(msg, sizeof(msg), "could not open feed");
1503 c->state = HTTPSTATE_RECEIVE_DATA;
1508 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1509 http_log("\nGot request:\n%s\n", c->buffer);
1513 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1516 /* open input stream */
1517 if (open_input_stream(c, info) < 0) {
1518 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1522 /* prepare http header */
1524 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1525 mime_type = c->stream->fmt->mime_type;
1527 mime_type = "application/x-octet_stream";
1528 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1530 /* for asf, we need extra headers */
1531 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1532 /* Need to allocate a client id */
1534 c->wmp_client_id = random() & 0x7fffffff;
1536 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);
1538 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1539 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1541 /* prepare output buffer */
1543 c->buffer_ptr = c->buffer;
1545 c->state = HTTPSTATE_SEND_HEADER;
1548 c->http_error = 404;
1550 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1551 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1552 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1553 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1554 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1555 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1556 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1558 /* prepare output buffer */
1559 c->buffer_ptr = c->buffer;
1561 c->state = HTTPSTATE_SEND_HEADER;
1565 c->http_error = 200; /* horrible : we use this value to avoid
1566 going to the send data state */
1567 c->state = HTTPSTATE_SEND_HEADER;
1571 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1573 static const char *suffix = " kMGTP";
1576 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1579 url_fprintf(pb, "%"PRId64"%c", count, *s);
1582 static void compute_stats(HTTPContext *c)
1589 ByteIOContext pb1, *pb = &pb1;
1591 if (url_open_dyn_buf(pb) < 0) {
1592 /* XXX: return an error ? */
1593 c->buffer_ptr = c->buffer;
1594 c->buffer_end = c->buffer;
1598 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1599 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1600 url_fprintf(pb, "Pragma: no-cache\r\n");
1601 url_fprintf(pb, "\r\n");
1603 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1604 if (c->stream->feed_filename) {
1605 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1607 url_fprintf(pb, "</HEAD>\n<BODY>");
1608 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1610 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1611 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1612 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");
1613 stream = first_stream;
1614 while (stream != NULL) {
1615 char sfilename[1024];
1618 if (stream->feed != stream) {
1619 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1620 eosf = sfilename + strlen(sfilename);
1621 if (eosf - sfilename >= 4) {
1622 if (strcmp(eosf - 4, ".asf") == 0) {
1623 strcpy(eosf - 4, ".asx");
1624 } else if (strcmp(eosf - 3, ".rm") == 0) {
1625 strcpy(eosf - 3, ".ram");
1626 } else if (stream->fmt == &rtp_mux) {
1627 /* generate a sample RTSP director if
1628 unicast. Generate an SDP redirector if
1630 eosf = strrchr(sfilename, '.');
1632 eosf = sfilename + strlen(sfilename);
1633 if (stream->is_multicast)
1634 strcpy(eosf, ".sdp");
1636 strcpy(eosf, ".rtsp");
1640 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1641 sfilename, stream->filename);
1642 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1643 stream->conns_served);
1644 fmt_bytecount(pb, stream->bytes_served);
1645 switch(stream->stream_type) {
1646 case STREAM_TYPE_LIVE:
1648 int audio_bit_rate = 0;
1649 int video_bit_rate = 0;
1650 const char *audio_codec_name = "";
1651 const char *video_codec_name = "";
1652 const char *audio_codec_name_extra = "";
1653 const char *video_codec_name_extra = "";
1655 for(i=0;i<stream->nb_streams;i++) {
1656 AVStream *st = stream->streams[i];
1657 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1658 switch(st->codec->codec_type) {
1659 case CODEC_TYPE_AUDIO:
1660 audio_bit_rate += st->codec->bit_rate;
1662 if (*audio_codec_name)
1663 audio_codec_name_extra = "...";
1664 audio_codec_name = codec->name;
1667 case CODEC_TYPE_VIDEO:
1668 video_bit_rate += st->codec->bit_rate;
1670 if (*video_codec_name)
1671 video_codec_name_extra = "...";
1672 video_codec_name = codec->name;
1675 case CODEC_TYPE_DATA:
1676 video_bit_rate += st->codec->bit_rate;
1682 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",
1685 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1686 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1688 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1690 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1692 url_fprintf(pb, "\n");
1696 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1700 stream = stream->next;
1702 url_fprintf(pb, "</TABLE>\n");
1704 stream = first_stream;
1705 while (stream != NULL) {
1706 if (stream->feed == stream) {
1707 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1709 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1711 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1716 /* This is somewhat linux specific I guess */
1717 snprintf(ps_cmd, sizeof(ps_cmd),
1718 "ps -o \"%%cpu,cputime\" --no-headers %d",
1721 pid_stat = popen(ps_cmd, "r");
1726 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1728 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1736 url_fprintf(pb, "<p>");
1738 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");
1740 for (i = 0; i < stream->nb_streams; i++) {
1741 AVStream *st = stream->streams[i];
1742 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1743 const char *type = "unknown";
1744 char parameters[64];
1748 switch(st->codec->codec_type) {
1749 case CODEC_TYPE_AUDIO:
1752 case CODEC_TYPE_VIDEO:
1754 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1755 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1760 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1761 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1763 url_fprintf(pb, "</table>\n");
1766 stream = stream->next;
1772 AVCodecContext *enc;
1776 stream = first_feed;
1777 while (stream != NULL) {
1778 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1779 url_fprintf(pb, "<TABLE>\n");
1780 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1781 for(i=0;i<stream->nb_streams;i++) {
1782 AVStream *st = stream->streams[i];
1783 FeedData *fdata = st->priv_data;
1786 avcodec_string(buf, sizeof(buf), enc);
1787 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1788 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1789 avg /= enc->frame_size;
1790 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1791 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1793 url_fprintf(pb, "</TABLE>\n");
1794 stream = stream->next_feed;
1799 /* connection status */
1800 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1802 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1803 nb_connections, nb_max_connections);
1805 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1806 current_bandwidth, max_bandwidth);
1808 url_fprintf(pb, "<TABLE>\n");
1809 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");
1810 c1 = first_http_ctx;
1812 while (c1 != NULL) {
1818 for (j = 0; j < c1->stream->nb_streams; j++) {
1819 if (!c1->stream->feed) {
1820 bitrate += c1->stream->streams[j]->codec->bit_rate;
1822 if (c1->feed_streams[j] >= 0) {
1823 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1830 p = inet_ntoa(c1->from_addr.sin_addr);
1831 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1833 c1->stream ? c1->stream->filename : "",
1834 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1837 http_state[c1->state]);
1838 fmt_bytecount(pb, bitrate);
1839 url_fprintf(pb, "<td align=right>");
1840 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1841 url_fprintf(pb, "<td align=right>");
1842 fmt_bytecount(pb, c1->data_count);
1843 url_fprintf(pb, "\n");
1846 url_fprintf(pb, "</TABLE>\n");
1851 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1852 url_fprintf(pb, "</BODY>\n</HTML>\n");
1854 len = url_close_dyn_buf(pb, &c->pb_buffer);
1855 c->buffer_ptr = c->pb_buffer;
1856 c->buffer_end = c->pb_buffer + len;
1859 /* check if the parser needs to be opened for stream i */
1860 static void open_parser(AVFormatContext *s, int i)
1862 AVStream *st = s->streams[i];
1865 if (!st->codec->codec) {
1866 codec = avcodec_find_decoder(st->codec->codec_id);
1867 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1868 st->codec->parse_only = 1;
1869 if (avcodec_open(st->codec, codec) < 0) {
1870 st->codec->parse_only = 0;
1876 static int open_input_stream(HTTPContext *c, const char *info)
1879 char input_filename[1024];
1884 /* find file name */
1885 if (c->stream->feed) {
1886 strcpy(input_filename, c->stream->feed->feed_filename);
1887 buf_size = FFM_PACKET_SIZE;
1888 /* compute position (absolute time) */
1889 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1890 stream_pos = parse_date(buf, 0);
1891 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1892 int prebuffer = strtol(buf, 0, 10);
1893 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1895 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1898 strcpy(input_filename, c->stream->feed_filename);
1900 /* compute position (relative time) */
1901 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1902 stream_pos = parse_date(buf, 1);
1907 if (input_filename[0] == '\0')
1911 { time_t when = stream_pos / 1000000;
1912 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1917 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1918 buf_size, c->stream->ap_in) < 0) {
1919 http_log("%s not found", input_filename);
1924 /* open each parser */
1925 for(i=0;i<s->nb_streams;i++)
1928 /* choose stream as clock source (we favorize video stream if
1929 present) for packet sending */
1930 c->pts_stream_index = 0;
1931 for(i=0;i<c->stream->nb_streams;i++) {
1932 if (c->pts_stream_index == 0 &&
1933 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1934 c->pts_stream_index = i;
1939 if (c->fmt_in->iformat->read_seek) {
1940 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1943 /* set the start time (needed for maxtime and RTP packet timing) */
1944 c->start_time = cur_time;
1945 c->first_pts = AV_NOPTS_VALUE;
1949 /* return the server clock (in us) */
1950 static int64_t get_server_clock(HTTPContext *c)
1952 /* compute current pts value from system time */
1953 return (int64_t)(cur_time - c->start_time) * 1000LL;
1956 /* return the estimated time at which the current packet must be sent
1958 static int64_t get_packet_send_clock(HTTPContext *c)
1960 int bytes_left, bytes_sent, frame_bytes;
1962 frame_bytes = c->cur_frame_bytes;
1963 if (frame_bytes <= 0) {
1966 bytes_left = c->buffer_end - c->buffer_ptr;
1967 bytes_sent = frame_bytes - bytes_left;
1968 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1973 static int http_prepare_data(HTTPContext *c)
1976 AVFormatContext *ctx;
1978 av_freep(&c->pb_buffer);
1980 case HTTPSTATE_SEND_DATA_HEADER:
1981 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1982 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
1984 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
1985 c->stream->comment);
1986 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
1987 c->stream->copyright);
1988 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
1991 /* open output stream by using specified codecs */
1992 c->fmt_ctx.oformat = c->stream->fmt;
1993 c->fmt_ctx.nb_streams = c->stream->nb_streams;
1994 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1997 st = av_mallocz(sizeof(AVStream));
1998 st->codec= avcodec_alloc_context();
1999 c->fmt_ctx.streams[i] = st;
2000 /* if file or feed, then just take streams from FFStream struct */
2001 if (!c->stream->feed ||
2002 c->stream->feed == c->stream)
2003 src = c->stream->streams[i];
2005 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2009 st->codec->frame_number = 0; /* XXX: should be done in
2010 AVStream, not in codec */
2011 /* I'm pretty sure that this is not correct...
2012 * However, without it, we crash
2014 st->codec->coded_frame = &dummy_frame;
2016 c->got_key_frame = 0;
2018 /* prepare header and save header data in a stream */
2019 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2020 /* XXX: potential leak */
2023 c->fmt_ctx.pb.is_streamed = 1;
2025 av_set_parameters(&c->fmt_ctx, NULL);
2026 av_write_header(&c->fmt_ctx);
2028 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2029 c->buffer_ptr = c->pb_buffer;
2030 c->buffer_end = c->pb_buffer + len;
2032 c->state = HTTPSTATE_SEND_DATA;
2033 c->last_packet_sent = 0;
2035 case HTTPSTATE_SEND_DATA:
2036 /* find a new packet */
2040 /* read a packet from the input stream */
2041 if (c->stream->feed) {
2042 ffm_set_write_index(c->fmt_in,
2043 c->stream->feed->feed_write_index,
2044 c->stream->feed->feed_size);
2047 if (c->stream->max_time &&
2048 c->stream->max_time + c->start_time - cur_time < 0) {
2049 /* We have timed out */
2050 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2053 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2054 if (c->stream->feed && c->stream->feed->feed_opened) {
2055 /* if coming from feed, it means we reached the end of the
2056 ffm file, so must wait for more data */
2057 c->state = HTTPSTATE_WAIT_FEED;
2058 return 1; /* state changed */
2060 if (c->stream->loop) {
2061 av_close_input_file(c->fmt_in);
2063 if (open_input_stream(c, "") < 0)
2068 /* must send trailer now because eof or error */
2069 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2073 /* update first pts if needed */
2074 if (c->first_pts == AV_NOPTS_VALUE) {
2075 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2076 c->start_time = cur_time;
2078 /* send it to the appropriate stream */
2079 if (c->stream->feed) {
2080 /* if coming from a feed, select the right stream */
2081 if (c->switch_pending) {
2082 c->switch_pending = 0;
2083 for(i=0;i<c->stream->nb_streams;i++) {
2084 if (c->switch_feed_streams[i] == pkt.stream_index) {
2085 if (pkt.flags & PKT_FLAG_KEY) {
2086 do_switch_stream(c, i);
2089 if (c->switch_feed_streams[i] >= 0) {
2090 c->switch_pending = 1;
2094 for(i=0;i<c->stream->nb_streams;i++) {
2095 if (c->feed_streams[i] == pkt.stream_index) {
2096 pkt.stream_index = i;
2097 if (pkt.flags & PKT_FLAG_KEY) {
2098 c->got_key_frame |= 1 << i;
2100 /* See if we have all the key frames, then
2101 * we start to send. This logic is not quite
2102 * right, but it works for the case of a
2103 * single video stream with one or more
2104 * audio streams (for which every frame is
2105 * typically a key frame).
2107 if (!c->stream->send_on_key ||
2108 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2114 AVCodecContext *codec;
2117 /* specific handling for RTP: we use several
2118 output stream (one for each RTP
2119 connection). XXX: need more abstract handling */
2120 if (c->is_packetized) {
2122 /* compute send time and duration */
2123 st = c->fmt_in->streams[pkt.stream_index];
2124 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2125 if (st->start_time != AV_NOPTS_VALUE)
2126 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2127 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2129 printf("index=%d pts=%0.3f duration=%0.6f\n",
2131 (double)c->cur_pts /
2133 (double)c->cur_frame_duration /
2136 /* find RTP context */
2137 c->packet_stream_index = pkt.stream_index;
2138 ctx = c->rtp_ctx[c->packet_stream_index];
2140 av_free_packet(&pkt);
2143 codec = ctx->streams[0]->codec;
2144 /* only one stream per RTP connection */
2145 pkt.stream_index = 0;
2149 codec = ctx->streams[pkt.stream_index]->codec;
2152 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2153 if (c->is_packetized) {
2154 int max_packet_size;
2155 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2156 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2158 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2159 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2161 ret = url_open_dyn_buf(&ctx->pb);
2164 /* XXX: potential leak */
2167 if (av_write_frame(ctx, &pkt)) {
2168 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2171 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2172 c->cur_frame_bytes = len;
2173 c->buffer_ptr = c->pb_buffer;
2174 c->buffer_end = c->pb_buffer + len;
2176 codec->frame_number++;
2180 av_free_packet(&pkt);
2186 case HTTPSTATE_SEND_DATA_TRAILER:
2187 /* last packet test ? */
2188 if (c->last_packet_sent || c->is_packetized)
2191 /* prepare header */
2192 if (url_open_dyn_buf(&ctx->pb) < 0) {
2193 /* XXX: potential leak */
2196 av_write_trailer(ctx);
2197 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2198 c->buffer_ptr = c->pb_buffer;
2199 c->buffer_end = c->pb_buffer + len;
2201 c->last_packet_sent = 1;
2208 #define SHORT_TERM_BANDWIDTH 8000000
2210 /* should convert the format at the same time */
2211 /* send data starting at c->buffer_ptr to the output connection
2212 (either UDP or TCP connection) */
2213 static int http_send_data(HTTPContext *c)
2218 if (c->buffer_ptr >= c->buffer_end) {
2219 ret = http_prepare_data(c);
2222 else if (ret != 0) {
2223 /* state change requested */
2227 if (c->is_packetized) {
2228 /* RTP data output */
2229 len = c->buffer_end - c->buffer_ptr;
2231 /* fail safe - should never happen */
2233 c->buffer_ptr = c->buffer_end;
2236 len = (c->buffer_ptr[0] << 24) |
2237 (c->buffer_ptr[1] << 16) |
2238 (c->buffer_ptr[2] << 8) |
2240 if (len > (c->buffer_end - c->buffer_ptr))
2242 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2243 /* nothing to send yet: we can wait */
2247 c->data_count += len;
2248 update_datarate(&c->datarate, c->data_count);
2250 c->stream->bytes_served += len;
2252 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2253 /* RTP packets are sent inside the RTSP TCP connection */
2254 ByteIOContext pb1, *pb = &pb1;
2255 int interleaved_index, size;
2257 HTTPContext *rtsp_c;
2260 /* if no RTSP connection left, error */
2263 /* if already sending something, then wait. */
2264 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
2267 if (url_open_dyn_buf(pb) < 0)
2269 interleaved_index = c->packet_stream_index * 2;
2270 /* RTCP packets are sent at odd indexes */
2271 if (c->buffer_ptr[1] == 200)
2272 interleaved_index++;
2273 /* write RTSP TCP header */
2275 header[1] = interleaved_index;
2276 header[2] = len >> 8;
2278 put_buffer(pb, header, 4);
2279 /* write RTP packet data */
2281 put_buffer(pb, c->buffer_ptr, len);
2282 size = url_close_dyn_buf(pb, &c->packet_buffer);
2283 /* prepare asynchronous TCP sending */
2284 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2285 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2286 c->buffer_ptr += len;
2288 /* send everything we can NOW */
2289 len = write(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2290 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr);
2292 rtsp_c->packet_buffer_ptr += len;
2294 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2295 /* if we could not send all the data, we will
2296 send it later, so a new state is needed to
2297 "lock" the RTSP TCP connection */
2298 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2301 /* all data has been sent */
2302 av_freep(&c->packet_buffer);
2305 /* send RTP packet directly in UDP */
2307 url_write(c->rtp_handles[c->packet_stream_index],
2308 c->buffer_ptr, len);
2309 c->buffer_ptr += len;
2310 /* here we continue as we can send several packets per 10 ms slot */
2313 /* TCP data output */
2314 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2316 if (errno != EAGAIN && errno != EINTR) {
2317 /* error : close connection */
2323 c->buffer_ptr += len;
2325 c->data_count += len;
2326 update_datarate(&c->datarate, c->data_count);
2328 c->stream->bytes_served += len;
2336 static int http_start_receive_data(HTTPContext *c)
2340 if (c->stream->feed_opened)
2343 /* Don't permit writing to this one */
2344 if (c->stream->readonly)
2348 fd = open(c->stream->feed_filename, O_RDWR);
2353 c->stream->feed_write_index = ffm_read_write_index(fd);
2354 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2355 lseek(fd, 0, SEEK_SET);
2357 /* init buffer input */
2358 c->buffer_ptr = c->buffer;
2359 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2360 c->stream->feed_opened = 1;
2364 static int http_receive_data(HTTPContext *c)
2368 if (c->buffer_end > c->buffer_ptr) {
2371 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2373 if (errno != EAGAIN && errno != EINTR) {
2374 /* error : close connection */
2377 } else if (len == 0) {
2378 /* end of connection : close it */
2381 c->buffer_ptr += len;
2382 c->data_count += len;
2383 update_datarate(&c->datarate, c->data_count);
2387 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2388 if (c->buffer[0] != 'f' ||
2389 c->buffer[1] != 'm') {
2390 http_log("Feed stream has become desynchronized -- disconnecting\n");
2395 if (c->buffer_ptr >= c->buffer_end) {
2396 FFStream *feed = c->stream;
2397 /* a packet has been received : write it in the store, except
2399 if (c->data_count > FFM_PACKET_SIZE) {
2401 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2402 /* XXX: use llseek or url_seek */
2403 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2404 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2406 feed->feed_write_index += FFM_PACKET_SIZE;
2407 /* update file size */
2408 if (feed->feed_write_index > c->stream->feed_size)
2409 feed->feed_size = feed->feed_write_index;
2411 /* handle wrap around if max file size reached */
2412 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2413 feed->feed_write_index = FFM_PACKET_SIZE;
2416 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2418 /* wake up any waiting connections */
2419 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2420 if (c1->state == HTTPSTATE_WAIT_FEED &&
2421 c1->stream->feed == c->stream->feed) {
2422 c1->state = HTTPSTATE_SEND_DATA;
2426 /* We have a header in our hands that contains useful data */
2428 AVInputFormat *fmt_in;
2429 ByteIOContext *pb = &s.pb;
2432 memset(&s, 0, sizeof(s));
2434 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2435 pb->buf_end = c->buffer_end; /* ?? */
2436 pb->is_streamed = 1;
2438 /* use feed output format name to find corresponding input format */
2439 fmt_in = av_find_input_format(feed->fmt->name);
2443 if (fmt_in->priv_data_size > 0) {
2444 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2450 if (fmt_in->read_header(&s, 0) < 0) {
2451 av_freep(&s.priv_data);
2455 /* Now we have the actual streams */
2456 if (s.nb_streams != feed->nb_streams) {
2457 av_freep(&s.priv_data);
2460 for (i = 0; i < s.nb_streams; i++) {
2461 memcpy(feed->streams[i]->codec,
2462 s.streams[i]->codec, sizeof(AVCodecContext));
2464 av_freep(&s.priv_data);
2466 c->buffer_ptr = c->buffer;
2471 c->stream->feed_opened = 0;
2476 /********************************************************************/
2479 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2486 switch(error_number) {
2487 #define DEF(n, c, s) case c: str = s; break;
2488 #include "rtspcodes.h"
2491 str = "Unknown Error";
2495 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2496 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2498 /* output GMT time */
2502 p = buf2 + strlen(p) - 1;
2505 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2508 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2510 rtsp_reply_header(c, error_number);
2511 url_fprintf(c->pb, "\r\n");
2514 static int rtsp_parse_request(HTTPContext *c)
2516 const char *p, *p1, *p2;
2523 RTSPHeader header1, *header = &header1;
2525 c->buffer_ptr[0] = '\0';
2528 get_word(cmd, sizeof(cmd), &p);
2529 get_word(url, sizeof(url), &p);
2530 get_word(protocol, sizeof(protocol), &p);
2532 pstrcpy(c->method, sizeof(c->method), cmd);
2533 pstrcpy(c->url, sizeof(c->url), url);
2534 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2537 if (url_open_dyn_buf(c->pb) < 0) {
2538 /* XXX: cannot do more */
2539 c->pb = NULL; /* safety */
2543 /* check version name */
2544 if (strcmp(protocol, "RTSP/1.0") != 0) {
2545 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2549 /* parse each header line */
2550 memset(header, 0, sizeof(RTSPHeader));
2551 /* skip to next line */
2552 while (*p != '\n' && *p != '\0')
2556 while (*p != '\0') {
2557 p1 = strchr(p, '\n');
2561 if (p2 > p && p2[-1] == '\r')
2563 /* skip empty line */
2567 if (len > sizeof(line) - 1)
2568 len = sizeof(line) - 1;
2569 memcpy(line, p, len);
2571 rtsp_parse_line(header, line);
2575 /* handle sequence number */
2576 c->seq = header->seq;
2578 if (!strcmp(cmd, "DESCRIBE")) {
2579 rtsp_cmd_describe(c, url);
2580 } else if (!strcmp(cmd, "OPTIONS")) {
2581 rtsp_cmd_options(c, url);
2582 } else if (!strcmp(cmd, "SETUP")) {
2583 rtsp_cmd_setup(c, url, header);
2584 } else if (!strcmp(cmd, "PLAY")) {
2585 rtsp_cmd_play(c, url, header);
2586 } else if (!strcmp(cmd, "PAUSE")) {
2587 rtsp_cmd_pause(c, url, header);
2588 } else if (!strcmp(cmd, "TEARDOWN")) {
2589 rtsp_cmd_teardown(c, url, header);
2591 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2594 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2595 c->pb = NULL; /* safety */
2597 /* XXX: cannot do more */
2600 c->buffer_ptr = c->pb_buffer;
2601 c->buffer_end = c->pb_buffer + len;
2602 c->state = RTSPSTATE_SEND_REPLY;
2606 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2608 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2609 struct in_addr my_ip)
2611 ByteIOContext pb1, *pb = &pb1;
2612 int i, payload_type, port, private_payload_type, j;
2613 const char *ipstr, *title, *mediatype;
2616 if (url_open_dyn_buf(pb) < 0)
2619 /* general media info */
2621 url_fprintf(pb, "v=0\n");
2622 ipstr = inet_ntoa(my_ip);
2623 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2624 title = stream->title;
2625 if (title[0] == '\0')
2627 url_fprintf(pb, "s=%s\n", title);
2628 if (stream->comment[0] != '\0')
2629 url_fprintf(pb, "i=%s\n", stream->comment);
2630 if (stream->is_multicast) {
2631 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2633 /* for each stream, we output the necessary info */
2634 private_payload_type = RTP_PT_PRIVATE;
2635 for(i = 0; i < stream->nb_streams; i++) {
2636 st = stream->streams[i];
2637 if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
2638 mediatype = "video";
2640 switch(st->codec->codec_type) {
2641 case CODEC_TYPE_AUDIO:
2642 mediatype = "audio";
2644 case CODEC_TYPE_VIDEO:
2645 mediatype = "video";
2648 mediatype = "application";
2652 /* NOTE: the port indication is not correct in case of
2653 unicast. It is not an issue because RTSP gives it */
2654 payload_type = rtp_get_payload_type(st->codec);
2655 if (payload_type < 0)
2656 payload_type = private_payload_type++;
2657 if (stream->is_multicast) {
2658 port = stream->multicast_port + 2 * i;
2662 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2663 mediatype, port, payload_type);
2664 if (payload_type >= RTP_PT_PRIVATE) {
2665 /* for private payload type, we need to give more info */
2666 switch(st->codec->codec_id) {
2667 case CODEC_ID_MPEG4:
2670 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2671 payload_type, 90000);
2672 /* we must also add the mpeg4 header */
2673 data = st->codec->extradata;
2675 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2676 for(j=0;j<st->codec->extradata_size;j++) {
2677 url_fprintf(pb, "%02x", data[j]);
2679 url_fprintf(pb, "\n");
2684 /* XXX: add other codecs ? */
2688 url_fprintf(pb, "a=control:streamid=%d\n", i);
2690 return url_close_dyn_buf(pb, pbuffer);
2692 url_close_dyn_buf(pb, pbuffer);
2697 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2699 // rtsp_reply_header(c, RTSP_STATUS_OK);
2700 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2701 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2702 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2703 url_fprintf(c->pb, "\r\n");
2706 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2712 int content_length, len;
2713 struct sockaddr_in my_addr;
2715 /* find which url is asked */
2716 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2721 for(stream = first_stream; stream != NULL; stream = stream->next) {
2722 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2723 !strcmp(path, stream->filename)) {
2727 /* no stream found */
2728 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2732 /* prepare the media description in sdp format */
2734 /* get the host IP */
2735 len = sizeof(my_addr);
2736 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2737 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2738 if (content_length < 0) {
2739 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2742 rtsp_reply_header(c, RTSP_STATUS_OK);
2743 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2744 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2745 url_fprintf(c->pb, "\r\n");
2746 put_buffer(c->pb, content, content_length);
2749 static HTTPContext *find_rtp_session(const char *session_id)
2753 if (session_id[0] == '\0')
2756 for(c = first_http_ctx; c != NULL; c = c->next) {
2757 if (!strcmp(c->session_id, session_id))
2763 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2765 RTSPTransportField *th;
2768 for(i=0;i<h->nb_transports;i++) {
2769 th = &h->transports[i];
2770 if (th->protocol == protocol)
2776 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2780 int stream_index, port;
2785 RTSPTransportField *th;
2786 struct sockaddr_in dest_addr;
2787 RTSPActionServerSetup setup;
2789 /* find which url is asked */
2790 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2795 /* now check each stream */
2796 for(stream = first_stream; stream != NULL; stream = stream->next) {
2797 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2798 /* accept aggregate filenames only if single stream */
2799 if (!strcmp(path, stream->filename)) {
2800 if (stream->nb_streams != 1) {
2801 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2808 for(stream_index = 0; stream_index < stream->nb_streams;
2810 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2811 stream->filename, stream_index);
2812 if (!strcmp(path, buf))
2817 /* no stream found */
2818 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2822 /* generate session id if needed */
2823 if (h->session_id[0] == '\0') {
2824 snprintf(h->session_id, sizeof(h->session_id),
2825 "%08x%08x", (int)random(), (int)random());
2828 /* find rtp session, and create it if none found */
2829 rtp_c = find_rtp_session(h->session_id);
2831 /* always prefer UDP */
2832 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2834 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2836 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2841 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2844 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2848 /* open input stream */
2849 if (open_input_stream(rtp_c, "") < 0) {
2850 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2855 /* test if stream is OK (test needed because several SETUP needs
2856 to be done for a given file) */
2857 if (rtp_c->stream != stream) {
2858 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2862 /* test if stream is already set up */
2863 if (rtp_c->rtp_ctx[stream_index]) {
2864 rtsp_reply_error(c, RTSP_STATUS_STATE);
2868 /* check transport */
2869 th = find_transport(h, rtp_c->rtp_protocol);
2870 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2871 th->client_port_min <= 0)) {
2872 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2876 /* setup default options */
2877 setup.transport_option[0] = '\0';
2878 dest_addr = rtp_c->from_addr;
2879 dest_addr.sin_port = htons(th->client_port_min);
2881 /* add transport option if needed */
2882 if (ff_rtsp_callback) {
2883 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2884 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2885 (char *)&setup, sizeof(setup),
2886 stream->rtsp_option) < 0) {
2887 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2890 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2894 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2895 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2899 /* now everything is OK, so we can send the connection parameters */
2900 rtsp_reply_header(c, RTSP_STATUS_OK);
2902 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2904 switch(rtp_c->rtp_protocol) {
2905 case RTSP_PROTOCOL_RTP_UDP:
2906 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2907 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2908 "client_port=%d-%d;server_port=%d-%d",
2909 th->client_port_min, th->client_port_min + 1,
2912 case RTSP_PROTOCOL_RTP_TCP:
2913 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2914 stream_index * 2, stream_index * 2 + 1);
2919 if (setup.transport_option[0] != '\0') {
2920 url_fprintf(c->pb, ";%s", setup.transport_option);
2922 url_fprintf(c->pb, "\r\n");
2925 url_fprintf(c->pb, "\r\n");
2929 /* find an rtp connection by using the session ID. Check consistency
2931 static HTTPContext *find_rtp_session_with_url(const char *url,
2932 const char *session_id)
2940 rtp_c = find_rtp_session(session_id);
2944 /* find which url is asked */
2945 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2949 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2950 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2951 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2952 rtp_c->stream->filename, s);
2953 if(!strncmp(path, buf, sizeof(buf))) {
2954 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2961 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2965 rtp_c = find_rtp_session_with_url(url, h->session_id);
2967 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2971 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2972 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2973 rtp_c->state != HTTPSTATE_READY) {
2974 rtsp_reply_error(c, RTSP_STATUS_STATE);
2979 /* XXX: seek in stream */
2980 if (h->range_start != AV_NOPTS_VALUE) {
2981 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2982 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2986 rtp_c->state = HTTPSTATE_SEND_DATA;
2988 /* now everything is OK, so we can send the connection parameters */
2989 rtsp_reply_header(c, RTSP_STATUS_OK);
2991 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2992 url_fprintf(c->pb, "\r\n");
2995 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2999 rtp_c = find_rtp_session_with_url(url, h->session_id);
3001 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3005 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3006 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3007 rtsp_reply_error(c, RTSP_STATUS_STATE);
3011 rtp_c->state = HTTPSTATE_READY;
3012 rtp_c->first_pts = AV_NOPTS_VALUE;
3013 /* now everything is OK, so we can send the connection parameters */
3014 rtsp_reply_header(c, RTSP_STATUS_OK);
3016 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3017 url_fprintf(c->pb, "\r\n");
3020 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3024 rtp_c = find_rtp_session_with_url(url, h->session_id);
3026 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3030 /* abort the session */
3031 close_connection(rtp_c);
3033 if (ff_rtsp_callback) {
3034 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3036 rtp_c->stream->rtsp_option);
3039 /* now everything is OK, so we can send the connection parameters */
3040 rtsp_reply_header(c, RTSP_STATUS_OK);
3042 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3043 url_fprintf(c->pb, "\r\n");
3047 /********************************************************************/
3050 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3051 FFStream *stream, const char *session_id,
3052 enum RTSPProtocol rtp_protocol)
3054 HTTPContext *c = NULL;
3055 const char *proto_str;
3057 /* XXX: should output a warning page when coming
3058 close to the connection limit */
3059 if (nb_connections >= nb_max_connections)
3062 /* add a new connection */
3063 c = av_mallocz(sizeof(HTTPContext));
3068 c->poll_entry = NULL;
3069 c->from_addr = *from_addr;
3070 c->buffer_size = IOBUFFER_INIT_SIZE;
3071 c->buffer = av_malloc(c->buffer_size);
3076 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3077 c->state = HTTPSTATE_READY;
3078 c->is_packetized = 1;
3079 c->rtp_protocol = rtp_protocol;
3081 /* protocol is shown in statistics */
3082 switch(c->rtp_protocol) {
3083 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3084 proto_str = "MCAST";
3086 case RTSP_PROTOCOL_RTP_UDP:
3089 case RTSP_PROTOCOL_RTP_TCP:
3096 pstrcpy(c->protocol, sizeof(c->protocol), "RTP/");
3097 pstrcat(c->protocol, sizeof(c->protocol), proto_str);
3099 current_bandwidth += stream->bandwidth;
3101 c->next = first_http_ctx;
3113 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3114 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3116 static int rtp_new_av_stream(HTTPContext *c,
3117 int stream_index, struct sockaddr_in *dest_addr,
3118 HTTPContext *rtsp_c)
3120 AVFormatContext *ctx;
3126 int max_packet_size;
3128 /* now we can open the relevant output stream */
3129 ctx = av_alloc_format_context();
3132 ctx->oformat = &rtp_mux;
3134 st = av_mallocz(sizeof(AVStream));
3137 st->codec= avcodec_alloc_context();
3138 ctx->nb_streams = 1;
3139 ctx->streams[0] = st;
3141 if (!c->stream->feed ||
3142 c->stream->feed == c->stream) {
3143 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3146 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3150 /* build destination RTP address */
3151 ipaddr = inet_ntoa(dest_addr->sin_addr);
3153 switch(c->rtp_protocol) {
3154 case RTSP_PROTOCOL_RTP_UDP:
3155 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3158 /* XXX: also pass as parameter to function ? */
3159 if (c->stream->is_multicast) {
3161 ttl = c->stream->multicast_ttl;
3164 snprintf(ctx->filename, sizeof(ctx->filename),
3165 "rtp://%s:%d?multicast=1&ttl=%d",
3166 ipaddr, ntohs(dest_addr->sin_port), ttl);
3168 snprintf(ctx->filename, sizeof(ctx->filename),
3169 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3172 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3174 c->rtp_handles[stream_index] = h;
3175 max_packet_size = url_get_max_packet_size(h);
3177 case RTSP_PROTOCOL_RTP_TCP:
3180 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3186 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3187 ipaddr, ntohs(dest_addr->sin_port),
3189 c->stream->filename, stream_index, c->protocol);
3191 /* normally, no packets should be output here, but the packet size may be checked */
3192 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3193 /* XXX: close stream */
3196 av_set_parameters(ctx, NULL);
3197 if (av_write_header(ctx) < 0) {
3204 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3207 c->rtp_ctx[stream_index] = ctx;
3211 /********************************************************************/
3212 /* ffserver initialization */
3214 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3218 fst = av_mallocz(sizeof(AVStream));
3221 fst->codec= avcodec_alloc_context();
3222 fst->priv_data = av_mallocz(sizeof(FeedData));
3223 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3224 fst->codec->coded_frame = &dummy_frame;
3225 fst->index = stream->nb_streams;
3226 av_set_pts_info(fst, 33, 1, 90000);
3227 stream->streams[stream->nb_streams++] = fst;
3231 /* return the stream number in the feed */
3232 static int add_av_stream(FFStream *feed, AVStream *st)
3235 AVCodecContext *av, *av1;
3239 for(i=0;i<feed->nb_streams;i++) {
3240 st = feed->streams[i];
3242 if (av1->codec_id == av->codec_id &&
3243 av1->codec_type == av->codec_type &&
3244 av1->bit_rate == av->bit_rate) {
3246 switch(av->codec_type) {
3247 case CODEC_TYPE_AUDIO:
3248 if (av1->channels == av->channels &&
3249 av1->sample_rate == av->sample_rate)
3252 case CODEC_TYPE_VIDEO:
3253 if (av1->width == av->width &&
3254 av1->height == av->height &&
3255 av1->time_base.den == av->time_base.den &&
3256 av1->time_base.num == av->time_base.num &&
3257 av1->gop_size == av->gop_size)
3266 fst = add_av_stream1(feed, av);
3269 return feed->nb_streams - 1;
3274 static void remove_stream(FFStream *stream)
3278 while (*ps != NULL) {
3279 if (*ps == stream) {
3287 /* specific mpeg4 handling : we extract the raw parameters */
3288 static void extract_mpeg4_header(AVFormatContext *infile)
3290 int mpeg4_count, i, size;
3296 for(i=0;i<infile->nb_streams;i++) {
3297 st = infile->streams[i];
3298 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3299 st->codec->extradata_size == 0) {
3306 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3307 while (mpeg4_count > 0) {
3308 if (av_read_packet(infile, &pkt) < 0)
3310 st = infile->streams[pkt.stream_index];
3311 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3312 st->codec->extradata_size == 0) {
3313 av_freep(&st->codec->extradata);
3314 /* fill extradata with the header */
3315 /* XXX: we make hard suppositions here ! */
3317 while (p < pkt.data + pkt.size - 4) {
3318 /* stop when vop header is found */
3319 if (p[0] == 0x00 && p[1] == 0x00 &&
3320 p[2] == 0x01 && p[3] == 0xb6) {
3321 size = p - pkt.data;
3322 // av_hex_dump(pkt.data, size);
3323 st->codec->extradata = av_malloc(size);
3324 st->codec->extradata_size = size;
3325 memcpy(st->codec->extradata, pkt.data, size);
3332 av_free_packet(&pkt);
3336 /* compute the needed AVStream for each file */
3337 static void build_file_streams(void)
3339 FFStream *stream, *stream_next;
3340 AVFormatContext *infile;
3343 /* gather all streams */
3344 for(stream = first_stream; stream != NULL; stream = stream_next) {
3345 stream_next = stream->next;
3346 if (stream->stream_type == STREAM_TYPE_LIVE &&
3348 /* the stream comes from a file */
3349 /* try to open the file */
3351 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3352 if (stream->fmt == &rtp_mux) {
3353 /* specific case : if transport stream output to RTP,
3354 we use a raw transport stream reader */
3355 stream->ap_in->mpeg2ts_raw = 1;
3356 stream->ap_in->mpeg2ts_compute_pcr = 1;
3359 if (av_open_input_file(&infile, stream->feed_filename,
3360 stream->ifmt, 0, stream->ap_in) < 0) {
3361 http_log("%s not found", stream->feed_filename);
3362 /* remove stream (no need to spend more time on it) */
3364 remove_stream(stream);
3366 /* find all the AVStreams inside and reference them in
3368 if (av_find_stream_info(infile) < 0) {
3369 http_log("Could not find codec parameters from '%s'",
3370 stream->feed_filename);
3371 av_close_input_file(infile);
3374 extract_mpeg4_header(infile);
3376 for(i=0;i<infile->nb_streams;i++) {
3377 add_av_stream1(stream, infile->streams[i]->codec);
3379 av_close_input_file(infile);
3385 /* compute the needed AVStream for each feed */
3386 static void build_feed_streams(void)
3388 FFStream *stream, *feed;
3391 /* gather all streams */
3392 for(stream = first_stream; stream != NULL; stream = stream->next) {
3393 feed = stream->feed;
3395 if (!stream->is_feed) {
3396 /* we handle a stream coming from a feed */
3397 for(i=0;i<stream->nb_streams;i++) {
3398 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3404 /* gather all streams */
3405 for(stream = first_stream; stream != NULL; stream = stream->next) {
3406 feed = stream->feed;
3408 if (stream->is_feed) {
3409 for(i=0;i<stream->nb_streams;i++) {
3410 stream->feed_streams[i] = i;
3416 /* create feed files if needed */
3417 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3420 if (url_exist(feed->feed_filename)) {
3421 /* See if it matches */
3425 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3426 /* Now see if it matches */
3427 if (s->nb_streams == feed->nb_streams) {
3429 for(i=0;i<s->nb_streams;i++) {
3431 sf = feed->streams[i];
3434 if (sf->index != ss->index ||
3436 printf("Index & Id do not match for stream %d (%s)\n",
3437 i, feed->feed_filename);
3440 AVCodecContext *ccf, *ccs;
3444 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3446 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3447 printf("Codecs do not match for stream %d\n", i);
3449 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3450 printf("Codec bitrates do not match for stream %d\n", i);
3452 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3453 if (CHECK_CODEC(time_base.den) ||
3454 CHECK_CODEC(time_base.num) ||
3455 CHECK_CODEC(width) ||
3456 CHECK_CODEC(height)) {
3457 printf("Codec width, height and framerate do not match for stream %d\n", i);
3460 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3461 if (CHECK_CODEC(sample_rate) ||
3462 CHECK_CODEC(channels) ||
3463 CHECK_CODEC(frame_size)) {
3464 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3468 printf("Unknown codec type\n");
3477 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3478 feed->feed_filename, s->nb_streams, feed->nb_streams);
3481 av_close_input_file(s);
3483 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3484 feed->feed_filename);
3487 if (feed->readonly) {
3488 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3489 feed->feed_filename);
3492 unlink(feed->feed_filename);
3495 if (!url_exist(feed->feed_filename)) {
3496 AVFormatContext s1, *s = &s1;
3498 if (feed->readonly) {
3499 printf("Unable to create feed file '%s' as it is marked readonly\n",
3500 feed->feed_filename);
3504 /* only write the header of the ffm file */
3505 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3506 fprintf(stderr, "Could not open output feed file '%s'\n",
3507 feed->feed_filename);
3510 s->oformat = feed->fmt;
3511 s->nb_streams = feed->nb_streams;
3512 for(i=0;i<s->nb_streams;i++) {
3514 st = feed->streams[i];
3517 av_set_parameters(s, NULL);
3519 /* XXX: need better api */
3520 av_freep(&s->priv_data);
3523 /* get feed size and write index */
3524 fd = open(feed->feed_filename, O_RDONLY);
3526 fprintf(stderr, "Could not open output feed file '%s'\n",
3527 feed->feed_filename);
3531 feed->feed_write_index = ffm_read_write_index(fd);
3532 feed->feed_size = lseek(fd, 0, SEEK_END);
3533 /* ensure that we do not wrap before the end of file */
3534 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3535 feed->feed_max_size = feed->feed_size;
3541 /* compute the bandwidth used by each stream */
3542 static void compute_bandwidth(void)
3547 for(stream = first_stream; stream != NULL; stream = stream->next) {
3549 for(i=0;i<stream->nb_streams;i++) {
3550 AVStream *st = stream->streams[i];
3551 switch(st->codec->codec_type) {
3552 case CODEC_TYPE_AUDIO:
3553 case CODEC_TYPE_VIDEO:
3554 bandwidth += st->codec->bit_rate;
3560 stream->bandwidth = (bandwidth + 999) / 1000;
3564 static void get_arg(char *buf, int buf_size, const char **pp)
3571 while (isspace(*p)) p++;
3574 if (*p == '\"' || *p == '\'')
3586 if ((q - buf) < buf_size - 1)
3591 if (quote && *p == quote)
3596 /* add a codec and set the default parameters */
3597 static void add_codec(FFStream *stream, AVCodecContext *av)
3601 /* compute default parameters */
3602 switch(av->codec_type) {
3603 case CODEC_TYPE_AUDIO:
3604 if (av->bit_rate == 0)
3605 av->bit_rate = 64000;
3606 if (av->sample_rate == 0)
3607 av->sample_rate = 22050;
3608 if (av->channels == 0)
3611 case CODEC_TYPE_VIDEO:
3612 if (av->bit_rate == 0)
3613 av->bit_rate = 64000;
3614 if (av->time_base.num == 0){
3615 av->time_base.den = 5;
3616 av->time_base.num = 1;
3618 if (av->width == 0 || av->height == 0) {
3622 /* Bitrate tolerance is less for streaming */
3623 if (av->bit_rate_tolerance == 0)
3624 av->bit_rate_tolerance = av->bit_rate / 4;
3629 if (av->max_qdiff == 0)
3631 av->qcompress = 0.5;
3634 if (!av->nsse_weight)
3635 av->nsse_weight = 8;
3637 av->frame_skip_cmp = FF_CMP_DCTMAX;
3638 av->me_method = ME_EPZS;
3639 av->rc_buffer_aggressivity = 1.0;
3642 av->rc_eq = "tex^qComp";
3643 if (!av->i_quant_factor)
3644 av->i_quant_factor = -0.8;
3645 if (!av->b_quant_factor)
3646 av->b_quant_factor = 1.25;
3647 if (!av->b_quant_offset)
3648 av->b_quant_offset = 1.25;
3649 if (!av->rc_max_rate)
3650 av->rc_max_rate = av->bit_rate * 2;
3652 if (av->rc_max_rate && !av->rc_buffer_size) {
3653 av->rc_buffer_size = av->rc_max_rate;
3662 st = av_mallocz(sizeof(AVStream));
3665 st->codec = avcodec_alloc_context();
3666 stream->streams[stream->nb_streams++] = st;
3667 memcpy(st->codec, av, sizeof(AVCodecContext));
3670 static int opt_audio_codec(const char *arg)
3676 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3681 return CODEC_ID_NONE;
3687 static int opt_video_codec(const char *arg)
3693 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3698 return CODEC_ID_NONE;
3704 /* simplistic plugin support */
3706 #ifdef CONFIG_HAVE_DLOPEN
3707 void load_module(const char *filename)
3710 void (*init_func)(void);
3711 dll = dlopen(filename, RTLD_NOW);
3713 fprintf(stderr, "Could not load module '%s' - %s\n",
3714 filename, dlerror());
3718 init_func = dlsym(dll, "ffserver_module_init");
3721 "%s: init function 'ffserver_module_init()' not found\n",
3730 static int parse_ffconfig(const char *filename)
3737 int val, errors, line_num;
3738 FFStream **last_stream, *stream, *redirect;
3739 FFStream **last_feed, *feed;
3740 AVCodecContext audio_enc, video_enc;
3741 int audio_id, video_id;
3743 f = fopen(filename, "r");
3751 first_stream = NULL;
3752 last_stream = &first_stream;
3754 last_feed = &first_feed;
3758 audio_id = CODEC_ID_NONE;
3759 video_id = CODEC_ID_NONE;
3761 if (fgets(line, sizeof(line), f) == NULL)
3767 if (*p == '\0' || *p == '#')
3770 get_arg(cmd, sizeof(cmd), &p);
3772 if (!strcasecmp(cmd, "Port")) {
3773 get_arg(arg, sizeof(arg), &p);
3774 my_http_addr.sin_port = htons (atoi(arg));
3775 } else if (!strcasecmp(cmd, "BindAddress")) {
3776 get_arg(arg, sizeof(arg), &p);
3777 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3778 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3779 filename, line_num, arg);
3782 } else if (!strcasecmp(cmd, "NoDaemon")) {
3783 ffserver_daemon = 0;
3784 } else if (!strcasecmp(cmd, "RTSPPort")) {
3785 get_arg(arg, sizeof(arg), &p);
3786 my_rtsp_addr.sin_port = htons (atoi(arg));
3787 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3788 get_arg(arg, sizeof(arg), &p);
3789 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3790 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3791 filename, line_num, arg);
3794 } else if (!strcasecmp(cmd, "MaxClients")) {
3795 get_arg(arg, sizeof(arg), &p);
3797 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3798 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3799 filename, line_num, arg);
3802 nb_max_connections = val;
3804 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3805 get_arg(arg, sizeof(arg), &p);
3807 if (val < 10 || val > 100000) {
3808 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3809 filename, line_num, arg);
3812 max_bandwidth = val;
3814 } else if (!strcasecmp(cmd, "CustomLog")) {
3815 get_arg(logfilename, sizeof(logfilename), &p);
3816 } else if (!strcasecmp(cmd, "<Feed")) {
3817 /*********************************************/
3818 /* Feed related options */
3820 if (stream || feed) {
3821 fprintf(stderr, "%s:%d: Already in a tag\n",
3822 filename, line_num);
3824 feed = av_mallocz(sizeof(FFStream));
3825 /* add in stream list */
3826 *last_stream = feed;
3827 last_stream = &feed->next;
3828 /* add in feed list */
3830 last_feed = &feed->next_feed;
3832 get_arg(feed->filename, sizeof(feed->filename), &p);
3833 q = strrchr(feed->filename, '>');
3836 feed->fmt = guess_format("ffm", NULL, NULL);
3837 /* defaut feed file */
3838 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3839 "/tmp/%s.ffm", feed->filename);
3840 feed->feed_max_size = 5 * 1024 * 1024;
3842 feed->feed = feed; /* self feeding :-) */
3844 } else if (!strcasecmp(cmd, "Launch")) {
3848 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3850 for (i = 0; i < 62; i++) {
3853 get_arg(argbuf, sizeof(argbuf), &p);
3857 feed->child_argv[i] = av_malloc(strlen(argbuf) + 1);
3858 strcpy(feed->child_argv[i], argbuf);
3861 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3863 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3865 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3866 inet_ntoa(my_http_addr.sin_addr),
3867 ntohs(my_http_addr.sin_port), feed->filename);
3872 fprintf(stdout, "Launch commandline: ");
3873 for (j = 0; j <= i; j++)
3874 fprintf(stdout, "%s ", feed->child_argv[j]);
3875 fprintf(stdout, "\n");
3878 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3880 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3882 } else if (stream) {
3883 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3885 } else if (!strcasecmp(cmd, "File")) {
3887 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3888 } else if (stream) {
3889 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3891 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3896 get_arg(arg, sizeof(arg), &p);
3898 fsize = strtod(p1, (char **)&p1);
3899 switch(toupper(*p1)) {
3904 fsize *= 1024 * 1024;
3907 fsize *= 1024 * 1024 * 1024;
3910 feed->feed_max_size = (int64_t)fsize;
3912 } else if (!strcasecmp(cmd, "</Feed>")) {
3914 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3915 filename, line_num);
3919 /* Make sure that we start out clean */
3920 if (unlink(feed->feed_filename) < 0
3921 && errno != ENOENT) {
3922 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3923 filename, line_num, feed->feed_filename, strerror(errno));
3929 } else if (!strcasecmp(cmd, "<Stream")) {
3930 /*********************************************/
3931 /* Stream related options */
3933 if (stream || feed) {
3934 fprintf(stderr, "%s:%d: Already in a tag\n",
3935 filename, line_num);
3937 stream = av_mallocz(sizeof(FFStream));
3938 *last_stream = stream;
3939 last_stream = &stream->next;
3941 get_arg(stream->filename, sizeof(stream->filename), &p);
3942 q = strrchr(stream->filename, '>');
3945 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3946 memset(&audio_enc, 0, sizeof(AVCodecContext));
3947 memset(&video_enc, 0, sizeof(AVCodecContext));
3948 audio_id = CODEC_ID_NONE;
3949 video_id = CODEC_ID_NONE;
3951 audio_id = stream->fmt->audio_codec;
3952 video_id = stream->fmt->video_codec;
3955 } else if (!strcasecmp(cmd, "Feed")) {
3956 get_arg(arg, sizeof(arg), &p);
3961 while (sfeed != NULL) {
3962 if (!strcmp(sfeed->filename, arg))
3964 sfeed = sfeed->next_feed;
3967 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3968 filename, line_num, arg);
3970 stream->feed = sfeed;
3973 } else if (!strcasecmp(cmd, "Format")) {
3974 get_arg(arg, sizeof(arg), &p);
3975 if (!strcmp(arg, "status")) {
3976 stream->stream_type = STREAM_TYPE_STATUS;
3979 stream->stream_type = STREAM_TYPE_LIVE;
3980 /* jpeg cannot be used here, so use single frame jpeg */
3981 if (!strcmp(arg, "jpeg"))
3982 strcpy(arg, "mjpeg");
3983 stream->fmt = guess_stream_format(arg, NULL, NULL);
3985 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3986 filename, line_num, arg);
3991 audio_id = stream->fmt->audio_codec;
3992 video_id = stream->fmt->video_codec;
3994 } else if (!strcasecmp(cmd, "InputFormat")) {
3995 stream->ifmt = av_find_input_format(arg);
3996 if (!stream->ifmt) {
3997 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
3998 filename, line_num, arg);
4000 } else if (!strcasecmp(cmd, "FaviconURL")) {
4001 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4002 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4004 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4005 filename, line_num);
4008 } else if (!strcasecmp(cmd, "Author")) {
4010 get_arg(stream->author, sizeof(stream->author), &p);
4012 } else if (!strcasecmp(cmd, "Comment")) {
4014 get_arg(stream->comment, sizeof(stream->comment), &p);
4016 } else if (!strcasecmp(cmd, "Copyright")) {
4018 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4020 } else if (!strcasecmp(cmd, "Title")) {
4022 get_arg(stream->title, sizeof(stream->title), &p);
4024 } else if (!strcasecmp(cmd, "Preroll")) {
4025 get_arg(arg, sizeof(arg), &p);
4027 stream->prebuffer = atof(arg) * 1000;
4029 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4031 stream->send_on_key = 1;
4033 } else if (!strcasecmp(cmd, "AudioCodec")) {
4034 get_arg(arg, sizeof(arg), &p);
4035 audio_id = opt_audio_codec(arg);
4036 if (audio_id == CODEC_ID_NONE) {
4037 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4038 filename, line_num, arg);
4041 } else if (!strcasecmp(cmd, "VideoCodec")) {
4042 get_arg(arg, sizeof(arg), &p);
4043 video_id = opt_video_codec(arg);
4044 if (video_id == CODEC_ID_NONE) {
4045 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4046 filename, line_num, arg);
4049 } else if (!strcasecmp(cmd, "MaxTime")) {
4050 get_arg(arg, sizeof(arg), &p);
4052 stream->max_time = atof(arg) * 1000;
4054 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4055 get_arg(arg, sizeof(arg), &p);
4057 audio_enc.bit_rate = atoi(arg) * 1000;
4059 } else if (!strcasecmp(cmd, "AudioChannels")) {
4060 get_arg(arg, sizeof(arg), &p);
4062 audio_enc.channels = atoi(arg);
4064 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4065 get_arg(arg, sizeof(arg), &p);
4067 audio_enc.sample_rate = atoi(arg);
4069 } else if (!strcasecmp(cmd, "AudioQuality")) {
4070 get_arg(arg, sizeof(arg), &p);
4072 // audio_enc.quality = atof(arg) * 1000;
4074 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4076 int minrate, maxrate;
4078 get_arg(arg, sizeof(arg), &p);
4080 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4081 video_enc.rc_min_rate = minrate * 1000;
4082 video_enc.rc_max_rate = maxrate * 1000;
4084 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4085 filename, line_num, arg);
4089 } else if (!strcasecmp(cmd, "Debug")) {
4091 get_arg(arg, sizeof(arg), &p);
4092 video_enc.debug = strtol(arg,0,0);
4094 } else if (!strcasecmp(cmd, "Strict")) {
4096 get_arg(arg, sizeof(arg), &p);
4097 video_enc.strict_std_compliance = atoi(arg);
4099 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4101 get_arg(arg, sizeof(arg), &p);
4102 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4104 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4106 get_arg(arg, sizeof(arg), &p);
4107 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4109 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4110 get_arg(arg, sizeof(arg), &p);
4112 video_enc.bit_rate = atoi(arg) * 1000;
4114 } else if (!strcasecmp(cmd, "VideoSize")) {
4115 get_arg(arg, sizeof(arg), &p);
4117 parse_image_size(&video_enc.width, &video_enc.height, arg);
4118 if ((video_enc.width % 16) != 0 ||
4119 (video_enc.height % 16) != 0) {
4120 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4121 filename, line_num);
4125 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4126 get_arg(arg, sizeof(arg), &p);
4128 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4129 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4131 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4132 get_arg(arg, sizeof(arg), &p);
4134 video_enc.gop_size = atoi(arg);
4136 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4138 video_enc.gop_size = 1;
4140 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4142 video_enc.mb_decision = FF_MB_DECISION_BITS;
4144 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4146 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4147 video_enc.flags |= CODEC_FLAG_4MV;
4149 } else if (!strcasecmp(cmd, "BitExact")) {
4151 video_enc.flags |= CODEC_FLAG_BITEXACT;
4153 } else if (!strcasecmp(cmd, "DctFastint")) {
4155 video_enc.dct_algo = FF_DCT_FASTINT;
4157 } else if (!strcasecmp(cmd, "IdctSimple")) {
4159 video_enc.idct_algo = FF_IDCT_SIMPLE;
4161 } else if (!strcasecmp(cmd, "Qscale")) {
4162 get_arg(arg, sizeof(arg), &p);
4164 video_enc.flags |= CODEC_FLAG_QSCALE;
4165 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4167 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4168 get_arg(arg, sizeof(arg), &p);
4170 video_enc.max_qdiff = atoi(arg);
4171 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4172 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4173 filename, line_num);
4177 } else if (!strcasecmp(cmd, "VideoQMax")) {
4178 get_arg(arg, sizeof(arg), &p);
4180 video_enc.qmax = atoi(arg);
4181 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4182 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4183 filename, line_num);
4187 } else if (!strcasecmp(cmd, "VideoQMin")) {
4188 get_arg(arg, sizeof(arg), &p);
4190 video_enc.qmin = atoi(arg);
4191 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4192 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4193 filename, line_num);
4197 } else if (!strcasecmp(cmd, "LumaElim")) {
4198 get_arg(arg, sizeof(arg), &p);
4200 video_enc.luma_elim_threshold = atoi(arg);
4202 } else if (!strcasecmp(cmd, "ChromaElim")) {
4203 get_arg(arg, sizeof(arg), &p);
4205 video_enc.chroma_elim_threshold = atoi(arg);
4207 } else if (!strcasecmp(cmd, "LumiMask")) {
4208 get_arg(arg, sizeof(arg), &p);
4210 video_enc.lumi_masking = atof(arg);
4212 } else if (!strcasecmp(cmd, "DarkMask")) {
4213 get_arg(arg, sizeof(arg), &p);
4215 video_enc.dark_masking = atof(arg);
4217 } else if (!strcasecmp(cmd, "NoVideo")) {
4218 video_id = CODEC_ID_NONE;
4219 } else if (!strcasecmp(cmd, "NoAudio")) {
4220 audio_id = CODEC_ID_NONE;
4221 } else if (!strcasecmp(cmd, "ACL")) {
4225 get_arg(arg, sizeof(arg), &p);
4226 if (strcasecmp(arg, "allow") == 0) {
4227 acl.action = IP_ALLOW;
4228 } else if (strcasecmp(arg, "deny") == 0) {
4229 acl.action = IP_DENY;
4231 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4232 filename, line_num, arg);
4236 get_arg(arg, sizeof(arg), &p);
4238 he = gethostbyname(arg);
4240 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4241 filename, line_num, arg);
4244 /* Only take the first */
4245 acl.first.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4246 acl.last = acl.first;
4249 get_arg(arg, sizeof(arg), &p);
4252 he = gethostbyname(arg);
4254 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4255 filename, line_num, arg);
4258 /* Only take the first */
4259 acl.last.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4264 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4265 IPAddressACL **naclp = 0;
4271 naclp = &stream->acl;
4275 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4276 filename, line_num);
4282 naclp = &(*naclp)->next;
4287 } else if (!strcasecmp(cmd, "RTSPOption")) {
4288 get_arg(arg, sizeof(arg), &p);
4290 av_freep(&stream->rtsp_option);
4291 /* XXX: av_strdup ? */
4292 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4293 if (stream->rtsp_option) {
4294 strcpy(stream->rtsp_option, arg);
4297 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4298 get_arg(arg, sizeof(arg), &p);
4300 if (!inet_aton(arg, &stream->multicast_ip)) {
4301 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4302 filename, line_num, arg);
4305 stream->is_multicast = 1;
4306 stream->loop = 1; /* default is looping */
4308 } else if (!strcasecmp(cmd, "MulticastPort")) {
4309 get_arg(arg, sizeof(arg), &p);
4311 stream->multicast_port = atoi(arg);
4313 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4314 get_arg(arg, sizeof(arg), &p);
4316 stream->multicast_ttl = atoi(arg);
4318 } else if (!strcasecmp(cmd, "NoLoop")) {
4322 } else if (!strcasecmp(cmd, "</Stream>")) {
4324 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4325 filename, line_num);
4328 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4329 if (audio_id != CODEC_ID_NONE) {
4330 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4331 audio_enc.codec_id = audio_id;
4332 add_codec(stream, &audio_enc);
4334 if (video_id != CODEC_ID_NONE) {
4335 video_enc.codec_type = CODEC_TYPE_VIDEO;
4336 video_enc.codec_id = video_id;
4337 add_codec(stream, &video_enc);
4341 } else if (!strcasecmp(cmd, "<Redirect")) {
4342 /*********************************************/
4344 if (stream || feed || redirect) {
4345 fprintf(stderr, "%s:%d: Already in a tag\n",
4346 filename, line_num);
4349 redirect = av_mallocz(sizeof(FFStream));
4350 *last_stream = redirect;
4351 last_stream = &redirect->next;
4353 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4354 q = strrchr(redirect->filename, '>');
4357 redirect->stream_type = STREAM_TYPE_REDIRECT;
4359 } else if (!strcasecmp(cmd, "URL")) {
4361 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4363 } else if (!strcasecmp(cmd, "</Redirect>")) {
4365 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4366 filename, line_num);
4369 if (!redirect->feed_filename[0]) {
4370 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4371 filename, line_num);
4375 } else if (!strcasecmp(cmd, "LoadModule")) {
4376 get_arg(arg, sizeof(arg), &p);
4377 #ifdef CONFIG_HAVE_DLOPEN
4380 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4381 filename, line_num, arg);
4385 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4386 filename, line_num, cmd);
4400 static void write_packet(FFCodec *ffenc,
4401 uint8_t *buf, int size)
4404 AVCodecContext *enc = &ffenc->enc;
4406 mk_header(&hdr, enc, size);
4407 wptr = http_fifo.wptr;
4408 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4409 fifo_write(&http_fifo, buf, size, &wptr);
4410 /* atomic modification of wptr */
4411 http_fifo.wptr = wptr;
4412 ffenc->data_count += size;
4413 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4417 static void show_banner(void)
4419 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2003 Fabrice Bellard\n");
4422 static void show_help(void)
4425 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4426 "Hyper fast multi format Audio/Video streaming server\n"
4428 "-L : print the LICENSE\n"
4430 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4434 static void show_license(void)
4438 "This library is free software; you can redistribute it and/or\n"
4439 "modify it under the terms of the GNU Lesser General Public\n"
4440 "License as published by the Free Software Foundation; either\n"
4441 "version 2 of the License, or (at your option) any later version.\n"
4443 "This library is distributed in the hope that it will be useful,\n"
4444 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4445 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4446 "Lesser General Public License for more details.\n"
4448 "You should have received a copy of the GNU Lesser General Public\n"
4449 "License along with this library; if not, write to the Free Software\n"
4450 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
4454 static void handle_child_exit(int sig)
4459 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4462 for (feed = first_feed; feed; feed = feed->next) {
4463 if (feed->pid == pid) {
4464 int uptime = time(0) - feed->pid_start;
4467 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4470 /* Turn off any more restarts */
4471 feed->child_argv = 0;
4477 need_to_start_children = 1;
4480 int main(int argc, char **argv)
4482 const char *config_filename;
4484 struct sigaction sigact;
4488 config_filename = "/etc/ffserver.conf";
4490 my_program_name = argv[0];
4491 my_program_dir = getcwd(0, 0);
4492 ffserver_daemon = 1;
4495 c = getopt(argc, argv, "ndLh?f:");
4511 ffserver_daemon = 0;
4514 config_filename = optarg;
4521 putenv("http_proxy"); /* Kill the http_proxy */
4523 srandom(gettime_ms() + (getpid() << 16));
4525 /* address on which the server will handle HTTP connections */
4526 my_http_addr.sin_family = AF_INET;
4527 my_http_addr.sin_port = htons (8080);
4528 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4530 /* address on which the server will handle RTSP connections */
4531 my_rtsp_addr.sin_family = AF_INET;
4532 my_rtsp_addr.sin_port = htons (5454);
4533 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4535 nb_max_connections = 5;
4536 max_bandwidth = 1000;
4537 first_stream = NULL;
4538 logfilename[0] = '\0';
4540 memset(&sigact, 0, sizeof(sigact));
4541 sigact.sa_handler = handle_child_exit;
4542 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4543 sigaction(SIGCHLD, &sigact, 0);
4545 if (parse_ffconfig(config_filename) < 0) {
4546 fprintf(stderr, "Incorrect config file - exiting.\n");
4550 build_file_streams();
4552 build_feed_streams();
4554 compute_bandwidth();
4556 /* put the process in background and detach it from its TTY */
4557 if (ffserver_daemon) {
4564 } else if (pid > 0) {
4572 open("/dev/null", O_RDWR);
4573 if (strcmp(logfilename, "-") != 0) {
4583 signal(SIGPIPE, SIG_IGN);
4585 /* open log file if needed */
4586 if (logfilename[0] != '\0') {
4587 if (!strcmp(logfilename, "-"))
4590 logfile = fopen(logfilename, "w");
4593 if (http_server() < 0) {
4594 fprintf(stderr, "Could not start server\n");