ffserver.c
85f07f22
 /*
773a21b8
  * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
85f07f22
  *
b78e7197
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
773a21b8
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
b78e7197
  * version 2.1 of the License, or (at your option) any later version.
85f07f22
  *
b78e7197
  * FFmpeg is distributed in the hope that it will be useful,
85f07f22
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
773a21b8
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
85f07f22
  *
773a21b8
  * You should have received a copy of the GNU Lesser General Public
b78e7197
  * License along with FFmpeg; if not, write to the Free Software
5509bffa
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
85f07f22
  */
364a9607
 
93613338
 /**
  * @file
  * multiple format streaming server based on the FFmpeg libraries
  */
 
0f4e8165
 #include "config.h"
b250f9c6
 #if !HAVE_CLOSESOCKET
0f4e8165
 #define closesocket close
 #endif
 #include <string.h>
 #include <stdlib.h>
a4cd2ad8
 #include <stdio.h>
245976da
 #include "libavformat/avformat.h"
9430c232
 // FIXME those are internal headers, ffserver _really_ shouldn't use them
3ee53dab
 #include "libavformat/ffm.h"
245976da
 #include "libavformat/network.h"
 #include "libavformat/os_support.h"
302879cb
 #include "libavformat/rtpdec.h"
b7e6da98
 #include "libavformat/rtpproto.h"
245976da
 #include "libavformat/rtsp.h"
403ee835
 #include "libavformat/avio_internal.h"
b263bf66
 #include "libavformat/internal.h"
 #include "libavformat/url.h"
 
9c6af3a3
 #include "libavutil/avassert.h"
959da985
 #include "libavutil/avstring.h"
042819c5
 #include "libavutil/lfg.h"
d2d67e42
 #include "libavutil/dict.h"
3b20eb25
 #include "libavutil/intreadwrite.h"
0ebcdf5c
 #include "libavutil/mathematics.h"
90ca8142
 #include "libavutil/pixdesc.h"
042819c5
 #include "libavutil/random_seed.h"
737eb597
 #include "libavutil/parseutils.h"
41d0eb1c
 #include "libavutil/opt.h"
676ea8fa
 #include "libavutil/time.h"
 
85f07f22
 #include <stdarg.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
b250f9c6
 #if HAVE_POLL_H
f8cda19e
 #include <poll.h>
b0c858d8
 #endif
85f07f22
 #include <errno.h>
 #include <time.h>
5eb765ef
 #include <sys/wait.h>
85f07f22
 #include <signal.h>
2effd274
 
4ce5df08
 #include "cmdutils.h"
85f07f22
 
89b503b5
 const char program_name[] = "ffserver";
ea9c581f
 const int program_birth_year = 2000;
86074ed1
 
5a635bc7
 static const OptionDef options[];
 
85f07f22
 enum HTTPState {
     HTTPSTATE_WAIT_REQUEST,
     HTTPSTATE_SEND_HEADER,
     HTTPSTATE_SEND_DATA_HEADER,
2effd274
     HTTPSTATE_SEND_DATA,          /* sending TCP or UDP data */
85f07f22
     HTTPSTATE_SEND_DATA_TRAILER,
115329f1
     HTTPSTATE_RECEIVE_DATA,
2effd274
     HTTPSTATE_WAIT_FEED,          /* wait for data from the feed */
     HTTPSTATE_READY,
 
     RTSPSTATE_WAIT_REQUEST,
     RTSPSTATE_SEND_REPLY,
bc351386
     RTSPSTATE_SEND_PACKET,
85f07f22
 };
 
9507a12e
 static const char *http_state[] = {
2effd274
     "HTTP_WAIT_REQUEST",
     "HTTP_SEND_HEADER",
 
85f07f22
     "SEND_DATA_HEADER",
     "SEND_DATA",
     "SEND_DATA_TRAILER",
     "RECEIVE_DATA",
     "WAIT_FEED",
2effd274
     "READY",
 
     "RTSP_WAIT_REQUEST",
     "RTSP_SEND_REPLY",
bc351386
     "RTSP_SEND_PACKET",
85f07f22
 };
 
aff88101
 #define MAX_STREAMS 20
 
cde25790
 #define IOBUFFER_INIT_SIZE 8192
85f07f22
 
 /* timeouts are in ms */
2effd274
 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
 
85f07f22
 #define SYNC_TIMEOUT (10 * 1000)
 
b516ecdd
 typedef struct RTSPActionServerSetup {
     uint32_t ipaddr;
     char transport_option[512];
 } RTSPActionServerSetup;
 
5eb765ef
 typedef struct {
0c1a9eda
     int64_t count1, count2;
c3f58185
     int64_t time1, time2;
5eb765ef
 } DataRateData;
 
85f07f22
 /* context associated with one connection */
 typedef struct HTTPContext {
     enum HTTPState state;
     int fd; /* socket file descriptor */
     struct sockaddr_in from_addr; /* origin */
     struct pollfd *poll_entry; /* used when polling */
c3f58185
     int64_t timeout;
0c1a9eda
     uint8_t *buffer_ptr, *buffer_end;
85f07f22
     int http_error;
edfdd798
     int post;
19c8c4ec
     int chunked_encoding;
     int chunk_size;               /* 0 if it needs to be read */
85f07f22
     struct HTTPContext *next;
42a63c6a
     int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
0c1a9eda
     int64_t data_count;
85f07f22
     /* feed input */
     int feed_fd;
     /* input format handling */
     AVFormatContext *fmt_in;
c3f58185
     int64_t start_time;            /* In milliseconds - this wraps fairly often */
0c1a9eda
     int64_t first_pts;            /* initial pts value */
e240a0bb
     int64_t cur_pts;             /* current pts value from the stream in us */
     int64_t cur_frame_duration;  /* duration of the current frame in us */
     int cur_frame_bytes;       /* output frame size, needed to compute
                                   the time at which we send each
                                   packet */
     int pts_stream_index;        /* stream we choose as clock reference */
     int64_t cur_clock;           /* current clock reference value in us */
85f07f22
     /* output format handling */
     struct FFStream *stream;
cde25790
     /* -1 is invalid stream */
     int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
     int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
     int switch_pending;
2effd274
     AVFormatContext fmt_ctx; /* instance of FFStream for one user */
85f07f22
     int last_packet_sent; /* true if last data packet was sent */
7434ba6d
     int suppress_log;
5eb765ef
     DataRateData datarate;
3120d2a2
     int wmp_client_id;
7434ba6d
     char protocol[16];
     char method[16];
     char url[128];
cde25790
     int buffer_size;
0c1a9eda
     uint8_t *buffer;
2effd274
     int is_packetized; /* if true, the stream is packetized */
     int packet_stream_index; /* current stream for output in state machine */
115329f1
 
2effd274
     /* RTSP state specific */
0c1a9eda
     uint8_t *pb_buffer; /* XXX: use that in all the code */
ae628ec1
     AVIOContext *pb;
2effd274
     int seq; /* RTSP sequence number */
115329f1
 
2effd274
     /* RTP state specific */
90abbdba
     enum RTSPLowerTransport rtp_protocol;
2effd274
     char session_id[32]; /* session id */
     AVFormatContext *rtp_ctx[MAX_STREAMS];
e240a0bb
 
bc351386
     /* RTP/UDP specific */
     URLContext *rtp_handles[MAX_STREAMS];
 
     /* RTP/TCP specific */
     struct HTTPContext *rtsp_c;
     uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
85f07f22
 } HTTPContext;
 
 /* each generated stream is described here */
 enum StreamType {
     STREAM_TYPE_LIVE,
     STREAM_TYPE_STATUS,
cde25790
     STREAM_TYPE_REDIRECT,
85f07f22
 };
 
8256c0a3
 enum IPAddressAction {
     IP_ALLOW = 1,
     IP_DENY,
 };
 
 typedef struct IPAddressACL {
     struct IPAddressACL *next;
     enum IPAddressAction action;
efa04ce2
     /* These are in host order */
8256c0a3
     struct in_addr first;
     struct in_addr last;
 } IPAddressACL;
 
85f07f22
 /* description of each stream of the ffserver.conf file */
 typedef struct FFStream {
     enum StreamType stream_type;
     char filename[1024];     /* stream filename */
2effd274
     struct FFStream *feed;   /* feed we are using (can be null if
                                 coming from file) */
50f2dfad
     AVDictionary *in_opts;   /* input parameters */
9985710a
     AVDictionary *metadata;  /* metadata to set on the stream */
e240a0bb
     AVInputFormat *ifmt;       /* if non NULL, force input format */
bd7cf6ad
     AVOutputFormat *fmt;
8256c0a3
     IPAddressACL *acl;
58f48adb
     char dynamic_acl[1024];
85f07f22
     int nb_streams;
42a63c6a
     int prebuffer;      /* Number of millseconds early to start */
c3f58185
     int64_t max_time;      /* Number of milliseconds to run */
79c4ea3c
     int send_on_key;
85f07f22
     AVStream *streams[MAX_STREAMS];
     int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
     char feed_filename[1024]; /* file name of the feed storage, or
                                  input file name for a stream */
cde25790
     pid_t pid;  /* Of ffmpeg process */
5eb765ef
     time_t pid_start;  /* Of ffmpeg process */
cde25790
     char **child_argv;
85f07f22
     struct FFStream *next;
177d2564
     unsigned bandwidth; /* bandwidth, in kbits/s */
2effd274
     /* RTSP options */
     char *rtsp_option;
829ac53d
     /* multicast specific */
     int is_multicast;
     struct in_addr multicast_ip;
     int multicast_port; /* first port used for multicast */
6edd6884
     int multicast_ttl;
     int loop; /* if true, send the stream in loops (only meaningful if file) */
829ac53d
 
85f07f22
     /* feed specific */
2effd274
     int feed_opened;     /* true if someone is writing to the feed */
85f07f22
     int is_feed;         /* true if it is a feed */
e322ea48
     int readonly;        /* True if writing is prohibited to the file */
861ec13a
     int truncate;        /* True if feeder connection truncate the feed file */
a6e14edd
     int conns_served;
0c1a9eda
     int64_t bytes_served;
6b0bdc75
     int64_t feed_max_size;      /* maximum storage size, zero means unlimited */
8bfb108b
     int64_t feed_write_index;   /* current write position in feed (it wraps around) */
0c1a9eda
     int64_t feed_size;          /* current size of feed */
85f07f22
     struct FFStream *next_feed;
 } FFStream;
 
 typedef struct FeedData {
     long long data_count;
8bfb108b
     float avg_frame_size;   /* frame size averaged over last frames with exponential mean */
85f07f22
 } FeedData;
 
18405874
 static struct sockaddr_in my_http_addr;
 static struct sockaddr_in my_rtsp_addr;
2effd274
 
33f5e2ec
 static char logfilename[1024];
 static HTTPContext *first_http_ctx;
 static FFStream *first_feed;   /* contains only feeds */
 static FFStream *first_stream; /* contains all streams, including feeds */
85f07f22
 
2effd274
 static void new_connection(int server_fd, int is_rtsp);
 static void close_connection(HTTPContext *c);
 
 /* HTTP handling */
 static int handle_connection(HTTPContext *c);
85f07f22
 static int http_parse_request(HTTPContext *c);
5eb765ef
 static int http_send_data(HTTPContext *c);
dca21085
 static void compute_status(HTTPContext *c);
85f07f22
 static int open_input_stream(HTTPContext *c, const char *info);
 static int http_start_receive_data(HTTPContext *c);
 static int http_receive_data(HTTPContext *c);
2effd274
 
 /* RTSP handling */
 static int rtsp_parse_request(HTTPContext *c);
 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
0df65975
 static void rtsp_cmd_options(HTTPContext *c, const char *url);
a9e534d5
 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
87079bd0
 static void rtsp_cmd_interrupt(HTTPContext *c, const char *url, RTSPMessageHeader *h, int pause_only);
2effd274
 
829ac53d
 /* SDP handling */
115329f1
 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
829ac53d
                                    struct in_addr my_ip);
 
2effd274
 /* RTP handling */
115329f1
 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
bc351386
                                        FFStream *stream, const char *session_id,
90abbdba
                                        enum RTSPLowerTransport rtp_protocol);
115329f1
 static int rtp_new_av_stream(HTTPContext *c,
bc351386
                              int stream_index, struct sockaddr_in *dest_addr,
                              HTTPContext *rtsp_c);
85f07f22
 
cde25790
 static const char *my_program_name;
 
19c41c6d
 static const char *config_filename;
6675a5bf
 
2ac887ba
 static int ffserver_debug;
 static int no_launch;
5eb765ef
 static int need_to_start_children;
2ac887ba
 
1c9ff179
 /* maximum number of simultaneous HTTP connections */
 static unsigned int nb_max_http_connections = 2000;
4af92de6
 static unsigned int nb_max_connections = 5;
 static unsigned int nb_connections;
85f07f22
 
f69bb0cc
 static uint64_t max_bandwidth = 1000;
1ad8289e
 static uint64_t current_bandwidth;
42a63c6a
 
c3f58185
 static int64_t cur_time;           // Making this global saves on passing it around everywhere
5eb765ef
 
042819c5
 static AVLFG random_state;
1df93ae9
 
85f07f22
 static FILE *logfile = NULL;
 
885739f3
 static void htmlstrip(char *s) {
     while (s && *s) {
         s += strspn(s, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,. ");
         if (*s)
             *s++ = '?';
     }
 }
 
3b20eb25
 static int64_t ffm_read_write_index(int fd)
 {
     uint8_t buf[8];
 
71bc8c95
     if (lseek(fd, 8, SEEK_SET) < 0)
         return AVERROR(EIO);
3b20eb25
     if (read(fd, buf, 8) != 8)
         return AVERROR(EIO);
     return AV_RB64(buf);
 }
 
 static int ffm_write_write_index(int fd, int64_t pos)
 {
     uint8_t buf[8];
     int i;
 
     for(i=0;i<8;i++)
         buf[i] = (pos >> (56 - i * 8)) & 0xff;
378a5b9c
     if (lseek(fd, 8, SEEK_SET) < 0)
         return AVERROR(EIO);
3b20eb25
     if (write(fd, buf, 8) != 8)
         return AVERROR(EIO);
     return 8;
 }
 
 static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
                                 int64_t file_size)
 {
     FFMContext *ffm = s->priv_data;
     ffm->write_index = pos;
     ffm->file_size = file_size;
 }
 
22f73dcc
 /* FIXME: make ffserver work with IPv6 */
 /* resolve host with also IP address parsing */
 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
 {
 
     if (!ff_inet_aton(hostname, sin_addr)) {
 #if HAVE_GETADDRINFO
         struct addrinfo *ai, *cur;
a92be9b8
         struct addrinfo hints = { 0 };
22f73dcc
         hints.ai_family = AF_INET;
         if (getaddrinfo(hostname, NULL, &hints, &ai))
             return -1;
         /* getaddrinfo returns a linked list of addrinfo structs.
          * Even if we set ai_family = AF_INET above, make sure
          * that the returned one actually is of the correct type. */
         for (cur = ai; cur; cur = cur->ai_next) {
             if (cur->ai_family == AF_INET) {
                 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
                 freeaddrinfo(ai);
                 return 0;
             }
         }
         freeaddrinfo(ai);
         return -1;
 #else
         struct hostent *hp;
         hp = gethostbyname(hostname);
         if (!hp)
             return -1;
         memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
 #endif
     }
     return 0;
 }
 
4a595cff
 static char *ctime1(char *buf2, int buf_size)
9fd3442f
 {
     time_t ti;
     char *p;
 
     ti = time(NULL);
     p = ctime(&ti);
4a595cff
     av_strlcpy(buf2, p, buf_size);
9fd3442f
     p = buf2 + strlen(p) - 1;
     if (*p == '\n')
         *p = '\0';
     return buf2;
 }
 
bcd3ce59
 static void http_vlog(const char *fmt, va_list vargs)
85f07f22
 {
124ed1c0
     static int print_prefix = 1;
7434ba6d
     if (logfile) {
124ed1c0
         if (print_prefix) {
9fd3442f
             char buf[32];
4a595cff
             ctime1(buf, sizeof(buf));
9fd3442f
             fprintf(logfile, "%s ", buf);
124ed1c0
         }
         print_prefix = strstr(fmt, "\n") != NULL;
bcd3ce59
         vfprintf(logfile, fmt, vargs);
7434ba6d
         fflush(logfile);
     }
bcd3ce59
 }
 
efa6ce99
 #ifdef __GNUC__
 __attribute__ ((format (printf, 1, 2)))
 #endif
 static void http_log(const char *fmt, ...)
bcd3ce59
 {
     va_list vargs;
     va_start(vargs, fmt);
     http_vlog(fmt, vargs);
     va_end(vargs);
 }
 
 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
 {
     static int print_prefix = 1;
     AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
49ceb58b
     if (level > av_log_get_level())
bcd3ce59
         return;
     if (print_prefix && avc)
59e7894c
         http_log("[%s @ %p]", avc->item_name(ptr), ptr);
bcd3ce59
     print_prefix = strstr(fmt, "\n") != NULL;
     http_vlog(fmt, vargs);
85f07f22
 }
 
6edd6884
 static void log_connection(HTTPContext *c)
 {
115329f1
     if (c->suppress_log)
6edd6884
         return;
 
82e0be62
     http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
              inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
6edd6884
              c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
cde25790
 }
 
0c1a9eda
 static void update_datarate(DataRateData *drd, int64_t count)
5eb765ef
 {
     if (!drd->time1 && !drd->count1) {
         drd->time1 = drd->time2 = cur_time;
         drd->count1 = drd->count2 = count;
eeffbdea
     } else if (cur_time - drd->time2 > 5000) {
33a4ecbe
         drd->time1 = drd->time2;
         drd->count1 = drd->count2;
         drd->time2 = cur_time;
         drd->count2 = count;
5eb765ef
     }
 }
 
 /* In bytes per second */
0c1a9eda
 static int compute_datarate(DataRateData *drd, int64_t count)
5eb765ef
 {
     if (cur_time == drd->time1)
         return 0;
115329f1
 
5eb765ef
     return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
 }
 
a782f209
 
cde25790
 static void start_children(FFStream *feed)
 {
2ac887ba
     if (no_launch)
         return;
 
cde25790
     for (; feed; feed = feed->next) {
5eb765ef
         if (feed->child_argv && !feed->pid) {
             feed->pid_start = time(0);
 
cde25790
             feed->pid = fork();
 
             if (feed->pid < 0) {
b4befb99
                 http_log("Unable to create children\n");
cde25790
                 exit(1);
             }
             if (!feed->pid) {
                 /* In child */
                 char pathname[1024];
                 char *slash;
                 int i;
 
47ba4728
                 /* replace "ffserver" with "ffmpeg" in the path of current
                  * program. Ignore user provided path */
40444a59
                 av_strlcpy(pathname, my_program_name, sizeof(pathname));
                 slash = strrchr(pathname, '/');
                 if (!slash)
                     slash = pathname;
                 else
                     slash++;
                 strcpy(slash, "ffmpeg");
 
da9cea77
                 http_log("Launch command line: ");
8bf61f5b
                 http_log("%s ", pathname);
                 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
                     http_log("%s ", feed->child_argv[i]);
                 http_log("\n");
40444a59
 
611c5741
                 for (i = 3; i < 256; i++)
5eb765ef
                     close(i);
cde25790
 
5eb765ef
                 if (!ffserver_debug) {
a4cd2ad8
                     if (!freopen("/dev/null", "r", stdin))
                         http_log("failed to redirect STDIN to /dev/null\n;");
                     if (!freopen("/dev/null", "w", stdout))
                         http_log("failed to redirect STDOUT to /dev/null\n;");
                     if (!freopen("/dev/null", "w", stderr))
                         http_log("failed to redirect STDERR to /dev/null\n;");
fc5a905a
                 }
d6562d2c
 
a4d70941
                 signal(SIGPIPE, SIG_DFL);
 
cde25790
                 execvp(pathname, feed->child_argv);
 
                 _exit(1);
             }
         }
     }
7434ba6d
 }
 
2effd274
 /* open a listening socket */
 static int socket_open_listen(struct sockaddr_in *my_addr)
85f07f22
 {
2effd274
     int server_fd, tmp;
85f07f22
 
     server_fd = socket(AF_INET,SOCK_STREAM,0);
     if (server_fd < 0) {
         perror ("socket");
         return -1;
     }
115329f1
 
85f07f22
     tmp = 1;
     setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
 
f5e717f3
     my_addr->sin_family = AF_INET;
2effd274
     if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
b17d099d
         char bindmsg[32];
         snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
         perror (bindmsg);
d96633bb
         closesocket(server_fd);
85f07f22
         return -1;
     }
115329f1
 
