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
29 #include "libavformat/avformat.h"
30 // FIXME those are internal headers, avserver _really_ shouldn't use them
31 #include "libavformat/ffm.h"
32 #include "libavformat/network.h"
33 #include "libavformat/os_support.h"
34 #include "libavformat/rtpdec.h"
35 #include "libavformat/rtsp.h"
36 #include "libavformat/avio_internal.h"
37 #include "libavformat/internal.h"
38 #include "libavformat/url.h"
40 #include "libavutil/avstring.h"
41 #include "libavutil/lfg.h"
42 #include "libavutil/dict.h"
43 #include "libavutil/intreadwrite.h"
44 #include "libavutil/mathematics.h"
45 #include "libavutil/random_seed.h"
46 #include "libavutil/parseutils.h"
47 #include "libavutil/opt.h"
48 #include "libavutil/time.h"
53 #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;
303 static const char *config_filename = "/etc/avserver.conf";
305 static int avserver_debug;
306 static int no_launch;
307 static int need_to_start_children;
309 /* maximum number of simultaneous HTTP connections */
310 static unsigned int nb_max_http_connections = 2000;
311 static unsigned int nb_max_connections = 5;
312 static unsigned int nb_connections;
314 static uint64_t max_bandwidth = 1000;
315 static uint64_t current_bandwidth;
317 static int64_t cur_time; // Making this global saves on passing it around everywhere
319 static AVLFG random_state;
321 static FILE *logfile = NULL;
323 static int64_t ffm_read_write_index(int fd)
327 lseek(fd, 8, SEEK_SET);
328 if (read(fd, buf, 8) != 8)
333 static int ffm_write_write_index(int fd, int64_t pos)
339 buf[i] = (pos >> (56 - i * 8)) & 0xff;
340 lseek(fd, 8, SEEK_SET);
341 if (write(fd, buf, 8) != 8)
346 static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
349 FFMContext *ffm = s->priv_data;
350 ffm->write_index = pos;
351 ffm->file_size = file_size;
354 /* FIXME: make avserver work with IPv6 */
355 /* resolve host with also IP address parsing */
356 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
359 if (!ff_inet_aton(hostname, sin_addr)) {
361 struct addrinfo *ai, *cur;
362 struct addrinfo hints = { 0 };
363 hints.ai_family = AF_INET;
364 if (getaddrinfo(hostname, NULL, &hints, &ai))
366 /* getaddrinfo returns a linked list of addrinfo structs.
367 * Even if we set ai_family = AF_INET above, make sure
368 * that the returned one actually is of the correct type. */
369 for (cur = ai; cur; cur = cur->ai_next) {
370 if (cur->ai_family == AF_INET) {
371 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
380 hp = gethostbyname(hostname);
383 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
389 static char *ctime1(char *buf2)
397 p = buf2 + strlen(p) - 1;
403 static void http_vlog(const char *fmt, va_list vargs)
405 static int print_prefix = 1;
410 fprintf(logfile, "%s ", buf);
412 print_prefix = strstr(fmt, "\n") != NULL;
413 vfprintf(logfile, fmt, vargs);
419 __attribute__ ((format (printf, 1, 2)))
421 static void http_log(const char *fmt, ...)
424 va_start(vargs, fmt);
425 http_vlog(fmt, vargs);
429 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
431 static int print_prefix = 1;
432 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
433 if (level > av_log_get_level())
435 if (print_prefix && avc)
436 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
437 print_prefix = strstr(fmt, "\n") != NULL;
438 http_vlog(fmt, vargs);
441 static void log_connection(HTTPContext *c)
446 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
447 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
448 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
451 static void update_datarate(DataRateData *drd, int64_t count)
453 if (!drd->time1 && !drd->count1) {
454 drd->time1 = drd->time2 = cur_time;
455 drd->count1 = drd->count2 = count;
456 } else if (cur_time - drd->time2 > 5000) {
457 drd->time1 = drd->time2;
458 drd->count1 = drd->count2;
459 drd->time2 = cur_time;
464 /* In bytes per second */
465 static int compute_datarate(DataRateData *drd, int64_t count)
467 if (cur_time == drd->time1)
470 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
474 static void start_children(FFStream *feed)
479 for (; feed; feed = feed->next) {
480 if (feed->child_argv && !feed->pid) {
481 feed->pid_start = time(0);
486 http_log("Unable to create children\n");
495 av_strlcpy(pathname, my_program_name, sizeof(pathname));
497 slash = strrchr(pathname, '/');
502 strcpy(slash, "avconv");
504 http_log("Launch command line: ");
505 http_log("%s ", pathname);
506 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
507 http_log("%s ", feed->child_argv[i]);
510 for (i = 3; i < 256; i++)
513 if (!avserver_debug) {
514 if (!freopen("/dev/null", "r", stdin))
515 http_log("failed to redirect STDIN to /dev/null\n;");
516 if (!freopen("/dev/null", "w", stdout))
517 http_log("failed to redirect STDOUT to /dev/null\n;");
518 if (!freopen("/dev/null", "w", stderr))
519 http_log("failed to redirect STDERR to /dev/null\n;");
522 signal(SIGPIPE, SIG_DFL);
524 execvp(pathname, feed->child_argv);
532 /* open a listening socket */
533 static int socket_open_listen(struct sockaddr_in *my_addr)
537 server_fd = socket(AF_INET,SOCK_STREAM,0);
544 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
546 my_addr->sin_family = AF_INET;
547 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
549 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
551 closesocket(server_fd);
555 if (listen (server_fd, 5) < 0) {
557 closesocket(server_fd);
560 ff_socket_nonblock(server_fd, 1);
565 /* start all multicast streams */
566 static void start_multicast(void)
571 struct sockaddr_in dest_addr;
572 int default_port, stream_index;
575 for(stream = first_stream; stream != NULL; stream = stream->next) {
576 if (stream->is_multicast) {
577 /* open the RTP connection */
578 snprintf(session_id, sizeof(session_id), "%08x%08x",
579 av_lfg_get(&random_state), av_lfg_get(&random_state));
581 /* choose a port if none given */
582 if (stream->multicast_port == 0) {
583 stream->multicast_port = default_port;
587 dest_addr.sin_family = AF_INET;
588 dest_addr.sin_addr = stream->multicast_ip;
589 dest_addr.sin_port = htons(stream->multicast_port);
591 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
592 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
596 if (open_input_stream(rtp_c, "") < 0) {
597 http_log("Could not open input stream for stream '%s'\n",
602 /* open each RTP stream */
603 for(stream_index = 0; stream_index < stream->nb_streams;
605 dest_addr.sin_port = htons(stream->multicast_port +
607 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
608 http_log("Could not open output stream '%s/streamid=%d'\n",
609 stream->filename, stream_index);
614 /* change state to send data */
615 rtp_c->state = HTTPSTATE_SEND_DATA;
620 /* main loop of the http server */
621 static int http_server(void)
623 int server_fd = 0, rtsp_server_fd = 0;
624 int ret, delay, delay1;
625 struct pollfd *poll_table, *poll_entry;
626 HTTPContext *c, *c_next;
628 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
629 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
633 if (my_http_addr.sin_port) {
634 server_fd = socket_open_listen(&my_http_addr);
639 if (my_rtsp_addr.sin_port) {
640 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
641 if (rtsp_server_fd < 0)
645 if (!rtsp_server_fd && !server_fd) {
646 http_log("HTTP and RTSP disabled.\n");
650 http_log("AVserver started.\n");
652 start_children(first_feed);
657 poll_entry = poll_table;
659 poll_entry->fd = server_fd;
660 poll_entry->events = POLLIN;
663 if (rtsp_server_fd) {
664 poll_entry->fd = rtsp_server_fd;
665 poll_entry->events = POLLIN;
669 /* wait for events on each HTTP handle */
676 case HTTPSTATE_SEND_HEADER:
677 case RTSPSTATE_SEND_REPLY:
678 case RTSPSTATE_SEND_PACKET:
679 c->poll_entry = poll_entry;
681 poll_entry->events = POLLOUT;
684 case HTTPSTATE_SEND_DATA_HEADER:
685 case HTTPSTATE_SEND_DATA:
686 case HTTPSTATE_SEND_DATA_TRAILER:
687 if (!c->is_packetized) {
688 /* for TCP, we output as much as we can (may need to put a limit) */
689 c->poll_entry = poll_entry;
691 poll_entry->events = POLLOUT;
694 /* when avserver is doing the timing, we work by
695 looking at which packet need to be sent every
697 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
702 case HTTPSTATE_WAIT_REQUEST:
703 case HTTPSTATE_RECEIVE_DATA:
704 case HTTPSTATE_WAIT_FEED:
705 case RTSPSTATE_WAIT_REQUEST:
706 /* need to catch errors */
707 c->poll_entry = poll_entry;
709 poll_entry->events = POLLIN;/* Maybe this will work */
713 c->poll_entry = NULL;
719 /* wait for an event on one connection. We poll at least every
720 second to handle timeouts */
722 ret = poll(poll_table, poll_entry - poll_table, delay);
723 if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
724 ff_neterrno() != AVERROR(EINTR))
728 cur_time = av_gettime() / 1000;
730 if (need_to_start_children) {
731 need_to_start_children = 0;
732 start_children(first_feed);
735 /* now handle the events */
736 for(c = first_http_ctx; c != NULL; c = c_next) {
738 if (handle_connection(c) < 0) {
739 /* close and free the connection */
745 poll_entry = poll_table;
747 /* new HTTP connection request ? */
748 if (poll_entry->revents & POLLIN)
749 new_connection(server_fd, 0);
752 if (rtsp_server_fd) {
753 /* new RTSP connection request ? */
754 if (poll_entry->revents & POLLIN)
755 new_connection(rtsp_server_fd, 1);
760 /* start waiting for a new HTTP/RTSP request */
761 static void start_wait_request(HTTPContext *c, int is_rtsp)
763 c->buffer_ptr = c->buffer;
764 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
767 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
768 c->state = RTSPSTATE_WAIT_REQUEST;
770 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
771 c->state = HTTPSTATE_WAIT_REQUEST;
775 static void http_send_too_busy_reply(int fd)
778 int len = snprintf(buffer, sizeof(buffer),
779 "HTTP/1.0 503 Server too busy\r\n"
780 "Content-type: text/html\r\n"
782 "<html><head><title>Too busy</title></head><body>\r\n"
783 "<p>The server is too busy to serve your request at this time.</p>\r\n"
784 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
785 "</body></html>\r\n",
786 nb_connections, nb_max_connections);
787 send(fd, buffer, len, 0);
791 static void new_connection(int server_fd, int is_rtsp)
793 struct sockaddr_in from_addr;
796 HTTPContext *c = NULL;
798 len = sizeof(from_addr);
799 fd = accept(server_fd, (struct sockaddr *)&from_addr,
802 http_log("error during accept %s\n", strerror(errno));
805 ff_socket_nonblock(fd, 1);
807 if (nb_connections >= nb_max_connections) {
808 http_send_too_busy_reply(fd);
812 /* add a new connection */
813 c = av_mallocz(sizeof(HTTPContext));
818 c->poll_entry = NULL;
819 c->from_addr = from_addr;
820 c->buffer_size = IOBUFFER_INIT_SIZE;
821 c->buffer = av_malloc(c->buffer_size);
825 c->next = first_http_ctx;
829 start_wait_request(c, is_rtsp);
841 static void close_connection(HTTPContext *c)
843 HTTPContext **cp, *c1;
845 AVFormatContext *ctx;
849 /* remove connection from list */
850 cp = &first_http_ctx;
851 while ((*cp) != NULL) {
859 /* remove references, if any (XXX: do it faster) */
860 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
865 /* remove connection associated resources */
869 /* close each frame parser */
870 for(i=0;i<c->fmt_in->nb_streams;i++) {
871 st = c->fmt_in->streams[i];
872 if (st->codec->codec)
873 avcodec_close(st->codec);
875 avformat_close_input(&c->fmt_in);
878 /* free RTP output streams if any */
881 nb_streams = c->stream->nb_streams;
883 for(i=0;i<nb_streams;i++) {
886 av_write_trailer(ctx);
887 av_dict_free(&ctx->metadata);
888 av_free(ctx->streams[0]);
891 h = c->rtp_handles[i];
898 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
901 if (avio_open_dyn_buf(&ctx->pb) >= 0) {
902 av_write_trailer(ctx);
903 av_freep(&c->pb_buffer);
904 avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
909 for(i=0; i<ctx->nb_streams; i++)
910 av_free(ctx->streams[i]);
912 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
913 current_bandwidth -= c->stream->bandwidth;
915 /* signal that there is no feed if we are the feeder socket */
916 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
917 c->stream->feed_opened = 0;
921 av_freep(&c->pb_buffer);
922 av_freep(&c->packet_buffer);
928 static int handle_connection(HTTPContext *c)
933 case HTTPSTATE_WAIT_REQUEST:
934 case RTSPSTATE_WAIT_REQUEST:
936 if ((c->timeout - cur_time) < 0)
938 if (c->poll_entry->revents & (POLLERR | POLLHUP))
941 /* no need to read if no events */
942 if (!(c->poll_entry->revents & POLLIN))
946 len = recv(c->fd, c->buffer_ptr, 1, 0);
948 if (ff_neterrno() != AVERROR(EAGAIN) &&
949 ff_neterrno() != AVERROR(EINTR))
951 } else if (len == 0) {
954 /* search for end of request. */
956 c->buffer_ptr += len;
958 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
959 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
960 /* request found : parse it and reply */
961 if (c->state == HTTPSTATE_WAIT_REQUEST) {
962 ret = http_parse_request(c);
964 ret = rtsp_parse_request(c);
968 } else if (ptr >= c->buffer_end) {
969 /* request too long: cannot do anything */
971 } else goto read_loop;
975 case HTTPSTATE_SEND_HEADER:
976 if (c->poll_entry->revents & (POLLERR | POLLHUP))
979 /* no need to write if no events */
980 if (!(c->poll_entry->revents & POLLOUT))
982 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
984 if (ff_neterrno() != AVERROR(EAGAIN) &&
985 ff_neterrno() != AVERROR(EINTR)) {
986 /* error : close connection */
987 av_freep(&c->pb_buffer);
991 c->buffer_ptr += len;
993 c->stream->bytes_served += len;
994 c->data_count += len;
995 if (c->buffer_ptr >= c->buffer_end) {
996 av_freep(&c->pb_buffer);
1000 /* all the buffer was sent : synchronize to the incoming stream */
1001 c->state = HTTPSTATE_SEND_DATA_HEADER;
1002 c->buffer_ptr = c->buffer_end = c->buffer;
1007 case HTTPSTATE_SEND_DATA:
1008 case HTTPSTATE_SEND_DATA_HEADER:
1009 case HTTPSTATE_SEND_DATA_TRAILER:
1010 /* for packetized output, we consider we can always write (the
1011 input streams sets the speed). It may be better to verify
1012 that we do not rely too much on the kernel queues */
1013 if (!c->is_packetized) {
1014 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1017 /* no need to read if no events */
1018 if (!(c->poll_entry->revents & POLLOUT))
1021 if (http_send_data(c) < 0)
1023 /* close connection if trailer sent */
1024 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
1027 case HTTPSTATE_RECEIVE_DATA:
1028 /* no need to read if no events */
1029 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1031 if (!(c->poll_entry->revents & POLLIN))
1033 if (http_receive_data(c) < 0)
1036 case HTTPSTATE_WAIT_FEED:
1037 /* no need to read if no events */
1038 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1041 /* nothing to do, we'll be waken up by incoming feed packets */
1044 case RTSPSTATE_SEND_REPLY:
1045 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1046 av_freep(&c->pb_buffer);
1049 /* no need to write if no events */
1050 if (!(c->poll_entry->revents & POLLOUT))
1052 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1054 if (ff_neterrno() != AVERROR(EAGAIN) &&
1055 ff_neterrno() != AVERROR(EINTR)) {
1056 /* error : close connection */
1057 av_freep(&c->pb_buffer);
1061 c->buffer_ptr += len;
1062 c->data_count += len;
1063 if (c->buffer_ptr >= c->buffer_end) {
1064 /* all the buffer was sent : wait for a new request */
1065 av_freep(&c->pb_buffer);
1066 start_wait_request(c, 1);
1070 case RTSPSTATE_SEND_PACKET:
1071 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1072 av_freep(&c->packet_buffer);
1075 /* no need to write if no events */
1076 if (!(c->poll_entry->revents & POLLOUT))
1078 len = send(c->fd, c->packet_buffer_ptr,
1079 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1081 if (ff_neterrno() != AVERROR(EAGAIN) &&
1082 ff_neterrno() != AVERROR(EINTR)) {
1083 /* error : close connection */
1084 av_freep(&c->packet_buffer);
1088 c->packet_buffer_ptr += len;
1089 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1090 /* all the buffer was sent : wait for a new request */
1091 av_freep(&c->packet_buffer);
1092 c->state = RTSPSTATE_WAIT_REQUEST;
1096 case HTTPSTATE_READY:
1105 static int extract_rates(char *rates, int ratelen, const char *request)
1109 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1110 if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1111 const char *q = p + 7;
1113 while (*q && *q != '\n' && av_isspace(*q))
1116 if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1122 memset(rates, 0xff, ratelen);
1125 while (*q && *q != '\n' && *q != ':')
1128 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1132 if (stream_no < ratelen && stream_no >= 0)
1133 rates[stream_no] = rate_no;
1135 while (*q && *q != '\n' && !av_isspace(*q))
1142 p = strchr(p, '\n');
1152 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1155 int best_bitrate = 100000000;
1158 for (i = 0; i < feed->nb_streams; i++) {
1159 AVCodecContext *feed_codec = feed->streams[i]->codec;
1161 if (feed_codec->codec_id != codec->codec_id ||
1162 feed_codec->sample_rate != codec->sample_rate ||
1163 feed_codec->width != codec->width ||
1164 feed_codec->height != codec->height)
1167 /* Potential stream */
1169 /* We want the fastest stream less than bit_rate, or the slowest
1170 * faster than bit_rate
1173 if (feed_codec->bit_rate <= bit_rate) {
1174 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1175 best_bitrate = feed_codec->bit_rate;
1179 if (feed_codec->bit_rate < best_bitrate) {
1180 best_bitrate = feed_codec->bit_rate;
1189 static int modify_current_stream(HTTPContext *c, char *rates)
1192 FFStream *req = c->stream;
1193 int action_required = 0;
1195 /* Not much we can do for a feed */
1199 for (i = 0; i < req->nb_streams; i++) {
1200 AVCodecContext *codec = req->streams[i]->codec;
1204 c->switch_feed_streams[i] = req->feed_streams[i];
1207 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1210 /* Wants off or slow */
1211 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1213 /* This doesn't work well when it turns off the only stream! */
1214 c->switch_feed_streams[i] = -2;
1215 c->feed_streams[i] = -2;
1220 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1221 action_required = 1;
1224 return action_required;
1227 /* XXX: factorize in utils.c ? */
1228 /* XXX: take care with different space meaning */
1229 static void skip_spaces(const char **pp)
1233 while (*p == ' ' || *p == '\t')
1238 static void get_word(char *buf, int buf_size, const char **pp)
1246 while (!av_isspace(*p) && *p != '\0') {
1247 if ((q - buf) < buf_size - 1)
1256 static void get_arg(char *buf, int buf_size, const char **pp)
1263 while (av_isspace(*p)) p++;
1266 if (*p == '\"' || *p == '\'')
1278 if ((q - buf) < buf_size - 1)
1283 if (quote && *p == quote)
1288 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1289 const char *p, const char *filename, int line_num)
1295 get_arg(arg, sizeof(arg), &p);
1296 if (av_strcasecmp(arg, "allow") == 0)
1297 acl.action = IP_ALLOW;
1298 else if (av_strcasecmp(arg, "deny") == 0)
1299 acl.action = IP_DENY;
1301 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1302 filename, line_num, arg);
1306 get_arg(arg, sizeof(arg), &p);
1308 if (resolve_host(&acl.first, arg) != 0) {
1309 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1310 filename, line_num, arg);
1313 acl.last = acl.first;
1315 get_arg(arg, sizeof(arg), &p);
1318 if (resolve_host(&acl.last, arg) != 0) {
1319 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1320 filename, line_num, arg);
1326 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1327 IPAddressACL **naclp = 0;
1333 naclp = &stream->acl;
1339 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1340 filename, line_num);
1346 naclp = &(*naclp)->next;
1354 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1359 IPAddressACL *acl = NULL;
1363 f = fopen(stream->dynamic_acl, "r");
1365 perror(stream->dynamic_acl);
1369 acl = av_mallocz(sizeof(IPAddressACL));
1373 if (fgets(line, sizeof(line), f) == NULL)
1377 while (av_isspace(*p))
1379 if (*p == '\0' || *p == '#')
1381 get_arg(cmd, sizeof(cmd), &p);
1383 if (!av_strcasecmp(cmd, "ACL"))
1384 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1391 static void free_acl_list(IPAddressACL *in_acl)
1393 IPAddressACL *pacl,*pacl2;
1403 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1405 enum IPAddressAction last_action = IP_DENY;
1407 struct in_addr *src = &c->from_addr.sin_addr;
1408 unsigned long src_addr = src->s_addr;
1410 for (acl = in_acl; acl; acl = acl->next) {
1411 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1412 return (acl->action == IP_ALLOW) ? 1 : 0;
1413 last_action = acl->action;
1416 /* Nothing matched, so return not the last action */
1417 return (last_action == IP_DENY) ? 1 : 0;
1420 static int validate_acl(FFStream *stream, HTTPContext *c)
1426 /* if stream->acl is null validate_acl_list will return 1 */
1427 ret = validate_acl_list(stream->acl, c);
1429 if (stream->dynamic_acl[0]) {
1430 acl = parse_dynamic_acl(stream, c);
1432 ret = validate_acl_list(acl, c);
1440 /* compute the real filename of a file by matching it without its
1441 extensions to all the stream filenames */
1442 static void compute_real_filename(char *filename, int max_size)
1449 /* compute filename by matching without the file extensions */
1450 av_strlcpy(file1, filename, sizeof(file1));
1451 p = strrchr(file1, '.');
1454 for(stream = first_stream; stream != NULL; stream = stream->next) {
1455 av_strlcpy(file2, stream->filename, sizeof(file2));
1456 p = strrchr(file2, '.');
1459 if (!strcmp(file1, file2)) {
1460 av_strlcpy(filename, stream->filename, max_size);
1475 /* parse http request and prepare header */
1476 static int http_parse_request(HTTPContext *c)
1480 enum RedirType redir_type;
1482 char info[1024], filename[1024];
1486 const char *mime_type;
1490 const char *useragent = 0;
1493 get_word(cmd, sizeof(cmd), &p);
1494 av_strlcpy(c->method, cmd, sizeof(c->method));
1496 if (!strcmp(cmd, "GET"))
1498 else if (!strcmp(cmd, "POST"))
1503 get_word(url, sizeof(url), &p);
1504 av_strlcpy(c->url, url, sizeof(c->url));
1506 get_word(protocol, sizeof(protocol), (const char **)&p);
1507 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1510 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1513 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1515 /* find the filename and the optional info string in the request */
1516 p1 = strchr(url, '?');
1518 av_strlcpy(info, p1, sizeof(info));
1523 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1525 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1526 if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1528 if (*useragent && *useragent != '\n' && av_isspace(*useragent))
1532 p = strchr(p, '\n');
1539 redir_type = REDIR_NONE;
1540 if (av_match_ext(filename, "asx")) {
1541 redir_type = REDIR_ASX;
1542 filename[strlen(filename)-1] = 'f';
1543 } else if (av_match_ext(filename, "asf") &&
1544 (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1545 /* if this isn't WMP or lookalike, return the redirector file */
1546 redir_type = REDIR_ASF;
1547 } else if (av_match_ext(filename, "rpm,ram")) {
1548 redir_type = REDIR_RAM;
1549 strcpy(filename + strlen(filename)-2, "m");
1550 } else if (av_match_ext(filename, "rtsp")) {
1551 redir_type = REDIR_RTSP;
1552 compute_real_filename(filename, sizeof(filename) - 1);
1553 } else if (av_match_ext(filename, "sdp")) {
1554 redir_type = REDIR_SDP;
1555 compute_real_filename(filename, sizeof(filename) - 1);
1558 // "redirect" / request to index.html
1559 if (!strlen(filename))
1560 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1562 stream = first_stream;
1563 while (stream != NULL) {
1564 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1566 stream = stream->next;
1568 if (stream == NULL) {
1569 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1570 http_log("File '%s' not found\n", url);
1575 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1576 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1578 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1579 c->http_error = 301;
1581 q += snprintf(q, c->buffer_size,
1582 "HTTP/1.0 301 Moved\r\n"
1584 "Content-type: text/html\r\n"
1586 "<html><head><title>Moved</title></head><body>\r\n"
1587 "You should be <a href=\"%s\">redirected</a>.\r\n"
1588 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1589 /* prepare output buffer */
1590 c->buffer_ptr = c->buffer;
1592 c->state = HTTPSTATE_SEND_HEADER;
1596 /* If this is WMP, get the rate information */
1597 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1598 if (modify_current_stream(c, ratebuf)) {
1599 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1600 if (c->switch_feed_streams[i] >= 0)
1601 c->switch_feed_streams[i] = -1;
1606 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1607 current_bandwidth += stream->bandwidth;
1609 /* If already streaming this feed, do not let start another feeder. */
1610 if (stream->feed_opened) {
1611 snprintf(msg, sizeof(msg), "This feed is already being received.");
1612 http_log("Feed '%s' already being received\n", stream->feed_filename);
1616 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1617 c->http_error = 503;
1619 q += snprintf(q, c->buffer_size,
1620 "HTTP/1.0 503 Server too busy\r\n"
1621 "Content-type: text/html\r\n"
1623 "<html><head><title>Too busy</title></head><body>\r\n"
1624 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1625 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1626 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1627 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1628 /* prepare output buffer */
1629 c->buffer_ptr = c->buffer;
1631 c->state = HTTPSTATE_SEND_HEADER;
1635 if (redir_type != REDIR_NONE) {
1636 const char *hostinfo = 0;
1638 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1639 if (av_strncasecmp(p, "Host:", 5) == 0) {
1643 p = strchr(p, '\n');
1654 while (av_isspace(*hostinfo))
1657 eoh = strchr(hostinfo, '\n');
1659 if (eoh[-1] == '\r')
1662 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1663 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1664 hostbuf[eoh - hostinfo] = 0;
1666 c->http_error = 200;
1668 switch(redir_type) {
1670 q += snprintf(q, c->buffer_size,
1671 "HTTP/1.0 200 ASX Follows\r\n"
1672 "Content-type: video/x-ms-asf\r\n"
1674 "<ASX Version=\"3\">\r\n"
1675 //"<!-- Autogenerated by avserver -->\r\n"
1676 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1677 "</ASX>\r\n", hostbuf, filename, info);
1680 q += snprintf(q, c->buffer_size,
1681 "HTTP/1.0 200 RAM Follows\r\n"
1682 "Content-type: audio/x-pn-realaudio\r\n"
1684 "# Autogenerated by avserver\r\n"
1685 "http://%s/%s%s\r\n", hostbuf, filename, info);
1688 q += snprintf(q, c->buffer_size,
1689 "HTTP/1.0 200 ASF Redirect follows\r\n"
1690 "Content-type: video/x-ms-asf\r\n"
1693 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1697 char hostname[256], *p;
1698 /* extract only hostname */
1699 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1700 p = strrchr(hostname, ':');
1703 q += snprintf(q, c->buffer_size,
1704 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1705 /* XXX: incorrect mime type ? */
1706 "Content-type: application/x-rtsp\r\n"
1708 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1716 struct sockaddr_in my_addr;
1718 q += snprintf(q, c->buffer_size,
1719 "HTTP/1.0 200 OK\r\n"
1720 "Content-type: application/sdp\r\n"
1723 len = sizeof(my_addr);
1724 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1726 /* XXX: should use a dynamic buffer */
1727 sdp_data_size = prepare_sdp_description(stream,
1730 if (sdp_data_size > 0) {
1731 memcpy(q, sdp_data, sdp_data_size);
1743 /* prepare output buffer */
1744 c->buffer_ptr = c->buffer;
1746 c->state = HTTPSTATE_SEND_HEADER;
1752 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1756 stream->conns_served++;
1758 /* XXX: add there authenticate and IP match */
1761 /* if post, it means a feed is being sent */
1762 if (!stream->is_feed) {
1763 /* However it might be a status report from WMP! Let us log the
1764 * data as it might come in handy one day. */
1765 const char *logline = 0;
1768 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1769 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1773 if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1774 client_id = strtol(p + 18, 0, 10);
1775 p = strchr(p, '\n');
1783 char *eol = strchr(logline, '\n');
1788 if (eol[-1] == '\r')
1790 http_log("%.*s\n", (int) (eol - logline), logline);
1791 c->suppress_log = 1;
1796 http_log("\nGot request:\n%s\n", c->buffer);
1799 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1802 /* Now we have to find the client_id */
1803 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1804 if (wmpc->wmp_client_id == client_id)
1808 if (wmpc && modify_current_stream(wmpc, ratebuf))
1809 wmpc->switch_pending = 1;
1812 snprintf(msg, sizeof(msg), "POST command not handled");
1816 if (http_start_receive_data(c) < 0) {
1817 snprintf(msg, sizeof(msg), "could not open feed");
1821 c->state = HTTPSTATE_RECEIVE_DATA;
1826 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1827 http_log("\nGot request:\n%s\n", c->buffer);
1830 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1833 /* open input stream */
1834 if (open_input_stream(c, info) < 0) {
1835 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1839 /* prepare http header */
1841 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1842 mime_type = c->stream->fmt->mime_type;
1844 mime_type = "application/x-octet-stream";
1845 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1847 /* for asf, we need extra headers */
1848 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1849 /* Need to allocate a client id */
1851 c->wmp_client_id = av_lfg_get(&random_state);
1853 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);
1855 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1856 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1858 /* prepare output buffer */
1860 c->buffer_ptr = c->buffer;
1862 c->state = HTTPSTATE_SEND_HEADER;
1865 c->http_error = 404;
1867 q += snprintf(q, c->buffer_size,
1868 "HTTP/1.0 404 Not Found\r\n"
1869 "Content-type: text/html\r\n"
1872 "<head><title>404 Not Found</title></head>\n"
1875 /* prepare output buffer */
1876 c->buffer_ptr = c->buffer;
1878 c->state = HTTPSTATE_SEND_HEADER;
1882 c->http_error = 200; /* horrible : we use this value to avoid
1883 going to the send data state */
1884 c->state = HTTPSTATE_SEND_HEADER;
1888 static void fmt_bytecount(AVIOContext *pb, int64_t count)
1890 static const char suffix[] = " kMGTP";
1893 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1895 avio_printf(pb, "%"PRId64"%c", count, *s);
1898 static void compute_status(HTTPContext *c)
1907 if (avio_open_dyn_buf(&pb) < 0) {
1908 /* XXX: return an error ? */
1909 c->buffer_ptr = c->buffer;
1910 c->buffer_end = c->buffer;
1914 avio_printf(pb, "HTTP/1.0 200 OK\r\n");
1915 avio_printf(pb, "Content-type: %s\r\n", "text/html");
1916 avio_printf(pb, "Pragma: no-cache\r\n");
1917 avio_printf(pb, "\r\n");
1919 avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1920 if (c->stream->feed_filename[0])
1921 avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1922 avio_printf(pb, "</head>\n<body>");
1923 avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
1925 avio_printf(pb, "<h2>Available Streams</h2>\n");
1926 avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
1927 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");
1928 stream = first_stream;
1929 while (stream != NULL) {
1930 char sfilename[1024];
1933 if (stream->feed != stream) {
1934 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1935 eosf = sfilename + strlen(sfilename);
1936 if (eosf - sfilename >= 4) {
1937 if (strcmp(eosf - 4, ".asf") == 0)
1938 strcpy(eosf - 4, ".asx");
1939 else if (strcmp(eosf - 3, ".rm") == 0)
1940 strcpy(eosf - 3, ".ram");
1941 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1942 /* generate a sample RTSP director if
1943 unicast. Generate an SDP redirector if
1945 eosf = strrchr(sfilename, '.');
1947 eosf = sfilename + strlen(sfilename);
1948 if (stream->is_multicast)
1949 strcpy(eosf, ".sdp");
1951 strcpy(eosf, ".rtsp");
1955 avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1956 sfilename, stream->filename);
1957 avio_printf(pb, "<td align=right> %d <td align=right> ",
1958 stream->conns_served);
1959 fmt_bytecount(pb, stream->bytes_served);
1960 switch(stream->stream_type) {
1961 case STREAM_TYPE_LIVE: {
1962 int audio_bit_rate = 0;
1963 int video_bit_rate = 0;
1964 const char *audio_codec_name = "";
1965 const char *video_codec_name = "";
1966 const char *audio_codec_name_extra = "";
1967 const char *video_codec_name_extra = "";
1969 for(i=0;i<stream->nb_streams;i++) {
1970 AVStream *st = stream->streams[i];
1971 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1972 switch(st->codec->codec_type) {
1973 case AVMEDIA_TYPE_AUDIO:
1974 audio_bit_rate += st->codec->bit_rate;
1976 if (*audio_codec_name)
1977 audio_codec_name_extra = "...";
1978 audio_codec_name = codec->name;
1981 case AVMEDIA_TYPE_VIDEO:
1982 video_bit_rate += st->codec->bit_rate;
1984 if (*video_codec_name)
1985 video_codec_name_extra = "...";
1986 video_codec_name = codec->name;
1989 case AVMEDIA_TYPE_DATA:
1990 video_bit_rate += st->codec->bit_rate;
1996 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",
1999 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
2000 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
2002 avio_printf(pb, "<td>%s", stream->feed->filename);
2004 avio_printf(pb, "<td>%s", stream->feed_filename);
2005 avio_printf(pb, "\n");
2009 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
2013 stream = stream->next;
2015 avio_printf(pb, "</table>\n");
2017 stream = first_stream;
2018 while (stream != NULL) {
2019 if (stream->feed == stream) {
2020 avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
2022 avio_printf(pb, "Running as pid %d.\n", stream->pid);
2024 #if defined(linux) && !defined(CONFIG_NOCUTILS)
2029 /* This is somewhat linux specific I guess */
2030 snprintf(ps_cmd, sizeof(ps_cmd),
2031 "ps -o \"%%cpu,cputime\" --no-headers %d",
2034 pid_stat = popen(ps_cmd, "r");
2039 if (fscanf(pid_stat, "%10s %64s", cpuperc,
2041 avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2049 avio_printf(pb, "<p>");
2051 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");
2053 for (i = 0; i < stream->nb_streams; i++) {
2054 AVStream *st = stream->streams[i];
2055 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2056 const char *type = "unknown";
2057 char parameters[64];
2061 switch(st->codec->codec_type) {
2062 case AVMEDIA_TYPE_AUDIO:
2064 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2066 case AVMEDIA_TYPE_VIDEO:
2068 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2069 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2074 avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2075 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2077 avio_printf(pb, "</table>\n");
2080 stream = stream->next;
2083 /* connection status */
2084 avio_printf(pb, "<h2>Connection Status</h2>\n");
2086 avio_printf(pb, "Number of connections: %d / %d<br>\n",
2087 nb_connections, nb_max_connections);
2089 avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2090 current_bandwidth, max_bandwidth);
2092 avio_printf(pb, "<table>\n");
2093 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");
2094 c1 = first_http_ctx;
2096 while (c1 != NULL) {
2102 for (j = 0; j < c1->stream->nb_streams; j++) {
2103 if (!c1->stream->feed)
2104 bitrate += c1->stream->streams[j]->codec->bit_rate;
2105 else if (c1->feed_streams[j] >= 0)
2106 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2111 p = inet_ntoa(c1->from_addr.sin_addr);
2112 avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2114 c1->stream ? c1->stream->filename : "",
2115 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2118 http_state[c1->state]);
2119 fmt_bytecount(pb, bitrate);
2120 avio_printf(pb, "<td align=right>");
2121 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2122 avio_printf(pb, "<td align=right>");
2123 fmt_bytecount(pb, c1->data_count);
2124 avio_printf(pb, "\n");
2127 avio_printf(pb, "</table>\n");
2132 avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
2133 avio_printf(pb, "</body>\n</html>\n");
2135 len = avio_close_dyn_buf(pb, &c->pb_buffer);
2136 c->buffer_ptr = c->pb_buffer;
2137 c->buffer_end = c->pb_buffer + len;
2140 static int open_input_stream(HTTPContext *c, const char *info)
2143 char input_filename[1024];
2144 AVFormatContext *s = NULL;
2148 /* find file name */
2149 if (c->stream->feed) {
2150 strcpy(input_filename, c->stream->feed->feed_filename);
2151 /* compute position (absolute time) */
2152 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2153 if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
2155 } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2156 int prebuffer = strtol(buf, 0, 10);
2157 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2159 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2161 strcpy(input_filename, c->stream->feed_filename);
2162 /* compute position (relative time) */
2163 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2164 if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
2169 if (input_filename[0] == '\0')
2173 if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2174 http_log("could not open %s: %d\n", input_filename, ret);
2177 s->flags |= AVFMT_FLAG_GENPTS;
2179 if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2180 http_log("Could not find stream info '%s'\n", input_filename);
2181 avformat_close_input(&s);
2185 /* choose stream as clock source (we favorize video stream if
2186 present) for packet sending */
2187 c->pts_stream_index = 0;
2188 for(i=0;i<c->stream->nb_streams;i++) {
2189 if (c->pts_stream_index == 0 &&
2190 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2191 c->pts_stream_index = i;
2195 if (c->fmt_in->iformat->read_seek)
2196 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2197 /* set the start time (needed for maxtime and RTP packet timing) */
2198 c->start_time = cur_time;
2199 c->first_pts = AV_NOPTS_VALUE;
2203 /* return the server clock (in us) */
2204 static int64_t get_server_clock(HTTPContext *c)
2206 /* compute current pts value from system time */
2207 return (cur_time - c->start_time) * 1000;
2210 /* return the estimated time at which the current packet must be sent
2212 static int64_t get_packet_send_clock(HTTPContext *c)
2214 int bytes_left, bytes_sent, frame_bytes;
2216 frame_bytes = c->cur_frame_bytes;
2217 if (frame_bytes <= 0)
2220 bytes_left = c->buffer_end - c->buffer_ptr;
2221 bytes_sent = frame_bytes - bytes_left;
2222 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2227 static int http_prepare_data(HTTPContext *c)
2230 AVFormatContext *ctx;
2232 av_freep(&c->pb_buffer);
2234 case HTTPSTATE_SEND_DATA_HEADER:
2235 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2236 av_dict_set(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2237 av_dict_set(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2238 av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2239 av_dict_set(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2241 c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2243 for(i=0;i<c->stream->nb_streams;i++) {
2245 c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2246 /* if file or feed, then just take streams from FFStream struct */
2247 if (!c->stream->feed ||
2248 c->stream->feed == c->stream)
2249 src = c->stream->streams[i];
2251 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2253 *(c->fmt_ctx.streams[i]) = *src;
2254 c->fmt_ctx.streams[i]->priv_data = 0;
2255 c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2256 AVStream, not in codec */
2258 /* set output format parameters */
2259 c->fmt_ctx.oformat = c->stream->fmt;
2260 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2262 c->got_key_frame = 0;
2264 /* prepare header and save header data in a stream */
2265 if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2266 /* XXX: potential leak */
2269 c->fmt_ctx.pb->seekable = 0;
2272 * HACK to avoid mpeg ps muxer to spit many underflow errors
2273 * Default value from Libav
2274 * Try to set it use configuration option
2276 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2278 if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
2279 http_log("Error writing output header\n");
2282 av_dict_free(&c->fmt_ctx.metadata);
2284 len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2285 c->buffer_ptr = c->pb_buffer;
2286 c->buffer_end = c->pb_buffer + len;
2288 c->state = HTTPSTATE_SEND_DATA;
2289 c->last_packet_sent = 0;
2291 case HTTPSTATE_SEND_DATA:
2292 /* find a new packet */
2293 /* read a packet from the input stream */
2294 if (c->stream->feed)
2295 ffm_set_write_index(c->fmt_in,
2296 c->stream->feed->feed_write_index,
2297 c->stream->feed->feed_size);
2299 if (c->stream->max_time &&
2300 c->stream->max_time + c->start_time - cur_time < 0)
2301 /* We have timed out */
2302 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2306 ret = av_read_frame(c->fmt_in, &pkt);
2308 if (c->stream->feed) {
2309 /* if coming from feed, it means we reached the end of the
2310 ffm file, so must wait for more data */
2311 c->state = HTTPSTATE_WAIT_FEED;
2312 return 1; /* state changed */
2313 } else if (ret == AVERROR(EAGAIN)) {
2314 /* input not ready, come back later */
2317 if (c->stream->loop) {
2318 avformat_close_input(&c->fmt_in);
2319 if (open_input_stream(c, "") < 0)
2324 /* must send trailer now because eof or error */
2325 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2329 int source_index = pkt.stream_index;
2330 /* update first pts if needed */
2331 if (c->first_pts == AV_NOPTS_VALUE) {
2332 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2333 c->start_time = cur_time;
2335 /* send it to the appropriate stream */
2336 if (c->stream->feed) {
2337 /* if coming from a feed, select the right stream */
2338 if (c->switch_pending) {
2339 c->switch_pending = 0;
2340 for(i=0;i<c->stream->nb_streams;i++) {
2341 if (c->switch_feed_streams[i] == pkt.stream_index)
2342 if (pkt.flags & AV_PKT_FLAG_KEY)
2343 c->switch_feed_streams[i] = -1;
2344 if (c->switch_feed_streams[i] >= 0)
2345 c->switch_pending = 1;
2348 for(i=0;i<c->stream->nb_streams;i++) {
2349 if (c->stream->feed_streams[i] == pkt.stream_index) {
2350 AVStream *st = c->fmt_in->streams[source_index];
2351 pkt.stream_index = i;
2352 if (pkt.flags & AV_PKT_FLAG_KEY &&
2353 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2354 c->stream->nb_streams == 1))
2355 c->got_key_frame = 1;
2356 if (!c->stream->send_on_key || c->got_key_frame)
2361 AVCodecContext *codec;
2362 AVStream *ist, *ost;
2364 ist = c->fmt_in->streams[source_index];
2365 /* specific handling for RTP: we use several
2366 output stream (one for each RTP
2367 connection). XXX: need more abstract handling */
2368 if (c->is_packetized) {
2369 /* compute send time and duration */
2370 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2371 c->cur_pts -= c->first_pts;
2372 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2373 /* find RTP context */
2374 c->packet_stream_index = pkt.stream_index;
2375 ctx = c->rtp_ctx[c->packet_stream_index];
2377 av_free_packet(&pkt);
2380 codec = ctx->streams[0]->codec;
2381 /* only one stream per RTP connection */
2382 pkt.stream_index = 0;
2386 codec = ctx->streams[pkt.stream_index]->codec;
2389 if (c->is_packetized) {
2390 int max_packet_size;
2391 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2392 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2394 max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
2395 ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2397 ret = avio_open_dyn_buf(&ctx->pb);
2400 /* XXX: potential leak */
2403 ost = ctx->streams[pkt.stream_index];
2405 ctx->pb->seekable = 0;
2406 if (pkt.dts != AV_NOPTS_VALUE)
2407 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2408 if (pkt.pts != AV_NOPTS_VALUE)
2409 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2410 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2411 if (av_write_frame(ctx, &pkt) < 0) {
2412 http_log("Error writing frame to output\n");
2413 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2416 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2417 c->cur_frame_bytes = len;
2418 c->buffer_ptr = c->pb_buffer;
2419 c->buffer_end = c->pb_buffer + len;
2421 codec->frame_number++;
2423 av_free_packet(&pkt);
2427 av_free_packet(&pkt);
2432 case HTTPSTATE_SEND_DATA_TRAILER:
2433 /* last packet test ? */
2434 if (c->last_packet_sent || c->is_packetized)
2437 /* prepare header */
2438 if (avio_open_dyn_buf(&ctx->pb) < 0) {
2439 /* XXX: potential leak */
2442 c->fmt_ctx.pb->seekable = 0;
2443 av_write_trailer(ctx);
2444 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2445 c->buffer_ptr = c->pb_buffer;
2446 c->buffer_end = c->pb_buffer + len;
2448 c->last_packet_sent = 1;
2454 /* should convert the format at the same time */
2455 /* send data starting at c->buffer_ptr to the output connection
2456 (either UDP or TCP connection) */
2457 static int http_send_data(HTTPContext *c)
2462 if (c->buffer_ptr >= c->buffer_end) {
2463 ret = http_prepare_data(c);
2467 /* state change requested */
2470 if (c->is_packetized) {
2471 /* RTP data output */
2472 len = c->buffer_end - c->buffer_ptr;
2474 /* fail safe - should never happen */
2476 c->buffer_ptr = c->buffer_end;
2479 len = (c->buffer_ptr[0] << 24) |
2480 (c->buffer_ptr[1] << 16) |
2481 (c->buffer_ptr[2] << 8) |
2483 if (len > (c->buffer_end - c->buffer_ptr))
2485 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2486 /* nothing to send yet: we can wait */
2490 c->data_count += len;
2491 update_datarate(&c->datarate, c->data_count);
2493 c->stream->bytes_served += len;
2495 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2496 /* RTP packets are sent inside the RTSP TCP connection */
2498 int interleaved_index, size;
2500 HTTPContext *rtsp_c;
2503 /* if no RTSP connection left, error */
2506 /* if already sending something, then wait. */
2507 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2509 if (avio_open_dyn_buf(&pb) < 0)
2511 interleaved_index = c->packet_stream_index * 2;
2512 /* RTCP packets are sent at odd indexes */
2513 if (c->buffer_ptr[1] == 200)
2514 interleaved_index++;
2515 /* write RTSP TCP header */
2517 header[1] = interleaved_index;
2518 header[2] = len >> 8;
2520 avio_write(pb, header, 4);
2521 /* write RTP packet data */
2523 avio_write(pb, c->buffer_ptr, len);
2524 size = avio_close_dyn_buf(pb, &c->packet_buffer);
2525 /* prepare asynchronous TCP sending */
2526 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2527 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2528 c->buffer_ptr += len;
2530 /* send everything we can NOW */
2531 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2532 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2534 rtsp_c->packet_buffer_ptr += len;
2535 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2536 /* if we could not send all the data, we will
2537 send it later, so a new state is needed to
2538 "lock" the RTSP TCP connection */
2539 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2542 /* all data has been sent */
2543 av_freep(&c->packet_buffer);
2545 /* send RTP packet directly in UDP */
2547 ffurl_write(c->rtp_handles[c->packet_stream_index],
2548 c->buffer_ptr, len);
2549 c->buffer_ptr += len;
2550 /* here we continue as we can send several packets per 10 ms slot */
2553 /* TCP data output */
2554 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2556 if (ff_neterrno() != AVERROR(EAGAIN) &&
2557 ff_neterrno() != AVERROR(EINTR))
2558 /* error : close connection */
2563 c->buffer_ptr += len;
2565 c->data_count += len;
2566 update_datarate(&c->datarate, c->data_count);
2568 c->stream->bytes_served += len;
2576 static int http_start_receive_data(HTTPContext *c)
2580 if (c->stream->feed_opened)
2583 /* Don't permit writing to this one */
2584 if (c->stream->readonly)
2588 fd = open(c->stream->feed_filename, O_RDWR);
2590 http_log("Error opening feeder file: %s\n", strerror(errno));
2595 if (c->stream->truncate) {
2596 /* truncate feed file */
2597 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2598 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2599 if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
2600 http_log("Error truncating feed file: %s\n", strerror(errno));
2604 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2605 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2610 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2611 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2612 lseek(fd, 0, SEEK_SET);
2614 /* init buffer input */
2615 c->buffer_ptr = c->buffer;
2616 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2617 c->stream->feed_opened = 1;
2618 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2622 static int http_receive_data(HTTPContext *c)
2625 int len, loop_run = 0;
2627 while (c->chunked_encoding && !c->chunk_size &&
2628 c->buffer_end > c->buffer_ptr) {
2629 /* read chunk header, if present */
2630 len = recv(c->fd, c->buffer_ptr, 1, 0);
2633 if (ff_neterrno() != AVERROR(EAGAIN) &&
2634 ff_neterrno() != AVERROR(EINTR))
2635 /* error : close connection */
2638 } else if (len == 0) {
2639 /* end of connection : close it */
2641 } else if (c->buffer_ptr - c->buffer >= 2 &&
2642 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2643 c->chunk_size = strtol(c->buffer, 0, 16);
2644 if (c->chunk_size == 0) // end of stream
2646 c->buffer_ptr = c->buffer;
2648 } else if (++loop_run > 10) {
2649 /* no chunk header, abort */
2656 if (c->buffer_end > c->buffer_ptr) {
2657 len = recv(c->fd, c->buffer_ptr,
2658 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2660 if (ff_neterrno() != AVERROR(EAGAIN) &&
2661 ff_neterrno() != AVERROR(EINTR))
2662 /* error : close connection */
2664 } else if (len == 0)
2665 /* end of connection : close it */
2668 c->chunk_size -= len;
2669 c->buffer_ptr += len;
2670 c->data_count += len;
2671 update_datarate(&c->datarate, c->data_count);
2675 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2676 if (c->buffer[0] != 'f' ||
2677 c->buffer[1] != 'm') {
2678 http_log("Feed stream has become desynchronized -- disconnecting\n");
2683 if (c->buffer_ptr >= c->buffer_end) {
2684 FFStream *feed = c->stream;
2685 /* a packet has been received : write it in the store, except
2687 if (c->data_count > FFM_PACKET_SIZE) {
2688 /* XXX: use llseek or url_seek */
2689 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2690 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2691 http_log("Error writing to feed file: %s\n", strerror(errno));
2695 feed->feed_write_index += FFM_PACKET_SIZE;
2696 /* update file size */
2697 if (feed->feed_write_index > c->stream->feed_size)
2698 feed->feed_size = feed->feed_write_index;
2700 /* handle wrap around if max file size reached */
2701 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2702 feed->feed_write_index = FFM_PACKET_SIZE;
2705 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2706 http_log("Error writing index to feed file: %s\n", strerror(errno));
2710 /* wake up any waiting connections */
2711 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2712 if (c1->state == HTTPSTATE_WAIT_FEED &&
2713 c1->stream->feed == c->stream->feed)
2714 c1->state = HTTPSTATE_SEND_DATA;
2717 /* We have a header in our hands that contains useful data */
2718 AVFormatContext *s = avformat_alloc_context();
2720 AVInputFormat *fmt_in;
2726 /* use feed output format name to find corresponding input format */
2727 fmt_in = av_find_input_format(feed->fmt->name);
2731 pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2732 0, NULL, NULL, NULL, NULL);
2736 if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2741 /* Now we have the actual streams */
2742 if (s->nb_streams != feed->nb_streams) {
2743 avformat_close_input(&s);
2745 http_log("Feed '%s' stream number does not match registered feed\n",
2746 c->stream->feed_filename);
2750 for (i = 0; i < s->nb_streams; i++) {
2751 AVStream *fst = feed->streams[i];
2752 AVStream *st = s->streams[i];
2753 avcodec_copy_context(fst->codec, st->codec);
2756 avformat_close_input(&s);
2759 c->buffer_ptr = c->buffer;
2764 c->stream->feed_opened = 0;
2766 /* wake up any waiting connections to stop waiting for feed */
2767 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2768 if (c1->state == HTTPSTATE_WAIT_FEED &&
2769 c1->stream->feed == c->stream->feed)
2770 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2775 /********************************************************************/
2778 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2785 switch(error_number) {
2786 case RTSP_STATUS_OK:
2789 case RTSP_STATUS_METHOD:
2790 str = "Method Not Allowed";
2792 case RTSP_STATUS_BANDWIDTH:
2793 str = "Not Enough Bandwidth";
2795 case RTSP_STATUS_SESSION:
2796 str = "Session Not Found";
2798 case RTSP_STATUS_STATE:
2799 str = "Method Not Valid in This State";
2801 case RTSP_STATUS_AGGREGATE:
2802 str = "Aggregate operation not allowed";
2804 case RTSP_STATUS_ONLY_AGGREGATE:
2805 str = "Only aggregate operation allowed";
2807 case RTSP_STATUS_TRANSPORT:
2808 str = "Unsupported transport";
2810 case RTSP_STATUS_INTERNAL:
2811 str = "Internal Server Error";
2813 case RTSP_STATUS_SERVICE:
2814 str = "Service Unavailable";
2816 case RTSP_STATUS_VERSION:
2817 str = "RTSP Version not supported";
2820 str = "Unknown Error";
2824 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2825 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2827 /* output GMT time */
2830 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2831 avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2834 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2836 rtsp_reply_header(c, error_number);
2837 avio_printf(c->pb, "\r\n");
2840 static int rtsp_parse_request(HTTPContext *c)
2842 const char *p, *p1, *p2;
2848 RTSPMessageHeader header1 = { 0 }, *header = &header1;
2850 c->buffer_ptr[0] = '\0';
2853 get_word(cmd, sizeof(cmd), &p);
2854 get_word(url, sizeof(url), &p);
2855 get_word(protocol, sizeof(protocol), &p);
2857 av_strlcpy(c->method, cmd, sizeof(c->method));
2858 av_strlcpy(c->url, url, sizeof(c->url));
2859 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2861 if (avio_open_dyn_buf(&c->pb) < 0) {
2862 /* XXX: cannot do more */
2863 c->pb = NULL; /* safety */
2867 /* check version name */
2868 if (strcmp(protocol, "RTSP/1.0") != 0) {
2869 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2873 /* parse each header line */
2874 /* skip to next line */
2875 while (*p != '\n' && *p != '\0')
2879 while (*p != '\0') {
2880 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2884 if (p2 > p && p2[-1] == '\r')
2886 /* skip empty line */
2890 if (len > sizeof(line) - 1)
2891 len = sizeof(line) - 1;
2892 memcpy(line, p, len);
2894 ff_rtsp_parse_line(header, line, NULL, NULL);
2898 /* handle sequence number */
2899 c->seq = header->seq;
2901 if (!strcmp(cmd, "DESCRIBE"))
2902 rtsp_cmd_describe(c, url);
2903 else if (!strcmp(cmd, "OPTIONS"))
2904 rtsp_cmd_options(c, url);
2905 else if (!strcmp(cmd, "SETUP"))
2906 rtsp_cmd_setup(c, url, header);
2907 else if (!strcmp(cmd, "PLAY"))
2908 rtsp_cmd_play(c, url, header);
2909 else if (!strcmp(cmd, "PAUSE"))
2910 rtsp_cmd_pause(c, url, header);
2911 else if (!strcmp(cmd, "TEARDOWN"))
2912 rtsp_cmd_teardown(c, url, header);
2914 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2917 len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2918 c->pb = NULL; /* safety */
2920 /* XXX: cannot do more */
2923 c->buffer_ptr = c->pb_buffer;
2924 c->buffer_end = c->pb_buffer + len;
2925 c->state = RTSPSTATE_SEND_REPLY;
2929 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2930 struct in_addr my_ip)
2932 AVFormatContext *avc;
2933 AVStream *avs = NULL;
2936 avc = avformat_alloc_context();
2940 av_dict_set(&avc->metadata, "title",
2941 stream->title[0] ? stream->title : "No Title", 0);
2942 avc->nb_streams = stream->nb_streams;
2943 if (stream->is_multicast) {
2944 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2945 inet_ntoa(stream->multicast_ip),
2946 stream->multicast_port, stream->multicast_ttl);
2948 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2951 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2952 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2954 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2955 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2958 for(i = 0; i < stream->nb_streams; i++) {
2959 avc->streams[i] = &avs[i];
2960 avc->streams[i]->codec = stream->streams[i]->codec;
2962 *pbuffer = av_mallocz(2048);
2963 av_sdp_create(&avc, 1, *pbuffer, 2048);
2966 av_free(avc->streams);
2967 av_dict_free(&avc->metadata);
2971 return strlen(*pbuffer);
2974 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2976 // rtsp_reply_header(c, RTSP_STATUS_OK);
2977 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2978 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2979 avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2980 avio_printf(c->pb, "\r\n");
2983 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2991 struct sockaddr_in my_addr;
2993 /* find which url is asked */
2994 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2999 for(stream = first_stream; stream != NULL; stream = stream->next) {
3000 if (!stream->is_feed &&
3001 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
3002 !strcmp(path, stream->filename)) {
3006 /* no stream found */
3007 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3011 /* prepare the media description in sdp format */
3013 /* get the host IP */
3014 len = sizeof(my_addr);
3015 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
3016 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3017 if (content_length < 0) {
3018 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3021 rtsp_reply_header(c, RTSP_STATUS_OK);
3022 avio_printf(c->pb, "Content-Base: %s/\r\n", url);
3023 avio_printf(c->pb, "Content-Type: application/sdp\r\n");
3024 avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
3025 avio_printf(c->pb, "\r\n");
3026 avio_write(c->pb, content, content_length);
3030 static HTTPContext *find_rtp_session(const char *session_id)
3034 if (session_id[0] == '\0')
3037 for(c = first_http_ctx; c != NULL; c = c->next) {
3038 if (!strcmp(c->session_id, session_id))
3044 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3046 RTSPTransportField *th;
3049 for(i=0;i<h->nb_transports;i++) {
3050 th = &h->transports[i];
3051 if (th->lower_transport == lower_transport)
3057 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3058 RTSPMessageHeader *h)
3061 int stream_index, rtp_port, rtcp_port;
3066 RTSPTransportField *th;
3067 struct sockaddr_in dest_addr;
3068 RTSPActionServerSetup setup;
3070 /* find which url is asked */
3071 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3076 /* now check each stream */
3077 for(stream = first_stream; stream != NULL; stream = stream->next) {
3078 if (!stream->is_feed &&
3079 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3080 /* accept aggregate filenames only if single stream */
3081 if (!strcmp(path, stream->filename)) {
3082 if (stream->nb_streams != 1) {
3083 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3090 for(stream_index = 0; stream_index < stream->nb_streams;
3092 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3093 stream->filename, stream_index);
3094 if (!strcmp(path, buf))
3099 /* no stream found */
3100 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3104 /* generate session id if needed */
3105 if (h->session_id[0] == '\0')
3106 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3107 av_lfg_get(&random_state), av_lfg_get(&random_state));
3109 /* find rtp session, and create it if none found */
3110 rtp_c = find_rtp_session(h->session_id);
3112 /* always prefer UDP */
3113 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3115 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3117 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3122 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3123 th->lower_transport);
3125 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3129 /* open input stream */
3130 if (open_input_stream(rtp_c, "") < 0) {
3131 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3136 /* test if stream is OK (test needed because several SETUP needs
3137 to be done for a given file) */
3138 if (rtp_c->stream != stream) {
3139 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3143 /* test if stream is already set up */
3144 if (rtp_c->rtp_ctx[stream_index]) {
3145 rtsp_reply_error(c, RTSP_STATUS_STATE);
3149 /* check transport */
3150 th = find_transport(h, rtp_c->rtp_protocol);
3151 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3152 th->client_port_min <= 0)) {
3153 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3157 /* setup default options */
3158 setup.transport_option[0] = '\0';
3159 dest_addr = rtp_c->from_addr;
3160 dest_addr.sin_port = htons(th->client_port_min);
3163 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3164 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3168 /* now everything is OK, so we can send the connection parameters */
3169 rtsp_reply_header(c, RTSP_STATUS_OK);
3171 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3173 switch(rtp_c->rtp_protocol) {
3174 case RTSP_LOWER_TRANSPORT_UDP:
3175 rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3176 rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3177 avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3178 "client_port=%d-%d;server_port=%d-%d",
3179 th->client_port_min, th->client_port_max,
3180 rtp_port, rtcp_port);
3182 case RTSP_LOWER_TRANSPORT_TCP:
3183 avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3184 stream_index * 2, stream_index * 2 + 1);
3189 if (setup.transport_option[0] != '\0')
3190 avio_printf(c->pb, ";%s", setup.transport_option);
3191 avio_printf(c->pb, "\r\n");
3194 avio_printf(c->pb, "\r\n");
3198 /* find an rtp connection by using the session ID. Check consistency
3200 static HTTPContext *find_rtp_session_with_url(const char *url,
3201 const char *session_id)
3209 rtp_c = find_rtp_session(session_id);
3213 /* find which url is asked */
3214 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3218 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3219 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3220 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3221 rtp_c->stream->filename, s);
3222 if(!strncmp(path, buf, sizeof(buf))) {
3223 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3228 if (len > 0 && path[len - 1] == '/' &&
3229 !strncmp(path, rtp_c->stream->filename, len - 1))
3234 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3238 rtp_c = find_rtp_session_with_url(url, h->session_id);
3240 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3244 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3245 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3246 rtp_c->state != HTTPSTATE_READY) {
3247 rtsp_reply_error(c, RTSP_STATUS_STATE);
3251 rtp_c->state = HTTPSTATE_SEND_DATA;
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_pause(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 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3271 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3272 rtsp_reply_error(c, RTSP_STATUS_STATE);
3276 rtp_c->state = HTTPSTATE_READY;
3277 rtp_c->first_pts = AV_NOPTS_VALUE;
3278 /* now everything is OK, so we can send the connection parameters */
3279 rtsp_reply_header(c, RTSP_STATUS_OK);
3281 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3282 avio_printf(c->pb, "\r\n");
3285 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3289 rtp_c = find_rtp_session_with_url(url, h->session_id);
3291 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3295 /* now everything is OK, so we can send the connection parameters */
3296 rtsp_reply_header(c, RTSP_STATUS_OK);
3298 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3299 avio_printf(c->pb, "\r\n");
3301 /* abort the session */
3302 close_connection(rtp_c);
3306 /********************************************************************/
3309 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3310 FFStream *stream, const char *session_id,
3311 enum RTSPLowerTransport rtp_protocol)
3313 HTTPContext *c = NULL;
3314 const char *proto_str;
3316 /* XXX: should output a warning page when coming
3317 close to the connection limit */
3318 if (nb_connections >= nb_max_connections)
3321 /* add a new connection */
3322 c = av_mallocz(sizeof(HTTPContext));
3327 c->poll_entry = NULL;
3328 c->from_addr = *from_addr;
3329 c->buffer_size = IOBUFFER_INIT_SIZE;
3330 c->buffer = av_malloc(c->buffer_size);
3335 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3336 c->state = HTTPSTATE_READY;
3337 c->is_packetized = 1;
3338 c->rtp_protocol = rtp_protocol;
3340 /* protocol is shown in statistics */
3341 switch(c->rtp_protocol) {
3342 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3343 proto_str = "MCAST";
3345 case RTSP_LOWER_TRANSPORT_UDP:
3348 case RTSP_LOWER_TRANSPORT_TCP:
3355 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3356 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3358 current_bandwidth += stream->bandwidth;
3360 c->next = first_http_ctx;
3372 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3373 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3375 static int rtp_new_av_stream(HTTPContext *c,
3376 int stream_index, struct sockaddr_in *dest_addr,
3377 HTTPContext *rtsp_c)
3379 AVFormatContext *ctx;
3382 URLContext *h = NULL;
3384 int max_packet_size;
3386 /* now we can open the relevant output stream */
3387 ctx = avformat_alloc_context();
3390 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3392 st = av_mallocz(sizeof(AVStream));
3395 ctx->nb_streams = 1;
3396 ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3399 ctx->streams[0] = st;
3401 if (!c->stream->feed ||
3402 c->stream->feed == c->stream)
3403 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3406 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3408 st->priv_data = NULL;
3410 /* build destination RTP address */
3411 ipaddr = inet_ntoa(dest_addr->sin_addr);
3413 switch(c->rtp_protocol) {
3414 case RTSP_LOWER_TRANSPORT_UDP:
3415 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3418 /* XXX: also pass as parameter to function ? */
3419 if (c->stream->is_multicast) {
3421 ttl = c->stream->multicast_ttl;
3424 snprintf(ctx->filename, sizeof(ctx->filename),
3425 "rtp://%s:%d?multicast=1&ttl=%d",
3426 ipaddr, ntohs(dest_addr->sin_port), ttl);
3428 snprintf(ctx->filename, sizeof(ctx->filename),
3429 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3432 if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3434 c->rtp_handles[stream_index] = h;
3435 max_packet_size = h->max_packet_size;
3437 case RTSP_LOWER_TRANSPORT_TCP:
3440 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3446 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3447 ipaddr, ntohs(dest_addr->sin_port),
3448 c->stream->filename, stream_index, c->protocol);
3450 /* normally, no packets should be output here, but the packet size may be checked */
3451 if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3452 /* XXX: close stream */
3455 if (avformat_write_header(ctx, NULL) < 0) {
3462 avio_close_dyn_buf(ctx->pb, &dummy_buf);
3465 c->rtp_ctx[stream_index] = ctx;
3469 /********************************************************************/
3470 /* avserver initialization */
3472 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3476 fst = av_mallocz(sizeof(AVStream));
3480 fst->codec = avcodec_alloc_context3(NULL);
3481 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3482 if (codec->extradata_size) {
3483 fst->codec->extradata = av_malloc(codec->extradata_size);
3484 memcpy(fst->codec->extradata, codec->extradata,
3485 codec->extradata_size);
3488 /* live streams must use the actual feed's codec since it may be
3489 * updated later to carry extradata needed by the streams.
3493 fst->priv_data = av_mallocz(sizeof(FeedData));
3494 fst->index = stream->nb_streams;
3495 avpriv_set_pts_info(fst, 33, 1, 90000);
3496 fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3497 stream->streams[stream->nb_streams++] = fst;
3501 /* return the stream number in the feed */
3502 static int add_av_stream(FFStream *feed, AVStream *st)
3505 AVCodecContext *av, *av1;
3509 for(i=0;i<feed->nb_streams;i++) {
3510 st = feed->streams[i];
3512 if (av1->codec_id == av->codec_id &&
3513 av1->codec_type == av->codec_type &&
3514 av1->bit_rate == av->bit_rate) {
3516 switch(av->codec_type) {
3517 case AVMEDIA_TYPE_AUDIO:
3518 if (av1->channels == av->channels &&
3519 av1->sample_rate == av->sample_rate)
3522 case AVMEDIA_TYPE_VIDEO:
3523 if (av1->width == av->width &&
3524 av1->height == av->height &&
3525 av1->time_base.den == av->time_base.den &&
3526 av1->time_base.num == av->time_base.num &&
3527 av1->gop_size == av->gop_size)
3536 fst = add_av_stream1(feed, av, 0);
3539 return feed->nb_streams - 1;
3542 static void remove_stream(FFStream *stream)
3546 while (*ps != NULL) {
3554 /* specific mpeg4 handling : we extract the raw parameters */
3555 static void extract_mpeg4_header(AVFormatContext *infile)
3557 int mpeg4_count, i, size;
3562 infile->flags |= AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE;
3565 for(i=0;i<infile->nb_streams;i++) {
3566 st = infile->streams[i];
3567 if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3568 st->codec->extradata_size == 0) {
3575 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3576 while (mpeg4_count > 0) {
3577 if (av_read_frame(infile, &pkt) < 0)
3579 st = infile->streams[pkt.stream_index];
3580 if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3581 st->codec->extradata_size == 0) {
3582 av_freep(&st->codec->extradata);
3583 /* fill extradata with the header */
3584 /* XXX: we make hard suppositions here ! */
3586 while (p < pkt.data + pkt.size - 4) {
3587 /* stop when vop header is found */
3588 if (p[0] == 0x00 && p[1] == 0x00 &&
3589 p[2] == 0x01 && p[3] == 0xb6) {
3590 size = p - pkt.data;
3591 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3592 st->codec->extradata = av_malloc(size);
3593 st->codec->extradata_size = size;
3594 memcpy(st->codec->extradata, pkt.data, size);
3601 av_free_packet(&pkt);
3605 /* compute the needed AVStream for each file */
3606 static void build_file_streams(void)
3608 FFStream *stream, *stream_next;
3611 /* gather all streams */
3612 for(stream = first_stream; stream != NULL; stream = stream_next) {
3613 AVFormatContext *infile = NULL;
3614 stream_next = stream->next;
3615 if (stream->stream_type == STREAM_TYPE_LIVE &&
3617 /* the stream comes from a file */
3618 /* try to open the file */
3620 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3621 /* specific case : if transport stream output to RTP,
3622 we use a raw transport stream reader */
3623 av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3626 http_log("Opening file '%s'\n", stream->feed_filename);
3627 if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3628 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3629 /* remove stream (no need to spend more time on it) */
3631 remove_stream(stream);
3633 /* find all the AVStreams inside and reference them in
3635 if (avformat_find_stream_info(infile, NULL) < 0) {
3636 http_log("Could not find codec parameters from '%s'\n",
3637 stream->feed_filename);
3638 avformat_close_input(&infile);
3641 extract_mpeg4_header(infile);
3643 for(i=0;i<infile->nb_streams;i++)
3644 add_av_stream1(stream, infile->streams[i]->codec, 1);
3646 avformat_close_input(&infile);
3652 /* compute the needed AVStream for each feed */
3653 static void build_feed_streams(void)
3655 FFStream *stream, *feed;
3658 /* gather all streams */
3659 for(stream = first_stream; stream != NULL; stream = stream->next) {
3660 feed = stream->feed;
3662 if (stream->is_feed) {
3663 for(i=0;i<stream->nb_streams;i++)
3664 stream->feed_streams[i] = i;
3666 /* we handle a stream coming from a feed */
3667 for(i=0;i<stream->nb_streams;i++)
3668 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3673 /* create feed files if needed */
3674 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3677 if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3678 /* See if it matches */
3679 AVFormatContext *s = NULL;
3682 if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3683 /* Now see if it matches */
3684 if (s->nb_streams == feed->nb_streams) {
3686 for(i=0;i<s->nb_streams;i++) {
3688 sf = feed->streams[i];
3691 if (sf->index != ss->index ||
3693 http_log("Index & Id do not match for stream %d (%s)\n",
3694 i, feed->feed_filename);
3697 AVCodecContext *ccf, *ccs;
3701 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3703 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3704 http_log("Codecs do not match for stream %d\n", i);
3706 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3707 http_log("Codec bitrates do not match for stream %d\n", i);
3709 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3710 if (CHECK_CODEC(time_base.den) ||
3711 CHECK_CODEC(time_base.num) ||
3712 CHECK_CODEC(width) ||
3713 CHECK_CODEC(height)) {
3714 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3717 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3718 if (CHECK_CODEC(sample_rate) ||
3719 CHECK_CODEC(channels) ||
3720 CHECK_CODEC(frame_size)) {
3721 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3725 http_log("Unknown codec type\n");
3733 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3734 feed->feed_filename, s->nb_streams, feed->nb_streams);
3736 avformat_close_input(&s);
3738 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3739 feed->feed_filename);
3742 if (feed->readonly) {
3743 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3744 feed->feed_filename);
3747 unlink(feed->feed_filename);
3750 if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3751 AVFormatContext s1 = {0}, *s = &s1;
3753 if (feed->readonly) {
3754 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3755 feed->feed_filename);
3759 /* only write the header of the ffm file */
3760 if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3761 http_log("Could not open output feed file '%s'\n",
3762 feed->feed_filename);
3765 s->oformat = feed->fmt;
3766 s->nb_streams = feed->nb_streams;
3767 s->streams = feed->streams;
3768 if (avformat_write_header(s, NULL) < 0) {
3769 http_log("Container doesn't supports the required parameters\n");
3772 /* XXX: need better api */
3773 av_freep(&s->priv_data);
3776 /* get feed size and write index */
3777 fd = open(feed->feed_filename, O_RDONLY);
3779 http_log("Could not open output feed file '%s'\n",
3780 feed->feed_filename);
3784 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3785 feed->feed_size = lseek(fd, 0, SEEK_END);
3786 /* ensure that we do not wrap before the end of file */
3787 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3788 feed->feed_max_size = feed->feed_size;
3794 /* compute the bandwidth used by each stream */
3795 static void compute_bandwidth(void)
3801 for(stream = first_stream; stream != NULL; stream = stream->next) {
3803 for(i=0;i<stream->nb_streams;i++) {
3804 AVStream *st = stream->streams[i];
3805 switch(st->codec->codec_type) {
3806 case AVMEDIA_TYPE_AUDIO:
3807 case AVMEDIA_TYPE_VIDEO:
3808 bandwidth += st->codec->bit_rate;
3814 stream->bandwidth = (bandwidth + 999) / 1000;
3818 /* add a codec and set the default parameters */
3819 static void add_codec(FFStream *stream, AVCodecContext *av)
3823 /* compute default parameters */
3824 switch(av->codec_type) {
3825 case AVMEDIA_TYPE_AUDIO:
3826 if (av->bit_rate == 0)
3827 av->bit_rate = 64000;
3828 if (av->sample_rate == 0)
3829 av->sample_rate = 22050;
3830 if (av->channels == 0)
3833 case AVMEDIA_TYPE_VIDEO:
3834 if (av->bit_rate == 0)
3835 av->bit_rate = 64000;
3836 if (av->time_base.num == 0){
3837 av->time_base.den = 5;
3838 av->time_base.num = 1;
3840 if (av->width == 0 || av->height == 0) {
3844 /* Bitrate tolerance is less for streaming */
3845 if (av->bit_rate_tolerance == 0)
3846 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3847 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3852 if (av->max_qdiff == 0)
3854 av->qcompress = 0.5;
3857 if (!av->nsse_weight)
3858 av->nsse_weight = 8;
3860 av->frame_skip_cmp = FF_CMP_DCTMAX;
3862 av->me_method = ME_EPZS;
3863 av->rc_buffer_aggressivity = 1.0;
3866 av->rc_eq = "tex^qComp";
3867 if (!av->i_quant_factor)
3868 av->i_quant_factor = -0.8;
3869 if (!av->b_quant_factor)
3870 av->b_quant_factor = 1.25;
3871 if (!av->b_quant_offset)
3872 av->b_quant_offset = 1.25;
3873 if (!av->rc_max_rate)
3874 av->rc_max_rate = av->bit_rate * 2;
3876 if (av->rc_max_rate && !av->rc_buffer_size) {
3877 av->rc_buffer_size = av->rc_max_rate;
3886 st = av_mallocz(sizeof(AVStream));
3889 st->codec = avcodec_alloc_context3(NULL);
3890 stream->streams[stream->nb_streams++] = st;
3891 memcpy(st->codec, av, sizeof(AVCodecContext));
3894 static enum AVCodecID opt_audio_codec(const char *arg)
3896 AVCodec *p= avcodec_find_encoder_by_name(arg);
3898 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3899 return AV_CODEC_ID_NONE;
3904 static enum AVCodecID opt_video_codec(const char *arg)
3906 AVCodec *p= avcodec_find_encoder_by_name(arg);
3908 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3909 return AV_CODEC_ID_NONE;
3914 static int avserver_opt_default(const char *opt, const char *arg,
3915 AVCodecContext *avctx, int type)
3918 const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3920 ret = av_opt_set(avctx, opt, arg, 0);
3924 static int avserver_opt_preset(const char *arg,
3925 AVCodecContext *avctx, int type,
3926 enum AVCodecID *audio_id, enum AVCodecID *video_id)
3929 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3931 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3933 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3934 codec ? codec->name : NULL))) {
3935 fprintf(stderr, "File for preset '%s' not found\n", arg);
3940 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3941 if(line[0] == '#' && !e)
3943 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3945 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3949 if(!strcmp(tmp, "acodec")){
3950 *audio_id = opt_audio_codec(tmp2);
3951 }else if(!strcmp(tmp, "vcodec")){
3952 *video_id = opt_video_codec(tmp2);
3953 }else if(!strcmp(tmp, "scodec")){
3954 /* opt_subtitle_codec(tmp2); */
3955 }else if(avserver_opt_default(tmp, tmp2, avctx, type) < 0){
3956 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
3967 static AVOutputFormat *avserver_guess_format(const char *short_name, const char *filename,
3968 const char *mime_type)
3970 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3973 AVOutputFormat *stream_fmt;
3974 char stream_format_name[64];
3976 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
3977 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
3986 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
3990 fprintf(stderr, "%s:%d: ", filename, line_num);
3991 vfprintf(stderr, fmt, vl);
3997 static int parse_ffconfig(const char *filename)
4004 int val, errors, line_num;
4005 FFStream **last_stream, *stream, *redirect;
4006 FFStream **last_feed, *feed, *s;
4007 AVCodecContext audio_enc, video_enc;
4008 enum AVCodecID audio_id, video_id;
4010 f = fopen(filename, "r");
4018 first_stream = NULL;
4019 last_stream = &first_stream;
4021 last_feed = &first_feed;
4025 audio_id = AV_CODEC_ID_NONE;
4026 video_id = AV_CODEC_ID_NONE;
4028 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4030 if (fgets(line, sizeof(line), f) == NULL)
4034 while (av_isspace(*p))
4036 if (*p == '\0' || *p == '#')
4039 get_arg(cmd, sizeof(cmd), &p);
4041 if (!av_strcasecmp(cmd, "Port")) {
4042 get_arg(arg, sizeof(arg), &p);
4044 if (val < 1 || val > 65536) {
4045 ERROR("Invalid_port: %s\n", arg);
4047 my_http_addr.sin_port = htons(val);
4048 } else if (!av_strcasecmp(cmd, "BindAddress")) {
4049 get_arg(arg, sizeof(arg), &p);
4050 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4051 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4053 } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4054 get_arg(arg, sizeof(arg), &p);
4056 if (val < 1 || val > 65536) {
4057 ERROR("%s:%d: Invalid port: %s\n", arg);
4059 my_rtsp_addr.sin_port = htons(atoi(arg));
4060 } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4061 get_arg(arg, sizeof(arg), &p);
4062 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4063 ERROR("Invalid host/IP address: %s\n", arg);
4065 } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4066 get_arg(arg, sizeof(arg), &p);
4068 if (val < 1 || val > 65536) {
4069 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4071 nb_max_http_connections = val;
4072 } else if (!av_strcasecmp(cmd, "MaxClients")) {
4073 get_arg(arg, sizeof(arg), &p);
4075 if (val < 1 || val > nb_max_http_connections) {
4076 ERROR("Invalid MaxClients: %s\n", arg);
4078 nb_max_connections = val;
4080 } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4082 get_arg(arg, sizeof(arg), &p);
4084 if (llval < 10 || llval > 10000000) {
4085 ERROR("Invalid MaxBandwidth: %s\n", arg);
4087 max_bandwidth = llval;
4088 } else if (!av_strcasecmp(cmd, "CustomLog")) {
4089 if (!avserver_debug)
4090 get_arg(logfilename, sizeof(logfilename), &p);
4091 } else if (!av_strcasecmp(cmd, "<Feed")) {
4092 /*********************************************/
4093 /* Feed related options */
4095 if (stream || feed) {
4096 ERROR("Already in a tag\n");
4098 feed = av_mallocz(sizeof(FFStream));
4099 get_arg(feed->filename, sizeof(feed->filename), &p);
4100 q = strrchr(feed->filename, '>');
4104 for (s = first_feed; s; s = s->next) {
4105 if (!strcmp(feed->filename, s->filename)) {
4106 ERROR("Feed '%s' already registered\n", s->filename);
4110 feed->fmt = av_guess_format("ffm", NULL, NULL);
4111 /* defaut feed file */
4112 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4113 "/tmp/%s.ffm", feed->filename);
4114 feed->feed_max_size = 5 * 1024 * 1024;
4116 feed->feed = feed; /* self feeding :-) */
4118 /* add in stream list */
4119 *last_stream = feed;
4120 last_stream = &feed->next;
4121 /* add in feed list */
4123 last_feed = &feed->next_feed;
4125 } else if (!av_strcasecmp(cmd, "Launch")) {
4129 feed->child_argv = av_mallocz(64 * sizeof(char *));
4131 for (i = 0; i < 62; i++) {
4132 get_arg(arg, sizeof(arg), &p);
4136 feed->child_argv[i] = av_strdup(arg);
4139 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4141 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4143 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4144 inet_ntoa(my_http_addr.sin_addr),
4145 ntohs(my_http_addr.sin_port), feed->filename);
4147 } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4149 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4151 } else if (stream) {
4152 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4154 } else if (!av_strcasecmp(cmd, "File")) {
4156 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4158 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4159 } else if (!av_strcasecmp(cmd, "Truncate")) {
4161 get_arg(arg, sizeof(arg), &p);
4162 feed->truncate = strtod(arg, NULL);
4164 } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4169 get_arg(arg, sizeof(arg), &p);
4171 fsize = strtod(p1, &p1);
4172 switch(av_toupper(*p1)) {
4177 fsize *= 1024 * 1024;
4180 fsize *= 1024 * 1024 * 1024;
4183 feed->feed_max_size = (int64_t)fsize;
4184 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4185 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4188 } else if (!av_strcasecmp(cmd, "</Feed>")) {
4190 ERROR("No corresponding <Feed> for </Feed>\n");
4193 } else if (!av_strcasecmp(cmd, "<Stream")) {
4194 /*********************************************/
4195 /* Stream related options */
4197 if (stream || feed) {
4198 ERROR("Already in a tag\n");
4201 stream = av_mallocz(sizeof(FFStream));
4202 get_arg(stream->filename, sizeof(stream->filename), &p);
4203 q = strrchr(stream->filename, '>');
4207 for (s = first_stream; s; s = s->next) {
4208 if (!strcmp(stream->filename, s->filename)) {
4209 ERROR("Stream '%s' already registered\n", s->filename);
4213 stream->fmt = avserver_guess_format(NULL, stream->filename, NULL);
4214 avcodec_get_context_defaults3(&video_enc, NULL);
4215 avcodec_get_context_defaults3(&audio_enc, NULL);
4216 audio_id = AV_CODEC_ID_NONE;
4217 video_id = AV_CODEC_ID_NONE;
4219 audio_id = stream->fmt->audio_codec;
4220 video_id = stream->fmt->video_codec;
4223 *last_stream = stream;
4224 last_stream = &stream->next;
4226 } else if (!av_strcasecmp(cmd, "Feed")) {
4227 get_arg(arg, sizeof(arg), &p);
4232 while (sfeed != NULL) {
4233 if (!strcmp(sfeed->filename, arg))
4235 sfeed = sfeed->next_feed;
4238 ERROR("feed '%s' not defined\n", arg);
4240 stream->feed = sfeed;
4242 } else if (!av_strcasecmp(cmd, "Format")) {
4243 get_arg(arg, sizeof(arg), &p);
4245 if (!strcmp(arg, "status")) {
4246 stream->stream_type = STREAM_TYPE_STATUS;
4249 stream->stream_type = STREAM_TYPE_LIVE;
4250 /* jpeg cannot be used here, so use single frame jpeg */
4251 if (!strcmp(arg, "jpeg"))
4252 strcpy(arg, "mjpeg");
4253 stream->fmt = avserver_guess_format(arg, NULL, NULL);
4255 ERROR("Unknown Format: %s\n", arg);
4259 audio_id = stream->fmt->audio_codec;
4260 video_id = stream->fmt->video_codec;
4263 } else if (!av_strcasecmp(cmd, "InputFormat")) {
4264 get_arg(arg, sizeof(arg), &p);
4266 stream->ifmt = av_find_input_format(arg);
4267 if (!stream->ifmt) {
4268 ERROR("Unknown input format: %s\n", arg);
4271 } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4272 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4273 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4275 ERROR("FaviconURL only permitted for status streams\n");
4277 } else if (!av_strcasecmp(cmd, "Author")) {
4279 get_arg(stream->author, sizeof(stream->author), &p);
4280 } else if (!av_strcasecmp(cmd, "Comment")) {
4282 get_arg(stream->comment, sizeof(stream->comment), &p);
4283 } else if (!av_strcasecmp(cmd, "Copyright")) {
4285 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4286 } else if (!av_strcasecmp(cmd, "Title")) {
4288 get_arg(stream->title, sizeof(stream->title), &p);
4289 } else if (!av_strcasecmp(cmd, "Preroll")) {
4290 get_arg(arg, sizeof(arg), &p);
4292 stream->prebuffer = atof(arg) * 1000;
4293 } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4295 stream->send_on_key = 1;
4296 } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4297 get_arg(arg, sizeof(arg), &p);
4298 audio_id = opt_audio_codec(arg);
4299 if (audio_id == AV_CODEC_ID_NONE) {
4300 ERROR("Unknown AudioCodec: %s\n", arg);
4302 } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4303 get_arg(arg, sizeof(arg), &p);
4304 video_id = opt_video_codec(arg);
4305 if (video_id == AV_CODEC_ID_NONE) {
4306 ERROR("Unknown VideoCodec: %s\n", arg);
4308 } else if (!av_strcasecmp(cmd, "MaxTime")) {
4309 get_arg(arg, sizeof(arg), &p);
4311 stream->max_time = atof(arg) * 1000;
4312 } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4313 get_arg(arg, sizeof(arg), &p);
4315 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4316 } else if (!av_strcasecmp(cmd, "AudioChannels")) {
4317 get_arg(arg, sizeof(arg), &p);
4319 audio_enc.channels = atoi(arg);
4320 } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4321 get_arg(arg, sizeof(arg), &p);
4323 audio_enc.sample_rate = atoi(arg);
4324 } else if (!av_strcasecmp(cmd, "AudioQuality")) {
4325 get_arg(arg, sizeof(arg), &p);
4327 // audio_enc.quality = atof(arg) * 1000;
4329 } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4331 int minrate, maxrate;
4333 get_arg(arg, sizeof(arg), &p);
4335 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4336 video_enc.rc_min_rate = minrate * 1000;
4337 video_enc.rc_max_rate = maxrate * 1000;
4339 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4342 } else if (!av_strcasecmp(cmd, "Debug")) {
4344 get_arg(arg, sizeof(arg), &p);
4345 video_enc.debug = strtol(arg,0,0);
4347 } else if (!av_strcasecmp(cmd, "Strict")) {
4349 get_arg(arg, sizeof(arg), &p);
4350 video_enc.strict_std_compliance = atoi(arg);
4352 } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4354 get_arg(arg, sizeof(arg), &p);
4355 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4357 } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4359 get_arg(arg, sizeof(arg), &p);
4360 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4362 } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4363 get_arg(arg, sizeof(arg), &p);
4365 video_enc.bit_rate = atoi(arg) * 1000;
4367 } else if (!av_strcasecmp(cmd, "VideoSize")) {
4368 get_arg(arg, sizeof(arg), &p);
4370 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4371 if ((video_enc.width % 16) != 0 ||
4372 (video_enc.height % 16) != 0) {
4373 ERROR("Image size must be a multiple of 16\n");
4376 } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4377 get_arg(arg, sizeof(arg), &p);
4379 AVRational frame_rate;
4380 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4381 ERROR("Incorrect frame rate: %s\n", arg);
4383 video_enc.time_base.num = frame_rate.den;
4384 video_enc.time_base.den = frame_rate.num;
4387 } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4388 get_arg(arg, sizeof(arg), &p);
4390 video_enc.gop_size = atoi(arg);
4391 } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4393 video_enc.gop_size = 1;
4394 } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4396 video_enc.mb_decision = FF_MB_DECISION_BITS;
4397 } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4399 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4400 video_enc.flags |= CODEC_FLAG_4MV;
4402 } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4403 !av_strcasecmp(cmd, "AVOptionAudio")) {
4405 AVCodecContext *avctx;
4407 get_arg(arg, sizeof(arg), &p);
4408 get_arg(arg2, sizeof(arg2), &p);
4409 if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4411 type = AV_OPT_FLAG_VIDEO_PARAM;
4414 type = AV_OPT_FLAG_AUDIO_PARAM;
4416 if (avserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4417 ERROR("AVOption error: %s %s\n", arg, arg2);
4419 } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4420 !av_strcasecmp(cmd, "AVPresetAudio")) {
4421 AVCodecContext *avctx;
4423 get_arg(arg, sizeof(arg), &p);
4424 if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4426 video_enc.codec_id = video_id;
4427 type = AV_OPT_FLAG_VIDEO_PARAM;
4430 audio_enc.codec_id = audio_id;
4431 type = AV_OPT_FLAG_AUDIO_PARAM;
4433 if (avserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4434 ERROR("AVPreset error: %s\n", arg);
4436 } else if (!av_strcasecmp(cmd, "VideoTag")) {
4437 get_arg(arg, sizeof(arg), &p);
4438 if ((strlen(arg) == 4) && stream)
4439 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4440 } else if (!av_strcasecmp(cmd, "BitExact")) {
4442 video_enc.flags |= CODEC_FLAG_BITEXACT;
4443 } else if (!av_strcasecmp(cmd, "DctFastint")) {
4445 video_enc.dct_algo = FF_DCT_FASTINT;
4446 } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4448 video_enc.idct_algo = FF_IDCT_SIMPLE;
4449 } else if (!av_strcasecmp(cmd, "Qscale")) {
4450 get_arg(arg, sizeof(arg), &p);
4452 video_enc.flags |= CODEC_FLAG_QSCALE;
4453 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4455 } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4456 get_arg(arg, sizeof(arg), &p);
4458 video_enc.max_qdiff = atoi(arg);
4459 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4460 ERROR("VideoQDiff out of range\n");
4463 } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4464 get_arg(arg, sizeof(arg), &p);
4466 video_enc.qmax = atoi(arg);
4467 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4468 ERROR("VideoQMax out of range\n");
4471 } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4472 get_arg(arg, sizeof(arg), &p);
4474 video_enc.qmin = atoi(arg);
4475 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4476 ERROR("VideoQMin out of range\n");
4479 } else if (!av_strcasecmp(cmd, "LumiMask")) {
4480 get_arg(arg, sizeof(arg), &p);
4482 video_enc.lumi_masking = atof(arg);
4483 } else if (!av_strcasecmp(cmd, "DarkMask")) {
4484 get_arg(arg, sizeof(arg), &p);
4486 video_enc.dark_masking = atof(arg);
4487 } else if (!av_strcasecmp(cmd, "NoVideo")) {
4488 video_id = AV_CODEC_ID_NONE;
4489 } else if (!av_strcasecmp(cmd, "NoAudio")) {
4490 audio_id = AV_CODEC_ID_NONE;
4491 } else if (!av_strcasecmp(cmd, "ACL")) {
4492 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4493 } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4495 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4497 } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4498 get_arg(arg, sizeof(arg), &p);
4500 av_freep(&stream->rtsp_option);
4501 stream->rtsp_option = av_strdup(arg);
4503 } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4504 get_arg(arg, sizeof(arg), &p);
4506 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4507 ERROR("Invalid host/IP address: %s\n", arg);
4509 stream->is_multicast = 1;
4510 stream->loop = 1; /* default is looping */
4512 } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4513 get_arg(arg, sizeof(arg), &p);
4515 stream->multicast_port = atoi(arg);
4516 } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4517 get_arg(arg, sizeof(arg), &p);
4519 stream->multicast_ttl = atoi(arg);
4520 } else if (!av_strcasecmp(cmd, "NoLoop")) {
4523 } else if (!av_strcasecmp(cmd, "</Stream>")) {
4525 ERROR("No corresponding <Stream> for </Stream>\n");
4527 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4528 if (audio_id != AV_CODEC_ID_NONE) {
4529 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4530 audio_enc.codec_id = audio_id;
4531 add_codec(stream, &audio_enc);
4533 if (video_id != AV_CODEC_ID_NONE) {
4534 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4535 video_enc.codec_id = video_id;
4536 add_codec(stream, &video_enc);
4541 } else if (!av_strcasecmp(cmd, "<Redirect")) {
4542 /*********************************************/
4544 if (stream || feed || redirect) {
4545 ERROR("Already in a tag\n");
4547 redirect = av_mallocz(sizeof(FFStream));
4548 *last_stream = redirect;
4549 last_stream = &redirect->next;
4551 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4552 q = strrchr(redirect->filename, '>');
4555 redirect->stream_type = STREAM_TYPE_REDIRECT;
4557 } else if (!av_strcasecmp(cmd, "URL")) {
4559 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4560 } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4562 ERROR("No corresponding <Redirect> for </Redirect>\n");
4564 if (!redirect->feed_filename[0]) {
4565 ERROR("No URL found for <Redirect>\n");
4569 } else if (!av_strcasecmp(cmd, "LoadModule")) {
4570 ERROR("Loadable modules no longer supported\n");
4572 ERROR("Incorrect keyword: '%s'\n", cmd);
4584 static void handle_child_exit(int sig)
4589 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4592 for (feed = first_feed; feed; feed = feed->next) {
4593 if (feed->pid == pid) {
4594 int uptime = time(0) - feed->pid_start;
4597 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4600 /* Turn off any more restarts */
4601 feed->child_argv = 0;
4606 need_to_start_children = 1;
4609 static void opt_debug(void)
4612 logfilename[0] = '-';
4615 void show_help_default(const char *opt, const char *arg)
4617 printf("usage: avserver [options]\n"
4618 "Hyper fast multi format Audio/Video streaming server\n");
4620 show_help_options(options, "Main options:", 0, 0, 0);
4623 static const OptionDef options[] = {
4624 #include "cmdutils_common_opts.h"
4625 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4626 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4627 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/avserver.conf", "configfile" },
4631 int main(int argc, char **argv)
4633 struct sigaction sigact = { { 0 } };
4635 parse_loglevel(argc, argv, options);
4637 avformat_network_init();
4641 my_program_name = argv[0];
4643 parse_options(NULL, argc, argv, options, NULL);
4645 unsetenv("http_proxy"); /* Kill the http_proxy */
4647 av_lfg_init(&random_state, av_get_random_seed());
4649 sigact.sa_handler = handle_child_exit;
4650 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4651 sigaction(SIGCHLD, &sigact, 0);
4653 if (parse_ffconfig(config_filename) < 0) {
4654 fprintf(stderr, "Incorrect config file - exiting.\n");
4658 /* open log file if needed */
4659 if (logfilename[0] != '\0') {
4660 if (!strcmp(logfilename, "-"))
4663 logfile = fopen(logfilename, "a");
4664 av_log_set_callback(http_av_log);
4667 build_file_streams();
4669 build_feed_streams();
4671 compute_bandwidth();
4674 signal(SIGPIPE, SIG_IGN);
4676 if (http_server() < 0) {
4677 http_log("Could not start server\n");