2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
28 #include "libavutil/random.h"
29 #include "libavutil/avstring.h"
30 #include "libavformat/avformat.h"
31 #include "libavformat/network.h"
32 #include "libavformat/os_support.h"
33 #include "libavformat/rtp.h"
34 #include "libavformat/rtsp.h"
35 #include "libavcodec/opt.h"
39 #include <sys/ioctl.h>
45 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
57 const char program_name[] = "FFserver";
58 const int program_birth_year = 2000;
60 static const OptionDef options[];
62 /* maximum number of simultaneous HTTP connections */
63 #define HTTP_MAX_CONNECTIONS 2000
66 HTTPSTATE_WAIT_REQUEST,
67 HTTPSTATE_SEND_HEADER,
68 HTTPSTATE_SEND_DATA_HEADER,
69 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
70 HTTPSTATE_SEND_DATA_TRAILER,
71 HTTPSTATE_RECEIVE_DATA,
72 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
75 RTSPSTATE_WAIT_REQUEST,
77 RTSPSTATE_SEND_PACKET,
80 static const char *http_state[] = {
96 #define IOBUFFER_INIT_SIZE 8192
98 /* timeouts are in ms */
99 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
100 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
102 #define SYNC_TIMEOUT (10 * 1000)
105 int64_t count1, count2;
106 int64_t time1, time2;
109 /* context associated with one connection */
110 typedef struct HTTPContext {
111 enum HTTPState state;
112 int fd; /* socket file descriptor */
113 struct sockaddr_in from_addr; /* origin */
114 struct pollfd *poll_entry; /* used when polling */
116 uint8_t *buffer_ptr, *buffer_end;
119 struct HTTPContext *next;
120 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
124 /* input format handling */
125 AVFormatContext *fmt_in;
126 int64_t start_time; /* In milliseconds - this wraps fairly often */
127 int64_t first_pts; /* initial pts value */
128 int64_t cur_pts; /* current pts value from the stream in us */
129 int64_t cur_frame_duration; /* duration of the current frame in us */
130 int cur_frame_bytes; /* output frame size, needed to compute
131 the time at which we send each
133 int pts_stream_index; /* stream we choose as clock reference */
134 int64_t cur_clock; /* current clock reference value in us */
135 /* output format handling */
136 struct FFStream *stream;
137 /* -1 is invalid stream */
138 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
139 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
141 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
142 int last_packet_sent; /* true if last data packet was sent */
144 DataRateData datarate;
151 int is_packetized; /* if true, the stream is packetized */
152 int packet_stream_index; /* current stream for output in state machine */
154 /* RTSP state specific */
155 uint8_t *pb_buffer; /* XXX: use that in all the code */
157 int seq; /* RTSP sequence number */
159 /* RTP state specific */
160 enum RTSPProtocol rtp_protocol;
161 char session_id[32]; /* session id */
162 AVFormatContext *rtp_ctx[MAX_STREAMS];
164 /* RTP/UDP specific */
165 URLContext *rtp_handles[MAX_STREAMS];
167 /* RTP/TCP specific */
168 struct HTTPContext *rtsp_c;
169 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
172 /* each generated stream is described here */
176 STREAM_TYPE_REDIRECT,
179 enum IPAddressAction {
184 typedef struct IPAddressACL {
185 struct IPAddressACL *next;
186 enum IPAddressAction action;
187 /* These are in host order */
188 struct in_addr first;
192 /* description of each stream of the ffserver.conf file */
193 typedef struct FFStream {
194 enum StreamType stream_type;
195 char filename[1024]; /* stream filename */
196 struct FFStream *feed; /* feed we are using (can be null if
198 AVFormatParameters *ap_in; /* input parameters */
199 AVInputFormat *ifmt; /* if non NULL, force input format */
203 int prebuffer; /* Number of millseconds early to start */
204 int64_t max_time; /* Number of milliseconds to run */
206 AVStream *streams[MAX_STREAMS];
207 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
208 char feed_filename[1024]; /* file name of the feed storage, or
209 input file name for a stream */
214 pid_t pid; /* Of ffmpeg process */
215 time_t pid_start; /* Of ffmpeg process */
217 struct FFStream *next;
218 unsigned bandwidth; /* bandwidth, in kbits/s */
221 /* multicast specific */
223 struct in_addr multicast_ip;
224 int multicast_port; /* first port used for multicast */
226 int loop; /* if true, send the stream in loops (only meaningful if file) */
229 int feed_opened; /* true if someone is writing to the feed */
230 int is_feed; /* true if it is a feed */
231 int readonly; /* True if writing is prohibited to the file */
233 int64_t bytes_served;
234 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
235 int64_t feed_write_index; /* current write position in feed (it wraps around) */
236 int64_t feed_size; /* current size of feed */
237 struct FFStream *next_feed;
240 typedef struct FeedData {
241 long long data_count;
242 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
245 static struct sockaddr_in my_http_addr;
246 static struct sockaddr_in my_rtsp_addr;
248 static char logfilename[1024];
249 static HTTPContext *first_http_ctx;
250 static FFStream *first_feed; /* contains only feeds */
251 static FFStream *first_stream; /* contains all streams, including feeds */
253 static void new_connection(int server_fd, int is_rtsp);
254 static void close_connection(HTTPContext *c);
257 static int handle_connection(HTTPContext *c);
258 static int http_parse_request(HTTPContext *c);
259 static int http_send_data(HTTPContext *c);
260 static void compute_status(HTTPContext *c);
261 static int open_input_stream(HTTPContext *c, const char *info);
262 static int http_start_receive_data(HTTPContext *c);
263 static int http_receive_data(HTTPContext *c);
266 static int rtsp_parse_request(HTTPContext *c);
267 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
268 static void rtsp_cmd_options(HTTPContext *c, const char *url);
269 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
270 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
271 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
275 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
276 struct in_addr my_ip);
279 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
280 FFStream *stream, const char *session_id,
281 enum RTSPProtocol rtp_protocol);
282 static int rtp_new_av_stream(HTTPContext *c,
283 int stream_index, struct sockaddr_in *dest_addr,
284 HTTPContext *rtsp_c);
286 static const char *my_program_name;
287 static const char *my_program_dir;
289 static const char *config_filename;
290 static int ffserver_debug;
291 static int ffserver_daemon;
292 static int no_launch;
293 static int need_to_start_children;
295 static int nb_max_connections = 5;
296 static int nb_connections;
298 static uint64_t max_bandwidth = 1000;
299 static uint64_t current_bandwidth;
301 static int64_t cur_time; // Making this global saves on passing it around everywhere
303 static AVRandomState random_state;
305 static FILE *logfile = NULL;
307 static char *ctime1(char *buf2)
315 p = buf2 + strlen(p) - 1;
321 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
323 static int print_prefix = 1;
331 fprintf(logfile, "%s ", buf);
333 print_prefix = strstr(fmt, "\n") != NULL;
334 vfprintf(logfile, fmt, ap);
340 static void log_connection(HTTPContext *c)
345 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
346 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
347 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
350 static void update_datarate(DataRateData *drd, int64_t count)
352 if (!drd->time1 && !drd->count1) {
353 drd->time1 = drd->time2 = cur_time;
354 drd->count1 = drd->count2 = count;
355 } else if (cur_time - drd->time2 > 5000) {
356 drd->time1 = drd->time2;
357 drd->count1 = drd->count2;
358 drd->time2 = cur_time;
363 /* In bytes per second */
364 static int compute_datarate(DataRateData *drd, int64_t count)
366 if (cur_time == drd->time1)
369 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
373 static void start_children(FFStream *feed)
378 for (; feed; feed = feed->next) {
379 if (feed->child_argv && !feed->pid) {
380 feed->pid_start = time(0);
385 http_log("Unable to create children\n");
394 for (i = 3; i < 256; i++)
397 if (!ffserver_debug) {
398 i = open("/dev/null", O_RDWR);
407 av_strlcpy(pathname, my_program_name, sizeof(pathname));
409 slash = strrchr(pathname, '/');
414 strcpy(slash, "ffmpeg");
416 /* This is needed to make relative pathnames work */
417 chdir(my_program_dir);
419 signal(SIGPIPE, SIG_DFL);
421 execvp(pathname, feed->child_argv);
429 /* open a listening socket */
430 static int socket_open_listen(struct sockaddr_in *my_addr)
434 server_fd = socket(AF_INET,SOCK_STREAM,0);
441 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
443 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
445 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
447 closesocket(server_fd);
451 if (listen (server_fd, 5) < 0) {
453 closesocket(server_fd);
456 ff_socket_nonblock(server_fd, 1);
461 /* start all multicast streams */
462 static void start_multicast(void)
467 struct sockaddr_in dest_addr;
468 int default_port, stream_index;
471 for(stream = first_stream; stream != NULL; stream = stream->next) {
472 if (stream->is_multicast) {
473 /* open the RTP connection */
474 snprintf(session_id, sizeof(session_id), "%08x%08x",
475 av_random(&random_state), av_random(&random_state));
477 /* choose a port if none given */
478 if (stream->multicast_port == 0) {
479 stream->multicast_port = default_port;
483 dest_addr.sin_family = AF_INET;
484 dest_addr.sin_addr = stream->multicast_ip;
485 dest_addr.sin_port = htons(stream->multicast_port);
487 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
488 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
492 if (open_input_stream(rtp_c, "") < 0) {
493 http_log("Could not open input stream for stream '%s'\n",
498 /* open each RTP stream */
499 for(stream_index = 0; stream_index < stream->nb_streams;
501 dest_addr.sin_port = htons(stream->multicast_port +
503 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
504 http_log("Could not open output stream '%s/streamid=%d'\n",
505 stream->filename, stream_index);
510 /* change state to send data */
511 rtp_c->state = HTTPSTATE_SEND_DATA;
516 /* main loop of the http server */
517 static int http_server(void)
519 int server_fd = 0, rtsp_server_fd = 0;
520 int ret, delay, delay1;
521 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
522 HTTPContext *c, *c_next;
524 if (my_http_addr.sin_port) {
525 server_fd = socket_open_listen(&my_http_addr);
530 if (my_rtsp_addr.sin_port) {
531 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
532 if (rtsp_server_fd < 0)
536 if (!rtsp_server_fd && !server_fd) {
537 http_log("HTTP and RTSP disabled.\n");
541 http_log("ffserver started.\n");
543 start_children(first_feed);
545 first_http_ctx = NULL;
551 poll_entry = poll_table;
553 poll_entry->fd = server_fd;
554 poll_entry->events = POLLIN;
557 if (rtsp_server_fd) {
558 poll_entry->fd = rtsp_server_fd;
559 poll_entry->events = POLLIN;
563 /* wait for events on each HTTP handle */
570 case HTTPSTATE_SEND_HEADER:
571 case RTSPSTATE_SEND_REPLY:
572 case RTSPSTATE_SEND_PACKET:
573 c->poll_entry = poll_entry;
575 poll_entry->events = POLLOUT;
578 case HTTPSTATE_SEND_DATA_HEADER:
579 case HTTPSTATE_SEND_DATA:
580 case HTTPSTATE_SEND_DATA_TRAILER:
581 if (!c->is_packetized) {
582 /* for TCP, we output as much as we can (may need to put a limit) */
583 c->poll_entry = poll_entry;
585 poll_entry->events = POLLOUT;
588 /* when ffserver is doing the timing, we work by
589 looking at which packet need to be sent every
591 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
596 case HTTPSTATE_WAIT_REQUEST:
597 case HTTPSTATE_RECEIVE_DATA:
598 case HTTPSTATE_WAIT_FEED:
599 case RTSPSTATE_WAIT_REQUEST:
600 /* need to catch errors */
601 c->poll_entry = poll_entry;
603 poll_entry->events = POLLIN;/* Maybe this will work */
607 c->poll_entry = NULL;
613 /* wait for an event on one connection. We poll at least every
614 second to handle timeouts */
616 ret = poll(poll_table, poll_entry - poll_table, delay);
617 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
618 ff_neterrno() != FF_NETERROR(EINTR))
622 cur_time = av_gettime() / 1000;
624 if (need_to_start_children) {
625 need_to_start_children = 0;
626 start_children(first_feed);
629 /* now handle the events */
630 for(c = first_http_ctx; c != NULL; c = c_next) {
632 if (handle_connection(c) < 0) {
633 /* close and free the connection */
639 poll_entry = poll_table;
641 /* new HTTP connection request ? */
642 if (poll_entry->revents & POLLIN)
643 new_connection(server_fd, 0);
646 if (rtsp_server_fd) {
647 /* new RTSP connection request ? */
648 if (poll_entry->revents & POLLIN)
649 new_connection(rtsp_server_fd, 1);
654 /* start waiting for a new HTTP/RTSP request */
655 static void start_wait_request(HTTPContext *c, int is_rtsp)
657 c->buffer_ptr = c->buffer;
658 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
661 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
662 c->state = RTSPSTATE_WAIT_REQUEST;
664 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
665 c->state = HTTPSTATE_WAIT_REQUEST;
669 static void new_connection(int server_fd, int is_rtsp)
671 struct sockaddr_in from_addr;
673 HTTPContext *c = NULL;
675 len = sizeof(from_addr);
676 fd = accept(server_fd, (struct sockaddr *)&from_addr,
679 http_log("error during accept %s\n", strerror(errno));
682 ff_socket_nonblock(fd, 1);
684 /* XXX: should output a warning page when coming
685 close to the connection limit */
686 if (nb_connections >= nb_max_connections)
689 /* add a new connection */
690 c = av_mallocz(sizeof(HTTPContext));
695 c->poll_entry = NULL;
696 c->from_addr = from_addr;
697 c->buffer_size = IOBUFFER_INIT_SIZE;
698 c->buffer = av_malloc(c->buffer_size);
702 c->next = first_http_ctx;
706 start_wait_request(c, is_rtsp);
718 static void close_connection(HTTPContext *c)
720 HTTPContext **cp, *c1;
722 AVFormatContext *ctx;
726 /* remove connection from list */
727 cp = &first_http_ctx;
728 while ((*cp) != NULL) {
736 /* remove references, if any (XXX: do it faster) */
737 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
742 /* remove connection associated resources */
746 /* close each frame parser */
747 for(i=0;i<c->fmt_in->nb_streams;i++) {
748 st = c->fmt_in->streams[i];
749 if (st->codec->codec)
750 avcodec_close(st->codec);
752 av_close_input_file(c->fmt_in);
755 /* free RTP output streams if any */
758 nb_streams = c->stream->nb_streams;
760 for(i=0;i<nb_streams;i++) {
763 av_write_trailer(ctx);
766 h = c->rtp_handles[i];
773 if (!c->last_packet_sent) {
776 if (url_open_dyn_buf(&ctx->pb) >= 0) {
777 av_write_trailer(ctx);
778 av_freep(&c->pb_buffer);
779 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
784 for(i=0; i<ctx->nb_streams; i++)
785 av_free(ctx->streams[i]);
787 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
788 current_bandwidth -= c->stream->bandwidth;
790 /* signal that there is no feed if we are the feeder socket */
791 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
792 c->stream->feed_opened = 0;
796 av_freep(&c->pb_buffer);
797 av_freep(&c->packet_buffer);
803 static int handle_connection(HTTPContext *c)
808 case HTTPSTATE_WAIT_REQUEST:
809 case RTSPSTATE_WAIT_REQUEST:
811 if ((c->timeout - cur_time) < 0)
813 if (c->poll_entry->revents & (POLLERR | POLLHUP))
816 /* no need to read if no events */
817 if (!(c->poll_entry->revents & POLLIN))
821 len = recv(c->fd, c->buffer_ptr, 1, 0);
823 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
824 ff_neterrno() != FF_NETERROR(EINTR))
826 } else if (len == 0) {
829 /* search for end of request. */
831 c->buffer_ptr += len;
833 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
834 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
835 /* request found : parse it and reply */
836 if (c->state == HTTPSTATE_WAIT_REQUEST) {
837 ret = http_parse_request(c);
839 ret = rtsp_parse_request(c);
843 } else if (ptr >= c->buffer_end) {
844 /* request too long: cannot do anything */
846 } else goto read_loop;
850 case HTTPSTATE_SEND_HEADER:
851 if (c->poll_entry->revents & (POLLERR | POLLHUP))
854 /* no need to write if no events */
855 if (!(c->poll_entry->revents & POLLOUT))
857 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
859 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
860 ff_neterrno() != FF_NETERROR(EINTR)) {
861 /* error : close connection */
862 av_freep(&c->pb_buffer);
866 c->buffer_ptr += len;
868 c->stream->bytes_served += len;
869 c->data_count += len;
870 if (c->buffer_ptr >= c->buffer_end) {
871 av_freep(&c->pb_buffer);
875 /* all the buffer was sent : synchronize to the incoming stream */
876 c->state = HTTPSTATE_SEND_DATA_HEADER;
877 c->buffer_ptr = c->buffer_end = c->buffer;
882 case HTTPSTATE_SEND_DATA:
883 case HTTPSTATE_SEND_DATA_HEADER:
884 case HTTPSTATE_SEND_DATA_TRAILER:
885 /* for packetized output, we consider we can always write (the
886 input streams sets the speed). It may be better to verify
887 that we do not rely too much on the kernel queues */
888 if (!c->is_packetized) {
889 if (c->poll_entry->revents & (POLLERR | POLLHUP))
892 /* no need to read if no events */
893 if (!(c->poll_entry->revents & POLLOUT))
896 if (http_send_data(c) < 0)
898 /* close connection if trailer sent */
899 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
902 case HTTPSTATE_RECEIVE_DATA:
903 /* no need to read if no events */
904 if (c->poll_entry->revents & (POLLERR | POLLHUP))
906 if (!(c->poll_entry->revents & POLLIN))
908 if (http_receive_data(c) < 0)
911 case HTTPSTATE_WAIT_FEED:
912 /* no need to read if no events */
913 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
916 /* nothing to do, we'll be waken up by incoming feed packets */
919 case RTSPSTATE_SEND_REPLY:
920 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
921 av_freep(&c->pb_buffer);
924 /* no need to write if no events */
925 if (!(c->poll_entry->revents & POLLOUT))
927 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
929 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
930 ff_neterrno() != FF_NETERROR(EINTR)) {
931 /* error : close connection */
932 av_freep(&c->pb_buffer);
936 c->buffer_ptr += len;
937 c->data_count += len;
938 if (c->buffer_ptr >= c->buffer_end) {
939 /* all the buffer was sent : wait for a new request */
940 av_freep(&c->pb_buffer);
941 start_wait_request(c, 1);
945 case RTSPSTATE_SEND_PACKET:
946 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
947 av_freep(&c->packet_buffer);
950 /* no need to write if no events */
951 if (!(c->poll_entry->revents & POLLOUT))
953 len = send(c->fd, c->packet_buffer_ptr,
954 c->packet_buffer_end - c->packet_buffer_ptr, 0);
956 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
957 ff_neterrno() != FF_NETERROR(EINTR)) {
958 /* error : close connection */
959 av_freep(&c->packet_buffer);
963 c->packet_buffer_ptr += len;
964 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
965 /* all the buffer was sent : wait for a new request */
966 av_freep(&c->packet_buffer);
967 c->state = RTSPSTATE_WAIT_REQUEST;
971 case HTTPSTATE_READY:
980 static int extract_rates(char *rates, int ratelen, const char *request)
984 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
985 if (strncasecmp(p, "Pragma:", 7) == 0) {
986 const char *q = p + 7;
988 while (*q && *q != '\n' && isspace(*q))
991 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
997 memset(rates, 0xff, ratelen);
1000 while (*q && *q != '\n' && *q != ':')
1003 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1007 if (stream_no < ratelen && stream_no >= 0)
1008 rates[stream_no] = rate_no;
1010 while (*q && *q != '\n' && !isspace(*q))
1017 p = strchr(p, '\n');
1027 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1030 int best_bitrate = 100000000;
1033 for (i = 0; i < feed->nb_streams; i++) {
1034 AVCodecContext *feed_codec = feed->streams[i]->codec;
1036 if (feed_codec->codec_id != codec->codec_id ||
1037 feed_codec->sample_rate != codec->sample_rate ||
1038 feed_codec->width != codec->width ||
1039 feed_codec->height != codec->height)
1042 /* Potential stream */
1044 /* We want the fastest stream less than bit_rate, or the slowest
1045 * faster than bit_rate
1048 if (feed_codec->bit_rate <= bit_rate) {
1049 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1050 best_bitrate = feed_codec->bit_rate;
1054 if (feed_codec->bit_rate < best_bitrate) {
1055 best_bitrate = feed_codec->bit_rate;
1064 static int modify_current_stream(HTTPContext *c, char *rates)
1067 FFStream *req = c->stream;
1068 int action_required = 0;
1070 /* Not much we can do for a feed */
1074 for (i = 0; i < req->nb_streams; i++) {
1075 AVCodecContext *codec = req->streams[i]->codec;
1079 c->switch_feed_streams[i] = req->feed_streams[i];
1082 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1085 /* Wants off or slow */
1086 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1088 /* This doesn't work well when it turns off the only stream! */
1089 c->switch_feed_streams[i] = -2;
1090 c->feed_streams[i] = -2;
1095 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1096 action_required = 1;
1099 return action_required;
1103 static void do_switch_stream(HTTPContext *c, int i)
1105 if (c->switch_feed_streams[i] >= 0) {
1107 c->feed_streams[i] = c->switch_feed_streams[i];
1110 /* Now update the stream */
1112 c->switch_feed_streams[i] = -1;
1115 /* XXX: factorize in utils.c ? */
1116 /* XXX: take care with different space meaning */
1117 static void skip_spaces(const char **pp)
1121 while (*p == ' ' || *p == '\t')
1126 static void get_word(char *buf, int buf_size, const char **pp)
1134 while (!isspace(*p) && *p != '\0') {
1135 if ((q - buf) < buf_size - 1)
1144 static int validate_acl(FFStream *stream, HTTPContext *c)
1146 enum IPAddressAction last_action = IP_DENY;
1148 struct in_addr *src = &c->from_addr.sin_addr;
1149 unsigned long src_addr = src->s_addr;
1151 for (acl = stream->acl; acl; acl = acl->next) {
1152 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1153 return (acl->action == IP_ALLOW) ? 1 : 0;
1154 last_action = acl->action;
1157 /* Nothing matched, so return not the last action */
1158 return (last_action == IP_DENY) ? 1 : 0;
1161 /* compute the real filename of a file by matching it without its
1162 extensions to all the stream filenames */
1163 static void compute_real_filename(char *filename, int max_size)
1170 /* compute filename by matching without the file extensions */
1171 av_strlcpy(file1, filename, sizeof(file1));
1172 p = strrchr(file1, '.');
1175 for(stream = first_stream; stream != NULL; stream = stream->next) {
1176 av_strlcpy(file2, stream->filename, sizeof(file2));
1177 p = strrchr(file2, '.');
1180 if (!strcmp(file1, file2)) {
1181 av_strlcpy(filename, stream->filename, max_size);
1196 /* parse http request and prepare header */
1197 static int http_parse_request(HTTPContext *c)
1200 enum RedirType redir_type;
1202 char info[1024], filename[1024];
1206 const char *mime_type;
1210 char *useragent = 0;
1213 get_word(cmd, sizeof(cmd), (const char **)&p);
1214 av_strlcpy(c->method, cmd, sizeof(c->method));
1216 if (!strcmp(cmd, "GET"))
1218 else if (!strcmp(cmd, "POST"))
1223 get_word(url, sizeof(url), (const char **)&p);
1224 av_strlcpy(c->url, url, sizeof(c->url));
1226 get_word(protocol, sizeof(protocol), (const char **)&p);
1227 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1230 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1233 http_log("New connection: %s %s\n", cmd, url);
1235 /* find the filename and the optional info string in the request */
1236 p = strchr(url, '?');
1238 av_strlcpy(info, p, sizeof(info));
1243 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1245 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1246 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1248 if (*useragent && *useragent != '\n' && isspace(*useragent))
1252 p = strchr(p, '\n');
1259 redir_type = REDIR_NONE;
1260 if (match_ext(filename, "asx")) {
1261 redir_type = REDIR_ASX;
1262 filename[strlen(filename)-1] = 'f';
1263 } else if (match_ext(filename, "asf") &&
1264 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1265 /* if this isn't WMP or lookalike, return the redirector file */
1266 redir_type = REDIR_ASF;
1267 } else if (match_ext(filename, "rpm,ram")) {
1268 redir_type = REDIR_RAM;
1269 strcpy(filename + strlen(filename)-2, "m");
1270 } else if (match_ext(filename, "rtsp")) {
1271 redir_type = REDIR_RTSP;
1272 compute_real_filename(filename, sizeof(filename) - 1);
1273 } else if (match_ext(filename, "sdp")) {
1274 redir_type = REDIR_SDP;
1275 compute_real_filename(filename, sizeof(filename) - 1);
1278 // "redirect" / request to index.html
1279 if (!strlen(filename))
1280 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1282 stream = first_stream;
1283 while (stream != NULL) {
1284 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1286 stream = stream->next;
1288 if (stream == NULL) {
1289 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1294 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1295 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1297 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1298 c->http_error = 301;
1300 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1301 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1302 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1303 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1304 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1305 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1306 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1308 /* prepare output buffer */
1309 c->buffer_ptr = c->buffer;
1311 c->state = HTTPSTATE_SEND_HEADER;
1315 /* If this is WMP, get the rate information */
1316 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1317 if (modify_current_stream(c, ratebuf)) {
1318 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1319 if (c->switch_feed_streams[i] >= 0)
1320 do_switch_stream(c, i);
1325 /* If already streaming this feed, do not let start another feeder. */
1326 if (stream->feed_opened) {
1327 snprintf(msg, sizeof(msg), "This feed is already being received.");
1331 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1332 current_bandwidth += stream->bandwidth;
1334 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1335 c->http_error = 200;
1337 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1338 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1339 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1340 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1341 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");
1342 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",
1343 current_bandwidth, max_bandwidth);
1344 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1346 /* prepare output buffer */
1347 c->buffer_ptr = c->buffer;
1349 c->state = HTTPSTATE_SEND_HEADER;
1353 if (redir_type != REDIR_NONE) {
1356 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1357 if (strncasecmp(p, "Host:", 5) == 0) {
1361 p = strchr(p, '\n');
1372 while (isspace(*hostinfo))
1375 eoh = strchr(hostinfo, '\n');
1377 if (eoh[-1] == '\r')
1380 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1381 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1382 hostbuf[eoh - hostinfo] = 0;
1384 c->http_error = 200;
1386 switch(redir_type) {
1388 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1389 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1390 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1391 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1392 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1393 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1394 hostbuf, filename, info);
1395 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1398 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1399 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1400 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1401 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1402 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1403 hostbuf, filename, info);
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1408 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1410 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1411 hostbuf, filename, info);
1415 char hostname[256], *p;
1416 /* extract only hostname */
1417 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1418 p = strrchr(hostname, ':');
1421 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1422 /* XXX: incorrect mime type ? */
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1425 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1426 hostname, ntohs(my_rtsp_addr.sin_port),
1433 int sdp_data_size, len;
1434 struct sockaddr_in my_addr;
1436 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1437 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1438 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1440 len = sizeof(my_addr);
1441 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1443 /* XXX: should use a dynamic buffer */
1444 sdp_data_size = prepare_sdp_description(stream,
1447 if (sdp_data_size > 0) {
1448 memcpy(q, sdp_data, sdp_data_size);
1460 /* prepare output buffer */
1461 c->buffer_ptr = c->buffer;
1463 c->state = HTTPSTATE_SEND_HEADER;
1469 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1473 stream->conns_served++;
1475 /* XXX: add there authenticate and IP match */
1478 /* if post, it means a feed is being sent */
1479 if (!stream->is_feed) {
1480 /* However it might be a status report from WMP! Lets log the data
1481 * as it might come in handy one day
1486 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1487 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1491 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1492 client_id = strtol(p + 18, 0, 10);
1493 p = strchr(p, '\n');
1501 char *eol = strchr(logline, '\n');
1506 if (eol[-1] == '\r')
1508 http_log("%.*s\n", (int) (eol - logline), logline);
1509 c->suppress_log = 1;
1514 http_log("\nGot request:\n%s\n", c->buffer);
1517 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1520 /* Now we have to find the client_id */
1521 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1522 if (wmpc->wmp_client_id == client_id)
1526 if (wmpc && modify_current_stream(wmpc, ratebuf))
1527 wmpc->switch_pending = 1;
1530 snprintf(msg, sizeof(msg), "POST command not handled");
1534 if (http_start_receive_data(c) < 0) {
1535 snprintf(msg, sizeof(msg), "could not open feed");
1539 c->state = HTTPSTATE_RECEIVE_DATA;
1544 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1545 http_log("\nGot request:\n%s\n", c->buffer);
1548 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1551 /* open input stream */
1552 if (open_input_stream(c, info) < 0) {
1553 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1557 /* prepare http header */
1559 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1560 mime_type = c->stream->fmt->mime_type;
1562 mime_type = "application/x-octet-stream";
1563 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1565 /* for asf, we need extra headers */
1566 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1567 /* Need to allocate a client id */
1569 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1571 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);
1573 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1574 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1576 /* prepare output buffer */
1578 c->buffer_ptr = c->buffer;
1580 c->state = HTTPSTATE_SEND_HEADER;
1583 c->http_error = 404;
1585 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1586 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1587 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1588 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1589 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1590 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1591 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1593 /* prepare output buffer */
1594 c->buffer_ptr = c->buffer;
1596 c->state = HTTPSTATE_SEND_HEADER;
1600 c->http_error = 200; /* horrible : we use this value to avoid
1601 going to the send data state */
1602 c->state = HTTPSTATE_SEND_HEADER;
1606 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1608 static const char *suffix = " kMGTP";
1611 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1613 url_fprintf(pb, "%"PRId64"%c", count, *s);
1616 static void compute_status(HTTPContext *c)
1625 if (url_open_dyn_buf(&pb) < 0) {
1626 /* XXX: return an error ? */
1627 c->buffer_ptr = c->buffer;
1628 c->buffer_end = c->buffer;
1632 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1633 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1634 url_fprintf(pb, "Pragma: no-cache\r\n");
1635 url_fprintf(pb, "\r\n");
1637 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1638 if (c->stream->feed_filename[0])
1639 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1640 url_fprintf(pb, "</HEAD>\n<BODY>");
1641 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1643 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1644 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1645 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");
1646 stream = first_stream;
1647 while (stream != NULL) {
1648 char sfilename[1024];
1651 if (stream->feed != stream) {
1652 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1653 eosf = sfilename + strlen(sfilename);
1654 if (eosf - sfilename >= 4) {
1655 if (strcmp(eosf - 4, ".asf") == 0)
1656 strcpy(eosf - 4, ".asx");
1657 else if (strcmp(eosf - 3, ".rm") == 0)
1658 strcpy(eosf - 3, ".ram");
1659 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1660 /* generate a sample RTSP director if
1661 unicast. Generate an SDP redirector if
1663 eosf = strrchr(sfilename, '.');
1665 eosf = sfilename + strlen(sfilename);
1666 if (stream->is_multicast)
1667 strcpy(eosf, ".sdp");
1669 strcpy(eosf, ".rtsp");
1673 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1674 sfilename, stream->filename);
1675 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1676 stream->conns_served);
1677 fmt_bytecount(pb, stream->bytes_served);
1678 switch(stream->stream_type) {
1679 case STREAM_TYPE_LIVE:
1681 int audio_bit_rate = 0;
1682 int video_bit_rate = 0;
1683 const char *audio_codec_name = "";
1684 const char *video_codec_name = "";
1685 const char *audio_codec_name_extra = "";
1686 const char *video_codec_name_extra = "";
1688 for(i=0;i<stream->nb_streams;i++) {
1689 AVStream *st = stream->streams[i];
1690 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1691 switch(st->codec->codec_type) {
1692 case CODEC_TYPE_AUDIO:
1693 audio_bit_rate += st->codec->bit_rate;
1695 if (*audio_codec_name)
1696 audio_codec_name_extra = "...";
1697 audio_codec_name = codec->name;
1700 case CODEC_TYPE_VIDEO:
1701 video_bit_rate += st->codec->bit_rate;
1703 if (*video_codec_name)
1704 video_codec_name_extra = "...";
1705 video_codec_name = codec->name;
1708 case CODEC_TYPE_DATA:
1709 video_bit_rate += st->codec->bit_rate;
1715 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",
1718 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1719 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1721 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1723 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1724 url_fprintf(pb, "\n");
1728 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1732 stream = stream->next;
1734 url_fprintf(pb, "</TABLE>\n");
1736 stream = first_stream;
1737 while (stream != NULL) {
1738 if (stream->feed == stream) {
1739 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1741 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1743 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1748 /* This is somewhat linux specific I guess */
1749 snprintf(ps_cmd, sizeof(ps_cmd),
1750 "ps -o \"%%cpu,cputime\" --no-headers %d",
1753 pid_stat = popen(ps_cmd, "r");
1758 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1760 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1768 url_fprintf(pb, "<p>");
1770 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");
1772 for (i = 0; i < stream->nb_streams; i++) {
1773 AVStream *st = stream->streams[i];
1774 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1775 const char *type = "unknown";
1776 char parameters[64];
1780 switch(st->codec->codec_type) {
1781 case CODEC_TYPE_AUDIO:
1783 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1785 case CODEC_TYPE_VIDEO:
1787 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1788 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1793 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1794 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1796 url_fprintf(pb, "</table>\n");
1799 stream = stream->next;
1805 AVCodecContext *enc;
1809 stream = first_feed;
1810 while (stream != NULL) {
1811 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1812 url_fprintf(pb, "<TABLE>\n");
1813 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1814 for(i=0;i<stream->nb_streams;i++) {
1815 AVStream *st = stream->streams[i];
1816 FeedData *fdata = st->priv_data;
1819 avcodec_string(buf, sizeof(buf), enc);
1820 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1821 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1822 avg /= enc->frame_size;
1823 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1824 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1826 url_fprintf(pb, "</TABLE>\n");
1827 stream = stream->next_feed;
1832 /* connection status */
1833 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1835 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1836 nb_connections, nb_max_connections);
1838 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1839 current_bandwidth, max_bandwidth);
1841 url_fprintf(pb, "<TABLE>\n");
1842 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");
1843 c1 = first_http_ctx;
1845 while (c1 != NULL) {
1851 for (j = 0; j < c1->stream->nb_streams; j++) {
1852 if (!c1->stream->feed)
1853 bitrate += c1->stream->streams[j]->codec->bit_rate;
1854 else if (c1->feed_streams[j] >= 0)
1855 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1860 p = inet_ntoa(c1->from_addr.sin_addr);
1861 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1863 c1->stream ? c1->stream->filename : "",
1864 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1867 http_state[c1->state]);
1868 fmt_bytecount(pb, bitrate);
1869 url_fprintf(pb, "<td align=right>");
1870 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1871 url_fprintf(pb, "<td align=right>");
1872 fmt_bytecount(pb, c1->data_count);
1873 url_fprintf(pb, "\n");
1876 url_fprintf(pb, "</TABLE>\n");
1881 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1882 url_fprintf(pb, "</BODY>\n</HTML>\n");
1884 len = url_close_dyn_buf(pb, &c->pb_buffer);
1885 c->buffer_ptr = c->pb_buffer;
1886 c->buffer_end = c->pb_buffer + len;
1889 /* check if the parser needs to be opened for stream i */
1890 static void open_parser(AVFormatContext *s, int i)
1892 AVStream *st = s->streams[i];
1895 if (!st->codec->codec) {
1896 codec = avcodec_find_decoder(st->codec->codec_id);
1897 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1898 st->codec->parse_only = 1;
1899 if (avcodec_open(st->codec, codec) < 0)
1900 st->codec->parse_only = 0;
1905 static int open_input_stream(HTTPContext *c, const char *info)
1908 char input_filename[1024];
1910 int buf_size, i, ret;
1913 /* find file name */
1914 if (c->stream->feed) {
1915 strcpy(input_filename, c->stream->feed->feed_filename);
1916 buf_size = FFM_PACKET_SIZE;
1917 /* compute position (absolute time) */
1918 if (find_info_tag(buf, sizeof(buf), "date", info))
1920 stream_pos = parse_date(buf, 0);
1921 if (stream_pos == INT64_MIN)
1924 else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1925 int prebuffer = strtol(buf, 0, 10);
1926 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1928 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1930 strcpy(input_filename, c->stream->feed_filename);
1932 /* compute position (relative time) */
1933 if (find_info_tag(buf, sizeof(buf), "date", info))
1935 stream_pos = parse_date(buf, 1);
1936 if (stream_pos == INT64_MIN)
1942 if (input_filename[0] == '\0')
1946 { time_t when = stream_pos / 1000000;
1947 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1952 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1953 buf_size, c->stream->ap_in)) < 0) {
1954 http_log("could not open %s: %d\n", input_filename, ret);
1957 s->flags |= AVFMT_FLAG_GENPTS;
1959 av_find_stream_info(c->fmt_in);
1961 /* open each parser */
1962 for(i=0;i<s->nb_streams;i++)
1965 /* choose stream as clock source (we favorize video stream if
1966 present) for packet sending */
1967 c->pts_stream_index = 0;
1968 for(i=0;i<c->stream->nb_streams;i++) {
1969 if (c->pts_stream_index == 0 &&
1970 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1971 c->pts_stream_index = i;
1976 if (c->fmt_in->iformat->read_seek)
1977 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1979 /* set the start time (needed for maxtime and RTP packet timing) */
1980 c->start_time = cur_time;
1981 c->first_pts = AV_NOPTS_VALUE;
1985 /* return the server clock (in us) */
1986 static int64_t get_server_clock(HTTPContext *c)
1988 /* compute current pts value from system time */
1989 return (cur_time - c->start_time) * 1000;
1992 /* return the estimated time at which the current packet must be sent
1994 static int64_t get_packet_send_clock(HTTPContext *c)
1996 int bytes_left, bytes_sent, frame_bytes;
1998 frame_bytes = c->cur_frame_bytes;
1999 if (frame_bytes <= 0)
2002 bytes_left = c->buffer_end - c->buffer_ptr;
2003 bytes_sent = frame_bytes - bytes_left;
2004 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2009 static int http_prepare_data(HTTPContext *c)
2012 AVFormatContext *ctx;
2014 av_freep(&c->pb_buffer);
2016 case HTTPSTATE_SEND_DATA_HEADER:
2017 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2018 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2019 sizeof(c->fmt_ctx.author));
2020 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2021 sizeof(c->fmt_ctx.comment));
2022 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2023 sizeof(c->fmt_ctx.copyright));
2024 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2025 sizeof(c->fmt_ctx.title));
2027 /* open output stream by using specified codecs */
2028 c->fmt_ctx.oformat = c->stream->fmt;
2029 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2030 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2033 st = av_mallocz(sizeof(AVStream));
2034 st->codec= avcodec_alloc_context();
2035 c->fmt_ctx.streams[i] = st;
2036 /* if file or feed, then just take streams from FFStream struct */
2037 if (!c->stream->feed ||
2038 c->stream->feed == c->stream)
2039 src = c->stream->streams[i];
2041 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2045 st->codec->frame_number = 0; /* XXX: should be done in
2046 AVStream, not in codec */
2048 c->got_key_frame = 0;
2050 /* prepare header and save header data in a stream */
2051 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2052 /* XXX: potential leak */
2055 c->fmt_ctx.pb->is_streamed = 1;
2058 * HACK to avoid mpeg ps muxer to spit many underflow errors
2059 * Default value from FFmpeg
2060 * Try to set it use configuration option
2062 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2063 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2065 av_set_parameters(&c->fmt_ctx, NULL);
2066 if (av_write_header(&c->fmt_ctx) < 0) {
2067 http_log("Error writing output header\n");
2071 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2072 c->buffer_ptr = c->pb_buffer;
2073 c->buffer_end = c->pb_buffer + len;
2075 c->state = HTTPSTATE_SEND_DATA;
2076 c->last_packet_sent = 0;
2078 case HTTPSTATE_SEND_DATA:
2079 /* find a new packet */
2080 /* read a packet from the input stream */
2081 if (c->stream->feed)
2082 ffm_set_write_index(c->fmt_in,
2083 c->stream->feed->feed_write_index,
2084 c->stream->feed->feed_size);
2086 if (c->stream->max_time &&
2087 c->stream->max_time + c->start_time - cur_time < 0)
2088 /* We have timed out */
2089 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2093 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2094 if (c->stream->feed && c->stream->feed->feed_opened) {
2095 /* if coming from feed, it means we reached the end of the
2096 ffm file, so must wait for more data */
2097 c->state = HTTPSTATE_WAIT_FEED;
2098 return 1; /* state changed */
2100 if (c->stream->loop) {
2101 av_close_input_file(c->fmt_in);
2103 if (open_input_stream(c, "") < 0)
2108 /* must send trailer now because eof or error */
2109 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2113 int source_index = pkt.stream_index;
2114 /* update first pts if needed */
2115 if (c->first_pts == AV_NOPTS_VALUE) {
2116 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2117 c->start_time = cur_time;
2119 /* send it to the appropriate stream */
2120 if (c->stream->feed) {
2121 /* if coming from a feed, select the right stream */
2122 if (c->switch_pending) {
2123 c->switch_pending = 0;
2124 for(i=0;i<c->stream->nb_streams;i++) {
2125 if (c->switch_feed_streams[i] == pkt.stream_index)
2126 if (pkt.flags & PKT_FLAG_KEY)
2127 do_switch_stream(c, i);
2128 if (c->switch_feed_streams[i] >= 0)
2129 c->switch_pending = 1;
2132 for(i=0;i<c->stream->nb_streams;i++) {
2133 if (c->feed_streams[i] == pkt.stream_index) {
2134 AVStream *st = c->fmt_in->streams[source_index];
2135 pkt.stream_index = i;
2136 if (pkt.flags & PKT_FLAG_KEY &&
2137 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2138 c->stream->nb_streams == 1))
2139 c->got_key_frame = 1;
2140 if (!c->stream->send_on_key || c->got_key_frame)
2145 AVCodecContext *codec;
2148 /* specific handling for RTP: we use several
2149 output stream (one for each RTP
2150 connection). XXX: need more abstract handling */
2151 if (c->is_packetized) {
2153 /* compute send time and duration */
2154 st = c->fmt_in->streams[pkt.stream_index];
2155 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2156 if (st->start_time != AV_NOPTS_VALUE)
2157 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2158 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2160 printf("index=%d pts=%0.3f duration=%0.6f\n",
2162 (double)c->cur_pts /
2164 (double)c->cur_frame_duration /
2167 /* find RTP context */
2168 c->packet_stream_index = pkt.stream_index;
2169 ctx = c->rtp_ctx[c->packet_stream_index];
2171 av_free_packet(&pkt);
2174 codec = ctx->streams[0]->codec;
2175 /* only one stream per RTP connection */
2176 pkt.stream_index = 0;
2180 codec = ctx->streams[pkt.stream_index]->codec;
2183 if (c->is_packetized) {
2184 int max_packet_size;
2185 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2186 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2188 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2189 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2191 ret = url_open_dyn_buf(&ctx->pb);
2194 /* XXX: potential leak */
2197 c->fmt_ctx.pb->is_streamed = 1;
2198 if (pkt.dts != AV_NOPTS_VALUE)
2199 pkt.dts = av_rescale_q(pkt.dts,
2200 c->fmt_in->streams[source_index]->time_base,
2201 ctx->streams[pkt.stream_index]->time_base);
2202 if (pkt.pts != AV_NOPTS_VALUE)
2203 pkt.pts = av_rescale_q(pkt.pts,
2204 c->fmt_in->streams[source_index]->time_base,
2205 ctx->streams[pkt.stream_index]->time_base);
2206 pkt.duration = av_rescale_q(pkt.duration,
2207 c->fmt_in->streams[source_index]->time_base,
2208 ctx->streams[pkt.stream_index]->time_base);
2209 if (av_write_frame(ctx, &pkt) < 0) {
2210 http_log("Error writing frame to output\n");
2211 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2215 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2216 c->cur_frame_bytes = len;
2217 c->buffer_ptr = c->pb_buffer;
2218 c->buffer_end = c->pb_buffer + len;
2220 codec->frame_number++;
2222 av_free_packet(&pkt);
2226 av_free_packet(&pkt);
2231 case HTTPSTATE_SEND_DATA_TRAILER:
2232 /* last packet test ? */
2233 if (c->last_packet_sent || c->is_packetized)
2236 /* prepare header */
2237 if (url_open_dyn_buf(&ctx->pb) < 0) {
2238 /* XXX: potential leak */
2241 c->fmt_ctx.pb->is_streamed = 1;
2242 av_write_trailer(ctx);
2243 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2244 c->buffer_ptr = c->pb_buffer;
2245 c->buffer_end = c->pb_buffer + len;
2247 c->last_packet_sent = 1;
2253 /* should convert the format at the same time */
2254 /* send data starting at c->buffer_ptr to the output connection
2255 (either UDP or TCP connection) */
2256 static int http_send_data(HTTPContext *c)
2261 if (c->buffer_ptr >= c->buffer_end) {
2262 ret = http_prepare_data(c);
2266 /* state change requested */
2269 if (c->is_packetized) {
2270 /* RTP data output */
2271 len = c->buffer_end - c->buffer_ptr;
2273 /* fail safe - should never happen */
2275 c->buffer_ptr = c->buffer_end;
2278 len = (c->buffer_ptr[0] << 24) |
2279 (c->buffer_ptr[1] << 16) |
2280 (c->buffer_ptr[2] << 8) |
2282 if (len > (c->buffer_end - c->buffer_ptr))
2284 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2285 /* nothing to send yet: we can wait */
2289 c->data_count += len;
2290 update_datarate(&c->datarate, c->data_count);
2292 c->stream->bytes_served += len;
2294 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2295 /* RTP packets are sent inside the RTSP TCP connection */
2297 int interleaved_index, size;
2299 HTTPContext *rtsp_c;
2302 /* if no RTSP connection left, error */
2305 /* if already sending something, then wait. */
2306 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2308 if (url_open_dyn_buf(&pb) < 0)
2310 interleaved_index = c->packet_stream_index * 2;
2311 /* RTCP packets are sent at odd indexes */
2312 if (c->buffer_ptr[1] == 200)
2313 interleaved_index++;
2314 /* write RTSP TCP header */
2316 header[1] = interleaved_index;
2317 header[2] = len >> 8;
2319 put_buffer(pb, header, 4);
2320 /* write RTP packet data */
2322 put_buffer(pb, c->buffer_ptr, len);
2323 size = url_close_dyn_buf(pb, &c->packet_buffer);
2324 /* prepare asynchronous TCP sending */
2325 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2326 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2327 c->buffer_ptr += len;
2329 /* send everything we can NOW */
2330 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2331 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2333 rtsp_c->packet_buffer_ptr += len;
2334 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2335 /* if we could not send all the data, we will
2336 send it later, so a new state is needed to
2337 "lock" the RTSP TCP connection */
2338 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2341 /* all data has been sent */
2342 av_freep(&c->packet_buffer);
2344 /* send RTP packet directly in UDP */
2346 url_write(c->rtp_handles[c->packet_stream_index],
2347 c->buffer_ptr, len);
2348 c->buffer_ptr += len;
2349 /* here we continue as we can send several packets per 10 ms slot */
2352 /* TCP data output */
2353 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2355 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2356 ff_neterrno() != FF_NETERROR(EINTR))
2357 /* error : close connection */
2362 c->buffer_ptr += len;
2364 c->data_count += len;
2365 update_datarate(&c->datarate, c->data_count);
2367 c->stream->bytes_served += len;
2375 static int http_start_receive_data(HTTPContext *c)
2379 if (c->stream->feed_opened)
2382 /* Don't permit writing to this one */
2383 if (c->stream->readonly)
2387 fd = open(c->stream->feed_filename, O_RDWR);
2389 http_log("Error opening feeder file: %s\n", strerror(errno));
2394 c->stream->feed_write_index = ffm_read_write_index(fd);
2395 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2396 lseek(fd, 0, SEEK_SET);
2398 /* init buffer input */
2399 c->buffer_ptr = c->buffer;
2400 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2401 c->stream->feed_opened = 1;
2405 static int http_receive_data(HTTPContext *c)
2409 if (c->buffer_end > c->buffer_ptr) {
2412 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2414 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2415 ff_neterrno() != FF_NETERROR(EINTR))
2416 /* error : close connection */
2418 } else if (len == 0)
2419 /* end of connection : close it */
2422 c->buffer_ptr += len;
2423 c->data_count += len;
2424 update_datarate(&c->datarate, c->data_count);
2428 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2429 if (c->buffer[0] != 'f' ||
2430 c->buffer[1] != 'm') {
2431 http_log("Feed stream has become desynchronized -- disconnecting\n");
2436 if (c->buffer_ptr >= c->buffer_end) {
2437 FFStream *feed = c->stream;
2438 /* a packet has been received : write it in the store, except
2440 if (c->data_count > FFM_PACKET_SIZE) {
2442 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2443 /* XXX: use llseek or url_seek */
2444 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2445 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2446 http_log("Error writing to feed file: %s\n", strerror(errno));
2450 feed->feed_write_index += FFM_PACKET_SIZE;
2451 /* update file size */
2452 if (feed->feed_write_index > c->stream->feed_size)
2453 feed->feed_size = feed->feed_write_index;
2455 /* handle wrap around if max file size reached */
2456 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2457 feed->feed_write_index = FFM_PACKET_SIZE;
2460 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2462 /* wake up any waiting connections */
2463 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2464 if (c1->state == HTTPSTATE_WAIT_FEED &&
2465 c1->stream->feed == c->stream->feed)
2466 c1->state = HTTPSTATE_SEND_DATA;
2469 /* We have a header in our hands that contains useful data */
2471 AVInputFormat *fmt_in;
2474 memset(&s, 0, sizeof(s));
2476 url_open_buf(&s.pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2477 s.pb->is_streamed = 1;
2479 /* use feed output format name to find corresponding input format */
2480 fmt_in = av_find_input_format(feed->fmt->name);
2484 if (fmt_in->priv_data_size > 0) {
2485 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2491 if (fmt_in->read_header(&s, 0) < 0) {
2492 av_freep(&s.priv_data);
2496 /* Now we have the actual streams */
2497 if (s.nb_streams != feed->nb_streams) {
2498 av_freep(&s.priv_data);
2501 for (i = 0; i < s.nb_streams; i++)
2502 memcpy(feed->streams[i]->codec,
2503 s.streams[i]->codec, sizeof(AVCodecContext));
2504 av_freep(&s.priv_data);
2506 c->buffer_ptr = c->buffer;
2511 c->stream->feed_opened = 0;
2513 /* wake up any waiting connections to stop waiting for feed */
2514 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2515 if (c1->state == HTTPSTATE_WAIT_FEED &&
2516 c1->stream->feed == c->stream->feed)
2517 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2522 /********************************************************************/
2525 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2532 switch(error_number) {
2533 case RTSP_STATUS_OK:
2536 case RTSP_STATUS_METHOD:
2537 str = "Method Not Allowed";
2539 case RTSP_STATUS_BANDWIDTH:
2540 str = "Not Enough Bandwidth";
2542 case RTSP_STATUS_SESSION:
2543 str = "Session Not Found";
2545 case RTSP_STATUS_STATE:
2546 str = "Method Not Valid in This State";
2548 case RTSP_STATUS_AGGREGATE:
2549 str = "Aggregate operation not allowed";
2551 case RTSP_STATUS_ONLY_AGGREGATE:
2552 str = "Only aggregate operation allowed";
2554 case RTSP_STATUS_TRANSPORT:
2555 str = "Unsupported transport";
2557 case RTSP_STATUS_INTERNAL:
2558 str = "Internal Server Error";
2560 case RTSP_STATUS_SERVICE:
2561 str = "Service Unavailable";
2563 case RTSP_STATUS_VERSION:
2564 str = "RTSP Version not supported";
2567 str = "Unknown Error";
2571 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2572 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2574 /* output GMT time */
2578 p = buf2 + strlen(p) - 1;
2581 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2584 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2586 rtsp_reply_header(c, error_number);
2587 url_fprintf(c->pb, "\r\n");
2590 static int rtsp_parse_request(HTTPContext *c)
2592 const char *p, *p1, *p2;
2598 RTSPHeader header1, *header = &header1;
2600 c->buffer_ptr[0] = '\0';
2603 get_word(cmd, sizeof(cmd), &p);
2604 get_word(url, sizeof(url), &p);
2605 get_word(protocol, sizeof(protocol), &p);
2607 av_strlcpy(c->method, cmd, sizeof(c->method));
2608 av_strlcpy(c->url, url, sizeof(c->url));
2609 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2611 if (url_open_dyn_buf(&c->pb) < 0) {
2612 /* XXX: cannot do more */
2613 c->pb = NULL; /* safety */
2617 /* check version name */
2618 if (strcmp(protocol, "RTSP/1.0") != 0) {
2619 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2623 /* parse each header line */
2624 memset(header, 0, sizeof(RTSPHeader));
2625 /* skip to next line */
2626 while (*p != '\n' && *p != '\0')
2630 while (*p != '\0') {
2631 p1 = strchr(p, '\n');
2635 if (p2 > p && p2[-1] == '\r')
2637 /* skip empty line */
2641 if (len > sizeof(line) - 1)
2642 len = sizeof(line) - 1;
2643 memcpy(line, p, len);
2645 rtsp_parse_line(header, line);
2649 /* handle sequence number */
2650 c->seq = header->seq;
2652 if (!strcmp(cmd, "DESCRIBE"))
2653 rtsp_cmd_describe(c, url);
2654 else if (!strcmp(cmd, "OPTIONS"))
2655 rtsp_cmd_options(c, url);
2656 else if (!strcmp(cmd, "SETUP"))
2657 rtsp_cmd_setup(c, url, header);
2658 else if (!strcmp(cmd, "PLAY"))
2659 rtsp_cmd_play(c, url, header);
2660 else if (!strcmp(cmd, "PAUSE"))
2661 rtsp_cmd_pause(c, url, header);
2662 else if (!strcmp(cmd, "TEARDOWN"))
2663 rtsp_cmd_teardown(c, url, header);
2665 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2668 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2669 c->pb = NULL; /* safety */
2671 /* XXX: cannot do more */
2674 c->buffer_ptr = c->pb_buffer;
2675 c->buffer_end = c->pb_buffer + len;
2676 c->state = RTSPSTATE_SEND_REPLY;
2680 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2681 struct in_addr my_ip)
2683 AVFormatContext *avc;
2684 AVStream avs[MAX_STREAMS];
2687 avc = av_alloc_format_context();
2691 if (stream->title[0] != 0) {
2692 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2694 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2696 avc->nb_streams = stream->nb_streams;
2697 if (stream->is_multicast) {
2698 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2699 inet_ntoa(stream->multicast_ip),
2700 stream->multicast_port, stream->multicast_ttl);
2703 for(i = 0; i < stream->nb_streams; i++) {
2704 avc->streams[i] = &avs[i];
2705 avc->streams[i]->codec = stream->streams[i]->codec;
2707 *pbuffer = av_mallocz(2048);
2708 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2711 return strlen(*pbuffer);
2714 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2716 // rtsp_reply_header(c, RTSP_STATUS_OK);
2717 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2718 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2719 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2720 url_fprintf(c->pb, "\r\n");
2723 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2729 int content_length, len;
2730 struct sockaddr_in my_addr;
2732 /* find which url is asked */
2733 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2738 for(stream = first_stream; stream != NULL; stream = stream->next) {
2739 if (!stream->is_feed &&
2740 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2741 !strcmp(path, stream->filename)) {
2745 /* no stream found */
2746 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2750 /* prepare the media description in sdp format */
2752 /* get the host IP */
2753 len = sizeof(my_addr);
2754 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2755 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2756 if (content_length < 0) {
2757 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2760 rtsp_reply_header(c, RTSP_STATUS_OK);
2761 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2762 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2763 url_fprintf(c->pb, "\r\n");
2764 put_buffer(c->pb, content, content_length);
2767 static HTTPContext *find_rtp_session(const char *session_id)
2771 if (session_id[0] == '\0')
2774 for(c = first_http_ctx; c != NULL; c = c->next) {
2775 if (!strcmp(c->session_id, session_id))
2781 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2783 RTSPTransportField *th;
2786 for(i=0;i<h->nb_transports;i++) {
2787 th = &h->transports[i];
2788 if (th->protocol == protocol)
2794 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2798 int stream_index, port;
2803 RTSPTransportField *th;
2804 struct sockaddr_in dest_addr;
2805 RTSPActionServerSetup setup;
2807 /* find which url is asked */
2808 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2813 /* now check each stream */
2814 for(stream = first_stream; stream != NULL; stream = stream->next) {
2815 if (!stream->is_feed &&
2816 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2817 /* accept aggregate filenames only if single stream */
2818 if (!strcmp(path, stream->filename)) {
2819 if (stream->nb_streams != 1) {
2820 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2827 for(stream_index = 0; stream_index < stream->nb_streams;
2829 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2830 stream->filename, stream_index);
2831 if (!strcmp(path, buf))
2836 /* no stream found */
2837 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2841 /* generate session id if needed */
2842 if (h->session_id[0] == '\0')
2843 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2844 av_random(&random_state), av_random(&random_state));
2846 /* find rtp session, and create it if none found */
2847 rtp_c = find_rtp_session(h->session_id);
2849 /* always prefer UDP */
2850 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2852 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2854 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2859 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2862 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2866 /* open input stream */
2867 if (open_input_stream(rtp_c, "") < 0) {
2868 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2873 /* test if stream is OK (test needed because several SETUP needs
2874 to be done for a given file) */
2875 if (rtp_c->stream != stream) {
2876 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2880 /* test if stream is already set up */
2881 if (rtp_c->rtp_ctx[stream_index]) {
2882 rtsp_reply_error(c, RTSP_STATUS_STATE);
2886 /* check transport */
2887 th = find_transport(h, rtp_c->rtp_protocol);
2888 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2889 th->client_port_min <= 0)) {
2890 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2894 /* setup default options */
2895 setup.transport_option[0] = '\0';
2896 dest_addr = rtp_c->from_addr;
2897 dest_addr.sin_port = htons(th->client_port_min);
2900 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2901 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2905 /* now everything is OK, so we can send the connection parameters */
2906 rtsp_reply_header(c, RTSP_STATUS_OK);
2908 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2910 switch(rtp_c->rtp_protocol) {
2911 case RTSP_PROTOCOL_RTP_UDP:
2912 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2913 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2914 "client_port=%d-%d;server_port=%d-%d",
2915 th->client_port_min, th->client_port_min + 1,
2918 case RTSP_PROTOCOL_RTP_TCP:
2919 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2920 stream_index * 2, stream_index * 2 + 1);
2925 if (setup.transport_option[0] != '\0')
2926 url_fprintf(c->pb, ";%s", setup.transport_option);
2927 url_fprintf(c->pb, "\r\n");
2930 url_fprintf(c->pb, "\r\n");
2934 /* find an rtp connection by using the session ID. Check consistency
2936 static HTTPContext *find_rtp_session_with_url(const char *url,
2937 const char *session_id)
2945 rtp_c = find_rtp_session(session_id);
2949 /* find which url is asked */
2950 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2954 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2955 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2956 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2957 rtp_c->stream->filename, s);
2958 if(!strncmp(path, buf, sizeof(buf))) {
2959 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2966 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2970 rtp_c = find_rtp_session_with_url(url, h->session_id);
2972 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2976 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2977 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2978 rtp_c->state != HTTPSTATE_READY) {
2979 rtsp_reply_error(c, RTSP_STATUS_STATE);
2984 /* XXX: seek in stream */
2985 if (h->range_start != AV_NOPTS_VALUE) {
2986 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2987 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2991 rtp_c->state = HTTPSTATE_SEND_DATA;
2993 /* now everything is OK, so we can send the connection parameters */
2994 rtsp_reply_header(c, RTSP_STATUS_OK);
2996 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2997 url_fprintf(c->pb, "\r\n");
3000 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3004 rtp_c = find_rtp_session_with_url(url, h->session_id);
3006 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3010 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3011 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3012 rtsp_reply_error(c, RTSP_STATUS_STATE);
3016 rtp_c->state = HTTPSTATE_READY;
3017 rtp_c->first_pts = AV_NOPTS_VALUE;
3018 /* now everything is OK, so we can send the connection parameters */
3019 rtsp_reply_header(c, RTSP_STATUS_OK);
3021 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3022 url_fprintf(c->pb, "\r\n");
3025 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3028 char session_id[32];
3030 rtp_c = find_rtp_session_with_url(url, h->session_id);
3032 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3036 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3038 /* abort the session */
3039 close_connection(rtp_c);
3041 /* now everything is OK, so we can send the connection parameters */
3042 rtsp_reply_header(c, RTSP_STATUS_OK);
3044 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3045 url_fprintf(c->pb, "\r\n");
3049 /********************************************************************/
3052 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3053 FFStream *stream, const char *session_id,
3054 enum RTSPProtocol rtp_protocol)
3056 HTTPContext *c = NULL;
3057 const char *proto_str;
3059 /* XXX: should output a warning page when coming
3060 close to the connection limit */
3061 if (nb_connections >= nb_max_connections)
3064 /* add a new connection */
3065 c = av_mallocz(sizeof(HTTPContext));
3070 c->poll_entry = NULL;
3071 c->from_addr = *from_addr;
3072 c->buffer_size = IOBUFFER_INIT_SIZE;
3073 c->buffer = av_malloc(c->buffer_size);
3078 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3079 c->state = HTTPSTATE_READY;
3080 c->is_packetized = 1;
3081 c->rtp_protocol = rtp_protocol;
3083 /* protocol is shown in statistics */
3084 switch(c->rtp_protocol) {
3085 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3086 proto_str = "MCAST";
3088 case RTSP_PROTOCOL_RTP_UDP:
3091 case RTSP_PROTOCOL_RTP_TCP:
3098 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3099 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3101 current_bandwidth += stream->bandwidth;
3103 c->next = first_http_ctx;
3115 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3116 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3118 static int rtp_new_av_stream(HTTPContext *c,
3119 int stream_index, struct sockaddr_in *dest_addr,
3120 HTTPContext *rtsp_c)
3122 AVFormatContext *ctx;
3125 URLContext *h = NULL;
3128 int max_packet_size;
3130 /* now we can open the relevant output stream */
3131 ctx = av_alloc_format_context();
3134 ctx->oformat = guess_format("rtp", NULL, NULL);
3136 st = av_mallocz(sizeof(AVStream));
3139 st->codec= avcodec_alloc_context();
3140 ctx->nb_streams = 1;
3141 ctx->streams[0] = st;
3143 if (!c->stream->feed ||
3144 c->stream->feed == c->stream)
3145 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3148 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3150 st->priv_data = NULL;
3152 /* build destination RTP address */
3153 ipaddr = inet_ntoa(dest_addr->sin_addr);
3155 switch(c->rtp_protocol) {
3156 case RTSP_PROTOCOL_RTP_UDP:
3157 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3160 /* XXX: also pass as parameter to function ? */
3161 if (c->stream->is_multicast) {
3163 ttl = c->stream->multicast_ttl;
3166 snprintf(ctx->filename, sizeof(ctx->filename),
3167 "rtp://%s:%d?multicast=1&ttl=%d",
3168 ipaddr, ntohs(dest_addr->sin_port), ttl);
3170 snprintf(ctx->filename, sizeof(ctx->filename),
3171 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3174 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3176 c->rtp_handles[stream_index] = h;
3177 max_packet_size = url_get_max_packet_size(h);
3179 case RTSP_PROTOCOL_RTP_TCP:
3182 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3188 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3189 ipaddr, ntohs(dest_addr->sin_port),
3191 c->stream->filename, stream_index, c->protocol);
3193 /* normally, no packets should be output here, but the packet size may be checked */
3194 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3195 /* XXX: close stream */
3198 av_set_parameters(ctx, NULL);
3199 if (av_write_header(ctx) < 0) {
3206 url_close_dyn_buf(ctx->pb, &dummy_buf);
3209 c->rtp_ctx[stream_index] = ctx;
3213 /********************************************************************/
3214 /* ffserver initialization */
3216 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3220 fst = av_mallocz(sizeof(AVStream));
3223 fst->codec= avcodec_alloc_context();
3224 fst->priv_data = av_mallocz(sizeof(FeedData));
3225 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3226 fst->index = stream->nb_streams;
3227 av_set_pts_info(fst, 33, 1, 90000);
3228 stream->streams[stream->nb_streams++] = fst;
3232 /* return the stream number in the feed */
3233 static int add_av_stream(FFStream *feed, AVStream *st)
3236 AVCodecContext *av, *av1;
3240 for(i=0;i<feed->nb_streams;i++) {
3241 st = feed->streams[i];
3243 if (av1->codec_id == av->codec_id &&
3244 av1->codec_type == av->codec_type &&
3245 av1->bit_rate == av->bit_rate) {
3247 switch(av->codec_type) {
3248 case CODEC_TYPE_AUDIO:
3249 if (av1->channels == av->channels &&
3250 av1->sample_rate == av->sample_rate)
3253 case CODEC_TYPE_VIDEO:
3254 if (av1->width == av->width &&
3255 av1->height == av->height &&
3256 av1->time_base.den == av->time_base.den &&
3257 av1->time_base.num == av->time_base.num &&
3258 av1->gop_size == av->gop_size)
3267 fst = add_av_stream1(feed, av);
3270 return feed->nb_streams - 1;
3275 static void remove_stream(FFStream *stream)
3279 while (*ps != NULL) {
3287 /* specific mpeg4 handling : we extract the raw parameters */
3288 static void extract_mpeg4_header(AVFormatContext *infile)
3290 int mpeg4_count, i, size;
3296 for(i=0;i<infile->nb_streams;i++) {
3297 st = infile->streams[i];
3298 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3299 st->codec->extradata_size == 0) {
3306 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3307 while (mpeg4_count > 0) {
3308 if (av_read_packet(infile, &pkt) < 0)
3310 st = infile->streams[pkt.stream_index];
3311 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3312 st->codec->extradata_size == 0) {
3313 av_freep(&st->codec->extradata);
3314 /* fill extradata with the header */
3315 /* XXX: we make hard suppositions here ! */
3317 while (p < pkt.data + pkt.size - 4) {
3318 /* stop when vop header is found */
3319 if (p[0] == 0x00 && p[1] == 0x00 &&
3320 p[2] == 0x01 && p[3] == 0xb6) {
3321 size = p - pkt.data;
3322 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3323 st->codec->extradata = av_malloc(size);
3324 st->codec->extradata_size = size;
3325 memcpy(st->codec->extradata, pkt.data, size);
3332 av_free_packet(&pkt);
3336 /* compute the needed AVStream for each file */
3337 static void build_file_streams(void)
3339 FFStream *stream, *stream_next;
3340 AVFormatContext *infile;
3343 /* gather all streams */
3344 for(stream = first_stream; stream != NULL; stream = stream_next) {
3345 stream_next = stream->next;
3346 if (stream->stream_type == STREAM_TYPE_LIVE &&
3348 /* the stream comes from a file */
3349 /* try to open the file */
3351 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3352 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3353 /* specific case : if transport stream output to RTP,
3354 we use a raw transport stream reader */
3355 stream->ap_in->mpeg2ts_raw = 1;
3356 stream->ap_in->mpeg2ts_compute_pcr = 1;
3359 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3360 stream->ifmt, 0, stream->ap_in)) < 0) {
3361 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3362 /* remove stream (no need to spend more time on it) */
3364 remove_stream(stream);
3366 /* find all the AVStreams inside and reference them in
3368 if (av_find_stream_info(infile) < 0) {
3369 http_log("Could not find codec parameters from '%s'\n",
3370 stream->feed_filename);
3371 av_close_input_file(infile);
3374 extract_mpeg4_header(infile);
3376 for(i=0;i<infile->nb_streams;i++)
3377 add_av_stream1(stream, infile->streams[i]->codec);
3379 av_close_input_file(infile);
3385 /* compute the needed AVStream for each feed */
3386 static void build_feed_streams(void)
3388 FFStream *stream, *feed;
3391 /* gather all streams */
3392 for(stream = first_stream; stream != NULL; stream = stream->next) {
3393 feed = stream->feed;
3395 if (!stream->is_feed) {
3396 /* we handle a stream coming from a feed */
3397 for(i=0;i<stream->nb_streams;i++)
3398 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3403 /* gather all streams */
3404 for(stream = first_stream; stream != NULL; stream = stream->next) {
3405 feed = stream->feed;
3407 if (stream->is_feed) {
3408 for(i=0;i<stream->nb_streams;i++)
3409 stream->feed_streams[i] = i;
3414 /* create feed files if needed */
3415 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3418 if (url_exist(feed->feed_filename)) {
3419 /* See if it matches */
3423 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3424 /* Now see if it matches */
3425 if (s->nb_streams == feed->nb_streams) {
3427 for(i=0;i<s->nb_streams;i++) {
3429 sf = feed->streams[i];
3432 if (sf->index != ss->index ||
3434 printf("Index & Id do not match for stream %d (%s)\n",
3435 i, feed->feed_filename);
3438 AVCodecContext *ccf, *ccs;
3442 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3444 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3445 printf("Codecs do not match for stream %d\n", i);
3447 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3448 printf("Codec bitrates do not match for stream %d\n", i);
3450 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3451 if (CHECK_CODEC(time_base.den) ||
3452 CHECK_CODEC(time_base.num) ||
3453 CHECK_CODEC(width) ||
3454 CHECK_CODEC(height)) {
3455 printf("Codec width, height and framerate do not match for stream %d\n", i);
3458 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3459 if (CHECK_CODEC(sample_rate) ||
3460 CHECK_CODEC(channels) ||
3461 CHECK_CODEC(frame_size)) {
3462 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3466 printf("Unknown codec type\n");
3474 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3475 feed->feed_filename, s->nb_streams, feed->nb_streams);
3477 av_close_input_file(s);
3479 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3480 feed->feed_filename);
3483 if (feed->readonly) {
3484 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3485 feed->feed_filename);
3488 unlink(feed->feed_filename);
3491 if (!url_exist(feed->feed_filename)) {
3492 AVFormatContext s1, *s = &s1;
3494 if (feed->readonly) {
3495 printf("Unable to create feed file '%s' as it is marked readonly\n",
3496 feed->feed_filename);
3500 /* only write the header of the ffm file */
3501 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3502 http_log("Could not open output feed file '%s'\n",
3503 feed->feed_filename);
3506 s->oformat = feed->fmt;
3507 s->nb_streams = feed->nb_streams;
3508 for(i=0;i<s->nb_streams;i++) {
3510 st = feed->streams[i];
3513 av_set_parameters(s, NULL);
3514 if (av_write_header(s) < 0) {
3515 http_log("Container doesn't supports the required parameters\n");
3518 /* XXX: need better api */
3519 av_freep(&s->priv_data);
3522 /* get feed size and write index */
3523 fd = open(feed->feed_filename, O_RDONLY);
3525 http_log("Could not open output feed file '%s'\n",
3526 feed->feed_filename);
3530 feed->feed_write_index = ffm_read_write_index(fd);
3531 feed->feed_size = lseek(fd, 0, SEEK_END);
3532 /* ensure that we do not wrap before the end of file */
3533 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3534 feed->feed_max_size = feed->feed_size;
3540 /* compute the bandwidth used by each stream */
3541 static void compute_bandwidth(void)
3547 for(stream = first_stream; stream != NULL; stream = stream->next) {
3549 for(i=0;i<stream->nb_streams;i++) {
3550 AVStream *st = stream->streams[i];
3551 switch(st->codec->codec_type) {
3552 case CODEC_TYPE_AUDIO:
3553 case CODEC_TYPE_VIDEO:
3554 bandwidth += st->codec->bit_rate;
3560 stream->bandwidth = (bandwidth + 999) / 1000;
3564 static void get_arg(char *buf, int buf_size, const char **pp)
3571 while (isspace(*p)) p++;
3574 if (*p == '\"' || *p == '\'')
3586 if ((q - buf) < buf_size - 1)
3591 if (quote && *p == quote)
3596 /* add a codec and set the default parameters */
3597 static void add_codec(FFStream *stream, AVCodecContext *av)
3601 /* compute default parameters */
3602 switch(av->codec_type) {
3603 case CODEC_TYPE_AUDIO:
3604 if (av->bit_rate == 0)
3605 av->bit_rate = 64000;
3606 if (av->sample_rate == 0)
3607 av->sample_rate = 22050;
3608 if (av->channels == 0)
3611 case CODEC_TYPE_VIDEO:
3612 if (av->bit_rate == 0)
3613 av->bit_rate = 64000;
3614 if (av->time_base.num == 0){
3615 av->time_base.den = 5;
3616 av->time_base.num = 1;
3618 if (av->width == 0 || av->height == 0) {
3622 /* Bitrate tolerance is less for streaming */
3623 if (av->bit_rate_tolerance == 0)
3624 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3625 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3630 if (av->max_qdiff == 0)
3632 av->qcompress = 0.5;
3635 if (!av->nsse_weight)
3636 av->nsse_weight = 8;
3638 av->frame_skip_cmp = FF_CMP_DCTMAX;
3639 av->me_method = ME_EPZS;
3640 av->rc_buffer_aggressivity = 1.0;
3643 av->rc_eq = "tex^qComp";
3644 if (!av->i_quant_factor)
3645 av->i_quant_factor = -0.8;
3646 if (!av->b_quant_factor)
3647 av->b_quant_factor = 1.25;
3648 if (!av->b_quant_offset)
3649 av->b_quant_offset = 1.25;
3650 if (!av->rc_max_rate)
3651 av->rc_max_rate = av->bit_rate * 2;
3653 if (av->rc_max_rate && !av->rc_buffer_size) {
3654 av->rc_buffer_size = av->rc_max_rate;
3663 st = av_mallocz(sizeof(AVStream));
3666 st->codec = avcodec_alloc_context();
3667 stream->streams[stream->nb_streams++] = st;
3668 memcpy(st->codec, av, sizeof(AVCodecContext));
3671 static int opt_audio_codec(const char *arg)
3673 AVCodec *p= avcodec_find_encoder_by_name(arg);
3675 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3676 return CODEC_ID_NONE;
3681 static int opt_video_codec(const char *arg)
3683 AVCodec *p= avcodec_find_encoder_by_name(arg);
3685 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3686 return CODEC_ID_NONE;
3691 /* simplistic plugin support */
3694 static void load_module(const char *filename)
3697 void (*init_func)(void);
3698 dll = dlopen(filename, RTLD_NOW);
3700 fprintf(stderr, "Could not load module '%s' - %s\n",
3701 filename, dlerror());
3705 init_func = dlsym(dll, "ffserver_module_init");
3708 "%s: init function 'ffserver_module_init()' not found\n",
3717 static int opt_default(const char *opt, const char *arg,
3718 AVCodecContext *avctx, int type)
3720 const AVOption *o = NULL;
3721 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3723 o = av_set_string(avctx, opt, arg);
3729 static int parse_ffconfig(const char *filename)
3736 int val, errors, line_num;
3737 FFStream **last_stream, *stream, *redirect;
3738 FFStream **last_feed, *feed;
3739 AVCodecContext audio_enc, video_enc;
3740 int audio_id, video_id;
3742 f = fopen(filename, "r");
3750 first_stream = NULL;
3751 last_stream = &first_stream;
3753 last_feed = &first_feed;
3757 audio_id = CODEC_ID_NONE;
3758 video_id = CODEC_ID_NONE;
3760 if (fgets(line, sizeof(line), f) == NULL)
3766 if (*p == '\0' || *p == '#')
3769 get_arg(cmd, sizeof(cmd), &p);
3771 if (!strcasecmp(cmd, "Port")) {
3772 get_arg(arg, sizeof(arg), &p);
3774 if (val < 1 || val > 65536) {
3775 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3776 filename, line_num, arg);
3779 my_http_addr.sin_port = htons(val);
3780 } else if (!strcasecmp(cmd, "BindAddress")) {
3781 get_arg(arg, sizeof(arg), &p);
3782 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3783 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3784 filename, line_num, arg);
3787 } else if (!strcasecmp(cmd, "NoDaemon")) {
3788 ffserver_daemon = 0;
3789 } else if (!strcasecmp(cmd, "RTSPPort")) {
3790 get_arg(arg, sizeof(arg), &p);
3792 if (val < 1 || val > 65536) {
3793 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3794 filename, line_num, arg);
3797 my_rtsp_addr.sin_port = htons(atoi(arg));
3798 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3799 get_arg(arg, sizeof(arg), &p);
3800 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3801 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3802 filename, line_num, arg);
3805 } else if (!strcasecmp(cmd, "MaxClients")) {
3806 get_arg(arg, sizeof(arg), &p);
3808 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3809 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3810 filename, line_num, arg);
3813 nb_max_connections = val;
3815 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3817 get_arg(arg, sizeof(arg), &p);
3819 if (llval < 10 || llval > 10000000) {
3820 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3821 filename, line_num, arg);
3824 max_bandwidth = llval;
3825 } else if (!strcasecmp(cmd, "CustomLog")) {
3826 if (!ffserver_debug)
3827 get_arg(logfilename, sizeof(logfilename), &p);
3828 } else if (!strcasecmp(cmd, "<Feed")) {
3829 /*********************************************/
3830 /* Feed related options */
3832 if (stream || feed) {
3833 fprintf(stderr, "%s:%d: Already in a tag\n",
3834 filename, line_num);
3836 feed = av_mallocz(sizeof(FFStream));
3837 /* add in stream list */
3838 *last_stream = feed;
3839 last_stream = &feed->next;
3840 /* add in feed list */
3842 last_feed = &feed->next_feed;
3844 get_arg(feed->filename, sizeof(feed->filename), &p);
3845 q = strrchr(feed->filename, '>');
3848 feed->fmt = guess_format("ffm", NULL, NULL);
3849 /* defaut feed file */
3850 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3851 "/tmp/%s.ffm", feed->filename);
3852 feed->feed_max_size = 5 * 1024 * 1024;
3854 feed->feed = feed; /* self feeding :-) */
3856 } else if (!strcasecmp(cmd, "Launch")) {
3860 feed->child_argv = av_mallocz(64 * sizeof(char *));
3862 for (i = 0; i < 62; i++) {
3863 get_arg(arg, sizeof(arg), &p);
3867 feed->child_argv[i] = av_strdup(arg);
3870 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3872 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3874 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3875 inet_ntoa(my_http_addr.sin_addr),
3876 ntohs(my_http_addr.sin_port), feed->filename);
3881 fprintf(stdout, "Launch commandline: ");
3882 for (j = 0; j <= i; j++)
3883 fprintf(stdout, "%s ", feed->child_argv[j]);
3884 fprintf(stdout, "\n");
3887 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3889 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3891 } else if (stream) {
3892 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3894 } else if (!strcasecmp(cmd, "File")) {
3896 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3898 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3899 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3904 get_arg(arg, sizeof(arg), &p);
3906 fsize = strtod(p1, &p1);
3907 switch(toupper(*p1)) {
3912 fsize *= 1024 * 1024;
3915 fsize *= 1024 * 1024 * 1024;
3918 feed->feed_max_size = (int64_t)fsize;
3920 } else if (!strcasecmp(cmd, "</Feed>")) {
3922 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3923 filename, line_num);
3927 } else if (!strcasecmp(cmd, "<Stream")) {
3928 /*********************************************/
3929 /* Stream related options */
3931 if (stream || feed) {
3932 fprintf(stderr, "%s:%d: Already in a tag\n",
3933 filename, line_num);
3935 const AVClass *class;
3936 stream = av_mallocz(sizeof(FFStream));
3937 *last_stream = stream;
3938 last_stream = &stream->next;
3940 get_arg(stream->filename, sizeof(stream->filename), &p);
3941 q = strrchr(stream->filename, '>');
3944 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3945 /* fetch avclass so AVOption works
3946 * FIXME try to use avcodec_get_context_defaults2
3947 * without changing defaults too much */
3948 avcodec_get_context_defaults(&video_enc);
3949 class = video_enc.av_class;
3950 memset(&audio_enc, 0, sizeof(AVCodecContext));
3951 memset(&video_enc, 0, sizeof(AVCodecContext));
3952 audio_enc.av_class = class;
3953 video_enc.av_class = class;
3954 audio_id = CODEC_ID_NONE;
3955 video_id = CODEC_ID_NONE;
3957 audio_id = stream->fmt->audio_codec;
3958 video_id = stream->fmt->video_codec;
3961 } else if (!strcasecmp(cmd, "Feed")) {
3962 get_arg(arg, sizeof(arg), &p);
3967 while (sfeed != NULL) {
3968 if (!strcmp(sfeed->filename, arg))
3970 sfeed = sfeed->next_feed;
3973 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3974 filename, line_num, arg);
3976 stream->feed = sfeed;
3978 } else if (!strcasecmp(cmd, "Format")) {
3979 get_arg(arg, sizeof(arg), &p);
3981 if (!strcmp(arg, "status")) {
3982 stream->stream_type = STREAM_TYPE_STATUS;
3985 stream->stream_type = STREAM_TYPE_LIVE;
3986 /* jpeg cannot be used here, so use single frame jpeg */
3987 if (!strcmp(arg, "jpeg"))
3988 strcpy(arg, "mjpeg");
3989 stream->fmt = guess_stream_format(arg, NULL, NULL);
3991 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3992 filename, line_num, arg);
3997 audio_id = stream->fmt->audio_codec;
3998 video_id = stream->fmt->video_codec;
4001 } else if (!strcasecmp(cmd, "InputFormat")) {
4002 get_arg(arg, sizeof(arg), &p);
4003 stream->ifmt = av_find_input_format(arg);
4004 if (!stream->ifmt) {
4005 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4006 filename, line_num, arg);
4008 } else if (!strcasecmp(cmd, "FaviconURL")) {
4009 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4010 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4012 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4013 filename, line_num);
4016 } else if (!strcasecmp(cmd, "Author")) {
4018 get_arg(stream->author, sizeof(stream->author), &p);
4019 } else if (!strcasecmp(cmd, "Comment")) {
4021 get_arg(stream->comment, sizeof(stream->comment), &p);
4022 } else if (!strcasecmp(cmd, "Copyright")) {
4024 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4025 } else if (!strcasecmp(cmd, "Title")) {
4027 get_arg(stream->title, sizeof(stream->title), &p);
4028 } else if (!strcasecmp(cmd, "Preroll")) {
4029 get_arg(arg, sizeof(arg), &p);
4031 stream->prebuffer = atof(arg) * 1000;
4032 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4034 stream->send_on_key = 1;
4035 } else if (!strcasecmp(cmd, "AudioCodec")) {
4036 get_arg(arg, sizeof(arg), &p);
4037 audio_id = opt_audio_codec(arg);
4038 if (audio_id == CODEC_ID_NONE) {
4039 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4040 filename, line_num, arg);
4043 } else if (!strcasecmp(cmd, "VideoCodec")) {
4044 get_arg(arg, sizeof(arg), &p);
4045 video_id = opt_video_codec(arg);
4046 if (video_id == CODEC_ID_NONE) {
4047 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4048 filename, line_num, arg);
4051 } else if (!strcasecmp(cmd, "MaxTime")) {
4052 get_arg(arg, sizeof(arg), &p);
4054 stream->max_time = atof(arg) * 1000;
4055 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4056 get_arg(arg, sizeof(arg), &p);
4058 audio_enc.bit_rate = atoi(arg) * 1000;
4059 } else if (!strcasecmp(cmd, "AudioChannels")) {
4060 get_arg(arg, sizeof(arg), &p);
4062 audio_enc.channels = atoi(arg);
4063 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4064 get_arg(arg, sizeof(arg), &p);
4066 audio_enc.sample_rate = atoi(arg);
4067 } else if (!strcasecmp(cmd, "AudioQuality")) {
4068 get_arg(arg, sizeof(arg), &p);
4070 // audio_enc.quality = atof(arg) * 1000;
4072 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4074 int minrate, maxrate;
4076 get_arg(arg, sizeof(arg), &p);
4078 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4079 video_enc.rc_min_rate = minrate * 1000;
4080 video_enc.rc_max_rate = maxrate * 1000;
4082 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4083 filename, line_num, arg);
4087 } else if (!strcasecmp(cmd, "Debug")) {
4089 get_arg(arg, sizeof(arg), &p);
4090 video_enc.debug = strtol(arg,0,0);
4092 } else if (!strcasecmp(cmd, "Strict")) {
4094 get_arg(arg, sizeof(arg), &p);
4095 video_enc.strict_std_compliance = atoi(arg);
4097 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4099 get_arg(arg, sizeof(arg), &p);
4100 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4102 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4104 get_arg(arg, sizeof(arg), &p);
4105 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4107 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4108 get_arg(arg, sizeof(arg), &p);
4110 video_enc.bit_rate = atoi(arg) * 1000;
4112 } else if (!strcasecmp(cmd, "VideoSize")) {
4113 get_arg(arg, sizeof(arg), &p);
4115 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4116 if ((video_enc.width % 16) != 0 ||
4117 (video_enc.height % 16) != 0) {
4118 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4119 filename, line_num);
4123 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4124 get_arg(arg, sizeof(arg), &p);
4126 AVRational frame_rate;
4127 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4128 fprintf(stderr, "Incorrect frame rate\n");
4131 video_enc.time_base.num = frame_rate.den;
4132 video_enc.time_base.den = frame_rate.num;
4135 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4136 get_arg(arg, sizeof(arg), &p);
4138 video_enc.gop_size = atoi(arg);
4139 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4141 video_enc.gop_size = 1;
4142 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4144 video_enc.mb_decision = FF_MB_DECISION_BITS;
4145 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4147 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4148 video_enc.flags |= CODEC_FLAG_4MV;
4150 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4151 !strcasecmp(cmd, "AVOptionAudio")) {
4153 AVCodecContext *avctx;
4155 get_arg(arg, sizeof(arg), &p);
4156 get_arg(arg2, sizeof(arg2), &p);
4157 if (!strcasecmp(cmd, "AVOptionVideo")) {
4159 type = AV_OPT_FLAG_VIDEO_PARAM;
4162 type = AV_OPT_FLAG_AUDIO_PARAM;
4164 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4165 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4168 } else if (!strcasecmp(cmd, "VideoTag")) {
4169 get_arg(arg, sizeof(arg), &p);
4170 if ((strlen(arg) == 4) && stream)
4171 video_enc.codec_tag = ff_get_fourcc(arg);
4172 } else if (!strcasecmp(cmd, "BitExact")) {
4174 video_enc.flags |= CODEC_FLAG_BITEXACT;
4175 } else if (!strcasecmp(cmd, "DctFastint")) {
4177 video_enc.dct_algo = FF_DCT_FASTINT;
4178 } else if (!strcasecmp(cmd, "IdctSimple")) {
4180 video_enc.idct_algo = FF_IDCT_SIMPLE;
4181 } else if (!strcasecmp(cmd, "Qscale")) {
4182 get_arg(arg, sizeof(arg), &p);
4184 video_enc.flags |= CODEC_FLAG_QSCALE;
4185 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4187 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4188 get_arg(arg, sizeof(arg), &p);
4190 video_enc.max_qdiff = atoi(arg);
4191 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4192 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4193 filename, line_num);
4197 } else if (!strcasecmp(cmd, "VideoQMax")) {
4198 get_arg(arg, sizeof(arg), &p);
4200 video_enc.qmax = atoi(arg);
4201 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4202 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4203 filename, line_num);
4207 } else if (!strcasecmp(cmd, "VideoQMin")) {
4208 get_arg(arg, sizeof(arg), &p);
4210 video_enc.qmin = atoi(arg);
4211 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4212 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4213 filename, line_num);
4217 } else if (!strcasecmp(cmd, "LumaElim")) {
4218 get_arg(arg, sizeof(arg), &p);
4220 video_enc.luma_elim_threshold = atoi(arg);
4221 } else if (!strcasecmp(cmd, "ChromaElim")) {
4222 get_arg(arg, sizeof(arg), &p);
4224 video_enc.chroma_elim_threshold = atoi(arg);
4225 } else if (!strcasecmp(cmd, "LumiMask")) {
4226 get_arg(arg, sizeof(arg), &p);
4228 video_enc.lumi_masking = atof(arg);
4229 } else if (!strcasecmp(cmd, "DarkMask")) {
4230 get_arg(arg, sizeof(arg), &p);
4232 video_enc.dark_masking = atof(arg);
4233 } else if (!strcasecmp(cmd, "NoVideo")) {
4234 video_id = CODEC_ID_NONE;
4235 } else if (!strcasecmp(cmd, "NoAudio")) {
4236 audio_id = CODEC_ID_NONE;
4237 } else if (!strcasecmp(cmd, "ACL")) {
4240 get_arg(arg, sizeof(arg), &p);
4241 if (strcasecmp(arg, "allow") == 0)
4242 acl.action = IP_ALLOW;
4243 else if (strcasecmp(arg, "deny") == 0)
4244 acl.action = IP_DENY;
4246 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4247 filename, line_num, arg);
4251 get_arg(arg, sizeof(arg), &p);
4253 if (resolve_host(&acl.first, arg) != 0) {
4254 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4255 filename, line_num, arg);
4258 acl.last = acl.first;
4260 get_arg(arg, sizeof(arg), &p);
4263 if (resolve_host(&acl.last, arg) != 0) {
4264 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4265 filename, line_num, arg);
4271 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4272 IPAddressACL **naclp = 0;
4278 naclp = &stream->acl;
4282 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4283 filename, line_num);
4289 naclp = &(*naclp)->next;
4294 } else if (!strcasecmp(cmd, "RTSPOption")) {
4295 get_arg(arg, sizeof(arg), &p);
4297 av_freep(&stream->rtsp_option);
4298 stream->rtsp_option = av_strdup(arg);
4300 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4301 get_arg(arg, sizeof(arg), &p);
4303 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4304 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4305 filename, line_num, arg);
4308 stream->is_multicast = 1;
4309 stream->loop = 1; /* default is looping */
4311 } else if (!strcasecmp(cmd, "MulticastPort")) {
4312 get_arg(arg, sizeof(arg), &p);
4314 stream->multicast_port = atoi(arg);
4315 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4316 get_arg(arg, sizeof(arg), &p);
4318 stream->multicast_ttl = atoi(arg);
4319 } else if (!strcasecmp(cmd, "NoLoop")) {
4322 } else if (!strcasecmp(cmd, "</Stream>")) {
4324 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4325 filename, line_num);
4328 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4329 if (audio_id != CODEC_ID_NONE) {
4330 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4331 audio_enc.codec_id = audio_id;
4332 add_codec(stream, &audio_enc);
4334 if (video_id != CODEC_ID_NONE) {
4335 video_enc.codec_type = CODEC_TYPE_VIDEO;
4336 video_enc.codec_id = video_id;
4337 add_codec(stream, &video_enc);
4342 } else if (!strcasecmp(cmd, "<Redirect")) {
4343 /*********************************************/
4345 if (stream || feed || redirect) {
4346 fprintf(stderr, "%s:%d: Already in a tag\n",
4347 filename, line_num);
4350 redirect = av_mallocz(sizeof(FFStream));
4351 *last_stream = redirect;
4352 last_stream = &redirect->next;
4354 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4355 q = strrchr(redirect->filename, '>');
4358 redirect->stream_type = STREAM_TYPE_REDIRECT;
4360 } else if (!strcasecmp(cmd, "URL")) {
4362 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4363 } else if (!strcasecmp(cmd, "</Redirect>")) {
4365 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4366 filename, line_num);
4369 if (!redirect->feed_filename[0]) {
4370 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4371 filename, line_num);
4376 } else if (!strcasecmp(cmd, "LoadModule")) {
4377 get_arg(arg, sizeof(arg), &p);
4381 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4382 filename, line_num, arg);
4386 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4387 filename, line_num, cmd);
4399 static void handle_child_exit(int sig)
4404 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4407 for (feed = first_feed; feed; feed = feed->next) {
4408 if (feed->pid == pid) {
4409 int uptime = time(0) - feed->pid_start;
4412 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4415 /* Turn off any more restarts */
4416 feed->child_argv = 0;
4421 need_to_start_children = 1;
4424 static void opt_debug()
4427 ffserver_daemon = 0;
4428 logfilename[0] = '-';
4431 static void opt_show_help(void)
4433 printf("usage: ffserver [options]\n"
4434 "Hyper fast multi format Audio/Video streaming server\n");
4436 show_help_options(options, "Main options:\n", 0, 0);
4439 static const OptionDef options[] = {
4440 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4441 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4442 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4443 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4444 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4445 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4446 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4450 int main(int argc, char **argv)
4452 struct sigaction sigact;
4458 config_filename = "/etc/ffserver.conf";
4460 my_program_name = argv[0];
4461 my_program_dir = getcwd(0, 0);
4462 ffserver_daemon = 1;
4464 parse_options(argc, argv, options, NULL);
4466 unsetenv("http_proxy"); /* Kill the http_proxy */
4468 av_init_random(av_gettime() + (getpid() << 16), &random_state);
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");