85f07f22
     if (listen (server_fd, 5) < 0) {
         perror ("listen");
d96633bb
         closesocket(server_fd);
85f07f22
         return -1;
     }
ba472aaf
     ff_socket_nonblock(server_fd, 1);
2effd274
 
     return server_fd;
 }
 
6edd6884
 /* start all multicast streams */
 static void start_multicast(void)
 {
     FFStream *stream;
     char session_id[32];
     HTTPContext *rtp_c;
a04698c4
     struct sockaddr_in dest_addr = {0};
6edd6884
     int default_port, stream_index;
 
     default_port = 6000;
     for(stream = first_stream; stream != NULL; stream = stream->next) {
         if (stream->is_multicast) {
d40c0e4a
             unsigned random0 = av_lfg_get(&random_state);
             unsigned random1 = av_lfg_get(&random_state);
6edd6884
             /* open the RTP connection */
1df93ae9
             snprintf(session_id, sizeof(session_id), "%08x%08x",
d40c0e4a
                      random0, random1);
6edd6884
 
             /* choose a port if none given */
             if (stream->multicast_port == 0) {
                 stream->multicast_port = default_port;
                 default_port += 100;
             }
 
             dest_addr.sin_family = AF_INET;
             dest_addr.sin_addr = stream->multicast_ip;
             dest_addr.sin_port = htons(stream->multicast_port);
 
115329f1
             rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
90abbdba
                                        RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
611c5741
             if (!rtp_c)
6edd6884
                 continue;
611c5741
 
6edd6884
             if (open_input_stream(rtp_c, "") < 0) {
b4befb99
                 http_log("Could not open input stream for stream '%s'\n",
                          stream->filename);
6edd6884
                 continue;
             }
 
             /* open each RTP stream */
115329f1
             for(stream_index = 0; stream_index < stream->nb_streams;
6edd6884
                 stream_index++) {
115329f1
                 dest_addr.sin_port = htons(stream->multicast_port +
6edd6884
                                            2 * stream_index);
bc351386
                 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
b4befb99
                     http_log("Could not open output stream '%s/streamid=%d'\n",
                              stream->filename, stream_index);
0fa45e19
                     exit(1);
6edd6884
                 }
             }
 
             rtp_c->state = HTTPSTATE_SEND_DATA;
         }
     }
 }
2effd274
 
47ba4728
 /* main loop of the HTTP server */
2effd274
 static int http_server(void)
 {
d2a1ea1d
     int server_fd = 0, rtsp_server_fd = 0;
958d98cc
     int ret, delay;
1c9ff179
     struct pollfd *poll_table, *poll_entry;
2effd274
     HTTPContext *c, *c_next;
85f07f22
 
a7f361eb
     if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
1c9ff179
         http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
         return -1;
     }
 
d2a1ea1d
     if (my_http_addr.sin_port) {
2b9cd1e7
         server_fd = socket_open_listen(&my_http_addr);
         if (server_fd < 0)
             return -1;
d2a1ea1d
     }
2effd274
 
d2a1ea1d
     if (my_rtsp_addr.sin_port) {
2b9cd1e7
         rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
         if (rtsp_server_fd < 0)
             return -1;
d2a1ea1d
     }
 
     if (!rtsp_server_fd && !server_fd) {
         http_log("HTTP and RTSP disabled.\n");
         return -1;
     }
115329f1
 
a3341b9d
     http_log("FFserver started.\n");
85f07f22
 
cde25790
     start_children(first_feed);
 
6edd6884
     start_multicast();
 
85f07f22
     for(;;) {
         poll_entry = poll_table;
d2a1ea1d
         if (server_fd) {
2b9cd1e7
             poll_entry->fd = server_fd;
             poll_entry->events = POLLIN;
             poll_entry++;
d2a1ea1d
         }
         if (rtsp_server_fd) {
2b9cd1e7
             poll_entry->fd = rtsp_server_fd;
             poll_entry->events = POLLIN;
             poll_entry++;
d2a1ea1d
         }
2effd274
 
85f07f22
         /* wait for events on each HTTP handle */
         c = first_http_ctx;
2effd274
         delay = 1000;
85f07f22
         while (c != NULL) {
             int fd;
             fd = c->fd;
             switch(c->state) {
2effd274
             case HTTPSTATE_SEND_HEADER:
             case RTSPSTATE_SEND_REPLY:
bc351386
             case RTSPSTATE_SEND_PACKET:
85f07f22
                 c->poll_entry = poll_entry;
                 poll_entry->fd = fd;
2effd274
                 poll_entry->events = POLLOUT;
85f07f22
                 poll_entry++;
                 break;
             case HTTPSTATE_SEND_DATA_HEADER:
             case HTTPSTATE_SEND_DATA:
             case HTTPSTATE_SEND_DATA_TRAILER:
2effd274
                 if (!c->is_packetized) {
47ba4728
                     /* for TCP, we output as much as we can
                      * (may need to put a limit) */
2effd274
                     c->poll_entry = poll_entry;
                     poll_entry->fd = fd;
                     poll_entry->events = POLLOUT;
                     poll_entry++;
                 } else {
e240a0bb
                     /* when ffserver is doing the timing, we work by
47ba4728
                        looking at which packet needs to be sent every
e240a0bb
                        10 ms */
958d98cc
                     /* one tick wait XXX: 10 ms assumed */
                     if (delay > 10)
                         delay = 10;
2effd274
                 }
85f07f22
                 break;
2effd274
             case HTTPSTATE_WAIT_REQUEST:
85f07f22
             case HTTPSTATE_RECEIVE_DATA:
             case HTTPSTATE_WAIT_FEED:
2effd274
             case RTSPSTATE_WAIT_REQUEST:
85f07f22
                 /* need to catch errors */
                 c->poll_entry = poll_entry;
                 poll_entry->fd = fd;
a6e14edd
                 poll_entry->events = POLLIN;/* Maybe this will work */
85f07f22
                 poll_entry++;
                 break;
             default:
                 c->poll_entry = NULL;
                 break;
             }
             c = c->next;
         }
 
         /* wait for an event on one connection. We poll at least every
            second to handle timeouts */
         do {
2effd274
             ret = poll(poll_table, poll_entry - poll_table, delay);
28c4741a
             if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(EINTR))
53e2f9ca
                 return -1;
e8d658df
         } while (ret < 0);
115329f1
 
c3f58185
         cur_time = av_gettime() / 1000;
85f07f22
 
5eb765ef
         if (need_to_start_children) {
             need_to_start_children = 0;
             start_children(first_feed);
         }
 
85f07f22
         /* now handle the events */
2effd274
         for(c = first_http_ctx; c != NULL; c = c_next) {
             c_next = c->next;
             if (handle_connection(c) < 0) {
7434ba6d
                 log_connection(c);
db93c2d0
                 /* close and free the connection */
2effd274
                 close_connection(c);
85f07f22
             }
         }
 
         poll_entry = poll_table;
d2a1ea1d
         if (server_fd) {
2b9cd1e7
             /* new HTTP connection request ? */
             if (poll_entry->revents & POLLIN)
                 new_connection(server_fd, 0);
             poll_entry++;
d2a1ea1d
         }
         if (rtsp_server_fd) {
2b9cd1e7
             /* new RTSP connection request ? */
             if (poll_entry->revents & POLLIN)
                 new_connection(rtsp_server_fd, 1);
d2a1ea1d
         }
85f07f22
     }
 }
 
2effd274
 /* start waiting for a new HTTP/RTSP request */
 static void start_wait_request(HTTPContext *c, int is_rtsp)
85f07f22
 {
2effd274
     c->buffer_ptr = c->buffer;
     c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
 
     if (is_rtsp) {
         c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
         c->state = RTSPSTATE_WAIT_REQUEST;
     } else {
         c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
         c->state = HTTPSTATE_WAIT_REQUEST;
     }
 }
 
0bdd8b85
 static void http_send_too_busy_reply(int fd)
 {
9c6af3a3
     char buffer[400];
0bdd8b85
     int len = snprintf(buffer, sizeof(buffer),
2a22187f
                        "HTTP/1.0 503 Server too busy\r\n"
0bdd8b85
                        "Content-type: text/html\r\n"
                        "\r\n"
                        "<html><head><title>Too busy</title></head><body>\r\n"
                        "<p>The server is too busy to serve your request at this time.</p>\r\n"
cc72d52d
                        "<p>The number of current connections is %u, and this exceeds the limit of %u.</p>\r\n"
0bdd8b85
                        "</body></html>\r\n",
                        nb_connections, nb_max_connections);
9c6af3a3
     av_assert0(len < sizeof(buffer));
0bdd8b85
     send(fd, buffer, len, 0);
 }
 
 
2effd274
 static void new_connection(int server_fd, int is_rtsp)
 {
     struct sockaddr_in from_addr;
cc64ec57
     socklen_t len;
     int fd;
2effd274
     HTTPContext *c = NULL;
 
     len = sizeof(from_addr);
115329f1
     fd = accept(server_fd, (struct sockaddr *)&from_addr,
2effd274
                 &len);
050056d0
     if (fd < 0) {
         http_log("error during accept %s\n", strerror(errno));
2effd274
         return;
050056d0
     }
ba472aaf
     ff_socket_nonblock(fd, 1);
2effd274
 
0bdd8b85
     if (nb_connections >= nb_max_connections) {
         http_send_too_busy_reply(fd);
2effd274
         goto fail;
0bdd8b85
     }
115329f1
 
2effd274
     /* add a new connection */
     c = av_mallocz(sizeof(HTTPContext));
     if (!c)
         goto fail;
115329f1
 
2effd274
     c->fd = fd;
     c->poll_entry = NULL;
     c->from_addr = from_addr;
     c->buffer_size = IOBUFFER_INIT_SIZE;
     c->buffer = av_malloc(c->buffer_size);
     if (!c->buffer)
         goto fail;
8bc80f8b
 
     c->next = first_http_ctx;
     first_http_ctx = c;
2effd274
     nb_connections++;
115329f1
 
2effd274
     start_wait_request(c, is_rtsp);
 
     return;
 
  fail:
     if (c) {
         av_free(c->buffer);
         av_free(c);
     }
d96633bb
     closesocket(fd);
2effd274
 }
 
 static void close_connection(HTTPContext *c)
 {
     HTTPContext **cp, *c1;
     int i, nb_streams;
     AVFormatContext *ctx;
     URLContext *h;
     AVStream *st;
 
     /* remove connection from list */
     cp = &first_http_ctx;
     while ((*cp) != NULL) {
         c1 = *cp;
611c5741
         if (c1 == c)
2effd274
             *cp = c->next;
611c5741
         else
2effd274
             cp = &c1->next;
     }
 
bc351386
     /* remove references, if any (XXX: do it faster) */
     for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
         if (c1->rtsp_c == c)
             c1->rtsp_c = NULL;
     }
 
2effd274
     /* remove connection associated resources */
     if (c->fd >= 0)
d96633bb
         closesocket(c->fd);
2effd274
     if (c->fmt_in) {
         /* close each frame parser */
         for(i=0;i<c->fmt_in->nb_streams;i++) {
             st = c->fmt_in->streams[i];
611c5741
             if (st->codec->codec)
01f4895c
                 avcodec_close(st->codec);
2effd274
         }
cd3716b9
         avformat_close_input(&c->fmt_in);
2effd274
     }
 
     /* free RTP output streams if any */
     nb_streams = 0;
115329f1
     if (c->stream)
2effd274
         nb_streams = c->stream->nb_streams;
115329f1
 
2effd274
     for(i=0;i<nb_streams;i++) {
         ctx = c->rtp_ctx[i];
         if (ctx) {
             av_write_trailer(ctx);
d2d67e42
             av_dict_free(&ctx->metadata);
ea4f8aab
             av_free(ctx->streams[0]);
2effd274
             av_free(ctx);
         }
         h = c->rtp_handles[i];
611c5741
         if (h)
b263bf66
             ffurl_close(h);
2effd274
     }
115329f1
 
b88ba823
     ctx = &c->fmt_ctx;
 
637b638e
     if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
87638494
         if (ctx->oformat) {
             /* prepare header */
b92c5452
             if (avio_open_dyn_buf(&ctx->pb) >= 0) {
87638494
                 av_write_trailer(ctx);
f8b06be9
                 av_freep(&c->pb_buffer);
6dc7d80d
                 avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
87638494
             }
         }
     }
 
115329f1
     for(i=0; i<ctx->nb_streams; i++)
0bd53967
         av_free(ctx->streams[i]);
0e482a8e
     av_freep(&ctx->streams);
     av_freep(&ctx->priv_data);
f0ef6240
 
edfdd798
     if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
6edd6884
         current_bandwidth -= c->stream->bandwidth;
5400e092
 
     /* signal that there is no feed if we are the feeder socket */
     if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
         c->stream->feed_opened = 0;
         close(c->feed_fd);
     }
 
2effd274
     av_freep(&c->pb_buffer);
bc351386
     av_freep(&c->packet_buffer);
2effd274
     av_free(c->buffer);
     av_free(c);
     nb_connections--;
 }
 
 static int handle_connection(HTTPContext *c)
 {
     int len, ret;
115329f1
 
85f07f22
     switch(c->state) {
     case HTTPSTATE_WAIT_REQUEST:
2effd274
     case RTSPSTATE_WAIT_REQUEST:
85f07f22
         /* timeout ? */
         if ((c->timeout - cur_time) < 0)
             return -1;
         if (c->poll_entry->revents & (POLLERR | POLLHUP))
             return -1;
 
         /* no need to read if no events */
         if (!(c->poll_entry->revents & POLLIN))
             return 0;
         /* read the data */
1bc1cfdd
     read_loop:
c60202df
         len = recv(c->fd, c->buffer_ptr, 1, 0);
85f07f22
         if (len < 0) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(EINTR))
85f07f22
                 return -1;
         } else if (len == 0) {
             return -1;
         } else {
94d9ad5f
             /* search for end of request. */
0c1a9eda
             uint8_t *ptr;
85f07f22
             c->buffer_ptr += len;
             ptr = c->buffer_ptr;
             if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
                 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
                 /* request found : parse it and reply */
2effd274
                 if (c->state == HTTPSTATE_WAIT_REQUEST) {
                     ret = http_parse_request(c);
                 } else {
                     ret = rtsp_parse_request(c);
                 }
                 if (ret < 0)
85f07f22
                     return -1;
             } else if (ptr >= c->buffer_end) {
                 /* request too long: cannot do anything */
                 return -1;
1bc1cfdd
             } else goto read_loop;
85f07f22
         }
         break;
 
     case HTTPSTATE_SEND_HEADER:
         if (c->poll_entry->revents & (POLLERR | POLLHUP))
             return -1;
 
2effd274
         /* no need to write if no events */
85f07f22
         if (!(c->poll_entry->revents & POLLOUT))
             return 0;
c60202df
         len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
85f07f22
         if (len < 0) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(EINTR)) {
ba6186d6
                 goto close_connection;
85f07f22
             }
         } else {
             c->buffer_ptr += len;
2e04edb3
             if (c->stream)
                 c->stream->bytes_served += len;
a6e14edd
             c->data_count += len;
85f07f22
             if (c->buffer_ptr >= c->buffer_end) {
2effd274
                 av_freep(&c->pb_buffer);
85f07f22
                 /* if error, exit */
611c5741
                 if (c->http_error)
85f07f22
                     return -1;
47ba4728
                 /* all the buffer was sent : synchronize to the incoming
                  * stream */
85f07f22
                 c->state = HTTPSTATE_SEND_DATA_HEADER;
                 c->buffer_ptr = c->buffer_end = c->buffer;
             }
         }
         break;
 
     case HTTPSTATE_SEND_DATA:
     case HTTPSTATE_SEND_DATA_HEADER:
     case HTTPSTATE_SEND_DATA_TRAILER:
2effd274
         /* for packetized output, we consider we can always write (the
47ba4728
            input streams set the speed). It may be better to verify
2effd274
            that we do not rely too much on the kernel queues */
         if (!c->is_packetized) {
             if (c->poll_entry->revents & (POLLERR | POLLHUP))
                 return -1;
115329f1
 
2effd274
             /* no need to read if no events */
             if (!(c->poll_entry->revents & POLLOUT))
                 return 0;
         }
5eb765ef
         if (http_send_data(c) < 0)
85f07f22
             return -1;
638831aa
         /* close connection if trailer sent */
         if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
             return -1;
85f07f22
         break;
     case HTTPSTATE_RECEIVE_DATA:
         /* no need to read if no events */
         if (c->poll_entry->revents & (POLLERR | POLLHUP))
             return -1;
         if (!(c->poll_entry->revents & POLLIN))
             return 0;
         if (http_receive_data(c) < 0)
             return -1;
         break;
     case HTTPSTATE_WAIT_FEED:
         /* no need to read if no events */
a6e14edd
         if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
85f07f22
             return -1;
 
         /* nothing to do, we'll be waken up by incoming feed packets */
         break;
2effd274
 
     case RTSPSTATE_SEND_REPLY:
ba6186d6
         if (c->poll_entry->revents & (POLLERR | POLLHUP))
             goto close_connection;
2effd274
         /* no need to write if no events */
         if (!(c->poll_entry->revents & POLLOUT))
             return 0;
c60202df
         len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2effd274
         if (len < 0) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(EINTR)) {
ba6186d6
                 goto close_connection;
2effd274
             }
         } else {
             c->buffer_ptr += len;
             c->data_count += len;
             if (c->buffer_ptr >= c->buffer_end) {
                 /* all the buffer was sent : wait for a new request */
                 av_freep(&c->pb_buffer);
                 start_wait_request(c, 1);
             }
         }
         break;
bc351386
     case RTSPSTATE_SEND_PACKET:
         if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
             av_freep(&c->packet_buffer);
             return -1;
         }
         /* no need to write if no events */
         if (!(c->poll_entry->revents & POLLOUT))
             return 0;
c60202df
         len = send(c->fd, c->packet_buffer_ptr,
                     c->packet_buffer_end - c->packet_buffer_ptr, 0);
bc351386
         if (len < 0) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(EINTR)) {
bc351386
                 /* error : close connection */
                 av_freep(&c->packet_buffer);
                 return -1;
             }
         } else {
             c->packet_buffer_ptr += len;
             if (c->packet_buffer_ptr >= c->packet_buffer_end) {
                 /* all the buffer was sent : wait for a new request */
                 av_freep(&c->packet_buffer);
                 c->state = RTSPSTATE_WAIT_REQUEST;
             }
         }
         break;
2effd274
     case HTTPSTATE_READY:
         /* nothing to do */
         break;
85f07f22
     default:
         return -1;
     }
     return 0;
ba6186d6
 
 close_connection:
     av_freep(&c->pb_buffer);
     return -1;
85f07f22
 }
 
3120d2a2
 static int extract_rates(char *rates, int ratelen, const char *request)
 {
     const char *p;
 
     for (p = request; *p && *p != '\r' && *p != '\n'; ) {
bb3244de
         if (av_strncasecmp(p, "Pragma:", 7) == 0) {
3120d2a2
             const char *q = p + 7;
 
88d55b82
             while (*q && *q != '\n' && av_isspace(*q))
3120d2a2
                 q++;
 
bb3244de
             if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
3120d2a2
                 int stream_no;
                 int rate_no;
 
                 q += 20;
 
cde25790
                 memset(rates, 0xff, ratelen);
3120d2a2
 
                 while (1) {
                     while (*q && *q != '\n' && *q != ':')
                         q++;
 
611c5741
                     if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
3120d2a2
                         break;
611c5741
 
3120d2a2
                     stream_no--;
611c5741
                     if (stream_no < ratelen && stream_no >= 0)
3120d2a2
                         rates[stream_no] = rate_no;
 
88d55b82
                     while (*q && *q != '\n' && !av_isspace(*q))
3120d2a2
                         q++;
                 }
 
                 return 1;
             }
         }
         p = strchr(p, '\n');
         if (!p)
             break;
 
         p++;
     }
 
     return 0;
 }
 
cde25790
 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
