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 "libavcodec/opt.h"
44 #include <sys/ioctl.h>
59 const char program_name[] = "FFserver";
60 const int program_birth_year = 2000;
62 static const OptionDef options[];
65 HTTPSTATE_WAIT_REQUEST,
66 HTTPSTATE_SEND_HEADER,
67 HTTPSTATE_SEND_DATA_HEADER,
68 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
69 HTTPSTATE_SEND_DATA_TRAILER,
70 HTTPSTATE_RECEIVE_DATA,
71 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
74 RTSPSTATE_WAIT_REQUEST,
76 RTSPSTATE_SEND_PACKET,
79 static const char *http_state[] = {
95 #define IOBUFFER_INIT_SIZE 8192
97 /* timeouts are in ms */
98 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
99 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
101 #define SYNC_TIMEOUT (10 * 1000)
103 typedef struct RTSPActionServerSetup {
105 char transport_option[512];
106 } RTSPActionServerSetup;
109 int64_t count1, count2;
110 int64_t time1, time2;
113 /* context associated with one connection */
114 typedef struct HTTPContext {
115 enum HTTPState state;
116 int fd; /* socket file descriptor */
117 struct sockaddr_in from_addr; /* origin */
118 struct pollfd *poll_entry; /* used when polling */
120 uint8_t *buffer_ptr, *buffer_end;
123 int chunked_encoding;
124 int chunk_size; /* 0 if it needs to be read */
125 struct HTTPContext *next;
126 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
130 /* input format handling */
131 AVFormatContext *fmt_in;
132 int64_t start_time; /* In milliseconds - this wraps fairly often */
133 int64_t first_pts; /* initial pts value */
134 int64_t cur_pts; /* current pts value from the stream in us */
135 int64_t cur_frame_duration; /* duration of the current frame in us */
136 int cur_frame_bytes; /* output frame size, needed to compute
137 the time at which we send each
139 int pts_stream_index; /* stream we choose as clock reference */
140 int64_t cur_clock; /* current clock reference value in us */
141 /* output format handling */
142 struct FFStream *stream;
143 /* -1 is invalid stream */
144 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
145 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
147 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
148 int last_packet_sent; /* true if last data packet was sent */
150 DataRateData datarate;
157 int is_packetized; /* if true, the stream is packetized */
158 int packet_stream_index; /* current stream for output in state machine */
160 /* RTSP state specific */
161 uint8_t *pb_buffer; /* XXX: use that in all the code */
163 int seq; /* RTSP sequence number */
165 /* RTP state specific */
166 enum RTSPLowerTransport rtp_protocol;
167 char session_id[32]; /* session id */
168 AVFormatContext *rtp_ctx[MAX_STREAMS];
170 /* RTP/UDP specific */
171 URLContext *rtp_handles[MAX_STREAMS];
173 /* RTP/TCP specific */
174 struct HTTPContext *rtsp_c;
175 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
178 /* each generated stream is described here */
182 STREAM_TYPE_REDIRECT,
185 enum IPAddressAction {
190 typedef struct IPAddressACL {
191 struct IPAddressACL *next;
192 enum IPAddressAction action;
193 /* These are in host order */
194 struct in_addr first;
198 /* description of each stream of the ffserver.conf file */
199 typedef struct FFStream {
200 enum StreamType stream_type;
201 char filename[1024]; /* stream filename */
202 struct FFStream *feed; /* feed we are using (can be null if
204 AVFormatParameters *ap_in; /* input parameters */
205 AVInputFormat *ifmt; /* if non NULL, force input format */
209 int prebuffer; /* Number of millseconds early to start */
210 int64_t max_time; /* Number of milliseconds to run */
212 AVStream *streams[MAX_STREAMS];
213 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
214 char feed_filename[1024]; /* file name of the feed storage, or
215 input file name for a stream */
220 pid_t pid; /* Of ffmpeg process */
221 time_t pid_start; /* Of ffmpeg process */
223 struct FFStream *next;
224 unsigned bandwidth; /* bandwidth, in kbits/s */
227 /* multicast specific */
229 struct in_addr multicast_ip;
230 int multicast_port; /* first port used for multicast */
232 int loop; /* if true, send the stream in loops (only meaningful if file) */
235 int feed_opened; /* true if someone is writing to the feed */
236 int is_feed; /* true if it is a feed */
237 int readonly; /* True if writing is prohibited to the file */
238 int truncate; /* True if feeder connection truncate the feed file */
240 int64_t bytes_served;
241 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
242 int64_t feed_write_index; /* current write position in feed (it wraps around) */
243 int64_t feed_size; /* current size of feed */
244 struct FFStream *next_feed;
247 typedef struct FeedData {
248 long long data_count;
249 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
252 static struct sockaddr_in my_http_addr;
253 static struct sockaddr_in my_rtsp_addr;
255 static char logfilename[1024];
256 static HTTPContext *first_http_ctx;
257 static FFStream *first_feed; /* contains only feeds */
258 static FFStream *first_stream; /* contains all streams, including feeds */
260 static void new_connection(int server_fd, int is_rtsp);
261 static void close_connection(HTTPContext *c);
264 static int handle_connection(HTTPContext *c);
265 static int http_parse_request(HTTPContext *c);
266 static int http_send_data(HTTPContext *c);
267 static void compute_status(HTTPContext *c);
268 static int open_input_stream(HTTPContext *c, const char *info);
269 static int http_start_receive_data(HTTPContext *c);
270 static int http_receive_data(HTTPContext *c);
273 static int rtsp_parse_request(HTTPContext *c);
274 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
275 static void rtsp_cmd_options(HTTPContext *c, const char *url);
276 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
277 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
278 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
279 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
282 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
283 struct in_addr my_ip);
286 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
287 FFStream *stream, const char *session_id,
288 enum RTSPLowerTransport rtp_protocol);
289 static int rtp_new_av_stream(HTTPContext *c,
290 int stream_index, struct sockaddr_in *dest_addr,
291 HTTPContext *rtsp_c);
293 static const char *my_program_name;
294 static const char *my_program_dir;
296 static const char *config_filename;
297 static int ffserver_debug;
298 static int ffserver_daemon;
299 static int no_launch;
300 static int need_to_start_children;
302 /* maximum number of simultaneous HTTP connections */
303 static unsigned int nb_max_http_connections = 2000;
304 static unsigned int nb_max_connections = 5;
305 static unsigned int nb_connections;
307 static uint64_t max_bandwidth = 1000;
308 static uint64_t current_bandwidth;
310 static int64_t cur_time; // Making this global saves on passing it around everywhere
312 static AVLFG random_state;
314 static FILE *logfile = NULL;
316 static char *ctime1(char *buf2)
324 p = buf2 + strlen(p) - 1;
330 static void http_vlog(const char *fmt, va_list vargs)
332 static int print_prefix = 1;
337 fprintf(logfile, "%s ", buf);
339 print_prefix = strstr(fmt, "\n") != NULL;
340 vfprintf(logfile, fmt, vargs);
345 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
348 va_start(vargs, fmt);
349 http_vlog(fmt, vargs);
353 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
355 static int print_prefix = 1;
356 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
357 if (level > av_log_get_level())
359 if (print_prefix && avc)
360 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
361 print_prefix = strstr(fmt, "\n") != NULL;
362 http_vlog(fmt, vargs);
365 static void log_connection(HTTPContext *c)
370 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
371 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
372 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
375 static void update_datarate(DataRateData *drd, int64_t count)
377 if (!drd->time1 && !drd->count1) {
378 drd->time1 = drd->time2 = cur_time;
379 drd->count1 = drd->count2 = count;
380 } else if (cur_time - drd->time2 > 5000) {
381 drd->time1 = drd->time2;
382 drd->count1 = drd->count2;
383 drd->time2 = cur_time;
388 /* In bytes per second */
389 static int compute_datarate(DataRateData *drd, int64_t count)
391 if (cur_time == drd->time1)
394 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
398 static void start_children(FFStream *feed)
403 for (; feed; feed = feed->next) {
404 if (feed->child_argv && !feed->pid) {
405 feed->pid_start = time(0);
410 http_log("Unable to create children\n");
419 av_strlcpy(pathname, my_program_name, sizeof(pathname));
421 slash = strrchr(pathname, '/');
426 strcpy(slash, "ffmpeg");
428 http_log("Launch commandline: ");
429 http_log("%s ", pathname);
430 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
431 http_log("%s ", feed->child_argv[i]);
434 for (i = 3; i < 256; i++)
437 if (!ffserver_debug) {
438 i = open("/dev/null", O_RDWR);
447 /* This is needed to make relative pathnames work */
448 chdir(my_program_dir);
450 signal(SIGPIPE, SIG_DFL);
452 execvp(pathname, feed->child_argv);
460 /* open a listening socket */
461 static int socket_open_listen(struct sockaddr_in *my_addr)
465 server_fd = socket(AF_INET,SOCK_STREAM,0);
472 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
474 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
476 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
478 closesocket(server_fd);
482 if (listen (server_fd, 5) < 0) {
484 closesocket(server_fd);
487 ff_socket_nonblock(server_fd, 1);
492 /* start all multicast streams */
493 static void start_multicast(void)
498 struct sockaddr_in dest_addr;
499 int default_port, stream_index;
502 for(stream = first_stream; stream != NULL; stream = stream->next) {
503 if (stream->is_multicast) {
504 /* open the RTP connection */
505 snprintf(session_id, sizeof(session_id), "%08x%08x",
506 av_lfg_get(&random_state), av_lfg_get(&random_state));
508 /* choose a port if none given */
509 if (stream->multicast_port == 0) {
510 stream->multicast_port = default_port;
514 dest_addr.sin_family = AF_INET;
515 dest_addr.sin_addr = stream->multicast_ip;
516 dest_addr.sin_port = htons(stream->multicast_port);
518 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
519 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
523 if (open_input_stream(rtp_c, "") < 0) {
524 http_log("Could not open input stream for stream '%s'\n",
529 /* open each RTP stream */
530 for(stream_index = 0; stream_index < stream->nb_streams;
532 dest_addr.sin_port = htons(stream->multicast_port +
534 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
535 http_log("Could not open output stream '%s/streamid=%d'\n",
536 stream->filename, stream_index);
541 /* change state to send data */
542 rtp_c->state = HTTPSTATE_SEND_DATA;
547 /* main loop of the http server */
548 static int http_server(void)
550 int server_fd = 0, rtsp_server_fd = 0;
551 int ret, delay, delay1;
552 struct pollfd *poll_table, *poll_entry;
553 HTTPContext *c, *c_next;
555 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
556 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
560 if (my_http_addr.sin_port) {
561 server_fd = socket_open_listen(&my_http_addr);
566 if (my_rtsp_addr.sin_port) {
567 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
568 if (rtsp_server_fd < 0)
572 if (!rtsp_server_fd && !server_fd) {
573 http_log("HTTP and RTSP disabled.\n");
577 http_log("FFserver started.\n");
579 start_children(first_feed);
584 poll_entry = poll_table;
586 poll_entry->fd = server_fd;
587 poll_entry->events = POLLIN;
590 if (rtsp_server_fd) {
591 poll_entry->fd = rtsp_server_fd;
592 poll_entry->events = POLLIN;
596 /* wait for events on each HTTP handle */
603 case HTTPSTATE_SEND_HEADER:
604 case RTSPSTATE_SEND_REPLY:
605 case RTSPSTATE_SEND_PACKET:
606 c->poll_entry = poll_entry;
608 poll_entry->events = POLLOUT;
611 case HTTPSTATE_SEND_DATA_HEADER:
612 case HTTPSTATE_SEND_DATA:
613 case HTTPSTATE_SEND_DATA_TRAILER:
614 if (!c->is_packetized) {
615 /* for TCP, we output as much as we can (may need to put a limit) */
616 c->poll_entry = poll_entry;
618 poll_entry->events = POLLOUT;
621 /* when ffserver is doing the timing, we work by
622 looking at which packet need to be sent every
624 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
629 case HTTPSTATE_WAIT_REQUEST:
630 case HTTPSTATE_RECEIVE_DATA:
631 case HTTPSTATE_WAIT_FEED:
632 case RTSPSTATE_WAIT_REQUEST:
633 /* need to catch errors */
634 c->poll_entry = poll_entry;
636 poll_entry->events = POLLIN;/* Maybe this will work */
640 c->poll_entry = NULL;
646 /* wait for an event on one connection. We poll at least every
647 second to handle timeouts */
649 ret = poll(poll_table, poll_entry - poll_table, delay);
650 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
651 ff_neterrno() != FF_NETERROR(EINTR))
655 cur_time = av_gettime() / 1000;
657 if (need_to_start_children) {
658 need_to_start_children = 0;
659 start_children(first_feed);
662 /* now handle the events */
663 for(c = first_http_ctx; c != NULL; c = c_next) {
665 if (handle_connection(c) < 0) {
666 /* close and free the connection */
672 poll_entry = poll_table;
674 /* new HTTP connection request ? */
675 if (poll_entry->revents & POLLIN)
676 new_connection(server_fd, 0);
679 if (rtsp_server_fd) {
680 /* new RTSP connection request ? */
681 if (poll_entry->revents & POLLIN)
682 new_connection(rtsp_server_fd, 1);
687 /* start waiting for a new HTTP/RTSP request */
688 static void start_wait_request(HTTPContext *c, int is_rtsp)
690 c->buffer_ptr = c->buffer;
691 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
694 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
695 c->state = RTSPSTATE_WAIT_REQUEST;
697 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
698 c->state = HTTPSTATE_WAIT_REQUEST;
702 static void http_send_too_busy_reply(int fd)
705 int len = snprintf(buffer, sizeof(buffer),
706 "HTTP/1.0 200 Server too busy\r\n"
707 "Content-type: text/html\r\n"
709 "<html><head><title>Too busy</title></head><body>\r\n"
710 "<p>The server is too busy to serve your request at this time.</p>\r\n"
711 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
712 "</body></html>\r\n",
713 nb_connections, nb_max_connections);
714 send(fd, buffer, len, 0);
718 static void new_connection(int server_fd, int is_rtsp)
720 struct sockaddr_in from_addr;
722 HTTPContext *c = NULL;
724 len = sizeof(from_addr);
725 fd = accept(server_fd, (struct sockaddr *)&from_addr,
728 http_log("error during accept %s\n", strerror(errno));
731 ff_socket_nonblock(fd, 1);
733 if (nb_connections >= nb_max_connections) {
734 http_send_too_busy_reply(fd);
738 /* add a new connection */
739 c = av_mallocz(sizeof(HTTPContext));
744 c->poll_entry = NULL;
745 c->from_addr = from_addr;
746 c->buffer_size = IOBUFFER_INIT_SIZE;
747 c->buffer = av_malloc(c->buffer_size);
751 c->next = first_http_ctx;
755 start_wait_request(c, is_rtsp);
767 static void close_connection(HTTPContext *c)
769 HTTPContext **cp, *c1;
771 AVFormatContext *ctx;
775 /* remove connection from list */
776 cp = &first_http_ctx;
777 while ((*cp) != NULL) {
785 /* remove references, if any (XXX: do it faster) */
786 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
791 /* remove connection associated resources */
795 /* close each frame parser */
796 for(i=0;i<c->fmt_in->nb_streams;i++) {
797 st = c->fmt_in->streams[i];
798 if (st->codec->codec)
799 avcodec_close(st->codec);
801 av_close_input_file(c->fmt_in);
804 /* free RTP output streams if any */
807 nb_streams = c->stream->nb_streams;
809 for(i=0;i<nb_streams;i++) {
812 av_write_trailer(ctx);
815 h = c->rtp_handles[i];
822 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
825 if (url_open_dyn_buf(&ctx->pb) >= 0) {
826 av_write_trailer(ctx);
827 av_freep(&c->pb_buffer);
828 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
833 for(i=0; i<ctx->nb_streams; i++)
834 av_free(ctx->streams[i]);
836 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
837 current_bandwidth -= c->stream->bandwidth;
839 /* signal that there is no feed if we are the feeder socket */
840 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
841 c->stream->feed_opened = 0;
845 av_freep(&c->pb_buffer);
846 av_freep(&c->packet_buffer);
852 static int handle_connection(HTTPContext *c)
857 case HTTPSTATE_WAIT_REQUEST:
858 case RTSPSTATE_WAIT_REQUEST:
860 if ((c->timeout - cur_time) < 0)
862 if (c->poll_entry->revents & (POLLERR | POLLHUP))
865 /* no need to read if no events */
866 if (!(c->poll_entry->revents & POLLIN))
870 len = recv(c->fd, c->buffer_ptr, 1, 0);
872 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
873 ff_neterrno() != FF_NETERROR(EINTR))
875 } else if (len == 0) {
878 /* search for end of request. */
880 c->buffer_ptr += len;
882 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
883 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
884 /* request found : parse it and reply */
885 if (c->state == HTTPSTATE_WAIT_REQUEST) {
886 ret = http_parse_request(c);
888 ret = rtsp_parse_request(c);
892 } else if (ptr >= c->buffer_end) {
893 /* request too long: cannot do anything */
895 } else goto read_loop;
899 case HTTPSTATE_SEND_HEADER:
900 if (c->poll_entry->revents & (POLLERR | POLLHUP))
903 /* no need to write if no events */
904 if (!(c->poll_entry->revents & POLLOUT))
906 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
908 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
909 ff_neterrno() != FF_NETERROR(EINTR)) {
910 /* error : close connection */
911 av_freep(&c->pb_buffer);
915 c->buffer_ptr += len;
917 c->stream->bytes_served += len;
918 c->data_count += len;
919 if (c->buffer_ptr >= c->buffer_end) {
920 av_freep(&c->pb_buffer);
924 /* all the buffer was sent : synchronize to the incoming stream */
925 c->state = HTTPSTATE_SEND_DATA_HEADER;
926 c->buffer_ptr = c->buffer_end = c->buffer;
931 case HTTPSTATE_SEND_DATA:
932 case HTTPSTATE_SEND_DATA_HEADER:
933 case HTTPSTATE_SEND_DATA_TRAILER:
934 /* for packetized output, we consider we can always write (the
935 input streams sets the speed). It may be better to verify
936 that we do not rely too much on the kernel queues */
937 if (!c->is_packetized) {
938 if (c->poll_entry->revents & (POLLERR | POLLHUP))
941 /* no need to read if no events */
942 if (!(c->poll_entry->revents & POLLOUT))
945 if (http_send_data(c) < 0)
947 /* close connection if trailer sent */
948 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
951 case HTTPSTATE_RECEIVE_DATA:
952 /* no need to read if no events */
953 if (c->poll_entry->revents & (POLLERR | POLLHUP))
955 if (!(c->poll_entry->revents & POLLIN))
957 if (http_receive_data(c) < 0)
960 case HTTPSTATE_WAIT_FEED:
961 /* no need to read if no events */
962 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
965 /* nothing to do, we'll be waken up by incoming feed packets */
968 case RTSPSTATE_SEND_REPLY:
969 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
970 av_freep(&c->pb_buffer);
973 /* no need to write if no events */
974 if (!(c->poll_entry->revents & POLLOUT))
976 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
978 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
979 ff_neterrno() != FF_NETERROR(EINTR)) {
980 /* error : close connection */
981 av_freep(&c->pb_buffer);
985 c->buffer_ptr += len;
986 c->data_count += len;
987 if (c->buffer_ptr >= c->buffer_end) {
988 /* all the buffer was sent : wait for a new request */
989 av_freep(&c->pb_buffer);
990 start_wait_request(c, 1);
994 case RTSPSTATE_SEND_PACKET:
995 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
996 av_freep(&c->packet_buffer);
999 /* no need to write if no events */
1000 if (!(c->poll_entry->revents & POLLOUT))
1002 len = send(c->fd, c->packet_buffer_ptr,
1003 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1005 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
1006 ff_neterrno() != FF_NETERROR(EINTR)) {
1007 /* error : close connection */
1008 av_freep(&c->packet_buffer);
1012 c->packet_buffer_ptr += len;
1013 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1014 /* all the buffer was sent : wait for a new request */
1015 av_freep(&c->packet_buffer);
1016 c->state = RTSPSTATE_WAIT_REQUEST;
1020 case HTTPSTATE_READY:
1029 static int extract_rates(char *rates, int ratelen, const char *request)
1033 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1034 if (strncasecmp(p, "Pragma:", 7) == 0) {
1035 const char *q = p + 7;
1037 while (*q && *q != '\n' && isspace(*q))
1040 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1046 memset(rates, 0xff, ratelen);
1049 while (*q && *q != '\n' && *q != ':')
1052 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1056 if (stream_no < ratelen && stream_no >= 0)
1057 rates[stream_no] = rate_no;
1059 while (*q && *q != '\n' && !isspace(*q))
1066 p = strchr(p, '\n');
1076 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1079 int best_bitrate = 100000000;
1082 for (i = 0; i < feed->nb_streams; i++) {
1083 AVCodecContext *feed_codec = feed->streams[i]->codec;
1085 if (feed_codec->codec_id != codec->codec_id ||
1086 feed_codec->sample_rate != codec->sample_rate ||
1087 feed_codec->width != codec->width ||
1088 feed_codec->height != codec->height)
1091 /* Potential stream */
1093 /* We want the fastest stream less than bit_rate, or the slowest
1094 * faster than bit_rate
1097 if (feed_codec->bit_rate <= bit_rate) {
1098 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1099 best_bitrate = feed_codec->bit_rate;
1103 if (feed_codec->bit_rate < best_bitrate) {
1104 best_bitrate = feed_codec->bit_rate;
1113 static int modify_current_stream(HTTPContext *c, char *rates)
1116 FFStream *req = c->stream;
1117 int action_required = 0;
1119 /* Not much we can do for a feed */
1123 for (i = 0; i < req->nb_streams; i++) {
1124 AVCodecContext *codec = req->streams[i]->codec;
1128 c->switch_feed_streams[i] = req->feed_streams[i];
1131 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1134 /* Wants off or slow */
1135 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1137 /* This doesn't work well when it turns off the only stream! */
1138 c->switch_feed_streams[i] = -2;
1139 c->feed_streams[i] = -2;
1144 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1145 action_required = 1;
1148 return action_required;
1152 static void do_switch_stream(HTTPContext *c, int i)
1154 if (c->switch_feed_streams[i] >= 0) {
1156 c->feed_streams[i] = c->switch_feed_streams[i];
1159 /* Now update the stream */
1161 c->switch_feed_streams[i] = -1;
1164 /* XXX: factorize in utils.c ? */
1165 /* XXX: take care with different space meaning */
1166 static void skip_spaces(const char **pp)
1170 while (*p == ' ' || *p == '\t')
1175 static void get_word(char *buf, int buf_size, const char **pp)
1183 while (!isspace(*p) && *p != '\0') {
1184 if ((q - buf) < buf_size - 1)
1193 static void get_arg(char *buf, int buf_size, const char **pp)
1200 while (isspace(*p)) p++;
1203 if (*p == '\"' || *p == '\'')
1215 if ((q - buf) < buf_size - 1)
1220 if (quote && *p == quote)
1225 static int validate_acl(FFStream *stream, HTTPContext *c)
1227 enum IPAddressAction last_action = IP_DENY;
1229 struct in_addr *src = &c->from_addr.sin_addr;
1230 unsigned long src_addr = src->s_addr;
1232 for (acl = stream->acl; acl; acl = acl->next) {
1233 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1234 return (acl->action == IP_ALLOW) ? 1 : 0;
1235 last_action = acl->action;
1238 /* Nothing matched, so return not the last action */
1239 return (last_action == IP_DENY) ? 1 : 0;
1242 /* compute the real filename of a file by matching it without its
1243 extensions to all the stream filenames */
1244 static void compute_real_filename(char *filename, int max_size)
1251 /* compute filename by matching without the file extensions */
1252 av_strlcpy(file1, filename, sizeof(file1));
1253 p = strrchr(file1, '.');
1256 for(stream = first_stream; stream != NULL; stream = stream->next) {
1257 av_strlcpy(file2, stream->filename, sizeof(file2));
1258 p = strrchr(file2, '.');
1261 if (!strcmp(file1, file2)) {
1262 av_strlcpy(filename, stream->filename, max_size);
1277 /* parse http request and prepare header */
1278 static int http_parse_request(HTTPContext *c)
1281 enum RedirType redir_type;
1283 char info[1024], filename[1024];
1287 const char *mime_type;
1291 char *useragent = 0;
1294 get_word(cmd, sizeof(cmd), (const char **)&p);
1295 av_strlcpy(c->method, cmd, sizeof(c->method));
1297 if (!strcmp(cmd, "GET"))
1299 else if (!strcmp(cmd, "POST"))
1304 get_word(url, sizeof(url), (const char **)&p);
1305 av_strlcpy(c->url, url, sizeof(c->url));
1307 get_word(protocol, sizeof(protocol), (const char **)&p);
1308 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1311 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1314 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1316 /* find the filename and the optional info string in the request */
1317 p = strchr(url, '?');
1319 av_strlcpy(info, p, sizeof(info));
1324 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1326 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1327 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1329 if (*useragent && *useragent != '\n' && isspace(*useragent))
1333 p = strchr(p, '\n');
1340 redir_type = REDIR_NONE;
1341 if (av_match_ext(filename, "asx")) {
1342 redir_type = REDIR_ASX;
1343 filename[strlen(filename)-1] = 'f';
1344 } else if (av_match_ext(filename, "asf") &&
1345 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1346 /* if this isn't WMP or lookalike, return the redirector file */
1347 redir_type = REDIR_ASF;
1348 } else if (av_match_ext(filename, "rpm,ram")) {
1349 redir_type = REDIR_RAM;
1350 strcpy(filename + strlen(filename)-2, "m");
1351 } else if (av_match_ext(filename, "rtsp")) {
1352 redir_type = REDIR_RTSP;
1353 compute_real_filename(filename, sizeof(filename) - 1);
1354 } else if (av_match_ext(filename, "sdp")) {
1355 redir_type = REDIR_SDP;
1356 compute_real_filename(filename, sizeof(filename) - 1);
1359 // "redirect" / request to index.html
1360 if (!strlen(filename))
1361 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1363 stream = first_stream;
1364 while (stream != NULL) {
1365 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1367 stream = stream->next;
1369 if (stream == NULL) {
1370 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1371 http_log("File '%s' not found\n", url);
1376 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1377 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1379 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1380 c->http_error = 301;
1382 q += snprintf(q, c->buffer_size,
1383 "HTTP/1.0 301 Moved\r\n"
1385 "Content-type: text/html\r\n"
1387 "<html><head><title>Moved</title></head><body>\r\n"
1388 "You should be <a href=\"%s\">redirected</a>.\r\n"
1389 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1390 /* prepare output buffer */
1391 c->buffer_ptr = c->buffer;
1393 c->state = HTTPSTATE_SEND_HEADER;
1397 /* If this is WMP, get the rate information */
1398 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1399 if (modify_current_stream(c, ratebuf)) {
1400 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1401 if (c->switch_feed_streams[i] >= 0)
1402 do_switch_stream(c, i);
1407 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1408 current_bandwidth += stream->bandwidth;
1410 /* If already streaming this feed, do not let start another feeder. */
1411 if (stream->feed_opened) {
1412 snprintf(msg, sizeof(msg), "This feed is already being received.");
1413 http_log("Feed '%s' already being received\n", stream->feed_filename);
1417 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1418 c->http_error = 200;
1420 q += snprintf(q, c->buffer_size,
1421 "HTTP/1.0 200 Server too busy\r\n"
1422 "Content-type: text/html\r\n"
1424 "<html><head><title>Too busy</title></head><body>\r\n"
1425 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1426 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1427 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1428 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1429 /* prepare output buffer */
1430 c->buffer_ptr = c->buffer;
1432 c->state = HTTPSTATE_SEND_HEADER;
1436 if (redir_type != REDIR_NONE) {
1439 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1440 if (strncasecmp(p, "Host:", 5) == 0) {
1444 p = strchr(p, '\n');
1455 while (isspace(*hostinfo))
1458 eoh = strchr(hostinfo, '\n');
1460 if (eoh[-1] == '\r')
1463 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1464 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1465 hostbuf[eoh - hostinfo] = 0;
1467 c->http_error = 200;
1469 switch(redir_type) {
1471 q += snprintf(q, c->buffer_size,
1472 "HTTP/1.0 200 ASX Follows\r\n"
1473 "Content-type: video/x-ms-asf\r\n"
1475 "<ASX Version=\"3\">\r\n"
1476 //"<!-- Autogenerated by ffserver -->\r\n"
1477 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1478 "</ASX>\r\n", hostbuf, filename, info);
1481 q += snprintf(q, c->buffer_size,
1482 "HTTP/1.0 200 RAM Follows\r\n"
1483 "Content-type: audio/x-pn-realaudio\r\n"
1485 "# Autogenerated by ffserver\r\n"
1486 "http://%s/%s%s\r\n", hostbuf, filename, info);
1489 q += snprintf(q, c->buffer_size,
1490 "HTTP/1.0 200 ASF Redirect follows\r\n"
1491 "Content-type: video/x-ms-asf\r\n"
1494 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1498 char hostname[256], *p;
1499 /* extract only hostname */
1500 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1501 p = strrchr(hostname, ':');
1504 q += snprintf(q, c->buffer_size,
1505 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1506 /* XXX: incorrect mime type ? */
1507 "Content-type: application/x-rtsp\r\n"
1509 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1515 int sdp_data_size, len;
1516 struct sockaddr_in my_addr;
1518 q += snprintf(q, c->buffer_size,
1519 "HTTP/1.0 200 OK\r\n"
1520 "Content-type: application/sdp\r\n"
1523 len = sizeof(my_addr);
1524 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1526 /* XXX: should use a dynamic buffer */
1527 sdp_data_size = prepare_sdp_description(stream,
1530 if (sdp_data_size > 0) {
1531 memcpy(q, sdp_data, sdp_data_size);
1543 /* prepare output buffer */
1544 c->buffer_ptr = c->buffer;
1546 c->state = HTTPSTATE_SEND_HEADER;
1552 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1556 stream->conns_served++;
1558 /* XXX: add there authenticate and IP match */
1561 /* if post, it means a feed is being sent */
1562 if (!stream->is_feed) {
1563 /* However it might be a status report from WMP! Let us log the
1564 * data as it might come in handy one day. */
1568 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1569 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1573 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1574 client_id = strtol(p + 18, 0, 10);
1575 p = strchr(p, '\n');
1583 char *eol = strchr(logline, '\n');
1588 if (eol[-1] == '\r')
1590 http_log("%.*s\n", (int) (eol - logline), logline);
1591 c->suppress_log = 1;
1596 http_log("\nGot request:\n%s\n", c->buffer);
1599 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1602 /* Now we have to find the client_id */
1603 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1604 if (wmpc->wmp_client_id == client_id)
1608 if (wmpc && modify_current_stream(wmpc, ratebuf))
1609 wmpc->switch_pending = 1;
1612 snprintf(msg, sizeof(msg), "POST command not handled");
1616 if (http_start_receive_data(c) < 0) {
1617 snprintf(msg, sizeof(msg), "could not open feed");
1621 c->state = HTTPSTATE_RECEIVE_DATA;
1626 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1627 http_log("\nGot request:\n%s\n", c->buffer);
1630 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1633 /* open input stream */
1634 if (open_input_stream(c, info) < 0) {
1635 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1639 /* prepare http header */
1641 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1642 mime_type = c->stream->fmt->mime_type;
1644 mime_type = "application/x-octet-stream";
1645 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1647 /* for asf, we need extra headers */
1648 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1649 /* Need to allocate a client id */
1651 c->wmp_client_id = av_lfg_get(&random_state);
1653 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);
1655 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1656 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1658 /* prepare output buffer */
1660 c->buffer_ptr = c->buffer;
1662 c->state = HTTPSTATE_SEND_HEADER;
1665 c->http_error = 404;
1667 q += snprintf(q, c->buffer_size,
1668 "HTTP/1.0 404 Not Found\r\n"
1669 "Content-type: text/html\r\n"
1672 "<head><title>404 Not Found</title></head>\n"
1675 /* prepare output buffer */
1676 c->buffer_ptr = c->buffer;
1678 c->state = HTTPSTATE_SEND_HEADER;
1682 c->http_error = 200; /* horrible : we use this value to avoid
1683 going to the send data state */
1684 c->state = HTTPSTATE_SEND_HEADER;
1688 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1690 static const char *suffix = " kMGTP";
1693 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1695 url_fprintf(pb, "%"PRId64"%c", count, *s);
1698 static void compute_status(HTTPContext *c)
1707 if (url_open_dyn_buf(&pb) < 0) {
1708 /* XXX: return an error ? */
1709 c->buffer_ptr = c->buffer;
1710 c->buffer_end = c->buffer;
1714 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1715 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1716 url_fprintf(pb, "Pragma: no-cache\r\n");
1717 url_fprintf(pb, "\r\n");
1719 url_fprintf(pb, "<html><head><title>%s Status</title>\n", program_name);
1720 if (c->stream->feed_filename[0])
1721 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1722 url_fprintf(pb, "</head>\n<body>");
1723 url_fprintf(pb, "<h1>%s Status</h1>\n", program_name);
1725 url_fprintf(pb, "<h2>Available Streams</h2>\n");
1726 url_fprintf(pb, "<table cellspacing=0 cellpadding=4>\n");
1727 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");
1728 stream = first_stream;
1729 while (stream != NULL) {
1730 char sfilename[1024];
1733 if (stream->feed != stream) {
1734 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1735 eosf = sfilename + strlen(sfilename);
1736 if (eosf - sfilename >= 4) {
1737 if (strcmp(eosf - 4, ".asf") == 0)
1738 strcpy(eosf - 4, ".asx");
1739 else if (strcmp(eosf - 3, ".rm") == 0)
1740 strcpy(eosf - 3, ".ram");
1741 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1742 /* generate a sample RTSP director if
1743 unicast. Generate an SDP redirector if
1745 eosf = strrchr(sfilename, '.');
1747 eosf = sfilename + strlen(sfilename);
1748 if (stream->is_multicast)
1749 strcpy(eosf, ".sdp");
1751 strcpy(eosf, ".rtsp");
1755 url_fprintf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1756 sfilename, stream->filename);
1757 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1758 stream->conns_served);
1759 fmt_bytecount(pb, stream->bytes_served);
1760 switch(stream->stream_type) {
1761 case STREAM_TYPE_LIVE: {
1762 int audio_bit_rate = 0;
1763 int video_bit_rate = 0;
1764 const char *audio_codec_name = "";
1765 const char *video_codec_name = "";
1766 const char *audio_codec_name_extra = "";
1767 const char *video_codec_name_extra = "";
1769 for(i=0;i<stream->nb_streams;i++) {
1770 AVStream *st = stream->streams[i];
1771 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1772 switch(st->codec->codec_type) {
1773 case CODEC_TYPE_AUDIO:
1774 audio_bit_rate += st->codec->bit_rate;
1776 if (*audio_codec_name)
1777 audio_codec_name_extra = "...";
1778 audio_codec_name = codec->name;
1781 case CODEC_TYPE_VIDEO:
1782 video_bit_rate += st->codec->bit_rate;
1784 if (*video_codec_name)
1785 video_codec_name_extra = "...";
1786 video_codec_name = codec->name;
1789 case CODEC_TYPE_DATA:
1790 video_bit_rate += st->codec->bit_rate;
1796 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",
1799 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1800 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1802 url_fprintf(pb, "<td>%s", stream->feed->filename);
1804 url_fprintf(pb, "<td>%s", stream->feed_filename);
1805 url_fprintf(pb, "\n");
1809 url_fprintf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1813 stream = stream->next;
1815 url_fprintf(pb, "</table>\n");
1817 stream = first_stream;
1818 while (stream != NULL) {
1819 if (stream->feed == stream) {
1820 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1822 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1824 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1829 /* This is somewhat linux specific I guess */
1830 snprintf(ps_cmd, sizeof(ps_cmd),
1831 "ps -o \"%%cpu,cputime\" --no-headers %d",
1834 pid_stat = popen(ps_cmd, "r");
1839 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1841 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1849 url_fprintf(pb, "<p>");
1851 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");
1853 for (i = 0; i < stream->nb_streams; i++) {
1854 AVStream *st = stream->streams[i];
1855 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1856 const char *type = "unknown";
1857 char parameters[64];
1861 switch(st->codec->codec_type) {
1862 case CODEC_TYPE_AUDIO:
1864 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1866 case CODEC_TYPE_VIDEO:
1868 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1869 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1874 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1875 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1877 url_fprintf(pb, "</table>\n");
1880 stream = stream->next;
1883 /* connection status */
1884 url_fprintf(pb, "<h2>Connection Status</h2>\n");
1886 url_fprintf(pb, "Number of connections: %d / %d<br>\n",
1887 nb_connections, nb_max_connections);
1889 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
1890 current_bandwidth, max_bandwidth);
1892 url_fprintf(pb, "<table>\n");
1893 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");
1894 c1 = first_http_ctx;
1896 while (c1 != NULL) {
1902 for (j = 0; j < c1->stream->nb_streams; j++) {
1903 if (!c1->stream->feed)
1904 bitrate += c1->stream->streams[j]->codec->bit_rate;
1905 else if (c1->feed_streams[j] >= 0)
1906 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1911 p = inet_ntoa(c1->from_addr.sin_addr);
1912 url_fprintf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
1914 c1->stream ? c1->stream->filename : "",
1915 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1918 http_state[c1->state]);
1919 fmt_bytecount(pb, bitrate);
1920 url_fprintf(pb, "<td align=right>");
1921 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1922 url_fprintf(pb, "<td align=right>");
1923 fmt_bytecount(pb, c1->data_count);
1924 url_fprintf(pb, "\n");
1927 url_fprintf(pb, "</table>\n");
1932 url_fprintf(pb, "<hr size=1 noshade>Generated at %s", p);
1933 url_fprintf(pb, "</body>\n</html>\n");
1935 len = url_close_dyn_buf(pb, &c->pb_buffer);
1936 c->buffer_ptr = c->pb_buffer;
1937 c->buffer_end = c->pb_buffer + len;
1940 /* check if the parser needs to be opened for stream i */
1941 static void open_parser(AVFormatContext *s, int i)
1943 AVStream *st = s->streams[i];
1946 if (!st->codec->codec) {
1947 codec = avcodec_find_decoder(st->codec->codec_id);
1948 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1949 st->codec->parse_only = 1;
1950 if (avcodec_open(st->codec, codec) < 0)
1951 st->codec->parse_only = 0;
1956 static int open_input_stream(HTTPContext *c, const char *info)
1959 char input_filename[1024];
1961 int buf_size, i, ret;
1964 /* find file name */
1965 if (c->stream->feed) {
1966 strcpy(input_filename, c->stream->feed->feed_filename);
1967 buf_size = FFM_PACKET_SIZE;
1968 /* compute position (absolute time) */
1969 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1970 stream_pos = parse_date(buf, 0);
1971 if (stream_pos == INT64_MIN)
1973 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1974 int prebuffer = strtol(buf, 0, 10);
1975 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1977 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1979 strcpy(input_filename, c->stream->feed_filename);
1981 /* compute position (relative time) */
1982 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1983 stream_pos = parse_date(buf, 1);
1984 if (stream_pos == INT64_MIN)
1989 if (input_filename[0] == '\0')
1993 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1994 buf_size, c->stream->ap_in)) < 0) {
1995 http_log("could not open %s: %d\n", input_filename, ret);
1998 s->flags |= AVFMT_FLAG_GENPTS;
2000 if (strcmp(s->iformat->name, "ffm") && av_find_stream_info(c->fmt_in) < 0) {
2001 http_log("Could not find stream info '%s'\n", input_filename);
2002 av_close_input_file(s);
2006 /* open each parser */
2007 for(i=0;i<s->nb_streams;i++)
2010 /* choose stream as clock source (we favorize video stream if
2011 present) for packet sending */
2012 c->pts_stream_index = 0;
2013 for(i=0;i<c->stream->nb_streams;i++) {
2014 if (c->pts_stream_index == 0 &&
2015 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
2016 c->pts_stream_index = i;
2021 if (c->fmt_in->iformat->read_seek)
2022 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2024 /* set the start time (needed for maxtime and RTP packet timing) */
2025 c->start_time = cur_time;
2026 c->first_pts = AV_NOPTS_VALUE;
2030 /* return the server clock (in us) */
2031 static int64_t get_server_clock(HTTPContext *c)
2033 /* compute current pts value from system time */
2034 return (cur_time - c->start_time) * 1000;
2037 /* return the estimated time at which the current packet must be sent
2039 static int64_t get_packet_send_clock(HTTPContext *c)
2041 int bytes_left, bytes_sent, frame_bytes;
2043 frame_bytes = c->cur_frame_bytes;
2044 if (frame_bytes <= 0)
2047 bytes_left = c->buffer_end - c->buffer_ptr;
2048 bytes_sent = frame_bytes - bytes_left;
2049 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2054 static int http_prepare_data(HTTPContext *c)
2057 AVFormatContext *ctx;
2059 av_freep(&c->pb_buffer);
2061 case HTTPSTATE_SEND_DATA_HEADER:
2062 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2063 av_metadata_set(&c->fmt_ctx.metadata, "author" ,c->stream->author);
2064 av_metadata_set(&c->fmt_ctx.metadata, "comment" ,c->stream->comment);
2065 av_metadata_set(&c->fmt_ctx.metadata, "copyright",c->stream->copyright);
2066 av_metadata_set(&c->fmt_ctx.metadata, "title" ,c->stream->title);
2068 for(i=0;i<c->stream->nb_streams;i++) {
2071 st = av_mallocz(sizeof(AVStream));
2072 c->fmt_ctx.streams[i] = st;
2073 /* if file or feed, then just take streams from FFStream struct */
2074 if (!c->stream->feed ||
2075 c->stream->feed == c->stream)
2076 src = c->stream->streams[i];
2078 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2082 st->codec->frame_number = 0; /* XXX: should be done in
2083 AVStream, not in codec */
2085 /* set output format parameters */
2086 c->fmt_ctx.oformat = c->stream->fmt;
2087 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2089 c->got_key_frame = 0;
2091 /* prepare header and save header data in a stream */
2092 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2093 /* XXX: potential leak */
2096 c->fmt_ctx.pb->is_streamed = 1;
2099 * HACK to avoid mpeg ps muxer to spit many underflow errors
2100 * Default value from FFmpeg
2101 * Try to set it use configuration option
2103 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2104 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2106 av_set_parameters(&c->fmt_ctx, NULL);
2107 if (av_write_header(&c->fmt_ctx) < 0) {
2108 http_log("Error writing output header\n");
2112 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2113 c->buffer_ptr = c->pb_buffer;
2114 c->buffer_end = c->pb_buffer + len;
2116 c->state = HTTPSTATE_SEND_DATA;
2117 c->last_packet_sent = 0;
2119 case HTTPSTATE_SEND_DATA:
2120 /* find a new packet */
2121 /* read a packet from the input stream */
2122 if (c->stream->feed)
2123 ffm_set_write_index(c->fmt_in,
2124 c->stream->feed->feed_write_index,
2125 c->stream->feed->feed_size);
2127 if (c->stream->max_time &&
2128 c->stream->max_time + c->start_time - cur_time < 0)
2129 /* We have timed out */
2130 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2134 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2135 if (c->stream->feed && c->stream->feed->feed_opened) {
2136 /* if coming from feed, it means we reached the end of the
2137 ffm file, so must wait for more data */
2138 c->state = HTTPSTATE_WAIT_FEED;
2139 return 1; /* state changed */
2141 if (c->stream->loop) {
2142 av_close_input_file(c->fmt_in);
2144 if (open_input_stream(c, "") < 0)
2149 /* must send trailer now because eof or error */
2150 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2154 int source_index = pkt.stream_index;
2155 /* update first pts if needed */
2156 if (c->first_pts == AV_NOPTS_VALUE) {
2157 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2158 c->start_time = cur_time;
2160 /* send it to the appropriate stream */
2161 if (c->stream->feed) {
2162 /* if coming from a feed, select the right stream */
2163 if (c->switch_pending) {
2164 c->switch_pending = 0;
2165 for(i=0;i<c->stream->nb_streams;i++) {
2166 if (c->switch_feed_streams[i] == pkt.stream_index)
2167 if (pkt.flags & PKT_FLAG_KEY)
2168 do_switch_stream(c, i);
2169 if (c->switch_feed_streams[i] >= 0)
2170 c->switch_pending = 1;
2173 for(i=0;i<c->stream->nb_streams;i++) {
2174 if (c->feed_streams[i] == pkt.stream_index) {
2175 AVStream *st = c->fmt_in->streams[source_index];
2176 pkt.stream_index = i;
2177 if (pkt.flags & PKT_FLAG_KEY &&
2178 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2179 c->stream->nb_streams == 1))
2180 c->got_key_frame = 1;
2181 if (!c->stream->send_on_key || c->got_key_frame)
2186 AVCodecContext *codec;
2187 AVStream *ist, *ost;
2189 ist = c->fmt_in->streams[source_index];
2190 /* specific handling for RTP: we use several
2191 output stream (one for each RTP
2192 connection). XXX: need more abstract handling */
2193 if (c->is_packetized) {
2194 /* compute send time and duration */
2195 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2196 if (ist->start_time != AV_NOPTS_VALUE)
2197 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2198 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2199 /* find RTP context */
2200 c->packet_stream_index = pkt.stream_index;
2201 ctx = c->rtp_ctx[c->packet_stream_index];
2203 av_free_packet(&pkt);
2206 codec = ctx->streams[0]->codec;
2207 /* only one stream per RTP connection */
2208 pkt.stream_index = 0;
2212 codec = ctx->streams[pkt.stream_index]->codec;
2215 if (c->is_packetized) {
2216 int max_packet_size;
2217 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2218 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2220 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2221 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2223 ret = url_open_dyn_buf(&ctx->pb);
2226 /* XXX: potential leak */
2229 ost = ctx->streams[pkt.stream_index];
2231 ctx->pb->is_streamed = 1;
2232 if (pkt.dts != AV_NOPTS_VALUE)
2233 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2234 if (pkt.pts != AV_NOPTS_VALUE)
2235 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2236 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2237 if (av_write_frame(ctx, &pkt) < 0) {
2238 http_log("Error writing frame to output\n");
2239 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2242 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2243 c->cur_frame_bytes = len;
2244 c->buffer_ptr = c->pb_buffer;
2245 c->buffer_end = c->pb_buffer + len;
2247 codec->frame_number++;
2249 av_free_packet(&pkt);
2253 av_free_packet(&pkt);
2258 case HTTPSTATE_SEND_DATA_TRAILER:
2259 /* last packet test ? */
2260 if (c->last_packet_sent || c->is_packetized)
2263 /* prepare header */
2264 if (url_open_dyn_buf(&ctx->pb) < 0) {
2265 /* XXX: potential leak */
2268 c->fmt_ctx.pb->is_streamed = 1;
2269 av_write_trailer(ctx);
2270 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2271 c->buffer_ptr = c->pb_buffer;
2272 c->buffer_end = c->pb_buffer + len;
2274 c->last_packet_sent = 1;
2280 /* should convert the format at the same time */
2281 /* send data starting at c->buffer_ptr to the output connection
2282 (either UDP or TCP connection) */
2283 static int http_send_data(HTTPContext *c)
2288 if (c->buffer_ptr >= c->buffer_end) {
2289 ret = http_prepare_data(c);
2293 /* state change requested */
2296 if (c->is_packetized) {
2297 /* RTP data output */
2298 len = c->buffer_end - c->buffer_ptr;
2300 /* fail safe - should never happen */
2302 c->buffer_ptr = c->buffer_end;
2305 len = (c->buffer_ptr[0] << 24) |
2306 (c->buffer_ptr[1] << 16) |
2307 (c->buffer_ptr[2] << 8) |
2309 if (len > (c->buffer_end - c->buffer_ptr))
2311 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2312 /* nothing to send yet: we can wait */
2316 c->data_count += len;
2317 update_datarate(&c->datarate, c->data_count);
2319 c->stream->bytes_served += len;
2321 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2322 /* RTP packets are sent inside the RTSP TCP connection */
2324 int interleaved_index, size;
2326 HTTPContext *rtsp_c;
2329 /* if no RTSP connection left, error */
2332 /* if already sending something, then wait. */
2333 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2335 if (url_open_dyn_buf(&pb) < 0)
2337 interleaved_index = c->packet_stream_index * 2;
2338 /* RTCP packets are sent at odd indexes */
2339 if (c->buffer_ptr[1] == 200)
2340 interleaved_index++;
2341 /* write RTSP TCP header */
2343 header[1] = interleaved_index;
2344 header[2] = len >> 8;
2346 put_buffer(pb, header, 4);
2347 /* write RTP packet data */
2349 put_buffer(pb, c->buffer_ptr, len);
2350 size = url_close_dyn_buf(pb, &c->packet_buffer);
2351 /* prepare asynchronous TCP sending */
2352 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2353 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2354 c->buffer_ptr += len;
2356 /* send everything we can NOW */
2357 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2358 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2360 rtsp_c->packet_buffer_ptr += len;
2361 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2362 /* if we could not send all the data, we will
2363 send it later, so a new state is needed to
2364 "lock" the RTSP TCP connection */
2365 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2368 /* all data has been sent */
2369 av_freep(&c->packet_buffer);
2371 /* send RTP packet directly in UDP */
2373 url_write(c->rtp_handles[c->packet_stream_index],
2374 c->buffer_ptr, len);
2375 c->buffer_ptr += len;
2376 /* here we continue as we can send several packets per 10 ms slot */
2379 /* TCP data output */
2380 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2382 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2383 ff_neterrno() != FF_NETERROR(EINTR))
2384 /* error : close connection */
2389 c->buffer_ptr += len;
2391 c->data_count += len;
2392 update_datarate(&c->datarate, c->data_count);
2394 c->stream->bytes_served += len;
2402 static int http_start_receive_data(HTTPContext *c)
2406 if (c->stream->feed_opened)
2409 /* Don't permit writing to this one */
2410 if (c->stream->readonly)
2414 fd = open(c->stream->feed_filename, O_RDWR);
2416 http_log("Error opening feeder file: %s\n", strerror(errno));
2421 if (c->stream->truncate) {
2422 /* truncate feed file */
2423 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2424 ftruncate(c->feed_fd, FFM_PACKET_SIZE);
2425 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2427 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2428 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2433 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2434 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2435 lseek(fd, 0, SEEK_SET);
2437 /* init buffer input */
2438 c->buffer_ptr = c->buffer;
2439 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2440 c->stream->feed_opened = 1;
2441 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2445 static int http_receive_data(HTTPContext *c)
2448 int len, loop_run = 0;
2450 while (c->chunked_encoding && !c->chunk_size &&
2451 c->buffer_end > c->buffer_ptr) {
2452 /* read chunk header, if present */
2453 len = recv(c->fd, c->buffer_ptr, 1, 0);
2456 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2457 ff_neterrno() != FF_NETERROR(EINTR))
2458 /* error : close connection */
2460 } else if (len == 0) {
2461 /* end of connection : close it */
2463 } else if (c->buffer_ptr - c->buffer >= 2 &&
2464 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2465 c->chunk_size = strtol(c->buffer, 0, 16);
2466 if (c->chunk_size == 0) // end of stream
2468 c->buffer_ptr = c->buffer;
2470 } else if (++loop_run > 10) {
2471 /* no chunk header, abort */
2478 if (c->buffer_end > c->buffer_ptr) {
2479 len = recv(c->fd, c->buffer_ptr,
2480 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2482 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2483 ff_neterrno() != FF_NETERROR(EINTR))
2484 /* error : close connection */
2486 } else if (len == 0)
2487 /* end of connection : close it */
2490 c->chunk_size -= len;
2491 c->buffer_ptr += len;
2492 c->data_count += len;
2493 update_datarate(&c->datarate, c->data_count);
2497 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2498 if (c->buffer[0] != 'f' ||
2499 c->buffer[1] != 'm') {
2500 http_log("Feed stream has become desynchronized -- disconnecting\n");
2505 if (c->buffer_ptr >= c->buffer_end) {
2506 FFStream *feed = c->stream;
2507 /* a packet has been received : write it in the store, except
2509 if (c->data_count > FFM_PACKET_SIZE) {
2511 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2512 /* XXX: use llseek or url_seek */
2513 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2514 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2515 http_log("Error writing to feed file: %s\n", strerror(errno));
2519 feed->feed_write_index += FFM_PACKET_SIZE;
2520 /* update file size */
2521 if (feed->feed_write_index > c->stream->feed_size)
2522 feed->feed_size = feed->feed_write_index;
2524 /* handle wrap around if max file size reached */
2525 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2526 feed->feed_write_index = FFM_PACKET_SIZE;
2529 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2530 http_log("Error writing index to feed file: %s\n", strerror(errno));
2534 /* wake up any waiting connections */
2535 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2536 if (c1->state == HTTPSTATE_WAIT_FEED &&
2537 c1->stream->feed == c->stream->feed)
2538 c1->state = HTTPSTATE_SEND_DATA;
2541 /* We have a header in our hands that contains useful data */
2542 AVFormatContext *s = NULL;
2544 AVInputFormat *fmt_in;
2547 /* use feed output format name to find corresponding input format */
2548 fmt_in = av_find_input_format(feed->fmt->name);
2552 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2553 pb->is_streamed = 1;
2555 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2560 /* Now we have the actual streams */
2561 if (s->nb_streams != feed->nb_streams) {
2562 av_close_input_stream(s);
2564 http_log("Feed '%s' stream number does not match registered feed\n",
2565 c->stream->feed_filename);
2569 for (i = 0; i < s->nb_streams; i++) {
2570 AVStream *fst = feed->streams[i];
2571 AVStream *st = s->streams[i];
2572 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2573 if (fst->codec->extradata_size) {
2574 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2575 if (!fst->codec->extradata)
2577 memcpy(fst->codec->extradata, st->codec->extradata,
2578 fst->codec->extradata_size);
2582 av_close_input_stream(s);
2585 c->buffer_ptr = c->buffer;
2590 c->stream->feed_opened = 0;
2592 /* wake up any waiting connections to stop waiting for feed */
2593 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2594 if (c1->state == HTTPSTATE_WAIT_FEED &&
2595 c1->stream->feed == c->stream->feed)
2596 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2601 /********************************************************************/
2604 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2611 switch(error_number) {
2612 case RTSP_STATUS_OK:
2615 case RTSP_STATUS_METHOD:
2616 str = "Method Not Allowed";
2618 case RTSP_STATUS_BANDWIDTH:
2619 str = "Not Enough Bandwidth";
2621 case RTSP_STATUS_SESSION:
2622 str = "Session Not Found";
2624 case RTSP_STATUS_STATE:
2625 str = "Method Not Valid in This State";
2627 case RTSP_STATUS_AGGREGATE:
2628 str = "Aggregate operation not allowed";
2630 case RTSP_STATUS_ONLY_AGGREGATE:
2631 str = "Only aggregate operation allowed";
2633 case RTSP_STATUS_TRANSPORT:
2634 str = "Unsupported transport";
2636 case RTSP_STATUS_INTERNAL:
2637 str = "Internal Server Error";
2639 case RTSP_STATUS_SERVICE:
2640 str = "Service Unavailable";
2642 case RTSP_STATUS_VERSION:
2643 str = "RTSP Version not supported";
2646 str = "Unknown Error";
2650 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2651 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2653 /* output GMT time */
2657 p = buf2 + strlen(p) - 1;
2660 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2663 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2665 rtsp_reply_header(c, error_number);
2666 url_fprintf(c->pb, "\r\n");
2669 static int rtsp_parse_request(HTTPContext *c)
2671 const char *p, *p1, *p2;
2677 RTSPMessageHeader header1, *header = &header1;
2679 c->buffer_ptr[0] = '\0';
2682 get_word(cmd, sizeof(cmd), &p);
2683 get_word(url, sizeof(url), &p);
2684 get_word(protocol, sizeof(protocol), &p);
2686 av_strlcpy(c->method, cmd, sizeof(c->method));
2687 av_strlcpy(c->url, url, sizeof(c->url));
2688 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2690 if (url_open_dyn_buf(&c->pb) < 0) {
2691 /* XXX: cannot do more */
2692 c->pb = NULL; /* safety */
2696 /* check version name */
2697 if (strcmp(protocol, "RTSP/1.0") != 0) {
2698 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2702 /* parse each header line */
2703 memset(header, 0, sizeof(*header));
2704 /* skip to next line */
2705 while (*p != '\n' && *p != '\0')
2709 while (*p != '\0') {
2710 p1 = strchr(p, '\n');
2714 if (p2 > p && p2[-1] == '\r')
2716 /* skip empty line */
2720 if (len > sizeof(line) - 1)
2721 len = sizeof(line) - 1;
2722 memcpy(line, p, len);
2724 ff_rtsp_parse_line(header, line);
2728 /* handle sequence number */
2729 c->seq = header->seq;
2731 if (!strcmp(cmd, "DESCRIBE"))
2732 rtsp_cmd_describe(c, url);
2733 else if (!strcmp(cmd, "OPTIONS"))
2734 rtsp_cmd_options(c, url);
2735 else if (!strcmp(cmd, "SETUP"))
2736 rtsp_cmd_setup(c, url, header);
2737 else if (!strcmp(cmd, "PLAY"))
2738 rtsp_cmd_play(c, url, header);
2739 else if (!strcmp(cmd, "PAUSE"))
2740 rtsp_cmd_pause(c, url, header);
2741 else if (!strcmp(cmd, "TEARDOWN"))
2742 rtsp_cmd_teardown(c, url, header);
2744 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2747 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2748 c->pb = NULL; /* safety */
2750 /* XXX: cannot do more */
2753 c->buffer_ptr = c->pb_buffer;
2754 c->buffer_end = c->pb_buffer + len;
2755 c->state = RTSPSTATE_SEND_REPLY;
2759 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2760 struct in_addr my_ip)
2762 AVFormatContext *avc;
2763 AVStream avs[MAX_STREAMS];
2766 avc = avformat_alloc_context();
2770 av_metadata_set(&avc->metadata, "title",
2771 stream->title[0] ? stream->title : "No Title");
2772 avc->nb_streams = stream->nb_streams;
2773 if (stream->is_multicast) {
2774 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2775 inet_ntoa(stream->multicast_ip),
2776 stream->multicast_port, stream->multicast_ttl);
2779 for(i = 0; i < stream->nb_streams; i++) {
2780 avc->streams[i] = &avs[i];
2781 avc->streams[i]->codec = stream->streams[i]->codec;
2783 *pbuffer = av_mallocz(2048);
2784 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2787 return strlen(*pbuffer);
2790 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2792 // rtsp_reply_header(c, RTSP_STATUS_OK);
2793 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2794 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2795 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2796 url_fprintf(c->pb, "\r\n");
2799 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2805 int content_length, len;
2806 struct sockaddr_in my_addr;
2808 /* find which url is asked */
2809 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2814 for(stream = first_stream; stream != NULL; stream = stream->next) {
2815 if (!stream->is_feed &&
2816 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2817 !strcmp(path, stream->filename)) {
2821 /* no stream found */
2822 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2826 /* prepare the media description in sdp format */
2828 /* get the host IP */
2829 len = sizeof(my_addr);
2830 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2831 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2832 if (content_length < 0) {
2833 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2836 rtsp_reply_header(c, RTSP_STATUS_OK);
2837 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2838 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2839 url_fprintf(c->pb, "\r\n");
2840 put_buffer(c->pb, content, content_length);
2843 static HTTPContext *find_rtp_session(const char *session_id)
2847 if (session_id[0] == '\0')
2850 for(c = first_http_ctx; c != NULL; c = c->next) {
2851 if (!strcmp(c->session_id, session_id))
2857 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
2859 RTSPTransportField *th;
2862 for(i=0;i<h->nb_transports;i++) {
2863 th = &h->transports[i];
2864 if (th->lower_transport == lower_transport)
2870 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2871 RTSPMessageHeader *h)
2874 int stream_index, port;
2879 RTSPTransportField *th;
2880 struct sockaddr_in dest_addr;
2881 RTSPActionServerSetup setup;
2883 /* find which url is asked */
2884 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2889 /* now check each stream */
2890 for(stream = first_stream; stream != NULL; stream = stream->next) {
2891 if (!stream->is_feed &&
2892 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2893 /* accept aggregate filenames only if single stream */
2894 if (!strcmp(path, stream->filename)) {
2895 if (stream->nb_streams != 1) {
2896 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2903 for(stream_index = 0; stream_index < stream->nb_streams;
2905 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2906 stream->filename, stream_index);
2907 if (!strcmp(path, buf))
2912 /* no stream found */
2913 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2917 /* generate session id if needed */
2918 if (h->session_id[0] == '\0')
2919 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2920 av_lfg_get(&random_state), av_lfg_get(&random_state));
2922 /* find rtp session, and create it if none found */
2923 rtp_c = find_rtp_session(h->session_id);
2925 /* always prefer UDP */
2926 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
2928 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
2930 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2935 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2936 th->lower_transport);
2938 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2942 /* open input stream */
2943 if (open_input_stream(rtp_c, "") < 0) {
2944 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2949 /* test if stream is OK (test needed because several SETUP needs
2950 to be done for a given file) */
2951 if (rtp_c->stream != stream) {
2952 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2956 /* test if stream is already set up */
2957 if (rtp_c->rtp_ctx[stream_index]) {
2958 rtsp_reply_error(c, RTSP_STATUS_STATE);
2962 /* check transport */
2963 th = find_transport(h, rtp_c->rtp_protocol);
2964 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
2965 th->client_port_min <= 0)) {
2966 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2970 /* setup default options */
2971 setup.transport_option[0] = '\0';
2972 dest_addr = rtp_c->from_addr;
2973 dest_addr.sin_port = htons(th->client_port_min);
2976 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2977 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2981 /* now everything is OK, so we can send the connection parameters */
2982 rtsp_reply_header(c, RTSP_STATUS_OK);
2984 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2986 switch(rtp_c->rtp_protocol) {
2987 case RTSP_LOWER_TRANSPORT_UDP:
2988 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2989 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2990 "client_port=%d-%d;server_port=%d-%d",
2991 th->client_port_min, th->client_port_min + 1,
2994 case RTSP_LOWER_TRANSPORT_TCP:
2995 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2996 stream_index * 2, stream_index * 2 + 1);
3001 if (setup.transport_option[0] != '\0')
3002 url_fprintf(c->pb, ";%s", setup.transport_option);
3003 url_fprintf(c->pb, "\r\n");
3006 url_fprintf(c->pb, "\r\n");
3010 /* find an rtp connection by using the session ID. Check consistency
3012 static HTTPContext *find_rtp_session_with_url(const char *url,
3013 const char *session_id)
3021 rtp_c = find_rtp_session(session_id);
3025 /* find which url is asked */
3026 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3030 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3031 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3032 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3033 rtp_c->stream->filename, s);
3034 if(!strncmp(path, buf, sizeof(buf))) {
3035 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3042 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3046 rtp_c = find_rtp_session_with_url(url, h->session_id);
3048 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3052 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3053 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3054 rtp_c->state != HTTPSTATE_READY) {
3055 rtsp_reply_error(c, RTSP_STATUS_STATE);
3059 rtp_c->state = HTTPSTATE_SEND_DATA;
3061 /* now everything is OK, so we can send the connection parameters */
3062 rtsp_reply_header(c, RTSP_STATUS_OK);
3064 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3065 url_fprintf(c->pb, "\r\n");
3068 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3072 rtp_c = find_rtp_session_with_url(url, h->session_id);
3074 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3078 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3079 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3080 rtsp_reply_error(c, RTSP_STATUS_STATE);
3084 rtp_c->state = HTTPSTATE_READY;
3085 rtp_c->first_pts = AV_NOPTS_VALUE;
3086 /* now everything is OK, so we can send the connection parameters */
3087 rtsp_reply_header(c, RTSP_STATUS_OK);
3089 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3090 url_fprintf(c->pb, "\r\n");
3093 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3096 char session_id[32];
3098 rtp_c = find_rtp_session_with_url(url, h->session_id);
3100 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3104 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3106 /* abort the session */
3107 close_connection(rtp_c);
3109 /* now everything is OK, so we can send the connection parameters */
3110 rtsp_reply_header(c, RTSP_STATUS_OK);
3112 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3113 url_fprintf(c->pb, "\r\n");
3117 /********************************************************************/
3120 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3121 FFStream *stream, const char *session_id,
3122 enum RTSPLowerTransport rtp_protocol)
3124 HTTPContext *c = NULL;
3125 const char *proto_str;
3127 /* XXX: should output a warning page when coming
3128 close to the connection limit */
3129 if (nb_connections >= nb_max_connections)
3132 /* add a new connection */
3133 c = av_mallocz(sizeof(HTTPContext));
3138 c->poll_entry = NULL;
3139 c->from_addr = *from_addr;
3140 c->buffer_size = IOBUFFER_INIT_SIZE;
3141 c->buffer = av_malloc(c->buffer_size);
3146 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3147 c->state = HTTPSTATE_READY;
3148 c->is_packetized = 1;
3149 c->rtp_protocol = rtp_protocol;
3151 /* protocol is shown in statistics */
3152 switch(c->rtp_protocol) {
3153 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3154 proto_str = "MCAST";
3156 case RTSP_LOWER_TRANSPORT_UDP:
3159 case RTSP_LOWER_TRANSPORT_TCP:
3166 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3167 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3169 current_bandwidth += stream->bandwidth;
3171 c->next = first_http_ctx;
3183 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3184 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3186 static int rtp_new_av_stream(HTTPContext *c,
3187 int stream_index, struct sockaddr_in *dest_addr,
3188 HTTPContext *rtsp_c)
3190 AVFormatContext *ctx;
3193 URLContext *h = NULL;
3195 int max_packet_size;
3197 /* now we can open the relevant output stream */
3198 ctx = avformat_alloc_context();
3201 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3203 st = av_mallocz(sizeof(AVStream));
3206 st->codec= avcodec_alloc_context();
3207 ctx->nb_streams = 1;
3208 ctx->streams[0] = st;
3210 if (!c->stream->feed ||
3211 c->stream->feed == c->stream)
3212 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3215 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3217 st->priv_data = NULL;
3219 /* build destination RTP address */
3220 ipaddr = inet_ntoa(dest_addr->sin_addr);
3222 switch(c->rtp_protocol) {
3223 case RTSP_LOWER_TRANSPORT_UDP:
3224 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3227 /* XXX: also pass as parameter to function ? */
3228 if (c->stream->is_multicast) {
3230 ttl = c->stream->multicast_ttl;
3233 snprintf(ctx->filename, sizeof(ctx->filename),
3234 "rtp://%s:%d?multicast=1&ttl=%d",
3235 ipaddr, ntohs(dest_addr->sin_port), ttl);
3237 snprintf(ctx->filename, sizeof(ctx->filename),
3238 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3241 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3243 c->rtp_handles[stream_index] = h;
3244 max_packet_size = url_get_max_packet_size(h);
3246 case RTSP_LOWER_TRANSPORT_TCP:
3249 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3255 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3256 ipaddr, ntohs(dest_addr->sin_port),
3257 c->stream->filename, stream_index, c->protocol);
3259 /* normally, no packets should be output here, but the packet size may be checked */
3260 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3261 /* XXX: close stream */
3264 av_set_parameters(ctx, NULL);
3265 if (av_write_header(ctx) < 0) {
3272 url_close_dyn_buf(ctx->pb, &dummy_buf);
3275 c->rtp_ctx[stream_index] = ctx;
3279 /********************************************************************/
3280 /* ffserver initialization */
3282 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3286 fst = av_mallocz(sizeof(AVStream));
3289 fst->codec= avcodec_alloc_context();
3290 fst->priv_data = av_mallocz(sizeof(FeedData));
3291 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3292 fst->index = stream->nb_streams;
3293 av_set_pts_info(fst, 33, 1, 90000);
3294 stream->streams[stream->nb_streams++] = fst;
3298 /* return the stream number in the feed */
3299 static int add_av_stream(FFStream *feed, AVStream *st)
3302 AVCodecContext *av, *av1;
3306 for(i=0;i<feed->nb_streams;i++) {
3307 st = feed->streams[i];
3309 if (av1->codec_id == av->codec_id &&
3310 av1->codec_type == av->codec_type &&
3311 av1->bit_rate == av->bit_rate) {
3313 switch(av->codec_type) {
3314 case CODEC_TYPE_AUDIO:
3315 if (av1->channels == av->channels &&
3316 av1->sample_rate == av->sample_rate)
3319 case CODEC_TYPE_VIDEO:
3320 if (av1->width == av->width &&
3321 av1->height == av->height &&
3322 av1->time_base.den == av->time_base.den &&
3323 av1->time_base.num == av->time_base.num &&
3324 av1->gop_size == av->gop_size)
3333 fst = add_av_stream1(feed, av);
3336 return feed->nb_streams - 1;
3341 static void remove_stream(FFStream *stream)
3345 while (*ps != NULL) {
3353 /* specific mpeg4 handling : we extract the raw parameters */
3354 static void extract_mpeg4_header(AVFormatContext *infile)
3356 int mpeg4_count, i, size;
3362 for(i=0;i<infile->nb_streams;i++) {
3363 st = infile->streams[i];
3364 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3365 st->codec->extradata_size == 0) {
3372 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3373 while (mpeg4_count > 0) {
3374 if (av_read_packet(infile, &pkt) < 0)
3376 st = infile->streams[pkt.stream_index];
3377 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3378 st->codec->extradata_size == 0) {
3379 av_freep(&st->codec->extradata);
3380 /* fill extradata with the header */
3381 /* XXX: we make hard suppositions here ! */
3383 while (p < pkt.data + pkt.size - 4) {
3384 /* stop when vop header is found */
3385 if (p[0] == 0x00 && p[1] == 0x00 &&
3386 p[2] == 0x01 && p[3] == 0xb6) {
3387 size = p - pkt.data;
3388 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3389 st->codec->extradata = av_malloc(size);
3390 st->codec->extradata_size = size;
3391 memcpy(st->codec->extradata, pkt.data, size);
3398 av_free_packet(&pkt);
3402 /* compute the needed AVStream for each file */
3403 static void build_file_streams(void)
3405 FFStream *stream, *stream_next;
3406 AVFormatContext *infile;
3409 /* gather all streams */
3410 for(stream = first_stream; stream != NULL; stream = stream_next) {
3411 stream_next = stream->next;
3412 if (stream->stream_type == STREAM_TYPE_LIVE &&
3414 /* the stream comes from a file */
3415 /* try to open the file */
3417 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3418 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3419 /* specific case : if transport stream output to RTP,
3420 we use a raw transport stream reader */
3421 stream->ap_in->mpeg2ts_raw = 1;
3422 stream->ap_in->mpeg2ts_compute_pcr = 1;
3425 http_log("Opening file '%s'\n", stream->feed_filename);
3426 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3427 stream->ifmt, 0, stream->ap_in)) < 0) {
3428 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3429 /* remove stream (no need to spend more time on it) */
3431 remove_stream(stream);
3433 /* find all the AVStreams inside and reference them in
3435 if (av_find_stream_info(infile) < 0) {
3436 http_log("Could not find codec parameters from '%s'\n",
3437 stream->feed_filename);
3438 av_close_input_file(infile);
3441 extract_mpeg4_header(infile);
3443 for(i=0;i<infile->nb_streams;i++)
3444 add_av_stream1(stream, infile->streams[i]->codec);
3446 av_close_input_file(infile);
3452 /* compute the needed AVStream for each feed */
3453 static void build_feed_streams(void)
3455 FFStream *stream, *feed;
3458 /* gather all streams */
3459 for(stream = first_stream; stream != NULL; stream = stream->next) {
3460 feed = stream->feed;
3462 if (!stream->is_feed) {
3463 /* we handle a stream coming from a feed */
3464 for(i=0;i<stream->nb_streams;i++)
3465 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3470 /* gather all streams */
3471 for(stream = first_stream; stream != NULL; stream = stream->next) {
3472 feed = stream->feed;
3474 if (stream->is_feed) {
3475 for(i=0;i<stream->nb_streams;i++)
3476 stream->feed_streams[i] = i;
3481 /* create feed files if needed */
3482 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3485 if (url_exist(feed->feed_filename)) {
3486 /* See if it matches */
3490 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3491 /* Now see if it matches */
3492 if (s->nb_streams == feed->nb_streams) {
3494 for(i=0;i<s->nb_streams;i++) {
3496 sf = feed->streams[i];
3499 if (sf->index != ss->index ||
3501 http_log("Index & Id do not match for stream %d (%s)\n",
3502 i, feed->feed_filename);
3505 AVCodecContext *ccf, *ccs;
3509 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3511 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3512 http_log("Codecs do not match for stream %d\n", i);
3514 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3515 http_log("Codec bitrates do not match for stream %d\n", i);
3517 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3518 if (CHECK_CODEC(time_base.den) ||
3519 CHECK_CODEC(time_base.num) ||
3520 CHECK_CODEC(width) ||
3521 CHECK_CODEC(height)) {
3522 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3525 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3526 if (CHECK_CODEC(sample_rate) ||
3527 CHECK_CODEC(channels) ||
3528 CHECK_CODEC(frame_size)) {
3529 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3533 http_log("Unknown codec type\n");
3541 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3542 feed->feed_filename, s->nb_streams, feed->nb_streams);
3544 av_close_input_file(s);
3546 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3547 feed->feed_filename);
3550 if (feed->readonly) {
3551 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3552 feed->feed_filename);
3555 unlink(feed->feed_filename);
3558 if (!url_exist(feed->feed_filename)) {
3559 AVFormatContext s1 = {0}, *s = &s1;
3561 if (feed->readonly) {
3562 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3563 feed->feed_filename);
3567 /* only write the header of the ffm file */
3568 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3569 http_log("Could not open output feed file '%s'\n",
3570 feed->feed_filename);
3573 s->oformat = feed->fmt;
3574 s->nb_streams = feed->nb_streams;
3575 for(i=0;i<s->nb_streams;i++) {
3577 st = feed->streams[i];
3580 av_set_parameters(s, NULL);
3581 if (av_write_header(s) < 0) {
3582 http_log("Container doesn't supports the required parameters\n");
3585 /* XXX: need better api */
3586 av_freep(&s->priv_data);
3589 /* get feed size and write index */
3590 fd = open(feed->feed_filename, O_RDONLY);
3592 http_log("Could not open output feed file '%s'\n",
3593 feed->feed_filename);
3597 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3598 feed->feed_size = lseek(fd, 0, SEEK_END);
3599 /* ensure that we do not wrap before the end of file */
3600 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3601 feed->feed_max_size = feed->feed_size;
3607 /* compute the bandwidth used by each stream */
3608 static void compute_bandwidth(void)
3614 for(stream = first_stream; stream != NULL; stream = stream->next) {
3616 for(i=0;i<stream->nb_streams;i++) {
3617 AVStream *st = stream->streams[i];
3618 switch(st->codec->codec_type) {
3619 case CODEC_TYPE_AUDIO:
3620 case CODEC_TYPE_VIDEO:
3621 bandwidth += st->codec->bit_rate;
3627 stream->bandwidth = (bandwidth + 999) / 1000;
3631 /* add a codec and set the default parameters */
3632 static void add_codec(FFStream *stream, AVCodecContext *av)
3636 /* compute default parameters */
3637 switch(av->codec_type) {
3638 case CODEC_TYPE_AUDIO:
3639 if (av->bit_rate == 0)
3640 av->bit_rate = 64000;
3641 if (av->sample_rate == 0)
3642 av->sample_rate = 22050;
3643 if (av->channels == 0)
3646 case CODEC_TYPE_VIDEO:
3647 if (av->bit_rate == 0)
3648 av->bit_rate = 64000;
3649 if (av->time_base.num == 0){
3650 av->time_base.den = 5;
3651 av->time_base.num = 1;
3653 if (av->width == 0 || av->height == 0) {
3657 /* Bitrate tolerance is less for streaming */
3658 if (av->bit_rate_tolerance == 0)
3659 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3660 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3665 if (av->max_qdiff == 0)
3667 av->qcompress = 0.5;
3670 if (!av->nsse_weight)
3671 av->nsse_weight = 8;
3673 av->frame_skip_cmp = FF_CMP_DCTMAX;
3674 av->me_method = ME_EPZS;
3675 av->rc_buffer_aggressivity = 1.0;
3678 av->rc_eq = "tex^qComp";
3679 if (!av->i_quant_factor)
3680 av->i_quant_factor = -0.8;
3681 if (!av->b_quant_factor)
3682 av->b_quant_factor = 1.25;
3683 if (!av->b_quant_offset)
3684 av->b_quant_offset = 1.25;
3685 if (!av->rc_max_rate)
3686 av->rc_max_rate = av->bit_rate * 2;
3688 if (av->rc_max_rate && !av->rc_buffer_size) {
3689 av->rc_buffer_size = av->rc_max_rate;
3698 st = av_mallocz(sizeof(AVStream));
3701 st->codec = avcodec_alloc_context();
3702 stream->streams[stream->nb_streams++] = st;
3703 memcpy(st->codec, av, sizeof(AVCodecContext));
3706 static enum CodecID opt_audio_codec(const char *arg)
3708 AVCodec *p= avcodec_find_encoder_by_name(arg);
3710 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3711 return CODEC_ID_NONE;
3716 static enum CodecID opt_video_codec(const char *arg)
3718 AVCodec *p= avcodec_find_encoder_by_name(arg);
3720 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3721 return CODEC_ID_NONE;
3726 /* simplistic plugin support */
3729 static void load_module(const char *filename)
3732 void (*init_func)(void);
3733 dll = dlopen(filename, RTLD_NOW);
3735 fprintf(stderr, "Could not load module '%s' - %s\n",
3736 filename, dlerror());
3740 init_func = dlsym(dll, "ffserver_module_init");
3743 "%s: init function 'ffserver_module_init()' not found\n",
3752 static int ffserver_opt_default(const char *opt, const char *arg,
3753 AVCodecContext *avctx, int type)
3756 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
3758 ret = av_set_string3(avctx, opt, arg, 1, NULL);
3762 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
3763 const char *mime_type)
3765 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3768 AVOutputFormat *stream_fmt;
3769 char stream_format_name[64];
3771 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
3772 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
3781 static int parse_ffconfig(const char *filename)
3788 int val, errors, line_num;
3789 FFStream **last_stream, *stream, *redirect;
3790 FFStream **last_feed, *feed, *s;
3791 AVCodecContext audio_enc, video_enc;
3792 enum CodecID audio_id, video_id;
3794 f = fopen(filename, "r");
3802 first_stream = NULL;
3803 last_stream = &first_stream;
3805 last_feed = &first_feed;
3809 audio_id = CODEC_ID_NONE;
3810 video_id = CODEC_ID_NONE;
3812 if (fgets(line, sizeof(line), f) == NULL)
3818 if (*p == '\0' || *p == '#')
3821 get_arg(cmd, sizeof(cmd), &p);
3823 if (!strcasecmp(cmd, "Port")) {
3824 get_arg(arg, sizeof(arg), &p);
3826 if (val < 1 || val > 65536) {
3827 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3828 filename, line_num, arg);
3831 my_http_addr.sin_port = htons(val);
3832 } else if (!strcasecmp(cmd, "BindAddress")) {
3833 get_arg(arg, sizeof(arg), &p);
3834 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3835 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3836 filename, line_num, arg);
3839 } else if (!strcasecmp(cmd, "NoDaemon")) {
3840 ffserver_daemon = 0;
3841 } else if (!strcasecmp(cmd, "RTSPPort")) {
3842 get_arg(arg, sizeof(arg), &p);
3844 if (val < 1 || val > 65536) {
3845 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3846 filename, line_num, arg);
3849 my_rtsp_addr.sin_port = htons(atoi(arg));
3850 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3851 get_arg(arg, sizeof(arg), &p);
3852 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3853 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3854 filename, line_num, arg);
3857 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
3858 get_arg(arg, sizeof(arg), &p);
3860 if (val < 1 || val > 65536) {
3861 fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n",
3862 filename, line_num, arg);
3865 nb_max_http_connections = val;
3866 } else if (!strcasecmp(cmd, "MaxClients")) {
3867 get_arg(arg, sizeof(arg), &p);
3869 if (val < 1 || val > nb_max_http_connections) {
3870 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3871 filename, line_num, arg);
3874 nb_max_connections = val;
3876 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3878 get_arg(arg, sizeof(arg), &p);
3880 if (llval < 10 || llval > 10000000) {
3881 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3882 filename, line_num, arg);
3885 max_bandwidth = llval;
3886 } else if (!strcasecmp(cmd, "CustomLog")) {
3887 if (!ffserver_debug)
3888 get_arg(logfilename, sizeof(logfilename), &p);
3889 } else if (!strcasecmp(cmd, "<Feed")) {
3890 /*********************************************/
3891 /* Feed related options */
3893 if (stream || feed) {
3894 fprintf(stderr, "%s:%d: Already in a tag\n",
3895 filename, line_num);
3897 feed = av_mallocz(sizeof(FFStream));
3898 get_arg(feed->filename, sizeof(feed->filename), &p);
3899 q = strrchr(feed->filename, '>');
3903 for (s = first_feed; s; s = s->next) {
3904 if (!strcmp(feed->filename, s->filename)) {
3905 fprintf(stderr, "%s:%d: Feed '%s' already registered\n",
3906 filename, line_num, s->filename);
3911 feed->fmt = av_guess_format("ffm", NULL, NULL);
3912 /* defaut feed file */
3913 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3914 "/tmp/%s.ffm", feed->filename);
3915 feed->feed_max_size = 5 * 1024 * 1024;
3917 feed->feed = feed; /* self feeding :-) */
3919 /* add in stream list */
3920 *last_stream = feed;
3921 last_stream = &feed->next;
3922 /* add in feed list */
3924 last_feed = &feed->next_feed;
3926 } else if (!strcasecmp(cmd, "Launch")) {
3930 feed->child_argv = av_mallocz(64 * sizeof(char *));
3932 for (i = 0; i < 62; i++) {
3933 get_arg(arg, sizeof(arg), &p);
3937 feed->child_argv[i] = av_strdup(arg);
3940 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3942 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3944 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3945 inet_ntoa(my_http_addr.sin_addr),
3946 ntohs(my_http_addr.sin_port), feed->filename);
3948 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3950 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3952 } else if (stream) {
3953 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3955 } else if (!strcasecmp(cmd, "File")) {
3957 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3959 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3960 } else if (!strcasecmp(cmd, "Truncate")) {
3962 get_arg(arg, sizeof(arg), &p);
3963 feed->truncate = strtod(arg, NULL);
3965 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3970 get_arg(arg, sizeof(arg), &p);
3972 fsize = strtod(p1, &p1);
3973 switch(toupper(*p1)) {
3978 fsize *= 1024 * 1024;
3981 fsize *= 1024 * 1024 * 1024;
3984 feed->feed_max_size = (int64_t)fsize;
3985 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
3986 fprintf(stderr, "%s:%d: Feed max file size is too small, "
3987 "must be at least %d\n", filename, line_num, FFM_PACKET_SIZE*4);
3991 } else if (!strcasecmp(cmd, "</Feed>")) {
3993 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3994 filename, line_num);
3998 } else if (!strcasecmp(cmd, "<Stream")) {
3999 /*********************************************/
4000 /* Stream related options */
4002 if (stream || feed) {
4003 fprintf(stderr, "%s:%d: Already in a tag\n",
4004 filename, line_num);
4007 const AVClass *class;
4008 stream = av_mallocz(sizeof(FFStream));
4009 get_arg(stream->filename, sizeof(stream->filename), &p);
4010 q = strrchr(stream->filename, '>');
4014 for (s = first_stream; s; s = s->next) {
4015 if (!strcmp(stream->filename, s->filename)) {
4016 fprintf(stderr, "%s:%d: Stream '%s' already registered\n",
4017 filename, line_num, s->filename);
4022 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4023 /* fetch avclass so AVOption works
4024 * FIXME try to use avcodec_get_context_defaults2
4025 * without changing defaults too much */
4026 avcodec_get_context_defaults(&video_enc);
4027 class = video_enc.av_class;
4028 memset(&audio_enc, 0, sizeof(AVCodecContext));
4029 memset(&video_enc, 0, sizeof(AVCodecContext));
4030 audio_enc.av_class = class;
4031 video_enc.av_class = class;
4032 audio_id = CODEC_ID_NONE;
4033 video_id = CODEC_ID_NONE;
4035 audio_id = stream->fmt->audio_codec;
4036 video_id = stream->fmt->video_codec;
4039 *last_stream = stream;
4040 last_stream = &stream->next;
4042 } else if (!strcasecmp(cmd, "Feed")) {
4043 get_arg(arg, sizeof(arg), &p);
4048 while (sfeed != NULL) {
4049 if (!strcmp(sfeed->filename, arg))
4051 sfeed = sfeed->next_feed;
4054 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
4055 filename, line_num, arg);
4057 stream->feed = sfeed;
4059 } else if (!strcasecmp(cmd, "Format")) {
4060 get_arg(arg, sizeof(arg), &p);
4062 if (!strcmp(arg, "status")) {
4063 stream->stream_type = STREAM_TYPE_STATUS;
4066 stream->stream_type = STREAM_TYPE_LIVE;
4067 /* jpeg cannot be used here, so use single frame jpeg */
4068 if (!strcmp(arg, "jpeg"))
4069 strcpy(arg, "mjpeg");
4070 stream->fmt = ffserver_guess_format(arg, NULL, NULL);
4072 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4073 filename, line_num, arg);
4078 audio_id = stream->fmt->audio_codec;
4079 video_id = stream->fmt->video_codec;
4082 } else if (!strcasecmp(cmd, "InputFormat")) {
4083 get_arg(arg, sizeof(arg), &p);
4085 stream->ifmt = av_find_input_format(arg);
4086 if (!stream->ifmt) {
4087 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4088 filename, line_num, arg);
4091 } else if (!strcasecmp(cmd, "FaviconURL")) {
4092 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4093 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4095 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4096 filename, line_num);
4099 } else if (!strcasecmp(cmd, "Author")) {
4101 get_arg(stream->author, sizeof(stream->author), &p);
4102 } else if (!strcasecmp(cmd, "Comment")) {
4104 get_arg(stream->comment, sizeof(stream->comment), &p);
4105 } else if (!strcasecmp(cmd, "Copyright")) {
4107 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4108 } else if (!strcasecmp(cmd, "Title")) {
4110 get_arg(stream->title, sizeof(stream->title), &p);
4111 } else if (!strcasecmp(cmd, "Preroll")) {
4112 get_arg(arg, sizeof(arg), &p);
4114 stream->prebuffer = atof(arg) * 1000;
4115 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4117 stream->send_on_key = 1;
4118 } else if (!strcasecmp(cmd, "AudioCodec")) {
4119 get_arg(arg, sizeof(arg), &p);
4120 audio_id = opt_audio_codec(arg);
4121 if (audio_id == CODEC_ID_NONE) {
4122 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4123 filename, line_num, arg);
4126 } else if (!strcasecmp(cmd, "VideoCodec")) {
4127 get_arg(arg, sizeof(arg), &p);
4128 video_id = opt_video_codec(arg);
4129 if (video_id == CODEC_ID_NONE) {
4130 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4131 filename, line_num, arg);
4134 } else if (!strcasecmp(cmd, "MaxTime")) {
4135 get_arg(arg, sizeof(arg), &p);
4137 stream->max_time = atof(arg) * 1000;
4138 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4139 get_arg(arg, sizeof(arg), &p);
4141 audio_enc.bit_rate = atoi(arg) * 1000;
4142 } else if (!strcasecmp(cmd, "AudioChannels")) {
4143 get_arg(arg, sizeof(arg), &p);
4145 audio_enc.channels = atoi(arg);
4146 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4147 get_arg(arg, sizeof(arg), &p);
4149 audio_enc.sample_rate = atoi(arg);
4150 } else if (!strcasecmp(cmd, "AudioQuality")) {
4151 get_arg(arg, sizeof(arg), &p);
4153 // audio_enc.quality = atof(arg) * 1000;
4155 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4157 int minrate, maxrate;
4159 get_arg(arg, sizeof(arg), &p);
4161 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4162 video_enc.rc_min_rate = minrate * 1000;
4163 video_enc.rc_max_rate = maxrate * 1000;
4165 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4166 filename, line_num, arg);
4170 } else if (!strcasecmp(cmd, "Debug")) {
4172 get_arg(arg, sizeof(arg), &p);
4173 video_enc.debug = strtol(arg,0,0);
4175 } else if (!strcasecmp(cmd, "Strict")) {
4177 get_arg(arg, sizeof(arg), &p);
4178 video_enc.strict_std_compliance = atoi(arg);
4180 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4182 get_arg(arg, sizeof(arg), &p);
4183 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4185 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4187 get_arg(arg, sizeof(arg), &p);
4188 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4190 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4191 get_arg(arg, sizeof(arg), &p);
4193 video_enc.bit_rate = atoi(arg) * 1000;
4195 } else if (!strcasecmp(cmd, "VideoSize")) {
4196 get_arg(arg, sizeof(arg), &p);
4198 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4199 if ((video_enc.width % 16) != 0 ||
4200 (video_enc.height % 16) != 0) {
4201 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4202 filename, line_num);
4206 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4207 get_arg(arg, sizeof(arg), &p);
4209 AVRational frame_rate;
4210 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4211 fprintf(stderr, "Incorrect frame rate\n");
4214 video_enc.time_base.num = frame_rate.den;
4215 video_enc.time_base.den = frame_rate.num;
4218 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4219 get_arg(arg, sizeof(arg), &p);
4221 video_enc.gop_size = atoi(arg);
4222 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4224 video_enc.gop_size = 1;
4225 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4227 video_enc.mb_decision = FF_MB_DECISION_BITS;
4228 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4230 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4231 video_enc.flags |= CODEC_FLAG_4MV;
4233 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4234 !strcasecmp(cmd, "AVOptionAudio")) {
4236 AVCodecContext *avctx;
4238 get_arg(arg, sizeof(arg), &p);
4239 get_arg(arg2, sizeof(arg2), &p);
4240 if (!strcasecmp(cmd, "AVOptionVideo")) {
4242 type = AV_OPT_FLAG_VIDEO_PARAM;
4245 type = AV_OPT_FLAG_AUDIO_PARAM;
4247 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4248 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4251 } else if (!strcasecmp(cmd, "VideoTag")) {
4252 get_arg(arg, sizeof(arg), &p);
4253 if ((strlen(arg) == 4) && stream)
4254 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4255 } else if (!strcasecmp(cmd, "BitExact")) {
4257 video_enc.flags |= CODEC_FLAG_BITEXACT;
4258 } else if (!strcasecmp(cmd, "DctFastint")) {
4260 video_enc.dct_algo = FF_DCT_FASTINT;
4261 } else if (!strcasecmp(cmd, "IdctSimple")) {
4263 video_enc.idct_algo = FF_IDCT_SIMPLE;
4264 } else if (!strcasecmp(cmd, "Qscale")) {
4265 get_arg(arg, sizeof(arg), &p);
4267 video_enc.flags |= CODEC_FLAG_QSCALE;
4268 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4270 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4271 get_arg(arg, sizeof(arg), &p);
4273 video_enc.max_qdiff = atoi(arg);
4274 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4275 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4276 filename, line_num);
4280 } else if (!strcasecmp(cmd, "VideoQMax")) {
4281 get_arg(arg, sizeof(arg), &p);
4283 video_enc.qmax = atoi(arg);
4284 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4285 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4286 filename, line_num);
4290 } else if (!strcasecmp(cmd, "VideoQMin")) {
4291 get_arg(arg, sizeof(arg), &p);
4293 video_enc.qmin = atoi(arg);
4294 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4295 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4296 filename, line_num);
4300 } else if (!strcasecmp(cmd, "LumaElim")) {
4301 get_arg(arg, sizeof(arg), &p);
4303 video_enc.luma_elim_threshold = atoi(arg);
4304 } else if (!strcasecmp(cmd, "ChromaElim")) {
4305 get_arg(arg, sizeof(arg), &p);
4307 video_enc.chroma_elim_threshold = atoi(arg);
4308 } else if (!strcasecmp(cmd, "LumiMask")) {
4309 get_arg(arg, sizeof(arg), &p);
4311 video_enc.lumi_masking = atof(arg);
4312 } else if (!strcasecmp(cmd, "DarkMask")) {
4313 get_arg(arg, sizeof(arg), &p);
4315 video_enc.dark_masking = atof(arg);
4316 } else if (!strcasecmp(cmd, "NoVideo")) {
4317 video_id = CODEC_ID_NONE;
4318 } else if (!strcasecmp(cmd, "NoAudio")) {
4319 audio_id = CODEC_ID_NONE;
4320 } else if (!strcasecmp(cmd, "ACL")) {
4323 get_arg(arg, sizeof(arg), &p);
4324 if (strcasecmp(arg, "allow") == 0)
4325 acl.action = IP_ALLOW;
4326 else if (strcasecmp(arg, "deny") == 0)
4327 acl.action = IP_DENY;
4329 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4330 filename, line_num, arg);
4334 get_arg(arg, sizeof(arg), &p);
4336 if (resolve_host(&acl.first, arg) != 0) {
4337 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4338 filename, line_num, arg);
4341 acl.last = acl.first;
4343 get_arg(arg, sizeof(arg), &p);
4346 if (resolve_host(&acl.last, arg) != 0) {
4347 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4348 filename, line_num, arg);
4354 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4355 IPAddressACL **naclp = 0;
4361 naclp = &stream->acl;
4365 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4366 filename, line_num);
4372 naclp = &(*naclp)->next;
4377 } else if (!strcasecmp(cmd, "RTSPOption")) {
4378 get_arg(arg, sizeof(arg), &p);
4380 av_freep(&stream->rtsp_option);
4381 stream->rtsp_option = av_strdup(arg);
4383 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4384 get_arg(arg, sizeof(arg), &p);
4386 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4387 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4388 filename, line_num, arg);
4391 stream->is_multicast = 1;
4392 stream->loop = 1; /* default is looping */
4394 } else if (!strcasecmp(cmd, "MulticastPort")) {
4395 get_arg(arg, sizeof(arg), &p);
4397 stream->multicast_port = atoi(arg);
4398 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4399 get_arg(arg, sizeof(arg), &p);
4401 stream->multicast_ttl = atoi(arg);
4402 } else if (!strcasecmp(cmd, "NoLoop")) {
4405 } else if (!strcasecmp(cmd, "</Stream>")) {
4407 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4408 filename, line_num);
4411 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4412 if (audio_id != CODEC_ID_NONE) {
4413 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4414 audio_enc.codec_id = audio_id;
4415 add_codec(stream, &audio_enc);
4417 if (video_id != CODEC_ID_NONE) {
4418 video_enc.codec_type = CODEC_TYPE_VIDEO;
4419 video_enc.codec_id = video_id;
4420 add_codec(stream, &video_enc);
4425 } else if (!strcasecmp(cmd, "<Redirect")) {
4426 /*********************************************/
4428 if (stream || feed || redirect) {
4429 fprintf(stderr, "%s:%d: Already in a tag\n",
4430 filename, line_num);
4433 redirect = av_mallocz(sizeof(FFStream));
4434 *last_stream = redirect;
4435 last_stream = &redirect->next;
4437 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4438 q = strrchr(redirect->filename, '>');
4441 redirect->stream_type = STREAM_TYPE_REDIRECT;
4443 } else if (!strcasecmp(cmd, "URL")) {
4445 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4446 } else if (!strcasecmp(cmd, "</Redirect>")) {
4448 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4449 filename, line_num);
4452 if (!redirect->feed_filename[0]) {
4453 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4454 filename, line_num);
4459 } else if (!strcasecmp(cmd, "LoadModule")) {
4460 get_arg(arg, sizeof(arg), &p);
4464 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4465 filename, line_num, arg);
4469 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4470 filename, line_num, cmd);
4481 static void handle_child_exit(int sig)
4486 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4489 for (feed = first_feed; feed; feed = feed->next) {
4490 if (feed->pid == pid) {
4491 int uptime = time(0) - feed->pid_start;
4494 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4497 /* Turn off any more restarts */
4498 feed->child_argv = 0;
4503 need_to_start_children = 1;
4506 static void opt_debug(void)
4509 ffserver_daemon = 0;
4510 logfilename[0] = '-';
4513 static void show_help(void)
4515 printf("usage: ffserver [options]\n"
4516 "Hyper fast multi format Audio/Video streaming server\n");
4518 show_help_options(options, "Main options:\n", 0, 0);
4521 static const OptionDef options[] = {
4522 #include "cmdutils_common_opts.h"
4523 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4524 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4525 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4529 int main(int argc, char **argv)
4531 struct sigaction sigact;
4537 config_filename = "/etc/ffserver.conf";
4539 my_program_name = argv[0];
4540 my_program_dir = getcwd(0, 0);
4541 ffserver_daemon = 1;
4543 parse_options(argc, argv, options, NULL);
4545 unsetenv("http_proxy"); /* Kill the http_proxy */
4547 av_lfg_init(&random_state, ff_random_get_seed());
4549 memset(&sigact, 0, sizeof(sigact));
4550 sigact.sa_handler = handle_child_exit;
4551 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4552 sigaction(SIGCHLD, &sigact, 0);
4554 if (parse_ffconfig(config_filename) < 0) {
4555 fprintf(stderr, "Incorrect config file - exiting.\n");
4559 /* open log file if needed */
4560 if (logfilename[0] != '\0') {
4561 if (!strcmp(logfilename, "-"))
4564 logfile = fopen(logfilename, "a");
4565 av_log_set_callback(http_av_log);
4568 build_file_streams();
4570 build_feed_streams();
4572 compute_bandwidth();
4574 /* put the process in background and detach it from its TTY */
4575 if (ffserver_daemon) {
4582 } else if (pid > 0) {
4589 open("/dev/null", O_RDWR);
4590 if (strcmp(logfilename, "-") != 0) {
4600 signal(SIGPIPE, SIG_IGN);
4602 if (ffserver_daemon)
4605 if (http_server() < 0) {
4606 http_log("Could not start server\n");