2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg 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 * FFmpeg 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 FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #define _XOPEN_SOURCE 600
26 #define closesocket close
31 /* avformat.h defines LIBAVFORMAT_BUILD, include it before all the other libav* headers which use it */
32 #include "libavformat/avformat.h"
33 #include "libavformat/network.h"
34 #include "libavformat/os_support.h"
35 #include "libavformat/rtp.h"
36 #include "libavformat/rtsp.h"
37 #include "libavutil/avstring.h"
38 #include "libavutil/random.h"
39 #include "libavutil/intreadwrite.h"
40 #include "libavcodec/opt.h"
44 #include <sys/ioctl.h>
50 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
62 const char program_name[] = "FFserver";
63 const int program_birth_year = 2000;
65 static const OptionDef options[];
68 HTTPSTATE_WAIT_REQUEST,
69 HTTPSTATE_SEND_HEADER,
70 HTTPSTATE_SEND_DATA_HEADER,
71 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
72 HTTPSTATE_SEND_DATA_TRAILER,
73 HTTPSTATE_RECEIVE_DATA,
74 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
77 RTSPSTATE_WAIT_REQUEST,
79 RTSPSTATE_SEND_PACKET,
82 static const char *http_state[] = {
98 #define IOBUFFER_INIT_SIZE 8192
100 /* timeouts are in ms */
101 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
102 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
104 #define SYNC_TIMEOUT (10 * 1000)
107 int64_t count1, count2;
108 int64_t time1, time2;
111 /* context associated with one connection */
112 typedef struct HTTPContext {
113 enum HTTPState state;
114 int fd; /* socket file descriptor */
115 struct sockaddr_in from_addr; /* origin */
116 struct pollfd *poll_entry; /* used when polling */
118 uint8_t *buffer_ptr, *buffer_end;
121 struct HTTPContext *next;
122 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
126 /* input format handling */
127 AVFormatContext *fmt_in;
128 int64_t start_time; /* In milliseconds - this wraps fairly often */
129 int64_t first_pts; /* initial pts value */
130 int64_t cur_pts; /* current pts value from the stream in us */
131 int64_t cur_frame_duration; /* duration of the current frame in us */
132 int cur_frame_bytes; /* output frame size, needed to compute
133 the time at which we send each
135 int pts_stream_index; /* stream we choose as clock reference */
136 int64_t cur_clock; /* current clock reference value in us */
137 /* output format handling */
138 struct FFStream *stream;
139 /* -1 is invalid stream */
140 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
141 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
143 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
144 int last_packet_sent; /* true if last data packet was sent */
146 DataRateData datarate;
153 int is_packetized; /* if true, the stream is packetized */
154 int packet_stream_index; /* current stream for output in state machine */
156 /* RTSP state specific */
157 uint8_t *pb_buffer; /* XXX: use that in all the code */
159 int seq; /* RTSP sequence number */
161 /* RTP state specific */
162 enum RTSPLowerTransport rtp_protocol;
163 char session_id[32]; /* session id */
164 AVFormatContext *rtp_ctx[MAX_STREAMS];
166 /* RTP/UDP specific */
167 URLContext *rtp_handles[MAX_STREAMS];
169 /* RTP/TCP specific */
170 struct HTTPContext *rtsp_c;
171 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
174 /* each generated stream is described here */
178 STREAM_TYPE_REDIRECT,
181 enum IPAddressAction {
186 typedef struct IPAddressACL {
187 struct IPAddressACL *next;
188 enum IPAddressAction action;
189 /* These are in host order */
190 struct in_addr first;
194 /* description of each stream of the ffserver.conf file */
195 typedef struct FFStream {
196 enum StreamType stream_type;
197 char filename[1024]; /* stream filename */
198 struct FFStream *feed; /* feed we are using (can be null if
200 AVFormatParameters *ap_in; /* input parameters */
201 AVInputFormat *ifmt; /* if non NULL, force input format */
205 int prebuffer; /* Number of millseconds early to start */
206 int64_t max_time; /* Number of milliseconds to run */
208 AVStream *streams[MAX_STREAMS];
209 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
210 char feed_filename[1024]; /* file name of the feed storage, or
211 input file name for a stream */
216 pid_t pid; /* Of ffmpeg process */
217 time_t pid_start; /* Of ffmpeg process */
219 struct FFStream *next;
220 unsigned bandwidth; /* bandwidth, in kbits/s */
223 /* multicast specific */
225 struct in_addr multicast_ip;
226 int multicast_port; /* first port used for multicast */
228 int loop; /* if true, send the stream in loops (only meaningful if file) */
231 int feed_opened; /* true if someone is writing to the feed */
232 int is_feed; /* true if it is a feed */
233 int readonly; /* True if writing is prohibited to the file */
235 int64_t bytes_served;
236 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
237 int64_t feed_write_index; /* current write position in feed (it wraps around) */
238 int64_t feed_size; /* current size of feed */
239 struct FFStream *next_feed;
242 typedef struct FeedData {
243 long long data_count;
244 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
247 static struct sockaddr_in my_http_addr;
248 static struct sockaddr_in my_rtsp_addr;
250 static char logfilename[1024];
251 static HTTPContext *first_http_ctx;
252 static FFStream *first_feed; /* contains only feeds */
253 static FFStream *first_stream; /* contains all streams, including feeds */
255 static void new_connection(int server_fd, int is_rtsp);
256 static void close_connection(HTTPContext *c);
259 static int handle_connection(HTTPContext *c);
260 static int http_parse_request(HTTPContext *c);
261 static int http_send_data(HTTPContext *c);
262 static void compute_status(HTTPContext *c);
263 static int open_input_stream(HTTPContext *c, const char *info);
264 static int http_start_receive_data(HTTPContext *c);
265 static int http_receive_data(HTTPContext *c);
268 static int rtsp_parse_request(HTTPContext *c);
269 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
270 static void rtsp_cmd_options(HTTPContext *c, const char *url);
271 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
273 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
274 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
277 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
278 struct in_addr my_ip);
281 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
282 FFStream *stream, const char *session_id,
283 enum RTSPLowerTransport rtp_protocol);
284 static int rtp_new_av_stream(HTTPContext *c,
285 int stream_index, struct sockaddr_in *dest_addr,
286 HTTPContext *rtsp_c);
288 static const char *my_program_name;
289 static const char *my_program_dir;
291 static const char *config_filename;
292 static int ffserver_debug;
293 static int ffserver_daemon;
294 static int no_launch;
295 static int need_to_start_children;
297 /* maximum number of simultaneous HTTP connections */
298 static unsigned int nb_max_http_connections = 2000;
299 static unsigned int nb_max_connections = 5;
300 static unsigned int nb_connections;
302 static uint64_t max_bandwidth = 1000;
303 static uint64_t current_bandwidth;
305 static int64_t cur_time; // Making this global saves on passing it around everywhere
307 static AVRandomState random_state;
309 static FILE *logfile = NULL;
311 static char *ctime1(char *buf2)
319 p = buf2 + strlen(p) - 1;
325 static void http_vlog(const char *fmt, va_list vargs)
327 static int print_prefix = 1;
332 fprintf(logfile, "%s ", buf);
334 print_prefix = strstr(fmt, "\n") != NULL;
335 vfprintf(logfile, fmt, vargs);
340 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
343 va_start(vargs, fmt);
344 http_vlog(fmt, vargs);
348 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
350 static int print_prefix = 1;
351 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
352 if (level > av_log_level)
354 if (print_prefix && avc)
355 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
356 print_prefix = strstr(fmt, "\n") != NULL;
357 http_vlog(fmt, vargs);
360 static void log_connection(HTTPContext *c)
365 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
366 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
367 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
370 static void update_datarate(DataRateData *drd, int64_t count)
372 if (!drd->time1 && !drd->count1) {
373 drd->time1 = drd->time2 = cur_time;
374 drd->count1 = drd->count2 = count;
375 } else if (cur_time - drd->time2 > 5000) {
376 drd->time1 = drd->time2;
377 drd->count1 = drd->count2;
378 drd->time2 = cur_time;
383 /* In bytes per second */
384 static int compute_datarate(DataRateData *drd, int64_t count)
386 if (cur_time == drd->time1)
389 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
393 static void start_children(FFStream *feed)
398 for (; feed; feed = feed->next) {
399 if (feed->child_argv && !feed->pid) {
400 feed->pid_start = time(0);
405 http_log("Unable to create children\n");
414 av_strlcpy(pathname, my_program_name, sizeof(pathname));
416 slash = strrchr(pathname, '/');
421 strcpy(slash, "ffmpeg");
423 http_log("Launch commandline: ");
424 http_log("%s ", pathname);
425 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
426 http_log("%s ", feed->child_argv[i]);
429 for (i = 3; i < 256; i++)
432 if (!ffserver_debug) {
433 i = open("/dev/null", O_RDWR);
442 /* This is needed to make relative pathnames work */
443 chdir(my_program_dir);
445 signal(SIGPIPE, SIG_DFL);
447 execvp(pathname, feed->child_argv);
455 /* open a listening socket */
456 static int socket_open_listen(struct sockaddr_in *my_addr)
460 server_fd = socket(AF_INET,SOCK_STREAM,0);
467 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
469 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
471 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
473 closesocket(server_fd);
477 if (listen (server_fd, 5) < 0) {
479 closesocket(server_fd);
482 ff_socket_nonblock(server_fd, 1);
487 /* start all multicast streams */
488 static void start_multicast(void)
493 struct sockaddr_in dest_addr;
494 int default_port, stream_index;
497 for(stream = first_stream; stream != NULL; stream = stream->next) {
498 if (stream->is_multicast) {
499 /* open the RTP connection */
500 snprintf(session_id, sizeof(session_id), "%08x%08x",
501 av_random(&random_state), av_random(&random_state));
503 /* choose a port if none given */
504 if (stream->multicast_port == 0) {
505 stream->multicast_port = default_port;
509 dest_addr.sin_family = AF_INET;
510 dest_addr.sin_addr = stream->multicast_ip;
511 dest_addr.sin_port = htons(stream->multicast_port);
513 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
514 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
518 if (open_input_stream(rtp_c, "") < 0) {
519 http_log("Could not open input stream for stream '%s'\n",
524 /* open each RTP stream */
525 for(stream_index = 0; stream_index < stream->nb_streams;
527 dest_addr.sin_port = htons(stream->multicast_port +
529 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
530 http_log("Could not open output stream '%s/streamid=%d'\n",
531 stream->filename, stream_index);
536 /* change state to send data */
537 rtp_c->state = HTTPSTATE_SEND_DATA;
542 /* main loop of the http server */
543 static int http_server(void)
545 int server_fd = 0, rtsp_server_fd = 0;
546 int ret, delay, delay1;
547 struct pollfd *poll_table, *poll_entry;
548 HTTPContext *c, *c_next;
550 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
551 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
555 if (my_http_addr.sin_port) {
556 server_fd = socket_open_listen(&my_http_addr);
561 if (my_rtsp_addr.sin_port) {
562 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
563 if (rtsp_server_fd < 0)
567 if (!rtsp_server_fd && !server_fd) {
568 http_log("HTTP and RTSP disabled.\n");
572 http_log("FFserver started.\n");
574 start_children(first_feed);
579 poll_entry = poll_table;
581 poll_entry->fd = server_fd;
582 poll_entry->events = POLLIN;
585 if (rtsp_server_fd) {
586 poll_entry->fd = rtsp_server_fd;
587 poll_entry->events = POLLIN;
591 /* wait for events on each HTTP handle */
598 case HTTPSTATE_SEND_HEADER:
599 case RTSPSTATE_SEND_REPLY:
600 case RTSPSTATE_SEND_PACKET:
601 c->poll_entry = poll_entry;
603 poll_entry->events = POLLOUT;
606 case HTTPSTATE_SEND_DATA_HEADER:
607 case HTTPSTATE_SEND_DATA:
608 case HTTPSTATE_SEND_DATA_TRAILER:
609 if (!c->is_packetized) {
610 /* for TCP, we output as much as we can (may need to put a limit) */
611 c->poll_entry = poll_entry;
613 poll_entry->events = POLLOUT;
616 /* when ffserver is doing the timing, we work by
617 looking at which packet need to be sent every
619 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
624 case HTTPSTATE_WAIT_REQUEST:
625 case HTTPSTATE_RECEIVE_DATA:
626 case HTTPSTATE_WAIT_FEED:
627 case RTSPSTATE_WAIT_REQUEST:
628 /* need to catch errors */
629 c->poll_entry = poll_entry;
631 poll_entry->events = POLLIN;/* Maybe this will work */
635 c->poll_entry = NULL;
641 /* wait for an event on one connection. We poll at least every
642 second to handle timeouts */
644 ret = poll(poll_table, poll_entry - poll_table, delay);
645 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
646 ff_neterrno() != FF_NETERROR(EINTR))
650 cur_time = av_gettime() / 1000;
652 if (need_to_start_children) {
653 need_to_start_children = 0;
654 start_children(first_feed);
657 /* now handle the events */
658 for(c = first_http_ctx; c != NULL; c = c_next) {
660 if (handle_connection(c) < 0) {
661 /* close and free the connection */
667 poll_entry = poll_table;
669 /* new HTTP connection request ? */
670 if (poll_entry->revents & POLLIN)
671 new_connection(server_fd, 0);
674 if (rtsp_server_fd) {
675 /* new RTSP connection request ? */
676 if (poll_entry->revents & POLLIN)
677 new_connection(rtsp_server_fd, 1);
682 /* start waiting for a new HTTP/RTSP request */
683 static void start_wait_request(HTTPContext *c, int is_rtsp)
685 c->buffer_ptr = c->buffer;
686 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
689 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
690 c->state = RTSPSTATE_WAIT_REQUEST;
692 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
693 c->state = HTTPSTATE_WAIT_REQUEST;
697 static void new_connection(int server_fd, int is_rtsp)
699 struct sockaddr_in from_addr;
701 HTTPContext *c = NULL;
703 len = sizeof(from_addr);
704 fd = accept(server_fd, (struct sockaddr *)&from_addr,
707 http_log("error during accept %s\n", strerror(errno));
710 ff_socket_nonblock(fd, 1);
712 /* XXX: should output a warning page when coming
713 close to the connection limit */
714 if (nb_connections >= nb_max_connections)
717 /* add a new connection */
718 c = av_mallocz(sizeof(HTTPContext));
723 c->poll_entry = NULL;
724 c->from_addr = from_addr;
725 c->buffer_size = IOBUFFER_INIT_SIZE;
726 c->buffer = av_malloc(c->buffer_size);
730 c->next = first_http_ctx;
734 start_wait_request(c, is_rtsp);
746 static void close_connection(HTTPContext *c)
748 HTTPContext **cp, *c1;
750 AVFormatContext *ctx;
754 /* remove connection from list */
755 cp = &first_http_ctx;
756 while ((*cp) != NULL) {
764 /* remove references, if any (XXX: do it faster) */
765 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
770 /* remove connection associated resources */
774 /* close each frame parser */
775 for(i=0;i<c->fmt_in->nb_streams;i++) {
776 st = c->fmt_in->streams[i];
777 if (st->codec->codec)
778 avcodec_close(st->codec);
780 av_close_input_file(c->fmt_in);
783 /* free RTP output streams if any */
786 nb_streams = c->stream->nb_streams;
788 for(i=0;i<nb_streams;i++) {
791 av_write_trailer(ctx);
794 h = c->rtp_handles[i];
801 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
804 if (url_open_dyn_buf(&ctx->pb) >= 0) {
805 av_write_trailer(ctx);
806 av_freep(&c->pb_buffer);
807 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
812 for(i=0; i<ctx->nb_streams; i++)
813 av_free(ctx->streams[i]);
815 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
816 current_bandwidth -= c->stream->bandwidth;
818 /* signal that there is no feed if we are the feeder socket */
819 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
820 c->stream->feed_opened = 0;
824 av_freep(&c->pb_buffer);
825 av_freep(&c->packet_buffer);
831 static int handle_connection(HTTPContext *c)
836 case HTTPSTATE_WAIT_REQUEST:
837 case RTSPSTATE_WAIT_REQUEST:
839 if ((c->timeout - cur_time) < 0)
841 if (c->poll_entry->revents & (POLLERR | POLLHUP))
844 /* no need to read if no events */
845 if (!(c->poll_entry->revents & POLLIN))
849 len = recv(c->fd, c->buffer_ptr, 1, 0);
851 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
852 ff_neterrno() != FF_NETERROR(EINTR))
854 } else if (len == 0) {
857 /* search for end of request. */
859 c->buffer_ptr += len;
861 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
862 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
863 /* request found : parse it and reply */
864 if (c->state == HTTPSTATE_WAIT_REQUEST) {
865 ret = http_parse_request(c);
867 ret = rtsp_parse_request(c);
871 } else if (ptr >= c->buffer_end) {
872 /* request too long: cannot do anything */
874 } else goto read_loop;
878 case HTTPSTATE_SEND_HEADER:
879 if (c->poll_entry->revents & (POLLERR | POLLHUP))
882 /* no need to write if no events */
883 if (!(c->poll_entry->revents & POLLOUT))
885 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
887 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
888 ff_neterrno() != FF_NETERROR(EINTR)) {
889 /* error : close connection */
890 av_freep(&c->pb_buffer);
894 c->buffer_ptr += len;
896 c->stream->bytes_served += len;
897 c->data_count += len;
898 if (c->buffer_ptr >= c->buffer_end) {
899 av_freep(&c->pb_buffer);
903 /* all the buffer was sent : synchronize to the incoming stream */
904 c->state = HTTPSTATE_SEND_DATA_HEADER;
905 c->buffer_ptr = c->buffer_end = c->buffer;
910 case HTTPSTATE_SEND_DATA:
911 case HTTPSTATE_SEND_DATA_HEADER:
912 case HTTPSTATE_SEND_DATA_TRAILER:
913 /* for packetized output, we consider we can always write (the
914 input streams sets the speed). It may be better to verify
915 that we do not rely too much on the kernel queues */
916 if (!c->is_packetized) {
917 if (c->poll_entry->revents & (POLLERR | POLLHUP))
920 /* no need to read if no events */
921 if (!(c->poll_entry->revents & POLLOUT))
924 if (http_send_data(c) < 0)
926 /* close connection if trailer sent */
927 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
930 case HTTPSTATE_RECEIVE_DATA:
931 /* no need to read if no events */
932 if (c->poll_entry->revents & (POLLERR | POLLHUP))
934 if (!(c->poll_entry->revents & POLLIN))
936 if (http_receive_data(c) < 0)
939 case HTTPSTATE_WAIT_FEED:
940 /* no need to read if no events */
941 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
944 /* nothing to do, we'll be waken up by incoming feed packets */
947 case RTSPSTATE_SEND_REPLY:
948 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
949 av_freep(&c->pb_buffer);
952 /* no need to write if no events */
953 if (!(c->poll_entry->revents & POLLOUT))
955 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
957 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
958 ff_neterrno() != FF_NETERROR(EINTR)) {
959 /* error : close connection */
960 av_freep(&c->pb_buffer);
964 c->buffer_ptr += len;
965 c->data_count += len;
966 if (c->buffer_ptr >= c->buffer_end) {
967 /* all the buffer was sent : wait for a new request */
968 av_freep(&c->pb_buffer);
969 start_wait_request(c, 1);
973 case RTSPSTATE_SEND_PACKET:
974 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
975 av_freep(&c->packet_buffer);
978 /* no need to write if no events */
979 if (!(c->poll_entry->revents & POLLOUT))
981 len = send(c->fd, c->packet_buffer_ptr,
982 c->packet_buffer_end - c->packet_buffer_ptr, 0);
984 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
985 ff_neterrno() != FF_NETERROR(EINTR)) {
986 /* error : close connection */
987 av_freep(&c->packet_buffer);
991 c->packet_buffer_ptr += len;
992 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
993 /* all the buffer was sent : wait for a new request */
994 av_freep(&c->packet_buffer);
995 c->state = RTSPSTATE_WAIT_REQUEST;
999 case HTTPSTATE_READY:
1008 static int extract_rates(char *rates, int ratelen, const char *request)
1012 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1013 if (strncasecmp(p, "Pragma:", 7) == 0) {
1014 const char *q = p + 7;
1016 while (*q && *q != '\n' && isspace(*q))
1019 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1025 memset(rates, 0xff, ratelen);
1028 while (*q && *q != '\n' && *q != ':')
1031 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1035 if (stream_no < ratelen && stream_no >= 0)
1036 rates[stream_no] = rate_no;
1038 while (*q && *q != '\n' && !isspace(*q))
1045 p = strchr(p, '\n');
1055 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1058 int best_bitrate = 100000000;
1061 for (i = 0; i < feed->nb_streams; i++) {
1062 AVCodecContext *feed_codec = feed->streams[i]->codec;
1064 if (feed_codec->codec_id != codec->codec_id ||
1065 feed_codec->sample_rate != codec->sample_rate ||
1066 feed_codec->width != codec->width ||
1067 feed_codec->height != codec->height)
1070 /* Potential stream */
1072 /* We want the fastest stream less than bit_rate, or the slowest
1073 * faster than bit_rate
1076 if (feed_codec->bit_rate <= bit_rate) {
1077 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1078 best_bitrate = feed_codec->bit_rate;
1082 if (feed_codec->bit_rate < best_bitrate) {
1083 best_bitrate = feed_codec->bit_rate;
1092 static int modify_current_stream(HTTPContext *c, char *rates)
1095 FFStream *req = c->stream;
1096 int action_required = 0;
1098 /* Not much we can do for a feed */
1102 for (i = 0; i < req->nb_streams; i++) {
1103 AVCodecContext *codec = req->streams[i]->codec;
1107 c->switch_feed_streams[i] = req->feed_streams[i];
1110 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1113 /* Wants off or slow */
1114 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1116 /* This doesn't work well when it turns off the only stream! */
1117 c->switch_feed_streams[i] = -2;
1118 c->feed_streams[i] = -2;
1123 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1124 action_required = 1;
1127 return action_required;
1131 static void do_switch_stream(HTTPContext *c, int i)
1133 if (c->switch_feed_streams[i] >= 0) {
1135 c->feed_streams[i] = c->switch_feed_streams[i];
1138 /* Now update the stream */
1140 c->switch_feed_streams[i] = -1;
1143 /* XXX: factorize in utils.c ? */
1144 /* XXX: take care with different space meaning */
1145 static void skip_spaces(const char **pp)
1149 while (*p == ' ' || *p == '\t')
1154 static void get_word(char *buf, int buf_size, const char **pp)
1162 while (!isspace(*p) && *p != '\0') {
1163 if ((q - buf) < buf_size - 1)
1172 static int validate_acl(FFStream *stream, HTTPContext *c)
1174 enum IPAddressAction last_action = IP_DENY;
1176 struct in_addr *src = &c->from_addr.sin_addr;
1177 unsigned long src_addr = src->s_addr;
1179 for (acl = stream->acl; acl; acl = acl->next) {
1180 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1181 return (acl->action == IP_ALLOW) ? 1 : 0;
1182 last_action = acl->action;
1185 /* Nothing matched, so return not the last action */
1186 return (last_action == IP_DENY) ? 1 : 0;
1189 /* compute the real filename of a file by matching it without its
1190 extensions to all the stream filenames */
1191 static void compute_real_filename(char *filename, int max_size)
1198 /* compute filename by matching without the file extensions */
1199 av_strlcpy(file1, filename, sizeof(file1));
1200 p = strrchr(file1, '.');
1203 for(stream = first_stream; stream != NULL; stream = stream->next) {
1204 av_strlcpy(file2, stream->filename, sizeof(file2));
1205 p = strrchr(file2, '.');
1208 if (!strcmp(file1, file2)) {
1209 av_strlcpy(filename, stream->filename, max_size);
1224 /* parse http request and prepare header */
1225 static int http_parse_request(HTTPContext *c)
1228 enum RedirType redir_type;
1230 char info[1024], filename[1024];
1234 const char *mime_type;
1238 char *useragent = 0;
1241 get_word(cmd, sizeof(cmd), (const char **)&p);
1242 av_strlcpy(c->method, cmd, sizeof(c->method));
1244 if (!strcmp(cmd, "GET"))
1246 else if (!strcmp(cmd, "POST"))
1251 get_word(url, sizeof(url), (const char **)&p);
1252 av_strlcpy(c->url, url, sizeof(c->url));
1254 get_word(protocol, sizeof(protocol), (const char **)&p);
1255 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1258 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1261 http_log("New connection: %s %s\n", cmd, url);
1263 /* find the filename and the optional info string in the request */
1264 p = strchr(url, '?');
1266 av_strlcpy(info, p, sizeof(info));
1271 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1273 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1274 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1276 if (*useragent && *useragent != '\n' && isspace(*useragent))
1280 p = strchr(p, '\n');
1287 redir_type = REDIR_NONE;
1288 if (match_ext(filename, "asx")) {
1289 redir_type = REDIR_ASX;
1290 filename[strlen(filename)-1] = 'f';
1291 } else if (match_ext(filename, "asf") &&
1292 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1293 /* if this isn't WMP or lookalike, return the redirector file */
1294 redir_type = REDIR_ASF;
1295 } else if (match_ext(filename, "rpm,ram")) {
1296 redir_type = REDIR_RAM;
1297 strcpy(filename + strlen(filename)-2, "m");
1298 } else if (match_ext(filename, "rtsp")) {
1299 redir_type = REDIR_RTSP;
1300 compute_real_filename(filename, sizeof(filename) - 1);
1301 } else if (match_ext(filename, "sdp")) {
1302 redir_type = REDIR_SDP;
1303 compute_real_filename(filename, sizeof(filename) - 1);
1306 // "redirect" / request to index.html
1307 if (!strlen(filename))
1308 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1310 stream = first_stream;
1311 while (stream != NULL) {
1312 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1314 stream = stream->next;
1316 if (stream == NULL) {
1317 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1322 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1323 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1325 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1326 c->http_error = 301;
1328 q += snprintf(q, c->buffer_size,
1329 "HTTP/1.0 301 Moved\r\n"
1331 "Content-type: text/html\r\n"
1333 "<html><head><title>Moved</title></head><body>\r\n"
1334 "You should be <a href=\"%s\">redirected</a>.\r\n"
1335 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1336 /* prepare output buffer */
1337 c->buffer_ptr = c->buffer;
1339 c->state = HTTPSTATE_SEND_HEADER;
1343 /* If this is WMP, get the rate information */
1344 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1345 if (modify_current_stream(c, ratebuf)) {
1346 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1347 if (c->switch_feed_streams[i] >= 0)
1348 do_switch_stream(c, i);
1353 /* If already streaming this feed, do not let start another feeder. */
1354 if (stream->feed_opened) {
1355 snprintf(msg, sizeof(msg), "This feed is already being received.");
1356 http_log("feed %s already being received\n", stream->feed_filename);
1360 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1361 current_bandwidth += stream->bandwidth;
1363 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1364 c->http_error = 200;
1366 q += snprintf(q, c->buffer_size,
1367 "HTTP/1.0 200 Server too busy\r\n"
1368 "Content-type: text/html\r\n"
1370 "<html><head><title>Too busy</title></head><body>\r\n"
1371 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1372 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1373 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1374 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1375 /* prepare output buffer */
1376 c->buffer_ptr = c->buffer;
1378 c->state = HTTPSTATE_SEND_HEADER;
1382 if (redir_type != REDIR_NONE) {
1385 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1386 if (strncasecmp(p, "Host:", 5) == 0) {
1390 p = strchr(p, '\n');
1401 while (isspace(*hostinfo))
1404 eoh = strchr(hostinfo, '\n');
1406 if (eoh[-1] == '\r')
1409 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1410 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1411 hostbuf[eoh - hostinfo] = 0;
1413 c->http_error = 200;
1415 switch(redir_type) {
1417 q += snprintf(q, c->buffer_size,
1418 "HTTP/1.0 200 ASX Follows\r\n"
1419 "Content-type: video/x-ms-asf\r\n"
1421 "<ASX Version=\"3\">\r\n"
1422 //"<!-- Autogenerated by ffserver -->\r\n"
1423 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1424 "</ASX>\r\n", hostbuf, filename, info);
1427 q += snprintf(q, c->buffer_size,
1428 "HTTP/1.0 200 RAM Follows\r\n"
1429 "Content-type: audio/x-pn-realaudio\r\n"
1431 "# Autogenerated by ffserver\r\n"
1432 "http://%s/%s%s\r\n", hostbuf, filename, info);
1435 q += snprintf(q, c->buffer_size,
1436 "HTTP/1.0 200 ASF Redirect follows\r\n"
1437 "Content-type: video/x-ms-asf\r\n"
1440 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1444 char hostname[256], *p;
1445 /* extract only hostname */
1446 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1447 p = strrchr(hostname, ':');
1450 q += snprintf(q, c->buffer_size,
1451 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1452 /* XXX: incorrect mime type ? */
1453 "Content-type: application/x-rtsp\r\n"
1455 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1461 int sdp_data_size, len;
1462 struct sockaddr_in my_addr;
1464 q += snprintf(q, c->buffer_size,
1465 "HTTP/1.0 200 OK\r\n"
1466 "Content-type: application/sdp\r\n"
1469 len = sizeof(my_addr);
1470 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1472 /* XXX: should use a dynamic buffer */
1473 sdp_data_size = prepare_sdp_description(stream,
1476 if (sdp_data_size > 0) {
1477 memcpy(q, sdp_data, sdp_data_size);
1489 /* prepare output buffer */
1490 c->buffer_ptr = c->buffer;
1492 c->state = HTTPSTATE_SEND_HEADER;
1498 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1502 stream->conns_served++;
1504 /* XXX: add there authenticate and IP match */
1507 /* if post, it means a feed is being sent */
1508 if (!stream->is_feed) {
1509 /* However it might be a status report from WMP! Let us log the
1510 * data as it might come in handy one day. */
1514 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1515 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1519 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1520 client_id = strtol(p + 18, 0, 10);
1521 p = strchr(p, '\n');
1529 char *eol = strchr(logline, '\n');
1534 if (eol[-1] == '\r')
1536 http_log("%.*s\n", (int) (eol - logline), logline);
1537 c->suppress_log = 1;
1542 http_log("\nGot request:\n%s\n", c->buffer);
1545 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1548 /* Now we have to find the client_id */
1549 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1550 if (wmpc->wmp_client_id == client_id)
1554 if (wmpc && modify_current_stream(wmpc, ratebuf))
1555 wmpc->switch_pending = 1;
1558 snprintf(msg, sizeof(msg), "POST command not handled");
1562 if (http_start_receive_data(c) < 0) {
1563 snprintf(msg, sizeof(msg), "could not open feed");
1567 c->state = HTTPSTATE_RECEIVE_DATA;
1572 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1573 http_log("\nGot request:\n%s\n", c->buffer);
1576 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1579 /* open input stream */
1580 if (open_input_stream(c, info) < 0) {
1581 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1585 /* prepare http header */
1587 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1588 mime_type = c->stream->fmt->mime_type;
1590 mime_type = "application/x-octet-stream";
1591 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1593 /* for asf, we need extra headers */
1594 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1595 /* Need to allocate a client id */
1597 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1599 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);
1601 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1602 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1604 /* prepare output buffer */
1606 c->buffer_ptr = c->buffer;
1608 c->state = HTTPSTATE_SEND_HEADER;
1611 c->http_error = 404;
1613 q += snprintf(q, c->buffer_size,
1614 "HTTP/1.0 404 Not Found\r\n"
1615 "Content-type: text/html\r\n"
1618 "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
1621 /* prepare output buffer */
1622 c->buffer_ptr = c->buffer;
1624 c->state = HTTPSTATE_SEND_HEADER;
1628 c->http_error = 200; /* horrible : we use this value to avoid
1629 going to the send data state */
1630 c->state = HTTPSTATE_SEND_HEADER;
1634 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1636 static const char *suffix = " kMGTP";
1639 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1641 url_fprintf(pb, "%"PRId64"%c", count, *s);
1644 static void compute_status(HTTPContext *c)
1653 if (url_open_dyn_buf(&pb) < 0) {
1654 /* XXX: return an error ? */
1655 c->buffer_ptr = c->buffer;
1656 c->buffer_end = c->buffer;
1660 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1661 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1662 url_fprintf(pb, "Pragma: no-cache\r\n");
1663 url_fprintf(pb, "\r\n");
1665 url_fprintf(pb, "<HTML><HEAD><TITLE>%s Status</TITLE>\n", program_name);
1666 if (c->stream->feed_filename[0])
1667 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1668 url_fprintf(pb, "</HEAD>\n<BODY>");
1669 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1671 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1672 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1673 url_fprintf(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");
1674 stream = first_stream;
1675 while (stream != NULL) {
1676 char sfilename[1024];
1679 if (stream->feed != stream) {
1680 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1681 eosf = sfilename + strlen(sfilename);
1682 if (eosf - sfilename >= 4) {
1683 if (strcmp(eosf - 4, ".asf") == 0)
1684 strcpy(eosf - 4, ".asx");
1685 else if (strcmp(eosf - 3, ".rm") == 0)
1686 strcpy(eosf - 3, ".ram");
1687 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1688 /* generate a sample RTSP director if
1689 unicast. Generate an SDP redirector if
1691 eosf = strrchr(sfilename, '.');
1693 eosf = sfilename + strlen(sfilename);
1694 if (stream->is_multicast)
1695 strcpy(eosf, ".sdp");
1697 strcpy(eosf, ".rtsp");
1701 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1702 sfilename, stream->filename);
1703 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1704 stream->conns_served);
1705 fmt_bytecount(pb, stream->bytes_served);
1706 switch(stream->stream_type) {
1707 case STREAM_TYPE_LIVE: {
1708 int audio_bit_rate = 0;
1709 int video_bit_rate = 0;
1710 const char *audio_codec_name = "";
1711 const char *video_codec_name = "";
1712 const char *audio_codec_name_extra = "";
1713 const char *video_codec_name_extra = "";
1715 for(i=0;i<stream->nb_streams;i++) {
1716 AVStream *st = stream->streams[i];
1717 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1718 switch(st->codec->codec_type) {
1719 case CODEC_TYPE_AUDIO:
1720 audio_bit_rate += st->codec->bit_rate;
1722 if (*audio_codec_name)
1723 audio_codec_name_extra = "...";
1724 audio_codec_name = codec->name;
1727 case CODEC_TYPE_VIDEO:
1728 video_bit_rate += st->codec->bit_rate;
1730 if (*video_codec_name)
1731 video_codec_name_extra = "...";
1732 video_codec_name = codec->name;
1735 case CODEC_TYPE_DATA:
1736 video_bit_rate += st->codec->bit_rate;
1742 url_fprintf(pb, "<TD align=center> %s <TD align=right> %d <TD align=right> %d <TD> %s %s <TD align=right> %d <TD> %s %s",
1745 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1746 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1748 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1750 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1751 url_fprintf(pb, "\n");
1755 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1759 stream = stream->next;
1761 url_fprintf(pb, "</TABLE>\n");
1763 stream = first_stream;
1764 while (stream != NULL) {
1765 if (stream->feed == stream) {
1766 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1768 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1770 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1775 /* This is somewhat linux specific I guess */
1776 snprintf(ps_cmd, sizeof(ps_cmd),
1777 "ps -o \"%%cpu,cputime\" --no-headers %d",
1780 pid_stat = popen(ps_cmd, "r");
1785 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1787 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1795 url_fprintf(pb, "<p>");
1797 url_fprintf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
1799 for (i = 0; i < stream->nb_streams; i++) {
1800 AVStream *st = stream->streams[i];
1801 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1802 const char *type = "unknown";
1803 char parameters[64];
1807 switch(st->codec->codec_type) {
1808 case CODEC_TYPE_AUDIO:
1810 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1812 case CODEC_TYPE_VIDEO:
1814 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1815 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1820 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1821 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1823 url_fprintf(pb, "</table>\n");
1826 stream = stream->next;
1832 AVCodecContext *enc;
1836 stream = first_feed;
1837 while (stream != NULL) {
1838 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1839 url_fprintf(pb, "<TABLE>\n");
1840 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1841 for(i=0;i<stream->nb_streams;i++) {
1842 AVStream *st = stream->streams[i];
1843 FeedData *fdata = st->priv_data;
1846 avcodec_string(buf, sizeof(buf), enc);
1847 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1848 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1849 avg /= enc->frame_size;
1850 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1851 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1853 url_fprintf(pb, "</TABLE>\n");
1854 stream = stream->next_feed;
1859 /* connection status */
1860 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1862 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1863 nb_connections, nb_max_connections);
1865 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<BR>\n",
1866 current_bandwidth, max_bandwidth);
1868 url_fprintf(pb, "<TABLE>\n");
1869 url_fprintf(pb, "<TR><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
1870 c1 = first_http_ctx;
1872 while (c1 != NULL) {
1878 for (j = 0; j < c1->stream->nb_streams; j++) {
1879 if (!c1->stream->feed)
1880 bitrate += c1->stream->streams[j]->codec->bit_rate;
1881 else if (c1->feed_streams[j] >= 0)
1882 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1887 p = inet_ntoa(c1->from_addr.sin_addr);
1888 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1890 c1->stream ? c1->stream->filename : "",
1891 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1894 http_state[c1->state]);
1895 fmt_bytecount(pb, bitrate);
1896 url_fprintf(pb, "<td align=right>");
1897 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1898 url_fprintf(pb, "<td align=right>");
1899 fmt_bytecount(pb, c1->data_count);
1900 url_fprintf(pb, "\n");
1903 url_fprintf(pb, "</TABLE>\n");
1908 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1909 url_fprintf(pb, "</BODY>\n</HTML>\n");
1911 len = url_close_dyn_buf(pb, &c->pb_buffer);
1912 c->buffer_ptr = c->pb_buffer;
1913 c->buffer_end = c->pb_buffer + len;
1916 /* check if the parser needs to be opened for stream i */
1917 static void open_parser(AVFormatContext *s, int i)
1919 AVStream *st = s->streams[i];
1922 if (!st->codec->codec) {
1923 codec = avcodec_find_decoder(st->codec->codec_id);
1924 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1925 st->codec->parse_only = 1;
1926 if (avcodec_open(st->codec, codec) < 0)
1927 st->codec->parse_only = 0;
1932 static int open_input_stream(HTTPContext *c, const char *info)
1935 char input_filename[1024];
1937 int buf_size, i, ret;
1940 /* find file name */
1941 if (c->stream->feed) {
1942 strcpy(input_filename, c->stream->feed->feed_filename);
1943 buf_size = FFM_PACKET_SIZE;
1944 /* compute position (absolute time) */
1945 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1946 stream_pos = parse_date(buf, 0);
1947 if (stream_pos == INT64_MIN)
1949 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1950 int prebuffer = strtol(buf, 0, 10);
1951 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1953 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1955 strcpy(input_filename, c->stream->feed_filename);
1957 /* compute position (relative time) */
1958 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1959 stream_pos = parse_date(buf, 1);
1960 if (stream_pos == INT64_MIN)
1965 if (input_filename[0] == '\0')
1969 { time_t when = stream_pos / 1000000;
1970 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1975 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1976 buf_size, c->stream->ap_in)) < 0) {
1977 http_log("could not open %s: %d\n", input_filename, ret);
1980 s->flags |= AVFMT_FLAG_GENPTS;
1982 av_find_stream_info(c->fmt_in);
1984 /* open each parser */
1985 for(i=0;i<s->nb_streams;i++)
1988 /* choose stream as clock source (we favorize video stream if
1989 present) for packet sending */
1990 c->pts_stream_index = 0;
1991 for(i=0;i<c->stream->nb_streams;i++) {
1992 if (c->pts_stream_index == 0 &&
1993 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1994 c->pts_stream_index = i;
1999 if (c->fmt_in->iformat->read_seek)
2000 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2002 /* set the start time (needed for maxtime and RTP packet timing) */
2003 c->start_time = cur_time;
2004 c->first_pts = AV_NOPTS_VALUE;
2008 /* return the server clock (in us) */
2009 static int64_t get_server_clock(HTTPContext *c)
2011 /* compute current pts value from system time */
2012 return (cur_time - c->start_time) * 1000;
2015 /* return the estimated time at which the current packet must be sent
2017 static int64_t get_packet_send_clock(HTTPContext *c)
2019 int bytes_left, bytes_sent, frame_bytes;
2021 frame_bytes = c->cur_frame_bytes;
2022 if (frame_bytes <= 0)
2025 bytes_left = c->buffer_end - c->buffer_ptr;
2026 bytes_sent = frame_bytes - bytes_left;
2027 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2032 static int http_prepare_data(HTTPContext *c)
2035 AVFormatContext *ctx;
2037 av_freep(&c->pb_buffer);
2039 case HTTPSTATE_SEND_DATA_HEADER:
2040 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2041 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2042 sizeof(c->fmt_ctx.author));
2043 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2044 sizeof(c->fmt_ctx.comment));
2045 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2046 sizeof(c->fmt_ctx.copyright));
2047 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2048 sizeof(c->fmt_ctx.title));
2050 for(i=0;i<c->stream->nb_streams;i++) {
2053 st = av_mallocz(sizeof(AVStream));
2054 c->fmt_ctx.streams[i] = st;
2055 /* if file or feed, then just take streams from FFStream struct */
2056 if (!c->stream->feed ||
2057 c->stream->feed == c->stream)
2058 src = c->stream->streams[i];
2060 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2064 st->codec->frame_number = 0; /* XXX: should be done in
2065 AVStream, not in codec */
2067 /* set output format parameters */
2068 c->fmt_ctx.oformat = c->stream->fmt;
2069 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2071 c->got_key_frame = 0;
2073 /* prepare header and save header data in a stream */
2074 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2075 /* XXX: potential leak */
2078 c->fmt_ctx.pb->is_streamed = 1;
2081 * HACK to avoid mpeg ps muxer to spit many underflow errors
2082 * Default value from FFmpeg
2083 * Try to set it use configuration option
2085 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2086 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2088 av_set_parameters(&c->fmt_ctx, NULL);
2089 if (av_write_header(&c->fmt_ctx) < 0) {
2090 http_log("Error writing output header\n");
2094 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2095 c->buffer_ptr = c->pb_buffer;
2096 c->buffer_end = c->pb_buffer + len;
2098 c->state = HTTPSTATE_SEND_DATA;
2099 c->last_packet_sent = 0;
2101 case HTTPSTATE_SEND_DATA:
2102 /* find a new packet */
2103 /* read a packet from the input stream */
2104 if (c->stream->feed)
2105 ffm_set_write_index(c->fmt_in,
2106 c->stream->feed->feed_write_index,
2107 c->stream->feed->feed_size);
2109 if (c->stream->max_time &&
2110 c->stream->max_time + c->start_time - cur_time < 0)
2111 /* We have timed out */
2112 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2116 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2117 if (c->stream->feed && c->stream->feed->feed_opened) {
2118 /* if coming from feed, it means we reached the end of the
2119 ffm file, so must wait for more data */
2120 c->state = HTTPSTATE_WAIT_FEED;
2121 return 1; /* state changed */
2123 if (c->stream->loop) {
2124 av_close_input_file(c->fmt_in);
2126 if (open_input_stream(c, "") < 0)
2131 /* must send trailer now because eof or error */
2132 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2136 int source_index = pkt.stream_index;
2137 /* update first pts if needed */
2138 if (c->first_pts == AV_NOPTS_VALUE) {
2139 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2140 c->start_time = cur_time;
2142 /* send it to the appropriate stream */
2143 if (c->stream->feed) {
2144 /* if coming from a feed, select the right stream */
2145 if (c->switch_pending) {
2146 c->switch_pending = 0;
2147 for(i=0;i<c->stream->nb_streams;i++) {
2148 if (c->switch_feed_streams[i] == pkt.stream_index)
2149 if (pkt.flags & PKT_FLAG_KEY)
2150 do_switch_stream(c, i);
2151 if (c->switch_feed_streams[i] >= 0)
2152 c->switch_pending = 1;
2155 for(i=0;i<c->stream->nb_streams;i++) {
2156 if (c->feed_streams[i] == pkt.stream_index) {
2157 AVStream *st = c->fmt_in->streams[source_index];
2158 pkt.stream_index = i;
2159 if (pkt.flags & PKT_FLAG_KEY &&
2160 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2161 c->stream->nb_streams == 1))
2162 c->got_key_frame = 1;
2163 if (!c->stream->send_on_key || c->got_key_frame)
2168 AVCodecContext *codec;
2169 AVStream *ist, *ost;
2171 ist = c->fmt_in->streams[source_index];
2172 /* specific handling for RTP: we use several
2173 output stream (one for each RTP
2174 connection). XXX: need more abstract handling */
2175 if (c->is_packetized) {
2176 /* compute send time and duration */
2177 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2178 if (ist->start_time != AV_NOPTS_VALUE)
2179 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2180 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2182 printf("index=%d pts=%0.3f duration=%0.6f\n",
2184 (double)c->cur_pts /
2186 (double)c->cur_frame_duration /
2189 /* find RTP context */
2190 c->packet_stream_index = pkt.stream_index;
2191 ctx = c->rtp_ctx[c->packet_stream_index];
2193 av_free_packet(&pkt);
2196 codec = ctx->streams[0]->codec;
2197 /* only one stream per RTP connection */
2198 pkt.stream_index = 0;
2202 codec = ctx->streams[pkt.stream_index]->codec;
2205 if (c->is_packetized) {
2206 int max_packet_size;
2207 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2208 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2210 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2211 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2213 ret = url_open_dyn_buf(&ctx->pb);
2216 /* XXX: potential leak */
2219 ost = ctx->streams[pkt.stream_index];
2221 ctx->pb->is_streamed = 1;
2222 if (pkt.dts != AV_NOPTS_VALUE)
2223 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2224 if (pkt.pts != AV_NOPTS_VALUE)
2225 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2226 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2227 if (av_write_frame(ctx, &pkt) < 0) {
2228 http_log("Error writing frame to output\n");
2229 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2232 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2233 c->cur_frame_bytes = len;
2234 c->buffer_ptr = c->pb_buffer;
2235 c->buffer_end = c->pb_buffer + len;
2237 codec->frame_number++;
2239 av_free_packet(&pkt);
2243 av_free_packet(&pkt);
2248 case HTTPSTATE_SEND_DATA_TRAILER:
2249 /* last packet test ? */
2250 if (c->last_packet_sent || c->is_packetized)
2253 /* prepare header */
2254 if (url_open_dyn_buf(&ctx->pb) < 0) {
2255 /* XXX: potential leak */
2258 c->fmt_ctx.pb->is_streamed = 1;
2259 av_write_trailer(ctx);
2260 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2261 c->buffer_ptr = c->pb_buffer;
2262 c->buffer_end = c->pb_buffer + len;
2264 c->last_packet_sent = 1;
2270 /* should convert the format at the same time */
2271 /* send data starting at c->buffer_ptr to the output connection
2272 (either UDP or TCP connection) */
2273 static int http_send_data(HTTPContext *c)
2278 if (c->buffer_ptr >= c->buffer_end) {
2279 ret = http_prepare_data(c);
2283 /* state change requested */
2286 if (c->is_packetized) {
2287 /* RTP data output */
2288 len = c->buffer_end - c->buffer_ptr;
2290 /* fail safe - should never happen */
2292 c->buffer_ptr = c->buffer_end;
2295 len = (c->buffer_ptr[0] << 24) |
2296 (c->buffer_ptr[1] << 16) |
2297 (c->buffer_ptr[2] << 8) |
2299 if (len > (c->buffer_end - c->buffer_ptr))
2301 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2302 /* nothing to send yet: we can wait */
2306 c->data_count += len;
2307 update_datarate(&c->datarate, c->data_count);
2309 c->stream->bytes_served += len;
2311 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2312 /* RTP packets are sent inside the RTSP TCP connection */
2314 int interleaved_index, size;
2316 HTTPContext *rtsp_c;
2319 /* if no RTSP connection left, error */
2322 /* if already sending something, then wait. */
2323 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2325 if (url_open_dyn_buf(&pb) < 0)
2327 interleaved_index = c->packet_stream_index * 2;
2328 /* RTCP packets are sent at odd indexes */
2329 if (c->buffer_ptr[1] == 200)
2330 interleaved_index++;
2331 /* write RTSP TCP header */
2333 header[1] = interleaved_index;
2334 header[2] = len >> 8;
2336 put_buffer(pb, header, 4);
2337 /* write RTP packet data */
2339 put_buffer(pb, c->buffer_ptr, len);
2340 size = url_close_dyn_buf(pb, &c->packet_buffer);
2341 /* prepare asynchronous TCP sending */
2342 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2343 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2344 c->buffer_ptr += len;
2346 /* send everything we can NOW */
2347 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2348 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2350 rtsp_c->packet_buffer_ptr += len;
2351 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2352 /* if we could not send all the data, we will
2353 send it later, so a new state is needed to
2354 "lock" the RTSP TCP connection */
2355 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2358 /* all data has been sent */
2359 av_freep(&c->packet_buffer);
2361 /* send RTP packet directly in UDP */
2363 url_write(c->rtp_handles[c->packet_stream_index],
2364 c->buffer_ptr, len);
2365 c->buffer_ptr += len;
2366 /* here we continue as we can send several packets per 10 ms slot */
2369 /* TCP data output */
2370 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2372 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2373 ff_neterrno() != FF_NETERROR(EINTR))
2374 /* error : close connection */
2379 c->buffer_ptr += len;
2381 c->data_count += len;
2382 update_datarate(&c->datarate, c->data_count);
2384 c->stream->bytes_served += len;
2392 static int http_start_receive_data(HTTPContext *c)
2396 if (c->stream->feed_opened)
2399 /* Don't permit writing to this one */
2400 if (c->stream->readonly)
2404 fd = open(c->stream->feed_filename, O_RDWR);
2406 http_log("Error opening feeder file: %s\n", strerror(errno));
2411 c->stream->feed_write_index = ffm_read_write_index(fd);
2412 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2413 lseek(fd, 0, SEEK_SET);
2415 /* init buffer input */
2416 c->buffer_ptr = c->buffer;
2417 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2418 c->stream->feed_opened = 1;
2422 static int http_receive_data(HTTPContext *c)
2426 if (c->buffer_end > c->buffer_ptr) {
2429 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2431 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2432 ff_neterrno() != FF_NETERROR(EINTR))
2433 /* error : close connection */
2435 } else if (len == 0)
2436 /* end of connection : close it */
2439 c->buffer_ptr += len;
2440 c->data_count += len;
2441 update_datarate(&c->datarate, c->data_count);
2445 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2446 if (c->buffer[0] != 'f' ||
2447 c->buffer[1] != 'm') {
2448 http_log("Feed stream has become desynchronized -- disconnecting\n");
2453 if (c->buffer_ptr >= c->buffer_end) {
2454 FFStream *feed = c->stream;
2455 /* a packet has been received : write it in the store, except
2457 if (c->data_count > FFM_PACKET_SIZE) {
2459 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2460 /* XXX: use llseek or url_seek */
2461 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2462 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2463 http_log("Error writing to feed file: %s\n", strerror(errno));
2467 feed->feed_write_index += FFM_PACKET_SIZE;
2468 /* update file size */
2469 if (feed->feed_write_index > c->stream->feed_size)
2470 feed->feed_size = feed->feed_write_index;
2472 /* handle wrap around if max file size reached */
2473 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2474 feed->feed_write_index = FFM_PACKET_SIZE;
2477 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2479 /* wake up any waiting connections */
2480 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2481 if (c1->state == HTTPSTATE_WAIT_FEED &&
2482 c1->stream->feed == c->stream->feed)
2483 c1->state = HTTPSTATE_SEND_DATA;
2486 /* We have a header in our hands that contains useful data */
2487 AVFormatContext *s = NULL;
2489 AVInputFormat *fmt_in;
2492 /* use feed output format name to find corresponding input format */
2493 fmt_in = av_find_input_format(feed->fmt->name);
2497 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2498 pb->is_streamed = 1;
2500 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2505 /* Now we have the actual streams */
2506 if (s->nb_streams != feed->nb_streams) {
2507 av_close_input_stream(s);
2512 for (i = 0; i < s->nb_streams; i++) {
2513 AVStream *fst = feed->streams[i];
2514 AVStream *st = s->streams[i];
2515 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2516 if (fst->codec->extradata_size) {
2517 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2518 if (!fst->codec->extradata)
2520 memcpy(fst->codec->extradata, st->codec->extradata,
2521 fst->codec->extradata_size);
2525 av_close_input_stream(s);
2528 c->buffer_ptr = c->buffer;
2533 c->stream->feed_opened = 0;
2535 /* wake up any waiting connections to stop waiting for feed */
2536 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2537 if (c1->state == HTTPSTATE_WAIT_FEED &&
2538 c1->stream->feed == c->stream->feed)
2539 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2544 /********************************************************************/
2547 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2554 switch(error_number) {
2555 case RTSP_STATUS_OK:
2558 case RTSP_STATUS_METHOD:
2559 str = "Method Not Allowed";
2561 case RTSP_STATUS_BANDWIDTH:
2562 str = "Not Enough Bandwidth";
2564 case RTSP_STATUS_SESSION:
2565 str = "Session Not Found";
2567 case RTSP_STATUS_STATE:
2568 str = "Method Not Valid in This State";
2570 case RTSP_STATUS_AGGREGATE:
2571 str = "Aggregate operation not allowed";
2573 case RTSP_STATUS_ONLY_AGGREGATE:
2574 str = "Only aggregate operation allowed";
2576 case RTSP_STATUS_TRANSPORT:
2577 str = "Unsupported transport";
2579 case RTSP_STATUS_INTERNAL:
2580 str = "Internal Server Error";
2582 case RTSP_STATUS_SERVICE:
2583 str = "Service Unavailable";
2585 case RTSP_STATUS_VERSION:
2586 str = "RTSP Version not supported";
2589 str = "Unknown Error";
2593 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2594 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2596 /* output GMT time */
2600 p = buf2 + strlen(p) - 1;
2603 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2606 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2608 rtsp_reply_header(c, error_number);
2609 url_fprintf(c->pb, "\r\n");
2612 static int rtsp_parse_request(HTTPContext *c)
2614 const char *p, *p1, *p2;
2620 RTSPHeader header1, *header = &header1;
2622 c->buffer_ptr[0] = '\0';
2625 get_word(cmd, sizeof(cmd), &p);
2626 get_word(url, sizeof(url), &p);
2627 get_word(protocol, sizeof(protocol), &p);
2629 av_strlcpy(c->method, cmd, sizeof(c->method));
2630 av_strlcpy(c->url, url, sizeof(c->url));
2631 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2633 if (url_open_dyn_buf(&c->pb) < 0) {
2634 /* XXX: cannot do more */
2635 c->pb = NULL; /* safety */
2639 /* check version name */
2640 if (strcmp(protocol, "RTSP/1.0") != 0) {
2641 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2645 /* parse each header line */
2646 memset(header, 0, sizeof(RTSPHeader));
2647 /* skip to next line */
2648 while (*p != '\n' && *p != '\0')
2652 while (*p != '\0') {
2653 p1 = strchr(p, '\n');
2657 if (p2 > p && p2[-1] == '\r')
2659 /* skip empty line */
2663 if (len > sizeof(line) - 1)
2664 len = sizeof(line) - 1;
2665 memcpy(line, p, len);
2667 rtsp_parse_line(header, line);
2671 /* handle sequence number */
2672 c->seq = header->seq;
2674 if (!strcmp(cmd, "DESCRIBE"))
2675 rtsp_cmd_describe(c, url);
2676 else if (!strcmp(cmd, "OPTIONS"))
2677 rtsp_cmd_options(c, url);
2678 else if (!strcmp(cmd, "SETUP"))
2679 rtsp_cmd_setup(c, url, header);
2680 else if (!strcmp(cmd, "PLAY"))
2681 rtsp_cmd_play(c, url, header);
2682 else if (!strcmp(cmd, "PAUSE"))
2683 rtsp_cmd_pause(c, url, header);
2684 else if (!strcmp(cmd, "TEARDOWN"))
2685 rtsp_cmd_teardown(c, url, header);
2687 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2690 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2691 c->pb = NULL; /* safety */
2693 /* XXX: cannot do more */
2696 c->buffer_ptr = c->pb_buffer;
2697 c->buffer_end = c->pb_buffer + len;
2698 c->state = RTSPSTATE_SEND_REPLY;
2702 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2703 struct in_addr my_ip)
2705 AVFormatContext *avc;
2706 AVStream avs[MAX_STREAMS];
2709 avc = av_alloc_format_context();
2713 if (stream->title[0] != 0) {
2714 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2716 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2718 avc->nb_streams = stream->nb_streams;
2719 if (stream->is_multicast) {
2720 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2721 inet_ntoa(stream->multicast_ip),
2722 stream->multicast_port, stream->multicast_ttl);
2725 for(i = 0; i < stream->nb_streams; i++) {
2726 avc->streams[i] = &avs[i];
2727 avc->streams[i]->codec = stream->streams[i]->codec;
2729 *pbuffer = av_mallocz(2048);
2730 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2733 return strlen(*pbuffer);
2736 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2738 // rtsp_reply_header(c, RTSP_STATUS_OK);
2739 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2740 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2741 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2742 url_fprintf(c->pb, "\r\n");
2745 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2751 int content_length, len;
2752 struct sockaddr_in my_addr;
2754 /* find which url is asked */
2755 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2760 for(stream = first_stream; stream != NULL; stream = stream->next) {
2761 if (!stream->is_feed &&
2762 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2763 !strcmp(path, stream->filename)) {
2767 /* no stream found */
2768 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2772 /* prepare the media description in sdp format */
2774 /* get the host IP */
2775 len = sizeof(my_addr);
2776 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2777 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2778 if (content_length < 0) {
2779 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2782 rtsp_reply_header(c, RTSP_STATUS_OK);
2783 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2784 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2785 url_fprintf(c->pb, "\r\n");
2786 put_buffer(c->pb, content, content_length);
2789 static HTTPContext *find_rtp_session(const char *session_id)
2793 if (session_id[0] == '\0')
2796 for(c = first_http_ctx; c != NULL; c = c->next) {
2797 if (!strcmp(c->session_id, session_id))
2803 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPLowerTransport lower_transport)
2805 RTSPTransportField *th;
2808 for(i=0;i<h->nb_transports;i++) {
2809 th = &h->transports[i];
2810 if (th->lower_transport == lower_transport)
2816 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2820 int stream_index, port;
2825 RTSPTransportField *th;
2826 struct sockaddr_in dest_addr;
2827 RTSPActionServerSetup setup;
2829 /* find which url is asked */
2830 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2835 /* now check each stream */
2836 for(stream = first_stream; stream != NULL; stream = stream->next) {
2837 if (!stream->is_feed &&
2838 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2839 /* accept aggregate filenames only if single stream */
2840 if (!strcmp(path, stream->filename)) {
2841 if (stream->nb_streams != 1) {
2842 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2849 for(stream_index = 0; stream_index < stream->nb_streams;
2851 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2852 stream->filename, stream_index);
2853 if (!strcmp(path, buf))
2858 /* no stream found */
2859 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2863 /* generate session id if needed */
2864 if (h->session_id[0] == '\0')
2865 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2866 av_random(&random_state), av_random(&random_state));
2868 /* find rtp session, and create it if none found */
2869 rtp_c = find_rtp_session(h->session_id);
2871 /* always prefer UDP */
2872 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
2874 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
2876 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2881 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2882 th->lower_transport);
2884 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2888 /* open input stream */
2889 if (open_input_stream(rtp_c, "") < 0) {
2890 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2895 /* test if stream is OK (test needed because several SETUP needs
2896 to be done for a given file) */
2897 if (rtp_c->stream != stream) {
2898 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2902 /* test if stream is already set up */
2903 if (rtp_c->rtp_ctx[stream_index]) {
2904 rtsp_reply_error(c, RTSP_STATUS_STATE);
2908 /* check transport */
2909 th = find_transport(h, rtp_c->rtp_protocol);
2910 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
2911 th->client_port_min <= 0)) {
2912 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2916 /* setup default options */
2917 setup.transport_option[0] = '\0';
2918 dest_addr = rtp_c->from_addr;
2919 dest_addr.sin_port = htons(th->client_port_min);
2922 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2923 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2927 /* now everything is OK, so we can send the connection parameters */
2928 rtsp_reply_header(c, RTSP_STATUS_OK);
2930 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2932 switch(rtp_c->rtp_protocol) {
2933 case RTSP_LOWER_TRANSPORT_UDP:
2934 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2935 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2936 "client_port=%d-%d;server_port=%d-%d",
2937 th->client_port_min, th->client_port_min + 1,
2940 case RTSP_LOWER_TRANSPORT_TCP:
2941 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2942 stream_index * 2, stream_index * 2 + 1);
2947 if (setup.transport_option[0] != '\0')
2948 url_fprintf(c->pb, ";%s", setup.transport_option);
2949 url_fprintf(c->pb, "\r\n");
2952 url_fprintf(c->pb, "\r\n");
2956 /* find an rtp connection by using the session ID. Check consistency
2958 static HTTPContext *find_rtp_session_with_url(const char *url,
2959 const char *session_id)
2967 rtp_c = find_rtp_session(session_id);
2971 /* find which url is asked */
2972 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2976 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2977 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2978 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2979 rtp_c->stream->filename, s);
2980 if(!strncmp(path, buf, sizeof(buf))) {
2981 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2988 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2992 rtp_c = find_rtp_session_with_url(url, h->session_id);
2994 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2998 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2999 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3000 rtp_c->state != HTTPSTATE_READY) {
3001 rtsp_reply_error(c, RTSP_STATUS_STATE);
3006 /* XXX: seek in stream */
3007 if (h->range_start != AV_NOPTS_VALUE) {
3008 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3009 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3013 rtp_c->state = HTTPSTATE_SEND_DATA;
3015 /* now everything is OK, so we can send the connection parameters */
3016 rtsp_reply_header(c, RTSP_STATUS_OK);
3018 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3019 url_fprintf(c->pb, "\r\n");
3022 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3026 rtp_c = find_rtp_session_with_url(url, h->session_id);
3028 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3032 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3033 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3034 rtsp_reply_error(c, RTSP_STATUS_STATE);
3038 rtp_c->state = HTTPSTATE_READY;
3039 rtp_c->first_pts = AV_NOPTS_VALUE;
3040 /* now everything is OK, so we can send the connection parameters */
3041 rtsp_reply_header(c, RTSP_STATUS_OK);
3043 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3044 url_fprintf(c->pb, "\r\n");
3047 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3050 char session_id[32];
3052 rtp_c = find_rtp_session_with_url(url, h->session_id);
3054 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3058 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3060 /* abort the session */
3061 close_connection(rtp_c);
3063 /* now everything is OK, so we can send the connection parameters */
3064 rtsp_reply_header(c, RTSP_STATUS_OK);
3066 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3067 url_fprintf(c->pb, "\r\n");
3071 /********************************************************************/
3074 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3075 FFStream *stream, const char *session_id,
3076 enum RTSPLowerTransport rtp_protocol)
3078 HTTPContext *c = NULL;
3079 const char *proto_str;
3081 /* XXX: should output a warning page when coming
3082 close to the connection limit */
3083 if (nb_connections >= nb_max_connections)
3086 /* add a new connection */
3087 c = av_mallocz(sizeof(HTTPContext));
3092 c->poll_entry = NULL;
3093 c->from_addr = *from_addr;
3094 c->buffer_size = IOBUFFER_INIT_SIZE;
3095 c->buffer = av_malloc(c->buffer_size);
3100 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3101 c->state = HTTPSTATE_READY;
3102 c->is_packetized = 1;
3103 c->rtp_protocol = rtp_protocol;
3105 /* protocol is shown in statistics */
3106 switch(c->rtp_protocol) {
3107 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3108 proto_str = "MCAST";
3110 case RTSP_LOWER_TRANSPORT_UDP:
3113 case RTSP_LOWER_TRANSPORT_TCP:
3120 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3121 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3123 current_bandwidth += stream->bandwidth;
3125 c->next = first_http_ctx;
3137 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3138 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3140 static int rtp_new_av_stream(HTTPContext *c,
3141 int stream_index, struct sockaddr_in *dest_addr,
3142 HTTPContext *rtsp_c)
3144 AVFormatContext *ctx;
3147 URLContext *h = NULL;
3149 int max_packet_size;
3151 /* now we can open the relevant output stream */
3152 ctx = av_alloc_format_context();
3155 ctx->oformat = guess_format("rtp", NULL, NULL);
3157 st = av_mallocz(sizeof(AVStream));
3160 st->codec= avcodec_alloc_context();
3161 ctx->nb_streams = 1;
3162 ctx->streams[0] = st;
3164 if (!c->stream->feed ||
3165 c->stream->feed == c->stream)
3166 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3169 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3171 st->priv_data = NULL;
3173 /* build destination RTP address */
3174 ipaddr = inet_ntoa(dest_addr->sin_addr);
3176 switch(c->rtp_protocol) {
3177 case RTSP_LOWER_TRANSPORT_UDP:
3178 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3181 /* XXX: also pass as parameter to function ? */
3182 if (c->stream->is_multicast) {
3184 ttl = c->stream->multicast_ttl;
3187 snprintf(ctx->filename, sizeof(ctx->filename),
3188 "rtp://%s:%d?multicast=1&ttl=%d",
3189 ipaddr, ntohs(dest_addr->sin_port), ttl);
3191 snprintf(ctx->filename, sizeof(ctx->filename),
3192 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3195 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3197 c->rtp_handles[stream_index] = h;
3198 max_packet_size = url_get_max_packet_size(h);
3200 case RTSP_LOWER_TRANSPORT_TCP:
3203 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3209 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3210 ipaddr, ntohs(dest_addr->sin_port),
3211 c->stream->filename, stream_index, c->protocol);
3213 /* normally, no packets should be output here, but the packet size may be checked */
3214 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3215 /* XXX: close stream */
3218 av_set_parameters(ctx, NULL);
3219 if (av_write_header(ctx) < 0) {
3226 url_close_dyn_buf(ctx->pb, &dummy_buf);
3229 c->rtp_ctx[stream_index] = ctx;
3233 /********************************************************************/
3234 /* ffserver initialization */
3236 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3240 fst = av_mallocz(sizeof(AVStream));
3243 fst->codec= avcodec_alloc_context();
3244 fst->priv_data = av_mallocz(sizeof(FeedData));
3245 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3246 fst->index = stream->nb_streams;
3247 av_set_pts_info(fst, 33, 1, 90000);
3248 stream->streams[stream->nb_streams++] = fst;
3252 /* return the stream number in the feed */
3253 static int add_av_stream(FFStream *feed, AVStream *st)
3256 AVCodecContext *av, *av1;
3260 for(i=0;i<feed->nb_streams;i++) {
3261 st = feed->streams[i];
3263 if (av1->codec_id == av->codec_id &&
3264 av1->codec_type == av->codec_type &&
3265 av1->bit_rate == av->bit_rate) {
3267 switch(av->codec_type) {
3268 case CODEC_TYPE_AUDIO:
3269 if (av1->channels == av->channels &&
3270 av1->sample_rate == av->sample_rate)
3273 case CODEC_TYPE_VIDEO:
3274 if (av1->width == av->width &&
3275 av1->height == av->height &&
3276 av1->time_base.den == av->time_base.den &&
3277 av1->time_base.num == av->time_base.num &&
3278 av1->gop_size == av->gop_size)
3287 fst = add_av_stream1(feed, av);
3290 return feed->nb_streams - 1;
3295 static void remove_stream(FFStream *stream)
3299 while (*ps != NULL) {
3307 /* specific mpeg4 handling : we extract the raw parameters */
3308 static void extract_mpeg4_header(AVFormatContext *infile)
3310 int mpeg4_count, i, size;
3316 for(i=0;i<infile->nb_streams;i++) {
3317 st = infile->streams[i];
3318 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3319 st->codec->extradata_size == 0) {
3326 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3327 while (mpeg4_count > 0) {
3328 if (av_read_packet(infile, &pkt) < 0)
3330 st = infile->streams[pkt.stream_index];
3331 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3332 st->codec->extradata_size == 0) {
3333 av_freep(&st->codec->extradata);
3334 /* fill extradata with the header */
3335 /* XXX: we make hard suppositions here ! */
3337 while (p < pkt.data + pkt.size - 4) {
3338 /* stop when vop header is found */
3339 if (p[0] == 0x00 && p[1] == 0x00 &&
3340 p[2] == 0x01 && p[3] == 0xb6) {
3341 size = p - pkt.data;
3342 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3343 st->codec->extradata = av_malloc(size);
3344 st->codec->extradata_size = size;
3345 memcpy(st->codec->extradata, pkt.data, size);
3352 av_free_packet(&pkt);
3356 /* compute the needed AVStream for each file */
3357 static void build_file_streams(void)
3359 FFStream *stream, *stream_next;
3360 AVFormatContext *infile;
3363 /* gather all streams */
3364 for(stream = first_stream; stream != NULL; stream = stream_next) {
3365 stream_next = stream->next;
3366 if (stream->stream_type == STREAM_TYPE_LIVE &&
3368 /* the stream comes from a file */
3369 /* try to open the file */
3371 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3372 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3373 /* specific case : if transport stream output to RTP,
3374 we use a raw transport stream reader */
3375 stream->ap_in->mpeg2ts_raw = 1;
3376 stream->ap_in->mpeg2ts_compute_pcr = 1;
3379 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3380 stream->ifmt, 0, stream->ap_in)) < 0) {
3381 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3382 /* remove stream (no need to spend more time on it) */
3384 remove_stream(stream);
3386 /* find all the AVStreams inside and reference them in
3388 if (av_find_stream_info(infile) < 0) {
3389 http_log("Could not find codec parameters from '%s'\n",
3390 stream->feed_filename);
3391 av_close_input_file(infile);
3394 extract_mpeg4_header(infile);
3396 for(i=0;i<infile->nb_streams;i++)
3397 add_av_stream1(stream, infile->streams[i]->codec);
3399 av_close_input_file(infile);
3405 /* compute the needed AVStream for each feed */
3406 static void build_feed_streams(void)
3408 FFStream *stream, *feed;
3411 /* gather all streams */
3412 for(stream = first_stream; stream != NULL; stream = stream->next) {
3413 feed = stream->feed;
3415 if (!stream->is_feed) {
3416 /* we handle a stream coming from a feed */
3417 for(i=0;i<stream->nb_streams;i++)
3418 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3423 /* gather all streams */
3424 for(stream = first_stream; stream != NULL; stream = stream->next) {
3425 feed = stream->feed;
3427 if (stream->is_feed) {
3428 for(i=0;i<stream->nb_streams;i++)
3429 stream->feed_streams[i] = i;
3434 /* create feed files if needed */
3435 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3438 if (url_exist(feed->feed_filename)) {
3439 /* See if it matches */
3443 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3444 /* Now see if it matches */
3445 if (s->nb_streams == feed->nb_streams) {
3447 for(i=0;i<s->nb_streams;i++) {
3449 sf = feed->streams[i];
3452 if (sf->index != ss->index ||
3454 http_log("Index & Id do not match for stream %d (%s)\n",
3455 i, feed->feed_filename);
3458 AVCodecContext *ccf, *ccs;
3462 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3464 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3465 http_log("Codecs do not match for stream %d\n", i);
3467 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3468 http_log("Codec bitrates do not match for stream %d\n", i);
3470 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3471 if (CHECK_CODEC(time_base.den) ||
3472 CHECK_CODEC(time_base.num) ||
3473 CHECK_CODEC(width) ||
3474 CHECK_CODEC(height)) {
3475 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3478 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3479 if (CHECK_CODEC(sample_rate) ||
3480 CHECK_CODEC(channels) ||
3481 CHECK_CODEC(frame_size)) {
3482 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3486 http_log("Unknown codec type\n");
3494 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3495 feed->feed_filename, s->nb_streams, feed->nb_streams);
3497 av_close_input_file(s);
3499 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3500 feed->feed_filename);
3503 if (feed->readonly) {
3504 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3505 feed->feed_filename);
3508 unlink(feed->feed_filename);
3511 if (!url_exist(feed->feed_filename)) {
3512 AVFormatContext s1 = {0}, *s = &s1;
3514 if (feed->readonly) {
3515 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3516 feed->feed_filename);
3520 /* only write the header of the ffm file */
3521 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3522 http_log("Could not open output feed file '%s'\n",
3523 feed->feed_filename);
3526 s->oformat = feed->fmt;
3527 s->nb_streams = feed->nb_streams;
3528 for(i=0;i<s->nb_streams;i++) {
3530 st = feed->streams[i];
3533 av_set_parameters(s, NULL);
3534 if (av_write_header(s) < 0) {
3535 http_log("Container doesn't supports the required parameters\n");
3538 /* XXX: need better api */
3539 av_freep(&s->priv_data);
3542 /* get feed size and write index */
3543 fd = open(feed->feed_filename, O_RDONLY);
3545 http_log("Could not open output feed file '%s'\n",
3546 feed->feed_filename);
3550 feed->feed_write_index = ffm_read_write_index(fd);
3551 feed->feed_size = lseek(fd, 0, SEEK_END);
3552 /* ensure that we do not wrap before the end of file */
3553 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3554 feed->feed_max_size = feed->feed_size;
3560 /* compute the bandwidth used by each stream */
3561 static void compute_bandwidth(void)
3567 for(stream = first_stream; stream != NULL; stream = stream->next) {
3569 for(i=0;i<stream->nb_streams;i++) {
3570 AVStream *st = stream->streams[i];
3571 switch(st->codec->codec_type) {
3572 case CODEC_TYPE_AUDIO:
3573 case CODEC_TYPE_VIDEO:
3574 bandwidth += st->codec->bit_rate;
3580 stream->bandwidth = (bandwidth + 999) / 1000;
3584 static void get_arg(char *buf, int buf_size, const char **pp)
3591 while (isspace(*p)) p++;
3594 if (*p == '\"' || *p == '\'')
3606 if ((q - buf) < buf_size - 1)
3611 if (quote && *p == quote)
3616 /* add a codec and set the default parameters */
3617 static void add_codec(FFStream *stream, AVCodecContext *av)
3621 /* compute default parameters */
3622 switch(av->codec_type) {
3623 case CODEC_TYPE_AUDIO:
3624 if (av->bit_rate == 0)
3625 av->bit_rate = 64000;
3626 if (av->sample_rate == 0)
3627 av->sample_rate = 22050;
3628 if (av->channels == 0)
3631 case CODEC_TYPE_VIDEO:
3632 if (av->bit_rate == 0)
3633 av->bit_rate = 64000;
3634 if (av->time_base.num == 0){
3635 av->time_base.den = 5;
3636 av->time_base.num = 1;
3638 if (av->width == 0 || av->height == 0) {
3642 /* Bitrate tolerance is less for streaming */
3643 if (av->bit_rate_tolerance == 0)
3644 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3645 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3650 if (av->max_qdiff == 0)
3652 av->qcompress = 0.5;
3655 if (!av->nsse_weight)
3656 av->nsse_weight = 8;
3658 av->frame_skip_cmp = FF_CMP_DCTMAX;
3659 av->me_method = ME_EPZS;
3660 av->rc_buffer_aggressivity = 1.0;
3663 av->rc_eq = "tex^qComp";
3664 if (!av->i_quant_factor)
3665 av->i_quant_factor = -0.8;
3666 if (!av->b_quant_factor)
3667 av->b_quant_factor = 1.25;
3668 if (!av->b_quant_offset)
3669 av->b_quant_offset = 1.25;
3670 if (!av->rc_max_rate)
3671 av->rc_max_rate = av->bit_rate * 2;
3673 if (av->rc_max_rate && !av->rc_buffer_size) {
3674 av->rc_buffer_size = av->rc_max_rate;
3683 st = av_mallocz(sizeof(AVStream));
3686 st->codec = avcodec_alloc_context();
3687 stream->streams[stream->nb_streams++] = st;
3688 memcpy(st->codec, av, sizeof(AVCodecContext));
3691 static enum CodecID opt_audio_codec(const char *arg)
3693 AVCodec *p= avcodec_find_encoder_by_name(arg);
3695 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3696 return CODEC_ID_NONE;
3701 static enum CodecID opt_video_codec(const char *arg)
3703 AVCodec *p= avcodec_find_encoder_by_name(arg);
3705 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3706 return CODEC_ID_NONE;
3711 /* simplistic plugin support */
3714 static void load_module(const char *filename)
3717 void (*init_func)(void);
3718 dll = dlopen(filename, RTLD_NOW);
3720 fprintf(stderr, "Could not load module '%s' - %s\n",
3721 filename, dlerror());
3725 init_func = dlsym(dll, "ffserver_module_init");
3728 "%s: init function 'ffserver_module_init()' not found\n",
3737 static int ffserver_opt_default(const char *opt, const char *arg,
3738 AVCodecContext *avctx, int type)
3741 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
3743 ret = av_set_string3(avctx, opt, arg, 1, NULL);
3747 static int parse_ffconfig(const char *filename)
3754 int val, errors, line_num;
3755 FFStream **last_stream, *stream, *redirect;
3756 FFStream **last_feed, *feed;
3757 AVCodecContext audio_enc, video_enc;
3758 enum CodecID audio_id, video_id;
3760 f = fopen(filename, "r");
3768 first_stream = NULL;
3769 last_stream = &first_stream;
3771 last_feed = &first_feed;
3775 audio_id = CODEC_ID_NONE;
3776 video_id = CODEC_ID_NONE;
3778 if (fgets(line, sizeof(line), f) == NULL)
3784 if (*p == '\0' || *p == '#')
3787 get_arg(cmd, sizeof(cmd), &p);
3789 if (!strcasecmp(cmd, "Port")) {
3790 get_arg(arg, sizeof(arg), &p);
3792 if (val < 1 || val > 65536) {
3793 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3794 filename, line_num, arg);
3797 my_http_addr.sin_port = htons(val);
3798 } else if (!strcasecmp(cmd, "BindAddress")) {
3799 get_arg(arg, sizeof(arg), &p);
3800 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3801 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3802 filename, line_num, arg);
3805 } else if (!strcasecmp(cmd, "NoDaemon")) {
3806 ffserver_daemon = 0;
3807 } else if (!strcasecmp(cmd, "RTSPPort")) {
3808 get_arg(arg, sizeof(arg), &p);
3810 if (val < 1 || val > 65536) {
3811 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3812 filename, line_num, arg);
3815 my_rtsp_addr.sin_port = htons(atoi(arg));
3816 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3817 get_arg(arg, sizeof(arg), &p);
3818 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3819 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3820 filename, line_num, arg);
3823 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
3824 get_arg(arg, sizeof(arg), &p);
3826 if (val < 1 || val > 65536) {
3827 fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n",
3828 filename, line_num, arg);
3831 nb_max_http_connections = val;
3832 } else if (!strcasecmp(cmd, "MaxClients")) {
3833 get_arg(arg, sizeof(arg), &p);
3835 if (val < 1 || val > nb_max_http_connections) {
3836 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3837 filename, line_num, arg);
3840 nb_max_connections = val;
3842 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3844 get_arg(arg, sizeof(arg), &p);
3846 if (llval < 10 || llval > 10000000) {
3847 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3848 filename, line_num, arg);
3851 max_bandwidth = llval;
3852 } else if (!strcasecmp(cmd, "CustomLog")) {
3853 if (!ffserver_debug)
3854 get_arg(logfilename, sizeof(logfilename), &p);
3855 } else if (!strcasecmp(cmd, "<Feed")) {
3856 /*********************************************/
3857 /* Feed related options */
3859 if (stream || feed) {
3860 fprintf(stderr, "%s:%d: Already in a tag\n",
3861 filename, line_num);
3863 feed = av_mallocz(sizeof(FFStream));
3864 /* add in stream list */
3865 *last_stream = feed;
3866 last_stream = &feed->next;
3867 /* add in feed list */
3869 last_feed = &feed->next_feed;
3871 get_arg(feed->filename, sizeof(feed->filename), &p);
3872 q = strrchr(feed->filename, '>');
3875 feed->fmt = guess_format("ffm", NULL, NULL);
3876 /* defaut feed file */
3877 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3878 "/tmp/%s.ffm", feed->filename);
3879 feed->feed_max_size = 5 * 1024 * 1024;
3881 feed->feed = feed; /* self feeding :-) */
3883 } else if (!strcasecmp(cmd, "Launch")) {
3887 feed->child_argv = av_mallocz(64 * sizeof(char *));
3889 for (i = 0; i < 62; i++) {
3890 get_arg(arg, sizeof(arg), &p);
3894 feed->child_argv[i] = av_strdup(arg);
3897 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3899 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3901 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3902 inet_ntoa(my_http_addr.sin_addr),
3903 ntohs(my_http_addr.sin_port), feed->filename);
3905 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3907 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3909 } else if (stream) {
3910 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3912 } else if (!strcasecmp(cmd, "File")) {
3914 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3916 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3917 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3922 get_arg(arg, sizeof(arg), &p);
3924 fsize = strtod(p1, &p1);
3925 switch(toupper(*p1)) {
3930 fsize *= 1024 * 1024;
3933 fsize *= 1024 * 1024 * 1024;
3936 feed->feed_max_size = (int64_t)fsize;
3938 } else if (!strcasecmp(cmd, "</Feed>")) {
3940 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3941 filename, line_num);
3945 } else if (!strcasecmp(cmd, "<Stream")) {
3946 /*********************************************/
3947 /* Stream related options */
3949 if (stream || feed) {
3950 fprintf(stderr, "%s:%d: Already in a tag\n",
3951 filename, line_num);
3953 const AVClass *class;
3954 stream = av_mallocz(sizeof(FFStream));
3955 *last_stream = stream;
3956 last_stream = &stream->next;
3958 get_arg(stream->filename, sizeof(stream->filename), &p);
3959 q = strrchr(stream->filename, '>');
3962 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3963 /* fetch avclass so AVOption works
3964 * FIXME try to use avcodec_get_context_defaults2
3965 * without changing defaults too much */
3966 avcodec_get_context_defaults(&video_enc);
3967 class = video_enc.av_class;
3968 memset(&audio_enc, 0, sizeof(AVCodecContext));
3969 memset(&video_enc, 0, sizeof(AVCodecContext));
3970 audio_enc.av_class = class;
3971 video_enc.av_class = class;
3972 audio_id = CODEC_ID_NONE;
3973 video_id = CODEC_ID_NONE;
3975 audio_id = stream->fmt->audio_codec;
3976 video_id = stream->fmt->video_codec;
3979 } else if (!strcasecmp(cmd, "Feed")) {
3980 get_arg(arg, sizeof(arg), &p);
3985 while (sfeed != NULL) {
3986 if (!strcmp(sfeed->filename, arg))
3988 sfeed = sfeed->next_feed;
3991 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3992 filename, line_num, arg);
3994 stream->feed = sfeed;
3996 } else if (!strcasecmp(cmd, "Format")) {
3997 get_arg(arg, sizeof(arg), &p);
3999 if (!strcmp(arg, "status")) {
4000 stream->stream_type = STREAM_TYPE_STATUS;
4003 stream->stream_type = STREAM_TYPE_LIVE;
4004 /* jpeg cannot be used here, so use single frame jpeg */
4005 if (!strcmp(arg, "jpeg"))
4006 strcpy(arg, "mjpeg");
4007 stream->fmt = guess_stream_format(arg, NULL, NULL);
4009 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4010 filename, line_num, arg);
4015 audio_id = stream->fmt->audio_codec;
4016 video_id = stream->fmt->video_codec;
4019 } else if (!strcasecmp(cmd, "InputFormat")) {
4020 get_arg(arg, sizeof(arg), &p);
4022 stream->ifmt = av_find_input_format(arg);
4023 if (!stream->ifmt) {
4024 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4025 filename, line_num, arg);
4028 } else if (!strcasecmp(cmd, "FaviconURL")) {
4029 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4030 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4032 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4033 filename, line_num);
4036 } else if (!strcasecmp(cmd, "Author")) {
4038 get_arg(stream->author, sizeof(stream->author), &p);
4039 } else if (!strcasecmp(cmd, "Comment")) {
4041 get_arg(stream->comment, sizeof(stream->comment), &p);
4042 } else if (!strcasecmp(cmd, "Copyright")) {
4044 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4045 } else if (!strcasecmp(cmd, "Title")) {
4047 get_arg(stream->title, sizeof(stream->title), &p);
4048 } else if (!strcasecmp(cmd, "Preroll")) {
4049 get_arg(arg, sizeof(arg), &p);
4051 stream->prebuffer = atof(arg) * 1000;
4052 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4054 stream->send_on_key = 1;
4055 } else if (!strcasecmp(cmd, "AudioCodec")) {
4056 get_arg(arg, sizeof(arg), &p);
4057 audio_id = opt_audio_codec(arg);
4058 if (audio_id == CODEC_ID_NONE) {
4059 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4060 filename, line_num, arg);
4063 } else if (!strcasecmp(cmd, "VideoCodec")) {
4064 get_arg(arg, sizeof(arg), &p);
4065 video_id = opt_video_codec(arg);
4066 if (video_id == CODEC_ID_NONE) {
4067 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4068 filename, line_num, arg);
4071 } else if (!strcasecmp(cmd, "MaxTime")) {
4072 get_arg(arg, sizeof(arg), &p);
4074 stream->max_time = atof(arg) * 1000;
4075 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4076 get_arg(arg, sizeof(arg), &p);
4078 audio_enc.bit_rate = atoi(arg) * 1000;
4079 } else if (!strcasecmp(cmd, "AudioChannels")) {
4080 get_arg(arg, sizeof(arg), &p);
4082 audio_enc.channels = atoi(arg);
4083 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4084 get_arg(arg, sizeof(arg), &p);
4086 audio_enc.sample_rate = atoi(arg);
4087 } else if (!strcasecmp(cmd, "AudioQuality")) {
4088 get_arg(arg, sizeof(arg), &p);
4090 // audio_enc.quality = atof(arg) * 1000;
4092 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4094 int minrate, maxrate;
4096 get_arg(arg, sizeof(arg), &p);
4098 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4099 video_enc.rc_min_rate = minrate * 1000;
4100 video_enc.rc_max_rate = maxrate * 1000;
4102 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4103 filename, line_num, arg);
4107 } else if (!strcasecmp(cmd, "Debug")) {
4109 get_arg(arg, sizeof(arg), &p);
4110 video_enc.debug = strtol(arg,0,0);
4112 } else if (!strcasecmp(cmd, "Strict")) {
4114 get_arg(arg, sizeof(arg), &p);
4115 video_enc.strict_std_compliance = atoi(arg);
4117 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4119 get_arg(arg, sizeof(arg), &p);
4120 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4122 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4124 get_arg(arg, sizeof(arg), &p);
4125 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4127 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4128 get_arg(arg, sizeof(arg), &p);
4130 video_enc.bit_rate = atoi(arg) * 1000;
4132 } else if (!strcasecmp(cmd, "VideoSize")) {
4133 get_arg(arg, sizeof(arg), &p);
4135 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4136 if ((video_enc.width % 16) != 0 ||
4137 (video_enc.height % 16) != 0) {
4138 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4139 filename, line_num);
4143 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4144 get_arg(arg, sizeof(arg), &p);
4146 AVRational frame_rate;
4147 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4148 fprintf(stderr, "Incorrect frame rate\n");
4151 video_enc.time_base.num = frame_rate.den;
4152 video_enc.time_base.den = frame_rate.num;
4155 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4156 get_arg(arg, sizeof(arg), &p);
4158 video_enc.gop_size = atoi(arg);
4159 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4161 video_enc.gop_size = 1;
4162 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4164 video_enc.mb_decision = FF_MB_DECISION_BITS;
4165 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4167 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4168 video_enc.flags |= CODEC_FLAG_4MV;
4170 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4171 !strcasecmp(cmd, "AVOptionAudio")) {
4173 AVCodecContext *avctx;
4175 get_arg(arg, sizeof(arg), &p);
4176 get_arg(arg2, sizeof(arg2), &p);
4177 if (!strcasecmp(cmd, "AVOptionVideo")) {
4179 type = AV_OPT_FLAG_VIDEO_PARAM;
4182 type = AV_OPT_FLAG_AUDIO_PARAM;
4184 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4185 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4188 } else if (!strcasecmp(cmd, "VideoTag")) {
4189 get_arg(arg, sizeof(arg), &p);
4190 if ((strlen(arg) == 4) && stream)
4191 video_enc.codec_tag = AV_RL32(arg);
4192 } else if (!strcasecmp(cmd, "BitExact")) {
4194 video_enc.flags |= CODEC_FLAG_BITEXACT;
4195 } else if (!strcasecmp(cmd, "DctFastint")) {
4197 video_enc.dct_algo = FF_DCT_FASTINT;
4198 } else if (!strcasecmp(cmd, "IdctSimple")) {
4200 video_enc.idct_algo = FF_IDCT_SIMPLE;
4201 } else if (!strcasecmp(cmd, "Qscale")) {
4202 get_arg(arg, sizeof(arg), &p);
4204 video_enc.flags |= CODEC_FLAG_QSCALE;
4205 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4207 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4208 get_arg(arg, sizeof(arg), &p);
4210 video_enc.max_qdiff = atoi(arg);
4211 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4212 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4213 filename, line_num);
4217 } else if (!strcasecmp(cmd, "VideoQMax")) {
4218 get_arg(arg, sizeof(arg), &p);
4220 video_enc.qmax = atoi(arg);
4221 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4222 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4223 filename, line_num);
4227 } else if (!strcasecmp(cmd, "VideoQMin")) {
4228 get_arg(arg, sizeof(arg), &p);
4230 video_enc.qmin = atoi(arg);
4231 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4232 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4233 filename, line_num);
4237 } else if (!strcasecmp(cmd, "LumaElim")) {
4238 get_arg(arg, sizeof(arg), &p);
4240 video_enc.luma_elim_threshold = atoi(arg);
4241 } else if (!strcasecmp(cmd, "ChromaElim")) {
4242 get_arg(arg, sizeof(arg), &p);
4244 video_enc.chroma_elim_threshold = atoi(arg);
4245 } else if (!strcasecmp(cmd, "LumiMask")) {
4246 get_arg(arg, sizeof(arg), &p);
4248 video_enc.lumi_masking = atof(arg);
4249 } else if (!strcasecmp(cmd, "DarkMask")) {
4250 get_arg(arg, sizeof(arg), &p);
4252 video_enc.dark_masking = atof(arg);
4253 } else if (!strcasecmp(cmd, "NoVideo")) {
4254 video_id = CODEC_ID_NONE;
4255 } else if (!strcasecmp(cmd, "NoAudio")) {
4256 audio_id = CODEC_ID_NONE;
4257 } else if (!strcasecmp(cmd, "ACL")) {
4260 get_arg(arg, sizeof(arg), &p);
4261 if (strcasecmp(arg, "allow") == 0)
4262 acl.action = IP_ALLOW;
4263 else if (strcasecmp(arg, "deny") == 0)
4264 acl.action = IP_DENY;
4266 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4267 filename, line_num, arg);
4271 get_arg(arg, sizeof(arg), &p);
4273 if (resolve_host(&acl.first, arg) != 0) {
4274 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4275 filename, line_num, arg);
4278 acl.last = acl.first;
4280 get_arg(arg, sizeof(arg), &p);
4283 if (resolve_host(&acl.last, arg) != 0) {
4284 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4285 filename, line_num, arg);
4291 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4292 IPAddressACL **naclp = 0;
4298 naclp = &stream->acl;
4302 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4303 filename, line_num);
4309 naclp = &(*naclp)->next;
4314 } else if (!strcasecmp(cmd, "RTSPOption")) {
4315 get_arg(arg, sizeof(arg), &p);
4317 av_freep(&stream->rtsp_option);
4318 stream->rtsp_option = av_strdup(arg);
4320 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4321 get_arg(arg, sizeof(arg), &p);
4323 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4324 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4325 filename, line_num, arg);
4328 stream->is_multicast = 1;
4329 stream->loop = 1; /* default is looping */
4331 } else if (!strcasecmp(cmd, "MulticastPort")) {
4332 get_arg(arg, sizeof(arg), &p);
4334 stream->multicast_port = atoi(arg);
4335 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4336 get_arg(arg, sizeof(arg), &p);
4338 stream->multicast_ttl = atoi(arg);
4339 } else if (!strcasecmp(cmd, "NoLoop")) {
4342 } else if (!strcasecmp(cmd, "</Stream>")) {
4344 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4345 filename, line_num);
4348 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4349 if (audio_id != CODEC_ID_NONE) {
4350 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4351 audio_enc.codec_id = audio_id;
4352 add_codec(stream, &audio_enc);
4354 if (video_id != CODEC_ID_NONE) {
4355 video_enc.codec_type = CODEC_TYPE_VIDEO;
4356 video_enc.codec_id = video_id;
4357 add_codec(stream, &video_enc);
4362 } else if (!strcasecmp(cmd, "<Redirect")) {
4363 /*********************************************/
4365 if (stream || feed || redirect) {
4366 fprintf(stderr, "%s:%d: Already in a tag\n",
4367 filename, line_num);
4370 redirect = av_mallocz(sizeof(FFStream));
4371 *last_stream = redirect;
4372 last_stream = &redirect->next;
4374 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4375 q = strrchr(redirect->filename, '>');
4378 redirect->stream_type = STREAM_TYPE_REDIRECT;
4380 } else if (!strcasecmp(cmd, "URL")) {
4382 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4383 } else if (!strcasecmp(cmd, "</Redirect>")) {
4385 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4386 filename, line_num);
4389 if (!redirect->feed_filename[0]) {
4390 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4391 filename, line_num);
4396 } else if (!strcasecmp(cmd, "LoadModule")) {
4397 get_arg(arg, sizeof(arg), &p);
4401 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4402 filename, line_num, arg);
4406 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4407 filename, line_num, cmd);
4419 static void handle_child_exit(int sig)
4424 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4427 for (feed = first_feed; feed; feed = feed->next) {
4428 if (feed->pid == pid) {
4429 int uptime = time(0) - feed->pid_start;
4432 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4435 /* Turn off any more restarts */
4436 feed->child_argv = 0;
4441 need_to_start_children = 1;
4444 static void opt_debug()
4447 ffserver_daemon = 0;
4448 logfilename[0] = '-';
4451 static void opt_show_help(void)
4453 printf("usage: ffserver [options]\n"
4454 "Hyper fast multi format Audio/Video streaming server\n");
4456 show_help_options(options, "Main options:\n", 0, 0);
4459 static const OptionDef options[] = {
4460 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4461 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4462 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4463 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4464 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4465 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4466 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4470 int main(int argc, char **argv)
4472 struct sigaction sigact;
4478 config_filename = "/etc/ffserver.conf";
4480 my_program_name = argv[0];
4481 my_program_dir = getcwd(0, 0);
4482 ffserver_daemon = 1;
4484 parse_options(argc, argv, options, NULL);
4486 unsetenv("http_proxy"); /* Kill the http_proxy */
4488 av_random_init(&random_state, av_gettime() + (getpid() << 16));
4490 memset(&sigact, 0, sizeof(sigact));
4491 sigact.sa_handler = handle_child_exit;
4492 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4493 sigaction(SIGCHLD, &sigact, 0);
4495 if (parse_ffconfig(config_filename) < 0) {
4496 fprintf(stderr, "Incorrect config file - exiting.\n");
4500 /* open log file if needed */
4501 if (logfilename[0] != '\0') {
4502 if (!strcmp(logfilename, "-"))
4505 logfile = fopen(logfilename, "a");
4506 av_log_set_callback(http_av_log);
4509 build_file_streams();
4511 build_feed_streams();
4513 compute_bandwidth();
4515 /* put the process in background and detach it from its TTY */
4516 if (ffserver_daemon) {
4523 } else if (pid > 0) {
4530 open("/dev/null", O_RDWR);
4531 if (strcmp(logfilename, "-") != 0) {
4541 signal(SIGPIPE, SIG_IGN);
4543 if (ffserver_daemon)
4546 if (http_server() < 0) {
4547 http_log("Could not start server\n");