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
26 #define closesocket close
31 #include "libavformat/avformat.h"
32 #include "libavformat/network.h"
33 #include "libavformat/os_support.h"
34 #include "libavformat/rtpdec.h"
35 #include "libavformat/rtsp.h"
36 #include "libavutil/avstring.h"
37 #include "libavutil/lfg.h"
38 #include "libavutil/random_seed.h"
39 #include "libavcore/parseutils.h"
40 #include "libavcodec/opt.h"
44 #include <sys/ioctl.h>
59 const char program_name[] = "FFserver";
60 const int program_birth_year = 2000;
62 static const OptionDef options[];
65 HTTPSTATE_WAIT_REQUEST,
66 HTTPSTATE_SEND_HEADER,
67 HTTPSTATE_SEND_DATA_HEADER,
68 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
69 HTTPSTATE_SEND_DATA_TRAILER,
70 HTTPSTATE_RECEIVE_DATA,
71 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
74 RTSPSTATE_WAIT_REQUEST,
76 RTSPSTATE_SEND_PACKET,
79 static const char *http_state[] = {
95 #define IOBUFFER_INIT_SIZE 8192
97 /* timeouts are in ms */
98 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
99 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
101 #define SYNC_TIMEOUT (10 * 1000)
103 typedef struct RTSPActionServerSetup {
105 char transport_option[512];
106 } RTSPActionServerSetup;
109 int64_t count1, count2;
110 int64_t time1, time2;
113 /* context associated with one connection */
114 typedef struct HTTPContext {
115 enum HTTPState state;
116 int fd; /* socket file descriptor */
117 struct sockaddr_in from_addr; /* origin */
118 struct pollfd *poll_entry; /* used when polling */
120 uint8_t *buffer_ptr, *buffer_end;
123 int chunked_encoding;
124 int chunk_size; /* 0 if it needs to be read */
125 struct HTTPContext *next;
126 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
130 /* input format handling */
131 AVFormatContext *fmt_in;
132 int64_t start_time; /* In milliseconds - this wraps fairly often */
133 int64_t first_pts; /* initial pts value */
134 int64_t cur_pts; /* current pts value from the stream in us */
135 int64_t cur_frame_duration; /* duration of the current frame in us */
136 int cur_frame_bytes; /* output frame size, needed to compute
137 the time at which we send each
139 int pts_stream_index; /* stream we choose as clock reference */
140 int64_t cur_clock; /* current clock reference value in us */
141 /* output format handling */
142 struct FFStream *stream;
143 /* -1 is invalid stream */
144 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
145 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
147 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
148 int last_packet_sent; /* true if last data packet was sent */
150 DataRateData datarate;
157 int is_packetized; /* if true, the stream is packetized */
158 int packet_stream_index; /* current stream for output in state machine */
160 /* RTSP state specific */
161 uint8_t *pb_buffer; /* XXX: use that in all the code */
163 int seq; /* RTSP sequence number */
165 /* RTP state specific */
166 enum RTSPLowerTransport rtp_protocol;
167 char session_id[32]; /* session id */
168 AVFormatContext *rtp_ctx[MAX_STREAMS];
170 /* RTP/UDP specific */
171 URLContext *rtp_handles[MAX_STREAMS];
173 /* RTP/TCP specific */
174 struct HTTPContext *rtsp_c;
175 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
178 /* each generated stream is described here */
182 STREAM_TYPE_REDIRECT,
185 enum IPAddressAction {
190 typedef struct IPAddressACL {
191 struct IPAddressACL *next;
192 enum IPAddressAction action;
193 /* These are in host order */
194 struct in_addr first;
198 /* description of each stream of the ffserver.conf file */
199 typedef struct FFStream {
200 enum StreamType stream_type;
201 char filename[1024]; /* stream filename */
202 struct FFStream *feed; /* feed we are using (can be null if
204 AVFormatParameters *ap_in; /* input parameters */
205 AVInputFormat *ifmt; /* if non NULL, force input format */
208 char dynamic_acl[1024];
210 int prebuffer; /* Number of millseconds early to start */
211 int64_t max_time; /* Number of milliseconds to run */
213 AVStream *streams[MAX_STREAMS];
214 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
215 char feed_filename[1024]; /* file name of the feed storage, or
216 input file name for a stream */
221 pid_t pid; /* Of ffmpeg process */
222 time_t pid_start; /* Of ffmpeg process */
224 struct FFStream *next;
225 unsigned bandwidth; /* bandwidth, in kbits/s */
228 /* multicast specific */
230 struct in_addr multicast_ip;
231 int multicast_port; /* first port used for multicast */
233 int loop; /* if true, send the stream in loops (only meaningful if file) */
236 int feed_opened; /* true if someone is writing to the feed */
237 int is_feed; /* true if it is a feed */
238 int readonly; /* True if writing is prohibited to the file */
239 int truncate; /* True if feeder connection truncate the feed file */
241 int64_t bytes_served;
242 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
243 int64_t feed_write_index; /* current write position in feed (it wraps around) */
244 int64_t feed_size; /* current size of feed */
245 struct FFStream *next_feed;
248 typedef struct FeedData {
249 long long data_count;
250 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
253 static struct sockaddr_in my_http_addr;
254 static struct sockaddr_in my_rtsp_addr;
256 static char logfilename[1024];
257 static HTTPContext *first_http_ctx;
258 static FFStream *first_feed; /* contains only feeds */
259 static FFStream *first_stream; /* contains all streams, including feeds */
261 static void new_connection(int server_fd, int is_rtsp);
262 static void close_connection(HTTPContext *c);
265 static int handle_connection(HTTPContext *c);
266 static int http_parse_request(HTTPContext *c);
267 static int http_send_data(HTTPContext *c);
268 static void compute_status(HTTPContext *c);
269 static int open_input_stream(HTTPContext *c, const char *info);
270 static int http_start_receive_data(HTTPContext *c);
271 static int http_receive_data(HTTPContext *c);
274 static int rtsp_parse_request(HTTPContext *c);
275 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
276 static void rtsp_cmd_options(HTTPContext *c, const char *url);
277 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
278 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
279 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
280 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
283 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
284 struct in_addr my_ip);
287 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
288 FFStream *stream, const char *session_id,
289 enum RTSPLowerTransport rtp_protocol);
290 static int rtp_new_av_stream(HTTPContext *c,
291 int stream_index, struct sockaddr_in *dest_addr,
292 HTTPContext *rtsp_c);
294 static const char *my_program_name;
295 static const char *my_program_dir;
297 static const char *config_filename = "/etc/ffserver.conf";
299 static int ffserver_debug;
300 static int ffserver_daemon;
301 static int no_launch;
302 static int need_to_start_children;
304 /* maximum number of simultaneous HTTP connections */
305 static unsigned int nb_max_http_connections = 2000;
306 static unsigned int nb_max_connections = 5;
307 static unsigned int nb_connections;
309 static uint64_t max_bandwidth = 1000;
310 static uint64_t current_bandwidth;
312 static int64_t cur_time; // Making this global saves on passing it around everywhere
314 static AVLFG random_state;
316 static FILE *logfile = NULL;
318 /* FIXME: make ffserver work with IPv6 */
319 /* resolve host with also IP address parsing */
320 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
323 if (!ff_inet_aton(hostname, sin_addr)) {
325 struct addrinfo *ai, *cur;
326 struct addrinfo hints;
327 memset(&hints, 0, sizeof(hints));
328 hints.ai_family = AF_INET;
329 if (getaddrinfo(hostname, NULL, &hints, &ai))
331 /* getaddrinfo returns a linked list of addrinfo structs.
332 * Even if we set ai_family = AF_INET above, make sure
333 * that the returned one actually is of the correct type. */
334 for (cur = ai; cur; cur = cur->ai_next) {
335 if (cur->ai_family == AF_INET) {
336 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
345 hp = gethostbyname(hostname);
348 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
354 static char *ctime1(char *buf2)
362 p = buf2 + strlen(p) - 1;
368 static void http_vlog(const char *fmt, va_list vargs)
370 static int print_prefix = 1;
375 fprintf(logfile, "%s ", buf);
377 print_prefix = strstr(fmt, "\n") != NULL;
378 vfprintf(logfile, fmt, vargs);
383 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
386 va_start(vargs, fmt);
387 http_vlog(fmt, vargs);
391 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
393 static int print_prefix = 1;
394 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
395 if (level > av_log_get_level())
397 if (print_prefix && avc)
398 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
399 print_prefix = strstr(fmt, "\n") != NULL;
400 http_vlog(fmt, vargs);
403 static void log_connection(HTTPContext *c)
408 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
409 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
410 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
413 static void update_datarate(DataRateData *drd, int64_t count)
415 if (!drd->time1 && !drd->count1) {
416 drd->time1 = drd->time2 = cur_time;
417 drd->count1 = drd->count2 = count;
418 } else if (cur_time - drd->time2 > 5000) {
419 drd->time1 = drd->time2;
420 drd->count1 = drd->count2;
421 drd->time2 = cur_time;
426 /* In bytes per second */
427 static int compute_datarate(DataRateData *drd, int64_t count)
429 if (cur_time == drd->time1)
432 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
436 static void start_children(FFStream *feed)
441 for (; feed; feed = feed->next) {
442 if (feed->child_argv && !feed->pid) {
443 feed->pid_start = time(0);
448 http_log("Unable to create children\n");
457 av_strlcpy(pathname, my_program_name, sizeof(pathname));
459 slash = strrchr(pathname, '/');
464 strcpy(slash, "ffmpeg");
466 http_log("Launch commandline: ");
467 http_log("%s ", pathname);
468 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
469 http_log("%s ", feed->child_argv[i]);
472 for (i = 3; i < 256; i++)
475 if (!ffserver_debug) {
476 i = open("/dev/null", O_RDWR);
485 /* This is needed to make relative pathnames work */
486 chdir(my_program_dir);
488 signal(SIGPIPE, SIG_DFL);
490 execvp(pathname, feed->child_argv);
498 /* open a listening socket */
499 static int socket_open_listen(struct sockaddr_in *my_addr)
503 server_fd = socket(AF_INET,SOCK_STREAM,0);
510 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
512 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
514 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
516 closesocket(server_fd);
520 if (listen (server_fd, 5) < 0) {
522 closesocket(server_fd);
525 ff_socket_nonblock(server_fd, 1);
530 /* start all multicast streams */
531 static void start_multicast(void)
536 struct sockaddr_in dest_addr;
537 int default_port, stream_index;
540 for(stream = first_stream; stream != NULL; stream = stream->next) {
541 if (stream->is_multicast) {
542 /* open the RTP connection */
543 snprintf(session_id, sizeof(session_id), "%08x%08x",
544 av_lfg_get(&random_state), av_lfg_get(&random_state));
546 /* choose a port if none given */
547 if (stream->multicast_port == 0) {
548 stream->multicast_port = default_port;
552 dest_addr.sin_family = AF_INET;
553 dest_addr.sin_addr = stream->multicast_ip;
554 dest_addr.sin_port = htons(stream->multicast_port);
556 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
557 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
561 if (open_input_stream(rtp_c, "") < 0) {
562 http_log("Could not open input stream for stream '%s'\n",
567 /* open each RTP stream */
568 for(stream_index = 0; stream_index < stream->nb_streams;
570 dest_addr.sin_port = htons(stream->multicast_port +
572 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
573 http_log("Could not open output stream '%s/streamid=%d'\n",
574 stream->filename, stream_index);
579 /* change state to send data */
580 rtp_c->state = HTTPSTATE_SEND_DATA;
585 /* main loop of the http server */
586 static int http_server(void)
588 int server_fd = 0, rtsp_server_fd = 0;
589 int ret, delay, delay1;
590 struct pollfd *poll_table, *poll_entry;
591 HTTPContext *c, *c_next;
593 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
594 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
598 if (my_http_addr.sin_port) {
599 server_fd = socket_open_listen(&my_http_addr);
604 if (my_rtsp_addr.sin_port) {
605 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
606 if (rtsp_server_fd < 0)
610 if (!rtsp_server_fd && !server_fd) {
611 http_log("HTTP and RTSP disabled.\n");
615 http_log("FFserver started.\n");
617 start_children(first_feed);
622 poll_entry = poll_table;
624 poll_entry->fd = server_fd;
625 poll_entry->events = POLLIN;
628 if (rtsp_server_fd) {
629 poll_entry->fd = rtsp_server_fd;
630 poll_entry->events = POLLIN;
634 /* wait for events on each HTTP handle */
641 case HTTPSTATE_SEND_HEADER:
642 case RTSPSTATE_SEND_REPLY:
643 case RTSPSTATE_SEND_PACKET:
644 c->poll_entry = poll_entry;
646 poll_entry->events = POLLOUT;
649 case HTTPSTATE_SEND_DATA_HEADER:
650 case HTTPSTATE_SEND_DATA:
651 case HTTPSTATE_SEND_DATA_TRAILER:
652 if (!c->is_packetized) {
653 /* for TCP, we output as much as we can (may need to put a limit) */
654 c->poll_entry = poll_entry;
656 poll_entry->events = POLLOUT;
659 /* when ffserver is doing the timing, we work by
660 looking at which packet need to be sent every
662 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
667 case HTTPSTATE_WAIT_REQUEST:
668 case HTTPSTATE_RECEIVE_DATA:
669 case HTTPSTATE_WAIT_FEED:
670 case RTSPSTATE_WAIT_REQUEST:
671 /* need to catch errors */
672 c->poll_entry = poll_entry;
674 poll_entry->events = POLLIN;/* Maybe this will work */
678 c->poll_entry = NULL;
684 /* wait for an event on one connection. We poll at least every
685 second to handle timeouts */
687 ret = poll(poll_table, poll_entry - poll_table, delay);
688 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
689 ff_neterrno() != FF_NETERROR(EINTR))
693 cur_time = av_gettime() / 1000;
695 if (need_to_start_children) {
696 need_to_start_children = 0;
697 start_children(first_feed);
700 /* now handle the events */
701 for(c = first_http_ctx; c != NULL; c = c_next) {
703 if (handle_connection(c) < 0) {
704 /* close and free the connection */
710 poll_entry = poll_table;
712 /* new HTTP connection request ? */
713 if (poll_entry->revents & POLLIN)
714 new_connection(server_fd, 0);
717 if (rtsp_server_fd) {
718 /* new RTSP connection request ? */
719 if (poll_entry->revents & POLLIN)
720 new_connection(rtsp_server_fd, 1);
725 /* start waiting for a new HTTP/RTSP request */
726 static void start_wait_request(HTTPContext *c, int is_rtsp)
728 c->buffer_ptr = c->buffer;
729 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
732 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
733 c->state = RTSPSTATE_WAIT_REQUEST;
735 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
736 c->state = HTTPSTATE_WAIT_REQUEST;
740 static void http_send_too_busy_reply(int fd)
743 int len = snprintf(buffer, sizeof(buffer),
744 "HTTP/1.0 503 Server too busy\r\n"
745 "Content-type: text/html\r\n"
747 "<html><head><title>Too busy</title></head><body>\r\n"
748 "<p>The server is too busy to serve your request at this time.</p>\r\n"
749 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
750 "</body></html>\r\n",
751 nb_connections, nb_max_connections);
752 send(fd, buffer, len, 0);
756 static void new_connection(int server_fd, int is_rtsp)
758 struct sockaddr_in from_addr;
760 HTTPContext *c = NULL;
762 len = sizeof(from_addr);
763 fd = accept(server_fd, (struct sockaddr *)&from_addr,
766 http_log("error during accept %s\n", strerror(errno));
769 ff_socket_nonblock(fd, 1);
771 if (nb_connections >= nb_max_connections) {
772 http_send_too_busy_reply(fd);
776 /* add a new connection */
777 c = av_mallocz(sizeof(HTTPContext));
782 c->poll_entry = NULL;
783 c->from_addr = from_addr;
784 c->buffer_size = IOBUFFER_INIT_SIZE;
785 c->buffer = av_malloc(c->buffer_size);
789 c->next = first_http_ctx;
793 start_wait_request(c, is_rtsp);
805 static void close_connection(HTTPContext *c)
807 HTTPContext **cp, *c1;
809 AVFormatContext *ctx;
813 /* remove connection from list */
814 cp = &first_http_ctx;
815 while ((*cp) != NULL) {
823 /* remove references, if any (XXX: do it faster) */
824 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
829 /* remove connection associated resources */
833 /* close each frame parser */
834 for(i=0;i<c->fmt_in->nb_streams;i++) {
835 st = c->fmt_in->streams[i];
836 if (st->codec->codec)
837 avcodec_close(st->codec);
839 av_close_input_file(c->fmt_in);
842 /* free RTP output streams if any */
845 nb_streams = c->stream->nb_streams;
847 for(i=0;i<nb_streams;i++) {
850 av_write_trailer(ctx);
851 av_metadata_free(&ctx->metadata);
852 av_free(ctx->streams[0]);
855 h = c->rtp_handles[i];
862 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
865 if (url_open_dyn_buf(&ctx->pb) >= 0) {
866 av_write_trailer(ctx);
867 av_freep(&c->pb_buffer);
868 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
873 for(i=0; i<ctx->nb_streams; i++)
874 av_free(ctx->streams[i]);
876 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
877 current_bandwidth -= c->stream->bandwidth;
879 /* signal that there is no feed if we are the feeder socket */
880 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
881 c->stream->feed_opened = 0;
885 av_freep(&c->pb_buffer);
886 av_freep(&c->packet_buffer);
892 static int handle_connection(HTTPContext *c)
897 case HTTPSTATE_WAIT_REQUEST:
898 case RTSPSTATE_WAIT_REQUEST:
900 if ((c->timeout - cur_time) < 0)
902 if (c->poll_entry->revents & (POLLERR | POLLHUP))
905 /* no need to read if no events */
906 if (!(c->poll_entry->revents & POLLIN))
910 len = recv(c->fd, c->buffer_ptr, 1, 0);
912 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
913 ff_neterrno() != FF_NETERROR(EINTR))
915 } else if (len == 0) {
918 /* search for end of request. */
920 c->buffer_ptr += len;
922 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
923 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
924 /* request found : parse it and reply */
925 if (c->state == HTTPSTATE_WAIT_REQUEST) {
926 ret = http_parse_request(c);
928 ret = rtsp_parse_request(c);
932 } else if (ptr >= c->buffer_end) {
933 /* request too long: cannot do anything */
935 } else goto read_loop;
939 case HTTPSTATE_SEND_HEADER:
940 if (c->poll_entry->revents & (POLLERR | POLLHUP))
943 /* no need to write if no events */
944 if (!(c->poll_entry->revents & POLLOUT))
946 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
948 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
949 ff_neterrno() != FF_NETERROR(EINTR)) {
950 /* error : close connection */
951 av_freep(&c->pb_buffer);
955 c->buffer_ptr += len;
957 c->stream->bytes_served += len;
958 c->data_count += len;
959 if (c->buffer_ptr >= c->buffer_end) {
960 av_freep(&c->pb_buffer);
964 /* all the buffer was sent : synchronize to the incoming stream */
965 c->state = HTTPSTATE_SEND_DATA_HEADER;
966 c->buffer_ptr = c->buffer_end = c->buffer;
971 case HTTPSTATE_SEND_DATA:
972 case HTTPSTATE_SEND_DATA_HEADER:
973 case HTTPSTATE_SEND_DATA_TRAILER:
974 /* for packetized output, we consider we can always write (the
975 input streams sets the speed). It may be better to verify
976 that we do not rely too much on the kernel queues */
977 if (!c->is_packetized) {
978 if (c->poll_entry->revents & (POLLERR | POLLHUP))
981 /* no need to read if no events */
982 if (!(c->poll_entry->revents & POLLOUT))
985 if (http_send_data(c) < 0)
987 /* close connection if trailer sent */
988 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
991 case HTTPSTATE_RECEIVE_DATA:
992 /* no need to read if no events */
993 if (c->poll_entry->revents & (POLLERR | POLLHUP))
995 if (!(c->poll_entry->revents & POLLIN))
997 if (http_receive_data(c) < 0)
1000 case HTTPSTATE_WAIT_FEED:
1001 /* no need to read if no events */
1002 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1005 /* nothing to do, we'll be waken up by incoming feed packets */
1008 case RTSPSTATE_SEND_REPLY:
1009 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1010 av_freep(&c->pb_buffer);
1013 /* no need to write if no events */
1014 if (!(c->poll_entry->revents & POLLOUT))
1016 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1018 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
1019 ff_neterrno() != FF_NETERROR(EINTR)) {
1020 /* error : close connection */
1021 av_freep(&c->pb_buffer);
1025 c->buffer_ptr += len;
1026 c->data_count += len;
1027 if (c->buffer_ptr >= c->buffer_end) {
1028 /* all the buffer was sent : wait for a new request */
1029 av_freep(&c->pb_buffer);
1030 start_wait_request(c, 1);
1034 case RTSPSTATE_SEND_PACKET:
1035 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1036 av_freep(&c->packet_buffer);
1039 /* no need to write if no events */
1040 if (!(c->poll_entry->revents & POLLOUT))
1042 len = send(c->fd, c->packet_buffer_ptr,
1043 c->packet_buffer_end - c->packet_buffer_ptr, 0);
1045 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
1046 ff_neterrno() != FF_NETERROR(EINTR)) {
1047 /* error : close connection */
1048 av_freep(&c->packet_buffer);
1052 c->packet_buffer_ptr += len;
1053 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1054 /* all the buffer was sent : wait for a new request */
1055 av_freep(&c->packet_buffer);
1056 c->state = RTSPSTATE_WAIT_REQUEST;
1060 case HTTPSTATE_READY:
1069 static int extract_rates(char *rates, int ratelen, const char *request)
1073 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1074 if (strncasecmp(p, "Pragma:", 7) == 0) {
1075 const char *q = p + 7;
1077 while (*q && *q != '\n' && isspace(*q))
1080 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1086 memset(rates, 0xff, ratelen);
1089 while (*q && *q != '\n' && *q != ':')
1092 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1096 if (stream_no < ratelen && stream_no >= 0)
1097 rates[stream_no] = rate_no;
1099 while (*q && *q != '\n' && !isspace(*q))
1106 p = strchr(p, '\n');
1116 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1119 int best_bitrate = 100000000;
1122 for (i = 0; i < feed->nb_streams; i++) {
1123 AVCodecContext *feed_codec = feed->streams[i]->codec;
1125 if (feed_codec->codec_id != codec->codec_id ||
1126 feed_codec->sample_rate != codec->sample_rate ||
1127 feed_codec->width != codec->width ||
1128 feed_codec->height != codec->height)
1131 /* Potential stream */
1133 /* We want the fastest stream less than bit_rate, or the slowest
1134 * faster than bit_rate
1137 if (feed_codec->bit_rate <= bit_rate) {
1138 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1139 best_bitrate = feed_codec->bit_rate;
1143 if (feed_codec->bit_rate < best_bitrate) {
1144 best_bitrate = feed_codec->bit_rate;
1153 static int modify_current_stream(HTTPContext *c, char *rates)
1156 FFStream *req = c->stream;
1157 int action_required = 0;
1159 /* Not much we can do for a feed */
1163 for (i = 0; i < req->nb_streams; i++) {
1164 AVCodecContext *codec = req->streams[i]->codec;
1168 c->switch_feed_streams[i] = req->feed_streams[i];
1171 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1174 /* Wants off or slow */
1175 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1177 /* This doesn't work well when it turns off the only stream! */
1178 c->switch_feed_streams[i] = -2;
1179 c->feed_streams[i] = -2;
1184 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1185 action_required = 1;
1188 return action_required;
1192 static void do_switch_stream(HTTPContext *c, int i)
1194 if (c->switch_feed_streams[i] >= 0) {
1196 c->feed_streams[i] = c->switch_feed_streams[i];
1199 /* Now update the stream */
1201 c->switch_feed_streams[i] = -1;
1204 /* XXX: factorize in utils.c ? */
1205 /* XXX: take care with different space meaning */
1206 static void skip_spaces(const char **pp)
1210 while (*p == ' ' || *p == '\t')
1215 static void get_word(char *buf, int buf_size, const char **pp)
1223 while (!isspace(*p) && *p != '\0') {
1224 if ((q - buf) < buf_size - 1)
1233 static void get_arg(char *buf, int buf_size, const char **pp)
1240 while (isspace(*p)) p++;
1243 if (*p == '\"' || *p == '\'')
1255 if ((q - buf) < buf_size - 1)
1260 if (quote && *p == quote)
1265 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1266 const char *p, const char *filename, int line_num)
1272 get_arg(arg, sizeof(arg), &p);
1273 if (strcasecmp(arg, "allow") == 0)
1274 acl.action = IP_ALLOW;
1275 else if (strcasecmp(arg, "deny") == 0)
1276 acl.action = IP_DENY;
1278 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1279 filename, line_num, arg);
1283 get_arg(arg, sizeof(arg), &p);
1285 if (resolve_host(&acl.first, arg) != 0) {
1286 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1287 filename, line_num, arg);
1290 acl.last = acl.first;
1292 get_arg(arg, sizeof(arg), &p);
1295 if (resolve_host(&acl.last, arg) != 0) {
1296 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1297 filename, line_num, arg);
1303 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1304 IPAddressACL **naclp = 0;
1310 naclp = &stream->acl;
1316 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1317 filename, line_num);
1323 naclp = &(*naclp)->next;
1331 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1336 IPAddressACL *acl = NULL;
1340 f = fopen(stream->dynamic_acl, "r");
1342 perror(stream->dynamic_acl);
1346 acl = av_mallocz(sizeof(IPAddressACL));
1350 if (fgets(line, sizeof(line), f) == NULL)
1356 if (*p == '\0' || *p == '#')
1358 get_arg(cmd, sizeof(cmd), &p);
1360 if (!strcasecmp(cmd, "ACL"))
1361 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1368 static void free_acl_list(IPAddressACL *in_acl)
1370 IPAddressACL *pacl,*pacl2;
1380 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1382 enum IPAddressAction last_action = IP_DENY;
1384 struct in_addr *src = &c->from_addr.sin_addr;
1385 unsigned long src_addr = src->s_addr;
1387 for (acl = in_acl; acl; acl = acl->next) {
1388 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1389 return (acl->action == IP_ALLOW) ? 1 : 0;
1390 last_action = acl->action;
1393 /* Nothing matched, so return not the last action */
1394 return (last_action == IP_DENY) ? 1 : 0;
1397 static int validate_acl(FFStream *stream, HTTPContext *c)
1403 /* if stream->acl is null validate_acl_list will return 1 */
1404 ret = validate_acl_list(stream->acl, c);
1406 if (stream->dynamic_acl[0]) {
1407 acl = parse_dynamic_acl(stream, c);
1409 ret = validate_acl_list(acl, c);
1417 /* compute the real filename of a file by matching it without its
1418 extensions to all the stream filenames */
1419 static void compute_real_filename(char *filename, int max_size)
1426 /* compute filename by matching without the file extensions */
1427 av_strlcpy(file1, filename, sizeof(file1));
1428 p = strrchr(file1, '.');
1431 for(stream = first_stream; stream != NULL; stream = stream->next) {
1432 av_strlcpy(file2, stream->filename, sizeof(file2));
1433 p = strrchr(file2, '.');
1436 if (!strcmp(file1, file2)) {
1437 av_strlcpy(filename, stream->filename, max_size);
1452 /* parse http request and prepare header */
1453 static int http_parse_request(HTTPContext *c)
1456 enum RedirType redir_type;
1458 char info[1024], filename[1024];
1462 const char *mime_type;
1466 char *useragent = 0;
1469 get_word(cmd, sizeof(cmd), (const char **)&p);
1470 av_strlcpy(c->method, cmd, sizeof(c->method));
1472 if (!strcmp(cmd, "GET"))
1474 else if (!strcmp(cmd, "POST"))
1479 get_word(url, sizeof(url), (const char **)&p);
1480 av_strlcpy(c->url, url, sizeof(c->url));
1482 get_word(protocol, sizeof(protocol), (const char **)&p);
1483 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1486 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1489 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1491 /* find the filename and the optional info string in the request */
1492 p = strchr(url, '?');
1494 av_strlcpy(info, p, sizeof(info));
1499 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1501 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1502 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1504 if (*useragent && *useragent != '\n' && isspace(*useragent))
1508 p = strchr(p, '\n');
1515 redir_type = REDIR_NONE;
1516 if (av_match_ext(filename, "asx")) {
1517 redir_type = REDIR_ASX;
1518 filename[strlen(filename)-1] = 'f';
1519 } else if (av_match_ext(filename, "asf") &&
1520 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1521 /* if this isn't WMP or lookalike, return the redirector file */
1522 redir_type = REDIR_ASF;
1523 } else if (av_match_ext(filename, "rpm,ram")) {
1524 redir_type = REDIR_RAM;
1525 strcpy(filename + strlen(filename)-2, "m");
1526 } else if (av_match_ext(filename, "rtsp")) {
1527 redir_type = REDIR_RTSP;
1528 compute_real_filename(filename, sizeof(filename) - 1);
1529 } else if (av_match_ext(filename, "sdp")) {
1530 redir_type = REDIR_SDP;
1531 compute_real_filename(filename, sizeof(filename) - 1);
1534 // "redirect" / request to index.html
1535 if (!strlen(filename))
1536 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1538 stream = first_stream;
1539 while (stream != NULL) {
1540 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1542 stream = stream->next;
1544 if (stream == NULL) {
1545 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1546 http_log("File '%s' not found\n", url);
1551 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1552 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1554 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1555 c->http_error = 301;
1557 q += snprintf(q, c->buffer_size,
1558 "HTTP/1.0 301 Moved\r\n"
1560 "Content-type: text/html\r\n"
1562 "<html><head><title>Moved</title></head><body>\r\n"
1563 "You should be <a href=\"%s\">redirected</a>.\r\n"
1564 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1565 /* prepare output buffer */
1566 c->buffer_ptr = c->buffer;
1568 c->state = HTTPSTATE_SEND_HEADER;
1572 /* If this is WMP, get the rate information */
1573 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1574 if (modify_current_stream(c, ratebuf)) {
1575 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1576 if (c->switch_feed_streams[i] >= 0)
1577 do_switch_stream(c, i);
1582 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1583 current_bandwidth += stream->bandwidth;
1585 /* If already streaming this feed, do not let start another feeder. */
1586 if (stream->feed_opened) {
1587 snprintf(msg, sizeof(msg), "This feed is already being received.");
1588 http_log("Feed '%s' already being received\n", stream->feed_filename);
1592 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1593 c->http_error = 503;
1595 q += snprintf(q, c->buffer_size,
1596 "HTTP/1.0 503 Server too busy\r\n"
1597 "Content-type: text/html\r\n"
1599 "<html><head><title>Too busy</title></head><body>\r\n"
1600 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1601 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1602 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1603 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1604 /* prepare output buffer */
1605 c->buffer_ptr = c->buffer;
1607 c->state = HTTPSTATE_SEND_HEADER;
1611 if (redir_type != REDIR_NONE) {
1614 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1615 if (strncasecmp(p, "Host:", 5) == 0) {
1619 p = strchr(p, '\n');
1630 while (isspace(*hostinfo))
1633 eoh = strchr(hostinfo, '\n');
1635 if (eoh[-1] == '\r')
1638 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1639 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1640 hostbuf[eoh - hostinfo] = 0;
1642 c->http_error = 200;
1644 switch(redir_type) {
1646 q += snprintf(q, c->buffer_size,
1647 "HTTP/1.0 200 ASX Follows\r\n"
1648 "Content-type: video/x-ms-asf\r\n"
1650 "<ASX Version=\"3\">\r\n"
1651 //"<!-- Autogenerated by ffserver -->\r\n"
1652 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1653 "</ASX>\r\n", hostbuf, filename, info);
1656 q += snprintf(q, c->buffer_size,
1657 "HTTP/1.0 200 RAM Follows\r\n"
1658 "Content-type: audio/x-pn-realaudio\r\n"
1660 "# Autogenerated by ffserver\r\n"
1661 "http://%s/%s%s\r\n", hostbuf, filename, info);
1664 q += snprintf(q, c->buffer_size,
1665 "HTTP/1.0 200 ASF Redirect follows\r\n"
1666 "Content-type: video/x-ms-asf\r\n"
1669 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1673 char hostname[256], *p;
1674 /* extract only hostname */
1675 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1676 p = strrchr(hostname, ':');
1679 q += snprintf(q, c->buffer_size,
1680 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1681 /* XXX: incorrect mime type ? */
1682 "Content-type: application/x-rtsp\r\n"
1684 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1690 int sdp_data_size, len;
1691 struct sockaddr_in my_addr;
1693 q += snprintf(q, c->buffer_size,
1694 "HTTP/1.0 200 OK\r\n"
1695 "Content-type: application/sdp\r\n"
1698 len = sizeof(my_addr);
1699 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1701 /* XXX: should use a dynamic buffer */
1702 sdp_data_size = prepare_sdp_description(stream,
1705 if (sdp_data_size > 0) {
1706 memcpy(q, sdp_data, sdp_data_size);
1718 /* prepare output buffer */
1719 c->buffer_ptr = c->buffer;
1721 c->state = HTTPSTATE_SEND_HEADER;
1727 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1731 stream->conns_served++;
1733 /* XXX: add there authenticate and IP match */
1736 /* if post, it means a feed is being sent */
1737 if (!stream->is_feed) {
1738 /* However it might be a status report from WMP! Let us log the
1739 * data as it might come in handy one day. */
1743 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1744 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1748 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1749 client_id = strtol(p + 18, 0, 10);
1750 p = strchr(p, '\n');
1758 char *eol = strchr(logline, '\n');
1763 if (eol[-1] == '\r')
1765 http_log("%.*s\n", (int) (eol - logline), logline);
1766 c->suppress_log = 1;
1771 http_log("\nGot request:\n%s\n", c->buffer);
1774 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1777 /* Now we have to find the client_id */
1778 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1779 if (wmpc->wmp_client_id == client_id)
1783 if (wmpc && modify_current_stream(wmpc, ratebuf))
1784 wmpc->switch_pending = 1;
1787 snprintf(msg, sizeof(msg), "POST command not handled");
1791 if (http_start_receive_data(c) < 0) {
1792 snprintf(msg, sizeof(msg), "could not open feed");
1796 c->state = HTTPSTATE_RECEIVE_DATA;
1801 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1802 http_log("\nGot request:\n%s\n", c->buffer);
1805 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1808 /* open input stream */
1809 if (open_input_stream(c, info) < 0) {
1810 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1814 /* prepare http header */
1816 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1817 mime_type = c->stream->fmt->mime_type;
1819 mime_type = "application/x-octet-stream";
1820 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1822 /* for asf, we need extra headers */
1823 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1824 /* Need to allocate a client id */
1826 c->wmp_client_id = av_lfg_get(&random_state);
1828 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);
1830 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1831 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1833 /* prepare output buffer */
1835 c->buffer_ptr = c->buffer;
1837 c->state = HTTPSTATE_SEND_HEADER;
1840 c->http_error = 404;
1842 q += snprintf(q, c->buffer_size,
1843 "HTTP/1.0 404 Not Found\r\n"
1844 "Content-type: text/html\r\n"
1847 "<head><title>404 Not Found</title></head>\n"
1850 /* prepare output buffer */
1851 c->buffer_ptr = c->buffer;
1853 c->state = HTTPSTATE_SEND_HEADER;
1857 c->http_error = 200; /* horrible : we use this value to avoid
1858 going to the send data state */
1859 c->state = HTTPSTATE_SEND_HEADER;
1863 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1865 static const char *suffix = " kMGTP";
1868 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1870 url_fprintf(pb, "%"PRId64"%c", count, *s);
1873 static void compute_status(HTTPContext *c)
1882 if (url_open_dyn_buf(&pb) < 0) {
1883 /* XXX: return an error ? */
1884 c->buffer_ptr = c->buffer;
1885 c->buffer_end = c->buffer;
1889 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1890 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1891 url_fprintf(pb, "Pragma: no-cache\r\n");
1892 url_fprintf(pb, "\r\n");
1894 url_fprintf(pb, "<html><head><title>%s Status</title>\n", program_name);
1895 if (c->stream->feed_filename[0])
1896 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1897 url_fprintf(pb, "</head>\n<body>");
1898 url_fprintf(pb, "<h1>%s Status</h1>\n", program_name);
1900 url_fprintf(pb, "<h2>Available Streams</h2>\n");
1901 url_fprintf(pb, "<table cellspacing=0 cellpadding=4>\n");
1902 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");
1903 stream = first_stream;
1904 while (stream != NULL) {
1905 char sfilename[1024];
1908 if (stream->feed != stream) {
1909 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1910 eosf = sfilename + strlen(sfilename);
1911 if (eosf - sfilename >= 4) {
1912 if (strcmp(eosf - 4, ".asf") == 0)
1913 strcpy(eosf - 4, ".asx");
1914 else if (strcmp(eosf - 3, ".rm") == 0)
1915 strcpy(eosf - 3, ".ram");
1916 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1917 /* generate a sample RTSP director if
1918 unicast. Generate an SDP redirector if
1920 eosf = strrchr(sfilename, '.');
1922 eosf = sfilename + strlen(sfilename);
1923 if (stream->is_multicast)
1924 strcpy(eosf, ".sdp");
1926 strcpy(eosf, ".rtsp");
1930 url_fprintf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1931 sfilename, stream->filename);
1932 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1933 stream->conns_served);
1934 fmt_bytecount(pb, stream->bytes_served);
1935 switch(stream->stream_type) {
1936 case STREAM_TYPE_LIVE: {
1937 int audio_bit_rate = 0;
1938 int video_bit_rate = 0;
1939 const char *audio_codec_name = "";
1940 const char *video_codec_name = "";
1941 const char *audio_codec_name_extra = "";
1942 const char *video_codec_name_extra = "";
1944 for(i=0;i<stream->nb_streams;i++) {
1945 AVStream *st = stream->streams[i];
1946 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1947 switch(st->codec->codec_type) {
1948 case AVMEDIA_TYPE_AUDIO:
1949 audio_bit_rate += st->codec->bit_rate;
1951 if (*audio_codec_name)
1952 audio_codec_name_extra = "...";
1953 audio_codec_name = codec->name;
1956 case AVMEDIA_TYPE_VIDEO:
1957 video_bit_rate += st->codec->bit_rate;
1959 if (*video_codec_name)
1960 video_codec_name_extra = "...";
1961 video_codec_name = codec->name;
1964 case AVMEDIA_TYPE_DATA:
1965 video_bit_rate += st->codec->bit_rate;
1971 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",
1974 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1975 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1977 url_fprintf(pb, "<td>%s", stream->feed->filename);
1979 url_fprintf(pb, "<td>%s", stream->feed_filename);
1980 url_fprintf(pb, "\n");
1984 url_fprintf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1988 stream = stream->next;
1990 url_fprintf(pb, "</table>\n");
1992 stream = first_stream;
1993 while (stream != NULL) {
1994 if (stream->feed == stream) {
1995 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1997 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1999 #if defined(linux) && !defined(CONFIG_NOCUTILS)
2004 /* This is somewhat linux specific I guess */
2005 snprintf(ps_cmd, sizeof(ps_cmd),
2006 "ps -o \"%%cpu,cputime\" --no-headers %d",
2009 pid_stat = popen(ps_cmd, "r");
2014 if (fscanf(pid_stat, "%10s %64s", cpuperc,
2016 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2024 url_fprintf(pb, "<p>");
2026 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");
2028 for (i = 0; i < stream->nb_streams; i++) {
2029 AVStream *st = stream->streams[i];
2030 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2031 const char *type = "unknown";
2032 char parameters[64];
2036 switch(st->codec->codec_type) {
2037 case AVMEDIA_TYPE_AUDIO:
2039 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2041 case AVMEDIA_TYPE_VIDEO:
2043 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2044 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2049 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2050 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2052 url_fprintf(pb, "</table>\n");
2055 stream = stream->next;
2058 /* connection status */
2059 url_fprintf(pb, "<h2>Connection Status</h2>\n");
2061 url_fprintf(pb, "Number of connections: %d / %d<br>\n",
2062 nb_connections, nb_max_connections);
2064 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2065 current_bandwidth, max_bandwidth);
2067 url_fprintf(pb, "<table>\n");
2068 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");
2069 c1 = first_http_ctx;
2071 while (c1 != NULL) {
2077 for (j = 0; j < c1->stream->nb_streams; j++) {
2078 if (!c1->stream->feed)
2079 bitrate += c1->stream->streams[j]->codec->bit_rate;
2080 else if (c1->feed_streams[j] >= 0)
2081 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2086 p = inet_ntoa(c1->from_addr.sin_addr);
2087 url_fprintf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2089 c1->stream ? c1->stream->filename : "",
2090 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2093 http_state[c1->state]);
2094 fmt_bytecount(pb, bitrate);
2095 url_fprintf(pb, "<td align=right>");
2096 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2097 url_fprintf(pb, "<td align=right>");
2098 fmt_bytecount(pb, c1->data_count);
2099 url_fprintf(pb, "\n");
2102 url_fprintf(pb, "</table>\n");
2107 url_fprintf(pb, "<hr size=1 noshade>Generated at %s", p);
2108 url_fprintf(pb, "</body>\n</html>\n");
2110 len = url_close_dyn_buf(pb, &c->pb_buffer);
2111 c->buffer_ptr = c->pb_buffer;
2112 c->buffer_end = c->pb_buffer + len;
2115 /* check if the parser needs to be opened for stream i */
2116 static void open_parser(AVFormatContext *s, int i)
2118 AVStream *st = s->streams[i];
2121 if (!st->codec->codec) {
2122 codec = avcodec_find_decoder(st->codec->codec_id);
2123 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
2124 st->codec->parse_only = 1;
2125 if (avcodec_open(st->codec, codec) < 0)
2126 st->codec->parse_only = 0;
2131 static int open_input_stream(HTTPContext *c, const char *info)
2134 char input_filename[1024];
2136 int buf_size, i, ret;
2139 /* find file name */
2140 if (c->stream->feed) {
2141 strcpy(input_filename, c->stream->feed->feed_filename);
2142 buf_size = FFM_PACKET_SIZE;
2143 /* compute position (absolute time) */
2144 if (find_info_tag(buf, sizeof(buf), "date", info)) {
2145 stream_pos = parse_date(buf, 0);
2146 if (stream_pos == INT64_MIN)
2148 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
2149 int prebuffer = strtol(buf, 0, 10);
2150 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2152 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2154 strcpy(input_filename, c->stream->feed_filename);
2156 /* compute position (relative time) */
2157 if (find_info_tag(buf, sizeof(buf), "date", info)) {
2158 stream_pos = parse_date(buf, 1);
2159 if (stream_pos == INT64_MIN)
2164 if (input_filename[0] == '\0')
2168 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
2169 buf_size, c->stream->ap_in)) < 0) {
2170 http_log("could not open %s: %d\n", input_filename, ret);
2173 s->flags |= AVFMT_FLAG_GENPTS;
2175 if (strcmp(s->iformat->name, "ffm") && av_find_stream_info(c->fmt_in) < 0) {
2176 http_log("Could not find stream info '%s'\n", input_filename);
2177 av_close_input_file(s);
2181 /* open each parser */
2182 for(i=0;i<s->nb_streams;i++)
2185 /* choose stream as clock source (we favorize video stream if
2186 present) for packet sending */
2187 c->pts_stream_index = 0;
2188 for(i=0;i<c->stream->nb_streams;i++) {
2189 if (c->pts_stream_index == 0 &&
2190 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2191 c->pts_stream_index = i;
2196 if (c->fmt_in->iformat->read_seek)
2197 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2199 /* set the start time (needed for maxtime and RTP packet timing) */
2200 c->start_time = cur_time;
2201 c->first_pts = AV_NOPTS_VALUE;
2205 /* return the server clock (in us) */
2206 static int64_t get_server_clock(HTTPContext *c)
2208 /* compute current pts value from system time */
2209 return (cur_time - c->start_time) * 1000;
2212 /* return the estimated time at which the current packet must be sent
2214 static int64_t get_packet_send_clock(HTTPContext *c)
2216 int bytes_left, bytes_sent, frame_bytes;
2218 frame_bytes = c->cur_frame_bytes;
2219 if (frame_bytes <= 0)
2222 bytes_left = c->buffer_end - c->buffer_ptr;
2223 bytes_sent = frame_bytes - bytes_left;
2224 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2229 static int http_prepare_data(HTTPContext *c)
2232 AVFormatContext *ctx;
2234 av_freep(&c->pb_buffer);
2236 case HTTPSTATE_SEND_DATA_HEADER:
2237 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2238 av_metadata_set2(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2239 av_metadata_set2(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2240 av_metadata_set2(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2241 av_metadata_set2(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2243 for(i=0;i<c->stream->nb_streams;i++) {
2246 st = av_mallocz(sizeof(AVStream));
2247 c->fmt_ctx.streams[i] = st;
2248 /* if file or feed, then just take streams from FFStream struct */
2249 if (!c->stream->feed ||
2250 c->stream->feed == c->stream)
2251 src = c->stream->streams[i];
2253 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2257 st->codec->frame_number = 0; /* XXX: should be done in
2258 AVStream, not in codec */
2260 /* set output format parameters */
2261 c->fmt_ctx.oformat = c->stream->fmt;
2262 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2264 c->got_key_frame = 0;
2266 /* prepare header and save header data in a stream */
2267 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2268 /* XXX: potential leak */
2271 c->fmt_ctx.pb->is_streamed = 1;
2274 * HACK to avoid mpeg ps muxer to spit many underflow errors
2275 * Default value from FFmpeg
2276 * Try to set it use configuration option
2278 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2279 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2281 av_set_parameters(&c->fmt_ctx, NULL);
2282 if (av_write_header(&c->fmt_ctx) < 0) {
2283 http_log("Error writing output header\n");
2286 av_metadata_free(&c->fmt_ctx.metadata);
2288 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2289 c->buffer_ptr = c->pb_buffer;
2290 c->buffer_end = c->pb_buffer + len;
2292 c->state = HTTPSTATE_SEND_DATA;
2293 c->last_packet_sent = 0;
2295 case HTTPSTATE_SEND_DATA:
2296 /* find a new packet */
2297 /* read a packet from the input stream */
2298 if (c->stream->feed)
2299 ffm_set_write_index(c->fmt_in,
2300 c->stream->feed->feed_write_index,
2301 c->stream->feed->feed_size);
2303 if (c->stream->max_time &&
2304 c->stream->max_time + c->start_time - cur_time < 0)
2305 /* We have timed out */
2306 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2310 ret = av_read_frame(c->fmt_in, &pkt);
2312 if (c->stream->feed) {
2313 /* if coming from feed, it means we reached the end of the
2314 ffm file, so must wait for more data */
2315 c->state = HTTPSTATE_WAIT_FEED;
2316 return 1; /* state changed */
2317 } else if (ret == AVERROR(EAGAIN)) {
2318 /* input not ready, come back later */
2321 if (c->stream->loop) {
2322 av_close_input_file(c->fmt_in);
2324 if (open_input_stream(c, "") < 0)
2329 /* must send trailer now because eof or error */
2330 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2334 int source_index = pkt.stream_index;
2335 /* update first pts if needed */
2336 if (c->first_pts == AV_NOPTS_VALUE) {
2337 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2338 c->start_time = cur_time;
2340 /* send it to the appropriate stream */
2341 if (c->stream->feed) {
2342 /* if coming from a feed, select the right stream */
2343 if (c->switch_pending) {
2344 c->switch_pending = 0;
2345 for(i=0;i<c->stream->nb_streams;i++) {
2346 if (c->switch_feed_streams[i] == pkt.stream_index)
2347 if (pkt.flags & AV_PKT_FLAG_KEY)
2348 do_switch_stream(c, i);
2349 if (c->switch_feed_streams[i] >= 0)
2350 c->switch_pending = 1;
2353 for(i=0;i<c->stream->nb_streams;i++) {
2354 if (c->stream->feed_streams[i] == pkt.stream_index) {
2355 AVStream *st = c->fmt_in->streams[source_index];
2356 pkt.stream_index = i;
2357 if (pkt.flags & AV_PKT_FLAG_KEY &&
2358 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2359 c->stream->nb_streams == 1))
2360 c->got_key_frame = 1;
2361 if (!c->stream->send_on_key || c->got_key_frame)
2366 AVCodecContext *codec;
2367 AVStream *ist, *ost;
2369 ist = c->fmt_in->streams[source_index];
2370 /* specific handling for RTP: we use several
2371 output stream (one for each RTP
2372 connection). XXX: need more abstract handling */
2373 if (c->is_packetized) {
2374 /* compute send time and duration */
2375 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2376 c->cur_pts -= c->first_pts;
2377 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2378 /* find RTP context */
2379 c->packet_stream_index = pkt.stream_index;
2380 ctx = c->rtp_ctx[c->packet_stream_index];
2382 av_free_packet(&pkt);
2385 codec = ctx->streams[0]->codec;
2386 /* only one stream per RTP connection */
2387 pkt.stream_index = 0;
2391 codec = ctx->streams[pkt.stream_index]->codec;
2394 if (c->is_packetized) {
2395 int max_packet_size;
2396 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2397 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2399 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2400 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2402 ret = url_open_dyn_buf(&ctx->pb);
2405 /* XXX: potential leak */
2408 ost = ctx->streams[pkt.stream_index];
2410 ctx->pb->is_streamed = 1;
2411 if (pkt.dts != AV_NOPTS_VALUE)
2412 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2413 if (pkt.pts != AV_NOPTS_VALUE)
2414 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2415 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2416 if (av_write_frame(ctx, &pkt) < 0) {
2417 http_log("Error writing frame to output\n");
2418 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2421 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2422 c->cur_frame_bytes = len;
2423 c->buffer_ptr = c->pb_buffer;
2424 c->buffer_end = c->pb_buffer + len;
2426 codec->frame_number++;
2428 av_free_packet(&pkt);
2432 av_free_packet(&pkt);
2437 case HTTPSTATE_SEND_DATA_TRAILER:
2438 /* last packet test ? */
2439 if (c->last_packet_sent || c->is_packetized)
2442 /* prepare header */
2443 if (url_open_dyn_buf(&ctx->pb) < 0) {
2444 /* XXX: potential leak */
2447 c->fmt_ctx.pb->is_streamed = 1;
2448 av_write_trailer(ctx);
2449 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2450 c->buffer_ptr = c->pb_buffer;
2451 c->buffer_end = c->pb_buffer + len;
2453 c->last_packet_sent = 1;
2459 /* should convert the format at the same time */
2460 /* send data starting at c->buffer_ptr to the output connection
2461 (either UDP or TCP connection) */
2462 static int http_send_data(HTTPContext *c)
2467 if (c->buffer_ptr >= c->buffer_end) {
2468 ret = http_prepare_data(c);
2472 /* state change requested */
2475 if (c->is_packetized) {
2476 /* RTP data output */
2477 len = c->buffer_end - c->buffer_ptr;
2479 /* fail safe - should never happen */
2481 c->buffer_ptr = c->buffer_end;
2484 len = (c->buffer_ptr[0] << 24) |
2485 (c->buffer_ptr[1] << 16) |
2486 (c->buffer_ptr[2] << 8) |
2488 if (len > (c->buffer_end - c->buffer_ptr))
2490 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2491 /* nothing to send yet: we can wait */
2495 c->data_count += len;
2496 update_datarate(&c->datarate, c->data_count);
2498 c->stream->bytes_served += len;
2500 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2501 /* RTP packets are sent inside the RTSP TCP connection */
2503 int interleaved_index, size;
2505 HTTPContext *rtsp_c;
2508 /* if no RTSP connection left, error */
2511 /* if already sending something, then wait. */
2512 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2514 if (url_open_dyn_buf(&pb) < 0)
2516 interleaved_index = c->packet_stream_index * 2;
2517 /* RTCP packets are sent at odd indexes */
2518 if (c->buffer_ptr[1] == 200)
2519 interleaved_index++;
2520 /* write RTSP TCP header */
2522 header[1] = interleaved_index;
2523 header[2] = len >> 8;
2525 put_buffer(pb, header, 4);
2526 /* write RTP packet data */
2528 put_buffer(pb, c->buffer_ptr, len);
2529 size = url_close_dyn_buf(pb, &c->packet_buffer);
2530 /* prepare asynchronous TCP sending */
2531 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2532 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2533 c->buffer_ptr += len;
2535 /* send everything we can NOW */
2536 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2537 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2539 rtsp_c->packet_buffer_ptr += len;
2540 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2541 /* if we could not send all the data, we will
2542 send it later, so a new state is needed to
2543 "lock" the RTSP TCP connection */
2544 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2547 /* all data has been sent */
2548 av_freep(&c->packet_buffer);
2550 /* send RTP packet directly in UDP */
2552 url_write(c->rtp_handles[c->packet_stream_index],
2553 c->buffer_ptr, len);
2554 c->buffer_ptr += len;
2555 /* here we continue as we can send several packets per 10 ms slot */
2558 /* TCP data output */
2559 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2561 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2562 ff_neterrno() != FF_NETERROR(EINTR))
2563 /* error : close connection */
2568 c->buffer_ptr += len;
2570 c->data_count += len;
2571 update_datarate(&c->datarate, c->data_count);
2573 c->stream->bytes_served += len;
2581 static int http_start_receive_data(HTTPContext *c)
2585 if (c->stream->feed_opened)
2588 /* Don't permit writing to this one */
2589 if (c->stream->readonly)
2593 fd = open(c->stream->feed_filename, O_RDWR);
2595 http_log("Error opening feeder file: %s\n", strerror(errno));
2600 if (c->stream->truncate) {
2601 /* truncate feed file */
2602 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2603 ftruncate(c->feed_fd, FFM_PACKET_SIZE);
2604 http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2606 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2607 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2612 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2613 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2614 lseek(fd, 0, SEEK_SET);
2616 /* init buffer input */
2617 c->buffer_ptr = c->buffer;
2618 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2619 c->stream->feed_opened = 1;
2620 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2624 static int http_receive_data(HTTPContext *c)
2627 int len, loop_run = 0;
2629 while (c->chunked_encoding && !c->chunk_size &&
2630 c->buffer_end > c->buffer_ptr) {
2631 /* read chunk header, if present */
2632 len = recv(c->fd, c->buffer_ptr, 1, 0);
2635 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2636 ff_neterrno() != FF_NETERROR(EINTR))
2637 /* error : close connection */
2640 } else if (len == 0) {
2641 /* end of connection : close it */
2643 } else if (c->buffer_ptr - c->buffer >= 2 &&
2644 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2645 c->chunk_size = strtol(c->buffer, 0, 16);
2646 if (c->chunk_size == 0) // end of stream
2648 c->buffer_ptr = c->buffer;
2650 } else if (++loop_run > 10) {
2651 /* no chunk header, abort */
2658 if (c->buffer_end > c->buffer_ptr) {
2659 len = recv(c->fd, c->buffer_ptr,
2660 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2662 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2663 ff_neterrno() != FF_NETERROR(EINTR))
2664 /* error : close connection */
2666 } else if (len == 0)
2667 /* end of connection : close it */
2670 c->chunk_size -= len;
2671 c->buffer_ptr += len;
2672 c->data_count += len;
2673 update_datarate(&c->datarate, c->data_count);
2677 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2678 if (c->buffer[0] != 'f' ||
2679 c->buffer[1] != 'm') {
2680 http_log("Feed stream has become desynchronized -- disconnecting\n");
2685 if (c->buffer_ptr >= c->buffer_end) {
2686 FFStream *feed = c->stream;
2687 /* a packet has been received : write it in the store, except
2689 if (c->data_count > FFM_PACKET_SIZE) {
2691 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2692 /* XXX: use llseek or url_seek */
2693 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2694 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2695 http_log("Error writing to feed file: %s\n", strerror(errno));
2699 feed->feed_write_index += FFM_PACKET_SIZE;
2700 /* update file size */
2701 if (feed->feed_write_index > c->stream->feed_size)
2702 feed->feed_size = feed->feed_write_index;
2704 /* handle wrap around if max file size reached */
2705 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2706 feed->feed_write_index = FFM_PACKET_SIZE;
2709 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2710 http_log("Error writing index to feed file: %s\n", strerror(errno));
2714 /* wake up any waiting connections */
2715 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2716 if (c1->state == HTTPSTATE_WAIT_FEED &&
2717 c1->stream->feed == c->stream->feed)
2718 c1->state = HTTPSTATE_SEND_DATA;
2721 /* We have a header in our hands that contains useful data */
2722 AVFormatContext *s = NULL;
2724 AVInputFormat *fmt_in;
2727 /* use feed output format name to find corresponding input format */
2728 fmt_in = av_find_input_format(feed->fmt->name);
2732 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2733 pb->is_streamed = 1;
2735 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2740 /* Now we have the actual streams */
2741 if (s->nb_streams != feed->nb_streams) {
2742 av_close_input_stream(s);
2744 http_log("Feed '%s' stream number does not match registered feed\n",
2745 c->stream->feed_filename);
2749 for (i = 0; i < s->nb_streams; i++) {
2750 AVStream *fst = feed->streams[i];
2751 AVStream *st = s->streams[i];
2752 avcodec_copy_context(fst->codec, st->codec);
2755 av_close_input_stream(s);
2758 c->buffer_ptr = c->buffer;
2763 c->stream->feed_opened = 0;
2765 /* wake up any waiting connections to stop waiting for feed */
2766 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2767 if (c1->state == HTTPSTATE_WAIT_FEED &&
2768 c1->stream->feed == c->stream->feed)
2769 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2774 /********************************************************************/
2777 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2784 switch(error_number) {
2785 case RTSP_STATUS_OK:
2788 case RTSP_STATUS_METHOD:
2789 str = "Method Not Allowed";
2791 case RTSP_STATUS_BANDWIDTH:
2792 str = "Not Enough Bandwidth";
2794 case RTSP_STATUS_SESSION:
2795 str = "Session Not Found";
2797 case RTSP_STATUS_STATE:
2798 str = "Method Not Valid in This State";
2800 case RTSP_STATUS_AGGREGATE:
2801 str = "Aggregate operation not allowed";
2803 case RTSP_STATUS_ONLY_AGGREGATE:
2804 str = "Only aggregate operation allowed";
2806 case RTSP_STATUS_TRANSPORT:
2807 str = "Unsupported transport";
2809 case RTSP_STATUS_INTERNAL:
2810 str = "Internal Server Error";
2812 case RTSP_STATUS_SERVICE:
2813 str = "Service Unavailable";
2815 case RTSP_STATUS_VERSION:
2816 str = "RTSP Version not supported";
2819 str = "Unknown Error";
2823 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2824 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2826 /* output GMT time */
2829 strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2830 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2833 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2835 rtsp_reply_header(c, error_number);
2836 url_fprintf(c->pb, "\r\n");
2839 static int rtsp_parse_request(HTTPContext *c)
2841 const char *p, *p1, *p2;
2847 RTSPMessageHeader header1, *header = &header1;
2849 c->buffer_ptr[0] = '\0';
2852 get_word(cmd, sizeof(cmd), &p);
2853 get_word(url, sizeof(url), &p);
2854 get_word(protocol, sizeof(protocol), &p);
2856 av_strlcpy(c->method, cmd, sizeof(c->method));
2857 av_strlcpy(c->url, url, sizeof(c->url));
2858 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2860 if (url_open_dyn_buf(&c->pb) < 0) {
2861 /* XXX: cannot do more */
2862 c->pb = NULL; /* safety */
2866 /* check version name */
2867 if (strcmp(protocol, "RTSP/1.0") != 0) {
2868 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2872 /* parse each header line */
2873 memset(header, 0, sizeof(*header));
2874 /* skip to next line */
2875 while (*p != '\n' && *p != '\0')
2879 while (*p != '\0') {
2880 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2884 if (p2 > p && p2[-1] == '\r')
2886 /* skip empty line */
2890 if (len > sizeof(line) - 1)
2891 len = sizeof(line) - 1;
2892 memcpy(line, p, len);
2894 ff_rtsp_parse_line(header, line, NULL);
2898 /* handle sequence number */
2899 c->seq = header->seq;
2901 if (!strcmp(cmd, "DESCRIBE"))
2902 rtsp_cmd_describe(c, url);
2903 else if (!strcmp(cmd, "OPTIONS"))
2904 rtsp_cmd_options(c, url);
2905 else if (!strcmp(cmd, "SETUP"))
2906 rtsp_cmd_setup(c, url, header);
2907 else if (!strcmp(cmd, "PLAY"))
2908 rtsp_cmd_play(c, url, header);
2909 else if (!strcmp(cmd, "PAUSE"))
2910 rtsp_cmd_pause(c, url, header);
2911 else if (!strcmp(cmd, "TEARDOWN"))
2912 rtsp_cmd_teardown(c, url, header);
2914 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2917 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2918 c->pb = NULL; /* safety */
2920 /* XXX: cannot do more */
2923 c->buffer_ptr = c->pb_buffer;
2924 c->buffer_end = c->pb_buffer + len;
2925 c->state = RTSPSTATE_SEND_REPLY;
2929 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2930 struct in_addr my_ip)
2932 AVFormatContext *avc;
2933 AVStream *avs = NULL;
2936 avc = avformat_alloc_context();
2940 av_metadata_set2(&avc->metadata, "title",
2941 stream->title[0] ? stream->title : "No Title", 0);
2942 avc->nb_streams = stream->nb_streams;
2943 if (stream->is_multicast) {
2944 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2945 inet_ntoa(stream->multicast_ip),
2946 stream->multicast_port, stream->multicast_ttl);
2948 snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2951 #if !FF_API_MAX_STREAMS
2952 if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2953 !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2956 if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2957 !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2960 for(i = 0; i < stream->nb_streams; i++) {
2961 avc->streams[i] = &avs[i];
2962 avc->streams[i]->codec = stream->streams[i]->codec;
2964 *pbuffer = av_mallocz(2048);
2965 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2968 #if !FF_API_MAX_STREAMS
2969 av_free(avc->streams);
2971 av_metadata_free(&avc->metadata);
2975 return strlen(*pbuffer);
2978 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2980 // rtsp_reply_header(c, RTSP_STATUS_OK);
2981 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2982 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2983 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2984 url_fprintf(c->pb, "\r\n");
2987 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2993 int content_length, len;
2994 struct sockaddr_in my_addr;
2996 /* find which url is asked */
2997 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3002 for(stream = first_stream; stream != NULL; stream = stream->next) {
3003 if (!stream->is_feed &&
3004 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
3005 !strcmp(path, stream->filename)) {
3009 /* no stream found */
3010 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3014 /* prepare the media description in sdp format */
3016 /* get the host IP */
3017 len = sizeof(my_addr);
3018 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
3019 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3020 if (content_length < 0) {
3021 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3024 rtsp_reply_header(c, RTSP_STATUS_OK);
3025 url_fprintf(c->pb, "Content-Base: %s/\r\n", url);
3026 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
3027 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
3028 url_fprintf(c->pb, "\r\n");
3029 put_buffer(c->pb, content, content_length);
3033 static HTTPContext *find_rtp_session(const char *session_id)
3037 if (session_id[0] == '\0')
3040 for(c = first_http_ctx; c != NULL; c = c->next) {
3041 if (!strcmp(c->session_id, session_id))
3047 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3049 RTSPTransportField *th;
3052 for(i=0;i<h->nb_transports;i++) {
3053 th = &h->transports[i];
3054 if (th->lower_transport == lower_transport)
3060 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3061 RTSPMessageHeader *h)
3064 int stream_index, rtp_port, rtcp_port;
3069 RTSPTransportField *th;
3070 struct sockaddr_in dest_addr;
3071 RTSPActionServerSetup setup;
3073 /* find which url is asked */
3074 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3079 /* now check each stream */
3080 for(stream = first_stream; stream != NULL; stream = stream->next) {
3081 if (!stream->is_feed &&
3082 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3083 /* accept aggregate filenames only if single stream */
3084 if (!strcmp(path, stream->filename)) {
3085 if (stream->nb_streams != 1) {
3086 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3093 for(stream_index = 0; stream_index < stream->nb_streams;
3095 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3096 stream->filename, stream_index);
3097 if (!strcmp(path, buf))
3102 /* no stream found */
3103 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3107 /* generate session id if needed */
3108 if (h->session_id[0] == '\0')
3109 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3110 av_lfg_get(&random_state), av_lfg_get(&random_state));
3112 /* find rtp session, and create it if none found */
3113 rtp_c = find_rtp_session(h->session_id);
3115 /* always prefer UDP */
3116 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3118 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3120 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3125 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3126 th->lower_transport);
3128 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3132 /* open input stream */
3133 if (open_input_stream(rtp_c, "") < 0) {
3134 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3139 /* test if stream is OK (test needed because several SETUP needs
3140 to be done for a given file) */
3141 if (rtp_c->stream != stream) {
3142 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3146 /* test if stream is already set up */
3147 if (rtp_c->rtp_ctx[stream_index]) {
3148 rtsp_reply_error(c, RTSP_STATUS_STATE);
3152 /* check transport */
3153 th = find_transport(h, rtp_c->rtp_protocol);
3154 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3155 th->client_port_min <= 0)) {
3156 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3160 /* setup default options */
3161 setup.transport_option[0] = '\0';
3162 dest_addr = rtp_c->from_addr;
3163 dest_addr.sin_port = htons(th->client_port_min);
3166 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3167 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3171 /* now everything is OK, so we can send the connection parameters */
3172 rtsp_reply_header(c, RTSP_STATUS_OK);
3174 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3176 switch(rtp_c->rtp_protocol) {
3177 case RTSP_LOWER_TRANSPORT_UDP:
3178 rtp_port = rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3179 rtcp_port = rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3180 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3181 "client_port=%d-%d;server_port=%d-%d",
3182 th->client_port_min, th->client_port_max,
3183 rtp_port, rtcp_port);
3185 case RTSP_LOWER_TRANSPORT_TCP:
3186 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3187 stream_index * 2, stream_index * 2 + 1);
3192 if (setup.transport_option[0] != '\0')
3193 url_fprintf(c->pb, ";%s", setup.transport_option);
3194 url_fprintf(c->pb, "\r\n");
3197 url_fprintf(c->pb, "\r\n");
3201 /* find an rtp connection by using the session ID. Check consistency
3203 static HTTPContext *find_rtp_session_with_url(const char *url,
3204 const char *session_id)
3212 rtp_c = find_rtp_session(session_id);
3216 /* find which url is asked */
3217 av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3221 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3222 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3223 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3224 rtp_c->stream->filename, s);
3225 if(!strncmp(path, buf, sizeof(buf))) {
3226 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3233 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3237 rtp_c = find_rtp_session_with_url(url, h->session_id);
3239 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3243 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3244 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3245 rtp_c->state != HTTPSTATE_READY) {
3246 rtsp_reply_error(c, RTSP_STATUS_STATE);
3250 rtp_c->state = HTTPSTATE_SEND_DATA;
3252 /* now everything is OK, so we can send the connection parameters */
3253 rtsp_reply_header(c, RTSP_STATUS_OK);
3255 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3256 url_fprintf(c->pb, "\r\n");
3259 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3263 rtp_c = find_rtp_session_with_url(url, h->session_id);
3265 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3269 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3270 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3271 rtsp_reply_error(c, RTSP_STATUS_STATE);
3275 rtp_c->state = HTTPSTATE_READY;
3276 rtp_c->first_pts = AV_NOPTS_VALUE;
3277 /* now everything is OK, so we can send the connection parameters */
3278 rtsp_reply_header(c, RTSP_STATUS_OK);
3280 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3281 url_fprintf(c->pb, "\r\n");
3284 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3287 char session_id[32];
3289 rtp_c = find_rtp_session_with_url(url, h->session_id);
3291 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3295 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3297 /* abort the session */
3298 close_connection(rtp_c);
3300 /* now everything is OK, so we can send the connection parameters */
3301 rtsp_reply_header(c, RTSP_STATUS_OK);
3303 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3304 url_fprintf(c->pb, "\r\n");
3308 /********************************************************************/
3311 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3312 FFStream *stream, const char *session_id,
3313 enum RTSPLowerTransport rtp_protocol)
3315 HTTPContext *c = NULL;
3316 const char *proto_str;
3318 /* XXX: should output a warning page when coming
3319 close to the connection limit */
3320 if (nb_connections >= nb_max_connections)
3323 /* add a new connection */
3324 c = av_mallocz(sizeof(HTTPContext));
3329 c->poll_entry = NULL;
3330 c->from_addr = *from_addr;
3331 c->buffer_size = IOBUFFER_INIT_SIZE;
3332 c->buffer = av_malloc(c->buffer_size);
3337 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3338 c->state = HTTPSTATE_READY;
3339 c->is_packetized = 1;
3340 c->rtp_protocol = rtp_protocol;
3342 /* protocol is shown in statistics */
3343 switch(c->rtp_protocol) {
3344 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3345 proto_str = "MCAST";
3347 case RTSP_LOWER_TRANSPORT_UDP:
3350 case RTSP_LOWER_TRANSPORT_TCP:
3357 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3358 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3360 current_bandwidth += stream->bandwidth;
3362 c->next = first_http_ctx;
3374 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3375 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3377 static int rtp_new_av_stream(HTTPContext *c,
3378 int stream_index, struct sockaddr_in *dest_addr,
3379 HTTPContext *rtsp_c)
3381 AVFormatContext *ctx;
3384 URLContext *h = NULL;
3386 int max_packet_size;
3388 /* now we can open the relevant output stream */
3389 ctx = avformat_alloc_context();
3392 ctx->oformat = av_guess_format("rtp", NULL, NULL);
3394 st = av_mallocz(sizeof(AVStream));
3397 ctx->nb_streams = 1;
3398 ctx->streams[0] = st;
3400 if (!c->stream->feed ||
3401 c->stream->feed == c->stream)
3402 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3405 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3407 st->priv_data = NULL;
3409 /* build destination RTP address */
3410 ipaddr = inet_ntoa(dest_addr->sin_addr);
3412 switch(c->rtp_protocol) {
3413 case RTSP_LOWER_TRANSPORT_UDP:
3414 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3417 /* XXX: also pass as parameter to function ? */
3418 if (c->stream->is_multicast) {
3420 ttl = c->stream->multicast_ttl;
3423 snprintf(ctx->filename, sizeof(ctx->filename),
3424 "rtp://%s:%d?multicast=1&ttl=%d",
3425 ipaddr, ntohs(dest_addr->sin_port), ttl);
3427 snprintf(ctx->filename, sizeof(ctx->filename),
3428 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3431 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3433 c->rtp_handles[stream_index] = h;
3434 max_packet_size = url_get_max_packet_size(h);
3436 case RTSP_LOWER_TRANSPORT_TCP:
3439 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3445 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3446 ipaddr, ntohs(dest_addr->sin_port),
3447 c->stream->filename, stream_index, c->protocol);
3449 /* normally, no packets should be output here, but the packet size may be checked */
3450 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3451 /* XXX: close stream */
3454 av_set_parameters(ctx, NULL);
3455 if (av_write_header(ctx) < 0) {
3462 url_close_dyn_buf(ctx->pb, &dummy_buf);
3465 c->rtp_ctx[stream_index] = ctx;
3469 /********************************************************************/
3470 /* ffserver initialization */
3472 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3476 fst = av_mallocz(sizeof(AVStream));
3480 fst->codec= avcodec_alloc_context();
3481 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3482 if (codec->extradata_size) {
3483 fst->codec->extradata = av_malloc(codec->extradata_size);
3484 memcpy(fst->codec->extradata, codec->extradata,
3485 codec->extradata_size);
3488 /* live streams must use the actual feed's codec since it may be
3489 * updated later to carry extradata needed by the streams.
3493 fst->priv_data = av_mallocz(sizeof(FeedData));
3494 fst->index = stream->nb_streams;
3495 av_set_pts_info(fst, 33, 1, 90000);
3496 stream->streams[stream->nb_streams++] = fst;
3500 /* return the stream number in the feed */
3501 static int add_av_stream(FFStream *feed, AVStream *st)
3504 AVCodecContext *av, *av1;
3508 for(i=0;i<feed->nb_streams;i++) {
3509 st = feed->streams[i];
3511 if (av1->codec_id == av->codec_id &&
3512 av1->codec_type == av->codec_type &&
3513 av1->bit_rate == av->bit_rate) {
3515 switch(av->codec_type) {
3516 case AVMEDIA_TYPE_AUDIO:
3517 if (av1->channels == av->channels &&
3518 av1->sample_rate == av->sample_rate)
3521 case AVMEDIA_TYPE_VIDEO:
3522 if (av1->width == av->width &&
3523 av1->height == av->height &&
3524 av1->time_base.den == av->time_base.den &&
3525 av1->time_base.num == av->time_base.num &&
3526 av1->gop_size == av->gop_size)
3535 fst = add_av_stream1(feed, av, 0);
3538 return feed->nb_streams - 1;
3543 static void remove_stream(FFStream *stream)
3547 while (*ps != NULL) {
3555 /* specific mpeg4 handling : we extract the raw parameters */
3556 static void extract_mpeg4_header(AVFormatContext *infile)
3558 int mpeg4_count, i, size;
3564 for(i=0;i<infile->nb_streams;i++) {
3565 st = infile->streams[i];
3566 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3567 st->codec->extradata_size == 0) {
3574 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3575 while (mpeg4_count > 0) {
3576 if (av_read_packet(infile, &pkt) < 0)
3578 st = infile->streams[pkt.stream_index];
3579 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3580 st->codec->extradata_size == 0) {
3581 av_freep(&st->codec->extradata);
3582 /* fill extradata with the header */
3583 /* XXX: we make hard suppositions here ! */
3585 while (p < pkt.data + pkt.size - 4) {
3586 /* stop when vop header is found */
3587 if (p[0] == 0x00 && p[1] == 0x00 &&
3588 p[2] == 0x01 && p[3] == 0xb6) {
3589 size = p - pkt.data;
3590 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3591 st->codec->extradata = av_malloc(size);
3592 st->codec->extradata_size = size;
3593 memcpy(st->codec->extradata, pkt.data, size);
3600 av_free_packet(&pkt);
3604 /* compute the needed AVStream for each file */
3605 static void build_file_streams(void)
3607 FFStream *stream, *stream_next;
3608 AVFormatContext *infile;
3611 /* gather all streams */
3612 for(stream = first_stream; stream != NULL; stream = stream_next) {
3613 stream_next = stream->next;
3614 if (stream->stream_type == STREAM_TYPE_LIVE &&
3616 /* the stream comes from a file */
3617 /* try to open the file */
3619 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3620 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3621 /* specific case : if transport stream output to RTP,
3622 we use a raw transport stream reader */
3623 stream->ap_in->mpeg2ts_raw = 1;
3624 stream->ap_in->mpeg2ts_compute_pcr = 1;
3627 http_log("Opening file '%s'\n", stream->feed_filename);
3628 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3629 stream->ifmt, 0, stream->ap_in)) < 0) {
3630 http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3631 /* remove stream (no need to spend more time on it) */
3633 remove_stream(stream);
3635 /* find all the AVStreams inside and reference them in
3637 if (av_find_stream_info(infile) < 0) {
3638 http_log("Could not find codec parameters from '%s'\n",
3639 stream->feed_filename);
3640 av_close_input_file(infile);
3643 extract_mpeg4_header(infile);
3645 for(i=0;i<infile->nb_streams;i++)
3646 add_av_stream1(stream, infile->streams[i]->codec, 1);
3648 av_close_input_file(infile);
3654 /* compute the needed AVStream for each feed */
3655 static void build_feed_streams(void)
3657 FFStream *stream, *feed;
3660 /* gather all streams */
3661 for(stream = first_stream; stream != NULL; stream = stream->next) {
3662 feed = stream->feed;
3664 if (!stream->is_feed) {
3665 /* we handle a stream coming from a feed */
3666 for(i=0;i<stream->nb_streams;i++)
3667 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3672 /* gather all streams */
3673 for(stream = first_stream; stream != NULL; stream = stream->next) {
3674 feed = stream->feed;
3676 if (stream->is_feed) {
3677 for(i=0;i<stream->nb_streams;i++)
3678 stream->feed_streams[i] = i;
3683 /* create feed files if needed */
3684 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3687 if (url_exist(feed->feed_filename)) {
3688 /* See if it matches */
3692 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3693 /* Now see if it matches */
3694 if (s->nb_streams == feed->nb_streams) {
3696 for(i=0;i<s->nb_streams;i++) {
3698 sf = feed->streams[i];
3701 if (sf->index != ss->index ||
3703 http_log("Index & Id do not match for stream %d (%s)\n",
3704 i, feed->feed_filename);
3707 AVCodecContext *ccf, *ccs;
3711 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3713 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3714 http_log("Codecs do not match for stream %d\n", i);
3716 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3717 http_log("Codec bitrates do not match for stream %d\n", i);
3719 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3720 if (CHECK_CODEC(time_base.den) ||
3721 CHECK_CODEC(time_base.num) ||
3722 CHECK_CODEC(width) ||
3723 CHECK_CODEC(height)) {
3724 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3727 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3728 if (CHECK_CODEC(sample_rate) ||
3729 CHECK_CODEC(channels) ||
3730 CHECK_CODEC(frame_size)) {
3731 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3735 http_log("Unknown codec type\n");
3743 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3744 feed->feed_filename, s->nb_streams, feed->nb_streams);
3746 av_close_input_file(s);
3748 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3749 feed->feed_filename);
3752 if (feed->readonly) {
3753 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3754 feed->feed_filename);
3757 unlink(feed->feed_filename);
3760 if (!url_exist(feed->feed_filename)) {
3761 AVFormatContext s1 = {0}, *s = &s1;
3763 if (feed->readonly) {
3764 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3765 feed->feed_filename);
3769 /* only write the header of the ffm file */
3770 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3771 http_log("Could not open output feed file '%s'\n",
3772 feed->feed_filename);
3775 s->oformat = feed->fmt;
3776 s->nb_streams = feed->nb_streams;
3777 for(i=0;i<s->nb_streams;i++) {
3779 st = feed->streams[i];
3782 av_set_parameters(s, NULL);
3783 if (av_write_header(s) < 0) {
3784 http_log("Container doesn't supports the required parameters\n");
3787 /* XXX: need better api */
3788 av_freep(&s->priv_data);
3791 /* get feed size and write index */
3792 fd = open(feed->feed_filename, O_RDONLY);
3794 http_log("Could not open output feed file '%s'\n",
3795 feed->feed_filename);
3799 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3800 feed->feed_size = lseek(fd, 0, SEEK_END);
3801 /* ensure that we do not wrap before the end of file */
3802 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3803 feed->feed_max_size = feed->feed_size;
3809 /* compute the bandwidth used by each stream */
3810 static void compute_bandwidth(void)
3816 for(stream = first_stream; stream != NULL; stream = stream->next) {
3818 for(i=0;i<stream->nb_streams;i++) {
3819 AVStream *st = stream->streams[i];
3820 switch(st->codec->codec_type) {
3821 case AVMEDIA_TYPE_AUDIO:
3822 case AVMEDIA_TYPE_VIDEO:
3823 bandwidth += st->codec->bit_rate;
3829 stream->bandwidth = (bandwidth + 999) / 1000;
3833 /* add a codec and set the default parameters */
3834 static void add_codec(FFStream *stream, AVCodecContext *av)
3838 /* compute default parameters */
3839 switch(av->codec_type) {
3840 case AVMEDIA_TYPE_AUDIO:
3841 if (av->bit_rate == 0)
3842 av->bit_rate = 64000;
3843 if (av->sample_rate == 0)
3844 av->sample_rate = 22050;
3845 if (av->channels == 0)
3848 case AVMEDIA_TYPE_VIDEO:
3849 if (av->bit_rate == 0)
3850 av->bit_rate = 64000;
3851 if (av->time_base.num == 0){
3852 av->time_base.den = 5;
3853 av->time_base.num = 1;
3855 if (av->width == 0 || av->height == 0) {
3859 /* Bitrate tolerance is less for streaming */
3860 if (av->bit_rate_tolerance == 0)
3861 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3862 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3867 if (av->max_qdiff == 0)
3869 av->qcompress = 0.5;
3872 if (!av->nsse_weight)
3873 av->nsse_weight = 8;
3875 av->frame_skip_cmp = FF_CMP_DCTMAX;
3877 av->me_method = ME_EPZS;
3878 av->rc_buffer_aggressivity = 1.0;
3881 av->rc_eq = "tex^qComp";
3882 if (!av->i_quant_factor)
3883 av->i_quant_factor = -0.8;
3884 if (!av->b_quant_factor)
3885 av->b_quant_factor = 1.25;
3886 if (!av->b_quant_offset)
3887 av->b_quant_offset = 1.25;
3888 if (!av->rc_max_rate)
3889 av->rc_max_rate = av->bit_rate * 2;
3891 if (av->rc_max_rate && !av->rc_buffer_size) {
3892 av->rc_buffer_size = av->rc_max_rate;
3901 st = av_mallocz(sizeof(AVStream));
3904 st->codec = avcodec_alloc_context();
3905 stream->streams[stream->nb_streams++] = st;
3906 memcpy(st->codec, av, sizeof(AVCodecContext));
3909 static enum CodecID opt_audio_codec(const char *arg)
3911 AVCodec *p= avcodec_find_encoder_by_name(arg);
3913 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3914 return CODEC_ID_NONE;
3919 static enum CodecID opt_video_codec(const char *arg)
3921 AVCodec *p= avcodec_find_encoder_by_name(arg);
3923 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3924 return CODEC_ID_NONE;
3929 /* simplistic plugin support */
3932 static void load_module(const char *filename)
3935 void (*init_func)(void);
3936 dll = dlopen(filename, RTLD_NOW);
3938 fprintf(stderr, "Could not load module '%s' - %s\n",
3939 filename, dlerror());
3943 init_func = dlsym(dll, "ffserver_module_init");
3946 "%s: init function 'ffserver_module_init()' not found\n",
3955 static int ffserver_opt_default(const char *opt, const char *arg,
3956 AVCodecContext *avctx, int type)
3959 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
3961 ret = av_set_string3(avctx, opt, arg, 1, NULL);
3965 static int ffserver_opt_preset(const char *arg,
3966 AVCodecContext *avctx, int type,
3967 enum CodecID *audio_id, enum CodecID *video_id)
3970 char filename[1000], tmp[1000], tmp2[1000], line[1000];
3972 const char *base[3]= { getenv("FFMPEG_DATADIR"),
3977 for(i=0; i<3 && !f; i++){
3980 snprintf(filename, sizeof(filename), "%s%s/%s.ffpreset", base[i], i != 1 ? "" : "/.ffmpeg", arg);
3981 f= fopen(filename, "r");
3983 AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3985 snprintf(filename, sizeof(filename), "%s%s/%s-%s.ffpreset", base[i], i != 1 ? "" : "/.ffmpeg", codec->name, arg);
3986 f= fopen(filename, "r");
3992 fprintf(stderr, "File for preset '%s' not found\n", arg);
3997 int e= fscanf(f, "%999[^\n]\n", line) - 1;
3998 if(line[0] == '#' && !e)
4000 e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
4002 fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
4006 if(!strcmp(tmp, "acodec")){
4007 *audio_id = opt_audio_codec(tmp2);
4008 }else if(!strcmp(tmp, "vcodec")){
4009 *video_id = opt_video_codec(tmp2);
4010 }else if(!strcmp(tmp, "scodec")){
4011 /* opt_subtitle_codec(tmp2); */
4012 }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
4013 fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
4024 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
4025 const char *mime_type)
4027 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
4030 AVOutputFormat *stream_fmt;
4031 char stream_format_name[64];
4033 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
4034 stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4043 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
4047 fprintf(stderr, "%s:%d: ", filename, line_num);
4048 vfprintf(stderr, fmt, vl);
4054 static int parse_ffconfig(const char *filename)
4061 int val, errors, line_num;
4062 FFStream **last_stream, *stream, *redirect;
4063 FFStream **last_feed, *feed, *s;
4064 AVCodecContext audio_enc, video_enc;
4065 enum CodecID audio_id, video_id;
4067 f = fopen(filename, "r");
4075 first_stream = NULL;
4076 last_stream = &first_stream;
4078 last_feed = &first_feed;
4082 audio_id = CODEC_ID_NONE;
4083 video_id = CODEC_ID_NONE;
4085 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4087 if (fgets(line, sizeof(line), f) == NULL)
4093 if (*p == '\0' || *p == '#')
4096 get_arg(cmd, sizeof(cmd), &p);
4098 if (!strcasecmp(cmd, "Port")) {
4099 get_arg(arg, sizeof(arg), &p);
4101 if (val < 1 || val > 65536) {
4102 ERROR("Invalid_port: %s\n", arg);
4104 my_http_addr.sin_port = htons(val);
4105 } else if (!strcasecmp(cmd, "BindAddress")) {
4106 get_arg(arg, sizeof(arg), &p);
4107 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4108 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4110 } else if (!strcasecmp(cmd, "NoDaemon")) {
4111 ffserver_daemon = 0;
4112 } else if (!strcasecmp(cmd, "RTSPPort")) {
4113 get_arg(arg, sizeof(arg), &p);
4115 if (val < 1 || val > 65536) {
4116 ERROR("%s:%d: Invalid port: %s\n", arg);
4118 my_rtsp_addr.sin_port = htons(atoi(arg));
4119 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
4120 get_arg(arg, sizeof(arg), &p);
4121 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4122 ERROR("Invalid host/IP address: %s\n", arg);
4124 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
4125 get_arg(arg, sizeof(arg), &p);
4127 if (val < 1 || val > 65536) {
4128 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4130 nb_max_http_connections = val;
4131 } else if (!strcasecmp(cmd, "MaxClients")) {
4132 get_arg(arg, sizeof(arg), &p);
4134 if (val < 1 || val > nb_max_http_connections) {
4135 ERROR("Invalid MaxClients: %s\n", arg);
4137 nb_max_connections = val;
4139 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
4141 get_arg(arg, sizeof(arg), &p);
4143 if (llval < 10 || llval > 10000000) {
4144 ERROR("Invalid MaxBandwidth: %s\n", arg);
4146 max_bandwidth = llval;
4147 } else if (!strcasecmp(cmd, "CustomLog")) {
4148 if (!ffserver_debug)
4149 get_arg(logfilename, sizeof(logfilename), &p);
4150 } else if (!strcasecmp(cmd, "<Feed")) {
4151 /*********************************************/
4152 /* Feed related options */
4154 if (stream || feed) {
4155 ERROR("Already in a tag\n");
4157 feed = av_mallocz(sizeof(FFStream));
4158 get_arg(feed->filename, sizeof(feed->filename), &p);
4159 q = strrchr(feed->filename, '>');
4163 for (s = first_feed; s; s = s->next) {
4164 if (!strcmp(feed->filename, s->filename)) {
4165 ERROR("Feed '%s' already registered\n", s->filename);
4169 feed->fmt = av_guess_format("ffm", NULL, NULL);
4170 /* defaut feed file */
4171 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4172 "/tmp/%s.ffm", feed->filename);
4173 feed->feed_max_size = 5 * 1024 * 1024;
4175 feed->feed = feed; /* self feeding :-) */
4177 /* add in stream list */
4178 *last_stream = feed;
4179 last_stream = &feed->next;
4180 /* add in feed list */
4182 last_feed = &feed->next_feed;
4184 } else if (!strcasecmp(cmd, "Launch")) {
4188 feed->child_argv = av_mallocz(64 * sizeof(char *));
4190 for (i = 0; i < 62; i++) {
4191 get_arg(arg, sizeof(arg), &p);
4195 feed->child_argv[i] = av_strdup(arg);
4198 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4200 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4202 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4203 inet_ntoa(my_http_addr.sin_addr),
4204 ntohs(my_http_addr.sin_port), feed->filename);
4206 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
4208 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4210 } else if (stream) {
4211 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4213 } else if (!strcasecmp(cmd, "File")) {
4215 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4217 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4218 } else if (!strcasecmp(cmd, "Truncate")) {
4220 get_arg(arg, sizeof(arg), &p);
4221 feed->truncate = strtod(arg, NULL);
4223 } else if (!strcasecmp(cmd, "FileMaxSize")) {
4228 get_arg(arg, sizeof(arg), &p);
4230 fsize = strtod(p1, &p1);
4231 switch(toupper(*p1)) {
4236 fsize *= 1024 * 1024;
4239 fsize *= 1024 * 1024 * 1024;
4242 feed->feed_max_size = (int64_t)fsize;
4243 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4244 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4247 } else if (!strcasecmp(cmd, "</Feed>")) {
4249 ERROR("No corresponding <Feed> for </Feed>\n");
4252 } else if (!strcasecmp(cmd, "<Stream")) {
4253 /*********************************************/
4254 /* Stream related options */
4256 if (stream || feed) {
4257 ERROR("Already in a tag\n");
4260 stream = av_mallocz(sizeof(FFStream));
4261 get_arg(stream->filename, sizeof(stream->filename), &p);
4262 q = strrchr(stream->filename, '>');
4266 for (s = first_stream; s; s = s->next) {
4267 if (!strcmp(stream->filename, s->filename)) {
4268 ERROR("Stream '%s' already registered\n", s->filename);
4272 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4273 avcodec_get_context_defaults2(&video_enc, AVMEDIA_TYPE_VIDEO);
4274 avcodec_get_context_defaults2(&audio_enc, AVMEDIA_TYPE_AUDIO);
4275 audio_id = CODEC_ID_NONE;
4276 video_id = CODEC_ID_NONE;
4278 audio_id = stream->fmt->audio_codec;
4279 video_id = stream->fmt->video_codec;
4282 *last_stream = stream;
4283 last_stream = &stream->next;
4285 } else if (!strcasecmp(cmd, "Feed")) {
4286 get_arg(arg, sizeof(arg), &p);
4291 while (sfeed != NULL) {
4292 if (!strcmp(sfeed->filename, arg))
4294 sfeed = sfeed->next_feed;
4297 ERROR("feed '%s' not defined\n", arg);
4299 stream->feed = sfeed;
4301 } else if (!strcasecmp(cmd, "Format")) {
4302 get_arg(arg, sizeof(arg), &p);
4304 if (!strcmp(arg, "status")) {
4305 stream->stream_type = STREAM_TYPE_STATUS;
4308 stream->stream_type = STREAM_TYPE_LIVE;
4309 /* jpeg cannot be used here, so use single frame jpeg */
4310 if (!strcmp(arg, "jpeg"))
4311 strcpy(arg, "mjpeg");
4312 stream->fmt = ffserver_guess_format(arg, NULL, NULL);
4314 ERROR("Unknown Format: %s\n", arg);
4318 audio_id = stream->fmt->audio_codec;
4319 video_id = stream->fmt->video_codec;
4322 } else if (!strcasecmp(cmd, "InputFormat")) {
4323 get_arg(arg, sizeof(arg), &p);
4325 stream->ifmt = av_find_input_format(arg);
4326 if (!stream->ifmt) {
4327 ERROR("Unknown input format: %s\n", arg);
4330 } else if (!strcasecmp(cmd, "FaviconURL")) {
4331 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4332 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4334 ERROR("FaviconURL only permitted for status streams\n");
4336 } else if (!strcasecmp(cmd, "Author")) {
4338 get_arg(stream->author, sizeof(stream->author), &p);
4339 } else if (!strcasecmp(cmd, "Comment")) {
4341 get_arg(stream->comment, sizeof(stream->comment), &p);
4342 } else if (!strcasecmp(cmd, "Copyright")) {
4344 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4345 } else if (!strcasecmp(cmd, "Title")) {
4347 get_arg(stream->title, sizeof(stream->title), &p);
4348 } else if (!strcasecmp(cmd, "Preroll")) {
4349 get_arg(arg, sizeof(arg), &p);
4351 stream->prebuffer = atof(arg) * 1000;
4352 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4354 stream->send_on_key = 1;
4355 } else if (!strcasecmp(cmd, "AudioCodec")) {
4356 get_arg(arg, sizeof(arg), &p);
4357 audio_id = opt_audio_codec(arg);
4358 if (audio_id == CODEC_ID_NONE) {
4359 ERROR("Unknown AudioCodec: %s\n", arg);
4361 } else if (!strcasecmp(cmd, "VideoCodec")) {
4362 get_arg(arg, sizeof(arg), &p);
4363 video_id = opt_video_codec(arg);
4364 if (video_id == CODEC_ID_NONE) {
4365 ERROR("Unknown VideoCodec: %s\n", arg);
4367 } else if (!strcasecmp(cmd, "MaxTime")) {
4368 get_arg(arg, sizeof(arg), &p);
4370 stream->max_time = atof(arg) * 1000;
4371 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4372 get_arg(arg, sizeof(arg), &p);
4374 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4375 } else if (!strcasecmp(cmd, "AudioChannels")) {
4376 get_arg(arg, sizeof(arg), &p);
4378 audio_enc.channels = atoi(arg);
4379 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4380 get_arg(arg, sizeof(arg), &p);
4382 audio_enc.sample_rate = atoi(arg);
4383 } else if (!strcasecmp(cmd, "AudioQuality")) {
4384 get_arg(arg, sizeof(arg), &p);
4386 // audio_enc.quality = atof(arg) * 1000;
4388 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4390 int minrate, maxrate;
4392 get_arg(arg, sizeof(arg), &p);
4394 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4395 video_enc.rc_min_rate = minrate * 1000;
4396 video_enc.rc_max_rate = maxrate * 1000;
4398 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4401 } else if (!strcasecmp(cmd, "Debug")) {
4403 get_arg(arg, sizeof(arg), &p);
4404 video_enc.debug = strtol(arg,0,0);
4406 } else if (!strcasecmp(cmd, "Strict")) {
4408 get_arg(arg, sizeof(arg), &p);
4409 video_enc.strict_std_compliance = atoi(arg);
4411 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4413 get_arg(arg, sizeof(arg), &p);
4414 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4416 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4418 get_arg(arg, sizeof(arg), &p);
4419 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4421 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4422 get_arg(arg, sizeof(arg), &p);
4424 video_enc.bit_rate = atoi(arg) * 1000;
4426 } else if (!strcasecmp(cmd, "VideoSize")) {
4427 get_arg(arg, sizeof(arg), &p);
4429 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4430 if ((video_enc.width % 16) != 0 ||
4431 (video_enc.height % 16) != 0) {
4432 ERROR("Image size must be a multiple of 16\n");
4435 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4436 get_arg(arg, sizeof(arg), &p);
4438 AVRational frame_rate;
4439 if (av_parse_video_rate(&frame_rate, arg) < 0) {
4440 ERROR("Incorrect frame rate: %s\n", arg);
4442 video_enc.time_base.num = frame_rate.den;
4443 video_enc.time_base.den = frame_rate.num;
4446 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4447 get_arg(arg, sizeof(arg), &p);
4449 video_enc.gop_size = atoi(arg);
4450 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4452 video_enc.gop_size = 1;
4453 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4455 video_enc.mb_decision = FF_MB_DECISION_BITS;
4456 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4458 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4459 video_enc.flags |= CODEC_FLAG_4MV;
4461 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4462 !strcasecmp(cmd, "AVOptionAudio")) {
4464 AVCodecContext *avctx;
4466 get_arg(arg, sizeof(arg), &p);
4467 get_arg(arg2, sizeof(arg2), &p);
4468 if (!strcasecmp(cmd, "AVOptionVideo")) {
4470 type = AV_OPT_FLAG_VIDEO_PARAM;
4473 type = AV_OPT_FLAG_AUDIO_PARAM;
4475 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4476 ERROR("AVOption error: %s %s\n", arg, arg2);
4478 } else if (!strcasecmp(cmd, "AVPresetVideo") ||
4479 !strcasecmp(cmd, "AVPresetAudio")) {
4480 AVCodecContext *avctx;
4482 get_arg(arg, sizeof(arg), &p);
4483 if (!strcasecmp(cmd, "AVPresetVideo")) {
4485 video_enc.codec_id = video_id;
4486 type = AV_OPT_FLAG_VIDEO_PARAM;
4489 audio_enc.codec_id = audio_id;
4490 type = AV_OPT_FLAG_AUDIO_PARAM;
4492 if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4493 ERROR("AVPreset error: %s\n", arg);
4495 } else if (!strcasecmp(cmd, "VideoTag")) {
4496 get_arg(arg, sizeof(arg), &p);
4497 if ((strlen(arg) == 4) && stream)
4498 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4499 } else if (!strcasecmp(cmd, "BitExact")) {
4501 video_enc.flags |= CODEC_FLAG_BITEXACT;
4502 } else if (!strcasecmp(cmd, "DctFastint")) {
4504 video_enc.dct_algo = FF_DCT_FASTINT;
4505 } else if (!strcasecmp(cmd, "IdctSimple")) {
4507 video_enc.idct_algo = FF_IDCT_SIMPLE;
4508 } else if (!strcasecmp(cmd, "Qscale")) {
4509 get_arg(arg, sizeof(arg), &p);
4511 video_enc.flags |= CODEC_FLAG_QSCALE;
4512 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4514 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4515 get_arg(arg, sizeof(arg), &p);
4517 video_enc.max_qdiff = atoi(arg);
4518 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4519 ERROR("VideoQDiff out of range\n");
4522 } else if (!strcasecmp(cmd, "VideoQMax")) {
4523 get_arg(arg, sizeof(arg), &p);
4525 video_enc.qmax = atoi(arg);
4526 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4527 ERROR("VideoQMax out of range\n");
4530 } else if (!strcasecmp(cmd, "VideoQMin")) {
4531 get_arg(arg, sizeof(arg), &p);
4533 video_enc.qmin = atoi(arg);
4534 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4535 ERROR("VideoQMin out of range\n");
4538 } else if (!strcasecmp(cmd, "LumaElim")) {
4539 get_arg(arg, sizeof(arg), &p);
4541 video_enc.luma_elim_threshold = atoi(arg);
4542 } else if (!strcasecmp(cmd, "ChromaElim")) {
4543 get_arg(arg, sizeof(arg), &p);
4545 video_enc.chroma_elim_threshold = atoi(arg);
4546 } else if (!strcasecmp(cmd, "LumiMask")) {
4547 get_arg(arg, sizeof(arg), &p);
4549 video_enc.lumi_masking = atof(arg);
4550 } else if (!strcasecmp(cmd, "DarkMask")) {
4551 get_arg(arg, sizeof(arg), &p);
4553 video_enc.dark_masking = atof(arg);
4554 } else if (!strcasecmp(cmd, "NoVideo")) {
4555 video_id = CODEC_ID_NONE;
4556 } else if (!strcasecmp(cmd, "NoAudio")) {
4557 audio_id = CODEC_ID_NONE;
4558 } else if (!strcasecmp(cmd, "ACL")) {
4559 parse_acl_row(stream, feed, NULL, p, filename, line_num);
4560 } else if (!strcasecmp(cmd, "DynamicACL")) {
4562 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4564 } else if (!strcasecmp(cmd, "RTSPOption")) {
4565 get_arg(arg, sizeof(arg), &p);
4567 av_freep(&stream->rtsp_option);
4568 stream->rtsp_option = av_strdup(arg);
4570 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4571 get_arg(arg, sizeof(arg), &p);
4573 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4574 ERROR("Invalid host/IP address: %s\n", arg);
4576 stream->is_multicast = 1;
4577 stream->loop = 1; /* default is looping */
4579 } else if (!strcasecmp(cmd, "MulticastPort")) {
4580 get_arg(arg, sizeof(arg), &p);
4582 stream->multicast_port = atoi(arg);
4583 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4584 get_arg(arg, sizeof(arg), &p);
4586 stream->multicast_ttl = atoi(arg);
4587 } else if (!strcasecmp(cmd, "NoLoop")) {
4590 } else if (!strcasecmp(cmd, "</Stream>")) {
4592 ERROR("No corresponding <Stream> for </Stream>\n");
4594 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4595 if (audio_id != CODEC_ID_NONE) {
4596 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4597 audio_enc.codec_id = audio_id;
4598 add_codec(stream, &audio_enc);
4600 if (video_id != CODEC_ID_NONE) {
4601 video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4602 video_enc.codec_id = video_id;
4603 add_codec(stream, &video_enc);
4608 } else if (!strcasecmp(cmd, "<Redirect")) {
4609 /*********************************************/
4611 if (stream || feed || redirect) {
4612 ERROR("Already in a tag\n");
4614 redirect = av_mallocz(sizeof(FFStream));
4615 *last_stream = redirect;
4616 last_stream = &redirect->next;
4618 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4619 q = strrchr(redirect->filename, '>');
4622 redirect->stream_type = STREAM_TYPE_REDIRECT;
4624 } else if (!strcasecmp(cmd, "URL")) {
4626 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4627 } else if (!strcasecmp(cmd, "</Redirect>")) {
4629 ERROR("No corresponding <Redirect> for </Redirect>\n");
4631 if (!redirect->feed_filename[0]) {
4632 ERROR("No URL found for <Redirect>\n");
4636 } else if (!strcasecmp(cmd, "LoadModule")) {
4637 get_arg(arg, sizeof(arg), &p);
4641 ERROR("Module support not compiled into this version: '%s'\n", arg);
4644 ERROR("Incorrect keyword: '%s'\n", cmd);
4656 static void handle_child_exit(int sig)
4661 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4664 for (feed = first_feed; feed; feed = feed->next) {
4665 if (feed->pid == pid) {
4666 int uptime = time(0) - feed->pid_start;
4669 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4672 /* Turn off any more restarts */
4673 feed->child_argv = 0;
4678 need_to_start_children = 1;
4681 static void opt_debug(void)
4684 ffserver_daemon = 0;
4685 logfilename[0] = '-';
4688 static void show_help(void)
4690 printf("usage: ffserver [options]\n"
4691 "Hyper fast multi format Audio/Video streaming server\n");
4693 show_help_options(options, "Main options:\n", 0, 0);
4696 static const OptionDef options[] = {
4697 #include "cmdutils_common_opts.h"
4698 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4699 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4700 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4704 int main(int argc, char **argv)
4706 struct sigaction sigact;
4712 my_program_name = argv[0];
4713 my_program_dir = getcwd(0, 0);
4714 ffserver_daemon = 1;
4716 parse_options(argc, argv, options, NULL);
4718 unsetenv("http_proxy"); /* Kill the http_proxy */
4720 av_lfg_init(&random_state, av_get_random_seed());
4722 memset(&sigact, 0, sizeof(sigact));
4723 sigact.sa_handler = handle_child_exit;
4724 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4725 sigaction(SIGCHLD, &sigact, 0);
4727 if (parse_ffconfig(config_filename) < 0) {
4728 fprintf(stderr, "Incorrect config file - exiting.\n");
4732 /* open log file if needed */
4733 if (logfilename[0] != '\0') {
4734 if (!strcmp(logfilename, "-"))
4737 logfile = fopen(logfilename, "a");
4738 av_log_set_callback(http_av_log);
4741 build_file_streams();
4743 build_feed_streams();
4745 compute_bandwidth();
4747 /* put the process in background and detach it from its TTY */
4748 if (ffserver_daemon) {
4755 } else if (pid > 0) {
4762 open("/dev/null", O_RDWR);
4763 if (strcmp(logfilename, "-") != 0) {
4773 signal(SIGPIPE, SIG_IGN);
4775 if (ffserver_daemon)
4778 if (http_server() < 0) {
4779 http_log("Could not start server\n");