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 /* avformat.h defines LIBAVFORMAT_BUILD, include it before all the other libav* headers which use it */
32 #include "libavformat/avformat.h"
33 #include "libavformat/network.h"
34 #include "libavformat/os_support.h"
35 #include "libavformat/rtpdec.h"
36 #include "libavformat/rtsp.h"
37 #include "libavutil/avstring.h"
38 #include "libavutil/random.h"
39 #include "libavutil/intreadwrite.h"
40 #include "libavcodec/opt.h"
44 #include <sys/ioctl.h>
50 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
62 const char program_name[] = "FFserver";
63 const int program_birth_year = 2000;
65 static const OptionDef options[];
68 HTTPSTATE_WAIT_REQUEST,
69 HTTPSTATE_SEND_HEADER,
70 HTTPSTATE_SEND_DATA_HEADER,
71 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
72 HTTPSTATE_SEND_DATA_TRAILER,
73 HTTPSTATE_RECEIVE_DATA,
74 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
77 RTSPSTATE_WAIT_REQUEST,
79 RTSPSTATE_SEND_PACKET,
82 static const char *http_state[] = {
98 #define IOBUFFER_INIT_SIZE 8192
100 /* timeouts are in ms */
101 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
102 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
104 #define SYNC_TIMEOUT (10 * 1000)
106 typedef struct RTSPActionServerSetup {
108 char transport_option[512];
109 } RTSPActionServerSetup;
112 int64_t count1, count2;
113 int64_t time1, time2;
116 /* context associated with one connection */
117 typedef struct HTTPContext {
118 enum HTTPState state;
119 int fd; /* socket file descriptor */
120 struct sockaddr_in from_addr; /* origin */
121 struct pollfd *poll_entry; /* used when polling */
123 uint8_t *buffer_ptr, *buffer_end;
126 struct HTTPContext *next;
127 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
131 /* input format handling */
132 AVFormatContext *fmt_in;
133 int64_t start_time; /* In milliseconds - this wraps fairly often */
134 int64_t first_pts; /* initial pts value */
135 int64_t cur_pts; /* current pts value from the stream in us */
136 int64_t cur_frame_duration; /* duration of the current frame in us */
137 int cur_frame_bytes; /* output frame size, needed to compute
138 the time at which we send each
140 int pts_stream_index; /* stream we choose as clock reference */
141 int64_t cur_clock; /* current clock reference value in us */
142 /* output format handling */
143 struct FFStream *stream;
144 /* -1 is invalid stream */
145 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
146 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
148 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
149 int last_packet_sent; /* true if last data packet was sent */
151 DataRateData datarate;
158 int is_packetized; /* if true, the stream is packetized */
159 int packet_stream_index; /* current stream for output in state machine */
161 /* RTSP state specific */
162 uint8_t *pb_buffer; /* XXX: use that in all the code */
164 int seq; /* RTSP sequence number */
166 /* RTP state specific */
167 enum RTSPLowerTransport rtp_protocol;
168 char session_id[32]; /* session id */
169 AVFormatContext *rtp_ctx[MAX_STREAMS];
171 /* RTP/UDP specific */
172 URLContext *rtp_handles[MAX_STREAMS];
174 /* RTP/TCP specific */
175 struct HTTPContext *rtsp_c;
176 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
179 /* each generated stream is described here */
183 STREAM_TYPE_REDIRECT,
186 enum IPAddressAction {
191 typedef struct IPAddressACL {
192 struct IPAddressACL *next;
193 enum IPAddressAction action;
194 /* These are in host order */
195 struct in_addr first;
199 /* description of each stream of the ffserver.conf file */
200 typedef struct FFStream {
201 enum StreamType stream_type;
202 char filename[1024]; /* stream filename */
203 struct FFStream *feed; /* feed we are using (can be null if
205 AVFormatParameters *ap_in; /* input parameters */
206 AVInputFormat *ifmt; /* if non NULL, force input format */
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 */
240 int64_t bytes_served;
241 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
242 int64_t feed_write_index; /* current write position in feed (it wraps around) */
243 int64_t feed_size; /* current size of feed */
244 struct FFStream *next_feed;
247 typedef struct FeedData {
248 long long data_count;
249 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
252 static struct sockaddr_in my_http_addr;
253 static struct sockaddr_in my_rtsp_addr;
255 static char logfilename[1024];
256 static HTTPContext *first_http_ctx;
257 static FFStream *first_feed; /* contains only feeds */
258 static FFStream *first_stream; /* contains all streams, including feeds */
260 static void new_connection(int server_fd, int is_rtsp);
261 static void close_connection(HTTPContext *c);
264 static int handle_connection(HTTPContext *c);
265 static int http_parse_request(HTTPContext *c);
266 static int http_send_data(HTTPContext *c);
267 static void compute_status(HTTPContext *c);
268 static int open_input_stream(HTTPContext *c, const char *info);
269 static int http_start_receive_data(HTTPContext *c);
270 static int http_receive_data(HTTPContext *c);
273 static int rtsp_parse_request(HTTPContext *c);
274 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
275 static void rtsp_cmd_options(HTTPContext *c, const char *url);
276 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
277 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
278 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
279 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
282 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
283 struct in_addr my_ip);
286 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
287 FFStream *stream, const char *session_id,
288 enum RTSPLowerTransport rtp_protocol);
289 static int rtp_new_av_stream(HTTPContext *c,
290 int stream_index, struct sockaddr_in *dest_addr,
291 HTTPContext *rtsp_c);
293 static const char *my_program_name;
294 static const char *my_program_dir;
296 static const char *config_filename;
297 static int ffserver_debug;
298 static int ffserver_daemon;
299 static int no_launch;
300 static int need_to_start_children;
302 /* maximum number of simultaneous HTTP connections */
303 static unsigned int nb_max_http_connections = 2000;
304 static unsigned int nb_max_connections = 5;
305 static unsigned int nb_connections;
307 static uint64_t max_bandwidth = 1000;
308 static uint64_t current_bandwidth;
310 static int64_t cur_time; // Making this global saves on passing it around everywhere
312 static AVRandomState random_state;
314 static FILE *logfile = NULL;
316 static char *ctime1(char *buf2)
324 p = buf2 + strlen(p) - 1;
330 static void http_vlog(const char *fmt, va_list vargs)
332 static int print_prefix = 1;
337 fprintf(logfile, "%s ", buf);
339 print_prefix = strstr(fmt, "\n") != NULL;
340 vfprintf(logfile, fmt, vargs);
345 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
348 va_start(vargs, fmt);
349 http_vlog(fmt, vargs);
353 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
355 static int print_prefix = 1;
356 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
357 if (level > av_log_level)
359 if (print_prefix && avc)
360 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
361 print_prefix = strstr(fmt, "\n") != NULL;
362 http_vlog(fmt, vargs);
365 static void log_connection(HTTPContext *c)
370 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
371 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
372 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
375 static void update_datarate(DataRateData *drd, int64_t count)
377 if (!drd->time1 && !drd->count1) {
378 drd->time1 = drd->time2 = cur_time;
379 drd->count1 = drd->count2 = count;
380 } else if (cur_time - drd->time2 > 5000) {
381 drd->time1 = drd->time2;
382 drd->count1 = drd->count2;
383 drd->time2 = cur_time;
388 /* In bytes per second */
389 static int compute_datarate(DataRateData *drd, int64_t count)
391 if (cur_time == drd->time1)
394 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
398 static void start_children(FFStream *feed)
403 for (; feed; feed = feed->next) {
404 if (feed->child_argv && !feed->pid) {
405 feed->pid_start = time(0);
410 http_log("Unable to create children\n");
419 av_strlcpy(pathname, my_program_name, sizeof(pathname));
421 slash = strrchr(pathname, '/');
426 strcpy(slash, "ffmpeg");
428 http_log("Launch commandline: ");
429 http_log("%s ", pathname);
430 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
431 http_log("%s ", feed->child_argv[i]);
434 for (i = 3; i < 256; i++)
437 if (!ffserver_debug) {
438 i = open("/dev/null", O_RDWR);
447 /* This is needed to make relative pathnames work */
448 chdir(my_program_dir);
450 signal(SIGPIPE, SIG_DFL);
452 execvp(pathname, feed->child_argv);
460 /* open a listening socket */
461 static int socket_open_listen(struct sockaddr_in *my_addr)
465 server_fd = socket(AF_INET,SOCK_STREAM,0);
472 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
474 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
476 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
478 closesocket(server_fd);
482 if (listen (server_fd, 5) < 0) {
484 closesocket(server_fd);
487 ff_socket_nonblock(server_fd, 1);
492 /* start all multicast streams */
493 static void start_multicast(void)
498 struct sockaddr_in dest_addr;
499 int default_port, stream_index;
502 for(stream = first_stream; stream != NULL; stream = stream->next) {
503 if (stream->is_multicast) {
504 /* open the RTP connection */
505 snprintf(session_id, sizeof(session_id), "%08x%08x",
506 av_random(&random_state), av_random(&random_state));
508 /* choose a port if none given */
509 if (stream->multicast_port == 0) {
510 stream->multicast_port = default_port;
514 dest_addr.sin_family = AF_INET;
515 dest_addr.sin_addr = stream->multicast_ip;
516 dest_addr.sin_port = htons(stream->multicast_port);
518 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
519 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
523 if (open_input_stream(rtp_c, "") < 0) {
524 http_log("Could not open input stream for stream '%s'\n",
529 /* open each RTP stream */
530 for(stream_index = 0; stream_index < stream->nb_streams;
532 dest_addr.sin_port = htons(stream->multicast_port +
534 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
535 http_log("Could not open output stream '%s/streamid=%d'\n",
536 stream->filename, stream_index);
541 /* change state to send data */
542 rtp_c->state = HTTPSTATE_SEND_DATA;
547 /* main loop of the http server */
548 static int http_server(void)
550 int server_fd = 0, rtsp_server_fd = 0;
551 int ret, delay, delay1;
552 struct pollfd *poll_table, *poll_entry;
553 HTTPContext *c, *c_next;
555 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
556 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
560 if (my_http_addr.sin_port) {
561 server_fd = socket_open_listen(&my_http_addr);
566 if (my_rtsp_addr.sin_port) {
567 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
568 if (rtsp_server_fd < 0)
572 if (!rtsp_server_fd && !server_fd) {
573 http_log("HTTP and RTSP disabled.\n");
577 http_log("FFserver started.\n");
579 start_children(first_feed);
584 poll_entry = poll_table;
586 poll_entry->fd = server_fd;
587 poll_entry->events = POLLIN;
590 if (rtsp_server_fd) {
591 poll_entry->fd = rtsp_server_fd;
592 poll_entry->events = POLLIN;
596 /* wait for events on each HTTP handle */
603 case HTTPSTATE_SEND_HEADER:
604 case RTSPSTATE_SEND_REPLY:
605 case RTSPSTATE_SEND_PACKET:
606 c->poll_entry = poll_entry;
608 poll_entry->events = POLLOUT;
611 case HTTPSTATE_SEND_DATA_HEADER:
612 case HTTPSTATE_SEND_DATA:
613 case HTTPSTATE_SEND_DATA_TRAILER:
614 if (!c->is_packetized) {
615 /* for TCP, we output as much as we can (may need to put a limit) */
616 c->poll_entry = poll_entry;
618 poll_entry->events = POLLOUT;
621 /* when ffserver is doing the timing, we work by
622 looking at which packet need to be sent every
624 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
629 case HTTPSTATE_WAIT_REQUEST:
630 case HTTPSTATE_RECEIVE_DATA:
631 case HTTPSTATE_WAIT_FEED:
632 case RTSPSTATE_WAIT_REQUEST:
633 /* need to catch errors */
634 c->poll_entry = poll_entry;
636 poll_entry->events = POLLIN;/* Maybe this will work */
640 c->poll_entry = NULL;
646 /* wait for an event on one connection. We poll at least every
647 second to handle timeouts */
649 ret = poll(poll_table, poll_entry - poll_table, delay);
650 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
651 ff_neterrno() != FF_NETERROR(EINTR))
655 cur_time = av_gettime() / 1000;
657 if (need_to_start_children) {
658 need_to_start_children = 0;
659 start_children(first_feed);
662 /* now handle the events */
663 for(c = first_http_ctx; c != NULL; c = c_next) {
665 if (handle_connection(c) < 0) {
666 /* close and free the connection */
672 poll_entry = poll_table;
674 /* new HTTP connection request ? */
675 if (poll_entry->revents & POLLIN)
676 new_connection(server_fd, 0);
679 if (rtsp_server_fd) {
680 /* new RTSP connection request ? */
681 if (poll_entry->revents & POLLIN)
682 new_connection(rtsp_server_fd, 1);
687 /* start waiting for a new HTTP/RTSP request */
688 static void start_wait_request(HTTPContext *c, int is_rtsp)
690 c->buffer_ptr = c->buffer;
691 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
694 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
695 c->state = RTSPSTATE_WAIT_REQUEST;
697 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
698 c->state = HTTPSTATE_WAIT_REQUEST;
702 static void new_connection(int server_fd, int is_rtsp)
704 struct sockaddr_in from_addr;
706 HTTPContext *c = NULL;
708 len = sizeof(from_addr);
709 fd = accept(server_fd, (struct sockaddr *)&from_addr,
712 http_log("error during accept %s\n", strerror(errno));
715 ff_socket_nonblock(fd, 1);
717 /* XXX: should output a warning page when coming
718 close to the connection limit */
719 if (nb_connections >= nb_max_connections)
722 /* add a new connection */
723 c = av_mallocz(sizeof(HTTPContext));
728 c->poll_entry = NULL;
729 c->from_addr = from_addr;
730 c->buffer_size = IOBUFFER_INIT_SIZE;
731 c->buffer = av_malloc(c->buffer_size);
735 c->next = first_http_ctx;
739 start_wait_request(c, is_rtsp);
751 static void close_connection(HTTPContext *c)
753 HTTPContext **cp, *c1;
755 AVFormatContext *ctx;
759 /* remove connection from list */
760 cp = &first_http_ctx;
761 while ((*cp) != NULL) {
769 /* remove references, if any (XXX: do it faster) */
770 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
775 /* remove connection associated resources */
779 /* close each frame parser */
780 for(i=0;i<c->fmt_in->nb_streams;i++) {
781 st = c->fmt_in->streams[i];
782 if (st->codec->codec)
783 avcodec_close(st->codec);
785 av_close_input_file(c->fmt_in);
788 /* free RTP output streams if any */
791 nb_streams = c->stream->nb_streams;
793 for(i=0;i<nb_streams;i++) {
796 av_write_trailer(ctx);
799 h = c->rtp_handles[i];
806 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
809 if (url_open_dyn_buf(&ctx->pb) >= 0) {
810 av_write_trailer(ctx);
811 av_freep(&c->pb_buffer);
812 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
817 for(i=0; i<ctx->nb_streams; i++)
818 av_free(ctx->streams[i]);
820 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
821 current_bandwidth -= c->stream->bandwidth;
823 /* signal that there is no feed if we are the feeder socket */
824 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
825 c->stream->feed_opened = 0;
829 av_freep(&c->pb_buffer);
830 av_freep(&c->packet_buffer);
836 static int handle_connection(HTTPContext *c)
841 case HTTPSTATE_WAIT_REQUEST:
842 case RTSPSTATE_WAIT_REQUEST:
844 if ((c->timeout - cur_time) < 0)
846 if (c->poll_entry->revents & (POLLERR | POLLHUP))
849 /* no need to read if no events */
850 if (!(c->poll_entry->revents & POLLIN))
854 len = recv(c->fd, c->buffer_ptr, 1, 0);
856 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
857 ff_neterrno() != FF_NETERROR(EINTR))
859 } else if (len == 0) {
862 /* search for end of request. */
864 c->buffer_ptr += len;
866 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
867 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
868 /* request found : parse it and reply */
869 if (c->state == HTTPSTATE_WAIT_REQUEST) {
870 ret = http_parse_request(c);
872 ret = rtsp_parse_request(c);
876 } else if (ptr >= c->buffer_end) {
877 /* request too long: cannot do anything */
879 } else goto read_loop;
883 case HTTPSTATE_SEND_HEADER:
884 if (c->poll_entry->revents & (POLLERR | POLLHUP))
887 /* no need to write if no events */
888 if (!(c->poll_entry->revents & POLLOUT))
890 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
892 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
893 ff_neterrno() != FF_NETERROR(EINTR)) {
894 /* error : close connection */
895 av_freep(&c->pb_buffer);
899 c->buffer_ptr += len;
901 c->stream->bytes_served += len;
902 c->data_count += len;
903 if (c->buffer_ptr >= c->buffer_end) {
904 av_freep(&c->pb_buffer);
908 /* all the buffer was sent : synchronize to the incoming stream */
909 c->state = HTTPSTATE_SEND_DATA_HEADER;
910 c->buffer_ptr = c->buffer_end = c->buffer;
915 case HTTPSTATE_SEND_DATA:
916 case HTTPSTATE_SEND_DATA_HEADER:
917 case HTTPSTATE_SEND_DATA_TRAILER:
918 /* for packetized output, we consider we can always write (the
919 input streams sets the speed). It may be better to verify
920 that we do not rely too much on the kernel queues */
921 if (!c->is_packetized) {
922 if (c->poll_entry->revents & (POLLERR | POLLHUP))
925 /* no need to read if no events */
926 if (!(c->poll_entry->revents & POLLOUT))
929 if (http_send_data(c) < 0)
931 /* close connection if trailer sent */
932 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
935 case HTTPSTATE_RECEIVE_DATA:
936 /* no need to read if no events */
937 if (c->poll_entry->revents & (POLLERR | POLLHUP))
939 if (!(c->poll_entry->revents & POLLIN))
941 if (http_receive_data(c) < 0)
944 case HTTPSTATE_WAIT_FEED:
945 /* no need to read if no events */
946 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
949 /* nothing to do, we'll be waken up by incoming feed packets */
952 case RTSPSTATE_SEND_REPLY:
953 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
954 av_freep(&c->pb_buffer);
957 /* no need to write if no events */
958 if (!(c->poll_entry->revents & POLLOUT))
960 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
962 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
963 ff_neterrno() != FF_NETERROR(EINTR)) {
964 /* error : close connection */
965 av_freep(&c->pb_buffer);
969 c->buffer_ptr += len;
970 c->data_count += len;
971 if (c->buffer_ptr >= c->buffer_end) {
972 /* all the buffer was sent : wait for a new request */
973 av_freep(&c->pb_buffer);
974 start_wait_request(c, 1);
978 case RTSPSTATE_SEND_PACKET:
979 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
980 av_freep(&c->packet_buffer);
983 /* no need to write if no events */
984 if (!(c->poll_entry->revents & POLLOUT))
986 len = send(c->fd, c->packet_buffer_ptr,
987 c->packet_buffer_end - c->packet_buffer_ptr, 0);
989 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
990 ff_neterrno() != FF_NETERROR(EINTR)) {
991 /* error : close connection */
992 av_freep(&c->packet_buffer);
996 c->packet_buffer_ptr += len;
997 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
998 /* all the buffer was sent : wait for a new request */
999 av_freep(&c->packet_buffer);
1000 c->state = RTSPSTATE_WAIT_REQUEST;
1004 case HTTPSTATE_READY:
1013 static int extract_rates(char *rates, int ratelen, const char *request)
1017 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1018 if (strncasecmp(p, "Pragma:", 7) == 0) {
1019 const char *q = p + 7;
1021 while (*q && *q != '\n' && isspace(*q))
1024 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1030 memset(rates, 0xff, ratelen);
1033 while (*q && *q != '\n' && *q != ':')
1036 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1040 if (stream_no < ratelen && stream_no >= 0)
1041 rates[stream_no] = rate_no;
1043 while (*q && *q != '\n' && !isspace(*q))
1050 p = strchr(p, '\n');
1060 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1063 int best_bitrate = 100000000;
1066 for (i = 0; i < feed->nb_streams; i++) {
1067 AVCodecContext *feed_codec = feed->streams[i]->codec;
1069 if (feed_codec->codec_id != codec->codec_id ||
1070 feed_codec->sample_rate != codec->sample_rate ||
1071 feed_codec->width != codec->width ||
1072 feed_codec->height != codec->height)
1075 /* Potential stream */
1077 /* We want the fastest stream less than bit_rate, or the slowest
1078 * faster than bit_rate
1081 if (feed_codec->bit_rate <= bit_rate) {
1082 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1083 best_bitrate = feed_codec->bit_rate;
1087 if (feed_codec->bit_rate < best_bitrate) {
1088 best_bitrate = feed_codec->bit_rate;
1097 static int modify_current_stream(HTTPContext *c, char *rates)
1100 FFStream *req = c->stream;
1101 int action_required = 0;
1103 /* Not much we can do for a feed */
1107 for (i = 0; i < req->nb_streams; i++) {
1108 AVCodecContext *codec = req->streams[i]->codec;
1112 c->switch_feed_streams[i] = req->feed_streams[i];
1115 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1118 /* Wants off or slow */
1119 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1121 /* This doesn't work well when it turns off the only stream! */
1122 c->switch_feed_streams[i] = -2;
1123 c->feed_streams[i] = -2;
1128 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1129 action_required = 1;
1132 return action_required;
1136 static void do_switch_stream(HTTPContext *c, int i)
1138 if (c->switch_feed_streams[i] >= 0) {
1140 c->feed_streams[i] = c->switch_feed_streams[i];
1143 /* Now update the stream */
1145 c->switch_feed_streams[i] = -1;
1148 /* XXX: factorize in utils.c ? */
1149 /* XXX: take care with different space meaning */
1150 static void skip_spaces(const char **pp)
1154 while (*p == ' ' || *p == '\t')
1159 static void get_word(char *buf, int buf_size, const char **pp)
1167 while (!isspace(*p) && *p != '\0') {
1168 if ((q - buf) < buf_size - 1)
1177 static int validate_acl(FFStream *stream, HTTPContext *c)
1179 enum IPAddressAction last_action = IP_DENY;
1181 struct in_addr *src = &c->from_addr.sin_addr;
1182 unsigned long src_addr = src->s_addr;
1184 for (acl = stream->acl; acl; acl = acl->next) {
1185 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1186 return (acl->action == IP_ALLOW) ? 1 : 0;
1187 last_action = acl->action;
1190 /* Nothing matched, so return not the last action */
1191 return (last_action == IP_DENY) ? 1 : 0;
1194 /* compute the real filename of a file by matching it without its
1195 extensions to all the stream filenames */
1196 static void compute_real_filename(char *filename, int max_size)
1203 /* compute filename by matching without the file extensions */
1204 av_strlcpy(file1, filename, sizeof(file1));
1205 p = strrchr(file1, '.');
1208 for(stream = first_stream; stream != NULL; stream = stream->next) {
1209 av_strlcpy(file2, stream->filename, sizeof(file2));
1210 p = strrchr(file2, '.');
1213 if (!strcmp(file1, file2)) {
1214 av_strlcpy(filename, stream->filename, max_size);
1229 /* parse http request and prepare header */
1230 static int http_parse_request(HTTPContext *c)
1233 enum RedirType redir_type;
1235 char info[1024], filename[1024];
1239 const char *mime_type;
1243 char *useragent = 0;
1246 get_word(cmd, sizeof(cmd), (const char **)&p);
1247 av_strlcpy(c->method, cmd, sizeof(c->method));
1249 if (!strcmp(cmd, "GET"))
1251 else if (!strcmp(cmd, "POST"))
1256 get_word(url, sizeof(url), (const char **)&p);
1257 av_strlcpy(c->url, url, sizeof(c->url));
1259 get_word(protocol, sizeof(protocol), (const char **)&p);
1260 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1263 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1266 http_log("New connection: %s %s\n", cmd, url);
1268 /* find the filename and the optional info string in the request */
1269 p = strchr(url, '?');
1271 av_strlcpy(info, p, sizeof(info));
1276 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1278 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1279 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1281 if (*useragent && *useragent != '\n' && isspace(*useragent))
1285 p = strchr(p, '\n');
1292 redir_type = REDIR_NONE;
1293 if (match_ext(filename, "asx")) {
1294 redir_type = REDIR_ASX;
1295 filename[strlen(filename)-1] = 'f';
1296 } else if (match_ext(filename, "asf") &&
1297 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1298 /* if this isn't WMP or lookalike, return the redirector file */
1299 redir_type = REDIR_ASF;
1300 } else if (match_ext(filename, "rpm,ram")) {
1301 redir_type = REDIR_RAM;
1302 strcpy(filename + strlen(filename)-2, "m");
1303 } else if (match_ext(filename, "rtsp")) {
1304 redir_type = REDIR_RTSP;
1305 compute_real_filename(filename, sizeof(filename) - 1);
1306 } else if (match_ext(filename, "sdp")) {
1307 redir_type = REDIR_SDP;
1308 compute_real_filename(filename, sizeof(filename) - 1);
1311 // "redirect" / request to index.html
1312 if (!strlen(filename))
1313 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1315 stream = first_stream;
1316 while (stream != NULL) {
1317 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1319 stream = stream->next;
1321 if (stream == NULL) {
1322 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1327 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1328 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1330 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1331 c->http_error = 301;
1333 q += snprintf(q, c->buffer_size,
1334 "HTTP/1.0 301 Moved\r\n"
1336 "Content-type: text/html\r\n"
1338 "<html><head><title>Moved</title></head><body>\r\n"
1339 "You should be <a href=\"%s\">redirected</a>.\r\n"
1340 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1341 /* prepare output buffer */
1342 c->buffer_ptr = c->buffer;
1344 c->state = HTTPSTATE_SEND_HEADER;
1348 /* If this is WMP, get the rate information */
1349 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1350 if (modify_current_stream(c, ratebuf)) {
1351 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1352 if (c->switch_feed_streams[i] >= 0)
1353 do_switch_stream(c, i);
1358 /* If already streaming this feed, do not let start another feeder. */
1359 if (stream->feed_opened) {
1360 snprintf(msg, sizeof(msg), "This feed is already being received.");
1361 http_log("feed %s already being received\n", stream->feed_filename);
1365 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1366 current_bandwidth += stream->bandwidth;
1368 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1369 c->http_error = 200;
1371 q += snprintf(q, c->buffer_size,
1372 "HTTP/1.0 200 Server too busy\r\n"
1373 "Content-type: text/html\r\n"
1375 "<html><head><title>Too busy</title></head><body>\r\n"
1376 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1377 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1378 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1379 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1380 /* prepare output buffer */
1381 c->buffer_ptr = c->buffer;
1383 c->state = HTTPSTATE_SEND_HEADER;
1387 if (redir_type != REDIR_NONE) {
1390 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1391 if (strncasecmp(p, "Host:", 5) == 0) {
1395 p = strchr(p, '\n');
1406 while (isspace(*hostinfo))
1409 eoh = strchr(hostinfo, '\n');
1411 if (eoh[-1] == '\r')
1414 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1415 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1416 hostbuf[eoh - hostinfo] = 0;
1418 c->http_error = 200;
1420 switch(redir_type) {
1422 q += snprintf(q, c->buffer_size,
1423 "HTTP/1.0 200 ASX Follows\r\n"
1424 "Content-type: video/x-ms-asf\r\n"
1426 "<ASX Version=\"3\">\r\n"
1427 //"<!-- Autogenerated by ffserver -->\r\n"
1428 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1429 "</ASX>\r\n", hostbuf, filename, info);
1432 q += snprintf(q, c->buffer_size,
1433 "HTTP/1.0 200 RAM Follows\r\n"
1434 "Content-type: audio/x-pn-realaudio\r\n"
1436 "# Autogenerated by ffserver\r\n"
1437 "http://%s/%s%s\r\n", hostbuf, filename, info);
1440 q += snprintf(q, c->buffer_size,
1441 "HTTP/1.0 200 ASF Redirect follows\r\n"
1442 "Content-type: video/x-ms-asf\r\n"
1445 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1449 char hostname[256], *p;
1450 /* extract only hostname */
1451 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1452 p = strrchr(hostname, ':');
1455 q += snprintf(q, c->buffer_size,
1456 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1457 /* XXX: incorrect mime type ? */
1458 "Content-type: application/x-rtsp\r\n"
1460 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1466 int sdp_data_size, len;
1467 struct sockaddr_in my_addr;
1469 q += snprintf(q, c->buffer_size,
1470 "HTTP/1.0 200 OK\r\n"
1471 "Content-type: application/sdp\r\n"
1474 len = sizeof(my_addr);
1475 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1477 /* XXX: should use a dynamic buffer */
1478 sdp_data_size = prepare_sdp_description(stream,
1481 if (sdp_data_size > 0) {
1482 memcpy(q, sdp_data, sdp_data_size);
1494 /* prepare output buffer */
1495 c->buffer_ptr = c->buffer;
1497 c->state = HTTPSTATE_SEND_HEADER;
1503 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1507 stream->conns_served++;
1509 /* XXX: add there authenticate and IP match */
1512 /* if post, it means a feed is being sent */
1513 if (!stream->is_feed) {
1514 /* However it might be a status report from WMP! Let us log the
1515 * data as it might come in handy one day. */
1519 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1520 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1524 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1525 client_id = strtol(p + 18, 0, 10);
1526 p = strchr(p, '\n');
1534 char *eol = strchr(logline, '\n');
1539 if (eol[-1] == '\r')
1541 http_log("%.*s\n", (int) (eol - logline), logline);
1542 c->suppress_log = 1;
1547 http_log("\nGot request:\n%s\n", c->buffer);
1550 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1553 /* Now we have to find the client_id */
1554 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1555 if (wmpc->wmp_client_id == client_id)
1559 if (wmpc && modify_current_stream(wmpc, ratebuf))
1560 wmpc->switch_pending = 1;
1563 snprintf(msg, sizeof(msg), "POST command not handled");
1567 if (http_start_receive_data(c) < 0) {
1568 snprintf(msg, sizeof(msg), "could not open feed");
1572 c->state = HTTPSTATE_RECEIVE_DATA;
1577 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1578 http_log("\nGot request:\n%s\n", c->buffer);
1581 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1584 /* open input stream */
1585 if (open_input_stream(c, info) < 0) {
1586 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1590 /* prepare http header */
1592 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1593 mime_type = c->stream->fmt->mime_type;
1595 mime_type = "application/x-octet-stream";
1596 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1598 /* for asf, we need extra headers */
1599 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1600 /* Need to allocate a client id */
1602 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1604 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);
1606 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1607 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1609 /* prepare output buffer */
1611 c->buffer_ptr = c->buffer;
1613 c->state = HTTPSTATE_SEND_HEADER;
1616 c->http_error = 404;
1618 q += snprintf(q, c->buffer_size,
1619 "HTTP/1.0 404 Not Found\r\n"
1620 "Content-type: text/html\r\n"
1623 "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
1626 /* prepare output buffer */
1627 c->buffer_ptr = c->buffer;
1629 c->state = HTTPSTATE_SEND_HEADER;
1633 c->http_error = 200; /* horrible : we use this value to avoid
1634 going to the send data state */
1635 c->state = HTTPSTATE_SEND_HEADER;
1639 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1641 static const char *suffix = " kMGTP";
1644 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1646 url_fprintf(pb, "%"PRId64"%c", count, *s);
1649 static void compute_status(HTTPContext *c)
1658 if (url_open_dyn_buf(&pb) < 0) {
1659 /* XXX: return an error ? */
1660 c->buffer_ptr = c->buffer;
1661 c->buffer_end = c->buffer;
1665 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1666 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1667 url_fprintf(pb, "Pragma: no-cache\r\n");
1668 url_fprintf(pb, "\r\n");
1670 url_fprintf(pb, "<HTML><HEAD><TITLE>%s Status</TITLE>\n", program_name);
1671 if (c->stream->feed_filename[0])
1672 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1673 url_fprintf(pb, "</HEAD>\n<BODY>");
1674 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1676 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1677 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1678 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");
1679 stream = first_stream;
1680 while (stream != NULL) {
1681 char sfilename[1024];
1684 if (stream->feed != stream) {
1685 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1686 eosf = sfilename + strlen(sfilename);
1687 if (eosf - sfilename >= 4) {
1688 if (strcmp(eosf - 4, ".asf") == 0)
1689 strcpy(eosf - 4, ".asx");
1690 else if (strcmp(eosf - 3, ".rm") == 0)
1691 strcpy(eosf - 3, ".ram");
1692 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1693 /* generate a sample RTSP director if
1694 unicast. Generate an SDP redirector if
1696 eosf = strrchr(sfilename, '.');
1698 eosf = sfilename + strlen(sfilename);
1699 if (stream->is_multicast)
1700 strcpy(eosf, ".sdp");
1702 strcpy(eosf, ".rtsp");
1706 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1707 sfilename, stream->filename);
1708 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1709 stream->conns_served);
1710 fmt_bytecount(pb, stream->bytes_served);
1711 switch(stream->stream_type) {
1712 case STREAM_TYPE_LIVE: {
1713 int audio_bit_rate = 0;
1714 int video_bit_rate = 0;
1715 const char *audio_codec_name = "";
1716 const char *video_codec_name = "";
1717 const char *audio_codec_name_extra = "";
1718 const char *video_codec_name_extra = "";
1720 for(i=0;i<stream->nb_streams;i++) {
1721 AVStream *st = stream->streams[i];
1722 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1723 switch(st->codec->codec_type) {
1724 case CODEC_TYPE_AUDIO:
1725 audio_bit_rate += st->codec->bit_rate;
1727 if (*audio_codec_name)
1728 audio_codec_name_extra = "...";
1729 audio_codec_name = codec->name;
1732 case CODEC_TYPE_VIDEO:
1733 video_bit_rate += st->codec->bit_rate;
1735 if (*video_codec_name)
1736 video_codec_name_extra = "...";
1737 video_codec_name = codec->name;
1740 case CODEC_TYPE_DATA:
1741 video_bit_rate += st->codec->bit_rate;
1747 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",
1750 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1751 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1753 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1755 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1756 url_fprintf(pb, "\n");
1760 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1764 stream = stream->next;
1766 url_fprintf(pb, "</TABLE>\n");
1768 stream = first_stream;
1769 while (stream != NULL) {
1770 if (stream->feed == stream) {
1771 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1773 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1775 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1780 /* This is somewhat linux specific I guess */
1781 snprintf(ps_cmd, sizeof(ps_cmd),
1782 "ps -o \"%%cpu,cputime\" --no-headers %d",
1785 pid_stat = popen(ps_cmd, "r");
1790 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1792 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1800 url_fprintf(pb, "<p>");
1802 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");
1804 for (i = 0; i < stream->nb_streams; i++) {
1805 AVStream *st = stream->streams[i];
1806 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1807 const char *type = "unknown";
1808 char parameters[64];
1812 switch(st->codec->codec_type) {
1813 case CODEC_TYPE_AUDIO:
1815 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1817 case CODEC_TYPE_VIDEO:
1819 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1820 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1825 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1826 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1828 url_fprintf(pb, "</table>\n");
1831 stream = stream->next;
1837 AVCodecContext *enc;
1841 stream = first_feed;
1842 while (stream != NULL) {
1843 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1844 url_fprintf(pb, "<TABLE>\n");
1845 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1846 for(i=0;i<stream->nb_streams;i++) {
1847 AVStream *st = stream->streams[i];
1848 FeedData *fdata = st->priv_data;
1851 avcodec_string(buf, sizeof(buf), enc);
1852 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1853 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1854 avg /= enc->frame_size;
1855 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1856 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1858 url_fprintf(pb, "</TABLE>\n");
1859 stream = stream->next_feed;
1864 /* connection status */
1865 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1867 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1868 nb_connections, nb_max_connections);
1870 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<BR>\n",
1871 current_bandwidth, max_bandwidth);
1873 url_fprintf(pb, "<TABLE>\n");
1874 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");
1875 c1 = first_http_ctx;
1877 while (c1 != NULL) {
1883 for (j = 0; j < c1->stream->nb_streams; j++) {
1884 if (!c1->stream->feed)
1885 bitrate += c1->stream->streams[j]->codec->bit_rate;
1886 else if (c1->feed_streams[j] >= 0)
1887 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1892 p = inet_ntoa(c1->from_addr.sin_addr);
1893 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1895 c1->stream ? c1->stream->filename : "",
1896 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1899 http_state[c1->state]);
1900 fmt_bytecount(pb, bitrate);
1901 url_fprintf(pb, "<td align=right>");
1902 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1903 url_fprintf(pb, "<td align=right>");
1904 fmt_bytecount(pb, c1->data_count);
1905 url_fprintf(pb, "\n");
1908 url_fprintf(pb, "</TABLE>\n");
1913 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1914 url_fprintf(pb, "</BODY>\n</HTML>\n");
1916 len = url_close_dyn_buf(pb, &c->pb_buffer);
1917 c->buffer_ptr = c->pb_buffer;
1918 c->buffer_end = c->pb_buffer + len;
1921 /* check if the parser needs to be opened for stream i */
1922 static void open_parser(AVFormatContext *s, int i)
1924 AVStream *st = s->streams[i];
1927 if (!st->codec->codec) {
1928 codec = avcodec_find_decoder(st->codec->codec_id);
1929 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1930 st->codec->parse_only = 1;
1931 if (avcodec_open(st->codec, codec) < 0)
1932 st->codec->parse_only = 0;
1937 static int open_input_stream(HTTPContext *c, const char *info)
1940 char input_filename[1024];
1942 int buf_size, i, ret;
1945 /* find file name */
1946 if (c->stream->feed) {
1947 strcpy(input_filename, c->stream->feed->feed_filename);
1948 buf_size = FFM_PACKET_SIZE;
1949 /* compute position (absolute time) */
1950 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1951 stream_pos = parse_date(buf, 0);
1952 if (stream_pos == INT64_MIN)
1954 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1955 int prebuffer = strtol(buf, 0, 10);
1956 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1958 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1960 strcpy(input_filename, c->stream->feed_filename);
1962 /* compute position (relative time) */
1963 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1964 stream_pos = parse_date(buf, 1);
1965 if (stream_pos == INT64_MIN)
1970 if (input_filename[0] == '\0')
1974 { time_t when = stream_pos / 1000000;
1975 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1980 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1981 buf_size, c->stream->ap_in)) < 0) {
1982 http_log("could not open %s: %d\n", input_filename, ret);
1985 s->flags |= AVFMT_FLAG_GENPTS;
1987 av_find_stream_info(c->fmt_in);
1989 /* open each parser */
1990 for(i=0;i<s->nb_streams;i++)
1993 /* choose stream as clock source (we favorize video stream if
1994 present) for packet sending */
1995 c->pts_stream_index = 0;
1996 for(i=0;i<c->stream->nb_streams;i++) {
1997 if (c->pts_stream_index == 0 &&
1998 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1999 c->pts_stream_index = i;
2004 if (c->fmt_in->iformat->read_seek)
2005 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2007 /* set the start time (needed for maxtime and RTP packet timing) */
2008 c->start_time = cur_time;
2009 c->first_pts = AV_NOPTS_VALUE;
2013 /* return the server clock (in us) */
2014 static int64_t get_server_clock(HTTPContext *c)
2016 /* compute current pts value from system time */
2017 return (cur_time - c->start_time) * 1000;
2020 /* return the estimated time at which the current packet must be sent
2022 static int64_t get_packet_send_clock(HTTPContext *c)
2024 int bytes_left, bytes_sent, frame_bytes;
2026 frame_bytes = c->cur_frame_bytes;
2027 if (frame_bytes <= 0)
2030 bytes_left = c->buffer_end - c->buffer_ptr;
2031 bytes_sent = frame_bytes - bytes_left;
2032 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2037 static int http_prepare_data(HTTPContext *c)
2040 AVFormatContext *ctx;
2042 av_freep(&c->pb_buffer);
2044 case HTTPSTATE_SEND_DATA_HEADER:
2045 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2046 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2047 sizeof(c->fmt_ctx.author));
2048 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2049 sizeof(c->fmt_ctx.comment));
2050 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2051 sizeof(c->fmt_ctx.copyright));
2052 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2053 sizeof(c->fmt_ctx.title));
2055 for(i=0;i<c->stream->nb_streams;i++) {
2058 st = av_mallocz(sizeof(AVStream));
2059 c->fmt_ctx.streams[i] = st;
2060 /* if file or feed, then just take streams from FFStream struct */
2061 if (!c->stream->feed ||
2062 c->stream->feed == c->stream)
2063 src = c->stream->streams[i];
2065 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2069 st->codec->frame_number = 0; /* XXX: should be done in
2070 AVStream, not in codec */
2072 /* set output format parameters */
2073 c->fmt_ctx.oformat = c->stream->fmt;
2074 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2076 c->got_key_frame = 0;
2078 /* prepare header and save header data in a stream */
2079 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2080 /* XXX: potential leak */
2083 c->fmt_ctx.pb->is_streamed = 1;
2086 * HACK to avoid mpeg ps muxer to spit many underflow errors
2087 * Default value from FFmpeg
2088 * Try to set it use configuration option
2090 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2091 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2093 av_set_parameters(&c->fmt_ctx, NULL);
2094 if (av_write_header(&c->fmt_ctx) < 0) {
2095 http_log("Error writing output header\n");
2099 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2100 c->buffer_ptr = c->pb_buffer;
2101 c->buffer_end = c->pb_buffer + len;
2103 c->state = HTTPSTATE_SEND_DATA;
2104 c->last_packet_sent = 0;
2106 case HTTPSTATE_SEND_DATA:
2107 /* find a new packet */
2108 /* read a packet from the input stream */
2109 if (c->stream->feed)
2110 ffm_set_write_index(c->fmt_in,
2111 c->stream->feed->feed_write_index,
2112 c->stream->feed->feed_size);
2114 if (c->stream->max_time &&
2115 c->stream->max_time + c->start_time - cur_time < 0)
2116 /* We have timed out */
2117 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2121 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2122 if (c->stream->feed && c->stream->feed->feed_opened) {
2123 /* if coming from feed, it means we reached the end of the
2124 ffm file, so must wait for more data */
2125 c->state = HTTPSTATE_WAIT_FEED;
2126 return 1; /* state changed */
2128 if (c->stream->loop) {
2129 av_close_input_file(c->fmt_in);
2131 if (open_input_stream(c, "") < 0)
2136 /* must send trailer now because eof or error */
2137 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2141 int source_index = pkt.stream_index;
2142 /* update first pts if needed */
2143 if (c->first_pts == AV_NOPTS_VALUE) {
2144 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2145 c->start_time = cur_time;
2147 /* send it to the appropriate stream */
2148 if (c->stream->feed) {
2149 /* if coming from a feed, select the right stream */
2150 if (c->switch_pending) {
2151 c->switch_pending = 0;
2152 for(i=0;i<c->stream->nb_streams;i++) {
2153 if (c->switch_feed_streams[i] == pkt.stream_index)
2154 if (pkt.flags & PKT_FLAG_KEY)
2155 do_switch_stream(c, i);
2156 if (c->switch_feed_streams[i] >= 0)
2157 c->switch_pending = 1;
2160 for(i=0;i<c->stream->nb_streams;i++) {
2161 if (c->feed_streams[i] == pkt.stream_index) {
2162 AVStream *st = c->fmt_in->streams[source_index];
2163 pkt.stream_index = i;
2164 if (pkt.flags & PKT_FLAG_KEY &&
2165 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2166 c->stream->nb_streams == 1))
2167 c->got_key_frame = 1;
2168 if (!c->stream->send_on_key || c->got_key_frame)
2173 AVCodecContext *codec;
2174 AVStream *ist, *ost;
2176 ist = c->fmt_in->streams[source_index];
2177 /* specific handling for RTP: we use several
2178 output stream (one for each RTP
2179 connection). XXX: need more abstract handling */
2180 if (c->is_packetized) {
2181 /* compute send time and duration */
2182 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2183 if (ist->start_time != AV_NOPTS_VALUE)
2184 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2185 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2187 printf("index=%d pts=%0.3f duration=%0.6f\n",
2189 (double)c->cur_pts /
2191 (double)c->cur_frame_duration /
2194 /* find RTP context */
2195 c->packet_stream_index = pkt.stream_index;
2196 ctx = c->rtp_ctx[c->packet_stream_index];
2198 av_free_packet(&pkt);
2201 codec = ctx->streams[0]->codec;
2202 /* only one stream per RTP connection */
2203 pkt.stream_index = 0;
2207 codec = ctx->streams[pkt.stream_index]->codec;
2210 if (c->is_packetized) {
2211 int max_packet_size;
2212 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2213 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2215 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2216 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2218 ret = url_open_dyn_buf(&ctx->pb);
2221 /* XXX: potential leak */
2224 ost = ctx->streams[pkt.stream_index];
2226 ctx->pb->is_streamed = 1;
2227 if (pkt.dts != AV_NOPTS_VALUE)
2228 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2229 if (pkt.pts != AV_NOPTS_VALUE)
2230 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2231 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2232 if (av_write_frame(ctx, &pkt) < 0) {
2233 http_log("Error writing frame to output\n");
2234 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2237 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2238 c->cur_frame_bytes = len;
2239 c->buffer_ptr = c->pb_buffer;
2240 c->buffer_end = c->pb_buffer + len;
2242 codec->frame_number++;
2244 av_free_packet(&pkt);
2248 av_free_packet(&pkt);
2253 case HTTPSTATE_SEND_DATA_TRAILER:
2254 /* last packet test ? */
2255 if (c->last_packet_sent || c->is_packetized)
2258 /* prepare header */
2259 if (url_open_dyn_buf(&ctx->pb) < 0) {
2260 /* XXX: potential leak */
2263 c->fmt_ctx.pb->is_streamed = 1;
2264 av_write_trailer(ctx);
2265 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2266 c->buffer_ptr = c->pb_buffer;
2267 c->buffer_end = c->pb_buffer + len;
2269 c->last_packet_sent = 1;
2275 /* should convert the format at the same time */
2276 /* send data starting at c->buffer_ptr to the output connection
2277 (either UDP or TCP connection) */
2278 static int http_send_data(HTTPContext *c)
2283 if (c->buffer_ptr >= c->buffer_end) {
2284 ret = http_prepare_data(c);
2288 /* state change requested */
2291 if (c->is_packetized) {
2292 /* RTP data output */
2293 len = c->buffer_end - c->buffer_ptr;
2295 /* fail safe - should never happen */
2297 c->buffer_ptr = c->buffer_end;
2300 len = (c->buffer_ptr[0] << 24) |
2301 (c->buffer_ptr[1] << 16) |
2302 (c->buffer_ptr[2] << 8) |
2304 if (len > (c->buffer_end - c->buffer_ptr))
2306 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2307 /* nothing to send yet: we can wait */
2311 c->data_count += len;
2312 update_datarate(&c->datarate, c->data_count);
2314 c->stream->bytes_served += len;
2316 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2317 /* RTP packets are sent inside the RTSP TCP connection */
2319 int interleaved_index, size;
2321 HTTPContext *rtsp_c;
2324 /* if no RTSP connection left, error */
2327 /* if already sending something, then wait. */
2328 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2330 if (url_open_dyn_buf(&pb) < 0)
2332 interleaved_index = c->packet_stream_index * 2;
2333 /* RTCP packets are sent at odd indexes */
2334 if (c->buffer_ptr[1] == 200)
2335 interleaved_index++;
2336 /* write RTSP TCP header */
2338 header[1] = interleaved_index;
2339 header[2] = len >> 8;
2341 put_buffer(pb, header, 4);
2342 /* write RTP packet data */
2344 put_buffer(pb, c->buffer_ptr, len);
2345 size = url_close_dyn_buf(pb, &c->packet_buffer);
2346 /* prepare asynchronous TCP sending */
2347 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2348 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2349 c->buffer_ptr += len;
2351 /* send everything we can NOW */
2352 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2353 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2355 rtsp_c->packet_buffer_ptr += len;
2356 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2357 /* if we could not send all the data, we will
2358 send it later, so a new state is needed to
2359 "lock" the RTSP TCP connection */
2360 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2363 /* all data has been sent */
2364 av_freep(&c->packet_buffer);
2366 /* send RTP packet directly in UDP */
2368 url_write(c->rtp_handles[c->packet_stream_index],
2369 c->buffer_ptr, len);
2370 c->buffer_ptr += len;
2371 /* here we continue as we can send several packets per 10 ms slot */
2374 /* TCP data output */
2375 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2377 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2378 ff_neterrno() != FF_NETERROR(EINTR))
2379 /* error : close connection */
2384 c->buffer_ptr += len;
2386 c->data_count += len;
2387 update_datarate(&c->datarate, c->data_count);
2389 c->stream->bytes_served += len;
2397 static int http_start_receive_data(HTTPContext *c)
2401 if (c->stream->feed_opened)
2404 /* Don't permit writing to this one */
2405 if (c->stream->readonly)
2409 fd = open(c->stream->feed_filename, O_RDWR);
2411 http_log("Error opening feeder file: %s\n", strerror(errno));
2416 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2417 http_log("Error reading write index from feed file: %s\n", strerror(errno));
2420 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2421 lseek(fd, 0, SEEK_SET);
2423 /* init buffer input */
2424 c->buffer_ptr = c->buffer;
2425 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2426 c->stream->feed_opened = 1;
2430 static int http_receive_data(HTTPContext *c)
2434 if (c->buffer_end > c->buffer_ptr) {
2437 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2439 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2440 ff_neterrno() != FF_NETERROR(EINTR))
2441 /* error : close connection */
2443 } else if (len == 0)
2444 /* end of connection : close it */
2447 c->buffer_ptr += len;
2448 c->data_count += len;
2449 update_datarate(&c->datarate, c->data_count);
2453 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2454 if (c->buffer[0] != 'f' ||
2455 c->buffer[1] != 'm') {
2456 http_log("Feed stream has become desynchronized -- disconnecting\n");
2461 if (c->buffer_ptr >= c->buffer_end) {
2462 FFStream *feed = c->stream;
2463 /* a packet has been received : write it in the store, except
2465 if (c->data_count > FFM_PACKET_SIZE) {
2467 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2468 /* XXX: use llseek or url_seek */
2469 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2470 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2471 http_log("Error writing to feed file: %s\n", strerror(errno));
2475 feed->feed_write_index += FFM_PACKET_SIZE;
2476 /* update file size */
2477 if (feed->feed_write_index > c->stream->feed_size)
2478 feed->feed_size = feed->feed_write_index;
2480 /* handle wrap around if max file size reached */
2481 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2482 feed->feed_write_index = FFM_PACKET_SIZE;
2485 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2486 http_log("Error writing index to feed file: %s\n", strerror(errno));
2490 /* wake up any waiting connections */
2491 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2492 if (c1->state == HTTPSTATE_WAIT_FEED &&
2493 c1->stream->feed == c->stream->feed)
2494 c1->state = HTTPSTATE_SEND_DATA;
2497 /* We have a header in our hands that contains useful data */
2498 AVFormatContext *s = NULL;
2500 AVInputFormat *fmt_in;
2503 /* use feed output format name to find corresponding input format */
2504 fmt_in = av_find_input_format(feed->fmt->name);
2508 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2509 pb->is_streamed = 1;
2511 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2516 /* Now we have the actual streams */
2517 if (s->nb_streams != feed->nb_streams) {
2518 av_close_input_stream(s);
2523 for (i = 0; i < s->nb_streams; i++) {
2524 AVStream *fst = feed->streams[i];
2525 AVStream *st = s->streams[i];
2526 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2527 if (fst->codec->extradata_size) {
2528 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2529 if (!fst->codec->extradata)
2531 memcpy(fst->codec->extradata, st->codec->extradata,
2532 fst->codec->extradata_size);
2536 av_close_input_stream(s);
2539 c->buffer_ptr = c->buffer;
2544 c->stream->feed_opened = 0;
2546 /* wake up any waiting connections to stop waiting for feed */
2547 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2548 if (c1->state == HTTPSTATE_WAIT_FEED &&
2549 c1->stream->feed == c->stream->feed)
2550 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2555 /********************************************************************/
2558 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2565 switch(error_number) {
2566 case RTSP_STATUS_OK:
2569 case RTSP_STATUS_METHOD:
2570 str = "Method Not Allowed";
2572 case RTSP_STATUS_BANDWIDTH:
2573 str = "Not Enough Bandwidth";
2575 case RTSP_STATUS_SESSION:
2576 str = "Session Not Found";
2578 case RTSP_STATUS_STATE:
2579 str = "Method Not Valid in This State";
2581 case RTSP_STATUS_AGGREGATE:
2582 str = "Aggregate operation not allowed";
2584 case RTSP_STATUS_ONLY_AGGREGATE:
2585 str = "Only aggregate operation allowed";
2587 case RTSP_STATUS_TRANSPORT:
2588 str = "Unsupported transport";
2590 case RTSP_STATUS_INTERNAL:
2591 str = "Internal Server Error";
2593 case RTSP_STATUS_SERVICE:
2594 str = "Service Unavailable";
2596 case RTSP_STATUS_VERSION:
2597 str = "RTSP Version not supported";
2600 str = "Unknown Error";
2604 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2605 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2607 /* output GMT time */
2611 p = buf2 + strlen(p) - 1;
2614 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2617 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2619 rtsp_reply_header(c, error_number);
2620 url_fprintf(c->pb, "\r\n");
2623 static int rtsp_parse_request(HTTPContext *c)
2625 const char *p, *p1, *p2;
2631 RTSPHeader header1, *header = &header1;
2633 c->buffer_ptr[0] = '\0';
2636 get_word(cmd, sizeof(cmd), &p);
2637 get_word(url, sizeof(url), &p);
2638 get_word(protocol, sizeof(protocol), &p);
2640 av_strlcpy(c->method, cmd, sizeof(c->method));
2641 av_strlcpy(c->url, url, sizeof(c->url));
2642 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2644 if (url_open_dyn_buf(&c->pb) < 0) {
2645 /* XXX: cannot do more */
2646 c->pb = NULL; /* safety */
2650 /* check version name */
2651 if (strcmp(protocol, "RTSP/1.0") != 0) {
2652 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2656 /* parse each header line */
2657 memset(header, 0, sizeof(RTSPHeader));
2658 /* skip to next line */
2659 while (*p != '\n' && *p != '\0')
2663 while (*p != '\0') {
2664 p1 = strchr(p, '\n');
2668 if (p2 > p && p2[-1] == '\r')
2670 /* skip empty line */
2674 if (len > sizeof(line) - 1)
2675 len = sizeof(line) - 1;
2676 memcpy(line, p, len);
2678 rtsp_parse_line(header, line);
2682 /* handle sequence number */
2683 c->seq = header->seq;
2685 if (!strcmp(cmd, "DESCRIBE"))
2686 rtsp_cmd_describe(c, url);
2687 else if (!strcmp(cmd, "OPTIONS"))
2688 rtsp_cmd_options(c, url);
2689 else if (!strcmp(cmd, "SETUP"))
2690 rtsp_cmd_setup(c, url, header);
2691 else if (!strcmp(cmd, "PLAY"))
2692 rtsp_cmd_play(c, url, header);
2693 else if (!strcmp(cmd, "PAUSE"))
2694 rtsp_cmd_pause(c, url, header);
2695 else if (!strcmp(cmd, "TEARDOWN"))
2696 rtsp_cmd_teardown(c, url, header);
2698 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2701 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2702 c->pb = NULL; /* safety */
2704 /* XXX: cannot do more */
2707 c->buffer_ptr = c->pb_buffer;
2708 c->buffer_end = c->pb_buffer + len;
2709 c->state = RTSPSTATE_SEND_REPLY;
2713 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2714 struct in_addr my_ip)
2716 AVFormatContext *avc;
2717 AVStream avs[MAX_STREAMS];
2720 avc = avformat_alloc_context();
2724 if (stream->title[0] != 0) {
2725 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2727 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2729 avc->nb_streams = stream->nb_streams;
2730 if (stream->is_multicast) {
2731 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2732 inet_ntoa(stream->multicast_ip),
2733 stream->multicast_port, stream->multicast_ttl);
2736 for(i = 0; i < stream->nb_streams; i++) {
2737 avc->streams[i] = &avs[i];
2738 avc->streams[i]->codec = stream->streams[i]->codec;
2740 *pbuffer = av_mallocz(2048);
2741 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2744 return strlen(*pbuffer);
2747 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2749 // rtsp_reply_header(c, RTSP_STATUS_OK);
2750 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2751 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2752 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2753 url_fprintf(c->pb, "\r\n");
2756 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2762 int content_length, len;
2763 struct sockaddr_in my_addr;
2765 /* find which url is asked */
2766 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2771 for(stream = first_stream; stream != NULL; stream = stream->next) {
2772 if (!stream->is_feed &&
2773 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2774 !strcmp(path, stream->filename)) {
2778 /* no stream found */
2779 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2783 /* prepare the media description in sdp format */
2785 /* get the host IP */
2786 len = sizeof(my_addr);
2787 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2788 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2789 if (content_length < 0) {
2790 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2793 rtsp_reply_header(c, RTSP_STATUS_OK);
2794 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2795 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2796 url_fprintf(c->pb, "\r\n");
2797 put_buffer(c->pb, content, content_length);
2800 static HTTPContext *find_rtp_session(const char *session_id)
2804 if (session_id[0] == '\0')
2807 for(c = first_http_ctx; c != NULL; c = c->next) {
2808 if (!strcmp(c->session_id, session_id))
2814 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPLowerTransport lower_transport)
2816 RTSPTransportField *th;
2819 for(i=0;i<h->nb_transports;i++) {
2820 th = &h->transports[i];
2821 if (th->lower_transport == lower_transport)
2827 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2831 int stream_index, port;
2836 RTSPTransportField *th;
2837 struct sockaddr_in dest_addr;
2838 RTSPActionServerSetup setup;
2840 /* find which url is asked */
2841 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2846 /* now check each stream */
2847 for(stream = first_stream; stream != NULL; stream = stream->next) {
2848 if (!stream->is_feed &&
2849 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2850 /* accept aggregate filenames only if single stream */
2851 if (!strcmp(path, stream->filename)) {
2852 if (stream->nb_streams != 1) {
2853 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2860 for(stream_index = 0; stream_index < stream->nb_streams;
2862 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2863 stream->filename, stream_index);
2864 if (!strcmp(path, buf))
2869 /* no stream found */
2870 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2874 /* generate session id if needed */
2875 if (h->session_id[0] == '\0')
2876 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2877 av_random(&random_state), av_random(&random_state));
2879 /* find rtp session, and create it if none found */
2880 rtp_c = find_rtp_session(h->session_id);
2882 /* always prefer UDP */
2883 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
2885 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
2887 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2892 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2893 th->lower_transport);
2895 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2899 /* open input stream */
2900 if (open_input_stream(rtp_c, "") < 0) {
2901 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2906 /* test if stream is OK (test needed because several SETUP needs
2907 to be done for a given file) */
2908 if (rtp_c->stream != stream) {
2909 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2913 /* test if stream is already set up */
2914 if (rtp_c->rtp_ctx[stream_index]) {
2915 rtsp_reply_error(c, RTSP_STATUS_STATE);
2919 /* check transport */
2920 th = find_transport(h, rtp_c->rtp_protocol);
2921 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
2922 th->client_port_min <= 0)) {
2923 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2927 /* setup default options */
2928 setup.transport_option[0] = '\0';
2929 dest_addr = rtp_c->from_addr;
2930 dest_addr.sin_port = htons(th->client_port_min);
2933 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2934 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2938 /* now everything is OK, so we can send the connection parameters */
2939 rtsp_reply_header(c, RTSP_STATUS_OK);
2941 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2943 switch(rtp_c->rtp_protocol) {
2944 case RTSP_LOWER_TRANSPORT_UDP:
2945 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2946 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2947 "client_port=%d-%d;server_port=%d-%d",
2948 th->client_port_min, th->client_port_min + 1,
2951 case RTSP_LOWER_TRANSPORT_TCP:
2952 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2953 stream_index * 2, stream_index * 2 + 1);
2958 if (setup.transport_option[0] != '\0')
2959 url_fprintf(c->pb, ";%s", setup.transport_option);
2960 url_fprintf(c->pb, "\r\n");
2963 url_fprintf(c->pb, "\r\n");
2967 /* find an rtp connection by using the session ID. Check consistency
2969 static HTTPContext *find_rtp_session_with_url(const char *url,
2970 const char *session_id)
2978 rtp_c = find_rtp_session(session_id);
2982 /* find which url is asked */
2983 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2987 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2988 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2989 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2990 rtp_c->stream->filename, s);
2991 if(!strncmp(path, buf, sizeof(buf))) {
2992 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2999 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
3003 rtp_c = find_rtp_session_with_url(url, h->session_id);
3005 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3009 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3010 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3011 rtp_c->state != HTTPSTATE_READY) {
3012 rtsp_reply_error(c, RTSP_STATUS_STATE);
3017 /* XXX: seek in stream */
3018 if (h->range_start != AV_NOPTS_VALUE) {
3019 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3020 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3024 rtp_c->state = HTTPSTATE_SEND_DATA;
3026 /* now everything is OK, so we can send the connection parameters */
3027 rtsp_reply_header(c, RTSP_STATUS_OK);
3029 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3030 url_fprintf(c->pb, "\r\n");
3033 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3037 rtp_c = find_rtp_session_with_url(url, h->session_id);
3039 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3043 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3044 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3045 rtsp_reply_error(c, RTSP_STATUS_STATE);
3049 rtp_c->state = HTTPSTATE_READY;
3050 rtp_c->first_pts = AV_NOPTS_VALUE;
3051 /* now everything is OK, so we can send the connection parameters */
3052 rtsp_reply_header(c, RTSP_STATUS_OK);
3054 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3055 url_fprintf(c->pb, "\r\n");
3058 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3061 char session_id[32];
3063 rtp_c = find_rtp_session_with_url(url, h->session_id);
3065 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3069 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3071 /* abort the session */
3072 close_connection(rtp_c);
3074 /* now everything is OK, so we can send the connection parameters */
3075 rtsp_reply_header(c, RTSP_STATUS_OK);
3077 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3078 url_fprintf(c->pb, "\r\n");
3082 /********************************************************************/
3085 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3086 FFStream *stream, const char *session_id,
3087 enum RTSPLowerTransport rtp_protocol)
3089 HTTPContext *c = NULL;
3090 const char *proto_str;
3092 /* XXX: should output a warning page when coming
3093 close to the connection limit */
3094 if (nb_connections >= nb_max_connections)
3097 /* add a new connection */
3098 c = av_mallocz(sizeof(HTTPContext));
3103 c->poll_entry = NULL;
3104 c->from_addr = *from_addr;
3105 c->buffer_size = IOBUFFER_INIT_SIZE;
3106 c->buffer = av_malloc(c->buffer_size);
3111 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3112 c->state = HTTPSTATE_READY;
3113 c->is_packetized = 1;
3114 c->rtp_protocol = rtp_protocol;
3116 /* protocol is shown in statistics */
3117 switch(c->rtp_protocol) {
3118 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3119 proto_str = "MCAST";
3121 case RTSP_LOWER_TRANSPORT_UDP:
3124 case RTSP_LOWER_TRANSPORT_TCP:
3131 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3132 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3134 current_bandwidth += stream->bandwidth;
3136 c->next = first_http_ctx;
3148 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3149 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3151 static int rtp_new_av_stream(HTTPContext *c,
3152 int stream_index, struct sockaddr_in *dest_addr,
3153 HTTPContext *rtsp_c)
3155 AVFormatContext *ctx;
3158 URLContext *h = NULL;
3160 int max_packet_size;
3162 /* now we can open the relevant output stream */
3163 ctx = avformat_alloc_context();
3166 ctx->oformat = guess_format("rtp", NULL, NULL);
3168 st = av_mallocz(sizeof(AVStream));
3171 st->codec= avcodec_alloc_context();
3172 ctx->nb_streams = 1;
3173 ctx->streams[0] = st;
3175 if (!c->stream->feed ||
3176 c->stream->feed == c->stream)
3177 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3180 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3182 st->priv_data = NULL;
3184 /* build destination RTP address */
3185 ipaddr = inet_ntoa(dest_addr->sin_addr);
3187 switch(c->rtp_protocol) {
3188 case RTSP_LOWER_TRANSPORT_UDP:
3189 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3192 /* XXX: also pass as parameter to function ? */
3193 if (c->stream->is_multicast) {
3195 ttl = c->stream->multicast_ttl;
3198 snprintf(ctx->filename, sizeof(ctx->filename),
3199 "rtp://%s:%d?multicast=1&ttl=%d",
3200 ipaddr, ntohs(dest_addr->sin_port), ttl);
3202 snprintf(ctx->filename, sizeof(ctx->filename),
3203 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3206 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3208 c->rtp_handles[stream_index] = h;
3209 max_packet_size = url_get_max_packet_size(h);
3211 case RTSP_LOWER_TRANSPORT_TCP:
3214 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3220 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3221 ipaddr, ntohs(dest_addr->sin_port),
3222 c->stream->filename, stream_index, c->protocol);
3224 /* normally, no packets should be output here, but the packet size may be checked */
3225 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3226 /* XXX: close stream */
3229 av_set_parameters(ctx, NULL);
3230 if (av_write_header(ctx) < 0) {
3237 url_close_dyn_buf(ctx->pb, &dummy_buf);
3240 c->rtp_ctx[stream_index] = ctx;
3244 /********************************************************************/
3245 /* ffserver initialization */
3247 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3251 fst = av_mallocz(sizeof(AVStream));
3254 fst->codec= avcodec_alloc_context();
3255 fst->priv_data = av_mallocz(sizeof(FeedData));
3256 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3257 fst->index = stream->nb_streams;
3258 av_set_pts_info(fst, 33, 1, 90000);
3259 stream->streams[stream->nb_streams++] = fst;
3263 /* return the stream number in the feed */
3264 static int add_av_stream(FFStream *feed, AVStream *st)
3267 AVCodecContext *av, *av1;
3271 for(i=0;i<feed->nb_streams;i++) {
3272 st = feed->streams[i];
3274 if (av1->codec_id == av->codec_id &&
3275 av1->codec_type == av->codec_type &&
3276 av1->bit_rate == av->bit_rate) {
3278 switch(av->codec_type) {
3279 case CODEC_TYPE_AUDIO:
3280 if (av1->channels == av->channels &&
3281 av1->sample_rate == av->sample_rate)
3284 case CODEC_TYPE_VIDEO:
3285 if (av1->width == av->width &&
3286 av1->height == av->height &&
3287 av1->time_base.den == av->time_base.den &&
3288 av1->time_base.num == av->time_base.num &&
3289 av1->gop_size == av->gop_size)
3298 fst = add_av_stream1(feed, av);
3301 return feed->nb_streams - 1;
3306 static void remove_stream(FFStream *stream)
3310 while (*ps != NULL) {
3318 /* specific mpeg4 handling : we extract the raw parameters */
3319 static void extract_mpeg4_header(AVFormatContext *infile)
3321 int mpeg4_count, i, size;
3327 for(i=0;i<infile->nb_streams;i++) {
3328 st = infile->streams[i];
3329 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3330 st->codec->extradata_size == 0) {
3337 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3338 while (mpeg4_count > 0) {
3339 if (av_read_packet(infile, &pkt) < 0)
3341 st = infile->streams[pkt.stream_index];
3342 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3343 st->codec->extradata_size == 0) {
3344 av_freep(&st->codec->extradata);
3345 /* fill extradata with the header */
3346 /* XXX: we make hard suppositions here ! */
3348 while (p < pkt.data + pkt.size - 4) {
3349 /* stop when vop header is found */
3350 if (p[0] == 0x00 && p[1] == 0x00 &&
3351 p[2] == 0x01 && p[3] == 0xb6) {
3352 size = p - pkt.data;
3353 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3354 st->codec->extradata = av_malloc(size);
3355 st->codec->extradata_size = size;
3356 memcpy(st->codec->extradata, pkt.data, size);
3363 av_free_packet(&pkt);
3367 /* compute the needed AVStream for each file */
3368 static void build_file_streams(void)
3370 FFStream *stream, *stream_next;
3371 AVFormatContext *infile;
3374 /* gather all streams */
3375 for(stream = first_stream; stream != NULL; stream = stream_next) {
3376 stream_next = stream->next;
3377 if (stream->stream_type == STREAM_TYPE_LIVE &&
3379 /* the stream comes from a file */
3380 /* try to open the file */
3382 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3383 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3384 /* specific case : if transport stream output to RTP,
3385 we use a raw transport stream reader */
3386 stream->ap_in->mpeg2ts_raw = 1;
3387 stream->ap_in->mpeg2ts_compute_pcr = 1;
3390 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3391 stream->ifmt, 0, stream->ap_in)) < 0) {
3392 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3393 /* remove stream (no need to spend more time on it) */
3395 remove_stream(stream);
3397 /* find all the AVStreams inside and reference them in
3399 if (av_find_stream_info(infile) < 0) {
3400 http_log("Could not find codec parameters from '%s'\n",
3401 stream->feed_filename);
3402 av_close_input_file(infile);
3405 extract_mpeg4_header(infile);
3407 for(i=0;i<infile->nb_streams;i++)
3408 add_av_stream1(stream, infile->streams[i]->codec);
3410 av_close_input_file(infile);
3416 /* compute the needed AVStream for each feed */
3417 static void build_feed_streams(void)
3419 FFStream *stream, *feed;
3422 /* gather all streams */
3423 for(stream = first_stream; stream != NULL; stream = stream->next) {
3424 feed = stream->feed;
3426 if (!stream->is_feed) {
3427 /* we handle a stream coming from a feed */
3428 for(i=0;i<stream->nb_streams;i++)
3429 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3434 /* gather all streams */
3435 for(stream = first_stream; stream != NULL; stream = stream->next) {
3436 feed = stream->feed;
3438 if (stream->is_feed) {
3439 for(i=0;i<stream->nb_streams;i++)
3440 stream->feed_streams[i] = i;
3445 /* create feed files if needed */
3446 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3449 if (url_exist(feed->feed_filename)) {
3450 /* See if it matches */
3454 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3455 /* Now see if it matches */
3456 if (s->nb_streams == feed->nb_streams) {
3458 for(i=0;i<s->nb_streams;i++) {
3460 sf = feed->streams[i];
3463 if (sf->index != ss->index ||
3465 http_log("Index & Id do not match for stream %d (%s)\n",
3466 i, feed->feed_filename);
3469 AVCodecContext *ccf, *ccs;
3473 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3475 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3476 http_log("Codecs do not match for stream %d\n", i);
3478 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3479 http_log("Codec bitrates do not match for stream %d\n", i);
3481 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3482 if (CHECK_CODEC(time_base.den) ||
3483 CHECK_CODEC(time_base.num) ||
3484 CHECK_CODEC(width) ||
3485 CHECK_CODEC(height)) {
3486 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3489 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3490 if (CHECK_CODEC(sample_rate) ||
3491 CHECK_CODEC(channels) ||
3492 CHECK_CODEC(frame_size)) {
3493 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3497 http_log("Unknown codec type\n");
3505 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3506 feed->feed_filename, s->nb_streams, feed->nb_streams);
3508 av_close_input_file(s);
3510 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3511 feed->feed_filename);
3514 if (feed->readonly) {
3515 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3516 feed->feed_filename);
3519 unlink(feed->feed_filename);
3522 if (!url_exist(feed->feed_filename)) {
3523 AVFormatContext s1 = {0}, *s = &s1;
3525 if (feed->readonly) {
3526 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3527 feed->feed_filename);
3531 /* only write the header of the ffm file */
3532 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3533 http_log("Could not open output feed file '%s'\n",
3534 feed->feed_filename);
3537 s->oformat = feed->fmt;
3538 s->nb_streams = feed->nb_streams;
3539 for(i=0;i<s->nb_streams;i++) {
3541 st = feed->streams[i];
3544 av_set_parameters(s, NULL);
3545 if (av_write_header(s) < 0) {
3546 http_log("Container doesn't supports the required parameters\n");
3549 /* XXX: need better api */
3550 av_freep(&s->priv_data);
3553 /* get feed size and write index */
3554 fd = open(feed->feed_filename, O_RDONLY);
3556 http_log("Could not open output feed file '%s'\n",
3557 feed->feed_filename);
3561 feed->feed_write_index = ffm_read_write_index(fd);
3562 feed->feed_size = lseek(fd, 0, SEEK_END);
3563 /* ensure that we do not wrap before the end of file */
3564 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3565 feed->feed_max_size = feed->feed_size;
3571 /* compute the bandwidth used by each stream */
3572 static void compute_bandwidth(void)
3578 for(stream = first_stream; stream != NULL; stream = stream->next) {
3580 for(i=0;i<stream->nb_streams;i++) {
3581 AVStream *st = stream->streams[i];
3582 switch(st->codec->codec_type) {
3583 case CODEC_TYPE_AUDIO:
3584 case CODEC_TYPE_VIDEO:
3585 bandwidth += st->codec->bit_rate;
3591 stream->bandwidth = (bandwidth + 999) / 1000;
3595 static void get_arg(char *buf, int buf_size, const char **pp)
3602 while (isspace(*p)) p++;
3605 if (*p == '\"' || *p == '\'')
3617 if ((q - buf) < buf_size - 1)
3622 if (quote && *p == quote)
3627 /* add a codec and set the default parameters */
3628 static void add_codec(FFStream *stream, AVCodecContext *av)
3632 /* compute default parameters */
3633 switch(av->codec_type) {
3634 case CODEC_TYPE_AUDIO:
3635 if (av->bit_rate == 0)
3636 av->bit_rate = 64000;
3637 if (av->sample_rate == 0)
3638 av->sample_rate = 22050;
3639 if (av->channels == 0)
3642 case CODEC_TYPE_VIDEO:
3643 if (av->bit_rate == 0)
3644 av->bit_rate = 64000;
3645 if (av->time_base.num == 0){
3646 av->time_base.den = 5;
3647 av->time_base.num = 1;
3649 if (av->width == 0 || av->height == 0) {
3653 /* Bitrate tolerance is less for streaming */
3654 if (av->bit_rate_tolerance == 0)
3655 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3656 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3661 if (av->max_qdiff == 0)
3663 av->qcompress = 0.5;
3666 if (!av->nsse_weight)
3667 av->nsse_weight = 8;
3669 av->frame_skip_cmp = FF_CMP_DCTMAX;
3670 av->me_method = ME_EPZS;
3671 av->rc_buffer_aggressivity = 1.0;
3674 av->rc_eq = "tex^qComp";
3675 if (!av->i_quant_factor)
3676 av->i_quant_factor = -0.8;
3677 if (!av->b_quant_factor)
3678 av->b_quant_factor = 1.25;
3679 if (!av->b_quant_offset)
3680 av->b_quant_offset = 1.25;
3681 if (!av->rc_max_rate)
3682 av->rc_max_rate = av->bit_rate * 2;
3684 if (av->rc_max_rate && !av->rc_buffer_size) {
3685 av->rc_buffer_size = av->rc_max_rate;
3694 st = av_mallocz(sizeof(AVStream));
3697 st->codec = avcodec_alloc_context();
3698 stream->streams[stream->nb_streams++] = st;
3699 memcpy(st->codec, av, sizeof(AVCodecContext));
3702 static enum CodecID opt_audio_codec(const char *arg)
3704 AVCodec *p= avcodec_find_encoder_by_name(arg);
3706 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3707 return CODEC_ID_NONE;
3712 static enum CodecID opt_video_codec(const char *arg)
3714 AVCodec *p= avcodec_find_encoder_by_name(arg);
3716 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3717 return CODEC_ID_NONE;
3722 /* simplistic plugin support */
3725 static void load_module(const char *filename)
3728 void (*init_func)(void);
3729 dll = dlopen(filename, RTLD_NOW);
3731 fprintf(stderr, "Could not load module '%s' - %s\n",
3732 filename, dlerror());
3736 init_func = dlsym(dll, "ffserver_module_init");
3739 "%s: init function 'ffserver_module_init()' not found\n",
3748 static int ffserver_opt_default(const char *opt, const char *arg,
3749 AVCodecContext *avctx, int type)
3752 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
3754 ret = av_set_string3(avctx, opt, arg, 1, NULL);
3758 static int parse_ffconfig(const char *filename)
3765 int val, errors, line_num;
3766 FFStream **last_stream, *stream, *redirect;
3767 FFStream **last_feed, *feed;
3768 AVCodecContext audio_enc, video_enc;
3769 enum CodecID audio_id, video_id;
3771 f = fopen(filename, "r");
3779 first_stream = NULL;
3780 last_stream = &first_stream;
3782 last_feed = &first_feed;
3786 audio_id = CODEC_ID_NONE;
3787 video_id = CODEC_ID_NONE;
3789 if (fgets(line, sizeof(line), f) == NULL)
3795 if (*p == '\0' || *p == '#')
3798 get_arg(cmd, sizeof(cmd), &p);
3800 if (!strcasecmp(cmd, "Port")) {
3801 get_arg(arg, sizeof(arg), &p);
3803 if (val < 1 || val > 65536) {
3804 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3805 filename, line_num, arg);
3808 my_http_addr.sin_port = htons(val);
3809 } else if (!strcasecmp(cmd, "BindAddress")) {
3810 get_arg(arg, sizeof(arg), &p);
3811 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3812 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3813 filename, line_num, arg);
3816 } else if (!strcasecmp(cmd, "NoDaemon")) {
3817 ffserver_daemon = 0;
3818 } else if (!strcasecmp(cmd, "RTSPPort")) {
3819 get_arg(arg, sizeof(arg), &p);
3821 if (val < 1 || val > 65536) {
3822 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3823 filename, line_num, arg);
3826 my_rtsp_addr.sin_port = htons(atoi(arg));
3827 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3828 get_arg(arg, sizeof(arg), &p);
3829 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3830 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3831 filename, line_num, arg);
3834 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
3835 get_arg(arg, sizeof(arg), &p);
3837 if (val < 1 || val > 65536) {
3838 fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n",
3839 filename, line_num, arg);
3842 nb_max_http_connections = val;
3843 } else if (!strcasecmp(cmd, "MaxClients")) {
3844 get_arg(arg, sizeof(arg), &p);
3846 if (val < 1 || val > nb_max_http_connections) {
3847 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3848 filename, line_num, arg);
3851 nb_max_connections = val;
3853 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3855 get_arg(arg, sizeof(arg), &p);
3857 if (llval < 10 || llval > 10000000) {
3858 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3859 filename, line_num, arg);
3862 max_bandwidth = llval;
3863 } else if (!strcasecmp(cmd, "CustomLog")) {
3864 if (!ffserver_debug)
3865 get_arg(logfilename, sizeof(logfilename), &p);
3866 } else if (!strcasecmp(cmd, "<Feed")) {
3867 /*********************************************/
3868 /* Feed related options */
3870 if (stream || feed) {
3871 fprintf(stderr, "%s:%d: Already in a tag\n",
3872 filename, line_num);
3874 feed = av_mallocz(sizeof(FFStream));
3875 /* add in stream list */
3876 *last_stream = feed;
3877 last_stream = &feed->next;
3878 /* add in feed list */
3880 last_feed = &feed->next_feed;
3882 get_arg(feed->filename, sizeof(feed->filename), &p);
3883 q = strrchr(feed->filename, '>');
3886 feed->fmt = guess_format("ffm", NULL, NULL);
3887 /* defaut feed file */
3888 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3889 "/tmp/%s.ffm", feed->filename);
3890 feed->feed_max_size = 5 * 1024 * 1024;
3892 feed->feed = feed; /* self feeding :-) */
3894 } else if (!strcasecmp(cmd, "Launch")) {
3898 feed->child_argv = av_mallocz(64 * sizeof(char *));
3900 for (i = 0; i < 62; i++) {
3901 get_arg(arg, sizeof(arg), &p);
3905 feed->child_argv[i] = av_strdup(arg);
3908 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3910 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3912 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3913 inet_ntoa(my_http_addr.sin_addr),
3914 ntohs(my_http_addr.sin_port), feed->filename);
3916 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3918 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3920 } else if (stream) {
3921 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3923 } else if (!strcasecmp(cmd, "File")) {
3925 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3927 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3928 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3933 get_arg(arg, sizeof(arg), &p);
3935 fsize = strtod(p1, &p1);
3936 switch(toupper(*p1)) {
3941 fsize *= 1024 * 1024;
3944 fsize *= 1024 * 1024 * 1024;
3947 feed->feed_max_size = (int64_t)fsize;
3949 } else if (!strcasecmp(cmd, "</Feed>")) {
3951 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3952 filename, line_num);
3956 } else if (!strcasecmp(cmd, "<Stream")) {
3957 /*********************************************/
3958 /* Stream related options */
3960 if (stream || feed) {
3961 fprintf(stderr, "%s:%d: Already in a tag\n",
3962 filename, line_num);
3964 const AVClass *class;
3965 stream = av_mallocz(sizeof(FFStream));
3966 *last_stream = stream;
3967 last_stream = &stream->next;
3969 get_arg(stream->filename, sizeof(stream->filename), &p);
3970 q = strrchr(stream->filename, '>');
3973 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3974 /* fetch avclass so AVOption works
3975 * FIXME try to use avcodec_get_context_defaults2
3976 * without changing defaults too much */
3977 avcodec_get_context_defaults(&video_enc);
3978 class = video_enc.av_class;
3979 memset(&audio_enc, 0, sizeof(AVCodecContext));
3980 memset(&video_enc, 0, sizeof(AVCodecContext));
3981 audio_enc.av_class = class;
3982 video_enc.av_class = class;
3983 audio_id = CODEC_ID_NONE;
3984 video_id = CODEC_ID_NONE;
3986 audio_id = stream->fmt->audio_codec;
3987 video_id = stream->fmt->video_codec;
3990 } else if (!strcasecmp(cmd, "Feed")) {
3991 get_arg(arg, sizeof(arg), &p);
3996 while (sfeed != NULL) {
3997 if (!strcmp(sfeed->filename, arg))
3999 sfeed = sfeed->next_feed;
4002 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
4003 filename, line_num, arg);
4005 stream->feed = sfeed;
4007 } else if (!strcasecmp(cmd, "Format")) {
4008 get_arg(arg, sizeof(arg), &p);
4010 if (!strcmp(arg, "status")) {
4011 stream->stream_type = STREAM_TYPE_STATUS;
4014 stream->stream_type = STREAM_TYPE_LIVE;
4015 /* jpeg cannot be used here, so use single frame jpeg */
4016 if (!strcmp(arg, "jpeg"))
4017 strcpy(arg, "mjpeg");
4018 stream->fmt = guess_stream_format(arg, NULL, NULL);
4020 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4021 filename, line_num, arg);
4026 audio_id = stream->fmt->audio_codec;
4027 video_id = stream->fmt->video_codec;
4030 } else if (!strcasecmp(cmd, "InputFormat")) {
4031 get_arg(arg, sizeof(arg), &p);
4033 stream->ifmt = av_find_input_format(arg);
4034 if (!stream->ifmt) {
4035 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4036 filename, line_num, arg);
4039 } else if (!strcasecmp(cmd, "FaviconURL")) {
4040 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4041 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4043 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4044 filename, line_num);
4047 } else if (!strcasecmp(cmd, "Author")) {
4049 get_arg(stream->author, sizeof(stream->author), &p);
4050 } else if (!strcasecmp(cmd, "Comment")) {
4052 get_arg(stream->comment, sizeof(stream->comment), &p);
4053 } else if (!strcasecmp(cmd, "Copyright")) {
4055 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4056 } else if (!strcasecmp(cmd, "Title")) {
4058 get_arg(stream->title, sizeof(stream->title), &p);
4059 } else if (!strcasecmp(cmd, "Preroll")) {
4060 get_arg(arg, sizeof(arg), &p);
4062 stream->prebuffer = atof(arg) * 1000;
4063 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4065 stream->send_on_key = 1;
4066 } else if (!strcasecmp(cmd, "AudioCodec")) {
4067 get_arg(arg, sizeof(arg), &p);
4068 audio_id = opt_audio_codec(arg);
4069 if (audio_id == CODEC_ID_NONE) {
4070 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4071 filename, line_num, arg);
4074 } else if (!strcasecmp(cmd, "VideoCodec")) {
4075 get_arg(arg, sizeof(arg), &p);
4076 video_id = opt_video_codec(arg);
4077 if (video_id == CODEC_ID_NONE) {
4078 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4079 filename, line_num, arg);
4082 } else if (!strcasecmp(cmd, "MaxTime")) {
4083 get_arg(arg, sizeof(arg), &p);
4085 stream->max_time = atof(arg) * 1000;
4086 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4087 get_arg(arg, sizeof(arg), &p);
4089 audio_enc.bit_rate = atoi(arg) * 1000;
4090 } else if (!strcasecmp(cmd, "AudioChannels")) {
4091 get_arg(arg, sizeof(arg), &p);
4093 audio_enc.channels = atoi(arg);
4094 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4095 get_arg(arg, sizeof(arg), &p);
4097 audio_enc.sample_rate = atoi(arg);
4098 } else if (!strcasecmp(cmd, "AudioQuality")) {
4099 get_arg(arg, sizeof(arg), &p);
4101 // audio_enc.quality = atof(arg) * 1000;
4103 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4105 int minrate, maxrate;
4107 get_arg(arg, sizeof(arg), &p);
4109 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4110 video_enc.rc_min_rate = minrate * 1000;
4111 video_enc.rc_max_rate = maxrate * 1000;
4113 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4114 filename, line_num, arg);
4118 } else if (!strcasecmp(cmd, "Debug")) {
4120 get_arg(arg, sizeof(arg), &p);
4121 video_enc.debug = strtol(arg,0,0);
4123 } else if (!strcasecmp(cmd, "Strict")) {
4125 get_arg(arg, sizeof(arg), &p);
4126 video_enc.strict_std_compliance = atoi(arg);
4128 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4130 get_arg(arg, sizeof(arg), &p);
4131 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4133 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4135 get_arg(arg, sizeof(arg), &p);
4136 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4138 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4139 get_arg(arg, sizeof(arg), &p);
4141 video_enc.bit_rate = atoi(arg) * 1000;
4143 } else if (!strcasecmp(cmd, "VideoSize")) {
4144 get_arg(arg, sizeof(arg), &p);
4146 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4147 if ((video_enc.width % 16) != 0 ||
4148 (video_enc.height % 16) != 0) {
4149 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4150 filename, line_num);
4154 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4155 get_arg(arg, sizeof(arg), &p);
4157 AVRational frame_rate;
4158 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4159 fprintf(stderr, "Incorrect frame rate\n");
4162 video_enc.time_base.num = frame_rate.den;
4163 video_enc.time_base.den = frame_rate.num;
4166 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4167 get_arg(arg, sizeof(arg), &p);
4169 video_enc.gop_size = atoi(arg);
4170 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4172 video_enc.gop_size = 1;
4173 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4175 video_enc.mb_decision = FF_MB_DECISION_BITS;
4176 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4178 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4179 video_enc.flags |= CODEC_FLAG_4MV;
4181 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4182 !strcasecmp(cmd, "AVOptionAudio")) {
4184 AVCodecContext *avctx;
4186 get_arg(arg, sizeof(arg), &p);
4187 get_arg(arg2, sizeof(arg2), &p);
4188 if (!strcasecmp(cmd, "AVOptionVideo")) {
4190 type = AV_OPT_FLAG_VIDEO_PARAM;
4193 type = AV_OPT_FLAG_AUDIO_PARAM;
4195 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4196 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4199 } else if (!strcasecmp(cmd, "VideoTag")) {
4200 get_arg(arg, sizeof(arg), &p);
4201 if ((strlen(arg) == 4) && stream)
4202 video_enc.codec_tag = AV_RL32(arg);
4203 } else if (!strcasecmp(cmd, "BitExact")) {
4205 video_enc.flags |= CODEC_FLAG_BITEXACT;
4206 } else if (!strcasecmp(cmd, "DctFastint")) {
4208 video_enc.dct_algo = FF_DCT_FASTINT;
4209 } else if (!strcasecmp(cmd, "IdctSimple")) {
4211 video_enc.idct_algo = FF_IDCT_SIMPLE;
4212 } else if (!strcasecmp(cmd, "Qscale")) {
4213 get_arg(arg, sizeof(arg), &p);
4215 video_enc.flags |= CODEC_FLAG_QSCALE;
4216 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4218 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4219 get_arg(arg, sizeof(arg), &p);
4221 video_enc.max_qdiff = atoi(arg);
4222 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4223 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4224 filename, line_num);
4228 } else if (!strcasecmp(cmd, "VideoQMax")) {
4229 get_arg(arg, sizeof(arg), &p);
4231 video_enc.qmax = atoi(arg);
4232 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4233 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4234 filename, line_num);
4238 } else if (!strcasecmp(cmd, "VideoQMin")) {
4239 get_arg(arg, sizeof(arg), &p);
4241 video_enc.qmin = atoi(arg);
4242 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4243 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4244 filename, line_num);
4248 } else if (!strcasecmp(cmd, "LumaElim")) {
4249 get_arg(arg, sizeof(arg), &p);
4251 video_enc.luma_elim_threshold = atoi(arg);
4252 } else if (!strcasecmp(cmd, "ChromaElim")) {
4253 get_arg(arg, sizeof(arg), &p);
4255 video_enc.chroma_elim_threshold = atoi(arg);
4256 } else if (!strcasecmp(cmd, "LumiMask")) {
4257 get_arg(arg, sizeof(arg), &p);
4259 video_enc.lumi_masking = atof(arg);
4260 } else if (!strcasecmp(cmd, "DarkMask")) {
4261 get_arg(arg, sizeof(arg), &p);
4263 video_enc.dark_masking = atof(arg);
4264 } else if (!strcasecmp(cmd, "NoVideo")) {
4265 video_id = CODEC_ID_NONE;
4266 } else if (!strcasecmp(cmd, "NoAudio")) {
4267 audio_id = CODEC_ID_NONE;
4268 } else if (!strcasecmp(cmd, "ACL")) {
4271 get_arg(arg, sizeof(arg), &p);
4272 if (strcasecmp(arg, "allow") == 0)
4273 acl.action = IP_ALLOW;
4274 else if (strcasecmp(arg, "deny") == 0)
4275 acl.action = IP_DENY;
4277 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4278 filename, line_num, arg);
4282 get_arg(arg, sizeof(arg), &p);
4284 if (resolve_host(&acl.first, arg) != 0) {
4285 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4286 filename, line_num, arg);
4289 acl.last = acl.first;
4291 get_arg(arg, sizeof(arg), &p);
4294 if (resolve_host(&acl.last, arg) != 0) {
4295 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4296 filename, line_num, arg);
4302 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4303 IPAddressACL **naclp = 0;
4309 naclp = &stream->acl;
4313 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4314 filename, line_num);
4320 naclp = &(*naclp)->next;
4325 } else if (!strcasecmp(cmd, "RTSPOption")) {
4326 get_arg(arg, sizeof(arg), &p);
4328 av_freep(&stream->rtsp_option);
4329 stream->rtsp_option = av_strdup(arg);
4331 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4332 get_arg(arg, sizeof(arg), &p);
4334 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4335 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4336 filename, line_num, arg);
4339 stream->is_multicast = 1;
4340 stream->loop = 1; /* default is looping */
4342 } else if (!strcasecmp(cmd, "MulticastPort")) {
4343 get_arg(arg, sizeof(arg), &p);
4345 stream->multicast_port = atoi(arg);
4346 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4347 get_arg(arg, sizeof(arg), &p);
4349 stream->multicast_ttl = atoi(arg);
4350 } else if (!strcasecmp(cmd, "NoLoop")) {
4353 } else if (!strcasecmp(cmd, "</Stream>")) {
4355 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4356 filename, line_num);
4359 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4360 if (audio_id != CODEC_ID_NONE) {
4361 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4362 audio_enc.codec_id = audio_id;
4363 add_codec(stream, &audio_enc);
4365 if (video_id != CODEC_ID_NONE) {
4366 video_enc.codec_type = CODEC_TYPE_VIDEO;
4367 video_enc.codec_id = video_id;
4368 add_codec(stream, &video_enc);
4373 } else if (!strcasecmp(cmd, "<Redirect")) {
4374 /*********************************************/
4376 if (stream || feed || redirect) {
4377 fprintf(stderr, "%s:%d: Already in a tag\n",
4378 filename, line_num);
4381 redirect = av_mallocz(sizeof(FFStream));
4382 *last_stream = redirect;
4383 last_stream = &redirect->next;
4385 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4386 q = strrchr(redirect->filename, '>');
4389 redirect->stream_type = STREAM_TYPE_REDIRECT;
4391 } else if (!strcasecmp(cmd, "URL")) {
4393 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4394 } else if (!strcasecmp(cmd, "</Redirect>")) {
4396 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4397 filename, line_num);
4400 if (!redirect->feed_filename[0]) {
4401 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4402 filename, line_num);
4407 } else if (!strcasecmp(cmd, "LoadModule")) {
4408 get_arg(arg, sizeof(arg), &p);
4412 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4413 filename, line_num, arg);
4417 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4418 filename, line_num, cmd);
4430 static void handle_child_exit(int sig)
4435 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4438 for (feed = first_feed; feed; feed = feed->next) {
4439 if (feed->pid == pid) {
4440 int uptime = time(0) - feed->pid_start;
4443 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4446 /* Turn off any more restarts */
4447 feed->child_argv = 0;
4452 need_to_start_children = 1;
4455 static void opt_debug(void)
4458 ffserver_daemon = 0;
4459 logfilename[0] = '-';
4462 static void opt_show_help(void)
4464 printf("usage: ffserver [options]\n"
4465 "Hyper fast multi format Audio/Video streaming server\n");
4467 show_help_options(options, "Main options:\n", 0, 0);
4470 static const OptionDef options[] = {
4471 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4472 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4473 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4474 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4475 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4476 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4477 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4481 int main(int argc, char **argv)
4483 struct sigaction sigact;
4489 config_filename = "/etc/ffserver.conf";
4491 my_program_name = argv[0];
4492 my_program_dir = getcwd(0, 0);
4493 ffserver_daemon = 1;
4495 parse_options(argc, argv, options, NULL);
4497 unsetenv("http_proxy"); /* Kill the http_proxy */
4499 av_random_init(&random_state, av_gettime() + (getpid() << 16));
4501 memset(&sigact, 0, sizeof(sigact));
4502 sigact.sa_handler = handle_child_exit;
4503 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4504 sigaction(SIGCHLD, &sigact, 0);
4506 if (parse_ffconfig(config_filename) < 0) {
4507 fprintf(stderr, "Incorrect config file - exiting.\n");
4511 /* open log file if needed */
4512 if (logfilename[0] != '\0') {
4513 if (!strcmp(logfilename, "-"))
4516 logfile = fopen(logfilename, "a");
4517 av_log_set_callback(http_av_log);
4520 build_file_streams();
4522 build_feed_streams();
4524 compute_bandwidth();
4526 /* put the process in background and detach it from its TTY */
4527 if (ffserver_daemon) {
4534 } else if (pid > 0) {
4541 open("/dev/null", O_RDWR);
4542 if (strcmp(logfilename, "-") != 0) {
4552 signal(SIGPIPE, SIG_IGN);
4554 if (ffserver_daemon)
4557 if (http_server() < 0) {
4558 http_log("Could not start server\n");