Newer
Older
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
Diego Biurrun
committed
* This file is part of FFmpeg.
*
* FFmpeg 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
Diego Biurrun
committed
* version 2.1 of the License, or (at your option) any later version.
Diego Biurrun
committed
* FFmpeg is distributed in the hope that it will be useful,
* 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.
* You should have received a copy of the GNU Lesser General Public
Diego Biurrun
committed
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include "riff.h"
#include <sys/time.h>
#include <time.h>
Philip Gladstone
committed
#undef NDEBUG
#include <assert.h>
Daniel Kristjansson
committed
/**
* @file libavformat/utils.c
* Various utility functions for using ffmpeg library.
*/
static void av_frac_init(AVFrac *f, int64_t val, int64_t num, int64_t den);
static void av_frac_add(AVFrac *f, int64_t incr);
Daniel Kristjansson
committed
/** head of registered input format linked list. */
Michael Niedermayer
committed
AVInputFormat *first_iformat = NULL;
Daniel Kristjansson
committed
/** head of registered output format linked list. */
Michael Niedermayer
committed
AVOutputFormat *first_oformat = NULL;
AVInputFormat *av_iformat_next(AVInputFormat *f)
{
if(f) return f->next;
else return first_iformat;
}
AVOutputFormat *av_oformat_next(AVOutputFormat *f)
{
if(f) return f->next;
else return first_oformat;
}
Fabrice Bellard
committed
void av_register_input_format(AVInputFormat *format)
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;
while (*p != NULL) p = &(*p)->next;
*p = format;
format->next = NULL;
}
Fabrice Bellard
committed
int match_ext(const char *filename, const char *extensions)
ext = strrchr(filename, '.');
if (ext) {
ext++;
p = extensions;
for(;;) {
q = ext1;
while (*p != '\0' && *p != ',' && q-ext1<sizeof(ext1)-1)
AVOutputFormat *guess_format(const char *short_name, const char *filename,
Fabrice Bellard
committed
const char *mime_type)
Fabrice Bellard
committed
AVOutputFormat *fmt, *fmt_found;
#ifdef CONFIG_IMAGE2_MUXER
if (!short_name && filename &&
Michel Bardiaux
committed
av_filename_number_test(filename) &&
av_guess_image2_codec(filename) != CODEC_ID_NONE) {
return guess_format("image2", NULL, NULL);
}
/* find the proper file type */
fmt_found = NULL;
score_max = 0;
Fabrice Bellard
committed
fmt = first_oformat;
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,
Philip Gladstone
committed
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;
}
enum CodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name,
const char *filename, const char *mime_type, enum CodecType type){
if(type == CODEC_TYPE_VIDEO){
enum CodecID codec_id= CODEC_ID_NONE;
#ifdef CONFIG_IMAGE2_MUXER
if(!strcmp(fmt->name, "image2") || !strcmp(fmt->name, "image2pipe")){
codec_id= av_guess_image2_codec(filename);
}
if(codec_id == CODEC_ID_NONE)
codec_id= fmt->video_codec;
return codec_id;
}else if(type == CODEC_TYPE_AUDIO)
return fmt->audio_codec;
else
return CODEC_ID_NONE;
}
Fabrice Bellard
committed
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;
}
Michael Niedermayer
committed
void av_destruct_packet(AVPacket *pkt)
{
av_free(pkt->data);
pkt->data = NULL; pkt->size = 0;
}
void av_init_packet(AVPacket *pkt)
{
pkt->pts = AV_NOPTS_VALUE;
pkt->dts = AV_NOPTS_VALUE;
pkt->pos = -1;
pkt->duration = 0;
pkt->flags = 0;
pkt->stream_index = 0;
pkt->destruct= av_destruct_packet_nofree;
}
if((unsigned)size > (unsigned)size + FF_INPUT_BUFFER_PADDING_SIZE)
return AVERROR(ENOMEM);
data = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
return AVERROR(ENOMEM);
memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
av_init_packet(pkt);
pkt->size = size;
pkt->destruct = av_destruct_packet;
int av_get_packet(ByteIOContext *s, AVPacket *pkt, int size)
{
int ret= av_new_packet(pkt, size);
if(ret<0)
return ret;
pkt->pos= url_ftell(s);
ret= get_buffer(s, pkt->data, size);
if(ret<=0)
av_free_packet(pkt);
else
pkt->size= ret;
return ret;
}
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 */
if((unsigned)pkt->size > (unsigned)pkt->size + FF_INPUT_BUFFER_PADDING_SIZE)
return AVERROR(ENOMEM);
data = av_malloc(pkt->size + FF_INPUT_BUFFER_PADDING_SIZE);
return AVERROR(ENOMEM);
}
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;
}
Michel Bardiaux
committed
int av_filename_number_test(const char *filename)
Fabrice Bellard
committed
{
char buf[1024];
Michel Bardiaux
committed
return filename && (av_get_frame_filename(buf, sizeof(buf), filename, 1)>=0);
Fabrice Bellard
committed
}
Michael Niedermayer
committed
static AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)
Fabrice Bellard
committed
{
AVInputFormat *fmt1, *fmt;
Michael Niedermayer
committed
int score;
Fabrice Bellard
committed
fmt = NULL;
for(fmt1 = first_iformat; fmt1 != NULL; fmt1 = fmt1->next) {
Reimar Döffinger
committed
if (!is_opened == !(fmt1->flags & AVFMT_NOFILE))
Fabrice Bellard
committed
continue;
score = 0;
Philip Gladstone
committed
if (fmt1->read_probe) {
score = fmt1->read_probe(pd);
} else if (fmt1->extensions) {
Fabrice Bellard
committed
if (match_ext(pd->filename, fmt1->extensions)) {
score = 50;
}
Michael Niedermayer
committed
if (score > *score_max) {
*score_max = score;
Fabrice Bellard
committed
fmt = fmt1;
}
}
return fmt;
}
Michael Niedermayer
committed
AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened){
int score=0;
return av_probe_input_format2(pd, is_opened, &score);
}
Fabrice Bellard
committed
/************************************************************/
/* input media file */
Daniel Kristjansson
committed
* Open a media file from an IO stream. 'fmt' must be specified.
if(fc->iformat) return fc->iformat->name;
else if(fc->oformat) return fc->oformat->name;
else return "NULL";
}
#define OFFSET(x) offsetof(AVFormatContext,x)
#define DEFAULT 0 //should be NAN but it does not work as it is not a constant in glibc as required by ANSI/ISO C
//these names are too long to be readable
#define E AV_OPT_FLAG_ENCODING_PARAM
#define D AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[]={
{"probesize", NULL, OFFSET(probesize), FF_OPT_TYPE_INT, 32000, 32, INT_MAX, D}, /* 32000 from mpegts.c: 1.0 second at 24Mbit/s */
{"muxrate", "set mux rate", OFFSET(mux_rate), FF_OPT_TYPE_INT, DEFAULT, 0, INT_MAX, E},
{"packetsize", "set packet size", OFFSET(packet_size), FF_OPT_TYPE_INT, DEFAULT, 0, INT_MAX, E},
{"fflags", NULL, OFFSET(flags), FF_OPT_TYPE_FLAGS, DEFAULT, INT_MIN, INT_MAX, D|E, "fflags"},
{"ignidx", "ignore index", 0, FF_OPT_TYPE_CONST, AVFMT_FLAG_IGNIDX, INT_MIN, INT_MAX, D, "fflags"},
{"genpts", "generate pts", 0, FF_OPT_TYPE_CONST, AVFMT_FLAG_GENPTS, INT_MIN, INT_MAX, D, "fflags"},
{"track", " set the track number", OFFSET(track), FF_OPT_TYPE_INT, DEFAULT, 0, INT_MAX, E},
{"year", "set the year", OFFSET(year), FF_OPT_TYPE_INT, DEFAULT, INT_MIN, INT_MAX, E},
{"analyzeduration", "how many microseconds are analyzed to estimate duration", OFFSET(max_analyze_duration), FF_OPT_TYPE_INT, 3*AV_TIME_BASE, 0, INT_MAX, D},
{"cryptokey", "decryption key", OFFSET(key), FF_OPT_TYPE_BINARY, 0, 0, 0, D},
Panagiotis Issaris
committed
#undef E
#undef D
#undef DEFAULT
static const AVClass av_format_context_class = { "AVFormatContext", format_to_name, options };
static void avformat_get_context_defaults(AVFormatContext *s)
{
memset(s, 0, sizeof(AVFormatContext));
s->av_class = &av_format_context_class;
av_opt_set_defaults(s);
AVFormatContext *av_alloc_format_context(void)
{
AVFormatContext *ic;
ic = av_malloc(sizeof(AVFormatContext));
if (!ic) return ic;
avformat_get_context_defaults(ic);
return ic;
}
int av_open_input_stream(AVFormatContext **ic_ptr,
ByteIOContext *pb, const char *filename,
AVInputFormat *fmt, AVFormatParameters *ap)
{
int err;
AVFormatContext *ic;
AVFormatParameters default_ap;
if(!ap){
ap=&default_ap;
memset(ap, 0, sizeof(default_ap));
}
if(!ap->prealloced_context)
ic = av_alloc_format_context();
else
ic = *ic_ptr;
if (!ic) {
err = AVERROR(ENOMEM);
goto fail;
}
ic->iformat = fmt;
ic->pb = pb;
ic->duration = AV_NOPTS_VALUE;
ic->start_time = AV_NOPTS_VALUE;
av_strlcpy(ic->filename, filename, sizeof(ic->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(ENOMEM);
goto fail;
}
} else {
ic->priv_data = NULL;
}
err = ic->iformat->read_header(ic, ap);
if (err < 0)
goto fail;
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;
}
Daniel Kristjansson
committed
/** Size of probe buffer, for guessing file type from file contents. */
#define PROBE_BUF_MIN 2048
#define PROBE_BUF_MAX (1<<20)
Fabrice Bellard
committed
int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
Fabrice Bellard
committed
AVInputFormat *fmt,
int buf_size,
AVFormatParameters *ap)
int err, must_open_file, file_opened, probe_size;
Fabrice Bellard
committed
AVProbeData probe_data, *pd = &probe_data;
ByteIOContext *pb;
file_opened = 0;
pd->filename = "";
if (filename)
pd->filename = filename;
pd->buf = NULL;
Fabrice Bellard
committed
pd->buf_size = 0;
if (!fmt) {
/* guess format if no file can be opened */
/* 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)) {
pb= NULL; //FIXME this or memset(pb, 0, sizeof(ByteIOContext)); otherwise it is uninitialized
/* if no file needed do not try to open one */
if ((err=url_fopen(&pb, filename, URL_RDONLY)) < 0) {
Fabrice Bellard
committed
}
file_opened = 1;
url_setbufsize(pb, buf_size);
for(probe_size= PROBE_BUF_MIN; probe_size<=PROBE_BUF_MAX && !fmt; probe_size<<=1){
Michael Niedermayer
committed
int score= probe_size < PROBE_BUF_MAX ? AVPROBE_SCORE_MAX/4 : 0;
Fabrice Bellard
committed
/* read probe data */
Michael Niedermayer
committed
pd->buf= av_realloc(pd->buf, probe_size + AVPROBE_PADDING_SIZE);
pd->buf_size = get_buffer(pb, pd->buf, probe_size);
Wolfram Gloger
committed
memset(pd->buf+pd->buf_size, 0, AVPROBE_PADDING_SIZE);
if (url_fseek(pb, 0, SEEK_SET) < 0) {
url_fclose(pb);
if (url_fopen(&pb, filename, URL_RDONLY) < 0) {
Michael Niedermayer
committed
file_opened = 0;
err = AVERROR(EIO);
goto fail;
}
}
/* guess file format */
Michael Niedermayer
committed
fmt = av_probe_input_format2(pd, 1, &score);
Fabrice Bellard
committed
}
av_freep(&pd->buf);
Fabrice Bellard
committed
}
/* if still no format found, error */
if (!fmt) {
err = AVERROR_NOFMT;
goto fail;
/* check filename in case of an image number is expected */
if (fmt->flags & AVFMT_NEEDNUMBER) {
Michel Bardiaux
committed
if (!av_filename_number_test(filename)) {
goto fail;
err = av_open_input_stream(ic_ptr, pb, filename, fmt, ap);
if (err)
goto fail;
Fabrice Bellard
committed
return 0;
av_freep(&pd->buf);
if (file_opened)
url_fclose(pb);
Fabrice Bellard
committed
*ic_ptr = NULL;
return err;
/*******************************************************/
int av_read_packet(AVFormatContext *s, AVPacket *pkt)
return s->iformat->read_packet(s, pkt);
}
/**********************************************************/
Daniel Kristjansson
committed
/**
* 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) {
int bits_per_sample = av_get_bits_per_sample(enc->codec_id);
if (bits_per_sample) {
if (enc->channels == 0)
return -1;
frame_size = (size << 3) / (bits_per_sample * enc->channels);
/* used for example by ADPCM codecs */
if (enc->bit_rate == 0)
return -1;
frame_size = (size * 8 * enc->sample_rate) / enc->bit_rate;
}
} else {
frame_size = enc->frame_size;
}
return frame_size;
}
Daniel Kristjansson
committed
/**
* Return the frame duration in seconds, return 0 if not available.
*/
static void compute_frame_duration(int *pnum, int *pden, AVStream *st,
AVCodecParserContext *pc, AVPacket *pkt)
{
int frame_size;
*pnum = 0;
*pden = 0;
Michael Niedermayer
committed
switch(st->codec->codec_type) {
case CODEC_TYPE_VIDEO:
*pnum = st->time_base.num;
*pden = st->time_base.den;
Michael Niedermayer
committed
}else if(st->codec->time_base.num*1000LL > st->codec->time_base.den){
*pnum = st->codec->time_base.num;
*pden = st->codec->time_base.den;
if (pc && pc->repeat_pict) {
*pden *= 2;
*pnum = (*pnum) * (2 + pc->repeat_pict);
}
}
break;
case CODEC_TYPE_AUDIO:
Michael Niedermayer
committed
frame_size = get_audio_frame_size(st->codec, pkt->size);
if (frame_size < 0)
break;
*pnum = frame_size;
Michael Niedermayer
committed
*pden = st->codec->sample_rate;
break;
default:
break;
}
}
Michael Niedermayer
committed
static int is_intra_only(AVCodecContext *enc){
if(enc->codec_type == CODEC_TYPE_AUDIO){
return 1;
}else if(enc->codec_type == CODEC_TYPE_VIDEO){
switch(enc->codec_id){
case CODEC_ID_MJPEG:
case CODEC_ID_MJPEGB:
case CODEC_ID_LJPEG:
case CODEC_ID_RAWVIDEO:
case CODEC_ID_DVVIDEO:
case CODEC_ID_HUFFYUV:
Michael Niedermayer
committed
case CODEC_ID_ASV1:
case CODEC_ID_ASV2:
case CODEC_ID_VCR1:
return 1;
default: break;
}
}
return 0;
}
static void update_initial_timestamps(AVFormatContext *s, int stream_index,
int64_t dts, int64_t pts)
{
AVStream *st= s->streams[stream_index];
AVPacketList *pktl= s->packet_buffer;
if(st->first_dts != AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE)
return;
st->first_dts= dts - st->cur_dts;
st->cur_dts= dts;
for(; pktl; pktl= pktl->next){
if(pktl->pkt.stream_index != stream_index)
continue;
//FIXME think more about this check
if(pktl->pkt.pts != AV_NOPTS_VALUE && pktl->pkt.pts == pktl->pkt.dts)
pktl->pkt.pts += st->first_dts;
if(pktl->pkt.dts != AV_NOPTS_VALUE)
pktl->pkt.dts += st->first_dts;
Michael Niedermayer
committed
if(st->start_time == AV_NOPTS_VALUE && pktl->pkt.pts != AV_NOPTS_VALUE)
st->start_time= pktl->pkt.pts;
if (st->start_time == AV_NOPTS_VALUE)
st->start_time = pts;
static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
AVCodecParserContext *pc, AVPacket *pkt)
{
int num, den, presentation_delayed, delay, i;
Michael Niedermayer
committed
if(pkt->pts != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE && pkt->dts > pkt->pts && st->pts_wrap_bits<63
/*&& pkt->dts-(1LL<<st->pts_wrap_bits) < pkt->pts*/){
pkt->dts -= 1LL<<st->pts_wrap_bits;
}
if (pkt->duration == 0) {
compute_frame_duration(&num, &den, st, pc, pkt);
pkt->duration = av_rescale(1, num * (int64_t)st->time_base.den, den * (int64_t)st->time_base.num);
/* correct timestamps with byte offset if demuxers only have timestamps on packet boundaries */
if(pc && st->need_parsing == AVSTREAM_PARSE_TIMESTAMPS && pkt->size){
/* this will estimate bitrate based on this frame's duration and size */
offset = av_rescale(pc->offset, pkt->duration, pkt->size);
if(pkt->pts != AV_NOPTS_VALUE)
pkt->pts += offset;
if(pkt->dts != AV_NOPTS_VALUE)
pkt->dts += offset;
}
/* do we have a video B frame ? */
delay= st->codec->has_b_frames;
presentation_delayed = 0;
/* XXX: need has_b_frame, but cannot get it if the codec is
not initialized */
if (delay &&
pc && pc->pict_type != FF_B_TYPE)
presentation_delayed = 1;
/* This may be redundant, but it should not hurt. */
if(pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->pts > pkt->dts)
presentation_delayed = 1;
if(st->cur_dts == AV_NOPTS_VALUE){
st->cur_dts = 0; //FIXME maybe set it to 0 during init
// av_log(NULL, AV_LOG_DEBUG, "IN delayed:%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64" st:%d pc:%p\n", presentation_delayed, pkt->pts, pkt->dts, st->cur_dts, pkt->stream_index, pc);
/* interpolate PTS and DTS if they are not present */
if(delay <=1){
if (presentation_delayed) {
/* DTS = decompression time stamp */
/* PTS = presentation time stamp */
if (pkt->dts == AV_NOPTS_VALUE)
pkt->dts = st->last_IP_pts;
update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts);
if (pkt->dts == AV_NOPTS_VALUE)
pkt->dts = st->cur_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->last_IP_duration = pkt->duration;
st->cur_dts = pkt->dts + st->last_IP_duration;
st->last_IP_duration = pkt->duration;
st->last_IP_pts= pkt->pts;
/* cannot compute PTS if not present (we can compute it only
by knowing the futur */
} else if(pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE || pkt->duration){
if(pkt->pts != AV_NOPTS_VALUE && pkt->duration){
int64_t old_diff= FFABS(st->cur_dts - pkt->duration - pkt->pts);
int64_t new_diff= FFABS(st->cur_dts - pkt->pts);
if(old_diff < new_diff && old_diff < (pkt->duration>>3)){
pkt->pts += pkt->duration;
// av_log(NULL, AV_LOG_DEBUG, "id:%d old:%"PRId64" new:%"PRId64" dur:%d cur:%"PRId64" size:%d\n", pkt->stream_index, old_diff, new_diff, pkt->duration, st->cur_dts, pkt->size);
}
/* presentation is not delayed : PTS and DTS are the same */
if(pkt->pts == AV_NOPTS_VALUE)
pkt->pts = pkt->dts;
update_initial_timestamps(s, pkt->stream_index, pkt->pts, pkt->pts);
if(pkt->pts == AV_NOPTS_VALUE)
pkt->pts = st->cur_dts;
pkt->dts = pkt->pts;
st->cur_dts = pkt->pts + pkt->duration;
}
if(pkt->pts != AV_NOPTS_VALUE){
st->pts_buffer[0]= pkt->pts;
for(i=1; i<delay+1 && st->pts_buffer[i] == AV_NOPTS_VALUE; i++)
st->pts_buffer[i]= (i-delay-1) * pkt->duration;
for(i=0; i<delay && st->pts_buffer[i] > st->pts_buffer[i+1]; i++)
FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i+1]);
if(pkt->dts == AV_NOPTS_VALUE)
pkt->dts= st->pts_buffer[0];
update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); // this should happen on the first packet
if(pkt->dts > st->cur_dts)
st->cur_dts = pkt->dts;
}
// av_log(NULL, AV_LOG_ERROR, "OUTdelayed:%d/%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64"\n", presentation_delayed, delay, pkt->pts, pkt->dts, st->cur_dts);
/* update flags */
if(is_intra_only(st->codec))
pkt->flags |= PKT_FLAG_KEY;
else if (pc) {
pkt->flags = 0;
/* key frame computation */
if (pc->pict_type == FF_I_TYPE)
pkt->flags |= PKT_FLAG_KEY;
}
}
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) {
Michael Niedermayer
committed
if (!st->need_parsing || !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;
} else if (s->cur_len > 0 && st->discard < AVDISCARD_ALL) {
len = av_parser_parse(st->parser, st->codec, &pkt->data, &pkt->size,
Fabrice Bellard
committed
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->pos = s->cur_pkt.pos; // Isn't quite accurate but close.
pkt->duration = 0;
pkt->stream_index = st->index;
Fabrice Bellard
committed
pkt->pts = st->parser->pts;
pkt->dts = st->parser->dts;
pkt->destruct = av_destruct_packet_nofree;
compute_pkt_fields(s, st, st->parser, pkt);
if((s->iformat->flags & AVFMT_GENERIC_INDEX) && pkt->flags & PKT_FLAG_KEY){
av_add_index_entry(st, st->parser->frame_offset, pkt->dts,
0, 0, AVINDEX_KEYFRAME);
}
s->cur_st = NULL;
}
} else {
/* read next packet */
ret = av_read_packet(s, &s->cur_pkt);
if (ret == AVERROR(EAGAIN))
return ret;
/* return the last frames, if any */
for(i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
Michael Niedermayer
committed
if (st->parser && st->need_parsing) {
av_parser_parse(st->parser, st->codec,
&pkt->data, &pkt->size,
NULL, 0,
Fabrice Bellard
committed
AV_NOPTS_VALUE, AV_NOPTS_VALUE);
if (pkt->size)
goto got_packet;
}
}
/* no more packets: really terminates parsing */
st = s->streams[s->cur_pkt.stream_index];
Dominik Mierzejewski
committed
av_log(s, AV_LOG_DEBUG, "av_read_packet stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d\n",
s->cur_pkt.stream_index,
s->cur_pkt.pts,
s->cur_pkt.dts,
s->cur_pkt.size);
s->cur_st = st;
s->cur_ptr = s->cur_pkt.data;
s->cur_len = s->cur_pkt.size;
if (st->need_parsing && !st->parser) {
Michael Niedermayer
committed
st->parser = av_parser_init(st->codec->codec_id);
if (!st->parser) {
/* no parser available : just output the raw packets */
st->need_parsing = AVSTREAM_PARSE_NONE;
}else if(st->need_parsing == AVSTREAM_PARSE_HEADERS){
st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
if(st->parser && (s->iformat->flags & AVFMT_GENERIC_INDEX)){
st->parser->last_frame_offset=
st->parser->cur_offset= s->cur_pkt.pos;
}
Dominik Mierzejewski
committed
av_log(s, AV_LOG_DEBUG, "av_read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d\n",
pkt->stream_index,
pkt->pts,
pkt->dts,
pkt->size);
return 0;
Michael Niedermayer
committed
static AVPacket *add_to_pktbuf(AVFormatContext *s, AVPacket *pkt){
AVPacketList *pktl= s->packet_buffer;
AVPacketList **plast_pktl= &s->packet_buffer;
while(*plast_pktl) plast_pktl= &(*plast_pktl)->next; //FIXME maybe maintain pointer to the last?
pktl = av_mallocz(sizeof(AVPacketList));
if (!pktl)
return NULL;
/* add the packet in the buffered packet list */
*plast_pktl = pktl;
pktl->pkt= *pkt;
return &pktl->pkt;
}
int av_read_frame(AVFormatContext *s, AVPacket *pkt)
int eof=0;
const int genpts= s->flags & AVFMT_FLAG_GENPTS;
for(;;){
pktl = s->packet_buffer;
if (pktl) {
AVPacket *next_pkt= &pktl->pkt;
if(genpts && next_pkt->dts != AV_NOPTS_VALUE){
while(pktl && next_pkt->pts == AV_NOPTS_VALUE){
if( pktl->pkt.stream_index == next_pkt->stream_index
&& next_pkt->dts < pktl->pkt.dts
&& pktl->pkt.pts != pktl->pkt.dts //not b frame
/*&& pktl->pkt.dts != AV_NOPTS_VALUE*/){
next_pkt->pts= pktl->pkt.dts;
}
pktl= pktl->next;
}
pktl = s->packet_buffer;
}
if( next_pkt->pts != AV_NOPTS_VALUE
|| next_pkt->dts == AV_NOPTS_VALUE
|| !genpts || eof){
/* read packet from packet buffer, if there is data */
*pkt = *next_pkt;
s->packet_buffer = pktl->next;
av_free(pktl);
return 0;
}
}
if(genpts){
int ret= av_read_frame_internal(s, pkt);
if(ret<0){
if(pktl && ret != AVERROR(EAGAIN)){
eof=1;
continue;
}else
return ret;
}
Michael Niedermayer
committed
if(av_dup_packet(add_to_pktbuf(s, pkt)) < 0)
return AVERROR(ENOMEM);
}else{
assert(!s->packet_buffer);
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;
break;
s->packet_buffer = pktl->next;
av_free_packet(&pktl->pkt);
av_free(pktl);
Fabrice Bellard
committed
}
}
/*******************************************************/
/* 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];
Michael Niedermayer
committed
if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
return i;
}
}
return 0;
}
Daniel Kristjansson
committed
/**
* 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 = AV_NOPTS_VALUE; /* we set the current DTS to an unspecified origin */
void av_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp){
int i;
for(i = 0; i < s->nb_streams; i++) {
Nathan Kurz
committed
AVStream *st = s->streams[i];
st->cur_dts = av_rescale(timestamp,
Nathan Kurz
committed
st->time_base.den * (int64_t)ref_st->time_base.num,
st->time_base.num * (int64_t)ref_st->time_base.den);
Michael Niedermayer
committed
int av_add_index_entry(AVStream *st,