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 for (i = 3; i < 256; i++)
413 if (!ffserver_debug) {
414 i = open("/dev/null", O_RDWR);
423 av_strlcpy(pathname, my_program_name, sizeof(pathname));
425 slash = strrchr(pathname, '/');
430 strcpy(slash, "ffmpeg");
432 /* This is needed to make relative pathnames work */
433 chdir(my_program_dir);
435 signal(SIGPIPE, SIG_DFL);
437 execvp(pathname, feed->child_argv);
445 /* open a listening socket */
446 static int socket_open_listen(struct sockaddr_in *my_addr)
450 server_fd = socket(AF_INET,SOCK_STREAM,0);
457 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
459 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
461 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
463 closesocket(server_fd);
467 if (listen (server_fd, 5) < 0) {
469 closesocket(server_fd);
472 ff_socket_nonblock(server_fd, 1);
477 /* start all multicast streams */
478 static void start_multicast(void)
483 struct sockaddr_in dest_addr;
484 int default_port, stream_index;
487 for(stream = first_stream; stream != NULL; stream = stream->next) {
488 if (stream->is_multicast) {
489 /* open the RTP connection */
490 snprintf(session_id, sizeof(session_id), "%08x%08x",
491 av_random(&random_state), av_random(&random_state));
493 /* choose a port if none given */
494 if (stream->multicast_port == 0) {
495 stream->multicast_port = default_port;
499 dest_addr.sin_family = AF_INET;
500 dest_addr.sin_addr = stream->multicast_ip;
501 dest_addr.sin_port = htons(stream->multicast_port);
503 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
504 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
508 if (open_input_stream(rtp_c, "") < 0) {
509 http_log("Could not open input stream for stream '%s'\n",
514 /* open each RTP stream */
515 for(stream_index = 0; stream_index < stream->nb_streams;
517 dest_addr.sin_port = htons(stream->multicast_port +
519 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
520 http_log("Could not open output stream '%s/streamid=%d'\n",
521 stream->filename, stream_index);
526 /* change state to send data */
527 rtp_c->state = HTTPSTATE_SEND_DATA;
532 /* main loop of the http server */
533 static int http_server(void)
535 int server_fd = 0, rtsp_server_fd = 0;
536 int ret, delay, delay1;
537 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
538 HTTPContext *c, *c_next;
540 if (my_http_addr.sin_port) {
541 server_fd = socket_open_listen(&my_http_addr);
546 if (my_rtsp_addr.sin_port) {
547 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
548 if (rtsp_server_fd < 0)
552 if (!rtsp_server_fd && !server_fd) {
553 http_log("HTTP and RTSP disabled.\n");
557 http_log("ffserver started.\n");
559 start_children(first_feed);
561 first_http_ctx = NULL;
567 poll_entry = poll_table;
569 poll_entry->fd = server_fd;
570 poll_entry->events = POLLIN;
573 if (rtsp_server_fd) {
574 poll_entry->fd = rtsp_server_fd;
575 poll_entry->events = POLLIN;
579 /* wait for events on each HTTP handle */
586 case HTTPSTATE_SEND_HEADER:
587 case RTSPSTATE_SEND_REPLY:
588 case RTSPSTATE_SEND_PACKET:
589 c->poll_entry = poll_entry;
591 poll_entry->events = POLLOUT;
594 case HTTPSTATE_SEND_DATA_HEADER:
595 case HTTPSTATE_SEND_DATA:
596 case HTTPSTATE_SEND_DATA_TRAILER:
597 if (!c->is_packetized) {
598 /* for TCP, we output as much as we can (may need to put a limit) */
599 c->poll_entry = poll_entry;
601 poll_entry->events = POLLOUT;
604 /* when ffserver is doing the timing, we work by
605 looking at which packet need to be sent every
607 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
612 case HTTPSTATE_WAIT_REQUEST:
613 case HTTPSTATE_RECEIVE_DATA:
614 case HTTPSTATE_WAIT_FEED:
615 case RTSPSTATE_WAIT_REQUEST:
616 /* need to catch errors */
617 c->poll_entry = poll_entry;
619 poll_entry->events = POLLIN;/* Maybe this will work */
623 c->poll_entry = NULL;
629 /* wait for an event on one connection. We poll at least every
630 second to handle timeouts */
632 ret = poll(poll_table, poll_entry - poll_table, delay);
633 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
634 ff_neterrno() != FF_NETERROR(EINTR))
638 cur_time = av_gettime() / 1000;
640 if (need_to_start_children) {
641 need_to_start_children = 0;
642 start_children(first_feed);
645 /* now handle the events */
646 for(c = first_http_ctx; c != NULL; c = c_next) {
648 if (handle_connection(c) < 0) {
649 /* close and free the connection */
655 poll_entry = poll_table;
657 /* new HTTP connection request ? */
658 if (poll_entry->revents & POLLIN)
659 new_connection(server_fd, 0);
662 if (rtsp_server_fd) {
663 /* new RTSP connection request ? */
664 if (poll_entry->revents & POLLIN)
665 new_connection(rtsp_server_fd, 1);
670 /* start waiting for a new HTTP/RTSP request */
671 static void start_wait_request(HTTPContext *c, int is_rtsp)
673 c->buffer_ptr = c->buffer;
674 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
677 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
678 c->state = RTSPSTATE_WAIT_REQUEST;
680 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
681 c->state = HTTPSTATE_WAIT_REQUEST;
685 static void new_connection(int server_fd, int is_rtsp)
687 struct sockaddr_in from_addr;
689 HTTPContext *c = NULL;
691 len = sizeof(from_addr);
692 fd = accept(server_fd, (struct sockaddr *)&from_addr,
695 http_log("error during accept %s\n", strerror(errno));
698 ff_socket_nonblock(fd, 1);
700 /* XXX: should output a warning page when coming
701 close to the connection limit */
702 if (nb_connections >= nb_max_connections)
705 /* add a new connection */
706 c = av_mallocz(sizeof(HTTPContext));
711 c->poll_entry = NULL;
712 c->from_addr = from_addr;
713 c->buffer_size = IOBUFFER_INIT_SIZE;
714 c->buffer = av_malloc(c->buffer_size);
718 c->next = first_http_ctx;
722 start_wait_request(c, is_rtsp);
734 static void close_connection(HTTPContext *c)
736 HTTPContext **cp, *c1;
738 AVFormatContext *ctx;
742 /* remove connection from list */
743 cp = &first_http_ctx;
744 while ((*cp) != NULL) {
752 /* remove references, if any (XXX: do it faster) */
753 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
758 /* remove connection associated resources */
762 /* close each frame parser */
763 for(i=0;i<c->fmt_in->nb_streams;i++) {
764 st = c->fmt_in->streams[i];
765 if (st->codec->codec)
766 avcodec_close(st->codec);
768 av_close_input_file(c->fmt_in);
771 /* free RTP output streams if any */
774 nb_streams = c->stream->nb_streams;
776 for(i=0;i<nb_streams;i++) {
779 av_write_trailer(ctx);
782 h = c->rtp_handles[i];
789 if (!c->last_packet_sent) {
792 if (url_open_dyn_buf(&ctx->pb) >= 0) {
793 av_write_trailer(ctx);
794 av_freep(&c->pb_buffer);
795 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
800 for(i=0; i<ctx->nb_streams; i++)
801 av_free(ctx->streams[i]);
803 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
804 current_bandwidth -= c->stream->bandwidth;
806 /* signal that there is no feed if we are the feeder socket */
807 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
808 c->stream->feed_opened = 0;
812 av_freep(&c->pb_buffer);
813 av_freep(&c->packet_buffer);
819 static int handle_connection(HTTPContext *c)
824 case HTTPSTATE_WAIT_REQUEST:
825 case RTSPSTATE_WAIT_REQUEST:
827 if ((c->timeout - cur_time) < 0)
829 if (c->poll_entry->revents & (POLLERR | POLLHUP))
832 /* no need to read if no events */
833 if (!(c->poll_entry->revents & POLLIN))
837 len = recv(c->fd, c->buffer_ptr, 1, 0);
839 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
840 ff_neterrno() != FF_NETERROR(EINTR))
842 } else if (len == 0) {
845 /* search for end of request. */
847 c->buffer_ptr += len;
849 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
850 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
851 /* request found : parse it and reply */
852 if (c->state == HTTPSTATE_WAIT_REQUEST) {
853 ret = http_parse_request(c);
855 ret = rtsp_parse_request(c);
859 } else if (ptr >= c->buffer_end) {
860 /* request too long: cannot do anything */
862 } else goto read_loop;
866 case HTTPSTATE_SEND_HEADER:
867 if (c->poll_entry->revents & (POLLERR | POLLHUP))
870 /* no need to write if no events */
871 if (!(c->poll_entry->revents & POLLOUT))
873 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
875 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
876 ff_neterrno() != FF_NETERROR(EINTR)) {
877 /* error : close connection */
878 av_freep(&c->pb_buffer);
882 c->buffer_ptr += len;
884 c->stream->bytes_served += len;
885 c->data_count += len;
886 if (c->buffer_ptr >= c->buffer_end) {
887 av_freep(&c->pb_buffer);
891 /* all the buffer was sent : synchronize to the incoming stream */
892 c->state = HTTPSTATE_SEND_DATA_HEADER;
893 c->buffer_ptr = c->buffer_end = c->buffer;
898 case HTTPSTATE_SEND_DATA:
899 case HTTPSTATE_SEND_DATA_HEADER:
900 case HTTPSTATE_SEND_DATA_TRAILER:
901 /* for packetized output, we consider we can always write (the
902 input streams sets the speed). It may be better to verify
903 that we do not rely too much on the kernel queues */
904 if (!c->is_packetized) {
905 if (c->poll_entry->revents & (POLLERR | POLLHUP))
908 /* no need to read if no events */
909 if (!(c->poll_entry->revents & POLLOUT))
912 if (http_send_data(c) < 0)
914 /* close connection if trailer sent */
915 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
918 case HTTPSTATE_RECEIVE_DATA:
919 /* no need to read if no events */
920 if (c->poll_entry->revents & (POLLERR | POLLHUP))
922 if (!(c->poll_entry->revents & POLLIN))
924 if (http_receive_data(c) < 0)
927 case HTTPSTATE_WAIT_FEED:
928 /* no need to read if no events */
929 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
932 /* nothing to do, we'll be waken up by incoming feed packets */
935 case RTSPSTATE_SEND_REPLY:
936 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
937 av_freep(&c->pb_buffer);
940 /* no need to write if no events */
941 if (!(c->poll_entry->revents & POLLOUT))
943 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
945 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
946 ff_neterrno() != FF_NETERROR(EINTR)) {
947 /* error : close connection */
948 av_freep(&c->pb_buffer);
952 c->buffer_ptr += len;
953 c->data_count += len;
954 if (c->buffer_ptr >= c->buffer_end) {
955 /* all the buffer was sent : wait for a new request */
956 av_freep(&c->pb_buffer);
957 start_wait_request(c, 1);
961 case RTSPSTATE_SEND_PACKET:
962 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
963 av_freep(&c->packet_buffer);
966 /* no need to write if no events */
967 if (!(c->poll_entry->revents & POLLOUT))
969 len = send(c->fd, c->packet_buffer_ptr,
970 c->packet_buffer_end - c->packet_buffer_ptr, 0);
972 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
973 ff_neterrno() != FF_NETERROR(EINTR)) {
974 /* error : close connection */
975 av_freep(&c->packet_buffer);
979 c->packet_buffer_ptr += len;
980 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
981 /* all the buffer was sent : wait for a new request */
982 av_freep(&c->packet_buffer);
983 c->state = RTSPSTATE_WAIT_REQUEST;
987 case HTTPSTATE_READY:
996 static int extract_rates(char *rates, int ratelen, const char *request)
1000 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1001 if (strncasecmp(p, "Pragma:", 7) == 0) {
1002 const char *q = p + 7;
1004 while (*q && *q != '\n' && isspace(*q))
1007 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1013 memset(rates, 0xff, ratelen);
1016 while (*q && *q != '\n' && *q != ':')
1019 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1023 if (stream_no < ratelen && stream_no >= 0)
1024 rates[stream_no] = rate_no;
1026 while (*q && *q != '\n' && !isspace(*q))
1033 p = strchr(p, '\n');
1043 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1046 int best_bitrate = 100000000;
1049 for (i = 0; i < feed->nb_streams; i++) {
1050 AVCodecContext *feed_codec = feed->streams[i]->codec;
1052 if (feed_codec->codec_id != codec->codec_id ||
1053 feed_codec->sample_rate != codec->sample_rate ||
1054 feed_codec->width != codec->width ||
1055 feed_codec->height != codec->height)
1058 /* Potential stream */
1060 /* We want the fastest stream less than bit_rate, or the slowest
1061 * faster than bit_rate
1064 if (feed_codec->bit_rate <= bit_rate) {
1065 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1066 best_bitrate = feed_codec->bit_rate;
1070 if (feed_codec->bit_rate < best_bitrate) {
1071 best_bitrate = feed_codec->bit_rate;
1080 static int modify_current_stream(HTTPContext *c, char *rates)
1083 FFStream *req = c->stream;
1084 int action_required = 0;
1086 /* Not much we can do for a feed */
1090 for (i = 0; i < req->nb_streams; i++) {
1091 AVCodecContext *codec = req->streams[i]->codec;
1095 c->switch_feed_streams[i] = req->feed_streams[i];
1098 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1101 /* Wants off or slow */
1102 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1104 /* This doesn't work well when it turns off the only stream! */
1105 c->switch_feed_streams[i] = -2;
1106 c->feed_streams[i] = -2;
1111 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1112 action_required = 1;
1115 return action_required;
1119 static void do_switch_stream(HTTPContext *c, int i)
1121 if (c->switch_feed_streams[i] >= 0) {
1123 c->feed_streams[i] = c->switch_feed_streams[i];
1126 /* Now update the stream */
1128 c->switch_feed_streams[i] = -1;
1131 /* XXX: factorize in utils.c ? */
1132 /* XXX: take care with different space meaning */
1133 static void skip_spaces(const char **pp)
1137 while (*p == ' ' || *p == '\t')
1142 static void get_word(char *buf, int buf_size, const char **pp)
1150 while (!isspace(*p) && *p != '\0') {
1151 if ((q - buf) < buf_size - 1)
1160 static int validate_acl(FFStream *stream, HTTPContext *c)
1162 enum IPAddressAction last_action = IP_DENY;
1164 struct in_addr *src = &c->from_addr.sin_addr;
1165 unsigned long src_addr = src->s_addr;
1167 for (acl = stream->acl; acl; acl = acl->next) {
1168 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1169 return (acl->action == IP_ALLOW) ? 1 : 0;
1170 last_action = acl->action;
1173 /* Nothing matched, so return not the last action */
1174 return (last_action == IP_DENY) ? 1 : 0;
1177 /* compute the real filename of a file by matching it without its
1178 extensions to all the stream filenames */
1179 static void compute_real_filename(char *filename, int max_size)
1186 /* compute filename by matching without the file extensions */
1187 av_strlcpy(file1, filename, sizeof(file1));
1188 p = strrchr(file1, '.');
1191 for(stream = first_stream; stream != NULL; stream = stream->next) {
1192 av_strlcpy(file2, stream->filename, sizeof(file2));
1193 p = strrchr(file2, '.');
1196 if (!strcmp(file1, file2)) {
1197 av_strlcpy(filename, stream->filename, max_size);
1212 /* parse http request and prepare header */
1213 static int http_parse_request(HTTPContext *c)
1216 enum RedirType redir_type;
1218 char info[1024], filename[1024];
1222 const char *mime_type;
1226 char *useragent = 0;
1229 get_word(cmd, sizeof(cmd), (const char **)&p);
1230 av_strlcpy(c->method, cmd, sizeof(c->method));
1232 if (!strcmp(cmd, "GET"))
1234 else if (!strcmp(cmd, "POST"))
1239 get_word(url, sizeof(url), (const char **)&p);
1240 av_strlcpy(c->url, url, sizeof(c->url));
1242 get_word(protocol, sizeof(protocol), (const char **)&p);
1243 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1246 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1249 http_log("New connection: %s %s\n", cmd, url);
1251 /* find the filename and the optional info string in the request */
1252 p = strchr(url, '?');
1254 av_strlcpy(info, p, sizeof(info));
1259 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1261 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1262 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1264 if (*useragent && *useragent != '\n' && isspace(*useragent))
1268 p = strchr(p, '\n');
1275 redir_type = REDIR_NONE;
1276 if (match_ext(filename, "asx")) {
1277 redir_type = REDIR_ASX;
1278 filename[strlen(filename)-1] = 'f';
1279 } else if (match_ext(filename, "asf") &&
1280 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1281 /* if this isn't WMP or lookalike, return the redirector file */
1282 redir_type = REDIR_ASF;
1283 } else if (match_ext(filename, "rpm,ram")) {
1284 redir_type = REDIR_RAM;
1285 strcpy(filename + strlen(filename)-2, "m");
1286 } else if (match_ext(filename, "rtsp")) {
1287 redir_type = REDIR_RTSP;
1288 compute_real_filename(filename, sizeof(filename) - 1);
1289 } else if (match_ext(filename, "sdp")) {
1290 redir_type = REDIR_SDP;
1291 compute_real_filename(filename, sizeof(filename) - 1);
1294 // "redirect" / request to index.html
1295 if (!strlen(filename))
1296 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1298 stream = first_stream;
1299 while (stream != NULL) {
1300 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1302 stream = stream->next;
1304 if (stream == NULL) {
1305 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1310 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1311 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1313 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1314 c->http_error = 301;
1316 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1317 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1318 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1319 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1320 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1321 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1324 /* prepare output buffer */
1325 c->buffer_ptr = c->buffer;
1327 c->state = HTTPSTATE_SEND_HEADER;
1331 /* If this is WMP, get the rate information */
1332 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1333 if (modify_current_stream(c, ratebuf)) {
1334 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1335 if (c->switch_feed_streams[i] >= 0)
1336 do_switch_stream(c, i);
1341 /* If already streaming this feed, do not let start another feeder. */
1342 if (stream->feed_opened) {
1343 snprintf(msg, sizeof(msg), "This feed is already being received.");
1344 http_log("feed %s already being received\n", stream->feed_filename);
1348 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1349 current_bandwidth += stream->bandwidth;
1351 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1352 c->http_error = 200;
1354 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1355 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1356 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1357 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1358 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");
1359 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",
1360 current_bandwidth, max_bandwidth);
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1363 /* prepare output buffer */
1364 c->buffer_ptr = c->buffer;
1366 c->state = HTTPSTATE_SEND_HEADER;
1370 if (redir_type != REDIR_NONE) {
1373 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1374 if (strncasecmp(p, "Host:", 5) == 0) {
1378 p = strchr(p, '\n');
1389 while (isspace(*hostinfo))
1392 eoh = strchr(hostinfo, '\n');
1394 if (eoh[-1] == '\r')
1397 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1398 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1399 hostbuf[eoh - hostinfo] = 0;
1401 c->http_error = 200;
1403 switch(redir_type) {
1405 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1408 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1409 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1410 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1411 hostbuf, filename, info);
1412 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1415 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1416 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1417 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1418 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1419 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1420 hostbuf, filename, info);
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1425 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1426 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1427 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1428 hostbuf, filename, info);
1432 char hostname[256], *p;
1433 /* extract only hostname */
1434 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1435 p = strrchr(hostname, ':');
1438 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1439 /* XXX: incorrect mime type ? */
1440 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1441 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1442 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1443 hostname, ntohs(my_rtsp_addr.sin_port),
1450 int sdp_data_size, len;
1451 struct sockaddr_in my_addr;
1453 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1454 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1455 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1457 len = sizeof(my_addr);
1458 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1460 /* XXX: should use a dynamic buffer */
1461 sdp_data_size = prepare_sdp_description(stream,
1464 if (sdp_data_size > 0) {
1465 memcpy(q, sdp_data, sdp_data_size);
1477 /* prepare output buffer */
1478 c->buffer_ptr = c->buffer;
1480 c->state = HTTPSTATE_SEND_HEADER;
1486 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1490 stream->conns_served++;
1492 /* XXX: add there authenticate and IP match */
1495 /* if post, it means a feed is being sent */
1496 if (!stream->is_feed) {
1497 /* However it might be a status report from WMP! Lets log the data
1498 * as it might come in handy one day
1503 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1504 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1508 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1509 client_id = strtol(p + 18, 0, 10);
1510 p = strchr(p, '\n');
1518 char *eol = strchr(logline, '\n');
1523 if (eol[-1] == '\r')
1525 http_log("%.*s\n", (int) (eol - logline), logline);
1526 c->suppress_log = 1;
1531 http_log("\nGot request:\n%s\n", c->buffer);
1534 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1537 /* Now we have to find the client_id */
1538 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1539 if (wmpc->wmp_client_id == client_id)
1543 if (wmpc && modify_current_stream(wmpc, ratebuf))
1544 wmpc->switch_pending = 1;
1547 snprintf(msg, sizeof(msg), "POST command not handled");
1551 if (http_start_receive_data(c) < 0) {
1552 snprintf(msg, sizeof(msg), "could not open feed");
1556 c->state = HTTPSTATE_RECEIVE_DATA;
1561 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1562 http_log("\nGot request:\n%s\n", c->buffer);
1565 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1568 /* open input stream */
1569 if (open_input_stream(c, info) < 0) {
1570 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1574 /* prepare http header */
1576 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1577 mime_type = c->stream->fmt->mime_type;
1579 mime_type = "application/x-octet-stream";
1580 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1582 /* for asf, we need extra headers */
1583 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1584 /* Need to allocate a client id */
1586 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1588 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);
1590 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1591 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1593 /* prepare output buffer */
1595 c->buffer_ptr = c->buffer;
1597 c->state = HTTPSTATE_SEND_HEADER;
1600 c->http_error = 404;
1602 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1603 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1604 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1605 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1606 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1607 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1608 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1610 /* prepare output buffer */
1611 c->buffer_ptr = c->buffer;
1613 c->state = HTTPSTATE_SEND_HEADER;
1617 c->http_error = 200; /* horrible : we use this value to avoid
1618 going to the send data state */
1619 c->state = HTTPSTATE_SEND_HEADER;
1623 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1625 static const char *suffix = " kMGTP";
1628 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1630 url_fprintf(pb, "%"PRId64"%c", count, *s);
1633 static void compute_status(HTTPContext *c)
1642 if (url_open_dyn_buf(&pb) < 0) {
1643 /* XXX: return an error ? */
1644 c->buffer_ptr = c->buffer;
1645 c->buffer_end = c->buffer;
1649 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1650 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1651 url_fprintf(pb, "Pragma: no-cache\r\n");
1652 url_fprintf(pb, "\r\n");
1654 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1655 if (c->stream->feed_filename[0])
1656 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1657 url_fprintf(pb, "</HEAD>\n<BODY>");
1658 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1660 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1661 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1662 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");
1663 stream = first_stream;
1664 while (stream != NULL) {
1665 char sfilename[1024];
1668 if (stream->feed != stream) {
1669 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1670 eosf = sfilename + strlen(sfilename);
1671 if (eosf - sfilename >= 4) {
1672 if (strcmp(eosf - 4, ".asf") == 0)
1673 strcpy(eosf - 4, ".asx");
1674 else if (strcmp(eosf - 3, ".rm") == 0)
1675 strcpy(eosf - 3, ".ram");
1676 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1677 /* generate a sample RTSP director if
1678 unicast. Generate an SDP redirector if
1680 eosf = strrchr(sfilename, '.');
1682 eosf = sfilename + strlen(sfilename);
1683 if (stream->is_multicast)
1684 strcpy(eosf, ".sdp");
1686 strcpy(eosf, ".rtsp");
1690 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1691 sfilename, stream->filename);
1692 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1693 stream->conns_served);
1694 fmt_bytecount(pb, stream->bytes_served);
1695 switch(stream->stream_type) {
1696 case STREAM_TYPE_LIVE: {
1697 int audio_bit_rate = 0;
1698 int video_bit_rate = 0;
1699 const char *audio_codec_name = "";
1700 const char *video_codec_name = "";
1701 const char *audio_codec_name_extra = "";
1702 const char *video_codec_name_extra = "";
1704 for(i=0;i<stream->nb_streams;i++) {
1705 AVStream *st = stream->streams[i];
1706 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1707 switch(st->codec->codec_type) {
1708 case CODEC_TYPE_AUDIO:
1709 audio_bit_rate += st->codec->bit_rate;
1711 if (*audio_codec_name)
1712 audio_codec_name_extra = "...";
1713 audio_codec_name = codec->name;
1716 case CODEC_TYPE_VIDEO:
1717 video_bit_rate += st->codec->bit_rate;
1719 if (*video_codec_name)
1720 video_codec_name_extra = "...";
1721 video_codec_name = codec->name;
1724 case CODEC_TYPE_DATA:
1725 video_bit_rate += st->codec->bit_rate;
1731 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",
1734 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1735 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1737 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1739 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1740 url_fprintf(pb, "\n");
1744 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1748 stream = stream->next;
1750 url_fprintf(pb, "</TABLE>\n");
1752 stream = first_stream;
1753 while (stream != NULL) {
1754 if (stream->feed == stream) {
1755 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1757 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1759 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1764 /* This is somewhat linux specific I guess */
1765 snprintf(ps_cmd, sizeof(ps_cmd),
1766 "ps -o \"%%cpu,cputime\" --no-headers %d",
1769 pid_stat = popen(ps_cmd, "r");
1774 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1776 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1784 url_fprintf(pb, "<p>");
1786 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");
1788 for (i = 0; i < stream->nb_streams; i++) {
1789 AVStream *st = stream->streams[i];
1790 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1791 const char *type = "unknown";
1792 char parameters[64];
1796 switch(st->codec->codec_type) {
1797 case CODEC_TYPE_AUDIO:
1799 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1801 case CODEC_TYPE_VIDEO:
1803 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1804 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1809 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1810 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1812 url_fprintf(pb, "</table>\n");
1815 stream = stream->next;
1821 AVCodecContext *enc;
1825 stream = first_feed;
1826 while (stream != NULL) {
1827 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1828 url_fprintf(pb, "<TABLE>\n");
1829 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1830 for(i=0;i<stream->nb_streams;i++) {
1831 AVStream *st = stream->streams[i];
1832 FeedData *fdata = st->priv_data;
1835 avcodec_string(buf, sizeof(buf), enc);
1836 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1837 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1838 avg /= enc->frame_size;
1839 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1840 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1842 url_fprintf(pb, "</TABLE>\n");
1843 stream = stream->next_feed;
1848 /* connection status */
1849 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1851 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1852 nb_connections, nb_max_connections);
1854 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1855 current_bandwidth, max_bandwidth);
1857 url_fprintf(pb, "<TABLE>\n");
1858 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");
1859 c1 = first_http_ctx;
1861 while (c1 != NULL) {
1867 for (j = 0; j < c1->stream->nb_streams; j++) {
1868 if (!c1->stream->feed)
1869 bitrate += c1->stream->streams[j]->codec->bit_rate;
1870 else if (c1->feed_streams[j] >= 0)
1871 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1876 p = inet_ntoa(c1->from_addr.sin_addr);
1877 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1879 c1->stream ? c1->stream->filename : "",
1880 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1883 http_state[c1->state]);
1884 fmt_bytecount(pb, bitrate);
1885 url_fprintf(pb, "<td align=right>");
1886 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1887 url_fprintf(pb, "<td align=right>");
1888 fmt_bytecount(pb, c1->data_count);
1889 url_fprintf(pb, "\n");
1892 url_fprintf(pb, "</TABLE>\n");
1897 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1898 url_fprintf(pb, "</BODY>\n</HTML>\n");
1900 len = url_close_dyn_buf(pb, &c->pb_buffer);
1901 c->buffer_ptr = c->pb_buffer;
1902 c->buffer_end = c->pb_buffer + len;
1905 /* check if the parser needs to be opened for stream i */
1906 static void open_parser(AVFormatContext *s, int i)
1908 AVStream *st = s->streams[i];
1911 if (!st->codec->codec) {
1912 codec = avcodec_find_decoder(st->codec->codec_id);
1913 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1914 st->codec->parse_only = 1;
1915 if (avcodec_open(st->codec, codec) < 0)
1916 st->codec->parse_only = 0;
1921 static int open_input_stream(HTTPContext *c, const char *info)
1924 char input_filename[1024];
1926 int buf_size, i, ret;
1929 /* find file name */
1930 if (c->stream->feed) {
1931 strcpy(input_filename, c->stream->feed->feed_filename);
1932 buf_size = FFM_PACKET_SIZE;
1933 /* compute position (absolute time) */
1934 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1935 stream_pos = parse_date(buf, 0);
1936 if (stream_pos == INT64_MIN)
1938 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1939 int prebuffer = strtol(buf, 0, 10);
1940 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1942 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1944 strcpy(input_filename, c->stream->feed_filename);
1946 /* compute position (relative time) */
1947 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1948 stream_pos = parse_date(buf, 1);
1949 if (stream_pos == INT64_MIN)
1954 if (input_filename[0] == '\0')
1958 { time_t when = stream_pos / 1000000;
1959 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1964 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1965 buf_size, c->stream->ap_in)) < 0) {
1966 http_log("could not open %s: %d\n", input_filename, ret);
1969 s->flags |= AVFMT_FLAG_GENPTS;
1971 av_find_stream_info(c->fmt_in);
1973 /* open each parser */
1974 for(i=0;i<s->nb_streams;i++)
1977 /* choose stream as clock source (we favorize video stream if
1978 present) for packet sending */
1979 c->pts_stream_index = 0;
1980 for(i=0;i<c->stream->nb_streams;i++) {
1981 if (c->pts_stream_index == 0 &&
1982 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1983 c->pts_stream_index = i;
1988 if (c->fmt_in->iformat->read_seek)
1989 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1991 /* set the start time (needed for maxtime and RTP packet timing) */
1992 c->start_time = cur_time;
1993 c->first_pts = AV_NOPTS_VALUE;
1997 /* return the server clock (in us) */
1998 static int64_t get_server_clock(HTTPContext *c)
2000 /* compute current pts value from system time */
2001 return (cur_time - c->start_time) * 1000;
2004 /* return the estimated time at which the current packet must be sent
2006 static int64_t get_packet_send_clock(HTTPContext *c)
2008 int bytes_left, bytes_sent, frame_bytes;
2010 frame_bytes = c->cur_frame_bytes;
2011 if (frame_bytes <= 0)
2014 bytes_left = c->buffer_end - c->buffer_ptr;
2015 bytes_sent = frame_bytes - bytes_left;
2016 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2021 static int http_prepare_data(HTTPContext *c)
2024 AVFormatContext *ctx;
2026 av_freep(&c->pb_buffer);
2028 case HTTPSTATE_SEND_DATA_HEADER:
2029 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2030 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2031 sizeof(c->fmt_ctx.author));
2032 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2033 sizeof(c->fmt_ctx.comment));
2034 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2035 sizeof(c->fmt_ctx.copyright));
2036 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2037 sizeof(c->fmt_ctx.title));
2039 for(i=0;i<c->stream->nb_streams;i++) {
2042 st = av_mallocz(sizeof(AVStream));
2043 c->fmt_ctx.streams[i] = st;
2044 /* if file or feed, then just take streams from FFStream struct */
2045 if (!c->stream->feed ||
2046 c->stream->feed == c->stream)
2047 src = c->stream->streams[i];
2049 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2053 st->codec->frame_number = 0; /* XXX: should be done in
2054 AVStream, not in codec */
2056 /* set output format parameters */
2057 c->fmt_ctx.oformat = c->stream->fmt;
2058 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2060 c->got_key_frame = 0;
2062 /* prepare header and save header data in a stream */
2063 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2064 /* XXX: potential leak */
2067 c->fmt_ctx.pb->is_streamed = 1;
2070 * HACK to avoid mpeg ps muxer to spit many underflow errors
2071 * Default value from FFmpeg
2072 * Try to set it use configuration option
2074 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2075 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2077 av_set_parameters(&c->fmt_ctx, NULL);
2078 if (av_write_header(&c->fmt_ctx) < 0) {
2079 http_log("Error writing output header\n");
2083 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2084 c->buffer_ptr = c->pb_buffer;
2085 c->buffer_end = c->pb_buffer + len;
2087 c->state = HTTPSTATE_SEND_DATA;
2088 c->last_packet_sent = 0;
2090 case HTTPSTATE_SEND_DATA:
2091 /* find a new packet */
2092 /* read a packet from the input stream */
2093 if (c->stream->feed)
2094 ffm_set_write_index(c->fmt_in,
2095 c->stream->feed->feed_write_index,
2096 c->stream->feed->feed_size);
2098 if (c->stream->max_time &&
2099 c->stream->max_time + c->start_time - cur_time < 0)
2100 /* We have timed out */
2101 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2105 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2106 if (c->stream->feed && c->stream->feed->feed_opened) {
2107 /* if coming from feed, it means we reached the end of the
2108 ffm file, so must wait for more data */
2109 c->state = HTTPSTATE_WAIT_FEED;
2110 return 1; /* state changed */
2112 if (c->stream->loop) {
2113 av_close_input_file(c->fmt_in);
2115 if (open_input_stream(c, "") < 0)
2120 /* must send trailer now because eof or error */
2121 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2125 int source_index = pkt.stream_index;
2126 /* update first pts if needed */
2127 if (c->first_pts == AV_NOPTS_VALUE) {
2128 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2129 c->start_time = cur_time;
2131 /* send it to the appropriate stream */
2132 if (c->stream->feed) {
2133 /* if coming from a feed, select the right stream */
2134 if (c->switch_pending) {
2135 c->switch_pending = 0;
2136 for(i=0;i<c->stream->nb_streams;i++) {
2137 if (c->switch_feed_streams[i] == pkt.stream_index)
2138 if (pkt.flags & PKT_FLAG_KEY)
2139 do_switch_stream(c, i);
2140 if (c->switch_feed_streams[i] >= 0)
2141 c->switch_pending = 1;
2144 for(i=0;i<c->stream->nb_streams;i++) {
2145 if (c->feed_streams[i] == pkt.stream_index) {
2146 AVStream *st = c->fmt_in->streams[source_index];
2147 pkt.stream_index = i;
2148 if (pkt.flags & PKT_FLAG_KEY &&
2149 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2150 c->stream->nb_streams == 1))
2151 c->got_key_frame = 1;
2152 if (!c->stream->send_on_key || c->got_key_frame)
2157 AVCodecContext *codec;
2158 AVStream *ist, *ost;
2160 ist = c->fmt_in->streams[source_index];
2161 /* specific handling for RTP: we use several
2162 output stream (one for each RTP
2163 connection). XXX: need more abstract handling */
2164 if (c->is_packetized) {
2165 /* compute send time and duration */
2166 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2167 if (ist->start_time != AV_NOPTS_VALUE)
2168 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2169 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2171 printf("index=%d pts=%0.3f duration=%0.6f\n",
2173 (double)c->cur_pts /
2175 (double)c->cur_frame_duration /
2178 /* find RTP context */
2179 c->packet_stream_index = pkt.stream_index;
2180 ctx = c->rtp_ctx[c->packet_stream_index];
2182 av_free_packet(&pkt);
2185 codec = ctx->streams[0]->codec;
2186 /* only one stream per RTP connection */
2187 pkt.stream_index = 0;
2191 codec = ctx->streams[pkt.stream_index]->codec;
2194 if (c->is_packetized) {
2195 int max_packet_size;
2196 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2197 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2199 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2200 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2202 ret = url_open_dyn_buf(&ctx->pb);
2205 /* XXX: potential leak */
2208 ost = ctx->streams[pkt.stream_index];
2210 ctx->pb->is_streamed = 1;
2211 if (pkt.dts != AV_NOPTS_VALUE)
2212 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2213 if (pkt.pts != AV_NOPTS_VALUE)
2214 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2215 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2216 if (av_write_frame(ctx, &pkt) < 0) {
2217 http_log("Error writing frame to output\n");
2218 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2221 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2222 c->cur_frame_bytes = len;
2223 c->buffer_ptr = c->pb_buffer;
2224 c->buffer_end = c->pb_buffer + len;
2226 codec->frame_number++;
2228 av_free_packet(&pkt);
2232 av_free_packet(&pkt);
2237 case HTTPSTATE_SEND_DATA_TRAILER:
2238 /* last packet test ? */
2239 if (c->last_packet_sent || c->is_packetized)
2242 /* prepare header */
2243 if (url_open_dyn_buf(&ctx->pb) < 0) {
2244 /* XXX: potential leak */
2247 c->fmt_ctx.pb->is_streamed = 1;
2248 av_write_trailer(ctx);
2249 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2250 c->buffer_ptr = c->pb_buffer;
2251 c->buffer_end = c->pb_buffer + len;
2253 c->last_packet_sent = 1;
2259 /* should convert the format at the same time */
2260 /* send data starting at c->buffer_ptr to the output connection
2261 (either UDP or TCP connection) */
2262 static int http_send_data(HTTPContext *c)
2267 if (c->buffer_ptr >= c->buffer_end) {
2268 ret = http_prepare_data(c);
2272 /* state change requested */
2275 if (c->is_packetized) {
2276 /* RTP data output */
2277 len = c->buffer_end - c->buffer_ptr;
2279 /* fail safe - should never happen */
2281 c->buffer_ptr = c->buffer_end;
2284 len = (c->buffer_ptr[0] << 24) |
2285 (c->buffer_ptr[1] << 16) |
2286 (c->buffer_ptr[2] << 8) |
2288 if (len > (c->buffer_end - c->buffer_ptr))
2290 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2291 /* nothing to send yet: we can wait */
2295 c->data_count += len;
2296 update_datarate(&c->datarate, c->data_count);
2298 c->stream->bytes_served += len;
2300 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2301 /* RTP packets are sent inside the RTSP TCP connection */
2303 int interleaved_index, size;
2305 HTTPContext *rtsp_c;
2308 /* if no RTSP connection left, error */
2311 /* if already sending something, then wait. */
2312 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2314 if (url_open_dyn_buf(&pb) < 0)
2316 interleaved_index = c->packet_stream_index * 2;
2317 /* RTCP packets are sent at odd indexes */
2318 if (c->buffer_ptr[1] == 200)
2319 interleaved_index++;
2320 /* write RTSP TCP header */
2322 header[1] = interleaved_index;
2323 header[2] = len >> 8;
2325 put_buffer(pb, header, 4);
2326 /* write RTP packet data */
2328 put_buffer(pb, c->buffer_ptr, len);
2329 size = url_close_dyn_buf(pb, &c->packet_buffer);
2330 /* prepare asynchronous TCP sending */
2331 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2332 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2333 c->buffer_ptr += len;
2335 /* send everything we can NOW */
2336 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2337 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2339 rtsp_c->packet_buffer_ptr += len;
2340 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2341 /* if we could not send all the data, we will
2342 send it later, so a new state is needed to
2343 "lock" the RTSP TCP connection */
2344 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2347 /* all data has been sent */
2348 av_freep(&c->packet_buffer);
2350 /* send RTP packet directly in UDP */
2352 url_write(c->rtp_handles[c->packet_stream_index],
2353 c->buffer_ptr, len);
2354 c->buffer_ptr += len;
2355 /* here we continue as we can send several packets per 10 ms slot */
2358 /* TCP data output */
2359 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2361 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2362 ff_neterrno() != FF_NETERROR(EINTR))
2363 /* error : close connection */
2368 c->buffer_ptr += len;
2370 c->data_count += len;
2371 update_datarate(&c->datarate, c->data_count);
2373 c->stream->bytes_served += len;
2381 static int http_start_receive_data(HTTPContext *c)
2385 if (c->stream->feed_opened)
2388 /* Don't permit writing to this one */
2389 if (c->stream->readonly)
2393 fd = open(c->stream->feed_filename, O_RDWR);
2395 http_log("Error opening feeder file: %s\n", strerror(errno));
2400 c->stream->feed_write_index = ffm_read_write_index(fd);
2401 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2402 lseek(fd, 0, SEEK_SET);
2404 /* init buffer input */
2405 c->buffer_ptr = c->buffer;
2406 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2407 c->stream->feed_opened = 1;
2411 static int http_receive_data(HTTPContext *c)
2415 if (c->buffer_end > c->buffer_ptr) {
2418 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2420 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2421 ff_neterrno() != FF_NETERROR(EINTR))
2422 /* error : close connection */
2424 } else if (len == 0)
2425 /* end of connection : close it */
2428 c->buffer_ptr += len;
2429 c->data_count += len;
2430 update_datarate(&c->datarate, c->data_count);
2434 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2435 if (c->buffer[0] != 'f' ||
2436 c->buffer[1] != 'm') {
2437 http_log("Feed stream has become desynchronized -- disconnecting\n");
2442 if (c->buffer_ptr >= c->buffer_end) {
2443 FFStream *feed = c->stream;
2444 /* a packet has been received : write it in the store, except
2446 if (c->data_count > FFM_PACKET_SIZE) {
2448 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2449 /* XXX: use llseek or url_seek */
2450 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2451 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2452 http_log("Error writing to feed file: %s\n", strerror(errno));
2456 feed->feed_write_index += FFM_PACKET_SIZE;
2457 /* update file size */
2458 if (feed->feed_write_index > c->stream->feed_size)
2459 feed->feed_size = feed->feed_write_index;
2461 /* handle wrap around if max file size reached */
2462 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2463 feed->feed_write_index = FFM_PACKET_SIZE;
2466 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2468 /* wake up any waiting connections */
2469 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2470 if (c1->state == HTTPSTATE_WAIT_FEED &&
2471 c1->stream->feed == c->stream->feed)
2472 c1->state = HTTPSTATE_SEND_DATA;
2475 /* We have a header in our hands that contains useful data */
2476 AVFormatContext *s = NULL;
2478 AVInputFormat *fmt_in;
2481 /* use feed output format name to find corresponding input format */
2482 fmt_in = av_find_input_format(feed->fmt->name);
2486 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2487 pb->is_streamed = 1;
2489 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2494 /* Now we have the actual streams */
2495 if (s->nb_streams != feed->nb_streams) {
2496 av_close_input_stream(s);
2501 for (i = 0; i < s->nb_streams; i++) {
2502 AVStream *fst = feed->streams[i];
2503 AVStream *st = s->streams[i];
2504 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2505 if (fst->codec->extradata_size) {
2506 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2507 if (!fst->codec->extradata)
2509 memcpy(fst->codec->extradata, st->codec->extradata,
2510 fst->codec->extradata_size);
2514 av_close_input_stream(s);
2517 c->buffer_ptr = c->buffer;
2522 c->stream->feed_opened = 0;
2524 /* wake up any waiting connections to stop waiting for feed */
2525 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2526 if (c1->state == HTTPSTATE_WAIT_FEED &&
2527 c1->stream->feed == c->stream->feed)
2528 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2533 /********************************************************************/
2536 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2543 switch(error_number) {
2544 case RTSP_STATUS_OK:
2547 case RTSP_STATUS_METHOD:
2548 str = "Method Not Allowed";
2550 case RTSP_STATUS_BANDWIDTH:
2551 str = "Not Enough Bandwidth";
2553 case RTSP_STATUS_SESSION:
2554 str = "Session Not Found";
2556 case RTSP_STATUS_STATE:
2557 str = "Method Not Valid in This State";
2559 case RTSP_STATUS_AGGREGATE:
2560 str = "Aggregate operation not allowed";
2562 case RTSP_STATUS_ONLY_AGGREGATE:
2563 str = "Only aggregate operation allowed";
2565 case RTSP_STATUS_TRANSPORT:
2566 str = "Unsupported transport";
2568 case RTSP_STATUS_INTERNAL:
2569 str = "Internal Server Error";
2571 case RTSP_STATUS_SERVICE:
2572 str = "Service Unavailable";
2574 case RTSP_STATUS_VERSION:
2575 str = "RTSP Version not supported";
2578 str = "Unknown Error";
2582 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2583 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2585 /* output GMT time */
2589 p = buf2 + strlen(p) - 1;
2592 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2595 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2597 rtsp_reply_header(c, error_number);
2598 url_fprintf(c->pb, "\r\n");
2601 static int rtsp_parse_request(HTTPContext *c)
2603 const char *p, *p1, *p2;
2609 RTSPHeader header1, *header = &header1;
2611 c->buffer_ptr[0] = '\0';
2614 get_word(cmd, sizeof(cmd), &p);
2615 get_word(url, sizeof(url), &p);
2616 get_word(protocol, sizeof(protocol), &p);
2618 av_strlcpy(c->method, cmd, sizeof(c->method));
2619 av_strlcpy(c->url, url, sizeof(c->url));
2620 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2622 if (url_open_dyn_buf(&c->pb) < 0) {
2623 /* XXX: cannot do more */
2624 c->pb = NULL; /* safety */
2628 /* check version name */
2629 if (strcmp(protocol, "RTSP/1.0") != 0) {
2630 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2634 /* parse each header line */
2635 memset(header, 0, sizeof(RTSPHeader));
2636 /* skip to next line */
2637 while (*p != '\n' && *p != '\0')
2641 while (*p != '\0') {
2642 p1 = strchr(p, '\n');
2646 if (p2 > p && p2[-1] == '\r')
2648 /* skip empty line */
2652 if (len > sizeof(line) - 1)
2653 len = sizeof(line) - 1;
2654 memcpy(line, p, len);
2656 rtsp_parse_line(header, line);
2660 /* handle sequence number */
2661 c->seq = header->seq;
2663 if (!strcmp(cmd, "DESCRIBE"))
2664 rtsp_cmd_describe(c, url);
2665 else if (!strcmp(cmd, "OPTIONS"))
2666 rtsp_cmd_options(c, url);
2667 else if (!strcmp(cmd, "SETUP"))
2668 rtsp_cmd_setup(c, url, header);
2669 else if (!strcmp(cmd, "PLAY"))
2670 rtsp_cmd_play(c, url, header);
2671 else if (!strcmp(cmd, "PAUSE"))
2672 rtsp_cmd_pause(c, url, header);
2673 else if (!strcmp(cmd, "TEARDOWN"))
2674 rtsp_cmd_teardown(c, url, header);
2676 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2679 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2680 c->pb = NULL; /* safety */
2682 /* XXX: cannot do more */
2685 c->buffer_ptr = c->pb_buffer;
2686 c->buffer_end = c->pb_buffer + len;
2687 c->state = RTSPSTATE_SEND_REPLY;
2691 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2692 struct in_addr my_ip)
2694 AVFormatContext *avc;
2695 AVStream avs[MAX_STREAMS];
2698 avc = av_alloc_format_context();
2702 if (stream->title[0] != 0) {
2703 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2705 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2707 avc->nb_streams = stream->nb_streams;
2708 if (stream->is_multicast) {
2709 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2710 inet_ntoa(stream->multicast_ip),
2711 stream->multicast_port, stream->multicast_ttl);
2714 for(i = 0; i < stream->nb_streams; i++) {
2715 avc->streams[i] = &avs[i];
2716 avc->streams[i]->codec = stream->streams[i]->codec;
2718 *pbuffer = av_mallocz(2048);
2719 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2722 return strlen(*pbuffer);
2725 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2727 // rtsp_reply_header(c, RTSP_STATUS_OK);
2728 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2729 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2730 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2731 url_fprintf(c->pb, "\r\n");
2734 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2740 int content_length, len;
2741 struct sockaddr_in my_addr;
2743 /* find which url is asked */
2744 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2749 for(stream = first_stream; stream != NULL; stream = stream->next) {
2750 if (!stream->is_feed &&
2751 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2752 !strcmp(path, stream->filename)) {
2756 /* no stream found */
2757 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2761 /* prepare the media description in sdp format */
2763 /* get the host IP */
2764 len = sizeof(my_addr);
2765 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2766 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2767 if (content_length < 0) {
2768 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2771 rtsp_reply_header(c, RTSP_STATUS_OK);
2772 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2773 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2774 url_fprintf(c->pb, "\r\n");
2775 put_buffer(c->pb, content, content_length);
2778 static HTTPContext *find_rtp_session(const char *session_id)
2782 if (session_id[0] == '\0')
2785 for(c = first_http_ctx; c != NULL; c = c->next) {
2786 if (!strcmp(c->session_id, session_id))
2792 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2794 RTSPTransportField *th;
2797 for(i=0;i<h->nb_transports;i++) {
2798 th = &h->transports[i];
2799 if (th->protocol == protocol)
2805 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2809 int stream_index, port;
2814 RTSPTransportField *th;
2815 struct sockaddr_in dest_addr;
2816 RTSPActionServerSetup setup;
2818 /* find which url is asked */
2819 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2824 /* now check each stream */
2825 for(stream = first_stream; stream != NULL; stream = stream->next) {
2826 if (!stream->is_feed &&
2827 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2828 /* accept aggregate filenames only if single stream */
2829 if (!strcmp(path, stream->filename)) {
2830 if (stream->nb_streams != 1) {
2831 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2838 for(stream_index = 0; stream_index < stream->nb_streams;
2840 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2841 stream->filename, stream_index);
2842 if (!strcmp(path, buf))
2847 /* no stream found */
2848 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2852 /* generate session id if needed */
2853 if (h->session_id[0] == '\0')
2854 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2855 av_random(&random_state), av_random(&random_state));
2857 /* find rtp session, and create it if none found */
2858 rtp_c = find_rtp_session(h->session_id);
2860 /* always prefer UDP */
2861 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2863 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2865 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2870 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2873 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2877 /* open input stream */
2878 if (open_input_stream(rtp_c, "") < 0) {
2879 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2884 /* test if stream is OK (test needed because several SETUP needs
2885 to be done for a given file) */
2886 if (rtp_c->stream != stream) {
2887 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2891 /* test if stream is already set up */
2892 if (rtp_c->rtp_ctx[stream_index]) {
2893 rtsp_reply_error(c, RTSP_STATUS_STATE);
2897 /* check transport */
2898 th = find_transport(h, rtp_c->rtp_protocol);
2899 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2900 th->client_port_min <= 0)) {
2901 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2905 /* setup default options */
2906 setup.transport_option[0] = '\0';
2907 dest_addr = rtp_c->from_addr;
2908 dest_addr.sin_port = htons(th->client_port_min);
2911 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2912 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2916 /* now everything is OK, so we can send the connection parameters */
2917 rtsp_reply_header(c, RTSP_STATUS_OK);
2919 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2921 switch(rtp_c->rtp_protocol) {
2922 case RTSP_PROTOCOL_RTP_UDP:
2923 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2924 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2925 "client_port=%d-%d;server_port=%d-%d",
2926 th->client_port_min, th->client_port_min + 1,
2929 case RTSP_PROTOCOL_RTP_TCP:
2930 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2931 stream_index * 2, stream_index * 2 + 1);
2936 if (setup.transport_option[0] != '\0')
2937 url_fprintf(c->pb, ";%s", setup.transport_option);
2938 url_fprintf(c->pb, "\r\n");
2941 url_fprintf(c->pb, "\r\n");
2945 /* find an rtp connection by using the session ID. Check consistency
2947 static HTTPContext *find_rtp_session_with_url(const char *url,
2948 const char *session_id)
2956 rtp_c = find_rtp_session(session_id);
2960 /* find which url is asked */
2961 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2965 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2966 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2967 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2968 rtp_c->stream->filename, s);
2969 if(!strncmp(path, buf, sizeof(buf))) {
2970 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2977 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2981 rtp_c = find_rtp_session_with_url(url, h->session_id);
2983 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2987 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2988 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2989 rtp_c->state != HTTPSTATE_READY) {
2990 rtsp_reply_error(c, RTSP_STATUS_STATE);
2995 /* XXX: seek in stream */
2996 if (h->range_start != AV_NOPTS_VALUE) {
2997 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2998 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3002 rtp_c->state = HTTPSTATE_SEND_DATA;
3004 /* now everything is OK, so we can send the connection parameters */
3005 rtsp_reply_header(c, RTSP_STATUS_OK);
3007 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3008 url_fprintf(c->pb, "\r\n");
3011 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3015 rtp_c = find_rtp_session_with_url(url, h->session_id);
3017 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3021 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3022 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3023 rtsp_reply_error(c, RTSP_STATUS_STATE);
3027 rtp_c->state = HTTPSTATE_READY;
3028 rtp_c->first_pts = AV_NOPTS_VALUE;
3029 /* now everything is OK, so we can send the connection parameters */
3030 rtsp_reply_header(c, RTSP_STATUS_OK);
3032 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3033 url_fprintf(c->pb, "\r\n");
3036 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3039 char session_id[32];
3041 rtp_c = find_rtp_session_with_url(url, h->session_id);
3043 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3047 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3049 /* abort the session */
3050 close_connection(rtp_c);
3052 /* now everything is OK, so we can send the connection parameters */
3053 rtsp_reply_header(c, RTSP_STATUS_OK);
3055 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3056 url_fprintf(c->pb, "\r\n");
3060 /********************************************************************/
3063 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3064 FFStream *stream, const char *session_id,
3065 enum RTSPProtocol rtp_protocol)
3067 HTTPContext *c = NULL;
3068 const char *proto_str;
3070 /* XXX: should output a warning page when coming
3071 close to the connection limit */
3072 if (nb_connections >= nb_max_connections)
3075 /* add a new connection */
3076 c = av_mallocz(sizeof(HTTPContext));
3081 c->poll_entry = NULL;
3082 c->from_addr = *from_addr;
3083 c->buffer_size = IOBUFFER_INIT_SIZE;
3084 c->buffer = av_malloc(c->buffer_size);
3089 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3090 c->state = HTTPSTATE_READY;
3091 c->is_packetized = 1;
3092 c->rtp_protocol = rtp_protocol;
3094 /* protocol is shown in statistics */
3095 switch(c->rtp_protocol) {
3096 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3097 proto_str = "MCAST";
3099 case RTSP_PROTOCOL_RTP_UDP:
3102 case RTSP_PROTOCOL_RTP_TCP:
3109 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3110 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3112 current_bandwidth += stream->bandwidth;
3114 c->next = first_http_ctx;
3126 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3127 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3129 static int rtp_new_av_stream(HTTPContext *c,
3130 int stream_index, struct sockaddr_in *dest_addr,
3131 HTTPContext *rtsp_c)
3133 AVFormatContext *ctx;
3136 URLContext *h = NULL;
3138 int max_packet_size;
3140 /* now we can open the relevant output stream */
3141 ctx = av_alloc_format_context();
3144 ctx->oformat = guess_format("rtp", NULL, NULL);
3146 st = av_mallocz(sizeof(AVStream));
3149 st->codec= avcodec_alloc_context();
3150 ctx->nb_streams = 1;
3151 ctx->streams[0] = st;
3153 if (!c->stream->feed ||
3154 c->stream->feed == c->stream)
3155 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3158 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3160 st->priv_data = NULL;
3162 /* build destination RTP address */
3163 ipaddr = inet_ntoa(dest_addr->sin_addr);
3165 switch(c->rtp_protocol) {
3166 case RTSP_PROTOCOL_RTP_UDP:
3167 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3170 /* XXX: also pass as parameter to function ? */
3171 if (c->stream->is_multicast) {
3173 ttl = c->stream->multicast_ttl;
3176 snprintf(ctx->filename, sizeof(ctx->filename),
3177 "rtp://%s:%d?multicast=1&ttl=%d",
3178 ipaddr, ntohs(dest_addr->sin_port), ttl);
3180 snprintf(ctx->filename, sizeof(ctx->filename),
3181 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3184 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3186 c->rtp_handles[stream_index] = h;
3187 max_packet_size = url_get_max_packet_size(h);
3189 case RTSP_PROTOCOL_RTP_TCP:
3192 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3198 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3199 ipaddr, ntohs(dest_addr->sin_port),
3200 c->stream->filename, stream_index, c->protocol);
3202 /* normally, no packets should be output here, but the packet size may be checked */
3203 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3204 /* XXX: close stream */
3207 av_set_parameters(ctx, NULL);
3208 if (av_write_header(ctx) < 0) {
3215 url_close_dyn_buf(ctx->pb, &dummy_buf);
3218 c->rtp_ctx[stream_index] = ctx;
3222 /********************************************************************/
3223 /* ffserver initialization */
3225 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3229 fst = av_mallocz(sizeof(AVStream));
3232 fst->codec= avcodec_alloc_context();
3233 fst->priv_data = av_mallocz(sizeof(FeedData));
3234 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3235 fst->index = stream->nb_streams;
3236 av_set_pts_info(fst, 33, 1, 90000);
3237 stream->streams[stream->nb_streams++] = fst;
3241 /* return the stream number in the feed */
3242 static int add_av_stream(FFStream *feed, AVStream *st)
3245 AVCodecContext *av, *av1;
3249 for(i=0;i<feed->nb_streams;i++) {
3250 st = feed->streams[i];
3252 if (av1->codec_id == av->codec_id &&
3253 av1->codec_type == av->codec_type &&
3254 av1->bit_rate == av->bit_rate) {
3256 switch(av->codec_type) {
3257 case CODEC_TYPE_AUDIO:
3258 if (av1->channels == av->channels &&
3259 av1->sample_rate == av->sample_rate)
3262 case CODEC_TYPE_VIDEO:
3263 if (av1->width == av->width &&
3264 av1->height == av->height &&
3265 av1->time_base.den == av->time_base.den &&
3266 av1->time_base.num == av->time_base.num &&
3267 av1->gop_size == av->gop_size)
3276 fst = add_av_stream1(feed, av);
3279 return feed->nb_streams - 1;
3284 static void remove_stream(FFStream *stream)
3288 while (*ps != NULL) {
3296 /* specific mpeg4 handling : we extract the raw parameters */
3297 static void extract_mpeg4_header(AVFormatContext *infile)
3299 int mpeg4_count, i, size;
3305 for(i=0;i<infile->nb_streams;i++) {
3306 st = infile->streams[i];
3307 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3308 st->codec->extradata_size == 0) {
3315 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3316 while (mpeg4_count > 0) {
3317 if (av_read_packet(infile, &pkt) < 0)
3319 st = infile->streams[pkt.stream_index];
3320 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3321 st->codec->extradata_size == 0) {
3322 av_freep(&st->codec->extradata);
3323 /* fill extradata with the header */
3324 /* XXX: we make hard suppositions here ! */
3326 while (p < pkt.data + pkt.size - 4) {
3327 /* stop when vop header is found */
3328 if (p[0] == 0x00 && p[1] == 0x00 &&
3329 p[2] == 0x01 && p[3] == 0xb6) {
3330 size = p - pkt.data;
3331 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3332 st->codec->extradata = av_malloc(size);
3333 st->codec->extradata_size = size;
3334 memcpy(st->codec->extradata, pkt.data, size);
3341 av_free_packet(&pkt);
3345 /* compute the needed AVStream for each file */
3346 static void build_file_streams(void)
3348 FFStream *stream, *stream_next;
3349 AVFormatContext *infile;
3352 /* gather all streams */
3353 for(stream = first_stream; stream != NULL; stream = stream_next) {
3354 stream_next = stream->next;
3355 if (stream->stream_type == STREAM_TYPE_LIVE &&
3357 /* the stream comes from a file */
3358 /* try to open the file */
3360 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3361 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3362 /* specific case : if transport stream output to RTP,
3363 we use a raw transport stream reader */
3364 stream->ap_in->mpeg2ts_raw = 1;
3365 stream->ap_in->mpeg2ts_compute_pcr = 1;
3368 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3369 stream->ifmt, 0, stream->ap_in)) < 0) {
3370 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3371 /* remove stream (no need to spend more time on it) */
3373 remove_stream(stream);
3375 /* find all the AVStreams inside and reference them in
3377 if (av_find_stream_info(infile) < 0) {
3378 http_log("Could not find codec parameters from '%s'\n",
3379 stream->feed_filename);
3380 av_close_input_file(infile);
3383 extract_mpeg4_header(infile);
3385 for(i=0;i<infile->nb_streams;i++)
3386 add_av_stream1(stream, infile->streams[i]->codec);
3388 av_close_input_file(infile);
3394 /* compute the needed AVStream for each feed */
3395 static void build_feed_streams(void)
3397 FFStream *stream, *feed;
3400 /* gather all streams */
3401 for(stream = first_stream; stream != NULL; stream = stream->next) {
3402 feed = stream->feed;
3404 if (!stream->is_feed) {
3405 /* we handle a stream coming from a feed */
3406 for(i=0;i<stream->nb_streams;i++)
3407 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3412 /* gather all streams */
3413 for(stream = first_stream; stream != NULL; stream = stream->next) {
3414 feed = stream->feed;
3416 if (stream->is_feed) {
3417 for(i=0;i<stream->nb_streams;i++)
3418 stream->feed_streams[i] = i;
3423 /* create feed files if needed */
3424 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3427 if (url_exist(feed->feed_filename)) {
3428 /* See if it matches */
3432 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3433 /* Now see if it matches */
3434 if (s->nb_streams == feed->nb_streams) {
3436 for(i=0;i<s->nb_streams;i++) {
3438 sf = feed->streams[i];
3441 if (sf->index != ss->index ||
3443 printf("Index & Id do not match for stream %d (%s)\n",
3444 i, feed->feed_filename);
3447 AVCodecContext *ccf, *ccs;
3451 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3453 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3454 printf("Codecs do not match for stream %d\n", i);
3456 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3457 printf("Codec bitrates do not match for stream %d\n", i);
3459 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3460 if (CHECK_CODEC(time_base.den) ||
3461 CHECK_CODEC(time_base.num) ||
3462 CHECK_CODEC(width) ||
3463 CHECK_CODEC(height)) {
3464 printf("Codec width, height and framerate do not match for stream %d\n", i);
3467 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3468 if (CHECK_CODEC(sample_rate) ||
3469 CHECK_CODEC(channels) ||
3470 CHECK_CODEC(frame_size)) {
3471 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3475 printf("Unknown codec type\n");
3483 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3484 feed->feed_filename, s->nb_streams, feed->nb_streams);
3486 av_close_input_file(s);
3488 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3489 feed->feed_filename);
3492 if (feed->readonly) {
3493 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3494 feed->feed_filename);
3497 unlink(feed->feed_filename);
3500 if (!url_exist(feed->feed_filename)) {
3501 AVFormatContext s1, *s = &s1;
3503 if (feed->readonly) {
3504 printf("Unable to create feed file '%s' as it is marked readonly\n",
3505 feed->feed_filename);
3509 /* only write the header of the ffm file */
3510 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3511 http_log("Could not open output feed file '%s'\n",
3512 feed->feed_filename);
3515 s->oformat = feed->fmt;
3516 s->nb_streams = feed->nb_streams;
3517 for(i=0;i<s->nb_streams;i++) {
3519 st = feed->streams[i];
3522 av_set_parameters(s, NULL);
3523 if (av_write_header(s) < 0) {
3524 http_log("Container doesn't supports the required parameters\n");
3527 /* XXX: need better api */
3528 av_freep(&s->priv_data);
3531 /* get feed size and write index */
3532 fd = open(feed->feed_filename, O_RDONLY);
3534 http_log("Could not open output feed file '%s'\n",
3535 feed->feed_filename);
3539 feed->feed_write_index = ffm_read_write_index(fd);
3540 feed->feed_size = lseek(fd, 0, SEEK_END);
3541 /* ensure that we do not wrap before the end of file */
3542 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3543 feed->feed_max_size = feed->feed_size;
3549 /* compute the bandwidth used by each stream */
3550 static void compute_bandwidth(void)
3556 for(stream = first_stream; stream != NULL; stream = stream->next) {
3558 for(i=0;i<stream->nb_streams;i++) {
3559 AVStream *st = stream->streams[i];
3560 switch(st->codec->codec_type) {
3561 case CODEC_TYPE_AUDIO:
3562 case CODEC_TYPE_VIDEO:
3563 bandwidth += st->codec->bit_rate;
3569 stream->bandwidth = (bandwidth + 999) / 1000;
3573 static void get_arg(char *buf, int buf_size, const char **pp)
3580 while (isspace(*p)) p++;
3583 if (*p == '\"' || *p == '\'')
3595 if ((q - buf) < buf_size - 1)
3600 if (quote && *p == quote)
3605 /* add a codec and set the default parameters */
3606 static void add_codec(FFStream *stream, AVCodecContext *av)
3610 /* compute default parameters */
3611 switch(av->codec_type) {
3612 case CODEC_TYPE_AUDIO:
3613 if (av->bit_rate == 0)
3614 av->bit_rate = 64000;
3615 if (av->sample_rate == 0)
3616 av->sample_rate = 22050;
3617 if (av->channels == 0)
3620 case CODEC_TYPE_VIDEO:
3621 if (av->bit_rate == 0)
3622 av->bit_rate = 64000;
3623 if (av->time_base.num == 0){
3624 av->time_base.den = 5;
3625 av->time_base.num = 1;
3627 if (av->width == 0 || av->height == 0) {
3631 /* Bitrate tolerance is less for streaming */
3632 if (av->bit_rate_tolerance == 0)
3633 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3634 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3639 if (av->max_qdiff == 0)
3641 av->qcompress = 0.5;
3644 if (!av->nsse_weight)
3645 av->nsse_weight = 8;
3647 av->frame_skip_cmp = FF_CMP_DCTMAX;
3648 av->me_method = ME_EPZS;
3649 av->rc_buffer_aggressivity = 1.0;
3652 av->rc_eq = "tex^qComp";
3653 if (!av->i_quant_factor)
3654 av->i_quant_factor = -0.8;
3655 if (!av->b_quant_factor)
3656 av->b_quant_factor = 1.25;
3657 if (!av->b_quant_offset)
3658 av->b_quant_offset = 1.25;
3659 if (!av->rc_max_rate)
3660 av->rc_max_rate = av->bit_rate * 2;
3662 if (av->rc_max_rate && !av->rc_buffer_size) {
3663 av->rc_buffer_size = av->rc_max_rate;
3672 st = av_mallocz(sizeof(AVStream));
3675 st->codec = avcodec_alloc_context();
3676 stream->streams[stream->nb_streams++] = st;
3677 memcpy(st->codec, av, sizeof(AVCodecContext));
3680 static int opt_audio_codec(const char *arg)
3682 AVCodec *p= avcodec_find_encoder_by_name(arg);
3684 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3685 return CODEC_ID_NONE;
3690 static int opt_video_codec(const char *arg)
3692 AVCodec *p= avcodec_find_encoder_by_name(arg);
3694 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3695 return CODEC_ID_NONE;
3700 /* simplistic plugin support */
3703 static void load_module(const char *filename)
3706 void (*init_func)(void);
3707 dll = dlopen(filename, RTLD_NOW);
3709 fprintf(stderr, "Could not load module '%s' - %s\n",
3710 filename, dlerror());
3714 init_func = dlsym(dll, "ffserver_module_init");
3717 "%s: init function 'ffserver_module_init()' not found\n",
3726 static int opt_default(const char *opt, const char *arg,
3727 AVCodecContext *avctx, int type)
3729 const AVOption *o = NULL;
3730 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3732 o = av_set_string(avctx, opt, arg);
3738 static int parse_ffconfig(const char *filename)
3745 int val, errors, line_num;
3746 FFStream **last_stream, *stream, *redirect;
3747 FFStream **last_feed, *feed;
3748 AVCodecContext audio_enc, video_enc;
3749 int audio_id, video_id;
3751 f = fopen(filename, "r");
3759 first_stream = NULL;
3760 last_stream = &first_stream;
3762 last_feed = &first_feed;
3766 audio_id = CODEC_ID_NONE;
3767 video_id = CODEC_ID_NONE;
3769 if (fgets(line, sizeof(line), f) == NULL)
3775 if (*p == '\0' || *p == '#')
3778 get_arg(cmd, sizeof(cmd), &p);
3780 if (!strcasecmp(cmd, "Port")) {
3781 get_arg(arg, sizeof(arg), &p);
3783 if (val < 1 || val > 65536) {
3784 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3785 filename, line_num, arg);
3788 my_http_addr.sin_port = htons(val);
3789 } else if (!strcasecmp(cmd, "BindAddress")) {
3790 get_arg(arg, sizeof(arg), &p);
3791 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3792 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3793 filename, line_num, arg);
3796 } else if (!strcasecmp(cmd, "NoDaemon")) {
3797 ffserver_daemon = 0;
3798 } else if (!strcasecmp(cmd, "RTSPPort")) {
3799 get_arg(arg, sizeof(arg), &p);
3801 if (val < 1 || val > 65536) {
3802 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3803 filename, line_num, arg);
3806 my_rtsp_addr.sin_port = htons(atoi(arg));
3807 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3808 get_arg(arg, sizeof(arg), &p);
3809 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3810 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3811 filename, line_num, arg);
3814 } else if (!strcasecmp(cmd, "MaxClients")) {
3815 get_arg(arg, sizeof(arg), &p);
3817 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3818 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3819 filename, line_num, arg);
3822 nb_max_connections = val;
3824 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3826 get_arg(arg, sizeof(arg), &p);
3828 if (llval < 10 || llval > 10000000) {
3829 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3830 filename, line_num, arg);
3833 max_bandwidth = llval;
3834 } else if (!strcasecmp(cmd, "CustomLog")) {
3835 if (!ffserver_debug)
3836 get_arg(logfilename, sizeof(logfilename), &p);
3837 } else if (!strcasecmp(cmd, "<Feed")) {
3838 /*********************************************/
3839 /* Feed related options */
3841 if (stream || feed) {
3842 fprintf(stderr, "%s:%d: Already in a tag\n",
3843 filename, line_num);
3845 feed = av_mallocz(sizeof(FFStream));
3846 /* add in stream list */
3847 *last_stream = feed;
3848 last_stream = &feed->next;
3849 /* add in feed list */
3851 last_feed = &feed->next_feed;
3853 get_arg(feed->filename, sizeof(feed->filename), &p);
3854 q = strrchr(feed->filename, '>');
3857 feed->fmt = guess_format("ffm", NULL, NULL);
3858 /* defaut feed file */
3859 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3860 "/tmp/%s.ffm", feed->filename);
3861 feed->feed_max_size = 5 * 1024 * 1024;
3863 feed->feed = feed; /* self feeding :-) */
3865 } else if (!strcasecmp(cmd, "Launch")) {
3869 feed->child_argv = av_mallocz(64 * sizeof(char *));
3871 for (i = 0; i < 62; i++) {
3872 get_arg(arg, sizeof(arg), &p);
3876 feed->child_argv[i] = av_strdup(arg);
3879 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3881 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3883 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3884 inet_ntoa(my_http_addr.sin_addr),
3885 ntohs(my_http_addr.sin_port), feed->filename);
3890 fprintf(stdout, "Launch commandline: ");
3891 for (j = 0; j <= i; j++)
3892 fprintf(stdout, "%s ", feed->child_argv[j]);
3893 fprintf(stdout, "\n");
3896 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3898 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3900 } else if (stream) {
3901 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3903 } else if (!strcasecmp(cmd, "File")) {
3905 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3907 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3908 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3913 get_arg(arg, sizeof(arg), &p);
3915 fsize = strtod(p1, &p1);
3916 switch(toupper(*p1)) {
3921 fsize *= 1024 * 1024;
3924 fsize *= 1024 * 1024 * 1024;
3927 feed->feed_max_size = (int64_t)fsize;
3929 } else if (!strcasecmp(cmd, "</Feed>")) {
3931 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3932 filename, line_num);
3936 } else if (!strcasecmp(cmd, "<Stream")) {
3937 /*********************************************/
3938 /* Stream related options */
3940 if (stream || feed) {
3941 fprintf(stderr, "%s:%d: Already in a tag\n",
3942 filename, line_num);
3944 const AVClass *class;
3945 stream = av_mallocz(sizeof(FFStream));
3946 *last_stream = stream;
3947 last_stream = &stream->next;
3949 get_arg(stream->filename, sizeof(stream->filename), &p);
3950 q = strrchr(stream->filename, '>');
3953 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3954 /* fetch avclass so AVOption works
3955 * FIXME try to use avcodec_get_context_defaults2
3956 * without changing defaults too much */
3957 avcodec_get_context_defaults(&video_enc);
3958 class = video_enc.av_class;
3959 memset(&audio_enc, 0, sizeof(AVCodecContext));
3960 memset(&video_enc, 0, sizeof(AVCodecContext));
3961 audio_enc.av_class = class;
3962 video_enc.av_class = class;
3963 audio_id = CODEC_ID_NONE;
3964 video_id = CODEC_ID_NONE;
3966 audio_id = stream->fmt->audio_codec;
3967 video_id = stream->fmt->video_codec;
3970 } else if (!strcasecmp(cmd, "Feed")) {
3971 get_arg(arg, sizeof(arg), &p);
3976 while (sfeed != NULL) {
3977 if (!strcmp(sfeed->filename, arg))
3979 sfeed = sfeed->next_feed;
3982 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3983 filename, line_num, arg);
3985 stream->feed = sfeed;
3987 } else if (!strcasecmp(cmd, "Format")) {
3988 get_arg(arg, sizeof(arg), &p);
3990 if (!strcmp(arg, "status")) {
3991 stream->stream_type = STREAM_TYPE_STATUS;
3994 stream->stream_type = STREAM_TYPE_LIVE;
3995 /* jpeg cannot be used here, so use single frame jpeg */
3996 if (!strcmp(arg, "jpeg"))
3997 strcpy(arg, "mjpeg");
3998 stream->fmt = guess_stream_format(arg, NULL, NULL);
4000 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4001 filename, line_num, arg);
4006 audio_id = stream->fmt->audio_codec;
4007 video_id = stream->fmt->video_codec;
4010 } else if (!strcasecmp(cmd, "InputFormat")) {
4011 get_arg(arg, sizeof(arg), &p);
4012 stream->ifmt = av_find_input_format(arg);
4013 if (!stream->ifmt) {
4014 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4015 filename, line_num, arg);
4017 } else if (!strcasecmp(cmd, "FaviconURL")) {
4018 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4019 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4021 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4022 filename, line_num);
4025 } else if (!strcasecmp(cmd, "Author")) {
4027 get_arg(stream->author, sizeof(stream->author), &p);
4028 } else if (!strcasecmp(cmd, "Comment")) {
4030 get_arg(stream->comment, sizeof(stream->comment), &p);
4031 } else if (!strcasecmp(cmd, "Copyright")) {
4033 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4034 } else if (!strcasecmp(cmd, "Title")) {
4036 get_arg(stream->title, sizeof(stream->title), &p);
4037 } else if (!strcasecmp(cmd, "Preroll")) {
4038 get_arg(arg, sizeof(arg), &p);
4040 stream->prebuffer = atof(arg) * 1000;
4041 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4043 stream->send_on_key = 1;
4044 } else if (!strcasecmp(cmd, "AudioCodec")) {
4045 get_arg(arg, sizeof(arg), &p);
4046 audio_id = opt_audio_codec(arg);
4047 if (audio_id == CODEC_ID_NONE) {
4048 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4049 filename, line_num, arg);
4052 } else if (!strcasecmp(cmd, "VideoCodec")) {
4053 get_arg(arg, sizeof(arg), &p);
4054 video_id = opt_video_codec(arg);
4055 if (video_id == CODEC_ID_NONE) {
4056 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4057 filename, line_num, arg);
4060 } else if (!strcasecmp(cmd, "MaxTime")) {
4061 get_arg(arg, sizeof(arg), &p);
4063 stream->max_time = atof(arg) * 1000;
4064 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4065 get_arg(arg, sizeof(arg), &p);
4067 audio_enc.bit_rate = atoi(arg) * 1000;
4068 } else if (!strcasecmp(cmd, "AudioChannels")) {
4069 get_arg(arg, sizeof(arg), &p);
4071 audio_enc.channels = atoi(arg);
4072 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4073 get_arg(arg, sizeof(arg), &p);
4075 audio_enc.sample_rate = atoi(arg);
4076 } else if (!strcasecmp(cmd, "AudioQuality")) {
4077 get_arg(arg, sizeof(arg), &p);
4079 // audio_enc.quality = atof(arg) * 1000;
4081 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4083 int minrate, maxrate;
4085 get_arg(arg, sizeof(arg), &p);
4087 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4088 video_enc.rc_min_rate = minrate * 1000;
4089 video_enc.rc_max_rate = maxrate * 1000;
4091 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4092 filename, line_num, arg);
4096 } else if (!strcasecmp(cmd, "Debug")) {
4098 get_arg(arg, sizeof(arg), &p);
4099 video_enc.debug = strtol(arg,0,0);
4101 } else if (!strcasecmp(cmd, "Strict")) {
4103 get_arg(arg, sizeof(arg), &p);
4104 video_enc.strict_std_compliance = atoi(arg);
4106 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4108 get_arg(arg, sizeof(arg), &p);
4109 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4111 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4113 get_arg(arg, sizeof(arg), &p);
4114 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4116 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4117 get_arg(arg, sizeof(arg), &p);
4119 video_enc.bit_rate = atoi(arg) * 1000;
4121 } else if (!strcasecmp(cmd, "VideoSize")) {
4122 get_arg(arg, sizeof(arg), &p);
4124 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4125 if ((video_enc.width % 16) != 0 ||
4126 (video_enc.height % 16) != 0) {
4127 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4128 filename, line_num);
4132 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4133 get_arg(arg, sizeof(arg), &p);
4135 AVRational frame_rate;
4136 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4137 fprintf(stderr, "Incorrect frame rate\n");
4140 video_enc.time_base.num = frame_rate.den;
4141 video_enc.time_base.den = frame_rate.num;
4144 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4145 get_arg(arg, sizeof(arg), &p);
4147 video_enc.gop_size = atoi(arg);
4148 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4150 video_enc.gop_size = 1;
4151 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4153 video_enc.mb_decision = FF_MB_DECISION_BITS;
4154 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4156 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4157 video_enc.flags |= CODEC_FLAG_4MV;
4159 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4160 !strcasecmp(cmd, "AVOptionAudio")) {
4162 AVCodecContext *avctx;
4164 get_arg(arg, sizeof(arg), &p);
4165 get_arg(arg2, sizeof(arg2), &p);
4166 if (!strcasecmp(cmd, "AVOptionVideo")) {
4168 type = AV_OPT_FLAG_VIDEO_PARAM;
4171 type = AV_OPT_FLAG_AUDIO_PARAM;
4173 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4174 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4177 } else if (!strcasecmp(cmd, "VideoTag")) {
4178 get_arg(arg, sizeof(arg), &p);
4179 if ((strlen(arg) == 4) && stream)
4180 video_enc.codec_tag = ff_get_fourcc(arg);
4181 } else if (!strcasecmp(cmd, "BitExact")) {
4183 video_enc.flags |= CODEC_FLAG_BITEXACT;
4184 } else if (!strcasecmp(cmd, "DctFastint")) {
4186 video_enc.dct_algo = FF_DCT_FASTINT;
4187 } else if (!strcasecmp(cmd, "IdctSimple")) {
4189 video_enc.idct_algo = FF_IDCT_SIMPLE;
4190 } else if (!strcasecmp(cmd, "Qscale")) {
4191 get_arg(arg, sizeof(arg), &p);
4193 video_enc.flags |= CODEC_FLAG_QSCALE;
4194 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4196 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4197 get_arg(arg, sizeof(arg), &p);
4199 video_enc.max_qdiff = atoi(arg);
4200 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4201 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4202 filename, line_num);
4206 } else if (!strcasecmp(cmd, "VideoQMax")) {
4207 get_arg(arg, sizeof(arg), &p);
4209 video_enc.qmax = atoi(arg);
4210 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4211 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4212 filename, line_num);
4216 } else if (!strcasecmp(cmd, "VideoQMin")) {
4217 get_arg(arg, sizeof(arg), &p);
4219 video_enc.qmin = atoi(arg);
4220 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4221 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4222 filename, line_num);
4226 } else if (!strcasecmp(cmd, "LumaElim")) {
4227 get_arg(arg, sizeof(arg), &p);
4229 video_enc.luma_elim_threshold = atoi(arg);
4230 } else if (!strcasecmp(cmd, "ChromaElim")) {
4231 get_arg(arg, sizeof(arg), &p);
4233 video_enc.chroma_elim_threshold = atoi(arg);
4234 } else if (!strcasecmp(cmd, "LumiMask")) {
4235 get_arg(arg, sizeof(arg), &p);
4237 video_enc.lumi_masking = atof(arg);
4238 } else if (!strcasecmp(cmd, "DarkMask")) {
4239 get_arg(arg, sizeof(arg), &p);
4241 video_enc.dark_masking = atof(arg);
4242 } else if (!strcasecmp(cmd, "NoVideo")) {
4243 video_id = CODEC_ID_NONE;
4244 } else if (!strcasecmp(cmd, "NoAudio")) {
4245 audio_id = CODEC_ID_NONE;
4246 } else if (!strcasecmp(cmd, "ACL")) {
4249 get_arg(arg, sizeof(arg), &p);
4250 if (strcasecmp(arg, "allow") == 0)
4251 acl.action = IP_ALLOW;
4252 else if (strcasecmp(arg, "deny") == 0)
4253 acl.action = IP_DENY;
4255 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4256 filename, line_num, arg);
4260 get_arg(arg, sizeof(arg), &p);
4262 if (resolve_host(&acl.first, arg) != 0) {
4263 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4264 filename, line_num, arg);
4267 acl.last = acl.first;
4269 get_arg(arg, sizeof(arg), &p);
4272 if (resolve_host(&acl.last, arg) != 0) {
4273 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4274 filename, line_num, arg);
4280 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4281 IPAddressACL **naclp = 0;
4287 naclp = &stream->acl;
4291 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4292 filename, line_num);
4298 naclp = &(*naclp)->next;
4303 } else if (!strcasecmp(cmd, "RTSPOption")) {
4304 get_arg(arg, sizeof(arg), &p);
4306 av_freep(&stream->rtsp_option);
4307 stream->rtsp_option = av_strdup(arg);
4309 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4310 get_arg(arg, sizeof(arg), &p);
4312 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4313 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4314 filename, line_num, arg);
4317 stream->is_multicast = 1;
4318 stream->loop = 1; /* default is looping */
4320 } else if (!strcasecmp(cmd, "MulticastPort")) {
4321 get_arg(arg, sizeof(arg), &p);
4323 stream->multicast_port = atoi(arg);
4324 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4325 get_arg(arg, sizeof(arg), &p);
4327 stream->multicast_ttl = atoi(arg);
4328 } else if (!strcasecmp(cmd, "NoLoop")) {
4331 } else if (!strcasecmp(cmd, "</Stream>")) {
4333 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4334 filename, line_num);
4337 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4338 if (audio_id != CODEC_ID_NONE) {
4339 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4340 audio_enc.codec_id = audio_id;
4341 add_codec(stream, &audio_enc);
4343 if (video_id != CODEC_ID_NONE) {
4344 video_enc.codec_type = CODEC_TYPE_VIDEO;
4345 video_enc.codec_id = video_id;
4346 add_codec(stream, &video_enc);
4351 } else if (!strcasecmp(cmd, "<Redirect")) {
4352 /*********************************************/
4354 if (stream || feed || redirect) {
4355 fprintf(stderr, "%s:%d: Already in a tag\n",
4356 filename, line_num);
4359 redirect = av_mallocz(sizeof(FFStream));
4360 *last_stream = redirect;
4361 last_stream = &redirect->next;
4363 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4364 q = strrchr(redirect->filename, '>');
4367 redirect->stream_type = STREAM_TYPE_REDIRECT;
4369 } else if (!strcasecmp(cmd, "URL")) {
4371 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4372 } else if (!strcasecmp(cmd, "</Redirect>")) {
4374 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4375 filename, line_num);
4378 if (!redirect->feed_filename[0]) {
4379 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4380 filename, line_num);
4385 } else if (!strcasecmp(cmd, "LoadModule")) {
4386 get_arg(arg, sizeof(arg), &p);
4390 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4391 filename, line_num, arg);
4395 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4396 filename, line_num, cmd);
4408 static void handle_child_exit(int sig)
4413 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4416 for (feed = first_feed; feed; feed = feed->next) {
4417 if (feed->pid == pid) {
4418 int uptime = time(0) - feed->pid_start;
4421 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4424 /* Turn off any more restarts */
4425 feed->child_argv = 0;
4430 need_to_start_children = 1;
4433 static void opt_debug()
4436 ffserver_daemon = 0;
4437 logfilename[0] = '-';
4440 static void opt_show_help(void)
4442 printf("usage: ffserver [options]\n"
4443 "Hyper fast multi format Audio/Video streaming server\n");
4445 show_help_options(options, "Main options:\n", 0, 0);
4448 static const OptionDef options[] = {
4449 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4450 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4451 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4452 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4453 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4454 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4455 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4459 int main(int argc, char **argv)
4461 struct sigaction sigact;
4467 config_filename = "/etc/ffserver.conf";
4469 my_program_name = argv[0];
4470 my_program_dir = getcwd(0, 0);
4471 ffserver_daemon = 1;
4473 parse_options(argc, argv, options, NULL);
4475 unsetenv("http_proxy"); /* Kill the http_proxy */
4477 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4479 memset(&sigact, 0, sizeof(sigact));
4480 sigact.sa_handler = handle_child_exit;
4481 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4482 sigaction(SIGCHLD, &sigact, 0);
4484 if (parse_ffconfig(config_filename) < 0) {
4485 fprintf(stderr, "Incorrect config file - exiting.\n");
4489 build_file_streams();
4491 build_feed_streams();
4493 compute_bandwidth();
4495 /* put the process in background and detach it from its TTY */
4496 if (ffserver_daemon) {
4503 } else if (pid > 0) {
4510 open("/dev/null", O_RDWR);
4511 if (strcmp(logfilename, "-") != 0) {
4521 signal(SIGPIPE, SIG_IGN);
4523 /* open log file if needed */
4524 if (logfilename[0] != '\0') {
4525 if (!strcmp(logfilename, "-"))
4528 logfile = fopen(logfilename, "a");
4529 av_log_set_callback(http_av_log);
4532 if (ffserver_daemon)
4535 if (http_server() < 0) {
4536 http_log("Could not start server\n");