2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #define _XOPEN_SOURCE 600
26 #define closesocket close
31 /* avformat.h defines LIBAVFORMAT_BUILD, include it before all the other libav* headers which use it */
32 #include "libavformat/avformat.h"
33 #include "libavformat/network.h"
34 #include "libavformat/os_support.h"
35 #include "libavformat/rtpdec.h"
36 #include "libavformat/rtsp.h"
37 #include "libavutil/avstring.h"
38 #include "libavutil/lfg.h"
39 #include "libavutil/random_seed.h"
40 #include "libavutil/intreadwrite.h"
41 #include "libavcodec/opt.h"
45 #include <sys/ioctl.h>
51 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
63 const char program_name[] = "FFserver";
64 const int program_birth_year = 2000;
66 static const OptionDef options[];
69 HTTPSTATE_WAIT_REQUEST,
70 HTTPSTATE_SEND_HEADER,
71 HTTPSTATE_SEND_DATA_HEADER,
72 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
73 HTTPSTATE_SEND_DATA_TRAILER,
74 HTTPSTATE_RECEIVE_DATA,
75 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
78 RTSPSTATE_WAIT_REQUEST,
80 RTSPSTATE_SEND_PACKET,
83 static const char *http_state[] = {
99 #define IOBUFFER_INIT_SIZE 8192
101 /* timeouts are in ms */
102 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
103 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
105 #define SYNC_TIMEOUT (10 * 1000)
107 typedef struct RTSPActionServerSetup {
109 char transport_option[512];
110 } RTSPActionServerSetup;
113 int64_t count1, count2;
114 int64_t time1, time2;
117 /* context associated with one connection */
118 typedef struct HTTPContext {
119 enum HTTPState state;
120 int fd; /* socket file descriptor */
121 struct sockaddr_in from_addr; /* origin */
122 struct pollfd *poll_entry; /* used when polling */
124 uint8_t *buffer_ptr, *buffer_end;
127 struct HTTPContext *next;
128 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
132 /* input format handling */
133 AVFormatContext *fmt_in;
134 int64_t start_time; /* In milliseconds - this wraps fairly often */
135 int64_t first_pts; /* initial pts value */
136 int64_t cur_pts; /* current pts value from the stream in us */
137 int64_t cur_frame_duration; /* duration of the current frame in us */
138 int cur_frame_bytes; /* output frame size, needed to compute
139 the time at which we send each
141 int pts_stream_index; /* stream we choose as clock reference */
142 int64_t cur_clock; /* current clock reference value in us */
143 /* output format handling */
144 struct FFStream *stream;
145 /* -1 is invalid stream */
146 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
147 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
149 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
150 int last_packet_sent; /* true if last data packet was sent */
152 DataRateData datarate;
159 int is_packetized; /* if true, the stream is packetized */
160 int packet_stream_index; /* current stream for output in state machine */
162 /* RTSP state specific */
163 uint8_t *pb_buffer; /* XXX: use that in all the code */
165 int seq; /* RTSP sequence number */
167 /* RTP state specific */
168 enum RTSPLowerTransport rtp_protocol;
169 char session_id[32]; /* session id */
170 AVFormatContext *rtp_ctx[MAX_STREAMS];
172 /* RTP/UDP specific */
173 URLContext *rtp_handles[MAX_STREAMS];
175 /* RTP/TCP specific */
176 struct HTTPContext *rtsp_c;
177 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
180 /* each generated stream is described here */
184 STREAM_TYPE_REDIRECT,
187 enum IPAddressAction {
192 typedef struct IPAddressACL {
193 struct IPAddressACL *next;
194 enum IPAddressAction action;
195 /* These are in host order */
196 struct in_addr first;
200 /* description of each stream of the ffserver.conf file */
201 typedef struct FFStream {
202 enum StreamType stream_type;
203 char filename[1024]; /* stream filename */
204 struct FFStream *feed; /* feed we are using (can be null if
206 AVFormatParameters *ap_in; /* input parameters */
207 AVInputFormat *ifmt; /* if non NULL, force input format */
211 int prebuffer; /* Number of millseconds early to start */
212 int64_t max_time; /* Number of milliseconds to run */
214 AVStream *streams[MAX_STREAMS];
215 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
216 char feed_filename[1024]; /* file name of the feed storage, or
217 input file name for a stream */
222 pid_t pid; /* Of ffmpeg process */
223 time_t pid_start; /* Of ffmpeg process */
225 struct FFStream *next;
226 unsigned bandwidth; /* bandwidth, in kbits/s */
229 /* multicast specific */
231 struct in_addr multicast_ip;
232 int multicast_port; /* first port used for multicast */
234 int loop; /* if true, send the stream in loops (only meaningful if file) */
237 int feed_opened; /* true if someone is writing to the feed */
238 int is_feed; /* true if it is a feed */
239 int readonly; /* True if writing is prohibited to the file */
241 int64_t bytes_served;
242 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
243 int64_t feed_write_index; /* current write position in feed (it wraps around) */
244 int64_t feed_size; /* current size of feed */
245 struct FFStream *next_feed;
248 typedef struct FeedData {
249 long long data_count;
250 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
253 static struct sockaddr_in my_http_addr;
254 static struct sockaddr_in my_rtsp_addr;
256 static char logfilename[1024];
257 static HTTPContext *first_http_ctx;
258 static FFStream *first_feed; /* contains only feeds */
259 static FFStream *first_stream; /* contains all streams, including feeds */
261 static void new_connection(int server_fd, int is_rtsp);
262 static void close_connection(HTTPContext *c);
265 static int handle_connection(HTTPContext *c);
266 static int http_parse_request(HTTPContext *c);
267 static int http_send_data(HTTPContext *c);
268 static void compute_status(HTTPContext *c);
269 static int open_input_stream(HTTPContext *c, const char *info);
270 static int http_start_receive_data(HTTPContext *c);
271 static int http_receive_data(HTTPContext *c);
274 static int rtsp_parse_request(HTTPContext *c);
275 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
276 static void rtsp_cmd_options(HTTPContext *c, const char *url);
277 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
278 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
279 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
280 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
283 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
284 struct in_addr my_ip);
287 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
288 FFStream *stream, const char *session_id,
289 enum RTSPLowerTransport rtp_protocol);
290 static int rtp_new_av_stream(HTTPContext *c,
291 int stream_index, struct sockaddr_in *dest_addr,
292 HTTPContext *rtsp_c);
294 static const char *my_program_name;
295 static const char *my_program_dir;
297 static const char *config_filename;
298 static int ffserver_debug;
299 static int ffserver_daemon;
300 static int no_launch;
301 static int need_to_start_children;
303 /* maximum number of simultaneous HTTP connections */
304 static unsigned int nb_max_http_connections = 2000;
305 static unsigned int nb_max_connections = 5;
306 static unsigned int nb_connections;
308 static uint64_t max_bandwidth = 1000;
309 static uint64_t current_bandwidth;
311 static int64_t cur_time; // Making this global saves on passing it around everywhere
313 static AVLFG random_state;
315 static FILE *logfile = NULL;
317 static char *ctime1(char *buf2)
325 p = buf2 + strlen(p) - 1;
331 static void http_vlog(const char *fmt, va_list vargs)
333 static int print_prefix = 1;
338 fprintf(logfile, "%s ", buf);
340 print_prefix = strstr(fmt, "\n") != NULL;
341 vfprintf(logfile, fmt, vargs);
346 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
349 va_start(vargs, fmt);
350 http_vlog(fmt, vargs);
354 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
356 static int print_prefix = 1;
357 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
358 if (level > av_log_get_level())
360 if (print_prefix && avc)
361 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
362 print_prefix = strstr(fmt, "\n") != NULL;
363 http_vlog(fmt, vargs);
366 static void log_connection(HTTPContext *c)
371 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
372 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
373 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
376 static void update_datarate(DataRateData *drd, int64_t count)
378 if (!drd->time1 && !drd->count1) {
379 drd->time1 = drd->time2 = cur_time;
380 drd->count1 = drd->count2 = count;
381 } else if (cur_time - drd->time2 > 5000) {
382 drd->time1 = drd->time2;
383 drd->count1 = drd->count2;
384 drd->time2 = cur_time;
389 /* In bytes per second */
390 static int compute_datarate(DataRateData *drd, int64_t count)
392 if (cur_time == drd->time1)
395 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
399 static void start_children(FFStream *feed)
404 for (; feed; feed = feed->next) {
405 if (feed->child_argv && !feed->pid) {
406 feed->pid_start = time(0);
411 http_log("Unable to create children\n");
420 av_strlcpy(pathname, my_program_name, sizeof(pathname));
422 slash = strrchr(pathname, '/');
427 strcpy(slash, "ffmpeg");
429 http_log("Launch commandline: ");
430 http_log("%s ", pathname);
431 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
432 http_log("%s ", feed->child_argv[i]);
435 for (i = 3; i < 256; i++)
438 if (!ffserver_debug) {
439 i = open("/dev/null", O_RDWR);
448 /* This is needed to make relative pathnames work */
449 chdir(my_program_dir);
451 signal(SIGPIPE, SIG_DFL);
453 execvp(pathname, feed->child_argv);
461 /* open a listening socket */
462 static int socket_open_listen(struct sockaddr_in *my_addr)
466 server_fd = socket(AF_INET,SOCK_STREAM,0);
473 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
475 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
477 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
479 closesocket(server_fd);
483 if (listen (server_fd, 5) < 0) {
485 closesocket(server_fd);
488 ff_socket_nonblock(server_fd, 1);
493 /* start all multicast streams */
494 static void start_multicast(void)
499 struct sockaddr_in dest_addr;
500 int default_port, stream_index;
503 for(stream = first_stream; stream != NULL; stream = stream->next) {
504 if (stream->is_multicast) {
505 /* open the RTP connection */
506 snprintf(session_id, sizeof(session_id), "%08x%08x",
507 av_lfg_get(&random_state), av_lfg_get(&random_state));
509 /* choose a port if none given */
510 if (stream->multicast_port == 0) {
511 stream->multicast_port = default_port;
515 dest_addr.sin_family = AF_INET;
516 dest_addr.sin_addr = stream->multicast_ip;
517 dest_addr.sin_port = htons(stream->multicast_port);
519 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
520 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
524 if (open_input_stream(rtp_c, "") < 0) {
525 http_log("Could not open input stream for stream '%s'\n",
530 /* open each RTP stream */
531 for(stream_index = 0; stream_index < stream->nb_streams;
533 dest_addr.sin_port = htons(stream->multicast_port +
535 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
536 http_log("Could not open output stream '%s/streamid=%d'\n",
537 stream->filename, stream_index);
542 /* change state to send data */
543 rtp_c->state = HTTPSTATE_SEND_DATA;
548 /* main loop of the http server */
549 static int http_server(void)
551 int server_fd = 0, rtsp_server_fd = 0;
552 int ret, delay, delay1;
553 struct pollfd *poll_table, *poll_entry;
554 HTTPContext *c, *c_next;
556 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
557 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
561 if (my_http_addr.sin_port) {
562 server_fd = socket_open_listen(&my_http_addr);
567 if (my_rtsp_addr.sin_port) {
568 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
569 if (rtsp_server_fd < 0)
573 if (!rtsp_server_fd && !server_fd) {
574 http_log("HTTP and RTSP disabled.\n");
578 http_log("FFserver started.\n");
580 start_children(first_feed);
585 poll_entry = poll_table;
587 poll_entry->fd = server_fd;
588 poll_entry->events = POLLIN;
591 if (rtsp_server_fd) {
592 poll_entry->fd = rtsp_server_fd;
593 poll_entry->events = POLLIN;
597 /* wait for events on each HTTP handle */
604 case HTTPSTATE_SEND_HEADER:
605 case RTSPSTATE_SEND_REPLY:
606 case RTSPSTATE_SEND_PACKET:
607 c->poll_entry = poll_entry;
609 poll_entry->events = POLLOUT;
612 case HTTPSTATE_SEND_DATA_HEADER:
613 case HTTPSTATE_SEND_DATA:
614 case HTTPSTATE_SEND_DATA_TRAILER:
615 if (!c->is_packetized) {
616 /* for TCP, we output as much as we can (may need to put a limit) */
617 c->poll_entry = poll_entry;
619 poll_entry->events = POLLOUT;
622 /* when ffserver is doing the timing, we work by
623 looking at which packet need to be sent every
625 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
630 case HTTPSTATE_WAIT_REQUEST:
631 case HTTPSTATE_RECEIVE_DATA:
632 case HTTPSTATE_WAIT_FEED:
633 case RTSPSTATE_WAIT_REQUEST:
634 /* need to catch errors */
635 c->poll_entry = poll_entry;
637 poll_entry->events = POLLIN;/* Maybe this will work */
641 c->poll_entry = NULL;
647 /* wait for an event on one connection. We poll at least every
648 second to handle timeouts */
650 ret = poll(poll_table, poll_entry - poll_table, delay);
651 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
652 ff_neterrno() != FF_NETERROR(EINTR))
656 cur_time = av_gettime() / 1000;
658 if (need_to_start_children) {
659 need_to_start_children = 0;
660 start_children(first_feed);
663 /* now handle the events */
664 for(c = first_http_ctx; c != NULL; c = c_next) {
666 if (handle_connection(c) < 0) {
667 /* close and free the connection */
673 poll_entry = poll_table;
675 /* new HTTP connection request ? */
676 if (poll_entry->revents & POLLIN)
677 new_connection(server_fd, 0);
680 if (rtsp_server_fd) {
681 /* new RTSP connection request ? */
682 if (poll_entry->revents & POLLIN)
683 new_connection(rtsp_server_fd, 1);
688 /* start waiting for a new HTTP/RTSP request */
689 static void start_wait_request(HTTPContext *c, int is_rtsp)
691 c->buffer_ptr = c->buffer;
692 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
695 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
696 c->state = RTSPSTATE_WAIT_REQUEST;
698 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
699 c->state = HTTPSTATE_WAIT_REQUEST;
703 static void http_send_too_busy_reply(int fd)
706 int len = snprintf(buffer, sizeof(buffer),
707 "HTTP/1.0 200 Server too busy\r\n"
708 "Content-type: text/html\r\n"
710 "<html><head><title>Too busy</title></head><body>\r\n"
711 "<p>The server is too busy to serve your request at this time.</p>\r\n"
712 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
713 "</body></html>\r\n",
714 nb_connections, nb_max_connections);
715 send(fd, buffer, len, 0);
719 static void new_connection(int server_fd, int is_rtsp)
721 struct sockaddr_in from_addr;
723 HTTPContext *c = NULL;
725 len = sizeof(from_addr);
726 fd = accept(server_fd, (struct sockaddr *)&from_addr,
729 http_log("error during accept %s\n", strerror(errno));
732 ff_socket_nonblock(fd, 1);
734 if (nb_connections >= nb_max_connections) {
735 http_send_too_busy_reply(fd);
739 /* add a new connection */
740 c = av_mallocz(sizeof(HTTPContext));
745 c->poll_entry = NULL;
746 c->from_addr = from_addr;
747 c->buffer_size = IOBUFFER_INIT_SIZE;
748 c->buffer = av_malloc(c->buffer_size);
752 c->next = first_http_ctx;
756 start_wait_request(c, is_rtsp);
768 static void close_connection(HTTPContext *c)
770 HTTPContext **cp, *c1;
772 AVFormatContext *ctx;
776 /* remove connection from list */
777 cp = &first_http_ctx;
778 while ((*cp) != NULL) {
786 /* remove references, if any (XXX: do it faster) */
787 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
792 /* remove connection associated resources */
796 /* close each frame parser */
797 for(i=0;i<c->fmt_in->nb_streams;i++) {
798 st = c->fmt_in->streams[i];
799 if (st->codec->codec)
800 avcodec_close(st->codec);
802 av_close_input_file(c->fmt_in);
805 /* free RTP output streams if any */
808 nb_streams = c->stream->nb_streams;
810 for(i=0;i<nb_streams;i++) {
813 av_write_trailer(ctx);
816 h = c->rtp_handles[i];
823 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
826 if (url_open_dyn_buf(&ctx->pb) >= 0) {
827 av_write_trailer(ctx);
828 av_freep(&c->pb_buffer);
829 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
834 for(i=0; i<ctx->nb_streams; i++)
835 av_free(ctx->streams[i]);
837 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
838 current_bandwidth -= c->stream->bandwidth;
840 /* signal that there is no feed if we are the feeder socket */
841 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
842 c->stream->feed_opened = 0;
846 av_freep(&c->pb_buffer);
847 av_freep(&c->packet_buffer);
853 static int handle_connection(HTTPContext *c)
858 case HTTPSTATE_WAIT_REQUEST:
859 case RTSPSTATE_WAIT_REQUEST:
861 if ((c->timeout - cur_time) < 0)
863 if (c->poll_entry->revents & (POLLERR | POLLHUP))
866 /* no need to read if no events */
867 if (!(c->poll_entry->revents & POLLIN))
871 len = recv(c->fd, c->buffer_ptr, 1, 0);
873 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
874 ff_neterrno() != FF_NETERROR(EINTR))
876 } else if (len == 0) {
879 /* search for end of request. */
881 c->buffer_ptr += len;
883 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
884 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
885 /* request found : parse it and reply */
886 if (c->state == HTTPSTATE_WAIT_REQUEST) {
887 ret = http_parse_request(c);
889 ret = rtsp_parse_request(c);
893 } else if (ptr >= c->buffer_end) {
894 /* request too long: cannot do anything */
896 } else goto read_loop;
900 case HTTPSTATE_SEND_HEADER:
901 if (c->poll_entry->revents & (POLLERR | POLLHUP))
904 /* no need to write if no events */
905 if (!(c->poll_entry->revents & POLLOUT))
907 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
909 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
910 ff_neterrno() != FF_NETERROR(EINTR)) {
911 /* error : close connection */
912 av_freep(&c->pb_buffer);
916 c->buffer_ptr += len;
918 c->stream->bytes_served += len;
919 c->data_count += len;
920 if (c->buffer_ptr >= c->buffer_end) {
921 av_freep(&c->pb_buffer);
925 /* all the buffer was sent : synchronize to the incoming stream */
926 c->state = HTTPSTATE_SEND_DATA_HEADER;
927 c->buffer_ptr = c->buffer_end = c->buffer;
932 case HTTPSTATE_SEND_DATA:
933 case HTTPSTATE_SEND_DATA_HEADER:
934 case HTTPSTATE_SEND_DATA_TRAILER:
935 /* for packetized output, we consider we can always write (the
936 input streams sets the speed). It may be better to verify
937 that we do not rely too much on the kernel queues */
938 if (!c->is_packetized) {
939 if (c->poll_entry->revents & (POLLERR | POLLHUP))
942 /* no need to read if no events */
943 if (!(c->poll_entry->revents & POLLOUT))
946 if (http_send_data(c) < 0)
948 /* close connection if trailer sent */
949 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
952 case HTTPSTATE_RECEIVE_DATA:
953 /* no need to read if no events */
954 if (c->poll_entry->revents & (POLLERR | POLLHUP))
956 if (!(c->poll_entry->revents & POLLIN))
958 if (http_receive_data(c) < 0)
961 case HTTPSTATE_WAIT_FEED:
962 /* no need to read if no events */
963 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
966 /* nothing to do, we'll be waken up by incoming feed packets */
969 case RTSPSTATE_SEND_REPLY:
970 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
971 av_freep(&c->pb_buffer);
974 /* no need to write if no events */
975 if (!(c->poll_entry->revents & POLLOUT))
977 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
979 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
980 ff_neterrno() != FF_NETERROR(EINTR)) {
981 /* error : close connection */
982 av_freep(&c->pb_buffer);
986 c->buffer_ptr += len;
987 c->data_count += len;
988 if (c->buffer_ptr >= c->buffer_end) {
989 /* all the buffer was sent : wait for a new request */
990 av_freep(&c->pb_buffer);
991 start_wait_request(c, 1);
995 case RTSPSTATE_SEND_PACKET:
996 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
997 av_freep(&c->packet_buffer);
1000 /* no need to write if no events */
1001 if (!(c->poll_entry->revents & POLLOUT))
1003 len = send(c->fd, c->packet_buffer_ptr,
1004 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1006 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
1007 ff_neterrno() != FF_NETERROR(EINTR)) {
1008 /* error : close connection */
1009 av_freep(&c->packet_buffer);
1013 c->packet_buffer_ptr += len;
1014 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1015 /* all the buffer was sent : wait for a new request */
1016 av_freep(&c->packet_buffer);
1017 c->state = RTSPSTATE_WAIT_REQUEST;
1021 case HTTPSTATE_READY:
1030 static int extract_rates(char *rates, int ratelen, const char *request)
1034 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1035 if (strncasecmp(p, "Pragma:", 7) == 0) {
1036 const char *q = p + 7;
1038 while (*q && *q != '\n' && isspace(*q))
1041 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1047 memset(rates, 0xff, ratelen);
1050 while (*q && *q != '\n' && *q != ':')
1053 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1057 if (stream_no < ratelen && stream_no >= 0)
1058 rates[stream_no] = rate_no;
1060 while (*q && *q != '\n' && !isspace(*q))
1067 p = strchr(p, '\n');
1077 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1080 int best_bitrate = 100000000;
1083 for (i = 0; i < feed->nb_streams; i++) {
1084 AVCodecContext *feed_codec = feed->streams[i]->codec;
1086 if (feed_codec->codec_id != codec->codec_id ||
1087 feed_codec->sample_rate != codec->sample_rate ||
1088 feed_codec->width != codec->width ||
1089 feed_codec->height != codec->height)
1092 /* Potential stream */
1094 /* We want the fastest stream less than bit_rate, or the slowest
1095 * faster than bit_rate
1098 if (feed_codec->bit_rate <= bit_rate) {
1099 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1100 best_bitrate = feed_codec->bit_rate;
1104 if (feed_codec->bit_rate < best_bitrate) {
1105 best_bitrate = feed_codec->bit_rate;
1114 static int modify_current_stream(HTTPContext *c, char *rates)
1117 FFStream *req = c->stream;
1118 int action_required = 0;
1120 /* Not much we can do for a feed */
1124 for (i = 0; i < req->nb_streams; i++) {
1125 AVCodecContext *codec = req->streams[i]->codec;
1129 c->switch_feed_streams[i] = req->feed_streams[i];
1132 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1135 /* Wants off or slow */
1136 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1138 /* This doesn't work well when it turns off the only stream! */
1139 c->switch_feed_streams[i] = -2;
1140 c->feed_streams[i] = -2;
1145 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1146 action_required = 1;
1149 return action_required;
1153 static void do_switch_stream(HTTPContext *c, int i)
1155 if (c->switch_feed_streams[i] >= 0) {
1157 c->feed_streams[i] = c->switch_feed_streams[i];
1160 /* Now update the stream */
1162 c->switch_feed_streams[i] = -1;
1165 /* XXX: factorize in utils.c ? */
1166 /* XXX: take care with different space meaning */
1167 static void skip_spaces(const char **pp)
1171 while (*p == ' ' || *p == '\t')
1176 static void get_word(char *buf, int buf_size, const char **pp)
1184 while (!isspace(*p) && *p != '\0') {
1185 if ((q - buf) < buf_size - 1)
1194 static int validate_acl(FFStream *stream, HTTPContext *c)
1196 enum IPAddressAction last_action = IP_DENY;
1198 struct in_addr *src = &c->from_addr.sin_addr;
1199 unsigned long src_addr = src->s_addr;
1201 for (acl = stream->acl; acl; acl = acl->next) {
1202 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1203 return (acl->action == IP_ALLOW) ? 1 : 0;
1204 last_action = acl->action;
1207 /* Nothing matched, so return not the last action */
1208 return (last_action == IP_DENY) ? 1 : 0;
1211 /* compute the real filename of a file by matching it without its
1212 extensions to all the stream filenames */
1213 static void compute_real_filename(char *filename, int max_size)
1220 /* compute filename by matching without the file extensions */
1221 av_strlcpy(file1, filename, sizeof(file1));
1222 p = strrchr(file1, '.');
1225 for(stream = first_stream; stream != NULL; stream = stream->next) {
1226 av_strlcpy(file2, stream->filename, sizeof(file2));
1227 p = strrchr(file2, '.');
1230 if (!strcmp(file1, file2)) {
1231 av_strlcpy(filename, stream->filename, max_size);
1246 /* parse http request and prepare header */
1247 static int http_parse_request(HTTPContext *c)
1250 enum RedirType redir_type;
1252 char info[1024], filename[1024];
1256 const char *mime_type;
1260 char *useragent = 0;
1263 get_word(cmd, sizeof(cmd), (const char **)&p);
1264 av_strlcpy(c->method, cmd, sizeof(c->method));
1266 if (!strcmp(cmd, "GET"))
1268 else if (!strcmp(cmd, "POST"))
1273 get_word(url, sizeof(url), (const char **)&p);
1274 av_strlcpy(c->url, url, sizeof(c->url));
1276 get_word(protocol, sizeof(protocol), (const char **)&p);
1277 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1280 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1283 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1285 /* find the filename and the optional info string in the request */
1286 p = strchr(url, '?');
1288 av_strlcpy(info, p, sizeof(info));
1293 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1295 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1296 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1298 if (*useragent && *useragent != '\n' && isspace(*useragent))
1302 p = strchr(p, '\n');
1309 redir_type = REDIR_NONE;
1310 if (match_ext(filename, "asx")) {
1311 redir_type = REDIR_ASX;
1312 filename[strlen(filename)-1] = 'f';
1313 } else if (match_ext(filename, "asf") &&
1314 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1315 /* if this isn't WMP or lookalike, return the redirector file */
1316 redir_type = REDIR_ASF;
1317 } else if (match_ext(filename, "rpm,ram")) {
1318 redir_type = REDIR_RAM;
1319 strcpy(filename + strlen(filename)-2, "m");
1320 } else if (match_ext(filename, "rtsp")) {
1321 redir_type = REDIR_RTSP;
1322 compute_real_filename(filename, sizeof(filename) - 1);
1323 } else if (match_ext(filename, "sdp")) {
1324 redir_type = REDIR_SDP;
1325 compute_real_filename(filename, sizeof(filename) - 1);
1328 // "redirect" / request to index.html
1329 if (!strlen(filename))
1330 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1332 stream = first_stream;
1333 while (stream != NULL) {
1334 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1336 stream = stream->next;
1338 if (stream == NULL) {
1339 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1340 http_log("File '%s' not found\n", url);
1345 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1346 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1348 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1349 c->http_error = 301;
1351 q += snprintf(q, c->buffer_size,
1352 "HTTP/1.0 301 Moved\r\n"
1354 "Content-type: text/html\r\n"
1356 "<html><head><title>Moved</title></head><body>\r\n"
1357 "You should be <a href=\"%s\">redirected</a>.\r\n"
1358 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1359 /* prepare output buffer */
1360 c->buffer_ptr = c->buffer;
1362 c->state = HTTPSTATE_SEND_HEADER;
1366 /* If this is WMP, get the rate information */
1367 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1368 if (modify_current_stream(c, ratebuf)) {
1369 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1370 if (c->switch_feed_streams[i] >= 0)
1371 do_switch_stream(c, i);
1376 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1377 current_bandwidth += stream->bandwidth;
1379 /* If already streaming this feed, do not let start another feeder. */
1380 if (stream->feed_opened) {
1381 snprintf(msg, sizeof(msg), "This feed is already being received.");
1382 http_log("Feed '%s' already being received\n", stream->feed_filename);
1386 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1387 c->http_error = 200;
1389 q += snprintf(q, c->buffer_size,
1390 "HTTP/1.0 200 Server too busy\r\n"
1391 "Content-type: text/html\r\n"
1393 "<html><head><title>Too busy</title></head><body>\r\n"
1394 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1395 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1396 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1397 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1398 /* prepare output buffer */
1399 c->buffer_ptr = c->buffer;
1401 c->state = HTTPSTATE_SEND_HEADER;
1405 if (redir_type != REDIR_NONE) {
1408 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1409 if (strncasecmp(p, "Host:", 5) == 0) {
1413 p = strchr(p, '\n');
1424 while (isspace(*hostinfo))
1427 eoh = strchr(hostinfo, '\n');
1429 if (eoh[-1] == '\r')
1432 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1433 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1434 hostbuf[eoh - hostinfo] = 0;
1436 c->http_error = 200;
1438 switch(redir_type) {
1440 q += snprintf(q, c->buffer_size,
1441 "HTTP/1.0 200 ASX Follows\r\n"
1442 "Content-type: video/x-ms-asf\r\n"
1444 "<ASX Version=\"3\">\r\n"
1445 //"<!-- Autogenerated by ffserver -->\r\n"
1446 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1447 "</ASX>\r\n", hostbuf, filename, info);
1450 q += snprintf(q, c->buffer_size,
1451 "HTTP/1.0 200 RAM Follows\r\n"
1452 "Content-type: audio/x-pn-realaudio\r\n"
1454 "# Autogenerated by ffserver\r\n"
1455 "http://%s/%s%s\r\n", hostbuf, filename, info);
1458 q += snprintf(q, c->buffer_size,
1459 "HTTP/1.0 200 ASF Redirect follows\r\n"
1460 "Content-type: video/x-ms-asf\r\n"
1463 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1467 char hostname[256], *p;
1468 /* extract only hostname */
1469 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1470 p = strrchr(hostname, ':');
1473 q += snprintf(q, c->buffer_size,
1474 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1475 /* XXX: incorrect mime type ? */
1476 "Content-type: application/x-rtsp\r\n"
1478 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1484 int sdp_data_size, len;
1485 struct sockaddr_in my_addr;
1487 q += snprintf(q, c->buffer_size,
1488 "HTTP/1.0 200 OK\r\n"
1489 "Content-type: application/sdp\r\n"
1492 len = sizeof(my_addr);
1493 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1495 /* XXX: should use a dynamic buffer */
1496 sdp_data_size = prepare_sdp_description(stream,
1499 if (sdp_data_size > 0) {
1500 memcpy(q, sdp_data, sdp_data_size);
1512 /* prepare output buffer */
1513 c->buffer_ptr = c->buffer;
1515 c->state = HTTPSTATE_SEND_HEADER;
1521 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1525 stream->conns_served++;
1527 /* XXX: add there authenticate and IP match */
1530 /* if post, it means a feed is being sent */
1531 if (!stream->is_feed) {
1532 /* However it might be a status report from WMP! Let us log the
1533 * data as it might come in handy one day. */
1537 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1538 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1542 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1543 client_id = strtol(p + 18, 0, 10);
1544 p = strchr(p, '\n');
1552 char *eol = strchr(logline, '\n');
1557 if (eol[-1] == '\r')
1559 http_log("%.*s\n", (int) (eol - logline), logline);
1560 c->suppress_log = 1;
1565 http_log("\nGot request:\n%s\n", c->buffer);
1568 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1571 /* Now we have to find the client_id */
1572 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1573 if (wmpc->wmp_client_id == client_id)
1577 if (wmpc && modify_current_stream(wmpc, ratebuf))
1578 wmpc->switch_pending = 1;
1581 snprintf(msg, sizeof(msg), "POST command not handled");
1585 if (http_start_receive_data(c) < 0) {
1586 snprintf(msg, sizeof(msg), "could not open feed");
1590 c->state = HTTPSTATE_RECEIVE_DATA;
1595 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1596 http_log("\nGot request:\n%s\n", c->buffer);
1599 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1602 /* open input stream */
1603 if (open_input_stream(c, info) < 0) {
1604 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1608 /* prepare http header */
1610 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1611 mime_type = c->stream->fmt->mime_type;
1613 mime_type = "application/x-octet-stream";
1614 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1616 /* for asf, we need extra headers */
1617 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1618 /* Need to allocate a client id */
1620 c->wmp_client_id = av_lfg_get(&random_state);
1622 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);
1624 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1625 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1627 /* prepare output buffer */
1629 c->buffer_ptr = c->buffer;
1631 c->state = HTTPSTATE_SEND_HEADER;
1634 c->http_error = 404;
1636 q += snprintf(q, c->buffer_size,
1637 "HTTP/1.0 404 Not Found\r\n"
1638 "Content-type: text/html\r\n"
1641 "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
1644 /* prepare output buffer */
1645 c->buffer_ptr = c->buffer;
1647 c->state = HTTPSTATE_SEND_HEADER;
1651 c->http_error = 200; /* horrible : we use this value to avoid
1652 going to the send data state */
1653 c->state = HTTPSTATE_SEND_HEADER;
1657 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1659 static const char *suffix = " kMGTP";
1662 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1664 url_fprintf(pb, "%"PRId64"%c", count, *s);
1667 static void compute_status(HTTPContext *c)
1676 if (url_open_dyn_buf(&pb) < 0) {
1677 /* XXX: return an error ? */
1678 c->buffer_ptr = c->buffer;
1679 c->buffer_end = c->buffer;
1683 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1684 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1685 url_fprintf(pb, "Pragma: no-cache\r\n");
1686 url_fprintf(pb, "\r\n");
1688 url_fprintf(pb, "<HTML><HEAD><TITLE>%s Status</TITLE>\n", program_name);
1689 if (c->stream->feed_filename[0])
1690 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1691 url_fprintf(pb, "</HEAD>\n<BODY>");
1692 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1694 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1695 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1696 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");
1697 stream = first_stream;
1698 while (stream != NULL) {
1699 char sfilename[1024];
1702 if (stream->feed != stream) {
1703 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1704 eosf = sfilename + strlen(sfilename);
1705 if (eosf - sfilename >= 4) {
1706 if (strcmp(eosf - 4, ".asf") == 0)
1707 strcpy(eosf - 4, ".asx");
1708 else if (strcmp(eosf - 3, ".rm") == 0)
1709 strcpy(eosf - 3, ".ram");
1710 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1711 /* generate a sample RTSP director if
1712 unicast. Generate an SDP redirector if
1714 eosf = strrchr(sfilename, '.');
1716 eosf = sfilename + strlen(sfilename);
1717 if (stream->is_multicast)
1718 strcpy(eosf, ".sdp");
1720 strcpy(eosf, ".rtsp");
1724 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1725 sfilename, stream->filename);
1726 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1727 stream->conns_served);
1728 fmt_bytecount(pb, stream->bytes_served);
1729 switch(stream->stream_type) {
1730 case STREAM_TYPE_LIVE: {
1731 int audio_bit_rate = 0;
1732 int video_bit_rate = 0;
1733 const char *audio_codec_name = "";
1734 const char *video_codec_name = "";
1735 const char *audio_codec_name_extra = "";
1736 const char *video_codec_name_extra = "";
1738 for(i=0;i<stream->nb_streams;i++) {
1739 AVStream *st = stream->streams[i];
1740 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1741 switch(st->codec->codec_type) {
1742 case CODEC_TYPE_AUDIO:
1743 audio_bit_rate += st->codec->bit_rate;
1745 if (*audio_codec_name)
1746 audio_codec_name_extra = "...";
1747 audio_codec_name = codec->name;
1750 case CODEC_TYPE_VIDEO:
1751 video_bit_rate += st->codec->bit_rate;
1753 if (*video_codec_name)
1754 video_codec_name_extra = "...";
1755 video_codec_name = codec->name;
1758 case CODEC_TYPE_DATA:
1759 video_bit_rate += st->codec->bit_rate;
1765 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",
1768 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1769 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1771 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1773 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1774 url_fprintf(pb, "\n");
1778 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1782 stream = stream->next;
1784 url_fprintf(pb, "</TABLE>\n");
1786 stream = first_stream;
1787 while (stream != NULL) {
1788 if (stream->feed == stream) {
1789 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1791 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1793 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1798 /* This is somewhat linux specific I guess */
1799 snprintf(ps_cmd, sizeof(ps_cmd),
1800 "ps -o \"%%cpu,cputime\" --no-headers %d",
1803 pid_stat = popen(ps_cmd, "r");
1808 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1810 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1818 url_fprintf(pb, "<p>");
1820 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");
1822 for (i = 0; i < stream->nb_streams; i++) {
1823 AVStream *st = stream->streams[i];
1824 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1825 const char *type = "unknown";
1826 char parameters[64];
1830 switch(st->codec->codec_type) {
1831 case CODEC_TYPE_AUDIO:
1833 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1835 case CODEC_TYPE_VIDEO:
1837 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1838 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1843 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1844 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1846 url_fprintf(pb, "</table>\n");
1849 stream = stream->next;
1855 AVCodecContext *enc;
1859 stream = first_feed;
1860 while (stream != NULL) {
1861 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1862 url_fprintf(pb, "<TABLE>\n");
1863 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1864 for(i=0;i<stream->nb_streams;i++) {
1865 AVStream *st = stream->streams[i];
1866 FeedData *fdata = st->priv_data;
1869 avcodec_string(buf, sizeof(buf), enc);
1870 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1871 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1872 avg /= enc->frame_size;
1873 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1874 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1876 url_fprintf(pb, "</TABLE>\n");
1877 stream = stream->next_feed;
1882 /* connection status */
1883 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1885 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1886 nb_connections, nb_max_connections);
1888 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<BR>\n",
1889 current_bandwidth, max_bandwidth);
1891 url_fprintf(pb, "<TABLE>\n");
1892 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");
1893 c1 = first_http_ctx;
1895 while (c1 != NULL) {
1901 for (j = 0; j < c1->stream->nb_streams; j++) {
1902 if (!c1->stream->feed)
1903 bitrate += c1->stream->streams[j]->codec->bit_rate;
1904 else if (c1->feed_streams[j] >= 0)
1905 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1910 p = inet_ntoa(c1->from_addr.sin_addr);
1911 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1913 c1->stream ? c1->stream->filename : "",
1914 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1917 http_state[c1->state]);
1918 fmt_bytecount(pb, bitrate);
1919 url_fprintf(pb, "<td align=right>");
1920 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1921 url_fprintf(pb, "<td align=right>");
1922 fmt_bytecount(pb, c1->data_count);
1923 url_fprintf(pb, "\n");
1926 url_fprintf(pb, "</TABLE>\n");
1931 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1932 url_fprintf(pb, "</BODY>\n</HTML>\n");
1934 len = url_close_dyn_buf(pb, &c->pb_buffer);
1935 c->buffer_ptr = c->pb_buffer;
1936 c->buffer_end = c->pb_buffer + len;
1939 /* check if the parser needs to be opened for stream i */
1940 static void open_parser(AVFormatContext *s, int i)
1942 AVStream *st = s->streams[i];
1945 if (!st->codec->codec) {
1946 codec = avcodec_find_decoder(st->codec->codec_id);
1947 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1948 st->codec->parse_only = 1;
1949 if (avcodec_open(st->codec, codec) < 0)
1950 st->codec->parse_only = 0;
1955 static int open_input_stream(HTTPContext *c, const char *info)
1958 char input_filename[1024];
1960 int buf_size, i, ret;
1963 /* find file name */
1964 if (c->stream->feed) {
1965 strcpy(input_filename, c->stream->feed->feed_filename);
1966 buf_size = FFM_PACKET_SIZE;
1967 /* compute position (absolute time) */
1968 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1969 stream_pos = parse_date(buf, 0);
1970 if (stream_pos == INT64_MIN)
1972 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1973 int prebuffer = strtol(buf, 0, 10);
1974 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1976 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1978 strcpy(input_filename, c->stream->feed_filename);
1980 /* compute position (relative time) */
1981 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1982 stream_pos = parse_date(buf, 1);
1983 if (stream_pos == INT64_MIN)
1988 if (input_filename[0] == '\0')
1992 { time_t when = stream_pos / 1000000;
1993 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1998 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1999 buf_size, c->stream->ap_in)) < 0) {
2000 http_log("could not open %s: %d\n", input_filename, ret);
2003 s->flags |= AVFMT_FLAG_GENPTS;
2005 if (strcmp(s->iformat->name, "ffm") && av_find_stream_info(c->fmt_in) < 0) {
2006 http_log("Could not find stream info '%s'\n", input_filename);
2007 av_close_input_file(s);
2011 /* open each parser */
2012 for(i=0;i<s->nb_streams;i++)
2015 /* choose stream as clock source (we favorize video stream if
2016 present) for packet sending */
2017 c->pts_stream_index = 0;
2018 for(i=0;i<c->stream->nb_streams;i++) {
2019 if (c->pts_stream_index == 0 &&
2020 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
2021 c->pts_stream_index = i;
2026 if (c->fmt_in->iformat->read_seek)
2027 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2029 /* set the start time (needed for maxtime and RTP packet timing) */
2030 c->start_time = cur_time;
2031 c->first_pts = AV_NOPTS_VALUE;
2035 /* return the server clock (in us) */
2036 static int64_t get_server_clock(HTTPContext *c)
2038 /* compute current pts value from system time */
2039 return (cur_time - c->start_time) * 1000;
2042 /* return the estimated time at which the current packet must be sent
2044 static int64_t get_packet_send_clock(HTTPContext *c)
2046 int bytes_left, bytes_sent, frame_bytes;
2048 frame_bytes = c->cur_frame_bytes;
2049 if (frame_bytes <= 0)
2052 bytes_left = c->buffer_end - c->buffer_ptr;
2053 bytes_sent = frame_bytes - bytes_left;
2054 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2059 static int http_prepare_data(HTTPContext *c)
2062 AVFormatContext *ctx;
2064 av_freep(&c->pb_buffer);
2066 case HTTPSTATE_SEND_DATA_HEADER:
2067 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2068 av_metadata_set(&c->fmt_ctx.metadata, "author" ,c->stream->author);
2069 av_metadata_set(&c->fmt_ctx.metadata, "comment" ,c->stream->comment);
2070 av_metadata_set(&c->fmt_ctx.metadata, "copyright",c->stream->copyright);
2071 av_metadata_set(&c->fmt_ctx.metadata, "title" ,c->stream->title);
2073 for(i=0;i<c->stream->nb_streams;i++) {
2076 st = av_mallocz(sizeof(AVStream));
2077 c->fmt_ctx.streams[i] = st;
2078 /* if file or feed, then just take streams from FFStream struct */
2079 if (!c->stream->feed ||
2080 c->stream->feed == c->stream)
2081 src = c->stream->streams[i];
2083 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2087 st->codec->frame_number = 0; /* XXX: should be done in
2088 AVStream, not in codec */
2090 /* set output format parameters */
2091 c->fmt_ctx.oformat = c->stream->fmt;
2092 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2094 c->got_key_frame = 0;
2096 /* prepare header and save header data in a stream */
2097 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2098 /* XXX: potential leak */
2101 c->fmt_ctx.pb->is_streamed = 1;
2104 * HACK to avoid mpeg ps muxer to spit many underflow errors
2105 * Default value from FFmpeg
2106 * Try to set it use configuration option
2108 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2109 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2111 av_set_parameters(&c->fmt_ctx, NULL);
2112 if (av_write_header(&c->fmt_ctx) < 0) {
2113 http_log("Error writing output header\n");
2117 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2118 c->buffer_ptr = c->pb_buffer;
2119 c->buffer_end = c->pb_buffer + len;
2121 c->state = HTTPSTATE_SEND_DATA;
2122 c->last_packet_sent = 0;
2124 case HTTPSTATE_SEND_DATA:
2125 /* find a new packet */
2126 /* read a packet from the input stream */
2127 if (c->stream->feed)
2128 ffm_set_write_index(c->fmt_in,
2129 c->stream->feed->feed_write_index,
2130 c->stream->feed->feed_size);
2132 if (c->stream->max_time &&
2133 c->stream->max_time + c->start_time - cur_time < 0)
2134 /* We have timed out */
2135 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2139 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2140 if (c->stream->feed && c->stream->feed->feed_opened) {
2141 /* if coming from feed, it means we reached the end of the
2142 ffm file, so must wait for more data */
2143 c->state = HTTPSTATE_WAIT_FEED;
2144 return 1; /* state changed */
2146 if (c->stream->loop) {
2147 av_close_input_file(c->fmt_in);
2149 if (open_input_stream(c, "") < 0)
2154 /* must send trailer now because eof or error */
2155 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2159 int source_index = pkt.stream_index;
2160 /* update first pts if needed */
2161 if (c->first_pts == AV_NOPTS_VALUE) {
2162 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2163 c->start_time = cur_time;
2165 /* send it to the appropriate stream */
2166 if (c->stream->feed) {
2167 /* if coming from a feed, select the right stream */
2168 if (c->switch_pending) {
2169 c->switch_pending = 0;
2170 for(i=0;i<c->stream->nb_streams;i++) {
2171 if (c->switch_feed_streams[i] == pkt.stream_index)
2172 if (pkt.flags & PKT_FLAG_KEY)
2173 do_switch_stream(c, i);
2174 if (c->switch_feed_streams[i] >= 0)
2175 c->switch_pending = 1;
2178 for(i=0;i<c->stream->nb_streams;i++) {
2179 if (c->feed_streams[i] == pkt.stream_index) {
2180 AVStream *st = c->fmt_in->streams[source_index];
2181 pkt.stream_index = i;
2182 if (pkt.flags & PKT_FLAG_KEY &&
2183 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2184 c->stream->nb_streams == 1))
2185 c->got_key_frame = 1;
2186 if (!c->stream->send_on_key || c->got_key_frame)
2191 AVCodecContext *codec;
2192 AVStream *ist, *ost;
2194 ist = c->fmt_in->streams[source_index];
2195 /* specific handling for RTP: we use several
2196 output stream (one for each RTP
2197 connection). XXX: need more abstract handling */
2198 if (c->is_packetized) {
2199 /* compute send time and duration */
2200 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2201 if (ist->start_time != AV_NOPTS_VALUE)
2202 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2203 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2205 printf("index=%d pts=%0.3f duration=%0.6f\n",
2207 (double)c->cur_pts /
2209 (double)c->cur_frame_duration /
2212 /* find RTP context */
2213 c->packet_stream_index = pkt.stream_index;
2214 ctx = c->rtp_ctx[c->packet_stream_index];
2216 av_free_packet(&pkt);
2219 codec = ctx->streams[0]->codec;
2220 /* only one stream per RTP connection */
2221 pkt.stream_index = 0;
2225 codec = ctx->streams[pkt.stream_index]->codec;
2228 if (c->is_packetized) {
2229 int max_packet_size;
2230 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2231 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2233 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2234 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2236 ret = url_open_dyn_buf(&ctx->pb);
2239 /* XXX: potential leak */
2242 ost = ctx->streams[pkt.stream_index];
2244 ctx->pb->is_streamed = 1;
2245 if (pkt.dts != AV_NOPTS_VALUE)
2246 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2247 if (pkt.pts != AV_NOPTS_VALUE)
2248 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2249 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2250 if (av_write_frame(ctx, &pkt) < 0) {
2251 http_log("Error writing frame to output\n");
2252 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2255 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2256 c->cur_frame_bytes = len;
2257 c->buffer_ptr = c->pb_buffer;
2258 c->buffer_end = c->pb_buffer + len;
2260 codec->frame_number++;
2262 av_free_packet(&pkt);
2266 av_free_packet(&pkt);
2271 case HTTPSTATE_SEND_DATA_TRAILER:
2272 /* last packet test ? */
2273 if (c->last_packet_sent || c->is_packetized)
2276 /* prepare header */
2277 if (url_open_dyn_buf(&ctx->pb) < 0) {
2278 /* XXX: potential leak */
2281 c->fmt_ctx.pb->is_streamed = 1;
2282 av_write_trailer(ctx);
2283 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2284 c->buffer_ptr = c->pb_buffer;
2285 c->buffer_end = c->pb_buffer + len;
2287 c->last_packet_sent = 1;
2293 /* should convert the format at the same time */
2294 /* send data starting at c->buffer_ptr to the output connection
2295 (either UDP or TCP connection) */
2296 static int http_send_data(HTTPContext *c)
2301 if (c->buffer_ptr >= c->buffer_end) {
2302 ret = http_prepare_data(c);
2306 /* state change requested */
2309 if (c->is_packetized) {
2310 /* RTP data output */
2311 len = c->buffer_end - c->buffer_ptr;
2313 /* fail safe - should never happen */
2315 c->buffer_ptr = c->buffer_end;
2318 len = (c->buffer_ptr[0] << 24) |
2319 (c->buffer_ptr[1] << 16) |
2320 (c->buffer_ptr[2] << 8) |
2322 if (len > (c->buffer_end - c->buffer_ptr))
2324 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2325 /* nothing to send yet: we can wait */
2329 c->data_count += len;
2330 update_datarate(&c->datarate, c->data_count);
2332 c->stream->bytes_served += len;
2334 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2335 /* RTP packets are sent inside the RTSP TCP connection */
2337 int interleaved_index, size;
2339 HTTPContext *rtsp_c;
2342 /* if no RTSP connection left, error */
2345 /* if already sending something, then wait. */
2346 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2348 if (url_open_dyn_buf(&pb) < 0)
2350 interleaved_index = c->packet_stream_index * 2;
2351 /* RTCP packets are sent at odd indexes */
2352 if (c->buffer_ptr[1] == 200)
2353 interleaved_index++;
2354 /* write RTSP TCP header */
2356 header[1] = interleaved_index;
2357 header[2] = len >> 8;
2359 put_buffer(pb, header, 4);
2360 /* write RTP packet data */
2362 put_buffer(pb, c->buffer_ptr, len);
2363 size = url_close_dyn_buf(pb, &c->packet_buffer);
2364 /* prepare asynchronous TCP sending */
2365 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2366 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2367 c->buffer_ptr += len;
2369 /* send everything we can NOW */
2370 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2371 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2373 rtsp_c->packet_buffer_ptr += len;
2374 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2375 /* if we could not send all the data, we will
2376 send it later, so a new state is needed to
2377 "lock" the RTSP TCP connection */
2378 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2381 /* all data has been sent */
2382 av_freep(&c->packet_buffer);
2384 /* send RTP packet directly in UDP */
2386 url_write(c->rtp_handles[c->packet_stream_index],
2387 c->buffer_ptr, len);
2388 c->buffer_ptr += len;
2389 /* here we continue as we can send several packets per 10 ms slot */
2392 /* TCP data output */
2393 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2395 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2396 ff_neterrno() != FF_NETERROR(EINTR))
2397 /* error : close connection */
2402 c->buffer_ptr += len;
2404 c->data_count += len;
2405 update_datarate(&c->datarate, c->data_count);
2407 c->stream->bytes_served += len;
2415 static int http_start_receive_data(HTTPContext *c)
2419 if (c->stream->feed_opened)
2422 /* Don't permit writing to this one */
2423 if (c->stream->readonly)
2427 fd = open(c->stream->feed_filename, O_RDWR);
2429 http_log("Error opening feeder file: %s\n", strerror(errno));
2434 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2435 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2438 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2440 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2441 lseek(fd, 0, SEEK_SET);
2443 /* init buffer input */
2444 c->buffer_ptr = c->buffer;
2445 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2446 c->stream->feed_opened = 1;
2450 static int http_receive_data(HTTPContext *c)
2454 if (c->buffer_end > c->buffer_ptr) {
2457 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2459 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2460 ff_neterrno() != FF_NETERROR(EINTR))
2461 /* error : close connection */
2463 } else if (len == 0)
2464 /* end of connection : close it */
2467 c->buffer_ptr += len;
2468 c->data_count += len;
2469 update_datarate(&c->datarate, c->data_count);
2473 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2474 if (c->buffer[0] != 'f' ||
2475 c->buffer[1] != 'm') {
2476 http_log("Feed stream has become desynchronized -- disconnecting\n");
2481 if (c->buffer_ptr >= c->buffer_end) {
2482 FFStream *feed = c->stream;
2483 /* a packet has been received : write it in the store, except
2485 if (c->data_count > FFM_PACKET_SIZE) {
2487 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2488 /* XXX: use llseek or url_seek */
2489 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2490 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2491 http_log("Error writing to feed file: %s\n", strerror(errno));
2495 feed->feed_write_index += FFM_PACKET_SIZE;
2496 /* update file size */
2497 if (feed->feed_write_index > c->stream->feed_size)
2498 feed->feed_size = feed->feed_write_index;
2500 /* handle wrap around if max file size reached */
2501 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2502 feed->feed_write_index = FFM_PACKET_SIZE;
2505 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2506 http_log("Error writing index to feed file: %s\n", strerror(errno));
2510 /* wake up any waiting connections */
2511 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2512 if (c1->state == HTTPSTATE_WAIT_FEED &&
2513 c1->stream->feed == c->stream->feed)
2514 c1->state = HTTPSTATE_SEND_DATA;
2517 /* We have a header in our hands that contains useful data */
2518 AVFormatContext *s = NULL;
2520 AVInputFormat *fmt_in;
2523 /* use feed output format name to find corresponding input format */
2524 fmt_in = av_find_input_format(feed->fmt->name);
2528 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2529 pb->is_streamed = 1;
2531 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2536 /* Now we have the actual streams */
2537 if (s->nb_streams != feed->nb_streams) {
2538 av_close_input_stream(s);
2540 http_log("Feed '%s' stream number does not match registered feed\n",
2541 c->stream->feed_filename);
2545 for (i = 0; i < s->nb_streams; i++) {
2546 AVStream *fst = feed->streams[i];
2547 AVStream *st = s->streams[i];
2548 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2549 if (fst->codec->extradata_size) {
2550 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2551 if (!fst->codec->extradata)
2553 memcpy(fst->codec->extradata, st->codec->extradata,
2554 fst->codec->extradata_size);
2558 av_close_input_stream(s);
2561 c->buffer_ptr = c->buffer;
2566 c->stream->feed_opened = 0;
2568 /* wake up any waiting connections to stop waiting for feed */
2569 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2570 if (c1->state == HTTPSTATE_WAIT_FEED &&
2571 c1->stream->feed == c->stream->feed)
2572 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2577 /********************************************************************/
2580 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2587 switch(error_number) {
2588 case RTSP_STATUS_OK:
2591 case RTSP_STATUS_METHOD:
2592 str = "Method Not Allowed";
2594 case RTSP_STATUS_BANDWIDTH:
2595 str = "Not Enough Bandwidth";
2597 case RTSP_STATUS_SESSION:
2598 str = "Session Not Found";
2600 case RTSP_STATUS_STATE:
2601 str = "Method Not Valid in This State";
2603 case RTSP_STATUS_AGGREGATE:
2604 str = "Aggregate operation not allowed";
2606 case RTSP_STATUS_ONLY_AGGREGATE:
2607 str = "Only aggregate operation allowed";
2609 case RTSP_STATUS_TRANSPORT:
2610 str = "Unsupported transport";
2612 case RTSP_STATUS_INTERNAL:
2613 str = "Internal Server Error";
2615 case RTSP_STATUS_SERVICE:
2616 str = "Service Unavailable";
2618 case RTSP_STATUS_VERSION:
2619 str = "RTSP Version not supported";
2622 str = "Unknown Error";
2626 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2627 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2629 /* output GMT time */
2633 p = buf2 + strlen(p) - 1;
2636 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2639 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2641 rtsp_reply_header(c, error_number);
2642 url_fprintf(c->pb, "\r\n");
2645 static int rtsp_parse_request(HTTPContext *c)
2647 const char *p, *p1, *p2;
2653 RTSPMessageHeader header1, *header = &header1;
2655 c->buffer_ptr[0] = '\0';
2658 get_word(cmd, sizeof(cmd), &p);
2659 get_word(url, sizeof(url), &p);
2660 get_word(protocol, sizeof(protocol), &p);
2662 av_strlcpy(c->method, cmd, sizeof(c->method));
2663 av_strlcpy(c->url, url, sizeof(c->url));
2664 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2666 if (url_open_dyn_buf(&c->pb) < 0) {
2667 /* XXX: cannot do more */
2668 c->pb = NULL; /* safety */
2672 /* check version name */
2673 if (strcmp(protocol, "RTSP/1.0") != 0) {
2674 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2678 /* parse each header line */
2679 memset(header, 0, sizeof(*header));
2680 /* skip to next line */
2681 while (*p != '\n' && *p != '\0')
2685 while (*p != '\0') {
2686 p1 = strchr(p, '\n');
2690 if (p2 > p && p2[-1] == '\r')
2692 /* skip empty line */
2696 if (len > sizeof(line) - 1)
2697 len = sizeof(line) - 1;
2698 memcpy(line, p, len);
2700 rtsp_parse_line(header, line);
2704 /* handle sequence number */
2705 c->seq = header->seq;
2707 if (!strcmp(cmd, "DESCRIBE"))
2708 rtsp_cmd_describe(c, url);
2709 else if (!strcmp(cmd, "OPTIONS"))
2710 rtsp_cmd_options(c, url);
2711 else if (!strcmp(cmd, "SETUP"))
2712 rtsp_cmd_setup(c, url, header);
2713 else if (!strcmp(cmd, "PLAY"))
2714 rtsp_cmd_play(c, url, header);
2715 else if (!strcmp(cmd, "PAUSE"))
2716 rtsp_cmd_pause(c, url, header);
2717 else if (!strcmp(cmd, "TEARDOWN"))
2718 rtsp_cmd_teardown(c, url, header);
2720 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2723 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2724 c->pb = NULL; /* safety */
2726 /* XXX: cannot do more */
2729 c->buffer_ptr = c->pb_buffer;
2730 c->buffer_end = c->pb_buffer + len;
2731 c->state = RTSPSTATE_SEND_REPLY;
2735 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2736 struct in_addr my_ip)
2738 AVFormatContext *avc;
2739 AVStream avs[MAX_STREAMS];
2742 avc = avformat_alloc_context();
2746 av_metadata_set(&avc->metadata, "title",
2747 stream->title[0] ? stream->title : "No Title");
2748 avc->nb_streams = stream->nb_streams;
2749 if (stream->is_multicast) {
2750 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2751 inet_ntoa(stream->multicast_ip),
2752 stream->multicast_port, stream->multicast_ttl);
2755 for(i = 0; i < stream->nb_streams; i++) {
2756 avc->streams[i] = &avs[i];
2757 avc->streams[i]->codec = stream->streams[i]->codec;
2759 *pbuffer = av_mallocz(2048);
2760 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2763 return strlen(*pbuffer);
2766 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2768 // rtsp_reply_header(c, RTSP_STATUS_OK);
2769 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2770 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2771 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2772 url_fprintf(c->pb, "\r\n");
2775 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2781 int content_length, len;
2782 struct sockaddr_in my_addr;
2784 /* find which url is asked */
2785 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2790 for(stream = first_stream; stream != NULL; stream = stream->next) {
2791 if (!stream->is_feed &&
2792 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2793 !strcmp(path, stream->filename)) {
2797 /* no stream found */
2798 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2802 /* prepare the media description in sdp format */
2804 /* get the host IP */
2805 len = sizeof(my_addr);
2806 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2807 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2808 if (content_length < 0) {
2809 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2812 rtsp_reply_header(c, RTSP_STATUS_OK);
2813 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2814 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2815 url_fprintf(c->pb, "\r\n");
2816 put_buffer(c->pb, content, content_length);
2819 static HTTPContext *find_rtp_session(const char *session_id)
2823 if (session_id[0] == '\0')
2826 for(c = first_http_ctx; c != NULL; c = c->next) {
2827 if (!strcmp(c->session_id, session_id))
2833 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
2835 RTSPTransportField *th;
2838 for(i=0;i<h->nb_transports;i++) {
2839 th = &h->transports[i];
2840 if (th->lower_transport == lower_transport)
2846 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2847 RTSPMessageHeader *h)
2850 int stream_index, port;
2855 RTSPTransportField *th;
2856 struct sockaddr_in dest_addr;
2857 RTSPActionServerSetup setup;
2859 /* find which url is asked */
2860 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2865 /* now check each stream */
2866 for(stream = first_stream; stream != NULL; stream = stream->next) {
2867 if (!stream->is_feed &&
2868 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2869 /* accept aggregate filenames only if single stream */
2870 if (!strcmp(path, stream->filename)) {
2871 if (stream->nb_streams != 1) {
2872 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2879 for(stream_index = 0; stream_index < stream->nb_streams;
2881 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2882 stream->filename, stream_index);
2883 if (!strcmp(path, buf))
2888 /* no stream found */
2889 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2893 /* generate session id if needed */
2894 if (h->session_id[0] == '\0')
2895 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2896 av_lfg_get(&random_state), av_lfg_get(&random_state));
2898 /* find rtp session, and create it if none found */
2899 rtp_c = find_rtp_session(h->session_id);
2901 /* always prefer UDP */
2902 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
2904 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
2906 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2911 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2912 th->lower_transport);
2914 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2918 /* open input stream */
2919 if (open_input_stream(rtp_c, "") < 0) {
2920 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2925 /* test if stream is OK (test needed because several SETUP needs
2926 to be done for a given file) */
2927 if (rtp_c->stream != stream) {
2928 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2932 /* test if stream is already set up */
2933 if (rtp_c->rtp_ctx[stream_index]) {
2934 rtsp_reply_error(c, RTSP_STATUS_STATE);
2938 /* check transport */
2939 th = find_transport(h, rtp_c->rtp_protocol);
2940 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
2941 th->client_port_min <= 0)) {
2942 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2946 /* setup default options */
2947 setup.transport_option[0] = '\0';
2948 dest_addr = rtp_c->from_addr;
2949 dest_addr.sin_port = htons(th->client_port_min);
2952 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2953 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2957 /* now everything is OK, so we can send the connection parameters */
2958 rtsp_reply_header(c, RTSP_STATUS_OK);
2960 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2962 switch(rtp_c->rtp_protocol) {
2963 case RTSP_LOWER_TRANSPORT_UDP:
2964 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2965 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2966 "client_port=%d-%d;server_port=%d-%d",
2967 th->client_port_min, th->client_port_min + 1,
2970 case RTSP_LOWER_TRANSPORT_TCP:
2971 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2972 stream_index * 2, stream_index * 2 + 1);
2977 if (setup.transport_option[0] != '\0')
2978 url_fprintf(c->pb, ";%s", setup.transport_option);
2979 url_fprintf(c->pb, "\r\n");
2982 url_fprintf(c->pb, "\r\n");
2986 /* find an rtp connection by using the session ID. Check consistency
2988 static HTTPContext *find_rtp_session_with_url(const char *url,
2989 const char *session_id)
2997 rtp_c = find_rtp_session(session_id);
3001 /* find which url is asked */
3002 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3006 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3007 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3008 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3009 rtp_c->stream->filename, s);
3010 if(!strncmp(path, buf, sizeof(buf))) {
3011 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3018 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3022 rtp_c = find_rtp_session_with_url(url, h->session_id);
3024 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3028 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3029 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3030 rtp_c->state != HTTPSTATE_READY) {
3031 rtsp_reply_error(c, RTSP_STATUS_STATE);
3036 /* XXX: seek in stream */
3037 if (h->range_start != AV_NOPTS_VALUE) {
3038 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3039 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3043 rtp_c->state = HTTPSTATE_SEND_DATA;
3045 /* now everything is OK, so we can send the connection parameters */
3046 rtsp_reply_header(c, RTSP_STATUS_OK);
3048 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3049 url_fprintf(c->pb, "\r\n");
3052 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3056 rtp_c = find_rtp_session_with_url(url, h->session_id);
3058 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3062 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3063 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3064 rtsp_reply_error(c, RTSP_STATUS_STATE);
3068 rtp_c->state = HTTPSTATE_READY;
3069 rtp_c->first_pts = AV_NOPTS_VALUE;
3070 /* now everything is OK, so we can send the connection parameters */
3071 rtsp_reply_header(c, RTSP_STATUS_OK);
3073 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3074 url_fprintf(c->pb, "\r\n");
3077 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3080 char session_id[32];
3082 rtp_c = find_rtp_session_with_url(url, h->session_id);
3084 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3088 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3090 /* abort the session */
3091 close_connection(rtp_c);
3093 /* now everything is OK, so we can send the connection parameters */
3094 rtsp_reply_header(c, RTSP_STATUS_OK);
3096 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3097 url_fprintf(c->pb, "\r\n");
3101 /********************************************************************/
3104 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3105 FFStream *stream, const char *session_id,
3106 enum RTSPLowerTransport rtp_protocol)
3108 HTTPContext *c = NULL;
3109 const char *proto_str;
3111 /* XXX: should output a warning page when coming
3112 close to the connection limit */
3113 if (nb_connections >= nb_max_connections)
3116 /* add a new connection */
3117 c = av_mallocz(sizeof(HTTPContext));
3122 c->poll_entry = NULL;
3123 c->from_addr = *from_addr;
3124 c->buffer_size = IOBUFFER_INIT_SIZE;
3125 c->buffer = av_malloc(c->buffer_size);
3130 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3131 c->state = HTTPSTATE_READY;
3132 c->is_packetized = 1;
3133 c->rtp_protocol = rtp_protocol;
3135 /* protocol is shown in statistics */
3136 switch(c->rtp_protocol) {
3137 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3138 proto_str = "MCAST";
3140 case RTSP_LOWER_TRANSPORT_UDP:
3143 case RTSP_LOWER_TRANSPORT_TCP:
3150 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3151 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3153 current_bandwidth += stream->bandwidth;
3155 c->next = first_http_ctx;
3167 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3168 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3170 static int rtp_new_av_stream(HTTPContext *c,
3171 int stream_index, struct sockaddr_in *dest_addr,
3172 HTTPContext *rtsp_c)
3174 AVFormatContext *ctx;
3177 URLContext *h = NULL;
3179 int max_packet_size;
3181 /* now we can open the relevant output stream */
3182 ctx = avformat_alloc_context();
3185 ctx->oformat = guess_format("rtp", NULL, NULL);
3187 st = av_mallocz(sizeof(AVStream));
3190 st->codec= avcodec_alloc_context();
3191 ctx->nb_streams = 1;
3192 ctx->streams[0] = st;
3194 if (!c->stream->feed ||
3195 c->stream->feed == c->stream)
3196 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3199 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3201 st->priv_data = NULL;
3203 /* build destination RTP address */
3204 ipaddr = inet_ntoa(dest_addr->sin_addr);
3206 switch(c->rtp_protocol) {
3207 case RTSP_LOWER_TRANSPORT_UDP:
3208 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3211 /* XXX: also pass as parameter to function ? */
3212 if (c->stream->is_multicast) {
3214 ttl = c->stream->multicast_ttl;
3217 snprintf(ctx->filename, sizeof(ctx->filename),
3218 "rtp://%s:%d?multicast=1&ttl=%d",
3219 ipaddr, ntohs(dest_addr->sin_port), ttl);
3221 snprintf(ctx->filename, sizeof(ctx->filename),
3222 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3225 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3227 c->rtp_handles[stream_index] = h;
3228 max_packet_size = url_get_max_packet_size(h);
3230 case RTSP_LOWER_TRANSPORT_TCP:
3233 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3239 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3240 ipaddr, ntohs(dest_addr->sin_port),
3241 c->stream->filename, stream_index, c->protocol);
3243 /* normally, no packets should be output here, but the packet size may be checked */
3244 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3245 /* XXX: close stream */
3248 av_set_parameters(ctx, NULL);
3249 if (av_write_header(ctx) < 0) {
3256 url_close_dyn_buf(ctx->pb, &dummy_buf);
3259 c->rtp_ctx[stream_index] = ctx;
3263 /********************************************************************/
3264 /* ffserver initialization */
3266 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3270 fst = av_mallocz(sizeof(AVStream));
3273 fst->codec= avcodec_alloc_context();
3274 fst->priv_data = av_mallocz(sizeof(FeedData));
3275 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3276 fst->index = stream->nb_streams;
3277 av_set_pts_info(fst, 33, 1, 90000);
3278 stream->streams[stream->nb_streams++] = fst;
3282 /* return the stream number in the feed */
3283 static int add_av_stream(FFStream *feed, AVStream *st)
3286 AVCodecContext *av, *av1;
3290 for(i=0;i<feed->nb_streams;i++) {
3291 st = feed->streams[i];
3293 if (av1->codec_id == av->codec_id &&
3294 av1->codec_type == av->codec_type &&
3295 av1->bit_rate == av->bit_rate) {
3297 switch(av->codec_type) {
3298 case CODEC_TYPE_AUDIO:
3299 if (av1->channels == av->channels &&
3300 av1->sample_rate == av->sample_rate)
3303 case CODEC_TYPE_VIDEO:
3304 if (av1->width == av->width &&
3305 av1->height == av->height &&
3306 av1->time_base.den == av->time_base.den &&
3307 av1->time_base.num == av->time_base.num &&
3308 av1->gop_size == av->gop_size)
3317 fst = add_av_stream1(feed, av);
3320 return feed->nb_streams - 1;
3325 static void remove_stream(FFStream *stream)
3329 while (*ps != NULL) {
3337 /* specific mpeg4 handling : we extract the raw parameters */
3338 static void extract_mpeg4_header(AVFormatContext *infile)
3340 int mpeg4_count, i, size;
3346 for(i=0;i<infile->nb_streams;i++) {
3347 st = infile->streams[i];
3348 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3349 st->codec->extradata_size == 0) {
3356 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3357 while (mpeg4_count > 0) {
3358 if (av_read_packet(infile, &pkt) < 0)
3360 st = infile->streams[pkt.stream_index];
3361 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3362 st->codec->extradata_size == 0) {
3363 av_freep(&st->codec->extradata);
3364 /* fill extradata with the header */
3365 /* XXX: we make hard suppositions here ! */
3367 while (p < pkt.data + pkt.size - 4) {
3368 /* stop when vop header is found */
3369 if (p[0] == 0x00 && p[1] == 0x00 &&
3370 p[2] == 0x01 && p[3] == 0xb6) {
3371 size = p - pkt.data;
3372 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3373 st->codec->extradata = av_malloc(size);
3374 st->codec->extradata_size = size;
3375 memcpy(st->codec->extradata, pkt.data, size);
3382 av_free_packet(&pkt);
3386 /* compute the needed AVStream for each file */
3387 static void build_file_streams(void)
3389 FFStream *stream, *stream_next;
3390 AVFormatContext *infile;
3393 /* gather all streams */
3394 for(stream = first_stream; stream != NULL; stream = stream_next) {
3395 stream_next = stream->next;
3396 if (stream->stream_type == STREAM_TYPE_LIVE &&
3398 /* the stream comes from a file */
3399 /* try to open the file */
3401 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3402 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3403 /* specific case : if transport stream output to RTP,
3404 we use a raw transport stream reader */
3405 stream->ap_in->mpeg2ts_raw = 1;
3406 stream->ap_in->mpeg2ts_compute_pcr = 1;
3409 http_log("Opening file '%s'\n", stream->feed_filename);
3410 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3411 stream->ifmt, 0, stream->ap_in)) < 0) {
3412 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3413 /* remove stream (no need to spend more time on it) */
3415 remove_stream(stream);
3417 /* find all the AVStreams inside and reference them in
3419 if (av_find_stream_info(infile) < 0) {
3420 http_log("Could not find codec parameters from '%s'\n",
3421 stream->feed_filename);
3422 av_close_input_file(infile);
3425 extract_mpeg4_header(infile);
3427 for(i=0;i<infile->nb_streams;i++)
3428 add_av_stream1(stream, infile->streams[i]->codec);
3430 av_close_input_file(infile);
3436 /* compute the needed AVStream for each feed */
3437 static void build_feed_streams(void)
3439 FFStream *stream, *feed;
3442 /* gather all streams */
3443 for(stream = first_stream; stream != NULL; stream = stream->next) {
3444 feed = stream->feed;
3446 if (!stream->is_feed) {
3447 /* we handle a stream coming from a feed */
3448 for(i=0;i<stream->nb_streams;i++)
3449 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3454 /* gather all streams */
3455 for(stream = first_stream; stream != NULL; stream = stream->next) {
3456 feed = stream->feed;
3458 if (stream->is_feed) {
3459 for(i=0;i<stream->nb_streams;i++)
3460 stream->feed_streams[i] = i;
3465 /* create feed files if needed */
3466 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3469 if (url_exist(feed->feed_filename)) {
3470 /* See if it matches */
3474 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3475 /* Now see if it matches */
3476 if (s->nb_streams == feed->nb_streams) {
3478 for(i=0;i<s->nb_streams;i++) {
3480 sf = feed->streams[i];
3483 if (sf->index != ss->index ||
3485 http_log("Index & Id do not match for stream %d (%s)\n",
3486 i, feed->feed_filename);
3489 AVCodecContext *ccf, *ccs;
3493 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3495 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3496 http_log("Codecs do not match for stream %d\n", i);
3498 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3499 http_log("Codec bitrates do not match for stream %d\n", i);
3501 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3502 if (CHECK_CODEC(time_base.den) ||
3503 CHECK_CODEC(time_base.num) ||
3504 CHECK_CODEC(width) ||
3505 CHECK_CODEC(height)) {
3506 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3509 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3510 if (CHECK_CODEC(sample_rate) ||
3511 CHECK_CODEC(channels) ||
3512 CHECK_CODEC(frame_size)) {
3513 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3517 http_log("Unknown codec type\n");
3525 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3526 feed->feed_filename, s->nb_streams, feed->nb_streams);
3528 av_close_input_file(s);
3530 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3531 feed->feed_filename);
3534 if (feed->readonly) {
3535 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3536 feed->feed_filename);
3539 unlink(feed->feed_filename);
3542 if (!url_exist(feed->feed_filename)) {
3543 AVFormatContext s1 = {0}, *s = &s1;
3545 if (feed->readonly) {
3546 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3547 feed->feed_filename);
3551 /* only write the header of the ffm file */
3552 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3553 http_log("Could not open output feed file '%s'\n",
3554 feed->feed_filename);
3557 s->oformat = feed->fmt;
3558 s->nb_streams = feed->nb_streams;
3559 for(i=0;i<s->nb_streams;i++) {
3561 st = feed->streams[i];
3564 av_set_parameters(s, NULL);
3565 if (av_write_header(s) < 0) {
3566 http_log("Container doesn't supports the required parameters\n");
3569 /* XXX: need better api */
3570 av_freep(&s->priv_data);
3573 /* get feed size and write index */
3574 fd = open(feed->feed_filename, O_RDONLY);
3576 http_log("Could not open output feed file '%s'\n",
3577 feed->feed_filename);
3581 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3582 feed->feed_size = lseek(fd, 0, SEEK_END);
3583 /* ensure that we do not wrap before the end of file */
3584 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3585 feed->feed_max_size = feed->feed_size;
3591 /* compute the bandwidth used by each stream */
3592 static void compute_bandwidth(void)
3598 for(stream = first_stream; stream != NULL; stream = stream->next) {
3600 for(i=0;i<stream->nb_streams;i++) {
3601 AVStream *st = stream->streams[i];
3602 switch(st->codec->codec_type) {
3603 case CODEC_TYPE_AUDIO:
3604 case CODEC_TYPE_VIDEO:
3605 bandwidth += st->codec->bit_rate;
3611 stream->bandwidth = (bandwidth + 999) / 1000;
3615 static void get_arg(char *buf, int buf_size, const char **pp)
3622 while (isspace(*p)) p++;
3625 if (*p == '\"' || *p == '\'')
3637 if ((q - buf) < buf_size - 1)
3642 if (quote && *p == quote)
3647 /* add a codec and set the default parameters */
3648 static void add_codec(FFStream *stream, AVCodecContext *av)
3652 /* compute default parameters */
3653 switch(av->codec_type) {
3654 case CODEC_TYPE_AUDIO:
3655 if (av->bit_rate == 0)
3656 av->bit_rate = 64000;
3657 if (av->sample_rate == 0)
3658 av->sample_rate = 22050;
3659 if (av->channels == 0)
3662 case CODEC_TYPE_VIDEO:
3663 if (av->bit_rate == 0)
3664 av->bit_rate = 64000;
3665 if (av->time_base.num == 0){
3666 av->time_base.den = 5;
3667 av->time_base.num = 1;
3669 if (av->width == 0 || av->height == 0) {
3673 /* Bitrate tolerance is less for streaming */
3674 if (av->bit_rate_tolerance == 0)
3675 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3676 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3681 if (av->max_qdiff == 0)
3683 av->qcompress = 0.5;
3686 if (!av->nsse_weight)
3687 av->nsse_weight = 8;
3689 av->frame_skip_cmp = FF_CMP_DCTMAX;
3690 av->me_method = ME_EPZS;
3691 av->rc_buffer_aggressivity = 1.0;
3694 av->rc_eq = "tex^qComp";
3695 if (!av->i_quant_factor)
3696 av->i_quant_factor = -0.8;
3697 if (!av->b_quant_factor)
3698 av->b_quant_factor = 1.25;
3699 if (!av->b_quant_offset)
3700 av->b_quant_offset = 1.25;
3701 if (!av->rc_max_rate)
3702 av->rc_max_rate = av->bit_rate * 2;
3704 if (av->rc_max_rate && !av->rc_buffer_size) {
3705 av->rc_buffer_size = av->rc_max_rate;
3714 st = av_mallocz(sizeof(AVStream));
3717 st->codec = avcodec_alloc_context();
3718 stream->streams[stream->nb_streams++] = st;
3719 memcpy(st->codec, av, sizeof(AVCodecContext));
3722 static enum CodecID opt_audio_codec(const char *arg)
3724 AVCodec *p= avcodec_find_encoder_by_name(arg);
3726 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3727 return CODEC_ID_NONE;
3732 static enum CodecID opt_video_codec(const char *arg)
3734 AVCodec *p= avcodec_find_encoder_by_name(arg);
3736 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3737 return CODEC_ID_NONE;
3742 /* simplistic plugin support */
3745 static void load_module(const char *filename)
3748 void (*init_func)(void);
3749 dll = dlopen(filename, RTLD_NOW);
3751 fprintf(stderr, "Could not load module '%s' - %s\n",
3752 filename, dlerror());
3756 init_func = dlsym(dll, "ffserver_module_init");
3759 "%s: init function 'ffserver_module_init()' not found\n",
3768 static int ffserver_opt_default(const char *opt, const char *arg,
3769 AVCodecContext *avctx, int type)
3772 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
3774 ret = av_set_string3(avctx, opt, arg, 1, NULL);
3778 static int parse_ffconfig(const char *filename)
3785 int val, errors, line_num;
3786 FFStream **last_stream, *stream, *redirect;
3787 FFStream **last_feed, *feed, *s;
3788 AVCodecContext audio_enc, video_enc;
3789 enum CodecID audio_id, video_id;
3791 f = fopen(filename, "r");
3799 first_stream = NULL;
3800 last_stream = &first_stream;
3802 last_feed = &first_feed;
3806 audio_id = CODEC_ID_NONE;
3807 video_id = CODEC_ID_NONE;
3809 if (fgets(line, sizeof(line), f) == NULL)
3815 if (*p == '\0' || *p == '#')
3818 get_arg(cmd, sizeof(cmd), &p);
3820 if (!strcasecmp(cmd, "Port")) {
3821 get_arg(arg, sizeof(arg), &p);
3823 if (val < 1 || val > 65536) {
3824 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3825 filename, line_num, arg);
3828 my_http_addr.sin_port = htons(val);
3829 } else if (!strcasecmp(cmd, "BindAddress")) {
3830 get_arg(arg, sizeof(arg), &p);
3831 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3832 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3833 filename, line_num, arg);
3836 } else if (!strcasecmp(cmd, "NoDaemon")) {
3837 ffserver_daemon = 0;
3838 } else if (!strcasecmp(cmd, "RTSPPort")) {
3839 get_arg(arg, sizeof(arg), &p);
3841 if (val < 1 || val > 65536) {
3842 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3843 filename, line_num, arg);
3846 my_rtsp_addr.sin_port = htons(atoi(arg));
3847 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3848 get_arg(arg, sizeof(arg), &p);
3849 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3850 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3851 filename, line_num, arg);
3854 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
3855 get_arg(arg, sizeof(arg), &p);
3857 if (val < 1 || val > 65536) {
3858 fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n",
3859 filename, line_num, arg);
3862 nb_max_http_connections = val;
3863 } else if (!strcasecmp(cmd, "MaxClients")) {
3864 get_arg(arg, sizeof(arg), &p);
3866 if (val < 1 || val > nb_max_http_connections) {
3867 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3868 filename, line_num, arg);
3871 nb_max_connections = val;
3873 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3875 get_arg(arg, sizeof(arg), &p);
3877 if (llval < 10 || llval > 10000000) {
3878 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3879 filename, line_num, arg);
3882 max_bandwidth = llval;
3883 } else if (!strcasecmp(cmd, "CustomLog")) {
3884 if (!ffserver_debug)
3885 get_arg(logfilename, sizeof(logfilename), &p);
3886 } else if (!strcasecmp(cmd, "<Feed")) {
3887 /*********************************************/
3888 /* Feed related options */
3890 if (stream || feed) {
3891 fprintf(stderr, "%s:%d: Already in a tag\n",
3892 filename, line_num);
3894 feed = av_mallocz(sizeof(FFStream));
3895 get_arg(feed->filename, sizeof(feed->filename), &p);
3896 q = strrchr(feed->filename, '>');
3900 for (s = first_feed; s; s = s->next) {
3901 if (!strcmp(feed->filename, s->filename)) {
3902 fprintf(stderr, "%s:%d: Feed '%s' already registered\n",
3903 filename, line_num, s->filename);
3908 feed->fmt = guess_format("ffm", NULL, NULL);
3909 /* defaut feed file */
3910 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3911 "/tmp/%s.ffm", feed->filename);
3912 feed->feed_max_size = 5 * 1024 * 1024;
3914 feed->feed = feed; /* self feeding :-) */
3916 /* add in stream list */
3917 *last_stream = feed;
3918 last_stream = &feed->next;
3919 /* add in feed list */
3921 last_feed = &feed->next_feed;
3923 } else if (!strcasecmp(cmd, "Launch")) {
3927 feed->child_argv = av_mallocz(64 * sizeof(char *));
3929 for (i = 0; i < 62; i++) {
3930 get_arg(arg, sizeof(arg), &p);
3934 feed->child_argv[i] = av_strdup(arg);
3937 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3939 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3941 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3942 inet_ntoa(my_http_addr.sin_addr),
3943 ntohs(my_http_addr.sin_port), feed->filename);
3945 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3947 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3949 } else if (stream) {
3950 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3952 } else if (!strcasecmp(cmd, "File")) {
3954 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3956 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3957 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3962 get_arg(arg, sizeof(arg), &p);
3964 fsize = strtod(p1, &p1);
3965 switch(toupper(*p1)) {
3970 fsize *= 1024 * 1024;
3973 fsize *= 1024 * 1024 * 1024;
3976 feed->feed_max_size = (int64_t)fsize;
3978 } else if (!strcasecmp(cmd, "</Feed>")) {
3980 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3981 filename, line_num);
3985 } else if (!strcasecmp(cmd, "<Stream")) {
3986 /*********************************************/
3987 /* Stream related options */
3989 if (stream || feed) {
3990 fprintf(stderr, "%s:%d: Already in a tag\n",
3991 filename, line_num);
3994 const AVClass *class;
3995 stream = av_mallocz(sizeof(FFStream));
3996 get_arg(stream->filename, sizeof(stream->filename), &p);
3997 q = strrchr(stream->filename, '>');
4001 for (s = first_stream; s; s = s->next) {
4002 if (!strcmp(stream->filename, s->filename)) {
4003 fprintf(stderr, "%s:%d: Stream '%s' already registered\n",
4004 filename, line_num, s->filename);
4009 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
4010 /* fetch avclass so AVOption works
4011 * FIXME try to use avcodec_get_context_defaults2
4012 * without changing defaults too much */
4013 avcodec_get_context_defaults(&video_enc);
4014 class = video_enc.av_class;
4015 memset(&audio_enc, 0, sizeof(AVCodecContext));
4016 memset(&video_enc, 0, sizeof(AVCodecContext));
4017 audio_enc.av_class = class;
4018 video_enc.av_class = class;
4019 audio_id = CODEC_ID_NONE;
4020 video_id = CODEC_ID_NONE;
4022 audio_id = stream->fmt->audio_codec;
4023 video_id = stream->fmt->video_codec;
4026 *last_stream = stream;
4027 last_stream = &stream->next;
4029 } else if (!strcasecmp(cmd, "Feed")) {
4030 get_arg(arg, sizeof(arg), &p);
4035 while (sfeed != NULL) {
4036 if (!strcmp(sfeed->filename, arg))
4038 sfeed = sfeed->next_feed;
4041 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
4042 filename, line_num, arg);
4044 stream->feed = sfeed;
4046 } else if (!strcasecmp(cmd, "Format")) {
4047 get_arg(arg, sizeof(arg), &p);
4049 if (!strcmp(arg, "status")) {
4050 stream->stream_type = STREAM_TYPE_STATUS;
4053 stream->stream_type = STREAM_TYPE_LIVE;
4054 /* jpeg cannot be used here, so use single frame jpeg */
4055 if (!strcmp(arg, "jpeg"))
4056 strcpy(arg, "mjpeg");
4057 stream->fmt = guess_stream_format(arg, NULL, NULL);
4059 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4060 filename, line_num, arg);
4065 audio_id = stream->fmt->audio_codec;
4066 video_id = stream->fmt->video_codec;
4069 } else if (!strcasecmp(cmd, "InputFormat")) {
4070 get_arg(arg, sizeof(arg), &p);
4072 stream->ifmt = av_find_input_format(arg);
4073 if (!stream->ifmt) {
4074 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4075 filename, line_num, arg);
4078 } else if (!strcasecmp(cmd, "FaviconURL")) {
4079 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4080 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4082 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4083 filename, line_num);
4086 } else if (!strcasecmp(cmd, "Author")) {
4088 get_arg(stream->author, sizeof(stream->author), &p);
4089 } else if (!strcasecmp(cmd, "Comment")) {
4091 get_arg(stream->comment, sizeof(stream->comment), &p);
4092 } else if (!strcasecmp(cmd, "Copyright")) {
4094 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4095 } else if (!strcasecmp(cmd, "Title")) {
4097 get_arg(stream->title, sizeof(stream->title), &p);
4098 } else if (!strcasecmp(cmd, "Preroll")) {
4099 get_arg(arg, sizeof(arg), &p);
4101 stream->prebuffer = atof(arg) * 1000;
4102 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4104 stream->send_on_key = 1;
4105 } else if (!strcasecmp(cmd, "AudioCodec")) {
4106 get_arg(arg, sizeof(arg), &p);
4107 audio_id = opt_audio_codec(arg);
4108 if (audio_id == CODEC_ID_NONE) {
4109 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4110 filename, line_num, arg);
4113 } else if (!strcasecmp(cmd, "VideoCodec")) {
4114 get_arg(arg, sizeof(arg), &p);
4115 video_id = opt_video_codec(arg);
4116 if (video_id == CODEC_ID_NONE) {
4117 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4118 filename, line_num, arg);
4121 } else if (!strcasecmp(cmd, "MaxTime")) {
4122 get_arg(arg, sizeof(arg), &p);
4124 stream->max_time = atof(arg) * 1000;
4125 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4126 get_arg(arg, sizeof(arg), &p);
4128 audio_enc.bit_rate = atoi(arg) * 1000;
4129 } else if (!strcasecmp(cmd, "AudioChannels")) {
4130 get_arg(arg, sizeof(arg), &p);
4132 audio_enc.channels = atoi(arg);
4133 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4134 get_arg(arg, sizeof(arg), &p);
4136 audio_enc.sample_rate = atoi(arg);
4137 } else if (!strcasecmp(cmd, "AudioQuality")) {
4138 get_arg(arg, sizeof(arg), &p);
4140 // audio_enc.quality = atof(arg) * 1000;
4142 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4144 int minrate, maxrate;
4146 get_arg(arg, sizeof(arg), &p);
4148 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4149 video_enc.rc_min_rate = minrate * 1000;
4150 video_enc.rc_max_rate = maxrate * 1000;
4152 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4153 filename, line_num, arg);
4157 } else if (!strcasecmp(cmd, "Debug")) {
4159 get_arg(arg, sizeof(arg), &p);
4160 video_enc.debug = strtol(arg,0,0);
4162 } else if (!strcasecmp(cmd, "Strict")) {
4164 get_arg(arg, sizeof(arg), &p);
4165 video_enc.strict_std_compliance = atoi(arg);
4167 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4169 get_arg(arg, sizeof(arg), &p);
4170 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4172 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4174 get_arg(arg, sizeof(arg), &p);
4175 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4177 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4178 get_arg(arg, sizeof(arg), &p);
4180 video_enc.bit_rate = atoi(arg) * 1000;
4182 } else if (!strcasecmp(cmd, "VideoSize")) {
4183 get_arg(arg, sizeof(arg), &p);
4185 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4186 if ((video_enc.width % 16) != 0 ||
4187 (video_enc.height % 16) != 0) {
4188 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4189 filename, line_num);
4193 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4194 get_arg(arg, sizeof(arg), &p);
4196 AVRational frame_rate;
4197 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4198 fprintf(stderr, "Incorrect frame rate\n");
4201 video_enc.time_base.num = frame_rate.den;
4202 video_enc.time_base.den = frame_rate.num;
4205 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4206 get_arg(arg, sizeof(arg), &p);
4208 video_enc.gop_size = atoi(arg);
4209 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4211 video_enc.gop_size = 1;
4212 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4214 video_enc.mb_decision = FF_MB_DECISION_BITS;
4215 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4217 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4218 video_enc.flags |= CODEC_FLAG_4MV;
4220 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4221 !strcasecmp(cmd, "AVOptionAudio")) {
4223 AVCodecContext *avctx;
4225 get_arg(arg, sizeof(arg), &p);
4226 get_arg(arg2, sizeof(arg2), &p);
4227 if (!strcasecmp(cmd, "AVOptionVideo")) {
4229 type = AV_OPT_FLAG_VIDEO_PARAM;
4232 type = AV_OPT_FLAG_AUDIO_PARAM;
4234 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4235 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4238 } else if (!strcasecmp(cmd, "VideoTag")) {
4239 get_arg(arg, sizeof(arg), &p);
4240 if ((strlen(arg) == 4) && stream)
4241 video_enc.codec_tag = AV_RL32(arg);
4242 } else if (!strcasecmp(cmd, "BitExact")) {
4244 video_enc.flags |= CODEC_FLAG_BITEXACT;
4245 } else if (!strcasecmp(cmd, "DctFastint")) {
4247 video_enc.dct_algo = FF_DCT_FASTINT;
4248 } else if (!strcasecmp(cmd, "IdctSimple")) {
4250 video_enc.idct_algo = FF_IDCT_SIMPLE;
4251 } else if (!strcasecmp(cmd, "Qscale")) {
4252 get_arg(arg, sizeof(arg), &p);
4254 video_enc.flags |= CODEC_FLAG_QSCALE;
4255 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4257 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4258 get_arg(arg, sizeof(arg), &p);
4260 video_enc.max_qdiff = atoi(arg);
4261 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4262 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4263 filename, line_num);
4267 } else if (!strcasecmp(cmd, "VideoQMax")) {
4268 get_arg(arg, sizeof(arg), &p);
4270 video_enc.qmax = atoi(arg);
4271 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4272 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4273 filename, line_num);
4277 } else if (!strcasecmp(cmd, "VideoQMin")) {
4278 get_arg(arg, sizeof(arg), &p);
4280 video_enc.qmin = atoi(arg);
4281 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4282 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4283 filename, line_num);
4287 } else if (!strcasecmp(cmd, "LumaElim")) {
4288 get_arg(arg, sizeof(arg), &p);
4290 video_enc.luma_elim_threshold = atoi(arg);
4291 } else if (!strcasecmp(cmd, "ChromaElim")) {
4292 get_arg(arg, sizeof(arg), &p);
4294 video_enc.chroma_elim_threshold = atoi(arg);
4295 } else if (!strcasecmp(cmd, "LumiMask")) {
4296 get_arg(arg, sizeof(arg), &p);
4298 video_enc.lumi_masking = atof(arg);
4299 } else if (!strcasecmp(cmd, "DarkMask")) {
4300 get_arg(arg, sizeof(arg), &p);
4302 video_enc.dark_masking = atof(arg);
4303 } else if (!strcasecmp(cmd, "NoVideo")) {
4304 video_id = CODEC_ID_NONE;
4305 } else if (!strcasecmp(cmd, "NoAudio")) {
4306 audio_id = CODEC_ID_NONE;
4307 } else if (!strcasecmp(cmd, "ACL")) {
4310 get_arg(arg, sizeof(arg), &p);
4311 if (strcasecmp(arg, "allow") == 0)
4312 acl.action = IP_ALLOW;
4313 else if (strcasecmp(arg, "deny") == 0)
4314 acl.action = IP_DENY;
4316 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4317 filename, line_num, arg);
4321 get_arg(arg, sizeof(arg), &p);
4323 if (resolve_host(&acl.first, arg) != 0) {
4324 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4325 filename, line_num, arg);
4328 acl.last = acl.first;
4330 get_arg(arg, sizeof(arg), &p);
4333 if (resolve_host(&acl.last, arg) != 0) {
4334 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4335 filename, line_num, arg);
4341 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4342 IPAddressACL **naclp = 0;
4348 naclp = &stream->acl;
4352 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4353 filename, line_num);
4359 naclp = &(*naclp)->next;
4364 } else if (!strcasecmp(cmd, "RTSPOption")) {
4365 get_arg(arg, sizeof(arg), &p);
4367 av_freep(&stream->rtsp_option);
4368 stream->rtsp_option = av_strdup(arg);
4370 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4371 get_arg(arg, sizeof(arg), &p);
4373 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4374 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4375 filename, line_num, arg);
4378 stream->is_multicast = 1;
4379 stream->loop = 1; /* default is looping */
4381 } else if (!strcasecmp(cmd, "MulticastPort")) {
4382 get_arg(arg, sizeof(arg), &p);
4384 stream->multicast_port = atoi(arg);
4385 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4386 get_arg(arg, sizeof(arg), &p);
4388 stream->multicast_ttl = atoi(arg);
4389 } else if (!strcasecmp(cmd, "NoLoop")) {
4392 } else if (!strcasecmp(cmd, "</Stream>")) {
4394 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4395 filename, line_num);
4398 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4399 if (audio_id != CODEC_ID_NONE) {
4400 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4401 audio_enc.codec_id = audio_id;
4402 add_codec(stream, &audio_enc);
4404 if (video_id != CODEC_ID_NONE) {
4405 video_enc.codec_type = CODEC_TYPE_VIDEO;
4406 video_enc.codec_id = video_id;
4407 add_codec(stream, &video_enc);
4412 } else if (!strcasecmp(cmd, "<Redirect")) {
4413 /*********************************************/
4415 if (stream || feed || redirect) {
4416 fprintf(stderr, "%s:%d: Already in a tag\n",
4417 filename, line_num);
4420 redirect = av_mallocz(sizeof(FFStream));
4421 *last_stream = redirect;
4422 last_stream = &redirect->next;
4424 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4425 q = strrchr(redirect->filename, '>');
4428 redirect->stream_type = STREAM_TYPE_REDIRECT;
4430 } else if (!strcasecmp(cmd, "URL")) {
4432 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4433 } else if (!strcasecmp(cmd, "</Redirect>")) {
4435 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4436 filename, line_num);
4439 if (!redirect->feed_filename[0]) {
4440 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4441 filename, line_num);
4446 } else if (!strcasecmp(cmd, "LoadModule")) {
4447 get_arg(arg, sizeof(arg), &p);
4451 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4452 filename, line_num, arg);
4456 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4457 filename, line_num, cmd);
4468 static void handle_child_exit(int sig)
4473 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4476 for (feed = first_feed; feed; feed = feed->next) {
4477 if (feed->pid == pid) {
4478 int uptime = time(0) - feed->pid_start;
4481 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4484 /* Turn off any more restarts */
4485 feed->child_argv = 0;
4490 need_to_start_children = 1;
4493 static void opt_debug(void)
4496 ffserver_daemon = 0;
4497 logfilename[0] = '-';
4500 static void opt_show_help(void)
4502 printf("usage: ffserver [options]\n"
4503 "Hyper fast multi format Audio/Video streaming server\n");
4505 show_help_options(options, "Main options:\n", 0, 0);
4508 static const OptionDef options[] = {
4509 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4510 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4511 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4512 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4513 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4514 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4515 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4519 int main(int argc, char **argv)
4521 struct sigaction sigact;
4527 config_filename = "/etc/ffserver.conf";
4529 my_program_name = argv[0];
4530 my_program_dir = getcwd(0, 0);
4531 ffserver_daemon = 1;
4533 parse_options(argc, argv, options, NULL);
4535 unsetenv("http_proxy"); /* Kill the http_proxy */
4537 av_lfg_init(&random_state, ff_random_get_seed());
4539 memset(&sigact, 0, sizeof(sigact));
4540 sigact.sa_handler = handle_child_exit;
4541 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4542 sigaction(SIGCHLD, &sigact, 0);
4544 if (parse_ffconfig(config_filename) < 0) {
4545 fprintf(stderr, "Incorrect config file - exiting.\n");
4549 /* open log file if needed */
4550 if (logfilename[0] != '\0') {
4551 if (!strcmp(logfilename, "-"))
4554 logfile = fopen(logfilename, "a");
4555 av_log_set_callback(http_av_log);
4558 build_file_streams();
4560 build_feed_streams();
4562 compute_bandwidth();
4564 /* put the process in background and detach it from its TTY */
4565 if (ffserver_daemon) {
4572 } else if (pid > 0) {
4579 open("/dev/null", O_RDWR);
4580 if (strcmp(logfilename, "-") != 0) {
4590 signal(SIGPIPE, SIG_IGN);
4592 if (ffserver_daemon)
4595 if (http_server() < 0) {
4596 http_log("Could not start server\n");