2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #define _XOPEN_SOURCE 600
26 #define closesocket close
31 #include "libavformat/avformat.h"
32 #include "libavformat/network.h"
33 #include "libavformat/os_support.h"
34 #include "libavformat/rtpdec.h"
35 #include "libavformat/rtsp.h"
36 #include "libavutil/avstring.h"
37 #include "libavutil/lfg.h"
38 #include "libavutil/random_seed.h"
39 #include "libavcore/parseutils.h"
40 #include "libavcodec/opt.h"
44 #include <sys/ioctl.h>
59 const char program_name[] = "FFserver";
60 const int program_birth_year = 2000;
62 static const OptionDef options[];
65 HTTPSTATE_WAIT_REQUEST,
66 HTTPSTATE_SEND_HEADER,
67 HTTPSTATE_SEND_DATA_HEADER,
68 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
69 HTTPSTATE_SEND_DATA_TRAILER,
70 HTTPSTATE_RECEIVE_DATA,
71 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
74 RTSPSTATE_WAIT_REQUEST,
76 RTSPSTATE_SEND_PACKET,
79 static const char *http_state[] = {
95 #if !FF_API_MAX_STREAMS
96 #define MAX_STREAMS 20
99 #define IOBUFFER_INIT_SIZE 8192
101 /* timeouts are in ms */
102 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
103 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
105 #define SYNC_TIMEOUT (10 * 1000)
107 typedef struct RTSPActionServerSetup {
109 char transport_option[512];
110 } RTSPActionServerSetup;
113 int64_t count1, count2;
114 int64_t time1, time2;
117 /* context associated with one connection */
118 typedef struct HTTPContext {
119 enum HTTPState state;
120 int fd; /* socket file descriptor */
121 struct sockaddr_in from_addr; /* origin */
122 struct pollfd *poll_entry; /* used when polling */
124 uint8_t *buffer_ptr, *buffer_end;
127 int chunked_encoding;
128 int chunk_size; /* 0 if it needs to be read */
129 struct HTTPContext *next;
130 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
134 /* input format handling */
135 AVFormatContext *fmt_in;
136 int64_t start_time; /* In milliseconds - this wraps fairly often */
137 int64_t first_pts; /* initial pts value */
138 int64_t cur_pts; /* current pts value from the stream in us */
139 int64_t cur_frame_duration; /* duration of the current frame in us */
140 int cur_frame_bytes; /* output frame size, needed to compute
141 the time at which we send each
143 int pts_stream_index; /* stream we choose as clock reference */
144 int64_t cur_clock; /* current clock reference value in us */
145 /* output format handling */
146 struct FFStream *stream;
147 /* -1 is invalid stream */
148 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
149 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
151 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
152 int last_packet_sent; /* true if last data packet was sent */
154 DataRateData datarate;
161 int is_packetized; /* if true, the stream is packetized */
162 int packet_stream_index; /* current stream for output in state machine */
164 /* RTSP state specific */
165 uint8_t *pb_buffer; /* XXX: use that in all the code */
167 int seq; /* RTSP sequence number */
169 /* RTP state specific */
170 enum RTSPLowerTransport rtp_protocol;
171 char session_id[32]; /* session id */
172 AVFormatContext *rtp_ctx[MAX_STREAMS];
174 /* RTP/UDP specific */
175 URLContext *rtp_handles[MAX_STREAMS];
177 /* RTP/TCP specific */
178 struct HTTPContext *rtsp_c;
179 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
182 /* each generated stream is described here */
186 STREAM_TYPE_REDIRECT,
189 enum IPAddressAction {
194 typedef struct IPAddressACL {
195 struct IPAddressACL *next;
196 enum IPAddressAction action;
197 /* These are in host order */
198 struct in_addr first;
202 /* description of each stream of the ffserver.conf file */
203 typedef struct FFStream {
204 enum StreamType stream_type;
205 char filename[1024]; /* stream filename */
206 struct FFStream *feed; /* feed we are using (can be null if
208 AVFormatParameters *ap_in; /* input parameters */
209 AVInputFormat *ifmt; /* if non NULL, force input format */
212 char dynamic_acl[1024];
214 int prebuffer; /* Number of millseconds early to start */
215 int64_t max_time; /* Number of milliseconds to run */
217 AVStream *streams[MAX_STREAMS];
218 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
219 char feed_filename[1024]; /* file name of the feed storage, or
220 input file name for a stream */
225 pid_t pid; /* Of ffmpeg process */
226 time_t pid_start; /* Of ffmpeg process */
228 struct FFStream *next;
229 unsigned bandwidth; /* bandwidth, in kbits/s */
232 /* multicast specific */
234 struct in_addr multicast_ip;
235 int multicast_port; /* first port used for multicast */
237 int loop; /* if true, send the stream in loops (only meaningful if file) */
240 int feed_opened; /* true if someone is writing to the feed */
241 int is_feed; /* true if it is a feed */
242 int readonly; /* True if writing is prohibited to the file */
243 int truncate; /* True if feeder connection truncate the feed file */
245 int64_t bytes_served;
246 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
247 int64_t feed_write_index; /* current write position in feed (it wraps around) */
248 int64_t feed_size; /* current size of feed */
249 struct FFStream *next_feed;
252 typedef struct FeedData {
253 long long data_count;
254 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
257 static struct sockaddr_in my_http_addr;
258 static struct sockaddr_in my_rtsp_addr;
260 static char logfilename[1024];
261 static HTTPContext *first_http_ctx;
262 static FFStream *first_feed; /* contains only feeds */
263 static FFStream *first_stream; /* contains all streams, including feeds */
265 static void new_connection(int server_fd, int is_rtsp);
266 static void close_connection(HTTPContext *c);
269 static int handle_connection(HTTPContext *c);
270 static int http_parse_request(HTTPContext *c);
271 static int http_send_data(HTTPContext *c);
272 static void compute_status(HTTPContext *c);
273 static int open_input_stream(HTTPContext *c, const char *info);
274 static int http_start_receive_data(HTTPContext *c);
275 static int http_receive_data(HTTPContext *c);
278 static int rtsp_parse_request(HTTPContext *c);
279 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
280 static void rtsp_cmd_options(HTTPContext *c, const char *url);
281 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
282 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
283 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
284 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
287 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
288 struct in_addr my_ip);
291 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
292 FFStream *stream, const char *session_id,
293 enum RTSPLowerTransport rtp_protocol);
294 static int rtp_new_av_stream(HTTPContext *c,
295 int stream_index, struct sockaddr_in *dest_addr,
296 HTTPContext *rtsp_c);
298 static const char *my_program_name;
299 static const char *my_program_dir;
301 static const char *config_filename = "/etc/ffserver.conf";
303 static int ffserver_debug;
304 static int ffserver_daemon;
305 static int no_launch;
306 static int need_to_start_children;
308 /* maximum number of simultaneous HTTP connections */
309 static unsigned int nb_max_http_connections = 2000;
310 static unsigned int nb_max_connections = 5;
311 static unsigned int nb_connections;
313 static uint64_t max_bandwidth = 1000;
314 static uint64_t current_bandwidth;
316 static int64_t cur_time; // Making this global saves on passing it around everywhere
318 static AVLFG random_state;
320 static FILE *logfile = NULL;
322 /* FIXME: make ffserver work with IPv6 */
323 /* resolve host with also IP address parsing */
324 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
327 if (!ff_inet_aton(hostname, sin_addr)) {
329 struct addrinfo *ai, *cur;
330 struct addrinfo hints;
331 memset(&hints, 0, sizeof(hints));
332 hints.ai_family = AF_INET;
333 if (getaddrinfo(hostname, NULL, &hints, &ai))
335 /* getaddrinfo returns a linked list of addrinfo structs.
336 * Even if we set ai_family = AF_INET above, make sure
337 * that the returned one actually is of the correct type. */
338 for (cur = ai; cur; cur = cur->ai_next) {
339 if (cur->ai_family == AF_INET) {
340 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
349 hp = gethostbyname(hostname);
352 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
358 static char *ctime1(char *buf2)
366 p = buf2 + strlen(p) - 1;
372 static void http_vlog(const char *fmt, va_list vargs)
374 static int print_prefix = 1;
379 fprintf(logfile, "%s ", buf);
381 print_prefix = strstr(fmt, "\n") != NULL;
382 vfprintf(logfile, fmt, vargs);
387 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
390 va_start(vargs, fmt);
391 http_vlog(fmt, vargs);
395 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
397 static int print_prefix = 1;
398 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
399 if (level > av_log_get_level())
401 if (print_prefix && avc)
402 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
403 print_prefix = strstr(fmt, "\n") != NULL;
404 http_vlog(fmt, vargs);
407 static void log_connection(HTTPContext *c)
412 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
413 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
414 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
417 static void update_datarate(DataRateData *drd, int64_t count)
419 if (!drd->time1 && !drd->count1) {
420 drd->time1 = drd->time2 = cur_time;
421 drd->count1 = drd->count2 = count;
422 } else if (cur_time - drd->time2 > 5000) {
423 drd->time1 = drd->time2;
424 drd->count1 = drd->count2;
425 drd->time2 = cur_time;
430 /* In bytes per second */
431 static int compute_datarate(DataRateData *drd, int64_t count)
433 if (cur_time == drd->time1)
436 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
440 static void start_children(FFStream *feed)
445 for (; feed; feed = feed->next) {
446 if (feed->child_argv && !feed->pid) {
447 feed->pid_start = time(0);
452 http_log("Unable to create children\n");
461 av_strlcpy(pathname, my_program_name, sizeof(pathname));
463 slash = strrchr(pathname, '/');
468 strcpy(slash, "ffmpeg");
470 http_log("Launch commandline: ");
471 http_log("%s ", pathname);
472 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
473 http_log("%s ", feed->child_argv[i]);
476 for (i = 3; i < 256; i++)
479 if (!ffserver_debug) {
480 i = open("/dev/null", O_RDWR);
489 /* This is needed to make relative pathnames work */
490 chdir(my_program_dir);
492 signal(SIGPIPE, SIG_DFL);
494 execvp(pathname, feed->child_argv);
502 /* open a listening socket */
503 static int socket_open_listen(struct sockaddr_in *my_addr)
507 server_fd = socket(AF_INET,SOCK_STREAM,0);
514 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
516 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
518 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
520 closesocket(server_fd);
524 if (listen (server_fd, 5) < 0) {
526 closesocket(server_fd);
529 ff_socket_nonblock(server_fd, 1);
534 /* start all multicast streams */
535 static void start_multicast(void)
540 struct sockaddr_in dest_addr;
541 int default_port, stream_index;
544 for(stream = first_stream; stream != NULL; stream = stream->next) {
545 if (stream->is_multicast) {
546 /* open the RTP connection */
547 snprintf(session_id, sizeof(session_id), "%08x%08x",
548 av_lfg_get(&random_state), av_lfg_get(&random_state));
550 /* choose a port if none given */
551 if (stream->multicast_port == 0) {
552 stream->multicast_port = default_port;
556 dest_addr.sin_family = AF_INET;
557 dest_addr.sin_addr = stream->multicast_ip;
558 dest_addr.sin_port = htons(stream->multicast_port);
560 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
561 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
565 if (open_input_stream(rtp_c, "") < 0) {
566 http_log("Could not open input stream for stream '%s'\n",
571 /* open each RTP stream */
572 for(stream_index = 0; stream_index < stream->nb_streams;
574 dest_addr.sin_port = htons(stream->multicast_port +
576 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
577 http_log("Could not open output stream '%s/streamid=%d'\n",
578 stream->filename, stream_index);
583 /* change state to send data */
584 rtp_c->state = HTTPSTATE_SEND_DATA;
589 /* main loop of the http server */
590 static int http_server(void)
592 int server_fd = 0, rtsp_server_fd = 0;
593 int ret, delay, delay1;
594 struct pollfd *poll_table, *poll_entry;
595 HTTPContext *c, *c_next;
597 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
598 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
602 if (my_http_addr.sin_port) {
603 server_fd = socket_open_listen(&my_http_addr);
608 if (my_rtsp_addr.sin_port) {
609 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
610 if (rtsp_server_fd < 0)
614 if (!rtsp_server_fd && !server_fd) {
615 http_log("HTTP and RTSP disabled.\n");
619 http_log("FFserver started.\n");
621 start_children(first_feed);
626 poll_entry = poll_table;
628 poll_entry->fd = server_fd;
629 poll_entry->events = POLLIN;
632 if (rtsp_server_fd) {
633 poll_entry->fd = rtsp_server_fd;
634 poll_entry->events = POLLIN;
638 /* wait for events on each HTTP handle */
645 case HTTPSTATE_SEND_HEADER:
646 case RTSPSTATE_SEND_REPLY:
647 case RTSPSTATE_SEND_PACKET:
648 c->poll_entry = poll_entry;
650 poll_entry->events = POLLOUT;
653 case HTTPSTATE_SEND_DATA_HEADER:
654 case HTTPSTATE_SEND_DATA:
655 case HTTPSTATE_SEND_DATA_TRAILER:
656 if (!c->is_packetized) {
657 /* for TCP, we output as much as we can (may need to put a limit) */
658 c->poll_entry = poll_entry;
660 poll_entry->events = POLLOUT;
663 /* when ffserver is doing the timing, we work by
664 looking at which packet need to be sent every
666 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
671 case HTTPSTATE_WAIT_REQUEST:
672 case HTTPSTATE_RECEIVE_DATA:
673 case HTTPSTATE_WAIT_FEED:
674 case RTSPSTATE_WAIT_REQUEST:
675 /* need to catch errors */
676 c->poll_entry = poll_entry;
678 poll_entry->events = POLLIN;/* Maybe this will work */
682 c->poll_entry = NULL;
688 /* wait for an event on one connection. We poll at least every
689 second to handle timeouts */
691 ret = poll(poll_table, poll_entry - poll_table, delay);
692 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
693 ff_neterrno() != FF_NETERROR(EINTR))
697 cur_time = av_gettime() / 1000;
699 if (need_to_start_children) {
700 need_to_start_children = 0;
701 start_children(first_feed);
704 /* now handle the events */
705 for(c = first_http_ctx; c != NULL; c = c_next) {
707 if (handle_connection(c) < 0) {
708 /* close and free the connection */
714 poll_entry = poll_table;
716 /* new HTTP connection request ? */
717 if (poll_entry->revents & POLLIN)
718 new_connection(server_fd, 0);
721 if (rtsp_server_fd) {
722 /* new RTSP connection request ? */
723 if (poll_entry->revents & POLLIN)
724 new_connection(rtsp_server_fd, 1);
729 /* start waiting for a new HTTP/RTSP request */
730 static void start_wait_request(HTTPContext *c, int is_rtsp)
732 c->buffer_ptr = c->buffer;
733 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
736 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
737 c->state = RTSPSTATE_WAIT_REQUEST;
739 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
740 c->state = HTTPSTATE_WAIT_REQUEST;
744 static void http_send_too_busy_reply(int fd)
747 int len = snprintf(buffer, sizeof(buffer),
748 "HTTP/1.0 503 Server too busy\r\n"
749 "Content-type: text/html\r\n"
751 "<html><head><title>Too busy</title></head><body>\r\n"
752 "<p>The server is too busy to serve your request at this time.</p>\r\n"
753 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
754 "</body></html>\r\n",
755 nb_connections, nb_max_connections);
756 send(fd, buffer, len, 0);
760 static void new_connection(int server_fd, int is_rtsp)
762 struct sockaddr_in from_addr;
764 HTTPContext *c = NULL;
766 len = sizeof(from_addr);
767 fd = accept(server_fd, (struct sockaddr *)&from_addr,
770 http_log("error during accept %s\n", strerror(errno));
773 ff_socket_nonblock(fd, 1);
775 if (nb_connections >= nb_max_connections) {
776 http_send_too_busy_reply(fd);
780 /* add a new connection */
781 c = av_mallocz(sizeof(HTTPContext));
786 c->poll_entry = NULL;
787 c->from_addr = from_addr;
788 c->buffer_size = IOBUFFER_INIT_SIZE;
789 c->buffer = av_malloc(c->buffer_size);
793 c->next = first_http_ctx;
797 start_wait_request(c, is_rtsp);
809 static void close_connection(HTTPContext *c)
811 HTTPContext **cp, *c1;
813 AVFormatContext *ctx;
817 /* remove connection from list */
818 cp = &first_http_ctx;
819 while ((*cp) != NULL) {
827 /* remove references, if any (XXX: do it faster) */
828 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
833 /* remove connection associated resources */
837 /* close each frame parser */
838 for(i=0;i<c->fmt_in->nb_streams;i++) {
839 st = c->fmt_in->streams[i];
840 if (st->codec->codec)
841 avcodec_close(st->codec);
843 av_close_input_file(c->fmt_in);
846 /* free RTP output streams if any */
849 nb_streams = c->stream->nb_streams;
851 for(i=0;i<nb_streams;i++) {
854 av_write_trailer(ctx);
855 av_metadata_free(&ctx->metadata);
856 av_free(ctx->streams[0]);
859 h = c->rtp_handles[i];
866 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
869 if (url_open_dyn_buf(&ctx->pb) >= 0) {
870 av_write_trailer(ctx);
871 av_freep(&c->pb_buffer);
872 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
877 for(i=0; i<ctx->nb_streams; i++)
878 av_free(ctx->streams[i]);
880 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
881 current_bandwidth -= c->stream->bandwidth;
883 /* signal that there is no feed if we are the feeder socket */
884 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
885 c->stream->feed_opened = 0;
889 av_freep(&c->pb_buffer);
890 av_freep(&c->packet_buffer);
896 static int handle_connection(HTTPContext *c)
901 case HTTPSTATE_WAIT_REQUEST:
902 case RTSPSTATE_WAIT_REQUEST:
904 if ((c->timeout - cur_time) < 0)
906 if (c->poll_entry->revents & (POLLERR | POLLHUP))
909 /* no need to read if no events */
910 if (!(c->poll_entry->revents & POLLIN))
914 len = recv(c->fd, c->buffer_ptr, 1, 0);
916 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
917 ff_neterrno() != FF_NETERROR(EINTR))
919 } else if (len == 0) {
922 /* search for end of request. */
924 c->buffer_ptr += len;
926 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
927 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
928 /* request found : parse it and reply */
929 if (c->state == HTTPSTATE_WAIT_REQUEST) {
930 ret = http_parse_request(c);
932 ret = rtsp_parse_request(c);
936 } else if (ptr >= c->buffer_end) {
937 /* request too long: cannot do anything */
939 } else goto read_loop;
943 case HTTPSTATE_SEND_HEADER:
944 if (c->poll_entry->revents & (POLLERR | POLLHUP))
947 /* no need to write if no events */
948 if (!(c->poll_entry->revents & POLLOUT))
950 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
952 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
953 ff_neterrno() != FF_NETERROR(EINTR)) {
954 /* error : close connection */
955 av_freep(&c->pb_buffer);
959 c->buffer_ptr += len;
961 c->stream->bytes_served += len;
962 c->data_count += len;
963 if (c->buffer_ptr >= c->buffer_end) {
964 av_freep(&c->pb_buffer);
968 /* all the buffer was sent : synchronize to the incoming stream */
969 c->state = HTTPSTATE_SEND_DATA_HEADER;
970 c->buffer_ptr = c->buffer_end = c->buffer;
975 case HTTPSTATE_SEND_DATA:
976 case HTTPSTATE_SEND_DATA_HEADER:
977 case HTTPSTATE_SEND_DATA_TRAILER:
978 /* for packetized output, we consider we can always write (the
979 input streams sets the speed). It may be better to verify
980 that we do not rely too much on the kernel queues */
981 if (!c->is_packetized) {
982 if (c->poll_entry->revents & (POLLERR | POLLHUP))
985 /* no need to read if no events */
986 if (!(c->poll_entry->revents & POLLOUT))
989 if (http_send_data(c) < 0)
991 /* close connection if trailer sent */
992 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
995 case HTTPSTATE_RECEIVE_DATA:
996 /* no need to read if no events */
997 if (c->poll_entry->revents & (POLLERR | POLLHUP))
999 if (!(c->poll_entry->revents & POLLIN))
1001 if (http_receive_data(c) < 0)
1004 case HTTPSTATE_WAIT_FEED:
1005 /* no need to read if no events */
1006 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1009 /* nothing to do, we'll be waken up by incoming feed packets */
1012 case RTSPSTATE_SEND_REPLY:
1013 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1014 av_freep(&c->pb_buffer);
1017 /* no need to write if no events */
1018 if (!(c->poll_entry->revents & POLLOUT))
1020 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1022 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
1023 ff_neterrno() != FF_NETERROR(EINTR)) {
1024 /* error : close connection */
1025 av_freep(&c->pb_buffer);
1029 c->buffer_ptr += len;
1030 c->data_count += len;
1031 if (c->buffer_ptr >= c->buffer_end) {
1032 /* all the buffer was sent : wait for a new request */
1033 av_freep(&c->pb_buffer);
1034 start_wait_request(c, 1);
1038 case RTSPSTATE_SEND_PACKET:
1039 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1040 av_freep(&c->packet_buffer);
1043 /* no need to write if no events */
1044 if (!(c->poll_entry->revents & POLLOUT))
1046 len = send(c->fd, c->packet_buffer_ptr,
1047 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1049 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
1050 ff_neterrno() != FF_NETERROR(EINTR)) {
1051 /* error : close connection */
1052 av_freep(&c->packet_buffer);
1056 c->packet_buffer_ptr += len;
1057 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1058 /* all the buffer was sent : wait for a new request */
1059 av_freep(&c->packet_buffer);
1060 c->state = RTSPSTATE_WAIT_REQUEST;
1064 case HTTPSTATE_READY:
1073 static int extract_rates(char *rates, int ratelen, const char *request)
1077 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1078 if (strncasecmp(p, "Pragma:", 7) == 0) {
1079 const char *q = p + 7;
1081 while (*q && *q != '\n' && isspace(*q))
1084 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1090 memset(rates, 0xff, ratelen);
1093 while (*q && *q != '\n' && *q != ':')
1096 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1100 if (stream_no < ratelen && stream_no >= 0)
1101 rates[stream_no] = rate_no;
1103 while (*q && *q != '\n' && !isspace(*q))
1110 p = strchr(p, '\n');
1120 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1123 int best_bitrate = 100000000;
1126 for (i = 0; i < feed->nb_streams; i++) {
1127 AVCodecContext *feed_codec = feed->streams[i]->codec;
1129 if (feed_codec->codec_id != codec->codec_id ||
1130 feed_codec->sample_rate != codec->sample_rate ||
1131 feed_codec->width != codec->width ||
1132 feed_codec->height != codec->height)
1135 /* Potential stream */
1137 /* We want the fastest stream less than bit_rate, or the slowest
1138 * faster than bit_rate
1141 if (feed_codec->bit_rate <= bit_rate) {
1142 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1143 best_bitrate = feed_codec->bit_rate;
1147 if (feed_codec->bit_rate < best_bitrate) {
1148 best_bitrate = feed_codec->bit_rate;
1157 static int modify_current_stream(HTTPContext *c, char *rates)
1160 FFStream *req = c->stream;
1161 int action_required = 0;
1163 /* Not much we can do for a feed */
1167 for (i = 0; i < req->nb_streams; i++) {
1168 AVCodecContext *codec = req->streams[i]->codec;
1172 c->switch_feed_streams[i] = req->feed_streams[i];
1175 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1178 /* Wants off or slow */
1179 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1181 /* This doesn't work well when it turns off the only stream! */
1182 c->switch_feed_streams[i] = -2;
1183 c->feed_streams[i] = -2;
1188 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1189 action_required = 1;
1192 return action_required;
1195 /* XXX: factorize in utils.c ? */
1196 /* XXX: take care with different space meaning */
1197 static void skip_spaces(const char **pp)
1201 while (*p == ' ' || *p == '\t')
1206 static void get_word(char *buf, int buf_size, const char **pp)
1214 while (!isspace(*p) && *p != '\0') {
1215 if ((q - buf) < buf_size - 1)
1224 static void get_arg(char *buf, int buf_size, const char **pp)
1231 while (isspace(*p)) p++;
1234 if (*p == '\"' || *p == '\'')
1246 if ((q - buf) < buf_size - 1)
1251 if (quote && *p == quote)
1256 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1257 const char *p, const char *filename, int line_num)
1263 get_arg(arg, sizeof(arg), &p);
1264 if (strcasecmp(arg, "allow") == 0)
1265 acl.action = IP_ALLOW;
1266 else if (strcasecmp(arg, "deny") == 0)
1267 acl.action = IP_DENY;
1269 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1270 filename, line_num, arg);
1274 get_arg(arg, sizeof(arg), &p);
1276 if (resolve_host(&acl.first, arg) != 0) {
1277 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1278 filename, line_num, arg);
1281 acl.last = acl.first;
1283 get_arg(arg, sizeof(arg), &p);
1286 if (resolve_host(&acl.last, arg) != 0) {
1287 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1288 filename, line_num, arg);
1294 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1295 IPAddressACL **naclp = 0;
1301 naclp = &stream->acl;
1307 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1308 filename, line_num);
1314 naclp = &(*naclp)->next;
1322 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1327 IPAddressACL *acl = NULL;
1331 f = fopen(stream->dynamic_acl, "r");
1333 perror(stream->dynamic_acl);
1337 acl = av_mallocz(sizeof(IPAddressACL));
1341 if (fgets(line, sizeof(line), f) == NULL)
1347 if (*p == '\0' || *p == '#')
1349 get_arg(cmd, sizeof(cmd), &p);
1351 if (!strcasecmp(cmd, "ACL"))
1352 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1359 static void free_acl_list(IPAddressACL *in_acl)
1361 IPAddressACL *pacl,*pacl2;
1371 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1373 enum IPAddressAction last_action = IP_DENY;
1375 struct in_addr *src = &c->from_addr.sin_addr;
1376 unsigned long src_addr = src->s_addr;
1378 for (acl = in_acl; acl; acl = acl->next) {
1379 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1380 return (acl->action == IP_ALLOW) ? 1 : 0;
1381 last_action = acl->action;
1384 /* Nothing matched, so return not the last action */
1385 return (last_action == IP_DENY) ? 1 : 0;
1388 static int validate_acl(FFStream *stream, HTTPContext *c)
1394 /* if stream->acl is null validate_acl_list will return 1 */
1395 ret = validate_acl_list(stream->acl, c);
1397 if (stream->dynamic_acl[0]) {
1398 acl = parse_dynamic_acl(stream, c);
1400 ret = validate_acl_list(acl, c);
1408 /* compute the real filename of a file by matching it without its
1409 extensions to all the stream filenames */
1410 static void compute_real_filename(char *filename, int max_size)
1417 /* compute filename by matching without the file extensions */
1418 av_strlcpy(file1, filename, sizeof(file1));
1419 p = strrchr(file1, '.');
1422 for(stream = first_stream; stream != NULL; stream = stream->next) {
1423 av_strlcpy(file2, stream->filename, sizeof(file2));
1424 p = strrchr(file2, '.');
1427 if (!strcmp(file1, file2)) {
1428 av_strlcpy(filename, stream->filename, max_size);
1443 /* parse http request and prepare header */
1444 static int http_parse_request(HTTPContext *c)
1447 enum RedirType redir_type;
1449 char info[1024], filename[1024];
1453 const char *mime_type;
1457 char *useragent = 0;
1460 get_word(cmd, sizeof(cmd), (const char **)&p);
1461 av_strlcpy(c->method, cmd, sizeof(c->method));
1463 if (!strcmp(cmd, "GET"))
1465 else if (!strcmp(cmd, "POST"))
1470 get_word(url, sizeof(url), (const char **)&p);
1471 av_strlcpy(c->url, url, sizeof(c->url));
1473 get_word(protocol, sizeof(protocol), (const char **)&p);
1474 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1477 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1480 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1482 /* find the filename and the optional info string in the request */
1483 p = strchr(url, '?');
1485 av_strlcpy(info, p, sizeof(info));
1490 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1492 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1493 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1495 if (*useragent && *useragent != '\n' && isspace(*useragent))
1499 p = strchr(p, '\n');
1506 redir_type = REDIR_NONE;
1507 if (av_match_ext(filename, "asx")) {
1508 redir_type = REDIR_ASX;
1509 filename[strlen(filename)-1] = 'f';
1510 } else if (av_match_ext(filename, "asf") &&
1511 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1512 /* if this isn't WMP or lookalike, return the redirector file */
1513 redir_type = REDIR_ASF;
1514 } else if (av_match_ext(filename, "rpm,ram")) {
1515 redir_type = REDIR_RAM;
1516 strcpy(filename + strlen(filename)-2, "m");
1517 } else if (av_match_ext(filename, "rtsp")) {
1518 redir_type = REDIR_RTSP;
1519 compute_real_filename(filename, sizeof(filename) - 1);
1520 } else if (av_match_ext(filename, "sdp")) {
1521 redir_type = REDIR_SDP;
1522 compute_real_filename(filename, sizeof(filename) - 1);
1525 // "redirect" / request to index.html
1526 if (!strlen(filename))
1527 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1529 stream = first_stream;
1530 while (stream != NULL) {
1531 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1533 stream = stream->next;
1535 if (stream == NULL) {
1536 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1537 http_log("File '%s' not found\n", url);
1542 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1543 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1545 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1546 c->http_error = 301;
1548 q += snprintf(q, c->buffer_size,
1549 "HTTP/1.0 301 Moved\r\n"
1551 "Content-type: text/html\r\n"
1553 "<html><head><title>Moved</title></head><body>\r\n"
1554 "You should be <a href=\"%s\">redirected</a>.\r\n"
1555 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1556 /* prepare output buffer */
1557 c->buffer_ptr = c->buffer;
1559 c->state = HTTPSTATE_SEND_HEADER;
1563 /* If this is WMP, get the rate information */
1564 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1565 if (modify_current_stream(c, ratebuf)) {
1566 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1567 if (c->switch_feed_streams[i] >= 0)
1568 c->switch_feed_streams[i] = -1;
1573 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1574 current_bandwidth += stream->bandwidth;
1576 /* If already streaming this feed, do not let start another feeder. */
1577 if (stream->feed_opened) {
1578 snprintf(msg, sizeof(msg), "This feed is already being received.");
1579 http_log("Feed '%s' already being received\n", stream->feed_filename);
1583 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1584 c->http_error = 503;
1586 q += snprintf(q, c->buffer_size,
1587 "HTTP/1.0 503 Server too busy\r\n"
1588 "Content-type: text/html\r\n"
1590 "<html><head><title>Too busy</title></head><body>\r\n"
1591 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1592 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1593 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1594 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1595 /* prepare output buffer */
1596 c->buffer_ptr = c->buffer;
1598 c->state = HTTPSTATE_SEND_HEADER;
1602 if (redir_type != REDIR_NONE) {
1605 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1606 if (strncasecmp(p, "Host:", 5) == 0) {
1610 p = strchr(p, '\n');
1621 while (isspace(*hostinfo))
1624 eoh = strchr(hostinfo, '\n');
1626 if (eoh[-1] == '\r')
1629 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1630 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1631 hostbuf[eoh - hostinfo] = 0;
1633 c->http_error = 200;
1635 switch(redir_type) {
1637 q += snprintf(q, c->buffer_size,
1638 "HTTP/1.0 200 ASX Follows\r\n"
1639 "Content-type: video/x-ms-asf\r\n"
1641 "<ASX Version=\"3\">\r\n"
1642 //"<!-- Autogenerated by ffserver -->\r\n"
1643 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1644 "</ASX>\r\n", hostbuf, filename, info);
1647 q += snprintf(q, c->buffer_size,
1648 "HTTP/1.0 200 RAM Follows\r\n"
1649 "Content-type: audio/x-pn-realaudio\r\n"
1651 "# Autogenerated by ffserver\r\n"
1652 "http://%s/%s%s\r\n", hostbuf, filename, info);
1655 q += snprintf(q, c->buffer_size,
1656 "HTTP/1.0 200 ASF Redirect follows\r\n"
1657 "Content-type: video/x-ms-asf\r\n"
1660 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1664 char hostname[256], *p;
1665 /* extract only hostname */
1666 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1667 p = strrchr(hostname, ':');
1670 q += snprintf(q, c->buffer_size,
1671 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1672 /* XXX: incorrect mime type ? */
1673 "Content-type: application/x-rtsp\r\n"
1675 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1681 int sdp_data_size, len;
1682 struct sockaddr_in my_addr;
1684 q += snprintf(q, c->buffer_size,
1685 "HTTP/1.0 200 OK\r\n"
1686 "Content-type: application/sdp\r\n"
1689 len = sizeof(my_addr);
1690 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1692 /* XXX: should use a dynamic buffer */
1693 sdp_data_size = prepare_sdp_description(stream,
1696 if (sdp_data_size > 0) {
1697 memcpy(q, sdp_data, sdp_data_size);
1709 /* prepare output buffer */
1710 c->buffer_ptr = c->buffer;
1712 c->state = HTTPSTATE_SEND_HEADER;
1718 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1722 stream->conns_served++;
1724 /* XXX: add there authenticate and IP match */
1727 /* if post, it means a feed is being sent */
1728 if (!stream->is_feed) {
1729 /* However it might be a status report from WMP! Let us log the
1730 * data as it might come in handy one day. */
1734 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1735 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1739 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1740 client_id = strtol(p + 18, 0, 10);
1741 p = strchr(p, '\n');
1749 char *eol = strchr(logline, '\n');
1754 if (eol[-1] == '\r')
1756 http_log("%.*s\n", (int) (eol - logline), logline);
1757 c->suppress_log = 1;
1762 http_log("\nGot request:\n%s\n", c->buffer);
1765 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1768 /* Now we have to find the client_id */
1769 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1770 if (wmpc->wmp_client_id == client_id)
1774 if (wmpc && modify_current_stream(wmpc, ratebuf))
1775 wmpc->switch_pending = 1;
1778 snprintf(msg, sizeof(msg), "POST command not handled");
1782 if (http_start_receive_data(c) < 0) {
1783 snprintf(msg, sizeof(msg), "could not open feed");
1787 c->state = HTTPSTATE_RECEIVE_DATA;
1792 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1793 http_log("\nGot request:\n%s\n", c->buffer);
1796 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1799 /* open input stream */
1800 if (open_input_stream(c, info) < 0) {
1801 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1805 /* prepare http header */
1807 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1808 mime_type = c->stream->fmt->mime_type;
1810 mime_type = "application/x-octet-stream";
1811 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1813 /* for asf, we need extra headers */
1814 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1815 /* Need to allocate a client id */
1817 c->wmp_client_id = av_lfg_get(&random_state);
1819 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);
1821 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1822 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1824 /* prepare output buffer */
1826 c->buffer_ptr = c->buffer;
1828 c->state = HTTPSTATE_SEND_HEADER;
1831 c->http_error = 404;
1833 q += snprintf(q, c->buffer_size,
1834 "HTTP/1.0 404 Not Found\r\n"
1835 "Content-type: text/html\r\n"
1838 "<head><title>404 Not Found</title></head>\n"
1841 /* prepare output buffer */
1842 c->buffer_ptr = c->buffer;
1844 c->state = HTTPSTATE_SEND_HEADER;
1848 c->http_error = 200; /* horrible : we use this value to avoid
1849 going to the send data state */
1850 c->state = HTTPSTATE_SEND_HEADER;
1854 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1856 static const char *suffix = " kMGTP";
1859 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1861 url_fprintf(pb, "%"PRId64"%c", count, *s);
1864 static void compute_status(HTTPContext *c)
1873 if (url_open_dyn_buf(&pb) < 0) {
1874 /* XXX: return an error ? */
1875 c->buffer_ptr = c->buffer;
1876 c->buffer_end = c->buffer;
1880 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1881 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1882 url_fprintf(pb, "Pragma: no-cache\r\n");
1883 url_fprintf(pb, "\r\n");
1885 url_fprintf(pb, "<html><head><title>%s Status</title>\n", program_name);
1886 if (c->stream->feed_filename[0])
1887 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1888 url_fprintf(pb, "</head>\n<body>");
1889 url_fprintf(pb, "<h1>%s Status</h1>\n", program_name);
1891 url_fprintf(pb, "<h2>Available Streams</h2>\n");
1892 url_fprintf(pb, "<table cellspacing=0 cellpadding=4>\n");
1893 url_fprintf(pb, "<tr><th valign=top>Path<th align=left>Served<br>Conns<th><br>bytes<th valign=top>Format<th>Bit rate<br>kbits/s<th align=left>Video<br>kbits/s<th><br>Codec<th align=left>Audio<br>kbits/s<th><br>Codec<th align=left valign=top>Feed\n");
1894 stream = first_stream;
1895 while (stream != NULL) {
1896 char sfilename[1024];
1899 if (stream->feed != stream) {
1900 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1901 eosf = sfilename + strlen(sfilename);
1902 if (eosf - sfilename >= 4) {
1903 if (strcmp(eosf - 4, ".asf") == 0)
1904 strcpy(eosf - 4, ".asx");
1905 else if (strcmp(eosf - 3, ".rm") == 0)
1906 strcpy(eosf - 3, ".ram");
1907 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1908 /* generate a sample RTSP director if
1909 unicast. Generate an SDP redirector if
1911 eosf = strrchr(sfilename, '.');
1913 eosf = sfilename + strlen(sfilename);
1914 if (stream->is_multicast)
1915 strcpy(eosf, ".sdp");
1917 strcpy(eosf, ".rtsp");
1921 url_fprintf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1922 sfilename, stream->filename);
1923 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1924 stream->conns_served);
1925 fmt_bytecount(pb, stream->bytes_served);
1926 switch(stream->stream_type) {
1927 case STREAM_TYPE_LIVE: {
1928 int audio_bit_rate = 0;
1929 int video_bit_rate = 0;
1930 const char *audio_codec_name = "";
1931 const char *video_codec_name = "";
1932 const char *audio_codec_name_extra = "";
1933 const char *video_codec_name_extra = "";
1935 for(i=0;i<stream->nb_streams;i++) {
1936 AVStream *st = stream->streams[i];
1937 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1938 switch(st->codec->codec_type) {
1939 case AVMEDIA_TYPE_AUDIO:
1940 audio_bit_rate += st->codec->bit_rate;
1942 if (*audio_codec_name)
1943 audio_codec_name_extra = "...";
1944 audio_codec_name = codec->name;
1947 case AVMEDIA_TYPE_VIDEO:
1948 video_bit_rate += st->codec->bit_rate;
1950 if (*video_codec_name)
1951 video_codec_name_extra = "...";
1952 video_codec_name = codec->name;
1955 case AVMEDIA_TYPE_DATA:
1956 video_bit_rate += st->codec->bit_rate;
1962 url_fprintf(pb, "<td align=center> %s <td align=right> %d <td align=right> %d <td> %s %s <td align=right> %d <td> %s %s",
1965 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1966 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1968 url_fprintf(pb, "<td>%s", stream->feed->filename);
1970 url_fprintf(pb, "<td>%s", stream->feed_filename);
1971 url_fprintf(pb, "\n");
1975 url_fprintf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1979 stream = stream->next;
1981 url_fprintf(pb, "</table>\n");
1983 stream = first_stream;
1984 while (stream != NULL) {
1985 if (stream->feed == stream) {
1986 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1988 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1990 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1995 /* This is somewhat linux specific I guess */
1996 snprintf(ps_cmd, sizeof(ps_cmd),
1997 "ps -o \"%%cpu,cputime\" --no-headers %d",
2000 pid_stat = popen(ps_cmd, "r");
2005 if (fscanf(pid_stat, "%10s %64s", cpuperc,
2007 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2015 url_fprintf(pb, "<p>");
2017 url_fprintf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
2019 for (i = 0; i < stream->nb_streams; i++) {
2020 AVStream *st = stream->streams[i];
2021 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2022 const char *type = "unknown";
2023 char parameters[64];
2027 switch(st->codec->codec_type) {
2028 case AVMEDIA_TYPE_AUDIO:
2030 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2032 case AVMEDIA_TYPE_VIDEO:
2034 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2035 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2040 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2041 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2043 url_fprintf(pb, "</table>\n");
2046 stream = stream->next;
2049 /* connection status */
2050 url_fprintf(pb, "<h2>Connection Status</h2>\n");
2052 url_fprintf(pb, "Number of connections: %d / %d<br>\n",
2053 nb_connections, nb_max_connections);
2055 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2056 current_bandwidth, max_bandwidth);
2058 url_fprintf(pb, "<table>\n");
2059 url_fprintf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
2060 c1 = first_http_ctx;
2062 while (c1 != NULL) {
2068 for (j = 0; j < c1->stream->nb_streams; j++) {
2069 if (!c1->stream->feed)
2070 bitrate += c1->stream->streams[j]->codec->bit_rate;
2071 else if (c1->feed_streams[j] >= 0)
2072 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2077 p = inet_ntoa(c1->from_addr.sin_addr);
2078 url_fprintf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2080 c1->stream ? c1->stream->filename : "",
2081 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2084 http_state[c1->state]);
2085 fmt_bytecount(pb, bitrate);
2086 url_fprintf(pb, "<td align=right>");
2087 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2088 url_fprintf(pb, "<td align=right>");
2089 fmt_bytecount(pb, c1->data_count);
2090 url_fprintf(pb, "\n");
2093 url_fprintf(pb, "</table>\n");
2098 url_fprintf(pb, "<hr size=1 noshade>Generated at %s", p);
2099 url_fprintf(pb, "</body>\n</html>\n");
2101 len = url_close_dyn_buf(pb, &c->pb_buffer);
2102 c->buffer_ptr = c->pb_buffer;
2103 c->buffer_end = c->pb_buffer + len;
2106 /* check if the parser needs to be opened for stream i */
2107 static void open_parser(AVFormatContext *s, int i)
2109 AVStream *st = s->streams[i];
2112 if (!st->codec->codec) {
2113 codec = avcodec_find_decoder(st->codec->codec_id);
2114 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
2115 st->codec->parse_only = 1;
2116 if (avcodec_open(st->codec, codec) < 0)
2117 st->codec->parse_only = 0;
2122 static int open_input_stream(HTTPContext *c, const char *info)
2125 char input_filename[1024];
2127 int buf_size, i, ret;
2130 /* find file name */
2131 if (c->stream->feed) {
2132 strcpy(input_filename, c->stream->feed->feed_filename);
2133 buf_size = FFM_PACKET_SIZE;
2134 /* compute position (absolute time) */
2135 if (find_info_tag(buf, sizeof(buf), "date", info)) {
2136 stream_pos = parse_date(buf, 0);
2137 if (stream_pos == INT64_MIN)
2139 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
2140 int prebuffer = strtol(buf, 0, 10);
2141 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2143 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2145 strcpy(input_filename, c->stream->feed_filename);
2147 /* compute position (relative time) */
2148 if (find_info_tag(buf, sizeof(buf), "date", info)) {
2149 stream_pos = parse_date(buf, 1);
2150 if (stream_pos == INT64_MIN)
2155 if (input_filename[0] == '\0')
2159 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
2160 buf_size, c->stream->ap_in)) < 0) {
2161 http_log("could not open %s: %d\n", input_filename, ret);
2164 s->flags |= AVFMT_FLAG_GENPTS;
2166 if (strcmp(s->iformat->name, "ffm") && av_find_stream_info(c->fmt_in) < 0) {
2167 http_log("Could not find stream info '%s'\n", input_filename);
2168 av_close_input_file(s);
2172 /* open each parser */
2173 for(i=0;i<s->nb_streams;i++)
2176 /* choose stream as clock source (we favorize video stream if
2177 present) for packet sending */
2178 c->pts_stream_index = 0;
2179 for(i=0;i<c->stream->nb_streams;i++) {
2180 if (c->pts_stream_index == 0 &&
2181 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2182 c->pts_stream_index = i;
2187 if (c->fmt_in->iformat->read_seek)
2188 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2190 /* set the start time (needed for maxtime and RTP packet timing) */
2191 c->start_time = cur_time;
2192 c->first_pts = AV_NOPTS_VALUE;
2196 /* return the server clock (in us) */
2197 static int64_t get_server_clock(HTTPContext *c)
2199 /* compute current pts value from system time */
2200 return (cur_time - c->start_time) * 1000;
2203 /* return the estimated time at which the current packet must be sent
2205 static int64_t get_packet_send_clock(HTTPContext *c)
2207 int bytes_left, bytes_sent, frame_bytes;
2209 frame_bytes = c->cur_frame_bytes;
2210 if (frame_bytes <= 0)
2213 bytes_left = c->buffer_end - c->buffer_ptr;
2214 bytes_sent = frame_bytes - bytes_left;
2215 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2220 static int http_prepare_data(HTTPContext *c)
2223 AVFormatContext *ctx;
2225 av_freep(&c->pb_buffer);
2227 case HTTPSTATE_SEND_DATA_HEADER:
2228 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2229 av_metadata_set2(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2230 av_metadata_set2(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2231 av_metadata_set2(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2232 av_metadata_set2(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2234 for(i=0;i<c->stream->nb_streams;i++) {
2237 st = av_mallocz(sizeof(AVStream));
2238 c->fmt_ctx.streams[i] = st;
2239 /* if file or feed, then just take streams from FFStream struct */
2240 if (!c->stream->feed ||
2241 c->stream->feed == c->stream)
2242 src = c->stream->streams[i];
2244 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2248 st->codec->frame_number = 0; /* XXX: should be done in
2249 AVStream, not in codec */
2251 /* set output format parameters */
2252 c->fmt_ctx.oformat = c->stream->fmt;
2253 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2255 c->got_key_frame = 0;
2257 /* prepare header and save header data in a stream */
2258 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2259 /* XXX: potential leak */
2262 c->fmt_ctx.pb->is_streamed = 1;
2265 * HACK to avoid mpeg ps muxer to spit many underflow errors
2266 * Default value from FFmpeg
2267 * Try to set it use configuration option
2269 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2270 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2272 av_set_parameters(&c->fmt_ctx, NULL);
2273 if (av_write_header(&c->fmt_ctx) < 0) {
2274 http_log("Error writing output header\n");
2277 av_metadata_free(&c->fmt_ctx.metadata);
2279 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2280 c->buffer_ptr = c->pb_buffer;
2281 c->buffer_end = c->pb_buffer + len;
2283 c->state = HTTPSTATE_SEND_DATA;
2284 c->last_packet_sent = 0;
2286 case HTTPSTATE_SEND_DATA:
2287 /* find a new packet */
2288 /* read a packet from the input stream */
2289 if (c->stream->feed)
2290 ffm_set_write_index(c->fmt_in,
2291 c->stream->feed->feed_write_index,
2292 c->stream->feed->feed_size);
2294 if (c->stream->max_time &&
2295 c->stream->max_time + c->start_time - cur_time < 0)
2296 /* We have timed out */
2297 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2301 ret = av_read_frame(c->fmt_in, &pkt);
2303 if (c->stream->feed) {
2304 /* if coming from feed, it means we reached the end of the
2305 ffm file, so must wait for more data */
2306 c->state = HTTPSTATE_WAIT_FEED;
2307 return 1; /* state changed */
2308 } else if (ret == AVERROR(EAGAIN)) {
2309 /* input not ready, come back later */
2312 if (c->stream->loop) {
2313 av_close_input_file(c->fmt_in);
2315 if (open_input_stream(c, "") < 0)
2320 /* must send trailer now because eof or error */
2321 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2325 int source_index = pkt.stream_index;
2326 /* update first pts if needed */
2327 if (c->first_pts == AV_NOPTS_VALUE) {
2328 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2329 c->start_time = cur_time;
2331 /* send it to the appropriate stream */
2332 if (c->stream->feed) {
2333 /* if coming from a feed, select the right stream */
2334 if (c->switch_pending) {
2335 c->switch_pending = 0;
2336 for(i=0;i<c->stream->nb_streams;i++) {
2337 if (c->switch_feed_streams[i] == pkt.stream_index)
2338 if (pkt.flags & AV_PKT_FLAG_KEY)
2339 c->switch_feed_streams[i] = -1;
2340 if (c->switch_feed_streams[i] >= 0)
2341 c->switch_pending = 1;
2344 for(i=0;i<c->stream->nb_streams;i++) {
2345 if (c->stream->feed_streams[i] == pkt.stream_index) {
2346 AVStream *st = c->fmt_in->streams[source_index];
2347 pkt.stream_index = i;
2348 if (pkt.flags & AV_PKT_FLAG_KEY &&
2349 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2350 c->stream->nb_streams == 1))
2351 c->got_key_frame = 1;
2352 if (!c->stream->send_on_key || c->got_key_frame)
2357 AVCodecContext *codec;
2358 AVStream *ist, *ost;
2360 ist = c->fmt_in->streams[source_index];
2361 /* specific handling for RTP: we use several
2362 output stream (one for each RTP
2363 connection). XXX: need more abstract handling */
2364 if (c->is_packetized) {
2365 /* compute send time and duration */
2366 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2367 c->cur_pts -= c->first_pts;
2368 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2369 /* find RTP context */
2370 c->packet_stream_index = pkt.stream_index;
2371 ctx = c->rtp_ctx[c->packet_stream_index];
2373 av_free_packet(&pkt);
2376 codec = ctx->streams[0]->codec;
2377 /* only one stream per RTP connection */
2378 pkt.stream_index = 0;
2382 codec = ctx->streams[pkt.stream_index]->codec;
2385 if (c->is_packetized) {
2386 int max_packet_size;
2387 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2388 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2390 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2391 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2393 ret = url_open_dyn_buf(&ctx->pb);
2396 /* XXX: potential leak */
2399 ost = ctx->streams[pkt.stream_index];
2401 ctx->pb->is_streamed = 1;
2402 if (pkt.dts != AV_NOPTS_VALUE)
2403 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2404 if (pkt.pts != AV_NOPTS_VALUE)
2405 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2406 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2407 if (av_write_frame(ctx, &pkt) < 0) {
2408 http_log("Error writing frame to output\n");
2409 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2412 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2413 c->cur_frame_bytes = len;
2414 c->buffer_ptr = c->pb_buffer;
2415 c->buffer_end = c->pb_buffer + len;
2417 codec->frame_number++;
2419 av_free_packet(&pkt);
2423 av_free_packet(&pkt);
2428 case HTTPSTATE_SEND_DATA_TRAILER:
2429 /* last packet test ? */
2430 if (c->last_packet_sent || c->is_packetized)
2433 /* prepare header */
2434 if (url_open_dyn_buf(&ctx->pb) < 0) {
2435 /* XXX: potential leak */
2438 c->fmt_ctx.pb->is_streamed = 1;
2439 av_write_trailer(ctx);
2440 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2441 c->buffer_ptr = c->pb_buffer;
2442 c->buffer_end = c->pb_buffer + len;
2444 c->last_packet_sent = 1;
2450 /* should convert the format at the same time */
2451 /* send data starting at c->buffer_ptr to the output connection
2452 (either UDP or TCP connection) */
2453 static int http_send_data(HTTPContext *c)
2458 if (c->buffer_ptr >= c->buffer_end) {
2459 ret = http_prepare_data(c);
2463 /* state change requested */
2466 if (c->is_packetized) {
2467 /* RTP data output */
2468 len = c->buffer_end - c->buffer_ptr;
2470 /* fail safe - should never happen */
2472 c->buffer_ptr = c->buffer_end;
2475 len = (c->buffer_ptr[0] << 24) |
2476 (c->buffer_ptr[1] << 16) |
2477 (c->buffer_ptr[2] << 8) |
2479 if (len > (c->buffer_end - c->buffer_ptr))
2481 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2482 /* nothing to send yet: we can wait */
2486 c->data_count += len;
2487 update_datarate(&c->datarate, c->data_count);
2489 c->stream->bytes_served += len;
2491 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2492 /* RTP packets are sent inside the RTSP TCP connection */
2494 int interleaved_index, size;
2496 HTTPContext *rtsp_c;
2499 /* if no RTSP connection left, error */
2502 /* if already sending something, then wait. */
2503 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2505 if (url_open_dyn_buf(&pb) < 0)
2507 interleaved_index = c->packet_stream_index * 2;
2508 /* RTCP packets are sent at odd indexes */
2509 if (c->buffer_ptr[1] == 200)
2510 interleaved_index++;
2511 /* write RTSP TCP header */
2513 header[1] = interleaved_index;
2514 header[2] = len >> 8;
2516 put_buffer(pb, header, 4);
2517 /* write RTP packet data */
2519 put_buffer(pb, c->buffer_ptr, len);
2520 size = url_close_dyn_buf(pb, &c->packet_buffer);
2521 /* prepare asynchronous TCP sending */
2522 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2523 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2524 c->buffer_ptr += len;
2526 /* send everything we can NOW */
2527 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2528 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2530 rtsp_c->packet_buffer_ptr += len;
2531 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2532 /* if we could not send all the data, we will
2533 send it later, so a new state is needed to
2534 "lock" the RTSP TCP connection */
2535 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2538 /* all data has been sent */
2539 av_freep(&c->packet_buffer);
2541 /* send RTP packet directly in UDP */
2543 url_write(c->rtp_handles[c->packet_stream_index],
2544 c->buffer_ptr, len);
2545 c->buffer_ptr += len;
2546 /* here we continue as we can send several packets per 10 ms slot */
2549 /* TCP data output */
2550 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2552 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2553 ff_neterrno() != FF_NETERROR(EINTR))
2554 /* error : close connection */
2559 c->buffer_ptr += len;
2561 c->data_count += len;
2562 update_datarate(&c->datarate, c->data_count);
2564 c->stream->bytes_served += len;
2572 static int http_start_receive_data(HTTPContext *c)
2576 if (c->stream->feed_opened)
2579 /* Don't permit writing to this one */
2580 if (c->stream->readonly)
2584 fd = open(c->stream->feed_filename, O_RDWR);
2586 http_log("Error opening feeder file: %s\n", strerror(errno));
2591 if (c->stream->truncate) {
2592 /* truncate feed file */
2593 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2594 ftruncate(c->feed_fd, FFM_PACKET_SIZE);
2595 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2597 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2598 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2603 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2604 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2605 lseek(fd, 0, SEEK_SET);
2607 /* init buffer input */
2608 c->buffer_ptr = c->buffer;
2609 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2610 c->stream->feed_opened = 1;
2611 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2615 static int http_receive_data(HTTPContext *c)
2618 int len, loop_run = 0;
2620 while (c->chunked_encoding && !c->chunk_size &&
2621 c->buffer_end > c->buffer_ptr) {
2622 /* read chunk header, if present */
2623 len = recv(c->fd, c->buffer_ptr, 1, 0);
2626 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2627 ff_neterrno() != FF_NETERROR(EINTR))
2628 /* error : close connection */
2631 } else if (len == 0) {
2632 /* end of connection : close it */
2634 } else if (c->buffer_ptr - c->buffer >= 2 &&
2635 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2636 c->chunk_size = strtol(c->buffer, 0, 16);
2637 if (c->chunk_size == 0) // end of stream
2639 c->buffer_ptr = c->buffer;
2641 } else if (++loop_run > 10) {
2642 /* no chunk header, abort */
2649 if (c->buffer_end > c->buffer_ptr) {
2650 len = recv(c->fd, c->buffer_ptr,
2651 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2653 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2654 ff_neterrno() != FF_NETERROR(EINTR))
2655 /* error : close connection */
2657 } else if (len == 0)
2658 /* end of connection : close it */
2661 c->chunk_size -= len;
2662 c->buffer_ptr += len;
2663 c->data_count += len;
2664 update_datarate(&c->datarate, c->data_count);
2668 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2669 if (c->buffer[0] != 'f' ||
2670 c->buffer[1] != 'm') {
2671 http_log("Feed stream has become desynchronized -- disconnecting\n");
2676 if (c->buffer_ptr >= c->buffer_end) {
2677 FFStream *feed = c->stream;
2678 /* a packet has been received : write it in the store, except
2680 if (c->data_count > FFM_PACKET_SIZE) {
2682 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2683 /* XXX: use llseek or url_seek */
2684 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2685 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2686 http_log("Error writing to feed file: %s\n", strerror(errno));
2690 feed->feed_write_index += FFM_PACKET_SIZE;
2691 /* update file size */
2692 if (feed->feed_write_index > c->stream->feed_size)
2693 feed->feed_size = feed->feed_write_index;
2695 /* handle wrap around if max file size reached */
2696 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2697 feed->feed_write_index = FFM_PACKET_SIZE;
2700 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2701 http_log("Error writing index to feed file: %s\n", strerror(errno));
2705 /* wake up any waiting connections */
2706 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2707 if (c1->state == HTTPSTATE_WAIT_FEED &&
2708 c1->stream->feed == c->stream->feed)
2709 c1->state = HTTPSTATE_SEND_DATA;
2712 /* We have a header in our hands that contains useful data */
2713 AVFormatContext *s = NULL;
2715 AVInputFormat *fmt_in;
2718 /* use feed output format name to find corresponding input format */
2719 fmt_in = av_find_input_format(feed->fmt->name);
2723 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2724 pb->is_streamed = 1;
2726 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2731 /* Now we have the actual streams */
2732 if (s->nb_streams != feed->nb_streams) {
2733 av_close_input_stream(s);
2735 http_log("Feed '%s' stream number does not match registered feed\n",
2736 c->stream->feed_filename);
2740 for (i = 0; i < s->nb_streams; i++) {
2741 AVStream *fst = feed->streams[i];
2742 AVStream *st = s->streams[i];
2743 avcodec_copy_context(fst->codec, st->codec);
2746 av_close_input_stream(s);
2749 c->buffer_ptr = c->buffer;
2754 c->stream->feed_opened = 0;
2756 /* wake up any waiting connections to stop waiting for feed */
2757 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2758 if (c1->state == HTTPSTATE_WAIT_FEED &&
2759 c1->stream->feed == c->stream->feed)
2760 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2765 /********************************************************************/
2768 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2775 switch(error_number) {
2776 case RTSP_STATUS_OK:
2779 case RTSP_STATUS_METHOD:
2780 str = "Method Not Allowed";
2782 case RTSP_STATUS_BANDWIDTH:
2783 str = "Not Enough Bandwidth";
2785 case RTSP_STATUS_SESSION:
2786 str = "Session Not Found";
2788 case RTSP_STATUS_STATE:
2789 str = "Method Not Valid in This State";
2791 case RTSP_STATUS_AGGREGATE:
2792 str = "Aggregate operation not allowed";
2794 case RTSP_STATUS_ONLY_AGGREGATE:
2795 str = "Only aggregate operation allowed";
2797 case RTSP_STATUS_TRANSPORT:
2798 str = "Unsupported transport";
2800 case RTSP_STATUS_INTERNAL:
2801 str = "Internal Server Error";
2803 case RTSP_STATUS_SERVICE:
2804 str = "Service Unavailable";
2806 case RTSP_STATUS_VERSION:
2807 str = "RTSP Version not supported";
2810 str = "Unknown Error";
2814 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2815 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2817 /* output GMT time */
2820 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2821 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2824 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2826 rtsp_reply_header(c, error_number);
2827 url_fprintf(c->pb, "\r\n");
2830 static int rtsp_parse_request(HTTPContext *c)
2832 const char *p, *p1, *p2;
2838 RTSPMessageHeader header1, *header = &header1;
2840 c->buffer_ptr[0] = '\0';
2843 get_word(cmd, sizeof(cmd), &p);
2844 get_word(url, sizeof(url), &p);
2845 get_word(protocol, sizeof(protocol), &p);
2847 av_strlcpy(c->method, cmd, sizeof(c->method));
2848 av_strlcpy(c->url, url, sizeof(c->url));
2849 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2851 if (url_open_dyn_buf(&c->pb) < 0) {
2852 /* XXX: cannot do more */
2853 c->pb = NULL; /* safety */
2857 /* check version name */
2858 if (strcmp(protocol, "RTSP/1.0") != 0) {
2859 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2863 /* parse each header line */
2864 memset(header, 0, sizeof(*header));
2865 /* skip to next line */
2866 while (*p != '\n' && *p != '\0')
2870 while (*p != '\0') {
2871 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2875 if (p2 > p && p2[-1] == '\r')
2877 /* skip empty line */
2881 if (len > sizeof(line) - 1)
2882 len = sizeof(line) - 1;
2883 memcpy(line, p, len);
2885 ff_rtsp_parse_line(header, line, NULL, NULL);
2889 /* handle sequence number */
2890 c->seq = header->seq;
2892 if (!strcmp(cmd, "DESCRIBE"))
2893 rtsp_cmd_describe(c, url);
2894 else if (!strcmp(cmd, "OPTIONS"))
2895 rtsp_cmd_options(c, url);
2896 else if (!strcmp(cmd, "SETUP"))
2897 rtsp_cmd_setup(c, url, header);
2898 else if (!strcmp(cmd, "PLAY"))
2899 rtsp_cmd_play(c, url, header);
2900 else if (!strcmp(cmd, "PAUSE"))
2901 rtsp_cmd_pause(c, url, header);
2902 else if (!strcmp(cmd, "TEARDOWN"))
2903 rtsp_cmd_teardown(c, url, header);
2905 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2908 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2909 c->pb = NULL; /* safety */
2911 /* XXX: cannot do more */
2914 c->buffer_ptr = c->pb_buffer;
2915 c->buffer_end = c->pb_buffer + len;
2916 c->state = RTSPSTATE_SEND_REPLY;
2920 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2921 struct in_addr my_ip)
2923 AVFormatContext *avc;
2924 AVStream *avs = NULL;
2927 avc = avformat_alloc_context();
2931 av_metadata_set2(&avc->metadata, "title",
2932 stream->title[0] ? stream->title : "No Title", 0);
2933 avc->nb_streams = stream->nb_streams;
2934 if (stream->is_multicast) {
2935 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2936 inet_ntoa(stream->multicast_ip),
2937 stream->multicast_port, stream->multicast_ttl);
2939 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2942 #if !FF_API_MAX_STREAMS
2943 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2944 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2947 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2948 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2951 for(i = 0; i < stream->nb_streams; i++) {
2952 avc->streams[i] = &avs[i];
2953 avc->streams[i]->codec = stream->streams[i]->codec;
2955 *pbuffer = av_mallocz(2048);
2956 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2959 #if !FF_API_MAX_STREAMS
2960 av_free(avc->streams);
2962 av_metadata_free(&avc->metadata);
2966 return strlen(*pbuffer);
2969 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2971 // rtsp_reply_header(c, RTSP_STATUS_OK);
2972 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2973 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2974 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2975 url_fprintf(c->pb, "\r\n");
2978 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2984 int content_length, len;
2985 struct sockaddr_in my_addr;
2987 /* find which url is asked */
2988 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2993 for(stream = first_stream; stream != NULL; stream = stream->next) {
2994 if (!stream->is_feed &&
2995 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2996 !strcmp(path, stream->filename)) {
3000 /* no stream found */
3001 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3005 /* prepare the media description in sdp format */
3007 /* get the host IP */
3008 len = sizeof(my_addr);
3009 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
3010 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3011 if (content_length < 0) {
3012 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3015 rtsp_reply_header(c, RTSP_STATUS_OK);
3016 url_fprintf(c->pb, "Content-Base: %s/\r\n", url);
3017 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
3018 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
3019 url_fprintf(c->pb, "\r\n");
3020 put_buffer(c->pb, content, content_length);
3024 static HTTPContext *find_rtp_session(const char *session_id)
3028 if (session_id[0] == '\0')
3031 for(c = first_http_ctx; c != NULL; c = c->next) {
3032 if (!strcmp(c->session_id, session_id))
3038 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3040 RTSPTransportField *th;
3043 for(i=0;i<h->nb_transports;i++) {
3044 th = &h->transports[i];
3045 if (th->lower_transport == lower_transport)
3051 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3052 RTSPMessageHeader *h)
3055 int stream_index, rtp_port, rtcp_port;
3060 RTSPTransportField *th;
3061 struct sockaddr_in dest_addr;
3062 RTSPActionServerSetup setup;
3064 /* find which url is asked */
3065 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3070 /* now check each stream */
3071 for(stream = first_stream; stream != NULL; stream = stream->next) {
3072 if (!stream->is_feed &&
3073 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3074 /* accept aggregate filenames only if single stream */
3075 if (!strcmp(path, stream->filename)) {
3076 if (stream->nb_streams != 1) {
3077 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3084 for(stream_index = 0; stream_index < stream->nb_streams;
3086 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3087 stream->filename, stream_index);
3088 if (!strcmp(path, buf))
3093 /* no stream found */
3094 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3098 /* generate session id if needed */
3099 if (h->session_id[0] == '\0')
3100 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3101 av_lfg_get(&random_state), av_lfg_get(&random_state));
3103 /* find rtp session, and create it if none found */
3104 rtp_c = find_rtp_session(h->session_id);
3106 /* always prefer UDP */
3107 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3109 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3111 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3116 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3117 th->lower_transport);
3119 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3123 /* open input stream */
3124 if (open_input_stream(rtp_c, "") < 0) {
3125 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3130 /* test if stream is OK (test needed because several SETUP needs
3131 to be done for a given file) */
3132 if (rtp_c->stream != stream) {
3133 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3137 /* test if stream is already set up */
3138 if (rtp_c->rtp_ctx[stream_index]) {
3139 rtsp_reply_error(c, RTSP_STATUS_STATE);
3143 /* check transport */
3144 th = find_transport(h, rtp_c->rtp_protocol);
3145 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3146 th->client_port_min <= 0)) {
3147 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3151 /* setup default options */
3152 setup.transport_option[0] = '\0';
3153 dest_addr = rtp_c->from_addr;
3154 dest_addr.sin_port = htons(th->client_port_min);
3157 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3158 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3162 /* now everything is OK, so we can send the connection parameters */
3163 rtsp_reply_header(c, RTSP_STATUS_OK);
3165 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3167 switch(rtp_c->rtp_protocol) {
3168 case RTSP_LOWER_TRANSPORT_UDP:
3169 rtp_port = rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3170 rtcp_port = rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3171 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3172 "client_port=%d-%d;server_port=%d-%d",
3173 th->client_port_min, th->client_port_max,
3174 rtp_port, rtcp_port);
3176 case RTSP_LOWER_TRANSPORT_TCP:
3177 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3178 stream_index * 2, stream_index * 2 + 1);
3183 if (setup.transport_option[0] != '\0')
3184 url_fprintf(c->pb, ";%s", setup.transport_option);
3185 url_fprintf(c->pb, "\r\n");
3188 url_fprintf(c->pb, "\r\n");
3192 /* find an rtp connection by using the session ID. Check consistency
3194 static HTTPContext *find_rtp_session_with_url(const char *url,
3195 const char *session_id)
3203 rtp_c = find_rtp_session(session_id);
3207 /* find which url is asked */
3208 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3212 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3213 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3214 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3215 rtp_c->stream->filename, s);
3216 if(!strncmp(path, buf, sizeof(buf))) {
3217 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3224 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3228 rtp_c = find_rtp_session_with_url(url, h->session_id);
3230 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3234 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3235 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3236 rtp_c->state != HTTPSTATE_READY) {
3237 rtsp_reply_error(c, RTSP_STATUS_STATE);
3241 rtp_c->state = HTTPSTATE_SEND_DATA;
3243 /* now everything is OK, so we can send the connection parameters */
3244 rtsp_reply_header(c, RTSP_STATUS_OK);
3246 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3247 url_fprintf(c->pb, "\r\n");
3250 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3254 rtp_c = find_rtp_session_with_url(url, h->session_id);
3256 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3260 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3261 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3262 rtsp_reply_error(c, RTSP_STATUS_STATE);
3266 rtp_c->state = HTTPSTATE_READY;
3267 rtp_c->first_pts = AV_NOPTS_VALUE;
3268 /* now everything is OK, so we can send the connection parameters */
3269 rtsp_reply_header(c, RTSP_STATUS_OK);
3271 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3272 url_fprintf(c->pb, "\r\n");
3275 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3278 char session_id[32];
3280 rtp_c = find_rtp_session_with_url(url, h->session_id);
3282 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3286 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3288 /* abort the session */
3289 close_connection(rtp_c);
3291 /* now everything is OK, so we can send the connection parameters */
3292 rtsp_reply_header(c, RTSP_STATUS_OK);
3294 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3295 url_fprintf(c->pb, "\r\n");
3299 /********************************************************************/
3302 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3303 FFStream *stream, const char *session_id,
3304 enum RTSPLowerTransport rtp_protocol)
3306 HTTPContext *c = NULL;
3307 const char *proto_str;
3309 /* XXX: should output a warning page when coming
3310 close to the connection limit */
3311 if (nb_connections >= nb_max_connections)
3314 /* add a new connection */
3315 c = av_mallocz(sizeof(HTTPContext));
3320 c->poll_entry = NULL;
3321 c->from_addr = *from_addr;
3322 c->buffer_size = IOBUFFER_INIT_SIZE;
3323 c->buffer = av_malloc(c->buffer_size);
3328 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3329 c->state = HTTPSTATE_READY;
3330 c->is_packetized = 1;
3331 c->rtp_protocol = rtp_protocol;
3333 /* protocol is shown in statistics */
3334 switch(c->rtp_protocol) {
3335 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3336 proto_str = "MCAST";
3338 case RTSP_LOWER_TRANSPORT_UDP:
3341 case RTSP_LOWER_TRANSPORT_TCP:
3348 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3349 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3351 current_bandwidth += stream->bandwidth;
3353 c->next = first_http_ctx;
3365 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3366 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3368 static int rtp_new_av_stream(HTTPContext *c,
3369 int stream_index, struct sockaddr_in *dest_addr,
3370 HTTPContext *rtsp_c)
3372 AVFormatContext *ctx;
3375 URLContext *h = NULL;
3377 int max_packet_size;
3379 /* now we can open the relevant output stream */
3380 ctx = avformat_alloc_context();
3383 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3385 st = av_mallocz(sizeof(AVStream));
3388 ctx->nb_streams = 1;
3389 ctx->streams[0] = st;
3391 if (!c->stream->feed ||
3392 c->stream->feed == c->stream)
3393 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3396 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3398 st->priv_data = NULL;
3400 /* build destination RTP address */
3401 ipaddr = inet_ntoa(dest_addr->sin_addr);
3403 switch(c->rtp_protocol) {
3404 case RTSP_LOWER_TRANSPORT_UDP:
3405 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3408 /* XXX: also pass as parameter to function ? */
3409 if (c->stream->is_multicast) {
3411 ttl = c->stream->multicast_ttl;
3414 snprintf(ctx->filename, sizeof(ctx->filename),
3415 "rtp://%s:%d?multicast=1&ttl=%d",
3416 ipaddr, ntohs(dest_addr->sin_port), ttl);
3418 snprintf(ctx->filename, sizeof(ctx->filename),
3419 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3422 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3424 c->rtp_handles[stream_index] = h;
3425 max_packet_size = url_get_max_packet_size(h);
3427 case RTSP_LOWER_TRANSPORT_TCP:
3430 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3436 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3437 ipaddr, ntohs(dest_addr->sin_port),
3438 c->stream->filename, stream_index, c->protocol);
3440 /* normally, no packets should be output here, but the packet size may be checked */
3441 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3442 /* XXX: close stream */
3445 av_set_parameters(ctx, NULL);
3446 if (av_write_header(ctx) < 0) {
3453 url_close_dyn_buf(ctx->pb, &dummy_buf);
3456 c->rtp_ctx[stream_index] = ctx;
3460 /********************************************************************/
3461 /* ffserver initialization */
3463 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3467 fst = av_mallocz(sizeof(AVStream));
3471 fst->codec= avcodec_alloc_context();
3472 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3473 if (codec->extradata_size) {
3474 fst->codec->extradata = av_malloc(codec->extradata_size);
3475 memcpy(fst->codec->extradata, codec->extradata,
3476 codec->extradata_size);
3479 /* live streams must use the actual feed's codec since it may be
3480 * updated later to carry extradata needed by the streams.
3484 fst->priv_data = av_mallocz(sizeof(FeedData));
3485 fst->index = stream->nb_streams;
3486 av_set_pts_info(fst, 33, 1, 90000);
3487 fst->sample_aspect_ratio = (AVRational){0,1};
3488 stream->streams[stream->nb_streams++] = fst;
3492 /* return the stream number in the feed */
3493 static int add_av_stream(FFStream *feed, AVStream *st)
3496 AVCodecContext *av, *av1;
3500 for(i=0;i<feed->nb_streams;i++) {
3501 st = feed->streams[i];
3503 if (av1->codec_id == av->codec_id &&
3504 av1->codec_type == av->codec_type &&
3505 av1->bit_rate == av->bit_rate) {
3507 switch(av->codec_type) {
3508 case AVMEDIA_TYPE_AUDIO:
3509 if (av1->channels == av->channels &&
3510 av1->sample_rate == av->sample_rate)
3513 case AVMEDIA_TYPE_VIDEO:
3514 if (av1->width == av->width &&
3515 av1->height == av->height &&
3516 av1->time_base.den == av->time_base.den &&
3517 av1->time_base.num == av->time_base.num &&
3518 av1->gop_size == av->gop_size)
3527 fst = add_av_stream1(feed, av, 0);
3530 return feed->nb_streams - 1;
3535 static void remove_stream(FFStream *stream)
3539 while (*ps != NULL) {
3547 /* specific mpeg4 handling : we extract the raw parameters */
3548 static void extract_mpeg4_header(AVFormatContext *infile)
3550 int mpeg4_count, i, size;
3556 for(i=0;i<infile->nb_streams;i++) {
3557 st = infile->streams[i];
3558 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3559 st->codec->extradata_size == 0) {
3566 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3567 while (mpeg4_count > 0) {
3568 if (av_read_packet(infile, &pkt) < 0)
3570 st = infile->streams[pkt.stream_index];
3571 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3572 st->codec->extradata_size == 0) {
3573 av_freep(&st->codec->extradata);
3574 /* fill extradata with the header */
3575 /* XXX: we make hard suppositions here ! */
3577 while (p < pkt.data + pkt.size - 4) {
3578 /* stop when vop header is found */
3579 if (p[0] == 0x00 && p[1] == 0x00 &&
3580 p[2] == 0x01 && p[3] == 0xb6) {
3581 size = p - pkt.data;
3582 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3583 st->codec->extradata = av_malloc(size);
3584 st->codec->extradata_size = size;
3585 memcpy(st->codec->extradata, pkt.data, size);
3592 av_free_packet(&pkt);
3596 /* compute the needed AVStream for each file */
3597 static void build_file_streams(void)
3599 FFStream *stream, *stream_next;
3600 AVFormatContext *infile;
3603 /* gather all streams */
3604 for(stream = first_stream; stream != NULL; stream = stream_next) {
3605 stream_next = stream->next;
3606 if (stream->stream_type == STREAM_TYPE_LIVE &&
3608 /* the stream comes from a file */
3609 /* try to open the file */
3611 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3612 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3613 /* specific case : if transport stream output to RTP,
3614 we use a raw transport stream reader */
3615 stream->ap_in->mpeg2ts_raw = 1;
3616 stream->ap_in->mpeg2ts_compute_pcr = 1;
3619 http_log("Opening file '%s'\n", stream->feed_filename);
3620 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3621 stream->ifmt, 0, stream->ap_in)) < 0) {
3622 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3623 /* remove stream (no need to spend more time on it) */
3625 remove_stream(stream);
3627 /* find all the AVStreams inside and reference them in
3629 if (av_find_stream_info(infile) < 0) {
3630 http_log("Could not find codec parameters from '%s'\n",
3631 stream->feed_filename);
3632 av_close_input_file(infile);
3635 extract_mpeg4_header(infile);
3637 for(i=0;i<infile->nb_streams;i++)
3638 add_av_stream1(stream, infile->streams[i]->codec, 1);
3640 av_close_input_file(infile);
3646 /* compute the needed AVStream for each feed */
3647 static void build_feed_streams(void)
3649 FFStream *stream, *feed;
3652 /* gather all streams */
3653 for(stream = first_stream; stream != NULL; stream = stream->next) {
3654 feed = stream->feed;
3656 if (!stream->is_feed) {
3657 /* we handle a stream coming from a feed */
3658 for(i=0;i<stream->nb_streams;i++)
3659 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3664 /* gather all streams */
3665 for(stream = first_stream; stream != NULL; stream = stream->next) {
3666 feed = stream->feed;
3668 if (stream->is_feed) {
3669 for(i=0;i<stream->nb_streams;i++)
3670 stream->feed_streams[i] = i;
3675 /* create feed files if needed */
3676 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3679 if (url_exist(feed->feed_filename)) {
3680 /* See if it matches */
3684 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3685 /* Now see if it matches */
3686 if (s->nb_streams == feed->nb_streams) {
3688 for(i=0;i<s->nb_streams;i++) {
3690 sf = feed->streams[i];
3693 if (sf->index != ss->index ||
3695 http_log("Index & Id do not match for stream %d (%s)\n",
3696 i, feed->feed_filename);
3699 AVCodecContext *ccf, *ccs;
3703 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3705 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3706 http_log("Codecs do not match for stream %d\n", i);
3708 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3709 http_log("Codec bitrates do not match for stream %d\n", i);
3711 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3712 if (CHECK_CODEC(time_base.den) ||
3713 CHECK_CODEC(time_base.num) ||
3714 CHECK_CODEC(width) ||
3715 CHECK_CODEC(height)) {
3716 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3719 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3720 if (CHECK_CODEC(sample_rate) ||
3721 CHECK_CODEC(channels) ||
3722 CHECK_CODEC(frame_size)) {
3723 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3727 http_log("Unknown codec type\n");
3735 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3736 feed->feed_filename, s->nb_streams, feed->nb_streams);
3738 av_close_input_file(s);
3740 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3741 feed->feed_filename);
3744 if (feed->readonly) {
3745 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3746 feed->feed_filename);
3749 unlink(feed->feed_filename);
3752 if (!url_exist(feed->feed_filename)) {
3753 AVFormatContext s1 = {0}, *s = &s1;
3755 if (feed->readonly) {
3756 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3757 feed->feed_filename);
3761 /* only write the header of the ffm file */
3762 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3763 http_log("Could not open output feed file '%s'\n",
3764 feed->feed_filename);
3767 s->oformat = feed->fmt;
3768 s->nb_streams = feed->nb_streams;
3769 for(i=0;i<s->nb_streams;i++) {
3771 st = feed->streams[i];
3774 av_set_parameters(s, NULL);
3775 if (av_write_header(s) < 0) {
3776 http_log("Container doesn't supports the required parameters\n");
3779 /* XXX: need better api */
3780 av_freep(&s->priv_data);
3783 /* get feed size and write index */
3784 fd = open(feed->feed_filename, O_RDONLY);
3786 http_log("Could not open output feed file '%s'\n",
3787 feed->feed_filename);
3791 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3792 feed->feed_size = lseek(fd, 0, SEEK_END);
3793 /* ensure that we do not wrap before the end of file */
3794 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3795 feed->feed_max_size = feed->feed_size;
3801 /* compute the bandwidth used by each stream */
3802 static void compute_bandwidth(void)
3808 for(stream = first_stream; stream != NULL; stream = stream->next) {
3810 for(i=0;i<stream->nb_streams;i++) {
3811 AVStream *st = stream->streams[i];
3812 switch(st->codec->codec_type) {
3813 case AVMEDIA_TYPE_AUDIO:
3814 case AVMEDIA_TYPE_VIDEO:
3815 bandwidth += st->codec->bit_rate;
3821 stream->bandwidth = (bandwidth + 999) / 1000;
3825 /* add a codec and set the default parameters */
3826 static void add_codec(FFStream *stream, AVCodecContext *av)
3830 /* compute default parameters */
3831 switch(av->codec_type) {
3832 case AVMEDIA_TYPE_AUDIO:
3833 if (av->bit_rate == 0)
3834 av->bit_rate = 64000;
3835 if (av->sample_rate == 0)
3836 av->sample_rate = 22050;
3837 if (av->channels == 0)
3840 case AVMEDIA_TYPE_VIDEO:
3841 if (av->bit_rate == 0)
3842 av->bit_rate = 64000;
3843 if (av->time_base.num == 0){
3844 av->time_base.den = 5;
3845 av->time_base.num = 1;
3847 if (av->width == 0 || av->height == 0) {
3851 /* Bitrate tolerance is less for streaming */
3852 if (av->bit_rate_tolerance == 0)
3853 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3854 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3859 if (av->max_qdiff == 0)
3861 av->qcompress = 0.5;
3864 if (!av->nsse_weight)
3865 av->nsse_weight = 8;
3867 av->frame_skip_cmp = FF_CMP_DCTMAX;
3869 av->me_method = ME_EPZS;
3870 av->rc_buffer_aggressivity = 1.0;
3873 av->rc_eq = "tex^qComp";
3874 if (!av->i_quant_factor)
3875 av->i_quant_factor = -0.8;
3876 if (!av->b_quant_factor)
3877 av->b_quant_factor = 1.25;
3878 if (!av->b_quant_offset)
3879 av->b_quant_offset = 1.25;
3880 if (!av->rc_max_rate)
3881 av->rc_max_rate = av->bit_rate * 2;
3883 if (av->rc_max_rate && !av->rc_buffer_size) {
3884 av->rc_buffer_size = av->rc_max_rate;
3893 st = av_mallocz(sizeof(AVStream));
3896 st->codec = avcodec_alloc_context();
3897 stream->streams[stream->nb_streams++] = st;
3898 memcpy(st->codec, av, sizeof(AVCodecContext));
3901 static enum CodecID opt_audio_codec(const char *arg)
3903 AVCodec *p= avcodec_find_encoder_by_name(arg);
3905 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3906 return CODEC_ID_NONE;
3911 static enum CodecID opt_video_codec(const char *arg)
3913 AVCodec *p= avcodec_find_encoder_by_name(arg);
3915 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3916 return CODEC_ID_NONE;
3921 /* simplistic plugin support */
3924 static void load_module(const char *filename)
3927 void (*init_func)(void);
3928 dll = dlopen(filename, RTLD_NOW);
3930 fprintf(stderr, "Could not load module '%s' - %s\n",
3931 filename, dlerror());
3935 init_func = dlsym(dll, "ffserver_module_init");
3938 "%s: init function 'ffserver_module_init()' not found\n",
3947 static int ffserver_opt_default(const char *opt, const char *arg,
3948 AVCodecContext *avctx, int type)
3951 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
3953 ret = av_set_string3(avctx, opt, arg, 1, NULL);
3957 static int ffserver_opt_preset(const char *arg,
3958 AVCodecContext *avctx, int type,
3959 enum CodecID *audio_id, enum CodecID *video_id)
3962 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3964 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3966 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3967 codec ? codec->name : NULL))) {
3968 fprintf(stderr, "File for preset '%s' not found\n", arg);
3973 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3974 if(line[0] == '#' && !e)
3976 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3978 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3982 if(!strcmp(tmp, "acodec")){
3983 *audio_id = opt_audio_codec(tmp2);
3984 }else if(!strcmp(tmp, "vcodec")){
3985 *video_id = opt_video_codec(tmp2);
3986 }else if(!strcmp(tmp, "scodec")){
3987 /* opt_subtitle_codec(tmp2); */
3988 }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
3989 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
4000 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
4001 const char *mime_type)
4003 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
4006 AVOutputFormat *stream_fmt;
4007 char stream_format_name[64];
4009 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
4010 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4019 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
4023 fprintf(stderr, "%s:%d: ", filename, line_num);
4024 vfprintf(stderr, fmt, vl);
4030 static int parse_ffconfig(const char *filename)
4037 int val, errors, line_num;
4038 FFStream **last_stream, *stream, *redirect;
4039 FFStream **last_feed, *feed, *s;
4040 AVCodecContext audio_enc, video_enc;
4041 enum CodecID audio_id, video_id;
4043 f = fopen(filename, "r");
4051 first_stream = NULL;
4052 last_stream = &first_stream;
4054 last_feed = &first_feed;
4058 audio_id = CODEC_ID_NONE;
4059 video_id = CODEC_ID_NONE;
4061 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4063 if (fgets(line, sizeof(line), f) == NULL)
4069 if (*p == '\0' || *p == '#')
4072 get_arg(cmd, sizeof(cmd), &p);
4074 if (!strcasecmp(cmd, "Port")) {
4075 get_arg(arg, sizeof(arg), &p);
4077 if (val < 1 || val > 65536) {
4078 ERROR("Invalid_port: %s\n", arg);
4080 my_http_addr.sin_port = htons(val);
4081 } else if (!strcasecmp(cmd, "BindAddress")) {
4082 get_arg(arg, sizeof(arg), &p);
4083 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4084 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4086 } else if (!strcasecmp(cmd, "NoDaemon")) {
4087 ffserver_daemon = 0;
4088 } else if (!strcasecmp(cmd, "RTSPPort")) {
4089 get_arg(arg, sizeof(arg), &p);
4091 if (val < 1 || val > 65536) {
4092 ERROR("%s:%d: Invalid port: %s\n", arg);
4094 my_rtsp_addr.sin_port = htons(atoi(arg));
4095 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
4096 get_arg(arg, sizeof(arg), &p);
4097 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4098 ERROR("Invalid host/IP address: %s\n", arg);
4100 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
4101 get_arg(arg, sizeof(arg), &p);
4103 if (val < 1 || val > 65536) {
4104 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4106 nb_max_http_connections = val;
4107 } else if (!strcasecmp(cmd, "MaxClients")) {
4108 get_arg(arg, sizeof(arg), &p);
4110 if (val < 1 || val > nb_max_http_connections) {
4111 ERROR("Invalid MaxClients: %s\n", arg);
4113 nb_max_connections = val;
4115 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
4117 get_arg(arg, sizeof(arg), &p);
4119 if (llval < 10 || llval > 10000000) {
4120 ERROR("Invalid MaxBandwidth: %s\n", arg);
4122 max_bandwidth = llval;
4123 } else if (!strcasecmp(cmd, "CustomLog")) {
4124 if (!ffserver_debug)
4125 get_arg(logfilename, sizeof(logfilename), &p);
4126 } else if (!strcasecmp(cmd, "<Feed")) {
4127 /*********************************************/
4128 /* Feed related options */
4130 if (stream || feed) {
4131 ERROR("Already in a tag\n");
4133 feed = av_mallocz(sizeof(FFStream));
4134 get_arg(feed->filename, sizeof(feed->filename), &p);
4135 q = strrchr(feed->filename, '>');
4139 for (s = first_feed; s; s = s->next) {
4140 if (!strcmp(feed->filename, s->filename)) {
4141 ERROR("Feed '%s' already registered\n", s->filename);
4145 feed->fmt = av_guess_format("ffm", NULL, NULL);
4146 /* defaut feed file */
4147 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4148 "/tmp/%s.ffm", feed->filename);
4149 feed->feed_max_size = 5 * 1024 * 1024;
4151 feed->feed = feed; /* self feeding :-) */
4153 /* add in stream list */
4154 *last_stream = feed;
4155 last_stream = &feed->next;
4156 /* add in feed list */
4158 last_feed = &feed->next_feed;
4160 } else if (!strcasecmp(cmd, "Launch")) {
4164 feed->child_argv = av_mallocz(64 * sizeof(char *));
4166 for (i = 0; i < 62; i++) {
4167 get_arg(arg, sizeof(arg), &p);
4171 feed->child_argv[i] = av_strdup(arg);
4174 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4176 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4178 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4179 inet_ntoa(my_http_addr.sin_addr),
4180 ntohs(my_http_addr.sin_port), feed->filename);
4182 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
4184 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4186 } else if (stream) {
4187 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4189 } else if (!strcasecmp(cmd, "File")) {
4191 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4193 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4194 } else if (!strcasecmp(cmd, "Truncate")) {
4196 get_arg(arg, sizeof(arg), &p);
4197 feed->truncate = strtod(arg, NULL);
4199 } else if (!strcasecmp(cmd, "FileMaxSize")) {
4204 get_arg(arg, sizeof(arg), &p);
4206 fsize = strtod(p1, &p1);
4207 switch(toupper(*p1)) {
4212 fsize *= 1024 * 1024;
4215 fsize *= 1024 * 1024 * 1024;
4218 feed->feed_max_size = (int64_t)fsize;
4219 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4220 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4223 } else if (!strcasecmp(cmd, "</Feed>")) {
4225 ERROR("No corresponding <Feed> for </Feed>\n");
4228 } else if (!strcasecmp(cmd, "<Stream")) {
4229 /*********************************************/
4230 /* Stream related options */
4232 if (stream || feed) {
4233 ERROR("Already in a tag\n");
4236 stream = av_mallocz(sizeof(FFStream));
4237 get_arg(stream->filename, sizeof(stream->filename), &p);
4238 q = strrchr(stream->filename, '>');
4242 for (s = first_stream; s; s = s->next) {
4243 if (!strcmp(stream->filename, s->filename)) {
4244 ERROR("Stream '%s' already registered\n", s->filename);
4248 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4249 avcodec_get_context_defaults2(&video_enc, AVMEDIA_TYPE_VIDEO);
4250 avcodec_get_context_defaults2(&audio_enc, AVMEDIA_TYPE_AUDIO);
4251 audio_id = CODEC_ID_NONE;
4252 video_id = CODEC_ID_NONE;
4254 audio_id = stream->fmt->audio_codec;
4255 video_id = stream->fmt->video_codec;
4258 *last_stream = stream;
4259 last_stream = &stream->next;
4261 } else if (!strcasecmp(cmd, "Feed")) {
4262 get_arg(arg, sizeof(arg), &p);
4267 while (sfeed != NULL) {
4268 if (!strcmp(sfeed->filename, arg))
4270 sfeed = sfeed->next_feed;
4273 ERROR("feed '%s' not defined\n", arg);
4275 stream->feed = sfeed;
4277 } else if (!strcasecmp(cmd, "Format")) {
4278 get_arg(arg, sizeof(arg), &p);
4280 if (!strcmp(arg, "status")) {
4281 stream->stream_type = STREAM_TYPE_STATUS;
4284 stream->stream_type = STREAM_TYPE_LIVE;
4285 /* jpeg cannot be used here, so use single frame jpeg */
4286 if (!strcmp(arg, "jpeg"))
4287 strcpy(arg, "mjpeg");
4288 stream->fmt = ffserver_guess_format(arg, NULL, NULL);
4290 ERROR("Unknown Format: %s\n", arg);
4294 audio_id = stream->fmt->audio_codec;
4295 video_id = stream->fmt->video_codec;
4298 } else if (!strcasecmp(cmd, "InputFormat")) {
4299 get_arg(arg, sizeof(arg), &p);
4301 stream->ifmt = av_find_input_format(arg);
4302 if (!stream->ifmt) {
4303 ERROR("Unknown input format: %s\n", arg);
4306 } else if (!strcasecmp(cmd, "FaviconURL")) {
4307 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4308 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4310 ERROR("FaviconURL only permitted for status streams\n");
4312 } else if (!strcasecmp(cmd, "Author")) {
4314 get_arg(stream->author, sizeof(stream->author), &p);
4315 } else if (!strcasecmp(cmd, "Comment")) {
4317 get_arg(stream->comment, sizeof(stream->comment), &p);
4318 } else if (!strcasecmp(cmd, "Copyright")) {
4320 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4321 } else if (!strcasecmp(cmd, "Title")) {
4323 get_arg(stream->title, sizeof(stream->title), &p);
4324 } else if (!strcasecmp(cmd, "Preroll")) {
4325 get_arg(arg, sizeof(arg), &p);
4327 stream->prebuffer = atof(arg) * 1000;
4328 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4330 stream->send_on_key = 1;
4331 } else if (!strcasecmp(cmd, "AudioCodec")) {
4332 get_arg(arg, sizeof(arg), &p);
4333 audio_id = opt_audio_codec(arg);
4334 if (audio_id == CODEC_ID_NONE) {
4335 ERROR("Unknown AudioCodec: %s\n", arg);
4337 } else if (!strcasecmp(cmd, "VideoCodec")) {
4338 get_arg(arg, sizeof(arg), &p);
4339 video_id = opt_video_codec(arg);
4340 if (video_id == CODEC_ID_NONE) {
4341 ERROR("Unknown VideoCodec: %s\n", arg);
4343 } else if (!strcasecmp(cmd, "MaxTime")) {
4344 get_arg(arg, sizeof(arg), &p);
4346 stream->max_time = atof(arg) * 1000;
4347 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4348 get_arg(arg, sizeof(arg), &p);
4350 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4351 } else if (!strcasecmp(cmd, "AudioChannels")) {
4352 get_arg(arg, sizeof(arg), &p);
4354 audio_enc.channels = atoi(arg);
4355 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4356 get_arg(arg, sizeof(arg), &p);
4358 audio_enc.sample_rate = atoi(arg);
4359 } else if (!strcasecmp(cmd, "AudioQuality")) {
4360 get_arg(arg, sizeof(arg), &p);
4362 // audio_enc.quality = atof(arg) * 1000;
4364 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4366 int minrate, maxrate;
4368 get_arg(arg, sizeof(arg), &p);
4370 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4371 video_enc.rc_min_rate = minrate * 1000;
4372 video_enc.rc_max_rate = maxrate * 1000;
4374 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4377 } else if (!strcasecmp(cmd, "Debug")) {
4379 get_arg(arg, sizeof(arg), &p);
4380 video_enc.debug = strtol(arg,0,0);
4382 } else if (!strcasecmp(cmd, "Strict")) {
4384 get_arg(arg, sizeof(arg), &p);
4385 video_enc.strict_std_compliance = atoi(arg);
4387 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4389 get_arg(arg, sizeof(arg), &p);
4390 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4392 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4394 get_arg(arg, sizeof(arg), &p);
4395 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4397 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4398 get_arg(arg, sizeof(arg), &p);
4400 video_enc.bit_rate = atoi(arg) * 1000;
4402 } else if (!strcasecmp(cmd, "VideoSize")) {
4403 get_arg(arg, sizeof(arg), &p);
4405 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4406 if ((video_enc.width % 16) != 0 ||
4407 (video_enc.height % 16) != 0) {
4408 ERROR("Image size must be a multiple of 16\n");
4411 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4412 get_arg(arg, sizeof(arg), &p);
4414 AVRational frame_rate;
4415 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4416 ERROR("Incorrect frame rate: %s\n", arg);
4418 video_enc.time_base.num = frame_rate.den;
4419 video_enc.time_base.den = frame_rate.num;
4422 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4423 get_arg(arg, sizeof(arg), &p);
4425 video_enc.gop_size = atoi(arg);
4426 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4428 video_enc.gop_size = 1;
4429 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4431 video_enc.mb_decision = FF_MB_DECISION_BITS;
4432 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4434 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4435 video_enc.flags |= CODEC_FLAG_4MV;
4437 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4438 !strcasecmp(cmd, "AVOptionAudio")) {
4440 AVCodecContext *avctx;
4442 get_arg(arg, sizeof(arg), &p);
4443 get_arg(arg2, sizeof(arg2), &p);
4444 if (!strcasecmp(cmd, "AVOptionVideo")) {
4446 type = AV_OPT_FLAG_VIDEO_PARAM;
4449 type = AV_OPT_FLAG_AUDIO_PARAM;
4451 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4452 ERROR("AVOption error: %s %s\n", arg, arg2);
4454 } else if (!strcasecmp(cmd, "AVPresetVideo") ||
4455 !strcasecmp(cmd, "AVPresetAudio")) {
4456 AVCodecContext *avctx;
4458 get_arg(arg, sizeof(arg), &p);
4459 if (!strcasecmp(cmd, "AVPresetVideo")) {
4461 video_enc.codec_id = video_id;
4462 type = AV_OPT_FLAG_VIDEO_PARAM;
4465 audio_enc.codec_id = audio_id;
4466 type = AV_OPT_FLAG_AUDIO_PARAM;
4468 if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4469 ERROR("AVPreset error: %s\n", arg);
4471 } else if (!strcasecmp(cmd, "VideoTag")) {
4472 get_arg(arg, sizeof(arg), &p);
4473 if ((strlen(arg) == 4) && stream)
4474 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4475 } else if (!strcasecmp(cmd, "BitExact")) {
4477 video_enc.flags |= CODEC_FLAG_BITEXACT;
4478 } else if (!strcasecmp(cmd, "DctFastint")) {
4480 video_enc.dct_algo = FF_DCT_FASTINT;
4481 } else if (!strcasecmp(cmd, "IdctSimple")) {
4483 video_enc.idct_algo = FF_IDCT_SIMPLE;
4484 } else if (!strcasecmp(cmd, "Qscale")) {
4485 get_arg(arg, sizeof(arg), &p);
4487 video_enc.flags |= CODEC_FLAG_QSCALE;
4488 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4490 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4491 get_arg(arg, sizeof(arg), &p);
4493 video_enc.max_qdiff = atoi(arg);
4494 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4495 ERROR("VideoQDiff out of range\n");
4498 } else if (!strcasecmp(cmd, "VideoQMax")) {
4499 get_arg(arg, sizeof(arg), &p);
4501 video_enc.qmax = atoi(arg);
4502 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4503 ERROR("VideoQMax out of range\n");
4506 } else if (!strcasecmp(cmd, "VideoQMin")) {
4507 get_arg(arg, sizeof(arg), &p);
4509 video_enc.qmin = atoi(arg);
4510 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4511 ERROR("VideoQMin out of range\n");
4514 } else if (!strcasecmp(cmd, "LumaElim")) {
4515 get_arg(arg, sizeof(arg), &p);
4517 video_enc.luma_elim_threshold = atoi(arg);
4518 } else if (!strcasecmp(cmd, "ChromaElim")) {
4519 get_arg(arg, sizeof(arg), &p);
4521 video_enc.chroma_elim_threshold = atoi(arg);
4522 } else if (!strcasecmp(cmd, "LumiMask")) {
4523 get_arg(arg, sizeof(arg), &p);
4525 video_enc.lumi_masking = atof(arg);
4526 } else if (!strcasecmp(cmd, "DarkMask")) {
4527 get_arg(arg, sizeof(arg), &p);
4529 video_enc.dark_masking = atof(arg);
4530 } else if (!strcasecmp(cmd, "NoVideo")) {
4531 video_id = CODEC_ID_NONE;
4532 } else if (!strcasecmp(cmd, "NoAudio")) {
4533 audio_id = CODEC_ID_NONE;
4534 } else if (!strcasecmp(cmd, "ACL")) {
4535 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4536 } else if (!strcasecmp(cmd, "DynamicACL")) {
4538 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4540 } else if (!strcasecmp(cmd, "RTSPOption")) {
4541 get_arg(arg, sizeof(arg), &p);
4543 av_freep(&stream->rtsp_option);
4544 stream->rtsp_option = av_strdup(arg);
4546 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4547 get_arg(arg, sizeof(arg), &p);
4549 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4550 ERROR("Invalid host/IP address: %s\n", arg);
4552 stream->is_multicast = 1;
4553 stream->loop = 1; /* default is looping */
4555 } else if (!strcasecmp(cmd, "MulticastPort")) {
4556 get_arg(arg, sizeof(arg), &p);
4558 stream->multicast_port = atoi(arg);
4559 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4560 get_arg(arg, sizeof(arg), &p);
4562 stream->multicast_ttl = atoi(arg);
4563 } else if (!strcasecmp(cmd, "NoLoop")) {
4566 } else if (!strcasecmp(cmd, "</Stream>")) {
4568 ERROR("No corresponding <Stream> for </Stream>\n");
4570 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4571 if (audio_id != CODEC_ID_NONE) {
4572 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4573 audio_enc.codec_id = audio_id;
4574 add_codec(stream, &audio_enc);
4576 if (video_id != CODEC_ID_NONE) {
4577 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4578 video_enc.codec_id = video_id;
4579 add_codec(stream, &video_enc);
4584 } else if (!strcasecmp(cmd, "<Redirect")) {
4585 /*********************************************/
4587 if (stream || feed || redirect) {
4588 ERROR("Already in a tag\n");
4590 redirect = av_mallocz(sizeof(FFStream));
4591 *last_stream = redirect;
4592 last_stream = &redirect->next;
4594 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4595 q = strrchr(redirect->filename, '>');
4598 redirect->stream_type = STREAM_TYPE_REDIRECT;
4600 } else if (!strcasecmp(cmd, "URL")) {
4602 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4603 } else if (!strcasecmp(cmd, "</Redirect>")) {
4605 ERROR("No corresponding <Redirect> for </Redirect>\n");
4607 if (!redirect->feed_filename[0]) {
4608 ERROR("No URL found for <Redirect>\n");
4612 } else if (!strcasecmp(cmd, "LoadModule")) {
4613 get_arg(arg, sizeof(arg), &p);
4617 ERROR("Module support not compiled into this version: '%s'\n", arg);
4620 ERROR("Incorrect keyword: '%s'\n", cmd);
4632 static void handle_child_exit(int sig)
4637 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4640 for (feed = first_feed; feed; feed = feed->next) {
4641 if (feed->pid == pid) {
4642 int uptime = time(0) - feed->pid_start;
4645 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4648 /* Turn off any more restarts */
4649 feed->child_argv = 0;
4654 need_to_start_children = 1;
4657 static void opt_debug(void)
4660 ffserver_daemon = 0;
4661 logfilename[0] = '-';
4664 static void show_help(void)
4666 printf("usage: ffserver [options]\n"
4667 "Hyper fast multi format Audio/Video streaming server\n");
4669 show_help_options(options, "Main options:\n", 0, 0);
4672 static const OptionDef options[] = {
4673 #include "cmdutils_common_opts.h"
4674 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4675 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4676 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4680 int main(int argc, char **argv)
4682 struct sigaction sigact;
4688 my_program_name = argv[0];
4689 my_program_dir = getcwd(0, 0);
4690 ffserver_daemon = 1;
4692 parse_options(argc, argv, options, NULL);
4694 unsetenv("http_proxy"); /* Kill the http_proxy */
4696 av_lfg_init(&random_state, av_get_random_seed());
4698 memset(&sigact, 0, sizeof(sigact));
4699 sigact.sa_handler = handle_child_exit;
4700 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4701 sigaction(SIGCHLD, &sigact, 0);
4703 if (parse_ffconfig(config_filename) < 0) {
4704 fprintf(stderr, "Incorrect config file - exiting.\n");
4708 /* open log file if needed */
4709 if (logfilename[0] != '\0') {
4710 if (!strcmp(logfilename, "-"))
4713 logfile = fopen(logfilename, "a");
4714 av_log_set_callback(http_av_log);
4717 build_file_streams();
4719 build_feed_streams();
4721 compute_bandwidth();
4723 /* put the process in background and detach it from its TTY */
4724 if (ffserver_daemon) {
4731 } else if (pid > 0) {
4738 open("/dev/null", O_RDWR);
4739 if (strcmp(logfilename, "-") != 0) {
4749 signal(SIGPIPE, SIG_IGN);
4751 if (ffserver_daemon)
4754 if (http_server() < 0) {
4755 http_log("Could not start server\n");