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)
static int match_format(const char *name, const char *names)
{
const char *p;
int len, namelen;
if (!name || !names)
return 0;
namelen = strlen(name);
while ((p = strchr(names, ','))) {
len = FFMAX(p - names, namelen);
if (!strncasecmp(name, names, len))
return 1;
names = p+1;
}
return !strcasecmp(name, names);
}
AVOutputFormat *guess_format(const char *short_name, const char *filename,
Fabrice Bellard
committed
const char *mime_type)
Fabrice Bellard
committed
AVOutputFormat *fmt, *fmt_found;
#if 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;
#if 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 (match_format(short_name, fmt->name))
Fabrice Bellard
committed
return fmt;
}
return NULL;
}
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
Reimar Döffinger
committed
av_shrink_packet(pkt, ret);
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;
} else if (!strcmp(fmt->name, "dts")) {
st->codec->codec_id = CODEC_ID_DTS;
st->codec->codec_type = CODEC_TYPE_AUDIO;
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.
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 = avformat_alloc_context();
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);
#if LIBAVFORMAT_VERSION_MAJOR < 53
ff_metadata_demux_compat(ic);
#endif
*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)
int ret, i;
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->streams[pkt->stream_index]->probe_packets){
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) {
if (!pktl || ret == AVERROR(EAGAIN))
return ret;
for (i = 0; i < s->nb_streams; i++)
s->streams[i]->probe_packets = 0;
continue;
}
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 ||
!st->probe_packets))
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;
--st->probe_packets;
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 = ((int64_t)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) {
*pnum = (*pnum) * (1 + 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;
if (pc && pc->pict_type == FF_B_TYPE)
st->codec->has_b_frames = 1;
/* 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_WARNING, "invalid dts/pts combination\n");
Michael Niedermayer
committed
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;
}
if (pc && pc->dts_sync_point >= 0) {
// we have synchronization info from the parser
int64_t den = st->codec->time_base.den * (int64_t) st->time_base.num;
if (den > 0) {
int64_t num = st->codec->time_base.num * (int64_t) st->time_base.den;
if (pkt->dts != AV_NOPTS_VALUE) {
// got DTS from the stream, update reference timestamp
st->reference_dts = pkt->dts - pc->dts_ref_dts_delta * num / den;
pkt->pts = pkt->dts + pc->pts_dts_delta * num / den;
} else if (st->reference_dts != AV_NOPTS_VALUE) {
// compute DTS based on reference timestamp
pkt->dts = st->reference_dts + pc->dts_ref_dts_delta * num / den;
pkt->pts = pkt->dts + pc->pts_dts_delta * num / den;
}
if (pc->dts_sync_point > 0)
st->reference_dts = pkt->dts; // new reference
}
}
/* 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
//We skip H264 currently because delay and has_b_frames are not reliably set
if((delay==0 || (delay==1 && pc)) && st->codec->codec_id != CODEC_ID_H264){
/* 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];
Michael Niedermayer
committed
if(st->codec->codec_id == CODEC_ID_H264){ //we skiped it above so we try here
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->key_frame == 1)
pkt->flags |= PKT_FLAG_KEY;
else if (pc->key_frame == -1 && pc->pict_type == FF_I_TYPE)
pkt->flags |= PKT_FLAG_KEY;
if (pc)
pkt->convergence_duration = pc->convergence_duration;
}
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 */
Michael Niedermayer
committed
*pkt = st->cur_pkt; st->cur_pkt.data= NULL;
compute_pkt_fields(s, st, NULL, pkt);
s->cur_st = NULL;
Reimar Döffinger
committed
if ((s->iformat->flags & AVFMT_GENERIC_INDEX) &&
(pkt->flags & PKT_FLAG_KEY) && pkt->dts != AV_NOPTS_VALUE) {
ff_reduce_index(s, st->index);
av_add_index_entry(st, pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME);
}
Michael Niedermayer
committed
} else if (st->cur_len > 0 && st->discard < AVDISCARD_ALL) {
Ivan Schreter
committed
len = av_parser_parse2(st->parser, st->codec, &pkt->data, &pkt->size,
st->cur_ptr, st->cur_len,
st->cur_pkt.pts, st->cur_pkt.dts,
st->cur_pkt.pos);
Michael Niedermayer
committed
st->cur_pkt.pts = AV_NOPTS_VALUE;
st->cur_pkt.dts = AV_NOPTS_VALUE;
/* increment read pointer */
Michael Niedermayer
committed
st->cur_ptr += len;
st->cur_len -= len;
/* return packet if any */
if (pkt->size) {
pkt->duration = 0;
pkt->stream_index = st->index;
Fabrice Bellard
committed
pkt->pts = st->parser->pts;
pkt->dts = st->parser->dts;
Ivan Schreter
committed
pkt->pos = st->parser->pos;
pkt->destruct = NULL;
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);
}
Michael Niedermayer
committed
av_free_packet(&st->cur_pkt);
s->cur_st = NULL;
}
} else {
Michael Niedermayer
committed
AVPacket cur_pkt;
/* read next packet */
Michael Niedermayer
committed
ret = av_read_packet(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) {
Ivan Schreter
committed
av_parser_parse2(st->parser, st->codec,
&pkt->data, &pkt->size,
NULL, 0,
Ivan Schreter
committed
AV_NOPTS_VALUE, AV_NOPTS_VALUE,
AV_NOPTS_VALUE);
if (pkt->size)
goto got_packet;
}
}
/* no more packets: really terminate parsing */
Michael Niedermayer
committed
st = s->streams[cur_pkt.stream_index];
st->cur_pkt= cur_pkt;
Michael Niedermayer
committed
if(st->cur_pkt.pts != AV_NOPTS_VALUE &&
st->cur_pkt.dts != AV_NOPTS_VALUE &&
st->cur_pkt.pts < st->cur_pkt.dts){
av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d\n",
Michael Niedermayer
committed
st->cur_pkt.stream_index,
st->cur_pkt.pts,
st->cur_pkt.dts,
st->cur_pkt.size);
// av_free_packet(&st->cur_pkt);
av_log(s, AV_LOG_DEBUG, "av_read_packet stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, flags=%d\n",
Michael Niedermayer
committed
st->cur_pkt.stream_index,
st->cur_pkt.pts,
st->cur_pkt.dts,
st->cur_pkt.size,
st->cur_pkt.flags);
s->cur_st = st;
Michael Niedermayer
committed
st->cur_ptr = st->cur_pkt.data;
st->cur_len = st->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->next_frame_offset=
Michael Niedermayer
committed
st->parser->cur_offset= st->cur_pkt.pos;
av_log(s, AV_LOG_DEBUG, "av_read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, flags=%d\n",
pkt->stream_index,
pkt->pts,
pkt->dts,
}
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;
}
if(av_dup_packet(add_to_pktbuf(&s->packet_buffer, pkt,
&s->packet_buffer_end)) < 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
}
while(s->raw_packet_buffer){
pktl = s->raw_packet_buffer;
s->raw_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 first_audio_index = -1;
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;
}
if (first_audio_index < 0 && st->codec->codec_type == CODEC_TYPE_AUDIO)
first_audio_index = i;
return first_audio_index >= 0 ? first_audio_index : 0;
Daniel Kristjansson
committed
/**
* Flush the frame reader.
*/
static void av_read_frame_flush(AVFormatContext *s)
{
AVStream *st;
int i;
flush_packet_queue(s);
Michael Niedermayer
committed
s->cur_st = NULL;
/* 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;
Michael Niedermayer
committed
av_free_packet(&st->cur_pkt);
st->cur_dts = AV_NOPTS_VALUE; /* we set the current DTS to an unspecified origin */
st->reference_dts = AV_NOPTS_VALUE;
Michael Niedermayer
committed
/* fail safe */
st->cur_ptr = NULL;
st->cur_len = 0;
st->probe_packets = MAX_PROBE_PACKETS;
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);
void ff_reduce_index(AVFormatContext *s, int stream_index)
{
AVStream *st= s->streams[stream_index];
unsigned int max_entries= s->max_index_size / sizeof(AVIndexEntry);
if((unsigned)st->nb_index_entries >= max_entries){
int i;
for(i=0; 2*i<st->nb_index_entries; i++)
st->index_entries[i]= st->index_entries[2*i];
st->nb_index_entries= i;
}
}
Michael Niedermayer
committed
int av_add_index_entry(AVStream *st,
int64_t pos, int64_t timestamp, int size, int distance, int flags)
{
AVIndexEntry *entries, *ie;
int index;
if((unsigned)st->nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry))
return -1;
entries = av_fast_realloc(st->index_entries,
&st->index_entries_allocated_size,
(st->nb_index_entries + 1) *
sizeof(AVIndexEntry));
if(!entries)
return -1;
st->index_entries= entries;
index= av_index_search_timestamp(st, timestamp, AVSEEK_FLAG_ANY);
Michael Niedermayer
committed
index= st->nb_index_entries++;
ie= &entries[index];
assert(index==0 || ie[-1].timestamp < timestamp);
}else{
ie= &entries[index];
if(ie->timestamp != timestamp){
if(ie->timestamp <= timestamp)
return -1;
memmove(entries + index + 1, entries + index, sizeof(AVIndexEntry)*(st->nb_index_entries - index));
st->nb_index_entries++;
}else if(ie->pos == pos && distance < ie->min_distance) //do not reduce the distance
Michael Niedermayer
committed
}
ie->pos = pos;
ie->timestamp = timestamp;
Michael Niedermayer
committed
ie->min_distance= distance;
ie->flags = flags;
Michael Niedermayer
committed
return index;
Michael Niedermayer
committed
int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp,
AVIndexEntry *entries= st->index_entries;
int nb_entries= st->nb_index_entries;
int a, b, m;
int64_t timestamp;
while (b - a > 1) {
m = (a + b) >> 1;
timestamp = entries[m].timestamp;
if(timestamp >= wanted_timestamp)
b = m;
if(timestamp <= wanted_timestamp)
m= (flags & AVSEEK_FLAG_BACKWARD) ? a : b;
if(!(flags & AVSEEK_FLAG_ANY)){
while(m>=0 && m<nb_entries && !(entries[m].flags & AVINDEX_KEYFRAME)){
m += (flags & AVSEEK_FLAG_BACKWARD) ? -1 : 1;
}
}
Michael Niedermayer
committed
int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){
Michael Niedermayer
committed
AVInputFormat *avif= s->iformat;
int64_t av_uninit(pos_min), av_uninit(pos_max), pos, pos_limit;
Michael Niedermayer
committed
int64_t ts_min, ts_max, ts;
Michael Niedermayer
committed
int index;
Michael Niedermayer
committed
AVStream *st;
if (stream_index < 0)
return -1;
Michael Niedermayer
committed
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "read_seek: %d %"PRId64"\n", stream_index, target_ts);
Michael Niedermayer
committed
#endif
ts_max=
ts_min= AV_NOPTS_VALUE;
pos_limit= -1; //gcc falsely says it may be uninitialized
Michael Niedermayer
committed
st= s->streams[stream_index];
if(st->index_entries){
AVIndexEntry *e;
index= av_index_search_timestamp(st, target_ts, flags | AVSEEK_FLAG_BACKWARD); //FIXME whole func must be checked for non-keyframe entries in index case, especially read_timestamp()
Michael Niedermayer
committed
e= &st->index_entries[index];
if(e->timestamp <= target_ts || e->pos == e->min_distance){
pos_min= e->pos;
ts_min= e->timestamp;
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "using cached pos_min=0x%"PRIx64" dts_min=%"PRId64"\n",
pos_min,ts_min);
Michael Niedermayer
committed
#endif
}else{
assert(index==0);
}
index= av_index_search_timestamp(st, target_ts, flags & ~AVSEEK_FLAG_BACKWARD);
assert(index < st->nb_index_entries);
if(index >= 0){
Michael Niedermayer
committed
e= &st->index_entries[index];
assert(e->timestamp >= target_ts);
pos_max= e->pos;
ts_max= e->timestamp;
pos_limit= pos_max - e->min_distance;
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64" dts_max=%"PRId64"\n",
pos_max,pos_limit, ts_max);
Michael Niedermayer
committed
#endif
}
}
Michael Niedermayer
committed
pos= av_gen_search(s, stream_index, target_ts, pos_min, pos_max, pos_limit, ts_min, ts_max, flags, &ts, avif->read_timestamp);
if(pos<0)
return -1;
/* do the seek */
url_fseek(s->pb, pos, SEEK_SET);
Michael Niedermayer
committed
av_update_cur_dts(s, st, ts);
return 0;
}
int64_t av_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, int64_t pos_min, int64_t pos_max, int64_t pos_limit, int64_t ts_min, int64_t ts_max, int flags, int64_t *ts_ret, int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )){
int64_t pos, ts;
int64_t start_pos, filesize;
int no_change;
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "gen_seek: %d %"PRId64"\n", stream_index, target_ts);
#endif
Michael Niedermayer
committed
if(ts_min == AV_NOPTS_VALUE){
pos_min = s->data_offset;
Michael Niedermayer
committed
ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
Michael Niedermayer
committed
if (ts_min == AV_NOPTS_VALUE)
return -1;
}
if(ts_max == AV_NOPTS_VALUE){
int step= 1024;
filesize = url_fsize(s->pb);
Kenneth Aafløy
committed
pos_max = filesize - 1;
Michael Niedermayer
committed
do{
pos_max -= step;
Michael Niedermayer
committed
ts_max = read_timestamp(s, stream_index, &pos_max, pos_max + step);
Michael Niedermayer
committed
step += step;
}while(ts_max == AV_NOPTS_VALUE && pos_max >= step);
if (ts_max == AV_NOPTS_VALUE)
return -1;
Michael Niedermayer
committed
for(;;){
int64_t tmp_pos= pos_max + 1;
Michael Niedermayer
committed
int64_t tmp_ts= read_timestamp(s, stream_index, &tmp_pos, INT64_MAX);
Michael Niedermayer
committed
if(tmp_ts == AV_NOPTS_VALUE)
break;
ts_max= tmp_ts;
pos_max= tmp_pos;
Kenneth Aafløy
committed
if(tmp_pos >= filesize)
break;
Michael Niedermayer
committed
}
pos_limit= pos_max;
}
if(ts_min > ts_max){
return -1;
}else if(ts_min == ts_max){
pos_limit= pos_min;
}
Michael Niedermayer
committed
no_change=0;
while (pos_min < pos_limit) {
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%"PRId64" dts_max=%"PRId64"\n",
Michael Niedermayer
committed
pos_min, pos_max,
ts_min, ts_max);
#endif
assert(pos_limit <= pos_max);
if(no_change==0){
int64_t approximate_keyframe_distance= pos_max - pos_limit;
// interpolate position (better than dichotomy)
pos = av_rescale(target_ts - ts_min, pos_max - pos_min, ts_max - ts_min)
+ pos_min - approximate_keyframe_distance;
Michael Niedermayer
committed
}else if(no_change==1){
// bisection, if interpolation failed to change min or max pos last time
pos = (pos_min + pos_limit)>>1;
}else{
/* linear search if bisection failed, can only happen if there
are very few or no keyframes between min/max */
Michael Niedermayer
committed
pos=pos_min;
}
if(pos <= pos_min)
pos= pos_min + 1;
else if(pos > pos_limit)
pos= pos_limit;
start_pos= pos;
Michael Niedermayer
committed
ts = read_timestamp(s, stream_index, &pos, INT64_MAX); //may pass pos_limit instead of -1
Michael Niedermayer
committed
if(pos == pos_max)
no_change++;
else
no_change=0;
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "%"PRId64" %"PRId64" %"PRId64" / %"PRId64" %"PRId64" %"PRId64" target:%"PRId64" limit:%"PRId64" start:%"PRId64" noc:%d\n",
pos_min, pos, pos_max, ts_min, ts, ts_max, target_ts, pos_limit,
start_pos, no_change);
Michael Niedermayer
committed
#endif
if(ts == AV_NOPTS_VALUE){
av_log(s, AV_LOG_ERROR, "read_timestamp() failed in the middle\n");
return -1;
}
Michael Niedermayer
committed
assert(ts != AV_NOPTS_VALUE);
Michael Niedermayer
committed
pos_limit = start_pos - 1;
pos_max = pos;
ts_max = ts;
Michael Niedermayer
committed
pos_min = pos;
ts_min = ts;
}
}
pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max;
ts = (flags & AVSEEK_FLAG_BACKWARD) ? ts_min : ts_max;
Michael Niedermayer
committed
#ifdef DEBUG_SEEK
pos_min = pos;
Michael Niedermayer
committed
ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
Michael Niedermayer
committed
pos_min++;
Michael Niedermayer
committed
ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
av_log(s, AV_LOG_DEBUG, "pos=0x%"PRIx64" %"PRId64"<=%"PRId64"<=%"PRId64"\n",
Michael Niedermayer
committed
pos, ts_min, target_ts, ts_max);
#endif
Michael Niedermayer
committed
*ts_ret= ts;
return pos;
Michael Niedermayer
committed
}
static int av_seek_frame_byte(AVFormatContext *s, int stream_index, int64_t pos, int flags){
int64_t pos_min, pos_max;
#if 0
AVStream *st;
if (stream_index < 0)
return -1;
st= s->streams[stream_index];
#endif
pos_min = s->data_offset;
pos_max = url_fsize(s->pb) - 1;
if (pos < pos_min) pos= pos_min;
else if(pos > pos_max) pos= pos_max;
url_fseek(s->pb, pos, SEEK_SET);
#endif
return 0;
}
static int av_seek_frame_generic(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags)
int index, ret;
AVStream *st;
AVIndexEntry *ie;
st = s->streams[stream_index];
index = av_index_search_timestamp(st, timestamp, flags);
if(index < 0 || index==st->nb_index_entries-1){
int i;
AVPacket pkt;
if(st->nb_index_entries){
assert(st->index_entries);
ie= &st->index_entries[st->nb_index_entries-1];
if ((ret = url_fseek(s->pb, ie->pos, SEEK_SET)) < 0)
return ret;
av_update_cur_dts(s, st, ie->timestamp);
if ((ret = url_fseek(s->pb, s->data_offset, SEEK_SET)) < 0)
return ret;
}
int ret;
do{
ret = av_read_frame(s, &pkt);
}while(ret == AVERROR(EAGAIN));
if(ret<0)
break;
av_free_packet(&pkt);
if(stream_index == pkt.stream_index){
if((pkt.flags & PKT_FLAG_KEY) && pkt.dts > timestamp)
break;
}
}
index = av_index_search_timestamp(st, timestamp, flags);
}
if (index < 0)
return -1;
av_read_frame_flush(s);
if (s->iformat->read_seek){
if(s->iformat->read_seek(s, stream_index, timestamp, flags) >= 0)
return 0;
}
ie = &st->index_entries[index];
if ((ret = url_fseek(s->pb, ie->pos, SEEK_SET)) < 0)
return ret;
return 0;
}
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
AVStream *st;
av_read_frame_flush(s);
if(flags & AVSEEK_FLAG_BYTE)
return av_seek_frame_byte(s, stream_index, timestamp, flags);
if(stream_index < 0){
stream_index= av_find_default_stream_index(s);
if(stream_index < 0)
return -1;
/* timestamp for default must be expressed in AV_TIME_BASE units */
timestamp = av_rescale(timestamp, st->time_base.den, AV_TIME_BASE * (int64_t)st->time_base.num);
}
/* first, we try the format specific seek */
if (s->iformat->read_seek)
ret = s->iformat->read_seek(s, stream_index, timestamp, flags);
else
ret = -1;
if (ret >= 0) {
return 0;
}
Michael Niedermayer
committed
if(s->iformat->read_timestamp)
return av_seek_frame_binary(s, stream_index, timestamp, flags);
Michael Niedermayer
committed
else
return av_seek_frame_generic(s, stream_index, timestamp, flags);
int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
{
if(min_ts > ts || max_ts < ts)
return -1;
av_read_frame_flush(s);
if (s->iformat->read_seek2)
return s->iformat->read_seek2(s, stream_index, min_ts, ts, max_ts, flags);
if(s->iformat->read_timestamp){
//try to seek via read_timestamp()
}
//Fallback to old API if new is not implemented but old is
//Note the old has somewat different sematics
if(s->iformat->read_seek || 1)
Michael Niedermayer
committed
return av_seek_frame(s, stream_index, ts, flags | (ts - min_ts > (uint64_t)(max_ts - ts) ? AVSEEK_FLAG_BACKWARD : 0));
// try some generic seek like av_seek_frame_generic() but with new ts semantics
}
/*******************************************************/
Fabrice Bellard
committed
Daniel Kristjansson
committed
/**
* Returns TRUE if the stream has accurate duration in any stream.
Daniel Kristjansson
committed
*
* @return TRUE if the stream has accurate duration for at least one component.
Daniel Kristjansson
committed
*/
static int av_has_duration(AVFormatContext *ic)
Fabrice Bellard
committed
{
int i;
AVStream *st;
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->duration != AV_NOPTS_VALUE)
Fabrice Bellard
committed
return 1;
}
return 0;
}
Daniel Kristjansson
committed
/**
* Estimate the stream timings from the one of each components.
*
* Also computes the global bitrate if possible.
*/
Fabrice Bellard
committed
static void av_update_stream_timings(AVFormatContext *ic)
{
int64_t start_time, start_time1, end_time, end_time1;
int64_t duration, duration1;
Fabrice Bellard
committed
int i;
AVStream *st;
start_time = INT64_MAX;
end_time = INT64_MIN;
duration = INT64_MIN;
Fabrice Bellard
committed
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time != AV_NOPTS_VALUE && st->time_base.den) {
start_time1= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
if (start_time1 < start_time)
start_time = start_time1;
Fabrice Bellard
committed
if (st->duration != AV_NOPTS_VALUE) {
end_time1 = start_time1
+ av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q);
Fabrice Bellard
committed
if (end_time1 > end_time)
end_time = end_time1;
}
}
if (st->duration != AV_NOPTS_VALUE) {
duration1 = av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q);
if (duration1 > duration)
duration = duration1;
}
Fabrice Bellard
committed
}
if (start_time != INT64_MAX) {
Fabrice Bellard
committed
ic->start_time = start_time;
if (end_time != INT64_MIN) {
if (end_time - start_time > duration)
duration = end_time - start_time;
}
}
if (duration != INT64_MIN) {
ic->duration = duration;
if (ic->file_size > 0) {
ic->bit_rate = (double)ic->file_size * 8.0 * AV_TIME_BASE /
(double)ic->duration;
Fabrice Bellard
committed
}
}
}
static void fill_all_stream_timings(AVFormatContext *ic)
{
int i;
AVStream *st;
av_update_stream_timings(ic);
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time == AV_NOPTS_VALUE) {
if(ic->start_time != AV_NOPTS_VALUE)
st->start_time = av_rescale_q(ic->start_time, AV_TIME_BASE_Q, st->time_base);
if(ic->duration != AV_NOPTS_VALUE)
st->duration = av_rescale_q(ic->duration, AV_TIME_BASE_Q, st->time_base);
Fabrice Bellard
committed
}
}
}
static void av_estimate_timings_from_bit_rate(AVFormatContext *ic)
{
int64_t filesize, duration;
int bit_rate, i;
AVStream *st;
/* if bit_rate is already set, we believe it */
if (ic->bit_rate == 0) {
bit_rate = 0;
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
Michael Niedermayer
committed
bit_rate += st->codec->bit_rate;
Fabrice Bellard
committed
}
ic->bit_rate = bit_rate;
}
/* if duration is already set, we believe it */
if (ic->duration == AV_NOPTS_VALUE &&
ic->bit_rate != 0 &&
Fabrice Bellard
committed
ic->file_size != 0) {
filesize = ic->file_size;
if (filesize > 0) {
for(i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
duration= av_rescale(8*filesize, st->time_base.den, ic->bit_rate*(int64_t)st->time_base.num);
if (st->duration == AV_NOPTS_VALUE)
Fabrice Bellard
committed
st->duration = duration;
}
}
}
}
#define DURATION_MAX_READ_SIZE 250000
/* only usable for MPEG-PS streams */
static void av_estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
Fabrice Bellard
committed
{
AVPacket pkt1, *pkt = &pkt1;
AVStream *st;
int read_size, i, ret;
Fabrice Bellard
committed
int64_t filesize, offset, duration;
ic->cur_st = NULL;
/* flush packet queue */
flush_packet_queue(ic);
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
if (st->parser) {
av_parser_close(st->parser);
st->parser= NULL;
Michael Niedermayer
committed
av_free_packet(&st->cur_pkt);
Fabrice Bellard
committed
/* we read the first packets to get the first PTS (not fully
accurate, but it is enough now) */
url_fseek(ic->pb, 0, SEEK_SET);
Fabrice Bellard
committed
read_size = 0;
for(;;) {
if (read_size >= DURATION_MAX_READ_SIZE)
break;
/* if all info is available, we can stop */
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time == AV_NOPTS_VALUE)
break;
}
if (i == ic->nb_streams)
break;
do{
ret = av_read_packet(ic, pkt);
}while(ret == AVERROR(EAGAIN));
Fabrice Bellard
committed
if (ret != 0)
break;
read_size += pkt->size;
st = ic->streams[pkt->stream_index];
if (pkt->pts != AV_NOPTS_VALUE) {
if (st->start_time == AV_NOPTS_VALUE)
Michael Niedermayer
committed
}
av_free_packet(pkt);
}
Fabrice Bellard
committed
/* estimate the end time (duration) */
/* XXX: may need to support wrapping */
filesize = ic->file_size;
offset = filesize - DURATION_MAX_READ_SIZE;
if (offset < 0)
offset = 0;
url_fseek(ic->pb, offset, SEEK_SET);
Fabrice Bellard
committed
read_size = 0;
for(;;) {
if (read_size >= DURATION_MAX_READ_SIZE)
break;
do{
ret = av_read_packet(ic, pkt);
}while(ret == AVERROR(EAGAIN));
Fabrice Bellard
committed
if (ret != 0)
break;
read_size += pkt->size;
st = ic->streams[pkt->stream_index];
if (pkt->pts != AV_NOPTS_VALUE &&
st->start_time != AV_NOPTS_VALUE) {
Fabrice Bellard
committed
duration = end_time - st->start_time;
if (duration > 0) {
if (st->duration == AV_NOPTS_VALUE ||
st->duration < duration)
st->duration = duration;
}
}
av_free_packet(pkt);
}
Fabrice Bellard
committed
url_fseek(ic->pb, old_offset, SEEK_SET);
for(i=0; i<ic->nb_streams; i++){
st= ic->streams[i];
st->cur_dts= st->first_dts;
Fabrice Bellard
committed
}
static void av_estimate_timings(AVFormatContext *ic, int64_t old_offset)
Fabrice Bellard
committed
{
int64_t file_size;
/* get the file size, if possible */
if (ic->iformat->flags & AVFMT_NOFILE) {
file_size = 0;
} else {
file_size = url_fsize(ic->pb);
Fabrice Bellard
committed
if (file_size < 0)
file_size = 0;
}
ic->file_size = file_size;
if ((!strcmp(ic->iformat->name, "mpeg") ||
!strcmp(ic->iformat->name, "mpegts")) &&
file_size && !url_is_streamed(ic->pb)) {
Fabrice Bellard
committed
/* get accurate estimate from the PTSes */
av_estimate_timings_from_pts(ic, old_offset);
} else if (av_has_duration(ic)) {
/* at least one component has timings - we use them for all
Fabrice Bellard
committed
the components */
fill_all_stream_timings(ic);
} else {
Fabrice Bellard
committed
av_estimate_timings_from_bit_rate(ic);
}
av_update_stream_timings(ic);
#if 0
{
int i;
AVStream *st;
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
printf("%d: start_time: %0.3f duration: %0.3f\n",
i, (double)st->start_time / AV_TIME_BASE,
Fabrice Bellard
committed
(double)st->duration / AV_TIME_BASE);
}
printf("stream: start_time: %0.3f duration: %0.3f bitrate=%d kb/s\n",
(double)ic->start_time / AV_TIME_BASE,
Fabrice Bellard
committed
(double)ic->duration / AV_TIME_BASE,
ic->bit_rate / 1000);
}
#endif
}
Fabrice Bellard
committed
static int has_codec_parameters(AVCodecContext *enc)
{
int val;
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
Baptiste Coudurier
committed
val = enc->sample_rate && enc->channels && enc->sample_fmt != SAMPLE_FMT_NONE;
if(!enc->frame_size &&
(enc->codec_id == CODEC_ID_VORBIS ||
enc->codec_id == CODEC_ID_AAC))
return 0;
Loading
Loading full blame...