3120d2a2
 {
     int i;
cde25790
     int best_bitrate = 100000000;
     int best = -1;
 
     for (i = 0; i < feed->nb_streams; i++) {
01f4895c
         AVCodecContext *feed_codec = feed->streams[i]->codec;
cde25790
 
         if (feed_codec->codec_id != codec->codec_id ||
             feed_codec->sample_rate != codec->sample_rate ||
             feed_codec->width != codec->width ||
611c5741
             feed_codec->height != codec->height)
cde25790
             continue;
 
         /* Potential stream */
 
115329f1
         /* We want the fastest stream less than bit_rate, or the slowest
cde25790
          * faster than bit_rate
          */
 
         if (feed_codec->bit_rate <= bit_rate) {
             if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
                 best_bitrate = feed_codec->bit_rate;
                 best = i;
             }
         } else {
             if (feed_codec->bit_rate < best_bitrate) {
                 best_bitrate = feed_codec->bit_rate;
                 best = i;
             }
         }
     }
 
     return best;
 }
 
 static int modify_current_stream(HTTPContext *c, char *rates)
 {
     int i;
     FFStream *req = c->stream;
     int action_required = 0;
3120d2a2
 
001bcd29
     /* Not much we can do for a feed */
     if (!req->feed)
         return 0;
 
3120d2a2
     for (i = 0; i < req->nb_streams; i++) {
01f4895c
         AVCodecContext *codec = req->streams[i]->codec;
3120d2a2
 
         switch(rates[i]) {
             case 0:
cde25790
                 c->switch_feed_streams[i] = req->feed_streams[i];
3120d2a2
                 break;
             case 1:
cde25790
                 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
3120d2a2
                 break;
             case 2:
cde25790
                 /* Wants off or slow */
                 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
 #ifdef WANTS_OFF
                 /* This doesn't work well when it turns off the only stream! */
                 c->switch_feed_streams[i] = -2;
                 c->feed_streams[i] = -2;
 #endif
3120d2a2
                 break;
         }
 
cde25790
         if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
             action_required = 1;
     }
3120d2a2
 
cde25790
     return action_required;
 }
3120d2a2
 
2effd274
 /* XXX: factorize in utils.c ? */
 /* XXX: take care with different space meaning */
 static void skip_spaces(const char **pp)
 {
     const char *p;
     p = *pp;
     while (*p == ' ' || *p == '\t')
         p++;
     *pp = p;
 }
 
 static void get_word(char *buf, int buf_size, const char **pp)
 {
     const char *p;
     char *q;
 
     p = *pp;
     skip_spaces(&p);
     q = buf;
88d55b82
     while (!av_isspace(*p) && *p != '\0') {
2effd274
         if ((q - buf) < buf_size - 1)
             *q++ = *p;
         p++;
     }
     if (buf_size > 0)
         *q = '\0';
     *pp = p;
 }
 
c64c0a9b
 static void get_arg(char *buf, int buf_size, const char **pp)
 {
     const char *p;
     char *q;
     int quote;
 
     p = *pp;
88d55b82
     while (av_isspace(*p)) p++;
c64c0a9b
     q = buf;
     quote = 0;
     if (*p == '\"' || *p == '\'')
         quote = *p++;
     for(;;) {
         if (quote) {
             if (*p == quote)
                 break;
         } else {
88d55b82
             if (av_isspace(*p))
c64c0a9b
                 break;
         }
         if (*p == '\0')
             break;
         if ((q - buf) < buf_size - 1)
             *q++ = *p;
         p++;
     }
     *q = '\0';
     if (quote && *p == quote)
         p++;
     *pp = p;
 }
 
58f48adb
 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
                          const char *p, const char *filename, int line_num)
 {
     char arg[1024];
     IPAddressACL acl;
     int errors = 0;
 
     get_arg(arg, sizeof(arg), &p);
bb3244de
     if (av_strcasecmp(arg, "allow") == 0)
58f48adb
         acl.action = IP_ALLOW;
bb3244de
     else if (av_strcasecmp(arg, "deny") == 0)
58f48adb
         acl.action = IP_DENY;
     else {
         fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
                 filename, line_num, arg);
         errors++;
     }
 
     get_arg(arg, sizeof(arg), &p);
 
     if (resolve_host(&acl.first, arg) != 0) {
         fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
                 filename, line_num, arg);
         errors++;
     } else
         acl.last = acl.first;
 
     get_arg(arg, sizeof(arg), &p);
 
     if (arg[0]) {
         if (resolve_host(&acl.last, arg) != 0) {
             fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
                     filename, line_num, arg);
             errors++;
         }
     }
 
     if (!errors) {
         IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
         IPAddressACL **naclp = 0;
 
         acl.next = 0;
         *nacl = acl;
 
         if (stream)
             naclp = &stream->acl;
         else if (feed)
             naclp = &feed->acl;
         else if (ext_acl)
             naclp = &ext_acl;
         else {
             fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
                     filename, line_num);
             errors++;
         }
 
         if (naclp) {
             while (*naclp)
                 naclp = &(*naclp)->next;
 
             *naclp = nacl;
         }
     }
 }
 
 
 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
 {
     FILE* f;
     char line[1024];
     char  cmd[1024];
     IPAddressACL *acl = NULL;
     int line_num = 0;
     const char *p;
 
     f = fopen(stream->dynamic_acl, "r");
     if (!f) {
         perror(stream->dynamic_acl);
         return NULL;
     }
 
     acl = av_mallocz(sizeof(IPAddressACL));
 
     /* Build ACL */
     for(;;) {
         if (fgets(line, sizeof(line), f) == NULL)
             break;
         line_num++;
         p = line;
88d55b82
         while (av_isspace(*p))
58f48adb
             p++;
         if (*p == '\0' || *p == '#')
             continue;
         get_arg(cmd, sizeof(cmd), &p);
 
bb3244de
         if (!av_strcasecmp(cmd, "ACL"))
58f48adb
             parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
     }
     fclose(f);
     return acl;
 }
 
 
 static void free_acl_list(IPAddressACL *in_acl)
 {
     IPAddressACL *pacl,*pacl2;
 
     pacl = in_acl;
     while(pacl) {
         pacl2 = pacl;
         pacl = pacl->next;
         av_freep(pacl2);
     }
 }
 
 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
8256c0a3
 {
     enum IPAddressAction last_action = IP_DENY;
     IPAddressACL *acl;
     struct in_addr *src = &c->from_addr.sin_addr;
2bd8416e
     unsigned long src_addr = src->s_addr;
8256c0a3
 
58f48adb
     for (acl = in_acl; acl; acl = acl->next) {
611c5741
         if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
8256c0a3
             return (acl->action == IP_ALLOW) ? 1 : 0;
         last_action = acl->action;
     }
 
     /* Nothing matched, so return not the last action */
     return (last_action == IP_DENY) ? 1 : 0;
 }
 
58f48adb
 static int validate_acl(FFStream *stream, HTTPContext *c)
 {
     int ret = 0;
     IPAddressACL *acl;
 
 
     /* if stream->acl is null validate_acl_list will return 1 */
     ret = validate_acl_list(stream->acl, c);
 
     if (stream->dynamic_acl[0]) {
         acl = parse_dynamic_acl(stream, c);
 
         ret = validate_acl_list(acl, c);
 
         free_acl_list(acl);
     }
 
     return ret;
 }
 
829ac53d
 /* compute the real filename of a file by matching it without its
47ba4728
    extensions to all the stream's filenames */
829ac53d
 static void compute_real_filename(char *filename, int max_size)
 {
     char file1[1024];
     char file2[1024];
     char *p;
     FFStream *stream;
 
     /* compute filename by matching without the file extensions */
f7d78f36
     av_strlcpy(file1, filename, sizeof(file1));
829ac53d
     p = strrchr(file1, '.');
     if (p)
         *p = '\0';
     for(stream = first_stream; stream != NULL; stream = stream->next) {
f7d78f36
         av_strlcpy(file2, stream->filename, sizeof(file2));
829ac53d
         p = strrchr(file2, '.');
         if (p)
             *p = '\0';
         if (!strcmp(file1, file2)) {
f7d78f36
             av_strlcpy(filename, stream->filename, max_size);
829ac53d
             break;
         }
     }
 }
 
 enum RedirType {
     REDIR_NONE,
     REDIR_ASX,
     REDIR_RAM,
     REDIR_ASF,
     REDIR_RTSP,
     REDIR_SDP,
 };
 
47ba4728
 /* parse HTTP request and prepare header */
85f07f22
 static int http_parse_request(HTTPContext *c)
 {
39c4afd9
     const char *p;
     char *p1;
829ac53d
     enum RedirType redir_type;
85f07f22
     char cmd[32];
bae79c04
     char info[1024], filename[1024];
85f07f22
     char url[1024], *q;
     char protocol[32];
     char msg[1024];
     const char *mime_type;
     FFStream *stream;
42a63c6a
     int i;
3120d2a2
     char ratebuf[32];
39c4afd9
     const char *useragent = 0;
85f07f22
 
     p = c->buffer;
39c4afd9
     get_word(cmd, sizeof(cmd), &p);
f7d78f36
     av_strlcpy(c->method, cmd, sizeof(c->method));
7434ba6d
 
85f07f22
     if (!strcmp(cmd, "GET"))
edfdd798
         c->post = 0;
85f07f22
     else if (!strcmp(cmd, "POST"))
edfdd798
         c->post = 1;
85f07f22
     else
         return -1;
 
39c4afd9
     get_word(url, sizeof(url), &p);
f7d78f36
     av_strlcpy(c->url, url, sizeof(c->url));
7434ba6d
 
2effd274
     get_word(protocol, sizeof(protocol), (const char **)&p);
85f07f22
     if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
         return -1;
7434ba6d
 
f7d78f36
     av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
90f9c440
 
     if (ffserver_debug)
77553ae3
         http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
115329f1
 
85f07f22
     /* find the filename and the optional info string in the request */
39c4afd9
     p1 = strchr(url, '?');
     if (p1) {
         av_strlcpy(info, p1, sizeof(info));
         *p1 = '\0';
611c5741
     } else
85f07f22
         info[0] = '\0';
 
f7d78f36
     av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
bae79c04
 
cde25790
     for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
bb3244de
         if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
cde25790
             useragent = p + 11;
88d55b82
             if (*useragent && *useragent != '\n' && av_isspace(*useragent))
cde25790
                 useragent++;
             break;
         }
         p = strchr(p, '\n');
         if (!p)
             break;
 
         p++;
     }
 
829ac53d
     redir_type = REDIR_NONE;
aa13b573
     if (av_match_ext(filename, "asx")) {
829ac53d
         redir_type = REDIR_ASX;
7434ba6d
         filename[strlen(filename)-1] = 'f';
aa13b573
     } else if (av_match_ext(filename, "asf") &&
bb3244de
         (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
cde25790
         /* if this isn't WMP or lookalike, return the redirector file */
829ac53d
         redir_type = REDIR_ASF;
aa13b573
     } else if (av_match_ext(filename, "rpm,ram")) {
829ac53d
         redir_type = REDIR_RAM;
42a63c6a
         strcpy(filename + strlen(filename)-2, "m");
aa13b573
     } else if (av_match_ext(filename, "rtsp")) {
829ac53d
         redir_type = REDIR_RTSP;
bae79c04
         compute_real_filename(filename, sizeof(filename) - 1);
aa13b573
     } else if (av_match_ext(filename, "sdp")) {
829ac53d
         redir_type = REDIR_SDP;
bae79c04
         compute_real_filename(filename, sizeof(filename) - 1);
42a63c6a
     }
115329f1
 
bae79c04
     // "redirect" / request to index.html
     if (!strlen(filename))
f7d78f36
         av_strlcpy(filename, "index.html", sizeof(filename) - 1);
bae79c04
 
85f07f22
     stream = first_stream;
     while (stream != NULL) {
8256c0a3
         if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
85f07f22
             break;
         stream = stream->next;
     }
     if (stream == NULL) {
d445a7e9
         snprintf(msg, sizeof(msg), "File '%s' not found", url);
77553ae3
         http_log("File '%s' not found\n", url);
85f07f22
         goto send_error;
     }
42a63c6a
 
cde25790
     c->stream = stream;
     memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
     memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
 
     if (stream->stream_type == STREAM_TYPE_REDIRECT) {
         c->http_error = 301;
         q = c->buffer;
1fc3e8f4
         snprintf(q, c->buffer_size,
a3aa4fed
                       "HTTP/1.0 301 Moved\r\n"
                       "Location: %s\r\n"
                       "Content-type: text/html\r\n"
                       "\r\n"
                       "<html><head><title>Moved</title></head><body>\r\n"
                       "You should be <a href=\"%s\">redirected</a>.\r\n"
                       "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1fc3e8f4
         q += strlen(q);
cde25790
         /* prepare output buffer */
         c->buffer_ptr = c->buffer;
         c->buffer_end = q;
         c->state = HTTPSTATE_SEND_HEADER;
         return 0;
     }
 
3120d2a2
     /* If this is WMP, get the rate information */
     if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
cde25790
         if (modify_current_stream(c, ratebuf)) {
37d3e066
             for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
cde25790
                 if (c->switch_feed_streams[i] >= 0)
305ca590
                     c->switch_feed_streams[i] = -1;
cde25790
             }
         }
3120d2a2
     }
 
d8f28a77
     if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
         current_bandwidth += stream->bandwidth;
 
755bfeab
     /* If already streaming this feed, do not let start another feeder. */
d0a5513b
     if (stream->feed_opened) {
         snprintf(msg, sizeof(msg), "This feed is already being received.");
77553ae3
         http_log("Feed '%s' already being received\n", stream->feed_filename);
d0a5513b
         goto send_error;
     }
 
edfdd798
     if (c->post == 0 && max_bandwidth < current_bandwidth) {
0aee2a57
         c->http_error = 503;
42a63c6a
         q = c->buffer;
1fc3e8f4
         snprintf(q, c->buffer_size,
0aee2a57
                       "HTTP/1.0 503 Server too busy\r\n"
a3aa4fed
                       "Content-type: text/html\r\n"
                       "\r\n"
                       "<html><head><title>Too busy</title></head><body>\r\n"
                       "<p>The server is too busy to serve your request at this time.</p>\r\n"
0be4b8d9
                       "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
                       "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
a3aa4fed
                       "</body></html>\r\n", current_bandwidth, max_bandwidth);
1fc3e8f4
         q += strlen(q);
42a63c6a
         /* prepare output buffer */
         c->buffer_ptr = c->buffer;
         c->buffer_end = q;
         c->state = HTTPSTATE_SEND_HEADER;
         return 0;
     }
115329f1
 
829ac53d
     if (redir_type != REDIR_NONE) {
39c4afd9
         const char *hostinfo = 0;
115329f1
 
7434ba6d
         for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
bb3244de
             if (av_strncasecmp(p, "Host:", 5) == 0) {
7434ba6d
                 hostinfo = p + 5;
                 break;
             }
             p = strchr(p, '\n');
             if (!p)
                 break;
 
             p++;
         }
 
         if (hostinfo) {
             char *eoh;
             char hostbuf[260];
 
88d55b82
             while (av_isspace(*hostinfo))
7434ba6d
                 hostinfo++;
 
             eoh = strchr(hostinfo, '\n');
             if (eoh) {
                 if (eoh[-1] == '\r')
                     eoh--;
 
                 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
                     memcpy(hostbuf, hostinfo, eoh - hostinfo);
                     hostbuf[eoh - hostinfo] = 0;
 
                     c->http_error = 200;
                     q = c->buffer;
829ac53d
                     switch(redir_type) {
                     case REDIR_ASX:
1fc3e8f4
                         snprintf(q, c->buffer_size,
a3aa4fed
                                       "HTTP/1.0 200 ASX Follows\r\n"
                                       "Content-type: video/x-ms-asf\r\n"
                                       "\r\n"
                                       "<ASX Version=\"3\">\r\n"
                                       //"<!-- Autogenerated by ffserver -->\r\n"
                                       "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
                                       "</ASX>\r\n", hostbuf, filename, info);
1fc3e8f4
                         q += strlen(q);
829ac53d
                         break;
                     case REDIR_RAM:
1fc3e8f4
                         snprintf(q, c->buffer_size,
a3aa4fed
                                       "HTTP/1.0 200 RAM Follows\r\n"
                                       "Content-type: audio/x-pn-realaudio\r\n"
                                       "\r\n"
                                       "# Autogenerated by ffserver\r\n"
                                       "http://%s/%s%s\r\n", hostbuf, filename, info);
1fc3e8f4
                         q += strlen(q);
829ac53d
                         break;
                     case REDIR_ASF:
1fc3e8f4
                         snprintf(q, c->buffer_size,
a3aa4fed
                                       "HTTP/1.0 200 ASF Redirect follows\r\n"
                                       "Content-type: video/x-ms-asf\r\n"
                                       "\r\n"
                                       "[Reference]\r\n"
                                       "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1fc3e8f4
                         q += strlen(q);
829ac53d
                         break;
                     case REDIR_RTSP:
                         {
                             char hostname[256], *p;
                             /* extract only hostname */
f7d78f36
                             av_strlcpy(hostname, hostbuf, sizeof(hostname));
829ac53d
                             p = strrchr(hostname, ':');
                             if (p)
                                 *p = '\0';
1fc3e8f4
                             snprintf(q, c->buffer_size,
a3aa4fed
                                           "HTTP/1.0 200 RTSP Redirect follows\r\n"
                                           /* XXX: incorrect mime type ? */
                                           "Content-type: application/x-rtsp\r\n"
                                           "\r\n"
                                           "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1fc3e8f4
                             q += strlen(q);
829ac53d
                         }
                         break;
                     case REDIR_SDP:
                         {
0c1a9eda
                             uint8_t *sdp_data;
cc64ec57
                             int sdp_data_size;
                             socklen_t len;
829ac53d
                             struct sockaddr_in my_addr;
 
1fc3e8f4
                             snprintf(q, c->buffer_size,
a3aa4fed
                                           "HTTP/1.0 200 OK\r\n"
                                           "Content-type: application/sdp\r\n"
                                           "\r\n");
1fc3e8f4
                             q += strlen(q);
829ac53d
 
                             len = sizeof(my_addr);
                             getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
115329f1
 
829ac53d
                             /* XXX: should use a dynamic buffer */
115329f1
                             sdp_data_size = prepare_sdp_description(stream,
                                                                     &sdp_data,
829ac53d
                                                                     my_addr.sin_addr);
                             if (sdp_data_size > 0) {
                                 memcpy(q, sdp_data, sdp_data_size);
                                 q += sdp_data_size;
                                 *q = '\0';
                                 av_free(sdp_data);
                             }
                         }
                         break;
                     default:
0f4e8165
                         abort();
829ac53d
                         break;
2effd274
                     }
7434ba6d
 
                     /* prepare output buffer */
                     c->buffer_ptr = c->buffer;
                     c->buffer_end = q;
                     c->state = HTTPSTATE_SEND_HEADER;
                     return 0;
                 }
             }
         }
 
d445a7e9
         snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
7434ba6d
         goto send_error;
85f07f22
     }
 
a6e14edd
     stream->conns_served++;
7434ba6d
 
85f07f22
     /* XXX: add there authenticate and IP match */
 
edfdd798
     if (c->post) {
85f07f22
         /* if post, it means a feed is being sent */
         if (!stream->is_feed) {
e16190fa
             /* However it might be a status report from WMP! Let us log the
              * data as it might come in handy one day. */
39c4afd9
             const char *logline = 0;
3120d2a2
             int client_id = 0;
115329f1
 
7434ba6d
             for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
bb3244de
                 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
7434ba6d
                     logline = p;
                     break;
                 }
bb3244de
                 if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
3120d2a2
                     client_id = strtol(p + 18, 0, 10);
7434ba6d
                 p = strchr(p, '\n');
                 if (!p)
                     break;
 
                 p++;
             }
 
             if (logline) {
                 char *eol = strchr(logline, '\n');
 
                 logline += 17;
 
                 if (eol) {
                     if (eol[-1] == '\r')
                         eol--;
7906085f
                     http_log("%.*s\n", (int) (eol - logline), logline);
7434ba6d
                     c->suppress_log = 1;
                 }
             }
3120d2a2
 
f190f676
 #ifdef DEBUG
cde25790
             http_log("\nGot request:\n%s\n", c->buffer);
3120d2a2
 #endif
 
             if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
                 HTTPContext *wmpc;
 
                 /* Now we have to find the client_id */
                 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
                     if (wmpc->wmp_client_id == client_id)
                         break;
                 }
 
2d563d2f
                 if (wmpc && modify_current_stream(wmpc, ratebuf))
                     wmpc->switch_pending = 1;
3120d2a2
             }
115329f1
 
d445a7e9
             snprintf(msg, sizeof(msg), "POST command not handled");
cb275dd9
             c->stream = 0;
85f07f22
             goto send_error;
         }
         if (http_start_receive_data(c) < 0) {
d445a7e9
             snprintf(msg, sizeof(msg), "could not open feed");
85f07f22
             goto send_error;
         }
         c->http_error = 0;
         c->state = HTTPSTATE_RECEIVE_DATA;
         return 0;
     }
 
f190f676
 #ifdef DEBUG
611c5741
     if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
cde25790
         http_log("\nGot request:\n%s\n", c->buffer);
3120d2a2
 #endif
 
85f07f22
     if (c->stream->stream_type == STREAM_TYPE_STATUS)
dca21085
         goto send_status;
