2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
28 #include "libavutil/random.h"
29 #include "libavutil/avstring.h"
30 #include "libavformat/avformat.h"
31 #include "libavformat/network.h"
32 #include "libavformat/os_support.h"
33 #include "libavformat/rtp.h"
34 #include "libavformat/rtsp.h"
35 #include "libavcodec/opt.h"
39 #include <sys/ioctl.h>
45 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
57 const char program_name[] = "FFserver";
58 const int program_birth_year = 2000;
60 static const OptionDef options[];
62 /* maximum number of simultaneous HTTP connections */
63 #define HTTP_MAX_CONNECTIONS 2000
66 HTTPSTATE_WAIT_REQUEST,
67 HTTPSTATE_SEND_HEADER,
68 HTTPSTATE_SEND_DATA_HEADER,
69 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
70 HTTPSTATE_SEND_DATA_TRAILER,
71 HTTPSTATE_RECEIVE_DATA,
72 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
75 RTSPSTATE_WAIT_REQUEST,
77 RTSPSTATE_SEND_PACKET,
80 static const char *http_state[] = {
96 #define IOBUFFER_INIT_SIZE 8192
98 /* timeouts are in ms */
99 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
100 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
102 #define SYNC_TIMEOUT (10 * 1000)
105 int64_t count1, count2;
106 int64_t time1, time2;
109 /* context associated with one connection */
110 typedef struct HTTPContext {
111 enum HTTPState state;
112 int fd; /* socket file descriptor */
113 struct sockaddr_in from_addr; /* origin */
114 struct pollfd *poll_entry; /* used when polling */
116 uint8_t *buffer_ptr, *buffer_end;
119 struct HTTPContext *next;
120 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
124 /* input format handling */
125 AVFormatContext *fmt_in;
126 int64_t start_time; /* In milliseconds - this wraps fairly often */
127 int64_t first_pts; /* initial pts value */
128 int64_t cur_pts; /* current pts value from the stream in us */
129 int64_t cur_frame_duration; /* duration of the current frame in us */
130 int cur_frame_bytes; /* output frame size, needed to compute
131 the time at which we send each
133 int pts_stream_index; /* stream we choose as clock reference */
134 int64_t cur_clock; /* current clock reference value in us */
135 /* output format handling */
136 struct FFStream *stream;
137 /* -1 is invalid stream */
138 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
139 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
141 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
142 int last_packet_sent; /* true if last data packet was sent */
144 DataRateData datarate;
151 int is_packetized; /* if true, the stream is packetized */
152 int packet_stream_index; /* current stream for output in state machine */
154 /* RTSP state specific */
155 uint8_t *pb_buffer; /* XXX: use that in all the code */
157 int seq; /* RTSP sequence number */
159 /* RTP state specific */
160 enum RTSPProtocol rtp_protocol;
161 char session_id[32]; /* session id */
162 AVFormatContext *rtp_ctx[MAX_STREAMS];
164 /* RTP/UDP specific */
165 URLContext *rtp_handles[MAX_STREAMS];
167 /* RTP/TCP specific */
168 struct HTTPContext *rtsp_c;
169 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
172 /* each generated stream is described here */
176 STREAM_TYPE_REDIRECT,
179 enum IPAddressAction {
184 typedef struct IPAddressACL {
185 struct IPAddressACL *next;
186 enum IPAddressAction action;
187 /* These are in host order */
188 struct in_addr first;
192 /* description of each stream of the ffserver.conf file */
193 typedef struct FFStream {
194 enum StreamType stream_type;
195 char filename[1024]; /* stream filename */
196 struct FFStream *feed; /* feed we are using (can be null if
198 AVFormatParameters *ap_in; /* input parameters */
199 AVInputFormat *ifmt; /* if non NULL, force input format */
203 int prebuffer; /* Number of millseconds early to start */
204 int64_t max_time; /* Number of milliseconds to run */
206 AVStream *streams[MAX_STREAMS];
207 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
208 char feed_filename[1024]; /* file name of the feed storage, or
209 input file name for a stream */
214 pid_t pid; /* Of ffmpeg process */
215 time_t pid_start; /* Of ffmpeg process */
217 struct FFStream *next;
218 unsigned bandwidth; /* bandwidth, in kbits/s */
221 /* multicast specific */
223 struct in_addr multicast_ip;
224 int multicast_port; /* first port used for multicast */
226 int loop; /* if true, send the stream in loops (only meaningful if file) */
229 int feed_opened; /* true if someone is writing to the feed */
230 int is_feed; /* true if it is a feed */
231 int readonly; /* True if writing is prohibited to the file */
233 int64_t bytes_served;
234 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
235 int64_t feed_write_index; /* current write position in feed (it wraps around) */
236 int64_t feed_size; /* current size of feed */
237 struct FFStream *next_feed;
240 typedef struct FeedData {
241 long long data_count;
242 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
245 static struct sockaddr_in my_http_addr;
246 static struct sockaddr_in my_rtsp_addr;
248 static char logfilename[1024];
249 static HTTPContext *first_http_ctx;
250 static FFStream *first_feed; /* contains only feeds */
251 static FFStream *first_stream; /* contains all streams, including feeds */
253 static void new_connection(int server_fd, int is_rtsp);
254 static void close_connection(HTTPContext *c);
257 static int handle_connection(HTTPContext *c);
258 static int http_parse_request(HTTPContext *c);
259 static int http_send_data(HTTPContext *c);
260 static void compute_status(HTTPContext *c);
261 static int open_input_stream(HTTPContext *c, const char *info);
262 static int http_start_receive_data(HTTPContext *c);
263 static int http_receive_data(HTTPContext *c);
266 static int rtsp_parse_request(HTTPContext *c);
267 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
268 static void rtsp_cmd_options(HTTPContext *c, const char *url);
269 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
270 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
271 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
275 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
276 struct in_addr my_ip);
279 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
280 FFStream *stream, const char *session_id,
281 enum RTSPProtocol rtp_protocol);
282 static int rtp_new_av_stream(HTTPContext *c,
283 int stream_index, struct sockaddr_in *dest_addr,
284 HTTPContext *rtsp_c);
286 static const char *my_program_name;
287 static const char *my_program_dir;
289 static const char *config_filename;
290 static int ffserver_debug;
291 static int ffserver_daemon;
292 static int no_launch;
293 static int need_to_start_children;
295 static int nb_max_connections = 5;
296 static int nb_connections;
298 static uint64_t max_bandwidth = 1000;
299 static uint64_t current_bandwidth;
301 static int64_t cur_time; // Making this global saves on passing it around everywhere
303 static AVRandomState random_state;
305 static FILE *logfile = NULL;
307 static char *ctime1(char *buf2)
315 p = buf2 + strlen(p) - 1;
321 static void http_vlog(const char *fmt, va_list vargs)
323 static int print_prefix = 1;
328 fprintf(logfile, "%s ", buf);
330 print_prefix = strstr(fmt, "\n") != NULL;
331 vfprintf(logfile, fmt, vargs);
336 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
339 va_start(vargs, fmt);
340 http_vlog(fmt, vargs);
344 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
346 static int print_prefix = 1;
347 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
348 if (level > av_log_level)
350 if (print_prefix && avc)
351 http_log("[%s @ %p]", avc->item_name(ptr), avc);
352 print_prefix = strstr(fmt, "\n") != NULL;
353 http_vlog(fmt, vargs);
356 static void log_connection(HTTPContext *c)
361 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
362 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
363 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
366 static void update_datarate(DataRateData *drd, int64_t count)
368 if (!drd->time1 && !drd->count1) {
369 drd->time1 = drd->time2 = cur_time;
370 drd->count1 = drd->count2 = count;
371 } else if (cur_time - drd->time2 > 5000) {
372 drd->time1 = drd->time2;
373 drd->count1 = drd->count2;
374 drd->time2 = cur_time;
379 /* In bytes per second */
380 static int compute_datarate(DataRateData *drd, int64_t count)
382 if (cur_time == drd->time1)
385 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
389 static void start_children(FFStream *feed)
394 for (; feed; feed = feed->next) {
395 if (feed->child_argv && !feed->pid) {
396 feed->pid_start = time(0);
401 http_log("Unable to create children\n");
410 av_strlcpy(pathname, my_program_name, sizeof(pathname));
412 slash = strrchr(pathname, '/');
417 strcpy(slash, "ffmpeg");
419 if (ffserver_debug) {
420 fprintf(stdout, "Launch commandline: ");
421 fprintf(stdout, "%s ", pathname);
422 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
423 fprintf(stdout, "%s ", feed->child_argv[i]);
424 fprintf(stdout, "\n");
427 for (i = 3; i < 256; i++)
430 if (!ffserver_debug) {
431 i = open("/dev/null", O_RDWR);
440 /* This is needed to make relative pathnames work */
441 chdir(my_program_dir);
443 signal(SIGPIPE, SIG_DFL);
445 execvp(pathname, feed->child_argv);
453 /* open a listening socket */
454 static int socket_open_listen(struct sockaddr_in *my_addr)
458 server_fd = socket(AF_INET,SOCK_STREAM,0);
465 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
467 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
469 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
471 closesocket(server_fd);
475 if (listen (server_fd, 5) < 0) {
477 closesocket(server_fd);
480 ff_socket_nonblock(server_fd, 1);
485 /* start all multicast streams */
486 static void start_multicast(void)
491 struct sockaddr_in dest_addr;
492 int default_port, stream_index;
495 for(stream = first_stream; stream != NULL; stream = stream->next) {
496 if (stream->is_multicast) {
497 /* open the RTP connection */
498 snprintf(session_id, sizeof(session_id), "%08x%08x",
499 av_random(&random_state), av_random(&random_state));
501 /* choose a port if none given */
502 if (stream->multicast_port == 0) {
503 stream->multicast_port = default_port;
507 dest_addr.sin_family = AF_INET;
508 dest_addr.sin_addr = stream->multicast_ip;
509 dest_addr.sin_port = htons(stream->multicast_port);
511 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
512 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
516 if (open_input_stream(rtp_c, "") < 0) {
517 http_log("Could not open input stream for stream '%s'\n",
522 /* open each RTP stream */
523 for(stream_index = 0; stream_index < stream->nb_streams;
525 dest_addr.sin_port = htons(stream->multicast_port +
527 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
528 http_log("Could not open output stream '%s/streamid=%d'\n",
529 stream->filename, stream_index);
534 /* change state to send data */
535 rtp_c->state = HTTPSTATE_SEND_DATA;
540 /* main loop of the http server */
541 static int http_server(void)
543 int server_fd = 0, rtsp_server_fd = 0;
544 int ret, delay, delay1;
545 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
546 HTTPContext *c, *c_next;
548 if (my_http_addr.sin_port) {
549 server_fd = socket_open_listen(&my_http_addr);
554 if (my_rtsp_addr.sin_port) {
555 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
556 if (rtsp_server_fd < 0)
560 if (!rtsp_server_fd && !server_fd) {
561 http_log("HTTP and RTSP disabled.\n");
565 http_log("ffserver started.\n");
567 start_children(first_feed);
569 first_http_ctx = NULL;
575 poll_entry = poll_table;
577 poll_entry->fd = server_fd;
578 poll_entry->events = POLLIN;
581 if (rtsp_server_fd) {
582 poll_entry->fd = rtsp_server_fd;
583 poll_entry->events = POLLIN;
587 /* wait for events on each HTTP handle */
594 case HTTPSTATE_SEND_HEADER:
595 case RTSPSTATE_SEND_REPLY:
596 case RTSPSTATE_SEND_PACKET:
597 c->poll_entry = poll_entry;
599 poll_entry->events = POLLOUT;
602 case HTTPSTATE_SEND_DATA_HEADER:
603 case HTTPSTATE_SEND_DATA:
604 case HTTPSTATE_SEND_DATA_TRAILER:
605 if (!c->is_packetized) {
606 /* for TCP, we output as much as we can (may need to put a limit) */
607 c->poll_entry = poll_entry;
609 poll_entry->events = POLLOUT;
612 /* when ffserver is doing the timing, we work by
613 looking at which packet need to be sent every
615 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
620 case HTTPSTATE_WAIT_REQUEST:
621 case HTTPSTATE_RECEIVE_DATA:
622 case HTTPSTATE_WAIT_FEED:
623 case RTSPSTATE_WAIT_REQUEST:
624 /* need to catch errors */
625 c->poll_entry = poll_entry;
627 poll_entry->events = POLLIN;/* Maybe this will work */
631 c->poll_entry = NULL;
637 /* wait for an event on one connection. We poll at least every
638 second to handle timeouts */
640 ret = poll(poll_table, poll_entry - poll_table, delay);
641 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
642 ff_neterrno() != FF_NETERROR(EINTR))
646 cur_time = av_gettime() / 1000;
648 if (need_to_start_children) {
649 need_to_start_children = 0;
650 start_children(first_feed);
653 /* now handle the events */
654 for(c = first_http_ctx; c != NULL; c = c_next) {
656 if (handle_connection(c) < 0) {
657 /* close and free the connection */
663 poll_entry = poll_table;
665 /* new HTTP connection request ? */
666 if (poll_entry->revents & POLLIN)
667 new_connection(server_fd, 0);
670 if (rtsp_server_fd) {
671 /* new RTSP connection request ? */
672 if (poll_entry->revents & POLLIN)
673 new_connection(rtsp_server_fd, 1);
678 /* start waiting for a new HTTP/RTSP request */
679 static void start_wait_request(HTTPContext *c, int is_rtsp)
681 c->buffer_ptr = c->buffer;
682 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
685 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
686 c->state = RTSPSTATE_WAIT_REQUEST;
688 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
689 c->state = HTTPSTATE_WAIT_REQUEST;
693 static void new_connection(int server_fd, int is_rtsp)
695 struct sockaddr_in from_addr;
697 HTTPContext *c = NULL;
699 len = sizeof(from_addr);
700 fd = accept(server_fd, (struct sockaddr *)&from_addr,
703 http_log("error during accept %s\n", strerror(errno));
706 ff_socket_nonblock(fd, 1);
708 /* XXX: should output a warning page when coming
709 close to the connection limit */
710 if (nb_connections >= nb_max_connections)
713 /* add a new connection */
714 c = av_mallocz(sizeof(HTTPContext));
719 c->poll_entry = NULL;
720 c->from_addr = from_addr;
721 c->buffer_size = IOBUFFER_INIT_SIZE;
722 c->buffer = av_malloc(c->buffer_size);
726 c->next = first_http_ctx;
730 start_wait_request(c, is_rtsp);
742 static void close_connection(HTTPContext *c)
744 HTTPContext **cp, *c1;
746 AVFormatContext *ctx;
750 /* remove connection from list */
751 cp = &first_http_ctx;
752 while ((*cp) != NULL) {
760 /* remove references, if any (XXX: do it faster) */
761 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
766 /* remove connection associated resources */
770 /* close each frame parser */
771 for(i=0;i<c->fmt_in->nb_streams;i++) {
772 st = c->fmt_in->streams[i];
773 if (st->codec->codec)
774 avcodec_close(st->codec);
776 av_close_input_file(c->fmt_in);
779 /* free RTP output streams if any */
782 nb_streams = c->stream->nb_streams;
784 for(i=0;i<nb_streams;i++) {
787 av_write_trailer(ctx);
790 h = c->rtp_handles[i];
797 if (!c->last_packet_sent) {
800 if (url_open_dyn_buf(&ctx->pb) >= 0) {
801 av_write_trailer(ctx);
802 av_freep(&c->pb_buffer);
803 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
808 for(i=0; i<ctx->nb_streams; i++)
809 av_free(ctx->streams[i]);
811 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
812 current_bandwidth -= c->stream->bandwidth;
814 /* signal that there is no feed if we are the feeder socket */
815 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
816 c->stream->feed_opened = 0;
820 av_freep(&c->pb_buffer);
821 av_freep(&c->packet_buffer);
827 static int handle_connection(HTTPContext *c)
832 case HTTPSTATE_WAIT_REQUEST:
833 case RTSPSTATE_WAIT_REQUEST:
835 if ((c->timeout - cur_time) < 0)
837 if (c->poll_entry->revents & (POLLERR | POLLHUP))
840 /* no need to read if no events */
841 if (!(c->poll_entry->revents & POLLIN))
845 len = recv(c->fd, c->buffer_ptr, 1, 0);
847 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
848 ff_neterrno() != FF_NETERROR(EINTR))
850 } else if (len == 0) {
853 /* search for end of request. */
855 c->buffer_ptr += len;
857 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
858 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
859 /* request found : parse it and reply */
860 if (c->state == HTTPSTATE_WAIT_REQUEST) {
861 ret = http_parse_request(c);
863 ret = rtsp_parse_request(c);
867 } else if (ptr >= c->buffer_end) {
868 /* request too long: cannot do anything */
870 } else goto read_loop;
874 case HTTPSTATE_SEND_HEADER:
875 if (c->poll_entry->revents & (POLLERR | POLLHUP))
878 /* no need to write if no events */
879 if (!(c->poll_entry->revents & POLLOUT))
881 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
883 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
884 ff_neterrno() != FF_NETERROR(EINTR)) {
885 /* error : close connection */
886 av_freep(&c->pb_buffer);
890 c->buffer_ptr += len;
892 c->stream->bytes_served += len;
893 c->data_count += len;
894 if (c->buffer_ptr >= c->buffer_end) {
895 av_freep(&c->pb_buffer);
899 /* all the buffer was sent : synchronize to the incoming stream */
900 c->state = HTTPSTATE_SEND_DATA_HEADER;
901 c->buffer_ptr = c->buffer_end = c->buffer;
906 case HTTPSTATE_SEND_DATA:
907 case HTTPSTATE_SEND_DATA_HEADER:
908 case HTTPSTATE_SEND_DATA_TRAILER:
909 /* for packetized output, we consider we can always write (the
910 input streams sets the speed). It may be better to verify
911 that we do not rely too much on the kernel queues */
912 if (!c->is_packetized) {
913 if (c->poll_entry->revents & (POLLERR | POLLHUP))
916 /* no need to read if no events */
917 if (!(c->poll_entry->revents & POLLOUT))
920 if (http_send_data(c) < 0)
922 /* close connection if trailer sent */
923 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
926 case HTTPSTATE_RECEIVE_DATA:
927 /* no need to read if no events */
928 if (c->poll_entry->revents & (POLLERR | POLLHUP))
930 if (!(c->poll_entry->revents & POLLIN))
932 if (http_receive_data(c) < 0)
935 case HTTPSTATE_WAIT_FEED:
936 /* no need to read if no events */
937 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
940 /* nothing to do, we'll be waken up by incoming feed packets */
943 case RTSPSTATE_SEND_REPLY:
944 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
945 av_freep(&c->pb_buffer);
948 /* no need to write if no events */
949 if (!(c->poll_entry->revents & POLLOUT))
951 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
953 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
954 ff_neterrno() != FF_NETERROR(EINTR)) {
955 /* error : close connection */
956 av_freep(&c->pb_buffer);
960 c->buffer_ptr += len;
961 c->data_count += len;
962 if (c->buffer_ptr >= c->buffer_end) {
963 /* all the buffer was sent : wait for a new request */
964 av_freep(&c->pb_buffer);
965 start_wait_request(c, 1);
969 case RTSPSTATE_SEND_PACKET:
970 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
971 av_freep(&c->packet_buffer);
974 /* no need to write if no events */
975 if (!(c->poll_entry->revents & POLLOUT))
977 len = send(c->fd, c->packet_buffer_ptr,
978 c->packet_buffer_end - c->packet_buffer_ptr, 0);
980 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
981 ff_neterrno() != FF_NETERROR(EINTR)) {
982 /* error : close connection */
983 av_freep(&c->packet_buffer);
987 c->packet_buffer_ptr += len;
988 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
989 /* all the buffer was sent : wait for a new request */
990 av_freep(&c->packet_buffer);
991 c->state = RTSPSTATE_WAIT_REQUEST;
995 case HTTPSTATE_READY:
1004 static int extract_rates(char *rates, int ratelen, const char *request)
1008 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1009 if (strncasecmp(p, "Pragma:", 7) == 0) {
1010 const char *q = p + 7;
1012 while (*q && *q != '\n' && isspace(*q))
1015 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1021 memset(rates, 0xff, ratelen);
1024 while (*q && *q != '\n' && *q != ':')
1027 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1031 if (stream_no < ratelen && stream_no >= 0)
1032 rates[stream_no] = rate_no;
1034 while (*q && *q != '\n' && !isspace(*q))
1041 p = strchr(p, '\n');
1051 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1054 int best_bitrate = 100000000;
1057 for (i = 0; i < feed->nb_streams; i++) {
1058 AVCodecContext *feed_codec = feed->streams[i]->codec;
1060 if (feed_codec->codec_id != codec->codec_id ||
1061 feed_codec->sample_rate != codec->sample_rate ||
1062 feed_codec->width != codec->width ||
1063 feed_codec->height != codec->height)
1066 /* Potential stream */
1068 /* We want the fastest stream less than bit_rate, or the slowest
1069 * faster than bit_rate
1072 if (feed_codec->bit_rate <= bit_rate) {
1073 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1074 best_bitrate = feed_codec->bit_rate;
1078 if (feed_codec->bit_rate < best_bitrate) {
1079 best_bitrate = feed_codec->bit_rate;
1088 static int modify_current_stream(HTTPContext *c, char *rates)
1091 FFStream *req = c->stream;
1092 int action_required = 0;
1094 /* Not much we can do for a feed */
1098 for (i = 0; i < req->nb_streams; i++) {
1099 AVCodecContext *codec = req->streams[i]->codec;
1103 c->switch_feed_streams[i] = req->feed_streams[i];
1106 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1109 /* Wants off or slow */
1110 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1112 /* This doesn't work well when it turns off the only stream! */
1113 c->switch_feed_streams[i] = -2;
1114 c->feed_streams[i] = -2;
1119 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1120 action_required = 1;
1123 return action_required;
1127 static void do_switch_stream(HTTPContext *c, int i)
1129 if (c->switch_feed_streams[i] >= 0) {
1131 c->feed_streams[i] = c->switch_feed_streams[i];
1134 /* Now update the stream */
1136 c->switch_feed_streams[i] = -1;
1139 /* XXX: factorize in utils.c ? */
1140 /* XXX: take care with different space meaning */
1141 static void skip_spaces(const char **pp)
1145 while (*p == ' ' || *p == '\t')
1150 static void get_word(char *buf, int buf_size, const char **pp)
1158 while (!isspace(*p) && *p != '\0') {
1159 if ((q - buf) < buf_size - 1)
1168 static int validate_acl(FFStream *stream, HTTPContext *c)
1170 enum IPAddressAction last_action = IP_DENY;
1172 struct in_addr *src = &c->from_addr.sin_addr;
1173 unsigned long src_addr = src->s_addr;
1175 for (acl = stream->acl; acl; acl = acl->next) {
1176 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1177 return (acl->action == IP_ALLOW) ? 1 : 0;
1178 last_action = acl->action;
1181 /* Nothing matched, so return not the last action */
1182 return (last_action == IP_DENY) ? 1 : 0;
1185 /* compute the real filename of a file by matching it without its
1186 extensions to all the stream filenames */
1187 static void compute_real_filename(char *filename, int max_size)
1194 /* compute filename by matching without the file extensions */
1195 av_strlcpy(file1, filename, sizeof(file1));
1196 p = strrchr(file1, '.');
1199 for(stream = first_stream; stream != NULL; stream = stream->next) {
1200 av_strlcpy(file2, stream->filename, sizeof(file2));
1201 p = strrchr(file2, '.');
1204 if (!strcmp(file1, file2)) {
1205 av_strlcpy(filename, stream->filename, max_size);
1220 /* parse http request and prepare header */
1221 static int http_parse_request(HTTPContext *c)
1224 enum RedirType redir_type;
1226 char info[1024], filename[1024];
1230 const char *mime_type;
1234 char *useragent = 0;
1237 get_word(cmd, sizeof(cmd), (const char **)&p);
1238 av_strlcpy(c->method, cmd, sizeof(c->method));
1240 if (!strcmp(cmd, "GET"))
1242 else if (!strcmp(cmd, "POST"))
1247 get_word(url, sizeof(url), (const char **)&p);
1248 av_strlcpy(c->url, url, sizeof(c->url));
1250 get_word(protocol, sizeof(protocol), (const char **)&p);
1251 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1254 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1257 http_log("New connection: %s %s\n", cmd, url);
1259 /* find the filename and the optional info string in the request */
1260 p = strchr(url, '?');
1262 av_strlcpy(info, p, sizeof(info));
1267 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1269 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1270 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1272 if (*useragent && *useragent != '\n' && isspace(*useragent))
1276 p = strchr(p, '\n');
1283 redir_type = REDIR_NONE;
1284 if (match_ext(filename, "asx")) {
1285 redir_type = REDIR_ASX;
1286 filename[strlen(filename)-1] = 'f';
1287 } else if (match_ext(filename, "asf") &&
1288 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1289 /* if this isn't WMP or lookalike, return the redirector file */
1290 redir_type = REDIR_ASF;
1291 } else if (match_ext(filename, "rpm,ram")) {
1292 redir_type = REDIR_RAM;
1293 strcpy(filename + strlen(filename)-2, "m");
1294 } else if (match_ext(filename, "rtsp")) {
1295 redir_type = REDIR_RTSP;
1296 compute_real_filename(filename, sizeof(filename) - 1);
1297 } else if (match_ext(filename, "sdp")) {
1298 redir_type = REDIR_SDP;
1299 compute_real_filename(filename, sizeof(filename) - 1);
1302 // "redirect" / request to index.html
1303 if (!strlen(filename))
1304 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1306 stream = first_stream;
1307 while (stream != NULL) {
1308 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1310 stream = stream->next;
1312 if (stream == NULL) {
1313 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1318 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1319 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1321 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1322 c->http_error = 301;
1324 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1325 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1326 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1327 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1328 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1329 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1330 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1332 /* prepare output buffer */
1333 c->buffer_ptr = c->buffer;
1335 c->state = HTTPSTATE_SEND_HEADER;
1339 /* If this is WMP, get the rate information */
1340 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1341 if (modify_current_stream(c, ratebuf)) {
1342 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1343 if (c->switch_feed_streams[i] >= 0)
1344 do_switch_stream(c, i);
1349 /* If already streaming this feed, do not let start another feeder. */
1350 if (stream->feed_opened) {
1351 snprintf(msg, sizeof(msg), "This feed is already being received.");
1352 http_log("feed %s already being received\n", stream->feed_filename);
1356 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1357 current_bandwidth += stream->bandwidth;
1359 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1360 c->http_error = 200;
1362 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1363 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1364 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1365 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1366 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");
1367 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",
1368 current_bandwidth, max_bandwidth);
1369 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1371 /* prepare output buffer */
1372 c->buffer_ptr = c->buffer;
1374 c->state = HTTPSTATE_SEND_HEADER;
1378 if (redir_type != REDIR_NONE) {
1381 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1382 if (strncasecmp(p, "Host:", 5) == 0) {
1386 p = strchr(p, '\n');
1397 while (isspace(*hostinfo))
1400 eoh = strchr(hostinfo, '\n');
1402 if (eoh[-1] == '\r')
1405 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1406 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1407 hostbuf[eoh - hostinfo] = 0;
1409 c->http_error = 200;
1411 switch(redir_type) {
1413 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1414 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1415 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1416 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1417 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1418 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1419 hostbuf, filename, info);
1420 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1425 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1426 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1427 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1428 hostbuf, filename, info);
1431 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1432 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1433 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1434 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1435 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1436 hostbuf, filename, info);
1440 char hostname[256], *p;
1441 /* extract only hostname */
1442 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1443 p = strrchr(hostname, ':');
1446 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1447 /* XXX: incorrect mime type ? */
1448 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1449 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1450 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1451 hostname, ntohs(my_rtsp_addr.sin_port),
1458 int sdp_data_size, len;
1459 struct sockaddr_in my_addr;
1461 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1462 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1463 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1465 len = sizeof(my_addr);
1466 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1468 /* XXX: should use a dynamic buffer */
1469 sdp_data_size = prepare_sdp_description(stream,
1472 if (sdp_data_size > 0) {
1473 memcpy(q, sdp_data, sdp_data_size);
1485 /* prepare output buffer */
1486 c->buffer_ptr = c->buffer;
1488 c->state = HTTPSTATE_SEND_HEADER;
1494 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1498 stream->conns_served++;
1500 /* XXX: add there authenticate and IP match */
1503 /* if post, it means a feed is being sent */
1504 if (!stream->is_feed) {
1505 /* However it might be a status report from WMP! Lets log the data
1506 * as it might come in handy one day
1511 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1512 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1516 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1517 client_id = strtol(p + 18, 0, 10);
1518 p = strchr(p, '\n');
1526 char *eol = strchr(logline, '\n');
1531 if (eol[-1] == '\r')
1533 http_log("%.*s\n", (int) (eol - logline), logline);
1534 c->suppress_log = 1;
1539 http_log("\nGot request:\n%s\n", c->buffer);
1542 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1545 /* Now we have to find the client_id */
1546 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1547 if (wmpc->wmp_client_id == client_id)
1551 if (wmpc && modify_current_stream(wmpc, ratebuf))
1552 wmpc->switch_pending = 1;
1555 snprintf(msg, sizeof(msg), "POST command not handled");
1559 if (http_start_receive_data(c) < 0) {
1560 snprintf(msg, sizeof(msg), "could not open feed");
1564 c->state = HTTPSTATE_RECEIVE_DATA;
1569 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1570 http_log("\nGot request:\n%s\n", c->buffer);
1573 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1576 /* open input stream */
1577 if (open_input_stream(c, info) < 0) {
1578 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1582 /* prepare http header */
1584 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1585 mime_type = c->stream->fmt->mime_type;
1587 mime_type = "application/x-octet-stream";
1588 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1590 /* for asf, we need extra headers */
1591 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1592 /* Need to allocate a client id */
1594 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1596 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);
1598 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1599 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1601 /* prepare output buffer */
1603 c->buffer_ptr = c->buffer;
1605 c->state = HTTPSTATE_SEND_HEADER;
1608 c->http_error = 404;
1610 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1611 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1612 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1613 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1614 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1615 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1616 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1618 /* prepare output buffer */
1619 c->buffer_ptr = c->buffer;
1621 c->state = HTTPSTATE_SEND_HEADER;
1625 c->http_error = 200; /* horrible : we use this value to avoid
1626 going to the send data state */
1627 c->state = HTTPSTATE_SEND_HEADER;
1631 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1633 static const char *suffix = " kMGTP";
1636 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1638 url_fprintf(pb, "%"PRId64"%c", count, *s);
1641 static void compute_status(HTTPContext *c)
1650 if (url_open_dyn_buf(&pb) < 0) {
1651 /* XXX: return an error ? */
1652 c->buffer_ptr = c->buffer;
1653 c->buffer_end = c->buffer;
1657 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1658 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1659 url_fprintf(pb, "Pragma: no-cache\r\n");
1660 url_fprintf(pb, "\r\n");
1662 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1663 if (c->stream->feed_filename[0])
1664 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1665 url_fprintf(pb, "</HEAD>\n<BODY>");
1666 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1668 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1669 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1670 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");
1671 stream = first_stream;
1672 while (stream != NULL) {
1673 char sfilename[1024];
1676 if (stream->feed != stream) {
1677 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1678 eosf = sfilename + strlen(sfilename);
1679 if (eosf - sfilename >= 4) {
1680 if (strcmp(eosf - 4, ".asf") == 0)
1681 strcpy(eosf - 4, ".asx");
1682 else if (strcmp(eosf - 3, ".rm") == 0)
1683 strcpy(eosf - 3, ".ram");
1684 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1685 /* generate a sample RTSP director if
1686 unicast. Generate an SDP redirector if
1688 eosf = strrchr(sfilename, '.');
1690 eosf = sfilename + strlen(sfilename);
1691 if (stream->is_multicast)
1692 strcpy(eosf, ".sdp");
1694 strcpy(eosf, ".rtsp");
1698 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1699 sfilename, stream->filename);
1700 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1701 stream->conns_served);
1702 fmt_bytecount(pb, stream->bytes_served);
1703 switch(stream->stream_type) {
1704 case STREAM_TYPE_LIVE: {
1705 int audio_bit_rate = 0;
1706 int video_bit_rate = 0;
1707 const char *audio_codec_name = "";
1708 const char *video_codec_name = "";
1709 const char *audio_codec_name_extra = "";
1710 const char *video_codec_name_extra = "";
1712 for(i=0;i<stream->nb_streams;i++) {
1713 AVStream *st = stream->streams[i];
1714 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1715 switch(st->codec->codec_type) {
1716 case CODEC_TYPE_AUDIO:
1717 audio_bit_rate += st->codec->bit_rate;
1719 if (*audio_codec_name)
1720 audio_codec_name_extra = "...";
1721 audio_codec_name = codec->name;
1724 case CODEC_TYPE_VIDEO:
1725 video_bit_rate += st->codec->bit_rate;
1727 if (*video_codec_name)
1728 video_codec_name_extra = "...";
1729 video_codec_name = codec->name;
1732 case CODEC_TYPE_DATA:
1733 video_bit_rate += st->codec->bit_rate;
1739 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",
1742 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1743 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1745 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1747 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1748 url_fprintf(pb, "\n");
1752 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1756 stream = stream->next;
1758 url_fprintf(pb, "</TABLE>\n");
1760 stream = first_stream;
1761 while (stream != NULL) {
1762 if (stream->feed == stream) {
1763 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1765 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1767 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1772 /* This is somewhat linux specific I guess */
1773 snprintf(ps_cmd, sizeof(ps_cmd),
1774 "ps -o \"%%cpu,cputime\" --no-headers %d",
1777 pid_stat = popen(ps_cmd, "r");
1782 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1784 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1792 url_fprintf(pb, "<p>");
1794 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");
1796 for (i = 0; i < stream->nb_streams; i++) {
1797 AVStream *st = stream->streams[i];
1798 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1799 const char *type = "unknown";
1800 char parameters[64];
1804 switch(st->codec->codec_type) {
1805 case CODEC_TYPE_AUDIO:
1807 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1809 case CODEC_TYPE_VIDEO:
1811 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1812 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1817 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1818 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1820 url_fprintf(pb, "</table>\n");
1823 stream = stream->next;
1829 AVCodecContext *enc;
1833 stream = first_feed;
1834 while (stream != NULL) {
1835 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1836 url_fprintf(pb, "<TABLE>\n");
1837 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1838 for(i=0;i<stream->nb_streams;i++) {
1839 AVStream *st = stream->streams[i];
1840 FeedData *fdata = st->priv_data;
1843 avcodec_string(buf, sizeof(buf), enc);
1844 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1845 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1846 avg /= enc->frame_size;
1847 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1848 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1850 url_fprintf(pb, "</TABLE>\n");
1851 stream = stream->next_feed;
1856 /* connection status */
1857 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1859 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1860 nb_connections, nb_max_connections);
1862 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1863 current_bandwidth, max_bandwidth);
1865 url_fprintf(pb, "<TABLE>\n");
1866 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");
1867 c1 = first_http_ctx;
1869 while (c1 != NULL) {
1875 for (j = 0; j < c1->stream->nb_streams; j++) {
1876 if (!c1->stream->feed)
1877 bitrate += c1->stream->streams[j]->codec->bit_rate;
1878 else if (c1->feed_streams[j] >= 0)
1879 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1884 p = inet_ntoa(c1->from_addr.sin_addr);
1885 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1887 c1->stream ? c1->stream->filename : "",
1888 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1891 http_state[c1->state]);
1892 fmt_bytecount(pb, bitrate);
1893 url_fprintf(pb, "<td align=right>");
1894 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1895 url_fprintf(pb, "<td align=right>");
1896 fmt_bytecount(pb, c1->data_count);
1897 url_fprintf(pb, "\n");
1900 url_fprintf(pb, "</TABLE>\n");
1905 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1906 url_fprintf(pb, "</BODY>\n</HTML>\n");
1908 len = url_close_dyn_buf(pb, &c->pb_buffer);
1909 c->buffer_ptr = c->pb_buffer;
1910 c->buffer_end = c->pb_buffer + len;
1913 /* check if the parser needs to be opened for stream i */
1914 static void open_parser(AVFormatContext *s, int i)
1916 AVStream *st = s->streams[i];
1919 if (!st->codec->codec) {
1920 codec = avcodec_find_decoder(st->codec->codec_id);
1921 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1922 st->codec->parse_only = 1;
1923 if (avcodec_open(st->codec, codec) < 0)
1924 st->codec->parse_only = 0;
1929 static int open_input_stream(HTTPContext *c, const char *info)
1932 char input_filename[1024];
1934 int buf_size, i, ret;
1937 /* find file name */
1938 if (c->stream->feed) {
1939 strcpy(input_filename, c->stream->feed->feed_filename);
1940 buf_size = FFM_PACKET_SIZE;
1941 /* compute position (absolute time) */
1942 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1943 stream_pos = parse_date(buf, 0);
1944 if (stream_pos == INT64_MIN)
1946 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1947 int prebuffer = strtol(buf, 0, 10);
1948 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1950 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1952 strcpy(input_filename, c->stream->feed_filename);
1954 /* compute position (relative time) */
1955 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1956 stream_pos = parse_date(buf, 1);
1957 if (stream_pos == INT64_MIN)
1962 if (input_filename[0] == '\0')
1966 { time_t when = stream_pos / 1000000;
1967 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1972 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1973 buf_size, c->stream->ap_in)) < 0) {
1974 http_log("could not open %s: %d\n", input_filename, ret);
1977 s->flags |= AVFMT_FLAG_GENPTS;
1979 av_find_stream_info(c->fmt_in);
1981 /* open each parser */
1982 for(i=0;i<s->nb_streams;i++)
1985 /* choose stream as clock source (we favorize video stream if
1986 present) for packet sending */
1987 c->pts_stream_index = 0;
1988 for(i=0;i<c->stream->nb_streams;i++) {
1989 if (c->pts_stream_index == 0 &&
1990 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1991 c->pts_stream_index = i;
1996 if (c->fmt_in->iformat->read_seek)
1997 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1999 /* set the start time (needed for maxtime and RTP packet timing) */
2000 c->start_time = cur_time;
2001 c->first_pts = AV_NOPTS_VALUE;
2005 /* return the server clock (in us) */
2006 static int64_t get_server_clock(HTTPContext *c)
2008 /* compute current pts value from system time */
2009 return (cur_time - c->start_time) * 1000;
2012 /* return the estimated time at which the current packet must be sent
2014 static int64_t get_packet_send_clock(HTTPContext *c)
2016 int bytes_left, bytes_sent, frame_bytes;
2018 frame_bytes = c->cur_frame_bytes;
2019 if (frame_bytes <= 0)
2022 bytes_left = c->buffer_end - c->buffer_ptr;
2023 bytes_sent = frame_bytes - bytes_left;
2024 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2029 static int http_prepare_data(HTTPContext *c)
2032 AVFormatContext *ctx;
2034 av_freep(&c->pb_buffer);
2036 case HTTPSTATE_SEND_DATA_HEADER:
2037 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2038 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2039 sizeof(c->fmt_ctx.author));
2040 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2041 sizeof(c->fmt_ctx.comment));
2042 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2043 sizeof(c->fmt_ctx.copyright));
2044 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2045 sizeof(c->fmt_ctx.title));
2047 for(i=0;i<c->stream->nb_streams;i++) {
2050 st = av_mallocz(sizeof(AVStream));
2051 c->fmt_ctx.streams[i] = st;
2052 /* if file or feed, then just take streams from FFStream struct */
2053 if (!c->stream->feed ||
2054 c->stream->feed == c->stream)
2055 src = c->stream->streams[i];
2057 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2061 st->codec->frame_number = 0; /* XXX: should be done in
2062 AVStream, not in codec */
2064 /* set output format parameters */
2065 c->fmt_ctx.oformat = c->stream->fmt;
2066 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2068 c->got_key_frame = 0;
2070 /* prepare header and save header data in a stream */
2071 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2072 /* XXX: potential leak */
2075 c->fmt_ctx.pb->is_streamed = 1;
2078 * HACK to avoid mpeg ps muxer to spit many underflow errors
2079 * Default value from FFmpeg
2080 * Try to set it use configuration option
2082 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2083 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2085 av_set_parameters(&c->fmt_ctx, NULL);
2086 if (av_write_header(&c->fmt_ctx) < 0) {
2087 http_log("Error writing output header\n");
2091 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2092 c->buffer_ptr = c->pb_buffer;
2093 c->buffer_end = c->pb_buffer + len;
2095 c->state = HTTPSTATE_SEND_DATA;
2096 c->last_packet_sent = 0;
2098 case HTTPSTATE_SEND_DATA:
2099 /* find a new packet */
2100 /* read a packet from the input stream */
2101 if (c->stream->feed)
2102 ffm_set_write_index(c->fmt_in,
2103 c->stream->feed->feed_write_index,
2104 c->stream->feed->feed_size);
2106 if (c->stream->max_time &&
2107 c->stream->max_time + c->start_time - cur_time < 0)
2108 /* We have timed out */
2109 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2113 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2114 if (c->stream->feed && c->stream->feed->feed_opened) {
2115 /* if coming from feed, it means we reached the end of the
2116 ffm file, so must wait for more data */
2117 c->state = HTTPSTATE_WAIT_FEED;
2118 return 1; /* state changed */
2120 if (c->stream->loop) {
2121 av_close_input_file(c->fmt_in);
2123 if (open_input_stream(c, "") < 0)
2128 /* must send trailer now because eof or error */
2129 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2133 int source_index = pkt.stream_index;
2134 /* update first pts if needed */
2135 if (c->first_pts == AV_NOPTS_VALUE) {
2136 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2137 c->start_time = cur_time;
2139 /* send it to the appropriate stream */
2140 if (c->stream->feed) {
2141 /* if coming from a feed, select the right stream */
2142 if (c->switch_pending) {
2143 c->switch_pending = 0;
2144 for(i=0;i<c->stream->nb_streams;i++) {
2145 if (c->switch_feed_streams[i] == pkt.stream_index)
2146 if (pkt.flags & PKT_FLAG_KEY)
2147 do_switch_stream(c, i);
2148 if (c->switch_feed_streams[i] >= 0)
2149 c->switch_pending = 1;
2152 for(i=0;i<c->stream->nb_streams;i++) {
2153 if (c->feed_streams[i] == pkt.stream_index) {
2154 AVStream *st = c->fmt_in->streams[source_index];
2155 pkt.stream_index = i;
2156 if (pkt.flags & PKT_FLAG_KEY &&
2157 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2158 c->stream->nb_streams == 1))
2159 c->got_key_frame = 1;
2160 if (!c->stream->send_on_key || c->got_key_frame)
2165 AVCodecContext *codec;
2166 AVStream *ist, *ost;
2168 ist = c->fmt_in->streams[source_index];
2169 /* specific handling for RTP: we use several
2170 output stream (one for each RTP
2171 connection). XXX: need more abstract handling */
2172 if (c->is_packetized) {
2173 /* compute send time and duration */
2174 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2175 if (ist->start_time != AV_NOPTS_VALUE)
2176 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2177 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2179 printf("index=%d pts=%0.3f duration=%0.6f\n",
2181 (double)c->cur_pts /
2183 (double)c->cur_frame_duration /
2186 /* find RTP context */
2187 c->packet_stream_index = pkt.stream_index;
2188 ctx = c->rtp_ctx[c->packet_stream_index];
2190 av_free_packet(&pkt);
2193 codec = ctx->streams[0]->codec;
2194 /* only one stream per RTP connection */
2195 pkt.stream_index = 0;
2199 codec = ctx->streams[pkt.stream_index]->codec;
2202 if (c->is_packetized) {
2203 int max_packet_size;
2204 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2205 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2207 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2208 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2210 ret = url_open_dyn_buf(&ctx->pb);
2213 /* XXX: potential leak */
2216 ost = ctx->streams[pkt.stream_index];
2218 ctx->pb->is_streamed = 1;
2219 if (pkt.dts != AV_NOPTS_VALUE)
2220 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2221 if (pkt.pts != AV_NOPTS_VALUE)
2222 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2223 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2224 if (av_write_frame(ctx, &pkt) < 0) {
2225 http_log("Error writing frame to output\n");
2226 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2229 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2230 c->cur_frame_bytes = len;
2231 c->buffer_ptr = c->pb_buffer;
2232 c->buffer_end = c->pb_buffer + len;
2234 codec->frame_number++;
2236 av_free_packet(&pkt);
2240 av_free_packet(&pkt);
2245 case HTTPSTATE_SEND_DATA_TRAILER:
2246 /* last packet test ? */
2247 if (c->last_packet_sent || c->is_packetized)
2250 /* prepare header */
2251 if (url_open_dyn_buf(&ctx->pb) < 0) {
2252 /* XXX: potential leak */
2255 c->fmt_ctx.pb->is_streamed = 1;
2256 av_write_trailer(ctx);
2257 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2258 c->buffer_ptr = c->pb_buffer;
2259 c->buffer_end = c->pb_buffer + len;
2261 c->last_packet_sent = 1;
2267 /* should convert the format at the same time */
2268 /* send data starting at c->buffer_ptr to the output connection
2269 (either UDP or TCP connection) */
2270 static int http_send_data(HTTPContext *c)
2275 if (c->buffer_ptr >= c->buffer_end) {
2276 ret = http_prepare_data(c);
2280 /* state change requested */
2283 if (c->is_packetized) {
2284 /* RTP data output */
2285 len = c->buffer_end - c->buffer_ptr;
2287 /* fail safe - should never happen */
2289 c->buffer_ptr = c->buffer_end;
2292 len = (c->buffer_ptr[0] << 24) |
2293 (c->buffer_ptr[1] << 16) |
2294 (c->buffer_ptr[2] << 8) |
2296 if (len > (c->buffer_end - c->buffer_ptr))
2298 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2299 /* nothing to send yet: we can wait */
2303 c->data_count += len;
2304 update_datarate(&c->datarate, c->data_count);
2306 c->stream->bytes_served += len;
2308 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2309 /* RTP packets are sent inside the RTSP TCP connection */
2311 int interleaved_index, size;
2313 HTTPContext *rtsp_c;
2316 /* if no RTSP connection left, error */
2319 /* if already sending something, then wait. */
2320 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2322 if (url_open_dyn_buf(&pb) < 0)
2324 interleaved_index = c->packet_stream_index * 2;
2325 /* RTCP packets are sent at odd indexes */
2326 if (c->buffer_ptr[1] == 200)
2327 interleaved_index++;
2328 /* write RTSP TCP header */
2330 header[1] = interleaved_index;
2331 header[2] = len >> 8;
2333 put_buffer(pb, header, 4);
2334 /* write RTP packet data */
2336 put_buffer(pb, c->buffer_ptr, len);
2337 size = url_close_dyn_buf(pb, &c->packet_buffer);
2338 /* prepare asynchronous TCP sending */
2339 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2340 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2341 c->buffer_ptr += len;
2343 /* send everything we can NOW */
2344 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2345 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2347 rtsp_c->packet_buffer_ptr += len;
2348 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2349 /* if we could not send all the data, we will
2350 send it later, so a new state is needed to
2351 "lock" the RTSP TCP connection */
2352 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2355 /* all data has been sent */
2356 av_freep(&c->packet_buffer);
2358 /* send RTP packet directly in UDP */
2360 url_write(c->rtp_handles[c->packet_stream_index],
2361 c->buffer_ptr, len);
2362 c->buffer_ptr += len;
2363 /* here we continue as we can send several packets per 10 ms slot */
2366 /* TCP data output */
2367 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2369 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2370 ff_neterrno() != FF_NETERROR(EINTR))
2371 /* error : close connection */
2376 c->buffer_ptr += len;
2378 c->data_count += len;
2379 update_datarate(&c->datarate, c->data_count);
2381 c->stream->bytes_served += len;
2389 static int http_start_receive_data(HTTPContext *c)
2393 if (c->stream->feed_opened)
2396 /* Don't permit writing to this one */
2397 if (c->stream->readonly)
2401 fd = open(c->stream->feed_filename, O_RDWR);
2403 http_log("Error opening feeder file: %s\n", strerror(errno));
2408 c->stream->feed_write_index = ffm_read_write_index(fd);
2409 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2410 lseek(fd, 0, SEEK_SET);
2412 /* init buffer input */
2413 c->buffer_ptr = c->buffer;
2414 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2415 c->stream->feed_opened = 1;
2419 static int http_receive_data(HTTPContext *c)
2423 if (c->buffer_end > c->buffer_ptr) {
2426 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2428 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2429 ff_neterrno() != FF_NETERROR(EINTR))
2430 /* error : close connection */
2432 } else if (len == 0)
2433 /* end of connection : close it */
2436 c->buffer_ptr += len;
2437 c->data_count += len;
2438 update_datarate(&c->datarate, c->data_count);
2442 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2443 if (c->buffer[0] != 'f' ||
2444 c->buffer[1] != 'm') {
2445 http_log("Feed stream has become desynchronized -- disconnecting\n");
2450 if (c->buffer_ptr >= c->buffer_end) {
2451 FFStream *feed = c->stream;
2452 /* a packet has been received : write it in the store, except
2454 if (c->data_count > FFM_PACKET_SIZE) {
2456 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2457 /* XXX: use llseek or url_seek */
2458 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2459 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2460 http_log("Error writing to feed file: %s\n", strerror(errno));
2464 feed->feed_write_index += FFM_PACKET_SIZE;
2465 /* update file size */
2466 if (feed->feed_write_index > c->stream->feed_size)
2467 feed->feed_size = feed->feed_write_index;
2469 /* handle wrap around if max file size reached */
2470 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2471 feed->feed_write_index = FFM_PACKET_SIZE;
2474 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2476 /* wake up any waiting connections */
2477 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2478 if (c1->state == HTTPSTATE_WAIT_FEED &&
2479 c1->stream->feed == c->stream->feed)
2480 c1->state = HTTPSTATE_SEND_DATA;
2483 /* We have a header in our hands that contains useful data */
2484 AVFormatContext *s = NULL;
2486 AVInputFormat *fmt_in;
2489 /* use feed output format name to find corresponding input format */
2490 fmt_in = av_find_input_format(feed->fmt->name);
2494 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2495 pb->is_streamed = 1;
2497 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2502 /* Now we have the actual streams */
2503 if (s->nb_streams != feed->nb_streams) {
2504 av_close_input_stream(s);
2509 for (i = 0; i < s->nb_streams; i++) {
2510 AVStream *fst = feed->streams[i];
2511 AVStream *st = s->streams[i];
2512 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2513 if (fst->codec->extradata_size) {
2514 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2515 if (!fst->codec->extradata)
2517 memcpy(fst->codec->extradata, st->codec->extradata,
2518 fst->codec->extradata_size);
2522 av_close_input_stream(s);
2525 c->buffer_ptr = c->buffer;
2530 c->stream->feed_opened = 0;
2532 /* wake up any waiting connections to stop waiting for feed */
2533 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2534 if (c1->state == HTTPSTATE_WAIT_FEED &&
2535 c1->stream->feed == c->stream->feed)
2536 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2541 /********************************************************************/
2544 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2551 switch(error_number) {
2552 case RTSP_STATUS_OK:
2555 case RTSP_STATUS_METHOD:
2556 str = "Method Not Allowed";
2558 case RTSP_STATUS_BANDWIDTH:
2559 str = "Not Enough Bandwidth";
2561 case RTSP_STATUS_SESSION:
2562 str = "Session Not Found";
2564 case RTSP_STATUS_STATE:
2565 str = "Method Not Valid in This State";
2567 case RTSP_STATUS_AGGREGATE:
2568 str = "Aggregate operation not allowed";
2570 case RTSP_STATUS_ONLY_AGGREGATE:
2571 str = "Only aggregate operation allowed";
2573 case RTSP_STATUS_TRANSPORT:
2574 str = "Unsupported transport";
2576 case RTSP_STATUS_INTERNAL:
2577 str = "Internal Server Error";
2579 case RTSP_STATUS_SERVICE:
2580 str = "Service Unavailable";
2582 case RTSP_STATUS_VERSION:
2583 str = "RTSP Version not supported";
2586 str = "Unknown Error";
2590 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2591 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2593 /* output GMT time */
2597 p = buf2 + strlen(p) - 1;
2600 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2603 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2605 rtsp_reply_header(c, error_number);
2606 url_fprintf(c->pb, "\r\n");
2609 static int rtsp_parse_request(HTTPContext *c)
2611 const char *p, *p1, *p2;
2617 RTSPHeader header1, *header = &header1;
2619 c->buffer_ptr[0] = '\0';
2622 get_word(cmd, sizeof(cmd), &p);
2623 get_word(url, sizeof(url), &p);
2624 get_word(protocol, sizeof(protocol), &p);
2626 av_strlcpy(c->method, cmd, sizeof(c->method));
2627 av_strlcpy(c->url, url, sizeof(c->url));
2628 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2630 if (url_open_dyn_buf(&c->pb) < 0) {
2631 /* XXX: cannot do more */
2632 c->pb = NULL; /* safety */
2636 /* check version name */
2637 if (strcmp(protocol, "RTSP/1.0") != 0) {
2638 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2642 /* parse each header line */
2643 memset(header, 0, sizeof(RTSPHeader));
2644 /* skip to next line */
2645 while (*p != '\n' && *p != '\0')
2649 while (*p != '\0') {
2650 p1 = strchr(p, '\n');
2654 if (p2 > p && p2[-1] == '\r')
2656 /* skip empty line */
2660 if (len > sizeof(line) - 1)
2661 len = sizeof(line) - 1;
2662 memcpy(line, p, len);
2664 rtsp_parse_line(header, line);
2668 /* handle sequence number */
2669 c->seq = header->seq;
2671 if (!strcmp(cmd, "DESCRIBE"))
2672 rtsp_cmd_describe(c, url);
2673 else if (!strcmp(cmd, "OPTIONS"))
2674 rtsp_cmd_options(c, url);
2675 else if (!strcmp(cmd, "SETUP"))
2676 rtsp_cmd_setup(c, url, header);
2677 else if (!strcmp(cmd, "PLAY"))
2678 rtsp_cmd_play(c, url, header);
2679 else if (!strcmp(cmd, "PAUSE"))
2680 rtsp_cmd_pause(c, url, header);
2681 else if (!strcmp(cmd, "TEARDOWN"))
2682 rtsp_cmd_teardown(c, url, header);
2684 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2687 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2688 c->pb = NULL; /* safety */
2690 /* XXX: cannot do more */
2693 c->buffer_ptr = c->pb_buffer;
2694 c->buffer_end = c->pb_buffer + len;
2695 c->state = RTSPSTATE_SEND_REPLY;
2699 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2700 struct in_addr my_ip)
2702 AVFormatContext *avc;
2703 AVStream avs[MAX_STREAMS];
2706 avc = av_alloc_format_context();
2710 if (stream->title[0] != 0) {
2711 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2713 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2715 avc->nb_streams = stream->nb_streams;
2716 if (stream->is_multicast) {
2717 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2718 inet_ntoa(stream->multicast_ip),
2719 stream->multicast_port, stream->multicast_ttl);
2722 for(i = 0; i < stream->nb_streams; i++) {
2723 avc->streams[i] = &avs[i];
2724 avc->streams[i]->codec = stream->streams[i]->codec;
2726 *pbuffer = av_mallocz(2048);
2727 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2730 return strlen(*pbuffer);
2733 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2735 // rtsp_reply_header(c, RTSP_STATUS_OK);
2736 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2737 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2738 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2739 url_fprintf(c->pb, "\r\n");
2742 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2748 int content_length, len;
2749 struct sockaddr_in my_addr;
2751 /* find which url is asked */
2752 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2757 for(stream = first_stream; stream != NULL; stream = stream->next) {
2758 if (!stream->is_feed &&
2759 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2760 !strcmp(path, stream->filename)) {
2764 /* no stream found */
2765 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2769 /* prepare the media description in sdp format */
2771 /* get the host IP */
2772 len = sizeof(my_addr);
2773 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2774 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2775 if (content_length < 0) {
2776 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2779 rtsp_reply_header(c, RTSP_STATUS_OK);
2780 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2781 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2782 url_fprintf(c->pb, "\r\n");
2783 put_buffer(c->pb, content, content_length);
2786 static HTTPContext *find_rtp_session(const char *session_id)
2790 if (session_id[0] == '\0')
2793 for(c = first_http_ctx; c != NULL; c = c->next) {
2794 if (!strcmp(c->session_id, session_id))
2800 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2802 RTSPTransportField *th;
2805 for(i=0;i<h->nb_transports;i++) {
2806 th = &h->transports[i];
2807 if (th->protocol == protocol)
2813 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2817 int stream_index, port;
2822 RTSPTransportField *th;
2823 struct sockaddr_in dest_addr;
2824 RTSPActionServerSetup setup;
2826 /* find which url is asked */
2827 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2832 /* now check each stream */
2833 for(stream = first_stream; stream != NULL; stream = stream->next) {
2834 if (!stream->is_feed &&
2835 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2836 /* accept aggregate filenames only if single stream */
2837 if (!strcmp(path, stream->filename)) {
2838 if (stream->nb_streams != 1) {
2839 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2846 for(stream_index = 0; stream_index < stream->nb_streams;
2848 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2849 stream->filename, stream_index);
2850 if (!strcmp(path, buf))
2855 /* no stream found */
2856 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2860 /* generate session id if needed */
2861 if (h->session_id[0] == '\0')
2862 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2863 av_random(&random_state), av_random(&random_state));
2865 /* find rtp session, and create it if none found */
2866 rtp_c = find_rtp_session(h->session_id);
2868 /* always prefer UDP */
2869 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2871 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2873 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2878 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2881 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2885 /* open input stream */
2886 if (open_input_stream(rtp_c, "") < 0) {
2887 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2892 /* test if stream is OK (test needed because several SETUP needs
2893 to be done for a given file) */
2894 if (rtp_c->stream != stream) {
2895 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2899 /* test if stream is already set up */
2900 if (rtp_c->rtp_ctx[stream_index]) {
2901 rtsp_reply_error(c, RTSP_STATUS_STATE);
2905 /* check transport */
2906 th = find_transport(h, rtp_c->rtp_protocol);
2907 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2908 th->client_port_min <= 0)) {
2909 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2913 /* setup default options */
2914 setup.transport_option[0] = '\0';
2915 dest_addr = rtp_c->from_addr;
2916 dest_addr.sin_port = htons(th->client_port_min);
2919 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2920 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2924 /* now everything is OK, so we can send the connection parameters */
2925 rtsp_reply_header(c, RTSP_STATUS_OK);
2927 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2929 switch(rtp_c->rtp_protocol) {
2930 case RTSP_PROTOCOL_RTP_UDP:
2931 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2932 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2933 "client_port=%d-%d;server_port=%d-%d",
2934 th->client_port_min, th->client_port_min + 1,
2937 case RTSP_PROTOCOL_RTP_TCP:
2938 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2939 stream_index * 2, stream_index * 2 + 1);
2944 if (setup.transport_option[0] != '\0')
2945 url_fprintf(c->pb, ";%s", setup.transport_option);
2946 url_fprintf(c->pb, "\r\n");
2949 url_fprintf(c->pb, "\r\n");
2953 /* find an rtp connection by using the session ID. Check consistency
2955 static HTTPContext *find_rtp_session_with_url(const char *url,
2956 const char *session_id)
2964 rtp_c = find_rtp_session(session_id);
2968 /* find which url is asked */
2969 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2973 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2974 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2975 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2976 rtp_c->stream->filename, s);
2977 if(!strncmp(path, buf, sizeof(buf))) {
2978 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2985 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2989 rtp_c = find_rtp_session_with_url(url, h->session_id);
2991 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2995 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2996 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2997 rtp_c->state != HTTPSTATE_READY) {
2998 rtsp_reply_error(c, RTSP_STATUS_STATE);
3003 /* XXX: seek in stream */
3004 if (h->range_start != AV_NOPTS_VALUE) {
3005 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3006 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3010 rtp_c->state = HTTPSTATE_SEND_DATA;
3012 /* now everything is OK, so we can send the connection parameters */
3013 rtsp_reply_header(c, RTSP_STATUS_OK);
3015 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3016 url_fprintf(c->pb, "\r\n");
3019 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3023 rtp_c = find_rtp_session_with_url(url, h->session_id);
3025 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3029 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3030 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3031 rtsp_reply_error(c, RTSP_STATUS_STATE);
3035 rtp_c->state = HTTPSTATE_READY;
3036 rtp_c->first_pts = AV_NOPTS_VALUE;
3037 /* now everything is OK, so we can send the connection parameters */
3038 rtsp_reply_header(c, RTSP_STATUS_OK);
3040 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3041 url_fprintf(c->pb, "\r\n");
3044 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3047 char session_id[32];
3049 rtp_c = find_rtp_session_with_url(url, h->session_id);
3051 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3055 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3057 /* abort the session */
3058 close_connection(rtp_c);
3060 /* now everything is OK, so we can send the connection parameters */
3061 rtsp_reply_header(c, RTSP_STATUS_OK);
3063 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3064 url_fprintf(c->pb, "\r\n");
3068 /********************************************************************/
3071 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3072 FFStream *stream, const char *session_id,
3073 enum RTSPProtocol rtp_protocol)
3075 HTTPContext *c = NULL;
3076 const char *proto_str;
3078 /* XXX: should output a warning page when coming
3079 close to the connection limit */
3080 if (nb_connections >= nb_max_connections)
3083 /* add a new connection */
3084 c = av_mallocz(sizeof(HTTPContext));
3089 c->poll_entry = NULL;
3090 c->from_addr = *from_addr;
3091 c->buffer_size = IOBUFFER_INIT_SIZE;
3092 c->buffer = av_malloc(c->buffer_size);
3097 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3098 c->state = HTTPSTATE_READY;
3099 c->is_packetized = 1;
3100 c->rtp_protocol = rtp_protocol;
3102 /* protocol is shown in statistics */
3103 switch(c->rtp_protocol) {
3104 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3105 proto_str = "MCAST";
3107 case RTSP_PROTOCOL_RTP_UDP:
3110 case RTSP_PROTOCOL_RTP_TCP:
3117 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3118 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3120 current_bandwidth += stream->bandwidth;
3122 c->next = first_http_ctx;
3134 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3135 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3137 static int rtp_new_av_stream(HTTPContext *c,
3138 int stream_index, struct sockaddr_in *dest_addr,
3139 HTTPContext *rtsp_c)
3141 AVFormatContext *ctx;
3144 URLContext *h = NULL;
3146 int max_packet_size;
3148 /* now we can open the relevant output stream */
3149 ctx = av_alloc_format_context();
3152 ctx->oformat = guess_format("rtp", NULL, NULL);
3154 st = av_mallocz(sizeof(AVStream));
3157 st->codec= avcodec_alloc_context();
3158 ctx->nb_streams = 1;
3159 ctx->streams[0] = st;
3161 if (!c->stream->feed ||
3162 c->stream->feed == c->stream)
3163 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3166 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3168 st->priv_data = NULL;
3170 /* build destination RTP address */
3171 ipaddr = inet_ntoa(dest_addr->sin_addr);
3173 switch(c->rtp_protocol) {
3174 case RTSP_PROTOCOL_RTP_UDP:
3175 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3178 /* XXX: also pass as parameter to function ? */
3179 if (c->stream->is_multicast) {
3181 ttl = c->stream->multicast_ttl;
3184 snprintf(ctx->filename, sizeof(ctx->filename),
3185 "rtp://%s:%d?multicast=1&ttl=%d",
3186 ipaddr, ntohs(dest_addr->sin_port), ttl);
3188 snprintf(ctx->filename, sizeof(ctx->filename),
3189 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3192 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3194 c->rtp_handles[stream_index] = h;
3195 max_packet_size = url_get_max_packet_size(h);
3197 case RTSP_PROTOCOL_RTP_TCP:
3200 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3206 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3207 ipaddr, ntohs(dest_addr->sin_port),
3208 c->stream->filename, stream_index, c->protocol);
3210 /* normally, no packets should be output here, but the packet size may be checked */
3211 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3212 /* XXX: close stream */
3215 av_set_parameters(ctx, NULL);
3216 if (av_write_header(ctx) < 0) {
3223 url_close_dyn_buf(ctx->pb, &dummy_buf);
3226 c->rtp_ctx[stream_index] = ctx;
3230 /********************************************************************/
3231 /* ffserver initialization */
3233 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3237 fst = av_mallocz(sizeof(AVStream));
3240 fst->codec= avcodec_alloc_context();
3241 fst->priv_data = av_mallocz(sizeof(FeedData));
3242 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3243 fst->index = stream->nb_streams;
3244 av_set_pts_info(fst, 33, 1, 90000);
3245 stream->streams[stream->nb_streams++] = fst;
3249 /* return the stream number in the feed */
3250 static int add_av_stream(FFStream *feed, AVStream *st)
3253 AVCodecContext *av, *av1;
3257 for(i=0;i<feed->nb_streams;i++) {
3258 st = feed->streams[i];
3260 if (av1->codec_id == av->codec_id &&
3261 av1->codec_type == av->codec_type &&
3262 av1->bit_rate == av->bit_rate) {
3264 switch(av->codec_type) {
3265 case CODEC_TYPE_AUDIO:
3266 if (av1->channels == av->channels &&
3267 av1->sample_rate == av->sample_rate)
3270 case CODEC_TYPE_VIDEO:
3271 if (av1->width == av->width &&
3272 av1->height == av->height &&
3273 av1->time_base.den == av->time_base.den &&
3274 av1->time_base.num == av->time_base.num &&
3275 av1->gop_size == av->gop_size)
3284 fst = add_av_stream1(feed, av);
3287 return feed->nb_streams - 1;
3292 static void remove_stream(FFStream *stream)
3296 while (*ps != NULL) {
3304 /* specific mpeg4 handling : we extract the raw parameters */
3305 static void extract_mpeg4_header(AVFormatContext *infile)
3307 int mpeg4_count, i, size;
3313 for(i=0;i<infile->nb_streams;i++) {
3314 st = infile->streams[i];
3315 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3316 st->codec->extradata_size == 0) {
3323 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3324 while (mpeg4_count > 0) {
3325 if (av_read_packet(infile, &pkt) < 0)
3327 st = infile->streams[pkt.stream_index];
3328 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3329 st->codec->extradata_size == 0) {
3330 av_freep(&st->codec->extradata);
3331 /* fill extradata with the header */
3332 /* XXX: we make hard suppositions here ! */
3334 while (p < pkt.data + pkt.size - 4) {
3335 /* stop when vop header is found */
3336 if (p[0] == 0x00 && p[1] == 0x00 &&
3337 p[2] == 0x01 && p[3] == 0xb6) {
3338 size = p - pkt.data;
3339 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3340 st->codec->extradata = av_malloc(size);
3341 st->codec->extradata_size = size;
3342 memcpy(st->codec->extradata, pkt.data, size);
3349 av_free_packet(&pkt);
3353 /* compute the needed AVStream for each file */
3354 static void build_file_streams(void)
3356 FFStream *stream, *stream_next;
3357 AVFormatContext *infile;
3360 /* gather all streams */
3361 for(stream = first_stream; stream != NULL; stream = stream_next) {
3362 stream_next = stream->next;
3363 if (stream->stream_type == STREAM_TYPE_LIVE &&
3365 /* the stream comes from a file */
3366 /* try to open the file */
3368 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3369 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3370 /* specific case : if transport stream output to RTP,
3371 we use a raw transport stream reader */
3372 stream->ap_in->mpeg2ts_raw = 1;
3373 stream->ap_in->mpeg2ts_compute_pcr = 1;
3376 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3377 stream->ifmt, 0, stream->ap_in)) < 0) {
3378 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3379 /* remove stream (no need to spend more time on it) */
3381 remove_stream(stream);
3383 /* find all the AVStreams inside and reference them in
3385 if (av_find_stream_info(infile) < 0) {
3386 http_log("Could not find codec parameters from '%s'\n",
3387 stream->feed_filename);
3388 av_close_input_file(infile);
3391 extract_mpeg4_header(infile);
3393 for(i=0;i<infile->nb_streams;i++)
3394 add_av_stream1(stream, infile->streams[i]->codec);
3396 av_close_input_file(infile);
3402 /* compute the needed AVStream for each feed */
3403 static void build_feed_streams(void)
3405 FFStream *stream, *feed;
3408 /* gather all streams */
3409 for(stream = first_stream; stream != NULL; stream = stream->next) {
3410 feed = stream->feed;
3412 if (!stream->is_feed) {
3413 /* we handle a stream coming from a feed */
3414 for(i=0;i<stream->nb_streams;i++)
3415 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3420 /* gather all streams */
3421 for(stream = first_stream; stream != NULL; stream = stream->next) {
3422 feed = stream->feed;
3424 if (stream->is_feed) {
3425 for(i=0;i<stream->nb_streams;i++)
3426 stream->feed_streams[i] = i;
3431 /* create feed files if needed */
3432 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3435 if (url_exist(feed->feed_filename)) {
3436 /* See if it matches */
3440 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3441 /* Now see if it matches */
3442 if (s->nb_streams == feed->nb_streams) {
3444 for(i=0;i<s->nb_streams;i++) {
3446 sf = feed->streams[i];
3449 if (sf->index != ss->index ||
3451 printf("Index & Id do not match for stream %d (%s)\n",
3452 i, feed->feed_filename);
3455 AVCodecContext *ccf, *ccs;
3459 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3461 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3462 printf("Codecs do not match for stream %d\n", i);
3464 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3465 printf("Codec bitrates do not match for stream %d\n", i);
3467 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3468 if (CHECK_CODEC(time_base.den) ||
3469 CHECK_CODEC(time_base.num) ||
3470 CHECK_CODEC(width) ||
3471 CHECK_CODEC(height)) {
3472 printf("Codec width, height and framerate do not match for stream %d\n", i);
3475 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3476 if (CHECK_CODEC(sample_rate) ||
3477 CHECK_CODEC(channels) ||
3478 CHECK_CODEC(frame_size)) {
3479 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3483 printf("Unknown codec type\n");
3491 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3492 feed->feed_filename, s->nb_streams, feed->nb_streams);
3494 av_close_input_file(s);
3496 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3497 feed->feed_filename);
3500 if (feed->readonly) {
3501 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3502 feed->feed_filename);
3505 unlink(feed->feed_filename);
3508 if (!url_exist(feed->feed_filename)) {
3509 AVFormatContext s1, *s = &s1;
3511 if (feed->readonly) {
3512 printf("Unable to create feed file '%s' as it is marked readonly\n",
3513 feed->feed_filename);
3517 /* only write the header of the ffm file */
3518 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3519 http_log("Could not open output feed file '%s'\n",
3520 feed->feed_filename);
3523 s->oformat = feed->fmt;
3524 s->nb_streams = feed->nb_streams;
3525 for(i=0;i<s->nb_streams;i++) {
3527 st = feed->streams[i];
3530 av_set_parameters(s, NULL);
3531 if (av_write_header(s) < 0) {
3532 http_log("Container doesn't supports the required parameters\n");
3535 /* XXX: need better api */
3536 av_freep(&s->priv_data);
3539 /* get feed size and write index */
3540 fd = open(feed->feed_filename, O_RDONLY);
3542 http_log("Could not open output feed file '%s'\n",
3543 feed->feed_filename);
3547 feed->feed_write_index = ffm_read_write_index(fd);
3548 feed->feed_size = lseek(fd, 0, SEEK_END);
3549 /* ensure that we do not wrap before the end of file */
3550 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3551 feed->feed_max_size = feed->feed_size;
3557 /* compute the bandwidth used by each stream */
3558 static void compute_bandwidth(void)
3564 for(stream = first_stream; stream != NULL; stream = stream->next) {
3566 for(i=0;i<stream->nb_streams;i++) {
3567 AVStream *st = stream->streams[i];
3568 switch(st->codec->codec_type) {
3569 case CODEC_TYPE_AUDIO:
3570 case CODEC_TYPE_VIDEO:
3571 bandwidth += st->codec->bit_rate;
3577 stream->bandwidth = (bandwidth + 999) / 1000;
3581 static void get_arg(char *buf, int buf_size, const char **pp)
3588 while (isspace(*p)) p++;
3591 if (*p == '\"' || *p == '\'')
3603 if ((q - buf) < buf_size - 1)
3608 if (quote && *p == quote)
3613 /* add a codec and set the default parameters */
3614 static void add_codec(FFStream *stream, AVCodecContext *av)
3618 /* compute default parameters */
3619 switch(av->codec_type) {
3620 case CODEC_TYPE_AUDIO:
3621 if (av->bit_rate == 0)
3622 av->bit_rate = 64000;
3623 if (av->sample_rate == 0)
3624 av->sample_rate = 22050;
3625 if (av->channels == 0)
3628 case CODEC_TYPE_VIDEO:
3629 if (av->bit_rate == 0)
3630 av->bit_rate = 64000;
3631 if (av->time_base.num == 0){
3632 av->time_base.den = 5;
3633 av->time_base.num = 1;
3635 if (av->width == 0 || av->height == 0) {
3639 /* Bitrate tolerance is less for streaming */
3640 if (av->bit_rate_tolerance == 0)
3641 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3642 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3647 if (av->max_qdiff == 0)
3649 av->qcompress = 0.5;
3652 if (!av->nsse_weight)
3653 av->nsse_weight = 8;
3655 av->frame_skip_cmp = FF_CMP_DCTMAX;
3656 av->me_method = ME_EPZS;
3657 av->rc_buffer_aggressivity = 1.0;
3660 av->rc_eq = "tex^qComp";
3661 if (!av->i_quant_factor)
3662 av->i_quant_factor = -0.8;
3663 if (!av->b_quant_factor)
3664 av->b_quant_factor = 1.25;
3665 if (!av->b_quant_offset)
3666 av->b_quant_offset = 1.25;
3667 if (!av->rc_max_rate)
3668 av->rc_max_rate = av->bit_rate * 2;
3670 if (av->rc_max_rate && !av->rc_buffer_size) {
3671 av->rc_buffer_size = av->rc_max_rate;
3680 st = av_mallocz(sizeof(AVStream));
3683 st->codec = avcodec_alloc_context();
3684 stream->streams[stream->nb_streams++] = st;
3685 memcpy(st->codec, av, sizeof(AVCodecContext));
3688 static int opt_audio_codec(const char *arg)
3690 AVCodec *p= avcodec_find_encoder_by_name(arg);
3692 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3693 return CODEC_ID_NONE;
3698 static int opt_video_codec(const char *arg)
3700 AVCodec *p= avcodec_find_encoder_by_name(arg);
3702 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3703 return CODEC_ID_NONE;
3708 /* simplistic plugin support */
3711 static void load_module(const char *filename)
3714 void (*init_func)(void);
3715 dll = dlopen(filename, RTLD_NOW);
3717 fprintf(stderr, "Could not load module '%s' - %s\n",
3718 filename, dlerror());
3722 init_func = dlsym(dll, "ffserver_module_init");
3725 "%s: init function 'ffserver_module_init()' not found\n",
3734 static int opt_default(const char *opt, const char *arg,
3735 AVCodecContext *avctx, int type)
3737 const AVOption *o = NULL;
3738 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3740 o = av_set_string(avctx, opt, arg);
3746 static int parse_ffconfig(const char *filename)
3753 int val, errors, line_num;
3754 FFStream **last_stream, *stream, *redirect;
3755 FFStream **last_feed, *feed;
3756 AVCodecContext audio_enc, video_enc;
3757 int audio_id, video_id;
3759 f = fopen(filename, "r");
3767 first_stream = NULL;
3768 last_stream = &first_stream;
3770 last_feed = &first_feed;
3774 audio_id = CODEC_ID_NONE;
3775 video_id = CODEC_ID_NONE;
3777 if (fgets(line, sizeof(line), f) == NULL)
3783 if (*p == '\0' || *p == '#')
3786 get_arg(cmd, sizeof(cmd), &p);
3788 if (!strcasecmp(cmd, "Port")) {
3789 get_arg(arg, sizeof(arg), &p);
3791 if (val < 1 || val > 65536) {
3792 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3793 filename, line_num, arg);
3796 my_http_addr.sin_port = htons(val);
3797 } else if (!strcasecmp(cmd, "BindAddress")) {
3798 get_arg(arg, sizeof(arg), &p);
3799 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3800 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3801 filename, line_num, arg);
3804 } else if (!strcasecmp(cmd, "NoDaemon")) {
3805 ffserver_daemon = 0;
3806 } else if (!strcasecmp(cmd, "RTSPPort")) {
3807 get_arg(arg, sizeof(arg), &p);
3809 if (val < 1 || val > 65536) {
3810 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3811 filename, line_num, arg);
3814 my_rtsp_addr.sin_port = htons(atoi(arg));
3815 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3816 get_arg(arg, sizeof(arg), &p);
3817 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3818 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3819 filename, line_num, arg);
3822 } else if (!strcasecmp(cmd, "MaxClients")) {
3823 get_arg(arg, sizeof(arg), &p);
3825 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3826 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3827 filename, line_num, arg);
3830 nb_max_connections = val;
3832 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3834 get_arg(arg, sizeof(arg), &p);
3836 if (llval < 10 || llval > 10000000) {
3837 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3838 filename, line_num, arg);
3841 max_bandwidth = llval;
3842 } else if (!strcasecmp(cmd, "CustomLog")) {
3843 if (!ffserver_debug)
3844 get_arg(logfilename, sizeof(logfilename), &p);
3845 } else if (!strcasecmp(cmd, "<Feed")) {
3846 /*********************************************/
3847 /* Feed related options */
3849 if (stream || feed) {
3850 fprintf(stderr, "%s:%d: Already in a tag\n",
3851 filename, line_num);
3853 feed = av_mallocz(sizeof(FFStream));
3854 /* add in stream list */
3855 *last_stream = feed;
3856 last_stream = &feed->next;
3857 /* add in feed list */
3859 last_feed = &feed->next_feed;
3861 get_arg(feed->filename, sizeof(feed->filename), &p);
3862 q = strrchr(feed->filename, '>');
3865 feed->fmt = guess_format("ffm", NULL, NULL);
3866 /* defaut feed file */
3867 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3868 "/tmp/%s.ffm", feed->filename);
3869 feed->feed_max_size = 5 * 1024 * 1024;
3871 feed->feed = feed; /* self feeding :-) */
3873 } else if (!strcasecmp(cmd, "Launch")) {
3877 feed->child_argv = av_mallocz(64 * sizeof(char *));
3879 for (i = 0; i < 62; i++) {
3880 get_arg(arg, sizeof(arg), &p);
3884 feed->child_argv[i] = av_strdup(arg);
3887 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3889 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3891 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3892 inet_ntoa(my_http_addr.sin_addr),
3893 ntohs(my_http_addr.sin_port), feed->filename);
3895 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3897 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3899 } else if (stream) {
3900 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3902 } else if (!strcasecmp(cmd, "File")) {
3904 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3906 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3907 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3912 get_arg(arg, sizeof(arg), &p);
3914 fsize = strtod(p1, &p1);
3915 switch(toupper(*p1)) {
3920 fsize *= 1024 * 1024;
3923 fsize *= 1024 * 1024 * 1024;
3926 feed->feed_max_size = (int64_t)fsize;
3928 } else if (!strcasecmp(cmd, "</Feed>")) {
3930 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3931 filename, line_num);
3935 } else if (!strcasecmp(cmd, "<Stream")) {
3936 /*********************************************/
3937 /* Stream related options */
3939 if (stream || feed) {
3940 fprintf(stderr, "%s:%d: Already in a tag\n",
3941 filename, line_num);
3943 const AVClass *class;
3944 stream = av_mallocz(sizeof(FFStream));
3945 *last_stream = stream;
3946 last_stream = &stream->next;
3948 get_arg(stream->filename, sizeof(stream->filename), &p);
3949 q = strrchr(stream->filename, '>');
3952 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3953 /* fetch avclass so AVOption works
3954 * FIXME try to use avcodec_get_context_defaults2
3955 * without changing defaults too much */
3956 avcodec_get_context_defaults(&video_enc);
3957 class = video_enc.av_class;
3958 memset(&audio_enc, 0, sizeof(AVCodecContext));
3959 memset(&video_enc, 0, sizeof(AVCodecContext));
3960 audio_enc.av_class = class;
3961 video_enc.av_class = class;
3962 audio_id = CODEC_ID_NONE;
3963 video_id = CODEC_ID_NONE;
3965 audio_id = stream->fmt->audio_codec;
3966 video_id = stream->fmt->video_codec;
3969 } else if (!strcasecmp(cmd, "Feed")) {
3970 get_arg(arg, sizeof(arg), &p);
3975 while (sfeed != NULL) {
3976 if (!strcmp(sfeed->filename, arg))
3978 sfeed = sfeed->next_feed;
3981 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3982 filename, line_num, arg);
3984 stream->feed = sfeed;
3986 } else if (!strcasecmp(cmd, "Format")) {
3987 get_arg(arg, sizeof(arg), &p);
3989 if (!strcmp(arg, "status")) {
3990 stream->stream_type = STREAM_TYPE_STATUS;
3993 stream->stream_type = STREAM_TYPE_LIVE;
3994 /* jpeg cannot be used here, so use single frame jpeg */
3995 if (!strcmp(arg, "jpeg"))
3996 strcpy(arg, "mjpeg");
3997 stream->fmt = guess_stream_format(arg, NULL, NULL);
3999 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4000 filename, line_num, arg);
4005 audio_id = stream->fmt->audio_codec;
4006 video_id = stream->fmt->video_codec;
4009 } else if (!strcasecmp(cmd, "InputFormat")) {
4010 get_arg(arg, sizeof(arg), &p);
4011 stream->ifmt = av_find_input_format(arg);
4012 if (!stream->ifmt) {
4013 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4014 filename, line_num, arg);
4016 } else if (!strcasecmp(cmd, "FaviconURL")) {
4017 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4018 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4020 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4021 filename, line_num);
4024 } else if (!strcasecmp(cmd, "Author")) {
4026 get_arg(stream->author, sizeof(stream->author), &p);
4027 } else if (!strcasecmp(cmd, "Comment")) {
4029 get_arg(stream->comment, sizeof(stream->comment), &p);
4030 } else if (!strcasecmp(cmd, "Copyright")) {
4032 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4033 } else if (!strcasecmp(cmd, "Title")) {
4035 get_arg(stream->title, sizeof(stream->title), &p);
4036 } else if (!strcasecmp(cmd, "Preroll")) {
4037 get_arg(arg, sizeof(arg), &p);
4039 stream->prebuffer = atof(arg) * 1000;
4040 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4042 stream->send_on_key = 1;
4043 } else if (!strcasecmp(cmd, "AudioCodec")) {
4044 get_arg(arg, sizeof(arg), &p);
4045 audio_id = opt_audio_codec(arg);
4046 if (audio_id == CODEC_ID_NONE) {
4047 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4048 filename, line_num, arg);
4051 } else if (!strcasecmp(cmd, "VideoCodec")) {
4052 get_arg(arg, sizeof(arg), &p);
4053 video_id = opt_video_codec(arg);
4054 if (video_id == CODEC_ID_NONE) {
4055 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4056 filename, line_num, arg);
4059 } else if (!strcasecmp(cmd, "MaxTime")) {
4060 get_arg(arg, sizeof(arg), &p);
4062 stream->max_time = atof(arg) * 1000;
4063 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4064 get_arg(arg, sizeof(arg), &p);
4066 audio_enc.bit_rate = atoi(arg) * 1000;
4067 } else if (!strcasecmp(cmd, "AudioChannels")) {
4068 get_arg(arg, sizeof(arg), &p);
4070 audio_enc.channels = atoi(arg);
4071 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4072 get_arg(arg, sizeof(arg), &p);
4074 audio_enc.sample_rate = atoi(arg);
4075 } else if (!strcasecmp(cmd, "AudioQuality")) {
4076 get_arg(arg, sizeof(arg), &p);
4078 // audio_enc.quality = atof(arg) * 1000;
4080 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4082 int minrate, maxrate;
4084 get_arg(arg, sizeof(arg), &p);
4086 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4087 video_enc.rc_min_rate = minrate * 1000;
4088 video_enc.rc_max_rate = maxrate * 1000;
4090 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4091 filename, line_num, arg);
4095 } else if (!strcasecmp(cmd, "Debug")) {
4097 get_arg(arg, sizeof(arg), &p);
4098 video_enc.debug = strtol(arg,0,0);
4100 } else if (!strcasecmp(cmd, "Strict")) {
4102 get_arg(arg, sizeof(arg), &p);
4103 video_enc.strict_std_compliance = atoi(arg);
4105 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4107 get_arg(arg, sizeof(arg), &p);
4108 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4110 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4112 get_arg(arg, sizeof(arg), &p);
4113 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4115 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4116 get_arg(arg, sizeof(arg), &p);
4118 video_enc.bit_rate = atoi(arg) * 1000;
4120 } else if (!strcasecmp(cmd, "VideoSize")) {
4121 get_arg(arg, sizeof(arg), &p);
4123 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4124 if ((video_enc.width % 16) != 0 ||
4125 (video_enc.height % 16) != 0) {
4126 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4127 filename, line_num);
4131 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4132 get_arg(arg, sizeof(arg), &p);
4134 AVRational frame_rate;
4135 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4136 fprintf(stderr, "Incorrect frame rate\n");
4139 video_enc.time_base.num = frame_rate.den;
4140 video_enc.time_base.den = frame_rate.num;
4143 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4144 get_arg(arg, sizeof(arg), &p);
4146 video_enc.gop_size = atoi(arg);
4147 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4149 video_enc.gop_size = 1;
4150 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4152 video_enc.mb_decision = FF_MB_DECISION_BITS;
4153 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4155 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4156 video_enc.flags |= CODEC_FLAG_4MV;
4158 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4159 !strcasecmp(cmd, "AVOptionAudio")) {
4161 AVCodecContext *avctx;
4163 get_arg(arg, sizeof(arg), &p);
4164 get_arg(arg2, sizeof(arg2), &p);
4165 if (!strcasecmp(cmd, "AVOptionVideo")) {
4167 type = AV_OPT_FLAG_VIDEO_PARAM;
4170 type = AV_OPT_FLAG_AUDIO_PARAM;
4172 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4173 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4176 } else if (!strcasecmp(cmd, "VideoTag")) {
4177 get_arg(arg, sizeof(arg), &p);
4178 if ((strlen(arg) == 4) && stream)
4179 video_enc.codec_tag = ff_get_fourcc(arg);
4180 } else if (!strcasecmp(cmd, "BitExact")) {
4182 video_enc.flags |= CODEC_FLAG_BITEXACT;
4183 } else if (!strcasecmp(cmd, "DctFastint")) {
4185 video_enc.dct_algo = FF_DCT_FASTINT;
4186 } else if (!strcasecmp(cmd, "IdctSimple")) {
4188 video_enc.idct_algo = FF_IDCT_SIMPLE;
4189 } else if (!strcasecmp(cmd, "Qscale")) {
4190 get_arg(arg, sizeof(arg), &p);
4192 video_enc.flags |= CODEC_FLAG_QSCALE;
4193 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4195 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4196 get_arg(arg, sizeof(arg), &p);
4198 video_enc.max_qdiff = atoi(arg);
4199 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4200 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4201 filename, line_num);
4205 } else if (!strcasecmp(cmd, "VideoQMax")) {
4206 get_arg(arg, sizeof(arg), &p);
4208 video_enc.qmax = atoi(arg);
4209 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4210 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4211 filename, line_num);
4215 } else if (!strcasecmp(cmd, "VideoQMin")) {
4216 get_arg(arg, sizeof(arg), &p);
4218 video_enc.qmin = atoi(arg);
4219 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4220 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4221 filename, line_num);
4225 } else if (!strcasecmp(cmd, "LumaElim")) {
4226 get_arg(arg, sizeof(arg), &p);
4228 video_enc.luma_elim_threshold = atoi(arg);
4229 } else if (!strcasecmp(cmd, "ChromaElim")) {
4230 get_arg(arg, sizeof(arg), &p);
4232 video_enc.chroma_elim_threshold = atoi(arg);
4233 } else if (!strcasecmp(cmd, "LumiMask")) {
4234 get_arg(arg, sizeof(arg), &p);
4236 video_enc.lumi_masking = atof(arg);
4237 } else if (!strcasecmp(cmd, "DarkMask")) {
4238 get_arg(arg, sizeof(arg), &p);
4240 video_enc.dark_masking = atof(arg);
4241 } else if (!strcasecmp(cmd, "NoVideo")) {
4242 video_id = CODEC_ID_NONE;
4243 } else if (!strcasecmp(cmd, "NoAudio")) {
4244 audio_id = CODEC_ID_NONE;
4245 } else if (!strcasecmp(cmd, "ACL")) {
4248 get_arg(arg, sizeof(arg), &p);
4249 if (strcasecmp(arg, "allow") == 0)
4250 acl.action = IP_ALLOW;
4251 else if (strcasecmp(arg, "deny") == 0)
4252 acl.action = IP_DENY;
4254 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4255 filename, line_num, arg);
4259 get_arg(arg, sizeof(arg), &p);
4261 if (resolve_host(&acl.first, arg) != 0) {
4262 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4263 filename, line_num, arg);
4266 acl.last = acl.first;
4268 get_arg(arg, sizeof(arg), &p);
4271 if (resolve_host(&acl.last, arg) != 0) {
4272 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4273 filename, line_num, arg);
4279 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4280 IPAddressACL **naclp = 0;
4286 naclp = &stream->acl;
4290 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4291 filename, line_num);
4297 naclp = &(*naclp)->next;
4302 } else if (!strcasecmp(cmd, "RTSPOption")) {
4303 get_arg(arg, sizeof(arg), &p);
4305 av_freep(&stream->rtsp_option);
4306 stream->rtsp_option = av_strdup(arg);
4308 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4309 get_arg(arg, sizeof(arg), &p);
4311 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4312 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4313 filename, line_num, arg);
4316 stream->is_multicast = 1;
4317 stream->loop = 1; /* default is looping */
4319 } else if (!strcasecmp(cmd, "MulticastPort")) {
4320 get_arg(arg, sizeof(arg), &p);
4322 stream->multicast_port = atoi(arg);
4323 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4324 get_arg(arg, sizeof(arg), &p);
4326 stream->multicast_ttl = atoi(arg);
4327 } else if (!strcasecmp(cmd, "NoLoop")) {
4330 } else if (!strcasecmp(cmd, "</Stream>")) {
4332 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4333 filename, line_num);
4336 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4337 if (audio_id != CODEC_ID_NONE) {
4338 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4339 audio_enc.codec_id = audio_id;
4340 add_codec(stream, &audio_enc);
4342 if (video_id != CODEC_ID_NONE) {
4343 video_enc.codec_type = CODEC_TYPE_VIDEO;
4344 video_enc.codec_id = video_id;
4345 add_codec(stream, &video_enc);
4350 } else if (!strcasecmp(cmd, "<Redirect")) {
4351 /*********************************************/
4353 if (stream || feed || redirect) {
4354 fprintf(stderr, "%s:%d: Already in a tag\n",
4355 filename, line_num);
4358 redirect = av_mallocz(sizeof(FFStream));
4359 *last_stream = redirect;
4360 last_stream = &redirect->next;
4362 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4363 q = strrchr(redirect->filename, '>');
4366 redirect->stream_type = STREAM_TYPE_REDIRECT;
4368 } else if (!strcasecmp(cmd, "URL")) {
4370 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4371 } else if (!strcasecmp(cmd, "</Redirect>")) {
4373 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4374 filename, line_num);
4377 if (!redirect->feed_filename[0]) {
4378 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4379 filename, line_num);
4384 } else if (!strcasecmp(cmd, "LoadModule")) {
4385 get_arg(arg, sizeof(arg), &p);
4389 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4390 filename, line_num, arg);
4394 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4395 filename, line_num, cmd);
4407 static void handle_child_exit(int sig)
4412 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4415 for (feed = first_feed; feed; feed = feed->next) {
4416 if (feed->pid == pid) {
4417 int uptime = time(0) - feed->pid_start;
4420 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4423 /* Turn off any more restarts */
4424 feed->child_argv = 0;
4429 need_to_start_children = 1;
4432 static void opt_debug()
4435 ffserver_daemon = 0;
4436 logfilename[0] = '-';
4439 static void opt_show_help(void)
4441 printf("usage: ffserver [options]\n"
4442 "Hyper fast multi format Audio/Video streaming server\n");
4444 show_help_options(options, "Main options:\n", 0, 0);
4447 static const OptionDef options[] = {
4448 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4449 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4450 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4451 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4452 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4453 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4454 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4458 int main(int argc, char **argv)
4460 struct sigaction sigact;
4466 config_filename = "/etc/ffserver.conf";
4468 my_program_name = argv[0];
4469 my_program_dir = getcwd(0, 0);
4470 ffserver_daemon = 1;
4472 parse_options(argc, argv, options, NULL);
4474 unsetenv("http_proxy"); /* Kill the http_proxy */
4476 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4478 memset(&sigact, 0, sizeof(sigact));
4479 sigact.sa_handler = handle_child_exit;
4480 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4481 sigaction(SIGCHLD, &sigact, 0);
4483 if (parse_ffconfig(config_filename) < 0) {
4484 fprintf(stderr, "Incorrect config file - exiting.\n");
4488 build_file_streams();
4490 build_feed_streams();
4492 compute_bandwidth();
4494 /* put the process in background and detach it from its TTY */
4495 if (ffserver_daemon) {
4502 } else if (pid > 0) {
4509 open("/dev/null", O_RDWR);
4510 if (strcmp(logfilename, "-") != 0) {
4520 signal(SIGPIPE, SIG_IGN);
4522 /* open log file if needed */
4523 if (logfilename[0] != '\0') {
4524 if (!strcmp(logfilename, "-"))
4527 logfile = fopen(logfilename, "a");
4528 av_log_set_callback(http_av_log);
4531 if (ffserver_daemon)
4534 if (http_server() < 0) {
4535 http_log("Could not start server\n");