2 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 * multiple format streaming server based on the FFmpeg libraries
28 #define closesocket close
32 #include "libavformat/avformat.h"
33 // FIXME those are internal headers, ffserver _really_ shouldn't use them
34 #include "libavformat/ffm.h"
35 #include "libavformat/network.h"
36 #include "libavformat/os_support.h"
37 #include "libavformat/rtpdec.h"
38 #include "libavformat/rtsp.h"
39 #include "libavformat/avio_internal.h"
40 #include "libavformat/internal.h"
41 #include "libavformat/url.h"
43 #include "libavutil/avstring.h"
44 #include "libavutil/lfg.h"
45 #include "libavutil/dict.h"
46 #include "libavutil/mathematics.h"
47 #include "libavutil/random_seed.h"
48 #include "libavutil/parseutils.h"
49 #include "libavutil/opt.h"
53 #include <sys/ioctl.h>
67 const char program_name[] = "ffserver";
68 const int program_birth_year = 2000;
70 static const OptionDef options[];
73 HTTPSTATE_WAIT_REQUEST,
74 HTTPSTATE_SEND_HEADER,
75 HTTPSTATE_SEND_DATA_HEADER,
76 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
77 HTTPSTATE_SEND_DATA_TRAILER,
78 HTTPSTATE_RECEIVE_DATA,
79 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
82 RTSPSTATE_WAIT_REQUEST,
84 RTSPSTATE_SEND_PACKET,
87 static const char *http_state[] = {
103 #define MAX_STREAMS 20
105 #define IOBUFFER_INIT_SIZE 8192
107 /* timeouts are in ms */
108 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
109 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
111 #define SYNC_TIMEOUT (10 * 1000)
113 typedef struct RTSPActionServerSetup {
115 char transport_option[512];
116 } RTSPActionServerSetup;
119 int64_t count1, count2;
120 int64_t time1, time2;
123 /* context associated with one connection */
124 typedef struct HTTPContext {
125 enum HTTPState state;
126 int fd; /* socket file descriptor */
127 struct sockaddr_in from_addr; /* origin */
128 struct pollfd *poll_entry; /* used when polling */
130 uint8_t *buffer_ptr, *buffer_end;
133 int chunked_encoding;
134 int chunk_size; /* 0 if it needs to be read */
135 struct HTTPContext *next;
136 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
140 /* input format handling */
141 AVFormatContext *fmt_in;
142 int64_t start_time; /* In milliseconds - this wraps fairly often */
143 int64_t first_pts; /* initial pts value */
144 int64_t cur_pts; /* current pts value from the stream in us */
145 int64_t cur_frame_duration; /* duration of the current frame in us */
146 int cur_frame_bytes; /* output frame size, needed to compute
147 the time at which we send each
149 int pts_stream_index; /* stream we choose as clock reference */
150 int64_t cur_clock; /* current clock reference value in us */
151 /* output format handling */
152 struct FFStream *stream;
153 /* -1 is invalid stream */
154 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
155 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
157 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
158 int last_packet_sent; /* true if last data packet was sent */
160 DataRateData datarate;
167 int is_packetized; /* if true, the stream is packetized */
168 int packet_stream_index; /* current stream for output in state machine */
170 /* RTSP state specific */
171 uint8_t *pb_buffer; /* XXX: use that in all the code */
173 int seq; /* RTSP sequence number */
175 /* RTP state specific */
176 enum RTSPLowerTransport rtp_protocol;
177 char session_id[32]; /* session id */
178 AVFormatContext *rtp_ctx[MAX_STREAMS];
180 /* RTP/UDP specific */
181 URLContext *rtp_handles[MAX_STREAMS];
183 /* RTP/TCP specific */
184 struct HTTPContext *rtsp_c;
185 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
188 /* each generated stream is described here */
192 STREAM_TYPE_REDIRECT,
195 enum IPAddressAction {
200 typedef struct IPAddressACL {
201 struct IPAddressACL *next;
202 enum IPAddressAction action;
203 /* These are in host order */
204 struct in_addr first;
208 /* description of each stream of the ffserver.conf file */
209 typedef struct FFStream {
210 enum StreamType stream_type;
211 char filename[1024]; /* stream filename */
212 struct FFStream *feed; /* feed we are using (can be null if
214 AVDictionary *in_opts; /* input parameters */
215 AVInputFormat *ifmt; /* if non NULL, force input format */
218 char dynamic_acl[1024];
220 int prebuffer; /* Number of millseconds early to start */
221 int64_t max_time; /* Number of milliseconds to run */
223 AVStream *streams[MAX_STREAMS];
224 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
225 char feed_filename[1024]; /* file name of the feed storage, or
226 input file name for a stream */
231 pid_t pid; /* Of ffmpeg process */
232 time_t pid_start; /* Of ffmpeg process */
234 struct FFStream *next;
235 unsigned bandwidth; /* bandwidth, in kbits/s */
238 /* multicast specific */
240 struct in_addr multicast_ip;
241 int multicast_port; /* first port used for multicast */
243 int loop; /* if true, send the stream in loops (only meaningful if file) */
246 int feed_opened; /* true if someone is writing to the feed */
247 int is_feed; /* true if it is a feed */
248 int readonly; /* True if writing is prohibited to the file */
249 int truncate; /* True if feeder connection truncate the feed file */
251 int64_t bytes_served;
252 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
253 int64_t feed_write_index; /* current write position in feed (it wraps around) */
254 int64_t feed_size; /* current size of feed */
255 struct FFStream *next_feed;
258 typedef struct FeedData {
259 long long data_count;
260 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
263 static struct sockaddr_in my_http_addr;
264 static struct sockaddr_in my_rtsp_addr;
266 static char logfilename[1024];
267 static HTTPContext *first_http_ctx;
268 static FFStream *first_feed; /* contains only feeds */
269 static FFStream *first_stream; /* contains all streams, including feeds */
271 static void new_connection(int server_fd, int is_rtsp);
272 static void close_connection(HTTPContext *c);
275 static int handle_connection(HTTPContext *c);
276 static int http_parse_request(HTTPContext *c);
277 static int http_send_data(HTTPContext *c);
278 static void compute_status(HTTPContext *c);
279 static int open_input_stream(HTTPContext *c, const char *info);
280 static int http_start_receive_data(HTTPContext *c);
281 static int http_receive_data(HTTPContext *c);
284 static int rtsp_parse_request(HTTPContext *c);
285 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
286 static void rtsp_cmd_options(HTTPContext *c, const char *url);
287 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
288 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
289 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
290 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
293 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
294 struct in_addr my_ip);
297 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
298 FFStream *stream, const char *session_id,
299 enum RTSPLowerTransport rtp_protocol);
300 static int rtp_new_av_stream(HTTPContext *c,
301 int stream_index, struct sockaddr_in *dest_addr,
302 HTTPContext *rtsp_c);
304 static const char *my_program_name;
305 static const char *my_program_dir;
307 static const char *config_filename = "/etc/ffserver.conf";
309 static int ffserver_debug;
310 static int ffserver_daemon;
311 static int no_launch;
312 static int need_to_start_children;
314 /* maximum number of simultaneous HTTP connections */
315 static unsigned int nb_max_http_connections = 2000;
316 static unsigned int nb_max_connections = 5;
317 static unsigned int nb_connections;
319 static uint64_t max_bandwidth = 1000;
320 static uint64_t current_bandwidth;
322 static int64_t cur_time; // Making this global saves on passing it around everywhere
324 static AVLFG random_state;
326 static FILE *logfile = NULL;
328 /* FIXME: make ffserver work with IPv6 */
329 void av_noreturn exit_program(int ret)
334 /* resolve host with also IP address parsing */
335 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
338 if (!ff_inet_aton(hostname, sin_addr)) {
340 struct addrinfo *ai, *cur;
341 struct addrinfo hints = { 0 };
342 hints.ai_family = AF_INET;
343 if (getaddrinfo(hostname, NULL, &hints, &ai))
345 /* getaddrinfo returns a linked list of addrinfo structs.
346 * Even if we set ai_family = AF_INET above, make sure
347 * that the returned one actually is of the correct type. */
348 for (cur = ai; cur; cur = cur->ai_next) {
349 if (cur->ai_family == AF_INET) {
350 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
359 hp = gethostbyname(hostname);
362 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
368 static char *ctime1(char *buf2)
376 p = buf2 + strlen(p) - 1;
382 static void http_vlog(const char *fmt, va_list vargs)
384 static int print_prefix = 1;
389 fprintf(logfile, "%s ", buf);
391 print_prefix = strstr(fmt, "\n") != NULL;
392 vfprintf(logfile, fmt, vargs);
398 __attribute__ ((format (printf, 1, 2)))
400 static void http_log(const char *fmt, ...)
403 va_start(vargs, fmt);
404 http_vlog(fmt, vargs);
408 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
410 static int print_prefix = 1;
411 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
412 if (level > av_log_get_level())
414 if (print_prefix && avc)
415 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
416 print_prefix = strstr(fmt, "\n") != NULL;
417 http_vlog(fmt, vargs);
420 static void log_connection(HTTPContext *c)
425 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
426 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
427 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
430 static void update_datarate(DataRateData *drd, int64_t count)
432 if (!drd->time1 && !drd->count1) {
433 drd->time1 = drd->time2 = cur_time;
434 drd->count1 = drd->count2 = count;
435 } else if (cur_time - drd->time2 > 5000) {
436 drd->time1 = drd->time2;
437 drd->count1 = drd->count2;
438 drd->time2 = cur_time;
443 /* In bytes per second */
444 static int compute_datarate(DataRateData *drd, int64_t count)
446 if (cur_time == drd->time1)
449 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
453 static void start_children(FFStream *feed)
458 for (; feed; feed = feed->next) {
459 if (feed->child_argv && !feed->pid) {
460 feed->pid_start = time(0);
465 http_log("Unable to create children\n");
474 av_strlcpy(pathname, my_program_name, sizeof(pathname));
476 slash = strrchr(pathname, '/');
481 strcpy(slash, "ffmpeg");
483 http_log("Launch command line: ");
484 http_log("%s ", pathname);
485 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
486 http_log("%s ", feed->child_argv[i]);
489 for (i = 3; i < 256; i++)
492 if (!ffserver_debug) {
493 i = open("/dev/null", O_RDWR);
502 /* This is needed to make relative pathnames work */
503 if (chdir(my_program_dir) < 0) {
504 http_log("chdir failed\n");
508 signal(SIGPIPE, SIG_DFL);
510 execvp(pathname, feed->child_argv);
518 /* open a listening socket */
519 static int socket_open_listen(struct sockaddr_in *my_addr)
523 server_fd = socket(AF_INET,SOCK_STREAM,0);
530 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
532 my_addr->sin_family = AF_INET;
533 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
535 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
537 closesocket(server_fd);
541 if (listen (server_fd, 5) < 0) {
543 closesocket(server_fd);
546 ff_socket_nonblock(server_fd, 1);
551 /* start all multicast streams */
552 static void start_multicast(void)
557 struct sockaddr_in dest_addr;
558 int default_port, stream_index;
561 for(stream = first_stream; stream != NULL; stream = stream->next) {
562 if (stream->is_multicast) {
563 /* open the RTP connection */
564 snprintf(session_id, sizeof(session_id), "%08x%08x",
565 av_lfg_get(&random_state), av_lfg_get(&random_state));
567 /* choose a port if none given */
568 if (stream->multicast_port == 0) {
569 stream->multicast_port = default_port;
573 dest_addr.sin_family = AF_INET;
574 dest_addr.sin_addr = stream->multicast_ip;
575 dest_addr.sin_port = htons(stream->multicast_port);
577 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
578 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
582 if (open_input_stream(rtp_c, "") < 0) {
583 http_log("Could not open input stream for stream '%s'\n",
588 /* open each RTP stream */
589 for(stream_index = 0; stream_index < stream->nb_streams;
591 dest_addr.sin_port = htons(stream->multicast_port +
593 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
594 http_log("Could not open output stream '%s/streamid=%d'\n",
595 stream->filename, stream_index);
600 /* change state to send data */
601 rtp_c->state = HTTPSTATE_SEND_DATA;
606 /* main loop of the http server */
607 static int http_server(void)
609 int server_fd = 0, rtsp_server_fd = 0;
610 int ret, delay, delay1;
611 struct pollfd *poll_table, *poll_entry;
612 HTTPContext *c, *c_next;
614 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
615 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
619 if (my_http_addr.sin_port) {
620 server_fd = socket_open_listen(&my_http_addr);
625 if (my_rtsp_addr.sin_port) {
626 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
627 if (rtsp_server_fd < 0)
631 if (!rtsp_server_fd && !server_fd) {
632 http_log("HTTP and RTSP disabled.\n");
636 http_log("FFserver started.\n");
638 start_children(first_feed);
643 poll_entry = poll_table;
645 poll_entry->fd = server_fd;
646 poll_entry->events = POLLIN;
649 if (rtsp_server_fd) {
650 poll_entry->fd = rtsp_server_fd;
651 poll_entry->events = POLLIN;
655 /* wait for events on each HTTP handle */
662 case HTTPSTATE_SEND_HEADER:
663 case RTSPSTATE_SEND_REPLY:
664 case RTSPSTATE_SEND_PACKET:
665 c->poll_entry = poll_entry;
667 poll_entry->events = POLLOUT;
670 case HTTPSTATE_SEND_DATA_HEADER:
671 case HTTPSTATE_SEND_DATA:
672 case HTTPSTATE_SEND_DATA_TRAILER:
673 if (!c->is_packetized) {
674 /* for TCP, we output as much as we can (may need to put a limit) */
675 c->poll_entry = poll_entry;
677 poll_entry->events = POLLOUT;
680 /* when ffserver is doing the timing, we work by
681 looking at which packet need to be sent every
683 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
688 case HTTPSTATE_WAIT_REQUEST:
689 case HTTPSTATE_RECEIVE_DATA:
690 case HTTPSTATE_WAIT_FEED:
691 case RTSPSTATE_WAIT_REQUEST:
692 /* need to catch errors */
693 c->poll_entry = poll_entry;
695 poll_entry->events = POLLIN;/* Maybe this will work */
699 c->poll_entry = NULL;
705 /* wait for an event on one connection. We poll at least every
706 second to handle timeouts */
708 ret = poll(poll_table, poll_entry - poll_table, delay);
709 if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
710 ff_neterrno() != AVERROR(EINTR))
714 cur_time = av_gettime() / 1000;
716 if (need_to_start_children) {
717 need_to_start_children = 0;
718 start_children(first_feed);
721 /* now handle the events */
722 for(c = first_http_ctx; c != NULL; c = c_next) {
724 if (handle_connection(c) < 0) {
725 /* close and free the connection */
731 poll_entry = poll_table;
733 /* new HTTP connection request ? */
734 if (poll_entry->revents & POLLIN)
735 new_connection(server_fd, 0);
738 if (rtsp_server_fd) {
739 /* new RTSP connection request ? */
740 if (poll_entry->revents & POLLIN)
741 new_connection(rtsp_server_fd, 1);
746 /* start waiting for a new HTTP/RTSP request */
747 static void start_wait_request(HTTPContext *c, int is_rtsp)
749 c->buffer_ptr = c->buffer;
750 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
753 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
754 c->state = RTSPSTATE_WAIT_REQUEST;
756 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
757 c->state = HTTPSTATE_WAIT_REQUEST;
761 static void http_send_too_busy_reply(int fd)
764 int len = snprintf(buffer, sizeof(buffer),
765 "HTTP/1.0 503 Server too busy\r\n"
766 "Content-type: text/html\r\n"
768 "<html><head><title>Too busy</title></head><body>\r\n"
769 "<p>The server is too busy to serve your request at this time.</p>\r\n"
770 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
771 "</body></html>\r\n",
772 nb_connections, nb_max_connections);
773 send(fd, buffer, len, 0);
777 static void new_connection(int server_fd, int is_rtsp)
779 struct sockaddr_in from_addr;
781 HTTPContext *c = NULL;
783 len = sizeof(from_addr);
784 fd = accept(server_fd, (struct sockaddr *)&from_addr,
787 http_log("error during accept %s\n", strerror(errno));
790 ff_socket_nonblock(fd, 1);
792 if (nb_connections >= nb_max_connections) {
793 http_send_too_busy_reply(fd);
797 /* add a new connection */
798 c = av_mallocz(sizeof(HTTPContext));
803 c->poll_entry = NULL;
804 c->from_addr = from_addr;
805 c->buffer_size = IOBUFFER_INIT_SIZE;
806 c->buffer = av_malloc(c->buffer_size);
810 c->next = first_http_ctx;
814 start_wait_request(c, is_rtsp);
826 static void close_connection(HTTPContext *c)
828 HTTPContext **cp, *c1;
830 AVFormatContext *ctx;
834 /* remove connection from list */
835 cp = &first_http_ctx;
836 while ((*cp) != NULL) {
844 /* remove references, if any (XXX: do it faster) */
845 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
850 /* remove connection associated resources */
854 /* close each frame parser */
855 for(i=0;i<c->fmt_in->nb_streams;i++) {
856 st = c->fmt_in->streams[i];
857 if (st->codec->codec)
858 avcodec_close(st->codec);
860 avformat_close_input(&c->fmt_in);
863 /* free RTP output streams if any */
866 nb_streams = c->stream->nb_streams;
868 for(i=0;i<nb_streams;i++) {
871 av_write_trailer(ctx);
872 av_dict_free(&ctx->metadata);
873 av_free(ctx->streams[0]);
876 h = c->rtp_handles[i];
883 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
886 if (avio_open_dyn_buf(&ctx->pb) >= 0) {
887 av_write_trailer(ctx);
888 av_freep(&c->pb_buffer);
889 avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
894 for(i=0; i<ctx->nb_streams; i++)
895 av_free(ctx->streams[i]);
897 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
898 current_bandwidth -= c->stream->bandwidth;
900 /* signal that there is no feed if we are the feeder socket */
901 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
902 c->stream->feed_opened = 0;
906 av_freep(&c->pb_buffer);
907 av_freep(&c->packet_buffer);
913 static int handle_connection(HTTPContext *c)
918 case HTTPSTATE_WAIT_REQUEST:
919 case RTSPSTATE_WAIT_REQUEST:
921 if ((c->timeout - cur_time) < 0)
923 if (c->poll_entry->revents & (POLLERR | POLLHUP))
926 /* no need to read if no events */
927 if (!(c->poll_entry->revents & POLLIN))
931 len = recv(c->fd, c->buffer_ptr, 1, 0);
933 if (ff_neterrno() != AVERROR(EAGAIN) &&
934 ff_neterrno() != AVERROR(EINTR))
936 } else if (len == 0) {
939 /* search for end of request. */
941 c->buffer_ptr += len;
943 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
944 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
945 /* request found : parse it and reply */
946 if (c->state == HTTPSTATE_WAIT_REQUEST) {
947 ret = http_parse_request(c);
949 ret = rtsp_parse_request(c);
953 } else if (ptr >= c->buffer_end) {
954 /* request too long: cannot do anything */
956 } else goto read_loop;
960 case HTTPSTATE_SEND_HEADER:
961 if (c->poll_entry->revents & (POLLERR | POLLHUP))
964 /* no need to write if no events */
965 if (!(c->poll_entry->revents & POLLOUT))
967 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
969 if (ff_neterrno() != AVERROR(EAGAIN) &&
970 ff_neterrno() != AVERROR(EINTR)) {
971 /* error : close connection */
972 av_freep(&c->pb_buffer);
976 c->buffer_ptr += len;
978 c->stream->bytes_served += len;
979 c->data_count += len;
980 if (c->buffer_ptr >= c->buffer_end) {
981 av_freep(&c->pb_buffer);
985 /* all the buffer was sent : synchronize to the incoming stream */
986 c->state = HTTPSTATE_SEND_DATA_HEADER;
987 c->buffer_ptr = c->buffer_end = c->buffer;
992 case HTTPSTATE_SEND_DATA:
993 case HTTPSTATE_SEND_DATA_HEADER:
994 case HTTPSTATE_SEND_DATA_TRAILER:
995 /* for packetized output, we consider we can always write (the
996 input streams sets the speed). It may be better to verify
997 that we do not rely too much on the kernel queues */
998 if (!c->is_packetized) {
999 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1002 /* no need to read if no events */
1003 if (!(c->poll_entry->revents & POLLOUT))
1006 if (http_send_data(c) < 0)
1008 /* close connection if trailer sent */
1009 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
1012 case HTTPSTATE_RECEIVE_DATA:
1013 /* no need to read if no events */
1014 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1016 if (!(c->poll_entry->revents & POLLIN))
1018 if (http_receive_data(c) < 0)
1021 case HTTPSTATE_WAIT_FEED:
1022 /* no need to read if no events */
1023 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1026 /* nothing to do, we'll be waken up by incoming feed packets */
1029 case RTSPSTATE_SEND_REPLY:
1030 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1031 av_freep(&c->pb_buffer);
1034 /* no need to write if no events */
1035 if (!(c->poll_entry->revents & POLLOUT))
1037 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1039 if (ff_neterrno() != AVERROR(EAGAIN) &&
1040 ff_neterrno() != AVERROR(EINTR)) {
1041 /* error : close connection */
1042 av_freep(&c->pb_buffer);
1046 c->buffer_ptr += len;
1047 c->data_count += len;
1048 if (c->buffer_ptr >= c->buffer_end) {
1049 /* all the buffer was sent : wait for a new request */
1050 av_freep(&c->pb_buffer);
1051 start_wait_request(c, 1);
1055 case RTSPSTATE_SEND_PACKET:
1056 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1057 av_freep(&c->packet_buffer);
1060 /* no need to write if no events */
1061 if (!(c->poll_entry->revents & POLLOUT))
1063 len = send(c->fd, c->packet_buffer_ptr,
1064 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1066 if (ff_neterrno() != AVERROR(EAGAIN) &&
1067 ff_neterrno() != AVERROR(EINTR)) {
1068 /* error : close connection */
1069 av_freep(&c->packet_buffer);
1073 c->packet_buffer_ptr += len;
1074 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1075 /* all the buffer was sent : wait for a new request */
1076 av_freep(&c->packet_buffer);
1077 c->state = RTSPSTATE_WAIT_REQUEST;
1081 case HTTPSTATE_READY:
1090 static int extract_rates(char *rates, int ratelen, const char *request)
1094 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1095 if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1096 const char *q = p + 7;
1098 while (*q && *q != '\n' && isspace(*q))
1101 if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1107 memset(rates, 0xff, ratelen);
1110 while (*q && *q != '\n' && *q != ':')
1113 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1117 if (stream_no < ratelen && stream_no >= 0)
1118 rates[stream_no] = rate_no;
1120 while (*q && *q != '\n' && !isspace(*q))
1127 p = strchr(p, '\n');
1137 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1140 int best_bitrate = 100000000;
1143 for (i = 0; i < feed->nb_streams; i++) {
1144 AVCodecContext *feed_codec = feed->streams[i]->codec;
1146 if (feed_codec->codec_id != codec->codec_id ||
1147 feed_codec->sample_rate != codec->sample_rate ||
1148 feed_codec->width != codec->width ||
1149 feed_codec->height != codec->height)
1152 /* Potential stream */
1154 /* We want the fastest stream less than bit_rate, or the slowest
1155 * faster than bit_rate
1158 if (feed_codec->bit_rate <= bit_rate) {
1159 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1160 best_bitrate = feed_codec->bit_rate;
1164 if (feed_codec->bit_rate < best_bitrate) {
1165 best_bitrate = feed_codec->bit_rate;
1174 static int modify_current_stream(HTTPContext *c, char *rates)
1177 FFStream *req = c->stream;
1178 int action_required = 0;
1180 /* Not much we can do for a feed */
1184 for (i = 0; i < req->nb_streams; i++) {
1185 AVCodecContext *codec = req->streams[i]->codec;
1189 c->switch_feed_streams[i] = req->feed_streams[i];
1192 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1195 /* Wants off or slow */
1196 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1198 /* This doesn't work well when it turns off the only stream! */
1199 c->switch_feed_streams[i] = -2;
1200 c->feed_streams[i] = -2;
1205 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1206 action_required = 1;
1209 return action_required;
1212 /* XXX: factorize in utils.c ? */
1213 /* XXX: take care with different space meaning */
1214 static void skip_spaces(const char **pp)
1218 while (*p == ' ' || *p == '\t')
1223 static void get_word(char *buf, int buf_size, const char **pp)
1231 while (!isspace(*p) && *p != '\0') {
1232 if ((q - buf) < buf_size - 1)
1241 static void get_arg(char *buf, int buf_size, const char **pp)
1248 while (isspace(*p)) p++;
1251 if (*p == '\"' || *p == '\'')
1263 if ((q - buf) < buf_size - 1)
1268 if (quote && *p == quote)
1273 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1274 const char *p, const char *filename, int line_num)
1280 get_arg(arg, sizeof(arg), &p);
1281 if (av_strcasecmp(arg, "allow") == 0)
1282 acl.action = IP_ALLOW;
1283 else if (av_strcasecmp(arg, "deny") == 0)
1284 acl.action = IP_DENY;
1286 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1287 filename, line_num, arg);
1291 get_arg(arg, sizeof(arg), &p);
1293 if (resolve_host(&acl.first, arg) != 0) {
1294 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1295 filename, line_num, arg);
1298 acl.last = acl.first;
1300 get_arg(arg, sizeof(arg), &p);
1303 if (resolve_host(&acl.last, arg) != 0) {
1304 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1305 filename, line_num, arg);
1311 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1312 IPAddressACL **naclp = 0;
1318 naclp = &stream->acl;
1324 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1325 filename, line_num);
1331 naclp = &(*naclp)->next;
1339 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1344 IPAddressACL *acl = NULL;
1348 f = fopen(stream->dynamic_acl, "r");
1350 perror(stream->dynamic_acl);
1354 acl = av_mallocz(sizeof(IPAddressACL));
1358 if (fgets(line, sizeof(line), f) == NULL)
1364 if (*p == '\0' || *p == '#')
1366 get_arg(cmd, sizeof(cmd), &p);
1368 if (!av_strcasecmp(cmd, "ACL"))
1369 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1376 static void free_acl_list(IPAddressACL *in_acl)
1378 IPAddressACL *pacl,*pacl2;
1388 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1390 enum IPAddressAction last_action = IP_DENY;
1392 struct in_addr *src = &c->from_addr.sin_addr;
1393 unsigned long src_addr = src->s_addr;
1395 for (acl = in_acl; acl; acl = acl->next) {
1396 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1397 return (acl->action == IP_ALLOW) ? 1 : 0;
1398 last_action = acl->action;
1401 /* Nothing matched, so return not the last action */
1402 return (last_action == IP_DENY) ? 1 : 0;
1405 static int validate_acl(FFStream *stream, HTTPContext *c)
1411 /* if stream->acl is null validate_acl_list will return 1 */
1412 ret = validate_acl_list(stream->acl, c);
1414 if (stream->dynamic_acl[0]) {
1415 acl = parse_dynamic_acl(stream, c);
1417 ret = validate_acl_list(acl, c);
1425 /* compute the real filename of a file by matching it without its
1426 extensions to all the stream filenames */
1427 static void compute_real_filename(char *filename, int max_size)
1434 /* compute filename by matching without the file extensions */
1435 av_strlcpy(file1, filename, sizeof(file1));
1436 p = strrchr(file1, '.');
1439 for(stream = first_stream; stream != NULL; stream = stream->next) {
1440 av_strlcpy(file2, stream->filename, sizeof(file2));
1441 p = strrchr(file2, '.');
1444 if (!strcmp(file1, file2)) {
1445 av_strlcpy(filename, stream->filename, max_size);
1460 /* parse http request and prepare header */
1461 static int http_parse_request(HTTPContext *c)
1464 enum RedirType redir_type;
1466 char info[1024], filename[1024];
1470 const char *mime_type;
1474 char *useragent = 0;
1477 get_word(cmd, sizeof(cmd), (const char **)&p);
1478 av_strlcpy(c->method, cmd, sizeof(c->method));
1480 if (!strcmp(cmd, "GET"))
1482 else if (!strcmp(cmd, "POST"))
1487 get_word(url, sizeof(url), (const char **)&p);
1488 av_strlcpy(c->url, url, sizeof(c->url));
1490 get_word(protocol, sizeof(protocol), (const char **)&p);
1491 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1494 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1497 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1499 /* find the filename and the optional info string in the request */
1500 p = strchr(url, '?');
1502 av_strlcpy(info, p, sizeof(info));
1507 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1509 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1510 if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1512 if (*useragent && *useragent != '\n' && isspace(*useragent))
1516 p = strchr(p, '\n');
1523 redir_type = REDIR_NONE;
1524 if (av_match_ext(filename, "asx")) {
1525 redir_type = REDIR_ASX;
1526 filename[strlen(filename)-1] = 'f';
1527 } else if (av_match_ext(filename, "asf") &&
1528 (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1529 /* if this isn't WMP or lookalike, return the redirector file */
1530 redir_type = REDIR_ASF;
1531 } else if (av_match_ext(filename, "rpm,ram")) {
1532 redir_type = REDIR_RAM;
1533 strcpy(filename + strlen(filename)-2, "m");
1534 } else if (av_match_ext(filename, "rtsp")) {
1535 redir_type = REDIR_RTSP;
1536 compute_real_filename(filename, sizeof(filename) - 1);
1537 } else if (av_match_ext(filename, "sdp")) {
1538 redir_type = REDIR_SDP;
1539 compute_real_filename(filename, sizeof(filename) - 1);
1542 // "redirect" / request to index.html
1543 if (!strlen(filename))
1544 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1546 stream = first_stream;
1547 while (stream != NULL) {
1548 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1550 stream = stream->next;
1552 if (stream == NULL) {
1553 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1554 http_log("File '%s' not found\n", url);
1559 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1560 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1562 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1563 c->http_error = 301;
1565 q += snprintf(q, c->buffer_size,
1566 "HTTP/1.0 301 Moved\r\n"
1568 "Content-type: text/html\r\n"
1570 "<html><head><title>Moved</title></head><body>\r\n"
1571 "You should be <a href=\"%s\">redirected</a>.\r\n"
1572 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1573 /* prepare output buffer */
1574 c->buffer_ptr = c->buffer;
1576 c->state = HTTPSTATE_SEND_HEADER;
1580 /* If this is WMP, get the rate information */
1581 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1582 if (modify_current_stream(c, ratebuf)) {
1583 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1584 if (c->switch_feed_streams[i] >= 0)
1585 c->switch_feed_streams[i] = -1;
1590 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1591 current_bandwidth += stream->bandwidth;
1593 /* If already streaming this feed, do not let start another feeder. */
1594 if (stream->feed_opened) {
1595 snprintf(msg, sizeof(msg), "This feed is already being received.");
1596 http_log("Feed '%s' already being received\n", stream->feed_filename);
1600 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1601 c->http_error = 503;
1603 q += snprintf(q, c->buffer_size,
1604 "HTTP/1.0 503 Server too busy\r\n"
1605 "Content-type: text/html\r\n"
1607 "<html><head><title>Too busy</title></head><body>\r\n"
1608 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1609 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1610 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1611 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1612 /* prepare output buffer */
1613 c->buffer_ptr = c->buffer;
1615 c->state = HTTPSTATE_SEND_HEADER;
1619 if (redir_type != REDIR_NONE) {
1622 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1623 if (av_strncasecmp(p, "Host:", 5) == 0) {
1627 p = strchr(p, '\n');
1638 while (isspace(*hostinfo))
1641 eoh = strchr(hostinfo, '\n');
1643 if (eoh[-1] == '\r')
1646 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1647 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1648 hostbuf[eoh - hostinfo] = 0;
1650 c->http_error = 200;
1652 switch(redir_type) {
1654 q += snprintf(q, c->buffer_size,
1655 "HTTP/1.0 200 ASX Follows\r\n"
1656 "Content-type: video/x-ms-asf\r\n"
1658 "<ASX Version=\"3\">\r\n"
1659 //"<!-- Autogenerated by ffserver -->\r\n"
1660 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1661 "</ASX>\r\n", hostbuf, filename, info);
1664 q += snprintf(q, c->buffer_size,
1665 "HTTP/1.0 200 RAM Follows\r\n"
1666 "Content-type: audio/x-pn-realaudio\r\n"
1668 "# Autogenerated by ffserver\r\n"
1669 "http://%s/%s%s\r\n", hostbuf, filename, info);
1672 q += snprintf(q, c->buffer_size,
1673 "HTTP/1.0 200 ASF Redirect follows\r\n"
1674 "Content-type: video/x-ms-asf\r\n"
1677 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1681 char hostname[256], *p;
1682 /* extract only hostname */
1683 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1684 p = strrchr(hostname, ':');
1687 q += snprintf(q, c->buffer_size,
1688 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1689 /* XXX: incorrect mime type ? */
1690 "Content-type: application/x-rtsp\r\n"
1692 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1698 int sdp_data_size, len;
1699 struct sockaddr_in my_addr;
1701 q += snprintf(q, c->buffer_size,
1702 "HTTP/1.0 200 OK\r\n"
1703 "Content-type: application/sdp\r\n"
1706 len = sizeof(my_addr);
1707 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1709 /* XXX: should use a dynamic buffer */
1710 sdp_data_size = prepare_sdp_description(stream,
1713 if (sdp_data_size > 0) {
1714 memcpy(q, sdp_data, sdp_data_size);
1726 /* prepare output buffer */
1727 c->buffer_ptr = c->buffer;
1729 c->state = HTTPSTATE_SEND_HEADER;
1735 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1739 stream->conns_served++;
1741 /* XXX: add there authenticate and IP match */
1744 /* if post, it means a feed is being sent */
1745 if (!stream->is_feed) {
1746 /* However it might be a status report from WMP! Let us log the
1747 * data as it might come in handy one day. */
1751 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1752 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1756 if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1757 client_id = strtol(p + 18, 0, 10);
1758 p = strchr(p, '\n');
1766 char *eol = strchr(logline, '\n');
1771 if (eol[-1] == '\r')
1773 http_log("%.*s\n", (int) (eol - logline), logline);
1774 c->suppress_log = 1;
1779 http_log("\nGot request:\n%s\n", c->buffer);
1782 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1785 /* Now we have to find the client_id */
1786 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1787 if (wmpc->wmp_client_id == client_id)
1791 if (wmpc && modify_current_stream(wmpc, ratebuf))
1792 wmpc->switch_pending = 1;
1795 snprintf(msg, sizeof(msg), "POST command not handled");
1799 if (http_start_receive_data(c) < 0) {
1800 snprintf(msg, sizeof(msg), "could not open feed");
1804 c->state = HTTPSTATE_RECEIVE_DATA;
1809 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1810 http_log("\nGot request:\n%s\n", c->buffer);
1813 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1816 /* open input stream */
1817 if (open_input_stream(c, info) < 0) {
1818 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1822 /* prepare http header */
1824 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1825 mime_type = c->stream->fmt->mime_type;
1827 mime_type = "application/x-octet-stream";
1828 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1830 /* for asf, we need extra headers */
1831 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1832 /* Need to allocate a client id */
1834 c->wmp_client_id = av_lfg_get(&random_state);
1836 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);
1838 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1839 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1841 /* prepare output buffer */
1843 c->buffer_ptr = c->buffer;
1845 c->state = HTTPSTATE_SEND_HEADER;
1848 c->http_error = 404;
1850 q += snprintf(q, c->buffer_size,
1851 "HTTP/1.0 404 Not Found\r\n"
1852 "Content-type: text/html\r\n"
1855 "<head><title>404 Not Found</title></head>\n"
1858 /* prepare output buffer */
1859 c->buffer_ptr = c->buffer;
1861 c->state = HTTPSTATE_SEND_HEADER;
1865 c->http_error = 200; /* horrible : we use this value to avoid
1866 going to the send data state */
1867 c->state = HTTPSTATE_SEND_HEADER;
1871 static void fmt_bytecount(AVIOContext *pb, int64_t count)
1873 static const char suffix[] = " kMGTP";
1876 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1878 avio_printf(pb, "%"PRId64"%c", count, *s);
1881 static void compute_status(HTTPContext *c)
1890 if (avio_open_dyn_buf(&pb) < 0) {
1891 /* XXX: return an error ? */
1892 c->buffer_ptr = c->buffer;
1893 c->buffer_end = c->buffer;
1897 avio_printf(pb, "HTTP/1.0 200 OK\r\n");
1898 avio_printf(pb, "Content-type: %s\r\n", "text/html");
1899 avio_printf(pb, "Pragma: no-cache\r\n");
1900 avio_printf(pb, "\r\n");
1902 avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1903 if (c->stream->feed_filename[0])
1904 avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1905 avio_printf(pb, "</head>\n<body>");
1906 avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
1908 avio_printf(pb, "<h2>Available Streams</h2>\n");
1909 avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
1910 avio_printf(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");
1911 stream = first_stream;
1912 while (stream != NULL) {
1913 char sfilename[1024];
1916 if (stream->feed != stream) {
1917 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1918 eosf = sfilename + strlen(sfilename);
1919 if (eosf - sfilename >= 4) {
1920 if (strcmp(eosf - 4, ".asf") == 0)
1921 strcpy(eosf - 4, ".asx");
1922 else if (strcmp(eosf - 3, ".rm") == 0)
1923 strcpy(eosf - 3, ".ram");
1924 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1925 /* generate a sample RTSP director if
1926 unicast. Generate an SDP redirector if
1928 eosf = strrchr(sfilename, '.');
1930 eosf = sfilename + strlen(sfilename);
1931 if (stream->is_multicast)
1932 strcpy(eosf, ".sdp");
1934 strcpy(eosf, ".rtsp");
1938 avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1939 sfilename, stream->filename);
1940 avio_printf(pb, "<td align=right> %d <td align=right> ",
1941 stream->conns_served);
1942 fmt_bytecount(pb, stream->bytes_served);
1943 switch(stream->stream_type) {
1944 case STREAM_TYPE_LIVE: {
1945 int audio_bit_rate = 0;
1946 int video_bit_rate = 0;
1947 const char *audio_codec_name = "";
1948 const char *video_codec_name = "";
1949 const char *audio_codec_name_extra = "";
1950 const char *video_codec_name_extra = "";
1952 for(i=0;i<stream->nb_streams;i++) {
1953 AVStream *st = stream->streams[i];
1954 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1955 switch(st->codec->codec_type) {
1956 case AVMEDIA_TYPE_AUDIO:
1957 audio_bit_rate += st->codec->bit_rate;
1959 if (*audio_codec_name)
1960 audio_codec_name_extra = "...";
1961 audio_codec_name = codec->name;
1964 case AVMEDIA_TYPE_VIDEO:
1965 video_bit_rate += st->codec->bit_rate;
1967 if (*video_codec_name)
1968 video_codec_name_extra = "...";
1969 video_codec_name = codec->name;
1972 case AVMEDIA_TYPE_DATA:
1973 video_bit_rate += st->codec->bit_rate;
1979 avio_printf(pb, "<td align=center> %s <td align=right> %d <td align=right> %d <td> %s %s <td align=right> %d <td> %s %s",
1982 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1983 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1985 avio_printf(pb, "<td>%s", stream->feed->filename);
1987 avio_printf(pb, "<td>%s", stream->feed_filename);
1988 avio_printf(pb, "\n");
1992 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1996 stream = stream->next;
1998 avio_printf(pb, "</table>\n");
2000 stream = first_stream;
2001 while (stream != NULL) {
2002 if (stream->feed == stream) {
2003 avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
2005 avio_printf(pb, "Running as pid %d.\n", stream->pid);
2007 #if defined(linux) && !defined(CONFIG_NOCUTILS)
2012 /* This is somewhat linux specific I guess */
2013 snprintf(ps_cmd, sizeof(ps_cmd),
2014 "ps -o \"%%cpu,cputime\" --no-headers %d",
2017 pid_stat = popen(ps_cmd, "r");
2022 if (fscanf(pid_stat, "%10s %64s", cpuperc,
2024 avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2032 avio_printf(pb, "<p>");
2034 avio_printf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
2036 for (i = 0; i < stream->nb_streams; i++) {
2037 AVStream *st = stream->streams[i];
2038 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2039 const char *type = "unknown";
2040 char parameters[64];
2044 switch(st->codec->codec_type) {
2045 case AVMEDIA_TYPE_AUDIO:
2047 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2049 case AVMEDIA_TYPE_VIDEO:
2051 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2052 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2057 avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2058 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2060 avio_printf(pb, "</table>\n");
2063 stream = stream->next;
2066 /* connection status */
2067 avio_printf(pb, "<h2>Connection Status</h2>\n");
2069 avio_printf(pb, "Number of connections: %d / %d<br>\n",
2070 nb_connections, nb_max_connections);
2072 avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2073 current_bandwidth, max_bandwidth);
2075 avio_printf(pb, "<table>\n");
2076 avio_printf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
2077 c1 = first_http_ctx;
2079 while (c1 != NULL) {
2085 for (j = 0; j < c1->stream->nb_streams; j++) {
2086 if (!c1->stream->feed)
2087 bitrate += c1->stream->streams[j]->codec->bit_rate;
2088 else if (c1->feed_streams[j] >= 0)
2089 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2094 p = inet_ntoa(c1->from_addr.sin_addr);
2095 avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2097 c1->stream ? c1->stream->filename : "",
2098 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2101 http_state[c1->state]);
2102 fmt_bytecount(pb, bitrate);
2103 avio_printf(pb, "<td align=right>");
2104 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2105 avio_printf(pb, "<td align=right>");
2106 fmt_bytecount(pb, c1->data_count);
2107 avio_printf(pb, "\n");
2110 avio_printf(pb, "</table>\n");
2115 avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
2116 avio_printf(pb, "</body>\n</html>\n");
2118 len = avio_close_dyn_buf(pb, &c->pb_buffer);
2119 c->buffer_ptr = c->pb_buffer;
2120 c->buffer_end = c->pb_buffer + len;
2123 static int open_input_stream(HTTPContext *c, const char *info)
2126 char input_filename[1024];
2127 AVFormatContext *s = NULL;
2128 int buf_size, i, ret;
2131 /* find file name */
2132 if (c->stream->feed) {
2133 strcpy(input_filename, c->stream->feed->feed_filename);
2134 buf_size = FFM_PACKET_SIZE;
2135 /* compute position (absolute time) */
2136 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2137 if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
2139 } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2140 int prebuffer = strtol(buf, 0, 10);
2141 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2143 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2145 strcpy(input_filename, c->stream->feed_filename);
2147 /* compute position (relative time) */
2148 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2149 if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
2154 if (input_filename[0] == '\0')
2158 if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2159 http_log("could not open %s: %d\n", input_filename, ret);
2163 /* set buffer size */
2164 if (buf_size > 0) ffio_set_buf_size(s->pb, buf_size);
2166 s->flags |= AVFMT_FLAG_GENPTS;
2168 if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2169 http_log("Could not find stream info '%s'\n", input_filename);
2170 avformat_close_input(&s);
2174 /* choose stream as clock source (we favorize video stream if
2175 present) for packet sending */
2176 c->pts_stream_index = 0;
2177 for(i=0;i<c->stream->nb_streams;i++) {
2178 if (c->pts_stream_index == 0 &&
2179 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2180 c->pts_stream_index = i;
2184 if (c->fmt_in->iformat->read_seek)
2185 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2186 /* set the start time (needed for maxtime and RTP packet timing) */
2187 c->start_time = cur_time;
2188 c->first_pts = AV_NOPTS_VALUE;
2192 /* return the server clock (in us) */
2193 static int64_t get_server_clock(HTTPContext *c)
2195 /* compute current pts value from system time */
2196 return (cur_time - c->start_time) * 1000;
2199 /* return the estimated time at which the current packet must be sent
2201 static int64_t get_packet_send_clock(HTTPContext *c)
2203 int bytes_left, bytes_sent, frame_bytes;
2205 frame_bytes = c->cur_frame_bytes;
2206 if (frame_bytes <= 0)
2209 bytes_left = c->buffer_end - c->buffer_ptr;
2210 bytes_sent = frame_bytes - bytes_left;
2211 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2216 static int http_prepare_data(HTTPContext *c)
2219 AVFormatContext *ctx;
2221 av_freep(&c->pb_buffer);
2223 case HTTPSTATE_SEND_DATA_HEADER:
2224 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2225 av_dict_set(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2226 av_dict_set(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2227 av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2228 av_dict_set(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2230 c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2232 for(i=0;i<c->stream->nb_streams;i++) {
2234 c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2235 /* if file or feed, then just take streams from FFStream struct */
2236 if (!c->stream->feed ||
2237 c->stream->feed == c->stream)
2238 src = c->stream->streams[i];
2240 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2242 *(c->fmt_ctx.streams[i]) = *src;
2243 c->fmt_ctx.streams[i]->priv_data = 0;
2244 c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2245 AVStream, not in codec */
2247 /* set output format parameters */
2248 c->fmt_ctx.oformat = c->stream->fmt;
2249 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2251 c->got_key_frame = 0;
2253 /* prepare header and save header data in a stream */
2254 if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2255 /* XXX: potential leak */
2258 c->fmt_ctx.pb->seekable = 0;
2261 * HACK to avoid mpeg ps muxer to spit many underflow errors
2262 * Default value from FFmpeg
2263 * Try to set it use configuration option
2265 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2267 if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
2268 http_log("Error writing output header\n");
2271 av_dict_free(&c->fmt_ctx.metadata);
2273 len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2274 c->buffer_ptr = c->pb_buffer;
2275 c->buffer_end = c->pb_buffer + len;
2277 c->state = HTTPSTATE_SEND_DATA;
2278 c->last_packet_sent = 0;
2280 case HTTPSTATE_SEND_DATA:
2281 /* find a new packet */
2282 /* read a packet from the input stream */
2283 if (c->stream->feed)
2284 ffm_set_write_index(c->fmt_in,
2285 c->stream->feed->feed_write_index,
2286 c->stream->feed->feed_size);
2288 if (c->stream->max_time &&
2289 c->stream->max_time + c->start_time - cur_time < 0)
2290 /* We have timed out */
2291 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2295 ret = av_read_frame(c->fmt_in, &pkt);
2297 if (c->stream->feed) {
2298 /* if coming from feed, it means we reached the end of the
2299 ffm file, so must wait for more data */
2300 c->state = HTTPSTATE_WAIT_FEED;
2301 return 1; /* state changed */
2302 } else if (ret == AVERROR(EAGAIN)) {
2303 /* input not ready, come back later */
2306 if (c->stream->loop) {
2307 avformat_close_input(&c->fmt_in);
2308 if (open_input_stream(c, "") < 0)
2313 /* must send trailer now because eof or error */
2314 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2318 int source_index = pkt.stream_index;
2319 /* update first pts if needed */
2320 if (c->first_pts == AV_NOPTS_VALUE) {
2321 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2322 c->start_time = cur_time;
2324 /* send it to the appropriate stream */
2325 if (c->stream->feed) {
2326 /* if coming from a feed, select the right stream */
2327 if (c->switch_pending) {
2328 c->switch_pending = 0;
2329 for(i=0;i<c->stream->nb_streams;i++) {
2330 if (c->switch_feed_streams[i] == pkt.stream_index)
2331 if (pkt.flags & AV_PKT_FLAG_KEY)
2332 c->switch_feed_streams[i] = -1;
2333 if (c->switch_feed_streams[i] >= 0)
2334 c->switch_pending = 1;
2337 for(i=0;i<c->stream->nb_streams;i++) {
2338 if (c->stream->feed_streams[i] == pkt.stream_index) {
2339 AVStream *st = c->fmt_in->streams[source_index];
2340 pkt.stream_index = i;
2341 if (pkt.flags & AV_PKT_FLAG_KEY &&
2342 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2343 c->stream->nb_streams == 1))
2344 c->got_key_frame = 1;
2345 if (!c->stream->send_on_key || c->got_key_frame)
2350 AVCodecContext *codec;
2351 AVStream *ist, *ost;
2353 ist = c->fmt_in->streams[source_index];
2354 /* specific handling for RTP: we use several
2355 output stream (one for each RTP
2356 connection). XXX: need more abstract handling */
2357 if (c->is_packetized) {
2358 /* compute send time and duration */
2359 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2360 c->cur_pts -= c->first_pts;
2361 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2362 /* find RTP context */
2363 c->packet_stream_index = pkt.stream_index;
2364 ctx = c->rtp_ctx[c->packet_stream_index];
2366 av_free_packet(&pkt);
2369 codec = ctx->streams[0]->codec;
2370 /* only one stream per RTP connection */
2371 pkt.stream_index = 0;
2375 codec = ctx->streams[pkt.stream_index]->codec;
2378 if (c->is_packetized) {
2379 int max_packet_size;
2380 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2381 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2383 max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
2384 ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2386 ret = avio_open_dyn_buf(&ctx->pb);
2389 /* XXX: potential leak */
2392 ost = ctx->streams[pkt.stream_index];
2394 ctx->pb->seekable = 0;
2395 if (pkt.dts != AV_NOPTS_VALUE)
2396 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2397 if (pkt.pts != AV_NOPTS_VALUE)
2398 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2399 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2400 if (av_write_frame(ctx, &pkt) < 0) {
2401 http_log("Error writing frame to output\n");
2402 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2405 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2406 c->cur_frame_bytes = len;
2407 c->buffer_ptr = c->pb_buffer;
2408 c->buffer_end = c->pb_buffer + len;
2410 codec->frame_number++;
2412 av_free_packet(&pkt);
2416 av_free_packet(&pkt);
2421 case HTTPSTATE_SEND_DATA_TRAILER:
2422 /* last packet test ? */
2423 if (c->last_packet_sent || c->is_packetized)
2426 /* prepare header */
2427 if (avio_open_dyn_buf(&ctx->pb) < 0) {
2428 /* XXX: potential leak */
2431 c->fmt_ctx.pb->seekable = 0;
2432 av_write_trailer(ctx);
2433 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2434 c->buffer_ptr = c->pb_buffer;
2435 c->buffer_end = c->pb_buffer + len;
2437 c->last_packet_sent = 1;
2443 /* should convert the format at the same time */
2444 /* send data starting at c->buffer_ptr to the output connection
2445 (either UDP or TCP connection) */
2446 static int http_send_data(HTTPContext *c)
2451 if (c->buffer_ptr >= c->buffer_end) {
2452 ret = http_prepare_data(c);
2456 /* state change requested */
2459 if (c->is_packetized) {
2460 /* RTP data output */
2461 len = c->buffer_end - c->buffer_ptr;
2463 /* fail safe - should never happen */
2465 c->buffer_ptr = c->buffer_end;
2468 len = (c->buffer_ptr[0] << 24) |
2469 (c->buffer_ptr[1] << 16) |
2470 (c->buffer_ptr[2] << 8) |
2472 if (len > (c->buffer_end - c->buffer_ptr))
2474 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2475 /* nothing to send yet: we can wait */
2479 c->data_count += len;
2480 update_datarate(&c->datarate, c->data_count);
2482 c->stream->bytes_served += len;
2484 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2485 /* RTP packets are sent inside the RTSP TCP connection */
2487 int interleaved_index, size;
2489 HTTPContext *rtsp_c;
2492 /* if no RTSP connection left, error */
2495 /* if already sending something, then wait. */
2496 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2498 if (avio_open_dyn_buf(&pb) < 0)
2500 interleaved_index = c->packet_stream_index * 2;
2501 /* RTCP packets are sent at odd indexes */
2502 if (c->buffer_ptr[1] == 200)
2503 interleaved_index++;
2504 /* write RTSP TCP header */
2506 header[1] = interleaved_index;
2507 header[2] = len >> 8;
2509 avio_write(pb, header, 4);
2510 /* write RTP packet data */
2512 avio_write(pb, c->buffer_ptr, len);
2513 size = avio_close_dyn_buf(pb, &c->packet_buffer);
2514 /* prepare asynchronous TCP sending */
2515 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2516 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2517 c->buffer_ptr += len;
2519 /* send everything we can NOW */
2520 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2521 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2523 rtsp_c->packet_buffer_ptr += len;
2524 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2525 /* if we could not send all the data, we will
2526 send it later, so a new state is needed to
2527 "lock" the RTSP TCP connection */
2528 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2531 /* all data has been sent */
2532 av_freep(&c->packet_buffer);
2534 /* send RTP packet directly in UDP */
2536 ffurl_write(c->rtp_handles[c->packet_stream_index],
2537 c->buffer_ptr, len);
2538 c->buffer_ptr += len;
2539 /* here we continue as we can send several packets per 10 ms slot */
2542 /* TCP data output */
2543 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2545 if (ff_neterrno() != AVERROR(EAGAIN) &&
2546 ff_neterrno() != AVERROR(EINTR))
2547 /* error : close connection */
2552 c->buffer_ptr += len;
2554 c->data_count += len;
2555 update_datarate(&c->datarate, c->data_count);
2557 c->stream->bytes_served += len;
2565 static int http_start_receive_data(HTTPContext *c)
2569 if (c->stream->feed_opened)
2572 /* Don't permit writing to this one */
2573 if (c->stream->readonly)
2577 fd = open(c->stream->feed_filename, O_RDWR);
2579 http_log("Error opening feeder file: %s\n", strerror(errno));
2584 if (c->stream->truncate) {
2585 /* truncate feed file */
2586 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2587 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2588 if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
2589 http_log("Error truncating feed file: %s\n", strerror(errno));
2593 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2594 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2599 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2600 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2601 lseek(fd, 0, SEEK_SET);
2603 /* init buffer input */
2604 c->buffer_ptr = c->buffer;
2605 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2606 c->stream->feed_opened = 1;
2607 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2611 static int http_receive_data(HTTPContext *c)
2614 int len, loop_run = 0;
2616 while (c->chunked_encoding && !c->chunk_size &&
2617 c->buffer_end > c->buffer_ptr) {
2618 /* read chunk header, if present */
2619 len = recv(c->fd, c->buffer_ptr, 1, 0);
2622 if (ff_neterrno() != AVERROR(EAGAIN) &&
2623 ff_neterrno() != AVERROR(EINTR))
2624 /* error : close connection */
2627 } else if (len == 0) {
2628 /* end of connection : close it */
2630 } else if (c->buffer_ptr - c->buffer >= 2 &&
2631 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2632 c->chunk_size = strtol(c->buffer, 0, 16);
2633 if (c->chunk_size == 0) // end of stream
2635 c->buffer_ptr = c->buffer;
2637 } else if (++loop_run > 10) {
2638 /* no chunk header, abort */
2645 if (c->buffer_end > c->buffer_ptr) {
2646 len = recv(c->fd, c->buffer_ptr,
2647 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2649 if (ff_neterrno() != AVERROR(EAGAIN) &&
2650 ff_neterrno() != AVERROR(EINTR))
2651 /* error : close connection */
2653 } else if (len == 0)
2654 /* end of connection : close it */
2657 c->chunk_size -= len;
2658 c->buffer_ptr += len;
2659 c->data_count += len;
2660 update_datarate(&c->datarate, c->data_count);
2664 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2665 if (c->buffer[0] != 'f' ||
2666 c->buffer[1] != 'm') {
2667 http_log("Feed stream has become desynchronized -- disconnecting\n");
2672 if (c->buffer_ptr >= c->buffer_end) {
2673 FFStream *feed = c->stream;
2674 /* a packet has been received : write it in the store, except
2676 if (c->data_count > FFM_PACKET_SIZE) {
2678 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2679 /* XXX: use llseek or url_seek */
2680 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2681 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2682 http_log("Error writing to feed file: %s\n", strerror(errno));
2686 feed->feed_write_index += FFM_PACKET_SIZE;
2687 /* update file size */
2688 if (feed->feed_write_index > c->stream->feed_size)
2689 feed->feed_size = feed->feed_write_index;
2691 /* handle wrap around if max file size reached */
2692 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2693 feed->feed_write_index = FFM_PACKET_SIZE;
2696 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2697 http_log("Error writing index to feed file: %s\n", strerror(errno));
2701 /* wake up any waiting connections */
2702 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2703 if (c1->state == HTTPSTATE_WAIT_FEED &&
2704 c1->stream->feed == c->stream->feed)
2705 c1->state = HTTPSTATE_SEND_DATA;
2708 /* We have a header in our hands that contains useful data */
2709 AVFormatContext *s = avformat_alloc_context();
2711 AVInputFormat *fmt_in;
2717 /* use feed output format name to find corresponding input format */
2718 fmt_in = av_find_input_format(feed->fmt->name);
2722 pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2723 0, NULL, NULL, NULL, NULL);
2727 if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2732 /* Now we have the actual streams */
2733 if (s->nb_streams != feed->nb_streams) {
2734 avformat_close_input(&s);
2736 http_log("Feed '%s' stream number does not match registered feed\n",
2737 c->stream->feed_filename);
2741 for (i = 0; i < s->nb_streams; i++) {
2742 AVStream *fst = feed->streams[i];
2743 AVStream *st = s->streams[i];
2744 avcodec_copy_context(fst->codec, st->codec);
2747 avformat_close_input(&s);
2750 c->buffer_ptr = c->buffer;
2755 c->stream->feed_opened = 0;
2757 /* wake up any waiting connections to stop waiting for feed */
2758 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2759 if (c1->state == HTTPSTATE_WAIT_FEED &&
2760 c1->stream->feed == c->stream->feed)
2761 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2766 /********************************************************************/
2769 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2776 switch(error_number) {
2777 case RTSP_STATUS_OK:
2780 case RTSP_STATUS_METHOD:
2781 str = "Method Not Allowed";
2783 case RTSP_STATUS_BANDWIDTH:
2784 str = "Not Enough Bandwidth";
2786 case RTSP_STATUS_SESSION:
2787 str = "Session Not Found";
2789 case RTSP_STATUS_STATE:
2790 str = "Method Not Valid in This State";
2792 case RTSP_STATUS_AGGREGATE:
2793 str = "Aggregate operation not allowed";
2795 case RTSP_STATUS_ONLY_AGGREGATE:
2796 str = "Only aggregate operation allowed";
2798 case RTSP_STATUS_TRANSPORT:
2799 str = "Unsupported transport";
2801 case RTSP_STATUS_INTERNAL:
2802 str = "Internal Server Error";
2804 case RTSP_STATUS_SERVICE:
2805 str = "Service Unavailable";
2807 case RTSP_STATUS_VERSION:
2808 str = "RTSP Version not supported";
2811 str = "Unknown Error";
2815 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2816 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2818 /* output GMT time */
2821 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2822 avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2825 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2827 rtsp_reply_header(c, error_number);
2828 avio_printf(c->pb, "\r\n");
2831 static int rtsp_parse_request(HTTPContext *c)
2833 const char *p, *p1, *p2;
2839 RTSPMessageHeader header1 = { 0 }, *header = &header1;
2841 c->buffer_ptr[0] = '\0';
2844 get_word(cmd, sizeof(cmd), &p);
2845 get_word(url, sizeof(url), &p);
2846 get_word(protocol, sizeof(protocol), &p);
2848 av_strlcpy(c->method, cmd, sizeof(c->method));
2849 av_strlcpy(c->url, url, sizeof(c->url));
2850 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2852 if (avio_open_dyn_buf(&c->pb) < 0) {
2853 /* XXX: cannot do more */
2854 c->pb = NULL; /* safety */
2858 /* check version name */
2859 if (strcmp(protocol, "RTSP/1.0") != 0) {
2860 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2864 /* parse each header line */
2865 /* skip to next line */
2866 while (*p != '\n' && *p != '\0')
2870 while (*p != '\0') {
2871 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2875 if (p2 > p && p2[-1] == '\r')
2877 /* skip empty line */
2881 if (len > sizeof(line) - 1)
2882 len = sizeof(line) - 1;
2883 memcpy(line, p, len);
2885 ff_rtsp_parse_line(header, line, NULL, NULL);
2889 /* handle sequence number */
2890 c->seq = header->seq;
2892 if (!strcmp(cmd, "DESCRIBE"))
2893 rtsp_cmd_describe(c, url);
2894 else if (!strcmp(cmd, "OPTIONS"))
2895 rtsp_cmd_options(c, url);
2896 else if (!strcmp(cmd, "SETUP"))
2897 rtsp_cmd_setup(c, url, header);
2898 else if (!strcmp(cmd, "PLAY"))
2899 rtsp_cmd_play(c, url, header);
2900 else if (!strcmp(cmd, "PAUSE"))
2901 rtsp_cmd_pause(c, url, header);
2902 else if (!strcmp(cmd, "TEARDOWN"))
2903 rtsp_cmd_teardown(c, url, header);
2905 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2908 len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2909 c->pb = NULL; /* safety */
2911 /* XXX: cannot do more */
2914 c->buffer_ptr = c->pb_buffer;
2915 c->buffer_end = c->pb_buffer + len;
2916 c->state = RTSPSTATE_SEND_REPLY;
2920 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2921 struct in_addr my_ip)
2923 AVFormatContext *avc;
2924 AVStream *avs = NULL;
2927 avc = avformat_alloc_context();
2931 av_dict_set(&avc->metadata, "title",
2932 stream->title[0] ? stream->title : "No Title", 0);
2933 avc->nb_streams = stream->nb_streams;
2934 if (stream->is_multicast) {
2935 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2936 inet_ntoa(stream->multicast_ip),
2937 stream->multicast_port, stream->multicast_ttl);
2939 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2942 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2943 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2945 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2946 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2949 for(i = 0; i < stream->nb_streams; i++) {
2950 avc->streams[i] = &avs[i];
2951 avc->streams[i]->codec = stream->streams[i]->codec;
2953 *pbuffer = av_mallocz(2048);
2954 av_sdp_create(&avc, 1, *pbuffer, 2048);
2957 av_free(avc->streams);
2958 av_dict_free(&avc->metadata);
2962 return strlen(*pbuffer);
2965 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2967 // rtsp_reply_header(c, RTSP_STATUS_OK);
2968 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2969 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2970 avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2971 avio_printf(c->pb, "\r\n");
2974 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2980 int content_length, len;
2981 struct sockaddr_in my_addr;
2983 /* find which url is asked */
2984 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2989 for(stream = first_stream; stream != NULL; stream = stream->next) {
2990 if (!stream->is_feed &&
2991 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2992 !strcmp(path, stream->filename)) {
2996 /* no stream found */
2997 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3001 /* prepare the media description in sdp format */
3003 /* get the host IP */
3004 len = sizeof(my_addr);
3005 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
3006 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3007 if (content_length < 0) {
3008 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3011 rtsp_reply_header(c, RTSP_STATUS_OK);
3012 avio_printf(c->pb, "Content-Base: %s/\r\n", url);
3013 avio_printf(c->pb, "Content-Type: application/sdp\r\n");
3014 avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
3015 avio_printf(c->pb, "\r\n");
3016 avio_write(c->pb, content, content_length);
3020 static HTTPContext *find_rtp_session(const char *session_id)
3024 if (session_id[0] == '\0')
3027 for(c = first_http_ctx; c != NULL; c = c->next) {
3028 if (!strcmp(c->session_id, session_id))
3034 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3036 RTSPTransportField *th;
3039 for(i=0;i<h->nb_transports;i++) {
3040 th = &h->transports[i];
3041 if (th->lower_transport == lower_transport)
3047 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3048 RTSPMessageHeader *h)
3051 int stream_index, rtp_port, rtcp_port;
3056 RTSPTransportField *th;
3057 struct sockaddr_in dest_addr;
3058 RTSPActionServerSetup setup;
3060 /* find which url is asked */
3061 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3066 /* now check each stream */
3067 for(stream = first_stream; stream != NULL; stream = stream->next) {
3068 if (!stream->is_feed &&
3069 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3070 /* accept aggregate filenames only if single stream */
3071 if (!strcmp(path, stream->filename)) {
3072 if (stream->nb_streams != 1) {
3073 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3080 for(stream_index = 0; stream_index < stream->nb_streams;
3082 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3083 stream->filename, stream_index);
3084 if (!strcmp(path, buf))
3089 /* no stream found */
3090 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3094 /* generate session id if needed */
3095 if (h->session_id[0] == '\0')
3096 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3097 av_lfg_get(&random_state), av_lfg_get(&random_state));
3099 /* find rtp session, and create it if none found */
3100 rtp_c = find_rtp_session(h->session_id);
3102 /* always prefer UDP */
3103 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3105 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3107 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3112 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3113 th->lower_transport);
3115 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3119 /* open input stream */
3120 if (open_input_stream(rtp_c, "") < 0) {
3121 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3126 /* test if stream is OK (test needed because several SETUP needs
3127 to be done for a given file) */
3128 if (rtp_c->stream != stream) {
3129 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3133 /* test if stream is already set up */
3134 if (rtp_c->rtp_ctx[stream_index]) {
3135 rtsp_reply_error(c, RTSP_STATUS_STATE);
3139 /* check transport */
3140 th = find_transport(h, rtp_c->rtp_protocol);
3141 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3142 th->client_port_min <= 0)) {
3143 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3147 /* setup default options */
3148 setup.transport_option[0] = '\0';
3149 dest_addr = rtp_c->from_addr;
3150 dest_addr.sin_port = htons(th->client_port_min);
3153 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3154 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3158 /* now everything is OK, so we can send the connection parameters */
3159 rtsp_reply_header(c, RTSP_STATUS_OK);
3161 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3163 switch(rtp_c->rtp_protocol) {
3164 case RTSP_LOWER_TRANSPORT_UDP:
3165 rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3166 rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3167 avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3168 "client_port=%d-%d;server_port=%d-%d",
3169 th->client_port_min, th->client_port_max,
3170 rtp_port, rtcp_port);
3172 case RTSP_LOWER_TRANSPORT_TCP:
3173 avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3174 stream_index * 2, stream_index * 2 + 1);
3179 if (setup.transport_option[0] != '\0')
3180 avio_printf(c->pb, ";%s", setup.transport_option);
3181 avio_printf(c->pb, "\r\n");
3184 avio_printf(c->pb, "\r\n");
3188 /* find an rtp connection by using the session ID. Check consistency
3190 static HTTPContext *find_rtp_session_with_url(const char *url,
3191 const char *session_id)
3199 rtp_c = find_rtp_session(session_id);
3203 /* find which url is asked */
3204 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3208 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3209 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3210 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3211 rtp_c->stream->filename, s);
3212 if(!strncmp(path, buf, sizeof(buf))) {
3213 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3218 if (len > 0 && path[len - 1] == '/' &&
3219 !strncmp(path, rtp_c->stream->filename, len - 1))
3224 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3228 rtp_c = find_rtp_session_with_url(url, h->session_id);
3230 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3234 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3235 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3236 rtp_c->state != HTTPSTATE_READY) {
3237 rtsp_reply_error(c, RTSP_STATUS_STATE);
3241 rtp_c->state = HTTPSTATE_SEND_DATA;
3243 /* now everything is OK, so we can send the connection parameters */
3244 rtsp_reply_header(c, RTSP_STATUS_OK);
3246 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3247 avio_printf(c->pb, "\r\n");
3250 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3254 rtp_c = find_rtp_session_with_url(url, h->session_id);
3256 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3260 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3261 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3262 rtsp_reply_error(c, RTSP_STATUS_STATE);
3266 rtp_c->state = HTTPSTATE_READY;
3267 rtp_c->first_pts = AV_NOPTS_VALUE;
3268 /* now everything is OK, so we can send the connection parameters */
3269 rtsp_reply_header(c, RTSP_STATUS_OK);
3271 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3272 avio_printf(c->pb, "\r\n");
3275 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3279 rtp_c = find_rtp_session_with_url(url, h->session_id);
3281 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3285 /* now everything is OK, so we can send the connection parameters */
3286 rtsp_reply_header(c, RTSP_STATUS_OK);
3288 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3289 avio_printf(c->pb, "\r\n");
3291 /* abort the session */
3292 close_connection(rtp_c);
3296 /********************************************************************/
3299 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3300 FFStream *stream, const char *session_id,
3301 enum RTSPLowerTransport rtp_protocol)
3303 HTTPContext *c = NULL;
3304 const char *proto_str;
3306 /* XXX: should output a warning page when coming
3307 close to the connection limit */
3308 if (nb_connections >= nb_max_connections)
3311 /* add a new connection */
3312 c = av_mallocz(sizeof(HTTPContext));
3317 c->poll_entry = NULL;
3318 c->from_addr = *from_addr;
3319 c->buffer_size = IOBUFFER_INIT_SIZE;
3320 c->buffer = av_malloc(c->buffer_size);
3325 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3326 c->state = HTTPSTATE_READY;
3327 c->is_packetized = 1;
3328 c->rtp_protocol = rtp_protocol;
3330 /* protocol is shown in statistics */
3331 switch(c->rtp_protocol) {
3332 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3333 proto_str = "MCAST";
3335 case RTSP_LOWER_TRANSPORT_UDP:
3338 case RTSP_LOWER_TRANSPORT_TCP:
3345 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3346 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3348 current_bandwidth += stream->bandwidth;
3350 c->next = first_http_ctx;
3362 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3363 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3365 static int rtp_new_av_stream(HTTPContext *c,
3366 int stream_index, struct sockaddr_in *dest_addr,
3367 HTTPContext *rtsp_c)
3369 AVFormatContext *ctx;
3372 URLContext *h = NULL;
3374 int max_packet_size;
3376 /* now we can open the relevant output stream */
3377 ctx = avformat_alloc_context();
3380 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3382 st = av_mallocz(sizeof(AVStream));
3385 ctx->nb_streams = 1;
3386 ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3389 ctx->streams[0] = st;
3391 if (!c->stream->feed ||
3392 c->stream->feed == c->stream)
3393 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3396 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3398 st->priv_data = NULL;
3400 /* build destination RTP address */
3401 ipaddr = inet_ntoa(dest_addr->sin_addr);
3403 switch(c->rtp_protocol) {
3404 case RTSP_LOWER_TRANSPORT_UDP:
3405 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3408 /* XXX: also pass as parameter to function ? */
3409 if (c->stream->is_multicast) {
3411 ttl = c->stream->multicast_ttl;
3414 snprintf(ctx->filename, sizeof(ctx->filename),
3415 "rtp://%s:%d?multicast=1&ttl=%d",
3416 ipaddr, ntohs(dest_addr->sin_port), ttl);
3418 snprintf(ctx->filename, sizeof(ctx->filename),
3419 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3422 if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3424 c->rtp_handles[stream_index] = h;
3425 max_packet_size = h->max_packet_size;
3427 case RTSP_LOWER_TRANSPORT_TCP:
3430 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3436 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3437 ipaddr, ntohs(dest_addr->sin_port),
3438 c->stream->filename, stream_index, c->protocol);
3440 /* normally, no packets should be output here, but the packet size may be checked */
3441 if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3442 /* XXX: close stream */
3445 if (avformat_write_header(ctx, NULL) < 0) {
3452 avio_close_dyn_buf(ctx->pb, &dummy_buf);
3455 c->rtp_ctx[stream_index] = ctx;
3459 /********************************************************************/
3460 /* ffserver initialization */
3462 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3466 fst = av_mallocz(sizeof(AVStream));
3470 fst->codec = avcodec_alloc_context3(NULL);
3471 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3472 if (codec->extradata_size) {
3473 fst->codec->extradata = av_malloc(codec->extradata_size);
3474 memcpy(fst->codec->extradata, codec->extradata,
3475 codec->extradata_size);
3478 /* live streams must use the actual feed's codec since it may be
3479 * updated later to carry extradata needed by the streams.
3483 fst->priv_data = av_mallocz(sizeof(FeedData));
3484 fst->index = stream->nb_streams;
3485 avpriv_set_pts_info(fst, 33, 1, 90000);
3486 fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3487 stream->streams[stream->nb_streams++] = fst;
3491 /* return the stream number in the feed */
3492 static int add_av_stream(FFStream *feed, AVStream *st)
3495 AVCodecContext *av, *av1;
3499 for(i=0;i<feed->nb_streams;i++) {
3500 st = feed->streams[i];
3502 if (av1->codec_id == av->codec_id &&
3503 av1->codec_type == av->codec_type &&
3504 av1->bit_rate == av->bit_rate) {
3506 switch(av->codec_type) {
3507 case AVMEDIA_TYPE_AUDIO:
3508 if (av1->channels == av->channels &&
3509 av1->sample_rate == av->sample_rate)
3512 case AVMEDIA_TYPE_VIDEO:
3513 if (av1->width == av->width &&
3514 av1->height == av->height &&
3515 av1->time_base.den == av->time_base.den &&
3516 av1->time_base.num == av->time_base.num &&
3517 av1->gop_size == av->gop_size)
3526 fst = add_av_stream1(feed, av, 0);
3529 return feed->nb_streams - 1;
3532 static void remove_stream(FFStream *stream)
3536 while (*ps != NULL) {
3544 /* specific mpeg4 handling : we extract the raw parameters */
3545 static void extract_mpeg4_header(AVFormatContext *infile)
3547 int mpeg4_count, i, size;
3553 for(i=0;i<infile->nb_streams;i++) {
3554 st = infile->streams[i];
3555 if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3556 st->codec->extradata_size == 0) {
3563 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3564 while (mpeg4_count > 0) {
3565 if (av_read_packet(infile, &pkt) < 0)
3567 st = infile->streams[pkt.stream_index];
3568 if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3569 st->codec->extradata_size == 0) {
3570 av_freep(&st->codec->extradata);
3571 /* fill extradata with the header */
3572 /* XXX: we make hard suppositions here ! */
3574 while (p < pkt.data + pkt.size - 4) {
3575 /* stop when vop header is found */
3576 if (p[0] == 0x00 && p[1] == 0x00 &&
3577 p[2] == 0x01 && p[3] == 0xb6) {
3578 size = p - pkt.data;
3579 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3580 st->codec->extradata = av_malloc(size);
3581 st->codec->extradata_size = size;
3582 memcpy(st->codec->extradata, pkt.data, size);
3589 av_free_packet(&pkt);
3593 /* compute the needed AVStream for each file */
3594 static void build_file_streams(void)
3596 FFStream *stream, *stream_next;
3599 /* gather all streams */
3600 for(stream = first_stream; stream != NULL; stream = stream_next) {
3601 AVFormatContext *infile = NULL;
3602 stream_next = stream->next;
3603 if (stream->stream_type == STREAM_TYPE_LIVE &&
3605 /* the stream comes from a file */
3606 /* try to open the file */
3608 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3609 /* specific case : if transport stream output to RTP,
3610 we use a raw transport stream reader */
3611 av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3614 http_log("Opening file '%s'\n", stream->feed_filename);
3615 if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3616 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3617 /* remove stream (no need to spend more time on it) */
3619 remove_stream(stream);
3621 /* find all the AVStreams inside and reference them in
3623 if (avformat_find_stream_info(infile, NULL) < 0) {
3624 http_log("Could not find codec parameters from '%s'\n",
3625 stream->feed_filename);
3626 avformat_close_input(&infile);
3629 extract_mpeg4_header(infile);
3631 for(i=0;i<infile->nb_streams;i++)
3632 add_av_stream1(stream, infile->streams[i]->codec, 1);
3634 avformat_close_input(&infile);
3640 /* compute the needed AVStream for each feed */
3641 static void build_feed_streams(void)
3643 FFStream *stream, *feed;
3646 /* gather all streams */
3647 for(stream = first_stream; stream != NULL; stream = stream->next) {
3648 feed = stream->feed;
3650 if (stream->is_feed) {
3651 for(i=0;i<stream->nb_streams;i++)
3652 stream->feed_streams[i] = i;
3654 /* we handle a stream coming from a feed */
3655 for(i=0;i<stream->nb_streams;i++)
3656 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3661 /* create feed files if needed */
3662 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3665 if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3666 /* See if it matches */
3667 AVFormatContext *s = NULL;
3670 if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3671 /* set buffer size */
3672 ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
3673 /* Now see if it matches */
3674 if (s->nb_streams == feed->nb_streams) {
3676 for(i=0;i<s->nb_streams;i++) {
3678 sf = feed->streams[i];
3681 if (sf->index != ss->index ||
3683 http_log("Index & Id do not match for stream %d (%s)\n",
3684 i, feed->feed_filename);
3687 AVCodecContext *ccf, *ccs;
3691 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3693 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3694 http_log("Codecs do not match for stream %d\n", i);
3696 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3697 http_log("Codec bitrates do not match for stream %d\n", i);
3699 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3700 if (CHECK_CODEC(time_base.den) ||
3701 CHECK_CODEC(time_base.num) ||
3702 CHECK_CODEC(width) ||
3703 CHECK_CODEC(height)) {
3704 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3707 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3708 if (CHECK_CODEC(sample_rate) ||
3709 CHECK_CODEC(channels) ||
3710 CHECK_CODEC(frame_size)) {
3711 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3715 http_log("Unknown codec type\n");
3723 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3724 feed->feed_filename, s->nb_streams, feed->nb_streams);
3726 avformat_close_input(&s);
3728 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3729 feed->feed_filename);
3732 if (feed->readonly) {
3733 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3734 feed->feed_filename);
3737 unlink(feed->feed_filename);
3740 if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3741 AVFormatContext s1 = {0}, *s = &s1;
3743 if (feed->readonly) {
3744 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3745 feed->feed_filename);
3749 /* only write the header of the ffm file */
3750 if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3751 http_log("Could not open output feed file '%s'\n",
3752 feed->feed_filename);
3755 s->oformat = feed->fmt;
3756 s->nb_streams = feed->nb_streams;
3757 s->streams = feed->streams;
3758 if (avformat_write_header(s, NULL) < 0) {
3759 http_log("Container doesn't supports the required parameters\n");
3762 /* XXX: need better api */
3763 av_freep(&s->priv_data);
3766 /* get feed size and write index */
3767 fd = open(feed->feed_filename, O_RDONLY);
3769 http_log("Could not open output feed file '%s'\n",
3770 feed->feed_filename);
3774 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3775 feed->feed_size = lseek(fd, 0, SEEK_END);
3776 /* ensure that we do not wrap before the end of file */
3777 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3778 feed->feed_max_size = feed->feed_size;
3784 /* compute the bandwidth used by each stream */
3785 static void compute_bandwidth(void)
3791 for(stream = first_stream; stream != NULL; stream = stream->next) {
3793 for(i=0;i<stream->nb_streams;i++) {
3794 AVStream *st = stream->streams[i];
3795 switch(st->codec->codec_type) {
3796 case AVMEDIA_TYPE_AUDIO:
3797 case AVMEDIA_TYPE_VIDEO:
3798 bandwidth += st->codec->bit_rate;
3804 stream->bandwidth = (bandwidth + 999) / 1000;
3808 /* add a codec and set the default parameters */
3809 static void add_codec(FFStream *stream, AVCodecContext *av)
3813 /* compute default parameters */
3814 switch(av->codec_type) {
3815 case AVMEDIA_TYPE_AUDIO:
3816 if (av->bit_rate == 0)
3817 av->bit_rate = 64000;
3818 if (av->sample_rate == 0)
3819 av->sample_rate = 22050;
3820 if (av->channels == 0)
3823 case AVMEDIA_TYPE_VIDEO:
3824 if (av->bit_rate == 0)
3825 av->bit_rate = 64000;
3826 if (av->time_base.num == 0){
3827 av->time_base.den = 5;
3828 av->time_base.num = 1;
3830 if (av->width == 0 || av->height == 0) {
3834 /* Bitrate tolerance is less for streaming */
3835 if (av->bit_rate_tolerance == 0)
3836 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3837 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3842 if (av->max_qdiff == 0)
3844 av->qcompress = 0.5;
3847 if (!av->nsse_weight)
3848 av->nsse_weight = 8;
3850 av->frame_skip_cmp = FF_CMP_DCTMAX;
3852 av->me_method = ME_EPZS;
3853 av->rc_buffer_aggressivity = 1.0;
3856 av->rc_eq = "tex^qComp";
3857 if (!av->i_quant_factor)
3858 av->i_quant_factor = -0.8;
3859 if (!av->b_quant_factor)
3860 av->b_quant_factor = 1.25;
3861 if (!av->b_quant_offset)
3862 av->b_quant_offset = 1.25;
3863 if (!av->rc_max_rate)
3864 av->rc_max_rate = av->bit_rate * 2;
3866 if (av->rc_max_rate && !av->rc_buffer_size) {
3867 av->rc_buffer_size = av->rc_max_rate;
3876 st = av_mallocz(sizeof(AVStream));
3879 st->codec = avcodec_alloc_context3(NULL);
3880 stream->streams[stream->nb_streams++] = st;
3881 memcpy(st->codec, av, sizeof(AVCodecContext));
3884 static enum AVCodecID opt_audio_codec(const char *arg)
3886 AVCodec *p= avcodec_find_encoder_by_name(arg);
3888 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3889 return AV_CODEC_ID_NONE;
3894 static enum AVCodecID opt_video_codec(const char *arg)
3896 AVCodec *p= avcodec_find_encoder_by_name(arg);
3898 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3899 return AV_CODEC_ID_NONE;
3904 /* simplistic plugin support */
3907 static void load_module(const char *filename)
3910 void (*init_func)(void);
3911 dll = dlopen(filename, RTLD_NOW);
3913 fprintf(stderr, "Could not load module '%s' - %s\n",
3914 filename, dlerror());
3918 init_func = dlsym(dll, "ffserver_module_init");
3921 "%s: init function 'ffserver_module_init()' not found\n",
3930 static int ffserver_opt_default(const char *opt, const char *arg,
3931 AVCodecContext *avctx, int type)
3934 const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3936 ret = av_opt_set(avctx, opt, arg, 0);
3940 static int ffserver_opt_preset(const char *arg,
3941 AVCodecContext *avctx, int type,
3942 enum AVCodecID *audio_id, enum AVCodecID *video_id)
3945 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3947 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3949 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3950 codec ? codec->name : NULL))) {
3951 fprintf(stderr, "File for preset '%s' not found\n", arg);
3956 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3957 if(line[0] == '#' && !e)
3959 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3961 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3965 if(!strcmp(tmp, "acodec")){
3966 *audio_id = opt_audio_codec(tmp2);
3967 }else if(!strcmp(tmp, "vcodec")){
3968 *video_id = opt_video_codec(tmp2);
3969 }else if(!strcmp(tmp, "scodec")){
3970 /* opt_subtitle_codec(tmp2); */
3971 }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
3972 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
3983 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
3984 const char *mime_type)
3986 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3989 AVOutputFormat *stream_fmt;
3990 char stream_format_name[64];
3992 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
3993 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4002 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
4006 fprintf(stderr, "%s:%d: ", filename, line_num);
4007 vfprintf(stderr, fmt, vl);
4013 static int parse_ffconfig(const char *filename)
4020 int val, errors, line_num;
4021 FFStream **last_stream, *stream, *redirect;
4022 FFStream **last_feed, *feed, *s;
4023 AVCodecContext audio_enc, video_enc;
4024 enum AVCodecID audio_id, video_id;
4026 f = fopen(filename, "r");
4034 first_stream = NULL;
4035 last_stream = &first_stream;
4037 last_feed = &first_feed;
4041 audio_id = AV_CODEC_ID_NONE;
4042 video_id = AV_CODEC_ID_NONE;
4044 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4046 if (fgets(line, sizeof(line), f) == NULL)
4052 if (*p == '\0' || *p == '#')
4055 get_arg(cmd, sizeof(cmd), &p);
4057 if (!av_strcasecmp(cmd, "Port")) {
4058 get_arg(arg, sizeof(arg), &p);
4060 if (val < 1 || val > 65536) {
4061 ERROR("Invalid_port: %s\n", arg);
4063 my_http_addr.sin_port = htons(val);
4064 } else if (!av_strcasecmp(cmd, "BindAddress")) {
4065 get_arg(arg, sizeof(arg), &p);
4066 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4067 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4069 } else if (!av_strcasecmp(cmd, "NoDaemon")) {
4070 ffserver_daemon = 0;
4071 } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4072 get_arg(arg, sizeof(arg), &p);
4074 if (val < 1 || val > 65536) {
4075 ERROR("%s:%d: Invalid port: %s\n", arg);
4077 my_rtsp_addr.sin_port = htons(atoi(arg));
4078 } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4079 get_arg(arg, sizeof(arg), &p);
4080 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4081 ERROR("Invalid host/IP address: %s\n", arg);
4083 } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4084 get_arg(arg, sizeof(arg), &p);
4086 if (val < 1 || val > 65536) {
4087 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4089 nb_max_http_connections = val;
4090 } else if (!av_strcasecmp(cmd, "MaxClients")) {
4091 get_arg(arg, sizeof(arg), &p);
4093 if (val < 1 || val > nb_max_http_connections) {
4094 ERROR("Invalid MaxClients: %s\n", arg);
4096 nb_max_connections = val;
4098 } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4100 get_arg(arg, sizeof(arg), &p);
4102 if (llval < 10 || llval > 10000000) {
4103 ERROR("Invalid MaxBandwidth: %s\n", arg);
4105 max_bandwidth = llval;
4106 } else if (!av_strcasecmp(cmd, "CustomLog")) {
4107 if (!ffserver_debug)
4108 get_arg(logfilename, sizeof(logfilename), &p);
4109 } else if (!av_strcasecmp(cmd, "<Feed")) {
4110 /*********************************************/
4111 /* Feed related options */
4113 if (stream || feed) {
4114 ERROR("Already in a tag\n");
4116 feed = av_mallocz(sizeof(FFStream));
4117 get_arg(feed->filename, sizeof(feed->filename), &p);
4118 q = strrchr(feed->filename, '>');
4122 for (s = first_feed; s; s = s->next) {
4123 if (!strcmp(feed->filename, s->filename)) {
4124 ERROR("Feed '%s' already registered\n", s->filename);
4128 feed->fmt = av_guess_format("ffm", NULL, NULL);
4129 /* defaut feed file */
4130 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4131 "/tmp/%s.ffm", feed->filename);
4132 feed->feed_max_size = 5 * 1024 * 1024;
4134 feed->feed = feed; /* self feeding :-) */
4136 /* add in stream list */
4137 *last_stream = feed;
4138 last_stream = &feed->next;
4139 /* add in feed list */
4141 last_feed = &feed->next_feed;
4143 } else if (!av_strcasecmp(cmd, "Launch")) {
4147 feed->child_argv = av_mallocz(64 * sizeof(char *));
4149 for (i = 0; i < 62; i++) {
4150 get_arg(arg, sizeof(arg), &p);
4154 feed->child_argv[i] = av_strdup(arg);
4157 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4159 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4161 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4162 inet_ntoa(my_http_addr.sin_addr),
4163 ntohs(my_http_addr.sin_port), feed->filename);
4165 } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4167 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4169 } else if (stream) {
4170 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4172 } else if (!av_strcasecmp(cmd, "File")) {
4174 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4176 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4177 } else if (!av_strcasecmp(cmd, "Truncate")) {
4179 get_arg(arg, sizeof(arg), &p);
4180 feed->truncate = strtod(arg, NULL);
4182 } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4187 get_arg(arg, sizeof(arg), &p);
4189 fsize = strtod(p1, &p1);
4190 switch(toupper(*p1)) {
4195 fsize *= 1024 * 1024;
4198 fsize *= 1024 * 1024 * 1024;
4201 feed->feed_max_size = (int64_t)fsize;
4202 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4203 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4206 } else if (!av_strcasecmp(cmd, "</Feed>")) {
4208 ERROR("No corresponding <Feed> for </Feed>\n");
4211 } else if (!av_strcasecmp(cmd, "<Stream")) {
4212 /*********************************************/
4213 /* Stream related options */
4215 if (stream || feed) {
4216 ERROR("Already in a tag\n");
4219 stream = av_mallocz(sizeof(FFStream));
4220 get_arg(stream->filename, sizeof(stream->filename), &p);
4221 q = strrchr(stream->filename, '>');
4225 for (s = first_stream; s; s = s->next) {
4226 if (!strcmp(stream->filename, s->filename)) {
4227 ERROR("Stream '%s' already registered\n", s->filename);
4231 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4232 avcodec_get_context_defaults3(&video_enc, NULL);
4233 avcodec_get_context_defaults3(&audio_enc, NULL);
4235 audio_id = AV_CODEC_ID_NONE;
4236 video_id = AV_CODEC_ID_NONE;
4238 audio_id = stream->fmt->audio_codec;
4239 video_id = stream->fmt->video_codec;
4242 *last_stream = stream;
4243 last_stream = &stream->next;
4245 } else if (!av_strcasecmp(cmd, "Feed")) {
4246 get_arg(arg, sizeof(arg), &p);
4251 while (sfeed != NULL) {
4252 if (!strcmp(sfeed->filename, arg))
4254 sfeed = sfeed->next_feed;
4257 ERROR("feed '%s' not defined\n", arg);
4259 stream->feed = sfeed;
4261 } else if (!av_strcasecmp(cmd, "Format")) {
4262 get_arg(arg, sizeof(arg), &p);
4264 if (!strcmp(arg, "status")) {
4265 stream->stream_type = STREAM_TYPE_STATUS;
4268 stream->stream_type = STREAM_TYPE_LIVE;
4269 /* jpeg cannot be used here, so use single frame jpeg */
4270 if (!strcmp(arg, "jpeg"))
4271 strcpy(arg, "mjpeg");
4272 stream->fmt = ffserver_guess_format(arg, NULL, NULL);
4274 ERROR("Unknown Format: %s\n", arg);
4278 audio_id = stream->fmt->audio_codec;
4279 video_id = stream->fmt->video_codec;
4282 } else if (!av_strcasecmp(cmd, "InputFormat")) {
4283 get_arg(arg, sizeof(arg), &p);
4285 stream->ifmt = av_find_input_format(arg);
4286 if (!stream->ifmt) {
4287 ERROR("Unknown input format: %s\n", arg);
4290 } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4291 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4292 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4294 ERROR("FaviconURL only permitted for status streams\n");
4296 } else if (!av_strcasecmp(cmd, "Author")) {
4298 get_arg(stream->author, sizeof(stream->author), &p);
4299 } else if (!av_strcasecmp(cmd, "Comment")) {
4301 get_arg(stream->comment, sizeof(stream->comment), &p);
4302 } else if (!av_strcasecmp(cmd, "Copyright")) {
4304 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4305 } else if (!av_strcasecmp(cmd, "Title")) {
4307 get_arg(stream->title, sizeof(stream->title), &p);
4308 } else if (!av_strcasecmp(cmd, "Preroll")) {
4309 get_arg(arg, sizeof(arg), &p);
4311 stream->prebuffer = atof(arg) * 1000;
4312 } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4314 stream->send_on_key = 1;
4315 } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4316 get_arg(arg, sizeof(arg), &p);
4317 audio_id = opt_audio_codec(arg);
4318 if (audio_id == AV_CODEC_ID_NONE) {
4319 ERROR("Unknown AudioCodec: %s\n", arg);
4321 } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4322 get_arg(arg, sizeof(arg), &p);
4323 video_id = opt_video_codec(arg);
4324 if (video_id == AV_CODEC_ID_NONE) {
4325 ERROR("Unknown VideoCodec: %s\n", arg);
4327 } else if (!av_strcasecmp(cmd, "MaxTime")) {
4328 get_arg(arg, sizeof(arg), &p);
4330 stream->max_time = atof(arg) * 1000;
4331 } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4332 get_arg(arg, sizeof(arg), &p);
4334 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4335 } else if (!av_strcasecmp(cmd, "AudioChannels")) {
4336 get_arg(arg, sizeof(arg), &p);
4338 audio_enc.channels = atoi(arg);
4339 } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4340 get_arg(arg, sizeof(arg), &p);
4342 audio_enc.sample_rate = atoi(arg);
4343 } else if (!av_strcasecmp(cmd, "AudioQuality")) {
4344 get_arg(arg, sizeof(arg), &p);
4346 // audio_enc.quality = atof(arg) * 1000;
4348 } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4350 int minrate, maxrate;
4352 get_arg(arg, sizeof(arg), &p);
4354 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4355 video_enc.rc_min_rate = minrate * 1000;
4356 video_enc.rc_max_rate = maxrate * 1000;
4358 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4361 } else if (!av_strcasecmp(cmd, "Debug")) {
4363 get_arg(arg, sizeof(arg), &p);
4364 video_enc.debug = strtol(arg,0,0);
4366 } else if (!av_strcasecmp(cmd, "Strict")) {
4368 get_arg(arg, sizeof(arg), &p);
4369 video_enc.strict_std_compliance = atoi(arg);
4371 } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4373 get_arg(arg, sizeof(arg), &p);
4374 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4376 } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4378 get_arg(arg, sizeof(arg), &p);
4379 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4381 } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4382 get_arg(arg, sizeof(arg), &p);
4384 video_enc.bit_rate = atoi(arg) * 1000;
4386 } else if (!av_strcasecmp(cmd, "VideoSize")) {
4387 get_arg(arg, sizeof(arg), &p);
4389 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4390 if ((video_enc.width % 16) != 0 ||
4391 (video_enc.height % 16) != 0) {
4392 ERROR("Image size must be a multiple of 16\n");
4395 } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4396 get_arg(arg, sizeof(arg), &p);
4398 AVRational frame_rate;
4399 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4400 ERROR("Incorrect frame rate: %s\n", arg);
4402 video_enc.time_base.num = frame_rate.den;
4403 video_enc.time_base.den = frame_rate.num;
4406 } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4407 get_arg(arg, sizeof(arg), &p);
4409 video_enc.gop_size = atoi(arg);
4410 } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4412 video_enc.gop_size = 1;
4413 } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4415 video_enc.mb_decision = FF_MB_DECISION_BITS;
4416 } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4418 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4419 video_enc.flags |= CODEC_FLAG_4MV;
4421 } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4422 !av_strcasecmp(cmd, "AVOptionAudio")) {
4424 AVCodecContext *avctx;
4426 get_arg(arg, sizeof(arg), &p);
4427 get_arg(arg2, sizeof(arg2), &p);
4428 if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4430 type = AV_OPT_FLAG_VIDEO_PARAM;
4433 type = AV_OPT_FLAG_AUDIO_PARAM;
4435 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4436 ERROR("AVOption error: %s %s\n", arg, arg2);
4438 } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4439 !av_strcasecmp(cmd, "AVPresetAudio")) {
4440 AVCodecContext *avctx;
4442 get_arg(arg, sizeof(arg), &p);
4443 if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4445 video_enc.codec_id = video_id;
4446 type = AV_OPT_FLAG_VIDEO_PARAM;
4449 audio_enc.codec_id = audio_id;
4450 type = AV_OPT_FLAG_AUDIO_PARAM;
4452 if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4453 ERROR("AVPreset error: %s\n", arg);
4455 } else if (!av_strcasecmp(cmd, "VideoTag")) {
4456 get_arg(arg, sizeof(arg), &p);
4457 if ((strlen(arg) == 4) && stream)
4458 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4459 } else if (!av_strcasecmp(cmd, "BitExact")) {
4461 video_enc.flags |= CODEC_FLAG_BITEXACT;
4462 } else if (!av_strcasecmp(cmd, "DctFastint")) {
4464 video_enc.dct_algo = FF_DCT_FASTINT;
4465 } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4467 video_enc.idct_algo = FF_IDCT_SIMPLE;
4468 } else if (!av_strcasecmp(cmd, "Qscale")) {
4469 get_arg(arg, sizeof(arg), &p);
4471 video_enc.flags |= CODEC_FLAG_QSCALE;
4472 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4474 } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4475 get_arg(arg, sizeof(arg), &p);
4477 video_enc.max_qdiff = atoi(arg);
4478 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4479 ERROR("VideoQDiff out of range\n");
4482 } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4483 get_arg(arg, sizeof(arg), &p);
4485 video_enc.qmax = atoi(arg);
4486 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4487 ERROR("VideoQMax out of range\n");
4490 } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4491 get_arg(arg, sizeof(arg), &p);
4493 video_enc.qmin = atoi(arg);
4494 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4495 ERROR("VideoQMin out of range\n");
4498 } else if (!av_strcasecmp(cmd, "LumaElim")) {
4499 get_arg(arg, sizeof(arg), &p);
4501 video_enc.luma_elim_threshold = atoi(arg);
4502 } else if (!av_strcasecmp(cmd, "ChromaElim")) {
4503 get_arg(arg, sizeof(arg), &p);
4505 video_enc.chroma_elim_threshold = atoi(arg);
4506 } else if (!av_strcasecmp(cmd, "LumiMask")) {
4507 get_arg(arg, sizeof(arg), &p);
4509 video_enc.lumi_masking = atof(arg);
4510 } else if (!av_strcasecmp(cmd, "DarkMask")) {
4511 get_arg(arg, sizeof(arg), &p);
4513 video_enc.dark_masking = atof(arg);
4514 } else if (!av_strcasecmp(cmd, "NoVideo")) {
4515 video_id = AV_CODEC_ID_NONE;
4516 } else if (!av_strcasecmp(cmd, "NoAudio")) {
4517 audio_id = AV_CODEC_ID_NONE;
4518 } else if (!av_strcasecmp(cmd, "ACL")) {
4519 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4520 } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4522 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4524 } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4525 get_arg(arg, sizeof(arg), &p);
4527 av_freep(&stream->rtsp_option);
4528 stream->rtsp_option = av_strdup(arg);
4530 } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4531 get_arg(arg, sizeof(arg), &p);
4533 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4534 ERROR("Invalid host/IP address: %s\n", arg);
4536 stream->is_multicast = 1;
4537 stream->loop = 1; /* default is looping */
4539 } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4540 get_arg(arg, sizeof(arg), &p);
4542 stream->multicast_port = atoi(arg);
4543 } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4544 get_arg(arg, sizeof(arg), &p);
4546 stream->multicast_ttl = atoi(arg);
4547 } else if (!av_strcasecmp(cmd, "NoLoop")) {
4550 } else if (!av_strcasecmp(cmd, "</Stream>")) {
4552 ERROR("No corresponding <Stream> for </Stream>\n");
4554 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4555 if (audio_id != AV_CODEC_ID_NONE) {
4556 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4557 audio_enc.codec_id = audio_id;
4558 add_codec(stream, &audio_enc);
4560 if (video_id != AV_CODEC_ID_NONE) {
4561 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4562 video_enc.codec_id = video_id;
4563 add_codec(stream, &video_enc);
4568 } else if (!av_strcasecmp(cmd, "<Redirect")) {
4569 /*********************************************/
4571 if (stream || feed || redirect) {
4572 ERROR("Already in a tag\n");
4574 redirect = av_mallocz(sizeof(FFStream));
4575 *last_stream = redirect;
4576 last_stream = &redirect->next;
4578 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4579 q = strrchr(redirect->filename, '>');
4582 redirect->stream_type = STREAM_TYPE_REDIRECT;
4584 } else if (!av_strcasecmp(cmd, "URL")) {
4586 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4587 } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4589 ERROR("No corresponding <Redirect> for </Redirect>\n");
4591 if (!redirect->feed_filename[0]) {
4592 ERROR("No URL found for <Redirect>\n");
4596 } else if (!av_strcasecmp(cmd, "LoadModule")) {
4597 get_arg(arg, sizeof(arg), &p);
4601 ERROR("Module support not compiled into this version: '%s'\n", arg);
4604 ERROR("Incorrect keyword: '%s'\n", cmd);
4616 static void handle_child_exit(int sig)
4621 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4624 for (feed = first_feed; feed; feed = feed->next) {
4625 if (feed->pid == pid) {
4626 int uptime = time(0) - feed->pid_start;
4629 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4632 /* Turn off any more restarts */
4633 feed->child_argv = 0;
4638 need_to_start_children = 1;
4641 static void opt_debug(void)
4644 ffserver_daemon = 0;
4645 logfilename[0] = '-';
4648 static int opt_help(const char *opt, const char *arg)
4650 printf("usage: ffserver [options]\n"
4651 "Hyper fast multi format Audio/Video streaming server\n");
4653 show_help_options(options, "Main options:\n", 0, 0);
4657 static const OptionDef options[] = {
4658 #include "cmdutils_common_opts.h"
4659 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4660 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4661 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4665 int main(int argc, char **argv)
4667 struct sigaction sigact = { { 0 } };
4669 parse_loglevel(argc, argv, options);
4671 avformat_network_init();
4673 show_banner(argc, argv, options);
4675 my_program_name = argv[0];
4676 my_program_dir = getcwd(0, 0);
4677 ffserver_daemon = 1;
4679 parse_options(NULL, argc, argv, options, NULL);
4681 unsetenv("http_proxy"); /* Kill the http_proxy */
4683 av_lfg_init(&random_state, av_get_random_seed());
4685 sigact.sa_handler = handle_child_exit;
4686 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4687 sigaction(SIGCHLD, &sigact, 0);
4689 if (parse_ffconfig(config_filename) < 0) {
4690 fprintf(stderr, "Incorrect config file - exiting.\n");
4694 /* open log file if needed */
4695 if (logfilename[0] != '\0') {
4696 if (!strcmp(logfilename, "-"))
4699 logfile = fopen(logfilename, "a");
4700 av_log_set_callback(http_av_log);
4703 build_file_streams();
4705 build_feed_streams();
4707 compute_bandwidth();
4709 /* put the process in background and detach it from its TTY */
4710 if (ffserver_daemon) {
4717 } else if (pid > 0) {
4724 open("/dev/null", O_RDWR);
4725 if (strcmp(logfilename, "-") != 0) {
4735 signal(SIGPIPE, SIG_IGN);
4737 if (ffserver_daemon)
4740 if (http_server() < 0) {
4741 http_log("Could not start server\n");