85f07f22
 
     /* open input stream */
     if (open_input_stream(c, info) < 0) {
d445a7e9
         snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
85f07f22
         goto send_error;
     }
 
47ba4728
     /* prepare HTTP header */
1fc3e8f4
     c->buffer[0] = 0;
     av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
85f07f22
     mime_type = c->stream->fmt->mime_type;
     if (!mime_type)
087fa475
         mime_type = "application/x-octet-stream";
1fc3e8f4
     av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
85f07f22
 
     /* for asf, we need extra headers */
8256c0a3
     if (!strcmp(c->stream->fmt->name,"asf_stream")) {
3120d2a2
         /* Need to allocate a client id */
 
042819c5
         c->wmp_client_id = av_lfg_get(&random_state);
3120d2a2
 
1fc3e8f4
         av_strlcatf(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);
85f07f22
     }
1fc3e8f4
     av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
     av_strlcatf(c->buffer, c->buffer_size, "\r\n");
     q = c->buffer + strlen(c->buffer);
115329f1
 
85f07f22
     /* prepare output buffer */
     c->http_error = 0;
     c->buffer_ptr = c->buffer;
     c->buffer_end = q;
     c->state = HTTPSTATE_SEND_HEADER;
     return 0;
  send_error:
     c->http_error = 404;
     q = c->buffer;
885739f3
     htmlstrip(msg);
1fc3e8f4
     snprintf(q, c->buffer_size,
a3aa4fed
                   "HTTP/1.0 404 Not Found\r\n"
                   "Content-type: text/html\r\n"
                   "\r\n"
5e567aae
                   "<html>\n"
                   "<head><title>404 Not Found</title></head>\n"
                   "<body>%s</body>\n"
                   "</html>\n", msg);
1fc3e8f4
     q += strlen(q);
85f07f22
     /* prepare output buffer */
     c->buffer_ptr = c->buffer;
     c->buffer_end = q;
     c->state = HTTPSTATE_SEND_HEADER;
     return 0;
dca21085
  send_status:
     compute_status(c);
85f07f22
     c->http_error = 200; /* horrible : we use this value to avoid
                             going to the send data state */
     c->state = HTTPSTATE_SEND_HEADER;
     return 0;
 }
 
ae628ec1
 static void fmt_bytecount(AVIOContext *pb, int64_t count)
2ac887ba
 {
b0f29db5
     static const char suffix[] = " kMGTP";
2ac887ba
     const char *s;
 
611c5741
     for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
2ac887ba
 
d9d86e00
     avio_printf(pb, "%"PRId64"%c", count, *s);
2ac887ba
 }
 
dca21085
 static void compute_status(HTTPContext *c)
85f07f22
 {
     HTTPContext *c1;
     FFStream *stream;
2effd274
     char *p;
85f07f22
     time_t ti;
2effd274
     int i, len;
ae628ec1
     AVIOContext *pb;
cde25790
 
b92c5452
     if (avio_open_dyn_buf(&pb) < 0) {
2effd274
         /* XXX: return an error ? */
cde25790
         c->buffer_ptr = c->buffer;
2effd274
         c->buffer_end = c->buffer;
         return;
cde25790
     }
85f07f22
 
d9d86e00
     avio_printf(pb, "HTTP/1.0 200 OK\r\n");
5df2a502
     avio_printf(pb, "Content-type: text/html\r\n");
d9d86e00
     avio_printf(pb, "Pragma: no-cache\r\n");
     avio_printf(pb, "\r\n");
115329f1
 
d9d86e00
     avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
0679719d
     if (c->stream->feed_filename[0])
d9d86e00
         avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
     avio_printf(pb, "</head>\n<body>");
     avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
85f07f22
     /* format status */
d9d86e00
     avio_printf(pb, "<h2>Available Streams</h2>\n");
     avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
     avio_printf(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");
85f07f22
     stream = first_stream;
     while (stream != NULL) {
42a63c6a
         char sfilename[1024];
         char *eosf;
 
a6e14edd
         if (stream->feed != stream) {
f7d78f36
             av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
a6e14edd
             eosf = sfilename + strlen(sfilename);
             if (eosf - sfilename >= 4) {
611c5741
                 if (strcmp(eosf - 4, ".asf") == 0)
a6e14edd
                     strcpy(eosf - 4, ".asx");
611c5741
                 else if (strcmp(eosf - 3, ".rm") == 0)
a6e14edd
                     strcpy(eosf - 3, ".ram");
25e3e53d
                 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
829ac53d
                     /* generate a sample RTSP director if
                        unicast. Generate an SDP redirector if
                        multicast */
2effd274
                     eosf = strrchr(sfilename, '.');
                     if (!eosf)
                         eosf = sfilename + strlen(sfilename);
829ac53d
                     if (stream->is_multicast)
                         strcpy(eosf, ".sdp");
                     else
                         strcpy(eosf, ".rtsp");
a6e14edd
                 }
42a63c6a
             }
115329f1
 
d9d86e00
             avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
a6e14edd
                          sfilename, stream->filename);
d9d86e00
             avio_printf(pb, "<td align=right> %d <td align=right> ",
2ac887ba
                         stream->conns_served);
2effd274
             fmt_bytecount(pb, stream->bytes_served);
a6e14edd
             switch(stream->stream_type) {
ace21da3
             case STREAM_TYPE_LIVE: {
a6e14edd
                     int audio_bit_rate = 0;
                     int video_bit_rate = 0;
58445440
                     const char *audio_codec_name = "";
                     const char *video_codec_name = "";
                     const char *audio_codec_name_extra = "";
                     const char *video_codec_name_extra = "";
a6e14edd
 
                     for(i=0;i<stream->nb_streams;i++) {
                         AVStream *st = stream->streams[i];
01f4895c
                         AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
                         switch(st->codec->codec_type) {
72415b2a
                         case AVMEDIA_TYPE_AUDIO:
01f4895c
                             audio_bit_rate += st->codec->bit_rate;
a6e14edd
                             if (codec) {
                                 if (*audio_codec_name)
                                     audio_codec_name_extra = "...";
                                 audio_codec_name = codec->name;
                             }
                             break;
72415b2a
                         case AVMEDIA_TYPE_VIDEO:
01f4895c
                             video_bit_rate += st->codec->bit_rate;
a6e14edd
                             if (codec) {
                                 if (*video_codec_name)
                                     video_codec_name_extra = "...";
                                 video_codec_name = codec->name;
                             }
                             break;
72415b2a
                         case AVMEDIA_TYPE_DATA:
01f4895c
                             video_bit_rate += st->codec->bit_rate;
e240a0bb
                             break;
a6e14edd
                         default:
0f4e8165
                             abort();
79c4ea3c
                         }
85f07f22
                     }
d9d86e00
                     avio_printf(pb, "<td align=center> %s <td align=right> %d <td align=right> %d <td> %s %s <td align=right> %d <td> %s %s",
a6e14edd
                                  stream->fmt->name,
6edd6884
                                  stream->bandwidth,
a6e14edd
                                  video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
                                  audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
611c5741
                     if (stream->feed)
d9d86e00
                         avio_printf(pb, "<td>%s", stream->feed->filename);
611c5741
                     else
d9d86e00
                         avio_printf(pb, "<td>%s", stream->feed_filename);
                     avio_printf(pb, "\n");
85f07f22
                 }
a6e14edd
                 break;
             default:
d9d86e00
                 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
a6e14edd
                 break;
85f07f22
             }
         }
         stream = stream->next;
     }
d9d86e00
     avio_printf(pb, "</table>\n");
a6e14edd
 
     stream = first_stream;
     while (stream != NULL) {
         if (stream->feed == stream) {
d9d86e00
             avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
cde25790
             if (stream->pid) {
d9d86e00
                 avio_printf(pb, "Running as pid %d.\n", stream->pid);
cde25790
 
c2c17268
 #if defined(linux)
2effd274
                 {
                     FILE *pid_stat;
                     char ps_cmd[64];
 
                     /* This is somewhat linux specific I guess */
115329f1
                     snprintf(ps_cmd, sizeof(ps_cmd),
                              "ps -o \"%%cpu,cputime\" --no-headers %d",
2effd274
                              stream->pid);
115329f1
 
2effd274
                     pid_stat = popen(ps_cmd, "r");
                     if (pid_stat) {
                         char cpuperc[10];
                         char cpuused[64];
115329f1
 
f077e1fb
                         if (fscanf(pid_stat, "%9s %63s", cpuperc,
2effd274
                                    cpuused) == 2) {
d9d86e00
                             avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2effd274
                                          cpuperc, cpuused);
                         }
                         fclose(pid_stat);
cde25790
                     }
                 }
 #endif
 
d9d86e00
                 avio_printf(pb, "<p>");
cde25790
             }
d9d86e00
             avio_printf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
a6e14edd
 
             for (i = 0; i < stream->nb_streams; i++) {
                 AVStream *st = stream->streams[i];
01f4895c
                 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
b29f97d1
                 const char *type = "unknown";
b582f314
                 char parameters[64];
 
                 parameters[0] = 0;
a6e14edd
 
01f4895c
                 switch(st->codec->codec_type) {
72415b2a
                 case AVMEDIA_TYPE_AUDIO:
a6e14edd
                     type = "audio";
acdc8520
                     snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
a6e14edd
                     break;
72415b2a
                 case AVMEDIA_TYPE_VIDEO:
a6e14edd
                     type = "video";
01f4895c
                     snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
                                 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
a6e14edd
                     break;
                 default:
0f4e8165
                     abort();
a6e14edd
                 }
d9d86e00
                 avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
01f4895c
                         i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
a6e14edd
             }
d9d86e00
             avio_printf(pb, "</table>\n");
a6e14edd
 
115329f1
         }
a6e14edd
         stream = stream->next;
     }
115329f1
 
85f07f22
     /* connection status */
d9d86e00
     avio_printf(pb, "<h2>Connection Status</h2>\n");
85f07f22
 
d9d86e00
     avio_printf(pb, "Number of connections: %d / %d<br>\n",
85f07f22
                  nb_connections, nb_max_connections);
 
d9d86e00
     avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
6edd6884
                  current_bandwidth, max_bandwidth);
42a63c6a
 
d9d86e00
     avio_printf(pb, "<table>\n");
     avio_printf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
85f07f22
     c1 = first_http_ctx;
     i = 0;
2effd274
     while (c1 != NULL) {
cde25790
         int bitrate;
         int j;
 
         bitrate = 0;
2effd274
         if (c1->stream) {
             for (j = 0; j < c1->stream->nb_streams; j++) {
2d563d2f
                 if (!c1->stream->feed)
01f4895c
                     bitrate += c1->stream->streams[j]->codec->bit_rate;
2d563d2f
                 else if (c1->feed_streams[j] >= 0)
                     bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
cde25790
             }
         }
 
85f07f22
         i++;
         p = inet_ntoa(c1->from_addr.sin_addr);
d9d86e00
         avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
115329f1
                     i,
                     c1->stream ? c1->stream->filename : "",
2effd274
                     c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
115329f1
                     p,
2effd274
                     c1->protocol,
                     http_state[c1->state]);
         fmt_bytecount(pb, bitrate);
d9d86e00
         avio_printf(pb, "<td align=right>");
2effd274
         fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
d9d86e00
         avio_printf(pb, "<td align=right>");
2effd274
         fmt_bytecount(pb, c1->data_count);
d9d86e00
         avio_printf(pb, "\n");
85f07f22
         c1 = c1->next;
     }
d9d86e00
     avio_printf(pb, "</table>\n");
115329f1
 
85f07f22
     /* date */
     ti = time(NULL);
     p = ctime(&ti);
d9d86e00
     avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
     avio_printf(pb, "</body>\n</html>\n");
85f07f22
 
6dc7d80d
     len = avio_close_dyn_buf(pb, &c->pb_buffer);
2effd274
     c->buffer_ptr = c->pb_buffer;
     c->buffer_end = c->pb_buffer + len;
85f07f22
 }
 
 static int open_input_stream(HTTPContext *c, const char *info)
 {
     char buf[128];
     char input_filename[1024];
50f2dfad
     AVFormatContext *s = NULL;
9fcc62ed
     int buf_size, i, ret;
0c1a9eda
     int64_t stream_pos;
85f07f22
 
     /* find file name */
     if (c->stream->feed) {
         strcpy(input_filename, c->stream->feed->feed_filename);
9fcc62ed
         buf_size = FFM_PACKET_SIZE;
85f07f22
         /* compute position (absolute time) */
ab0287fc
         if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
0e1e5d00
             if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0) {
                 http_log("Invalid date specification '%s' for stream\n", buf);
9fcae973
                 return ret;
0e1e5d00
             }
ab0287fc
         } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
f747e6d3
             int prebuffer = strtol(buf, 0, 10);
0c1a9eda
             stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
611c5741
         } else
0c1a9eda
             stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
85f07f22
     } else {
         strcpy(input_filename, c->stream->feed_filename);
9fcc62ed
         buf_size = 0;
85f07f22
         /* compute position (relative time) */
ab0287fc
         if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
0e1e5d00
             if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0) {
                 http_log("Invalid date specification '%s' for stream\n", buf);
9fcae973
                 return ret;
0e1e5d00
             }
ace21da3
         } else
85f07f22
             stream_pos = 0;
     }
0e1e5d00
     if (!input_filename[0]) {
         http_log("No filename was specified for stream\n");
         return AVERROR(EINVAL);
     }
85f07f22
 
     /* open stream */
50f2dfad
     if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
0e1e5d00
         http_log("Could not open input '%s': %s\n", input_filename, av_err2str(ret));
         return ret;
2effd274
     }
00969376
 
     /* set buffer size */
     if (buf_size > 0) ffio_set_buf_size(s->pb, buf_size);
 
9dc0bc3d
     s->flags |= AVFMT_FLAG_GENPTS;
85f07f22
     c->fmt_in = s;
0e1e5d00
     if (strcmp(s->iformat->name, "ffm") &&
         (ret = avformat_find_stream_info(c->fmt_in, NULL)) < 0) {
         http_log("Could not find stream info for input '%s'\n", input_filename);
cd3716b9
         avformat_close_input(&s);
0e1e5d00
         return ret;
20f93c3c
     }
115329f1
 
35e525b7
     /* choose stream as clock source (we favor the video stream if
      * present) for packet sending */
2effd274
     c->pts_stream_index = 0;
     for(i=0;i<c->stream->nb_streams;i++) {
115329f1
         if (c->pts_stream_index == 0 &&
72415b2a
             c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2effd274
             c->pts_stream_index = i;
         }
     }
85f07f22
 
611c5741
     if (c->fmt_in->iformat->read_seek)
60a04f7f
         av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2effd274
     /* set the start time (needed for maxtime and RTP packet timing) */
     c->start_time = cur_time;
     c->first_pts = AV_NOPTS_VALUE;
     return 0;
 }
 
e240a0bb
 /* return the server clock (in us) */
 static int64_t get_server_clock(HTTPContext *c)
2effd274
 {
e240a0bb
     /* compute current pts value from system time */
c3f58185
     return (cur_time - c->start_time) * 1000;
2effd274
 }
 
e240a0bb
 /* return the estimated time at which the current packet must be sent
    (in us) */
 static int64_t get_packet_send_clock(HTTPContext *c)
2effd274
 {
e240a0bb
     int bytes_left, bytes_sent, frame_bytes;
115329f1
 
e240a0bb
     frame_bytes = c->cur_frame_bytes;
611c5741
     if (frame_bytes <= 0)
e240a0bb
         return c->cur_pts;
611c5741
     else {
e240a0bb
         bytes_left = c->buffer_end - c->buffer_ptr;
         bytes_sent = frame_bytes - bytes_left;
         return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2effd274
     }
 }
 
 
5eb765ef
 static int http_prepare_data(HTTPContext *c)
85f07f22
 {
2effd274
     int i, len, ret;
     AVFormatContext *ctx;
85f07f22
 
bc351386
     av_freep(&c->pb_buffer);
85f07f22
     switch(c->state) {
     case HTTPSTATE_SEND_DATA_HEADER:
c18cfd10
         ctx = avformat_alloc_context();
         c->fmt_ctx = *ctx;
         av_freep(&ctx);
9985710a
         av_dict_copy(&(c->fmt_ctx.metadata), c->stream->metadata, 0);
db3262b7
         c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
 
3d9cc27d
         for(i=0;i<c->stream->nb_streams;i++) {
bb270c08
             AVStream *src;
db3262b7
             c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2effd274
             /* if file or feed, then just take streams from FFStream struct */
115329f1
             if (!c->stream->feed ||
2effd274
                 c->stream->feed == c->stream)
7c054ea7
                 src = c->stream->streams[i];
2effd274
             else
7c054ea7
                 src = c->stream->feed->streams[c->stream->feed_streams[i]];
 
db3262b7
             *(c->fmt_ctx.streams[i]) = *src;
             c->fmt_ctx.streams[i]->priv_data = 0;
35e525b7
             /* XXX: should be done in AVStream, not in codec */
             c->fmt_ctx.streams[i]->codec->frame_number = 0;
2effd274
         }
3d9cc27d
         /* set output format parameters */
         c->fmt_ctx.oformat = c->stream->fmt;
         c->fmt_ctx.nb_streams = c->stream->nb_streams;
 
2effd274
         c->got_key_frame = 0;
f747e6d3
 
2effd274
         /* prepare header and save header data in a stream */
b92c5452
         if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2effd274
             /* XXX: potential leak */
             return -1;
85f07f22
         }
8978feda
         c->fmt_ctx.pb->seekable = 0;
2effd274
 
8aae202e
         /*
          * HACK to avoid mpeg ps muxer to spit many underflow errors
          * Default value from FFmpeg
35e525b7
          * Try to set it using configuration option
8aae202e
          */
         c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
 
73b87304
         if ((ret = avformat_write_header(&c->fmt_ctx, NULL)) < 0) {
             http_log("Error writing output header for stream '%s': %s\n",
                      c->stream->filename, av_err2str(ret));
             return ret;
929a9b75
         }
d2d67e42
         av_dict_free(&c->fmt_ctx.metadata);
2effd274
 
6dc7d80d
         len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2effd274
         c->buffer_ptr = c->pb_buffer;
         c->buffer_end = c->pb_buffer + len;
 
85f07f22
         c->state = HTTPSTATE_SEND_DATA;
         c->last_packet_sent = 0;
         break;
     case HTTPSTATE_SEND_DATA:
         /* find a new packet */
3b371676
         /* read a packet from the input stream */
         if (c->stream->feed)
             ffm_set_write_index(c->fmt_in,
                                 c->stream->feed->feed_write_index,
                                 c->stream->feed->feed_size);
 
         if (c->stream->max_time &&
             c->stream->max_time + c->start_time - cur_time < 0)
             /* We have timed out */
             c->state = HTTPSTATE_SEND_DATA_TRAILER;
         else {
             AVPacket pkt;
         redo:
d9aac267
             ret = av_read_frame(c->fmt_in, &pkt);
             if (ret < 0) {
                 if (c->stream->feed) {
3b371676
                     /* if coming from feed, it means we reached the end of the
                        ffm file, so must wait for more data */
                     c->state = HTTPSTATE_WAIT_FEED;
                     return 1; /* state changed */
d9aac267
                 } else if (ret == AVERROR(EAGAIN)) {
                     /* input not ready, come back later */
                     return 0;
2effd274
                 } else {
3b371676
                     if (c->stream->loop) {
cd3716b9
                         avformat_close_input(&c->fmt_in);
3b371676
                         if (open_input_stream(c, "") < 0)
                             goto no_loop;
                         goto redo;
                     } else {
                     no_loop:
35e525b7
                         /* must send trailer now because EOF or error */
3b371676
                         c->state = HTTPSTATE_SEND_DATA_TRAILER;
1bc1cfdd
                     }
3b371676
                 }
             } else {
084a8912
                 int source_index = pkt.stream_index;
3b371676
                 /* update first pts if needed */
                 if (c->first_pts == AV_NOPTS_VALUE) {
                     c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
                     c->start_time = cur_time;
                 }
                 /* send it to the appropriate stream */
                 if (c->stream->feed) {
                     /* if coming from a feed, select the right stream */
                     if (c->switch_pending) {
                         c->switch_pending = 0;
cde25790
                         for(i=0;i<c->stream->nb_streams;i++) {
3b371676
                             if (c->switch_feed_streams[i] == pkt.stream_index)
cc947f04
                                 if (pkt.flags & AV_PKT_FLAG_KEY)
305ca590
                                     c->switch_feed_streams[i] = -1;
3b371676
                             if (c->switch_feed_streams[i] >= 0)
                                 c->switch_pending = 1;
cde25790
                         }
3b371676
                     }
                     for(i=0;i<c->stream->nb_streams;i++) {
a5ba4ced
                         if (c->stream->feed_streams[i] == pkt.stream_index) {
78728064
                             AVStream *st = c->fmt_in->streams[source_index];
3b371676
                             pkt.stream_index = i;
cc947f04
                             if (pkt.flags & AV_PKT_FLAG_KEY &&
72415b2a
                                 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
180b7026
                                  c->stream->nb_streams == 1))
0332f549
                                 c->got_key_frame = 1;
                             if (!c->stream->send_on_key || c->got_key_frame)
3b371676
                                 goto send_it;
                         }
                     }
                 } else {
                     AVCodecContext *codec;
dc3a6a36
                     AVStream *ist, *ost;
                 send_it:
                     ist = c->fmt_in->streams[source_index];
3b371676
                     /* specific handling for RTP: we use several
35e525b7
                      * output streams (one for each RTP connection).
                      * XXX: need more abstract handling */
3b371676
                     if (c->is_packetized) {
                         /* compute send time and duration */
8f56ccca
                         c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
f475f35f
                         c->cur_pts -= c->first_pts;
8f56ccca
                         c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
3b371676
                         /* find RTP context */
                         c->packet_stream_index = pkt.stream_index;
                         ctx = c->rtp_ctx[c->packet_stream_index];
                         if(!ctx) {
8a0b55ff
                             av_free_packet(&pkt);
3b371676
                             break;
8a0b55ff
                         }
3b371676
                         codec = ctx->streams[0]->codec;
                         /* only one stream per RTP connection */
                         pkt.stream_index = 0;
                     } else {
                         ctx = &c->fmt_ctx;
                         /* Fudge here */
3ab29d8e
                         codec = ctx->streams[pkt.stream_index]->codec;
3b371676
                     }
 
                     if (c->is_packetized) {
                         int max_packet_size;
90abbdba
                         if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
3b371676
                             max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
                         else
b263bf66
                             max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
403ee835
                         ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
3b371676
                     } else {
b92c5452
                         ret = avio_open_dyn_buf(&ctx->pb);
3b371676
                     }
                     if (ret < 0) {
                         /* XXX: potential leak */
                         return -1;
                     }
3ab29d8e
                     ost = ctx->streams[pkt.stream_index];
 
8978feda
                     ctx->pb->seekable = 0;
3b371676
                     if (pkt.dts != AV_NOPTS_VALUE)
d80904cc
                         pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
3b371676
                     if (pkt.pts != AV_NOPTS_VALUE)
d80904cc
                         pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
                     pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
93f08827
                     if ((ret = av_write_frame(ctx, &pkt)) < 0) {
                         http_log("Error writing frame to output for stream '%s': %s\n",
                                  c->stream->filename, av_err2str(ret));
3b371676
                         c->state = HTTPSTATE_SEND_DATA_TRAILER;
3766ed72
                     }
3b371676
 
6dc7d80d
                     len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
3b371676
                     c->cur_frame_bytes = len;
                     c->buffer_ptr = c->pb_buffer;
                     c->buffer_end = c->pb_buffer + len;
 
                     codec->frame_number++;
                     if (len == 0) {
                         av_free_packet(&pkt);
                         goto redo;
f747e6d3
                     }
85f07f22
                 }
3b371676
                 av_free_packet(&pkt);
85f07f22
             }
3b371676
         }
