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"
49 #include <sys/ioctl.h>
64 const char program_name[] = "avserver";
65 const int program_birth_year = 2000;
67 static const OptionDef options[];
70 HTTPSTATE_WAIT_REQUEST,
71 HTTPSTATE_SEND_HEADER,
72 HTTPSTATE_SEND_DATA_HEADER,
73 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
74 HTTPSTATE_SEND_DATA_TRAILER,
75 HTTPSTATE_RECEIVE_DATA,
76 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
79 RTSPSTATE_WAIT_REQUEST,
81 RTSPSTATE_SEND_PACKET,
84 static const char *http_state[] = {
100 #define MAX_STREAMS 20
102 #define IOBUFFER_INIT_SIZE 8192
104 /* timeouts are in ms */
105 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
106 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
108 #define SYNC_TIMEOUT (10 * 1000)
110 typedef struct RTSPActionServerSetup {
112 char transport_option[512];
113 } RTSPActionServerSetup;
116 int64_t count1, count2;
117 int64_t time1, time2;
120 /* context associated with one connection */
121 typedef struct HTTPContext {
122 enum HTTPState state;
123 int fd; /* socket file descriptor */
124 struct sockaddr_in from_addr; /* origin */
125 struct pollfd *poll_entry; /* used when polling */
127 uint8_t *buffer_ptr, *buffer_end;
130 int chunked_encoding;
131 int chunk_size; /* 0 if it needs to be read */
132 struct HTTPContext *next;
133 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
137 /* input format handling */
138 AVFormatContext *fmt_in;
139 int64_t start_time; /* In milliseconds - this wraps fairly often */
140 int64_t first_pts; /* initial pts value */
141 int64_t cur_pts; /* current pts value from the stream in us */
142 int64_t cur_frame_duration; /* duration of the current frame in us */
143 int cur_frame_bytes; /* output frame size, needed to compute
144 the time at which we send each
146 int pts_stream_index; /* stream we choose as clock reference */
147 int64_t cur_clock; /* current clock reference value in us */
148 /* output format handling */
149 struct FFStream *stream;
150 /* -1 is invalid stream */
151 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
152 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
154 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
155 int last_packet_sent; /* true if last data packet was sent */
157 DataRateData datarate;
164 int is_packetized; /* if true, the stream is packetized */
165 int packet_stream_index; /* current stream for output in state machine */
167 /* RTSP state specific */
168 uint8_t *pb_buffer; /* XXX: use that in all the code */
170 int seq; /* RTSP sequence number */
172 /* RTP state specific */
173 enum RTSPLowerTransport rtp_protocol;
174 char session_id[32]; /* session id */
175 AVFormatContext *rtp_ctx[MAX_STREAMS];
177 /* RTP/UDP specific */
178 URLContext *rtp_handles[MAX_STREAMS];
180 /* RTP/TCP specific */
181 struct HTTPContext *rtsp_c;
182 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
185 /* each generated stream is described here */
189 STREAM_TYPE_REDIRECT,
192 enum IPAddressAction {
197 typedef struct IPAddressACL {
198 struct IPAddressACL *next;
199 enum IPAddressAction action;
200 /* These are in host order */
201 struct in_addr first;
205 /* description of each stream of the avserver.conf file */
206 typedef struct FFStream {
207 enum StreamType stream_type;
208 char filename[1024]; /* stream filename */
209 struct FFStream *feed; /* feed we are using (can be null if
211 AVDictionary *in_opts; /* input parameters */
212 AVInputFormat *ifmt; /* if non NULL, force input format */
215 char dynamic_acl[1024];
217 int prebuffer; /* Number of millseconds early to start */
218 int64_t max_time; /* Number of milliseconds to run */
220 AVStream *streams[MAX_STREAMS];
221 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
222 char feed_filename[1024]; /* file name of the feed storage, or
223 input file name for a stream */
228 pid_t pid; /* of avconv process */
229 time_t pid_start; /* of avconv process */
231 struct FFStream *next;
232 unsigned bandwidth; /* bandwidth, in kbits/s */
235 /* multicast specific */
237 struct in_addr multicast_ip;
238 int multicast_port; /* first port used for multicast */
240 int loop; /* if true, send the stream in loops (only meaningful if file) */
243 int feed_opened; /* true if someone is writing to the feed */
244 int is_feed; /* true if it is a feed */
245 int readonly; /* True if writing is prohibited to the file */
246 int truncate; /* True if feeder connection truncate the feed file */
248 int64_t bytes_served;
249 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
250 int64_t feed_write_index; /* current write position in feed (it wraps around) */
251 int64_t feed_size; /* current size of feed */
252 struct FFStream *next_feed;
255 typedef struct FeedData {
256 long long data_count;
257 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
260 static struct sockaddr_in my_http_addr;
261 static struct sockaddr_in my_rtsp_addr;
263 static char logfilename[1024];
264 static HTTPContext *first_http_ctx;
265 static FFStream *first_feed; /* contains only feeds */
266 static FFStream *first_stream; /* contains all streams, including feeds */
268 static void new_connection(int server_fd, int is_rtsp);
269 static void close_connection(HTTPContext *c);
272 static int handle_connection(HTTPContext *c);
273 static int http_parse_request(HTTPContext *c);
274 static int http_send_data(HTTPContext *c);
275 static void compute_status(HTTPContext *c);
276 static int open_input_stream(HTTPContext *c, const char *info);
277 static int http_start_receive_data(HTTPContext *c);
278 static int http_receive_data(HTTPContext *c);
281 static int rtsp_parse_request(HTTPContext *c);
282 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
283 static void rtsp_cmd_options(HTTPContext *c, const char *url);
284 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
285 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
286 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
287 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
290 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
291 struct in_addr my_ip);
294 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
295 FFStream *stream, const char *session_id,
296 enum RTSPLowerTransport rtp_protocol);
297 static int rtp_new_av_stream(HTTPContext *c,
298 int stream_index, struct sockaddr_in *dest_addr,
299 HTTPContext *rtsp_c);
301 static const char *my_program_name;
302 static const char *my_program_dir;
304 static const char *config_filename = "/etc/avserver.conf";
306 static int avserver_debug;
307 static int avserver_daemon;
308 static int no_launch;
309 static int need_to_start_children;
311 /* maximum number of simultaneous HTTP connections */
312 static unsigned int nb_max_http_connections = 2000;
313 static unsigned int nb_max_connections = 5;
314 static unsigned int nb_connections;
316 static uint64_t max_bandwidth = 1000;
317 static uint64_t current_bandwidth;
319 static int64_t cur_time; // Making this global saves on passing it around everywhere
321 static AVLFG random_state;
323 static FILE *logfile = NULL;
325 void exit_program(int ret)
330 /* FIXME: make avserver work with IPv6 */
331 /* resolve host with also IP address parsing */
332 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
335 if (!ff_inet_aton(hostname, sin_addr)) {
337 struct addrinfo *ai, *cur;
338 struct addrinfo hints = { 0 };
339 hints.ai_family = AF_INET;
340 if (getaddrinfo(hostname, NULL, &hints, &ai))
342 /* getaddrinfo returns a linked list of addrinfo structs.
343 * Even if we set ai_family = AF_INET above, make sure
344 * that the returned one actually is of the correct type. */
345 for (cur = ai; cur; cur = cur->ai_next) {
346 if (cur->ai_family == AF_INET) {
347 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
356 hp = gethostbyname(hostname);
359 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
365 static char *ctime1(char *buf2)
373 p = buf2 + strlen(p) - 1;
379 static void http_vlog(const char *fmt, va_list vargs)
381 static int print_prefix = 1;
386 fprintf(logfile, "%s ", buf);
388 print_prefix = strstr(fmt, "\n") != NULL;
389 vfprintf(logfile, fmt, vargs);
395 __attribute__ ((format (printf, 1, 2)))
397 static void http_log(const char *fmt, ...)
400 va_start(vargs, fmt);
401 http_vlog(fmt, vargs);
405 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
407 static int print_prefix = 1;
408 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
409 if (level > av_log_get_level())
411 if (print_prefix && avc)
412 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
413 print_prefix = strstr(fmt, "\n") != NULL;
414 http_vlog(fmt, vargs);
417 static void log_connection(HTTPContext *c)
422 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
423 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
424 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
427 static void update_datarate(DataRateData *drd, int64_t count)
429 if (!drd->time1 && !drd->count1) {
430 drd->time1 = drd->time2 = cur_time;
431 drd->count1 = drd->count2 = count;
432 } else if (cur_time - drd->time2 > 5000) {
433 drd->time1 = drd->time2;
434 drd->count1 = drd->count2;
435 drd->time2 = cur_time;
440 /* In bytes per second */
441 static int compute_datarate(DataRateData *drd, int64_t count)
443 if (cur_time == drd->time1)
446 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
450 static void start_children(FFStream *feed)
455 for (; feed; feed = feed->next) {
456 if (feed->child_argv && !feed->pid) {
457 feed->pid_start = time(0);
462 http_log("Unable to create children\n");
471 av_strlcpy(pathname, my_program_name, sizeof(pathname));
473 slash = strrchr(pathname, '/');
478 strcpy(slash, "avconv");
480 http_log("Launch command line: ");
481 http_log("%s ", pathname);
482 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
483 http_log("%s ", feed->child_argv[i]);
486 for (i = 3; i < 256; i++)
489 if (!avserver_debug) {
490 i = open("/dev/null", O_RDWR);
499 /* This is needed to make relative pathnames work */
500 chdir(my_program_dir);
502 signal(SIGPIPE, SIG_DFL);
504 execvp(pathname, feed->child_argv);
512 /* open a listening socket */
513 static int socket_open_listen(struct sockaddr_in *my_addr)
517 server_fd = socket(AF_INET,SOCK_STREAM,0);
524 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
526 my_addr->sin_family = AF_INET;
527 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
529 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
531 closesocket(server_fd);
535 if (listen (server_fd, 5) < 0) {
537 closesocket(server_fd);
540 ff_socket_nonblock(server_fd, 1);
545 /* start all multicast streams */
546 static void start_multicast(void)
551 struct sockaddr_in dest_addr;
552 int default_port, stream_index;
555 for(stream = first_stream; stream != NULL; stream = stream->next) {
556 if (stream->is_multicast) {
557 /* open the RTP connection */
558 snprintf(session_id, sizeof(session_id), "%08x%08x",
559 av_lfg_get(&random_state), av_lfg_get(&random_state));
561 /* choose a port if none given */
562 if (stream->multicast_port == 0) {
563 stream->multicast_port = default_port;
567 dest_addr.sin_family = AF_INET;
568 dest_addr.sin_addr = stream->multicast_ip;
569 dest_addr.sin_port = htons(stream->multicast_port);
571 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
572 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
576 if (open_input_stream(rtp_c, "") < 0) {
577 http_log("Could not open input stream for stream '%s'\n",
582 /* open each RTP stream */
583 for(stream_index = 0; stream_index < stream->nb_streams;
585 dest_addr.sin_port = htons(stream->multicast_port +
587 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
588 http_log("Could not open output stream '%s/streamid=%d'\n",
589 stream->filename, stream_index);
594 /* change state to send data */
595 rtp_c->state = HTTPSTATE_SEND_DATA;
600 /* main loop of the http server */
601 static int http_server(void)
603 int server_fd = 0, rtsp_server_fd = 0;
604 int ret, delay, delay1;
605 struct pollfd *poll_table, *poll_entry;
606 HTTPContext *c, *c_next;
608 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
609 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
613 if (my_http_addr.sin_port) {
614 server_fd = socket_open_listen(&my_http_addr);
619 if (my_rtsp_addr.sin_port) {
620 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
621 if (rtsp_server_fd < 0)
625 if (!rtsp_server_fd && !server_fd) {
626 http_log("HTTP and RTSP disabled.\n");
630 http_log("AVserver started.\n");
632 start_children(first_feed);
637 poll_entry = poll_table;
639 poll_entry->fd = server_fd;
640 poll_entry->events = POLLIN;
643 if (rtsp_server_fd) {
644 poll_entry->fd = rtsp_server_fd;
645 poll_entry->events = POLLIN;
649 /* wait for events on each HTTP handle */
656 case HTTPSTATE_SEND_HEADER:
657 case RTSPSTATE_SEND_REPLY:
658 case RTSPSTATE_SEND_PACKET:
659 c->poll_entry = poll_entry;
661 poll_entry->events = POLLOUT;
664 case HTTPSTATE_SEND_DATA_HEADER:
665 case HTTPSTATE_SEND_DATA:
666 case HTTPSTATE_SEND_DATA_TRAILER:
667 if (!c->is_packetized) {
668 /* for TCP, we output as much as we can (may need to put a limit) */
669 c->poll_entry = poll_entry;
671 poll_entry->events = POLLOUT;
674 /* when avserver is doing the timing, we work by
675 looking at which packet need to be sent every
677 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
682 case HTTPSTATE_WAIT_REQUEST:
683 case HTTPSTATE_RECEIVE_DATA:
684 case HTTPSTATE_WAIT_FEED:
685 case RTSPSTATE_WAIT_REQUEST:
686 /* need to catch errors */
687 c->poll_entry = poll_entry;
689 poll_entry->events = POLLIN;/* Maybe this will work */
693 c->poll_entry = NULL;
699 /* wait for an event on one connection. We poll at least every
700 second to handle timeouts */
702 ret = poll(poll_table, poll_entry - poll_table, delay);
703 if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
704 ff_neterrno() != AVERROR(EINTR))
708 cur_time = av_gettime() / 1000;
710 if (need_to_start_children) {
711 need_to_start_children = 0;
712 start_children(first_feed);
715 /* now handle the events */
716 for(c = first_http_ctx; c != NULL; c = c_next) {
718 if (handle_connection(c) < 0) {
719 /* close and free the connection */
725 poll_entry = poll_table;
727 /* new HTTP connection request ? */
728 if (poll_entry->revents & POLLIN)
729 new_connection(server_fd, 0);
732 if (rtsp_server_fd) {
733 /* new RTSP connection request ? */
734 if (poll_entry->revents & POLLIN)
735 new_connection(rtsp_server_fd, 1);
740 /* start waiting for a new HTTP/RTSP request */
741 static void start_wait_request(HTTPContext *c, int is_rtsp)
743 c->buffer_ptr = c->buffer;
744 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
747 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
748 c->state = RTSPSTATE_WAIT_REQUEST;
750 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
751 c->state = HTTPSTATE_WAIT_REQUEST;
755 static void http_send_too_busy_reply(int fd)
758 int len = snprintf(buffer, sizeof(buffer),
759 "HTTP/1.0 503 Server too busy\r\n"
760 "Content-type: text/html\r\n"
762 "<html><head><title>Too busy</title></head><body>\r\n"
763 "<p>The server is too busy to serve your request at this time.</p>\r\n"
764 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
765 "</body></html>\r\n",
766 nb_connections, nb_max_connections);
767 send(fd, buffer, len, 0);
771 static void new_connection(int server_fd, int is_rtsp)
773 struct sockaddr_in from_addr;
775 HTTPContext *c = NULL;
777 len = sizeof(from_addr);
778 fd = accept(server_fd, (struct sockaddr *)&from_addr,
781 http_log("error during accept %s\n", strerror(errno));
784 ff_socket_nonblock(fd, 1);
786 if (nb_connections >= nb_max_connections) {
787 http_send_too_busy_reply(fd);
791 /* add a new connection */
792 c = av_mallocz(sizeof(HTTPContext));
797 c->poll_entry = NULL;
798 c->from_addr = from_addr;
799 c->buffer_size = IOBUFFER_INIT_SIZE;
800 c->buffer = av_malloc(c->buffer_size);
804 c->next = first_http_ctx;
808 start_wait_request(c, is_rtsp);
820 static void close_connection(HTTPContext *c)
822 HTTPContext **cp, *c1;
824 AVFormatContext *ctx;
828 /* remove connection from list */
829 cp = &first_http_ctx;
830 while ((*cp) != NULL) {
838 /* remove references, if any (XXX: do it faster) */
839 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
844 /* remove connection associated resources */
848 /* close each frame parser */
849 for(i=0;i<c->fmt_in->nb_streams;i++) {
850 st = c->fmt_in->streams[i];
851 if (st->codec->codec)
852 avcodec_close(st->codec);
854 avformat_close_input(&c->fmt_in);
857 /* free RTP output streams if any */
860 nb_streams = c->stream->nb_streams;
862 for(i=0;i<nb_streams;i++) {
865 av_write_trailer(ctx);
866 av_dict_free(&ctx->metadata);
867 av_free(ctx->streams[0]);
870 h = c->rtp_handles[i];
877 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
880 if (avio_open_dyn_buf(&ctx->pb) >= 0) {
881 av_write_trailer(ctx);
882 av_freep(&c->pb_buffer);
883 avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
888 for(i=0; i<ctx->nb_streams; i++)
889 av_free(ctx->streams[i]);
891 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
892 current_bandwidth -= c->stream->bandwidth;
894 /* signal that there is no feed if we are the feeder socket */
895 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
896 c->stream->feed_opened = 0;
900 av_freep(&c->pb_buffer);
901 av_freep(&c->packet_buffer);
907 static int handle_connection(HTTPContext *c)
912 case HTTPSTATE_WAIT_REQUEST:
913 case RTSPSTATE_WAIT_REQUEST:
915 if ((c->timeout - cur_time) < 0)
917 if (c->poll_entry->revents & (POLLERR | POLLHUP))
920 /* no need to read if no events */
921 if (!(c->poll_entry->revents & POLLIN))
925 len = recv(c->fd, c->buffer_ptr, 1, 0);
927 if (ff_neterrno() != AVERROR(EAGAIN) &&
928 ff_neterrno() != AVERROR(EINTR))
930 } else if (len == 0) {
933 /* search for end of request. */
935 c->buffer_ptr += len;
937 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
938 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
939 /* request found : parse it and reply */
940 if (c->state == HTTPSTATE_WAIT_REQUEST) {
941 ret = http_parse_request(c);
943 ret = rtsp_parse_request(c);
947 } else if (ptr >= c->buffer_end) {
948 /* request too long: cannot do anything */
950 } else goto read_loop;
954 case HTTPSTATE_SEND_HEADER:
955 if (c->poll_entry->revents & (POLLERR | POLLHUP))
958 /* no need to write if no events */
959 if (!(c->poll_entry->revents & POLLOUT))
961 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
963 if (ff_neterrno() != AVERROR(EAGAIN) &&
964 ff_neterrno() != AVERROR(EINTR)) {
965 /* error : close connection */
966 av_freep(&c->pb_buffer);
970 c->buffer_ptr += len;
972 c->stream->bytes_served += len;
973 c->data_count += len;
974 if (c->buffer_ptr >= c->buffer_end) {
975 av_freep(&c->pb_buffer);
979 /* all the buffer was sent : synchronize to the incoming stream */
980 c->state = HTTPSTATE_SEND_DATA_HEADER;
981 c->buffer_ptr = c->buffer_end = c->buffer;
986 case HTTPSTATE_SEND_DATA:
987 case HTTPSTATE_SEND_DATA_HEADER:
988 case HTTPSTATE_SEND_DATA_TRAILER:
989 /* for packetized output, we consider we can always write (the
990 input streams sets the speed). It may be better to verify
991 that we do not rely too much on the kernel queues */
992 if (!c->is_packetized) {
993 if (c->poll_entry->revents & (POLLERR | POLLHUP))
996 /* no need to read if no events */
997 if (!(c->poll_entry->revents & POLLOUT))
1000 if (http_send_data(c) < 0)
1002 /* close connection if trailer sent */
1003 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
1006 case HTTPSTATE_RECEIVE_DATA:
1007 /* no need to read if no events */
1008 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1010 if (!(c->poll_entry->revents & POLLIN))
1012 if (http_receive_data(c) < 0)
1015 case HTTPSTATE_WAIT_FEED:
1016 /* no need to read if no events */
1017 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1020 /* nothing to do, we'll be waken up by incoming feed packets */
1023 case RTSPSTATE_SEND_REPLY:
1024 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1025 av_freep(&c->pb_buffer);
1028 /* no need to write if no events */
1029 if (!(c->poll_entry->revents & POLLOUT))
1031 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1033 if (ff_neterrno() != AVERROR(EAGAIN) &&
1034 ff_neterrno() != AVERROR(EINTR)) {
1035 /* error : close connection */
1036 av_freep(&c->pb_buffer);
1040 c->buffer_ptr += len;
1041 c->data_count += len;
1042 if (c->buffer_ptr >= c->buffer_end) {
1043 /* all the buffer was sent : wait for a new request */
1044 av_freep(&c->pb_buffer);
1045 start_wait_request(c, 1);
1049 case RTSPSTATE_SEND_PACKET:
1050 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1051 av_freep(&c->packet_buffer);
1054 /* no need to write if no events */
1055 if (!(c->poll_entry->revents & POLLOUT))
1057 len = send(c->fd, c->packet_buffer_ptr,
1058 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1060 if (ff_neterrno() != AVERROR(EAGAIN) &&
1061 ff_neterrno() != AVERROR(EINTR)) {
1062 /* error : close connection */
1063 av_freep(&c->packet_buffer);
1067 c->packet_buffer_ptr += len;
1068 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1069 /* all the buffer was sent : wait for a new request */
1070 av_freep(&c->packet_buffer);
1071 c->state = RTSPSTATE_WAIT_REQUEST;
1075 case HTTPSTATE_READY:
1084 static int extract_rates(char *rates, int ratelen, const char *request)
1088 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1089 if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1090 const char *q = p + 7;
1092 while (*q && *q != '\n' && isspace(*q))
1095 if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1101 memset(rates, 0xff, ratelen);
1104 while (*q && *q != '\n' && *q != ':')
1107 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1111 if (stream_no < ratelen && stream_no >= 0)
1112 rates[stream_no] = rate_no;
1114 while (*q && *q != '\n' && !isspace(*q))
1121 p = strchr(p, '\n');
1131 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1134 int best_bitrate = 100000000;
1137 for (i = 0; i < feed->nb_streams; i++) {
1138 AVCodecContext *feed_codec = feed->streams[i]->codec;
1140 if (feed_codec->codec_id != codec->codec_id ||
1141 feed_codec->sample_rate != codec->sample_rate ||
1142 feed_codec->width != codec->width ||
1143 feed_codec->height != codec->height)
1146 /* Potential stream */
1148 /* We want the fastest stream less than bit_rate, or the slowest
1149 * faster than bit_rate
1152 if (feed_codec->bit_rate <= bit_rate) {
1153 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1154 best_bitrate = feed_codec->bit_rate;
1158 if (feed_codec->bit_rate < best_bitrate) {
1159 best_bitrate = feed_codec->bit_rate;
1168 static int modify_current_stream(HTTPContext *c, char *rates)
1171 FFStream *req = c->stream;
1172 int action_required = 0;
1174 /* Not much we can do for a feed */
1178 for (i = 0; i < req->nb_streams; i++) {
1179 AVCodecContext *codec = req->streams[i]->codec;
1183 c->switch_feed_streams[i] = req->feed_streams[i];
1186 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1189 /* Wants off or slow */
1190 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1192 /* This doesn't work well when it turns off the only stream! */
1193 c->switch_feed_streams[i] = -2;
1194 c->feed_streams[i] = -2;
1199 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1200 action_required = 1;
1203 return action_required;
1206 /* XXX: factorize in utils.c ? */
1207 /* XXX: take care with different space meaning */
1208 static void skip_spaces(const char **pp)
1212 while (*p == ' ' || *p == '\t')
1217 static void get_word(char *buf, int buf_size, const char **pp)
1225 while (!isspace(*p) && *p != '\0') {
1226 if ((q - buf) < buf_size - 1)
1235 static void get_arg(char *buf, int buf_size, const char **pp)
1242 while (isspace(*p)) p++;
1245 if (*p == '\"' || *p == '\'')
1257 if ((q - buf) < buf_size - 1)
1262 if (quote && *p == quote)
1267 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1268 const char *p, const char *filename, int line_num)
1274 get_arg(arg, sizeof(arg), &p);
1275 if (av_strcasecmp(arg, "allow") == 0)
1276 acl.action = IP_ALLOW;
1277 else if (av_strcasecmp(arg, "deny") == 0)
1278 acl.action = IP_DENY;
1280 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1281 filename, line_num, arg);
1285 get_arg(arg, sizeof(arg), &p);
1287 if (resolve_host(&acl.first, arg) != 0) {
1288 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1289 filename, line_num, arg);
1292 acl.last = acl.first;
1294 get_arg(arg, sizeof(arg), &p);
1297 if (resolve_host(&acl.last, arg) != 0) {
1298 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1299 filename, line_num, arg);
1305 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1306 IPAddressACL **naclp = 0;
1312 naclp = &stream->acl;
1318 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1319 filename, line_num);
1325 naclp = &(*naclp)->next;
1333 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1338 IPAddressACL *acl = NULL;
1342 f = fopen(stream->dynamic_acl, "r");
1344 perror(stream->dynamic_acl);
1348 acl = av_mallocz(sizeof(IPAddressACL));
1352 if (fgets(line, sizeof(line), f) == NULL)
1358 if (*p == '\0' || *p == '#')
1360 get_arg(cmd, sizeof(cmd), &p);
1362 if (!av_strcasecmp(cmd, "ACL"))
1363 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1370 static void free_acl_list(IPAddressACL *in_acl)
1372 IPAddressACL *pacl,*pacl2;
1382 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1384 enum IPAddressAction last_action = IP_DENY;
1386 struct in_addr *src = &c->from_addr.sin_addr;
1387 unsigned long src_addr = src->s_addr;
1389 for (acl = in_acl; acl; acl = acl->next) {
1390 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1391 return (acl->action == IP_ALLOW) ? 1 : 0;
1392 last_action = acl->action;
1395 /* Nothing matched, so return not the last action */
1396 return (last_action == IP_DENY) ? 1 : 0;
1399 static int validate_acl(FFStream *stream, HTTPContext *c)
1405 /* if stream->acl is null validate_acl_list will return 1 */
1406 ret = validate_acl_list(stream->acl, c);
1408 if (stream->dynamic_acl[0]) {
1409 acl = parse_dynamic_acl(stream, c);
1411 ret = validate_acl_list(acl, c);
1419 /* compute the real filename of a file by matching it without its
1420 extensions to all the stream filenames */
1421 static void compute_real_filename(char *filename, int max_size)
1428 /* compute filename by matching without the file extensions */
1429 av_strlcpy(file1, filename, sizeof(file1));
1430 p = strrchr(file1, '.');
1433 for(stream = first_stream; stream != NULL; stream = stream->next) {
1434 av_strlcpy(file2, stream->filename, sizeof(file2));
1435 p = strrchr(file2, '.');
1438 if (!strcmp(file1, file2)) {
1439 av_strlcpy(filename, stream->filename, max_size);
1454 /* parse http request and prepare header */
1455 static int http_parse_request(HTTPContext *c)
1458 enum RedirType redir_type;
1460 char info[1024], filename[1024];
1464 const char *mime_type;
1468 char *useragent = 0;
1471 get_word(cmd, sizeof(cmd), (const char **)&p);
1472 av_strlcpy(c->method, cmd, sizeof(c->method));
1474 if (!strcmp(cmd, "GET"))
1476 else if (!strcmp(cmd, "POST"))
1481 get_word(url, sizeof(url), (const char **)&p);
1482 av_strlcpy(c->url, url, sizeof(c->url));
1484 get_word(protocol, sizeof(protocol), (const char **)&p);
1485 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1488 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1491 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1493 /* find the filename and the optional info string in the request */
1494 p = strchr(url, '?');
1496 av_strlcpy(info, p, sizeof(info));
1501 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1503 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1504 if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1506 if (*useragent && *useragent != '\n' && isspace(*useragent))
1510 p = strchr(p, '\n');
1517 redir_type = REDIR_NONE;
1518 if (av_match_ext(filename, "asx")) {
1519 redir_type = REDIR_ASX;
1520 filename[strlen(filename)-1] = 'f';
1521 } else if (av_match_ext(filename, "asf") &&
1522 (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1523 /* if this isn't WMP or lookalike, return the redirector file */
1524 redir_type = REDIR_ASF;
1525 } else if (av_match_ext(filename, "rpm,ram")) {
1526 redir_type = REDIR_RAM;
1527 strcpy(filename + strlen(filename)-2, "m");
1528 } else if (av_match_ext(filename, "rtsp")) {
1529 redir_type = REDIR_RTSP;
1530 compute_real_filename(filename, sizeof(filename) - 1);
1531 } else if (av_match_ext(filename, "sdp")) {
1532 redir_type = REDIR_SDP;
1533 compute_real_filename(filename, sizeof(filename) - 1);
1536 // "redirect" / request to index.html
1537 if (!strlen(filename))
1538 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1540 stream = first_stream;
1541 while (stream != NULL) {
1542 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1544 stream = stream->next;
1546 if (stream == NULL) {
1547 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1548 http_log("File '%s' not found\n", url);
1553 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1554 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1556 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1557 c->http_error = 301;
1559 q += snprintf(q, c->buffer_size,
1560 "HTTP/1.0 301 Moved\r\n"
1562 "Content-type: text/html\r\n"
1564 "<html><head><title>Moved</title></head><body>\r\n"
1565 "You should be <a href=\"%s\">redirected</a>.\r\n"
1566 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1567 /* prepare output buffer */
1568 c->buffer_ptr = c->buffer;
1570 c->state = HTTPSTATE_SEND_HEADER;
1574 /* If this is WMP, get the rate information */
1575 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1576 if (modify_current_stream(c, ratebuf)) {
1577 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1578 if (c->switch_feed_streams[i] >= 0)
1579 c->switch_feed_streams[i] = -1;
1584 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1585 current_bandwidth += stream->bandwidth;
1587 /* If already streaming this feed, do not let start another feeder. */
1588 if (stream->feed_opened) {
1589 snprintf(msg, sizeof(msg), "This feed is already being received.");
1590 http_log("Feed '%s' already being received\n", stream->feed_filename);
1594 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1595 c->http_error = 503;
1597 q += snprintf(q, c->buffer_size,
1598 "HTTP/1.0 503 Server too busy\r\n"
1599 "Content-type: text/html\r\n"
1601 "<html><head><title>Too busy</title></head><body>\r\n"
1602 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1603 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1604 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1605 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1606 /* prepare output buffer */
1607 c->buffer_ptr = c->buffer;
1609 c->state = HTTPSTATE_SEND_HEADER;
1613 if (redir_type != REDIR_NONE) {
1616 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1617 if (av_strncasecmp(p, "Host:", 5) == 0) {
1621 p = strchr(p, '\n');
1632 while (isspace(*hostinfo))
1635 eoh = strchr(hostinfo, '\n');
1637 if (eoh[-1] == '\r')
1640 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1641 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1642 hostbuf[eoh - hostinfo] = 0;
1644 c->http_error = 200;
1646 switch(redir_type) {
1648 q += snprintf(q, c->buffer_size,
1649 "HTTP/1.0 200 ASX Follows\r\n"
1650 "Content-type: video/x-ms-asf\r\n"
1652 "<ASX Version=\"3\">\r\n"
1653 //"<!-- Autogenerated by avserver -->\r\n"
1654 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1655 "</ASX>\r\n", hostbuf, filename, info);
1658 q += snprintf(q, c->buffer_size,
1659 "HTTP/1.0 200 RAM Follows\r\n"
1660 "Content-type: audio/x-pn-realaudio\r\n"
1662 "# Autogenerated by avserver\r\n"
1663 "http://%s/%s%s\r\n", hostbuf, filename, info);
1666 q += snprintf(q, c->buffer_size,
1667 "HTTP/1.0 200 ASF Redirect follows\r\n"
1668 "Content-type: video/x-ms-asf\r\n"
1671 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1675 char hostname[256], *p;
1676 /* extract only hostname */
1677 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1678 p = strrchr(hostname, ':');
1681 q += snprintf(q, c->buffer_size,
1682 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1683 /* XXX: incorrect mime type ? */
1684 "Content-type: application/x-rtsp\r\n"
1686 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1692 int sdp_data_size, len;
1693 struct sockaddr_in my_addr;
1695 q += snprintf(q, c->buffer_size,
1696 "HTTP/1.0 200 OK\r\n"
1697 "Content-type: application/sdp\r\n"
1700 len = sizeof(my_addr);
1701 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1703 /* XXX: should use a dynamic buffer */
1704 sdp_data_size = prepare_sdp_description(stream,
1707 if (sdp_data_size > 0) {
1708 memcpy(q, sdp_data, sdp_data_size);
1720 /* prepare output buffer */
1721 c->buffer_ptr = c->buffer;
1723 c->state = HTTPSTATE_SEND_HEADER;
1729 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1733 stream->conns_served++;
1735 /* XXX: add there authenticate and IP match */
1738 /* if post, it means a feed is being sent */
1739 if (!stream->is_feed) {
1740 /* However it might be a status report from WMP! Let us log the
1741 * data as it might come in handy one day. */
1745 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1746 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1750 if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1751 client_id = strtol(p + 18, 0, 10);
1752 p = strchr(p, '\n');
1760 char *eol = strchr(logline, '\n');
1765 if (eol[-1] == '\r')
1767 http_log("%.*s\n", (int) (eol - logline), logline);
1768 c->suppress_log = 1;
1773 http_log("\nGot request:\n%s\n", c->buffer);
1776 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1779 /* Now we have to find the client_id */
1780 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1781 if (wmpc->wmp_client_id == client_id)
1785 if (wmpc && modify_current_stream(wmpc, ratebuf))
1786 wmpc->switch_pending = 1;
1789 snprintf(msg, sizeof(msg), "POST command not handled");
1793 if (http_start_receive_data(c) < 0) {
1794 snprintf(msg, sizeof(msg), "could not open feed");
1798 c->state = HTTPSTATE_RECEIVE_DATA;
1803 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1804 http_log("\nGot request:\n%s\n", c->buffer);
1807 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1810 /* open input stream */
1811 if (open_input_stream(c, info) < 0) {
1812 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1816 /* prepare http header */
1818 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1819 mime_type = c->stream->fmt->mime_type;
1821 mime_type = "application/x-octet-stream";
1822 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1824 /* for asf, we need extra headers */
1825 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1826 /* Need to allocate a client id */
1828 c->wmp_client_id = av_lfg_get(&random_state);
1830 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);
1832 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1833 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1835 /* prepare output buffer */
1837 c->buffer_ptr = c->buffer;
1839 c->state = HTTPSTATE_SEND_HEADER;
1842 c->http_error = 404;
1844 q += snprintf(q, c->buffer_size,
1845 "HTTP/1.0 404 Not Found\r\n"
1846 "Content-type: text/html\r\n"
1849 "<head><title>404 Not Found</title></head>\n"
1852 /* prepare output buffer */
1853 c->buffer_ptr = c->buffer;
1855 c->state = HTTPSTATE_SEND_HEADER;
1859 c->http_error = 200; /* horrible : we use this value to avoid
1860 going to the send data state */
1861 c->state = HTTPSTATE_SEND_HEADER;
1865 static void fmt_bytecount(AVIOContext *pb, int64_t count)
1867 static const char suffix[] = " kMGTP";
1870 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1872 avio_printf(pb, "%"PRId64"%c", count, *s);
1875 static void compute_status(HTTPContext *c)
1884 if (avio_open_dyn_buf(&pb) < 0) {
1885 /* XXX: return an error ? */
1886 c->buffer_ptr = c->buffer;
1887 c->buffer_end = c->buffer;
1891 avio_printf(pb, "HTTP/1.0 200 OK\r\n");
1892 avio_printf(pb, "Content-type: %s\r\n", "text/html");
1893 avio_printf(pb, "Pragma: no-cache\r\n");
1894 avio_printf(pb, "\r\n");
1896 avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1897 if (c->stream->feed_filename[0])
1898 avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1899 avio_printf(pb, "</head>\n<body>");
1900 avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
1902 avio_printf(pb, "<h2>Available Streams</h2>\n");
1903 avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
1904 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");
1905 stream = first_stream;
1906 while (stream != NULL) {
1907 char sfilename[1024];
1910 if (stream->feed != stream) {
1911 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1912 eosf = sfilename + strlen(sfilename);
1913 if (eosf - sfilename >= 4) {
1914 if (strcmp(eosf - 4, ".asf") == 0)
1915 strcpy(eosf - 4, ".asx");
1916 else if (strcmp(eosf - 3, ".rm") == 0)
1917 strcpy(eosf - 3, ".ram");
1918 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1919 /* generate a sample RTSP director if
1920 unicast. Generate an SDP redirector if
1922 eosf = strrchr(sfilename, '.');
1924 eosf = sfilename + strlen(sfilename);
1925 if (stream->is_multicast)
1926 strcpy(eosf, ".sdp");
1928 strcpy(eosf, ".rtsp");
1932 avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1933 sfilename, stream->filename);
1934 avio_printf(pb, "<td align=right> %d <td align=right> ",
1935 stream->conns_served);
1936 fmt_bytecount(pb, stream->bytes_served);
1937 switch(stream->stream_type) {
1938 case STREAM_TYPE_LIVE: {
1939 int audio_bit_rate = 0;
1940 int video_bit_rate = 0;
1941 const char *audio_codec_name = "";
1942 const char *video_codec_name = "";
1943 const char *audio_codec_name_extra = "";
1944 const char *video_codec_name_extra = "";
1946 for(i=0;i<stream->nb_streams;i++) {
1947 AVStream *st = stream->streams[i];
1948 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1949 switch(st->codec->codec_type) {
1950 case AVMEDIA_TYPE_AUDIO:
1951 audio_bit_rate += st->codec->bit_rate;
1953 if (*audio_codec_name)
1954 audio_codec_name_extra = "...";
1955 audio_codec_name = codec->name;
1958 case AVMEDIA_TYPE_VIDEO:
1959 video_bit_rate += st->codec->bit_rate;
1961 if (*video_codec_name)
1962 video_codec_name_extra = "...";
1963 video_codec_name = codec->name;
1966 case AVMEDIA_TYPE_DATA:
1967 video_bit_rate += st->codec->bit_rate;
1973 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",
1976 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1977 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1979 avio_printf(pb, "<td>%s", stream->feed->filename);
1981 avio_printf(pb, "<td>%s", stream->feed_filename);
1982 avio_printf(pb, "\n");
1986 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1990 stream = stream->next;
1992 avio_printf(pb, "</table>\n");
1994 stream = first_stream;
1995 while (stream != NULL) {
1996 if (stream->feed == stream) {
1997 avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
1999 avio_printf(pb, "Running as pid %d.\n", stream->pid);
2001 #if defined(linux) && !defined(CONFIG_NOCUTILS)
2006 /* This is somewhat linux specific I guess */
2007 snprintf(ps_cmd, sizeof(ps_cmd),
2008 "ps -o \"%%cpu,cputime\" --no-headers %d",
2011 pid_stat = popen(ps_cmd, "r");
2016 if (fscanf(pid_stat, "%10s %64s", cpuperc,
2018 avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2026 avio_printf(pb, "<p>");
2028 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");
2030 for (i = 0; i < stream->nb_streams; i++) {
2031 AVStream *st = stream->streams[i];
2032 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2033 const char *type = "unknown";
2034 char parameters[64];
2038 switch(st->codec->codec_type) {
2039 case AVMEDIA_TYPE_AUDIO:
2041 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2043 case AVMEDIA_TYPE_VIDEO:
2045 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2046 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2051 avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2052 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2054 avio_printf(pb, "</table>\n");
2057 stream = stream->next;
2060 /* connection status */
2061 avio_printf(pb, "<h2>Connection Status</h2>\n");
2063 avio_printf(pb, "Number of connections: %d / %d<br>\n",
2064 nb_connections, nb_max_connections);
2066 avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2067 current_bandwidth, max_bandwidth);
2069 avio_printf(pb, "<table>\n");
2070 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");
2071 c1 = first_http_ctx;
2073 while (c1 != NULL) {
2079 for (j = 0; j < c1->stream->nb_streams; j++) {
2080 if (!c1->stream->feed)
2081 bitrate += c1->stream->streams[j]->codec->bit_rate;
2082 else if (c1->feed_streams[j] >= 0)
2083 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2088 p = inet_ntoa(c1->from_addr.sin_addr);
2089 avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2091 c1->stream ? c1->stream->filename : "",
2092 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2095 http_state[c1->state]);
2096 fmt_bytecount(pb, bitrate);
2097 avio_printf(pb, "<td align=right>");
2098 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2099 avio_printf(pb, "<td align=right>");
2100 fmt_bytecount(pb, c1->data_count);
2101 avio_printf(pb, "\n");
2104 avio_printf(pb, "</table>\n");
2109 avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
2110 avio_printf(pb, "</body>\n</html>\n");
2112 len = avio_close_dyn_buf(pb, &c->pb_buffer);
2113 c->buffer_ptr = c->pb_buffer;
2114 c->buffer_end = c->pb_buffer + len;
2117 static int open_input_stream(HTTPContext *c, const char *info)
2120 char input_filename[1024];
2121 AVFormatContext *s = NULL;
2125 /* find file name */
2126 if (c->stream->feed) {
2127 strcpy(input_filename, c->stream->feed->feed_filename);
2128 /* compute position (absolute time) */
2129 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2130 if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
2132 } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2133 int prebuffer = strtol(buf, 0, 10);
2134 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2136 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2138 strcpy(input_filename, c->stream->feed_filename);
2139 /* compute position (relative time) */
2140 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2141 if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
2146 if (input_filename[0] == '\0')
2150 if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2151 http_log("could not open %s: %d\n", input_filename, ret);
2154 s->flags |= AVFMT_FLAG_GENPTS;
2156 if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2157 http_log("Could not find stream info '%s'\n", input_filename);
2158 avformat_close_input(&s);
2162 /* choose stream as clock source (we favorize video stream if
2163 present) for packet sending */
2164 c->pts_stream_index = 0;
2165 for(i=0;i<c->stream->nb_streams;i++) {
2166 if (c->pts_stream_index == 0 &&
2167 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2168 c->pts_stream_index = i;
2172 if (c->fmt_in->iformat->read_seek)
2173 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2174 /* set the start time (needed for maxtime and RTP packet timing) */
2175 c->start_time = cur_time;
2176 c->first_pts = AV_NOPTS_VALUE;
2180 /* return the server clock (in us) */
2181 static int64_t get_server_clock(HTTPContext *c)
2183 /* compute current pts value from system time */
2184 return (cur_time - c->start_time) * 1000;
2187 /* return the estimated time at which the current packet must be sent
2189 static int64_t get_packet_send_clock(HTTPContext *c)
2191 int bytes_left, bytes_sent, frame_bytes;
2193 frame_bytes = c->cur_frame_bytes;
2194 if (frame_bytes <= 0)
2197 bytes_left = c->buffer_end - c->buffer_ptr;
2198 bytes_sent = frame_bytes - bytes_left;
2199 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2204 static int http_prepare_data(HTTPContext *c)
2207 AVFormatContext *ctx;
2209 av_freep(&c->pb_buffer);
2211 case HTTPSTATE_SEND_DATA_HEADER:
2212 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2213 av_dict_set(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2214 av_dict_set(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2215 av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2216 av_dict_set(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2218 c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2220 for(i=0;i<c->stream->nb_streams;i++) {
2222 c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2223 /* if file or feed, then just take streams from FFStream struct */
2224 if (!c->stream->feed ||
2225 c->stream->feed == c->stream)
2226 src = c->stream->streams[i];
2228 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2230 *(c->fmt_ctx.streams[i]) = *src;
2231 c->fmt_ctx.streams[i]->priv_data = 0;
2232 c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2233 AVStream, not in codec */
2235 /* set output format parameters */
2236 c->fmt_ctx.oformat = c->stream->fmt;
2237 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2239 c->got_key_frame = 0;
2241 /* prepare header and save header data in a stream */
2242 if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2243 /* XXX: potential leak */
2246 c->fmt_ctx.pb->seekable = 0;
2249 * HACK to avoid mpeg ps muxer to spit many underflow errors
2250 * Default value from Libav
2251 * Try to set it use configuration option
2253 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2255 if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
2256 http_log("Error writing output header\n");
2259 av_dict_free(&c->fmt_ctx.metadata);
2261 len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2262 c->buffer_ptr = c->pb_buffer;
2263 c->buffer_end = c->pb_buffer + len;
2265 c->state = HTTPSTATE_SEND_DATA;
2266 c->last_packet_sent = 0;
2268 case HTTPSTATE_SEND_DATA:
2269 /* find a new packet */
2270 /* read a packet from the input stream */
2271 if (c->stream->feed)
2272 ffm_set_write_index(c->fmt_in,
2273 c->stream->feed->feed_write_index,
2274 c->stream->feed->feed_size);
2276 if (c->stream->max_time &&
2277 c->stream->max_time + c->start_time - cur_time < 0)
2278 /* We have timed out */
2279 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2283 ret = av_read_frame(c->fmt_in, &pkt);
2285 if (c->stream->feed) {
2286 /* if coming from feed, it means we reached the end of the
2287 ffm file, so must wait for more data */
2288 c->state = HTTPSTATE_WAIT_FEED;
2289 return 1; /* state changed */
2290 } else if (ret == AVERROR(EAGAIN)) {
2291 /* input not ready, come back later */
2294 if (c->stream->loop) {
2295 avformat_close_input(&c->fmt_in);
2296 if (open_input_stream(c, "") < 0)
2301 /* must send trailer now because eof or error */
2302 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2306 int source_index = pkt.stream_index;
2307 /* update first pts if needed */
2308 if (c->first_pts == AV_NOPTS_VALUE) {
2309 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2310 c->start_time = cur_time;
2312 /* send it to the appropriate stream */
2313 if (c->stream->feed) {
2314 /* if coming from a feed, select the right stream */
2315 if (c->switch_pending) {
2316 c->switch_pending = 0;
2317 for(i=0;i<c->stream->nb_streams;i++) {
2318 if (c->switch_feed_streams[i] == pkt.stream_index)
2319 if (pkt.flags & AV_PKT_FLAG_KEY)
2320 c->switch_feed_streams[i] = -1;
2321 if (c->switch_feed_streams[i] >= 0)
2322 c->switch_pending = 1;
2325 for(i=0;i<c->stream->nb_streams;i++) {
2326 if (c->stream->feed_streams[i] == pkt.stream_index) {
2327 AVStream *st = c->fmt_in->streams[source_index];
2328 pkt.stream_index = i;
2329 if (pkt.flags & AV_PKT_FLAG_KEY &&
2330 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2331 c->stream->nb_streams == 1))
2332 c->got_key_frame = 1;
2333 if (!c->stream->send_on_key || c->got_key_frame)
2338 AVCodecContext *codec;
2339 AVStream *ist, *ost;
2341 ist = c->fmt_in->streams[source_index];
2342 /* specific handling for RTP: we use several
2343 output stream (one for each RTP
2344 connection). XXX: need more abstract handling */
2345 if (c->is_packetized) {
2346 /* compute send time and duration */
2347 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2348 c->cur_pts -= c->first_pts;
2349 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2350 /* find RTP context */
2351 c->packet_stream_index = pkt.stream_index;
2352 ctx = c->rtp_ctx[c->packet_stream_index];
2354 av_free_packet(&pkt);
2357 codec = ctx->streams[0]->codec;
2358 /* only one stream per RTP connection */
2359 pkt.stream_index = 0;
2363 codec = ctx->streams[pkt.stream_index]->codec;
2366 if (c->is_packetized) {
2367 int max_packet_size;
2368 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2369 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2371 max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
2372 ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2374 ret = avio_open_dyn_buf(&ctx->pb);
2377 /* XXX: potential leak */
2380 ost = ctx->streams[pkt.stream_index];
2382 ctx->pb->seekable = 0;
2383 if (pkt.dts != AV_NOPTS_VALUE)
2384 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2385 if (pkt.pts != AV_NOPTS_VALUE)
2386 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2387 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2388 if (av_write_frame(ctx, &pkt) < 0) {
2389 http_log("Error writing frame to output\n");
2390 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2393 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2394 c->cur_frame_bytes = len;
2395 c->buffer_ptr = c->pb_buffer;
2396 c->buffer_end = c->pb_buffer + len;
2398 codec->frame_number++;
2400 av_free_packet(&pkt);
2404 av_free_packet(&pkt);
2409 case HTTPSTATE_SEND_DATA_TRAILER:
2410 /* last packet test ? */
2411 if (c->last_packet_sent || c->is_packetized)
2414 /* prepare header */
2415 if (avio_open_dyn_buf(&ctx->pb) < 0) {
2416 /* XXX: potential leak */
2419 c->fmt_ctx.pb->seekable = 0;
2420 av_write_trailer(ctx);
2421 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2422 c->buffer_ptr = c->pb_buffer;
2423 c->buffer_end = c->pb_buffer + len;
2425 c->last_packet_sent = 1;
2431 /* should convert the format at the same time */
2432 /* send data starting at c->buffer_ptr to the output connection
2433 (either UDP or TCP connection) */
2434 static int http_send_data(HTTPContext *c)
2439 if (c->buffer_ptr >= c->buffer_end) {
2440 ret = http_prepare_data(c);
2444 /* state change requested */
2447 if (c->is_packetized) {
2448 /* RTP data output */
2449 len = c->buffer_end - c->buffer_ptr;
2451 /* fail safe - should never happen */
2453 c->buffer_ptr = c->buffer_end;
2456 len = (c->buffer_ptr[0] << 24) |
2457 (c->buffer_ptr[1] << 16) |
2458 (c->buffer_ptr[2] << 8) |
2460 if (len > (c->buffer_end - c->buffer_ptr))
2462 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2463 /* nothing to send yet: we can wait */
2467 c->data_count += len;
2468 update_datarate(&c->datarate, c->data_count);
2470 c->stream->bytes_served += len;
2472 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2473 /* RTP packets are sent inside the RTSP TCP connection */
2475 int interleaved_index, size;
2477 HTTPContext *rtsp_c;
2480 /* if no RTSP connection left, error */
2483 /* if already sending something, then wait. */
2484 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2486 if (avio_open_dyn_buf(&pb) < 0)
2488 interleaved_index = c->packet_stream_index * 2;
2489 /* RTCP packets are sent at odd indexes */
2490 if (c->buffer_ptr[1] == 200)
2491 interleaved_index++;
2492 /* write RTSP TCP header */
2494 header[1] = interleaved_index;
2495 header[2] = len >> 8;
2497 avio_write(pb, header, 4);
2498 /* write RTP packet data */
2500 avio_write(pb, c->buffer_ptr, len);
2501 size = avio_close_dyn_buf(pb, &c->packet_buffer);
2502 /* prepare asynchronous TCP sending */
2503 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2504 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2505 c->buffer_ptr += len;
2507 /* send everything we can NOW */
2508 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2509 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2511 rtsp_c->packet_buffer_ptr += len;
2512 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2513 /* if we could not send all the data, we will
2514 send it later, so a new state is needed to
2515 "lock" the RTSP TCP connection */
2516 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2519 /* all data has been sent */
2520 av_freep(&c->packet_buffer);
2522 /* send RTP packet directly in UDP */
2524 ffurl_write(c->rtp_handles[c->packet_stream_index],
2525 c->buffer_ptr, len);
2526 c->buffer_ptr += len;
2527 /* here we continue as we can send several packets per 10 ms slot */
2530 /* TCP data output */
2531 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2533 if (ff_neterrno() != AVERROR(EAGAIN) &&
2534 ff_neterrno() != AVERROR(EINTR))
2535 /* error : close connection */
2540 c->buffer_ptr += len;
2542 c->data_count += len;
2543 update_datarate(&c->datarate, c->data_count);
2545 c->stream->bytes_served += len;
2553 static int http_start_receive_data(HTTPContext *c)
2557 if (c->stream->feed_opened)
2560 /* Don't permit writing to this one */
2561 if (c->stream->readonly)
2565 fd = open(c->stream->feed_filename, O_RDWR);
2567 http_log("Error opening feeder file: %s\n", strerror(errno));
2572 if (c->stream->truncate) {
2573 /* truncate feed file */
2574 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2575 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2576 if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
2577 http_log("Error truncating feed file: %s\n", strerror(errno));
2581 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2582 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2587 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2588 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2589 lseek(fd, 0, SEEK_SET);
2591 /* init buffer input */
2592 c->buffer_ptr = c->buffer;
2593 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2594 c->stream->feed_opened = 1;
2595 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2599 static int http_receive_data(HTTPContext *c)
2602 int len, loop_run = 0;
2604 while (c->chunked_encoding && !c->chunk_size &&
2605 c->buffer_end > c->buffer_ptr) {
2606 /* read chunk header, if present */
2607 len = recv(c->fd, c->buffer_ptr, 1, 0);
2610 if (ff_neterrno() != AVERROR(EAGAIN) &&
2611 ff_neterrno() != AVERROR(EINTR))
2612 /* error : close connection */
2615 } else if (len == 0) {
2616 /* end of connection : close it */
2618 } else if (c->buffer_ptr - c->buffer >= 2 &&
2619 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2620 c->chunk_size = strtol(c->buffer, 0, 16);
2621 if (c->chunk_size == 0) // end of stream
2623 c->buffer_ptr = c->buffer;
2625 } else if (++loop_run > 10) {
2626 /* no chunk header, abort */
2633 if (c->buffer_end > c->buffer_ptr) {
2634 len = recv(c->fd, c->buffer_ptr,
2635 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2637 if (ff_neterrno() != AVERROR(EAGAIN) &&
2638 ff_neterrno() != AVERROR(EINTR))
2639 /* error : close connection */
2641 } else if (len == 0)
2642 /* end of connection : close it */
2645 c->chunk_size -= len;
2646 c->buffer_ptr += len;
2647 c->data_count += len;
2648 update_datarate(&c->datarate, c->data_count);
2652 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2653 if (c->buffer[0] != 'f' ||
2654 c->buffer[1] != 'm') {
2655 http_log("Feed stream has become desynchronized -- disconnecting\n");
2660 if (c->buffer_ptr >= c->buffer_end) {
2661 FFStream *feed = c->stream;
2662 /* a packet has been received : write it in the store, except
2664 if (c->data_count > FFM_PACKET_SIZE) {
2666 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2667 /* XXX: use llseek or url_seek */
2668 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2669 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2670 http_log("Error writing to feed file: %s\n", strerror(errno));
2674 feed->feed_write_index += FFM_PACKET_SIZE;
2675 /* update file size */
2676 if (feed->feed_write_index > c->stream->feed_size)
2677 feed->feed_size = feed->feed_write_index;
2679 /* handle wrap around if max file size reached */
2680 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2681 feed->feed_write_index = FFM_PACKET_SIZE;
2684 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2685 http_log("Error writing index to feed file: %s\n", strerror(errno));
2689 /* wake up any waiting connections */
2690 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2691 if (c1->state == HTTPSTATE_WAIT_FEED &&
2692 c1->stream->feed == c->stream->feed)
2693 c1->state = HTTPSTATE_SEND_DATA;
2696 /* We have a header in our hands that contains useful data */
2697 AVFormatContext *s = avformat_alloc_context();
2699 AVInputFormat *fmt_in;
2705 /* use feed output format name to find corresponding input format */
2706 fmt_in = av_find_input_format(feed->fmt->name);
2710 pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2711 0, NULL, NULL, NULL, NULL);
2715 if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2720 /* Now we have the actual streams */
2721 if (s->nb_streams != feed->nb_streams) {
2722 avformat_close_input(&s);
2724 http_log("Feed '%s' stream number does not match registered feed\n",
2725 c->stream->feed_filename);
2729 for (i = 0; i < s->nb_streams; i++) {
2730 AVStream *fst = feed->streams[i];
2731 AVStream *st = s->streams[i];
2732 avcodec_copy_context(fst->codec, st->codec);
2735 avformat_close_input(&s);
2738 c->buffer_ptr = c->buffer;
2743 c->stream->feed_opened = 0;
2745 /* wake up any waiting connections to stop waiting for feed */
2746 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2747 if (c1->state == HTTPSTATE_WAIT_FEED &&
2748 c1->stream->feed == c->stream->feed)
2749 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2754 /********************************************************************/
2757 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2764 switch(error_number) {
2765 case RTSP_STATUS_OK:
2768 case RTSP_STATUS_METHOD:
2769 str = "Method Not Allowed";
2771 case RTSP_STATUS_BANDWIDTH:
2772 str = "Not Enough Bandwidth";
2774 case RTSP_STATUS_SESSION:
2775 str = "Session Not Found";
2777 case RTSP_STATUS_STATE:
2778 str = "Method Not Valid in This State";
2780 case RTSP_STATUS_AGGREGATE:
2781 str = "Aggregate operation not allowed";
2783 case RTSP_STATUS_ONLY_AGGREGATE:
2784 str = "Only aggregate operation allowed";
2786 case RTSP_STATUS_TRANSPORT:
2787 str = "Unsupported transport";
2789 case RTSP_STATUS_INTERNAL:
2790 str = "Internal Server Error";
2792 case RTSP_STATUS_SERVICE:
2793 str = "Service Unavailable";
2795 case RTSP_STATUS_VERSION:
2796 str = "RTSP Version not supported";
2799 str = "Unknown Error";
2803 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2804 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2806 /* output GMT time */
2809 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2810 avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2813 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2815 rtsp_reply_header(c, error_number);
2816 avio_printf(c->pb, "\r\n");
2819 static int rtsp_parse_request(HTTPContext *c)
2821 const char *p, *p1, *p2;
2827 RTSPMessageHeader header1 = { 0 }, *header = &header1;
2829 c->buffer_ptr[0] = '\0';
2832 get_word(cmd, sizeof(cmd), &p);
2833 get_word(url, sizeof(url), &p);
2834 get_word(protocol, sizeof(protocol), &p);
2836 av_strlcpy(c->method, cmd, sizeof(c->method));
2837 av_strlcpy(c->url, url, sizeof(c->url));
2838 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2840 if (avio_open_dyn_buf(&c->pb) < 0) {
2841 /* XXX: cannot do more */
2842 c->pb = NULL; /* safety */
2846 /* check version name */
2847 if (strcmp(protocol, "RTSP/1.0") != 0) {
2848 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2852 /* parse each header line */
2853 /* skip to next line */
2854 while (*p != '\n' && *p != '\0')
2858 while (*p != '\0') {
2859 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2863 if (p2 > p && p2[-1] == '\r')
2865 /* skip empty line */
2869 if (len > sizeof(line) - 1)
2870 len = sizeof(line) - 1;
2871 memcpy(line, p, len);
2873 ff_rtsp_parse_line(header, line, NULL, NULL);
2877 /* handle sequence number */
2878 c->seq = header->seq;
2880 if (!strcmp(cmd, "DESCRIBE"))
2881 rtsp_cmd_describe(c, url);
2882 else if (!strcmp(cmd, "OPTIONS"))
2883 rtsp_cmd_options(c, url);
2884 else if (!strcmp(cmd, "SETUP"))
2885 rtsp_cmd_setup(c, url, header);
2886 else if (!strcmp(cmd, "PLAY"))
2887 rtsp_cmd_play(c, url, header);
2888 else if (!strcmp(cmd, "PAUSE"))
2889 rtsp_cmd_pause(c, url, header);
2890 else if (!strcmp(cmd, "TEARDOWN"))
2891 rtsp_cmd_teardown(c, url, header);
2893 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2896 len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2897 c->pb = NULL; /* safety */
2899 /* XXX: cannot do more */
2902 c->buffer_ptr = c->pb_buffer;
2903 c->buffer_end = c->pb_buffer + len;
2904 c->state = RTSPSTATE_SEND_REPLY;
2908 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2909 struct in_addr my_ip)
2911 AVFormatContext *avc;
2912 AVStream *avs = NULL;
2915 avc = avformat_alloc_context();
2919 av_dict_set(&avc->metadata, "title",
2920 stream->title[0] ? stream->title : "No Title", 0);
2921 avc->nb_streams = stream->nb_streams;
2922 if (stream->is_multicast) {
2923 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2924 inet_ntoa(stream->multicast_ip),
2925 stream->multicast_port, stream->multicast_ttl);
2927 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2930 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2931 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2933 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2934 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2937 for(i = 0; i < stream->nb_streams; i++) {
2938 avc->streams[i] = &avs[i];
2939 avc->streams[i]->codec = stream->streams[i]->codec;
2941 *pbuffer = av_mallocz(2048);
2942 av_sdp_create(&avc, 1, *pbuffer, 2048);
2945 av_free(avc->streams);
2946 av_dict_free(&avc->metadata);
2950 return strlen(*pbuffer);
2953 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2955 // rtsp_reply_header(c, RTSP_STATUS_OK);
2956 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2957 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2958 avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2959 avio_printf(c->pb, "\r\n");
2962 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2968 int content_length, len;
2969 struct sockaddr_in my_addr;
2971 /* find which url is asked */
2972 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2977 for(stream = first_stream; stream != NULL; stream = stream->next) {
2978 if (!stream->is_feed &&
2979 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2980 !strcmp(path, stream->filename)) {
2984 /* no stream found */
2985 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2989 /* prepare the media description in sdp format */
2991 /* get the host IP */
2992 len = sizeof(my_addr);
2993 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2994 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2995 if (content_length < 0) {
2996 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2999 rtsp_reply_header(c, RTSP_STATUS_OK);
3000 avio_printf(c->pb, "Content-Base: %s/\r\n", url);
3001 avio_printf(c->pb, "Content-Type: application/sdp\r\n");
3002 avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
3003 avio_printf(c->pb, "\r\n");
3004 avio_write(c->pb, content, content_length);
3008 static HTTPContext *find_rtp_session(const char *session_id)
3012 if (session_id[0] == '\0')
3015 for(c = first_http_ctx; c != NULL; c = c->next) {
3016 if (!strcmp(c->session_id, session_id))
3022 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3024 RTSPTransportField *th;
3027 for(i=0;i<h->nb_transports;i++) {
3028 th = &h->transports[i];
3029 if (th->lower_transport == lower_transport)
3035 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3036 RTSPMessageHeader *h)
3039 int stream_index, rtp_port, rtcp_port;
3044 RTSPTransportField *th;
3045 struct sockaddr_in dest_addr;
3046 RTSPActionServerSetup setup;
3048 /* find which url is asked */
3049 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3054 /* now check each stream */
3055 for(stream = first_stream; stream != NULL; stream = stream->next) {
3056 if (!stream->is_feed &&
3057 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3058 /* accept aggregate filenames only if single stream */
3059 if (!strcmp(path, stream->filename)) {
3060 if (stream->nb_streams != 1) {
3061 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3068 for(stream_index = 0; stream_index < stream->nb_streams;
3070 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3071 stream->filename, stream_index);
3072 if (!strcmp(path, buf))
3077 /* no stream found */
3078 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3082 /* generate session id if needed */
3083 if (h->session_id[0] == '\0')
3084 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3085 av_lfg_get(&random_state), av_lfg_get(&random_state));
3087 /* find rtp session, and create it if none found */
3088 rtp_c = find_rtp_session(h->session_id);
3090 /* always prefer UDP */
3091 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3093 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3095 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3100 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3101 th->lower_transport);
3103 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3107 /* open input stream */
3108 if (open_input_stream(rtp_c, "") < 0) {
3109 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3114 /* test if stream is OK (test needed because several SETUP needs
3115 to be done for a given file) */
3116 if (rtp_c->stream != stream) {
3117 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3121 /* test if stream is already set up */
3122 if (rtp_c->rtp_ctx[stream_index]) {
3123 rtsp_reply_error(c, RTSP_STATUS_STATE);
3127 /* check transport */
3128 th = find_transport(h, rtp_c->rtp_protocol);
3129 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3130 th->client_port_min <= 0)) {
3131 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3135 /* setup default options */
3136 setup.transport_option[0] = '\0';
3137 dest_addr = rtp_c->from_addr;
3138 dest_addr.sin_port = htons(th->client_port_min);
3141 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3142 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3146 /* now everything is OK, so we can send the connection parameters */
3147 rtsp_reply_header(c, RTSP_STATUS_OK);
3149 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3151 switch(rtp_c->rtp_protocol) {
3152 case RTSP_LOWER_TRANSPORT_UDP:
3153 rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3154 rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3155 avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3156 "client_port=%d-%d;server_port=%d-%d",
3157 th->client_port_min, th->client_port_max,
3158 rtp_port, rtcp_port);
3160 case RTSP_LOWER_TRANSPORT_TCP:
3161 avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3162 stream_index * 2, stream_index * 2 + 1);
3167 if (setup.transport_option[0] != '\0')
3168 avio_printf(c->pb, ";%s", setup.transport_option);
3169 avio_printf(c->pb, "\r\n");
3172 avio_printf(c->pb, "\r\n");
3176 /* find an rtp connection by using the session ID. Check consistency
3178 static HTTPContext *find_rtp_session_with_url(const char *url,
3179 const char *session_id)
3187 rtp_c = find_rtp_session(session_id);
3191 /* find which url is asked */
3192 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3196 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3197 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3198 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3199 rtp_c->stream->filename, s);
3200 if(!strncmp(path, buf, sizeof(buf))) {
3201 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3206 if (len > 0 && path[len - 1] == '/' &&
3207 !strncmp(path, rtp_c->stream->filename, len - 1))
3212 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3216 rtp_c = find_rtp_session_with_url(url, h->session_id);
3218 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3222 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3223 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3224 rtp_c->state != HTTPSTATE_READY) {
3225 rtsp_reply_error(c, RTSP_STATUS_STATE);
3229 rtp_c->state = HTTPSTATE_SEND_DATA;
3231 /* now everything is OK, so we can send the connection parameters */
3232 rtsp_reply_header(c, RTSP_STATUS_OK);
3234 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3235 avio_printf(c->pb, "\r\n");
3238 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3242 rtp_c = find_rtp_session_with_url(url, h->session_id);
3244 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3248 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3249 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3250 rtsp_reply_error(c, RTSP_STATUS_STATE);
3254 rtp_c->state = HTTPSTATE_READY;
3255 rtp_c->first_pts = AV_NOPTS_VALUE;
3256 /* now everything is OK, so we can send the connection parameters */
3257 rtsp_reply_header(c, RTSP_STATUS_OK);
3259 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3260 avio_printf(c->pb, "\r\n");
3263 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3267 rtp_c = find_rtp_session_with_url(url, h->session_id);
3269 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3273 /* now everything is OK, so we can send the connection parameters */
3274 rtsp_reply_header(c, RTSP_STATUS_OK);
3276 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3277 avio_printf(c->pb, "\r\n");
3279 /* abort the session */
3280 close_connection(rtp_c);
3284 /********************************************************************/
3287 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3288 FFStream *stream, const char *session_id,
3289 enum RTSPLowerTransport rtp_protocol)
3291 HTTPContext *c = NULL;
3292 const char *proto_str;
3294 /* XXX: should output a warning page when coming
3295 close to the connection limit */
3296 if (nb_connections >= nb_max_connections)
3299 /* add a new connection */
3300 c = av_mallocz(sizeof(HTTPContext));
3305 c->poll_entry = NULL;
3306 c->from_addr = *from_addr;
3307 c->buffer_size = IOBUFFER_INIT_SIZE;
3308 c->buffer = av_malloc(c->buffer_size);
3313 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3314 c->state = HTTPSTATE_READY;
3315 c->is_packetized = 1;
3316 c->rtp_protocol = rtp_protocol;
3318 /* protocol is shown in statistics */
3319 switch(c->rtp_protocol) {
3320 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3321 proto_str = "MCAST";
3323 case RTSP_LOWER_TRANSPORT_UDP:
3326 case RTSP_LOWER_TRANSPORT_TCP:
3333 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3334 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3336 current_bandwidth += stream->bandwidth;
3338 c->next = first_http_ctx;
3350 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3351 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3353 static int rtp_new_av_stream(HTTPContext *c,
3354 int stream_index, struct sockaddr_in *dest_addr,
3355 HTTPContext *rtsp_c)
3357 AVFormatContext *ctx;
3360 URLContext *h = NULL;
3362 int max_packet_size;
3364 /* now we can open the relevant output stream */
3365 ctx = avformat_alloc_context();
3368 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3370 st = av_mallocz(sizeof(AVStream));
3373 ctx->nb_streams = 1;
3374 ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3377 ctx->streams[0] = st;
3379 if (!c->stream->feed ||
3380 c->stream->feed == c->stream)
3381 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3384 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3386 st->priv_data = NULL;
3388 /* build destination RTP address */
3389 ipaddr = inet_ntoa(dest_addr->sin_addr);
3391 switch(c->rtp_protocol) {
3392 case RTSP_LOWER_TRANSPORT_UDP:
3393 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3396 /* XXX: also pass as parameter to function ? */
3397 if (c->stream->is_multicast) {
3399 ttl = c->stream->multicast_ttl;
3402 snprintf(ctx->filename, sizeof(ctx->filename),
3403 "rtp://%s:%d?multicast=1&ttl=%d",
3404 ipaddr, ntohs(dest_addr->sin_port), ttl);
3406 snprintf(ctx->filename, sizeof(ctx->filename),
3407 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3410 if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3412 c->rtp_handles[stream_index] = h;
3413 max_packet_size = h->max_packet_size;
3415 case RTSP_LOWER_TRANSPORT_TCP:
3418 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3424 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3425 ipaddr, ntohs(dest_addr->sin_port),
3426 c->stream->filename, stream_index, c->protocol);
3428 /* normally, no packets should be output here, but the packet size may be checked */
3429 if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3430 /* XXX: close stream */
3433 if (avformat_write_header(ctx, NULL) < 0) {
3440 avio_close_dyn_buf(ctx->pb, &dummy_buf);
3443 c->rtp_ctx[stream_index] = ctx;
3447 /********************************************************************/
3448 /* avserver initialization */
3450 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3454 fst = av_mallocz(sizeof(AVStream));
3458 fst->codec = avcodec_alloc_context3(NULL);
3459 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3460 if (codec->extradata_size) {
3461 fst->codec->extradata = av_malloc(codec->extradata_size);
3462 memcpy(fst->codec->extradata, codec->extradata,
3463 codec->extradata_size);
3466 /* live streams must use the actual feed's codec since it may be
3467 * updated later to carry extradata needed by the streams.
3471 fst->priv_data = av_mallocz(sizeof(FeedData));
3472 fst->index = stream->nb_streams;
3473 avpriv_set_pts_info(fst, 33, 1, 90000);
3474 fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3475 stream->streams[stream->nb_streams++] = fst;
3479 /* return the stream number in the feed */
3480 static int add_av_stream(FFStream *feed, AVStream *st)
3483 AVCodecContext *av, *av1;
3487 for(i=0;i<feed->nb_streams;i++) {
3488 st = feed->streams[i];
3490 if (av1->codec_id == av->codec_id &&
3491 av1->codec_type == av->codec_type &&
3492 av1->bit_rate == av->bit_rate) {
3494 switch(av->codec_type) {
3495 case AVMEDIA_TYPE_AUDIO:
3496 if (av1->channels == av->channels &&
3497 av1->sample_rate == av->sample_rate)
3500 case AVMEDIA_TYPE_VIDEO:
3501 if (av1->width == av->width &&
3502 av1->height == av->height &&
3503 av1->time_base.den == av->time_base.den &&
3504 av1->time_base.num == av->time_base.num &&
3505 av1->gop_size == av->gop_size)
3514 fst = add_av_stream1(feed, av, 0);
3517 return feed->nb_streams - 1;
3520 static void remove_stream(FFStream *stream)
3524 while (*ps != NULL) {
3532 /* specific mpeg4 handling : we extract the raw parameters */
3533 static void extract_mpeg4_header(AVFormatContext *infile)
3535 int mpeg4_count, i, size;
3541 for(i=0;i<infile->nb_streams;i++) {
3542 st = infile->streams[i];
3543 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3544 st->codec->extradata_size == 0) {
3551 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3552 while (mpeg4_count > 0) {
3553 if (av_read_packet(infile, &pkt) < 0)
3555 st = infile->streams[pkt.stream_index];
3556 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3557 st->codec->extradata_size == 0) {
3558 av_freep(&st->codec->extradata);
3559 /* fill extradata with the header */
3560 /* XXX: we make hard suppositions here ! */
3562 while (p < pkt.data + pkt.size - 4) {
3563 /* stop when vop header is found */
3564 if (p[0] == 0x00 && p[1] == 0x00 &&
3565 p[2] == 0x01 && p[3] == 0xb6) {
3566 size = p - pkt.data;
3567 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3568 st->codec->extradata = av_malloc(size);
3569 st->codec->extradata_size = size;
3570 memcpy(st->codec->extradata, pkt.data, size);
3577 av_free_packet(&pkt);
3581 /* compute the needed AVStream for each file */
3582 static void build_file_streams(void)
3584 FFStream *stream, *stream_next;
3587 /* gather all streams */
3588 for(stream = first_stream; stream != NULL; stream = stream_next) {
3589 AVFormatContext *infile = NULL;
3590 stream_next = stream->next;
3591 if (stream->stream_type == STREAM_TYPE_LIVE &&
3593 /* the stream comes from a file */
3594 /* try to open the file */
3596 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3597 /* specific case : if transport stream output to RTP,
3598 we use a raw transport stream reader */
3599 av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3602 http_log("Opening file '%s'\n", stream->feed_filename);
3603 if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3604 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3605 /* remove stream (no need to spend more time on it) */
3607 remove_stream(stream);
3609 /* find all the AVStreams inside and reference them in
3611 if (avformat_find_stream_info(infile, NULL) < 0) {
3612 http_log("Could not find codec parameters from '%s'\n",
3613 stream->feed_filename);
3614 avformat_close_input(&infile);
3617 extract_mpeg4_header(infile);
3619 for(i=0;i<infile->nb_streams;i++)
3620 add_av_stream1(stream, infile->streams[i]->codec, 1);
3622 avformat_close_input(&infile);
3628 /* compute the needed AVStream for each feed */
3629 static void build_feed_streams(void)
3631 FFStream *stream, *feed;
3634 /* gather all streams */
3635 for(stream = first_stream; stream != NULL; stream = stream->next) {
3636 feed = stream->feed;
3638 if (stream->is_feed) {
3639 for(i=0;i<stream->nb_streams;i++)
3640 stream->feed_streams[i] = i;
3642 /* we handle a stream coming from a feed */
3643 for(i=0;i<stream->nb_streams;i++)
3644 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3649 /* create feed files if needed */
3650 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3653 if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3654 /* See if it matches */
3655 AVFormatContext *s = NULL;
3658 if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3659 /* Now see if it matches */
3660 if (s->nb_streams == feed->nb_streams) {
3662 for(i=0;i<s->nb_streams;i++) {
3664 sf = feed->streams[i];
3667 if (sf->index != ss->index ||
3669 http_log("Index & Id do not match for stream %d (%s)\n",
3670 i, feed->feed_filename);
3673 AVCodecContext *ccf, *ccs;
3677 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3679 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3680 http_log("Codecs do not match for stream %d\n", i);
3682 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3683 http_log("Codec bitrates do not match for stream %d\n", i);
3685 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3686 if (CHECK_CODEC(time_base.den) ||
3687 CHECK_CODEC(time_base.num) ||
3688 CHECK_CODEC(width) ||
3689 CHECK_CODEC(height)) {
3690 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3693 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3694 if (CHECK_CODEC(sample_rate) ||
3695 CHECK_CODEC(channels) ||
3696 CHECK_CODEC(frame_size)) {
3697 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3701 http_log("Unknown codec type\n");
3709 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3710 feed->feed_filename, s->nb_streams, feed->nb_streams);
3712 avformat_close_input(&s);
3714 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3715 feed->feed_filename);
3718 if (feed->readonly) {
3719 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3720 feed->feed_filename);
3723 unlink(feed->feed_filename);
3726 if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3727 AVFormatContext s1 = {0}, *s = &s1;
3729 if (feed->readonly) {
3730 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3731 feed->feed_filename);
3735 /* only write the header of the ffm file */
3736 if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3737 http_log("Could not open output feed file '%s'\n",
3738 feed->feed_filename);
3741 s->oformat = feed->fmt;
3742 s->nb_streams = feed->nb_streams;
3743 s->streams = feed->streams;
3744 if (avformat_write_header(s, NULL) < 0) {
3745 http_log("Container doesn't supports the required parameters\n");
3748 /* XXX: need better api */
3749 av_freep(&s->priv_data);
3752 /* get feed size and write index */
3753 fd = open(feed->feed_filename, O_RDONLY);
3755 http_log("Could not open output feed file '%s'\n",
3756 feed->feed_filename);
3760 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3761 feed->feed_size = lseek(fd, 0, SEEK_END);
3762 /* ensure that we do not wrap before the end of file */
3763 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3764 feed->feed_max_size = feed->feed_size;
3770 /* compute the bandwidth used by each stream */
3771 static void compute_bandwidth(void)
3777 for(stream = first_stream; stream != NULL; stream = stream->next) {
3779 for(i=0;i<stream->nb_streams;i++) {
3780 AVStream *st = stream->streams[i];
3781 switch(st->codec->codec_type) {
3782 case AVMEDIA_TYPE_AUDIO:
3783 case AVMEDIA_TYPE_VIDEO:
3784 bandwidth += st->codec->bit_rate;
3790 stream->bandwidth = (bandwidth + 999) / 1000;
3794 /* add a codec and set the default parameters */
3795 static void add_codec(FFStream *stream, AVCodecContext *av)
3799 /* compute default parameters */
3800 switch(av->codec_type) {
3801 case AVMEDIA_TYPE_AUDIO:
3802 if (av->bit_rate == 0)
3803 av->bit_rate = 64000;
3804 if (av->sample_rate == 0)
3805 av->sample_rate = 22050;
3806 if (av->channels == 0)
3809 case AVMEDIA_TYPE_VIDEO:
3810 if (av->bit_rate == 0)
3811 av->bit_rate = 64000;
3812 if (av->time_base.num == 0){
3813 av->time_base.den = 5;
3814 av->time_base.num = 1;
3816 if (av->width == 0 || av->height == 0) {
3820 /* Bitrate tolerance is less for streaming */
3821 if (av->bit_rate_tolerance == 0)
3822 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3823 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3828 if (av->max_qdiff == 0)
3830 av->qcompress = 0.5;
3833 if (!av->nsse_weight)
3834 av->nsse_weight = 8;
3836 av->frame_skip_cmp = FF_CMP_DCTMAX;
3838 av->me_method = ME_EPZS;
3839 av->rc_buffer_aggressivity = 1.0;
3842 av->rc_eq = "tex^qComp";
3843 if (!av->i_quant_factor)
3844 av->i_quant_factor = -0.8;
3845 if (!av->b_quant_factor)
3846 av->b_quant_factor = 1.25;
3847 if (!av->b_quant_offset)
3848 av->b_quant_offset = 1.25;
3849 if (!av->rc_max_rate)
3850 av->rc_max_rate = av->bit_rate * 2;
3852 if (av->rc_max_rate && !av->rc_buffer_size) {
3853 av->rc_buffer_size = av->rc_max_rate;
3862 st = av_mallocz(sizeof(AVStream));
3865 st->codec = avcodec_alloc_context3(NULL);
3866 stream->streams[stream->nb_streams++] = st;
3867 memcpy(st->codec, av, sizeof(AVCodecContext));
3870 static enum CodecID opt_audio_codec(const char *arg)
3872 AVCodec *p= avcodec_find_encoder_by_name(arg);
3874 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3875 return CODEC_ID_NONE;
3880 static enum CodecID opt_video_codec(const char *arg)
3882 AVCodec *p= avcodec_find_encoder_by_name(arg);
3884 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3885 return CODEC_ID_NONE;
3890 /* simplistic plugin support */
3893 static void load_module(const char *filename)
3896 void (*init_func)(void);
3897 dll = dlopen(filename, RTLD_NOW);
3899 fprintf(stderr, "Could not load module '%s' - %s\n",
3900 filename, dlerror());
3904 init_func = dlsym(dll, "avserver_module_init");
3907 "%s: init function 'avserver_module_init()' not found\n",
3916 static int avserver_opt_default(const char *opt, const char *arg,
3917 AVCodecContext *avctx, int type)
3920 const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3922 ret = av_opt_set(avctx, opt, arg, 0);
3926 static int avserver_opt_preset(const char *arg,
3927 AVCodecContext *avctx, int type,
3928 enum CodecID *audio_id, enum CodecID *video_id)
3931 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3933 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3935 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3936 codec ? codec->name : NULL))) {
3937 fprintf(stderr, "File for preset '%s' not found\n", arg);
3942 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3943 if(line[0] == '#' && !e)
3945 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3947 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3951 if(!strcmp(tmp, "acodec")){
3952 *audio_id = opt_audio_codec(tmp2);
3953 }else if(!strcmp(tmp, "vcodec")){
3954 *video_id = opt_video_codec(tmp2);
3955 }else if(!strcmp(tmp, "scodec")){
3956 /* opt_subtitle_codec(tmp2); */
3957 }else if(avserver_opt_default(tmp, tmp2, avctx, type) < 0){
3958 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
3969 static AVOutputFormat *avserver_guess_format(const char *short_name, const char *filename,
3970 const char *mime_type)
3972 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3975 AVOutputFormat *stream_fmt;
3976 char stream_format_name[64];
3978 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
3979 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
3988 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
3992 fprintf(stderr, "%s:%d: ", filename, line_num);
3993 vfprintf(stderr, fmt, vl);
3999 static int parse_ffconfig(const char *filename)
4006 int val, errors, line_num;
4007 FFStream **last_stream, *stream, *redirect;
4008 FFStream **last_feed, *feed, *s;
4009 AVCodecContext audio_enc, video_enc;
4010 enum CodecID audio_id, video_id;
4012 f = fopen(filename, "r");
4020 first_stream = NULL;
4021 last_stream = &first_stream;
4023 last_feed = &first_feed;
4027 audio_id = CODEC_ID_NONE;
4028 video_id = CODEC_ID_NONE;
4030 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4032 if (fgets(line, sizeof(line), f) == NULL)
4038 if (*p == '\0' || *p == '#')
4041 get_arg(cmd, sizeof(cmd), &p);
4043 if (!av_strcasecmp(cmd, "Port")) {
4044 get_arg(arg, sizeof(arg), &p);
4046 if (val < 1 || val > 65536) {
4047 ERROR("Invalid_port: %s\n", arg);
4049 my_http_addr.sin_port = htons(val);
4050 } else if (!av_strcasecmp(cmd, "BindAddress")) {
4051 get_arg(arg, sizeof(arg), &p);
4052 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4053 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4055 } else if (!av_strcasecmp(cmd, "NoDaemon")) {
4056 avserver_daemon = 0;
4057 } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4058 get_arg(arg, sizeof(arg), &p);
4060 if (val < 1 || val > 65536) {
4061 ERROR("%s:%d: Invalid port: %s\n", arg);
4063 my_rtsp_addr.sin_port = htons(atoi(arg));
4064 } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4065 get_arg(arg, sizeof(arg), &p);
4066 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4067 ERROR("Invalid host/IP address: %s\n", arg);
4069 } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4070 get_arg(arg, sizeof(arg), &p);
4072 if (val < 1 || val > 65536) {
4073 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4075 nb_max_http_connections = val;
4076 } else if (!av_strcasecmp(cmd, "MaxClients")) {
4077 get_arg(arg, sizeof(arg), &p);
4079 if (val < 1 || val > nb_max_http_connections) {
4080 ERROR("Invalid MaxClients: %s\n", arg);
4082 nb_max_connections = val;
4084 } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4086 get_arg(arg, sizeof(arg), &p);
4088 if (llval < 10 || llval > 10000000) {
4089 ERROR("Invalid MaxBandwidth: %s\n", arg);
4091 max_bandwidth = llval;
4092 } else if (!av_strcasecmp(cmd, "CustomLog")) {
4093 if (!avserver_debug)
4094 get_arg(logfilename, sizeof(logfilename), &p);
4095 } else if (!av_strcasecmp(cmd, "<Feed")) {
4096 /*********************************************/
4097 /* Feed related options */
4099 if (stream || feed) {
4100 ERROR("Already in a tag\n");
4102 feed = av_mallocz(sizeof(FFStream));
4103 get_arg(feed->filename, sizeof(feed->filename), &p);
4104 q = strrchr(feed->filename, '>');
4108 for (s = first_feed; s; s = s->next) {
4109 if (!strcmp(feed->filename, s->filename)) {
4110 ERROR("Feed '%s' already registered\n", s->filename);
4114 feed->fmt = av_guess_format("ffm", NULL, NULL);
4115 /* defaut feed file */
4116 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4117 "/tmp/%s.ffm", feed->filename);
4118 feed->feed_max_size = 5 * 1024 * 1024;
4120 feed->feed = feed; /* self feeding :-) */
4122 /* add in stream list */
4123 *last_stream = feed;
4124 last_stream = &feed->next;
4125 /* add in feed list */
4127 last_feed = &feed->next_feed;
4129 } else if (!av_strcasecmp(cmd, "Launch")) {
4133 feed->child_argv = av_mallocz(64 * sizeof(char *));
4135 for (i = 0; i < 62; i++) {
4136 get_arg(arg, sizeof(arg), &p);
4140 feed->child_argv[i] = av_strdup(arg);
4143 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4145 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4147 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4148 inet_ntoa(my_http_addr.sin_addr),
4149 ntohs(my_http_addr.sin_port), feed->filename);
4151 } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4153 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4155 } else if (stream) {
4156 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4158 } else if (!av_strcasecmp(cmd, "File")) {
4160 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4162 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4163 } else if (!av_strcasecmp(cmd, "Truncate")) {
4165 get_arg(arg, sizeof(arg), &p);
4166 feed->truncate = strtod(arg, NULL);
4168 } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4173 get_arg(arg, sizeof(arg), &p);
4175 fsize = strtod(p1, &p1);
4176 switch(toupper(*p1)) {
4181 fsize *= 1024 * 1024;
4184 fsize *= 1024 * 1024 * 1024;
4187 feed->feed_max_size = (int64_t)fsize;
4188 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4189 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4192 } else if (!av_strcasecmp(cmd, "</Feed>")) {
4194 ERROR("No corresponding <Feed> for </Feed>\n");
4197 } else if (!av_strcasecmp(cmd, "<Stream")) {
4198 /*********************************************/
4199 /* Stream related options */
4201 if (stream || feed) {
4202 ERROR("Already in a tag\n");
4205 stream = av_mallocz(sizeof(FFStream));
4206 get_arg(stream->filename, sizeof(stream->filename), &p);
4207 q = strrchr(stream->filename, '>');
4211 for (s = first_stream; s; s = s->next) {
4212 if (!strcmp(stream->filename, s->filename)) {
4213 ERROR("Stream '%s' already registered\n", s->filename);
4217 stream->fmt = avserver_guess_format(NULL, stream->filename, NULL);
4218 avcodec_get_context_defaults3(&video_enc, NULL);
4219 avcodec_get_context_defaults3(&audio_enc, NULL);
4220 audio_id = CODEC_ID_NONE;
4221 video_id = CODEC_ID_NONE;
4223 audio_id = stream->fmt->audio_codec;
4224 video_id = stream->fmt->video_codec;
4227 *last_stream = stream;
4228 last_stream = &stream->next;
4230 } else if (!av_strcasecmp(cmd, "Feed")) {
4231 get_arg(arg, sizeof(arg), &p);
4236 while (sfeed != NULL) {
4237 if (!strcmp(sfeed->filename, arg))
4239 sfeed = sfeed->next_feed;
4242 ERROR("feed '%s' not defined\n", arg);
4244 stream->feed = sfeed;
4246 } else if (!av_strcasecmp(cmd, "Format")) {
4247 get_arg(arg, sizeof(arg), &p);
4249 if (!strcmp(arg, "status")) {
4250 stream->stream_type = STREAM_TYPE_STATUS;
4253 stream->stream_type = STREAM_TYPE_LIVE;
4254 /* jpeg cannot be used here, so use single frame jpeg */
4255 if (!strcmp(arg, "jpeg"))
4256 strcpy(arg, "mjpeg");
4257 stream->fmt = avserver_guess_format(arg, NULL, NULL);
4259 ERROR("Unknown Format: %s\n", arg);
4263 audio_id = stream->fmt->audio_codec;
4264 video_id = stream->fmt->video_codec;
4267 } else if (!av_strcasecmp(cmd, "InputFormat")) {
4268 get_arg(arg, sizeof(arg), &p);
4270 stream->ifmt = av_find_input_format(arg);
4271 if (!stream->ifmt) {
4272 ERROR("Unknown input format: %s\n", arg);
4275 } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4276 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4277 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4279 ERROR("FaviconURL only permitted for status streams\n");
4281 } else if (!av_strcasecmp(cmd, "Author")) {
4283 get_arg(stream->author, sizeof(stream->author), &p);
4284 } else if (!av_strcasecmp(cmd, "Comment")) {
4286 get_arg(stream->comment, sizeof(stream->comment), &p);
4287 } else if (!av_strcasecmp(cmd, "Copyright")) {
4289 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4290 } else if (!av_strcasecmp(cmd, "Title")) {
4292 get_arg(stream->title, sizeof(stream->title), &p);
4293 } else if (!av_strcasecmp(cmd, "Preroll")) {
4294 get_arg(arg, sizeof(arg), &p);
4296 stream->prebuffer = atof(arg) * 1000;
4297 } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4299 stream->send_on_key = 1;
4300 } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4301 get_arg(arg, sizeof(arg), &p);
4302 audio_id = opt_audio_codec(arg);
4303 if (audio_id == CODEC_ID_NONE) {
4304 ERROR("Unknown AudioCodec: %s\n", arg);
4306 } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4307 get_arg(arg, sizeof(arg), &p);
4308 video_id = opt_video_codec(arg);
4309 if (video_id == CODEC_ID_NONE) {
4310 ERROR("Unknown VideoCodec: %s\n", arg);
4312 } else if (!av_strcasecmp(cmd, "MaxTime")) {
4313 get_arg(arg, sizeof(arg), &p);
4315 stream->max_time = atof(arg) * 1000;
4316 } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4317 get_arg(arg, sizeof(arg), &p);
4319 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4320 } else if (!av_strcasecmp(cmd, "AudioChannels")) {
4321 get_arg(arg, sizeof(arg), &p);
4323 audio_enc.channels = atoi(arg);
4324 } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4325 get_arg(arg, sizeof(arg), &p);
4327 audio_enc.sample_rate = atoi(arg);
4328 } else if (!av_strcasecmp(cmd, "AudioQuality")) {
4329 get_arg(arg, sizeof(arg), &p);
4331 // audio_enc.quality = atof(arg) * 1000;
4333 } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4335 int minrate, maxrate;
4337 get_arg(arg, sizeof(arg), &p);
4339 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4340 video_enc.rc_min_rate = minrate * 1000;
4341 video_enc.rc_max_rate = maxrate * 1000;
4343 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4346 } else if (!av_strcasecmp(cmd, "Debug")) {
4348 get_arg(arg, sizeof(arg), &p);
4349 video_enc.debug = strtol(arg,0,0);
4351 } else if (!av_strcasecmp(cmd, "Strict")) {
4353 get_arg(arg, sizeof(arg), &p);
4354 video_enc.strict_std_compliance = atoi(arg);
4356 } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4358 get_arg(arg, sizeof(arg), &p);
4359 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4361 } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4363 get_arg(arg, sizeof(arg), &p);
4364 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4366 } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4367 get_arg(arg, sizeof(arg), &p);
4369 video_enc.bit_rate = atoi(arg) * 1000;
4371 } else if (!av_strcasecmp(cmd, "VideoSize")) {
4372 get_arg(arg, sizeof(arg), &p);
4374 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4375 if ((video_enc.width % 16) != 0 ||
4376 (video_enc.height % 16) != 0) {
4377 ERROR("Image size must be a multiple of 16\n");
4380 } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4381 get_arg(arg, sizeof(arg), &p);
4383 AVRational frame_rate;
4384 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4385 ERROR("Incorrect frame rate: %s\n", arg);
4387 video_enc.time_base.num = frame_rate.den;
4388 video_enc.time_base.den = frame_rate.num;
4391 } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4392 get_arg(arg, sizeof(arg), &p);
4394 video_enc.gop_size = atoi(arg);
4395 } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4397 video_enc.gop_size = 1;
4398 } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4400 video_enc.mb_decision = FF_MB_DECISION_BITS;
4401 } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4403 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4404 video_enc.flags |= CODEC_FLAG_4MV;
4406 } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4407 !av_strcasecmp(cmd, "AVOptionAudio")) {
4409 AVCodecContext *avctx;
4411 get_arg(arg, sizeof(arg), &p);
4412 get_arg(arg2, sizeof(arg2), &p);
4413 if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4415 type = AV_OPT_FLAG_VIDEO_PARAM;
4418 type = AV_OPT_FLAG_AUDIO_PARAM;
4420 if (avserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4421 ERROR("AVOption error: %s %s\n", arg, arg2);
4423 } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4424 !av_strcasecmp(cmd, "AVPresetAudio")) {
4425 AVCodecContext *avctx;
4427 get_arg(arg, sizeof(arg), &p);
4428 if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4430 video_enc.codec_id = video_id;
4431 type = AV_OPT_FLAG_VIDEO_PARAM;
4434 audio_enc.codec_id = audio_id;
4435 type = AV_OPT_FLAG_AUDIO_PARAM;
4437 if (avserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4438 ERROR("AVPreset error: %s\n", arg);
4440 } else if (!av_strcasecmp(cmd, "VideoTag")) {
4441 get_arg(arg, sizeof(arg), &p);
4442 if ((strlen(arg) == 4) && stream)
4443 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4444 } else if (!av_strcasecmp(cmd, "BitExact")) {
4446 video_enc.flags |= CODEC_FLAG_BITEXACT;
4447 } else if (!av_strcasecmp(cmd, "DctFastint")) {
4449 video_enc.dct_algo = FF_DCT_FASTINT;
4450 } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4452 video_enc.idct_algo = FF_IDCT_SIMPLE;
4453 } else if (!av_strcasecmp(cmd, "Qscale")) {
4454 get_arg(arg, sizeof(arg), &p);
4456 video_enc.flags |= CODEC_FLAG_QSCALE;
4457 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4459 } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4460 get_arg(arg, sizeof(arg), &p);
4462 video_enc.max_qdiff = atoi(arg);
4463 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4464 ERROR("VideoQDiff out of range\n");
4467 } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4468 get_arg(arg, sizeof(arg), &p);
4470 video_enc.qmax = atoi(arg);
4471 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4472 ERROR("VideoQMax out of range\n");
4475 } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4476 get_arg(arg, sizeof(arg), &p);
4478 video_enc.qmin = atoi(arg);
4479 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4480 ERROR("VideoQMin out of range\n");
4483 } else if (!av_strcasecmp(cmd, "LumaElim")) {
4484 get_arg(arg, sizeof(arg), &p);
4486 video_enc.luma_elim_threshold = atoi(arg);
4487 } else if (!av_strcasecmp(cmd, "ChromaElim")) {
4488 get_arg(arg, sizeof(arg), &p);
4490 video_enc.chroma_elim_threshold = atoi(arg);
4491 } else if (!av_strcasecmp(cmd, "LumiMask")) {
4492 get_arg(arg, sizeof(arg), &p);
4494 video_enc.lumi_masking = atof(arg);
4495 } else if (!av_strcasecmp(cmd, "DarkMask")) {
4496 get_arg(arg, sizeof(arg), &p);
4498 video_enc.dark_masking = atof(arg);
4499 } else if (!av_strcasecmp(cmd, "NoVideo")) {
4500 video_id = CODEC_ID_NONE;
4501 } else if (!av_strcasecmp(cmd, "NoAudio")) {
4502 audio_id = CODEC_ID_NONE;
4503 } else if (!av_strcasecmp(cmd, "ACL")) {
4504 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4505 } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4507 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4509 } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4510 get_arg(arg, sizeof(arg), &p);
4512 av_freep(&stream->rtsp_option);
4513 stream->rtsp_option = av_strdup(arg);
4515 } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4516 get_arg(arg, sizeof(arg), &p);
4518 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4519 ERROR("Invalid host/IP address: %s\n", arg);
4521 stream->is_multicast = 1;
4522 stream->loop = 1; /* default is looping */
4524 } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4525 get_arg(arg, sizeof(arg), &p);
4527 stream->multicast_port = atoi(arg);
4528 } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4529 get_arg(arg, sizeof(arg), &p);
4531 stream->multicast_ttl = atoi(arg);
4532 } else if (!av_strcasecmp(cmd, "NoLoop")) {
4535 } else if (!av_strcasecmp(cmd, "</Stream>")) {
4537 ERROR("No corresponding <Stream> for </Stream>\n");
4539 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4540 if (audio_id != CODEC_ID_NONE) {
4541 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4542 audio_enc.codec_id = audio_id;
4543 add_codec(stream, &audio_enc);
4545 if (video_id != CODEC_ID_NONE) {
4546 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4547 video_enc.codec_id = video_id;
4548 add_codec(stream, &video_enc);
4553 } else if (!av_strcasecmp(cmd, "<Redirect")) {
4554 /*********************************************/
4556 if (stream || feed || redirect) {
4557 ERROR("Already in a tag\n");
4559 redirect = av_mallocz(sizeof(FFStream));
4560 *last_stream = redirect;
4561 last_stream = &redirect->next;
4563 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4564 q = strrchr(redirect->filename, '>');
4567 redirect->stream_type = STREAM_TYPE_REDIRECT;
4569 } else if (!av_strcasecmp(cmd, "URL")) {
4571 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4572 } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4574 ERROR("No corresponding <Redirect> for </Redirect>\n");
4576 if (!redirect->feed_filename[0]) {
4577 ERROR("No URL found for <Redirect>\n");
4581 } else if (!av_strcasecmp(cmd, "LoadModule")) {
4582 get_arg(arg, sizeof(arg), &p);
4586 ERROR("Module support not compiled into this version: '%s'\n", arg);
4589 ERROR("Incorrect keyword: '%s'\n", cmd);
4601 static void handle_child_exit(int sig)
4606 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4609 for (feed = first_feed; feed; feed = feed->next) {
4610 if (feed->pid == pid) {
4611 int uptime = time(0) - feed->pid_start;
4614 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4617 /* Turn off any more restarts */
4618 feed->child_argv = 0;
4623 need_to_start_children = 1;
4626 static void opt_debug(void)
4629 avserver_daemon = 0;
4630 logfilename[0] = '-';
4633 static void show_help(void)
4635 printf("usage: avserver [options]\n"
4636 "Hyper fast multi format Audio/Video streaming server\n");
4638 show_help_options(options, "Main options:\n", 0, 0);
4641 static const OptionDef options[] = {
4642 #include "cmdutils_common_opts.h"
4643 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4644 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4645 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/avserver.conf", "configfile" },
4649 int main(int argc, char **argv)
4651 struct sigaction sigact = { { 0 } };
4653 parse_loglevel(argc, argv, options);
4655 avformat_network_init();
4659 my_program_name = argv[0];
4660 my_program_dir = getcwd(0, 0);
4661 avserver_daemon = 1;
4663 parse_options(NULL, argc, argv, options, NULL);
4665 unsetenv("http_proxy"); /* Kill the http_proxy */
4667 av_lfg_init(&random_state, av_get_random_seed());
4669 sigact.sa_handler = handle_child_exit;
4670 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4671 sigaction(SIGCHLD, &sigact, 0);
4673 if (parse_ffconfig(config_filename) < 0) {
4674 fprintf(stderr, "Incorrect config file - exiting.\n");
4678 /* open log file if needed */
4679 if (logfilename[0] != '\0') {
4680 if (!strcmp(logfilename, "-"))
4683 logfile = fopen(logfilename, "a");
4684 av_log_set_callback(http_av_log);
4687 build_file_streams();
4689 build_feed_streams();
4691 compute_bandwidth();
4693 /* put the process in background and detach it from its TTY */
4694 if (avserver_daemon) {
4701 } else if (pid > 0) {
4708 open("/dev/null", O_RDWR);
4709 if (strcmp(logfilename, "-") != 0) {
4719 signal(SIGPIPE, SIG_IGN);
4721 if (avserver_daemon)
4724 if (http_server() < 0) {
4725 http_log("Could not start server\n");