2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #if HAVE_CLOSESOCKET != 1
24 #define closesocket close
33 #include <sys/ioctl.h>
34 #ifdef HAVE_SYS_POLL_H
39 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
55 /* maximum number of simultaneous HTTP connections */
56 #define HTTP_MAX_CONNECTIONS 2000
59 HTTPSTATE_WAIT_REQUEST,
60 HTTPSTATE_SEND_HEADER,
61 HTTPSTATE_SEND_DATA_HEADER,
62 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
63 HTTPSTATE_SEND_DATA_TRAILER,
64 HTTPSTATE_RECEIVE_DATA,
65 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
68 RTSPSTATE_WAIT_REQUEST,
70 RTSPSTATE_SEND_PACKET,
73 const char *http_state[] = {
89 #define IOBUFFER_INIT_SIZE 8192
91 /* timeouts are in ms */
92 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
93 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
95 #define SYNC_TIMEOUT (10 * 1000)
98 int64_t count1, count2;
102 /* context associated with one connection */
103 typedef struct HTTPContext {
104 enum HTTPState state;
105 int fd; /* socket file descriptor */
106 struct sockaddr_in from_addr; /* origin */
107 struct pollfd *poll_entry; /* used when polling */
109 uint8_t *buffer_ptr, *buffer_end;
112 struct HTTPContext *next;
113 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
117 /* input format handling */
118 AVFormatContext *fmt_in;
119 int64_t start_time; /* In milliseconds - this wraps fairly often */
120 int64_t first_pts; /* initial pts value */
121 int64_t cur_pts; /* current pts value from the stream in us */
122 int64_t cur_frame_duration; /* duration of the current frame in us */
123 int cur_frame_bytes; /* output frame size, needed to compute
124 the time at which we send each
126 int pts_stream_index; /* stream we choose as clock reference */
127 int64_t cur_clock; /* current clock reference value in us */
128 /* output format handling */
129 struct FFStream *stream;
130 /* -1 is invalid stream */
131 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
132 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
134 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
135 int last_packet_sent; /* true if last data packet was sent */
137 DataRateData datarate;
144 int is_packetized; /* if true, the stream is packetized */
145 int packet_stream_index; /* current stream for output in state machine */
147 /* RTSP state specific */
148 uint8_t *pb_buffer; /* XXX: use that in all the code */
150 int seq; /* RTSP sequence number */
152 /* RTP state specific */
153 enum RTSPProtocol rtp_protocol;
154 char session_id[32]; /* session id */
155 AVFormatContext *rtp_ctx[MAX_STREAMS];
157 /* RTP/UDP specific */
158 URLContext *rtp_handles[MAX_STREAMS];
160 /* RTP/TCP specific */
161 struct HTTPContext *rtsp_c;
162 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
165 static AVFrame dummy_frame;
167 /* each generated stream is described here */
171 STREAM_TYPE_REDIRECT,
174 enum IPAddressAction {
179 typedef struct IPAddressACL {
180 struct IPAddressACL *next;
181 enum IPAddressAction action;
182 /* These are in host order */
183 struct in_addr first;
187 /* description of each stream of the ffserver.conf file */
188 typedef struct FFStream {
189 enum StreamType stream_type;
190 char filename[1024]; /* stream filename */
191 struct FFStream *feed; /* feed we are using (can be null if
193 AVFormatParameters *ap_in; /* input parameters */
194 AVInputFormat *ifmt; /* if non NULL, force input format */
198 int prebuffer; /* Number of millseconds early to start */
199 int64_t max_time; /* Number of milliseconds to run */
201 AVStream *streams[MAX_STREAMS];
202 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
203 char feed_filename[1024]; /* file name of the feed storage, or
204 input file name for a stream */
209 pid_t pid; /* Of ffmpeg process */
210 time_t pid_start; /* Of ffmpeg process */
212 struct FFStream *next;
213 int bandwidth; /* bandwidth, in kbits/s */
216 /* multicast specific */
218 struct in_addr multicast_ip;
219 int multicast_port; /* first port used for multicast */
221 int loop; /* if true, send the stream in loops (only meaningful if file) */
224 int feed_opened; /* true if someone is writing to the feed */
225 int is_feed; /* true if it is a feed */
226 int readonly; /* True if writing is prohibited to the file */
228 int64_t bytes_served;
229 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
230 int64_t feed_write_index; /* current write position in feed (it wraps round) */
231 int64_t feed_size; /* current size of feed */
232 struct FFStream *next_feed;
235 typedef struct FeedData {
236 long long data_count;
237 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
240 static struct sockaddr_in my_http_addr;
241 static struct sockaddr_in my_rtsp_addr;
243 static char logfilename[1024];
244 static HTTPContext *first_http_ctx;
245 static FFStream *first_feed; /* contains only feeds */
246 static FFStream *first_stream; /* contains all streams, including feeds */
248 static void new_connection(int server_fd, int is_rtsp);
249 static void close_connection(HTTPContext *c);
252 static int handle_connection(HTTPContext *c);
253 static int http_parse_request(HTTPContext *c);
254 static int http_send_data(HTTPContext *c);
255 static void compute_stats(HTTPContext *c);
256 static int open_input_stream(HTTPContext *c, const char *info);
257 static int http_start_receive_data(HTTPContext *c);
258 static int http_receive_data(HTTPContext *c);
261 static int rtsp_parse_request(HTTPContext *c);
262 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
263 static void rtsp_cmd_options(HTTPContext *c, const char *url);
264 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
265 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
266 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
267 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
270 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
271 struct in_addr my_ip);
274 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
275 FFStream *stream, const char *session_id,
276 enum RTSPProtocol rtp_protocol);
277 static int rtp_new_av_stream(HTTPContext *c,
278 int stream_index, struct sockaddr_in *dest_addr,
279 HTTPContext *rtsp_c);
281 static const char *my_program_name;
282 static const char *my_program_dir;
284 static int ffserver_debug;
285 static int ffserver_daemon;
286 static int no_launch;
287 static int need_to_start_children;
289 static int nb_max_connections;
290 static int nb_connections;
292 static int max_bandwidth;
293 static int current_bandwidth;
295 static int64_t cur_time; // Making this global saves on passing it around everywhere
297 static AVRandomState random_state;
299 static FILE *logfile = NULL;
301 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
307 vfprintf(logfile, fmt, ap);
313 static char *ctime1(char *buf2)
321 p = buf2 + strlen(p) - 1;
327 static void log_connection(HTTPContext *c)
334 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
335 inet_ntoa(c->from_addr.sin_addr),
336 ctime1(buf2), c->method, c->url,
337 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
340 static void update_datarate(DataRateData *drd, int64_t count)
342 if (!drd->time1 && !drd->count1) {
343 drd->time1 = drd->time2 = cur_time;
344 drd->count1 = drd->count2 = count;
345 } else if (cur_time - drd->time2 > 5000) {
346 drd->time1 = drd->time2;
347 drd->count1 = drd->count2;
348 drd->time2 = cur_time;
353 /* In bytes per second */
354 static int compute_datarate(DataRateData *drd, int64_t count)
356 if (cur_time == drd->time1)
359 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
363 static void start_children(FFStream *feed)
368 for (; feed; feed = feed->next) {
369 if (feed->child_argv && !feed->pid) {
370 feed->pid_start = time(0);
375 fprintf(stderr, "Unable to create children\n");
384 for (i = 3; i < 256; i++) {
388 if (!ffserver_debug) {
389 i = open("/dev/null", O_RDWR);
398 av_strlcpy(pathname, my_program_name, sizeof(pathname));
400 slash = strrchr(pathname, '/');
406 strcpy(slash, "ffmpeg");
408 /* This is needed to make relative pathnames work */
409 chdir(my_program_dir);
411 signal(SIGPIPE, SIG_DFL);
413 execvp(pathname, feed->child_argv);
421 /* open a listening socket */
422 static int socket_open_listen(struct sockaddr_in *my_addr)
426 server_fd = socket(AF_INET,SOCK_STREAM,0);
433 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
435 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
437 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
439 closesocket(server_fd);
443 if (listen (server_fd, 5) < 0) {
445 closesocket(server_fd);
448 ff_socket_nonblock(server_fd, 1);
453 /* start all multicast streams */
454 static void start_multicast(void)
459 struct sockaddr_in dest_addr;
460 int default_port, stream_index;
463 for(stream = first_stream; stream != NULL; stream = stream->next) {
464 if (stream->is_multicast) {
465 /* open the RTP connection */
466 snprintf(session_id, sizeof(session_id), "%08x%08x",
467 av_random(&random_state), av_random(&random_state));
469 /* choose a port if none given */
470 if (stream->multicast_port == 0) {
471 stream->multicast_port = default_port;
475 dest_addr.sin_family = AF_INET;
476 dest_addr.sin_addr = stream->multicast_ip;
477 dest_addr.sin_port = htons(stream->multicast_port);
479 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
480 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
484 if (open_input_stream(rtp_c, "") < 0) {
485 fprintf(stderr, "Could not open input stream for stream '%s'\n",
490 /* open each RTP stream */
491 for(stream_index = 0; stream_index < stream->nb_streams;
493 dest_addr.sin_port = htons(stream->multicast_port +
495 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
496 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
497 stream->filename, stream_index);
502 /* change state to send data */
503 rtp_c->state = HTTPSTATE_SEND_DATA;
508 /* main loop of the http server */
509 static int http_server(void)
511 int server_fd, ret, rtsp_server_fd, delay, delay1;
512 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
513 HTTPContext *c, *c_next;
515 server_fd = socket_open_listen(&my_http_addr);
519 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
520 if (rtsp_server_fd < 0)
523 http_log("ffserver started.\n");
525 start_children(first_feed);
527 first_http_ctx = NULL;
533 poll_entry = poll_table;
534 poll_entry->fd = server_fd;
535 poll_entry->events = POLLIN;
538 poll_entry->fd = rtsp_server_fd;
539 poll_entry->events = POLLIN;
542 /* wait for events on each HTTP handle */
549 case HTTPSTATE_SEND_HEADER:
550 case RTSPSTATE_SEND_REPLY:
551 case RTSPSTATE_SEND_PACKET:
552 c->poll_entry = poll_entry;
554 poll_entry->events = POLLOUT;
557 case HTTPSTATE_SEND_DATA_HEADER:
558 case HTTPSTATE_SEND_DATA:
559 case HTTPSTATE_SEND_DATA_TRAILER:
560 if (!c->is_packetized) {
561 /* for TCP, we output as much as we can (may need to put a limit) */
562 c->poll_entry = poll_entry;
564 poll_entry->events = POLLOUT;
567 /* when ffserver is doing the timing, we work by
568 looking at which packet need to be sent every
570 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
575 case HTTPSTATE_WAIT_REQUEST:
576 case HTTPSTATE_RECEIVE_DATA:
577 case HTTPSTATE_WAIT_FEED:
578 case RTSPSTATE_WAIT_REQUEST:
579 /* need to catch errors */
580 c->poll_entry = poll_entry;
582 poll_entry->events = POLLIN;/* Maybe this will work */
586 c->poll_entry = NULL;
592 /* wait for an event on one connection. We poll at least every
593 second to handle timeouts */
595 ret = poll(poll_table, poll_entry - poll_table, delay);
596 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
597 ff_neterrno() != FF_NETERROR(EINTR))
601 cur_time = av_gettime() / 1000;
603 if (need_to_start_children) {
604 need_to_start_children = 0;
605 start_children(first_feed);
608 /* now handle the events */
609 for(c = first_http_ctx; c != NULL; c = c_next) {
611 if (handle_connection(c) < 0) {
612 /* close and free the connection */
618 poll_entry = poll_table;
619 /* new HTTP connection request ? */
620 if (poll_entry->revents & POLLIN) {
621 new_connection(server_fd, 0);
624 /* new RTSP connection request ? */
625 if (poll_entry->revents & POLLIN) {
626 new_connection(rtsp_server_fd, 1);
631 /* start waiting for a new HTTP/RTSP request */
632 static void start_wait_request(HTTPContext *c, int is_rtsp)
634 c->buffer_ptr = c->buffer;
635 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
638 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
639 c->state = RTSPSTATE_WAIT_REQUEST;
641 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
642 c->state = HTTPSTATE_WAIT_REQUEST;
646 static void new_connection(int server_fd, int is_rtsp)
648 struct sockaddr_in from_addr;
650 HTTPContext *c = NULL;
652 len = sizeof(from_addr);
653 fd = accept(server_fd, (struct sockaddr *)&from_addr,
657 ff_socket_nonblock(fd, 1);
659 /* XXX: should output a warning page when coming
660 close to the connection limit */
661 if (nb_connections >= nb_max_connections)
664 /* add a new connection */
665 c = av_mallocz(sizeof(HTTPContext));
670 c->poll_entry = NULL;
671 c->from_addr = from_addr;
672 c->buffer_size = IOBUFFER_INIT_SIZE;
673 c->buffer = av_malloc(c->buffer_size);
677 c->next = first_http_ctx;
681 start_wait_request(c, is_rtsp);
693 static void close_connection(HTTPContext *c)
695 HTTPContext **cp, *c1;
697 AVFormatContext *ctx;
701 /* remove connection from list */
702 cp = &first_http_ctx;
703 while ((*cp) != NULL) {
712 /* remove references, if any (XXX: do it faster) */
713 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
718 /* remove connection associated resources */
722 /* close each frame parser */
723 for(i=0;i<c->fmt_in->nb_streams;i++) {
724 st = c->fmt_in->streams[i];
725 if (st->codec->codec) {
726 avcodec_close(st->codec);
729 av_close_input_file(c->fmt_in);
732 /* free RTP output streams if any */
735 nb_streams = c->stream->nb_streams;
737 for(i=0;i<nb_streams;i++) {
740 av_write_trailer(ctx);
743 h = c->rtp_handles[i];
751 if (!c->last_packet_sent) {
754 if (url_open_dyn_buf(&ctx->pb) >= 0) {
755 av_write_trailer(ctx);
756 url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
761 for(i=0; i<ctx->nb_streams; i++)
762 av_free(ctx->streams[i]);
764 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
765 current_bandwidth -= c->stream->bandwidth;
767 /* signal that there is no feed if we are the feeder socket */
768 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
769 c->stream->feed_opened = 0;
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 = recv(c->fd, c->buffer_ptr, 1, 0);
800 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
801 ff_neterrno() != FF_NETERROR(EINTR))
803 } else if (len == 0) {
806 /* search for end of request. */
808 c->buffer_ptr += len;
810 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
811 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
812 /* request found : parse it and reply */
813 if (c->state == HTTPSTATE_WAIT_REQUEST) {
814 ret = http_parse_request(c);
816 ret = rtsp_parse_request(c);
820 } else if (ptr >= c->buffer_end) {
821 /* request too long: cannot do anything */
823 } else goto read_loop;
827 case HTTPSTATE_SEND_HEADER:
828 if (c->poll_entry->revents & (POLLERR | POLLHUP))
831 /* no need to write if no events */
832 if (!(c->poll_entry->revents & POLLOUT))
834 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
836 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
837 ff_neterrno() != FF_NETERROR(EINTR)) {
838 /* error : close connection */
839 av_freep(&c->pb_buffer);
843 c->buffer_ptr += len;
845 c->stream->bytes_served += len;
846 c->data_count += len;
847 if (c->buffer_ptr >= c->buffer_end) {
848 av_freep(&c->pb_buffer);
853 /* all the buffer was sent : synchronize to the incoming stream */
854 c->state = HTTPSTATE_SEND_DATA_HEADER;
855 c->buffer_ptr = c->buffer_end = c->buffer;
860 case HTTPSTATE_SEND_DATA:
861 case HTTPSTATE_SEND_DATA_HEADER:
862 case HTTPSTATE_SEND_DATA_TRAILER:
863 /* for packetized output, we consider we can always write (the
864 input streams sets the speed). It may be better to verify
865 that we do not rely too much on the kernel queues */
866 if (!c->is_packetized) {
867 if (c->poll_entry->revents & (POLLERR | POLLHUP))
870 /* no need to read if no events */
871 if (!(c->poll_entry->revents & POLLOUT))
874 if (http_send_data(c) < 0)
876 /* close connection if trailer sent */
877 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
880 case HTTPSTATE_RECEIVE_DATA:
881 /* no need to read if no events */
882 if (c->poll_entry->revents & (POLLERR | POLLHUP))
884 if (!(c->poll_entry->revents & POLLIN))
886 if (http_receive_data(c) < 0)
889 case HTTPSTATE_WAIT_FEED:
890 /* no need to read if no events */
891 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
894 /* nothing to do, we'll be waken up by incoming feed packets */
897 case RTSPSTATE_SEND_REPLY:
898 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
899 av_freep(&c->pb_buffer);
902 /* no need to write if no events */
903 if (!(c->poll_entry->revents & POLLOUT))
905 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
907 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
908 ff_neterrno() != FF_NETERROR(EINTR)) {
909 /* error : close connection */
910 av_freep(&c->pb_buffer);
914 c->buffer_ptr += len;
915 c->data_count += len;
916 if (c->buffer_ptr >= c->buffer_end) {
917 /* all the buffer was sent : wait for a new request */
918 av_freep(&c->pb_buffer);
919 start_wait_request(c, 1);
923 case RTSPSTATE_SEND_PACKET:
924 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
925 av_freep(&c->packet_buffer);
928 /* no need to write if no events */
929 if (!(c->poll_entry->revents & POLLOUT))
931 len = send(c->fd, c->packet_buffer_ptr,
932 c->packet_buffer_end - c->packet_buffer_ptr, 0);
934 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
935 ff_neterrno() != FF_NETERROR(EINTR)) {
936 /* error : close connection */
937 av_freep(&c->packet_buffer);
941 c->packet_buffer_ptr += len;
942 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
943 /* all the buffer was sent : wait for a new request */
944 av_freep(&c->packet_buffer);
945 c->state = RTSPSTATE_WAIT_REQUEST;
949 case HTTPSTATE_READY:
958 static int extract_rates(char *rates, int ratelen, const char *request)
962 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
963 if (strncasecmp(p, "Pragma:", 7) == 0) {
964 const char *q = p + 7;
966 while (*q && *q != '\n' && isspace(*q))
969 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
975 memset(rates, 0xff, ratelen);
978 while (*q && *q != '\n' && *q != ':')
981 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
985 if (stream_no < ratelen && stream_no >= 0) {
986 rates[stream_no] = rate_no;
989 while (*q && *q != '\n' && !isspace(*q))
1006 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1009 int best_bitrate = 100000000;
1012 for (i = 0; i < feed->nb_streams; i++) {
1013 AVCodecContext *feed_codec = feed->streams[i]->codec;
1015 if (feed_codec->codec_id != codec->codec_id ||
1016 feed_codec->sample_rate != codec->sample_rate ||
1017 feed_codec->width != codec->width ||
1018 feed_codec->height != codec->height) {
1022 /* Potential stream */
1024 /* We want the fastest stream less than bit_rate, or the slowest
1025 * faster than bit_rate
1028 if (feed_codec->bit_rate <= bit_rate) {
1029 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1030 best_bitrate = feed_codec->bit_rate;
1034 if (feed_codec->bit_rate < best_bitrate) {
1035 best_bitrate = feed_codec->bit_rate;
1044 static int modify_current_stream(HTTPContext *c, char *rates)
1047 FFStream *req = c->stream;
1048 int action_required = 0;
1050 /* Not much we can do for a feed */
1054 for (i = 0; i < req->nb_streams; i++) {
1055 AVCodecContext *codec = req->streams[i]->codec;
1059 c->switch_feed_streams[i] = req->feed_streams[i];
1062 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1065 /* Wants off or slow */
1066 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1068 /* This doesn't work well when it turns off the only stream! */
1069 c->switch_feed_streams[i] = -2;
1070 c->feed_streams[i] = -2;
1075 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1076 action_required = 1;
1079 return action_required;
1083 static void do_switch_stream(HTTPContext *c, int i)
1085 if (c->switch_feed_streams[i] >= 0) {
1087 c->feed_streams[i] = c->switch_feed_streams[i];
1090 /* Now update the stream */
1092 c->switch_feed_streams[i] = -1;
1095 /* XXX: factorize in utils.c ? */
1096 /* XXX: take care with different space meaning */
1097 static void skip_spaces(const char **pp)
1101 while (*p == ' ' || *p == '\t')
1106 static void get_word(char *buf, int buf_size, const char **pp)
1114 while (!isspace(*p) && *p != '\0') {
1115 if ((q - buf) < buf_size - 1)
1124 static int validate_acl(FFStream *stream, HTTPContext *c)
1126 enum IPAddressAction last_action = IP_DENY;
1128 struct in_addr *src = &c->from_addr.sin_addr;
1129 unsigned long src_addr = src->s_addr;
1131 for (acl = stream->acl; acl; acl = acl->next) {
1132 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1133 return (acl->action == IP_ALLOW) ? 1 : 0;
1135 last_action = acl->action;
1138 /* Nothing matched, so return not the last action */
1139 return (last_action == IP_DENY) ? 1 : 0;
1142 /* compute the real filename of a file by matching it without its
1143 extensions to all the stream filenames */
1144 static void compute_real_filename(char *filename, int max_size)
1151 /* compute filename by matching without the file extensions */
1152 av_strlcpy(file1, filename, sizeof(file1));
1153 p = strrchr(file1, '.');
1156 for(stream = first_stream; stream != NULL; stream = stream->next) {
1157 av_strlcpy(file2, stream->filename, sizeof(file2));
1158 p = strrchr(file2, '.');
1161 if (!strcmp(file1, file2)) {
1162 av_strlcpy(filename, stream->filename, max_size);
1177 /* parse http request and prepare header */
1178 static int http_parse_request(HTTPContext *c)
1181 enum RedirType redir_type;
1183 char info[1024], filename[1024];
1187 const char *mime_type;
1191 char *useragent = 0;
1194 get_word(cmd, sizeof(cmd), (const char **)&p);
1195 av_strlcpy(c->method, cmd, sizeof(c->method));
1197 if (!strcmp(cmd, "GET"))
1199 else if (!strcmp(cmd, "POST"))
1204 get_word(url, sizeof(url), (const char **)&p);
1205 av_strlcpy(c->url, url, sizeof(c->url));
1207 get_word(protocol, sizeof(protocol), (const char **)&p);
1208 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1211 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1214 http_log("New connection: %s %s\n", cmd, url);
1216 /* find the filename and the optional info string in the request */
1217 p = strchr(url, '?');
1219 av_strlcpy(info, p, sizeof(info));
1225 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1227 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1228 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1230 if (*useragent && *useragent != '\n' && isspace(*useragent))
1234 p = strchr(p, '\n');
1241 redir_type = REDIR_NONE;
1242 if (match_ext(filename, "asx")) {
1243 redir_type = REDIR_ASX;
1244 filename[strlen(filename)-1] = 'f';
1245 } else if (match_ext(filename, "asf") &&
1246 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1247 /* if this isn't WMP or lookalike, return the redirector file */
1248 redir_type = REDIR_ASF;
1249 } else if (match_ext(filename, "rpm,ram")) {
1250 redir_type = REDIR_RAM;
1251 strcpy(filename + strlen(filename)-2, "m");
1252 } else if (match_ext(filename, "rtsp")) {
1253 redir_type = REDIR_RTSP;
1254 compute_real_filename(filename, sizeof(filename) - 1);
1255 } else if (match_ext(filename, "sdp")) {
1256 redir_type = REDIR_SDP;
1257 compute_real_filename(filename, sizeof(filename) - 1);
1260 // "redirect" / request to index.html
1261 if (!strlen(filename))
1262 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1264 stream = first_stream;
1265 while (stream != NULL) {
1266 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1268 stream = stream->next;
1270 if (stream == NULL) {
1271 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1276 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1277 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1279 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1280 c->http_error = 301;
1282 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1283 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1284 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1285 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1286 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1287 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1288 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1290 /* prepare output buffer */
1291 c->buffer_ptr = c->buffer;
1293 c->state = HTTPSTATE_SEND_HEADER;
1297 /* If this is WMP, get the rate information */
1298 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1299 if (modify_current_stream(c, ratebuf)) {
1300 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1301 if (c->switch_feed_streams[i] >= 0)
1302 do_switch_stream(c, i);
1307 /* If already streaming this feed, do not let start another feeder. */
1308 if (stream->feed_opened) {
1309 snprintf(msg, sizeof(msg), "This feed is already being received.");
1313 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1314 current_bandwidth += stream->bandwidth;
1317 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1318 c->http_error = 200;
1320 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1321 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1323 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1324 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The server is too busy to serve your request at this time.</p>\r\n");
1325 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec.</p>\r\n",
1326 current_bandwidth, max_bandwidth);
1327 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1329 /* prepare output buffer */
1330 c->buffer_ptr = c->buffer;
1332 c->state = HTTPSTATE_SEND_HEADER;
1336 if (redir_type != REDIR_NONE) {
1339 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1340 if (strncasecmp(p, "Host:", 5) == 0) {
1344 p = strchr(p, '\n');
1355 while (isspace(*hostinfo))
1358 eoh = strchr(hostinfo, '\n');
1360 if (eoh[-1] == '\r')
1363 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1364 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1365 hostbuf[eoh - hostinfo] = 0;
1367 c->http_error = 200;
1369 switch(redir_type) {
1371 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1372 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1373 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1374 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1375 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1376 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1377 hostbuf, filename, info);
1378 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1381 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1382 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1383 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1384 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1385 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1386 hostbuf, filename, info);
1389 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1390 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1391 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1392 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1393 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1394 hostbuf, filename, info);
1398 char hostname[256], *p;
1399 /* extract only hostname */
1400 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1401 p = strrchr(hostname, ':');
1404 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1405 /* XXX: incorrect mime type ? */
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1408 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1409 hostname, ntohs(my_rtsp_addr.sin_port),
1416 int sdp_data_size, len;
1417 struct sockaddr_in my_addr;
1419 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1420 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1421 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1423 len = sizeof(my_addr);
1424 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1426 /* XXX: should use a dynamic buffer */
1427 sdp_data_size = prepare_sdp_description(stream,
1430 if (sdp_data_size > 0) {
1431 memcpy(q, sdp_data, sdp_data_size);
1443 /* prepare output buffer */
1444 c->buffer_ptr = c->buffer;
1446 c->state = HTTPSTATE_SEND_HEADER;
1452 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1456 stream->conns_served++;
1458 /* XXX: add there authenticate and IP match */
1461 /* if post, it means a feed is being sent */
1462 if (!stream->is_feed) {
1463 /* However it might be a status report from WMP! Lets log the data
1464 * as it might come in handy one day
1469 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1470 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1474 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1475 client_id = strtol(p + 18, 0, 10);
1477 p = strchr(p, '\n');
1485 char *eol = strchr(logline, '\n');
1490 if (eol[-1] == '\r')
1492 http_log("%.*s\n", (int) (eol - logline), logline);
1493 c->suppress_log = 1;
1498 http_log("\nGot request:\n%s\n", c->buffer);
1501 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1504 /* Now we have to find the client_id */
1505 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1506 if (wmpc->wmp_client_id == client_id)
1511 if (modify_current_stream(wmpc, ratebuf)) {
1512 wmpc->switch_pending = 1;
1517 snprintf(msg, sizeof(msg), "POST command not handled");
1521 if (http_start_receive_data(c) < 0) {
1522 snprintf(msg, sizeof(msg), "could not open feed");
1526 c->state = HTTPSTATE_RECEIVE_DATA;
1531 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1532 http_log("\nGot request:\n%s\n", c->buffer);
1536 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1539 /* open input stream */
1540 if (open_input_stream(c, info) < 0) {
1541 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1545 /* prepare http header */
1547 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1548 mime_type = c->stream->fmt->mime_type;
1550 mime_type = "application/x-octet-stream";
1551 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1553 /* for asf, we need extra headers */
1554 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1555 /* Need to allocate a client id */
1557 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1559 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);
1561 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1562 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1564 /* prepare output buffer */
1566 c->buffer_ptr = c->buffer;
1568 c->state = HTTPSTATE_SEND_HEADER;
1571 c->http_error = 404;
1573 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1574 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1575 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1576 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1577 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1578 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1579 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1581 /* prepare output buffer */
1582 c->buffer_ptr = c->buffer;
1584 c->state = HTTPSTATE_SEND_HEADER;
1588 c->http_error = 200; /* horrible : we use this value to avoid
1589 going to the send data state */
1590 c->state = HTTPSTATE_SEND_HEADER;
1594 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1596 static const char *suffix = " kMGTP";
1599 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1602 url_fprintf(pb, "%"PRId64"%c", count, *s);
1605 static void compute_stats(HTTPContext *c)
1612 ByteIOContext pb1, *pb = &pb1;
1614 if (url_open_dyn_buf(pb) < 0) {
1615 /* XXX: return an error ? */
1616 c->buffer_ptr = c->buffer;
1617 c->buffer_end = c->buffer;
1621 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1622 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1623 url_fprintf(pb, "Pragma: no-cache\r\n");
1624 url_fprintf(pb, "\r\n");
1626 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1627 if (c->stream->feed_filename) {
1628 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1630 url_fprintf(pb, "</HEAD>\n<BODY>");
1631 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1633 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1634 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1635 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");
1636 stream = first_stream;
1637 while (stream != NULL) {
1638 char sfilename[1024];
1641 if (stream->feed != stream) {
1642 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1643 eosf = sfilename + strlen(sfilename);
1644 if (eosf - sfilename >= 4) {
1645 if (strcmp(eosf - 4, ".asf") == 0) {
1646 strcpy(eosf - 4, ".asx");
1647 } else if (strcmp(eosf - 3, ".rm") == 0) {
1648 strcpy(eosf - 3, ".ram");
1649 } else if (stream->fmt == &rtp_muxer) {
1650 /* generate a sample RTSP director if
1651 unicast. Generate an SDP redirector if
1653 eosf = strrchr(sfilename, '.');
1655 eosf = sfilename + strlen(sfilename);
1656 if (stream->is_multicast)
1657 strcpy(eosf, ".sdp");
1659 strcpy(eosf, ".rtsp");
1663 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1664 sfilename, stream->filename);
1665 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1666 stream->conns_served);
1667 fmt_bytecount(pb, stream->bytes_served);
1668 switch(stream->stream_type) {
1669 case STREAM_TYPE_LIVE:
1671 int audio_bit_rate = 0;
1672 int video_bit_rate = 0;
1673 const char *audio_codec_name = "";
1674 const char *video_codec_name = "";
1675 const char *audio_codec_name_extra = "";
1676 const char *video_codec_name_extra = "";
1678 for(i=0;i<stream->nb_streams;i++) {
1679 AVStream *st = stream->streams[i];
1680 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1681 switch(st->codec->codec_type) {
1682 case CODEC_TYPE_AUDIO:
1683 audio_bit_rate += st->codec->bit_rate;
1685 if (*audio_codec_name)
1686 audio_codec_name_extra = "...";
1687 audio_codec_name = codec->name;
1690 case CODEC_TYPE_VIDEO:
1691 video_bit_rate += st->codec->bit_rate;
1693 if (*video_codec_name)
1694 video_codec_name_extra = "...";
1695 video_codec_name = codec->name;
1698 case CODEC_TYPE_DATA:
1699 video_bit_rate += st->codec->bit_rate;
1705 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",
1708 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1709 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1711 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1713 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1715 url_fprintf(pb, "\n");
1719 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1723 stream = stream->next;
1725 url_fprintf(pb, "</TABLE>\n");
1727 stream = first_stream;
1728 while (stream != NULL) {
1729 if (stream->feed == stream) {
1730 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1732 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1734 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1739 /* This is somewhat linux specific I guess */
1740 snprintf(ps_cmd, sizeof(ps_cmd),
1741 "ps -o \"%%cpu,cputime\" --no-headers %d",
1744 pid_stat = popen(ps_cmd, "r");
1749 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1751 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1759 url_fprintf(pb, "<p>");
1761 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");
1763 for (i = 0; i < stream->nb_streams; i++) {
1764 AVStream *st = stream->streams[i];
1765 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1766 const char *type = "unknown";
1767 char parameters[64];
1771 switch(st->codec->codec_type) {
1772 case CODEC_TYPE_AUDIO:
1774 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1776 case CODEC_TYPE_VIDEO:
1778 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1779 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1784 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1785 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1787 url_fprintf(pb, "</table>\n");
1790 stream = stream->next;
1796 AVCodecContext *enc;
1800 stream = first_feed;
1801 while (stream != NULL) {
1802 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1803 url_fprintf(pb, "<TABLE>\n");
1804 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1805 for(i=0;i<stream->nb_streams;i++) {
1806 AVStream *st = stream->streams[i];
1807 FeedData *fdata = st->priv_data;
1810 avcodec_string(buf, sizeof(buf), enc);
1811 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1812 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1813 avg /= enc->frame_size;
1814 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1815 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1817 url_fprintf(pb, "</TABLE>\n");
1818 stream = stream->next_feed;
1823 /* connection status */
1824 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1826 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1827 nb_connections, nb_max_connections);
1829 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1830 current_bandwidth, max_bandwidth);
1832 url_fprintf(pb, "<TABLE>\n");
1833 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");
1834 c1 = first_http_ctx;
1836 while (c1 != NULL) {
1842 for (j = 0; j < c1->stream->nb_streams; j++) {
1843 if (!c1->stream->feed) {
1844 bitrate += c1->stream->streams[j]->codec->bit_rate;
1846 if (c1->feed_streams[j] >= 0) {
1847 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1854 p = inet_ntoa(c1->from_addr.sin_addr);
1855 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1857 c1->stream ? c1->stream->filename : "",
1858 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1861 http_state[c1->state]);
1862 fmt_bytecount(pb, bitrate);
1863 url_fprintf(pb, "<td align=right>");
1864 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1865 url_fprintf(pb, "<td align=right>");
1866 fmt_bytecount(pb, c1->data_count);
1867 url_fprintf(pb, "\n");
1870 url_fprintf(pb, "</TABLE>\n");
1875 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1876 url_fprintf(pb, "</BODY>\n</HTML>\n");
1878 len = url_close_dyn_buf(pb, &c->pb_buffer);
1879 c->buffer_ptr = c->pb_buffer;
1880 c->buffer_end = c->pb_buffer + len;
1883 /* check if the parser needs to be opened for stream i */
1884 static void open_parser(AVFormatContext *s, int i)
1886 AVStream *st = s->streams[i];
1889 if (!st->codec->codec) {
1890 codec = avcodec_find_decoder(st->codec->codec_id);
1891 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1892 st->codec->parse_only = 1;
1893 if (avcodec_open(st->codec, codec) < 0) {
1894 st->codec->parse_only = 0;
1900 static int open_input_stream(HTTPContext *c, const char *info)
1903 char input_filename[1024];
1908 /* find file name */
1909 if (c->stream->feed) {
1910 strcpy(input_filename, c->stream->feed->feed_filename);
1911 buf_size = FFM_PACKET_SIZE;
1912 /* compute position (absolute time) */
1913 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1914 stream_pos = parse_date(buf, 0);
1915 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1916 int prebuffer = strtol(buf, 0, 10);
1917 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1919 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1922 strcpy(input_filename, c->stream->feed_filename);
1924 /* compute position (relative time) */
1925 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1926 stream_pos = parse_date(buf, 1);
1931 if (input_filename[0] == '\0')
1935 { time_t when = stream_pos / 1000000;
1936 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1941 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1942 buf_size, c->stream->ap_in) < 0) {
1943 http_log("%s not found", input_filename);
1948 /* open each parser */
1949 for(i=0;i<s->nb_streams;i++)
1952 /* choose stream as clock source (we favorize video stream if
1953 present) for packet sending */
1954 c->pts_stream_index = 0;
1955 for(i=0;i<c->stream->nb_streams;i++) {
1956 if (c->pts_stream_index == 0 &&
1957 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1958 c->pts_stream_index = i;
1963 if (c->fmt_in->iformat->read_seek) {
1964 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1967 /* set the start time (needed for maxtime and RTP packet timing) */
1968 c->start_time = cur_time;
1969 c->first_pts = AV_NOPTS_VALUE;
1973 /* return the server clock (in us) */
1974 static int64_t get_server_clock(HTTPContext *c)
1976 /* compute current pts value from system time */
1977 return (cur_time - c->start_time) * 1000;
1980 /* return the estimated time at which the current packet must be sent
1982 static int64_t get_packet_send_clock(HTTPContext *c)
1984 int bytes_left, bytes_sent, frame_bytes;
1986 frame_bytes = c->cur_frame_bytes;
1987 if (frame_bytes <= 0) {
1990 bytes_left = c->buffer_end - c->buffer_ptr;
1991 bytes_sent = frame_bytes - bytes_left;
1992 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1997 static int http_prepare_data(HTTPContext *c)
2000 AVFormatContext *ctx;
2002 av_freep(&c->pb_buffer);
2004 case HTTPSTATE_SEND_DATA_HEADER:
2005 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2006 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2007 sizeof(c->fmt_ctx.author));
2008 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2009 sizeof(c->fmt_ctx.comment));
2010 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2011 sizeof(c->fmt_ctx.copyright));
2012 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2013 sizeof(c->fmt_ctx.title));
2015 /* open output stream by using specified codecs */
2016 c->fmt_ctx.oformat = c->stream->fmt;
2017 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2018 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2021 st = av_mallocz(sizeof(AVStream));
2022 st->codec= avcodec_alloc_context();
2023 c->fmt_ctx.streams[i] = st;
2024 /* if file or feed, then just take streams from FFStream struct */
2025 if (!c->stream->feed ||
2026 c->stream->feed == c->stream)
2027 src = c->stream->streams[i];
2029 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2033 st->codec->frame_number = 0; /* XXX: should be done in
2034 AVStream, not in codec */
2035 /* I'm pretty sure that this is not correct...
2036 * However, without it, we crash
2038 st->codec->coded_frame = &dummy_frame;
2040 c->got_key_frame = 0;
2042 /* prepare header and save header data in a stream */
2043 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2044 /* XXX: potential leak */
2047 c->fmt_ctx.pb.is_streamed = 1;
2049 av_set_parameters(&c->fmt_ctx, NULL);
2050 if (av_write_header(&c->fmt_ctx) < 0)
2053 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2054 c->buffer_ptr = c->pb_buffer;
2055 c->buffer_end = c->pb_buffer + len;
2057 c->state = HTTPSTATE_SEND_DATA;
2058 c->last_packet_sent = 0;
2060 case HTTPSTATE_SEND_DATA:
2061 /* find a new packet */
2065 /* read a packet from the input stream */
2066 if (c->stream->feed) {
2067 ffm_set_write_index(c->fmt_in,
2068 c->stream->feed->feed_write_index,
2069 c->stream->feed->feed_size);
2072 if (c->stream->max_time &&
2073 c->stream->max_time + c->start_time - cur_time < 0) {
2074 /* We have timed out */
2075 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2078 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2079 if (c->stream->feed && c->stream->feed->feed_opened) {
2080 /* if coming from feed, it means we reached the end of the
2081 ffm file, so must wait for more data */
2082 c->state = HTTPSTATE_WAIT_FEED;
2083 return 1; /* state changed */
2085 if (c->stream->loop) {
2086 av_close_input_file(c->fmt_in);
2088 if (open_input_stream(c, "") < 0)
2093 /* must send trailer now because eof or error */
2094 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2098 /* update first pts if needed */
2099 if (c->first_pts == AV_NOPTS_VALUE) {
2100 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2101 c->start_time = cur_time;
2103 /* send it to the appropriate stream */
2104 if (c->stream->feed) {
2105 /* if coming from a feed, select the right stream */
2106 if (c->switch_pending) {
2107 c->switch_pending = 0;
2108 for(i=0;i<c->stream->nb_streams;i++) {
2109 if (c->switch_feed_streams[i] == pkt.stream_index) {
2110 if (pkt.flags & PKT_FLAG_KEY) {
2111 do_switch_stream(c, i);
2114 if (c->switch_feed_streams[i] >= 0) {
2115 c->switch_pending = 1;
2119 for(i=0;i<c->stream->nb_streams;i++) {
2120 if (c->feed_streams[i] == pkt.stream_index) {
2121 pkt.stream_index = i;
2122 if (pkt.flags & PKT_FLAG_KEY) {
2123 c->got_key_frame |= 1 << i;
2125 /* See if we have all the key frames, then
2126 * we start to send. This logic is not quite
2127 * right, but it works for the case of a
2128 * single video stream with one or more
2129 * audio streams (for which every frame is
2130 * typically a key frame).
2132 if (!c->stream->send_on_key ||
2133 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2139 AVCodecContext *codec;
2142 /* specific handling for RTP: we use several
2143 output stream (one for each RTP
2144 connection). XXX: need more abstract handling */
2145 if (c->is_packetized) {
2147 /* compute send time and duration */
2148 st = c->fmt_in->streams[pkt.stream_index];
2149 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2150 if (st->start_time != AV_NOPTS_VALUE)
2151 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2152 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2154 printf("index=%d pts=%0.3f duration=%0.6f\n",
2156 (double)c->cur_pts /
2158 (double)c->cur_frame_duration /
2161 /* find RTP context */
2162 c->packet_stream_index = pkt.stream_index;
2163 ctx = c->rtp_ctx[c->packet_stream_index];
2165 av_free_packet(&pkt);
2168 codec = ctx->streams[0]->codec;
2169 /* only one stream per RTP connection */
2170 pkt.stream_index = 0;
2174 codec = ctx->streams[pkt.stream_index]->codec;
2177 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2178 if (c->is_packetized) {
2179 int max_packet_size;
2180 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2181 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2183 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2184 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2186 ret = url_open_dyn_buf(&ctx->pb);
2189 /* XXX: potential leak */
2192 if (pkt.dts != AV_NOPTS_VALUE)
2193 pkt.dts = av_rescale_q(pkt.dts,
2194 c->fmt_in->streams[pkt.stream_index]->time_base,
2195 ctx->streams[pkt.stream_index]->time_base);
2196 if (pkt.pts != AV_NOPTS_VALUE)
2197 pkt.pts = av_rescale_q(pkt.pts,
2198 c->fmt_in->streams[pkt.stream_index]->time_base,
2199 ctx->streams[pkt.stream_index]->time_base);
2200 if (av_write_frame(ctx, &pkt)) {
2201 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2204 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2205 c->cur_frame_bytes = len;
2206 c->buffer_ptr = c->pb_buffer;
2207 c->buffer_end = c->pb_buffer + len;
2209 codec->frame_number++;
2213 av_free_packet(&pkt);
2219 case HTTPSTATE_SEND_DATA_TRAILER:
2220 /* last packet test ? */
2221 if (c->last_packet_sent || c->is_packetized)
2224 /* prepare header */
2225 if (url_open_dyn_buf(&ctx->pb) < 0) {
2226 /* XXX: potential leak */
2229 av_write_trailer(ctx);
2230 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2231 c->buffer_ptr = c->pb_buffer;
2232 c->buffer_end = c->pb_buffer + len;
2234 c->last_packet_sent = 1;
2240 /* should convert the format at the same time */
2241 /* send data starting at c->buffer_ptr to the output connection
2242 (either UDP or TCP connection) */
2243 static int http_send_data(HTTPContext *c)
2248 if (c->buffer_ptr >= c->buffer_end) {
2249 ret = http_prepare_data(c);
2252 else if (ret != 0) {
2253 /* state change requested */
2257 if (c->is_packetized) {
2258 /* RTP data output */
2259 len = c->buffer_end - c->buffer_ptr;
2261 /* fail safe - should never happen */
2263 c->buffer_ptr = c->buffer_end;
2266 len = (c->buffer_ptr[0] << 24) |
2267 (c->buffer_ptr[1] << 16) |
2268 (c->buffer_ptr[2] << 8) |
2270 if (len > (c->buffer_end - c->buffer_ptr))
2272 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2273 /* nothing to send yet: we can wait */
2277 c->data_count += len;
2278 update_datarate(&c->datarate, c->data_count);
2280 c->stream->bytes_served += len;
2282 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2283 /* RTP packets are sent inside the RTSP TCP connection */
2284 ByteIOContext pb1, *pb = &pb1;
2285 int interleaved_index, size;
2287 HTTPContext *rtsp_c;
2290 /* if no RTSP connection left, error */
2293 /* if already sending something, then wait. */
2294 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
2297 if (url_open_dyn_buf(pb) < 0)
2299 interleaved_index = c->packet_stream_index * 2;
2300 /* RTCP packets are sent at odd indexes */
2301 if (c->buffer_ptr[1] == 200)
2302 interleaved_index++;
2303 /* write RTSP TCP header */
2305 header[1] = interleaved_index;
2306 header[2] = len >> 8;
2308 put_buffer(pb, header, 4);
2309 /* write RTP packet data */
2311 put_buffer(pb, c->buffer_ptr, len);
2312 size = url_close_dyn_buf(pb, &c->packet_buffer);
2313 /* prepare asynchronous TCP sending */
2314 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2315 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2316 c->buffer_ptr += len;
2318 /* send everything we can NOW */
2319 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2320 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2322 rtsp_c->packet_buffer_ptr += len;
2324 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2325 /* if we could not send all the data, we will
2326 send it later, so a new state is needed to
2327 "lock" the RTSP TCP connection */
2328 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2331 /* all data has been sent */
2332 av_freep(&c->packet_buffer);
2335 /* send RTP packet directly in UDP */
2337 url_write(c->rtp_handles[c->packet_stream_index],
2338 c->buffer_ptr, len);
2339 c->buffer_ptr += len;
2340 /* here we continue as we can send several packets per 10 ms slot */
2343 /* TCP data output */
2344 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2346 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2347 ff_neterrno() != FF_NETERROR(EINTR)) {
2348 /* error : close connection */
2354 c->buffer_ptr += len;
2356 c->data_count += len;
2357 update_datarate(&c->datarate, c->data_count);
2359 c->stream->bytes_served += len;
2367 static int http_start_receive_data(HTTPContext *c)
2371 if (c->stream->feed_opened)
2374 /* Don't permit writing to this one */
2375 if (c->stream->readonly)
2379 fd = open(c->stream->feed_filename, O_RDWR);
2384 c->stream->feed_write_index = ffm_read_write_index(fd);
2385 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2386 lseek(fd, 0, SEEK_SET);
2388 /* init buffer input */
2389 c->buffer_ptr = c->buffer;
2390 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2391 c->stream->feed_opened = 1;
2395 static int http_receive_data(HTTPContext *c)
2399 if (c->buffer_end > c->buffer_ptr) {
2402 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2404 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2405 ff_neterrno() != FF_NETERROR(EINTR)) {
2406 /* error : close connection */
2409 } else if (len == 0) {
2410 /* end of connection : close it */
2413 c->buffer_ptr += len;
2414 c->data_count += len;
2415 update_datarate(&c->datarate, c->data_count);
2419 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2420 if (c->buffer[0] != 'f' ||
2421 c->buffer[1] != 'm') {
2422 http_log("Feed stream has become desynchronized -- disconnecting\n");
2427 if (c->buffer_ptr >= c->buffer_end) {
2428 FFStream *feed = c->stream;
2429 /* a packet has been received : write it in the store, except
2431 if (c->data_count > FFM_PACKET_SIZE) {
2433 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2434 /* XXX: use llseek or url_seek */
2435 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2436 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2438 feed->feed_write_index += FFM_PACKET_SIZE;
2439 /* update file size */
2440 if (feed->feed_write_index > c->stream->feed_size)
2441 feed->feed_size = feed->feed_write_index;
2443 /* handle wrap around if max file size reached */
2444 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2445 feed->feed_write_index = FFM_PACKET_SIZE;
2448 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2450 /* wake up any waiting connections */
2451 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2452 if (c1->state == HTTPSTATE_WAIT_FEED &&
2453 c1->stream->feed == c->stream->feed) {
2454 c1->state = HTTPSTATE_SEND_DATA;
2458 /* We have a header in our hands that contains useful data */
2460 AVInputFormat *fmt_in;
2461 ByteIOContext *pb = &s.pb;
2464 memset(&s, 0, sizeof(s));
2466 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2467 pb->buf_end = c->buffer_end; /* ?? */
2468 pb->is_streamed = 1;
2470 /* use feed output format name to find corresponding input format */
2471 fmt_in = av_find_input_format(feed->fmt->name);
2475 if (fmt_in->priv_data_size > 0) {
2476 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2482 if (fmt_in->read_header(&s, 0) < 0) {
2483 av_freep(&s.priv_data);
2487 /* Now we have the actual streams */
2488 if (s.nb_streams != feed->nb_streams) {
2489 av_freep(&s.priv_data);
2492 for (i = 0; i < s.nb_streams; i++) {
2493 memcpy(feed->streams[i]->codec,
2494 s.streams[i]->codec, sizeof(AVCodecContext));
2496 av_freep(&s.priv_data);
2498 c->buffer_ptr = c->buffer;
2503 c->stream->feed_opened = 0;
2508 /********************************************************************/
2511 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2518 switch(error_number) {
2519 case RTSP_STATUS_OK:
2522 case RTSP_STATUS_METHOD:
2523 str = "Method Not Allowed";
2525 case RTSP_STATUS_BANDWIDTH:
2526 str = "Not Enough Bandwidth";
2528 case RTSP_STATUS_SESSION:
2529 str = "Session Not Found";
2531 case RTSP_STATUS_STATE:
2532 str = "Method Not Valid in This State";
2534 case RTSP_STATUS_AGGREGATE:
2535 str = "Aggregate operation not allowed";
2537 case RTSP_STATUS_ONLY_AGGREGATE:
2538 str = "Only aggregate operation allowed";
2540 case RTSP_STATUS_TRANSPORT:
2541 str = "Unsupported transport";
2543 case RTSP_STATUS_INTERNAL:
2544 str = "Internal Server Error";
2546 case RTSP_STATUS_SERVICE:
2547 str = "Service Unavailable";
2549 case RTSP_STATUS_VERSION:
2550 str = "RTSP Version not supported";
2553 str = "Unknown Error";
2557 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2558 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2560 /* output GMT time */
2564 p = buf2 + strlen(p) - 1;
2567 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2570 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2572 rtsp_reply_header(c, error_number);
2573 url_fprintf(c->pb, "\r\n");
2576 static int rtsp_parse_request(HTTPContext *c)
2578 const char *p, *p1, *p2;
2585 RTSPHeader header1, *header = &header1;
2587 c->buffer_ptr[0] = '\0';
2590 get_word(cmd, sizeof(cmd), &p);
2591 get_word(url, sizeof(url), &p);
2592 get_word(protocol, sizeof(protocol), &p);
2594 av_strlcpy(c->method, cmd, sizeof(c->method));
2595 av_strlcpy(c->url, url, sizeof(c->url));
2596 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2599 if (url_open_dyn_buf(c->pb) < 0) {
2600 /* XXX: cannot do more */
2601 c->pb = NULL; /* safety */
2605 /* check version name */
2606 if (strcmp(protocol, "RTSP/1.0") != 0) {
2607 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2611 /* parse each header line */
2612 memset(header, 0, sizeof(RTSPHeader));
2613 /* skip to next line */
2614 while (*p != '\n' && *p != '\0')
2618 while (*p != '\0') {
2619 p1 = strchr(p, '\n');
2623 if (p2 > p && p2[-1] == '\r')
2625 /* skip empty line */
2629 if (len > sizeof(line) - 1)
2630 len = sizeof(line) - 1;
2631 memcpy(line, p, len);
2633 rtsp_parse_line(header, line);
2637 /* handle sequence number */
2638 c->seq = header->seq;
2640 if (!strcmp(cmd, "DESCRIBE")) {
2641 rtsp_cmd_describe(c, url);
2642 } else if (!strcmp(cmd, "OPTIONS")) {
2643 rtsp_cmd_options(c, url);
2644 } else if (!strcmp(cmd, "SETUP")) {
2645 rtsp_cmd_setup(c, url, header);
2646 } else if (!strcmp(cmd, "PLAY")) {
2647 rtsp_cmd_play(c, url, header);
2648 } else if (!strcmp(cmd, "PAUSE")) {
2649 rtsp_cmd_pause(c, url, header);
2650 } else if (!strcmp(cmd, "TEARDOWN")) {
2651 rtsp_cmd_teardown(c, url, header);
2653 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2656 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2657 c->pb = NULL; /* safety */
2659 /* XXX: cannot do more */
2662 c->buffer_ptr = c->pb_buffer;
2663 c->buffer_end = c->pb_buffer + len;
2664 c->state = RTSPSTATE_SEND_REPLY;
2668 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2670 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2671 struct in_addr my_ip)
2673 ByteIOContext pb1, *pb = &pb1;
2674 int i, payload_type, port, private_payload_type, j;
2675 const char *ipstr, *title, *mediatype;
2678 if (url_open_dyn_buf(pb) < 0)
2681 /* general media info */
2683 url_fprintf(pb, "v=0\n");
2684 ipstr = inet_ntoa(my_ip);
2685 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2686 title = stream->title;
2687 if (title[0] == '\0')
2689 url_fprintf(pb, "s=%s\n", title);
2690 if (stream->comment[0] != '\0')
2691 url_fprintf(pb, "i=%s\n", stream->comment);
2692 if (stream->is_multicast) {
2693 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2695 /* for each stream, we output the necessary info */
2696 private_payload_type = RTP_PT_PRIVATE;
2697 for(i = 0; i < stream->nb_streams; i++) {
2698 st = stream->streams[i];
2699 if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
2700 mediatype = "video";
2702 switch(st->codec->codec_type) {
2703 case CODEC_TYPE_AUDIO:
2704 mediatype = "audio";
2706 case CODEC_TYPE_VIDEO:
2707 mediatype = "video";
2710 mediatype = "application";
2714 /* NOTE: the port indication is not correct in case of
2715 unicast. It is not an issue because RTSP gives it */
2716 payload_type = rtp_get_payload_type(st->codec);
2717 if (payload_type < 0)
2718 payload_type = private_payload_type++;
2719 if (stream->is_multicast) {
2720 port = stream->multicast_port + 2 * i;
2724 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2725 mediatype, port, payload_type);
2726 if (payload_type >= RTP_PT_PRIVATE) {
2727 /* for private payload type, we need to give more info */
2728 switch(st->codec->codec_id) {
2729 case CODEC_ID_MPEG4:
2732 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2733 payload_type, 90000);
2734 /* we must also add the mpeg4 header */
2735 data = st->codec->extradata;
2737 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2738 for(j=0;j<st->codec->extradata_size;j++) {
2739 url_fprintf(pb, "%02x", data[j]);
2741 url_fprintf(pb, "\n");
2746 /* XXX: add other codecs ? */
2750 url_fprintf(pb, "a=control:streamid=%d\n", i);
2752 return url_close_dyn_buf(pb, pbuffer);
2754 url_close_dyn_buf(pb, pbuffer);
2759 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2761 // rtsp_reply_header(c, RTSP_STATUS_OK);
2762 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2763 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2764 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2765 url_fprintf(c->pb, "\r\n");
2768 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2774 int content_length, len;
2775 struct sockaddr_in my_addr;
2777 /* find which url is asked */
2778 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2783 for(stream = first_stream; stream != NULL; stream = stream->next) {
2784 if (!stream->is_feed && stream->fmt == &rtp_muxer &&
2785 !strcmp(path, stream->filename)) {
2789 /* no stream found */
2790 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2794 /* prepare the media description in sdp format */
2796 /* get the host IP */
2797 len = sizeof(my_addr);
2798 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2799 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2800 if (content_length < 0) {
2801 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2804 rtsp_reply_header(c, RTSP_STATUS_OK);
2805 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2806 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2807 url_fprintf(c->pb, "\r\n");
2808 put_buffer(c->pb, content, content_length);
2811 static HTTPContext *find_rtp_session(const char *session_id)
2815 if (session_id[0] == '\0')
2818 for(c = first_http_ctx; c != NULL; c = c->next) {
2819 if (!strcmp(c->session_id, session_id))
2825 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2827 RTSPTransportField *th;
2830 for(i=0;i<h->nb_transports;i++) {
2831 th = &h->transports[i];
2832 if (th->protocol == protocol)
2838 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2842 int stream_index, port;
2847 RTSPTransportField *th;
2848 struct sockaddr_in dest_addr;
2849 RTSPActionServerSetup setup;
2851 /* find which url is asked */
2852 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2857 /* now check each stream */
2858 for(stream = first_stream; stream != NULL; stream = stream->next) {
2859 if (!stream->is_feed && stream->fmt == &rtp_muxer) {
2860 /* accept aggregate filenames only if single stream */
2861 if (!strcmp(path, stream->filename)) {
2862 if (stream->nb_streams != 1) {
2863 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2870 for(stream_index = 0; stream_index < stream->nb_streams;
2872 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2873 stream->filename, stream_index);
2874 if (!strcmp(path, buf))
2879 /* no stream found */
2880 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2884 /* generate session id if needed */
2885 if (h->session_id[0] == '\0') {
2886 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2887 av_random(&random_state), av_random(&random_state));
2890 /* find rtp session, and create it if none found */
2891 rtp_c = find_rtp_session(h->session_id);
2893 /* always prefer UDP */
2894 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2896 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2898 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2903 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2906 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2910 /* open input stream */
2911 if (open_input_stream(rtp_c, "") < 0) {
2912 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2917 /* test if stream is OK (test needed because several SETUP needs
2918 to be done for a given file) */
2919 if (rtp_c->stream != stream) {
2920 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2924 /* test if stream is already set up */
2925 if (rtp_c->rtp_ctx[stream_index]) {
2926 rtsp_reply_error(c, RTSP_STATUS_STATE);
2930 /* check transport */
2931 th = find_transport(h, rtp_c->rtp_protocol);
2932 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2933 th->client_port_min <= 0)) {
2934 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2938 /* setup default options */
2939 setup.transport_option[0] = '\0';
2940 dest_addr = rtp_c->from_addr;
2941 dest_addr.sin_port = htons(th->client_port_min);
2944 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2945 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2949 /* now everything is OK, so we can send the connection parameters */
2950 rtsp_reply_header(c, RTSP_STATUS_OK);
2952 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2954 switch(rtp_c->rtp_protocol) {
2955 case RTSP_PROTOCOL_RTP_UDP:
2956 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2957 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2958 "client_port=%d-%d;server_port=%d-%d",
2959 th->client_port_min, th->client_port_min + 1,
2962 case RTSP_PROTOCOL_RTP_TCP:
2963 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2964 stream_index * 2, stream_index * 2 + 1);
2969 if (setup.transport_option[0] != '\0') {
2970 url_fprintf(c->pb, ";%s", setup.transport_option);
2972 url_fprintf(c->pb, "\r\n");
2975 url_fprintf(c->pb, "\r\n");
2979 /* find an rtp connection by using the session ID. Check consistency
2981 static HTTPContext *find_rtp_session_with_url(const char *url,
2982 const char *session_id)
2990 rtp_c = find_rtp_session(session_id);
2994 /* find which url is asked */
2995 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2999 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3000 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3001 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3002 rtp_c->stream->filename, s);
3003 if(!strncmp(path, buf, sizeof(buf))) {
3004 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3011 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
3015 rtp_c = find_rtp_session_with_url(url, h->session_id);
3017 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3021 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3022 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3023 rtp_c->state != HTTPSTATE_READY) {
3024 rtsp_reply_error(c, RTSP_STATUS_STATE);
3029 /* XXX: seek in stream */
3030 if (h->range_start != AV_NOPTS_VALUE) {
3031 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3032 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3036 rtp_c->state = HTTPSTATE_SEND_DATA;
3038 /* now everything is OK, so we can send the connection parameters */
3039 rtsp_reply_header(c, RTSP_STATUS_OK);
3041 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3042 url_fprintf(c->pb, "\r\n");
3045 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3049 rtp_c = find_rtp_session_with_url(url, h->session_id);
3051 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3055 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3056 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3057 rtsp_reply_error(c, RTSP_STATUS_STATE);
3061 rtp_c->state = HTTPSTATE_READY;
3062 rtp_c->first_pts = AV_NOPTS_VALUE;
3063 /* now everything is OK, so we can send the connection parameters */
3064 rtsp_reply_header(c, RTSP_STATUS_OK);
3066 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3067 url_fprintf(c->pb, "\r\n");
3070 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3073 char session_id[32];
3075 rtp_c = find_rtp_session_with_url(url, h->session_id);
3077 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3081 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3083 /* abort the session */
3084 close_connection(rtp_c);
3086 /* now everything is OK, so we can send the connection parameters */
3087 rtsp_reply_header(c, RTSP_STATUS_OK);
3089 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3090 url_fprintf(c->pb, "\r\n");
3094 /********************************************************************/
3097 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3098 FFStream *stream, const char *session_id,
3099 enum RTSPProtocol rtp_protocol)
3101 HTTPContext *c = NULL;
3102 const char *proto_str;
3104 /* XXX: should output a warning page when coming
3105 close to the connection limit */
3106 if (nb_connections >= nb_max_connections)
3109 /* add a new connection */
3110 c = av_mallocz(sizeof(HTTPContext));
3115 c->poll_entry = NULL;
3116 c->from_addr = *from_addr;
3117 c->buffer_size = IOBUFFER_INIT_SIZE;
3118 c->buffer = av_malloc(c->buffer_size);
3123 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3124 c->state = HTTPSTATE_READY;
3125 c->is_packetized = 1;
3126 c->rtp_protocol = rtp_protocol;
3128 /* protocol is shown in statistics */
3129 switch(c->rtp_protocol) {
3130 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3131 proto_str = "MCAST";
3133 case RTSP_PROTOCOL_RTP_UDP:
3136 case RTSP_PROTOCOL_RTP_TCP:
3143 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3144 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3146 current_bandwidth += stream->bandwidth;
3148 c->next = first_http_ctx;
3160 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3161 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3163 static int rtp_new_av_stream(HTTPContext *c,
3164 int stream_index, struct sockaddr_in *dest_addr,
3165 HTTPContext *rtsp_c)
3167 AVFormatContext *ctx;
3173 int max_packet_size;
3175 /* now we can open the relevant output stream */
3176 ctx = av_alloc_format_context();
3179 ctx->oformat = &rtp_muxer;
3181 st = av_mallocz(sizeof(AVStream));
3184 st->codec= avcodec_alloc_context();
3185 ctx->nb_streams = 1;
3186 ctx->streams[0] = st;
3188 if (!c->stream->feed ||
3189 c->stream->feed == c->stream) {
3190 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3193 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3196 st->priv_data = NULL;
3198 /* build destination RTP address */
3199 ipaddr = inet_ntoa(dest_addr->sin_addr);
3201 switch(c->rtp_protocol) {
3202 case RTSP_PROTOCOL_RTP_UDP:
3203 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3206 /* XXX: also pass as parameter to function ? */
3207 if (c->stream->is_multicast) {
3209 ttl = c->stream->multicast_ttl;
3212 snprintf(ctx->filename, sizeof(ctx->filename),
3213 "rtp://%s:%d?multicast=1&ttl=%d",
3214 ipaddr, ntohs(dest_addr->sin_port), ttl);
3216 snprintf(ctx->filename, sizeof(ctx->filename),
3217 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3220 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3222 c->rtp_handles[stream_index] = h;
3223 max_packet_size = url_get_max_packet_size(h);
3225 case RTSP_PROTOCOL_RTP_TCP:
3228 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3234 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3235 ipaddr, ntohs(dest_addr->sin_port),
3237 c->stream->filename, stream_index, c->protocol);
3239 /* normally, no packets should be output here, but the packet size may be checked */
3240 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3241 /* XXX: close stream */
3244 av_set_parameters(ctx, NULL);
3245 if (av_write_header(ctx) < 0) {
3252 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3255 c->rtp_ctx[stream_index] = ctx;
3259 /********************************************************************/
3260 /* ffserver initialization */
3262 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3266 fst = av_mallocz(sizeof(AVStream));
3269 fst->codec= avcodec_alloc_context();
3270 fst->priv_data = av_mallocz(sizeof(FeedData));
3271 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3272 fst->codec->coded_frame = &dummy_frame;
3273 fst->index = stream->nb_streams;
3274 av_set_pts_info(fst, 33, 1, 90000);
3275 stream->streams[stream->nb_streams++] = fst;
3279 /* return the stream number in the feed */
3280 static int add_av_stream(FFStream *feed, AVStream *st)
3283 AVCodecContext *av, *av1;
3287 for(i=0;i<feed->nb_streams;i++) {
3288 st = feed->streams[i];
3290 if (av1->codec_id == av->codec_id &&
3291 av1->codec_type == av->codec_type &&
3292 av1->bit_rate == av->bit_rate) {
3294 switch(av->codec_type) {
3295 case CODEC_TYPE_AUDIO:
3296 if (av1->channels == av->channels &&
3297 av1->sample_rate == av->sample_rate)
3300 case CODEC_TYPE_VIDEO:
3301 if (av1->width == av->width &&
3302 av1->height == av->height &&
3303 av1->time_base.den == av->time_base.den &&
3304 av1->time_base.num == av->time_base.num &&
3305 av1->gop_size == av->gop_size)
3314 fst = add_av_stream1(feed, av);
3317 return feed->nb_streams - 1;
3322 static void remove_stream(FFStream *stream)
3326 while (*ps != NULL) {
3327 if (*ps == stream) {
3335 /* specific mpeg4 handling : we extract the raw parameters */
3336 static void extract_mpeg4_header(AVFormatContext *infile)
3338 int mpeg4_count, i, size;
3344 for(i=0;i<infile->nb_streams;i++) {
3345 st = infile->streams[i];
3346 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3347 st->codec->extradata_size == 0) {
3354 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3355 while (mpeg4_count > 0) {
3356 if (av_read_packet(infile, &pkt) < 0)
3358 st = infile->streams[pkt.stream_index];
3359 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3360 st->codec->extradata_size == 0) {
3361 av_freep(&st->codec->extradata);
3362 /* fill extradata with the header */
3363 /* XXX: we make hard suppositions here ! */
3365 while (p < pkt.data + pkt.size - 4) {
3366 /* stop when vop header is found */
3367 if (p[0] == 0x00 && p[1] == 0x00 &&
3368 p[2] == 0x01 && p[3] == 0xb6) {
3369 size = p - pkt.data;
3370 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3371 st->codec->extradata = av_malloc(size);
3372 st->codec->extradata_size = size;
3373 memcpy(st->codec->extradata, pkt.data, size);
3380 av_free_packet(&pkt);
3384 /* compute the needed AVStream for each file */
3385 static void build_file_streams(void)
3387 FFStream *stream, *stream_next;
3388 AVFormatContext *infile;
3391 /* gather all streams */
3392 for(stream = first_stream; stream != NULL; stream = stream_next) {
3393 stream_next = stream->next;
3394 if (stream->stream_type == STREAM_TYPE_LIVE &&
3396 /* the stream comes from a file */
3397 /* try to open the file */
3399 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3400 if (stream->fmt == &rtp_muxer) {
3401 /* specific case : if transport stream output to RTP,
3402 we use a raw transport stream reader */
3403 stream->ap_in->mpeg2ts_raw = 1;
3404 stream->ap_in->mpeg2ts_compute_pcr = 1;
3407 if (av_open_input_file(&infile, stream->feed_filename,
3408 stream->ifmt, 0, stream->ap_in) < 0) {
3409 http_log("%s not found", stream->feed_filename);
3410 /* remove stream (no need to spend more time on it) */
3412 remove_stream(stream);
3414 /* find all the AVStreams inside and reference them in
3416 if (av_find_stream_info(infile) < 0) {
3417 http_log("Could not find codec parameters from '%s'",
3418 stream->feed_filename);
3419 av_close_input_file(infile);
3422 extract_mpeg4_header(infile);
3424 for(i=0;i<infile->nb_streams;i++) {
3425 add_av_stream1(stream, infile->streams[i]->codec);
3427 av_close_input_file(infile);
3433 /* compute the needed AVStream for each feed */
3434 static void build_feed_streams(void)
3436 FFStream *stream, *feed;
3439 /* gather all streams */
3440 for(stream = first_stream; stream != NULL; stream = stream->next) {
3441 feed = stream->feed;
3443 if (!stream->is_feed) {
3444 /* we handle a stream coming from a feed */
3445 for(i=0;i<stream->nb_streams;i++) {
3446 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3452 /* gather all streams */
3453 for(stream = first_stream; stream != NULL; stream = stream->next) {
3454 feed = stream->feed;
3456 if (stream->is_feed) {
3457 for(i=0;i<stream->nb_streams;i++) {
3458 stream->feed_streams[i] = i;
3464 /* create feed files if needed */
3465 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3468 if (url_exist(feed->feed_filename)) {
3469 /* See if it matches */
3473 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3474 /* Now see if it matches */
3475 if (s->nb_streams == feed->nb_streams) {
3477 for(i=0;i<s->nb_streams;i++) {
3479 sf = feed->streams[i];
3482 if (sf->index != ss->index ||
3484 printf("Index & Id do not match for stream %d (%s)\n",
3485 i, feed->feed_filename);
3488 AVCodecContext *ccf, *ccs;
3492 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3494 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3495 printf("Codecs do not match for stream %d\n", i);
3497 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3498 printf("Codec bitrates do not match for stream %d\n", i);
3500 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3501 if (CHECK_CODEC(time_base.den) ||
3502 CHECK_CODEC(time_base.num) ||
3503 CHECK_CODEC(width) ||
3504 CHECK_CODEC(height)) {
3505 printf("Codec width, height and framerate do not match for stream %d\n", i);
3508 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3509 if (CHECK_CODEC(sample_rate) ||
3510 CHECK_CODEC(channels) ||
3511 CHECK_CODEC(frame_size)) {
3512 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3516 printf("Unknown codec type\n");
3525 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3526 feed->feed_filename, s->nb_streams, feed->nb_streams);
3529 av_close_input_file(s);
3531 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3532 feed->feed_filename);
3535 if (feed->readonly) {
3536 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3537 feed->feed_filename);
3540 unlink(feed->feed_filename);
3543 if (!url_exist(feed->feed_filename)) {
3544 AVFormatContext s1, *s = &s1;
3546 if (feed->readonly) {
3547 printf("Unable to create feed file '%s' as it is marked readonly\n",
3548 feed->feed_filename);
3552 /* only write the header of the ffm file */
3553 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3554 fprintf(stderr, "Could not open output feed file '%s'\n",
3555 feed->feed_filename);
3558 s->oformat = feed->fmt;
3559 s->nb_streams = feed->nb_streams;
3560 for(i=0;i<s->nb_streams;i++) {
3562 st = feed->streams[i];
3565 av_set_parameters(s, NULL);
3566 if (av_write_header(s) < 0) {
3567 fprintf(stderr, "Container doesn't supports the required parameters\n");
3570 /* XXX: need better api */
3571 av_freep(&s->priv_data);
3574 /* get feed size and write index */
3575 fd = open(feed->feed_filename, O_RDONLY);
3577 fprintf(stderr, "Could not open output feed file '%s'\n",
3578 feed->feed_filename);
3582 feed->feed_write_index = ffm_read_write_index(fd);
3583 feed->feed_size = lseek(fd, 0, SEEK_END);
3584 /* ensure that we do not wrap before the end of file */
3585 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3586 feed->feed_max_size = feed->feed_size;
3592 /* compute the bandwidth used by each stream */
3593 static void compute_bandwidth(void)
3598 for(stream = first_stream; stream != NULL; stream = stream->next) {
3600 for(i=0;i<stream->nb_streams;i++) {
3601 AVStream *st = stream->streams[i];
3602 switch(st->codec->codec_type) {
3603 case CODEC_TYPE_AUDIO:
3604 case CODEC_TYPE_VIDEO:
3605 bandwidth += st->codec->bit_rate;
3611 stream->bandwidth = (bandwidth + 999) / 1000;
3615 static void get_arg(char *buf, int buf_size, const char **pp)
3622 while (isspace(*p)) p++;
3625 if (*p == '\"' || *p == '\'')
3637 if ((q - buf) < buf_size - 1)
3642 if (quote && *p == quote)
3647 /* add a codec and set the default parameters */
3648 static void add_codec(FFStream *stream, AVCodecContext *av)
3652 /* compute default parameters */
3653 switch(av->codec_type) {
3654 case CODEC_TYPE_AUDIO:
3655 if (av->bit_rate == 0)
3656 av->bit_rate = 64000;
3657 if (av->sample_rate == 0)
3658 av->sample_rate = 22050;
3659 if (av->channels == 0)
3662 case CODEC_TYPE_VIDEO:
3663 if (av->bit_rate == 0)
3664 av->bit_rate = 64000;
3665 if (av->time_base.num == 0){
3666 av->time_base.den = 5;
3667 av->time_base.num = 1;
3669 if (av->width == 0 || av->height == 0) {
3673 /* Bitrate tolerance is less for streaming */
3674 if (av->bit_rate_tolerance == 0)
3675 av->bit_rate_tolerance = av->bit_rate / 4;
3680 if (av->max_qdiff == 0)
3682 av->qcompress = 0.5;
3685 if (!av->nsse_weight)
3686 av->nsse_weight = 8;
3688 av->frame_skip_cmp = FF_CMP_DCTMAX;
3689 av->me_method = ME_EPZS;
3690 av->rc_buffer_aggressivity = 1.0;
3693 av->rc_eq = "tex^qComp";
3694 if (!av->i_quant_factor)
3695 av->i_quant_factor = -0.8;
3696 if (!av->b_quant_factor)
3697 av->b_quant_factor = 1.25;
3698 if (!av->b_quant_offset)
3699 av->b_quant_offset = 1.25;
3700 if (!av->rc_max_rate)
3701 av->rc_max_rate = av->bit_rate * 2;
3703 if (av->rc_max_rate && !av->rc_buffer_size) {
3704 av->rc_buffer_size = av->rc_max_rate;
3713 st = av_mallocz(sizeof(AVStream));
3716 st->codec = avcodec_alloc_context();
3717 stream->streams[stream->nb_streams++] = st;
3718 memcpy(st->codec, av, sizeof(AVCodecContext));
3721 static int opt_audio_codec(const char *arg)
3727 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3732 return CODEC_ID_NONE;
3738 static int opt_video_codec(const char *arg)
3744 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3749 return CODEC_ID_NONE;
3755 /* simplistic plugin support */
3758 static void load_module(const char *filename)
3761 void (*init_func)(void);
3762 dll = dlopen(filename, RTLD_NOW);
3764 fprintf(stderr, "Could not load module '%s' - %s\n",
3765 filename, dlerror());
3769 init_func = dlsym(dll, "ffserver_module_init");
3772 "%s: init function 'ffserver_module_init()' not found\n",
3781 static int parse_ffconfig(const char *filename)
3788 int val, errors, line_num;
3789 FFStream **last_stream, *stream, *redirect;
3790 FFStream **last_feed, *feed;
3791 AVCodecContext audio_enc, video_enc;
3792 int audio_id, video_id;
3794 f = fopen(filename, "r");
3802 first_stream = NULL;
3803 last_stream = &first_stream;
3805 last_feed = &first_feed;
3809 audio_id = CODEC_ID_NONE;
3810 video_id = CODEC_ID_NONE;
3812 if (fgets(line, sizeof(line), f) == NULL)
3818 if (*p == '\0' || *p == '#')
3821 get_arg(cmd, sizeof(cmd), &p);
3823 if (!strcasecmp(cmd, "Port")) {
3824 get_arg(arg, sizeof(arg), &p);
3826 if (val < 1 || val > 65536) {
3827 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3828 filename, line_num, arg);
3831 my_http_addr.sin_port = htons(val);
3832 } else if (!strcasecmp(cmd, "BindAddress")) {
3833 get_arg(arg, sizeof(arg), &p);
3834 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3835 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3836 filename, line_num, arg);
3839 } else if (!strcasecmp(cmd, "NoDaemon")) {
3840 ffserver_daemon = 0;
3841 } else if (!strcasecmp(cmd, "RTSPPort")) {
3842 get_arg(arg, sizeof(arg), &p);
3844 if (val < 1 || val > 65536) {
3845 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3846 filename, line_num, arg);
3849 my_rtsp_addr.sin_port = htons(atoi(arg));
3850 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3851 get_arg(arg, sizeof(arg), &p);
3852 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3853 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3854 filename, line_num, arg);
3857 } else if (!strcasecmp(cmd, "MaxClients")) {
3858 get_arg(arg, sizeof(arg), &p);
3860 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3861 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3862 filename, line_num, arg);
3865 nb_max_connections = val;
3867 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3868 get_arg(arg, sizeof(arg), &p);
3870 if (val < 10 || val > 100000) {
3871 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3872 filename, line_num, arg);
3875 max_bandwidth = val;
3877 } else if (!strcasecmp(cmd, "CustomLog")) {
3878 get_arg(logfilename, sizeof(logfilename), &p);
3879 } else if (!strcasecmp(cmd, "<Feed")) {
3880 /*********************************************/
3881 /* Feed related options */
3883 if (stream || feed) {
3884 fprintf(stderr, "%s:%d: Already in a tag\n",
3885 filename, line_num);
3887 feed = av_mallocz(sizeof(FFStream));
3888 /* add in stream list */
3889 *last_stream = feed;
3890 last_stream = &feed->next;
3891 /* add in feed list */
3893 last_feed = &feed->next_feed;
3895 get_arg(feed->filename, sizeof(feed->filename), &p);
3896 q = strrchr(feed->filename, '>');
3899 feed->fmt = guess_format("ffm", NULL, NULL);
3900 /* defaut feed file */
3901 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3902 "/tmp/%s.ffm", feed->filename);
3903 feed->feed_max_size = 5 * 1024 * 1024;
3905 feed->feed = feed; /* self feeding :-) */
3907 } else if (!strcasecmp(cmd, "Launch")) {
3911 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3913 for (i = 0; i < 62; i++) {
3914 get_arg(arg, sizeof(arg), &p);
3918 feed->child_argv[i] = av_strdup(arg);
3921 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3923 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3925 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3926 inet_ntoa(my_http_addr.sin_addr),
3927 ntohs(my_http_addr.sin_port), feed->filename);
3932 fprintf(stdout, "Launch commandline: ");
3933 for (j = 0; j <= i; j++)
3934 fprintf(stdout, "%s ", feed->child_argv[j]);
3935 fprintf(stdout, "\n");
3938 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3940 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3942 } else if (stream) {
3943 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3945 } else if (!strcasecmp(cmd, "File")) {
3947 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3948 } else if (stream) {
3949 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3951 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3956 get_arg(arg, sizeof(arg), &p);
3958 fsize = strtod(p1, (char **)&p1);
3959 switch(toupper(*p1)) {
3964 fsize *= 1024 * 1024;
3967 fsize *= 1024 * 1024 * 1024;
3970 feed->feed_max_size = (int64_t)fsize;
3972 } else if (!strcasecmp(cmd, "</Feed>")) {
3974 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3975 filename, line_num);
3979 } else if (!strcasecmp(cmd, "<Stream")) {
3980 /*********************************************/
3981 /* Stream related options */
3983 if (stream || feed) {
3984 fprintf(stderr, "%s:%d: Already in a tag\n",
3985 filename, line_num);
3987 stream = av_mallocz(sizeof(FFStream));
3988 *last_stream = stream;
3989 last_stream = &stream->next;
3991 get_arg(stream->filename, sizeof(stream->filename), &p);
3992 q = strrchr(stream->filename, '>');
3995 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3996 memset(&audio_enc, 0, sizeof(AVCodecContext));
3997 memset(&video_enc, 0, sizeof(AVCodecContext));
3998 audio_id = CODEC_ID_NONE;
3999 video_id = CODEC_ID_NONE;
4001 audio_id = stream->fmt->audio_codec;
4002 video_id = stream->fmt->video_codec;
4005 } else if (!strcasecmp(cmd, "Feed")) {
4006 get_arg(arg, sizeof(arg), &p);
4011 while (sfeed != NULL) {
4012 if (!strcmp(sfeed->filename, arg))
4014 sfeed = sfeed->next_feed;
4017 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
4018 filename, line_num, arg);
4020 stream->feed = sfeed;
4023 } else if (!strcasecmp(cmd, "Format")) {
4024 get_arg(arg, sizeof(arg), &p);
4025 if (!strcmp(arg, "status")) {
4026 stream->stream_type = STREAM_TYPE_STATUS;
4029 stream->stream_type = STREAM_TYPE_LIVE;
4030 /* jpeg cannot be used here, so use single frame jpeg */
4031 if (!strcmp(arg, "jpeg"))
4032 strcpy(arg, "mjpeg");
4033 stream->fmt = guess_stream_format(arg, NULL, NULL);
4035 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4036 filename, line_num, arg);
4041 audio_id = stream->fmt->audio_codec;
4042 video_id = stream->fmt->video_codec;
4044 } else if (!strcasecmp(cmd, "InputFormat")) {
4045 get_arg(arg, sizeof(arg), &p);
4046 stream->ifmt = av_find_input_format(arg);
4047 if (!stream->ifmt) {
4048 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4049 filename, line_num, arg);
4051 } else if (!strcasecmp(cmd, "FaviconURL")) {
4052 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4053 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4055 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4056 filename, line_num);
4059 } else if (!strcasecmp(cmd, "Author")) {
4061 get_arg(stream->author, sizeof(stream->author), &p);
4063 } else if (!strcasecmp(cmd, "Comment")) {
4065 get_arg(stream->comment, sizeof(stream->comment), &p);
4067 } else if (!strcasecmp(cmd, "Copyright")) {
4069 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4071 } else if (!strcasecmp(cmd, "Title")) {
4073 get_arg(stream->title, sizeof(stream->title), &p);
4075 } else if (!strcasecmp(cmd, "Preroll")) {
4076 get_arg(arg, sizeof(arg), &p);
4078 stream->prebuffer = atof(arg) * 1000;
4080 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4082 stream->send_on_key = 1;
4084 } else if (!strcasecmp(cmd, "AudioCodec")) {
4085 get_arg(arg, sizeof(arg), &p);
4086 audio_id = opt_audio_codec(arg);
4087 if (audio_id == CODEC_ID_NONE) {
4088 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4089 filename, line_num, arg);
4092 } else if (!strcasecmp(cmd, "VideoCodec")) {
4093 get_arg(arg, sizeof(arg), &p);
4094 video_id = opt_video_codec(arg);
4095 if (video_id == CODEC_ID_NONE) {
4096 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4097 filename, line_num, arg);
4100 } else if (!strcasecmp(cmd, "MaxTime")) {
4101 get_arg(arg, sizeof(arg), &p);
4103 stream->max_time = atof(arg) * 1000;
4105 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4106 get_arg(arg, sizeof(arg), &p);
4108 audio_enc.bit_rate = atoi(arg) * 1000;
4110 } else if (!strcasecmp(cmd, "AudioChannels")) {
4111 get_arg(arg, sizeof(arg), &p);
4113 audio_enc.channels = atoi(arg);
4115 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4116 get_arg(arg, sizeof(arg), &p);
4118 audio_enc.sample_rate = atoi(arg);
4120 } else if (!strcasecmp(cmd, "AudioQuality")) {
4121 get_arg(arg, sizeof(arg), &p);
4123 // audio_enc.quality = atof(arg) * 1000;
4125 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4127 int minrate, maxrate;
4129 get_arg(arg, sizeof(arg), &p);
4131 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4132 video_enc.rc_min_rate = minrate * 1000;
4133 video_enc.rc_max_rate = maxrate * 1000;
4135 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4136 filename, line_num, arg);
4140 } else if (!strcasecmp(cmd, "Debug")) {
4142 get_arg(arg, sizeof(arg), &p);
4143 video_enc.debug = strtol(arg,0,0);
4145 } else if (!strcasecmp(cmd, "Strict")) {
4147 get_arg(arg, sizeof(arg), &p);
4148 video_enc.strict_std_compliance = atoi(arg);
4150 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4152 get_arg(arg, sizeof(arg), &p);
4153 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4155 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4157 get_arg(arg, sizeof(arg), &p);
4158 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4160 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4161 get_arg(arg, sizeof(arg), &p);
4163 video_enc.bit_rate = atoi(arg) * 1000;
4165 } else if (!strcasecmp(cmd, "VideoSize")) {
4166 get_arg(arg, sizeof(arg), &p);
4168 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4169 if ((video_enc.width % 16) != 0 ||
4170 (video_enc.height % 16) != 0) {
4171 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4172 filename, line_num);
4176 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4177 get_arg(arg, sizeof(arg), &p);
4179 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4180 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4182 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4183 get_arg(arg, sizeof(arg), &p);
4185 video_enc.gop_size = atoi(arg);
4187 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4189 video_enc.gop_size = 1;
4191 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4193 video_enc.mb_decision = FF_MB_DECISION_BITS;
4195 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4197 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4198 video_enc.flags |= CODEC_FLAG_4MV;
4200 } else if (!strcasecmp(cmd, "VideoTag")) {
4201 get_arg(arg, sizeof(arg), &p);
4202 if ((strlen(arg) == 4) && stream) {
4203 video_enc.codec_tag = ff_get_fourcc(arg);
4205 } else if (!strcasecmp(cmd, "BitExact")) {
4207 video_enc.flags |= CODEC_FLAG_BITEXACT;
4209 } else if (!strcasecmp(cmd, "DctFastint")) {
4211 video_enc.dct_algo = FF_DCT_FASTINT;
4213 } else if (!strcasecmp(cmd, "IdctSimple")) {
4215 video_enc.idct_algo = FF_IDCT_SIMPLE;
4217 } else if (!strcasecmp(cmd, "Qscale")) {
4218 get_arg(arg, sizeof(arg), &p);
4220 video_enc.flags |= CODEC_FLAG_QSCALE;
4221 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4223 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4224 get_arg(arg, sizeof(arg), &p);
4226 video_enc.max_qdiff = atoi(arg);
4227 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4228 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4229 filename, line_num);
4233 } else if (!strcasecmp(cmd, "VideoQMax")) {
4234 get_arg(arg, sizeof(arg), &p);
4236 video_enc.qmax = atoi(arg);
4237 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4238 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4239 filename, line_num);
4243 } else if (!strcasecmp(cmd, "VideoQMin")) {
4244 get_arg(arg, sizeof(arg), &p);
4246 video_enc.qmin = atoi(arg);
4247 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4248 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4249 filename, line_num);
4253 } else if (!strcasecmp(cmd, "LumaElim")) {
4254 get_arg(arg, sizeof(arg), &p);
4256 video_enc.luma_elim_threshold = atoi(arg);
4258 } else if (!strcasecmp(cmd, "ChromaElim")) {
4259 get_arg(arg, sizeof(arg), &p);
4261 video_enc.chroma_elim_threshold = atoi(arg);
4263 } else if (!strcasecmp(cmd, "LumiMask")) {
4264 get_arg(arg, sizeof(arg), &p);
4266 video_enc.lumi_masking = atof(arg);
4268 } else if (!strcasecmp(cmd, "DarkMask")) {
4269 get_arg(arg, sizeof(arg), &p);
4271 video_enc.dark_masking = atof(arg);
4273 } else if (!strcasecmp(cmd, "NoVideo")) {
4274 video_id = CODEC_ID_NONE;
4275 } else if (!strcasecmp(cmd, "NoAudio")) {
4276 audio_id = CODEC_ID_NONE;
4277 } else if (!strcasecmp(cmd, "ACL")) {
4280 get_arg(arg, sizeof(arg), &p);
4281 if (strcasecmp(arg, "allow") == 0) {
4282 acl.action = IP_ALLOW;
4283 } else if (strcasecmp(arg, "deny") == 0) {
4284 acl.action = IP_DENY;
4286 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4287 filename, line_num, arg);
4291 get_arg(arg, sizeof(arg), &p);
4293 if (resolve_host(&acl.first, arg) != 0) {
4294 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4295 filename, line_num, arg);
4298 acl.last = acl.first;
4301 get_arg(arg, sizeof(arg), &p);
4304 if (resolve_host(&acl.last, arg) != 0) {
4305 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4306 filename, line_num, arg);
4312 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4313 IPAddressACL **naclp = 0;
4319 naclp = &stream->acl;
4323 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4324 filename, line_num);
4330 naclp = &(*naclp)->next;
4335 } else if (!strcasecmp(cmd, "RTSPOption")) {
4336 get_arg(arg, sizeof(arg), &p);
4338 av_freep(&stream->rtsp_option);
4339 stream->rtsp_option = av_strdup(arg);
4341 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4342 get_arg(arg, sizeof(arg), &p);
4344 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4345 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4346 filename, line_num, arg);
4349 stream->is_multicast = 1;
4350 stream->loop = 1; /* default is looping */
4352 } else if (!strcasecmp(cmd, "MulticastPort")) {
4353 get_arg(arg, sizeof(arg), &p);
4355 stream->multicast_port = atoi(arg);
4357 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4358 get_arg(arg, sizeof(arg), &p);
4360 stream->multicast_ttl = atoi(arg);
4362 } else if (!strcasecmp(cmd, "NoLoop")) {
4366 } else if (!strcasecmp(cmd, "</Stream>")) {
4368 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4369 filename, line_num);
4372 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4373 if (audio_id != CODEC_ID_NONE) {
4374 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4375 audio_enc.codec_id = audio_id;
4376 add_codec(stream, &audio_enc);
4378 if (video_id != CODEC_ID_NONE) {
4379 video_enc.codec_type = CODEC_TYPE_VIDEO;
4380 video_enc.codec_id = video_id;
4381 add_codec(stream, &video_enc);
4385 } else if (!strcasecmp(cmd, "<Redirect")) {
4386 /*********************************************/
4388 if (stream || feed || redirect) {
4389 fprintf(stderr, "%s:%d: Already in a tag\n",
4390 filename, line_num);
4393 redirect = av_mallocz(sizeof(FFStream));
4394 *last_stream = redirect;
4395 last_stream = &redirect->next;
4397 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4398 q = strrchr(redirect->filename, '>');
4401 redirect->stream_type = STREAM_TYPE_REDIRECT;
4403 } else if (!strcasecmp(cmd, "URL")) {
4405 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4407 } else if (!strcasecmp(cmd, "</Redirect>")) {
4409 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4410 filename, line_num);
4413 if (!redirect->feed_filename[0]) {
4414 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4415 filename, line_num);
4419 } else if (!strcasecmp(cmd, "LoadModule")) {
4420 get_arg(arg, sizeof(arg), &p);
4424 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4425 filename, line_num, arg);
4429 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4430 filename, line_num, cmd);
4442 static void show_banner(void)
4444 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4447 static void show_help(void)
4450 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4451 "Hyper fast multi format Audio/Video streaming server\n"
4453 "-L : print the LICENSE\n"
4455 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4459 static void show_license(void)
4463 "FFmpeg is free software; you can redistribute it and/or\n"
4464 "modify it under the terms of the GNU Lesser General Public\n"
4465 "License as published by the Free Software Foundation; either\n"
4466 "version 2.1 of the License, or (at your option) any later version.\n"
4468 "FFmpeg is distributed in the hope that it will be useful,\n"
4469 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4470 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4471 "Lesser General Public License for more details.\n"
4473 "You should have received a copy of the GNU Lesser General Public\n"
4474 "License along with FFmpeg; if not, write to the Free Software\n"
4475 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
4479 static void handle_child_exit(int sig)
4484 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4487 for (feed = first_feed; feed; feed = feed->next) {
4488 if (feed->pid == pid) {
4489 int uptime = time(0) - feed->pid_start;
4492 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4495 /* Turn off any more restarts */
4496 feed->child_argv = 0;
4502 need_to_start_children = 1;
4505 int main(int argc, char **argv)
4507 const char *config_filename;
4509 struct sigaction sigact;
4513 config_filename = "/etc/ffserver.conf";
4515 my_program_name = argv[0];
4516 my_program_dir = getcwd(0, 0);
4517 ffserver_daemon = 1;
4520 c = getopt(argc, argv, "ndLh?f:");
4536 ffserver_daemon = 0;
4539 config_filename = optarg;
4546 putenv("http_proxy"); /* Kill the http_proxy */
4548 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4550 /* address on which the server will handle HTTP connections */
4551 my_http_addr.sin_family = AF_INET;
4552 my_http_addr.sin_port = htons (8080);
4553 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4555 /* address on which the server will handle RTSP connections */
4556 my_rtsp_addr.sin_family = AF_INET;
4557 my_rtsp_addr.sin_port = htons (5454);
4558 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4560 nb_max_connections = 5;
4561 max_bandwidth = 1000;
4562 first_stream = NULL;
4563 logfilename[0] = '\0';
4565 memset(&sigact, 0, sizeof(sigact));
4566 sigact.sa_handler = handle_child_exit;
4567 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4568 sigaction(SIGCHLD, &sigact, 0);
4570 if (parse_ffconfig(config_filename) < 0) {
4571 fprintf(stderr, "Incorrect config file - exiting.\n");
4575 build_file_streams();
4577 build_feed_streams();
4579 compute_bandwidth();
4581 /* put the process in background and detach it from its TTY */
4582 if (ffserver_daemon) {
4589 } else if (pid > 0) {
4597 open("/dev/null", O_RDWR);
4598 if (strcmp(logfilename, "-") != 0) {
4608 signal(SIGPIPE, SIG_IGN);
4610 /* open log file if needed */
4611 if (logfilename[0] != '\0') {
4612 if (!strcmp(logfilename, "-"))
4615 logfile = fopen(logfilename, "w");
4618 if (http_server() < 0) {
4619 fprintf(stderr, "Could not start server\n");