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
23 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
28 #include "libavutil/random.h"
29 #include "libavutil/avstring.h"
30 #include "libavformat/avformat.h"
31 #include "libavformat/network.h"
32 #include "libavformat/os_support.h"
33 #include "libavformat/rtp.h"
34 #include "libavformat/rtsp.h"
35 #include "libavcodec/opt.h"
39 #include <sys/ioctl.h>
45 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
57 const char program_name[] = "FFserver";
58 const int program_birth_year = 2000;
60 static const OptionDef options[];
62 /* maximum number of simultaneous HTTP connections */
63 #define HTTP_MAX_CONNECTIONS 2000
66 HTTPSTATE_WAIT_REQUEST,
67 HTTPSTATE_SEND_HEADER,
68 HTTPSTATE_SEND_DATA_HEADER,
69 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
70 HTTPSTATE_SEND_DATA_TRAILER,
71 HTTPSTATE_RECEIVE_DATA,
72 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
75 RTSPSTATE_WAIT_REQUEST,
77 RTSPSTATE_SEND_PACKET,
80 static const char *http_state[] = {
96 #define IOBUFFER_INIT_SIZE 8192
98 /* timeouts are in ms */
99 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
100 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
102 #define SYNC_TIMEOUT (10 * 1000)
105 int64_t count1, count2;
106 int64_t time1, time2;
109 /* context associated with one connection */
110 typedef struct HTTPContext {
111 enum HTTPState state;
112 int fd; /* socket file descriptor */
113 struct sockaddr_in from_addr; /* origin */
114 struct pollfd *poll_entry; /* used when polling */
116 uint8_t *buffer_ptr, *buffer_end;
119 struct HTTPContext *next;
120 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
124 /* input format handling */
125 AVFormatContext *fmt_in;
126 int64_t start_time; /* In milliseconds - this wraps fairly often */
127 int64_t first_pts; /* initial pts value */
128 int64_t cur_pts; /* current pts value from the stream in us */
129 int64_t cur_frame_duration; /* duration of the current frame in us */
130 int cur_frame_bytes; /* output frame size, needed to compute
131 the time at which we send each
133 int pts_stream_index; /* stream we choose as clock reference */
134 int64_t cur_clock; /* current clock reference value in us */
135 /* output format handling */
136 struct FFStream *stream;
137 /* -1 is invalid stream */
138 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
139 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
141 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
142 int last_packet_sent; /* true if last data packet was sent */
144 DataRateData datarate;
151 int is_packetized; /* if true, the stream is packetized */
152 int packet_stream_index; /* current stream for output in state machine */
154 /* RTSP state specific */
155 uint8_t *pb_buffer; /* XXX: use that in all the code */
157 int seq; /* RTSP sequence number */
159 /* RTP state specific */
160 enum RTSPProtocol rtp_protocol;
161 char session_id[32]; /* session id */
162 AVFormatContext *rtp_ctx[MAX_STREAMS];
164 /* RTP/UDP specific */
165 URLContext *rtp_handles[MAX_STREAMS];
167 /* RTP/TCP specific */
168 struct HTTPContext *rtsp_c;
169 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
172 /* each generated stream is described here */
176 STREAM_TYPE_REDIRECT,
179 enum IPAddressAction {
184 typedef struct IPAddressACL {
185 struct IPAddressACL *next;
186 enum IPAddressAction action;
187 /* These are in host order */
188 struct in_addr first;
192 /* description of each stream of the ffserver.conf file */
193 typedef struct FFStream {
194 enum StreamType stream_type;
195 char filename[1024]; /* stream filename */
196 struct FFStream *feed; /* feed we are using (can be null if
198 AVFormatParameters *ap_in; /* input parameters */
199 AVInputFormat *ifmt; /* if non NULL, force input format */
203 int prebuffer; /* Number of millseconds early to start */
204 int64_t max_time; /* Number of milliseconds to run */
206 AVStream *streams[MAX_STREAMS];
207 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
208 char feed_filename[1024]; /* file name of the feed storage, or
209 input file name for a stream */
214 pid_t pid; /* Of ffmpeg process */
215 time_t pid_start; /* Of ffmpeg process */
217 struct FFStream *next;
218 unsigned bandwidth; /* bandwidth, in kbits/s */
221 /* multicast specific */
223 struct in_addr multicast_ip;
224 int multicast_port; /* first port used for multicast */
226 int loop; /* if true, send the stream in loops (only meaningful if file) */
229 int feed_opened; /* true if someone is writing to the feed */
230 int is_feed; /* true if it is a feed */
231 int readonly; /* True if writing is prohibited to the file */
233 int64_t bytes_served;
234 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
235 int64_t feed_write_index; /* current write position in feed (it wraps around) */
236 int64_t feed_size; /* current size of feed */
237 struct FFStream *next_feed;
240 typedef struct FeedData {
241 long long data_count;
242 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
245 static struct sockaddr_in my_http_addr;
246 static struct sockaddr_in my_rtsp_addr;
248 static char logfilename[1024];
249 static HTTPContext *first_http_ctx;
250 static FFStream *first_feed; /* contains only feeds */
251 static FFStream *first_stream; /* contains all streams, including feeds */
253 static void new_connection(int server_fd, int is_rtsp);
254 static void close_connection(HTTPContext *c);
257 static int handle_connection(HTTPContext *c);
258 static int http_parse_request(HTTPContext *c);
259 static int http_send_data(HTTPContext *c);
260 static void compute_status(HTTPContext *c);
261 static int open_input_stream(HTTPContext *c, const char *info);
262 static int http_start_receive_data(HTTPContext *c);
263 static int http_receive_data(HTTPContext *c);
266 static int rtsp_parse_request(HTTPContext *c);
267 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
268 static void rtsp_cmd_options(HTTPContext *c, const char *url);
269 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
270 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
271 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
275 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
276 struct in_addr my_ip);
279 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
280 FFStream *stream, const char *session_id,
281 enum RTSPProtocol rtp_protocol);
282 static int rtp_new_av_stream(HTTPContext *c,
283 int stream_index, struct sockaddr_in *dest_addr,
284 HTTPContext *rtsp_c);
286 static const char *my_program_name;
287 static const char *my_program_dir;
289 static const char *config_filename;
290 static int ffserver_debug;
291 static int ffserver_daemon;
292 static int no_launch;
293 static int need_to_start_children;
295 static int nb_max_connections = 5;
296 static int nb_connections;
298 static uint64_t max_bandwidth = 1000;
299 static uint64_t current_bandwidth;
301 static int64_t cur_time; // Making this global saves on passing it around everywhere
303 static AVRandomState random_state;
305 static FILE *logfile = NULL;
307 static char *ctime1(char *buf2)
315 p = buf2 + strlen(p) - 1;
321 static void http_vlog(const char *fmt, va_list vargs)
323 static int print_prefix = 1;
328 fprintf(logfile, "%s ", buf);
330 print_prefix = strstr(fmt, "\n") != NULL;
331 vfprintf(logfile, fmt, vargs);
336 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
339 va_start(vargs, fmt);
340 http_vlog(fmt, vargs);
344 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
346 static int print_prefix = 1;
347 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
348 if (level > av_log_level)
350 if (print_prefix && avc)
351 http_log("[%s @ %p]", avc->item_name(ptr), avc);
352 print_prefix = strstr(fmt, "\n") != NULL;
353 http_vlog(fmt, vargs);
356 static void log_connection(HTTPContext *c)
361 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
362 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
363 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
366 static void update_datarate(DataRateData *drd, int64_t count)
368 if (!drd->time1 && !drd->count1) {
369 drd->time1 = drd->time2 = cur_time;
370 drd->count1 = drd->count2 = count;
371 } else if (cur_time - drd->time2 > 5000) {
372 drd->time1 = drd->time2;
373 drd->count1 = drd->count2;
374 drd->time2 = cur_time;
379 /* In bytes per second */
380 static int compute_datarate(DataRateData *drd, int64_t count)
382 if (cur_time == drd->time1)
385 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
389 static void start_children(FFStream *feed)
394 for (; feed; feed = feed->next) {
395 if (feed->child_argv && !feed->pid) {
396 feed->pid_start = time(0);
401 http_log("Unable to create children\n");
410 for (i = 3; i < 256; i++)
413 if (!ffserver_debug) {
414 i = open("/dev/null", O_RDWR);
423 av_strlcpy(pathname, my_program_name, sizeof(pathname));
425 slash = strrchr(pathname, '/');
430 strcpy(slash, "ffmpeg");
432 /* This is needed to make relative pathnames work */
433 chdir(my_program_dir);
435 signal(SIGPIPE, SIG_DFL);
437 execvp(pathname, feed->child_argv);
445 /* open a listening socket */
446 static int socket_open_listen(struct sockaddr_in *my_addr)
450 server_fd = socket(AF_INET,SOCK_STREAM,0);
457 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
459 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
461 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
463 closesocket(server_fd);
467 if (listen (server_fd, 5) < 0) {
469 closesocket(server_fd);
472 ff_socket_nonblock(server_fd, 1);
477 /* start all multicast streams */
478 static void start_multicast(void)
483 struct sockaddr_in dest_addr;
484 int default_port, stream_index;
487 for(stream = first_stream; stream != NULL; stream = stream->next) {
488 if (stream->is_multicast) {
489 /* open the RTP connection */
490 snprintf(session_id, sizeof(session_id), "%08x%08x",
491 av_random(&random_state), av_random(&random_state));
493 /* choose a port if none given */
494 if (stream->multicast_port == 0) {
495 stream->multicast_port = default_port;
499 dest_addr.sin_family = AF_INET;
500 dest_addr.sin_addr = stream->multicast_ip;
501 dest_addr.sin_port = htons(stream->multicast_port);
503 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
504 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
508 if (open_input_stream(rtp_c, "") < 0) {
509 http_log("Could not open input stream for stream '%s'\n",
514 /* open each RTP stream */
515 for(stream_index = 0; stream_index < stream->nb_streams;
517 dest_addr.sin_port = htons(stream->multicast_port +
519 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
520 http_log("Could not open output stream '%s/streamid=%d'\n",
521 stream->filename, stream_index);
526 /* change state to send data */
527 rtp_c->state = HTTPSTATE_SEND_DATA;
532 /* main loop of the http server */
533 static int http_server(void)
535 int server_fd = 0, rtsp_server_fd = 0;
536 int ret, delay, delay1;
537 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
538 HTTPContext *c, *c_next;
540 if (my_http_addr.sin_port) {
541 server_fd = socket_open_listen(&my_http_addr);
546 if (my_rtsp_addr.sin_port) {
547 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
548 if (rtsp_server_fd < 0)
552 if (!rtsp_server_fd && !server_fd) {
553 http_log("HTTP and RTSP disabled.\n");
557 http_log("ffserver started.\n");
559 start_children(first_feed);
561 first_http_ctx = NULL;
567 poll_entry = poll_table;
569 poll_entry->fd = server_fd;
570 poll_entry->events = POLLIN;
573 if (rtsp_server_fd) {
574 poll_entry->fd = rtsp_server_fd;
575 poll_entry->events = POLLIN;
579 /* wait for events on each HTTP handle */
586 case HTTPSTATE_SEND_HEADER:
587 case RTSPSTATE_SEND_REPLY:
588 case RTSPSTATE_SEND_PACKET:
589 c->poll_entry = poll_entry;
591 poll_entry->events = POLLOUT;
594 case HTTPSTATE_SEND_DATA_HEADER:
595 case HTTPSTATE_SEND_DATA:
596 case HTTPSTATE_SEND_DATA_TRAILER:
597 if (!c->is_packetized) {
598 /* for TCP, we output as much as we can (may need to put a limit) */
599 c->poll_entry = poll_entry;
601 poll_entry->events = POLLOUT;
604 /* when ffserver is doing the timing, we work by
605 looking at which packet need to be sent every
607 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
612 case HTTPSTATE_WAIT_REQUEST:
613 case HTTPSTATE_RECEIVE_DATA:
614 case HTTPSTATE_WAIT_FEED:
615 case RTSPSTATE_WAIT_REQUEST:
616 /* need to catch errors */
617 c->poll_entry = poll_entry;
619 poll_entry->events = POLLIN;/* Maybe this will work */
623 c->poll_entry = NULL;
629 /* wait for an event on one connection. We poll at least every
630 second to handle timeouts */
632 ret = poll(poll_table, poll_entry - poll_table, delay);
633 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
634 ff_neterrno() != FF_NETERROR(EINTR))
638 cur_time = av_gettime() / 1000;
640 if (need_to_start_children) {
641 need_to_start_children = 0;
642 start_children(first_feed);
645 /* now handle the events */
646 for(c = first_http_ctx; c != NULL; c = c_next) {
648 if (handle_connection(c) < 0) {
649 /* close and free the connection */
655 poll_entry = poll_table;
657 /* new HTTP connection request ? */
658 if (poll_entry->revents & POLLIN)
659 new_connection(server_fd, 0);
662 if (rtsp_server_fd) {
663 /* new RTSP connection request ? */
664 if (poll_entry->revents & POLLIN)
665 new_connection(rtsp_server_fd, 1);
670 /* start waiting for a new HTTP/RTSP request */
671 static void start_wait_request(HTTPContext *c, int is_rtsp)
673 c->buffer_ptr = c->buffer;
674 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
677 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
678 c->state = RTSPSTATE_WAIT_REQUEST;
680 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
681 c->state = HTTPSTATE_WAIT_REQUEST;
685 static void new_connection(int server_fd, int is_rtsp)
687 struct sockaddr_in from_addr;
689 HTTPContext *c = NULL;
691 len = sizeof(from_addr);
692 fd = accept(server_fd, (struct sockaddr *)&from_addr,
695 http_log("error during accept %s\n", strerror(errno));
698 ff_socket_nonblock(fd, 1);
700 /* XXX: should output a warning page when coming
701 close to the connection limit */
702 if (nb_connections >= nb_max_connections)
705 /* add a new connection */
706 c = av_mallocz(sizeof(HTTPContext));
711 c->poll_entry = NULL;
712 c->from_addr = from_addr;
713 c->buffer_size = IOBUFFER_INIT_SIZE;
714 c->buffer = av_malloc(c->buffer_size);
718 c->next = first_http_ctx;
722 start_wait_request(c, is_rtsp);
734 static void close_connection(HTTPContext *c)
736 HTTPContext **cp, *c1;
738 AVFormatContext *ctx;
742 /* remove connection from list */
743 cp = &first_http_ctx;
744 while ((*cp) != NULL) {
752 /* remove references, if any (XXX: do it faster) */
753 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
758 /* remove connection associated resources */
762 /* close each frame parser */
763 for(i=0;i<c->fmt_in->nb_streams;i++) {
764 st = c->fmt_in->streams[i];
765 if (st->codec->codec)
766 avcodec_close(st->codec);
768 av_close_input_file(c->fmt_in);
771 /* free RTP output streams if any */
774 nb_streams = c->stream->nb_streams;
776 for(i=0;i<nb_streams;i++) {
779 av_write_trailer(ctx);
782 h = c->rtp_handles[i];
789 if (!c->last_packet_sent) {
792 if (url_open_dyn_buf(&ctx->pb) >= 0) {
793 av_write_trailer(ctx);
794 av_freep(&c->pb_buffer);
795 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
800 for(i=0; i<ctx->nb_streams; i++)
801 av_free(ctx->streams[i]);
803 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
804 current_bandwidth -= c->stream->bandwidth;
806 /* signal that there is no feed if we are the feeder socket */
807 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
808 c->stream->feed_opened = 0;
812 av_freep(&c->pb_buffer);
813 av_freep(&c->packet_buffer);
819 static int handle_connection(HTTPContext *c)
824 case HTTPSTATE_WAIT_REQUEST:
825 case RTSPSTATE_WAIT_REQUEST:
827 if ((c->timeout - cur_time) < 0)
829 if (c->poll_entry->revents & (POLLERR | POLLHUP))
832 /* no need to read if no events */
833 if (!(c->poll_entry->revents & POLLIN))
837 len = recv(c->fd, c->buffer_ptr, 1, 0);
839 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
840 ff_neterrno() != FF_NETERROR(EINTR))
842 } else if (len == 0) {
845 /* search for end of request. */
847 c->buffer_ptr += len;
849 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
850 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
851 /* request found : parse it and reply */
852 if (c->state == HTTPSTATE_WAIT_REQUEST) {
853 ret = http_parse_request(c);
855 ret = rtsp_parse_request(c);
859 } else if (ptr >= c->buffer_end) {
860 /* request too long: cannot do anything */
862 } else goto read_loop;
866 case HTTPSTATE_SEND_HEADER:
867 if (c->poll_entry->revents & (POLLERR | POLLHUP))
870 /* no need to write if no events */
871 if (!(c->poll_entry->revents & POLLOUT))
873 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
875 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
876 ff_neterrno() != FF_NETERROR(EINTR)) {
877 /* error : close connection */
878 av_freep(&c->pb_buffer);
882 c->buffer_ptr += len;
884 c->stream->bytes_served += len;
885 c->data_count += len;
886 if (c->buffer_ptr >= c->buffer_end) {
887 av_freep(&c->pb_buffer);
891 /* all the buffer was sent : synchronize to the incoming stream */
892 c->state = HTTPSTATE_SEND_DATA_HEADER;
893 c->buffer_ptr = c->buffer_end = c->buffer;
898 case HTTPSTATE_SEND_DATA:
899 case HTTPSTATE_SEND_DATA_HEADER:
900 case HTTPSTATE_SEND_DATA_TRAILER:
901 /* for packetized output, we consider we can always write (the
902 input streams sets the speed). It may be better to verify
903 that we do not rely too much on the kernel queues */
904 if (!c->is_packetized) {
905 if (c->poll_entry->revents & (POLLERR | POLLHUP))
908 /* no need to read if no events */
909 if (!(c->poll_entry->revents & POLLOUT))
912 if (http_send_data(c) < 0)
914 /* close connection if trailer sent */
915 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
918 case HTTPSTATE_RECEIVE_DATA:
919 /* no need to read if no events */
920 if (c->poll_entry->revents & (POLLERR | POLLHUP))
922 if (!(c->poll_entry->revents & POLLIN))
924 if (http_receive_data(c) < 0)
927 case HTTPSTATE_WAIT_FEED:
928 /* no need to read if no events */
929 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
932 /* nothing to do, we'll be waken up by incoming feed packets */
935 case RTSPSTATE_SEND_REPLY:
936 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
937 av_freep(&c->pb_buffer);
940 /* no need to write if no events */
941 if (!(c->poll_entry->revents & POLLOUT))
943 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
945 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
946 ff_neterrno() != FF_NETERROR(EINTR)) {
947 /* error : close connection */
948 av_freep(&c->pb_buffer);
952 c->buffer_ptr += len;
953 c->data_count += len;
954 if (c->buffer_ptr >= c->buffer_end) {
955 /* all the buffer was sent : wait for a new request */
956 av_freep(&c->pb_buffer);
957 start_wait_request(c, 1);
961 case RTSPSTATE_SEND_PACKET:
962 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
963 av_freep(&c->packet_buffer);
966 /* no need to write if no events */
967 if (!(c->poll_entry->revents & POLLOUT))
969 len = send(c->fd, c->packet_buffer_ptr,
970 c->packet_buffer_end - c->packet_buffer_ptr, 0);
972 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
973 ff_neterrno() != FF_NETERROR(EINTR)) {
974 /* error : close connection */
975 av_freep(&c->packet_buffer);
979 c->packet_buffer_ptr += len;
980 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
981 /* all the buffer was sent : wait for a new request */
982 av_freep(&c->packet_buffer);
983 c->state = RTSPSTATE_WAIT_REQUEST;
987 case HTTPSTATE_READY:
996 static int extract_rates(char *rates, int ratelen, const char *request)
1000 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1001 if (strncasecmp(p, "Pragma:", 7) == 0) {
1002 const char *q = p + 7;
1004 while (*q && *q != '\n' && isspace(*q))
1007 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1013 memset(rates, 0xff, ratelen);
1016 while (*q && *q != '\n' && *q != ':')
1019 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1023 if (stream_no < ratelen && stream_no >= 0)
1024 rates[stream_no] = rate_no;
1026 while (*q && *q != '\n' && !isspace(*q))
1033 p = strchr(p, '\n');
1043 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1046 int best_bitrate = 100000000;
1049 for (i = 0; i < feed->nb_streams; i++) {
1050 AVCodecContext *feed_codec = feed->streams[i]->codec;
1052 if (feed_codec->codec_id != codec->codec_id ||
1053 feed_codec->sample_rate != codec->sample_rate ||
1054 feed_codec->width != codec->width ||
1055 feed_codec->height != codec->height)
1058 /* Potential stream */
1060 /* We want the fastest stream less than bit_rate, or the slowest
1061 * faster than bit_rate
1064 if (feed_codec->bit_rate <= bit_rate) {
1065 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1066 best_bitrate = feed_codec->bit_rate;
1070 if (feed_codec->bit_rate < best_bitrate) {
1071 best_bitrate = feed_codec->bit_rate;
1080 static int modify_current_stream(HTTPContext *c, char *rates)
1083 FFStream *req = c->stream;
1084 int action_required = 0;
1086 /* Not much we can do for a feed */
1090 for (i = 0; i < req->nb_streams; i++) {
1091 AVCodecContext *codec = req->streams[i]->codec;
1095 c->switch_feed_streams[i] = req->feed_streams[i];
1098 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1101 /* Wants off or slow */
1102 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1104 /* This doesn't work well when it turns off the only stream! */
1105 c->switch_feed_streams[i] = -2;
1106 c->feed_streams[i] = -2;
1111 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1112 action_required = 1;
1115 return action_required;
1119 static void do_switch_stream(HTTPContext *c, int i)
1121 if (c->switch_feed_streams[i] >= 0) {
1123 c->feed_streams[i] = c->switch_feed_streams[i];
1126 /* Now update the stream */
1128 c->switch_feed_streams[i] = -1;
1131 /* XXX: factorize in utils.c ? */
1132 /* XXX: take care with different space meaning */
1133 static void skip_spaces(const char **pp)
1137 while (*p == ' ' || *p == '\t')
1142 static void get_word(char *buf, int buf_size, const char **pp)
1150 while (!isspace(*p) && *p != '\0') {
1151 if ((q - buf) < buf_size - 1)
1160 static int validate_acl(FFStream *stream, HTTPContext *c)
1162 enum IPAddressAction last_action = IP_DENY;
1164 struct in_addr *src = &c->from_addr.sin_addr;
1165 unsigned long src_addr = src->s_addr;
1167 for (acl = stream->acl; acl; acl = acl->next) {
1168 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1169 return (acl->action == IP_ALLOW) ? 1 : 0;
1170 last_action = acl->action;
1173 /* Nothing matched, so return not the last action */
1174 return (last_action == IP_DENY) ? 1 : 0;
1177 /* compute the real filename of a file by matching it without its
1178 extensions to all the stream filenames */
1179 static void compute_real_filename(char *filename, int max_size)
1186 /* compute filename by matching without the file extensions */
1187 av_strlcpy(file1, filename, sizeof(file1));
1188 p = strrchr(file1, '.');
1191 for(stream = first_stream; stream != NULL; stream = stream->next) {
1192 av_strlcpy(file2, stream->filename, sizeof(file2));
1193 p = strrchr(file2, '.');
1196 if (!strcmp(file1, file2)) {
1197 av_strlcpy(filename, stream->filename, max_size);
1212 /* parse http request and prepare header */
1213 static int http_parse_request(HTTPContext *c)
1216 enum RedirType redir_type;
1218 char info[1024], filename[1024];
1222 const char *mime_type;
1226 char *useragent = 0;
1229 get_word(cmd, sizeof(cmd), (const char **)&p);
1230 av_strlcpy(c->method, cmd, sizeof(c->method));
1232 if (!strcmp(cmd, "GET"))
1234 else if (!strcmp(cmd, "POST"))
1239 get_word(url, sizeof(url), (const char **)&p);
1240 av_strlcpy(c->url, url, sizeof(c->url));
1242 get_word(protocol, sizeof(protocol), (const char **)&p);
1243 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1246 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1249 http_log("New connection: %s %s\n", cmd, url);
1251 /* find the filename and the optional info string in the request */
1252 p = strchr(url, '?');
1254 av_strlcpy(info, p, sizeof(info));
1259 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1261 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1262 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1264 if (*useragent && *useragent != '\n' && isspace(*useragent))
1268 p = strchr(p, '\n');
1275 redir_type = REDIR_NONE;
1276 if (match_ext(filename, "asx")) {
1277 redir_type = REDIR_ASX;
1278 filename[strlen(filename)-1] = 'f';
1279 } else if (match_ext(filename, "asf") &&
1280 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1281 /* if this isn't WMP or lookalike, return the redirector file */
1282 redir_type = REDIR_ASF;
1283 } else if (match_ext(filename, "rpm,ram")) {
1284 redir_type = REDIR_RAM;
1285 strcpy(filename + strlen(filename)-2, "m");
1286 } else if (match_ext(filename, "rtsp")) {
1287 redir_type = REDIR_RTSP;
1288 compute_real_filename(filename, sizeof(filename) - 1);
1289 } else if (match_ext(filename, "sdp")) {
1290 redir_type = REDIR_SDP;
1291 compute_real_filename(filename, sizeof(filename) - 1);
1294 // "redirect" / request to index.html
1295 if (!strlen(filename))
1296 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1298 stream = first_stream;
1299 while (stream != NULL) {
1300 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1302 stream = stream->next;
1304 if (stream == NULL) {
1305 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1310 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1311 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1313 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1314 c->http_error = 301;
1316 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1317 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1318 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1319 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1320 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1321 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1324 /* prepare output buffer */
1325 c->buffer_ptr = c->buffer;
1327 c->state = HTTPSTATE_SEND_HEADER;
1331 /* If this is WMP, get the rate information */
1332 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1333 if (modify_current_stream(c, ratebuf)) {
1334 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1335 if (c->switch_feed_streams[i] >= 0)
1336 do_switch_stream(c, i);
1341 /* If already streaming this feed, do not let start another feeder. */
1342 if (stream->feed_opened) {
1343 snprintf(msg, sizeof(msg), "This feed is already being received.");
1347 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1348 current_bandwidth += stream->bandwidth;
1350 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1351 c->http_error = 200;
1353 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1354 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1355 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1356 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1357 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The server is too busy to serve your request at this time.</p>\r\n");
1358 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The bandwidth being served (including your stream) is %lldkbit/sec, and this exceeds the limit of %lldkbit/sec.</p>\r\n",
1359 current_bandwidth, max_bandwidth);
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1362 /* prepare output buffer */
1363 c->buffer_ptr = c->buffer;
1365 c->state = HTTPSTATE_SEND_HEADER;
1369 if (redir_type != REDIR_NONE) {
1372 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1373 if (strncasecmp(p, "Host:", 5) == 0) {
1377 p = strchr(p, '\n');
1388 while (isspace(*hostinfo))
1391 eoh = strchr(hostinfo, '\n');
1393 if (eoh[-1] == '\r')
1396 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1397 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1398 hostbuf[eoh - hostinfo] = 0;
1400 c->http_error = 200;
1402 switch(redir_type) {
1404 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1405 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1408 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1410 hostbuf, filename, info);
1411 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1414 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1415 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1416 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1417 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1418 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1419 hostbuf, filename, info);
1422 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1425 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1426 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1427 hostbuf, filename, info);
1431 char hostname[256], *p;
1432 /* extract only hostname */
1433 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1434 p = strrchr(hostname, ':');
1437 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1438 /* XXX: incorrect mime type ? */
1439 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1440 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1441 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1442 hostname, ntohs(my_rtsp_addr.sin_port),
1449 int sdp_data_size, len;
1450 struct sockaddr_in my_addr;
1452 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1453 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1454 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1456 len = sizeof(my_addr);
1457 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1459 /* XXX: should use a dynamic buffer */
1460 sdp_data_size = prepare_sdp_description(stream,
1463 if (sdp_data_size > 0) {
1464 memcpy(q, sdp_data, sdp_data_size);
1476 /* prepare output buffer */
1477 c->buffer_ptr = c->buffer;
1479 c->state = HTTPSTATE_SEND_HEADER;
1485 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1489 stream->conns_served++;
1491 /* XXX: add there authenticate and IP match */
1494 /* if post, it means a feed is being sent */
1495 if (!stream->is_feed) {
1496 /* However it might be a status report from WMP! Lets log the data
1497 * as it might come in handy one day
1502 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1503 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1507 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1508 client_id = strtol(p + 18, 0, 10);
1509 p = strchr(p, '\n');
1517 char *eol = strchr(logline, '\n');
1522 if (eol[-1] == '\r')
1524 http_log("%.*s\n", (int) (eol - logline), logline);
1525 c->suppress_log = 1;
1530 http_log("\nGot request:\n%s\n", c->buffer);
1533 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1536 /* Now we have to find the client_id */
1537 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1538 if (wmpc->wmp_client_id == client_id)
1542 if (wmpc && modify_current_stream(wmpc, ratebuf))
1543 wmpc->switch_pending = 1;
1546 snprintf(msg, sizeof(msg), "POST command not handled");
1550 if (http_start_receive_data(c) < 0) {
1551 snprintf(msg, sizeof(msg), "could not open feed");
1555 c->state = HTTPSTATE_RECEIVE_DATA;
1560 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1561 http_log("\nGot request:\n%s\n", c->buffer);
1564 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1567 /* open input stream */
1568 if (open_input_stream(c, info) < 0) {
1569 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1573 /* prepare http header */
1575 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1576 mime_type = c->stream->fmt->mime_type;
1578 mime_type = "application/x-octet-stream";
1579 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1581 /* for asf, we need extra headers */
1582 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1583 /* Need to allocate a client id */
1585 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1587 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);
1589 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1590 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1592 /* prepare output buffer */
1594 c->buffer_ptr = c->buffer;
1596 c->state = HTTPSTATE_SEND_HEADER;
1599 c->http_error = 404;
1601 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1602 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1603 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1604 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1605 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1606 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1607 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1609 /* prepare output buffer */
1610 c->buffer_ptr = c->buffer;
1612 c->state = HTTPSTATE_SEND_HEADER;
1616 c->http_error = 200; /* horrible : we use this value to avoid
1617 going to the send data state */
1618 c->state = HTTPSTATE_SEND_HEADER;
1622 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1624 static const char *suffix = " kMGTP";
1627 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1629 url_fprintf(pb, "%"PRId64"%c", count, *s);
1632 static void compute_status(HTTPContext *c)
1641 if (url_open_dyn_buf(&pb) < 0) {
1642 /* XXX: return an error ? */
1643 c->buffer_ptr = c->buffer;
1644 c->buffer_end = c->buffer;
1648 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1649 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1650 url_fprintf(pb, "Pragma: no-cache\r\n");
1651 url_fprintf(pb, "\r\n");
1653 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1654 if (c->stream->feed_filename[0])
1655 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1656 url_fprintf(pb, "</HEAD>\n<BODY>");
1657 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1659 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1660 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1661 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");
1662 stream = first_stream;
1663 while (stream != NULL) {
1664 char sfilename[1024];
1667 if (stream->feed != stream) {
1668 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1669 eosf = sfilename + strlen(sfilename);
1670 if (eosf - sfilename >= 4) {
1671 if (strcmp(eosf - 4, ".asf") == 0)
1672 strcpy(eosf - 4, ".asx");
1673 else if (strcmp(eosf - 3, ".rm") == 0)
1674 strcpy(eosf - 3, ".ram");
1675 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1676 /* generate a sample RTSP director if
1677 unicast. Generate an SDP redirector if
1679 eosf = strrchr(sfilename, '.');
1681 eosf = sfilename + strlen(sfilename);
1682 if (stream->is_multicast)
1683 strcpy(eosf, ".sdp");
1685 strcpy(eosf, ".rtsp");
1689 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1690 sfilename, stream->filename);
1691 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1692 stream->conns_served);
1693 fmt_bytecount(pb, stream->bytes_served);
1694 switch(stream->stream_type) {
1695 case STREAM_TYPE_LIVE: {
1696 int audio_bit_rate = 0;
1697 int video_bit_rate = 0;
1698 const char *audio_codec_name = "";
1699 const char *video_codec_name = "";
1700 const char *audio_codec_name_extra = "";
1701 const char *video_codec_name_extra = "";
1703 for(i=0;i<stream->nb_streams;i++) {
1704 AVStream *st = stream->streams[i];
1705 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1706 switch(st->codec->codec_type) {
1707 case CODEC_TYPE_AUDIO:
1708 audio_bit_rate += st->codec->bit_rate;
1710 if (*audio_codec_name)
1711 audio_codec_name_extra = "...";
1712 audio_codec_name = codec->name;
1715 case CODEC_TYPE_VIDEO:
1716 video_bit_rate += st->codec->bit_rate;
1718 if (*video_codec_name)
1719 video_codec_name_extra = "...";
1720 video_codec_name = codec->name;
1723 case CODEC_TYPE_DATA:
1724 video_bit_rate += st->codec->bit_rate;
1730 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",
1733 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1734 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1736 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1738 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1739 url_fprintf(pb, "\n");
1743 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1747 stream = stream->next;
1749 url_fprintf(pb, "</TABLE>\n");
1751 stream = first_stream;
1752 while (stream != NULL) {
1753 if (stream->feed == stream) {
1754 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1756 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1758 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1763 /* This is somewhat linux specific I guess */
1764 snprintf(ps_cmd, sizeof(ps_cmd),
1765 "ps -o \"%%cpu,cputime\" --no-headers %d",
1768 pid_stat = popen(ps_cmd, "r");
1773 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1775 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1783 url_fprintf(pb, "<p>");
1785 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");
1787 for (i = 0; i < stream->nb_streams; i++) {
1788 AVStream *st = stream->streams[i];
1789 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1790 const char *type = "unknown";
1791 char parameters[64];
1795 switch(st->codec->codec_type) {
1796 case CODEC_TYPE_AUDIO:
1798 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1800 case CODEC_TYPE_VIDEO:
1802 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1803 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1808 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1809 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1811 url_fprintf(pb, "</table>\n");
1814 stream = stream->next;
1820 AVCodecContext *enc;
1824 stream = first_feed;
1825 while (stream != NULL) {
1826 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1827 url_fprintf(pb, "<TABLE>\n");
1828 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1829 for(i=0;i<stream->nb_streams;i++) {
1830 AVStream *st = stream->streams[i];
1831 FeedData *fdata = st->priv_data;
1834 avcodec_string(buf, sizeof(buf), enc);
1835 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1836 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1837 avg /= enc->frame_size;
1838 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1839 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1841 url_fprintf(pb, "</TABLE>\n");
1842 stream = stream->next_feed;
1847 /* connection status */
1848 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1850 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1851 nb_connections, nb_max_connections);
1853 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1854 current_bandwidth, max_bandwidth);
1856 url_fprintf(pb, "<TABLE>\n");
1857 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");
1858 c1 = first_http_ctx;
1860 while (c1 != NULL) {
1866 for (j = 0; j < c1->stream->nb_streams; j++) {
1867 if (!c1->stream->feed)
1868 bitrate += c1->stream->streams[j]->codec->bit_rate;
1869 else if (c1->feed_streams[j] >= 0)
1870 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1875 p = inet_ntoa(c1->from_addr.sin_addr);
1876 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1878 c1->stream ? c1->stream->filename : "",
1879 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1882 http_state[c1->state]);
1883 fmt_bytecount(pb, bitrate);
1884 url_fprintf(pb, "<td align=right>");
1885 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1886 url_fprintf(pb, "<td align=right>");
1887 fmt_bytecount(pb, c1->data_count);
1888 url_fprintf(pb, "\n");
1891 url_fprintf(pb, "</TABLE>\n");
1896 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1897 url_fprintf(pb, "</BODY>\n</HTML>\n");
1899 len = url_close_dyn_buf(pb, &c->pb_buffer);
1900 c->buffer_ptr = c->pb_buffer;
1901 c->buffer_end = c->pb_buffer + len;
1904 /* check if the parser needs to be opened for stream i */
1905 static void open_parser(AVFormatContext *s, int i)
1907 AVStream *st = s->streams[i];
1910 if (!st->codec->codec) {
1911 codec = avcodec_find_decoder(st->codec->codec_id);
1912 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1913 st->codec->parse_only = 1;
1914 if (avcodec_open(st->codec, codec) < 0)
1915 st->codec->parse_only = 0;
1920 static int open_input_stream(HTTPContext *c, const char *info)
1923 char input_filename[1024];
1925 int buf_size, i, ret;
1928 /* find file name */
1929 if (c->stream->feed) {
1930 strcpy(input_filename, c->stream->feed->feed_filename);
1931 buf_size = FFM_PACKET_SIZE;
1932 /* compute position (absolute time) */
1933 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1934 stream_pos = parse_date(buf, 0);
1935 if (stream_pos == INT64_MIN)
1937 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1938 int prebuffer = strtol(buf, 0, 10);
1939 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1941 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1943 strcpy(input_filename, c->stream->feed_filename);
1945 /* compute position (relative time) */
1946 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1947 stream_pos = parse_date(buf, 1);
1948 if (stream_pos == INT64_MIN)
1953 if (input_filename[0] == '\0')
1957 { time_t when = stream_pos / 1000000;
1958 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1963 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1964 buf_size, c->stream->ap_in)) < 0) {
1965 http_log("could not open %s: %d\n", input_filename, ret);
1968 s->flags |= AVFMT_FLAG_GENPTS;
1970 av_find_stream_info(c->fmt_in);
1972 /* open each parser */
1973 for(i=0;i<s->nb_streams;i++)
1976 /* choose stream as clock source (we favorize video stream if
1977 present) for packet sending */
1978 c->pts_stream_index = 0;
1979 for(i=0;i<c->stream->nb_streams;i++) {
1980 if (c->pts_stream_index == 0 &&
1981 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1982 c->pts_stream_index = i;
1987 if (c->fmt_in->iformat->read_seek)
1988 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1990 /* set the start time (needed for maxtime and RTP packet timing) */
1991 c->start_time = cur_time;
1992 c->first_pts = AV_NOPTS_VALUE;
1996 /* return the server clock (in us) */
1997 static int64_t get_server_clock(HTTPContext *c)
1999 /* compute current pts value from system time */
2000 return (cur_time - c->start_time) * 1000;
2003 /* return the estimated time at which the current packet must be sent
2005 static int64_t get_packet_send_clock(HTTPContext *c)
2007 int bytes_left, bytes_sent, frame_bytes;
2009 frame_bytes = c->cur_frame_bytes;
2010 if (frame_bytes <= 0)
2013 bytes_left = c->buffer_end - c->buffer_ptr;
2014 bytes_sent = frame_bytes - bytes_left;
2015 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2020 static int http_prepare_data(HTTPContext *c)
2023 AVFormatContext *ctx;
2025 av_freep(&c->pb_buffer);
2027 case HTTPSTATE_SEND_DATA_HEADER:
2028 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2029 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2030 sizeof(c->fmt_ctx.author));
2031 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2032 sizeof(c->fmt_ctx.comment));
2033 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2034 sizeof(c->fmt_ctx.copyright));
2035 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2036 sizeof(c->fmt_ctx.title));
2038 for(i=0;i<c->stream->nb_streams;i++) {
2041 st = av_mallocz(sizeof(AVStream));
2042 c->fmt_ctx.streams[i] = st;
2043 /* if file or feed, then just take streams from FFStream struct */
2044 if (!c->stream->feed ||
2045 c->stream->feed == c->stream)
2046 src = c->stream->streams[i];
2048 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2052 st->codec->frame_number = 0; /* XXX: should be done in
2053 AVStream, not in codec */
2055 /* set output format parameters */
2056 c->fmt_ctx.oformat = c->stream->fmt;
2057 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2059 c->got_key_frame = 0;
2061 /* prepare header and save header data in a stream */
2062 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2063 /* XXX: potential leak */
2066 c->fmt_ctx.pb->is_streamed = 1;
2069 * HACK to avoid mpeg ps muxer to spit many underflow errors
2070 * Default value from FFmpeg
2071 * Try to set it use configuration option
2073 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2074 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2076 av_set_parameters(&c->fmt_ctx, NULL);
2077 if (av_write_header(&c->fmt_ctx) < 0) {
2078 http_log("Error writing output header\n");
2082 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2083 c->buffer_ptr = c->pb_buffer;
2084 c->buffer_end = c->pb_buffer + len;
2086 c->state = HTTPSTATE_SEND_DATA;
2087 c->last_packet_sent = 0;
2089 case HTTPSTATE_SEND_DATA:
2090 /* find a new packet */
2091 /* read a packet from the input stream */
2092 if (c->stream->feed)
2093 ffm_set_write_index(c->fmt_in,
2094 c->stream->feed->feed_write_index,
2095 c->stream->feed->feed_size);
2097 if (c->stream->max_time &&
2098 c->stream->max_time + c->start_time - cur_time < 0)
2099 /* We have timed out */
2100 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2104 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2105 if (c->stream->feed && c->stream->feed->feed_opened) {
2106 /* if coming from feed, it means we reached the end of the
2107 ffm file, so must wait for more data */
2108 c->state = HTTPSTATE_WAIT_FEED;
2109 return 1; /* state changed */
2111 if (c->stream->loop) {
2112 av_close_input_file(c->fmt_in);
2114 if (open_input_stream(c, "") < 0)
2119 /* must send trailer now because eof or error */
2120 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2124 int source_index = pkt.stream_index;
2125 /* update first pts if needed */
2126 if (c->first_pts == AV_NOPTS_VALUE) {
2127 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2128 c->start_time = cur_time;
2130 /* send it to the appropriate stream */
2131 if (c->stream->feed) {
2132 /* if coming from a feed, select the right stream */
2133 if (c->switch_pending) {
2134 c->switch_pending = 0;
2135 for(i=0;i<c->stream->nb_streams;i++) {
2136 if (c->switch_feed_streams[i] == pkt.stream_index)
2137 if (pkt.flags & PKT_FLAG_KEY)
2138 do_switch_stream(c, i);
2139 if (c->switch_feed_streams[i] >= 0)
2140 c->switch_pending = 1;
2143 for(i=0;i<c->stream->nb_streams;i++) {
2144 if (c->feed_streams[i] == pkt.stream_index) {
2145 AVStream *st = c->fmt_in->streams[source_index];
2146 pkt.stream_index = i;
2147 if (pkt.flags & PKT_FLAG_KEY &&
2148 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2149 c->stream->nb_streams == 1))
2150 c->got_key_frame = 1;
2151 if (!c->stream->send_on_key || c->got_key_frame)
2156 AVCodecContext *codec;
2157 AVStream *ist, *ost;
2159 ist = c->fmt_in->streams[source_index];
2160 /* specific handling for RTP: we use several
2161 output stream (one for each RTP
2162 connection). XXX: need more abstract handling */
2163 if (c->is_packetized) {
2164 /* compute send time and duration */
2165 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2166 if (ist->start_time != AV_NOPTS_VALUE)
2167 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2168 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2170 printf("index=%d pts=%0.3f duration=%0.6f\n",
2172 (double)c->cur_pts /
2174 (double)c->cur_frame_duration /
2177 /* find RTP context */
2178 c->packet_stream_index = pkt.stream_index;
2179 ctx = c->rtp_ctx[c->packet_stream_index];
2181 av_free_packet(&pkt);
2184 codec = ctx->streams[0]->codec;
2185 /* only one stream per RTP connection */
2186 pkt.stream_index = 0;
2190 codec = ctx->streams[pkt.stream_index]->codec;
2193 if (c->is_packetized) {
2194 int max_packet_size;
2195 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2196 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2198 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2199 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2201 ret = url_open_dyn_buf(&ctx->pb);
2204 /* XXX: potential leak */
2207 ost = ctx->streams[pkt.stream_index];
2209 ctx->pb->is_streamed = 1;
2210 if (pkt.dts != AV_NOPTS_VALUE)
2211 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2212 if (pkt.pts != AV_NOPTS_VALUE)
2213 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2214 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2215 if (av_write_frame(ctx, &pkt) < 0) {
2216 http_log("Error writing frame to output\n");
2217 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2220 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2221 c->cur_frame_bytes = len;
2222 c->buffer_ptr = c->pb_buffer;
2223 c->buffer_end = c->pb_buffer + len;
2225 codec->frame_number++;
2227 av_free_packet(&pkt);
2231 av_free_packet(&pkt);
2236 case HTTPSTATE_SEND_DATA_TRAILER:
2237 /* last packet test ? */
2238 if (c->last_packet_sent || c->is_packetized)
2241 /* prepare header */
2242 if (url_open_dyn_buf(&ctx->pb) < 0) {
2243 /* XXX: potential leak */
2246 c->fmt_ctx.pb->is_streamed = 1;
2247 av_write_trailer(ctx);
2248 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2249 c->buffer_ptr = c->pb_buffer;
2250 c->buffer_end = c->pb_buffer + len;
2252 c->last_packet_sent = 1;
2258 /* should convert the format at the same time */
2259 /* send data starting at c->buffer_ptr to the output connection
2260 (either UDP or TCP connection) */
2261 static int http_send_data(HTTPContext *c)
2266 if (c->buffer_ptr >= c->buffer_end) {
2267 ret = http_prepare_data(c);
2271 /* state change requested */
2274 if (c->is_packetized) {
2275 /* RTP data output */
2276 len = c->buffer_end - c->buffer_ptr;
2278 /* fail safe - should never happen */
2280 c->buffer_ptr = c->buffer_end;
2283 len = (c->buffer_ptr[0] << 24) |
2284 (c->buffer_ptr[1] << 16) |
2285 (c->buffer_ptr[2] << 8) |
2287 if (len > (c->buffer_end - c->buffer_ptr))
2289 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2290 /* nothing to send yet: we can wait */
2294 c->data_count += len;
2295 update_datarate(&c->datarate, c->data_count);
2297 c->stream->bytes_served += len;
2299 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2300 /* RTP packets are sent inside the RTSP TCP connection */
2302 int interleaved_index, size;
2304 HTTPContext *rtsp_c;
2307 /* if no RTSP connection left, error */
2310 /* if already sending something, then wait. */
2311 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2313 if (url_open_dyn_buf(&pb) < 0)
2315 interleaved_index = c->packet_stream_index * 2;
2316 /* RTCP packets are sent at odd indexes */
2317 if (c->buffer_ptr[1] == 200)
2318 interleaved_index++;
2319 /* write RTSP TCP header */
2321 header[1] = interleaved_index;
2322 header[2] = len >> 8;
2324 put_buffer(pb, header, 4);
2325 /* write RTP packet data */
2327 put_buffer(pb, c->buffer_ptr, len);
2328 size = url_close_dyn_buf(pb, &c->packet_buffer);
2329 /* prepare asynchronous TCP sending */
2330 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2331 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2332 c->buffer_ptr += len;
2334 /* send everything we can NOW */
2335 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2336 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2338 rtsp_c->packet_buffer_ptr += len;
2339 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2340 /* if we could not send all the data, we will
2341 send it later, so a new state is needed to
2342 "lock" the RTSP TCP connection */
2343 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2346 /* all data has been sent */
2347 av_freep(&c->packet_buffer);
2349 /* send RTP packet directly in UDP */
2351 url_write(c->rtp_handles[c->packet_stream_index],
2352 c->buffer_ptr, len);
2353 c->buffer_ptr += len;
2354 /* here we continue as we can send several packets per 10 ms slot */
2357 /* TCP data output */
2358 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2360 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2361 ff_neterrno() != FF_NETERROR(EINTR))
2362 /* error : close connection */
2367 c->buffer_ptr += len;
2369 c->data_count += len;
2370 update_datarate(&c->datarate, c->data_count);
2372 c->stream->bytes_served += len;
2380 static int http_start_receive_data(HTTPContext *c)
2384 if (c->stream->feed_opened)
2387 /* Don't permit writing to this one */
2388 if (c->stream->readonly)
2392 fd = open(c->stream->feed_filename, O_RDWR);
2394 http_log("Error opening feeder file: %s\n", strerror(errno));
2399 c->stream->feed_write_index = ffm_read_write_index(fd);
2400 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2401 lseek(fd, 0, SEEK_SET);
2403 /* init buffer input */
2404 c->buffer_ptr = c->buffer;
2405 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2406 c->stream->feed_opened = 1;
2410 static int http_receive_data(HTTPContext *c)
2414 if (c->buffer_end > c->buffer_ptr) {
2417 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2419 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2420 ff_neterrno() != FF_NETERROR(EINTR))
2421 /* error : close connection */
2423 } else if (len == 0)
2424 /* end of connection : close it */
2427 c->buffer_ptr += len;
2428 c->data_count += len;
2429 update_datarate(&c->datarate, c->data_count);
2433 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2434 if (c->buffer[0] != 'f' ||
2435 c->buffer[1] != 'm') {
2436 http_log("Feed stream has become desynchronized -- disconnecting\n");
2441 if (c->buffer_ptr >= c->buffer_end) {
2442 FFStream *feed = c->stream;
2443 /* a packet has been received : write it in the store, except
2445 if (c->data_count > FFM_PACKET_SIZE) {
2447 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2448 /* XXX: use llseek or url_seek */
2449 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2450 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2451 http_log("Error writing to feed file: %s\n", strerror(errno));
2455 feed->feed_write_index += FFM_PACKET_SIZE;
2456 /* update file size */
2457 if (feed->feed_write_index > c->stream->feed_size)
2458 feed->feed_size = feed->feed_write_index;
2460 /* handle wrap around if max file size reached */
2461 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2462 feed->feed_write_index = FFM_PACKET_SIZE;
2465 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2467 /* wake up any waiting connections */
2468 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2469 if (c1->state == HTTPSTATE_WAIT_FEED &&
2470 c1->stream->feed == c->stream->feed)
2471 c1->state = HTTPSTATE_SEND_DATA;
2474 /* We have a header in our hands that contains useful data */
2475 AVFormatContext *s = NULL;
2477 AVInputFormat *fmt_in;
2480 /* use feed output format name to find corresponding input format */
2481 fmt_in = av_find_input_format(feed->fmt->name);
2485 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2486 pb->is_streamed = 1;
2488 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2493 /* Now we have the actual streams */
2494 if (s->nb_streams != feed->nb_streams) {
2495 av_close_input_stream(s);
2500 for (i = 0; i < s->nb_streams; i++)
2501 memcpy(feed->streams[i]->codec,
2502 s->streams[i]->codec, sizeof(AVCodecContext));
2504 av_close_input_stream(s);
2507 c->buffer_ptr = c->buffer;
2512 c->stream->feed_opened = 0;
2514 /* wake up any waiting connections to stop waiting for feed */
2515 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2516 if (c1->state == HTTPSTATE_WAIT_FEED &&
2517 c1->stream->feed == c->stream->feed)
2518 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2523 /********************************************************************/
2526 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2533 switch(error_number) {
2534 case RTSP_STATUS_OK:
2537 case RTSP_STATUS_METHOD:
2538 str = "Method Not Allowed";
2540 case RTSP_STATUS_BANDWIDTH:
2541 str = "Not Enough Bandwidth";
2543 case RTSP_STATUS_SESSION:
2544 str = "Session Not Found";
2546 case RTSP_STATUS_STATE:
2547 str = "Method Not Valid in This State";
2549 case RTSP_STATUS_AGGREGATE:
2550 str = "Aggregate operation not allowed";
2552 case RTSP_STATUS_ONLY_AGGREGATE:
2553 str = "Only aggregate operation allowed";
2555 case RTSP_STATUS_TRANSPORT:
2556 str = "Unsupported transport";
2558 case RTSP_STATUS_INTERNAL:
2559 str = "Internal Server Error";
2561 case RTSP_STATUS_SERVICE:
2562 str = "Service Unavailable";
2564 case RTSP_STATUS_VERSION:
2565 str = "RTSP Version not supported";
2568 str = "Unknown Error";
2572 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2573 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2575 /* output GMT time */
2579 p = buf2 + strlen(p) - 1;
2582 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2585 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2587 rtsp_reply_header(c, error_number);
2588 url_fprintf(c->pb, "\r\n");
2591 static int rtsp_parse_request(HTTPContext *c)
2593 const char *p, *p1, *p2;
2599 RTSPHeader header1, *header = &header1;
2601 c->buffer_ptr[0] = '\0';
2604 get_word(cmd, sizeof(cmd), &p);
2605 get_word(url, sizeof(url), &p);
2606 get_word(protocol, sizeof(protocol), &p);
2608 av_strlcpy(c->method, cmd, sizeof(c->method));
2609 av_strlcpy(c->url, url, sizeof(c->url));
2610 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2612 if (url_open_dyn_buf(&c->pb) < 0) {
2613 /* XXX: cannot do more */
2614 c->pb = NULL; /* safety */
2618 /* check version name */
2619 if (strcmp(protocol, "RTSP/1.0") != 0) {
2620 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2624 /* parse each header line */
2625 memset(header, 0, sizeof(RTSPHeader));
2626 /* skip to next line */
2627 while (*p != '\n' && *p != '\0')
2631 while (*p != '\0') {
2632 p1 = strchr(p, '\n');
2636 if (p2 > p && p2[-1] == '\r')
2638 /* skip empty line */
2642 if (len > sizeof(line) - 1)
2643 len = sizeof(line) - 1;
2644 memcpy(line, p, len);
2646 rtsp_parse_line(header, line);
2650 /* handle sequence number */
2651 c->seq = header->seq;
2653 if (!strcmp(cmd, "DESCRIBE"))
2654 rtsp_cmd_describe(c, url);
2655 else if (!strcmp(cmd, "OPTIONS"))
2656 rtsp_cmd_options(c, url);
2657 else if (!strcmp(cmd, "SETUP"))
2658 rtsp_cmd_setup(c, url, header);
2659 else if (!strcmp(cmd, "PLAY"))
2660 rtsp_cmd_play(c, url, header);
2661 else if (!strcmp(cmd, "PAUSE"))
2662 rtsp_cmd_pause(c, url, header);
2663 else if (!strcmp(cmd, "TEARDOWN"))
2664 rtsp_cmd_teardown(c, url, header);
2666 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2669 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2670 c->pb = NULL; /* safety */
2672 /* XXX: cannot do more */
2675 c->buffer_ptr = c->pb_buffer;
2676 c->buffer_end = c->pb_buffer + len;
2677 c->state = RTSPSTATE_SEND_REPLY;
2681 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2682 struct in_addr my_ip)
2684 AVFormatContext *avc;
2685 AVStream avs[MAX_STREAMS];
2688 avc = av_alloc_format_context();
2692 if (stream->title[0] != 0) {
2693 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2695 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2697 avc->nb_streams = stream->nb_streams;
2698 if (stream->is_multicast) {
2699 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2700 inet_ntoa(stream->multicast_ip),
2701 stream->multicast_port, stream->multicast_ttl);
2704 for(i = 0; i < stream->nb_streams; i++) {
2705 avc->streams[i] = &avs[i];
2706 avc->streams[i]->codec = stream->streams[i]->codec;
2708 *pbuffer = av_mallocz(2048);
2709 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2712 return strlen(*pbuffer);
2715 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2717 // rtsp_reply_header(c, RTSP_STATUS_OK);
2718 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2719 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2720 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2721 url_fprintf(c->pb, "\r\n");
2724 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2730 int content_length, len;
2731 struct sockaddr_in my_addr;
2733 /* find which url is asked */
2734 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2739 for(stream = first_stream; stream != NULL; stream = stream->next) {
2740 if (!stream->is_feed &&
2741 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2742 !strcmp(path, stream->filename)) {
2746 /* no stream found */
2747 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2751 /* prepare the media description in sdp format */
2753 /* get the host IP */
2754 len = sizeof(my_addr);
2755 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2756 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2757 if (content_length < 0) {
2758 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2761 rtsp_reply_header(c, RTSP_STATUS_OK);
2762 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2763 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2764 url_fprintf(c->pb, "\r\n");
2765 put_buffer(c->pb, content, content_length);
2768 static HTTPContext *find_rtp_session(const char *session_id)
2772 if (session_id[0] == '\0')
2775 for(c = first_http_ctx; c != NULL; c = c->next) {
2776 if (!strcmp(c->session_id, session_id))
2782 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2784 RTSPTransportField *th;
2787 for(i=0;i<h->nb_transports;i++) {
2788 th = &h->transports[i];
2789 if (th->protocol == protocol)
2795 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2799 int stream_index, port;
2804 RTSPTransportField *th;
2805 struct sockaddr_in dest_addr;
2806 RTSPActionServerSetup setup;
2808 /* find which url is asked */
2809 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2814 /* now check each stream */
2815 for(stream = first_stream; stream != NULL; stream = stream->next) {
2816 if (!stream->is_feed &&
2817 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2818 /* accept aggregate filenames only if single stream */
2819 if (!strcmp(path, stream->filename)) {
2820 if (stream->nb_streams != 1) {
2821 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2828 for(stream_index = 0; stream_index < stream->nb_streams;
2830 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2831 stream->filename, stream_index);
2832 if (!strcmp(path, buf))
2837 /* no stream found */
2838 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2842 /* generate session id if needed */
2843 if (h->session_id[0] == '\0')
2844 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2845 av_random(&random_state), av_random(&random_state));
2847 /* find rtp session, and create it if none found */
2848 rtp_c = find_rtp_session(h->session_id);
2850 /* always prefer UDP */
2851 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2853 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2855 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2860 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2863 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2867 /* open input stream */
2868 if (open_input_stream(rtp_c, "") < 0) {
2869 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2874 /* test if stream is OK (test needed because several SETUP needs
2875 to be done for a given file) */
2876 if (rtp_c->stream != stream) {
2877 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2881 /* test if stream is already set up */
2882 if (rtp_c->rtp_ctx[stream_index]) {
2883 rtsp_reply_error(c, RTSP_STATUS_STATE);
2887 /* check transport */
2888 th = find_transport(h, rtp_c->rtp_protocol);
2889 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2890 th->client_port_min <= 0)) {
2891 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2895 /* setup default options */
2896 setup.transport_option[0] = '\0';
2897 dest_addr = rtp_c->from_addr;
2898 dest_addr.sin_port = htons(th->client_port_min);
2901 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2902 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2906 /* now everything is OK, so we can send the connection parameters */
2907 rtsp_reply_header(c, RTSP_STATUS_OK);
2909 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2911 switch(rtp_c->rtp_protocol) {
2912 case RTSP_PROTOCOL_RTP_UDP:
2913 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2914 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2915 "client_port=%d-%d;server_port=%d-%d",
2916 th->client_port_min, th->client_port_min + 1,
2919 case RTSP_PROTOCOL_RTP_TCP:
2920 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2921 stream_index * 2, stream_index * 2 + 1);
2926 if (setup.transport_option[0] != '\0')
2927 url_fprintf(c->pb, ";%s", setup.transport_option);
2928 url_fprintf(c->pb, "\r\n");
2931 url_fprintf(c->pb, "\r\n");
2935 /* find an rtp connection by using the session ID. Check consistency
2937 static HTTPContext *find_rtp_session_with_url(const char *url,
2938 const char *session_id)
2946 rtp_c = find_rtp_session(session_id);
2950 /* find which url is asked */
2951 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2955 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2956 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2957 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2958 rtp_c->stream->filename, s);
2959 if(!strncmp(path, buf, sizeof(buf))) {
2960 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2967 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2971 rtp_c = find_rtp_session_with_url(url, h->session_id);
2973 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2977 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2978 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2979 rtp_c->state != HTTPSTATE_READY) {
2980 rtsp_reply_error(c, RTSP_STATUS_STATE);
2985 /* XXX: seek in stream */
2986 if (h->range_start != AV_NOPTS_VALUE) {
2987 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2988 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2992 rtp_c->state = HTTPSTATE_SEND_DATA;
2994 /* now everything is OK, so we can send the connection parameters */
2995 rtsp_reply_header(c, RTSP_STATUS_OK);
2997 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2998 url_fprintf(c->pb, "\r\n");
3001 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3005 rtp_c = find_rtp_session_with_url(url, h->session_id);
3007 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3011 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3012 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3013 rtsp_reply_error(c, RTSP_STATUS_STATE);
3017 rtp_c->state = HTTPSTATE_READY;
3018 rtp_c->first_pts = AV_NOPTS_VALUE;
3019 /* now everything is OK, so we can send the connection parameters */
3020 rtsp_reply_header(c, RTSP_STATUS_OK);
3022 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3023 url_fprintf(c->pb, "\r\n");
3026 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3029 char session_id[32];
3031 rtp_c = find_rtp_session_with_url(url, h->session_id);
3033 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3037 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3039 /* abort the session */
3040 close_connection(rtp_c);
3042 /* now everything is OK, so we can send the connection parameters */
3043 rtsp_reply_header(c, RTSP_STATUS_OK);
3045 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3046 url_fprintf(c->pb, "\r\n");
3050 /********************************************************************/
3053 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3054 FFStream *stream, const char *session_id,
3055 enum RTSPProtocol rtp_protocol)
3057 HTTPContext *c = NULL;
3058 const char *proto_str;
3060 /* XXX: should output a warning page when coming
3061 close to the connection limit */
3062 if (nb_connections >= nb_max_connections)
3065 /* add a new connection */
3066 c = av_mallocz(sizeof(HTTPContext));
3071 c->poll_entry = NULL;
3072 c->from_addr = *from_addr;
3073 c->buffer_size = IOBUFFER_INIT_SIZE;
3074 c->buffer = av_malloc(c->buffer_size);
3079 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3080 c->state = HTTPSTATE_READY;
3081 c->is_packetized = 1;
3082 c->rtp_protocol = rtp_protocol;
3084 /* protocol is shown in statistics */
3085 switch(c->rtp_protocol) {
3086 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3087 proto_str = "MCAST";
3089 case RTSP_PROTOCOL_RTP_UDP:
3092 case RTSP_PROTOCOL_RTP_TCP:
3099 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3100 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3102 current_bandwidth += stream->bandwidth;
3104 c->next = first_http_ctx;
3116 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3117 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3119 static int rtp_new_av_stream(HTTPContext *c,
3120 int stream_index, struct sockaddr_in *dest_addr,
3121 HTTPContext *rtsp_c)
3123 AVFormatContext *ctx;
3126 URLContext *h = NULL;
3128 int max_packet_size;
3130 /* now we can open the relevant output stream */
3131 ctx = av_alloc_format_context();
3134 ctx->oformat = guess_format("rtp", NULL, NULL);
3136 st = av_mallocz(sizeof(AVStream));
3139 st->codec= avcodec_alloc_context();
3140 ctx->nb_streams = 1;
3141 ctx->streams[0] = st;
3143 if (!c->stream->feed ||
3144 c->stream->feed == c->stream)
3145 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3148 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3150 st->priv_data = NULL;
3152 /* build destination RTP address */
3153 ipaddr = inet_ntoa(dest_addr->sin_addr);
3155 switch(c->rtp_protocol) {
3156 case RTSP_PROTOCOL_RTP_UDP:
3157 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3160 /* XXX: also pass as parameter to function ? */
3161 if (c->stream->is_multicast) {
3163 ttl = c->stream->multicast_ttl;
3166 snprintf(ctx->filename, sizeof(ctx->filename),
3167 "rtp://%s:%d?multicast=1&ttl=%d",
3168 ipaddr, ntohs(dest_addr->sin_port), ttl);
3170 snprintf(ctx->filename, sizeof(ctx->filename),
3171 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3174 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3176 c->rtp_handles[stream_index] = h;
3177 max_packet_size = url_get_max_packet_size(h);
3179 case RTSP_PROTOCOL_RTP_TCP:
3182 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3188 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3189 ipaddr, ntohs(dest_addr->sin_port),
3190 c->stream->filename, stream_index, c->protocol);
3192 /* normally, no packets should be output here, but the packet size may be checked */
3193 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3194 /* XXX: close stream */
3197 av_set_parameters(ctx, NULL);
3198 if (av_write_header(ctx) < 0) {
3205 url_close_dyn_buf(ctx->pb, &dummy_buf);
3208 c->rtp_ctx[stream_index] = ctx;
3212 /********************************************************************/
3213 /* ffserver initialization */
3215 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3219 fst = av_mallocz(sizeof(AVStream));
3222 fst->codec= avcodec_alloc_context();
3223 fst->priv_data = av_mallocz(sizeof(FeedData));
3224 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3225 fst->index = stream->nb_streams;
3226 av_set_pts_info(fst, 33, 1, 90000);
3227 stream->streams[stream->nb_streams++] = fst;
3231 /* return the stream number in the feed */
3232 static int add_av_stream(FFStream *feed, AVStream *st)
3235 AVCodecContext *av, *av1;
3239 for(i=0;i<feed->nb_streams;i++) {
3240 st = feed->streams[i];
3242 if (av1->codec_id == av->codec_id &&
3243 av1->codec_type == av->codec_type &&
3244 av1->bit_rate == av->bit_rate) {
3246 switch(av->codec_type) {
3247 case CODEC_TYPE_AUDIO:
3248 if (av1->channels == av->channels &&
3249 av1->sample_rate == av->sample_rate)
3252 case CODEC_TYPE_VIDEO:
3253 if (av1->width == av->width &&
3254 av1->height == av->height &&
3255 av1->time_base.den == av->time_base.den &&
3256 av1->time_base.num == av->time_base.num &&
3257 av1->gop_size == av->gop_size)
3266 fst = add_av_stream1(feed, av);
3269 return feed->nb_streams - 1;
3274 static void remove_stream(FFStream *stream)
3278 while (*ps != NULL) {
3286 /* specific mpeg4 handling : we extract the raw parameters */
3287 static void extract_mpeg4_header(AVFormatContext *infile)
3289 int mpeg4_count, i, size;
3295 for(i=0;i<infile->nb_streams;i++) {
3296 st = infile->streams[i];
3297 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3298 st->codec->extradata_size == 0) {
3305 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3306 while (mpeg4_count > 0) {
3307 if (av_read_packet(infile, &pkt) < 0)
3309 st = infile->streams[pkt.stream_index];
3310 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3311 st->codec->extradata_size == 0) {
3312 av_freep(&st->codec->extradata);
3313 /* fill extradata with the header */
3314 /* XXX: we make hard suppositions here ! */
3316 while (p < pkt.data + pkt.size - 4) {
3317 /* stop when vop header is found */
3318 if (p[0] == 0x00 && p[1] == 0x00 &&
3319 p[2] == 0x01 && p[3] == 0xb6) {
3320 size = p - pkt.data;
3321 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3322 st->codec->extradata = av_malloc(size);
3323 st->codec->extradata_size = size;
3324 memcpy(st->codec->extradata, pkt.data, size);
3331 av_free_packet(&pkt);
3335 /* compute the needed AVStream for each file */
3336 static void build_file_streams(void)
3338 FFStream *stream, *stream_next;
3339 AVFormatContext *infile;
3342 /* gather all streams */
3343 for(stream = first_stream; stream != NULL; stream = stream_next) {
3344 stream_next = stream->next;
3345 if (stream->stream_type == STREAM_TYPE_LIVE &&
3347 /* the stream comes from a file */
3348 /* try to open the file */
3350 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3351 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3352 /* specific case : if transport stream output to RTP,
3353 we use a raw transport stream reader */
3354 stream->ap_in->mpeg2ts_raw = 1;
3355 stream->ap_in->mpeg2ts_compute_pcr = 1;
3358 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3359 stream->ifmt, 0, stream->ap_in)) < 0) {
3360 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3361 /* remove stream (no need to spend more time on it) */
3363 remove_stream(stream);
3365 /* find all the AVStreams inside and reference them in
3367 if (av_find_stream_info(infile) < 0) {
3368 http_log("Could not find codec parameters from '%s'\n",
3369 stream->feed_filename);
3370 av_close_input_file(infile);
3373 extract_mpeg4_header(infile);
3375 for(i=0;i<infile->nb_streams;i++)
3376 add_av_stream1(stream, infile->streams[i]->codec);
3378 av_close_input_file(infile);
3384 /* compute the needed AVStream for each feed */
3385 static void build_feed_streams(void)
3387 FFStream *stream, *feed;
3390 /* gather all streams */
3391 for(stream = first_stream; stream != NULL; stream = stream->next) {
3392 feed = stream->feed;
3394 if (!stream->is_feed) {
3395 /* we handle a stream coming from a feed */
3396 for(i=0;i<stream->nb_streams;i++)
3397 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3402 /* gather all streams */
3403 for(stream = first_stream; stream != NULL; stream = stream->next) {
3404 feed = stream->feed;
3406 if (stream->is_feed) {
3407 for(i=0;i<stream->nb_streams;i++)
3408 stream->feed_streams[i] = i;
3413 /* create feed files if needed */
3414 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3417 if (url_exist(feed->feed_filename)) {
3418 /* See if it matches */
3422 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3423 /* Now see if it matches */
3424 if (s->nb_streams == feed->nb_streams) {
3426 for(i=0;i<s->nb_streams;i++) {
3428 sf = feed->streams[i];
3431 if (sf->index != ss->index ||
3433 printf("Index & Id do not match for stream %d (%s)\n",
3434 i, feed->feed_filename);
3437 AVCodecContext *ccf, *ccs;
3441 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3443 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3444 printf("Codecs do not match for stream %d\n", i);
3446 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3447 printf("Codec bitrates do not match for stream %d\n", i);
3449 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3450 if (CHECK_CODEC(time_base.den) ||
3451 CHECK_CODEC(time_base.num) ||
3452 CHECK_CODEC(width) ||
3453 CHECK_CODEC(height)) {
3454 printf("Codec width, height and framerate do not match for stream %d\n", i);
3457 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3458 if (CHECK_CODEC(sample_rate) ||
3459 CHECK_CODEC(channels) ||
3460 CHECK_CODEC(frame_size)) {
3461 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3465 printf("Unknown codec type\n");
3473 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3474 feed->feed_filename, s->nb_streams, feed->nb_streams);
3476 av_close_input_file(s);
3478 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3479 feed->feed_filename);
3482 if (feed->readonly) {
3483 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3484 feed->feed_filename);
3487 unlink(feed->feed_filename);
3490 if (!url_exist(feed->feed_filename)) {
3491 AVFormatContext s1, *s = &s1;
3493 if (feed->readonly) {
3494 printf("Unable to create feed file '%s' as it is marked readonly\n",
3495 feed->feed_filename);
3499 /* only write the header of the ffm file */
3500 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3501 http_log("Could not open output feed file '%s'\n",
3502 feed->feed_filename);
3505 s->oformat = feed->fmt;
3506 s->nb_streams = feed->nb_streams;
3507 for(i=0;i<s->nb_streams;i++) {
3509 st = feed->streams[i];
3512 av_set_parameters(s, NULL);
3513 if (av_write_header(s) < 0) {
3514 http_log("Container doesn't supports the required parameters\n");
3517 /* XXX: need better api */
3518 av_freep(&s->priv_data);
3521 /* get feed size and write index */
3522 fd = open(feed->feed_filename, O_RDONLY);
3524 http_log("Could not open output feed file '%s'\n",
3525 feed->feed_filename);
3529 feed->feed_write_index = ffm_read_write_index(fd);
3530 feed->feed_size = lseek(fd, 0, SEEK_END);
3531 /* ensure that we do not wrap before the end of file */
3532 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3533 feed->feed_max_size = feed->feed_size;
3539 /* compute the bandwidth used by each stream */
3540 static void compute_bandwidth(void)
3546 for(stream = first_stream; stream != NULL; stream = stream->next) {
3548 for(i=0;i<stream->nb_streams;i++) {
3549 AVStream *st = stream->streams[i];
3550 switch(st->codec->codec_type) {
3551 case CODEC_TYPE_AUDIO:
3552 case CODEC_TYPE_VIDEO:
3553 bandwidth += st->codec->bit_rate;
3559 stream->bandwidth = (bandwidth + 999) / 1000;
3563 static void get_arg(char *buf, int buf_size, const char **pp)
3570 while (isspace(*p)) p++;
3573 if (*p == '\"' || *p == '\'')
3585 if ((q - buf) < buf_size - 1)
3590 if (quote && *p == quote)
3595 /* add a codec and set the default parameters */
3596 static void add_codec(FFStream *stream, AVCodecContext *av)
3600 /* compute default parameters */
3601 switch(av->codec_type) {
3602 case CODEC_TYPE_AUDIO:
3603 if (av->bit_rate == 0)
3604 av->bit_rate = 64000;
3605 if (av->sample_rate == 0)
3606 av->sample_rate = 22050;
3607 if (av->channels == 0)
3610 case CODEC_TYPE_VIDEO:
3611 if (av->bit_rate == 0)
3612 av->bit_rate = 64000;
3613 if (av->time_base.num == 0){
3614 av->time_base.den = 5;
3615 av->time_base.num = 1;
3617 if (av->width == 0 || av->height == 0) {
3621 /* Bitrate tolerance is less for streaming */
3622 if (av->bit_rate_tolerance == 0)
3623 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3624 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3629 if (av->max_qdiff == 0)
3631 av->qcompress = 0.5;
3634 if (!av->nsse_weight)
3635 av->nsse_weight = 8;
3637 av->frame_skip_cmp = FF_CMP_DCTMAX;
3638 av->me_method = ME_EPZS;
3639 av->rc_buffer_aggressivity = 1.0;
3642 av->rc_eq = "tex^qComp";
3643 if (!av->i_quant_factor)
3644 av->i_quant_factor = -0.8;
3645 if (!av->b_quant_factor)
3646 av->b_quant_factor = 1.25;
3647 if (!av->b_quant_offset)
3648 av->b_quant_offset = 1.25;
3649 if (!av->rc_max_rate)
3650 av->rc_max_rate = av->bit_rate * 2;
3652 if (av->rc_max_rate && !av->rc_buffer_size) {
3653 av->rc_buffer_size = av->rc_max_rate;
3662 st = av_mallocz(sizeof(AVStream));
3665 st->codec = avcodec_alloc_context();
3666 stream->streams[stream->nb_streams++] = st;
3667 memcpy(st->codec, av, sizeof(AVCodecContext));
3670 static int opt_audio_codec(const char *arg)
3672 AVCodec *p= avcodec_find_encoder_by_name(arg);
3674 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3675 return CODEC_ID_NONE;
3680 static int opt_video_codec(const char *arg)
3682 AVCodec *p= avcodec_find_encoder_by_name(arg);
3684 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3685 return CODEC_ID_NONE;
3690 /* simplistic plugin support */
3693 static void load_module(const char *filename)
3696 void (*init_func)(void);
3697 dll = dlopen(filename, RTLD_NOW);
3699 fprintf(stderr, "Could not load module '%s' - %s\n",
3700 filename, dlerror());
3704 init_func = dlsym(dll, "ffserver_module_init");
3707 "%s: init function 'ffserver_module_init()' not found\n",
3716 static int opt_default(const char *opt, const char *arg,
3717 AVCodecContext *avctx, int type)
3719 const AVOption *o = NULL;
3720 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3722 o = av_set_string(avctx, opt, arg);
3728 static int parse_ffconfig(const char *filename)
3735 int val, errors, line_num;
3736 FFStream **last_stream, *stream, *redirect;
3737 FFStream **last_feed, *feed;
3738 AVCodecContext audio_enc, video_enc;
3739 int audio_id, video_id;
3741 f = fopen(filename, "r");
3749 first_stream = NULL;
3750 last_stream = &first_stream;
3752 last_feed = &first_feed;
3756 audio_id = CODEC_ID_NONE;
3757 video_id = CODEC_ID_NONE;
3759 if (fgets(line, sizeof(line), f) == NULL)
3765 if (*p == '\0' || *p == '#')
3768 get_arg(cmd, sizeof(cmd), &p);
3770 if (!strcasecmp(cmd, "Port")) {
3771 get_arg(arg, sizeof(arg), &p);
3773 if (val < 1 || val > 65536) {
3774 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3775 filename, line_num, arg);
3778 my_http_addr.sin_port = htons(val);
3779 } else if (!strcasecmp(cmd, "BindAddress")) {
3780 get_arg(arg, sizeof(arg), &p);
3781 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3782 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3783 filename, line_num, arg);
3786 } else if (!strcasecmp(cmd, "NoDaemon")) {
3787 ffserver_daemon = 0;
3788 } else if (!strcasecmp(cmd, "RTSPPort")) {
3789 get_arg(arg, sizeof(arg), &p);
3791 if (val < 1 || val > 65536) {
3792 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3793 filename, line_num, arg);
3796 my_rtsp_addr.sin_port = htons(atoi(arg));
3797 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3798 get_arg(arg, sizeof(arg), &p);
3799 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3800 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3801 filename, line_num, arg);
3804 } else if (!strcasecmp(cmd, "MaxClients")) {
3805 get_arg(arg, sizeof(arg), &p);
3807 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3808 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3809 filename, line_num, arg);
3812 nb_max_connections = val;
3814 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3816 get_arg(arg, sizeof(arg), &p);
3818 if (llval < 10 || llval > 10000000) {
3819 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3820 filename, line_num, arg);
3823 max_bandwidth = llval;
3824 } else if (!strcasecmp(cmd, "CustomLog")) {
3825 if (!ffserver_debug)
3826 get_arg(logfilename, sizeof(logfilename), &p);
3827 } else if (!strcasecmp(cmd, "<Feed")) {
3828 /*********************************************/
3829 /* Feed related options */
3831 if (stream || feed) {
3832 fprintf(stderr, "%s:%d: Already in a tag\n",
3833 filename, line_num);
3835 feed = av_mallocz(sizeof(FFStream));
3836 /* add in stream list */
3837 *last_stream = feed;
3838 last_stream = &feed->next;
3839 /* add in feed list */
3841 last_feed = &feed->next_feed;
3843 get_arg(feed->filename, sizeof(feed->filename), &p);
3844 q = strrchr(feed->filename, '>');
3847 feed->fmt = guess_format("ffm", NULL, NULL);
3848 /* defaut feed file */
3849 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3850 "/tmp/%s.ffm", feed->filename);
3851 feed->feed_max_size = 5 * 1024 * 1024;
3853 feed->feed = feed; /* self feeding :-) */
3855 } else if (!strcasecmp(cmd, "Launch")) {
3859 feed->child_argv = av_mallocz(64 * sizeof(char *));
3861 for (i = 0; i < 62; i++) {
3862 get_arg(arg, sizeof(arg), &p);
3866 feed->child_argv[i] = av_strdup(arg);
3869 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3871 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3873 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3874 inet_ntoa(my_http_addr.sin_addr),
3875 ntohs(my_http_addr.sin_port), feed->filename);
3880 fprintf(stdout, "Launch commandline: ");
3881 for (j = 0; j <= i; j++)
3882 fprintf(stdout, "%s ", feed->child_argv[j]);
3883 fprintf(stdout, "\n");
3886 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3888 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3890 } else if (stream) {
3891 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3893 } else if (!strcasecmp(cmd, "File")) {
3895 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3897 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3898 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3903 get_arg(arg, sizeof(arg), &p);
3905 fsize = strtod(p1, &p1);
3906 switch(toupper(*p1)) {
3911 fsize *= 1024 * 1024;
3914 fsize *= 1024 * 1024 * 1024;
3917 feed->feed_max_size = (int64_t)fsize;
3919 } else if (!strcasecmp(cmd, "</Feed>")) {
3921 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3922 filename, line_num);
3926 } else if (!strcasecmp(cmd, "<Stream")) {
3927 /*********************************************/
3928 /* Stream related options */
3930 if (stream || feed) {
3931 fprintf(stderr, "%s:%d: Already in a tag\n",
3932 filename, line_num);
3934 const AVClass *class;
3935 stream = av_mallocz(sizeof(FFStream));
3936 *last_stream = stream;
3937 last_stream = &stream->next;
3939 get_arg(stream->filename, sizeof(stream->filename), &p);
3940 q = strrchr(stream->filename, '>');
3943 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3944 /* fetch avclass so AVOption works
3945 * FIXME try to use avcodec_get_context_defaults2
3946 * without changing defaults too much */
3947 avcodec_get_context_defaults(&video_enc);
3948 class = video_enc.av_class;
3949 memset(&audio_enc, 0, sizeof(AVCodecContext));
3950 memset(&video_enc, 0, sizeof(AVCodecContext));
3951 audio_enc.av_class = class;
3952 video_enc.av_class = class;
3953 audio_id = CODEC_ID_NONE;
3954 video_id = CODEC_ID_NONE;
3956 audio_id = stream->fmt->audio_codec;
3957 video_id = stream->fmt->video_codec;
3960 } else if (!strcasecmp(cmd, "Feed")) {
3961 get_arg(arg, sizeof(arg), &p);
3966 while (sfeed != NULL) {
3967 if (!strcmp(sfeed->filename, arg))
3969 sfeed = sfeed->next_feed;
3972 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3973 filename, line_num, arg);
3975 stream->feed = sfeed;
3977 } else if (!strcasecmp(cmd, "Format")) {
3978 get_arg(arg, sizeof(arg), &p);
3980 if (!strcmp(arg, "status")) {
3981 stream->stream_type = STREAM_TYPE_STATUS;
3984 stream->stream_type = STREAM_TYPE_LIVE;
3985 /* jpeg cannot be used here, so use single frame jpeg */
3986 if (!strcmp(arg, "jpeg"))
3987 strcpy(arg, "mjpeg");
3988 stream->fmt = guess_stream_format(arg, NULL, NULL);
3990 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3991 filename, line_num, arg);
3996 audio_id = stream->fmt->audio_codec;
3997 video_id = stream->fmt->video_codec;
4000 } else if (!strcasecmp(cmd, "InputFormat")) {
4001 get_arg(arg, sizeof(arg), &p);
4002 stream->ifmt = av_find_input_format(arg);
4003 if (!stream->ifmt) {
4004 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4005 filename, line_num, arg);
4007 } else if (!strcasecmp(cmd, "FaviconURL")) {
4008 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4009 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4011 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4012 filename, line_num);
4015 } else if (!strcasecmp(cmd, "Author")) {
4017 get_arg(stream->author, sizeof(stream->author), &p);
4018 } else if (!strcasecmp(cmd, "Comment")) {
4020 get_arg(stream->comment, sizeof(stream->comment), &p);
4021 } else if (!strcasecmp(cmd, "Copyright")) {
4023 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4024 } else if (!strcasecmp(cmd, "Title")) {
4026 get_arg(stream->title, sizeof(stream->title), &p);
4027 } else if (!strcasecmp(cmd, "Preroll")) {
4028 get_arg(arg, sizeof(arg), &p);
4030 stream->prebuffer = atof(arg) * 1000;
4031 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4033 stream->send_on_key = 1;
4034 } else if (!strcasecmp(cmd, "AudioCodec")) {
4035 get_arg(arg, sizeof(arg), &p);
4036 audio_id = opt_audio_codec(arg);
4037 if (audio_id == CODEC_ID_NONE) {
4038 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4039 filename, line_num, arg);
4042 } else if (!strcasecmp(cmd, "VideoCodec")) {
4043 get_arg(arg, sizeof(arg), &p);
4044 video_id = opt_video_codec(arg);
4045 if (video_id == CODEC_ID_NONE) {
4046 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4047 filename, line_num, arg);
4050 } else if (!strcasecmp(cmd, "MaxTime")) {
4051 get_arg(arg, sizeof(arg), &p);
4053 stream->max_time = atof(arg) * 1000;
4054 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4055 get_arg(arg, sizeof(arg), &p);
4057 audio_enc.bit_rate = atoi(arg) * 1000;
4058 } else if (!strcasecmp(cmd, "AudioChannels")) {
4059 get_arg(arg, sizeof(arg), &p);
4061 audio_enc.channels = atoi(arg);
4062 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4063 get_arg(arg, sizeof(arg), &p);
4065 audio_enc.sample_rate = atoi(arg);
4066 } else if (!strcasecmp(cmd, "AudioQuality")) {
4067 get_arg(arg, sizeof(arg), &p);
4069 // audio_enc.quality = atof(arg) * 1000;
4071 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4073 int minrate, maxrate;
4075 get_arg(arg, sizeof(arg), &p);
4077 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4078 video_enc.rc_min_rate = minrate * 1000;
4079 video_enc.rc_max_rate = maxrate * 1000;
4081 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4082 filename, line_num, arg);
4086 } else if (!strcasecmp(cmd, "Debug")) {
4088 get_arg(arg, sizeof(arg), &p);
4089 video_enc.debug = strtol(arg,0,0);
4091 } else if (!strcasecmp(cmd, "Strict")) {
4093 get_arg(arg, sizeof(arg), &p);
4094 video_enc.strict_std_compliance = atoi(arg);
4096 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4098 get_arg(arg, sizeof(arg), &p);
4099 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4101 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4103 get_arg(arg, sizeof(arg), &p);
4104 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4106 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4107 get_arg(arg, sizeof(arg), &p);
4109 video_enc.bit_rate = atoi(arg) * 1000;
4111 } else if (!strcasecmp(cmd, "VideoSize")) {
4112 get_arg(arg, sizeof(arg), &p);
4114 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4115 if ((video_enc.width % 16) != 0 ||
4116 (video_enc.height % 16) != 0) {
4117 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4118 filename, line_num);
4122 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4123 get_arg(arg, sizeof(arg), &p);
4125 AVRational frame_rate;
4126 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4127 fprintf(stderr, "Incorrect frame rate\n");
4130 video_enc.time_base.num = frame_rate.den;
4131 video_enc.time_base.den = frame_rate.num;
4134 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4135 get_arg(arg, sizeof(arg), &p);
4137 video_enc.gop_size = atoi(arg);
4138 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4140 video_enc.gop_size = 1;
4141 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4143 video_enc.mb_decision = FF_MB_DECISION_BITS;
4144 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4146 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4147 video_enc.flags |= CODEC_FLAG_4MV;
4149 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4150 !strcasecmp(cmd, "AVOptionAudio")) {
4152 AVCodecContext *avctx;
4154 get_arg(arg, sizeof(arg), &p);
4155 get_arg(arg2, sizeof(arg2), &p);
4156 if (!strcasecmp(cmd, "AVOptionVideo")) {
4158 type = AV_OPT_FLAG_VIDEO_PARAM;
4161 type = AV_OPT_FLAG_AUDIO_PARAM;
4163 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4164 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4167 } else if (!strcasecmp(cmd, "VideoTag")) {
4168 get_arg(arg, sizeof(arg), &p);
4169 if ((strlen(arg) == 4) && stream)
4170 video_enc.codec_tag = ff_get_fourcc(arg);
4171 } else if (!strcasecmp(cmd, "BitExact")) {
4173 video_enc.flags |= CODEC_FLAG_BITEXACT;
4174 } else if (!strcasecmp(cmd, "DctFastint")) {
4176 video_enc.dct_algo = FF_DCT_FASTINT;
4177 } else if (!strcasecmp(cmd, "IdctSimple")) {
4179 video_enc.idct_algo = FF_IDCT_SIMPLE;
4180 } else if (!strcasecmp(cmd, "Qscale")) {
4181 get_arg(arg, sizeof(arg), &p);
4183 video_enc.flags |= CODEC_FLAG_QSCALE;
4184 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4186 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4187 get_arg(arg, sizeof(arg), &p);
4189 video_enc.max_qdiff = atoi(arg);
4190 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4191 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4192 filename, line_num);
4196 } else if (!strcasecmp(cmd, "VideoQMax")) {
4197 get_arg(arg, sizeof(arg), &p);
4199 video_enc.qmax = atoi(arg);
4200 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4201 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4202 filename, line_num);
4206 } else if (!strcasecmp(cmd, "VideoQMin")) {
4207 get_arg(arg, sizeof(arg), &p);
4209 video_enc.qmin = atoi(arg);
4210 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4211 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4212 filename, line_num);
4216 } else if (!strcasecmp(cmd, "LumaElim")) {
4217 get_arg(arg, sizeof(arg), &p);
4219 video_enc.luma_elim_threshold = atoi(arg);
4220 } else if (!strcasecmp(cmd, "ChromaElim")) {
4221 get_arg(arg, sizeof(arg), &p);
4223 video_enc.chroma_elim_threshold = atoi(arg);
4224 } else if (!strcasecmp(cmd, "LumiMask")) {
4225 get_arg(arg, sizeof(arg), &p);
4227 video_enc.lumi_masking = atof(arg);
4228 } else if (!strcasecmp(cmd, "DarkMask")) {
4229 get_arg(arg, sizeof(arg), &p);
4231 video_enc.dark_masking = atof(arg);
4232 } else if (!strcasecmp(cmd, "NoVideo")) {
4233 video_id = CODEC_ID_NONE;
4234 } else if (!strcasecmp(cmd, "NoAudio")) {
4235 audio_id = CODEC_ID_NONE;
4236 } else if (!strcasecmp(cmd, "ACL")) {
4239 get_arg(arg, sizeof(arg), &p);
4240 if (strcasecmp(arg, "allow") == 0)
4241 acl.action = IP_ALLOW;
4242 else if (strcasecmp(arg, "deny") == 0)
4243 acl.action = IP_DENY;
4245 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4246 filename, line_num, arg);
4250 get_arg(arg, sizeof(arg), &p);
4252 if (resolve_host(&acl.first, arg) != 0) {
4253 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4254 filename, line_num, arg);
4257 acl.last = acl.first;
4259 get_arg(arg, sizeof(arg), &p);
4262 if (resolve_host(&acl.last, arg) != 0) {
4263 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4264 filename, line_num, arg);
4270 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4271 IPAddressACL **naclp = 0;
4277 naclp = &stream->acl;
4281 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4282 filename, line_num);
4288 naclp = &(*naclp)->next;
4293 } else if (!strcasecmp(cmd, "RTSPOption")) {
4294 get_arg(arg, sizeof(arg), &p);
4296 av_freep(&stream->rtsp_option);
4297 stream->rtsp_option = av_strdup(arg);
4299 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4300 get_arg(arg, sizeof(arg), &p);
4302 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4303 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4304 filename, line_num, arg);
4307 stream->is_multicast = 1;
4308 stream->loop = 1; /* default is looping */
4310 } else if (!strcasecmp(cmd, "MulticastPort")) {
4311 get_arg(arg, sizeof(arg), &p);
4313 stream->multicast_port = atoi(arg);
4314 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4315 get_arg(arg, sizeof(arg), &p);
4317 stream->multicast_ttl = atoi(arg);
4318 } else if (!strcasecmp(cmd, "NoLoop")) {
4321 } else if (!strcasecmp(cmd, "</Stream>")) {
4323 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4324 filename, line_num);
4327 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4328 if (audio_id != CODEC_ID_NONE) {
4329 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4330 audio_enc.codec_id = audio_id;
4331 add_codec(stream, &audio_enc);
4333 if (video_id != CODEC_ID_NONE) {
4334 video_enc.codec_type = CODEC_TYPE_VIDEO;
4335 video_enc.codec_id = video_id;
4336 add_codec(stream, &video_enc);
4341 } else if (!strcasecmp(cmd, "<Redirect")) {
4342 /*********************************************/
4344 if (stream || feed || redirect) {
4345 fprintf(stderr, "%s:%d: Already in a tag\n",
4346 filename, line_num);
4349 redirect = av_mallocz(sizeof(FFStream));
4350 *last_stream = redirect;
4351 last_stream = &redirect->next;
4353 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4354 q = strrchr(redirect->filename, '>');
4357 redirect->stream_type = STREAM_TYPE_REDIRECT;
4359 } else if (!strcasecmp(cmd, "URL")) {
4361 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4362 } else if (!strcasecmp(cmd, "</Redirect>")) {
4364 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4365 filename, line_num);
4368 if (!redirect->feed_filename[0]) {
4369 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4370 filename, line_num);
4375 } else if (!strcasecmp(cmd, "LoadModule")) {
4376 get_arg(arg, sizeof(arg), &p);
4380 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4381 filename, line_num, arg);
4385 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4386 filename, line_num, cmd);
4398 static void handle_child_exit(int sig)
4403 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4406 for (feed = first_feed; feed; feed = feed->next) {
4407 if (feed->pid == pid) {
4408 int uptime = time(0) - feed->pid_start;
4411 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4414 /* Turn off any more restarts */
4415 feed->child_argv = 0;
4420 need_to_start_children = 1;
4423 static void opt_debug()
4426 ffserver_daemon = 0;
4427 logfilename[0] = '-';
4430 static void opt_show_help(void)
4432 printf("usage: ffserver [options]\n"
4433 "Hyper fast multi format Audio/Video streaming server\n");
4435 show_help_options(options, "Main options:\n", 0, 0);
4438 static const OptionDef options[] = {
4439 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4440 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4441 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4442 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4443 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4444 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4445 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4449 int main(int argc, char **argv)
4451 struct sigaction sigact;
4457 config_filename = "/etc/ffserver.conf";
4459 my_program_name = argv[0];
4460 my_program_dir = getcwd(0, 0);
4461 ffserver_daemon = 1;
4463 parse_options(argc, argv, options, NULL);
4465 unsetenv("http_proxy"); /* Kill the http_proxy */
4467 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4469 memset(&sigact, 0, sizeof(sigact));
4470 sigact.sa_handler = handle_child_exit;
4471 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4472 sigaction(SIGCHLD, &sigact, 0);
4474 if (parse_ffconfig(config_filename) < 0) {
4475 fprintf(stderr, "Incorrect config file - exiting.\n");
4479 build_file_streams();
4481 build_feed_streams();
4483 compute_bandwidth();
4485 /* put the process in background and detach it from its TTY */
4486 if (ffserver_daemon) {
4493 } else if (pid > 0) {
4500 open("/dev/null", O_RDWR);
4501 if (strcmp(logfilename, "-") != 0) {
4511 signal(SIGPIPE, SIG_IGN);
4513 /* open log file if needed */
4514 if (logfilename[0] != '\0') {
4515 if (!strcmp(logfilename, "-"))
4518 logfile = fopen(logfilename, "a");
4519 av_log_set_callback(http_av_log);
4522 if (ffserver_daemon)
4525 if (http_server() < 0) {
4526 http_log("Could not start server\n");