Skip to content
Snippets Groups Projects
ffserver.c 142 KiB
Newer Older
  • Learn to ignore specific revisions
  •     /* session ID */
        url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
        url_fprintf(c->pb, "\r\n");
    }
    
    static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
    {
        HTTPContext *rtp_c;
    
        rtp_c = find_rtp_session_with_url(url, h->session_id);
        if (!rtp_c) {
            rtsp_reply_error(c, RTSP_STATUS_SESSION);
            return;
        }
        
        /* abort the session */
        close_connection(rtp_c);
    
        if (ff_rtsp_callback) {
            ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id, 
                             NULL, 0,
                             rtp_c->stream->rtsp_option);
        }
    
        /* now everything is OK, so we can send the connection parameters */
        rtsp_reply_header(c, RTSP_STATUS_OK);
        /* session ID */
        url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
        url_fprintf(c->pb, "\r\n");
    }
    
    
    /********************************************************************/
    /* RTP handling */
    
    
    static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, 
    
                                           FFStream *stream, const char *session_id)
    {
        HTTPContext *c = NULL;
    
        /* XXX: should output a warning page when coming
           close to the connection limit */
        if (nb_connections >= nb_max_connections)
            goto fail;
        
        /* add a new connection */
        c = av_mallocz(sizeof(HTTPContext));
        if (!c)
            goto fail;
        
        c->fd = -1;
        c->poll_entry = NULL;
    
        c->buffer_size = IOBUFFER_INIT_SIZE;
        c->buffer = av_malloc(c->buffer_size);
        if (!c->buffer)
            goto fail;
        nb_connections++;
        c->stream = stream;
        pstrcpy(c->session_id, sizeof(c->session_id), session_id);
        c->state = HTTPSTATE_READY;
        c->is_packetized = 1;
        /* protocol is shown in statistics */
        pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
    
    
        c->next = first_http_ctx;
        first_http_ctx = c;
        return c;
            
     fail:
        if (c) {
            av_free(c->buffer);
            av_free(c);
        }
        return NULL;
    }
    
    /* add a new RTP stream in an RTP connection (used in RTSP SETUP
       command). if dest_addr is NULL, then TCP tunneling in RTSP is
       used. */
    static int rtp_new_av_stream(HTTPContext *c, 
                                 int stream_index, struct sockaddr_in *dest_addr)
    {
        AVFormatContext *ctx;
        AVStream *st;
        char *ipaddr;
        URLContext *h;
        UINT8 *dummy_buf;
    
        /* now we can open the relevant output stream */
        ctx = av_mallocz(sizeof(AVFormatContext));
        if (!ctx)
            return -1;
        ctx->oformat = &rtp_mux;
    
        st = av_mallocz(sizeof(AVStream));
        if (!st)
            goto fail;
        ctx->nb_streams = 1;
        ctx->streams[0] = st;
    
        if (!c->stream->feed || 
            c->stream->feed == c->stream) {
            memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
        } else {
            memcpy(st, 
                   c->stream->feed->streams[c->stream->feed_streams[stream_index]],
                   sizeof(AVStream));
        }
        
        if (dest_addr) {
            /* build destination RTP address */
            ipaddr = inet_ntoa(dest_addr->sin_addr);
            
    
            /* XXX: also pass as parameter to function ? */
            if (c->stream->is_multicast) {
                int ttl;
                ttl = c->stream->multicast_ttl;
                if (!ttl)
                    ttl = 16;
                snprintf(ctx->filename, sizeof(ctx->filename),
                         "rtp://%s:%d?multicast=1&ttl=%d", 
                         ipaddr, ntohs(dest_addr->sin_port), ttl);
            } else {
                snprintf(ctx->filename, sizeof(ctx->filename),
                         "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
            }
    
    
            if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
                goto fail;
            c->rtp_handles[stream_index] = h;
        } else {
            goto fail;
        }
    
    
        http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
                 ipaddr, ntohs(dest_addr->sin_port), 
                 ctime1(buf2), 
                 c->stream->filename, stream_index);
    
    
        /* normally, no packets should be output here, but the packet size may be checked */
        if (url_open_dyn_packet_buf(&ctx->pb, 
                                    url_get_max_packet_size(h)) < 0) {
            /* XXX: close stream */
            goto fail;
        }
    
        av_set_parameters(ctx, NULL);
    
        if (av_write_header(ctx) < 0) {
        fail:
            if (h)
                url_close(h);
            av_free(ctx);
            return -1;
        }
        url_close_dyn_buf(&ctx->pb, &dummy_buf);
        av_free(dummy_buf);
        
        c->rtp_ctx[stream_index] = ctx;
        return 0;
    }
    
    /********************************************************************/
    /* ffserver initialization */
    
    
    static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
    
    {
        AVStream *fst;
    
        fst = av_mallocz(sizeof(AVStream));
        if (!fst)
            return NULL;
        fst->priv_data = av_mallocz(sizeof(FeedData));
        memcpy(&fst->codec, codec, sizeof(AVCodecContext));
    
        stream->streams[stream->nb_streams++] = fst;
        return fst;
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* return the stream number in the feed */
    
    static int add_av_stream(FFStream *feed, AVStream *st)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        AVStream *fst;
        AVCodecContext *av, *av1;
        int i;
    
        av = &st->codec;
        for(i=0;i<feed->nb_streams;i++) {
            st = feed->streams[i];
            av1 = &st->codec;
    
            if (av1->codec_id == av->codec_id &&
                av1->codec_type == av->codec_type &&
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                av1->bit_rate == av->bit_rate) {
    
                switch(av->codec_type) {
                case CODEC_TYPE_AUDIO:
                    if (av1->channels == av->channels &&
                        av1->sample_rate == av->sample_rate)
                        goto found;
                    break;
                case CODEC_TYPE_VIDEO:
                    if (av1->width == av->width &&
                        av1->height == av->height &&
                        av1->frame_rate == av->frame_rate &&
                        av1->gop_size == av->gop_size)
                        goto found;
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (!fst)
            return -1;
        return feed->nb_streams - 1;
     found:
        return i;
    }
    
    
    static void remove_stream(FFStream *stream)
    
    {
        FFStream **ps;
        ps = &first_stream;
        while (*ps != NULL) {
            if (*ps == stream) {
                *ps = (*ps)->next;
            } else {
                ps = &(*ps)->next;
            }
        }
    }
    
    
    /* specific mpeg4 handling : we extract the raw parameters */
    
    static void extract_mpeg4_header(AVFormatContext *infile)
    
    {
        int mpeg4_count, i, size;
        AVPacket pkt;
        AVStream *st;
        const UINT8 *p;
    
        mpeg4_count = 0;
        for(i=0;i<infile->nb_streams;i++) {
            st = infile->streams[i];
            if (st->codec.codec_id == CODEC_ID_MPEG4 &&
                st->codec.extradata == NULL) {
                mpeg4_count++;
            }
        }
        if (!mpeg4_count)
            return;
    
        printf("MPEG4 without extra data: trying to find header\n");
        while (mpeg4_count > 0) {
            if (av_read_packet(infile, &pkt) < 0)
                break;
            st = infile->streams[pkt.stream_index];
            if (st->codec.codec_id == CODEC_ID_MPEG4 &&
                st->codec.extradata == NULL) {
                /* fill extradata with the header */
                /* XXX: we make hard suppositions here ! */
                p = pkt.data;
                while (p < pkt.data + pkt.size - 4) {
                    /* stop when vop header is found */
                    if (p[0] == 0x00 && p[1] == 0x00 && 
                        p[2] == 0x01 && p[3] == 0xb6) {
                        size = p - pkt.data;
                        //                    av_hex_dump(pkt.data, size);
                        st->codec.extradata = av_malloc(size);
                        st->codec.extradata_size = size;
                        memcpy(st->codec.extradata, pkt.data, size);
                        break;
                    }
                    p++;
                }
                mpeg4_count--;
            }
            av_free_packet(&pkt);
        }
    }
    
    
    /* compute the needed AVStream for each file */
    
    static void build_file_streams(void)
    
    {
        FFStream *stream, *stream_next;
        AVFormatContext *infile;
        int i;
    
        /* gather all streams */
        for(stream = first_stream; stream != NULL; stream = stream_next) {
            stream_next = stream->next;
            if (stream->stream_type == STREAM_TYPE_LIVE &&
                !stream->feed) {
                /* the stream comes from a file */
                /* try to open the file */
                /* open stream */
                if (av_open_input_file(&infile, stream->feed_filename, 
                                       NULL, 0, NULL) < 0) {
                    http_log("%s not found", stream->feed_filename);
                    /* remove stream (no need to spend more time on it) */
                fail:
                    remove_stream(stream);
                } else {
                    /* find all the AVStreams inside and reference them in
                       'stream' */
                    if (av_find_stream_info(infile) < 0) {
                        http_log("Could not find codec parameters from '%s'", 
                                 stream->feed_filename);
                        av_close_input_file(infile);
                        goto fail;
                    }
    
                    for(i=0;i<infile->nb_streams;i++) {
                        add_av_stream1(stream, &infile->streams[i]->codec);
                    }
                    av_close_input_file(infile);
                }
            }
        }
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* compute the needed AVStream for each feed */
    
    static void build_feed_streams(void)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        FFStream *stream, *feed;
        int i;
    
        /* gather all streams */
        for(stream = first_stream; stream != NULL; stream = stream->next) {
            feed = stream->feed;
            if (feed) {
                if (!stream->is_feed) {
    
                    /* we handle a stream coming from a feed */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    for(i=0;i<stream->nb_streams;i++) {
                        stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
                    }
    
                }
            }
        }
    
        /* gather all streams */
        for(stream = first_stream; stream != NULL; stream = stream->next) {
            feed = stream->feed;
            if (feed) {
                if (stream->is_feed) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    for(i=0;i<stream->nb_streams;i++) {
                        stream->feed_streams[i] = i;
                    }
                }
            }
        }
    
        /* create feed files if needed */
        for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
            int fd;
    
    
            if (url_exist(feed->feed_filename)) {
                /* See if it matches */
                AVFormatContext *s;
                int matches = 0;
    
                if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
                    /* Now see if it matches */
                    if (s->nb_streams == feed->nb_streams) {
                        matches = 1;
                        for(i=0;i<s->nb_streams;i++) {
                            AVStream *sf, *ss;
                            sf = feed->streams[i];
                            ss = s->streams[i];
    
                            if (sf->index != ss->index ||
                                sf->id != ss->id) {
                                printf("Index & Id do not match for stream %d\n", i);
                                matches = 0;
                            } else {
                                AVCodecContext *ccf, *ccs;
    
                                ccf = &sf->codec;
                                ccs = &ss->codec;
    #define CHECK_CODEC(x)  (ccf->x != ccs->x)
    
                                if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
                                    printf("Codecs do not match for stream %d\n", i);
                                    matches = 0;
                                } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
                                    printf("Codec bitrates do not match for stream %d\n", i);
                                    matches = 0;
                                } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
                                    if (CHECK_CODEC(frame_rate) ||
                                        CHECK_CODEC(width) ||
                                        CHECK_CODEC(height)) {
                                        printf("Codec width, height and framerate do not match for stream %d\n", i);
                                        matches = 0;
                                    }
                                } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
                                    if (CHECK_CODEC(sample_rate) ||
                                        CHECK_CODEC(channels) ||
                                        CHECK_CODEC(frame_size)) {
                                        printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
                                        matches = 0;
                                    }
                                } else {
                                    printf("Unknown codec type\n");
                                    matches = 0;
                                }
                            }
                            if (!matches) {
                                break;
                            }
                        }
                    } else {
                        printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
                            feed->feed_filename, s->nb_streams, feed->nb_streams);
                    }
    
                    av_close_input_file(s);
                } else {
                    printf("Deleting feed file '%s' as it appears to be corrupt\n",
                            feed->feed_filename);
                }
                if (!matches)
                    unlink(feed->feed_filename);
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (!url_exist(feed->feed_filename)) {
                AVFormatContext s1, *s = &s1;
    
                /* only write the header of the ffm file */
                if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
                    fprintf(stderr, "Could not open output feed file '%s'\n",
                            feed->feed_filename);
                    exit(1);
                }
    
                s->oformat = feed->fmt;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                s->nb_streams = feed->nb_streams;
                for(i=0;i<s->nb_streams;i++) {
                    AVStream *st;
                    st = feed->streams[i];
                    s->streams[i] = st;
                }
    
                av_set_parameters(s, NULL);
    
                av_write_header(s);
                /* XXX: need better api */
                av_freep(&s->priv_data);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                url_fclose(&s->pb);
            }
            /* get feed size and write index */
            fd = open(feed->feed_filename, O_RDONLY);
            if (fd < 0) {
                fprintf(stderr, "Could not open output feed file '%s'\n",
                        feed->feed_filename);
                exit(1);
            }
    
            feed->feed_write_index = ffm_read_write_index(fd);
            feed->feed_size = lseek(fd, 0, SEEK_END);
            /* ensure that we do not wrap before the end of file */
            if (feed->feed_max_size < feed->feed_size)
                feed->feed_max_size = feed->feed_size;
    
            close(fd);
        }
    }
    
    
    /* compute the bandwidth used by each stream */
    static void compute_bandwidth(void)
    {
        int bandwidth, i;
        FFStream *stream;
        
        for(stream = first_stream; stream != NULL; stream = stream->next) {
            bandwidth = 0;
            for(i=0;i<stream->nb_streams;i++) {
                AVStream *st = stream->streams[i];
                switch(st->codec.codec_type) {
                case CODEC_TYPE_AUDIO:
                case CODEC_TYPE_VIDEO:
                    bandwidth += st->codec.bit_rate;
                    break;
                default:
                    break;
                }
            }
            stream->bandwidth = (bandwidth + 999) / 1000;
        }
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static void get_arg(char *buf, int buf_size, const char **pp)
    {
        const char *p;
        char *q;
        int quote;
    
        p = *pp;
        while (isspace(*p)) p++;
        q = buf;
        quote = 0;
        if (*p == '\"' || *p == '\'')
            quote = *p++;
        for(;;) {
            if (quote) {
                if (*p == quote)
                    break;
            } else {
                if (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;
    }
    
    /* add a codec and set the default parameters */
    
    static void add_codec(FFStream *stream, AVCodecContext *av)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        AVStream *st;
    
        /* compute default parameters */
        switch(av->codec_type) {
        case CODEC_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 CODEC_TYPE_VIDEO:
            if (av->bit_rate == 0)
                av->bit_rate = 64000;
            if (av->frame_rate == 0)
                av->frame_rate = 5 * FRAME_RATE_BASE;
            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 = av->bit_rate / 4;
            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->rc_eq)
                av->rc_eq = "tex^qComp";
            if (!av->i_quant_factor)
    
            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_min_rate)
                av->rc_min_rate = av->bit_rate / 2;
            if (!av->rc_max_rate)
                av->rc_max_rate = av->bit_rate * 2;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        st = av_mallocz(sizeof(AVStream));
        if (!st)
            return;
        stream->streams[stream->nb_streams++] = st;
        memcpy(&st->codec, av, sizeof(AVCodecContext));
    }
    
    
    static int opt_audio_codec(const char *arg)
    
    {
        AVCodec *p;
    
        p = first_avcodec;
        while (p) {
            if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
                break;
            p = p->next;
        }
        if (p == NULL) {
            return CODEC_ID_NONE;
        }
    
        return p->id;
    }
    
    
    static int opt_video_codec(const char *arg)
    
    {
        AVCodec *p;
    
        p = first_avcodec;
        while (p) {
            if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
                break;
            p = p->next;
        }
        if (p == NULL) {
            return CODEC_ID_NONE;
        }
    
        return p->id;
    }
    
    
    #ifdef CONFIG_HAVE_DLOPEN
    
    void load_module(const char *filename)
    {
        void *dll;
        void (*init_func)(void);
        dll = dlopen(filename, RTLD_NOW);
        if (!dll) {
            fprintf(stderr, "Could not load module '%s' - %s\n",
                    filename, dlerror());
            return;
        }
        
        init_func = dlsym(dll, "ffserver_module_init");
        if (!init_func) {
            fprintf(stderr, 
                    "%s: init function 'ffserver_module_init()' not found\n",
                    filename);
            dlclose(dll);
        }
    
        init_func();
    }
    
    static int parse_ffconfig(const char *filename)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        FILE *f;
        char line[1024];
        char cmd[64];
        char arg[1024];
        const char *p;
        int val, errors, line_num;
    
        FFStream **last_stream, *stream, *redirect;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        FFStream **last_feed, *feed;
        AVCodecContext audio_enc, video_enc;
        int audio_id, video_id;
    
        f = fopen(filename, "r");
        if (!f) {
            perror(filename);
            return -1;
        }
        
        errors = 0;
        line_num = 0;
        first_stream = NULL;
        last_stream = &first_stream;
        first_feed = NULL;
        last_feed = &first_feed;
        stream = NULL;
        feed = NULL;
    
        redirect = NULL;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        audio_id = CODEC_ID_NONE;
        video_id = CODEC_ID_NONE;
        for(;;) {
            if (fgets(line, sizeof(line), f) == NULL)
                break;
            line_num++;
            p = line;
            while (isspace(*p)) 
                p++;
            if (*p == '\0' || *p == '#')
                continue;
    
            get_arg(cmd, sizeof(cmd), &p);
            
            if (!strcasecmp(cmd, "Port")) {
                get_arg(arg, sizeof(arg), &p);
    
                my_http_addr.sin_port = htons (atoi(arg));
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "BindAddress")) {
                get_arg(arg, sizeof(arg), &p);
    
                if (!inet_aton(arg, &my_http_addr.sin_addr)) {
                    fprintf(stderr, "%s:%d: Invalid IP address: %s\n", 
                            filename, line_num, arg);
                    errors++;
                }
            } else if (!strcasecmp(cmd, "NoDaemon")) {
                ffserver_daemon = 0;
            } else if (!strcasecmp(cmd, "RTSPPort")) {
                get_arg(arg, sizeof(arg), &p);
                my_rtsp_addr.sin_port = htons (atoi(arg));
            } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
                get_arg(arg, sizeof(arg), &p);
                if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    fprintf(stderr, "%s:%d: Invalid IP address: %s\n", 
                            filename, line_num, arg);
                    errors++;
                }
            } else if (!strcasecmp(cmd, "MaxClients")) {
                get_arg(arg, sizeof(arg), &p);
                val = atoi(arg);
                if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
                    fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n", 
                            filename, line_num, arg);
                    errors++;
                } else {
                    nb_max_connections = val;
                }
    
            } else if (!strcasecmp(cmd, "MaxBandwidth")) {
                get_arg(arg, sizeof(arg), &p);
                val = atoi(arg);
                if (val < 10 || val > 100000) {
                    fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n", 
                            filename, line_num, arg);
                    errors++;
                } else {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "CustomLog")) {
                get_arg(logfilename, sizeof(logfilename), &p);
            } else if (!strcasecmp(cmd, "<Feed")) {
                /*********************************************/
                /* Feed related options */
                char *q;
                if (stream || feed) {
                    fprintf(stderr, "%s:%d: Already in a tag\n",
                            filename, line_num);
                } else {
                    feed = av_mallocz(sizeof(FFStream));
                    /* add in stream list */
                    *last_stream = feed;
                    last_stream = &feed->next;
                    /* add in feed list */
                    *last_feed = feed;
                    last_feed = &feed->next_feed;
                    
                    get_arg(feed->filename, sizeof(feed->filename), &p);
                    q = strrchr(feed->filename, '>');
                    if (*q)
                        *q = '\0';
                    feed->fmt = guess_format("ffm", NULL, NULL);
                    /* defaut 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 :-) */
                }
    
            } else if (!strcasecmp(cmd, "Launch")) {
                if (feed) {
                    int i;
    
                    feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
    
                    feed->child_argv[0] = av_malloc(7);
                    strcpy(feed->child_argv[0], "ffmpeg");
    
                    for (i = 1; i < 62; i++) {
                        char argbuf[256];
    
                        get_arg(argbuf, sizeof(argbuf), &p);
                        if (!argbuf[0])
                            break;
    
                        feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
                        strcpy(feed->child_argv[i], argbuf);
                    }
    
                    feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
    
                    snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s", 
    
                        ntohs(my_http_addr.sin_port), feed->filename);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "File")) {
                if (feed) {
                    get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
                } else if (stream) {
                    get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
                }
            } else if (!strcasecmp(cmd, "FileMaxSize")) {
                if (feed) {
                    const char *p1;
                    double fsize;
    
                    get_arg(arg, sizeof(arg), &p);
                    p1 = arg;
                    fsize = strtod(p1, (char **)&p1);
                    switch(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)fsize;
                }
            } else if (!strcasecmp(cmd, "</Feed>")) {
                if (!feed) {
                    fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
                            filename, line_num);
                    errors++;
    
                } else {
                    /* Make sure that we start out clean */
    
                    if (unlink(feed->feed_filename) < 0 
                        && errno != ENOENT) {
                        fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
                            filename, line_num, feed->feed_filename, strerror(errno));
                        errors++;
                    }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
                feed = NULL;
            } else if (!strcasecmp(cmd, "<Stream")) {
                /*********************************************/
                /* Stream related options */
                char *q;
                if (stream || feed) {
                    fprintf(stderr, "%s:%d: Already in a tag\n",
                            filename, line_num);
                } else {
                    stream = av_mallocz(sizeof(FFStream));
                    *last_stream = stream;
                    last_stream = &stream->next;
    
                    get_arg(stream->filename, sizeof(stream->filename), &p);
                    q = strrchr(stream->filename, '>');
                    if (*q)
                        *q = '\0';
    
                    stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    memset(&audio_enc, 0, sizeof(AVCodecContext));
                    memset(&video_enc, 0, sizeof(AVCodecContext));
                    audio_id = CODEC_ID_NONE;
                    video_id = CODEC_ID_NONE;
                    if (stream->fmt) {
                        audio_id = stream->fmt->audio_codec;
                        video_id = stream->fmt->video_codec;
                    }
                }
            } else if (!strcasecmp(cmd, "Feed")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    FFStream *sfeed;
                    
                    sfeed = first_feed;
                    while (sfeed != NULL) {
                        if (!strcmp(sfeed->filename, arg))
                            break;
                        sfeed = sfeed->next_feed;
                    }
                    if (!sfeed) {
                        fprintf(stderr, "%s:%d: feed '%s' not defined\n",
                                filename, line_num, arg);
                    } else {
                        stream->feed = sfeed;
                    }
                }
            } else if (!strcasecmp(cmd, "Format")) {
                get_arg(arg, sizeof(arg), &p);
                if (!strcmp(arg, "status")) {
                    stream->stream_type = STREAM_TYPE_STATUS;
                    stream->fmt = NULL;
                } else {
                    stream->stream_type = STREAM_TYPE_LIVE;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    /* jpeg cannot be used here, so use single frame jpeg */
                    if (!strcmp(arg, "jpeg"))
                        strcpy(arg, "singlejpeg");
    
                    stream->fmt = guess_stream_format(arg, NULL, NULL);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    if (!stream->fmt) {
                        fprintf(stderr, "%s:%d: Unknown Format: %s\n", 
                                filename, line_num, arg);
                        errors++;
                    }
                }
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
                }
    
            } else if (!strcasecmp(cmd, "FaviconURL")) {
                if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
                    get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
                } else {
                    fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n", 
                                filename, line_num);
                    errors++;
                }
    
            } else if (!strcasecmp(cmd, "Author")) {
                if (stream) {
                    get_arg(stream->author, sizeof(stream->author), &p);
                }
            } else if (!strcasecmp(cmd, "Comment")) {
                if (stream) {
                    get_arg(stream->comment, sizeof(stream->comment), &p);
                }
            } else if (!strcasecmp(cmd, "Copyright")) {
                if (stream) {
                    get_arg(stream->copyright, sizeof(stream->copyright), &p);
                }
            } else if (!strcasecmp(cmd, "Title")) {
                if (stream) {
                    get_arg(stream->title, sizeof(stream->title), &p);
                }
    
            } else if (!strcasecmp(cmd, "Preroll")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
    
                    stream->prebuffer = atof(arg) * 1000;
    
            } else if (!strcasecmp(cmd, "StartSendOnKey")) {
                if (stream) {
                    stream->send_on_key = 1;
                }
    
            } else if (!strcasecmp(cmd, "AudioCodec")) {
                get_arg(arg, sizeof(arg), &p);
                audio_id = opt_audio_codec(arg);
                if (audio_id == CODEC_ID_NONE) {
                    fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n", 
                            filename, line_num, arg);
                    errors++;
                }
            } else if (!strcasecmp(cmd, "VideoCodec")) {
                get_arg(arg, sizeof(arg), &p);
                video_id = opt_video_codec(arg);
                if (video_id == CODEC_ID_NONE) {
                    fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n", 
                            filename, line_num, arg);
                    errors++;
                }
    
            } else if (!strcasecmp(cmd, "MaxTime")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "AudioBitRate")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    audio_enc.bit_rate = atoi(arg) * 1000;
                }
            } else if (!strcasecmp(cmd, "AudioChannels")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    audio_enc.channels = atoi(arg);
                }
            } else if (!strcasecmp(cmd, "AudioSampleRate")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    audio_enc.sample_rate = atoi(arg);
                }
    
    	} else if (!strcasecmp(cmd, "AudioQuality")) {
    	    get_arg(arg, sizeof(arg), &p);
                if (stream) {
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
    //                audio_enc.quality = atof(arg) * 1000;
    
            } else if (!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 {
                        fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", 
                                filename, line_num, arg);
                        errors++;
                    }
                }
            } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
                if (stream) {
                    get_arg(arg, sizeof(arg), &p);
                    video_enc.bit_rate_tolerance = atoi(arg) * 1000;
                }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "VideoBitRate")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    video_enc.bit_rate = atoi(arg) * 1000;
                }
            } else if (!strcasecmp(cmd, "VideoSize")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    parse_image_size(&video_enc.width, &video_enc.height, arg);
                    if ((video_enc.width % 16) != 0 ||
                        (video_enc.height % 16) != 0) {
                        fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
                                filename, line_num);
                        errors++;
                    }