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 #include "libavformat/ffm.h"
34 #include "libavformat/network.h"
35 #include "libavformat/os_support.h"
36 #include "libavformat/rtpdec.h"
37 #include "libavformat/rtsp.h"
38 // XXX for ffio_open_dyn_packet_buffer, to be removed
39 #include "libavformat/avio_internal.h"
40 #include "libavutil/avstring.h"
41 #include "libavutil/lfg.h"
42 #include "libavutil/dict.h"
43 #include "libavutil/mathematics.h"
44 #include "libavutil/random_seed.h"
45 #include "libavutil/parseutils.h"
46 #include "libavutil/opt.h"
50 #include <sys/ioctl.h>
65 const char program_name[] = "ffserver";
66 const int program_birth_year = 2000;
68 static const OptionDef options[];
71 HTTPSTATE_WAIT_REQUEST,
72 HTTPSTATE_SEND_HEADER,
73 HTTPSTATE_SEND_DATA_HEADER,
74 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
75 HTTPSTATE_SEND_DATA_TRAILER,
76 HTTPSTATE_RECEIVE_DATA,
77 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
80 RTSPSTATE_WAIT_REQUEST,
82 RTSPSTATE_SEND_PACKET,
85 static const char *http_state[] = {
101 #define MAX_STREAMS 20
103 #define IOBUFFER_INIT_SIZE 8192
105 /* timeouts are in ms */
106 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
107 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
109 #define SYNC_TIMEOUT (10 * 1000)
111 typedef struct RTSPActionServerSetup {
113 char transport_option[512];
114 } RTSPActionServerSetup;
117 int64_t count1, count2;
118 int64_t time1, time2;
121 /* context associated with one connection */
122 typedef struct HTTPContext {
123 enum HTTPState state;
124 int fd; /* socket file descriptor */
125 struct sockaddr_in from_addr; /* origin */
126 struct pollfd *poll_entry; /* used when polling */
128 uint8_t *buffer_ptr, *buffer_end;
131 int chunked_encoding;
132 int chunk_size; /* 0 if it needs to be read */
133 struct HTTPContext *next;
134 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
138 /* input format handling */
139 AVFormatContext *fmt_in;
140 int64_t start_time; /* In milliseconds - this wraps fairly often */
141 int64_t first_pts; /* initial pts value */
142 int64_t cur_pts; /* current pts value from the stream in us */
143 int64_t cur_frame_duration; /* duration of the current frame in us */
144 int cur_frame_bytes; /* output frame size, needed to compute
145 the time at which we send each
147 int pts_stream_index; /* stream we choose as clock reference */
148 int64_t cur_clock; /* current clock reference value in us */
149 /* output format handling */
150 struct FFStream *stream;
151 /* -1 is invalid stream */
152 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
153 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
155 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
156 int last_packet_sent; /* true if last data packet was sent */
158 DataRateData datarate;
165 int is_packetized; /* if true, the stream is packetized */
166 int packet_stream_index; /* current stream for output in state machine */
168 /* RTSP state specific */
169 uint8_t *pb_buffer; /* XXX: use that in all the code */
171 int seq; /* RTSP sequence number */
173 /* RTP state specific */
174 enum RTSPLowerTransport rtp_protocol;
175 char session_id[32]; /* session id */
176 AVFormatContext *rtp_ctx[MAX_STREAMS];
178 /* RTP/UDP specific */
179 URLContext *rtp_handles[MAX_STREAMS];
181 /* RTP/TCP specific */
182 struct HTTPContext *rtsp_c;
183 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
186 /* each generated stream is described here */
190 STREAM_TYPE_REDIRECT,
193 enum IPAddressAction {
198 typedef struct IPAddressACL {
199 struct IPAddressACL *next;
200 enum IPAddressAction action;
201 /* These are in host order */
202 struct in_addr first;
206 /* description of each stream of the ffserver.conf file */
207 typedef struct FFStream {
208 enum StreamType stream_type;
209 char filename[1024]; /* stream filename */
210 struct FFStream *feed; /* feed we are using (can be null if
212 AVDictionary *in_opts; /* input parameters */
213 AVInputFormat *ifmt; /* if non NULL, force input format */
216 char dynamic_acl[1024];
218 int prebuffer; /* Number of millseconds early to start */
219 int64_t max_time; /* Number of milliseconds to run */
221 AVStream *streams[MAX_STREAMS];
222 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
223 char feed_filename[1024]; /* file name of the feed storage, or
224 input file name for a stream */
229 pid_t pid; /* Of ffmpeg process */
230 time_t pid_start; /* Of ffmpeg process */
232 struct FFStream *next;
233 unsigned bandwidth; /* bandwidth, in kbits/s */
236 /* multicast specific */
238 struct in_addr multicast_ip;
239 int multicast_port; /* first port used for multicast */
241 int loop; /* if true, send the stream in loops (only meaningful if file) */
244 int feed_opened; /* true if someone is writing to the feed */
245 int is_feed; /* true if it is a feed */
246 int readonly; /* True if writing is prohibited to the file */
247 int truncate; /* True if feeder connection truncate the feed file */
249 int64_t bytes_served;
250 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
251 int64_t feed_write_index; /* current write position in feed (it wraps around) */
252 int64_t feed_size; /* current size of feed */
253 struct FFStream *next_feed;
256 typedef struct FeedData {
257 long long data_count;
258 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
261 static struct sockaddr_in my_http_addr;
262 static struct sockaddr_in my_rtsp_addr;
264 static char logfilename[1024];
265 static HTTPContext *first_http_ctx;
266 static FFStream *first_feed; /* contains only feeds */
267 static FFStream *first_stream; /* contains all streams, including feeds */
269 static void new_connection(int server_fd, int is_rtsp);
270 static void close_connection(HTTPContext *c);
273 static int handle_connection(HTTPContext *c);
274 static int http_parse_request(HTTPContext *c);
275 static int http_send_data(HTTPContext *c);
276 static void compute_status(HTTPContext *c);
277 static int open_input_stream(HTTPContext *c, const char *info);
278 static int http_start_receive_data(HTTPContext *c);
279 static int http_receive_data(HTTPContext *c);
282 static int rtsp_parse_request(HTTPContext *c);
283 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
284 static void rtsp_cmd_options(HTTPContext *c, const char *url);
285 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
286 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
287 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
288 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
291 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
292 struct in_addr my_ip);
295 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
296 FFStream *stream, const char *session_id,
297 enum RTSPLowerTransport rtp_protocol);
298 static int rtp_new_av_stream(HTTPContext *c,
299 int stream_index, struct sockaddr_in *dest_addr,
300 HTTPContext *rtsp_c);
302 static const char *my_program_name;
303 static const char *my_program_dir;
305 static const char *config_filename = "/etc/ffserver.conf";
307 static int ffserver_debug;
308 static int ffserver_daemon;
309 static int no_launch;
310 static int need_to_start_children;
312 /* maximum number of simultaneous HTTP connections */
313 static unsigned int nb_max_http_connections = 2000;
314 static unsigned int nb_max_connections = 5;
315 static unsigned int nb_connections;
317 static uint64_t max_bandwidth = 1000;
318 static uint64_t current_bandwidth;
320 static int64_t cur_time; // Making this global saves on passing it around everywhere
322 static AVLFG random_state;
324 static FILE *logfile = NULL;
326 /* FIXME: make ffserver work with IPv6 */
327 void av_noreturn exit_program(int ret)
332 /* resolve host with also IP address parsing */
333 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
336 if (!ff_inet_aton(hostname, sin_addr)) {
338 struct addrinfo *ai, *cur;
339 struct addrinfo hints;
340 memset(&hints, 0, sizeof(hints));
341 hints.ai_family = AF_INET;
342 if (getaddrinfo(hostname, NULL, &hints, &ai))
344 /* getaddrinfo returns a linked list of addrinfo structs.
345 * Even if we set ai_family = AF_INET above, make sure
346 * that the returned one actually is of the correct type. */
347 for (cur = ai; cur; cur = cur->ai_next) {
348 if (cur->ai_family == AF_INET) {
349 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
358 hp = gethostbyname(hostname);
361 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
367 static char *ctime1(char *buf2)
375 p = buf2 + strlen(p) - 1;
381 static void http_vlog(const char *fmt, va_list vargs)
383 static int print_prefix = 1;
388 fprintf(logfile, "%s ", buf);
390 print_prefix = strstr(fmt, "\n") != NULL;
391 vfprintf(logfile, fmt, vargs);
397 __attribute__ ((format (printf, 1, 2)))
399 static void http_log(const char *fmt, ...)
402 va_start(vargs, fmt);
403 http_vlog(fmt, vargs);
407 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
409 static int print_prefix = 1;
410 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
411 if (level > av_log_get_level())
413 if (print_prefix && avc)
414 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
415 print_prefix = strstr(fmt, "\n") != NULL;
416 http_vlog(fmt, vargs);
419 static void log_connection(HTTPContext *c)
424 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
425 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
426 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
429 static void update_datarate(DataRateData *drd, int64_t count)
431 if (!drd->time1 && !drd->count1) {
432 drd->time1 = drd->time2 = cur_time;
433 drd->count1 = drd->count2 = count;
434 } else if (cur_time - drd->time2 > 5000) {
435 drd->time1 = drd->time2;
436 drd->count1 = drd->count2;
437 drd->time2 = cur_time;
442 /* In bytes per second */
443 static int compute_datarate(DataRateData *drd, int64_t count)
445 if (cur_time == drd->time1)
448 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
452 static void start_children(FFStream *feed)
457 for (; feed; feed = feed->next) {
458 if (feed->child_argv && !feed->pid) {
459 feed->pid_start = time(0);
464 http_log("Unable to create children\n");
473 av_strlcpy(pathname, my_program_name, sizeof(pathname));
475 slash = strrchr(pathname, '/');
480 strcpy(slash, "ffmpeg");
482 http_log("Launch command line: ");
483 http_log("%s ", pathname);
484 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
485 http_log("%s ", feed->child_argv[i]);
488 for (i = 3; i < 256; i++)
491 if (!ffserver_debug) {
492 i = open("/dev/null", O_RDWR);
501 /* This is needed to make relative pathnames work */
502 if (chdir(my_program_dir) < 0) {
503 http_log("chdir failed\n");
507 signal(SIGPIPE, SIG_DFL);
509 execvp(pathname, feed->child_argv);
517 /* open a listening socket */
518 static int socket_open_listen(struct sockaddr_in *my_addr)
522 server_fd = socket(AF_INET,SOCK_STREAM,0);
529 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
531 my_addr->sin_family = AF_INET;
532 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
534 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
536 closesocket(server_fd);
540 if (listen (server_fd, 5) < 0) {
542 closesocket(server_fd);
545 ff_socket_nonblock(server_fd, 1);
550 /* start all multicast streams */
551 static void start_multicast(void)
556 struct sockaddr_in dest_addr;
557 int default_port, stream_index;
560 for(stream = first_stream; stream != NULL; stream = stream->next) {
561 if (stream->is_multicast) {
562 /* open the RTP connection */
563 snprintf(session_id, sizeof(session_id), "%08x%08x",
564 av_lfg_get(&random_state), av_lfg_get(&random_state));
566 /* choose a port if none given */
567 if (stream->multicast_port == 0) {
568 stream->multicast_port = default_port;
572 dest_addr.sin_family = AF_INET;
573 dest_addr.sin_addr = stream->multicast_ip;
574 dest_addr.sin_port = htons(stream->multicast_port);
576 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
577 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
581 if (open_input_stream(rtp_c, "") < 0) {
582 http_log("Could not open input stream for stream '%s'\n",
587 /* open each RTP stream */
588 for(stream_index = 0; stream_index < stream->nb_streams;
590 dest_addr.sin_port = htons(stream->multicast_port +
592 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
593 http_log("Could not open output stream '%s/streamid=%d'\n",
594 stream->filename, stream_index);
599 /* change state to send data */
600 rtp_c->state = HTTPSTATE_SEND_DATA;
605 /* main loop of the http server */
606 static int http_server(void)
608 int server_fd = 0, rtsp_server_fd = 0;
609 int ret, delay, delay1;
610 struct pollfd *poll_table, *poll_entry;
611 HTTPContext *c, *c_next;
613 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
614 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
618 if (my_http_addr.sin_port) {
619 server_fd = socket_open_listen(&my_http_addr);
624 if (my_rtsp_addr.sin_port) {
625 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
626 if (rtsp_server_fd < 0)
630 if (!rtsp_server_fd && !server_fd) {
631 http_log("HTTP and RTSP disabled.\n");
635 http_log("FFserver started.\n");
637 start_children(first_feed);
642 poll_entry = poll_table;
644 poll_entry->fd = server_fd;
645 poll_entry->events = POLLIN;
648 if (rtsp_server_fd) {
649 poll_entry->fd = rtsp_server_fd;
650 poll_entry->events = POLLIN;
654 /* wait for events on each HTTP handle */
661 case HTTPSTATE_SEND_HEADER:
662 case RTSPSTATE_SEND_REPLY:
663 case RTSPSTATE_SEND_PACKET:
664 c->poll_entry = poll_entry;
666 poll_entry->events = POLLOUT;
669 case HTTPSTATE_SEND_DATA_HEADER:
670 case HTTPSTATE_SEND_DATA:
671 case HTTPSTATE_SEND_DATA_TRAILER:
672 if (!c->is_packetized) {
673 /* for TCP, we output as much as we can (may need to put a limit) */
674 c->poll_entry = poll_entry;
676 poll_entry->events = POLLOUT;
679 /* when ffserver is doing the timing, we work by
680 looking at which packet need to be sent every
682 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
687 case HTTPSTATE_WAIT_REQUEST:
688 case HTTPSTATE_RECEIVE_DATA:
689 case HTTPSTATE_WAIT_FEED:
690 case RTSPSTATE_WAIT_REQUEST:
691 /* need to catch errors */
692 c->poll_entry = poll_entry;
694 poll_entry->events = POLLIN;/* Maybe this will work */
698 c->poll_entry = NULL;
704 /* wait for an event on one connection. We poll at least every
705 second to handle timeouts */
707 ret = poll(poll_table, poll_entry - poll_table, delay);
708 if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
709 ff_neterrno() != AVERROR(EINTR))
713 cur_time = av_gettime() / 1000;
715 if (need_to_start_children) {
716 need_to_start_children = 0;
717 start_children(first_feed);
720 /* now handle the events */
721 for(c = first_http_ctx; c != NULL; c = c_next) {
723 if (handle_connection(c) < 0) {
724 /* close and free the connection */
730 poll_entry = poll_table;
732 /* new HTTP connection request ? */
733 if (poll_entry->revents & POLLIN)
734 new_connection(server_fd, 0);
737 if (rtsp_server_fd) {
738 /* new RTSP connection request ? */
739 if (poll_entry->revents & POLLIN)
740 new_connection(rtsp_server_fd, 1);
745 /* start waiting for a new HTTP/RTSP request */
746 static void start_wait_request(HTTPContext *c, int is_rtsp)
748 c->buffer_ptr = c->buffer;
749 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
752 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
753 c->state = RTSPSTATE_WAIT_REQUEST;
755 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
756 c->state = HTTPSTATE_WAIT_REQUEST;
760 static void http_send_too_busy_reply(int fd)
763 int len = snprintf(buffer, sizeof(buffer),
764 "HTTP/1.0 503 Server too busy\r\n"
765 "Content-type: text/html\r\n"
767 "<html><head><title>Too busy</title></head><body>\r\n"
768 "<p>The server is too busy to serve your request at this time.</p>\r\n"
769 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
770 "</body></html>\r\n",
771 nb_connections, nb_max_connections);
772 send(fd, buffer, len, 0);
776 static void new_connection(int server_fd, int is_rtsp)
778 struct sockaddr_in from_addr;
780 HTTPContext *c = NULL;
782 len = sizeof(from_addr);
783 fd = accept(server_fd, (struct sockaddr *)&from_addr,
786 http_log("error during accept %s\n", strerror(errno));
789 ff_socket_nonblock(fd, 1);
791 if (nb_connections >= nb_max_connections) {
792 http_send_too_busy_reply(fd);
796 /* add a new connection */
797 c = av_mallocz(sizeof(HTTPContext));
802 c->poll_entry = NULL;
803 c->from_addr = from_addr;
804 c->buffer_size = IOBUFFER_INIT_SIZE;
805 c->buffer = av_malloc(c->buffer_size);
809 c->next = first_http_ctx;
813 start_wait_request(c, is_rtsp);
825 static void close_connection(HTTPContext *c)
827 HTTPContext **cp, *c1;
829 AVFormatContext *ctx;
833 /* remove connection from list */
834 cp = &first_http_ctx;
835 while ((*cp) != NULL) {
843 /* remove references, if any (XXX: do it faster) */
844 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
849 /* remove connection associated resources */
853 /* close each frame parser */
854 for(i=0;i<c->fmt_in->nb_streams;i++) {
855 st = c->fmt_in->streams[i];
856 if (st->codec->codec)
857 avcodec_close(st->codec);
859 avformat_close_input(&c->fmt_in);
862 /* free RTP output streams if any */
865 nb_streams = c->stream->nb_streams;
867 for(i=0;i<nb_streams;i++) {
870 av_write_trailer(ctx);
871 av_dict_free(&ctx->metadata);
872 av_free(ctx->streams[0]);
875 h = c->rtp_handles[i];
882 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
885 if (avio_open_dyn_buf(&ctx->pb) >= 0) {
886 av_write_trailer(ctx);
887 av_freep(&c->pb_buffer);
888 avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
893 for(i=0; i<ctx->nb_streams; i++)
894 av_free(ctx->streams[i]);
896 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
897 current_bandwidth -= c->stream->bandwidth;
899 /* signal that there is no feed if we are the feeder socket */
900 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
901 c->stream->feed_opened = 0;
905 av_freep(&c->pb_buffer);
906 av_freep(&c->packet_buffer);
912 static int handle_connection(HTTPContext *c)
917 case HTTPSTATE_WAIT_REQUEST:
918 case RTSPSTATE_WAIT_REQUEST:
920 if ((c->timeout - cur_time) < 0)
922 if (c->poll_entry->revents & (POLLERR | POLLHUP))
925 /* no need to read if no events */
926 if (!(c->poll_entry->revents & POLLIN))
930 len = recv(c->fd, c->buffer_ptr, 1, 0);
932 if (ff_neterrno() != AVERROR(EAGAIN) &&
933 ff_neterrno() != AVERROR(EINTR))
935 } else if (len == 0) {
938 /* search for end of request. */
940 c->buffer_ptr += len;
942 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
943 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
944 /* request found : parse it and reply */
945 if (c->state == HTTPSTATE_WAIT_REQUEST) {
946 ret = http_parse_request(c);
948 ret = rtsp_parse_request(c);
952 } else if (ptr >= c->buffer_end) {
953 /* request too long: cannot do anything */
955 } else goto read_loop;
959 case HTTPSTATE_SEND_HEADER:
960 if (c->poll_entry->revents & (POLLERR | POLLHUP))
963 /* no need to write if no events */
964 if (!(c->poll_entry->revents & POLLOUT))
966 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
968 if (ff_neterrno() != AVERROR(EAGAIN) &&
969 ff_neterrno() != AVERROR(EINTR)) {
970 /* error : close connection */
971 av_freep(&c->pb_buffer);
975 c->buffer_ptr += len;
977 c->stream->bytes_served += len;
978 c->data_count += len;
979 if (c->buffer_ptr >= c->buffer_end) {
980 av_freep(&c->pb_buffer);
984 /* all the buffer was sent : synchronize to the incoming stream */
985 c->state = HTTPSTATE_SEND_DATA_HEADER;
986 c->buffer_ptr = c->buffer_end = c->buffer;
991 case HTTPSTATE_SEND_DATA:
992 case HTTPSTATE_SEND_DATA_HEADER:
993 case HTTPSTATE_SEND_DATA_TRAILER:
994 /* for packetized output, we consider we can always write (the
995 input streams sets the speed). It may be better to verify
996 that we do not rely too much on the kernel queues */
997 if (!c->is_packetized) {
998 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1001 /* no need to read if no events */
1002 if (!(c->poll_entry->revents & POLLOUT))
1005 if (http_send_data(c) < 0)
1007 /* close connection if trailer sent */
1008 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
1011 case HTTPSTATE_RECEIVE_DATA:
1012 /* no need to read if no events */
1013 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1015 if (!(c->poll_entry->revents & POLLIN))
1017 if (http_receive_data(c) < 0)
1020 case HTTPSTATE_WAIT_FEED:
1021 /* no need to read if no events */
1022 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1025 /* nothing to do, we'll be waken up by incoming feed packets */
1028 case RTSPSTATE_SEND_REPLY:
1029 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1030 av_freep(&c->pb_buffer);
1033 /* no need to write if no events */
1034 if (!(c->poll_entry->revents & POLLOUT))
1036 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1038 if (ff_neterrno() != AVERROR(EAGAIN) &&
1039 ff_neterrno() != AVERROR(EINTR)) {
1040 /* error : close connection */
1041 av_freep(&c->pb_buffer);
1045 c->buffer_ptr += len;
1046 c->data_count += len;
1047 if (c->buffer_ptr >= c->buffer_end) {
1048 /* all the buffer was sent : wait for a new request */
1049 av_freep(&c->pb_buffer);
1050 start_wait_request(c, 1);
1054 case RTSPSTATE_SEND_PACKET:
1055 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1056 av_freep(&c->packet_buffer);
1059 /* no need to write if no events */
1060 if (!(c->poll_entry->revents & POLLOUT))
1062 len = send(c->fd, c->packet_buffer_ptr,
1063 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1065 if (ff_neterrno() != AVERROR(EAGAIN) &&
1066 ff_neterrno() != AVERROR(EINTR)) {
1067 /* error : close connection */
1068 av_freep(&c->packet_buffer);
1072 c->packet_buffer_ptr += len;
1073 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1074 /* all the buffer was sent : wait for a new request */
1075 av_freep(&c->packet_buffer);
1076 c->state = RTSPSTATE_WAIT_REQUEST;
1080 case HTTPSTATE_READY:
1089 static int extract_rates(char *rates, int ratelen, const char *request)
1093 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1094 if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1095 const char *q = p + 7;
1097 while (*q && *q != '\n' && isspace(*q))
1100 if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1106 memset(rates, 0xff, ratelen);
1109 while (*q && *q != '\n' && *q != ':')
1112 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1116 if (stream_no < ratelen && stream_no >= 0)
1117 rates[stream_no] = rate_no;
1119 while (*q && *q != '\n' && !isspace(*q))
1126 p = strchr(p, '\n');
1136 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1139 int best_bitrate = 100000000;
1142 for (i = 0; i < feed->nb_streams; i++) {
1143 AVCodecContext *feed_codec = feed->streams[i]->codec;
1145 if (feed_codec->codec_id != codec->codec_id ||
1146 feed_codec->sample_rate != codec->sample_rate ||
1147 feed_codec->width != codec->width ||
1148 feed_codec->height != codec->height)
1151 /* Potential stream */
1153 /* We want the fastest stream less than bit_rate, or the slowest
1154 * faster than bit_rate
1157 if (feed_codec->bit_rate <= bit_rate) {
1158 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1159 best_bitrate = feed_codec->bit_rate;
1163 if (feed_codec->bit_rate < best_bitrate) {
1164 best_bitrate = feed_codec->bit_rate;
1173 static int modify_current_stream(HTTPContext *c, char *rates)
1176 FFStream *req = c->stream;
1177 int action_required = 0;
1179 /* Not much we can do for a feed */
1183 for (i = 0; i < req->nb_streams; i++) {
1184 AVCodecContext *codec = req->streams[i]->codec;
1188 c->switch_feed_streams[i] = req->feed_streams[i];
1191 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1194 /* Wants off or slow */
1195 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1197 /* This doesn't work well when it turns off the only stream! */
1198 c->switch_feed_streams[i] = -2;
1199 c->feed_streams[i] = -2;
1204 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1205 action_required = 1;
1208 return action_required;
1211 /* XXX: factorize in utils.c ? */
1212 /* XXX: take care with different space meaning */
1213 static void skip_spaces(const char **pp)
1217 while (*p == ' ' || *p == '\t')
1222 static void get_word(char *buf, int buf_size, const char **pp)
1230 while (!isspace(*p) && *p != '\0') {
1231 if ((q - buf) < buf_size - 1)
1240 static void get_arg(char *buf, int buf_size, const char **pp)
1247 while (isspace(*p)) p++;
1250 if (*p == '\"' || *p == '\'')
1262 if ((q - buf) < buf_size - 1)
1267 if (quote && *p == quote)
1272 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1273 const char *p, const char *filename, int line_num)
1279 get_arg(arg, sizeof(arg), &p);
1280 if (av_strcasecmp(arg, "allow") == 0)
1281 acl.action = IP_ALLOW;
1282 else if (av_strcasecmp(arg, "deny") == 0)
1283 acl.action = IP_DENY;
1285 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1286 filename, line_num, arg);
1290 get_arg(arg, sizeof(arg), &p);
1292 if (resolve_host(&acl.first, arg) != 0) {
1293 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1294 filename, line_num, arg);
1297 acl.last = acl.first;
1299 get_arg(arg, sizeof(arg), &p);
1302 if (resolve_host(&acl.last, arg) != 0) {
1303 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1304 filename, line_num, arg);
1310 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1311 IPAddressACL **naclp = 0;
1317 naclp = &stream->acl;
1323 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1324 filename, line_num);
1330 naclp = &(*naclp)->next;
1338 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1343 IPAddressACL *acl = NULL;
1347 f = fopen(stream->dynamic_acl, "r");
1349 perror(stream->dynamic_acl);
1353 acl = av_mallocz(sizeof(IPAddressACL));
1357 if (fgets(line, sizeof(line), f) == NULL)
1363 if (*p == '\0' || *p == '#')
1365 get_arg(cmd, sizeof(cmd), &p);
1367 if (!av_strcasecmp(cmd, "ACL"))
1368 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1375 static void free_acl_list(IPAddressACL *in_acl)
1377 IPAddressACL *pacl,*pacl2;
1387 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1389 enum IPAddressAction last_action = IP_DENY;
1391 struct in_addr *src = &c->from_addr.sin_addr;
1392 unsigned long src_addr = src->s_addr;
1394 for (acl = in_acl; acl; acl = acl->next) {
1395 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1396 return (acl->action == IP_ALLOW) ? 1 : 0;
1397 last_action = acl->action;
1400 /* Nothing matched, so return not the last action */
1401 return (last_action == IP_DENY) ? 1 : 0;
1404 static int validate_acl(FFStream *stream, HTTPContext *c)
1410 /* if stream->acl is null validate_acl_list will return 1 */
1411 ret = validate_acl_list(stream->acl, c);
1413 if (stream->dynamic_acl[0]) {
1414 acl = parse_dynamic_acl(stream, c);
1416 ret = validate_acl_list(acl, c);
1424 /* compute the real filename of a file by matching it without its
1425 extensions to all the stream filenames */
1426 static void compute_real_filename(char *filename, int max_size)
1433 /* compute filename by matching without the file extensions */
1434 av_strlcpy(file1, filename, sizeof(file1));
1435 p = strrchr(file1, '.');
1438 for(stream = first_stream; stream != NULL; stream = stream->next) {
1439 av_strlcpy(file2, stream->filename, sizeof(file2));
1440 p = strrchr(file2, '.');
1443 if (!strcmp(file1, file2)) {
1444 av_strlcpy(filename, stream->filename, max_size);
1459 /* parse http request and prepare header */
1460 static int http_parse_request(HTTPContext *c)
1463 enum RedirType redir_type;
1465 char info[1024], filename[1024];
1469 const char *mime_type;
1473 char *useragent = 0;
1476 get_word(cmd, sizeof(cmd), (const char **)&p);
1477 av_strlcpy(c->method, cmd, sizeof(c->method));
1479 if (!strcmp(cmd, "GET"))
1481 else if (!strcmp(cmd, "POST"))
1486 get_word(url, sizeof(url), (const char **)&p);
1487 av_strlcpy(c->url, url, sizeof(c->url));
1489 get_word(protocol, sizeof(protocol), (const char **)&p);
1490 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1493 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1496 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1498 /* find the filename and the optional info string in the request */
1499 p = strchr(url, '?');
1501 av_strlcpy(info, p, sizeof(info));
1506 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1508 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1509 if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1511 if (*useragent && *useragent != '\n' && isspace(*useragent))
1515 p = strchr(p, '\n');
1522 redir_type = REDIR_NONE;
1523 if (av_match_ext(filename, "asx")) {
1524 redir_type = REDIR_ASX;
1525 filename[strlen(filename)-1] = 'f';
1526 } else if (av_match_ext(filename, "asf") &&
1527 (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1528 /* if this isn't WMP or lookalike, return the redirector file */
1529 redir_type = REDIR_ASF;
1530 } else if (av_match_ext(filename, "rpm,ram")) {
1531 redir_type = REDIR_RAM;
1532 strcpy(filename + strlen(filename)-2, "m");
1533 } else if (av_match_ext(filename, "rtsp")) {
1534 redir_type = REDIR_RTSP;
1535 compute_real_filename(filename, sizeof(filename) - 1);
1536 } else if (av_match_ext(filename, "sdp")) {
1537 redir_type = REDIR_SDP;
1538 compute_real_filename(filename, sizeof(filename) - 1);
1541 // "redirect" / request to index.html
1542 if (!strlen(filename))
1543 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1545 stream = first_stream;
1546 while (stream != NULL) {
1547 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1549 stream = stream->next;
1551 if (stream == NULL) {
1552 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1553 http_log("File '%s' not found\n", url);
1558 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1559 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1561 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1562 c->http_error = 301;
1564 q += snprintf(q, c->buffer_size,
1565 "HTTP/1.0 301 Moved\r\n"
1567 "Content-type: text/html\r\n"
1569 "<html><head><title>Moved</title></head><body>\r\n"
1570 "You should be <a href=\"%s\">redirected</a>.\r\n"
1571 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1572 /* prepare output buffer */
1573 c->buffer_ptr = c->buffer;
1575 c->state = HTTPSTATE_SEND_HEADER;
1579 /* If this is WMP, get the rate information */
1580 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1581 if (modify_current_stream(c, ratebuf)) {
1582 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1583 if (c->switch_feed_streams[i] >= 0)
1584 c->switch_feed_streams[i] = -1;
1589 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1590 current_bandwidth += stream->bandwidth;
1592 /* If already streaming this feed, do not let start another feeder. */
1593 if (stream->feed_opened) {
1594 snprintf(msg, sizeof(msg), "This feed is already being received.");
1595 http_log("Feed '%s' already being received\n", stream->feed_filename);
1599 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1600 c->http_error = 503;
1602 q += snprintf(q, c->buffer_size,
1603 "HTTP/1.0 503 Server too busy\r\n"
1604 "Content-type: text/html\r\n"
1606 "<html><head><title>Too busy</title></head><body>\r\n"
1607 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1608 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1609 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1610 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1611 /* prepare output buffer */
1612 c->buffer_ptr = c->buffer;
1614 c->state = HTTPSTATE_SEND_HEADER;
1618 if (redir_type != REDIR_NONE) {
1621 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1622 if (av_strncasecmp(p, "Host:", 5) == 0) {
1626 p = strchr(p, '\n');
1637 while (isspace(*hostinfo))
1640 eoh = strchr(hostinfo, '\n');
1642 if (eoh[-1] == '\r')
1645 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1646 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1647 hostbuf[eoh - hostinfo] = 0;
1649 c->http_error = 200;
1651 switch(redir_type) {
1653 q += snprintf(q, c->buffer_size,
1654 "HTTP/1.0 200 ASX Follows\r\n"
1655 "Content-type: video/x-ms-asf\r\n"
1657 "<ASX Version=\"3\">\r\n"
1658 //"<!-- Autogenerated by ffserver -->\r\n"
1659 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1660 "</ASX>\r\n", hostbuf, filename, info);
1663 q += snprintf(q, c->buffer_size,
1664 "HTTP/1.0 200 RAM Follows\r\n"
1665 "Content-type: audio/x-pn-realaudio\r\n"
1667 "# Autogenerated by ffserver\r\n"
1668 "http://%s/%s%s\r\n", hostbuf, filename, info);
1671 q += snprintf(q, c->buffer_size,
1672 "HTTP/1.0 200 ASF Redirect follows\r\n"
1673 "Content-type: video/x-ms-asf\r\n"
1676 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1680 char hostname[256], *p;
1681 /* extract only hostname */
1682 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1683 p = strrchr(hostname, ':');
1686 q += snprintf(q, c->buffer_size,
1687 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1688 /* XXX: incorrect mime type ? */
1689 "Content-type: application/x-rtsp\r\n"
1691 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1697 int sdp_data_size, len;
1698 struct sockaddr_in my_addr;
1700 q += snprintf(q, c->buffer_size,
1701 "HTTP/1.0 200 OK\r\n"
1702 "Content-type: application/sdp\r\n"
1705 len = sizeof(my_addr);
1706 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1708 /* XXX: should use a dynamic buffer */
1709 sdp_data_size = prepare_sdp_description(stream,
1712 if (sdp_data_size > 0) {
1713 memcpy(q, sdp_data, sdp_data_size);
1725 /* prepare output buffer */
1726 c->buffer_ptr = c->buffer;
1728 c->state = HTTPSTATE_SEND_HEADER;
1734 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1738 stream->conns_served++;
1740 /* XXX: add there authenticate and IP match */
1743 /* if post, it means a feed is being sent */
1744 if (!stream->is_feed) {
1745 /* However it might be a status report from WMP! Let us log the
1746 * data as it might come in handy one day. */
1750 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1751 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1755 if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1756 client_id = strtol(p + 18, 0, 10);
1757 p = strchr(p, '\n');
1765 char *eol = strchr(logline, '\n');
1770 if (eol[-1] == '\r')
1772 http_log("%.*s\n", (int) (eol - logline), logline);
1773 c->suppress_log = 1;
1778 http_log("\nGot request:\n%s\n", c->buffer);
1781 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1784 /* Now we have to find the client_id */
1785 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1786 if (wmpc->wmp_client_id == client_id)
1790 if (wmpc && modify_current_stream(wmpc, ratebuf))
1791 wmpc->switch_pending = 1;
1794 snprintf(msg, sizeof(msg), "POST command not handled");
1798 if (http_start_receive_data(c) < 0) {
1799 snprintf(msg, sizeof(msg), "could not open feed");
1803 c->state = HTTPSTATE_RECEIVE_DATA;
1808 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1809 http_log("\nGot request:\n%s\n", c->buffer);
1812 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1815 /* open input stream */
1816 if (open_input_stream(c, info) < 0) {
1817 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1821 /* prepare http header */
1823 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1824 mime_type = c->stream->fmt->mime_type;
1826 mime_type = "application/x-octet-stream";
1827 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1829 /* for asf, we need extra headers */
1830 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1831 /* Need to allocate a client id */
1833 c->wmp_client_id = av_lfg_get(&random_state);
1835 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);
1837 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1838 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1840 /* prepare output buffer */
1842 c->buffer_ptr = c->buffer;
1844 c->state = HTTPSTATE_SEND_HEADER;
1847 c->http_error = 404;
1849 q += snprintf(q, c->buffer_size,
1850 "HTTP/1.0 404 Not Found\r\n"
1851 "Content-type: text/html\r\n"
1854 "<head><title>404 Not Found</title></head>\n"
1857 /* prepare output buffer */
1858 c->buffer_ptr = c->buffer;
1860 c->state = HTTPSTATE_SEND_HEADER;
1864 c->http_error = 200; /* horrible : we use this value to avoid
1865 going to the send data state */
1866 c->state = HTTPSTATE_SEND_HEADER;
1870 static void fmt_bytecount(AVIOContext *pb, int64_t count)
1872 static const char *suffix = " kMGTP";
1875 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1877 avio_printf(pb, "%"PRId64"%c", count, *s);
1880 static void compute_status(HTTPContext *c)
1889 if (avio_open_dyn_buf(&pb) < 0) {
1890 /* XXX: return an error ? */
1891 c->buffer_ptr = c->buffer;
1892 c->buffer_end = c->buffer;
1896 avio_printf(pb, "HTTP/1.0 200 OK\r\n");
1897 avio_printf(pb, "Content-type: %s\r\n", "text/html");
1898 avio_printf(pb, "Pragma: no-cache\r\n");
1899 avio_printf(pb, "\r\n");
1901 avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1902 if (c->stream->feed_filename[0])
1903 avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1904 avio_printf(pb, "</head>\n<body>");
1905 avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
1907 avio_printf(pb, "<h2>Available Streams</h2>\n");
1908 avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
1909 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");
1910 stream = first_stream;
1911 while (stream != NULL) {
1912 char sfilename[1024];
1915 if (stream->feed != stream) {
1916 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1917 eosf = sfilename + strlen(sfilename);
1918 if (eosf - sfilename >= 4) {
1919 if (strcmp(eosf - 4, ".asf") == 0)
1920 strcpy(eosf - 4, ".asx");
1921 else if (strcmp(eosf - 3, ".rm") == 0)
1922 strcpy(eosf - 3, ".ram");
1923 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1924 /* generate a sample RTSP director if
1925 unicast. Generate an SDP redirector if
1927 eosf = strrchr(sfilename, '.');
1929 eosf = sfilename + strlen(sfilename);
1930 if (stream->is_multicast)
1931 strcpy(eosf, ".sdp");
1933 strcpy(eosf, ".rtsp");
1937 avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1938 sfilename, stream->filename);
1939 avio_printf(pb, "<td align=right> %d <td align=right> ",
1940 stream->conns_served);
1941 fmt_bytecount(pb, stream->bytes_served);
1942 switch(stream->stream_type) {
1943 case STREAM_TYPE_LIVE: {
1944 int audio_bit_rate = 0;
1945 int video_bit_rate = 0;
1946 const char *audio_codec_name = "";
1947 const char *video_codec_name = "";
1948 const char *audio_codec_name_extra = "";
1949 const char *video_codec_name_extra = "";
1951 for(i=0;i<stream->nb_streams;i++) {
1952 AVStream *st = stream->streams[i];
1953 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1954 switch(st->codec->codec_type) {
1955 case AVMEDIA_TYPE_AUDIO:
1956 audio_bit_rate += st->codec->bit_rate;
1958 if (*audio_codec_name)
1959 audio_codec_name_extra = "...";
1960 audio_codec_name = codec->name;
1963 case AVMEDIA_TYPE_VIDEO:
1964 video_bit_rate += st->codec->bit_rate;
1966 if (*video_codec_name)
1967 video_codec_name_extra = "...";
1968 video_codec_name = codec->name;
1971 case AVMEDIA_TYPE_DATA:
1972 video_bit_rate += st->codec->bit_rate;
1978 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",
1981 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1982 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1984 avio_printf(pb, "<td>%s", stream->feed->filename);
1986 avio_printf(pb, "<td>%s", stream->feed_filename);
1987 avio_printf(pb, "\n");
1991 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1995 stream = stream->next;
1997 avio_printf(pb, "</table>\n");
1999 stream = first_stream;
2000 while (stream != NULL) {
2001 if (stream->feed == stream) {
2002 avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
2004 avio_printf(pb, "Running as pid %d.\n", stream->pid);
2006 #if defined(linux) && !defined(CONFIG_NOCUTILS)
2011 /* This is somewhat linux specific I guess */
2012 snprintf(ps_cmd, sizeof(ps_cmd),
2013 "ps -o \"%%cpu,cputime\" --no-headers %d",
2016 pid_stat = popen(ps_cmd, "r");
2021 if (fscanf(pid_stat, "%10s %64s", cpuperc,
2023 avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2031 avio_printf(pb, "<p>");
2033 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");
2035 for (i = 0; i < stream->nb_streams; i++) {
2036 AVStream *st = stream->streams[i];
2037 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2038 const char *type = "unknown";
2039 char parameters[64];
2043 switch(st->codec->codec_type) {
2044 case AVMEDIA_TYPE_AUDIO:
2046 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2048 case AVMEDIA_TYPE_VIDEO:
2050 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2051 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2056 avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2057 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2059 avio_printf(pb, "</table>\n");
2062 stream = stream->next;
2065 /* connection status */
2066 avio_printf(pb, "<h2>Connection Status</h2>\n");
2068 avio_printf(pb, "Number of connections: %d / %d<br>\n",
2069 nb_connections, nb_max_connections);
2071 avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2072 current_bandwidth, max_bandwidth);
2074 avio_printf(pb, "<table>\n");
2075 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");
2076 c1 = first_http_ctx;
2078 while (c1 != NULL) {
2084 for (j = 0; j < c1->stream->nb_streams; j++) {
2085 if (!c1->stream->feed)
2086 bitrate += c1->stream->streams[j]->codec->bit_rate;
2087 else if (c1->feed_streams[j] >= 0)
2088 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2093 p = inet_ntoa(c1->from_addr.sin_addr);
2094 avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2096 c1->stream ? c1->stream->filename : "",
2097 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2100 http_state[c1->state]);
2101 fmt_bytecount(pb, bitrate);
2102 avio_printf(pb, "<td align=right>");
2103 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2104 avio_printf(pb, "<td align=right>");
2105 fmt_bytecount(pb, c1->data_count);
2106 avio_printf(pb, "\n");
2109 avio_printf(pb, "</table>\n");
2114 avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
2115 avio_printf(pb, "</body>\n</html>\n");
2117 len = avio_close_dyn_buf(pb, &c->pb_buffer);
2118 c->buffer_ptr = c->pb_buffer;
2119 c->buffer_end = c->pb_buffer + len;
2122 static int open_input_stream(HTTPContext *c, const char *info)
2125 char input_filename[1024];
2126 AVFormatContext *s = NULL;
2130 /* find file name */
2131 if (c->stream->feed) {
2132 strcpy(input_filename, c->stream->feed->feed_filename);
2133 /* compute position (absolute time) */
2134 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2135 if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
2137 } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2138 int prebuffer = strtol(buf, 0, 10);
2139 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2141 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2143 strcpy(input_filename, c->stream->feed_filename);
2144 /* compute position (relative time) */
2145 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2146 if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
2151 if (input_filename[0] == '\0')
2155 if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2156 http_log("could not open %s: %d\n", input_filename, ret);
2159 s->flags |= AVFMT_FLAG_GENPTS;
2161 if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2162 http_log("Could not find stream info '%s'\n", input_filename);
2163 avformat_close_input(&s);
2167 /* choose stream as clock source (we favorize video stream if
2168 present) for packet sending */
2169 c->pts_stream_index = 0;
2170 for(i=0;i<c->stream->nb_streams;i++) {
2171 if (c->pts_stream_index == 0 &&
2172 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2173 c->pts_stream_index = i;
2177 if (c->fmt_in->iformat->read_seek)
2178 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2179 /* set the start time (needed for maxtime and RTP packet timing) */
2180 c->start_time = cur_time;
2181 c->first_pts = AV_NOPTS_VALUE;
2185 /* return the server clock (in us) */
2186 static int64_t get_server_clock(HTTPContext *c)
2188 /* compute current pts value from system time */
2189 return (cur_time - c->start_time) * 1000;
2192 /* return the estimated time at which the current packet must be sent
2194 static int64_t get_packet_send_clock(HTTPContext *c)
2196 int bytes_left, bytes_sent, frame_bytes;
2198 frame_bytes = c->cur_frame_bytes;
2199 if (frame_bytes <= 0)
2202 bytes_left = c->buffer_end - c->buffer_ptr;
2203 bytes_sent = frame_bytes - bytes_left;
2204 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2209 static int http_prepare_data(HTTPContext *c)
2212 AVFormatContext *ctx;
2214 av_freep(&c->pb_buffer);
2216 case HTTPSTATE_SEND_DATA_HEADER:
2217 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2218 av_dict_set(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2219 av_dict_set(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2220 av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2221 av_dict_set(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2223 c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2225 for(i=0;i<c->stream->nb_streams;i++) {
2227 c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2228 /* if file or feed, then just take streams from FFStream struct */
2229 if (!c->stream->feed ||
2230 c->stream->feed == c->stream)
2231 src = c->stream->streams[i];
2233 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2235 *(c->fmt_ctx.streams[i]) = *src;
2236 c->fmt_ctx.streams[i]->priv_data = 0;
2237 c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2238 AVStream, not in codec */
2240 /* set output format parameters */
2241 c->fmt_ctx.oformat = c->stream->fmt;
2242 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2244 c->got_key_frame = 0;
2246 /* prepare header and save header data in a stream */
2247 if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2248 /* XXX: potential leak */
2251 c->fmt_ctx.pb->seekable = 0;
2254 * HACK to avoid mpeg ps muxer to spit many underflow errors
2255 * Default value from FFmpeg
2256 * Try to set it use configuration option
2258 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2259 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2261 if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
2262 http_log("Error writing output header\n");
2265 av_dict_free(&c->fmt_ctx.metadata);
2267 len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2268 c->buffer_ptr = c->pb_buffer;
2269 c->buffer_end = c->pb_buffer + len;
2271 c->state = HTTPSTATE_SEND_DATA;
2272 c->last_packet_sent = 0;
2274 case HTTPSTATE_SEND_DATA:
2275 /* find a new packet */
2276 /* read a packet from the input stream */
2277 if (c->stream->feed)
2278 ffm_set_write_index(c->fmt_in,
2279 c->stream->feed->feed_write_index,
2280 c->stream->feed->feed_size);
2282 if (c->stream->max_time &&
2283 c->stream->max_time + c->start_time - cur_time < 0)
2284 /* We have timed out */
2285 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2289 ret = av_read_frame(c->fmt_in, &pkt);
2291 if (c->stream->feed) {
2292 /* if coming from feed, it means we reached the end of the
2293 ffm file, so must wait for more data */
2294 c->state = HTTPSTATE_WAIT_FEED;
2295 return 1; /* state changed */
2296 } else if (ret == AVERROR(EAGAIN)) {
2297 /* input not ready, come back later */
2300 if (c->stream->loop) {
2301 avformat_close_input(&c->fmt_in);
2302 if (open_input_stream(c, "") < 0)
2307 /* must send trailer now because eof or error */
2308 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2312 int source_index = pkt.stream_index;
2313 /* update first pts if needed */
2314 if (c->first_pts == AV_NOPTS_VALUE) {
2315 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2316 c->start_time = cur_time;
2318 /* send it to the appropriate stream */
2319 if (c->stream->feed) {
2320 /* if coming from a feed, select the right stream */
2321 if (c->switch_pending) {
2322 c->switch_pending = 0;
2323 for(i=0;i<c->stream->nb_streams;i++) {
2324 if (c->switch_feed_streams[i] == pkt.stream_index)
2325 if (pkt.flags & AV_PKT_FLAG_KEY)
2326 c->switch_feed_streams[i] = -1;
2327 if (c->switch_feed_streams[i] >= 0)
2328 c->switch_pending = 1;
2331 for(i=0;i<c->stream->nb_streams;i++) {
2332 if (c->stream->feed_streams[i] == pkt.stream_index) {
2333 AVStream *st = c->fmt_in->streams[source_index];
2334 pkt.stream_index = i;
2335 if (pkt.flags & AV_PKT_FLAG_KEY &&
2336 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2337 c->stream->nb_streams == 1))
2338 c->got_key_frame = 1;
2339 if (!c->stream->send_on_key || c->got_key_frame)
2344 AVCodecContext *codec;
2345 AVStream *ist, *ost;
2347 ist = c->fmt_in->streams[source_index];
2348 /* specific handling for RTP: we use several
2349 output stream (one for each RTP
2350 connection). XXX: need more abstract handling */
2351 if (c->is_packetized) {
2352 /* compute send time and duration */
2353 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2354 c->cur_pts -= c->first_pts;
2355 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2356 /* find RTP context */
2357 c->packet_stream_index = pkt.stream_index;
2358 ctx = c->rtp_ctx[c->packet_stream_index];
2360 av_free_packet(&pkt);
2363 codec = ctx->streams[0]->codec;
2364 /* only one stream per RTP connection */
2365 pkt.stream_index = 0;
2369 codec = ctx->streams[pkt.stream_index]->codec;
2372 if (c->is_packetized) {
2373 int max_packet_size;
2374 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2375 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2377 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2378 ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2380 ret = avio_open_dyn_buf(&ctx->pb);
2383 /* XXX: potential leak */
2386 ost = ctx->streams[pkt.stream_index];
2388 ctx->pb->seekable = 0;
2389 if (pkt.dts != AV_NOPTS_VALUE)
2390 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2391 if (pkt.pts != AV_NOPTS_VALUE)
2392 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2393 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2394 if (av_write_frame(ctx, &pkt) < 0) {
2395 http_log("Error writing frame to output\n");
2396 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2399 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2400 c->cur_frame_bytes = len;
2401 c->buffer_ptr = c->pb_buffer;
2402 c->buffer_end = c->pb_buffer + len;
2404 codec->frame_number++;
2406 av_free_packet(&pkt);
2410 av_free_packet(&pkt);
2415 case HTTPSTATE_SEND_DATA_TRAILER:
2416 /* last packet test ? */
2417 if (c->last_packet_sent || c->is_packetized)
2420 /* prepare header */
2421 if (avio_open_dyn_buf(&ctx->pb) < 0) {
2422 /* XXX: potential leak */
2425 c->fmt_ctx.pb->seekable = 0;
2426 av_write_trailer(ctx);
2427 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2428 c->buffer_ptr = c->pb_buffer;
2429 c->buffer_end = c->pb_buffer + len;
2431 c->last_packet_sent = 1;
2437 /* should convert the format at the same time */
2438 /* send data starting at c->buffer_ptr to the output connection
2439 (either UDP or TCP connection) */
2440 static int http_send_data(HTTPContext *c)
2445 if (c->buffer_ptr >= c->buffer_end) {
2446 ret = http_prepare_data(c);
2450 /* state change requested */
2453 if (c->is_packetized) {
2454 /* RTP data output */
2455 len = c->buffer_end - c->buffer_ptr;
2457 /* fail safe - should never happen */
2459 c->buffer_ptr = c->buffer_end;
2462 len = (c->buffer_ptr[0] << 24) |
2463 (c->buffer_ptr[1] << 16) |
2464 (c->buffer_ptr[2] << 8) |
2466 if (len > (c->buffer_end - c->buffer_ptr))
2468 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2469 /* nothing to send yet: we can wait */
2473 c->data_count += len;
2474 update_datarate(&c->datarate, c->data_count);
2476 c->stream->bytes_served += len;
2478 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2479 /* RTP packets are sent inside the RTSP TCP connection */
2481 int interleaved_index, size;
2483 HTTPContext *rtsp_c;
2486 /* if no RTSP connection left, error */
2489 /* if already sending something, then wait. */
2490 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2492 if (avio_open_dyn_buf(&pb) < 0)
2494 interleaved_index = c->packet_stream_index * 2;
2495 /* RTCP packets are sent at odd indexes */
2496 if (c->buffer_ptr[1] == 200)
2497 interleaved_index++;
2498 /* write RTSP TCP header */
2500 header[1] = interleaved_index;
2501 header[2] = len >> 8;
2503 avio_write(pb, header, 4);
2504 /* write RTP packet data */
2506 avio_write(pb, c->buffer_ptr, len);
2507 size = avio_close_dyn_buf(pb, &c->packet_buffer);
2508 /* prepare asynchronous TCP sending */
2509 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2510 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2511 c->buffer_ptr += len;
2513 /* send everything we can NOW */
2514 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2515 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2517 rtsp_c->packet_buffer_ptr += len;
2518 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2519 /* if we could not send all the data, we will
2520 send it later, so a new state is needed to
2521 "lock" the RTSP TCP connection */
2522 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2525 /* all data has been sent */
2526 av_freep(&c->packet_buffer);
2528 /* send RTP packet directly in UDP */
2530 url_write(c->rtp_handles[c->packet_stream_index],
2531 c->buffer_ptr, len);
2532 c->buffer_ptr += len;
2533 /* here we continue as we can send several packets per 10 ms slot */
2536 /* TCP data output */
2537 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2539 if (ff_neterrno() != AVERROR(EAGAIN) &&
2540 ff_neterrno() != AVERROR(EINTR))
2541 /* error : close connection */
2546 c->buffer_ptr += len;
2548 c->data_count += len;
2549 update_datarate(&c->datarate, c->data_count);
2551 c->stream->bytes_served += len;
2559 static int http_start_receive_data(HTTPContext *c)
2563 if (c->stream->feed_opened)
2566 /* Don't permit writing to this one */
2567 if (c->stream->readonly)
2571 fd = open(c->stream->feed_filename, O_RDWR);
2573 http_log("Error opening feeder file: %s\n", strerror(errno));
2578 if (c->stream->truncate) {
2579 /* truncate feed file */
2580 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2581 ftruncate(c->feed_fd, FFM_PACKET_SIZE);
2582 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2584 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2585 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2590 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2591 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2592 lseek(fd, 0, SEEK_SET);
2594 /* init buffer input */
2595 c->buffer_ptr = c->buffer;
2596 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2597 c->stream->feed_opened = 1;
2598 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2602 static int http_receive_data(HTTPContext *c)
2605 int len, loop_run = 0;
2607 while (c->chunked_encoding && !c->chunk_size &&
2608 c->buffer_end > c->buffer_ptr) {
2609 /* read chunk header, if present */
2610 len = recv(c->fd, c->buffer_ptr, 1, 0);
2613 if (ff_neterrno() != AVERROR(EAGAIN) &&
2614 ff_neterrno() != AVERROR(EINTR))
2615 /* error : close connection */
2618 } else if (len == 0) {
2619 /* end of connection : close it */
2621 } else if (c->buffer_ptr - c->buffer >= 2 &&
2622 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2623 c->chunk_size = strtol(c->buffer, 0, 16);
2624 if (c->chunk_size == 0) // end of stream
2626 c->buffer_ptr = c->buffer;
2628 } else if (++loop_run > 10) {
2629 /* no chunk header, abort */
2636 if (c->buffer_end > c->buffer_ptr) {
2637 len = recv(c->fd, c->buffer_ptr,
2638 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2640 if (ff_neterrno() != AVERROR(EAGAIN) &&
2641 ff_neterrno() != AVERROR(EINTR))
2642 /* error : close connection */
2644 } else if (len == 0)
2645 /* end of connection : close it */
2648 c->chunk_size -= len;
2649 c->buffer_ptr += len;
2650 c->data_count += len;
2651 update_datarate(&c->datarate, c->data_count);
2655 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2656 if (c->buffer[0] != 'f' ||
2657 c->buffer[1] != 'm') {
2658 http_log("Feed stream has become desynchronized -- disconnecting\n");
2663 if (c->buffer_ptr >= c->buffer_end) {
2664 FFStream *feed = c->stream;
2665 /* a packet has been received : write it in the store, except
2667 if (c->data_count > FFM_PACKET_SIZE) {
2669 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2670 /* XXX: use llseek or url_seek */
2671 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2672 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2673 http_log("Error writing to feed file: %s\n", strerror(errno));
2677 feed->feed_write_index += FFM_PACKET_SIZE;
2678 /* update file size */
2679 if (feed->feed_write_index > c->stream->feed_size)
2680 feed->feed_size = feed->feed_write_index;
2682 /* handle wrap around if max file size reached */
2683 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2684 feed->feed_write_index = FFM_PACKET_SIZE;
2687 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2688 http_log("Error writing index to feed file: %s\n", strerror(errno));
2692 /* wake up any waiting connections */
2693 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2694 if (c1->state == HTTPSTATE_WAIT_FEED &&
2695 c1->stream->feed == c->stream->feed)
2696 c1->state = HTTPSTATE_SEND_DATA;
2699 /* We have a header in our hands that contains useful data */
2700 AVFormatContext *s = avformat_alloc_context();
2702 AVInputFormat *fmt_in;
2708 /* use feed output format name to find corresponding input format */
2709 fmt_in = av_find_input_format(feed->fmt->name);
2713 pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2714 0, NULL, NULL, NULL, NULL);
2718 if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2723 /* Now we have the actual streams */
2724 if (s->nb_streams != feed->nb_streams) {
2725 avformat_close_input(&s);
2727 http_log("Feed '%s' stream number does not match registered feed\n",
2728 c->stream->feed_filename);
2732 for (i = 0; i < s->nb_streams; i++) {
2733 AVStream *fst = feed->streams[i];
2734 AVStream *st = s->streams[i];
2735 avcodec_copy_context(fst->codec, st->codec);
2738 avformat_close_input(&s);
2741 c->buffer_ptr = c->buffer;
2746 c->stream->feed_opened = 0;
2748 /* wake up any waiting connections to stop waiting for feed */
2749 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2750 if (c1->state == HTTPSTATE_WAIT_FEED &&
2751 c1->stream->feed == c->stream->feed)
2752 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2757 /********************************************************************/
2760 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2767 switch(error_number) {
2768 case RTSP_STATUS_OK:
2771 case RTSP_STATUS_METHOD:
2772 str = "Method Not Allowed";
2774 case RTSP_STATUS_BANDWIDTH:
2775 str = "Not Enough Bandwidth";
2777 case RTSP_STATUS_SESSION:
2778 str = "Session Not Found";
2780 case RTSP_STATUS_STATE:
2781 str = "Method Not Valid in This State";
2783 case RTSP_STATUS_AGGREGATE:
2784 str = "Aggregate operation not allowed";
2786 case RTSP_STATUS_ONLY_AGGREGATE:
2787 str = "Only aggregate operation allowed";
2789 case RTSP_STATUS_TRANSPORT:
2790 str = "Unsupported transport";
2792 case RTSP_STATUS_INTERNAL:
2793 str = "Internal Server Error";
2795 case RTSP_STATUS_SERVICE:
2796 str = "Service Unavailable";
2798 case RTSP_STATUS_VERSION:
2799 str = "RTSP Version not supported";
2802 str = "Unknown Error";
2806 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2807 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2809 /* output GMT time */
2812 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2813 avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2816 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2818 rtsp_reply_header(c, error_number);
2819 avio_printf(c->pb, "\r\n");
2822 static int rtsp_parse_request(HTTPContext *c)
2824 const char *p, *p1, *p2;
2830 RTSPMessageHeader header1, *header = &header1;
2832 c->buffer_ptr[0] = '\0';
2835 get_word(cmd, sizeof(cmd), &p);
2836 get_word(url, sizeof(url), &p);
2837 get_word(protocol, sizeof(protocol), &p);
2839 av_strlcpy(c->method, cmd, sizeof(c->method));
2840 av_strlcpy(c->url, url, sizeof(c->url));
2841 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2843 if (avio_open_dyn_buf(&c->pb) < 0) {
2844 /* XXX: cannot do more */
2845 c->pb = NULL; /* safety */
2849 /* check version name */
2850 if (strcmp(protocol, "RTSP/1.0") != 0) {
2851 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2855 /* parse each header line */
2856 memset(header, 0, sizeof(*header));
2857 /* skip to next line */
2858 while (*p != '\n' && *p != '\0')
2862 while (*p != '\0') {
2863 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2867 if (p2 > p && p2[-1] == '\r')
2869 /* skip empty line */
2873 if (len > sizeof(line) - 1)
2874 len = sizeof(line) - 1;
2875 memcpy(line, p, len);
2877 ff_rtsp_parse_line(header, line, NULL, NULL);
2881 /* handle sequence number */
2882 c->seq = header->seq;
2884 if (!strcmp(cmd, "DESCRIBE"))
2885 rtsp_cmd_describe(c, url);
2886 else if (!strcmp(cmd, "OPTIONS"))
2887 rtsp_cmd_options(c, url);
2888 else if (!strcmp(cmd, "SETUP"))
2889 rtsp_cmd_setup(c, url, header);
2890 else if (!strcmp(cmd, "PLAY"))
2891 rtsp_cmd_play(c, url, header);
2892 else if (!strcmp(cmd, "PAUSE"))
2893 rtsp_cmd_pause(c, url, header);
2894 else if (!strcmp(cmd, "TEARDOWN"))
2895 rtsp_cmd_teardown(c, url, header);
2897 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2900 len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2901 c->pb = NULL; /* safety */
2903 /* XXX: cannot do more */
2906 c->buffer_ptr = c->pb_buffer;
2907 c->buffer_end = c->pb_buffer + len;
2908 c->state = RTSPSTATE_SEND_REPLY;
2912 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2913 struct in_addr my_ip)
2915 AVFormatContext *avc;
2916 AVStream *avs = NULL;
2919 avc = avformat_alloc_context();
2923 av_dict_set(&avc->metadata, "title",
2924 stream->title[0] ? stream->title : "No Title", 0);
2925 avc->nb_streams = stream->nb_streams;
2926 if (stream->is_multicast) {
2927 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2928 inet_ntoa(stream->multicast_ip),
2929 stream->multicast_port, stream->multicast_ttl);
2931 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2934 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2935 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2937 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2938 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2941 for(i = 0; i < stream->nb_streams; i++) {
2942 avc->streams[i] = &avs[i];
2943 avc->streams[i]->codec = stream->streams[i]->codec;
2945 *pbuffer = av_mallocz(2048);
2946 av_sdp_create(&avc, 1, *pbuffer, 2048);
2949 av_free(avc->streams);
2950 av_dict_free(&avc->metadata);
2954 return strlen(*pbuffer);
2957 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2959 // rtsp_reply_header(c, RTSP_STATUS_OK);
2960 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2961 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2962 avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2963 avio_printf(c->pb, "\r\n");
2966 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2972 int content_length, len;
2973 struct sockaddr_in my_addr;
2975 /* find which url is asked */
2976 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2981 for(stream = first_stream; stream != NULL; stream = stream->next) {
2982 if (!stream->is_feed &&
2983 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2984 !strcmp(path, stream->filename)) {
2988 /* no stream found */
2989 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2993 /* prepare the media description in sdp format */
2995 /* get the host IP */
2996 len = sizeof(my_addr);
2997 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2998 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2999 if (content_length < 0) {
3000 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3003 rtsp_reply_header(c, RTSP_STATUS_OK);
3004 avio_printf(c->pb, "Content-Base: %s/\r\n", url);
3005 avio_printf(c->pb, "Content-Type: application/sdp\r\n");
3006 avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
3007 avio_printf(c->pb, "\r\n");
3008 avio_write(c->pb, content, content_length);
3012 static HTTPContext *find_rtp_session(const char *session_id)
3016 if (session_id[0] == '\0')
3019 for(c = first_http_ctx; c != NULL; c = c->next) {
3020 if (!strcmp(c->session_id, session_id))
3026 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3028 RTSPTransportField *th;
3031 for(i=0;i<h->nb_transports;i++) {
3032 th = &h->transports[i];
3033 if (th->lower_transport == lower_transport)
3039 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3040 RTSPMessageHeader *h)
3043 int stream_index, rtp_port, rtcp_port;
3048 RTSPTransportField *th;
3049 struct sockaddr_in dest_addr;
3050 RTSPActionServerSetup setup;
3052 /* find which url is asked */
3053 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3058 /* now check each stream */
3059 for(stream = first_stream; stream != NULL; stream = stream->next) {
3060 if (!stream->is_feed &&
3061 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3062 /* accept aggregate filenames only if single stream */
3063 if (!strcmp(path, stream->filename)) {
3064 if (stream->nb_streams != 1) {
3065 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3072 for(stream_index = 0; stream_index < stream->nb_streams;
3074 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3075 stream->filename, stream_index);
3076 if (!strcmp(path, buf))
3081 /* no stream found */
3082 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3086 /* generate session id if needed */
3087 if (h->session_id[0] == '\0')
3088 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3089 av_lfg_get(&random_state), av_lfg_get(&random_state));
3091 /* find rtp session, and create it if none found */
3092 rtp_c = find_rtp_session(h->session_id);
3094 /* always prefer UDP */
3095 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3097 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3099 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3104 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3105 th->lower_transport);
3107 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3111 /* open input stream */
3112 if (open_input_stream(rtp_c, "") < 0) {
3113 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3118 /* test if stream is OK (test needed because several SETUP needs
3119 to be done for a given file) */
3120 if (rtp_c->stream != stream) {
3121 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3125 /* test if stream is already set up */
3126 if (rtp_c->rtp_ctx[stream_index]) {
3127 rtsp_reply_error(c, RTSP_STATUS_STATE);
3131 /* check transport */
3132 th = find_transport(h, rtp_c->rtp_protocol);
3133 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3134 th->client_port_min <= 0)) {
3135 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3139 /* setup default options */
3140 setup.transport_option[0] = '\0';
3141 dest_addr = rtp_c->from_addr;
3142 dest_addr.sin_port = htons(th->client_port_min);
3145 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3146 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3150 /* now everything is OK, so we can send the connection parameters */
3151 rtsp_reply_header(c, RTSP_STATUS_OK);
3153 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3155 switch(rtp_c->rtp_protocol) {
3156 case RTSP_LOWER_TRANSPORT_UDP:
3157 rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3158 rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3159 avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3160 "client_port=%d-%d;server_port=%d-%d",
3161 th->client_port_min, th->client_port_max,
3162 rtp_port, rtcp_port);
3164 case RTSP_LOWER_TRANSPORT_TCP:
3165 avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3166 stream_index * 2, stream_index * 2 + 1);
3171 if (setup.transport_option[0] != '\0')
3172 avio_printf(c->pb, ";%s", setup.transport_option);
3173 avio_printf(c->pb, "\r\n");
3176 avio_printf(c->pb, "\r\n");
3180 /* find an rtp connection by using the session ID. Check consistency
3182 static HTTPContext *find_rtp_session_with_url(const char *url,
3183 const char *session_id)
3191 rtp_c = find_rtp_session(session_id);
3195 /* find which url is asked */
3196 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3200 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3201 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3202 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3203 rtp_c->stream->filename, s);
3204 if(!strncmp(path, buf, sizeof(buf))) {
3205 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3210 if (len > 0 && path[len - 1] == '/' &&
3211 !strncmp(path, rtp_c->stream->filename, len - 1))
3216 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3220 rtp_c = find_rtp_session_with_url(url, h->session_id);
3222 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3226 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3227 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3228 rtp_c->state != HTTPSTATE_READY) {
3229 rtsp_reply_error(c, RTSP_STATUS_STATE);
3233 rtp_c->state = HTTPSTATE_SEND_DATA;
3235 /* now everything is OK, so we can send the connection parameters */
3236 rtsp_reply_header(c, RTSP_STATUS_OK);
3238 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3239 avio_printf(c->pb, "\r\n");
3242 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3246 rtp_c = find_rtp_session_with_url(url, h->session_id);
3248 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3252 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3253 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3254 rtsp_reply_error(c, RTSP_STATUS_STATE);
3258 rtp_c->state = HTTPSTATE_READY;
3259 rtp_c->first_pts = AV_NOPTS_VALUE;
3260 /* now everything is OK, so we can send the connection parameters */
3261 rtsp_reply_header(c, RTSP_STATUS_OK);
3263 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3264 avio_printf(c->pb, "\r\n");
3267 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3271 rtp_c = find_rtp_session_with_url(url, h->session_id);
3273 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3277 /* now everything is OK, so we can send the connection parameters */
3278 rtsp_reply_header(c, RTSP_STATUS_OK);
3280 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3281 avio_printf(c->pb, "\r\n");
3283 /* abort the session */
3284 close_connection(rtp_c);
3288 /********************************************************************/
3291 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3292 FFStream *stream, const char *session_id,
3293 enum RTSPLowerTransport rtp_protocol)
3295 HTTPContext *c = NULL;
3296 const char *proto_str;
3298 /* XXX: should output a warning page when coming
3299 close to the connection limit */
3300 if (nb_connections >= nb_max_connections)
3303 /* add a new connection */
3304 c = av_mallocz(sizeof(HTTPContext));
3309 c->poll_entry = NULL;
3310 c->from_addr = *from_addr;
3311 c->buffer_size = IOBUFFER_INIT_SIZE;
3312 c->buffer = av_malloc(c->buffer_size);
3317 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3318 c->state = HTTPSTATE_READY;
3319 c->is_packetized = 1;
3320 c->rtp_protocol = rtp_protocol;
3322 /* protocol is shown in statistics */
3323 switch(c->rtp_protocol) {
3324 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3325 proto_str = "MCAST";
3327 case RTSP_LOWER_TRANSPORT_UDP:
3330 case RTSP_LOWER_TRANSPORT_TCP:
3337 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3338 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3340 current_bandwidth += stream->bandwidth;
3342 c->next = first_http_ctx;
3354 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3355 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3357 static int rtp_new_av_stream(HTTPContext *c,
3358 int stream_index, struct sockaddr_in *dest_addr,
3359 HTTPContext *rtsp_c)
3361 AVFormatContext *ctx;
3364 URLContext *h = NULL;
3366 int max_packet_size;
3368 /* now we can open the relevant output stream */
3369 ctx = avformat_alloc_context();
3372 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3374 st = av_mallocz(sizeof(AVStream));
3377 ctx->nb_streams = 1;
3378 ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3381 ctx->streams[0] = st;
3383 if (!c->stream->feed ||
3384 c->stream->feed == c->stream)
3385 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3388 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3390 st->priv_data = NULL;
3392 /* build destination RTP address */
3393 ipaddr = inet_ntoa(dest_addr->sin_addr);
3395 switch(c->rtp_protocol) {
3396 case RTSP_LOWER_TRANSPORT_UDP:
3397 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3400 /* XXX: also pass as parameter to function ? */
3401 if (c->stream->is_multicast) {
3403 ttl = c->stream->multicast_ttl;
3406 snprintf(ctx->filename, sizeof(ctx->filename),
3407 "rtp://%s:%d?multicast=1&ttl=%d",
3408 ipaddr, ntohs(dest_addr->sin_port), ttl);
3410 snprintf(ctx->filename, sizeof(ctx->filename),
3411 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3414 if (url_open(&h, ctx->filename, AVIO_FLAG_WRITE) < 0)
3416 c->rtp_handles[stream_index] = h;
3417 max_packet_size = url_get_max_packet_size(h);
3419 case RTSP_LOWER_TRANSPORT_TCP:
3422 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3428 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3429 ipaddr, ntohs(dest_addr->sin_port),
3430 c->stream->filename, stream_index, c->protocol);
3432 /* normally, no packets should be output here, but the packet size may be checked */
3433 if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3434 /* XXX: close stream */
3437 if (avformat_write_header(ctx, NULL) < 0) {
3444 avio_close_dyn_buf(ctx->pb, &dummy_buf);
3447 c->rtp_ctx[stream_index] = ctx;
3451 /********************************************************************/
3452 /* ffserver initialization */
3454 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3458 fst = av_mallocz(sizeof(AVStream));
3462 fst->codec = avcodec_alloc_context3(NULL);
3463 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3464 if (codec->extradata_size) {
3465 fst->codec->extradata = av_malloc(codec->extradata_size);
3466 memcpy(fst->codec->extradata, codec->extradata,
3467 codec->extradata_size);
3470 /* live streams must use the actual feed's codec since it may be
3471 * updated later to carry extradata needed by the streams.
3475 fst->priv_data = av_mallocz(sizeof(FeedData));
3476 fst->index = stream->nb_streams;
3477 av_set_pts_info(fst, 33, 1, 90000);
3478 fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3479 stream->streams[stream->nb_streams++] = fst;
3483 /* return the stream number in the feed */
3484 static int add_av_stream(FFStream *feed, AVStream *st)
3487 AVCodecContext *av, *av1;
3491 for(i=0;i<feed->nb_streams;i++) {
3492 st = feed->streams[i];
3494 if (av1->codec_id == av->codec_id &&
3495 av1->codec_type == av->codec_type &&
3496 av1->bit_rate == av->bit_rate) {
3498 switch(av->codec_type) {
3499 case AVMEDIA_TYPE_AUDIO:
3500 if (av1->channels == av->channels &&
3501 av1->sample_rate == av->sample_rate)
3504 case AVMEDIA_TYPE_VIDEO:
3505 if (av1->width == av->width &&
3506 av1->height == av->height &&
3507 av1->time_base.den == av->time_base.den &&
3508 av1->time_base.num == av->time_base.num &&
3509 av1->gop_size == av->gop_size)
3518 fst = add_av_stream1(feed, av, 0);
3521 return feed->nb_streams - 1;
3524 static void remove_stream(FFStream *stream)
3528 while (*ps != NULL) {
3536 /* specific mpeg4 handling : we extract the raw parameters */
3537 static void extract_mpeg4_header(AVFormatContext *infile)
3539 int mpeg4_count, i, size;
3545 for(i=0;i<infile->nb_streams;i++) {
3546 st = infile->streams[i];
3547 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3548 st->codec->extradata_size == 0) {
3555 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3556 while (mpeg4_count > 0) {
3557 if (av_read_packet(infile, &pkt) < 0)
3559 st = infile->streams[pkt.stream_index];
3560 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3561 st->codec->extradata_size == 0) {
3562 av_freep(&st->codec->extradata);
3563 /* fill extradata with the header */
3564 /* XXX: we make hard suppositions here ! */
3566 while (p < pkt.data + pkt.size - 4) {
3567 /* stop when vop header is found */
3568 if (p[0] == 0x00 && p[1] == 0x00 &&
3569 p[2] == 0x01 && p[3] == 0xb6) {
3570 size = p - pkt.data;
3571 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3572 st->codec->extradata = av_malloc(size);
3573 st->codec->extradata_size = size;
3574 memcpy(st->codec->extradata, pkt.data, size);
3581 av_free_packet(&pkt);
3585 /* compute the needed AVStream for each file */
3586 static void build_file_streams(void)
3588 FFStream *stream, *stream_next;
3591 /* gather all streams */
3592 for(stream = first_stream; stream != NULL; stream = stream_next) {
3593 AVFormatContext *infile = NULL;
3594 stream_next = stream->next;
3595 if (stream->stream_type == STREAM_TYPE_LIVE &&
3597 /* the stream comes from a file */
3598 /* try to open the file */
3600 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3601 /* specific case : if transport stream output to RTP,
3602 we use a raw transport stream reader */
3603 av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3606 http_log("Opening file '%s'\n", stream->feed_filename);
3607 if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3608 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3609 /* remove stream (no need to spend more time on it) */
3611 remove_stream(stream);
3613 /* find all the AVStreams inside and reference them in
3615 if (avformat_find_stream_info(infile, NULL) < 0) {
3616 http_log("Could not find codec parameters from '%s'\n",
3617 stream->feed_filename);
3618 avformat_close_input(&infile);
3621 extract_mpeg4_header(infile);
3623 for(i=0;i<infile->nb_streams;i++)
3624 add_av_stream1(stream, infile->streams[i]->codec, 1);
3626 avformat_close_input(&infile);
3632 /* compute the needed AVStream for each feed */
3633 static void build_feed_streams(void)
3635 FFStream *stream, *feed;
3638 /* gather all streams */
3639 for(stream = first_stream; stream != NULL; stream = stream->next) {
3640 feed = stream->feed;
3642 if (stream->is_feed) {
3643 for(i=0;i<stream->nb_streams;i++)
3644 stream->feed_streams[i] = i;
3646 /* we handle a stream coming from a feed */
3647 for(i=0;i<stream->nb_streams;i++)
3648 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3653 /* create feed files if needed */
3654 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3657 if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3658 /* See if it matches */
3659 AVFormatContext *s = NULL;
3662 if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3663 /* Now see if it matches */
3664 if (s->nb_streams == feed->nb_streams) {
3666 for(i=0;i<s->nb_streams;i++) {
3668 sf = feed->streams[i];
3671 if (sf->index != ss->index ||
3673 http_log("Index & Id do not match for stream %d (%s)\n",
3674 i, feed->feed_filename);
3677 AVCodecContext *ccf, *ccs;
3681 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3683 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3684 http_log("Codecs do not match for stream %d\n", i);
3686 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3687 http_log("Codec bitrates do not match for stream %d\n", i);
3689 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3690 if (CHECK_CODEC(time_base.den) ||
3691 CHECK_CODEC(time_base.num) ||
3692 CHECK_CODEC(width) ||
3693 CHECK_CODEC(height)) {
3694 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3697 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3698 if (CHECK_CODEC(sample_rate) ||
3699 CHECK_CODEC(channels) ||
3700 CHECK_CODEC(frame_size)) {
3701 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3705 http_log("Unknown codec type\n");
3713 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3714 feed->feed_filename, s->nb_streams, feed->nb_streams);
3716 avformat_close_input(&s);
3718 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3719 feed->feed_filename);
3722 if (feed->readonly) {
3723 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3724 feed->feed_filename);
3727 unlink(feed->feed_filename);
3730 if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3731 AVFormatContext s1 = {0}, *s = &s1;
3733 if (feed->readonly) {
3734 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3735 feed->feed_filename);
3739 /* only write the header of the ffm file */
3740 if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3741 http_log("Could not open output feed file '%s'\n",
3742 feed->feed_filename);
3745 s->oformat = feed->fmt;
3746 s->nb_streams = feed->nb_streams;
3747 s->streams = feed->streams;
3748 if (avformat_write_header(s, NULL) < 0) {
3749 http_log("Container doesn't supports the required parameters\n");
3752 /* XXX: need better api */
3753 av_freep(&s->priv_data);
3756 /* get feed size and write index */
3757 fd = open(feed->feed_filename, O_RDONLY);
3759 http_log("Could not open output feed file '%s'\n",
3760 feed->feed_filename);
3764 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3765 feed->feed_size = lseek(fd, 0, SEEK_END);
3766 /* ensure that we do not wrap before the end of file */
3767 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3768 feed->feed_max_size = feed->feed_size;
3774 /* compute the bandwidth used by each stream */
3775 static void compute_bandwidth(void)
3781 for(stream = first_stream; stream != NULL; stream = stream->next) {
3783 for(i=0;i<stream->nb_streams;i++) {
3784 AVStream *st = stream->streams[i];
3785 switch(st->codec->codec_type) {
3786 case AVMEDIA_TYPE_AUDIO:
3787 case AVMEDIA_TYPE_VIDEO:
3788 bandwidth += st->codec->bit_rate;
3794 stream->bandwidth = (bandwidth + 999) / 1000;
3798 /* add a codec and set the default parameters */
3799 static void add_codec(FFStream *stream, AVCodecContext *av)
3803 /* compute default parameters */
3804 switch(av->codec_type) {
3805 case AVMEDIA_TYPE_AUDIO:
3806 if (av->bit_rate == 0)
3807 av->bit_rate = 64000;
3808 if (av->sample_rate == 0)
3809 av->sample_rate = 22050;
3810 if (av->channels == 0)
3813 case AVMEDIA_TYPE_VIDEO:
3814 if (av->bit_rate == 0)
3815 av->bit_rate = 64000;
3816 if (av->time_base.num == 0){
3817 av->time_base.den = 5;
3818 av->time_base.num = 1;
3820 if (av->width == 0 || av->height == 0) {
3824 /* Bitrate tolerance is less for streaming */
3825 if (av->bit_rate_tolerance == 0)
3826 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3827 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3832 if (av->max_qdiff == 0)
3834 av->qcompress = 0.5;
3837 if (!av->nsse_weight)
3838 av->nsse_weight = 8;
3840 av->frame_skip_cmp = FF_CMP_DCTMAX;
3842 av->me_method = ME_EPZS;
3843 av->rc_buffer_aggressivity = 1.0;
3846 av->rc_eq = "tex^qComp";
3847 if (!av->i_quant_factor)
3848 av->i_quant_factor = -0.8;
3849 if (!av->b_quant_factor)
3850 av->b_quant_factor = 1.25;
3851 if (!av->b_quant_offset)
3852 av->b_quant_offset = 1.25;
3853 if (!av->rc_max_rate)
3854 av->rc_max_rate = av->bit_rate * 2;
3856 if (av->rc_max_rate && !av->rc_buffer_size) {
3857 av->rc_buffer_size = av->rc_max_rate;
3866 st = av_mallocz(sizeof(AVStream));
3869 st->codec = avcodec_alloc_context3(NULL);
3870 stream->streams[stream->nb_streams++] = st;
3871 memcpy(st->codec, av, sizeof(AVCodecContext));
3874 static enum CodecID opt_audio_codec(const char *arg)
3876 AVCodec *p= avcodec_find_encoder_by_name(arg);
3878 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3879 return CODEC_ID_NONE;
3884 static enum CodecID opt_video_codec(const char *arg)
3886 AVCodec *p= avcodec_find_encoder_by_name(arg);
3888 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3889 return CODEC_ID_NONE;
3894 /* simplistic plugin support */
3897 static void load_module(const char *filename)
3900 void (*init_func)(void);
3901 dll = dlopen(filename, RTLD_NOW);
3903 fprintf(stderr, "Could not load module '%s' - %s\n",
3904 filename, dlerror());
3908 init_func = dlsym(dll, "ffserver_module_init");
3911 "%s: init function 'ffserver_module_init()' not found\n",
3920 static int ffserver_opt_default(const char *opt, const char *arg,
3921 AVCodecContext *avctx, int type)
3924 const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3926 ret = av_opt_set(avctx, opt, arg, 0);
3930 static int ffserver_opt_preset(const char *arg,
3931 AVCodecContext *avctx, int type,
3932 enum CodecID *audio_id, enum CodecID *video_id)
3935 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3937 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3939 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3940 codec ? codec->name : NULL))) {
3941 fprintf(stderr, "File for preset '%s' not found\n", arg);
3946 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3947 if(line[0] == '#' && !e)
3949 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3951 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3955 if(!strcmp(tmp, "acodec")){
3956 *audio_id = opt_audio_codec(tmp2);
3957 }else if(!strcmp(tmp, "vcodec")){
3958 *video_id = opt_video_codec(tmp2);
3959 }else if(!strcmp(tmp, "scodec")){
3960 /* opt_subtitle_codec(tmp2); */
3961 }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
3962 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
3973 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
3974 const char *mime_type)
3976 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3979 AVOutputFormat *stream_fmt;
3980 char stream_format_name[64];
3982 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
3983 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
3992 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
3996 fprintf(stderr, "%s:%d: ", filename, line_num);
3997 vfprintf(stderr, fmt, vl);
4003 static int parse_ffconfig(const char *filename)
4010 int val, errors, line_num;
4011 FFStream **last_stream, *stream, *redirect;
4012 FFStream **last_feed, *feed, *s;
4013 AVCodecContext audio_enc, video_enc;
4014 enum CodecID audio_id, video_id;
4016 f = fopen(filename, "r");
4024 first_stream = NULL;
4025 last_stream = &first_stream;
4027 last_feed = &first_feed;
4031 audio_id = CODEC_ID_NONE;
4032 video_id = CODEC_ID_NONE;
4034 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4036 if (fgets(line, sizeof(line), f) == NULL)
4042 if (*p == '\0' || *p == '#')
4045 get_arg(cmd, sizeof(cmd), &p);
4047 if (!av_strcasecmp(cmd, "Port")) {
4048 get_arg(arg, sizeof(arg), &p);
4050 if (val < 1 || val > 65536) {
4051 ERROR("Invalid_port: %s\n", arg);
4053 my_http_addr.sin_port = htons(val);
4054 } else if (!av_strcasecmp(cmd, "BindAddress")) {
4055 get_arg(arg, sizeof(arg), &p);
4056 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4057 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4059 } else if (!av_strcasecmp(cmd, "NoDaemon")) {
4060 ffserver_daemon = 0;
4061 } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4062 get_arg(arg, sizeof(arg), &p);
4064 if (val < 1 || val > 65536) {
4065 ERROR("%s:%d: Invalid port: %s\n", arg);
4067 my_rtsp_addr.sin_port = htons(atoi(arg));
4068 } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4069 get_arg(arg, sizeof(arg), &p);
4070 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4071 ERROR("Invalid host/IP address: %s\n", arg);
4073 } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4074 get_arg(arg, sizeof(arg), &p);
4076 if (val < 1 || val > 65536) {
4077 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4079 nb_max_http_connections = val;
4080 } else if (!av_strcasecmp(cmd, "MaxClients")) {
4081 get_arg(arg, sizeof(arg), &p);
4083 if (val < 1 || val > nb_max_http_connections) {
4084 ERROR("Invalid MaxClients: %s\n", arg);
4086 nb_max_connections = val;
4088 } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4090 get_arg(arg, sizeof(arg), &p);
4092 if (llval < 10 || llval > 10000000) {
4093 ERROR("Invalid MaxBandwidth: %s\n", arg);
4095 max_bandwidth = llval;
4096 } else if (!av_strcasecmp(cmd, "CustomLog")) {
4097 if (!ffserver_debug)
4098 get_arg(logfilename, sizeof(logfilename), &p);
4099 } else if (!av_strcasecmp(cmd, "<Feed")) {
4100 /*********************************************/
4101 /* Feed related options */
4103 if (stream || feed) {
4104 ERROR("Already in a tag\n");
4106 feed = av_mallocz(sizeof(FFStream));
4107 get_arg(feed->filename, sizeof(feed->filename), &p);
4108 q = strrchr(feed->filename, '>');
4112 for (s = first_feed; s; s = s->next) {
4113 if (!strcmp(feed->filename, s->filename)) {
4114 ERROR("Feed '%s' already registered\n", s->filename);
4118 feed->fmt = av_guess_format("ffm", NULL, NULL);
4119 /* defaut feed file */
4120 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4121 "/tmp/%s.ffm", feed->filename);
4122 feed->feed_max_size = 5 * 1024 * 1024;
4124 feed->feed = feed; /* self feeding :-) */
4126 /* add in stream list */
4127 *last_stream = feed;
4128 last_stream = &feed->next;
4129 /* add in feed list */
4131 last_feed = &feed->next_feed;
4133 } else if (!av_strcasecmp(cmd, "Launch")) {
4137 feed->child_argv = av_mallocz(64 * sizeof(char *));
4139 for (i = 0; i < 62; i++) {
4140 get_arg(arg, sizeof(arg), &p);
4144 feed->child_argv[i] = av_strdup(arg);
4147 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4149 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4151 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4152 inet_ntoa(my_http_addr.sin_addr),
4153 ntohs(my_http_addr.sin_port), feed->filename);
4155 } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4157 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4159 } else if (stream) {
4160 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4162 } else if (!av_strcasecmp(cmd, "File")) {
4164 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4166 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4167 } else if (!av_strcasecmp(cmd, "Truncate")) {
4169 get_arg(arg, sizeof(arg), &p);
4170 feed->truncate = strtod(arg, NULL);
4172 } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4177 get_arg(arg, sizeof(arg), &p);
4179 fsize = strtod(p1, &p1);
4180 switch(toupper(*p1)) {
4185 fsize *= 1024 * 1024;
4188 fsize *= 1024 * 1024 * 1024;
4191 feed->feed_max_size = (int64_t)fsize;
4192 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4193 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4196 } else if (!av_strcasecmp(cmd, "</Feed>")) {
4198 ERROR("No corresponding <Feed> for </Feed>\n");
4201 } else if (!av_strcasecmp(cmd, "<Stream")) {
4202 /*********************************************/
4203 /* Stream related options */
4205 if (stream || feed) {
4206 ERROR("Already in a tag\n");
4209 stream = av_mallocz(sizeof(FFStream));
4210 get_arg(stream->filename, sizeof(stream->filename), &p);
4211 q = strrchr(stream->filename, '>');
4215 for (s = first_stream; s; s = s->next) {
4216 if (!strcmp(stream->filename, s->filename)) {
4217 ERROR("Stream '%s' already registered\n", s->filename);
4221 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4222 avcodec_get_context_defaults2(&video_enc, AVMEDIA_TYPE_VIDEO);
4223 avcodec_get_context_defaults2(&audio_enc, AVMEDIA_TYPE_AUDIO);
4225 audio_id = CODEC_ID_NONE;
4226 video_id = CODEC_ID_NONE;
4228 audio_id = stream->fmt->audio_codec;
4229 video_id = stream->fmt->video_codec;
4232 *last_stream = stream;
4233 last_stream = &stream->next;
4235 } else if (!av_strcasecmp(cmd, "Feed")) {
4236 get_arg(arg, sizeof(arg), &p);
4241 while (sfeed != NULL) {
4242 if (!strcmp(sfeed->filename, arg))
4244 sfeed = sfeed->next_feed;
4247 ERROR("feed '%s' not defined\n", arg);
4249 stream->feed = sfeed;
4251 } else if (!av_strcasecmp(cmd, "Format")) {
4252 get_arg(arg, sizeof(arg), &p);
4254 if (!strcmp(arg, "status")) {
4255 stream->stream_type = STREAM_TYPE_STATUS;
4258 stream->stream_type = STREAM_TYPE_LIVE;
4259 /* jpeg cannot be used here, so use single frame jpeg */
4260 if (!strcmp(arg, "jpeg"))
4261 strcpy(arg, "mjpeg");
4262 stream->fmt = ffserver_guess_format(arg, NULL, NULL);
4264 ERROR("Unknown Format: %s\n", arg);
4268 audio_id = stream->fmt->audio_codec;
4269 video_id = stream->fmt->video_codec;
4272 } else if (!av_strcasecmp(cmd, "InputFormat")) {
4273 get_arg(arg, sizeof(arg), &p);
4275 stream->ifmt = av_find_input_format(arg);
4276 if (!stream->ifmt) {
4277 ERROR("Unknown input format: %s\n", arg);
4280 } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4281 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4282 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4284 ERROR("FaviconURL only permitted for status streams\n");
4286 } else if (!av_strcasecmp(cmd, "Author")) {
4288 get_arg(stream->author, sizeof(stream->author), &p);
4289 } else if (!av_strcasecmp(cmd, "Comment")) {
4291 get_arg(stream->comment, sizeof(stream->comment), &p);
4292 } else if (!av_strcasecmp(cmd, "Copyright")) {
4294 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4295 } else if (!av_strcasecmp(cmd, "Title")) {
4297 get_arg(stream->title, sizeof(stream->title), &p);
4298 } else if (!av_strcasecmp(cmd, "Preroll")) {
4299 get_arg(arg, sizeof(arg), &p);
4301 stream->prebuffer = atof(arg) * 1000;
4302 } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4304 stream->send_on_key = 1;
4305 } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4306 get_arg(arg, sizeof(arg), &p);
4307 audio_id = opt_audio_codec(arg);
4308 if (audio_id == CODEC_ID_NONE) {
4309 ERROR("Unknown AudioCodec: %s\n", arg);
4311 } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4312 get_arg(arg, sizeof(arg), &p);
4313 video_id = opt_video_codec(arg);
4314 if (video_id == CODEC_ID_NONE) {
4315 ERROR("Unknown VideoCodec: %s\n", arg);
4317 } else if (!av_strcasecmp(cmd, "MaxTime")) {
4318 get_arg(arg, sizeof(arg), &p);
4320 stream->max_time = atof(arg) * 1000;
4321 } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4322 get_arg(arg, sizeof(arg), &p);
4324 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4325 } else if (!av_strcasecmp(cmd, "AudioChannels")) {
4326 get_arg(arg, sizeof(arg), &p);
4328 audio_enc.channels = atoi(arg);
4329 } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4330 get_arg(arg, sizeof(arg), &p);
4332 audio_enc.sample_rate = atoi(arg);
4333 } else if (!av_strcasecmp(cmd, "AudioQuality")) {
4334 get_arg(arg, sizeof(arg), &p);
4336 // audio_enc.quality = atof(arg) * 1000;
4338 } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4340 int minrate, maxrate;
4342 get_arg(arg, sizeof(arg), &p);
4344 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4345 video_enc.rc_min_rate = minrate * 1000;
4346 video_enc.rc_max_rate = maxrate * 1000;
4348 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4351 } else if (!av_strcasecmp(cmd, "Debug")) {
4353 get_arg(arg, sizeof(arg), &p);
4354 video_enc.debug = strtol(arg,0,0);
4356 } else if (!av_strcasecmp(cmd, "Strict")) {
4358 get_arg(arg, sizeof(arg), &p);
4359 video_enc.strict_std_compliance = atoi(arg);
4361 } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4363 get_arg(arg, sizeof(arg), &p);
4364 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4366 } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4368 get_arg(arg, sizeof(arg), &p);
4369 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4371 } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4372 get_arg(arg, sizeof(arg), &p);
4374 video_enc.bit_rate = atoi(arg) * 1000;
4376 } else if (!av_strcasecmp(cmd, "VideoSize")) {
4377 get_arg(arg, sizeof(arg), &p);
4379 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4380 if ((video_enc.width % 16) != 0 ||
4381 (video_enc.height % 16) != 0) {
4382 ERROR("Image size must be a multiple of 16\n");
4385 } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4386 get_arg(arg, sizeof(arg), &p);
4388 AVRational frame_rate;
4389 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4390 ERROR("Incorrect frame rate: %s\n", arg);
4392 video_enc.time_base.num = frame_rate.den;
4393 video_enc.time_base.den = frame_rate.num;
4396 } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4397 get_arg(arg, sizeof(arg), &p);
4399 video_enc.gop_size = atoi(arg);
4400 } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4402 video_enc.gop_size = 1;
4403 } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4405 video_enc.mb_decision = FF_MB_DECISION_BITS;
4406 } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4408 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4409 video_enc.flags |= CODEC_FLAG_4MV;
4411 } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4412 !av_strcasecmp(cmd, "AVOptionAudio")) {
4414 AVCodecContext *avctx;
4416 get_arg(arg, sizeof(arg), &p);
4417 get_arg(arg2, sizeof(arg2), &p);
4418 if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4420 type = AV_OPT_FLAG_VIDEO_PARAM;
4423 type = AV_OPT_FLAG_AUDIO_PARAM;
4425 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4426 ERROR("AVOption error: %s %s\n", arg, arg2);
4428 } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4429 !av_strcasecmp(cmd, "AVPresetAudio")) {
4430 AVCodecContext *avctx;
4432 get_arg(arg, sizeof(arg), &p);
4433 if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4435 video_enc.codec_id = video_id;
4436 type = AV_OPT_FLAG_VIDEO_PARAM;
4439 audio_enc.codec_id = audio_id;
4440 type = AV_OPT_FLAG_AUDIO_PARAM;
4442 if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4443 ERROR("AVPreset error: %s\n", arg);
4445 } else if (!av_strcasecmp(cmd, "VideoTag")) {
4446 get_arg(arg, sizeof(arg), &p);
4447 if ((strlen(arg) == 4) && stream)
4448 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4449 } else if (!av_strcasecmp(cmd, "BitExact")) {
4451 video_enc.flags |= CODEC_FLAG_BITEXACT;
4452 } else if (!av_strcasecmp(cmd, "DctFastint")) {
4454 video_enc.dct_algo = FF_DCT_FASTINT;
4455 } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4457 video_enc.idct_algo = FF_IDCT_SIMPLE;
4458 } else if (!av_strcasecmp(cmd, "Qscale")) {
4459 get_arg(arg, sizeof(arg), &p);
4461 video_enc.flags |= CODEC_FLAG_QSCALE;
4462 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4464 } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4465 get_arg(arg, sizeof(arg), &p);
4467 video_enc.max_qdiff = atoi(arg);
4468 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4469 ERROR("VideoQDiff out of range\n");
4472 } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4473 get_arg(arg, sizeof(arg), &p);
4475 video_enc.qmax = atoi(arg);
4476 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4477 ERROR("VideoQMax out of range\n");
4480 } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4481 get_arg(arg, sizeof(arg), &p);
4483 video_enc.qmin = atoi(arg);
4484 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4485 ERROR("VideoQMin out of range\n");
4488 } else if (!av_strcasecmp(cmd, "LumaElim")) {
4489 get_arg(arg, sizeof(arg), &p);
4491 video_enc.luma_elim_threshold = atoi(arg);
4492 } else if (!av_strcasecmp(cmd, "ChromaElim")) {
4493 get_arg(arg, sizeof(arg), &p);
4495 video_enc.chroma_elim_threshold = atoi(arg);
4496 } else if (!av_strcasecmp(cmd, "LumiMask")) {
4497 get_arg(arg, sizeof(arg), &p);
4499 video_enc.lumi_masking = atof(arg);
4500 } else if (!av_strcasecmp(cmd, "DarkMask")) {
4501 get_arg(arg, sizeof(arg), &p);
4503 video_enc.dark_masking = atof(arg);
4504 } else if (!av_strcasecmp(cmd, "NoVideo")) {
4505 video_id = CODEC_ID_NONE;
4506 } else if (!av_strcasecmp(cmd, "NoAudio")) {
4507 audio_id = CODEC_ID_NONE;
4508 } else if (!av_strcasecmp(cmd, "ACL")) {
4509 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4510 } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4512 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4514 } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4515 get_arg(arg, sizeof(arg), &p);
4517 av_freep(&stream->rtsp_option);
4518 stream->rtsp_option = av_strdup(arg);
4520 } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4521 get_arg(arg, sizeof(arg), &p);
4523 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4524 ERROR("Invalid host/IP address: %s\n", arg);
4526 stream->is_multicast = 1;
4527 stream->loop = 1; /* default is looping */
4529 } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4530 get_arg(arg, sizeof(arg), &p);
4532 stream->multicast_port = atoi(arg);
4533 } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4534 get_arg(arg, sizeof(arg), &p);
4536 stream->multicast_ttl = atoi(arg);
4537 } else if (!av_strcasecmp(cmd, "NoLoop")) {
4540 } else if (!av_strcasecmp(cmd, "</Stream>")) {
4542 ERROR("No corresponding <Stream> for </Stream>\n");
4544 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4545 if (audio_id != CODEC_ID_NONE) {
4546 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4547 audio_enc.codec_id = audio_id;
4548 add_codec(stream, &audio_enc);
4550 if (video_id != CODEC_ID_NONE) {
4551 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4552 video_enc.codec_id = video_id;
4553 add_codec(stream, &video_enc);
4558 } else if (!av_strcasecmp(cmd, "<Redirect")) {
4559 /*********************************************/
4561 if (stream || feed || redirect) {
4562 ERROR("Already in a tag\n");
4564 redirect = av_mallocz(sizeof(FFStream));
4565 *last_stream = redirect;
4566 last_stream = &redirect->next;
4568 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4569 q = strrchr(redirect->filename, '>');
4572 redirect->stream_type = STREAM_TYPE_REDIRECT;
4574 } else if (!av_strcasecmp(cmd, "URL")) {
4576 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4577 } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4579 ERROR("No corresponding <Redirect> for </Redirect>\n");
4581 if (!redirect->feed_filename[0]) {
4582 ERROR("No URL found for <Redirect>\n");
4586 } else if (!av_strcasecmp(cmd, "LoadModule")) {
4587 get_arg(arg, sizeof(arg), &p);
4591 ERROR("Module support not compiled into this version: '%s'\n", arg);
4594 ERROR("Incorrect keyword: '%s'\n", cmd);
4606 static void handle_child_exit(int sig)
4611 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4614 for (feed = first_feed; feed; feed = feed->next) {
4615 if (feed->pid == pid) {
4616 int uptime = time(0) - feed->pid_start;
4619 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4622 /* Turn off any more restarts */
4623 feed->child_argv = 0;
4628 need_to_start_children = 1;
4631 static void opt_debug(void)
4634 ffserver_daemon = 0;
4635 logfilename[0] = '-';
4638 static int opt_help(const char *opt, const char *arg)
4640 printf("usage: ffserver [options]\n"
4641 "Hyper fast multi format Audio/Video streaming server\n");
4643 show_help_options(options, "Main options:\n", 0, 0);
4647 static const OptionDef options[] = {
4648 #include "cmdutils_common_opts.h"
4649 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4650 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4651 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4655 int main(int argc, char **argv)
4657 struct sigaction sigact;
4659 parse_loglevel(argc, argv, options);
4661 avformat_network_init();
4663 show_banner(argc, argv, options);
4665 my_program_name = argv[0];
4666 my_program_dir = getcwd(0, 0);
4667 ffserver_daemon = 1;
4669 parse_options(NULL, argc, argv, options, NULL);
4671 unsetenv("http_proxy"); /* Kill the http_proxy */
4673 av_lfg_init(&random_state, av_get_random_seed());
4675 memset(&sigact, 0, sizeof(sigact));
4676 sigact.sa_handler = handle_child_exit;
4677 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4678 sigaction(SIGCHLD, &sigact, 0);
4680 if (parse_ffconfig(config_filename) < 0) {
4681 fprintf(stderr, "Incorrect config file - exiting.\n");
4685 /* open log file if needed */
4686 if (logfilename[0] != '\0') {
4687 if (!strcmp(logfilename, "-"))
4690 logfile = fopen(logfilename, "a");
4691 av_log_set_callback(http_av_log);
4694 build_file_streams();
4696 build_feed_streams();
4698 compute_bandwidth();
4700 /* put the process in background and detach it from its TTY */
4701 if (ffserver_daemon) {
4708 } else if (pid > 0) {
4715 open("/dev/null", O_RDWR);
4716 if (strcmp(logfilename, "-") != 0) {
4726 signal(SIGPIPE, SIG_IGN);
4728 if (ffserver_daemon)
4731 if (http_server() < 0) {
4732 http_log("Could not start server\n");