Skip to content
Snippets Groups Projects
utils.c 64.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • Fabrice Bellard's avatar
    Fabrice Bellard committed
    /*
     * Various utilities for ffmpeg system
    
     * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     *
    
     * This library is free software; you can redistribute it and/or
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
     * version 2 of the License, or (at your option) any later version.
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     *
    
     * This library is distributed in the hope that it will be useful,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
    
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     *
    
     * You should have received a copy of the GNU Lesser General Public
     * License along with this library; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #include "avformat.h"
    
    #undef NDEBUG
    #include <assert.h>
    
    
    AVInputFormat *first_iformat;
    AVOutputFormat *first_oformat;
    
    AVImageFormat *first_image_format;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
    void av_register_input_format(AVInputFormat *format)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
        AVInputFormat **p;
        p = &first_iformat;
        while (*p != NULL) p = &(*p)->next;
        *p = format;
        format->next = NULL;
    }
    
    void av_register_output_format(AVOutputFormat *format)
    {
        AVOutputFormat **p;
        p = &first_oformat;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        while (*p != NULL) p = &(*p)->next;
        *p = format;
        format->next = NULL;
    }
    
    
    int match_ext(const char *filename, const char *extensions)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        const char *ext, *p;
        char ext1[32], *q;
    
        ext = strrchr(filename, '.');
        if (ext) {
            ext++;
            p = extensions;
            for(;;) {
                q = ext1;
                while (*p != '\0' && *p != ',') 
                    *q++ = *p++;
                *q = '\0';
                if (!strcasecmp(ext1, ext)) 
                    return 1;
                if (*p == '\0') 
                    break;
                p++;
            }
        }
        return 0;
    }
    
    
    AVOutputFormat *guess_format(const char *short_name, const char *filename, 
                                 const char *mime_type)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int score_max, score;
    
    
        /* specific test for image sequences */
    
        if (!short_name && filename && 
            filename_number_test(filename) >= 0 &&
            guess_image_format(filename)) {
    
            return guess_format("image", NULL, NULL);
        }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* find the proper file type */
        fmt_found = NULL;
        score_max = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        while (fmt != NULL) {
            score = 0;
            if (fmt->name && short_name && !strcmp(fmt->name, short_name))
                score += 100;
            if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type))
                score += 10;
            if (filename && fmt->extensions && 
                match_ext(filename, fmt->extensions)) {
                score += 5;
            }
            if (score > score_max) {
                score_max = score;
                fmt_found = fmt;
            }
            fmt = fmt->next;
        }
        return fmt_found;
    }   
    
    
    AVOutputFormat *guess_stream_format(const char *short_name, const char *filename, 
                                 const char *mime_type)
    {
        AVOutputFormat *fmt = guess_format(short_name, filename, mime_type);
    
        if (fmt) {
            AVOutputFormat *stream_fmt;
            char stream_format_name[64];
    
            snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
            stream_fmt = guess_format(stream_format_name, NULL, NULL);
    
            if (stream_fmt)
                fmt = stream_fmt;
        }
    
        return fmt;
    }
    
    
    AVInputFormat *av_find_input_format(const char *short_name)
    {
        AVInputFormat *fmt;
        for(fmt = first_iformat; fmt != NULL; fmt = fmt->next) {
            if (!strcmp(fmt->name, short_name))
                return fmt;
        }
        return NULL;
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* memory handling */
    
    
    /**
     * Default packet destructor 
     */
    static void av_destruct_packet(AVPacket *pkt)
    {
        av_free(pkt->data);
        pkt->data = NULL; pkt->size = 0;
    }
    
    
    /**
     * Allocate the payload of a packet and intialized its fields to default values.
     *
     * @param pkt packet
     * @param size wanted payload size
     * @return 0 if OK. AVERROR_xxx otherwise.
     */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    int av_new_packet(AVPacket *pkt, int size)
    {
    
        void *data = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
        if (!data)
    
        memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
    
        av_init_packet(pkt);
        pkt->data = data; 
        pkt->size = size;
        pkt->destruct = av_destruct_packet;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        return 0;
    }
    
    
    /* This is a hack - the packet memory allocation stuff is broken. The
       packet is allocated if it was not really allocated */
    int av_dup_packet(AVPacket *pkt)
    {
        if (pkt->destruct != av_destruct_packet) {
            uint8_t *data;
    
            /* we duplicate the packet and don't forget to put the padding
               again */
            data = av_malloc(pkt->size + FF_INPUT_BUFFER_PADDING_SIZE);
    
            if (!data) {
                return AVERROR_NOMEM;
            }
            memcpy(data, pkt->data, pkt->size);
    
            memset(data + pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
    
            pkt->data = data;
            pkt->destruct = av_destruct_packet;
        }
        return 0;
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* fifo handling */
    
    int fifo_init(FifoBuffer *f, int size)
    {
    
        f->buffer = av_malloc(size);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (!f->buffer)
            return -1;
        f->end = f->buffer + size;
        f->wptr = f->rptr = f->buffer;
        return 0;
    }
    
    void fifo_free(FifoBuffer *f)
    {
    
        av_free(f->buffer);
    
    int fifo_size(FifoBuffer *f, uint8_t *rptr)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        int size;
    
        if (f->wptr >= rptr) {
            size = f->wptr - rptr;
        } else {
            size = (f->end - rptr) + (f->wptr - f->buffer);
        }
        return size;
    }
    
    /* get data from the fifo (return -1 if not enough data) */
    
    int fifo_read(FifoBuffer *f, uint8_t *buf, int buf_size, uint8_t **rptr_ptr)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
        uint8_t *rptr = *rptr_ptr;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int size, len;
    
        if (f->wptr >= rptr) {
            size = f->wptr - rptr;
        } else {
            size = (f->end - rptr) + (f->wptr - f->buffer);
        }
        
        if (size < buf_size)
            return -1;
        while (buf_size > 0) {
            len = f->end - rptr;
            if (len > buf_size)
                len = buf_size;
            memcpy(buf, rptr, len);
            buf += len;
            rptr += len;
            if (rptr >= f->end)
                rptr = f->buffer;
            buf_size -= len;
        }
        *rptr_ptr = rptr;
        return 0;
    }
    
    
    void fifo_write(FifoBuffer *f, uint8_t *buf, int size, uint8_t **wptr_ptr)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        int len;
    
        uint8_t *wptr;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        wptr = *wptr_ptr;
        while (size > 0) {
            len = f->end - wptr;
            if (len > size)
                len = size;
            memcpy(wptr, buf, len);
            wptr += len;
            if (wptr >= f->end)
                wptr = f->buffer;
            buf += len;
            size -= len;
        }
        *wptr_ptr = wptr;
    }
    
    
    int filename_number_test(const char *filename)
    {
        char buf[1024];
        return get_frame_filename(buf, sizeof(buf), filename, 1);
    }
    
    /* guess file format */
    
    AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)
    
    {
        AVInputFormat *fmt1, *fmt;
        int score, score_max;
    
        fmt = NULL;
        score_max = 0;
        for(fmt1 = first_iformat; fmt1 != NULL; fmt1 = fmt1->next) {
            if (!is_opened && !(fmt1->flags & AVFMT_NOFILE))
                continue;
            score = 0;
    
            if (fmt1->read_probe) {
                score = fmt1->read_probe(pd);
            } else if (fmt1->extensions) {
    
                if (match_ext(pd->filename, fmt1->extensions)) {
                    score = 50;
                }
    
            if (score > score_max) {
                score_max = score;
                fmt = fmt1;
            }
        }
        return fmt;
    }
    
    /************************************************************/
    /* input media file */
    
    /**
     * open a media file from an IO stream. 'fmt' must be specified.
     */
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
    static const char* format_to_name(void* ptr)
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
        AVFormatContext* fc = (AVFormatContext*) ptr;
    
        if(fc->iformat) return fc->iformat->name;
        else if(fc->oformat) return fc->oformat->name;
        else return "NULL";
    }
    
    static const AVClass av_format_context_class = { "AVFormatContext", format_to_name };
    
    AVFormatContext *av_alloc_format_context(void)
    {
        AVFormatContext *ic;
        ic = av_mallocz(sizeof(AVFormatContext));
        if (!ic) return ic;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
        ic->av_class = &av_format_context_class;
    
    int av_open_input_stream(AVFormatContext **ic_ptr, 
                             ByteIOContext *pb, const char *filename, 
                             AVInputFormat *fmt, AVFormatParameters *ap)
    {
        int err;
        AVFormatContext *ic;
    
    
        if (!ic) {
            err = AVERROR_NOMEM;
            goto fail;
        }
        ic->iformat = fmt;
        if (pb)
            ic->pb = *pb;
        ic->duration = AV_NOPTS_VALUE;
        ic->start_time = AV_NOPTS_VALUE;
        pstrcpy(ic->filename, sizeof(ic->filename), filename);
    
        /* allocate private data */
        if (fmt->priv_data_size > 0) {
            ic->priv_data = av_mallocz(fmt->priv_data_size);
            if (!ic->priv_data) {
                err = AVERROR_NOMEM;
                goto fail;
            }
        } else {
            ic->priv_data = NULL;
        }
    
        /* default pts settings is MPEG like */
        av_set_pts_info(ic, 33, 1, 90000);
    
        ic->last_pkt_pts = AV_NOPTS_VALUE;
        ic->last_pkt_dts = AV_NOPTS_VALUE;
        ic->last_pkt_stream_pts = AV_NOPTS_VALUE;
        ic->last_pkt_stream_dts = AV_NOPTS_VALUE;
        
    
        err = ic->iformat->read_header(ic, ap);
        if (err < 0)
            goto fail;
    
    
        if (pb)
            ic->data_offset = url_ftell(&ic->pb);
    
    
        *ic_ptr = ic;
        return 0;
     fail:
        if (ic) {
            av_freep(&ic->priv_data);
        }
        av_free(ic);
        *ic_ptr = NULL;
        return err;
    }
    
    
    #define PROBE_BUF_SIZE 2048
    
    /**
     * Open a media file as input. The codec are not opened. Only the file
     * header (if present) is read.
     *
     * @param ic_ptr the opened media file handle is put here
     * @param filename filename to open.
     * @param fmt if non NULL, force the file format to use
     * @param buf_size optional buffer size (zero if default is OK)
     * @param ap additionnal parameters needed when opening the file (NULL if default)
     * @return 0 if OK. AVERROR_xxx otherwise.
     */
    int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, 
                           AVInputFormat *fmt,
                           int buf_size,
                           AVFormatParameters *ap)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
        int err, must_open_file, file_opened;
        uint8_t buf[PROBE_BUF_SIZE];
    
        ByteIOContext pb1, *pb = &pb1;
        
        file_opened = 0;
        pd->filename = "";
        if (filename)
            pd->filename = filename;
    
        pd->buf = buf;
        pd->buf_size = 0;
    
        if (!fmt) {
            /* guess format if no file can be opened  */
    
            fmt = av_probe_input_format(pd, 0);
    
        /* do not open file if the format does not need it. XXX: specific
           hack needed to handle RTSP/TCP */
        must_open_file = 1;
    
        if (fmt && (fmt->flags & AVFMT_NOFILE)) {
    
            must_open_file = 0;
        }
    
        if (!fmt || must_open_file) {
    
            /* if no file needed do not try to open one */
    
            if (url_fopen(pb, filename, URL_RDONLY) < 0) {
    
            if (buf_size > 0) {
    
                pd->buf_size = get_buffer(pb, buf, PROBE_BUF_SIZE);
                url_fseek(pb, 0, SEEK_SET);
    
            fmt = av_probe_input_format(pd, 1);
    
        }
    
        /* if still no format found, error */
        if (!fmt) {
            err = AVERROR_NOFMT;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        /* XXX: suppress this hack for redirectors */
    
            err = redir_open(ic_ptr, pb);
            url_fclose(pb);
    
        /* check filename in case of an image number is expected */
    
        if (fmt->flags & AVFMT_NEEDNUMBER) {
            if (filename_number_test(filename) < 0) { 
    
                err = AVERROR_NUMEXPECTED;
    
        err = av_open_input_stream(ic_ptr, pb, filename, fmt, ap);
        if (err)
            goto fail;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     fail:
    
    /*******************************************************/
    
    
     * Read a transport packet from a media file. This function is
     * absolete and should never be used. Use av_read_frame() instead.
    
     * @return 0 if OK. AVERROR_xxx if error.  
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    int av_read_packet(AVFormatContext *s, AVPacket *pkt)
    
    {
        return s->iformat->read_packet(s, pkt);
    }
    
    /**********************************************************/
    
    /* convert the packet time stamp units and handle wrapping. The
       wrapping is handled by considering the next PTS/DTS as a delta to
       the previous value. We handle the delta as a fraction to avoid any
       rounding errors. */
    static inline int64_t convert_timestamp_units(AVFormatContext *s,
                                            int64_t *plast_pkt_pts,
                                            int *plast_pkt_pts_frac,
                                            int64_t *plast_pkt_stream_pts,
                                            int64_t pts)
    {
        int64_t stream_pts;
        int64_t delta_pts;
        int shift, pts_frac;
    
        if (pts != AV_NOPTS_VALUE) {
            stream_pts = pts;
            if (*plast_pkt_stream_pts != AV_NOPTS_VALUE) {
                shift = 64 - s->pts_wrap_bits;
                delta_pts = ((stream_pts - *plast_pkt_stream_pts) << shift) >> shift;
                /* XXX: overflow possible but very unlikely as it is a delta */
                delta_pts = delta_pts * AV_TIME_BASE * s->pts_num;
                pts = *plast_pkt_pts + (delta_pts / s->pts_den);
                pts_frac = *plast_pkt_pts_frac + (delta_pts % s->pts_den);
                if (pts_frac >= s->pts_den) {
                    pts_frac -= s->pts_den;
                    pts++;
                }
            } else {
                /* no previous pts, so no wrapping possible */
                pts = (int64_t)(((double)stream_pts * AV_TIME_BASE * s->pts_num) / 
                                (double)s->pts_den);
                pts_frac = 0;
            }
            *plast_pkt_stream_pts = stream_pts;
            *plast_pkt_pts = pts;
            *plast_pkt_pts_frac = pts_frac;
        }
        return pts;
    }
    
    /* get the number of samples of an audio frame. Return (-1) if error */
    static int get_audio_frame_size(AVCodecContext *enc, int size)
    {
        int frame_size;
    
        if (enc->frame_size <= 1) {
            /* specific hack for pcm codecs because no frame size is
               provided */
            switch(enc->codec_id) {
            case CODEC_ID_PCM_S16LE:
            case CODEC_ID_PCM_S16BE:
            case CODEC_ID_PCM_U16LE:
            case CODEC_ID_PCM_U16BE:
                if (enc->channels == 0)
                    return -1;
                frame_size = size / (2 * enc->channels);
                break;
            case CODEC_ID_PCM_S8:
            case CODEC_ID_PCM_U8:
            case CODEC_ID_PCM_MULAW:
            case CODEC_ID_PCM_ALAW:
                if (enc->channels == 0)
                    return -1;
                frame_size = size / (enc->channels);
                break;
            default:
                /* used for example by ADPCM codecs */
                if (enc->bit_rate == 0)
                    return -1;
                frame_size = (size * 8 * enc->sample_rate) / enc->bit_rate;
                break;
            }
        } else {
            frame_size = enc->frame_size;
        }
        return frame_size;
    }
    
    
    /* return the frame duration in seconds, return 0 if not available */
    static void compute_frame_duration(int *pnum, int *pden,
                                       AVFormatContext *s, AVStream *st, 
                                       AVCodecParserContext *pc, AVPacket *pkt)
    {
        int frame_size;
    
        *pnum = 0;
        *pden = 0;
        switch(st->codec.codec_type) {
        case CODEC_TYPE_VIDEO:
            *pnum = st->codec.frame_rate_base;
            *pden = st->codec.frame_rate;
            if (pc && pc->repeat_pict) {
                *pden *= 2;
                *pnum = (*pnum) * (2 + pc->repeat_pict);
            }
            break;
        case CODEC_TYPE_AUDIO:
            frame_size = get_audio_frame_size(&st->codec, pkt->size);
            if (frame_size < 0)
                break;
            *pnum = frame_size;
            *pden = st->codec.sample_rate;
            break;
        default:
            break;
        }
    }
    
    static void compute_pkt_fields(AVFormatContext *s, AVStream *st, 
                                   AVCodecParserContext *pc, AVPacket *pkt)
    {
        int num, den, presentation_delayed;
    
        if (pkt->duration == 0) {
            compute_frame_duration(&num, &den, s, st, pc, pkt);
            if (den && num) {
                pkt->duration = (num * (int64_t)AV_TIME_BASE) / den;
            }
        }
    
        /* do we have a video B frame ? */
        presentation_delayed = 0;
        if (st->codec.codec_type == CODEC_TYPE_VIDEO) {
            /* XXX: need has_b_frame, but cannot get it if the codec is
               not initialized */
            if ((st->codec.codec_id == CODEC_ID_MPEG1VIDEO ||
                 st->codec.codec_id == CODEC_ID_MPEG2VIDEO ||
                 st->codec.codec_id == CODEC_ID_MPEG4 ||
                 st->codec.codec_id == CODEC_ID_H264) && 
                pc && pc->pict_type != FF_B_TYPE)
                presentation_delayed = 1;
        }
    
        /* interpolate PTS and DTS if they are not present */
        if (presentation_delayed) {
            /* DTS = decompression time stamp */
            /* PTS = presentation time stamp */
            if (pkt->dts == AV_NOPTS_VALUE) {
                pkt->dts = st->cur_dts;
            } else {
                st->cur_dts = pkt->dts;
            }
            /* this is tricky: the dts must be incremented by the duration
               of the frame we are displaying, i.e. the last I or P frame */
            if (st->last_IP_duration == 0)
                st->cur_dts += pkt->duration;
            else
                st->cur_dts += st->last_IP_duration;
            st->last_IP_duration  = pkt->duration;
            /* cannot compute PTS if not present (we can compute it only
               by knowing the futur */
        } else {
            /* presentation is not delayed : PTS and DTS are the same */
            if (pkt->pts == AV_NOPTS_VALUE) {
    
                if (pkt->dts == AV_NOPTS_VALUE) {
                    pkt->pts = st->cur_dts;
                    pkt->dts = st->cur_dts;
                }
                else {
                    st->cur_dts = pkt->dts;
                    pkt->pts = pkt->dts;
                }
    
            } else {
                st->cur_dts = pkt->pts;
                pkt->dts = pkt->pts;
            }
            st->cur_dts += pkt->duration;
        }
        
        /* update flags */
        if (pc) {
            pkt->flags = 0;
            /* key frame computation */
            switch(st->codec.codec_type) {
            case CODEC_TYPE_VIDEO:
                if (pc->pict_type == FF_I_TYPE)
                    pkt->flags |= PKT_FLAG_KEY;
                break;
            case CODEC_TYPE_AUDIO:
                pkt->flags |= PKT_FLAG_KEY;
                break;
            default:
                break;
            }
        }
    
    }
    
    static void av_destruct_packet_nofree(AVPacket *pkt)
    {
        pkt->data = NULL; pkt->size = 0;
    }
    
    static int av_read_frame_internal(AVFormatContext *s, AVPacket *pkt)
    {
        AVStream *st;
    
    
        for(;;) {
            /* select current input stream component */
            st = s->cur_st;
            if (st) {
                if (!st->parser) {
                    /* no parsing needed: we just output the packet as is */
                    /* raw data support */
                    *pkt = s->cur_pkt;
                    compute_pkt_fields(s, st, NULL, pkt);
                    s->cur_st = NULL;
                    return 0;
                } else if (s->cur_len > 0) {
                    len = av_parser_parse(st->parser, &st->codec, &pkt->data, &pkt->size, 
    
                                          s->cur_ptr, s->cur_len,
                                          s->cur_pkt.pts, s->cur_pkt.dts);
                    s->cur_pkt.pts = AV_NOPTS_VALUE;
                    s->cur_pkt.dts = AV_NOPTS_VALUE;
    
                    /* increment read pointer */
                    s->cur_ptr += len;
                    s->cur_len -= len;
                    
                    /* return packet if any */
                    if (pkt->size) {
    
                        pkt->duration = 0;
                        pkt->stream_index = st->index;
    
                        pkt->pts = st->parser->pts;
                        pkt->dts = st->parser->dts;
    
                        pkt->destruct = av_destruct_packet_nofree;
                        compute_pkt_fields(s, st, st->parser, pkt);
                        return 0;
                    }
                } else {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    /* free packet */
                    av_free_packet(&s->cur_pkt); 
    
                    s->cur_st = NULL;
                }
            } else {
                /* read next packet */
                ret = av_read_packet(s, &s->cur_pkt);
    
                if (ret < 0) {
                    if (ret == -EAGAIN)
                        return ret;
                    /* return the last frames, if any */
                    for(i = 0; i < s->nb_streams; i++) {
                        st = s->streams[i];
                        if (st->parser) {
                            av_parser_parse(st->parser, &st->codec, 
                                            &pkt->data, &pkt->size, 
    
                            if (pkt->size)
                                goto got_packet;
                        }
                    }
                    /* no more packets: really terminates parsing */
    
    
                /* convert the packet time stamp units and handle wrapping */
                s->cur_pkt.pts = convert_timestamp_units(s, 
                                                   &s->last_pkt_pts, &s->last_pkt_pts_frac,
                                                   &s->last_pkt_stream_pts,
                                                   s->cur_pkt.pts);
                s->cur_pkt.dts = convert_timestamp_units(s, 
                                                   &s->last_pkt_dts,  &s->last_pkt_dts_frac,
                                                   &s->last_pkt_stream_dts,
                                                   s->cur_pkt.dts);
    #if 0
    
                    if (s->cur_pkt.pts != AV_NOPTS_VALUE) 
                        printf("PACKET pts=%0.3f\n", 
                               (double)s->cur_pkt.pts / AV_TIME_BASE);
                    if (s->cur_pkt.dts != AV_NOPTS_VALUE) 
                        printf("PACKET dts=%0.3f\n", 
                               (double)s->cur_pkt.dts / AV_TIME_BASE);
                }
    #endif
                
                /* duration field */
                if (s->cur_pkt.duration != 0) {
                    s->cur_pkt.duration = ((int64_t)s->cur_pkt.duration * AV_TIME_BASE * s->pts_num) / 
                        s->pts_den;
                }
    
                st = s->streams[s->cur_pkt.stream_index];
                s->cur_st = st;
                s->cur_ptr = s->cur_pkt.data;
                s->cur_len = s->cur_pkt.size;
                if (st->need_parsing && !st->parser) {
                    st->parser = av_parser_init(st->codec.codec_id);
                    if (!st->parser) {
                        /* no parser available : just output the raw packets */
                        st->need_parsing = 0;
                    }
                }
            }
        }
    }
    
    /**
     * Return the next frame of a stream. The returned packet is valid
     * until the next av_read_frame() or until av_close_input_file() and
     * must be freed with av_free_packet. For video, the packet contains
     * exactly one frame. For audio, it contains an integer number of
     * frames if each frame has a known fixed size (e.g. PCM or ADPCM
     * data). If the audio frames have a variable size (e.g. MPEG audio),
     * then it contains one frame.
     * 
     * pkt->pts, pkt->dts and pkt->duration are always set to correct
     * values in AV_TIME_BASE unit (and guessed if the format cannot
     * provided them). pkt->pts can be AV_NOPTS_VALUE if the video format
     * has B frames, so it is better to rely on pkt->dts if you do not
     * decompress the payload.
     * 
     * Return 0 if OK, < 0 if error or end of file.  
     */
    int av_read_frame(AVFormatContext *s, AVPacket *pkt)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        AVPacketList *pktl;
    
        pktl = s->packet_buffer;
        if (pktl) {
            /* read packet from packet buffer, if there is data */
            *pkt = pktl->pkt;
            s->packet_buffer = pktl->next;
    
            av_free(pktl);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            return 0;
        } else {
    
            return av_read_frame_internal(s, pkt);
        }
    }
    
    /* XXX: suppress the packet queue */
    static void flush_packet_queue(AVFormatContext *s)
    {
        AVPacketList *pktl;
    
        for(;;) {
            pktl = s->packet_buffer;
            if (!pktl) 
                break;
            s->packet_buffer = pktl->next;
            av_free_packet(&pktl->pkt);
            av_free(pktl);
    
    /*******************************************************/
    /* seek support */
    
    
    int av_find_default_stream_index(AVFormatContext *s)
    {
        int i;
        AVStream *st;
    
        if (s->nb_streams <= 0)
            return -1;
        for(i = 0; i < s->nb_streams; i++) {
            st = s->streams[i];
            if (st->codec.codec_type == CODEC_TYPE_VIDEO) {
                return i;
            }
        }
        return 0;
    }
    
    
    /* flush the frame reader */
    static void av_read_frame_flush(AVFormatContext *s)
    {
        AVStream *st;
        int i;
    
        flush_packet_queue(s);
    
        /* free previous packet */
        if (s->cur_st) {
            if (s->cur_st->parser)
                av_free_packet(&s->cur_pkt);
            s->cur_st = NULL;
        }
        /* fail safe */
        s->cur_ptr = NULL;
        s->cur_len = 0;
        
        /* for each stream, reset read state */
        for(i = 0; i < s->nb_streams; i++) {
            st = s->streams[i];
            
            if (st->parser) {
                av_parser_close(st->parser);
                st->parser = NULL;
            }
            st->cur_dts = 0; /* we set the current DTS to an unspecified origin */
        }
    }
    
    
    /* add a index entry into a sorted list updateing if it is already there */
    
    int av_add_index_entry(AVStream *st,
                                int64_t pos, int64_t timestamp, int distance, int flags)
    
    {
        AVIndexEntry *entries, *ie;
    
        
        entries = av_fast_realloc(st->index_entries,
                                  &st->index_entries_allocated_size,
                                  (st->nb_index_entries + 1) * 
                                  sizeof(AVIndexEntry));
    
        st->index_entries= entries;
    
        if(st->nb_index_entries){
            index= av_index_search_timestamp(st, timestamp);
            ie= &entries[index];
    
            if(ie->timestamp != timestamp){
                if(ie->timestamp < timestamp){
                    index++; //index points to next instead of previous entry, maybe nonexistant
                    ie= &st->index_entries[index];
                }else
                    assert(index==0);
                    
                if(index != st->nb_index_entries){
                    assert(index < st->nb_index_entries);
                    memmove(entries + index + 1, entries + index, sizeof(AVIndexEntry)*(st->nb_index_entries - index));
                }
                st->nb_index_entries++;
            }
    
        }else{
            index= st->nb_index_entries++;
            ie= &entries[index];
        }
    
        
        ie->pos = pos;
        ie->timestamp = timestamp;
    
    }
    
    /* build an index for raw streams using a parser */
    static void av_build_index_raw(AVFormatContext *s)
    {
        AVPacket pkt1, *pkt = &pkt1;
        int ret;
        AVStream *st;
    
        st = s->streams[0];
        av_read_frame_flush(s);
        url_fseek(&s->pb, s->data_offset, SEEK_SET);
    
        for(;;) {
            ret = av_read_frame(s, pkt);
            if (ret < 0)
                break;
            if (pkt->stream_index == 0 && st->parser &&
                (pkt->flags & PKT_FLAG_KEY)) {
    
                av_add_index_entry(st, st->parser->frame_offset, pkt->dts, 
    
            }
            av_free_packet(pkt);
        }
    }
    
    /* return TRUE if we deal with a raw stream (raw codec data and
       parsing needed) */
    static int is_raw_stream(AVFormatContext *s)
    {
        AVStream *st;
    
        if (s->nb_streams != 1)
            return 0;
        st = s->streams[0];
        if (!st->need_parsing)
            return 0;
        return 1;
    }
    
    /* return the largest index entry whose timestamp is <=
       wanted_timestamp */
    
    int av_index_search_timestamp(AVStream *st, int wanted_timestamp)
    
        AVIndexEntry *entries= st->index_entries;
        int nb_entries= st->nb_index_entries;
    
        int a, b, m;
        int64_t timestamp;
    
        if (nb_entries <= 0)
            return -1;
        
        a = 0;
        b = nb_entries - 1;
    
    
        while (a < b) {
            m = (a + b + 1) >> 1;
    
            timestamp = entries[m].timestamp;
    
            if (timestamp > wanted_timestamp) {
    
    }
    
    static int av_seek_frame_generic(AVFormatContext *s, 
                                     int stream_index, int64_t timestamp)
    {
        int index;