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
22 #define _XOPEN_SOURCE 600
25 #ifndef HAVE_CLOSESOCKET
26 #define closesocket close
30 #include "libavutil/random.h"
31 #include "libavutil/avstring.h"
32 #include "libavformat/avformat.h"
33 #include "libavformat/network.h"
34 #include "libavformat/os_support.h"
35 #include "libavformat/rtp.h"
36 #include "libavformat/rtsp.h"
37 #include "libavcodec/opt.h"
41 #include <sys/ioctl.h>
47 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
59 const char program_name[] = "FFserver";
60 const int program_birth_year = 2000;
62 static const OptionDef options[];
64 /* maximum number of simultaneous HTTP connections */
65 #define HTTP_MAX_CONNECTIONS 2000
68 HTTPSTATE_WAIT_REQUEST,
69 HTTPSTATE_SEND_HEADER,
70 HTTPSTATE_SEND_DATA_HEADER,
71 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
72 HTTPSTATE_SEND_DATA_TRAILER,
73 HTTPSTATE_RECEIVE_DATA,
74 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
77 RTSPSTATE_WAIT_REQUEST,
79 RTSPSTATE_SEND_PACKET,
82 static const char *http_state[] = {
98 #define IOBUFFER_INIT_SIZE 8192
100 /* timeouts are in ms */
101 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
102 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
104 #define SYNC_TIMEOUT (10 * 1000)
107 int64_t count1, count2;
108 int64_t time1, time2;
111 /* context associated with one connection */
112 typedef struct HTTPContext {
113 enum HTTPState state;
114 int fd; /* socket file descriptor */
115 struct sockaddr_in from_addr; /* origin */
116 struct pollfd *poll_entry; /* used when polling */
118 uint8_t *buffer_ptr, *buffer_end;
121 struct HTTPContext *next;
122 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
126 /* input format handling */
127 AVFormatContext *fmt_in;
128 int64_t start_time; /* In milliseconds - this wraps fairly often */
129 int64_t first_pts; /* initial pts value */
130 int64_t cur_pts; /* current pts value from the stream in us */
131 int64_t cur_frame_duration; /* duration of the current frame in us */
132 int cur_frame_bytes; /* output frame size, needed to compute
133 the time at which we send each
135 int pts_stream_index; /* stream we choose as clock reference */
136 int64_t cur_clock; /* current clock reference value in us */
137 /* output format handling */
138 struct FFStream *stream;
139 /* -1 is invalid stream */
140 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
141 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
143 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
144 int last_packet_sent; /* true if last data packet was sent */
146 DataRateData datarate;
153 int is_packetized; /* if true, the stream is packetized */
154 int packet_stream_index; /* current stream for output in state machine */
156 /* RTSP state specific */
157 uint8_t *pb_buffer; /* XXX: use that in all the code */
159 int seq; /* RTSP sequence number */
161 /* RTP state specific */
162 enum RTSPProtocol rtp_protocol;
163 char session_id[32]; /* session id */
164 AVFormatContext *rtp_ctx[MAX_STREAMS];
166 /* RTP/UDP specific */
167 URLContext *rtp_handles[MAX_STREAMS];
169 /* RTP/TCP specific */
170 struct HTTPContext *rtsp_c;
171 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
174 /* each generated stream is described here */
178 STREAM_TYPE_REDIRECT,
181 enum IPAddressAction {
186 typedef struct IPAddressACL {
187 struct IPAddressACL *next;
188 enum IPAddressAction action;
189 /* These are in host order */
190 struct in_addr first;
194 /* description of each stream of the ffserver.conf file */
195 typedef struct FFStream {
196 enum StreamType stream_type;
197 char filename[1024]; /* stream filename */
198 struct FFStream *feed; /* feed we are using (can be null if
200 AVFormatParameters *ap_in; /* input parameters */
201 AVInputFormat *ifmt; /* if non NULL, force input format */
205 int prebuffer; /* Number of millseconds early to start */
206 int64_t max_time; /* Number of milliseconds to run */
208 AVStream *streams[MAX_STREAMS];
209 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
210 char feed_filename[1024]; /* file name of the feed storage, or
211 input file name for a stream */
216 pid_t pid; /* Of ffmpeg process */
217 time_t pid_start; /* Of ffmpeg process */
219 struct FFStream *next;
220 unsigned bandwidth; /* bandwidth, in kbits/s */
223 /* multicast specific */
225 struct in_addr multicast_ip;
226 int multicast_port; /* first port used for multicast */
228 int loop; /* if true, send the stream in loops (only meaningful if file) */
231 int feed_opened; /* true if someone is writing to the feed */
232 int is_feed; /* true if it is a feed */
233 int readonly; /* True if writing is prohibited to the file */
235 int64_t bytes_served;
236 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
237 int64_t feed_write_index; /* current write position in feed (it wraps around) */
238 int64_t feed_size; /* current size of feed */
239 struct FFStream *next_feed;
242 typedef struct FeedData {
243 long long data_count;
244 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
247 static struct sockaddr_in my_http_addr;
248 static struct sockaddr_in my_rtsp_addr;
250 static char logfilename[1024];
251 static HTTPContext *first_http_ctx;
252 static FFStream *first_feed; /* contains only feeds */
253 static FFStream *first_stream; /* contains all streams, including feeds */
255 static void new_connection(int server_fd, int is_rtsp);
256 static void close_connection(HTTPContext *c);
259 static int handle_connection(HTTPContext *c);
260 static int http_parse_request(HTTPContext *c);
261 static int http_send_data(HTTPContext *c);
262 static void compute_status(HTTPContext *c);
263 static int open_input_stream(HTTPContext *c, const char *info);
264 static int http_start_receive_data(HTTPContext *c);
265 static int http_receive_data(HTTPContext *c);
268 static int rtsp_parse_request(HTTPContext *c);
269 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
270 static void rtsp_cmd_options(HTTPContext *c, const char *url);
271 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
273 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
274 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
277 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
278 struct in_addr my_ip);
281 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
282 FFStream *stream, const char *session_id,
283 enum RTSPProtocol rtp_protocol);
284 static int rtp_new_av_stream(HTTPContext *c,
285 int stream_index, struct sockaddr_in *dest_addr,
286 HTTPContext *rtsp_c);
288 static const char *my_program_name;
289 static const char *my_program_dir;
291 static const char *config_filename;
292 static int ffserver_debug;
293 static int ffserver_daemon;
294 static int no_launch;
295 static int need_to_start_children;
297 static int nb_max_connections = 5;
298 static int nb_connections;
300 static uint64_t max_bandwidth = 1000;
301 static uint64_t current_bandwidth;
303 static int64_t cur_time; // Making this global saves on passing it around everywhere
305 static AVRandomState random_state;
307 static FILE *logfile = NULL;
309 static char *ctime1(char *buf2)
317 p = buf2 + strlen(p) - 1;
323 static void http_vlog(const char *fmt, va_list vargs)
325 static int print_prefix = 1;
330 fprintf(logfile, "%s ", buf);
332 print_prefix = strstr(fmt, "\n") != NULL;
333 vfprintf(logfile, fmt, vargs);
338 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
341 va_start(vargs, fmt);
342 http_vlog(fmt, vargs);
346 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
348 static int print_prefix = 1;
349 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
350 if (level > av_log_level)
352 if (print_prefix && avc)
353 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
354 print_prefix = strstr(fmt, "\n") != NULL;
355 http_vlog(fmt, vargs);
358 static void log_connection(HTTPContext *c)
363 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
364 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
365 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
368 static void update_datarate(DataRateData *drd, int64_t count)
370 if (!drd->time1 && !drd->count1) {
371 drd->time1 = drd->time2 = cur_time;
372 drd->count1 = drd->count2 = count;
373 } else if (cur_time - drd->time2 > 5000) {
374 drd->time1 = drd->time2;
375 drd->count1 = drd->count2;
376 drd->time2 = cur_time;
381 /* In bytes per second */
382 static int compute_datarate(DataRateData *drd, int64_t count)
384 if (cur_time == drd->time1)
387 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
391 static void start_children(FFStream *feed)
396 for (; feed; feed = feed->next) {
397 if (feed->child_argv && !feed->pid) {
398 feed->pid_start = time(0);
403 http_log("Unable to create children\n");
412 av_strlcpy(pathname, my_program_name, sizeof(pathname));
414 slash = strrchr(pathname, '/');
419 strcpy(slash, "ffmpeg");
421 http_log("Launch commandline: ");
422 http_log("%s ", pathname);
423 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
424 http_log("%s ", feed->child_argv[i]);
427 for (i = 3; i < 256; i++)
430 if (!ffserver_debug) {
431 i = open("/dev/null", O_RDWR);
440 /* This is needed to make relative pathnames work */
441 chdir(my_program_dir);
443 signal(SIGPIPE, SIG_DFL);
445 execvp(pathname, feed->child_argv);
453 /* open a listening socket */
454 static int socket_open_listen(struct sockaddr_in *my_addr)
458 server_fd = socket(AF_INET,SOCK_STREAM,0);
465 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
467 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
469 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
471 closesocket(server_fd);
475 if (listen (server_fd, 5) < 0) {
477 closesocket(server_fd);
480 ff_socket_nonblock(server_fd, 1);
485 /* start all multicast streams */
486 static void start_multicast(void)
491 struct sockaddr_in dest_addr;
492 int default_port, stream_index;
495 for(stream = first_stream; stream != NULL; stream = stream->next) {
496 if (stream->is_multicast) {
497 /* open the RTP connection */
498 snprintf(session_id, sizeof(session_id), "%08x%08x",
499 av_random(&random_state), av_random(&random_state));
501 /* choose a port if none given */
502 if (stream->multicast_port == 0) {
503 stream->multicast_port = default_port;
507 dest_addr.sin_family = AF_INET;
508 dest_addr.sin_addr = stream->multicast_ip;
509 dest_addr.sin_port = htons(stream->multicast_port);
511 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
512 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
516 if (open_input_stream(rtp_c, "") < 0) {
517 http_log("Could not open input stream for stream '%s'\n",
522 /* open each RTP stream */
523 for(stream_index = 0; stream_index < stream->nb_streams;
525 dest_addr.sin_port = htons(stream->multicast_port +
527 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
528 http_log("Could not open output stream '%s/streamid=%d'\n",
529 stream->filename, stream_index);
534 /* change state to send data */
535 rtp_c->state = HTTPSTATE_SEND_DATA;
540 /* main loop of the http server */
541 static int http_server(void)
543 int server_fd = 0, rtsp_server_fd = 0;
544 int ret, delay, delay1;
545 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
546 HTTPContext *c, *c_next;
548 if (my_http_addr.sin_port) {
549 server_fd = socket_open_listen(&my_http_addr);
554 if (my_rtsp_addr.sin_port) {
555 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
556 if (rtsp_server_fd < 0)
560 if (!rtsp_server_fd && !server_fd) {
561 http_log("HTTP and RTSP disabled.\n");
565 http_log("ffserver started.\n");
567 start_children(first_feed);
572 poll_entry = poll_table;
574 poll_entry->fd = server_fd;
575 poll_entry->events = POLLIN;
578 if (rtsp_server_fd) {
579 poll_entry->fd = rtsp_server_fd;
580 poll_entry->events = POLLIN;
584 /* wait for events on each HTTP handle */
591 case HTTPSTATE_SEND_HEADER:
592 case RTSPSTATE_SEND_REPLY:
593 case RTSPSTATE_SEND_PACKET:
594 c->poll_entry = poll_entry;
596 poll_entry->events = POLLOUT;
599 case HTTPSTATE_SEND_DATA_HEADER:
600 case HTTPSTATE_SEND_DATA:
601 case HTTPSTATE_SEND_DATA_TRAILER:
602 if (!c->is_packetized) {
603 /* for TCP, we output as much as we can (may need to put a limit) */
604 c->poll_entry = poll_entry;
606 poll_entry->events = POLLOUT;
609 /* when ffserver is doing the timing, we work by
610 looking at which packet need to be sent every
612 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
617 case HTTPSTATE_WAIT_REQUEST:
618 case HTTPSTATE_RECEIVE_DATA:
619 case HTTPSTATE_WAIT_FEED:
620 case RTSPSTATE_WAIT_REQUEST:
621 /* need to catch errors */
622 c->poll_entry = poll_entry;
624 poll_entry->events = POLLIN;/* Maybe this will work */
628 c->poll_entry = NULL;
634 /* wait for an event on one connection. We poll at least every
635 second to handle timeouts */
637 ret = poll(poll_table, poll_entry - poll_table, delay);
638 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
639 ff_neterrno() != FF_NETERROR(EINTR))
643 cur_time = av_gettime() / 1000;
645 if (need_to_start_children) {
646 need_to_start_children = 0;
647 start_children(first_feed);
650 /* now handle the events */
651 for(c = first_http_ctx; c != NULL; c = c_next) {
653 if (handle_connection(c) < 0) {
654 /* close and free the connection */
660 poll_entry = poll_table;
662 /* new HTTP connection request ? */
663 if (poll_entry->revents & POLLIN)
664 new_connection(server_fd, 0);
667 if (rtsp_server_fd) {
668 /* new RTSP connection request ? */
669 if (poll_entry->revents & POLLIN)
670 new_connection(rtsp_server_fd, 1);
675 /* start waiting for a new HTTP/RTSP request */
676 static void start_wait_request(HTTPContext *c, int is_rtsp)
678 c->buffer_ptr = c->buffer;
679 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
682 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
683 c->state = RTSPSTATE_WAIT_REQUEST;
685 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
686 c->state = HTTPSTATE_WAIT_REQUEST;
690 static void new_connection(int server_fd, int is_rtsp)
692 struct sockaddr_in from_addr;
694 HTTPContext *c = NULL;
696 len = sizeof(from_addr);
697 fd = accept(server_fd, (struct sockaddr *)&from_addr,
700 http_log("error during accept %s\n", strerror(errno));
703 ff_socket_nonblock(fd, 1);
705 /* XXX: should output a warning page when coming
706 close to the connection limit */
707 if (nb_connections >= nb_max_connections)
710 /* add a new connection */
711 c = av_mallocz(sizeof(HTTPContext));
716 c->poll_entry = NULL;
717 c->from_addr = from_addr;
718 c->buffer_size = IOBUFFER_INIT_SIZE;
719 c->buffer = av_malloc(c->buffer_size);
723 c->next = first_http_ctx;
727 start_wait_request(c, is_rtsp);
739 static void close_connection(HTTPContext *c)
741 HTTPContext **cp, *c1;
743 AVFormatContext *ctx;
747 /* remove connection from list */
748 cp = &first_http_ctx;
749 while ((*cp) != NULL) {
757 /* remove references, if any (XXX: do it faster) */
758 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
763 /* remove connection associated resources */
767 /* close each frame parser */
768 for(i=0;i<c->fmt_in->nb_streams;i++) {
769 st = c->fmt_in->streams[i];
770 if (st->codec->codec)
771 avcodec_close(st->codec);
773 av_close_input_file(c->fmt_in);
776 /* free RTP output streams if any */
779 nb_streams = c->stream->nb_streams;
781 for(i=0;i<nb_streams;i++) {
784 av_write_trailer(ctx);
787 h = c->rtp_handles[i];
794 if (!c->last_packet_sent) {
797 if (url_open_dyn_buf(&ctx->pb) >= 0) {
798 av_write_trailer(ctx);
799 av_freep(&c->pb_buffer);
800 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
805 for(i=0; i<ctx->nb_streams; i++)
806 av_free(ctx->streams[i]);
808 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
809 current_bandwidth -= c->stream->bandwidth;
811 /* signal that there is no feed if we are the feeder socket */
812 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
813 c->stream->feed_opened = 0;
817 av_freep(&c->pb_buffer);
818 av_freep(&c->packet_buffer);
824 static int handle_connection(HTTPContext *c)
829 case HTTPSTATE_WAIT_REQUEST:
830 case RTSPSTATE_WAIT_REQUEST:
832 if ((c->timeout - cur_time) < 0)
834 if (c->poll_entry->revents & (POLLERR | POLLHUP))
837 /* no need to read if no events */
838 if (!(c->poll_entry->revents & POLLIN))
842 len = recv(c->fd, c->buffer_ptr, 1, 0);
844 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
845 ff_neterrno() != FF_NETERROR(EINTR))
847 } else if (len == 0) {
850 /* search for end of request. */
852 c->buffer_ptr += len;
854 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
855 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
856 /* request found : parse it and reply */
857 if (c->state == HTTPSTATE_WAIT_REQUEST) {
858 ret = http_parse_request(c);
860 ret = rtsp_parse_request(c);
864 } else if (ptr >= c->buffer_end) {
865 /* request too long: cannot do anything */
867 } else goto read_loop;
871 case HTTPSTATE_SEND_HEADER:
872 if (c->poll_entry->revents & (POLLERR | POLLHUP))
875 /* no need to write if no events */
876 if (!(c->poll_entry->revents & POLLOUT))
878 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
880 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
881 ff_neterrno() != FF_NETERROR(EINTR)) {
882 /* error : close connection */
883 av_freep(&c->pb_buffer);
887 c->buffer_ptr += len;
889 c->stream->bytes_served += len;
890 c->data_count += len;
891 if (c->buffer_ptr >= c->buffer_end) {
892 av_freep(&c->pb_buffer);
896 /* all the buffer was sent : synchronize to the incoming stream */
897 c->state = HTTPSTATE_SEND_DATA_HEADER;
898 c->buffer_ptr = c->buffer_end = c->buffer;
903 case HTTPSTATE_SEND_DATA:
904 case HTTPSTATE_SEND_DATA_HEADER:
905 case HTTPSTATE_SEND_DATA_TRAILER:
906 /* for packetized output, we consider we can always write (the
907 input streams sets the speed). It may be better to verify
908 that we do not rely too much on the kernel queues */
909 if (!c->is_packetized) {
910 if (c->poll_entry->revents & (POLLERR | POLLHUP))
913 /* no need to read if no events */
914 if (!(c->poll_entry->revents & POLLOUT))
917 if (http_send_data(c) < 0)
919 /* close connection if trailer sent */
920 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
923 case HTTPSTATE_RECEIVE_DATA:
924 /* no need to read if no events */
925 if (c->poll_entry->revents & (POLLERR | POLLHUP))
927 if (!(c->poll_entry->revents & POLLIN))
929 if (http_receive_data(c) < 0)
932 case HTTPSTATE_WAIT_FEED:
933 /* no need to read if no events */
934 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
937 /* nothing to do, we'll be waken up by incoming feed packets */
940 case RTSPSTATE_SEND_REPLY:
941 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
942 av_freep(&c->pb_buffer);
945 /* no need to write if no events */
946 if (!(c->poll_entry->revents & POLLOUT))
948 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
950 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
951 ff_neterrno() != FF_NETERROR(EINTR)) {
952 /* error : close connection */
953 av_freep(&c->pb_buffer);
957 c->buffer_ptr += len;
958 c->data_count += len;
959 if (c->buffer_ptr >= c->buffer_end) {
960 /* all the buffer was sent : wait for a new request */
961 av_freep(&c->pb_buffer);
962 start_wait_request(c, 1);
966 case RTSPSTATE_SEND_PACKET:
967 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
968 av_freep(&c->packet_buffer);
971 /* no need to write if no events */
972 if (!(c->poll_entry->revents & POLLOUT))
974 len = send(c->fd, c->packet_buffer_ptr,
975 c->packet_buffer_end - c->packet_buffer_ptr, 0);
977 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
978 ff_neterrno() != FF_NETERROR(EINTR)) {
979 /* error : close connection */
980 av_freep(&c->packet_buffer);
984 c->packet_buffer_ptr += len;
985 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
986 /* all the buffer was sent : wait for a new request */
987 av_freep(&c->packet_buffer);
988 c->state = RTSPSTATE_WAIT_REQUEST;
992 case HTTPSTATE_READY:
1001 static int extract_rates(char *rates, int ratelen, const char *request)
1005 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1006 if (strncasecmp(p, "Pragma:", 7) == 0) {
1007 const char *q = p + 7;
1009 while (*q && *q != '\n' && isspace(*q))
1012 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1018 memset(rates, 0xff, ratelen);
1021 while (*q && *q != '\n' && *q != ':')
1024 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1028 if (stream_no < ratelen && stream_no >= 0)
1029 rates[stream_no] = rate_no;
1031 while (*q && *q != '\n' && !isspace(*q))
1038 p = strchr(p, '\n');
1048 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1051 int best_bitrate = 100000000;
1054 for (i = 0; i < feed->nb_streams; i++) {
1055 AVCodecContext *feed_codec = feed->streams[i]->codec;
1057 if (feed_codec->codec_id != codec->codec_id ||
1058 feed_codec->sample_rate != codec->sample_rate ||
1059 feed_codec->width != codec->width ||
1060 feed_codec->height != codec->height)
1063 /* Potential stream */
1065 /* We want the fastest stream less than bit_rate, or the slowest
1066 * faster than bit_rate
1069 if (feed_codec->bit_rate <= bit_rate) {
1070 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1071 best_bitrate = feed_codec->bit_rate;
1075 if (feed_codec->bit_rate < best_bitrate) {
1076 best_bitrate = feed_codec->bit_rate;
1085 static int modify_current_stream(HTTPContext *c, char *rates)
1088 FFStream *req = c->stream;
1089 int action_required = 0;
1091 /* Not much we can do for a feed */
1095 for (i = 0; i < req->nb_streams; i++) {
1096 AVCodecContext *codec = req->streams[i]->codec;
1100 c->switch_feed_streams[i] = req->feed_streams[i];
1103 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1106 /* Wants off or slow */
1107 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1109 /* This doesn't work well when it turns off the only stream! */
1110 c->switch_feed_streams[i] = -2;
1111 c->feed_streams[i] = -2;
1116 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1117 action_required = 1;
1120 return action_required;
1124 static void do_switch_stream(HTTPContext *c, int i)
1126 if (c->switch_feed_streams[i] >= 0) {
1128 c->feed_streams[i] = c->switch_feed_streams[i];
1131 /* Now update the stream */
1133 c->switch_feed_streams[i] = -1;
1136 /* XXX: factorize in utils.c ? */
1137 /* XXX: take care with different space meaning */
1138 static void skip_spaces(const char **pp)
1142 while (*p == ' ' || *p == '\t')
1147 static void get_word(char *buf, int buf_size, const char **pp)
1155 while (!isspace(*p) && *p != '\0') {
1156 if ((q - buf) < buf_size - 1)
1165 static int validate_acl(FFStream *stream, HTTPContext *c)
1167 enum IPAddressAction last_action = IP_DENY;
1169 struct in_addr *src = &c->from_addr.sin_addr;
1170 unsigned long src_addr = src->s_addr;
1172 for (acl = stream->acl; acl; acl = acl->next) {
1173 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1174 return (acl->action == IP_ALLOW) ? 1 : 0;
1175 last_action = acl->action;
1178 /* Nothing matched, so return not the last action */
1179 return (last_action == IP_DENY) ? 1 : 0;
1182 /* compute the real filename of a file by matching it without its
1183 extensions to all the stream filenames */
1184 static void compute_real_filename(char *filename, int max_size)
1191 /* compute filename by matching without the file extensions */
1192 av_strlcpy(file1, filename, sizeof(file1));
1193 p = strrchr(file1, '.');
1196 for(stream = first_stream; stream != NULL; stream = stream->next) {
1197 av_strlcpy(file2, stream->filename, sizeof(file2));
1198 p = strrchr(file2, '.');
1201 if (!strcmp(file1, file2)) {
1202 av_strlcpy(filename, stream->filename, max_size);
1217 /* parse http request and prepare header */
1218 static int http_parse_request(HTTPContext *c)
1221 enum RedirType redir_type;
1223 char info[1024], filename[1024];
1227 const char *mime_type;
1231 char *useragent = 0;
1234 get_word(cmd, sizeof(cmd), (const char **)&p);
1235 av_strlcpy(c->method, cmd, sizeof(c->method));
1237 if (!strcmp(cmd, "GET"))
1239 else if (!strcmp(cmd, "POST"))
1244 get_word(url, sizeof(url), (const char **)&p);
1245 av_strlcpy(c->url, url, sizeof(c->url));
1247 get_word(protocol, sizeof(protocol), (const char **)&p);
1248 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1251 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1254 http_log("New connection: %s %s\n", cmd, url);
1256 /* find the filename and the optional info string in the request */
1257 p = strchr(url, '?');
1259 av_strlcpy(info, p, sizeof(info));
1264 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1266 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1267 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1269 if (*useragent && *useragent != '\n' && isspace(*useragent))
1273 p = strchr(p, '\n');
1280 redir_type = REDIR_NONE;
1281 if (match_ext(filename, "asx")) {
1282 redir_type = REDIR_ASX;
1283 filename[strlen(filename)-1] = 'f';
1284 } else if (match_ext(filename, "asf") &&
1285 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1286 /* if this isn't WMP or lookalike, return the redirector file */
1287 redir_type = REDIR_ASF;
1288 } else if (match_ext(filename, "rpm,ram")) {
1289 redir_type = REDIR_RAM;
1290 strcpy(filename + strlen(filename)-2, "m");
1291 } else if (match_ext(filename, "rtsp")) {
1292 redir_type = REDIR_RTSP;
1293 compute_real_filename(filename, sizeof(filename) - 1);
1294 } else if (match_ext(filename, "sdp")) {
1295 redir_type = REDIR_SDP;
1296 compute_real_filename(filename, sizeof(filename) - 1);
1299 // "redirect" / request to index.html
1300 if (!strlen(filename))
1301 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1303 stream = first_stream;
1304 while (stream != NULL) {
1305 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1307 stream = stream->next;
1309 if (stream == NULL) {
1310 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1315 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1316 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1318 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1319 c->http_error = 301;
1321 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1323 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1324 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1325 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1326 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1327 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1329 /* prepare output buffer */
1330 c->buffer_ptr = c->buffer;
1332 c->state = HTTPSTATE_SEND_HEADER;
1336 /* If this is WMP, get the rate information */
1337 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1338 if (modify_current_stream(c, ratebuf)) {
1339 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1340 if (c->switch_feed_streams[i] >= 0)
1341 do_switch_stream(c, i);
1346 /* If already streaming this feed, do not let start another feeder. */
1347 if (stream->feed_opened) {
1348 snprintf(msg, sizeof(msg), "This feed is already being received.");
1349 http_log("feed %s already being received\n", stream->feed_filename);
1353 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1354 current_bandwidth += stream->bandwidth;
1356 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1357 c->http_error = 200;
1359 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1362 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1363 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");
1364 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",
1365 current_bandwidth, max_bandwidth);
1366 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1368 /* prepare output buffer */
1369 c->buffer_ptr = c->buffer;
1371 c->state = HTTPSTATE_SEND_HEADER;
1375 if (redir_type != REDIR_NONE) {
1378 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1379 if (strncasecmp(p, "Host:", 5) == 0) {
1383 p = strchr(p, '\n');
1394 while (isspace(*hostinfo))
1397 eoh = strchr(hostinfo, '\n');
1399 if (eoh[-1] == '\r')
1402 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1403 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1404 hostbuf[eoh - hostinfo] = 0;
1406 c->http_error = 200;
1408 switch(redir_type) {
1410 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1411 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1412 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1413 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1414 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1415 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1416 hostbuf, filename, info);
1417 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1420 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1421 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1422 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1425 hostbuf, filename, info);
1428 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1429 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1430 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1431 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1432 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1433 hostbuf, filename, info);
1437 char hostname[256], *p;
1438 /* extract only hostname */
1439 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1440 p = strrchr(hostname, ':');
1443 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1444 /* XXX: incorrect mime type ? */
1445 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1446 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1447 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1448 hostname, ntohs(my_rtsp_addr.sin_port),
1455 int sdp_data_size, len;
1456 struct sockaddr_in my_addr;
1458 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1459 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1460 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1462 len = sizeof(my_addr);
1463 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1465 /* XXX: should use a dynamic buffer */
1466 sdp_data_size = prepare_sdp_description(stream,
1469 if (sdp_data_size > 0) {
1470 memcpy(q, sdp_data, sdp_data_size);
1482 /* prepare output buffer */
1483 c->buffer_ptr = c->buffer;
1485 c->state = HTTPSTATE_SEND_HEADER;
1491 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1495 stream->conns_served++;
1497 /* XXX: add there authenticate and IP match */
1500 /* if post, it means a feed is being sent */
1501 if (!stream->is_feed) {
1502 /* However it might be a status report from WMP! Let us log the
1503 * data as it might come in handy one day. */
1507 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1508 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1512 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1513 client_id = strtol(p + 18, 0, 10);
1514 p = strchr(p, '\n');
1522 char *eol = strchr(logline, '\n');
1527 if (eol[-1] == '\r')
1529 http_log("%.*s\n", (int) (eol - logline), logline);
1530 c->suppress_log = 1;
1535 http_log("\nGot request:\n%s\n", c->buffer);
1538 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1541 /* Now we have to find the client_id */
1542 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1543 if (wmpc->wmp_client_id == client_id)
1547 if (wmpc && modify_current_stream(wmpc, ratebuf))
1548 wmpc->switch_pending = 1;
1551 snprintf(msg, sizeof(msg), "POST command not handled");
1555 if (http_start_receive_data(c) < 0) {
1556 snprintf(msg, sizeof(msg), "could not open feed");
1560 c->state = HTTPSTATE_RECEIVE_DATA;
1565 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1566 http_log("\nGot request:\n%s\n", c->buffer);
1569 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1572 /* open input stream */
1573 if (open_input_stream(c, info) < 0) {
1574 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1578 /* prepare http header */
1580 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1581 mime_type = c->stream->fmt->mime_type;
1583 mime_type = "application/x-octet-stream";
1584 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1586 /* for asf, we need extra headers */
1587 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1588 /* Need to allocate a client id */
1590 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1592 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);
1594 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1595 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1597 /* prepare output buffer */
1599 c->buffer_ptr = c->buffer;
1601 c->state = HTTPSTATE_SEND_HEADER;
1604 c->http_error = 404;
1606 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1607 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1608 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1609 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1610 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1611 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1612 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1614 /* prepare output buffer */
1615 c->buffer_ptr = c->buffer;
1617 c->state = HTTPSTATE_SEND_HEADER;
1621 c->http_error = 200; /* horrible : we use this value to avoid
1622 going to the send data state */
1623 c->state = HTTPSTATE_SEND_HEADER;
1627 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1629 static const char *suffix = " kMGTP";
1632 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1634 url_fprintf(pb, "%"PRId64"%c", count, *s);
1637 static void compute_status(HTTPContext *c)
1646 if (url_open_dyn_buf(&pb) < 0) {
1647 /* XXX: return an error ? */
1648 c->buffer_ptr = c->buffer;
1649 c->buffer_end = c->buffer;
1653 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1654 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1655 url_fprintf(pb, "Pragma: no-cache\r\n");
1656 url_fprintf(pb, "\r\n");
1658 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1659 if (c->stream->feed_filename[0])
1660 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1661 url_fprintf(pb, "</HEAD>\n<BODY>");
1662 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1664 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1665 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1666 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");
1667 stream = first_stream;
1668 while (stream != NULL) {
1669 char sfilename[1024];
1672 if (stream->feed != stream) {
1673 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1674 eosf = sfilename + strlen(sfilename);
1675 if (eosf - sfilename >= 4) {
1676 if (strcmp(eosf - 4, ".asf") == 0)
1677 strcpy(eosf - 4, ".asx");
1678 else if (strcmp(eosf - 3, ".rm") == 0)
1679 strcpy(eosf - 3, ".ram");
1680 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1681 /* generate a sample RTSP director if
1682 unicast. Generate an SDP redirector if
1684 eosf = strrchr(sfilename, '.');
1686 eosf = sfilename + strlen(sfilename);
1687 if (stream->is_multicast)
1688 strcpy(eosf, ".sdp");
1690 strcpy(eosf, ".rtsp");
1694 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1695 sfilename, stream->filename);
1696 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1697 stream->conns_served);
1698 fmt_bytecount(pb, stream->bytes_served);
1699 switch(stream->stream_type) {
1700 case STREAM_TYPE_LIVE: {
1701 int audio_bit_rate = 0;
1702 int video_bit_rate = 0;
1703 const char *audio_codec_name = "";
1704 const char *video_codec_name = "";
1705 const char *audio_codec_name_extra = "";
1706 const char *video_codec_name_extra = "";
1708 for(i=0;i<stream->nb_streams;i++) {
1709 AVStream *st = stream->streams[i];
1710 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1711 switch(st->codec->codec_type) {
1712 case CODEC_TYPE_AUDIO:
1713 audio_bit_rate += st->codec->bit_rate;
1715 if (*audio_codec_name)
1716 audio_codec_name_extra = "...";
1717 audio_codec_name = codec->name;
1720 case CODEC_TYPE_VIDEO:
1721 video_bit_rate += st->codec->bit_rate;
1723 if (*video_codec_name)
1724 video_codec_name_extra = "...";
1725 video_codec_name = codec->name;
1728 case CODEC_TYPE_DATA:
1729 video_bit_rate += st->codec->bit_rate;
1735 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",
1738 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1739 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1741 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1743 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1744 url_fprintf(pb, "\n");
1748 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1752 stream = stream->next;
1754 url_fprintf(pb, "</TABLE>\n");
1756 stream = first_stream;
1757 while (stream != NULL) {
1758 if (stream->feed == stream) {
1759 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1761 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1763 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1768 /* This is somewhat linux specific I guess */
1769 snprintf(ps_cmd, sizeof(ps_cmd),
1770 "ps -o \"%%cpu,cputime\" --no-headers %d",
1773 pid_stat = popen(ps_cmd, "r");
1778 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1780 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1788 url_fprintf(pb, "<p>");
1790 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");
1792 for (i = 0; i < stream->nb_streams; i++) {
1793 AVStream *st = stream->streams[i];
1794 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1795 const char *type = "unknown";
1796 char parameters[64];
1800 switch(st->codec->codec_type) {
1801 case CODEC_TYPE_AUDIO:
1803 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1805 case CODEC_TYPE_VIDEO:
1807 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1808 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1813 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1814 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1816 url_fprintf(pb, "</table>\n");
1819 stream = stream->next;
1825 AVCodecContext *enc;
1829 stream = first_feed;
1830 while (stream != NULL) {
1831 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1832 url_fprintf(pb, "<TABLE>\n");
1833 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1834 for(i=0;i<stream->nb_streams;i++) {
1835 AVStream *st = stream->streams[i];
1836 FeedData *fdata = st->priv_data;
1839 avcodec_string(buf, sizeof(buf), enc);
1840 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1841 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1842 avg /= enc->frame_size;
1843 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1844 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1846 url_fprintf(pb, "</TABLE>\n");
1847 stream = stream->next_feed;
1852 /* connection status */
1853 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1855 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1856 nb_connections, nb_max_connections);
1858 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1859 current_bandwidth, max_bandwidth);
1861 url_fprintf(pb, "<TABLE>\n");
1862 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");
1863 c1 = first_http_ctx;
1865 while (c1 != NULL) {
1871 for (j = 0; j < c1->stream->nb_streams; j++) {
1872 if (!c1->stream->feed)
1873 bitrate += c1->stream->streams[j]->codec->bit_rate;
1874 else if (c1->feed_streams[j] >= 0)
1875 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1880 p = inet_ntoa(c1->from_addr.sin_addr);
1881 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1883 c1->stream ? c1->stream->filename : "",
1884 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1887 http_state[c1->state]);
1888 fmt_bytecount(pb, bitrate);
1889 url_fprintf(pb, "<td align=right>");
1890 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1891 url_fprintf(pb, "<td align=right>");
1892 fmt_bytecount(pb, c1->data_count);
1893 url_fprintf(pb, "\n");
1896 url_fprintf(pb, "</TABLE>\n");
1901 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1902 url_fprintf(pb, "</BODY>\n</HTML>\n");
1904 len = url_close_dyn_buf(pb, &c->pb_buffer);
1905 c->buffer_ptr = c->pb_buffer;
1906 c->buffer_end = c->pb_buffer + len;
1909 /* check if the parser needs to be opened for stream i */
1910 static void open_parser(AVFormatContext *s, int i)
1912 AVStream *st = s->streams[i];
1915 if (!st->codec->codec) {
1916 codec = avcodec_find_decoder(st->codec->codec_id);
1917 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1918 st->codec->parse_only = 1;
1919 if (avcodec_open(st->codec, codec) < 0)
1920 st->codec->parse_only = 0;
1925 static int open_input_stream(HTTPContext *c, const char *info)
1928 char input_filename[1024];
1930 int buf_size, i, ret;
1933 /* find file name */
1934 if (c->stream->feed) {
1935 strcpy(input_filename, c->stream->feed->feed_filename);
1936 buf_size = FFM_PACKET_SIZE;
1937 /* compute position (absolute time) */
1938 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1939 stream_pos = parse_date(buf, 0);
1940 if (stream_pos == INT64_MIN)
1942 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1943 int prebuffer = strtol(buf, 0, 10);
1944 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1946 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1948 strcpy(input_filename, c->stream->feed_filename);
1950 /* compute position (relative time) */
1951 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1952 stream_pos = parse_date(buf, 1);
1953 if (stream_pos == INT64_MIN)
1958 if (input_filename[0] == '\0')
1962 { time_t when = stream_pos / 1000000;
1963 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1968 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1969 buf_size, c->stream->ap_in)) < 0) {
1970 http_log("could not open %s: %d\n", input_filename, ret);
1973 s->flags |= AVFMT_FLAG_GENPTS;
1975 av_find_stream_info(c->fmt_in);
1977 /* open each parser */
1978 for(i=0;i<s->nb_streams;i++)
1981 /* choose stream as clock source (we favorize video stream if
1982 present) for packet sending */
1983 c->pts_stream_index = 0;
1984 for(i=0;i<c->stream->nb_streams;i++) {
1985 if (c->pts_stream_index == 0 &&
1986 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1987 c->pts_stream_index = i;
1992 if (c->fmt_in->iformat->read_seek)
1993 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1995 /* set the start time (needed for maxtime and RTP packet timing) */
1996 c->start_time = cur_time;
1997 c->first_pts = AV_NOPTS_VALUE;
2001 /* return the server clock (in us) */
2002 static int64_t get_server_clock(HTTPContext *c)
2004 /* compute current pts value from system time */
2005 return (cur_time - c->start_time) * 1000;
2008 /* return the estimated time at which the current packet must be sent
2010 static int64_t get_packet_send_clock(HTTPContext *c)
2012 int bytes_left, bytes_sent, frame_bytes;
2014 frame_bytes = c->cur_frame_bytes;
2015 if (frame_bytes <= 0)
2018 bytes_left = c->buffer_end - c->buffer_ptr;
2019 bytes_sent = frame_bytes - bytes_left;
2020 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2025 static int http_prepare_data(HTTPContext *c)
2028 AVFormatContext *ctx;
2030 av_freep(&c->pb_buffer);
2032 case HTTPSTATE_SEND_DATA_HEADER:
2033 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2034 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2035 sizeof(c->fmt_ctx.author));
2036 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2037 sizeof(c->fmt_ctx.comment));
2038 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2039 sizeof(c->fmt_ctx.copyright));
2040 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2041 sizeof(c->fmt_ctx.title));
2043 for(i=0;i<c->stream->nb_streams;i++) {
2046 st = av_mallocz(sizeof(AVStream));
2047 c->fmt_ctx.streams[i] = st;
2048 /* if file or feed, then just take streams from FFStream struct */
2049 if (!c->stream->feed ||
2050 c->stream->feed == c->stream)
2051 src = c->stream->streams[i];
2053 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2057 st->codec->frame_number = 0; /* XXX: should be done in
2058 AVStream, not in codec */
2060 /* set output format parameters */
2061 c->fmt_ctx.oformat = c->stream->fmt;
2062 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2064 c->got_key_frame = 0;
2066 /* prepare header and save header data in a stream */
2067 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2068 /* XXX: potential leak */
2071 c->fmt_ctx.pb->is_streamed = 1;
2074 * HACK to avoid mpeg ps muxer to spit many underflow errors
2075 * Default value from FFmpeg
2076 * Try to set it use configuration option
2078 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2079 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2081 av_set_parameters(&c->fmt_ctx, NULL);
2082 if (av_write_header(&c->fmt_ctx) < 0) {
2083 http_log("Error writing output header\n");
2087 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2088 c->buffer_ptr = c->pb_buffer;
2089 c->buffer_end = c->pb_buffer + len;
2091 c->state = HTTPSTATE_SEND_DATA;
2092 c->last_packet_sent = 0;
2094 case HTTPSTATE_SEND_DATA:
2095 /* find a new packet */
2096 /* read a packet from the input stream */
2097 if (c->stream->feed)
2098 ffm_set_write_index(c->fmt_in,
2099 c->stream->feed->feed_write_index,
2100 c->stream->feed->feed_size);
2102 if (c->stream->max_time &&
2103 c->stream->max_time + c->start_time - cur_time < 0)
2104 /* We have timed out */
2105 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2109 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2110 if (c->stream->feed && c->stream->feed->feed_opened) {
2111 /* if coming from feed, it means we reached the end of the
2112 ffm file, so must wait for more data */
2113 c->state = HTTPSTATE_WAIT_FEED;
2114 return 1; /* state changed */
2116 if (c->stream->loop) {
2117 av_close_input_file(c->fmt_in);
2119 if (open_input_stream(c, "") < 0)
2124 /* must send trailer now because eof or error */
2125 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2129 int source_index = pkt.stream_index;
2130 /* update first pts if needed */
2131 if (c->first_pts == AV_NOPTS_VALUE) {
2132 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2133 c->start_time = cur_time;
2135 /* send it to the appropriate stream */
2136 if (c->stream->feed) {
2137 /* if coming from a feed, select the right stream */
2138 if (c->switch_pending) {
2139 c->switch_pending = 0;
2140 for(i=0;i<c->stream->nb_streams;i++) {
2141 if (c->switch_feed_streams[i] == pkt.stream_index)
2142 if (pkt.flags & PKT_FLAG_KEY)
2143 do_switch_stream(c, i);
2144 if (c->switch_feed_streams[i] >= 0)
2145 c->switch_pending = 1;
2148 for(i=0;i<c->stream->nb_streams;i++) {
2149 if (c->feed_streams[i] == pkt.stream_index) {
2150 AVStream *st = c->fmt_in->streams[source_index];
2151 pkt.stream_index = i;
2152 if (pkt.flags & PKT_FLAG_KEY &&
2153 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2154 c->stream->nb_streams == 1))
2155 c->got_key_frame = 1;
2156 if (!c->stream->send_on_key || c->got_key_frame)
2161 AVCodecContext *codec;
2162 AVStream *ist, *ost;
2164 ist = c->fmt_in->streams[source_index];
2165 /* specific handling for RTP: we use several
2166 output stream (one for each RTP
2167 connection). XXX: need more abstract handling */
2168 if (c->is_packetized) {
2169 /* compute send time and duration */
2170 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2171 if (ist->start_time != AV_NOPTS_VALUE)
2172 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2173 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2175 printf("index=%d pts=%0.3f duration=%0.6f\n",
2177 (double)c->cur_pts /
2179 (double)c->cur_frame_duration /
2182 /* find RTP context */
2183 c->packet_stream_index = pkt.stream_index;
2184 ctx = c->rtp_ctx[c->packet_stream_index];
2186 av_free_packet(&pkt);
2189 codec = ctx->streams[0]->codec;
2190 /* only one stream per RTP connection */
2191 pkt.stream_index = 0;
2195 codec = ctx->streams[pkt.stream_index]->codec;
2198 if (c->is_packetized) {
2199 int max_packet_size;
2200 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2201 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2203 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2204 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2206 ret = url_open_dyn_buf(&ctx->pb);
2209 /* XXX: potential leak */
2212 ost = ctx->streams[pkt.stream_index];
2214 ctx->pb->is_streamed = 1;
2215 if (pkt.dts != AV_NOPTS_VALUE)
2216 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2217 if (pkt.pts != AV_NOPTS_VALUE)
2218 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2219 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2220 if (av_write_frame(ctx, &pkt) < 0) {
2221 http_log("Error writing frame to output\n");
2222 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2225 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2226 c->cur_frame_bytes = len;
2227 c->buffer_ptr = c->pb_buffer;
2228 c->buffer_end = c->pb_buffer + len;
2230 codec->frame_number++;
2232 av_free_packet(&pkt);
2236 av_free_packet(&pkt);
2241 case HTTPSTATE_SEND_DATA_TRAILER:
2242 /* last packet test ? */
2243 if (c->last_packet_sent || c->is_packetized)
2246 /* prepare header */
2247 if (url_open_dyn_buf(&ctx->pb) < 0) {
2248 /* XXX: potential leak */
2251 c->fmt_ctx.pb->is_streamed = 1;
2252 av_write_trailer(ctx);
2253 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2254 c->buffer_ptr = c->pb_buffer;
2255 c->buffer_end = c->pb_buffer + len;
2257 c->last_packet_sent = 1;
2263 /* should convert the format at the same time */
2264 /* send data starting at c->buffer_ptr to the output connection
2265 (either UDP or TCP connection) */
2266 static int http_send_data(HTTPContext *c)
2271 if (c->buffer_ptr >= c->buffer_end) {
2272 ret = http_prepare_data(c);
2276 /* state change requested */
2279 if (c->is_packetized) {
2280 /* RTP data output */
2281 len = c->buffer_end - c->buffer_ptr;
2283 /* fail safe - should never happen */
2285 c->buffer_ptr = c->buffer_end;
2288 len = (c->buffer_ptr[0] << 24) |
2289 (c->buffer_ptr[1] << 16) |
2290 (c->buffer_ptr[2] << 8) |
2292 if (len > (c->buffer_end - c->buffer_ptr))
2294 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2295 /* nothing to send yet: we can wait */
2299 c->data_count += len;
2300 update_datarate(&c->datarate, c->data_count);
2302 c->stream->bytes_served += len;
2304 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2305 /* RTP packets are sent inside the RTSP TCP connection */
2307 int interleaved_index, size;
2309 HTTPContext *rtsp_c;
2312 /* if no RTSP connection left, error */
2315 /* if already sending something, then wait. */
2316 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2318 if (url_open_dyn_buf(&pb) < 0)
2320 interleaved_index = c->packet_stream_index * 2;
2321 /* RTCP packets are sent at odd indexes */
2322 if (c->buffer_ptr[1] == 200)
2323 interleaved_index++;
2324 /* write RTSP TCP header */
2326 header[1] = interleaved_index;
2327 header[2] = len >> 8;
2329 put_buffer(pb, header, 4);
2330 /* write RTP packet data */
2332 put_buffer(pb, c->buffer_ptr, len);
2333 size = url_close_dyn_buf(pb, &c->packet_buffer);
2334 /* prepare asynchronous TCP sending */
2335 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2336 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2337 c->buffer_ptr += len;
2339 /* send everything we can NOW */
2340 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2341 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2343 rtsp_c->packet_buffer_ptr += len;
2344 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2345 /* if we could not send all the data, we will
2346 send it later, so a new state is needed to
2347 "lock" the RTSP TCP connection */
2348 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2351 /* all data has been sent */
2352 av_freep(&c->packet_buffer);
2354 /* send RTP packet directly in UDP */
2356 url_write(c->rtp_handles[c->packet_stream_index],
2357 c->buffer_ptr, len);
2358 c->buffer_ptr += len;
2359 /* here we continue as we can send several packets per 10 ms slot */
2362 /* TCP data output */
2363 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2365 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2366 ff_neterrno() != FF_NETERROR(EINTR))
2367 /* error : close connection */
2372 c->buffer_ptr += len;
2374 c->data_count += len;
2375 update_datarate(&c->datarate, c->data_count);
2377 c->stream->bytes_served += len;
2385 static int http_start_receive_data(HTTPContext *c)
2389 if (c->stream->feed_opened)
2392 /* Don't permit writing to this one */
2393 if (c->stream->readonly)
2397 fd = open(c->stream->feed_filename, O_RDWR);
2399 http_log("Error opening feeder file: %s\n", strerror(errno));
2404 c->stream->feed_write_index = ffm_read_write_index(fd);
2405 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2406 lseek(fd, 0, SEEK_SET);
2408 /* init buffer input */
2409 c->buffer_ptr = c->buffer;
2410 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2411 c->stream->feed_opened = 1;
2415 static int http_receive_data(HTTPContext *c)
2419 if (c->buffer_end > c->buffer_ptr) {
2422 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2424 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2425 ff_neterrno() != FF_NETERROR(EINTR))
2426 /* error : close connection */
2428 } else if (len == 0)
2429 /* end of connection : close it */
2432 c->buffer_ptr += len;
2433 c->data_count += len;
2434 update_datarate(&c->datarate, c->data_count);
2438 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2439 if (c->buffer[0] != 'f' ||
2440 c->buffer[1] != 'm') {
2441 http_log("Feed stream has become desynchronized -- disconnecting\n");
2446 if (c->buffer_ptr >= c->buffer_end) {
2447 FFStream *feed = c->stream;
2448 /* a packet has been received : write it in the store, except
2450 if (c->data_count > FFM_PACKET_SIZE) {
2452 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2453 /* XXX: use llseek or url_seek */
2454 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2455 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2456 http_log("Error writing to feed file: %s\n", strerror(errno));
2460 feed->feed_write_index += FFM_PACKET_SIZE;
2461 /* update file size */
2462 if (feed->feed_write_index > c->stream->feed_size)
2463 feed->feed_size = feed->feed_write_index;
2465 /* handle wrap around if max file size reached */
2466 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2467 feed->feed_write_index = FFM_PACKET_SIZE;
2470 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2472 /* wake up any waiting connections */
2473 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2474 if (c1->state == HTTPSTATE_WAIT_FEED &&
2475 c1->stream->feed == c->stream->feed)
2476 c1->state = HTTPSTATE_SEND_DATA;
2479 /* We have a header in our hands that contains useful data */
2480 AVFormatContext *s = NULL;
2482 AVInputFormat *fmt_in;
2485 /* use feed output format name to find corresponding input format */
2486 fmt_in = av_find_input_format(feed->fmt->name);
2490 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2491 pb->is_streamed = 1;
2493 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2498 /* Now we have the actual streams */
2499 if (s->nb_streams != feed->nb_streams) {
2500 av_close_input_stream(s);
2505 for (i = 0; i < s->nb_streams; i++) {
2506 AVStream *fst = feed->streams[i];
2507 AVStream *st = s->streams[i];
2508 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2509 if (fst->codec->extradata_size) {
2510 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2511 if (!fst->codec->extradata)
2513 memcpy(fst->codec->extradata, st->codec->extradata,
2514 fst->codec->extradata_size);
2518 av_close_input_stream(s);
2521 c->buffer_ptr = c->buffer;
2526 c->stream->feed_opened = 0;
2528 /* wake up any waiting connections to stop waiting for feed */
2529 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2530 if (c1->state == HTTPSTATE_WAIT_FEED &&
2531 c1->stream->feed == c->stream->feed)
2532 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2537 /********************************************************************/
2540 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2547 switch(error_number) {
2548 case RTSP_STATUS_OK:
2551 case RTSP_STATUS_METHOD:
2552 str = "Method Not Allowed";
2554 case RTSP_STATUS_BANDWIDTH:
2555 str = "Not Enough Bandwidth";
2557 case RTSP_STATUS_SESSION:
2558 str = "Session Not Found";
2560 case RTSP_STATUS_STATE:
2561 str = "Method Not Valid in This State";
2563 case RTSP_STATUS_AGGREGATE:
2564 str = "Aggregate operation not allowed";
2566 case RTSP_STATUS_ONLY_AGGREGATE:
2567 str = "Only aggregate operation allowed";
2569 case RTSP_STATUS_TRANSPORT:
2570 str = "Unsupported transport";
2572 case RTSP_STATUS_INTERNAL:
2573 str = "Internal Server Error";
2575 case RTSP_STATUS_SERVICE:
2576 str = "Service Unavailable";
2578 case RTSP_STATUS_VERSION:
2579 str = "RTSP Version not supported";
2582 str = "Unknown Error";
2586 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2587 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2589 /* output GMT time */
2593 p = buf2 + strlen(p) - 1;
2596 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2599 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2601 rtsp_reply_header(c, error_number);
2602 url_fprintf(c->pb, "\r\n");
2605 static int rtsp_parse_request(HTTPContext *c)
2607 const char *p, *p1, *p2;
2613 RTSPHeader header1, *header = &header1;
2615 c->buffer_ptr[0] = '\0';
2618 get_word(cmd, sizeof(cmd), &p);
2619 get_word(url, sizeof(url), &p);
2620 get_word(protocol, sizeof(protocol), &p);
2622 av_strlcpy(c->method, cmd, sizeof(c->method));
2623 av_strlcpy(c->url, url, sizeof(c->url));
2624 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2626 if (url_open_dyn_buf(&c->pb) < 0) {
2627 /* XXX: cannot do more */
2628 c->pb = NULL; /* safety */
2632 /* check version name */
2633 if (strcmp(protocol, "RTSP/1.0") != 0) {
2634 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2638 /* parse each header line */
2639 memset(header, 0, sizeof(RTSPHeader));
2640 /* skip to next line */
2641 while (*p != '\n' && *p != '\0')
2645 while (*p != '\0') {
2646 p1 = strchr(p, '\n');
2650 if (p2 > p && p2[-1] == '\r')
2652 /* skip empty line */
2656 if (len > sizeof(line) - 1)
2657 len = sizeof(line) - 1;
2658 memcpy(line, p, len);
2660 rtsp_parse_line(header, line);
2664 /* handle sequence number */
2665 c->seq = header->seq;
2667 if (!strcmp(cmd, "DESCRIBE"))
2668 rtsp_cmd_describe(c, url);
2669 else if (!strcmp(cmd, "OPTIONS"))
2670 rtsp_cmd_options(c, url);
2671 else if (!strcmp(cmd, "SETUP"))
2672 rtsp_cmd_setup(c, url, header);
2673 else if (!strcmp(cmd, "PLAY"))
2674 rtsp_cmd_play(c, url, header);
2675 else if (!strcmp(cmd, "PAUSE"))
2676 rtsp_cmd_pause(c, url, header);
2677 else if (!strcmp(cmd, "TEARDOWN"))
2678 rtsp_cmd_teardown(c, url, header);
2680 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2683 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2684 c->pb = NULL; /* safety */
2686 /* XXX: cannot do more */
2689 c->buffer_ptr = c->pb_buffer;
2690 c->buffer_end = c->pb_buffer + len;
2691 c->state = RTSPSTATE_SEND_REPLY;
2695 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2696 struct in_addr my_ip)
2698 AVFormatContext *avc;
2699 AVStream avs[MAX_STREAMS];
2702 avc = av_alloc_format_context();
2706 if (stream->title[0] != 0) {
2707 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2709 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2711 avc->nb_streams = stream->nb_streams;
2712 if (stream->is_multicast) {
2713 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2714 inet_ntoa(stream->multicast_ip),
2715 stream->multicast_port, stream->multicast_ttl);
2718 for(i = 0; i < stream->nb_streams; i++) {
2719 avc->streams[i] = &avs[i];
2720 avc->streams[i]->codec = stream->streams[i]->codec;
2722 *pbuffer = av_mallocz(2048);
2723 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2726 return strlen(*pbuffer);
2729 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2731 // rtsp_reply_header(c, RTSP_STATUS_OK);
2732 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2733 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2734 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2735 url_fprintf(c->pb, "\r\n");
2738 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2744 int content_length, len;
2745 struct sockaddr_in my_addr;
2747 /* find which url is asked */
2748 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2753 for(stream = first_stream; stream != NULL; stream = stream->next) {
2754 if (!stream->is_feed &&
2755 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2756 !strcmp(path, stream->filename)) {
2760 /* no stream found */
2761 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2765 /* prepare the media description in sdp format */
2767 /* get the host IP */
2768 len = sizeof(my_addr);
2769 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2770 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2771 if (content_length < 0) {
2772 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2775 rtsp_reply_header(c, RTSP_STATUS_OK);
2776 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2777 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2778 url_fprintf(c->pb, "\r\n");
2779 put_buffer(c->pb, content, content_length);
2782 static HTTPContext *find_rtp_session(const char *session_id)
2786 if (session_id[0] == '\0')
2789 for(c = first_http_ctx; c != NULL; c = c->next) {
2790 if (!strcmp(c->session_id, session_id))
2796 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2798 RTSPTransportField *th;
2801 for(i=0;i<h->nb_transports;i++) {
2802 th = &h->transports[i];
2803 if (th->protocol == protocol)
2809 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2813 int stream_index, port;
2818 RTSPTransportField *th;
2819 struct sockaddr_in dest_addr;
2820 RTSPActionServerSetup setup;
2822 /* find which url is asked */
2823 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2828 /* now check each stream */
2829 for(stream = first_stream; stream != NULL; stream = stream->next) {
2830 if (!stream->is_feed &&
2831 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2832 /* accept aggregate filenames only if single stream */
2833 if (!strcmp(path, stream->filename)) {
2834 if (stream->nb_streams != 1) {
2835 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2842 for(stream_index = 0; stream_index < stream->nb_streams;
2844 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2845 stream->filename, stream_index);
2846 if (!strcmp(path, buf))
2851 /* no stream found */
2852 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2856 /* generate session id if needed */
2857 if (h->session_id[0] == '\0')
2858 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2859 av_random(&random_state), av_random(&random_state));
2861 /* find rtp session, and create it if none found */
2862 rtp_c = find_rtp_session(h->session_id);
2864 /* always prefer UDP */
2865 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2867 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2869 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2874 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2877 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2881 /* open input stream */
2882 if (open_input_stream(rtp_c, "") < 0) {
2883 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2888 /* test if stream is OK (test needed because several SETUP needs
2889 to be done for a given file) */
2890 if (rtp_c->stream != stream) {
2891 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2895 /* test if stream is already set up */
2896 if (rtp_c->rtp_ctx[stream_index]) {
2897 rtsp_reply_error(c, RTSP_STATUS_STATE);
2901 /* check transport */
2902 th = find_transport(h, rtp_c->rtp_protocol);
2903 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2904 th->client_port_min <= 0)) {
2905 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2909 /* setup default options */
2910 setup.transport_option[0] = '\0';
2911 dest_addr = rtp_c->from_addr;
2912 dest_addr.sin_port = htons(th->client_port_min);
2915 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2916 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2920 /* now everything is OK, so we can send the connection parameters */
2921 rtsp_reply_header(c, RTSP_STATUS_OK);
2923 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2925 switch(rtp_c->rtp_protocol) {
2926 case RTSP_PROTOCOL_RTP_UDP:
2927 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2928 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2929 "client_port=%d-%d;server_port=%d-%d",
2930 th->client_port_min, th->client_port_min + 1,
2933 case RTSP_PROTOCOL_RTP_TCP:
2934 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2935 stream_index * 2, stream_index * 2 + 1);
2940 if (setup.transport_option[0] != '\0')
2941 url_fprintf(c->pb, ";%s", setup.transport_option);
2942 url_fprintf(c->pb, "\r\n");
2945 url_fprintf(c->pb, "\r\n");
2949 /* find an rtp connection by using the session ID. Check consistency
2951 static HTTPContext *find_rtp_session_with_url(const char *url,
2952 const char *session_id)
2960 rtp_c = find_rtp_session(session_id);
2964 /* find which url is asked */
2965 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2969 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2970 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2971 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2972 rtp_c->stream->filename, s);
2973 if(!strncmp(path, buf, sizeof(buf))) {
2974 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2981 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2985 rtp_c = find_rtp_session_with_url(url, h->session_id);
2987 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2991 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2992 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2993 rtp_c->state != HTTPSTATE_READY) {
2994 rtsp_reply_error(c, RTSP_STATUS_STATE);
2999 /* XXX: seek in stream */
3000 if (h->range_start != AV_NOPTS_VALUE) {
3001 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3002 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3006 rtp_c->state = HTTPSTATE_SEND_DATA;
3008 /* now everything is OK, so we can send the connection parameters */
3009 rtsp_reply_header(c, RTSP_STATUS_OK);
3011 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3012 url_fprintf(c->pb, "\r\n");
3015 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3019 rtp_c = find_rtp_session_with_url(url, h->session_id);
3021 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3025 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3026 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3027 rtsp_reply_error(c, RTSP_STATUS_STATE);
3031 rtp_c->state = HTTPSTATE_READY;
3032 rtp_c->first_pts = AV_NOPTS_VALUE;
3033 /* now everything is OK, so we can send the connection parameters */
3034 rtsp_reply_header(c, RTSP_STATUS_OK);
3036 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3037 url_fprintf(c->pb, "\r\n");
3040 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3043 char session_id[32];
3045 rtp_c = find_rtp_session_with_url(url, h->session_id);
3047 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3051 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3053 /* abort the session */
3054 close_connection(rtp_c);
3056 /* now everything is OK, so we can send the connection parameters */
3057 rtsp_reply_header(c, RTSP_STATUS_OK);
3059 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3060 url_fprintf(c->pb, "\r\n");
3064 /********************************************************************/
3067 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3068 FFStream *stream, const char *session_id,
3069 enum RTSPProtocol rtp_protocol)
3071 HTTPContext *c = NULL;
3072 const char *proto_str;
3074 /* XXX: should output a warning page when coming
3075 close to the connection limit */
3076 if (nb_connections >= nb_max_connections)
3079 /* add a new connection */
3080 c = av_mallocz(sizeof(HTTPContext));
3085 c->poll_entry = NULL;
3086 c->from_addr = *from_addr;
3087 c->buffer_size = IOBUFFER_INIT_SIZE;
3088 c->buffer = av_malloc(c->buffer_size);
3093 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3094 c->state = HTTPSTATE_READY;
3095 c->is_packetized = 1;
3096 c->rtp_protocol = rtp_protocol;
3098 /* protocol is shown in statistics */
3099 switch(c->rtp_protocol) {
3100 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3101 proto_str = "MCAST";
3103 case RTSP_PROTOCOL_RTP_UDP:
3106 case RTSP_PROTOCOL_RTP_TCP:
3113 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3114 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3116 current_bandwidth += stream->bandwidth;
3118 c->next = first_http_ctx;
3130 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3131 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3133 static int rtp_new_av_stream(HTTPContext *c,
3134 int stream_index, struct sockaddr_in *dest_addr,
3135 HTTPContext *rtsp_c)
3137 AVFormatContext *ctx;
3140 URLContext *h = NULL;
3142 int max_packet_size;
3144 /* now we can open the relevant output stream */
3145 ctx = av_alloc_format_context();
3148 ctx->oformat = guess_format("rtp", NULL, NULL);
3150 st = av_mallocz(sizeof(AVStream));
3153 st->codec= avcodec_alloc_context();
3154 ctx->nb_streams = 1;
3155 ctx->streams[0] = st;
3157 if (!c->stream->feed ||
3158 c->stream->feed == c->stream)
3159 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3162 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3164 st->priv_data = NULL;
3166 /* build destination RTP address */
3167 ipaddr = inet_ntoa(dest_addr->sin_addr);
3169 switch(c->rtp_protocol) {
3170 case RTSP_PROTOCOL_RTP_UDP:
3171 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3174 /* XXX: also pass as parameter to function ? */
3175 if (c->stream->is_multicast) {
3177 ttl = c->stream->multicast_ttl;
3180 snprintf(ctx->filename, sizeof(ctx->filename),
3181 "rtp://%s:%d?multicast=1&ttl=%d",
3182 ipaddr, ntohs(dest_addr->sin_port), ttl);
3184 snprintf(ctx->filename, sizeof(ctx->filename),
3185 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3188 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3190 c->rtp_handles[stream_index] = h;
3191 max_packet_size = url_get_max_packet_size(h);
3193 case RTSP_PROTOCOL_RTP_TCP:
3196 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3202 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3203 ipaddr, ntohs(dest_addr->sin_port),
3204 c->stream->filename, stream_index, c->protocol);
3206 /* normally, no packets should be output here, but the packet size may be checked */
3207 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3208 /* XXX: close stream */
3211 av_set_parameters(ctx, NULL);
3212 if (av_write_header(ctx) < 0) {
3219 url_close_dyn_buf(ctx->pb, &dummy_buf);
3222 c->rtp_ctx[stream_index] = ctx;
3226 /********************************************************************/
3227 /* ffserver initialization */
3229 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3233 fst = av_mallocz(sizeof(AVStream));
3236 fst->codec= avcodec_alloc_context();
3237 fst->priv_data = av_mallocz(sizeof(FeedData));
3238 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3239 fst->index = stream->nb_streams;
3240 av_set_pts_info(fst, 33, 1, 90000);
3241 stream->streams[stream->nb_streams++] = fst;
3245 /* return the stream number in the feed */
3246 static int add_av_stream(FFStream *feed, AVStream *st)
3249 AVCodecContext *av, *av1;
3253 for(i=0;i<feed->nb_streams;i++) {
3254 st = feed->streams[i];
3256 if (av1->codec_id == av->codec_id &&
3257 av1->codec_type == av->codec_type &&
3258 av1->bit_rate == av->bit_rate) {
3260 switch(av->codec_type) {
3261 case CODEC_TYPE_AUDIO:
3262 if (av1->channels == av->channels &&
3263 av1->sample_rate == av->sample_rate)
3266 case CODEC_TYPE_VIDEO:
3267 if (av1->width == av->width &&
3268 av1->height == av->height &&
3269 av1->time_base.den == av->time_base.den &&
3270 av1->time_base.num == av->time_base.num &&
3271 av1->gop_size == av->gop_size)
3280 fst = add_av_stream1(feed, av);
3283 return feed->nb_streams - 1;
3288 static void remove_stream(FFStream *stream)
3292 while (*ps != NULL) {
3300 /* specific mpeg4 handling : we extract the raw parameters */
3301 static void extract_mpeg4_header(AVFormatContext *infile)
3303 int mpeg4_count, i, size;
3309 for(i=0;i<infile->nb_streams;i++) {
3310 st = infile->streams[i];
3311 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3312 st->codec->extradata_size == 0) {
3319 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3320 while (mpeg4_count > 0) {
3321 if (av_read_packet(infile, &pkt) < 0)
3323 st = infile->streams[pkt.stream_index];
3324 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3325 st->codec->extradata_size == 0) {
3326 av_freep(&st->codec->extradata);
3327 /* fill extradata with the header */
3328 /* XXX: we make hard suppositions here ! */
3330 while (p < pkt.data + pkt.size - 4) {
3331 /* stop when vop header is found */
3332 if (p[0] == 0x00 && p[1] == 0x00 &&
3333 p[2] == 0x01 && p[3] == 0xb6) {
3334 size = p - pkt.data;
3335 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3336 st->codec->extradata = av_malloc(size);
3337 st->codec->extradata_size = size;
3338 memcpy(st->codec->extradata, pkt.data, size);
3345 av_free_packet(&pkt);
3349 /* compute the needed AVStream for each file */
3350 static void build_file_streams(void)
3352 FFStream *stream, *stream_next;
3353 AVFormatContext *infile;
3356 /* gather all streams */
3357 for(stream = first_stream; stream != NULL; stream = stream_next) {
3358 stream_next = stream->next;
3359 if (stream->stream_type == STREAM_TYPE_LIVE &&
3361 /* the stream comes from a file */
3362 /* try to open the file */
3364 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3365 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3366 /* specific case : if transport stream output to RTP,
3367 we use a raw transport stream reader */
3368 stream->ap_in->mpeg2ts_raw = 1;
3369 stream->ap_in->mpeg2ts_compute_pcr = 1;
3372 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3373 stream->ifmt, 0, stream->ap_in)) < 0) {
3374 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3375 /* remove stream (no need to spend more time on it) */
3377 remove_stream(stream);
3379 /* find all the AVStreams inside and reference them in
3381 if (av_find_stream_info(infile) < 0) {
3382 http_log("Could not find codec parameters from '%s'\n",
3383 stream->feed_filename);
3384 av_close_input_file(infile);
3387 extract_mpeg4_header(infile);
3389 for(i=0;i<infile->nb_streams;i++)
3390 add_av_stream1(stream, infile->streams[i]->codec);
3392 av_close_input_file(infile);
3398 /* compute the needed AVStream for each feed */
3399 static void build_feed_streams(void)
3401 FFStream *stream, *feed;
3404 /* gather all streams */
3405 for(stream = first_stream; stream != NULL; stream = stream->next) {
3406 feed = stream->feed;
3408 if (!stream->is_feed) {
3409 /* we handle a stream coming from a feed */
3410 for(i=0;i<stream->nb_streams;i++)
3411 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3416 /* gather all streams */
3417 for(stream = first_stream; stream != NULL; stream = stream->next) {
3418 feed = stream->feed;
3420 if (stream->is_feed) {
3421 for(i=0;i<stream->nb_streams;i++)
3422 stream->feed_streams[i] = i;
3427 /* create feed files if needed */
3428 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3431 if (url_exist(feed->feed_filename)) {
3432 /* See if it matches */
3436 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3437 /* Now see if it matches */
3438 if (s->nb_streams == feed->nb_streams) {
3440 for(i=0;i<s->nb_streams;i++) {
3442 sf = feed->streams[i];
3445 if (sf->index != ss->index ||
3447 http_log("Index & Id do not match for stream %d (%s)\n",
3448 i, feed->feed_filename);
3451 AVCodecContext *ccf, *ccs;
3455 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3457 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3458 http_log("Codecs do not match for stream %d\n", i);
3460 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3461 http_log("Codec bitrates do not match for stream %d\n", i);
3463 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3464 if (CHECK_CODEC(time_base.den) ||
3465 CHECK_CODEC(time_base.num) ||
3466 CHECK_CODEC(width) ||
3467 CHECK_CODEC(height)) {
3468 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3471 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3472 if (CHECK_CODEC(sample_rate) ||
3473 CHECK_CODEC(channels) ||
3474 CHECK_CODEC(frame_size)) {
3475 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3479 http_log("Unknown codec type\n");
3487 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3488 feed->feed_filename, s->nb_streams, feed->nb_streams);
3490 av_close_input_file(s);
3492 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3493 feed->feed_filename);
3496 if (feed->readonly) {
3497 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3498 feed->feed_filename);
3501 unlink(feed->feed_filename);
3504 if (!url_exist(feed->feed_filename)) {
3505 AVFormatContext s1, *s = &s1;
3507 if (feed->readonly) {
3508 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3509 feed->feed_filename);
3513 /* only write the header of the ffm file */
3514 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3515 http_log("Could not open output feed file '%s'\n",
3516 feed->feed_filename);
3519 s->oformat = feed->fmt;
3520 s->nb_streams = feed->nb_streams;
3521 for(i=0;i<s->nb_streams;i++) {
3523 st = feed->streams[i];
3526 av_set_parameters(s, NULL);
3527 if (av_write_header(s) < 0) {
3528 http_log("Container doesn't supports the required parameters\n");
3531 /* XXX: need better api */
3532 av_freep(&s->priv_data);
3535 /* get feed size and write index */
3536 fd = open(feed->feed_filename, O_RDONLY);
3538 http_log("Could not open output feed file '%s'\n",
3539 feed->feed_filename);
3543 feed->feed_write_index = ffm_read_write_index(fd);
3544 feed->feed_size = lseek(fd, 0, SEEK_END);
3545 /* ensure that we do not wrap before the end of file */
3546 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3547 feed->feed_max_size = feed->feed_size;
3553 /* compute the bandwidth used by each stream */
3554 static void compute_bandwidth(void)
3560 for(stream = first_stream; stream != NULL; stream = stream->next) {
3562 for(i=0;i<stream->nb_streams;i++) {
3563 AVStream *st = stream->streams[i];
3564 switch(st->codec->codec_type) {
3565 case CODEC_TYPE_AUDIO:
3566 case CODEC_TYPE_VIDEO:
3567 bandwidth += st->codec->bit_rate;
3573 stream->bandwidth = (bandwidth + 999) / 1000;
3577 static void get_arg(char *buf, int buf_size, const char **pp)
3584 while (isspace(*p)) p++;
3587 if (*p == '\"' || *p == '\'')
3599 if ((q - buf) < buf_size - 1)
3604 if (quote && *p == quote)
3609 /* add a codec and set the default parameters */
3610 static void add_codec(FFStream *stream, AVCodecContext *av)
3614 /* compute default parameters */
3615 switch(av->codec_type) {
3616 case CODEC_TYPE_AUDIO:
3617 if (av->bit_rate == 0)
3618 av->bit_rate = 64000;
3619 if (av->sample_rate == 0)
3620 av->sample_rate = 22050;
3621 if (av->channels == 0)
3624 case CODEC_TYPE_VIDEO:
3625 if (av->bit_rate == 0)
3626 av->bit_rate = 64000;
3627 if (av->time_base.num == 0){
3628 av->time_base.den = 5;
3629 av->time_base.num = 1;
3631 if (av->width == 0 || av->height == 0) {
3635 /* Bitrate tolerance is less for streaming */
3636 if (av->bit_rate_tolerance == 0)
3637 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3638 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3643 if (av->max_qdiff == 0)
3645 av->qcompress = 0.5;
3648 if (!av->nsse_weight)
3649 av->nsse_weight = 8;
3651 av->frame_skip_cmp = FF_CMP_DCTMAX;
3652 av->me_method = ME_EPZS;
3653 av->rc_buffer_aggressivity = 1.0;
3656 av->rc_eq = "tex^qComp";
3657 if (!av->i_quant_factor)
3658 av->i_quant_factor = -0.8;
3659 if (!av->b_quant_factor)
3660 av->b_quant_factor = 1.25;
3661 if (!av->b_quant_offset)
3662 av->b_quant_offset = 1.25;
3663 if (!av->rc_max_rate)
3664 av->rc_max_rate = av->bit_rate * 2;
3666 if (av->rc_max_rate && !av->rc_buffer_size) {
3667 av->rc_buffer_size = av->rc_max_rate;
3676 st = av_mallocz(sizeof(AVStream));
3679 st->codec = avcodec_alloc_context();
3680 stream->streams[stream->nb_streams++] = st;
3681 memcpy(st->codec, av, sizeof(AVCodecContext));
3684 static int opt_audio_codec(const char *arg)
3686 AVCodec *p= avcodec_find_encoder_by_name(arg);
3688 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3689 return CODEC_ID_NONE;
3694 static int opt_video_codec(const char *arg)
3696 AVCodec *p= avcodec_find_encoder_by_name(arg);
3698 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3699 return CODEC_ID_NONE;
3704 /* simplistic plugin support */
3707 static void load_module(const char *filename)
3710 void (*init_func)(void);
3711 dll = dlopen(filename, RTLD_NOW);
3713 fprintf(stderr, "Could not load module '%s' - %s\n",
3714 filename, dlerror());
3718 init_func = dlsym(dll, "ffserver_module_init");
3721 "%s: init function 'ffserver_module_init()' not found\n",
3730 static int opt_default(const char *opt, const char *arg,
3731 AVCodecContext *avctx, int type)
3733 const AVOption *o = NULL;
3734 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3736 o = av_set_string2(avctx, opt, arg, 1);
3742 static int parse_ffconfig(const char *filename)
3749 int val, errors, line_num;
3750 FFStream **last_stream, *stream, *redirect;
3751 FFStream **last_feed, *feed;
3752 AVCodecContext audio_enc, video_enc;
3753 int audio_id, video_id;
3755 f = fopen(filename, "r");
3763 first_stream = NULL;
3764 last_stream = &first_stream;
3766 last_feed = &first_feed;
3770 audio_id = CODEC_ID_NONE;
3771 video_id = CODEC_ID_NONE;
3773 if (fgets(line, sizeof(line), f) == NULL)
3779 if (*p == '\0' || *p == '#')
3782 get_arg(cmd, sizeof(cmd), &p);
3784 if (!strcasecmp(cmd, "Port")) {
3785 get_arg(arg, sizeof(arg), &p);
3787 if (val < 1 || val > 65536) {
3788 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3789 filename, line_num, arg);
3792 my_http_addr.sin_port = htons(val);
3793 } else if (!strcasecmp(cmd, "BindAddress")) {
3794 get_arg(arg, sizeof(arg), &p);
3795 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3796 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3797 filename, line_num, arg);
3800 } else if (!strcasecmp(cmd, "NoDaemon")) {
3801 ffserver_daemon = 0;
3802 } else if (!strcasecmp(cmd, "RTSPPort")) {
3803 get_arg(arg, sizeof(arg), &p);
3805 if (val < 1 || val > 65536) {
3806 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3807 filename, line_num, arg);
3810 my_rtsp_addr.sin_port = htons(atoi(arg));
3811 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3812 get_arg(arg, sizeof(arg), &p);
3813 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3814 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3815 filename, line_num, arg);
3818 } else if (!strcasecmp(cmd, "MaxClients")) {
3819 get_arg(arg, sizeof(arg), &p);
3821 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3822 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3823 filename, line_num, arg);
3826 nb_max_connections = val;
3828 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3830 get_arg(arg, sizeof(arg), &p);
3832 if (llval < 10 || llval > 10000000) {
3833 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3834 filename, line_num, arg);
3837 max_bandwidth = llval;
3838 } else if (!strcasecmp(cmd, "CustomLog")) {
3839 if (!ffserver_debug)
3840 get_arg(logfilename, sizeof(logfilename), &p);
3841 } else if (!strcasecmp(cmd, "<Feed")) {
3842 /*********************************************/
3843 /* Feed related options */
3845 if (stream || feed) {
3846 fprintf(stderr, "%s:%d: Already in a tag\n",
3847 filename, line_num);
3849 feed = av_mallocz(sizeof(FFStream));
3850 /* add in stream list */
3851 *last_stream = feed;
3852 last_stream = &feed->next;
3853 /* add in feed list */
3855 last_feed = &feed->next_feed;
3857 get_arg(feed->filename, sizeof(feed->filename), &p);
3858 q = strrchr(feed->filename, '>');
3861 feed->fmt = guess_format("ffm", NULL, NULL);
3862 /* defaut feed file */
3863 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3864 "/tmp/%s.ffm", feed->filename);
3865 feed->feed_max_size = 5 * 1024 * 1024;
3867 feed->feed = feed; /* self feeding :-) */
3869 } else if (!strcasecmp(cmd, "Launch")) {
3873 feed->child_argv = av_mallocz(64 * sizeof(char *));
3875 for (i = 0; i < 62; i++) {
3876 get_arg(arg, sizeof(arg), &p);
3880 feed->child_argv[i] = av_strdup(arg);
3883 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3885 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3887 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3888 inet_ntoa(my_http_addr.sin_addr),
3889 ntohs(my_http_addr.sin_port), feed->filename);
3891 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3893 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3895 } else if (stream) {
3896 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3898 } else if (!strcasecmp(cmd, "File")) {
3900 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3902 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3903 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3908 get_arg(arg, sizeof(arg), &p);
3910 fsize = strtod(p1, &p1);
3911 switch(toupper(*p1)) {
3916 fsize *= 1024 * 1024;
3919 fsize *= 1024 * 1024 * 1024;
3922 feed->feed_max_size = (int64_t)fsize;
3924 } else if (!strcasecmp(cmd, "</Feed>")) {
3926 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3927 filename, line_num);
3931 } else if (!strcasecmp(cmd, "<Stream")) {
3932 /*********************************************/
3933 /* Stream related options */
3935 if (stream || feed) {
3936 fprintf(stderr, "%s:%d: Already in a tag\n",
3937 filename, line_num);
3939 const AVClass *class;
3940 stream = av_mallocz(sizeof(FFStream));
3941 *last_stream = stream;
3942 last_stream = &stream->next;
3944 get_arg(stream->filename, sizeof(stream->filename), &p);
3945 q = strrchr(stream->filename, '>');
3948 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3949 /* fetch avclass so AVOption works
3950 * FIXME try to use avcodec_get_context_defaults2
3951 * without changing defaults too much */
3952 avcodec_get_context_defaults(&video_enc);
3953 class = video_enc.av_class;
3954 memset(&audio_enc, 0, sizeof(AVCodecContext));
3955 memset(&video_enc, 0, sizeof(AVCodecContext));
3956 audio_enc.av_class = class;
3957 video_enc.av_class = class;
3958 audio_id = CODEC_ID_NONE;
3959 video_id = CODEC_ID_NONE;
3961 audio_id = stream->fmt->audio_codec;
3962 video_id = stream->fmt->video_codec;
3965 } else if (!strcasecmp(cmd, "Feed")) {
3966 get_arg(arg, sizeof(arg), &p);
3971 while (sfeed != NULL) {
3972 if (!strcmp(sfeed->filename, arg))
3974 sfeed = sfeed->next_feed;
3977 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3978 filename, line_num, arg);
3980 stream->feed = sfeed;
3982 } else if (!strcasecmp(cmd, "Format")) {
3983 get_arg(arg, sizeof(arg), &p);
3985 if (!strcmp(arg, "status")) {
3986 stream->stream_type = STREAM_TYPE_STATUS;
3989 stream->stream_type = STREAM_TYPE_LIVE;
3990 /* jpeg cannot be used here, so use single frame jpeg */
3991 if (!strcmp(arg, "jpeg"))
3992 strcpy(arg, "mjpeg");
3993 stream->fmt = guess_stream_format(arg, NULL, NULL);
3995 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3996 filename, line_num, arg);
4001 audio_id = stream->fmt->audio_codec;
4002 video_id = stream->fmt->video_codec;
4005 } else if (!strcasecmp(cmd, "InputFormat")) {
4006 get_arg(arg, sizeof(arg), &p);
4007 stream->ifmt = av_find_input_format(arg);
4008 if (!stream->ifmt) {
4009 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4010 filename, line_num, arg);
4012 } else if (!strcasecmp(cmd, "FaviconURL")) {
4013 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4014 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4016 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4017 filename, line_num);
4020 } else if (!strcasecmp(cmd, "Author")) {
4022 get_arg(stream->author, sizeof(stream->author), &p);
4023 } else if (!strcasecmp(cmd, "Comment")) {
4025 get_arg(stream->comment, sizeof(stream->comment), &p);
4026 } else if (!strcasecmp(cmd, "Copyright")) {
4028 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4029 } else if (!strcasecmp(cmd, "Title")) {
4031 get_arg(stream->title, sizeof(stream->title), &p);
4032 } else if (!strcasecmp(cmd, "Preroll")) {
4033 get_arg(arg, sizeof(arg), &p);
4035 stream->prebuffer = atof(arg) * 1000;
4036 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4038 stream->send_on_key = 1;
4039 } else if (!strcasecmp(cmd, "AudioCodec")) {
4040 get_arg(arg, sizeof(arg), &p);
4041 audio_id = opt_audio_codec(arg);
4042 if (audio_id == CODEC_ID_NONE) {
4043 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4044 filename, line_num, arg);
4047 } else if (!strcasecmp(cmd, "VideoCodec")) {
4048 get_arg(arg, sizeof(arg), &p);
4049 video_id = opt_video_codec(arg);
4050 if (video_id == CODEC_ID_NONE) {
4051 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4052 filename, line_num, arg);
4055 } else if (!strcasecmp(cmd, "MaxTime")) {
4056 get_arg(arg, sizeof(arg), &p);
4058 stream->max_time = atof(arg) * 1000;
4059 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4060 get_arg(arg, sizeof(arg), &p);
4062 audio_enc.bit_rate = atoi(arg) * 1000;
4063 } else if (!strcasecmp(cmd, "AudioChannels")) {
4064 get_arg(arg, sizeof(arg), &p);
4066 audio_enc.channels = atoi(arg);
4067 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4068 get_arg(arg, sizeof(arg), &p);
4070 audio_enc.sample_rate = atoi(arg);
4071 } else if (!strcasecmp(cmd, "AudioQuality")) {
4072 get_arg(arg, sizeof(arg), &p);
4074 // audio_enc.quality = atof(arg) * 1000;
4076 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4078 int minrate, maxrate;
4080 get_arg(arg, sizeof(arg), &p);
4082 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4083 video_enc.rc_min_rate = minrate * 1000;
4084 video_enc.rc_max_rate = maxrate * 1000;
4086 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4087 filename, line_num, arg);
4091 } else if (!strcasecmp(cmd, "Debug")) {
4093 get_arg(arg, sizeof(arg), &p);
4094 video_enc.debug = strtol(arg,0,0);
4096 } else if (!strcasecmp(cmd, "Strict")) {
4098 get_arg(arg, sizeof(arg), &p);
4099 video_enc.strict_std_compliance = atoi(arg);
4101 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4103 get_arg(arg, sizeof(arg), &p);
4104 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4106 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4108 get_arg(arg, sizeof(arg), &p);
4109 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4111 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4112 get_arg(arg, sizeof(arg), &p);
4114 video_enc.bit_rate = atoi(arg) * 1000;
4116 } else if (!strcasecmp(cmd, "VideoSize")) {
4117 get_arg(arg, sizeof(arg), &p);
4119 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4120 if ((video_enc.width % 16) != 0 ||
4121 (video_enc.height % 16) != 0) {
4122 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4123 filename, line_num);
4127 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4128 get_arg(arg, sizeof(arg), &p);
4130 AVRational frame_rate;
4131 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4132 fprintf(stderr, "Incorrect frame rate\n");
4135 video_enc.time_base.num = frame_rate.den;
4136 video_enc.time_base.den = frame_rate.num;
4139 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4140 get_arg(arg, sizeof(arg), &p);
4142 video_enc.gop_size = atoi(arg);
4143 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4145 video_enc.gop_size = 1;
4146 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4148 video_enc.mb_decision = FF_MB_DECISION_BITS;
4149 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4151 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4152 video_enc.flags |= CODEC_FLAG_4MV;
4154 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4155 !strcasecmp(cmd, "AVOptionAudio")) {
4157 AVCodecContext *avctx;
4159 get_arg(arg, sizeof(arg), &p);
4160 get_arg(arg2, sizeof(arg2), &p);
4161 if (!strcasecmp(cmd, "AVOptionVideo")) {
4163 type = AV_OPT_FLAG_VIDEO_PARAM;
4166 type = AV_OPT_FLAG_AUDIO_PARAM;
4168 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4169 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4172 } else if (!strcasecmp(cmd, "VideoTag")) {
4173 get_arg(arg, sizeof(arg), &p);
4174 if ((strlen(arg) == 4) && stream)
4175 video_enc.codec_tag = ff_get_fourcc(arg);
4176 } else if (!strcasecmp(cmd, "BitExact")) {
4178 video_enc.flags |= CODEC_FLAG_BITEXACT;
4179 } else if (!strcasecmp(cmd, "DctFastint")) {
4181 video_enc.dct_algo = FF_DCT_FASTINT;
4182 } else if (!strcasecmp(cmd, "IdctSimple")) {
4184 video_enc.idct_algo = FF_IDCT_SIMPLE;
4185 } else if (!strcasecmp(cmd, "Qscale")) {
4186 get_arg(arg, sizeof(arg), &p);
4188 video_enc.flags |= CODEC_FLAG_QSCALE;
4189 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4191 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4192 get_arg(arg, sizeof(arg), &p);
4194 video_enc.max_qdiff = atoi(arg);
4195 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4196 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4197 filename, line_num);
4201 } else if (!strcasecmp(cmd, "VideoQMax")) {
4202 get_arg(arg, sizeof(arg), &p);
4204 video_enc.qmax = atoi(arg);
4205 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4206 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4207 filename, line_num);
4211 } else if (!strcasecmp(cmd, "VideoQMin")) {
4212 get_arg(arg, sizeof(arg), &p);
4214 video_enc.qmin = atoi(arg);
4215 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4216 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4217 filename, line_num);
4221 } else if (!strcasecmp(cmd, "LumaElim")) {
4222 get_arg(arg, sizeof(arg), &p);
4224 video_enc.luma_elim_threshold = atoi(arg);
4225 } else if (!strcasecmp(cmd, "ChromaElim")) {
4226 get_arg(arg, sizeof(arg), &p);
4228 video_enc.chroma_elim_threshold = atoi(arg);
4229 } else if (!strcasecmp(cmd, "LumiMask")) {
4230 get_arg(arg, sizeof(arg), &p);
4232 video_enc.lumi_masking = atof(arg);
4233 } else if (!strcasecmp(cmd, "DarkMask")) {
4234 get_arg(arg, sizeof(arg), &p);
4236 video_enc.dark_masking = atof(arg);
4237 } else if (!strcasecmp(cmd, "NoVideo")) {
4238 video_id = CODEC_ID_NONE;
4239 } else if (!strcasecmp(cmd, "NoAudio")) {
4240 audio_id = CODEC_ID_NONE;
4241 } else if (!strcasecmp(cmd, "ACL")) {
4244 get_arg(arg, sizeof(arg), &p);
4245 if (strcasecmp(arg, "allow") == 0)
4246 acl.action = IP_ALLOW;
4247 else if (strcasecmp(arg, "deny") == 0)
4248 acl.action = IP_DENY;
4250 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4251 filename, line_num, arg);
4255 get_arg(arg, sizeof(arg), &p);
4257 if (resolve_host(&acl.first, arg) != 0) {
4258 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4259 filename, line_num, arg);
4262 acl.last = acl.first;
4264 get_arg(arg, sizeof(arg), &p);
4267 if (resolve_host(&acl.last, arg) != 0) {
4268 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4269 filename, line_num, arg);
4275 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4276 IPAddressACL **naclp = 0;
4282 naclp = &stream->acl;
4286 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4287 filename, line_num);
4293 naclp = &(*naclp)->next;
4298 } else if (!strcasecmp(cmd, "RTSPOption")) {
4299 get_arg(arg, sizeof(arg), &p);
4301 av_freep(&stream->rtsp_option);
4302 stream->rtsp_option = av_strdup(arg);
4304 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4305 get_arg(arg, sizeof(arg), &p);
4307 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4308 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4309 filename, line_num, arg);
4312 stream->is_multicast = 1;
4313 stream->loop = 1; /* default is looping */
4315 } else if (!strcasecmp(cmd, "MulticastPort")) {
4316 get_arg(arg, sizeof(arg), &p);
4318 stream->multicast_port = atoi(arg);
4319 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4320 get_arg(arg, sizeof(arg), &p);
4322 stream->multicast_ttl = atoi(arg);
4323 } else if (!strcasecmp(cmd, "NoLoop")) {
4326 } else if (!strcasecmp(cmd, "</Stream>")) {
4328 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4329 filename, line_num);
4332 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4333 if (audio_id != CODEC_ID_NONE) {
4334 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4335 audio_enc.codec_id = audio_id;
4336 add_codec(stream, &audio_enc);
4338 if (video_id != CODEC_ID_NONE) {
4339 video_enc.codec_type = CODEC_TYPE_VIDEO;
4340 video_enc.codec_id = video_id;
4341 add_codec(stream, &video_enc);
4346 } else if (!strcasecmp(cmd, "<Redirect")) {
4347 /*********************************************/
4349 if (stream || feed || redirect) {
4350 fprintf(stderr, "%s:%d: Already in a tag\n",
4351 filename, line_num);
4354 redirect = av_mallocz(sizeof(FFStream));
4355 *last_stream = redirect;
4356 last_stream = &redirect->next;
4358 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4359 q = strrchr(redirect->filename, '>');
4362 redirect->stream_type = STREAM_TYPE_REDIRECT;
4364 } else if (!strcasecmp(cmd, "URL")) {
4366 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4367 } else if (!strcasecmp(cmd, "</Redirect>")) {
4369 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4370 filename, line_num);
4373 if (!redirect->feed_filename[0]) {
4374 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4375 filename, line_num);
4380 } else if (!strcasecmp(cmd, "LoadModule")) {
4381 get_arg(arg, sizeof(arg), &p);
4385 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4386 filename, line_num, arg);
4390 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4391 filename, line_num, cmd);
4403 static void handle_child_exit(int sig)
4408 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4411 for (feed = first_feed; feed; feed = feed->next) {
4412 if (feed->pid == pid) {
4413 int uptime = time(0) - feed->pid_start;
4416 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4419 /* Turn off any more restarts */
4420 feed->child_argv = 0;
4425 need_to_start_children = 1;
4428 static void opt_debug()
4431 ffserver_daemon = 0;
4432 logfilename[0] = '-';
4435 static void opt_show_help(void)
4437 printf("usage: ffserver [options]\n"
4438 "Hyper fast multi format Audio/Video streaming server\n");
4440 show_help_options(options, "Main options:\n", 0, 0);
4443 static const OptionDef options[] = {
4444 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4445 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4446 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4447 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4448 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4449 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4450 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4454 int main(int argc, char **argv)
4456 struct sigaction sigact;
4462 config_filename = "/etc/ffserver.conf";
4464 my_program_name = argv[0];
4465 my_program_dir = getcwd(0, 0);
4466 ffserver_daemon = 1;
4468 parse_options(argc, argv, options, NULL);
4470 unsetenv("http_proxy"); /* Kill the http_proxy */
4472 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4474 memset(&sigact, 0, sizeof(sigact));
4475 sigact.sa_handler = handle_child_exit;
4476 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4477 sigaction(SIGCHLD, &sigact, 0);
4479 if (parse_ffconfig(config_filename) < 0) {
4480 fprintf(stderr, "Incorrect config file - exiting.\n");
4484 /* open log file if needed */
4485 if (logfilename[0] != '\0') {
4486 if (!strcmp(logfilename, "-"))
4489 logfile = fopen(logfilename, "a");
4490 av_log_set_callback(http_av_log);
4493 build_file_streams();
4495 build_feed_streams();
4497 compute_bandwidth();
4499 /* put the process in background and detach it from its TTY */
4500 if (ffserver_daemon) {
4507 } else if (pid > 0) {
4514 open("/dev/null", O_RDWR);
4515 if (strcmp(logfilename, "-") != 0) {
4525 signal(SIGPIPE, SIG_IGN);
4527 if (ffserver_daemon)
4530 if (http_server() < 0) {
4531 http_log("Could not start server\n");