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;
1196 static void do_switch_stream(HTTPContext *c, int i)
1198 if (c->switch_feed_streams[i] >= 0) {
1200 c->feed_streams[i] = c->switch_feed_streams[i];
1203 /* Now update the stream */
1205 c->switch_feed_streams[i] = -1;
1208 /* XXX: factorize in utils.c ? */
1209 /* XXX: take care with different space meaning */
1210 static void skip_spaces(const char **pp)
1214 while (*p == ' ' || *p == '\t')
1219 static void get_word(char *buf, int buf_size, const char **pp)
1227 while (!isspace(*p) && *p != '\0') {
1228 if ((q - buf) < buf_size - 1)
1237 static void get_arg(char *buf, int buf_size, const char **pp)
1244 while (isspace(*p)) p++;
1247 if (*p == '\"' || *p == '\'')
1259 if ((q - buf) < buf_size - 1)
1264 if (quote && *p == quote)
1269 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1270 const char *p, const char *filename, int line_num)
1276 get_arg(arg, sizeof(arg), &p);
1277 if (strcasecmp(arg, "allow") == 0)
1278 acl.action = IP_ALLOW;
1279 else if (strcasecmp(arg, "deny") == 0)
1280 acl.action = IP_DENY;
1282 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1283 filename, line_num, arg);
1287 get_arg(arg, sizeof(arg), &p);
1289 if (resolve_host(&acl.first, arg) != 0) {
1290 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1291 filename, line_num, arg);
1294 acl.last = acl.first;
1296 get_arg(arg, sizeof(arg), &p);
1299 if (resolve_host(&acl.last, arg) != 0) {
1300 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1301 filename, line_num, arg);
1307 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1308 IPAddressACL **naclp = 0;
1314 naclp = &stream->acl;
1320 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1321 filename, line_num);
1327 naclp = &(*naclp)->next;
1335 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1340 IPAddressACL *acl = NULL;
1344 f = fopen(stream->dynamic_acl, "r");
1346 perror(stream->dynamic_acl);
1350 acl = av_mallocz(sizeof(IPAddressACL));
1354 if (fgets(line, sizeof(line), f) == NULL)
1360 if (*p == '\0' || *p == '#')
1362 get_arg(cmd, sizeof(cmd), &p);
1364 if (!strcasecmp(cmd, "ACL"))
1365 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1372 static void free_acl_list(IPAddressACL *in_acl)
1374 IPAddressACL *pacl,*pacl2;
1384 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1386 enum IPAddressAction last_action = IP_DENY;
1388 struct in_addr *src = &c->from_addr.sin_addr;
1389 unsigned long src_addr = src->s_addr;
1391 for (acl = in_acl; acl; acl = acl->next) {
1392 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1393 return (acl->action == IP_ALLOW) ? 1 : 0;
1394 last_action = acl->action;
1397 /* Nothing matched, so return not the last action */
1398 return (last_action == IP_DENY) ? 1 : 0;
1401 static int validate_acl(FFStream *stream, HTTPContext *c)
1407 /* if stream->acl is null validate_acl_list will return 1 */
1408 ret = validate_acl_list(stream->acl, c);
1410 if (stream->dynamic_acl[0]) {
1411 acl = parse_dynamic_acl(stream, c);
1413 ret = validate_acl_list(acl, c);
1421 /* compute the real filename of a file by matching it without its
1422 extensions to all the stream filenames */
1423 static void compute_real_filename(char *filename, int max_size)
1430 /* compute filename by matching without the file extensions */
1431 av_strlcpy(file1, filename, sizeof(file1));
1432 p = strrchr(file1, '.');
1435 for(stream = first_stream; stream != NULL; stream = stream->next) {
1436 av_strlcpy(file2, stream->filename, sizeof(file2));
1437 p = strrchr(file2, '.');
1440 if (!strcmp(file1, file2)) {
1441 av_strlcpy(filename, stream->filename, max_size);
1456 /* parse http request and prepare header */
1457 static int http_parse_request(HTTPContext *c)
1460 enum RedirType redir_type;
1462 char info[1024], filename[1024];
1466 const char *mime_type;
1470 char *useragent = 0;
1473 get_word(cmd, sizeof(cmd), (const char **)&p);
1474 av_strlcpy(c->method, cmd, sizeof(c->method));
1476 if (!strcmp(cmd, "GET"))
1478 else if (!strcmp(cmd, "POST"))
1483 get_word(url, sizeof(url), (const char **)&p);
1484 av_strlcpy(c->url, url, sizeof(c->url));
1486 get_word(protocol, sizeof(protocol), (const char **)&p);
1487 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1490 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1493 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1495 /* find the filename and the optional info string in the request */
1496 p = strchr(url, '?');
1498 av_strlcpy(info, p, sizeof(info));
1503 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1505 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1506 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1508 if (*useragent && *useragent != '\n' && isspace(*useragent))
1512 p = strchr(p, '\n');
1519 redir_type = REDIR_NONE;
1520 if (av_match_ext(filename, "asx")) {
1521 redir_type = REDIR_ASX;
1522 filename[strlen(filename)-1] = 'f';
1523 } else if (av_match_ext(filename, "asf") &&
1524 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1525 /* if this isn't WMP or lookalike, return the redirector file */
1526 redir_type = REDIR_ASF;
1527 } else if (av_match_ext(filename, "rpm,ram")) {
1528 redir_type = REDIR_RAM;
1529 strcpy(filename + strlen(filename)-2, "m");
1530 } else if (av_match_ext(filename, "rtsp")) {
1531 redir_type = REDIR_RTSP;
1532 compute_real_filename(filename, sizeof(filename) - 1);
1533 } else if (av_match_ext(filename, "sdp")) {
1534 redir_type = REDIR_SDP;
1535 compute_real_filename(filename, sizeof(filename) - 1);
1538 // "redirect" / request to index.html
1539 if (!strlen(filename))
1540 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1542 stream = first_stream;
1543 while (stream != NULL) {
1544 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1546 stream = stream->next;
1548 if (stream == NULL) {
1549 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1550 http_log("File '%s' not found\n", url);
1555 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1556 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1558 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1559 c->http_error = 301;
1561 q += snprintf(q, c->buffer_size,
1562 "HTTP/1.0 301 Moved\r\n"
1564 "Content-type: text/html\r\n"
1566 "<html><head><title>Moved</title></head><body>\r\n"
1567 "You should be <a href=\"%s\">redirected</a>.\r\n"
1568 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1569 /* prepare output buffer */
1570 c->buffer_ptr = c->buffer;
1572 c->state = HTTPSTATE_SEND_HEADER;
1576 /* If this is WMP, get the rate information */
1577 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1578 if (modify_current_stream(c, ratebuf)) {
1579 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1580 if (c->switch_feed_streams[i] >= 0)
1581 do_switch_stream(c, i);
1586 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1587 current_bandwidth += stream->bandwidth;
1589 /* If already streaming this feed, do not let start another feeder. */
1590 if (stream->feed_opened) {
1591 snprintf(msg, sizeof(msg), "This feed is already being received.");
1592 http_log("Feed '%s' already being received\n", stream->feed_filename);
1596 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1597 c->http_error = 503;
1599 q += snprintf(q, c->buffer_size,
1600 "HTTP/1.0 503 Server too busy\r\n"
1601 "Content-type: text/html\r\n"
1603 "<html><head><title>Too busy</title></head><body>\r\n"
1604 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1605 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1606 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1607 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1608 /* prepare output buffer */
1609 c->buffer_ptr = c->buffer;
1611 c->state = HTTPSTATE_SEND_HEADER;
1615 if (redir_type != REDIR_NONE) {
1618 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1619 if (strncasecmp(p, "Host:", 5) == 0) {
1623 p = strchr(p, '\n');
1634 while (isspace(*hostinfo))
1637 eoh = strchr(hostinfo, '\n');
1639 if (eoh[-1] == '\r')
1642 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1643 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1644 hostbuf[eoh - hostinfo] = 0;
1646 c->http_error = 200;
1648 switch(redir_type) {
1650 q += snprintf(q, c->buffer_size,
1651 "HTTP/1.0 200 ASX Follows\r\n"
1652 "Content-type: video/x-ms-asf\r\n"
1654 "<ASX Version=\"3\">\r\n"
1655 //"<!-- Autogenerated by ffserver -->\r\n"
1656 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1657 "</ASX>\r\n", hostbuf, filename, info);
1660 q += snprintf(q, c->buffer_size,
1661 "HTTP/1.0 200 RAM Follows\r\n"
1662 "Content-type: audio/x-pn-realaudio\r\n"
1664 "# Autogenerated by ffserver\r\n"
1665 "http://%s/%s%s\r\n", hostbuf, filename, info);
1668 q += snprintf(q, c->buffer_size,
1669 "HTTP/1.0 200 ASF Redirect follows\r\n"
1670 "Content-type: video/x-ms-asf\r\n"
1673 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1677 char hostname[256], *p;
1678 /* extract only hostname */
1679 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1680 p = strrchr(hostname, ':');
1683 q += snprintf(q, c->buffer_size,
1684 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1685 /* XXX: incorrect mime type ? */
1686 "Content-type: application/x-rtsp\r\n"
1688 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1694 int sdp_data_size, len;
1695 struct sockaddr_in my_addr;
1697 q += snprintf(q, c->buffer_size,
1698 "HTTP/1.0 200 OK\r\n"
1699 "Content-type: application/sdp\r\n"
1702 len = sizeof(my_addr);
1703 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1705 /* XXX: should use a dynamic buffer */
1706 sdp_data_size = prepare_sdp_description(stream,
1709 if (sdp_data_size > 0) {
1710 memcpy(q, sdp_data, sdp_data_size);
1722 /* prepare output buffer */
1723 c->buffer_ptr = c->buffer;
1725 c->state = HTTPSTATE_SEND_HEADER;
1731 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1735 stream->conns_served++;
1737 /* XXX: add there authenticate and IP match */
1740 /* if post, it means a feed is being sent */
1741 if (!stream->is_feed) {
1742 /* However it might be a status report from WMP! Let us log the
1743 * data as it might come in handy one day. */
1747 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1748 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1752 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1753 client_id = strtol(p + 18, 0, 10);
1754 p = strchr(p, '\n');
1762 char *eol = strchr(logline, '\n');
1767 if (eol[-1] == '\r')
1769 http_log("%.*s\n", (int) (eol - logline), logline);
1770 c->suppress_log = 1;
1775 http_log("\nGot request:\n%s\n", c->buffer);
1778 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1781 /* Now we have to find the client_id */
1782 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1783 if (wmpc->wmp_client_id == client_id)
1787 if (wmpc && modify_current_stream(wmpc, ratebuf))
1788 wmpc->switch_pending = 1;
1791 snprintf(msg, sizeof(msg), "POST command not handled");
1795 if (http_start_receive_data(c) < 0) {
1796 snprintf(msg, sizeof(msg), "could not open feed");
1800 c->state = HTTPSTATE_RECEIVE_DATA;
1805 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1806 http_log("\nGot request:\n%s\n", c->buffer);
1809 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1812 /* open input stream */
1813 if (open_input_stream(c, info) < 0) {
1814 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1818 /* prepare http header */
1820 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1821 mime_type = c->stream->fmt->mime_type;
1823 mime_type = "application/x-octet-stream";
1824 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1826 /* for asf, we need extra headers */
1827 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1828 /* Need to allocate a client id */
1830 c->wmp_client_id = av_lfg_get(&random_state);
1832 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);
1834 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1835 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1837 /* prepare output buffer */
1839 c->buffer_ptr = c->buffer;
1841 c->state = HTTPSTATE_SEND_HEADER;
1844 c->http_error = 404;
1846 q += snprintf(q, c->buffer_size,
1847 "HTTP/1.0 404 Not Found\r\n"
1848 "Content-type: text/html\r\n"
1851 "<head><title>404 Not Found</title></head>\n"
1854 /* prepare output buffer */
1855 c->buffer_ptr = c->buffer;
1857 c->state = HTTPSTATE_SEND_HEADER;
1861 c->http_error = 200; /* horrible : we use this value to avoid
1862 going to the send data state */
1863 c->state = HTTPSTATE_SEND_HEADER;
1867 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1869 static const char *suffix = " kMGTP";
1872 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1874 url_fprintf(pb, "%"PRId64"%c", count, *s);
1877 static void compute_status(HTTPContext *c)
1886 if (url_open_dyn_buf(&pb) < 0) {
1887 /* XXX: return an error ? */
1888 c->buffer_ptr = c->buffer;
1889 c->buffer_end = c->buffer;
1893 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1894 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1895 url_fprintf(pb, "Pragma: no-cache\r\n");
1896 url_fprintf(pb, "\r\n");
1898 url_fprintf(pb, "<html><head><title>%s Status</title>\n", program_name);
1899 if (c->stream->feed_filename[0])
1900 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1901 url_fprintf(pb, "</head>\n<body>");
1902 url_fprintf(pb, "<h1>%s Status</h1>\n", program_name);
1904 url_fprintf(pb, "<h2>Available Streams</h2>\n");
1905 url_fprintf(pb, "<table cellspacing=0 cellpadding=4>\n");
1906 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");
1907 stream = first_stream;
1908 while (stream != NULL) {
1909 char sfilename[1024];
1912 if (stream->feed != stream) {
1913 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1914 eosf = sfilename + strlen(sfilename);
1915 if (eosf - sfilename >= 4) {
1916 if (strcmp(eosf - 4, ".asf") == 0)
1917 strcpy(eosf - 4, ".asx");
1918 else if (strcmp(eosf - 3, ".rm") == 0)
1919 strcpy(eosf - 3, ".ram");
1920 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1921 /* generate a sample RTSP director if
1922 unicast. Generate an SDP redirector if
1924 eosf = strrchr(sfilename, '.');
1926 eosf = sfilename + strlen(sfilename);
1927 if (stream->is_multicast)
1928 strcpy(eosf, ".sdp");
1930 strcpy(eosf, ".rtsp");
1934 url_fprintf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1935 sfilename, stream->filename);
1936 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1937 stream->conns_served);
1938 fmt_bytecount(pb, stream->bytes_served);
1939 switch(stream->stream_type) {
1940 case STREAM_TYPE_LIVE: {
1941 int audio_bit_rate = 0;
1942 int video_bit_rate = 0;
1943 const char *audio_codec_name = "";
1944 const char *video_codec_name = "";
1945 const char *audio_codec_name_extra = "";
1946 const char *video_codec_name_extra = "";
1948 for(i=0;i<stream->nb_streams;i++) {
1949 AVStream *st = stream->streams[i];
1950 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1951 switch(st->codec->codec_type) {
1952 case AVMEDIA_TYPE_AUDIO:
1953 audio_bit_rate += st->codec->bit_rate;
1955 if (*audio_codec_name)
1956 audio_codec_name_extra = "...";
1957 audio_codec_name = codec->name;
1960 case AVMEDIA_TYPE_VIDEO:
1961 video_bit_rate += st->codec->bit_rate;
1963 if (*video_codec_name)
1964 video_codec_name_extra = "...";
1965 video_codec_name = codec->name;
1968 case AVMEDIA_TYPE_DATA:
1969 video_bit_rate += st->codec->bit_rate;
1975 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",
1978 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1979 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1981 url_fprintf(pb, "<td>%s", stream->feed->filename);
1983 url_fprintf(pb, "<td>%s", stream->feed_filename);
1984 url_fprintf(pb, "\n");
1988 url_fprintf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1992 stream = stream->next;
1994 url_fprintf(pb, "</table>\n");
1996 stream = first_stream;
1997 while (stream != NULL) {
1998 if (stream->feed == stream) {
1999 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
2001 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
2003 #if defined(linux) && !defined(CONFIG_NOCUTILS)
2008 /* This is somewhat linux specific I guess */
2009 snprintf(ps_cmd, sizeof(ps_cmd),
2010 "ps -o \"%%cpu,cputime\" --no-headers %d",
2013 pid_stat = popen(ps_cmd, "r");
2018 if (fscanf(pid_stat, "%10s %64s", cpuperc,
2020 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2028 url_fprintf(pb, "<p>");
2030 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");
2032 for (i = 0; i < stream->nb_streams; i++) {
2033 AVStream *st = stream->streams[i];
2034 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2035 const char *type = "unknown";
2036 char parameters[64];
2040 switch(st->codec->codec_type) {
2041 case AVMEDIA_TYPE_AUDIO:
2043 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2045 case AVMEDIA_TYPE_VIDEO:
2047 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2048 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2053 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2054 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2056 url_fprintf(pb, "</table>\n");
2059 stream = stream->next;
2062 /* connection status */
2063 url_fprintf(pb, "<h2>Connection Status</h2>\n");
2065 url_fprintf(pb, "Number of connections: %d / %d<br>\n",
2066 nb_connections, nb_max_connections);
2068 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2069 current_bandwidth, max_bandwidth);
2071 url_fprintf(pb, "<table>\n");
2072 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");
2073 c1 = first_http_ctx;
2075 while (c1 != NULL) {
2081 for (j = 0; j < c1->stream->nb_streams; j++) {
2082 if (!c1->stream->feed)
2083 bitrate += c1->stream->streams[j]->codec->bit_rate;
2084 else if (c1->feed_streams[j] >= 0)
2085 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2090 p = inet_ntoa(c1->from_addr.sin_addr);
2091 url_fprintf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2093 c1->stream ? c1->stream->filename : "",
2094 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2097 http_state[c1->state]);
2098 fmt_bytecount(pb, bitrate);
2099 url_fprintf(pb, "<td align=right>");
2100 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2101 url_fprintf(pb, "<td align=right>");
2102 fmt_bytecount(pb, c1->data_count);
2103 url_fprintf(pb, "\n");
2106 url_fprintf(pb, "</table>\n");
2111 url_fprintf(pb, "<hr size=1 noshade>Generated at %s", p);
2112 url_fprintf(pb, "</body>\n</html>\n");
2114 len = url_close_dyn_buf(pb, &c->pb_buffer);
2115 c->buffer_ptr = c->pb_buffer;
2116 c->buffer_end = c->pb_buffer + len;
2119 /* check if the parser needs to be opened for stream i */
2120 static void open_parser(AVFormatContext *s, int i)
2122 AVStream *st = s->streams[i];
2125 if (!st->codec->codec) {
2126 codec = avcodec_find_decoder(st->codec->codec_id);
2127 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
2128 st->codec->parse_only = 1;
2129 if (avcodec_open(st->codec, codec) < 0)
2130 st->codec->parse_only = 0;
2135 static int open_input_stream(HTTPContext *c, const char *info)
2138 char input_filename[1024];
2140 int buf_size, i, ret;
2143 /* find file name */
2144 if (c->stream->feed) {
2145 strcpy(input_filename, c->stream->feed->feed_filename);
2146 buf_size = FFM_PACKET_SIZE;
2147 /* compute position (absolute time) */
2148 if (find_info_tag(buf, sizeof(buf), "date", info)) {
2149 stream_pos = parse_date(buf, 0);
2150 if (stream_pos == INT64_MIN)
2152 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
2153 int prebuffer = strtol(buf, 0, 10);
2154 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2156 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2158 strcpy(input_filename, c->stream->feed_filename);
2160 /* compute position (relative time) */
2161 if (find_info_tag(buf, sizeof(buf), "date", info)) {
2162 stream_pos = parse_date(buf, 1);
2163 if (stream_pos == INT64_MIN)
2168 if (input_filename[0] == '\0')
2172 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
2173 buf_size, c->stream->ap_in)) < 0) {
2174 http_log("could not open %s: %d\n", input_filename, ret);
2177 s->flags |= AVFMT_FLAG_GENPTS;
2179 if (strcmp(s->iformat->name, "ffm") && av_find_stream_info(c->fmt_in) < 0) {
2180 http_log("Could not find stream info '%s'\n", input_filename);
2181 av_close_input_file(s);
2185 /* open each parser */
2186 for(i=0;i<s->nb_streams;i++)
2189 /* choose stream as clock source (we favorize video stream if
2190 present) for packet sending */
2191 c->pts_stream_index = 0;
2192 for(i=0;i<c->stream->nb_streams;i++) {
2193 if (c->pts_stream_index == 0 &&
2194 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2195 c->pts_stream_index = i;
2200 if (c->fmt_in->iformat->read_seek)
2201 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2203 /* set the start time (needed for maxtime and RTP packet timing) */
2204 c->start_time = cur_time;
2205 c->first_pts = AV_NOPTS_VALUE;
2209 /* return the server clock (in us) */
2210 static int64_t get_server_clock(HTTPContext *c)
2212 /* compute current pts value from system time */
2213 return (cur_time - c->start_time) * 1000;
2216 /* return the estimated time at which the current packet must be sent
2218 static int64_t get_packet_send_clock(HTTPContext *c)
2220 int bytes_left, bytes_sent, frame_bytes;
2222 frame_bytes = c->cur_frame_bytes;
2223 if (frame_bytes <= 0)
2226 bytes_left = c->buffer_end - c->buffer_ptr;
2227 bytes_sent = frame_bytes - bytes_left;
2228 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2233 static int http_prepare_data(HTTPContext *c)
2236 AVFormatContext *ctx;
2238 av_freep(&c->pb_buffer);
2240 case HTTPSTATE_SEND_DATA_HEADER:
2241 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2242 av_metadata_set2(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2243 av_metadata_set2(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2244 av_metadata_set2(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2245 av_metadata_set2(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2247 for(i=0;i<c->stream->nb_streams;i++) {
2250 st = av_mallocz(sizeof(AVStream));
2251 c->fmt_ctx.streams[i] = st;
2252 /* if file or feed, then just take streams from FFStream struct */
2253 if (!c->stream->feed ||
2254 c->stream->feed == c->stream)
2255 src = c->stream->streams[i];
2257 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2261 st->codec->frame_number = 0; /* XXX: should be done in
2262 AVStream, not in codec */
2264 /* set output format parameters */
2265 c->fmt_ctx.oformat = c->stream->fmt;
2266 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2268 c->got_key_frame = 0;
2270 /* prepare header and save header data in a stream */
2271 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2272 /* XXX: potential leak */
2275 c->fmt_ctx.pb->is_streamed = 1;
2278 * HACK to avoid mpeg ps muxer to spit many underflow errors
2279 * Default value from FFmpeg
2280 * Try to set it use configuration option
2282 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2283 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2285 av_set_parameters(&c->fmt_ctx, NULL);
2286 if (av_write_header(&c->fmt_ctx) < 0) {
2287 http_log("Error writing output header\n");
2290 av_metadata_free(&c->fmt_ctx.metadata);
2292 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2293 c->buffer_ptr = c->pb_buffer;
2294 c->buffer_end = c->pb_buffer + len;
2296 c->state = HTTPSTATE_SEND_DATA;
2297 c->last_packet_sent = 0;
2299 case HTTPSTATE_SEND_DATA:
2300 /* find a new packet */
2301 /* read a packet from the input stream */
2302 if (c->stream->feed)
2303 ffm_set_write_index(c->fmt_in,
2304 c->stream->feed->feed_write_index,
2305 c->stream->feed->feed_size);
2307 if (c->stream->max_time &&
2308 c->stream->max_time + c->start_time - cur_time < 0)
2309 /* We have timed out */
2310 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2314 ret = av_read_frame(c->fmt_in, &pkt);
2316 if (c->stream->feed) {
2317 /* if coming from feed, it means we reached the end of the
2318 ffm file, so must wait for more data */
2319 c->state = HTTPSTATE_WAIT_FEED;
2320 return 1; /* state changed */
2321 } else if (ret == AVERROR(EAGAIN)) {
2322 /* input not ready, come back later */
2325 if (c->stream->loop) {
2326 av_close_input_file(c->fmt_in);
2328 if (open_input_stream(c, "") < 0)
2333 /* must send trailer now because eof or error */
2334 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2338 int source_index = pkt.stream_index;
2339 /* update first pts if needed */
2340 if (c->first_pts == AV_NOPTS_VALUE) {
2341 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2342 c->start_time = cur_time;
2344 /* send it to the appropriate stream */
2345 if (c->stream->feed) {
2346 /* if coming from a feed, select the right stream */
2347 if (c->switch_pending) {
2348 c->switch_pending = 0;
2349 for(i=0;i<c->stream->nb_streams;i++) {
2350 if (c->switch_feed_streams[i] == pkt.stream_index)
2351 if (pkt.flags & AV_PKT_FLAG_KEY)
2352 do_switch_stream(c, i);
2353 if (c->switch_feed_streams[i] >= 0)
2354 c->switch_pending = 1;
2357 for(i=0;i<c->stream->nb_streams;i++) {
2358 if (c->stream->feed_streams[i] == pkt.stream_index) {
2359 AVStream *st = c->fmt_in->streams[source_index];
2360 pkt.stream_index = i;
2361 if (pkt.flags & AV_PKT_FLAG_KEY &&
2362 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2363 c->stream->nb_streams == 1))
2364 c->got_key_frame = 1;
2365 if (!c->stream->send_on_key || c->got_key_frame)
2370 AVCodecContext *codec;
2371 AVStream *ist, *ost;
2373 ist = c->fmt_in->streams[source_index];
2374 /* specific handling for RTP: we use several
2375 output stream (one for each RTP
2376 connection). XXX: need more abstract handling */
2377 if (c->is_packetized) {
2378 /* compute send time and duration */
2379 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2380 c->cur_pts -= c->first_pts;
2381 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2382 /* find RTP context */
2383 c->packet_stream_index = pkt.stream_index;
2384 ctx = c->rtp_ctx[c->packet_stream_index];
2386 av_free_packet(&pkt);
2389 codec = ctx->streams[0]->codec;
2390 /* only one stream per RTP connection */
2391 pkt.stream_index = 0;
2395 codec = ctx->streams[pkt.stream_index]->codec;
2398 if (c->is_packetized) {
2399 int max_packet_size;
2400 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2401 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2403 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2404 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2406 ret = url_open_dyn_buf(&ctx->pb);
2409 /* XXX: potential leak */
2412 ost = ctx->streams[pkt.stream_index];
2414 ctx->pb->is_streamed = 1;
2415 if (pkt.dts != AV_NOPTS_VALUE)
2416 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2417 if (pkt.pts != AV_NOPTS_VALUE)
2418 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2419 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2420 if (av_write_frame(ctx, &pkt) < 0) {
2421 http_log("Error writing frame to output\n");
2422 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2425 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2426 c->cur_frame_bytes = len;
2427 c->buffer_ptr = c->pb_buffer;
2428 c->buffer_end = c->pb_buffer + len;
2430 codec->frame_number++;
2432 av_free_packet(&pkt);
2436 av_free_packet(&pkt);
2441 case HTTPSTATE_SEND_DATA_TRAILER:
2442 /* last packet test ? */
2443 if (c->last_packet_sent || c->is_packetized)
2446 /* prepare header */
2447 if (url_open_dyn_buf(&ctx->pb) < 0) {
2448 /* XXX: potential leak */
2451 c->fmt_ctx.pb->is_streamed = 1;
2452 av_write_trailer(ctx);
2453 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2454 c->buffer_ptr = c->pb_buffer;
2455 c->buffer_end = c->pb_buffer + len;
2457 c->last_packet_sent = 1;
2463 /* should convert the format at the same time */
2464 /* send data starting at c->buffer_ptr to the output connection
2465 (either UDP or TCP connection) */
2466 static int http_send_data(HTTPContext *c)
2471 if (c->buffer_ptr >= c->buffer_end) {
2472 ret = http_prepare_data(c);
2476 /* state change requested */
2479 if (c->is_packetized) {
2480 /* RTP data output */
2481 len = c->buffer_end - c->buffer_ptr;
2483 /* fail safe - should never happen */
2485 c->buffer_ptr = c->buffer_end;
2488 len = (c->buffer_ptr[0] << 24) |
2489 (c->buffer_ptr[1] << 16) |
2490 (c->buffer_ptr[2] << 8) |
2492 if (len > (c->buffer_end - c->buffer_ptr))
2494 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2495 /* nothing to send yet: we can wait */
2499 c->data_count += len;
2500 update_datarate(&c->datarate, c->data_count);
2502 c->stream->bytes_served += len;
2504 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2505 /* RTP packets are sent inside the RTSP TCP connection */
2507 int interleaved_index, size;
2509 HTTPContext *rtsp_c;
2512 /* if no RTSP connection left, error */
2515 /* if already sending something, then wait. */
2516 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2518 if (url_open_dyn_buf(&pb) < 0)
2520 interleaved_index = c->packet_stream_index * 2;
2521 /* RTCP packets are sent at odd indexes */
2522 if (c->buffer_ptr[1] == 200)
2523 interleaved_index++;
2524 /* write RTSP TCP header */
2526 header[1] = interleaved_index;
2527 header[2] = len >> 8;
2529 put_buffer(pb, header, 4);
2530 /* write RTP packet data */
2532 put_buffer(pb, c->buffer_ptr, len);
2533 size = url_close_dyn_buf(pb, &c->packet_buffer);
2534 /* prepare asynchronous TCP sending */
2535 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2536 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2537 c->buffer_ptr += len;
2539 /* send everything we can NOW */
2540 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2541 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2543 rtsp_c->packet_buffer_ptr += len;
2544 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2545 /* if we could not send all the data, we will
2546 send it later, so a new state is needed to
2547 "lock" the RTSP TCP connection */
2548 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2551 /* all data has been sent */
2552 av_freep(&c->packet_buffer);
2554 /* send RTP packet directly in UDP */
2556 url_write(c->rtp_handles[c->packet_stream_index],
2557 c->buffer_ptr, len);
2558 c->buffer_ptr += len;
2559 /* here we continue as we can send several packets per 10 ms slot */
2562 /* TCP data output */
2563 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2565 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2566 ff_neterrno() != FF_NETERROR(EINTR))
2567 /* error : close connection */
2572 c->buffer_ptr += len;
2574 c->data_count += len;
2575 update_datarate(&c->datarate, c->data_count);
2577 c->stream->bytes_served += len;
2585 static int http_start_receive_data(HTTPContext *c)
2589 if (c->stream->feed_opened)
2592 /* Don't permit writing to this one */
2593 if (c->stream->readonly)
2597 fd = open(c->stream->feed_filename, O_RDWR);
2599 http_log("Error opening feeder file: %s\n", strerror(errno));
2604 if (c->stream->truncate) {
2605 /* truncate feed file */
2606 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2607 ftruncate(c->feed_fd, FFM_PACKET_SIZE);
2608 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2610 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2611 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2616 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2617 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2618 lseek(fd, 0, SEEK_SET);
2620 /* init buffer input */
2621 c->buffer_ptr = c->buffer;
2622 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2623 c->stream->feed_opened = 1;
2624 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2628 static int http_receive_data(HTTPContext *c)
2631 int len, loop_run = 0;
2633 while (c->chunked_encoding && !c->chunk_size &&
2634 c->buffer_end > c->buffer_ptr) {
2635 /* read chunk header, if present */
2636 len = recv(c->fd, c->buffer_ptr, 1, 0);
2639 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2640 ff_neterrno() != FF_NETERROR(EINTR))
2641 /* error : close connection */
2644 } else if (len == 0) {
2645 /* end of connection : close it */
2647 } else if (c->buffer_ptr - c->buffer >= 2 &&
2648 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2649 c->chunk_size = strtol(c->buffer, 0, 16);
2650 if (c->chunk_size == 0) // end of stream
2652 c->buffer_ptr = c->buffer;
2654 } else if (++loop_run > 10) {
2655 /* no chunk header, abort */
2662 if (c->buffer_end > c->buffer_ptr) {
2663 len = recv(c->fd, c->buffer_ptr,
2664 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2666 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2667 ff_neterrno() != FF_NETERROR(EINTR))
2668 /* error : close connection */
2670 } else if (len == 0)
2671 /* end of connection : close it */
2674 c->chunk_size -= len;
2675 c->buffer_ptr += len;
2676 c->data_count += len;
2677 update_datarate(&c->datarate, c->data_count);
2681 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2682 if (c->buffer[0] != 'f' ||
2683 c->buffer[1] != 'm') {
2684 http_log("Feed stream has become desynchronized -- disconnecting\n");
2689 if (c->buffer_ptr >= c->buffer_end) {
2690 FFStream *feed = c->stream;
2691 /* a packet has been received : write it in the store, except
2693 if (c->data_count > FFM_PACKET_SIZE) {
2695 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2696 /* XXX: use llseek or url_seek */
2697 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2698 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2699 http_log("Error writing to feed file: %s\n", strerror(errno));
2703 feed->feed_write_index += FFM_PACKET_SIZE;
2704 /* update file size */
2705 if (feed->feed_write_index > c->stream->feed_size)
2706 feed->feed_size = feed->feed_write_index;
2708 /* handle wrap around if max file size reached */
2709 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2710 feed->feed_write_index = FFM_PACKET_SIZE;
2713 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2714 http_log("Error writing index to feed file: %s\n", strerror(errno));
2718 /* wake up any waiting connections */
2719 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2720 if (c1->state == HTTPSTATE_WAIT_FEED &&
2721 c1->stream->feed == c->stream->feed)
2722 c1->state = HTTPSTATE_SEND_DATA;
2725 /* We have a header in our hands that contains useful data */
2726 AVFormatContext *s = NULL;
2728 AVInputFormat *fmt_in;
2731 /* use feed output format name to find corresponding input format */
2732 fmt_in = av_find_input_format(feed->fmt->name);
2736 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2737 pb->is_streamed = 1;
2739 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2744 /* Now we have the actual streams */
2745 if (s->nb_streams != feed->nb_streams) {
2746 av_close_input_stream(s);
2748 http_log("Feed '%s' stream number does not match registered feed\n",
2749 c->stream->feed_filename);
2753 for (i = 0; i < s->nb_streams; i++) {
2754 AVStream *fst = feed->streams[i];
2755 AVStream *st = s->streams[i];
2756 avcodec_copy_context(fst->codec, st->codec);
2759 av_close_input_stream(s);
2762 c->buffer_ptr = c->buffer;
2767 c->stream->feed_opened = 0;
2769 /* wake up any waiting connections to stop waiting for feed */
2770 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2771 if (c1->state == HTTPSTATE_WAIT_FEED &&
2772 c1->stream->feed == c->stream->feed)
2773 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2778 /********************************************************************/
2781 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2788 switch(error_number) {
2789 case RTSP_STATUS_OK:
2792 case RTSP_STATUS_METHOD:
2793 str = "Method Not Allowed";
2795 case RTSP_STATUS_BANDWIDTH:
2796 str = "Not Enough Bandwidth";
2798 case RTSP_STATUS_SESSION:
2799 str = "Session Not Found";
2801 case RTSP_STATUS_STATE:
2802 str = "Method Not Valid in This State";
2804 case RTSP_STATUS_AGGREGATE:
2805 str = "Aggregate operation not allowed";
2807 case RTSP_STATUS_ONLY_AGGREGATE:
2808 str = "Only aggregate operation allowed";
2810 case RTSP_STATUS_TRANSPORT:
2811 str = "Unsupported transport";
2813 case RTSP_STATUS_INTERNAL:
2814 str = "Internal Server Error";
2816 case RTSP_STATUS_SERVICE:
2817 str = "Service Unavailable";
2819 case RTSP_STATUS_VERSION:
2820 str = "RTSP Version not supported";
2823 str = "Unknown Error";
2827 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2828 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2830 /* output GMT time */
2833 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2834 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2837 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2839 rtsp_reply_header(c, error_number);
2840 url_fprintf(c->pb, "\r\n");
2843 static int rtsp_parse_request(HTTPContext *c)
2845 const char *p, *p1, *p2;
2851 RTSPMessageHeader header1, *header = &header1;
2853 c->buffer_ptr[0] = '\0';
2856 get_word(cmd, sizeof(cmd), &p);
2857 get_word(url, sizeof(url), &p);
2858 get_word(protocol, sizeof(protocol), &p);
2860 av_strlcpy(c->method, cmd, sizeof(c->method));
2861 av_strlcpy(c->url, url, sizeof(c->url));
2862 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2864 if (url_open_dyn_buf(&c->pb) < 0) {
2865 /* XXX: cannot do more */
2866 c->pb = NULL; /* safety */
2870 /* check version name */
2871 if (strcmp(protocol, "RTSP/1.0") != 0) {
2872 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2876 /* parse each header line */
2877 memset(header, 0, sizeof(*header));
2878 /* skip to next line */
2879 while (*p != '\n' && *p != '\0')
2883 while (*p != '\0') {
2884 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2888 if (p2 > p && p2[-1] == '\r')
2890 /* skip empty line */
2894 if (len > sizeof(line) - 1)
2895 len = sizeof(line) - 1;
2896 memcpy(line, p, len);
2898 ff_rtsp_parse_line(header, line, NULL);
2902 /* handle sequence number */
2903 c->seq = header->seq;
2905 if (!strcmp(cmd, "DESCRIBE"))
2906 rtsp_cmd_describe(c, url);
2907 else if (!strcmp(cmd, "OPTIONS"))
2908 rtsp_cmd_options(c, url);
2909 else if (!strcmp(cmd, "SETUP"))
2910 rtsp_cmd_setup(c, url, header);
2911 else if (!strcmp(cmd, "PLAY"))
2912 rtsp_cmd_play(c, url, header);
2913 else if (!strcmp(cmd, "PAUSE"))
2914 rtsp_cmd_pause(c, url, header);
2915 else if (!strcmp(cmd, "TEARDOWN"))
2916 rtsp_cmd_teardown(c, url, header);
2918 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2921 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2922 c->pb = NULL; /* safety */
2924 /* XXX: cannot do more */
2927 c->buffer_ptr = c->pb_buffer;
2928 c->buffer_end = c->pb_buffer + len;
2929 c->state = RTSPSTATE_SEND_REPLY;
2933 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2934 struct in_addr my_ip)
2936 AVFormatContext *avc;
2937 AVStream *avs = NULL;
2940 avc = avformat_alloc_context();
2944 av_metadata_set2(&avc->metadata, "title",
2945 stream->title[0] ? stream->title : "No Title", 0);
2946 avc->nb_streams = stream->nb_streams;
2947 if (stream->is_multicast) {
2948 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2949 inet_ntoa(stream->multicast_ip),
2950 stream->multicast_port, stream->multicast_ttl);
2952 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2955 #if !FF_API_MAX_STREAMS
2956 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2957 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2960 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2961 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2964 for(i = 0; i < stream->nb_streams; i++) {
2965 avc->streams[i] = &avs[i];
2966 avc->streams[i]->codec = stream->streams[i]->codec;
2968 *pbuffer = av_mallocz(2048);
2969 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2972 #if !FF_API_MAX_STREAMS
2973 av_free(avc->streams);
2975 av_metadata_free(&avc->metadata);
2979 return strlen(*pbuffer);
2982 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2984 // rtsp_reply_header(c, RTSP_STATUS_OK);
2985 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2986 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2987 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2988 url_fprintf(c->pb, "\r\n");
2991 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2997 int content_length, len;
2998 struct sockaddr_in my_addr;
3000 /* find which url is asked */
3001 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3006 for(stream = first_stream; stream != NULL; stream = stream->next) {
3007 if (!stream->is_feed &&
3008 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
3009 !strcmp(path, stream->filename)) {
3013 /* no stream found */
3014 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3018 /* prepare the media description in sdp format */
3020 /* get the host IP */
3021 len = sizeof(my_addr);
3022 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
3023 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3024 if (content_length < 0) {
3025 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3028 rtsp_reply_header(c, RTSP_STATUS_OK);
3029 url_fprintf(c->pb, "Content-Base: %s/\r\n", url);
3030 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
3031 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
3032 url_fprintf(c->pb, "\r\n");
3033 put_buffer(c->pb, content, content_length);
3037 static HTTPContext *find_rtp_session(const char *session_id)
3041 if (session_id[0] == '\0')
3044 for(c = first_http_ctx; c != NULL; c = c->next) {
3045 if (!strcmp(c->session_id, session_id))
3051 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3053 RTSPTransportField *th;
3056 for(i=0;i<h->nb_transports;i++) {
3057 th = &h->transports[i];
3058 if (th->lower_transport == lower_transport)
3064 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3065 RTSPMessageHeader *h)
3068 int stream_index, rtp_port, rtcp_port;
3073 RTSPTransportField *th;
3074 struct sockaddr_in dest_addr;
3075 RTSPActionServerSetup setup;
3077 /* find which url is asked */
3078 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3083 /* now check each stream */
3084 for(stream = first_stream; stream != NULL; stream = stream->next) {
3085 if (!stream->is_feed &&
3086 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3087 /* accept aggregate filenames only if single stream */
3088 if (!strcmp(path, stream->filename)) {
3089 if (stream->nb_streams != 1) {
3090 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3097 for(stream_index = 0; stream_index < stream->nb_streams;
3099 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3100 stream->filename, stream_index);
3101 if (!strcmp(path, buf))
3106 /* no stream found */
3107 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3111 /* generate session id if needed */
3112 if (h->session_id[0] == '\0')
3113 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3114 av_lfg_get(&random_state), av_lfg_get(&random_state));
3116 /* find rtp session, and create it if none found */
3117 rtp_c = find_rtp_session(h->session_id);
3119 /* always prefer UDP */
3120 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3122 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3124 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3129 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3130 th->lower_transport);
3132 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3136 /* open input stream */
3137 if (open_input_stream(rtp_c, "") < 0) {
3138 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3143 /* test if stream is OK (test needed because several SETUP needs
3144 to be done for a given file) */
3145 if (rtp_c->stream != stream) {
3146 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3150 /* test if stream is already set up */
3151 if (rtp_c->rtp_ctx[stream_index]) {
3152 rtsp_reply_error(c, RTSP_STATUS_STATE);
3156 /* check transport */
3157 th = find_transport(h, rtp_c->rtp_protocol);
3158 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3159 th->client_port_min <= 0)) {
3160 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3164 /* setup default options */
3165 setup.transport_option[0] = '\0';
3166 dest_addr = rtp_c->from_addr;
3167 dest_addr.sin_port = htons(th->client_port_min);
3170 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3171 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3175 /* now everything is OK, so we can send the connection parameters */
3176 rtsp_reply_header(c, RTSP_STATUS_OK);
3178 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3180 switch(rtp_c->rtp_protocol) {
3181 case RTSP_LOWER_TRANSPORT_UDP:
3182 rtp_port = rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3183 rtcp_port = rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3184 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3185 "client_port=%d-%d;server_port=%d-%d",
3186 th->client_port_min, th->client_port_max,
3187 rtp_port, rtcp_port);
3189 case RTSP_LOWER_TRANSPORT_TCP:
3190 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3191 stream_index * 2, stream_index * 2 + 1);
3196 if (setup.transport_option[0] != '\0')
3197 url_fprintf(c->pb, ";%s", setup.transport_option);
3198 url_fprintf(c->pb, "\r\n");
3201 url_fprintf(c->pb, "\r\n");
3205 /* find an rtp connection by using the session ID. Check consistency
3207 static HTTPContext *find_rtp_session_with_url(const char *url,
3208 const char *session_id)
3216 rtp_c = find_rtp_session(session_id);
3220 /* find which url is asked */
3221 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3225 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3226 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3227 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3228 rtp_c->stream->filename, s);
3229 if(!strncmp(path, buf, sizeof(buf))) {
3230 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3237 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3241 rtp_c = find_rtp_session_with_url(url, h->session_id);
3243 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3247 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3248 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3249 rtp_c->state != HTTPSTATE_READY) {
3250 rtsp_reply_error(c, RTSP_STATUS_STATE);
3254 rtp_c->state = HTTPSTATE_SEND_DATA;
3256 /* now everything is OK, so we can send the connection parameters */
3257 rtsp_reply_header(c, RTSP_STATUS_OK);
3259 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3260 url_fprintf(c->pb, "\r\n");
3263 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3267 rtp_c = find_rtp_session_with_url(url, h->session_id);
3269 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3273 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3274 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3275 rtsp_reply_error(c, RTSP_STATUS_STATE);
3279 rtp_c->state = HTTPSTATE_READY;
3280 rtp_c->first_pts = AV_NOPTS_VALUE;
3281 /* now everything is OK, so we can send the connection parameters */
3282 rtsp_reply_header(c, RTSP_STATUS_OK);
3284 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3285 url_fprintf(c->pb, "\r\n");
3288 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3291 char session_id[32];
3293 rtp_c = find_rtp_session_with_url(url, h->session_id);
3295 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3299 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3301 /* abort the session */
3302 close_connection(rtp_c);
3304 /* now everything is OK, so we can send the connection parameters */
3305 rtsp_reply_header(c, RTSP_STATUS_OK);
3307 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3308 url_fprintf(c->pb, "\r\n");
3312 /********************************************************************/
3315 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3316 FFStream *stream, const char *session_id,
3317 enum RTSPLowerTransport rtp_protocol)
3319 HTTPContext *c = NULL;
3320 const char *proto_str;
3322 /* XXX: should output a warning page when coming
3323 close to the connection limit */
3324 if (nb_connections >= nb_max_connections)
3327 /* add a new connection */
3328 c = av_mallocz(sizeof(HTTPContext));
3333 c->poll_entry = NULL;
3334 c->from_addr = *from_addr;
3335 c->buffer_size = IOBUFFER_INIT_SIZE;
3336 c->buffer = av_malloc(c->buffer_size);
3341 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3342 c->state = HTTPSTATE_READY;
3343 c->is_packetized = 1;
3344 c->rtp_protocol = rtp_protocol;
3346 /* protocol is shown in statistics */
3347 switch(c->rtp_protocol) {
3348 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3349 proto_str = "MCAST";
3351 case RTSP_LOWER_TRANSPORT_UDP:
3354 case RTSP_LOWER_TRANSPORT_TCP:
3361 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3362 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3364 current_bandwidth += stream->bandwidth;
3366 c->next = first_http_ctx;
3378 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3379 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3381 static int rtp_new_av_stream(HTTPContext *c,
3382 int stream_index, struct sockaddr_in *dest_addr,
3383 HTTPContext *rtsp_c)
3385 AVFormatContext *ctx;
3388 URLContext *h = NULL;
3390 int max_packet_size;
3392 /* now we can open the relevant output stream */
3393 ctx = avformat_alloc_context();
3396 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3398 st = av_mallocz(sizeof(AVStream));
3401 ctx->nb_streams = 1;
3402 ctx->streams[0] = st;
3404 if (!c->stream->feed ||
3405 c->stream->feed == c->stream)
3406 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3409 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3411 st->priv_data = NULL;
3413 /* build destination RTP address */
3414 ipaddr = inet_ntoa(dest_addr->sin_addr);
3416 switch(c->rtp_protocol) {
3417 case RTSP_LOWER_TRANSPORT_UDP:
3418 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3421 /* XXX: also pass as parameter to function ? */
3422 if (c->stream->is_multicast) {
3424 ttl = c->stream->multicast_ttl;
3427 snprintf(ctx->filename, sizeof(ctx->filename),
3428 "rtp://%s:%d?multicast=1&ttl=%d",
3429 ipaddr, ntohs(dest_addr->sin_port), ttl);
3431 snprintf(ctx->filename, sizeof(ctx->filename),
3432 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3435 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3437 c->rtp_handles[stream_index] = h;
3438 max_packet_size = url_get_max_packet_size(h);
3440 case RTSP_LOWER_TRANSPORT_TCP:
3443 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3449 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3450 ipaddr, ntohs(dest_addr->sin_port),
3451 c->stream->filename, stream_index, c->protocol);
3453 /* normally, no packets should be output here, but the packet size may be checked */
3454 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3455 /* XXX: close stream */
3458 av_set_parameters(ctx, NULL);
3459 if (av_write_header(ctx) < 0) {
3466 url_close_dyn_buf(ctx->pb, &dummy_buf);
3469 c->rtp_ctx[stream_index] = ctx;
3473 /********************************************************************/
3474 /* ffserver initialization */
3476 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3480 fst = av_mallocz(sizeof(AVStream));
3484 fst->codec= avcodec_alloc_context();
3485 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3486 if (codec->extradata_size) {
3487 fst->codec->extradata = av_malloc(codec->extradata_size);
3488 memcpy(fst->codec->extradata, codec->extradata,
3489 codec->extradata_size);
3492 /* live streams must use the actual feed's codec since it may be
3493 * updated later to carry extradata needed by the streams.
3497 fst->priv_data = av_mallocz(sizeof(FeedData));
3498 fst->index = stream->nb_streams;
3499 av_set_pts_info(fst, 33, 1, 90000);
3500 stream->streams[stream->nb_streams++] = fst;
3504 /* return the stream number in the feed */
3505 static int add_av_stream(FFStream *feed, AVStream *st)
3508 AVCodecContext *av, *av1;
3512 for(i=0;i<feed->nb_streams;i++) {
3513 st = feed->streams[i];
3515 if (av1->codec_id == av->codec_id &&
3516 av1->codec_type == av->codec_type &&
3517 av1->bit_rate == av->bit_rate) {
3519 switch(av->codec_type) {
3520 case AVMEDIA_TYPE_AUDIO:
3521 if (av1->channels == av->channels &&
3522 av1->sample_rate == av->sample_rate)
3525 case AVMEDIA_TYPE_VIDEO:
3526 if (av1->width == av->width &&
3527 av1->height == av->height &&
3528 av1->time_base.den == av->time_base.den &&
3529 av1->time_base.num == av->time_base.num &&
3530 av1->gop_size == av->gop_size)
3539 fst = add_av_stream1(feed, av, 0);
3542 return feed->nb_streams - 1;
3547 static void remove_stream(FFStream *stream)
3551 while (*ps != NULL) {
3559 /* specific mpeg4 handling : we extract the raw parameters */
3560 static void extract_mpeg4_header(AVFormatContext *infile)
3562 int mpeg4_count, i, size;
3568 for(i=0;i<infile->nb_streams;i++) {
3569 st = infile->streams[i];
3570 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3571 st->codec->extradata_size == 0) {
3578 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3579 while (mpeg4_count > 0) {
3580 if (av_read_packet(infile, &pkt) < 0)
3582 st = infile->streams[pkt.stream_index];
3583 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3584 st->codec->extradata_size == 0) {
3585 av_freep(&st->codec->extradata);
3586 /* fill extradata with the header */
3587 /* XXX: we make hard suppositions here ! */
3589 while (p < pkt.data + pkt.size - 4) {
3590 /* stop when vop header is found */
3591 if (p[0] == 0x00 && p[1] == 0x00 &&
3592 p[2] == 0x01 && p[3] == 0xb6) {
3593 size = p - pkt.data;
3594 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3595 st->codec->extradata = av_malloc(size);
3596 st->codec->extradata_size = size;
3597 memcpy(st->codec->extradata, pkt.data, size);
3604 av_free_packet(&pkt);
3608 /* compute the needed AVStream for each file */
3609 static void build_file_streams(void)
3611 FFStream *stream, *stream_next;
3612 AVFormatContext *infile;
3615 /* gather all streams */
3616 for(stream = first_stream; stream != NULL; stream = stream_next) {
3617 stream_next = stream->next;
3618 if (stream->stream_type == STREAM_TYPE_LIVE &&
3620 /* the stream comes from a file */
3621 /* try to open the file */
3623 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3624 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3625 /* specific case : if transport stream output to RTP,
3626 we use a raw transport stream reader */
3627 stream->ap_in->mpeg2ts_raw = 1;
3628 stream->ap_in->mpeg2ts_compute_pcr = 1;
3631 http_log("Opening file '%s'\n", stream->feed_filename);
3632 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3633 stream->ifmt, 0, stream->ap_in)) < 0) {
3634 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3635 /* remove stream (no need to spend more time on it) */
3637 remove_stream(stream);
3639 /* find all the AVStreams inside and reference them in
3641 if (av_find_stream_info(infile) < 0) {
3642 http_log("Could not find codec parameters from '%s'\n",
3643 stream->feed_filename);
3644 av_close_input_file(infile);
3647 extract_mpeg4_header(infile);
3649 for(i=0;i<infile->nb_streams;i++)
3650 add_av_stream1(stream, infile->streams[i]->codec, 1);
3652 av_close_input_file(infile);
3658 /* compute the needed AVStream for each feed */
3659 static void build_feed_streams(void)
3661 FFStream *stream, *feed;
3664 /* gather all streams */
3665 for(stream = first_stream; stream != NULL; stream = stream->next) {
3666 feed = stream->feed;
3668 if (!stream->is_feed) {
3669 /* we handle a stream coming from a feed */
3670 for(i=0;i<stream->nb_streams;i++)
3671 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3676 /* gather all streams */
3677 for(stream = first_stream; stream != NULL; stream = stream->next) {
3678 feed = stream->feed;
3680 if (stream->is_feed) {
3681 for(i=0;i<stream->nb_streams;i++)
3682 stream->feed_streams[i] = i;
3687 /* create feed files if needed */
3688 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3691 if (url_exist(feed->feed_filename)) {
3692 /* See if it matches */
3696 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3697 /* Now see if it matches */
3698 if (s->nb_streams == feed->nb_streams) {
3700 for(i=0;i<s->nb_streams;i++) {
3702 sf = feed->streams[i];
3705 if (sf->index != ss->index ||
3707 http_log("Index & Id do not match for stream %d (%s)\n",
3708 i, feed->feed_filename);
3711 AVCodecContext *ccf, *ccs;
3715 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3717 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3718 http_log("Codecs do not match for stream %d\n", i);
3720 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3721 http_log("Codec bitrates do not match for stream %d\n", i);
3723 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3724 if (CHECK_CODEC(time_base.den) ||
3725 CHECK_CODEC(time_base.num) ||
3726 CHECK_CODEC(width) ||
3727 CHECK_CODEC(height)) {
3728 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3731 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3732 if (CHECK_CODEC(sample_rate) ||
3733 CHECK_CODEC(channels) ||
3734 CHECK_CODEC(frame_size)) {
3735 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3739 http_log("Unknown codec type\n");
3747 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3748 feed->feed_filename, s->nb_streams, feed->nb_streams);
3750 av_close_input_file(s);
3752 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3753 feed->feed_filename);
3756 if (feed->readonly) {
3757 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3758 feed->feed_filename);
3761 unlink(feed->feed_filename);
3764 if (!url_exist(feed->feed_filename)) {
3765 AVFormatContext s1 = {0}, *s = &s1;
3767 if (feed->readonly) {
3768 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3769 feed->feed_filename);
3773 /* only write the header of the ffm file */
3774 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3775 http_log("Could not open output feed file '%s'\n",
3776 feed->feed_filename);
3779 s->oformat = feed->fmt;
3780 s->nb_streams = feed->nb_streams;
3781 for(i=0;i<s->nb_streams;i++) {
3783 st = feed->streams[i];
3786 av_set_parameters(s, NULL);
3787 if (av_write_header(s) < 0) {
3788 http_log("Container doesn't supports the required parameters\n");
3791 /* XXX: need better api */
3792 av_freep(&s->priv_data);
3795 /* get feed size and write index */
3796 fd = open(feed->feed_filename, O_RDONLY);
3798 http_log("Could not open output feed file '%s'\n",
3799 feed->feed_filename);
3803 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3804 feed->feed_size = lseek(fd, 0, SEEK_END);
3805 /* ensure that we do not wrap before the end of file */
3806 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3807 feed->feed_max_size = feed->feed_size;
3813 /* compute the bandwidth used by each stream */
3814 static void compute_bandwidth(void)
3820 for(stream = first_stream; stream != NULL; stream = stream->next) {
3822 for(i=0;i<stream->nb_streams;i++) {
3823 AVStream *st = stream->streams[i];
3824 switch(st->codec->codec_type) {
3825 case AVMEDIA_TYPE_AUDIO:
3826 case AVMEDIA_TYPE_VIDEO:
3827 bandwidth += st->codec->bit_rate;
3833 stream->bandwidth = (bandwidth + 999) / 1000;
3837 /* add a codec and set the default parameters */
3838 static void add_codec(FFStream *stream, AVCodecContext *av)
3842 /* compute default parameters */
3843 switch(av->codec_type) {
3844 case AVMEDIA_TYPE_AUDIO:
3845 if (av->bit_rate == 0)
3846 av->bit_rate = 64000;
3847 if (av->sample_rate == 0)
3848 av->sample_rate = 22050;
3849 if (av->channels == 0)
3852 case AVMEDIA_TYPE_VIDEO:
3853 if (av->bit_rate == 0)
3854 av->bit_rate = 64000;
3855 if (av->time_base.num == 0){
3856 av->time_base.den = 5;
3857 av->time_base.num = 1;
3859 if (av->width == 0 || av->height == 0) {
3863 /* Bitrate tolerance is less for streaming */
3864 if (av->bit_rate_tolerance == 0)
3865 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3866 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3871 if (av->max_qdiff == 0)
3873 av->qcompress = 0.5;
3876 if (!av->nsse_weight)
3877 av->nsse_weight = 8;
3879 av->frame_skip_cmp = FF_CMP_DCTMAX;
3881 av->me_method = ME_EPZS;
3882 av->rc_buffer_aggressivity = 1.0;
3885 av->rc_eq = "tex^qComp";
3886 if (!av->i_quant_factor)
3887 av->i_quant_factor = -0.8;
3888 if (!av->b_quant_factor)
3889 av->b_quant_factor = 1.25;
3890 if (!av->b_quant_offset)
3891 av->b_quant_offset = 1.25;
3892 if (!av->rc_max_rate)
3893 av->rc_max_rate = av->bit_rate * 2;
3895 if (av->rc_max_rate && !av->rc_buffer_size) {
3896 av->rc_buffer_size = av->rc_max_rate;
3905 st = av_mallocz(sizeof(AVStream));
3908 st->codec = avcodec_alloc_context();
3909 stream->streams[stream->nb_streams++] = st;
3910 memcpy(st->codec, av, sizeof(AVCodecContext));
3913 static enum CodecID opt_audio_codec(const char *arg)
3915 AVCodec *p= avcodec_find_encoder_by_name(arg);
3917 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3918 return CODEC_ID_NONE;
3923 static enum CodecID opt_video_codec(const char *arg)
3925 AVCodec *p= avcodec_find_encoder_by_name(arg);
3927 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3928 return CODEC_ID_NONE;
3933 /* simplistic plugin support */
3936 static void load_module(const char *filename)
3939 void (*init_func)(void);
3940 dll = dlopen(filename, RTLD_NOW);
3942 fprintf(stderr, "Could not load module '%s' - %s\n",
3943 filename, dlerror());
3947 init_func = dlsym(dll, "ffserver_module_init");
3950 "%s: init function 'ffserver_module_init()' not found\n",
3959 static int ffserver_opt_default(const char *opt, const char *arg,
3960 AVCodecContext *avctx, int type)
3963 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
3965 ret = av_set_string3(avctx, opt, arg, 1, NULL);
3969 static int ffserver_opt_preset(const char *arg,
3970 AVCodecContext *avctx, int type,
3971 enum CodecID *audio_id, enum CodecID *video_id)
3974 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3976 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3978 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3979 codec ? codec->name : NULL))) {
3980 fprintf(stderr, "File for preset '%s' not found\n", arg);
3985 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3986 if(line[0] == '#' && !e)
3988 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3990 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3994 if(!strcmp(tmp, "acodec")){
3995 *audio_id = opt_audio_codec(tmp2);
3996 }else if(!strcmp(tmp, "vcodec")){
3997 *video_id = opt_video_codec(tmp2);
3998 }else if(!strcmp(tmp, "scodec")){
3999 /* opt_subtitle_codec(tmp2); */
4000 }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
4001 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
4012 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
4013 const char *mime_type)
4015 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
4018 AVOutputFormat *stream_fmt;
4019 char stream_format_name[64];
4021 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
4022 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4031 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
4035 fprintf(stderr, "%s:%d: ", filename, line_num);
4036 vfprintf(stderr, fmt, vl);
4042 static int parse_ffconfig(const char *filename)
4049 int val, errors, line_num;
4050 FFStream **last_stream, *stream, *redirect;
4051 FFStream **last_feed, *feed, *s;
4052 AVCodecContext audio_enc, video_enc;
4053 enum CodecID audio_id, video_id;
4055 f = fopen(filename, "r");
4063 first_stream = NULL;
4064 last_stream = &first_stream;
4066 last_feed = &first_feed;
4070 audio_id = CODEC_ID_NONE;
4071 video_id = CODEC_ID_NONE;
4073 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4075 if (fgets(line, sizeof(line), f) == NULL)
4081 if (*p == '\0' || *p == '#')
4084 get_arg(cmd, sizeof(cmd), &p);
4086 if (!strcasecmp(cmd, "Port")) {
4087 get_arg(arg, sizeof(arg), &p);
4089 if (val < 1 || val > 65536) {
4090 ERROR("Invalid_port: %s\n", arg);
4092 my_http_addr.sin_port = htons(val);
4093 } else if (!strcasecmp(cmd, "BindAddress")) {
4094 get_arg(arg, sizeof(arg), &p);
4095 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4096 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4098 } else if (!strcasecmp(cmd, "NoDaemon")) {
4099 ffserver_daemon = 0;
4100 } else if (!strcasecmp(cmd, "RTSPPort")) {
4101 get_arg(arg, sizeof(arg), &p);
4103 if (val < 1 || val > 65536) {
4104 ERROR("%s:%d: Invalid port: %s\n", arg);
4106 my_rtsp_addr.sin_port = htons(atoi(arg));
4107 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
4108 get_arg(arg, sizeof(arg), &p);
4109 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4110 ERROR("Invalid host/IP address: %s\n", arg);
4112 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
4113 get_arg(arg, sizeof(arg), &p);
4115 if (val < 1 || val > 65536) {
4116 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4118 nb_max_http_connections = val;
4119 } else if (!strcasecmp(cmd, "MaxClients")) {
4120 get_arg(arg, sizeof(arg), &p);
4122 if (val < 1 || val > nb_max_http_connections) {
4123 ERROR("Invalid MaxClients: %s\n", arg);
4125 nb_max_connections = val;
4127 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
4129 get_arg(arg, sizeof(arg), &p);
4131 if (llval < 10 || llval > 10000000) {
4132 ERROR("Invalid MaxBandwidth: %s\n", arg);
4134 max_bandwidth = llval;
4135 } else if (!strcasecmp(cmd, "CustomLog")) {
4136 if (!ffserver_debug)
4137 get_arg(logfilename, sizeof(logfilename), &p);
4138 } else if (!strcasecmp(cmd, "<Feed")) {
4139 /*********************************************/
4140 /* Feed related options */
4142 if (stream || feed) {
4143 ERROR("Already in a tag\n");
4145 feed = av_mallocz(sizeof(FFStream));
4146 get_arg(feed->filename, sizeof(feed->filename), &p);
4147 q = strrchr(feed->filename, '>');
4151 for (s = first_feed; s; s = s->next) {
4152 if (!strcmp(feed->filename, s->filename)) {
4153 ERROR("Feed '%s' already registered\n", s->filename);
4157 feed->fmt = av_guess_format("ffm", NULL, NULL);
4158 /* defaut feed file */
4159 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4160 "/tmp/%s.ffm", feed->filename);
4161 feed->feed_max_size = 5 * 1024 * 1024;
4163 feed->feed = feed; /* self feeding :-) */
4165 /* add in stream list */
4166 *last_stream = feed;
4167 last_stream = &feed->next;
4168 /* add in feed list */
4170 last_feed = &feed->next_feed;
4172 } else if (!strcasecmp(cmd, "Launch")) {
4176 feed->child_argv = av_mallocz(64 * sizeof(char *));
4178 for (i = 0; i < 62; i++) {
4179 get_arg(arg, sizeof(arg), &p);
4183 feed->child_argv[i] = av_strdup(arg);
4186 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4188 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4190 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4191 inet_ntoa(my_http_addr.sin_addr),
4192 ntohs(my_http_addr.sin_port), feed->filename);
4194 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
4196 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4198 } else if (stream) {
4199 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4201 } else if (!strcasecmp(cmd, "File")) {
4203 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4205 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4206 } else if (!strcasecmp(cmd, "Truncate")) {
4208 get_arg(arg, sizeof(arg), &p);
4209 feed->truncate = strtod(arg, NULL);
4211 } else if (!strcasecmp(cmd, "FileMaxSize")) {
4216 get_arg(arg, sizeof(arg), &p);
4218 fsize = strtod(p1, &p1);
4219 switch(toupper(*p1)) {
4224 fsize *= 1024 * 1024;
4227 fsize *= 1024 * 1024 * 1024;
4230 feed->feed_max_size = (int64_t)fsize;
4231 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4232 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4235 } else if (!strcasecmp(cmd, "</Feed>")) {
4237 ERROR("No corresponding <Feed> for </Feed>\n");
4240 } else if (!strcasecmp(cmd, "<Stream")) {
4241 /*********************************************/
4242 /* Stream related options */
4244 if (stream || feed) {
4245 ERROR("Already in a tag\n");
4248 stream = av_mallocz(sizeof(FFStream));
4249 get_arg(stream->filename, sizeof(stream->filename), &p);
4250 q = strrchr(stream->filename, '>');
4254 for (s = first_stream; s; s = s->next) {
4255 if (!strcmp(stream->filename, s->filename)) {
4256 ERROR("Stream '%s' already registered\n", s->filename);
4260 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4261 avcodec_get_context_defaults2(&video_enc, AVMEDIA_TYPE_VIDEO);
4262 avcodec_get_context_defaults2(&audio_enc, AVMEDIA_TYPE_AUDIO);
4263 audio_id = CODEC_ID_NONE;
4264 video_id = CODEC_ID_NONE;
4266 audio_id = stream->fmt->audio_codec;
4267 video_id = stream->fmt->video_codec;
4270 *last_stream = stream;
4271 last_stream = &stream->next;
4273 } else if (!strcasecmp(cmd, "Feed")) {
4274 get_arg(arg, sizeof(arg), &p);
4279 while (sfeed != NULL) {
4280 if (!strcmp(sfeed->filename, arg))
4282 sfeed = sfeed->next_feed;
4285 ERROR("feed '%s' not defined\n", arg);
4287 stream->feed = sfeed;
4289 } else if (!strcasecmp(cmd, "Format")) {
4290 get_arg(arg, sizeof(arg), &p);
4292 if (!strcmp(arg, "status")) {
4293 stream->stream_type = STREAM_TYPE_STATUS;
4296 stream->stream_type = STREAM_TYPE_LIVE;
4297 /* jpeg cannot be used here, so use single frame jpeg */
4298 if (!strcmp(arg, "jpeg"))
4299 strcpy(arg, "mjpeg");
4300 stream->fmt = ffserver_guess_format(arg, NULL, NULL);
4302 ERROR("Unknown Format: %s\n", arg);
4306 audio_id = stream->fmt->audio_codec;
4307 video_id = stream->fmt->video_codec;
4310 } else if (!strcasecmp(cmd, "InputFormat")) {
4311 get_arg(arg, sizeof(arg), &p);
4313 stream->ifmt = av_find_input_format(arg);
4314 if (!stream->ifmt) {
4315 ERROR("Unknown input format: %s\n", arg);
4318 } else if (!strcasecmp(cmd, "FaviconURL")) {
4319 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4320 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4322 ERROR("FaviconURL only permitted for status streams\n");
4324 } else if (!strcasecmp(cmd, "Author")) {
4326 get_arg(stream->author, sizeof(stream->author), &p);
4327 } else if (!strcasecmp(cmd, "Comment")) {
4329 get_arg(stream->comment, sizeof(stream->comment), &p);
4330 } else if (!strcasecmp(cmd, "Copyright")) {
4332 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4333 } else if (!strcasecmp(cmd, "Title")) {
4335 get_arg(stream->title, sizeof(stream->title), &p);
4336 } else if (!strcasecmp(cmd, "Preroll")) {
4337 get_arg(arg, sizeof(arg), &p);
4339 stream->prebuffer = atof(arg) * 1000;
4340 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4342 stream->send_on_key = 1;
4343 } else if (!strcasecmp(cmd, "AudioCodec")) {
4344 get_arg(arg, sizeof(arg), &p);
4345 audio_id = opt_audio_codec(arg);
4346 if (audio_id == CODEC_ID_NONE) {
4347 ERROR("Unknown AudioCodec: %s\n", arg);
4349 } else if (!strcasecmp(cmd, "VideoCodec")) {
4350 get_arg(arg, sizeof(arg), &p);
4351 video_id = opt_video_codec(arg);
4352 if (video_id == CODEC_ID_NONE) {
4353 ERROR("Unknown VideoCodec: %s\n", arg);
4355 } else if (!strcasecmp(cmd, "MaxTime")) {
4356 get_arg(arg, sizeof(arg), &p);
4358 stream->max_time = atof(arg) * 1000;
4359 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4360 get_arg(arg, sizeof(arg), &p);
4362 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4363 } else if (!strcasecmp(cmd, "AudioChannels")) {
4364 get_arg(arg, sizeof(arg), &p);
4366 audio_enc.channels = atoi(arg);
4367 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4368 get_arg(arg, sizeof(arg), &p);
4370 audio_enc.sample_rate = atoi(arg);
4371 } else if (!strcasecmp(cmd, "AudioQuality")) {
4372 get_arg(arg, sizeof(arg), &p);
4374 // audio_enc.quality = atof(arg) * 1000;
4376 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4378 int minrate, maxrate;
4380 get_arg(arg, sizeof(arg), &p);
4382 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4383 video_enc.rc_min_rate = minrate * 1000;
4384 video_enc.rc_max_rate = maxrate * 1000;
4386 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4389 } else if (!strcasecmp(cmd, "Debug")) {
4391 get_arg(arg, sizeof(arg), &p);
4392 video_enc.debug = strtol(arg,0,0);
4394 } else if (!strcasecmp(cmd, "Strict")) {
4396 get_arg(arg, sizeof(arg), &p);
4397 video_enc.strict_std_compliance = atoi(arg);
4399 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4401 get_arg(arg, sizeof(arg), &p);
4402 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4404 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4406 get_arg(arg, sizeof(arg), &p);
4407 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4409 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4410 get_arg(arg, sizeof(arg), &p);
4412 video_enc.bit_rate = atoi(arg) * 1000;
4414 } else if (!strcasecmp(cmd, "VideoSize")) {
4415 get_arg(arg, sizeof(arg), &p);
4417 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4418 if ((video_enc.width % 16) != 0 ||
4419 (video_enc.height % 16) != 0) {
4420 ERROR("Image size must be a multiple of 16\n");
4423 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4424 get_arg(arg, sizeof(arg), &p);
4426 AVRational frame_rate;
4427 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4428 ERROR("Incorrect frame rate: %s\n", arg);
4430 video_enc.time_base.num = frame_rate.den;
4431 video_enc.time_base.den = frame_rate.num;
4434 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4435 get_arg(arg, sizeof(arg), &p);
4437 video_enc.gop_size = atoi(arg);
4438 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4440 video_enc.gop_size = 1;
4441 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4443 video_enc.mb_decision = FF_MB_DECISION_BITS;
4444 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4446 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4447 video_enc.flags |= CODEC_FLAG_4MV;
4449 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4450 !strcasecmp(cmd, "AVOptionAudio")) {
4452 AVCodecContext *avctx;
4454 get_arg(arg, sizeof(arg), &p);
4455 get_arg(arg2, sizeof(arg2), &p);
4456 if (!strcasecmp(cmd, "AVOptionVideo")) {
4458 type = AV_OPT_FLAG_VIDEO_PARAM;
4461 type = AV_OPT_FLAG_AUDIO_PARAM;
4463 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4464 ERROR("AVOption error: %s %s\n", arg, arg2);
4466 } else if (!strcasecmp(cmd, "AVPresetVideo") ||
4467 !strcasecmp(cmd, "AVPresetAudio")) {
4468 AVCodecContext *avctx;
4470 get_arg(arg, sizeof(arg), &p);
4471 if (!strcasecmp(cmd, "AVPresetVideo")) {
4473 video_enc.codec_id = video_id;
4474 type = AV_OPT_FLAG_VIDEO_PARAM;
4477 audio_enc.codec_id = audio_id;
4478 type = AV_OPT_FLAG_AUDIO_PARAM;
4480 if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4481 ERROR("AVPreset error: %s\n", arg);
4483 } else if (!strcasecmp(cmd, "VideoTag")) {
4484 get_arg(arg, sizeof(arg), &p);
4485 if ((strlen(arg) == 4) && stream)
4486 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4487 } else if (!strcasecmp(cmd, "BitExact")) {
4489 video_enc.flags |= CODEC_FLAG_BITEXACT;
4490 } else if (!strcasecmp(cmd, "DctFastint")) {
4492 video_enc.dct_algo = FF_DCT_FASTINT;
4493 } else if (!strcasecmp(cmd, "IdctSimple")) {
4495 video_enc.idct_algo = FF_IDCT_SIMPLE;
4496 } else if (!strcasecmp(cmd, "Qscale")) {
4497 get_arg(arg, sizeof(arg), &p);
4499 video_enc.flags |= CODEC_FLAG_QSCALE;
4500 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4502 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4503 get_arg(arg, sizeof(arg), &p);
4505 video_enc.max_qdiff = atoi(arg);
4506 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4507 ERROR("VideoQDiff out of range\n");
4510 } else if (!strcasecmp(cmd, "VideoQMax")) {
4511 get_arg(arg, sizeof(arg), &p);
4513 video_enc.qmax = atoi(arg);
4514 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4515 ERROR("VideoQMax out of range\n");
4518 } else if (!strcasecmp(cmd, "VideoQMin")) {
4519 get_arg(arg, sizeof(arg), &p);
4521 video_enc.qmin = atoi(arg);
4522 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4523 ERROR("VideoQMin out of range\n");
4526 } else if (!strcasecmp(cmd, "LumaElim")) {
4527 get_arg(arg, sizeof(arg), &p);
4529 video_enc.luma_elim_threshold = atoi(arg);
4530 } else if (!strcasecmp(cmd, "ChromaElim")) {
4531 get_arg(arg, sizeof(arg), &p);
4533 video_enc.chroma_elim_threshold = atoi(arg);
4534 } else if (!strcasecmp(cmd, "LumiMask")) {
4535 get_arg(arg, sizeof(arg), &p);
4537 video_enc.lumi_masking = atof(arg);
4538 } else if (!strcasecmp(cmd, "DarkMask")) {
4539 get_arg(arg, sizeof(arg), &p);
4541 video_enc.dark_masking = atof(arg);
4542 } else if (!strcasecmp(cmd, "NoVideo")) {
4543 video_id = CODEC_ID_NONE;
4544 } else if (!strcasecmp(cmd, "NoAudio")) {
4545 audio_id = CODEC_ID_NONE;
4546 } else if (!strcasecmp(cmd, "ACL")) {
4547 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4548 } else if (!strcasecmp(cmd, "DynamicACL")) {
4550 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4552 } else if (!strcasecmp(cmd, "RTSPOption")) {
4553 get_arg(arg, sizeof(arg), &p);
4555 av_freep(&stream->rtsp_option);
4556 stream->rtsp_option = av_strdup(arg);
4558 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4559 get_arg(arg, sizeof(arg), &p);
4561 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4562 ERROR("Invalid host/IP address: %s\n", arg);
4564 stream->is_multicast = 1;
4565 stream->loop = 1; /* default is looping */
4567 } else if (!strcasecmp(cmd, "MulticastPort")) {
4568 get_arg(arg, sizeof(arg), &p);
4570 stream->multicast_port = atoi(arg);
4571 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4572 get_arg(arg, sizeof(arg), &p);
4574 stream->multicast_ttl = atoi(arg);
4575 } else if (!strcasecmp(cmd, "NoLoop")) {
4578 } else if (!strcasecmp(cmd, "</Stream>")) {
4580 ERROR("No corresponding <Stream> for </Stream>\n");
4582 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4583 if (audio_id != CODEC_ID_NONE) {
4584 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4585 audio_enc.codec_id = audio_id;
4586 add_codec(stream, &audio_enc);
4588 if (video_id != CODEC_ID_NONE) {
4589 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4590 video_enc.codec_id = video_id;
4591 add_codec(stream, &video_enc);
4596 } else if (!strcasecmp(cmd, "<Redirect")) {
4597 /*********************************************/
4599 if (stream || feed || redirect) {
4600 ERROR("Already in a tag\n");
4602 redirect = av_mallocz(sizeof(FFStream));
4603 *last_stream = redirect;
4604 last_stream = &redirect->next;
4606 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4607 q = strrchr(redirect->filename, '>');
4610 redirect->stream_type = STREAM_TYPE_REDIRECT;
4612 } else if (!strcasecmp(cmd, "URL")) {
4614 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4615 } else if (!strcasecmp(cmd, "</Redirect>")) {
4617 ERROR("No corresponding <Redirect> for </Redirect>\n");
4619 if (!redirect->feed_filename[0]) {
4620 ERROR("No URL found for <Redirect>\n");
4624 } else if (!strcasecmp(cmd, "LoadModule")) {
4625 get_arg(arg, sizeof(arg), &p);
4629 ERROR("Module support not compiled into this version: '%s'\n", arg);
4632 ERROR("Incorrect keyword: '%s'\n", cmd);
4644 static void handle_child_exit(int sig)
4649 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4652 for (feed = first_feed; feed; feed = feed->next) {
4653 if (feed->pid == pid) {
4654 int uptime = time(0) - feed->pid_start;
4657 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4660 /* Turn off any more restarts */
4661 feed->child_argv = 0;
4666 need_to_start_children = 1;
4669 static void opt_debug(void)
4672 ffserver_daemon = 0;
4673 logfilename[0] = '-';
4676 static void show_help(void)
4678 printf("usage: ffserver [options]\n"
4679 "Hyper fast multi format Audio/Video streaming server\n");
4681 show_help_options(options, "Main options:\n", 0, 0);
4684 static const OptionDef options[] = {
4685 #include "cmdutils_common_opts.h"
4686 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4687 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4688 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4692 int main(int argc, char **argv)
4694 struct sigaction sigact;
4700 my_program_name = argv[0];
4701 my_program_dir = getcwd(0, 0);
4702 ffserver_daemon = 1;
4704 parse_options(argc, argv, options, NULL);
4706 unsetenv("http_proxy"); /* Kill the http_proxy */
4708 av_lfg_init(&random_state, av_get_random_seed());
4710 memset(&sigact, 0, sizeof(sigact));
4711 sigact.sa_handler = handle_child_exit;
4712 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4713 sigaction(SIGCHLD, &sigact, 0);
4715 if (parse_ffconfig(config_filename) < 0) {
4716 fprintf(stderr, "Incorrect config file - exiting.\n");
4720 /* open log file if needed */
4721 if (logfilename[0] != '\0') {
4722 if (!strcmp(logfilename, "-"))
4725 logfile = fopen(logfilename, "a");
4726 av_log_set_callback(http_av_log);
4729 build_file_streams();
4731 build_feed_streams();
4733 compute_bandwidth();
4735 /* put the process in background and detach it from its TTY */
4736 if (ffserver_daemon) {
4743 } else if (pid > 0) {
4750 open("/dev/null", O_RDWR);
4751 if (strcmp(logfilename, "-") != 0) {
4761 signal(SIGPIPE, SIG_IGN);
4763 if (ffserver_daemon)
4766 if (http_server() < 0) {
4767 http_log("Could not start server\n");