ffserver: move configuration code to separate file
authorLukasz Marek <lukasz.m.luki2@gmail.com>
Sun, 19 Oct 2014 13:39:06 +0000 (15:39 +0200)
committerLukasz Marek <lukasz.m.luki2@gmail.com>
Wed, 22 Oct 2014 20:37:40 +0000 (22:37 +0200)
This commit doesn't change any existing logic.
It moves ffserver configuration related code to separate file.
It intends to make maintaining easier.

Signed-off-by: Lukasz Marek <lukasz.m.luki2@gmail.com>
Makefile
ffserver.c
ffserver_config.c [new file with mode: 0644]
ffserver_config.h [new file with mode: 0644]

index 57f6a91..1e1dbb3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -32,6 +32,7 @@ OBJS-ffmpeg                   += ffmpeg_opt.o ffmpeg_filter.o
 OBJS-ffmpeg-$(HAVE_VDPAU_X11) += ffmpeg_vdpau.o
 OBJS-ffmpeg-$(HAVE_DXVA2_LIB) += ffmpeg_dxva2.o
 OBJS-ffmpeg-$(CONFIG_VDA)     += ffmpeg_vda.o
+OBJS-ffserver                 += ffserver_config.o
 
 TESTTOOLS   = audiogen videogen rotozoom tiny_psnr tiny_ssim base64
 HOSTPROGS  := $(TESTTOOLS:%=tests/%) doc/print_options
index e98cc62..ea2a2ae 100644 (file)
@@ -49,7 +49,6 @@
 #include "libavutil/dict.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mathematics.h"
-#include "libavutil/pixdesc.h"
 #include "libavutil/random_seed.h"
 #include "libavutil/parseutils.h"
 #include "libavutil/opt.h"
@@ -70,6 +69,7 @@
 #include <signal.h>
 
 #include "cmdutils.h"
+#include "ffserver_config.h"
 
 const char program_name[] = "ffserver";
 const int program_birth_year = 2000;
@@ -107,8 +107,6 @@ static const char * const http_state[] = {
     "RTSP_SEND_PACKET",
 };
 
-#define MAX_STREAMS 20
-
 #define IOBUFFER_INIT_SIZE 8192
 
 /* timeouts are in ms */
@@ -156,12 +154,12 @@ typedef struct HTTPContext {
     int pts_stream_index;        /* stream we choose as clock reference */
     int64_t cur_clock;           /* current clock reference value in us */
     /* output format handling */
-    struct FFStream *stream;
+    struct FFServerStream *stream;
     /* -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 feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */
+    int switch_feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */
     int switch_pending;
-    AVFormatContext fmt_ctx; /* instance of FFStream for one user */
+    AVFormatContext fmt_ctx; /* instance of FFServerStream for one user */
     int last_packet_sent; /* true if last data packet was sent */
     int suppress_log;
     DataRateData datarate;
@@ -182,95 +180,28 @@ typedef struct HTTPContext {
     /* RTP state specific */
     enum RTSPLowerTransport rtp_protocol;
     char session_id[32]; /* session id */
-    AVFormatContext *rtp_ctx[MAX_STREAMS];
+    AVFormatContext *rtp_ctx[FFSERVER_MAX_STREAMS];
 
     /* RTP/UDP specific */
-    URLContext *rtp_handles[MAX_STREAMS];
+    URLContext *rtp_handles[FFSERVER_MAX_STREAMS];
 
     /* RTP/TCP specific */
     struct HTTPContext *rtsp_c;
     uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
 } HTTPContext;
 
-/* each generated stream is described here */
-enum StreamType {
-    STREAM_TYPE_LIVE,
-    STREAM_TYPE_STATUS,
-    STREAM_TYPE_REDIRECT,
-};
-
-enum IPAddressAction {
-    IP_ALLOW = 1,
-    IP_DENY,
-};
-
-typedef struct IPAddressACL {
-    struct IPAddressACL *next;
-    enum IPAddressAction action;
-    /* These are in host order */
-    struct in_addr first;
-    struct in_addr last;
-} IPAddressACL;
-
-/* description of each stream of the ffserver.conf file */
-typedef struct FFStream {
-    enum StreamType stream_type;
-    char filename[1024];     /* stream filename */
-    struct FFStream *feed;   /* feed we are using (can be null if
-                                coming from file) */
-    AVDictionary *in_opts;   /* input parameters */
-    AVDictionary *metadata;  /* metadata to set on the stream */
-    AVInputFormat *ifmt;       /* if non NULL, force input format */
-    AVOutputFormat *fmt;
-    IPAddressACL *acl;
-    char dynamic_acl[1024];
-    int nb_streams;
-    int prebuffer;      /* Number of milliseconds early to start */
-    int64_t max_time;      /* Number of milliseconds to run */
-    int send_on_key;
-    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 */
-    pid_t pid;  /* Of ffmpeg process */
-    time_t pid_start;  /* Of ffmpeg process */
-    char **child_argv;
-    struct FFStream *next;
-    unsigned bandwidth; /* bandwidth, in kbits/s */
-    /* RTSP options */
-    char *rtsp_option;
-    /* multicast specific */
-    int is_multicast;
-    struct in_addr multicast_ip;
-    int multicast_port; /* first port used for multicast */
-    int multicast_ttl;
-    int loop; /* if true, send the stream in loops (only meaningful if file) */
-
-    /* feed specific */
-    int feed_opened;     /* true if someone is writing to the feed */
-    int is_feed;         /* true if it is a feed */
-    int readonly;        /* True if writing is prohibited to the file */
-    int truncate;        /* True if feeder connection truncate the feed file */
-    int conns_served;
-    int64_t bytes_served;
-    int64_t feed_max_size;      /* maximum storage size, zero means unlimited */
-    int64_t feed_write_index;   /* current write position in feed (it wraps around) */
-    int64_t feed_size;          /* current size of feed */
-    struct FFStream *next_feed;
-} FFStream;
-
 typedef struct FeedData {
     long long data_count;
     float avg_frame_size;   /* frame size averaged over last frames with exponential mean */
 } FeedData;
 
-static struct sockaddr_in my_http_addr;
-static struct sockaddr_in my_rtsp_addr;
-
-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 */
+
+static FFServerConfig config = {
+    .nb_max_http_connections = 2000,
+    .nb_max_connections = 5,
+    .max_bandwidth = 1000,
+};
 
 static void new_connection(int server_fd, int is_rtsp);
 static void close_connection(HTTPContext *c);
@@ -293,12 +224,12 @@ static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
 static void rtsp_cmd_interrupt(HTTPContext *c, const char *url, RTSPMessageHeader *h, int pause_only);
 
 /* SDP handling */
-static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
+static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer,
                                    struct in_addr my_ip);
 
 /* RTP handling */
 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
