Skip to content
Snippets Groups Projects
ffserver.c 149 KiB
Newer Older
  • Learn to ignore specific revisions
  •     /* abort the session */
        close_connection(rtp_c);
    
        /* now everything is OK, so we can send the connection parameters */
        rtsp_reply_header(c, RTSP_STATUS_OK);
        /* session ID */
    
    Luca Abeni's avatar
    Luca Abeni committed
        url_fprintf(c->pb, "Session: %s\r\n", 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;
    
        av_strlcpy(c->session_id, session_id, sizeof(c->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;
        }
    
        av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
        av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
    
        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)
    
        URLContext *h = NULL;
    
        uint8_t *dummy_buf;
    
        int max_packet_size;
    
        /* now we can open the relevant output stream */
    
        ctx->oformat = guess_format("rtp", NULL, NULL);
    
        st->codec= avcodec_alloc_context();
    
        if (!c->stream->feed ||
    
            c->stream->feed == c->stream)
    
            memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
    
                   c->stream->feed->streams[c->stream->feed_streams[stream_index]],
                   sizeof(AVStream));
    
        st->priv_data = NULL;
    
        /* 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);
    
        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));
    
        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) {
    
    /* 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_log(infile, AV_LOG_DEBUG, 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;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        int i, ret;
    
    
        /* 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 && !strcmp(stream->fmt->name, "rtp")) {
    
                    /* 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;
                }
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                if ((ret = av_open_input_file(&infile, stream->feed_filename,
                                              stream->ifmt, 0, stream->ap_in)) < 0) {
                    http_log("could not open %s: %d\n", stream->feed_filename, ret);
    
                    /* 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'\n",
    
                                 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);
    
    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 */
    
                    for(i=0;i<stream->nb_streams;i++)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        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) {
    
                    for(i=0;i<stream->nb_streams;i++)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        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;
                                }
                            }
    
                        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);
    
                    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) {
    
                    http_log("Could not open output feed file '%s'\n",
                             feed->feed_filename);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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);
    
                    http_log("Container doesn't supports the required parameters\n");
    
                /* XXX: need better api */
                av_freep(&s->priv_data);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
            /* get feed size and write index */
            fd = open(feed->feed_filename, O_RDONLY);
            if (fd < 0) {
    
                http_log("Could not open output feed file '%s'\n",
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        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 = 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;
            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= avcodec_find_encoder_by_name(arg);
    
        if (p == NULL || p->type != CODEC_TYPE_AUDIO)
    
    static int opt_video_codec(const char *arg)
    
        AVCodec *p= avcodec_find_encoder_by_name(arg);
    
        if (p == NULL || p->type != CODEC_TYPE_VIDEO)
    
    static 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 opt_default(const char *opt, const char *arg,
                           AVCodecContext *avctx, int type)
    {
        const AVOption *o  = NULL;
        const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
        if(o2)
            o = av_set_string(avctx, opt, arg);
        if(!o)
            return -1;
        return 0;
    }
    
    
    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);
    
                val = atoi(arg);
                if (val < 1 || val > 65536) {
                    fprintf(stderr, "%s:%d: Invalid port: %s\n",
                            filename, line_num, arg);
                    errors++;
                }
                my_http_addr.sin_port = htons(val);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "BindAddress")) {
                get_arg(arg, sizeof(arg), &p);
    
                if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
                    fprintf(stderr, "%s:%d: Invalid host/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);
    
                val = atoi(arg);
                if (val < 1 || val > 65536) {
                    fprintf(stderr, "%s:%d: Invalid port: %s\n",
                            filename, line_num, arg);
                    errors++;
                }
                my_rtsp_addr.sin_port = htons(atoi(arg));
    
            } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
                get_arg(arg, sizeof(arg), &p);
    
                if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
                    fprintf(stderr, "%s:%d: Invalid host/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);
    
                llval = atoll(arg);
                if (llval < 10 || llval > 10000000) {
    
                    fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
    
                            filename, line_num, arg);
                    errors++;
    
    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 = av_mallocz(64 * sizeof(char *));
    
    Alex Beregszaszi's avatar
    Alex Beregszaszi committed
                        get_arg(arg, sizeof(arg), &p);
                        if (!arg[0])
    
    Alex Beregszaszi's avatar
    Alex Beregszaszi committed
                        feed->child_argv[i] = av_strdup(arg);
    
                    }
    
                    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);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
            } else if (!strcasecmp(cmd, "FileMaxSize")) {
                if (feed) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    double fsize;
    
                    get_arg(arg, sizeof(arg), &p);
                    p1 = arg;
    
                    fsize = strtod(p1, &p1);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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++;
                }
                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 {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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);
    
                    /* fetch avclass so AVOption works
                     * FIXME try to use avcodec_get_context_defaults2
                     * without changing defaults too much */
                    avcodec_get_context_defaults(&video_enc);
                    class = video_enc.av_class;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    memset(&audio_enc, 0, sizeof(AVCodecContext));
                    memset(&video_enc, 0, sizeof(AVCodecContext));
    
                    audio_enc.av_class = class;
                    video_enc.av_class = class;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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;
                    }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        fprintf(stderr, "%s:%d: feed '%s' not defined\n",
                                filename, line_num, arg);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        stream->feed = sfeed;
                }
            } else if (!strcasecmp(cmd, "Format")) {
                get_arg(arg, sizeof(arg), &p);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                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")) {
    
                get_arg(arg, sizeof(arg), &p);
    
                stream->ifmt = av_find_input_format(arg);
                if (!stream->ifmt) {
    
                    fprintf(stderr, "%s:%d: Unknown input format: %s\n",
    
            } 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")) {
    
                    get_arg(stream->author, sizeof(stream->author), &p);
            } else if (!strcasecmp(cmd, "Comment")) {
    
                    get_arg(stream->comment, sizeof(stream->comment), &p);
            } else if (!strcasecmp(cmd, "Copyright")) {
    
                    get_arg(stream->copyright, sizeof(stream->copyright), &p);
            } else if (!strcasecmp(cmd, "Title")) {
    
                    get_arg(stream->title, sizeof(stream->title), &p);
    
            } else if (!strcasecmp(cmd, "Preroll")) {
                get_arg(arg, sizeof(arg), &p);
    
                    stream->prebuffer = atof(arg) * 1000;
    
            } else if (!strcasecmp(cmd, "StartSendOnKey")) {
    
            } else if (!strcasecmp(cmd, "AudioCodec")) {
                get_arg(arg, sizeof(arg), &p);
                audio_id = opt_audio_codec(arg);
                if (audio_id == CODEC_ID_NONE) {