2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
28 #include "libavutil/random.h"
29 #include "libavutil/avstring.h"
30 #include "libavformat/avformat.h"
31 #include "libavformat/network.h"
32 #include "libavformat/os_support.h"
33 #include "libavformat/rtp.h"
34 #include "libavformat/rtsp.h"
35 #include "libavcodec/opt.h"
39 #include <sys/ioctl.h>
45 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
57 const char program_name[] = "FFserver";
58 const int program_birth_year = 2000;
60 static const OptionDef options[];
62 /* maximum number of simultaneous HTTP connections */
63 #define HTTP_MAX_CONNECTIONS 2000
66 HTTPSTATE_WAIT_REQUEST,
67 HTTPSTATE_SEND_HEADER,
68 HTTPSTATE_SEND_DATA_HEADER,
69 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
70 HTTPSTATE_SEND_DATA_TRAILER,
71 HTTPSTATE_RECEIVE_DATA,
72 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
75 RTSPSTATE_WAIT_REQUEST,
77 RTSPSTATE_SEND_PACKET,
80 static const char *http_state[] = {
96 #define IOBUFFER_INIT_SIZE 8192
98 /* timeouts are in ms */
99 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
100 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
102 #define SYNC_TIMEOUT (10 * 1000)
105 int64_t count1, count2;
106 int64_t time1, time2;
109 /* context associated with one connection */
110 typedef struct HTTPContext {
111 enum HTTPState state;
112 int fd; /* socket file descriptor */
113 struct sockaddr_in from_addr; /* origin */
114 struct pollfd *poll_entry; /* used when polling */
116 uint8_t *buffer_ptr, *buffer_end;
119 struct HTTPContext *next;
120 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
124 /* input format handling */
125 AVFormatContext *fmt_in;
126 int64_t start_time; /* In milliseconds - this wraps fairly often */
127 int64_t first_pts; /* initial pts value */
128 int64_t cur_pts; /* current pts value from the stream in us */
129 int64_t cur_frame_duration; /* duration of the current frame in us */
130 int cur_frame_bytes; /* output frame size, needed to compute
131 the time at which we send each
133 int pts_stream_index; /* stream we choose as clock reference */
134 int64_t cur_clock; /* current clock reference value in us */
135 /* output format handling */
136 struct FFStream *stream;
137 /* -1 is invalid stream */
138 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
139 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
141 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
142 int last_packet_sent; /* true if last data packet was sent */
144 DataRateData datarate;
151 int is_packetized; /* if true, the stream is packetized */
152 int packet_stream_index; /* current stream for output in state machine */
154 /* RTSP state specific */
155 uint8_t *pb_buffer; /* XXX: use that in all the code */
157 int seq; /* RTSP sequence number */
159 /* RTP state specific */
160 enum RTSPProtocol rtp_protocol;
161 char session_id[32]; /* session id */
162 AVFormatContext *rtp_ctx[MAX_STREAMS];
164 /* RTP/UDP specific */
165 URLContext *rtp_handles[MAX_STREAMS];
167 /* RTP/TCP specific */
168 struct HTTPContext *rtsp_c;
169 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
172 /* each generated stream is described here */
176 STREAM_TYPE_REDIRECT,
179 enum IPAddressAction {
184 typedef struct IPAddressACL {
185 struct IPAddressACL *next;
186 enum IPAddressAction action;
187 /* These are in host order */
188 struct in_addr first;
192 /* description of each stream of the ffserver.conf file */
193 typedef struct FFStream {
194 enum StreamType stream_type;
195 char filename[1024]; /* stream filename */
196 struct FFStream *feed; /* feed we are using (can be null if
198 AVFormatParameters *ap_in; /* input parameters */
199 AVInputFormat *ifmt; /* if non NULL, force input format */
203 int prebuffer; /* Number of millseconds early to start */
204 int64_t max_time; /* Number of milliseconds to run */
206 AVStream *streams[MAX_STREAMS];
207 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
208 char feed_filename[1024]; /* file name of the feed storage, or
209 input file name for a stream */
214 pid_t pid; /* Of ffmpeg process */
215 time_t pid_start; /* Of ffmpeg process */
217 struct FFStream *next;
218 unsigned bandwidth; /* bandwidth, in kbits/s */
221 /* multicast specific */
223 struct in_addr multicast_ip;
224 int multicast_port; /* first port used for multicast */
226 int loop; /* if true, send the stream in loops (only meaningful if file) */
229 int feed_opened; /* true if someone is writing to the feed */
230 int is_feed; /* true if it is a feed */
231 int readonly; /* True if writing is prohibited to the file */
233 int64_t bytes_served;
234 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
235 int64_t feed_write_index; /* current write position in feed (it wraps around) */
236 int64_t feed_size; /* current size of feed */
237 struct FFStream *next_feed;
240 typedef struct FeedData {
241 long long data_count;
242 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
245 static struct sockaddr_in my_http_addr;
246 static struct sockaddr_in my_rtsp_addr;
248 static char logfilename[1024];
249 static HTTPContext *first_http_ctx;
250 static FFStream *first_feed; /* contains only feeds */
251 static FFStream *first_stream; /* contains all streams, including feeds */
253 static void new_connection(int server_fd, int is_rtsp);
254 static void close_connection(HTTPContext *c);
257 static int handle_connection(HTTPContext *c);
258 static int http_parse_request(HTTPContext *c);
259 static int http_send_data(HTTPContext *c);
260 static void compute_status(HTTPContext *c);
261 static int open_input_stream(HTTPContext *c, const char *info);
262 static int http_start_receive_data(HTTPContext *c);
263 static int http_receive_data(HTTPContext *c);
266 static int rtsp_parse_request(HTTPContext *c);
267 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
268 static void rtsp_cmd_options(HTTPContext *c, const char *url);
269 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
270 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
271 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
275 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
276 struct in_addr my_ip);
279 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
280 FFStream *stream, const char *session_id,
281 enum RTSPProtocol rtp_protocol);
282 static int rtp_new_av_stream(HTTPContext *c,
283 int stream_index, struct sockaddr_in *dest_addr,
284 HTTPContext *rtsp_c);
286 static const char *my_program_name;
287 static const char *my_program_dir;
289 static const char *config_filename;
290 static int ffserver_debug;
291 static int ffserver_daemon;
292 static int no_launch;
293 static int need_to_start_children;
295 static int nb_max_connections;
296 static int nb_connections;
298 static uint64_t max_bandwidth;
299 static uint64_t current_bandwidth;
301 static int64_t cur_time; // Making this global saves on passing it around everywhere
303 static AVRandomState random_state;
305 static FILE *logfile = NULL;
307 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
313 vfprintf(logfile, fmt, ap);
319 static char *ctime1(char *buf2)
327 p = buf2 + strlen(p) - 1;
333 static void log_connection(HTTPContext *c)
340 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
341 inet_ntoa(c->from_addr.sin_addr),
342 ctime1(buf2), c->method, c->url,
343 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
346 static void update_datarate(DataRateData *drd, int64_t count)
348 if (!drd->time1 && !drd->count1) {
349 drd->time1 = drd->time2 = cur_time;
350 drd->count1 = drd->count2 = count;
351 } else if (cur_time - drd->time2 > 5000) {
352 drd->time1 = drd->time2;
353 drd->count1 = drd->count2;
354 drd->time2 = cur_time;
359 /* In bytes per second */
360 static int compute_datarate(DataRateData *drd, int64_t count)
362 if (cur_time == drd->time1)
365 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
369 static void start_children(FFStream *feed)
374 for (; feed; feed = feed->next) {
375 if (feed->child_argv && !feed->pid) {
376 feed->pid_start = time(0);
381 http_log("Unable to create children\n");
390 for (i = 3; i < 256; i++)
393 if (!ffserver_debug) {
394 i = open("/dev/null", O_RDWR);
403 av_strlcpy(pathname, my_program_name, sizeof(pathname));
405 slash = strrchr(pathname, '/');
410 strcpy(slash, "ffmpeg");
412 /* This is needed to make relative pathnames work */
413 chdir(my_program_dir);
415 signal(SIGPIPE, SIG_DFL);
417 execvp(pathname, feed->child_argv);
425 /* open a listening socket */
426 static int socket_open_listen(struct sockaddr_in *my_addr)
430 server_fd = socket(AF_INET,SOCK_STREAM,0);
437 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
439 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
441 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
443 closesocket(server_fd);
447 if (listen (server_fd, 5) < 0) {
449 closesocket(server_fd);
452 ff_socket_nonblock(server_fd, 1);
457 /* start all multicast streams */
458 static void start_multicast(void)
463 struct sockaddr_in dest_addr;
464 int default_port, stream_index;
467 for(stream = first_stream; stream != NULL; stream = stream->next) {
468 if (stream->is_multicast) {
469 /* open the RTP connection */
470 snprintf(session_id, sizeof(session_id), "%08x%08x",
471 av_random(&random_state), av_random(&random_state));
473 /* choose a port if none given */
474 if (stream->multicast_port == 0) {
475 stream->multicast_port = default_port;
479 dest_addr.sin_family = AF_INET;
480 dest_addr.sin_addr = stream->multicast_ip;
481 dest_addr.sin_port = htons(stream->multicast_port);
483 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
484 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
488 if (open_input_stream(rtp_c, "") < 0) {
489 http_log("Could not open input stream for stream '%s'\n",
494 /* open each RTP stream */
495 for(stream_index = 0; stream_index < stream->nb_streams;
497 dest_addr.sin_port = htons(stream->multicast_port +
499 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
500 http_log("Could not open output stream '%s/streamid=%d'\n",
501 stream->filename, stream_index);
506 /* change state to send data */
507 rtp_c->state = HTTPSTATE_SEND_DATA;
512 /* main loop of the http server */
513 static int http_server(void)
515 int server_fd, ret, rtsp_server_fd, delay, delay1;
516 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
517 HTTPContext *c, *c_next;
519 server_fd = socket_open_listen(&my_http_addr);
523 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
524 if (rtsp_server_fd < 0)
527 http_log("ffserver started.\n");
529 start_children(first_feed);
531 first_http_ctx = NULL;
537 poll_entry = poll_table;
538 poll_entry->fd = server_fd;
539 poll_entry->events = POLLIN;
542 poll_entry->fd = rtsp_server_fd;
543 poll_entry->events = POLLIN;
546 /* wait for events on each HTTP handle */
553 case HTTPSTATE_SEND_HEADER:
554 case RTSPSTATE_SEND_REPLY:
555 case RTSPSTATE_SEND_PACKET:
556 c->poll_entry = poll_entry;
558 poll_entry->events = POLLOUT;
561 case HTTPSTATE_SEND_DATA_HEADER:
562 case HTTPSTATE_SEND_DATA:
563 case HTTPSTATE_SEND_DATA_TRAILER:
564 if (!c->is_packetized) {
565 /* for TCP, we output as much as we can (may need to put a limit) */
566 c->poll_entry = poll_entry;
568 poll_entry->events = POLLOUT;
571 /* when ffserver is doing the timing, we work by
572 looking at which packet need to be sent every
574 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
579 case HTTPSTATE_WAIT_REQUEST:
580 case HTTPSTATE_RECEIVE_DATA:
581 case HTTPSTATE_WAIT_FEED:
582 case RTSPSTATE_WAIT_REQUEST:
583 /* need to catch errors */
584 c->poll_entry = poll_entry;
586 poll_entry->events = POLLIN;/* Maybe this will work */
590 c->poll_entry = NULL;
596 /* wait for an event on one connection. We poll at least every
597 second to handle timeouts */
599 ret = poll(poll_table, poll_entry - poll_table, delay);
600 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
601 ff_neterrno() != FF_NETERROR(EINTR))
605 cur_time = av_gettime() / 1000;
607 if (need_to_start_children) {
608 need_to_start_children = 0;
609 start_children(first_feed);
612 /* now handle the events */
613 for(c = first_http_ctx; c != NULL; c = c_next) {
615 if (handle_connection(c) < 0) {
616 /* close and free the connection */
622 poll_entry = poll_table;
623 /* new HTTP connection request ? */
624 if (poll_entry->revents & POLLIN)
625 new_connection(server_fd, 0);
627 /* new RTSP connection request ? */
628 if (poll_entry->revents & POLLIN)
629 new_connection(rtsp_server_fd, 1);
633 /* start waiting for a new HTTP/RTSP request */
634 static void start_wait_request(HTTPContext *c, int is_rtsp)
636 c->buffer_ptr = c->buffer;
637 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
640 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
641 c->state = RTSPSTATE_WAIT_REQUEST;
643 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
644 c->state = HTTPSTATE_WAIT_REQUEST;
648 static void new_connection(int server_fd, int is_rtsp)
650 struct sockaddr_in from_addr;
652 HTTPContext *c = NULL;
654 len = sizeof(from_addr);
655 fd = accept(server_fd, (struct sockaddr *)&from_addr,
658 http_log("error during accept %s\n", strerror(errno));
661 ff_socket_nonblock(fd, 1);
663 /* XXX: should output a warning page when coming
664 close to the connection limit */
665 if (nb_connections >= nb_max_connections)
668 /* add a new connection */
669 c = av_mallocz(sizeof(HTTPContext));
674 c->poll_entry = NULL;
675 c->from_addr = from_addr;
676 c->buffer_size = IOBUFFER_INIT_SIZE;
677 c->buffer = av_malloc(c->buffer_size);
681 c->next = first_http_ctx;
685 start_wait_request(c, is_rtsp);
697 static void close_connection(HTTPContext *c)
699 HTTPContext **cp, *c1;
701 AVFormatContext *ctx;
705 /* remove connection from list */
706 cp = &first_http_ctx;
707 while ((*cp) != NULL) {
715 /* remove references, if any (XXX: do it faster) */
716 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
721 /* remove connection associated resources */
725 /* close each frame parser */
726 for(i=0;i<c->fmt_in->nb_streams;i++) {
727 st = c->fmt_in->streams[i];
728 if (st->codec->codec)
729 avcodec_close(st->codec);
731 av_close_input_file(c->fmt_in);
734 /* free RTP output streams if any */
737 nb_streams = c->stream->nb_streams;
739 for(i=0;i<nb_streams;i++) {
742 av_write_trailer(ctx);
745 h = c->rtp_handles[i];
752 if (!c->last_packet_sent) {
755 if (url_open_dyn_buf(&ctx->pb) >= 0) {
756 av_write_trailer(ctx);
757 av_freep(&c->pb_buffer);
758 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
763 for(i=0; i<ctx->nb_streams; i++)
764 av_free(ctx->streams[i]);
766 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
767 current_bandwidth -= c->stream->bandwidth;
769 /* signal that there is no feed if we are the feeder socket */
770 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
771 c->stream->feed_opened = 0;
775 av_freep(&c->pb_buffer);
776 av_freep(&c->packet_buffer);
782 static int handle_connection(HTTPContext *c)
787 case HTTPSTATE_WAIT_REQUEST:
788 case RTSPSTATE_WAIT_REQUEST:
790 if ((c->timeout - cur_time) < 0)
792 if (c->poll_entry->revents & (POLLERR | POLLHUP))
795 /* no need to read if no events */
796 if (!(c->poll_entry->revents & POLLIN))
800 len = recv(c->fd, c->buffer_ptr, 1, 0);
802 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
803 ff_neterrno() != FF_NETERROR(EINTR))
805 } else if (len == 0) {
808 /* search for end of request. */
810 c->buffer_ptr += len;
812 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
813 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
814 /* request found : parse it and reply */
815 if (c->state == HTTPSTATE_WAIT_REQUEST) {
816 ret = http_parse_request(c);
818 ret = rtsp_parse_request(c);
822 } else if (ptr >= c->buffer_end) {
823 /* request too long: cannot do anything */
825 } else goto read_loop;
829 case HTTPSTATE_SEND_HEADER:
830 if (c->poll_entry->revents & (POLLERR | POLLHUP))
833 /* no need to write if no events */
834 if (!(c->poll_entry->revents & POLLOUT))
836 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
838 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
839 ff_neterrno() != FF_NETERROR(EINTR)) {
840 /* error : close connection */
841 av_freep(&c->pb_buffer);
845 c->buffer_ptr += len;
847 c->stream->bytes_served += len;
848 c->data_count += len;
849 if (c->buffer_ptr >= c->buffer_end) {
850 av_freep(&c->pb_buffer);
854 /* all the buffer was sent : synchronize to the incoming stream */
855 c->state = HTTPSTATE_SEND_DATA_HEADER;
856 c->buffer_ptr = c->buffer_end = c->buffer;
861 case HTTPSTATE_SEND_DATA:
862 case HTTPSTATE_SEND_DATA_HEADER:
863 case HTTPSTATE_SEND_DATA_TRAILER:
864 /* for packetized output, we consider we can always write (the
865 input streams sets the speed). It may be better to verify
866 that we do not rely too much on the kernel queues */
867 if (!c->is_packetized) {
868 if (c->poll_entry->revents & (POLLERR | POLLHUP))
871 /* no need to read if no events */
872 if (!(c->poll_entry->revents & POLLOUT))
875 if (http_send_data(c) < 0)
877 /* close connection if trailer sent */
878 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
881 case HTTPSTATE_RECEIVE_DATA:
882 /* no need to read if no events */
883 if (c->poll_entry->revents & (POLLERR | POLLHUP))
885 if (!(c->poll_entry->revents & POLLIN))
887 if (http_receive_data(c) < 0)
890 case HTTPSTATE_WAIT_FEED:
891 /* no need to read if no events */
892 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
895 /* nothing to do, we'll be waken up by incoming feed packets */
898 case RTSPSTATE_SEND_REPLY:
899 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
900 av_freep(&c->pb_buffer);
903 /* no need to write if no events */
904 if (!(c->poll_entry->revents & POLLOUT))
906 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
908 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
909 ff_neterrno() != FF_NETERROR(EINTR)) {
910 /* error : close connection */
911 av_freep(&c->pb_buffer);
915 c->buffer_ptr += len;
916 c->data_count += len;
917 if (c->buffer_ptr >= c->buffer_end) {
918 /* all the buffer was sent : wait for a new request */
919 av_freep(&c->pb_buffer);
920 start_wait_request(c, 1);
924 case RTSPSTATE_SEND_PACKET:
925 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
926 av_freep(&c->packet_buffer);
929 /* no need to write if no events */
930 if (!(c->poll_entry->revents & POLLOUT))
932 len = send(c->fd, c->packet_buffer_ptr,
933 c->packet_buffer_end - c->packet_buffer_ptr, 0);
935 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
936 ff_neterrno() != FF_NETERROR(EINTR)) {
937 /* error : close connection */
938 av_freep(&c->packet_buffer);
942 c->packet_buffer_ptr += len;
943 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
944 /* all the buffer was sent : wait for a new request */
945 av_freep(&c->packet_buffer);
946 c->state = RTSPSTATE_WAIT_REQUEST;
950 case HTTPSTATE_READY:
959 static int extract_rates(char *rates, int ratelen, const char *request)
963 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
964 if (strncasecmp(p, "Pragma:", 7) == 0) {
965 const char *q = p + 7;
967 while (*q && *q != '\n' && isspace(*q))
970 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
976 memset(rates, 0xff, ratelen);
979 while (*q && *q != '\n' && *q != ':')
982 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
986 if (stream_no < ratelen && stream_no >= 0)
987 rates[stream_no] = rate_no;
989 while (*q && *q != '\n' && !isspace(*q))
1006 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1009 int best_bitrate = 100000000;
1012 for (i = 0; i < feed->nb_streams; i++) {
1013 AVCodecContext *feed_codec = feed->streams[i]->codec;
1015 if (feed_codec->codec_id != codec->codec_id ||
1016 feed_codec->sample_rate != codec->sample_rate ||
1017 feed_codec->width != codec->width ||
1018 feed_codec->height != codec->height)
1021 /* Potential stream */
1023 /* We want the fastest stream less than bit_rate, or the slowest
1024 * faster than bit_rate
1027 if (feed_codec->bit_rate <= bit_rate) {
1028 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1029 best_bitrate = feed_codec->bit_rate;
1033 if (feed_codec->bit_rate < best_bitrate) {
1034 best_bitrate = feed_codec->bit_rate;
1043 static int modify_current_stream(HTTPContext *c, char *rates)
1046 FFStream *req = c->stream;
1047 int action_required = 0;
1049 /* Not much we can do for a feed */
1053 for (i = 0; i < req->nb_streams; i++) {
1054 AVCodecContext *codec = req->streams[i]->codec;
1058 c->switch_feed_streams[i] = req->feed_streams[i];
1061 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1064 /* Wants off or slow */
1065 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1067 /* This doesn't work well when it turns off the only stream! */
1068 c->switch_feed_streams[i] = -2;
1069 c->feed_streams[i] = -2;
1074 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1075 action_required = 1;
1078 return action_required;
1082 static void do_switch_stream(HTTPContext *c, int i)
1084 if (c->switch_feed_streams[i] >= 0) {
1086 c->feed_streams[i] = c->switch_feed_streams[i];
1089 /* Now update the stream */
1091 c->switch_feed_streams[i] = -1;
1094 /* XXX: factorize in utils.c ? */
1095 /* XXX: take care with different space meaning */
1096 static void skip_spaces(const char **pp)
1100 while (*p == ' ' || *p == '\t')
1105 static void get_word(char *buf, int buf_size, const char **pp)
1113 while (!isspace(*p) && *p != '\0') {
1114 if ((q - buf) < buf_size - 1)
1123 static int validate_acl(FFStream *stream, HTTPContext *c)
1125 enum IPAddressAction last_action = IP_DENY;
1127 struct in_addr *src = &c->from_addr.sin_addr;
1128 unsigned long src_addr = src->s_addr;
1130 for (acl = stream->acl; acl; acl = acl->next) {
1131 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1132 return (acl->action == IP_ALLOW) ? 1 : 0;
1133 last_action = acl->action;
1136 /* Nothing matched, so return not the last action */
1137 return (last_action == IP_DENY) ? 1 : 0;
1140 /* compute the real filename of a file by matching it without its
1141 extensions to all the stream filenames */
1142 static void compute_real_filename(char *filename, int max_size)
1149 /* compute filename by matching without the file extensions */
1150 av_strlcpy(file1, filename, sizeof(file1));
1151 p = strrchr(file1, '.');
1154 for(stream = first_stream; stream != NULL; stream = stream->next) {
1155 av_strlcpy(file2, stream->filename, sizeof(file2));
1156 p = strrchr(file2, '.');
1159 if (!strcmp(file1, file2)) {
1160 av_strlcpy(filename, stream->filename, max_size);
1175 /* parse http request and prepare header */
1176 static int http_parse_request(HTTPContext *c)
1179 enum RedirType redir_type;
1181 char info[1024], filename[1024];
1185 const char *mime_type;
1189 char *useragent = 0;
1192 get_word(cmd, sizeof(cmd), (const char **)&p);
1193 av_strlcpy(c->method, cmd, sizeof(c->method));
1195 if (!strcmp(cmd, "GET"))
1197 else if (!strcmp(cmd, "POST"))
1202 get_word(url, sizeof(url), (const char **)&p);
1203 av_strlcpy(c->url, url, sizeof(c->url));
1205 get_word(protocol, sizeof(protocol), (const char **)&p);
1206 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1209 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1212 http_log("New connection: %s %s\n", cmd, url);
1214 /* find the filename and the optional info string in the request */
1215 p = strchr(url, '?');
1217 av_strlcpy(info, p, sizeof(info));
1222 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1224 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1225 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1227 if (*useragent && *useragent != '\n' && isspace(*useragent))
1231 p = strchr(p, '\n');
1238 redir_type = REDIR_NONE;
1239 if (match_ext(filename, "asx")) {
1240 redir_type = REDIR_ASX;
1241 filename[strlen(filename)-1] = 'f';
1242 } else if (match_ext(filename, "asf") &&
1243 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1244 /* if this isn't WMP or lookalike, return the redirector file */
1245 redir_type = REDIR_ASF;
1246 } else if (match_ext(filename, "rpm,ram")) {
1247 redir_type = REDIR_RAM;
1248 strcpy(filename + strlen(filename)-2, "m");
1249 } else if (match_ext(filename, "rtsp")) {
1250 redir_type = REDIR_RTSP;
1251 compute_real_filename(filename, sizeof(filename) - 1);
1252 } else if (match_ext(filename, "sdp")) {
1253 redir_type = REDIR_SDP;
1254 compute_real_filename(filename, sizeof(filename) - 1);
1257 // "redirect" / request to index.html
1258 if (!strlen(filename))
1259 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1261 stream = first_stream;
1262 while (stream != NULL) {
1263 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1265 stream = stream->next;
1267 if (stream == NULL) {
1268 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1273 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1274 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1276 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1277 c->http_error = 301;
1279 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1280 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1281 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1282 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1283 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1284 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1285 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1287 /* prepare output buffer */
1288 c->buffer_ptr = c->buffer;
1290 c->state = HTTPSTATE_SEND_HEADER;
1294 /* If this is WMP, get the rate information */
1295 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1296 if (modify_current_stream(c, ratebuf)) {
1297 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1298 if (c->switch_feed_streams[i] >= 0)
1299 do_switch_stream(c, i);
1304 /* If already streaming this feed, do not let start another feeder. */
1305 if (stream->feed_opened) {
1306 snprintf(msg, sizeof(msg), "This feed is already being received.");
1310 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1311 current_bandwidth += stream->bandwidth;
1313 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1314 c->http_error = 200;
1316 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1317 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1318 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1319 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1320 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");
1321 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",
1322 current_bandwidth, max_bandwidth);
1323 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1325 /* prepare output buffer */
1326 c->buffer_ptr = c->buffer;
1328 c->state = HTTPSTATE_SEND_HEADER;
1332 if (redir_type != REDIR_NONE) {
1335 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1336 if (strncasecmp(p, "Host:", 5) == 0) {
1340 p = strchr(p, '\n');
1351 while (isspace(*hostinfo))
1354 eoh = strchr(hostinfo, '\n');
1356 if (eoh[-1] == '\r')
1359 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1360 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1361 hostbuf[eoh - hostinfo] = 0;
1363 c->http_error = 200;
1365 switch(redir_type) {
1367 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1368 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1369 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1370 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1371 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1372 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1373 hostbuf, filename, info);
1374 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1377 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1378 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1379 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1380 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1381 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1382 hostbuf, filename, info);
1385 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1386 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1387 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1388 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1389 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1390 hostbuf, filename, info);
1394 char hostname[256], *p;
1395 /* extract only hostname */
1396 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1397 p = strrchr(hostname, ':');
1400 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1401 /* XXX: incorrect mime type ? */
1402 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1403 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1404 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1405 hostname, ntohs(my_rtsp_addr.sin_port),
1412 int sdp_data_size, len;
1413 struct sockaddr_in my_addr;
1415 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1416 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1417 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1419 len = sizeof(my_addr);
1420 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1422 /* XXX: should use a dynamic buffer */
1423 sdp_data_size = prepare_sdp_description(stream,
1426 if (sdp_data_size > 0) {
1427 memcpy(q, sdp_data, sdp_data_size);
1439 /* prepare output buffer */
1440 c->buffer_ptr = c->buffer;
1442 c->state = HTTPSTATE_SEND_HEADER;
1448 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1452 stream->conns_served++;
1454 /* XXX: add there authenticate and IP match */
1457 /* if post, it means a feed is being sent */
1458 if (!stream->is_feed) {
1459 /* However it might be a status report from WMP! Lets log the data
1460 * as it might come in handy one day
1465 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1466 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1470 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1471 client_id = strtol(p + 18, 0, 10);
1472 p = strchr(p, '\n');
1480 char *eol = strchr(logline, '\n');
1485 if (eol[-1] == '\r')
1487 http_log("%.*s\n", (int) (eol - logline), logline);
1488 c->suppress_log = 1;
1493 http_log("\nGot request:\n%s\n", c->buffer);
1496 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1499 /* Now we have to find the client_id */
1500 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1501 if (wmpc->wmp_client_id == client_id)
1505 if (wmpc && modify_current_stream(wmpc, ratebuf))
1506 wmpc->switch_pending = 1;
1509 snprintf(msg, sizeof(msg), "POST command not handled");
1513 if (http_start_receive_data(c) < 0) {
1514 snprintf(msg, sizeof(msg), "could not open feed");
1518 c->state = HTTPSTATE_RECEIVE_DATA;
1523 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1524 http_log("\nGot request:\n%s\n", c->buffer);
1527 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1530 /* open input stream */
1531 if (open_input_stream(c, info) < 0) {
1532 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1536 /* prepare http header */
1538 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1539 mime_type = c->stream->fmt->mime_type;
1541 mime_type = "application/x-octet-stream";
1542 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1544 /* for asf, we need extra headers */
1545 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1546 /* Need to allocate a client id */
1548 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1550 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);
1552 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1553 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1555 /* prepare output buffer */
1557 c->buffer_ptr = c->buffer;
1559 c->state = HTTPSTATE_SEND_HEADER;
1562 c->http_error = 404;
1564 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1565 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1566 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1567 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1568 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1569 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1570 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1572 /* prepare output buffer */
1573 c->buffer_ptr = c->buffer;
1575 c->state = HTTPSTATE_SEND_HEADER;
1579 c->http_error = 200; /* horrible : we use this value to avoid
1580 going to the send data state */
1581 c->state = HTTPSTATE_SEND_HEADER;
1585 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1587 static const char *suffix = " kMGTP";
1590 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1592 url_fprintf(pb, "%"PRId64"%c", count, *s);
1595 static void compute_status(HTTPContext *c)
1604 if (url_open_dyn_buf(&pb) < 0) {
1605 /* XXX: return an error ? */
1606 c->buffer_ptr = c->buffer;
1607 c->buffer_end = c->buffer;
1611 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1612 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1613 url_fprintf(pb, "Pragma: no-cache\r\n");
1614 url_fprintf(pb, "\r\n");
1616 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1617 if (c->stream->feed_filename[0])
1618 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1619 url_fprintf(pb, "</HEAD>\n<BODY>");
1620 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1622 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1623 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1624 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");
1625 stream = first_stream;
1626 while (stream != NULL) {
1627 char sfilename[1024];
1630 if (stream->feed != stream) {
1631 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1632 eosf = sfilename + strlen(sfilename);
1633 if (eosf - sfilename >= 4) {
1634 if (strcmp(eosf - 4, ".asf") == 0)
1635 strcpy(eosf - 4, ".asx");
1636 else if (strcmp(eosf - 3, ".rm") == 0)
1637 strcpy(eosf - 3, ".ram");
1638 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1639 /* generate a sample RTSP director if
1640 unicast. Generate an SDP redirector if
1642 eosf = strrchr(sfilename, '.');
1644 eosf = sfilename + strlen(sfilename);
1645 if (stream->is_multicast)
1646 strcpy(eosf, ".sdp");
1648 strcpy(eosf, ".rtsp");
1652 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1653 sfilename, stream->filename);
1654 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1655 stream->conns_served);
1656 fmt_bytecount(pb, stream->bytes_served);
1657 switch(stream->stream_type) {
1658 case STREAM_TYPE_LIVE:
1660 int audio_bit_rate = 0;
1661 int video_bit_rate = 0;
1662 const char *audio_codec_name = "";
1663 const char *video_codec_name = "";
1664 const char *audio_codec_name_extra = "";
1665 const char *video_codec_name_extra = "";
1667 for(i=0;i<stream->nb_streams;i++) {
1668 AVStream *st = stream->streams[i];
1669 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1670 switch(st->codec->codec_type) {
1671 case CODEC_TYPE_AUDIO:
1672 audio_bit_rate += st->codec->bit_rate;
1674 if (*audio_codec_name)
1675 audio_codec_name_extra = "...";
1676 audio_codec_name = codec->name;
1679 case CODEC_TYPE_VIDEO:
1680 video_bit_rate += st->codec->bit_rate;
1682 if (*video_codec_name)
1683 video_codec_name_extra = "...";
1684 video_codec_name = codec->name;
1687 case CODEC_TYPE_DATA:
1688 video_bit_rate += st->codec->bit_rate;
1694 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",
1697 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1698 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1700 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1702 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1703 url_fprintf(pb, "\n");
1707 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1711 stream = stream->next;
1713 url_fprintf(pb, "</TABLE>\n");
1715 stream = first_stream;
1716 while (stream != NULL) {
1717 if (stream->feed == stream) {
1718 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1720 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1722 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1727 /* This is somewhat linux specific I guess */
1728 snprintf(ps_cmd, sizeof(ps_cmd),
1729 "ps -o \"%%cpu,cputime\" --no-headers %d",
1732 pid_stat = popen(ps_cmd, "r");
1737 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1739 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1747 url_fprintf(pb, "<p>");
1749 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");
1751 for (i = 0; i < stream->nb_streams; i++) {
1752 AVStream *st = stream->streams[i];
1753 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1754 const char *type = "unknown";
1755 char parameters[64];
1759 switch(st->codec->codec_type) {
1760 case CODEC_TYPE_AUDIO:
1762 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1764 case CODEC_TYPE_VIDEO:
1766 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1767 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1772 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1773 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1775 url_fprintf(pb, "</table>\n");
1778 stream = stream->next;
1784 AVCodecContext *enc;
1788 stream = first_feed;
1789 while (stream != NULL) {
1790 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1791 url_fprintf(pb, "<TABLE>\n");
1792 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1793 for(i=0;i<stream->nb_streams;i++) {
1794 AVStream *st = stream->streams[i];
1795 FeedData *fdata = st->priv_data;
1798 avcodec_string(buf, sizeof(buf), enc);
1799 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1800 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1801 avg /= enc->frame_size;
1802 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1803 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1805 url_fprintf(pb, "</TABLE>\n");
1806 stream = stream->next_feed;
1811 /* connection status */
1812 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1814 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1815 nb_connections, nb_max_connections);
1817 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1818 current_bandwidth, max_bandwidth);
1820 url_fprintf(pb, "<TABLE>\n");
1821 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");
1822 c1 = first_http_ctx;
1824 while (c1 != NULL) {
1830 for (j = 0; j < c1->stream->nb_streams; j++) {
1831 if (!c1->stream->feed)
1832 bitrate += c1->stream->streams[j]->codec->bit_rate;
1833 else if (c1->feed_streams[j] >= 0)
1834 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1839 p = inet_ntoa(c1->from_addr.sin_addr);
1840 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1842 c1->stream ? c1->stream->filename : "",
1843 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1846 http_state[c1->state]);
1847 fmt_bytecount(pb, bitrate);
1848 url_fprintf(pb, "<td align=right>");
1849 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1850 url_fprintf(pb, "<td align=right>");
1851 fmt_bytecount(pb, c1->data_count);
1852 url_fprintf(pb, "\n");
1855 url_fprintf(pb, "</TABLE>\n");
1860 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1861 url_fprintf(pb, "</BODY>\n</HTML>\n");
1863 len = url_close_dyn_buf(pb, &c->pb_buffer);
1864 c->buffer_ptr = c->pb_buffer;
1865 c->buffer_end = c->pb_buffer + len;
1868 /* check if the parser needs to be opened for stream i */
1869 static void open_parser(AVFormatContext *s, int i)
1871 AVStream *st = s->streams[i];
1874 if (!st->codec->codec) {
1875 codec = avcodec_find_decoder(st->codec->codec_id);
1876 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1877 st->codec->parse_only = 1;
1878 if (avcodec_open(st->codec, codec) < 0)
1879 st->codec->parse_only = 0;
1884 static int open_input_stream(HTTPContext *c, const char *info)
1887 char input_filename[1024];
1889 int buf_size, i, ret;
1892 /* find file name */
1893 if (c->stream->feed) {
1894 strcpy(input_filename, c->stream->feed->feed_filename);
1895 buf_size = FFM_PACKET_SIZE;
1896 /* compute position (absolute time) */
1897 if (find_info_tag(buf, sizeof(buf), "date", info))
1899 stream_pos = parse_date(buf, 0);
1900 if (stream_pos == INT64_MIN)
1903 else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1904 int prebuffer = strtol(buf, 0, 10);
1905 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1907 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1909 strcpy(input_filename, c->stream->feed_filename);
1911 /* compute position (relative time) */
1912 if (find_info_tag(buf, sizeof(buf), "date", info))
1914 stream_pos = parse_date(buf, 1);
1915 if (stream_pos == INT64_MIN)
1921 if (input_filename[0] == '\0')
1925 { time_t when = stream_pos / 1000000;
1926 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1931 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1932 buf_size, c->stream->ap_in)) < 0) {
1933 http_log("could not open %s: %d\n", input_filename, ret);
1936 s->flags |= AVFMT_FLAG_GENPTS;
1938 av_find_stream_info(c->fmt_in);
1940 /* open each parser */
1941 for(i=0;i<s->nb_streams;i++)
1944 /* choose stream as clock source (we favorize video stream if
1945 present) for packet sending */
1946 c->pts_stream_index = 0;
1947 for(i=0;i<c->stream->nb_streams;i++) {
1948 if (c->pts_stream_index == 0 &&
1949 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1950 c->pts_stream_index = i;
1955 if (c->fmt_in->iformat->read_seek)
1956 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1958 /* set the start time (needed for maxtime and RTP packet timing) */
1959 c->start_time = cur_time;
1960 c->first_pts = AV_NOPTS_VALUE;
1964 /* return the server clock (in us) */
1965 static int64_t get_server_clock(HTTPContext *c)
1967 /* compute current pts value from system time */
1968 return (cur_time - c->start_time) * 1000;
1971 /* return the estimated time at which the current packet must be sent
1973 static int64_t get_packet_send_clock(HTTPContext *c)
1975 int bytes_left, bytes_sent, frame_bytes;
1977 frame_bytes = c->cur_frame_bytes;
1978 if (frame_bytes <= 0)
1981 bytes_left = c->buffer_end - c->buffer_ptr;
1982 bytes_sent = frame_bytes - bytes_left;
1983 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1988 static int http_prepare_data(HTTPContext *c)
1991 AVFormatContext *ctx;
1993 av_freep(&c->pb_buffer);
1995 case HTTPSTATE_SEND_DATA_HEADER:
1996 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1997 av_strlcpy(c->fmt_ctx.author, c->stream->author,
1998 sizeof(c->fmt_ctx.author));
1999 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2000 sizeof(c->fmt_ctx.comment));
2001 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2002 sizeof(c->fmt_ctx.copyright));
2003 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2004 sizeof(c->fmt_ctx.title));
2006 /* open output stream by using specified codecs */
2007 c->fmt_ctx.oformat = c->stream->fmt;
2008 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2009 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2012 st = av_mallocz(sizeof(AVStream));
2013 st->codec= avcodec_alloc_context();
2014 c->fmt_ctx.streams[i] = st;
2015 /* if file or feed, then just take streams from FFStream struct */
2016 if (!c->stream->feed ||
2017 c->stream->feed == c->stream)
2018 src = c->stream->streams[i];
2020 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2024 st->codec->frame_number = 0; /* XXX: should be done in
2025 AVStream, not in codec */
2027 c->got_key_frame = 0;
2029 /* prepare header and save header data in a stream */
2030 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2031 /* XXX: potential leak */
2034 c->fmt_ctx.pb->is_streamed = 1;
2037 * HACK to avoid mpeg ps muxer to spit many underflow errors
2038 * Default value from FFmpeg
2039 * Try to set it use configuration option
2041 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2042 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2044 av_set_parameters(&c->fmt_ctx, NULL);
2045 if (av_write_header(&c->fmt_ctx) < 0) {
2046 http_log("Error writing output header\n");
2050 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2051 c->buffer_ptr = c->pb_buffer;
2052 c->buffer_end = c->pb_buffer + len;
2054 c->state = HTTPSTATE_SEND_DATA;
2055 c->last_packet_sent = 0;
2057 case HTTPSTATE_SEND_DATA:
2058 /* find a new packet */
2059 /* read a packet from the input stream */
2060 if (c->stream->feed)
2061 ffm_set_write_index(c->fmt_in,
2062 c->stream->feed->feed_write_index,
2063 c->stream->feed->feed_size);
2065 if (c->stream->max_time &&
2066 c->stream->max_time + c->start_time - cur_time < 0)
2067 /* We have timed out */
2068 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2072 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2073 if (c->stream->feed && c->stream->feed->feed_opened) {
2074 /* if coming from feed, it means we reached the end of the
2075 ffm file, so must wait for more data */
2076 c->state = HTTPSTATE_WAIT_FEED;
2077 return 1; /* state changed */
2079 if (c->stream->loop) {
2080 av_close_input_file(c->fmt_in);
2082 if (open_input_stream(c, "") < 0)
2087 /* must send trailer now because eof or error */
2088 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2092 int source_index = pkt.stream_index;
2093 /* update first pts if needed */
2094 if (c->first_pts == AV_NOPTS_VALUE) {
2095 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2096 c->start_time = cur_time;
2098 /* send it to the appropriate stream */
2099 if (c->stream->feed) {
2100 /* if coming from a feed, select the right stream */
2101 if (c->switch_pending) {
2102 c->switch_pending = 0;
2103 for(i=0;i<c->stream->nb_streams;i++) {
2104 if (c->switch_feed_streams[i] == pkt.stream_index)
2105 if (pkt.flags & PKT_FLAG_KEY)
2106 do_switch_stream(c, i);
2107 if (c->switch_feed_streams[i] >= 0)
2108 c->switch_pending = 1;
2111 for(i=0;i<c->stream->nb_streams;i++) {
2112 if (c->feed_streams[i] == pkt.stream_index) {
2113 AVStream *st = c->fmt_in->streams[source_index];
2114 pkt.stream_index = i;
2115 if (pkt.flags & PKT_FLAG_KEY &&
2116 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2117 c->stream->nb_streams == 1))
2118 c->got_key_frame = 1;
2119 if (!c->stream->send_on_key || c->got_key_frame)
2124 AVCodecContext *codec;
2127 /* specific handling for RTP: we use several
2128 output stream (one for each RTP
2129 connection). XXX: need more abstract handling */
2130 if (c->is_packetized) {
2132 /* compute send time and duration */
2133 st = c->fmt_in->streams[pkt.stream_index];
2134 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2135 if (st->start_time != AV_NOPTS_VALUE)
2136 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2137 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2139 printf("index=%d pts=%0.3f duration=%0.6f\n",
2141 (double)c->cur_pts /
2143 (double)c->cur_frame_duration /
2146 /* find RTP context */
2147 c->packet_stream_index = pkt.stream_index;
2148 ctx = c->rtp_ctx[c->packet_stream_index];
2150 av_free_packet(&pkt);
2153 codec = ctx->streams[0]->codec;
2154 /* only one stream per RTP connection */
2155 pkt.stream_index = 0;
2159 codec = ctx->streams[pkt.stream_index]->codec;
2162 if (c->is_packetized) {
2163 int max_packet_size;
2164 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2165 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2167 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2168 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2170 ret = url_open_dyn_buf(&ctx->pb);
2173 /* XXX: potential leak */
2176 c->fmt_ctx.pb->is_streamed = 1;
2177 if (pkt.dts != AV_NOPTS_VALUE)
2178 pkt.dts = av_rescale_q(pkt.dts,
2179 c->fmt_in->streams[source_index]->time_base,
2180 ctx->streams[pkt.stream_index]->time_base);
2181 if (pkt.pts != AV_NOPTS_VALUE)
2182 pkt.pts = av_rescale_q(pkt.pts,
2183 c->fmt_in->streams[source_index]->time_base,
2184 ctx->streams[pkt.stream_index]->time_base);
2185 pkt.duration = av_rescale_q(pkt.duration,
2186 c->fmt_in->streams[source_index]->time_base,
2187 ctx->streams[pkt.stream_index]->time_base);
2188 if (av_write_frame(ctx, &pkt) < 0) {
2189 http_log("Error writing frame to output\n");
2190 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2194 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2195 c->cur_frame_bytes = len;
2196 c->buffer_ptr = c->pb_buffer;
2197 c->buffer_end = c->pb_buffer + len;
2199 codec->frame_number++;
2201 av_free_packet(&pkt);
2205 av_free_packet(&pkt);
2210 case HTTPSTATE_SEND_DATA_TRAILER:
2211 /* last packet test ? */
2212 if (c->last_packet_sent || c->is_packetized)
2215 /* prepare header */
2216 if (url_open_dyn_buf(&ctx->pb) < 0) {
2217 /* XXX: potential leak */
2220 c->fmt_ctx.pb->is_streamed = 1;
2221 av_write_trailer(ctx);
2222 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2223 c->buffer_ptr = c->pb_buffer;
2224 c->buffer_end = c->pb_buffer + len;
2226 c->last_packet_sent = 1;
2232 /* should convert the format at the same time */
2233 /* send data starting at c->buffer_ptr to the output connection
2234 (either UDP or TCP connection) */
2235 static int http_send_data(HTTPContext *c)
2240 if (c->buffer_ptr >= c->buffer_end) {
2241 ret = http_prepare_data(c);
2245 /* state change requested */
2248 if (c->is_packetized) {
2249 /* RTP data output */
2250 len = c->buffer_end - c->buffer_ptr;
2252 /* fail safe - should never happen */
2254 c->buffer_ptr = c->buffer_end;
2257 len = (c->buffer_ptr[0] << 24) |
2258 (c->buffer_ptr[1] << 16) |
2259 (c->buffer_ptr[2] << 8) |
2261 if (len > (c->buffer_end - c->buffer_ptr))
2263 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2264 /* nothing to send yet: we can wait */
2268 c->data_count += len;
2269 update_datarate(&c->datarate, c->data_count);
2271 c->stream->bytes_served += len;
2273 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2274 /* RTP packets are sent inside the RTSP TCP connection */
2276 int interleaved_index, size;
2278 HTTPContext *rtsp_c;
2281 /* if no RTSP connection left, error */
2284 /* if already sending something, then wait. */
2285 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2287 if (url_open_dyn_buf(&pb) < 0)
2289 interleaved_index = c->packet_stream_index * 2;
2290 /* RTCP packets are sent at odd indexes */
2291 if (c->buffer_ptr[1] == 200)
2292 interleaved_index++;
2293 /* write RTSP TCP header */
2295 header[1] = interleaved_index;
2296 header[2] = len >> 8;
2298 put_buffer(pb, header, 4);
2299 /* write RTP packet data */
2301 put_buffer(pb, c->buffer_ptr, len);
2302 size = url_close_dyn_buf(pb, &c->packet_buffer);
2303 /* prepare asynchronous TCP sending */
2304 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2305 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2306 c->buffer_ptr += len;
2308 /* send everything we can NOW */
2309 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2310 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2312 rtsp_c->packet_buffer_ptr += len;
2313 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2314 /* if we could not send all the data, we will
2315 send it later, so a new state is needed to
2316 "lock" the RTSP TCP connection */
2317 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2320 /* all data has been sent */
2321 av_freep(&c->packet_buffer);
2323 /* send RTP packet directly in UDP */
2325 url_write(c->rtp_handles[c->packet_stream_index],
2326 c->buffer_ptr, len);
2327 c->buffer_ptr += len;
2328 /* here we continue as we can send several packets per 10 ms slot */
2331 /* TCP data output */
2332 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2334 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2335 ff_neterrno() != FF_NETERROR(EINTR))
2336 /* error : close connection */
2341 c->buffer_ptr += len;
2343 c->data_count += len;
2344 update_datarate(&c->datarate, c->data_count);
2346 c->stream->bytes_served += len;
2354 static int http_start_receive_data(HTTPContext *c)
2358 if (c->stream->feed_opened)
2361 /* Don't permit writing to this one */
2362 if (c->stream->readonly)
2366 fd = open(c->stream->feed_filename, O_RDWR);
2368 http_log("Error opening feeder file: %s\n", strerror(errno));
2373 c->stream->feed_write_index = ffm_read_write_index(fd);
2374 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2375 lseek(fd, 0, SEEK_SET);
2377 /* init buffer input */
2378 c->buffer_ptr = c->buffer;
2379 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2380 c->stream->feed_opened = 1;
2384 static int http_receive_data(HTTPContext *c)
2388 if (c->buffer_end > c->buffer_ptr) {
2391 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2393 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2394 ff_neterrno() != FF_NETERROR(EINTR))
2395 /* error : close connection */
2397 } else if (len == 0)
2398 /* end of connection : close it */
2401 c->buffer_ptr += len;
2402 c->data_count += len;
2403 update_datarate(&c->datarate, c->data_count);
2407 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2408 if (c->buffer[0] != 'f' ||
2409 c->buffer[1] != 'm') {
2410 http_log("Feed stream has become desynchronized -- disconnecting\n");
2415 if (c->buffer_ptr >= c->buffer_end) {
2416 FFStream *feed = c->stream;
2417 /* a packet has been received : write it in the store, except
2419 if (c->data_count > FFM_PACKET_SIZE) {
2421 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2422 /* XXX: use llseek or url_seek */
2423 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2424 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2425 http_log("Error writing to feed file: %s\n", strerror(errno));
2429 feed->feed_write_index += FFM_PACKET_SIZE;
2430 /* update file size */
2431 if (feed->feed_write_index > c->stream->feed_size)
2432 feed->feed_size = feed->feed_write_index;
2434 /* handle wrap around if max file size reached */
2435 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2436 feed->feed_write_index = FFM_PACKET_SIZE;
2439 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2441 /* wake up any waiting connections */
2442 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2443 if (c1->state == HTTPSTATE_WAIT_FEED &&
2444 c1->stream->feed == c->stream->feed)
2445 c1->state = HTTPSTATE_SEND_DATA;
2448 /* We have a header in our hands that contains useful data */
2450 AVInputFormat *fmt_in;
2453 memset(&s, 0, sizeof(s));
2455 url_open_buf(&s.pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2456 s.pb->is_streamed = 1;
2458 /* use feed output format name to find corresponding input format */
2459 fmt_in = av_find_input_format(feed->fmt->name);
2463 if (fmt_in->priv_data_size > 0) {
2464 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2470 if (fmt_in->read_header(&s, 0) < 0) {
2471 av_freep(&s.priv_data);
2475 /* Now we have the actual streams */
2476 if (s.nb_streams != feed->nb_streams) {
2477 av_freep(&s.priv_data);
2480 for (i = 0; i < s.nb_streams; i++)
2481 memcpy(feed->streams[i]->codec,
2482 s.streams[i]->codec, sizeof(AVCodecContext));
2483 av_freep(&s.priv_data);
2485 c->buffer_ptr = c->buffer;
2490 c->stream->feed_opened = 0;
2492 /* wake up any waiting connections to stop waiting for feed */
2493 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2494 if (c1->state == HTTPSTATE_WAIT_FEED &&
2495 c1->stream->feed == c->stream->feed)
2496 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2501 /********************************************************************/
2504 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2511 switch(error_number) {
2512 case RTSP_STATUS_OK:
2515 case RTSP_STATUS_METHOD:
2516 str = "Method Not Allowed";
2518 case RTSP_STATUS_BANDWIDTH:
2519 str = "Not Enough Bandwidth";
2521 case RTSP_STATUS_SESSION:
2522 str = "Session Not Found";
2524 case RTSP_STATUS_STATE:
2525 str = "Method Not Valid in This State";
2527 case RTSP_STATUS_AGGREGATE:
2528 str = "Aggregate operation not allowed";
2530 case RTSP_STATUS_ONLY_AGGREGATE:
2531 str = "Only aggregate operation allowed";
2533 case RTSP_STATUS_TRANSPORT:
2534 str = "Unsupported transport";
2536 case RTSP_STATUS_INTERNAL:
2537 str = "Internal Server Error";
2539 case RTSP_STATUS_SERVICE:
2540 str = "Service Unavailable";
2542 case RTSP_STATUS_VERSION:
2543 str = "RTSP Version not supported";
2546 str = "Unknown Error";
2550 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2551 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2553 /* output GMT time */
2557 p = buf2 + strlen(p) - 1;
2560 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2563 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2565 rtsp_reply_header(c, error_number);
2566 url_fprintf(c->pb, "\r\n");
2569 static int rtsp_parse_request(HTTPContext *c)
2571 const char *p, *p1, *p2;
2577 RTSPHeader header1, *header = &header1;
2579 c->buffer_ptr[0] = '\0';
2582 get_word(cmd, sizeof(cmd), &p);
2583 get_word(url, sizeof(url), &p);
2584 get_word(protocol, sizeof(protocol), &p);
2586 av_strlcpy(c->method, cmd, sizeof(c->method));
2587 av_strlcpy(c->url, url, sizeof(c->url));
2588 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2590 if (url_open_dyn_buf(&c->pb) < 0) {
2591 /* XXX: cannot do more */
2592 c->pb = NULL; /* safety */
2596 /* check version name */
2597 if (strcmp(protocol, "RTSP/1.0") != 0) {
2598 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2602 /* parse each header line */
2603 memset(header, 0, sizeof(RTSPHeader));
2604 /* skip to next line */
2605 while (*p != '\n' && *p != '\0')
2609 while (*p != '\0') {
2610 p1 = strchr(p, '\n');
2614 if (p2 > p && p2[-1] == '\r')
2616 /* skip empty line */
2620 if (len > sizeof(line) - 1)
2621 len = sizeof(line) - 1;
2622 memcpy(line, p, len);
2624 rtsp_parse_line(header, line);
2628 /* handle sequence number */
2629 c->seq = header->seq;
2631 if (!strcmp(cmd, "DESCRIBE"))
2632 rtsp_cmd_describe(c, url);
2633 else if (!strcmp(cmd, "OPTIONS"))
2634 rtsp_cmd_options(c, url);
2635 else if (!strcmp(cmd, "SETUP"))
2636 rtsp_cmd_setup(c, url, header);
2637 else if (!strcmp(cmd, "PLAY"))
2638 rtsp_cmd_play(c, url, header);
2639 else if (!strcmp(cmd, "PAUSE"))
2640 rtsp_cmd_pause(c, url, header);
2641 else if (!strcmp(cmd, "TEARDOWN"))
2642 rtsp_cmd_teardown(c, url, header);
2644 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2647 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2648 c->pb = NULL; /* safety */
2650 /* XXX: cannot do more */
2653 c->buffer_ptr = c->pb_buffer;
2654 c->buffer_end = c->pb_buffer + len;
2655 c->state = RTSPSTATE_SEND_REPLY;
2659 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2660 struct in_addr my_ip)
2662 AVFormatContext *avc;
2663 AVStream avs[MAX_STREAMS];
2666 avc = av_alloc_format_context();
2670 if (stream->title[0] != 0) {
2671 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2673 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2675 avc->nb_streams = stream->nb_streams;
2676 if (stream->is_multicast) {
2677 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2678 inet_ntoa(stream->multicast_ip),
2679 stream->multicast_port, stream->multicast_ttl);
2682 for(i = 0; i < stream->nb_streams; i++) {
2683 avc->streams[i] = &avs[i];
2684 avc->streams[i]->codec = stream->streams[i]->codec;
2686 *pbuffer = av_mallocz(2048);
2687 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2690 return strlen(*pbuffer);
2693 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2695 // rtsp_reply_header(c, RTSP_STATUS_OK);
2696 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2697 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2698 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2699 url_fprintf(c->pb, "\r\n");
2702 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2708 int content_length, len;
2709 struct sockaddr_in my_addr;
2711 /* find which url is asked */
2712 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2717 for(stream = first_stream; stream != NULL; stream = stream->next) {
2718 if (!stream->is_feed &&
2719 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2720 !strcmp(path, stream->filename)) {
2724 /* no stream found */
2725 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2729 /* prepare the media description in sdp format */
2731 /* get the host IP */
2732 len = sizeof(my_addr);
2733 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2734 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2735 if (content_length < 0) {
2736 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2739 rtsp_reply_header(c, RTSP_STATUS_OK);
2740 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2741 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2742 url_fprintf(c->pb, "\r\n");
2743 put_buffer(c->pb, content, content_length);
2746 static HTTPContext *find_rtp_session(const char *session_id)
2750 if (session_id[0] == '\0')
2753 for(c = first_http_ctx; c != NULL; c = c->next) {
2754 if (!strcmp(c->session_id, session_id))
2760 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2762 RTSPTransportField *th;
2765 for(i=0;i<h->nb_transports;i++) {
2766 th = &h->transports[i];
2767 if (th->protocol == protocol)
2773 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2777 int stream_index, port;
2782 RTSPTransportField *th;
2783 struct sockaddr_in dest_addr;
2784 RTSPActionServerSetup setup;
2786 /* find which url is asked */
2787 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2792 /* now check each stream */
2793 for(stream = first_stream; stream != NULL; stream = stream->next) {
2794 if (!stream->is_feed &&
2795 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2796 /* accept aggregate filenames only if single stream */
2797 if (!strcmp(path, stream->filename)) {
2798 if (stream->nb_streams != 1) {
2799 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2806 for(stream_index = 0; stream_index < stream->nb_streams;
2808 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2809 stream->filename, stream_index);
2810 if (!strcmp(path, buf))
2815 /* no stream found */
2816 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2820 /* generate session id if needed */
2821 if (h->session_id[0] == '\0')
2822 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2823 av_random(&random_state), av_random(&random_state));
2825 /* find rtp session, and create it if none found */
2826 rtp_c = find_rtp_session(h->session_id);
2828 /* always prefer UDP */
2829 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2831 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2833 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2838 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2841 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2845 /* open input stream */
2846 if (open_input_stream(rtp_c, "") < 0) {
2847 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2852 /* test if stream is OK (test needed because several SETUP needs
2853 to be done for a given file) */
2854 if (rtp_c->stream != stream) {
2855 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2859 /* test if stream is already set up */
2860 if (rtp_c->rtp_ctx[stream_index]) {
2861 rtsp_reply_error(c, RTSP_STATUS_STATE);
2865 /* check transport */
2866 th = find_transport(h, rtp_c->rtp_protocol);
2867 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2868 th->client_port_min <= 0)) {
2869 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2873 /* setup default options */
2874 setup.transport_option[0] = '\0';
2875 dest_addr = rtp_c->from_addr;
2876 dest_addr.sin_port = htons(th->client_port_min);
2879 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2880 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2884 /* now everything is OK, so we can send the connection parameters */
2885 rtsp_reply_header(c, RTSP_STATUS_OK);
2887 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2889 switch(rtp_c->rtp_protocol) {
2890 case RTSP_PROTOCOL_RTP_UDP:
2891 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2892 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2893 "client_port=%d-%d;server_port=%d-%d",
2894 th->client_port_min, th->client_port_min + 1,
2897 case RTSP_PROTOCOL_RTP_TCP:
2898 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2899 stream_index * 2, stream_index * 2 + 1);
2904 if (setup.transport_option[0] != '\0')
2905 url_fprintf(c->pb, ";%s", setup.transport_option);
2906 url_fprintf(c->pb, "\r\n");
2909 url_fprintf(c->pb, "\r\n");
2913 /* find an rtp connection by using the session ID. Check consistency
2915 static HTTPContext *find_rtp_session_with_url(const char *url,
2916 const char *session_id)
2924 rtp_c = find_rtp_session(session_id);
2928 /* find which url is asked */
2929 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2933 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2934 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2935 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2936 rtp_c->stream->filename, s);
2937 if(!strncmp(path, buf, sizeof(buf))) {
2938 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2945 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2949 rtp_c = find_rtp_session_with_url(url, h->session_id);
2951 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2955 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2956 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2957 rtp_c->state != HTTPSTATE_READY) {
2958 rtsp_reply_error(c, RTSP_STATUS_STATE);
2963 /* XXX: seek in stream */
2964 if (h->range_start != AV_NOPTS_VALUE) {
2965 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2966 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2970 rtp_c->state = HTTPSTATE_SEND_DATA;
2972 /* now everything is OK, so we can send the connection parameters */
2973 rtsp_reply_header(c, RTSP_STATUS_OK);
2975 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2976 url_fprintf(c->pb, "\r\n");
2979 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2983 rtp_c = find_rtp_session_with_url(url, h->session_id);
2985 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2989 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2990 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2991 rtsp_reply_error(c, RTSP_STATUS_STATE);
2995 rtp_c->state = HTTPSTATE_READY;
2996 rtp_c->first_pts = AV_NOPTS_VALUE;
2997 /* now everything is OK, so we can send the connection parameters */
2998 rtsp_reply_header(c, RTSP_STATUS_OK);
3000 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3001 url_fprintf(c->pb, "\r\n");
3004 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3007 char session_id[32];
3009 rtp_c = find_rtp_session_with_url(url, h->session_id);
3011 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3015 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3017 /* abort the session */
3018 close_connection(rtp_c);
3020 /* now everything is OK, so we can send the connection parameters */
3021 rtsp_reply_header(c, RTSP_STATUS_OK);
3023 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3024 url_fprintf(c->pb, "\r\n");
3028 /********************************************************************/
3031 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3032 FFStream *stream, const char *session_id,
3033 enum RTSPProtocol rtp_protocol)
3035 HTTPContext *c = NULL;
3036 const char *proto_str;
3038 /* XXX: should output a warning page when coming
3039 close to the connection limit */
3040 if (nb_connections >= nb_max_connections)
3043 /* add a new connection */
3044 c = av_mallocz(sizeof(HTTPContext));
3049 c->poll_entry = NULL;
3050 c->from_addr = *from_addr;
3051 c->buffer_size = IOBUFFER_INIT_SIZE;
3052 c->buffer = av_malloc(c->buffer_size);
3057 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3058 c->state = HTTPSTATE_READY;
3059 c->is_packetized = 1;
3060 c->rtp_protocol = rtp_protocol;
3062 /* protocol is shown in statistics */
3063 switch(c->rtp_protocol) {
3064 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3065 proto_str = "MCAST";
3067 case RTSP_PROTOCOL_RTP_UDP:
3070 case RTSP_PROTOCOL_RTP_TCP:
3077 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3078 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3080 current_bandwidth += stream->bandwidth;
3082 c->next = first_http_ctx;
3094 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3095 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3097 static int rtp_new_av_stream(HTTPContext *c,
3098 int stream_index, struct sockaddr_in *dest_addr,
3099 HTTPContext *rtsp_c)
3101 AVFormatContext *ctx;
3104 URLContext *h = NULL;
3107 int max_packet_size;
3109 /* now we can open the relevant output stream */
3110 ctx = av_alloc_format_context();
3113 ctx->oformat = guess_format("rtp", NULL, NULL);
3115 st = av_mallocz(sizeof(AVStream));
3118 st->codec= avcodec_alloc_context();
3119 ctx->nb_streams = 1;
3120 ctx->streams[0] = st;
3122 if (!c->stream->feed ||
3123 c->stream->feed == c->stream)
3124 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3127 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3129 st->priv_data = NULL;
3131 /* build destination RTP address */
3132 ipaddr = inet_ntoa(dest_addr->sin_addr);
3134 switch(c->rtp_protocol) {
3135 case RTSP_PROTOCOL_RTP_UDP:
3136 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3139 /* XXX: also pass as parameter to function ? */
3140 if (c->stream->is_multicast) {
3142 ttl = c->stream->multicast_ttl;
3145 snprintf(ctx->filename, sizeof(ctx->filename),
3146 "rtp://%s:%d?multicast=1&ttl=%d",
3147 ipaddr, ntohs(dest_addr->sin_port), ttl);
3149 snprintf(ctx->filename, sizeof(ctx->filename),
3150 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3153 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3155 c->rtp_handles[stream_index] = h;
3156 max_packet_size = url_get_max_packet_size(h);
3158 case RTSP_PROTOCOL_RTP_TCP:
3161 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3167 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3168 ipaddr, ntohs(dest_addr->sin_port),
3170 c->stream->filename, stream_index, c->protocol);
3172 /* normally, no packets should be output here, but the packet size may be checked */
3173 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3174 /* XXX: close stream */
3177 av_set_parameters(ctx, NULL);
3178 if (av_write_header(ctx) < 0) {
3185 url_close_dyn_buf(ctx->pb, &dummy_buf);
3188 c->rtp_ctx[stream_index] = ctx;
3192 /********************************************************************/
3193 /* ffserver initialization */
3195 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3199 fst = av_mallocz(sizeof(AVStream));
3202 fst->codec= avcodec_alloc_context();
3203 fst->priv_data = av_mallocz(sizeof(FeedData));
3204 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3205 fst->index = stream->nb_streams;
3206 av_set_pts_info(fst, 33, 1, 90000);
3207 stream->streams[stream->nb_streams++] = fst;
3211 /* return the stream number in the feed */
3212 static int add_av_stream(FFStream *feed, AVStream *st)
3215 AVCodecContext *av, *av1;
3219 for(i=0;i<feed->nb_streams;i++) {
3220 st = feed->streams[i];
3222 if (av1->codec_id == av->codec_id &&
3223 av1->codec_type == av->codec_type &&
3224 av1->bit_rate == av->bit_rate) {
3226 switch(av->codec_type) {
3227 case CODEC_TYPE_AUDIO:
3228 if (av1->channels == av->channels &&
3229 av1->sample_rate == av->sample_rate)
3232 case CODEC_TYPE_VIDEO:
3233 if (av1->width == av->width &&
3234 av1->height == av->height &&
3235 av1->time_base.den == av->time_base.den &&
3236 av1->time_base.num == av->time_base.num &&
3237 av1->gop_size == av->gop_size)
3246 fst = add_av_stream1(feed, av);
3249 return feed->nb_streams - 1;
3254 static void remove_stream(FFStream *stream)
3258 while (*ps != NULL) {
3266 /* specific mpeg4 handling : we extract the raw parameters */
3267 static void extract_mpeg4_header(AVFormatContext *infile)
3269 int mpeg4_count, i, size;
3275 for(i=0;i<infile->nb_streams;i++) {
3276 st = infile->streams[i];
3277 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3278 st->codec->extradata_size == 0) {
3285 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3286 while (mpeg4_count > 0) {
3287 if (av_read_packet(infile, &pkt) < 0)
3289 st = infile->streams[pkt.stream_index];
3290 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3291 st->codec->extradata_size == 0) {
3292 av_freep(&st->codec->extradata);
3293 /* fill extradata with the header */
3294 /* XXX: we make hard suppositions here ! */
3296 while (p < pkt.data + pkt.size - 4) {
3297 /* stop when vop header is found */
3298 if (p[0] == 0x00 && p[1] == 0x00 &&
3299 p[2] == 0x01 && p[3] == 0xb6) {
3300 size = p - pkt.data;
3301 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3302 st->codec->extradata = av_malloc(size);
3303 st->codec->extradata_size = size;
3304 memcpy(st->codec->extradata, pkt.data, size);
3311 av_free_packet(&pkt);
3315 /* compute the needed AVStream for each file */
3316 static void build_file_streams(void)
3318 FFStream *stream, *stream_next;
3319 AVFormatContext *infile;
3322 /* gather all streams */
3323 for(stream = first_stream; stream != NULL; stream = stream_next) {
3324 stream_next = stream->next;
3325 if (stream->stream_type == STREAM_TYPE_LIVE &&
3327 /* the stream comes from a file */
3328 /* try to open the file */
3330 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3331 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3332 /* specific case : if transport stream output to RTP,
3333 we use a raw transport stream reader */
3334 stream->ap_in->mpeg2ts_raw = 1;
3335 stream->ap_in->mpeg2ts_compute_pcr = 1;
3338 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3339 stream->ifmt, 0, stream->ap_in)) < 0) {
3340 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3341 /* remove stream (no need to spend more time on it) */
3343 remove_stream(stream);
3345 /* find all the AVStreams inside and reference them in
3347 if (av_find_stream_info(infile) < 0) {
3348 http_log("Could not find codec parameters from '%s'\n",
3349 stream->feed_filename);
3350 av_close_input_file(infile);
3353 extract_mpeg4_header(infile);
3355 for(i=0;i<infile->nb_streams;i++)
3356 add_av_stream1(stream, infile->streams[i]->codec);
3358 av_close_input_file(infile);
3364 /* compute the needed AVStream for each feed */
3365 static void build_feed_streams(void)
3367 FFStream *stream, *feed;
3370 /* gather all streams */
3371 for(stream = first_stream; stream != NULL; stream = stream->next) {
3372 feed = stream->feed;
3374 if (!stream->is_feed) {
3375 /* we handle a stream coming from a feed */
3376 for(i=0;i<stream->nb_streams;i++)
3377 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3382 /* gather all streams */
3383 for(stream = first_stream; stream != NULL; stream = stream->next) {
3384 feed = stream->feed;
3386 if (stream->is_feed) {
3387 for(i=0;i<stream->nb_streams;i++)
3388 stream->feed_streams[i] = i;
3393 /* create feed files if needed */
3394 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3397 if (url_exist(feed->feed_filename)) {
3398 /* See if it matches */
3402 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3403 /* Now see if it matches */
3404 if (s->nb_streams == feed->nb_streams) {
3406 for(i=0;i<s->nb_streams;i++) {
3408 sf = feed->streams[i];
3411 if (sf->index != ss->index ||
3413 printf("Index & Id do not match for stream %d (%s)\n",
3414 i, feed->feed_filename);
3417 AVCodecContext *ccf, *ccs;
3421 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3423 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3424 printf("Codecs do not match for stream %d\n", i);
3426 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3427 printf("Codec bitrates do not match for stream %d\n", i);
3429 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3430 if (CHECK_CODEC(time_base.den) ||
3431 CHECK_CODEC(time_base.num) ||
3432 CHECK_CODEC(width) ||
3433 CHECK_CODEC(height)) {
3434 printf("Codec width, height and framerate do not match for stream %d\n", i);
3437 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3438 if (CHECK_CODEC(sample_rate) ||
3439 CHECK_CODEC(channels) ||
3440 CHECK_CODEC(frame_size)) {
3441 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3445 printf("Unknown codec type\n");
3453 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3454 feed->feed_filename, s->nb_streams, feed->nb_streams);
3456 av_close_input_file(s);
3458 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3459 feed->feed_filename);
3462 if (feed->readonly) {
3463 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3464 feed->feed_filename);
3467 unlink(feed->feed_filename);
3470 if (!url_exist(feed->feed_filename)) {
3471 AVFormatContext s1, *s = &s1;
3473 if (feed->readonly) {
3474 printf("Unable to create feed file '%s' as it is marked readonly\n",
3475 feed->feed_filename);
3479 /* only write the header of the ffm file */
3480 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3481 http_log("Could not open output feed file '%s'\n",
3482 feed->feed_filename);
3485 s->oformat = feed->fmt;
3486 s->nb_streams = feed->nb_streams;
3487 for(i=0;i<s->nb_streams;i++) {
3489 st = feed->streams[i];
3492 av_set_parameters(s, NULL);
3493 if (av_write_header(s) < 0) {
3494 http_log("Container doesn't supports the required parameters\n");
3497 /* XXX: need better api */
3498 av_freep(&s->priv_data);
3501 /* get feed size and write index */
3502 fd = open(feed->feed_filename, O_RDONLY);
3504 http_log("Could not open output feed file '%s'\n",
3505 feed->feed_filename);
3509 feed->feed_write_index = ffm_read_write_index(fd);
3510 feed->feed_size = lseek(fd, 0, SEEK_END);
3511 /* ensure that we do not wrap before the end of file */
3512 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3513 feed->feed_max_size = feed->feed_size;
3519 /* compute the bandwidth used by each stream */
3520 static void compute_bandwidth(void)
3526 for(stream = first_stream; stream != NULL; stream = stream->next) {
3528 for(i=0;i<stream->nb_streams;i++) {
3529 AVStream *st = stream->streams[i];
3530 switch(st->codec->codec_type) {
3531 case CODEC_TYPE_AUDIO:
3532 case CODEC_TYPE_VIDEO:
3533 bandwidth += st->codec->bit_rate;
3539 stream->bandwidth = (bandwidth + 999) / 1000;
3543 static void get_arg(char *buf, int buf_size, const char **pp)
3550 while (isspace(*p)) p++;
3553 if (*p == '\"' || *p == '\'')
3565 if ((q - buf) < buf_size - 1)
3570 if (quote && *p == quote)
3575 /* add a codec and set the default parameters */
3576 static void add_codec(FFStream *stream, AVCodecContext *av)
3580 /* compute default parameters */
3581 switch(av->codec_type) {
3582 case CODEC_TYPE_AUDIO:
3583 if (av->bit_rate == 0)
3584 av->bit_rate = 64000;
3585 if (av->sample_rate == 0)
3586 av->sample_rate = 22050;
3587 if (av->channels == 0)
3590 case CODEC_TYPE_VIDEO:
3591 if (av->bit_rate == 0)
3592 av->bit_rate = 64000;
3593 if (av->time_base.num == 0){
3594 av->time_base.den = 5;
3595 av->time_base.num = 1;
3597 if (av->width == 0 || av->height == 0) {
3601 /* Bitrate tolerance is less for streaming */
3602 if (av->bit_rate_tolerance == 0)
3603 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3604 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3609 if (av->max_qdiff == 0)
3611 av->qcompress = 0.5;
3614 if (!av->nsse_weight)
3615 av->nsse_weight = 8;
3617 av->frame_skip_cmp = FF_CMP_DCTMAX;
3618 av->me_method = ME_EPZS;
3619 av->rc_buffer_aggressivity = 1.0;
3622 av->rc_eq = "tex^qComp";
3623 if (!av->i_quant_factor)
3624 av->i_quant_factor = -0.8;
3625 if (!av->b_quant_factor)
3626 av->b_quant_factor = 1.25;
3627 if (!av->b_quant_offset)
3628 av->b_quant_offset = 1.25;
3629 if (!av->rc_max_rate)
3630 av->rc_max_rate = av->bit_rate * 2;
3632 if (av->rc_max_rate && !av->rc_buffer_size) {
3633 av->rc_buffer_size = av->rc_max_rate;
3642 st = av_mallocz(sizeof(AVStream));
3645 st->codec = avcodec_alloc_context();
3646 stream->streams[stream->nb_streams++] = st;
3647 memcpy(st->codec, av, sizeof(AVCodecContext));
3650 static int opt_audio_codec(const char *arg)
3652 AVCodec *p= avcodec_find_encoder_by_name(arg);
3654 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3655 return CODEC_ID_NONE;
3660 static int opt_video_codec(const char *arg)
3662 AVCodec *p= avcodec_find_encoder_by_name(arg);
3664 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3665 return CODEC_ID_NONE;
3670 /* simplistic plugin support */
3673 static void load_module(const char *filename)
3676 void (*init_func)(void);
3677 dll = dlopen(filename, RTLD_NOW);
3679 fprintf(stderr, "Could not load module '%s' - %s\n",
3680 filename, dlerror());
3684 init_func = dlsym(dll, "ffserver_module_init");
3687 "%s: init function 'ffserver_module_init()' not found\n",
3696 static int opt_default(const char *opt, const char *arg,
3697 AVCodecContext *avctx, int type)
3699 const AVOption *o = NULL;
3700 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3702 o = av_set_string(avctx, opt, arg);
3708 static int parse_ffconfig(const char *filename)
3715 int val, errors, line_num;
3716 FFStream **last_stream, *stream, *redirect;
3717 FFStream **last_feed, *feed;
3718 AVCodecContext audio_enc, video_enc;
3719 int audio_id, video_id;
3721 f = fopen(filename, "r");
3729 first_stream = NULL;
3730 last_stream = &first_stream;
3732 last_feed = &first_feed;
3736 audio_id = CODEC_ID_NONE;
3737 video_id = CODEC_ID_NONE;
3739 if (fgets(line, sizeof(line), f) == NULL)
3745 if (*p == '\0' || *p == '#')
3748 get_arg(cmd, sizeof(cmd), &p);
3750 if (!strcasecmp(cmd, "Port")) {
3751 get_arg(arg, sizeof(arg), &p);
3753 if (val < 1 || val > 65536) {
3754 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3755 filename, line_num, arg);
3758 my_http_addr.sin_port = htons(val);
3759 } else if (!strcasecmp(cmd, "BindAddress")) {
3760 get_arg(arg, sizeof(arg), &p);
3761 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3762 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3763 filename, line_num, arg);
3766 } else if (!strcasecmp(cmd, "NoDaemon")) {
3767 ffserver_daemon = 0;
3768 } else if (!strcasecmp(cmd, "RTSPPort")) {
3769 get_arg(arg, sizeof(arg), &p);
3771 if (val < 1 || val > 65536) {
3772 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3773 filename, line_num, arg);
3776 my_rtsp_addr.sin_port = htons(atoi(arg));
3777 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3778 get_arg(arg, sizeof(arg), &p);
3779 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3780 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3781 filename, line_num, arg);
3784 } else if (!strcasecmp(cmd, "MaxClients")) {
3785 get_arg(arg, sizeof(arg), &p);
3787 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3788 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3789 filename, line_num, arg);
3792 nb_max_connections = val;
3794 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3796 get_arg(arg, sizeof(arg), &p);
3798 if (llval < 10 || llval > 10000000) {
3799 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3800 filename, line_num, arg);
3803 max_bandwidth = llval;
3804 } else if (!strcasecmp(cmd, "CustomLog")) {
3805 if (!ffserver_debug)
3806 get_arg(logfilename, sizeof(logfilename), &p);
3807 } else if (!strcasecmp(cmd, "<Feed")) {
3808 /*********************************************/
3809 /* Feed related options */
3811 if (stream || feed) {
3812 fprintf(stderr, "%s:%d: Already in a tag\n",
3813 filename, line_num);
3815 feed = av_mallocz(sizeof(FFStream));
3816 /* add in stream list */
3817 *last_stream = feed;
3818 last_stream = &feed->next;
3819 /* add in feed list */
3821 last_feed = &feed->next_feed;
3823 get_arg(feed->filename, sizeof(feed->filename), &p);
3824 q = strrchr(feed->filename, '>');
3827 feed->fmt = guess_format("ffm", NULL, NULL);
3828 /* defaut feed file */
3829 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3830 "/tmp/%s.ffm", feed->filename);
3831 feed->feed_max_size = 5 * 1024 * 1024;
3833 feed->feed = feed; /* self feeding :-) */
3835 } else if (!strcasecmp(cmd, "Launch")) {
3839 feed->child_argv = av_mallocz(64 * sizeof(char *));
3841 for (i = 0; i < 62; i++) {
3842 get_arg(arg, sizeof(arg), &p);
3846 feed->child_argv[i] = av_strdup(arg);
3849 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3851 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3853 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3854 inet_ntoa(my_http_addr.sin_addr),
3855 ntohs(my_http_addr.sin_port), feed->filename);
3860 fprintf(stdout, "Launch commandline: ");
3861 for (j = 0; j <= i; j++)
3862 fprintf(stdout, "%s ", feed->child_argv[j]);
3863 fprintf(stdout, "\n");
3866 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3868 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3870 } else if (stream) {
3871 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3873 } else if (!strcasecmp(cmd, "File")) {
3875 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3877 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3878 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3883 get_arg(arg, sizeof(arg), &p);
3885 fsize = strtod(p1, &p1);
3886 switch(toupper(*p1)) {
3891 fsize *= 1024 * 1024;
3894 fsize *= 1024 * 1024 * 1024;
3897 feed->feed_max_size = (int64_t)fsize;
3899 } else if (!strcasecmp(cmd, "</Feed>")) {
3901 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3902 filename, line_num);
3906 } else if (!strcasecmp(cmd, "<Stream")) {
3907 /*********************************************/
3908 /* Stream related options */
3910 if (stream || feed) {
3911 fprintf(stderr, "%s:%d: Already in a tag\n",
3912 filename, line_num);
3914 const AVClass *class;
3915 stream = av_mallocz(sizeof(FFStream));
3916 *last_stream = stream;
3917 last_stream = &stream->next;
3919 get_arg(stream->filename, sizeof(stream->filename), &p);
3920 q = strrchr(stream->filename, '>');
3923 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3924 /* fetch avclass so AVOption works
3925 * FIXME try to use avcodec_get_context_defaults2
3926 * without changing defaults too much */
3927 avcodec_get_context_defaults(&video_enc);
3928 class = video_enc.av_class;
3929 memset(&audio_enc, 0, sizeof(AVCodecContext));
3930 memset(&video_enc, 0, sizeof(AVCodecContext));
3931 audio_enc.av_class = class;
3932 video_enc.av_class = class;
3933 audio_id = CODEC_ID_NONE;
3934 video_id = CODEC_ID_NONE;
3936 audio_id = stream->fmt->audio_codec;
3937 video_id = stream->fmt->video_codec;
3940 } else if (!strcasecmp(cmd, "Feed")) {
3941 get_arg(arg, sizeof(arg), &p);
3946 while (sfeed != NULL) {
3947 if (!strcmp(sfeed->filename, arg))
3949 sfeed = sfeed->next_feed;
3952 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3953 filename, line_num, arg);
3955 stream->feed = sfeed;
3957 } else if (!strcasecmp(cmd, "Format")) {
3958 get_arg(arg, sizeof(arg), &p);
3960 if (!strcmp(arg, "status")) {
3961 stream->stream_type = STREAM_TYPE_STATUS;
3964 stream->stream_type = STREAM_TYPE_LIVE;
3965 /* jpeg cannot be used here, so use single frame jpeg */
3966 if (!strcmp(arg, "jpeg"))
3967 strcpy(arg, "mjpeg");
3968 stream->fmt = guess_stream_format(arg, NULL, NULL);
3970 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3971 filename, line_num, arg);
3976 audio_id = stream->fmt->audio_codec;
3977 video_id = stream->fmt->video_codec;
3980 } else if (!strcasecmp(cmd, "InputFormat")) {
3981 get_arg(arg, sizeof(arg), &p);
3982 stream->ifmt = av_find_input_format(arg);
3983 if (!stream->ifmt) {
3984 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
3985 filename, line_num, arg);
3987 } else if (!strcasecmp(cmd, "FaviconURL")) {
3988 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3989 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3991 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3992 filename, line_num);
3995 } else if (!strcasecmp(cmd, "Author")) {
3997 get_arg(stream->author, sizeof(stream->author), &p);
3998 } else if (!strcasecmp(cmd, "Comment")) {
4000 get_arg(stream->comment, sizeof(stream->comment), &p);
4001 } else if (!strcasecmp(cmd, "Copyright")) {
4003 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4004 } else if (!strcasecmp(cmd, "Title")) {
4006 get_arg(stream->title, sizeof(stream->title), &p);
4007 } else if (!strcasecmp(cmd, "Preroll")) {
4008 get_arg(arg, sizeof(arg), &p);
4010 stream->prebuffer = atof(arg) * 1000;
4011 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4013 stream->send_on_key = 1;
4014 } else if (!strcasecmp(cmd, "AudioCodec")) {
4015 get_arg(arg, sizeof(arg), &p);
4016 audio_id = opt_audio_codec(arg);
4017 if (audio_id == CODEC_ID_NONE) {
4018 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4019 filename, line_num, arg);
4022 } else if (!strcasecmp(cmd, "VideoCodec")) {
4023 get_arg(arg, sizeof(arg), &p);
4024 video_id = opt_video_codec(arg);
4025 if (video_id == CODEC_ID_NONE) {
4026 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4027 filename, line_num, arg);
4030 } else if (!strcasecmp(cmd, "MaxTime")) {
4031 get_arg(arg, sizeof(arg), &p);
4033 stream->max_time = atof(arg) * 1000;
4034 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4035 get_arg(arg, sizeof(arg), &p);
4037 audio_enc.bit_rate = atoi(arg) * 1000;
4038 } else if (!strcasecmp(cmd, "AudioChannels")) {
4039 get_arg(arg, sizeof(arg), &p);
4041 audio_enc.channels = atoi(arg);
4042 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4043 get_arg(arg, sizeof(arg), &p);
4045 audio_enc.sample_rate = atoi(arg);
4046 } else if (!strcasecmp(cmd, "AudioQuality")) {
4047 get_arg(arg, sizeof(arg), &p);
4049 // audio_enc.quality = atof(arg) * 1000;
4051 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4053 int minrate, maxrate;
4055 get_arg(arg, sizeof(arg), &p);
4057 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4058 video_enc.rc_min_rate = minrate * 1000;
4059 video_enc.rc_max_rate = maxrate * 1000;
4061 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4062 filename, line_num, arg);
4066 } else if (!strcasecmp(cmd, "Debug")) {
4068 get_arg(arg, sizeof(arg), &p);
4069 video_enc.debug = strtol(arg,0,0);
4071 } else if (!strcasecmp(cmd, "Strict")) {
4073 get_arg(arg, sizeof(arg), &p);
4074 video_enc.strict_std_compliance = atoi(arg);
4076 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4078 get_arg(arg, sizeof(arg), &p);
4079 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4081 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4083 get_arg(arg, sizeof(arg), &p);
4084 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4086 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4087 get_arg(arg, sizeof(arg), &p);
4089 video_enc.bit_rate = atoi(arg) * 1000;
4091 } else if (!strcasecmp(cmd, "VideoSize")) {
4092 get_arg(arg, sizeof(arg), &p);
4094 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4095 if ((video_enc.width % 16) != 0 ||
4096 (video_enc.height % 16) != 0) {
4097 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4098 filename, line_num);
4102 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4103 get_arg(arg, sizeof(arg), &p);
4105 AVRational frame_rate;
4106 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4107 fprintf(stderr, "Incorrect frame rate\n");
4110 video_enc.time_base.num = frame_rate.den;
4111 video_enc.time_base.den = frame_rate.num;
4114 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4115 get_arg(arg, sizeof(arg), &p);
4117 video_enc.gop_size = atoi(arg);
4118 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4120 video_enc.gop_size = 1;
4121 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4123 video_enc.mb_decision = FF_MB_DECISION_BITS;
4124 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4126 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4127 video_enc.flags |= CODEC_FLAG_4MV;
4129 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4130 !strcasecmp(cmd, "AVOptionAudio")) {
4132 AVCodecContext *avctx;
4134 get_arg(arg, sizeof(arg), &p);
4135 get_arg(arg2, sizeof(arg2), &p);
4136 if (!strcasecmp(cmd, "AVOptionVideo")) {
4138 type = AV_OPT_FLAG_VIDEO_PARAM;
4141 type = AV_OPT_FLAG_AUDIO_PARAM;
4143 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4144 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4147 } else if (!strcasecmp(cmd, "VideoTag")) {
4148 get_arg(arg, sizeof(arg), &p);
4149 if ((strlen(arg) == 4) && stream)
4150 video_enc.codec_tag = ff_get_fourcc(arg);
4151 } else if (!strcasecmp(cmd, "BitExact")) {
4153 video_enc.flags |= CODEC_FLAG_BITEXACT;
4154 } else if (!strcasecmp(cmd, "DctFastint")) {
4156 video_enc.dct_algo = FF_DCT_FASTINT;
4157 } else if (!strcasecmp(cmd, "IdctSimple")) {
4159 video_enc.idct_algo = FF_IDCT_SIMPLE;
4160 } else if (!strcasecmp(cmd, "Qscale")) {
4161 get_arg(arg, sizeof(arg), &p);
4163 video_enc.flags |= CODEC_FLAG_QSCALE;
4164 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4166 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4167 get_arg(arg, sizeof(arg), &p);
4169 video_enc.max_qdiff = atoi(arg);
4170 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4171 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4172 filename, line_num);
4176 } else if (!strcasecmp(cmd, "VideoQMax")) {
4177 get_arg(arg, sizeof(arg), &p);
4179 video_enc.qmax = atoi(arg);
4180 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4181 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4182 filename, line_num);
4186 } else if (!strcasecmp(cmd, "VideoQMin")) {
4187 get_arg(arg, sizeof(arg), &p);
4189 video_enc.qmin = atoi(arg);
4190 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4191 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4192 filename, line_num);
4196 } else if (!strcasecmp(cmd, "LumaElim")) {
4197 get_arg(arg, sizeof(arg), &p);
4199 video_enc.luma_elim_threshold = atoi(arg);
4200 } else if (!strcasecmp(cmd, "ChromaElim")) {
4201 get_arg(arg, sizeof(arg), &p);
4203 video_enc.chroma_elim_threshold = atoi(arg);
4204 } else if (!strcasecmp(cmd, "LumiMask")) {
4205 get_arg(arg, sizeof(arg), &p);
4207 video_enc.lumi_masking = atof(arg);
4208 } else if (!strcasecmp(cmd, "DarkMask")) {
4209 get_arg(arg, sizeof(arg), &p);
4211 video_enc.dark_masking = atof(arg);
4212 } else if (!strcasecmp(cmd, "NoVideo")) {
4213 video_id = CODEC_ID_NONE;
4214 } else if (!strcasecmp(cmd, "NoAudio")) {
4215 audio_id = CODEC_ID_NONE;
4216 } else if (!strcasecmp(cmd, "ACL")) {
4219 get_arg(arg, sizeof(arg), &p);
4220 if (strcasecmp(arg, "allow") == 0)
4221 acl.action = IP_ALLOW;
4222 else if (strcasecmp(arg, "deny") == 0)
4223 acl.action = IP_DENY;
4225 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4226 filename, line_num, arg);
4230 get_arg(arg, sizeof(arg), &p);
4232 if (resolve_host(&acl.first, arg) != 0) {
4233 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4234 filename, line_num, arg);
4237 acl.last = acl.first;
4239 get_arg(arg, sizeof(arg), &p);
4242 if (resolve_host(&acl.last, arg) != 0) {
4243 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4244 filename, line_num, arg);
4250 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4251 IPAddressACL **naclp = 0;
4257 naclp = &stream->acl;
4261 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4262 filename, line_num);
4268 naclp = &(*naclp)->next;
4273 } else if (!strcasecmp(cmd, "RTSPOption")) {
4274 get_arg(arg, sizeof(arg), &p);
4276 av_freep(&stream->rtsp_option);
4277 stream->rtsp_option = av_strdup(arg);
4279 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4280 get_arg(arg, sizeof(arg), &p);
4282 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4283 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4284 filename, line_num, arg);
4287 stream->is_multicast = 1;
4288 stream->loop = 1; /* default is looping */
4290 } else if (!strcasecmp(cmd, "MulticastPort")) {
4291 get_arg(arg, sizeof(arg), &p);
4293 stream->multicast_port = atoi(arg);
4294 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4295 get_arg(arg, sizeof(arg), &p);
4297 stream->multicast_ttl = atoi(arg);
4298 } else if (!strcasecmp(cmd, "NoLoop")) {
4301 } else if (!strcasecmp(cmd, "</Stream>")) {
4303 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4304 filename, line_num);
4307 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4308 if (audio_id != CODEC_ID_NONE) {
4309 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4310 audio_enc.codec_id = audio_id;
4311 add_codec(stream, &audio_enc);
4313 if (video_id != CODEC_ID_NONE) {
4314 video_enc.codec_type = CODEC_TYPE_VIDEO;
4315 video_enc.codec_id = video_id;
4316 add_codec(stream, &video_enc);
4321 } else if (!strcasecmp(cmd, "<Redirect")) {
4322 /*********************************************/
4324 if (stream || feed || redirect) {
4325 fprintf(stderr, "%s:%d: Already in a tag\n",
4326 filename, line_num);
4329 redirect = av_mallocz(sizeof(FFStream));
4330 *last_stream = redirect;
4331 last_stream = &redirect->next;
4333 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4334 q = strrchr(redirect->filename, '>');
4337 redirect->stream_type = STREAM_TYPE_REDIRECT;
4339 } else if (!strcasecmp(cmd, "URL")) {
4341 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4342 } else if (!strcasecmp(cmd, "</Redirect>")) {
4344 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4345 filename, line_num);
4348 if (!redirect->feed_filename[0]) {
4349 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4350 filename, line_num);
4355 } else if (!strcasecmp(cmd, "LoadModule")) {
4356 get_arg(arg, sizeof(arg), &p);
4360 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4361 filename, line_num, arg);
4365 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4366 filename, line_num, cmd);
4378 static void handle_child_exit(int sig)
4383 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4386 for (feed = first_feed; feed; feed = feed->next) {
4387 if (feed->pid == pid) {
4388 int uptime = time(0) - feed->pid_start;
4391 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4394 /* Turn off any more restarts */
4395 feed->child_argv = 0;
4400 need_to_start_children = 1;
4403 static void opt_debug()
4406 ffserver_daemon = 0;
4407 logfilename[0] = '-';
4410 static void opt_show_help(void)
4412 printf("usage: ffserver [options]\n"
4413 "Hyper fast multi format Audio/Video streaming server\n");
4415 show_help_options(options, "Main options:\n", 0, 0);
4418 static const OptionDef options[] = {
4419 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4420 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4421 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4422 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4423 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4424 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4425 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4429 int main(int argc, char **argv)
4431 struct sigaction sigact;
4437 config_filename = "/etc/ffserver.conf";
4439 my_program_name = argv[0];
4440 my_program_dir = getcwd(0, 0);
4441 ffserver_daemon = 1;
4443 parse_options(argc, argv, options, NULL);
4445 unsetenv("http_proxy"); /* Kill the http_proxy */
4447 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4449 /* address on which the server will handle HTTP connections */
4450 my_http_addr.sin_family = AF_INET;
4451 my_http_addr.sin_port = htons (8080);
4452 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4454 /* address on which the server will handle RTSP connections */
4455 my_rtsp_addr.sin_family = AF_INET;
4456 my_rtsp_addr.sin_port = htons (5454);
4457 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4459 nb_max_connections = 5;
4460 max_bandwidth = 1000;
4461 first_stream = NULL;
4463 memset(&sigact, 0, sizeof(sigact));
4464 sigact.sa_handler = handle_child_exit;
4465 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4466 sigaction(SIGCHLD, &sigact, 0);
4468 if (parse_ffconfig(config_filename) < 0) {
4469 fprintf(stderr, "Incorrect config file - exiting.\n");
4473 build_file_streams();
4475 build_feed_streams();
4477 compute_bandwidth();
4479 /* put the process in background and detach it from its TTY */
4480 if (ffserver_daemon) {
4487 } else if (pid > 0) {
4495 open("/dev/null", O_RDWR);
4496 if (strcmp(logfilename, "-") != 0) {
4506 signal(SIGPIPE, SIG_IGN);
4508 /* open log file if needed */
4509 if (logfilename[0] != '\0') {
4510 if (!strcmp(logfilename, "-"))
4513 logfile = fopen(logfilename, "a");
4516 if (http_server() < 0) {
4517 http_log("Could not start server\n");