85f07f22
         break;
     default:
     case HTTPSTATE_SEND_DATA_TRAILER:
         /* last packet test ? */
2effd274
         if (c->last_packet_sent || c->is_packetized)
85f07f22
             return -1;
2effd274
         ctx = &c->fmt_ctx;
85f07f22
         /* prepare header */
b92c5452
         if (avio_open_dyn_buf(&ctx->pb) < 0) {
2effd274
             /* XXX: potential leak */
             return -1;
         }
8978feda
         c->fmt_ctx.pb->seekable = 0;
2effd274
         av_write_trailer(ctx);
6dc7d80d
         len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2effd274
         c->buffer_ptr = c->pb_buffer;
         c->buffer_end = c->pb_buffer + len;
 
85f07f22
         c->last_packet_sent = 1;
         break;
     }
     return 0;
 }
 
 /* should convert the format at the same time */
bc351386
 /* send data starting at c->buffer_ptr to the output connection
35e525b7
  * (either UDP or TCP) */
5eb765ef
 static int http_send_data(HTTPContext *c)
85f07f22
 {
e240a0bb
     int len, ret;
85f07f22
 
bc351386
     for(;;) {
         if (c->buffer_ptr >= c->buffer_end) {
             ret = http_prepare_data(c);
             if (ret < 0)
                 return -1;
611c5741
             else if (ret != 0)
bc351386
                 /* state change requested */
                 break;
2effd274
         } else {
bc351386
             if (c->is_packetized) {
                 /* RTP data output */
                 len = c->buffer_end - c->buffer_ptr;
                 if (len < 4) {
                     /* fail safe - should never happen */
                 fail1:
                     c->buffer_ptr = c->buffer_end;
2effd274
                     return 0;
                 }
bc351386
                 len = (c->buffer_ptr[0] << 24) |
                     (c->buffer_ptr[1] << 16) |
                     (c->buffer_ptr[2] << 8) |
                     (c->buffer_ptr[3]);
                 if (len > (c->buffer_end - c->buffer_ptr))
                     goto fail1;
e240a0bb
                 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
                     /* nothing to send yet: we can wait */
                     return 0;
                 }
 
                 c->data_count += len;
                 update_datarate(&c->datarate, c->data_count);
                 if (c->stream)
                     c->stream->bytes_served += len;
 
90abbdba
                 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
bc351386
                     /* RTP packets are sent inside the RTSP TCP connection */
ae628ec1
                     AVIOContext *pb;
bc351386
                     int interleaved_index, size;
                     uint8_t header[4];
                     HTTPContext *rtsp_c;
115329f1
 
bc351386
                     rtsp_c = c->rtsp_c;
                     /* if no RTSP connection left, error */
                     if (!rtsp_c)
                         return -1;
                     /* if already sending something, then wait. */
611c5741
                     if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
bc351386
                         break;
b92c5452
                     if (avio_open_dyn_buf(&pb) < 0)
bc351386
                         goto fail1;
                     interleaved_index = c->packet_stream_index * 2;
                     /* RTCP packets are sent at odd indexes */
                     if (c->buffer_ptr[1] == 200)
                         interleaved_index++;
                     /* write RTSP TCP header */
                     header[0] = '$';
                     header[1] = interleaved_index;
                     header[2] = len >> 8;
                     header[3] = len;
77eb5504
                     avio_write(pb, header, 4);
bc351386
                     /* write RTP packet data */
                     c->buffer_ptr += 4;
77eb5504
                     avio_write(pb, c->buffer_ptr, len);
6dc7d80d
                     size = avio_close_dyn_buf(pb, &c->packet_buffer);
bc351386
                     /* prepare asynchronous TCP sending */
                     rtsp_c->packet_buffer_ptr = c->packet_buffer;
                     rtsp_c->packet_buffer_end = c->packet_buffer + size;
e240a0bb
                     c->buffer_ptr += len;
115329f1
 
e240a0bb
                     /* send everything we can NOW */
c60202df
                     len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
                                 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
611c5741
                     if (len > 0)
e240a0bb
                         rtsp_c->packet_buffer_ptr += len;
                     if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
                         /* if we could not send all the data, we will
                            send it later, so a new state is needed to
                            "lock" the RTSP TCP connection */
                         rtsp_c->state = RTSPSTATE_SEND_PACKET;
                         break;
611c5741
                     } else
e240a0bb
                         /* all data has been sent */
                         av_freep(&c->packet_buffer);
                 } else {
                     /* send RTP packet directly in UDP */
bc351386
                     c->buffer_ptr += 4;
b263bf66
                     ffurl_write(c->rtp_handles[c->packet_stream_index],
                                 c->buffer_ptr, len);
e240a0bb
                     c->buffer_ptr += len;
                     /* here we continue as we can send several packets per 10 ms slot */
bc351386
                 }
             } else {
                 /* TCP data output */
c60202df
                 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
bc351386
                 if (len < 0) {
28c4741a
                     if (ff_neterrno() != AVERROR(EAGAIN) &&
                         ff_neterrno() != AVERROR(EINTR))
bc351386
                         /* error : close connection */
                         return -1;
611c5741
                     else
bc351386
                         return 0;
611c5741
                 } else
bc351386
                     c->buffer_ptr += len;
611c5741
 
e240a0bb
                 c->data_count += len;
                 update_datarate(&c->datarate, c->data_count);
                 if (c->stream)
                     c->stream->bytes_served += len;
                 break;
2effd274
             }
85f07f22
         }
bc351386
     } /* for(;;) */
85f07f22
     return 0;
 }
 
 static int http_start_receive_data(HTTPContext *c)
 {
     int fd;
0124fca0
     int ret;
85f07f22
 
0124fca0
     if (c->stream->feed_opened) {
         http_log("Stream feed '%s' was not opened\n", c->stream->feed_filename);
         return AVERROR(EINVAL);
     }
85f07f22
 
e322ea48
     /* Don't permit writing to this one */
0124fca0
     if (c->stream->readonly) {
         http_log("Cannot write to read-only file '%s'\n", c->stream->feed_filename);
         return AVERROR(EINVAL);
     }
e322ea48
 
85f07f22
     /* open feed */
     fd = open(c->stream->feed_filename, O_RDWR);
929a9b75
     if (fd < 0) {
0124fca0
         ret = AVERROR(errno);
33f10fa6
         http_log("Could not open feed file '%s': %s\n",
0124fca0
                  c->stream->feed_filename, strerror(errno));
         return ret;
929a9b75
     }
85f07f22
     c->feed_fd = fd;
115329f1
 
861ec13a
     if (c->stream->truncate) {
         /* truncate feed file */
         ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
         http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
0de1319e
         if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
0124fca0
             ret = AVERROR(errno);
             http_log("Error truncating feed file '%s': %s\n",
                      c->stream->feed_filename, strerror(errno));
             return ret;
0de1319e
         }
861ec13a
     } else {
0124fca0
         ret = ffm_read_write_index(fd);
         if (ret < 0) {
             http_log("Error reading write index from feed file '%s': %s\n",
                      c->stream->feed_filename, strerror(errno));
             return ret;
         } else {
             c->stream->feed_write_index = ret;
1f611549
         }
861ec13a
     }
 
7e24aa0c
     c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
85f07f22
     c->stream->feed_size = lseek(fd, 0, SEEK_END);
     lseek(fd, 0, SEEK_SET);
 
     /* init buffer input */
     c->buffer_ptr = c->buffer;
     c->buffer_end = c->buffer + FFM_PACKET_SIZE;
     c->stream->feed_opened = 1;
fd7bec5e
     c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
85f07f22
     return 0;
 }
115329f1
 
85f07f22
 static int http_receive_data(HTTPContext *c)
 {
     HTTPContext *c1;
19c8c4ec
     int len, loop_run = 0;
85f07f22
 
19c8c4ec
     while (c->chunked_encoding && !c->chunk_size &&
            c->buffer_end > c->buffer_ptr) {
         /* read chunk header, if present */
         len = recv(c->fd, c->buffer_ptr, 1, 0);
 
         if (len < 0) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(EINTR))
19c8c4ec
                 /* error : close connection */
                 goto fail;
686d6f40
             return 0;
19c8c4ec
         } else if (len == 0) {
             /* end of connection : close it */
             goto fail;
         } else if (c->buffer_ptr - c->buffer >= 2 &&
                    !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
             c->chunk_size = strtol(c->buffer, 0, 16);
             if (c->chunk_size == 0) // end of stream
                 goto fail;
             c->buffer_ptr = c->buffer;
             break;
         } else if (++loop_run > 10) {
             /* no chunk header, abort */
             goto fail;
         } else {
             c->buffer_ptr++;
         }
     }
a6e14edd
 
19c8c4ec
     if (c->buffer_end > c->buffer_ptr) {
         len = recv(c->fd, c->buffer_ptr,
                    FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
a6e14edd
         if (len < 0) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(EINTR))
a6e14edd
                 /* error : close connection */
                 goto fail;
611c5741
         } else if (len == 0)
a6e14edd
             /* end of connection : close it */
             goto fail;
611c5741
         else {
19c8c4ec
             c->chunk_size -= len;
a6e14edd
             c->buffer_ptr += len;
             c->data_count += len;
5eb765ef
             update_datarate(&c->datarate, c->data_count);
a6e14edd
         }
     }
 
d445a7e9
     if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
         if (c->buffer[0] != 'f' ||
             c->buffer[1] != 'm') {
             http_log("Feed stream has become desynchronized -- disconnecting\n");
             goto fail;
         }
     }
 
85f07f22
     if (c->buffer_ptr >= c->buffer_end) {
f747e6d3
         FFStream *feed = c->stream;
85f07f22
         /* a packet has been received : write it in the store, except
            if header */
         if (c->data_count > FFM_PACKET_SIZE) {
             /* XXX: use llseek or url_seek */
             lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
929a9b75
             if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
                 http_log("Error writing to feed file: %s\n", strerror(errno));
                 goto fail;
             }
115329f1
 
85f07f22
             feed->feed_write_index += FFM_PACKET_SIZE;
             /* update file size */
             if (feed->feed_write_index > c->stream->feed_size)
                 feed->feed_size = feed->feed_write_index;
 
             /* handle wrap around if max file size reached */
6b0bdc75
             if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
85f07f22
                 feed->feed_write_index = FFM_PACKET_SIZE;
 
             /* write index */
2779cdad
             if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
                 http_log("Error writing index to feed file: %s\n", strerror(errno));
                 goto fail;
             }
85f07f22
 
             /* wake up any waiting connections */
             for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
115329f1
                 if (c1->state == HTTPSTATE_WAIT_FEED &&
611c5741
                     c1->stream->feed == c->stream->feed)
85f07f22
                     c1->state = HTTPSTATE_SEND_DATA;
             }
f747e6d3
         } else {
             /* We have a header in our hands that contains useful data */
50f2dfad
             AVFormatContext *s = avformat_alloc_context();
ae628ec1
             AVIOContext *pb;
bd7cf6ad
             AVInputFormat *fmt_in;
f747e6d3
             int i;
 
50f2dfad
             if (!s)
                 goto fail;
 
bd7cf6ad
             /* use feed output format name to find corresponding input format */
             fmt_in = av_find_input_format(feed->fmt->name);
             if (!fmt_in)
                 goto fail;
 
83fddaeb
             pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
                                     0, NULL, NULL, NULL, NULL);
8978feda
             pb->seekable = 0;
697efa36
 
50f2dfad
             s->pb = pb;
             if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
e6f0deab
                 av_free(pb);
                 goto fail;
             }
f747e6d3
 
             /* Now we have the actual streams */
f2972c8c
             if (s->nb_streams != feed->nb_streams) {
cd3716b9
                 avformat_close_input(&s);
86771c68
                 av_free(pb);
77553ae3
                 http_log("Feed '%s' stream number does not match registered feed\n",
                          c->stream->feed_filename);
f747e6d3
                 goto fail;
             }
f2972c8c
 
cb51aef1
             for (i = 0; i < s->nb_streams; i++) {
                 AVStream *fst = feed->streams[i];
                 AVStream *st = s->streams[i];
5634f30c
                 avcodec_copy_context(fst->codec, st->codec);
cb51aef1
             }
f2972c8c
 
cd3716b9
             avformat_close_input(&s);
86771c68
             av_free(pb);
85f07f22
         }
         c->buffer_ptr = c->buffer;
     }
 
     return 0;
  fail:
     c->stream->feed_opened = 0;
     close(c->feed_fd);
c1593d0e
     /* wake up any waiting connections to stop waiting for feed */
     for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
         if (c1->state == HTTPSTATE_WAIT_FEED &&
             c1->stream->feed == c->stream->feed)
             c1->state = HTTPSTATE_SEND_DATA_TRAILER;
     }
85f07f22
     return -1;
 }
 
2effd274
 /********************************************************************/
 /* RTSP handling */
 
 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
 {
     const char *str;
     time_t ti;
0156fcf2
     struct tm *tm;
2effd274
     char buf2[32];
 
     switch(error_number) {
7e665cd3
     case RTSP_STATUS_OK:
         str = "OK";
         break;
     case RTSP_STATUS_METHOD:
         str = "Method Not Allowed";
         break;
     case RTSP_STATUS_BANDWIDTH:
         str = "Not Enough Bandwidth";
         break;
     case RTSP_STATUS_SESSION:
         str = "Session Not Found";
         break;
     case RTSP_STATUS_STATE:
         str = "Method Not Valid in This State";
         break;
     case RTSP_STATUS_AGGREGATE:
         str = "Aggregate operation not allowed";
         break;
     case RTSP_STATUS_ONLY_AGGREGATE:
         str = "Only aggregate operation allowed";
         break;
     case RTSP_STATUS_TRANSPORT:
         str = "Unsupported transport";
         break;
     case RTSP_STATUS_INTERNAL:
         str = "Internal Server Error";
         break;
     case RTSP_STATUS_SERVICE:
         str = "Service Unavailable";
         break;
     case RTSP_STATUS_VERSION:
         str = "RTSP Version not supported";
         break;
2effd274
     default:
         str = "Unknown Error";
         break;
     }
115329f1
 
d9d86e00
     avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
     avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2effd274
 
     /* output GMT time */
     ti = time(NULL);
0156fcf2
     tm = gmtime(&ti);
     strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
d9d86e00
     avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2effd274
 }
 
 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
 {
     rtsp_reply_header(c, error_number);
d9d86e00
     avio_printf(c->pb, "\r\n");
2effd274
 }
 
 static int rtsp_parse_request(HTTPContext *c)
 {
     const char *p, *p1, *p2;
     char cmd[32];
     char url[1024];
     char protocol[32];
     char line[1024];
     int len;
a92be9b8
     RTSPMessageHeader header1 = { 0 }, *header = &header1;
115329f1
 
2effd274
     c->buffer_ptr[0] = '\0';
     p = c->buffer;
115329f1
 
2effd274
     get_word(cmd, sizeof(cmd), &p);
     get_word(url, sizeof(url), &p);
     get_word(protocol, sizeof(protocol), &p);
 
f7d78f36
     av_strlcpy(c->method, cmd, sizeof(c->method));
     av_strlcpy(c->url, url, sizeof(c->url));
     av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2effd274
 
b92c5452
     if (avio_open_dyn_buf(&c->pb) < 0) {
2effd274
         /* XXX: cannot do more */
         c->pb = NULL; /* safety */
         return -1;
     }
 
     /* check version name */
     if (strcmp(protocol, "RTSP/1.0") != 0) {
         rtsp_reply_error(c, RTSP_STATUS_VERSION);
         goto the_end;
     }
 
     /* parse each header line */
     /* skip to next line */
     while (*p != '\n' && *p != '\0')
         p++;
     if (*p == '\n')
         p++;
     while (*p != '\0') {
c966c912
         p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2effd274
         if (!p1)
             break;
         p2 = p1;
         if (p2 > p && p2[-1] == '\r')
             p2--;
         /* skip empty line */
         if (p2 == p)
             break;
         len = p2 - p;
         if (len > sizeof(line) - 1)
             len = sizeof(line) - 1;
         memcpy(line, p, len);
         line[len] = '\0';
77223c53
         ff_rtsp_parse_line(header, line, NULL, NULL);
2effd274
         p = p1 + 1;
     }
 
     /* handle sequence number */
     c->seq = header->seq;
 
611c5741
     if (!strcmp(cmd, "DESCRIBE"))
2effd274
         rtsp_cmd_describe(c, url);
611c5741
     else if (!strcmp(cmd, "OPTIONS"))
0df65975
         rtsp_cmd_options(c, url);
611c5741
     else if (!strcmp(cmd, "SETUP"))
2effd274
         rtsp_cmd_setup(c, url, header);
611c5741
     else if (!strcmp(cmd, "PLAY"))
2effd274
         rtsp_cmd_play(c, url, header);
611c5741
     else if (!strcmp(cmd, "PAUSE"))
87079bd0
         rtsp_cmd_interrupt(c, url, header, 1);
611c5741
     else if (!strcmp(cmd, "TEARDOWN"))
87079bd0
         rtsp_cmd_interrupt(c, url, header, 0);
611c5741
     else
2effd274
         rtsp_reply_error(c, RTSP_STATUS_METHOD);
611c5741
 
2effd274
  the_end:
6dc7d80d
     len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2effd274
     c->pb = NULL; /* safety */
     if (len < 0) {
         /* XXX: cannot do more */
         return -1;
     }
     c->buffer_ptr = c->pb_buffer;
     c->buffer_end = c->pb_buffer + len;
     c->state = RTSPSTATE_SEND_REPLY;
     return 0;
 }
 
115329f1
 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
829ac53d
                                    struct in_addr my_ip)
