2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
28 #include "libavutil/random.h"
29 #include "libavutil/avstring.h"
30 #include "libavformat/avformat.h"
31 #include "libavformat/network.h"
32 #include "libavformat/os_support.h"
33 #include "libavformat/rtp.h"
34 #include "libavformat/rtsp.h"
35 #include "libavcodec/opt.h"
39 #include <sys/ioctl.h>
45 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
57 const char program_name[] = "FFserver";
58 const int program_birth_year = 2000;
60 static const OptionDef options[];
62 /* maximum number of simultaneous HTTP connections */
63 #define HTTP_MAX_CONNECTIONS 2000
66 HTTPSTATE_WAIT_REQUEST,
67 HTTPSTATE_SEND_HEADER,
68 HTTPSTATE_SEND_DATA_HEADER,
69 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
70 HTTPSTATE_SEND_DATA_TRAILER,
71 HTTPSTATE_RECEIVE_DATA,
72 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
75 RTSPSTATE_WAIT_REQUEST,
77 RTSPSTATE_SEND_PACKET,
80 static const char *http_state[] = {
96 #define IOBUFFER_INIT_SIZE 8192
98 /* timeouts are in ms */
99 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
100 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
102 #define SYNC_TIMEOUT (10 * 1000)
105 int64_t count1, count2;
106 int64_t time1, time2;
109 /* context associated with one connection */
110 typedef struct HTTPContext {
111 enum HTTPState state;
112 int fd; /* socket file descriptor */
113 struct sockaddr_in from_addr; /* origin */
114 struct pollfd *poll_entry; /* used when polling */
116 uint8_t *buffer_ptr, *buffer_end;
119 struct HTTPContext *next;
120 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
124 /* input format handling */
125 AVFormatContext *fmt_in;
126 int64_t start_time; /* In milliseconds - this wraps fairly often */
127 int64_t first_pts; /* initial pts value */
128 int64_t cur_pts; /* current pts value from the stream in us */
129 int64_t cur_frame_duration; /* duration of the current frame in us */
130 int cur_frame_bytes; /* output frame size, needed to compute
131 the time at which we send each
133 int pts_stream_index; /* stream we choose as clock reference */
134 int64_t cur_clock; /* current clock reference value in us */
135 /* output format handling */
136 struct FFStream *stream;
137 /* -1 is invalid stream */
138 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
139 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
141 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
142 int last_packet_sent; /* true if last data packet was sent */
144 DataRateData datarate;
151 int is_packetized; /* if true, the stream is packetized */
152 int packet_stream_index; /* current stream for output in state machine */
154 /* RTSP state specific */
155 uint8_t *pb_buffer; /* XXX: use that in all the code */
157 int seq; /* RTSP sequence number */
159 /* RTP state specific */
160 enum RTSPProtocol rtp_protocol;
161 char session_id[32]; /* session id */
162 AVFormatContext *rtp_ctx[MAX_STREAMS];
164 /* RTP/UDP specific */
165 URLContext *rtp_handles[MAX_STREAMS];
167 /* RTP/TCP specific */
168 struct HTTPContext *rtsp_c;
169 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
172 /* each generated stream is described here */
176 STREAM_TYPE_REDIRECT,
179 enum IPAddressAction {
184 typedef struct IPAddressACL {
185 struct IPAddressACL *next;
186 enum IPAddressAction action;
187 /* These are in host order */
188 struct in_addr first;
192 /* description of each stream of the ffserver.conf file */
193 typedef struct FFStream {
194 enum StreamType stream_type;
195 char filename[1024]; /* stream filename */
196 struct FFStream *feed; /* feed we are using (can be null if
198 AVFormatParameters *ap_in; /* input parameters */
199 AVInputFormat *ifmt; /* if non NULL, force input format */
203 int prebuffer; /* Number of millseconds early to start */
204 int64_t max_time; /* Number of milliseconds to run */
206 AVStream *streams[MAX_STREAMS];
207 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
208 char feed_filename[1024]; /* file name of the feed storage, or
209 input file name for a stream */
214 pid_t pid; /* Of ffmpeg process */
215 time_t pid_start; /* Of ffmpeg process */
217 struct FFStream *next;
218 unsigned bandwidth; /* bandwidth, in kbits/s */
221 /* multicast specific */
223 struct in_addr multicast_ip;
224 int multicast_port; /* first port used for multicast */
226 int loop; /* if true, send the stream in loops (only meaningful if file) */
229 int feed_opened; /* true if someone is writing to the feed */
230 int is_feed; /* true if it is a feed */
231 int readonly; /* True if writing is prohibited to the file */
233 int64_t bytes_served;
234 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
235 int64_t feed_write_index; /* current write position in feed (it wraps around) */
236 int64_t feed_size; /* current size of feed */
237 struct FFStream *next_feed;
240 typedef struct FeedData {
241 long long data_count;
242 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
245 static struct sockaddr_in my_http_addr;
246 static struct sockaddr_in my_rtsp_addr;
248 static char logfilename[1024];
249 static HTTPContext *first_http_ctx;
250 static FFStream *first_feed; /* contains only feeds */
251 static FFStream *first_stream; /* contains all streams, including feeds */
253 static void new_connection(int server_fd, int is_rtsp);
254 static void close_connection(HTTPContext *c);
257 static int handle_connection(HTTPContext *c);
258 static int http_parse_request(HTTPContext *c);
259 static int http_send_data(HTTPContext *c);
260 static void compute_status(HTTPContext *c);
261 static int open_input_stream(HTTPContext *c, const char *info);
262 static int http_start_receive_data(HTTPContext *c);
263 static int http_receive_data(HTTPContext *c);
266 static int rtsp_parse_request(HTTPContext *c);
267 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
268 static void rtsp_cmd_options(HTTPContext *c, const char *url);
269 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
270 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
271 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
275 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
276 struct in_addr my_ip);
279 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
280 FFStream *stream, const char *session_id,
281 enum RTSPProtocol rtp_protocol);
282 static int rtp_new_av_stream(HTTPContext *c,
283 int stream_index, struct sockaddr_in *dest_addr,
284 HTTPContext *rtsp_c);
286 static const char *my_program_name;
287 static const char *my_program_dir;
289 static const char *config_filename;
290 static int ffserver_debug;
291 static int ffserver_daemon;
292 static int no_launch;
293 static int need_to_start_children;
295 static int nb_max_connections = 5;
296 static int nb_connections;
298 static uint64_t max_bandwidth = 1000;
299 static uint64_t current_bandwidth;
301 static int64_t cur_time; // Making this global saves on passing it around everywhere
303 static AVRandomState random_state;
305 static FILE *logfile = NULL;
307 static char *ctime1(char *buf2)
315 p = buf2 + strlen(p) - 1;
321 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
323 static int print_prefix = 1;
331 fprintf(logfile, "%s ", buf);
333 print_prefix = strstr(fmt, "\n") != NULL;
334 vfprintf(logfile, fmt, ap);
340 static void log_connection(HTTPContext *c)
347 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
348 inet_ntoa(c->from_addr.sin_addr),
349 ctime1(buf2), c->method, c->url,
350 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
353 static void update_datarate(DataRateData *drd, int64_t count)
355 if (!drd->time1 && !drd->count1) {
356 drd->time1 = drd->time2 = cur_time;
357 drd->count1 = drd->count2 = count;
358 } else if (cur_time - drd->time2 > 5000) {
359 drd->time1 = drd->time2;
360 drd->count1 = drd->count2;
361 drd->time2 = cur_time;
366 /* In bytes per second */
367 static int compute_datarate(DataRateData *drd, int64_t count)
369 if (cur_time == drd->time1)
372 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
376 static void start_children(FFStream *feed)
381 for (; feed; feed = feed->next) {
382 if (feed->child_argv && !feed->pid) {
383 feed->pid_start = time(0);
388 http_log("Unable to create children\n");
397 for (i = 3; i < 256; i++)
400 if (!ffserver_debug) {
401 i = open("/dev/null", O_RDWR);
410 av_strlcpy(pathname, my_program_name, sizeof(pathname));
412 slash = strrchr(pathname, '/');
417 strcpy(slash, "ffmpeg");
419 /* This is needed to make relative pathnames work */
420 chdir(my_program_dir);
422 signal(SIGPIPE, SIG_DFL);
424 execvp(pathname, feed->child_argv);
432 /* open a listening socket */
433 static int socket_open_listen(struct sockaddr_in *my_addr)
437 server_fd = socket(AF_INET,SOCK_STREAM,0);
444 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
446 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
448 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
450 closesocket(server_fd);
454 if (listen (server_fd, 5) < 0) {
456 closesocket(server_fd);
459 ff_socket_nonblock(server_fd, 1);
464 /* start all multicast streams */
465 static void start_multicast(void)
470 struct sockaddr_in dest_addr;
471 int default_port, stream_index;
474 for(stream = first_stream; stream != NULL; stream = stream->next) {
475 if (stream->is_multicast) {
476 /* open the RTP connection */
477 snprintf(session_id, sizeof(session_id), "%08x%08x",
478 av_random(&random_state), av_random(&random_state));
480 /* choose a port if none given */
481 if (stream->multicast_port == 0) {
482 stream->multicast_port = default_port;
486 dest_addr.sin_family = AF_INET;
487 dest_addr.sin_addr = stream->multicast_ip;
488 dest_addr.sin_port = htons(stream->multicast_port);
490 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
491 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
495 if (open_input_stream(rtp_c, "") < 0) {
496 http_log("Could not open input stream for stream '%s'\n",
501 /* open each RTP stream */
502 for(stream_index = 0; stream_index < stream->nb_streams;
504 dest_addr.sin_port = htons(stream->multicast_port +
506 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
507 http_log("Could not open output stream '%s/streamid=%d'\n",
508 stream->filename, stream_index);
513 /* change state to send data */
514 rtp_c->state = HTTPSTATE_SEND_DATA;
519 /* main loop of the http server */
520 static int http_server(void)
522 int server_fd = 0, rtsp_server_fd = 0;
523 int ret, delay, delay1;
524 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
525 HTTPContext *c, *c_next;
527 if (my_http_addr.sin_port) {
528 server_fd = socket_open_listen(&my_http_addr);
533 if (my_rtsp_addr.sin_port) {
534 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
535 if (rtsp_server_fd < 0)
539 if (!rtsp_server_fd && !server_fd) {
540 http_log("HTTP and RTSP disabled.\n");
544 http_log("ffserver started.\n");
546 start_children(first_feed);
548 first_http_ctx = NULL;
554 poll_entry = poll_table;
556 poll_entry->fd = server_fd;
557 poll_entry->events = POLLIN;
560 if (rtsp_server_fd) {
561 poll_entry->fd = rtsp_server_fd;
562 poll_entry->events = POLLIN;
566 /* wait for events on each HTTP handle */
573 case HTTPSTATE_SEND_HEADER:
574 case RTSPSTATE_SEND_REPLY:
575 case RTSPSTATE_SEND_PACKET:
576 c->poll_entry = poll_entry;
578 poll_entry->events = POLLOUT;
581 case HTTPSTATE_SEND_DATA_HEADER:
582 case HTTPSTATE_SEND_DATA:
583 case HTTPSTATE_SEND_DATA_TRAILER:
584 if (!c->is_packetized) {
585 /* for TCP, we output as much as we can (may need to put a limit) */
586 c->poll_entry = poll_entry;
588 poll_entry->events = POLLOUT;
591 /* when ffserver is doing the timing, we work by
592 looking at which packet need to be sent every
594 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
599 case HTTPSTATE_WAIT_REQUEST:
600 case HTTPSTATE_RECEIVE_DATA:
601 case HTTPSTATE_WAIT_FEED:
602 case RTSPSTATE_WAIT_REQUEST:
603 /* need to catch errors */
604 c->poll_entry = poll_entry;
606 poll_entry->events = POLLIN;/* Maybe this will work */
610 c->poll_entry = NULL;
616 /* wait for an event on one connection. We poll at least every
617 second to handle timeouts */
619 ret = poll(poll_table, poll_entry - poll_table, delay);
620 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
621 ff_neterrno() != FF_NETERROR(EINTR))
625 cur_time = av_gettime() / 1000;
627 if (need_to_start_children) {
628 need_to_start_children = 0;
629 start_children(first_feed);
632 /* now handle the events */
633 for(c = first_http_ctx; c != NULL; c = c_next) {
635 if (handle_connection(c) < 0) {
636 /* close and free the connection */
642 poll_entry = poll_table;
644 /* new HTTP connection request ? */
645 if (poll_entry->revents & POLLIN)
646 new_connection(server_fd, 0);
649 if (rtsp_server_fd) {
650 /* new RTSP connection request ? */
651 if (poll_entry->revents & POLLIN)
652 new_connection(rtsp_server_fd, 1);
657 /* start waiting for a new HTTP/RTSP request */
658 static void start_wait_request(HTTPContext *c, int is_rtsp)
660 c->buffer_ptr = c->buffer;
661 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
664 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
665 c->state = RTSPSTATE_WAIT_REQUEST;
667 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
668 c->state = HTTPSTATE_WAIT_REQUEST;
672 static void new_connection(int server_fd, int is_rtsp)
674 struct sockaddr_in from_addr;
676 HTTPContext *c = NULL;
678 len = sizeof(from_addr);
679 fd = accept(server_fd, (struct sockaddr *)&from_addr,
682 http_log("error during accept %s\n", strerror(errno));
685 ff_socket_nonblock(fd, 1);
687 /* XXX: should output a warning page when coming
688 close to the connection limit */
689 if (nb_connections >= nb_max_connections)
692 /* add a new connection */
693 c = av_mallocz(sizeof(HTTPContext));
698 c->poll_entry = NULL;
699 c->from_addr = from_addr;
700 c->buffer_size = IOBUFFER_INIT_SIZE;
701 c->buffer = av_malloc(c->buffer_size);
705 c->next = first_http_ctx;
709 start_wait_request(c, is_rtsp);
721 static void close_connection(HTTPContext *c)
723 HTTPContext **cp, *c1;
725 AVFormatContext *ctx;
729 /* remove connection from list */
730 cp = &first_http_ctx;
731 while ((*cp) != NULL) {
739 /* remove references, if any (XXX: do it faster) */
740 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
745 /* remove connection associated resources */
749 /* close each frame parser */
750 for(i=0;i<c->fmt_in->nb_streams;i++) {
751 st = c->fmt_in->streams[i];
752 if (st->codec->codec)
753 avcodec_close(st->codec);
755 av_close_input_file(c->fmt_in);
758 /* free RTP output streams if any */
761 nb_streams = c->stream->nb_streams;
763 for(i=0;i<nb_streams;i++) {
766 av_write_trailer(ctx);
769 h = c->rtp_handles[i];
776 if (!c->last_packet_sent) {
779 if (url_open_dyn_buf(&ctx->pb) >= 0) {
780 av_write_trailer(ctx);
781 av_freep(&c->pb_buffer);
782 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
787 for(i=0; i<ctx->nb_streams; i++)
788 av_free(ctx->streams[i]);
790 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
791 current_bandwidth -= c->stream->bandwidth;
793 /* signal that there is no feed if we are the feeder socket */
794 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
795 c->stream->feed_opened = 0;
799 av_freep(&c->pb_buffer);
800 av_freep(&c->packet_buffer);
806 static int handle_connection(HTTPContext *c)
811 case HTTPSTATE_WAIT_REQUEST:
812 case RTSPSTATE_WAIT_REQUEST:
814 if ((c->timeout - cur_time) < 0)
816 if (c->poll_entry->revents & (POLLERR | POLLHUP))
819 /* no need to read if no events */
820 if (!(c->poll_entry->revents & POLLIN))
824 len = recv(c->fd, c->buffer_ptr, 1, 0);
826 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
827 ff_neterrno() != FF_NETERROR(EINTR))
829 } else if (len == 0) {
832 /* search for end of request. */
834 c->buffer_ptr += len;
836 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
837 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
838 /* request found : parse it and reply */
839 if (c->state == HTTPSTATE_WAIT_REQUEST) {
840 ret = http_parse_request(c);
842 ret = rtsp_parse_request(c);
846 } else if (ptr >= c->buffer_end) {
847 /* request too long: cannot do anything */
849 } else goto read_loop;
853 case HTTPSTATE_SEND_HEADER:
854 if (c->poll_entry->revents & (POLLERR | POLLHUP))
857 /* no need to write if no events */
858 if (!(c->poll_entry->revents & POLLOUT))
860 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
862 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
863 ff_neterrno() != FF_NETERROR(EINTR)) {
864 /* error : close connection */
865 av_freep(&c->pb_buffer);
869 c->buffer_ptr += len;
871 c->stream->bytes_served += len;
872 c->data_count += len;
873 if (c->buffer_ptr >= c->buffer_end) {
874 av_freep(&c->pb_buffer);
878 /* all the buffer was sent : synchronize to the incoming stream */
879 c->state = HTTPSTATE_SEND_DATA_HEADER;
880 c->buffer_ptr = c->buffer_end = c->buffer;
885 case HTTPSTATE_SEND_DATA:
886 case HTTPSTATE_SEND_DATA_HEADER:
887 case HTTPSTATE_SEND_DATA_TRAILER:
888 /* for packetized output, we consider we can always write (the
889 input streams sets the speed). It may be better to verify
890 that we do not rely too much on the kernel queues */
891 if (!c->is_packetized) {
892 if (c->poll_entry->revents & (POLLERR | POLLHUP))
895 /* no need to read if no events */
896 if (!(c->poll_entry->revents & POLLOUT))
899 if (http_send_data(c) < 0)
901 /* close connection if trailer sent */
902 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
905 case HTTPSTATE_RECEIVE_DATA:
906 /* no need to read if no events */
907 if (c->poll_entry->revents & (POLLERR | POLLHUP))
909 if (!(c->poll_entry->revents & POLLIN))
911 if (http_receive_data(c) < 0)
914 case HTTPSTATE_WAIT_FEED:
915 /* no need to read if no events */
916 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
919 /* nothing to do, we'll be waken up by incoming feed packets */
922 case RTSPSTATE_SEND_REPLY:
923 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
924 av_freep(&c->pb_buffer);
927 /* no need to write if no events */
928 if (!(c->poll_entry->revents & POLLOUT))
930 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
932 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
933 ff_neterrno() != FF_NETERROR(EINTR)) {
934 /* error : close connection */
935 av_freep(&c->pb_buffer);
939 c->buffer_ptr += len;
940 c->data_count += len;
941 if (c->buffer_ptr >= c->buffer_end) {
942 /* all the buffer was sent : wait for a new request */
943 av_freep(&c->pb_buffer);
944 start_wait_request(c, 1);
948 case RTSPSTATE_SEND_PACKET:
949 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
950 av_freep(&c->packet_buffer);
953 /* no need to write if no events */
954 if (!(c->poll_entry->revents & POLLOUT))
956 len = send(c->fd, c->packet_buffer_ptr,
957 c->packet_buffer_end - c->packet_buffer_ptr, 0);
959 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
960 ff_neterrno() != FF_NETERROR(EINTR)) {
961 /* error : close connection */
962 av_freep(&c->packet_buffer);
966 c->packet_buffer_ptr += len;
967 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
968 /* all the buffer was sent : wait for a new request */
969 av_freep(&c->packet_buffer);
970 c->state = RTSPSTATE_WAIT_REQUEST;
974 case HTTPSTATE_READY:
983 static int extract_rates(char *rates, int ratelen, const char *request)
987 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
988 if (strncasecmp(p, "Pragma:", 7) == 0) {
989 const char *q = p + 7;
991 while (*q && *q != '\n' && isspace(*q))
994 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1000 memset(rates, 0xff, ratelen);
1003 while (*q && *q != '\n' && *q != ':')
1006 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1010 if (stream_no < ratelen && stream_no >= 0)
1011 rates[stream_no] = rate_no;
1013 while (*q && *q != '\n' && !isspace(*q))
1020 p = strchr(p, '\n');
1030 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1033 int best_bitrate = 100000000;
1036 for (i = 0; i < feed->nb_streams; i++) {
1037 AVCodecContext *feed_codec = feed->streams[i]->codec;
1039 if (feed_codec->codec_id != codec->codec_id ||
1040 feed_codec->sample_rate != codec->sample_rate ||
1041 feed_codec->width != codec->width ||
1042 feed_codec->height != codec->height)
1045 /* Potential stream */
1047 /* We want the fastest stream less than bit_rate, or the slowest
1048 * faster than bit_rate
1051 if (feed_codec->bit_rate <= bit_rate) {
1052 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1053 best_bitrate = feed_codec->bit_rate;
1057 if (feed_codec->bit_rate < best_bitrate) {
1058 best_bitrate = feed_codec->bit_rate;
1067 static int modify_current_stream(HTTPContext *c, char *rates)
1070 FFStream *req = c->stream;
1071 int action_required = 0;
1073 /* Not much we can do for a feed */
1077 for (i = 0; i < req->nb_streams; i++) {
1078 AVCodecContext *codec = req->streams[i]->codec;
1082 c->switch_feed_streams[i] = req->feed_streams[i];
1085 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1088 /* Wants off or slow */
1089 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1091 /* This doesn't work well when it turns off the only stream! */
1092 c->switch_feed_streams[i] = -2;
1093 c->feed_streams[i] = -2;
1098 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1099 action_required = 1;
1102 return action_required;
1106 static void do_switch_stream(HTTPContext *c, int i)
1108 if (c->switch_feed_streams[i] >= 0) {
1110 c->feed_streams[i] = c->switch_feed_streams[i];
1113 /* Now update the stream */
1115 c->switch_feed_streams[i] = -1;
1118 /* XXX: factorize in utils.c ? */
1119 /* XXX: take care with different space meaning */
1120 static void skip_spaces(const char **pp)
1124 while (*p == ' ' || *p == '\t')
1129 static void get_word(char *buf, int buf_size, const char **pp)
1137 while (!isspace(*p) && *p != '\0') {
1138 if ((q - buf) < buf_size - 1)
1147 static int validate_acl(FFStream *stream, HTTPContext *c)
1149 enum IPAddressAction last_action = IP_DENY;
1151 struct in_addr *src = &c->from_addr.sin_addr;
1152 unsigned long src_addr = src->s_addr;
1154 for (acl = stream->acl; acl; acl = acl->next) {
1155 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1156 return (acl->action == IP_ALLOW) ? 1 : 0;
1157 last_action = acl->action;
1160 /* Nothing matched, so return not the last action */
1161 return (last_action == IP_DENY) ? 1 : 0;
1164 /* compute the real filename of a file by matching it without its
1165 extensions to all the stream filenames */
1166 static void compute_real_filename(char *filename, int max_size)
1173 /* compute filename by matching without the file extensions */
1174 av_strlcpy(file1, filename, sizeof(file1));
1175 p = strrchr(file1, '.');
1178 for(stream = first_stream; stream != NULL; stream = stream->next) {
1179 av_strlcpy(file2, stream->filename, sizeof(file2));
1180 p = strrchr(file2, '.');
1183 if (!strcmp(file1, file2)) {
1184 av_strlcpy(filename, stream->filename, max_size);
1199 /* parse http request and prepare header */
1200 static int http_parse_request(HTTPContext *c)
1203 enum RedirType redir_type;
1205 char info[1024], filename[1024];
1209 const char *mime_type;
1213 char *useragent = 0;
1216 get_word(cmd, sizeof(cmd), (const char **)&p);
1217 av_strlcpy(c->method, cmd, sizeof(c->method));
1219 if (!strcmp(cmd, "GET"))
1221 else if (!strcmp(cmd, "POST"))
1226 get_word(url, sizeof(url), (const char **)&p);
1227 av_strlcpy(c->url, url, sizeof(c->url));
1229 get_word(protocol, sizeof(protocol), (const char **)&p);
1230 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1233 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1236 http_log("New connection: %s %s\n", cmd, url);
1238 /* find the filename and the optional info string in the request */
1239 p = strchr(url, '?');
1241 av_strlcpy(info, p, sizeof(info));
1246 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1248 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1249 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1251 if (*useragent && *useragent != '\n' && isspace(*useragent))
1255 p = strchr(p, '\n');
1262 redir_type = REDIR_NONE;
1263 if (match_ext(filename, "asx")) {
1264 redir_type = REDIR_ASX;
1265 filename[strlen(filename)-1] = 'f';
1266 } else if (match_ext(filename, "asf") &&
1267 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1268 /* if this isn't WMP or lookalike, return the redirector file */
1269 redir_type = REDIR_ASF;
1270 } else if (match_ext(filename, "rpm,ram")) {
1271 redir_type = REDIR_RAM;
1272 strcpy(filename + strlen(filename)-2, "m");
1273 } else if (match_ext(filename, "rtsp")) {
1274 redir_type = REDIR_RTSP;
1275 compute_real_filename(filename, sizeof(filename) - 1);
1276 } else if (match_ext(filename, "sdp")) {
1277 redir_type = REDIR_SDP;
1278 compute_real_filename(filename, sizeof(filename) - 1);
1281 // "redirect" / request to index.html
1282 if (!strlen(filename))
1283 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1285 stream = first_stream;
1286 while (stream != NULL) {
1287 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1289 stream = stream->next;
1291 if (stream == NULL) {
1292 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1297 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1298 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1300 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1301 c->http_error = 301;
1303 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1304 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1305 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1306 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1307 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1308 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1309 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1311 /* prepare output buffer */
1312 c->buffer_ptr = c->buffer;
1314 c->state = HTTPSTATE_SEND_HEADER;
1318 /* If this is WMP, get the rate information */
1319 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1320 if (modify_current_stream(c, ratebuf)) {
1321 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1322 if (c->switch_feed_streams[i] >= 0)
1323 do_switch_stream(c, i);
1328 /* If already streaming this feed, do not let start another feeder. */
1329 if (stream->feed_opened) {
1330 snprintf(msg, sizeof(msg), "This feed is already being received.");
1334 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1335 current_bandwidth += stream->bandwidth;
1337 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1338 c->http_error = 200;
1340 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1341 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1342 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1343 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1344 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");
1345 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",
1346 current_bandwidth, max_bandwidth);
1347 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1349 /* prepare output buffer */
1350 c->buffer_ptr = c->buffer;
1352 c->state = HTTPSTATE_SEND_HEADER;
1356 if (redir_type != REDIR_NONE) {
1359 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1360 if (strncasecmp(p, "Host:", 5) == 0) {
1364 p = strchr(p, '\n');
1375 while (isspace(*hostinfo))
1378 eoh = strchr(hostinfo, '\n');
1380 if (eoh[-1] == '\r')
1383 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1384 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1385 hostbuf[eoh - hostinfo] = 0;
1387 c->http_error = 200;
1389 switch(redir_type) {
1391 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1392 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1393 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1394 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1395 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1396 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1397 hostbuf, filename, info);
1398 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1401 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1402 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\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, "# Autogenerated by ffserver\r\n");
1405 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1406 hostbuf, filename, info);
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1410 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1411 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1412 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1413 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1414 hostbuf, filename, info);
1418 char hostname[256], *p;
1419 /* extract only hostname */
1420 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1421 p = strrchr(hostname, ':');
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1425 /* XXX: incorrect mime type ? */
1426 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1427 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1428 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1429 hostname, ntohs(my_rtsp_addr.sin_port),
1436 int sdp_data_size, len;
1437 struct sockaddr_in my_addr;
1439 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1440 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1441 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1443 len = sizeof(my_addr);
1444 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1446 /* XXX: should use a dynamic buffer */
1447 sdp_data_size = prepare_sdp_description(stream,
1450 if (sdp_data_size > 0) {
1451 memcpy(q, sdp_data, sdp_data_size);
1463 /* prepare output buffer */
1464 c->buffer_ptr = c->buffer;
1466 c->state = HTTPSTATE_SEND_HEADER;
1472 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1476 stream->conns_served++;
1478 /* XXX: add there authenticate and IP match */
1481 /* if post, it means a feed is being sent */
1482 if (!stream->is_feed) {
1483 /* However it might be a status report from WMP! Lets log the data
1484 * as it might come in handy one day
1489 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1490 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1494 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1495 client_id = strtol(p + 18, 0, 10);
1496 p = strchr(p, '\n');
1504 char *eol = strchr(logline, '\n');
1509 if (eol[-1] == '\r')
1511 http_log("%.*s\n", (int) (eol - logline), logline);
1512 c->suppress_log = 1;
1517 http_log("\nGot request:\n%s\n", c->buffer);
1520 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1523 /* Now we have to find the client_id */
1524 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1525 if (wmpc->wmp_client_id == client_id)
1529 if (wmpc && modify_current_stream(wmpc, ratebuf))
1530 wmpc->switch_pending = 1;
1533 snprintf(msg, sizeof(msg), "POST command not handled");
1537 if (http_start_receive_data(c) < 0) {
1538 snprintf(msg, sizeof(msg), "could not open feed");
1542 c->state = HTTPSTATE_RECEIVE_DATA;
1547 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1548 http_log("\nGot request:\n%s\n", c->buffer);
1551 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1554 /* open input stream */
1555 if (open_input_stream(c, info) < 0) {
1556 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1560 /* prepare http header */
1562 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1563 mime_type = c->stream->fmt->mime_type;
1565 mime_type = "application/x-octet-stream";
1566 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1568 /* for asf, we need extra headers */
1569 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1570 /* Need to allocate a client id */
1572 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1574 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);
1576 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1577 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1579 /* prepare output buffer */
1581 c->buffer_ptr = c->buffer;
1583 c->state = HTTPSTATE_SEND_HEADER;
1586 c->http_error = 404;
1588 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1589 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1590 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1591 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1592 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1593 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1594 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1596 /* prepare output buffer */
1597 c->buffer_ptr = c->buffer;
1599 c->state = HTTPSTATE_SEND_HEADER;
1603 c->http_error = 200; /* horrible : we use this value to avoid
1604 going to the send data state */
1605 c->state = HTTPSTATE_SEND_HEADER;
1609 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1611 static const char *suffix = " kMGTP";
1614 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1616 url_fprintf(pb, "%"PRId64"%c", count, *s);
1619 static void compute_status(HTTPContext *c)
1628 if (url_open_dyn_buf(&pb) < 0) {
1629 /* XXX: return an error ? */
1630 c->buffer_ptr = c->buffer;
1631 c->buffer_end = c->buffer;
1635 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1636 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1637 url_fprintf(pb, "Pragma: no-cache\r\n");
1638 url_fprintf(pb, "\r\n");
1640 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1641 if (c->stream->feed_filename[0])
1642 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1643 url_fprintf(pb, "</HEAD>\n<BODY>");
1644 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1646 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1647 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1648 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");
1649 stream = first_stream;
1650 while (stream != NULL) {
1651 char sfilename[1024];
1654 if (stream->feed != stream) {
1655 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1656 eosf = sfilename + strlen(sfilename);
1657 if (eosf - sfilename >= 4) {
1658 if (strcmp(eosf - 4, ".asf") == 0)
1659 strcpy(eosf - 4, ".asx");
1660 else if (strcmp(eosf - 3, ".rm") == 0)
1661 strcpy(eosf - 3, ".ram");
1662 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1663 /* generate a sample RTSP director if
1664 unicast. Generate an SDP redirector if
1666 eosf = strrchr(sfilename, '.');
1668 eosf = sfilename + strlen(sfilename);
1669 if (stream->is_multicast)
1670 strcpy(eosf, ".sdp");
1672 strcpy(eosf, ".rtsp");
1676 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1677 sfilename, stream->filename);
1678 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1679 stream->conns_served);
1680 fmt_bytecount(pb, stream->bytes_served);
1681 switch(stream->stream_type) {
1682 case STREAM_TYPE_LIVE:
1684 int audio_bit_rate = 0;
1685 int video_bit_rate = 0;
1686 const char *audio_codec_name = "";
1687 const char *video_codec_name = "";
1688 const char *audio_codec_name_extra = "";
1689 const char *video_codec_name_extra = "";
1691 for(i=0;i<stream->nb_streams;i++) {
1692 AVStream *st = stream->streams[i];
1693 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1694 switch(st->codec->codec_type) {
1695 case CODEC_TYPE_AUDIO:
1696 audio_bit_rate += st->codec->bit_rate;
1698 if (*audio_codec_name)
1699 audio_codec_name_extra = "...";
1700 audio_codec_name = codec->name;
1703 case CODEC_TYPE_VIDEO:
1704 video_bit_rate += st->codec->bit_rate;
1706 if (*video_codec_name)
1707 video_codec_name_extra = "...";
1708 video_codec_name = codec->name;
1711 case CODEC_TYPE_DATA:
1712 video_bit_rate += st->codec->bit_rate;
1718 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",
1721 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1722 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1724 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1726 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1727 url_fprintf(pb, "\n");
1731 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1735 stream = stream->next;
1737 url_fprintf(pb, "</TABLE>\n");
1739 stream = first_stream;
1740 while (stream != NULL) {
1741 if (stream->feed == stream) {
1742 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1744 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1746 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1751 /* This is somewhat linux specific I guess */
1752 snprintf(ps_cmd, sizeof(ps_cmd),
1753 "ps -o \"%%cpu,cputime\" --no-headers %d",
1756 pid_stat = popen(ps_cmd, "r");
1761 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1763 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1771 url_fprintf(pb, "<p>");
1773 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");
1775 for (i = 0; i < stream->nb_streams; i++) {
1776 AVStream *st = stream->streams[i];
1777 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1778 const char *type = "unknown";
1779 char parameters[64];
1783 switch(st->codec->codec_type) {
1784 case CODEC_TYPE_AUDIO:
1786 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1788 case CODEC_TYPE_VIDEO:
1790 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1791 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1796 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1797 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1799 url_fprintf(pb, "</table>\n");
1802 stream = stream->next;
1808 AVCodecContext *enc;
1812 stream = first_feed;
1813 while (stream != NULL) {
1814 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1815 url_fprintf(pb, "<TABLE>\n");
1816 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1817 for(i=0;i<stream->nb_streams;i++) {
1818 AVStream *st = stream->streams[i];
1819 FeedData *fdata = st->priv_data;
1822 avcodec_string(buf, sizeof(buf), enc);
1823 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1824 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1825 avg /= enc->frame_size;
1826 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1827 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1829 url_fprintf(pb, "</TABLE>\n");
1830 stream = stream->next_feed;
1835 /* connection status */
1836 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1838 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1839 nb_connections, nb_max_connections);
1841 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1842 current_bandwidth, max_bandwidth);
1844 url_fprintf(pb, "<TABLE>\n");
1845 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");
1846 c1 = first_http_ctx;
1848 while (c1 != NULL) {
1854 for (j = 0; j < c1->stream->nb_streams; j++) {
1855 if (!c1->stream->feed)
1856 bitrate += c1->stream->streams[j]->codec->bit_rate;
1857 else if (c1->feed_streams[j] >= 0)
1858 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1863 p = inet_ntoa(c1->from_addr.sin_addr);
1864 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1866 c1->stream ? c1->stream->filename : "",
1867 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1870 http_state[c1->state]);
1871 fmt_bytecount(pb, bitrate);
1872 url_fprintf(pb, "<td align=right>");
1873 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1874 url_fprintf(pb, "<td align=right>");
1875 fmt_bytecount(pb, c1->data_count);
1876 url_fprintf(pb, "\n");
1879 url_fprintf(pb, "</TABLE>\n");
1884 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1885 url_fprintf(pb, "</BODY>\n</HTML>\n");
1887 len = url_close_dyn_buf(pb, &c->pb_buffer);
1888 c->buffer_ptr = c->pb_buffer;
1889 c->buffer_end = c->pb_buffer + len;
1892 /* check if the parser needs to be opened for stream i */
1893 static void open_parser(AVFormatContext *s, int i)
1895 AVStream *st = s->streams[i];
1898 if (!st->codec->codec) {
1899 codec = avcodec_find_decoder(st->codec->codec_id);
1900 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1901 st->codec->parse_only = 1;
1902 if (avcodec_open(st->codec, codec) < 0)
1903 st->codec->parse_only = 0;
1908 static int open_input_stream(HTTPContext *c, const char *info)
1911 char input_filename[1024];
1913 int buf_size, i, ret;
1916 /* find file name */
1917 if (c->stream->feed) {
1918 strcpy(input_filename, c->stream->feed->feed_filename);
1919 buf_size = FFM_PACKET_SIZE;
1920 /* compute position (absolute time) */
1921 if (find_info_tag(buf, sizeof(buf), "date", info))
1923 stream_pos = parse_date(buf, 0);
1924 if (stream_pos == INT64_MIN)
1927 else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1928 int prebuffer = strtol(buf, 0, 10);
1929 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1931 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1933 strcpy(input_filename, c->stream->feed_filename);
1935 /* compute position (relative time) */
1936 if (find_info_tag(buf, sizeof(buf), "date", info))
1938 stream_pos = parse_date(buf, 1);
1939 if (stream_pos == INT64_MIN)
1945 if (input_filename[0] == '\0')
1949 { time_t when = stream_pos / 1000000;
1950 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1955 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1956 buf_size, c->stream->ap_in)) < 0) {
1957 http_log("could not open %s: %d\n", input_filename, ret);
1960 s->flags |= AVFMT_FLAG_GENPTS;
1962 av_find_stream_info(c->fmt_in);
1964 /* open each parser */
1965 for(i=0;i<s->nb_streams;i++)
1968 /* choose stream as clock source (we favorize video stream if
1969 present) for packet sending */
1970 c->pts_stream_index = 0;
1971 for(i=0;i<c->stream->nb_streams;i++) {
1972 if (c->pts_stream_index == 0 &&
1973 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1974 c->pts_stream_index = i;
1979 if (c->fmt_in->iformat->read_seek)
1980 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1982 /* set the start time (needed for maxtime and RTP packet timing) */
1983 c->start_time = cur_time;
1984 c->first_pts = AV_NOPTS_VALUE;
1988 /* return the server clock (in us) */
1989 static int64_t get_server_clock(HTTPContext *c)
1991 /* compute current pts value from system time */
1992 return (cur_time - c->start_time) * 1000;
1995 /* return the estimated time at which the current packet must be sent
1997 static int64_t get_packet_send_clock(HTTPContext *c)
1999 int bytes_left, bytes_sent, frame_bytes;
2001 frame_bytes = c->cur_frame_bytes;
2002 if (frame_bytes <= 0)
2005 bytes_left = c->buffer_end - c->buffer_ptr;
2006 bytes_sent = frame_bytes - bytes_left;
2007 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2012 static int http_prepare_data(HTTPContext *c)
2015 AVFormatContext *ctx;
2017 av_freep(&c->pb_buffer);
2019 case HTTPSTATE_SEND_DATA_HEADER:
2020 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2021 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2022 sizeof(c->fmt_ctx.author));
2023 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2024 sizeof(c->fmt_ctx.comment));
2025 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2026 sizeof(c->fmt_ctx.copyright));
2027 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2028 sizeof(c->fmt_ctx.title));
2030 /* open output stream by using specified codecs */
2031 c->fmt_ctx.oformat = c->stream->fmt;
2032 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2033 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2036 st = av_mallocz(sizeof(AVStream));
2037 st->codec= avcodec_alloc_context();
2038 c->fmt_ctx.streams[i] = st;
2039 /* if file or feed, then just take streams from FFStream struct */
2040 if (!c->stream->feed ||
2041 c->stream->feed == c->stream)
2042 src = c->stream->streams[i];
2044 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2048 st->codec->frame_number = 0; /* XXX: should be done in
2049 AVStream, not in codec */
2051 c->got_key_frame = 0;
2053 /* prepare header and save header data in a stream */
2054 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2055 /* XXX: potential leak */
2058 c->fmt_ctx.pb->is_streamed = 1;
2061 * HACK to avoid mpeg ps muxer to spit many underflow errors
2062 * Default value from FFmpeg
2063 * Try to set it use configuration option
2065 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2066 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2068 av_set_parameters(&c->fmt_ctx, NULL);
2069 if (av_write_header(&c->fmt_ctx) < 0) {
2070 http_log("Error writing output header\n");
2074 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2075 c->buffer_ptr = c->pb_buffer;
2076 c->buffer_end = c->pb_buffer + len;
2078 c->state = HTTPSTATE_SEND_DATA;
2079 c->last_packet_sent = 0;
2081 case HTTPSTATE_SEND_DATA:
2082 /* find a new packet */
2083 /* read a packet from the input stream */
2084 if (c->stream->feed)
2085 ffm_set_write_index(c->fmt_in,
2086 c->stream->feed->feed_write_index,
2087 c->stream->feed->feed_size);
2089 if (c->stream->max_time &&
2090 c->stream->max_time + c->start_time - cur_time < 0)
2091 /* We have timed out */
2092 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2096 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2097 if (c->stream->feed && c->stream->feed->feed_opened) {
2098 /* if coming from feed, it means we reached the end of the
2099 ffm file, so must wait for more data */
2100 c->state = HTTPSTATE_WAIT_FEED;
2101 return 1; /* state changed */
2103 if (c->stream->loop) {
2104 av_close_input_file(c->fmt_in);
2106 if (open_input_stream(c, "") < 0)
2111 /* must send trailer now because eof or error */
2112 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2116 int source_index = pkt.stream_index;
2117 /* update first pts if needed */
2118 if (c->first_pts == AV_NOPTS_VALUE) {
2119 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2120 c->start_time = cur_time;
2122 /* send it to the appropriate stream */
2123 if (c->stream->feed) {
2124 /* if coming from a feed, select the right stream */
2125 if (c->switch_pending) {
2126 c->switch_pending = 0;
2127 for(i=0;i<c->stream->nb_streams;i++) {
2128 if (c->switch_feed_streams[i] == pkt.stream_index)
2129 if (pkt.flags & PKT_FLAG_KEY)
2130 do_switch_stream(c, i);
2131 if (c->switch_feed_streams[i] >= 0)
2132 c->switch_pending = 1;
2135 for(i=0;i<c->stream->nb_streams;i++) {
2136 if (c->feed_streams[i] == pkt.stream_index) {
2137 AVStream *st = c->fmt_in->streams[source_index];
2138 pkt.stream_index = i;
2139 if (pkt.flags & PKT_FLAG_KEY &&
2140 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2141 c->stream->nb_streams == 1))
2142 c->got_key_frame = 1;
2143 if (!c->stream->send_on_key || c->got_key_frame)
2148 AVCodecContext *codec;
2151 /* specific handling for RTP: we use several
2152 output stream (one for each RTP
2153 connection). XXX: need more abstract handling */
2154 if (c->is_packetized) {
2156 /* compute send time and duration */
2157 st = c->fmt_in->streams[pkt.stream_index];
2158 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2159 if (st->start_time != AV_NOPTS_VALUE)
2160 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2161 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2163 printf("index=%d pts=%0.3f duration=%0.6f\n",
2165 (double)c->cur_pts /
2167 (double)c->cur_frame_duration /
2170 /* find RTP context */
2171 c->packet_stream_index = pkt.stream_index;
2172 ctx = c->rtp_ctx[c->packet_stream_index];
2174 av_free_packet(&pkt);
2177 codec = ctx->streams[0]->codec;
2178 /* only one stream per RTP connection */
2179 pkt.stream_index = 0;
2183 codec = ctx->streams[pkt.stream_index]->codec;
2186 if (c->is_packetized) {
2187 int max_packet_size;
2188 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2189 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2191 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2192 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2194 ret = url_open_dyn_buf(&ctx->pb);
2197 /* XXX: potential leak */
2200 c->fmt_ctx.pb->is_streamed = 1;
2201 if (pkt.dts != AV_NOPTS_VALUE)
2202 pkt.dts = av_rescale_q(pkt.dts,
2203 c->fmt_in->streams[source_index]->time_base,
2204 ctx->streams[pkt.stream_index]->time_base);
2205 if (pkt.pts != AV_NOPTS_VALUE)
2206 pkt.pts = av_rescale_q(pkt.pts,
2207 c->fmt_in->streams[source_index]->time_base,
2208 ctx->streams[pkt.stream_index]->time_base);
2209 pkt.duration = av_rescale_q(pkt.duration,
2210 c->fmt_in->streams[source_index]->time_base,
2211 ctx->streams[pkt.stream_index]->time_base);
2212 if (av_write_frame(ctx, &pkt) < 0) {
2213 http_log("Error writing frame to output\n");
2214 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2218 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2219 c->cur_frame_bytes = len;
2220 c->buffer_ptr = c->pb_buffer;
2221 c->buffer_end = c->pb_buffer + len;
2223 codec->frame_number++;
2225 av_free_packet(&pkt);
2229 av_free_packet(&pkt);
2234 case HTTPSTATE_SEND_DATA_TRAILER:
2235 /* last packet test ? */
2236 if (c->last_packet_sent || c->is_packetized)
2239 /* prepare header */
2240 if (url_open_dyn_buf(&ctx->pb) < 0) {
2241 /* XXX: potential leak */
2244 c->fmt_ctx.pb->is_streamed = 1;
2245 av_write_trailer(ctx);
2246 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2247 c->buffer_ptr = c->pb_buffer;
2248 c->buffer_end = c->pb_buffer + len;
2250 c->last_packet_sent = 1;
2256 /* should convert the format at the same time */
2257 /* send data starting at c->buffer_ptr to the output connection
2258 (either UDP or TCP connection) */
2259 static int http_send_data(HTTPContext *c)
2264 if (c->buffer_ptr >= c->buffer_end) {
2265 ret = http_prepare_data(c);
2269 /* state change requested */
2272 if (c->is_packetized) {
2273 /* RTP data output */
2274 len = c->buffer_end - c->buffer_ptr;
2276 /* fail safe - should never happen */
2278 c->buffer_ptr = c->buffer_end;
2281 len = (c->buffer_ptr[0] << 24) |
2282 (c->buffer_ptr[1] << 16) |
2283 (c->buffer_ptr[2] << 8) |
2285 if (len > (c->buffer_end - c->buffer_ptr))
2287 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2288 /* nothing to send yet: we can wait */
2292 c->data_count += len;
2293 update_datarate(&c->datarate, c->data_count);
2295 c->stream->bytes_served += len;
2297 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2298 /* RTP packets are sent inside the RTSP TCP connection */
2300 int interleaved_index, size;
2302 HTTPContext *rtsp_c;
2305 /* if no RTSP connection left, error */
2308 /* if already sending something, then wait. */
2309 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2311 if (url_open_dyn_buf(&pb) < 0)
2313 interleaved_index = c->packet_stream_index * 2;
2314 /* RTCP packets are sent at odd indexes */
2315 if (c->buffer_ptr[1] == 200)
2316 interleaved_index++;
2317 /* write RTSP TCP header */
2319 header[1] = interleaved_index;
2320 header[2] = len >> 8;
2322 put_buffer(pb, header, 4);
2323 /* write RTP packet data */
2325 put_buffer(pb, c->buffer_ptr, len);
2326 size = url_close_dyn_buf(pb, &c->packet_buffer);
2327 /* prepare asynchronous TCP sending */
2328 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2329 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2330 c->buffer_ptr += len;
2332 /* send everything we can NOW */
2333 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2334 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2336 rtsp_c->packet_buffer_ptr += len;
2337 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2338 /* if we could not send all the data, we will
2339 send it later, so a new state is needed to
2340 "lock" the RTSP TCP connection */
2341 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2344 /* all data has been sent */
2345 av_freep(&c->packet_buffer);
2347 /* send RTP packet directly in UDP */
2349 url_write(c->rtp_handles[c->packet_stream_index],
2350 c->buffer_ptr, len);
2351 c->buffer_ptr += len;
2352 /* here we continue as we can send several packets per 10 ms slot */
2355 /* TCP data output */
2356 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2358 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2359 ff_neterrno() != FF_NETERROR(EINTR))
2360 /* error : close connection */
2365 c->buffer_ptr += len;
2367 c->data_count += len;
2368 update_datarate(&c->datarate, c->data_count);
2370 c->stream->bytes_served += len;
2378 static int http_start_receive_data(HTTPContext *c)
2382 if (c->stream->feed_opened)
2385 /* Don't permit writing to this one */
2386 if (c->stream->readonly)
2390 fd = open(c->stream->feed_filename, O_RDWR);
2392 http_log("Error opening feeder file: %s\n", strerror(errno));
2397 c->stream->feed_write_index = ffm_read_write_index(fd);
2398 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2399 lseek(fd, 0, SEEK_SET);
2401 /* init buffer input */
2402 c->buffer_ptr = c->buffer;
2403 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2404 c->stream->feed_opened = 1;
2408 static int http_receive_data(HTTPContext *c)
2412 if (c->buffer_end > c->buffer_ptr) {
2415 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2417 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2418 ff_neterrno() != FF_NETERROR(EINTR))
2419 /* error : close connection */
2421 } else if (len == 0)
2422 /* end of connection : close it */
2425 c->buffer_ptr += len;
2426 c->data_count += len;
2427 update_datarate(&c->datarate, c->data_count);
2431 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2432 if (c->buffer[0] != 'f' ||
2433 c->buffer[1] != 'm') {
2434 http_log("Feed stream has become desynchronized -- disconnecting\n");
2439 if (c->buffer_ptr >= c->buffer_end) {
2440 FFStream *feed = c->stream;
2441 /* a packet has been received : write it in the store, except
2443 if (c->data_count > FFM_PACKET_SIZE) {
2445 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2446 /* XXX: use llseek or url_seek */
2447 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2448 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2449 http_log("Error writing to feed file: %s\n", strerror(errno));
2453 feed->feed_write_index += FFM_PACKET_SIZE;
2454 /* update file size */
2455 if (feed->feed_write_index > c->stream->feed_size)
2456 feed->feed_size = feed->feed_write_index;
2458 /* handle wrap around if max file size reached */
2459 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2460 feed->feed_write_index = FFM_PACKET_SIZE;
2463 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2465 /* wake up any waiting connections */
2466 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2467 if (c1->state == HTTPSTATE_WAIT_FEED &&
2468 c1->stream->feed == c->stream->feed)
2469 c1->state = HTTPSTATE_SEND_DATA;
2472 /* We have a header in our hands that contains useful data */
2474 AVInputFormat *fmt_in;
2477 memset(&s, 0, sizeof(s));
2479 url_open_buf(&s.pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2480 s.pb->is_streamed = 1;
2482 /* use feed output format name to find corresponding input format */
2483 fmt_in = av_find_input_format(feed->fmt->name);
2487 if (fmt_in->priv_data_size > 0) {
2488 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2494 if (fmt_in->read_header(&s, 0) < 0) {
2495 av_freep(&s.priv_data);
2499 /* Now we have the actual streams */
2500 if (s.nb_streams != feed->nb_streams) {
2501 av_freep(&s.priv_data);
2504 for (i = 0; i < s.nb_streams; i++)
2505 memcpy(feed->streams[i]->codec,
2506 s.streams[i]->codec, sizeof(AVCodecContext));
2507 av_freep(&s.priv_data);
2509 c->buffer_ptr = c->buffer;
2514 c->stream->feed_opened = 0;
2516 /* wake up any waiting connections to stop waiting for feed */
2517 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2518 if (c1->state == HTTPSTATE_WAIT_FEED &&
2519 c1->stream->feed == c->stream->feed)
2520 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2525 /********************************************************************/
2528 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2535 switch(error_number) {
2536 case RTSP_STATUS_OK:
2539 case RTSP_STATUS_METHOD:
2540 str = "Method Not Allowed";
2542 case RTSP_STATUS_BANDWIDTH:
2543 str = "Not Enough Bandwidth";
2545 case RTSP_STATUS_SESSION:
2546 str = "Session Not Found";
2548 case RTSP_STATUS_STATE:
2549 str = "Method Not Valid in This State";
2551 case RTSP_STATUS_AGGREGATE:
2552 str = "Aggregate operation not allowed";
2554 case RTSP_STATUS_ONLY_AGGREGATE:
2555 str = "Only aggregate operation allowed";
2557 case RTSP_STATUS_TRANSPORT:
2558 str = "Unsupported transport";
2560 case RTSP_STATUS_INTERNAL:
2561 str = "Internal Server Error";
2563 case RTSP_STATUS_SERVICE:
2564 str = "Service Unavailable";
2566 case RTSP_STATUS_VERSION:
2567 str = "RTSP Version not supported";
2570 str = "Unknown Error";
2574 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2575 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2577 /* output GMT time */
2581 p = buf2 + strlen(p) - 1;
2584 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2587 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2589 rtsp_reply_header(c, error_number);
2590 url_fprintf(c->pb, "\r\n");
2593 static int rtsp_parse_request(HTTPContext *c)
2595 const char *p, *p1, *p2;
2601 RTSPHeader header1, *header = &header1;
2603 c->buffer_ptr[0] = '\0';
2606 get_word(cmd, sizeof(cmd), &p);
2607 get_word(url, sizeof(url), &p);
2608 get_word(protocol, sizeof(protocol), &p);
2610 av_strlcpy(c->method, cmd, sizeof(c->method));
2611 av_strlcpy(c->url, url, sizeof(c->url));
2612 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2614 if (url_open_dyn_buf(&c->pb) < 0) {
2615 /* XXX: cannot do more */
2616 c->pb = NULL; /* safety */
2620 /* check version name */
2621 if (strcmp(protocol, "RTSP/1.0") != 0) {
2622 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2626 /* parse each header line */
2627 memset(header, 0, sizeof(RTSPHeader));
2628 /* skip to next line */
2629 while (*p != '\n' && *p != '\0')
2633 while (*p != '\0') {
2634 p1 = strchr(p, '\n');
2638 if (p2 > p && p2[-1] == '\r')
2640 /* skip empty line */
2644 if (len > sizeof(line) - 1)
2645 len = sizeof(line) - 1;
2646 memcpy(line, p, len);
2648 rtsp_parse_line(header, line);
2652 /* handle sequence number */
2653 c->seq = header->seq;
2655 if (!strcmp(cmd, "DESCRIBE"))
2656 rtsp_cmd_describe(c, url);
2657 else if (!strcmp(cmd, "OPTIONS"))
2658 rtsp_cmd_options(c, url);
2659 else if (!strcmp(cmd, "SETUP"))
2660 rtsp_cmd_setup(c, url, header);
2661 else if (!strcmp(cmd, "PLAY"))
2662 rtsp_cmd_play(c, url, header);
2663 else if (!strcmp(cmd, "PAUSE"))
2664 rtsp_cmd_pause(c, url, header);
2665 else if (!strcmp(cmd, "TEARDOWN"))
2666 rtsp_cmd_teardown(c, url, header);
2668 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2671 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2672 c->pb = NULL; /* safety */
2674 /* XXX: cannot do more */
2677 c->buffer_ptr = c->pb_buffer;
2678 c->buffer_end = c->pb_buffer + len;
2679 c->state = RTSPSTATE_SEND_REPLY;
2683 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2684 struct in_addr my_ip)
2686 AVFormatContext *avc;
2687 AVStream avs[MAX_STREAMS];
2690 avc = av_alloc_format_context();
2694 if (stream->title[0] != 0) {
2695 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2697 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2699 avc->nb_streams = stream->nb_streams;
2700 if (stream->is_multicast) {
2701 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2702 inet_ntoa(stream->multicast_ip),
2703 stream->multicast_port, stream->multicast_ttl);
2706 for(i = 0; i < stream->nb_streams; i++) {
2707 avc->streams[i] = &avs[i];
2708 avc->streams[i]->codec = stream->streams[i]->codec;
2710 *pbuffer = av_mallocz(2048);
2711 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2714 return strlen(*pbuffer);
2717 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2719 // rtsp_reply_header(c, RTSP_STATUS_OK);
2720 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2721 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2722 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2723 url_fprintf(c->pb, "\r\n");
2726 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2732 int content_length, len;
2733 struct sockaddr_in my_addr;
2735 /* find which url is asked */
2736 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2741 for(stream = first_stream; stream != NULL; stream = stream->next) {
2742 if (!stream->is_feed &&
2743 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2744 !strcmp(path, stream->filename)) {
2748 /* no stream found */
2749 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2753 /* prepare the media description in sdp format */
2755 /* get the host IP */
2756 len = sizeof(my_addr);
2757 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2758 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2759 if (content_length < 0) {
2760 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2763 rtsp_reply_header(c, RTSP_STATUS_OK);
2764 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2765 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2766 url_fprintf(c->pb, "\r\n");
2767 put_buffer(c->pb, content, content_length);
2770 static HTTPContext *find_rtp_session(const char *session_id)
2774 if (session_id[0] == '\0')
2777 for(c = first_http_ctx; c != NULL; c = c->next) {
2778 if (!strcmp(c->session_id, session_id))
2784 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2786 RTSPTransportField *th;
2789 for(i=0;i<h->nb_transports;i++) {
2790 th = &h->transports[i];
2791 if (th->protocol == protocol)
2797 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2801 int stream_index, port;
2806 RTSPTransportField *th;
2807 struct sockaddr_in dest_addr;
2808 RTSPActionServerSetup setup;
2810 /* find which url is asked */
2811 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2816 /* now check each stream */
2817 for(stream = first_stream; stream != NULL; stream = stream->next) {
2818 if (!stream->is_feed &&
2819 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2820 /* accept aggregate filenames only if single stream */
2821 if (!strcmp(path, stream->filename)) {
2822 if (stream->nb_streams != 1) {
2823 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2830 for(stream_index = 0; stream_index < stream->nb_streams;
2832 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2833 stream->filename, stream_index);
2834 if (!strcmp(path, buf))
2839 /* no stream found */
2840 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2844 /* generate session id if needed */
2845 if (h->session_id[0] == '\0')
2846 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2847 av_random(&random_state), av_random(&random_state));
2849 /* find rtp session, and create it if none found */
2850 rtp_c = find_rtp_session(h->session_id);
2852 /* always prefer UDP */
2853 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2855 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2857 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2862 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2865 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2869 /* open input stream */
2870 if (open_input_stream(rtp_c, "") < 0) {
2871 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2876 /* test if stream is OK (test needed because several SETUP needs
2877 to be done for a given file) */
2878 if (rtp_c->stream != stream) {
2879 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2883 /* test if stream is already set up */
2884 if (rtp_c->rtp_ctx[stream_index]) {
2885 rtsp_reply_error(c, RTSP_STATUS_STATE);
2889 /* check transport */
2890 th = find_transport(h, rtp_c->rtp_protocol);
2891 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2892 th->client_port_min <= 0)) {
2893 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2897 /* setup default options */
2898 setup.transport_option[0] = '\0';
2899 dest_addr = rtp_c->from_addr;
2900 dest_addr.sin_port = htons(th->client_port_min);
2903 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2904 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2908 /* now everything is OK, so we can send the connection parameters */
2909 rtsp_reply_header(c, RTSP_STATUS_OK);
2911 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2913 switch(rtp_c->rtp_protocol) {
2914 case RTSP_PROTOCOL_RTP_UDP:
2915 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2916 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2917 "client_port=%d-%d;server_port=%d-%d",
2918 th->client_port_min, th->client_port_min + 1,
2921 case RTSP_PROTOCOL_RTP_TCP:
2922 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2923 stream_index * 2, stream_index * 2 + 1);
2928 if (setup.transport_option[0] != '\0')
2929 url_fprintf(c->pb, ";%s", setup.transport_option);
2930 url_fprintf(c->pb, "\r\n");
2933 url_fprintf(c->pb, "\r\n");
2937 /* find an rtp connection by using the session ID. Check consistency
2939 static HTTPContext *find_rtp_session_with_url(const char *url,
2940 const char *session_id)
2948 rtp_c = find_rtp_session(session_id);
2952 /* find which url is asked */
2953 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2957 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2958 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2959 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2960 rtp_c->stream->filename, s);
2961 if(!strncmp(path, buf, sizeof(buf))) {
2962 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2969 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2973 rtp_c = find_rtp_session_with_url(url, h->session_id);
2975 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2979 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2980 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2981 rtp_c->state != HTTPSTATE_READY) {
2982 rtsp_reply_error(c, RTSP_STATUS_STATE);
2987 /* XXX: seek in stream */
2988 if (h->range_start != AV_NOPTS_VALUE) {
2989 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2990 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2994 rtp_c->state = HTTPSTATE_SEND_DATA;
2996 /* now everything is OK, so we can send the connection parameters */
2997 rtsp_reply_header(c, RTSP_STATUS_OK);
2999 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3000 url_fprintf(c->pb, "\r\n");
3003 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3007 rtp_c = find_rtp_session_with_url(url, h->session_id);
3009 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3013 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3014 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3015 rtsp_reply_error(c, RTSP_STATUS_STATE);
3019 rtp_c->state = HTTPSTATE_READY;
3020 rtp_c->first_pts = AV_NOPTS_VALUE;
3021 /* now everything is OK, so we can send the connection parameters */
3022 rtsp_reply_header(c, RTSP_STATUS_OK);
3024 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3025 url_fprintf(c->pb, "\r\n");
3028 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3031 char session_id[32];
3033 rtp_c = find_rtp_session_with_url(url, h->session_id);
3035 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3039 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3041 /* abort the session */
3042 close_connection(rtp_c);
3044 /* now everything is OK, so we can send the connection parameters */
3045 rtsp_reply_header(c, RTSP_STATUS_OK);
3047 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3048 url_fprintf(c->pb, "\r\n");
3052 /********************************************************************/
3055 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3056 FFStream *stream, const char *session_id,
3057 enum RTSPProtocol rtp_protocol)
3059 HTTPContext *c = NULL;
3060 const char *proto_str;
3062 /* XXX: should output a warning page when coming
3063 close to the connection limit */
3064 if (nb_connections >= nb_max_connections)
3067 /* add a new connection */
3068 c = av_mallocz(sizeof(HTTPContext));
3073 c->poll_entry = NULL;
3074 c->from_addr = *from_addr;
3075 c->buffer_size = IOBUFFER_INIT_SIZE;
3076 c->buffer = av_malloc(c->buffer_size);
3081 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3082 c->state = HTTPSTATE_READY;
3083 c->is_packetized = 1;
3084 c->rtp_protocol = rtp_protocol;
3086 /* protocol is shown in statistics */
3087 switch(c->rtp_protocol) {
3088 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3089 proto_str = "MCAST";
3091 case RTSP_PROTOCOL_RTP_UDP:
3094 case RTSP_PROTOCOL_RTP_TCP:
3101 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3102 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3104 current_bandwidth += stream->bandwidth;
3106 c->next = first_http_ctx;
3118 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3119 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3121 static int rtp_new_av_stream(HTTPContext *c,
3122 int stream_index, struct sockaddr_in *dest_addr,
3123 HTTPContext *rtsp_c)
3125 AVFormatContext *ctx;
3128 URLContext *h = NULL;
3131 int max_packet_size;
3133 /* now we can open the relevant output stream */
3134 ctx = av_alloc_format_context();
3137 ctx->oformat = guess_format("rtp", NULL, NULL);
3139 st = av_mallocz(sizeof(AVStream));
3142 st->codec= avcodec_alloc_context();
3143 ctx->nb_streams = 1;
3144 ctx->streams[0] = st;
3146 if (!c->stream->feed ||
3147 c->stream->feed == c->stream)
3148 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3151 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3153 st->priv_data = NULL;
3155 /* build destination RTP address */
3156 ipaddr = inet_ntoa(dest_addr->sin_addr);
3158 switch(c->rtp_protocol) {
3159 case RTSP_PROTOCOL_RTP_UDP:
3160 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3163 /* XXX: also pass as parameter to function ? */
3164 if (c->stream->is_multicast) {
3166 ttl = c->stream->multicast_ttl;
3169 snprintf(ctx->filename, sizeof(ctx->filename),
3170 "rtp://%s:%d?multicast=1&ttl=%d",
3171 ipaddr, ntohs(dest_addr->sin_port), ttl);
3173 snprintf(ctx->filename, sizeof(ctx->filename),
3174 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3177 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3179 c->rtp_handles[stream_index] = h;
3180 max_packet_size = url_get_max_packet_size(h);
3182 case RTSP_PROTOCOL_RTP_TCP:
3185 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3191 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3192 ipaddr, ntohs(dest_addr->sin_port),
3194 c->stream->filename, stream_index, c->protocol);
3196 /* normally, no packets should be output here, but the packet size may be checked */
3197 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3198 /* XXX: close stream */
3201 av_set_parameters(ctx, NULL);
3202 if (av_write_header(ctx) < 0) {
3209 url_close_dyn_buf(ctx->pb, &dummy_buf);
3212 c->rtp_ctx[stream_index] = ctx;
3216 /********************************************************************/
3217 /* ffserver initialization */
3219 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3223 fst = av_mallocz(sizeof(AVStream));
3226 fst->codec= avcodec_alloc_context();
3227 fst->priv_data = av_mallocz(sizeof(FeedData));
3228 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3229 fst->index = stream->nb_streams;
3230 av_set_pts_info(fst, 33, 1, 90000);
3231 stream->streams[stream->nb_streams++] = fst;
3235 /* return the stream number in the feed */
3236 static int add_av_stream(FFStream *feed, AVStream *st)
3239 AVCodecContext *av, *av1;
3243 for(i=0;i<feed->nb_streams;i++) {
3244 st = feed->streams[i];
3246 if (av1->codec_id == av->codec_id &&
3247 av1->codec_type == av->codec_type &&
3248 av1->bit_rate == av->bit_rate) {
3250 switch(av->codec_type) {
3251 case CODEC_TYPE_AUDIO:
3252 if (av1->channels == av->channels &&
3253 av1->sample_rate == av->sample_rate)
3256 case CODEC_TYPE_VIDEO:
3257 if (av1->width == av->width &&
3258 av1->height == av->height &&
3259 av1->time_base.den == av->time_base.den &&
3260 av1->time_base.num == av->time_base.num &&
3261 av1->gop_size == av->gop_size)
3270 fst = add_av_stream1(feed, av);
3273 return feed->nb_streams - 1;
3278 static void remove_stream(FFStream *stream)
3282 while (*ps != NULL) {
3290 /* specific mpeg4 handling : we extract the raw parameters */
3291 static void extract_mpeg4_header(AVFormatContext *infile)
3293 int mpeg4_count, i, size;
3299 for(i=0;i<infile->nb_streams;i++) {
3300 st = infile->streams[i];
3301 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3302 st->codec->extradata_size == 0) {
3309 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3310 while (mpeg4_count > 0) {
3311 if (av_read_packet(infile, &pkt) < 0)
3313 st = infile->streams[pkt.stream_index];
3314 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3315 st->codec->extradata_size == 0) {
3316 av_freep(&st->codec->extradata);
3317 /* fill extradata with the header */
3318 /* XXX: we make hard suppositions here ! */
3320 while (p < pkt.data + pkt.size - 4) {
3321 /* stop when vop header is found */
3322 if (p[0] == 0x00 && p[1] == 0x00 &&
3323 p[2] == 0x01 && p[3] == 0xb6) {
3324 size = p - pkt.data;
3325 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3326 st->codec->extradata = av_malloc(size);
3327 st->codec->extradata_size = size;
3328 memcpy(st->codec->extradata, pkt.data, size);
3335 av_free_packet(&pkt);
3339 /* compute the needed AVStream for each file */
3340 static void build_file_streams(void)
3342 FFStream *stream, *stream_next;
3343 AVFormatContext *infile;
3346 /* gather all streams */
3347 for(stream = first_stream; stream != NULL; stream = stream_next) {
3348 stream_next = stream->next;
3349 if (stream->stream_type == STREAM_TYPE_LIVE &&
3351 /* the stream comes from a file */
3352 /* try to open the file */
3354 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3355 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3356 /* specific case : if transport stream output to RTP,
3357 we use a raw transport stream reader */
3358 stream->ap_in->mpeg2ts_raw = 1;
3359 stream->ap_in->mpeg2ts_compute_pcr = 1;
3362 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3363 stream->ifmt, 0, stream->ap_in)) < 0) {
3364 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3365 /* remove stream (no need to spend more time on it) */
3367 remove_stream(stream);
3369 /* find all the AVStreams inside and reference them in
3371 if (av_find_stream_info(infile) < 0) {
3372 http_log("Could not find codec parameters from '%s'\n",
3373 stream->feed_filename);
3374 av_close_input_file(infile);
3377 extract_mpeg4_header(infile);
3379 for(i=0;i<infile->nb_streams;i++)
3380 add_av_stream1(stream, infile->streams[i]->codec);
3382 av_close_input_file(infile);
3388 /* compute the needed AVStream for each feed */
3389 static void build_feed_streams(void)
3391 FFStream *stream, *feed;
3394 /* gather all streams */
3395 for(stream = first_stream; stream != NULL; stream = stream->next) {
3396 feed = stream->feed;
3398 if (!stream->is_feed) {
3399 /* we handle a stream coming from a feed */
3400 for(i=0;i<stream->nb_streams;i++)
3401 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3406 /* gather all streams */
3407 for(stream = first_stream; stream != NULL; stream = stream->next) {
3408 feed = stream->feed;
3410 if (stream->is_feed) {
3411 for(i=0;i<stream->nb_streams;i++)
3412 stream->feed_streams[i] = i;
3417 /* create feed files if needed */
3418 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3421 if (url_exist(feed->feed_filename)) {
3422 /* See if it matches */
3426 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3427 /* Now see if it matches */
3428 if (s->nb_streams == feed->nb_streams) {
3430 for(i=0;i<s->nb_streams;i++) {
3432 sf = feed->streams[i];
3435 if (sf->index != ss->index ||
3437 printf("Index & Id do not match for stream %d (%s)\n",
3438 i, feed->feed_filename);
3441 AVCodecContext *ccf, *ccs;
3445 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3447 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3448 printf("Codecs do not match for stream %d\n", i);
3450 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3451 printf("Codec bitrates do not match for stream %d\n", i);
3453 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3454 if (CHECK_CODEC(time_base.den) ||
3455 CHECK_CODEC(time_base.num) ||
3456 CHECK_CODEC(width) ||
3457 CHECK_CODEC(height)) {
3458 printf("Codec width, height and framerate do not match for stream %d\n", i);
3461 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3462 if (CHECK_CODEC(sample_rate) ||
3463 CHECK_CODEC(channels) ||
3464 CHECK_CODEC(frame_size)) {
3465 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3469 printf("Unknown codec type\n");
3477 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3478 feed->feed_filename, s->nb_streams, feed->nb_streams);
3480 av_close_input_file(s);
3482 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3483 feed->feed_filename);
3486 if (feed->readonly) {
3487 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3488 feed->feed_filename);
3491 unlink(feed->feed_filename);
3494 if (!url_exist(feed->feed_filename)) {
3495 AVFormatContext s1, *s = &s1;
3497 if (feed->readonly) {
3498 printf("Unable to create feed file '%s' as it is marked readonly\n",
3499 feed->feed_filename);
3503 /* only write the header of the ffm file */
3504 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3505 http_log("Could not open output feed file '%s'\n",
3506 feed->feed_filename);
3509 s->oformat = feed->fmt;
3510 s->nb_streams = feed->nb_streams;
3511 for(i=0;i<s->nb_streams;i++) {
3513 st = feed->streams[i];
3516 av_set_parameters(s, NULL);
3517 if (av_write_header(s) < 0) {
3518 http_log("Container doesn't supports the required parameters\n");
3521 /* XXX: need better api */
3522 av_freep(&s->priv_data);
3525 /* get feed size and write index */
3526 fd = open(feed->feed_filename, O_RDONLY);
3528 http_log("Could not open output feed file '%s'\n",
3529 feed->feed_filename);
3533 feed->feed_write_index = ffm_read_write_index(fd);
3534 feed->feed_size = lseek(fd, 0, SEEK_END);
3535 /* ensure that we do not wrap before the end of file */
3536 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3537 feed->feed_max_size = feed->feed_size;
3543 /* compute the bandwidth used by each stream */
3544 static void compute_bandwidth(void)
3550 for(stream = first_stream; stream != NULL; stream = stream->next) {
3552 for(i=0;i<stream->nb_streams;i++) {
3553 AVStream *st = stream->streams[i];
3554 switch(st->codec->codec_type) {
3555 case CODEC_TYPE_AUDIO:
3556 case CODEC_TYPE_VIDEO:
3557 bandwidth += st->codec->bit_rate;
3563 stream->bandwidth = (bandwidth + 999) / 1000;
3567 static void get_arg(char *buf, int buf_size, const char **pp)
3574 while (isspace(*p)) p++;
3577 if (*p == '\"' || *p == '\'')
3589 if ((q - buf) < buf_size - 1)
3594 if (quote && *p == quote)
3599 /* add a codec and set the default parameters */
3600 static void add_codec(FFStream *stream, AVCodecContext *av)
3604 /* compute default parameters */
3605 switch(av->codec_type) {
3606 case CODEC_TYPE_AUDIO:
3607 if (av->bit_rate == 0)
3608 av->bit_rate = 64000;
3609 if (av->sample_rate == 0)
3610 av->sample_rate = 22050;
3611 if (av->channels == 0)
3614 case CODEC_TYPE_VIDEO:
3615 if (av->bit_rate == 0)
3616 av->bit_rate = 64000;
3617 if (av->time_base.num == 0){
3618 av->time_base.den = 5;
3619 av->time_base.num = 1;
3621 if (av->width == 0 || av->height == 0) {
3625 /* Bitrate tolerance is less for streaming */
3626 if (av->bit_rate_tolerance == 0)
3627 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3628 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3633 if (av->max_qdiff == 0)
3635 av->qcompress = 0.5;
3638 if (!av->nsse_weight)
3639 av->nsse_weight = 8;
3641 av->frame_skip_cmp = FF_CMP_DCTMAX;
3642 av->me_method = ME_EPZS;
3643 av->rc_buffer_aggressivity = 1.0;
3646 av->rc_eq = "tex^qComp";
3647 if (!av->i_quant_factor)
3648 av->i_quant_factor = -0.8;
3649 if (!av->b_quant_factor)
3650 av->b_quant_factor = 1.25;
3651 if (!av->b_quant_offset)
3652 av->b_quant_offset = 1.25;
3653 if (!av->rc_max_rate)
3654 av->rc_max_rate = av->bit_rate * 2;
3656 if (av->rc_max_rate && !av->rc_buffer_size) {
3657 av->rc_buffer_size = av->rc_max_rate;
3666 st = av_mallocz(sizeof(AVStream));
3669 st->codec = avcodec_alloc_context();
3670 stream->streams[stream->nb_streams++] = st;
3671 memcpy(st->codec, av, sizeof(AVCodecContext));
3674 static int opt_audio_codec(const char *arg)
3676 AVCodec *p= avcodec_find_encoder_by_name(arg);
3678 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3679 return CODEC_ID_NONE;
3684 static int opt_video_codec(const char *arg)
3686 AVCodec *p= avcodec_find_encoder_by_name(arg);
3688 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3689 return CODEC_ID_NONE;
3694 /* simplistic plugin support */
3697 static void load_module(const char *filename)
3700 void (*init_func)(void);
3701 dll = dlopen(filename, RTLD_NOW);
3703 fprintf(stderr, "Could not load module '%s' - %s\n",
3704 filename, dlerror());
3708 init_func = dlsym(dll, "ffserver_module_init");
3711 "%s: init function 'ffserver_module_init()' not found\n",
3720 static int opt_default(const char *opt, const char *arg,
3721 AVCodecContext *avctx, int type)
3723 const AVOption *o = NULL;
3724 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3726 o = av_set_string(avctx, opt, arg);
3732 static int parse_ffconfig(const char *filename)
3739 int val, errors, line_num;
3740 FFStream **last_stream, *stream, *redirect;
3741 FFStream **last_feed, *feed;
3742 AVCodecContext audio_enc, video_enc;
3743 int audio_id, video_id;
3745 f = fopen(filename, "r");
3753 first_stream = NULL;
3754 last_stream = &first_stream;
3756 last_feed = &first_feed;
3760 audio_id = CODEC_ID_NONE;
3761 video_id = CODEC_ID_NONE;
3763 if (fgets(line, sizeof(line), f) == NULL)
3769 if (*p == '\0' || *p == '#')
3772 get_arg(cmd, sizeof(cmd), &p);
3774 if (!strcasecmp(cmd, "Port")) {
3775 get_arg(arg, sizeof(arg), &p);
3777 if (val < 1 || val > 65536) {
3778 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3779 filename, line_num, arg);
3782 my_http_addr.sin_port = htons(val);
3783 } else if (!strcasecmp(cmd, "BindAddress")) {
3784 get_arg(arg, sizeof(arg), &p);
3785 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3786 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3787 filename, line_num, arg);
3790 } else if (!strcasecmp(cmd, "NoDaemon")) {
3791 ffserver_daemon = 0;
3792 } else if (!strcasecmp(cmd, "RTSPPort")) {
3793 get_arg(arg, sizeof(arg), &p);
3795 if (val < 1 || val > 65536) {
3796 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3797 filename, line_num, arg);
3800 my_rtsp_addr.sin_port = htons(atoi(arg));
3801 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3802 get_arg(arg, sizeof(arg), &p);
3803 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3804 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3805 filename, line_num, arg);
3808 } else if (!strcasecmp(cmd, "MaxClients")) {
3809 get_arg(arg, sizeof(arg), &p);
3811 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3812 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3813 filename, line_num, arg);
3816 nb_max_connections = val;
3818 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3820 get_arg(arg, sizeof(arg), &p);
3822 if (llval < 10 || llval > 10000000) {
3823 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3824 filename, line_num, arg);
3827 max_bandwidth = llval;
3828 } else if (!strcasecmp(cmd, "CustomLog")) {
3829 if (!ffserver_debug)
3830 get_arg(logfilename, sizeof(logfilename), &p);
3831 } else if (!strcasecmp(cmd, "<Feed")) {
3832 /*********************************************/
3833 /* Feed related options */
3835 if (stream || feed) {
3836 fprintf(stderr, "%s:%d: Already in a tag\n",
3837 filename, line_num);
3839 feed = av_mallocz(sizeof(FFStream));
3840 /* add in stream list */
3841 *last_stream = feed;
3842 last_stream = &feed->next;
3843 /* add in feed list */
3845 last_feed = &feed->next_feed;
3847 get_arg(feed->filename, sizeof(feed->filename), &p);
3848 q = strrchr(feed->filename, '>');
3851 feed->fmt = guess_format("ffm", NULL, NULL);
3852 /* defaut feed file */
3853 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3854 "/tmp/%s.ffm", feed->filename);
3855 feed->feed_max_size = 5 * 1024 * 1024;
3857 feed->feed = feed; /* self feeding :-) */
3859 } else if (!strcasecmp(cmd, "Launch")) {
3863 feed->child_argv = av_mallocz(64 * sizeof(char *));
3865 for (i = 0; i < 62; i++) {
3866 get_arg(arg, sizeof(arg), &p);
3870 feed->child_argv[i] = av_strdup(arg);
3873 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3875 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3877 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3878 inet_ntoa(my_http_addr.sin_addr),
3879 ntohs(my_http_addr.sin_port), feed->filename);
3884 fprintf(stdout, "Launch commandline: ");
3885 for (j = 0; j <= i; j++)
3886 fprintf(stdout, "%s ", feed->child_argv[j]);
3887 fprintf(stdout, "\n");
3890 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3892 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3894 } else if (stream) {
3895 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3897 } else if (!strcasecmp(cmd, "File")) {
3899 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3901 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3902 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3907 get_arg(arg, sizeof(arg), &p);
3909 fsize = strtod(p1, &p1);
3910 switch(toupper(*p1)) {
3915 fsize *= 1024 * 1024;
3918 fsize *= 1024 * 1024 * 1024;
3921 feed->feed_max_size = (int64_t)fsize;
3923 } else if (!strcasecmp(cmd, "</Feed>")) {
3925 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3926 filename, line_num);
3930 } else if (!strcasecmp(cmd, "<Stream")) {
3931 /*********************************************/
3932 /* Stream related options */
3934 if (stream || feed) {
3935 fprintf(stderr, "%s:%d: Already in a tag\n",
3936 filename, line_num);
3938 const AVClass *class;
3939 stream = av_mallocz(sizeof(FFStream));
3940 *last_stream = stream;
3941 last_stream = &stream->next;
3943 get_arg(stream->filename, sizeof(stream->filename), &p);
3944 q = strrchr(stream->filename, '>');
3947 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3948 /* fetch avclass so AVOption works
3949 * FIXME try to use avcodec_get_context_defaults2
3950 * without changing defaults too much */
3951 avcodec_get_context_defaults(&video_enc);
3952 class = video_enc.av_class;
3953 memset(&audio_enc, 0, sizeof(AVCodecContext));
3954 memset(&video_enc, 0, sizeof(AVCodecContext));
3955 audio_enc.av_class = class;
3956 video_enc.av_class = class;
3957 audio_id = CODEC_ID_NONE;
3958 video_id = CODEC_ID_NONE;
3960 audio_id = stream->fmt->audio_codec;
3961 video_id = stream->fmt->video_codec;
3964 } else if (!strcasecmp(cmd, "Feed")) {
3965 get_arg(arg, sizeof(arg), &p);
3970 while (sfeed != NULL) {
3971 if (!strcmp(sfeed->filename, arg))
3973 sfeed = sfeed->next_feed;
3976 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3977 filename, line_num, arg);
3979 stream->feed = sfeed;
3981 } else if (!strcasecmp(cmd, "Format")) {
3982 get_arg(arg, sizeof(arg), &p);
3984 if (!strcmp(arg, "status")) {
3985 stream->stream_type = STREAM_TYPE_STATUS;
3988 stream->stream_type = STREAM_TYPE_LIVE;
3989 /* jpeg cannot be used here, so use single frame jpeg */
3990 if (!strcmp(arg, "jpeg"))
3991 strcpy(arg, "mjpeg");
3992 stream->fmt = guess_stream_format(arg, NULL, NULL);
3994 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3995 filename, line_num, arg);
4000 audio_id = stream->fmt->audio_codec;
4001 video_id = stream->fmt->video_codec;
4004 } else if (!strcasecmp(cmd, "InputFormat")) {
4005 get_arg(arg, sizeof(arg), &p);
4006 stream->ifmt = av_find_input_format(arg);
4007 if (!stream->ifmt) {
4008 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4009 filename, line_num, arg);
4011 } else if (!strcasecmp(cmd, "FaviconURL")) {
4012 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4013 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4015 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4016 filename, line_num);
4019 } else if (!strcasecmp(cmd, "Author")) {
4021 get_arg(stream->author, sizeof(stream->author), &p);
4022 } else if (!strcasecmp(cmd, "Comment")) {
4024 get_arg(stream->comment, sizeof(stream->comment), &p);
4025 } else if (!strcasecmp(cmd, "Copyright")) {
4027 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4028 } else if (!strcasecmp(cmd, "Title")) {
4030 get_arg(stream->title, sizeof(stream->title), &p);
4031 } else if (!strcasecmp(cmd, "Preroll")) {
4032 get_arg(arg, sizeof(arg), &p);
4034 stream->prebuffer = atof(arg) * 1000;
4035 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4037 stream->send_on_key = 1;
4038 } else if (!strcasecmp(cmd, "AudioCodec")) {
4039 get_arg(arg, sizeof(arg), &p);
4040 audio_id = opt_audio_codec(arg);
4041 if (audio_id == CODEC_ID_NONE) {
4042 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4043 filename, line_num, arg);
4046 } else if (!strcasecmp(cmd, "VideoCodec")) {
4047 get_arg(arg, sizeof(arg), &p);
4048 video_id = opt_video_codec(arg);
4049 if (video_id == CODEC_ID_NONE) {
4050 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4051 filename, line_num, arg);
4054 } else if (!strcasecmp(cmd, "MaxTime")) {
4055 get_arg(arg, sizeof(arg), &p);
4057 stream->max_time = atof(arg) * 1000;
4058 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4059 get_arg(arg, sizeof(arg), &p);
4061 audio_enc.bit_rate = atoi(arg) * 1000;
4062 } else if (!strcasecmp(cmd, "AudioChannels")) {
4063 get_arg(arg, sizeof(arg), &p);
4065 audio_enc.channels = atoi(arg);
4066 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4067 get_arg(arg, sizeof(arg), &p);
4069 audio_enc.sample_rate = atoi(arg);
4070 } else if (!strcasecmp(cmd, "AudioQuality")) {
4071 get_arg(arg, sizeof(arg), &p);
4073 // audio_enc.quality = atof(arg) * 1000;
4075 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4077 int minrate, maxrate;
4079 get_arg(arg, sizeof(arg), &p);
4081 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4082 video_enc.rc_min_rate = minrate * 1000;
4083 video_enc.rc_max_rate = maxrate * 1000;
4085 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4086 filename, line_num, arg);
4090 } else if (!strcasecmp(cmd, "Debug")) {
4092 get_arg(arg, sizeof(arg), &p);
4093 video_enc.debug = strtol(arg,0,0);
4095 } else if (!strcasecmp(cmd, "Strict")) {
4097 get_arg(arg, sizeof(arg), &p);
4098 video_enc.strict_std_compliance = atoi(arg);
4100 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4102 get_arg(arg, sizeof(arg), &p);
4103 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4105 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4107 get_arg(arg, sizeof(arg), &p);
4108 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4110 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4111 get_arg(arg, sizeof(arg), &p);
4113 video_enc.bit_rate = atoi(arg) * 1000;
4115 } else if (!strcasecmp(cmd, "VideoSize")) {
4116 get_arg(arg, sizeof(arg), &p);
4118 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4119 if ((video_enc.width % 16) != 0 ||
4120 (video_enc.height % 16) != 0) {
4121 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4122 filename, line_num);
4126 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4127 get_arg(arg, sizeof(arg), &p);
4129 AVRational frame_rate;
4130 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4131 fprintf(stderr, "Incorrect frame rate\n");
4134 video_enc.time_base.num = frame_rate.den;
4135 video_enc.time_base.den = frame_rate.num;
4138 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4139 get_arg(arg, sizeof(arg), &p);
4141 video_enc.gop_size = atoi(arg);
4142 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4144 video_enc.gop_size = 1;
4145 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4147 video_enc.mb_decision = FF_MB_DECISION_BITS;
4148 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4150 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4151 video_enc.flags |= CODEC_FLAG_4MV;
4153 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4154 !strcasecmp(cmd, "AVOptionAudio")) {
4156 AVCodecContext *avctx;
4158 get_arg(arg, sizeof(arg), &p);
4159 get_arg(arg2, sizeof(arg2), &p);
4160 if (!strcasecmp(cmd, "AVOptionVideo")) {
4162 type = AV_OPT_FLAG_VIDEO_PARAM;
4165 type = AV_OPT_FLAG_AUDIO_PARAM;
4167 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4168 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4171 } else if (!strcasecmp(cmd, "VideoTag")) {
4172 get_arg(arg, sizeof(arg), &p);
4173 if ((strlen(arg) == 4) && stream)
4174 video_enc.codec_tag = ff_get_fourcc(arg);
4175 } else if (!strcasecmp(cmd, "BitExact")) {
4177 video_enc.flags |= CODEC_FLAG_BITEXACT;
4178 } else if (!strcasecmp(cmd, "DctFastint")) {
4180 video_enc.dct_algo = FF_DCT_FASTINT;
4181 } else if (!strcasecmp(cmd, "IdctSimple")) {
4183 video_enc.idct_algo = FF_IDCT_SIMPLE;
4184 } else if (!strcasecmp(cmd, "Qscale")) {
4185 get_arg(arg, sizeof(arg), &p);
4187 video_enc.flags |= CODEC_FLAG_QSCALE;
4188 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4190 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4191 get_arg(arg, sizeof(arg), &p);
4193 video_enc.max_qdiff = atoi(arg);
4194 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4195 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4196 filename, line_num);
4200 } else if (!strcasecmp(cmd, "VideoQMax")) {
4201 get_arg(arg, sizeof(arg), &p);
4203 video_enc.qmax = atoi(arg);
4204 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4205 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4206 filename, line_num);
4210 } else if (!strcasecmp(cmd, "VideoQMin")) {
4211 get_arg(arg, sizeof(arg), &p);
4213 video_enc.qmin = atoi(arg);
4214 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4215 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4216 filename, line_num);
4220 } else if (!strcasecmp(cmd, "LumaElim")) {
4221 get_arg(arg, sizeof(arg), &p);
4223 video_enc.luma_elim_threshold = atoi(arg);
4224 } else if (!strcasecmp(cmd, "ChromaElim")) {
4225 get_arg(arg, sizeof(arg), &p);
4227 video_enc.chroma_elim_threshold = atoi(arg);
4228 } else if (!strcasecmp(cmd, "LumiMask")) {
4229 get_arg(arg, sizeof(arg), &p);
4231 video_enc.lumi_masking = atof(arg);
4232 } else if (!strcasecmp(cmd, "DarkMask")) {
4233 get_arg(arg, sizeof(arg), &p);
4235 video_enc.dark_masking = atof(arg);
4236 } else if (!strcasecmp(cmd, "NoVideo")) {
4237 video_id = CODEC_ID_NONE;
4238 } else if (!strcasecmp(cmd, "NoAudio")) {
4239 audio_id = CODEC_ID_NONE;
4240 } else if (!strcasecmp(cmd, "ACL")) {
4243 get_arg(arg, sizeof(arg), &p);
4244 if (strcasecmp(arg, "allow") == 0)
4245 acl.action = IP_ALLOW;
4246 else if (strcasecmp(arg, "deny") == 0)
4247 acl.action = IP_DENY;
4249 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4250 filename, line_num, arg);
4254 get_arg(arg, sizeof(arg), &p);
4256 if (resolve_host(&acl.first, arg) != 0) {
4257 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4258 filename, line_num, arg);
4261 acl.last = acl.first;
4263 get_arg(arg, sizeof(arg), &p);
4266 if (resolve_host(&acl.last, arg) != 0) {
4267 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4268 filename, line_num, arg);
4274 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4275 IPAddressACL **naclp = 0;
4281 naclp = &stream->acl;
4285 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4286 filename, line_num);
4292 naclp = &(*naclp)->next;
4297 } else if (!strcasecmp(cmd, "RTSPOption")) {
4298 get_arg(arg, sizeof(arg), &p);
4300 av_freep(&stream->rtsp_option);
4301 stream->rtsp_option = av_strdup(arg);
4303 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4304 get_arg(arg, sizeof(arg), &p);
4306 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4307 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4308 filename, line_num, arg);
4311 stream->is_multicast = 1;
4312 stream->loop = 1; /* default is looping */
4314 } else if (!strcasecmp(cmd, "MulticastPort")) {
4315 get_arg(arg, sizeof(arg), &p);
4317 stream->multicast_port = atoi(arg);
4318 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4319 get_arg(arg, sizeof(arg), &p);
4321 stream->multicast_ttl = atoi(arg);
4322 } else if (!strcasecmp(cmd, "NoLoop")) {
4325 } else if (!strcasecmp(cmd, "</Stream>")) {
4327 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4328 filename, line_num);
4331 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4332 if (audio_id != CODEC_ID_NONE) {
4333 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4334 audio_enc.codec_id = audio_id;
4335 add_codec(stream, &audio_enc);
4337 if (video_id != CODEC_ID_NONE) {
4338 video_enc.codec_type = CODEC_TYPE_VIDEO;
4339 video_enc.codec_id = video_id;
4340 add_codec(stream, &video_enc);
4345 } else if (!strcasecmp(cmd, "<Redirect")) {
4346 /*********************************************/
4348 if (stream || feed || redirect) {
4349 fprintf(stderr, "%s:%d: Already in a tag\n",
4350 filename, line_num);
4353 redirect = av_mallocz(sizeof(FFStream));
4354 *last_stream = redirect;
4355 last_stream = &redirect->next;
4357 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4358 q = strrchr(redirect->filename, '>');
4361 redirect->stream_type = STREAM_TYPE_REDIRECT;
4363 } else if (!strcasecmp(cmd, "URL")) {
4365 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4366 } else if (!strcasecmp(cmd, "</Redirect>")) {
4368 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4369 filename, line_num);
4372 if (!redirect->feed_filename[0]) {
4373 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4374 filename, line_num);
4379 } else if (!strcasecmp(cmd, "LoadModule")) {
4380 get_arg(arg, sizeof(arg), &p);
4384 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4385 filename, line_num, arg);
4389 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4390 filename, line_num, cmd);
4402 static void handle_child_exit(int sig)
4407 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4410 for (feed = first_feed; feed; feed = feed->next) {
4411 if (feed->pid == pid) {
4412 int uptime = time(0) - feed->pid_start;
4415 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4418 /* Turn off any more restarts */
4419 feed->child_argv = 0;
4424 need_to_start_children = 1;
4427 static void opt_debug()
4430 ffserver_daemon = 0;
4431 logfilename[0] = '-';
4434 static void opt_show_help(void)
4436 printf("usage: ffserver [options]\n"
4437 "Hyper fast multi format Audio/Video streaming server\n");
4439 show_help_options(options, "Main options:\n", 0, 0);
4442 static const OptionDef options[] = {
4443 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4444 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4445 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4446 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4447 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4448 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4449 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4453 int main(int argc, char **argv)
4455 struct sigaction sigact;
4461 config_filename = "/etc/ffserver.conf";
4463 my_program_name = argv[0];
4464 my_program_dir = getcwd(0, 0);
4465 ffserver_daemon = 1;
4467 parse_options(argc, argv, options, NULL);
4469 unsetenv("http_proxy"); /* Kill the http_proxy */
4471 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4473 memset(&sigact, 0, sizeof(sigact));
4474 sigact.sa_handler = handle_child_exit;
4475 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4476 sigaction(SIGCHLD, &sigact, 0);
4478 if (parse_ffconfig(config_filename) < 0) {
4479 fprintf(stderr, "Incorrect config file - exiting.\n");
4483 build_file_streams();
4485 build_feed_streams();
4487 compute_bandwidth();
4489 /* put the process in background and detach it from its TTY */
4490 if (ffserver_daemon) {
4497 } else if (pid > 0) {
4505 open("/dev/null", O_RDWR);
4506 if (strcmp(logfilename, "-") != 0) {
4516 signal(SIGPIPE, SIG_IGN);
4518 /* open log file if needed */
4519 if (logfilename[0] != '\0') {
4520 if (!strcmp(logfilename, "-"))
4523 logfile = fopen(logfilename, "a");
4526 if (http_server() < 0) {
4527 http_log("Could not start server\n");