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 char logfilename[1024];
236 HTTPContext *first_http_ctx;
237 FFStream *first_feed; /* contains only feeds */
238 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 int nb_max_connections;
285 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);
362 static int get_longterm_datarate(DataRateData *drd, int64_t count)
364 /* You get the first 3 seconds flat out */
365 if (cur_time - drd->time1 < 3000)
367 return compute_datarate(drd, count);
371 static void start_children(FFStream *feed)
376 for (; feed; feed = feed->next) {
377 if (feed->child_argv && !feed->pid) {
378 feed->pid_start = time(0);
383 fprintf(stderr, "Unable to create children\n");
392 for (i = 3; i < 256; i++) {
396 if (!ffserver_debug) {
397 i = open("/dev/null", O_RDWR);
406 pstrcpy(pathname, sizeof(pathname), my_program_name);
408 slash = strrchr(pathname, '/');
414 strcpy(slash, "ffmpeg");
416 /* This is needed to make relative pathnames work */
417 chdir(my_program_dir);
419 signal(SIGPIPE, SIG_DFL);
421 execvp(pathname, feed->child_argv);
429 /* open a listening socket */
430 static int socket_open_listen(struct sockaddr_in *my_addr)
434 server_fd = socket(AF_INET,SOCK_STREAM,0);
441 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
443 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
445 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
451 if (listen (server_fd, 5) < 0) {
456 fcntl(server_fd, F_SETFL, O_NONBLOCK);
461 /* start all multicast streams */
462 static void start_multicast(void)
467 struct sockaddr_in dest_addr;
468 int default_port, stream_index;
471 for(stream = first_stream; stream != NULL; stream = stream->next) {
472 if (stream->is_multicast) {
473 /* open the RTP connection */
474 snprintf(session_id, sizeof(session_id),
475 "%08x%08x", (int)random(), (int)random());
477 /* choose a port if none given */
478 if (stream->multicast_port == 0) {
479 stream->multicast_port = default_port;
483 dest_addr.sin_family = AF_INET;
484 dest_addr.sin_addr = stream->multicast_ip;
485 dest_addr.sin_port = htons(stream->multicast_port);
487 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
488 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
492 if (open_input_stream(rtp_c, "") < 0) {
493 fprintf(stderr, "Could not open input stream for stream '%s'\n",
498 /* open each RTP stream */
499 for(stream_index = 0; stream_index < stream->nb_streams;
501 dest_addr.sin_port = htons(stream->multicast_port +
503 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
504 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
505 stream->filename, stream_index);
510 /* change state to send data */
511 rtp_c->state = HTTPSTATE_SEND_DATA;
516 /* main loop of the http server */
517 static int http_server(void)
519 int server_fd, ret, rtsp_server_fd, delay, delay1;
520 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
521 HTTPContext *c, *c_next;
523 server_fd = socket_open_listen(&my_http_addr);
527 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
528 if (rtsp_server_fd < 0)
531 http_log("ffserver started.\n");
533 start_children(first_feed);
535 first_http_ctx = NULL;
541 poll_entry = poll_table;
542 poll_entry->fd = server_fd;
543 poll_entry->events = POLLIN;
546 poll_entry->fd = rtsp_server_fd;
547 poll_entry->events = POLLIN;
550 /* wait for events on each HTTP handle */
557 case HTTPSTATE_SEND_HEADER:
558 case RTSPSTATE_SEND_REPLY:
559 case RTSPSTATE_SEND_PACKET:
560 c->poll_entry = poll_entry;
562 poll_entry->events = POLLOUT;
565 case HTTPSTATE_SEND_DATA_HEADER:
566 case HTTPSTATE_SEND_DATA:
567 case HTTPSTATE_SEND_DATA_TRAILER:
568 if (!c->is_packetized) {
569 /* for TCP, we output as much as we can (may need to put a limit) */
570 c->poll_entry = poll_entry;
572 poll_entry->events = POLLOUT;
575 /* when ffserver is doing the timing, we work by
576 looking at which packet need to be sent every
578 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
583 case HTTPSTATE_WAIT_REQUEST:
584 case HTTPSTATE_RECEIVE_DATA:
585 case HTTPSTATE_WAIT_FEED:
586 case RTSPSTATE_WAIT_REQUEST:
587 /* need to catch errors */
588 c->poll_entry = poll_entry;
590 poll_entry->events = POLLIN;/* Maybe this will work */
594 c->poll_entry = NULL;
600 /* wait for an event on one connection. We poll at least every
601 second to handle timeouts */
603 ret = poll(poll_table, poll_entry - poll_table, delay);
604 if (ret < 0 && errno != EAGAIN && errno != EINTR)
608 cur_time = gettime_ms();
610 if (need_to_start_children) {
611 need_to_start_children = 0;
612 start_children(first_feed);
615 /* now handle the events */
616 for(c = first_http_ctx; c != NULL; c = c_next) {
618 if (handle_connection(c) < 0) {
619 /* close and free the connection */
625 poll_entry = poll_table;
626 /* new HTTP connection request ? */
627 if (poll_entry->revents & POLLIN) {
628 new_connection(server_fd, 0);
631 /* new RTSP connection request ? */
632 if (poll_entry->revents & POLLIN) {
633 new_connection(rtsp_server_fd, 1);
638 /* start waiting for a new HTTP/RTSP request */
639 static void start_wait_request(HTTPContext *c, int is_rtsp)
641 c->buffer_ptr = c->buffer;
642 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
645 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
646 c->state = RTSPSTATE_WAIT_REQUEST;
648 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
649 c->state = HTTPSTATE_WAIT_REQUEST;
653 static void new_connection(int server_fd, int is_rtsp)
655 struct sockaddr_in from_addr;
657 HTTPContext *c = NULL;
659 len = sizeof(from_addr);
660 fd = accept(server_fd, (struct sockaddr *)&from_addr,
664 fcntl(fd, F_SETFL, O_NONBLOCK);
666 /* XXX: should output a warning page when coming
667 close to the connection limit */
668 if (nb_connections >= nb_max_connections)
671 /* add a new connection */
672 c = av_mallocz(sizeof(HTTPContext));
677 c->poll_entry = NULL;
678 c->from_addr = from_addr;
679 c->buffer_size = IOBUFFER_INIT_SIZE;
680 c->buffer = av_malloc(c->buffer_size);
684 c->next = first_http_ctx;
688 start_wait_request(c, is_rtsp);
700 static void close_connection(HTTPContext *c)
702 HTTPContext **cp, *c1;
704 AVFormatContext *ctx;
708 /* remove connection from list */
709 cp = &first_http_ctx;
710 while ((*cp) != NULL) {
719 /* remove references, if any (XXX: do it faster) */
720 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
725 /* remove connection associated resources */
729 /* close each frame parser */
730 for(i=0;i<c->fmt_in->nb_streams;i++) {
731 st = c->fmt_in->streams[i];
732 if (st->codec->codec) {
733 avcodec_close(st->codec);
736 av_close_input_file(c->fmt_in);
739 /* free RTP output streams if any */
742 nb_streams = c->stream->nb_streams;
744 for(i=0;i<nb_streams;i++) {
747 av_write_trailer(ctx);
750 h = c->rtp_handles[i];
758 if (!c->last_packet_sent) {
761 if (url_open_dyn_buf(&ctx->pb) >= 0) {
762 av_write_trailer(ctx);
763 url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
768 for(i=0; i<ctx->nb_streams; i++)
769 av_free(ctx->streams[i]) ;
771 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
772 current_bandwidth -= c->stream->bandwidth;
773 av_freep(&c->pb_buffer);
774 av_freep(&c->packet_buffer);
780 static int handle_connection(HTTPContext *c)
785 case HTTPSTATE_WAIT_REQUEST:
786 case RTSPSTATE_WAIT_REQUEST:
788 if ((c->timeout - cur_time) < 0)
790 if (c->poll_entry->revents & (POLLERR | POLLHUP))
793 /* no need to read if no events */
794 if (!(c->poll_entry->revents & POLLIN))
798 len = read(c->fd, c->buffer_ptr, 1);
800 if (errno != EAGAIN && errno != EINTR)
802 } else if (len == 0) {
805 /* search for end of request. */
807 c->buffer_ptr += len;
809 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
810 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
811 /* request found : parse it and reply */
812 if (c->state == HTTPSTATE_WAIT_REQUEST) {
813 ret = http_parse_request(c);
815 ret = rtsp_parse_request(c);
819 } else if (ptr >= c->buffer_end) {
820 /* request too long: cannot do anything */
822 } else goto read_loop;
826 case HTTPSTATE_SEND_HEADER:
827 if (c->poll_entry->revents & (POLLERR | POLLHUP))
830 /* no need to write if no events */
831 if (!(c->poll_entry->revents & POLLOUT))
833 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
835 if (errno != EAGAIN && errno != EINTR) {
836 /* error : close connection */
837 av_freep(&c->pb_buffer);
841 c->buffer_ptr += len;
843 c->stream->bytes_served += len;
844 c->data_count += len;
845 if (c->buffer_ptr >= c->buffer_end) {
846 av_freep(&c->pb_buffer);
851 /* all the buffer was sent : synchronize to the incoming stream */
852 c->state = HTTPSTATE_SEND_DATA_HEADER;
853 c->buffer_ptr = c->buffer_end = c->buffer;
858 case HTTPSTATE_SEND_DATA:
859 case HTTPSTATE_SEND_DATA_HEADER:
860 case HTTPSTATE_SEND_DATA_TRAILER:
861 /* for packetized output, we consider we can always write (the
862 input streams sets the speed). It may be better to verify
863 that we do not rely too much on the kernel queues */
864 if (!c->is_packetized) {
865 if (c->poll_entry->revents & (POLLERR | POLLHUP))
868 /* no need to read if no events */
869 if (!(c->poll_entry->revents & POLLOUT))
872 if (http_send_data(c) < 0)
875 case HTTPSTATE_RECEIVE_DATA:
876 /* no need to read if no events */
877 if (c->poll_entry->revents & (POLLERR | POLLHUP))
879 if (!(c->poll_entry->revents & POLLIN))
881 if (http_receive_data(c) < 0)
884 case HTTPSTATE_WAIT_FEED:
885 /* no need to read if no events */
886 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
889 /* nothing to do, we'll be waken up by incoming feed packets */
892 case RTSPSTATE_SEND_REPLY:
893 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
894 av_freep(&c->pb_buffer);
897 /* no need to write if no events */
898 if (!(c->poll_entry->revents & POLLOUT))
900 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
902 if (errno != EAGAIN && errno != EINTR) {
903 /* error : close connection */
904 av_freep(&c->pb_buffer);
908 c->buffer_ptr += len;
909 c->data_count += len;
910 if (c->buffer_ptr >= c->buffer_end) {
911 /* all the buffer was sent : wait for a new request */
912 av_freep(&c->pb_buffer);
913 start_wait_request(c, 1);
917 case RTSPSTATE_SEND_PACKET:
918 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
919 av_freep(&c->packet_buffer);
922 /* no need to write if no events */
923 if (!(c->poll_entry->revents & POLLOUT))
925 len = write(c->fd, c->packet_buffer_ptr,
926 c->packet_buffer_end - c->packet_buffer_ptr);
928 if (errno != EAGAIN && errno != EINTR) {
929 /* error : close connection */
930 av_freep(&c->packet_buffer);
934 c->packet_buffer_ptr += len;
935 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
936 /* all the buffer was sent : wait for a new request */
937 av_freep(&c->packet_buffer);
938 c->state = RTSPSTATE_WAIT_REQUEST;
942 case HTTPSTATE_READY:
951 static int extract_rates(char *rates, int ratelen, const char *request)
955 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
956 if (strncasecmp(p, "Pragma:", 7) == 0) {
957 const char *q = p + 7;
959 while (*q && *q != '\n' && isspace(*q))
962 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
968 memset(rates, 0xff, ratelen);
971 while (*q && *q != '\n' && *q != ':')
974 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
978 if (stream_no < ratelen && stream_no >= 0) {
979 rates[stream_no] = rate_no;
982 while (*q && *q != '\n' && !isspace(*q))
999 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1002 int best_bitrate = 100000000;
1005 for (i = 0; i < feed->nb_streams; i++) {
1006 AVCodecContext *feed_codec = feed->streams[i]->codec;
1008 if (feed_codec->codec_id != codec->codec_id ||
1009 feed_codec->sample_rate != codec->sample_rate ||
1010 feed_codec->width != codec->width ||
1011 feed_codec->height != codec->height) {
1015 /* Potential stream */
1017 /* We want the fastest stream less than bit_rate, or the slowest
1018 * faster than bit_rate
1021 if (feed_codec->bit_rate <= bit_rate) {
1022 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1023 best_bitrate = feed_codec->bit_rate;
1027 if (feed_codec->bit_rate < best_bitrate) {
1028 best_bitrate = feed_codec->bit_rate;
1037 static int modify_current_stream(HTTPContext *c, char *rates)
1040 FFStream *req = c->stream;
1041 int action_required = 0;
1043 /* Not much we can do for a feed */
1047 for (i = 0; i < req->nb_streams; i++) {
1048 AVCodecContext *codec = req->streams[i]->codec;
1052 c->switch_feed_streams[i] = req->feed_streams[i];
1055 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1058 /* Wants off or slow */
1059 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1061 /* This doesn't work well when it turns off the only stream! */
1062 c->switch_feed_streams[i] = -2;
1063 c->feed_streams[i] = -2;
1068 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1069 action_required = 1;
1072 return action_required;
1076 static void do_switch_stream(HTTPContext *c, int i)
1078 if (c->switch_feed_streams[i] >= 0) {
1080 c->feed_streams[i] = c->switch_feed_streams[i];
1083 /* Now update the stream */
1085 c->switch_feed_streams[i] = -1;
1088 /* XXX: factorize in utils.c ? */
1089 /* XXX: take care with different space meaning */
1090 static void skip_spaces(const char **pp)
1094 while (*p == ' ' || *p == '\t')
1099 static void get_word(char *buf, int buf_size, const char **pp)
1107 while (!isspace(*p) && *p != '\0') {
1108 if ((q - buf) < buf_size - 1)
1117 static int validate_acl(FFStream *stream, HTTPContext *c)
1119 enum IPAddressAction last_action = IP_DENY;
1121 struct in_addr *src = &c->from_addr.sin_addr;
1122 unsigned long src_addr = ntohl(src->s_addr);
1124 for (acl = stream->acl; acl; acl = acl->next) {
1125 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1126 return (acl->action == IP_ALLOW) ? 1 : 0;
1128 last_action = acl->action;
1131 /* Nothing matched, so return not the last action */
1132 return (last_action == IP_DENY) ? 1 : 0;
1135 /* compute the real filename of a file by matching it without its
1136 extensions to all the stream filenames */
1137 static void compute_real_filename(char *filename, int max_size)
1144 /* compute filename by matching without the file extensions */
1145 pstrcpy(file1, sizeof(file1), filename);
1146 p = strrchr(file1, '.');
1149 for(stream = first_stream; stream != NULL; stream = stream->next) {
1150 pstrcpy(file2, sizeof(file2), stream->filename);
1151 p = strrchr(file2, '.');
1154 if (!strcmp(file1, file2)) {
1155 pstrcpy(filename, max_size, stream->filename);
1170 /* parse http request and prepare header */
1171 static int http_parse_request(HTTPContext *c)
1174 enum RedirType redir_type;
1176 char info[1024], *filename;
1180 const char *mime_type;
1184 char *useragent = 0;
1187 get_word(cmd, sizeof(cmd), (const char **)&p);
1188 pstrcpy(c->method, sizeof(c->method), cmd);
1190 if (!strcmp(cmd, "GET"))
1192 else if (!strcmp(cmd, "POST"))
1197 get_word(url, sizeof(url), (const char **)&p);
1198 pstrcpy(c->url, sizeof(c->url), url);
1200 get_word(protocol, sizeof(protocol), (const char **)&p);
1201 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1204 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1207 http_log("New connection: %s %s\n", cmd, url);
1209 /* find the filename and the optional info string in the request */
1216 pstrcpy(info, sizeof(info), p);
1222 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1223 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1225 if (*useragent && *useragent != '\n' && isspace(*useragent))
1229 p = strchr(p, '\n');
1236 redir_type = REDIR_NONE;
1237 if (match_ext(filename, "asx")) {
1238 redir_type = REDIR_ASX;
1239 filename[strlen(filename)-1] = 'f';
1240 } else if (match_ext(filename, "asf") &&
1241 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1242 /* if this isn't WMP or lookalike, return the redirector file */
1243 redir_type = REDIR_ASF;
1244 } else if (match_ext(filename, "rpm,ram")) {
1245 redir_type = REDIR_RAM;
1246 strcpy(filename + strlen(filename)-2, "m");
1247 } else if (match_ext(filename, "rtsp")) {
1248 redir_type = REDIR_RTSP;
1249 compute_real_filename(filename, sizeof(url) - 1);
1250 } else if (match_ext(filename, "sdp")) {
1251 redir_type = REDIR_SDP;
1252 compute_real_filename(filename, sizeof(url) - 1);
1255 stream = first_stream;
1256 while (stream != NULL) {
1257 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1259 stream = stream->next;
1261 if (stream == NULL) {
1262 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1267 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1268 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1270 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1271 c->http_error = 301;
1273 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1274 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1275 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1276 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1277 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1278 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1279 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1281 /* prepare output buffer */
1282 c->buffer_ptr = c->buffer;
1284 c->state = HTTPSTATE_SEND_HEADER;
1288 /* If this is WMP, get the rate information */
1289 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1290 if (modify_current_stream(c, ratebuf)) {
1291 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1292 if (c->switch_feed_streams[i] >= 0)
1293 do_switch_stream(c, i);
1298 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1299 current_bandwidth += stream->bandwidth;
1302 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1303 c->http_error = 200;
1305 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1306 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1307 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1308 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1309 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "The server is too busy to serve your request at this time.<p>\r\n");
1310 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1311 current_bandwidth, max_bandwidth);
1312 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1314 /* prepare output buffer */
1315 c->buffer_ptr = c->buffer;
1317 c->state = HTTPSTATE_SEND_HEADER;
1321 if (redir_type != REDIR_NONE) {
1324 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1325 if (strncasecmp(p, "Host:", 5) == 0) {
1329 p = strchr(p, '\n');
1340 while (isspace(*hostinfo))
1343 eoh = strchr(hostinfo, '\n');
1345 if (eoh[-1] == '\r')
1348 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1349 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1350 hostbuf[eoh - hostinfo] = 0;
1352 c->http_error = 200;
1354 switch(redir_type) {
1356 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1357 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1358 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1359 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1360 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1362 hostbuf, filename, info);
1363 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1366 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1367 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\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, "# Autogenerated by ffserver\r\n");
1370 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1371 hostbuf, filename, info);
1374 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1375 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1376 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1377 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1378 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1379 hostbuf, filename, info);
1383 char hostname[256], *p;
1384 /* extract only hostname */
1385 pstrcpy(hostname, sizeof(hostname), hostbuf);
1386 p = strrchr(hostname, ':');
1389 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1390 /* XXX: incorrect mime type ? */
1391 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1392 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1393 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1394 hostname, ntohs(my_rtsp_addr.sin_port),
1401 int sdp_data_size, len;
1402 struct sockaddr_in my_addr;
1404 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1405 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1408 len = sizeof(my_addr);
1409 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1411 /* XXX: should use a dynamic buffer */
1412 sdp_data_size = prepare_sdp_description(stream,
1415 if (sdp_data_size > 0) {
1416 memcpy(q, sdp_data, sdp_data_size);
1428 /* prepare output buffer */
1429 c->buffer_ptr = c->buffer;
1431 c->state = HTTPSTATE_SEND_HEADER;
1437 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1441 stream->conns_served++;
1443 /* XXX: add there authenticate and IP match */
1446 /* if post, it means a feed is being sent */
1447 if (!stream->is_feed) {
1448 /* However it might be a status report from WMP! Lets log the data
1449 * as it might come in handy one day
1454 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1455 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1459 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1460 client_id = strtol(p + 18, 0, 10);
1462 p = strchr(p, '\n');
1470 char *eol = strchr(logline, '\n');
1475 if (eol[-1] == '\r')
1477 http_log("%.*s\n", (int) (eol - logline), logline);
1478 c->suppress_log = 1;
1483 http_log("\nGot request:\n%s\n", c->buffer);
1486 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1489 /* Now we have to find the client_id */
1490 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1491 if (wmpc->wmp_client_id == client_id)
1496 if (modify_current_stream(wmpc, ratebuf)) {
1497 wmpc->switch_pending = 1;
1502 snprintf(msg, sizeof(msg), "POST command not handled");
1506 if (http_start_receive_data(c) < 0) {
1507 snprintf(msg, sizeof(msg), "could not open feed");
1511 c->state = HTTPSTATE_RECEIVE_DATA;
1516 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1517 http_log("\nGot request:\n%s\n", c->buffer);
1521 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1524 /* open input stream */
1525 if (open_input_stream(c, info) < 0) {
1526 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1530 /* prepare http header */
1532 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1533 mime_type = c->stream->fmt->mime_type;
1535 mime_type = "application/x-octet_stream";
1536 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1538 /* for asf, we need extra headers */
1539 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1540 /* Need to allocate a client id */
1542 c->wmp_client_id = random() & 0x7fffffff;
1544 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);
1546 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1547 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1549 /* prepare output buffer */
1551 c->buffer_ptr = c->buffer;
1553 c->state = HTTPSTATE_SEND_HEADER;
1556 c->http_error = 404;
1558 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1559 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1560 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1561 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1562 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1563 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1564 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1566 /* prepare output buffer */
1567 c->buffer_ptr = c->buffer;
1569 c->state = HTTPSTATE_SEND_HEADER;
1573 c->http_error = 200; /* horrible : we use this value to avoid
1574 going to the send data state */
1575 c->state = HTTPSTATE_SEND_HEADER;
1579 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1581 static const char *suffix = " kMGTP";
1584 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1587 url_fprintf(pb, "%"PRId64"%c", count, *s);
1590 static void compute_stats(HTTPContext *c)
1597 ByteIOContext pb1, *pb = &pb1;
1599 if (url_open_dyn_buf(pb) < 0) {
1600 /* XXX: return an error ? */
1601 c->buffer_ptr = c->buffer;
1602 c->buffer_end = c->buffer;
1606 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1607 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1608 url_fprintf(pb, "Pragma: no-cache\r\n");
1609 url_fprintf(pb, "\r\n");
1611 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1612 if (c->stream->feed_filename) {
1613 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1615 url_fprintf(pb, "</HEAD>\n<BODY>");
1616 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1618 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1619 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1620 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");
1621 stream = first_stream;
1622 while (stream != NULL) {
1623 char sfilename[1024];
1626 if (stream->feed != stream) {
1627 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1628 eosf = sfilename + strlen(sfilename);
1629 if (eosf - sfilename >= 4) {
1630 if (strcmp(eosf - 4, ".asf") == 0) {
1631 strcpy(eosf - 4, ".asx");
1632 } else if (strcmp(eosf - 3, ".rm") == 0) {
1633 strcpy(eosf - 3, ".ram");
1634 } else if (stream->fmt == &rtp_mux) {
1635 /* generate a sample RTSP director if
1636 unicast. Generate an SDP redirector if
1638 eosf = strrchr(sfilename, '.');
1640 eosf = sfilename + strlen(sfilename);
1641 if (stream->is_multicast)
1642 strcpy(eosf, ".sdp");
1644 strcpy(eosf, ".rtsp");
1648 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1649 sfilename, stream->filename);
1650 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1651 stream->conns_served);
1652 fmt_bytecount(pb, stream->bytes_served);
1653 switch(stream->stream_type) {
1654 case STREAM_TYPE_LIVE:
1656 int audio_bit_rate = 0;
1657 int video_bit_rate = 0;
1658 const char *audio_codec_name = "";
1659 const char *video_codec_name = "";
1660 const char *audio_codec_name_extra = "";
1661 const char *video_codec_name_extra = "";
1663 for(i=0;i<stream->nb_streams;i++) {
1664 AVStream *st = stream->streams[i];
1665 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1666 switch(st->codec->codec_type) {
1667 case CODEC_TYPE_AUDIO:
1668 audio_bit_rate += st->codec->bit_rate;
1670 if (*audio_codec_name)
1671 audio_codec_name_extra = "...";
1672 audio_codec_name = codec->name;
1675 case CODEC_TYPE_VIDEO:
1676 video_bit_rate += st->codec->bit_rate;
1678 if (*video_codec_name)
1679 video_codec_name_extra = "...";
1680 video_codec_name = codec->name;
1683 case CODEC_TYPE_DATA:
1684 video_bit_rate += st->codec->bit_rate;
1690 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",
1693 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1694 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1696 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1698 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1700 url_fprintf(pb, "\n");
1704 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1708 stream = stream->next;
1710 url_fprintf(pb, "</TABLE>\n");
1712 stream = first_stream;
1713 while (stream != NULL) {
1714 if (stream->feed == stream) {
1715 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1717 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1719 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1724 /* This is somewhat linux specific I guess */
1725 snprintf(ps_cmd, sizeof(ps_cmd),
1726 "ps -o \"%%cpu,cputime\" --no-headers %d",
1729 pid_stat = popen(ps_cmd, "r");
1734 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1736 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1744 url_fprintf(pb, "<p>");
1746 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");
1748 for (i = 0; i < stream->nb_streams; i++) {
1749 AVStream *st = stream->streams[i];
1750 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1751 const char *type = "unknown";
1752 char parameters[64];
1756 switch(st->codec->codec_type) {
1757 case CODEC_TYPE_AUDIO:
1760 case CODEC_TYPE_VIDEO:
1762 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1763 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1768 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1769 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1771 url_fprintf(pb, "</table>\n");
1774 stream = stream->next;
1780 AVCodecContext *enc;
1784 stream = first_feed;
1785 while (stream != NULL) {
1786 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1787 url_fprintf(pb, "<TABLE>\n");
1788 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1789 for(i=0;i<stream->nb_streams;i++) {
1790 AVStream *st = stream->streams[i];
1791 FeedData *fdata = st->priv_data;
1794 avcodec_string(buf, sizeof(buf), enc);
1795 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1796 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1797 avg /= enc->frame_size;
1798 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1799 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1801 url_fprintf(pb, "</TABLE>\n");
1802 stream = stream->next_feed;
1807 /* connection status */
1808 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1810 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1811 nb_connections, nb_max_connections);
1813 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1814 current_bandwidth, max_bandwidth);
1816 url_fprintf(pb, "<TABLE>\n");
1817 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");
1818 c1 = first_http_ctx;
1820 while (c1 != NULL) {
1826 for (j = 0; j < c1->stream->nb_streams; j++) {
1827 if (!c1->stream->feed) {
1828 bitrate += c1->stream->streams[j]->codec->bit_rate;
1830 if (c1->feed_streams[j] >= 0) {
1831 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1838 p = inet_ntoa(c1->from_addr.sin_addr);
1839 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1841 c1->stream ? c1->stream->filename : "",
1842 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1845 http_state[c1->state]);
1846 fmt_bytecount(pb, bitrate);
1847 url_fprintf(pb, "<td align=right>");
1848 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1849 url_fprintf(pb, "<td align=right>");
1850 fmt_bytecount(pb, c1->data_count);
1851 url_fprintf(pb, "\n");
1854 url_fprintf(pb, "</TABLE>\n");
1859 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1860 url_fprintf(pb, "</BODY>\n</HTML>\n");
1862 len = url_close_dyn_buf(pb, &c->pb_buffer);
1863 c->buffer_ptr = c->pb_buffer;
1864 c->buffer_end = c->pb_buffer + len;
1867 /* check if the parser needs to be opened for stream i */
1868 static void open_parser(AVFormatContext *s, int i)
1870 AVStream *st = s->streams[i];
1873 if (!st->codec->codec) {
1874 codec = avcodec_find_decoder(st->codec->codec_id);
1875 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1876 st->codec->parse_only = 1;
1877 if (avcodec_open(st->codec, codec) < 0) {
1878 st->codec->parse_only = 0;
1884 static int open_input_stream(HTTPContext *c, const char *info)
1887 char input_filename[1024];
1892 /* find file name */
1893 if (c->stream->feed) {
1894 strcpy(input_filename, c->stream->feed->feed_filename);
1895 buf_size = FFM_PACKET_SIZE;
1896 /* compute position (absolute time) */
1897 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1898 stream_pos = parse_date(buf, 0);
1899 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1900 int prebuffer = strtol(buf, 0, 10);
1901 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1903 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1906 strcpy(input_filename, c->stream->feed_filename);
1908 /* compute position (relative time) */
1909 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1910 stream_pos = parse_date(buf, 1);
1915 if (input_filename[0] == '\0')
1919 { time_t when = stream_pos / 1000000;
1920 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1925 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1926 buf_size, c->stream->ap_in) < 0) {
1927 http_log("%s not found", input_filename);
1932 /* open each parser */
1933 for(i=0;i<s->nb_streams;i++)
1936 /* choose stream as clock source (we favorize video stream if
1937 present) for packet sending */
1938 c->pts_stream_index = 0;
1939 for(i=0;i<c->stream->nb_streams;i++) {
1940 if (c->pts_stream_index == 0 &&
1941 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1942 c->pts_stream_index = i;
1947 if (c->fmt_in->iformat->read_seek) {
1948 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1951 /* set the start time (needed for maxtime and RTP packet timing) */
1952 c->start_time = cur_time;
1953 c->first_pts = AV_NOPTS_VALUE;
1957 /* return the server clock (in us) */
1958 static int64_t get_server_clock(HTTPContext *c)
1960 /* compute current pts value from system time */
1961 return (int64_t)(cur_time - c->start_time) * 1000LL;
1964 /* return the estimated time at which the current packet must be sent
1966 static int64_t get_packet_send_clock(HTTPContext *c)
1968 int bytes_left, bytes_sent, frame_bytes;
1970 frame_bytes = c->cur_frame_bytes;
1971 if (frame_bytes <= 0) {
1974 bytes_left = c->buffer_end - c->buffer_ptr;
1975 bytes_sent = frame_bytes - bytes_left;
1976 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1981 static int http_prepare_data(HTTPContext *c)
1984 AVFormatContext *ctx;
1986 av_freep(&c->pb_buffer);
1988 case HTTPSTATE_SEND_DATA_HEADER:
1989 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1990 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
1992 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
1993 c->stream->comment);
1994 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
1995 c->stream->copyright);
1996 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
1999 /* open output stream by using specified codecs */
2000 c->fmt_ctx.oformat = c->stream->fmt;
2001 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2002 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2005 st = av_mallocz(sizeof(AVStream));
2006 st->codec= avcodec_alloc_context();
2007 c->fmt_ctx.streams[i] = st;
2008 /* if file or feed, then just take streams from FFStream struct */
2009 if (!c->stream->feed ||
2010 c->stream->feed == c->stream)
2011 src = c->stream->streams[i];
2013 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2017 st->codec->frame_number = 0; /* XXX: should be done in
2018 AVStream, not in codec */
2019 /* I'm pretty sure that this is not correct...
2020 * However, without it, we crash
2022 st->codec->coded_frame = &dummy_frame;
2024 c->got_key_frame = 0;
2026 /* prepare header and save header data in a stream */
2027 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2028 /* XXX: potential leak */
2031 c->fmt_ctx.pb.is_streamed = 1;
2033 av_set_parameters(&c->fmt_ctx, NULL);
2034 av_write_header(&c->fmt_ctx);
2036 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2037 c->buffer_ptr = c->pb_buffer;
2038 c->buffer_end = c->pb_buffer + len;
2040 c->state = HTTPSTATE_SEND_DATA;
2041 c->last_packet_sent = 0;
2043 case HTTPSTATE_SEND_DATA:
2044 /* find a new packet */
2048 /* read a packet from the input stream */
2049 if (c->stream->feed) {
2050 ffm_set_write_index(c->fmt_in,
2051 c->stream->feed->feed_write_index,
2052 c->stream->feed->feed_size);
2055 if (c->stream->max_time &&
2056 c->stream->max_time + c->start_time - cur_time < 0) {
2057 /* We have timed out */
2058 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2061 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2062 if (c->stream->feed && c->stream->feed->feed_opened) {
2063 /* if coming from feed, it means we reached the end of the
2064 ffm file, so must wait for more data */
2065 c->state = HTTPSTATE_WAIT_FEED;
2066 return 1; /* state changed */
2068 if (c->stream->loop) {
2069 av_close_input_file(c->fmt_in);
2071 if (open_input_stream(c, "") < 0)
2076 /* must send trailer now because eof or error */
2077 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2081 /* update first pts if needed */
2082 if (c->first_pts == AV_NOPTS_VALUE) {
2083 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2084 c->start_time = cur_time;
2086 /* send it to the appropriate stream */
2087 if (c->stream->feed) {
2088 /* if coming from a feed, select the right stream */
2089 if (c->switch_pending) {
2090 c->switch_pending = 0;
2091 for(i=0;i<c->stream->nb_streams;i++) {
2092 if (c->switch_feed_streams[i] == pkt.stream_index) {
2093 if (pkt.flags & PKT_FLAG_KEY) {
2094 do_switch_stream(c, i);
2097 if (c->switch_feed_streams[i] >= 0) {
2098 c->switch_pending = 1;
2102 for(i=0;i<c->stream->nb_streams;i++) {
2103 if (c->feed_streams[i] == pkt.stream_index) {
2104 pkt.stream_index = i;
2105 if (pkt.flags & PKT_FLAG_KEY) {
2106 c->got_key_frame |= 1 << i;
2108 /* See if we have all the key frames, then
2109 * we start to send. This logic is not quite
2110 * right, but it works for the case of a
2111 * single video stream with one or more
2112 * audio streams (for which every frame is
2113 * typically a key frame).
2115 if (!c->stream->send_on_key ||
2116 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2122 AVCodecContext *codec;
2125 /* specific handling for RTP: we use several
2126 output stream (one for each RTP
2127 connection). XXX: need more abstract handling */
2128 if (c->is_packetized) {
2130 /* compute send time and duration */
2131 st = c->fmt_in->streams[pkt.stream_index];
2132 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2133 if (st->start_time != AV_NOPTS_VALUE)
2134 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2135 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2137 printf("index=%d pts=%0.3f duration=%0.6f\n",
2139 (double)c->cur_pts /
2141 (double)c->cur_frame_duration /
2144 /* find RTP context */
2145 c->packet_stream_index = pkt.stream_index;
2146 ctx = c->rtp_ctx[c->packet_stream_index];
2148 av_free_packet(&pkt);
2151 codec = ctx->streams[0]->codec;
2152 /* only one stream per RTP connection */
2153 pkt.stream_index = 0;
2157 codec = ctx->streams[pkt.stream_index]->codec;
2160 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2161 if (c->is_packetized) {
2162 int max_packet_size;
2163 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2164 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2166 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2167 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2169 ret = url_open_dyn_buf(&ctx->pb);
2172 /* XXX: potential leak */
2175 if (av_write_frame(ctx, &pkt)) {
2176 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2179 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2180 c->cur_frame_bytes = len;
2181 c->buffer_ptr = c->pb_buffer;
2182 c->buffer_end = c->pb_buffer + len;
2184 codec->frame_number++;
2188 av_free_packet(&pkt);
2194 case HTTPSTATE_SEND_DATA_TRAILER:
2195 /* last packet test ? */
2196 if (c->last_packet_sent || c->is_packetized)
2199 /* prepare header */
2200 if (url_open_dyn_buf(&ctx->pb) < 0) {
2201 /* XXX: potential leak */
2204 av_write_trailer(ctx);
2205 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2206 c->buffer_ptr = c->pb_buffer;
2207 c->buffer_end = c->pb_buffer + len;
2209 c->last_packet_sent = 1;
2216 #define SHORT_TERM_BANDWIDTH 8000000
2218 /* should convert the format at the same time */
2219 /* send data starting at c->buffer_ptr to the output connection
2220 (either UDP or TCP connection) */
2221 static int http_send_data(HTTPContext *c)
2226 if (c->buffer_ptr >= c->buffer_end) {
2227 ret = http_prepare_data(c);
2230 else if (ret != 0) {
2231 /* state change requested */
2235 if (c->is_packetized) {
2236 /* RTP data output */
2237 len = c->buffer_end - c->buffer_ptr;
2239 /* fail safe - should never happen */
2241 c->buffer_ptr = c->buffer_end;
2244 len = (c->buffer_ptr[0] << 24) |
2245 (c->buffer_ptr[1] << 16) |
2246 (c->buffer_ptr[2] << 8) |
2248 if (len > (c->buffer_end - c->buffer_ptr))
2250 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2251 /* nothing to send yet: we can wait */
2255 c->data_count += len;
2256 update_datarate(&c->datarate, c->data_count);
2258 c->stream->bytes_served += len;
2260 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2261 /* RTP packets are sent inside the RTSP TCP connection */
2262 ByteIOContext pb1, *pb = &pb1;
2263 int interleaved_index, size;
2265 HTTPContext *rtsp_c;
2268 /* if no RTSP connection left, error */
2271 /* if already sending something, then wait. */
2272 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
2275 if (url_open_dyn_buf(pb) < 0)
2277 interleaved_index = c->packet_stream_index * 2;
2278 /* RTCP packets are sent at odd indexes */
2279 if (c->buffer_ptr[1] == 200)
2280 interleaved_index++;
2281 /* write RTSP TCP header */
2283 header[1] = interleaved_index;
2284 header[2] = len >> 8;
2286 put_buffer(pb, header, 4);
2287 /* write RTP packet data */
2289 put_buffer(pb, c->buffer_ptr, len);
2290 size = url_close_dyn_buf(pb, &c->packet_buffer);
2291 /* prepare asynchronous TCP sending */
2292 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2293 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2294 c->buffer_ptr += len;
2296 /* send everything we can NOW */
2297 len = write(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2298 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr);
2300 rtsp_c->packet_buffer_ptr += len;
2302 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2303 /* if we could not send all the data, we will
2304 send it later, so a new state is needed to
2305 "lock" the RTSP TCP connection */
2306 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2309 /* all data has been sent */
2310 av_freep(&c->packet_buffer);
2313 /* send RTP packet directly in UDP */
2315 url_write(c->rtp_handles[c->packet_stream_index],
2316 c->buffer_ptr, len);
2317 c->buffer_ptr += len;
2318 /* here we continue as we can send several packets per 10 ms slot */
2321 /* TCP data output */
2322 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2324 if (errno != EAGAIN && errno != EINTR) {
2325 /* error : close connection */
2331 c->buffer_ptr += len;
2333 c->data_count += len;
2334 update_datarate(&c->datarate, c->data_count);
2336 c->stream->bytes_served += len;
2344 static int http_start_receive_data(HTTPContext *c)
2348 if (c->stream->feed_opened)
2351 /* Don't permit writing to this one */
2352 if (c->stream->readonly)
2356 fd = open(c->stream->feed_filename, O_RDWR);
2361 c->stream->feed_write_index = ffm_read_write_index(fd);
2362 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2363 lseek(fd, 0, SEEK_SET);
2365 /* init buffer input */
2366 c->buffer_ptr = c->buffer;
2367 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2368 c->stream->feed_opened = 1;
2372 static int http_receive_data(HTTPContext *c)
2376 if (c->buffer_end > c->buffer_ptr) {
2379 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2381 if (errno != EAGAIN && errno != EINTR) {
2382 /* error : close connection */
2385 } else if (len == 0) {
2386 /* end of connection : close it */
2389 c->buffer_ptr += len;
2390 c->data_count += len;
2391 update_datarate(&c->datarate, c->data_count);
2395 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2396 if (c->buffer[0] != 'f' ||
2397 c->buffer[1] != 'm') {
2398 http_log("Feed stream has become desynchronized -- disconnecting\n");
2403 if (c->buffer_ptr >= c->buffer_end) {
2404 FFStream *feed = c->stream;
2405 /* a packet has been received : write it in the store, except
2407 if (c->data_count > FFM_PACKET_SIZE) {
2409 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2410 /* XXX: use llseek or url_seek */
2411 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2412 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2414 feed->feed_write_index += FFM_PACKET_SIZE;
2415 /* update file size */
2416 if (feed->feed_write_index > c->stream->feed_size)
2417 feed->feed_size = feed->feed_write_index;
2419 /* handle wrap around if max file size reached */
2420 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2421 feed->feed_write_index = FFM_PACKET_SIZE;
2424 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2426 /* wake up any waiting connections */
2427 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2428 if (c1->state == HTTPSTATE_WAIT_FEED &&
2429 c1->stream->feed == c->stream->feed) {
2430 c1->state = HTTPSTATE_SEND_DATA;
2434 /* We have a header in our hands that contains useful data */
2436 AVInputFormat *fmt_in;
2437 ByteIOContext *pb = &s.pb;
2440 memset(&s, 0, sizeof(s));
2442 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2443 pb->buf_end = c->buffer_end; /* ?? */
2444 pb->is_streamed = 1;
2446 /* use feed output format name to find corresponding input format */
2447 fmt_in = av_find_input_format(feed->fmt->name);
2451 if (fmt_in->priv_data_size > 0) {
2452 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2458 if (fmt_in->read_header(&s, 0) < 0) {
2459 av_freep(&s.priv_data);
2463 /* Now we have the actual streams */
2464 if (s.nb_streams != feed->nb_streams) {
2465 av_freep(&s.priv_data);
2468 for (i = 0; i < s.nb_streams; i++) {
2469 memcpy(feed->streams[i]->codec,
2470 s.streams[i]->codec, sizeof(AVCodecContext));
2472 av_freep(&s.priv_data);
2474 c->buffer_ptr = c->buffer;
2479 c->stream->feed_opened = 0;
2484 /********************************************************************/
2487 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2494 switch(error_number) {
2495 #define DEF(n, c, s) case c: str = s; break;
2496 #include "rtspcodes.h"
2499 str = "Unknown Error";
2503 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2504 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2506 /* output GMT time */
2510 p = buf2 + strlen(p) - 1;
2513 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2516 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2518 rtsp_reply_header(c, error_number);
2519 url_fprintf(c->pb, "\r\n");
2522 static int rtsp_parse_request(HTTPContext *c)
2524 const char *p, *p1, *p2;
2531 RTSPHeader header1, *header = &header1;
2533 c->buffer_ptr[0] = '\0';
2536 get_word(cmd, sizeof(cmd), &p);
2537 get_word(url, sizeof(url), &p);
2538 get_word(protocol, sizeof(protocol), &p);
2540 pstrcpy(c->method, sizeof(c->method), cmd);
2541 pstrcpy(c->url, sizeof(c->url), url);
2542 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2545 if (url_open_dyn_buf(c->pb) < 0) {
2546 /* XXX: cannot do more */
2547 c->pb = NULL; /* safety */
2551 /* check version name */
2552 if (strcmp(protocol, "RTSP/1.0") != 0) {
2553 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2557 /* parse each header line */
2558 memset(header, 0, sizeof(RTSPHeader));
2559 /* skip to next line */
2560 while (*p != '\n' && *p != '\0')
2564 while (*p != '\0') {
2565 p1 = strchr(p, '\n');
2569 if (p2 > p && p2[-1] == '\r')
2571 /* skip empty line */
2575 if (len > sizeof(line) - 1)
2576 len = sizeof(line) - 1;
2577 memcpy(line, p, len);
2579 rtsp_parse_line(header, line);
2583 /* handle sequence number */
2584 c->seq = header->seq;
2586 if (!strcmp(cmd, "DESCRIBE")) {
2587 rtsp_cmd_describe(c, url);
2588 } else if (!strcmp(cmd, "OPTIONS")) {
2589 rtsp_cmd_options(c, url);
2590 } else if (!strcmp(cmd, "SETUP")) {
2591 rtsp_cmd_setup(c, url, header);
2592 } else if (!strcmp(cmd, "PLAY")) {
2593 rtsp_cmd_play(c, url, header);
2594 } else if (!strcmp(cmd, "PAUSE")) {
2595 rtsp_cmd_pause(c, url, header);
2596 } else if (!strcmp(cmd, "TEARDOWN")) {
2597 rtsp_cmd_teardown(c, url, header);
2599 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2602 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2603 c->pb = NULL; /* safety */
2605 /* XXX: cannot do more */
2608 c->buffer_ptr = c->pb_buffer;
2609 c->buffer_end = c->pb_buffer + len;
2610 c->state = RTSPSTATE_SEND_REPLY;
2614 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2616 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2617 struct in_addr my_ip)
2619 ByteIOContext pb1, *pb = &pb1;
2620 int i, payload_type, port, private_payload_type, j;
2621 const char *ipstr, *title, *mediatype;
2624 if (url_open_dyn_buf(pb) < 0)
2627 /* general media info */
2629 url_fprintf(pb, "v=0\n");
2630 ipstr = inet_ntoa(my_ip);
2631 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2632 title = stream->title;
2633 if (title[0] == '\0')
2635 url_fprintf(pb, "s=%s\n", title);
2636 if (stream->comment[0] != '\0')
2637 url_fprintf(pb, "i=%s\n", stream->comment);
2638 if (stream->is_multicast) {
2639 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2641 /* for each stream, we output the necessary info */
2642 private_payload_type = RTP_PT_PRIVATE;
2643 for(i = 0; i < stream->nb_streams; i++) {
2644 st = stream->streams[i];
2645 if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
2646 mediatype = "video";
2648 switch(st->codec->codec_type) {
2649 case CODEC_TYPE_AUDIO:
2650 mediatype = "audio";
2652 case CODEC_TYPE_VIDEO:
2653 mediatype = "video";
2656 mediatype = "application";
2660 /* NOTE: the port indication is not correct in case of
2661 unicast. It is not an issue because RTSP gives it */
2662 payload_type = rtp_get_payload_type(st->codec);
2663 if (payload_type < 0)
2664 payload_type = private_payload_type++;
2665 if (stream->is_multicast) {
2666 port = stream->multicast_port + 2 * i;
2670 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2671 mediatype, port, payload_type);
2672 if (payload_type >= RTP_PT_PRIVATE) {
2673 /* for private payload type, we need to give more info */
2674 switch(st->codec->codec_id) {
2675 case CODEC_ID_MPEG4:
2678 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2679 payload_type, 90000);
2680 /* we must also add the mpeg4 header */
2681 data = st->codec->extradata;
2683 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2684 for(j=0;j<st->codec->extradata_size;j++) {
2685 url_fprintf(pb, "%02x", data[j]);
2687 url_fprintf(pb, "\n");
2692 /* XXX: add other codecs ? */
2696 url_fprintf(pb, "a=control:streamid=%d\n", i);
2698 return url_close_dyn_buf(pb, pbuffer);
2700 url_close_dyn_buf(pb, pbuffer);
2705 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2707 // rtsp_reply_header(c, RTSP_STATUS_OK);
2708 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2709 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2710 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2711 url_fprintf(c->pb, "\r\n");
2714 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2720 int content_length, len;
2721 struct sockaddr_in my_addr;
2723 /* find which url is asked */
2724 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2729 for(stream = first_stream; stream != NULL; stream = stream->next) {
2730 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2731 !strcmp(path, stream->filename)) {
2735 /* no stream found */
2736 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2740 /* prepare the media description in sdp format */
2742 /* get the host IP */
2743 len = sizeof(my_addr);
2744 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2745 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2746 if (content_length < 0) {
2747 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2750 rtsp_reply_header(c, RTSP_STATUS_OK);
2751 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2752 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2753 url_fprintf(c->pb, "\r\n");
2754 put_buffer(c->pb, content, content_length);
2757 static HTTPContext *find_rtp_session(const char *session_id)
2761 if (session_id[0] == '\0')
2764 for(c = first_http_ctx; c != NULL; c = c->next) {
2765 if (!strcmp(c->session_id, session_id))
2771 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2773 RTSPTransportField *th;
2776 for(i=0;i<h->nb_transports;i++) {
2777 th = &h->transports[i];
2778 if (th->protocol == protocol)
2784 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2788 int stream_index, port;
2793 RTSPTransportField *th;
2794 struct sockaddr_in dest_addr;
2795 RTSPActionServerSetup setup;
2797 /* find which url is asked */
2798 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2803 /* now check each stream */
2804 for(stream = first_stream; stream != NULL; stream = stream->next) {
2805 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2806 /* accept aggregate filenames only if single stream */
2807 if (!strcmp(path, stream->filename)) {
2808 if (stream->nb_streams != 1) {
2809 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2816 for(stream_index = 0; stream_index < stream->nb_streams;
2818 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2819 stream->filename, stream_index);
2820 if (!strcmp(path, buf))
2825 /* no stream found */
2826 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2830 /* generate session id if needed */
2831 if (h->session_id[0] == '\0') {
2832 snprintf(h->session_id, sizeof(h->session_id),
2833 "%08x%08x", (int)random(), (int)random());
2836 /* find rtp session, and create it if none found */
2837 rtp_c = find_rtp_session(h->session_id);
2839 /* always prefer UDP */
2840 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2842 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2844 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2849 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2852 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2856 /* open input stream */
2857 if (open_input_stream(rtp_c, "") < 0) {
2858 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2863 /* test if stream is OK (test needed because several SETUP needs
2864 to be done for a given file) */
2865 if (rtp_c->stream != stream) {
2866 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2870 /* test if stream is already set up */
2871 if (rtp_c->rtp_ctx[stream_index]) {
2872 rtsp_reply_error(c, RTSP_STATUS_STATE);
2876 /* check transport */
2877 th = find_transport(h, rtp_c->rtp_protocol);
2878 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2879 th->client_port_min <= 0)) {
2880 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2884 /* setup default options */
2885 setup.transport_option[0] = '\0';
2886 dest_addr = rtp_c->from_addr;
2887 dest_addr.sin_port = htons(th->client_port_min);
2889 /* add transport option if needed */
2890 if (ff_rtsp_callback) {
2891 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2892 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2893 (char *)&setup, sizeof(setup),
2894 stream->rtsp_option) < 0) {
2895 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2898 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2902 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2903 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2907 /* now everything is OK, so we can send the connection parameters */
2908 rtsp_reply_header(c, RTSP_STATUS_OK);
2910 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2912 switch(rtp_c->rtp_protocol) {
2913 case RTSP_PROTOCOL_RTP_UDP:
2914 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2915 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2916 "client_port=%d-%d;server_port=%d-%d",
2917 th->client_port_min, th->client_port_min + 1,
2920 case RTSP_PROTOCOL_RTP_TCP:
2921 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2922 stream_index * 2, stream_index * 2 + 1);
2927 if (setup.transport_option[0] != '\0') {
2928 url_fprintf(c->pb, ";%s", setup.transport_option);
2930 url_fprintf(c->pb, "\r\n");
2933 url_fprintf(c->pb, "\r\n");
2937 /* find an rtp connection by using the session ID. Check consistency
2939 static HTTPContext *find_rtp_session_with_url(const char *url,
2940 const char *session_id)
2948 rtp_c = find_rtp_session(session_id);
2952 /* find which url is asked */
2953 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2957 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2958 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2959 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2960 rtp_c->stream->filename, s);
2961 if(!strncmp(path, buf, sizeof(buf))) {
2962 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2969 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2973 rtp_c = find_rtp_session_with_url(url, h->session_id);
2975 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2979 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2980 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2981 rtp_c->state != HTTPSTATE_READY) {
2982 rtsp_reply_error(c, RTSP_STATUS_STATE);
2987 /* XXX: seek in stream */
2988 if (h->range_start != AV_NOPTS_VALUE) {
2989 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2990 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2994 rtp_c->state = HTTPSTATE_SEND_DATA;
2996 /* now everything is OK, so we can send the connection parameters */
2997 rtsp_reply_header(c, RTSP_STATUS_OK);
2999 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3000 url_fprintf(c->pb, "\r\n");
3003 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3007 rtp_c = find_rtp_session_with_url(url, h->session_id);
3009 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3013 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3014 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3015 rtsp_reply_error(c, RTSP_STATUS_STATE);
3019 rtp_c->state = HTTPSTATE_READY;
3020 rtp_c->first_pts = AV_NOPTS_VALUE;
3021 /* now everything is OK, so we can send the connection parameters */
3022 rtsp_reply_header(c, RTSP_STATUS_OK);
3024 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3025 url_fprintf(c->pb, "\r\n");
3028 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3032 rtp_c = find_rtp_session_with_url(url, h->session_id);
3034 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3038 /* abort the session */
3039 close_connection(rtp_c);
3041 if (ff_rtsp_callback) {
3042 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3044 rtp_c->stream->rtsp_option);
3047 /* now everything is OK, so we can send the connection parameters */
3048 rtsp_reply_header(c, RTSP_STATUS_OK);
3050 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3051 url_fprintf(c->pb, "\r\n");
3055 /********************************************************************/
3058 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3059 FFStream *stream, const char *session_id,
3060 enum RTSPProtocol rtp_protocol)
3062 HTTPContext *c = NULL;
3063 const char *proto_str;
3065 /* XXX: should output a warning page when coming
3066 close to the connection limit */
3067 if (nb_connections >= nb_max_connections)
3070 /* add a new connection */
3071 c = av_mallocz(sizeof(HTTPContext));
3076 c->poll_entry = NULL;
3077 c->from_addr = *from_addr;
3078 c->buffer_size = IOBUFFER_INIT_SIZE;
3079 c->buffer = av_malloc(c->buffer_size);
3084 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3085 c->state = HTTPSTATE_READY;
3086 c->is_packetized = 1;
3087 c->rtp_protocol = rtp_protocol;
3089 /* protocol is shown in statistics */
3090 switch(c->rtp_protocol) {
3091 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3092 proto_str = "MCAST";
3094 case RTSP_PROTOCOL_RTP_UDP:
3097 case RTSP_PROTOCOL_RTP_TCP:
3104 pstrcpy(c->protocol, sizeof(c->protocol), "RTP/");
3105 pstrcat(c->protocol, sizeof(c->protocol), proto_str);
3107 current_bandwidth += stream->bandwidth;
3109 c->next = first_http_ctx;
3121 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3122 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3124 static int rtp_new_av_stream(HTTPContext *c,
3125 int stream_index, struct sockaddr_in *dest_addr,
3126 HTTPContext *rtsp_c)
3128 AVFormatContext *ctx;
3134 int max_packet_size;
3136 /* now we can open the relevant output stream */
3137 ctx = av_alloc_format_context();
3140 ctx->oformat = &rtp_mux;
3142 st = av_mallocz(sizeof(AVStream));
3145 st->codec= avcodec_alloc_context();
3146 ctx->nb_streams = 1;
3147 ctx->streams[0] = st;
3149 if (!c->stream->feed ||
3150 c->stream->feed == c->stream) {
3151 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3154 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3158 /* build destination RTP address */
3159 ipaddr = inet_ntoa(dest_addr->sin_addr);
3161 switch(c->rtp_protocol) {
3162 case RTSP_PROTOCOL_RTP_UDP:
3163 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3166 /* XXX: also pass as parameter to function ? */
3167 if (c->stream->is_multicast) {
3169 ttl = c->stream->multicast_ttl;
3172 snprintf(ctx->filename, sizeof(ctx->filename),
3173 "rtp://%s:%d?multicast=1&ttl=%d",
3174 ipaddr, ntohs(dest_addr->sin_port), ttl);
3176 snprintf(ctx->filename, sizeof(ctx->filename),
3177 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3180 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3182 c->rtp_handles[stream_index] = h;
3183 max_packet_size = url_get_max_packet_size(h);
3185 case RTSP_PROTOCOL_RTP_TCP:
3188 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3194 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3195 ipaddr, ntohs(dest_addr->sin_port),
3197 c->stream->filename, stream_index, c->protocol);
3199 /* normally, no packets should be output here, but the packet size may be checked */
3200 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3201 /* XXX: close stream */
3204 av_set_parameters(ctx, NULL);
3205 if (av_write_header(ctx) < 0) {
3212 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3215 c->rtp_ctx[stream_index] = ctx;
3219 /********************************************************************/
3220 /* ffserver initialization */
3222 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3226 fst = av_mallocz(sizeof(AVStream));
3229 fst->codec= avcodec_alloc_context();
3230 fst->priv_data = av_mallocz(sizeof(FeedData));
3231 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3232 fst->codec->coded_frame = &dummy_frame;
3233 fst->index = stream->nb_streams;
3234 av_set_pts_info(fst, 33, 1, 90000);
3235 stream->streams[stream->nb_streams++] = fst;
3239 /* return the stream number in the feed */
3240 static int add_av_stream(FFStream *feed, AVStream *st)
3243 AVCodecContext *av, *av1;
3247 for(i=0;i<feed->nb_streams;i++) {
3248 st = feed->streams[i];
3250 if (av1->codec_id == av->codec_id &&
3251 av1->codec_type == av->codec_type &&
3252 av1->bit_rate == av->bit_rate) {
3254 switch(av->codec_type) {
3255 case CODEC_TYPE_AUDIO:
3256 if (av1->channels == av->channels &&
3257 av1->sample_rate == av->sample_rate)
3260 case CODEC_TYPE_VIDEO:
3261 if (av1->width == av->width &&
3262 av1->height == av->height &&
3263 av1->time_base.den == av->time_base.den &&
3264 av1->time_base.num == av->time_base.num &&
3265 av1->gop_size == av->gop_size)
3274 fst = add_av_stream1(feed, av);
3277 return feed->nb_streams - 1;
3282 static void remove_stream(FFStream *stream)
3286 while (*ps != NULL) {
3287 if (*ps == stream) {
3295 /* specific mpeg4 handling : we extract the raw parameters */
3296 static void extract_mpeg4_header(AVFormatContext *infile)
3298 int mpeg4_count, i, size;
3304 for(i=0;i<infile->nb_streams;i++) {
3305 st = infile->streams[i];
3306 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3307 st->codec->extradata_size == 0) {
3314 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3315 while (mpeg4_count > 0) {
3316 if (av_read_packet(infile, &pkt) < 0)
3318 st = infile->streams[pkt.stream_index];
3319 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3320 st->codec->extradata_size == 0) {
3321 av_freep(&st->codec->extradata);
3322 /* fill extradata with the header */
3323 /* XXX: we make hard suppositions here ! */
3325 while (p < pkt.data + pkt.size - 4) {
3326 /* stop when vop header is found */
3327 if (p[0] == 0x00 && p[1] == 0x00 &&
3328 p[2] == 0x01 && p[3] == 0xb6) {
3329 size = p - pkt.data;
3330 // av_hex_dump(pkt.data, size);
3331 st->codec->extradata = av_malloc(size);
3332 st->codec->extradata_size = size;
3333 memcpy(st->codec->extradata, pkt.data, size);
3340 av_free_packet(&pkt);
3344 /* compute the needed AVStream for each file */
3345 static void build_file_streams(void)
3347 FFStream *stream, *stream_next;
3348 AVFormatContext *infile;
3351 /* gather all streams */
3352 for(stream = first_stream; stream != NULL; stream = stream_next) {
3353 stream_next = stream->next;
3354 if (stream->stream_type == STREAM_TYPE_LIVE &&
3356 /* the stream comes from a file */
3357 /* try to open the file */
3359 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3360 if (stream->fmt == &rtp_mux) {
3361 /* specific case : if transport stream output to RTP,
3362 we use a raw transport stream reader */
3363 stream->ap_in->mpeg2ts_raw = 1;
3364 stream->ap_in->mpeg2ts_compute_pcr = 1;
3367 if (av_open_input_file(&infile, stream->feed_filename,
3368 stream->ifmt, 0, stream->ap_in) < 0) {
3369 http_log("%s not found", stream->feed_filename);
3370 /* remove stream (no need to spend more time on it) */
3372 remove_stream(stream);
3374 /* find all the AVStreams inside and reference them in
3376 if (av_find_stream_info(infile) < 0) {
3377 http_log("Could not find codec parameters from '%s'",
3378 stream->feed_filename);
3379 av_close_input_file(infile);
3382 extract_mpeg4_header(infile);
3384 for(i=0;i<infile->nb_streams;i++) {
3385 add_av_stream1(stream, infile->streams[i]->codec);
3387 av_close_input_file(infile);
3393 /* compute the needed AVStream for each feed */
3394 static void build_feed_streams(void)
3396 FFStream *stream, *feed;
3399 /* gather all streams */
3400 for(stream = first_stream; stream != NULL; stream = stream->next) {
3401 feed = stream->feed;
3403 if (!stream->is_feed) {
3404 /* we handle a stream coming from a feed */
3405 for(i=0;i<stream->nb_streams;i++) {
3406 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3412 /* gather all streams */
3413 for(stream = first_stream; stream != NULL; stream = stream->next) {
3414 feed = stream->feed;
3416 if (stream->is_feed) {
3417 for(i=0;i<stream->nb_streams;i++) {
3418 stream->feed_streams[i] = i;
3424 /* create feed files if needed */
3425 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3428 if (url_exist(feed->feed_filename)) {
3429 /* See if it matches */
3433 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3434 /* Now see if it matches */
3435 if (s->nb_streams == feed->nb_streams) {
3437 for(i=0;i<s->nb_streams;i++) {
3439 sf = feed->streams[i];
3442 if (sf->index != ss->index ||
3444 printf("Index & Id do not match for stream %d (%s)\n",
3445 i, feed->feed_filename);
3448 AVCodecContext *ccf, *ccs;
3452 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3454 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3455 printf("Codecs do not match for stream %d\n", i);
3457 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3458 printf("Codec bitrates do not match for stream %d\n", i);
3460 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3461 if (CHECK_CODEC(time_base.den) ||
3462 CHECK_CODEC(time_base.num) ||
3463 CHECK_CODEC(width) ||
3464 CHECK_CODEC(height)) {
3465 printf("Codec width, height and framerate do not match for stream %d\n", i);
3468 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3469 if (CHECK_CODEC(sample_rate) ||
3470 CHECK_CODEC(channels) ||
3471 CHECK_CODEC(frame_size)) {
3472 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3476 printf("Unknown codec type\n");
3485 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3486 feed->feed_filename, s->nb_streams, feed->nb_streams);
3489 av_close_input_file(s);
3491 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3492 feed->feed_filename);
3495 if (feed->readonly) {
3496 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3497 feed->feed_filename);
3500 unlink(feed->feed_filename);
3503 if (!url_exist(feed->feed_filename)) {
3504 AVFormatContext s1, *s = &s1;
3506 if (feed->readonly) {
3507 printf("Unable to create feed file '%s' as it is marked readonly\n",
3508 feed->feed_filename);
3512 /* only write the header of the ffm file */
3513 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3514 fprintf(stderr, "Could not open output feed file '%s'\n",
3515 feed->feed_filename);
3518 s->oformat = feed->fmt;
3519 s->nb_streams = feed->nb_streams;
3520 for(i=0;i<s->nb_streams;i++) {
3522 st = feed->streams[i];
3525 av_set_parameters(s, NULL);
3527 /* XXX: need better api */
3528 av_freep(&s->priv_data);
3531 /* get feed size and write index */
3532 fd = open(feed->feed_filename, O_RDONLY);
3534 fprintf(stderr, "Could not open output feed file '%s'\n",
3535 feed->feed_filename);
3539 feed->feed_write_index = ffm_read_write_index(fd);
3540 feed->feed_size = lseek(fd, 0, SEEK_END);
3541 /* ensure that we do not wrap before the end of file */
3542 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3543 feed->feed_max_size = feed->feed_size;
3549 /* compute the bandwidth used by each stream */
3550 static void compute_bandwidth(void)
3555 for(stream = first_stream; stream != NULL; stream = stream->next) {
3557 for(i=0;i<stream->nb_streams;i++) {
3558 AVStream *st = stream->streams[i];
3559 switch(st->codec->codec_type) {
3560 case CODEC_TYPE_AUDIO:
3561 case CODEC_TYPE_VIDEO:
3562 bandwidth += st->codec->bit_rate;
3568 stream->bandwidth = (bandwidth + 999) / 1000;
3572 static void get_arg(char *buf, int buf_size, const char **pp)
3579 while (isspace(*p)) p++;
3582 if (*p == '\"' || *p == '\'')
3594 if ((q - buf) < buf_size - 1)
3599 if (quote && *p == quote)
3604 /* add a codec and set the default parameters */
3605 static void add_codec(FFStream *stream, AVCodecContext *av)
3609 /* compute default parameters */
3610 switch(av->codec_type) {
3611 case CODEC_TYPE_AUDIO:
3612 if (av->bit_rate == 0)
3613 av->bit_rate = 64000;
3614 if (av->sample_rate == 0)
3615 av->sample_rate = 22050;
3616 if (av->channels == 0)
3619 case CODEC_TYPE_VIDEO:
3620 if (av->bit_rate == 0)
3621 av->bit_rate = 64000;
3622 if (av->time_base.num == 0){
3623 av->time_base.den = 5;
3624 av->time_base.num = 1;
3626 if (av->width == 0 || av->height == 0) {
3630 /* Bitrate tolerance is less for streaming */
3631 if (av->bit_rate_tolerance == 0)
3632 av->bit_rate_tolerance = av->bit_rate / 4;
3637 if (av->max_qdiff == 0)
3639 av->qcompress = 0.5;
3642 if (!av->nsse_weight)
3643 av->nsse_weight = 8;
3645 av->frame_skip_cmp = FF_CMP_DCTMAX;
3646 av->me_method = ME_EPZS;
3647 av->rc_buffer_aggressivity = 1.0;
3650 av->rc_eq = "tex^qComp";
3651 if (!av->i_quant_factor)
3652 av->i_quant_factor = -0.8;
3653 if (!av->b_quant_factor)
3654 av->b_quant_factor = 1.25;
3655 if (!av->b_quant_offset)
3656 av->b_quant_offset = 1.25;
3657 if (!av->rc_max_rate)
3658 av->rc_max_rate = av->bit_rate * 2;
3660 if (av->rc_max_rate && !av->rc_buffer_size) {
3661 av->rc_buffer_size = av->rc_max_rate;
3670 st = av_mallocz(sizeof(AVStream));
3673 st->codec = avcodec_alloc_context();
3674 stream->streams[stream->nb_streams++] = st;
3675 memcpy(st->codec, av, sizeof(AVCodecContext));
3678 static int opt_audio_codec(const char *arg)
3684 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3689 return CODEC_ID_NONE;
3695 static int opt_video_codec(const char *arg)
3701 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3706 return CODEC_ID_NONE;
3712 /* simplistic plugin support */
3714 #ifdef CONFIG_HAVE_DLOPEN
3715 void load_module(const char *filename)
3718 void (*init_func)(void);
3719 dll = dlopen(filename, RTLD_NOW);
3721 fprintf(stderr, "Could not load module '%s' - %s\n",
3722 filename, dlerror());
3726 init_func = dlsym(dll, "ffserver_module_init");
3729 "%s: init function 'ffserver_module_init()' not found\n",
3738 static int parse_ffconfig(const char *filename)
3745 int val, errors, line_num;
3746 FFStream **last_stream, *stream, *redirect;
3747 FFStream **last_feed, *feed;
3748 AVCodecContext audio_enc, video_enc;
3749 int audio_id, video_id;
3751 f = fopen(filename, "r");
3759 first_stream = NULL;
3760 last_stream = &first_stream;
3762 last_feed = &first_feed;
3766 audio_id = CODEC_ID_NONE;
3767 video_id = CODEC_ID_NONE;
3769 if (fgets(line, sizeof(line), f) == NULL)
3775 if (*p == '\0' || *p == '#')
3778 get_arg(cmd, sizeof(cmd), &p);
3780 if (!strcasecmp(cmd, "Port")) {
3781 get_arg(arg, sizeof(arg), &p);
3782 my_http_addr.sin_port = htons (atoi(arg));
3783 } else if (!strcasecmp(cmd, "BindAddress")) {
3784 get_arg(arg, sizeof(arg), &p);
3785 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3786 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3787 filename, line_num, arg);
3790 } else if (!strcasecmp(cmd, "NoDaemon")) {
3791 ffserver_daemon = 0;
3792 } else if (!strcasecmp(cmd, "RTSPPort")) {
3793 get_arg(arg, sizeof(arg), &p);
3794 my_rtsp_addr.sin_port = htons (atoi(arg));
3795 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3796 get_arg(arg, sizeof(arg), &p);
3797 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3798 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3799 filename, line_num, arg);
3802 } else if (!strcasecmp(cmd, "MaxClients")) {
3803 get_arg(arg, sizeof(arg), &p);
3805 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3806 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3807 filename, line_num, arg);
3810 nb_max_connections = val;
3812 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3813 get_arg(arg, sizeof(arg), &p);
3815 if (val < 10 || val > 100000) {
3816 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3817 filename, line_num, arg);
3820 max_bandwidth = val;
3822 } else if (!strcasecmp(cmd, "CustomLog")) {
3823 get_arg(logfilename, sizeof(logfilename), &p);
3824 } else if (!strcasecmp(cmd, "<Feed")) {
3825 /*********************************************/
3826 /* Feed related options */
3828 if (stream || feed) {
3829 fprintf(stderr, "%s:%d: Already in a tag\n",
3830 filename, line_num);
3832 feed = av_mallocz(sizeof(FFStream));
3833 /* add in stream list */
3834 *last_stream = feed;
3835 last_stream = &feed->next;
3836 /* add in feed list */
3838 last_feed = &feed->next_feed;
3840 get_arg(feed->filename, sizeof(feed->filename), &p);
3841 q = strrchr(feed->filename, '>');
3844 feed->fmt = guess_format("ffm", NULL, NULL);
3845 /* defaut feed file */
3846 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3847 "/tmp/%s.ffm", feed->filename);
3848 feed->feed_max_size = 5 * 1024 * 1024;
3850 feed->feed = feed; /* self feeding :-) */
3852 } else if (!strcasecmp(cmd, "Launch")) {
3856 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3858 for (i = 0; i < 62; i++) {
3861 get_arg(argbuf, sizeof(argbuf), &p);
3865 feed->child_argv[i] = av_malloc(strlen(argbuf) + 1);
3866 strcpy(feed->child_argv[i], argbuf);
3869 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3871 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3873 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3874 inet_ntoa(my_http_addr.sin_addr),
3875 ntohs(my_http_addr.sin_port), feed->filename);
3880 fprintf(stdout, "Launch commandline: ");
3881 for (j = 0; j <= i; j++)
3882 fprintf(stdout, "%s ", feed->child_argv[j]);
3883 fprintf(stdout, "\n");
3886 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3888 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3890 } else if (stream) {
3891 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3893 } else if (!strcasecmp(cmd, "File")) {
3895 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3896 } else if (stream) {
3897 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3899 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3904 get_arg(arg, sizeof(arg), &p);
3906 fsize = strtod(p1, (char **)&p1);
3907 switch(toupper(*p1)) {
3912 fsize *= 1024 * 1024;
3915 fsize *= 1024 * 1024 * 1024;
3918 feed->feed_max_size = (int64_t)fsize;
3920 } else if (!strcasecmp(cmd, "</Feed>")) {
3922 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3923 filename, line_num);
3927 /* Make sure that we start out clean */
3928 if (unlink(feed->feed_filename) < 0
3929 && errno != ENOENT) {
3930 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3931 filename, line_num, feed->feed_filename, strerror(errno));
3937 } else if (!strcasecmp(cmd, "<Stream")) {
3938 /*********************************************/
3939 /* Stream related options */
3941 if (stream || feed) {
3942 fprintf(stderr, "%s:%d: Already in a tag\n",
3943 filename, line_num);
3945 stream = av_mallocz(sizeof(FFStream));
3946 *last_stream = stream;
3947 last_stream = &stream->next;
3949 get_arg(stream->filename, sizeof(stream->filename), &p);
3950 q = strrchr(stream->filename, '>');
3953 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3954 memset(&audio_enc, 0, sizeof(AVCodecContext));
3955 memset(&video_enc, 0, sizeof(AVCodecContext));
3956 audio_id = CODEC_ID_NONE;
3957 video_id = CODEC_ID_NONE;
3959 audio_id = stream->fmt->audio_codec;
3960 video_id = stream->fmt->video_codec;
3963 } else if (!strcasecmp(cmd, "Feed")) {
3964 get_arg(arg, sizeof(arg), &p);
3969 while (sfeed != NULL) {
3970 if (!strcmp(sfeed->filename, arg))
3972 sfeed = sfeed->next_feed;
3975 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3976 filename, line_num, arg);
3978 stream->feed = sfeed;
3981 } else if (!strcasecmp(cmd, "Format")) {
3982 get_arg(arg, sizeof(arg), &p);
3983 if (!strcmp(arg, "status")) {
3984 stream->stream_type = STREAM_TYPE_STATUS;
3987 stream->stream_type = STREAM_TYPE_LIVE;
3988 /* jpeg cannot be used here, so use single frame jpeg */
3989 if (!strcmp(arg, "jpeg"))
3990 strcpy(arg, "mjpeg");
3991 stream->fmt = guess_stream_format(arg, NULL, NULL);
3993 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3994 filename, line_num, arg);
3999 audio_id = stream->fmt->audio_codec;
4000 video_id = stream->fmt->video_codec;
4002 } else if (!strcasecmp(cmd, "InputFormat")) {
4003 stream->ifmt = av_find_input_format(arg);
4004 if (!stream->ifmt) {
4005 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4006 filename, line_num, arg);
4008 } else if (!strcasecmp(cmd, "FaviconURL")) {
4009 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4010 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4012 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4013 filename, line_num);
4016 } else if (!strcasecmp(cmd, "Author")) {
4018 get_arg(stream->author, sizeof(stream->author), &p);
4020 } else if (!strcasecmp(cmd, "Comment")) {
4022 get_arg(stream->comment, sizeof(stream->comment), &p);
4024 } else if (!strcasecmp(cmd, "Copyright")) {
4026 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4028 } else if (!strcasecmp(cmd, "Title")) {
4030 get_arg(stream->title, sizeof(stream->title), &p);
4032 } else if (!strcasecmp(cmd, "Preroll")) {
4033 get_arg(arg, sizeof(arg), &p);
4035 stream->prebuffer = atof(arg) * 1000;
4037 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4039 stream->send_on_key = 1;
4041 } else if (!strcasecmp(cmd, "AudioCodec")) {
4042 get_arg(arg, sizeof(arg), &p);
4043 audio_id = opt_audio_codec(arg);
4044 if (audio_id == CODEC_ID_NONE) {
4045 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4046 filename, line_num, arg);
4049 } else if (!strcasecmp(cmd, "VideoCodec")) {
4050 get_arg(arg, sizeof(arg), &p);
4051 video_id = opt_video_codec(arg);
4052 if (video_id == CODEC_ID_NONE) {
4053 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4054 filename, line_num, arg);
4057 } else if (!strcasecmp(cmd, "MaxTime")) {
4058 get_arg(arg, sizeof(arg), &p);
4060 stream->max_time = atof(arg) * 1000;
4062 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4063 get_arg(arg, sizeof(arg), &p);
4065 audio_enc.bit_rate = atoi(arg) * 1000;
4067 } else if (!strcasecmp(cmd, "AudioChannels")) {
4068 get_arg(arg, sizeof(arg), &p);
4070 audio_enc.channels = atoi(arg);
4072 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4073 get_arg(arg, sizeof(arg), &p);
4075 audio_enc.sample_rate = atoi(arg);
4077 } else if (!strcasecmp(cmd, "AudioQuality")) {
4078 get_arg(arg, sizeof(arg), &p);
4080 // audio_enc.quality = atof(arg) * 1000;
4082 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4084 int minrate, maxrate;
4086 get_arg(arg, sizeof(arg), &p);
4088 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4089 video_enc.rc_min_rate = minrate * 1000;
4090 video_enc.rc_max_rate = maxrate * 1000;
4092 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4093 filename, line_num, arg);
4097 } else if (!strcasecmp(cmd, "Debug")) {
4099 get_arg(arg, sizeof(arg), &p);
4100 video_enc.debug = strtol(arg,0,0);
4102 } else if (!strcasecmp(cmd, "Strict")) {
4104 get_arg(arg, sizeof(arg), &p);
4105 video_enc.strict_std_compliance = atoi(arg);
4107 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4109 get_arg(arg, sizeof(arg), &p);
4110 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4112 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4114 get_arg(arg, sizeof(arg), &p);
4115 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4117 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4118 get_arg(arg, sizeof(arg), &p);
4120 video_enc.bit_rate = atoi(arg) * 1000;
4122 } else if (!strcasecmp(cmd, "VideoSize")) {
4123 get_arg(arg, sizeof(arg), &p);
4125 parse_image_size(&video_enc.width, &video_enc.height, arg);
4126 if ((video_enc.width % 16) != 0 ||
4127 (video_enc.height % 16) != 0) {
4128 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4129 filename, line_num);
4133 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4134 get_arg(arg, sizeof(arg), &p);
4136 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4137 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4139 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4140 get_arg(arg, sizeof(arg), &p);
4142 video_enc.gop_size = atoi(arg);
4144 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4146 video_enc.gop_size = 1;
4148 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4150 video_enc.mb_decision = FF_MB_DECISION_BITS;
4152 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4154 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4155 video_enc.flags |= CODEC_FLAG_4MV;
4157 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4158 get_arg(arg, sizeof(arg), &p);
4160 video_enc.max_qdiff = atoi(arg);
4161 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4162 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4163 filename, line_num);
4167 } else if (!strcasecmp(cmd, "VideoQMax")) {
4168 get_arg(arg, sizeof(arg), &p);
4170 video_enc.qmax = atoi(arg);
4171 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4172 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4173 filename, line_num);
4177 } else if (!strcasecmp(cmd, "VideoQMin")) {
4178 get_arg(arg, sizeof(arg), &p);
4180 video_enc.qmin = atoi(arg);
4181 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4182 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4183 filename, line_num);
4187 } else if (!strcasecmp(cmd, "LumaElim")) {
4188 get_arg(arg, sizeof(arg), &p);
4190 video_enc.luma_elim_threshold = atoi(arg);
4192 } else if (!strcasecmp(cmd, "ChromaElim")) {
4193 get_arg(arg, sizeof(arg), &p);
4195 video_enc.chroma_elim_threshold = atoi(arg);
4197 } else if (!strcasecmp(cmd, "LumiMask")) {
4198 get_arg(arg, sizeof(arg), &p);
4200 video_enc.lumi_masking = atof(arg);
4202 } else if (!strcasecmp(cmd, "DarkMask")) {
4203 get_arg(arg, sizeof(arg), &p);
4205 video_enc.dark_masking = atof(arg);
4207 } else if (!strcasecmp(cmd, "NoVideo")) {
4208 video_id = CODEC_ID_NONE;
4209 } else if (!strcasecmp(cmd, "NoAudio")) {
4210 audio_id = CODEC_ID_NONE;
4211 } else if (!strcasecmp(cmd, "ACL")) {
4215 get_arg(arg, sizeof(arg), &p);
4216 if (strcasecmp(arg, "allow") == 0) {
4217 acl.action = IP_ALLOW;
4218 } else if (strcasecmp(arg, "deny") == 0) {
4219 acl.action = IP_DENY;
4221 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4222 filename, line_num, arg);
4226 get_arg(arg, sizeof(arg), &p);
4228 he = gethostbyname(arg);
4230 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4231 filename, line_num, arg);
4234 /* Only take the first */
4235 acl.first.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4236 acl.last = acl.first;
4239 get_arg(arg, sizeof(arg), &p);
4242 he = gethostbyname(arg);
4244 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4245 filename, line_num, arg);
4248 /* Only take the first */
4249 acl.last.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4254 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4255 IPAddressACL **naclp = 0;
4261 naclp = &stream->acl;
4265 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4266 filename, line_num);
4272 naclp = &(*naclp)->next;
4277 } else if (!strcasecmp(cmd, "RTSPOption")) {
4278 get_arg(arg, sizeof(arg), &p);
4280 av_freep(&stream->rtsp_option);
4281 /* XXX: av_strdup ? */
4282 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4283 if (stream->rtsp_option) {
4284 strcpy(stream->rtsp_option, arg);
4287 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4288 get_arg(arg, sizeof(arg), &p);
4290 if (!inet_aton(arg, &stream->multicast_ip)) {
4291 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4292 filename, line_num, arg);
4295 stream->is_multicast = 1;
4296 stream->loop = 1; /* default is looping */
4298 } else if (!strcasecmp(cmd, "MulticastPort")) {
4299 get_arg(arg, sizeof(arg), &p);
4301 stream->multicast_port = atoi(arg);
4303 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4304 get_arg(arg, sizeof(arg), &p);
4306 stream->multicast_ttl = atoi(arg);
4308 } else if (!strcasecmp(cmd, "NoLoop")) {
4312 } else if (!strcasecmp(cmd, "</Stream>")) {
4314 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4315 filename, line_num);
4318 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4319 if (audio_id != CODEC_ID_NONE) {
4320 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4321 audio_enc.codec_id = audio_id;
4322 add_codec(stream, &audio_enc);
4324 if (video_id != CODEC_ID_NONE) {
4325 video_enc.codec_type = CODEC_TYPE_VIDEO;
4326 video_enc.codec_id = video_id;
4327 add_codec(stream, &video_enc);
4331 } else if (!strcasecmp(cmd, "<Redirect")) {
4332 /*********************************************/
4334 if (stream || feed || redirect) {
4335 fprintf(stderr, "%s:%d: Already in a tag\n",
4336 filename, line_num);
4339 redirect = av_mallocz(sizeof(FFStream));
4340 *last_stream = redirect;
4341 last_stream = &redirect->next;
4343 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4344 q = strrchr(redirect->filename, '>');
4347 redirect->stream_type = STREAM_TYPE_REDIRECT;
4349 } else if (!strcasecmp(cmd, "URL")) {
4351 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4353 } else if (!strcasecmp(cmd, "</Redirect>")) {
4355 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4356 filename, line_num);
4359 if (!redirect->feed_filename[0]) {
4360 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4361 filename, line_num);
4365 } else if (!strcasecmp(cmd, "LoadModule")) {
4366 get_arg(arg, sizeof(arg), &p);
4367 #ifdef CONFIG_HAVE_DLOPEN
4370 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4371 filename, line_num, arg);
4375 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4376 filename, line_num, cmd);
4390 static void write_packet(FFCodec *ffenc,
4391 uint8_t *buf, int size)
4394 AVCodecContext *enc = &ffenc->enc;
4396 mk_header(&hdr, enc, size);
4397 wptr = http_fifo.wptr;
4398 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4399 fifo_write(&http_fifo, buf, size, &wptr);
4400 /* atomic modification of wptr */
4401 http_fifo.wptr = wptr;
4402 ffenc->data_count += size;
4403 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4407 static void show_banner(void)
4409 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2003 Fabrice Bellard\n");
4412 static void show_help(void)
4415 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4416 "Hyper fast multi format Audio/Video streaming server\n"
4418 "-L : print the LICENSE\n"
4420 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4424 static void show_license(void)
4428 "This library is free software; you can redistribute it and/or\n"
4429 "modify it under the terms of the GNU Lesser General Public\n"
4430 "License as published by the Free Software Foundation; either\n"
4431 "version 2 of the License, or (at your option) any later version.\n"
4433 "This library is distributed in the hope that it will be useful,\n"
4434 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4435 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4436 "Lesser General Public License for more details.\n"
4438 "You should have received a copy of the GNU Lesser General Public\n"
4439 "License along with this library; if not, write to the Free Software\n"
4440 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
4444 static void handle_child_exit(int sig)
4449 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4452 for (feed = first_feed; feed; feed = feed->next) {
4453 if (feed->pid == pid) {
4454 int uptime = time(0) - feed->pid_start;
4457 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4460 /* Turn off any more restarts */
4461 feed->child_argv = 0;
4467 need_to_start_children = 1;
4470 int main(int argc, char **argv)
4472 const char *config_filename;
4474 struct sigaction sigact;
4478 config_filename = "/etc/ffserver.conf";
4480 my_program_name = argv[0];
4481 my_program_dir = getcwd(0, 0);
4482 ffserver_daemon = 1;
4485 c = getopt(argc, argv, "ndLh?f:");
4501 ffserver_daemon = 0;
4504 config_filename = optarg;
4511 putenv("http_proxy"); /* Kill the http_proxy */
4513 srandom(gettime_ms() + (getpid() << 16));
4515 /* address on which the server will handle HTTP connections */
4516 my_http_addr.sin_family = AF_INET;
4517 my_http_addr.sin_port = htons (8080);
4518 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4520 /* address on which the server will handle RTSP connections */
4521 my_rtsp_addr.sin_family = AF_INET;
4522 my_rtsp_addr.sin_port = htons (5454);
4523 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4525 nb_max_connections = 5;
4526 max_bandwidth = 1000;
4527 first_stream = NULL;
4528 logfilename[0] = '\0';
4530 memset(&sigact, 0, sizeof(sigact));
4531 sigact.sa_handler = handle_child_exit;
4532 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4533 sigaction(SIGCHLD, &sigact, 0);
4535 if (parse_ffconfig(config_filename) < 0) {
4536 fprintf(stderr, "Incorrect config file - exiting.\n");
4540 build_file_streams();
4542 build_feed_streams();
4544 compute_bandwidth();
4546 /* put the process in background and detach it from its TTY */
4547 if (ffserver_daemon) {
4554 } else if (pid > 0) {
4562 open("/dev/null", O_RDWR);
4563 if (strcmp(logfilename, "-") != 0) {
4573 signal(SIGPIPE, SIG_IGN);
4575 /* open log file if needed */
4576 if (logfilename[0] != '\0') {
4577 if (!strcmp(logfilename, "-"))
4580 logfile = fopen(logfilename, "w");
4583 if (http_server() < 0) {
4584 fprintf(stderr, "Could not start server\n");