2effd274
 {
dd723472
     AVFormatContext *avc;
9389b925
     AVStream *avs = NULL;
cbe43e62
     AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
9985710a
     AVDictionaryEntry *entry = av_dict_get(stream->metadata, "title", NULL, 0);
dd723472
     int i;
115329f1
 
8e2fd8e1
     avc =  avformat_alloc_context();
cbe43e62
     if (avc == NULL || !rtp_format) {
2effd274
         return -1;
dd723472
     }
cbe43e62
     avc->oformat = rtp_format;
d2d67e42
     av_dict_set(&avc->metadata, "title",
9985710a
                 entry ? entry->value : "No Title", 0);
dd723472
     avc->nb_streams = stream->nb_streams;
     if (stream->is_multicast) {
         snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
                  inet_ntoa(stream->multicast_ip),
                  stream->multicast_port, stream->multicast_ttl);
43d09faf
     } else {
         snprintf(avc->filename, 1024, "rtp://0.0.0.0");
dd723472
     }
115329f1
 
9389b925
     if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
         !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
         goto sdp_done;
     if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
         !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
         goto sdp_done;
 
2effd274
     for(i = 0; i < stream->nb_streams; i++) {
dd723472
         avc->streams[i] = &avs[i];
         avc->streams[i]->codec = stream->streams[i]->codec;
2effd274
     }
dd723472
     *pbuffer = av_mallocz(2048);
c3675dfe
     av_sdp_create(&avc, 1, *pbuffer, 2048);
9389b925
 
  sdp_done:
     av_free(avc->streams);
d2d67e42
     av_dict_free(&avc->metadata);
dd723472
     av_free(avc);
9389b925
     av_free(avs);
dd723472
 
     return strlen(*pbuffer);
2effd274
 }
 
0df65975
 static void rtsp_cmd_options(HTTPContext *c, const char *url)
 {
 //    rtsp_reply_header(c, RTSP_STATUS_OK);
d9d86e00
     avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
     avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
     avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
     avio_printf(c->pb, "\r\n");
0df65975
 }
 
2effd274
 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
 {
     FFStream *stream;
     char path1[1024];
     const char *path;
0c1a9eda
     uint8_t *content;
cc64ec57
     int content_length;
     socklen_t len;
829ac53d
     struct sockaddr_in my_addr;
115329f1
 
2effd274
     /* find which url is asked */
f3bfe388
     av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2effd274
     path = path1;
     if (*path == '/')
         path++;
 
     for(stream = first_stream; stream != NULL; stream = stream->next) {
25e3e53d
         if (!stream->is_feed &&
             stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2effd274
             !strcmp(path, stream->filename)) {
             goto found;
         }
     }
     /* no stream found */
     rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
     return;
 
  found:
     /* prepare the media description in sdp format */
829ac53d
 
     /* get the host IP */
     len = sizeof(my_addr);
     getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
     content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2effd274
     if (content_length < 0) {
         rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
         return;
     }
     rtsp_reply_header(c, RTSP_STATUS_OK);
d9d86e00
     avio_printf(c->pb, "Content-Base: %s/\r\n", url);
     avio_printf(c->pb, "Content-Type: application/sdp\r\n");
     avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
     avio_printf(c->pb, "\r\n");
77eb5504
     avio_write(c->pb, content, content_length);
ea4f8aab
     av_free(content);
2effd274
 }
 
 static HTTPContext *find_rtp_session(const char *session_id)
 {
     HTTPContext *c;
 
     if (session_id[0] == '\0')
         return NULL;
 
     for(c = first_http_ctx; c != NULL; c = c->next) {
         if (!strcmp(c->session_id, session_id))
             return c;
     }
     return NULL;
 }
 
a9e534d5
 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
2effd274
 {
     RTSPTransportField *th;
     int i;
 
     for(i=0;i<h->nb_transports;i++) {
         th = &h->transports[i];
90abbdba
         if (th->lower_transport == lower_transport)
2effd274
             return th;
     }
     return NULL;
 }
 
115329f1
 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
a9e534d5
                            RTSPMessageHeader *h)
2effd274
 {
     FFStream *stream;
bacde646
     int stream_index, rtp_port, rtcp_port;
2effd274
     char buf[1024];
     char path1[1024];
     const char *path;
     HTTPContext *rtp_c;
     RTSPTransportField *th;
     struct sockaddr_in dest_addr;
     RTSPActionServerSetup setup;
115329f1
 
2effd274
     /* find which url is asked */
f3bfe388
     av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2effd274
     path = path1;
     if (*path == '/')
         path++;
 
     /* now check each stream */
     for(stream = first_stream; stream != NULL; stream = stream->next) {
25e3e53d
         if (!stream->is_feed &&
             stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2effd274
             /* accept aggregate filenames only if single stream */
             if (!strcmp(path, stream->filename)) {
                 if (stream->nb_streams != 1) {
                     rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
                     return;
                 }
                 stream_index = 0;
                 goto found;
             }
115329f1
 
2effd274
             for(stream_index = 0; stream_index < stream->nb_streams;
                 stream_index++) {
115329f1
                 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2effd274
                          stream->filename, stream_index);
                 if (!strcmp(path, buf))
                     goto found;
             }
         }
     }
     /* no stream found */
     rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
     return;
  found:
 
     /* generate session id if needed */
d40c0e4a
     if (h->session_id[0] == '\0') {
         unsigned random0 = av_lfg_get(&random_state);
         unsigned random1 = av_lfg_get(&random_state);
1df93ae9
         snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
d40c0e4a
                  random0, random1);
     }
2effd274
 
     /* find rtp session, and create it if none found */
     rtp_c = find_rtp_session(h->session_id);
     if (!rtp_c) {
bc351386
         /* always prefer UDP */
90abbdba
         th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
bc351386
         if (!th) {
90abbdba
             th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
bc351386
             if (!th) {
                 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
                 return;
             }
         }
 
         rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
90abbdba
                                    th->lower_transport);
2effd274
         if (!rtp_c) {
             rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
             return;
         }
 
         /* open input stream */
         if (open_input_stream(rtp_c, "") < 0) {
             rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
             return;
         }
     }
115329f1
 
2effd274
     /* test if stream is OK (test needed because several SETUP needs
        to be done for a given file) */
     if (rtp_c->stream != stream) {
         rtsp_reply_error(c, RTSP_STATUS_SERVICE);
         return;
     }
115329f1
 
2effd274
     /* test if stream is already set up */
     if (rtp_c->rtp_ctx[stream_index]) {
         rtsp_reply_error(c, RTSP_STATUS_STATE);
         return;
     }
 
     /* check transport */
     th = find_transport(h, rtp_c->rtp_protocol);
90abbdba
     if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
2effd274
                 th->client_port_min <= 0)) {
         rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
         return;
     }
 
     /* setup default options */
     setup.transport_option[0] = '\0';
     dest_addr = rtp_c->from_addr;
     dest_addr.sin_port = htons(th->client_port_min);
115329f1
 
2effd274
     /* setup stream */
bc351386
     if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2effd274
         rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
         return;
     }
 
     /* now everything is OK, so we can send the connection parameters */
     rtsp_reply_header(c, RTSP_STATUS_OK);
     /* session ID */
d9d86e00
     avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2effd274
 
     switch(rtp_c->rtp_protocol) {
90abbdba
     case RTSP_LOWER_TRANSPORT_UDP:
bfc6db44
         rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
         rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
d9d86e00
         avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2effd274
                     "client_port=%d-%d;server_port=%d-%d",
bacde646
                     th->client_port_min, th->client_port_max,
                     rtp_port, rtcp_port);
2effd274
         break;
90abbdba
     case RTSP_LOWER_TRANSPORT_TCP:
d9d86e00
         avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2effd274
                     stream_index * 2, stream_index * 2 + 1);
         break;
     default:
         break;
     }
611c5741
     if (setup.transport_option[0] != '\0')
d9d86e00
         avio_printf(c->pb, ";%s", setup.transport_option);
     avio_printf(c->pb, "\r\n");
115329f1
 
2effd274
 
d9d86e00
     avio_printf(c->pb, "\r\n");
2effd274
 }
 
 
 /* find an rtp connection by using the session ID. Check consistency
    with filename */
115329f1
 static HTTPContext *find_rtp_session_with_url(const char *url,
2effd274
                                               const char *session_id)
 {
     HTTPContext *rtp_c;
     char path1[1024];
     const char *path;
94d9ad5f
     char buf[1024];
c2ca851b
     int s, len;
2effd274
 
     rtp_c = find_rtp_session(session_id);
     if (!rtp_c)
         return NULL;
 
     /* find which url is asked */
f3bfe388
     av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2effd274
     path = path1;
     if (*path == '/')
         path++;
94d9ad5f
     if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
     for(s=0; s<rtp_c->stream->nb_streams; ++s) {
       snprintf(buf, sizeof(buf), "%s/streamid=%d",
         rtp_c->stream->filename, s);
       if(!strncmp(path, buf, sizeof(buf))) {
     // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
         return rtp_c;
       }
     }
c2ca851b
     len = strlen(path);
     if (len > 0 && path[len - 1] == '/' &&
         !strncmp(path, rtp_c->stream->filename, len - 1))
         return rtp_c;
94d9ad5f
     return NULL;
2effd274
 }
 
a9e534d5
 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
2effd274
 {
     HTTPContext *rtp_c;
 
     rtp_c = find_rtp_session_with_url(url, h->session_id);
     if (!rtp_c) {
         rtsp_reply_error(c, RTSP_STATUS_SESSION);
         return;
     }
115329f1
 
2effd274
     if (rtp_c->state != HTTPSTATE_SEND_DATA &&
         rtp_c->state != HTTPSTATE_WAIT_FEED &&
         rtp_c->state != HTTPSTATE_READY) {
         rtsp_reply_error(c, RTSP_STATUS_STATE);
         return;
     }
 
     rtp_c->state = HTTPSTATE_SEND_DATA;
115329f1
 
2effd274
     /* now everything is OK, so we can send the connection parameters */
     rtsp_reply_header(c, RTSP_STATUS_OK);
     /* session ID */
d9d86e00
     avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
     avio_printf(c->pb, "\r\n");
2effd274
 }
 
87079bd0
 static void rtsp_cmd_interrupt(HTTPContext *c, const char *url, RTSPMessageHeader *h, int pause_only)
2effd274
 {
     HTTPContext *rtp_c;
 
     rtp_c = find_rtp_session_with_url(url, h->session_id);
     if (!rtp_c) {
         rtsp_reply_error(c, RTSP_STATUS_SESSION);
         return;
     }
115329f1
 
87079bd0
     if (pause_only) {
         if (rtp_c->state != HTTPSTATE_SEND_DATA &&
             rtp_c->state != HTTPSTATE_WAIT_FEED) {
             rtsp_reply_error(c, RTSP_STATUS_STATE);
             return;
         }
         rtp_c->state = HTTPSTATE_READY;
         rtp_c->first_pts = AV_NOPTS_VALUE;
2effd274
     }
115329f1
 
2effd274
     /* now everything is OK, so we can send the connection parameters */
     rtsp_reply_header(c, RTSP_STATUS_OK);
     /* session ID */
e2d7dc87
     avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
d9d86e00
     avio_printf(c->pb, "\r\n");
e2d7dc87
 
87079bd0
     if (!pause_only)
         close_connection(rtp_c);
2effd274
 }
 
 /********************************************************************/
 /* RTP handling */
 
115329f1
 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
bc351386
                                        FFStream *stream, const char *session_id,
90abbdba
                                        enum RTSPLowerTransport rtp_protocol)
2effd274
 {
     HTTPContext *c = NULL;
bc351386
     const char *proto_str;
115329f1
 
2effd274
     /* XXX: should output a warning page when coming
        close to the connection limit */
     if (nb_connections >= nb_max_connections)
         goto fail;
115329f1
 
2effd274
     /* add a new connection */
     c = av_mallocz(sizeof(HTTPContext));
     if (!c)
         goto fail;
115329f1
 
2effd274
     c->fd = -1;
     c->poll_entry = NULL;
6edd6884
     c->from_addr = *from_addr;
2effd274
     c->buffer_size = IOBUFFER_INIT_SIZE;
     c->buffer = av_malloc(c->buffer_size);
     if (!c->buffer)
         goto fail;
     nb_connections++;
     c->stream = stream;
f7d78f36
     av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
2effd274
     c->state = HTTPSTATE_READY;
     c->is_packetized = 1;
bc351386
     c->rtp_protocol = rtp_protocol;
 
2effd274
     /* protocol is shown in statistics */
bc351386
     switch(c->rtp_protocol) {
90abbdba
     case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
bc351386
         proto_str = "MCAST";
         break;
90abbdba
     case RTSP_LOWER_TRANSPORT_UDP:
bc351386
         proto_str = "UDP";
         break;
90abbdba
     case RTSP_LOWER_TRANSPORT_TCP:
bc351386
         proto_str = "TCP";
         break;
     default:
         proto_str = "???";
         break;
     }
f7d78f36
     av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
     av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
2effd274
 
6edd6884
     current_bandwidth += stream->bandwidth;
 
2effd274
     c->next = first_http_ctx;
     first_http_ctx = c;
     return c;
115329f1
 
2effd274
  fail:
     if (c) {
         av_free(c->buffer);
         av_free(c);
     }
     return NULL;
 }
 
 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
bc351386
    command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
2effd274
    used. */
115329f1
 static int rtp_new_av_stream(HTTPContext *c,
bc351386
                              int stream_index, struct sockaddr_in *dest_addr,
                              HTTPContext *rtsp_c)
2effd274
 {
     AVFormatContext *ctx;
     AVStream *st;
     char *ipaddr;
75480e86
     URLContext *h = NULL;
0c1a9eda
     uint8_t *dummy_buf;
bc351386
     int max_packet_size;
115329f1
 
2effd274
     /* now we can open the relevant output stream */
8e2fd8e1
     ctx = avformat_alloc_context();
2effd274
     if (!ctx)
         return -1;
0f52ef1a
     ctx->oformat = av_guess_format("rtp", NULL, NULL);
2effd274
 
     st = av_mallocz(sizeof(AVStream));
     if (!st)
         goto fail;
     ctx->nb_streams = 1;
db3262b7
     ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
840238b8
     if (!ctx->streams)
db3262b7
       goto fail;
2effd274
     ctx->streams[0] = st;
 
115329f1
     if (!c->stream->feed ||
611c5741
         c->stream->feed == c->stream)
2effd274
         memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
611c5741
     else
115329f1
         memcpy(st,
2effd274
                c->stream->feed->streams[c->stream->feed_streams[stream_index]],
                sizeof(AVStream));
57dbe08b
     st->priv_data = NULL;
115329f1
 
bc351386
     /* build destination RTP address */
     ipaddr = inet_ntoa(dest_addr->sin_addr);
 
     switch(c->rtp_protocol) {
90abbdba
     case RTSP_LOWER_TRANSPORT_UDP:
     case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
bc351386
         /* RTP/UDP case */
115329f1
 
6edd6884
         /* XXX: also pass as parameter to function ? */
         if (c->stream->is_multicast) {
             int ttl;
             ttl = c->stream->multicast_ttl;
             if (!ttl)
                 ttl = 16;
             snprintf(ctx->filename, sizeof(ctx->filename),
115329f1
                      "rtp://%s:%d?multicast=1&ttl=%d",
6edd6884
                      ipaddr, ntohs(dest_addr->sin_port), ttl);
         } else {
             snprintf(ctx->filename, sizeof(ctx->filename),
                      "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
         }
2effd274
 
b263bf66
         if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
2effd274
             goto fail;
         c->rtp_handles[stream_index] = h;
b263bf66
         max_packet_size = h->max_packet_size;
bc351386
         break;
90abbdba
     case RTSP_LOWER_TRANSPORT_TCP:
bc351386
         /* RTP/TCP case */
         c->rtsp_c = rtsp_c;
         max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
         break;
     default:
2effd274
         goto fail;
     }
 
e21ac209
     http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
115329f1
              ipaddr, ntohs(dest_addr->sin_port),
bc351386
              c->stream->filename, stream_index, c->protocol);
6edd6884
 
47ba4728
     /* normally, no packets should be output here, but the packet size may
      * be checked */
403ee835
     if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
2effd274
         /* XXX: close stream */
         goto fail;
     }
50f2dfad
     if (avformat_write_header(ctx, NULL) < 0) {
2effd274
     fail:
         if (h)
b263bf66
             ffurl_close(h);
2effd274
         av_free(ctx);
         return -1;
     }
6dc7d80d
     avio_close_dyn_buf(ctx->pb, &dummy_buf);
2effd274
     av_free(dummy_buf);
115329f1
 
2effd274
     c->rtp_ctx[stream_index] = ctx;
     return 0;
 }
 
 /********************************************************************/
 /* ffserver initialization */
 
e175b55e
 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
2effd274
 {
     AVStream *fst;
 
0f46825d
     if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
         return NULL;
 
2effd274
     fst = av_mallocz(sizeof(AVStream));
     if (!fst)
         return NULL;
e175b55e
     if (copy) {
71a861cf
         fst->codec = avcodec_alloc_context3(NULL);
e175b55e
         memcpy(fst->codec, codec, sizeof(AVCodecContext));
         if (codec->extradata_size) {
9db5f820
             fst->codec->extradata = av_mallocz(codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
e175b55e
             memcpy(fst->codec->extradata, codec->extradata,
                 codec->extradata_size);
         }
     } else {
         /* live streams must use the actual feed's codec since it may be
35e525b7
          * updated later to carry extradata needed by them.
e175b55e
          */
         fst->codec = codec;
     }
2effd274
     fst->priv_data = av_mallocz(sizeof(FeedData));
d445a7e9
     fst->index = stream->nb_streams;
b263bf66
     avpriv_set_pts_info(fst, 33, 1, 90000);
6741f7c9
     fst->sample_aspect_ratio = codec->sample_aspect_ratio;
2effd274
     stream->streams[stream->nb_streams++] = fst;
     return fst;
 }
 
85f07f22
 /* return the stream number in the feed */
b29f97d1
 static int add_av_stream(FFStream *feed, AVStream *st)
85f07f22
 {
     AVStream *fst;
     AVCodecContext *av, *av1;
     int i;
 
01f4895c
     av = st->codec;
85f07f22
     for(i=0;i<feed->nb_streams;i++) {
         st = feed->streams[i];
01f4895c
         av1 = st->codec;
f747e6d3
         if (av1->codec_id == av->codec_id &&
             av1->codec_type == av->codec_type &&
85f07f22
             av1->bit_rate == av->bit_rate) {
 
             switch(av->codec_type) {
72415b2a
             case AVMEDIA_TYPE_AUDIO:
85f07f22
                 if (av1->channels == av->channels &&
                     av1->sample_rate == av->sample_rate)
71a1d111
                     return i;
85f07f22
                 break;
72415b2a
             case AVMEDIA_TYPE_VIDEO:
85f07f22
                 if (av1->width == av->width &&
                     av1->height == av->height &&
c0df9d75
                     av1->time_base.den == av->time_base.den &&
                     av1->time_base.num == av->time_base.num &&
85f07f22
                     av1->gop_size == av->gop_size)
71a1d111
                     return i;
85f07f22
                 break;
f747e6d3
             default:
0f4e8165
                 abort();
85f07f22
             }
         }
     }
115329f1
 
e175b55e
     fst = add_av_stream1(feed, av, 0);
85f07f22
     if (!fst)
         return -1;
     return feed->nb_streams - 1;
 }
 
b29f97d1
 static void remove_stream(FFStream *stream)
2effd274
 {
     FFStream **ps;
     ps = &first_stream;
     while (*ps != NULL) {
611c5741
         if (*ps == stream)
2effd274
             *ps = (*ps)->next;
611c5741
         else
2effd274
             ps = &(*ps)->next;
     }
 }
 
0fa45e19
 /* specific mpeg4 handling : we extract the raw parameters */
b29f97d1
 static void extract_mpeg4_header(AVFormatContext *infile)
0fa45e19
 {
     int mpeg4_count, i, size;
     AVPacket pkt;
     AVStream *st;
0c1a9eda
     const uint8_t *p;
0fa45e19
 
566de8cd
     infile->flags |= AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE;
 
0fa45e19
     mpeg4_count = 0;
     for(i=0;i<infile->nb_streams;i++) {
         st = infile->streams[i];
36ef5369
         if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
01f4895c
             st->codec->extradata_size == 0) {
0fa45e19
             mpeg4_count++;
         }
     }
     if (!mpeg4_count)
         return;
 
d445a7e9
     printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
0fa45e19
     while (mpeg4_count > 0) {
566de8cd
         if (av_read_frame(infile, &pkt) < 0)
0fa45e19
             break;
         st = infile->streams[pkt.stream_index];
36ef5369
         if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
01f4895c
             st->codec->extradata_size == 0) {
             av_freep(&st->codec->extradata);
0fa45e19
             /* fill extradata with the header */
             /* XXX: we make hard suppositions here ! */
             p = pkt.data;
             while (p < pkt.data + pkt.size - 4) {
                 /* stop when vop header is found */
115329f1
                 if (p[0] == 0x00 && p[1] == 0x00 &&
0fa45e19
                     p[2] == 0x01 && p[3] == 0xb6) {
                     size = p - pkt.data;
750f0e1f
                     //                    av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
9db5f820
                     st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
01f4895c
                     st->codec->extradata_size = size;
                     memcpy(st->codec->extradata, pkt.data, size);
0fa45e19
                     break;
                 }
                 p++;
             }
             mpeg4_count--;
         }
         av_free_packet(&pkt);
     }
 }
 
