Skip to content
Snippets Groups Projects
utils.c 94.4 KiB
Newer Older
  • Learn to ignore specific revisions
  •            decompress the frame. We try to avoid that in most cases as
               it takes longer and uses more memory. For MPEG4, we need to
               decompress for Quicktime. */
    
            if (!has_codec_parameters(st->codec) /*&&
                (st->codec->codec_id == CODEC_ID_FLV1 ||
                 st->codec->codec_id == CODEC_ID_H264 ||
                 st->codec->codec_id == CODEC_ID_H263 ||
                 st->codec->codec_id == CODEC_ID_H261 ||
                 st->codec->codec_id == CODEC_ID_VORBIS ||
                 st->codec->codec_id == CODEC_ID_MJPEG ||
                 st->codec->codec_id == CODEC_ID_PNG ||
                 st->codec->codec_id == CODEC_ID_PAM ||
                 st->codec->codec_id == CODEC_ID_PGM ||
                 st->codec->codec_id == CODEC_ID_PGMYUV ||
                 st->codec->codec_id == CODEC_ID_PBM ||
                 st->codec->codec_id == CODEC_ID_PPM ||
                 st->codec->codec_id == CODEC_ID_SHORTEN ||
                 (st->codec->codec_id == CODEC_ID_MPEG4 && !st->need_parsing))*/)
    
                try_decode_frame(st, pkt->data, pkt->size);
    
            if (av_rescale_q(st->codec_info_duration, st->time_base, AV_TIME_BASE_Q) >= MAX_STREAM_DURATION) {
    
        // close codecs which where opened in try_decode_frame()
        for(i=0;i<ic->nb_streams;i++) {
            st = ic->streams[i];
    
            if(st->codec->codec)
                avcodec_close(st->codec);
    
            if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
                if(st->codec->codec_id == CODEC_ID_RAWVIDEO && !st->codec->codec_tag && !st->codec->bits_per_sample)
                    st->codec->codec_tag= avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt);
    
                if(duration_count[i] && st->codec->time_base.num*101LL <= st->codec->time_base.den &&
                   st->time_base.num*duration_sum[i]/duration_count[i]*101LL > st->time_base.den){
                    int64_t num, den, error, best_error;
    
                    num= st->time_base.den*duration_count[i];
                    den= st->time_base.num*duration_sum[i];
    
                    best_error= INT64_MAX;
                    for(j=1; j<60*12; j++){
                        error= ABS(1001*12*num - 1001*j*den);
                        if(error < best_error){
                            best_error= error;
                            av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, j, 12, INT_MAX);
                        }
                    }
                    for(j=24; j<=30; j+=6){
                        error= ABS(1001*12*num - 1000*12*j*den);
                        if(error < best_error){
                            best_error= error;
                            av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, j*1000, 1001, INT_MAX);
                        }
    
                /* set real frame rate info */
    
                /* compute the real frame rate for telecine */
    
                if ((st->codec->codec_id == CODEC_ID_MPEG1VIDEO ||
                     st->codec->codec_id == CODEC_ID_MPEG2VIDEO) &&
                    st->codec->sub_id == 2) {
    
                    if (st->codec_info_nb_frames >= 20) {
                        float coded_frame_rate, est_frame_rate;
    
                        est_frame_rate = ((double)st->codec_info_nb_frames * AV_TIME_BASE) /
    
                            (double)st->codec_info_duration ;
    
                        coded_frame_rate = 1.0/av_q2d(st->codec->time_base);
    
                        printf("telecine: coded_frame_rate=%0.3f est_frame_rate=%0.3f\n",
    
                               coded_frame_rate, est_frame_rate);
    #endif
                        /* if we detect that it could be a telecine, we
                           signal it. It would be better to do it at a
                           higher level as it can change in a film */
    
                        if (coded_frame_rate >= 24.97 &&
    
                            (est_frame_rate >= 23.5 && est_frame_rate < 24.5)) {
    
                            st->r_frame_rate = (AVRational){24000, 1001};
    
                        }
                    }
                }
                /* if no real frame rate, use the codec one */
    
                if (!st->r_frame_rate.num){
    
                    st->r_frame_rate.num = st->codec->time_base.den;
                    st->r_frame_rate.den = st->codec->time_base.num;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
    #if 0
        /* correct DTS for b frame streams with no timestamps */
        for(i=0;i<ic->nb_streams;i++) {
            st = ic->streams[i];
    
            if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
    
                if(b-frames){
                    ppktl = &ic->packet_buffer;
                    while(ppkt1){
                        if(ppkt1->stream_index != i)
                            continue;
                        if(ppkt1->pkt->dts < 0)
                            break;
                        if(ppkt1->pkt->pts != AV_NOPTS_VALUE)
                            break;
                        ppkt1->pkt->dts -= delta;
                        ppkt1= ppkt1->next;
                    }
                    if(ppkt1)
                        continue;
                    st->cur_dts -= delta;
                }
            }
        }
    #endif
    
    /*******************************************************/
    
    /**
     * start playing a network based stream (e.g. RTSP stream) at the
    
     * current position
    
     */
    int av_read_play(AVFormatContext *s)
    {
        if (!s->iformat->read_play)
            return AVERROR_NOTSUPP;
        return s->iformat->read_play(s);
    }
    
    /**
    
     * Pause a network based stream (e.g. RTSP stream).
     *
     * Use av_read_play() to resume it.
    
     */
    int av_read_pause(AVFormatContext *s)
    {
        if (!s->iformat->read_pause)
            return AVERROR_NOTSUPP;
        return s->iformat->read_pause(s);
    }
    
    
     * Close a media file (but not its codecs).
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    void av_close_input_file(AVFormatContext *s)
    {
    
        int i, must_open_file;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        /* free previous packet */
        if (s->cur_st && s->cur_st->parser)
    
            av_free_packet(&s->cur_pkt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        for(i=0;i<s->nb_streams;i++) {
    
            /* free all data in a stream component */
            st = s->streams[i];
    
            if (st->parser) {
                av_parser_close(st->parser);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
    
            av_free(st->index_entries);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        must_open_file = 1;
    
        if (s->iformat->flags & AVFMT_NOFILE) {
    
            must_open_file = 0;
        }
        if (must_open_file) {
    
            url_fclose(&s->pb);
        }
    
        av_free(s);
    
     * Can only be called in the read_header() function. If the flag
     * AVFMTCTX_NOHEADER is in the format context, then new streams
     * can be added in read_packet too.
    
     * @param id file format dependent stream id
    
     */
    AVStream *av_new_stream(AVFormatContext *s, int id)
    {
        AVStream *st;
    
        if (s->nb_streams >= MAX_STREAMS)
            return NULL;
    
        st = av_mallocz(sizeof(AVStream));
        if (!st)
            return NULL;
    
        if (s->iformat) {
            /* no default bitrate if decoding */
    
        st->start_time = AV_NOPTS_VALUE;
        st->duration = AV_NOPTS_VALUE;
    
        st->cur_dts = AV_NOPTS_VALUE;
    
    
        /* default pts settings is MPEG like */
        av_set_pts_info(st, 33, 1, 90000);
    
        st->last_IP_pts = AV_NOPTS_VALUE;
    
        s->streams[s->nb_streams++] = st;
        return st;
    }
    
    /************************************************************/
    /* output media file */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
    int av_set_parameters(AVFormatContext *s, AVFormatParameters *ap)
    {
        int ret;
    
        if (s->oformat->priv_data_size > 0) {
            s->priv_data = av_mallocz(s->oformat->priv_data_size);
            if (!s->priv_data)
                return AVERROR_NOMEM;
        } else
            s->priv_data = NULL;
    
        if (s->oformat->set_parameters) {
            ret = s->oformat->set_parameters(s, ap);
            if (ret < 0)
                return ret;
        }
        return 0;
    }
    
    
    /**
     * allocate the stream private data and write the stream header to an
     * output media file
     *
     * @param s media file handle
    
     * @return 0 if OK. AVERROR_xxx if error.
    
        // some sanity checks
        for(i=0;i<s->nb_streams;i++) {
            st = s->streams[i];
    
            switch (st->codec->codec_type) {
            case CODEC_TYPE_AUDIO:
                if(st->codec->sample_rate<=0){
                    av_log(s, AV_LOG_ERROR, "sample rate not set\n");
                    return -1;
                }
                break;
            case CODEC_TYPE_VIDEO:
                if(st->codec->time_base.num<=0 || st->codec->time_base.den<=0){ //FIXME audio too?
                    av_log(s, AV_LOG_ERROR, "time base not set\n");
                    return -1;
                }
                if(st->codec->width<=0 || st->codec->height<=0){
                    av_log(s, AV_LOG_ERROR, "dimensions not set\n");
                    return -1;
                }
                break;
            }
        }
    
    
        if(s->oformat->write_header){
            ret = s->oformat->write_header(s);
            if (ret < 0)
                return ret;
        }
    
    
        /* init PTS generation */
        for(i=0;i<s->nb_streams;i++) {
    
            case CODEC_TYPE_AUDIO:
    
                den = (int64_t)st->time_base.num * st->codec->sample_rate;
    
                break;
            case CODEC_TYPE_VIDEO:
    
                den = (int64_t)st->time_base.num * st->codec->time_base.den;
    
            if (den != AV_NOPTS_VALUE) {
                if (den <= 0)
                    return AVERROR_INVALIDDATA;
                av_frac_init(&st->pts, 0, 0, den);
            }
    
    //FIXME merge with compute_pkt_fields
    
    static int compute_pkt_fields2(AVStream *st, AVPacket *pkt){
    
        int b_frames = FFMAX(st->codec->has_b_frames, st->codec->max_b_frames);
    
        int num, den, frame_size;
    
    //    av_log(NULL, AV_LOG_DEBUG, "av_write_frame: pts:%lld dts:%lld cur_dts:%lld b:%d size:%d st:%d\n", pkt->pts, pkt->dts, st->cur_dts, b_frames, pkt->size, pkt->stream_index);
    
    /*    if(pkt->pts == AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE)
            return -1;*/
    
        /* duration field */
    
        if (pkt->duration == 0) {
            compute_frame_duration(&num, &den, st, NULL, pkt);
            if (den && num) {
                pkt->duration = av_rescale(1, num * (int64_t)st->time_base.den, den * (int64_t)st->time_base.num);
            }
        }
    
    
        //XXX/FIXME this is a temporary hack until all encoders output pts
        if((pkt->pts == 0 || pkt->pts == AV_NOPTS_VALUE) && pkt->dts == AV_NOPTS_VALUE && !b_frames){
            pkt->dts=
    //        pkt->pts= st->cur_dts;
            pkt->pts= st->pts.val;
        }
    
    
        //calculate dts from pts
    
        if(pkt->pts != AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE){
            if(b_frames){
                if(st->last_IP_pts == AV_NOPTS_VALUE){
    
                    st->last_IP_pts= -pkt->duration;
    
                }
                if(st->last_IP_pts < pkt->pts){
                    pkt->dts= st->last_IP_pts;
                    st->last_IP_pts= pkt->pts;
                }else
                    pkt->dts= pkt->pts;
            }else
                pkt->dts= pkt->pts;
        }
    
        if(st->cur_dts && st->cur_dts != AV_NOPTS_VALUE && st->cur_dts >= pkt->dts){
    
            av_log(NULL, AV_LOG_ERROR, "error, non monotone timestamps %"PRId64" >= %"PRId64"\n", st->cur_dts, pkt->dts);
    
            return -1;
        }
        if(pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->pts < pkt->dts){
            av_log(NULL, AV_LOG_ERROR, "error, pts < dts\n");
            return -1;
        }
    
    
    //    av_log(NULL, AV_LOG_DEBUG, "av_write_frame: pts2:%lld dts2:%lld\n", pkt->pts, pkt->dts);
    
        st->cur_dts= pkt->dts;
        st->pts.val= pkt->dts;
    
    
        case CODEC_TYPE_AUDIO:
    
            frame_size = get_audio_frame_size(st->codec, pkt->size);
    
            /* HACK/FIXME, we skip the initial 0-size packets as they are most likely equal to the encoder delay,
    
               but it would be better if we had the real timestamps from the encoder */
    
            if (frame_size >= 0 && (pkt->size || st->pts.num!=st->pts.den>>1 || st->pts.val)) {
    
                av_frac_add(&st->pts, (int64_t)st->time_base.den * frame_size);
    
            break;
        case CODEC_TYPE_VIDEO:
    
            av_frac_add(&st->pts, (int64_t)st->time_base.den * st->codec->time_base.num);
    
    }
    
    static void truncate_ts(AVStream *st, AVPacket *pkt){
        int64_t pts_mask = (2LL << (st->pts_wrap_bits-1)) - 1;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
    //    if(pkt->dts < 0)
    //        pkt->dts= 0;  //this happens for low_delay=0 and b frames, FIXME, needs further invstigation about what we should do here
    
        pkt->pts &= pts_mask;
        pkt->dts &= pts_mask;
    }
    
    /**
    
     * Write a packet to an output media file.
     *
     * The packet shall contain one audio or video frame.
    
     *
     * @param s media file handle
     * @param pkt the packet, which contains the stream_index, buf/buf_size, dts/pts, ...
     * @return < 0 if error, = 0 if OK, 1 if end of stream wanted.
     */
    int av_write_frame(AVFormatContext *s, AVPacket *pkt)
    {
    
        ret=compute_pkt_fields2(s->streams[pkt->stream_index], pkt);
        if(ret<0)
            return ret;
    
        truncate_ts(s->streams[pkt->stream_index], pkt);
    
    
        ret= s->oformat->write_packet(s, pkt);
        if(!ret)
            ret= url_ferror(&s->pb);
        return ret;
    
    /**
     * interleave_packet implementation which will interleave per DTS.
    
     * packets with pkt->destruct == av_destruct_packet will be freed inside this function.
    
     * so they cannot be used after it, note calling av_free_packet() on them is still safe
    
     */
    static int av_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush){
        AVPacketList *pktl, **next_point, *this_pktl;
        int stream_count=0;
        int streams[MAX_STREAMS];
    
        if(pkt){
            AVStream *st= s->streams[ pkt->stream_index];
    
    
    //        assert(pkt->destruct != av_destruct_packet); //FIXME
    
    
            this_pktl = av_mallocz(sizeof(AVPacketList));
            this_pktl->pkt= *pkt;
    
            if(pkt->destruct == av_destruct_packet)
                pkt->destruct= NULL; // non shared -> must keep original from being freed
            else
                av_dup_packet(&this_pktl->pkt);  //shared -> must dup
    
    
            next_point = &s->packet_buffer;
            while(*next_point){
                AVStream *st2= s->streams[ (*next_point)->pkt.stream_index];
                int64_t left=  st2->time_base.num * (int64_t)st ->time_base.den;
                int64_t right= st ->time_base.num * (int64_t)st2->time_base.den;
                if((*next_point)->pkt.dts * left > pkt->dts * right) //FIXME this can overflow
                    break;
                next_point= &(*next_point)->next;
            }
            this_pktl->next= *next_point;
            *next_point= this_pktl;
        }
    
        memset(streams, 0, sizeof(streams));
        pktl= s->packet_buffer;
        while(pktl){
    //av_log(s, AV_LOG_DEBUG, "show st:%d dts:%lld\n", pktl->pkt.stream_index, pktl->pkt.dts);
            if(streams[ pktl->pkt.stream_index ] == 0)
                stream_count++;
            streams[ pktl->pkt.stream_index ]++;
            pktl= pktl->next;
        }
    
        if(s->nb_streams == stream_count || (flush && stream_count)){
            pktl= s->packet_buffer;
            *out= pktl->pkt;
    
    
            s->packet_buffer= pktl->next;
    
            av_freep(&pktl);
            return 1;
        }else{
            av_init_packet(out);
            return 0;
        }
    }
    
    /**
     * Interleaves a AVPacket correctly so it can be muxed.
     * @param out the interleaved packet will be output here
     * @param in the input packet
     * @param flush 1 if no further packets are available as input and all
     *              remaining packets should be output
    
     * @return 1 if a packet was output, 0 if no packet could be output,
    
     *         < 0 if an error occured
     */
    static int av_interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush){
        if(s->oformat->interleave_packet)
            return s->oformat->interleave_packet(s, out, in, flush);
        else
            return av_interleave_packet_per_dts(s, out, in, flush);
    }
    
    
     * Writes a packet to an output media file ensuring correct interleaving.
     *
     * The packet must contain one audio or video frame.
    
     * If the packets are already correctly interleaved the application should
     * call av_write_frame() instead as its slightly faster, its also important
    
     * to keep in mind that completly non interleaved input will need huge amounts
    
     * of memory to interleave with this, so its prefereable to interleave at the
     * demuxer level
     *
     * @param s media file handle
     * @param pkt the packet, which contains the stream_index, buf/buf_size, dts/pts, ...
     * @return < 0 if error, = 0 if OK, 1 if end of stream wanted.
     */
    int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt){
        AVStream *st= s->streams[ pkt->stream_index];
    
    
        //FIXME/XXX/HACK drop zero sized packets
    
        if(st->codec->codec_type == CODEC_TYPE_AUDIO && pkt->size==0)
    
    
    //av_log(NULL, AV_LOG_DEBUG, "av_interleaved_write_frame %d %Ld %Ld\n", pkt->size, pkt->dts, pkt->pts);
        if(compute_pkt_fields2(st, pkt) < 0)
            return -1;
    
        if(pkt->dts == AV_NOPTS_VALUE)
            return -1;
    
    
        for(;;){
            AVPacket opkt;
            int ret= av_interleave_packet(s, &opkt, pkt, 0);
            if(ret<=0) //FIXME cleanup needed for ret<0 ?
                return ret;
    
            truncate_ts(s->streams[opkt.stream_index], &opkt);
            ret= s->oformat->write_packet(s, &opkt);
    
            if(url_ferror(&s->pb))
                return url_ferror(&s->pb);
    
     * @brief Write the stream trailer to an output media file and
     *        free the file private data.
    
        for(;;){
            AVPacket pkt;
            ret= av_interleave_packet(s, &pkt, NULL, 1);
            if(ret<0) //FIXME cleanup needed for ret<0 ?
    
            truncate_ts(s->streams[pkt.stream_index], &pkt);
            ret= s->oformat->write_packet(s, &pkt);
    
            if(url_ferror(&s->pb))
                goto fail;
    
        if(s->oformat->write_trailer)
            ret = s->oformat->write_trailer(s);
    
        if(ret == 0)
           ret=url_ferror(&s->pb);
    
        for(i=0;i<s->nb_streams;i++)
            av_freep(&s->streams[i]->priv_data);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    /* "user interface" functions */
    
    void dump_format(AVFormatContext *ic,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                     const char *url,
                     int is_output)
    {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        char buf[256];
    
    
        av_log(NULL, AV_LOG_INFO, "%s #%d, %s, %s '%s':\n",
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                is_output ? "Output" : "Input",
    
                index,
                is_output ? ic->oformat->name : ic->iformat->name,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                is_output ? "to" : "from", url);
    
            av_log(NULL, AV_LOG_INFO, "  Duration: ");
    
            if (ic->duration != AV_NOPTS_VALUE) {
                int hours, mins, secs, us;
                secs = ic->duration / AV_TIME_BASE;
                us = ic->duration % AV_TIME_BASE;
                mins = secs / 60;
                secs %= 60;
                hours = mins / 60;
                mins %= 60;
    
                av_log(NULL, AV_LOG_INFO, "%02d:%02d:%02d.%01d", hours, mins, secs,
    
                av_log(NULL, AV_LOG_INFO, "N/A");
    
            if (ic->start_time != AV_NOPTS_VALUE) {
                int secs, us;
    
                av_log(NULL, AV_LOG_INFO, ", start: ");
    
                secs = ic->start_time / AV_TIME_BASE;
                us = ic->start_time % AV_TIME_BASE;
    
                av_log(NULL, AV_LOG_INFO, "%d.%06d",
    
            av_log(NULL, AV_LOG_INFO, ", bitrate: ");
    
                av_log(NULL, AV_LOG_INFO,"%d kb/s", ic->bit_rate / 1000);
    
                av_log(NULL, AV_LOG_INFO, "N/A");
    
            av_log(NULL, AV_LOG_INFO, "\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        for(i=0;i<ic->nb_streams;i++) {
            AVStream *st = ic->streams[i];
    
            int g= ff_gcd(st->time_base.num, st->time_base.den);
    
            avcodec_string(buf, sizeof(buf), st->codec, is_output);
    
            av_log(NULL, AV_LOG_INFO, "  Stream #%d.%d", index, i);
    
            /* the pid is an important information, so we display it */
            /* XXX: add a generic system */
            if (is_output)
                flags = ic->oformat->flags;
            else
                flags = ic->iformat->flags;
            if (flags & AVFMT_SHOW_IDS) {
    
                av_log(NULL, AV_LOG_INFO, "[0x%x]", st->id);
    
            if (strlen(st->language) > 0) {
                av_log(NULL, AV_LOG_INFO, "(%s)", st->language);
            }
    
            av_log(NULL, AV_LOG_DEBUG, ", %d/%d", st->time_base.num/g, st->time_base.den/g);
            if(st->codec->codec_type == CODEC_TYPE_VIDEO){
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                if(st->r_frame_rate.den && st->r_frame_rate.num)
                    av_log(NULL, AV_LOG_INFO, ", %5.2f fps(r)", av_q2d(st->r_frame_rate));
    /*            else if(st->time_base.den && st->time_base.num)
                    av_log(NULL, AV_LOG_INFO, ", %5.2f fps(m)", 1/av_q2d(st->time_base));*/
                else
                    av_log(NULL, AV_LOG_INFO, ", %5.2f fps(c)", 1/av_q2d(st->codec->time_base));
    
            av_log(NULL, AV_LOG_INFO, ": %s\n", buf);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    }
    
    typedef struct {
    
        const char *abv;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int width, height;
    
        int frame_rate, frame_rate_base;
    } AbvEntry;
    
    static AbvEntry frame_abvs[] = {
    
        { "ntsc",      720, 480, 30000, 1001 },
        { "pal",       720, 576,    25,    1 },
        { "qntsc",     352, 240, 30000, 1001 }, /* VCD compliant ntsc */
        { "qpal",      352, 288,    25,    1 }, /* VCD compliant pal */
    
        { "sntsc",     640, 480, 30000, 1001 }, /* square pixel ntsc */
        { "spal",      768, 576,    25,    1 }, /* square pixel pal */
    
        { "film",      352, 240,    24,    1 },
        { "ntsc-film", 352, 240, 24000, 1001 },
        { "sqcif",     128,  96,     0,    0 },
        { "qcif",      176, 144,     0,    0 },
        { "cif",       352, 288,     0,    0 },
        { "4cif",      704, 576,     0,    0 },
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    };
    
    /**
     * parses width and height out of string str.
     */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    int parse_image_size(int *width_ptr, int *height_ptr, const char *str)
    {
        int i;
    
        int n = sizeof(frame_abvs) / sizeof(AbvEntry);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        const char *p;
        int frame_width = 0, frame_height = 0;
    
        for(i=0;i<n;i++) {
    
            if (!strcmp(frame_abvs[i].abv, str)) {
                frame_width = frame_abvs[i].width;
                frame_height = frame_abvs[i].height;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                break;
            }
        }
        if (i == n) {
            p = str;
            frame_width = strtol(p, (char **)&p, 10);
            if (*p)
                p++;
            frame_height = strtol(p, (char **)&p, 10);
        }
        if (frame_width <= 0 || frame_height <= 0)
            return -1;
        *width_ptr = frame_width;
        *height_ptr = frame_height;
        return 0;
    }
    
    
    /**
     * Converts frame rate from string to a fraction.
     *
     * First we try to get an exact integer or fractional frame rate.
     * If this fails we convert the frame rate to a double and return
     * an approximate fraction using the DEFAULT_FRAME_RATE_BASE.
     */
    
    int parse_frame_rate(int *frame_rate, int *frame_rate_base, const char *arg)
    {
        int i;
        char* cp;
    
        /* First, we check our abbreviation table */
        for (i = 0; i < sizeof(frame_abvs)/sizeof(*frame_abvs); ++i)
             if (!strcmp(frame_abvs[i].abv, arg)) {
    
                 *frame_rate = frame_abvs[i].frame_rate;
                 *frame_rate_base = frame_abvs[i].frame_rate_base;
                 return 0;
             }
    
    
        /* Then, we try to parse it as fraction */
        cp = strchr(arg, '/');
    
        if (cp) {
            char* cpp;
    
            *frame_rate = strtol(arg, &cpp, 10);
            if (cpp != arg || cpp == cp)
                *frame_rate_base = strtol(cp+1, &cpp, 10);
            else
               *frame_rate = 0;
    
        else {
            /* Finally we give up and parse it as double */
    
            AVRational time_base = av_d2q(strtod(arg, 0), DEFAULT_FRAME_RATE_BASE);
            *frame_rate_base = time_base.den;
            *frame_rate = time_base.num;
    
        }
        if (!*frame_rate || !*frame_rate_base)
            return -1;
        else
            return 0;
    }
    
    
    /**
     * Converts date string to number of seconds since Jan 1st, 1970.
     *
     * @code
     * Syntax:
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     * - If not a duration:
     *  [{YYYY-MM-DD|YYYYMMDD}]{T| }{HH[:MM[:SS[.m...]]][Z]|HH[MM[SS[.m...]]][Z]}
    
     * Time is localtime unless Z is suffixed to the end. In this case GMT
    
     * Return the date in micro seconds since 1970
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     *  HH[:MM[:SS[.m...]]]
     *  S+[.m...]
    
    #ifndef CONFIG_WINCE
    
    int64_t parse_date(const char *datestr, int duration)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        const char *p;
    
        int64_t t;
    
        int i;
        static const char *date_fmt[] = {
            "%Y-%m-%d",
            "%Y%m%d",
        };
        static const char *time_fmt[] = {
            "%H:%M:%S",
            "%H%M%S",
        };
        const char *q;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int is_utc, len;
    
        int negative = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        len = strlen(datestr);
        if (len > 0)
            lastch = datestr[len - 1];
        else
            lastch = '\0';
    
        is_utc = (lastch == 'z' || lastch == 'Z');
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        p = datestr;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        q = NULL;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (!duration) {
    
            for (i = 0; i < sizeof(date_fmt) / sizeof(date_fmt[0]); i++) {
    
                q = small_strptime(p, date_fmt[i], &dt);
    
                if (q) {
                    break;
                }
            }
    
            if (!q) {
                if (is_utc) {
                    dt = *gmtime(&now);
                } else {
                    dt = *localtime(&now);
                }
                dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            for (i = 0; i < sizeof(time_fmt) / sizeof(time_fmt[0]); i++) {
    
                q = small_strptime(p, time_fmt[i], &dt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                if (q) {
                    break;
                }
            }
        } else {
    
            if (p[0] == '-') {
                negative = 1;
                ++p;
            }
    
            q = small_strptime(p, time_fmt[0], &dt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (!q) {
                dt.tm_sec = strtol(p, (char **)&q, 10);
                dt.tm_min = 0;
                dt.tm_hour = 0;
    
            }
        }
    
        /* Now we have all the fields that we can get */
        if (!q) {
            if (duration)
                return 0;
            else
    
                return now * int64_t_C(1000000);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
            t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
    
            dt.tm_isdst = -1;       /* unknown */
            if (is_utc) {
                t = mktimegm(&dt);
            } else {
                t = mktime(&dt);
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            int val, n;
    
            q++;
            for (val = 0, n = 100000; n >= 1; n /= 10, q++) {
    
                if (!isdigit(*q))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
            t += val;
        }
    
        return negative ? -t : t;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    #endif /* CONFIG_WINCE */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
    /**
     * Attempts to find a specific tag in a URL.
     *
     * syntax: '?tag1=val1&tag2=val2...'. Little URL decoding is done.
     * Return 1 if found.
     */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    int find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
    {
        const char *p;
        char tag[128], *q;
    
        p = info;
        if (*p == '?')
            p++;
        for(;;) {
            q = tag;
            while (*p != '\0' && *p != '=' && *p != '&') {
                if ((q - tag) < sizeof(tag) - 1)
                    *q++ = *p;
                p++;
            }
            *q = '\0';
            q = arg;
            if (*p == '=') {
                p++;
                while (*p != '&' && *p != '\0') {
    
                    if ((q - arg) < arg_size - 1) {
                        if (*p == '+')
                            *q++ = ' ';
                        else
                            *q++ = *p;
                    }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    p++;
                }
                *q = '\0';
            }
    
            if (!strcmp(tag, tag1))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                return 1;
            if (*p != '&')
                break;
    
    /**
     * Returns in 'buf' the path with '%d' replaced by number.
     *
     * Also handles the '%0nd' format where 'n' is the total number
     * of digits and '%%'. Return 0 if OK, and -1 if format error.
     */
    
    int get_frame_filename(char *buf, int buf_size,
                           const char *path, int number)
    {
        const char *p;
    
    
        q = buf;
        p = path;
        percentd_found = 0;
        for(;;) {
            c = *p++;
            if (c == '\0')
                break;
            if (c == '%') {
    
                do {
                    nd = 0;
                    while (isdigit(*p)) {
                        nd = nd * 10 + *p++ - '0';
                    }
                    c = *p++;
                } while (isdigit(c));
    
    
                switch(c) {
                case '%':
                    goto addchar;
                case 'd':
                    if (percentd_found)
                        goto fail;
                    percentd_found = 1;
                    snprintf(buf1, sizeof(buf1), "%0*d", nd, number);
                    len = strlen(buf1);
                    if ((q - buf + len) > buf_size - 1)
                        goto fail;
                    memcpy(q, buf1, len);
                    q += len;
                    break;
                default:
                    goto fail;
                }
            } else {
            addchar:
                if ((q - buf) < buf_size - 1)
                    *q++ = c;
            }
        }
        if (!percentd_found)
            goto fail;
        *q = '\0';
        return 0;
     fail:
        *q = '\0';
        return -1;
    }
    
    
     * Print  nice hexa dump of a buffer
     * @param f stream for output
    
    void av_hex_dump(FILE *f, uint8_t *buf, int size)
    
    {
        int len, i, j, c;
    
        for(i=0;i<size;i+=16) {
            len = size - i;
            if (len > 16)
                len = 16;
    
            fprintf(f, "%08x ", i);