Skip to content
Snippets Groups Projects
ffserver.c 158 KiB
Newer Older
  • Learn to ignore specific revisions
  •     for(i = 0; i < stream->nb_streams; i++) {
    
            avc->streams[i] = &avs[i];
            avc->streams[i]->codec = stream->streams[i]->codec;
    
        *pbuffer = av_mallocz(2048);
    
        av_sdp_create(&avc, 1, *pbuffer, 2048);
    
        av_dict_free(&avc->metadata);
    
        av_free(avc);
    
    
        return strlen(*pbuffer);
    
    static void rtsp_cmd_options(HTTPContext *c, const char *url)
    {
    //    rtsp_reply_header(c, RTSP_STATUS_OK);
    
        avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
        avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
        avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
        avio_printf(c->pb, "\r\n");
    
    static void rtsp_cmd_describe(HTTPContext *c, const char *url)
    {
        FFStream *stream;
        char path1[1024];
        const char *path;
    
        uint8_t *content;
    
        int content_length;
        socklen_t len;
    
        /* find which URL is asked */
    
        av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
    
        path = path1;
        if (*path == '/')
            path++;
    
        for(stream = first_stream; stream != NULL; stream = stream->next) {
    
            if (!stream->is_feed &&
                stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
    
                !strcmp(path, stream->filename)) {
                goto found;
            }
        }
        /* no stream found */
        rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
        return;
    
     found:
    
        /* prepare the media description in SDP format */
    
    
        /* get the host IP */
        len = sizeof(my_addr);
        getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
        content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
    
        if (content_length < 0) {
            rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
            return;
        }
        rtsp_reply_header(c, RTSP_STATUS_OK);
    
        avio_printf(c->pb, "Content-Base: %s/\r\n", url);
        avio_printf(c->pb, "Content-Type: application/sdp\r\n");
        avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
        avio_printf(c->pb, "\r\n");
    
        avio_write(c->pb, content, content_length);
    
        av_free(content);
    
    }
    
    static HTTPContext *find_rtp_session(const char *session_id)
    {
        HTTPContext *c;
    
        if (session_id[0] == '\0')
            return NULL;
    
        for(c = first_http_ctx; c != NULL; c = c->next) {
            if (!strcmp(c->session_id, session_id))
                return c;
        }
        return NULL;
    }
    
    
    static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
    
    {
        RTSPTransportField *th;
        int i;
    
        for(i=0;i<h->nb_transports;i++) {
            th = &h->transports[i];
    
            if (th->lower_transport == lower_transport)
    
    static void rtsp_cmd_setup(HTTPContext *c, const char *url,
    
        int stream_index, rtp_port, rtcp_port;
    
        char buf[1024];
        char path1[1024];
        const char *path;
        HTTPContext *rtp_c;
        RTSPTransportField *th;
        struct sockaddr_in dest_addr;
        RTSPActionServerSetup setup;
    
        /* find which URL is asked */
    
        av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
    
        path = path1;
        if (*path == '/')
            path++;
    
        /* now check each stream */
        for(stream = first_stream; stream != NULL; stream = stream->next) {
    
            if (!stream->is_feed &&
                stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
    
                /* accept aggregate filenames only if single stream */
                if (!strcmp(path, stream->filename)) {
                    if (stream->nb_streams != 1) {
                        rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
                        return;
                    }
                    stream_index = 0;
                    goto found;
                }
    
                for(stream_index = 0; stream_index < stream->nb_streams;
                    stream_index++) {
    
                    snprintf(buf, sizeof(buf), "%s/streamid=%d",
    
                             stream->filename, stream_index);
                    if (!strcmp(path, buf))
                        goto found;
                }
            }
        }
        /* no stream found */
        rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
        return;
     found:
    
        /* generate session id if needed */
    
        if (h->session_id[0] == '\0') {
            unsigned random0 = av_lfg_get(&random_state);
            unsigned random1 = av_lfg_get(&random_state);
    
            snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
    
        /* find RTP session, and create it if none found */
    
        rtp_c = find_rtp_session(h->session_id);
        if (!rtp_c) {
    
            /* always prefer UDP */
    
            th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
    
            if (!th) {
    
                th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
    
                if (!th) {
                    rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
                    return;
                }
            }
    
            rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
    
            if (!rtp_c) {
                rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
                return;
            }
    
            /* open input stream */
            if (open_input_stream(rtp_c, "") < 0) {
                rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
                return;
            }
        }
    
        /* test if stream is OK (test needed because several SETUP needs
           to be done for a given file) */
        if (rtp_c->stream != stream) {
            rtsp_reply_error(c, RTSP_STATUS_SERVICE);
            return;
        }
    
        /* test if stream is already set up */
        if (rtp_c->rtp_ctx[stream_index]) {
            rtsp_reply_error(c, RTSP_STATUS_STATE);
            return;
        }
    
        /* check transport */
        th = find_transport(h, rtp_c->rtp_protocol);
    
        if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
    
                    th->client_port_min <= 0)) {
            rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
            return;
        }
    
        /* setup default options */
        setup.transport_option[0] = '\0';
        dest_addr = rtp_c->from_addr;
        dest_addr.sin_port = htons(th->client_port_min);
    
        if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
    
            rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
            return;
        }
    
        /* now everything is OK, so we can send the connection parameters */
        rtsp_reply_header(c, RTSP_STATUS_OK);
        /* session ID */
    
        avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
    
            rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
            rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
    
            avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
    
                        th->client_port_min, th->client_port_max,
                        rtp_port, rtcp_port);
    
            avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
    
                        stream_index * 2, stream_index * 2 + 1);
            break;
        default:
            break;
        }
    
        if (setup.transport_option[0] != '\0')
    
            avio_printf(c->pb, ";%s", setup.transport_option);
        avio_printf(c->pb, "\r\n");
    
        avio_printf(c->pb, "\r\n");
    
    /* find an RTP connection by using the session ID. Check consistency
    
    static HTTPContext *find_rtp_session_with_url(const char *url,
    
                                                  const char *session_id)
    {
        HTTPContext *rtp_c;
        char path1[1024];
        const char *path;
    
        char buf[1024];
    
    
        rtp_c = find_rtp_session(session_id);
        if (!rtp_c)
            return NULL;
    
    
        /* find which URL is asked */
    
        av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
    
        if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
        for(s=0; s<rtp_c->stream->nb_streams; ++s) {
          snprintf(buf, sizeof(buf), "%s/streamid=%d",
            rtp_c->stream->filename, s);
          if(!strncmp(path, buf, sizeof(buf))) {
        // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
            return rtp_c;
          }
        }
    
        len = strlen(path);
        if (len > 0 && path[len - 1] == '/' &&
            !strncmp(path, rtp_c->stream->filename, len - 1))
            return rtp_c;
    
        return NULL;
    
    static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *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;
        }
    
        if (rtp_c->state != HTTPSTATE_SEND_DATA &&
            rtp_c->state != HTTPSTATE_WAIT_FEED &&
            rtp_c->state != HTTPSTATE_READY) {
            rtsp_reply_error(c, RTSP_STATUS_STATE);
            return;
        }
    
        rtp_c->state = HTTPSTATE_SEND_DATA;
    
        /* now everything is OK, so we can send the connection parameters */
        rtsp_reply_header(c, RTSP_STATUS_OK);
        /* session ID */
    
        avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
        avio_printf(c->pb, "\r\n");
    
    static void rtsp_cmd_interrupt(HTTPContext *c, const char *url, RTSPMessageHeader *h, int pause_only)
    
    {
        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;
        }
    
        if (pause_only) {
            if (rtp_c->state != HTTPSTATE_SEND_DATA &&
                rtp_c->state != HTTPSTATE_WAIT_FEED) {
                rtsp_reply_error(c, RTSP_STATUS_STATE);
                return;
            }
            rtp_c->state = HTTPSTATE_READY;
            rtp_c->first_pts = AV_NOPTS_VALUE;
    
        /* now everything is OK, so we can send the connection parameters */
        rtsp_reply_header(c, RTSP_STATUS_OK);
        /* session ID */
    
        avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
    
        avio_printf(c->pb, "\r\n");
    
        if (!pause_only)
            close_connection(rtp_c);
    
    }
    
    /********************************************************************/
    /* RTP handling */
    
    
    static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
    
                                           FFStream *stream, const char *session_id,
    
        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) {
    
            proto_str = "MCAST";
            break;
    
            proto_str = "UDP";
            break;
    
            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 = av_guess_format("rtp", NULL, NULL);
    
    
        st = av_mallocz(sizeof(AVStream));
        if (!st)
            goto fail;
        ctx->nb_streams = 1;
    
        ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
    
    Mike William's avatar
    Mike William committed
        if (!ctx->streams)
    
        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_LOWER_TRANSPORT_UDP:
        case RTSP_LOWER_TRANSPORT_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 (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
    
            max_packet_size = h->max_packet_size;
    
            /* RTP/TCP case */
            c->rtsp_c = rtsp_c;
            max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
            break;
        default:
    
        http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
    
                 ipaddr, ntohs(dest_addr->sin_port),
    
                 c->stream->filename, stream_index, c->protocol);
    
        /* normally, no packets should be output here, but the packet size may
         * be checked */
    
        if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
    
        if (avformat_write_header(ctx, NULL) < 0) {
    
                ffurl_close(h);
    
        avio_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, int copy)
    
        if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
            return NULL;
    
    
        fst = av_mallocz(sizeof(AVStream));
        if (!fst)
            return NULL;
    
        if (copy) {
    
            fst->codec = avcodec_alloc_context3(NULL);
    
            memcpy(fst->codec, codec, sizeof(AVCodecContext));
            if (codec->extradata_size) {
    
                fst->codec->extradata = av_mallocz(codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
    
                memcpy(fst->codec->extradata, codec->extradata,
                    codec->extradata_size);
            }
        } else {
            /* live streams must use the actual feed's codec since it may be
    
             * updated later to carry extradata needed by them.
    
             */
            fst->codec = codec;
        }
    
        fst->priv_data = av_mallocz(sizeof(FeedData));
    
        avpriv_set_pts_info(fst, 33, 1, 90000);
    
        fst->sample_aspect_ratio = codec->sample_aspect_ratio;
    
        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) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    if (av1->channels == av->channels &&
                        av1->sample_rate == av->sample_rate)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
            }
        }
    
        fst = add_av_stream1(feed, av, 0);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (!fst)
            return -1;
        return feed->nb_streams - 1;
    }
    
    
    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;
    
        infile->flags |= AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE;
    
    
        mpeg4_count = 0;
        for(i=0;i<infile->nb_streams;i++) {
            st = infile->streams[i];
    
            if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
    
        printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
    
            if (av_read_frame(infile, &pkt) < 0)
    
                break;
            st = infile->streams[pkt.stream_index];
    
            if (st->codec->codec_id == AV_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_mallocz(size + FF_INPUT_BUFFER_PADDING_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)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        int i, ret;
    
    
        /* gather all streams */
        for(stream = first_stream; stream != NULL; stream = stream_next) {
    
            AVFormatContext *infile = NULL;
    
            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 (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
    
                    /* specific case : if transport stream output to RTP,
                       we use a raw transport stream reader */
    
                    av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
    
                if (!stream->feed_filename[0]) {
                    http_log("Unspecified feed file for stream '%s'\n", stream->filename);
                    goto fail;
                }
    
                http_log("Opening feed file '%s' for stream '%s'\n", stream->feed_filename, stream->filename);
    
                if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
    
                    http_log("Could not open '%s': %s\n", stream->feed_filename, av_err2str(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 (avformat_find_stream_info(infile, NULL) < 0) {
    
                        http_log("Could not find codec parameters from '%s'\n",
    
                    for(i=0;i<infile->nb_streams;i++)
    
                        add_av_stream1(stream, infile->streams[i]->codec, 1);
    
    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) {
    
                    for(i=0;i<stream->nb_streams;i++)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        stream->feed_streams[i] = i;
    
                } else {
                    /* we handle a stream coming from a feed */
                    for(i=0;i<stream->nb_streams;i++)
                        stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
            }
        }
    
        /* create feed files if needed */
        for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
            int fd;
    
    
            if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
    
                AVFormatContext *s = NULL;
    
                if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
    
                    /* set buffer size */
                    ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
    
                    /* 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) {
    
                                http_log("Index & Id do not match for stream %d (%s)\n",
    
    #define CHECK_CODEC(x)  (ccf->x != ccs->x)
    
    
                                if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
    
                                    http_log("Codecs do not match for stream %d\n", i);
    
                                    matches = 0;
                                } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
    
                                    http_log("Codec bitrates do not match for stream %d\n", i);
    
                                } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
    
                                    if (CHECK_CODEC(time_base.den) ||
                                        CHECK_CODEC(time_base.num) ||
    
                                        CHECK_CODEC(width) ||
                                        CHECK_CODEC(height)) {
    
                                        http_log("Codec width, height and framerate do not match for stream %d\n", i);
    
                                } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
    
                                    if (CHECK_CODEC(sample_rate) ||
                                        CHECK_CODEC(channels) ||
                                        CHECK_CODEC(frame_size)) {
    
                                        http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
    
                                    http_log("Unknown codec type\n");
    
                        http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
    
                            feed->feed_filename, s->nb_streams, feed->nb_streams);
    
    
                    http_log("Deleting feed file '%s' as it appears to be corrupt\n",
    
                        http_log("Unable to delete feed file '%s' as it is marked readonly\n",
    
            if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
    
                AVFormatContext *s = avformat_alloc_context();
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
                    http_log("Unable to create feed file '%s' as it is marked readonly\n",
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                /* only write the header of the ffm file */
    
                if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 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;
    
    Mike William's avatar
    Mike William committed
                s->streams = feed->streams;
    
                if (avformat_write_header(s, NULL) < 0) {
    
                    http_log("Container doesn't support the required parameters\n");
    
                /* XXX: need better API */
    
                av_freep(&s->priv_data);
    
                s->streams = NULL;
                s->nb_streams = 0;
                avformat_free_context(s);
    
    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 = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            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)
    {
    
        unsigned bandwidth;
        int i;
    
        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 AVMEDIA_TYPE_AUDIO:
                case AVMEDIA_TYPE_VIDEO:
    
                    break;
                default:
                    break;
                }
            }
            stream->bandwidth = (bandwidth + 999) / 1000;
        }
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* 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;
    
    
        if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* compute default parameters */
        switch(av->codec_type) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            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;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            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;
    
            if (!av->me_method)
    
    Martin Storsjö's avatar
    Martin Storsjö committed
                av->me_method = ME_EPZS;
    
                av->rc_eq = av_strdup("tex^qComp");
    
            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_context3(NULL);
    
    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 enum AVCodecID opt_codec(const char *name, enum AVMediaType type)
    
        AVCodec *codec = avcodec_find_encoder_by_name(name);
    
        if (!codec || codec->type != type)
    
            return AV_CODEC_ID_NONE;
    
        return codec->id;
    
    static int ffserver_opt_default(const char *opt, const char *arg,
    
                           AVCodecContext *avctx, int type)
    {
    
        const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
    
            ret = av_opt_set(avctx, opt, arg, 0);
    
    static int ffserver_opt_preset(const char *arg,
                           AVCodecContext *avctx, int type,
    
                           enum AVCodecID *audio_id, enum AVCodecID *video_id)
    
    {
        FILE *f=NULL;
        char filename[1000], tmp[1000], tmp2[1000], line[1000];
    
        int ret = 0;
        AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
    
        if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
                                  codec ? codec->name : NULL))) {
    
            fprintf(stderr, "File for preset '%s' not found\n", arg);
            return 1;
        }
    
        while(!feof(f)){
            int e= fscanf(f, "%999[^\n]\n", line) - 1;
            if(line[0] == '#' && !e)
                continue;
            e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
            if(e){
                fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
                ret = 1;
                break;
            }
            if(!strcmp(tmp, "acodec")){
    
                *audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO);
    
            }else if(!strcmp(tmp, "vcodec")){
    
                *video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO);
    
            }else if(!strcmp(tmp, "scodec")){
                /* opt_subtitle_codec(tmp2); */
            }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
                fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
                ret = 1;
                break;
            }
        }