2effd274
 /* compute the needed AVStream for each file */
b29f97d1
 static void build_file_streams(void)
2effd274
 {
     FFStream *stream, *stream_next;
f61d45c9
     int i, ret;
2effd274
 
     /* gather all streams */
     for(stream = first_stream; stream != NULL; stream = stream_next) {
50f2dfad
         AVFormatContext *infile = NULL;
2effd274
         stream_next = stream->next;
         if (stream->stream_type == STREAM_TYPE_LIVE &&
             !stream->feed) {
             /* the stream comes from a file */
             /* try to open the file */
             /* open stream */
25e3e53d
             if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
e240a0bb
                 /* specific case : if transport stream output to RTP,
                    we use a raw transport stream reader */
50f2dfad
                 av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
e240a0bb
             }
115329f1
 
b133ec62
             if (!stream->feed_filename[0]) {
                 http_log("Unspecified feed file for stream '%s'\n", stream->filename);
                 goto fail;
             }
 
             http_log("Opening feed file '%s' for stream '%s'\n", stream->feed_filename, stream->filename);
50f2dfad
             if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
b58161d6
                 http_log("Could not open '%s': %s\n", stream->feed_filename, av_err2str(ret));
2effd274
                 /* remove stream (no need to spend more time on it) */
             fail:
                 remove_stream(stream);
             } else {
                 /* find all the AVStreams inside and reference them in
                    'stream' */
c960e67a
                 if (avformat_find_stream_info(infile, NULL) < 0) {
2dae1dd0
                     http_log("Could not find codec parameters from '%s'\n",
2effd274
                              stream->feed_filename);
cd3716b9
                     avformat_close_input(&infile);
2effd274
                     goto fail;
                 }
0fa45e19
                 extract_mpeg4_header(infile);
 
611c5741
                 for(i=0;i<infile->nb_streams;i++)
e175b55e
                     add_av_stream1(stream, infile->streams[i]->codec, 1);
611c5741
 
cd3716b9
                 avformat_close_input(&infile);
2effd274
             }
         }
     }
 }
 
85f07f22
 /* compute the needed AVStream for each feed */
b29f97d1
 static void build_feed_streams(void)
85f07f22
 {
     FFStream *stream, *feed;
     int i;
 
     /* gather all streams */
     for(stream = first_stream; stream != NULL; stream = stream->next) {
         feed = stream->feed;
         if (feed) {
cde25790
             if (stream->is_feed) {
611c5741
                 for(i=0;i<stream->nb_streams;i++)
85f07f22
                     stream->feed_streams[i] = i;
863e2046
             } else {
                 /* we handle a stream coming from a feed */
                 for(i=0;i<stream->nb_streams;i++)
                     stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
85f07f22
             }
         }
     }
 
     /* create feed files if needed */
     for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
         int fd;
 
55815edc
         if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
59eb2ed1
             /* See if it matches */
50f2dfad
             AVFormatContext *s = NULL;
59eb2ed1
             int matches = 0;
 
50f2dfad
             if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
00969376
                 /* set buffer size */
                 ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
59eb2ed1
                 /* Now see if it matches */
                 if (s->nb_streams == feed->nb_streams) {
                     matches = 1;
                     for(i=0;i<s->nb_streams;i++) {
                         AVStream *sf, *ss;
                         sf = feed->streams[i];
                         ss = s->streams[i];
 
                         if (sf->index != ss->index ||
                             sf->id != ss->id) {
80b616fc
                             http_log("Index & Id do not match for stream %d (%s)\n",
e240a0bb
                                    i, feed->feed_filename);
59eb2ed1
                             matches = 0;
                         } else {
                             AVCodecContext *ccf, *ccs;
 
01f4895c
                             ccf = sf->codec;
                             ccs = ss->codec;
59eb2ed1
 #define CHECK_CODEC(x)  (ccf->x != ccs->x)
 
111c9359
                             if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
80b616fc
                                 http_log("Codecs do not match for stream %d\n", i);
59eb2ed1
                                 matches = 0;
                             } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
80b616fc
                                 http_log("Codec bitrates do not match for stream %d\n", i);
59eb2ed1
                                 matches = 0;
72415b2a
                             } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
c0df9d75
                                 if (CHECK_CODEC(time_base.den) ||
                                     CHECK_CODEC(time_base.num) ||
59eb2ed1
                                     CHECK_CODEC(width) ||
                                     CHECK_CODEC(height)) {
80b616fc
                                     http_log("Codec width, height and framerate do not match for stream %d\n", i);
59eb2ed1
                                     matches = 0;
                                 }
72415b2a
                             } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
59eb2ed1
                                 if (CHECK_CODEC(sample_rate) ||
                                     CHECK_CODEC(channels) ||
                                     CHECK_CODEC(frame_size)) {
80b616fc
                                     http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
59eb2ed1
                                     matches = 0;
                                 }
                             } else {
80b616fc
                                 http_log("Unknown codec type\n");
59eb2ed1
                                 matches = 0;
                             }
                         }
611c5741
                         if (!matches)
59eb2ed1
                             break;
                     }
611c5741
                 } else
80b616fc
                     http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
59eb2ed1
                         feed->feed_filename, s->nb_streams, feed->nb_streams);
 
cd3716b9
                 avformat_close_input(&s);
611c5741
             } else
80b616fc
                 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
59eb2ed1
                         feed->feed_filename);
611c5741
 
e322ea48
             if (!matches) {
                 if (feed->readonly) {
80b616fc
                     http_log("Unable to delete feed file '%s' as it is marked readonly\n",
e322ea48
                         feed->feed_filename);
                     exit(1);
                 }
59eb2ed1
                 unlink(feed->feed_filename);
e322ea48
             }
59eb2ed1
         }
55815edc
         if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
c18cfd10
             AVFormatContext *s = avformat_alloc_context();
85f07f22
 
e322ea48
             if (feed->readonly) {
80b616fc
                 http_log("Unable to create feed file '%s' as it is marked readonly\n",
e322ea48
                     feed->feed_filename);
                 exit(1);
             }
 
85f07f22
             /* only write the header of the ffm file */
59d96941
             if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
b4befb99
                 http_log("Could not open output feed file '%s'\n",
                          feed->feed_filename);
85f07f22
                 exit(1);
             }
bd7cf6ad
             s->oformat = feed->fmt;
85f07f22
             s->nb_streams = feed->nb_streams;
840238b8
             s->streams = feed->streams;
50f2dfad
             if (avformat_write_header(s, NULL) < 0) {
f1b6c142
                 http_log("Container doesn't support the required parameters\n");
f75cdda7
                 exit(1);
             }
bd7cf6ad
             /* XXX: need better api */
             av_freep(&s->priv_data);
22a3212e
             avio_close(s->pb);
c18cfd10
             s->streams = NULL;
             s->nb_streams = 0;
             avformat_free_context(s);
85f07f22
         }
         /* get feed size and write index */
         fd = open(feed->feed_filename, O_RDONLY);
         if (fd < 0) {
b4befb99
             http_log("Could not open output feed file '%s'\n",
85f07f22
                     feed->feed_filename);
             exit(1);
         }
 
7e24aa0c
         feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
85f07f22
         feed->feed_size = lseek(fd, 0, SEEK_END);
         /* ensure that we do not wrap before the end of file */
6b0bdc75
         if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
85f07f22
             feed->feed_max_size = feed->feed_size;
 
         close(fd);
     }
 }
 
6edd6884
 /* compute the bandwidth used by each stream */
 static void compute_bandwidth(void)
 {
177d2564
     unsigned bandwidth;
     int i;
6edd6884
     FFStream *stream;
115329f1
 
6edd6884
     for(stream = first_stream; stream != NULL; stream = stream->next) {
         bandwidth = 0;
         for(i=0;i<stream->nb_streams;i++) {
             AVStream *st = stream->streams[i];
01f4895c
             switch(st->codec->codec_type) {
72415b2a
             case AVMEDIA_TYPE_AUDIO:
             case AVMEDIA_TYPE_VIDEO:
01f4895c
                 bandwidth += st->codec->bit_rate;
6edd6884
                 break;
             default:
                 break;
             }
         }
         stream->bandwidth = (bandwidth + 999) / 1000;
     }
 }
 
85f07f22
 /* add a codec and set the default parameters */
b29f97d1
 static void add_codec(FFStream *stream, AVCodecContext *av)
85f07f22
 {
     AVStream *st;
 
0f46825d
     if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
ff814c75
         return;
0f46825d
 
85f07f22
     /* compute default parameters */
     switch(av->codec_type) {
72415b2a
     case AVMEDIA_TYPE_AUDIO:
85f07f22
         if (av->bit_rate == 0)
             av->bit_rate = 64000;
         if (av->sample_rate == 0)
             av->sample_rate = 22050;
         if (av->channels == 0)
             av->channels = 1;
         break;
72415b2a
     case AVMEDIA_TYPE_VIDEO:
85f07f22
         if (av->bit_rate == 0)
             av->bit_rate = 64000;
c0df9d75
         if (av->time_base.num == 0){
             av->time_base.den = 5;
             av->time_base.num = 1;
14bea432
         }
85f07f22
         if (av->width == 0 || av->height == 0) {
             av->width = 160;
             av->height = 128;
         }
ba9b374f
         /* Bitrate tolerance is less for streaming */
42a63c6a
         if (av->bit_rate_tolerance == 0)
1692008f
             av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
                       (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
42a63c6a
         if (av->qmin == 0)
             av->qmin = 3;
         if (av->qmax == 0)
             av->qmax = 31;
         if (av->max_qdiff == 0)
             av->max_qdiff = 3;
ba9b374f
         av->qcompress = 0.5;
         av->qblur = 0.5;
68d7eef9
 
115329f1
         if (!av->nsse_weight)
291fe90a
             av->nsse_weight = 8;
 
         av->frame_skip_cmp = FF_CMP_DCTMAX;
1bf5228e
         if (!av->me_method)
90c92100
             av->me_method = ME_EPZS;
291fe90a
         av->rc_buffer_aggressivity = 1.0;
 
a782f209
         if (!av->rc_eq)
e85771f2
             av->rc_eq = av_strdup("tex^qComp");
a782f209
         if (!av->i_quant_factor)
b3a391e8
             av->i_quant_factor = -0.8;
a782f209
         if (!av->b_quant_factor)
             av->b_quant_factor = 1.25;
         if (!av->b_quant_offset)
             av->b_quant_offset = 1.25;
d6562d2c
         if (!av->rc_max_rate)
             av->rc_max_rate = av->bit_rate * 2;
a782f209
 
291fe90a
         if (av->rc_max_rate && !av->rc_buffer_size) {
             av->rc_buffer_size = av->rc_max_rate;
         }
 
 
85f07f22
         break;
f747e6d3
     default:
0f4e8165
         abort();
85f07f22
     }
 
     st = av_mallocz(sizeof(AVStream));
     if (!st)
         return;
71a861cf
     st->codec = avcodec_alloc_context3(NULL);
85f07f22
     stream->streams[stream->nb_streams++] = st;
01f4895c
     memcpy(st->codec, av, sizeof(AVCodecContext));
85f07f22
 }
 
8adaee56
 static enum AVCodecID opt_codec(const char *name, enum AVMediaType type)
f747e6d3
 {
8adaee56
     AVCodec *codec = avcodec_find_encoder_by_name(name);
f747e6d3
 
8adaee56
     if (!codec || codec->type != type)
36ef5369
         return AV_CODEC_ID_NONE;
8adaee56
     return codec->id;
f747e6d3
 }
 
df1a4b11
 static int ffserver_opt_default(const char *opt, const char *arg,
7ab08864
                        AVCodecContext *avctx, int type)
 {
f16dd7e6
     int ret = 0;
dc59ec5e
     const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
5c548937
     if(o)
3b3ea346
         ret = av_opt_set(avctx, opt, arg, 0);
f16dd7e6
     return ret;
7ab08864
 }
 
8190f62f
 static int ffserver_opt_preset(const char *arg,
                        AVCodecContext *avctx, int type,
36ef5369
                        enum AVCodecID *audio_id, enum AVCodecID *video_id)
8190f62f
 {
     FILE *f=NULL;
     char filename[1000], tmp[1000], tmp2[1000], line[1000];
6e872935
     int ret = 0;
     AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
8190f62f
 
6e872935
     if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
                               codec ? codec->name : NULL))) {
8190f62f
         fprintf(stderr, "File for preset '%s' not found\n", arg);
         return 1;
     }
 
     while(!feof(f)){
         int e= fscanf(f, "%999[^\n]\n", line) - 1;
         if(line[0] == '#' && !e)
             continue;
         e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
         if(e){
             fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
             ret = 1;
             break;
         }
         if(!strcmp(tmp, "acodec")){
8adaee56
             *audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO);
8190f62f
         }else if(!strcmp(tmp, "vcodec")){
8adaee56
             *video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO);
8190f62f
         }else if(!strcmp(tmp, "scodec")){
             /* opt_subtitle_codec(tmp2); */
         }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
             fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
             ret = 1;
             break;
         }
     }
 
     fclose(f);
 
     return ret;
 }
 
720530e5
 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename, const char *mime_type)
1642ee43
 {
0f52ef1a
     AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
1642ee43
 
     if (fmt) {
         AVOutputFormat *stream_fmt;
         char stream_format_name[64];
 
         snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
0f52ef1a
         stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
1642ee43
 
         if (stream_fmt)
             fmt = stream_fmt;
     }
 
     return fmt;
 }
 
1d6666a6
 static void report_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, ...)
472e12f5
 {
     va_list vl;
     va_start(vl, fmt);
1d6666a6
     av_log(NULL, log_level, "%s:%d: ", filename, line_num);
     av_vlog(NULL, log_level, fmt, vl);
472e12f5
     va_end(vl);
 
     (*errors)++;
 }
 
b29f97d1
 static int parse_ffconfig(const char *filename)
