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
25 #ifndef HAVE_CLOSESOCKET
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 "libavcodec/opt.h"
43 #include <sys/ioctl.h>
49 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
61 const char program_name[] = "FFserver";
62 const int program_birth_year = 2000;
64 static const OptionDef options[];
67 HTTPSTATE_WAIT_REQUEST,
68 HTTPSTATE_SEND_HEADER,
69 HTTPSTATE_SEND_DATA_HEADER,
70 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
71 HTTPSTATE_SEND_DATA_TRAILER,
72 HTTPSTATE_RECEIVE_DATA,
73 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
76 RTSPSTATE_WAIT_REQUEST,
78 RTSPSTATE_SEND_PACKET,
81 static const char *http_state[] = {
97 #define IOBUFFER_INIT_SIZE 8192
99 /* timeouts are in ms */
100 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
101 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
103 #define SYNC_TIMEOUT (10 * 1000)
106 int64_t count1, count2;
107 int64_t time1, time2;
110 /* context associated with one connection */
111 typedef struct HTTPContext {
112 enum HTTPState state;
113 int fd; /* socket file descriptor */
114 struct sockaddr_in from_addr; /* origin */
115 struct pollfd *poll_entry; /* used when polling */
117 uint8_t *buffer_ptr, *buffer_end;
120 struct HTTPContext *next;
121 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
125 /* input format handling */
126 AVFormatContext *fmt_in;
127 int64_t start_time; /* In milliseconds - this wraps fairly often */
128 int64_t first_pts; /* initial pts value */
129 int64_t cur_pts; /* current pts value from the stream in us */
130 int64_t cur_frame_duration; /* duration of the current frame in us */
131 int cur_frame_bytes; /* output frame size, needed to compute
132 the time at which we send each
134 int pts_stream_index; /* stream we choose as clock reference */
135 int64_t cur_clock; /* current clock reference value in us */
136 /* output format handling */
137 struct FFStream *stream;
138 /* -1 is invalid stream */
139 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
140 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
142 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
143 int last_packet_sent; /* true if last data packet was sent */
145 DataRateData datarate;
152 int is_packetized; /* if true, the stream is packetized */
153 int packet_stream_index; /* current stream for output in state machine */
155 /* RTSP state specific */
156 uint8_t *pb_buffer; /* XXX: use that in all the code */
158 int seq; /* RTSP sequence number */
160 /* RTP state specific */
161 enum RTSPLowerTransport rtp_protocol;
162 char session_id[32]; /* session id */
163 AVFormatContext *rtp_ctx[MAX_STREAMS];
165 /* RTP/UDP specific */
166 URLContext *rtp_handles[MAX_STREAMS];
168 /* RTP/TCP specific */
169 struct HTTPContext *rtsp_c;
170 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
173 /* each generated stream is described here */
177 STREAM_TYPE_REDIRECT,
180 enum IPAddressAction {
185 typedef struct IPAddressACL {
186 struct IPAddressACL *next;
187 enum IPAddressAction action;
188 /* These are in host order */
189 struct in_addr first;
193 /* description of each stream of the ffserver.conf file */
194 typedef struct FFStream {
195 enum StreamType stream_type;
196 char filename[1024]; /* stream filename */
197 struct FFStream *feed; /* feed we are using (can be null if
199 AVFormatParameters *ap_in; /* input parameters */
200 AVInputFormat *ifmt; /* if non NULL, force input format */
204 int prebuffer; /* Number of millseconds early to start */
205 int64_t max_time; /* Number of milliseconds to run */
207 AVStream *streams[MAX_STREAMS];
208 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
209 char feed_filename[1024]; /* file name of the feed storage, or
210 input file name for a stream */
215 pid_t pid; /* Of ffmpeg process */
216 time_t pid_start; /* Of ffmpeg process */
218 struct FFStream *next;
219 unsigned bandwidth; /* bandwidth, in kbits/s */
222 /* multicast specific */
224 struct in_addr multicast_ip;
225 int multicast_port; /* first port used for multicast */
227 int loop; /* if true, send the stream in loops (only meaningful if file) */
230 int feed_opened; /* true if someone is writing to the feed */
231 int is_feed; /* true if it is a feed */
232 int readonly; /* True if writing is prohibited to the file */
234 int64_t bytes_served;
235 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
236 int64_t feed_write_index; /* current write position in feed (it wraps around) */
237 int64_t feed_size; /* current size of feed */
238 struct FFStream *next_feed;
241 typedef struct FeedData {
242 long long data_count;
243 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
246 static struct sockaddr_in my_http_addr;
247 static struct sockaddr_in my_rtsp_addr;
249 static char logfilename[1024];
250 static HTTPContext *first_http_ctx;
251 static FFStream *first_feed; /* contains only feeds */
252 static FFStream *first_stream; /* contains all streams, including feeds */
254 static void new_connection(int server_fd, int is_rtsp);
255 static void close_connection(HTTPContext *c);
258 static int handle_connection(HTTPContext *c);
259 static int http_parse_request(HTTPContext *c);
260 static int http_send_data(HTTPContext *c);
261 static void compute_status(HTTPContext *c);
262 static int open_input_stream(HTTPContext *c, const char *info);
263 static int http_start_receive_data(HTTPContext *c);
264 static int http_receive_data(HTTPContext *c);
267 static int rtsp_parse_request(HTTPContext *c);
268 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
269 static void rtsp_cmd_options(HTTPContext *c, const char *url);
270 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
271 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
273 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
276 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
277 struct in_addr my_ip);
280 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
281 FFStream *stream, const char *session_id,
282 enum RTSPLowerTransport rtp_protocol);
283 static int rtp_new_av_stream(HTTPContext *c,
284 int stream_index, struct sockaddr_in *dest_addr,
285 HTTPContext *rtsp_c);
287 static const char *my_program_name;
288 static const char *my_program_dir;
290 static const char *config_filename;
291 static int ffserver_debug;
292 static int ffserver_daemon;
293 static int no_launch;
294 static int need_to_start_children;
296 /* maximum number of simultaneous HTTP connections */
297 static unsigned int nb_max_http_connections = 2000;
298 static unsigned int nb_max_connections = 5;
299 static unsigned int nb_connections;
301 static uint64_t max_bandwidth = 1000;
302 static uint64_t current_bandwidth;
304 static int64_t cur_time; // Making this global saves on passing it around everywhere
306 static AVRandomState random_state;
308 static FILE *logfile = NULL;
310 static char *ctime1(char *buf2)
318 p = buf2 + strlen(p) - 1;
324 static void http_vlog(const char *fmt, va_list vargs)
326 static int print_prefix = 1;
331 fprintf(logfile, "%s ", buf);
333 print_prefix = strstr(fmt, "\n") != NULL;
334 vfprintf(logfile, fmt, vargs);
339 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
342 va_start(vargs, fmt);
343 http_vlog(fmt, vargs);
347 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
349 static int print_prefix = 1;
350 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
351 if (level > av_log_level)
353 if (print_prefix && avc)
354 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
355 print_prefix = strstr(fmt, "\n") != NULL;
356 http_vlog(fmt, vargs);
359 static void log_connection(HTTPContext *c)
364 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
365 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
366 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
369 static void update_datarate(DataRateData *drd, int64_t count)
371 if (!drd->time1 && !drd->count1) {
372 drd->time1 = drd->time2 = cur_time;
373 drd->count1 = drd->count2 = count;
374 } else if (cur_time - drd->time2 > 5000) {
375 drd->time1 = drd->time2;
376 drd->count1 = drd->count2;
377 drd->time2 = cur_time;
382 /* In bytes per second */
383 static int compute_datarate(DataRateData *drd, int64_t count)
385 if (cur_time == drd->time1)
388 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
392 static void start_children(FFStream *feed)
397 for (; feed; feed = feed->next) {
398 if (feed->child_argv && !feed->pid) {
399 feed->pid_start = time(0);
404 http_log("Unable to create children\n");
413 av_strlcpy(pathname, my_program_name, sizeof(pathname));
415 slash = strrchr(pathname, '/');
420 strcpy(slash, "ffmpeg");
422 http_log("Launch commandline: ");
423 http_log("%s ", pathname);
424 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
425 http_log("%s ", feed->child_argv[i]);
428 for (i = 3; i < 256; i++)
431 if (!ffserver_debug) {
432 i = open("/dev/null", O_RDWR);
441 /* This is needed to make relative pathnames work */
442 chdir(my_program_dir);
444 signal(SIGPIPE, SIG_DFL);
446 execvp(pathname, feed->child_argv);
454 /* open a listening socket */
455 static int socket_open_listen(struct sockaddr_in *my_addr)
459 server_fd = socket(AF_INET,SOCK_STREAM,0);
466 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
468 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
470 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
472 closesocket(server_fd);
476 if (listen (server_fd, 5) < 0) {
478 closesocket(server_fd);
481 ff_socket_nonblock(server_fd, 1);
486 /* start all multicast streams */
487 static void start_multicast(void)
492 struct sockaddr_in dest_addr;
493 int default_port, stream_index;
496 for(stream = first_stream; stream != NULL; stream = stream->next) {
497 if (stream->is_multicast) {
498 /* open the RTP connection */
499 snprintf(session_id, sizeof(session_id), "%08x%08x",
500 av_random(&random_state), av_random(&random_state));
502 /* choose a port if none given */
503 if (stream->multicast_port == 0) {
504 stream->multicast_port = default_port;
508 dest_addr.sin_family = AF_INET;
509 dest_addr.sin_addr = stream->multicast_ip;
510 dest_addr.sin_port = htons(stream->multicast_port);
512 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
513 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
517 if (open_input_stream(rtp_c, "") < 0) {
518 http_log("Could not open input stream for stream '%s'\n",
523 /* open each RTP stream */
524 for(stream_index = 0; stream_index < stream->nb_streams;
526 dest_addr.sin_port = htons(stream->multicast_port +
528 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
529 http_log("Could not open output stream '%s/streamid=%d'\n",
530 stream->filename, stream_index);
535 /* change state to send data */
536 rtp_c->state = HTTPSTATE_SEND_DATA;
541 /* main loop of the http server */
542 static int http_server(void)
544 int server_fd = 0, rtsp_server_fd = 0;
545 int ret, delay, delay1;
546 struct pollfd *poll_table, *poll_entry;
547 HTTPContext *c, *c_next;
549 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
550 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
554 if (my_http_addr.sin_port) {
555 server_fd = socket_open_listen(&my_http_addr);
560 if (my_rtsp_addr.sin_port) {
561 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
562 if (rtsp_server_fd < 0)
566 if (!rtsp_server_fd && !server_fd) {
567 http_log("HTTP and RTSP disabled.\n");
571 http_log("FFserver started.\n");
573 start_children(first_feed);
578 poll_entry = poll_table;
580 poll_entry->fd = server_fd;
581 poll_entry->events = POLLIN;
584 if (rtsp_server_fd) {
585 poll_entry->fd = rtsp_server_fd;
586 poll_entry->events = POLLIN;
590 /* wait for events on each HTTP handle */
597 case HTTPSTATE_SEND_HEADER:
598 case RTSPSTATE_SEND_REPLY:
599 case RTSPSTATE_SEND_PACKET:
600 c->poll_entry = poll_entry;
602 poll_entry->events = POLLOUT;
605 case HTTPSTATE_SEND_DATA_HEADER:
606 case HTTPSTATE_SEND_DATA:
607 case HTTPSTATE_SEND_DATA_TRAILER:
608 if (!c->is_packetized) {
609 /* for TCP, we output as much as we can (may need to put a limit) */
610 c->poll_entry = poll_entry;
612 poll_entry->events = POLLOUT;
615 /* when ffserver is doing the timing, we work by
616 looking at which packet need to be sent every
618 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
623 case HTTPSTATE_WAIT_REQUEST:
624 case HTTPSTATE_RECEIVE_DATA:
625 case HTTPSTATE_WAIT_FEED:
626 case RTSPSTATE_WAIT_REQUEST:
627 /* need to catch errors */
628 c->poll_entry = poll_entry;
630 poll_entry->events = POLLIN;/* Maybe this will work */
634 c->poll_entry = NULL;
640 /* wait for an event on one connection. We poll at least every
641 second to handle timeouts */
643 ret = poll(poll_table, poll_entry - poll_table, delay);
644 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
645 ff_neterrno() != FF_NETERROR(EINTR))
649 cur_time = av_gettime() / 1000;
651 if (need_to_start_children) {
652 need_to_start_children = 0;
653 start_children(first_feed);
656 /* now handle the events */
657 for(c = first_http_ctx; c != NULL; c = c_next) {
659 if (handle_connection(c) < 0) {
660 /* close and free the connection */
666 poll_entry = poll_table;
668 /* new HTTP connection request ? */
669 if (poll_entry->revents & POLLIN)
670 new_connection(server_fd, 0);
673 if (rtsp_server_fd) {
674 /* new RTSP connection request ? */
675 if (poll_entry->revents & POLLIN)
676 new_connection(rtsp_server_fd, 1);
681 /* start waiting for a new HTTP/RTSP request */
682 static void start_wait_request(HTTPContext *c, int is_rtsp)
684 c->buffer_ptr = c->buffer;
685 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
688 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
689 c->state = RTSPSTATE_WAIT_REQUEST;
691 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
692 c->state = HTTPSTATE_WAIT_REQUEST;
696 static void new_connection(int server_fd, int is_rtsp)
698 struct sockaddr_in from_addr;
700 HTTPContext *c = NULL;
702 len = sizeof(from_addr);
703 fd = accept(server_fd, (struct sockaddr *)&from_addr,
706 http_log("error during accept %s\n", strerror(errno));
709 ff_socket_nonblock(fd, 1);
711 /* XXX: should output a warning page when coming
712 close to the connection limit */
713 if (nb_connections >= nb_max_connections)
716 /* add a new connection */
717 c = av_mallocz(sizeof(HTTPContext));
722 c->poll_entry = NULL;
723 c->from_addr = from_addr;
724 c->buffer_size = IOBUFFER_INIT_SIZE;
725 c->buffer = av_malloc(c->buffer_size);
729 c->next = first_http_ctx;
733 start_wait_request(c, is_rtsp);
745 static void close_connection(HTTPContext *c)
747 HTTPContext **cp, *c1;
749 AVFormatContext *ctx;
753 /* remove connection from list */
754 cp = &first_http_ctx;
755 while ((*cp) != NULL) {
763 /* remove references, if any (XXX: do it faster) */
764 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
769 /* remove connection associated resources */
773 /* close each frame parser */
774 for(i=0;i<c->fmt_in->nb_streams;i++) {
775 st = c->fmt_in->streams[i];
776 if (st->codec->codec)
777 avcodec_close(st->codec);
779 av_close_input_file(c->fmt_in);
782 /* free RTP output streams if any */
785 nb_streams = c->stream->nb_streams;
787 for(i=0;i<nb_streams;i++) {
790 av_write_trailer(ctx);
793 h = c->rtp_handles[i];
800 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
803 if (url_open_dyn_buf(&ctx->pb) >= 0) {
804 av_write_trailer(ctx);
805 av_freep(&c->pb_buffer);
806 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
811 for(i=0; i<ctx->nb_streams; i++)
812 av_free(ctx->streams[i]);
814 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
815 current_bandwidth -= c->stream->bandwidth;
817 /* signal that there is no feed if we are the feeder socket */
818 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
819 c->stream->feed_opened = 0;
823 av_freep(&c->pb_buffer);
824 av_freep(&c->packet_buffer);
830 static int handle_connection(HTTPContext *c)
835 case HTTPSTATE_WAIT_REQUEST:
836 case RTSPSTATE_WAIT_REQUEST:
838 if ((c->timeout - cur_time) < 0)
840 if (c->poll_entry->revents & (POLLERR | POLLHUP))
843 /* no need to read if no events */
844 if (!(c->poll_entry->revents & POLLIN))
848 len = recv(c->fd, c->buffer_ptr, 1, 0);
850 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
851 ff_neterrno() != FF_NETERROR(EINTR))
853 } else if (len == 0) {
856 /* search for end of request. */
858 c->buffer_ptr += len;
860 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
861 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
862 /* request found : parse it and reply */
863 if (c->state == HTTPSTATE_WAIT_REQUEST) {
864 ret = http_parse_request(c);
866 ret = rtsp_parse_request(c);
870 } else if (ptr >= c->buffer_end) {
871 /* request too long: cannot do anything */
873 } else goto read_loop;
877 case HTTPSTATE_SEND_HEADER:
878 if (c->poll_entry->revents & (POLLERR | POLLHUP))
881 /* no need to write if no events */
882 if (!(c->poll_entry->revents & POLLOUT))
884 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
886 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
887 ff_neterrno() != FF_NETERROR(EINTR)) {
888 /* error : close connection */
889 av_freep(&c->pb_buffer);
893 c->buffer_ptr += len;
895 c->stream->bytes_served += len;
896 c->data_count += len;
897 if (c->buffer_ptr >= c->buffer_end) {
898 av_freep(&c->pb_buffer);
902 /* all the buffer was sent : synchronize to the incoming stream */
903 c->state = HTTPSTATE_SEND_DATA_HEADER;
904 c->buffer_ptr = c->buffer_end = c->buffer;
909 case HTTPSTATE_SEND_DATA:
910 case HTTPSTATE_SEND_DATA_HEADER:
911 case HTTPSTATE_SEND_DATA_TRAILER:
912 /* for packetized output, we consider we can always write (the
913 input streams sets the speed). It may be better to verify
914 that we do not rely too much on the kernel queues */
915 if (!c->is_packetized) {
916 if (c->poll_entry->revents & (POLLERR | POLLHUP))
919 /* no need to read if no events */
920 if (!(c->poll_entry->revents & POLLOUT))
923 if (http_send_data(c) < 0)
925 /* close connection if trailer sent */
926 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
929 case HTTPSTATE_RECEIVE_DATA:
930 /* no need to read if no events */
931 if (c->poll_entry->revents & (POLLERR | POLLHUP))
933 if (!(c->poll_entry->revents & POLLIN))
935 if (http_receive_data(c) < 0)
938 case HTTPSTATE_WAIT_FEED:
939 /* no need to read if no events */
940 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
943 /* nothing to do, we'll be waken up by incoming feed packets */
946 case RTSPSTATE_SEND_REPLY:
947 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
948 av_freep(&c->pb_buffer);
951 /* no need to write if no events */
952 if (!(c->poll_entry->revents & POLLOUT))
954 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
956 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
957 ff_neterrno() != FF_NETERROR(EINTR)) {
958 /* error : close connection */
959 av_freep(&c->pb_buffer);
963 c->buffer_ptr += len;
964 c->data_count += len;
965 if (c->buffer_ptr >= c->buffer_end) {
966 /* all the buffer was sent : wait for a new request */
967 av_freep(&c->pb_buffer);
968 start_wait_request(c, 1);
972 case RTSPSTATE_SEND_PACKET:
973 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
974 av_freep(&c->packet_buffer);
977 /* no need to write if no events */
978 if (!(c->poll_entry->revents & POLLOUT))
980 len = send(c->fd, c->packet_buffer_ptr,
981 c->packet_buffer_end - c->packet_buffer_ptr, 0);
983 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
984 ff_neterrno() != FF_NETERROR(EINTR)) {
985 /* error : close connection */
986 av_freep(&c->packet_buffer);
990 c->packet_buffer_ptr += len;
991 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
992 /* all the buffer was sent : wait for a new request */
993 av_freep(&c->packet_buffer);
994 c->state = RTSPSTATE_WAIT_REQUEST;
998 case HTTPSTATE_READY:
1007 static int extract_rates(char *rates, int ratelen, const char *request)
1011 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1012 if (strncasecmp(p, "Pragma:", 7) == 0) {
1013 const char *q = p + 7;
1015 while (*q && *q != '\n' && isspace(*q))
1018 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1024 memset(rates, 0xff, ratelen);
1027 while (*q && *q != '\n' && *q != ':')
1030 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1034 if (stream_no < ratelen && stream_no >= 0)
1035 rates[stream_no] = rate_no;
1037 while (*q && *q != '\n' && !isspace(*q))
1044 p = strchr(p, '\n');
1054 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1057 int best_bitrate = 100000000;
1060 for (i = 0; i < feed->nb_streams; i++) {
1061 AVCodecContext *feed_codec = feed->streams[i]->codec;
1063 if (feed_codec->codec_id != codec->codec_id ||
1064 feed_codec->sample_rate != codec->sample_rate ||
1065 feed_codec->width != codec->width ||
1066 feed_codec->height != codec->height)
1069 /* Potential stream */
1071 /* We want the fastest stream less than bit_rate, or the slowest
1072 * faster than bit_rate
1075 if (feed_codec->bit_rate <= bit_rate) {
1076 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1077 best_bitrate = feed_codec->bit_rate;
1081 if (feed_codec->bit_rate < best_bitrate) {
1082 best_bitrate = feed_codec->bit_rate;
1091 static int modify_current_stream(HTTPContext *c, char *rates)
1094 FFStream *req = c->stream;
1095 int action_required = 0;
1097 /* Not much we can do for a feed */
1101 for (i = 0; i < req->nb_streams; i++) {
1102 AVCodecContext *codec = req->streams[i]->codec;
1106 c->switch_feed_streams[i] = req->feed_streams[i];
1109 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1112 /* Wants off or slow */
1113 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1115 /* This doesn't work well when it turns off the only stream! */
1116 c->switch_feed_streams[i] = -2;
1117 c->feed_streams[i] = -2;
1122 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1123 action_required = 1;
1126 return action_required;
1130 static void do_switch_stream(HTTPContext *c, int i)
1132 if (c->switch_feed_streams[i] >= 0) {
1134 c->feed_streams[i] = c->switch_feed_streams[i];
1137 /* Now update the stream */
1139 c->switch_feed_streams[i] = -1;
1142 /* XXX: factorize in utils.c ? */
1143 /* XXX: take care with different space meaning */
1144 static void skip_spaces(const char **pp)
1148 while (*p == ' ' || *p == '\t')
1153 static void get_word(char *buf, int buf_size, const char **pp)
1161 while (!isspace(*p) && *p != '\0') {
1162 if ((q - buf) < buf_size - 1)
1171 static int validate_acl(FFStream *stream, HTTPContext *c)
1173 enum IPAddressAction last_action = IP_DENY;
1175 struct in_addr *src = &c->from_addr.sin_addr;
1176 unsigned long src_addr = src->s_addr;
1178 for (acl = stream->acl; acl; acl = acl->next) {
1179 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1180 return (acl->action == IP_ALLOW) ? 1 : 0;
1181 last_action = acl->action;
1184 /* Nothing matched, so return not the last action */
1185 return (last_action == IP_DENY) ? 1 : 0;
1188 /* compute the real filename of a file by matching it without its
1189 extensions to all the stream filenames */
1190 static void compute_real_filename(char *filename, int max_size)
1197 /* compute filename by matching without the file extensions */
1198 av_strlcpy(file1, filename, sizeof(file1));
1199 p = strrchr(file1, '.');
1202 for(stream = first_stream; stream != NULL; stream = stream->next) {
1203 av_strlcpy(file2, stream->filename, sizeof(file2));
1204 p = strrchr(file2, '.');
1207 if (!strcmp(file1, file2)) {
1208 av_strlcpy(filename, stream->filename, max_size);
1223 /* parse http request and prepare header */
1224 static int http_parse_request(HTTPContext *c)
1227 enum RedirType redir_type;
1229 char info[1024], filename[1024];
1233 const char *mime_type;
1237 char *useragent = 0;
1240 get_word(cmd, sizeof(cmd), (const char **)&p);
1241 av_strlcpy(c->method, cmd, sizeof(c->method));
1243 if (!strcmp(cmd, "GET"))
1245 else if (!strcmp(cmd, "POST"))
1250 get_word(url, sizeof(url), (const char **)&p);
1251 av_strlcpy(c->url, url, sizeof(c->url));
1253 get_word(protocol, sizeof(protocol), (const char **)&p);
1254 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1257 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1260 http_log("New connection: %s %s\n", cmd, url);
1262 /* find the filename and the optional info string in the request */
1263 p = strchr(url, '?');
1265 av_strlcpy(info, p, sizeof(info));
1270 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1272 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1273 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1275 if (*useragent && *useragent != '\n' && isspace(*useragent))
1279 p = strchr(p, '\n');
1286 redir_type = REDIR_NONE;
1287 if (match_ext(filename, "asx")) {
1288 redir_type = REDIR_ASX;
1289 filename[strlen(filename)-1] = 'f';
1290 } else if (match_ext(filename, "asf") &&
1291 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1292 /* if this isn't WMP or lookalike, return the redirector file */
1293 redir_type = REDIR_ASF;
1294 } else if (match_ext(filename, "rpm,ram")) {
1295 redir_type = REDIR_RAM;
1296 strcpy(filename + strlen(filename)-2, "m");
1297 } else if (match_ext(filename, "rtsp")) {
1298 redir_type = REDIR_RTSP;
1299 compute_real_filename(filename, sizeof(filename) - 1);
1300 } else if (match_ext(filename, "sdp")) {
1301 redir_type = REDIR_SDP;
1302 compute_real_filename(filename, sizeof(filename) - 1);
1305 // "redirect" / request to index.html
1306 if (!strlen(filename))
1307 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1309 stream = first_stream;
1310 while (stream != NULL) {
1311 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1313 stream = stream->next;
1315 if (stream == NULL) {
1316 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1321 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1322 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1324 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1325 c->http_error = 301;
1327 q += snprintf(q, c->buffer_size,
1328 "HTTP/1.0 301 Moved\r\n"
1330 "Content-type: text/html\r\n"
1332 "<html><head><title>Moved</title></head><body>\r\n"
1333 "You should be <a href=\"%s\">redirected</a>.\r\n"
1334 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1335 /* prepare output buffer */
1336 c->buffer_ptr = c->buffer;
1338 c->state = HTTPSTATE_SEND_HEADER;
1342 /* If this is WMP, get the rate information */
1343 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1344 if (modify_current_stream(c, ratebuf)) {
1345 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1346 if (c->switch_feed_streams[i] >= 0)
1347 do_switch_stream(c, i);
1352 /* If already streaming this feed, do not let start another feeder. */
1353 if (stream->feed_opened) {
1354 snprintf(msg, sizeof(msg), "This feed is already being received.");
1355 http_log("feed %s already being received\n", stream->feed_filename);
1359 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1360 current_bandwidth += stream->bandwidth;
1362 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1363 c->http_error = 200;
1365 q += snprintf(q, c->buffer_size,
1366 "HTTP/1.0 200 Server too busy\r\n"
1367 "Content-type: text/html\r\n"
1369 "<html><head><title>Too busy</title></head><body>\r\n"
1370 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1371 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1372 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1373 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1374 /* prepare output buffer */
1375 c->buffer_ptr = c->buffer;
1377 c->state = HTTPSTATE_SEND_HEADER;
1381 if (redir_type != REDIR_NONE) {
1384 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1385 if (strncasecmp(p, "Host:", 5) == 0) {
1389 p = strchr(p, '\n');
1400 while (isspace(*hostinfo))
1403 eoh = strchr(hostinfo, '\n');
1405 if (eoh[-1] == '\r')
1408 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1409 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1410 hostbuf[eoh - hostinfo] = 0;
1412 c->http_error = 200;
1414 switch(redir_type) {
1416 q += snprintf(q, c->buffer_size,
1417 "HTTP/1.0 200 ASX Follows\r\n"
1418 "Content-type: video/x-ms-asf\r\n"
1420 "<ASX Version=\"3\">\r\n"
1421 //"<!-- Autogenerated by ffserver -->\r\n"
1422 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1423 "</ASX>\r\n", hostbuf, filename, info);
1426 q += snprintf(q, c->buffer_size,
1427 "HTTP/1.0 200 RAM Follows\r\n"
1428 "Content-type: audio/x-pn-realaudio\r\n"
1430 "# Autogenerated by ffserver\r\n"
1431 "http://%s/%s%s\r\n", hostbuf, filename, info);
1434 q += snprintf(q, c->buffer_size,
1435 "HTTP/1.0 200 ASF Redirect follows\r\n"
1436 "Content-type: video/x-ms-asf\r\n"
1439 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1443 char hostname[256], *p;
1444 /* extract only hostname */
1445 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1446 p = strrchr(hostname, ':');
1449 q += snprintf(q, c->buffer_size,
1450 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1451 /* XXX: incorrect mime type ? */
1452 "Content-type: application/x-rtsp\r\n"
1454 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1460 int sdp_data_size, len;
1461 struct sockaddr_in my_addr;
1463 q += snprintf(q, c->buffer_size,
1464 "HTTP/1.0 200 OK\r\n"
1465 "Content-type: application/sdp\r\n"
1468 len = sizeof(my_addr);
1469 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1471 /* XXX: should use a dynamic buffer */
1472 sdp_data_size = prepare_sdp_description(stream,
1475 if (sdp_data_size > 0) {
1476 memcpy(q, sdp_data, sdp_data_size);
1488 /* prepare output buffer */
1489 c->buffer_ptr = c->buffer;
1491 c->state = HTTPSTATE_SEND_HEADER;
1497 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1501 stream->conns_served++;
1503 /* XXX: add there authenticate and IP match */
1506 /* if post, it means a feed is being sent */
1507 if (!stream->is_feed) {
1508 /* However it might be a status report from WMP! Let us log the
1509 * data as it might come in handy one day. */
1513 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1514 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1518 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1519 client_id = strtol(p + 18, 0, 10);
1520 p = strchr(p, '\n');
1528 char *eol = strchr(logline, '\n');
1533 if (eol[-1] == '\r')
1535 http_log("%.*s\n", (int) (eol - logline), logline);
1536 c->suppress_log = 1;
1541 http_log("\nGot request:\n%s\n", c->buffer);
1544 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1547 /* Now we have to find the client_id */
1548 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1549 if (wmpc->wmp_client_id == client_id)
1553 if (wmpc && modify_current_stream(wmpc, ratebuf))
1554 wmpc->switch_pending = 1;
1557 snprintf(msg, sizeof(msg), "POST command not handled");
1561 if (http_start_receive_data(c) < 0) {
1562 snprintf(msg, sizeof(msg), "could not open feed");
1566 c->state = HTTPSTATE_RECEIVE_DATA;
1571 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1572 http_log("\nGot request:\n%s\n", c->buffer);
1575 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1578 /* open input stream */
1579 if (open_input_stream(c, info) < 0) {
1580 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1584 /* prepare http header */
1586 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1587 mime_type = c->stream->fmt->mime_type;
1589 mime_type = "application/x-octet-stream";
1590 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1592 /* for asf, we need extra headers */
1593 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1594 /* Need to allocate a client id */
1596 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1598 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);
1600 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1601 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1603 /* prepare output buffer */
1605 c->buffer_ptr = c->buffer;
1607 c->state = HTTPSTATE_SEND_HEADER;
1610 c->http_error = 404;
1612 q += snprintf(q, c->buffer_size,
1613 "HTTP/1.0 404 Not Found\r\n"
1614 "Content-type: text/html\r\n"
1617 "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
1620 /* prepare output buffer */
1621 c->buffer_ptr = c->buffer;
1623 c->state = HTTPSTATE_SEND_HEADER;
1627 c->http_error = 200; /* horrible : we use this value to avoid
1628 going to the send data state */
1629 c->state = HTTPSTATE_SEND_HEADER;
1633 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1635 static const char *suffix = " kMGTP";
1638 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1640 url_fprintf(pb, "%"PRId64"%c", count, *s);
1643 static void compute_status(HTTPContext *c)
1652 if (url_open_dyn_buf(&pb) < 0) {
1653 /* XXX: return an error ? */
1654 c->buffer_ptr = c->buffer;
1655 c->buffer_end = c->buffer;
1659 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1660 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1661 url_fprintf(pb, "Pragma: no-cache\r\n");
1662 url_fprintf(pb, "\r\n");
1664 url_fprintf(pb, "<HTML><HEAD><TITLE>%s Status</TITLE>\n", program_name);
1665 if (c->stream->feed_filename[0])
1666 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1667 url_fprintf(pb, "</HEAD>\n<BODY>");
1668 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1670 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1671 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1672 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");
1673 stream = first_stream;
1674 while (stream != NULL) {
1675 char sfilename[1024];
1678 if (stream->feed != stream) {
1679 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1680 eosf = sfilename + strlen(sfilename);
1681 if (eosf - sfilename >= 4) {
1682 if (strcmp(eosf - 4, ".asf") == 0)
1683 strcpy(eosf - 4, ".asx");
1684 else if (strcmp(eosf - 3, ".rm") == 0)
1685 strcpy(eosf - 3, ".ram");
1686 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1687 /* generate a sample RTSP director if
1688 unicast. Generate an SDP redirector if
1690 eosf = strrchr(sfilename, '.');
1692 eosf = sfilename + strlen(sfilename);
1693 if (stream->is_multicast)
1694 strcpy(eosf, ".sdp");
1696 strcpy(eosf, ".rtsp");
1700 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1701 sfilename, stream->filename);
1702 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1703 stream->conns_served);
1704 fmt_bytecount(pb, stream->bytes_served);
1705 switch(stream->stream_type) {
1706 case STREAM_TYPE_LIVE: {
1707 int audio_bit_rate = 0;
1708 int video_bit_rate = 0;
1709 const char *audio_codec_name = "";
1710 const char *video_codec_name = "";
1711 const char *audio_codec_name_extra = "";
1712 const char *video_codec_name_extra = "";
1714 for(i=0;i<stream->nb_streams;i++) {
1715 AVStream *st = stream->streams[i];
1716 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1717 switch(st->codec->codec_type) {
1718 case CODEC_TYPE_AUDIO:
1719 audio_bit_rate += st->codec->bit_rate;
1721 if (*audio_codec_name)
1722 audio_codec_name_extra = "...";
1723 audio_codec_name = codec->name;
1726 case CODEC_TYPE_VIDEO:
1727 video_bit_rate += st->codec->bit_rate;
1729 if (*video_codec_name)
1730 video_codec_name_extra = "...";
1731 video_codec_name = codec->name;
1734 case CODEC_TYPE_DATA:
1735 video_bit_rate += st->codec->bit_rate;
1741 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",
1744 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1745 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1747 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1749 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1750 url_fprintf(pb, "\n");
1754 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1758 stream = stream->next;
1760 url_fprintf(pb, "</TABLE>\n");
1762 stream = first_stream;
1763 while (stream != NULL) {
1764 if (stream->feed == stream) {
1765 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1767 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1769 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1774 /* This is somewhat linux specific I guess */
1775 snprintf(ps_cmd, sizeof(ps_cmd),
1776 "ps -o \"%%cpu,cputime\" --no-headers %d",
1779 pid_stat = popen(ps_cmd, "r");
1784 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1786 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1794 url_fprintf(pb, "<p>");
1796 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");
1798 for (i = 0; i < stream->nb_streams; i++) {
1799 AVStream *st = stream->streams[i];
1800 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1801 const char *type = "unknown";
1802 char parameters[64];
1806 switch(st->codec->codec_type) {
1807 case CODEC_TYPE_AUDIO:
1809 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1811 case CODEC_TYPE_VIDEO:
1813 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1814 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1819 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1820 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1822 url_fprintf(pb, "</table>\n");
1825 stream = stream->next;
1831 AVCodecContext *enc;
1835 stream = first_feed;
1836 while (stream != NULL) {
1837 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1838 url_fprintf(pb, "<TABLE>\n");
1839 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1840 for(i=0;i<stream->nb_streams;i++) {
1841 AVStream *st = stream->streams[i];
1842 FeedData *fdata = st->priv_data;
1845 avcodec_string(buf, sizeof(buf), enc);
1846 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1847 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1848 avg /= enc->frame_size;
1849 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1850 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1852 url_fprintf(pb, "</TABLE>\n");
1853 stream = stream->next_feed;
1858 /* connection status */
1859 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1861 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1862 nb_connections, nb_max_connections);
1864 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<BR>\n",
1865 current_bandwidth, max_bandwidth);
1867 url_fprintf(pb, "<TABLE>\n");
1868 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");
1869 c1 = first_http_ctx;
1871 while (c1 != NULL) {
1877 for (j = 0; j < c1->stream->nb_streams; j++) {
1878 if (!c1->stream->feed)
1879 bitrate += c1->stream->streams[j]->codec->bit_rate;
1880 else if (c1->feed_streams[j] >= 0)
1881 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1886 p = inet_ntoa(c1->from_addr.sin_addr);
1887 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1889 c1->stream ? c1->stream->filename : "",
1890 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1893 http_state[c1->state]);
1894 fmt_bytecount(pb, bitrate);
1895 url_fprintf(pb, "<td align=right>");
1896 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1897 url_fprintf(pb, "<td align=right>");
1898 fmt_bytecount(pb, c1->data_count);
1899 url_fprintf(pb, "\n");
1902 url_fprintf(pb, "</TABLE>\n");
1907 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1908 url_fprintf(pb, "</BODY>\n</HTML>\n");
1910 len = url_close_dyn_buf(pb, &c->pb_buffer);
1911 c->buffer_ptr = c->pb_buffer;
1912 c->buffer_end = c->pb_buffer + len;
1915 /* check if the parser needs to be opened for stream i */
1916 static void open_parser(AVFormatContext *s, int i)
1918 AVStream *st = s->streams[i];
1921 if (!st->codec->codec) {
1922 codec = avcodec_find_decoder(st->codec->codec_id);
1923 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1924 st->codec->parse_only = 1;
1925 if (avcodec_open(st->codec, codec) < 0)
1926 st->codec->parse_only = 0;
1931 static int open_input_stream(HTTPContext *c, const char *info)
1934 char input_filename[1024];
1936 int buf_size, i, ret;
1939 /* find file name */
1940 if (c->stream->feed) {
1941 strcpy(input_filename, c->stream->feed->feed_filename);
1942 buf_size = FFM_PACKET_SIZE;
1943 /* compute position (absolute time) */
1944 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1945 stream_pos = parse_date(buf, 0);
1946 if (stream_pos == INT64_MIN)
1948 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1949 int prebuffer = strtol(buf, 0, 10);
1950 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1952 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1954 strcpy(input_filename, c->stream->feed_filename);
1956 /* compute position (relative time) */
1957 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1958 stream_pos = parse_date(buf, 1);
1959 if (stream_pos == INT64_MIN)
1964 if (input_filename[0] == '\0')
1968 { time_t when = stream_pos / 1000000;
1969 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1974 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1975 buf_size, c->stream->ap_in)) < 0) {
1976 http_log("could not open %s: %d\n", input_filename, ret);
1979 s->flags |= AVFMT_FLAG_GENPTS;
1981 av_find_stream_info(c->fmt_in);
1983 /* open each parser */
1984 for(i=0;i<s->nb_streams;i++)
1987 /* choose stream as clock source (we favorize video stream if
1988 present) for packet sending */
1989 c->pts_stream_index = 0;
1990 for(i=0;i<c->stream->nb_streams;i++) {
1991 if (c->pts_stream_index == 0 &&
1992 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1993 c->pts_stream_index = i;
1998 if (c->fmt_in->iformat->read_seek)
1999 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2001 /* set the start time (needed for maxtime and RTP packet timing) */
2002 c->start_time = cur_time;
2003 c->first_pts = AV_NOPTS_VALUE;
2007 /* return the server clock (in us) */
2008 static int64_t get_server_clock(HTTPContext *c)
2010 /* compute current pts value from system time */
2011 return (cur_time - c->start_time) * 1000;
2014 /* return the estimated time at which the current packet must be sent
2016 static int64_t get_packet_send_clock(HTTPContext *c)
2018 int bytes_left, bytes_sent, frame_bytes;
2020 frame_bytes = c->cur_frame_bytes;
2021 if (frame_bytes <= 0)
2024 bytes_left = c->buffer_end - c->buffer_ptr;
2025 bytes_sent = frame_bytes - bytes_left;
2026 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2031 static int http_prepare_data(HTTPContext *c)
2034 AVFormatContext *ctx;
2036 av_freep(&c->pb_buffer);
2038 case HTTPSTATE_SEND_DATA_HEADER:
2039 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2040 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2041 sizeof(c->fmt_ctx.author));
2042 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2043 sizeof(c->fmt_ctx.comment));
2044 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2045 sizeof(c->fmt_ctx.copyright));
2046 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2047 sizeof(c->fmt_ctx.title));
2049 for(i=0;i<c->stream->nb_streams;i++) {
2052 st = av_mallocz(sizeof(AVStream));
2053 c->fmt_ctx.streams[i] = st;
2054 /* if file or feed, then just take streams from FFStream struct */
2055 if (!c->stream->feed ||
2056 c->stream->feed == c->stream)
2057 src = c->stream->streams[i];
2059 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2063 st->codec->frame_number = 0; /* XXX: should be done in
2064 AVStream, not in codec */
2066 /* set output format parameters */
2067 c->fmt_ctx.oformat = c->stream->fmt;
2068 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2070 c->got_key_frame = 0;
2072 /* prepare header and save header data in a stream */
2073 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2074 /* XXX: potential leak */
2077 c->fmt_ctx.pb->is_streamed = 1;
2080 * HACK to avoid mpeg ps muxer to spit many underflow errors
2081 * Default value from FFmpeg
2082 * Try to set it use configuration option
2084 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2085 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2087 av_set_parameters(&c->fmt_ctx, NULL);
2088 if (av_write_header(&c->fmt_ctx) < 0) {
2089 http_log("Error writing output header\n");
2093 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2094 c->buffer_ptr = c->pb_buffer;
2095 c->buffer_end = c->pb_buffer + len;
2097 c->state = HTTPSTATE_SEND_DATA;
2098 c->last_packet_sent = 0;
2100 case HTTPSTATE_SEND_DATA:
2101 /* find a new packet */
2102 /* read a packet from the input stream */
2103 if (c->stream->feed)
2104 ffm_set_write_index(c->fmt_in,
2105 c->stream->feed->feed_write_index,
2106 c->stream->feed->feed_size);
2108 if (c->stream->max_time &&
2109 c->stream->max_time + c->start_time - cur_time < 0)
2110 /* We have timed out */
2111 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2115 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2116 if (c->stream->feed && c->stream->feed->feed_opened) {
2117 /* if coming from feed, it means we reached the end of the
2118 ffm file, so must wait for more data */
2119 c->state = HTTPSTATE_WAIT_FEED;
2120 return 1; /* state changed */
2122 if (c->stream->loop) {
2123 av_close_input_file(c->fmt_in);
2125 if (open_input_stream(c, "") < 0)
2130 /* must send trailer now because eof or error */
2131 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2135 int source_index = pkt.stream_index;
2136 /* update first pts if needed */
2137 if (c->first_pts == AV_NOPTS_VALUE) {
2138 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2139 c->start_time = cur_time;
2141 /* send it to the appropriate stream */
2142 if (c->stream->feed) {
2143 /* if coming from a feed, select the right stream */
2144 if (c->switch_pending) {
2145 c->switch_pending = 0;
2146 for(i=0;i<c->stream->nb_streams;i++) {
2147 if (c->switch_feed_streams[i] == pkt.stream_index)
2148 if (pkt.flags & PKT_FLAG_KEY)
2149 do_switch_stream(c, i);
2150 if (c->switch_feed_streams[i] >= 0)
2151 c->switch_pending = 1;
2154 for(i=0;i<c->stream->nb_streams;i++) {
2155 if (c->feed_streams[i] == pkt.stream_index) {
2156 AVStream *st = c->fmt_in->streams[source_index];
2157 pkt.stream_index = i;
2158 if (pkt.flags & PKT_FLAG_KEY &&
2159 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2160 c->stream->nb_streams == 1))
2161 c->got_key_frame = 1;
2162 if (!c->stream->send_on_key || c->got_key_frame)
2167 AVCodecContext *codec;
2168 AVStream *ist, *ost;
2170 ist = c->fmt_in->streams[source_index];
2171 /* specific handling for RTP: we use several
2172 output stream (one for each RTP
2173 connection). XXX: need more abstract handling */
2174 if (c->is_packetized) {
2175 /* compute send time and duration */
2176 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2177 if (ist->start_time != AV_NOPTS_VALUE)
2178 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2179 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2181 printf("index=%d pts=%0.3f duration=%0.6f\n",
2183 (double)c->cur_pts /
2185 (double)c->cur_frame_duration /
2188 /* find RTP context */
2189 c->packet_stream_index = pkt.stream_index;
2190 ctx = c->rtp_ctx[c->packet_stream_index];
2192 av_free_packet(&pkt);
2195 codec = ctx->streams[0]->codec;
2196 /* only one stream per RTP connection */
2197 pkt.stream_index = 0;
2201 codec = ctx->streams[pkt.stream_index]->codec;
2204 if (c->is_packetized) {
2205 int max_packet_size;
2206 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2207 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2209 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2210 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2212 ret = url_open_dyn_buf(&ctx->pb);
2215 /* XXX: potential leak */
2218 ost = ctx->streams[pkt.stream_index];
2220 ctx->pb->is_streamed = 1;
2221 if (pkt.dts != AV_NOPTS_VALUE)
2222 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2223 if (pkt.pts != AV_NOPTS_VALUE)
2224 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2225 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2226 if (av_write_frame(ctx, &pkt) < 0) {
2227 http_log("Error writing frame to output\n");
2228 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2231 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2232 c->cur_frame_bytes = len;
2233 c->buffer_ptr = c->pb_buffer;
2234 c->buffer_end = c->pb_buffer + len;
2236 codec->frame_number++;
2238 av_free_packet(&pkt);
2242 av_free_packet(&pkt);
2247 case HTTPSTATE_SEND_DATA_TRAILER:
2248 /* last packet test ? */
2249 if (c->last_packet_sent || c->is_packetized)
2252 /* prepare header */
2253 if (url_open_dyn_buf(&ctx->pb) < 0) {
2254 /* XXX: potential leak */
2257 c->fmt_ctx.pb->is_streamed = 1;
2258 av_write_trailer(ctx);
2259 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2260 c->buffer_ptr = c->pb_buffer;
2261 c->buffer_end = c->pb_buffer + len;
2263 c->last_packet_sent = 1;
2269 /* should convert the format at the same time */
2270 /* send data starting at c->buffer_ptr to the output connection
2271 (either UDP or TCP connection) */
2272 static int http_send_data(HTTPContext *c)
2277 if (c->buffer_ptr >= c->buffer_end) {
2278 ret = http_prepare_data(c);
2282 /* state change requested */
2285 if (c->is_packetized) {
2286 /* RTP data output */
2287 len = c->buffer_end - c->buffer_ptr;
2289 /* fail safe - should never happen */
2291 c->buffer_ptr = c->buffer_end;
2294 len = (c->buffer_ptr[0] << 24) |
2295 (c->buffer_ptr[1] << 16) |
2296 (c->buffer_ptr[2] << 8) |
2298 if (len > (c->buffer_end - c->buffer_ptr))
2300 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2301 /* nothing to send yet: we can wait */
2305 c->data_count += len;
2306 update_datarate(&c->datarate, c->data_count);
2308 c->stream->bytes_served += len;
2310 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2311 /* RTP packets are sent inside the RTSP TCP connection */
2313 int interleaved_index, size;
2315 HTTPContext *rtsp_c;
2318 /* if no RTSP connection left, error */
2321 /* if already sending something, then wait. */
2322 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2324 if (url_open_dyn_buf(&pb) < 0)
2326 interleaved_index = c->packet_stream_index * 2;
2327 /* RTCP packets are sent at odd indexes */
2328 if (c->buffer_ptr[1] == 200)
2329 interleaved_index++;
2330 /* write RTSP TCP header */
2332 header[1] = interleaved_index;
2333 header[2] = len >> 8;
2335 put_buffer(pb, header, 4);
2336 /* write RTP packet data */
2338 put_buffer(pb, c->buffer_ptr, len);
2339 size = url_close_dyn_buf(pb, &c->packet_buffer);
2340 /* prepare asynchronous TCP sending */
2341 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2342 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2343 c->buffer_ptr += len;
2345 /* send everything we can NOW */
2346 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2347 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2349 rtsp_c->packet_buffer_ptr += len;
2350 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2351 /* if we could not send all the data, we will
2352 send it later, so a new state is needed to
2353 "lock" the RTSP TCP connection */
2354 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2357 /* all data has been sent */
2358 av_freep(&c->packet_buffer);
2360 /* send RTP packet directly in UDP */
2362 url_write(c->rtp_handles[c->packet_stream_index],
2363 c->buffer_ptr, len);
2364 c->buffer_ptr += len;
2365 /* here we continue as we can send several packets per 10 ms slot */
2368 /* TCP data output */
2369 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2371 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2372 ff_neterrno() != FF_NETERROR(EINTR))
2373 /* error : close connection */
2378 c->buffer_ptr += len;
2380 c->data_count += len;
2381 update_datarate(&c->datarate, c->data_count);
2383 c->stream->bytes_served += len;
2391 static int http_start_receive_data(HTTPContext *c)
2395 if (c->stream->feed_opened)
2398 /* Don't permit writing to this one */
2399 if (c->stream->readonly)
2403 fd = open(c->stream->feed_filename, O_RDWR);
2405 http_log("Error opening feeder file: %s\n", strerror(errno));
2410 c->stream->feed_write_index = ffm_read_write_index(fd);
2411 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2412 lseek(fd, 0, SEEK_SET);
2414 /* init buffer input */
2415 c->buffer_ptr = c->buffer;
2416 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2417 c->stream->feed_opened = 1;
2421 static int http_receive_data(HTTPContext *c)
2425 if (c->buffer_end > c->buffer_ptr) {
2428 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2430 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2431 ff_neterrno() != FF_NETERROR(EINTR))
2432 /* error : close connection */
2434 } else if (len == 0)
2435 /* end of connection : close it */
2438 c->buffer_ptr += len;
2439 c->data_count += len;
2440 update_datarate(&c->datarate, c->data_count);
2444 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2445 if (c->buffer[0] != 'f' ||
2446 c->buffer[1] != 'm') {
2447 http_log("Feed stream has become desynchronized -- disconnecting\n");
2452 if (c->buffer_ptr >= c->buffer_end) {
2453 FFStream *feed = c->stream;
2454 /* a packet has been received : write it in the store, except
2456 if (c->data_count > FFM_PACKET_SIZE) {
2458 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2459 /* XXX: use llseek or url_seek */
2460 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2461 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2462 http_log("Error writing to feed file: %s\n", strerror(errno));
2466 feed->feed_write_index += FFM_PACKET_SIZE;
2467 /* update file size */
2468 if (feed->feed_write_index > c->stream->feed_size)
2469 feed->feed_size = feed->feed_write_index;
2471 /* handle wrap around if max file size reached */
2472 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2473 feed->feed_write_index = FFM_PACKET_SIZE;
2476 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2478 /* wake up any waiting connections */
2479 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2480 if (c1->state == HTTPSTATE_WAIT_FEED &&
2481 c1->stream->feed == c->stream->feed)
2482 c1->state = HTTPSTATE_SEND_DATA;
2485 /* We have a header in our hands that contains useful data */
2486 AVFormatContext *s = NULL;
2488 AVInputFormat *fmt_in;
2491 /* use feed output format name to find corresponding input format */
2492 fmt_in = av_find_input_format(feed->fmt->name);
2496 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2497 pb->is_streamed = 1;
2499 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2504 /* Now we have the actual streams */
2505 if (s->nb_streams != feed->nb_streams) {
2506 av_close_input_stream(s);
2511 for (i = 0; i < s->nb_streams; i++) {
2512 AVStream *fst = feed->streams[i];
2513 AVStream *st = s->streams[i];
2514 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2515 if (fst->codec->extradata_size) {
2516 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2517 if (!fst->codec->extradata)
2519 memcpy(fst->codec->extradata, st->codec->extradata,
2520 fst->codec->extradata_size);
2524 av_close_input_stream(s);
2527 c->buffer_ptr = c->buffer;
2532 c->stream->feed_opened = 0;
2534 /* wake up any waiting connections to stop waiting for feed */
2535 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2536 if (c1->state == HTTPSTATE_WAIT_FEED &&
2537 c1->stream->feed == c->stream->feed)
2538 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2543 /********************************************************************/
2546 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2553 switch(error_number) {
2554 case RTSP_STATUS_OK:
2557 case RTSP_STATUS_METHOD:
2558 str = "Method Not Allowed";
2560 case RTSP_STATUS_BANDWIDTH:
2561 str = "Not Enough Bandwidth";
2563 case RTSP_STATUS_SESSION:
2564 str = "Session Not Found";
2566 case RTSP_STATUS_STATE:
2567 str = "Method Not Valid in This State";
2569 case RTSP_STATUS_AGGREGATE:
2570 str = "Aggregate operation not allowed";
2572 case RTSP_STATUS_ONLY_AGGREGATE:
2573 str = "Only aggregate operation allowed";
2575 case RTSP_STATUS_TRANSPORT:
2576 str = "Unsupported transport";
2578 case RTSP_STATUS_INTERNAL:
2579 str = "Internal Server Error";
2581 case RTSP_STATUS_SERVICE:
2582 str = "Service Unavailable";
2584 case RTSP_STATUS_VERSION:
2585 str = "RTSP Version not supported";
2588 str = "Unknown Error";
2592 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2593 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2595 /* output GMT time */
2599 p = buf2 + strlen(p) - 1;
2602 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2605 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2607 rtsp_reply_header(c, error_number);
2608 url_fprintf(c->pb, "\r\n");
2611 static int rtsp_parse_request(HTTPContext *c)
2613 const char *p, *p1, *p2;
2619 RTSPHeader header1, *header = &header1;
2621 c->buffer_ptr[0] = '\0';
2624 get_word(cmd, sizeof(cmd), &p);
2625 get_word(url, sizeof(url), &p);
2626 get_word(protocol, sizeof(protocol), &p);
2628 av_strlcpy(c->method, cmd, sizeof(c->method));
2629 av_strlcpy(c->url, url, sizeof(c->url));
2630 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2632 if (url_open_dyn_buf(&c->pb) < 0) {
2633 /* XXX: cannot do more */
2634 c->pb = NULL; /* safety */
2638 /* check version name */
2639 if (strcmp(protocol, "RTSP/1.0") != 0) {
2640 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2644 /* parse each header line */
2645 memset(header, 0, sizeof(RTSPHeader));
2646 /* skip to next line */
2647 while (*p != '\n' && *p != '\0')
2651 while (*p != '\0') {
2652 p1 = strchr(p, '\n');
2656 if (p2 > p && p2[-1] == '\r')
2658 /* skip empty line */
2662 if (len > sizeof(line) - 1)
2663 len = sizeof(line) - 1;
2664 memcpy(line, p, len);
2666 rtsp_parse_line(header, line);
2670 /* handle sequence number */
2671 c->seq = header->seq;
2673 if (!strcmp(cmd, "DESCRIBE"))
2674 rtsp_cmd_describe(c, url);
2675 else if (!strcmp(cmd, "OPTIONS"))
2676 rtsp_cmd_options(c, url);
2677 else if (!strcmp(cmd, "SETUP"))
2678 rtsp_cmd_setup(c, url, header);
2679 else if (!strcmp(cmd, "PLAY"))
2680 rtsp_cmd_play(c, url, header);
2681 else if (!strcmp(cmd, "PAUSE"))
2682 rtsp_cmd_pause(c, url, header);
2683 else if (!strcmp(cmd, "TEARDOWN"))
2684 rtsp_cmd_teardown(c, url, header);
2686 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2689 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2690 c->pb = NULL; /* safety */
2692 /* XXX: cannot do more */
2695 c->buffer_ptr = c->pb_buffer;
2696 c->buffer_end = c->pb_buffer + len;
2697 c->state = RTSPSTATE_SEND_REPLY;
2701 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2702 struct in_addr my_ip)
2704 AVFormatContext *avc;
2705 AVStream avs[MAX_STREAMS];
2708 avc = av_alloc_format_context();
2712 if (stream->title[0] != 0) {
2713 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2715 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2717 avc->nb_streams = stream->nb_streams;
2718 if (stream->is_multicast) {
2719 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2720 inet_ntoa(stream->multicast_ip),
2721 stream->multicast_port, stream->multicast_ttl);
2724 for(i = 0; i < stream->nb_streams; i++) {
2725 avc->streams[i] = &avs[i];
2726 avc->streams[i]->codec = stream->streams[i]->codec;
2728 *pbuffer = av_mallocz(2048);
2729 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2732 return strlen(*pbuffer);
2735 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2737 // rtsp_reply_header(c, RTSP_STATUS_OK);
2738 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2739 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2740 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2741 url_fprintf(c->pb, "\r\n");
2744 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2750 int content_length, len;
2751 struct sockaddr_in my_addr;
2753 /* find which url is asked */
2754 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2759 for(stream = first_stream; stream != NULL; stream = stream->next) {
2760 if (!stream->is_feed &&
2761 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2762 !strcmp(path, stream->filename)) {
2766 /* no stream found */
2767 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2771 /* prepare the media description in sdp format */
2773 /* get the host IP */
2774 len = sizeof(my_addr);
2775 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2776 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2777 if (content_length < 0) {
2778 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2781 rtsp_reply_header(c, RTSP_STATUS_OK);
2782 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2783 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2784 url_fprintf(c->pb, "\r\n");
2785 put_buffer(c->pb, content, content_length);
2788 static HTTPContext *find_rtp_session(const char *session_id)
2792 if (session_id[0] == '\0')
2795 for(c = first_http_ctx; c != NULL; c = c->next) {
2796 if (!strcmp(c->session_id, session_id))
2802 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPLowerTransport lower_transport)
2804 RTSPTransportField *th;
2807 for(i=0;i<h->nb_transports;i++) {
2808 th = &h->transports[i];
2809 if (th->lower_transport == lower_transport)
2815 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2819 int stream_index, port;
2824 RTSPTransportField *th;
2825 struct sockaddr_in dest_addr;
2826 RTSPActionServerSetup setup;
2828 /* find which url is asked */
2829 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2834 /* now check each stream */
2835 for(stream = first_stream; stream != NULL; stream = stream->next) {
2836 if (!stream->is_feed &&
2837 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2838 /* accept aggregate filenames only if single stream */
2839 if (!strcmp(path, stream->filename)) {
2840 if (stream->nb_streams != 1) {
2841 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2848 for(stream_index = 0; stream_index < stream->nb_streams;
2850 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2851 stream->filename, stream_index);
2852 if (!strcmp(path, buf))
2857 /* no stream found */
2858 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2862 /* generate session id if needed */
2863 if (h->session_id[0] == '\0')
2864 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2865 av_random(&random_state), av_random(&random_state));
2867 /* find rtp session, and create it if none found */
2868 rtp_c = find_rtp_session(h->session_id);
2870 /* always prefer UDP */
2871 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
2873 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
2875 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2880 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2881 th->lower_transport);
2883 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2887 /* open input stream */
2888 if (open_input_stream(rtp_c, "") < 0) {
2889 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2894 /* test if stream is OK (test needed because several SETUP needs
2895 to be done for a given file) */
2896 if (rtp_c->stream != stream) {
2897 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2901 /* test if stream is already set up */
2902 if (rtp_c->rtp_ctx[stream_index]) {
2903 rtsp_reply_error(c, RTSP_STATUS_STATE);
2907 /* check transport */
2908 th = find_transport(h, rtp_c->rtp_protocol);
2909 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
2910 th->client_port_min <= 0)) {
2911 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2915 /* setup default options */
2916 setup.transport_option[0] = '\0';
2917 dest_addr = rtp_c->from_addr;
2918 dest_addr.sin_port = htons(th->client_port_min);
2921 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2922 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2926 /* now everything is OK, so we can send the connection parameters */
2927 rtsp_reply_header(c, RTSP_STATUS_OK);
2929 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2931 switch(rtp_c->rtp_protocol) {
2932 case RTSP_LOWER_TRANSPORT_UDP:
2933 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2934 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2935 "client_port=%d-%d;server_port=%d-%d",
2936 th->client_port_min, th->client_port_min + 1,
2939 case RTSP_LOWER_TRANSPORT_TCP:
2940 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2941 stream_index * 2, stream_index * 2 + 1);
2946 if (setup.transport_option[0] != '\0')
2947 url_fprintf(c->pb, ";%s", setup.transport_option);
2948 url_fprintf(c->pb, "\r\n");
2951 url_fprintf(c->pb, "\r\n");
2955 /* find an rtp connection by using the session ID. Check consistency
2957 static HTTPContext *find_rtp_session_with_url(const char *url,
2958 const char *session_id)
2966 rtp_c = find_rtp_session(session_id);
2970 /* find which url is asked */
2971 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2975 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2976 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2977 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2978 rtp_c->stream->filename, s);
2979 if(!strncmp(path, buf, sizeof(buf))) {
2980 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2987 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2991 rtp_c = find_rtp_session_with_url(url, h->session_id);
2993 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2997 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2998 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2999 rtp_c->state != HTTPSTATE_READY) {
3000 rtsp_reply_error(c, RTSP_STATUS_STATE);
3005 /* XXX: seek in stream */
3006 if (h->range_start != AV_NOPTS_VALUE) {
3007 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3008 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3012 rtp_c->state = HTTPSTATE_SEND_DATA;
3014 /* now everything is OK, so we can send the connection parameters */
3015 rtsp_reply_header(c, RTSP_STATUS_OK);
3017 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3018 url_fprintf(c->pb, "\r\n");
3021 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3025 rtp_c = find_rtp_session_with_url(url, h->session_id);
3027 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3031 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3032 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3033 rtsp_reply_error(c, RTSP_STATUS_STATE);
3037 rtp_c->state = HTTPSTATE_READY;
3038 rtp_c->first_pts = AV_NOPTS_VALUE;
3039 /* now everything is OK, so we can send the connection parameters */
3040 rtsp_reply_header(c, RTSP_STATUS_OK);
3042 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3043 url_fprintf(c->pb, "\r\n");
3046 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3049 char session_id[32];
3051 rtp_c = find_rtp_session_with_url(url, h->session_id);
3053 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3057 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3059 /* abort the session */
3060 close_connection(rtp_c);
3062 /* now everything is OK, so we can send the connection parameters */
3063 rtsp_reply_header(c, RTSP_STATUS_OK);
3065 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3066 url_fprintf(c->pb, "\r\n");
3070 /********************************************************************/
3073 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3074 FFStream *stream, const char *session_id,
3075 enum RTSPLowerTransport rtp_protocol)
3077 HTTPContext *c = NULL;
3078 const char *proto_str;
3080 /* XXX: should output a warning page when coming
3081 close to the connection limit */
3082 if (nb_connections >= nb_max_connections)
3085 /* add a new connection */
3086 c = av_mallocz(sizeof(HTTPContext));
3091 c->poll_entry = NULL;
3092 c->from_addr = *from_addr;
3093 c->buffer_size = IOBUFFER_INIT_SIZE;
3094 c->buffer = av_malloc(c->buffer_size);
3099 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3100 c->state = HTTPSTATE_READY;
3101 c->is_packetized = 1;
3102 c->rtp_protocol = rtp_protocol;
3104 /* protocol is shown in statistics */
3105 switch(c->rtp_protocol) {
3106 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3107 proto_str = "MCAST";
3109 case RTSP_LOWER_TRANSPORT_UDP:
3112 case RTSP_LOWER_TRANSPORT_TCP:
3119 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3120 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3122 current_bandwidth += stream->bandwidth;
3124 c->next = first_http_ctx;
3136 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3137 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3139 static int rtp_new_av_stream(HTTPContext *c,
3140 int stream_index, struct sockaddr_in *dest_addr,
3141 HTTPContext *rtsp_c)
3143 AVFormatContext *ctx;
3146 URLContext *h = NULL;
3148 int max_packet_size;
3150 /* now we can open the relevant output stream */
3151 ctx = av_alloc_format_context();
3154 ctx->oformat = guess_format("rtp", NULL, NULL);
3156 st = av_mallocz(sizeof(AVStream));
3159 st->codec= avcodec_alloc_context();
3160 ctx->nb_streams = 1;
3161 ctx->streams[0] = st;
3163 if (!c->stream->feed ||
3164 c->stream->feed == c->stream)
3165 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3168 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3170 st->priv_data = NULL;
3172 /* build destination RTP address */
3173 ipaddr = inet_ntoa(dest_addr->sin_addr);
3175 switch(c->rtp_protocol) {
3176 case RTSP_LOWER_TRANSPORT_UDP:
3177 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3180 /* XXX: also pass as parameter to function ? */
3181 if (c->stream->is_multicast) {
3183 ttl = c->stream->multicast_ttl;
3186 snprintf(ctx->filename, sizeof(ctx->filename),
3187 "rtp://%s:%d?multicast=1&ttl=%d",
3188 ipaddr, ntohs(dest_addr->sin_port), ttl);
3190 snprintf(ctx->filename, sizeof(ctx->filename),
3191 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3194 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3196 c->rtp_handles[stream_index] = h;
3197 max_packet_size = url_get_max_packet_size(h);
3199 case RTSP_LOWER_TRANSPORT_TCP:
3202 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3208 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3209 ipaddr, ntohs(dest_addr->sin_port),
3210 c->stream->filename, stream_index, c->protocol);
3212 /* normally, no packets should be output here, but the packet size may be checked */
3213 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3214 /* XXX: close stream */
3217 av_set_parameters(ctx, NULL);
3218 if (av_write_header(ctx) < 0) {
3225 url_close_dyn_buf(ctx->pb, &dummy_buf);
3228 c->rtp_ctx[stream_index] = ctx;
3232 /********************************************************************/
3233 /* ffserver initialization */
3235 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3239 fst = av_mallocz(sizeof(AVStream));
3242 fst->codec= avcodec_alloc_context();
3243 fst->priv_data = av_mallocz(sizeof(FeedData));
3244 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3245 fst->index = stream->nb_streams;
3246 av_set_pts_info(fst, 33, 1, 90000);
3247 stream->streams[stream->nb_streams++] = fst;
3251 /* return the stream number in the feed */
3252 static int add_av_stream(FFStream *feed, AVStream *st)
3255 AVCodecContext *av, *av1;
3259 for(i=0;i<feed->nb_streams;i++) {
3260 st = feed->streams[i];
3262 if (av1->codec_id == av->codec_id &&
3263 av1->codec_type == av->codec_type &&
3264 av1->bit_rate == av->bit_rate) {
3266 switch(av->codec_type) {
3267 case CODEC_TYPE_AUDIO:
3268 if (av1->channels == av->channels &&
3269 av1->sample_rate == av->sample_rate)
3272 case CODEC_TYPE_VIDEO:
3273 if (av1->width == av->width &&
3274 av1->height == av->height &&
3275 av1->time_base.den == av->time_base.den &&
3276 av1->time_base.num == av->time_base.num &&
3277 av1->gop_size == av->gop_size)
3286 fst = add_av_stream1(feed, av);
3289 return feed->nb_streams - 1;
3294 static void remove_stream(FFStream *stream)
3298 while (*ps != NULL) {
3306 /* specific mpeg4 handling : we extract the raw parameters */
3307 static void extract_mpeg4_header(AVFormatContext *infile)
3309 int mpeg4_count, i, size;
3315 for(i=0;i<infile->nb_streams;i++) {
3316 st = infile->streams[i];
3317 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3318 st->codec->extradata_size == 0) {
3325 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3326 while (mpeg4_count > 0) {
3327 if (av_read_packet(infile, &pkt) < 0)
3329 st = infile->streams[pkt.stream_index];
3330 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3331 st->codec->extradata_size == 0) {
3332 av_freep(&st->codec->extradata);
3333 /* fill extradata with the header */
3334 /* XXX: we make hard suppositions here ! */
3336 while (p < pkt.data + pkt.size - 4) {
3337 /* stop when vop header is found */
3338 if (p[0] == 0x00 && p[1] == 0x00 &&
3339 p[2] == 0x01 && p[3] == 0xb6) {
3340 size = p - pkt.data;
3341 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3342 st->codec->extradata = av_malloc(size);
3343 st->codec->extradata_size = size;
3344 memcpy(st->codec->extradata, pkt.data, size);
3351 av_free_packet(&pkt);
3355 /* compute the needed AVStream for each file */
3356 static void build_file_streams(void)
3358 FFStream *stream, *stream_next;
3359 AVFormatContext *infile;
3362 /* gather all streams */
3363 for(stream = first_stream; stream != NULL; stream = stream_next) {
3364 stream_next = stream->next;
3365 if (stream->stream_type == STREAM_TYPE_LIVE &&
3367 /* the stream comes from a file */
3368 /* try to open the file */
3370 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3371 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3372 /* specific case : if transport stream output to RTP,
3373 we use a raw transport stream reader */
3374 stream->ap_in->mpeg2ts_raw = 1;
3375 stream->ap_in->mpeg2ts_compute_pcr = 1;
3378 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3379 stream->ifmt, 0, stream->ap_in)) < 0) {
3380 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3381 /* remove stream (no need to spend more time on it) */
3383 remove_stream(stream);
3385 /* find all the AVStreams inside and reference them in
3387 if (av_find_stream_info(infile) < 0) {
3388 http_log("Could not find codec parameters from '%s'\n",
3389 stream->feed_filename);
3390 av_close_input_file(infile);
3393 extract_mpeg4_header(infile);
3395 for(i=0;i<infile->nb_streams;i++)
3396 add_av_stream1(stream, infile->streams[i]->codec);
3398 av_close_input_file(infile);
3404 /* compute the needed AVStream for each feed */
3405 static void build_feed_streams(void)
3407 FFStream *stream, *feed;
3410 /* gather all streams */
3411 for(stream = first_stream; stream != NULL; stream = stream->next) {
3412 feed = stream->feed;
3414 if (!stream->is_feed) {
3415 /* we handle a stream coming from a feed */
3416 for(i=0;i<stream->nb_streams;i++)
3417 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3422 /* gather all streams */
3423 for(stream = first_stream; stream != NULL; stream = stream->next) {
3424 feed = stream->feed;
3426 if (stream->is_feed) {
3427 for(i=0;i<stream->nb_streams;i++)
3428 stream->feed_streams[i] = i;
3433 /* create feed files if needed */
3434 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3437 if (url_exist(feed->feed_filename)) {
3438 /* See if it matches */
3442 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3443 /* Now see if it matches */
3444 if (s->nb_streams == feed->nb_streams) {
3446 for(i=0;i<s->nb_streams;i++) {
3448 sf = feed->streams[i];
3451 if (sf->index != ss->index ||
3453 http_log("Index & Id do not match for stream %d (%s)\n",
3454 i, feed->feed_filename);
3457 AVCodecContext *ccf, *ccs;
3461 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3463 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3464 http_log("Codecs do not match for stream %d\n", i);
3466 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3467 http_log("Codec bitrates do not match for stream %d\n", i);
3469 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3470 if (CHECK_CODEC(time_base.den) ||
3471 CHECK_CODEC(time_base.num) ||
3472 CHECK_CODEC(width) ||
3473 CHECK_CODEC(height)) {
3474 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3477 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3478 if (CHECK_CODEC(sample_rate) ||
3479 CHECK_CODEC(channels) ||
3480 CHECK_CODEC(frame_size)) {
3481 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3485 http_log("Unknown codec type\n");
3493 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3494 feed->feed_filename, s->nb_streams, feed->nb_streams);
3496 av_close_input_file(s);
3498 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3499 feed->feed_filename);
3502 if (feed->readonly) {
3503 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3504 feed->feed_filename);
3507 unlink(feed->feed_filename);
3510 if (!url_exist(feed->feed_filename)) {
3511 AVFormatContext s1 = {0}, *s = &s1;
3513 if (feed->readonly) {
3514 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3515 feed->feed_filename);
3519 /* only write the header of the ffm file */
3520 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3521 http_log("Could not open output feed file '%s'\n",
3522 feed->feed_filename);
3525 s->oformat = feed->fmt;
3526 s->nb_streams = feed->nb_streams;
3527 for(i=0;i<s->nb_streams;i++) {
3529 st = feed->streams[i];
3532 av_set_parameters(s, NULL);
3533 if (av_write_header(s) < 0) {
3534 http_log("Container doesn't supports the required parameters\n");
3537 /* XXX: need better api */
3538 av_freep(&s->priv_data);
3541 /* get feed size and write index */
3542 fd = open(feed->feed_filename, O_RDONLY);
3544 http_log("Could not open output feed file '%s'\n",
3545 feed->feed_filename);
3549 feed->feed_write_index = ffm_read_write_index(fd);
3550 feed->feed_size = lseek(fd, 0, SEEK_END);
3551 /* ensure that we do not wrap before the end of file */
3552 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3553 feed->feed_max_size = feed->feed_size;
3559 /* compute the bandwidth used by each stream */
3560 static void compute_bandwidth(void)
3566 for(stream = first_stream; stream != NULL; stream = stream->next) {
3568 for(i=0;i<stream->nb_streams;i++) {
3569 AVStream *st = stream->streams[i];
3570 switch(st->codec->codec_type) {
3571 case CODEC_TYPE_AUDIO:
3572 case CODEC_TYPE_VIDEO:
3573 bandwidth += st->codec->bit_rate;
3579 stream->bandwidth = (bandwidth + 999) / 1000;
3583 static void get_arg(char *buf, int buf_size, const char **pp)
3590 while (isspace(*p)) p++;
3593 if (*p == '\"' || *p == '\'')
3605 if ((q - buf) < buf_size - 1)
3610 if (quote && *p == quote)
3615 /* add a codec and set the default parameters */
3616 static void add_codec(FFStream *stream, AVCodecContext *av)
3620 /* compute default parameters */
3621 switch(av->codec_type) {
3622 case CODEC_TYPE_AUDIO:
3623 if (av->bit_rate == 0)
3624 av->bit_rate = 64000;
3625 if (av->sample_rate == 0)
3626 av->sample_rate = 22050;
3627 if (av->channels == 0)
3630 case CODEC_TYPE_VIDEO:
3631 if (av->bit_rate == 0)
3632 av->bit_rate = 64000;
3633 if (av->time_base.num == 0){
3634 av->time_base.den = 5;
3635 av->time_base.num = 1;
3637 if (av->width == 0 || av->height == 0) {
3641 /* Bitrate tolerance is less for streaming */
3642 if (av->bit_rate_tolerance == 0)
3643 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3644 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3649 if (av->max_qdiff == 0)
3651 av->qcompress = 0.5;
3654 if (!av->nsse_weight)
3655 av->nsse_weight = 8;
3657 av->frame_skip_cmp = FF_CMP_DCTMAX;
3658 av->me_method = ME_EPZS;
3659 av->rc_buffer_aggressivity = 1.0;
3662 av->rc_eq = "tex^qComp";
3663 if (!av->i_quant_factor)
3664 av->i_quant_factor = -0.8;
3665 if (!av->b_quant_factor)
3666 av->b_quant_factor = 1.25;
3667 if (!av->b_quant_offset)
3668 av->b_quant_offset = 1.25;
3669 if (!av->rc_max_rate)
3670 av->rc_max_rate = av->bit_rate * 2;
3672 if (av->rc_max_rate && !av->rc_buffer_size) {
3673 av->rc_buffer_size = av->rc_max_rate;
3682 st = av_mallocz(sizeof(AVStream));
3685 st->codec = avcodec_alloc_context();
3686 stream->streams[stream->nb_streams++] = st;
3687 memcpy(st->codec, av, sizeof(AVCodecContext));
3690 static enum CodecID opt_audio_codec(const char *arg)
3692 AVCodec *p= avcodec_find_encoder_by_name(arg);
3694 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3695 return CODEC_ID_NONE;
3700 static enum CodecID opt_video_codec(const char *arg)
3702 AVCodec *p= avcodec_find_encoder_by_name(arg);
3704 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3705 return CODEC_ID_NONE;
3710 /* simplistic plugin support */
3713 static void load_module(const char *filename)
3716 void (*init_func)(void);
3717 dll = dlopen(filename, RTLD_NOW);
3719 fprintf(stderr, "Could not load module '%s' - %s\n",
3720 filename, dlerror());
3724 init_func = dlsym(dll, "ffserver_module_init");
3727 "%s: init function 'ffserver_module_init()' not found\n",
3736 static int ffserver_opt_default(const char *opt, const char *arg,
3737 AVCodecContext *avctx, int type)
3740 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
3742 ret = av_set_string3(avctx, opt, arg, 1, NULL);
3746 static int parse_ffconfig(const char *filename)
3753 int val, errors, line_num;
3754 FFStream **last_stream, *stream, *redirect;
3755 FFStream **last_feed, *feed;
3756 AVCodecContext audio_enc, video_enc;
3757 enum CodecID audio_id, video_id;
3759 f = fopen(filename, "r");
3767 first_stream = NULL;
3768 last_stream = &first_stream;
3770 last_feed = &first_feed;
3774 audio_id = CODEC_ID_NONE;
3775 video_id = CODEC_ID_NONE;
3777 if (fgets(line, sizeof(line), f) == NULL)
3783 if (*p == '\0' || *p == '#')
3786 get_arg(cmd, sizeof(cmd), &p);
3788 if (!strcasecmp(cmd, "Port")) {
3789 get_arg(arg, sizeof(arg), &p);
3791 if (val < 1 || val > 65536) {
3792 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3793 filename, line_num, arg);
3796 my_http_addr.sin_port = htons(val);
3797 } else if (!strcasecmp(cmd, "BindAddress")) {
3798 get_arg(arg, sizeof(arg), &p);
3799 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3800 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3801 filename, line_num, arg);
3804 } else if (!strcasecmp(cmd, "NoDaemon")) {
3805 ffserver_daemon = 0;
3806 } else if (!strcasecmp(cmd, "RTSPPort")) {
3807 get_arg(arg, sizeof(arg), &p);
3809 if (val < 1 || val > 65536) {
3810 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3811 filename, line_num, arg);
3814 my_rtsp_addr.sin_port = htons(atoi(arg));
3815 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3816 get_arg(arg, sizeof(arg), &p);
3817 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3818 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3819 filename, line_num, arg);
3822 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
3823 get_arg(arg, sizeof(arg), &p);
3825 if (val < 1 || val > 65536) {
3826 fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n",
3827 filename, line_num, arg);
3830 nb_max_http_connections = val;
3831 } else if (!strcasecmp(cmd, "MaxClients")) {
3832 get_arg(arg, sizeof(arg), &p);
3834 if (val < 1 || val > nb_max_http_connections) {
3835 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3836 filename, line_num, arg);
3839 nb_max_connections = val;
3841 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3843 get_arg(arg, sizeof(arg), &p);
3845 if (llval < 10 || llval > 10000000) {
3846 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3847 filename, line_num, arg);
3850 max_bandwidth = llval;
3851 } else if (!strcasecmp(cmd, "CustomLog")) {
3852 if (!ffserver_debug)
3853 get_arg(logfilename, sizeof(logfilename), &p);
3854 } else if (!strcasecmp(cmd, "<Feed")) {
3855 /*********************************************/
3856 /* Feed related options */
3858 if (stream || feed) {
3859 fprintf(stderr, "%s:%d: Already in a tag\n",
3860 filename, line_num);
3862 feed = av_mallocz(sizeof(FFStream));
3863 /* add in stream list */
3864 *last_stream = feed;
3865 last_stream = &feed->next;
3866 /* add in feed list */
3868 last_feed = &feed->next_feed;
3870 get_arg(feed->filename, sizeof(feed->filename), &p);
3871 q = strrchr(feed->filename, '>');
3874 feed->fmt = guess_format("ffm", NULL, NULL);
3875 /* defaut feed file */
3876 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3877 "/tmp/%s.ffm", feed->filename);
3878 feed->feed_max_size = 5 * 1024 * 1024;
3880 feed->feed = feed; /* self feeding :-) */
3882 } else if (!strcasecmp(cmd, "Launch")) {
3886 feed->child_argv = av_mallocz(64 * sizeof(char *));
3888 for (i = 0; i < 62; i++) {
3889 get_arg(arg, sizeof(arg), &p);
3893 feed->child_argv[i] = av_strdup(arg);
3896 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3898 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3900 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3901 inet_ntoa(my_http_addr.sin_addr),
3902 ntohs(my_http_addr.sin_port), feed->filename);
3904 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3906 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3908 } else if (stream) {
3909 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3911 } else if (!strcasecmp(cmd, "File")) {
3913 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3915 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3916 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3921 get_arg(arg, sizeof(arg), &p);
3923 fsize = strtod(p1, &p1);
3924 switch(toupper(*p1)) {
3929 fsize *= 1024 * 1024;
3932 fsize *= 1024 * 1024 * 1024;
3935 feed->feed_max_size = (int64_t)fsize;
3937 } else if (!strcasecmp(cmd, "</Feed>")) {
3939 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3940 filename, line_num);
3944 } else if (!strcasecmp(cmd, "<Stream")) {
3945 /*********************************************/
3946 /* Stream related options */
3948 if (stream || feed) {
3949 fprintf(stderr, "%s:%d: Already in a tag\n",
3950 filename, line_num);
3952 const AVClass *class;
3953 stream = av_mallocz(sizeof(FFStream));
3954 *last_stream = stream;
3955 last_stream = &stream->next;
3957 get_arg(stream->filename, sizeof(stream->filename), &p);
3958 q = strrchr(stream->filename, '>');
3961 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3962 /* fetch avclass so AVOption works
3963 * FIXME try to use avcodec_get_context_defaults2
3964 * without changing defaults too much */
3965 avcodec_get_context_defaults(&video_enc);
3966 class = video_enc.av_class;
3967 memset(&audio_enc, 0, sizeof(AVCodecContext));
3968 memset(&video_enc, 0, sizeof(AVCodecContext));
3969 audio_enc.av_class = class;
3970 video_enc.av_class = class;
3971 audio_id = CODEC_ID_NONE;
3972 video_id = CODEC_ID_NONE;
3974 audio_id = stream->fmt->audio_codec;
3975 video_id = stream->fmt->video_codec;
3978 } else if (!strcasecmp(cmd, "Feed")) {
3979 get_arg(arg, sizeof(arg), &p);
3984 while (sfeed != NULL) {
3985 if (!strcmp(sfeed->filename, arg))
3987 sfeed = sfeed->next_feed;
3990 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3991 filename, line_num, arg);
3993 stream->feed = sfeed;
3995 } else if (!strcasecmp(cmd, "Format")) {
3996 get_arg(arg, sizeof(arg), &p);
3998 if (!strcmp(arg, "status")) {
3999 stream->stream_type = STREAM_TYPE_STATUS;
4002 stream->stream_type = STREAM_TYPE_LIVE;
4003 /* jpeg cannot be used here, so use single frame jpeg */
4004 if (!strcmp(arg, "jpeg"))
4005 strcpy(arg, "mjpeg");
4006 stream->fmt = guess_stream_format(arg, NULL, NULL);
4008 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4009 filename, line_num, arg);
4014 audio_id = stream->fmt->audio_codec;
4015 video_id = stream->fmt->video_codec;
4018 } else if (!strcasecmp(cmd, "InputFormat")) {
4019 get_arg(arg, sizeof(arg), &p);
4021 stream->ifmt = av_find_input_format(arg);
4022 if (!stream->ifmt) {
4023 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4024 filename, line_num, arg);
4027 } else if (!strcasecmp(cmd, "FaviconURL")) {
4028 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4029 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4031 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4032 filename, line_num);
4035 } else if (!strcasecmp(cmd, "Author")) {
4037 get_arg(stream->author, sizeof(stream->author), &p);
4038 } else if (!strcasecmp(cmd, "Comment")) {
4040 get_arg(stream->comment, sizeof(stream->comment), &p);
4041 } else if (!strcasecmp(cmd, "Copyright")) {
4043 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4044 } else if (!strcasecmp(cmd, "Title")) {
4046 get_arg(stream->title, sizeof(stream->title), &p);
4047 } else if (!strcasecmp(cmd, "Preroll")) {
4048 get_arg(arg, sizeof(arg), &p);
4050 stream->prebuffer = atof(arg) * 1000;
4051 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4053 stream->send_on_key = 1;
4054 } else if (!strcasecmp(cmd, "AudioCodec")) {
4055 get_arg(arg, sizeof(arg), &p);
4056 audio_id = opt_audio_codec(arg);
4057 if (audio_id == CODEC_ID_NONE) {
4058 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4059 filename, line_num, arg);
4062 } else if (!strcasecmp(cmd, "VideoCodec")) {
4063 get_arg(arg, sizeof(arg), &p);
4064 video_id = opt_video_codec(arg);
4065 if (video_id == CODEC_ID_NONE) {
4066 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4067 filename, line_num, arg);
4070 } else if (!strcasecmp(cmd, "MaxTime")) {
4071 get_arg(arg, sizeof(arg), &p);
4073 stream->max_time = atof(arg) * 1000;
4074 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4075 get_arg(arg, sizeof(arg), &p);
4077 audio_enc.bit_rate = atoi(arg) * 1000;
4078 } else if (!strcasecmp(cmd, "AudioChannels")) {
4079 get_arg(arg, sizeof(arg), &p);
4081 audio_enc.channels = atoi(arg);
4082 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4083 get_arg(arg, sizeof(arg), &p);
4085 audio_enc.sample_rate = atoi(arg);
4086 } else if (!strcasecmp(cmd, "AudioQuality")) {
4087 get_arg(arg, sizeof(arg), &p);
4089 // audio_enc.quality = atof(arg) * 1000;
4091 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4093 int minrate, maxrate;
4095 get_arg(arg, sizeof(arg), &p);
4097 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4098 video_enc.rc_min_rate = minrate * 1000;
4099 video_enc.rc_max_rate = maxrate * 1000;
4101 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4102 filename, line_num, arg);
4106 } else if (!strcasecmp(cmd, "Debug")) {
4108 get_arg(arg, sizeof(arg), &p);
4109 video_enc.debug = strtol(arg,0,0);
4111 } else if (!strcasecmp(cmd, "Strict")) {
4113 get_arg(arg, sizeof(arg), &p);
4114 video_enc.strict_std_compliance = atoi(arg);
4116 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4118 get_arg(arg, sizeof(arg), &p);
4119 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4121 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4123 get_arg(arg, sizeof(arg), &p);
4124 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4126 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4127 get_arg(arg, sizeof(arg), &p);
4129 video_enc.bit_rate = atoi(arg) * 1000;
4131 } else if (!strcasecmp(cmd, "VideoSize")) {
4132 get_arg(arg, sizeof(arg), &p);
4134 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4135 if ((video_enc.width % 16) != 0 ||
4136 (video_enc.height % 16) != 0) {
4137 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4138 filename, line_num);
4142 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4143 get_arg(arg, sizeof(arg), &p);
4145 AVRational frame_rate;
4146 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4147 fprintf(stderr, "Incorrect frame rate\n");
4150 video_enc.time_base.num = frame_rate.den;
4151 video_enc.time_base.den = frame_rate.num;
4154 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4155 get_arg(arg, sizeof(arg), &p);
4157 video_enc.gop_size = atoi(arg);
4158 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4160 video_enc.gop_size = 1;
4161 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4163 video_enc.mb_decision = FF_MB_DECISION_BITS;
4164 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4166 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4167 video_enc.flags |= CODEC_FLAG_4MV;
4169 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4170 !strcasecmp(cmd, "AVOptionAudio")) {
4172 AVCodecContext *avctx;
4174 get_arg(arg, sizeof(arg), &p);
4175 get_arg(arg2, sizeof(arg2), &p);
4176 if (!strcasecmp(cmd, "AVOptionVideo")) {
4178 type = AV_OPT_FLAG_VIDEO_PARAM;
4181 type = AV_OPT_FLAG_AUDIO_PARAM;
4183 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4184 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4187 } else if (!strcasecmp(cmd, "VideoTag")) {
4188 get_arg(arg, sizeof(arg), &p);
4189 if ((strlen(arg) == 4) && stream)
4190 video_enc.codec_tag = ff_get_fourcc(arg);
4191 } else if (!strcasecmp(cmd, "BitExact")) {
4193 video_enc.flags |= CODEC_FLAG_BITEXACT;
4194 } else if (!strcasecmp(cmd, "DctFastint")) {
4196 video_enc.dct_algo = FF_DCT_FASTINT;
4197 } else if (!strcasecmp(cmd, "IdctSimple")) {
4199 video_enc.idct_algo = FF_IDCT_SIMPLE;
4200 } else if (!strcasecmp(cmd, "Qscale")) {
4201 get_arg(arg, sizeof(arg), &p);
4203 video_enc.flags |= CODEC_FLAG_QSCALE;
4204 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4206 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4207 get_arg(arg, sizeof(arg), &p);
4209 video_enc.max_qdiff = atoi(arg);
4210 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4211 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4212 filename, line_num);
4216 } else if (!strcasecmp(cmd, "VideoQMax")) {
4217 get_arg(arg, sizeof(arg), &p);
4219 video_enc.qmax = atoi(arg);
4220 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4221 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4222 filename, line_num);
4226 } else if (!strcasecmp(cmd, "VideoQMin")) {
4227 get_arg(arg, sizeof(arg), &p);
4229 video_enc.qmin = atoi(arg);
4230 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4231 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4232 filename, line_num);
4236 } else if (!strcasecmp(cmd, "LumaElim")) {
4237 get_arg(arg, sizeof(arg), &p);
4239 video_enc.luma_elim_threshold = atoi(arg);
4240 } else if (!strcasecmp(cmd, "ChromaElim")) {
4241 get_arg(arg, sizeof(arg), &p);
4243 video_enc.chroma_elim_threshold = atoi(arg);
4244 } else if (!strcasecmp(cmd, "LumiMask")) {
4245 get_arg(arg, sizeof(arg), &p);
4247 video_enc.lumi_masking = atof(arg);
4248 } else if (!strcasecmp(cmd, "DarkMask")) {
4249 get_arg(arg, sizeof(arg), &p);
4251 video_enc.dark_masking = atof(arg);
4252 } else if (!strcasecmp(cmd, "NoVideo")) {
4253 video_id = CODEC_ID_NONE;
4254 } else if (!strcasecmp(cmd, "NoAudio")) {
4255 audio_id = CODEC_ID_NONE;
4256 } else if (!strcasecmp(cmd, "ACL")) {
4259 get_arg(arg, sizeof(arg), &p);
4260 if (strcasecmp(arg, "allow") == 0)
4261 acl.action = IP_ALLOW;
4262 else if (strcasecmp(arg, "deny") == 0)
4263 acl.action = IP_DENY;
4265 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4266 filename, line_num, arg);
4270 get_arg(arg, sizeof(arg), &p);
4272 if (resolve_host(&acl.first, arg) != 0) {
4273 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4274 filename, line_num, arg);
4277 acl.last = acl.first;
4279 get_arg(arg, sizeof(arg), &p);
4282 if (resolve_host(&acl.last, arg) != 0) {
4283 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4284 filename, line_num, arg);
4290 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4291 IPAddressACL **naclp = 0;
4297 naclp = &stream->acl;
4301 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4302 filename, line_num);
4308 naclp = &(*naclp)->next;
4313 } else if (!strcasecmp(cmd, "RTSPOption")) {
4314 get_arg(arg, sizeof(arg), &p);
4316 av_freep(&stream->rtsp_option);
4317 stream->rtsp_option = av_strdup(arg);
4319 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4320 get_arg(arg, sizeof(arg), &p);
4322 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4323 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4324 filename, line_num, arg);
4327 stream->is_multicast = 1;
4328 stream->loop = 1; /* default is looping */
4330 } else if (!strcasecmp(cmd, "MulticastPort")) {
4331 get_arg(arg, sizeof(arg), &p);
4333 stream->multicast_port = atoi(arg);
4334 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4335 get_arg(arg, sizeof(arg), &p);
4337 stream->multicast_ttl = atoi(arg);
4338 } else if (!strcasecmp(cmd, "NoLoop")) {
4341 } else if (!strcasecmp(cmd, "</Stream>")) {
4343 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4344 filename, line_num);
4347 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4348 if (audio_id != CODEC_ID_NONE) {
4349 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4350 audio_enc.codec_id = audio_id;
4351 add_codec(stream, &audio_enc);
4353 if (video_id != CODEC_ID_NONE) {
4354 video_enc.codec_type = CODEC_TYPE_VIDEO;
4355 video_enc.codec_id = video_id;
4356 add_codec(stream, &video_enc);
4361 } else if (!strcasecmp(cmd, "<Redirect")) {
4362 /*********************************************/
4364 if (stream || feed || redirect) {
4365 fprintf(stderr, "%s:%d: Already in a tag\n",
4366 filename, line_num);
4369 redirect = av_mallocz(sizeof(FFStream));
4370 *last_stream = redirect;
4371 last_stream = &redirect->next;
4373 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4374 q = strrchr(redirect->filename, '>');
4377 redirect->stream_type = STREAM_TYPE_REDIRECT;
4379 } else if (!strcasecmp(cmd, "URL")) {
4381 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4382 } else if (!strcasecmp(cmd, "</Redirect>")) {
4384 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4385 filename, line_num);
4388 if (!redirect->feed_filename[0]) {
4389 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4390 filename, line_num);
4395 } else if (!strcasecmp(cmd, "LoadModule")) {
4396 get_arg(arg, sizeof(arg), &p);
4400 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4401 filename, line_num, arg);
4405 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4406 filename, line_num, cmd);
4418 static void handle_child_exit(int sig)
4423 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4426 for (feed = first_feed; feed; feed = feed->next) {
4427 if (feed->pid == pid) {
4428 int uptime = time(0) - feed->pid_start;
4431 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4434 /* Turn off any more restarts */
4435 feed->child_argv = 0;
4440 need_to_start_children = 1;
4443 static void opt_debug()
4446 ffserver_daemon = 0;
4447 logfilename[0] = '-';
4450 static void opt_show_help(void)
4452 printf("usage: ffserver [options]\n"
4453 "Hyper fast multi format Audio/Video streaming server\n");
4455 show_help_options(options, "Main options:\n", 0, 0);
4458 static const OptionDef options[] = {
4459 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4460 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4461 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4462 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4463 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4464 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4465 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4469 int main(int argc, char **argv)
4471 struct sigaction sigact;
4477 config_filename = "/etc/ffserver.conf";
4479 my_program_name = argv[0];
4480 my_program_dir = getcwd(0, 0);
4481 ffserver_daemon = 1;
4483 parse_options(argc, argv, options, NULL);
4485 unsetenv("http_proxy"); /* Kill the http_proxy */
4487 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4489 memset(&sigact, 0, sizeof(sigact));
4490 sigact.sa_handler = handle_child_exit;
4491 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4492 sigaction(SIGCHLD, &sigact, 0);
4494 if (parse_ffconfig(config_filename) < 0) {
4495 fprintf(stderr, "Incorrect config file - exiting.\n");
4499 /* open log file if needed */
4500 if (logfilename[0] != '\0') {
4501 if (!strcmp(logfilename, "-"))
4504 logfile = fopen(logfilename, "a");
4505 av_log_set_callback(http_av_log);
4508 build_file_streams();
4510 build_feed_streams();
4512 compute_bandwidth();
4514 /* put the process in background and detach it from its TTY */
4515 if (ffserver_daemon) {
4522 } else if (pid > 0) {
4529 open("/dev/null", O_RDWR);
4530 if (strcmp(logfilename, "-") != 0) {
4540 signal(SIGPIPE, SIG_IGN);
4542 if (ffserver_daemon)
4545 if (http_server() < 0) {
4546 http_log("Could not start server\n");