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 http_log("Launch commandline: ");
420 http_log("%s ", pathname);
421 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
422 http_log("%s ", feed->child_argv[i]);
425 for (i = 3; i < 256; i++)
428 if (!ffserver_debug) {
429 i = open("/dev/null", O_RDWR);
438 /* This is needed to make relative pathnames work */
439 chdir(my_program_dir);
441 signal(SIGPIPE, SIG_DFL);
443 execvp(pathname, feed->child_argv);
451 /* open a listening socket */
452 static int socket_open_listen(struct sockaddr_in *my_addr)
456 server_fd = socket(AF_INET,SOCK_STREAM,0);
463 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
465 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
467 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
469 closesocket(server_fd);
473 if (listen (server_fd, 5) < 0) {
475 closesocket(server_fd);
478 ff_socket_nonblock(server_fd, 1);
483 /* start all multicast streams */
484 static void start_multicast(void)
489 struct sockaddr_in dest_addr;
490 int default_port, stream_index;
493 for(stream = first_stream; stream != NULL; stream = stream->next) {
494 if (stream->is_multicast) {
495 /* open the RTP connection */
496 snprintf(session_id, sizeof(session_id), "%08x%08x",
497 av_random(&random_state), av_random(&random_state));
499 /* choose a port if none given */
500 if (stream->multicast_port == 0) {
501 stream->multicast_port = default_port;
505 dest_addr.sin_family = AF_INET;
506 dest_addr.sin_addr = stream->multicast_ip;
507 dest_addr.sin_port = htons(stream->multicast_port);
509 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
510 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
514 if (open_input_stream(rtp_c, "") < 0) {
515 http_log("Could not open input stream for stream '%s'\n",
520 /* open each RTP stream */
521 for(stream_index = 0; stream_index < stream->nb_streams;
523 dest_addr.sin_port = htons(stream->multicast_port +
525 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
526 http_log("Could not open output stream '%s/streamid=%d'\n",
527 stream->filename, stream_index);
532 /* change state to send data */
533 rtp_c->state = HTTPSTATE_SEND_DATA;
538 /* main loop of the http server */
539 static int http_server(void)
541 int server_fd = 0, rtsp_server_fd = 0;
542 int ret, delay, delay1;
543 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
544 HTTPContext *c, *c_next;
546 if (my_http_addr.sin_port) {
547 server_fd = socket_open_listen(&my_http_addr);
552 if (my_rtsp_addr.sin_port) {
553 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
554 if (rtsp_server_fd < 0)
558 if (!rtsp_server_fd && !server_fd) {
559 http_log("HTTP and RTSP disabled.\n");
563 http_log("ffserver started.\n");
565 start_children(first_feed);
567 first_http_ctx = NULL;
573 poll_entry = poll_table;
575 poll_entry->fd = server_fd;
576 poll_entry->events = POLLIN;
579 if (rtsp_server_fd) {
580 poll_entry->fd = rtsp_server_fd;
581 poll_entry->events = POLLIN;
585 /* wait for events on each HTTP handle */
592 case HTTPSTATE_SEND_HEADER:
593 case RTSPSTATE_SEND_REPLY:
594 case RTSPSTATE_SEND_PACKET:
595 c->poll_entry = poll_entry;
597 poll_entry->events = POLLOUT;
600 case HTTPSTATE_SEND_DATA_HEADER:
601 case HTTPSTATE_SEND_DATA:
602 case HTTPSTATE_SEND_DATA_TRAILER:
603 if (!c->is_packetized) {
604 /* for TCP, we output as much as we can (may need to put a limit) */
605 c->poll_entry = poll_entry;
607 poll_entry->events = POLLOUT;
610 /* when ffserver is doing the timing, we work by
611 looking at which packet need to be sent every
613 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
618 case HTTPSTATE_WAIT_REQUEST:
619 case HTTPSTATE_RECEIVE_DATA:
620 case HTTPSTATE_WAIT_FEED:
621 case RTSPSTATE_WAIT_REQUEST:
622 /* need to catch errors */
623 c->poll_entry = poll_entry;
625 poll_entry->events = POLLIN;/* Maybe this will work */
629 c->poll_entry = NULL;
635 /* wait for an event on one connection. We poll at least every
636 second to handle timeouts */
638 ret = poll(poll_table, poll_entry - poll_table, delay);
639 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
640 ff_neterrno() != FF_NETERROR(EINTR))
644 cur_time = av_gettime() / 1000;
646 if (need_to_start_children) {
647 need_to_start_children = 0;
648 start_children(first_feed);
651 /* now handle the events */
652 for(c = first_http_ctx; c != NULL; c = c_next) {
654 if (handle_connection(c) < 0) {
655 /* close and free the connection */
661 poll_entry = poll_table;
663 /* new HTTP connection request ? */
664 if (poll_entry->revents & POLLIN)
665 new_connection(server_fd, 0);
668 if (rtsp_server_fd) {
669 /* new RTSP connection request ? */
670 if (poll_entry->revents & POLLIN)
671 new_connection(rtsp_server_fd, 1);
676 /* start waiting for a new HTTP/RTSP request */
677 static void start_wait_request(HTTPContext *c, int is_rtsp)
679 c->buffer_ptr = c->buffer;
680 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
683 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
684 c->state = RTSPSTATE_WAIT_REQUEST;
686 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
687 c->state = HTTPSTATE_WAIT_REQUEST;
691 static void new_connection(int server_fd, int is_rtsp)
693 struct sockaddr_in from_addr;
695 HTTPContext *c = NULL;
697 len = sizeof(from_addr);
698 fd = accept(server_fd, (struct sockaddr *)&from_addr,
701 http_log("error during accept %s\n", strerror(errno));
704 ff_socket_nonblock(fd, 1);
706 /* XXX: should output a warning page when coming
707 close to the connection limit */
708 if (nb_connections >= nb_max_connections)
711 /* add a new connection */
712 c = av_mallocz(sizeof(HTTPContext));
717 c->poll_entry = NULL;
718 c->from_addr = from_addr;
719 c->buffer_size = IOBUFFER_INIT_SIZE;
720 c->buffer = av_malloc(c->buffer_size);
724 c->next = first_http_ctx;
728 start_wait_request(c, is_rtsp);
740 static void close_connection(HTTPContext *c)
742 HTTPContext **cp, *c1;
744 AVFormatContext *ctx;
748 /* remove connection from list */
749 cp = &first_http_ctx;
750 while ((*cp) != NULL) {
758 /* remove references, if any (XXX: do it faster) */
759 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
764 /* remove connection associated resources */
768 /* close each frame parser */
769 for(i=0;i<c->fmt_in->nb_streams;i++) {
770 st = c->fmt_in->streams[i];
771 if (st->codec->codec)
772 avcodec_close(st->codec);
774 av_close_input_file(c->fmt_in);
777 /* free RTP output streams if any */
780 nb_streams = c->stream->nb_streams;
782 for(i=0;i<nb_streams;i++) {
785 av_write_trailer(ctx);
788 h = c->rtp_handles[i];
795 if (!c->last_packet_sent) {
798 if (url_open_dyn_buf(&ctx->pb) >= 0) {
799 av_write_trailer(ctx);
800 av_freep(&c->pb_buffer);
801 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
806 for(i=0; i<ctx->nb_streams; i++)
807 av_free(ctx->streams[i]);
809 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
810 current_bandwidth -= c->stream->bandwidth;
812 /* signal that there is no feed if we are the feeder socket */
813 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
814 c->stream->feed_opened = 0;
818 av_freep(&c->pb_buffer);
819 av_freep(&c->packet_buffer);
825 static int handle_connection(HTTPContext *c)
830 case HTTPSTATE_WAIT_REQUEST:
831 case RTSPSTATE_WAIT_REQUEST:
833 if ((c->timeout - cur_time) < 0)
835 if (c->poll_entry->revents & (POLLERR | POLLHUP))
838 /* no need to read if no events */
839 if (!(c->poll_entry->revents & POLLIN))
843 len = recv(c->fd, c->buffer_ptr, 1, 0);
845 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
846 ff_neterrno() != FF_NETERROR(EINTR))
848 } else if (len == 0) {
851 /* search for end of request. */
853 c->buffer_ptr += len;
855 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
856 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
857 /* request found : parse it and reply */
858 if (c->state == HTTPSTATE_WAIT_REQUEST) {
859 ret = http_parse_request(c);
861 ret = rtsp_parse_request(c);
865 } else if (ptr >= c->buffer_end) {
866 /* request too long: cannot do anything */
868 } else goto read_loop;
872 case HTTPSTATE_SEND_HEADER:
873 if (c->poll_entry->revents & (POLLERR | POLLHUP))
876 /* no need to write if no events */
877 if (!(c->poll_entry->revents & POLLOUT))
879 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
881 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
882 ff_neterrno() != FF_NETERROR(EINTR)) {
883 /* error : close connection */
884 av_freep(&c->pb_buffer);
888 c->buffer_ptr += len;
890 c->stream->bytes_served += len;
891 c->data_count += len;
892 if (c->buffer_ptr >= c->buffer_end) {
893 av_freep(&c->pb_buffer);
897 /* all the buffer was sent : synchronize to the incoming stream */
898 c->state = HTTPSTATE_SEND_DATA_HEADER;
899 c->buffer_ptr = c->buffer_end = c->buffer;
904 case HTTPSTATE_SEND_DATA:
905 case HTTPSTATE_SEND_DATA_HEADER:
906 case HTTPSTATE_SEND_DATA_TRAILER:
907 /* for packetized output, we consider we can always write (the
908 input streams sets the speed). It may be better to verify
909 that we do not rely too much on the kernel queues */
910 if (!c->is_packetized) {
911 if (c->poll_entry->revents & (POLLERR | POLLHUP))
914 /* no need to read if no events */
915 if (!(c->poll_entry->revents & POLLOUT))
918 if (http_send_data(c) < 0)
920 /* close connection if trailer sent */
921 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
924 case HTTPSTATE_RECEIVE_DATA:
925 /* no need to read if no events */
926 if (c->poll_entry->revents & (POLLERR | POLLHUP))
928 if (!(c->poll_entry->revents & POLLIN))
930 if (http_receive_data(c) < 0)
933 case HTTPSTATE_WAIT_FEED:
934 /* no need to read if no events */
935 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
938 /* nothing to do, we'll be waken up by incoming feed packets */
941 case RTSPSTATE_SEND_REPLY:
942 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
943 av_freep(&c->pb_buffer);
946 /* no need to write if no events */
947 if (!(c->poll_entry->revents & POLLOUT))
949 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
951 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
952 ff_neterrno() != FF_NETERROR(EINTR)) {
953 /* error : close connection */
954 av_freep(&c->pb_buffer);
958 c->buffer_ptr += len;
959 c->data_count += len;
960 if (c->buffer_ptr >= c->buffer_end) {
961 /* all the buffer was sent : wait for a new request */
962 av_freep(&c->pb_buffer);
963 start_wait_request(c, 1);
967 case RTSPSTATE_SEND_PACKET:
968 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
969 av_freep(&c->packet_buffer);
972 /* no need to write if no events */
973 if (!(c->poll_entry->revents & POLLOUT))
975 len = send(c->fd, c->packet_buffer_ptr,
976 c->packet_buffer_end - c->packet_buffer_ptr, 0);
978 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
979 ff_neterrno() != FF_NETERROR(EINTR)) {
980 /* error : close connection */
981 av_freep(&c->packet_buffer);
985 c->packet_buffer_ptr += len;
986 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
987 /* all the buffer was sent : wait for a new request */
988 av_freep(&c->packet_buffer);
989 c->state = RTSPSTATE_WAIT_REQUEST;
993 case HTTPSTATE_READY:
1002 static int extract_rates(char *rates, int ratelen, const char *request)
1006 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1007 if (strncasecmp(p, "Pragma:", 7) == 0) {
1008 const char *q = p + 7;
1010 while (*q && *q != '\n' && isspace(*q))
1013 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1019 memset(rates, 0xff, ratelen);
1022 while (*q && *q != '\n' && *q != ':')
1025 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1029 if (stream_no < ratelen && stream_no >= 0)
1030 rates[stream_no] = rate_no;
1032 while (*q && *q != '\n' && !isspace(*q))
1039 p = strchr(p, '\n');
1049 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1052 int best_bitrate = 100000000;
1055 for (i = 0; i < feed->nb_streams; i++) {
1056 AVCodecContext *feed_codec = feed->streams[i]->codec;
1058 if (feed_codec->codec_id != codec->codec_id ||
1059 feed_codec->sample_rate != codec->sample_rate ||
1060 feed_codec->width != codec->width ||
1061 feed_codec->height != codec->height)
1064 /* Potential stream */
1066 /* We want the fastest stream less than bit_rate, or the slowest
1067 * faster than bit_rate
1070 if (feed_codec->bit_rate <= bit_rate) {
1071 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1072 best_bitrate = feed_codec->bit_rate;
1076 if (feed_codec->bit_rate < best_bitrate) {
1077 best_bitrate = feed_codec->bit_rate;
1086 static int modify_current_stream(HTTPContext *c, char *rates)
1089 FFStream *req = c->stream;
1090 int action_required = 0;
1092 /* Not much we can do for a feed */
1096 for (i = 0; i < req->nb_streams; i++) {
1097 AVCodecContext *codec = req->streams[i]->codec;
1101 c->switch_feed_streams[i] = req->feed_streams[i];
1104 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1107 /* Wants off or slow */
1108 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1110 /* This doesn't work well when it turns off the only stream! */
1111 c->switch_feed_streams[i] = -2;
1112 c->feed_streams[i] = -2;
1117 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1118 action_required = 1;
1121 return action_required;
1125 static void do_switch_stream(HTTPContext *c, int i)
1127 if (c->switch_feed_streams[i] >= 0) {
1129 c->feed_streams[i] = c->switch_feed_streams[i];
1132 /* Now update the stream */
1134 c->switch_feed_streams[i] = -1;
1137 /* XXX: factorize in utils.c ? */
1138 /* XXX: take care with different space meaning */
1139 static void skip_spaces(const char **pp)
1143 while (*p == ' ' || *p == '\t')
1148 static void get_word(char *buf, int buf_size, const char **pp)
1156 while (!isspace(*p) && *p != '\0') {
1157 if ((q - buf) < buf_size - 1)
1166 static int validate_acl(FFStream *stream, HTTPContext *c)
1168 enum IPAddressAction last_action = IP_DENY;
1170 struct in_addr *src = &c->from_addr.sin_addr;
1171 unsigned long src_addr = src->s_addr;
1173 for (acl = stream->acl; acl; acl = acl->next) {
1174 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1175 return (acl->action == IP_ALLOW) ? 1 : 0;
1176 last_action = acl->action;
1179 /* Nothing matched, so return not the last action */
1180 return (last_action == IP_DENY) ? 1 : 0;
1183 /* compute the real filename of a file by matching it without its
1184 extensions to all the stream filenames */
1185 static void compute_real_filename(char *filename, int max_size)
1192 /* compute filename by matching without the file extensions */
1193 av_strlcpy(file1, filename, sizeof(file1));
1194 p = strrchr(file1, '.');
1197 for(stream = first_stream; stream != NULL; stream = stream->next) {
1198 av_strlcpy(file2, stream->filename, sizeof(file2));
1199 p = strrchr(file2, '.');
1202 if (!strcmp(file1, file2)) {
1203 av_strlcpy(filename, stream->filename, max_size);
1218 /* parse http request and prepare header */
1219 static int http_parse_request(HTTPContext *c)
1222 enum RedirType redir_type;
1224 char info[1024], filename[1024];
1228 const char *mime_type;
1232 char *useragent = 0;
1235 get_word(cmd, sizeof(cmd), (const char **)&p);
1236 av_strlcpy(c->method, cmd, sizeof(c->method));
1238 if (!strcmp(cmd, "GET"))
1240 else if (!strcmp(cmd, "POST"))
1245 get_word(url, sizeof(url), (const char **)&p);
1246 av_strlcpy(c->url, url, sizeof(c->url));
1248 get_word(protocol, sizeof(protocol), (const char **)&p);
1249 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1252 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1255 http_log("New connection: %s %s\n", cmd, url);
1257 /* find the filename and the optional info string in the request */
1258 p = strchr(url, '?');
1260 av_strlcpy(info, p, sizeof(info));
1265 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1267 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1268 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1270 if (*useragent && *useragent != '\n' && isspace(*useragent))
1274 p = strchr(p, '\n');
1281 redir_type = REDIR_NONE;
1282 if (match_ext(filename, "asx")) {
1283 redir_type = REDIR_ASX;
1284 filename[strlen(filename)-1] = 'f';
1285 } else if (match_ext(filename, "asf") &&
1286 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1287 /* if this isn't WMP or lookalike, return the redirector file */
1288 redir_type = REDIR_ASF;
1289 } else if (match_ext(filename, "rpm,ram")) {
1290 redir_type = REDIR_RAM;
1291 strcpy(filename + strlen(filename)-2, "m");
1292 } else if (match_ext(filename, "rtsp")) {
1293 redir_type = REDIR_RTSP;
1294 compute_real_filename(filename, sizeof(filename) - 1);
1295 } else if (match_ext(filename, "sdp")) {
1296 redir_type = REDIR_SDP;
1297 compute_real_filename(filename, sizeof(filename) - 1);
1300 // "redirect" / request to index.html
1301 if (!strlen(filename))
1302 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1304 stream = first_stream;
1305 while (stream != NULL) {
1306 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1308 stream = stream->next;
1310 if (stream == NULL) {
1311 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1316 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1317 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1319 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1320 c->http_error = 301;
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1323 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1324 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1325 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1326 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1327 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1328 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1330 /* prepare output buffer */
1331 c->buffer_ptr = c->buffer;
1333 c->state = HTTPSTATE_SEND_HEADER;
1337 /* If this is WMP, get the rate information */
1338 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1339 if (modify_current_stream(c, ratebuf)) {
1340 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1341 if (c->switch_feed_streams[i] >= 0)
1342 do_switch_stream(c, i);
1347 /* If already streaming this feed, do not let start another feeder. */
1348 if (stream->feed_opened) {
1349 snprintf(msg, sizeof(msg), "This feed is already being received.");
1350 http_log("feed %s already being received\n", stream->feed_filename);
1354 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1355 current_bandwidth += stream->bandwidth;
1357 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1358 c->http_error = 200;
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1362 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1363 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1364 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");
1365 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",
1366 current_bandwidth, max_bandwidth);
1367 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1369 /* prepare output buffer */
1370 c->buffer_ptr = c->buffer;
1372 c->state = HTTPSTATE_SEND_HEADER;
1376 if (redir_type != REDIR_NONE) {
1379 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1380 if (strncasecmp(p, "Host:", 5) == 0) {
1384 p = strchr(p, '\n');
1395 while (isspace(*hostinfo))
1398 eoh = strchr(hostinfo, '\n');
1400 if (eoh[-1] == '\r')
1403 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1404 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1405 hostbuf[eoh - hostinfo] = 0;
1407 c->http_error = 200;
1409 switch(redir_type) {
1411 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1412 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1413 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1414 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1415 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1416 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1417 hostbuf, filename, info);
1418 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1421 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1422 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1425 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1426 hostbuf, filename, info);
1429 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1430 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1431 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1432 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1433 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1434 hostbuf, filename, info);
1438 char hostname[256], *p;
1439 /* extract only hostname */
1440 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1441 p = strrchr(hostname, ':');
1444 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1445 /* XXX: incorrect mime type ? */
1446 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1447 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1448 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1449 hostname, ntohs(my_rtsp_addr.sin_port),
1456 int sdp_data_size, len;
1457 struct sockaddr_in my_addr;
1459 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1460 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1461 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1463 len = sizeof(my_addr);
1464 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1466 /* XXX: should use a dynamic buffer */
1467 sdp_data_size = prepare_sdp_description(stream,
1470 if (sdp_data_size > 0) {
1471 memcpy(q, sdp_data, sdp_data_size);
1483 /* prepare output buffer */
1484 c->buffer_ptr = c->buffer;
1486 c->state = HTTPSTATE_SEND_HEADER;
1492 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1496 stream->conns_served++;
1498 /* XXX: add there authenticate and IP match */
1501 /* if post, it means a feed is being sent */
1502 if (!stream->is_feed) {
1503 /* However it might be a status report from WMP! Lets log the data
1504 * as it might come in handy one day
1509 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1510 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1514 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1515 client_id = strtol(p + 18, 0, 10);
1516 p = strchr(p, '\n');
1524 char *eol = strchr(logline, '\n');
1529 if (eol[-1] == '\r')
1531 http_log("%.*s\n", (int) (eol - logline), logline);
1532 c->suppress_log = 1;
1537 http_log("\nGot request:\n%s\n", c->buffer);
1540 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1543 /* Now we have to find the client_id */
1544 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1545 if (wmpc->wmp_client_id == client_id)
1549 if (wmpc && modify_current_stream(wmpc, ratebuf))
1550 wmpc->switch_pending = 1;
1553 snprintf(msg, sizeof(msg), "POST command not handled");
1557 if (http_start_receive_data(c) < 0) {
1558 snprintf(msg, sizeof(msg), "could not open feed");
1562 c->state = HTTPSTATE_RECEIVE_DATA;
1567 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1568 http_log("\nGot request:\n%s\n", c->buffer);
1571 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1574 /* open input stream */
1575 if (open_input_stream(c, info) < 0) {
1576 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1580 /* prepare http header */
1582 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1583 mime_type = c->stream->fmt->mime_type;
1585 mime_type = "application/x-octet-stream";
1586 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1588 /* for asf, we need extra headers */
1589 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1590 /* Need to allocate a client id */
1592 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1594 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);
1596 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1597 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1599 /* prepare output buffer */
1601 c->buffer_ptr = c->buffer;
1603 c->state = HTTPSTATE_SEND_HEADER;
1606 c->http_error = 404;
1608 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1609 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1610 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1611 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1612 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1613 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1614 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1616 /* prepare output buffer */
1617 c->buffer_ptr = c->buffer;
1619 c->state = HTTPSTATE_SEND_HEADER;
1623 c->http_error = 200; /* horrible : we use this value to avoid
1624 going to the send data state */
1625 c->state = HTTPSTATE_SEND_HEADER;
1629 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1631 static const char *suffix = " kMGTP";
1634 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1636 url_fprintf(pb, "%"PRId64"%c", count, *s);
1639 static void compute_status(HTTPContext *c)
1648 if (url_open_dyn_buf(&pb) < 0) {
1649 /* XXX: return an error ? */
1650 c->buffer_ptr = c->buffer;
1651 c->buffer_end = c->buffer;
1655 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1656 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1657 url_fprintf(pb, "Pragma: no-cache\r\n");
1658 url_fprintf(pb, "\r\n");
1660 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1661 if (c->stream->feed_filename[0])
1662 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1663 url_fprintf(pb, "</HEAD>\n<BODY>");
1664 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1666 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1667 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1668 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");
1669 stream = first_stream;
1670 while (stream != NULL) {
1671 char sfilename[1024];
1674 if (stream->feed != stream) {
1675 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1676 eosf = sfilename + strlen(sfilename);
1677 if (eosf - sfilename >= 4) {
1678 if (strcmp(eosf - 4, ".asf") == 0)
1679 strcpy(eosf - 4, ".asx");
1680 else if (strcmp(eosf - 3, ".rm") == 0)
1681 strcpy(eosf - 3, ".ram");
1682 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1683 /* generate a sample RTSP director if
1684 unicast. Generate an SDP redirector if
1686 eosf = strrchr(sfilename, '.');
1688 eosf = sfilename + strlen(sfilename);
1689 if (stream->is_multicast)
1690 strcpy(eosf, ".sdp");
1692 strcpy(eosf, ".rtsp");
1696 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1697 sfilename, stream->filename);
1698 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1699 stream->conns_served);
1700 fmt_bytecount(pb, stream->bytes_served);
1701 switch(stream->stream_type) {
1702 case STREAM_TYPE_LIVE: {
1703 int audio_bit_rate = 0;
1704 int video_bit_rate = 0;
1705 const char *audio_codec_name = "";
1706 const char *video_codec_name = "";
1707 const char *audio_codec_name_extra = "";
1708 const char *video_codec_name_extra = "";
1710 for(i=0;i<stream->nb_streams;i++) {
1711 AVStream *st = stream->streams[i];
1712 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1713 switch(st->codec->codec_type) {
1714 case CODEC_TYPE_AUDIO:
1715 audio_bit_rate += st->codec->bit_rate;
1717 if (*audio_codec_name)
1718 audio_codec_name_extra = "...";
1719 audio_codec_name = codec->name;
1722 case CODEC_TYPE_VIDEO:
1723 video_bit_rate += st->codec->bit_rate;
1725 if (*video_codec_name)
1726 video_codec_name_extra = "...";
1727 video_codec_name = codec->name;
1730 case CODEC_TYPE_DATA:
1731 video_bit_rate += st->codec->bit_rate;
1737 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",
1740 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1741 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1743 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1745 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1746 url_fprintf(pb, "\n");
1750 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1754 stream = stream->next;
1756 url_fprintf(pb, "</TABLE>\n");
1758 stream = first_stream;
1759 while (stream != NULL) {
1760 if (stream->feed == stream) {
1761 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1763 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1765 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1770 /* This is somewhat linux specific I guess */
1771 snprintf(ps_cmd, sizeof(ps_cmd),
1772 "ps -o \"%%cpu,cputime\" --no-headers %d",
1775 pid_stat = popen(ps_cmd, "r");
1780 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1782 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1790 url_fprintf(pb, "<p>");
1792 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");
1794 for (i = 0; i < stream->nb_streams; i++) {
1795 AVStream *st = stream->streams[i];
1796 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1797 const char *type = "unknown";
1798 char parameters[64];
1802 switch(st->codec->codec_type) {
1803 case CODEC_TYPE_AUDIO:
1805 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1807 case CODEC_TYPE_VIDEO:
1809 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1810 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1815 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1816 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1818 url_fprintf(pb, "</table>\n");
1821 stream = stream->next;
1827 AVCodecContext *enc;
1831 stream = first_feed;
1832 while (stream != NULL) {
1833 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1834 url_fprintf(pb, "<TABLE>\n");
1835 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1836 for(i=0;i<stream->nb_streams;i++) {
1837 AVStream *st = stream->streams[i];
1838 FeedData *fdata = st->priv_data;
1841 avcodec_string(buf, sizeof(buf), enc);
1842 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1843 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1844 avg /= enc->frame_size;
1845 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1846 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1848 url_fprintf(pb, "</TABLE>\n");
1849 stream = stream->next_feed;
1854 /* connection status */
1855 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1857 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1858 nb_connections, nb_max_connections);
1860 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1861 current_bandwidth, max_bandwidth);
1863 url_fprintf(pb, "<TABLE>\n");
1864 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");
1865 c1 = first_http_ctx;
1867 while (c1 != NULL) {
1873 for (j = 0; j < c1->stream->nb_streams; j++) {
1874 if (!c1->stream->feed)
1875 bitrate += c1->stream->streams[j]->codec->bit_rate;
1876 else if (c1->feed_streams[j] >= 0)
1877 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1882 p = inet_ntoa(c1->from_addr.sin_addr);
1883 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1885 c1->stream ? c1->stream->filename : "",
1886 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1889 http_state[c1->state]);
1890 fmt_bytecount(pb, bitrate);
1891 url_fprintf(pb, "<td align=right>");
1892 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1893 url_fprintf(pb, "<td align=right>");
1894 fmt_bytecount(pb, c1->data_count);
1895 url_fprintf(pb, "\n");
1898 url_fprintf(pb, "</TABLE>\n");
1903 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1904 url_fprintf(pb, "</BODY>\n</HTML>\n");
1906 len = url_close_dyn_buf(pb, &c->pb_buffer);
1907 c->buffer_ptr = c->pb_buffer;
1908 c->buffer_end = c->pb_buffer + len;
1911 /* check if the parser needs to be opened for stream i */
1912 static void open_parser(AVFormatContext *s, int i)
1914 AVStream *st = s->streams[i];
1917 if (!st->codec->codec) {
1918 codec = avcodec_find_decoder(st->codec->codec_id);
1919 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1920 st->codec->parse_only = 1;
1921 if (avcodec_open(st->codec, codec) < 0)
1922 st->codec->parse_only = 0;
1927 static int open_input_stream(HTTPContext *c, const char *info)
1930 char input_filename[1024];
1932 int buf_size, i, ret;
1935 /* find file name */
1936 if (c->stream->feed) {
1937 strcpy(input_filename, c->stream->feed->feed_filename);
1938 buf_size = FFM_PACKET_SIZE;
1939 /* compute position (absolute time) */
1940 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1941 stream_pos = parse_date(buf, 0);
1942 if (stream_pos == INT64_MIN)
1944 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1945 int prebuffer = strtol(buf, 0, 10);
1946 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1948 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1950 strcpy(input_filename, c->stream->feed_filename);
1952 /* compute position (relative time) */
1953 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1954 stream_pos = parse_date(buf, 1);
1955 if (stream_pos == INT64_MIN)
1960 if (input_filename[0] == '\0')
1964 { time_t when = stream_pos / 1000000;
1965 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1970 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1971 buf_size, c->stream->ap_in)) < 0) {
1972 http_log("could not open %s: %d\n", input_filename, ret);
1975 s->flags |= AVFMT_FLAG_GENPTS;
1977 av_find_stream_info(c->fmt_in);
1979 /* open each parser */
1980 for(i=0;i<s->nb_streams;i++)
1983 /* choose stream as clock source (we favorize video stream if
1984 present) for packet sending */
1985 c->pts_stream_index = 0;
1986 for(i=0;i<c->stream->nb_streams;i++) {
1987 if (c->pts_stream_index == 0 &&
1988 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1989 c->pts_stream_index = i;
1994 if (c->fmt_in->iformat->read_seek)
1995 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1997 /* set the start time (needed for maxtime and RTP packet timing) */
1998 c->start_time = cur_time;
1999 c->first_pts = AV_NOPTS_VALUE;
2003 /* return the server clock (in us) */
2004 static int64_t get_server_clock(HTTPContext *c)
2006 /* compute current pts value from system time */
2007 return (cur_time - c->start_time) * 1000;
2010 /* return the estimated time at which the current packet must be sent
2012 static int64_t get_packet_send_clock(HTTPContext *c)
2014 int bytes_left, bytes_sent, frame_bytes;
2016 frame_bytes = c->cur_frame_bytes;
2017 if (frame_bytes <= 0)
2020 bytes_left = c->buffer_end - c->buffer_ptr;
2021 bytes_sent = frame_bytes - bytes_left;
2022 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2027 static int http_prepare_data(HTTPContext *c)
2030 AVFormatContext *ctx;
2032 av_freep(&c->pb_buffer);
2034 case HTTPSTATE_SEND_DATA_HEADER:
2035 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2036 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2037 sizeof(c->fmt_ctx.author));
2038 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2039 sizeof(c->fmt_ctx.comment));
2040 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2041 sizeof(c->fmt_ctx.copyright));
2042 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2043 sizeof(c->fmt_ctx.title));
2045 for(i=0;i<c->stream->nb_streams;i++) {
2048 st = av_mallocz(sizeof(AVStream));
2049 c->fmt_ctx.streams[i] = st;
2050 /* if file or feed, then just take streams from FFStream struct */
2051 if (!c->stream->feed ||
2052 c->stream->feed == c->stream)
2053 src = c->stream->streams[i];
2055 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2059 st->codec->frame_number = 0; /* XXX: should be done in
2060 AVStream, not in codec */
2062 /* set output format parameters */
2063 c->fmt_ctx.oformat = c->stream->fmt;
2064 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2066 c->got_key_frame = 0;
2068 /* prepare header and save header data in a stream */
2069 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2070 /* XXX: potential leak */
2073 c->fmt_ctx.pb->is_streamed = 1;
2076 * HACK to avoid mpeg ps muxer to spit many underflow errors
2077 * Default value from FFmpeg
2078 * Try to set it use configuration option
2080 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2081 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2083 av_set_parameters(&c->fmt_ctx, NULL);
2084 if (av_write_header(&c->fmt_ctx) < 0) {
2085 http_log("Error writing output header\n");
2089 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2090 c->buffer_ptr = c->pb_buffer;
2091 c->buffer_end = c->pb_buffer + len;
2093 c->state = HTTPSTATE_SEND_DATA;
2094 c->last_packet_sent = 0;
2096 case HTTPSTATE_SEND_DATA:
2097 /* find a new packet */
2098 /* read a packet from the input stream */
2099 if (c->stream->feed)
2100 ffm_set_write_index(c->fmt_in,
2101 c->stream->feed->feed_write_index,
2102 c->stream->feed->feed_size);
2104 if (c->stream->max_time &&
2105 c->stream->max_time + c->start_time - cur_time < 0)
2106 /* We have timed out */
2107 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2111 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2112 if (c->stream->feed && c->stream->feed->feed_opened) {
2113 /* if coming from feed, it means we reached the end of the
2114 ffm file, so must wait for more data */
2115 c->state = HTTPSTATE_WAIT_FEED;
2116 return 1; /* state changed */
2118 if (c->stream->loop) {
2119 av_close_input_file(c->fmt_in);
2121 if (open_input_stream(c, "") < 0)
2126 /* must send trailer now because eof or error */
2127 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2131 int source_index = pkt.stream_index;
2132 /* update first pts if needed */
2133 if (c->first_pts == AV_NOPTS_VALUE) {
2134 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2135 c->start_time = cur_time;
2137 /* send it to the appropriate stream */
2138 if (c->stream->feed) {
2139 /* if coming from a feed, select the right stream */
2140 if (c->switch_pending) {
2141 c->switch_pending = 0;
2142 for(i=0;i<c->stream->nb_streams;i++) {
2143 if (c->switch_feed_streams[i] == pkt.stream_index)
2144 if (pkt.flags & PKT_FLAG_KEY)
2145 do_switch_stream(c, i);
2146 if (c->switch_feed_streams[i] >= 0)
2147 c->switch_pending = 1;
2150 for(i=0;i<c->stream->nb_streams;i++) {
2151 if (c->feed_streams[i] == pkt.stream_index) {
2152 AVStream *st = c->fmt_in->streams[source_index];
2153 pkt.stream_index = i;
2154 if (pkt.flags & PKT_FLAG_KEY &&
2155 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2156 c->stream->nb_streams == 1))
2157 c->got_key_frame = 1;
2158 if (!c->stream->send_on_key || c->got_key_frame)
2163 AVCodecContext *codec;
2164 AVStream *ist, *ost;
2166 ist = c->fmt_in->streams[source_index];
2167 /* specific handling for RTP: we use several
2168 output stream (one for each RTP
2169 connection). XXX: need more abstract handling */
2170 if (c->is_packetized) {
2171 /* compute send time and duration */
2172 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2173 if (ist->start_time != AV_NOPTS_VALUE)
2174 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2175 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2177 printf("index=%d pts=%0.3f duration=%0.6f\n",
2179 (double)c->cur_pts /
2181 (double)c->cur_frame_duration /
2184 /* find RTP context */
2185 c->packet_stream_index = pkt.stream_index;
2186 ctx = c->rtp_ctx[c->packet_stream_index];
2188 av_free_packet(&pkt);
2191 codec = ctx->streams[0]->codec;
2192 /* only one stream per RTP connection */
2193 pkt.stream_index = 0;
2197 codec = ctx->streams[pkt.stream_index]->codec;
2200 if (c->is_packetized) {
2201 int max_packet_size;
2202 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2203 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2205 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2206 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2208 ret = url_open_dyn_buf(&ctx->pb);
2211 /* XXX: potential leak */
2214 ost = ctx->streams[pkt.stream_index];
2216 ctx->pb->is_streamed = 1;
2217 if (pkt.dts != AV_NOPTS_VALUE)
2218 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2219 if (pkt.pts != AV_NOPTS_VALUE)
2220 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2221 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2222 if (av_write_frame(ctx, &pkt) < 0) {
2223 http_log("Error writing frame to output\n");
2224 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2227 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2228 c->cur_frame_bytes = len;
2229 c->buffer_ptr = c->pb_buffer;
2230 c->buffer_end = c->pb_buffer + len;
2232 codec->frame_number++;
2234 av_free_packet(&pkt);
2238 av_free_packet(&pkt);
2243 case HTTPSTATE_SEND_DATA_TRAILER:
2244 /* last packet test ? */
2245 if (c->last_packet_sent || c->is_packetized)
2248 /* prepare header */
2249 if (url_open_dyn_buf(&ctx->pb) < 0) {
2250 /* XXX: potential leak */
2253 c->fmt_ctx.pb->is_streamed = 1;
2254 av_write_trailer(ctx);
2255 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2256 c->buffer_ptr = c->pb_buffer;
2257 c->buffer_end = c->pb_buffer + len;
2259 c->last_packet_sent = 1;
2265 /* should convert the format at the same time */
2266 /* send data starting at c->buffer_ptr to the output connection
2267 (either UDP or TCP connection) */
2268 static int http_send_data(HTTPContext *c)
2273 if (c->buffer_ptr >= c->buffer_end) {
2274 ret = http_prepare_data(c);
2278 /* state change requested */
2281 if (c->is_packetized) {
2282 /* RTP data output */
2283 len = c->buffer_end - c->buffer_ptr;
2285 /* fail safe - should never happen */
2287 c->buffer_ptr = c->buffer_end;
2290 len = (c->buffer_ptr[0] << 24) |
2291 (c->buffer_ptr[1] << 16) |
2292 (c->buffer_ptr[2] << 8) |
2294 if (len > (c->buffer_end - c->buffer_ptr))
2296 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2297 /* nothing to send yet: we can wait */
2301 c->data_count += len;
2302 update_datarate(&c->datarate, c->data_count);
2304 c->stream->bytes_served += len;
2306 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2307 /* RTP packets are sent inside the RTSP TCP connection */
2309 int interleaved_index, size;
2311 HTTPContext *rtsp_c;
2314 /* if no RTSP connection left, error */
2317 /* if already sending something, then wait. */
2318 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2320 if (url_open_dyn_buf(&pb) < 0)
2322 interleaved_index = c->packet_stream_index * 2;
2323 /* RTCP packets are sent at odd indexes */
2324 if (c->buffer_ptr[1] == 200)
2325 interleaved_index++;
2326 /* write RTSP TCP header */
2328 header[1] = interleaved_index;
2329 header[2] = len >> 8;
2331 put_buffer(pb, header, 4);
2332 /* write RTP packet data */
2334 put_buffer(pb, c->buffer_ptr, len);
2335 size = url_close_dyn_buf(pb, &c->packet_buffer);
2336 /* prepare asynchronous TCP sending */
2337 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2338 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2339 c->buffer_ptr += len;
2341 /* send everything we can NOW */
2342 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2343 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2345 rtsp_c->packet_buffer_ptr += len;
2346 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2347 /* if we could not send all the data, we will
2348 send it later, so a new state is needed to
2349 "lock" the RTSP TCP connection */
2350 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2353 /* all data has been sent */
2354 av_freep(&c->packet_buffer);
2356 /* send RTP packet directly in UDP */
2358 url_write(c->rtp_handles[c->packet_stream_index],
2359 c->buffer_ptr, len);
2360 c->buffer_ptr += len;
2361 /* here we continue as we can send several packets per 10 ms slot */
2364 /* TCP data output */
2365 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2367 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2368 ff_neterrno() != FF_NETERROR(EINTR))
2369 /* error : close connection */
2374 c->buffer_ptr += len;
2376 c->data_count += len;
2377 update_datarate(&c->datarate, c->data_count);
2379 c->stream->bytes_served += len;
2387 static int http_start_receive_data(HTTPContext *c)
2391 if (c->stream->feed_opened)
2394 /* Don't permit writing to this one */
2395 if (c->stream->readonly)
2399 fd = open(c->stream->feed_filename, O_RDWR);
2401 http_log("Error opening feeder file: %s\n", strerror(errno));
2406 c->stream->feed_write_index = ffm_read_write_index(fd);
2407 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2408 lseek(fd, 0, SEEK_SET);
2410 /* init buffer input */
2411 c->buffer_ptr = c->buffer;
2412 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2413 c->stream->feed_opened = 1;
2417 static int http_receive_data(HTTPContext *c)
2421 if (c->buffer_end > c->buffer_ptr) {
2424 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2426 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2427 ff_neterrno() != FF_NETERROR(EINTR))
2428 /* error : close connection */
2430 } else if (len == 0)
2431 /* end of connection : close it */
2434 c->buffer_ptr += len;
2435 c->data_count += len;
2436 update_datarate(&c->datarate, c->data_count);
2440 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2441 if (c->buffer[0] != 'f' ||
2442 c->buffer[1] != 'm') {
2443 http_log("Feed stream has become desynchronized -- disconnecting\n");
2448 if (c->buffer_ptr >= c->buffer_end) {
2449 FFStream *feed = c->stream;
2450 /* a packet has been received : write it in the store, except
2452 if (c->data_count > FFM_PACKET_SIZE) {
2454 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2455 /* XXX: use llseek or url_seek */
2456 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2457 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2458 http_log("Error writing to feed file: %s\n", strerror(errno));
2462 feed->feed_write_index += FFM_PACKET_SIZE;
2463 /* update file size */
2464 if (feed->feed_write_index > c->stream->feed_size)
2465 feed->feed_size = feed->feed_write_index;
2467 /* handle wrap around if max file size reached */
2468 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2469 feed->feed_write_index = FFM_PACKET_SIZE;
2472 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2474 /* wake up any waiting connections */
2475 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2476 if (c1->state == HTTPSTATE_WAIT_FEED &&
2477 c1->stream->feed == c->stream->feed)
2478 c1->state = HTTPSTATE_SEND_DATA;
2481 /* We have a header in our hands that contains useful data */
2482 AVFormatContext *s = NULL;
2484 AVInputFormat *fmt_in;
2487 /* use feed output format name to find corresponding input format */
2488 fmt_in = av_find_input_format(feed->fmt->name);
2492 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2493 pb->is_streamed = 1;
2495 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2500 /* Now we have the actual streams */
2501 if (s->nb_streams != feed->nb_streams) {
2502 av_close_input_stream(s);
2507 for (i = 0; i < s->nb_streams; i++) {
2508 AVStream *fst = feed->streams[i];
2509 AVStream *st = s->streams[i];
2510 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2511 if (fst->codec->extradata_size) {
2512 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2513 if (!fst->codec->extradata)
2515 memcpy(fst->codec->extradata, st->codec->extradata,
2516 fst->codec->extradata_size);
2520 av_close_input_stream(s);
2523 c->buffer_ptr = c->buffer;
2528 c->stream->feed_opened = 0;
2530 /* wake up any waiting connections to stop waiting for feed */
2531 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2532 if (c1->state == HTTPSTATE_WAIT_FEED &&
2533 c1->stream->feed == c->stream->feed)
2534 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2539 /********************************************************************/
2542 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2549 switch(error_number) {
2550 case RTSP_STATUS_OK:
2553 case RTSP_STATUS_METHOD:
2554 str = "Method Not Allowed";
2556 case RTSP_STATUS_BANDWIDTH:
2557 str = "Not Enough Bandwidth";
2559 case RTSP_STATUS_SESSION:
2560 str = "Session Not Found";
2562 case RTSP_STATUS_STATE:
2563 str = "Method Not Valid in This State";
2565 case RTSP_STATUS_AGGREGATE:
2566 str = "Aggregate operation not allowed";
2568 case RTSP_STATUS_ONLY_AGGREGATE:
2569 str = "Only aggregate operation allowed";
2571 case RTSP_STATUS_TRANSPORT:
2572 str = "Unsupported transport";
2574 case RTSP_STATUS_INTERNAL:
2575 str = "Internal Server Error";
2577 case RTSP_STATUS_SERVICE:
2578 str = "Service Unavailable";
2580 case RTSP_STATUS_VERSION:
2581 str = "RTSP Version not supported";
2584 str = "Unknown Error";
2588 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2589 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2591 /* output GMT time */
2595 p = buf2 + strlen(p) - 1;
2598 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2601 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2603 rtsp_reply_header(c, error_number);
2604 url_fprintf(c->pb, "\r\n");
2607 static int rtsp_parse_request(HTTPContext *c)
2609 const char *p, *p1, *p2;
2615 RTSPHeader header1, *header = &header1;
2617 c->buffer_ptr[0] = '\0';
2620 get_word(cmd, sizeof(cmd), &p);
2621 get_word(url, sizeof(url), &p);
2622 get_word(protocol, sizeof(protocol), &p);
2624 av_strlcpy(c->method, cmd, sizeof(c->method));
2625 av_strlcpy(c->url, url, sizeof(c->url));
2626 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2628 if (url_open_dyn_buf(&c->pb) < 0) {
2629 /* XXX: cannot do more */
2630 c->pb = NULL; /* safety */
2634 /* check version name */
2635 if (strcmp(protocol, "RTSP/1.0") != 0) {
2636 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2640 /* parse each header line */
2641 memset(header, 0, sizeof(RTSPHeader));
2642 /* skip to next line */
2643 while (*p != '\n' && *p != '\0')
2647 while (*p != '\0') {
2648 p1 = strchr(p, '\n');
2652 if (p2 > p && p2[-1] == '\r')
2654 /* skip empty line */
2658 if (len > sizeof(line) - 1)
2659 len = sizeof(line) - 1;
2660 memcpy(line, p, len);
2662 rtsp_parse_line(header, line);
2666 /* handle sequence number */
2667 c->seq = header->seq;
2669 if (!strcmp(cmd, "DESCRIBE"))
2670 rtsp_cmd_describe(c, url);
2671 else if (!strcmp(cmd, "OPTIONS"))
2672 rtsp_cmd_options(c, url);
2673 else if (!strcmp(cmd, "SETUP"))
2674 rtsp_cmd_setup(c, url, header);
2675 else if (!strcmp(cmd, "PLAY"))
2676 rtsp_cmd_play(c, url, header);
2677 else if (!strcmp(cmd, "PAUSE"))
2678 rtsp_cmd_pause(c, url, header);
2679 else if (!strcmp(cmd, "TEARDOWN"))
2680 rtsp_cmd_teardown(c, url, header);
2682 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2685 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2686 c->pb = NULL; /* safety */
2688 /* XXX: cannot do more */
2691 c->buffer_ptr = c->pb_buffer;
2692 c->buffer_end = c->pb_buffer + len;
2693 c->state = RTSPSTATE_SEND_REPLY;
2697 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2698 struct in_addr my_ip)
2700 AVFormatContext *avc;
2701 AVStream avs[MAX_STREAMS];
2704 avc = av_alloc_format_context();
2708 if (stream->title[0] != 0) {
2709 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2711 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2713 avc->nb_streams = stream->nb_streams;
2714 if (stream->is_multicast) {
2715 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2716 inet_ntoa(stream->multicast_ip),
2717 stream->multicast_port, stream->multicast_ttl);
2720 for(i = 0; i < stream->nb_streams; i++) {
2721 avc->streams[i] = &avs[i];
2722 avc->streams[i]->codec = stream->streams[i]->codec;
2724 *pbuffer = av_mallocz(2048);
2725 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2728 return strlen(*pbuffer);
2731 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2733 // rtsp_reply_header(c, RTSP_STATUS_OK);
2734 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2735 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2736 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2737 url_fprintf(c->pb, "\r\n");
2740 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2746 int content_length, len;
2747 struct sockaddr_in my_addr;
2749 /* find which url is asked */
2750 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2755 for(stream = first_stream; stream != NULL; stream = stream->next) {
2756 if (!stream->is_feed &&
2757 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2758 !strcmp(path, stream->filename)) {
2762 /* no stream found */
2763 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2767 /* prepare the media description in sdp format */
2769 /* get the host IP */
2770 len = sizeof(my_addr);
2771 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2772 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2773 if (content_length < 0) {
2774 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2777 rtsp_reply_header(c, RTSP_STATUS_OK);
2778 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2779 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2780 url_fprintf(c->pb, "\r\n");
2781 put_buffer(c->pb, content, content_length);
2784 static HTTPContext *find_rtp_session(const char *session_id)
2788 if (session_id[0] == '\0')
2791 for(c = first_http_ctx; c != NULL; c = c->next) {
2792 if (!strcmp(c->session_id, session_id))
2798 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2800 RTSPTransportField *th;
2803 for(i=0;i<h->nb_transports;i++) {
2804 th = &h->transports[i];
2805 if (th->protocol == protocol)
2811 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2815 int stream_index, port;
2820 RTSPTransportField *th;
2821 struct sockaddr_in dest_addr;
2822 RTSPActionServerSetup setup;
2824 /* find which url is asked */
2825 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2830 /* now check each stream */
2831 for(stream = first_stream; stream != NULL; stream = stream->next) {
2832 if (!stream->is_feed &&
2833 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2834 /* accept aggregate filenames only if single stream */
2835 if (!strcmp(path, stream->filename)) {
2836 if (stream->nb_streams != 1) {
2837 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2844 for(stream_index = 0; stream_index < stream->nb_streams;
2846 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2847 stream->filename, stream_index);
2848 if (!strcmp(path, buf))
2853 /* no stream found */
2854 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2858 /* generate session id if needed */
2859 if (h->session_id[0] == '\0')
2860 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2861 av_random(&random_state), av_random(&random_state));
2863 /* find rtp session, and create it if none found */
2864 rtp_c = find_rtp_session(h->session_id);
2866 /* always prefer UDP */
2867 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2869 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2871 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2876 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2879 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2883 /* open input stream */
2884 if (open_input_stream(rtp_c, "") < 0) {
2885 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2890 /* test if stream is OK (test needed because several SETUP needs
2891 to be done for a given file) */
2892 if (rtp_c->stream != stream) {
2893 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2897 /* test if stream is already set up */
2898 if (rtp_c->rtp_ctx[stream_index]) {
2899 rtsp_reply_error(c, RTSP_STATUS_STATE);
2903 /* check transport */
2904 th = find_transport(h, rtp_c->rtp_protocol);
2905 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2906 th->client_port_min <= 0)) {
2907 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2911 /* setup default options */
2912 setup.transport_option[0] = '\0';
2913 dest_addr = rtp_c->from_addr;
2914 dest_addr.sin_port = htons(th->client_port_min);
2917 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2918 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2922 /* now everything is OK, so we can send the connection parameters */
2923 rtsp_reply_header(c, RTSP_STATUS_OK);
2925 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2927 switch(rtp_c->rtp_protocol) {
2928 case RTSP_PROTOCOL_RTP_UDP:
2929 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2930 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2931 "client_port=%d-%d;server_port=%d-%d",
2932 th->client_port_min, th->client_port_min + 1,
2935 case RTSP_PROTOCOL_RTP_TCP:
2936 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2937 stream_index * 2, stream_index * 2 + 1);
2942 if (setup.transport_option[0] != '\0')
2943 url_fprintf(c->pb, ";%s", setup.transport_option);
2944 url_fprintf(c->pb, "\r\n");
2947 url_fprintf(c->pb, "\r\n");
2951 /* find an rtp connection by using the session ID. Check consistency
2953 static HTTPContext *find_rtp_session_with_url(const char *url,
2954 const char *session_id)
2962 rtp_c = find_rtp_session(session_id);
2966 /* find which url is asked */
2967 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2971 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2972 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2973 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2974 rtp_c->stream->filename, s);
2975 if(!strncmp(path, buf, sizeof(buf))) {
2976 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2983 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2987 rtp_c = find_rtp_session_with_url(url, h->session_id);
2989 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2993 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2994 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2995 rtp_c->state != HTTPSTATE_READY) {
2996 rtsp_reply_error(c, RTSP_STATUS_STATE);
3001 /* XXX: seek in stream */
3002 if (h->range_start != AV_NOPTS_VALUE) {
3003 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3004 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3008 rtp_c->state = HTTPSTATE_SEND_DATA;
3010 /* now everything is OK, so we can send the connection parameters */
3011 rtsp_reply_header(c, RTSP_STATUS_OK);
3013 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3014 url_fprintf(c->pb, "\r\n");
3017 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3021 rtp_c = find_rtp_session_with_url(url, h->session_id);
3023 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3027 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3028 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3029 rtsp_reply_error(c, RTSP_STATUS_STATE);
3033 rtp_c->state = HTTPSTATE_READY;
3034 rtp_c->first_pts = AV_NOPTS_VALUE;
3035 /* now everything is OK, so we can send the connection parameters */
3036 rtsp_reply_header(c, RTSP_STATUS_OK);
3038 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3039 url_fprintf(c->pb, "\r\n");
3042 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3045 char session_id[32];
3047 rtp_c = find_rtp_session_with_url(url, h->session_id);
3049 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3053 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3055 /* abort the session */
3056 close_connection(rtp_c);
3058 /* now everything is OK, so we can send the connection parameters */
3059 rtsp_reply_header(c, RTSP_STATUS_OK);
3061 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3062 url_fprintf(c->pb, "\r\n");
3066 /********************************************************************/
3069 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3070 FFStream *stream, const char *session_id,
3071 enum RTSPProtocol rtp_protocol)
3073 HTTPContext *c = NULL;
3074 const char *proto_str;
3076 /* XXX: should output a warning page when coming
3077 close to the connection limit */
3078 if (nb_connections >= nb_max_connections)
3081 /* add a new connection */
3082 c = av_mallocz(sizeof(HTTPContext));
3087 c->poll_entry = NULL;
3088 c->from_addr = *from_addr;
3089 c->buffer_size = IOBUFFER_INIT_SIZE;
3090 c->buffer = av_malloc(c->buffer_size);
3095 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3096 c->state = HTTPSTATE_READY;
3097 c->is_packetized = 1;
3098 c->rtp_protocol = rtp_protocol;
3100 /* protocol is shown in statistics */
3101 switch(c->rtp_protocol) {
3102 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3103 proto_str = "MCAST";
3105 case RTSP_PROTOCOL_RTP_UDP:
3108 case RTSP_PROTOCOL_RTP_TCP:
3115 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3116 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3118 current_bandwidth += stream->bandwidth;
3120 c->next = first_http_ctx;
3132 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3133 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3135 static int rtp_new_av_stream(HTTPContext *c,
3136 int stream_index, struct sockaddr_in *dest_addr,
3137 HTTPContext *rtsp_c)
3139 AVFormatContext *ctx;
3142 URLContext *h = NULL;
3144 int max_packet_size;
3146 /* now we can open the relevant output stream */
3147 ctx = av_alloc_format_context();
3150 ctx->oformat = guess_format("rtp", NULL, NULL);
3152 st = av_mallocz(sizeof(AVStream));
3155 st->codec= avcodec_alloc_context();
3156 ctx->nb_streams = 1;
3157 ctx->streams[0] = st;
3159 if (!c->stream->feed ||
3160 c->stream->feed == c->stream)
3161 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3164 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3166 st->priv_data = NULL;
3168 /* build destination RTP address */
3169 ipaddr = inet_ntoa(dest_addr->sin_addr);
3171 switch(c->rtp_protocol) {
3172 case RTSP_PROTOCOL_RTP_UDP:
3173 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3176 /* XXX: also pass as parameter to function ? */
3177 if (c->stream->is_multicast) {
3179 ttl = c->stream->multicast_ttl;
3182 snprintf(ctx->filename, sizeof(ctx->filename),
3183 "rtp://%s:%d?multicast=1&ttl=%d",
3184 ipaddr, ntohs(dest_addr->sin_port), ttl);
3186 snprintf(ctx->filename, sizeof(ctx->filename),
3187 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3190 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3192 c->rtp_handles[stream_index] = h;
3193 max_packet_size = url_get_max_packet_size(h);
3195 case RTSP_PROTOCOL_RTP_TCP:
3198 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3204 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3205 ipaddr, ntohs(dest_addr->sin_port),
3206 c->stream->filename, stream_index, c->protocol);
3208 /* normally, no packets should be output here, but the packet size may be checked */
3209 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3210 /* XXX: close stream */
3213 av_set_parameters(ctx, NULL);
3214 if (av_write_header(ctx) < 0) {
3221 url_close_dyn_buf(ctx->pb, &dummy_buf);
3224 c->rtp_ctx[stream_index] = ctx;
3228 /********************************************************************/
3229 /* ffserver initialization */
3231 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3235 fst = av_mallocz(sizeof(AVStream));
3238 fst->codec= avcodec_alloc_context();
3239 fst->priv_data = av_mallocz(sizeof(FeedData));
3240 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3241 fst->index = stream->nb_streams;
3242 av_set_pts_info(fst, 33, 1, 90000);
3243 stream->streams[stream->nb_streams++] = fst;
3247 /* return the stream number in the feed */
3248 static int add_av_stream(FFStream *feed, AVStream *st)
3251 AVCodecContext *av, *av1;
3255 for(i=0;i<feed->nb_streams;i++) {
3256 st = feed->streams[i];
3258 if (av1->codec_id == av->codec_id &&
3259 av1->codec_type == av->codec_type &&
3260 av1->bit_rate == av->bit_rate) {
3262 switch(av->codec_type) {
3263 case CODEC_TYPE_AUDIO:
3264 if (av1->channels == av->channels &&
3265 av1->sample_rate == av->sample_rate)
3268 case CODEC_TYPE_VIDEO:
3269 if (av1->width == av->width &&
3270 av1->height == av->height &&
3271 av1->time_base.den == av->time_base.den &&
3272 av1->time_base.num == av->time_base.num &&
3273 av1->gop_size == av->gop_size)
3282 fst = add_av_stream1(feed, av);
3285 return feed->nb_streams - 1;
3290 static void remove_stream(FFStream *stream)
3294 while (*ps != NULL) {
3302 /* specific mpeg4 handling : we extract the raw parameters */
3303 static void extract_mpeg4_header(AVFormatContext *infile)
3305 int mpeg4_count, i, size;
3311 for(i=0;i<infile->nb_streams;i++) {
3312 st = infile->streams[i];
3313 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3314 st->codec->extradata_size == 0) {
3321 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3322 while (mpeg4_count > 0) {
3323 if (av_read_packet(infile, &pkt) < 0)
3325 st = infile->streams[pkt.stream_index];
3326 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3327 st->codec->extradata_size == 0) {
3328 av_freep(&st->codec->extradata);
3329 /* fill extradata with the header */
3330 /* XXX: we make hard suppositions here ! */
3332 while (p < pkt.data + pkt.size - 4) {
3333 /* stop when vop header is found */
3334 if (p[0] == 0x00 && p[1] == 0x00 &&
3335 p[2] == 0x01 && p[3] == 0xb6) {
3336 size = p - pkt.data;
3337 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3338 st->codec->extradata = av_malloc(size);
3339 st->codec->extradata_size = size;
3340 memcpy(st->codec->extradata, pkt.data, size);
3347 av_free_packet(&pkt);
3351 /* compute the needed AVStream for each file */
3352 static void build_file_streams(void)
3354 FFStream *stream, *stream_next;
3355 AVFormatContext *infile;
3358 /* gather all streams */
3359 for(stream = first_stream; stream != NULL; stream = stream_next) {
3360 stream_next = stream->next;
3361 if (stream->stream_type == STREAM_TYPE_LIVE &&
3363 /* the stream comes from a file */
3364 /* try to open the file */
3366 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3367 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3368 /* specific case : if transport stream output to RTP,
3369 we use a raw transport stream reader */
3370 stream->ap_in->mpeg2ts_raw = 1;
3371 stream->ap_in->mpeg2ts_compute_pcr = 1;
3374 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3375 stream->ifmt, 0, stream->ap_in)) < 0) {
3376 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3377 /* remove stream (no need to spend more time on it) */
3379 remove_stream(stream);
3381 /* find all the AVStreams inside and reference them in
3383 if (av_find_stream_info(infile) < 0) {
3384 http_log("Could not find codec parameters from '%s'\n",
3385 stream->feed_filename);
3386 av_close_input_file(infile);
3389 extract_mpeg4_header(infile);
3391 for(i=0;i<infile->nb_streams;i++)
3392 add_av_stream1(stream, infile->streams[i]->codec);
3394 av_close_input_file(infile);
3400 /* compute the needed AVStream for each feed */
3401 static void build_feed_streams(void)
3403 FFStream *stream, *feed;
3406 /* gather all streams */
3407 for(stream = first_stream; stream != NULL; stream = stream->next) {
3408 feed = stream->feed;
3410 if (!stream->is_feed) {
3411 /* we handle a stream coming from a feed */
3412 for(i=0;i<stream->nb_streams;i++)
3413 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3418 /* gather all streams */
3419 for(stream = first_stream; stream != NULL; stream = stream->next) {
3420 feed = stream->feed;
3422 if (stream->is_feed) {
3423 for(i=0;i<stream->nb_streams;i++)
3424 stream->feed_streams[i] = i;
3429 /* create feed files if needed */
3430 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3433 if (url_exist(feed->feed_filename)) {
3434 /* See if it matches */
3438 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3439 /* Now see if it matches */
3440 if (s->nb_streams == feed->nb_streams) {
3442 for(i=0;i<s->nb_streams;i++) {
3444 sf = feed->streams[i];
3447 if (sf->index != ss->index ||
3449 printf("Index & Id do not match for stream %d (%s)\n",
3450 i, feed->feed_filename);
3453 AVCodecContext *ccf, *ccs;
3457 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3459 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3460 printf("Codecs do not match for stream %d\n", i);
3462 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3463 printf("Codec bitrates do not match for stream %d\n", i);
3465 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3466 if (CHECK_CODEC(time_base.den) ||
3467 CHECK_CODEC(time_base.num) ||
3468 CHECK_CODEC(width) ||
3469 CHECK_CODEC(height)) {
3470 printf("Codec width, height and framerate do not match for stream %d\n", i);
3473 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3474 if (CHECK_CODEC(sample_rate) ||
3475 CHECK_CODEC(channels) ||
3476 CHECK_CODEC(frame_size)) {
3477 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3481 printf("Unknown codec type\n");
3489 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3490 feed->feed_filename, s->nb_streams, feed->nb_streams);
3492 av_close_input_file(s);
3494 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3495 feed->feed_filename);
3498 if (feed->readonly) {
3499 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3500 feed->feed_filename);
3503 unlink(feed->feed_filename);
3506 if (!url_exist(feed->feed_filename)) {
3507 AVFormatContext s1, *s = &s1;
3509 if (feed->readonly) {
3510 printf("Unable to create feed file '%s' as it is marked readonly\n",
3511 feed->feed_filename);
3515 /* only write the header of the ffm file */
3516 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3517 http_log("Could not open output feed file '%s'\n",
3518 feed->feed_filename);
3521 s->oformat = feed->fmt;
3522 s->nb_streams = feed->nb_streams;
3523 for(i=0;i<s->nb_streams;i++) {
3525 st = feed->streams[i];
3528 av_set_parameters(s, NULL);
3529 if (av_write_header(s) < 0) {
3530 http_log("Container doesn't supports the required parameters\n");
3533 /* XXX: need better api */
3534 av_freep(&s->priv_data);
3537 /* get feed size and write index */
3538 fd = open(feed->feed_filename, O_RDONLY);
3540 http_log("Could not open output feed file '%s'\n",
3541 feed->feed_filename);
3545 feed->feed_write_index = ffm_read_write_index(fd);
3546 feed->feed_size = lseek(fd, 0, SEEK_END);
3547 /* ensure that we do not wrap before the end of file */
3548 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3549 feed->feed_max_size = feed->feed_size;
3555 /* compute the bandwidth used by each stream */
3556 static void compute_bandwidth(void)
3562 for(stream = first_stream; stream != NULL; stream = stream->next) {
3564 for(i=0;i<stream->nb_streams;i++) {
3565 AVStream *st = stream->streams[i];
3566 switch(st->codec->codec_type) {
3567 case CODEC_TYPE_AUDIO:
3568 case CODEC_TYPE_VIDEO:
3569 bandwidth += st->codec->bit_rate;
3575 stream->bandwidth = (bandwidth + 999) / 1000;
3579 static void get_arg(char *buf, int buf_size, const char **pp)
3586 while (isspace(*p)) p++;
3589 if (*p == '\"' || *p == '\'')
3601 if ((q - buf) < buf_size - 1)
3606 if (quote && *p == quote)
3611 /* add a codec and set the default parameters */
3612 static void add_codec(FFStream *stream, AVCodecContext *av)
3616 /* compute default parameters */
3617 switch(av->codec_type) {
3618 case CODEC_TYPE_AUDIO:
3619 if (av->bit_rate == 0)
3620 av->bit_rate = 64000;
3621 if (av->sample_rate == 0)
3622 av->sample_rate = 22050;
3623 if (av->channels == 0)
3626 case CODEC_TYPE_VIDEO:
3627 if (av->bit_rate == 0)
3628 av->bit_rate = 64000;
3629 if (av->time_base.num == 0){
3630 av->time_base.den = 5;
3631 av->time_base.num = 1;
3633 if (av->width == 0 || av->height == 0) {
3637 /* Bitrate tolerance is less for streaming */
3638 if (av->bit_rate_tolerance == 0)
3639 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3640 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3645 if (av->max_qdiff == 0)
3647 av->qcompress = 0.5;
3650 if (!av->nsse_weight)
3651 av->nsse_weight = 8;
3653 av->frame_skip_cmp = FF_CMP_DCTMAX;
3654 av->me_method = ME_EPZS;
3655 av->rc_buffer_aggressivity = 1.0;
3658 av->rc_eq = "tex^qComp";
3659 if (!av->i_quant_factor)
3660 av->i_quant_factor = -0.8;
3661 if (!av->b_quant_factor)
3662 av->b_quant_factor = 1.25;
3663 if (!av->b_quant_offset)
3664 av->b_quant_offset = 1.25;
3665 if (!av->rc_max_rate)
3666 av->rc_max_rate = av->bit_rate * 2;
3668 if (av->rc_max_rate && !av->rc_buffer_size) {
3669 av->rc_buffer_size = av->rc_max_rate;
3678 st = av_mallocz(sizeof(AVStream));
3681 st->codec = avcodec_alloc_context();
3682 stream->streams[stream->nb_streams++] = st;
3683 memcpy(st->codec, av, sizeof(AVCodecContext));
3686 static int opt_audio_codec(const char *arg)
3688 AVCodec *p= avcodec_find_encoder_by_name(arg);
3690 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3691 return CODEC_ID_NONE;
3696 static int opt_video_codec(const char *arg)
3698 AVCodec *p= avcodec_find_encoder_by_name(arg);
3700 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3701 return CODEC_ID_NONE;
3706 /* simplistic plugin support */
3709 static void load_module(const char *filename)
3712 void (*init_func)(void);
3713 dll = dlopen(filename, RTLD_NOW);
3715 fprintf(stderr, "Could not load module '%s' - %s\n",
3716 filename, dlerror());
3720 init_func = dlsym(dll, "ffserver_module_init");
3723 "%s: init function 'ffserver_module_init()' not found\n",
3732 static int opt_default(const char *opt, const char *arg,
3733 AVCodecContext *avctx, int type)
3735 const AVOption *o = NULL;
3736 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3738 o = av_set_string(avctx, opt, arg);
3744 static int parse_ffconfig(const char *filename)
3751 int val, errors, line_num;
3752 FFStream **last_stream, *stream, *redirect;
3753 FFStream **last_feed, *feed;
3754 AVCodecContext audio_enc, video_enc;
3755 int audio_id, video_id;
3757 f = fopen(filename, "r");
3765 first_stream = NULL;
3766 last_stream = &first_stream;
3768 last_feed = &first_feed;
3772 audio_id = CODEC_ID_NONE;
3773 video_id = CODEC_ID_NONE;
3775 if (fgets(line, sizeof(line), f) == NULL)
3781 if (*p == '\0' || *p == '#')
3784 get_arg(cmd, sizeof(cmd), &p);
3786 if (!strcasecmp(cmd, "Port")) {
3787 get_arg(arg, sizeof(arg), &p);
3789 if (val < 1 || val > 65536) {
3790 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3791 filename, line_num, arg);
3794 my_http_addr.sin_port = htons(val);
3795 } else if (!strcasecmp(cmd, "BindAddress")) {
3796 get_arg(arg, sizeof(arg), &p);
3797 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3798 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3799 filename, line_num, arg);
3802 } else if (!strcasecmp(cmd, "NoDaemon")) {
3803 ffserver_daemon = 0;
3804 } else if (!strcasecmp(cmd, "RTSPPort")) {
3805 get_arg(arg, sizeof(arg), &p);
3807 if (val < 1 || val > 65536) {
3808 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3809 filename, line_num, arg);
3812 my_rtsp_addr.sin_port = htons(atoi(arg));
3813 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3814 get_arg(arg, sizeof(arg), &p);
3815 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3816 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3817 filename, line_num, arg);
3820 } else if (!strcasecmp(cmd, "MaxClients")) {
3821 get_arg(arg, sizeof(arg), &p);
3823 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3824 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3825 filename, line_num, arg);
3828 nb_max_connections = val;
3830 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3832 get_arg(arg, sizeof(arg), &p);
3834 if (llval < 10 || llval > 10000000) {
3835 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3836 filename, line_num, arg);
3839 max_bandwidth = llval;
3840 } else if (!strcasecmp(cmd, "CustomLog")) {
3841 if (!ffserver_debug)
3842 get_arg(logfilename, sizeof(logfilename), &p);
3843 } else if (!strcasecmp(cmd, "<Feed")) {
3844 /*********************************************/
3845 /* Feed related options */
3847 if (stream || feed) {
3848 fprintf(stderr, "%s:%d: Already in a tag\n",
3849 filename, line_num);
3851 feed = av_mallocz(sizeof(FFStream));
3852 /* add in stream list */
3853 *last_stream = feed;
3854 last_stream = &feed->next;
3855 /* add in feed list */
3857 last_feed = &feed->next_feed;
3859 get_arg(feed->filename, sizeof(feed->filename), &p);
3860 q = strrchr(feed->filename, '>');
3863 feed->fmt = guess_format("ffm", NULL, NULL);
3864 /* defaut feed file */
3865 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3866 "/tmp/%s.ffm", feed->filename);
3867 feed->feed_max_size = 5 * 1024 * 1024;
3869 feed->feed = feed; /* self feeding :-) */
3871 } else if (!strcasecmp(cmd, "Launch")) {
3875 feed->child_argv = av_mallocz(64 * sizeof(char *));
3877 for (i = 0; i < 62; i++) {
3878 get_arg(arg, sizeof(arg), &p);
3882 feed->child_argv[i] = av_strdup(arg);
3885 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3887 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3889 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3890 inet_ntoa(my_http_addr.sin_addr),
3891 ntohs(my_http_addr.sin_port), feed->filename);
3893 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3895 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3897 } else if (stream) {
3898 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3900 } else if (!strcasecmp(cmd, "File")) {
3902 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3904 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3905 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3910 get_arg(arg, sizeof(arg), &p);
3912 fsize = strtod(p1, &p1);
3913 switch(toupper(*p1)) {
3918 fsize *= 1024 * 1024;
3921 fsize *= 1024 * 1024 * 1024;
3924 feed->feed_max_size = (int64_t)fsize;
3926 } else if (!strcasecmp(cmd, "</Feed>")) {
3928 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3929 filename, line_num);
3933 } else if (!strcasecmp(cmd, "<Stream")) {
3934 /*********************************************/
3935 /* Stream related options */
3937 if (stream || feed) {
3938 fprintf(stderr, "%s:%d: Already in a tag\n",
3939 filename, line_num);
3941 const AVClass *class;
3942 stream = av_mallocz(sizeof(FFStream));
3943 *last_stream = stream;
3944 last_stream = &stream->next;
3946 get_arg(stream->filename, sizeof(stream->filename), &p);
3947 q = strrchr(stream->filename, '>');
3950 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3951 /* fetch avclass so AVOption works
3952 * FIXME try to use avcodec_get_context_defaults2
3953 * without changing defaults too much */
3954 avcodec_get_context_defaults(&video_enc);
3955 class = video_enc.av_class;
3956 memset(&audio_enc, 0, sizeof(AVCodecContext));
3957 memset(&video_enc, 0, sizeof(AVCodecContext));
3958 audio_enc.av_class = class;
3959 video_enc.av_class = class;
3960 audio_id = CODEC_ID_NONE;
3961 video_id = CODEC_ID_NONE;
3963 audio_id = stream->fmt->audio_codec;
3964 video_id = stream->fmt->video_codec;
3967 } else if (!strcasecmp(cmd, "Feed")) {
3968 get_arg(arg, sizeof(arg), &p);
3973 while (sfeed != NULL) {
3974 if (!strcmp(sfeed->filename, arg))
3976 sfeed = sfeed->next_feed;
3979 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3980 filename, line_num, arg);
3982 stream->feed = sfeed;
3984 } else if (!strcasecmp(cmd, "Format")) {
3985 get_arg(arg, sizeof(arg), &p);
3987 if (!strcmp(arg, "status")) {
3988 stream->stream_type = STREAM_TYPE_STATUS;
3991 stream->stream_type = STREAM_TYPE_LIVE;
3992 /* jpeg cannot be used here, so use single frame jpeg */
3993 if (!strcmp(arg, "jpeg"))
3994 strcpy(arg, "mjpeg");
3995 stream->fmt = guess_stream_format(arg, NULL, NULL);
3997 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3998 filename, line_num, arg);
4003 audio_id = stream->fmt->audio_codec;
4004 video_id = stream->fmt->video_codec;
4007 } else if (!strcasecmp(cmd, "InputFormat")) {
4008 get_arg(arg, sizeof(arg), &p);
4009 stream->ifmt = av_find_input_format(arg);
4010 if (!stream->ifmt) {
4011 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4012 filename, line_num, arg);
4014 } else if (!strcasecmp(cmd, "FaviconURL")) {
4015 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4016 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4018 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4019 filename, line_num);
4022 } else if (!strcasecmp(cmd, "Author")) {
4024 get_arg(stream->author, sizeof(stream->author), &p);
4025 } else if (!strcasecmp(cmd, "Comment")) {
4027 get_arg(stream->comment, sizeof(stream->comment), &p);
4028 } else if (!strcasecmp(cmd, "Copyright")) {
4030 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4031 } else if (!strcasecmp(cmd, "Title")) {
4033 get_arg(stream->title, sizeof(stream->title), &p);
4034 } else if (!strcasecmp(cmd, "Preroll")) {
4035 get_arg(arg, sizeof(arg), &p);
4037 stream->prebuffer = atof(arg) * 1000;
4038 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4040 stream->send_on_key = 1;
4041 } else if (!strcasecmp(cmd, "AudioCodec")) {
4042 get_arg(arg, sizeof(arg), &p);
4043 audio_id = opt_audio_codec(arg);
4044 if (audio_id == CODEC_ID_NONE) {
4045 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4046 filename, line_num, arg);
4049 } else if (!strcasecmp(cmd, "VideoCodec")) {
4050 get_arg(arg, sizeof(arg), &p);
4051 video_id = opt_video_codec(arg);
4052 if (video_id == CODEC_ID_NONE) {
4053 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4054 filename, line_num, arg);
4057 } else if (!strcasecmp(cmd, "MaxTime")) {
4058 get_arg(arg, sizeof(arg), &p);
4060 stream->max_time = atof(arg) * 1000;
4061 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4062 get_arg(arg, sizeof(arg), &p);
4064 audio_enc.bit_rate = atoi(arg) * 1000;
4065 } else if (!strcasecmp(cmd, "AudioChannels")) {
4066 get_arg(arg, sizeof(arg), &p);
4068 audio_enc.channels = atoi(arg);
4069 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4070 get_arg(arg, sizeof(arg), &p);
4072 audio_enc.sample_rate = atoi(arg);
4073 } else if (!strcasecmp(cmd, "AudioQuality")) {
4074 get_arg(arg, sizeof(arg), &p);
4076 // audio_enc.quality = atof(arg) * 1000;
4078 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4080 int minrate, maxrate;
4082 get_arg(arg, sizeof(arg), &p);
4084 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4085 video_enc.rc_min_rate = minrate * 1000;
4086 video_enc.rc_max_rate = maxrate * 1000;
4088 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4089 filename, line_num, arg);
4093 } else if (!strcasecmp(cmd, "Debug")) {
4095 get_arg(arg, sizeof(arg), &p);
4096 video_enc.debug = strtol(arg,0,0);
4098 } else if (!strcasecmp(cmd, "Strict")) {
4100 get_arg(arg, sizeof(arg), &p);
4101 video_enc.strict_std_compliance = atoi(arg);
4103 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4105 get_arg(arg, sizeof(arg), &p);
4106 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4108 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4110 get_arg(arg, sizeof(arg), &p);
4111 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4113 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4114 get_arg(arg, sizeof(arg), &p);
4116 video_enc.bit_rate = atoi(arg) * 1000;
4118 } else if (!strcasecmp(cmd, "VideoSize")) {
4119 get_arg(arg, sizeof(arg), &p);
4121 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4122 if ((video_enc.width % 16) != 0 ||
4123 (video_enc.height % 16) != 0) {
4124 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4125 filename, line_num);
4129 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4130 get_arg(arg, sizeof(arg), &p);
4132 AVRational frame_rate;
4133 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4134 fprintf(stderr, "Incorrect frame rate\n");
4137 video_enc.time_base.num = frame_rate.den;
4138 video_enc.time_base.den = frame_rate.num;
4141 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4142 get_arg(arg, sizeof(arg), &p);
4144 video_enc.gop_size = atoi(arg);
4145 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4147 video_enc.gop_size = 1;
4148 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4150 video_enc.mb_decision = FF_MB_DECISION_BITS;
4151 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4153 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4154 video_enc.flags |= CODEC_FLAG_4MV;
4156 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4157 !strcasecmp(cmd, "AVOptionAudio")) {
4159 AVCodecContext *avctx;
4161 get_arg(arg, sizeof(arg), &p);
4162 get_arg(arg2, sizeof(arg2), &p);
4163 if (!strcasecmp(cmd, "AVOptionVideo")) {
4165 type = AV_OPT_FLAG_VIDEO_PARAM;
4168 type = AV_OPT_FLAG_AUDIO_PARAM;
4170 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4171 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4174 } else if (!strcasecmp(cmd, "VideoTag")) {
4175 get_arg(arg, sizeof(arg), &p);
4176 if ((strlen(arg) == 4) && stream)
4177 video_enc.codec_tag = ff_get_fourcc(arg);
4178 } else if (!strcasecmp(cmd, "BitExact")) {
4180 video_enc.flags |= CODEC_FLAG_BITEXACT;
4181 } else if (!strcasecmp(cmd, "DctFastint")) {
4183 video_enc.dct_algo = FF_DCT_FASTINT;
4184 } else if (!strcasecmp(cmd, "IdctSimple")) {
4186 video_enc.idct_algo = FF_IDCT_SIMPLE;
4187 } else if (!strcasecmp(cmd, "Qscale")) {
4188 get_arg(arg, sizeof(arg), &p);
4190 video_enc.flags |= CODEC_FLAG_QSCALE;
4191 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4193 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4194 get_arg(arg, sizeof(arg), &p);
4196 video_enc.max_qdiff = atoi(arg);
4197 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4198 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4199 filename, line_num);
4203 } else if (!strcasecmp(cmd, "VideoQMax")) {
4204 get_arg(arg, sizeof(arg), &p);
4206 video_enc.qmax = atoi(arg);
4207 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4208 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4209 filename, line_num);
4213 } else if (!strcasecmp(cmd, "VideoQMin")) {
4214 get_arg(arg, sizeof(arg), &p);
4216 video_enc.qmin = atoi(arg);
4217 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4218 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4219 filename, line_num);
4223 } else if (!strcasecmp(cmd, "LumaElim")) {
4224 get_arg(arg, sizeof(arg), &p);
4226 video_enc.luma_elim_threshold = atoi(arg);
4227 } else if (!strcasecmp(cmd, "ChromaElim")) {
4228 get_arg(arg, sizeof(arg), &p);
4230 video_enc.chroma_elim_threshold = atoi(arg);
4231 } else if (!strcasecmp(cmd, "LumiMask")) {
4232 get_arg(arg, sizeof(arg), &p);
4234 video_enc.lumi_masking = atof(arg);
4235 } else if (!strcasecmp(cmd, "DarkMask")) {
4236 get_arg(arg, sizeof(arg), &p);
4238 video_enc.dark_masking = atof(arg);
4239 } else if (!strcasecmp(cmd, "NoVideo")) {
4240 video_id = CODEC_ID_NONE;
4241 } else if (!strcasecmp(cmd, "NoAudio")) {
4242 audio_id = CODEC_ID_NONE;
4243 } else if (!strcasecmp(cmd, "ACL")) {
4246 get_arg(arg, sizeof(arg), &p);
4247 if (strcasecmp(arg, "allow") == 0)
4248 acl.action = IP_ALLOW;
4249 else if (strcasecmp(arg, "deny") == 0)
4250 acl.action = IP_DENY;
4252 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4253 filename, line_num, arg);
4257 get_arg(arg, sizeof(arg), &p);
4259 if (resolve_host(&acl.first, arg) != 0) {
4260 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4261 filename, line_num, arg);
4264 acl.last = acl.first;
4266 get_arg(arg, sizeof(arg), &p);
4269 if (resolve_host(&acl.last, arg) != 0) {
4270 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4271 filename, line_num, arg);
4277 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4278 IPAddressACL **naclp = 0;
4284 naclp = &stream->acl;
4288 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4289 filename, line_num);
4295 naclp = &(*naclp)->next;
4300 } else if (!strcasecmp(cmd, "RTSPOption")) {
4301 get_arg(arg, sizeof(arg), &p);
4303 av_freep(&stream->rtsp_option);
4304 stream->rtsp_option = av_strdup(arg);
4306 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4307 get_arg(arg, sizeof(arg), &p);
4309 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4310 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4311 filename, line_num, arg);
4314 stream->is_multicast = 1;
4315 stream->loop = 1; /* default is looping */
4317 } else if (!strcasecmp(cmd, "MulticastPort")) {
4318 get_arg(arg, sizeof(arg), &p);
4320 stream->multicast_port = atoi(arg);
4321 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4322 get_arg(arg, sizeof(arg), &p);
4324 stream->multicast_ttl = atoi(arg);
4325 } else if (!strcasecmp(cmd, "NoLoop")) {
4328 } else if (!strcasecmp(cmd, "</Stream>")) {
4330 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4331 filename, line_num);
4334 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4335 if (audio_id != CODEC_ID_NONE) {
4336 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4337 audio_enc.codec_id = audio_id;
4338 add_codec(stream, &audio_enc);
4340 if (video_id != CODEC_ID_NONE) {
4341 video_enc.codec_type = CODEC_TYPE_VIDEO;
4342 video_enc.codec_id = video_id;
4343 add_codec(stream, &video_enc);
4348 } else if (!strcasecmp(cmd, "<Redirect")) {
4349 /*********************************************/
4351 if (stream || feed || redirect) {
4352 fprintf(stderr, "%s:%d: Already in a tag\n",
4353 filename, line_num);
4356 redirect = av_mallocz(sizeof(FFStream));
4357 *last_stream = redirect;
4358 last_stream = &redirect->next;
4360 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4361 q = strrchr(redirect->filename, '>');
4364 redirect->stream_type = STREAM_TYPE_REDIRECT;
4366 } else if (!strcasecmp(cmd, "URL")) {
4368 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4369 } else if (!strcasecmp(cmd, "</Redirect>")) {
4371 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4372 filename, line_num);
4375 if (!redirect->feed_filename[0]) {
4376 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4377 filename, line_num);
4382 } else if (!strcasecmp(cmd, "LoadModule")) {
4383 get_arg(arg, sizeof(arg), &p);
4387 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4388 filename, line_num, arg);
4392 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4393 filename, line_num, cmd);
4405 static void handle_child_exit(int sig)
4410 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4413 for (feed = first_feed; feed; feed = feed->next) {
4414 if (feed->pid == pid) {
4415 int uptime = time(0) - feed->pid_start;
4418 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4421 /* Turn off any more restarts */
4422 feed->child_argv = 0;
4427 need_to_start_children = 1;
4430 static void opt_debug()
4433 ffserver_daemon = 0;
4434 logfilename[0] = '-';
4437 static void opt_show_help(void)
4439 printf("usage: ffserver [options]\n"
4440 "Hyper fast multi format Audio/Video streaming server\n");
4442 show_help_options(options, "Main options:\n", 0, 0);
4445 static const OptionDef options[] = {
4446 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4447 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4448 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4449 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4450 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4451 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4452 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4456 int main(int argc, char **argv)
4458 struct sigaction sigact;
4464 config_filename = "/etc/ffserver.conf";
4466 my_program_name = argv[0];
4467 my_program_dir = getcwd(0, 0);
4468 ffserver_daemon = 1;
4470 parse_options(argc, argv, options, NULL);
4472 unsetenv("http_proxy"); /* Kill the http_proxy */
4474 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4476 memset(&sigact, 0, sizeof(sigact));
4477 sigact.sa_handler = handle_child_exit;
4478 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4479 sigaction(SIGCHLD, &sigact, 0);
4481 if (parse_ffconfig(config_filename) < 0) {
4482 fprintf(stderr, "Incorrect config file - exiting.\n");
4486 build_file_streams();
4488 build_feed_streams();
4490 compute_bandwidth();
4492 /* put the process in background and detach it from its TTY */
4493 if (ffserver_daemon) {
4500 } else if (pid > 0) {
4507 open("/dev/null", O_RDWR);
4508 if (strcmp(logfilename, "-") != 0) {
4518 signal(SIGPIPE, SIG_IGN);
4520 /* open log file if needed */
4521 if (logfilename[0] != '\0') {
4522 if (!strcmp(logfilename, "-"))
4525 logfile = fopen(logfilename, "a");
4526 av_log_set_callback(http_av_log);
4529 if (ffserver_daemon)
4532 if (http_server() < 0) {
4533 http_log("Could not start server\n");