85f07f22
 {
     FILE *f;
     char line[1024];
     char cmd[64];
9985710a
     char arg[1024], arg2[1024];
85f07f22
     const char *p;
1d6666a6
     int val, errors, warnings, line_num;
cde25790
     FFStream **last_stream, *stream, *redirect;
637af098
     FFStream **last_feed, *feed, *s;
85f07f22
     AVCodecContext audio_enc, video_enc;
36ef5369
     enum AVCodecID audio_id, video_id;
7cbbc4f7
     int ret = 0;
85f07f22
 
     f = fopen(filename, "r");
     if (!f) {
7cbbc4f7
         ret = AVERROR(errno);
         av_log(NULL, AV_LOG_ERROR, "Could not open the configuration file '%s'\n", filename);
         return ret;
85f07f22
     }
115329f1
 
1d6666a6
     errors = warnings = 0;
85f07f22
     line_num = 0;
     first_stream = NULL;
     last_stream = &first_stream;
     first_feed = NULL;
     last_feed = &first_feed;
     stream = NULL;
     feed = NULL;
cde25790
     redirect = NULL;
36ef5369
     audio_id = AV_CODEC_ID_NONE;
     video_id = AV_CODEC_ID_NONE;
1d6666a6
 #define ERROR(...)   report_config_error(filename, line_num, AV_LOG_ERROR,   &errors,   __VA_ARGS__)
 #define WARNING(...) report_config_error(filename, line_num, AV_LOG_WARNING, &warnings, __VA_ARGS__)
472e12f5
 
85f07f22
     for(;;) {
         if (fgets(line, sizeof(line), f) == NULL)
             break;
         line_num++;
         p = line;
88d55b82
         while (av_isspace(*p))
85f07f22
             p++;
         if (*p == '\0' || *p == '#')
             continue;
 
         get_arg(cmd, sizeof(cmd), &p);
115329f1
 
bb3244de
         if (!av_strcasecmp(cmd, "Port")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
aabce533
             val = atoi(arg);
             if (val < 1 || val > 65536) {
472e12f5
                 ERROR("Invalid_port: %s\n", arg);
aabce533
             }
             my_http_addr.sin_port = htons(val);
bb3244de
         } else if (!av_strcasecmp(cmd, "BindAddress")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
ad8b8abc
             if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
472e12f5
                 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
2effd274
             }
f30cf51d
         } else if (!av_strcasecmp(cmd, "NoDaemon")) {
1d6666a6
             WARNING("NoDaemon option has no effect, you should remove it\n");
bb3244de
         } else if (!av_strcasecmp(cmd, "RTSPPort")) {
2effd274
             get_arg(arg, sizeof(arg), &p);
aabce533
             val = atoi(arg);
             if (val < 1 || val > 65536) {
472e12f5
                 ERROR("%s:%d: Invalid port: %s\n", arg);
aabce533
             }
             my_rtsp_addr.sin_port = htons(atoi(arg));
bb3244de
         } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
2effd274
             get_arg(arg, sizeof(arg), &p);
ad8b8abc
             if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
472e12f5
                 ERROR("Invalid host/IP address: %s\n", arg);
85f07f22
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
1c9ff179
             get_arg(arg, sizeof(arg), &p);
             val = atoi(arg);
             if (val < 1 || val > 65536) {
472e12f5
                 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
1c9ff179
             }
             nb_max_http_connections = val;
bb3244de
         } else if (!av_strcasecmp(cmd, "MaxClients")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
             val = atoi(arg);
1c9ff179
             if (val < 1 || val > nb_max_http_connections) {
472e12f5
                 ERROR("Invalid MaxClients: %s\n", arg);
85f07f22
             } else {
                 nb_max_connections = val;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
0dc17c21
             int64_t llval;
42a63c6a
             get_arg(arg, sizeof(arg), &p);
73920ac5
             llval = strtoll(arg, NULL, 10);
0dc17c21
             if (llval < 10 || llval > 10000000) {
472e12f5
                 ERROR("Invalid MaxBandwidth: %s\n", arg);
611c5741
             } else
0dc17c21
                 max_bandwidth = llval;
bb3244de
         } else if (!av_strcasecmp(cmd, "CustomLog")) {
4e8f77ab
             if (!ffserver_debug)
                 get_arg(logfilename, sizeof(logfilename), &p);
bb3244de
         } else if (!av_strcasecmp(cmd, "<Feed")) {
85f07f22
             /*********************************************/
             /* Feed related options */
             char *q;
             if (stream || feed) {
472e12f5
                 ERROR("Already in a tag\n");
85f07f22
             } else {
                 feed = av_mallocz(sizeof(FFStream));
7cbbc4f7
                 if (!feed) {
                     ret = AVERROR(ENOMEM);
                     goto end;
                 }
85f07f22
                 get_arg(feed->filename, sizeof(feed->filename), &p);
                 q = strrchr(feed->filename, '>');
                 if (*q)
                     *q = '\0';
637af098
 
                 for (s = first_feed; s; s = s->next) {
                     if (!strcmp(feed->filename, s->filename)) {
472e12f5
                         ERROR("Feed '%s' already registered\n", s->filename);
637af098
                     }
                 }
 
0f52ef1a
                 feed->fmt = av_guess_format("ffm", NULL, NULL);
ab89d2dd
                 /* default feed file */
85f07f22
                 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
                          "/tmp/%s.ffm", feed->filename);
                 feed->feed_max_size = 5 * 1024 * 1024;
                 feed->is_feed = 1;
                 feed->feed = feed; /* self feeding :-) */
637af098
 
                 /* add in stream list */
                 *last_stream = feed;
                 last_stream = &feed->next;
                 /* add in feed list */
                 *last_feed = feed;
                 last_feed = &feed->next_feed;
85f07f22
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Launch")) {
cde25790
             if (feed) {
                 int i;
 
90901860
                 feed->child_argv = av_mallocz(64 * sizeof(char *));
7cbbc4f7
                 if (!feed->child_argv) {
                     ret = AVERROR(ENOMEM);
                     goto end;
                 }
ac6a655b
                 for (i = 0; i < 62; i++) {
5eb782f0
                     get_arg(arg, sizeof(arg), &p);
                     if (!arg[0])
cde25790
                         break;
 
5eb782f0
                     feed->child_argv[i] = av_strdup(arg);
7cbbc4f7
                     if (!feed->child_argv[i]) {
                         ret = AVERROR(ENOMEM);
                         goto end;
                     }
cde25790
                 }
 
7cbbc4f7
                 feed->child_argv[i] =
                     av_asprintf("http://%s:%d/%s",
                                 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
                                 inet_ntoa(my_http_addr.sin_addr), ntohs(my_http_addr.sin_port),
                                 feed->filename);
                 if (!feed->child_argv[i]) {
                     ret = AVERROR(ENOMEM);
                     goto end;
                 }
cde25790
             }
77f5cb92
         } else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) {
e322ea48
             if (feed) {
                 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
77f5cb92
                 feed->readonly = !av_strcasecmp(cmd, "ReadOnlyFile");
e322ea48
             } else if (stream) {
                 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Truncate")) {
861ec13a
             if (feed) {
                 get_arg(arg, sizeof(arg), &p);
b1049f89
                 /* assume Truncate is true in case no argument is specified */
                 if (!arg[0]) {
                     feed->truncate = 1;
                 } else {
1d6666a6
                     WARNING("Truncate N syntax in configuration file is deprecated, "
                             "use Truncate alone with no arguments\n");
b1049f89
                     feed->truncate = strtod(arg, NULL);
                 }
861ec13a
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
85f07f22
             if (feed) {
815f98cc
                 char *p1;
85f07f22
                 double fsize;
 
                 get_arg(arg, sizeof(arg), &p);
                 p1 = arg;
815f98cc
                 fsize = strtod(p1, &p1);
88d55b82
                 switch(av_toupper(*p1)) {
85f07f22
                 case 'K':
                     fsize *= 1024;
                     break;
                 case 'M':
                     fsize *= 1024 * 1024;
                     break;
                 case 'G':
                     fsize *= 1024 * 1024 * 1024;
                     break;
                 }
0c1a9eda
                 feed->feed_max_size = (int64_t)fsize;
64159a58
                 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
472e12f5
                     ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
64159a58
                 }
85f07f22
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "</Feed>")) {
85f07f22
             if (!feed) {
472e12f5
                 ERROR("No corresponding <Feed> for </Feed>\n");
85f07f22
             }
             feed = NULL;
bb3244de
         } else if (!av_strcasecmp(cmd, "<Stream")) {
85f07f22
             /*********************************************/
             /* Stream related options */
             char *q;
             if (stream || feed) {
472e12f5
                 ERROR("Already in a tag\n");
85f07f22
             } else {
637af098
                 FFStream *s;
85f07f22
                 stream = av_mallocz(sizeof(FFStream));
7cbbc4f7
                 if (!stream) {
                     ret = AVERROR(ENOMEM);
                     goto end;
                 }
85f07f22
                 get_arg(stream->filename, sizeof(stream->filename), &p);
                 q = strrchr(stream->filename, '>');
26afdbcf
                 if (q)
85f07f22
                     *q = '\0';
637af098
 
                 for (s = first_stream; s; s = s->next) {
                     if (!strcmp(stream->filename, s->filename)) {
472e12f5
                         ERROR("Stream '%s' already registered\n", s->filename);
637af098
                     }
                 }
 
1642ee43
                 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
0b717e24
                 avcodec_get_context_defaults3(&video_enc, NULL);
                 avcodec_get_context_defaults3(&audio_enc, NULL);
d0492578
 
36ef5369
                 audio_id = AV_CODEC_ID_NONE;
                 video_id = AV_CODEC_ID_NONE;
85f07f22
                 if (stream->fmt) {
                     audio_id = stream->fmt->audio_codec;
                     video_id = stream->fmt->video_codec;
                 }
637af098
 
                 *last_stream = stream;
                 last_stream = &stream->next;
85f07f22
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Feed")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 FFStream *sfeed;
115329f1
 
85f07f22
                 sfeed = first_feed;
                 while (sfeed != NULL) {
                     if (!strcmp(sfeed->filename, arg))
                         break;
                     sfeed = sfeed->next_feed;
                 }
611c5741
                 if (!sfeed)
00835147
                     ERROR("Feed with name '%s' for stream '%s' is not defined\n", arg, stream->filename);
611c5741
                 else
85f07f22
                     stream->feed = sfeed;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Format")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
5735f6dc
             if (stream) {
65f2f028
                 if (!strcmp(arg, "status")) {
                     stream->stream_type = STREAM_TYPE_STATUS;
                     stream->fmt = NULL;
                 } else {
                     stream->stream_type = STREAM_TYPE_LIVE;
                     /* jpeg cannot be used here, so use single frame jpeg */
                     if (!strcmp(arg, "jpeg"))
                         strcpy(arg, "mjpeg");
1642ee43
                     stream->fmt = ffserver_guess_format(arg, NULL, NULL);
65f2f028
                     if (!stream->fmt) {
472e12f5
                         ERROR("Unknown Format: %s\n", arg);
65f2f028
                     }
                 }
                 if (stream->fmt) {
                     audio_id = stream->fmt->audio_codec;
                     video_id = stream->fmt->video_codec;
85f07f22
                 }
5735f6dc
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "InputFormat")) {
d2040a8f
             get_arg(arg, sizeof(arg), &p);
a69b06be
             if (stream) {
f9739144
                 stream->ifmt = av_find_input_format(arg);
                 if (!stream->ifmt) {
472e12f5
                     ERROR("Unknown input format: %s\n", arg);
f9739144
                 }
a69b06be
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "FaviconURL")) {
cde25790
             if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
                 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
             } else {
472e12f5
                 ERROR("FaviconURL only permitted for status streams\n");
cde25790
             }
9985710a
         } else if (!av_strcasecmp(cmd, "Author")    ||
                    !av_strcasecmp(cmd, "Comment")   ||
                    !av_strcasecmp(cmd, "Copyright") ||
                    !av_strcasecmp(cmd, "Title")) {
             get_arg(arg, sizeof(arg), &p);
 
             if (stream) {
                 char key[32];
                 int i, ret;
 
                 for (i = 0; i < strlen(cmd); i++)
                     key[i] = av_tolower(cmd[i]);
                 key[i] = 0;
1d6666a6
                 WARNING("'%s' option in configuration file is deprecated, "
                         "use 'Metadata %s VALUE' instead\n", cmd, key);
9985710a
                 if ((ret = av_dict_set(&stream->metadata, key, arg, 0)) < 0) {
                     ERROR("Could not set metadata '%s' to value '%s': %s\n",
                           key, arg, av_err2str(ret));
                 }
             }
         } else if (!av_strcasecmp(cmd, "Metadata")) {
             get_arg(arg, sizeof(arg), &p);
             get_arg(arg2, sizeof(arg2), &p);
             if (stream) {
                 int ret;
                 if ((ret = av_dict_set(&stream->metadata, arg, arg2, 0)) < 0) {
                     ERROR("Could not set metadata '%s' to value '%s': %s\n",
                           arg, arg2, av_err2str(ret));
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Preroll")) {
42a63c6a
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
8256c0a3
                 stream->prebuffer = atof(arg) * 1000;
bb3244de
         } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
611c5741
             if (stream)
79c4ea3c
                 stream->send_on_key = 1;
bb3244de
         } else if (!av_strcasecmp(cmd, "AudioCodec")) {
f747e6d3
             get_arg(arg, sizeof(arg), &p);
8adaee56
             audio_id = opt_codec(arg, AVMEDIA_TYPE_AUDIO);
36ef5369
             if (audio_id == AV_CODEC_ID_NONE) {
472e12f5
                 ERROR("Unknown AudioCodec: %s\n", arg);
f747e6d3
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoCodec")) {
f747e6d3
             get_arg(arg, sizeof(arg), &p);
8adaee56
             video_id = opt_codec(arg, AVMEDIA_TYPE_VIDEO);
36ef5369
             if (video_id == AV_CODEC_ID_NONE) {
472e12f5
                 ERROR("Unknown VideoCodec: %s\n", arg);
f747e6d3
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MaxTime")) {
ec3b2232
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
8256c0a3
                 stream->max_time = atof(arg) * 1000;
bb3244de
         } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
3b963552
                 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
bb3244de
         } else if (!av_strcasecmp(cmd, "AudioChannels")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 audio_enc.channels = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 audio_enc.sample_rate = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
d6562d2c
             if (stream) {
                 int minrate, maxrate;
 
                 get_arg(arg, sizeof(arg), &p);
 
                 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
                     video_enc.rc_min_rate = minrate * 1000;
                     video_enc.rc_max_rate = maxrate * 1000;
                 } else {
472e12f5
                     ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
d6562d2c
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Debug")) {
291fe90a
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
                 video_enc.debug = strtol(arg,0,0);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Strict")) {
291fe90a
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
                 video_enc.strict_std_compliance = atoi(arg);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
46026f4e
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
291fe90a
                 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
46026f4e
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
d6562d2c
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
                 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 video_enc.bit_rate = atoi(arg) * 1000;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoSize")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
7cbbc4f7
                 ret = av_parse_video_size(&video_enc.width, &video_enc.height, arg);
                 if (ret < 0) {
                     ERROR("Invalid video size '%s'\n", arg);
                 } else {
                     if ((video_enc.width % 16) != 0 ||
                         (video_enc.height % 16) != 0) {
                         ERROR("Image size must be a multiple of 16\n");
                     }
85f07f22
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
36907468
                 AVRational frame_rate;
126b638e
                 if (av_parse_video_rate(&frame_rate, arg) < 0) {
472e12f5
                     ERROR("Incorrect frame rate: %s\n", arg);
36907468
                 } else {
                     video_enc.time_base.num = frame_rate.den;
                     video_enc.time_base.den = frame_rate.num;
                 }
85f07f22
             }
90ca8142
         } else if (!av_strcasecmp(cmd, "PixelFormat")) {
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 video_enc.pix_fmt = av_get_pix_fmt(arg);
                 if (video_enc.pix_fmt == AV_PIX_FMT_NONE) {
                     ERROR("Unknown pixel format: %s\n", arg);
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 video_enc.gop_size = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
611c5741
             if (stream)
85f07f22
                 video_enc.gop_size = 1;
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
611c5741
             if (stream)
7d1c3fc1
                 video_enc.mb_decision = FF_MB_DECISION_BITS;
bb3244de
         } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
e322ea48
             if (stream) {
7d1c3fc1
                 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
e322ea48
                 video_enc.flags |= CODEC_FLAG_4MV;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
                    !av_strcasecmp(cmd, "AVOptionAudio")) {
7ab08864
             AVCodecContext *avctx;
             int type;
             get_arg(arg, sizeof(arg), &p);
             get_arg(arg2, sizeof(arg2), &p);
bb3244de
             if (!av_strcasecmp(cmd, "AVOptionVideo")) {
7ab08864
                 avctx = &video_enc;
                 type = AV_OPT_FLAG_VIDEO_PARAM;
             } else {
                 avctx = &audio_enc;
                 type = AV_OPT_FLAG_AUDIO_PARAM;
             }
df1a4b11
             if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
f7fbb7ac
                 ERROR("Error setting %s option to %s %s\n", cmd, arg, arg2);
7ab08864
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
                    !av_strcasecmp(cmd, "AVPresetAudio")) {
8190f62f
             AVCodecContext *avctx;
             int type;
             get_arg(arg, sizeof(arg), &p);
bb3244de
             if (!av_strcasecmp(cmd, "AVPresetVideo")) {
8190f62f
                 avctx = &video_enc;
                 video_enc.codec_id = video_id;
                 type = AV_OPT_FLAG_VIDEO_PARAM;
             } else {
                 avctx = &audio_enc;
                 audio_enc.codec_id = audio_id;
                 type = AV_OPT_FLAG_AUDIO_PARAM;
             }
             if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
                 ERROR("AVPreset error: %s\n", arg);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoTag")) {
038a1243
             get_arg(arg, sizeof(arg), &p);
611c5741
             if ((strlen(arg) == 4) && stream)
acd0026c
                 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
bb3244de
         } else if (!av_strcasecmp(cmd, "BitExact")) {
611c5741
             if (stream)
267b0e57
                 video_enc.flags |= CODEC_FLAG_BITEXACT;
bb3244de
         } else if (!av_strcasecmp(cmd, "DctFastint")) {
611c5741
             if (stream)
267b0e57
                 video_enc.dct_algo  = FF_DCT_FASTINT;
bb3244de
         } else if (!av_strcasecmp(cmd, "IdctSimple")) {
611c5741
             if (stream)
267b0e57
                 video_enc.idct_algo = FF_IDCT_SIMPLE;
bb3244de
         } else if (!av_strcasecmp(cmd, "Qscale")) {
267b0e57
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 video_enc.flags |= CODEC_FLAG_QSCALE;
                 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
a782f209
             get_arg(arg, sizeof(arg), &p);
42a63c6a
             if (stream) {
                 video_enc.max_qdiff = atoi(arg);
                 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
472e12f5
                     ERROR("VideoQDiff out of range\n");
42a63c6a
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoQMax")) {
a782f209
             get_arg(arg, sizeof(arg), &p);
42a63c6a
             if (stream) {
                 video_enc.qmax = atoi(arg);
                 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
472e12f5
                     ERROR("VideoQMax out of range\n");
42a63c6a
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoQMin")) {
a782f209
             get_arg(arg, sizeof(arg), &p);
42a63c6a
             if (stream) {
                 video_enc.qmin = atoi(arg);
                 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
472e12f5
                     ERROR("VideoQMin out of range\n");
42a63c6a
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "LumiMask")) {
6b10e6e4
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.lumi_masking = atof(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "DarkMask")) {
6b10e6e4
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.dark_masking = atof(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "NoVideo")) {
36ef5369
             video_id = AV_CODEC_ID_NONE;
bb3244de
         } else if (!av_strcasecmp(cmd, "NoAudio")) {
36ef5369
             audio_id = AV_CODEC_ID_NONE;
bb3244de
         } else if (!av_strcasecmp(cmd, "ACL")) {
58f48adb
             parse_acl_row(stream, feed, NULL, p, filename, line_num);
bb3244de
         } else if (!av_strcasecmp(cmd, "DynamicACL")) {
58f48adb
             if (stream) {
                 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
8256c0a3
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "RTSPOption")) {
2effd274
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 av_freep(&stream->rtsp_option);
65e72261
                 stream->rtsp_option = av_strdup(arg);
2effd274
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
829ac53d
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
ad8b8abc
                 if (resolve_host(&stream->multicast_ip, arg) != 0) {
472e12f5
                     ERROR("Invalid host/IP address: %s\n", arg);
829ac53d
                 }
                 stream->is_multicast = 1;
6edd6884
                 stream->loop = 1; /* default is looping */
829ac53d
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MulticastPort")) {
829ac53d
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
829ac53d
                 stream->multicast_port = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
6edd6884
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6edd6884
                 stream->multicast_ttl = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "NoLoop")) {
611c5741
             if (stream)
6edd6884
                 stream->loop = 0;
bb3244de
         } else if (!av_strcasecmp(cmd, "</Stream>")) {
85f07f22
             if (!stream) {
472e12f5
                 ERROR("No corresponding <Stream> for </Stream>\n");
ce651af2
             } else {
7e183a93
                 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
36ef5369
                     if (audio_id != AV_CODEC_ID_NONE) {
72415b2a
                         audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
7e183a93
                         audio_enc.codec_id = audio_id;
                         add_codec(stream, &audio_enc);
                     }
36ef5369
                     if (video_id != AV_CODEC_ID_NONE) {
72415b2a
                         video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
7e183a93
                         video_enc.codec_id = video_id;
                         add_codec(stream, &video_enc);
                     }
85f07f22
                 }
7e183a93
                 stream = NULL;
ce651af2
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "<Redirect")) {
cde25790
             /*********************************************/
             char *q;
             if (stream || feed || redirect) {
472e12f5
                 ERROR("Already in a tag\n");
cde25790
             } else {
                 redirect = av_mallocz(sizeof(FFStream));
7cbbc4f7
                 if (!redirect) {
                     ret = AVERROR(ENOMEM);
                     goto end;
                 }
cde25790
                 *last_stream = redirect;
                 last_stream = &redirect->next;
 
                 get_arg(redirect->filename, sizeof(redirect->filename), &p);
                 q = strrchr(redirect->filename, '>');
                 if (*q)
                     *q = '\0';
                 redirect->stream_type = STREAM_TYPE_REDIRECT;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "URL")) {
611c5741
             if (redirect)
cde25790
                 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
bb3244de
         } else if (!av_strcasecmp(cmd, "</Redirect>")) {
cde25790
             if (!redirect) {
472e12f5
                 ERROR("No corresponding <Redirect> for </Redirect>\n");
a9c1bb71
             } else {
9667a2d2
                 if (!redirect->feed_filename[0]) {
472e12f5
                     ERROR("No URL found for <Redirect>\n");
9667a2d2
                 }
                 redirect = NULL;
a9c1bb71
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "LoadModule")) {
d010e95f
             ERROR("Loadable modules no longer supported\n");
85f07f22
         } else {
472e12f5
             ERROR("Incorrect keyword: '%s'\n", cmd);
85f07f22
         }
     }
472e12f5
 #undef ERROR
85f07f22
 
7cbbc4f7
 end:
85f07f22
     fclose(f);
7cbbc4f7
     if (ret < 0)
         return ret;
85f07f22
     if (errors)
7cbbc4f7
         return AVERROR(EINVAL);
85f07f22
     else
         return 0;
 }
 
5eb765ef
 static void handle_child_exit(int sig)
 {
     pid_t pid;
     int status;
 
     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
         FFStream *feed;
 
         for (feed = first_feed; feed; feed = feed->next) {
             if (feed->pid == pid) {
                 int uptime = time(0) - feed->pid_start;
 
                 feed->pid = 0;
                 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
 
611c5741
                 if (uptime < 30)
5eb765ef
                     /* Turn off any more restarts */
                     feed->child_argv = 0;
             }
         }
     }
 
     need_to_start_children = 1;
 }
 
cf2c671f
 static void opt_debug(void)
5a635bc7
 {
     ffserver_debug = 1;
4e8f77ab
     logfilename[0] = '-';
5a635bc7
 }
 
a3ad68d3
 void show_help_default(const char *opt, const char *arg)
5a635bc7
 {
10ba7404
     printf("usage: ffserver [options]\n"
5a635bc7
            "Hyper fast multi format Audio/Video streaming server\n");
     printf("\n");
f9fada27
     show_help_options(options, "Main options:", 0, 0, 0);
5a635bc7
 }
 
 static const OptionDef options[] = {
992f8eae
 #include "cmdutils_common_opts.h"
5a635bc7
     { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
     { "d", 0, {(void*)opt_debug}, "enable debug mode" },
     { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
     { NULL },
 };
 
85f07f22
 int main(int argc, char **argv)
 {
a92be9b8
     struct sigaction sigact = { { 0 } };
04702a0d
     int ret = 0;
85f07f22
 
4131f21f
     config_filename = av_strdup("/etc/ffserver.conf");
612a5049
 
182cbe43
     parse_loglevel(argc, argv, options);
2c4ae653
     av_register_all();
776f2bb9
     avformat_network_init();
85f07f22
 
452406bd
     show_banner(argc, argv, options);
3578e9a0
 
cde25790
     my_program_name = argv[0];
115329f1
 
7cc8d638
     parse_options(NULL, argc, argv, options, NULL);
85f07f22
 
59c2959d
     unsetenv("http_proxy");             /* Kill the http_proxy */
cde25790
 
576fb48e
     av_lfg_init(&random_state, av_get_random_seed());
8256c0a3
 
5eb765ef
     sigact.sa_handler = handle_child_exit;
     sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
     sigaction(SIGCHLD, &sigact, 0);
 
04702a0d
     if ((ret = parse_ffconfig(config_filename)) < 0) {
         fprintf(stderr, "Error reading configuration file '%s': %s\n",
                 config_filename, av_err2str(ret));
85f07f22
         exit(1);
     }
19c41c6d
     av_freep(&config_filename);
85f07f22
 
f10d55ed
     /* open log file if needed */
     if (logfilename[0] != '\0') {
         if (!strcmp(logfilename, "-"))
a9d9aa36
             logfile = stdout;
f10d55ed
         else
             logfile = fopen(logfilename, "a");
         av_log_set_callback(http_av_log);
     }
 
2effd274
     build_file_streams();
 
85f07f22
     build_feed_streams();
 
6edd6884
     compute_bandwidth();
 
85f07f22
     /* signal init */
     signal(SIGPIPE, SIG_IGN);
 
2effd274
     if (http_server() < 0) {
acae1492
         http_log("Could not start server\n");
85f07f22
         exit(1);
     }
 
     return 0;
 }