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;
296 static int nb_connections;
298 static uint64_t max_bandwidth;
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 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
313 vfprintf(logfile, fmt, ap);
319 static char *ctime1(char *buf2)
327 p = buf2 + strlen(p) - 1;
333 static void log_connection(HTTPContext *c)
340 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
341 inet_ntoa(c->from_addr.sin_addr),
342 ctime1(buf2), c->method, c->url,
343 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
346 static void update_datarate(DataRateData *drd, int64_t count)
348 if (!drd->time1 && !drd->count1) {
349 drd->time1 = drd->time2 = cur_time;
350 drd->count1 = drd->count2 = count;
351 } else if (cur_time - drd->time2 > 5000) {
352 drd->time1 = drd->time2;
353 drd->count1 = drd->count2;
354 drd->time2 = cur_time;
359 /* In bytes per second */
360 static int compute_datarate(DataRateData *drd, int64_t count)
362 if (cur_time == drd->time1)
365 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
369 static void start_children(FFStream *feed)
374 for (; feed; feed = feed->next) {
375 if (feed->child_argv && !feed->pid) {
376 feed->pid_start = time(0);
381 http_log("Unable to create children\n");
390 for (i = 3; i < 256; i++)
393 if (!ffserver_debug) {
394 i = open("/dev/null", O_RDWR);
403 av_strlcpy(pathname, my_program_name, sizeof(pathname));
405 slash = strrchr(pathname, '/');
410 strcpy(slash, "ffmpeg");
412 /* This is needed to make relative pathnames work */
413 chdir(my_program_dir);
415 signal(SIGPIPE, SIG_DFL);
417 execvp(pathname, feed->child_argv);
425 /* open a listening socket */
426 static int socket_open_listen(struct sockaddr_in *my_addr)
430 server_fd = socket(AF_INET,SOCK_STREAM,0);
437 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
439 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
441 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
443 closesocket(server_fd);
447 if (listen (server_fd, 5) < 0) {
449 closesocket(server_fd);
452 ff_socket_nonblock(server_fd, 1);
457 /* start all multicast streams */
458 static void start_multicast(void)
463 struct sockaddr_in dest_addr;
464 int default_port, stream_index;
467 for(stream = first_stream; stream != NULL; stream = stream->next) {
468 if (stream->is_multicast) {
469 /* open the RTP connection */
470 snprintf(session_id, sizeof(session_id), "%08x%08x",
471 av_random(&random_state), av_random(&random_state));
473 /* choose a port if none given */
474 if (stream->multicast_port == 0) {
475 stream->multicast_port = default_port;
479 dest_addr.sin_family = AF_INET;
480 dest_addr.sin_addr = stream->multicast_ip;
481 dest_addr.sin_port = htons(stream->multicast_port);
483 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
484 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
488 if (open_input_stream(rtp_c, "") < 0) {
489 http_log("Could not open input stream for stream '%s'\n",
494 /* open each RTP stream */
495 for(stream_index = 0; stream_index < stream->nb_streams;
497 dest_addr.sin_port = htons(stream->multicast_port +
499 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
500 http_log("Could not open output stream '%s/streamid=%d'\n",
501 stream->filename, stream_index);
506 /* change state to send data */
507 rtp_c->state = HTTPSTATE_SEND_DATA;
512 /* main loop of the http server */
513 static int http_server(void)
515 int server_fd = 0, rtsp_server_fd = 0;
516 int ret, delay, delay1;
517 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
518 HTTPContext *c, *c_next;
520 if (my_http_addr.sin_port) {
521 server_fd = socket_open_listen(&my_http_addr);
526 if (my_rtsp_addr.sin_port) {
527 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
528 if (rtsp_server_fd < 0)
532 if (!rtsp_server_fd && !server_fd) {
533 http_log("HTTP and RTSP disabled.\n");
537 http_log("ffserver started.\n");
539 start_children(first_feed);
541 first_http_ctx = NULL;
547 poll_entry = poll_table;
549 poll_entry->fd = server_fd;
550 poll_entry->events = POLLIN;
553 if (rtsp_server_fd) {
554 poll_entry->fd = rtsp_server_fd;
555 poll_entry->events = POLLIN;
559 /* wait for events on each HTTP handle */
566 case HTTPSTATE_SEND_HEADER:
567 case RTSPSTATE_SEND_REPLY:
568 case RTSPSTATE_SEND_PACKET:
569 c->poll_entry = poll_entry;
571 poll_entry->events = POLLOUT;
574 case HTTPSTATE_SEND_DATA_HEADER:
575 case HTTPSTATE_SEND_DATA:
576 case HTTPSTATE_SEND_DATA_TRAILER:
577 if (!c->is_packetized) {
578 /* for TCP, we output as much as we can (may need to put a limit) */
579 c->poll_entry = poll_entry;
581 poll_entry->events = POLLOUT;
584 /* when ffserver is doing the timing, we work by
585 looking at which packet need to be sent every
587 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
592 case HTTPSTATE_WAIT_REQUEST:
593 case HTTPSTATE_RECEIVE_DATA:
594 case HTTPSTATE_WAIT_FEED:
595 case RTSPSTATE_WAIT_REQUEST:
596 /* need to catch errors */
597 c->poll_entry = poll_entry;
599 poll_entry->events = POLLIN;/* Maybe this will work */
603 c->poll_entry = NULL;
609 /* wait for an event on one connection. We poll at least every
610 second to handle timeouts */
612 ret = poll(poll_table, poll_entry - poll_table, delay);
613 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
614 ff_neterrno() != FF_NETERROR(EINTR))
618 cur_time = av_gettime() / 1000;
620 if (need_to_start_children) {
621 need_to_start_children = 0;
622 start_children(first_feed);
625 /* now handle the events */
626 for(c = first_http_ctx; c != NULL; c = c_next) {
628 if (handle_connection(c) < 0) {
629 /* close and free the connection */
635 poll_entry = poll_table;
637 /* new HTTP connection request ? */
638 if (poll_entry->revents & POLLIN)
639 new_connection(server_fd, 0);
642 if (rtsp_server_fd) {
643 /* new RTSP connection request ? */
644 if (poll_entry->revents & POLLIN)
645 new_connection(rtsp_server_fd, 1);
650 /* start waiting for a new HTTP/RTSP request */
651 static void start_wait_request(HTTPContext *c, int is_rtsp)
653 c->buffer_ptr = c->buffer;
654 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
657 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
658 c->state = RTSPSTATE_WAIT_REQUEST;
660 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
661 c->state = HTTPSTATE_WAIT_REQUEST;
665 static void new_connection(int server_fd, int is_rtsp)
667 struct sockaddr_in from_addr;
669 HTTPContext *c = NULL;
671 len = sizeof(from_addr);
672 fd = accept(server_fd, (struct sockaddr *)&from_addr,
675 http_log("error during accept %s\n", strerror(errno));
678 ff_socket_nonblock(fd, 1);
680 /* XXX: should output a warning page when coming
681 close to the connection limit */
682 if (nb_connections >= nb_max_connections)
685 /* add a new connection */
686 c = av_mallocz(sizeof(HTTPContext));
691 c->poll_entry = NULL;
692 c->from_addr = from_addr;
693 c->buffer_size = IOBUFFER_INIT_SIZE;
694 c->buffer = av_malloc(c->buffer_size);
698 c->next = first_http_ctx;
702 start_wait_request(c, is_rtsp);
714 static void close_connection(HTTPContext *c)
716 HTTPContext **cp, *c1;
718 AVFormatContext *ctx;
722 /* remove connection from list */
723 cp = &first_http_ctx;
724 while ((*cp) != NULL) {
732 /* remove references, if any (XXX: do it faster) */
733 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
738 /* remove connection associated resources */
742 /* close each frame parser */
743 for(i=0;i<c->fmt_in->nb_streams;i++) {
744 st = c->fmt_in->streams[i];
745 if (st->codec->codec)
746 avcodec_close(st->codec);
748 av_close_input_file(c->fmt_in);
751 /* free RTP output streams if any */
754 nb_streams = c->stream->nb_streams;
756 for(i=0;i<nb_streams;i++) {
759 av_write_trailer(ctx);
762 h = c->rtp_handles[i];
769 if (!c->last_packet_sent) {
772 if (url_open_dyn_buf(&ctx->pb) >= 0) {
773 av_write_trailer(ctx);
774 av_freep(&c->pb_buffer);
775 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
780 for(i=0; i<ctx->nb_streams; i++)
781 av_free(ctx->streams[i]);
783 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
784 current_bandwidth -= c->stream->bandwidth;
786 /* signal that there is no feed if we are the feeder socket */
787 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
788 c->stream->feed_opened = 0;
792 av_freep(&c->pb_buffer);
793 av_freep(&c->packet_buffer);
799 static int handle_connection(HTTPContext *c)
804 case HTTPSTATE_WAIT_REQUEST:
805 case RTSPSTATE_WAIT_REQUEST:
807 if ((c->timeout - cur_time) < 0)
809 if (c->poll_entry->revents & (POLLERR | POLLHUP))
812 /* no need to read if no events */
813 if (!(c->poll_entry->revents & POLLIN))
817 len = recv(c->fd, c->buffer_ptr, 1, 0);
819 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
820 ff_neterrno() != FF_NETERROR(EINTR))
822 } else if (len == 0) {
825 /* search for end of request. */
827 c->buffer_ptr += len;
829 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
830 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
831 /* request found : parse it and reply */
832 if (c->state == HTTPSTATE_WAIT_REQUEST) {
833 ret = http_parse_request(c);
835 ret = rtsp_parse_request(c);
839 } else if (ptr >= c->buffer_end) {
840 /* request too long: cannot do anything */
842 } else goto read_loop;
846 case HTTPSTATE_SEND_HEADER:
847 if (c->poll_entry->revents & (POLLERR | POLLHUP))
850 /* no need to write if no events */
851 if (!(c->poll_entry->revents & POLLOUT))
853 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
855 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
856 ff_neterrno() != FF_NETERROR(EINTR)) {
857 /* error : close connection */
858 av_freep(&c->pb_buffer);
862 c->buffer_ptr += len;
864 c->stream->bytes_served += len;
865 c->data_count += len;
866 if (c->buffer_ptr >= c->buffer_end) {
867 av_freep(&c->pb_buffer);
871 /* all the buffer was sent : synchronize to the incoming stream */
872 c->state = HTTPSTATE_SEND_DATA_HEADER;
873 c->buffer_ptr = c->buffer_end = c->buffer;
878 case HTTPSTATE_SEND_DATA:
879 case HTTPSTATE_SEND_DATA_HEADER:
880 case HTTPSTATE_SEND_DATA_TRAILER:
881 /* for packetized output, we consider we can always write (the
882 input streams sets the speed). It may be better to verify
883 that we do not rely too much on the kernel queues */
884 if (!c->is_packetized) {
885 if (c->poll_entry->revents & (POLLERR | POLLHUP))
888 /* no need to read if no events */
889 if (!(c->poll_entry->revents & POLLOUT))
892 if (http_send_data(c) < 0)
894 /* close connection if trailer sent */
895 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
898 case HTTPSTATE_RECEIVE_DATA:
899 /* no need to read if no events */
900 if (c->poll_entry->revents & (POLLERR | POLLHUP))
902 if (!(c->poll_entry->revents & POLLIN))
904 if (http_receive_data(c) < 0)
907 case HTTPSTATE_WAIT_FEED:
908 /* no need to read if no events */
909 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
912 /* nothing to do, we'll be waken up by incoming feed packets */
915 case RTSPSTATE_SEND_REPLY:
916 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
917 av_freep(&c->pb_buffer);
920 /* no need to write if no events */
921 if (!(c->poll_entry->revents & POLLOUT))
923 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
925 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
926 ff_neterrno() != FF_NETERROR(EINTR)) {
927 /* error : close connection */
928 av_freep(&c->pb_buffer);
932 c->buffer_ptr += len;
933 c->data_count += len;
934 if (c->buffer_ptr >= c->buffer_end) {
935 /* all the buffer was sent : wait for a new request */
936 av_freep(&c->pb_buffer);
937 start_wait_request(c, 1);
941 case RTSPSTATE_SEND_PACKET:
942 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
943 av_freep(&c->packet_buffer);
946 /* no need to write if no events */
947 if (!(c->poll_entry->revents & POLLOUT))
949 len = send(c->fd, c->packet_buffer_ptr,
950 c->packet_buffer_end - c->packet_buffer_ptr, 0);
952 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
953 ff_neterrno() != FF_NETERROR(EINTR)) {
954 /* error : close connection */
955 av_freep(&c->packet_buffer);
959 c->packet_buffer_ptr += len;
960 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
961 /* all the buffer was sent : wait for a new request */
962 av_freep(&c->packet_buffer);
963 c->state = RTSPSTATE_WAIT_REQUEST;
967 case HTTPSTATE_READY:
976 static int extract_rates(char *rates, int ratelen, const char *request)
980 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
981 if (strncasecmp(p, "Pragma:", 7) == 0) {
982 const char *q = p + 7;
984 while (*q && *q != '\n' && isspace(*q))
987 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
993 memset(rates, 0xff, ratelen);
996 while (*q && *q != '\n' && *q != ':')
999 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1003 if (stream_no < ratelen && stream_no >= 0)
1004 rates[stream_no] = rate_no;
1006 while (*q && *q != '\n' && !isspace(*q))
1013 p = strchr(p, '\n');
1023 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1026 int best_bitrate = 100000000;
1029 for (i = 0; i < feed->nb_streams; i++) {
1030 AVCodecContext *feed_codec = feed->streams[i]->codec;
1032 if (feed_codec->codec_id != codec->codec_id ||
1033 feed_codec->sample_rate != codec->sample_rate ||
1034 feed_codec->width != codec->width ||
1035 feed_codec->height != codec->height)
1038 /* Potential stream */
1040 /* We want the fastest stream less than bit_rate, or the slowest
1041 * faster than bit_rate
1044 if (feed_codec->bit_rate <= bit_rate) {
1045 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1046 best_bitrate = feed_codec->bit_rate;
1050 if (feed_codec->bit_rate < best_bitrate) {
1051 best_bitrate = feed_codec->bit_rate;
1060 static int modify_current_stream(HTTPContext *c, char *rates)
1063 FFStream *req = c->stream;
1064 int action_required = 0;
1066 /* Not much we can do for a feed */
1070 for (i = 0; i < req->nb_streams; i++) {
1071 AVCodecContext *codec = req->streams[i]->codec;
1075 c->switch_feed_streams[i] = req->feed_streams[i];
1078 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1081 /* Wants off or slow */
1082 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1084 /* This doesn't work well when it turns off the only stream! */
1085 c->switch_feed_streams[i] = -2;
1086 c->feed_streams[i] = -2;
1091 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1092 action_required = 1;
1095 return action_required;
1099 static void do_switch_stream(HTTPContext *c, int i)
1101 if (c->switch_feed_streams[i] >= 0) {
1103 c->feed_streams[i] = c->switch_feed_streams[i];
1106 /* Now update the stream */
1108 c->switch_feed_streams[i] = -1;
1111 /* XXX: factorize in utils.c ? */
1112 /* XXX: take care with different space meaning */
1113 static void skip_spaces(const char **pp)
1117 while (*p == ' ' || *p == '\t')
1122 static void get_word(char *buf, int buf_size, const char **pp)
1130 while (!isspace(*p) && *p != '\0') {
1131 if ((q - buf) < buf_size - 1)
1140 static int validate_acl(FFStream *stream, HTTPContext *c)
1142 enum IPAddressAction last_action = IP_DENY;
1144 struct in_addr *src = &c->from_addr.sin_addr;
1145 unsigned long src_addr = src->s_addr;
1147 for (acl = stream->acl; acl; acl = acl->next) {
1148 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1149 return (acl->action == IP_ALLOW) ? 1 : 0;
1150 last_action = acl->action;
1153 /* Nothing matched, so return not the last action */
1154 return (last_action == IP_DENY) ? 1 : 0;
1157 /* compute the real filename of a file by matching it without its
1158 extensions to all the stream filenames */
1159 static void compute_real_filename(char *filename, int max_size)
1166 /* compute filename by matching without the file extensions */
1167 av_strlcpy(file1, filename, sizeof(file1));
1168 p = strrchr(file1, '.');
1171 for(stream = first_stream; stream != NULL; stream = stream->next) {
1172 av_strlcpy(file2, stream->filename, sizeof(file2));
1173 p = strrchr(file2, '.');
1176 if (!strcmp(file1, file2)) {
1177 av_strlcpy(filename, stream->filename, max_size);
1192 /* parse http request and prepare header */
1193 static int http_parse_request(HTTPContext *c)
1196 enum RedirType redir_type;
1198 char info[1024], filename[1024];
1202 const char *mime_type;
1206 char *useragent = 0;
1209 get_word(cmd, sizeof(cmd), (const char **)&p);
1210 av_strlcpy(c->method, cmd, sizeof(c->method));
1212 if (!strcmp(cmd, "GET"))
1214 else if (!strcmp(cmd, "POST"))
1219 get_word(url, sizeof(url), (const char **)&p);
1220 av_strlcpy(c->url, url, sizeof(c->url));
1222 get_word(protocol, sizeof(protocol), (const char **)&p);
1223 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1226 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1229 http_log("New connection: %s %s\n", cmd, url);
1231 /* find the filename and the optional info string in the request */
1232 p = strchr(url, '?');
1234 av_strlcpy(info, p, sizeof(info));
1239 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1241 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1242 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1244 if (*useragent && *useragent != '\n' && isspace(*useragent))
1248 p = strchr(p, '\n');
1255 redir_type = REDIR_NONE;
1256 if (match_ext(filename, "asx")) {
1257 redir_type = REDIR_ASX;
1258 filename[strlen(filename)-1] = 'f';
1259 } else if (match_ext(filename, "asf") &&
1260 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1261 /* if this isn't WMP or lookalike, return the redirector file */
1262 redir_type = REDIR_ASF;
1263 } else if (match_ext(filename, "rpm,ram")) {
1264 redir_type = REDIR_RAM;
1265 strcpy(filename + strlen(filename)-2, "m");
1266 } else if (match_ext(filename, "rtsp")) {
1267 redir_type = REDIR_RTSP;
1268 compute_real_filename(filename, sizeof(filename) - 1);
1269 } else if (match_ext(filename, "sdp")) {
1270 redir_type = REDIR_SDP;
1271 compute_real_filename(filename, sizeof(filename) - 1);
1274 // "redirect" / request to index.html
1275 if (!strlen(filename))
1276 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1278 stream = first_stream;
1279 while (stream != NULL) {
1280 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1282 stream = stream->next;
1284 if (stream == NULL) {
1285 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1290 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1291 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1293 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1294 c->http_error = 301;
1296 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1297 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1298 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1299 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1300 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1301 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1302 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1304 /* prepare output buffer */
1305 c->buffer_ptr = c->buffer;
1307 c->state = HTTPSTATE_SEND_HEADER;
1311 /* If this is WMP, get the rate information */
1312 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1313 if (modify_current_stream(c, ratebuf)) {
1314 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1315 if (c->switch_feed_streams[i] >= 0)
1316 do_switch_stream(c, i);
1321 /* If already streaming this feed, do not let start another feeder. */
1322 if (stream->feed_opened) {
1323 snprintf(msg, sizeof(msg), "This feed is already being received.");
1327 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1328 current_bandwidth += stream->bandwidth;
1330 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1331 c->http_error = 200;
1333 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1334 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1335 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1336 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1337 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");
1338 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",
1339 current_bandwidth, max_bandwidth);
1340 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1342 /* prepare output buffer */
1343 c->buffer_ptr = c->buffer;
1345 c->state = HTTPSTATE_SEND_HEADER;
1349 if (redir_type != REDIR_NONE) {
1352 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1353 if (strncasecmp(p, "Host:", 5) == 0) {
1357 p = strchr(p, '\n');
1368 while (isspace(*hostinfo))
1371 eoh = strchr(hostinfo, '\n');
1373 if (eoh[-1] == '\r')
1376 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1377 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1378 hostbuf[eoh - hostinfo] = 0;
1380 c->http_error = 200;
1382 switch(redir_type) {
1384 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1385 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1386 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1387 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1388 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1389 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1390 hostbuf, filename, info);
1391 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1394 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1395 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1396 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1397 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1398 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1399 hostbuf, filename, info);
1402 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1403 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1404 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1405 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1407 hostbuf, filename, info);
1411 char hostname[256], *p;
1412 /* extract only hostname */
1413 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1414 p = strrchr(hostname, ':');
1417 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1418 /* XXX: incorrect mime type ? */
1419 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1420 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1421 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1422 hostname, ntohs(my_rtsp_addr.sin_port),
1429 int sdp_data_size, len;
1430 struct sockaddr_in my_addr;
1432 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1433 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1434 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1436 len = sizeof(my_addr);
1437 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1439 /* XXX: should use a dynamic buffer */
1440 sdp_data_size = prepare_sdp_description(stream,
1443 if (sdp_data_size > 0) {
1444 memcpy(q, sdp_data, sdp_data_size);
1456 /* prepare output buffer */
1457 c->buffer_ptr = c->buffer;
1459 c->state = HTTPSTATE_SEND_HEADER;
1465 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1469 stream->conns_served++;
1471 /* XXX: add there authenticate and IP match */
1474 /* if post, it means a feed is being sent */
1475 if (!stream->is_feed) {
1476 /* However it might be a status report from WMP! Lets log the data
1477 * as it might come in handy one day
1482 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1483 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1487 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1488 client_id = strtol(p + 18, 0, 10);
1489 p = strchr(p, '\n');
1497 char *eol = strchr(logline, '\n');
1502 if (eol[-1] == '\r')
1504 http_log("%.*s\n", (int) (eol - logline), logline);
1505 c->suppress_log = 1;
1510 http_log("\nGot request:\n%s\n", c->buffer);
1513 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1516 /* Now we have to find the client_id */
1517 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1518 if (wmpc->wmp_client_id == client_id)
1522 if (wmpc && modify_current_stream(wmpc, ratebuf))
1523 wmpc->switch_pending = 1;
1526 snprintf(msg, sizeof(msg), "POST command not handled");
1530 if (http_start_receive_data(c) < 0) {
1531 snprintf(msg, sizeof(msg), "could not open feed");
1535 c->state = HTTPSTATE_RECEIVE_DATA;
1540 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1541 http_log("\nGot request:\n%s\n", c->buffer);
1544 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1547 /* open input stream */
1548 if (open_input_stream(c, info) < 0) {
1549 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1553 /* prepare http header */
1555 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1556 mime_type = c->stream->fmt->mime_type;
1558 mime_type = "application/x-octet-stream";
1559 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1561 /* for asf, we need extra headers */
1562 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1563 /* Need to allocate a client id */
1565 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1567 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);
1569 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1570 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1572 /* prepare output buffer */
1574 c->buffer_ptr = c->buffer;
1576 c->state = HTTPSTATE_SEND_HEADER;
1579 c->http_error = 404;
1581 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1582 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1583 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1584 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1585 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1586 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1587 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1589 /* prepare output buffer */
1590 c->buffer_ptr = c->buffer;
1592 c->state = HTTPSTATE_SEND_HEADER;
1596 c->http_error = 200; /* horrible : we use this value to avoid
1597 going to the send data state */
1598 c->state = HTTPSTATE_SEND_HEADER;
1602 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1604 static const char *suffix = " kMGTP";
1607 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1609 url_fprintf(pb, "%"PRId64"%c", count, *s);
1612 static void compute_status(HTTPContext *c)
1621 if (url_open_dyn_buf(&pb) < 0) {
1622 /* XXX: return an error ? */
1623 c->buffer_ptr = c->buffer;
1624 c->buffer_end = c->buffer;
1628 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1629 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1630 url_fprintf(pb, "Pragma: no-cache\r\n");
1631 url_fprintf(pb, "\r\n");
1633 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1634 if (c->stream->feed_filename[0])
1635 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1636 url_fprintf(pb, "</HEAD>\n<BODY>");
1637 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1639 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1640 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1641 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");
1642 stream = first_stream;
1643 while (stream != NULL) {
1644 char sfilename[1024];
1647 if (stream->feed != stream) {
1648 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1649 eosf = sfilename + strlen(sfilename);
1650 if (eosf - sfilename >= 4) {
1651 if (strcmp(eosf - 4, ".asf") == 0)
1652 strcpy(eosf - 4, ".asx");
1653 else if (strcmp(eosf - 3, ".rm") == 0)
1654 strcpy(eosf - 3, ".ram");
1655 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1656 /* generate a sample RTSP director if
1657 unicast. Generate an SDP redirector if
1659 eosf = strrchr(sfilename, '.');
1661 eosf = sfilename + strlen(sfilename);
1662 if (stream->is_multicast)
1663 strcpy(eosf, ".sdp");
1665 strcpy(eosf, ".rtsp");
1669 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1670 sfilename, stream->filename);
1671 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1672 stream->conns_served);
1673 fmt_bytecount(pb, stream->bytes_served);
1674 switch(stream->stream_type) {
1675 case STREAM_TYPE_LIVE:
1677 int audio_bit_rate = 0;
1678 int video_bit_rate = 0;
1679 const char *audio_codec_name = "";
1680 const char *video_codec_name = "";
1681 const char *audio_codec_name_extra = "";
1682 const char *video_codec_name_extra = "";
1684 for(i=0;i<stream->nb_streams;i++) {
1685 AVStream *st = stream->streams[i];
1686 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1687 switch(st->codec->codec_type) {
1688 case CODEC_TYPE_AUDIO:
1689 audio_bit_rate += st->codec->bit_rate;
1691 if (*audio_codec_name)
1692 audio_codec_name_extra = "...";
1693 audio_codec_name = codec->name;
1696 case CODEC_TYPE_VIDEO:
1697 video_bit_rate += st->codec->bit_rate;
1699 if (*video_codec_name)
1700 video_codec_name_extra = "...";
1701 video_codec_name = codec->name;
1704 case CODEC_TYPE_DATA:
1705 video_bit_rate += st->codec->bit_rate;
1711 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",
1714 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1715 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1717 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1719 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1720 url_fprintf(pb, "\n");
1724 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1728 stream = stream->next;
1730 url_fprintf(pb, "</TABLE>\n");
1732 stream = first_stream;
1733 while (stream != NULL) {
1734 if (stream->feed == stream) {
1735 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1737 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1739 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1744 /* This is somewhat linux specific I guess */
1745 snprintf(ps_cmd, sizeof(ps_cmd),
1746 "ps -o \"%%cpu,cputime\" --no-headers %d",
1749 pid_stat = popen(ps_cmd, "r");
1754 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1756 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1764 url_fprintf(pb, "<p>");
1766 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");
1768 for (i = 0; i < stream->nb_streams; i++) {
1769 AVStream *st = stream->streams[i];
1770 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1771 const char *type = "unknown";
1772 char parameters[64];
1776 switch(st->codec->codec_type) {
1777 case CODEC_TYPE_AUDIO:
1779 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1781 case CODEC_TYPE_VIDEO:
1783 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1784 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1789 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1790 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1792 url_fprintf(pb, "</table>\n");
1795 stream = stream->next;
1801 AVCodecContext *enc;
1805 stream = first_feed;
1806 while (stream != NULL) {
1807 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1808 url_fprintf(pb, "<TABLE>\n");
1809 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1810 for(i=0;i<stream->nb_streams;i++) {
1811 AVStream *st = stream->streams[i];
1812 FeedData *fdata = st->priv_data;
1815 avcodec_string(buf, sizeof(buf), enc);
1816 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1817 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1818 avg /= enc->frame_size;
1819 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1820 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1822 url_fprintf(pb, "</TABLE>\n");
1823 stream = stream->next_feed;
1828 /* connection status */
1829 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1831 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1832 nb_connections, nb_max_connections);
1834 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1835 current_bandwidth, max_bandwidth);
1837 url_fprintf(pb, "<TABLE>\n");
1838 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");
1839 c1 = first_http_ctx;
1841 while (c1 != NULL) {
1847 for (j = 0; j < c1->stream->nb_streams; j++) {
1848 if (!c1->stream->feed)
1849 bitrate += c1->stream->streams[j]->codec->bit_rate;
1850 else if (c1->feed_streams[j] >= 0)
1851 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1856 p = inet_ntoa(c1->from_addr.sin_addr);
1857 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1859 c1->stream ? c1->stream->filename : "",
1860 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1863 http_state[c1->state]);
1864 fmt_bytecount(pb, bitrate);
1865 url_fprintf(pb, "<td align=right>");
1866 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1867 url_fprintf(pb, "<td align=right>");
1868 fmt_bytecount(pb, c1->data_count);
1869 url_fprintf(pb, "\n");
1872 url_fprintf(pb, "</TABLE>\n");
1877 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1878 url_fprintf(pb, "</BODY>\n</HTML>\n");
1880 len = url_close_dyn_buf(pb, &c->pb_buffer);
1881 c->buffer_ptr = c->pb_buffer;
1882 c->buffer_end = c->pb_buffer + len;
1885 /* check if the parser needs to be opened for stream i */
1886 static void open_parser(AVFormatContext *s, int i)
1888 AVStream *st = s->streams[i];
1891 if (!st->codec->codec) {
1892 codec = avcodec_find_decoder(st->codec->codec_id);
1893 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1894 st->codec->parse_only = 1;
1895 if (avcodec_open(st->codec, codec) < 0)
1896 st->codec->parse_only = 0;
1901 static int open_input_stream(HTTPContext *c, const char *info)
1904 char input_filename[1024];
1906 int buf_size, i, ret;
1909 /* find file name */
1910 if (c->stream->feed) {
1911 strcpy(input_filename, c->stream->feed->feed_filename);
1912 buf_size = FFM_PACKET_SIZE;
1913 /* compute position (absolute time) */
1914 if (find_info_tag(buf, sizeof(buf), "date", info))
1916 stream_pos = parse_date(buf, 0);
1917 if (stream_pos == INT64_MIN)
1920 else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1921 int prebuffer = strtol(buf, 0, 10);
1922 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1924 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1926 strcpy(input_filename, c->stream->feed_filename);
1928 /* compute position (relative time) */
1929 if (find_info_tag(buf, sizeof(buf), "date", info))
1931 stream_pos = parse_date(buf, 1);
1932 if (stream_pos == INT64_MIN)
1938 if (input_filename[0] == '\0')
1942 { time_t when = stream_pos / 1000000;
1943 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1948 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1949 buf_size, c->stream->ap_in)) < 0) {
1950 http_log("could not open %s: %d\n", input_filename, ret);
1953 s->flags |= AVFMT_FLAG_GENPTS;
1955 av_find_stream_info(c->fmt_in);
1957 /* open each parser */
1958 for(i=0;i<s->nb_streams;i++)
1961 /* choose stream as clock source (we favorize video stream if
1962 present) for packet sending */
1963 c->pts_stream_index = 0;
1964 for(i=0;i<c->stream->nb_streams;i++) {
1965 if (c->pts_stream_index == 0 &&
1966 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1967 c->pts_stream_index = i;
1972 if (c->fmt_in->iformat->read_seek)
1973 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1975 /* set the start time (needed for maxtime and RTP packet timing) */
1976 c->start_time = cur_time;
1977 c->first_pts = AV_NOPTS_VALUE;
1981 /* return the server clock (in us) */
1982 static int64_t get_server_clock(HTTPContext *c)
1984 /* compute current pts value from system time */
1985 return (cur_time - c->start_time) * 1000;
1988 /* return the estimated time at which the current packet must be sent
1990 static int64_t get_packet_send_clock(HTTPContext *c)
1992 int bytes_left, bytes_sent, frame_bytes;
1994 frame_bytes = c->cur_frame_bytes;
1995 if (frame_bytes <= 0)
1998 bytes_left = c->buffer_end - c->buffer_ptr;
1999 bytes_sent = frame_bytes - bytes_left;
2000 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2005 static int http_prepare_data(HTTPContext *c)
2008 AVFormatContext *ctx;
2010 av_freep(&c->pb_buffer);
2012 case HTTPSTATE_SEND_DATA_HEADER:
2013 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2014 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2015 sizeof(c->fmt_ctx.author));
2016 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2017 sizeof(c->fmt_ctx.comment));
2018 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2019 sizeof(c->fmt_ctx.copyright));
2020 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2021 sizeof(c->fmt_ctx.title));
2023 /* open output stream by using specified codecs */
2024 c->fmt_ctx.oformat = c->stream->fmt;
2025 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2026 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2029 st = av_mallocz(sizeof(AVStream));
2030 st->codec= avcodec_alloc_context();
2031 c->fmt_ctx.streams[i] = st;
2032 /* if file or feed, then just take streams from FFStream struct */
2033 if (!c->stream->feed ||
2034 c->stream->feed == c->stream)
2035 src = c->stream->streams[i];
2037 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2041 st->codec->frame_number = 0; /* XXX: should be done in
2042 AVStream, not in codec */
2044 c->got_key_frame = 0;
2046 /* prepare header and save header data in a stream */
2047 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2048 /* XXX: potential leak */
2051 c->fmt_ctx.pb->is_streamed = 1;
2054 * HACK to avoid mpeg ps muxer to spit many underflow errors
2055 * Default value from FFmpeg
2056 * Try to set it use configuration option
2058 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2059 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2061 av_set_parameters(&c->fmt_ctx, NULL);
2062 if (av_write_header(&c->fmt_ctx) < 0) {
2063 http_log("Error writing output header\n");
2067 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2068 c->buffer_ptr = c->pb_buffer;
2069 c->buffer_end = c->pb_buffer + len;
2071 c->state = HTTPSTATE_SEND_DATA;
2072 c->last_packet_sent = 0;
2074 case HTTPSTATE_SEND_DATA:
2075 /* find a new packet */
2076 /* read a packet from the input stream */
2077 if (c->stream->feed)
2078 ffm_set_write_index(c->fmt_in,
2079 c->stream->feed->feed_write_index,
2080 c->stream->feed->feed_size);
2082 if (c->stream->max_time &&
2083 c->stream->max_time + c->start_time - cur_time < 0)
2084 /* We have timed out */
2085 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2089 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2090 if (c->stream->feed && c->stream->feed->feed_opened) {
2091 /* if coming from feed, it means we reached the end of the
2092 ffm file, so must wait for more data */
2093 c->state = HTTPSTATE_WAIT_FEED;
2094 return 1; /* state changed */
2096 if (c->stream->loop) {
2097 av_close_input_file(c->fmt_in);
2099 if (open_input_stream(c, "") < 0)
2104 /* must send trailer now because eof or error */
2105 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2109 int source_index = pkt.stream_index;
2110 /* update first pts if needed */
2111 if (c->first_pts == AV_NOPTS_VALUE) {
2112 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2113 c->start_time = cur_time;
2115 /* send it to the appropriate stream */
2116 if (c->stream->feed) {
2117 /* if coming from a feed, select the right stream */
2118 if (c->switch_pending) {
2119 c->switch_pending = 0;
2120 for(i=0;i<c->stream->nb_streams;i++) {
2121 if (c->switch_feed_streams[i] == pkt.stream_index)
2122 if (pkt.flags & PKT_FLAG_KEY)
2123 do_switch_stream(c, i);
2124 if (c->switch_feed_streams[i] >= 0)
2125 c->switch_pending = 1;
2128 for(i=0;i<c->stream->nb_streams;i++) {
2129 if (c->feed_streams[i] == pkt.stream_index) {
2130 AVStream *st = c->fmt_in->streams[source_index];
2131 pkt.stream_index = i;
2132 if (pkt.flags & PKT_FLAG_KEY &&
2133 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2134 c->stream->nb_streams == 1))
2135 c->got_key_frame = 1;
2136 if (!c->stream->send_on_key || c->got_key_frame)
2141 AVCodecContext *codec;
2144 /* specific handling for RTP: we use several
2145 output stream (one for each RTP
2146 connection). XXX: need more abstract handling */
2147 if (c->is_packetized) {
2149 /* compute send time and duration */
2150 st = c->fmt_in->streams[pkt.stream_index];
2151 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2152 if (st->start_time != AV_NOPTS_VALUE)
2153 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2154 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2156 printf("index=%d pts=%0.3f duration=%0.6f\n",
2158 (double)c->cur_pts /
2160 (double)c->cur_frame_duration /
2163 /* find RTP context */
2164 c->packet_stream_index = pkt.stream_index;
2165 ctx = c->rtp_ctx[c->packet_stream_index];
2167 av_free_packet(&pkt);
2170 codec = ctx->streams[0]->codec;
2171 /* only one stream per RTP connection */
2172 pkt.stream_index = 0;
2176 codec = ctx->streams[pkt.stream_index]->codec;
2179 if (c->is_packetized) {
2180 int max_packet_size;
2181 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2182 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2184 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2185 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2187 ret = url_open_dyn_buf(&ctx->pb);
2190 /* XXX: potential leak */
2193 c->fmt_ctx.pb->is_streamed = 1;
2194 if (pkt.dts != AV_NOPTS_VALUE)
2195 pkt.dts = av_rescale_q(pkt.dts,
2196 c->fmt_in->streams[source_index]->time_base,
2197 ctx->streams[pkt.stream_index]->time_base);
2198 if (pkt.pts != AV_NOPTS_VALUE)
2199 pkt.pts = av_rescale_q(pkt.pts,
2200 c->fmt_in->streams[source_index]->time_base,
2201 ctx->streams[pkt.stream_index]->time_base);
2202 pkt.duration = av_rescale_q(pkt.duration,
2203 c->fmt_in->streams[source_index]->time_base,
2204 ctx->streams[pkt.stream_index]->time_base);
2205 if (av_write_frame(ctx, &pkt) < 0) {
2206 http_log("Error writing frame to output\n");
2207 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2211 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2212 c->cur_frame_bytes = len;
2213 c->buffer_ptr = c->pb_buffer;
2214 c->buffer_end = c->pb_buffer + len;
2216 codec->frame_number++;
2218 av_free_packet(&pkt);
2222 av_free_packet(&pkt);
2227 case HTTPSTATE_SEND_DATA_TRAILER:
2228 /* last packet test ? */
2229 if (c->last_packet_sent || c->is_packetized)
2232 /* prepare header */
2233 if (url_open_dyn_buf(&ctx->pb) < 0) {
2234 /* XXX: potential leak */
2237 c->fmt_ctx.pb->is_streamed = 1;
2238 av_write_trailer(ctx);
2239 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2240 c->buffer_ptr = c->pb_buffer;
2241 c->buffer_end = c->pb_buffer + len;
2243 c->last_packet_sent = 1;
2249 /* should convert the format at the same time */
2250 /* send data starting at c->buffer_ptr to the output connection
2251 (either UDP or TCP connection) */
2252 static int http_send_data(HTTPContext *c)
2257 if (c->buffer_ptr >= c->buffer_end) {
2258 ret = http_prepare_data(c);
2262 /* state change requested */
2265 if (c->is_packetized) {
2266 /* RTP data output */
2267 len = c->buffer_end - c->buffer_ptr;
2269 /* fail safe - should never happen */
2271 c->buffer_ptr = c->buffer_end;
2274 len = (c->buffer_ptr[0] << 24) |
2275 (c->buffer_ptr[1] << 16) |
2276 (c->buffer_ptr[2] << 8) |
2278 if (len > (c->buffer_end - c->buffer_ptr))
2280 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2281 /* nothing to send yet: we can wait */
2285 c->data_count += len;
2286 update_datarate(&c->datarate, c->data_count);
2288 c->stream->bytes_served += len;
2290 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2291 /* RTP packets are sent inside the RTSP TCP connection */
2293 int interleaved_index, size;
2295 HTTPContext *rtsp_c;
2298 /* if no RTSP connection left, error */
2301 /* if already sending something, then wait. */
2302 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2304 if (url_open_dyn_buf(&pb) < 0)
2306 interleaved_index = c->packet_stream_index * 2;
2307 /* RTCP packets are sent at odd indexes */
2308 if (c->buffer_ptr[1] == 200)
2309 interleaved_index++;
2310 /* write RTSP TCP header */
2312 header[1] = interleaved_index;
2313 header[2] = len >> 8;
2315 put_buffer(pb, header, 4);
2316 /* write RTP packet data */
2318 put_buffer(pb, c->buffer_ptr, len);
2319 size = url_close_dyn_buf(pb, &c->packet_buffer);
2320 /* prepare asynchronous TCP sending */
2321 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2322 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2323 c->buffer_ptr += len;
2325 /* send everything we can NOW */
2326 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2327 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2329 rtsp_c->packet_buffer_ptr += len;
2330 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2331 /* if we could not send all the data, we will
2332 send it later, so a new state is needed to
2333 "lock" the RTSP TCP connection */
2334 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2337 /* all data has been sent */
2338 av_freep(&c->packet_buffer);
2340 /* send RTP packet directly in UDP */
2342 url_write(c->rtp_handles[c->packet_stream_index],
2343 c->buffer_ptr, len);
2344 c->buffer_ptr += len;
2345 /* here we continue as we can send several packets per 10 ms slot */
2348 /* TCP data output */
2349 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2351 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2352 ff_neterrno() != FF_NETERROR(EINTR))
2353 /* error : close connection */
2358 c->buffer_ptr += len;
2360 c->data_count += len;
2361 update_datarate(&c->datarate, c->data_count);
2363 c->stream->bytes_served += len;
2371 static int http_start_receive_data(HTTPContext *c)
2375 if (c->stream->feed_opened)
2378 /* Don't permit writing to this one */
2379 if (c->stream->readonly)
2383 fd = open(c->stream->feed_filename, O_RDWR);
2385 http_log("Error opening feeder file: %s\n", strerror(errno));
2390 c->stream->feed_write_index = ffm_read_write_index(fd);
2391 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2392 lseek(fd, 0, SEEK_SET);
2394 /* init buffer input */
2395 c->buffer_ptr = c->buffer;
2396 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2397 c->stream->feed_opened = 1;
2401 static int http_receive_data(HTTPContext *c)
2405 if (c->buffer_end > c->buffer_ptr) {
2408 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2410 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2411 ff_neterrno() != FF_NETERROR(EINTR))
2412 /* error : close connection */
2414 } else if (len == 0)
2415 /* end of connection : close it */
2418 c->buffer_ptr += len;
2419 c->data_count += len;
2420 update_datarate(&c->datarate, c->data_count);
2424 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2425 if (c->buffer[0] != 'f' ||
2426 c->buffer[1] != 'm') {
2427 http_log("Feed stream has become desynchronized -- disconnecting\n");
2432 if (c->buffer_ptr >= c->buffer_end) {
2433 FFStream *feed = c->stream;
2434 /* a packet has been received : write it in the store, except
2436 if (c->data_count > FFM_PACKET_SIZE) {
2438 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2439 /* XXX: use llseek or url_seek */
2440 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2441 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2442 http_log("Error writing to feed file: %s\n", strerror(errno));
2446 feed->feed_write_index += FFM_PACKET_SIZE;
2447 /* update file size */
2448 if (feed->feed_write_index > c->stream->feed_size)
2449 feed->feed_size = feed->feed_write_index;
2451 /* handle wrap around if max file size reached */
2452 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2453 feed->feed_write_index = FFM_PACKET_SIZE;
2456 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2458 /* wake up any waiting connections */
2459 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2460 if (c1->state == HTTPSTATE_WAIT_FEED &&
2461 c1->stream->feed == c->stream->feed)
2462 c1->state = HTTPSTATE_SEND_DATA;
2465 /* We have a header in our hands that contains useful data */
2467 AVInputFormat *fmt_in;
2470 memset(&s, 0, sizeof(s));
2472 url_open_buf(&s.pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2473 s.pb->is_streamed = 1;
2475 /* use feed output format name to find corresponding input format */
2476 fmt_in = av_find_input_format(feed->fmt->name);
2480 if (fmt_in->priv_data_size > 0) {
2481 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2487 if (fmt_in->read_header(&s, 0) < 0) {
2488 av_freep(&s.priv_data);
2492 /* Now we have the actual streams */
2493 if (s.nb_streams != feed->nb_streams) {
2494 av_freep(&s.priv_data);
2497 for (i = 0; i < s.nb_streams; i++)
2498 memcpy(feed->streams[i]->codec,
2499 s.streams[i]->codec, sizeof(AVCodecContext));
2500 av_freep(&s.priv_data);
2502 c->buffer_ptr = c->buffer;
2507 c->stream->feed_opened = 0;
2509 /* wake up any waiting connections to stop waiting for feed */
2510 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2511 if (c1->state == HTTPSTATE_WAIT_FEED &&
2512 c1->stream->feed == c->stream->feed)
2513 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2518 /********************************************************************/
2521 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2528 switch(error_number) {
2529 case RTSP_STATUS_OK:
2532 case RTSP_STATUS_METHOD:
2533 str = "Method Not Allowed";
2535 case RTSP_STATUS_BANDWIDTH:
2536 str = "Not Enough Bandwidth";
2538 case RTSP_STATUS_SESSION:
2539 str = "Session Not Found";
2541 case RTSP_STATUS_STATE:
2542 str = "Method Not Valid in This State";
2544 case RTSP_STATUS_AGGREGATE:
2545 str = "Aggregate operation not allowed";
2547 case RTSP_STATUS_ONLY_AGGREGATE:
2548 str = "Only aggregate operation allowed";
2550 case RTSP_STATUS_TRANSPORT:
2551 str = "Unsupported transport";
2553 case RTSP_STATUS_INTERNAL:
2554 str = "Internal Server Error";
2556 case RTSP_STATUS_SERVICE:
2557 str = "Service Unavailable";
2559 case RTSP_STATUS_VERSION:
2560 str = "RTSP Version not supported";
2563 str = "Unknown Error";
2567 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2568 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2570 /* output GMT time */
2574 p = buf2 + strlen(p) - 1;
2577 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2580 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2582 rtsp_reply_header(c, error_number);
2583 url_fprintf(c->pb, "\r\n");
2586 static int rtsp_parse_request(HTTPContext *c)
2588 const char *p, *p1, *p2;
2594 RTSPHeader header1, *header = &header1;
2596 c->buffer_ptr[0] = '\0';
2599 get_word(cmd, sizeof(cmd), &p);
2600 get_word(url, sizeof(url), &p);
2601 get_word(protocol, sizeof(protocol), &p);
2603 av_strlcpy(c->method, cmd, sizeof(c->method));
2604 av_strlcpy(c->url, url, sizeof(c->url));
2605 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2607 if (url_open_dyn_buf(&c->pb) < 0) {
2608 /* XXX: cannot do more */
2609 c->pb = NULL; /* safety */
2613 /* check version name */
2614 if (strcmp(protocol, "RTSP/1.0") != 0) {
2615 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2619 /* parse each header line */
2620 memset(header, 0, sizeof(RTSPHeader));
2621 /* skip to next line */
2622 while (*p != '\n' && *p != '\0')
2626 while (*p != '\0') {
2627 p1 = strchr(p, '\n');
2631 if (p2 > p && p2[-1] == '\r')
2633 /* skip empty line */
2637 if (len > sizeof(line) - 1)
2638 len = sizeof(line) - 1;
2639 memcpy(line, p, len);
2641 rtsp_parse_line(header, line);
2645 /* handle sequence number */
2646 c->seq = header->seq;
2648 if (!strcmp(cmd, "DESCRIBE"))
2649 rtsp_cmd_describe(c, url);
2650 else if (!strcmp(cmd, "OPTIONS"))
2651 rtsp_cmd_options(c, url);
2652 else if (!strcmp(cmd, "SETUP"))
2653 rtsp_cmd_setup(c, url, header);
2654 else if (!strcmp(cmd, "PLAY"))
2655 rtsp_cmd_play(c, url, header);
2656 else if (!strcmp(cmd, "PAUSE"))
2657 rtsp_cmd_pause(c, url, header);
2658 else if (!strcmp(cmd, "TEARDOWN"))
2659 rtsp_cmd_teardown(c, url, header);
2661 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2664 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2665 c->pb = NULL; /* safety */
2667 /* XXX: cannot do more */
2670 c->buffer_ptr = c->pb_buffer;
2671 c->buffer_end = c->pb_buffer + len;
2672 c->state = RTSPSTATE_SEND_REPLY;
2676 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2677 struct in_addr my_ip)
2679 AVFormatContext *avc;
2680 AVStream avs[MAX_STREAMS];
2683 avc = av_alloc_format_context();
2687 if (stream->title[0] != 0) {
2688 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2690 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2692 avc->nb_streams = stream->nb_streams;
2693 if (stream->is_multicast) {
2694 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2695 inet_ntoa(stream->multicast_ip),
2696 stream->multicast_port, stream->multicast_ttl);
2699 for(i = 0; i < stream->nb_streams; i++) {
2700 avc->streams[i] = &avs[i];
2701 avc->streams[i]->codec = stream->streams[i]->codec;
2703 *pbuffer = av_mallocz(2048);
2704 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2707 return strlen(*pbuffer);
2710 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2712 // rtsp_reply_header(c, RTSP_STATUS_OK);
2713 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2714 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2715 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2716 url_fprintf(c->pb, "\r\n");
2719 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2725 int content_length, len;
2726 struct sockaddr_in my_addr;
2728 /* find which url is asked */
2729 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2734 for(stream = first_stream; stream != NULL; stream = stream->next) {
2735 if (!stream->is_feed &&
2736 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2737 !strcmp(path, stream->filename)) {
2741 /* no stream found */
2742 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2746 /* prepare the media description in sdp format */
2748 /* get the host IP */
2749 len = sizeof(my_addr);
2750 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2751 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2752 if (content_length < 0) {
2753 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2756 rtsp_reply_header(c, RTSP_STATUS_OK);
2757 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2758 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2759 url_fprintf(c->pb, "\r\n");
2760 put_buffer(c->pb, content, content_length);
2763 static HTTPContext *find_rtp_session(const char *session_id)
2767 if (session_id[0] == '\0')
2770 for(c = first_http_ctx; c != NULL; c = c->next) {
2771 if (!strcmp(c->session_id, session_id))
2777 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2779 RTSPTransportField *th;
2782 for(i=0;i<h->nb_transports;i++) {
2783 th = &h->transports[i];
2784 if (th->protocol == protocol)
2790 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2794 int stream_index, port;
2799 RTSPTransportField *th;
2800 struct sockaddr_in dest_addr;
2801 RTSPActionServerSetup setup;
2803 /* find which url is asked */
2804 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2809 /* now check each stream */
2810 for(stream = first_stream; stream != NULL; stream = stream->next) {
2811 if (!stream->is_feed &&
2812 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2813 /* accept aggregate filenames only if single stream */
2814 if (!strcmp(path, stream->filename)) {
2815 if (stream->nb_streams != 1) {
2816 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2823 for(stream_index = 0; stream_index < stream->nb_streams;
2825 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2826 stream->filename, stream_index);
2827 if (!strcmp(path, buf))
2832 /* no stream found */
2833 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2837 /* generate session id if needed */
2838 if (h->session_id[0] == '\0')
2839 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2840 av_random(&random_state), av_random(&random_state));
2842 /* find rtp session, and create it if none found */
2843 rtp_c = find_rtp_session(h->session_id);
2845 /* always prefer UDP */
2846 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2848 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2850 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2855 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2858 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2862 /* open input stream */
2863 if (open_input_stream(rtp_c, "") < 0) {
2864 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2869 /* test if stream is OK (test needed because several SETUP needs
2870 to be done for a given file) */
2871 if (rtp_c->stream != stream) {
2872 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2876 /* test if stream is already set up */
2877 if (rtp_c->rtp_ctx[stream_index]) {
2878 rtsp_reply_error(c, RTSP_STATUS_STATE);
2882 /* check transport */
2883 th = find_transport(h, rtp_c->rtp_protocol);
2884 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2885 th->client_port_min <= 0)) {
2886 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2890 /* setup default options */
2891 setup.transport_option[0] = '\0';
2892 dest_addr = rtp_c->from_addr;
2893 dest_addr.sin_port = htons(th->client_port_min);
2896 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2897 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2901 /* now everything is OK, so we can send the connection parameters */
2902 rtsp_reply_header(c, RTSP_STATUS_OK);
2904 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2906 switch(rtp_c->rtp_protocol) {
2907 case RTSP_PROTOCOL_RTP_UDP:
2908 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2909 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2910 "client_port=%d-%d;server_port=%d-%d",
2911 th->client_port_min, th->client_port_min + 1,
2914 case RTSP_PROTOCOL_RTP_TCP:
2915 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2916 stream_index * 2, stream_index * 2 + 1);
2921 if (setup.transport_option[0] != '\0')
2922 url_fprintf(c->pb, ";%s", setup.transport_option);
2923 url_fprintf(c->pb, "\r\n");
2926 url_fprintf(c->pb, "\r\n");
2930 /* find an rtp connection by using the session ID. Check consistency
2932 static HTTPContext *find_rtp_session_with_url(const char *url,
2933 const char *session_id)
2941 rtp_c = find_rtp_session(session_id);
2945 /* find which url is asked */
2946 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2950 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2951 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2952 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2953 rtp_c->stream->filename, s);
2954 if(!strncmp(path, buf, sizeof(buf))) {
2955 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2962 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2966 rtp_c = find_rtp_session_with_url(url, h->session_id);
2968 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2972 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2973 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2974 rtp_c->state != HTTPSTATE_READY) {
2975 rtsp_reply_error(c, RTSP_STATUS_STATE);
2980 /* XXX: seek in stream */
2981 if (h->range_start != AV_NOPTS_VALUE) {
2982 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2983 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2987 rtp_c->state = HTTPSTATE_SEND_DATA;
2989 /* now everything is OK, so we can send the connection parameters */
2990 rtsp_reply_header(c, RTSP_STATUS_OK);
2992 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2993 url_fprintf(c->pb, "\r\n");
2996 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3000 rtp_c = find_rtp_session_with_url(url, h->session_id);
3002 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3006 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3007 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3008 rtsp_reply_error(c, RTSP_STATUS_STATE);
3012 rtp_c->state = HTTPSTATE_READY;
3013 rtp_c->first_pts = AV_NOPTS_VALUE;
3014 /* now everything is OK, so we can send the connection parameters */
3015 rtsp_reply_header(c, RTSP_STATUS_OK);
3017 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3018 url_fprintf(c->pb, "\r\n");
3021 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3024 char session_id[32];
3026 rtp_c = find_rtp_session_with_url(url, h->session_id);
3028 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3032 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3034 /* abort the session */
3035 close_connection(rtp_c);
3037 /* now everything is OK, so we can send the connection parameters */
3038 rtsp_reply_header(c, RTSP_STATUS_OK);
3040 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3041 url_fprintf(c->pb, "\r\n");
3045 /********************************************************************/
3048 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3049 FFStream *stream, const char *session_id,
3050 enum RTSPProtocol rtp_protocol)
3052 HTTPContext *c = NULL;
3053 const char *proto_str;
3055 /* XXX: should output a warning page when coming
3056 close to the connection limit */
3057 if (nb_connections >= nb_max_connections)
3060 /* add a new connection */
3061 c = av_mallocz(sizeof(HTTPContext));
3066 c->poll_entry = NULL;
3067 c->from_addr = *from_addr;
3068 c->buffer_size = IOBUFFER_INIT_SIZE;
3069 c->buffer = av_malloc(c->buffer_size);
3074 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3075 c->state = HTTPSTATE_READY;
3076 c->is_packetized = 1;
3077 c->rtp_protocol = rtp_protocol;
3079 /* protocol is shown in statistics */
3080 switch(c->rtp_protocol) {
3081 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3082 proto_str = "MCAST";
3084 case RTSP_PROTOCOL_RTP_UDP:
3087 case RTSP_PROTOCOL_RTP_TCP:
3094 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3095 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3097 current_bandwidth += stream->bandwidth;
3099 c->next = first_http_ctx;
3111 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3112 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3114 static int rtp_new_av_stream(HTTPContext *c,
3115 int stream_index, struct sockaddr_in *dest_addr,
3116 HTTPContext *rtsp_c)
3118 AVFormatContext *ctx;
3121 URLContext *h = NULL;
3124 int max_packet_size;
3126 /* now we can open the relevant output stream */
3127 ctx = av_alloc_format_context();
3130 ctx->oformat = guess_format("rtp", NULL, NULL);
3132 st = av_mallocz(sizeof(AVStream));
3135 st->codec= avcodec_alloc_context();
3136 ctx->nb_streams = 1;
3137 ctx->streams[0] = st;
3139 if (!c->stream->feed ||
3140 c->stream->feed == c->stream)
3141 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3144 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3146 st->priv_data = NULL;
3148 /* build destination RTP address */
3149 ipaddr = inet_ntoa(dest_addr->sin_addr);
3151 switch(c->rtp_protocol) {
3152 case RTSP_PROTOCOL_RTP_UDP:
3153 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3156 /* XXX: also pass as parameter to function ? */
3157 if (c->stream->is_multicast) {
3159 ttl = c->stream->multicast_ttl;
3162 snprintf(ctx->filename, sizeof(ctx->filename),
3163 "rtp://%s:%d?multicast=1&ttl=%d",
3164 ipaddr, ntohs(dest_addr->sin_port), ttl);
3166 snprintf(ctx->filename, sizeof(ctx->filename),
3167 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3170 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3172 c->rtp_handles[stream_index] = h;
3173 max_packet_size = url_get_max_packet_size(h);
3175 case RTSP_PROTOCOL_RTP_TCP:
3178 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3184 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3185 ipaddr, ntohs(dest_addr->sin_port),
3187 c->stream->filename, stream_index, c->protocol);
3189 /* normally, no packets should be output here, but the packet size may be checked */
3190 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3191 /* XXX: close stream */
3194 av_set_parameters(ctx, NULL);
3195 if (av_write_header(ctx) < 0) {
3202 url_close_dyn_buf(ctx->pb, &dummy_buf);
3205 c->rtp_ctx[stream_index] = ctx;
3209 /********************************************************************/
3210 /* ffserver initialization */
3212 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3216 fst = av_mallocz(sizeof(AVStream));
3219 fst->codec= avcodec_alloc_context();
3220 fst->priv_data = av_mallocz(sizeof(FeedData));
3221 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3222 fst->index = stream->nb_streams;
3223 av_set_pts_info(fst, 33, 1, 90000);
3224 stream->streams[stream->nb_streams++] = fst;
3228 /* return the stream number in the feed */
3229 static int add_av_stream(FFStream *feed, AVStream *st)
3232 AVCodecContext *av, *av1;
3236 for(i=0;i<feed->nb_streams;i++) {
3237 st = feed->streams[i];
3239 if (av1->codec_id == av->codec_id &&
3240 av1->codec_type == av->codec_type &&
3241 av1->bit_rate == av->bit_rate) {
3243 switch(av->codec_type) {
3244 case CODEC_TYPE_AUDIO:
3245 if (av1->channels == av->channels &&
3246 av1->sample_rate == av->sample_rate)
3249 case CODEC_TYPE_VIDEO:
3250 if (av1->width == av->width &&
3251 av1->height == av->height &&
3252 av1->time_base.den == av->time_base.den &&
3253 av1->time_base.num == av->time_base.num &&
3254 av1->gop_size == av->gop_size)
3263 fst = add_av_stream1(feed, av);
3266 return feed->nb_streams - 1;
3271 static void remove_stream(FFStream *stream)
3275 while (*ps != NULL) {
3283 /* specific mpeg4 handling : we extract the raw parameters */
3284 static void extract_mpeg4_header(AVFormatContext *infile)
3286 int mpeg4_count, i, size;
3292 for(i=0;i<infile->nb_streams;i++) {
3293 st = infile->streams[i];
3294 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3295 st->codec->extradata_size == 0) {
3302 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3303 while (mpeg4_count > 0) {
3304 if (av_read_packet(infile, &pkt) < 0)
3306 st = infile->streams[pkt.stream_index];
3307 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3308 st->codec->extradata_size == 0) {
3309 av_freep(&st->codec->extradata);
3310 /* fill extradata with the header */
3311 /* XXX: we make hard suppositions here ! */
3313 while (p < pkt.data + pkt.size - 4) {
3314 /* stop when vop header is found */
3315 if (p[0] == 0x00 && p[1] == 0x00 &&
3316 p[2] == 0x01 && p[3] == 0xb6) {
3317 size = p - pkt.data;
3318 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3319 st->codec->extradata = av_malloc(size);
3320 st->codec->extradata_size = size;
3321 memcpy(st->codec->extradata, pkt.data, size);
3328 av_free_packet(&pkt);
3332 /* compute the needed AVStream for each file */
3333 static void build_file_streams(void)
3335 FFStream *stream, *stream_next;
3336 AVFormatContext *infile;
3339 /* gather all streams */
3340 for(stream = first_stream; stream != NULL; stream = stream_next) {
3341 stream_next = stream->next;
3342 if (stream->stream_type == STREAM_TYPE_LIVE &&
3344 /* the stream comes from a file */
3345 /* try to open the file */
3347 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3348 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3349 /* specific case : if transport stream output to RTP,
3350 we use a raw transport stream reader */
3351 stream->ap_in->mpeg2ts_raw = 1;
3352 stream->ap_in->mpeg2ts_compute_pcr = 1;
3355 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3356 stream->ifmt, 0, stream->ap_in)) < 0) {
3357 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3358 /* remove stream (no need to spend more time on it) */
3360 remove_stream(stream);
3362 /* find all the AVStreams inside and reference them in
3364 if (av_find_stream_info(infile) < 0) {
3365 http_log("Could not find codec parameters from '%s'\n",
3366 stream->feed_filename);
3367 av_close_input_file(infile);
3370 extract_mpeg4_header(infile);
3372 for(i=0;i<infile->nb_streams;i++)
3373 add_av_stream1(stream, infile->streams[i]->codec);
3375 av_close_input_file(infile);
3381 /* compute the needed AVStream for each feed */
3382 static void build_feed_streams(void)
3384 FFStream *stream, *feed;
3387 /* gather all streams */
3388 for(stream = first_stream; stream != NULL; stream = stream->next) {
3389 feed = stream->feed;
3391 if (!stream->is_feed) {
3392 /* we handle a stream coming from a feed */
3393 for(i=0;i<stream->nb_streams;i++)
3394 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3399 /* gather all streams */
3400 for(stream = first_stream; stream != NULL; stream = stream->next) {
3401 feed = stream->feed;
3403 if (stream->is_feed) {
3404 for(i=0;i<stream->nb_streams;i++)
3405 stream->feed_streams[i] = i;
3410 /* create feed files if needed */
3411 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3414 if (url_exist(feed->feed_filename)) {
3415 /* See if it matches */
3419 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3420 /* Now see if it matches */
3421 if (s->nb_streams == feed->nb_streams) {
3423 for(i=0;i<s->nb_streams;i++) {
3425 sf = feed->streams[i];
3428 if (sf->index != ss->index ||
3430 printf("Index & Id do not match for stream %d (%s)\n",
3431 i, feed->feed_filename);
3434 AVCodecContext *ccf, *ccs;
3438 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3440 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3441 printf("Codecs do not match for stream %d\n", i);
3443 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3444 printf("Codec bitrates do not match for stream %d\n", i);
3446 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3447 if (CHECK_CODEC(time_base.den) ||
3448 CHECK_CODEC(time_base.num) ||
3449 CHECK_CODEC(width) ||
3450 CHECK_CODEC(height)) {
3451 printf("Codec width, height and framerate do not match for stream %d\n", i);
3454 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3455 if (CHECK_CODEC(sample_rate) ||
3456 CHECK_CODEC(channels) ||
3457 CHECK_CODEC(frame_size)) {
3458 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3462 printf("Unknown codec type\n");
3470 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3471 feed->feed_filename, s->nb_streams, feed->nb_streams);
3473 av_close_input_file(s);
3475 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3476 feed->feed_filename);
3479 if (feed->readonly) {
3480 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3481 feed->feed_filename);
3484 unlink(feed->feed_filename);
3487 if (!url_exist(feed->feed_filename)) {
3488 AVFormatContext s1, *s = &s1;
3490 if (feed->readonly) {
3491 printf("Unable to create feed file '%s' as it is marked readonly\n",
3492 feed->feed_filename);
3496 /* only write the header of the ffm file */
3497 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3498 http_log("Could not open output feed file '%s'\n",
3499 feed->feed_filename);
3502 s->oformat = feed->fmt;
3503 s->nb_streams = feed->nb_streams;
3504 for(i=0;i<s->nb_streams;i++) {
3506 st = feed->streams[i];
3509 av_set_parameters(s, NULL);
3510 if (av_write_header(s) < 0) {
3511 http_log("Container doesn't supports the required parameters\n");
3514 /* XXX: need better api */
3515 av_freep(&s->priv_data);
3518 /* get feed size and write index */
3519 fd = open(feed->feed_filename, O_RDONLY);
3521 http_log("Could not open output feed file '%s'\n",
3522 feed->feed_filename);
3526 feed->feed_write_index = ffm_read_write_index(fd);
3527 feed->feed_size = lseek(fd, 0, SEEK_END);
3528 /* ensure that we do not wrap before the end of file */
3529 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3530 feed->feed_max_size = feed->feed_size;
3536 /* compute the bandwidth used by each stream */
3537 static void compute_bandwidth(void)
3543 for(stream = first_stream; stream != NULL; stream = stream->next) {
3545 for(i=0;i<stream->nb_streams;i++) {
3546 AVStream *st = stream->streams[i];
3547 switch(st->codec->codec_type) {
3548 case CODEC_TYPE_AUDIO:
3549 case CODEC_TYPE_VIDEO:
3550 bandwidth += st->codec->bit_rate;
3556 stream->bandwidth = (bandwidth + 999) / 1000;
3560 static void get_arg(char *buf, int buf_size, const char **pp)
3567 while (isspace(*p)) p++;
3570 if (*p == '\"' || *p == '\'')
3582 if ((q - buf) < buf_size - 1)
3587 if (quote && *p == quote)
3592 /* add a codec and set the default parameters */
3593 static void add_codec(FFStream *stream, AVCodecContext *av)
3597 /* compute default parameters */
3598 switch(av->codec_type) {
3599 case CODEC_TYPE_AUDIO:
3600 if (av->bit_rate == 0)
3601 av->bit_rate = 64000;
3602 if (av->sample_rate == 0)
3603 av->sample_rate = 22050;
3604 if (av->channels == 0)
3607 case CODEC_TYPE_VIDEO:
3608 if (av->bit_rate == 0)
3609 av->bit_rate = 64000;
3610 if (av->time_base.num == 0){
3611 av->time_base.den = 5;
3612 av->time_base.num = 1;
3614 if (av->width == 0 || av->height == 0) {
3618 /* Bitrate tolerance is less for streaming */
3619 if (av->bit_rate_tolerance == 0)
3620 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3621 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3626 if (av->max_qdiff == 0)
3628 av->qcompress = 0.5;
3631 if (!av->nsse_weight)
3632 av->nsse_weight = 8;
3634 av->frame_skip_cmp = FF_CMP_DCTMAX;
3635 av->me_method = ME_EPZS;
3636 av->rc_buffer_aggressivity = 1.0;
3639 av->rc_eq = "tex^qComp";
3640 if (!av->i_quant_factor)
3641 av->i_quant_factor = -0.8;
3642 if (!av->b_quant_factor)
3643 av->b_quant_factor = 1.25;
3644 if (!av->b_quant_offset)
3645 av->b_quant_offset = 1.25;
3646 if (!av->rc_max_rate)
3647 av->rc_max_rate = av->bit_rate * 2;
3649 if (av->rc_max_rate && !av->rc_buffer_size) {
3650 av->rc_buffer_size = av->rc_max_rate;
3659 st = av_mallocz(sizeof(AVStream));
3662 st->codec = avcodec_alloc_context();
3663 stream->streams[stream->nb_streams++] = st;
3664 memcpy(st->codec, av, sizeof(AVCodecContext));
3667 static int opt_audio_codec(const char *arg)
3669 AVCodec *p= avcodec_find_encoder_by_name(arg);
3671 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3672 return CODEC_ID_NONE;
3677 static int opt_video_codec(const char *arg)
3679 AVCodec *p= avcodec_find_encoder_by_name(arg);
3681 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3682 return CODEC_ID_NONE;
3687 /* simplistic plugin support */
3690 static void load_module(const char *filename)
3693 void (*init_func)(void);
3694 dll = dlopen(filename, RTLD_NOW);
3696 fprintf(stderr, "Could not load module '%s' - %s\n",
3697 filename, dlerror());
3701 init_func = dlsym(dll, "ffserver_module_init");
3704 "%s: init function 'ffserver_module_init()' not found\n",
3713 static int opt_default(const char *opt, const char *arg,
3714 AVCodecContext *avctx, int type)
3716 const AVOption *o = NULL;
3717 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3719 o = av_set_string(avctx, opt, arg);
3725 static int parse_ffconfig(const char *filename)
3732 int val, errors, line_num;
3733 FFStream **last_stream, *stream, *redirect;
3734 FFStream **last_feed, *feed;
3735 AVCodecContext audio_enc, video_enc;
3736 int audio_id, video_id;
3738 f = fopen(filename, "r");
3746 first_stream = NULL;
3747 last_stream = &first_stream;
3749 last_feed = &first_feed;
3753 audio_id = CODEC_ID_NONE;
3754 video_id = CODEC_ID_NONE;
3756 if (fgets(line, sizeof(line), f) == NULL)
3762 if (*p == '\0' || *p == '#')
3765 get_arg(cmd, sizeof(cmd), &p);
3767 if (!strcasecmp(cmd, "Port")) {
3768 get_arg(arg, sizeof(arg), &p);
3770 if (val < 1 || val > 65536) {
3771 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3772 filename, line_num, arg);
3775 my_http_addr.sin_port = htons(val);
3776 } else if (!strcasecmp(cmd, "BindAddress")) {
3777 get_arg(arg, sizeof(arg), &p);
3778 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3779 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3780 filename, line_num, arg);
3783 } else if (!strcasecmp(cmd, "NoDaemon")) {
3784 ffserver_daemon = 0;
3785 } else if (!strcasecmp(cmd, "RTSPPort")) {
3786 get_arg(arg, sizeof(arg), &p);
3788 if (val < 1 || val > 65536) {
3789 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3790 filename, line_num, arg);
3793 my_rtsp_addr.sin_port = htons(atoi(arg));
3794 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3795 get_arg(arg, sizeof(arg), &p);
3796 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3797 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3798 filename, line_num, arg);
3801 } else if (!strcasecmp(cmd, "MaxClients")) {
3802 get_arg(arg, sizeof(arg), &p);
3804 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3805 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3806 filename, line_num, arg);
3809 nb_max_connections = val;
3811 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3813 get_arg(arg, sizeof(arg), &p);
3815 if (llval < 10 || llval > 10000000) {
3816 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3817 filename, line_num, arg);
3820 max_bandwidth = llval;
3821 } else if (!strcasecmp(cmd, "CustomLog")) {
3822 if (!ffserver_debug)
3823 get_arg(logfilename, sizeof(logfilename), &p);
3824 } else if (!strcasecmp(cmd, "<Feed")) {
3825 /*********************************************/
3826 /* Feed related options */
3828 if (stream || feed) {
3829 fprintf(stderr, "%s:%d: Already in a tag\n",
3830 filename, line_num);
3832 feed = av_mallocz(sizeof(FFStream));
3833 /* add in stream list */
3834 *last_stream = feed;
3835 last_stream = &feed->next;
3836 /* add in feed list */
3838 last_feed = &feed->next_feed;
3840 get_arg(feed->filename, sizeof(feed->filename), &p);
3841 q = strrchr(feed->filename, '>');
3844 feed->fmt = guess_format("ffm", NULL, NULL);
3845 /* defaut feed file */
3846 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3847 "/tmp/%s.ffm", feed->filename);
3848 feed->feed_max_size = 5 * 1024 * 1024;
3850 feed->feed = feed; /* self feeding :-) */
3852 } else if (!strcasecmp(cmd, "Launch")) {
3856 feed->child_argv = av_mallocz(64 * sizeof(char *));
3858 for (i = 0; i < 62; i++) {
3859 get_arg(arg, sizeof(arg), &p);
3863 feed->child_argv[i] = av_strdup(arg);
3866 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3868 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3870 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3871 inet_ntoa(my_http_addr.sin_addr),
3872 ntohs(my_http_addr.sin_port), feed->filename);
3877 fprintf(stdout, "Launch commandline: ");
3878 for (j = 0; j <= i; j++)
3879 fprintf(stdout, "%s ", feed->child_argv[j]);
3880 fprintf(stdout, "\n");
3883 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3885 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3887 } else if (stream) {
3888 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3890 } else if (!strcasecmp(cmd, "File")) {
3892 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3894 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3895 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3900 get_arg(arg, sizeof(arg), &p);
3902 fsize = strtod(p1, &p1);
3903 switch(toupper(*p1)) {
3908 fsize *= 1024 * 1024;
3911 fsize *= 1024 * 1024 * 1024;
3914 feed->feed_max_size = (int64_t)fsize;
3916 } else if (!strcasecmp(cmd, "</Feed>")) {
3918 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3919 filename, line_num);
3923 } else if (!strcasecmp(cmd, "<Stream")) {
3924 /*********************************************/
3925 /* Stream related options */
3927 if (stream || feed) {
3928 fprintf(stderr, "%s:%d: Already in a tag\n",
3929 filename, line_num);
3931 const AVClass *class;
3932 stream = av_mallocz(sizeof(FFStream));
3933 *last_stream = stream;
3934 last_stream = &stream->next;
3936 get_arg(stream->filename, sizeof(stream->filename), &p);
3937 q = strrchr(stream->filename, '>');
3940 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3941 /* fetch avclass so AVOption works
3942 * FIXME try to use avcodec_get_context_defaults2
3943 * without changing defaults too much */
3944 avcodec_get_context_defaults(&video_enc);
3945 class = video_enc.av_class;
3946 memset(&audio_enc, 0, sizeof(AVCodecContext));
3947 memset(&video_enc, 0, sizeof(AVCodecContext));
3948 audio_enc.av_class = class;
3949 video_enc.av_class = class;
3950 audio_id = CODEC_ID_NONE;
3951 video_id = CODEC_ID_NONE;
3953 audio_id = stream->fmt->audio_codec;
3954 video_id = stream->fmt->video_codec;
3957 } else if (!strcasecmp(cmd, "Feed")) {
3958 get_arg(arg, sizeof(arg), &p);
3963 while (sfeed != NULL) {
3964 if (!strcmp(sfeed->filename, arg))
3966 sfeed = sfeed->next_feed;
3969 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3970 filename, line_num, arg);
3972 stream->feed = sfeed;
3974 } else if (!strcasecmp(cmd, "Format")) {
3975 get_arg(arg, sizeof(arg), &p);
3977 if (!strcmp(arg, "status")) {
3978 stream->stream_type = STREAM_TYPE_STATUS;
3981 stream->stream_type = STREAM_TYPE_LIVE;
3982 /* jpeg cannot be used here, so use single frame jpeg */
3983 if (!strcmp(arg, "jpeg"))
3984 strcpy(arg, "mjpeg");
3985 stream->fmt = guess_stream_format(arg, NULL, NULL);
3987 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3988 filename, line_num, arg);
3993 audio_id = stream->fmt->audio_codec;
3994 video_id = stream->fmt->video_codec;
3997 } else if (!strcasecmp(cmd, "InputFormat")) {
3998 get_arg(arg, sizeof(arg), &p);
3999 stream->ifmt = av_find_input_format(arg);
4000 if (!stream->ifmt) {
4001 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4002 filename, line_num, arg);
4004 } else if (!strcasecmp(cmd, "FaviconURL")) {
4005 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4006 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4008 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4009 filename, line_num);
4012 } else if (!strcasecmp(cmd, "Author")) {
4014 get_arg(stream->author, sizeof(stream->author), &p);
4015 } else if (!strcasecmp(cmd, "Comment")) {
4017 get_arg(stream->comment, sizeof(stream->comment), &p);
4018 } else if (!strcasecmp(cmd, "Copyright")) {
4020 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4021 } else if (!strcasecmp(cmd, "Title")) {
4023 get_arg(stream->title, sizeof(stream->title), &p);
4024 } else if (!strcasecmp(cmd, "Preroll")) {
4025 get_arg(arg, sizeof(arg), &p);
4027 stream->prebuffer = atof(arg) * 1000;
4028 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4030 stream->send_on_key = 1;
4031 } else if (!strcasecmp(cmd, "AudioCodec")) {
4032 get_arg(arg, sizeof(arg), &p);
4033 audio_id = opt_audio_codec(arg);
4034 if (audio_id == CODEC_ID_NONE) {
4035 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4036 filename, line_num, arg);
4039 } else if (!strcasecmp(cmd, "VideoCodec")) {
4040 get_arg(arg, sizeof(arg), &p);
4041 video_id = opt_video_codec(arg);
4042 if (video_id == CODEC_ID_NONE) {
4043 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4044 filename, line_num, arg);
4047 } else if (!strcasecmp(cmd, "MaxTime")) {
4048 get_arg(arg, sizeof(arg), &p);
4050 stream->max_time = atof(arg) * 1000;
4051 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4052 get_arg(arg, sizeof(arg), &p);
4054 audio_enc.bit_rate = atoi(arg) * 1000;
4055 } else if (!strcasecmp(cmd, "AudioChannels")) {
4056 get_arg(arg, sizeof(arg), &p);
4058 audio_enc.channels = atoi(arg);
4059 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4060 get_arg(arg, sizeof(arg), &p);
4062 audio_enc.sample_rate = atoi(arg);
4063 } else if (!strcasecmp(cmd, "AudioQuality")) {
4064 get_arg(arg, sizeof(arg), &p);
4066 // audio_enc.quality = atof(arg) * 1000;
4068 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4070 int minrate, maxrate;
4072 get_arg(arg, sizeof(arg), &p);
4074 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4075 video_enc.rc_min_rate = minrate * 1000;
4076 video_enc.rc_max_rate = maxrate * 1000;
4078 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4079 filename, line_num, arg);
4083 } else if (!strcasecmp(cmd, "Debug")) {
4085 get_arg(arg, sizeof(arg), &p);
4086 video_enc.debug = strtol(arg,0,0);
4088 } else if (!strcasecmp(cmd, "Strict")) {
4090 get_arg(arg, sizeof(arg), &p);
4091 video_enc.strict_std_compliance = atoi(arg);
4093 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4095 get_arg(arg, sizeof(arg), &p);
4096 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4098 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4100 get_arg(arg, sizeof(arg), &p);
4101 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4103 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4104 get_arg(arg, sizeof(arg), &p);
4106 video_enc.bit_rate = atoi(arg) * 1000;
4108 } else if (!strcasecmp(cmd, "VideoSize")) {
4109 get_arg(arg, sizeof(arg), &p);
4111 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4112 if ((video_enc.width % 16) != 0 ||
4113 (video_enc.height % 16) != 0) {
4114 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4115 filename, line_num);
4119 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4120 get_arg(arg, sizeof(arg), &p);
4122 AVRational frame_rate;
4123 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4124 fprintf(stderr, "Incorrect frame rate\n");
4127 video_enc.time_base.num = frame_rate.den;
4128 video_enc.time_base.den = frame_rate.num;
4131 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4132 get_arg(arg, sizeof(arg), &p);
4134 video_enc.gop_size = atoi(arg);
4135 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4137 video_enc.gop_size = 1;
4138 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4140 video_enc.mb_decision = FF_MB_DECISION_BITS;
4141 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4143 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4144 video_enc.flags |= CODEC_FLAG_4MV;
4146 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4147 !strcasecmp(cmd, "AVOptionAudio")) {
4149 AVCodecContext *avctx;
4151 get_arg(arg, sizeof(arg), &p);
4152 get_arg(arg2, sizeof(arg2), &p);
4153 if (!strcasecmp(cmd, "AVOptionVideo")) {
4155 type = AV_OPT_FLAG_VIDEO_PARAM;
4158 type = AV_OPT_FLAG_AUDIO_PARAM;
4160 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4161 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4164 } else if (!strcasecmp(cmd, "VideoTag")) {
4165 get_arg(arg, sizeof(arg), &p);
4166 if ((strlen(arg) == 4) && stream)
4167 video_enc.codec_tag = ff_get_fourcc(arg);
4168 } else if (!strcasecmp(cmd, "BitExact")) {
4170 video_enc.flags |= CODEC_FLAG_BITEXACT;
4171 } else if (!strcasecmp(cmd, "DctFastint")) {
4173 video_enc.dct_algo = FF_DCT_FASTINT;
4174 } else if (!strcasecmp(cmd, "IdctSimple")) {
4176 video_enc.idct_algo = FF_IDCT_SIMPLE;
4177 } else if (!strcasecmp(cmd, "Qscale")) {
4178 get_arg(arg, sizeof(arg), &p);
4180 video_enc.flags |= CODEC_FLAG_QSCALE;
4181 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4183 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4184 get_arg(arg, sizeof(arg), &p);
4186 video_enc.max_qdiff = atoi(arg);
4187 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4188 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4189 filename, line_num);
4193 } else if (!strcasecmp(cmd, "VideoQMax")) {
4194 get_arg(arg, sizeof(arg), &p);
4196 video_enc.qmax = atoi(arg);
4197 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4198 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4199 filename, line_num);
4203 } else if (!strcasecmp(cmd, "VideoQMin")) {
4204 get_arg(arg, sizeof(arg), &p);
4206 video_enc.qmin = atoi(arg);
4207 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4208 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4209 filename, line_num);
4213 } else if (!strcasecmp(cmd, "LumaElim")) {
4214 get_arg(arg, sizeof(arg), &p);
4216 video_enc.luma_elim_threshold = atoi(arg);
4217 } else if (!strcasecmp(cmd, "ChromaElim")) {
4218 get_arg(arg, sizeof(arg), &p);
4220 video_enc.chroma_elim_threshold = atoi(arg);
4221 } else if (!strcasecmp(cmd, "LumiMask")) {
4222 get_arg(arg, sizeof(arg), &p);
4224 video_enc.lumi_masking = atof(arg);
4225 } else if (!strcasecmp(cmd, "DarkMask")) {
4226 get_arg(arg, sizeof(arg), &p);
4228 video_enc.dark_masking = atof(arg);
4229 } else if (!strcasecmp(cmd, "NoVideo")) {
4230 video_id = CODEC_ID_NONE;
4231 } else if (!strcasecmp(cmd, "NoAudio")) {
4232 audio_id = CODEC_ID_NONE;
4233 } else if (!strcasecmp(cmd, "ACL")) {
4236 get_arg(arg, sizeof(arg), &p);
4237 if (strcasecmp(arg, "allow") == 0)
4238 acl.action = IP_ALLOW;
4239 else if (strcasecmp(arg, "deny") == 0)
4240 acl.action = IP_DENY;
4242 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4243 filename, line_num, arg);
4247 get_arg(arg, sizeof(arg), &p);
4249 if (resolve_host(&acl.first, arg) != 0) {
4250 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4251 filename, line_num, arg);
4254 acl.last = acl.first;
4256 get_arg(arg, sizeof(arg), &p);
4259 if (resolve_host(&acl.last, arg) != 0) {
4260 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4261 filename, line_num, arg);
4267 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4268 IPAddressACL **naclp = 0;
4274 naclp = &stream->acl;
4278 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4279 filename, line_num);
4285 naclp = &(*naclp)->next;
4290 } else if (!strcasecmp(cmd, "RTSPOption")) {
4291 get_arg(arg, sizeof(arg), &p);
4293 av_freep(&stream->rtsp_option);
4294 stream->rtsp_option = av_strdup(arg);
4296 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4297 get_arg(arg, sizeof(arg), &p);
4299 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4300 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4301 filename, line_num, arg);
4304 stream->is_multicast = 1;
4305 stream->loop = 1; /* default is looping */
4307 } else if (!strcasecmp(cmd, "MulticastPort")) {
4308 get_arg(arg, sizeof(arg), &p);
4310 stream->multicast_port = atoi(arg);
4311 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4312 get_arg(arg, sizeof(arg), &p);
4314 stream->multicast_ttl = atoi(arg);
4315 } else if (!strcasecmp(cmd, "NoLoop")) {
4318 } else if (!strcasecmp(cmd, "</Stream>")) {
4320 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4321 filename, line_num);
4324 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4325 if (audio_id != CODEC_ID_NONE) {
4326 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4327 audio_enc.codec_id = audio_id;
4328 add_codec(stream, &audio_enc);
4330 if (video_id != CODEC_ID_NONE) {
4331 video_enc.codec_type = CODEC_TYPE_VIDEO;
4332 video_enc.codec_id = video_id;
4333 add_codec(stream, &video_enc);
4338 } else if (!strcasecmp(cmd, "<Redirect")) {
4339 /*********************************************/
4341 if (stream || feed || redirect) {
4342 fprintf(stderr, "%s:%d: Already in a tag\n",
4343 filename, line_num);
4346 redirect = av_mallocz(sizeof(FFStream));
4347 *last_stream = redirect;
4348 last_stream = &redirect->next;
4350 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4351 q = strrchr(redirect->filename, '>');
4354 redirect->stream_type = STREAM_TYPE_REDIRECT;
4356 } else if (!strcasecmp(cmd, "URL")) {
4358 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4359 } else if (!strcasecmp(cmd, "</Redirect>")) {
4361 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4362 filename, line_num);
4365 if (!redirect->feed_filename[0]) {
4366 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4367 filename, line_num);
4372 } else if (!strcasecmp(cmd, "LoadModule")) {
4373 get_arg(arg, sizeof(arg), &p);
4377 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4378 filename, line_num, arg);
4382 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4383 filename, line_num, cmd);
4395 static void handle_child_exit(int sig)
4400 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4403 for (feed = first_feed; feed; feed = feed->next) {
4404 if (feed->pid == pid) {
4405 int uptime = time(0) - feed->pid_start;
4408 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4411 /* Turn off any more restarts */
4412 feed->child_argv = 0;
4417 need_to_start_children = 1;
4420 static void opt_debug()
4423 ffserver_daemon = 0;
4424 logfilename[0] = '-';
4427 static void opt_show_help(void)
4429 printf("usage: ffserver [options]\n"
4430 "Hyper fast multi format Audio/Video streaming server\n");
4432 show_help_options(options, "Main options:\n", 0, 0);
4435 static const OptionDef options[] = {
4436 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4437 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4438 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4439 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4440 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4441 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4442 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4446 int main(int argc, char **argv)
4448 struct sigaction sigact;
4454 config_filename = "/etc/ffserver.conf";
4456 my_program_name = argv[0];
4457 my_program_dir = getcwd(0, 0);
4458 ffserver_daemon = 1;
4460 parse_options(argc, argv, options, NULL);
4462 unsetenv("http_proxy"); /* Kill the http_proxy */
4464 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4466 nb_max_connections = 5;
4467 max_bandwidth = 1000;
4468 first_stream = NULL;
4470 memset(&sigact, 0, sizeof(sigact));
4471 sigact.sa_handler = handle_child_exit;
4472 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4473 sigaction(SIGCHLD, &sigact, 0);
4475 if (parse_ffconfig(config_filename) < 0) {
4476 fprintf(stderr, "Incorrect config file - exiting.\n");
4480 build_file_streams();
4482 build_feed_streams();
4484 compute_bandwidth();
4486 /* put the process in background and detach it from its TTY */
4487 if (ffserver_daemon) {
4494 } else if (pid > 0) {
4502 open("/dev/null", O_RDWR);
4503 if (strcmp(logfilename, "-") != 0) {
4513 signal(SIGPIPE, SIG_IGN);
4515 /* open log file if needed */
4516 if (logfilename[0] != '\0') {
4517 if (!strcmp(logfilename, "-"))
4520 logfile = fopen(logfilename, "a");
4523 if (http_server() < 0) {
4524 http_log("Could not start server\n");