2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of Libav.
7 * Libav is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * Libav is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with Libav; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 #define closesocket close
28 #include "libavformat/avformat.h"
29 // FIXME those are internal headers, avserver _really_ shouldn't use them
30 #include "libavformat/ffm.h"
31 #include "libavformat/network.h"
32 #include "libavformat/os_support.h"
33 #include "libavformat/rtpdec.h"
34 #include "libavformat/rtsp.h"
35 #include "libavformat/avio_internal.h"
36 #include "libavformat/internal.h"
37 #include "libavformat/url.h"
39 #include "libavutil/avstring.h"
40 #include "libavutil/lfg.h"
41 #include "libavutil/dict.h"
42 #include "libavutil/mathematics.h"
43 #include "libavutil/random_seed.h"
44 #include "libavutil/parseutils.h"
45 #include "libavutil/opt.h"
46 #include "libavutil/time.h"
51 #include <sys/ioctl.h>
65 const char program_name[] = "avserver";
66 const int program_birth_year = 2000;
68 static const OptionDef options[];
71 HTTPSTATE_WAIT_REQUEST,
72 HTTPSTATE_SEND_HEADER,
73 HTTPSTATE_SEND_DATA_HEADER,
74 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
75 HTTPSTATE_SEND_DATA_TRAILER,
76 HTTPSTATE_RECEIVE_DATA,
77 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
80 RTSPSTATE_WAIT_REQUEST,
82 RTSPSTATE_SEND_PACKET,
85 static const char *http_state[] = {
101 #define MAX_STREAMS 20
103 #define IOBUFFER_INIT_SIZE 8192
105 /* timeouts are in ms */
106 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
107 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
109 #define SYNC_TIMEOUT (10 * 1000)
111 typedef struct RTSPActionServerSetup {
113 char transport_option[512];
114 } RTSPActionServerSetup;
117 int64_t count1, count2;
118 int64_t time1, time2;
121 /* context associated with one connection */
122 typedef struct HTTPContext {
123 enum HTTPState state;
124 int fd; /* socket file descriptor */
125 struct sockaddr_in from_addr; /* origin */
126 struct pollfd *poll_entry; /* used when polling */
128 uint8_t *buffer_ptr, *buffer_end;
131 int chunked_encoding;
132 int chunk_size; /* 0 if it needs to be read */
133 struct HTTPContext *next;
134 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
138 /* input format handling */
139 AVFormatContext *fmt_in;
140 int64_t start_time; /* In milliseconds - this wraps fairly often */
141 int64_t first_pts; /* initial pts value */
142 int64_t cur_pts; /* current pts value from the stream in us */
143 int64_t cur_frame_duration; /* duration of the current frame in us */
144 int cur_frame_bytes; /* output frame size, needed to compute
145 the time at which we send each
147 int pts_stream_index; /* stream we choose as clock reference */
148 int64_t cur_clock; /* current clock reference value in us */
149 /* output format handling */
150 struct FFStream *stream;
151 /* -1 is invalid stream */
152 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
153 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
155 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
156 int last_packet_sent; /* true if last data packet was sent */
158 DataRateData datarate;
165 int is_packetized; /* if true, the stream is packetized */
166 int packet_stream_index; /* current stream for output in state machine */
168 /* RTSP state specific */
169 uint8_t *pb_buffer; /* XXX: use that in all the code */
171 int seq; /* RTSP sequence number */
173 /* RTP state specific */
174 enum RTSPLowerTransport rtp_protocol;
175 char session_id[32]; /* session id */
176 AVFormatContext *rtp_ctx[MAX_STREAMS];
178 /* RTP/UDP specific */
179 URLContext *rtp_handles[MAX_STREAMS];
181 /* RTP/TCP specific */
182 struct HTTPContext *rtsp_c;
183 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
186 /* each generated stream is described here */
190 STREAM_TYPE_REDIRECT,
193 enum IPAddressAction {
198 typedef struct IPAddressACL {
199 struct IPAddressACL *next;
200 enum IPAddressAction action;
201 /* These are in host order */
202 struct in_addr first;
206 /* description of each stream of the avserver.conf file */
207 typedef struct FFStream {
208 enum StreamType stream_type;
209 char filename[1024]; /* stream filename */
210 struct FFStream *feed; /* feed we are using (can be null if
212 AVDictionary *in_opts; /* input parameters */
213 AVInputFormat *ifmt; /* if non NULL, force input format */
216 char dynamic_acl[1024];
218 int prebuffer; /* Number of millseconds early to start */
219 int64_t max_time; /* Number of milliseconds to run */
221 AVStream *streams[MAX_STREAMS];
222 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
223 char feed_filename[1024]; /* file name of the feed storage, or
224 input file name for a stream */
229 pid_t pid; /* of avconv process */
230 time_t pid_start; /* of avconv process */
232 struct FFStream *next;
233 unsigned bandwidth; /* bandwidth, in kbits/s */
236 /* multicast specific */
238 struct in_addr multicast_ip;
239 int multicast_port; /* first port used for multicast */
241 int loop; /* if true, send the stream in loops (only meaningful if file) */
244 int feed_opened; /* true if someone is writing to the feed */
245 int is_feed; /* true if it is a feed */
246 int readonly; /* True if writing is prohibited to the file */
247 int truncate; /* True if feeder connection truncate the feed file */
249 int64_t bytes_served;
250 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
251 int64_t feed_write_index; /* current write position in feed (it wraps around) */
252 int64_t feed_size; /* current size of feed */
253 struct FFStream *next_feed;
256 typedef struct FeedData {
257 long long data_count;
258 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
261 static struct sockaddr_in my_http_addr;
262 static struct sockaddr_in my_rtsp_addr;
264 static char logfilename[1024];
265 static HTTPContext *first_http_ctx;
266 static FFStream *first_feed; /* contains only feeds */
267 static FFStream *first_stream; /* contains all streams, including feeds */
269 static void new_connection(int server_fd, int is_rtsp);
270 static void close_connection(HTTPContext *c);
273 static int handle_connection(HTTPContext *c);
274 static int http_parse_request(HTTPContext *c);
275 static int http_send_data(HTTPContext *c);
276 static void compute_status(HTTPContext *c);
277 static int open_input_stream(HTTPContext *c, const char *info);
278 static int http_start_receive_data(HTTPContext *c);
279 static int http_receive_data(HTTPContext *c);
282 static int rtsp_parse_request(HTTPContext *c);
283 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
284 static void rtsp_cmd_options(HTTPContext *c, const char *url);
285 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
286 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
287 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
288 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
291 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
292 struct in_addr my_ip);
295 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
296 FFStream *stream, const char *session_id,
297 enum RTSPLowerTransport rtp_protocol);
298 static int rtp_new_av_stream(HTTPContext *c,
299 int stream_index, struct sockaddr_in *dest_addr,
300 HTTPContext *rtsp_c);
302 static const char *my_program_name;
303 static const char *my_program_dir;
305 static const char *config_filename = "/etc/avserver.conf";
307 static int avserver_debug;
308 static int avserver_daemon;
309 static int no_launch;
310 static int need_to_start_children;
312 /* maximum number of simultaneous HTTP connections */
313 static unsigned int nb_max_http_connections = 2000;
314 static unsigned int nb_max_connections = 5;
315 static unsigned int nb_connections;
317 static uint64_t max_bandwidth = 1000;
318 static uint64_t current_bandwidth;
320 static int64_t cur_time; // Making this global saves on passing it around everywhere
322 static AVLFG random_state;
324 static FILE *logfile = NULL;
326 /* FIXME: make avserver work with IPv6 */
327 /* resolve host with also IP address parsing */
328 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
331 if (!ff_inet_aton(hostname, sin_addr)) {
333 struct addrinfo *ai, *cur;
334 struct addrinfo hints = { 0 };
335 hints.ai_family = AF_INET;
336 if (getaddrinfo(hostname, NULL, &hints, &ai))
338 /* getaddrinfo returns a linked list of addrinfo structs.
339 * Even if we set ai_family = AF_INET above, make sure
340 * that the returned one actually is of the correct type. */
341 for (cur = ai; cur; cur = cur->ai_next) {
342 if (cur->ai_family == AF_INET) {
343 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
352 hp = gethostbyname(hostname);
355 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
361 static char *ctime1(char *buf2)
369 p = buf2 + strlen(p) - 1;
375 static void http_vlog(const char *fmt, va_list vargs)
377 static int print_prefix = 1;
382 fprintf(logfile, "%s ", buf);
384 print_prefix = strstr(fmt, "\n") != NULL;
385 vfprintf(logfile, fmt, vargs);
391 __attribute__ ((format (printf, 1, 2)))
393 static void http_log(const char *fmt, ...)
396 va_start(vargs, fmt);
397 http_vlog(fmt, vargs);
401 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
403 static int print_prefix = 1;
404 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
405 if (level > av_log_get_level())
407 if (print_prefix && avc)
408 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
409 print_prefix = strstr(fmt, "\n") != NULL;
410 http_vlog(fmt, vargs);
413 static void log_connection(HTTPContext *c)
418 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
419 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
420 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
423 static void update_datarate(DataRateData *drd, int64_t count)
425 if (!drd->time1 && !drd->count1) {
426 drd->time1 = drd->time2 = cur_time;
427 drd->count1 = drd->count2 = count;
428 } else if (cur_time - drd->time2 > 5000) {
429 drd->time1 = drd->time2;
430 drd->count1 = drd->count2;
431 drd->time2 = cur_time;
436 /* In bytes per second */
437 static int compute_datarate(DataRateData *drd, int64_t count)
439 if (cur_time == drd->time1)
442 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
446 static void start_children(FFStream *feed)
451 for (; feed; feed = feed->next) {
452 if (feed->child_argv && !feed->pid) {
453 feed->pid_start = time(0);
458 http_log("Unable to create children\n");
467 av_strlcpy(pathname, my_program_name, sizeof(pathname));
469 slash = strrchr(pathname, '/');
474 strcpy(slash, "avconv");
476 http_log("Launch command line: ");
477 http_log("%s ", pathname);
478 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
479 http_log("%s ", feed->child_argv[i]);
482 for (i = 3; i < 256; i++)
485 if (!avserver_debug) {
486 i = open("/dev/null", O_RDWR);
495 /* This is needed to make relative pathnames work */
496 chdir(my_program_dir);
498 signal(SIGPIPE, SIG_DFL);
500 execvp(pathname, feed->child_argv);
508 /* open a listening socket */
509 static int socket_open_listen(struct sockaddr_in *my_addr)
513 server_fd = socket(AF_INET,SOCK_STREAM,0);
520 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
522 my_addr->sin_family = AF_INET;
523 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
525 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
527 closesocket(server_fd);
531 if (listen (server_fd, 5) < 0) {
533 closesocket(server_fd);
536 ff_socket_nonblock(server_fd, 1);
541 /* start all multicast streams */
542 static void start_multicast(void)
547 struct sockaddr_in dest_addr;
548 int default_port, stream_index;
551 for(stream = first_stream; stream != NULL; stream = stream->next) {
552 if (stream->is_multicast) {
553 /* open the RTP connection */
554 snprintf(session_id, sizeof(session_id), "%08x%08x",
555 av_lfg_get(&random_state), av_lfg_get(&random_state));
557 /* choose a port if none given */
558 if (stream->multicast_port == 0) {
559 stream->multicast_port = default_port;
563 dest_addr.sin_family = AF_INET;
564 dest_addr.sin_addr = stream->multicast_ip;
565 dest_addr.sin_port = htons(stream->multicast_port);
567 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
568 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
572 if (open_input_stream(rtp_c, "") < 0) {
573 http_log("Could not open input stream for stream '%s'\n",
578 /* open each RTP stream */
579 for(stream_index = 0; stream_index < stream->nb_streams;
581 dest_addr.sin_port = htons(stream->multicast_port +
583 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
584 http_log("Could not open output stream '%s/streamid=%d'\n",
585 stream->filename, stream_index);
590 /* change state to send data */
591 rtp_c->state = HTTPSTATE_SEND_DATA;
596 /* main loop of the http server */
597 static int http_server(void)
599 int server_fd = 0, rtsp_server_fd = 0;
600 int ret, delay, delay1;
601 struct pollfd *poll_table, *poll_entry;
602 HTTPContext *c, *c_next;
604 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
605 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
609 if (my_http_addr.sin_port) {
610 server_fd = socket_open_listen(&my_http_addr);
615 if (my_rtsp_addr.sin_port) {
616 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
617 if (rtsp_server_fd < 0)
621 if (!rtsp_server_fd && !server_fd) {
622 http_log("HTTP and RTSP disabled.\n");
626 http_log("AVserver started.\n");
628 start_children(first_feed);
633 poll_entry = poll_table;
635 poll_entry->fd = server_fd;
636 poll_entry->events = POLLIN;
639 if (rtsp_server_fd) {
640 poll_entry->fd = rtsp_server_fd;
641 poll_entry->events = POLLIN;
645 /* wait for events on each HTTP handle */
652 case HTTPSTATE_SEND_HEADER:
653 case RTSPSTATE_SEND_REPLY:
654 case RTSPSTATE_SEND_PACKET:
655 c->poll_entry = poll_entry;
657 poll_entry->events = POLLOUT;
660 case HTTPSTATE_SEND_DATA_HEADER:
661 case HTTPSTATE_SEND_DATA:
662 case HTTPSTATE_SEND_DATA_TRAILER:
663 if (!c->is_packetized) {
664 /* for TCP, we output as much as we can (may need to put a limit) */
665 c->poll_entry = poll_entry;
667 poll_entry->events = POLLOUT;
670 /* when avserver is doing the timing, we work by
671 looking at which packet need to be sent every
673 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
678 case HTTPSTATE_WAIT_REQUEST:
679 case HTTPSTATE_RECEIVE_DATA:
680 case HTTPSTATE_WAIT_FEED:
681 case RTSPSTATE_WAIT_REQUEST:
682 /* need to catch errors */
683 c->poll_entry = poll_entry;
685 poll_entry->events = POLLIN;/* Maybe this will work */
689 c->poll_entry = NULL;
695 /* wait for an event on one connection. We poll at least every
696 second to handle timeouts */
698 ret = poll(poll_table, poll_entry - poll_table, delay);
699 if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
700 ff_neterrno() != AVERROR(EINTR))
704 cur_time = av_gettime() / 1000;
706 if (need_to_start_children) {
707 need_to_start_children = 0;
708 start_children(first_feed);
711 /* now handle the events */
712 for(c = first_http_ctx; c != NULL; c = c_next) {
714 if (handle_connection(c) < 0) {
715 /* close and free the connection */
721 poll_entry = poll_table;
723 /* new HTTP connection request ? */
724 if (poll_entry->revents & POLLIN)
725 new_connection(server_fd, 0);
728 if (rtsp_server_fd) {
729 /* new RTSP connection request ? */
730 if (poll_entry->revents & POLLIN)
731 new_connection(rtsp_server_fd, 1);
736 /* start waiting for a new HTTP/RTSP request */
737 static void start_wait_request(HTTPContext *c, int is_rtsp)
739 c->buffer_ptr = c->buffer;
740 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
743 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
744 c->state = RTSPSTATE_WAIT_REQUEST;
746 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
747 c->state = HTTPSTATE_WAIT_REQUEST;
751 static void http_send_too_busy_reply(int fd)
754 int len = snprintf(buffer, sizeof(buffer),
755 "HTTP/1.0 503 Server too busy\r\n"
756 "Content-type: text/html\r\n"
758 "<html><head><title>Too busy</title></head><body>\r\n"
759 "<p>The server is too busy to serve your request at this time.</p>\r\n"
760 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
761 "</body></html>\r\n",
762 nb_connections, nb_max_connections);
763 send(fd, buffer, len, 0);
767 static void new_connection(int server_fd, int is_rtsp)
769 struct sockaddr_in from_addr;
771 HTTPContext *c = NULL;
773 len = sizeof(from_addr);
774 fd = accept(server_fd, (struct sockaddr *)&from_addr,
777 http_log("error during accept %s\n", strerror(errno));
780 ff_socket_nonblock(fd, 1);
782 if (nb_connections >= nb_max_connections) {
783 http_send_too_busy_reply(fd);
787 /* add a new connection */
788 c = av_mallocz(sizeof(HTTPContext));
793 c->poll_entry = NULL;
794 c->from_addr = from_addr;
795 c->buffer_size = IOBUFFER_INIT_SIZE;
796 c->buffer = av_malloc(c->buffer_size);
800 c->next = first_http_ctx;
804 start_wait_request(c, is_rtsp);
816 static void close_connection(HTTPContext *c)
818 HTTPContext **cp, *c1;
820 AVFormatContext *ctx;
824 /* remove connection from list */
825 cp = &first_http_ctx;
826 while ((*cp) != NULL) {
834 /* remove references, if any (XXX: do it faster) */
835 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
840 /* remove connection associated resources */
844 /* close each frame parser */
845 for(i=0;i<c->fmt_in->nb_streams;i++) {
846 st = c->fmt_in->streams[i];
847 if (st->codec->codec)
848 avcodec_close(st->codec);
850 avformat_close_input(&c->fmt_in);
853 /* free RTP output streams if any */
856 nb_streams = c->stream->nb_streams;
858 for(i=0;i<nb_streams;i++) {
861 av_write_trailer(ctx);
862 av_dict_free(&ctx->metadata);
863 av_free(ctx->streams[0]);
866 h = c->rtp_handles[i];
873 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
876 if (avio_open_dyn_buf(&ctx->pb) >= 0) {
877 av_write_trailer(ctx);
878 av_freep(&c->pb_buffer);
879 avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
884 for(i=0; i<ctx->nb_streams; i++)
885 av_free(ctx->streams[i]);
887 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
888 current_bandwidth -= c->stream->bandwidth;
890 /* signal that there is no feed if we are the feeder socket */
891 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
892 c->stream->feed_opened = 0;
896 av_freep(&c->pb_buffer);
897 av_freep(&c->packet_buffer);
903 static int handle_connection(HTTPContext *c)
908 case HTTPSTATE_WAIT_REQUEST:
909 case RTSPSTATE_WAIT_REQUEST:
911 if ((c->timeout - cur_time) < 0)
913 if (c->poll_entry->revents & (POLLERR | POLLHUP))
916 /* no need to read if no events */
917 if (!(c->poll_entry->revents & POLLIN))
921 len = recv(c->fd, c->buffer_ptr, 1, 0);
923 if (ff_neterrno() != AVERROR(EAGAIN) &&
924 ff_neterrno() != AVERROR(EINTR))
926 } else if (len == 0) {
929 /* search for end of request. */
931 c->buffer_ptr += len;
933 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
934 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
935 /* request found : parse it and reply */
936 if (c->state == HTTPSTATE_WAIT_REQUEST) {
937 ret = http_parse_request(c);
939 ret = rtsp_parse_request(c);
943 } else if (ptr >= c->buffer_end) {
944 /* request too long: cannot do anything */
946 } else goto read_loop;
950 case HTTPSTATE_SEND_HEADER:
951 if (c->poll_entry->revents & (POLLERR | POLLHUP))
954 /* no need to write if no events */
955 if (!(c->poll_entry->revents & POLLOUT))
957 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
959 if (ff_neterrno() != AVERROR(EAGAIN) &&
960 ff_neterrno() != AVERROR(EINTR)) {
961 /* error : close connection */
962 av_freep(&c->pb_buffer);
966 c->buffer_ptr += len;
968 c->stream->bytes_served += len;
969 c->data_count += len;
970 if (c->buffer_ptr >= c->buffer_end) {
971 av_freep(&c->pb_buffer);
975 /* all the buffer was sent : synchronize to the incoming stream */
976 c->state = HTTPSTATE_SEND_DATA_HEADER;
977 c->buffer_ptr = c->buffer_end = c->buffer;
982 case HTTPSTATE_SEND_DATA:
983 case HTTPSTATE_SEND_DATA_HEADER:
984 case HTTPSTATE_SEND_DATA_TRAILER:
985 /* for packetized output, we consider we can always write (the
986 input streams sets the speed). It may be better to verify
987 that we do not rely too much on the kernel queues */
988 if (!c->is_packetized) {
989 if (c->poll_entry->revents & (POLLERR | POLLHUP))
992 /* no need to read if no events */
993 if (!(c->poll_entry->revents & POLLOUT))
996 if (http_send_data(c) < 0)
998 /* close connection if trailer sent */
999 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
1002 case HTTPSTATE_RECEIVE_DATA:
1003 /* no need to read if no events */
1004 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1006 if (!(c->poll_entry->revents & POLLIN))
1008 if (http_receive_data(c) < 0)
1011 case HTTPSTATE_WAIT_FEED:
1012 /* no need to read if no events */
1013 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1016 /* nothing to do, we'll be waken up by incoming feed packets */
1019 case RTSPSTATE_SEND_REPLY:
1020 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1021 av_freep(&c->pb_buffer);
1024 /* no need to write if no events */
1025 if (!(c->poll_entry->revents & POLLOUT))
1027 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1029 if (ff_neterrno() != AVERROR(EAGAIN) &&
1030 ff_neterrno() != AVERROR(EINTR)) {
1031 /* error : close connection */
1032 av_freep(&c->pb_buffer);
1036 c->buffer_ptr += len;
1037 c->data_count += len;
1038 if (c->buffer_ptr >= c->buffer_end) {
1039 /* all the buffer was sent : wait for a new request */
1040 av_freep(&c->pb_buffer);
1041 start_wait_request(c, 1);
1045 case RTSPSTATE_SEND_PACKET:
1046 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1047 av_freep(&c->packet_buffer);
1050 /* no need to write if no events */
1051 if (!(c->poll_entry->revents & POLLOUT))
1053 len = send(c->fd, c->packet_buffer_ptr,
1054 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1056 if (ff_neterrno() != AVERROR(EAGAIN) &&
1057 ff_neterrno() != AVERROR(EINTR)) {
1058 /* error : close connection */
1059 av_freep(&c->packet_buffer);
1063 c->packet_buffer_ptr += len;
1064 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1065 /* all the buffer was sent : wait for a new request */
1066 av_freep(&c->packet_buffer);
1067 c->state = RTSPSTATE_WAIT_REQUEST;
1071 case HTTPSTATE_READY:
1080 static int extract_rates(char *rates, int ratelen, const char *request)
1084 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1085 if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1086 const char *q = p + 7;
1088 while (*q && *q != '\n' && isspace(*q))
1091 if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1097 memset(rates, 0xff, ratelen);
1100 while (*q && *q != '\n' && *q != ':')
1103 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1107 if (stream_no < ratelen && stream_no >= 0)
1108 rates[stream_no] = rate_no;
1110 while (*q && *q != '\n' && !isspace(*q))
1117 p = strchr(p, '\n');
1127 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1130 int best_bitrate = 100000000;
1133 for (i = 0; i < feed->nb_streams; i++) {
1134 AVCodecContext *feed_codec = feed->streams[i]->codec;
1136 if (feed_codec->codec_id != codec->codec_id ||
1137 feed_codec->sample_rate != codec->sample_rate ||
1138 feed_codec->width != codec->width ||
1139 feed_codec->height != codec->height)
1142 /* Potential stream */
1144 /* We want the fastest stream less than bit_rate, or the slowest
1145 * faster than bit_rate
1148 if (feed_codec->bit_rate <= bit_rate) {
1149 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1150 best_bitrate = feed_codec->bit_rate;
1154 if (feed_codec->bit_rate < best_bitrate) {
1155 best_bitrate = feed_codec->bit_rate;
1164 static int modify_current_stream(HTTPContext *c, char *rates)
1167 FFStream *req = c->stream;
1168 int action_required = 0;
1170 /* Not much we can do for a feed */
1174 for (i = 0; i < req->nb_streams; i++) {
1175 AVCodecContext *codec = req->streams[i]->codec;
1179 c->switch_feed_streams[i] = req->feed_streams[i];
1182 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1185 /* Wants off or slow */
1186 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1188 /* This doesn't work well when it turns off the only stream! */
1189 c->switch_feed_streams[i] = -2;
1190 c->feed_streams[i] = -2;
1195 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1196 action_required = 1;
1199 return action_required;
1202 /* XXX: factorize in utils.c ? */
1203 /* XXX: take care with different space meaning */
1204 static void skip_spaces(const char **pp)
1208 while (*p == ' ' || *p == '\t')
1213 static void get_word(char *buf, int buf_size, const char **pp)
1221 while (!isspace(*p) && *p != '\0') {
1222 if ((q - buf) < buf_size - 1)
1231 static void get_arg(char *buf, int buf_size, const char **pp)
1238 while (isspace(*p)) p++;
1241 if (*p == '\"' || *p == '\'')
1253 if ((q - buf) < buf_size - 1)
1258 if (quote && *p == quote)
1263 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1264 const char *p, const char *filename, int line_num)
1270 get_arg(arg, sizeof(arg), &p);
1271 if (av_strcasecmp(arg, "allow") == 0)
1272 acl.action = IP_ALLOW;
1273 else if (av_strcasecmp(arg, "deny") == 0)
1274 acl.action = IP_DENY;
1276 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1277 filename, line_num, arg);
1281 get_arg(arg, sizeof(arg), &p);
1283 if (resolve_host(&acl.first, arg) != 0) {
1284 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1285 filename, line_num, arg);
1288 acl.last = acl.first;
1290 get_arg(arg, sizeof(arg), &p);
1293 if (resolve_host(&acl.last, arg) != 0) {
1294 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1295 filename, line_num, arg);
1301 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1302 IPAddressACL **naclp = 0;
1308 naclp = &stream->acl;
1314 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1315 filename, line_num);
1321 naclp = &(*naclp)->next;
1329 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1334 IPAddressACL *acl = NULL;
1338 f = fopen(stream->dynamic_acl, "r");
1340 perror(stream->dynamic_acl);
1344 acl = av_mallocz(sizeof(IPAddressACL));
1348 if (fgets(line, sizeof(line), f) == NULL)
1354 if (*p == '\0' || *p == '#')
1356 get_arg(cmd, sizeof(cmd), &p);
1358 if (!av_strcasecmp(cmd, "ACL"))
1359 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1366 static void free_acl_list(IPAddressACL *in_acl)
1368 IPAddressACL *pacl,*pacl2;
1378 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1380 enum IPAddressAction last_action = IP_DENY;
1382 struct in_addr *src = &c->from_addr.sin_addr;
1383 unsigned long src_addr = src->s_addr;
1385 for (acl = in_acl; acl; acl = acl->next) {
1386 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1387 return (acl->action == IP_ALLOW) ? 1 : 0;
1388 last_action = acl->action;
1391 /* Nothing matched, so return not the last action */
1392 return (last_action == IP_DENY) ? 1 : 0;
1395 static int validate_acl(FFStream *stream, HTTPContext *c)
1401 /* if stream->acl is null validate_acl_list will return 1 */
1402 ret = validate_acl_list(stream->acl, c);
1404 if (stream->dynamic_acl[0]) {
1405 acl = parse_dynamic_acl(stream, c);
1407 ret = validate_acl_list(acl, c);
1415 /* compute the real filename of a file by matching it without its
1416 extensions to all the stream filenames */
1417 static void compute_real_filename(char *filename, int max_size)
1424 /* compute filename by matching without the file extensions */
1425 av_strlcpy(file1, filename, sizeof(file1));
1426 p = strrchr(file1, '.');
1429 for(stream = first_stream; stream != NULL; stream = stream->next) {
1430 av_strlcpy(file2, stream->filename, sizeof(file2));
1431 p = strrchr(file2, '.');
1434 if (!strcmp(file1, file2)) {
1435 av_strlcpy(filename, stream->filename, max_size);
1450 /* parse http request and prepare header */
1451 static int http_parse_request(HTTPContext *c)
1454 enum RedirType redir_type;
1456 char info[1024], filename[1024];
1460 const char *mime_type;
1464 char *useragent = 0;
1467 get_word(cmd, sizeof(cmd), (const char **)&p);
1468 av_strlcpy(c->method, cmd, sizeof(c->method));
1470 if (!strcmp(cmd, "GET"))
1472 else if (!strcmp(cmd, "POST"))
1477 get_word(url, sizeof(url), (const char **)&p);
1478 av_strlcpy(c->url, url, sizeof(c->url));
1480 get_word(protocol, sizeof(protocol), (const char **)&p);
1481 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1484 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1487 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1489 /* find the filename and the optional info string in the request */
1490 p = strchr(url, '?');
1492 av_strlcpy(info, p, sizeof(info));
1497 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1499 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1500 if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1502 if (*useragent && *useragent != '\n' && isspace(*useragent))
1506 p = strchr(p, '\n');
1513 redir_type = REDIR_NONE;
1514 if (av_match_ext(filename, "asx")) {
1515 redir_type = REDIR_ASX;
1516 filename[strlen(filename)-1] = 'f';
1517 } else if (av_match_ext(filename, "asf") &&
1518 (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1519 /* if this isn't WMP or lookalike, return the redirector file */
1520 redir_type = REDIR_ASF;
1521 } else if (av_match_ext(filename, "rpm,ram")) {
1522 redir_type = REDIR_RAM;
1523 strcpy(filename + strlen(filename)-2, "m");
1524 } else if (av_match_ext(filename, "rtsp")) {
1525 redir_type = REDIR_RTSP;
1526 compute_real_filename(filename, sizeof(filename) - 1);
1527 } else if (av_match_ext(filename, "sdp")) {
1528 redir_type = REDIR_SDP;
1529 compute_real_filename(filename, sizeof(filename) - 1);
1532 // "redirect" / request to index.html
1533 if (!strlen(filename))
1534 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1536 stream = first_stream;
1537 while (stream != NULL) {
1538 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1540 stream = stream->next;
1542 if (stream == NULL) {
1543 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1544 http_log("File '%s' not found\n", url);
1549 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1550 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1552 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1553 c->http_error = 301;
1555 q += snprintf(q, c->buffer_size,
1556 "HTTP/1.0 301 Moved\r\n"
1558 "Content-type: text/html\r\n"
1560 "<html><head><title>Moved</title></head><body>\r\n"
1561 "You should be <a href=\"%s\">redirected</a>.\r\n"
1562 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1563 /* prepare output buffer */
1564 c->buffer_ptr = c->buffer;
1566 c->state = HTTPSTATE_SEND_HEADER;
1570 /* If this is WMP, get the rate information */
1571 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1572 if (modify_current_stream(c, ratebuf)) {
1573 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1574 if (c->switch_feed_streams[i] >= 0)
1575 c->switch_feed_streams[i] = -1;
1580 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1581 current_bandwidth += stream->bandwidth;
1583 /* If already streaming this feed, do not let start another feeder. */
1584 if (stream->feed_opened) {
1585 snprintf(msg, sizeof(msg), "This feed is already being received.");
1586 http_log("Feed '%s' already being received\n", stream->feed_filename);
1590 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1591 c->http_error = 503;
1593 q += snprintf(q, c->buffer_size,
1594 "HTTP/1.0 503 Server too busy\r\n"
1595 "Content-type: text/html\r\n"
1597 "<html><head><title>Too busy</title></head><body>\r\n"
1598 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1599 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1600 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1601 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1602 /* prepare output buffer */
1603 c->buffer_ptr = c->buffer;
1605 c->state = HTTPSTATE_SEND_HEADER;
1609 if (redir_type != REDIR_NONE) {
1612 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1613 if (av_strncasecmp(p, "Host:", 5) == 0) {
1617 p = strchr(p, '\n');
1628 while (isspace(*hostinfo))
1631 eoh = strchr(hostinfo, '\n');
1633 if (eoh[-1] == '\r')
1636 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1637 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1638 hostbuf[eoh - hostinfo] = 0;
1640 c->http_error = 200;
1642 switch(redir_type) {
1644 q += snprintf(q, c->buffer_size,
1645 "HTTP/1.0 200 ASX Follows\r\n"
1646 "Content-type: video/x-ms-asf\r\n"
1648 "<ASX Version=\"3\">\r\n"
1649 //"<!-- Autogenerated by avserver -->\r\n"
1650 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1651 "</ASX>\r\n", hostbuf, filename, info);
1654 q += snprintf(q, c->buffer_size,
1655 "HTTP/1.0 200 RAM Follows\r\n"
1656 "Content-type: audio/x-pn-realaudio\r\n"
1658 "# Autogenerated by avserver\r\n"
1659 "http://%s/%s%s\r\n", hostbuf, filename, info);
1662 q += snprintf(q, c->buffer_size,
1663 "HTTP/1.0 200 ASF Redirect follows\r\n"
1664 "Content-type: video/x-ms-asf\r\n"
1667 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1671 char hostname[256], *p;
1672 /* extract only hostname */
1673 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1674 p = strrchr(hostname, ':');
1677 q += snprintf(q, c->buffer_size,
1678 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1679 /* XXX: incorrect mime type ? */
1680 "Content-type: application/x-rtsp\r\n"
1682 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1688 int sdp_data_size, len;
1689 struct sockaddr_in my_addr;
1691 q += snprintf(q, c->buffer_size,
1692 "HTTP/1.0 200 OK\r\n"
1693 "Content-type: application/sdp\r\n"
1696 len = sizeof(my_addr);
1697 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1699 /* XXX: should use a dynamic buffer */
1700 sdp_data_size = prepare_sdp_description(stream,
1703 if (sdp_data_size > 0) {
1704 memcpy(q, sdp_data, sdp_data_size);
1716 /* prepare output buffer */
1717 c->buffer_ptr = c->buffer;
1719 c->state = HTTPSTATE_SEND_HEADER;
1725 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1729 stream->conns_served++;
1731 /* XXX: add there authenticate and IP match */
1734 /* if post, it means a feed is being sent */
1735 if (!stream->is_feed) {
1736 /* However it might be a status report from WMP! Let us log the
1737 * data as it might come in handy one day. */
1741 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1742 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1746 if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1747 client_id = strtol(p + 18, 0, 10);
1748 p = strchr(p, '\n');
1756 char *eol = strchr(logline, '\n');
1761 if (eol[-1] == '\r')
1763 http_log("%.*s\n", (int) (eol - logline), logline);
1764 c->suppress_log = 1;
1769 http_log("\nGot request:\n%s\n", c->buffer);
1772 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1775 /* Now we have to find the client_id */
1776 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1777 if (wmpc->wmp_client_id == client_id)
1781 if (wmpc && modify_current_stream(wmpc, ratebuf))
1782 wmpc->switch_pending = 1;
1785 snprintf(msg, sizeof(msg), "POST command not handled");
1789 if (http_start_receive_data(c) < 0) {
1790 snprintf(msg, sizeof(msg), "could not open feed");
1794 c->state = HTTPSTATE_RECEIVE_DATA;
1799 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1800 http_log("\nGot request:\n%s\n", c->buffer);
1803 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1806 /* open input stream */
1807 if (open_input_stream(c, info) < 0) {
1808 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1812 /* prepare http header */
1814 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1815 mime_type = c->stream->fmt->mime_type;
1817 mime_type = "application/x-octet-stream";
1818 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1820 /* for asf, we need extra headers */
1821 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1822 /* Need to allocate a client id */
1824 c->wmp_client_id = av_lfg_get(&random_state);
1826 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
1828 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1829 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1831 /* prepare output buffer */
1833 c->buffer_ptr = c->buffer;
1835 c->state = HTTPSTATE_SEND_HEADER;
1838 c->http_error = 404;
1840 q += snprintf(q, c->buffer_size,
1841 "HTTP/1.0 404 Not Found\r\n"
1842 "Content-type: text/html\r\n"
1845 "<head><title>404 Not Found</title></head>\n"
1848 /* prepare output buffer */
1849 c->buffer_ptr = c->buffer;
1851 c->state = HTTPSTATE_SEND_HEADER;
1855 c->http_error = 200; /* horrible : we use this value to avoid
1856 going to the send data state */
1857 c->state = HTTPSTATE_SEND_HEADER;
1861 static void fmt_bytecount(AVIOContext *pb, int64_t count)
1863 static const char suffix[] = " kMGTP";
1866 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1868 avio_printf(pb, "%"PRId64"%c", count, *s);
1871 static void compute_status(HTTPContext *c)
1880 if (avio_open_dyn_buf(&pb) < 0) {
1881 /* XXX: return an error ? */
1882 c->buffer_ptr = c->buffer;
1883 c->buffer_end = c->buffer;
1887 avio_printf(pb, "HTTP/1.0 200 OK\r\n");
1888 avio_printf(pb, "Content-type: %s\r\n", "text/html");
1889 avio_printf(pb, "Pragma: no-cache\r\n");
1890 avio_printf(pb, "\r\n");
1892 avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1893 if (c->stream->feed_filename[0])
1894 avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1895 avio_printf(pb, "</head>\n<body>");
1896 avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
1898 avio_printf(pb, "<h2>Available Streams</h2>\n");
1899 avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
1900 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");
1901 stream = first_stream;
1902 while (stream != NULL) {
1903 char sfilename[1024];
1906 if (stream->feed != stream) {
1907 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1908 eosf = sfilename + strlen(sfilename);
1909 if (eosf - sfilename >= 4) {
1910 if (strcmp(eosf - 4, ".asf") == 0)
1911 strcpy(eosf - 4, ".asx");
1912 else if (strcmp(eosf - 3, ".rm") == 0)
1913 strcpy(eosf - 3, ".ram");
1914 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1915 /* generate a sample RTSP director if
1916 unicast. Generate an SDP redirector if
1918 eosf = strrchr(sfilename, '.');
1920 eosf = sfilename + strlen(sfilename);
1921 if (stream->is_multicast)
1922 strcpy(eosf, ".sdp");
1924 strcpy(eosf, ".rtsp");
1928 avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1929 sfilename, stream->filename);
1930 avio_printf(pb, "<td align=right> %d <td align=right> ",
1931 stream->conns_served);
1932 fmt_bytecount(pb, stream->bytes_served);
1933 switch(stream->stream_type) {
1934 case STREAM_TYPE_LIVE: {
1935 int audio_bit_rate = 0;
1936 int video_bit_rate = 0;
1937 const char *audio_codec_name = "";
1938 const char *video_codec_name = "";
1939 const char *audio_codec_name_extra = "";
1940 const char *video_codec_name_extra = "";
1942 for(i=0;i<stream->nb_streams;i++) {
1943 AVStream *st = stream->streams[i];
1944 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1945 switch(st->codec->codec_type) {
1946 case AVMEDIA_TYPE_AUDIO:
1947 audio_bit_rate += st->codec->bit_rate;
1949 if (*audio_codec_name)
1950 audio_codec_name_extra = "...";
1951 audio_codec_name = codec->name;
1954 case AVMEDIA_TYPE_VIDEO:
1955 video_bit_rate += st->codec->bit_rate;
1957 if (*video_codec_name)
1958 video_codec_name_extra = "...";
1959 video_codec_name = codec->name;
1962 case AVMEDIA_TYPE_DATA:
1963 video_bit_rate += st->codec->bit_rate;
1969 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",
1972 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1973 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1975 avio_printf(pb, "<td>%s", stream->feed->filename);
1977 avio_printf(pb, "<td>%s", stream->feed_filename);
1978 avio_printf(pb, "\n");
1982 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1986 stream = stream->next;
1988 avio_printf(pb, "</table>\n");
1990 stream = first_stream;
1991 while (stream != NULL) {
1992 if (stream->feed == stream) {
1993 avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
1995 avio_printf(pb, "Running as pid %d.\n", stream->pid);
1997 #if defined(linux) && !defined(CONFIG_NOCUTILS)
2002 /* This is somewhat linux specific I guess */
2003 snprintf(ps_cmd, sizeof(ps_cmd),
2004 "ps -o \"%%cpu,cputime\" --no-headers %d",
2007 pid_stat = popen(ps_cmd, "r");
2012 if (fscanf(pid_stat, "%10s %64s", cpuperc,
2014 avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2022 avio_printf(pb, "<p>");
2024 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");
2026 for (i = 0; i < stream->nb_streams; i++) {
2027 AVStream *st = stream->streams[i];
2028 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2029 const char *type = "unknown";
2030 char parameters[64];
2034 switch(st->codec->codec_type) {
2035 case AVMEDIA_TYPE_AUDIO:
2037 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2039 case AVMEDIA_TYPE_VIDEO:
2041 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2042 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2047 avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2048 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2050 avio_printf(pb, "</table>\n");
2053 stream = stream->next;
2056 /* connection status */
2057 avio_printf(pb, "<h2>Connection Status</h2>\n");
2059 avio_printf(pb, "Number of connections: %d / %d<br>\n",
2060 nb_connections, nb_max_connections);
2062 avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2063 current_bandwidth, max_bandwidth);
2065 avio_printf(pb, "<table>\n");
2066 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");
2067 c1 = first_http_ctx;
2069 while (c1 != NULL) {
2075 for (j = 0; j < c1->stream->nb_streams; j++) {
2076 if (!c1->stream->feed)
2077 bitrate += c1->stream->streams[j]->codec->bit_rate;
2078 else if (c1->feed_streams[j] >= 0)
2079 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2084 p = inet_ntoa(c1->from_addr.sin_addr);
2085 avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2087 c1->stream ? c1->stream->filename : "",
2088 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2091 http_state[c1->state]);
2092 fmt_bytecount(pb, bitrate);
2093 avio_printf(pb, "<td align=right>");
2094 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2095 avio_printf(pb, "<td align=right>");
2096 fmt_bytecount(pb, c1->data_count);
2097 avio_printf(pb, "\n");
2100 avio_printf(pb, "</table>\n");
2105 avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
2106 avio_printf(pb, "</body>\n</html>\n");
2108 len = avio_close_dyn_buf(pb, &c->pb_buffer);
2109 c->buffer_ptr = c->pb_buffer;
2110 c->buffer_end = c->pb_buffer + len;
2113 static int open_input_stream(HTTPContext *c, const char *info)
2116 char input_filename[1024];
2117 AVFormatContext *s = NULL;
2121 /* find file name */
2122 if (c->stream->feed) {
2123 strcpy(input_filename, c->stream->feed->feed_filename);
2124 /* compute position (absolute time) */
2125 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2126 if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
2128 } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2129 int prebuffer = strtol(buf, 0, 10);
2130 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2132 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2134 strcpy(input_filename, c->stream->feed_filename);
2135 /* compute position (relative time) */
2136 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2137 if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
2142 if (input_filename[0] == '\0')
2146 if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2147 http_log("could not open %s: %d\n", input_filename, ret);
2150 s->flags |= AVFMT_FLAG_GENPTS;
2152 if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2153 http_log("Could not find stream info '%s'\n", input_filename);
2154 avformat_close_input(&s);
2158 /* choose stream as clock source (we favorize video stream if
2159 present) for packet sending */
2160 c->pts_stream_index = 0;
2161 for(i=0;i<c->stream->nb_streams;i++) {
2162 if (c->pts_stream_index == 0 &&
2163 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2164 c->pts_stream_index = i;
2168 if (c->fmt_in->iformat->read_seek)
2169 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2170 /* set the start time (needed for maxtime and RTP packet timing) */
2171 c->start_time = cur_time;
2172 c->first_pts = AV_NOPTS_VALUE;
2176 /* return the server clock (in us) */
2177 static int64_t get_server_clock(HTTPContext *c)
2179 /* compute current pts value from system time */
2180 return (cur_time - c->start_time) * 1000;
2183 /* return the estimated time at which the current packet must be sent
2185 static int64_t get_packet_send_clock(HTTPContext *c)
2187 int bytes_left, bytes_sent, frame_bytes;
2189 frame_bytes = c->cur_frame_bytes;
2190 if (frame_bytes <= 0)
2193 bytes_left = c->buffer_end - c->buffer_ptr;
2194 bytes_sent = frame_bytes - bytes_left;
2195 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2200 static int http_prepare_data(HTTPContext *c)
2203 AVFormatContext *ctx;
2205 av_freep(&c->pb_buffer);
2207 case HTTPSTATE_SEND_DATA_HEADER:
2208 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2209 av_dict_set(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2210 av_dict_set(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2211 av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2212 av_dict_set(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2214 c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2216 for(i=0;i<c->stream->nb_streams;i++) {
2218 c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2219 /* if file or feed, then just take streams from FFStream struct */
2220 if (!c->stream->feed ||
2221 c->stream->feed == c->stream)
2222 src = c->stream->streams[i];
2224 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2226 *(c->fmt_ctx.streams[i]) = *src;
2227 c->fmt_ctx.streams[i]->priv_data = 0;
2228 c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2229 AVStream, not in codec */
2231 /* set output format parameters */
2232 c->fmt_ctx.oformat = c->stream->fmt;
2233 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2235 c->got_key_frame = 0;
2237 /* prepare header and save header data in a stream */
2238 if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2239 /* XXX: potential leak */
2242 c->fmt_ctx.pb->seekable = 0;
2245 * HACK to avoid mpeg ps muxer to spit many underflow errors
2246 * Default value from Libav
2247 * Try to set it use configuration option
2249 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2251 if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
2252 http_log("Error writing output header\n");
2255 av_dict_free(&c->fmt_ctx.metadata);
2257 len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2258 c->buffer_ptr = c->pb_buffer;
2259 c->buffer_end = c->pb_buffer + len;
2261 c->state = HTTPSTATE_SEND_DATA;
2262 c->last_packet_sent = 0;
2264 case HTTPSTATE_SEND_DATA:
2265 /* find a new packet */
2266 /* read a packet from the input stream */
2267 if (c->stream->feed)
2268 ffm_set_write_index(c->fmt_in,
2269 c->stream->feed->feed_write_index,
2270 c->stream->feed->feed_size);
2272 if (c->stream->max_time &&
2273 c->stream->max_time + c->start_time - cur_time < 0)
2274 /* We have timed out */
2275 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2279 ret = av_read_frame(c->fmt_in, &pkt);
2281 if (c->stream->feed) {
2282 /* if coming from feed, it means we reached the end of the
2283 ffm file, so must wait for more data */
2284 c->state = HTTPSTATE_WAIT_FEED;
2285 return 1; /* state changed */
2286 } else if (ret == AVERROR(EAGAIN)) {
2287 /* input not ready, come back later */
2290 if (c->stream->loop) {
2291 avformat_close_input(&c->fmt_in);
2292 if (open_input_stream(c, "") < 0)
2297 /* must send trailer now because eof or error */
2298 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2302 int source_index = pkt.stream_index;
2303 /* update first pts if needed */
2304 if (c->first_pts == AV_NOPTS_VALUE) {
2305 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2306 c->start_time = cur_time;
2308 /* send it to the appropriate stream */
2309 if (c->stream->feed) {
2310 /* if coming from a feed, select the right stream */
2311 if (c->switch_pending) {
2312 c->switch_pending = 0;
2313 for(i=0;i<c->stream->nb_streams;i++) {
2314 if (c->switch_feed_streams[i] == pkt.stream_index)
2315 if (pkt.flags & AV_PKT_FLAG_KEY)
2316 c->switch_feed_streams[i] = -1;
2317 if (c->switch_feed_streams[i] >= 0)
2318 c->switch_pending = 1;
2321 for(i=0;i<c->stream->nb_streams;i++) {
2322 if (c->stream->feed_streams[i] == pkt.stream_index) {
2323 AVStream *st = c->fmt_in->streams[source_index];
2324 pkt.stream_index = i;
2325 if (pkt.flags & AV_PKT_FLAG_KEY &&
2326 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2327 c->stream->nb_streams == 1))
2328 c->got_key_frame = 1;
2329 if (!c->stream->send_on_key || c->got_key_frame)
2334 AVCodecContext *codec;
2335 AVStream *ist, *ost;
2337 ist = c->fmt_in->streams[source_index];
2338 /* specific handling for RTP: we use several
2339 output stream (one for each RTP
2340 connection). XXX: need more abstract handling */
2341 if (c->is_packetized) {
2342 /* compute send time and duration */
2343 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2344 c->cur_pts -= c->first_pts;
2345 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2346 /* find RTP context */
2347 c->packet_stream_index = pkt.stream_index;
2348 ctx = c->rtp_ctx[c->packet_stream_index];
2350 av_free_packet(&pkt);
2353 codec = ctx->streams[0]->codec;
2354 /* only one stream per RTP connection */
2355 pkt.stream_index = 0;
2359 codec = ctx->streams[pkt.stream_index]->codec;
2362 if (c->is_packetized) {
2363 int max_packet_size;
2364 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2365 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2367 max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
2368 ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2370 ret = avio_open_dyn_buf(&ctx->pb);
2373 /* XXX: potential leak */
2376 ost = ctx->streams[pkt.stream_index];
2378 ctx->pb->seekable = 0;
2379 if (pkt.dts != AV_NOPTS_VALUE)
2380 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2381 if (pkt.pts != AV_NOPTS_VALUE)
2382 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2383 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2384 if (av_write_frame(ctx, &pkt) < 0) {
2385 http_log("Error writing frame to output\n");
2386 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2389 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2390 c->cur_frame_bytes = len;
2391 c->buffer_ptr = c->pb_buffer;
2392 c->buffer_end = c->pb_buffer + len;
2394 codec->frame_number++;
2396 av_free_packet(&pkt);
2400 av_free_packet(&pkt);
2405 case HTTPSTATE_SEND_DATA_TRAILER:
2406 /* last packet test ? */
2407 if (c->last_packet_sent || c->is_packetized)
2410 /* prepare header */
2411 if (avio_open_dyn_buf(&ctx->pb) < 0) {
2412 /* XXX: potential leak */
2415 c->fmt_ctx.pb->seekable = 0;
2416 av_write_trailer(ctx);
2417 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2418 c->buffer_ptr = c->pb_buffer;
2419 c->buffer_end = c->pb_buffer + len;
2421 c->last_packet_sent = 1;
2427 /* should convert the format at the same time */
2428 /* send data starting at c->buffer_ptr to the output connection
2429 (either UDP or TCP connection) */
2430 static int http_send_data(HTTPContext *c)
2435 if (c->buffer_ptr >= c->buffer_end) {
2436 ret = http_prepare_data(c);
2440 /* state change requested */
2443 if (c->is_packetized) {
2444 /* RTP data output */
2445 len = c->buffer_end - c->buffer_ptr;
2447 /* fail safe - should never happen */
2449 c->buffer_ptr = c->buffer_end;
2452 len = (c->buffer_ptr[0] << 24) |
2453 (c->buffer_ptr[1] << 16) |
2454 (c->buffer_ptr[2] << 8) |
2456 if (len > (c->buffer_end - c->buffer_ptr))
2458 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2459 /* nothing to send yet: we can wait */
2463 c->data_count += len;
2464 update_datarate(&c->datarate, c->data_count);
2466 c->stream->bytes_served += len;
2468 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2469 /* RTP packets are sent inside the RTSP TCP connection */
2471 int interleaved_index, size;
2473 HTTPContext *rtsp_c;
2476 /* if no RTSP connection left, error */
2479 /* if already sending something, then wait. */
2480 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2482 if (avio_open_dyn_buf(&pb) < 0)
2484 interleaved_index = c->packet_stream_index * 2;
2485 /* RTCP packets are sent at odd indexes */
2486 if (c->buffer_ptr[1] == 200)
2487 interleaved_index++;
2488 /* write RTSP TCP header */
2490 header[1] = interleaved_index;
2491 header[2] = len >> 8;
2493 avio_write(pb, header, 4);
2494 /* write RTP packet data */
2496 avio_write(pb, c->buffer_ptr, len);
2497 size = avio_close_dyn_buf(pb, &c->packet_buffer);
2498 /* prepare asynchronous TCP sending */
2499 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2500 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2501 c->buffer_ptr += len;
2503 /* send everything we can NOW */
2504 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2505 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2507 rtsp_c->packet_buffer_ptr += len;
2508 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2509 /* if we could not send all the data, we will
2510 send it later, so a new state is needed to
2511 "lock" the RTSP TCP connection */
2512 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2515 /* all data has been sent */
2516 av_freep(&c->packet_buffer);
2518 /* send RTP packet directly in UDP */
2520 ffurl_write(c->rtp_handles[c->packet_stream_index],
2521 c->buffer_ptr, len);
2522 c->buffer_ptr += len;
2523 /* here we continue as we can send several packets per 10 ms slot */
2526 /* TCP data output */
2527 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2529 if (ff_neterrno() != AVERROR(EAGAIN) &&
2530 ff_neterrno() != AVERROR(EINTR))
2531 /* error : close connection */
2536 c->buffer_ptr += len;
2538 c->data_count += len;
2539 update_datarate(&c->datarate, c->data_count);
2541 c->stream->bytes_served += len;
2549 static int http_start_receive_data(HTTPContext *c)
2553 if (c->stream->feed_opened)
2556 /* Don't permit writing to this one */
2557 if (c->stream->readonly)
2561 fd = open(c->stream->feed_filename, O_RDWR);
2563 http_log("Error opening feeder file: %s\n", strerror(errno));
2568 if (c->stream->truncate) {
2569 /* truncate feed file */
2570 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2571 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2572 if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
2573 http_log("Error truncating feed file: %s\n", strerror(errno));
2577 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2578 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2583 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2584 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2585 lseek(fd, 0, SEEK_SET);
2587 /* init buffer input */
2588 c->buffer_ptr = c->buffer;
2589 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2590 c->stream->feed_opened = 1;
2591 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2595 static int http_receive_data(HTTPContext *c)
2598 int len, loop_run = 0;
2600 while (c->chunked_encoding && !c->chunk_size &&
2601 c->buffer_end > c->buffer_ptr) {
2602 /* read chunk header, if present */
2603 len = recv(c->fd, c->buffer_ptr, 1, 0);
2606 if (ff_neterrno() != AVERROR(EAGAIN) &&
2607 ff_neterrno() != AVERROR(EINTR))
2608 /* error : close connection */
2611 } else if (len == 0) {
2612 /* end of connection : close it */
2614 } else if (c->buffer_ptr - c->buffer >= 2 &&
2615 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2616 c->chunk_size = strtol(c->buffer, 0, 16);
2617 if (c->chunk_size == 0) // end of stream
2619 c->buffer_ptr = c->buffer;
2621 } else if (++loop_run > 10) {
2622 /* no chunk header, abort */
2629 if (c->buffer_end > c->buffer_ptr) {
2630 len = recv(c->fd, c->buffer_ptr,
2631 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2633 if (ff_neterrno() != AVERROR(EAGAIN) &&
2634 ff_neterrno() != AVERROR(EINTR))
2635 /* error : close connection */
2637 } else if (len == 0)
2638 /* end of connection : close it */
2641 c->chunk_size -= len;
2642 c->buffer_ptr += len;
2643 c->data_count += len;
2644 update_datarate(&c->datarate, c->data_count);
2648 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2649 if (c->buffer[0] != 'f' ||
2650 c->buffer[1] != 'm') {
2651 http_log("Feed stream has become desynchronized -- disconnecting\n");
2656 if (c->buffer_ptr >= c->buffer_end) {
2657 FFStream *feed = c->stream;
2658 /* a packet has been received : write it in the store, except
2660 if (c->data_count > FFM_PACKET_SIZE) {
2661 /* XXX: use llseek or url_seek */
2662 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2663 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2664 http_log("Error writing to feed file: %s\n", strerror(errno));
2668 feed->feed_write_index += FFM_PACKET_SIZE;
2669 /* update file size */
2670 if (feed->feed_write_index > c->stream->feed_size)
2671 feed->feed_size = feed->feed_write_index;
2673 /* handle wrap around if max file size reached */
2674 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2675 feed->feed_write_index = FFM_PACKET_SIZE;
2678 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2679 http_log("Error writing index to feed file: %s\n", strerror(errno));
2683 /* wake up any waiting connections */
2684 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2685 if (c1->state == HTTPSTATE_WAIT_FEED &&
2686 c1->stream->feed == c->stream->feed)
2687 c1->state = HTTPSTATE_SEND_DATA;
2690 /* We have a header in our hands that contains useful data */
2691 AVFormatContext *s = avformat_alloc_context();
2693 AVInputFormat *fmt_in;
2699 /* use feed output format name to find corresponding input format */
2700 fmt_in = av_find_input_format(feed->fmt->name);
2704 pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2705 0, NULL, NULL, NULL, NULL);
2709 if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2714 /* Now we have the actual streams */
2715 if (s->nb_streams != feed->nb_streams) {
2716 avformat_close_input(&s);
2718 http_log("Feed '%s' stream number does not match registered feed\n",
2719 c->stream->feed_filename);
2723 for (i = 0; i < s->nb_streams; i++) {
2724 AVStream *fst = feed->streams[i];
2725 AVStream *st = s->streams[i];
2726 avcodec_copy_context(fst->codec, st->codec);
2729 avformat_close_input(&s);
2732 c->buffer_ptr = c->buffer;
2737 c->stream->feed_opened = 0;
2739 /* wake up any waiting connections to stop waiting for feed */
2740 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2741 if (c1->state == HTTPSTATE_WAIT_FEED &&
2742 c1->stream->feed == c->stream->feed)
2743 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2748 /********************************************************************/
2751 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2758 switch(error_number) {
2759 case RTSP_STATUS_OK:
2762 case RTSP_STATUS_METHOD:
2763 str = "Method Not Allowed";
2765 case RTSP_STATUS_BANDWIDTH:
2766 str = "Not Enough Bandwidth";
2768 case RTSP_STATUS_SESSION:
2769 str = "Session Not Found";
2771 case RTSP_STATUS_STATE:
2772 str = "Method Not Valid in This State";
2774 case RTSP_STATUS_AGGREGATE:
2775 str = "Aggregate operation not allowed";
2777 case RTSP_STATUS_ONLY_AGGREGATE:
2778 str = "Only aggregate operation allowed";
2780 case RTSP_STATUS_TRANSPORT:
2781 str = "Unsupported transport";
2783 case RTSP_STATUS_INTERNAL:
2784 str = "Internal Server Error";
2786 case RTSP_STATUS_SERVICE:
2787 str = "Service Unavailable";
2789 case RTSP_STATUS_VERSION:
2790 str = "RTSP Version not supported";
2793 str = "Unknown Error";
2797 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2798 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2800 /* output GMT time */
2803 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2804 avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2807 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2809 rtsp_reply_header(c, error_number);
2810 avio_printf(c->pb, "\r\n");
2813 static int rtsp_parse_request(HTTPContext *c)
2815 const char *p, *p1, *p2;
2821 RTSPMessageHeader header1 = { 0 }, *header = &header1;
2823 c->buffer_ptr[0] = '\0';
2826 get_word(cmd, sizeof(cmd), &p);
2827 get_word(url, sizeof(url), &p);
2828 get_word(protocol, sizeof(protocol), &p);
2830 av_strlcpy(c->method, cmd, sizeof(c->method));
2831 av_strlcpy(c->url, url, sizeof(c->url));
2832 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2834 if (avio_open_dyn_buf(&c->pb) < 0) {
2835 /* XXX: cannot do more */
2836 c->pb = NULL; /* safety */
2840 /* check version name */
2841 if (strcmp(protocol, "RTSP/1.0") != 0) {
2842 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2846 /* parse each header line */
2847 /* skip to next line */
2848 while (*p != '\n' && *p != '\0')
2852 while (*p != '\0') {
2853 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2857 if (p2 > p && p2[-1] == '\r')
2859 /* skip empty line */
2863 if (len > sizeof(line) - 1)
2864 len = sizeof(line) - 1;
2865 memcpy(line, p, len);
2867 ff_rtsp_parse_line(header, line, NULL, NULL);
2871 /* handle sequence number */
2872 c->seq = header->seq;
2874 if (!strcmp(cmd, "DESCRIBE"))
2875 rtsp_cmd_describe(c, url);
2876 else if (!strcmp(cmd, "OPTIONS"))
2877 rtsp_cmd_options(c, url);
2878 else if (!strcmp(cmd, "SETUP"))
2879 rtsp_cmd_setup(c, url, header);
2880 else if (!strcmp(cmd, "PLAY"))
2881 rtsp_cmd_play(c, url, header);
2882 else if (!strcmp(cmd, "PAUSE"))
2883 rtsp_cmd_pause(c, url, header);
2884 else if (!strcmp(cmd, "TEARDOWN"))
2885 rtsp_cmd_teardown(c, url, header);
2887 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2890 len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2891 c->pb = NULL; /* safety */
2893 /* XXX: cannot do more */
2896 c->buffer_ptr = c->pb_buffer;
2897 c->buffer_end = c->pb_buffer + len;
2898 c->state = RTSPSTATE_SEND_REPLY;
2902 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2903 struct in_addr my_ip)
2905 AVFormatContext *avc;
2906 AVStream *avs = NULL;
2909 avc = avformat_alloc_context();
2913 av_dict_set(&avc->metadata, "title",
2914 stream->title[0] ? stream->title : "No Title", 0);
2915 avc->nb_streams = stream->nb_streams;
2916 if (stream->is_multicast) {
2917 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2918 inet_ntoa(stream->multicast_ip),
2919 stream->multicast_port, stream->multicast_ttl);
2921 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2924 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2925 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2927 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2928 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2931 for(i = 0; i < stream->nb_streams; i++) {
2932 avc->streams[i] = &avs[i];
2933 avc->streams[i]->codec = stream->streams[i]->codec;
2935 *pbuffer = av_mallocz(2048);
2936 av_sdp_create(&avc, 1, *pbuffer, 2048);
2939 av_free(avc->streams);
2940 av_dict_free(&avc->metadata);
2944 return strlen(*pbuffer);
2947 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2949 // rtsp_reply_header(c, RTSP_STATUS_OK);
2950 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2951 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2952 avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2953 avio_printf(c->pb, "\r\n");
2956 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2962 int content_length, len;
2963 struct sockaddr_in my_addr;
2965 /* find which url is asked */
2966 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2971 for(stream = first_stream; stream != NULL; stream = stream->next) {
2972 if (!stream->is_feed &&
2973 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2974 !strcmp(path, stream->filename)) {
2978 /* no stream found */
2979 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2983 /* prepare the media description in sdp format */
2985 /* get the host IP */
2986 len = sizeof(my_addr);
2987 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2988 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2989 if (content_length < 0) {
2990 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2993 rtsp_reply_header(c, RTSP_STATUS_OK);
2994 avio_printf(c->pb, "Content-Base: %s/\r\n", url);
2995 avio_printf(c->pb, "Content-Type: application/sdp\r\n");
2996 avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
2997 avio_printf(c->pb, "\r\n");
2998 avio_write(c->pb, content, content_length);
3002 static HTTPContext *find_rtp_session(const char *session_id)
3006 if (session_id[0] == '\0')
3009 for(c = first_http_ctx; c != NULL; c = c->next) {
3010 if (!strcmp(c->session_id, session_id))
3016 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3018 RTSPTransportField *th;
3021 for(i=0;i<h->nb_transports;i++) {
3022 th = &h->transports[i];
3023 if (th->lower_transport == lower_transport)
3029 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3030 RTSPMessageHeader *h)
3033 int stream_index, rtp_port, rtcp_port;
3038 RTSPTransportField *th;
3039 struct sockaddr_in dest_addr;
3040 RTSPActionServerSetup setup;
3042 /* find which url is asked */
3043 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3048 /* now check each stream */
3049 for(stream = first_stream; stream != NULL; stream = stream->next) {
3050 if (!stream->is_feed &&
3051 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3052 /* accept aggregate filenames only if single stream */
3053 if (!strcmp(path, stream->filename)) {
3054 if (stream->nb_streams != 1) {
3055 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3062 for(stream_index = 0; stream_index < stream->nb_streams;
3064 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3065 stream->filename, stream_index);
3066 if (!strcmp(path, buf))
3071 /* no stream found */
3072 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3076 /* generate session id if needed */
3077 if (h->session_id[0] == '\0')
3078 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3079 av_lfg_get(&random_state), av_lfg_get(&random_state));
3081 /* find rtp session, and create it if none found */
3082 rtp_c = find_rtp_session(h->session_id);
3084 /* always prefer UDP */
3085 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3087 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3089 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3094 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3095 th->lower_transport);
3097 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3101 /* open input stream */
3102 if (open_input_stream(rtp_c, "") < 0) {
3103 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3108 /* test if stream is OK (test needed because several SETUP needs
3109 to be done for a given file) */
3110 if (rtp_c->stream != stream) {
3111 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3115 /* test if stream is already set up */
3116 if (rtp_c->rtp_ctx[stream_index]) {
3117 rtsp_reply_error(c, RTSP_STATUS_STATE);
3121 /* check transport */
3122 th = find_transport(h, rtp_c->rtp_protocol);
3123 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3124 th->client_port_min <= 0)) {
3125 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3129 /* setup default options */
3130 setup.transport_option[0] = '\0';
3131 dest_addr = rtp_c->from_addr;
3132 dest_addr.sin_port = htons(th->client_port_min);
3135 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3136 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3140 /* now everything is OK, so we can send the connection parameters */
3141 rtsp_reply_header(c, RTSP_STATUS_OK);
3143 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3145 switch(rtp_c->rtp_protocol) {
3146 case RTSP_LOWER_TRANSPORT_UDP:
3147 rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3148 rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3149 avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3150 "client_port=%d-%d;server_port=%d-%d",
3151 th->client_port_min, th->client_port_max,
3152 rtp_port, rtcp_port);
3154 case RTSP_LOWER_TRANSPORT_TCP:
3155 avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3156 stream_index * 2, stream_index * 2 + 1);
3161 if (setup.transport_option[0] != '\0')
3162 avio_printf(c->pb, ";%s", setup.transport_option);
3163 avio_printf(c->pb, "\r\n");
3166 avio_printf(c->pb, "\r\n");
3170 /* find an rtp connection by using the session ID. Check consistency
3172 static HTTPContext *find_rtp_session_with_url(const char *url,
3173 const char *session_id)
3181 rtp_c = find_rtp_session(session_id);
3185 /* find which url is asked */
3186 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3190 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3191 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3192 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3193 rtp_c->stream->filename, s);
3194 if(!strncmp(path, buf, sizeof(buf))) {
3195 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3200 if (len > 0 && path[len - 1] == '/' &&
3201 !strncmp(path, rtp_c->stream->filename, len - 1))
3206 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3210 rtp_c = find_rtp_session_with_url(url, h->session_id);
3212 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3216 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3217 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3218 rtp_c->state != HTTPSTATE_READY) {
3219 rtsp_reply_error(c, RTSP_STATUS_STATE);
3223 rtp_c->state = HTTPSTATE_SEND_DATA;
3225 /* now everything is OK, so we can send the connection parameters */
3226 rtsp_reply_header(c, RTSP_STATUS_OK);
3228 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3229 avio_printf(c->pb, "\r\n");
3232 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3236 rtp_c = find_rtp_session_with_url(url, h->session_id);
3238 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3242 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3243 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3244 rtsp_reply_error(c, RTSP_STATUS_STATE);
3248 rtp_c->state = HTTPSTATE_READY;
3249 rtp_c->first_pts = AV_NOPTS_VALUE;
3250 /* now everything is OK, so we can send the connection parameters */
3251 rtsp_reply_header(c, RTSP_STATUS_OK);
3253 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3254 avio_printf(c->pb, "\r\n");
3257 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3261 rtp_c = find_rtp_session_with_url(url, h->session_id);
3263 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3267 /* now everything is OK, so we can send the connection parameters */
3268 rtsp_reply_header(c, RTSP_STATUS_OK);
3270 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3271 avio_printf(c->pb, "\r\n");
3273 /* abort the session */
3274 close_connection(rtp_c);
3278 /********************************************************************/
3281 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3282 FFStream *stream, const char *session_id,
3283 enum RTSPLowerTransport rtp_protocol)
3285 HTTPContext *c = NULL;
3286 const char *proto_str;
3288 /* XXX: should output a warning page when coming
3289 close to the connection limit */
3290 if (nb_connections >= nb_max_connections)
3293 /* add a new connection */
3294 c = av_mallocz(sizeof(HTTPContext));
3299 c->poll_entry = NULL;
3300 c->from_addr = *from_addr;
3301 c->buffer_size = IOBUFFER_INIT_SIZE;
3302 c->buffer = av_malloc(c->buffer_size);
3307 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3308 c->state = HTTPSTATE_READY;
3309 c->is_packetized = 1;
3310 c->rtp_protocol = rtp_protocol;
3312 /* protocol is shown in statistics */
3313 switch(c->rtp_protocol) {
3314 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3315 proto_str = "MCAST";
3317 case RTSP_LOWER_TRANSPORT_UDP:
3320 case RTSP_LOWER_TRANSPORT_TCP:
3327 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3328 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3330 current_bandwidth += stream->bandwidth;
3332 c->next = first_http_ctx;
3344 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3345 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3347 static int rtp_new_av_stream(HTTPContext *c,
3348 int stream_index, struct sockaddr_in *dest_addr,
3349 HTTPContext *rtsp_c)
3351 AVFormatContext *ctx;
3354 URLContext *h = NULL;
3356 int max_packet_size;
3358 /* now we can open the relevant output stream */
3359 ctx = avformat_alloc_context();
3362 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3364 st = av_mallocz(sizeof(AVStream));
3367 ctx->nb_streams = 1;
3368 ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3371 ctx->streams[0] = st;
3373 if (!c->stream->feed ||
3374 c->stream->feed == c->stream)
3375 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3378 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3380 st->priv_data = NULL;
3382 /* build destination RTP address */
3383 ipaddr = inet_ntoa(dest_addr->sin_addr);
3385 switch(c->rtp_protocol) {
3386 case RTSP_LOWER_TRANSPORT_UDP:
3387 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3390 /* XXX: also pass as parameter to function ? */
3391 if (c->stream->is_multicast) {
3393 ttl = c->stream->multicast_ttl;
3396 snprintf(ctx->filename, sizeof(ctx->filename),
3397 "rtp://%s:%d?multicast=1&ttl=%d",
3398 ipaddr, ntohs(dest_addr->sin_port), ttl);
3400 snprintf(ctx->filename, sizeof(ctx->filename),
3401 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3404 if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3406 c->rtp_handles[stream_index] = h;
3407 max_packet_size = h->max_packet_size;
3409 case RTSP_LOWER_TRANSPORT_TCP:
3412 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3418 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3419 ipaddr, ntohs(dest_addr->sin_port),
3420 c->stream->filename, stream_index, c->protocol);
3422 /* normally, no packets should be output here, but the packet size may be checked */
3423 if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3424 /* XXX: close stream */
3427 if (avformat_write_header(ctx, NULL) < 0) {
3434 avio_close_dyn_buf(ctx->pb, &dummy_buf);
3437 c->rtp_ctx[stream_index] = ctx;
3441 /********************************************************************/
3442 /* avserver initialization */
3444 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3448 fst = av_mallocz(sizeof(AVStream));
3452 fst->codec = avcodec_alloc_context3(NULL);
3453 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3454 if (codec->extradata_size) {
3455 fst->codec->extradata = av_malloc(codec->extradata_size);
3456 memcpy(fst->codec->extradata, codec->extradata,
3457 codec->extradata_size);
3460 /* live streams must use the actual feed's codec since it may be
3461 * updated later to carry extradata needed by the streams.
3465 fst->priv_data = av_mallocz(sizeof(FeedData));
3466 fst->index = stream->nb_streams;
3467 avpriv_set_pts_info(fst, 33, 1, 90000);
3468 fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3469 stream->streams[stream->nb_streams++] = fst;
3473 /* return the stream number in the feed */
3474 static int add_av_stream(FFStream *feed, AVStream *st)
3477 AVCodecContext *av, *av1;
3481 for(i=0;i<feed->nb_streams;i++) {
3482 st = feed->streams[i];
3484 if (av1->codec_id == av->codec_id &&
3485 av1->codec_type == av->codec_type &&
3486 av1->bit_rate == av->bit_rate) {
3488 switch(av->codec_type) {
3489 case AVMEDIA_TYPE_AUDIO:
3490 if (av1->channels == av->channels &&
3491 av1->sample_rate == av->sample_rate)
3494 case AVMEDIA_TYPE_VIDEO:
3495 if (av1->width == av->width &&
3496 av1->height == av->height &&
3497 av1->time_base.den == av->time_base.den &&
3498 av1->time_base.num == av->time_base.num &&
3499 av1->gop_size == av->gop_size)
3508 fst = add_av_stream1(feed, av, 0);
3511 return feed->nb_streams - 1;
3514 static void remove_stream(FFStream *stream)
3518 while (*ps != NULL) {
3526 /* specific mpeg4 handling : we extract the raw parameters */
3527 static void extract_mpeg4_header(AVFormatContext *infile)
3529 int mpeg4_count, i, size;
3535 for(i=0;i<infile->nb_streams;i++) {
3536 st = infile->streams[i];
3537 if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3538 st->codec->extradata_size == 0) {
3545 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3546 while (mpeg4_count > 0) {
3547 if (av_read_packet(infile, &pkt) < 0)
3549 st = infile->streams[pkt.stream_index];
3550 if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3551 st->codec->extradata_size == 0) {
3552 av_freep(&st->codec->extradata);
3553 /* fill extradata with the header */
3554 /* XXX: we make hard suppositions here ! */
3556 while (p < pkt.data + pkt.size - 4) {
3557 /* stop when vop header is found */
3558 if (p[0] == 0x00 && p[1] == 0x00 &&
3559 p[2] == 0x01 && p[3] == 0xb6) {
3560 size = p - pkt.data;
3561 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3562 st->codec->extradata = av_malloc(size);
3563 st->codec->extradata_size = size;
3564 memcpy(st->codec->extradata, pkt.data, size);
3571 av_free_packet(&pkt);
3575 /* compute the needed AVStream for each file */
3576 static void build_file_streams(void)
3578 FFStream *stream, *stream_next;
3581 /* gather all streams */
3582 for(stream = first_stream; stream != NULL; stream = stream_next) {
3583 AVFormatContext *infile = NULL;
3584 stream_next = stream->next;
3585 if (stream->stream_type == STREAM_TYPE_LIVE &&
3587 /* the stream comes from a file */
3588 /* try to open the file */
3590 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3591 /* specific case : if transport stream output to RTP,
3592 we use a raw transport stream reader */
3593 av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3596 http_log("Opening file '%s'\n", stream->feed_filename);
3597 if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3598 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3599 /* remove stream (no need to spend more time on it) */
3601 remove_stream(stream);
3603 /* find all the AVStreams inside and reference them in
3605 if (avformat_find_stream_info(infile, NULL) < 0) {
3606 http_log("Could not find codec parameters from '%s'\n",
3607 stream->feed_filename);
3608 avformat_close_input(&infile);
3611 extract_mpeg4_header(infile);
3613 for(i=0;i<infile->nb_streams;i++)
3614 add_av_stream1(stream, infile->streams[i]->codec, 1);
3616 avformat_close_input(&infile);
3622 /* compute the needed AVStream for each feed */
3623 static void build_feed_streams(void)
3625 FFStream *stream, *feed;
3628 /* gather all streams */
3629 for(stream = first_stream; stream != NULL; stream = stream->next) {
3630 feed = stream->feed;
3632 if (stream->is_feed) {
3633 for(i=0;i<stream->nb_streams;i++)
3634 stream->feed_streams[i] = i;
3636 /* we handle a stream coming from a feed */
3637 for(i=0;i<stream->nb_streams;i++)
3638 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3643 /* create feed files if needed */
3644 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3647 if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3648 /* See if it matches */
3649 AVFormatContext *s = NULL;
3652 if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3653 /* Now see if it matches */
3654 if (s->nb_streams == feed->nb_streams) {
3656 for(i=0;i<s->nb_streams;i++) {
3658 sf = feed->streams[i];
3661 if (sf->index != ss->index ||
3663 http_log("Index & Id do not match for stream %d (%s)\n",
3664 i, feed->feed_filename);
3667 AVCodecContext *ccf, *ccs;
3671 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3673 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3674 http_log("Codecs do not match for stream %d\n", i);
3676 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3677 http_log("Codec bitrates do not match for stream %d\n", i);
3679 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3680 if (CHECK_CODEC(time_base.den) ||
3681 CHECK_CODEC(time_base.num) ||
3682 CHECK_CODEC(width) ||
3683 CHECK_CODEC(height)) {
3684 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3687 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3688 if (CHECK_CODEC(sample_rate) ||
3689 CHECK_CODEC(channels) ||
3690 CHECK_CODEC(frame_size)) {
3691 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3695 http_log("Unknown codec type\n");
3703 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3704 feed->feed_filename, s->nb_streams, feed->nb_streams);
3706 avformat_close_input(&s);
3708 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3709 feed->feed_filename);
3712 if (feed->readonly) {
3713 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3714 feed->feed_filename);
3717 unlink(feed->feed_filename);
3720 if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3721 AVFormatContext s1 = {0}, *s = &s1;
3723 if (feed->readonly) {
3724 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3725 feed->feed_filename);
3729 /* only write the header of the ffm file */
3730 if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3731 http_log("Could not open output feed file '%s'\n",
3732 feed->feed_filename);
3735 s->oformat = feed->fmt;
3736 s->nb_streams = feed->nb_streams;
3737 s->streams = feed->streams;
3738 if (avformat_write_header(s, NULL) < 0) {
3739 http_log("Container doesn't supports the required parameters\n");
3742 /* XXX: need better api */
3743 av_freep(&s->priv_data);
3746 /* get feed size and write index */
3747 fd = open(feed->feed_filename, O_RDONLY);
3749 http_log("Could not open output feed file '%s'\n",
3750 feed->feed_filename);
3754 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3755 feed->feed_size = lseek(fd, 0, SEEK_END);
3756 /* ensure that we do not wrap before the end of file */
3757 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3758 feed->feed_max_size = feed->feed_size;
3764 /* compute the bandwidth used by each stream */
3765 static void compute_bandwidth(void)
3771 for(stream = first_stream; stream != NULL; stream = stream->next) {
3773 for(i=0;i<stream->nb_streams;i++) {
3774 AVStream *st = stream->streams[i];
3775 switch(st->codec->codec_type) {
3776 case AVMEDIA_TYPE_AUDIO:
3777 case AVMEDIA_TYPE_VIDEO:
3778 bandwidth += st->codec->bit_rate;
3784 stream->bandwidth = (bandwidth + 999) / 1000;
3788 /* add a codec and set the default parameters */
3789 static void add_codec(FFStream *stream, AVCodecContext *av)
3793 /* compute default parameters */
3794 switch(av->codec_type) {
3795 case AVMEDIA_TYPE_AUDIO:
3796 if (av->bit_rate == 0)
3797 av->bit_rate = 64000;
3798 if (av->sample_rate == 0)
3799 av->sample_rate = 22050;
3800 if (av->channels == 0)
3803 case AVMEDIA_TYPE_VIDEO:
3804 if (av->bit_rate == 0)
3805 av->bit_rate = 64000;
3806 if (av->time_base.num == 0){
3807 av->time_base.den = 5;
3808 av->time_base.num = 1;
3810 if (av->width == 0 || av->height == 0) {
3814 /* Bitrate tolerance is less for streaming */
3815 if (av->bit_rate_tolerance == 0)
3816 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3817 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3822 if (av->max_qdiff == 0)
3824 av->qcompress = 0.5;
3827 if (!av->nsse_weight)
3828 av->nsse_weight = 8;
3830 av->frame_skip_cmp = FF_CMP_DCTMAX;
3832 av->me_method = ME_EPZS;
3833 av->rc_buffer_aggressivity = 1.0;
3836 av->rc_eq = "tex^qComp";
3837 if (!av->i_quant_factor)
3838 av->i_quant_factor = -0.8;
3839 if (!av->b_quant_factor)
3840 av->b_quant_factor = 1.25;
3841 if (!av->b_quant_offset)
3842 av->b_quant_offset = 1.25;
3843 if (!av->rc_max_rate)
3844 av->rc_max_rate = av->bit_rate * 2;
3846 if (av->rc_max_rate && !av->rc_buffer_size) {
3847 av->rc_buffer_size = av->rc_max_rate;
3856 st = av_mallocz(sizeof(AVStream));
3859 st->codec = avcodec_alloc_context3(NULL);
3860 stream->streams[stream->nb_streams++] = st;
3861 memcpy(st->codec, av, sizeof(AVCodecContext));
3864 static enum AVCodecID opt_audio_codec(const char *arg)
3866 AVCodec *p= avcodec_find_encoder_by_name(arg);
3868 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3869 return AV_CODEC_ID_NONE;
3874 static enum AVCodecID opt_video_codec(const char *arg)
3876 AVCodec *p= avcodec_find_encoder_by_name(arg);
3878 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3879 return AV_CODEC_ID_NONE;
3884 /* simplistic plugin support */
3887 static void load_module(const char *filename)
3890 void (*init_func)(void);
3891 dll = dlopen(filename, RTLD_NOW);
3893 fprintf(stderr, "Could not load module '%s' - %s\n",
3894 filename, dlerror());
3898 init_func = dlsym(dll, "avserver_module_init");
3901 "%s: init function 'avserver_module_init()' not found\n",
3910 static int avserver_opt_default(const char *opt, const char *arg,
3911 AVCodecContext *avctx, int type)
3914 const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3916 ret = av_opt_set(avctx, opt, arg, 0);
3920 static int avserver_opt_preset(const char *arg,
3921 AVCodecContext *avctx, int type,
3922 enum AVCodecID *audio_id, enum AVCodecID *video_id)
3925 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3927 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3929 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3930 codec ? codec->name : NULL))) {
3931 fprintf(stderr, "File for preset '%s' not found\n", arg);
3936 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3937 if(line[0] == '#' && !e)
3939 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3941 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3945 if(!strcmp(tmp, "acodec")){
3946 *audio_id = opt_audio_codec(tmp2);
3947 }else if(!strcmp(tmp, "vcodec")){
3948 *video_id = opt_video_codec(tmp2);
3949 }else if(!strcmp(tmp, "scodec")){
3950 /* opt_subtitle_codec(tmp2); */
3951 }else if(avserver_opt_default(tmp, tmp2, avctx, type) < 0){
3952 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
3963 static AVOutputFormat *avserver_guess_format(const char *short_name, const char *filename,
3964 const char *mime_type)
3966 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3969 AVOutputFormat *stream_fmt;
3970 char stream_format_name[64];
3972 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
3973 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
3982 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
3986 fprintf(stderr, "%s:%d: ", filename, line_num);
3987 vfprintf(stderr, fmt, vl);
3993 static int parse_ffconfig(const char *filename)
4000 int val, errors, line_num;
4001 FFStream **last_stream, *stream, *redirect;
4002 FFStream **last_feed, *feed, *s;
4003 AVCodecContext audio_enc, video_enc;
4004 enum AVCodecID audio_id, video_id;
4006 f = fopen(filename, "r");
4014 first_stream = NULL;
4015 last_stream = &first_stream;
4017 last_feed = &first_feed;
4021 audio_id = AV_CODEC_ID_NONE;
4022 video_id = AV_CODEC_ID_NONE;
4024 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4026 if (fgets(line, sizeof(line), f) == NULL)
4032 if (*p == '\0' || *p == '#')
4035 get_arg(cmd, sizeof(cmd), &p);
4037 if (!av_strcasecmp(cmd, "Port")) {
4038 get_arg(arg, sizeof(arg), &p);
4040 if (val < 1 || val > 65536) {
4041 ERROR("Invalid_port: %s\n", arg);
4043 my_http_addr.sin_port = htons(val);
4044 } else if (!av_strcasecmp(cmd, "BindAddress")) {
4045 get_arg(arg, sizeof(arg), &p);
4046 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4047 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4049 } else if (!av_strcasecmp(cmd, "NoDaemon")) {
4050 avserver_daemon = 0;
4051 } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4052 get_arg(arg, sizeof(arg), &p);
4054 if (val < 1 || val > 65536) {
4055 ERROR("%s:%d: Invalid port: %s\n", arg);
4057 my_rtsp_addr.sin_port = htons(atoi(arg));
4058 } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4059 get_arg(arg, sizeof(arg), &p);
4060 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4061 ERROR("Invalid host/IP address: %s\n", arg);
4063 } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4064 get_arg(arg, sizeof(arg), &p);
4066 if (val < 1 || val > 65536) {
4067 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4069 nb_max_http_connections = val;
4070 } else if (!av_strcasecmp(cmd, "MaxClients")) {
4071 get_arg(arg, sizeof(arg), &p);
4073 if (val < 1 || val > nb_max_http_connections) {
4074 ERROR("Invalid MaxClients: %s\n", arg);
4076 nb_max_connections = val;
4078 } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4080 get_arg(arg, sizeof(arg), &p);
4082 if (llval < 10 || llval > 10000000) {
4083 ERROR("Invalid MaxBandwidth: %s\n", arg);
4085 max_bandwidth = llval;
4086 } else if (!av_strcasecmp(cmd, "CustomLog")) {
4087 if (!avserver_debug)
4088 get_arg(logfilename, sizeof(logfilename), &p);
4089 } else if (!av_strcasecmp(cmd, "<Feed")) {
4090 /*********************************************/
4091 /* Feed related options */
4093 if (stream || feed) {
4094 ERROR("Already in a tag\n");
4096 feed = av_mallocz(sizeof(FFStream));
4097 get_arg(feed->filename, sizeof(feed->filename), &p);
4098 q = strrchr(feed->filename, '>');
4102 for (s = first_feed; s; s = s->next) {
4103 if (!strcmp(feed->filename, s->filename)) {
4104 ERROR("Feed '%s' already registered\n", s->filename);
4108 feed->fmt = av_guess_format("ffm", NULL, NULL);
4109 /* defaut feed file */
4110 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4111 "/tmp/%s.ffm", feed->filename);
4112 feed->feed_max_size = 5 * 1024 * 1024;
4114 feed->feed = feed; /* self feeding :-) */
4116 /* add in stream list */
4117 *last_stream = feed;
4118 last_stream = &feed->next;
4119 /* add in feed list */
4121 last_feed = &feed->next_feed;
4123 } else if (!av_strcasecmp(cmd, "Launch")) {
4127 feed->child_argv = av_mallocz(64 * sizeof(char *));
4129 for (i = 0; i < 62; i++) {
4130 get_arg(arg, sizeof(arg), &p);
4134 feed->child_argv[i] = av_strdup(arg);
4137 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4139 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4141 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4142 inet_ntoa(my_http_addr.sin_addr),
4143 ntohs(my_http_addr.sin_port), feed->filename);
4145 } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4147 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4149 } else if (stream) {
4150 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4152 } else if (!av_strcasecmp(cmd, "File")) {
4154 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4156 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4157 } else if (!av_strcasecmp(cmd, "Truncate")) {
4159 get_arg(arg, sizeof(arg), &p);
4160 feed->truncate = strtod(arg, NULL);
4162 } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4167 get_arg(arg, sizeof(arg), &p);
4169 fsize = strtod(p1, &p1);
4170 switch(toupper(*p1)) {
4175 fsize *= 1024 * 1024;
4178 fsize *= 1024 * 1024 * 1024;
4181 feed->feed_max_size = (int64_t)fsize;
4182 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4183 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4186 } else if (!av_strcasecmp(cmd, "</Feed>")) {
4188 ERROR("No corresponding <Feed> for </Feed>\n");
4191 } else if (!av_strcasecmp(cmd, "<Stream")) {
4192 /*********************************************/
4193 /* Stream related options */
4195 if (stream || feed) {
4196 ERROR("Already in a tag\n");
4199 stream = av_mallocz(sizeof(FFStream));
4200 get_arg(stream->filename, sizeof(stream->filename), &p);
4201 q = strrchr(stream->filename, '>');
4205 for (s = first_stream; s; s = s->next) {
4206 if (!strcmp(stream->filename, s->filename)) {
4207 ERROR("Stream '%s' already registered\n", s->filename);
4211 stream->fmt = avserver_guess_format(NULL, stream->filename, NULL);
4212 avcodec_get_context_defaults3(&video_enc, NULL);
4213 avcodec_get_context_defaults3(&audio_enc, NULL);
4214 audio_id = AV_CODEC_ID_NONE;
4215 video_id = AV_CODEC_ID_NONE;
4217 audio_id = stream->fmt->audio_codec;
4218 video_id = stream->fmt->video_codec;
4221 *last_stream = stream;
4222 last_stream = &stream->next;
4224 } else if (!av_strcasecmp(cmd, "Feed")) {
4225 get_arg(arg, sizeof(arg), &p);
4230 while (sfeed != NULL) {
4231 if (!strcmp(sfeed->filename, arg))
4233 sfeed = sfeed->next_feed;
4236 ERROR("feed '%s' not defined\n", arg);
4238 stream->feed = sfeed;
4240 } else if (!av_strcasecmp(cmd, "Format")) {
4241 get_arg(arg, sizeof(arg), &p);
4243 if (!strcmp(arg, "status")) {
4244 stream->stream_type = STREAM_TYPE_STATUS;
4247 stream->stream_type = STREAM_TYPE_LIVE;
4248 /* jpeg cannot be used here, so use single frame jpeg */
4249 if (!strcmp(arg, "jpeg"))
4250 strcpy(arg, "mjpeg");
4251 stream->fmt = avserver_guess_format(arg, NULL, NULL);
4253 ERROR("Unknown Format: %s\n", arg);
4257 audio_id = stream->fmt->audio_codec;
4258 video_id = stream->fmt->video_codec;
4261 } else if (!av_strcasecmp(cmd, "InputFormat")) {
4262 get_arg(arg, sizeof(arg), &p);
4264 stream->ifmt = av_find_input_format(arg);
4265 if (!stream->ifmt) {
4266 ERROR("Unknown input format: %s\n", arg);
4269 } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4270 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4271 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4273 ERROR("FaviconURL only permitted for status streams\n");
4275 } else if (!av_strcasecmp(cmd, "Author")) {
4277 get_arg(stream->author, sizeof(stream->author), &p);
4278 } else if (!av_strcasecmp(cmd, "Comment")) {
4280 get_arg(stream->comment, sizeof(stream->comment), &p);
4281 } else if (!av_strcasecmp(cmd, "Copyright")) {
4283 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4284 } else if (!av_strcasecmp(cmd, "Title")) {
4286 get_arg(stream->title, sizeof(stream->title), &p);
4287 } else if (!av_strcasecmp(cmd, "Preroll")) {
4288 get_arg(arg, sizeof(arg), &p);
4290 stream->prebuffer = atof(arg) * 1000;
4291 } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4293 stream->send_on_key = 1;
4294 } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4295 get_arg(arg, sizeof(arg), &p);
4296 audio_id = opt_audio_codec(arg);
4297 if (audio_id == AV_CODEC_ID_NONE) {
4298 ERROR("Unknown AudioCodec: %s\n", arg);
4300 } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4301 get_arg(arg, sizeof(arg), &p);
4302 video_id = opt_video_codec(arg);
4303 if (video_id == AV_CODEC_ID_NONE) {
4304 ERROR("Unknown VideoCodec: %s\n", arg);
4306 } else if (!av_strcasecmp(cmd, "MaxTime")) {
4307 get_arg(arg, sizeof(arg), &p);
4309 stream->max_time = atof(arg) * 1000;
4310 } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4311 get_arg(arg, sizeof(arg), &p);
4313 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4314 } else if (!av_strcasecmp(cmd, "AudioChannels")) {
4315 get_arg(arg, sizeof(arg), &p);
4317 audio_enc.channels = atoi(arg);
4318 } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4319 get_arg(arg, sizeof(arg), &p);
4321 audio_enc.sample_rate = atoi(arg);
4322 } else if (!av_strcasecmp(cmd, "AudioQuality")) {
4323 get_arg(arg, sizeof(arg), &p);
4325 // audio_enc.quality = atof(arg) * 1000;
4327 } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4329 int minrate, maxrate;
4331 get_arg(arg, sizeof(arg), &p);
4333 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4334 video_enc.rc_min_rate = minrate * 1000;
4335 video_enc.rc_max_rate = maxrate * 1000;
4337 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4340 } else if (!av_strcasecmp(cmd, "Debug")) {
4342 get_arg(arg, sizeof(arg), &p);
4343 video_enc.debug = strtol(arg,0,0);
4345 } else if (!av_strcasecmp(cmd, "Strict")) {
4347 get_arg(arg, sizeof(arg), &p);
4348 video_enc.strict_std_compliance = atoi(arg);
4350 } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4352 get_arg(arg, sizeof(arg), &p);
4353 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4355 } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4357 get_arg(arg, sizeof(arg), &p);
4358 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4360 } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4361 get_arg(arg, sizeof(arg), &p);
4363 video_enc.bit_rate = atoi(arg) * 1000;
4365 } else if (!av_strcasecmp(cmd, "VideoSize")) {
4366 get_arg(arg, sizeof(arg), &p);
4368 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4369 if ((video_enc.width % 16) != 0 ||
4370 (video_enc.height % 16) != 0) {
4371 ERROR("Image size must be a multiple of 16\n");
4374 } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4375 get_arg(arg, sizeof(arg), &p);
4377 AVRational frame_rate;
4378 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4379 ERROR("Incorrect frame rate: %s\n", arg);
4381 video_enc.time_base.num = frame_rate.den;
4382 video_enc.time_base.den = frame_rate.num;
4385 } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4386 get_arg(arg, sizeof(arg), &p);
4388 video_enc.gop_size = atoi(arg);
4389 } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4391 video_enc.gop_size = 1;
4392 } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4394 video_enc.mb_decision = FF_MB_DECISION_BITS;
4395 } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4397 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4398 video_enc.flags |= CODEC_FLAG_4MV;
4400 } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4401 !av_strcasecmp(cmd, "AVOptionAudio")) {
4403 AVCodecContext *avctx;
4405 get_arg(arg, sizeof(arg), &p);
4406 get_arg(arg2, sizeof(arg2), &p);
4407 if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4409 type = AV_OPT_FLAG_VIDEO_PARAM;
4412 type = AV_OPT_FLAG_AUDIO_PARAM;
4414 if (avserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4415 ERROR("AVOption error: %s %s\n", arg, arg2);
4417 } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4418 !av_strcasecmp(cmd, "AVPresetAudio")) {
4419 AVCodecContext *avctx;
4421 get_arg(arg, sizeof(arg), &p);
4422 if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4424 video_enc.codec_id = video_id;
4425 type = AV_OPT_FLAG_VIDEO_PARAM;
4428 audio_enc.codec_id = audio_id;
4429 type = AV_OPT_FLAG_AUDIO_PARAM;
4431 if (avserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4432 ERROR("AVPreset error: %s\n", arg);
4434 } else if (!av_strcasecmp(cmd, "VideoTag")) {
4435 get_arg(arg, sizeof(arg), &p);
4436 if ((strlen(arg) == 4) && stream)
4437 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4438 } else if (!av_strcasecmp(cmd, "BitExact")) {
4440 video_enc.flags |= CODEC_FLAG_BITEXACT;
4441 } else if (!av_strcasecmp(cmd, "DctFastint")) {
4443 video_enc.dct_algo = FF_DCT_FASTINT;
4444 } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4446 video_enc.idct_algo = FF_IDCT_SIMPLE;
4447 } else if (!av_strcasecmp(cmd, "Qscale")) {
4448 get_arg(arg, sizeof(arg), &p);
4450 video_enc.flags |= CODEC_FLAG_QSCALE;
4451 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4453 } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4454 get_arg(arg, sizeof(arg), &p);
4456 video_enc.max_qdiff = atoi(arg);
4457 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4458 ERROR("VideoQDiff out of range\n");
4461 } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4462 get_arg(arg, sizeof(arg), &p);
4464 video_enc.qmax = atoi(arg);
4465 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4466 ERROR("VideoQMax out of range\n");
4469 } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4470 get_arg(arg, sizeof(arg), &p);
4472 video_enc.qmin = atoi(arg);
4473 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4474 ERROR("VideoQMin out of range\n");
4477 } else if (!av_strcasecmp(cmd, "LumaElim")) {
4478 get_arg(arg, sizeof(arg), &p);
4480 video_enc.luma_elim_threshold = atoi(arg);
4481 } else if (!av_strcasecmp(cmd, "ChromaElim")) {
4482 get_arg(arg, sizeof(arg), &p);
4484 video_enc.chroma_elim_threshold = atoi(arg);
4485 } else if (!av_strcasecmp(cmd, "LumiMask")) {
4486 get_arg(arg, sizeof(arg), &p);
4488 video_enc.lumi_masking = atof(arg);
4489 } else if (!av_strcasecmp(cmd, "DarkMask")) {
4490 get_arg(arg, sizeof(arg), &p);
4492 video_enc.dark_masking = atof(arg);
4493 } else if (!av_strcasecmp(cmd, "NoVideo")) {
4494 video_id = AV_CODEC_ID_NONE;
4495 } else if (!av_strcasecmp(cmd, "NoAudio")) {
4496 audio_id = AV_CODEC_ID_NONE;
4497 } else if (!av_strcasecmp(cmd, "ACL")) {
4498 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4499 } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4501 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4503 } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4504 get_arg(arg, sizeof(arg), &p);
4506 av_freep(&stream->rtsp_option);
4507 stream->rtsp_option = av_strdup(arg);
4509 } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4510 get_arg(arg, sizeof(arg), &p);
4512 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4513 ERROR("Invalid host/IP address: %s\n", arg);
4515 stream->is_multicast = 1;
4516 stream->loop = 1; /* default is looping */
4518 } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4519 get_arg(arg, sizeof(arg), &p);
4521 stream->multicast_port = atoi(arg);
4522 } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4523 get_arg(arg, sizeof(arg), &p);
4525 stream->multicast_ttl = atoi(arg);
4526 } else if (!av_strcasecmp(cmd, "NoLoop")) {
4529 } else if (!av_strcasecmp(cmd, "</Stream>")) {
4531 ERROR("No corresponding <Stream> for </Stream>\n");
4533 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4534 if (audio_id != AV_CODEC_ID_NONE) {
4535 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4536 audio_enc.codec_id = audio_id;
4537 add_codec(stream, &audio_enc);
4539 if (video_id != AV_CODEC_ID_NONE) {
4540 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4541 video_enc.codec_id = video_id;
4542 add_codec(stream, &video_enc);
4547 } else if (!av_strcasecmp(cmd, "<Redirect")) {
4548 /*********************************************/
4550 if (stream || feed || redirect) {
4551 ERROR("Already in a tag\n");
4553 redirect = av_mallocz(sizeof(FFStream));
4554 *last_stream = redirect;
4555 last_stream = &redirect->next;
4557 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4558 q = strrchr(redirect->filename, '>');
4561 redirect->stream_type = STREAM_TYPE_REDIRECT;
4563 } else if (!av_strcasecmp(cmd, "URL")) {
4565 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4566 } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4568 ERROR("No corresponding <Redirect> for </Redirect>\n");
4570 if (!redirect->feed_filename[0]) {
4571 ERROR("No URL found for <Redirect>\n");
4575 } else if (!av_strcasecmp(cmd, "LoadModule")) {
4576 get_arg(arg, sizeof(arg), &p);
4580 ERROR("Module support not compiled into this version: '%s'\n", arg);
4583 ERROR("Incorrect keyword: '%s'\n", cmd);
4595 static void handle_child_exit(int sig)
4600 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4603 for (feed = first_feed; feed; feed = feed->next) {
4604 if (feed->pid == pid) {
4605 int uptime = time(0) - feed->pid_start;
4608 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4611 /* Turn off any more restarts */
4612 feed->child_argv = 0;
4617 need_to_start_children = 1;
4620 static void opt_debug(void)
4623 avserver_daemon = 0;
4624 logfilename[0] = '-';
4627 void show_help_default(const char *opt, const char *arg)
4629 printf("usage: avserver [options]\n"
4630 "Hyper fast multi format Audio/Video streaming server\n");
4632 show_help_options(options, "Main options:", 0, 0, 0);
4635 static const OptionDef options[] = {
4636 #include "cmdutils_common_opts.h"
4637 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4638 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4639 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/avserver.conf", "configfile" },
4643 int main(int argc, char **argv)
4645 struct sigaction sigact = { { 0 } };
4647 parse_loglevel(argc, argv, options);
4649 avformat_network_init();
4653 my_program_name = argv[0];
4654 my_program_dir = getcwd(0, 0);
4655 avserver_daemon = 1;
4657 parse_options(NULL, argc, argv, options, NULL);
4659 unsetenv("http_proxy"); /* Kill the http_proxy */
4661 av_lfg_init(&random_state, av_get_random_seed());
4663 sigact.sa_handler = handle_child_exit;
4664 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4665 sigaction(SIGCHLD, &sigact, 0);
4667 if (parse_ffconfig(config_filename) < 0) {
4668 fprintf(stderr, "Incorrect config file - exiting.\n");
4672 /* open log file if needed */
4673 if (logfilename[0] != '\0') {
4674 if (!strcmp(logfilename, "-"))
4677 logfile = fopen(logfilename, "a");
4678 av_log_set_callback(http_av_log);
4681 build_file_streams();
4683 build_feed_streams();
4685 compute_bandwidth();
4687 /* put the process in background and detach it from its TTY */
4688 if (avserver_daemon) {
4695 } else if (pid > 0) {
4702 open("/dev/null", O_RDWR);
4703 if (strcmp(logfilename, "-") != 0) {
4713 signal(SIGPIPE, SIG_IGN);
4715 if (avserver_daemon)
4718 if (http_server() < 0) {
4719 http_log("Could not start server\n");