-                                       FFStream *stream, const char *session_id,
+                                       FFServerStream *stream, const char *session_id,
                                        enum RTSPLowerTransport rtp_protocol);
 static int rtp_new_av_stream(HTTPContext *c,
                              int stream_index, struct sockaddr_in *dest_addr,
@@ -306,18 +237,12 @@ static int rtp_new_av_stream(HTTPContext *c,
 
 static const char *my_program_name;
 
-static const char *config_filename;
-
-static int ffserver_debug;
 static int no_launch;
 static int need_to_start_children;
 
 /* maximum number of simultaneous HTTP connections */
-static unsigned int nb_max_http_connections = 2000;
-static unsigned int nb_max_connections = 5;
 static unsigned int nb_connections;
 
-static uint64_t max_bandwidth = 1000;
 static uint64_t current_bandwidth;
 
 static int64_t cur_time;           // Making this global saves on passing it around everywhere
@@ -367,41 +292,6 @@ static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
     ffm->file_size = file_size;
 }
 
-/* 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;
-        struct addrinfo hints = { 0 };
-        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;
-}
-
 static char *ctime1(char *buf2, int buf_size)
 {
     time_t ti;
@@ -487,7 +377,7 @@ static int compute_datarate(DataRateData *drd, int64_t count)
 }
 
 
-static void start_children(FFStream *feed)
+static void start_children(FFServerStream *feed)
 {
     if (no_launch)
         return;
@@ -527,7 +417,7 @@ static void start_children(FFStream *feed)
                 for (i = 3; i < 256; i++)
                     close(i);
 
-                if (!ffserver_debug) {
+                if (!config.debug) {
                     if (!freopen("/dev/null", "r", stdin))
                         http_log("failed to redirect STDIN to /dev/null\n;");
                     if (!freopen("/dev/null", "w", stdout))
@@ -585,14 +475,14 @@ static int socket_open_listen(struct sockaddr_in *my_addr)
 /* start all multicast streams */
 static void start_multicast(void)
 {
-    FFStream *stream;
+    FFServerStream *stream;
     char session_id[32];
     HTTPContext *rtp_c;
     struct sockaddr_in dest_addr = {0};
     int default_port, stream_index;
 
     default_port = 6000;
-    for(stream = first_stream; stream; stream = stream->next) {
+    for(stream = config.first_stream; stream; stream = stream->next) {
         if (stream->is_multicast) {
             unsigned random0 = av_lfg_get(&random_state);
             unsigned random1 = av_lfg_get(&random_state);
@@ -646,21 +536,21 @@ static int http_server(void)
     struct pollfd *poll_table, *poll_entry;
     HTTPContext *c, *c_next;
 
-    if(!(poll_table = av_mallocz_array(nb_max_http_connections + 2, sizeof(*poll_table)))) {
-        http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
+    if(!(poll_table = av_mallocz_array(config.nb_max_http_connections + 2, sizeof(*poll_table)))) {
+        http_log("Impossible to allocate a poll table handling %d connections.\n", config.nb_max_http_connections);
         return -1;
     }
 
-    if (my_http_addr.sin_port) {
-        server_fd = socket_open_listen(&my_http_addr);
+    if (config.http_addr.sin_port) {
+        server_fd = socket_open_listen(&config.http_addr);
         if (server_fd < 0) {
             av_free(poll_table);
             return -1;
         }
     }
 
-    if (my_rtsp_addr.sin_port) {
-        rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
+    if (config.rtsp_addr.sin_port) {
+        rtsp_server_fd = socket_open_listen(&config.rtsp_addr);
         if (rtsp_server_fd < 0) {
             av_free(poll_table);
             closesocket(server_fd);
@@ -676,7 +566,7 @@ static int http_server(void)
 
     http_log("FFserver started.\n");
 
-    start_children(first_feed);
+    start_children(config.first_feed);
 
     start_multicast();
 
@@ -759,7 +649,7 @@ static int http_server(void)
 
         if (need_to_start_children) {
             need_to_start_children = 0;
-            start_children(first_feed);
+            start_children(config.first_feed);
         }
 
         /* now handle the events */
@@ -813,7 +703,7 @@ static void http_send_too_busy_reply(int fd)
                        "<p>The server is too busy to serve your request at this time.</p>\r\n"
                        "<p>The number of current connections is %u, and this exceeds the limit of %u.</p>\r\n"
                        "</body></html>\r\n",
-                       nb_connections, nb_max_connections);
+                       nb_connections, config.nb_max_connections);
     av_assert0(len < sizeof(buffer));
     if (send(fd, buffer, len, 0) < len)
         av_log(NULL, AV_LOG_WARNING, "Could not send too-busy reply, send() failed\n");
@@ -837,7 +727,7 @@ static void new_connection(int server_fd, int is_rtsp)
     if (ff_socket_nonblock(fd, 1) < 0)
         av_log(NULL, AV_LOG_WARNING, "ff_socket_nonblock failed\n");
 
-    if (nb_connections >= nb_max_connections) {
+    if (nb_connections >= config.nb_max_connections) {
         http_send_too_busy_reply(fd);
         goto fail;
     }
@@ -1183,7 +1073,7 @@ static int extract_rates(char *rates, int ratelen, const char *request)
     return 0;
 }
 
-static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
+static int find_stream_in_feed(FFServerStream *feed, AVCodecContext *codec, int bit_rate)
 {
     int i;
     int best_bitrate = 100000000;
@@ -1223,7 +1113,7 @@ static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_ra
 static int modify_current_stream(HTTPContext *c, char *rates)
 {
     int i;
-    FFStream *req = c->stream;
+    FFServerStream *req = c->stream;
     int action_required = 0;
 
     /* Not much we can do for a feed */
@@ -1276,111 +1166,12 @@ static void get_word(char *buf, int buf_size, const char **pp)
     *pp = p;
 }
 
-static void get_arg(char *buf, int buf_size, const char **pp)
-{
-    const char *p;
-    char *q;
-    int quote;
-
-    p = *pp;
-    while (av_isspace(*p)) p++;
-    q = buf;
-    quote = 0;
-    if (*p == '\"' || *p == '\'')
-        quote = *p++;
-    for(;;) {
-        if (quote) {
-            if (*p == quote)
-                break;
-        } else {
-            if (av_isspace(*p))
-                break;
-        }
-        if (*p == '\0')
-            break;
-        if ((q - buf) < buf_size - 1)
-            *q++ = *p;
-        p++;
-    }
-    *q = '\0';
-    if (quote && *p == quote)
-        p++;
-    *pp = p;
-}
-
-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);
-    if (av_strcasecmp(arg, "allow") == 0)
-        acl.action = IP_ALLOW;
-    else if (av_strcasecmp(arg, "deny") == 0)
-        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;
-        } else
-            av_free(nacl);
-    }
-}
-
-
-static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
+static FFServerIPAddressACL* parse_dynamic_acl(FFServerStream *stream, HTTPContext *c)
 {
     FILE* f;
     char line[1024];
     char  cmd[1024];
-    IPAddressACL *acl = NULL;
+    FFServerIPAddressACL *acl = NULL;
     int line_num = 0;
     const char *p;
 
@@ -1390,7 +1181,7 @@ static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
         return NULL;
     }
 
-    acl = av_mallocz(sizeof(IPAddressACL));
+    acl = av_mallocz(sizeof(FFServerIPAddressACL));
 
     /* Build ACL */
     for(;;) {
@@ -1402,19 +1193,19 @@ static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
             p++;
         if (*p == '\0' || *p == '#')
             continue;
-        get_arg(cmd, sizeof(cmd), &p);
+        ffserver_get_arg(cmd, sizeof(cmd), &p);
 
         if (!av_strcasecmp(cmd, "ACL"))
-            parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
+            ffserver_parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
     }
     fclose(f);
     return acl;
 }
 
 
-static void free_acl_list(IPAddressACL *in_acl)
+static void free_acl_list(FFServerIPAddressACL *in_acl)
 {
-    IPAddressACL *pacl,*pacl2;
+    FFServerIPAddressACL *pacl, *pacl2;
 
     pacl = in_acl;
     while(pacl) {
@@ -1424,10 +1215,10 @@ static void free_acl_list(IPAddressACL *in_acl)
     }
 }
 
-static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
+static int validate_acl_list(FFServerIPAddressACL *in_acl, HTTPContext *c)
 {
-    enum IPAddressAction last_action = IP_DENY;
-    IPAddressACL *acl;
+    enum FFServerIPAddressAction last_action = IP_DENY;
+    FFServerIPAddressACL *acl;
     struct in_addr *src = &c->from_addr.sin_addr;
     unsigned long src_addr = src->s_addr;
 
@@ -1441,11 +1232,10 @@ static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
     return (last_action == IP_DENY) ? 1 : 0;
 }
 
-static int validate_acl(FFStream *stream, HTTPContext *c)
+static int validate_acl(FFServerStream *stream, HTTPContext *c)
 {
     int ret = 0;
-    IPAddressACL *acl;
-
+    FFServerIPAddressACL *acl;
 
     /* if stream->acl is null validate_acl_list will return 1 */
     ret = validate_acl_list(stream->acl, c);
@@ -1468,14 +1258,14 @@ static void compute_real_filename(char *filename, int max_size)
     char file1[1024];
     char file2[1024];
     char *p;
-    FFStream *stream;
+    FFServerStream *stream;
 
     /* compute filename by matching without the file extensions */
     av_strlcpy(file1, filename, sizeof(file1));
     p = strrchr(file1, '.');
     if (p)
         *p = '\0';
-    for(stream = first_stream; stream; stream = stream->next) {
+    for(stream = config.first_stream; stream; stream = stream->next) {
         av_strlcpy(file2, stream->filename, sizeof(file2));
         p = strrchr(file2, '.');
         if (p)
@@ -1508,7 +1298,7 @@ static int http_parse_request(HTTPContext *c)
     char protocol[32];
     char msg[1024];
     const char *mime_type;
-    FFStream *stream;
+    FFServerStream *stream;
     int i;
     char ratebuf[32];
     const char *useragent = 0;
@@ -1533,7 +1323,7 @@ static int http_parse_request(HTTPContext *c)
 
     av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
 
-    if (ffserver_debug)
+    if (config.debug)
         http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
 
     /* find the filename and the optional info string in the request */
@@ -1583,7 +1373,7 @@ static int http_parse_request(HTTPContext *c)
     if (!strlen(filename))
         av_strlcpy(filename, "index.html", sizeof(filename) - 1);
 
-    stream = first_stream;
+    stream = config.first_stream;
     while (stream) {
         if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
             break;
@@ -1638,7 +1428,7 @@ static int http_parse_request(HTTPContext *c)
         goto send_error;
     }
 
-    if (c->post == 0 && max_bandwidth < current_bandwidth) {
+    if (c->post == 0 && config.max_bandwidth < current_bandwidth) {
         c->http_error = 503;
         q = c->buffer;
         snprintf(q, c->buffer_size,
@@ -1649,7 +1439,7 @@ static int http_parse_request(HTTPContext *c)
                       "<p>The server is too busy to serve your request at this time.</p>\r\n"
                       "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
                       "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
-                      "</body></html>\r\n", current_bandwidth, max_bandwidth);
+                      "</body></html>\r\n", current_bandwidth, config.max_bandwidth);
         q += strlen(q);
         /* prepare output buffer */
         c->buffer_ptr = c->buffer;
@@ -1734,7 +1524,7 @@ static int http_parse_request(HTTPContext *c)
                                           /* 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);
+                                          "rtsp://%s:%d/%s\r\n", hostname, ntohs(config.rtsp_addr.sin_port), filename);
                             q += strlen(q);
                         }
                         break;
@@ -1935,7 +1725,7 @@ static void fmt_bytecount(AVIOContext *pb, int64_t count)
 static void compute_status(HTTPContext *c)
 {
     HTTPContext *c1;
-    FFStream *stream;
+    FFServerStream *stream;
     char *p;
     time_t ti;
     int i, len;
@@ -1962,7 +1752,7 @@ static void compute_status(HTTPContext *c)
     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");
-    stream = first_stream;
+    stream = config.first_stream;
     while (stream) {
         char sfilename[1024];
         char *eosf;
@@ -2051,7 +1841,7 @@ static void compute_status(HTTPContext *c)
     }
     avio_printf(pb, "</table>\n");
 
-    stream = first_stream;
+    stream = config.first_stream;
     while (stream) {
         if (stream->feed == stream) {
             avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
@@ -2121,10 +1911,10 @@ static void compute_status(HTTPContext *c)
     avio_printf(pb, "<h2>Connection Status</h2>\n");
 
     avio_printf(pb, "Number of connections: %d / %d<br>\n",
-                 nb_connections, nb_max_connections);
+                 nb_connections, config.nb_max_connections);
 
     avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
-                 current_bandwidth, max_bandwidth);
+                 current_bandwidth, config.max_bandwidth);
 
     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");
@@ -2291,7 +2081,7 @@ static int http_prepare_data(HTTPContext *c)
         for(i=0;i<c->stream->nb_streams;i++) {
             AVStream *src;
             c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
-            /* if file or feed, then just take streams from FFStream struct */
+            /* if file or feed, then just take streams from FFServerStream struct */
             if (!c->stream->feed ||
                 c->stream->feed == c->stream)
                 src = c->stream->streams[i];
@@ -2744,7 +2534,7 @@ static int http_receive_data(HTTPContext *c)
     }
 
     if (c->buffer_ptr >= c->buffer_end) {
-        FFStream *feed = c->stream;
+        FFServerStream *feed = c->stream;
         /* a packet has been received : write it in the store, except
            if header */
         if (c->data_count > FFM_PACKET_SIZE) {
@@ -2957,7 +2747,7 @@ static int rtsp_parse_request(HTTPContext *c)
     return 0;
 }
 
-static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
+static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer,
                                    struct in_addr my_ip)
 {
     AVFormatContext *avc;
@@ -3018,7 +2808,7 @@ static void rtsp_cmd_options(HTTPContext *c, const char *url)
 
 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
 {
-    FFStream *stream;
+    FFServerStream *stream;
     char path1[1024];
     const char *path;
     uint8_t *content;
@@ -3032,7 +2822,7 @@ static void rtsp_cmd_describe(HTTPContext *c, const char *url)
     if (*path == '/')
         path++;
 
-    for(stream = first_stream; stream; stream = stream->next) {
+    for(stream = config.first_stream; stream; stream = stream->next) {
         if (!stream->is_feed &&
             stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
             !strcmp(path, stream->filename)) {
@@ -3093,7 +2883,7 @@ static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTr
 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
                            RTSPMessageHeader *h)
 {
-    FFStream *stream;
+    FFServerStream *stream;
     int stream_index, rtp_port, rtcp_port;
     char buf[1024];
     char path1[1024];
@@ -3110,7 +2900,7 @@ static void rtsp_cmd_setup(HTTPContext *c, const char *url,
         path++;
 
     /* now check each stream */
-    for(stream = first_stream; stream; stream = stream->next) {
+    for(stream = config.first_stream; stream; stream = stream->next) {
         if (!stream->is_feed &&
             stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
             /* accept aggregate filenames only if single stream */
@@ -3330,7 +3120,7 @@ static void rtsp_cmd_interrupt(HTTPContext *c, const char *url, RTSPMessageHeade
 /* RTP handling */
 
 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
-                                       FFStream *stream, const char *session_id,
+                                       FFServerStream *stream, const char *session_id,
                                        enum RTSPLowerTransport rtp_protocol)
 {
     HTTPContext *c = NULL;
@@ -3338,7 +3128,7 @@ static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
 
     /* XXX: should output a warning page when coming
        close to the connection limit */
-    if (nb_connections >= nb_max_connections)
+    if (nb_connections >= config.nb_max_connections)
         goto fail;
 
     /* add a new connection */
@@ -3494,7 +3284,7 @@ static int rtp_new_av_stream(HTTPContext *c,
 /********************************************************************/
 /* ffserver initialization */
 
-static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
+static AVStream *add_av_stream1(FFServerStream *stream, AVCodecContext *codec, int copy)
 {
     AVStream *fst;
 
@@ -3527,7 +3317,7 @@ static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int cop
 }
 
 /* return the stream number in the feed */
-static int add_av_stream(FFStream *feed, AVStream *st)
+static int add_av_stream(FFServerStream *feed, AVStream *st)
 {
     AVStream *fst;
     AVCodecContext *av, *av1;
@@ -3567,10 +3357,10 @@ static int add_av_stream(FFStream *feed, AVStream *st)
     return feed->nb_streams - 1;
 }
 
-static void remove_stream(FFStream *stream)
+static void remove_stream(FFServerStream *stream)
 {
-    FFStream **ps;
-    ps = &first_stream;
+    FFServerStream **ps;
+    ps = &config.first_stream;
     while (*ps) {
         if (*ps == stream)
             *ps = (*ps)->next;
@@ -3633,11 +3423,11 @@ static void extract_mpeg4_header(AVFormatContext *infile)
 /* compute the needed AVStream for each file */
 static void build_file_streams(void)
 {
-    FFStream *stream, *stream_next;
+    FFServerStream *stream, *stream_next;
     int i, ret;
 
     /* gather all streams */
-    for(stream = first_stream; stream; stream = stream_next) {
+    for(stream = config.first_stream; stream; stream = stream_next) {
         AVFormatContext *infile = NULL;
         stream_next = stream->next;
         if (stream->stream_type == STREAM_TYPE_LIVE &&
@@ -3685,11 +3475,11 @@ static void build_file_streams(void)
 /* compute the needed AVStream for each feed */
 static void build_feed_streams(void)
 {
-    FFStream *stream, *feed;
+    FFServerStream *stream, *feed;
     int i;
 
     /* gather all streams */
-    for(stream = first_stream; stream; stream = stream->next) {
+    for(stream = config.first_stream; stream; stream = stream->next) {
         feed = stream->feed;
         if (feed) {
             if (stream->is_feed) {
@@ -3704,7 +3494,7 @@ static void build_feed_streams(void)
     }
 
     /* create feed files if needed */
-    for(feed = first_feed; feed; feed = feed->next_feed) {
+    for(feed = config.first_feed; feed; feed = feed->next_feed) {
         int fd;
 
         if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
@@ -3834,9 +3624,9 @@ static void compute_bandwidth(void)
 {
     unsigned bandwidth;
     int i;
-    FFStream *stream;
+    FFServerStream *stream;
 
-    for(stream = first_stream; stream; stream = stream->next) {
+    for(stream = config.first_stream; stream; stream = stream->next) {
         bandwidth = 0;
         for(i=0;i<stream->nb_streams;i++) {
             AVStream *st = stream->streams[i];
@@ -3853,834 +3643,15 @@ static void compute_bandwidth(void)
     }
 }
 
-/* add a codec and set the default parameters */
-static void add_codec(FFStream *stream, AVCodecContext *av)
-{
-    AVStream *st;
-
-    if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
-        return;
-
-    /* compute default parameters */
-    switch(av->codec_type) {
-    case AVMEDIA_TYPE_AUDIO:
-        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;
-    case AVMEDIA_TYPE_VIDEO:
-        if (av->bit_rate == 0)
-            av->bit_rate = 64000;
-        if (av->time_base.num == 0){
-            av->time_base.den = 5;
-            av->time_base.num = 1;
-        }
-        if (av->width == 0 || av->height == 0) {
-            av->width = 160;
-            av->height = 128;
-        }
-        /* Bitrate tolerance is less for streaming */
-        if (av->bit_rate_tolerance == 0)
-            av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
-                      (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
-        if (av->qmin == 0)
-            av->qmin = 3;
-        if (av->qmax == 0)
-            av->qmax = 31;
-        if (av->max_qdiff == 0)
-            av->max_qdiff = 3;
-        av->qcompress = 0.5;
-        av->qblur = 0.5;
-
-        if (!av->nsse_weight)
-            av->nsse_weight = 8;
-
-        av->frame_skip_cmp = FF_CMP_DCTMAX;
-        if (!av->me_method)
-            av->me_method = ME_EPZS;
-        av->rc_buffer_aggressivity = 1.0;
-
-        if (!av->rc_eq)
-            av->rc_eq = av_strdup("tex^qComp");
-        if (!av->i_quant_factor)
-            av->i_quant_factor = -0.8;
-        if (!av->b_quant_factor)
-            av->b_quant_factor = 1.25;
-        if (!av->b_quant_offset)
-            av->b_quant_offset = 1.25;
-        if (!av->rc_max_rate)
-            av->rc_max_rate = av->bit_rate * 2;
-
-        if (av->rc_max_rate && !av->rc_buffer_size) {
-            av->rc_buffer_size = av->rc_max_rate;
-        }
-
-
-        break;
-    default:
-        abort();
-    }
-
-    st = av_mallocz(sizeof(AVStream));
-    if (!st)
-        return;
-    st->codec = avcodec_alloc_context3(NULL);
-    stream->streams[stream->nb_streams++] = st;
-    memcpy(st->codec, av, sizeof(AVCodecContext));
-}
-
-static enum AVCodecID opt_codec(const char *name, enum AVMediaType type)
-{
-    AVCodec *codec = avcodec_find_encoder_by_name(name);
-
-    if (!codec || codec->type != type)
-        return AV_CODEC_ID_NONE;
-    return codec->id;
-}
-
-static int ffserver_opt_default(const char *opt, const char *arg,
-                       AVCodecContext *avctx, int type)
-{
-    int ret = 0;
-    const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
-    if(o)
-        ret = av_opt_set(avctx, opt, arg, 0);
-    return ret;
-}
-
-static int ffserver_opt_preset(const char *arg,
-                       AVCodecContext *avctx, int type,
-                       enum AVCodecID *audio_id, enum AVCodecID *video_id)
-{
-    FILE *f=NULL;
-    char filename[1000], tmp[1000], tmp2[1000], line[1000];
-    int ret = 0;
-    AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
-
-    if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
-                              codec ? codec->name : NULL))) {
-        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")){
-            *audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO);
-        }else if(!strcmp(tmp, "vcodec")){
-            *video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO);
-        }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;
-}
-
-static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename, const char *mime_type)
-{
-    AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
-
-    if (fmt) {
-        AVOutputFormat *stream_fmt;
-        char stream_format_name[64];
-
-        snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
-        stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
-
-        if (stream_fmt)
-            fmt = stream_fmt;
-    }
-
-    return fmt;
-}
-
-static void report_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, ...)
-{
-    va_list vl;
-    va_start(vl, fmt);
-    av_log(NULL, log_level, "%s:%d: ", filename, line_num);
-    av_vlog(NULL, log_level, fmt, vl);
-    va_end(vl);
-
-    (*errors)++;
-}
-
-static int parse_ffconfig(const char *filename)
-{
-    FILE *f;
-    char line[1024];
-    char cmd[64];
-    char arg[1024], arg2[1024];
-    const char *p;
-    int val, errors, warnings, line_num;
-    FFStream **last_stream, *stream, *redirect;
-    FFStream **last_feed, *feed, *s;
-    AVCodecContext audio_enc, video_enc;
-    enum AVCodecID audio_id, video_id;
-    int ret = 0;
-
-    f = fopen(filename, "r");
-    if (!f) {
-        ret = AVERROR(errno);
-        av_log(NULL, AV_LOG_ERROR, "Could not open the configuration file '%s'\n", filename);
-        return ret;
-    }
-
-    errors = warnings = 0;
-    line_num = 0;
-    first_stream = NULL;
-    last_stream = &first_stream;
-    first_feed = NULL;
-    last_feed = &first_feed;
-    stream = NULL;
-    feed = NULL;
-    redirect = NULL;
-    audio_id = AV_CODEC_ID_NONE;
-    video_id = AV_CODEC_ID_NONE;
-#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__)
-
-    for(;;) {
-        if (fgets(line, sizeof(line), f) == NULL)
-            break;
-        line_num++;
-        p = line;
-        while (av_isspace(*p))
-            p++;
-        if (*p == '\0' || *p == '#')
-            continue;
-
-        get_arg(cmd, sizeof(cmd), &p);
-
-        if (!av_strcasecmp(cmd, "Port") || !av_strcasecmp(cmd, "HTTPPort")) {
-            if (!av_strcasecmp(cmd, "Port"))
-                WARNING("Port option is deprecated, use HTTPPort instead\n");
-            get_arg(arg, sizeof(arg), &p);
-            val = atoi(arg);
-            if (val < 1 || val > 65536) {
-                ERROR("Invalid port: %s\n", arg);
-            }
-            if (val < 1024)
-                WARNING("Trying to use IETF assigned system port: %d\n", val);
-            my_http_addr.sin_port = htons(val);
-        } else if (!av_strcasecmp(cmd, "HTTPBindAddress") || !av_strcasecmp(cmd, "BindAddress")) {
-            if (!av_strcasecmp(cmd, "BindAddress"))
-                WARNING("BindAddress option is deprecated, use HTTPBindAddress instead\n");
-            get_arg(arg, sizeof(arg), &p);
-            if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
-                ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
-            }
-        } else if (!av_strcasecmp(cmd, "NoDaemon")) {
-            WARNING("NoDaemon option has no effect, you should remove it\n");
-        } else if (!av_strcasecmp(cmd, "RTSPPort")) {
-            get_arg(arg, sizeof(arg), &p);
-            val = atoi(arg);
-            if (val < 1 || val > 65536) {
-                ERROR("%s:%d: Invalid port: %s\n", arg);
-            }
-            my_rtsp_addr.sin_port = htons(atoi(arg));
-        } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
-                ERROR("Invalid host/IP address: %s\n", arg);
-            }
-        } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
-            get_arg(arg, sizeof(arg), &p);
-            val = atoi(arg);
-            if (val < 1 || val > 65536) {
-                ERROR("Invalid MaxHTTPConnections: %s\n", arg);
-            }
-            nb_max_http_connections = val;
-        } else if (!av_strcasecmp(cmd, "MaxClients")) {
-            get_arg(arg, sizeof(arg), &p);
-            val = atoi(arg);
-            if (val < 1 || val > nb_max_http_connections) {
-                ERROR("Invalid MaxClients: %s\n", arg);
-            } else {
-                nb_max_connections = val;
-            }
-        } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
-            int64_t llval;
-            get_arg(arg, sizeof(arg), &p);
-            llval = strtoll(arg, NULL, 10);
-            if (llval < 10 || llval > 10000000) {
-                ERROR("Invalid MaxBandwidth: %s\n", arg);
-            } else
-                max_bandwidth = llval;
-        } else if (!av_strcasecmp(cmd, "CustomLog")) {
-            if (!ffserver_debug)
-                get_arg(logfilename, sizeof(logfilename), &p);
-        } else if (!av_strcasecmp(cmd, "<Feed")) {
-            /*********************************************/
-            /* Feed related options */
-            char *q;
-            if (stream || feed) {
-                ERROR("Already in a tag\n");
-            } else {
-                feed = av_mallocz(sizeof(FFStream));
-                if (!feed) {
-                    ret = AVERROR(ENOMEM);
-                    goto end;
-                }
-                get_arg(feed->filename, sizeof(feed->filename), &p);
-                q = strrchr(feed->filename, '>');
-                if (*q)
-                    *q = '\0';
-
-                for (s = first_feed; s; s = s->next) {
-                    if (!strcmp(feed->filename, s->filename)) {
-                        ERROR("Feed '%s' already registered\n", s->filename);
-                    }
-                }
-
-                feed->fmt = av_guess_format("ffm", NULL, NULL);
-                /* default feed file */
-                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 :-) */
-
-                /* add in stream list */
-                *last_stream = feed;
-                last_stream = &feed->next;
-                /* add in feed list */
-                *last_feed = feed;
-                last_feed = &feed->next_feed;
-            }
-        } else if (!av_strcasecmp(cmd, "Launch")) {
-            if (feed) {
-                int i;
-
-                feed->child_argv = av_mallocz(64 * sizeof(char *));
-                if (!feed->child_argv) {
-                    ret = AVERROR(ENOMEM);
-                    goto end;
-                }
-                for (i = 0; i < 62; i++) {
-                    get_arg(arg, sizeof(arg), &p);
-                    if (!arg[0])
-                        break;
-
-                    feed->child_argv[i] = av_strdup(arg);
-                    if (!feed->child_argv[i]) {
-                        ret = AVERROR(ENOMEM);
-                        goto end;
-                    }
-                }
-
-                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;
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) {
-            if (feed) {
-                get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
-                feed->readonly = !av_strcasecmp(cmd, "ReadOnlyFile");
-            } else if (stream) {
-                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
-            }
-        } else if (!av_strcasecmp(cmd, "Truncate")) {
-            if (feed) {
-                get_arg(arg, sizeof(arg), &p);
-                /* assume Truncate is true in case no argument is specified */
-                if (!arg[0]) {
-                    feed->truncate = 1;
-                } else {
-                    WARNING("Truncate N syntax in configuration file is deprecated, "
-                            "use Truncate alone with no arguments\n");
-                    feed->truncate = strtod(arg, NULL);
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
-            if (feed) {
-                char *p1;
-                double fsize;
-
-                get_arg(arg, sizeof(arg), &p);
-                p1 = arg;
-                fsize = strtod(p1, &p1);
-                switch(av_toupper(*p1)) {
-                case 'K':
-                    fsize *= 1024;
-                    break;
-                case 'M':
-                    fsize *= 1024 * 1024;
-                    break;
-                case 'G':
-                    fsize *= 1024 * 1024 * 1024;
-                    break;
-                }
-                feed->feed_max_size = (int64_t)fsize;
-                if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
-                    ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "</Feed>")) {
-            if (!feed) {
-                ERROR("No corresponding <Feed> for </Feed>\n");
-            }
-            feed = NULL;
-        } else if (!av_strcasecmp(cmd, "<Stream")) {
-            /*********************************************/
-            /* Stream related options */
-            char *q;
-            if (stream || feed) {
-                ERROR("Already in a tag\n");
-            } else {
-                FFStream *s;
-                stream = av_mallocz(sizeof(FFStream));
-                if (!stream) {
-                    ret = AVERROR(ENOMEM);
-                    goto end;
-                }
-                get_arg(stream->filename, sizeof(stream->filename), &p);
-                q = strrchr(stream->filename, '>');
-                if (q)
-                    *q = '\0';
-
-                for (s = first_stream; s; s = s->next) {
-                    if (!strcmp(stream->filename, s->filename)) {
-                        ERROR("Stream '%s' already registered\n", s->filename);
-                    }
-                }
-
-                stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
-                avcodec_get_context_defaults3(&video_enc, NULL);
-                avcodec_get_context_defaults3(&audio_enc, NULL);
-
-                audio_id = AV_CODEC_ID_NONE;
-                video_id = AV_CODEC_ID_NONE;
-                if (stream->fmt) {
-                    audio_id = stream->fmt->audio_codec;
-                    video_id = stream->fmt->video_codec;
-                }
-
-                *last_stream = stream;
-                last_stream = &stream->next;
-            }
-        } else if (!av_strcasecmp(cmd, "Feed")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                FFStream *sfeed;
-
-                sfeed = first_feed;
-                while (sfeed) {
-                    if (!strcmp(sfeed->filename, arg))
-                        break;
-                    sfeed = sfeed->next_feed;
-                }
-                if (!sfeed)
-                    ERROR("Feed with name '%s' for stream '%s' is not defined\n", arg, stream->filename);
-                else
-                    stream->feed = sfeed;
-            }
-        } else if (!av_strcasecmp(cmd, "Format")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                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 MJPEG */
-                    if (!strcmp(arg, "jpeg"))
-                        strcpy(arg, "mjpeg");
-                    stream->fmt = ffserver_guess_format(arg, NULL, NULL);
-                    if (!stream->fmt) {
-                        ERROR("Unknown Format: %s\n", arg);
-                    }
-                }
-                if (stream->fmt) {
-                    audio_id = stream->fmt->audio_codec;
-                    video_id = stream->fmt->video_codec;
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "InputFormat")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                stream->ifmt = av_find_input_format(arg);
-                if (!stream->ifmt) {
-                    ERROR("Unknown input format: %s\n", arg);
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "FaviconURL")) {
-            if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
-                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
-            } else {
-                ERROR("FaviconURL only permitted for status streams\n");
-            }
-        } 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;
-                WARNING("'%s' option in configuration file is deprecated, "
-                        "use 'Metadata %s VALUE' instead\n", cmd, key);
-                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));
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "Preroll")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                stream->prebuffer = atof(arg) * 1000;
-        } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
-            if (stream)
-                stream->send_on_key = 1;
-        } else if (!av_strcasecmp(cmd, "AudioCodec")) {
-            get_arg(arg, sizeof(arg), &p);
-            audio_id = opt_codec(arg, AVMEDIA_TYPE_AUDIO);
-            if (audio_id == AV_CODEC_ID_NONE) {
-                ERROR("Unknown AudioCodec: %s\n", arg);
-            }
-        } else if (!av_strcasecmp(cmd, "VideoCodec")) {
-            get_arg(arg, sizeof(arg), &p);
-            video_id = opt_codec(arg, AVMEDIA_TYPE_VIDEO);
-            if (video_id == AV_CODEC_ID_NONE) {
-                ERROR("Unknown VideoCodec: %s\n", arg);
-            }
-        } else if (!av_strcasecmp(cmd, "MaxTime")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                stream->max_time = atof(arg) * 1000;
-        } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                audio_enc.bit_rate = lrintf(atof(arg) * 1000);
-        } else if (!av_strcasecmp(cmd, "AudioChannels")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                audio_enc.channels = atoi(arg);
-        } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                audio_enc.sample_rate = atoi(arg);
-        } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
-            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 {
-                    ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "Debug")) {
-            if (stream) {
-                get_arg(arg, sizeof(arg), &p);
-                video_enc.debug = strtol(arg,0,0);
-            }
-        } else if (!av_strcasecmp(cmd, "Strict")) {
-            if (stream) {
-                get_arg(arg, sizeof(arg), &p);
-                video_enc.strict_std_compliance = atoi(arg);
-            }
-        } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
-            if (stream) {
-                get_arg(arg, sizeof(arg), &p);
-                video_enc.rc_buffer_size = atoi(arg) * 8*1024;
-            }
-        } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
-            if (stream) {
-                get_arg(arg, sizeof(arg), &p);
-                video_enc.bit_rate_tolerance = atoi(arg) * 1000;
-            }
-        } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                video_enc.bit_rate = atoi(arg) * 1000;
-            }
-        } else if (!av_strcasecmp(cmd, "VideoSize")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                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");
-                    }
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                AVRational frame_rate;
-                if (av_parse_video_rate(&frame_rate, arg) < 0) {
-                    ERROR("Incorrect frame rate: %s\n", arg);
-                } else {
-                    video_enc.time_base.num = frame_rate.den;
-                    video_enc.time_base.den = frame_rate.num;
-                }
-            }
-        } 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);
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                video_enc.gop_size = atoi(arg);
-        } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
-            if (stream)
-                video_enc.gop_size = 1;
-        } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
-            if (stream)
-                video_enc.mb_decision = FF_MB_DECISION_BITS;
-        } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
-            if (stream) {
-                video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
-                video_enc.flags |= CODEC_FLAG_4MV;
-            }
-        } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
-                   !av_strcasecmp(cmd, "AVOptionAudio")) {
-            AVCodecContext *avctx;
-            int type;
-            get_arg(arg, sizeof(arg), &p);
-            get_arg(arg2, sizeof(arg2), &p);
-            if (!av_strcasecmp(cmd, "AVOptionVideo")) {
-                avctx = &video_enc;
-                type = AV_OPT_FLAG_VIDEO_PARAM;
-            } else {
-                avctx = &audio_enc;
-                type = AV_OPT_FLAG_AUDIO_PARAM;
-            }
-            if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
-                ERROR("Error setting %s option to %s %s\n", cmd, arg, arg2);
-            }
-        } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
-                   !av_strcasecmp(cmd, "AVPresetAudio")) {
-            AVCodecContext *avctx;
-            int type;
-            get_arg(arg, sizeof(arg), &p);
-            if (!av_strcasecmp(cmd, "AVPresetVideo")) {
-                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);
-            }
-        } else if (!av_strcasecmp(cmd, "VideoTag")) {
-            get_arg(arg, sizeof(arg), &p);
-            if ((strlen(arg) == 4) && stream)
-                video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
-        } else if (!av_strcasecmp(cmd, "BitExact")) {
-            if (stream)
-                video_enc.flags |= CODEC_FLAG_BITEXACT;
-        } else if (!av_strcasecmp(cmd, "DctFastint")) {
-            if (stream)
-                video_enc.dct_algo  = FF_DCT_FASTINT;
-        } else if (!av_strcasecmp(cmd, "IdctSimple")) {
-            if (stream)
-                video_enc.idct_algo = FF_IDCT_SIMPLE;
-        } else if (!av_strcasecmp(cmd, "Qscale")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                video_enc.flags |= CODEC_FLAG_QSCALE;
-                video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
-            }
-        } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                video_enc.max_qdiff = atoi(arg);
-                if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
-                    ERROR("VideoQDiff out of range\n");
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "VideoQMax")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                video_enc.qmax = atoi(arg);
-                if (video_enc.qmax < 1 || video_enc.qmax > 31) {
-                    ERROR("VideoQMax out of range\n");
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "VideoQMin")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                video_enc.qmin = atoi(arg);
-                if (video_enc.qmin < 1 || video_enc.qmin > 31) {
-                    ERROR("VideoQMin out of range\n");
-                }
-            }
-        } else if (!av_strcasecmp(cmd, "LumiMask")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                video_enc.lumi_masking = atof(arg);
-        } else if (!av_strcasecmp(cmd, "DarkMask")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                video_enc.dark_masking = atof(arg);
-        } else if (!av_strcasecmp(cmd, "NoVideo")) {
-            video_id = AV_CODEC_ID_NONE;
-        } else if (!av_strcasecmp(cmd, "NoAudio")) {
-            audio_id = AV_CODEC_ID_NONE;
-        } else if (!av_strcasecmp(cmd, "ACL")) {
-            parse_acl_row(stream, feed, NULL, p, filename, line_num);
-        } else if (!av_strcasecmp(cmd, "DynamicACL")) {
-            if (stream) {
-                get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
-            }
-        } else if (!av_strcasecmp(cmd, "RTSPOption")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                av_freep(&stream->rtsp_option);
-                stream->rtsp_option = av_strdup(arg);
-            }
-        } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream) {
-                if (resolve_host(&stream->multicast_ip, arg) != 0) {
-                    ERROR("Invalid host/IP address: %s\n", arg);
-                }
-                stream->is_multicast = 1;
-                stream->loop = 1; /* default is looping */
-            }
-        } else if (!av_strcasecmp(cmd, "MulticastPort")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                stream->multicast_port = atoi(arg);
-        } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
-            get_arg(arg, sizeof(arg), &p);
-            if (stream)
-                stream->multicast_ttl = atoi(arg);
-        } else if (!av_strcasecmp(cmd, "NoLoop")) {
-            if (stream)
-                stream->loop = 0;
-        } else if (!av_strcasecmp(cmd, "</Stream>")) {
-            if (!stream) {
-                ERROR("No corresponding <Stream> for </Stream>\n");
-            } else {
-                if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
-                    if (audio_id != AV_CODEC_ID_NONE) {
-                        audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
-                        audio_enc.codec_id = audio_id;
-                        add_codec(stream, &audio_enc);
-                    }
-                    if (video_id != AV_CODEC_ID_NONE) {
-                        video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
-                        video_enc.codec_id = video_id;
-                        add_codec(stream, &video_enc);
-                    }
-                }
-                stream = NULL;
-            }
-        } else if (!av_strcasecmp(cmd, "<Redirect")) {
-            /*********************************************/
-            char *q;
-            if (stream || feed || redirect) {
-                ERROR("Already in a tag\n");
-            } else {
-                redirect = av_mallocz(sizeof(FFStream));
-                if (!redirect) {
-                    ret = AVERROR(ENOMEM);
-                    goto end;
-                }
-                *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;
-            }
-        } else if (!av_strcasecmp(cmd, "URL")) {
-            if (redirect)
-                get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
-        } else if (!av_strcasecmp(cmd, "</Redirect>")) {
-            if (!redirect) {
-                ERROR("No corresponding <Redirect> for </Redirect>\n");
-            } else {
-                if (!redirect->feed_filename[0]) {
-                    ERROR("No URL found for <Redirect>\n");
-                }
-                redirect = NULL;
-            }
-        } else if (!av_strcasecmp(cmd, "LoadModule")) {
-            ERROR("Loadable modules no longer supported\n");
-        } else {
-            ERROR("Incorrect keyword: '%s'\n", cmd);
-        }
-    }
-#undef ERROR
-
-end:
-    fclose(f);
-    if (ret < 0)
-        return ret;
-    if (errors)
-        return AVERROR(EINVAL);
-    else
-        return 0;
-}
-
 static void handle_child_exit(int sig)
 {
     pid_t pid;
     int status;
 
     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
-        FFStream *feed;
+        FFServerStream *feed;
 
-        for (feed = first_feed; feed; feed = feed->next) {
+        for (feed = config.first_feed; feed; feed = feed->next) {
             if (feed->pid == pid) {
                 int uptime = time(0) - feed->pid_start;
 
@@ -4699,8 +3670,8 @@ static void handle_child_exit(int sig)
 
 static void opt_debug(void)
 {
-    ffserver_debug = 1;
-    logfilename[0] = '-';
+    config.debug = 1;
+    snprintf(config.logfilename, sizeof(config.logfilename), "-");
 }
 
 void show_help_default(const char *opt, const char *arg)
@@ -4715,7 +3686,7 @@ static const OptionDef options[] = {
 #include "cmdutils_common_opts.h"
     { "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" },
+    { "f", HAS_ARG | OPT_STRING, {(void*)&config.filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
     { NULL },
 };
 
@@ -4724,7 +3695,7 @@ int main(int argc, char **argv)
     struct sigaction sigact = { { 0 } };
     int ret = 0;
 
-    config_filename = av_strdup("/etc/ffserver.conf");
+    config.filename = av_strdup("/etc/ffserver.conf");
 
     parse_loglevel(argc, argv, options);
     av_register_all();
@@ -4744,19 +3715,19 @@ int main(int argc, char **argv)
     sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
     sigaction(SIGCHLD, &sigact, 0);
 
-    if ((ret = parse_ffconfig(config_filename)) < 0) {
+    if ((ret = ffserver_parse_ffconfig(config.filename, &config)) < 0) {
         fprintf(stderr, "Error reading configuration file '%s': %s\n",
-                config_filename, av_err2str(ret));
+                config.filename, av_err2str(ret));
         exit(1);
     }
-    av_freep(&config_filename);
+    av_freep(&config.filename);
 
     /* open log file if needed */
-    if (logfilename[0] != '\0') {
-        if (!strcmp(logfilename, "-"))
+    if (config.logfilename[0] != '\0') {
+        if (!strcmp(config.logfilename, "-"))
             logfile = stdout;
         else
-            logfile = fopen(logfilename, "a");
+            logfile = fopen(config.logfilename, "a");
         av_log_set_callback(http_av_log);
     }
 
diff --git a/ffserver_config.c b/ffserver_config.c
new file mode 100644 (file)
index 0000000..927b6fe
--- /dev/null
@@ -0,0 +1,986 @@
+/*
+ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/avstring.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/avassert.h"
+
+// FIXME those are internal headers, ffserver _really_ shouldn't use them
+#include "libavformat/ffm.h"
+
+#include "cmdutils.h"
+#include "ffserver_config.h"
+
+/* 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;
+        struct addrinfo hints = { 0 };
+        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;
+}
+
+void ffserver_get_arg(char *buf, int buf_size, const char **pp)
+{
+    const char *p;
+    char *q;
+    int quote;
+
+    p = *pp;
+    while (av_isspace(*p)) p++;
+    q = buf;
+    quote = 0;
+    if (*p == '\"' || *p == '\'')
+        quote = *p++;
+    for(;;) {
+        if (quote) {
+            if (*p == quote)
+                break;
+        } else {
+            if (av_isspace(*p))
+                break;
+        }
+        if (*p == '\0')
+            break;
+        if ((q - buf) < buf_size - 1)
+            *q++ = *p;
+        p++;
+    }
+    *q = '\0';
+    if (quote && *p == quote)
+        p++;
+    *pp = p;
+}
+
+void ffserver_parse_acl_row(FFServerStream *stream, FFServerStream* feed,
+                            FFServerIPAddressACL *ext_acl,
+                            const char *p, const char *filename, int line_num)
+{
+    char arg[1024];
+    FFServerIPAddressACL acl;
+    int errors = 0;
+
+    ffserver_get_arg(arg, sizeof(arg), &p);
+    if (av_strcasecmp(arg, "allow") == 0)
+        acl.action = IP_ALLOW;
+    else if (av_strcasecmp(arg, "deny") == 0)
+        acl.action = IP_DENY;
+    else {
+        fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
+                filename, line_num, arg);
+        errors++;
+    }
+
+    ffserver_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;
+
+    ffserver_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) {
+        FFServerIPAddressACL *nacl = av_mallocz(sizeof(*nacl));
+        FFServerIPAddressACL **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;
+        } else
+            av_free(nacl);
+    }
+}
+
+/* add a codec and set the default parameters */
+static void add_codec(FFServerStream *stream, AVCodecContext *av)
+{
+    AVStream *st;
+
+    if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
+        return;
+
+    /* compute default parameters */
+    switch(av->codec_type) {
+    case AVMEDIA_TYPE_AUDIO:
+        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;
+    case AVMEDIA_TYPE_VIDEO:
+        if (av->bit_rate == 0)
+            av->bit_rate = 64000;
+        if (av->time_base.num == 0){
+            av->time_base.den = 5;
+            av->time_base.num = 1;
+        }
+        if (av->width == 0 || av->height == 0) {
+            av->width = 160;
+            av->height = 128;
+        }
+        /* Bitrate tolerance is less for streaming */
+        if (av->bit_rate_tolerance == 0)
+            av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
+                      (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
+        if (av->qmin == 0)
+            av->qmin = 3;
+        if (av->qmax == 0)
+            av->qmax = 31;
+        if (av->max_qdiff == 0)
+            av->max_qdiff = 3;
+        av->qcompress = 0.5;
+        av->qblur = 0.5;
+
+        if (!av->nsse_weight)
+            av->nsse_weight = 8;
+
+        av->frame_skip_cmp = FF_CMP_DCTMAX;
+        if (!av->me_method)
+            av->me_method = ME_EPZS;
+        av->rc_buffer_aggressivity = 1.0;
+
+        if (!av->rc_eq)
+            av->rc_eq = av_strdup("tex^qComp");
+        if (!av->i_quant_factor)
+            av->i_quant_factor = -0.8;
+        if (!av->b_quant_factor)
+            av->b_quant_factor = 1.25;
+        if (!av->b_quant_offset)
+            av->b_quant_offset = 1.25;
+        if (!av->rc_max_rate)
+            av->rc_max_rate = av->bit_rate * 2;
+
+        if (av->rc_max_rate && !av->rc_buffer_size) {
+            av->rc_buffer_size = av->rc_max_rate;
+        }
+
+
+        break;
+    default:
+        abort();
+    }
+
+    st = av_mallocz(sizeof(AVStream));
+    if (!st)
+        return;
+    st->codec = avcodec_alloc_context3(NULL);
+    stream->streams[stream->nb_streams++] = st;
+    memcpy(st->codec, av, sizeof(AVCodecContext));
+}
+
+static enum AVCodecID opt_codec(const char *name, enum AVMediaType type)
+{
+    AVCodec *codec = avcodec_find_encoder_by_name(name);
+
+    if (!codec || codec->type != type)
+        return AV_CODEC_ID_NONE;
+    return codec->id;
+}
+
+static int ffserver_opt_default(const char *opt, const char *arg,
+                       AVCodecContext *avctx, int type)
+{
+    int ret = 0;
+    const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
+    if(o)
+        ret = av_opt_set(avctx, opt, arg, 0);
+    return ret;
+}
+
+static int ffserver_opt_preset(const char *arg,
+                       AVCodecContext *avctx, int type,
+                       enum AVCodecID *audio_id, enum AVCodecID *video_id)
+{
+    FILE *f=NULL;
+    char filename[1000], tmp[1000], tmp2[1000], line[1000];
+    int ret = 0;
+    AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
+
+    if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
+                              codec ? codec->name : NULL))) {
+        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")){
+            *audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO);
+        }else if(!strcmp(tmp, "vcodec")){
+            *video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO);
+        }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;
+}
+
+static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename, const char *mime_type)
+{
+    AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
+
+    if (fmt) {
+        AVOutputFormat *stream_fmt;
+        char stream_format_name[64];
+
+        snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
+        stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
+
+        if (stream_fmt)
+            fmt = stream_fmt;
+    }
+
+    return fmt;
+}
+
+static void report_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, ...)
+{
+    va_list vl;
+    va_start(vl, fmt);
+    av_log(NULL, log_level, "%s:%d: ", filename, line_num);
+    av_vlog(NULL, log_level, fmt, vl);
+    va_end(vl);
+
+    (*errors)++;
+}
+
+int ffserver_parse_ffconfig(const char *filename, FFServerConfig *config)
+{
+    FILE *f;
+    char line[1024];
+    char cmd[64];
+    char arg[1024], arg2[1024];
+    const char *p;
+    int val, errors, warnings, line_num;
+    FFServerStream **last_stream, *stream, *redirect;
+    FFServerStream **last_feed, *feed, *s;
+    AVCodecContext audio_enc, video_enc;
+    enum AVCodecID audio_id, video_id;
+    int ret = 0;
+
+    av_assert0(config);
+
+    f = fopen(filename, "r");
+    if (!f) {
+        ret = AVERROR(errno);
+        av_log(NULL, AV_LOG_ERROR, "Could not open the configuration file '%s'\n", filename);
+        return ret;
+    }
+
+    errors = warnings = 0;
+    line_num = 0;
+    config->first_stream = NULL;
+    last_stream = &config->first_stream;
+    config->first_feed = NULL;
+    last_feed = &config->first_feed;
+    stream = NULL;
+    feed = NULL;
+    redirect = NULL;
+    audio_id = AV_CODEC_ID_NONE;
+    video_id = AV_CODEC_ID_NONE;
+#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__)
+
+    for(;;) {
+        if (fgets(line, sizeof(line), f) == NULL)
+            break;
+        line_num++;
+        p = line;
+        while (av_isspace(*p))
+            p++;
+        if (*p == '\0' || *p == '#')
+            continue;
+
+        ffserver_get_arg(cmd, sizeof(cmd), &p);
+
+        if (!av_strcasecmp(cmd, "Port") || !av_strcasecmp(cmd, "HTTPPort")) {
+            if (!av_strcasecmp(cmd, "Port"))
+                WARNING("Port option is deprecated, use HTTPPort instead\n");
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            val = atoi(arg);
+            if (val < 1 || val > 65536) {
+                ERROR("Invalid port: %s\n", arg);
+            }
+            if (val < 1024)
+                WARNING("Trying to use IETF assigned system port: %d\n", val);
+            config->http_addr.sin_port = htons(val);
+        } else if (!av_strcasecmp(cmd, "HTTPBindAddress") || !av_strcasecmp(cmd, "BindAddress")) {
+            if (!av_strcasecmp(cmd, "BindAddress"))
+                WARNING("BindAddress option is deprecated, use HTTPBindAddress instead\n");
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (resolve_host(&config->http_addr.sin_addr, arg) != 0) {
+                ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
+            }
+        } else if (!av_strcasecmp(cmd, "NoDaemon")) {
+            WARNING("NoDaemon option has no effect, you should remove it\n");
+        } else if (!av_strcasecmp(cmd, "RTSPPort")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            val = atoi(arg);
+            if (val < 1 || val > 65536) {
+                ERROR("%s:%d: Invalid port: %s\n", arg);
+            }
+            config->rtsp_addr.sin_port = htons(atoi(arg));
+        } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (resolve_host(&config->rtsp_addr.sin_addr, arg) != 0) {
+                ERROR("Invalid host/IP address: %s\n", arg);
+            }
+        } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            val = atoi(arg);
+            if (val < 1 || val > 65536) {
+                ERROR("Invalid MaxHTTPConnections: %s\n", arg);
+            }
+            config->nb_max_http_connections = val;
+        } else if (!av_strcasecmp(cmd, "MaxClients")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            val = atoi(arg);
+            if (val < 1 || val > config->nb_max_http_connections) {
+                ERROR("Invalid MaxClients: %s\n", arg);
+            } else {
+                config->nb_max_connections = val;
+            }
+        } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
+            int64_t llval;
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            llval = strtoll(arg, NULL, 10);
+            if (llval < 10 || llval > 10000000) {
+                ERROR("Invalid MaxBandwidth: %s\n", arg);
+            } else
+                config->max_bandwidth = llval;
+        } else if (!av_strcasecmp(cmd, "CustomLog")) {
+            if (!config->debug)
+                ffserver_get_arg(config->logfilename, sizeof(config->logfilename), &p);
+        } else if (!av_strcasecmp(cmd, "<Feed")) {
+            /*********************************************/
+            /* Feed related options */
+            char *q;
+            if (stream || feed) {
+                ERROR("Already in a tag\n");
+            } else {
+                feed = av_mallocz(sizeof(FFServerStream));
+                if (!feed) {
+                    ret = AVERROR(ENOMEM);
+                    goto end;
+                }
+                ffserver_get_arg(feed->filename, sizeof(feed->filename), &p);
+                q = strrchr(feed->filename, '>');
+                if (*q)
+                    *q = '\0';
+
+                for (s = config->first_feed; s; s = s->next) {
+                    if (!strcmp(feed->filename, s->filename)) {
+                        ERROR("Feed '%s' already registered\n", s->filename);
+                    }
+                }
+
+                feed->fmt = av_guess_format("ffm", NULL, NULL);
+                /* default feed file */
+                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 :-) */
+
+                /* add in stream list */
+                *last_stream = feed;
+                last_stream = &feed->next;
+                /* add in feed list */
+                *last_feed = feed;
+                last_feed = &feed->next_feed;
+            }
+        } else if (!av_strcasecmp(cmd, "Launch")) {
+            if (feed) {
+                int i;
+
+                feed->child_argv = av_mallocz(64 * sizeof(char *));
+                if (!feed->child_argv) {
+                    ret = AVERROR(ENOMEM);
+                    goto end;
+                }
+                for (i = 0; i < 62; i++) {
+                    ffserver_get_arg(arg, sizeof(arg), &p);
+                    if (!arg[0])
+                        break;
+
+                    feed->child_argv[i] = av_strdup(arg);
+                    if (!feed->child_argv[i]) {
+                        ret = AVERROR(ENOMEM);
+                        goto end;
+                    }
+                }
+
+                feed->child_argv[i] =
+                    av_asprintf("http://%s:%d/%s",
+                                (config->http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
+                                inet_ntoa(config->http_addr.sin_addr), ntohs(config->http_addr.sin_port),
+                                feed->filename);
+                if (!feed->child_argv[i]) {
+                    ret = AVERROR(ENOMEM);
+                    goto end;
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) {
+            if (feed) {
+                ffserver_get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
+                feed->readonly = !av_strcasecmp(cmd, "ReadOnlyFile");
+            } else if (stream) {
+                ffserver_get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
+            }
+        } else if (!av_strcasecmp(cmd, "Truncate")) {
+            if (feed) {
+                ffserver_get_arg(arg, sizeof(arg), &p);
+                /* assume Truncate is true in case no argument is specified */
+                if (!arg[0]) {
+                    feed->truncate = 1;
+                } else {
+                    WARNING("Truncate N syntax in configuration file is deprecated, "
+                            "use Truncate alone with no arguments\n");
+                    feed->truncate = strtod(arg, NULL);
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
+            if (feed) {
+                char *p1;
+                double fsize;
+
+                ffserver_get_arg(arg, sizeof(arg), &p);
+                p1 = arg;
+                fsize = strtod(p1, &p1);
+                switch(av_toupper(*p1)) {
+                case 'K':
+                    fsize *= 1024;
+                    break;
+                case 'M':
+                    fsize *= 1024 * 1024;
+                    break;
+                case 'G':
+                    fsize *= 1024 * 1024 * 1024;
+                    break;
+                }
+                feed->feed_max_size = (int64_t)fsize;
+                if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
+                    ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "</Feed>")) {
+            if (!feed) {
+                ERROR("No corresponding <Feed> for </Feed>\n");
+            }
+            feed = NULL;
+        } else if (!av_strcasecmp(cmd, "<Stream")) {
+            /*********************************************/
+            /* Stream related options */
+            char *q;
+            if (stream || feed) {
+                ERROR("Already in a tag\n");
+            } else {
+                FFServerStream *s;
+                stream = av_mallocz(sizeof(FFServerStream));
+                if (!stream) {
+                    ret = AVERROR(ENOMEM);
+                    goto end;
+                }
+                ffserver_get_arg(stream->filename, sizeof(stream->filename), &p);
+                q = strrchr(stream->filename, '>');
+                if (q)
+                    *q = '\0';
+
+                for (s = config->first_stream; s; s = s->next) {
+                    if (!strcmp(stream->filename, s->filename)) {
+                        ERROR("Stream '%s' already registered\n", s->filename);
+                    }
+                }
+
+                stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
+                avcodec_get_context_defaults3(&video_enc, NULL);
+                avcodec_get_context_defaults3(&audio_enc, NULL);
+
+                audio_id = AV_CODEC_ID_NONE;
+                video_id = AV_CODEC_ID_NONE;
+                if (stream->fmt) {
+                    audio_id = stream->fmt->audio_codec;
+                    video_id = stream->fmt->video_codec;
+                }
+
+                *last_stream = stream;
+                last_stream = &stream->next;
+            }
+        } else if (!av_strcasecmp(cmd, "Feed")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                FFServerStream *sfeed;
+
+                sfeed = config->first_feed;
+                while (sfeed) {
+                    if (!strcmp(sfeed->filename, arg))
+                        break;
+                    sfeed = sfeed->next_feed;
+                }
+                if (!sfeed)
+                    ERROR("Feed with name '%s' for stream '%s' is not defined\n", arg, stream->filename);
+                else
+                    stream->feed = sfeed;
+            }
+        } else if (!av_strcasecmp(cmd, "Format")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                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 MJPEG */
+                    if (!strcmp(arg, "jpeg"))
+                        strcpy(arg, "mjpeg");
+                    stream->fmt = ffserver_guess_format(arg, NULL, NULL);
+                    if (!stream->fmt) {
+                        ERROR("Unknown Format: %s\n", arg);
+                    }
+                }
+                if (stream->fmt) {
+                    audio_id = stream->fmt->audio_codec;
+                    video_id = stream->fmt->video_codec;
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "InputFormat")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                stream->ifmt = av_find_input_format(arg);
+                if (!stream->ifmt) {
+                    ERROR("Unknown input format: %s\n", arg);
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "FaviconURL")) {
+            if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
+                ffserver_get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
+            } else {
+                ERROR("FaviconURL only permitted for status streams\n");
+            }
+        } else if (!av_strcasecmp(cmd, "Author")    ||
+                   !av_strcasecmp(cmd, "Comment")   ||
+                   !av_strcasecmp(cmd, "Copyright") ||
+                   !av_strcasecmp(cmd, "Title")) {
+            ffserver_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;
+                WARNING("'%s' option in configuration file is deprecated, "
+                        "use 'Metadata %s VALUE' instead\n", cmd, key);
+                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")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            ffserver_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));
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "Preroll")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                stream->prebuffer = atof(arg) * 1000;
+        } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
+            if (stream)
+                stream->send_on_key = 1;
+        } else if (!av_strcasecmp(cmd, "AudioCodec")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            audio_id = opt_codec(arg, AVMEDIA_TYPE_AUDIO);
+            if (audio_id == AV_CODEC_ID_NONE) {
+                ERROR("Unknown AudioCodec: %s\n", arg);
+            }
+        } else if (!av_strcasecmp(cmd, "VideoCodec")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            video_id = opt_codec(arg, AVMEDIA_TYPE_VIDEO);
+            if (video_id == AV_CODEC_ID_NONE) {
+                ERROR("Unknown VideoCodec: %s\n", arg);
+            }
+        } else if (!av_strcasecmp(cmd, "MaxTime")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                stream->max_time = atof(arg) * 1000;
+        } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                audio_enc.bit_rate = lrintf(atof(arg) * 1000);
+        } else if (!av_strcasecmp(cmd, "AudioChannels")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                audio_enc.channels = atoi(arg);
+        } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                audio_enc.sample_rate = atoi(arg);
+        } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
+            if (stream) {
+                int minrate, maxrate;
+
+                ffserver_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 {
+                    ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "Debug")) {
+            if (stream) {
+                ffserver_get_arg(arg, sizeof(arg), &p);
+                video_enc.debug = strtol(arg,0,0);
+            }
+        } else if (!av_strcasecmp(cmd, "Strict")) {
+            if (stream) {
+                ffserver_get_arg(arg, sizeof(arg), &p);
+                video_enc.strict_std_compliance = atoi(arg);
+            }
+        } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
+            if (stream) {
+                ffserver_get_arg(arg, sizeof(arg), &p);
+                video_enc.rc_buffer_size = atoi(arg) * 8*1024;
+            }
+        } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
+            if (stream) {
+                ffserver_get_arg(arg, sizeof(arg), &p);
+                video_enc.bit_rate_tolerance = atoi(arg) * 1000;
+            }
+        } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                video_enc.bit_rate = atoi(arg) * 1000;
+            }
+        } else if (!av_strcasecmp(cmd, "VideoSize")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                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");
+                    }
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                AVRational frame_rate;
+                if (av_parse_video_rate(&frame_rate, arg) < 0) {
+                    ERROR("Incorrect frame rate: %s\n", arg);
+                } else {
+                    video_enc.time_base.num = frame_rate.den;
+                    video_enc.time_base.den = frame_rate.num;
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "PixelFormat")) {
+            ffserver_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);
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                video_enc.gop_size = atoi(arg);
+        } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
+            if (stream)
+                video_enc.gop_size = 1;
+        } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
+            if (stream)
+                video_enc.mb_decision = FF_MB_DECISION_BITS;
+        } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
+            if (stream) {
+                video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
+                video_enc.flags |= CODEC_FLAG_4MV;
+            }
+        } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
+                   !av_strcasecmp(cmd, "AVOptionAudio")) {
+            AVCodecContext *avctx;
+            int type;
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            ffserver_get_arg(arg2, sizeof(arg2), &p);
+            if (!av_strcasecmp(cmd, "AVOptionVideo")) {
+                avctx = &video_enc;
+                type = AV_OPT_FLAG_VIDEO_PARAM;
+            } else {
+                avctx = &audio_enc;
+                type = AV_OPT_FLAG_AUDIO_PARAM;
+            }
+            if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
+                ERROR("Error setting %s option to %s %s\n", cmd, arg, arg2);
+            }
+        } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
+                   !av_strcasecmp(cmd, "AVPresetAudio")) {
+            AVCodecContext *avctx;
+            int type;
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (!av_strcasecmp(cmd, "AVPresetVideo")) {
+                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);
+            }
+        } else if (!av_strcasecmp(cmd, "VideoTag")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if ((strlen(arg) == 4) && stream)
+                video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
+        } else if (!av_strcasecmp(cmd, "BitExact")) {
+            if (stream)
+                video_enc.flags |= CODEC_FLAG_BITEXACT;
+        } else if (!av_strcasecmp(cmd, "DctFastint")) {
+            if (stream)
+                video_enc.dct_algo  = FF_DCT_FASTINT;
+        } else if (!av_strcasecmp(cmd, "IdctSimple")) {
+            if (stream)
+                video_enc.idct_algo = FF_IDCT_SIMPLE;
+        } else if (!av_strcasecmp(cmd, "Qscale")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                video_enc.flags |= CODEC_FLAG_QSCALE;
+                video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
+            }
+        } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                video_enc.max_qdiff = atoi(arg);
+                if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
+                    ERROR("VideoQDiff out of range\n");
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "VideoQMax")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                video_enc.qmax = atoi(arg);
+                if (video_enc.qmax < 1 || video_enc.qmax > 31) {
+                    ERROR("VideoQMax out of range\n");
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "VideoQMin")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                video_enc.qmin = atoi(arg);
+                if (video_enc.qmin < 1 || video_enc.qmin > 31) {
+                    ERROR("VideoQMin out of range\n");
+                }
+            }
+        } else if (!av_strcasecmp(cmd, "LumiMask")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                video_enc.lumi_masking = atof(arg);
+        } else if (!av_strcasecmp(cmd, "DarkMask")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                video_enc.dark_masking = atof(arg);
+        } else if (!av_strcasecmp(cmd, "NoVideo")) {
+            video_id = AV_CODEC_ID_NONE;
+        } else if (!av_strcasecmp(cmd, "NoAudio")) {
+            audio_id = AV_CODEC_ID_NONE;
+        } else if (!av_strcasecmp(cmd, "ACL")) {
+            ffserver_parse_acl_row(stream, feed, NULL, p, filename, line_num);
+        } else if (!av_strcasecmp(cmd, "DynamicACL")) {
+            if (stream) {
+                ffserver_get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
+            }
+        } else if (!av_strcasecmp(cmd, "RTSPOption")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                av_freep(&stream->rtsp_option);
+                stream->rtsp_option = av_strdup(arg);
+            }
+        } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream) {
+                if (resolve_host(&stream->multicast_ip, arg) != 0) {
+                    ERROR("Invalid host/IP address: %s\n", arg);
+                }
+                stream->is_multicast = 1;
+                stream->loop = 1; /* default is looping */
+            }
+        } else if (!av_strcasecmp(cmd, "MulticastPort")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                stream->multicast_port = atoi(arg);
+        } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
+            ffserver_get_arg(arg, sizeof(arg), &p);
+            if (stream)
+                stream->multicast_ttl = atoi(arg);
+        } else if (!av_strcasecmp(cmd, "NoLoop")) {
+            if (stream)
+                stream->loop = 0;
+        } else if (!av_strcasecmp(cmd, "</Stream>")) {
+            if (!stream) {
+                ERROR("No corresponding <Stream> for </Stream>\n");
+            } else {
+                if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
+                    if (audio_id != AV_CODEC_ID_NONE) {
+                        audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
+                        audio_enc.codec_id = audio_id;
+                        add_codec(stream, &audio_enc);
+                    }
+                    if (video_id != AV_CODEC_ID_NONE) {
+                        video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
+                        video_enc.codec_id = video_id;
+                        add_codec(stream, &video_enc);
+                    }
+                }
+                stream = NULL;
+            }
+        } else if (!av_strcasecmp(cmd, "<Redirect")) {
+            /*********************************************/
+            char *q;
+            if (stream || feed || redirect) {
+                ERROR("Already in a tag\n");
+            } else {
+                redirect = av_mallocz(sizeof(FFServerStream));
+                if (!redirect) {
+                    ret = AVERROR(ENOMEM);
+                    goto end;
+                }
+                *last_stream = redirect;
+                last_stream = &redirect->next;
+
+                ffserver_get_arg(redirect->filename, sizeof(redirect->filename), &p);
+                q = strrchr(redirect->filename, '>');
+                if (*q)
+                    *q = '\0';
+                redirect->stream_type = STREAM_TYPE_REDIRECT;
+            }
+        } else if (!av_strcasecmp(cmd, "URL")) {
+            if (redirect)
+                ffserver_get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
+        } else if (!av_strcasecmp(cmd, "</Redirect>")) {
+            if (!redirect) {
+                ERROR("No corresponding <Redirect> for </Redirect>\n");
+            } else {
+                if (!redirect->feed_filename[0]) {
+                    ERROR("No URL found for <Redirect>\n");
+                }
+                redirect = NULL;
+            }
+        } else if (!av_strcasecmp(cmd, "LoadModule")) {
+            ERROR("Loadable modules no longer supported\n");
+        } else {
+            ERROR("Incorrect keyword: '%s'\n", cmd);
+        }
+    }
+#undef ERROR
+
+end:
+    fclose(f);
+    if (ret < 0)
+        return ret;
+    if (errors)
+        return AVERROR(EINVAL);
+    else
+        return 0;
+}
diff --git a/ffserver_config.h b/ffserver_config.h
new file mode 100644 (file)
index 0000000..b19b3fe
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FFSERVER_CONFIG_H
+#define FFSERVER_CONFIG_H
+
+#include "libavutil/dict.h"
+#include "libavformat/avformat.h"
+#include "libavformat/network.h"
+
+#define FFSERVER_MAX_STREAMS 20
+
+/* each generated stream is described here */
+enum FFServerStreamType {
+    STREAM_TYPE_LIVE,
+    STREAM_TYPE_STATUS,
+    STREAM_TYPE_REDIRECT,
+};
+
+enum FFServerIPAddressAction {
+    IP_ALLOW = 1,
+    IP_DENY,
+};
+
+typedef struct FFServerIPAddressACL {
+    struct FFServerIPAddressACL *next;
+    enum FFServerIPAddressAction action;
+    /* These are in host order */
+    struct in_addr first;
+    struct in_addr last;
+} FFServerIPAddressACL;
+
+/* description of each stream of the ffserver.conf file */
+typedef struct FFServerStream {
+    enum FFServerStreamType stream_type;
+    char filename[1024];          /* stream filename */
+    struct FFServerStream *feed;  /* feed we are using (can be null if coming from file) */
+    AVDictionary *in_opts;        /* input parameters */
+    AVDictionary *metadata;       /* metadata to set on the stream */
+    AVInputFormat *ifmt;          /* if non NULL, force input format */
+    AVOutputFormat *fmt;
+    FFServerIPAddressACL *acl;
+    char dynamic_acl[1024];
+    int nb_streams;
+    int prebuffer;                /* Number of milliseconds early to start */
+    int64_t max_time;             /* Number of milliseconds to run */
+    int send_on_key;
+    AVStream *streams[FFSERVER_MAX_STREAMS];
+    int feed_streams[FFSERVER_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 */
+    pid_t pid;                    /* Of ffmpeg process */
+    time_t pid_start;             /* Of ffmpeg process */
+    char **child_argv;
+    struct FFServerStream *next;
+    unsigned bandwidth;           /* bandwidth, in kbits/s */
+    /* RTSP options */
+    char *rtsp_option;
+    /* multicast specific */
+    int is_multicast;
+    struct in_addr multicast_ip;
+    int multicast_port;           /* first port used for multicast */
+    int multicast_ttl;
+    int loop;                     /* if true, send the stream in loops (only meaningful if file) */
+
+    /* feed specific */
+    int feed_opened;              /* true if someone is writing to the feed */
+    int is_feed;                  /* true if it is a feed */
+    int readonly;                 /* True if writing is prohibited to the file */
+    int truncate;                 /* True if feeder connection truncate the feed file */
+    int conns_served;
+    int64_t bytes_served;
+    int64_t feed_max_size;        /* maximum storage size, zero means unlimited */
+    int64_t feed_write_index;     /* current write position in feed (it wraps around) */
+    int64_t feed_size;            /* current size of feed */
+    struct FFServerStream *next_feed;
+} FFServerStream;
+
+typedef struct FFServerConfig {
+    char *filename;
+    FFServerStream *first_feed;   /* contains only feeds */
+    FFServerStream *first_stream; /* contains all streams, including feeds */
+    unsigned int nb_max_http_connections;
+    unsigned int nb_max_connections;
+    uint64_t max_bandwidth;
+    int debug;
+    char logfilename[1024];
+    struct sockaddr_in http_addr;
+    struct sockaddr_in rtsp_addr;
+
+} FFServerConfig;
+
+void ffserver_get_arg(char *buf, int buf_size, const char **pp);
+
+void ffserver_parse_acl_row(FFServerStream *stream, FFServerStream* feed,
+                            FFServerIPAddressACL *ext_acl,
+                            const char *p, const char *filename, int line_num);
+
+int ffserver_parse_ffconfig(const char *filename, FFServerConfig *config);
+
+#endif /* FFSERVER_CONFIG_H */