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>
67 const char program_name[] = "avserver";
68 const int program_birth_year = 2000;
70 static const OptionDef options[];
73 HTTPSTATE_WAIT_REQUEST,
74 HTTPSTATE_SEND_HEADER,
75 HTTPSTATE_SEND_DATA_HEADER,
76 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
77 HTTPSTATE_SEND_DATA_TRAILER,
78 HTTPSTATE_RECEIVE_DATA,
79 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
82 RTSPSTATE_WAIT_REQUEST,
84 RTSPSTATE_SEND_PACKET,
87 static const char *http_state[] = {
103 #define MAX_STREAMS 20
105 #define IOBUFFER_INIT_SIZE 8192
107 /* timeouts are in ms */
108 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
109 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
111 #define SYNC_TIMEOUT (10 * 1000)
113 typedef struct RTSPActionServerSetup {
115 char transport_option[512];
116 } RTSPActionServerSetup;
119 int64_t count1, count2;
120 int64_t time1, time2;
123 /* context associated with one connection */
124 typedef struct HTTPContext {
125 enum HTTPState state;
126 int fd; /* socket file descriptor */
127 struct sockaddr_in from_addr; /* origin */
128 struct pollfd *poll_entry; /* used when polling */
130 uint8_t *buffer_ptr, *buffer_end;
133 int chunked_encoding;
134 int chunk_size; /* 0 if it needs to be read */
135 struct HTTPContext *next;
136 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
140 /* input format handling */
141 AVFormatContext *fmt_in;
142 int64_t start_time; /* In milliseconds - this wraps fairly often */
143 int64_t first_pts; /* initial pts value */
144 int64_t cur_pts; /* current pts value from the stream in us */
145 int64_t cur_frame_duration; /* duration of the current frame in us */
146 int cur_frame_bytes; /* output frame size, needed to compute
147 the time at which we send each
149 int pts_stream_index; /* stream we choose as clock reference */
150 int64_t cur_clock; /* current clock reference value in us */
151 /* output format handling */
152 struct FFStream *stream;
153 /* -1 is invalid stream */
154 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
155 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
157 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
158 int last_packet_sent; /* true if last data packet was sent */
160 DataRateData datarate;
167 int is_packetized; /* if true, the stream is packetized */
168 int packet_stream_index; /* current stream for output in state machine */
170 /* RTSP state specific */
171 uint8_t *pb_buffer; /* XXX: use that in all the code */
173 int seq; /* RTSP sequence number */
175 /* RTP state specific */
176 enum RTSPLowerTransport rtp_protocol;
177 char session_id[32]; /* session id */
178 AVFormatContext *rtp_ctx[MAX_STREAMS];
180 /* RTP/UDP specific */
181 URLContext *rtp_handles[MAX_STREAMS];
183 /* RTP/TCP specific */
184 struct HTTPContext *rtsp_c;
185 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
188 /* each generated stream is described here */
192 STREAM_TYPE_REDIRECT,
195 enum IPAddressAction {
200 typedef struct IPAddressACL {
201 struct IPAddressACL *next;
202 enum IPAddressAction action;
203 /* These are in host order */
204 struct in_addr first;
208 /* description of each stream of the avserver.conf file */
209 typedef struct FFStream {
210 enum StreamType stream_type;
211 char filename[1024]; /* stream filename */
212 struct FFStream *feed; /* feed we are using (can be null if
214 AVDictionary *in_opts; /* input parameters */
215 AVInputFormat *ifmt; /* if non NULL, force input format */
218 char dynamic_acl[1024];
220 int prebuffer; /* Number of millseconds early to start */
221 int64_t max_time; /* Number of milliseconds to run */
223 AVStream *streams[MAX_STREAMS];
224 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
225 char feed_filename[1024]; /* file name of the feed storage, or
226 input file name for a stream */
231 pid_t pid; /* of avconv process */
232 time_t pid_start; /* of avconv process */
234 struct FFStream *next;
235 unsigned bandwidth; /* bandwidth, in kbits/s */
238 /* multicast specific */
240 struct in_addr multicast_ip;
241 int multicast_port; /* first port used for multicast */
243 int loop; /* if true, send the stream in loops (only meaningful if file) */
246 int feed_opened; /* true if someone is writing to the feed */
247 int is_feed; /* true if it is a feed */
248 int readonly; /* True if writing is prohibited to the file */
249 int truncate; /* True if feeder connection truncate the feed file */
251 int64_t bytes_served;
252 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
253 int64_t feed_write_index; /* current write position in feed (it wraps around) */
254 int64_t feed_size; /* current size of feed */
255 struct FFStream *next_feed;
258 typedef struct FeedData {
259 long long data_count;
260 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
263 static struct sockaddr_in my_http_addr;
264 static struct sockaddr_in my_rtsp_addr;
266 static char logfilename[1024];
267 static HTTPContext *first_http_ctx;
268 static FFStream *first_feed; /* contains only feeds */
269 static FFStream *first_stream; /* contains all streams, including feeds */
271 static void new_connection(int server_fd, int is_rtsp);
272 static void close_connection(HTTPContext *c);
275 static int handle_connection(HTTPContext *c);
276 static int http_parse_request(HTTPContext *c);
277 static int http_send_data(HTTPContext *c);
278 static void compute_status(HTTPContext *c);
279 static int open_input_stream(HTTPContext *c, const char *info);
280 static int http_start_receive_data(HTTPContext *c);
281 static int http_receive_data(HTTPContext *c);
284 static int rtsp_parse_request(HTTPContext *c);
285 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
286 static void rtsp_cmd_options(HTTPContext *c, const char *url);
287 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
288 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
289 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
290 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
293 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
294 struct in_addr my_ip);
297 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
298 FFStream *stream, const char *session_id,
299 enum RTSPLowerTransport rtp_protocol);
300 static int rtp_new_av_stream(HTTPContext *c,
301 int stream_index, struct sockaddr_in *dest_addr,
302 HTTPContext *rtsp_c);
304 static const char *my_program_name;
306 static const char *config_filename = "/etc/avserver.conf";
308 static int avserver_debug;
309 static int no_launch;
310 static int need_to_start_children;
312 /* maximum number of simultaneous HTTP connections */
313 static unsigned int nb_max_http_connections = 2000;
314 static unsigned int nb_max_connections = 5;
315 static unsigned int nb_connections;
317 static uint64_t max_bandwidth = 1000;
318 static uint64_t current_bandwidth;
320 static int64_t cur_time; // Making this global saves on passing it around everywhere
322 static AVLFG random_state;
324 static FILE *logfile = NULL;
326 static int64_t ffm_read_write_index(int fd)
330 lseek(fd, 8, SEEK_SET);
331 if (read(fd, buf, 8) != 8)
336 static int ffm_write_write_index(int fd, int64_t pos)
342 buf[i] = (pos >> (56 - i * 8)) & 0xff;
343 lseek(fd, 8, SEEK_SET);
344 if (write(fd, buf, 8) != 8)
349 static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
352 FFMContext *ffm = s->priv_data;
353 ffm->write_index = pos;
354 ffm->file_size = file_size;
357 /* FIXME: make avserver work with IPv6 */
358 /* resolve host with also IP address parsing */
359 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
362 if (!ff_inet_aton(hostname, sin_addr)) {
364 struct addrinfo *ai, *cur;
365 struct addrinfo hints = { 0 };
366 hints.ai_family = AF_INET;
367 if (getaddrinfo(hostname, NULL, &hints, &ai))
369 /* getaddrinfo returns a linked list of addrinfo structs.
370 * Even if we set ai_family = AF_INET above, make sure
371 * that the returned one actually is of the correct type. */
372 for (cur = ai; cur; cur = cur->ai_next) {
373 if (cur->ai_family == AF_INET) {
374 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
383 hp = gethostbyname(hostname);
386 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
392 static char *ctime1(char *buf2)
400 p = buf2 + strlen(p) - 1;
406 static void http_vlog(const char *fmt, va_list vargs)
408 static int print_prefix = 1;
413 fprintf(logfile, "%s ", buf);
415 print_prefix = strstr(fmt, "\n") != NULL;
416 vfprintf(logfile, fmt, vargs);
422 __attribute__ ((format (printf, 1, 2)))
424 static void http_log(const char *fmt, ...)
427 va_start(vargs, fmt);
428 http_vlog(fmt, vargs);
432 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
434 static int print_prefix = 1;
435 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
436 if (level > av_log_get_level())
438 if (print_prefix && avc)
439 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
440 print_prefix = strstr(fmt, "\n") != NULL;
441 http_vlog(fmt, vargs);
444 static void log_connection(HTTPContext *c)
449 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
450 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
451 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
454 static void update_datarate(DataRateData *drd, int64_t count)
456 if (!drd->time1 && !drd->count1) {
457 drd->time1 = drd->time2 = cur_time;
458 drd->count1 = drd->count2 = count;
459 } else if (cur_time - drd->time2 > 5000) {
460 drd->time1 = drd->time2;
461 drd->count1 = drd->count2;
462 drd->time2 = cur_time;
467 /* In bytes per second */
468 static int compute_datarate(DataRateData *drd, int64_t count)
470 if (cur_time == drd->time1)
473 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
477 static void start_children(FFStream *feed)
482 for (; feed; feed = feed->next) {
483 if (feed->child_argv && !feed->pid) {
484 feed->pid_start = time(0);
489 http_log("Unable to create children\n");
498 av_strlcpy(pathname, my_program_name, sizeof(pathname));
500 slash = strrchr(pathname, '/');
505 strcpy(slash, "avconv");
507 http_log("Launch command line: ");
508 http_log("%s ", pathname);
509 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
510 http_log("%s ", feed->child_argv[i]);
513 for (i = 3; i < 256; i++)
516 if (!avserver_debug) {
517 if (!freopen("/dev/null", "r", stdin))
518 http_log("failed to redirect STDIN to /dev/null\n;");
519 if (!freopen("/dev/null", "w", stdout))
520 http_log("failed to redirect STDOUT to /dev/null\n;");
521 if (!freopen("/dev/null", "w", stderr))
522 http_log("failed to redirect STDERR to /dev/null\n;");
525 signal(SIGPIPE, SIG_DFL);
527 execvp(pathname, feed->child_argv);
535 /* open a listening socket */
536 static int socket_open_listen(struct sockaddr_in *my_addr)
540 server_fd = socket(AF_INET,SOCK_STREAM,0);
547 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
549 my_addr->sin_family = AF_INET;
550 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
552 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
554 closesocket(server_fd);
558 if (listen (server_fd, 5) < 0) {
560 closesocket(server_fd);
563 ff_socket_nonblock(server_fd, 1);
568 /* start all multicast streams */
569 static void start_multicast(void)
574 struct sockaddr_in dest_addr;
575 int default_port, stream_index;
578 for(stream = first_stream; stream != NULL; stream = stream->next) {
579 if (stream->is_multicast) {
580 /* open the RTP connection */
581 snprintf(session_id, sizeof(session_id), "%08x%08x",
582 av_lfg_get(&random_state), av_lfg_get(&random_state));
584 /* choose a port if none given */
585 if (stream->multicast_port == 0) {
586 stream->multicast_port = default_port;
590 dest_addr.sin_family = AF_INET;
591 dest_addr.sin_addr = stream->multicast_ip;
592 dest_addr.sin_port = htons(stream->multicast_port);
594 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
595 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
599 if (open_input_stream(rtp_c, "") < 0) {
600 http_log("Could not open input stream for stream '%s'\n",
605 /* open each RTP stream */
606 for(stream_index = 0; stream_index < stream->nb_streams;
608 dest_addr.sin_port = htons(stream->multicast_port +
610 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
611 http_log("Could not open output stream '%s/streamid=%d'\n",
612 stream->filename, stream_index);
617 /* change state to send data */
618 rtp_c->state = HTTPSTATE_SEND_DATA;
623 /* main loop of the http server */
624 static int http_server(void)
626 int server_fd = 0, rtsp_server_fd = 0;
627 int ret, delay, delay1;
628 struct pollfd *poll_table, *poll_entry;
629 HTTPContext *c, *c_next;
631 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
632 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
636 if (my_http_addr.sin_port) {
637 server_fd = socket_open_listen(&my_http_addr);
642 if (my_rtsp_addr.sin_port) {
643 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
644 if (rtsp_server_fd < 0)
648 if (!rtsp_server_fd && !server_fd) {
649 http_log("HTTP and RTSP disabled.\n");
653 http_log("AVserver started.\n");
655 start_children(first_feed);
660 poll_entry = poll_table;
662 poll_entry->fd = server_fd;
663 poll_entry->events = POLLIN;
666 if (rtsp_server_fd) {
667 poll_entry->fd = rtsp_server_fd;
668 poll_entry->events = POLLIN;
672 /* wait for events on each HTTP handle */
679 case HTTPSTATE_SEND_HEADER:
680 case RTSPSTATE_SEND_REPLY:
681 case RTSPSTATE_SEND_PACKET:
682 c->poll_entry = poll_entry;
684 poll_entry->events = POLLOUT;
687 case HTTPSTATE_SEND_DATA_HEADER:
688 case HTTPSTATE_SEND_DATA:
689 case HTTPSTATE_SEND_DATA_TRAILER:
690 if (!c->is_packetized) {
691 /* for TCP, we output as much as we can (may need to put a limit) */
692 c->poll_entry = poll_entry;
694 poll_entry->events = POLLOUT;
697 /* when avserver is doing the timing, we work by
698 looking at which packet need to be sent every
700 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
705 case HTTPSTATE_WAIT_REQUEST:
706 case HTTPSTATE_RECEIVE_DATA:
707 case HTTPSTATE_WAIT_FEED:
708 case RTSPSTATE_WAIT_REQUEST:
709 /* need to catch errors */
710 c->poll_entry = poll_entry;
712 poll_entry->events = POLLIN;/* Maybe this will work */
716 c->poll_entry = NULL;
722 /* wait for an event on one connection. We poll at least every
723 second to handle timeouts */
725 ret = poll(poll_table, poll_entry - poll_table, delay);
726 if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
727 ff_neterrno() != AVERROR(EINTR))
731 cur_time = av_gettime() / 1000;
733 if (need_to_start_children) {
734 need_to_start_children = 0;
735 start_children(first_feed);
738 /* now handle the events */
739 for(c = first_http_ctx; c != NULL; c = c_next) {
741 if (handle_connection(c) < 0) {
742 /* close and free the connection */
748 poll_entry = poll_table;
750 /* new HTTP connection request ? */
751 if (poll_entry->revents & POLLIN)
752 new_connection(server_fd, 0);
755 if (rtsp_server_fd) {
756 /* new RTSP connection request ? */
757 if (poll_entry->revents & POLLIN)
758 new_connection(rtsp_server_fd, 1);
763 /* start waiting for a new HTTP/RTSP request */
764 static void start_wait_request(HTTPContext *c, int is_rtsp)
766 c->buffer_ptr = c->buffer;
767 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
770 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
771 c->state = RTSPSTATE_WAIT_REQUEST;
773 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
774 c->state = HTTPSTATE_WAIT_REQUEST;
778 static void http_send_too_busy_reply(int fd)
781 int len = snprintf(buffer, sizeof(buffer),
782 "HTTP/1.0 503 Server too busy\r\n"
783 "Content-type: text/html\r\n"
785 "<html><head><title>Too busy</title></head><body>\r\n"
786 "<p>The server is too busy to serve your request at this time.</p>\r\n"
787 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
788 "</body></html>\r\n",
789 nb_connections, nb_max_connections);
790 send(fd, buffer, len, 0);
794 static void new_connection(int server_fd, int is_rtsp)
796 struct sockaddr_in from_addr;
799 HTTPContext *c = NULL;
801 len = sizeof(from_addr);
802 fd = accept(server_fd, (struct sockaddr *)&from_addr,
805 http_log("error during accept %s\n", strerror(errno));
808 ff_socket_nonblock(fd, 1);
810 if (nb_connections >= nb_max_connections) {
811 http_send_too_busy_reply(fd);
815 /* add a new connection */
816 c = av_mallocz(sizeof(HTTPContext));
821 c->poll_entry = NULL;
822 c->from_addr = from_addr;
823 c->buffer_size = IOBUFFER_INIT_SIZE;
824 c->buffer = av_malloc(c->buffer_size);
828 c->next = first_http_ctx;
832 start_wait_request(c, is_rtsp);
844 static void close_connection(HTTPContext *c)
846 HTTPContext **cp, *c1;
848 AVFormatContext *ctx;
852 /* remove connection from list */
853 cp = &first_http_ctx;
854 while ((*cp) != NULL) {
862 /* remove references, if any (XXX: do it faster) */
863 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
868 /* remove connection associated resources */
872 /* close each frame parser */
873 for(i=0;i<c->fmt_in->nb_streams;i++) {
874 st = c->fmt_in->streams[i];
875 if (st->codec->codec)
876 avcodec_close(st->codec);
878 avformat_close_input(&c->fmt_in);
881 /* free RTP output streams if any */
884 nb_streams = c->stream->nb_streams;
886 for(i=0;i<nb_streams;i++) {
889 av_write_trailer(ctx);
890 av_dict_free(&ctx->metadata);
891 av_free(ctx->streams[0]);
894 h = c->rtp_handles[i];
901 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
904 if (avio_open_dyn_buf(&ctx->pb) >= 0) {
905 av_write_trailer(ctx);
906 av_freep(&c->pb_buffer);
907 avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
912 for(i=0; i<ctx->nb_streams; i++)
913 av_free(ctx->streams[i]);
915 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
916 current_bandwidth -= c->stream->bandwidth;
918 /* signal that there is no feed if we are the feeder socket */
919 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
920 c->stream->feed_opened = 0;
924 av_freep(&c->pb_buffer);
925 av_freep(&c->packet_buffer);
931 static int handle_connection(HTTPContext *c)
936 case HTTPSTATE_WAIT_REQUEST:
937 case RTSPSTATE_WAIT_REQUEST:
939 if ((c->timeout - cur_time) < 0)
941 if (c->poll_entry->revents & (POLLERR | POLLHUP))
944 /* no need to read if no events */
945 if (!(c->poll_entry->revents & POLLIN))
949 len = recv(c->fd, c->buffer_ptr, 1, 0);
951 if (ff_neterrno() != AVERROR(EAGAIN) &&
952 ff_neterrno() != AVERROR(EINTR))
954 } else if (len == 0) {
957 /* search for end of request. */
959 c->buffer_ptr += len;
961 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
962 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
963 /* request found : parse it and reply */
964 if (c->state == HTTPSTATE_WAIT_REQUEST) {
965 ret = http_parse_request(c);
967 ret = rtsp_parse_request(c);
971 } else if (ptr >= c->buffer_end) {
972 /* request too long: cannot do anything */
974 } else goto read_loop;
978 case HTTPSTATE_SEND_HEADER:
979 if (c->poll_entry->revents & (POLLERR | POLLHUP))
982 /* no need to write if no events */
983 if (!(c->poll_entry->revents & POLLOUT))
985 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
987 if (ff_neterrno() != AVERROR(EAGAIN) &&
988 ff_neterrno() != AVERROR(EINTR)) {
989 /* error : close connection */
990 av_freep(&c->pb_buffer);
994 c->buffer_ptr += len;
996 c->stream->bytes_served += len;
997 c->data_count += len;
998 if (c->buffer_ptr >= c->buffer_end) {
999 av_freep(&c->pb_buffer);
1000 /* if error, exit */
1003 /* all the buffer was sent : synchronize to the incoming stream */
1004 c->state = HTTPSTATE_SEND_DATA_HEADER;
1005 c->buffer_ptr = c->buffer_end = c->buffer;
1010 case HTTPSTATE_SEND_DATA:
1011 case HTTPSTATE_SEND_DATA_HEADER:
1012 case HTTPSTATE_SEND_DATA_TRAILER:
1013 /* for packetized output, we consider we can always write (the
1014 input streams sets the speed). It may be better to verify
1015 that we do not rely too much on the kernel queues */
1016 if (!c->is_packetized) {
1017 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1020 /* no need to read if no events */
1021 if (!(c->poll_entry->revents & POLLOUT))
1024 if (http_send_data(c) < 0)
1026 /* close connection if trailer sent */
1027 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
1030 case HTTPSTATE_RECEIVE_DATA:
1031 /* no need to read if no events */
1032 if (c->poll_entry->revents & (POLLERR | POLLHUP))
1034 if (!(c->poll_entry->revents & POLLIN))
1036 if (http_receive_data(c) < 0)
1039 case HTTPSTATE_WAIT_FEED:
1040 /* no need to read if no events */
1041 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1044 /* nothing to do, we'll be waken up by incoming feed packets */
1047 case RTSPSTATE_SEND_REPLY:
1048 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1049 av_freep(&c->pb_buffer);
1052 /* no need to write if no events */
1053 if (!(c->poll_entry->revents & POLLOUT))
1055 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1057 if (ff_neterrno() != AVERROR(EAGAIN) &&
1058 ff_neterrno() != AVERROR(EINTR)) {
1059 /* error : close connection */
1060 av_freep(&c->pb_buffer);
1064 c->buffer_ptr += len;
1065 c->data_count += len;
1066 if (c->buffer_ptr >= c->buffer_end) {
1067 /* all the buffer was sent : wait for a new request */
1068 av_freep(&c->pb_buffer);
1069 start_wait_request(c, 1);
1073 case RTSPSTATE_SEND_PACKET:
1074 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1075 av_freep(&c->packet_buffer);
1078 /* no need to write if no events */
1079 if (!(c->poll_entry->revents & POLLOUT))
1081 len = send(c->fd, c->packet_buffer_ptr,
1082 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1084 if (ff_neterrno() != AVERROR(EAGAIN) &&
1085 ff_neterrno() != AVERROR(EINTR)) {
1086 /* error : close connection */
1087 av_freep(&c->packet_buffer);
1091 c->packet_buffer_ptr += len;
1092 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1093 /* all the buffer was sent : wait for a new request */
1094 av_freep(&c->packet_buffer);
1095 c->state = RTSPSTATE_WAIT_REQUEST;
1099 case HTTPSTATE_READY:
1108 static int extract_rates(char *rates, int ratelen, const char *request)
1112 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1113 if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1114 const char *q = p + 7;
1116 while (*q && *q != '\n' && isspace(*q))
1119 if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1125 memset(rates, 0xff, ratelen);
1128 while (*q && *q != '\n' && *q != ':')
1131 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1135 if (stream_no < ratelen && stream_no >= 0)
1136 rates[stream_no] = rate_no;
1138 while (*q && *q != '\n' && !isspace(*q))
1145 p = strchr(p, '\n');
1155 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1158 int best_bitrate = 100000000;
1161 for (i = 0; i < feed->nb_streams; i++) {
1162 AVCodecContext *feed_codec = feed->streams[i]->codec;
1164 if (feed_codec->codec_id != codec->codec_id ||
1165 feed_codec->sample_rate != codec->sample_rate ||
1166 feed_codec->width != codec->width ||
1167 feed_codec->height != codec->height)
1170 /* Potential stream */
1172 /* We want the fastest stream less than bit_rate, or the slowest
1173 * faster than bit_rate
1176 if (feed_codec->bit_rate <= bit_rate) {
1177 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1178 best_bitrate = feed_codec->bit_rate;
1182 if (feed_codec->bit_rate < best_bitrate) {
1183 best_bitrate = feed_codec->bit_rate;
1192 static int modify_current_stream(HTTPContext *c, char *rates)
1195 FFStream *req = c->stream;
1196 int action_required = 0;
1198 /* Not much we can do for a feed */
1202 for (i = 0; i < req->nb_streams; i++) {
1203 AVCodecContext *codec = req->streams[i]->codec;
1207 c->switch_feed_streams[i] = req->feed_streams[i];
1210 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1213 /* Wants off or slow */
1214 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1216 /* This doesn't work well when it turns off the only stream! */
1217 c->switch_feed_streams[i] = -2;
1218 c->feed_streams[i] = -2;
1223 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1224 action_required = 1;
1227 return action_required;
1230 /* XXX: factorize in utils.c ? */
1231 /* XXX: take care with different space meaning */
1232 static void skip_spaces(const char **pp)
1236 while (*p == ' ' || *p == '\t')
1241 static void get_word(char *buf, int buf_size, const char **pp)
1249 while (!isspace(*p) && *p != '\0') {
1250 if ((q - buf) < buf_size - 1)
1259 static void get_arg(char *buf, int buf_size, const char **pp)
1266 while (isspace(*p)) p++;
1269 if (*p == '\"' || *p == '\'')
1281 if ((q - buf) < buf_size - 1)
1286 if (quote && *p == quote)
1291 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1292 const char *p, const char *filename, int line_num)
1298 get_arg(arg, sizeof(arg), &p);
1299 if (av_strcasecmp(arg, "allow") == 0)
1300 acl.action = IP_ALLOW;
1301 else if (av_strcasecmp(arg, "deny") == 0)
1302 acl.action = IP_DENY;
1304 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1305 filename, line_num, arg);
1309 get_arg(arg, sizeof(arg), &p);
1311 if (resolve_host(&acl.first, arg) != 0) {
1312 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1313 filename, line_num, arg);
1316 acl.last = acl.first;
1318 get_arg(arg, sizeof(arg), &p);
1321 if (resolve_host(&acl.last, arg) != 0) {
1322 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1323 filename, line_num, arg);
1329 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1330 IPAddressACL **naclp = 0;
1336 naclp = &stream->acl;
1342 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1343 filename, line_num);
1349 naclp = &(*naclp)->next;
1357 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1362 IPAddressACL *acl = NULL;
1366 f = fopen(stream->dynamic_acl, "r");
1368 perror(stream->dynamic_acl);
1372 acl = av_mallocz(sizeof(IPAddressACL));
1376 if (fgets(line, sizeof(line), f) == NULL)
1382 if (*p == '\0' || *p == '#')
1384 get_arg(cmd, sizeof(cmd), &p);
1386 if (!av_strcasecmp(cmd, "ACL"))
1387 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1394 static void free_acl_list(IPAddressACL *in_acl)
1396 IPAddressACL *pacl,*pacl2;
1406 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1408 enum IPAddressAction last_action = IP_DENY;
1410 struct in_addr *src = &c->from_addr.sin_addr;
1411 unsigned long src_addr = src->s_addr;
1413 for (acl = in_acl; acl; acl = acl->next) {
1414 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1415 return (acl->action == IP_ALLOW) ? 1 : 0;
1416 last_action = acl->action;
1419 /* Nothing matched, so return not the last action */
1420 return (last_action == IP_DENY) ? 1 : 0;
1423 static int validate_acl(FFStream *stream, HTTPContext *c)
1429 /* if stream->acl is null validate_acl_list will return 1 */
1430 ret = validate_acl_list(stream->acl, c);
1432 if (stream->dynamic_acl[0]) {
1433 acl = parse_dynamic_acl(stream, c);
1435 ret = validate_acl_list(acl, c);
1443 /* compute the real filename of a file by matching it without its
1444 extensions to all the stream filenames */
1445 static void compute_real_filename(char *filename, int max_size)
1452 /* compute filename by matching without the file extensions */
1453 av_strlcpy(file1, filename, sizeof(file1));
1454 p = strrchr(file1, '.');
1457 for(stream = first_stream; stream != NULL; stream = stream->next) {
1458 av_strlcpy(file2, stream->filename, sizeof(file2));
1459 p = strrchr(file2, '.');
1462 if (!strcmp(file1, file2)) {
1463 av_strlcpy(filename, stream->filename, max_size);
1478 /* parse http request and prepare header */
1479 static int http_parse_request(HTTPContext *c)
1482 enum RedirType redir_type;
1484 char info[1024], filename[1024];
1488 const char *mime_type;
1492 char *useragent = 0;
1495 get_word(cmd, sizeof(cmd), (const char **)&p);
1496 av_strlcpy(c->method, cmd, sizeof(c->method));
1498 if (!strcmp(cmd, "GET"))
1500 else if (!strcmp(cmd, "POST"))
1505 get_word(url, sizeof(url), (const char **)&p);
1506 av_strlcpy(c->url, url, sizeof(c->url));
1508 get_word(protocol, sizeof(protocol), (const char **)&p);
1509 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1512 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1515 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1517 /* find the filename and the optional info string in the request */
1518 p = strchr(url, '?');
1520 av_strlcpy(info, p, sizeof(info));
1525 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1527 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1528 if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1530 if (*useragent && *useragent != '\n' && isspace(*useragent))
1534 p = strchr(p, '\n');
1541 redir_type = REDIR_NONE;
1542 if (av_match_ext(filename, "asx")) {
1543 redir_type = REDIR_ASX;
1544 filename[strlen(filename)-1] = 'f';
1545 } else if (av_match_ext(filename, "asf") &&
1546 (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1547 /* if this isn't WMP or lookalike, return the redirector file */
1548 redir_type = REDIR_ASF;
1549 } else if (av_match_ext(filename, "rpm,ram")) {
1550 redir_type = REDIR_RAM;
1551 strcpy(filename + strlen(filename)-2, "m");
1552 } else if (av_match_ext(filename, "rtsp")) {
1553 redir_type = REDIR_RTSP;
1554 compute_real_filename(filename, sizeof(filename) - 1);
1555 } else if (av_match_ext(filename, "sdp")) {
1556 redir_type = REDIR_SDP;
1557 compute_real_filename(filename, sizeof(filename) - 1);
1560 // "redirect" / request to index.html
1561 if (!strlen(filename))
1562 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1564 stream = first_stream;
1565 while (stream != NULL) {
1566 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1568 stream = stream->next;
1570 if (stream == NULL) {
1571 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1572 http_log("File '%s' not found\n", url);
1577 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1578 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1580 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1581 c->http_error = 301;
1583 q += snprintf(q, c->buffer_size,
1584 "HTTP/1.0 301 Moved\r\n"
1586 "Content-type: text/html\r\n"
1588 "<html><head><title>Moved</title></head><body>\r\n"
1589 "You should be <a href=\"%s\">redirected</a>.\r\n"
1590 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1591 /* prepare output buffer */
1592 c->buffer_ptr = c->buffer;
1594 c->state = HTTPSTATE_SEND_HEADER;
1598 /* If this is WMP, get the rate information */
1599 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1600 if (modify_current_stream(c, ratebuf)) {
1601 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1602 if (c->switch_feed_streams[i] >= 0)
1603 c->switch_feed_streams[i] = -1;
1608 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1609 current_bandwidth += stream->bandwidth;
1611 /* If already streaming this feed, do not let start another feeder. */
1612 if (stream->feed_opened) {
1613 snprintf(msg, sizeof(msg), "This feed is already being received.");
1614 http_log("Feed '%s' already being received\n", stream->feed_filename);
1618 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1619 c->http_error = 503;
1621 q += snprintf(q, c->buffer_size,
1622 "HTTP/1.0 503 Server too busy\r\n"
1623 "Content-type: text/html\r\n"
1625 "<html><head><title>Too busy</title></head><body>\r\n"
1626 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1627 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1628 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1629 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1630 /* prepare output buffer */
1631 c->buffer_ptr = c->buffer;
1633 c->state = HTTPSTATE_SEND_HEADER;
1637 if (redir_type != REDIR_NONE) {
1640 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1641 if (av_strncasecmp(p, "Host:", 5) == 0) {
1645 p = strchr(p, '\n');
1656 while (isspace(*hostinfo))
1659 eoh = strchr(hostinfo, '\n');
1661 if (eoh[-1] == '\r')
1664 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1665 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1666 hostbuf[eoh - hostinfo] = 0;
1668 c->http_error = 200;
1670 switch(redir_type) {
1672 q += snprintf(q, c->buffer_size,
1673 "HTTP/1.0 200 ASX Follows\r\n"
1674 "Content-type: video/x-ms-asf\r\n"
1676 "<ASX Version=\"3\">\r\n"
1677 //"<!-- Autogenerated by avserver -->\r\n"
1678 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1679 "</ASX>\r\n", hostbuf, filename, info);
1682 q += snprintf(q, c->buffer_size,
1683 "HTTP/1.0 200 RAM Follows\r\n"
1684 "Content-type: audio/x-pn-realaudio\r\n"
1686 "# Autogenerated by avserver\r\n"
1687 "http://%s/%s%s\r\n", hostbuf, filename, info);
1690 q += snprintf(q, c->buffer_size,
1691 "HTTP/1.0 200 ASF Redirect follows\r\n"
1692 "Content-type: video/x-ms-asf\r\n"
1695 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1699 char hostname[256], *p;
1700 /* extract only hostname */
1701 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1702 p = strrchr(hostname, ':');
1705 q += snprintf(q, c->buffer_size,
1706 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1707 /* XXX: incorrect mime type ? */
1708 "Content-type: application/x-rtsp\r\n"
1710 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1718 struct sockaddr_in my_addr;
1720 q += snprintf(q, c->buffer_size,
1721 "HTTP/1.0 200 OK\r\n"
1722 "Content-type: application/sdp\r\n"
1725 len = sizeof(my_addr);
1726 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1728 /* XXX: should use a dynamic buffer */
1729 sdp_data_size = prepare_sdp_description(stream,
1732 if (sdp_data_size > 0) {
1733 memcpy(q, sdp_data, sdp_data_size);
1745 /* prepare output buffer */
1746 c->buffer_ptr = c->buffer;
1748 c->state = HTTPSTATE_SEND_HEADER;
1754 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1758 stream->conns_served++;
1760 /* XXX: add there authenticate and IP match */
1763 /* if post, it means a feed is being sent */
1764 if (!stream->is_feed) {
1765 /* However it might be a status report from WMP! Let us log the
1766 * data as it might come in handy one day. */
1770 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1771 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1775 if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1776 client_id = strtol(p + 18, 0, 10);
1777 p = strchr(p, '\n');
1785 char *eol = strchr(logline, '\n');
1790 if (eol[-1] == '\r')
1792 http_log("%.*s\n", (int) (eol - logline), logline);
1793 c->suppress_log = 1;
1798 http_log("\nGot request:\n%s\n", c->buffer);
1801 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1804 /* Now we have to find the client_id */
1805 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1806 if (wmpc->wmp_client_id == client_id)
1810 if (wmpc && modify_current_stream(wmpc, ratebuf))
1811 wmpc->switch_pending = 1;
1814 snprintf(msg, sizeof(msg), "POST command not handled");
1818 if (http_start_receive_data(c) < 0) {
1819 snprintf(msg, sizeof(msg), "could not open feed");
1823 c->state = HTTPSTATE_RECEIVE_DATA;
1828 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1829 http_log("\nGot request:\n%s\n", c->buffer);
1832 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1835 /* open input stream */
1836 if (open_input_stream(c, info) < 0) {
1837 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1841 /* prepare http header */
1843 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1844 mime_type = c->stream->fmt->mime_type;
1846 mime_type = "application/x-octet-stream";
1847 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1849 /* for asf, we need extra headers */
1850 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1851 /* Need to allocate a client id */
1853 c->wmp_client_id = av_lfg_get(&random_state);
1855 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);
1857 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1858 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1860 /* prepare output buffer */
1862 c->buffer_ptr = c->buffer;
1864 c->state = HTTPSTATE_SEND_HEADER;
1867 c->http_error = 404;
1869 q += snprintf(q, c->buffer_size,
1870 "HTTP/1.0 404 Not Found\r\n"
1871 "Content-type: text/html\r\n"
1874 "<head><title>404 Not Found</title></head>\n"
1877 /* prepare output buffer */
1878 c->buffer_ptr = c->buffer;
1880 c->state = HTTPSTATE_SEND_HEADER;
1884 c->http_error = 200; /* horrible : we use this value to avoid
1885 going to the send data state */
1886 c->state = HTTPSTATE_SEND_HEADER;
1890 static void fmt_bytecount(AVIOContext *pb, int64_t count)
1892 static const char suffix[] = " kMGTP";
1895 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1897 avio_printf(pb, "%"PRId64"%c", count, *s);
1900 static void compute_status(HTTPContext *c)
1909 if (avio_open_dyn_buf(&pb) < 0) {
1910 /* XXX: return an error ? */
1911 c->buffer_ptr = c->buffer;
1912 c->buffer_end = c->buffer;
1916 avio_printf(pb, "HTTP/1.0 200 OK\r\n");
1917 avio_printf(pb, "Content-type: %s\r\n", "text/html");
1918 avio_printf(pb, "Pragma: no-cache\r\n");
1919 avio_printf(pb, "\r\n");
1921 avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1922 if (c->stream->feed_filename[0])
1923 avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1924 avio_printf(pb, "</head>\n<body>");
1925 avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
1927 avio_printf(pb, "<h2>Available Streams</h2>\n");
1928 avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
1929 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");
1930 stream = first_stream;
1931 while (stream != NULL) {
1932 char sfilename[1024];
1935 if (stream->feed != stream) {
1936 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1937 eosf = sfilename + strlen(sfilename);
1938 if (eosf - sfilename >= 4) {
1939 if (strcmp(eosf - 4, ".asf") == 0)
1940 strcpy(eosf - 4, ".asx");
1941 else if (strcmp(eosf - 3, ".rm") == 0)
1942 strcpy(eosf - 3, ".ram");
1943 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1944 /* generate a sample RTSP director if
1945 unicast. Generate an SDP redirector if
1947 eosf = strrchr(sfilename, '.');
1949 eosf = sfilename + strlen(sfilename);
1950 if (stream->is_multicast)
1951 strcpy(eosf, ".sdp");
1953 strcpy(eosf, ".rtsp");
1957 avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1958 sfilename, stream->filename);
1959 avio_printf(pb, "<td align=right> %d <td align=right> ",
1960 stream->conns_served);
1961 fmt_bytecount(pb, stream->bytes_served);
1962 switch(stream->stream_type) {
1963 case STREAM_TYPE_LIVE: {
1964 int audio_bit_rate = 0;
1965 int video_bit_rate = 0;
1966 const char *audio_codec_name = "";
1967 const char *video_codec_name = "";
1968 const char *audio_codec_name_extra = "";
1969 const char *video_codec_name_extra = "";
1971 for(i=0;i<stream->nb_streams;i++) {
1972 AVStream *st = stream->streams[i];
1973 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1974 switch(st->codec->codec_type) {
1975 case AVMEDIA_TYPE_AUDIO:
1976 audio_bit_rate += st->codec->bit_rate;
1978 if (*audio_codec_name)
1979 audio_codec_name_extra = "...";
1980 audio_codec_name = codec->name;
1983 case AVMEDIA_TYPE_VIDEO:
1984 video_bit_rate += st->codec->bit_rate;
1986 if (*video_codec_name)
1987 video_codec_name_extra = "...";
1988 video_codec_name = codec->name;
1991 case AVMEDIA_TYPE_DATA:
1992 video_bit_rate += st->codec->bit_rate;
1998 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",
2001 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
2002 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
2004 avio_printf(pb, "<td>%s", stream->feed->filename);
2006 avio_printf(pb, "<td>%s", stream->feed_filename);
2007 avio_printf(pb, "\n");
2011 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
2015 stream = stream->next;
2017 avio_printf(pb, "</table>\n");
2019 stream = first_stream;
2020 while (stream != NULL) {
2021 if (stream->feed == stream) {
2022 avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
2024 avio_printf(pb, "Running as pid %d.\n", stream->pid);
2026 #if defined(linux) && !defined(CONFIG_NOCUTILS)
2031 /* This is somewhat linux specific I guess */
2032 snprintf(ps_cmd, sizeof(ps_cmd),
2033 "ps -o \"%%cpu,cputime\" --no-headers %d",
2036 pid_stat = popen(ps_cmd, "r");
2041 if (fscanf(pid_stat, "%10s %64s", cpuperc,
2043 avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2051 avio_printf(pb, "<p>");
2053 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");
2055 for (i = 0; i < stream->nb_streams; i++) {
2056 AVStream *st = stream->streams[i];
2057 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2058 const char *type = "unknown";
2059 char parameters[64];
2063 switch(st->codec->codec_type) {
2064 case AVMEDIA_TYPE_AUDIO:
2066 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2068 case AVMEDIA_TYPE_VIDEO:
2070 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2071 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2076 avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2077 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2079 avio_printf(pb, "</table>\n");
2082 stream = stream->next;
2085 /* connection status */
2086 avio_printf(pb, "<h2>Connection Status</h2>\n");
2088 avio_printf(pb, "Number of connections: %d / %d<br>\n",
2089 nb_connections, nb_max_connections);
2091 avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2092 current_bandwidth, max_bandwidth);
2094 avio_printf(pb, "<table>\n");
2095 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");
2096 c1 = first_http_ctx;
2098 while (c1 != NULL) {
2104 for (j = 0; j < c1->stream->nb_streams; j++) {
2105 if (!c1->stream->feed)
2106 bitrate += c1->stream->streams[j]->codec->bit_rate;
2107 else if (c1->feed_streams[j] >= 0)
2108 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2113 p = inet_ntoa(c1->from_addr.sin_addr);
2114 avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2116 c1->stream ? c1->stream->filename : "",
2117 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2120 http_state[c1->state]);
2121 fmt_bytecount(pb, bitrate);
2122 avio_printf(pb, "<td align=right>");
2123 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2124 avio_printf(pb, "<td align=right>");
2125 fmt_bytecount(pb, c1->data_count);
2126 avio_printf(pb, "\n");
2129 avio_printf(pb, "</table>\n");
2134 avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
2135 avio_printf(pb, "</body>\n</html>\n");
2137 len = avio_close_dyn_buf(pb, &c->pb_buffer);
2138 c->buffer_ptr = c->pb_buffer;
2139 c->buffer_end = c->pb_buffer + len;
2142 static int open_input_stream(HTTPContext *c, const char *info)
2145 char input_filename[1024];
2146 AVFormatContext *s = NULL;
2150 /* find file name */
2151 if (c->stream->feed) {
2152 strcpy(input_filename, c->stream->feed->feed_filename);
2153 /* compute position (absolute time) */
2154 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2155 if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
2157 } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2158 int prebuffer = strtol(buf, 0, 10);
2159 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2161 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2163 strcpy(input_filename, c->stream->feed_filename);
2164 /* compute position (relative time) */
2165 if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2166 if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
2171 if (input_filename[0] == '\0')
2175 if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2176 http_log("could not open %s: %d\n", input_filename, ret);
2179 s->flags |= AVFMT_FLAG_GENPTS;
2181 if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2182 http_log("Could not find stream info '%s'\n", input_filename);
2183 avformat_close_input(&s);
2187 /* choose stream as clock source (we favorize video stream if
2188 present) for packet sending */
2189 c->pts_stream_index = 0;
2190 for(i=0;i<c->stream->nb_streams;i++) {
2191 if (c->pts_stream_index == 0 &&
2192 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2193 c->pts_stream_index = i;
2197 if (c->fmt_in->iformat->read_seek)
2198 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2199 /* set the start time (needed for maxtime and RTP packet timing) */
2200 c->start_time = cur_time;
2201 c->first_pts = AV_NOPTS_VALUE;
2205 /* return the server clock (in us) */
2206 static int64_t get_server_clock(HTTPContext *c)
2208 /* compute current pts value from system time */
2209 return (cur_time - c->start_time) * 1000;
2212 /* return the estimated time at which the current packet must be sent
2214 static int64_t get_packet_send_clock(HTTPContext *c)
2216 int bytes_left, bytes_sent, frame_bytes;
2218 frame_bytes = c->cur_frame_bytes;
2219 if (frame_bytes <= 0)
2222 bytes_left = c->buffer_end - c->buffer_ptr;
2223 bytes_sent = frame_bytes - bytes_left;
2224 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2229 static int http_prepare_data(HTTPContext *c)
2232 AVFormatContext *ctx;
2234 av_freep(&c->pb_buffer);
2236 case HTTPSTATE_SEND_DATA_HEADER:
2237 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2238 av_dict_set(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2239 av_dict_set(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2240 av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2241 av_dict_set(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2243 c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2245 for(i=0;i<c->stream->nb_streams;i++) {
2247 c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2248 /* if file or feed, then just take streams from FFStream struct */
2249 if (!c->stream->feed ||
2250 c->stream->feed == c->stream)
2251 src = c->stream->streams[i];
2253 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2255 *(c->fmt_ctx.streams[i]) = *src;
2256 c->fmt_ctx.streams[i]->priv_data = 0;
2257 c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2258 AVStream, not in codec */
2260 /* set output format parameters */
2261 c->fmt_ctx.oformat = c->stream->fmt;
2262 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2264 c->got_key_frame = 0;
2266 /* prepare header and save header data in a stream */
2267 if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2268 /* XXX: potential leak */
2271 c->fmt_ctx.pb->seekable = 0;
2274 * HACK to avoid mpeg ps muxer to spit many underflow errors
2275 * Default value from Libav
2276 * Try to set it use configuration option
2278 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2280 if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
2281 http_log("Error writing output header\n");
2284 av_dict_free(&c->fmt_ctx.metadata);
2286 len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2287 c->buffer_ptr = c->pb_buffer;
2288 c->buffer_end = c->pb_buffer + len;
2290 c->state = HTTPSTATE_SEND_DATA;
2291 c->last_packet_sent = 0;
2293 case HTTPSTATE_SEND_DATA:
2294 /* find a new packet */
2295 /* read a packet from the input stream */
2296 if (c->stream->feed)
2297 ffm_set_write_index(c->fmt_in,
2298 c->stream->feed->feed_write_index,
2299 c->stream->feed->feed_size);
2301 if (c->stream->max_time &&
2302 c->stream->max_time + c->start_time - cur_time < 0)
2303 /* We have timed out */
2304 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2308 ret = av_read_frame(c->fmt_in, &pkt);
2310 if (c->stream->feed) {
2311 /* if coming from feed, it means we reached the end of the
2312 ffm file, so must wait for more data */
2313 c->state = HTTPSTATE_WAIT_FEED;
2314 return 1; /* state changed */
2315 } else if (ret == AVERROR(EAGAIN)) {
2316 /* input not ready, come back later */
2319 if (c->stream->loop) {
2320 avformat_close_input(&c->fmt_in);
2321 if (open_input_stream(c, "") < 0)
2326 /* must send trailer now because eof or error */
2327 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2331 int source_index = pkt.stream_index;
2332 /* update first pts if needed */
2333 if (c->first_pts == AV_NOPTS_VALUE) {
2334 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2335 c->start_time = cur_time;
2337 /* send it to the appropriate stream */
2338 if (c->stream->feed) {
2339 /* if coming from a feed, select the right stream */
2340 if (c->switch_pending) {
2341 c->switch_pending = 0;
2342 for(i=0;i<c->stream->nb_streams;i++) {
2343 if (c->switch_feed_streams[i] == pkt.stream_index)
2344 if (pkt.flags & AV_PKT_FLAG_KEY)
2345 c->switch_feed_streams[i] = -1;
2346 if (c->switch_feed_streams[i] >= 0)
2347 c->switch_pending = 1;
2350 for(i=0;i<c->stream->nb_streams;i++) {
2351 if (c->stream->feed_streams[i] == pkt.stream_index) {
2352 AVStream *st = c->fmt_in->streams[source_index];
2353 pkt.stream_index = i;
2354 if (pkt.flags & AV_PKT_FLAG_KEY &&
2355 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2356 c->stream->nb_streams == 1))
2357 c->got_key_frame = 1;
2358 if (!c->stream->send_on_key || c->got_key_frame)
2363 AVCodecContext *codec;
2364 AVStream *ist, *ost;
2366 ist = c->fmt_in->streams[source_index];
2367 /* specific handling for RTP: we use several
2368 output stream (one for each RTP
2369 connection). XXX: need more abstract handling */
2370 if (c->is_packetized) {
2371 /* compute send time and duration */
2372 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2373 c->cur_pts -= c->first_pts;
2374 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2375 /* find RTP context */
2376 c->packet_stream_index = pkt.stream_index;
2377 ctx = c->rtp_ctx[c->packet_stream_index];
2379 av_free_packet(&pkt);
2382 codec = ctx->streams[0]->codec;
2383 /* only one stream per RTP connection */
2384 pkt.stream_index = 0;
2388 codec = ctx->streams[pkt.stream_index]->codec;
2391 if (c->is_packetized) {
2392 int max_packet_size;
2393 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2394 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2396 max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
2397 ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2399 ret = avio_open_dyn_buf(&ctx->pb);
2402 /* XXX: potential leak */
2405 ost = ctx->streams[pkt.stream_index];
2407 ctx->pb->seekable = 0;
2408 if (pkt.dts != AV_NOPTS_VALUE)
2409 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2410 if (pkt.pts != AV_NOPTS_VALUE)
2411 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2412 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2413 if (av_write_frame(ctx, &pkt) < 0) {
2414 http_log("Error writing frame to output\n");
2415 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2418 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2419 c->cur_frame_bytes = len;
2420 c->buffer_ptr = c->pb_buffer;
2421 c->buffer_end = c->pb_buffer + len;
2423 codec->frame_number++;
2425 av_free_packet(&pkt);
2429 av_free_packet(&pkt);
2434 case HTTPSTATE_SEND_DATA_TRAILER:
2435 /* last packet test ? */
2436 if (c->last_packet_sent || c->is_packetized)
2439 /* prepare header */
2440 if (avio_open_dyn_buf(&ctx->pb) < 0) {
2441 /* XXX: potential leak */
2444 c->fmt_ctx.pb->seekable = 0;
2445 av_write_trailer(ctx);
2446 len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2447 c->buffer_ptr = c->pb_buffer;
2448 c->buffer_end = c->pb_buffer + len;
2450 c->last_packet_sent = 1;
2456 /* should convert the format at the same time */
2457 /* send data starting at c->buffer_ptr to the output connection
2458 (either UDP or TCP connection) */
2459 static int http_send_data(HTTPContext *c)
2464 if (c->buffer_ptr >= c->buffer_end) {
2465 ret = http_prepare_data(c);
2469 /* state change requested */
2472 if (c->is_packetized) {
2473 /* RTP data output */
2474 len = c->buffer_end - c->buffer_ptr;
2476 /* fail safe - should never happen */
2478 c->buffer_ptr = c->buffer_end;
2481 len = (c->buffer_ptr[0] << 24) |
2482 (c->buffer_ptr[1] << 16) |
2483 (c->buffer_ptr[2] << 8) |
2485 if (len > (c->buffer_end - c->buffer_ptr))
2487 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2488 /* nothing to send yet: we can wait */
2492 c->data_count += len;
2493 update_datarate(&c->datarate, c->data_count);
2495 c->stream->bytes_served += len;
2497 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2498 /* RTP packets are sent inside the RTSP TCP connection */
2500 int interleaved_index, size;
2502 HTTPContext *rtsp_c;
2505 /* if no RTSP connection left, error */
2508 /* if already sending something, then wait. */
2509 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2511 if (avio_open_dyn_buf(&pb) < 0)
2513 interleaved_index = c->packet_stream_index * 2;
2514 /* RTCP packets are sent at odd indexes */
2515 if (c->buffer_ptr[1] == 200)
2516 interleaved_index++;
2517 /* write RTSP TCP header */
2519 header[1] = interleaved_index;
2520 header[2] = len >> 8;
2522 avio_write(pb, header, 4);
2523 /* write RTP packet data */
2525 avio_write(pb, c->buffer_ptr, len);
2526 size = avio_close_dyn_buf(pb, &c->packet_buffer);
2527 /* prepare asynchronous TCP sending */
2528 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2529 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2530 c->buffer_ptr += len;
2532 /* send everything we can NOW */
2533 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2534 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2536 rtsp_c->packet_buffer_ptr += len;
2537 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2538 /* if we could not send all the data, we will
2539 send it later, so a new state is needed to
2540 "lock" the RTSP TCP connection */
2541 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2544 /* all data has been sent */
2545 av_freep(&c->packet_buffer);
2547 /* send RTP packet directly in UDP */
2549 ffurl_write(c->rtp_handles[c->packet_stream_index],
2550 c->buffer_ptr, len);
2551 c->buffer_ptr += len;
2552 /* here we continue as we can send several packets per 10 ms slot */
2555 /* TCP data output */
2556 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2558 if (ff_neterrno() != AVERROR(EAGAIN) &&
2559 ff_neterrno() != AVERROR(EINTR))
2560 /* error : close connection */
2565 c->buffer_ptr += len;
2567 c->data_count += len;
2568 update_datarate(&c->datarate, c->data_count);
2570 c->stream->bytes_served += len;
2578 static int http_start_receive_data(HTTPContext *c)
2582 if (c->stream->feed_opened)
2585 /* Don't permit writing to this one */
2586 if (c->stream->readonly)
2590 fd = open(c->stream->feed_filename, O_RDWR);
2592 http_log("Error opening feeder file: %s\n", strerror(errno));
2597 if (c->stream->truncate) {
2598 /* truncate feed file */
2599 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2600 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2601 if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
2602 http_log("Error truncating feed file: %s\n", strerror(errno));
2606 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2607 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2612 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2613 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2614 lseek(fd, 0, SEEK_SET);
2616 /* init buffer input */
2617 c->buffer_ptr = c->buffer;
2618 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2619 c->stream->feed_opened = 1;
2620 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2624 static int http_receive_data(HTTPContext *c)
2627 int len, loop_run = 0;
2629 while (c->chunked_encoding && !c->chunk_size &&
2630 c->buffer_end > c->buffer_ptr) {
2631 /* read chunk header, if present */
2632 len = recv(c->fd, c->buffer_ptr, 1, 0);
2635 if (ff_neterrno() != AVERROR(EAGAIN) &&
2636 ff_neterrno() != AVERROR(EINTR))
2637 /* error : close connection */
2640 } else if (len == 0) {
2641 /* end of connection : close it */
2643 } else if (c->buffer_ptr - c->buffer >= 2 &&
2644 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2645 c->chunk_size = strtol(c->buffer, 0, 16);
2646 if (c->chunk_size == 0) // end of stream
2648 c->buffer_ptr = c->buffer;
2650 } else if (++loop_run > 10) {
2651 /* no chunk header, abort */
2658 if (c->buffer_end > c->buffer_ptr) {
2659 len = recv(c->fd, c->buffer_ptr,
2660 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2662 if (ff_neterrno() != AVERROR(EAGAIN) &&
2663 ff_neterrno() != AVERROR(EINTR))
2664 /* error : close connection */
2666 } else if (len == 0)
2667 /* end of connection : close it */
2670 c->chunk_size -= len;
2671 c->buffer_ptr += len;
2672 c->data_count += len;
2673 update_datarate(&c->datarate, c->data_count);
2677 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2678 if (c->buffer[0] != 'f' ||
2679 c->buffer[1] != 'm') {
2680 http_log("Feed stream has become desynchronized -- disconnecting\n");
2685 if (c->buffer_ptr >= c->buffer_end) {
2686 FFStream *feed = c->stream;
2687 /* a packet has been received : write it in the store, except
2689 if (c->data_count > FFM_PACKET_SIZE) {
2690 /* XXX: use llseek or url_seek */
2691 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2692 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2693 http_log("Error writing to feed file: %s\n", strerror(errno));
2697 feed->feed_write_index += FFM_PACKET_SIZE;
2698 /* update file size */
2699 if (feed->feed_write_index > c->stream->feed_size)
2700 feed->feed_size = feed->feed_write_index;
2702 /* handle wrap around if max file size reached */
2703 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2704 feed->feed_write_index = FFM_PACKET_SIZE;
2707 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2708 http_log("Error writing index to feed file: %s\n", strerror(errno));
2712 /* wake up any waiting connections */
2713 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2714 if (c1->state == HTTPSTATE_WAIT_FEED &&
2715 c1->stream->feed == c->stream->feed)
2716 c1->state = HTTPSTATE_SEND_DATA;
2719 /* We have a header in our hands that contains useful data */
2720 AVFormatContext *s = avformat_alloc_context();
2722 AVInputFormat *fmt_in;
2728 /* use feed output format name to find corresponding input format */
2729 fmt_in = av_find_input_format(feed->fmt->name);
2733 pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2734 0, NULL, NULL, NULL, NULL);
2738 if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2743 /* Now we have the actual streams */
2744 if (s->nb_streams != feed->nb_streams) {
2745 avformat_close_input(&s);
2747 http_log("Feed '%s' stream number does not match registered feed\n",
2748 c->stream->feed_filename);
2752 for (i = 0; i < s->nb_streams; i++) {
2753 AVStream *fst = feed->streams[i];
2754 AVStream *st = s->streams[i];
2755 avcodec_copy_context(fst->codec, st->codec);
2758 avformat_close_input(&s);
2761 c->buffer_ptr = c->buffer;
2766 c->stream->feed_opened = 0;
2768 /* wake up any waiting connections to stop waiting for feed */
2769 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2770 if (c1->state == HTTPSTATE_WAIT_FEED &&
2771 c1->stream->feed == c->stream->feed)
2772 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2777 /********************************************************************/
2780 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2787 switch(error_number) {
2788 case RTSP_STATUS_OK:
2791 case RTSP_STATUS_METHOD:
2792 str = "Method Not Allowed";
2794 case RTSP_STATUS_BANDWIDTH:
2795 str = "Not Enough Bandwidth";
2797 case RTSP_STATUS_SESSION:
2798 str = "Session Not Found";
2800 case RTSP_STATUS_STATE:
2801 str = "Method Not Valid in This State";
2803 case RTSP_STATUS_AGGREGATE:
2804 str = "Aggregate operation not allowed";
2806 case RTSP_STATUS_ONLY_AGGREGATE:
2807 str = "Only aggregate operation allowed";
2809 case RTSP_STATUS_TRANSPORT:
2810 str = "Unsupported transport";
2812 case RTSP_STATUS_INTERNAL:
2813 str = "Internal Server Error";
2815 case RTSP_STATUS_SERVICE:
2816 str = "Service Unavailable";
2818 case RTSP_STATUS_VERSION:
2819 str = "RTSP Version not supported";
2822 str = "Unknown Error";
2826 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2827 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2829 /* output GMT time */
2832 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2833 avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2836 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2838 rtsp_reply_header(c, error_number);
2839 avio_printf(c->pb, "\r\n");
2842 static int rtsp_parse_request(HTTPContext *c)
2844 const char *p, *p1, *p2;
2850 RTSPMessageHeader header1 = { 0 }, *header = &header1;
2852 c->buffer_ptr[0] = '\0';
2855 get_word(cmd, sizeof(cmd), &p);
2856 get_word(url, sizeof(url), &p);
2857 get_word(protocol, sizeof(protocol), &p);
2859 av_strlcpy(c->method, cmd, sizeof(c->method));
2860 av_strlcpy(c->url, url, sizeof(c->url));
2861 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2863 if (avio_open_dyn_buf(&c->pb) < 0) {
2864 /* XXX: cannot do more */
2865 c->pb = NULL; /* safety */
2869 /* check version name */
2870 if (strcmp(protocol, "RTSP/1.0") != 0) {
2871 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2875 /* parse each header line */
2876 /* skip to next line */
2877 while (*p != '\n' && *p != '\0')
2881 while (*p != '\0') {
2882 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2886 if (p2 > p && p2[-1] == '\r')
2888 /* skip empty line */
2892 if (len > sizeof(line) - 1)
2893 len = sizeof(line) - 1;
2894 memcpy(line, p, len);
2896 ff_rtsp_parse_line(header, line, NULL, NULL);
2900 /* handle sequence number */
2901 c->seq = header->seq;
2903 if (!strcmp(cmd, "DESCRIBE"))
2904 rtsp_cmd_describe(c, url);
2905 else if (!strcmp(cmd, "OPTIONS"))
2906 rtsp_cmd_options(c, url);
2907 else if (!strcmp(cmd, "SETUP"))
2908 rtsp_cmd_setup(c, url, header);
2909 else if (!strcmp(cmd, "PLAY"))
2910 rtsp_cmd_play(c, url, header);
2911 else if (!strcmp(cmd, "PAUSE"))
2912 rtsp_cmd_pause(c, url, header);
2913 else if (!strcmp(cmd, "TEARDOWN"))
2914 rtsp_cmd_teardown(c, url, header);
2916 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2919 len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2920 c->pb = NULL; /* safety */
2922 /* XXX: cannot do more */
2925 c->buffer_ptr = c->pb_buffer;
2926 c->buffer_end = c->pb_buffer + len;
2927 c->state = RTSPSTATE_SEND_REPLY;
2931 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2932 struct in_addr my_ip)
2934 AVFormatContext *avc;
2935 AVStream *avs = NULL;
2938 avc = avformat_alloc_context();
2942 av_dict_set(&avc->metadata, "title",
2943 stream->title[0] ? stream->title : "No Title", 0);
2944 avc->nb_streams = stream->nb_streams;
2945 if (stream->is_multicast) {
2946 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2947 inet_ntoa(stream->multicast_ip),
2948 stream->multicast_port, stream->multicast_ttl);
2950 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2953 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2954 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2956 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2957 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2960 for(i = 0; i < stream->nb_streams; i++) {
2961 avc->streams[i] = &avs[i];
2962 avc->streams[i]->codec = stream->streams[i]->codec;
2964 *pbuffer = av_mallocz(2048);
2965 av_sdp_create(&avc, 1, *pbuffer, 2048);
2968 av_free(avc->streams);
2969 av_dict_free(&avc->metadata);
2973 return strlen(*pbuffer);
2976 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2978 // rtsp_reply_header(c, RTSP_STATUS_OK);
2979 avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2980 avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2981 avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2982 avio_printf(c->pb, "\r\n");
2985 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2993 struct sockaddr_in my_addr;
2995 /* find which url is asked */
2996 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3001 for(stream = first_stream; stream != NULL; stream = stream->next) {
3002 if (!stream->is_feed &&
3003 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
3004 !strcmp(path, stream->filename)) {
3008 /* no stream found */
3009 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3013 /* prepare the media description in sdp format */
3015 /* get the host IP */
3016 len = sizeof(my_addr);
3017 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
3018 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3019 if (content_length < 0) {
3020 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3023 rtsp_reply_header(c, RTSP_STATUS_OK);
3024 avio_printf(c->pb, "Content-Base: %s/\r\n", url);
3025 avio_printf(c->pb, "Content-Type: application/sdp\r\n");
3026 avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
3027 avio_printf(c->pb, "\r\n");
3028 avio_write(c->pb, content, content_length);
3032 static HTTPContext *find_rtp_session(const char *session_id)
3036 if (session_id[0] == '\0')
3039 for(c = first_http_ctx; c != NULL; c = c->next) {
3040 if (!strcmp(c->session_id, session_id))
3046 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3048 RTSPTransportField *th;
3051 for(i=0;i<h->nb_transports;i++) {
3052 th = &h->transports[i];
3053 if (th->lower_transport == lower_transport)
3059 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3060 RTSPMessageHeader *h)
3063 int stream_index, rtp_port, rtcp_port;
3068 RTSPTransportField *th;
3069 struct sockaddr_in dest_addr;
3070 RTSPActionServerSetup setup;
3072 /* find which url is asked */
3073 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3078 /* now check each stream */
3079 for(stream = first_stream; stream != NULL; stream = stream->next) {
3080 if (!stream->is_feed &&
3081 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3082 /* accept aggregate filenames only if single stream */
3083 if (!strcmp(path, stream->filename)) {
3084 if (stream->nb_streams != 1) {
3085 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3092 for(stream_index = 0; stream_index < stream->nb_streams;
3094 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3095 stream->filename, stream_index);
3096 if (!strcmp(path, buf))
3101 /* no stream found */
3102 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3106 /* generate session id if needed */
3107 if (h->session_id[0] == '\0')
3108 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3109 av_lfg_get(&random_state), av_lfg_get(&random_state));
3111 /* find rtp session, and create it if none found */
3112 rtp_c = find_rtp_session(h->session_id);
3114 /* always prefer UDP */
3115 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3117 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3119 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3124 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3125 th->lower_transport);
3127 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3131 /* open input stream */
3132 if (open_input_stream(rtp_c, "") < 0) {
3133 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3138 /* test if stream is OK (test needed because several SETUP needs
3139 to be done for a given file) */
3140 if (rtp_c->stream != stream) {
3141 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3145 /* test if stream is already set up */
3146 if (rtp_c->rtp_ctx[stream_index]) {
3147 rtsp_reply_error(c, RTSP_STATUS_STATE);
3151 /* check transport */
3152 th = find_transport(h, rtp_c->rtp_protocol);
3153 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3154 th->client_port_min <= 0)) {
3155 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3159 /* setup default options */
3160 setup.transport_option[0] = '\0';
3161 dest_addr = rtp_c->from_addr;
3162 dest_addr.sin_port = htons(th->client_port_min);
3165 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3166 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3170 /* now everything is OK, so we can send the connection parameters */
3171 rtsp_reply_header(c, RTSP_STATUS_OK);
3173 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3175 switch(rtp_c->rtp_protocol) {
3176 case RTSP_LOWER_TRANSPORT_UDP:
3177 rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3178 rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3179 avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3180 "client_port=%d-%d;server_port=%d-%d",
3181 th->client_port_min, th->client_port_max,
3182 rtp_port, rtcp_port);
3184 case RTSP_LOWER_TRANSPORT_TCP:
3185 avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3186 stream_index * 2, stream_index * 2 + 1);
3191 if (setup.transport_option[0] != '\0')
3192 avio_printf(c->pb, ";%s", setup.transport_option);
3193 avio_printf(c->pb, "\r\n");
3196 avio_printf(c->pb, "\r\n");
3200 /* find an rtp connection by using the session ID. Check consistency
3202 static HTTPContext *find_rtp_session_with_url(const char *url,
3203 const char *session_id)
3211 rtp_c = find_rtp_session(session_id);
3215 /* find which url is asked */
3216 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3220 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3221 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3222 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3223 rtp_c->stream->filename, s);
3224 if(!strncmp(path, buf, sizeof(buf))) {
3225 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3230 if (len > 0 && path[len - 1] == '/' &&
3231 !strncmp(path, rtp_c->stream->filename, len - 1))
3236 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3240 rtp_c = find_rtp_session_with_url(url, h->session_id);
3242 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3246 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3247 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3248 rtp_c->state != HTTPSTATE_READY) {
3249 rtsp_reply_error(c, RTSP_STATUS_STATE);
3253 rtp_c->state = HTTPSTATE_SEND_DATA;
3255 /* now everything is OK, so we can send the connection parameters */
3256 rtsp_reply_header(c, RTSP_STATUS_OK);
3258 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3259 avio_printf(c->pb, "\r\n");
3262 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3266 rtp_c = find_rtp_session_with_url(url, h->session_id);
3268 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3272 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3273 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3274 rtsp_reply_error(c, RTSP_STATUS_STATE);
3278 rtp_c->state = HTTPSTATE_READY;
3279 rtp_c->first_pts = AV_NOPTS_VALUE;
3280 /* now everything is OK, so we can send the connection parameters */
3281 rtsp_reply_header(c, RTSP_STATUS_OK);
3283 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3284 avio_printf(c->pb, "\r\n");
3287 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3291 rtp_c = find_rtp_session_with_url(url, h->session_id);
3293 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3297 /* now everything is OK, so we can send the connection parameters */
3298 rtsp_reply_header(c, RTSP_STATUS_OK);
3300 avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3301 avio_printf(c->pb, "\r\n");
3303 /* abort the session */
3304 close_connection(rtp_c);
3308 /********************************************************************/
3311 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3312 FFStream *stream, const char *session_id,
3313 enum RTSPLowerTransport rtp_protocol)
3315 HTTPContext *c = NULL;
3316 const char *proto_str;
3318 /* XXX: should output a warning page when coming
3319 close to the connection limit */
3320 if (nb_connections >= nb_max_connections)
3323 /* add a new connection */
3324 c = av_mallocz(sizeof(HTTPContext));
3329 c->poll_entry = NULL;
3330 c->from_addr = *from_addr;
3331 c->buffer_size = IOBUFFER_INIT_SIZE;
3332 c->buffer = av_malloc(c->buffer_size);
3337 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3338 c->state = HTTPSTATE_READY;
3339 c->is_packetized = 1;
3340 c->rtp_protocol = rtp_protocol;
3342 /* protocol is shown in statistics */
3343 switch(c->rtp_protocol) {
3344 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3345 proto_str = "MCAST";
3347 case RTSP_LOWER_TRANSPORT_UDP:
3350 case RTSP_LOWER_TRANSPORT_TCP:
3357 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3358 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3360 current_bandwidth += stream->bandwidth;
3362 c->next = first_http_ctx;
3374 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3375 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3377 static int rtp_new_av_stream(HTTPContext *c,
3378 int stream_index, struct sockaddr_in *dest_addr,
3379 HTTPContext *rtsp_c)
3381 AVFormatContext *ctx;
3384 URLContext *h = NULL;
3386 int max_packet_size;
3388 /* now we can open the relevant output stream */
3389 ctx = avformat_alloc_context();
3392 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3394 st = av_mallocz(sizeof(AVStream));
3397 ctx->nb_streams = 1;
3398 ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3401 ctx->streams[0] = st;
3403 if (!c->stream->feed ||
3404 c->stream->feed == c->stream)
3405 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3408 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3410 st->priv_data = NULL;
3412 /* build destination RTP address */
3413 ipaddr = inet_ntoa(dest_addr->sin_addr);
3415 switch(c->rtp_protocol) {
3416 case RTSP_LOWER_TRANSPORT_UDP:
3417 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3420 /* XXX: also pass as parameter to function ? */
3421 if (c->stream->is_multicast) {
3423 ttl = c->stream->multicast_ttl;
3426 snprintf(ctx->filename, sizeof(ctx->filename),
3427 "rtp://%s:%d?multicast=1&ttl=%d",
3428 ipaddr, ntohs(dest_addr->sin_port), ttl);
3430 snprintf(ctx->filename, sizeof(ctx->filename),
3431 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3434 if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3436 c->rtp_handles[stream_index] = h;
3437 max_packet_size = h->max_packet_size;
3439 case RTSP_LOWER_TRANSPORT_TCP:
3442 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3448 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3449 ipaddr, ntohs(dest_addr->sin_port),
3450 c->stream->filename, stream_index, c->protocol);
3452 /* normally, no packets should be output here, but the packet size may be checked */
3453 if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3454 /* XXX: close stream */
3457 if (avformat_write_header(ctx, NULL) < 0) {
3464 avio_close_dyn_buf(ctx->pb, &dummy_buf);
3467 c->rtp_ctx[stream_index] = ctx;
3471 /********************************************************************/
3472 /* avserver initialization */
3474 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3478 fst = av_mallocz(sizeof(AVStream));
3482 fst->codec = avcodec_alloc_context3(NULL);
3483 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3484 if (codec->extradata_size) {
3485 fst->codec->extradata = av_malloc(codec->extradata_size);
3486 memcpy(fst->codec->extradata, codec->extradata,
3487 codec->extradata_size);
3490 /* live streams must use the actual feed's codec since it may be
3491 * updated later to carry extradata needed by the streams.
3495 fst->priv_data = av_mallocz(sizeof(FeedData));
3496 fst->index = stream->nb_streams;
3497 avpriv_set_pts_info(fst, 33, 1, 90000);
3498 fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3499 stream->streams[stream->nb_streams++] = fst;
3503 /* return the stream number in the feed */
3504 static int add_av_stream(FFStream *feed, AVStream *st)
3507 AVCodecContext *av, *av1;
3511 for(i=0;i<feed->nb_streams;i++) {
3512 st = feed->streams[i];
3514 if (av1->codec_id == av->codec_id &&
3515 av1->codec_type == av->codec_type &&
3516 av1->bit_rate == av->bit_rate) {
3518 switch(av->codec_type) {
3519 case AVMEDIA_TYPE_AUDIO:
3520 if (av1->channels == av->channels &&
3521 av1->sample_rate == av->sample_rate)
3524 case AVMEDIA_TYPE_VIDEO:
3525 if (av1->width == av->width &&
3526 av1->height == av->height &&
3527 av1->time_base.den == av->time_base.den &&
3528 av1->time_base.num == av->time_base.num &&
3529 av1->gop_size == av->gop_size)
3538 fst = add_av_stream1(feed, av, 0);
3541 return feed->nb_streams - 1;
3544 static void remove_stream(FFStream *stream)
3548 while (*ps != NULL) {
3556 /* specific mpeg4 handling : we extract the raw parameters */
3557 static void extract_mpeg4_header(AVFormatContext *infile)
3559 int mpeg4_count, i, size;
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_packet(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 /* simplistic plugin support */
3917 static void load_module(const char *filename)
3920 void (*init_func)(void);
3921 dll = dlopen(filename, RTLD_NOW);
3923 fprintf(stderr, "Could not load module '%s' - %s\n",
3924 filename, dlerror());
3928 init_func = dlsym(dll, "avserver_module_init");
3931 "%s: init function 'avserver_module_init()' not found\n",
3940 static int avserver_opt_default(const char *opt, const char *arg,
3941 AVCodecContext *avctx, int type)
3944 const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3946 ret = av_opt_set(avctx, opt, arg, 0);
3950 static int avserver_opt_preset(const char *arg,
3951 AVCodecContext *avctx, int type,
3952 enum AVCodecID *audio_id, enum AVCodecID *video_id)
3955 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3957 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3959 if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3960 codec ? codec->name : NULL))) {
3961 fprintf(stderr, "File for preset '%s' not found\n", arg);
3966 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3967 if(line[0] == '#' && !e)
3969 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3971 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3975 if(!strcmp(tmp, "acodec")){
3976 *audio_id = opt_audio_codec(tmp2);
3977 }else if(!strcmp(tmp, "vcodec")){
3978 *video_id = opt_video_codec(tmp2);
3979 }else if(!strcmp(tmp, "scodec")){
3980 /* opt_subtitle_codec(tmp2); */
3981 }else if(avserver_opt_default(tmp, tmp2, avctx, type) < 0){
3982 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
3993 static AVOutputFormat *avserver_guess_format(const char *short_name, const char *filename,
3994 const char *mime_type)
3996 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3999 AVOutputFormat *stream_fmt;
4000 char stream_format_name[64];
4002 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
4003 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4012 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
4016 fprintf(stderr, "%s:%d: ", filename, line_num);
4017 vfprintf(stderr, fmt, vl);
4023 static int parse_ffconfig(const char *filename)
4030 int val, errors, line_num;
4031 FFStream **last_stream, *stream, *redirect;
4032 FFStream **last_feed, *feed, *s;
4033 AVCodecContext audio_enc, video_enc;
4034 enum AVCodecID audio_id, video_id;
4036 f = fopen(filename, "r");
4044 first_stream = NULL;
4045 last_stream = &first_stream;
4047 last_feed = &first_feed;
4051 audio_id = AV_CODEC_ID_NONE;
4052 video_id = AV_CODEC_ID_NONE;
4054 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4056 if (fgets(line, sizeof(line), f) == NULL)
4062 if (*p == '\0' || *p == '#')
4065 get_arg(cmd, sizeof(cmd), &p);
4067 if (!av_strcasecmp(cmd, "Port")) {
4068 get_arg(arg, sizeof(arg), &p);
4070 if (val < 1 || val > 65536) {
4071 ERROR("Invalid_port: %s\n", arg);
4073 my_http_addr.sin_port = htons(val);
4074 } else if (!av_strcasecmp(cmd, "BindAddress")) {
4075 get_arg(arg, sizeof(arg), &p);
4076 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4077 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4079 } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4080 get_arg(arg, sizeof(arg), &p);
4082 if (val < 1 || val > 65536) {
4083 ERROR("%s:%d: Invalid port: %s\n", arg);
4085 my_rtsp_addr.sin_port = htons(atoi(arg));
4086 } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4087 get_arg(arg, sizeof(arg), &p);
4088 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4089 ERROR("Invalid host/IP address: %s\n", arg);
4091 } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4092 get_arg(arg, sizeof(arg), &p);
4094 if (val < 1 || val > 65536) {
4095 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4097 nb_max_http_connections = val;
4098 } else if (!av_strcasecmp(cmd, "MaxClients")) {
4099 get_arg(arg, sizeof(arg), &p);
4101 if (val < 1 || val > nb_max_http_connections) {
4102 ERROR("Invalid MaxClients: %s\n", arg);
4104 nb_max_connections = val;
4106 } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4108 get_arg(arg, sizeof(arg), &p);
4110 if (llval < 10 || llval > 10000000) {
4111 ERROR("Invalid MaxBandwidth: %s\n", arg);
4113 max_bandwidth = llval;
4114 } else if (!av_strcasecmp(cmd, "CustomLog")) {
4115 if (!avserver_debug)
4116 get_arg(logfilename, sizeof(logfilename), &p);
4117 } else if (!av_strcasecmp(cmd, "<Feed")) {
4118 /*********************************************/
4119 /* Feed related options */
4121 if (stream || feed) {
4122 ERROR("Already in a tag\n");
4124 feed = av_mallocz(sizeof(FFStream));
4125 get_arg(feed->filename, sizeof(feed->filename), &p);
4126 q = strrchr(feed->filename, '>');
4130 for (s = first_feed; s; s = s->next) {
4131 if (!strcmp(feed->filename, s->filename)) {
4132 ERROR("Feed '%s' already registered\n", s->filename);
4136 feed->fmt = av_guess_format("ffm", NULL, NULL);
4137 /* defaut feed file */
4138 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4139 "/tmp/%s.ffm", feed->filename);
4140 feed->feed_max_size = 5 * 1024 * 1024;
4142 feed->feed = feed; /* self feeding :-) */
4144 /* add in stream list */
4145 *last_stream = feed;
4146 last_stream = &feed->next;
4147 /* add in feed list */
4149 last_feed = &feed->next_feed;
4151 } else if (!av_strcasecmp(cmd, "Launch")) {
4155 feed->child_argv = av_mallocz(64 * sizeof(char *));
4157 for (i = 0; i < 62; i++) {
4158 get_arg(arg, sizeof(arg), &p);
4162 feed->child_argv[i] = av_strdup(arg);
4165 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4167 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4169 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4170 inet_ntoa(my_http_addr.sin_addr),
4171 ntohs(my_http_addr.sin_port), feed->filename);
4173 } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4175 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4177 } else if (stream) {
4178 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4180 } else if (!av_strcasecmp(cmd, "File")) {
4182 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4184 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4185 } else if (!av_strcasecmp(cmd, "Truncate")) {
4187 get_arg(arg, sizeof(arg), &p);
4188 feed->truncate = strtod(arg, NULL);
4190 } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4195 get_arg(arg, sizeof(arg), &p);
4197 fsize = strtod(p1, &p1);
4198 switch(toupper(*p1)) {
4203 fsize *= 1024 * 1024;
4206 fsize *= 1024 * 1024 * 1024;
4209 feed->feed_max_size = (int64_t)fsize;
4210 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4211 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4214 } else if (!av_strcasecmp(cmd, "</Feed>")) {
4216 ERROR("No corresponding <Feed> for </Feed>\n");
4219 } else if (!av_strcasecmp(cmd, "<Stream")) {
4220 /*********************************************/
4221 /* Stream related options */
4223 if (stream || feed) {
4224 ERROR("Already in a tag\n");
4227 stream = av_mallocz(sizeof(FFStream));
4228 get_arg(stream->filename, sizeof(stream->filename), &p);
4229 q = strrchr(stream->filename, '>');
4233 for (s = first_stream; s; s = s->next) {
4234 if (!strcmp(stream->filename, s->filename)) {
4235 ERROR("Stream '%s' already registered\n", s->filename);
4239 stream->fmt = avserver_guess_format(NULL, stream->filename, NULL);
4240 avcodec_get_context_defaults3(&video_enc, NULL);
4241 avcodec_get_context_defaults3(&audio_enc, NULL);
4242 audio_id = AV_CODEC_ID_NONE;
4243 video_id = AV_CODEC_ID_NONE;
4245 audio_id = stream->fmt->audio_codec;
4246 video_id = stream->fmt->video_codec;
4249 *last_stream = stream;
4250 last_stream = &stream->next;
4252 } else if (!av_strcasecmp(cmd, "Feed")) {
4253 get_arg(arg, sizeof(arg), &p);
4258 while (sfeed != NULL) {
4259 if (!strcmp(sfeed->filename, arg))
4261 sfeed = sfeed->next_feed;
4264 ERROR("feed '%s' not defined\n", arg);
4266 stream->feed = sfeed;
4268 } else if (!av_strcasecmp(cmd, "Format")) {
4269 get_arg(arg, sizeof(arg), &p);
4271 if (!strcmp(arg, "status")) {
4272 stream->stream_type = STREAM_TYPE_STATUS;
4275 stream->stream_type = STREAM_TYPE_LIVE;
4276 /* jpeg cannot be used here, so use single frame jpeg */
4277 if (!strcmp(arg, "jpeg"))
4278 strcpy(arg, "mjpeg");
4279 stream->fmt = avserver_guess_format(arg, NULL, NULL);
4281 ERROR("Unknown Format: %s\n", arg);
4285 audio_id = stream->fmt->audio_codec;
4286 video_id = stream->fmt->video_codec;
4289 } else if (!av_strcasecmp(cmd, "InputFormat")) {
4290 get_arg(arg, sizeof(arg), &p);
4292 stream->ifmt = av_find_input_format(arg);
4293 if (!stream->ifmt) {
4294 ERROR("Unknown input format: %s\n", arg);
4297 } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4298 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4299 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4301 ERROR("FaviconURL only permitted for status streams\n");
4303 } else if (!av_strcasecmp(cmd, "Author")) {
4305 get_arg(stream->author, sizeof(stream->author), &p);
4306 } else if (!av_strcasecmp(cmd, "Comment")) {
4308 get_arg(stream->comment, sizeof(stream->comment), &p);
4309 } else if (!av_strcasecmp(cmd, "Copyright")) {
4311 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4312 } else if (!av_strcasecmp(cmd, "Title")) {
4314 get_arg(stream->title, sizeof(stream->title), &p);
4315 } else if (!av_strcasecmp(cmd, "Preroll")) {
4316 get_arg(arg, sizeof(arg), &p);
4318 stream->prebuffer = atof(arg) * 1000;
4319 } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4321 stream->send_on_key = 1;
4322 } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4323 get_arg(arg, sizeof(arg), &p);
4324 audio_id = opt_audio_codec(arg);
4325 if (audio_id == AV_CODEC_ID_NONE) {
4326 ERROR("Unknown AudioCodec: %s\n", arg);
4328 } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4329 get_arg(arg, sizeof(arg), &p);
4330 video_id = opt_video_codec(arg);
4331 if (video_id == AV_CODEC_ID_NONE) {
4332 ERROR("Unknown VideoCodec: %s\n", arg);
4334 } else if (!av_strcasecmp(cmd, "MaxTime")) {
4335 get_arg(arg, sizeof(arg), &p);
4337 stream->max_time = atof(arg) * 1000;
4338 } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4339 get_arg(arg, sizeof(arg), &p);
4341 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4342 } else if (!av_strcasecmp(cmd, "AudioChannels")) {
4343 get_arg(arg, sizeof(arg), &p);
4345 audio_enc.channels = atoi(arg);
4346 } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4347 get_arg(arg, sizeof(arg), &p);
4349 audio_enc.sample_rate = atoi(arg);
4350 } else if (!av_strcasecmp(cmd, "AudioQuality")) {
4351 get_arg(arg, sizeof(arg), &p);
4353 // audio_enc.quality = atof(arg) * 1000;
4355 } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4357 int minrate, maxrate;
4359 get_arg(arg, sizeof(arg), &p);
4361 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4362 video_enc.rc_min_rate = minrate * 1000;
4363 video_enc.rc_max_rate = maxrate * 1000;
4365 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4368 } else if (!av_strcasecmp(cmd, "Debug")) {
4370 get_arg(arg, sizeof(arg), &p);
4371 video_enc.debug = strtol(arg,0,0);
4373 } else if (!av_strcasecmp(cmd, "Strict")) {
4375 get_arg(arg, sizeof(arg), &p);
4376 video_enc.strict_std_compliance = atoi(arg);
4378 } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4380 get_arg(arg, sizeof(arg), &p);
4381 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4383 } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4385 get_arg(arg, sizeof(arg), &p);
4386 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4388 } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4389 get_arg(arg, sizeof(arg), &p);
4391 video_enc.bit_rate = atoi(arg) * 1000;
4393 } else if (!av_strcasecmp(cmd, "VideoSize")) {
4394 get_arg(arg, sizeof(arg), &p);
4396 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4397 if ((video_enc.width % 16) != 0 ||
4398 (video_enc.height % 16) != 0) {
4399 ERROR("Image size must be a multiple of 16\n");
4402 } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4403 get_arg(arg, sizeof(arg), &p);
4405 AVRational frame_rate;
4406 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4407 ERROR("Incorrect frame rate: %s\n", arg);
4409 video_enc.time_base.num = frame_rate.den;
4410 video_enc.time_base.den = frame_rate.num;
4413 } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4414 get_arg(arg, sizeof(arg), &p);
4416 video_enc.gop_size = atoi(arg);
4417 } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4419 video_enc.gop_size = 1;
4420 } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4422 video_enc.mb_decision = FF_MB_DECISION_BITS;
4423 } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4425 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4426 video_enc.flags |= CODEC_FLAG_4MV;
4428 } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4429 !av_strcasecmp(cmd, "AVOptionAudio")) {
4431 AVCodecContext *avctx;
4433 get_arg(arg, sizeof(arg), &p);
4434 get_arg(arg2, sizeof(arg2), &p);
4435 if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4437 type = AV_OPT_FLAG_VIDEO_PARAM;
4440 type = AV_OPT_FLAG_AUDIO_PARAM;
4442 if (avserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4443 ERROR("AVOption error: %s %s\n", arg, arg2);
4445 } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4446 !av_strcasecmp(cmd, "AVPresetAudio")) {
4447 AVCodecContext *avctx;
4449 get_arg(arg, sizeof(arg), &p);
4450 if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4452 video_enc.codec_id = video_id;
4453 type = AV_OPT_FLAG_VIDEO_PARAM;
4456 audio_enc.codec_id = audio_id;
4457 type = AV_OPT_FLAG_AUDIO_PARAM;
4459 if (avserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4460 ERROR("AVPreset error: %s\n", arg);
4462 } else if (!av_strcasecmp(cmd, "VideoTag")) {
4463 get_arg(arg, sizeof(arg), &p);
4464 if ((strlen(arg) == 4) && stream)
4465 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4466 } else if (!av_strcasecmp(cmd, "BitExact")) {
4468 video_enc.flags |= CODEC_FLAG_BITEXACT;
4469 } else if (!av_strcasecmp(cmd, "DctFastint")) {
4471 video_enc.dct_algo = FF_DCT_FASTINT;
4472 } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4474 video_enc.idct_algo = FF_IDCT_SIMPLE;
4475 } else if (!av_strcasecmp(cmd, "Qscale")) {
4476 get_arg(arg, sizeof(arg), &p);
4478 video_enc.flags |= CODEC_FLAG_QSCALE;
4479 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4481 } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4482 get_arg(arg, sizeof(arg), &p);
4484 video_enc.max_qdiff = atoi(arg);
4485 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4486 ERROR("VideoQDiff out of range\n");
4489 } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4490 get_arg(arg, sizeof(arg), &p);
4492 video_enc.qmax = atoi(arg);
4493 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4494 ERROR("VideoQMax out of range\n");
4497 } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4498 get_arg(arg, sizeof(arg), &p);
4500 video_enc.qmin = atoi(arg);
4501 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4502 ERROR("VideoQMin out of range\n");
4505 } else if (!av_strcasecmp(cmd, "LumaElim")) {
4506 get_arg(arg, sizeof(arg), &p);
4508 video_enc.luma_elim_threshold = atoi(arg);
4509 } else if (!av_strcasecmp(cmd, "ChromaElim")) {
4510 get_arg(arg, sizeof(arg), &p);
4512 video_enc.chroma_elim_threshold = atoi(arg);
4513 } else if (!av_strcasecmp(cmd, "LumiMask")) {
4514 get_arg(arg, sizeof(arg), &p);
4516 video_enc.lumi_masking = atof(arg);
4517 } else if (!av_strcasecmp(cmd, "DarkMask")) {
4518 get_arg(arg, sizeof(arg), &p);
4520 video_enc.dark_masking = atof(arg);
4521 } else if (!av_strcasecmp(cmd, "NoVideo")) {
4522 video_id = AV_CODEC_ID_NONE;
4523 } else if (!av_strcasecmp(cmd, "NoAudio")) {
4524 audio_id = AV_CODEC_ID_NONE;
4525 } else if (!av_strcasecmp(cmd, "ACL")) {
4526 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4527 } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4529 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4531 } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4532 get_arg(arg, sizeof(arg), &p);
4534 av_freep(&stream->rtsp_option);
4535 stream->rtsp_option = av_strdup(arg);
4537 } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4538 get_arg(arg, sizeof(arg), &p);
4540 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4541 ERROR("Invalid host/IP address: %s\n", arg);
4543 stream->is_multicast = 1;
4544 stream->loop = 1; /* default is looping */
4546 } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4547 get_arg(arg, sizeof(arg), &p);
4549 stream->multicast_port = atoi(arg);
4550 } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4551 get_arg(arg, sizeof(arg), &p);
4553 stream->multicast_ttl = atoi(arg);
4554 } else if (!av_strcasecmp(cmd, "NoLoop")) {
4557 } else if (!av_strcasecmp(cmd, "</Stream>")) {
4559 ERROR("No corresponding <Stream> for </Stream>\n");
4561 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4562 if (audio_id != AV_CODEC_ID_NONE) {
4563 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4564 audio_enc.codec_id = audio_id;
4565 add_codec(stream, &audio_enc);
4567 if (video_id != AV_CODEC_ID_NONE) {
4568 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4569 video_enc.codec_id = video_id;
4570 add_codec(stream, &video_enc);
4575 } else if (!av_strcasecmp(cmd, "<Redirect")) {
4576 /*********************************************/
4578 if (stream || feed || redirect) {
4579 ERROR("Already in a tag\n");
4581 redirect = av_mallocz(sizeof(FFStream));
4582 *last_stream = redirect;
4583 last_stream = &redirect->next;
4585 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4586 q = strrchr(redirect->filename, '>');
4589 redirect->stream_type = STREAM_TYPE_REDIRECT;
4591 } else if (!av_strcasecmp(cmd, "URL")) {
4593 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4594 } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4596 ERROR("No corresponding <Redirect> for </Redirect>\n");
4598 if (!redirect->feed_filename[0]) {
4599 ERROR("No URL found for <Redirect>\n");
4603 } else if (!av_strcasecmp(cmd, "LoadModule")) {
4604 get_arg(arg, sizeof(arg), &p);
4608 ERROR("Module support not compiled into this version: '%s'\n", arg);
4611 ERROR("Incorrect keyword: '%s'\n", cmd);
4623 static void handle_child_exit(int sig)
4628 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4631 for (feed = first_feed; feed; feed = feed->next) {
4632 if (feed->pid == pid) {
4633 int uptime = time(0) - feed->pid_start;
4636 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4639 /* Turn off any more restarts */
4640 feed->child_argv = 0;
4645 need_to_start_children = 1;
4648 static void opt_debug(void)
4651 logfilename[0] = '-';
4654 void show_help_default(const char *opt, const char *arg)
4656 printf("usage: avserver [options]\n"
4657 "Hyper fast multi format Audio/Video streaming server\n");
4659 show_help_options(options, "Main options:", 0, 0, 0);
4662 static const OptionDef options[] = {
4663 #include "cmdutils_common_opts.h"
4664 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4665 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4666 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/avserver.conf", "configfile" },
4670 int main(int argc, char **argv)
4672 struct sigaction sigact = { { 0 } };
4674 parse_loglevel(argc, argv, options);
4676 avformat_network_init();
4680 my_program_name = argv[0];
4682 parse_options(NULL, argc, argv, options, NULL);
4684 unsetenv("http_proxy"); /* Kill the http_proxy */
4686 av_lfg_init(&random_state, av_get_random_seed());
4688 sigact.sa_handler = handle_child_exit;
4689 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4690 sigaction(SIGCHLD, &sigact, 0);
4692 if (parse_ffconfig(config_filename) < 0) {
4693 fprintf(stderr, "Incorrect config file - exiting.\n");
4697 /* open log file if needed */
4698 if (logfilename[0] != '\0') {
4699 if (!strcmp(logfilename, "-"))
4702 logfile = fopen(logfilename, "a");
4703 av_log_set_callback(http_av_log);
4706 build_file_streams();
4708 build_feed_streams();
4710 compute_bandwidth();
4713 signal(SIGPIPE, SIG_IGN);
4715 if (http_server() < 0) {
4716 http_log("Could not start server\n");