Newer
Older
* various utility functions for use within FFmpeg
* 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
Ronald S. Bultje
committed
#include "internal.h"
#include "libavcodec/opt.h"
#include "libavutil/avstring.h"
#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 use within FFmpeg
Daniel Kristjansson
committed
*/
unsigned avformat_version(void)
{
return LIBAVFORMAT_VERSION_INT;
}
Stefano Sabatini
committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/* fraction handling */
/**
* f = val + (num / den) + 0.5.
*
* 'num' is normalized so that it is such as 0 <= num < den.
*
* @param f fractional number
* @param val integer value
* @param num must be >= 0
* @param den must be >= 1
*/
static void av_frac_init(AVFrac *f, int64_t val, int64_t num, int64_t den)
{
num += (den >> 1);
if (num >= den) {
val += num / den;
num = num % den;
}
f->val = val;
f->num = num;
f->den = den;
}
/**
* Fractional addition to f: f = f + (incr / f->den).
*
* @param f fractional number
* @param incr increment, can be positive or negative
*/
static void av_frac_add(AVFrac *f, int64_t incr)
{
int64_t num, den;
num = f->num + incr;
den = f->den;
if (num < 0) {
f->val += num / den;
num = num % den;
if (num < 0) {
num += den;
f->val--;
}
} else if (num >= den) {
f->val += num / den;
num = num % den;
}
f->num = num;
}
/** head of registered input format linked list */
Michael Niedermayer
committed
AVInputFormat *first_iformat = NULL;
/** 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)
{
const char *ext, *p;
char ext1[32], *q;
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);
}
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 add 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;
}else if (score == *score_max)
fmt = NULL;
Fabrice Bellard
committed
}
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);
}
Michael Niedermayer
committed
static int set_codec_from_probe_data(AVStream *st, AVProbeData *pd, int score)
{
AVInputFormat *fmt;
fmt = av_probe_input_format2(pd, 1, &score);
if (fmt) {
if (!strcmp(fmt->name, "mp3")) {
Michael Niedermayer
committed
st->codec->codec_id = CODEC_ID_MP3;
st->codec->codec_type = CODEC_TYPE_AUDIO;
} else if (!strcmp(fmt->name, "ac3")) {
Michael Niedermayer
committed
st->codec->codec_id = CODEC_ID_AC3;
st->codec->codec_type = CODEC_TYPE_AUDIO;
} else if (!strcmp(fmt->name, "mpegvideo")) {
st->codec->codec_id = CODEC_ID_MPEG2VIDEO;
st->codec->codec_type = CODEC_TYPE_VIDEO;
} else if (!strcmp(fmt->name, "m4v")) {
st->codec->codec_id = CODEC_ID_MPEG4;
st->codec->codec_type = CODEC_TYPE_VIDEO;
} else if (!strcmp(fmt->name, "h264")) {
st->codec->codec_id = CODEC_ID_H264;
st->codec->codec_type = CODEC_TYPE_VIDEO;
}
Michael Niedermayer
committed
}
return !!fmt;
}
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},
{"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), FF_OPT_TYPE_INT, 1<<20, 0, INT_MAX, D},
{"rtbufsize", "max memory used for buffering real-time frames", OFFSET(max_picture_buffer), FF_OPT_TYPE_INT, 3041280, 0, INT_MAX, D}, /* defaults to 1s of 15fps 352x288 YUYV422 video */
{"fdebug", "print specific debug info", OFFSET(debug), FF_OPT_TYPE_FLAGS, DEFAULT, 0, INT_MAX, E|D, "fdebug"},
{"ts", NULL, 0, FF_OPT_TYPE_CONST, FF_FDEBUG_TS, INT_MIN, INT_MAX, E|D, "fdebug"},
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;
}
Ronald S. Bultje
committed
if (ic->iformat->read_header) {
err = ic->iformat->read_header(ic, ap);
if (err < 0)
goto fail;
Ronald S. Bultje
committed
}
ic->data_offset = url_ftell(ic->pb);
*ic_ptr = ic;
return 0;
fail:
if (ic) {
av_freep(&ic->priv_data);
for(i=0;i<ic->nb_streams;i++) {
AVStream *st = ic->streams[i];
if (st) {
av_free(st->priv_data);
av_free(st->codec->extradata);
}
av_free(st);
}
}
av_free(ic);
*ic_ptr = NULL;
return err;
}
/** 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)
Fabrice Bellard
committed
AVProbeData probe_data, *pd = &probe_data;
pd->filename = "";
if (filename)
pd->filename = filename;
pd->buf = NULL;
Fabrice Bellard
committed
pd->buf_size = 0;
if (!fmt) {
/* Do not open file if the format does not need it. XXX: specific
if (!fmt || !(fmt->flags & AVFMT_NOFILE)) {
/* if no file needed do not try to open one */
if ((err=url_fopen(&pb, filename, URL_RDONLY)) < 0) {
Fabrice Bellard
committed
}
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) {
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 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);
url_fclose(pb);
Fabrice Bellard
committed
*ic_ptr = NULL;
return err;
/*******************************************************/
static AVPacket *add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt,
AVPacketList **plast_pktl){
AVPacketList *pktl = av_mallocz(sizeof(AVPacketList));
Michael Niedermayer
committed
if (!pktl)
return NULL;
if (*packet_buffer)
(*plast_pktl)->next = pktl;
else
*packet_buffer = pktl;
Michael Niedermayer
committed
/* add the packet in the buffered packet list */
*plast_pktl = pktl;
pktl->pkt= *pkt;
return &pktl->pkt;
}
int av_read_packet(AVFormatContext *s, AVPacket *pkt)
for(;;){
AVPacketList *pktl = s->raw_packet_buffer;
if (pktl) {
*pkt = pktl->pkt;
if(s->streams[pkt->stream_index]->codec->codec_id != CODEC_ID_PROBE){
s->raw_packet_buffer = pktl->next;
av_free(pktl);
return 0;
}
}
av_init_packet(pkt);
ret= s->iformat->read_packet(s, pkt);
if (ret < 0)
return ret;
st= s->streams[pkt->stream_index];
switch(st->codec->codec_type){
case CODEC_TYPE_VIDEO:
if(s->video_codec_id) st->codec->codec_id= s->video_codec_id;
break;
case CODEC_TYPE_AUDIO:
if(s->audio_codec_id) st->codec->codec_id= s->audio_codec_id;
break;
case CODEC_TYPE_SUBTITLE:
if(s->subtitle_codec_id)st->codec->codec_id= s->subtitle_codec_id;
break;
}
if(!pktl && st->codec->codec_id!=CODEC_ID_PROBE)
return ret;
add_to_pktbuf(&s->raw_packet_buffer, pkt, &s->raw_packet_buffer_end);
if(st->codec->codec_id == CODEC_ID_PROBE){
AVProbeData *pd = &st->probe_data;
pd->buf = av_realloc(pd->buf, pd->buf_size+pkt->size+AVPROBE_PADDING_SIZE);
memcpy(pd->buf+pd->buf_size, pkt->data, pkt->size);
pd->buf_size += pkt->size;
memset(pd->buf+pd->buf_size, 0, AVPROBE_PADDING_SIZE);
Michael Niedermayer
committed
if(av_log2(pd->buf_size) != av_log2(pd->buf_size - pkt->size)){
set_codec_from_probe_data(st, pd, 1);
if(st->codec->codec_id != CODEC_ID_PROBE){
pd->buf_size=0;
av_freep(&pd->buf);
}
Michael Niedermayer
committed
}
}
/**********************************************************/
Daniel Kristjansson
committed
/**
* Get the number of samples of an audio frame. Return -1 on error.
Daniel Kristjansson
committed
*/
static int get_audio_frame_size(AVCodecContext *enc, int size)
{
int frame_size;
Michael Niedermayer
committed
if(enc->codec_id == CODEC_ID_VORBIS)
return -1;
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.
Daniel Kristjansson
committed
*/
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:
Michael Niedermayer
committed
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 || st->cur_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;
Michael Niedermayer
committed
static void update_initial_durations(AVFormatContext *s, AVStream *st, AVPacket *pkt)
{
AVPacketList *pktl= s->packet_buffer;
int64_t cur_dts= 0;
if(st->first_dts != AV_NOPTS_VALUE){
cur_dts= st->first_dts;
for(; pktl; pktl= pktl->next){
if(pktl->pkt.stream_index == pkt->stream_index){
if(pktl->pkt.pts != pktl->pkt.dts || pktl->pkt.dts != AV_NOPTS_VALUE || pktl->pkt.duration)
break;
cur_dts -= pkt->duration;
}
}
pktl= s->packet_buffer;
st->first_dts = cur_dts;
}else if(st->cur_dts)
return;
Michael Niedermayer
committed
for(; pktl; pktl= pktl->next){
if(pktl->pkt.stream_index != pkt->stream_index)
continue;
if(pktl->pkt.pts == pktl->pkt.dts && pktl->pkt.dts == AV_NOPTS_VALUE
&& !pktl->pkt.duration){
pktl->pkt.dts= cur_dts;
if(!st->codec->has_b_frames)
pktl->pkt.pts= cur_dts;
cur_dts += pkt->duration;
Michael Niedermayer
committed
pktl->pkt.duration= pkt->duration;
}else
break;
}
if(st->first_dts == AV_NOPTS_VALUE)
st->cur_dts= cur_dts;
Michael Niedermayer
committed
}
static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
AVCodecParserContext *pc, AVPacket *pkt)
{
int num, den, presentation_delayed, delay, i;
/* 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;
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;
}
Michael Niedermayer
committed
// some mpeg2 in mpeg-ps lack dts (issue171 / input_file.mpg)
// we take the conservative approach and discard both
// Note, if this is misbehaving for a H.264 file then possibly presentation_delayed is not set correctly.
if(delay==1 && pkt->dts == pkt->pts && pkt->dts != AV_NOPTS_VALUE && presentation_delayed){
av_log(s, AV_LOG_ERROR, "invalid dts/pts combination\n");
pkt->dts= pkt->pts= AV_NOPTS_VALUE;
}
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);
Michael Niedermayer
committed
if(pkt->duration != 0 && s->packet_buffer)
Michael Niedermayer
committed
update_initial_durations(s, st, pkt);
/* 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;
}
/* 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;
// 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 */
Michael Niedermayer
committed
if(delay==0 || (delay==1 && pc)){
/* DTS = decompression timestamp */
/* PTS = presentation timestamp */
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;
if(pkt->dts != AV_NOPTS_VALUE)
st->last_IP_duration = pkt->duration;
st->last_IP_pts= pkt->pts;
/* cannot compute PTS if not present (we can compute it only
} 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;
if(pkt->pts != AV_NOPTS_VALUE)
Baptiste Coudurier
committed
if(pkt->pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY){
st->pts_buffer[0]= pkt->pts;
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) {
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){
ff_reduce_index(s, st->index);
av_add_index_entry(st, st->parser->frame_offset, pkt->dts,
0, 0, AVINDEX_KEYFRAME);
}
av_free_packet(&s->cur_pkt);