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 ftruncate(c->feed_fd, FFM_PACKET_SIZE);
2576 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2578 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2579 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2584 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2585 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2586 lseek(fd, 0, SEEK_SET);
2588 /* init buffer input */
2589 c->buffer_ptr = c->buffer;
2590 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2591 c->stream->feed_opened = 1;
2592 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2596 static int http_receive_data(HTTPContext *c)
2599 int len, loop_run = 0;
2601 while (c->chunked_encoding && !c->chunk_size &&
2602 c->buffer_end > c->buffer_ptr) {
2603 /* read chunk header, if present */
2604 len = recv(c->fd, c->buffer_ptr, 1, 0);
2607 if (ff_neterrno() != AVERROR(EAGAIN) &&
2608 ff_neterrno() != AVERROR(EINTR))
2609 /* error : close connection */
2612 } else if (len == 0) {
2613 /* end of connection : close it */
2615 } else if (c->buffer_ptr - c->buffer >= 2 &&
2616 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2617 c->chunk_size = strtol(c->buffer, 0, 16);
2618 if (c->chunk_size == 0) // end of stream
2620 c->buffer_ptr = c->buffer;
2622 } else if (++loop_run > 10) {
2623 /* no chunk header, abort */
2630 if (c->buffer_end > c->buffer_ptr) {
2631 len = recv(c->fd, c->buffer_ptr,
2632 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2634 if (ff_neterrno() != AVERROR(EAGAIN) &&
2635 ff_neterrno() != AVERROR(EINTR))
2636 /* error : close connection */
2638 } else if (len == 0)
2639 /* end of connection : close it */
2642 c->chunk_size -= len;
2643 c->buffer_ptr += len;
2644 c->data_count += len;
2645 update_datarate(&c->datarate, c->data_count);
2649 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2650 if (c->buffer[0] != 'f' ||
2651 c->buffer[1] != 'm') {
2652 http_log("Feed stream has become desynchronized -- disconnecting\n");
2657 if (c->buffer_ptr >= c->buffer_end) {
2658 FFStream *feed = c->stream;
2659 /* a packet has been received : write it in the store, except
2661 if (c->data_count > FFM_PACKET_SIZE) {
2663 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2664 /* XXX: use llseek or url_seek */
2665 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2666 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2667 http_log("Error writing to feed file: %s\n", strerror(errno));
2671 feed->feed_write_index += FFM_PACKET_SIZE;
2672 /* update file size */
2673 if (feed->feed_write_index > c->stream->feed_size)
2674 feed->feed_size = feed->feed_write_index;
2676 /* handle wrap around if max file size reached */
2677 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2678 feed->feed_write_index = FFM_PACKET_SIZE;
2681 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2682 http_log("Error writing index to feed file: %s\n", strerror(errno));
2686 /* wake up any waiting connections */
2687 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2688 if (c1->state == HTTPSTATE_WAIT_FEED &&
2689 c1->stream->feed == c->stream->feed)
2690 c1->state = HTTPSTATE_SEND_DATA;
2693 /* We have a header in our hands that contains useful data */
2694 AVFormatContext *s = avformat_alloc_context();
2696 AVInputFormat *fmt_in;
2702 /* use feed output format name to find corresponding input format */
2703 fmt_in = av_find_input_format(feed->fmt->name);
2707 pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2708 0, NULL, NULL, NULL, NULL);
2712 if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2717 /* Now we have the actual streams */
2718 if (s->nb_streams != feed->nb_streams) {
2719 avformat_close_input(&s);
2721 http_log("Feed '%s' stream number does not match registered feed\n",
2722 c->stream->feed_filename);
2726 for (i = 0; i < s->nb_streams; i++) {
2727 AVStream *fst = feed->streams[i];
2728 AVStream *st = s->streams[i];
2729 avcodec_copy_context(fst->codec, st->codec);
2732 avformat_close_input(&s);
2735 c->buffer_ptr = c->buffer;
2740 c->stream->feed_opened = 0;
2742 /* wake up any waiting connections to stop waiting for feed */
2743 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2744 if (c1->state == HTTPSTATE_WAIT_FEED &&
2745 c1->stream->feed == c->stream->feed)
2746 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2751 /********************************************************************/
2754 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2761 switch(error_number) {
2762 case RTSP_STATUS_OK:
2765 case RTSP_STATUS_METHOD:
2766 str = "Method Not Allowed";
2768 case RTSP_STATUS_BANDWIDTH:
2769 str = "Not Enough Bandwidth";
2771 case RTSP_STATUS_SESSION:
2772 str = "Session Not Found";
2774 case RTSP_STATUS_STATE:
2775 str = "Method Not Valid in This State";
2777 case RTSP_STATUS_AGGREGATE:
2778 str = "Aggregate operation not allowed";
2780 case RTSP_STATUS_ONLY_AGGREGATE:
2781 str = "Only aggregate operation allowed";
2783 case RTSP_STATUS_TRANSPORT:
2784 str = "Unsupported transport";
2786 case RTSP_STATUS_INTERNAL:
2787 str = "Internal Server Error";
2789 case RTSP_STATUS_SERVICE:
2790 str = "Service Unavailable";
2792 case RTSP_STATUS_VERSION:
2793 str = "RTSP Version not supported";
2796 str = "Unknown Error";
2800 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2801 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2803 /* output GMT time */
2806 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2807 avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2810 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2812 rtsp_reply_header(c, error_number);
2813 avio_printf(c->pb, "\r\n");
2816 static int rtsp_parse_request(HTTPContext *c)
2818 const char *p, *p1, *p2;
2824 RTSPMessageHeader header1 = { 0 }, *header = &header1;
2826 c->buffer_ptr[0] = '\0';
2829 get_word(cmd, sizeof(cmd), &p);
2830 get_word(url, sizeof(url), &p);
2831 get_word(protocol, sizeof(protocol), &p);
2833 av_strlcpy(c->method, cmd, sizeof(c->method));
2834 av_strlcpy(c->url, url, sizeof(c->url));
2835 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2837 if (avio_open_dyn_buf(&c->pb) < 0) {
2838 /* XXX: cannot do more */
2839 c->pb = NULL; /* safety */
2843 /* check version name */
2844 if (strcmp(protocol, "RTSP/1.0") != 0) {
2845 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2849 /* parse each header line */
2850 /* skip to next line */
2851 while (*p != '\n' && *p != '\0')
2855 while (*p != '\0') {
2856 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2860 if (p2 > p && p2[-1] == '\r')
2862 /* skip empty line */
2866 if (len > sizeof(line) - 1)
2867 len = sizeof(line) - 1;
2868 memcpy(line, p, len);
2870 ff_rtsp_parse_line(header, line, NULL, NULL);
2874 /* handle sequence number */
2875 c->seq = header->seq;
2877 if (!strcmp(cmd, "DESCRIBE"))
2878 rtsp_cmd_describe(c, url);
2879 else if (!strcmp(cmd, "OPTIONS"))
2880 rtsp_cmd_options(c, url);
2881 else if (!strcmp(cmd, "SETUP"))
2882 rtsp_cmd_setup(c, url, header);
2883 else if (!strcmp(cmd, "PLAY"))
2884 rtsp_cmd_play(c, url, header);
2885 else if (!strcmp(cmd, "PAUSE"))
2886 rtsp_cmd_pause(c, url, header);
2887 else if (!strcmp(cmd, "TEARDOWN"))
2888 rtsp_cmd_teardown(c, url, header);
2890 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2893 len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2894 c->pb = NULL; /* safety */
2896 /* XXX: cannot do more */
2899 c->buffer_ptr = c->pb_buffer;
2900 c->buffer_end = c->pb_buffer + len;
2901 c->state = RTSPSTATE_SEND_REPLY;
2905 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2906 struct in_addr my_ip)
2908 AVFormatContext *avc;
2909 AVStream *avs = NULL;
2912 avc = avformat_alloc_context();
2916 av_dict_set(&avc->metadata, "title",
2917 stream->title[0] ? stream->title : "No Title", 0);
2918 avc->nb_streams = stream->nb_streams;
2919 if (stream->is_multicast) {
2920 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2921 inet_ntoa(stream->multicast_ip),
2922 stream->multicast_port, stream->multicast_ttl);
2924 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2927 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2928 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2930 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2931 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2934 for(i = 0; i < stream->nb_streams; i++) {
2935 avc->streams[i] = &avs[i];
2936 avc->streams[i]->codec = stream->streams[i]->codec;
2938 *pbuffer = av_mallocz(2048);
2939 av_sdp_create(&avc, 1, *pbuffer, 2048);
2942 av_free(avc->streams);
2943 av_dict_free(&avc->metadata);
2947 return strlen(*pbuffer);
2950 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2952 // rtsp_reply_header(c, RTSP_STATUS_OK);
2953 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2954 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2955 avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2956 avio_printf(c->pb, "\r\n");
2959 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2965 int content_length, len;
2966 struct sockaddr_in my_addr;
2968 /* find which url is asked */
2969 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2974 for(stream = first_stream; stream != NULL; stream = stream->next) {
2975 if (!stream->is_feed &&
2976 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2977 !strcmp(path, stream->filename)) {
2981 /* no stream found */
2982 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2986 /* prepare the media description in sdp format */
2988 /* get the host IP */
2989 len = sizeof(my_addr);
2990 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2991 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2992 if (content_length < 0) {
2993 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2996 rtsp_reply_header(c, RTSP_STATUS_OK);
2997 avio_printf(c->pb, "Content-Base: %s/\r\n", url);
2998 avio_printf(c->pb, "Content-Type: application/sdp\r\n");
2999 avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
3000 avio_printf(c->pb, "\r\n");
3001 avio_write(c->pb, content, content_length);
3005 static HTTPContext *find_rtp_session(const char *session_id)
3009 if (session_id[0] == '\0')
3012 for(c = first_http_ctx; c != NULL; c = c->next) {
3013 if (!strcmp(c->session_id, session_id))
3019 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3021 RTSPTransportField *th;
3024 for(i=0;i<h->nb_transports;i++) {
3025 th = &h->transports[i];
3026 if (th->lower_transport == lower_transport)
3032 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3033 RTSPMessageHeader *h)
3036 int stream_index, rtp_port, rtcp_port;
3041 RTSPTransportField *th;
3042 struct sockaddr_in dest_addr;
3043 RTSPActionServerSetup setup;
3045 /* find which url is asked */
3046 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3051 /* now check each stream */
3052 for(stream = first_stream; stream != NULL; stream = stream->next) {
3053 if (!stream->is_feed &&
3054 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3055 /* accept aggregate filenames only if single stream */
3056 if (!strcmp(path, stream->filename)) {
3057 if (stream->nb_streams != 1) {
3058 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3065 for(stream_index = 0; stream_index < stream->nb_streams;
3067 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3068 stream->filename, stream_index);
3069 if (!strcmp(path, buf))
3074 /* no stream found */
3075 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3079 /* generate session id if needed */
3080 if (h->session_id[0] == '\0')
3081 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3082 av_lfg_get(&random_state), av_lfg_get(&random_state));
3084 /* find rtp session, and create it if none found */
3085 rtp_c = find_rtp_session(h->session_id);
3087 /* always prefer UDP */
3088 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3090 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3092 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3097 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3098 th->lower_transport);
3100 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3104 /* open input stream */
3105 if (open_input_stream(rtp_c, "") < 0) {
3106 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3111 /* test if stream is OK (test needed because several SETUP needs
3112 to be done for a given file) */
3113 if (rtp_c->stream != stream) {
3114 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3118 /* test if stream is already set up */
3119 if (rtp_c->rtp_ctx[stream_index]) {
3120 rtsp_reply_error(c, RTSP_STATUS_STATE);
3124 /* check transport */
3125 th = find_transport(h, rtp_c->rtp_protocol);
3126 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3127 th->client_port_min <= 0)) {
3128 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3132 /* setup default options */
3133 setup.transport_option[0] = '\0';
3134 dest_addr = rtp_c->from_addr;
3135 dest_addr.sin_port = htons(th->client_port_min);
3138 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3139 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3143 /* now everything is OK, so we can send the connection parameters */
3144 rtsp_reply_header(c, RTSP_STATUS_OK);
3146 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3148 switch(rtp_c->rtp_protocol) {
3149 case RTSP_LOWER_TRANSPORT_UDP:
3150 rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3151 rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3152 avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3153 "client_port=%d-%d;server_port=%d-%d",
3154 th->client_port_min, th->client_port_max,
3155 rtp_port, rtcp_port);
3157 case RTSP_LOWER_TRANSPORT_TCP:
3158 avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3159 stream_index * 2, stream_index * 2 + 1);
3164 if (setup.transport_option[0] != '\0')
3165 avio_printf(c->pb, ";%s", setup.transport_option);
3166 avio_printf(c->pb, "\r\n");
3169 avio_printf(c->pb, "\r\n");
3173 /* find an rtp connection by using the session ID. Check consistency
3175 static HTTPContext *find_rtp_session_with_url(const char *url,
3176 const char *session_id)
3184 rtp_c = find_rtp_session(session_id);
3188 /* find which url is asked */
3189 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3193 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3194 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3195 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3196 rtp_c->stream->filename, s);
3197 if(!strncmp(path, buf, sizeof(buf))) {
3198 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3203 if (len > 0 && path[len - 1] == '/' &&
3204 !strncmp(path, rtp_c->stream->filename, len - 1))
3209 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3213 rtp_c = find_rtp_session_with_url(url, h->session_id);
3215 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3219 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3220 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3221 rtp_c->state != HTTPSTATE_READY) {
3222 rtsp_reply_error(c, RTSP_STATUS_STATE);
3226 rtp_c->state = HTTPSTATE_SEND_DATA;
3228 /* now everything is OK, so we can send the connection parameters */
3229 rtsp_reply_header(c, RTSP_STATUS_OK);
3231 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3232 avio_printf(c->pb, "\r\n");
3235 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3239 rtp_c = find_rtp_session_with_url(url, h->session_id);
3241 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3245 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3246 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3247 rtsp_reply_error(c, RTSP_STATUS_STATE);
3251 rtp_c->state = HTTPSTATE_READY;
3252 rtp_c->first_pts = AV_NOPTS_VALUE;
3253 /* now everything is OK, so we can send the connection parameters */
3254 rtsp_reply_header(c, RTSP_STATUS_OK);
3256 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3257 avio_printf(c->pb, "\r\n");
3260 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3264 rtp_c = find_rtp_session_with_url(url, h->session_id);
3266 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3270 /* now everything is OK, so we can send the connection parameters */
3271 rtsp_reply_header(c, RTSP_STATUS_OK);
3273 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3274 avio_printf(c->pb, "\r\n");
3276 /* abort the session */
3277 close_connection(rtp_c);
3281 /********************************************************************/
3284 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3285 FFStream *stream, const char *session_id,
3286 enum RTSPLowerTransport rtp_protocol)
3288 HTTPContext *c = NULL;
3289 const char *proto_str;
3291 /* XXX: should output a warning page when coming
3292 close to the connection limit */
3293 if (nb_connections >= nb_max_connections)
3296 /* add a new connection */
3297 c = av_mallocz(sizeof(HTTPContext));
3302 c->poll_entry = NULL;
3303 c->from_addr = *from_addr;
3304 c->buffer_size = IOBUFFER_INIT_SIZE;
3305 c->buffer = av_malloc(c->buffer_size);
3310 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3311 c->state = HTTPSTATE_READY;
3312 c->is_packetized = 1;
3313 c->rtp_protocol = rtp_protocol;
3315 /* protocol is shown in statistics */
3316 switch(c->rtp_protocol) {
3317 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3318 proto_str = "MCAST";
3320 case RTSP_LOWER_TRANSPORT_UDP:
3323 case RTSP_LOWER_TRANSPORT_TCP:
3330 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3331 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3333 current_bandwidth += stream->bandwidth;
3335 c->next = first_http_ctx;
3347 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3348 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3350 static int rtp_new_av_stream(HTTPContext *c,
3351 int stream_index, struct sockaddr_in *dest_addr,
3352 HTTPContext *rtsp_c)
3354 AVFormatContext *ctx;
3357 URLContext *h = NULL;
3359 int max_packet_size;
3361 /* now we can open the relevant output stream */
3362 ctx = avformat_alloc_context();
3365 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3367 st = av_mallocz(sizeof(AVStream));
3370 ctx->nb_streams = 1;
3371 ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3374 ctx->streams[0] = st;
3376 if (!c->stream->feed ||
3377 c->stream->feed == c->stream)
3378 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3381 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3383 st->priv_data = NULL;
3385 /* build destination RTP address */
3386 ipaddr = inet_ntoa(dest_addr->sin_addr);
3388 switch(c->rtp_protocol) {
3389 case RTSP_LOWER_TRANSPORT_UDP:
3390 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3393 /* XXX: also pass as parameter to function ? */
3394 if (c->stream->is_multicast) {
3396 ttl = c->stream->multicast_ttl;
3399 snprintf(ctx->filename, sizeof(ctx->filename),
3400 "rtp://%s:%d?multicast=1&ttl=%d",
3401 ipaddr, ntohs(dest_addr->sin_port), ttl);
3403 snprintf(ctx->filename, sizeof(ctx->filename),
3404 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3407 if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3409 c->rtp_handles[stream_index] = h;
3410 max_packet_size = h->max_packet_size;
3412 case RTSP_LOWER_TRANSPORT_TCP:
3415 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3421 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3422 ipaddr, ntohs(dest_addr->sin_port),
3423 c->stream->filename, stream_index, c->protocol);
3425 /* normally, no packets should be output here, but the packet size may be checked */
3426 if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3427 /* XXX: close stream */
3430 if (avformat_write_header(ctx, NULL) < 0) {
3437 avio_close_dyn_buf(ctx->pb, &dummy_buf);
3440 c->rtp_ctx[stream_index] = ctx;
3444 /********************************************************************/
3445 /* avserver initialization */
3447 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3451 fst = av_mallocz(sizeof(AVStream));
3455 fst->codec = avcodec_alloc_context3(NULL);
3456 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3457 if (codec->extradata_size) {
3458 fst->codec->extradata = av_malloc(codec->extradata_size);
3459 memcpy(fst->codec->extradata, codec->extradata,
3460 codec->extradata_size);
3463 /* live streams must use the actual feed's codec since it may be
3464 * updated later to carry extradata needed by the streams.
3468 fst->priv_data = av_mallocz(sizeof(FeedData));
3469 fst->index = stream->nb_streams;
3470 avpriv_set_pts_info(fst, 33, 1, 90000);
3471 fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3472 stream->streams[stream->nb_streams++] = fst;
3476 /* return the stream number in the feed */
3477 static int add_av_stream(FFStream *feed, AVStream *st)
3480 AVCodecContext *av, *av1;
3484 for(i=0;i<feed->nb_streams;i++) {
3485 st = feed->streams[i];
3487 if (av1->codec_id == av->codec_id &&
3488 av1->codec_type == av->codec_type &&
3489 av1->bit_rate == av->bit_rate) {
3491 switch(av->codec_type) {
3492 case AVMEDIA_TYPE_AUDIO:
3493 if (av1->channels == av->channels &&
3494 av1->sample_rate == av->sample_rate)
3497 case AVMEDIA_TYPE_VIDEO:
3498 if (av1->width == av->width &&
3499 av1->height == av->height &&
3500 av1->time_base.den == av->time_base.den &&
3501 av1->time_base.num == av->time_base.num &&
3502 av1->gop_size == av->gop_size)
3511 fst = add_av_stream1(feed, av, 0);
3514 return feed->nb_streams - 1;
3517 static void remove_stream(FFStream *stream)
3521 while (*ps != NULL) {
3529 /* specific mpeg4 handling : we extract the raw parameters */
3530 static void extract_mpeg4_header(AVFormatContext *infile)
3532 int mpeg4_count, i, size;
3538 for(i=0;i<infile->nb_streams;i++) {
3539 st = infile->streams[i];
3540 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3541 st->codec->extradata_size == 0) {
3548 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3549 while (mpeg4_count > 0) {
3550 if (av_read_packet(infile, &pkt) < 0)
3552 st = infile->streams[pkt.stream_index];
3553 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3554 st->codec->extradata_size == 0) {
3555 av_freep(&st->codec->extradata);
3556 /* fill extradata with the header */
3557 /* XXX: we make hard suppositions here ! */
3559 while (p < pkt.data + pkt.size - 4) {
3560 /* stop when vop header is found */
3561 if (p[0] == 0x00 && p[1] == 0x00 &&
3562 p[2] == 0x01 && p[3] == 0xb6) {
3563 size = p - pkt.data;
3564 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3565 st->codec->extradata = av_malloc(size);
3566 st->codec->extradata_size = size;
3567 memcpy(st->codec->extradata, pkt.data, size);
3574 av_free_packet(&pkt);
3578 /* compute the needed AVStream for each file */
3579 static void build_file_streams(void)
3581 FFStream *stream, *stream_next;
3584 /* gather all streams */
3585 for(stream = first_stream; stream != NULL; stream = stream_next) {
3586 AVFormatContext *infile = NULL;
3587 stream_next = stream->next;
3588 if (stream->stream_type == STREAM_TYPE_LIVE &&
3590 /* the stream comes from a file */
3591 /* try to open the file */
3593 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3594 /* specific case : if transport stream output to RTP,
3595 we use a raw transport stream reader */
3596 av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3599 http_log("Opening file '%s'\n", stream->feed_filename);
3600 if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3601 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3602 /* remove stream (no need to spend more time on it) */
3604 remove_stream(stream);
3606 /* find all the AVStreams inside and reference them in
3608 if (avformat_find_stream_info(infile, NULL) < 0) {
3609 http_log("Could not find codec parameters from '%s'\n",
3610 stream->feed_filename);
3611 avformat_close_input(&infile);
3614 extract_mpeg4_header(infile);
3616 for(i=0;i<infile->nb_streams;i++)
3617 add_av_stream1(stream, infile->streams[i]->codec, 1);
3619 avformat_close_input(&infile);
3625 /* compute the needed AVStream for each feed */
3626 static void build_feed_streams(void)
3628 FFStream *stream, *feed;
3631 /* gather all streams */
3632 for(stream = first_stream; stream != NULL; stream = stream->next) {
3633 feed = stream->feed;
3635 if (stream->is_feed) {
3636 for(i=0;i<stream->nb_streams;i++)
3637 stream->feed_streams[i] = i;
3639 /* we handle a stream coming from a feed */
3640 for(i=0;i<stream->nb_streams;i++)
3641 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3646 /* create feed files if needed */
3647 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3650 if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3651 /* See if it matches */
3652 AVFormatContext *s = NULL;
3655 if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3656 /* Now see if it matches */
3657 if (s->nb_streams == feed->nb_streams) {
3659 for(i=0;i<s->nb_streams;i++) {
3661 sf = feed->streams[i];
3664 if (sf->index != ss->index ||
3666 http_log("Index & Id do not match for stream %d (%s)\n",
3667 i, feed->feed_filename);
3670 AVCodecContext *ccf, *ccs;
3674 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3676 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3677 http_log("Codecs do not match for stream %d\n", i);
3679 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3680 http_log("Codec bitrates do not match for stream %d\n", i);
3682 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3683 if (CHECK_CODEC(time_base.den) ||
3684 CHECK_CODEC(time_base.num) ||
3685 CHECK_CODEC(width) ||
3686 CHECK_CODEC(height)) {
3687 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3690 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3691 if (CHECK_CODEC(sample_rate) ||
3692 CHECK_CODEC(channels) ||
3693 CHECK_CODEC(frame_size)) {
3694 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3698 http_log("Unknown codec type\n");
3706 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3707 feed->feed_filename, s->nb_streams, feed->nb_streams);
3709 avformat_close_input(&s);
3711 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3712 feed->feed_filename);
3715 if (feed->readonly) {
3716 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3717 feed->feed_filename);
3720 unlink(feed->feed_filename);
3723 if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3724 AVFormatContext s1 = {0}, *s = &s1;
3726 if (feed->readonly) {
3727 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3728 feed->feed_filename);
3732 /* only write the header of the ffm file */
3733 if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3734 http_log("Could not open output feed file '%s'\n",
3735 feed->feed_filename);
3738 s->oformat = feed->fmt;
3739 s->nb_streams = feed->nb_streams;
3740 s->streams = feed->streams;
3741 if (avformat_write_header(s, NULL) < 0) {
3742 http_log("Container doesn't supports the required parameters\n");
3745 /* XXX: need better api */
3746 av_freep(&s->priv_data);
3749 /* get feed size and write index */
3750 fd = open(feed->feed_filename, O_RDONLY);
3752 http_log("Could not open output feed file '%s'\n",
3753 feed->feed_filename);
3757 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3758 feed->feed_size = lseek(fd, 0, SEEK_END);
3759 /* ensure that we do not wrap before the end of file */
3760 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3761 feed->feed_max_size = feed->feed_size;
3767 /* compute the bandwidth used by each stream */
3768 static void compute_bandwidth(void)
3774 for(stream = first_stream; stream != NULL; stream = stream->next) {
3776 for(i=0;i<stream->nb_streams;i++) {
3777 AVStream *st = stream->streams[i];
3778 switch(st->codec->codec_type) {
3779 case AVMEDIA_TYPE_AUDIO:
3780 case AVMEDIA_TYPE_VIDEO:
3781 bandwidth += st->codec->bit_rate;
3787 stream->bandwidth = (bandwidth + 999) / 1000;
3791 /* add a codec and set the default parameters */
3792 static void add_codec(FFStream *stream, AVCodecContext *av)
3796 /* compute default parameters */
3797 switch(av->codec_type) {
3798 case AVMEDIA_TYPE_AUDIO:
3799 if (av->bit_rate == 0)
3800 av->bit_rate = 64000;
3801 if (av->sample_rate == 0)
3802 av->sample_rate = 22050;
3803 if (av->channels == 0)
3806 case AVMEDIA_TYPE_VIDEO:
3807 if (av->bit_rate == 0)
3808 av->bit_rate = 64000;
3809 if (av->time_base.num == 0){
3810 av->time_base.den = 5;
3811 av->time_base.num = 1;
3813 if (av->width == 0 || av->height == 0) {
3817 /* Bitrate tolerance is less for streaming */
3818 if (av->bit_rate_tolerance == 0)
3819 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3820 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3825 if (av->max_qdiff == 0)
3827 av->qcompress = 0.5;
3830 if (!av->nsse_weight)
3831 av->nsse_weight = 8;
3833 av->frame_skip_cmp = FF_CMP_DCTMAX;
3835 av->me_method = ME_EPZS;
3836 av->rc_buffer_aggressivity = 1.0;
3839 av->rc_eq = "tex^qComp";
3840 if (!av->i_quant_factor)
3841 av->i_quant_factor = -0.8;
3842 if (!av->b_quant_factor)
3843 av->b_quant_factor = 1.25;
3844 if (!av->b_quant_offset)
3845 av->b_quant_offset = 1.25;
3846 if (!av->rc_max_rate)
3847 av->rc_max_rate = av->bit_rate * 2;
3849 if (av->rc_max_rate && !av->rc_buffer_size) {
3850 av->rc_buffer_size = av->rc_max_rate;
3859 st = av_mallocz(sizeof(AVStream));
3862 st->codec = avcodec_alloc_context3(NULL);
3863 stream->streams[stream->nb_streams++] = st;
3864 memcpy(st->codec, av, sizeof(AVCodecContext));
3867 static enum CodecID opt_audio_codec(const char *arg)
3869 AVCodec *p= avcodec_find_encoder_by_name(arg);
3871 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3872 return CODEC_ID_NONE;
3877 static enum CodecID opt_video_codec(const char *arg)
3879 AVCodec *p= avcodec_find_encoder_by_name(arg);
3881 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3882 return CODEC_ID_NONE;
3887 /* simplistic plugin support */
3890 static void load_module(const char *filename)
3893 void (*init_func)(void);
3894 dll = dlopen(filename, RTLD_NOW);
3896 fprintf(stderr, "Could not load module '%s' - %s\n",
3897 filename, dlerror());
3901 init_func = dlsym(dll, "avserver_module_init");
3904 "%s: init function 'avserver_module_init()' not found\n",
3913 static int avserver_opt_default(const char *opt, const char *arg,
3914 AVCodecContext *avctx, int type)
3917 const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3919 ret = av_opt_set(avctx, opt, arg, 0);
3923 static int avserver_opt_preset(const char *arg,
3924 AVCodecContext *avctx, int type,
3925 enum CodecID *audio_id, enum CodecID *video_id)
3928 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3930 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3932 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3933 codec ? codec->name : NULL))) {
3934 fprintf(stderr, "File for preset '%s' not found\n", arg);
3939 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3940 if(line[0] == '#' && !e)
3942 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3944 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3948 if(!strcmp(tmp, "acodec")){
3949 *audio_id = opt_audio_codec(tmp2);
3950 }else if(!strcmp(tmp, "vcodec")){
3951 *video_id = opt_video_codec(tmp2);
3952 }else if(!strcmp(tmp, "scodec")){
3953 /* opt_subtitle_codec(tmp2); */
3954 }else if(avserver_opt_default(tmp, tmp2, avctx, type) < 0){
3955 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
3966 static AVOutputFormat *avserver_guess_format(const char *short_name, const char *filename,
3967 const char *mime_type)
3969 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3972 AVOutputFormat *stream_fmt;
3973 char stream_format_name[64];
3975 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
3976 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
3985 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
3989 fprintf(stderr, "%s:%d: ", filename, line_num);
3990 vfprintf(stderr, fmt, vl);
3996 static int parse_ffconfig(const char *filename)
4003 int val, errors, line_num;
4004 FFStream **last_stream, *stream, *redirect;
4005 FFStream **last_feed, *feed, *s;
4006 AVCodecContext audio_enc, video_enc;
4007 enum CodecID audio_id, video_id;
4009 f = fopen(filename, "r");
4017 first_stream = NULL;
4018 last_stream = &first_stream;
4020 last_feed = &first_feed;
4024 audio_id = CODEC_ID_NONE;
4025 video_id = CODEC_ID_NONE;
4027 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4029 if (fgets(line, sizeof(line), f) == NULL)
4035 if (*p == '\0' || *p == '#')
4038 get_arg(cmd, sizeof(cmd), &p);
4040 if (!av_strcasecmp(cmd, "Port")) {
4041 get_arg(arg, sizeof(arg), &p);
4043 if (val < 1 || val > 65536) {
4044 ERROR("Invalid_port: %s\n", arg);
4046 my_http_addr.sin_port = htons(val);
4047 } else if (!av_strcasecmp(cmd, "BindAddress")) {
4048 get_arg(arg, sizeof(arg), &p);
4049 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4050 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4052 } else if (!av_strcasecmp(cmd, "NoDaemon")) {
4053 avserver_daemon = 0;
4054 } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4055 get_arg(arg, sizeof(arg), &p);
4057 if (val < 1 || val > 65536) {
4058 ERROR("%s:%d: Invalid port: %s\n", arg);
4060 my_rtsp_addr.sin_port = htons(atoi(arg));
4061 } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4062 get_arg(arg, sizeof(arg), &p);
4063 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4064 ERROR("Invalid host/IP address: %s\n", arg);
4066 } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4067 get_arg(arg, sizeof(arg), &p);
4069 if (val < 1 || val > 65536) {
4070 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4072 nb_max_http_connections = val;
4073 } else if (!av_strcasecmp(cmd, "MaxClients")) {
4074 get_arg(arg, sizeof(arg), &p);
4076 if (val < 1 || val > nb_max_http_connections) {
4077 ERROR("Invalid MaxClients: %s\n", arg);
4079 nb_max_connections = val;
4081 } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4083 get_arg(arg, sizeof(arg), &p);
4085 if (llval < 10 || llval > 10000000) {
4086 ERROR("Invalid MaxBandwidth: %s\n", arg);
4088 max_bandwidth = llval;
4089 } else if (!av_strcasecmp(cmd, "CustomLog")) {
4090 if (!avserver_debug)
4091 get_arg(logfilename, sizeof(logfilename), &p);
4092 } else if (!av_strcasecmp(cmd, "<Feed")) {
4093 /*********************************************/
4094 /* Feed related options */
4096 if (stream || feed) {
4097 ERROR("Already in a tag\n");
4099 feed = av_mallocz(sizeof(FFStream));
4100 get_arg(feed->filename, sizeof(feed->filename), &p);
4101 q = strrchr(feed->filename, '>');
4105 for (s = first_feed; s; s = s->next) {
4106 if (!strcmp(feed->filename, s->filename)) {
4107 ERROR("Feed '%s' already registered\n", s->filename);
4111 feed->fmt = av_guess_format("ffm", NULL, NULL);
4112 /* defaut feed file */
4113 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4114 "/tmp/%s.ffm", feed->filename);
4115 feed->feed_max_size = 5 * 1024 * 1024;
4117 feed->feed = feed; /* self feeding :-) */
4119 /* add in stream list */
4120 *last_stream = feed;
4121 last_stream = &feed->next;
4122 /* add in feed list */
4124 last_feed = &feed->next_feed;
4126 } else if (!av_strcasecmp(cmd, "Launch")) {
4130 feed->child_argv = av_mallocz(64 * sizeof(char *));
4132 for (i = 0; i < 62; i++) {
4133 get_arg(arg, sizeof(arg), &p);
4137 feed->child_argv[i] = av_strdup(arg);
4140 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4142 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4144 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4145 inet_ntoa(my_http_addr.sin_addr),
4146 ntohs(my_http_addr.sin_port), feed->filename);
4148 } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4150 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4152 } else if (stream) {
4153 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4155 } else if (!av_strcasecmp(cmd, "File")) {
4157 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4159 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4160 } else if (!av_strcasecmp(cmd, "Truncate")) {
4162 get_arg(arg, sizeof(arg), &p);
4163 feed->truncate = strtod(arg, NULL);
4165 } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4170 get_arg(arg, sizeof(arg), &p);
4172 fsize = strtod(p1, &p1);
4173 switch(toupper(*p1)) {
4178 fsize *= 1024 * 1024;
4181 fsize *= 1024 * 1024 * 1024;
4184 feed->feed_max_size = (int64_t)fsize;
4185 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4186 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4189 } else if (!av_strcasecmp(cmd, "</Feed>")) {
4191 ERROR("No corresponding <Feed> for </Feed>\n");
4194 } else if (!av_strcasecmp(cmd, "<Stream")) {
4195 /*********************************************/
4196 /* Stream related options */
4198 if (stream || feed) {
4199 ERROR("Already in a tag\n");
4202 stream = av_mallocz(sizeof(FFStream));
4203 get_arg(stream->filename, sizeof(stream->filename), &p);
4204 q = strrchr(stream->filename, '>');
4208 for (s = first_stream; s; s = s->next) {
4209 if (!strcmp(stream->filename, s->filename)) {
4210 ERROR("Stream '%s' already registered\n", s->filename);
4214 stream->fmt = avserver_guess_format(NULL, stream->filename, NULL);
4215 avcodec_get_context_defaults3(&video_enc, NULL);
4216 avcodec_get_context_defaults3(&audio_enc, NULL);
4217 audio_id = CODEC_ID_NONE;
4218 video_id = CODEC_ID_NONE;
4220 audio_id = stream->fmt->audio_codec;
4221 video_id = stream->fmt->video_codec;
4224 *last_stream = stream;
4225 last_stream = &stream->next;
4227 } else if (!av_strcasecmp(cmd, "Feed")) {
4228 get_arg(arg, sizeof(arg), &p);
4233 while (sfeed != NULL) {
4234 if (!strcmp(sfeed->filename, arg))
4236 sfeed = sfeed->next_feed;
4239 ERROR("feed '%s' not defined\n", arg);
4241 stream->feed = sfeed;
4243 } else if (!av_strcasecmp(cmd, "Format")) {
4244 get_arg(arg, sizeof(arg), &p);
4246 if (!strcmp(arg, "status")) {
4247 stream->stream_type = STREAM_TYPE_STATUS;
4250 stream->stream_type = STREAM_TYPE_LIVE;
4251 /* jpeg cannot be used here, so use single frame jpeg */
4252 if (!strcmp(arg, "jpeg"))
4253 strcpy(arg, "mjpeg");
4254 stream->fmt = avserver_guess_format(arg, NULL, NULL);
4256 ERROR("Unknown Format: %s\n", arg);
4260 audio_id = stream->fmt->audio_codec;
4261 video_id = stream->fmt->video_codec;
4264 } else if (!av_strcasecmp(cmd, "InputFormat")) {
4265 get_arg(arg, sizeof(arg), &p);
4267 stream->ifmt = av_find_input_format(arg);
4268 if (!stream->ifmt) {
4269 ERROR("Unknown input format: %s\n", arg);
4272 } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4273 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4274 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4276 ERROR("FaviconURL only permitted for status streams\n");
4278 } else if (!av_strcasecmp(cmd, "Author")) {
4280 get_arg(stream->author, sizeof(stream->author), &p);
4281 } else if (!av_strcasecmp(cmd, "Comment")) {
4283 get_arg(stream->comment, sizeof(stream->comment), &p);
4284 } else if (!av_strcasecmp(cmd, "Copyright")) {
4286 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4287 } else if (!av_strcasecmp(cmd, "Title")) {
4289 get_arg(stream->title, sizeof(stream->title), &p);
4290 } else if (!av_strcasecmp(cmd, "Preroll")) {
4291 get_arg(arg, sizeof(arg), &p);
4293 stream->prebuffer = atof(arg) * 1000;
4294 } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4296 stream->send_on_key = 1;
4297 } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4298 get_arg(arg, sizeof(arg), &p);
4299 audio_id = opt_audio_codec(arg);
4300 if (audio_id == CODEC_ID_NONE) {
4301 ERROR("Unknown AudioCodec: %s\n", arg);
4303 } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4304 get_arg(arg, sizeof(arg), &p);
4305 video_id = opt_video_codec(arg);
4306 if (video_id == CODEC_ID_NONE) {
4307 ERROR("Unknown VideoCodec: %s\n", arg);
4309 } else if (!av_strcasecmp(cmd, "MaxTime")) {
4310 get_arg(arg, sizeof(arg), &p);
4312 stream->max_time = atof(arg) * 1000;
4313 } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4314 get_arg(arg, sizeof(arg), &p);
4316 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4317 } else if (!av_strcasecmp(cmd, "AudioChannels")) {
4318 get_arg(arg, sizeof(arg), &p);
4320 audio_enc.channels = atoi(arg);
4321 } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4322 get_arg(arg, sizeof(arg), &p);
4324 audio_enc.sample_rate = atoi(arg);
4325 } else if (!av_strcasecmp(cmd, "AudioQuality")) {
4326 get_arg(arg, sizeof(arg), &p);
4328 // audio_enc.quality = atof(arg) * 1000;
4330 } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4332 int minrate, maxrate;
4334 get_arg(arg, sizeof(arg), &p);
4336 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4337 video_enc.rc_min_rate = minrate * 1000;
4338 video_enc.rc_max_rate = maxrate * 1000;
4340 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4343 } else if (!av_strcasecmp(cmd, "Debug")) {
4345 get_arg(arg, sizeof(arg), &p);
4346 video_enc.debug = strtol(arg,0,0);
4348 } else if (!av_strcasecmp(cmd, "Strict")) {
4350 get_arg(arg, sizeof(arg), &p);
4351 video_enc.strict_std_compliance = atoi(arg);
4353 } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4355 get_arg(arg, sizeof(arg), &p);
4356 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4358 } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4360 get_arg(arg, sizeof(arg), &p);
4361 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4363 } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4364 get_arg(arg, sizeof(arg), &p);
4366 video_enc.bit_rate = atoi(arg) * 1000;
4368 } else if (!av_strcasecmp(cmd, "VideoSize")) {
4369 get_arg(arg, sizeof(arg), &p);
4371 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4372 if ((video_enc.width % 16) != 0 ||
4373 (video_enc.height % 16) != 0) {
4374 ERROR("Image size must be a multiple of 16\n");
4377 } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4378 get_arg(arg, sizeof(arg), &p);
4380 AVRational frame_rate;
4381 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4382 ERROR("Incorrect frame rate: %s\n", arg);
4384 video_enc.time_base.num = frame_rate.den;
4385 video_enc.time_base.den = frame_rate.num;
4388 } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4389 get_arg(arg, sizeof(arg), &p);
4391 video_enc.gop_size = atoi(arg);
4392 } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4394 video_enc.gop_size = 1;
4395 } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4397 video_enc.mb_decision = FF_MB_DECISION_BITS;
4398 } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4400 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4401 video_enc.flags |= CODEC_FLAG_4MV;
4403 } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4404 !av_strcasecmp(cmd, "AVOptionAudio")) {
4406 AVCodecContext *avctx;
4408 get_arg(arg, sizeof(arg), &p);
4409 get_arg(arg2, sizeof(arg2), &p);
4410 if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4412 type = AV_OPT_FLAG_VIDEO_PARAM;
4415 type = AV_OPT_FLAG_AUDIO_PARAM;
4417 if (avserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4418 ERROR("AVOption error: %s %s\n", arg, arg2);
4420 } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4421 !av_strcasecmp(cmd, "AVPresetAudio")) {
4422 AVCodecContext *avctx;
4424 get_arg(arg, sizeof(arg), &p);
4425 if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4427 video_enc.codec_id = video_id;
4428 type = AV_OPT_FLAG_VIDEO_PARAM;
4431 audio_enc.codec_id = audio_id;
4432 type = AV_OPT_FLAG_AUDIO_PARAM;
4434 if (avserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4435 ERROR("AVPreset error: %s\n", arg);
4437 } else if (!av_strcasecmp(cmd, "VideoTag")) {
4438 get_arg(arg, sizeof(arg), &p);
4439 if ((strlen(arg) == 4) && stream)
4440 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4441 } else if (!av_strcasecmp(cmd, "BitExact")) {
4443 video_enc.flags |= CODEC_FLAG_BITEXACT;
4444 } else if (!av_strcasecmp(cmd, "DctFastint")) {
4446 video_enc.dct_algo = FF_DCT_FASTINT;
4447 } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4449 video_enc.idct_algo = FF_IDCT_SIMPLE;
4450 } else if (!av_strcasecmp(cmd, "Qscale")) {
4451 get_arg(arg, sizeof(arg), &p);
4453 video_enc.flags |= CODEC_FLAG_QSCALE;
4454 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4456 } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4457 get_arg(arg, sizeof(arg), &p);
4459 video_enc.max_qdiff = atoi(arg);
4460 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4461 ERROR("VideoQDiff out of range\n");
4464 } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4465 get_arg(arg, sizeof(arg), &p);
4467 video_enc.qmax = atoi(arg);
4468 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4469 ERROR("VideoQMax out of range\n");
4472 } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4473 get_arg(arg, sizeof(arg), &p);
4475 video_enc.qmin = atoi(arg);
4476 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4477 ERROR("VideoQMin out of range\n");
4480 } else if (!av_strcasecmp(cmd, "LumaElim")) {
4481 get_arg(arg, sizeof(arg), &p);
4483 video_enc.luma_elim_threshold = atoi(arg);
4484 } else if (!av_strcasecmp(cmd, "ChromaElim")) {
4485 get_arg(arg, sizeof(arg), &p);
4487 video_enc.chroma_elim_threshold = atoi(arg);
4488 } else if (!av_strcasecmp(cmd, "LumiMask")) {
4489 get_arg(arg, sizeof(arg), &p);
4491 video_enc.lumi_masking = atof(arg);
4492 } else if (!av_strcasecmp(cmd, "DarkMask")) {
4493 get_arg(arg, sizeof(arg), &p);
4495 video_enc.dark_masking = atof(arg);
4496 } else if (!av_strcasecmp(cmd, "NoVideo")) {
4497 video_id = CODEC_ID_NONE;
4498 } else if (!av_strcasecmp(cmd, "NoAudio")) {
4499 audio_id = CODEC_ID_NONE;
4500 } else if (!av_strcasecmp(cmd, "ACL")) {
4501 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4502 } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4504 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4506 } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4507 get_arg(arg, sizeof(arg), &p);
4509 av_freep(&stream->rtsp_option);
4510 stream->rtsp_option = av_strdup(arg);
4512 } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4513 get_arg(arg, sizeof(arg), &p);
4515 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4516 ERROR("Invalid host/IP address: %s\n", arg);
4518 stream->is_multicast = 1;
4519 stream->loop = 1; /* default is looping */
4521 } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4522 get_arg(arg, sizeof(arg), &p);
4524 stream->multicast_port = atoi(arg);
4525 } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4526 get_arg(arg, sizeof(arg), &p);
4528 stream->multicast_ttl = atoi(arg);
4529 } else if (!av_strcasecmp(cmd, "NoLoop")) {
4532 } else if (!av_strcasecmp(cmd, "</Stream>")) {
4534 ERROR("No corresponding <Stream> for </Stream>\n");
4536 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4537 if (audio_id != CODEC_ID_NONE) {
4538 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4539 audio_enc.codec_id = audio_id;
4540 add_codec(stream, &audio_enc);
4542 if (video_id != CODEC_ID_NONE) {
4543 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4544 video_enc.codec_id = video_id;
4545 add_codec(stream, &video_enc);
4550 } else if (!av_strcasecmp(cmd, "<Redirect")) {
4551 /*********************************************/
4553 if (stream || feed || redirect) {
4554 ERROR("Already in a tag\n");
4556 redirect = av_mallocz(sizeof(FFStream));
4557 *last_stream = redirect;
4558 last_stream = &redirect->next;
4560 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4561 q = strrchr(redirect->filename, '>');
4564 redirect->stream_type = STREAM_TYPE_REDIRECT;
4566 } else if (!av_strcasecmp(cmd, "URL")) {
4568 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4569 } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4571 ERROR("No corresponding <Redirect> for </Redirect>\n");
4573 if (!redirect->feed_filename[0]) {
4574 ERROR("No URL found for <Redirect>\n");
4578 } else if (!av_strcasecmp(cmd, "LoadModule")) {
4579 get_arg(arg, sizeof(arg), &p);
4583 ERROR("Module support not compiled into this version: '%s'\n", arg);
4586 ERROR("Incorrect keyword: '%s'\n", cmd);
4598 static void handle_child_exit(int sig)
4603 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4606 for (feed = first_feed; feed; feed = feed->next) {
4607 if (feed->pid == pid) {
4608 int uptime = time(0) - feed->pid_start;
4611 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4614 /* Turn off any more restarts */
4615 feed->child_argv = 0;
4620 need_to_start_children = 1;
4623 static void opt_debug(void)
4626 avserver_daemon = 0;
4627 logfilename[0] = '-';
4630 static void show_help(void)
4632 printf("usage: avserver [options]\n"
4633 "Hyper fast multi format Audio/Video streaming server\n");
4635 show_help_options(options, "Main options:\n", 0, 0);
4638 static const OptionDef options[] = {
4639 #include "cmdutils_common_opts.h"
4640 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4641 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4642 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/avserver.conf", "configfile" },
4646 int main(int argc, char **argv)
4648 struct sigaction sigact = { { 0 } };
4650 parse_loglevel(argc, argv, options);
4652 avformat_network_init();
4656 my_program_name = argv[0];
4657 my_program_dir = getcwd(0, 0);
4658 avserver_daemon = 1;
4660 parse_options(NULL, argc, argv, options, NULL);
4662 unsetenv("http_proxy"); /* Kill the http_proxy */
4664 av_lfg_init(&random_state, av_get_random_seed());
4666 sigact.sa_handler = handle_child_exit;
4667 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4668 sigaction(SIGCHLD, &sigact, 0);
4670 if (parse_ffconfig(config_filename) < 0) {
4671 fprintf(stderr, "Incorrect config file - exiting.\n");
4675 /* open log file if needed */
4676 if (logfilename[0] != '\0') {
4677 if (!strcmp(logfilename, "-"))
4680 logfile = fopen(logfilename, "a");
4681 av_log_set_callback(http_av_log);
4684 build_file_streams();
4686 build_feed_streams();
4688 compute_bandwidth();
4690 /* put the process in background and detach it from its TTY */
4691 if (avserver_daemon) {
4698 } else if (pid > 0) {
4705 open("/dev/null", O_RDWR);
4706 if (strcmp(logfilename, "-") != 0) {
4716 signal(SIGPIPE, SIG_IGN);
4718 if (avserver_daemon)
4721 if (http_server() < 0) {
4722 http_log("Could not start server\n");