2 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 * multiple format streaming server based on the FFmpeg libraries
28 #define closesocket close
33 #include "libavformat/avformat.h"
34 // FIXME those are internal headers, ffserver _really_ shouldn't use them
35 #include "libavformat/ffm.h"
36 #include "libavformat/network.h"
37 #include "libavformat/os_support.h"
38 #include "libavformat/rtpdec.h"
39 #include "libavformat/rtpproto.h"
40 #include "libavformat/rtsp.h"
41 #include "libavformat/avio_internal.h"
42 #include "libavformat/internal.h"
43 #include "libavformat/url.h"
45 #include "libavutil/avassert.h"
46 #include "libavutil/avstring.h"
47 #include "libavutil/lfg.h"
48 #include "libavutil/dict.h"
49 #include "libavutil/intreadwrite.h"
50 #include "libavutil/mathematics.h"
51 #include "libavutil/pixdesc.h"
52 #include "libavutil/random_seed.h"
53 #include "libavutil/parseutils.h"
54 #include "libavutil/opt.h"
55 #include "libavutil/time.h"
60 #include <sys/ioctl.h>
71 const char program_name[] = "ffserver";
72 const int program_birth_year = 2000;
74 static const OptionDef options[];
77 HTTPSTATE_WAIT_REQUEST,
78 HTTPSTATE_SEND_HEADER,
79 HTTPSTATE_SEND_DATA_HEADER,
80 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
81 HTTPSTATE_SEND_DATA_TRAILER,
82 HTTPSTATE_RECEIVE_DATA,
83 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
86 RTSPSTATE_WAIT_REQUEST,
88 RTSPSTATE_SEND_PACKET,
91 static const char *http_state[] = {
107 #define MAX_STREAMS 20
109 #define IOBUFFER_INIT_SIZE 8192
111 /* timeouts are in ms */
112 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
113 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
115 #define SYNC_TIMEOUT (10 * 1000)
117 typedef struct RTSPActionServerSetup {
119 char transport_option[512];
120 } RTSPActionServerSetup;
123 int64_t count1, count2;
124 int64_t time1, time2;
127 /* context associated with one connection */
128 typedef struct HTTPContext {
129 enum HTTPState state;
130 int fd; /* socket file descriptor */
131 struct sockaddr_in from_addr; /* origin */
132 struct pollfd *poll_entry; /* used when polling */
134 uint8_t *buffer_ptr, *buffer_end;
137 int chunked_encoding;
138 int chunk_size; /* 0 if it needs to be read */
139 struct HTTPContext *next;
140 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
144 /* input format handling */
145 AVFormatContext *fmt_in;
146 int64_t start_time; /* In milliseconds - this wraps fairly often */
147 int64_t first_pts; /* initial pts value */
148 int64_t cur_pts; /* current pts value from the stream in us */
149 int64_t cur_frame_duration; /* duration of the current frame in us */
150 int cur_frame_bytes; /* output frame size, needed to compute
151 the time at which we send each
153 int pts_stream_index; /* stream we choose as clock reference */
154 int64_t cur_clock; /* current clock reference value in us */
155 /* output format handling */
156 struct FFStream *stream;
157 /* -1 is invalid stream */
158 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
159 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
161 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
162 int last_packet_sent; /* true if last data packet was sent */
164 DataRateData datarate;
171 int is_packetized; /* if true, the stream is packetized */
172 int packet_stream_index; /* current stream for output in state machine */
174 /* RTSP state specific */
175 uint8_t *pb_buffer; /* XXX: use that in all the code */
177 int seq; /* RTSP sequence number */
179 /* RTP state specific */
180 enum RTSPLowerTransport rtp_protocol;
181 char session_id[32]; /* session id */
182 AVFormatContext *rtp_ctx[MAX_STREAMS];
184 /* RTP/UDP specific */
185 URLContext *rtp_handles[MAX_STREAMS];
187 /* RTP/TCP specific */
188 struct HTTPContext *rtsp_c;
189 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
192 /* each generated stream is described here */
196 STREAM_TYPE_REDIRECT,
199 enum IPAddressAction {
204 typedef struct IPAddressACL {
205 struct IPAddressACL *next;
206 enum IPAddressAction action;
207 /* These are in host order */
208 struct in_addr first;
212 /* description of each stream of the ffserver.conf file */
213 typedef struct FFStream {
214 enum StreamType stream_type;
215 char filename[1024]; /* stream filename */
216 struct FFStream *feed; /* feed we are using (can be null if
218 AVDictionary *in_opts; /* input parameters */
219 AVDictionary *metadata; /* metadata to set on the stream */
220 AVInputFormat *ifmt; /* if non NULL, force input format */
223 char dynamic_acl[1024];
225 int prebuffer; /* Number of millseconds early to start */
226 int64_t max_time; /* Number of milliseconds to run */
228 AVStream *streams[MAX_STREAMS];
229 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
230 char feed_filename[1024]; /* file name of the feed storage, or
231 input file name for a stream */
232 pid_t pid; /* Of ffmpeg process */
233 time_t pid_start; /* Of ffmpeg process */
235 struct FFStream *next;
236 unsigned bandwidth; /* bandwidth, in kbits/s */
239 /* multicast specific */
241 struct in_addr multicast_ip;
242 int multicast_port; /* first port used for multicast */
244 int loop; /* if true, send the stream in loops (only meaningful if file) */
247 int feed_opened; /* true if someone is writing to the feed */
248 int is_feed; /* true if it is a feed */
249 int readonly; /* True if writing is prohibited to the file */
250 int truncate; /* True if feeder connection truncate the feed file */
252 int64_t bytes_served;
253 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
254 int64_t feed_write_index; /* current write position in feed (it wraps around) */
255 int64_t feed_size; /* current size of feed */
256 struct FFStream *next_feed;
259 typedef struct FeedData {
260 long long data_count;
261 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
264 static struct sockaddr_in my_http_addr;
265 static struct sockaddr_in my_rtsp_addr;
267 static char logfilename[1024];
268 static HTTPContext *first_http_ctx;
269 static FFStream *first_feed; /* contains only feeds */
270 static FFStream *first_stream; /* contains all streams, including feeds */
272 static void new_connection(int server_fd, int is_rtsp);
273 static void close_connection(HTTPContext *c);
276 static int handle_connection(HTTPContext *c);
277 static int http_parse_request(HTTPContext *c);
278 static int http_send_data(HTTPContext *c);
279 static void compute_status(HTTPContext *c);
280 static int open_input_stream(HTTPContext *c, const char *info);
281 static int http_start_receive_data(HTTPContext *c);
282 static int http_receive_data(HTTPContext *c);
285 static int rtsp_parse_request(HTTPContext *c);
286 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
287 static void rtsp_cmd_options(HTTPContext *c, const char *url);
288 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
289 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
290 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
291 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
294 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
295 struct in_addr my_ip);
298 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
299 FFStream *stream, const char *session_id,
300 enum RTSPLowerTransport rtp_protocol);
301 static int rtp_new_av_stream(HTTPContext *c,
302 int stream_index, struct sockaddr_in *dest_addr,
303 HTTPContext *rtsp_c);
305 static const char *my_program_name;
307 static const char *config_filename;
309 static int ffserver_debug;
310 static int no_launch;
311 static int need_to_start_children;
313 /* maximum number of simultaneous HTTP connections */
314 static unsigned int nb_max_http_connections = 2000;
315 static unsigned int nb_max_connections = 5;
316 static unsigned int nb_connections;
318 static uint64_t max_bandwidth = 1000;
319 static uint64_t current_bandwidth;
321 static int64_t cur_time; // Making this global saves on passing it around everywhere
323 static AVLFG random_state;
325 static FILE *logfile = NULL;
327 static void htmlstrip(char *s) {
329 s += strspn(s, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,. ");
335 static int64_t ffm_read_write_index(int fd)
339 if (lseek(fd, 8, SEEK_SET) < 0)
341 if (read(fd, buf, 8) != 8)
346 static int ffm_write_write_index(int fd, int64_t pos)
352 buf[i] = (pos >> (56 - i * 8)) & 0xff;
353 if (lseek(fd, 8, SEEK_SET) < 0)
355 if (write(fd, buf, 8) != 8)
360 static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
363 FFMContext *ffm = s->priv_data;
364 ffm->write_index = pos;
365 ffm->file_size = file_size;
368 /* FIXME: make ffserver work with IPv6 */
369 /* resolve host with also IP address parsing */
370 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
373 if (!ff_inet_aton(hostname, sin_addr)) {
375 struct addrinfo *ai, *cur;
376 struct addrinfo hints = { 0 };
377 hints.ai_family = AF_INET;
378 if (getaddrinfo(hostname, NULL, &hints, &ai))
380 /* getaddrinfo returns a linked list of addrinfo structs.
381 * Even if we set ai_family = AF_INET above, make sure
382 * that the returned one actually is of the correct type. */
383 for (cur = ai; cur; cur = cur->ai_next) {
384 if (cur->ai_family == AF_INET) {
385 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
394 hp = gethostbyname(hostname);
397 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
403 static char *ctime1(char *buf2, int buf_size)
410 av_strlcpy(buf2, p, buf_size);
411 p = buf2 + strlen(p) - 1;
417 static void http_vlog(const char *fmt, va_list vargs)
419 static int print_prefix = 1;
423 ctime1(buf, sizeof(buf));
424 fprintf(logfile, "%s ", buf);
426 print_prefix = strstr(fmt, "\n") != NULL;
427 vfprintf(logfile, fmt, vargs);
433 __attribute__ ((format (printf, 1, 2)))
435 static void http_log(const char *fmt, ...)
438 va_start(vargs, fmt);
439 http_vlog(fmt, vargs);
443 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
445 static int print_prefix = 1;
446 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
447 if (level > av_log_get_level())
449 if (print_prefix && avc)
450 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
451 print_prefix = strstr(fmt, "\n") != NULL;
452 http_vlog(fmt, vargs);
455 static void log_connection(HTTPContext *c)
460 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
461 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
462 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
465 static void update_datarate(DataRateData *drd, int64_t count)
467 if (!drd->time1 && !drd->count1) {
468 drd->time1 = drd->time2 = cur_time;
469 drd->count1 = drd->count2 = count;
470 } else if (cur_time - drd->time2 > 5000) {
471 drd->time1 = drd->time2;
472 drd->count1 = drd->count2;
473 drd->time2 = cur_time;
478 /* In bytes per second */
479 static int compute_datarate(DataRateData *drd, int64_t count)
481 if (cur_time == drd->time1)
484 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
488 static void start_children(FFStream *feed)
493 for (; feed; feed = feed->next) {
494 if (feed->child_argv && !feed->pid) {
495 feed->pid_start = time(0);
500 http_log("Unable to create children\n");
509 /* replace "ffserver" with "ffmpeg" in the path of current
510 * program. Ignore user provided path */
511 av_strlcpy(pathname, my_program_name, sizeof(pathname));
512 slash = strrchr(pathname, '/');
517 strcpy(slash, "ffmpeg");
519 http_log("Launch command line: ");
520 http_log("%s ", pathname);
521 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
522 http_log("%s ", feed->child_argv[i]);
525 for (i = 3; i < 256; i++)
528 if (!ffserver_debug) {
529 if (!freopen("/dev/null", "r", stdin))
530 http_log("failed to redirect STDIN to /dev/null\n;");
531 if (!freopen("/dev/null", "w", stdout))
532 http_log("failed to redirect STDOUT to /dev/null\n;");
533 if (!freopen("/dev/null", "w", stderr))
534 http_log("failed to redirect STDERR to /dev/null\n;");
537 signal(SIGPIPE, SIG_DFL);
539 execvp(pathname, feed->child_argv);
547 /* open a listening socket */
548 static int socket_open_listen(struct sockaddr_in *my_addr)
552 server_fd = socket(AF_INET,SOCK_STREAM,0);
559 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
561 my_addr->sin_family = AF_INET;
562 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
564 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
566 closesocket(server_fd);
570 if (listen (server_fd, 5) < 0) {
572 closesocket(server_fd);
575 ff_socket_nonblock(server_fd, 1);
580 /* start all multicast streams */
581 static void start_multicast(void)
586 struct sockaddr_in dest_addr = {0};
587 int default_port, stream_index;
590 for(stream = first_stream; stream != NULL; stream = stream->next) {
591 if (stream->is_multicast) {
592 unsigned random0 = av_lfg_get(&random_state);
593 unsigned random1 = av_lfg_get(&random_state);
594 /* open the RTP connection */
595 snprintf(session_id, sizeof(session_id), "%08x%08x",
598 /* choose a port if none given */
599 if (stream->multicast_port == 0) {
600 stream->multicast_port = default_port;
604 dest_addr.sin_family = AF_INET;
605 dest_addr.sin_addr = stream->multicast_ip;
606 dest_addr.sin_port = htons(stream->multicast_port);
608 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
609 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
613 if (open_input_stream(rtp_c, "") < 0) {
614 http_log("Could not open input stream for stream '%s'\n",
619 /* open each RTP stream */
620 for(stream_index = 0; stream_index < stream->nb_streams;
622 dest_addr.sin_port = htons(stream->multicast_port +
624 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
625 http_log("Could not open output stream '%s/streamid=%d'\n",
626 stream->filename, stream_index);
631 rtp_c->state = HTTPSTATE_SEND_DATA;
636 /* main loop of the HTTP server */
637 static int http_server(void)
639 int server_fd = 0, rtsp_server_fd = 0;
641 struct pollfd *poll_table, *poll_entry;
642 HTTPContext *c, *c_next;
644 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
645 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
649 if (my_http_addr.sin_port) {
650 server_fd = socket_open_listen(&my_http_addr);
655 if (my_rtsp_addr.sin_port) {
656 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
657 if (rtsp_server_fd < 0)
661 if (!rtsp_server_fd && !server_fd) {
662 http_log("HTTP and RTSP disabled.\n");
666 http_log("FFserver started.\n");
668 start_children(first_feed);
673 poll_entry = poll_table;
675 poll_entry->fd = server_fd;
676 poll_entry->events = POLLIN;
679 if (rtsp_server_fd) {
680 poll_entry->fd = rtsp_server_fd;
681 poll_entry->events = POLLIN;
685 /* wait for events on each HTTP handle */
692 case HTTPSTATE_SEND_HEADER:
693 case RTSPSTATE_SEND_REPLY:
694 case RTSPSTATE_SEND_PACKET:
695 c->poll_entry = poll_entry;
697 poll_entry->events = POLLOUT;
700 case HTTPSTATE_SEND_DATA_HEADER:
701 case HTTPSTATE_SEND_DATA:
702 case HTTPSTATE_SEND_DATA_TRAILER:
703 if (!c->is_packetized) {
704 /* for TCP, we output as much as we can
705 * (may need to put a limit) */
706 c->poll_entry = poll_entry;
708 poll_entry->events = POLLOUT;
711 /* when ffserver is doing the timing, we work by
712 looking at which packet needs to be sent every
714 /* one tick wait XXX: 10 ms assumed */
719 case HTTPSTATE_WAIT_REQUEST:
720 case HTTPSTATE_RECEIVE_DATA:
721 case HTTPSTATE_WAIT_FEED:
722 case RTSPSTATE_WAIT_REQUEST:
723 /* need to catch errors */
724 c->poll_entry = poll_entry;
726 poll_entry->events = POLLIN;/* Maybe this will work */
730 c->poll_entry = NULL;
736 /* wait for an event on one connection. We poll at least every
737 second to handle timeouts */
739 ret = poll(poll_table, poll_entry - poll_table, delay);
740 if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
741 ff_neterrno() != AVERROR(EINTR))
745 cur_time = av_gettime() / 1000;
747 if (need_to_start_children) {
748 need_to_start_children = 0;
749 start_children(first_feed);
752 /* now handle the events */
753 for(c = first_http_ctx; c != NULL; c = c_next) {
755 if (handle_connection(c) < 0) {
757 /* close and free the connection */
762 poll_entry = poll_table;
764 /* new HTTP connection request ? */
765 if (poll_entry->revents & POLLIN)
766 new_connection(server_fd, 0);
769 if (rtsp_server_fd) {
770 /* new RTSP connection request ? */
771 if (poll_entry->revents & POLLIN)
772 new_connection(rtsp_server_fd, 1);
777 /* start waiting for a new HTTP/RTSP request */
778 static void start_wait_request(HTTPContext *c, int is_rtsp)
780 c->buffer_ptr = c->buffer;
781 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
784 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
785 c->state = RTSPSTATE_WAIT_REQUEST;
787 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
788 c->state = HTTPSTATE_WAIT_REQUEST;
792 static void http_send_too_busy_reply(int fd)
795 int len = snprintf(buffer, sizeof(buffer),
796 "HTTP/1.0 503 Server too busy\r\n"
797 "Content-type: text/html\r\n"
799 "<html><head><title>Too busy</title></head><body>\r\n"
800 "<p>The server is too busy to serve your request at this time.</p>\r\n"
801 "<p>The number of current connections is %u, and this exceeds the limit of %u.</p>\r\n"
802 "</body></html>\r\n",
803 nb_connections, nb_max_connections);
804 av_assert0(len < sizeof(buffer));
805 send(fd, buffer, len, 0);
809 static void new_connection(int server_fd, int is_rtsp)
811 struct sockaddr_in from_addr;
814 HTTPContext *c = NULL;
816 len = sizeof(from_addr);
817 fd = accept(server_fd, (struct sockaddr *)&from_addr,
820 http_log("error during accept %s\n", strerror(errno));
823 ff_socket_nonblock(fd, 1);
825 if (nb_connections >= nb_max_connections) {
826 http_send_too_busy_reply(fd);
830 /* add a new connection */
831 c = av_mallocz(sizeof(HTTPContext));
836 c->poll_entry = NULL;
837 c->from_addr = from_addr;
838 c->buffer_size = IOBUFFER_INIT_SIZE;
839 c->buffer = av_malloc(c->buffer_size);
843 c->next = first_http_ctx;
847 start_wait_request(c, is_rtsp);
859 static void close_connection(HTTPContext *c)
861 HTTPContext **cp, *c1;
863 AVFormatContext *ctx;
867 /* remove connection from list */
868 cp = &first_http_ctx;
869 while ((*cp) != NULL) {
877 /* remove references, if any (XXX: do it faster) */
878 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
883 /* remove connection associated resources */
887 /* close each frame parser */
888 for(i=0;i<c->fmt_in->nb_streams;i++) {
889 st = c->fmt_in->streams[i];
890 if (st->codec->codec)
891 avcodec_close(st->codec);
893 avformat_close_input(&c->fmt_in);
896 /* free RTP output streams if any */
899 nb_streams = c->stream->nb_streams;
901 for(i=0;i<nb_streams;i++) {
904 av_write_trailer(ctx);
905 av_dict_free(&ctx->metadata);
906 av_free(ctx->streams[0]);
909 h = c->rtp_handles[i];
916 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
919 if (avio_open_dyn_buf(&ctx->pb) >= 0) {
920 av_write_trailer(ctx);
921 av_freep(&c->pb_buffer);
922 avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
927 for(i=0; i<ctx->nb_streams; i++)
928 av_free(ctx->streams[i]);
929 av_freep(&ctx->streams);
930 av_freep(&ctx->priv_data);
932 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
933 current_bandwidth -= c->stream->bandwidth;
935 /* signal that there is no feed if we are the feeder socket */
936 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
937 c->stream->feed_opened = 0;
941 av_freep(&c->pb_buffer);
942 av_freep(&c->packet_buffer);
948 static int handle_connection(HTTPContext *c)
953 case HTTPSTATE_WAIT_REQUEST:
954 case RTSPSTATE_WAIT_REQUEST:
956 if ((c->timeout - cur_time) < 0)
958 if (c->poll_entry->revents & (POLLERR | POLLHUP))
961 /* no need to read if no events */
962 if (!(c->poll_entry->revents & POLLIN))
966 len = recv(c->fd, c->buffer_ptr, 1, 0);
968 if (ff_neterrno() != AVERROR(EAGAIN) &&
969 ff_neterrno() != AVERROR(EINTR))
971 } else if (len == 0) {
974 /* search for end of request. */
976 c->buffer_ptr += len;
978 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
979 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
980 /* request found : parse it and reply */
981 if (c->state == HTTPSTATE_WAIT_REQUEST) {
982 ret = http_parse_request(c);
984 ret = rtsp_parse_request(c);
988 } else if (ptr >= c->buffer_end) {
989 /* request too long: cannot do anything */
991 } else goto read_loop;
995 case HTTPSTATE_SEND_HEADER:
996 if (c->poll_entry->revents & (POLLERR | POLLHUP))
999 /* no need to write if no events */
1000 if (!(c->poll_entry->revents & POLLOUT))
1002 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1004 if (ff_neterrno() != AVERROR(EAGAIN) &&
1005 ff_neterrno() != AVERROR(EINTR)) {
1006 /* error : close connection */
1007 av_freep(&c->pb_buffer);
1011 c->buffer_ptr += len;
1013 c->stream->bytes_served += len;
1014 c->data_count += len;
1015 if (c->buffer_ptr >= c->buffer_end) {
1016 av_freep(&c->pb_buffer);
1017 /* if error, exit */
1020 /* all the buffer was sent : synchronize to the incoming
1022 c->state = HTTPSTATE_SEND_DATA_HEADER;
1023 c->buffer_ptr = c->buffer_end = c->buffer;
1028 case HTTPSTATE_SEND_DATA:
1029 case HTTPSTATE_SEND_DATA_HEADER:
1030 case HTTPSTATE_SEND_DATA_TRAILER:
1031 /* for packetized output, we consider we can always write (the
1032 input streams set the speed). It may be better to verify
1033 that we do not rely too much on the kernel queues */
1034 if (!c->is_packetized) {
1035 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1038 /* no need to read if no events */
1039 if (!(c->poll_entry->revents & POLLOUT))
1042 if (http_send_data(c) < 0)
1044 /* close connection if trailer sent */
1045 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
1048 case HTTPSTATE_RECEIVE_DATA:
1049 /* no need to read if no events */
1050 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1052 if (!(c->poll_entry->revents & POLLIN))
1054 if (http_receive_data(c) < 0)
1057 case HTTPSTATE_WAIT_FEED:
1058 /* no need to read if no events */
1059 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1062 /* nothing to do, we'll be waken up by incoming feed packets */
1065 case RTSPSTATE_SEND_REPLY:
1066 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1067 av_freep(&c->pb_buffer);
1070 /* no need to write if no events */
1071 if (!(c->poll_entry->revents & POLLOUT))
1073 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1075 if (ff_neterrno() != AVERROR(EAGAIN) &&
1076 ff_neterrno() != AVERROR(EINTR)) {
1077 /* error : close connection */
1078 av_freep(&c->pb_buffer);
1082 c->buffer_ptr += len;
1083 c->data_count += len;
1084 if (c->buffer_ptr >= c->buffer_end) {
1085 /* all the buffer was sent : wait for a new request */
1086 av_freep(&c->pb_buffer);
1087 start_wait_request(c, 1);
1091 case RTSPSTATE_SEND_PACKET:
1092 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1093 av_freep(&c->packet_buffer);
1096 /* no need to write if no events */
1097 if (!(c->poll_entry->revents & POLLOUT))
1099 len = send(c->fd, c->packet_buffer_ptr,
1100 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1102 if (ff_neterrno() != AVERROR(EAGAIN) &&
1103 ff_neterrno() != AVERROR(EINTR)) {
1104 /* error : close connection */
1105 av_freep(&c->packet_buffer);
1109 c->packet_buffer_ptr += len;
1110 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1111 /* all the buffer was sent : wait for a new request */
1112 av_freep(&c->packet_buffer);
1113 c->state = RTSPSTATE_WAIT_REQUEST;
1117 case HTTPSTATE_READY:
1126 static int extract_rates(char *rates, int ratelen, const char *request)
1130 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1131 if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1132 const char *q = p + 7;
1134 while (*q && *q != '\n' && av_isspace(*q))
1137 if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1143 memset(rates, 0xff, ratelen);
1146 while (*q && *q != '\n' && *q != ':')
1149 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1153 if (stream_no < ratelen && stream_no >= 0)
1154 rates[stream_no] = rate_no;
1156 while (*q && *q != '\n' && !av_isspace(*q))
1163 p = strchr(p, '\n');
1173 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1176 int best_bitrate = 100000000;
1179 for (i = 0; i < feed->nb_streams; i++) {
1180 AVCodecContext *feed_codec = feed->streams[i]->codec;
1182 if (feed_codec->codec_id != codec->codec_id ||
1183 feed_codec->sample_rate != codec->sample_rate ||
1184 feed_codec->width != codec->width ||
1185 feed_codec->height != codec->height)
1188 /* Potential stream */
1190 /* We want the fastest stream less than bit_rate, or the slowest
1191 * faster than bit_rate
1194 if (feed_codec->bit_rate <= bit_rate) {
1195 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1196 best_bitrate = feed_codec->bit_rate;
1200 if (feed_codec->bit_rate < best_bitrate) {
1201 best_bitrate = feed_codec->bit_rate;
1210 static int modify_current_stream(HTTPContext *c, char *rates)
1213 FFStream *req = c->stream;
1214 int action_required = 0;
1216 /* Not much we can do for a feed */
1220 for (i = 0; i < req->nb_streams; i++) {
1221 AVCodecContext *codec = req->streams[i]->codec;
1225 c->switch_feed_streams[i] = req->feed_streams[i];
1228 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1231 /* Wants off or slow */
1232 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1234 /* This doesn't work well when it turns off the only stream! */
1235 c->switch_feed_streams[i] = -2;
1236 c->feed_streams[i] = -2;
1241 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1242 action_required = 1;
1245 return action_required;
1248 /* XXX: factorize in utils.c ? */
1249 /* XXX: take care with different space meaning */
1250 static void skip_spaces(const char **pp)
1254 while (*p == ' ' || *p == '\t')
1259 static void get_word(char *buf, int buf_size, const char **pp)
1267 while (!av_isspace(*p) && *p != '\0') {
1268 if ((q - buf) < buf_size - 1)
1277 static void get_arg(char *buf, int buf_size, const char **pp)
1284 while (av_isspace(*p)) p++;
1287 if (*p == '\"' || *p == '\'')
1299 if ((q - buf) < buf_size - 1)
1304 if (quote && *p == quote)
1309 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1310 const char *p, const char *filename, int line_num)
1316 get_arg(arg, sizeof(arg), &p);
1317 if (av_strcasecmp(arg, "allow") == 0)
1318 acl.action = IP_ALLOW;
1319 else if (av_strcasecmp(arg, "deny") == 0)
1320 acl.action = IP_DENY;
1322 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1323 filename, line_num, arg);
1327 get_arg(arg, sizeof(arg), &p);
1329 if (resolve_host(&acl.first, arg) != 0) {
1330 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1331 filename, line_num, arg);
1334 acl.last = acl.first;
1336 get_arg(arg, sizeof(arg), &p);
1339 if (resolve_host(&acl.last, arg) != 0) {
1340 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1341 filename, line_num, arg);
1347 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1348 IPAddressACL **naclp = 0;
1354 naclp = &stream->acl;
1360 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1361 filename, line_num);
1367 naclp = &(*naclp)->next;
1375 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1380 IPAddressACL *acl = NULL;
1384 f = fopen(stream->dynamic_acl, "r");
1386 perror(stream->dynamic_acl);
1390 acl = av_mallocz(sizeof(IPAddressACL));
1394 if (fgets(line, sizeof(line), f) == NULL)
1398 while (av_isspace(*p))
1400 if (*p == '\0' || *p == '#')
1402 get_arg(cmd, sizeof(cmd), &p);
1404 if (!av_strcasecmp(cmd, "ACL"))
1405 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1412 static void free_acl_list(IPAddressACL *in_acl)
1414 IPAddressACL *pacl,*pacl2;
1424 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1426 enum IPAddressAction last_action = IP_DENY;
1428 struct in_addr *src = &c->from_addr.sin_addr;
1429 unsigned long src_addr = src->s_addr;
1431 for (acl = in_acl; acl; acl = acl->next) {
1432 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1433 return (acl->action == IP_ALLOW) ? 1 : 0;
1434 last_action = acl->action;
1437 /* Nothing matched, so return not the last action */
1438 return (last_action == IP_DENY) ? 1 : 0;
1441 static int validate_acl(FFStream *stream, HTTPContext *c)
1447 /* if stream->acl is null validate_acl_list will return 1 */
1448 ret = validate_acl_list(stream->acl, c);
1450 if (stream->dynamic_acl[0]) {
1451 acl = parse_dynamic_acl(stream, c);
1453 ret = validate_acl_list(acl, c);
1461 /* compute the real filename of a file by matching it without its
1462 extensions to all the stream's filenames */
1463 static void compute_real_filename(char *filename, int max_size)
1470 /* compute filename by matching without the file extensions */
1471 av_strlcpy(file1, filename, sizeof(file1));
1472 p = strrchr(file1, '.');
1475 for(stream = first_stream; stream != NULL; stream = stream->next) {
1476 av_strlcpy(file2, stream->filename, sizeof(file2));
1477 p = strrchr(file2, '.');
1480 if (!strcmp(file1, file2)) {
1481 av_strlcpy(filename, stream->filename, max_size);
1496 /* parse HTTP request and prepare header */
1497 static int http_parse_request(HTTPContext *c)
1501 enum RedirType redir_type;
1503 char info[1024], filename[1024];
1507 const char *mime_type;
1511 const char *useragent = 0;
1514 get_word(cmd, sizeof(cmd), &p);
1515 av_strlcpy(c->method, cmd, sizeof(c->method));
1517 if (!strcmp(cmd, "GET"))
1519 else if (!strcmp(cmd, "POST"))
1524 get_word(url, sizeof(url), &p);
1525 av_strlcpy(c->url, url, sizeof(c->url));
1527 get_word(protocol, sizeof(protocol), (const char **)&p);
1528 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1531 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1534 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1536 /* find the filename and the optional info string in the request */
1537 p1 = strchr(url, '?');
1539 av_strlcpy(info, p1, sizeof(info));
1544 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1546 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1547 if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1549 if (*useragent && *useragent != '\n' && av_isspace(*useragent))
1553 p = strchr(p, '\n');
1560 redir_type = REDIR_NONE;
1561 if (av_match_ext(filename, "asx")) {
1562 redir_type = REDIR_ASX;
1563 filename[strlen(filename)-1] = 'f';
1564 } else if (av_match_ext(filename, "asf") &&
1565 (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1566 /* if this isn't WMP or lookalike, return the redirector file */
1567 redir_type = REDIR_ASF;
1568 } else if (av_match_ext(filename, "rpm,ram")) {
1569 redir_type = REDIR_RAM;
1570 strcpy(filename + strlen(filename)-2, "m");
1571 } else if (av_match_ext(filename, "rtsp")) {
1572 redir_type = REDIR_RTSP;
1573 compute_real_filename(filename, sizeof(filename) - 1);
1574 } else if (av_match_ext(filename, "sdp")) {
1575 redir_type = REDIR_SDP;
1576 compute_real_filename(filename, sizeof(filename) - 1);
1579 // "redirect" / request to index.html
1580 if (!strlen(filename))
1581 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1583 stream = first_stream;
1584 while (stream != NULL) {
1585 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1587 stream = stream->next;
1589 if (stream == NULL) {
1590 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1591 http_log("File '%s' not found\n", url);
1596 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1597 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1599 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1600 c->http_error = 301;
1602 snprintf(q, c->buffer_size,
1603 "HTTP/1.0 301 Moved\r\n"
1605 "Content-type: text/html\r\n"
1607 "<html><head><title>Moved</title></head><body>\r\n"
1608 "You should be <a href=\"%s\">redirected</a>.\r\n"
1609 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1611 /* prepare output buffer */
1612 c->buffer_ptr = c->buffer;
1614 c->state = HTTPSTATE_SEND_HEADER;
1618 /* If this is WMP, get the rate information */
1619 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1620 if (modify_current_stream(c, ratebuf)) {
1621 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1622 if (c->switch_feed_streams[i] >= 0)
1623 c->switch_feed_streams[i] = -1;
1628 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1629 current_bandwidth += stream->bandwidth;
1631 /* If already streaming this feed, do not let start another feeder. */
1632 if (stream->feed_opened) {
1633 snprintf(msg, sizeof(msg), "This feed is already being received.");
1634 http_log("Feed '%s' already being received\n", stream->feed_filename);
1638 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1639 c->http_error = 503;
1641 snprintf(q, c->buffer_size,
1642 "HTTP/1.0 503 Server too busy\r\n"
1643 "Content-type: text/html\r\n"
1645 "<html><head><title>Too busy</title></head><body>\r\n"
1646 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1647 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1648 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1649 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1651 /* prepare output buffer */
1652 c->buffer_ptr = c->buffer;
1654 c->state = HTTPSTATE_SEND_HEADER;
1658 if (redir_type != REDIR_NONE) {
1659 const char *hostinfo = 0;
1661 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1662 if (av_strncasecmp(p, "Host:", 5) == 0) {
1666 p = strchr(p, '\n');
1677 while (av_isspace(*hostinfo))
1680 eoh = strchr(hostinfo, '\n');
1682 if (eoh[-1] == '\r')
1685 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1686 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1687 hostbuf[eoh - hostinfo] = 0;
1689 c->http_error = 200;
1691 switch(redir_type) {
1693 snprintf(q, c->buffer_size,
1694 "HTTP/1.0 200 ASX Follows\r\n"
1695 "Content-type: video/x-ms-asf\r\n"
1697 "<ASX Version=\"3\">\r\n"
1698 //"<!-- Autogenerated by ffserver -->\r\n"
1699 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1700 "</ASX>\r\n", hostbuf, filename, info);
1704 snprintf(q, c->buffer_size,
1705 "HTTP/1.0 200 RAM Follows\r\n"
1706 "Content-type: audio/x-pn-realaudio\r\n"
1708 "# Autogenerated by ffserver\r\n"
1709 "http://%s/%s%s\r\n", hostbuf, filename, info);
1713 snprintf(q, c->buffer_size,
1714 "HTTP/1.0 200 ASF Redirect follows\r\n"
1715 "Content-type: video/x-ms-asf\r\n"
1718 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1723 char hostname[256], *p;
1724 /* extract only hostname */
1725 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1726 p = strrchr(hostname, ':');
1729 snprintf(q, c->buffer_size,
1730 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1731 /* XXX: incorrect mime type ? */
1732 "Content-type: application/x-rtsp\r\n"
1734 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1743 struct sockaddr_in my_addr;
1745 snprintf(q, c->buffer_size,
1746 "HTTP/1.0 200 OK\r\n"
1747 "Content-type: application/sdp\r\n"
1751 len = sizeof(my_addr);
1752 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1754 /* XXX: should use a dynamic buffer */
1755 sdp_data_size = prepare_sdp_description(stream,
1758 if (sdp_data_size > 0) {
1759 memcpy(q, sdp_data, sdp_data_size);
1771 /* prepare output buffer */
1772 c->buffer_ptr = c->buffer;
1774 c->state = HTTPSTATE_SEND_HEADER;
1780 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1784 stream->conns_served++;
1786 /* XXX: add there authenticate and IP match */
1789 /* if post, it means a feed is being sent */
1790 if (!stream->is_feed) {
1791 /* However it might be a status report from WMP! Let us log the
1792 * data as it might come in handy one day. */
1793 const char *logline = 0;
1796 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1797 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1801 if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1802 client_id = strtol(p + 18, 0, 10);
1803 p = strchr(p, '\n');
1811 char *eol = strchr(logline, '\n');
1816 if (eol[-1] == '\r')
1818 http_log("%.*s\n", (int) (eol - logline), logline);
1819 c->suppress_log = 1;
1824 http_log("\nGot request:\n%s\n", c->buffer);
1827 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1830 /* Now we have to find the client_id */
1831 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1832 if (wmpc->wmp_client_id == client_id)
1836 if (wmpc && modify_current_stream(wmpc, ratebuf))
1837 wmpc->switch_pending = 1;
1840 snprintf(msg, sizeof(msg), "POST command not handled");
1844 if (http_start_receive_data(c) < 0) {
1845 snprintf(msg, sizeof(msg), "could not open feed");
1849 c->state = HTTPSTATE_RECEIVE_DATA;
1854 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1855 http_log("\nGot request:\n%s\n", c->buffer);
1858 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1861 /* open input stream */
1862 if (open_input_stream(c, info) < 0) {
1863 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1867 /* prepare HTTP header */
1869 av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
1870 mime_type = c->stream->fmt->mime_type;
1872 mime_type = "application/x-octet-stream";
1873 av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
1875 /* for asf, we need extra headers */
1876 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1877 /* Need to allocate a client id */
1879 c->wmp_client_id = av_lfg_get(&random_state);
1881 av_strlcatf(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);
1883 av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
1884 av_strlcatf(c->buffer, c->buffer_size, "\r\n");
1885 q = c->buffer + strlen(c->buffer);
1887 /* prepare output buffer */
1889 c->buffer_ptr = c->buffer;
1891 c->state = HTTPSTATE_SEND_HEADER;
1894 c->http_error = 404;
1897 snprintf(q, c->buffer_size,
1898 "HTTP/1.0 404 Not Found\r\n"
1899 "Content-type: text/html\r\n"
1902 "<head><title>404 Not Found</title></head>\n"
1906 /* prepare output buffer */
1907 c->buffer_ptr = c->buffer;
1909 c->state = HTTPSTATE_SEND_HEADER;
1913 c->http_error = 200; /* horrible : we use this value to avoid
1914 going to the send data state */
1915 c->state = HTTPSTATE_SEND_HEADER;
1919 static void fmt_bytecount(AVIOContext *pb, int64_t count)
1921 static const char suffix[] = " kMGTP";
1924 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1926 avio_printf(pb, "%"PRId64"%c", count, *s);
1929 static void compute_status(HTTPContext *c)
1938 if (avio_open_dyn_buf(&pb) < 0) {
1939 /* XXX: return an error ? */
1940 c->buffer_ptr = c->buffer;
1941 c->buffer_end = c->buffer;
1945 avio_printf(pb, "HTTP/1.0 200 OK\r\n");
1946 avio_printf(pb, "Content-type: %s\r\n", "text/html");
1947 avio_printf(pb, "Pragma: no-cache\r\n");
1948 avio_printf(pb, "\r\n");
1950 avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1951 if (c->stream->feed_filename[0])
1952 avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1953 avio_printf(pb, "</head>\n<body>");
1954 avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
1956 avio_printf(pb, "<h2>Available Streams</h2>\n");
1957 avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
1958 avio_printf(pb, "<tr><th valign=top>Path<th align=left>Served<br>Conns<th><br>bytes<th valign=top>Format<th>Bit rate<br>kbits/s<th align=left>Video<br>kbits/s<th><br>Codec<th align=left>Audio<br>kbits/s<th><br>Codec<th align=left valign=top>Feed\n");
1959 stream = first_stream;
1960 while (stream != NULL) {
1961 char sfilename[1024];
1964 if (stream->feed != stream) {
1965 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1966 eosf = sfilename + strlen(sfilename);
1967 if (eosf - sfilename >= 4) {
1968 if (strcmp(eosf - 4, ".asf") == 0)
1969 strcpy(eosf - 4, ".asx");
1970 else if (strcmp(eosf - 3, ".rm") == 0)
1971 strcpy(eosf - 3, ".ram");
1972 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1973 /* generate a sample RTSP director if
1974 unicast. Generate an SDP redirector if
1976 eosf = strrchr(sfilename, '.');
1978 eosf = sfilename + strlen(sfilename);
1979 if (stream->is_multicast)
1980 strcpy(eosf, ".sdp");
1982 strcpy(eosf, ".rtsp");
1986 avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1987 sfilename, stream->filename);
1988 avio_printf(pb, "<td align=right> %d <td align=right> ",
1989 stream->conns_served);
1990 fmt_bytecount(pb, stream->bytes_served);
1991 switch(stream->stream_type) {
1992 case STREAM_TYPE_LIVE: {
1993 int audio_bit_rate = 0;
1994 int video_bit_rate = 0;
1995 const char *audio_codec_name = "";
1996 const char *video_codec_name = "";
1997 const char *audio_codec_name_extra = "";
1998 const char *video_codec_name_extra = "";
2000 for(i=0;i<stream->nb_streams;i++) {
2001 AVStream *st = stream->streams[i];
2002 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2003 switch(st->codec->codec_type) {
2004 case AVMEDIA_TYPE_AUDIO:
2005 audio_bit_rate += st->codec->bit_rate;
2007 if (*audio_codec_name)
2008 audio_codec_name_extra = "...";
2009 audio_codec_name = codec->name;
2012 case AVMEDIA_TYPE_VIDEO:
2013 video_bit_rate += st->codec->bit_rate;
2015 if (*video_codec_name)
2016 video_codec_name_extra = "...";
2017 video_codec_name = codec->name;
2020 case AVMEDIA_TYPE_DATA:
2021 video_bit_rate += st->codec->bit_rate;
2027 avio_printf(pb, "<td align=center> %s <td align=right> %d <td align=right> %d <td> %s %s <td align=right> %d <td> %s %s",
2030 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
2031 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
2033 avio_printf(pb, "<td>%s", stream->feed->filename);
2035 avio_printf(pb, "<td>%s", stream->feed_filename);
2036 avio_printf(pb, "\n");
2040 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
2044 stream = stream->next;
2046 avio_printf(pb, "</table>\n");
2048 stream = first_stream;
2049 while (stream != NULL) {
2050 if (stream->feed == stream) {
2051 avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
2053 avio_printf(pb, "Running as pid %d.\n", stream->pid);
2060 /* This is somewhat linux specific I guess */
2061 snprintf(ps_cmd, sizeof(ps_cmd),
2062 "ps -o \"%%cpu,cputime\" --no-headers %d",
2065 pid_stat = popen(ps_cmd, "r");
2070 if (fscanf(pid_stat, "%9s %63s", cpuperc,
2072 avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2080 avio_printf(pb, "<p>");
2082 avio_printf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
2084 for (i = 0; i < stream->nb_streams; i++) {
2085 AVStream *st = stream->streams[i];
2086 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2087 const char *type = "unknown";
2088 char parameters[64];
2092 switch(st->codec->codec_type) {
2093 case AVMEDIA_TYPE_AUDIO:
2095 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2097 case AVMEDIA_TYPE_VIDEO:
2099 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2100 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2105 avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2106 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2108 avio_printf(pb, "</table>\n");
2111 stream = stream->next;
2114 /* connection status */
2115 avio_printf(pb, "<h2>Connection Status</h2>\n");
2117 avio_printf(pb, "Number of connections: %d / %d<br>\n",
2118 nb_connections, nb_max_connections);
2120 avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2121 current_bandwidth, max_bandwidth);
2123 avio_printf(pb, "<table>\n");
2124 avio_printf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
2125 c1 = first_http_ctx;
2127 while (c1 != NULL) {
2133 for (j = 0; j < c1->stream->nb_streams; j++) {
2134 if (!c1->stream->feed)
2135 bitrate += c1->stream->streams[j]->codec->bit_rate;
2136 else if (c1->feed_streams[j] >= 0)
2137 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2142 p = inet_ntoa(c1->from_addr.sin_addr);
2143 avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2145 c1->stream ? c1->stream->filename : "",
2146 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2149 http_state[c1->state]);
2150 fmt_bytecount(pb, bitrate);
2151 avio_printf(pb, "<td align=right>");
2152 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2153 avio_printf(pb, "<td align=right>");
2154 fmt_bytecount(pb, c1->data_count);
2155 avio_printf(pb, "\n");
2158 avio_printf(pb, "</table>\n");
2163 avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
2164 avio_printf(pb, "</body>\n</html>\n");
2166 len = avio_close_dyn_buf(pb, &c->pb_buffer);
2167 c->buffer_ptr = c->pb_buffer;
2168 c->buffer_end = c->pb_buffer + len;
2171 static int open_input_stream(HTTPContext *c, const char *info)
2174 char input_filename[1024];
2175 AVFormatContext *s = NULL;
2176 int buf_size, i, ret;
2179 /* find file name */
2180 if (c->stream->feed) {
2181 strcpy(input_filename, c->stream->feed->feed_filename);
2182 buf_size = FFM_PACKET_SIZE;
2183 /* compute position (absolute time) */
2184 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2185 if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0) {
2186 http_log("Invalid date specification '%s' for stream\n", buf);
2189 } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2190 int prebuffer = strtol(buf, 0, 10);
2191 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2193 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2195 strcpy(input_filename, c->stream->feed_filename);
2197 /* compute position (relative time) */
2198 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2199 if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0) {
2200 http_log("Invalid date specification '%s' for stream\n", buf);
2206 if (!input_filename[0]) {
2207 http_log("No filename was specified for stream\n");
2208 return AVERROR(EINVAL);
2212 if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2213 http_log("Could not open input '%s': %s\n", input_filename, av_err2str(ret));
2217 /* set buffer size */
2218 if (buf_size > 0) ffio_set_buf_size(s->pb, buf_size);
2220 s->flags |= AVFMT_FLAG_GENPTS;
2222 if (strcmp(s->iformat->name, "ffm") &&
2223 (ret = avformat_find_stream_info(c->fmt_in, NULL)) < 0) {
2224 http_log("Could not find stream info for input '%s'\n", input_filename);
2225 avformat_close_input(&s);
2229 /* choose stream as clock source (we favorize video stream if
2230 present) for packet sending */
2231 c->pts_stream_index = 0;
2232 for(i=0;i<c->stream->nb_streams;i++) {
2233 if (c->pts_stream_index == 0 &&
2234 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2235 c->pts_stream_index = i;
2239 if (c->fmt_in->iformat->read_seek)
2240 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2241 /* set the start time (needed for maxtime and RTP packet timing) */
2242 c->start_time = cur_time;
2243 c->first_pts = AV_NOPTS_VALUE;
2247 /* return the server clock (in us) */
2248 static int64_t get_server_clock(HTTPContext *c)
2250 /* compute current pts value from system time */
2251 return (cur_time - c->start_time) * 1000;
2254 /* return the estimated time at which the current packet must be sent
2256 static int64_t get_packet_send_clock(HTTPContext *c)
2258 int bytes_left, bytes_sent, frame_bytes;
2260 frame_bytes = c->cur_frame_bytes;
2261 if (frame_bytes <= 0)
2264 bytes_left = c->buffer_end - c->buffer_ptr;
2265 bytes_sent = frame_bytes - bytes_left;
2266 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2271 static int http_prepare_data(HTTPContext *c)
2274 AVFormatContext *ctx;
2276 av_freep(&c->pb_buffer);
2278 case HTTPSTATE_SEND_DATA_HEADER:
2279 ctx = avformat_alloc_context();
2282 av_dict_copy(&(c->fmt_ctx.metadata), c->stream->metadata, 0);
2283 c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2285 for(i=0;i<c->stream->nb_streams;i++) {
2287 c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2288 /* if file or feed, then just take streams from FFStream struct */
2289 if (!c->stream->feed ||
2290 c->stream->feed == c->stream)
2291 src = c->stream->streams[i];
2293 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2295 *(c->fmt_ctx.streams[i]) = *src;
2296 c->fmt_ctx.streams[i]->priv_data = 0;
2297 c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2298 AVStream, not in codec */
2300 /* set output format parameters */
2301 c->fmt_ctx.oformat = c->stream->fmt;
2302 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2304 c->got_key_frame = 0;
2306 /* prepare header and save header data in a stream */
2307 if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2308 /* XXX: potential leak */
2311 c->fmt_ctx.pb->seekable = 0;
2314 * HACK to avoid mpeg ps muxer to spit many underflow errors
2315 * Default value from FFmpeg
2316 * Try to set it use configuration option
2318 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2320 if ((ret = avformat_write_header(&c->fmt_ctx, NULL)) < 0) {
2321 http_log("Error writing output header for stream '%s': %s\n",
2322 c->stream->filename, av_err2str(ret));
2325 av_dict_free(&c->fmt_ctx.metadata);
2327 len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2328 c->buffer_ptr = c->pb_buffer;
2329 c->buffer_end = c->pb_buffer + len;
2331 c->state = HTTPSTATE_SEND_DATA;
2332 c->last_packet_sent = 0;
2334 case HTTPSTATE_SEND_DATA:
2335 /* find a new packet */
2336 /* read a packet from the input stream */
2337 if (c->stream->feed)
2338 ffm_set_write_index(c->fmt_in,
2339 c->stream->feed->feed_write_index,
2340 c->stream->feed->feed_size);
2342 if (c->stream->max_time &&
2343 c->stream->max_time + c->start_time - cur_time < 0)
2344 /* We have timed out */
2345 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2349 ret = av_read_frame(c->fmt_in, &pkt);
2351 if (c->stream->feed) {
2352 /* if coming from feed, it means we reached the end of the
2353 ffm file, so must wait for more data */
2354 c->state = HTTPSTATE_WAIT_FEED;
2355 return 1; /* state changed */
2356 } else if (ret == AVERROR(EAGAIN)) {
2357 /* input not ready, come back later */
2360 if (c->stream->loop) {
2361 avformat_close_input(&c->fmt_in);
2362 if (open_input_stream(c, "") < 0)
2367 /* must send trailer now because eof or error */
2368 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2372 int source_index = pkt.stream_index;
2373 /* update first pts if needed */
2374 if (c->first_pts == AV_NOPTS_VALUE) {
2375 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2376 c->start_time = cur_time;
2378 /* send it to the appropriate stream */
2379 if (c->stream->feed) {
2380 /* if coming from a feed, select the right stream */
2381 if (c->switch_pending) {
2382 c->switch_pending = 0;
2383 for(i=0;i<c->stream->nb_streams;i++) {
2384 if (c->switch_feed_streams[i] == pkt.stream_index)
2385 if (pkt.flags & AV_PKT_FLAG_KEY)
2386 c->switch_feed_streams[i] = -1;
2387 if (c->switch_feed_streams[i] >= 0)
2388 c->switch_pending = 1;
2391 for(i=0;i<c->stream->nb_streams;i++) {
2392 if (c->stream->feed_streams[i] == pkt.stream_index) {
2393 AVStream *st = c->fmt_in->streams[source_index];
2394 pkt.stream_index = i;
2395 if (pkt.flags & AV_PKT_FLAG_KEY &&
2396 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2397 c->stream->nb_streams == 1))
2398 c->got_key_frame = 1;
2399 if (!c->stream->send_on_key || c->got_key_frame)
2404 AVCodecContext *codec;
2405 AVStream *ist, *ost;
2407 ist = c->fmt_in->streams[source_index];
2408 /* specific handling for RTP: we use several
2409 output stream (one for each RTP
2410 connection). XXX: need more abstract handling */
2411 if (c->is_packetized) {
2412 /* compute send time and duration */
2413 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2414 c->cur_pts -= c->first_pts;
2415 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2416 /* find RTP context */
2417 c->packet_stream_index = pkt.stream_index;
2418 ctx = c->rtp_ctx[c->packet_stream_index];
2420 av_free_packet(&pkt);
2423 codec = ctx->streams[0]->codec;
2424 /* only one stream per RTP connection */
2425 pkt.stream_index = 0;
2429 codec = ctx->streams[pkt.stream_index]->codec;
2432 if (c->is_packetized) {
2433 int max_packet_size;
2434 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2435 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2437 max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
2438 ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2440 ret = avio_open_dyn_buf(&ctx->pb);
2443 /* XXX: potential leak */
2446 ost = ctx->streams[pkt.stream_index];
2448 ctx->pb->seekable = 0;
2449 if (pkt.dts != AV_NOPTS_VALUE)
2450 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2451 if (pkt.pts != AV_NOPTS_VALUE)
2452 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2453 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2454 if ((ret = av_write_frame(ctx, &pkt)) < 0) {
2455 http_log("Error writing frame to output for stream '%s': %s\n",
2456 c->stream->filename, av_err2str(ret));
2457 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2460 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2461 c->cur_frame_bytes = len;
2462 c->buffer_ptr = c->pb_buffer;
2463 c->buffer_end = c->pb_buffer + len;
2465 codec->frame_number++;
2467 av_free_packet(&pkt);
2471 av_free_packet(&pkt);
2476 case HTTPSTATE_SEND_DATA_TRAILER:
2477 /* last packet test ? */
2478 if (c->last_packet_sent || c->is_packetized)
2481 /* prepare header */
2482 if (avio_open_dyn_buf(&ctx->pb) < 0) {
2483 /* XXX: potential leak */
2486 c->fmt_ctx.pb->seekable = 0;
2487 av_write_trailer(ctx);
2488 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2489 c->buffer_ptr = c->pb_buffer;
2490 c->buffer_end = c->pb_buffer + len;
2492 c->last_packet_sent = 1;
2498 /* should convert the format at the same time */
2499 /* send data starting at c->buffer_ptr to the output connection
2500 (either UDP or TCP connection) */
2501 static int http_send_data(HTTPContext *c)
2506 if (c->buffer_ptr >= c->buffer_end) {
2507 ret = http_prepare_data(c);
2511 /* state change requested */
2514 if (c->is_packetized) {
2515 /* RTP data output */
2516 len = c->buffer_end - c->buffer_ptr;
2518 /* fail safe - should never happen */
2520 c->buffer_ptr = c->buffer_end;
2523 len = (c->buffer_ptr[0] << 24) |
2524 (c->buffer_ptr[1] << 16) |
2525 (c->buffer_ptr[2] << 8) |
2527 if (len > (c->buffer_end - c->buffer_ptr))
2529 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2530 /* nothing to send yet: we can wait */
2534 c->data_count += len;
2535 update_datarate(&c->datarate, c->data_count);
2537 c->stream->bytes_served += len;
2539 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2540 /* RTP packets are sent inside the RTSP TCP connection */
2542 int interleaved_index, size;
2544 HTTPContext *rtsp_c;
2547 /* if no RTSP connection left, error */
2550 /* if already sending something, then wait. */
2551 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2553 if (avio_open_dyn_buf(&pb) < 0)
2555 interleaved_index = c->packet_stream_index * 2;
2556 /* RTCP packets are sent at odd indexes */
2557 if (c->buffer_ptr[1] == 200)
2558 interleaved_index++;
2559 /* write RTSP TCP header */
2561 header[1] = interleaved_index;
2562 header[2] = len >> 8;
2564 avio_write(pb, header, 4);
2565 /* write RTP packet data */
2567 avio_write(pb, c->buffer_ptr, len);
2568 size = avio_close_dyn_buf(pb, &c->packet_buffer);
2569 /* prepare asynchronous TCP sending */
2570 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2571 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2572 c->buffer_ptr += len;
2574 /* send everything we can NOW */
2575 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2576 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2578 rtsp_c->packet_buffer_ptr += len;
2579 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2580 /* if we could not send all the data, we will
2581 send it later, so a new state is needed to
2582 "lock" the RTSP TCP connection */
2583 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2586 /* all data has been sent */
2587 av_freep(&c->packet_buffer);
2589 /* send RTP packet directly in UDP */
2591 ffurl_write(c->rtp_handles[c->packet_stream_index],
2592 c->buffer_ptr, len);
2593 c->buffer_ptr += len;
2594 /* here we continue as we can send several packets per 10 ms slot */
2597 /* TCP data output */
2598 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2600 if (ff_neterrno() != AVERROR(EAGAIN) &&
2601 ff_neterrno() != AVERROR(EINTR))
2602 /* error : close connection */
2607 c->buffer_ptr += len;
2609 c->data_count += len;
2610 update_datarate(&c->datarate, c->data_count);
2612 c->stream->bytes_served += len;
2620 static int http_start_receive_data(HTTPContext *c)
2625 if (c->stream->feed_opened) {
2626 http_log("Stream feed '%s' was not opened\n", c->stream->feed_filename);
2627 return AVERROR(EINVAL);
2630 /* Don't permit writing to this one */
2631 if (c->stream->readonly) {
2632 http_log("Cannot write to read-only file '%s'\n", c->stream->feed_filename);
2633 return AVERROR(EINVAL);
2637 fd = open(c->stream->feed_filename, O_RDWR);
2639 ret = AVERROR(errno);
2640 http_log("Could not open feed file '%s': %s\n",
2641 c->stream->feed_filename, strerror(errno));
2646 if (c->stream->truncate) {
2647 /* truncate feed file */
2648 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2649 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2650 if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
2651 ret = AVERROR(errno);
2652 http_log("Error truncating feed file '%s': %s\n",
2653 c->stream->feed_filename, strerror(errno));
2657 ret = ffm_read_write_index(fd);
2659 http_log("Error reading write index from feed file '%s': %s\n",
2660 c->stream->feed_filename, strerror(errno));
2663 c->stream->feed_write_index = ret;
2667 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2668 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2669 lseek(fd, 0, SEEK_SET);
2671 /* init buffer input */
2672 c->buffer_ptr = c->buffer;
2673 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2674 c->stream->feed_opened = 1;
2675 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2679 static int http_receive_data(HTTPContext *c)
2682 int len, loop_run = 0;
2684 while (c->chunked_encoding && !c->chunk_size &&
2685 c->buffer_end > c->buffer_ptr) {
2686 /* read chunk header, if present */
2687 len = recv(c->fd, c->buffer_ptr, 1, 0);
2690 if (ff_neterrno() != AVERROR(EAGAIN) &&
2691 ff_neterrno() != AVERROR(EINTR))
2692 /* error : close connection */
2695 } else if (len == 0) {
2696 /* end of connection : close it */
2698 } else if (c->buffer_ptr - c->buffer >= 2 &&
2699 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2700 c->chunk_size = strtol(c->buffer, 0, 16);
2701 if (c->chunk_size == 0) // end of stream
2703 c->buffer_ptr = c->buffer;
2705 } else if (++loop_run > 10) {
2706 /* no chunk header, abort */
2713 if (c->buffer_end > c->buffer_ptr) {
2714 len = recv(c->fd, c->buffer_ptr,
2715 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2717 if (ff_neterrno() != AVERROR(EAGAIN) &&
2718 ff_neterrno() != AVERROR(EINTR))
2719 /* error : close connection */
2721 } else if (len == 0)
2722 /* end of connection : close it */
2725 c->chunk_size -= len;
2726 c->buffer_ptr += len;
2727 c->data_count += len;
2728 update_datarate(&c->datarate, c->data_count);
2732 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2733 if (c->buffer[0] != 'f' ||
2734 c->buffer[1] != 'm') {
2735 http_log("Feed stream has become desynchronized -- disconnecting\n");
2740 if (c->buffer_ptr >= c->buffer_end) {
2741 FFStream *feed = c->stream;
2742 /* a packet has been received : write it in the store, except
2744 if (c->data_count > FFM_PACKET_SIZE) {
2745 /* XXX: use llseek or url_seek */
2746 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2747 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2748 http_log("Error writing to feed file: %s\n", strerror(errno));
2752 feed->feed_write_index += FFM_PACKET_SIZE;
2753 /* update file size */
2754 if (feed->feed_write_index > c->stream->feed_size)
2755 feed->feed_size = feed->feed_write_index;
2757 /* handle wrap around if max file size reached */
2758 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2759 feed->feed_write_index = FFM_PACKET_SIZE;
2762 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2763 http_log("Error writing index to feed file: %s\n", strerror(errno));
2767 /* wake up any waiting connections */
2768 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2769 if (c1->state == HTTPSTATE_WAIT_FEED &&
2770 c1->stream->feed == c->stream->feed)
2771 c1->state = HTTPSTATE_SEND_DATA;
2774 /* We have a header in our hands that contains useful data */
2775 AVFormatContext *s = avformat_alloc_context();
2777 AVInputFormat *fmt_in;
2783 /* use feed output format name to find corresponding input format */
2784 fmt_in = av_find_input_format(feed->fmt->name);
2788 pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2789 0, NULL, NULL, NULL, NULL);
2793 if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2798 /* Now we have the actual streams */
2799 if (s->nb_streams != feed->nb_streams) {
2800 avformat_close_input(&s);
2802 http_log("Feed '%s' stream number does not match registered feed\n",
2803 c->stream->feed_filename);
2807 for (i = 0; i < s->nb_streams; i++) {
2808 AVStream *fst = feed->streams[i];
2809 AVStream *st = s->streams[i];
2810 avcodec_copy_context(fst->codec, st->codec);
2813 avformat_close_input(&s);
2816 c->buffer_ptr = c->buffer;
2821 c->stream->feed_opened = 0;
2823 /* wake up any waiting connections to stop waiting for feed */
2824 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2825 if (c1->state == HTTPSTATE_WAIT_FEED &&
2826 c1->stream->feed == c->stream->feed)
2827 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2832 /********************************************************************/
2835 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2842 switch(error_number) {
2843 case RTSP_STATUS_OK:
2846 case RTSP_STATUS_METHOD:
2847 str = "Method Not Allowed";
2849 case RTSP_STATUS_BANDWIDTH:
2850 str = "Not Enough Bandwidth";
2852 case RTSP_STATUS_SESSION:
2853 str = "Session Not Found";
2855 case RTSP_STATUS_STATE:
2856 str = "Method Not Valid in This State";
2858 case RTSP_STATUS_AGGREGATE:
2859 str = "Aggregate operation not allowed";
2861 case RTSP_STATUS_ONLY_AGGREGATE:
2862 str = "Only aggregate operation allowed";
2864 case RTSP_STATUS_TRANSPORT:
2865 str = "Unsupported transport";
2867 case RTSP_STATUS_INTERNAL:
2868 str = "Internal Server Error";
2870 case RTSP_STATUS_SERVICE:
2871 str = "Service Unavailable";
2873 case RTSP_STATUS_VERSION:
2874 str = "RTSP Version not supported";
2877 str = "Unknown Error";
2881 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2882 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2884 /* output GMT time */
2887 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2888 avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2891 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2893 rtsp_reply_header(c, error_number);
2894 avio_printf(c->pb, "\r\n");
2897 static int rtsp_parse_request(HTTPContext *c)
2899 const char *p, *p1, *p2;
2905 RTSPMessageHeader header1 = { 0 }, *header = &header1;
2907 c->buffer_ptr[0] = '\0';
2910 get_word(cmd, sizeof(cmd), &p);
2911 get_word(url, sizeof(url), &p);
2912 get_word(protocol, sizeof(protocol), &p);
2914 av_strlcpy(c->method, cmd, sizeof(c->method));
2915 av_strlcpy(c->url, url, sizeof(c->url));
2916 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2918 if (avio_open_dyn_buf(&c->pb) < 0) {
2919 /* XXX: cannot do more */
2920 c->pb = NULL; /* safety */
2924 /* check version name */
2925 if (strcmp(protocol, "RTSP/1.0") != 0) {
2926 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2930 /* parse each header line */
2931 /* skip to next line */
2932 while (*p != '\n' && *p != '\0')
2936 while (*p != '\0') {
2937 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2941 if (p2 > p && p2[-1] == '\r')
2943 /* skip empty line */
2947 if (len > sizeof(line) - 1)
2948 len = sizeof(line) - 1;
2949 memcpy(line, p, len);
2951 ff_rtsp_parse_line(header, line, NULL, NULL);
2955 /* handle sequence number */
2956 c->seq = header->seq;
2958 if (!strcmp(cmd, "DESCRIBE"))
2959 rtsp_cmd_describe(c, url);
2960 else if (!strcmp(cmd, "OPTIONS"))
2961 rtsp_cmd_options(c, url);
2962 else if (!strcmp(cmd, "SETUP"))
2963 rtsp_cmd_setup(c, url, header);
2964 else if (!strcmp(cmd, "PLAY"))
2965 rtsp_cmd_play(c, url, header);
2966 else if (!strcmp(cmd, "PAUSE"))
2967 rtsp_cmd_pause(c, url, header);
2968 else if (!strcmp(cmd, "TEARDOWN"))
2969 rtsp_cmd_teardown(c, url, header);
2971 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2974 len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2975 c->pb = NULL; /* safety */
2977 /* XXX: cannot do more */
2980 c->buffer_ptr = c->pb_buffer;
2981 c->buffer_end = c->pb_buffer + len;
2982 c->state = RTSPSTATE_SEND_REPLY;
2986 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2987 struct in_addr my_ip)
2989 AVFormatContext *avc;
2990 AVStream *avs = NULL;
2991 AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
2992 AVDictionaryEntry *entry = av_dict_get(stream->metadata, "title", NULL, 0);
2995 avc = avformat_alloc_context();
2996 if (avc == NULL || !rtp_format) {
2999 avc->oformat = rtp_format;
3000 av_dict_set(&avc->metadata, "title",
3001 entry ? entry->value : "No Title", 0);
3002 avc->nb_streams = stream->nb_streams;
3003 if (stream->is_multicast) {
3004 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
3005 inet_ntoa(stream->multicast_ip),
3006 stream->multicast_port, stream->multicast_ttl);
3008 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
3011 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
3012 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
3014 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
3015 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
3018 for(i = 0; i < stream->nb_streams; i++) {
3019 avc->streams[i] = &avs[i];
3020 avc->streams[i]->codec = stream->streams[i]->codec;
3022 *pbuffer = av_mallocz(2048);
3023 av_sdp_create(&avc, 1, *pbuffer, 2048);
3026 av_free(avc->streams);
3027 av_dict_free(&avc->metadata);
3031 return strlen(*pbuffer);
3034 static void rtsp_cmd_options(HTTPContext *c, const char *url)
3036 // rtsp_reply_header(c, RTSP_STATUS_OK);
3037 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
3038 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
3039 avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
3040 avio_printf(c->pb, "\r\n");
3043 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
3051 struct sockaddr_in my_addr;
3053 /* find which url is asked */
3054 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3059 for(stream = first_stream; stream != NULL; stream = stream->next) {
3060 if (!stream->is_feed &&
3061 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
3062 !strcmp(path, stream->filename)) {
3066 /* no stream found */
3067 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3071 /* prepare the media description in sdp format */
3073 /* get the host IP */
3074 len = sizeof(my_addr);
3075 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
3076 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3077 if (content_length < 0) {
3078 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3081 rtsp_reply_header(c, RTSP_STATUS_OK);
3082 avio_printf(c->pb, "Content-Base: %s/\r\n", url);
3083 avio_printf(c->pb, "Content-Type: application/sdp\r\n");
3084 avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
3085 avio_printf(c->pb, "\r\n");
3086 avio_write(c->pb, content, content_length);
3090 static HTTPContext *find_rtp_session(const char *session_id)
3094 if (session_id[0] == '\0')
3097 for(c = first_http_ctx; c != NULL; c = c->next) {
3098 if (!strcmp(c->session_id, session_id))
3104 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3106 RTSPTransportField *th;
3109 for(i=0;i<h->nb_transports;i++) {
3110 th = &h->transports[i];
3111 if (th->lower_transport == lower_transport)
3117 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3118 RTSPMessageHeader *h)
3121 int stream_index, rtp_port, rtcp_port;
3126 RTSPTransportField *th;
3127 struct sockaddr_in dest_addr;
3128 RTSPActionServerSetup setup;
3130 /* find which url is asked */
3131 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3136 /* now check each stream */
3137 for(stream = first_stream; stream != NULL; stream = stream->next) {
3138 if (!stream->is_feed &&
3139 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3140 /* accept aggregate filenames only if single stream */
3141 if (!strcmp(path, stream->filename)) {
3142 if (stream->nb_streams != 1) {
3143 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3150 for(stream_index = 0; stream_index < stream->nb_streams;
3152 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3153 stream->filename, stream_index);
3154 if (!strcmp(path, buf))
3159 /* no stream found */
3160 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3164 /* generate session id if needed */
3165 if (h->session_id[0] == '\0') {
3166 unsigned random0 = av_lfg_get(&random_state);
3167 unsigned random1 = av_lfg_get(&random_state);
3168 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3172 /* find rtp session, and create it if none found */
3173 rtp_c = find_rtp_session(h->session_id);
3175 /* always prefer UDP */
3176 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3178 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3180 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3185 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3186 th->lower_transport);
3188 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3192 /* open input stream */
3193 if (open_input_stream(rtp_c, "") < 0) {
3194 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3199 /* test if stream is OK (test needed because several SETUP needs
3200 to be done for a given file) */
3201 if (rtp_c->stream != stream) {
3202 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3206 /* test if stream is already set up */
3207 if (rtp_c->rtp_ctx[stream_index]) {
3208 rtsp_reply_error(c, RTSP_STATUS_STATE);
3212 /* check transport */
3213 th = find_transport(h, rtp_c->rtp_protocol);
3214 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3215 th->client_port_min <= 0)) {
3216 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3220 /* setup default options */
3221 setup.transport_option[0] = '\0';
3222 dest_addr = rtp_c->from_addr;
3223 dest_addr.sin_port = htons(th->client_port_min);
3226 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3227 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3231 /* now everything is OK, so we can send the connection parameters */
3232 rtsp_reply_header(c, RTSP_STATUS_OK);
3234 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3236 switch(rtp_c->rtp_protocol) {
3237 case RTSP_LOWER_TRANSPORT_UDP:
3238 rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3239 rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3240 avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3241 "client_port=%d-%d;server_port=%d-%d",
3242 th->client_port_min, th->client_port_max,
3243 rtp_port, rtcp_port);
3245 case RTSP_LOWER_TRANSPORT_TCP:
3246 avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3247 stream_index * 2, stream_index * 2 + 1);
3252 if (setup.transport_option[0] != '\0')
3253 avio_printf(c->pb, ";%s", setup.transport_option);
3254 avio_printf(c->pb, "\r\n");
3257 avio_printf(c->pb, "\r\n");
3261 /* find an rtp connection by using the session ID. Check consistency
3263 static HTTPContext *find_rtp_session_with_url(const char *url,
3264 const char *session_id)
3272 rtp_c = find_rtp_session(session_id);
3276 /* find which url is asked */
3277 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3281 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3282 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3283 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3284 rtp_c->stream->filename, s);
3285 if(!strncmp(path, buf, sizeof(buf))) {
3286 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3291 if (len > 0 && path[len - 1] == '/' &&
3292 !strncmp(path, rtp_c->stream->filename, len - 1))
3297 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3301 rtp_c = find_rtp_session_with_url(url, h->session_id);
3303 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3307 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3308 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3309 rtp_c->state != HTTPSTATE_READY) {
3310 rtsp_reply_error(c, RTSP_STATUS_STATE);
3314 rtp_c->state = HTTPSTATE_SEND_DATA;
3316 /* now everything is OK, so we can send the connection parameters */
3317 rtsp_reply_header(c, RTSP_STATUS_OK);
3319 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3320 avio_printf(c->pb, "\r\n");
3323 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3327 rtp_c = find_rtp_session_with_url(url, h->session_id);
3329 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3333 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3334 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3335 rtsp_reply_error(c, RTSP_STATUS_STATE);
3339 rtp_c->state = HTTPSTATE_READY;
3340 rtp_c->first_pts = AV_NOPTS_VALUE;
3341 /* now everything is OK, so we can send the connection parameters */
3342 rtsp_reply_header(c, RTSP_STATUS_OK);
3344 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3345 avio_printf(c->pb, "\r\n");
3348 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3352 rtp_c = find_rtp_session_with_url(url, h->session_id);
3354 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3358 /* now everything is OK, so we can send the connection parameters */
3359 rtsp_reply_header(c, RTSP_STATUS_OK);
3361 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3362 avio_printf(c->pb, "\r\n");
3364 /* abort the session */
3365 close_connection(rtp_c);
3369 /********************************************************************/
3372 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3373 FFStream *stream, const char *session_id,
3374 enum RTSPLowerTransport rtp_protocol)
3376 HTTPContext *c = NULL;
3377 const char *proto_str;
3379 /* XXX: should output a warning page when coming
3380 close to the connection limit */
3381 if (nb_connections >= nb_max_connections)
3384 /* add a new connection */
3385 c = av_mallocz(sizeof(HTTPContext));
3390 c->poll_entry = NULL;
3391 c->from_addr = *from_addr;
3392 c->buffer_size = IOBUFFER_INIT_SIZE;
3393 c->buffer = av_malloc(c->buffer_size);
3398 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3399 c->state = HTTPSTATE_READY;
3400 c->is_packetized = 1;
3401 c->rtp_protocol = rtp_protocol;
3403 /* protocol is shown in statistics */
3404 switch(c->rtp_protocol) {
3405 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3406 proto_str = "MCAST";
3408 case RTSP_LOWER_TRANSPORT_UDP:
3411 case RTSP_LOWER_TRANSPORT_TCP:
3418 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3419 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3421 current_bandwidth += stream->bandwidth;
3423 c->next = first_http_ctx;
3435 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3436 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3438 static int rtp_new_av_stream(HTTPContext *c,
3439 int stream_index, struct sockaddr_in *dest_addr,
3440 HTTPContext *rtsp_c)
3442 AVFormatContext *ctx;
3445 URLContext *h = NULL;
3447 int max_packet_size;
3449 /* now we can open the relevant output stream */
3450 ctx = avformat_alloc_context();
3453 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3455 st = av_mallocz(sizeof(AVStream));
3458 ctx->nb_streams = 1;
3459 ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3462 ctx->streams[0] = st;
3464 if (!c->stream->feed ||
3465 c->stream->feed == c->stream)
3466 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3469 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3471 st->priv_data = NULL;
3473 /* build destination RTP address */
3474 ipaddr = inet_ntoa(dest_addr->sin_addr);
3476 switch(c->rtp_protocol) {
3477 case RTSP_LOWER_TRANSPORT_UDP:
3478 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3481 /* XXX: also pass as parameter to function ? */
3482 if (c->stream->is_multicast) {
3484 ttl = c->stream->multicast_ttl;
3487 snprintf(ctx->filename, sizeof(ctx->filename),
3488 "rtp://%s:%d?multicast=1&ttl=%d",
3489 ipaddr, ntohs(dest_addr->sin_port), ttl);
3491 snprintf(ctx->filename, sizeof(ctx->filename),
3492 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3495 if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3497 c->rtp_handles[stream_index] = h;
3498 max_packet_size = h->max_packet_size;
3500 case RTSP_LOWER_TRANSPORT_TCP:
3503 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3509 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3510 ipaddr, ntohs(dest_addr->sin_port),
3511 c->stream->filename, stream_index, c->protocol);
3513 /* normally, no packets should be output here, but the packet size may
3515 if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3516 /* XXX: close stream */
3519 if (avformat_write_header(ctx, NULL) < 0) {
3526 avio_close_dyn_buf(ctx->pb, &dummy_buf);
3529 c->rtp_ctx[stream_index] = ctx;
3533 /********************************************************************/
3534 /* ffserver initialization */
3536 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3540 if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
3543 fst = av_mallocz(sizeof(AVStream));
3547 fst->codec = avcodec_alloc_context3(NULL);
3548 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3549 if (codec->extradata_size) {
3550 fst->codec->extradata = av_mallocz(codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
3551 memcpy(fst->codec->extradata, codec->extradata,
3552 codec->extradata_size);
3555 /* live streams must use the actual feed's codec since it may be
3556 * updated later to carry extradata needed by the streams.
3560 fst->priv_data = av_mallocz(sizeof(FeedData));
3561 fst->index = stream->nb_streams;
3562 avpriv_set_pts_info(fst, 33, 1, 90000);
3563 fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3564 stream->streams[stream->nb_streams++] = fst;
3568 /* return the stream number in the feed */
3569 static int add_av_stream(FFStream *feed, AVStream *st)
3572 AVCodecContext *av, *av1;
3576 for(i=0;i<feed->nb_streams;i++) {
3577 st = feed->streams[i];
3579 if (av1->codec_id == av->codec_id &&
3580 av1->codec_type == av->codec_type &&
3581 av1->bit_rate == av->bit_rate) {
3583 switch(av->codec_type) {
3584 case AVMEDIA_TYPE_AUDIO:
3585 if (av1->channels == av->channels &&
3586 av1->sample_rate == av->sample_rate)
3589 case AVMEDIA_TYPE_VIDEO:
3590 if (av1->width == av->width &&
3591 av1->height == av->height &&
3592 av1->time_base.den == av->time_base.den &&
3593 av1->time_base.num == av->time_base.num &&
3594 av1->gop_size == av->gop_size)
3603 fst = add_av_stream1(feed, av, 0);
3606 return feed->nb_streams - 1;
3609 static void remove_stream(FFStream *stream)
3613 while (*ps != NULL) {
3621 /* specific mpeg4 handling : we extract the raw parameters */
3622 static void extract_mpeg4_header(AVFormatContext *infile)
3624 int mpeg4_count, i, size;
3629 infile->flags |= AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE;
3632 for(i=0;i<infile->nb_streams;i++) {
3633 st = infile->streams[i];
3634 if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3635 st->codec->extradata_size == 0) {
3642 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3643 while (mpeg4_count > 0) {
3644 if (av_read_frame(infile, &pkt) < 0)
3646 st = infile->streams[pkt.stream_index];
3647 if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3648 st->codec->extradata_size == 0) {
3649 av_freep(&st->codec->extradata);
3650 /* fill extradata with the header */
3651 /* XXX: we make hard suppositions here ! */
3653 while (p < pkt.data + pkt.size - 4) {
3654 /* stop when vop header is found */
3655 if (p[0] == 0x00 && p[1] == 0x00 &&
3656 p[2] == 0x01 && p[3] == 0xb6) {
3657 size = p - pkt.data;
3658 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3659 st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
3660 st->codec->extradata_size = size;
3661 memcpy(st->codec->extradata, pkt.data, size);
3668 av_free_packet(&pkt);
3672 /* compute the needed AVStream for each file */
3673 static void build_file_streams(void)
3675 FFStream *stream, *stream_next;
3678 /* gather all streams */
3679 for(stream = first_stream; stream != NULL; stream = stream_next) {
3680 AVFormatContext *infile = NULL;
3681 stream_next = stream->next;
3682 if (stream->stream_type == STREAM_TYPE_LIVE &&
3684 /* the stream comes from a file */
3685 /* try to open the file */
3687 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3688 /* specific case : if transport stream output to RTP,
3689 we use a raw transport stream reader */
3690 av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3693 if (!stream->feed_filename[0]) {
3694 http_log("Unspecified feed file for stream '%s'\n", stream->filename);
3698 http_log("Opening feed file '%s' for stream '%s'\n", stream->feed_filename, stream->filename);
3699 if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3700 http_log("Could not open '%s': %s\n", stream->feed_filename, av_err2str(ret));
3701 /* remove stream (no need to spend more time on it) */
3703 remove_stream(stream);
3705 /* find all the AVStreams inside and reference them in
3707 if (avformat_find_stream_info(infile, NULL) < 0) {
3708 http_log("Could not find codec parameters from '%s'\n",
3709 stream->feed_filename);
3710 avformat_close_input(&infile);
3713 extract_mpeg4_header(infile);
3715 for(i=0;i<infile->nb_streams;i++)
3716 add_av_stream1(stream, infile->streams[i]->codec, 1);
3718 avformat_close_input(&infile);
3724 /* compute the needed AVStream for each feed */
3725 static void build_feed_streams(void)
3727 FFStream *stream, *feed;
3730 /* gather all streams */
3731 for(stream = first_stream; stream != NULL; stream = stream->next) {
3732 feed = stream->feed;
3734 if (stream->is_feed) {
3735 for(i=0;i<stream->nb_streams;i++)
3736 stream->feed_streams[i] = i;
3738 /* we handle a stream coming from a feed */
3739 for(i=0;i<stream->nb_streams;i++)
3740 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3745 /* create feed files if needed */
3746 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3749 if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3750 /* See if it matches */
3751 AVFormatContext *s = NULL;
3754 if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3755 /* set buffer size */
3756 ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
3757 /* Now see if it matches */
3758 if (s->nb_streams == feed->nb_streams) {
3760 for(i=0;i<s->nb_streams;i++) {
3762 sf = feed->streams[i];
3765 if (sf->index != ss->index ||
3767 http_log("Index & Id do not match for stream %d (%s)\n",
3768 i, feed->feed_filename);
3771 AVCodecContext *ccf, *ccs;
3775 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3777 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3778 http_log("Codecs do not match for stream %d\n", i);
3780 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3781 http_log("Codec bitrates do not match for stream %d\n", i);
3783 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3784 if (CHECK_CODEC(time_base.den) ||
3785 CHECK_CODEC(time_base.num) ||
3786 CHECK_CODEC(width) ||
3787 CHECK_CODEC(height)) {
3788 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3791 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3792 if (CHECK_CODEC(sample_rate) ||
3793 CHECK_CODEC(channels) ||
3794 CHECK_CODEC(frame_size)) {
3795 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3799 http_log("Unknown codec type\n");
3807 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3808 feed->feed_filename, s->nb_streams, feed->nb_streams);
3810 avformat_close_input(&s);
3812 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3813 feed->feed_filename);
3816 if (feed->readonly) {
3817 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3818 feed->feed_filename);
3821 unlink(feed->feed_filename);
3824 if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3825 AVFormatContext *s = avformat_alloc_context();
3827 if (feed->readonly) {
3828 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3829 feed->feed_filename);
3833 /* only write the header of the ffm file */
3834 if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3835 http_log("Could not open output feed file '%s'\n",
3836 feed->feed_filename);
3839 s->oformat = feed->fmt;
3840 s->nb_streams = feed->nb_streams;
3841 s->streams = feed->streams;
3842 if (avformat_write_header(s, NULL) < 0) {
3843 http_log("Container doesn't support the required parameters\n");
3846 /* XXX: need better api */
3847 av_freep(&s->priv_data);
3851 avformat_free_context(s);
3853 /* get feed size and write index */
3854 fd = open(feed->feed_filename, O_RDONLY);
3856 http_log("Could not open output feed file '%s'\n",
3857 feed->feed_filename);
3861 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3862 feed->feed_size = lseek(fd, 0, SEEK_END);
3863 /* ensure that we do not wrap before the end of file */
3864 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3865 feed->feed_max_size = feed->feed_size;
3871 /* compute the bandwidth used by each stream */
3872 static void compute_bandwidth(void)
3878 for(stream = first_stream; stream != NULL; stream = stream->next) {
3880 for(i=0;i<stream->nb_streams;i++) {
3881 AVStream *st = stream->streams[i];
3882 switch(st->codec->codec_type) {
3883 case AVMEDIA_TYPE_AUDIO:
3884 case AVMEDIA_TYPE_VIDEO:
3885 bandwidth += st->codec->bit_rate;
3891 stream->bandwidth = (bandwidth + 999) / 1000;
3895 /* add a codec and set the default parameters */
3896 static void add_codec(FFStream *stream, AVCodecContext *av)
3900 if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
3903 /* compute default parameters */
3904 switch(av->codec_type) {
3905 case AVMEDIA_TYPE_AUDIO:
3906 if (av->bit_rate == 0)
3907 av->bit_rate = 64000;
3908 if (av->sample_rate == 0)
3909 av->sample_rate = 22050;
3910 if (av->channels == 0)
3913 case AVMEDIA_TYPE_VIDEO:
3914 if (av->bit_rate == 0)
3915 av->bit_rate = 64000;
3916 if (av->time_base.num == 0){
3917 av->time_base.den = 5;
3918 av->time_base.num = 1;
3920 if (av->width == 0 || av->height == 0) {
3924 /* Bitrate tolerance is less for streaming */
3925 if (av->bit_rate_tolerance == 0)
3926 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3927 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3932 if (av->max_qdiff == 0)
3934 av->qcompress = 0.5;
3937 if (!av->nsse_weight)
3938 av->nsse_weight = 8;
3940 av->frame_skip_cmp = FF_CMP_DCTMAX;
3942 av->me_method = ME_EPZS;
3943 av->rc_buffer_aggressivity = 1.0;
3946 av->rc_eq = av_strdup("tex^qComp");
3947 if (!av->i_quant_factor)
3948 av->i_quant_factor = -0.8;
3949 if (!av->b_quant_factor)
3950 av->b_quant_factor = 1.25;
3951 if (!av->b_quant_offset)
3952 av->b_quant_offset = 1.25;
3953 if (!av->rc_max_rate)
3954 av->rc_max_rate = av->bit_rate * 2;
3956 if (av->rc_max_rate && !av->rc_buffer_size) {
3957 av->rc_buffer_size = av->rc_max_rate;
3966 st = av_mallocz(sizeof(AVStream));
3969 st->codec = avcodec_alloc_context3(NULL);
3970 stream->streams[stream->nb_streams++] = st;
3971 memcpy(st->codec, av, sizeof(AVCodecContext));
3974 static enum AVCodecID opt_codec(const char *name, enum AVMediaType type)
3976 AVCodec *codec = avcodec_find_encoder_by_name(name);
3978 if (!codec || codec->type != type)
3979 return AV_CODEC_ID_NONE;
3983 static int ffserver_opt_default(const char *opt, const char *arg,
3984 AVCodecContext *avctx, int type)
3987 const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3989 ret = av_opt_set(avctx, opt, arg, 0);
3993 static int ffserver_opt_preset(const char *arg,
3994 AVCodecContext *avctx, int type,
3995 enum AVCodecID *audio_id, enum AVCodecID *video_id)
3998 char filename[1000], tmp[1000], tmp2[1000], line[1000];
4000 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
4002 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
4003 codec ? codec->name : NULL))) {
4004 fprintf(stderr, "File for preset '%s' not found\n", arg);
4009 int e= fscanf(f, "%999[^\n]\n", line) - 1;
4010 if(line[0] == '#' && !e)
4012 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
4014 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
4018 if(!strcmp(tmp, "acodec")){
4019 *audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO);
4020 }else if(!strcmp(tmp, "vcodec")){
4021 *video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO);
4022 }else if(!strcmp(tmp, "scodec")){
4023 /* opt_subtitle_codec(tmp2); */
4024 }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
4025 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
4036 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
4037 const char *mime_type)
4039 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
4042 AVOutputFormat *stream_fmt;
4043 char stream_format_name[64];
4045 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
4046 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4055 static void report_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, ...)
4059 av_log(NULL, log_level, "%s:%d: ", filename, line_num);
4060 av_vlog(NULL, log_level, fmt, vl);
4066 static int parse_ffconfig(const char *filename)
4071 char arg[1024], arg2[1024];
4073 int val, errors, warnings, line_num;
4074 FFStream **last_stream, *stream, *redirect;
4075 FFStream **last_feed, *feed, *s;
4076 AVCodecContext audio_enc, video_enc;
4077 enum AVCodecID audio_id, video_id;
4080 f = fopen(filename, "r");
4082 ret = AVERROR(errno);
4083 av_log(NULL, AV_LOG_ERROR, "Could not open the configuration file '%s'\n", filename);
4087 errors = warnings = 0;
4089 first_stream = NULL;
4090 last_stream = &first_stream;
4092 last_feed = &first_feed;
4096 audio_id = AV_CODEC_ID_NONE;
4097 video_id = AV_CODEC_ID_NONE;
4098 #define ERROR(...) report_config_error(filename, line_num, AV_LOG_ERROR, &errors, __VA_ARGS__)
4099 #define WARNING(...) report_config_error(filename, line_num, AV_LOG_WARNING, &warnings, __VA_ARGS__)
4102 if (fgets(line, sizeof(line), f) == NULL)
4106 while (av_isspace(*p))
4108 if (*p == '\0' || *p == '#')
4111 get_arg(cmd, sizeof(cmd), &p);
4113 if (!av_strcasecmp(cmd, "Port")) {
4114 get_arg(arg, sizeof(arg), &p);
4116 if (val < 1 || val > 65536) {
4117 ERROR("Invalid_port: %s\n", arg);
4119 my_http_addr.sin_port = htons(val);
4120 } else if (!av_strcasecmp(cmd, "BindAddress")) {
4121 get_arg(arg, sizeof(arg), &p);
4122 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4123 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4125 } else if (!av_strcasecmp(cmd, "NoDaemon")) {
4126 WARNING("NoDaemon option has no effect, you should remove it\n");
4127 } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4128 get_arg(arg, sizeof(arg), &p);
4130 if (val < 1 || val > 65536) {
4131 ERROR("%s:%d: Invalid port: %s\n", arg);
4133 my_rtsp_addr.sin_port = htons(atoi(arg));
4134 } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4135 get_arg(arg, sizeof(arg), &p);
4136 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4137 ERROR("Invalid host/IP address: %s\n", arg);
4139 } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4140 get_arg(arg, sizeof(arg), &p);
4142 if (val < 1 || val > 65536) {
4143 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4145 nb_max_http_connections = val;
4146 } else if (!av_strcasecmp(cmd, "MaxClients")) {
4147 get_arg(arg, sizeof(arg), &p);
4149 if (val < 1 || val > nb_max_http_connections) {
4150 ERROR("Invalid MaxClients: %s\n", arg);
4152 nb_max_connections = val;
4154 } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4156 get_arg(arg, sizeof(arg), &p);
4157 llval = strtoll(arg, NULL, 10);
4158 if (llval < 10 || llval > 10000000) {
4159 ERROR("Invalid MaxBandwidth: %s\n", arg);
4161 max_bandwidth = llval;
4162 } else if (!av_strcasecmp(cmd, "CustomLog")) {
4163 if (!ffserver_debug)
4164 get_arg(logfilename, sizeof(logfilename), &p);
4165 } else if (!av_strcasecmp(cmd, "<Feed")) {
4166 /*********************************************/
4167 /* Feed related options */
4169 if (stream || feed) {
4170 ERROR("Already in a tag\n");
4172 feed = av_mallocz(sizeof(FFStream));
4174 ret = AVERROR(ENOMEM);
4177 get_arg(feed->filename, sizeof(feed->filename), &p);
4178 q = strrchr(feed->filename, '>');
4182 for (s = first_feed; s; s = s->next) {
4183 if (!strcmp(feed->filename, s->filename)) {
4184 ERROR("Feed '%s' already registered\n", s->filename);
4188 feed->fmt = av_guess_format("ffm", NULL, NULL);
4189 /* default feed file */
4190 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4191 "/tmp/%s.ffm", feed->filename);
4192 feed->feed_max_size = 5 * 1024 * 1024;
4194 feed->feed = feed; /* self feeding :-) */
4196 /* add in stream list */
4197 *last_stream = feed;
4198 last_stream = &feed->next;
4199 /* add in feed list */
4201 last_feed = &feed->next_feed;
4203 } else if (!av_strcasecmp(cmd, "Launch")) {
4207 feed->child_argv = av_mallocz(64 * sizeof(char *));
4208 if (!feed->child_argv) {
4209 ret = AVERROR(ENOMEM);
4212 for (i = 0; i < 62; i++) {
4213 get_arg(arg, sizeof(arg), &p);
4217 feed->child_argv[i] = av_strdup(arg);
4218 if (!feed->child_argv[i]) {
4219 ret = AVERROR(ENOMEM);
4224 feed->child_argv[i] =
4225 av_asprintf("http://%s:%d/%s",
4226 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4227 inet_ntoa(my_http_addr.sin_addr), ntohs(my_http_addr.sin_port),
4229 if (!feed->child_argv[i]) {
4230 ret = AVERROR(ENOMEM);
4234 } else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) {
4236 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4237 feed->readonly = !av_strcasecmp(cmd, "ReadOnlyFile");
4238 } else if (stream) {
4239 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4241 } else if (!av_strcasecmp(cmd, "Truncate")) {
4243 get_arg(arg, sizeof(arg), &p);
4244 /* assume Truncate is true in case no argument is specified */
4248 WARNING("Truncate N syntax in configuration file is deprecated, "
4249 "use Truncate alone with no arguments\n");
4250 feed->truncate = strtod(arg, NULL);
4253 } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4258 get_arg(arg, sizeof(arg), &p);
4260 fsize = strtod(p1, &p1);
4261 switch(av_toupper(*p1)) {
4266 fsize *= 1024 * 1024;
4269 fsize *= 1024 * 1024 * 1024;
4272 feed->feed_max_size = (int64_t)fsize;
4273 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4274 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4277 } else if (!av_strcasecmp(cmd, "</Feed>")) {
4279 ERROR("No corresponding <Feed> for </Feed>\n");
4282 } else if (!av_strcasecmp(cmd, "<Stream")) {
4283 /*********************************************/
4284 /* Stream related options */
4286 if (stream || feed) {
4287 ERROR("Already in a tag\n");
4290 stream = av_mallocz(sizeof(FFStream));
4292 ret = AVERROR(ENOMEM);
4295 get_arg(stream->filename, sizeof(stream->filename), &p);
4296 q = strrchr(stream->filename, '>');
4300 for (s = first_stream; s; s = s->next) {
4301 if (!strcmp(stream->filename, s->filename)) {
4302 ERROR("Stream '%s' already registered\n", s->filename);
4306 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4307 avcodec_get_context_defaults3(&video_enc, NULL);
4308 avcodec_get_context_defaults3(&audio_enc, NULL);
4310 audio_id = AV_CODEC_ID_NONE;
4311 video_id = AV_CODEC_ID_NONE;
4313 audio_id = stream->fmt->audio_codec;
4314 video_id = stream->fmt->video_codec;
4317 *last_stream = stream;
4318 last_stream = &stream->next;
4320 } else if (!av_strcasecmp(cmd, "Feed")) {
4321 get_arg(arg, sizeof(arg), &p);
4326 while (sfeed != NULL) {
4327 if (!strcmp(sfeed->filename, arg))
4329 sfeed = sfeed->next_feed;
4332 ERROR("Feed with name '%s' for stream '%s' is not defined\n", arg, stream->filename);
4334 stream->feed = sfeed;
4336 } else if (!av_strcasecmp(cmd, "Format")) {
4337 get_arg(arg, sizeof(arg), &p);
4339 if (!strcmp(arg, "status")) {
4340 stream->stream_type = STREAM_TYPE_STATUS;
4343 stream->stream_type = STREAM_TYPE_LIVE;
4344 /* jpeg cannot be used here, so use single frame jpeg */
4345 if (!strcmp(arg, "jpeg"))
4346 strcpy(arg, "mjpeg");
4347 stream->fmt = ffserver_guess_format(arg, NULL, NULL);
4349 ERROR("Unknown Format: %s\n", arg);
4353 audio_id = stream->fmt->audio_codec;
4354 video_id = stream->fmt->video_codec;
4357 } else if (!av_strcasecmp(cmd, "InputFormat")) {
4358 get_arg(arg, sizeof(arg), &p);
4360 stream->ifmt = av_find_input_format(arg);
4361 if (!stream->ifmt) {
4362 ERROR("Unknown input format: %s\n", arg);
4365 } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4366 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4367 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4369 ERROR("FaviconURL only permitted for status streams\n");
4371 } else if (!av_strcasecmp(cmd, "Author") ||
4372 !av_strcasecmp(cmd, "Comment") ||
4373 !av_strcasecmp(cmd, "Copyright") ||
4374 !av_strcasecmp(cmd, "Title")) {
4375 get_arg(arg, sizeof(arg), &p);
4381 for (i = 0; i < strlen(cmd); i++)
4382 key[i] = av_tolower(cmd[i]);
4384 WARNING("'%s' option in configuration file is deprecated, "
4385 "use 'Metadata %s VALUE' instead\n", cmd, key);
4386 if ((ret = av_dict_set(&stream->metadata, key, arg, 0)) < 0) {
4387 ERROR("Could not set metadata '%s' to value '%s': %s\n",
4388 key, arg, av_err2str(ret));
4391 } else if (!av_strcasecmp(cmd, "Metadata")) {
4392 get_arg(arg, sizeof(arg), &p);
4393 get_arg(arg2, sizeof(arg2), &p);
4396 if ((ret = av_dict_set(&stream->metadata, arg, arg2, 0)) < 0) {
4397 ERROR("Could not set metadata '%s' to value '%s': %s\n",
4398 arg, arg2, av_err2str(ret));
4401 } else if (!av_strcasecmp(cmd, "Preroll")) {
4402 get_arg(arg, sizeof(arg), &p);
4404 stream->prebuffer = atof(arg) * 1000;
4405 } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4407 stream->send_on_key = 1;
4408 } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4409 get_arg(arg, sizeof(arg), &p);
4410 audio_id = opt_codec(arg, AVMEDIA_TYPE_AUDIO);
4411 if (audio_id == AV_CODEC_ID_NONE) {
4412 ERROR("Unknown AudioCodec: %s\n", arg);
4414 } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4415 get_arg(arg, sizeof(arg), &p);
4416 video_id = opt_codec(arg, AVMEDIA_TYPE_VIDEO);
4417 if (video_id == AV_CODEC_ID_NONE) {
4418 ERROR("Unknown VideoCodec: %s\n", arg);
4420 } else if (!av_strcasecmp(cmd, "MaxTime")) {
4421 get_arg(arg, sizeof(arg), &p);
4423 stream->max_time = atof(arg) * 1000;
4424 } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4425 get_arg(arg, sizeof(arg), &p);
4427 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4428 } else if (!av_strcasecmp(cmd, "AudioChannels")) {
4429 get_arg(arg, sizeof(arg), &p);
4431 audio_enc.channels = atoi(arg);
4432 } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4433 get_arg(arg, sizeof(arg), &p);
4435 audio_enc.sample_rate = atoi(arg);
4436 } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4438 int minrate, maxrate;
4440 get_arg(arg, sizeof(arg), &p);
4442 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4443 video_enc.rc_min_rate = minrate * 1000;
4444 video_enc.rc_max_rate = maxrate * 1000;
4446 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4449 } else if (!av_strcasecmp(cmd, "Debug")) {
4451 get_arg(arg, sizeof(arg), &p);
4452 video_enc.debug = strtol(arg,0,0);
4454 } else if (!av_strcasecmp(cmd, "Strict")) {
4456 get_arg(arg, sizeof(arg), &p);
4457 video_enc.strict_std_compliance = atoi(arg);
4459 } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4461 get_arg(arg, sizeof(arg), &p);
4462 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4464 } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4466 get_arg(arg, sizeof(arg), &p);
4467 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4469 } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4470 get_arg(arg, sizeof(arg), &p);
4472 video_enc.bit_rate = atoi(arg) * 1000;
4474 } else if (!av_strcasecmp(cmd, "VideoSize")) {
4475 get_arg(arg, sizeof(arg), &p);
4477 ret = av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4479 ERROR("Invalid video size '%s'\n", arg);
4481 if ((video_enc.width % 16) != 0 ||
4482 (video_enc.height % 16) != 0) {
4483 ERROR("Image size must be a multiple of 16\n");
4487 } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4488 get_arg(arg, sizeof(arg), &p);
4490 AVRational frame_rate;
4491 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4492 ERROR("Incorrect frame rate: %s\n", arg);
4494 video_enc.time_base.num = frame_rate.den;
4495 video_enc.time_base.den = frame_rate.num;
4498 } else if (!av_strcasecmp(cmd, "PixelFormat")) {
4499 get_arg(arg, sizeof(arg), &p);
4501 video_enc.pix_fmt = av_get_pix_fmt(arg);
4502 if (video_enc.pix_fmt == AV_PIX_FMT_NONE) {
4503 ERROR("Unknown pixel format: %s\n", arg);
4506 } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4507 get_arg(arg, sizeof(arg), &p);
4509 video_enc.gop_size = atoi(arg);
4510 } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4512 video_enc.gop_size = 1;
4513 } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4515 video_enc.mb_decision = FF_MB_DECISION_BITS;
4516 } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4518 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4519 video_enc.flags |= CODEC_FLAG_4MV;
4521 } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4522 !av_strcasecmp(cmd, "AVOptionAudio")) {
4523 AVCodecContext *avctx;
4525 get_arg(arg, sizeof(arg), &p);
4526 get_arg(arg2, sizeof(arg2), &p);
4527 if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4529 type = AV_OPT_FLAG_VIDEO_PARAM;
4532 type = AV_OPT_FLAG_AUDIO_PARAM;
4534 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4535 ERROR("Error setting %s option to %s %s\n", cmd, arg, arg2);
4537 } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4538 !av_strcasecmp(cmd, "AVPresetAudio")) {
4539 AVCodecContext *avctx;
4541 get_arg(arg, sizeof(arg), &p);
4542 if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4544 video_enc.codec_id = video_id;
4545 type = AV_OPT_FLAG_VIDEO_PARAM;
4548 audio_enc.codec_id = audio_id;
4549 type = AV_OPT_FLAG_AUDIO_PARAM;
4551 if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4552 ERROR("AVPreset error: %s\n", arg);
4554 } else if (!av_strcasecmp(cmd, "VideoTag")) {
4555 get_arg(arg, sizeof(arg), &p);
4556 if ((strlen(arg) == 4) && stream)
4557 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4558 } else if (!av_strcasecmp(cmd, "BitExact")) {
4560 video_enc.flags |= CODEC_FLAG_BITEXACT;
4561 } else if (!av_strcasecmp(cmd, "DctFastint")) {
4563 video_enc.dct_algo = FF_DCT_FASTINT;
4564 } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4566 video_enc.idct_algo = FF_IDCT_SIMPLE;
4567 } else if (!av_strcasecmp(cmd, "Qscale")) {
4568 get_arg(arg, sizeof(arg), &p);
4570 video_enc.flags |= CODEC_FLAG_QSCALE;
4571 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4573 } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4574 get_arg(arg, sizeof(arg), &p);
4576 video_enc.max_qdiff = atoi(arg);
4577 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4578 ERROR("VideoQDiff out of range\n");
4581 } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4582 get_arg(arg, sizeof(arg), &p);
4584 video_enc.qmax = atoi(arg);
4585 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4586 ERROR("VideoQMax out of range\n");
4589 } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4590 get_arg(arg, sizeof(arg), &p);
4592 video_enc.qmin = atoi(arg);
4593 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4594 ERROR("VideoQMin out of range\n");
4597 } else if (!av_strcasecmp(cmd, "LumiMask")) {
4598 get_arg(arg, sizeof(arg), &p);
4600 video_enc.lumi_masking = atof(arg);
4601 } else if (!av_strcasecmp(cmd, "DarkMask")) {
4602 get_arg(arg, sizeof(arg), &p);
4604 video_enc.dark_masking = atof(arg);
4605 } else if (!av_strcasecmp(cmd, "NoVideo")) {
4606 video_id = AV_CODEC_ID_NONE;
4607 } else if (!av_strcasecmp(cmd, "NoAudio")) {
4608 audio_id = AV_CODEC_ID_NONE;
4609 } else if (!av_strcasecmp(cmd, "ACL")) {
4610 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4611 } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4613 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4615 } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4616 get_arg(arg, sizeof(arg), &p);
4618 av_freep(&stream->rtsp_option);
4619 stream->rtsp_option = av_strdup(arg);
4621 } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4622 get_arg(arg, sizeof(arg), &p);
4624 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4625 ERROR("Invalid host/IP address: %s\n", arg);
4627 stream->is_multicast = 1;
4628 stream->loop = 1; /* default is looping */
4630 } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4631 get_arg(arg, sizeof(arg), &p);
4633 stream->multicast_port = atoi(arg);
4634 } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4635 get_arg(arg, sizeof(arg), &p);
4637 stream->multicast_ttl = atoi(arg);
4638 } else if (!av_strcasecmp(cmd, "NoLoop")) {
4641 } else if (!av_strcasecmp(cmd, "</Stream>")) {
4643 ERROR("No corresponding <Stream> for </Stream>\n");
4645 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4646 if (audio_id != AV_CODEC_ID_NONE) {
4647 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4648 audio_enc.codec_id = audio_id;
4649 add_codec(stream, &audio_enc);
4651 if (video_id != AV_CODEC_ID_NONE) {
4652 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4653 video_enc.codec_id = video_id;
4654 add_codec(stream, &video_enc);
4659 } else if (!av_strcasecmp(cmd, "<Redirect")) {
4660 /*********************************************/
4662 if (stream || feed || redirect) {
4663 ERROR("Already in a tag\n");
4665 redirect = av_mallocz(sizeof(FFStream));
4667 ret = AVERROR(ENOMEM);
4670 *last_stream = redirect;
4671 last_stream = &redirect->next;
4673 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4674 q = strrchr(redirect->filename, '>');
4677 redirect->stream_type = STREAM_TYPE_REDIRECT;
4679 } else if (!av_strcasecmp(cmd, "URL")) {
4681 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4682 } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4684 ERROR("No corresponding <Redirect> for </Redirect>\n");
4686 if (!redirect->feed_filename[0]) {
4687 ERROR("No URL found for <Redirect>\n");
4691 } else if (!av_strcasecmp(cmd, "LoadModule")) {
4692 ERROR("Loadable modules no longer supported\n");
4694 ERROR("Incorrect keyword: '%s'\n", cmd);
4704 return AVERROR(EINVAL);
4709 static void handle_child_exit(int sig)
4714 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4717 for (feed = first_feed; feed; feed = feed->next) {
4718 if (feed->pid == pid) {
4719 int uptime = time(0) - feed->pid_start;
4722 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4725 /* Turn off any more restarts */
4726 feed->child_argv = 0;
4731 need_to_start_children = 1;
4734 static void opt_debug(void)
4737 logfilename[0] = '-';
4740 void show_help_default(const char *opt, const char *arg)
4742 printf("usage: ffserver [options]\n"
4743 "Hyper fast multi format Audio/Video streaming server\n");
4745 show_help_options(options, "Main options:", 0, 0, 0);
4748 static const OptionDef options[] = {
4749 #include "cmdutils_common_opts.h"
4750 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4751 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4752 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4756 int main(int argc, char **argv)
4758 struct sigaction sigact = { { 0 } };
4761 config_filename = av_strdup("/etc/ffserver.conf");
4763 parse_loglevel(argc, argv, options);
4765 avformat_network_init();
4767 show_banner(argc, argv, options);
4769 my_program_name = argv[0];
4771 parse_options(NULL, argc, argv, options, NULL);
4773 unsetenv("http_proxy"); /* Kill the http_proxy */
4775 av_lfg_init(&random_state, av_get_random_seed());
4777 sigact.sa_handler = handle_child_exit;
4778 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4779 sigaction(SIGCHLD, &sigact, 0);
4781 if ((ret = parse_ffconfig(config_filename)) < 0) {
4782 fprintf(stderr, "Error reading configuration file '%s': %s\n",
4783 config_filename, av_err2str(ret));
4786 av_freep(&config_filename);
4788 /* open log file if needed */
4789 if (logfilename[0] != '\0') {
4790 if (!strcmp(logfilename, "-"))
4793 logfile = fopen(logfilename, "a");
4794 av_log_set_callback(http_av_log);
4797 build_file_streams();
4799 build_feed_streams();
4801 compute_bandwidth();
4804 signal(SIGPIPE, SIG_IGN);
4806 if (http_server() < 0) {
4807 http_log("Could not start server\n");