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 AVInputFormat *ifmt; /* if non NULL, force input format */
222 char dynamic_acl[1024];
224 int prebuffer; /* Number of millseconds early to start */
225 int64_t max_time; /* Number of milliseconds to run */
227 AVStream *streams[MAX_STREAMS];
228 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
229 char feed_filename[1024]; /* file name of the feed storage, or
230 input file name for a stream */
235 pid_t pid; /* Of ffmpeg process */
236 time_t pid_start; /* Of ffmpeg process */
238 struct FFStream *next;
239 unsigned bandwidth; /* bandwidth, in kbits/s */
242 /* multicast specific */
244 struct in_addr multicast_ip;
245 int multicast_port; /* first port used for multicast */
247 int loop; /* if true, send the stream in loops (only meaningful if file) */
250 int feed_opened; /* true if someone is writing to the feed */
251 int is_feed; /* true if it is a feed */
252 int readonly; /* True if writing is prohibited to the file */
253 int truncate; /* True if feeder connection truncate the feed file */
255 int64_t bytes_served;
256 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
257 int64_t feed_write_index; /* current write position in feed (it wraps around) */
258 int64_t feed_size; /* current size of feed */
259 struct FFStream *next_feed;
262 typedef struct FeedData {
263 long long data_count;
264 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
267 static struct sockaddr_in my_http_addr;
268 static struct sockaddr_in my_rtsp_addr;
270 static char logfilename[1024];
271 static HTTPContext *first_http_ctx;
272 static FFStream *first_feed; /* contains only feeds */
273 static FFStream *first_stream; /* contains all streams, including feeds */
275 static void new_connection(int server_fd, int is_rtsp);
276 static void close_connection(HTTPContext *c);
279 static int handle_connection(HTTPContext *c);
280 static int http_parse_request(HTTPContext *c);
281 static int http_send_data(HTTPContext *c);
282 static void compute_status(HTTPContext *c);
283 static int open_input_stream(HTTPContext *c, const char *info);
284 static int http_start_receive_data(HTTPContext *c);
285 static int http_receive_data(HTTPContext *c);
288 static int rtsp_parse_request(HTTPContext *c);
289 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
290 static void rtsp_cmd_options(HTTPContext *c, const char *url);
291 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
292 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
293 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
294 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
297 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
298 struct in_addr my_ip);
301 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
302 FFStream *stream, const char *session_id,
303 enum RTSPLowerTransport rtp_protocol);
304 static int rtp_new_av_stream(HTTPContext *c,
305 int stream_index, struct sockaddr_in *dest_addr,
306 HTTPContext *rtsp_c);
308 static const char *my_program_name;
310 static const char *config_filename;
312 static int ffserver_debug;
313 static int no_launch;
314 static int need_to_start_children;
316 /* maximum number of simultaneous HTTP connections */
317 static unsigned int nb_max_http_connections = 2000;
318 static unsigned int nb_max_connections = 5;
319 static unsigned int nb_connections;
321 static uint64_t max_bandwidth = 1000;
322 static uint64_t current_bandwidth;
324 static int64_t cur_time; // Making this global saves on passing it around everywhere
326 static AVLFG random_state;
328 static FILE *logfile = NULL;
330 static void htmlstrip(char *s) {
332 s += strspn(s, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,. ");
338 static int64_t ffm_read_write_index(int fd)
342 if (lseek(fd, 8, SEEK_SET) < 0)
344 if (read(fd, buf, 8) != 8)
349 static int ffm_write_write_index(int fd, int64_t pos)
355 buf[i] = (pos >> (56 - i * 8)) & 0xff;
356 if (lseek(fd, 8, SEEK_SET) < 0)
358 if (write(fd, buf, 8) != 8)
363 static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
366 FFMContext *ffm = s->priv_data;
367 ffm->write_index = pos;
368 ffm->file_size = file_size;
371 /* FIXME: make ffserver work with IPv6 */
372 /* resolve host with also IP address parsing */
373 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
376 if (!ff_inet_aton(hostname, sin_addr)) {
378 struct addrinfo *ai, *cur;
379 struct addrinfo hints = { 0 };
380 hints.ai_family = AF_INET;
381 if (getaddrinfo(hostname, NULL, &hints, &ai))
383 /* getaddrinfo returns a linked list of addrinfo structs.
384 * Even if we set ai_family = AF_INET above, make sure
385 * that the returned one actually is of the correct type. */
386 for (cur = ai; cur; cur = cur->ai_next) {
387 if (cur->ai_family == AF_INET) {
388 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
397 hp = gethostbyname(hostname);
400 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
406 static char *ctime1(char *buf2, int buf_size)
413 av_strlcpy(buf2, p, buf_size);
414 p = buf2 + strlen(p) - 1;
420 static void http_vlog(const char *fmt, va_list vargs)
422 static int print_prefix = 1;
426 ctime1(buf, sizeof(buf));
427 fprintf(logfile, "%s ", buf);
429 print_prefix = strstr(fmt, "\n") != NULL;
430 vfprintf(logfile, fmt, vargs);
436 __attribute__ ((format (printf, 1, 2)))
438 static void http_log(const char *fmt, ...)
441 va_start(vargs, fmt);
442 http_vlog(fmt, vargs);
446 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
448 static int print_prefix = 1;
449 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
450 if (level > av_log_get_level())
452 if (print_prefix && avc)
453 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
454 print_prefix = strstr(fmt, "\n") != NULL;
455 http_vlog(fmt, vargs);
458 static void log_connection(HTTPContext *c)
463 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
464 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
465 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
468 static void update_datarate(DataRateData *drd, int64_t count)
470 if (!drd->time1 && !drd->count1) {
471 drd->time1 = drd->time2 = cur_time;
472 drd->count1 = drd->count2 = count;
473 } else if (cur_time - drd->time2 > 5000) {
474 drd->time1 = drd->time2;
475 drd->count1 = drd->count2;
476 drd->time2 = cur_time;
481 /* In bytes per second */
482 static int compute_datarate(DataRateData *drd, int64_t count)
484 if (cur_time == drd->time1)
487 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
491 static void start_children(FFStream *feed)
496 for (; feed; feed = feed->next) {
497 if (feed->child_argv && !feed->pid) {
498 feed->pid_start = time(0);
503 http_log("Unable to create children\n");
512 /* replace "ffserver" with "ffmpeg" in the path of current program,
513 * ignore user provided path */
514 av_strlcpy(pathname, my_program_name, sizeof(pathname));
515 slash = strrchr(pathname, '/');
520 strcpy(slash, "ffmpeg");
522 http_log("Launch command line: ");
523 http_log("%s ", pathname);
524 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
525 http_log("%s ", feed->child_argv[i]);
528 for (i = 3; i < 256; i++)
531 if (!ffserver_debug) {
532 if (!freopen("/dev/null", "r", stdin))
533 http_log("failed to redirect STDIN to /dev/null\n;");
534 if (!freopen("/dev/null", "w", stdout))
535 http_log("failed to redirect STDOUT to /dev/null\n;");
536 if (!freopen("/dev/null", "w", stderr))
537 http_log("failed to redirect STDERR to /dev/null\n;");
540 signal(SIGPIPE, SIG_DFL);
542 execvp(pathname, feed->child_argv);
550 /* open a listening socket */
551 static int socket_open_listen(struct sockaddr_in *my_addr)
555 server_fd = socket(AF_INET,SOCK_STREAM,0);
562 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
564 my_addr->sin_family = AF_INET;
565 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
567 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
569 closesocket(server_fd);
573 if (listen (server_fd, 5) < 0) {
575 closesocket(server_fd);
578 ff_socket_nonblock(server_fd, 1);
583 /* start all multicast streams */
584 static void start_multicast(void)
589 struct sockaddr_in dest_addr = {0};
590 int default_port, stream_index;
593 for(stream = first_stream; stream != NULL; stream = stream->next) {
594 if (stream->is_multicast) {
595 unsigned random0 = av_lfg_get(&random_state);
596 unsigned random1 = av_lfg_get(&random_state);
597 /* open the RTP connection */
598 snprintf(session_id, sizeof(session_id), "%08x%08x",
601 /* choose a port if none given */
602 if (stream->multicast_port == 0) {
603 stream->multicast_port = default_port;
607 dest_addr.sin_family = AF_INET;
608 dest_addr.sin_addr = stream->multicast_ip;
609 dest_addr.sin_port = htons(stream->multicast_port);
611 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
612 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
616 if (open_input_stream(rtp_c, "") < 0) {
617 http_log("Could not open input stream for stream '%s'\n",
622 /* open each RTP stream */
623 for(stream_index = 0; stream_index < stream->nb_streams;
625 dest_addr.sin_port = htons(stream->multicast_port +
627 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
628 http_log("Could not open output stream '%s/streamid=%d'\n",
629 stream->filename, stream_index);
634 /* change state to send data */
635 rtp_c->state = HTTPSTATE_SEND_DATA;
640 /* main loop of the http server */
641 static int http_server(void)
643 int server_fd = 0, rtsp_server_fd = 0;
644 int ret, delay, delay1;
645 struct pollfd *poll_table, *poll_entry;
646 HTTPContext *c, *c_next;
648 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
649 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
653 if (my_http_addr.sin_port) {
654 server_fd = socket_open_listen(&my_http_addr);
659 if (my_rtsp_addr.sin_port) {
660 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
661 if (rtsp_server_fd < 0)
665 if (!rtsp_server_fd && !server_fd) {
666 http_log("HTTP and RTSP disabled.\n");
670 http_log("FFserver started.\n");
672 start_children(first_feed);
677 poll_entry = poll_table;
679 poll_entry->fd = server_fd;
680 poll_entry->events = POLLIN;
683 if (rtsp_server_fd) {
684 poll_entry->fd = rtsp_server_fd;
685 poll_entry->events = POLLIN;
689 /* wait for events on each HTTP handle */
696 case HTTPSTATE_SEND_HEADER:
697 case RTSPSTATE_SEND_REPLY:
698 case RTSPSTATE_SEND_PACKET:
699 c->poll_entry = poll_entry;
701 poll_entry->events = POLLOUT;
704 case HTTPSTATE_SEND_DATA_HEADER:
705 case HTTPSTATE_SEND_DATA:
706 case HTTPSTATE_SEND_DATA_TRAILER:
707 if (!c->is_packetized) {
708 /* for TCP, we output as much as we can (may need to put a limit) */
709 c->poll_entry = poll_entry;
711 poll_entry->events = POLLOUT;
714 /* when ffserver is doing the timing, we work by
715 looking at which packet need to be sent every
717 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
722 case HTTPSTATE_WAIT_REQUEST:
723 case HTTPSTATE_RECEIVE_DATA:
724 case HTTPSTATE_WAIT_FEED:
725 case RTSPSTATE_WAIT_REQUEST:
726 /* need to catch errors */
727 c->poll_entry = poll_entry;
729 poll_entry->events = POLLIN;/* Maybe this will work */
733 c->poll_entry = NULL;
739 /* wait for an event on one connection. We poll at least every
740 second to handle timeouts */
742 ret = poll(poll_table, poll_entry - poll_table, delay);
743 if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
744 ff_neterrno() != AVERROR(EINTR))
748 cur_time = av_gettime() / 1000;
750 if (need_to_start_children) {
751 need_to_start_children = 0;
752 start_children(first_feed);
755 /* now handle the events */
756 for(c = first_http_ctx; c != NULL; c = c_next) {
758 if (handle_connection(c) < 0) {
759 /* close and free the connection */
765 poll_entry = poll_table;
767 /* new HTTP connection request ? */
768 if (poll_entry->revents & POLLIN)
769 new_connection(server_fd, 0);
772 if (rtsp_server_fd) {
773 /* new RTSP connection request ? */
774 if (poll_entry->revents & POLLIN)
775 new_connection(rtsp_server_fd, 1);
780 /* start waiting for a new HTTP/RTSP request */
781 static void start_wait_request(HTTPContext *c, int is_rtsp)
783 c->buffer_ptr = c->buffer;
784 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
787 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
788 c->state = RTSPSTATE_WAIT_REQUEST;
790 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
791 c->state = HTTPSTATE_WAIT_REQUEST;
795 static void http_send_too_busy_reply(int fd)
798 int len = snprintf(buffer, sizeof(buffer),
799 "HTTP/1.0 503 Server too busy\r\n"
800 "Content-type: text/html\r\n"
802 "<html><head><title>Too busy</title></head><body>\r\n"
803 "<p>The server is too busy to serve your request at this time.</p>\r\n"
804 "<p>The number of current connections is %u, and this exceeds the limit of %u.</p>\r\n"
805 "</body></html>\r\n",
806 nb_connections, nb_max_connections);
807 av_assert0(len < sizeof(buffer));
808 send(fd, buffer, len, 0);
812 static void new_connection(int server_fd, int is_rtsp)
814 struct sockaddr_in from_addr;
817 HTTPContext *c = NULL;
819 len = sizeof(from_addr);
820 fd = accept(server_fd, (struct sockaddr *)&from_addr,
823 http_log("error during accept %s\n", strerror(errno));
826 ff_socket_nonblock(fd, 1);
828 if (nb_connections >= nb_max_connections) {
829 http_send_too_busy_reply(fd);
833 /* add a new connection */
834 c = av_mallocz(sizeof(HTTPContext));
839 c->poll_entry = NULL;
840 c->from_addr = from_addr;
841 c->buffer_size = IOBUFFER_INIT_SIZE;
842 c->buffer = av_malloc(c->buffer_size);
846 c->next = first_http_ctx;
850 start_wait_request(c, is_rtsp);
862 static void close_connection(HTTPContext *c)
864 HTTPContext **cp, *c1;
866 AVFormatContext *ctx;
870 /* remove connection from list */
871 cp = &first_http_ctx;
872 while ((*cp) != NULL) {
880 /* remove references, if any (XXX: do it faster) */
881 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
886 /* remove connection associated resources */
890 /* close each frame parser */
891 for(i=0;i<c->fmt_in->nb_streams;i++) {
892 st = c->fmt_in->streams[i];
893 if (st->codec->codec)
894 avcodec_close(st->codec);
896 avformat_close_input(&c->fmt_in);
899 /* free RTP output streams if any */
902 nb_streams = c->stream->nb_streams;
904 for(i=0;i<nb_streams;i++) {
907 av_write_trailer(ctx);
908 av_dict_free(&ctx->metadata);
909 av_free(ctx->streams[0]);
912 h = c->rtp_handles[i];
919 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
922 if (avio_open_dyn_buf(&ctx->pb) >= 0) {
923 av_write_trailer(ctx);
924 av_freep(&c->pb_buffer);
925 avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
930 for(i=0; i<ctx->nb_streams; i++)
931 av_free(ctx->streams[i]);
932 av_freep(&ctx->streams);
933 av_freep(&ctx->priv_data);
935 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
936 current_bandwidth -= c->stream->bandwidth;
938 /* signal that there is no feed if we are the feeder socket */
939 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
940 c->stream->feed_opened = 0;
944 av_freep(&c->pb_buffer);
945 av_freep(&c->packet_buffer);
951 static int handle_connection(HTTPContext *c)
956 case HTTPSTATE_WAIT_REQUEST:
957 case RTSPSTATE_WAIT_REQUEST:
959 if ((c->timeout - cur_time) < 0)
961 if (c->poll_entry->revents & (POLLERR | POLLHUP))
964 /* no need to read if no events */
965 if (!(c->poll_entry->revents & POLLIN))
969 len = recv(c->fd, c->buffer_ptr, 1, 0);
971 if (ff_neterrno() != AVERROR(EAGAIN) &&
972 ff_neterrno() != AVERROR(EINTR))
974 } else if (len == 0) {
977 /* search for end of request. */
979 c->buffer_ptr += len;
981 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
982 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
983 /* request found : parse it and reply */
984 if (c->state == HTTPSTATE_WAIT_REQUEST) {
985 ret = http_parse_request(c);
987 ret = rtsp_parse_request(c);
991 } else if (ptr >= c->buffer_end) {
992 /* request too long: cannot do anything */
994 } else goto read_loop;
998 case HTTPSTATE_SEND_HEADER:
999 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1002 /* no need to write if no events */
1003 if (!(c->poll_entry->revents & POLLOUT))
1005 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1007 if (ff_neterrno() != AVERROR(EAGAIN) &&
1008 ff_neterrno() != AVERROR(EINTR)) {
1009 /* error : close connection */
1010 av_freep(&c->pb_buffer);
1014 c->buffer_ptr += len;
1016 c->stream->bytes_served += len;
1017 c->data_count += len;
1018 if (c->buffer_ptr >= c->buffer_end) {
1019 av_freep(&c->pb_buffer);
1020 /* if error, exit */
1023 /* all the buffer was sent : synchronize to the incoming stream */
1024 c->state = HTTPSTATE_SEND_DATA_HEADER;
1025 c->buffer_ptr = c->buffer_end = c->buffer;
1030 case HTTPSTATE_SEND_DATA:
1031 case HTTPSTATE_SEND_DATA_HEADER:
1032 case HTTPSTATE_SEND_DATA_TRAILER:
1033 /* for packetized output, we consider we can always write (the
1034 input streams sets the speed). It may be better to verify
1035 that we do not rely too much on the kernel queues */
1036 if (!c->is_packetized) {
1037 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1040 /* no need to read if no events */
1041 if (!(c->poll_entry->revents & POLLOUT))
1044 if (http_send_data(c) < 0)
1046 /* close connection if trailer sent */
1047 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
1050 case HTTPSTATE_RECEIVE_DATA:
1051 /* no need to read if no events */
1052 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1054 if (!(c->poll_entry->revents & POLLIN))
1056 if (http_receive_data(c) < 0)
1059 case HTTPSTATE_WAIT_FEED:
1060 /* no need to read if no events */
1061 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1064 /* nothing to do, we'll be waken up by incoming feed packets */
1067 case RTSPSTATE_SEND_REPLY:
1068 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1069 av_freep(&c->pb_buffer);
1072 /* no need to write if no events */
1073 if (!(c->poll_entry->revents & POLLOUT))
1075 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1077 if (ff_neterrno() != AVERROR(EAGAIN) &&
1078 ff_neterrno() != AVERROR(EINTR)) {
1079 /* error : close connection */
1080 av_freep(&c->pb_buffer);
1084 c->buffer_ptr += len;
1085 c->data_count += len;
1086 if (c->buffer_ptr >= c->buffer_end) {
1087 /* all the buffer was sent : wait for a new request */
1088 av_freep(&c->pb_buffer);
1089 start_wait_request(c, 1);
1093 case RTSPSTATE_SEND_PACKET:
1094 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1095 av_freep(&c->packet_buffer);
1098 /* no need to write if no events */
1099 if (!(c->poll_entry->revents & POLLOUT))
1101 len = send(c->fd, c->packet_buffer_ptr,
1102 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1104 if (ff_neterrno() != AVERROR(EAGAIN) &&
1105 ff_neterrno() != AVERROR(EINTR)) {
1106 /* error : close connection */
1107 av_freep(&c->packet_buffer);
1111 c->packet_buffer_ptr += len;
1112 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1113 /* all the buffer was sent : wait for a new request */
1114 av_freep(&c->packet_buffer);
1115 c->state = RTSPSTATE_WAIT_REQUEST;
1119 case HTTPSTATE_READY:
1128 static int extract_rates(char *rates, int ratelen, const char *request)
1132 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1133 if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1134 const char *q = p + 7;
1136 while (*q && *q != '\n' && av_isspace(*q))
1139 if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1145 memset(rates, 0xff, ratelen);
1148 while (*q && *q != '\n' && *q != ':')
1151 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1155 if (stream_no < ratelen && stream_no >= 0)
1156 rates[stream_no] = rate_no;
1158 while (*q && *q != '\n' && !av_isspace(*q))
1165 p = strchr(p, '\n');
1175 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1178 int best_bitrate = 100000000;
1181 for (i = 0; i < feed->nb_streams; i++) {
1182 AVCodecContext *feed_codec = feed->streams[i]->codec;
1184 if (feed_codec->codec_id != codec->codec_id ||
1185 feed_codec->sample_rate != codec->sample_rate ||
1186 feed_codec->width != codec->width ||
1187 feed_codec->height != codec->height)
1190 /* Potential stream */
1192 /* We want the fastest stream less than bit_rate, or the slowest
1193 * faster than bit_rate
1196 if (feed_codec->bit_rate <= bit_rate) {
1197 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1198 best_bitrate = feed_codec->bit_rate;
1202 if (feed_codec->bit_rate < best_bitrate) {
1203 best_bitrate = feed_codec->bit_rate;
1212 static int modify_current_stream(HTTPContext *c, char *rates)
1215 FFStream *req = c->stream;
1216 int action_required = 0;
1218 /* Not much we can do for a feed */
1222 for (i = 0; i < req->nb_streams; i++) {
1223 AVCodecContext *codec = req->streams[i]->codec;
1227 c->switch_feed_streams[i] = req->feed_streams[i];
1230 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1233 /* Wants off or slow */
1234 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1236 /* This doesn't work well when it turns off the only stream! */
1237 c->switch_feed_streams[i] = -2;
1238 c->feed_streams[i] = -2;
1243 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1244 action_required = 1;
1247 return action_required;
1250 /* XXX: factorize in utils.c ? */
1251 /* XXX: take care with different space meaning */
1252 static void skip_spaces(const char **pp)
1256 while (*p == ' ' || *p == '\t')
1261 static void get_word(char *buf, int buf_size, const char **pp)
1269 while (!av_isspace(*p) && *p != '\0') {
1270 if ((q - buf) < buf_size - 1)
1279 static void get_arg(char *buf, int buf_size, const char **pp)
1286 while (av_isspace(*p)) p++;
1289 if (*p == '\"' || *p == '\'')
1301 if ((q - buf) < buf_size - 1)
1306 if (quote && *p == quote)
1311 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1312 const char *p, const char *filename, int line_num)
1318 get_arg(arg, sizeof(arg), &p);
1319 if (av_strcasecmp(arg, "allow") == 0)
1320 acl.action = IP_ALLOW;
1321 else if (av_strcasecmp(arg, "deny") == 0)
1322 acl.action = IP_DENY;
1324 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1325 filename, line_num, arg);
1329 get_arg(arg, sizeof(arg), &p);
1331 if (resolve_host(&acl.first, arg) != 0) {
1332 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1333 filename, line_num, arg);
1336 acl.last = acl.first;
1338 get_arg(arg, sizeof(arg), &p);
1341 if (resolve_host(&acl.last, arg) != 0) {
1342 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1343 filename, line_num, arg);
1349 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1350 IPAddressACL **naclp = 0;
1356 naclp = &stream->acl;
1362 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1363 filename, line_num);
1369 naclp = &(*naclp)->next;
1377 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1382 IPAddressACL *acl = NULL;
1386 f = fopen(stream->dynamic_acl, "r");
1388 perror(stream->dynamic_acl);
1392 acl = av_mallocz(sizeof(IPAddressACL));
1396 if (fgets(line, sizeof(line), f) == NULL)
1400 while (av_isspace(*p))
1402 if (*p == '\0' || *p == '#')
1404 get_arg(cmd, sizeof(cmd), &p);
1406 if (!av_strcasecmp(cmd, "ACL"))
1407 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1414 static void free_acl_list(IPAddressACL *in_acl)
1416 IPAddressACL *pacl,*pacl2;
1426 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1428 enum IPAddressAction last_action = IP_DENY;
1430 struct in_addr *src = &c->from_addr.sin_addr;
1431 unsigned long src_addr = src->s_addr;
1433 for (acl = in_acl; acl; acl = acl->next) {
1434 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1435 return (acl->action == IP_ALLOW) ? 1 : 0;
1436 last_action = acl->action;
1439 /* Nothing matched, so return not the last action */
1440 return (last_action == IP_DENY) ? 1 : 0;
1443 static int validate_acl(FFStream *stream, HTTPContext *c)
1449 /* if stream->acl is null validate_acl_list will return 1 */
1450 ret = validate_acl_list(stream->acl, c);
1452 if (stream->dynamic_acl[0]) {
1453 acl = parse_dynamic_acl(stream, c);
1455 ret = validate_acl_list(acl, c);
1463 /* compute the real filename of a file by matching it without its
1464 extensions to all the stream filenames */
1465 static void compute_real_filename(char *filename, int max_size)
1472 /* compute filename by matching without the file extensions */
1473 av_strlcpy(file1, filename, sizeof(file1));
1474 p = strrchr(file1, '.');
1477 for(stream = first_stream; stream != NULL; stream = stream->next) {
1478 av_strlcpy(file2, stream->filename, sizeof(file2));
1479 p = strrchr(file2, '.');
1482 if (!strcmp(file1, file2)) {
1483 av_strlcpy(filename, stream->filename, max_size);
1498 /* parse http request and prepare header */
1499 static int http_parse_request(HTTPContext *c)
1503 enum RedirType redir_type;
1505 char info[1024], filename[1024];
1509 const char *mime_type;
1513 const char *useragent = 0;
1516 get_word(cmd, sizeof(cmd), &p);
1517 av_strlcpy(c->method, cmd, sizeof(c->method));
1519 if (!strcmp(cmd, "GET"))
1521 else if (!strcmp(cmd, "POST"))
1526 get_word(url, sizeof(url), &p);
1527 av_strlcpy(c->url, url, sizeof(c->url));
1529 get_word(protocol, sizeof(protocol), (const char **)&p);
1530 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1533 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1536 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1538 /* find the filename and the optional info string in the request */
1539 p1 = strchr(url, '?');
1541 av_strlcpy(info, p1, sizeof(info));
1546 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1548 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1549 if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1551 if (*useragent && *useragent != '\n' && av_isspace(*useragent))
1555 p = strchr(p, '\n');
1562 redir_type = REDIR_NONE;
1563 if (av_match_ext(filename, "asx")) {
1564 redir_type = REDIR_ASX;
1565 filename[strlen(filename)-1] = 'f';
1566 } else if (av_match_ext(filename, "asf") &&
1567 (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1568 /* if this isn't WMP or lookalike, return the redirector file */
1569 redir_type = REDIR_ASF;
1570 } else if (av_match_ext(filename, "rpm,ram")) {
1571 redir_type = REDIR_RAM;
1572 strcpy(filename + strlen(filename)-2, "m");
1573 } else if (av_match_ext(filename, "rtsp")) {
1574 redir_type = REDIR_RTSP;
1575 compute_real_filename(filename, sizeof(filename) - 1);
1576 } else if (av_match_ext(filename, "sdp")) {
1577 redir_type = REDIR_SDP;
1578 compute_real_filename(filename, sizeof(filename) - 1);
1581 // "redirect" / request to index.html
1582 if (!strlen(filename))
1583 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1585 stream = first_stream;
1586 while (stream != NULL) {
1587 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1589 stream = stream->next;
1591 if (stream == NULL) {
1592 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1593 http_log("File '%s' not found\n", url);
1598 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1599 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1601 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1602 c->http_error = 301;
1604 snprintf(q, c->buffer_size,
1605 "HTTP/1.0 301 Moved\r\n"
1607 "Content-type: text/html\r\n"
1609 "<html><head><title>Moved</title></head><body>\r\n"
1610 "You should be <a href=\"%s\">redirected</a>.\r\n"
1611 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1613 /* prepare output buffer */
1614 c->buffer_ptr = c->buffer;
1616 c->state = HTTPSTATE_SEND_HEADER;
1620 /* If this is WMP, get the rate information */
1621 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1622 if (modify_current_stream(c, ratebuf)) {
1623 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1624 if (c->switch_feed_streams[i] >= 0)
1625 c->switch_feed_streams[i] = -1;
1630 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1631 current_bandwidth += stream->bandwidth;
1633 /* If already streaming this feed, do not let start another feeder. */
1634 if (stream->feed_opened) {
1635 snprintf(msg, sizeof(msg), "This feed is already being received.");
1636 http_log("Feed '%s' already being received\n", stream->feed_filename);
1640 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1641 c->http_error = 503;
1643 snprintf(q, c->buffer_size,
1644 "HTTP/1.0 503 Server too busy\r\n"
1645 "Content-type: text/html\r\n"
1647 "<html><head><title>Too busy</title></head><body>\r\n"
1648 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1649 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1650 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1651 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1653 /* prepare output buffer */
1654 c->buffer_ptr = c->buffer;
1656 c->state = HTTPSTATE_SEND_HEADER;
1660 if (redir_type != REDIR_NONE) {
1661 const char *hostinfo = 0;
1663 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1664 if (av_strncasecmp(p, "Host:", 5) == 0) {
1668 p = strchr(p, '\n');
1679 while (av_isspace(*hostinfo))
1682 eoh = strchr(hostinfo, '\n');
1684 if (eoh[-1] == '\r')
1687 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1688 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1689 hostbuf[eoh - hostinfo] = 0;
1691 c->http_error = 200;
1693 switch(redir_type) {
1695 snprintf(q, c->buffer_size,
1696 "HTTP/1.0 200 ASX Follows\r\n"
1697 "Content-type: video/x-ms-asf\r\n"
1699 "<ASX Version=\"3\">\r\n"
1700 //"<!-- Autogenerated by ffserver -->\r\n"
1701 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1702 "</ASX>\r\n", hostbuf, filename, info);
1706 snprintf(q, c->buffer_size,
1707 "HTTP/1.0 200 RAM Follows\r\n"
1708 "Content-type: audio/x-pn-realaudio\r\n"
1710 "# Autogenerated by ffserver\r\n"
1711 "http://%s/%s%s\r\n", hostbuf, filename, info);
1715 snprintf(q, c->buffer_size,
1716 "HTTP/1.0 200 ASF Redirect follows\r\n"
1717 "Content-type: video/x-ms-asf\r\n"
1720 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1725 char hostname[256], *p;
1726 /* extract only hostname */
1727 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1728 p = strrchr(hostname, ':');
1731 snprintf(q, c->buffer_size,
1732 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1733 /* XXX: incorrect mime type ? */
1734 "Content-type: application/x-rtsp\r\n"
1736 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1745 struct sockaddr_in my_addr;
1747 snprintf(q, c->buffer_size,
1748 "HTTP/1.0 200 OK\r\n"
1749 "Content-type: application/sdp\r\n"
1753 len = sizeof(my_addr);
1754 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1756 /* XXX: should use a dynamic buffer */
1757 sdp_data_size = prepare_sdp_description(stream,
1760 if (sdp_data_size > 0) {
1761 memcpy(q, sdp_data, sdp_data_size);
1773 /* prepare output buffer */
1774 c->buffer_ptr = c->buffer;
1776 c->state = HTTPSTATE_SEND_HEADER;
1782 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1786 stream->conns_served++;
1788 /* XXX: add there authenticate and IP match */
1791 /* if post, it means a feed is being sent */
1792 if (!stream->is_feed) {
1793 /* However it might be a status report from WMP! Let us log the
1794 * data as it might come in handy one day. */
1795 const char *logline = 0;
1798 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1799 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1803 if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1804 client_id = strtol(p + 18, 0, 10);
1805 p = strchr(p, '\n');
1813 char *eol = strchr(logline, '\n');
1818 if (eol[-1] == '\r')
1820 http_log("%.*s\n", (int) (eol - logline), logline);
1821 c->suppress_log = 1;
1826 http_log("\nGot request:\n%s\n", c->buffer);
1829 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1832 /* Now we have to find the client_id */
1833 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1834 if (wmpc->wmp_client_id == client_id)
1838 if (wmpc && modify_current_stream(wmpc, ratebuf))
1839 wmpc->switch_pending = 1;
1842 snprintf(msg, sizeof(msg), "POST command not handled");
1846 if (http_start_receive_data(c) < 0) {
1847 snprintf(msg, sizeof(msg), "could not open feed");
1851 c->state = HTTPSTATE_RECEIVE_DATA;
1856 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1857 http_log("\nGot request:\n%s\n", c->buffer);
1860 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1863 /* open input stream */
1864 if (open_input_stream(c, info) < 0) {
1865 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1869 /* prepare http header */
1871 av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
1872 mime_type = c->stream->fmt->mime_type;
1874 mime_type = "application/x-octet-stream";
1875 av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
1877 /* for asf, we need extra headers */
1878 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1879 /* Need to allocate a client id */
1881 c->wmp_client_id = av_lfg_get(&random_state);
1883 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);
1885 av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
1886 av_strlcatf(c->buffer, c->buffer_size, "\r\n");
1887 q = c->buffer + strlen(c->buffer);
1889 /* prepare output buffer */
1891 c->buffer_ptr = c->buffer;
1893 c->state = HTTPSTATE_SEND_HEADER;
1896 c->http_error = 404;
1899 snprintf(q, c->buffer_size,
1900 "HTTP/1.0 404 Not Found\r\n"
1901 "Content-type: text/html\r\n"
1904 "<head><title>404 Not Found</title></head>\n"
1908 /* prepare output buffer */
1909 c->buffer_ptr = c->buffer;
1911 c->state = HTTPSTATE_SEND_HEADER;
1915 c->http_error = 200; /* horrible : we use this value to avoid
1916 going to the send data state */
1917 c->state = HTTPSTATE_SEND_HEADER;
1921 static void fmt_bytecount(AVIOContext *pb, int64_t count)
1923 static const char suffix[] = " kMGTP";
1926 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1928 avio_printf(pb, "%"PRId64"%c", count, *s);
1931 static void compute_status(HTTPContext *c)
1940 if (avio_open_dyn_buf(&pb) < 0) {
1941 /* XXX: return an error ? */
1942 c->buffer_ptr = c->buffer;
1943 c->buffer_end = c->buffer;
1947 avio_printf(pb, "HTTP/1.0 200 OK\r\n");
1948 avio_printf(pb, "Content-type: %s\r\n", "text/html");
1949 avio_printf(pb, "Pragma: no-cache\r\n");
1950 avio_printf(pb, "\r\n");
1952 avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1953 if (c->stream->feed_filename[0])
1954 avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1955 avio_printf(pb, "</head>\n<body>");
1956 avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
1958 avio_printf(pb, "<h2>Available Streams</h2>\n");
1959 avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
1960 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");
1961 stream = first_stream;
1962 while (stream != NULL) {
1963 char sfilename[1024];
1966 if (stream->feed != stream) {
1967 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1968 eosf = sfilename + strlen(sfilename);
1969 if (eosf - sfilename >= 4) {
1970 if (strcmp(eosf - 4, ".asf") == 0)
1971 strcpy(eosf - 4, ".asx");
1972 else if (strcmp(eosf - 3, ".rm") == 0)
1973 strcpy(eosf - 3, ".ram");
1974 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1975 /* generate a sample RTSP director if
1976 unicast. Generate an SDP redirector if
1978 eosf = strrchr(sfilename, '.');
1980 eosf = sfilename + strlen(sfilename);
1981 if (stream->is_multicast)
1982 strcpy(eosf, ".sdp");
1984 strcpy(eosf, ".rtsp");
1988 avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1989 sfilename, stream->filename);
1990 avio_printf(pb, "<td align=right> %d <td align=right> ",
1991 stream->conns_served);
1992 fmt_bytecount(pb, stream->bytes_served);
1993 switch(stream->stream_type) {
1994 case STREAM_TYPE_LIVE: {
1995 int audio_bit_rate = 0;
1996 int video_bit_rate = 0;
1997 const char *audio_codec_name = "";
1998 const char *video_codec_name = "";
1999 const char *audio_codec_name_extra = "";
2000 const char *video_codec_name_extra = "";
2002 for(i=0;i<stream->nb_streams;i++) {
2003 AVStream *st = stream->streams[i];
2004 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2005 switch(st->codec->codec_type) {
2006 case AVMEDIA_TYPE_AUDIO:
2007 audio_bit_rate += st->codec->bit_rate;
2009 if (*audio_codec_name)
2010 audio_codec_name_extra = "...";
2011 audio_codec_name = codec->name;
2014 case AVMEDIA_TYPE_VIDEO:
2015 video_bit_rate += st->codec->bit_rate;
2017 if (*video_codec_name)
2018 video_codec_name_extra = "...";
2019 video_codec_name = codec->name;
2022 case AVMEDIA_TYPE_DATA:
2023 video_bit_rate += st->codec->bit_rate;
2029 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",
2032 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
2033 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
2035 avio_printf(pb, "<td>%s", stream->feed->filename);
2037 avio_printf(pb, "<td>%s", stream->feed_filename);
2038 avio_printf(pb, "\n");
2042 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
2046 stream = stream->next;
2048 avio_printf(pb, "</table>\n");
2050 stream = first_stream;
2051 while (stream != NULL) {
2052 if (stream->feed == stream) {
2053 avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
2055 avio_printf(pb, "Running as pid %d.\n", stream->pid);
2062 /* This is somewhat linux specific I guess */
2063 snprintf(ps_cmd, sizeof(ps_cmd),
2064 "ps -o \"%%cpu,cputime\" --no-headers %d",
2067 pid_stat = popen(ps_cmd, "r");
2072 if (fscanf(pid_stat, "%9s %63s", cpuperc,
2074 avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2082 avio_printf(pb, "<p>");
2084 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");
2086 for (i = 0; i < stream->nb_streams; i++) {
2087 AVStream *st = stream->streams[i];
2088 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2089 const char *type = "unknown";
2090 char parameters[64];
2094 switch(st->codec->codec_type) {
2095 case AVMEDIA_TYPE_AUDIO:
2097 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2099 case AVMEDIA_TYPE_VIDEO:
2101 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2102 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2107 avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2108 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2110 avio_printf(pb, "</table>\n");
2113 stream = stream->next;
2116 /* connection status */
2117 avio_printf(pb, "<h2>Connection Status</h2>\n");
2119 avio_printf(pb, "Number of connections: %d / %d<br>\n",
2120 nb_connections, nb_max_connections);
2122 avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2123 current_bandwidth, max_bandwidth);
2125 avio_printf(pb, "<table>\n");
2126 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");
2127 c1 = first_http_ctx;
2129 while (c1 != NULL) {
2135 for (j = 0; j < c1->stream->nb_streams; j++) {
2136 if (!c1->stream->feed)
2137 bitrate += c1->stream->streams[j]->codec->bit_rate;
2138 else if (c1->feed_streams[j] >= 0)
2139 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2144 p = inet_ntoa(c1->from_addr.sin_addr);
2145 avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2147 c1->stream ? c1->stream->filename : "",
2148 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2151 http_state[c1->state]);
2152 fmt_bytecount(pb, bitrate);
2153 avio_printf(pb, "<td align=right>");
2154 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2155 avio_printf(pb, "<td align=right>");
2156 fmt_bytecount(pb, c1->data_count);
2157 avio_printf(pb, "\n");
2160 avio_printf(pb, "</table>\n");
2165 avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
2166 avio_printf(pb, "</body>\n</html>\n");
2168 len = avio_close_dyn_buf(pb, &c->pb_buffer);
2169 c->buffer_ptr = c->pb_buffer;
2170 c->buffer_end = c->pb_buffer + len;
2173 static int open_input_stream(HTTPContext *c, const char *info)
2176 char input_filename[1024];
2177 AVFormatContext *s = NULL;
2178 int buf_size, i, ret;
2181 /* find file name */
2182 if (c->stream->feed) {
2183 strcpy(input_filename, c->stream->feed->feed_filename);
2184 buf_size = FFM_PACKET_SIZE;
2185 /* compute position (absolute time) */
2186 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2187 if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0) {
2188 http_log("Invalid date specification '%s' for stream\n", buf);
2191 } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2192 int prebuffer = strtol(buf, 0, 10);
2193 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2195 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2197 strcpy(input_filename, c->stream->feed_filename);
2199 /* compute position (relative time) */
2200 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2201 if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0) {
2202 http_log("Invalid date specification '%s' for stream\n", buf);
2208 if (!input_filename[0]) {
2209 http_log("No filename was specified for stream\n");
2210 return AVERROR(EINVAL);
2214 if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2215 http_log("Could not open input '%s': %s\n", input_filename, av_err2str(ret));
2219 /* set buffer size */
2220 if (buf_size > 0) ffio_set_buf_size(s->pb, buf_size);
2222 s->flags |= AVFMT_FLAG_GENPTS;
2224 if (strcmp(s->iformat->name, "ffm") &&
2225 (ret = avformat_find_stream_info(c->fmt_in, NULL)) < 0) {
2226 http_log("Could not find stream info for input '%s'\n", input_filename);
2227 avformat_close_input(&s);
2231 /* choose stream as clock source (we favorize video stream if
2232 present) for packet sending */
2233 c->pts_stream_index = 0;
2234 for(i=0;i<c->stream->nb_streams;i++) {
2235 if (c->pts_stream_index == 0 &&
2236 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2237 c->pts_stream_index = i;
2241 if (c->fmt_in->iformat->read_seek)
2242 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2243 /* set the start time (needed for maxtime and RTP packet timing) */
2244 c->start_time = cur_time;
2245 c->first_pts = AV_NOPTS_VALUE;
2249 /* return the server clock (in us) */
2250 static int64_t get_server_clock(HTTPContext *c)
2252 /* compute current pts value from system time */
2253 return (cur_time - c->start_time) * 1000;
2256 /* return the estimated time at which the current packet must be sent
2258 static int64_t get_packet_send_clock(HTTPContext *c)
2260 int bytes_left, bytes_sent, frame_bytes;
2262 frame_bytes = c->cur_frame_bytes;
2263 if (frame_bytes <= 0)
2266 bytes_left = c->buffer_end - c->buffer_ptr;
2267 bytes_sent = frame_bytes - bytes_left;
2268 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2273 static int http_prepare_data(HTTPContext *c)
2276 AVFormatContext *ctx;
2278 av_freep(&c->pb_buffer);
2280 case HTTPSTATE_SEND_DATA_HEADER:
2281 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2282 av_dict_set(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2283 av_dict_set(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2284 av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2285 av_dict_set(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2287 c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2289 for(i=0;i<c->stream->nb_streams;i++) {
2291 c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2292 /* if file or feed, then just take streams from FFStream struct */
2293 if (!c->stream->feed ||
2294 c->stream->feed == c->stream)
2295 src = c->stream->streams[i];
2297 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2299 *(c->fmt_ctx.streams[i]) = *src;
2300 c->fmt_ctx.streams[i]->priv_data = 0;
2301 c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2302 AVStream, not in codec */
2304 /* set output format parameters */
2305 c->fmt_ctx.oformat = c->stream->fmt;
2306 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2308 c->got_key_frame = 0;
2310 /* prepare header and save header data in a stream */
2311 if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2312 /* XXX: potential leak */
2315 c->fmt_ctx.pb->seekable = 0;
2318 * HACK to avoid mpeg ps muxer to spit many underflow errors
2319 * Default value from FFmpeg
2320 * Try to set it use configuration option
2322 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2324 if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
2325 http_log("Error writing output header\n");
2328 av_dict_free(&c->fmt_ctx.metadata);
2330 len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2331 c->buffer_ptr = c->pb_buffer;
2332 c->buffer_end = c->pb_buffer + len;
2334 c->state = HTTPSTATE_SEND_DATA;
2335 c->last_packet_sent = 0;
2337 case HTTPSTATE_SEND_DATA:
2338 /* find a new packet */
2339 /* read a packet from the input stream */
2340 if (c->stream->feed)
2341 ffm_set_write_index(c->fmt_in,
2342 c->stream->feed->feed_write_index,
2343 c->stream->feed->feed_size);
2345 if (c->stream->max_time &&
2346 c->stream->max_time + c->start_time - cur_time < 0)
2347 /* We have timed out */
2348 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2352 ret = av_read_frame(c->fmt_in, &pkt);
2354 if (c->stream->feed) {
2355 /* if coming from feed, it means we reached the end of the
2356 ffm file, so must wait for more data */
2357 c->state = HTTPSTATE_WAIT_FEED;
2358 return 1; /* state changed */
2359 } else if (ret == AVERROR(EAGAIN)) {
2360 /* input not ready, come back later */
2363 if (c->stream->loop) {
2364 avformat_close_input(&c->fmt_in);
2365 if (open_input_stream(c, "") < 0)
2370 /* must send trailer now because eof or error */
2371 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2375 int source_index = pkt.stream_index;
2376 /* update first pts if needed */
2377 if (c->first_pts == AV_NOPTS_VALUE) {
2378 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2379 c->start_time = cur_time;
2381 /* send it to the appropriate stream */
2382 if (c->stream->feed) {
2383 /* if coming from a feed, select the right stream */
2384 if (c->switch_pending) {
2385 c->switch_pending = 0;
2386 for(i=0;i<c->stream->nb_streams;i++) {
2387 if (c->switch_feed_streams[i] == pkt.stream_index)
2388 if (pkt.flags & AV_PKT_FLAG_KEY)
2389 c->switch_feed_streams[i] = -1;
2390 if (c->switch_feed_streams[i] >= 0)
2391 c->switch_pending = 1;
2394 for(i=0;i<c->stream->nb_streams;i++) {
2395 if (c->stream->feed_streams[i] == pkt.stream_index) {
2396 AVStream *st = c->fmt_in->streams[source_index];
2397 pkt.stream_index = i;
2398 if (pkt.flags & AV_PKT_FLAG_KEY &&
2399 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2400 c->stream->nb_streams == 1))
2401 c->got_key_frame = 1;
2402 if (!c->stream->send_on_key || c->got_key_frame)
2407 AVCodecContext *codec;
2408 AVStream *ist, *ost;
2410 ist = c->fmt_in->streams[source_index];
2411 /* specific handling for RTP: we use several
2412 output stream (one for each RTP
2413 connection). XXX: need more abstract handling */
2414 if (c->is_packetized) {
2415 /* compute send time and duration */
2416 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2417 c->cur_pts -= c->first_pts;
2418 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2419 /* find RTP context */
2420 c->packet_stream_index = pkt.stream_index;
2421 ctx = c->rtp_ctx[c->packet_stream_index];
2423 av_free_packet(&pkt);
2426 codec = ctx->streams[0]->codec;
2427 /* only one stream per RTP connection */
2428 pkt.stream_index = 0;
2432 codec = ctx->streams[pkt.stream_index]->codec;
2435 if (c->is_packetized) {
2436 int max_packet_size;
2437 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2438 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2440 max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
2441 ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2443 ret = avio_open_dyn_buf(&ctx->pb);
2446 /* XXX: potential leak */
2449 ost = ctx->streams[pkt.stream_index];
2451 ctx->pb->seekable = 0;
2452 if (pkt.dts != AV_NOPTS_VALUE)
2453 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2454 if (pkt.pts != AV_NOPTS_VALUE)
2455 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2456 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2457 if (av_write_frame(ctx, &pkt) < 0) {
2458 http_log("Error writing frame to output\n");
2459 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2462 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2463 c->cur_frame_bytes = len;
2464 c->buffer_ptr = c->pb_buffer;
2465 c->buffer_end = c->pb_buffer + len;
2467 codec->frame_number++;
2469 av_free_packet(&pkt);
2473 av_free_packet(&pkt);
2478 case HTTPSTATE_SEND_DATA_TRAILER:
2479 /* last packet test ? */
2480 if (c->last_packet_sent || c->is_packetized)
2483 /* prepare header */
2484 if (avio_open_dyn_buf(&ctx->pb) < 0) {
2485 /* XXX: potential leak */
2488 c->fmt_ctx.pb->seekable = 0;
2489 av_write_trailer(ctx);
2490 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2491 c->buffer_ptr = c->pb_buffer;
2492 c->buffer_end = c->pb_buffer + len;
2494 c->last_packet_sent = 1;
2500 /* should convert the format at the same time */
2501 /* send data starting at c->buffer_ptr to the output connection
2502 (either UDP or TCP connection) */
2503 static int http_send_data(HTTPContext *c)
2508 if (c->buffer_ptr >= c->buffer_end) {
2509 ret = http_prepare_data(c);
2513 /* state change requested */
2516 if (c->is_packetized) {
2517 /* RTP data output */
2518 len = c->buffer_end - c->buffer_ptr;
2520 /* fail safe - should never happen */
2522 c->buffer_ptr = c->buffer_end;
2525 len = (c->buffer_ptr[0] << 24) |
2526 (c->buffer_ptr[1] << 16) |
2527 (c->buffer_ptr[2] << 8) |
2529 if (len > (c->buffer_end - c->buffer_ptr))
2531 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2532 /* nothing to send yet: we can wait */
2536 c->data_count += len;
2537 update_datarate(&c->datarate, c->data_count);
2539 c->stream->bytes_served += len;
2541 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2542 /* RTP packets are sent inside the RTSP TCP connection */
2544 int interleaved_index, size;
2546 HTTPContext *rtsp_c;
2549 /* if no RTSP connection left, error */
2552 /* if already sending something, then wait. */
2553 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2555 if (avio_open_dyn_buf(&pb) < 0)
2557 interleaved_index = c->packet_stream_index * 2;
2558 /* RTCP packets are sent at odd indexes */
2559 if (c->buffer_ptr[1] == 200)
2560 interleaved_index++;
2561 /* write RTSP TCP header */
2563 header[1] = interleaved_index;
2564 header[2] = len >> 8;
2566 avio_write(pb, header, 4);
2567 /* write RTP packet data */
2569 avio_write(pb, c->buffer_ptr, len);
2570 size = avio_close_dyn_buf(pb, &c->packet_buffer);
2571 /* prepare asynchronous TCP sending */
2572 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2573 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2574 c->buffer_ptr += len;
2576 /* send everything we can NOW */
2577 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2578 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2580 rtsp_c->packet_buffer_ptr += len;
2581 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2582 /* if we could not send all the data, we will
2583 send it later, so a new state is needed to
2584 "lock" the RTSP TCP connection */
2585 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2588 /* all data has been sent */
2589 av_freep(&c->packet_buffer);
2591 /* send RTP packet directly in UDP */
2593 ffurl_write(c->rtp_handles[c->packet_stream_index],
2594 c->buffer_ptr, len);
2595 c->buffer_ptr += len;
2596 /* here we continue as we can send several packets per 10 ms slot */
2599 /* TCP data output */
2600 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2602 if (ff_neterrno() != AVERROR(EAGAIN) &&
2603 ff_neterrno() != AVERROR(EINTR))
2604 /* error : close connection */
2609 c->buffer_ptr += len;
2611 c->data_count += len;
2612 update_datarate(&c->datarate, c->data_count);
2614 c->stream->bytes_served += len;
2622 static int http_start_receive_data(HTTPContext *c)
2627 if (c->stream->feed_opened) {
2628 http_log("Stream feed '%s' was not opened\n", c->stream->feed_filename);
2629 return AVERROR(EINVAL);
2632 /* Don't permit writing to this one */
2633 if (c->stream->readonly) {
2634 http_log("Cannot write to read-only file '%s'\n", c->stream->feed_filename);
2635 return AVERROR(EINVAL);
2639 fd = open(c->stream->feed_filename, O_RDWR);
2641 ret = AVERROR(errno);
2642 http_log("Could not open feed file '%s':%s \n",
2643 c->stream->feed_filename, strerror(errno));
2648 if (c->stream->truncate) {
2649 /* truncate feed file */
2650 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2651 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2652 if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
2653 ret = AVERROR(errno);
2654 http_log("Error truncating feed file '%s': %s\n",
2655 c->stream->feed_filename, strerror(errno));
2659 ret = ffm_read_write_index(fd);
2661 http_log("Error reading write index from feed file '%s': %s\n",
2662 c->stream->feed_filename, strerror(errno));
2665 c->stream->feed_write_index = ret;
2669 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2670 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2671 lseek(fd, 0, SEEK_SET);
2673 /* init buffer input */
2674 c->buffer_ptr = c->buffer;
2675 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2676 c->stream->feed_opened = 1;
2677 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2681 static int http_receive_data(HTTPContext *c)
2684 int len, loop_run = 0;
2686 while (c->chunked_encoding && !c->chunk_size &&
2687 c->buffer_end > c->buffer_ptr) {
2688 /* read chunk header, if present */
2689 len = recv(c->fd, c->buffer_ptr, 1, 0);
2692 if (ff_neterrno() != AVERROR(EAGAIN) &&
2693 ff_neterrno() != AVERROR(EINTR))
2694 /* error : close connection */
2697 } else if (len == 0) {
2698 /* end of connection : close it */
2700 } else if (c->buffer_ptr - c->buffer >= 2 &&
2701 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2702 c->chunk_size = strtol(c->buffer, 0, 16);
2703 if (c->chunk_size == 0) // end of stream
2705 c->buffer_ptr = c->buffer;
2707 } else if (++loop_run > 10) {
2708 /* no chunk header, abort */
2715 if (c->buffer_end > c->buffer_ptr) {
2716 len = recv(c->fd, c->buffer_ptr,
2717 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2719 if (ff_neterrno() != AVERROR(EAGAIN) &&
2720 ff_neterrno() != AVERROR(EINTR))
2721 /* error : close connection */
2723 } else if (len == 0)
2724 /* end of connection : close it */
2727 c->chunk_size -= len;
2728 c->buffer_ptr += len;
2729 c->data_count += len;
2730 update_datarate(&c->datarate, c->data_count);
2734 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2735 if (c->buffer[0] != 'f' ||
2736 c->buffer[1] != 'm') {
2737 http_log("Feed stream has become desynchronized -- disconnecting\n");
2742 if (c->buffer_ptr >= c->buffer_end) {
2743 FFStream *feed = c->stream;
2744 /* a packet has been received : write it in the store, except
2746 if (c->data_count > FFM_PACKET_SIZE) {
2747 /* XXX: use llseek or url_seek */
2748 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2749 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2750 http_log("Error writing to feed file: %s\n", strerror(errno));
2754 feed->feed_write_index += FFM_PACKET_SIZE;
2755 /* update file size */
2756 if (feed->feed_write_index > c->stream->feed_size)
2757 feed->feed_size = feed->feed_write_index;
2759 /* handle wrap around if max file size reached */
2760 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2761 feed->feed_write_index = FFM_PACKET_SIZE;
2764 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2765 http_log("Error writing index to feed file: %s\n", strerror(errno));
2769 /* wake up any waiting connections */
2770 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2771 if (c1->state == HTTPSTATE_WAIT_FEED &&
2772 c1->stream->feed == c->stream->feed)
2773 c1->state = HTTPSTATE_SEND_DATA;
2776 /* We have a header in our hands that contains useful data */
2777 AVFormatContext *s = avformat_alloc_context();
2779 AVInputFormat *fmt_in;
2785 /* use feed output format name to find corresponding input format */
2786 fmt_in = av_find_input_format(feed->fmt->name);
2790 pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2791 0, NULL, NULL, NULL, NULL);
2795 if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2800 /* Now we have the actual streams */
2801 if (s->nb_streams != feed->nb_streams) {
2802 avformat_close_input(&s);
2804 http_log("Feed '%s' stream number does not match registered feed\n",
2805 c->stream->feed_filename);
2809 for (i = 0; i < s->nb_streams; i++) {
2810 AVStream *fst = feed->streams[i];
2811 AVStream *st = s->streams[i];
2812 avcodec_copy_context(fst->codec, st->codec);
2815 avformat_close_input(&s);
2818 c->buffer_ptr = c->buffer;
2823 c->stream->feed_opened = 0;
2825 /* wake up any waiting connections to stop waiting for feed */
2826 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2827 if (c1->state == HTTPSTATE_WAIT_FEED &&
2828 c1->stream->feed == c->stream->feed)
2829 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2834 /********************************************************************/
2837 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2844 switch(error_number) {
2845 case RTSP_STATUS_OK:
2848 case RTSP_STATUS_METHOD:
2849 str = "Method Not Allowed";
2851 case RTSP_STATUS_BANDWIDTH:
2852 str = "Not Enough Bandwidth";
2854 case RTSP_STATUS_SESSION:
2855 str = "Session Not Found";
2857 case RTSP_STATUS_STATE:
2858 str = "Method Not Valid in This State";
2860 case RTSP_STATUS_AGGREGATE:
2861 str = "Aggregate operation not allowed";
2863 case RTSP_STATUS_ONLY_AGGREGATE:
2864 str = "Only aggregate operation allowed";
2866 case RTSP_STATUS_TRANSPORT:
2867 str = "Unsupported transport";
2869 case RTSP_STATUS_INTERNAL:
2870 str = "Internal Server Error";
2872 case RTSP_STATUS_SERVICE:
2873 str = "Service Unavailable";
2875 case RTSP_STATUS_VERSION:
2876 str = "RTSP Version not supported";
2879 str = "Unknown Error";
2883 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2884 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2886 /* output GMT time */
2889 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2890 avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2893 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2895 rtsp_reply_header(c, error_number);
2896 avio_printf(c->pb, "\r\n");
2899 static int rtsp_parse_request(HTTPContext *c)
2901 const char *p, *p1, *p2;
2907 RTSPMessageHeader header1 = { 0 }, *header = &header1;
2909 c->buffer_ptr[0] = '\0';
2912 get_word(cmd, sizeof(cmd), &p);
2913 get_word(url, sizeof(url), &p);
2914 get_word(protocol, sizeof(protocol), &p);
2916 av_strlcpy(c->method, cmd, sizeof(c->method));
2917 av_strlcpy(c->url, url, sizeof(c->url));
2918 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2920 if (avio_open_dyn_buf(&c->pb) < 0) {
2921 /* XXX: cannot do more */
2922 c->pb = NULL; /* safety */
2926 /* check version name */
2927 if (strcmp(protocol, "RTSP/1.0") != 0) {
2928 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2932 /* parse each header line */
2933 /* skip to next line */
2934 while (*p != '\n' && *p != '\0')
2938 while (*p != '\0') {
2939 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2943 if (p2 > p && p2[-1] == '\r')
2945 /* skip empty line */
2949 if (len > sizeof(line) - 1)
2950 len = sizeof(line) - 1;
2951 memcpy(line, p, len);
2953 ff_rtsp_parse_line(header, line, NULL, NULL);
2957 /* handle sequence number */
2958 c->seq = header->seq;
2960 if (!strcmp(cmd, "DESCRIBE"))
2961 rtsp_cmd_describe(c, url);
2962 else if (!strcmp(cmd, "OPTIONS"))
2963 rtsp_cmd_options(c, url);
2964 else if (!strcmp(cmd, "SETUP"))
2965 rtsp_cmd_setup(c, url, header);
2966 else if (!strcmp(cmd, "PLAY"))
2967 rtsp_cmd_play(c, url, header);
2968 else if (!strcmp(cmd, "PAUSE"))
2969 rtsp_cmd_pause(c, url, header);
2970 else if (!strcmp(cmd, "TEARDOWN"))
2971 rtsp_cmd_teardown(c, url, header);
2973 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2976 len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2977 c->pb = NULL; /* safety */
2979 /* XXX: cannot do more */
2982 c->buffer_ptr = c->pb_buffer;
2983 c->buffer_end = c->pb_buffer + len;
2984 c->state = RTSPSTATE_SEND_REPLY;
2988 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2989 struct in_addr my_ip)
2991 AVFormatContext *avc;
2992 AVStream *avs = NULL;
2993 AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
2996 avc = avformat_alloc_context();
2997 if (avc == NULL || !rtp_format) {
3000 avc->oformat = rtp_format;
3001 av_dict_set(&avc->metadata, "title",
3002 stream->title[0] ? stream->title : "No Title", 0);
3003 avc->nb_streams = stream->nb_streams;
3004 if (stream->is_multicast) {
3005 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
3006 inet_ntoa(stream->multicast_ip),
3007 stream->multicast_port, stream->multicast_ttl);
3009 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
3012 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
3013 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
3015 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
3016 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
3019 for(i = 0; i < stream->nb_streams; i++) {
3020 avc->streams[i] = &avs[i];
3021 avc->streams[i]->codec = stream->streams[i]->codec;
3023 *pbuffer = av_mallocz(2048);
3024 av_sdp_create(&avc, 1, *pbuffer, 2048);
3027 av_free(avc->streams);
3028 av_dict_free(&avc->metadata);
3032 return strlen(*pbuffer);
3035 static void rtsp_cmd_options(HTTPContext *c, const char *url)
3037 // rtsp_reply_header(c, RTSP_STATUS_OK);
3038 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
3039 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
3040 avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
3041 avio_printf(c->pb, "\r\n");
3044 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
3052 struct sockaddr_in my_addr;
3054 /* find which url is asked */
3055 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3060 for(stream = first_stream; stream != NULL; stream = stream->next) {
3061 if (!stream->is_feed &&
3062 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
3063 !strcmp(path, stream->filename)) {
3067 /* no stream found */
3068 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3072 /* prepare the media description in sdp format */
3074 /* get the host IP */
3075 len = sizeof(my_addr);
3076 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
3077 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3078 if (content_length < 0) {
3079 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3082 rtsp_reply_header(c, RTSP_STATUS_OK);
3083 avio_printf(c->pb, "Content-Base: %s/\r\n", url);
3084 avio_printf(c->pb, "Content-Type: application/sdp\r\n");
3085 avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
3086 avio_printf(c->pb, "\r\n");
3087 avio_write(c->pb, content, content_length);
3091 static HTTPContext *find_rtp_session(const char *session_id)
3095 if (session_id[0] == '\0')
3098 for(c = first_http_ctx; c != NULL; c = c->next) {
3099 if (!strcmp(c->session_id, session_id))
3105 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3107 RTSPTransportField *th;
3110 for(i=0;i<h->nb_transports;i++) {
3111 th = &h->transports[i];
3112 if (th->lower_transport == lower_transport)
3118 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3119 RTSPMessageHeader *h)
3122 int stream_index, rtp_port, rtcp_port;
3127 RTSPTransportField *th;
3128 struct sockaddr_in dest_addr;
3129 RTSPActionServerSetup setup;
3131 /* find which url is asked */
3132 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3137 /* now check each stream */
3138 for(stream = first_stream; stream != NULL; stream = stream->next) {
3139 if (!stream->is_feed &&
3140 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3141 /* accept aggregate filenames only if single stream */
3142 if (!strcmp(path, stream->filename)) {
3143 if (stream->nb_streams != 1) {
3144 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3151 for(stream_index = 0; stream_index < stream->nb_streams;
3153 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3154 stream->filename, stream_index);
3155 if (!strcmp(path, buf))
3160 /* no stream found */
3161 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3165 /* generate session id if needed */
3166 if (h->session_id[0] == '\0') {
3167 unsigned random0 = av_lfg_get(&random_state);
3168 unsigned random1 = av_lfg_get(&random_state);
3169 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3173 /* find rtp session, and create it if none found */
3174 rtp_c = find_rtp_session(h->session_id);
3176 /* always prefer UDP */
3177 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3179 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3181 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3186 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3187 th->lower_transport);
3189 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3193 /* open input stream */
3194 if (open_input_stream(rtp_c, "") < 0) {
3195 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3200 /* test if stream is OK (test needed because several SETUP needs
3201 to be done for a given file) */
3202 if (rtp_c->stream != stream) {
3203 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3207 /* test if stream is already set up */
3208 if (rtp_c->rtp_ctx[stream_index]) {
3209 rtsp_reply_error(c, RTSP_STATUS_STATE);
3213 /* check transport */
3214 th = find_transport(h, rtp_c->rtp_protocol);
3215 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3216 th->client_port_min <= 0)) {
3217 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3221 /* setup default options */
3222 setup.transport_option[0] = '\0';
3223 dest_addr = rtp_c->from_addr;
3224 dest_addr.sin_port = htons(th->client_port_min);
3227 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3228 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3232 /* now everything is OK, so we can send the connection parameters */
3233 rtsp_reply_header(c, RTSP_STATUS_OK);
3235 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3237 switch(rtp_c->rtp_protocol) {
3238 case RTSP_LOWER_TRANSPORT_UDP:
3239 rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3240 rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3241 avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3242 "client_port=%d-%d;server_port=%d-%d",
3243 th->client_port_min, th->client_port_max,
3244 rtp_port, rtcp_port);
3246 case RTSP_LOWER_TRANSPORT_TCP:
3247 avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3248 stream_index * 2, stream_index * 2 + 1);
3253 if (setup.transport_option[0] != '\0')
3254 avio_printf(c->pb, ";%s", setup.transport_option);
3255 avio_printf(c->pb, "\r\n");
3258 avio_printf(c->pb, "\r\n");
3262 /* find an rtp connection by using the session ID. Check consistency
3264 static HTTPContext *find_rtp_session_with_url(const char *url,
3265 const char *session_id)
3273 rtp_c = find_rtp_session(session_id);
3277 /* find which url is asked */
3278 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3282 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3283 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3284 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3285 rtp_c->stream->filename, s);
3286 if(!strncmp(path, buf, sizeof(buf))) {
3287 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3292 if (len > 0 && path[len - 1] == '/' &&
3293 !strncmp(path, rtp_c->stream->filename, len - 1))
3298 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3302 rtp_c = find_rtp_session_with_url(url, h->session_id);
3304 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3308 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3309 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3310 rtp_c->state != HTTPSTATE_READY) {
3311 rtsp_reply_error(c, RTSP_STATUS_STATE);
3315 rtp_c->state = HTTPSTATE_SEND_DATA;
3317 /* now everything is OK, so we can send the connection parameters */
3318 rtsp_reply_header(c, RTSP_STATUS_OK);
3320 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3321 avio_printf(c->pb, "\r\n");
3324 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3328 rtp_c = find_rtp_session_with_url(url, h->session_id);
3330 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3334 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3335 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3336 rtsp_reply_error(c, RTSP_STATUS_STATE);
3340 rtp_c->state = HTTPSTATE_READY;
3341 rtp_c->first_pts = AV_NOPTS_VALUE;
3342 /* now everything is OK, so we can send the connection parameters */
3343 rtsp_reply_header(c, RTSP_STATUS_OK);
3345 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3346 avio_printf(c->pb, "\r\n");
3349 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3353 rtp_c = find_rtp_session_with_url(url, h->session_id);
3355 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3359 /* now everything is OK, so we can send the connection parameters */
3360 rtsp_reply_header(c, RTSP_STATUS_OK);
3362 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3363 avio_printf(c->pb, "\r\n");
3365 /* abort the session */
3366 close_connection(rtp_c);
3370 /********************************************************************/
3373 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3374 FFStream *stream, const char *session_id,
3375 enum RTSPLowerTransport rtp_protocol)
3377 HTTPContext *c = NULL;
3378 const char *proto_str;
3380 /* XXX: should output a warning page when coming
3381 close to the connection limit */
3382 if (nb_connections >= nb_max_connections)
3385 /* add a new connection */
3386 c = av_mallocz(sizeof(HTTPContext));
3391 c->poll_entry = NULL;
3392 c->from_addr = *from_addr;
3393 c->buffer_size = IOBUFFER_INIT_SIZE;
3394 c->buffer = av_malloc(c->buffer_size);
3399 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3400 c->state = HTTPSTATE_READY;
3401 c->is_packetized = 1;
3402 c->rtp_protocol = rtp_protocol;
3404 /* protocol is shown in statistics */
3405 switch(c->rtp_protocol) {
3406 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3407 proto_str = "MCAST";
3409 case RTSP_LOWER_TRANSPORT_UDP:
3412 case RTSP_LOWER_TRANSPORT_TCP:
3419 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3420 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3422 current_bandwidth += stream->bandwidth;
3424 c->next = first_http_ctx;
3436 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3437 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3439 static int rtp_new_av_stream(HTTPContext *c,
3440 int stream_index, struct sockaddr_in *dest_addr,
3441 HTTPContext *rtsp_c)
3443 AVFormatContext *ctx;
3446 URLContext *h = NULL;
3448 int max_packet_size;
3450 /* now we can open the relevant output stream */
3451 ctx = avformat_alloc_context();
3454 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3456 st = av_mallocz(sizeof(AVStream));
3459 ctx->nb_streams = 1;
3460 ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3463 ctx->streams[0] = st;
3465 if (!c->stream->feed ||
3466 c->stream->feed == c->stream)
3467 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3470 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3472 st->priv_data = NULL;
3474 /* build destination RTP address */
3475 ipaddr = inet_ntoa(dest_addr->sin_addr);
3477 switch(c->rtp_protocol) {
3478 case RTSP_LOWER_TRANSPORT_UDP:
3479 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3482 /* XXX: also pass as parameter to function ? */
3483 if (c->stream->is_multicast) {
3485 ttl = c->stream->multicast_ttl;
3488 snprintf(ctx->filename, sizeof(ctx->filename),
3489 "rtp://%s:%d?multicast=1&ttl=%d",
3490 ipaddr, ntohs(dest_addr->sin_port), ttl);
3492 snprintf(ctx->filename, sizeof(ctx->filename),
3493 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3496 if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3498 c->rtp_handles[stream_index] = h;
3499 max_packet_size = h->max_packet_size;
3501 case RTSP_LOWER_TRANSPORT_TCP:
3504 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3510 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3511 ipaddr, ntohs(dest_addr->sin_port),
3512 c->stream->filename, stream_index, c->protocol);
3514 /* normally, no packets should be output here, but the packet size may be checked */
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 http_log("Opening file '%s'\n", stream->feed_filename);
3694 if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3695 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3696 /* remove stream (no need to spend more time on it) */
3698 remove_stream(stream);
3700 /* find all the AVStreams inside and reference them in
3702 if (avformat_find_stream_info(infile, NULL) < 0) {
3703 http_log("Could not find codec parameters from '%s'\n",
3704 stream->feed_filename);
3705 avformat_close_input(&infile);
3708 extract_mpeg4_header(infile);
3710 for(i=0;i<infile->nb_streams;i++)
3711 add_av_stream1(stream, infile->streams[i]->codec, 1);
3713 avformat_close_input(&infile);
3719 /* compute the needed AVStream for each feed */
3720 static void build_feed_streams(void)
3722 FFStream *stream, *feed;
3725 /* gather all streams */
3726 for(stream = first_stream; stream != NULL; stream = stream->next) {
3727 feed = stream->feed;
3729 if (stream->is_feed) {
3730 for(i=0;i<stream->nb_streams;i++)
3731 stream->feed_streams[i] = i;
3733 /* we handle a stream coming from a feed */
3734 for(i=0;i<stream->nb_streams;i++)
3735 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3740 /* create feed files if needed */
3741 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3744 if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3745 /* See if it matches */
3746 AVFormatContext *s = NULL;
3749 if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3750 /* set buffer size */
3751 ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
3752 /* Now see if it matches */
3753 if (s->nb_streams == feed->nb_streams) {
3755 for(i=0;i<s->nb_streams;i++) {
3757 sf = feed->streams[i];
3760 if (sf->index != ss->index ||
3762 http_log("Index & Id do not match for stream %d (%s)\n",
3763 i, feed->feed_filename);
3766 AVCodecContext *ccf, *ccs;
3770 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3772 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3773 http_log("Codecs do not match for stream %d\n", i);
3775 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3776 http_log("Codec bitrates do not match for stream %d\n", i);
3778 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3779 if (CHECK_CODEC(time_base.den) ||
3780 CHECK_CODEC(time_base.num) ||
3781 CHECK_CODEC(width) ||
3782 CHECK_CODEC(height)) {
3783 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3786 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3787 if (CHECK_CODEC(sample_rate) ||
3788 CHECK_CODEC(channels) ||
3789 CHECK_CODEC(frame_size)) {
3790 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3794 http_log("Unknown codec type\n");
3802 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3803 feed->feed_filename, s->nb_streams, feed->nb_streams);
3805 avformat_close_input(&s);
3807 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3808 feed->feed_filename);
3811 if (feed->readonly) {
3812 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3813 feed->feed_filename);
3816 unlink(feed->feed_filename);
3819 if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3820 AVFormatContext s1 = {0}, *s = &s1;
3822 if (feed->readonly) {
3823 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3824 feed->feed_filename);
3828 /* only write the header of the ffm file */
3829 if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3830 http_log("Could not open output feed file '%s'\n",
3831 feed->feed_filename);
3834 s->oformat = feed->fmt;
3835 s->nb_streams = feed->nb_streams;
3836 s->streams = feed->streams;
3837 if (avformat_write_header(s, NULL) < 0) {
3838 http_log("Container doesn't support the required parameters\n");
3841 /* XXX: need better api */
3842 av_freep(&s->priv_data);
3845 /* get feed size and write index */
3846 fd = open(feed->feed_filename, O_RDONLY);
3848 http_log("Could not open output feed file '%s'\n",
3849 feed->feed_filename);
3853 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3854 feed->feed_size = lseek(fd, 0, SEEK_END);
3855 /* ensure that we do not wrap before the end of file */
3856 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3857 feed->feed_max_size = feed->feed_size;
3863 /* compute the bandwidth used by each stream */
3864 static void compute_bandwidth(void)
3870 for(stream = first_stream; stream != NULL; stream = stream->next) {
3872 for(i=0;i<stream->nb_streams;i++) {
3873 AVStream *st = stream->streams[i];
3874 switch(st->codec->codec_type) {
3875 case AVMEDIA_TYPE_AUDIO:
3876 case AVMEDIA_TYPE_VIDEO:
3877 bandwidth += st->codec->bit_rate;
3883 stream->bandwidth = (bandwidth + 999) / 1000;
3887 /* add a codec and set the default parameters */
3888 static void add_codec(FFStream *stream, AVCodecContext *av)
3892 if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
3895 /* compute default parameters */
3896 switch(av->codec_type) {
3897 case AVMEDIA_TYPE_AUDIO:
3898 if (av->bit_rate == 0)
3899 av->bit_rate = 64000;
3900 if (av->sample_rate == 0)
3901 av->sample_rate = 22050;
3902 if (av->channels == 0)
3905 case AVMEDIA_TYPE_VIDEO:
3906 if (av->bit_rate == 0)
3907 av->bit_rate = 64000;
3908 if (av->time_base.num == 0){
3909 av->time_base.den = 5;
3910 av->time_base.num = 1;
3912 if (av->width == 0 || av->height == 0) {
3916 /* Bitrate tolerance is less for streaming */
3917 if (av->bit_rate_tolerance == 0)
3918 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3919 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3924 if (av->max_qdiff == 0)
3926 av->qcompress = 0.5;
3929 if (!av->nsse_weight)
3930 av->nsse_weight = 8;
3932 av->frame_skip_cmp = FF_CMP_DCTMAX;
3934 av->me_method = ME_EPZS;
3935 av->rc_buffer_aggressivity = 1.0;
3938 av->rc_eq = av_strdup("tex^qComp");
3939 if (!av->i_quant_factor)
3940 av->i_quant_factor = -0.8;
3941 if (!av->b_quant_factor)
3942 av->b_quant_factor = 1.25;
3943 if (!av->b_quant_offset)
3944 av->b_quant_offset = 1.25;
3945 if (!av->rc_max_rate)
3946 av->rc_max_rate = av->bit_rate * 2;
3948 if (av->rc_max_rate && !av->rc_buffer_size) {
3949 av->rc_buffer_size = av->rc_max_rate;
3958 st = av_mallocz(sizeof(AVStream));
3961 st->codec = avcodec_alloc_context3(NULL);
3962 stream->streams[stream->nb_streams++] = st;
3963 memcpy(st->codec, av, sizeof(AVCodecContext));
3966 static enum AVCodecID opt_codec(const char *name, enum AVMediaType type)
3968 AVCodec *codec = avcodec_find_encoder_by_name(name);
3970 if (!codec || codec->type != type)
3971 return AV_CODEC_ID_NONE;
3975 static int ffserver_opt_default(const char *opt, const char *arg,
3976 AVCodecContext *avctx, int type)
3979 const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3981 ret = av_opt_set(avctx, opt, arg, 0);
3985 static int ffserver_opt_preset(const char *arg,
3986 AVCodecContext *avctx, int type,
3987 enum AVCodecID *audio_id, enum AVCodecID *video_id)
3990 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3992 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3994 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3995 codec ? codec->name : NULL))) {
3996 fprintf(stderr, "File for preset '%s' not found\n", arg);
4001 int e= fscanf(f, "%999[^\n]\n", line) - 1;
4002 if(line[0] == '#' && !e)
4004 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
4006 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
4010 if(!strcmp(tmp, "acodec")){
4011 *audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO);
4012 }else if(!strcmp(tmp, "vcodec")){
4013 *video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO);
4014 }else if(!strcmp(tmp, "scodec")){
4015 /* opt_subtitle_codec(tmp2); */
4016 }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
4017 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
4028 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
4029 const char *mime_type)
4031 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
4034 AVOutputFormat *stream_fmt;
4035 char stream_format_name[64];
4037 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
4038 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4047 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
4051 fprintf(stderr, "%s:%d: ", filename, line_num);
4052 vfprintf(stderr, fmt, vl);
4058 static int parse_ffconfig(const char *filename)
4065 int val, errors, line_num;
4066 FFStream **last_stream, *stream, *redirect;
4067 FFStream **last_feed, *feed, *s;
4068 AVCodecContext audio_enc, video_enc;
4069 enum AVCodecID audio_id, video_id;
4072 f = fopen(filename, "r");
4074 ret = AVERROR(errno);
4075 av_log(NULL, AV_LOG_ERROR, "Could not open the configuration file '%s'\n", filename);
4081 first_stream = NULL;
4082 last_stream = &first_stream;
4084 last_feed = &first_feed;
4088 audio_id = AV_CODEC_ID_NONE;
4089 video_id = AV_CODEC_ID_NONE;
4091 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4093 if (fgets(line, sizeof(line), f) == NULL)
4097 while (av_isspace(*p))
4099 if (*p == '\0' || *p == '#')
4102 get_arg(cmd, sizeof(cmd), &p);
4104 if (!av_strcasecmp(cmd, "Port")) {
4105 get_arg(arg, sizeof(arg), &p);
4107 if (val < 1 || val > 65536) {
4108 ERROR("Invalid_port: %s\n", arg);
4110 my_http_addr.sin_port = htons(val);
4111 } else if (!av_strcasecmp(cmd, "BindAddress")) {
4112 get_arg(arg, sizeof(arg), &p);
4113 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4114 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4116 } else if (!av_strcasecmp(cmd, "NoDaemon")) {
4117 // do nothing here, its the default now
4118 } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4119 get_arg(arg, sizeof(arg), &p);
4121 if (val < 1 || val > 65536) {
4122 ERROR("%s:%d: Invalid port: %s\n", arg);
4124 my_rtsp_addr.sin_port = htons(atoi(arg));
4125 } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4126 get_arg(arg, sizeof(arg), &p);
4127 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4128 ERROR("Invalid host/IP address: %s\n", arg);
4130 } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4131 get_arg(arg, sizeof(arg), &p);
4133 if (val < 1 || val > 65536) {
4134 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4136 nb_max_http_connections = val;
4137 } else if (!av_strcasecmp(cmd, "MaxClients")) {
4138 get_arg(arg, sizeof(arg), &p);
4140 if (val < 1 || val > nb_max_http_connections) {
4141 ERROR("Invalid MaxClients: %s\n", arg);
4143 nb_max_connections = val;
4145 } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4147 get_arg(arg, sizeof(arg), &p);
4148 llval = strtoll(arg, NULL, 10);
4149 if (llval < 10 || llval > 10000000) {
4150 ERROR("Invalid MaxBandwidth: %s\n", arg);
4152 max_bandwidth = llval;
4153 } else if (!av_strcasecmp(cmd, "CustomLog")) {
4154 if (!ffserver_debug)
4155 get_arg(logfilename, sizeof(logfilename), &p);
4156 } else if (!av_strcasecmp(cmd, "<Feed")) {
4157 /*********************************************/
4158 /* Feed related options */
4160 if (stream || feed) {
4161 ERROR("Already in a tag\n");
4163 feed = av_mallocz(sizeof(FFStream));
4165 ret = AVERROR(ENOMEM);
4168 get_arg(feed->filename, sizeof(feed->filename), &p);
4169 q = strrchr(feed->filename, '>');
4173 for (s = first_feed; s; s = s->next) {
4174 if (!strcmp(feed->filename, s->filename)) {
4175 ERROR("Feed '%s' already registered\n", s->filename);
4179 feed->fmt = av_guess_format("ffm", NULL, NULL);
4180 /* default feed file */
4181 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4182 "/tmp/%s.ffm", feed->filename);
4183 feed->feed_max_size = 5 * 1024 * 1024;
4185 feed->feed = feed; /* self feeding :-) */
4187 /* add in stream list */
4188 *last_stream = feed;
4189 last_stream = &feed->next;
4190 /* add in feed list */
4192 last_feed = &feed->next_feed;
4194 } else if (!av_strcasecmp(cmd, "Launch")) {
4198 feed->child_argv = av_mallocz(64 * sizeof(char *));
4199 if (!feed->child_argv) {
4200 ret = AVERROR(ENOMEM);
4203 for (i = 0; i < 62; i++) {
4204 get_arg(arg, sizeof(arg), &p);
4208 feed->child_argv[i] = av_strdup(arg);
4209 if (!feed->child_argv[i]) {
4210 ret = AVERROR(ENOMEM);
4215 feed->child_argv[i] =
4216 av_asprintf("http://%s:%d/%s",
4217 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4218 inet_ntoa(my_http_addr.sin_addr), ntohs(my_http_addr.sin_port),
4220 if (!feed->child_argv[i]) {
4221 ret = AVERROR(ENOMEM);
4225 } else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) {
4227 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4228 feed->readonly = !av_strcasecmp(cmd, "ReadOnlyFile");
4229 } else if (stream) {
4230 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4232 } else if (!av_strcasecmp(cmd, "Truncate")) {
4234 get_arg(arg, sizeof(arg), &p);
4235 feed->truncate = strtod(arg, NULL);
4237 } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4242 get_arg(arg, sizeof(arg), &p);
4244 fsize = strtod(p1, &p1);
4245 switch(av_toupper(*p1)) {
4250 fsize *= 1024 * 1024;
4253 fsize *= 1024 * 1024 * 1024;
4256 feed->feed_max_size = (int64_t)fsize;
4257 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4258 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4261 } else if (!av_strcasecmp(cmd, "</Feed>")) {
4263 ERROR("No corresponding <Feed> for </Feed>\n");
4266 } else if (!av_strcasecmp(cmd, "<Stream")) {
4267 /*********************************************/
4268 /* Stream related options */
4270 if (stream || feed) {
4271 ERROR("Already in a tag\n");
4274 stream = av_mallocz(sizeof(FFStream));
4276 ret = AVERROR(ENOMEM);
4279 get_arg(stream->filename, sizeof(stream->filename), &p);
4280 q = strrchr(stream->filename, '>');
4284 for (s = first_stream; s; s = s->next) {
4285 if (!strcmp(stream->filename, s->filename)) {
4286 ERROR("Stream '%s' already registered\n", s->filename);
4290 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4291 avcodec_get_context_defaults3(&video_enc, NULL);
4292 avcodec_get_context_defaults3(&audio_enc, NULL);
4294 audio_id = AV_CODEC_ID_NONE;
4295 video_id = AV_CODEC_ID_NONE;
4297 audio_id = stream->fmt->audio_codec;
4298 video_id = stream->fmt->video_codec;
4301 *last_stream = stream;
4302 last_stream = &stream->next;
4304 } else if (!av_strcasecmp(cmd, "Feed")) {
4305 get_arg(arg, sizeof(arg), &p);
4310 while (sfeed != NULL) {
4311 if (!strcmp(sfeed->filename, arg))
4313 sfeed = sfeed->next_feed;
4316 ERROR("feed '%s' not defined\n", arg);
4318 stream->feed = sfeed;
4320 } else if (!av_strcasecmp(cmd, "Format")) {
4321 get_arg(arg, sizeof(arg), &p);
4323 if (!strcmp(arg, "status")) {
4324 stream->stream_type = STREAM_TYPE_STATUS;
4327 stream->stream_type = STREAM_TYPE_LIVE;
4328 /* jpeg cannot be used here, so use single frame jpeg */
4329 if (!strcmp(arg, "jpeg"))
4330 strcpy(arg, "mjpeg");
4331 stream->fmt = ffserver_guess_format(arg, NULL, NULL);
4333 ERROR("Unknown Format: %s\n", arg);
4337 audio_id = stream->fmt->audio_codec;
4338 video_id = stream->fmt->video_codec;
4341 } else if (!av_strcasecmp(cmd, "InputFormat")) {
4342 get_arg(arg, sizeof(arg), &p);
4344 stream->ifmt = av_find_input_format(arg);
4345 if (!stream->ifmt) {
4346 ERROR("Unknown input format: %s\n", arg);
4349 } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4350 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4351 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4353 ERROR("FaviconURL only permitted for status streams\n");
4355 } else if (!av_strcasecmp(cmd, "Author")) {
4357 get_arg(stream->author, sizeof(stream->author), &p);
4358 } else if (!av_strcasecmp(cmd, "Comment")) {
4360 get_arg(stream->comment, sizeof(stream->comment), &p);
4361 } else if (!av_strcasecmp(cmd, "Copyright")) {
4363 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4364 } else if (!av_strcasecmp(cmd, "Title")) {
4366 get_arg(stream->title, sizeof(stream->title), &p);
4367 } else if (!av_strcasecmp(cmd, "Preroll")) {
4368 get_arg(arg, sizeof(arg), &p);
4370 stream->prebuffer = atof(arg) * 1000;
4371 } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4373 stream->send_on_key = 1;
4374 } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4375 get_arg(arg, sizeof(arg), &p);
4376 audio_id = opt_codec(arg, AVMEDIA_TYPE_AUDIO);
4377 if (audio_id == AV_CODEC_ID_NONE) {
4378 ERROR("Unknown AudioCodec: %s\n", arg);
4380 } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4381 get_arg(arg, sizeof(arg), &p);
4382 video_id = opt_codec(arg, AVMEDIA_TYPE_VIDEO);
4383 if (video_id == AV_CODEC_ID_NONE) {
4384 ERROR("Unknown VideoCodec: %s\n", arg);
4386 } else if (!av_strcasecmp(cmd, "MaxTime")) {
4387 get_arg(arg, sizeof(arg), &p);
4389 stream->max_time = atof(arg) * 1000;
4390 } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4391 get_arg(arg, sizeof(arg), &p);
4393 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4394 } else if (!av_strcasecmp(cmd, "AudioChannels")) {
4395 get_arg(arg, sizeof(arg), &p);
4397 audio_enc.channels = atoi(arg);
4398 } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4399 get_arg(arg, sizeof(arg), &p);
4401 audio_enc.sample_rate = atoi(arg);
4402 } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4404 int minrate, maxrate;
4406 get_arg(arg, sizeof(arg), &p);
4408 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4409 video_enc.rc_min_rate = minrate * 1000;
4410 video_enc.rc_max_rate = maxrate * 1000;
4412 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4415 } else if (!av_strcasecmp(cmd, "Debug")) {
4417 get_arg(arg, sizeof(arg), &p);
4418 video_enc.debug = strtol(arg,0,0);
4420 } else if (!av_strcasecmp(cmd, "Strict")) {
4422 get_arg(arg, sizeof(arg), &p);
4423 video_enc.strict_std_compliance = atoi(arg);
4425 } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4427 get_arg(arg, sizeof(arg), &p);
4428 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4430 } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4432 get_arg(arg, sizeof(arg), &p);
4433 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4435 } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4436 get_arg(arg, sizeof(arg), &p);
4438 video_enc.bit_rate = atoi(arg) * 1000;
4440 } else if (!av_strcasecmp(cmd, "VideoSize")) {
4441 get_arg(arg, sizeof(arg), &p);
4443 ret = av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4445 ERROR("Invalid video size '%s'\n", arg);
4447 if ((video_enc.width % 16) != 0 ||
4448 (video_enc.height % 16) != 0) {
4449 ERROR("Image size must be a multiple of 16\n");
4453 } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4454 get_arg(arg, sizeof(arg), &p);
4456 AVRational frame_rate;
4457 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4458 ERROR("Incorrect frame rate: %s\n", arg);
4460 video_enc.time_base.num = frame_rate.den;
4461 video_enc.time_base.den = frame_rate.num;
4464 } else if (!av_strcasecmp(cmd, "PixelFormat")) {
4465 get_arg(arg, sizeof(arg), &p);
4467 video_enc.pix_fmt = av_get_pix_fmt(arg);
4468 if (video_enc.pix_fmt == AV_PIX_FMT_NONE) {
4469 ERROR("Unknown pixel format: %s\n", arg);
4472 } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4473 get_arg(arg, sizeof(arg), &p);
4475 video_enc.gop_size = atoi(arg);
4476 } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4478 video_enc.gop_size = 1;
4479 } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4481 video_enc.mb_decision = FF_MB_DECISION_BITS;
4482 } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4484 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4485 video_enc.flags |= CODEC_FLAG_4MV;
4487 } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4488 !av_strcasecmp(cmd, "AVOptionAudio")) {
4490 AVCodecContext *avctx;
4492 get_arg(arg, sizeof(arg), &p);
4493 get_arg(arg2, sizeof(arg2), &p);
4494 if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4496 type = AV_OPT_FLAG_VIDEO_PARAM;
4499 type = AV_OPT_FLAG_AUDIO_PARAM;
4501 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4502 ERROR("Error setting %s option to %s %s\n", cmd, arg, arg2);
4504 } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4505 !av_strcasecmp(cmd, "AVPresetAudio")) {
4506 AVCodecContext *avctx;
4508 get_arg(arg, sizeof(arg), &p);
4509 if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4511 video_enc.codec_id = video_id;
4512 type = AV_OPT_FLAG_VIDEO_PARAM;
4515 audio_enc.codec_id = audio_id;
4516 type = AV_OPT_FLAG_AUDIO_PARAM;
4518 if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4519 ERROR("AVPreset error: %s\n", arg);
4521 } else if (!av_strcasecmp(cmd, "VideoTag")) {
4522 get_arg(arg, sizeof(arg), &p);
4523 if ((strlen(arg) == 4) && stream)
4524 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4525 } else if (!av_strcasecmp(cmd, "BitExact")) {
4527 video_enc.flags |= CODEC_FLAG_BITEXACT;
4528 } else if (!av_strcasecmp(cmd, "DctFastint")) {
4530 video_enc.dct_algo = FF_DCT_FASTINT;
4531 } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4533 video_enc.idct_algo = FF_IDCT_SIMPLE;
4534 } else if (!av_strcasecmp(cmd, "Qscale")) {
4535 get_arg(arg, sizeof(arg), &p);
4537 video_enc.flags |= CODEC_FLAG_QSCALE;
4538 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4540 } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4541 get_arg(arg, sizeof(arg), &p);
4543 video_enc.max_qdiff = atoi(arg);
4544 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4545 ERROR("VideoQDiff out of range\n");
4548 } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4549 get_arg(arg, sizeof(arg), &p);
4551 video_enc.qmax = atoi(arg);
4552 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4553 ERROR("VideoQMax out of range\n");
4556 } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4557 get_arg(arg, sizeof(arg), &p);
4559 video_enc.qmin = atoi(arg);
4560 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4561 ERROR("VideoQMin out of range\n");
4564 } else if (!av_strcasecmp(cmd, "LumiMask")) {
4565 get_arg(arg, sizeof(arg), &p);
4567 video_enc.lumi_masking = atof(arg);
4568 } else if (!av_strcasecmp(cmd, "DarkMask")) {
4569 get_arg(arg, sizeof(arg), &p);
4571 video_enc.dark_masking = atof(arg);
4572 } else if (!av_strcasecmp(cmd, "NoVideo")) {
4573 video_id = AV_CODEC_ID_NONE;
4574 } else if (!av_strcasecmp(cmd, "NoAudio")) {
4575 audio_id = AV_CODEC_ID_NONE;
4576 } else if (!av_strcasecmp(cmd, "ACL")) {
4577 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4578 } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4580 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4582 } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4583 get_arg(arg, sizeof(arg), &p);
4585 av_freep(&stream->rtsp_option);
4586 stream->rtsp_option = av_strdup(arg);
4588 } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4589 get_arg(arg, sizeof(arg), &p);
4591 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4592 ERROR("Invalid host/IP address: %s\n", arg);
4594 stream->is_multicast = 1;
4595 stream->loop = 1; /* default is looping */
4597 } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4598 get_arg(arg, sizeof(arg), &p);
4600 stream->multicast_port = atoi(arg);
4601 } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4602 get_arg(arg, sizeof(arg), &p);
4604 stream->multicast_ttl = atoi(arg);
4605 } else if (!av_strcasecmp(cmd, "NoLoop")) {
4608 } else if (!av_strcasecmp(cmd, "</Stream>")) {
4610 ERROR("No corresponding <Stream> for </Stream>\n");
4612 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4613 if (audio_id != AV_CODEC_ID_NONE) {
4614 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4615 audio_enc.codec_id = audio_id;
4616 add_codec(stream, &audio_enc);
4618 if (video_id != AV_CODEC_ID_NONE) {
4619 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4620 video_enc.codec_id = video_id;
4621 add_codec(stream, &video_enc);
4626 } else if (!av_strcasecmp(cmd, "<Redirect")) {
4627 /*********************************************/
4629 if (stream || feed || redirect) {
4630 ERROR("Already in a tag\n");
4632 redirect = av_mallocz(sizeof(FFStream));
4634 ret = AVERROR(ENOMEM);
4637 *last_stream = redirect;
4638 last_stream = &redirect->next;
4640 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4641 q = strrchr(redirect->filename, '>');
4644 redirect->stream_type = STREAM_TYPE_REDIRECT;
4646 } else if (!av_strcasecmp(cmd, "URL")) {
4648 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4649 } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4651 ERROR("No corresponding <Redirect> for </Redirect>\n");
4653 if (!redirect->feed_filename[0]) {
4654 ERROR("No URL found for <Redirect>\n");
4658 } else if (!av_strcasecmp(cmd, "LoadModule")) {
4659 ERROR("Loadable modules no longer supported\n");
4661 ERROR("Incorrect keyword: '%s'\n", cmd);
4671 return AVERROR(EINVAL);
4676 static void handle_child_exit(int sig)
4681 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4684 for (feed = first_feed; feed; feed = feed->next) {
4685 if (feed->pid == pid) {
4686 int uptime = time(0) - feed->pid_start;
4689 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4692 /* Turn off any more restarts */
4693 feed->child_argv = 0;
4698 need_to_start_children = 1;
4701 static void opt_debug(void)
4704 logfilename[0] = '-';
4707 void show_help_default(const char *opt, const char *arg)
4709 printf("usage: ffserver [options]\n"
4710 "Hyper fast multi format Audio/Video streaming server\n");
4712 show_help_options(options, "Main options:", 0, 0, 0);
4715 static const OptionDef options[] = {
4716 #include "cmdutils_common_opts.h"
4717 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4718 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4719 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4723 int main(int argc, char **argv)
4725 struct sigaction sigact = { { 0 } };
4728 config_filename = av_strdup("/etc/ffserver.conf");
4730 parse_loglevel(argc, argv, options);
4732 avformat_network_init();
4734 show_banner(argc, argv, options);
4736 my_program_name = argv[0];
4738 parse_options(NULL, argc, argv, options, NULL);
4740 unsetenv("http_proxy"); /* Kill the http_proxy */
4742 av_lfg_init(&random_state, av_get_random_seed());
4744 sigact.sa_handler = handle_child_exit;
4745 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4746 sigaction(SIGCHLD, &sigact, 0);
4748 if ((ret = parse_ffconfig(config_filename)) < 0) {
4749 fprintf(stderr, "Error reading configuration file '%s': %s\n",
4750 config_filename, av_err2str(ret));
4753 av_freep(&config_filename);
4755 /* open log file if needed */
4756 if (logfilename[0] != '\0') {
4757 if (!strcmp(logfilename, "-"))
4760 logfile = fopen(logfilename, "a");
4761 av_log_set_callback(http_av_log);
4764 build_file_streams();
4766 build_feed_streams();
4768 compute_bandwidth();
4771 signal(SIGPIPE, SIG_IGN);
4773 if (http_server() < 0) {
4774 http_log("Could not start server\n");