Skip to content
Snippets Groups Projects
ffserver.c 150 KiB
Newer Older
  • Learn to ignore specific revisions
  •         rtsp_reply_error(c, RTSP_STATUS_SESSION);
            return;
        }
    
        if (rtp_c->state != HTTPSTATE_SEND_DATA &&
            rtp_c->state != HTTPSTATE_WAIT_FEED) {
            rtsp_reply_error(c, RTSP_STATUS_STATE);
            return;
        }
    
        /* 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");
    }
    
    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,
                                           enum RTSPProtocol rtp_protocol)
    
        const char *proto_str;
    
        /* 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->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;
    
        c->rtp_protocol = rtp_protocol;
    
    
        switch(c->rtp_protocol) {
        case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
            proto_str = "MCAST";
            break;
        case RTSP_PROTOCOL_RTP_UDP:
            proto_str = "UDP";
            break;
        case RTSP_PROTOCOL_RTP_TCP:
            proto_str = "TCP";
            break;
        default:
            proto_str = "???";
            break;
        }
        pstrcpy(c->protocol, sizeof(c->protocol), "RTP/");
        pstrcat(c->protocol, sizeof(c->protocol), proto_str);
    
        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 RTP/TCP protocol is used, TCP connection 'rtsp_c' is
    
    static int rtp_new_av_stream(HTTPContext *c,
    
                                 int stream_index, struct sockaddr_in *dest_addr,
                                 HTTPContext *rtsp_c)
    
    {
        AVFormatContext *ctx;
        AVStream *st;
        char *ipaddr;
        URLContext *h;
    
        uint8_t *dummy_buf;
    
        int max_packet_size;
    
        /* now we can open the relevant output stream */
    
        if (!ctx)
            return -1;
        ctx->oformat = &rtp_mux;
    
        st = av_mallocz(sizeof(AVStream));
        if (!st)
            goto fail;
    
        st->codec= avcodec_alloc_context();
    
        if (!c->stream->feed ||
    
            c->stream->feed == c->stream) {
            memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
        } else {
    
                   c->stream->feed->streams[c->stream->feed_streams[stream_index]],
                   sizeof(AVStream));
        }
    
        /* build destination RTP address */
        ipaddr = inet_ntoa(dest_addr->sin_addr);
    
        switch(c->rtp_protocol) {
        case RTSP_PROTOCOL_RTP_UDP:
        case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
            /* RTP/UDP case */
    
            /* 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;
    
            max_packet_size = url_get_max_packet_size(h);
            break;
        case RTSP_PROTOCOL_RTP_TCP:
            /* RTP/TCP case */
            c->rtsp_c = rtsp_c;
            max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
            break;
        default:
    
        http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
    
                 ipaddr, ntohs(dest_addr->sin_port),
                 ctime1(buf2),
    
                 c->stream->filename, stream_index, c->protocol);
    
        /* normally, no packets should be output here, but the packet size may be checked */
    
        if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
    
        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->codec= avcodec_alloc_context();
    
        fst->priv_data = av_mallocz(sizeof(FeedData));
    
        memcpy(fst->codec, codec, sizeof(AVCodecContext));
        fst->codec->coded_frame = &dummy_frame;
    
        av_set_pts_info(fst, 33, 1, 90000);
    
        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;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        for(i=0;i<feed->nb_streams;i++) {
            st = feed->streams[i];
    
            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->time_base.den == av->time_base.den &&
                        av1->time_base.num == av->time_base.num &&
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        av1->gop_size == av->gop_size)
                        goto found;
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
            }
        }
    
    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_t *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_size == 0) {
    
        printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
    
        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_size == 0) {
                av_freep(&st->codec->extradata);
    
                /* 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);
    
    /* 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 */
    
                stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
                if (stream->fmt == &rtp_mux) {
                    /* specific case : if transport stream output to RTP,
                       we use a raw transport stream reader */
                    stream->ap_in->mpeg2ts_raw = 1;
                    stream->ap_in->mpeg2ts_compute_pcr = 1;
                }
    
    
                if (av_open_input_file(&infile, stream->feed_filename,
    
                                       stream->ifmt, 0, stream->ap_in) < 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;
                    }
    
                        add_av_stream1(stream, infile->streams[i]->codec);
    
    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 (%s)\n",
    
    #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(time_base.den) ||
                                        CHECK_CODEC(time_base.num) ||
    
                                        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) {
                    if (feed->readonly) {
                        printf("Unable to delete feed file '%s' as it is marked readonly\n",
                            feed->feed_filename);
                        exit(1);
                    }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (!url_exist(feed->feed_filename)) {
                AVFormatContext s1, *s = &s1;
    
    
                if (feed->readonly) {
                    printf("Unable to create feed file '%s' as it is marked readonly\n",
                        feed->feed_filename);
                    exit(1);
                }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                /* 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_max_size < feed->feed_size)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                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];
    
                case CODEC_TYPE_AUDIO:
                case CODEC_TYPE_VIDEO:
    
                    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->time_base.num == 0){
                av->time_base.den = 5;
                av->time_base.num = 1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            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->nsse_weight)
    
                av->nsse_weight = 8;
    
            av->frame_skip_cmp = FF_CMP_DCTMAX;
            av->me_method = ME_EPZS;
            av->rc_buffer_aggressivity = 1.0;
    
    
            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_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;
            }
    
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        st = av_mallocz(sizeof(AVStream));
        if (!st)
            return;
    
        st->codec = avcodec_alloc_context();
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        stream->streams[stream->nb_streams++] = st;
    
        memcpy(st->codec, av, sizeof(AVCodecContext));
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    
    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;
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        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))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                p++;
            if (*p == '\0' || *p == '#')
                continue;
    
            get_arg(cmd, sizeof(cmd), &p);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            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)) {
    
                    fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                            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",
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                            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;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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 *));
    
    
                        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], 30+strlen(feed->filename),
                        "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 (ffserver_debug)
                    {
                        int j;
                        fprintf(stdout, "Launch commandline: ");
                        for (j = 0; j <= i; j++)
                            fprintf(stdout, "%s ", feed->child_argv[j]);
                        fprintf(stdout, "\n");
                    }
    
            } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
                if (feed) {
                    get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
                    feed->readonly = 1;
                } else if (stream) {
                    get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
                }
    
    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_t)fsize;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
            } 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;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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, "mjpeg");
    
                    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",
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                                filename, line_num, arg);
                        errors++;
                    }
                }
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
                }
    
            } else if (!strcasecmp(cmd, "InputFormat")) {
                stream->ifmt = av_find_input_format(arg);
                if (!stream->ifmt) {
    
                    fprintf(stderr, "%s:%d: Unknown input format: %s\n",
    
            } else if (!strcasecmp(cmd, "FaviconURL")) {