blob: 5bc1798b4efcca3524f82361fb328ada098eafd7 [file] [log] [blame]
/*
* various utility functions for use within FFmpeg
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
* 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
* version 2.1 of the License, or (at your option) any later version.
*
* 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
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdarg.h>
#include <stdint.h>
#include "config.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/dict.h"
#include "libavutil/internal.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#include "libavutil/time.h"
#include "libavutil/time_internal.h"
#include "libavutil/timestamp.h"
#include "libavcodec/bytestream.h"
#include "libavcodec/internal.h"
#include "libavcodec/raw.h"
#include "audiointerleave.h"
#include "avformat.h"
#include "avio_internal.h"
#include "id3v2.h"
#include "internal.h"
#include "metadata.h"
#if CONFIG_NETWORK
#include "network.h"
#endif
#include "riff.h"
#include "url.h"
#include "libavutil/ffversion.h"
const char av_format_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
/**
* @file
* various utility functions for use within FFmpeg
*/
unsigned avformat_version(void)
{
av_assert0(LIBAVFORMAT_VERSION_MICRO >= 100);
return LIBAVFORMAT_VERSION_INT;
}
const char *avformat_configuration(void)
{
return FFMPEG_CONFIGURATION;
}
const char *avformat_license(void)
{
#define LICENSE_PREFIX "libavformat license: "
return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1;
}
#define RELATIVE_TS_BASE (INT64_MAX - (1LL<<48))
static int is_relative(int64_t ts) {
return ts > (RELATIVE_TS_BASE - (1LL<<48));
}
/**
* Wrap a given time stamp, if there is an indication for an overflow
*
* @param st stream
* @param timestamp the time stamp to wrap
* @return resulting time stamp
*/
static int64_t wrap_timestamp(const AVStream *st, int64_t timestamp)
{
if (st->pts_wrap_behavior != AV_PTS_WRAP_IGNORE &&
st->pts_wrap_reference != AV_NOPTS_VALUE && timestamp != AV_NOPTS_VALUE) {
if (st->pts_wrap_behavior == AV_PTS_WRAP_ADD_OFFSET &&
timestamp < st->pts_wrap_reference)
return timestamp + (1ULL << st->pts_wrap_bits);
else if (st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET &&
timestamp >= st->pts_wrap_reference)
return timestamp - (1ULL << st->pts_wrap_bits);
}
return timestamp;
}
MAKE_ACCESSORS(AVStream, stream, AVRational, r_frame_rate)
MAKE_ACCESSORS(AVStream, stream, char *, recommended_encoder_configuration)
MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, video_codec)
MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, audio_codec)
MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, subtitle_codec)
MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, data_codec)
MAKE_ACCESSORS(AVFormatContext, format, int, metadata_header_padding)
MAKE_ACCESSORS(AVFormatContext, format, void *, opaque)
MAKE_ACCESSORS(AVFormatContext, format, av_format_control_message, control_message_cb)
#if FF_API_OLD_OPEN_CALLBACKS
FF_DISABLE_DEPRECATION_WARNINGS
MAKE_ACCESSORS(AVFormatContext, format, AVOpenCallback, open_cb)
FF_ENABLE_DEPRECATION_WARNINGS
#endif
int64_t av_stream_get_end_pts(const AVStream *st)
{
if (st->priv_pts) {
return st->priv_pts->val;
} else
return AV_NOPTS_VALUE;
}
struct AVCodecParserContext *av_stream_get_parser(const AVStream *st)
{
return st->parser;
}
void av_format_inject_global_side_data(AVFormatContext *s)
{
int i;
s->internal->inject_global_side_data = 1;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
st->inject_global_side_data = 1;
}
}
int ff_copy_whiteblacklists(AVFormatContext *dst, const AVFormatContext *src)
{
av_assert0(!dst->codec_whitelist &&
!dst->format_whitelist &&
!dst->protocol_whitelist &&
!dst->protocol_blacklist);
dst-> codec_whitelist = av_strdup(src->codec_whitelist);
dst->format_whitelist = av_strdup(src->format_whitelist);
dst->protocol_whitelist = av_strdup(src->protocol_whitelist);
dst->protocol_blacklist = av_strdup(src->protocol_blacklist);
if ( (src-> codec_whitelist && !dst-> codec_whitelist)
|| (src-> format_whitelist && !dst-> format_whitelist)
|| (src->protocol_whitelist && !dst->protocol_whitelist)
|| (src->protocol_blacklist && !dst->protocol_blacklist)) {
av_log(dst, AV_LOG_ERROR, "Failed to duplicate black/whitelist\n");
return AVERROR(ENOMEM);
}
return 0;
}
static const AVCodec *find_decoder(AVFormatContext *s, const AVStream *st, enum AVCodecID codec_id)
{
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
if (st->codec->codec)
return st->codec->codec;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_VIDEO:
if (s->video_codec) return s->video_codec;
break;
case AVMEDIA_TYPE_AUDIO:
if (s->audio_codec) return s->audio_codec;
break;
case AVMEDIA_TYPE_SUBTITLE:
if (s->subtitle_codec) return s->subtitle_codec;
break;
}
return avcodec_find_decoder(codec_id);
}
static const AVCodec *find_probe_decoder(AVFormatContext *s, const AVStream *st, enum AVCodecID codec_id)
{
const AVCodec *codec;
#if CONFIG_H264_DECODER
/* Other parts of the code assume this decoder to be used for h264,
* so force it if possible. */
if (codec_id == AV_CODEC_ID_H264)
return avcodec_find_decoder_by_name("h264");
#endif
codec = find_decoder(s, st, codec_id);
if (!codec)
return NULL;
if (codec->capabilities & AV_CODEC_CAP_AVOID_PROBING) {
const AVCodec *probe_codec = NULL;
while (probe_codec = av_codec_next(probe_codec)) {
if (probe_codec->id == codec_id &&
av_codec_is_decoder(probe_codec) &&
!(probe_codec->capabilities & (AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_EXPERIMENTAL))) {
return probe_codec;
}
}
}
return codec;
}
int av_format_get_probe_score(const AVFormatContext *s)
{
return s->probe_score;
}
/* an arbitrarily chosen "sane" max packet size -- 50M */
#define SANE_CHUNK_SIZE (50000000)
int ffio_limit(AVIOContext *s, int size)
{
if (s->maxsize>= 0) {
int64_t remaining= s->maxsize - avio_tell(s);
if (remaining < size) {
int64_t newsize = avio_size(s);
if (!s->maxsize || s->maxsize<newsize)
s->maxsize = newsize - !newsize;
remaining= s->maxsize - avio_tell(s);
remaining= FFMAX(remaining, 0);
}
if (s->maxsize>= 0 && remaining+1 < size) {
av_log(NULL, remaining ? AV_LOG_ERROR : AV_LOG_DEBUG, "Truncating packet of size %d to %"PRId64"\n", size, remaining+1);
size = remaining+1;
}
}
return size;
}
/* Read the data in sane-sized chunks and append to pkt.
* Return the number of bytes read or an error. */
static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size)
{
int64_t orig_pos = pkt->pos; // av_grow_packet might reset pos
int orig_size = pkt->size;
int ret;
do {
int prev_size = pkt->size;
int read_size;
/* When the caller requests a lot of data, limit it to the amount
* left in file or SANE_CHUNK_SIZE when it is not known. */
read_size = size;
if (read_size > SANE_CHUNK_SIZE/10) {
read_size = ffio_limit(s, read_size);
// If filesize/maxsize is unknown, limit to SANE_CHUNK_SIZE
if (s->maxsize < 0)
read_size = FFMIN(read_size, SANE_CHUNK_SIZE);
}
ret = av_grow_packet(pkt, read_size);
if (ret < 0)
break;
ret = avio_read(s, pkt->data + prev_size, read_size);
if (ret != read_size) {
av_shrink_packet(pkt, prev_size + FFMAX(ret, 0));
break;
}
size -= read_size;
} while (size > 0);
if (size > 0)
pkt->flags |= AV_PKT_FLAG_CORRUPT;
pkt->pos = orig_pos;
if (!pkt->size)
av_packet_unref(pkt);
return pkt->size > orig_size ? pkt->size - orig_size : ret;
}
int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
{
av_init_packet(pkt);
pkt->data = NULL;
pkt->size = 0;
pkt->pos = avio_tell(s);
return append_packet_chunked(s, pkt, size);
}
int av_append_packet(AVIOContext *s, AVPacket *pkt, int size)
{
if (!pkt->size)
return av_get_packet(s, pkt, size);
return append_packet_chunked(s, pkt, size);
}
int av_filename_number_test(const char *filename)
{
char buf[1024];
return filename &&
(av_get_frame_filename(buf, sizeof(buf), filename, 1) >= 0);
}
static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st,
AVProbeData *pd)
{
static const struct {
const char *name;
enum AVCodecID id;
enum AVMediaType type;
} fmt_id_type[] = {
{ "aac", AV_CODEC_ID_AAC, AVMEDIA_TYPE_AUDIO },
{ "ac3", AV_CODEC_ID_AC3, AVMEDIA_TYPE_AUDIO },
{ "dts", AV_CODEC_ID_DTS, AVMEDIA_TYPE_AUDIO },
{ "dvbsub", AV_CODEC_ID_DVB_SUBTITLE,AVMEDIA_TYPE_SUBTITLE },
{ "dvbtxt", AV_CODEC_ID_DVB_TELETEXT,AVMEDIA_TYPE_SUBTITLE },
{ "eac3", AV_CODEC_ID_EAC3, AVMEDIA_TYPE_AUDIO },
{ "h264", AV_CODEC_ID_H264, AVMEDIA_TYPE_VIDEO },
{ "hevc", AV_CODEC_ID_HEVC, AVMEDIA_TYPE_VIDEO },
{ "loas", AV_CODEC_ID_AAC_LATM, AVMEDIA_TYPE_AUDIO },
{ "m4v", AV_CODEC_ID_MPEG4, AVMEDIA_TYPE_VIDEO },
{ "mjpeg_2000",AV_CODEC_ID_JPEG2000, AVMEDIA_TYPE_VIDEO },
{ "mp3", AV_CODEC_ID_MP3, AVMEDIA_TYPE_AUDIO },
{ "mpegvideo", AV_CODEC_ID_MPEG2VIDEO, AVMEDIA_TYPE_VIDEO },
{ "truehd", AV_CODEC_ID_TRUEHD, AVMEDIA_TYPE_AUDIO },
{ 0 }
};
int score;
AVInputFormat *fmt = av_probe_input_format3(pd, 1, &score);
if (fmt) {
int i;
av_log(s, AV_LOG_DEBUG,
"Probe with size=%d, packets=%d detected %s with score=%d\n",
pd->buf_size, MAX_PROBE_PACKETS - st->probe_packets,
fmt->name, score);
for (i = 0; fmt_id_type[i].name; i++) {
if (!strcmp(fmt->name, fmt_id_type[i].name)) {
if (fmt_id_type[i].type != AVMEDIA_TYPE_AUDIO &&
st->codecpar->sample_rate)
continue;
if (st->request_probe > score &&
st->codecpar->codec_id != fmt_id_type[i].id)
continue;
st->codecpar->codec_id = fmt_id_type[i].id;
st->codecpar->codec_type = fmt_id_type[i].type;
st->internal->need_context_update = 1;
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
st->codec->codec_type = st->codecpar->codec_type;
st->codec->codec_id = st->codecpar->codec_id;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
return score;
}
}
}
return 0;
}
/************************************************************/
/* input media file */
int av_demuxer_open(AVFormatContext *ic) {
int err;
if (ic->format_whitelist && av_match_list(ic->iformat->name, ic->format_whitelist, ',') <= 0) {
av_log(ic, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", ic->format_whitelist);
return AVERROR(EINVAL);
}
if (ic->iformat->read_header) {
err = ic->iformat->read_header(ic);
if (err < 0)
return err;
}
if (ic->pb && !ic->internal->data_offset)
ic->internal->data_offset = avio_tell(ic->pb);
return 0;
}
/* Open input file and probe the format if necessary. */
static int init_input(AVFormatContext *s, const char *filename,
AVDictionary **options)
{
int ret;
AVProbeData pd = { filename, NULL, 0 };
int score = AVPROBE_SCORE_RETRY;
if (s->pb) {
s->flags |= AVFMT_FLAG_CUSTOM_IO;
if (!s->iformat)
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
else if (s->iformat->flags & AVFMT_NOFILE)
av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
"will be ignored with AVFMT_NOFILE format.\n");
return 0;
}
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
(!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
return score;
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
return ret;
if (s->iformat)
return 0;
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
}
static int add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt,
AVPacketList **plast_pktl, int ref)
{
AVPacketList *pktl = av_mallocz(sizeof(AVPacketList));
int ret;
if (!pktl)
return AVERROR(ENOMEM);
if (ref) {
if ((ret = av_packet_ref(&pktl->pkt, pkt)) < 0) {
av_free(pktl);
return ret;
}
} else {
pktl->pkt = *pkt;
}
if (*packet_buffer)
(*plast_pktl)->next = pktl;
else
*packet_buffer = pktl;
/* Add the packet in the buffered packet list. */
*plast_pktl = pktl;
return 0;
}
int avformat_queue_attached_pictures(AVFormatContext *s)
{
int i, ret;
for (i = 0; i < s->nb_streams; i++)
if (s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC &&
s->streams[i]->discard < AVDISCARD_ALL) {
if (s->streams[i]->attached_pic.size <= 0) {
av_log(s, AV_LOG_WARNING,
"Attached picture on stream %d has invalid size, "
"ignoring\n", i);
continue;
}
ret = add_to_pktbuf(&s->internal->raw_packet_buffer,
&s->streams[i]->attached_pic,
&s->internal->raw_packet_buffer_end, 1);
if (ret < 0)
return ret;
}
return 0;
}
static int update_stream_avctx(AVFormatContext *s)
{
int i, ret;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
if (!st->internal->need_context_update)
continue;
/* close parser, because it depends on the codec */
if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
av_parser_close(st->parser);
st->parser = NULL;
}
/* update internal codec context, for the parser */
ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
if (ret < 0)
return ret;
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
/* update deprecated public codec context */
ret = avcodec_parameters_to_context(st->codec, st->codecpar);
if (ret < 0)
return ret;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
st->internal->need_context_update = 0;
}
return 0;
}
int avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)
{
AVFormatContext *s = *ps;
int i, ret = 0;
AVDictionary *tmp = NULL;
ID3v2ExtraMeta *id3v2_extra_meta = NULL;
if (!s && !(s = avformat_alloc_context()))
return AVERROR(ENOMEM);
if (!s->av_class) {
av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
return AVERROR(EINVAL);
}
if (fmt)
s->iformat = fmt;
if (options)
av_dict_copy(&tmp, *options, 0);
if (s->pb) // must be before any goto fail
s->flags |= AVFMT_FLAG_CUSTOM_IO;
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
s->probe_score = ret;
if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
if (!s->protocol_whitelist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
if (!s->protocol_blacklist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
ret = AVERROR(EINVAL);
goto fail;
}
avio_skip(s->pb, s->skip_initial_bytes);
/* Check filename in case an image number is expected. */
if (s->iformat->flags & AVFMT_NEEDNUMBER) {
if (!av_filename_number_test(filename)) {
ret = AVERROR(EINVAL);
goto fail;
}
}
s->duration = s->start_time = AV_NOPTS_VALUE;
/* Allocate private data. */
if (s->iformat->priv_data_size > 0) {
if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
ret = AVERROR(ENOMEM);
goto fail;
}
if (s->iformat->priv_class) {
*(const AVClass **) s->priv_data = s->iformat->priv_class;
av_opt_set_defaults(s->priv_data);
if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
goto fail;
}
}
/* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
if (s->pb)
ff_id3v2_read_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
if ((ret = s->iformat->read_header(s)) < 0)
goto fail;
if (!s->metadata) {
s->metadata = s->internal->id3v2_meta;
s->internal->id3v2_meta = NULL;
} else if (s->internal->id3v2_meta) {
int level = AV_LOG_WARNING;
if (s->error_recognition & AV_EF_COMPLIANT)
level = AV_LOG_ERROR;
av_log(s, level, "Discarding ID3 tags because more suitable tags were found.\n");
av_dict_free(&s->internal->id3v2_meta);
if (s->error_recognition & AV_EF_EXPLODE)
return AVERROR_INVALIDDATA;
}
if (id3v2_extra_meta) {
if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
!strcmp(s->iformat->name, "tta")) {
if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
goto fail;
} else
av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
}
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
if ((ret = avformat_queue_attached_pictures(s)) < 0)
goto fail;
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
s->internal->data_offset = avio_tell(s->pb);
s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
update_stream_avctx(s);
for (i = 0; i < s->nb_streams; i++)
s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;
if (options) {
av_dict_free(options);
*options = tmp;
}
*ps = s;
return 0;
fail:
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
av_dict_free(&tmp);
if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
avio_closep(&s->pb);
avformat_free_context(s);
*ps = NULL;
return ret;
}
/*******************************************************/
static void force_codec_ids(AVFormatContext *s, AVStream *st)
{
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_VIDEO:
if (s->video_codec_id)
st->codecpar->codec_id = s->video_codec_id;
break;
case AVMEDIA_TYPE_AUDIO:
if (s->audio_codec_id)
st->codecpar->codec_id = s->audio_codec_id;
break;
case AVMEDIA_TYPE_SUBTITLE:
if (s->subtitle_codec_id)
st->codecpar->codec_id = s->subtitle_codec_id;
break;
case AVMEDIA_TYPE_DATA:
if (s->data_codec_id)
st->codecpar->codec_id = s->data_codec_id;
break;
}
}
static int probe_codec(AVFormatContext *s, AVStream *st, const AVPacket *pkt)
{
if (st->request_probe>0) {
AVProbeData *pd = &st->probe_data;
int end;
av_log(s, AV_LOG_DEBUG, "probing stream %d pp:%d\n", st->index, st->probe_packets);
--st->probe_packets;
if (pkt) {
uint8_t *new_buf = av_realloc(pd->buf, pd->buf_size+pkt->size+AVPROBE_PADDING_SIZE);
if (!new_buf) {
av_log(s, AV_LOG_WARNING,
"Failed to reallocate probe buffer for stream %d\n",
st->index);
goto no_packet;
}
pd->buf = new_buf;
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);
} else {
no_packet:
st->probe_packets = 0;
if (!pd->buf_size) {
av_log(s, AV_LOG_WARNING,
"nothing to probe for stream %d\n", st->index);
}
}
end= s->internal->raw_packet_buffer_remaining_size <= 0
|| st->probe_packets<= 0;
if (end || av_log2(pd->buf_size) != av_log2(pd->buf_size - pkt->size)) {
int score = set_codec_from_probe_data(s, st, pd);
if ( (st->codecpar->codec_id != AV_CODEC_ID_NONE && score > AVPROBE_SCORE_STREAM_RETRY)
|| end) {
pd->buf_size = 0;
av_freep(&pd->buf);
st->request_probe = -1;
if (st->codecpar->codec_id != AV_CODEC_ID_NONE) {
av_log(s, AV_LOG_DEBUG, "probed stream %d\n", st->index);
} else
av_log(s, AV_LOG_WARNING, "probed stream %d failed\n", st->index);
}
force_codec_ids(s, st);
}
}
return 0;
}
static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_index, AVPacket *pkt)
{
int64_t ref = pkt->dts;
int i, pts_wrap_behavior;
int64_t pts_wrap_reference;
AVProgram *first_program;
if (ref == AV_NOPTS_VALUE)
ref = pkt->pts;
if (st->pts_wrap_reference != AV_NOPTS_VALUE || st->pts_wrap_bits >= 63 || ref == AV_NOPTS_VALUE || !s->correct_ts_overflow)
return 0;
ref &= (1LL << st->pts_wrap_bits)-1;
// reference time stamp should be 60 s before first time stamp
pts_wrap_reference = ref - av_rescale(60, st->time_base.den, st->time_base.num);
// if first time stamp is not more than 1/8 and 60s before the wrap point, subtract rather than add wrap offset
pts_wrap_behavior = (ref < (1LL << st->pts_wrap_bits) - (1LL << st->pts_wrap_bits-3)) ||
(ref < (1LL << st->pts_wrap_bits) - av_rescale(60, st->time_base.den, st->time_base.num)) ?
AV_PTS_WRAP_ADD_OFFSET : AV_PTS_WRAP_SUB_OFFSET;
first_program = av_find_program_from_stream(s, NULL, stream_index);
if (!first_program) {
int default_stream_index = av_find_default_stream_index(s);
if (s->streams[default_stream_index]->pts_wrap_reference == AV_NOPTS_VALUE) {
for (i = 0; i < s->nb_streams; i++) {
if (av_find_program_from_stream(s, NULL, i))
continue;
s->streams[i]->pts_wrap_reference = pts_wrap_reference;
s->streams[i]->pts_wrap_behavior = pts_wrap_behavior;
}
}
else {
st->pts_wrap_reference = s->streams[default_stream_index]->pts_wrap_reference;
st->pts_wrap_behavior = s->streams[default_stream_index]->pts_wrap_behavior;
}
}
else {
AVProgram *program = first_program;
while (program) {
if (program->pts_wrap_reference != AV_NOPTS_VALUE) {
pts_wrap_reference = program->pts_wrap_reference;
pts_wrap_behavior = program->pts_wrap_behavior;
break;
}
program = av_find_program_from_stream(s, program, stream_index);
}
// update every program with differing pts_wrap_reference
program = first_program;
while (program) {
if (program->pts_wrap_reference != pts_wrap_reference) {
for (i = 0; i<program->nb_stream_indexes; i++) {
s->streams[program->stream_index[i]]->pts_wrap_reference = pts_wrap_reference;
s->streams[program->stream_index[i]]->pts_wrap_behavior = pts_wrap_behavior;
}
program->pts_wrap_reference = pts_wrap_reference;
program->pts_wrap_behavior = pts_wrap_behavior;
}
program = av_find_program_from_stream(s, program, stream_index);
}
}
return 1;
}
int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
{
int ret, i, err;
AVStream *st;
for (;;) {
AVPacketList *pktl = s->internal->raw_packet_buffer;
if (pktl) {
*pkt = pktl->pkt;
st = s->streams[pkt->stream_index];
if (s->internal->raw_packet_buffer_remaining_size <= 0)
if ((err = probe_codec(s, st, NULL)) < 0)
return err;
if (st->request_probe <= 0) {
s->internal->raw_packet_buffer = pktl->next;
s->internal->raw_packet_buffer_remaining_size += pkt->size;
av_free(pktl);
return 0;
}
}
pkt->data = NULL;
pkt->size = 0;
av_init_packet(pkt);
ret = s->iformat->read_packet(s, pkt);
if (ret < 0) {
/* Some demuxers return FFERROR_REDO when they consume
data and discard it (ignored streams, junk, extradata).
We must re-call the demuxer to get the real packet. */
if (ret == FFERROR_REDO)
continue;
if (!pktl || ret == AVERROR(EAGAIN))
return ret;
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (st->probe_packets || st->request_probe > 0)
if ((err = probe_codec(s, st, NULL)) < 0)
return err;
av_assert0(st->request_probe <= 0);
}
continue;
}
if (!pkt->buf) {
AVPacket tmp = { 0 };
ret = av_packet_ref(&tmp, pkt);
if (ret < 0)
return ret;
*pkt = tmp;
}
if ((s->flags & AVFMT_FLAG_DISCARD_CORRUPT) &&
(pkt->flags & AV_PKT_FLAG_CORRUPT)) {
av_log(s, AV_LOG_WARNING,
"Dropped corrupted packet (stream = %d)\n",
pkt->stream_index);
av_packet_unref(pkt);
continue;
}
if (pkt->stream_index >= (unsigned)s->nb_streams) {
av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index);
continue;
}
st = s->streams[pkt->stream_index];
if (update_wrap_reference(s, st, pkt->stream_index, pkt) && st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
// correct first time stamps to negative values
if (!is_relative(st->first_dts))
st->first_dts = wrap_timestamp(st, st->first_dts);
if (!is_relative(st->start_time))
st->start_time = wrap_timestamp(st, st->start_time);
if (!is_relative(st->cur_dts))
st->cur_dts = wrap_timestamp(st, st->cur_dts);
}
pkt->dts = wrap_timestamp(st, pkt->dts);
pkt->pts = wrap_timestamp(st, pkt->pts);
force_codec_ids(s, st);
/* TODO: audio: time filter; video: frame reordering (pts != dts) */
if (s->use_wallclock_as_timestamps)
pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);
if (!pktl && st->request_probe <= 0)
return ret;
err = add_to_pktbuf(&s->internal->raw_packet_buffer, pkt,
&s->internal->raw_packet_buffer_end, 0);
if (err)
return err;
s->internal->raw_packet_buffer_remaining_size -= pkt->size;
if ((err = probe_codec(s, st, pkt)) < 0)
return err;
}
}
/**********************************************************/
static int determinable_frame_size(AVCodecContext *avctx)
{
switch(avctx->codec_id) {
case AV_CODEC_ID_MP1:
case AV_CODEC_ID_MP2:
case AV_CODEC_ID_MP3:
return 1;
}
return 0;
}
/**
* Return the frame duration in seconds. Return 0 if not available.
*/
void ff_compute_frame_duration(AVFormatContext *s, int *pnum, int *pden, AVStream *st,
AVCodecParserContext *pc, AVPacket *pkt)
{
AVRational codec_framerate = s->iformat ? st->internal->avctx->framerate :
av_mul_q(av_inv_q(st->internal->avctx->time_base), (AVRational){1, st->internal->avctx->ticks_per_frame});
int frame_size, sample_rate;
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
if ((!codec_framerate.den || !codec_framerate.num) && st->codec->time_base.den && st->codec->time_base.num)
codec_framerate = av_mul_q(av_inv_q(st->codec->time_base), (AVRational){1, st->codec->ticks_per_frame});
FF_ENABLE_DEPRECATION_WARNINGS
#endif
*pnum = 0;
*pden = 0;
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_VIDEO:
if (st->r_frame_rate.num && !pc && s->iformat) {
*pnum = st->r_frame_rate.den;
*pden = st->r_frame_rate.num;
} else if (st->time_base.num * 1000LL > st->time_base.den) {
*pnum = st->time_base.num;
*pden = st->time_base.den;
} else if (codec_framerate.den * 1000LL > codec_framerate.num) {
av_assert0(st->internal->avctx->ticks_per_frame);
av_reduce(pnum, pden,
codec_framerate.den,
codec_framerate.num * (int64_t)st->internal->avctx->ticks_per_frame,
INT_MAX);
if (pc && pc->repeat_pict) {
av_assert0(s->iformat); // this may be wrong for interlaced encoding but its not used for that case
av_reduce(pnum, pden,
(*pnum) * (1LL + pc->repeat_pict),
(*pden),
INT_MAX);
}
/* If this codec can be interlaced or progressive then we need
* a parser to compute duration of a packet. Thus if we have
* no parser in such case leave duration undefined. */
if (st->internal->avctx->ticks_per_frame > 1 && !pc)
*pnum = *pden = 0;
}
break;
case AVMEDIA_TYPE_AUDIO:
if (st->internal->avctx_inited) {
frame_size = av_get_audio_frame_duration(st->internal->avctx, pkt->size);
sample_rate = st->internal->avctx->sample_rate;
} else {
frame_size = av_get_audio_frame_duration2(st->codecpar, pkt->size);
sample_rate = st->codecpar->sample_rate;
}
if (frame_size <= 0 || sample_rate <= 0)
break;
*pnum = frame_size;
*pden = sample_rate;
break;
default:
break;
}
}
static int is_intra_only(enum AVCodecID id)
{
const AVCodecDescriptor *d = avcodec_descriptor_get(id);
if (!d)
return 0;
if (d->type == AVMEDIA_TYPE_VIDEO && !(d->props & AV_CODEC_PROP_INTRA_ONLY))
return 0;
return 1;
}
static int has_decode_delay_been_guessed(AVStream *st)
{
if (st->codecpar->codec_id != AV_CODEC_ID_H264) return 1;
if (!st->info) // if we have left find_stream_info then nb_decoded_frames won't increase anymore for stream copy
return 1;
#if CONFIG_H264_DECODER
if (st->internal->avctx->has_b_frames &&
avpriv_h264_has_num_reorder_frames(st->internal->avctx) == st->internal->avctx->has_b_frames)
return 1;
#endif
if (st->internal->avctx->has_b_frames<3)
return st->nb_decoded_frames >= 7;
else if (st->internal->avctx->has_b_frames<4)
return st->nb_decoded_frames >= 18;
else
return st->nb_decoded_frames >= 20;
}
static AVPacketList *get_next_pkt(AVFormatContext *s, AVStream *st, AVPacketList *pktl)
{
if (pktl->next)
return pktl->next;
if (pktl == s->internal->packet_buffer_end)
return s->internal->parse_queue;
return NULL;
}
static int64_t select_from_pts_buffer(AVStream *st, int64_t *pts_buffer, int64_t dts) {
int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 &&
st->codecpar->codec_id != AV_CODEC_ID_HEVC;
if(!onein_oneout) {
int delay = st->internal->avctx->has_b_frames;
int i;
if (dts == AV_NOPTS_VALUE) {
int64_t best_score = INT64_MAX;
for (i = 0; i<delay; i++) {
if (st->pts_reorder_error_count[i]) {
int64_t score = st->pts_reorder_error[i] / st->pts_reorder_error_count[i];
if (score < best_score) {
best_score = score;
dts = pts_buffer[i];
}
}
}
} else {
for (i = 0; i<delay; i++) {
if (pts_buffer[i] != AV_NOPTS_VALUE) {
int64_t diff = FFABS(pts_buffer[i] - dts)
+ (uint64_t)st->pts_reorder_error[i];
diff = FFMAX(diff, st->pts_reorder_error[i]);
st->pts_reorder_error[i] = diff;
st->pts_reorder_error_count[i]++;
if (st->pts_reorder_error_count[i] > 250) {
st->pts_reorder_error[i] >>= 1;
st->pts_reorder_error_count[i] >>= 1;
}
}
}
}
}
if (dts == AV_NOPTS_VALUE)
dts = pts_buffer[0];
return dts;
}
/**
* Updates the dts of packets of a stream in pkt_buffer, by re-ordering the pts
* of the packets in a window.
*/
static void update_dts_from_pts(AVFormatContext *s, int stream_index,
AVPacketList *pkt_buffer)
{
AVStream *st = s->streams[stream_index];
int delay = st->internal->avctx->has_b_frames;
int i;
int64_t pts_buffer[MAX_REORDER_DELAY+1];
for (i = 0; i<MAX_REORDER_DELAY+1; i++)
pts_buffer[i] = AV_NOPTS_VALUE;
for (; pkt_buffer; pkt_buffer = get_next_pkt(s, st, pkt_buffer)) {
if (pkt_buffer->pkt.stream_index != stream_index)
continue;
if (pkt_buffer->pkt.pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) {
pts_buffer[0] = pkt_buffer->pkt.pts;
for (i = 0; i<delay && pts_buffer[i] > pts_buffer[i + 1]; i++)
FFSWAP(int64_t, pts_buffer[i], pts_buffer[i + 1]);
pkt_buffer->pkt.dts = select_from_pts_buffer(st, pts_buffer, pkt_buffer->pkt.dts);
}
}
}
static void update_initial_timestamps(AVFormatContext *s, int stream_index,
int64_t dts, int64_t pts, AVPacket *pkt)
{
AVStream *st = s->streams[stream_index];
AVPacketList *pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue;
AVPacketList *pktl_it;
uint64_t shift;
if (st->first_dts != AV_NOPTS_VALUE ||
dts == AV_NOPTS_VALUE ||
st->cur_dts == AV_NOPTS_VALUE ||
is_relative(dts))
return;
st->first_dts = dts - (st->cur_dts - RELATIVE_TS_BASE);
st->cur_dts = dts;
shift = (uint64_t)st->first_dts - RELATIVE_TS_BASE;
if (is_relative(pts))
pts += shift;
for (pktl_it = pktl; pktl_it; pktl_it = get_next_pkt(s, st, pktl_it)) {
if (pktl_it->pkt.stream_index != stream_index)
continue;
if (is_relative(pktl_it->pkt.pts))
pktl_it->pkt.pts += shift;
if (is_relative(pktl_it->pkt.dts))
pktl_it->pkt.dts += shift;
if (st->start_time == AV_NOPTS_VALUE && pktl_it->pkt.pts != AV_NOPTS_VALUE) {
st->start_time = pktl_it->pkt.pts;
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate)
st->start_time += av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base);
}
}
if (has_decode_delay_been_guessed(st)) {
update_dts_from_pts(s, stream_index, pktl);
}
if (st->start_time == AV_NOPTS_VALUE) {
st->start_time = pts;
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate)
st->start_time += av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base);
}
}
static void update_initial_durations(AVFormatContext *s, AVStream *st,
int stream_index, int duration)
{
AVPacketList *pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue;
int64_t cur_dts = RELATIVE_TS_BASE;
if (st->first_dts != AV_NOPTS_VALUE) {
if (st->update_initial_durations_done)
return;
st->update_initial_durations_done = 1;
cur_dts = st->first_dts;
for (; pktl; pktl = get_next_pkt(s, st, pktl)) {
if (pktl->pkt.stream_index == stream_index) {
if (pktl->pkt.pts != pktl->pkt.dts ||
pktl->pkt.dts != AV_NOPTS_VALUE ||
pktl->pkt.duration)
break;
cur_dts -= duration;
}
}
if (pktl && pktl->pkt.dts != st->first_dts) {
av_log(s, AV_LOG_DEBUG, "first_dts %s not matching first dts %s (pts %s, duration %"PRId64") in the queue\n",
av_ts2str(st->first_dts), av_ts2str(pktl->pkt.dts), av_ts2str(pktl->pkt.pts), pktl->pkt.duration);
return;
}
if (!pktl) {
av_log(s, AV_LOG_DEBUG, "first_dts %s but no packet with dts in the queue\n", av_ts2str(st->first_dts));
return;
}
pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue;
st->first_dts = cur_dts;
} else if (st->cur_dts != RELATIVE_TS_BASE)
return;
for (; pktl; pktl = get_next_pkt(s, st, pktl)) {
if (pktl->pkt.stream_index != stream_index)
continue;
if ((pktl->pkt.pts == pktl->pkt.dts ||
pktl->pkt.pts == AV_NOPTS_VALUE) &&
(pktl->pkt.dts == AV_NOPTS_VALUE ||
pktl->pkt.dts == st->first_dts ||
pktl->pkt.dts == RELATIVE_TS_BASE) &&
!pktl->pkt.duration) {
pktl->pkt.dts = cur_dts;
if (!st->internal->avctx->has_b_frames)
pktl->pkt.pts = cur_dts;
// if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
pktl->pkt.duration = duration;
} else
break;
cur_dts = pktl->pkt.dts + pktl->pkt.duration;
}
if (!pktl)
st->cur_dts = cur_dts;
}
static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
AVCodecParserContext *pc, AVPacket *pkt,
int64_t next_dts, int64_t next_pts)
{
int num, den, presentation_delayed, delay, i;
int64_t offset;
AVRational duration;
int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 &&
st->codecpar->codec_id != AV_CODEC_ID_HEVC;
if (s->flags & AVFMT_FLAG_NOFILLIN)
return;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && pkt->dts != AV_NOPTS_VALUE) {
if (pkt->dts == pkt->pts && st->last_dts_for_order_check != AV_NOPTS_VALUE) {
if (st->last_dts_for_order_check <= pkt->dts) {
st->dts_ordered++;
} else {
av_log(s, st->dts_misordered ? AV_LOG_DEBUG : AV_LOG_WARNING,
"DTS %"PRIi64" < %"PRIi64" out of order\n",
pkt->dts,
st->last_dts_for_order_check);
st->dts_misordered++;
}
if (st->dts_ordered + st->dts_misordered > 250) {
st->dts_ordered >>= 1;
st->dts_misordered >>= 1;
}
}
st->last_dts_for_order_check = pkt->dts;
if (st->dts_ordered < 8*st->dts_misordered && pkt->dts == pkt->pts)
pkt->dts = AV_NOPTS_VALUE;
}
if ((s->flags & AVFMT_FLAG_IGNDTS) && pkt->pts != AV_NOPTS_VALUE)
pkt->dts = AV_NOPTS_VALUE;
if (pc && pc->pict_type == AV_PICTURE_TYPE_B
&& !st->internal->avctx->has_b_frames)
//FIXME Set low_delay = 0 when has_b_frames = 1
st->internal->avctx->has_b_frames = 1;
/* do we have a video B-frame ? */
delay = st->internal->avctx->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 != AV_PICTURE_TYPE_B)
presentation_delayed = 1;
if (pkt->pts != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE &&
st->pts_wrap_bits < 63 &&
pkt->dts - (1LL << (st->pts_wrap_bits - 1)) > pkt->pts) {
if (is_relative(st->cur_dts) || pkt->dts - (1LL<<(st->pts_wrap_bits - 1)) > st->cur_dts) {
pkt->dts -= 1LL << st->pts_wrap_bits;
} else
pkt->pts += 1LL << st->pts_wrap_bits;
}
/* Some MPEG-2 in MPEG-PS lack dts (issue #171 / input_file.mpg).
* We take the conservative approach and discard both.
* Note: If this is misbehaving for an 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_DEBUG, "invalid dts/pts combination %"PRIi64"\n", pkt->dts);
if ( strcmp(s->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2")
&& strcmp(s->iformat->name, "flv")) // otherwise we discard correct timestamps for vc1-wmapro.ism
pkt->dts = AV_NOPTS_VALUE;
}
duration = av_mul_q((AVRational) {pkt->duration, 1}, st->time_base);
if (pkt->duration == 0) {
ff_compute_frame_duration(s, &num, &den, st, pc, pkt);
if (den && num) {
duration = (AVRational) {num, den};
pkt->duration = av_rescale_rnd(1,
num * (int64_t) st->time_base.den,
den * (int64_t) st->time_base.num,
AV_ROUND_DOWN);
}
}
if (pkt->duration != 0 && (s->internal->packet_buffer || s->internal->parse_queue))
update_initial_durations(s, st, pkt->stream_index, pkt->duration);
/* 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;
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_TRACE,
"IN delayed:%d pts:%s, dts:%s cur_dts:%s st:%d pc:%p duration:%"PRId64" delay:%d onein_oneout:%d\n",
presentation_delayed, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts),
pkt->stream_index, pc, pkt->duration, delay, onein_oneout);
/* Interpolate PTS and DTS if they are not present. We skip H264
* currently because delay and has_b_frames are not reliably set. */
if ((delay == 0 || (delay == 1 && pc)) &&
onein_oneout) {
if (presentation_delayed) {
/* DTS = decompression timestamp */
/* PTS = presentation timestamp */
if (pkt->dts == AV_NOPTS_VALUE)
pkt->dts = st->last_IP_pts;
update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt);
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->cur_dts = pkt->dts + st->last_IP_duration;
if (pkt->dts != AV_NOPTS_VALUE &&
pkt->pts == AV_NOPTS_VALUE &&
st->last_IP_duration > 0 &&
((uint64_t)st->cur_dts - (uint64_t)next_dts + 1) <= 2 &&
next_dts != next_pts &&
next_pts != AV_NOPTS_VALUE)
pkt->pts = next_dts;
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 future. */
} else if (pkt->pts != AV_NOPTS_VALUE ||
pkt->dts != AV_NOPTS_VALUE ||
pkt->duration ) {
/* 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, pkt);
if (pkt->pts == AV_NOPTS_VALUE)
pkt->pts = st->cur_dts;
pkt->dts = pkt->pts;
if (pkt->pts != AV_NOPTS_VALUE)
st->cur_dts = av_add_stable(st->time_base, pkt->pts, duration, 1);
}
}
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(has_decode_delay_been_guessed(st))
pkt->dts = select_from_pts_buffer(st, st->pts_buffer, pkt->dts);
}
// We skipped it above so we try here.
if (!onein_oneout)
// This should happen on the first packet
update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt);
if (pkt->dts > st->cur_dts)
st->cur_dts = pkt->dts;
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_TRACE, "OUTdelayed:%d/%d pts:%s, dts:%s cur_dts:%s\n",
presentation_delayed, delay, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts));
/* update flags */
if (is_intra_only(st->codecpar->codec_id))
pkt->flags |= AV_PKT_FLAG_KEY;
#if FF_API_CONVERGENCE_DURATION
FF_DISABLE_DEPRECATION_WARNINGS
if (pc)
pkt->convergence_duration = pc->convergence_duration;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
}
static void free_packet_buffer(AVPacketList **pkt_buf, AVPacketList **pkt_buf_end)
{
while (*pkt_buf) {
AVPacketList *pktl = *pkt_buf;
*pkt_buf = pktl->next;
av_packet_unref(&pktl->pkt);
av_freep(&pktl);
}
*pkt_buf_end = NULL;
}
/**
* Parse a packet, add all split parts to parse_queue.
*
* @param pkt Packet to parse, NULL when flushing the parser at end of stream.
*/
static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index)
{
AVPacket out_pkt = { 0 }, flush_pkt = { 0 };
AVStream *st = s->streams[stream_index];
uint8_t *data = pkt ? pkt->data : NULL;
int size = pkt ? pkt->size : 0;
int ret = 0, got_output = 0;
if (!pkt) {
av_init_packet(&flush_pkt);
pkt = &flush_pkt;
got_output = 1;
} else if (!size && st->parser->flags & PARSER_FLAG_COMPLETE_FRAMES) {
// preserve 0-size sync packets
compute_pkt_fields(s, st, st->parser, pkt, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
}
while (size > 0 || (pkt == &flush_pkt && got_output)) {
int len;
int64_t next_pts = pkt->pts;
int64_t next_dts = pkt->dts;
av_init_packet(&out_pkt);
len = av_parser_parse2(st->parser, st->internal->avctx,
&out_pkt.data, &out_pkt.size, data, size,
pkt->pts, pkt->dts, pkt->pos);
pkt->pts = pkt->dts = AV_NOPTS_VALUE;
pkt->pos = -1;
/* increment read pointer */
data += len;
size -= len;
got_output = !!out_pkt.size;
if (!out_pkt.size)
continue;
if (pkt->side_data) {
out_pkt.side_data = pkt->side_data;
out_pkt.side_data_elems = pkt->side_data_elems;
pkt->side_data = NULL;
pkt->side_data_elems = 0;
}
/* set the duration */
out_pkt.duration = (st->parser->flags & PARSER_FLAG_COMPLETE_FRAMES) ? pkt->duration : 0;
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
if (st->internal->avctx->sample_rate > 0) {
out_pkt.duration =
av_rescale_q_rnd(st->parser->duration,
(AVRational) { 1, st->internal->avctx->sample_rate },
st->time_base,
AV_ROUND_DOWN);
}
}
out_pkt.stream_index = st->index;
out_pkt.pts = st->parser->pts;
out_pkt.dts = st->parser->dts;
out_pkt.pos = st->parser->pos;
if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW)
out_pkt.pos = st->parser->frame_offset;
if (st->parser->key_frame == 1 ||
(st->parser->key_frame == -1 &&
st->parser->pict_type == AV_PICTURE_TYPE_I))
out_pkt.flags |= AV_PKT_FLAG_KEY;
if (st->parser->key_frame == -1 && st->parser->pict_type ==AV_PICTURE_TYPE_NONE && (pkt->flags&AV_PKT_FLAG_KEY))
out_pkt.flags |= AV_PKT_FLAG_KEY;
compute_pkt_fields(s, st, st->parser, &out_pkt, next_dts, next_pts);
ret = add_to_pktbuf(&s->internal->parse_queue, &out_pkt,
&s->internal->parse_queue_end, 1);
av_packet_unref(&out_pkt);
if (ret < 0)
goto fail;
}
/* end of the stream => close and free the parser */
if (pkt == &flush_pkt) {
av_parser_close(st->parser);
st->parser = NULL;
}
fail:
av_packet_unref(pkt);
return ret;
}
static int read_from_packet_buffer(AVPacketList **pkt_buffer,
AVPacketList **pkt_buffer_end,
AVPacket *pkt)
{
AVPacketList *pktl;
av_assert0(*pkt_buffer);
pktl = *pkt_buffer;
*pkt = pktl->pkt;
*pkt_buffer = pktl->next;
if (!pktl->next)
*pkt_buffer_end = NULL;
av_freep(&pktl);
return 0;
}
static int64_t ts_to_samples(AVStream *st, int64_t ts)
{
return av_rescale(ts, st->time_base.num * st->codecpar->sample_rate, st->time_base.den);
}
static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
{
int ret = 0, i, got_packet = 0;
AVDictionary *metadata = NULL;
av_init_packet(pkt);
while (!got_packet && !s->internal->parse_queue) {
AVStream *st;
AVPacket cur_pkt;
/* read next packet */
ret = ff_read_packet(s, &cur_pkt);
if (ret < 0) {
if (ret == AVERROR(EAGAIN))
return ret;
/* flush the parsers */
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (st->parser && st->need_parsing)
parse_packet(s, NULL, st->index);
}
/* all remaining packets are now in parse_queue =>
* really terminate parsing */
break;
}
ret = 0;
st = s->streams[cur_pkt.stream_index];
/* update context if required */
if (st->internal->need_context_update) {
if (avcodec_is_open(st->internal->avctx)) {
av_log(s, AV_LOG_DEBUG, "Demuxer context update while decoder is open, closing and trying to re-open\n");
avcodec_close(st->internal->avctx);
st->info->found_decoder = 0;
}
/* close parser, because it depends on the codec */
if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
av_parser_close(st->parser);
st->parser = NULL;
}
ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
if (ret < 0)
return ret;
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
/* update deprecated public codec context */
ret = avcodec_parameters_to_context(st->codec, st->codecpar);
if (ret < 0)
return ret;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
st->internal->need_context_update = 0;
}
if (cur_pkt.pts != AV_NOPTS_VALUE &&
cur_pkt.dts != AV_NOPTS_VALUE &&
cur_pkt.pts < cur_pkt.dts) {
av_log(s, AV_LOG_WARNING,
"Invalid timestamps stream=%d, pts=%s, dts=%s, size=%d\n",
cur_pkt.stream_index,
av_ts2str(cur_pkt.pts),
av_ts2str(cur_pkt.dts),
cur_pkt.size);
}
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_DEBUG,
"ff_read_packet stream=%d, pts=%s, dts=%s, size=%d, duration=%"PRId64", flags=%d\n",
cur_pkt.stream_index,
av_ts2str(cur_pkt.pts),
av_ts2str(cur_pkt.dts),
cur_pkt.size, cur_pkt.duration, cur_pkt.flags);
if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) {
st->parser = av_parser_init(st->codecpar->codec_id);
if (!st->parser) {
av_log(s, AV_LOG_VERBOSE, "parser not found for codec "
"%s, packets or times may be invalid.\n",
avcodec_get_name(st->codecpar->codec_id));
/* 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;
else if (st->need_parsing == AVSTREAM_PARSE_FULL_ONCE)
st->parser->flags |= PARSER_FLAG_ONCE;
else if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW)
st->parser->flags |= PARSER_FLAG_USE_CODEC_TS;
}
if (!st->need_parsing || !st->parser) {
/* no parsing needed: we just output the packet as is */
*pkt = cur_pkt;
compute_pkt_fields(s, st, NULL, pkt, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
if ((s->iformat->flags & AVFMT_GENERIC_INDEX) &&
(pkt->flags & AV_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);
}
got_packet = 1;
} else if (st->discard < AVDISCARD_ALL) {
if ((ret = parse_packet(s, &cur_pkt, cur_pkt.stream_index)) < 0)
return ret;
st->codecpar->sample_rate = st->internal->avctx->sample_rate;
st->codecpar->bit_rate = st->internal->avctx->bit_rate;
st->codecpar->channels = st->internal->avctx->channels;
st->codecpar->channel_layout = st->internal->avctx->channel_layout;
st->codecpar->codec_id = st->internal->avctx->codec_id;
} else {
/* free packet */
av_packet_unref(&cur_pkt);
}
if (pkt->flags & AV_PKT_FLAG_KEY)
st->skip_to_keyframe = 0;
if (st->skip_to_keyframe) {
av_packet_unref(&cur_pkt);
if (got_packet) {
*pkt = cur_pkt;
}
got_packet = 0;
}
}
if (!got_packet && s->internal->parse_queue)
ret = read_from_packet_buffer(&s->internal->parse_queue, &s->internal->parse_queue_end, pkt);
if (ret >= 0) {
AVStream *st = s->streams[pkt->stream_index];
int discard_padding = 0;
if (st->first_discard_sample && pkt->pts != AV_NOPTS_VALUE) {
int64_t pts = pkt->pts - (is_relative(pkt->pts) ? RELATIVE_TS_BASE : 0);
int64_t sample = ts_to_samples(st, pts);
int duration = ts_to_samples(st, pkt->duration);
int64_t end_sample = sample + duration;
if (duration > 0 && end_sample >= st->first_discard_sample &&
sample < st->last_discard_sample)
discard_padding = FFMIN(end_sample - st->first_discard_sample, duration);
}
if (st->start_skip_samples && (pkt->pts == 0 || pkt->pts == RELATIVE_TS_BASE))
st->skip_samples = st->start_skip_samples;
if (st->skip_samples || discard_padding) {
uint8_t *p = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
if (p) {
AV_WL32(p, st->skip_samples);
AV_WL32(p + 4, discard_padding);
av_log(s, AV_LOG_DEBUG, "demuxer injecting skip %d / discard %d\n", st->skip_samples, discard_padding);
}
st->skip_samples = 0;
}
if (st->inject_global_side_data) {
for (i = 0; i < st->nb_side_data; i++) {
AVPacketSideData *src_sd = &st->side_data[i];
uint8_t *dst_data;
if (av_packet_get_side_data(pkt, src_sd->type, NULL))
continue;
dst_data = av_packet_new_side_data(pkt, src_sd->type, src_sd->size);
if (!dst_data) {
av_log(s, AV_LOG_WARNING, "Could not inject global side data\n");
continue;
}
memcpy(dst_data, src_sd->data, src_sd->size);
}
st->inject_global_side_data = 0;
}
#if FF_API_LAVF_MERGE_SD
FF_DISABLE_DEPRECATION_WARNINGS
if (!(s->flags & AVFMT_FLAG_KEEP_SIDE_DATA))
av_packet_merge_side_data(pkt);
FF_ENABLE_DEPRECATION_WARNINGS
#endif
}
av_opt_get_dict_val(s, "metadata", AV_OPT_SEARCH_CHILDREN, &metadata);
if (metadata) {
s->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
av_dict_copy(&s->metadata, metadata, 0);
av_dict_free(&metadata);
av_opt_set_dict_val(s, "metadata", NULL, AV_OPT_SEARCH_CHILDREN);
}
#if FF_API_LAVF_AVCTX
update_stream_avctx(s);
#endif
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_DEBUG,
"read_frame_internal stream=%d, pts=%s, dts=%s, "
"size=%d, duration=%"PRId64", flags=%d\n",
pkt->stream_index,
av_ts2str(pkt->pts),
av_ts2str(pkt->dts),
pkt->size, pkt->duration, pkt->flags);
return ret;
}
int av_read_frame(AVFormatContext *s, AVPacket *pkt)
{
const int genpts = s->flags & AVFMT_FLAG_GENPTS;
int eof = 0;
int ret;
AVStream *st;
if (!genpts) {
ret = s->internal->packet_buffer
? read_from_packet_buffer(&s->internal->packet_buffer,
&s->internal->packet_buffer_end, pkt)
: read_frame_internal(s, pkt);
if (ret < 0)
return ret;
goto return_packet;
}
for (;;) {
AVPacketList *pktl = s->internal->packet_buffer;
if (pktl) {
AVPacket *next_pkt = &pktl->pkt;
if (next_pkt->dts != AV_NOPTS_VALUE) {
int wrap_bits = s->streams[next_pkt->stream_index]->pts_wrap_bits;
// last dts seen for this stream. if any of packets following
// current one had no dts, we will set this to AV_NOPTS_VALUE.
int64_t last_dts = next_pkt->dts;
while (pktl && next_pkt->pts == AV_NOPTS_VALUE) {
if (pktl->pkt.stream_index == next_pkt->stream_index &&
(av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0)) {
if (av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) {
// not B-frame
next_pkt->pts = pktl->pkt.dts;
}
if (last_dts != AV_NOPTS_VALUE) {
// Once last dts was set to AV_NOPTS_VALUE, we don't change it.
last_dts = pktl->pkt.dts;
}
}
pktl = pktl->next;
}
if (eof && next_pkt->pts == AV_NOPTS_VALUE && last_dts != AV_NOPTS_VALUE) {
// Fixing the last reference frame had none pts issue (For MXF etc).
// We only do this when
// 1. eof.
// 2. we are not able to resolve a pts value for current packet.
// 3. the packets for this stream at the end of the files had valid dts.
next_pkt->pts = last_dts + next_pkt->duration;
}
pktl = s->internal->packet_buffer;
}
/* read packet from packet buffer, if there is data */
st = s->streams[next_pkt->stream_index];
if (!(next_pkt->pts == AV_NOPTS_VALUE && st->discard < AVDISCARD_ALL &&
next_pkt->dts != AV_NOPTS_VALUE && !eof)) {
ret = read_from_packet_buffer(&s->internal->packet_buffer,
&s->internal->packet_buffer_end, pkt);
goto return_packet;
}
}
ret = read_frame_internal(s, pkt);
if (ret < 0) {
if (pktl && ret != AVERROR(EAGAIN)) {
eof = 1;
continue;
} else
return ret;
}
ret = add_to_pktbuf(&s->internal->packet_buffer, pkt,
&s->internal->packet_buffer_end, 1);
av_packet_unref(pkt);
if (ret < 0)
return ret;
}
return_packet:
st = s->streams[pkt->stream_index];
if ((s->iformat->flags & AVFMT_GENERIC_INDEX) && pkt->flags & AV_PKT_FLAG_KEY) {
ff_reduce_index(s, st->index);
av_add_index_entry(st, pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME);
}
if (is_relative(pkt->dts))
pkt->dts -= RELATIVE_TS_BASE;
if (is_relative(pkt->pts))
pkt->pts -= RELATIVE_TS_BASE;
return ret;
}
/* XXX: suppress the packet queue */
static void flush_packet_queue(AVFormatContext *s)
{
if (!s->internal)
return;
free_packet_buffer(&s->internal->parse_queue, &s->internal->parse_queue_end);
free_packet_buffer(&s->internal->packet_buffer, &s->internal->packet_buffer_end);
free_packet_buffer(&s->internal->raw_packet_buffer, &s->internal->raw_packet_buffer_end);
s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
}
/*******************************************************/
/* seek support */
int av_find_default_stream_index(AVFormatContext *s)
{
int i;
AVStream *st;
int best_stream = 0;
int best_score = INT_MIN;
if (s->nb_streams <= 0)
return -1;
for (i = 0; i < s->nb_streams; i++) {
int score = 0;
st = s->streams[i];
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
score -= 400;
if (st->codecpar->width && st->codecpar->height)
score += 50;
score+= 25;
}
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
if (st->codecpar->sample_rate)
score += 50;
}
if (st->codec_info_nb_frames)
score += 12;
if (st->discard != AVDISCARD_ALL)
score += 200;
if (score > best_score) {
best_score = score;
best_stream = i;
}
}
return best_stream;
}
/** Flush the frame reader. */
void ff_read_frame_flush(AVFormatContext *s)
{
AVStream *st;
int i, j;
flush_packet_queue(s);
/* Reset read state for each stream. */
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (st->parser) {
av_parser_close(st->parser);
st->parser = NULL;
}
st->last_IP_pts = AV_NOPTS_VALUE;
st->last_dts_for_order_check = AV_NOPTS_VALUE;
if (st->first_dts == AV_NOPTS_VALUE)
st->cur_dts = RELATIVE_TS_BASE;
else
/* We set the current DTS to an unspecified origin. */
st->cur_dts = AV_NOPTS_VALUE;
st->probe_packets = MAX_PROBE_PACKETS;
for (j = 0; j < MAX_REORDER_DELAY + 1; j++)
st->pts_buffer[j] = AV_NOPTS_VALUE;
if (s->internal->inject_global_side_data)
st->inject_global_side_data = 1;
st->skip_samples = 0;
}
}
void ff_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
{
int i;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
st->cur_dts =
av_rescale(timestamp,
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;
}
}
int ff_add_index_entry(AVIndexEntry **index_entries,
int *nb_index_entries,
unsigned int *index_entries_allocated_size,
int64_t pos, int64_t timestamp,
int size, int distance, int flags)
{
AVIndexEntry *entries, *ie;
int index;
if ((unsigned) *nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry))
return -1;
if (timestamp == AV_NOPTS_VALUE)
return AVERROR(EINVAL);
if (size < 0 || size > 0x3FFFFFFF)
return AVERROR(EINVAL);
if (is_relative(timestamp)) //FIXME this maintains previous behavior but we should shift by the correct offset once known
timestamp -= RELATIVE_TS_BASE;
entries = av_fast_realloc(*index_entries,
index_entries_allocated_size,
(*nb_index_entries + 1) *
sizeof(AVIndexEntry));
if (!entries)
return -1;
*index_entries = entries;
index = ff_index_search_timestamp(*index_entries, *nb_index_entries,
timestamp, AVSEEK_FLAG_ANY);
if (index < 0) {
index = (*nb_index_entries)++;
ie = &entries[index];
av_assert0(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) * (*nb_index_entries - index));
(*nb_index_entries)++;
} else if (ie->pos == pos && distance < ie->min_distance)
// do not reduce the distance
distance = ie->min_distance;
}
ie->pos = pos;
ie->timestamp = timestamp;
ie->min_distance = distance;
ie->size = size;
ie->flags = flags;
return index;
}
int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp,
int size, int distance, int flags)
{
timestamp = wrap_timestamp(st, timestamp);
return ff_add_index_entry(&st->index_entries, &st->nb_index_entries,
&st->index_entries_allocated_size, pos,
timestamp, size, distance, flags);
}
int ff_index_search_timestamp(const AVIndexEntry *entries, int nb_entries,
int64_t wanted_timestamp, int flags)
{
int a, b, m;
int64_t timestamp;
a = -1;
b = nb_entries;
// Optimize appending index entries at the end.
if (b && entries[b - 1].timestamp < wanted_timestamp)
a = b - 1;
while (b - a > 1) {
m = (a + b) >> 1;
// Search for the next non-discarded packet.
while ((entries[m].flags & AVINDEX_DISCARD_FRAME) && m < b && m < nb_entries - 1) {
m++;
if (m == b && entries[m].timestamp >= wanted_timestamp) {
m = b - 1;
break;
}
}
timestamp = entries[m].timestamp;
if (timestamp >= wanted_timestamp)
b = m;
if (timestamp <= wanted_timestamp)
a = m;
}
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;
if (m == nb_entries)
return -1;
return m;
}
void ff_configure_buffers_for_index(AVFormatContext *s, int64_t time_tolerance)
{
int ist1, ist2;
int64_t pos_delta = 0;
int64_t skip = 0;
//We could use URLProtocol flags here but as many user applications do not use URLProtocols this would be unreliable
const char *proto = avio_find_protocol_name(s->filename);
if (!proto) {
av_log(s, AV_LOG_INFO,
"Protocol name not provided, cannot determine if input is local or "
"a network protocol, buffers and access patterns cannot be configured "
"optimally without knowing the protocol\n");
}
if (proto && !(strcmp(proto, "file") && strcmp(proto, "pipe") && strcmp(proto, "cache")))
return;
for (ist1 = 0; ist1 < s->nb_streams; ist1++) {
AVStream *st1 = s->streams[ist1];
for (ist2 = 0; ist2 < s->nb_streams; ist2++) {
AVStream *st2 = s->streams[ist2];
int i1, i2;
if (ist1 == ist2)
continue;
for (i1 = i2 = 0; i1 < st1->nb_index_entries; i1++) {
AVIndexEntry *e1 = &st1->index_entries[i1];
int64_t e1_pts = av_rescale_q(e1->timestamp, st1->time_base, AV_TIME_BASE_Q);
skip = FFMAX(skip, e1->size);
for (; i2 < st2->nb_index_entries; i2++) {
AVIndexEntry *e2 = &st2->index_entries[i2];
int64_t e2_pts = av_rescale_q(e2->timestamp, st2->time_base, AV_TIME_BASE_Q);
if (e2_pts - e1_pts < time_tolerance)
continue;
pos_delta = FFMAX(pos_delta, e1->pos - e2->pos);
break;
}
}
}
}
pos_delta *= 2;
/* XXX This could be adjusted depending on protocol*/
if (s->pb->buffer_size < pos_delta && pos_delta < (1<<24)) {
av_log(s, AV_LOG_VERBOSE, "Reconfiguring buffers to size %"PRId64"\n", pos_delta);
ffio_set_buf_size(s->pb, pos_delta);
s->pb->short_seek_threshold = FFMAX(s->pb->short_seek_threshold, pos_delta/2);
}
if (skip < (1<<23)) {
s->pb->short_seek_threshold = FFMAX(s->pb->short_seek_threshold, skip);
}
}
int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp, int flags)
{
return ff_index_search_timestamp(st->index_entries, st->nb_index_entries,
wanted_timestamp, flags);
}
static int64_t ff_read_timestamp(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit,
int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t ))
{
int64_t ts = read_timestamp(s, stream_index, ppos, pos_limit);
if (stream_index >= 0)
ts = wrap_timestamp(s->streams[stream_index], ts);
return ts;
}
int ff_seek_frame_binary(AVFormatContext *s, int stream_index,
int64_t target_ts, int flags)
{
AVInputFormat *avif = s->iformat;
int64_t av_uninit(pos_min), av_uninit(pos_max), pos, pos_limit;
int64_t ts_min, ts_max, ts;
int index;
int64_t ret;
AVStream *st;
if (stream_index < 0)
return -1;
av_log(s, AV_LOG_TRACE, "read_seek: %d %s\n", stream_index, av_ts2str(target_ts));
ts_max =
ts_min = AV_NOPTS_VALUE;
pos_limit = -1; // GCC falsely says it may be uninitialized.
st = s->streams[stream_index];
if (st->index_entries) {
AVIndexEntry *e;
/* FIXME: Whole function must be checked for non-keyframe entries in
* index case, especially read_timestamp(). */
index = av_index_search_timestamp(st, target_ts,
flags | AVSEEK_FLAG_BACKWARD);
index = FFMAX(index, 0);
e = &st->index_entries[index];
if (e->timestamp <= target_ts || e->pos == e->min_distance) {
pos_min = e->pos;
ts_min = e->timestamp;
av_log(s, AV_LOG_TRACE, "using cached pos_min=0x%"PRIx64" dts_min=%s\n",
pos_min, av_ts2str(ts_min));
} else {
av_assert1(index == 0);
}
index = av_index_search_timestamp(st, target_ts,
flags & ~AVSEEK_FLAG_BACKWARD);
av_assert0(index < st->nb_index_entries);
if (index >= 0) {
e = &st->index_entries[index];
av_assert1(e->timestamp >= target_ts);
pos_max = e->pos;
ts_max = e->timestamp;
pos_limit = pos_max - e->min_distance;
av_log(s, AV_LOG_TRACE, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64
" dts_max=%s\n", pos_max, pos_limit, av_ts2str(ts_max));
}
}
pos = ff_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 */
if ((ret = avio_seek(s->pb, pos, SEEK_SET)) < 0)
return ret;
ff_read_frame_flush(s);
ff_update_cur_dts(s, st, ts);
return 0;
}
int ff_find_last_ts(AVFormatContext *s, int stream_index, int64_t *ts, int64_t *pos,
int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t ))
{
int64_t step = 1024;
int64_t limit, ts_max;
int64_t filesize = avio_size(s->pb);
int64_t pos_max = filesize - 1;
do {
limit = pos_max;
pos_max = FFMAX(0, (pos_max) - step);
ts_max = ff_read_timestamp(s, stream_index,
&pos_max, limit, read_timestamp);
step += step;
} while (ts_max == AV_NOPTS_VALUE && 2*limit > step);
if (ts_max == AV_NOPTS_VALUE)
return -1;
for (;;) {
int64_t tmp_pos = pos_max + 1;
int64_t tmp_ts = ff_read_timestamp(s, stream_index,
&tmp_pos, INT64_MAX, read_timestamp);
if (tmp_ts == AV_NOPTS_VALUE)
break;
av_assert0(tmp_pos > pos_max);
ts_max = tmp_ts;
pos_max = tmp_pos;
if (tmp_pos >= filesize)
break;
}
if (ts)
*ts = ts_max;
if (pos)
*pos = pos_max;
return 0;
}
int64_t ff_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;
int no_change;
int ret;
av_log(s, AV_LOG_TRACE, "gen_seek: %d %s\n", stream_index, av_ts2str(target_ts));
if (ts_min == AV_NOPTS_VALUE) {
pos_min = s->internal->data_offset;
ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp);
if (ts_min == AV_NOPTS_VALUE)
return -1;
}
if (ts_min >= target_ts) {
*ts_ret = ts_min;
return pos_min;
}
if (ts_max == AV_NOPTS_VALUE) {
if ((ret = ff_find_last_ts(s, stream_index, &ts_max, &pos_max, read_timestamp)) < 0)
return ret;
pos_limit = pos_max;
}
if (ts_max <= target_ts) {
*ts_ret = ts_max;
return pos_max;
}
av_assert0(ts_min < ts_max);
no_change = 0;
while (pos_min < pos_limit) {
av_log(s, AV_LOG_TRACE,
"pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%s dts_max=%s\n",
pos_min, pos_max, av_ts2str(ts_min), av_ts2str(ts_max));
av_assert0(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;
} else if (no_change == 1) {
// bisection if interpolation did not change min / 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 */
pos = pos_min;
}
if (pos <= pos_min)
pos = pos_min + 1;
else if (pos > pos_limit)
pos = pos_limit;
start_pos = pos;
// May pass pos_limit instead of -1.
ts = ff_read_timestamp(s, stream_index, &pos, INT64_MAX, read_timestamp);
if (pos == pos_max)
no_change++;
else
no_change = 0;
av_log(s, AV_LOG_TRACE, "%"PRId64" %"PRId64" %"PRId64" / %s %s %s"
" target:%s limit:%"PRId64" start:%"PRId64" noc:%d\n",
pos_min, pos, pos_max,
av_ts2str(ts_min), av_ts2str(ts), av_ts2str(ts_max), av_ts2str(target_ts),
pos_limit, start_pos, no_change);
if (ts == AV_NOPTS_VALUE) {
av_log(s, AV_LOG_ERROR, "read_timestamp() failed in the middle\n");
return -1;
}
if (target_ts <= ts) {
pos_limit = start_pos - 1;
pos_max = pos;
ts_max = ts;
}
if (target_ts >= ts) {
pos_min = pos;
ts_min = ts;
}
}
pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max;
ts = (flags & AVSEEK_FLAG_BACKWARD) ? ts_min : ts_max;
#if 0
pos_min = pos;
ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp);
pos_min++;
ts_max = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp);
av_log(s, AV_LOG_TRACE, "pos=0x%"PRIx64" %s<=%s<=%s\n",
pos, av_ts2str(ts_min), av_ts2str(target_ts), av_ts2str(ts_max));
#endif
*ts_ret = ts;
return pos;
}
static int seek_frame_byte(AVFormatContext *s, int stream_index,
int64_t pos, int flags)
{
int64_t pos_min, pos_max;
pos_min = s->internal->data_offset;
pos_max = avio_size(s->pb) - 1;
if (pos < pos_min)
pos = pos_min;
else if (pos > pos_max)
pos = pos_max;
avio_seek(s->pb, pos, SEEK_SET);
s->io_repositioned = 1;
return 0;
}
static int seek_frame_generic(AVFormatContext *s, int stream_index,
int64_t timestamp, int flags)
{
int index;
int64_t ret;
AVStream *st;
AVIndexEntry *ie;
st = s->streams[stream_index];
index = av_index_search_timestamp(st, timestamp, flags);
if (index < 0 && st->nb_index_entries &&
timestamp < st->index_entries[0].timestamp)
return -1;
if (index < 0 || index == st->nb_index_entries - 1) {
AVPacket pkt;
int nonkey = 0;
if (st->nb_index_entries) {
av_assert0(st->index_entries);
ie = &st->index_entries[st->nb_index_entries - 1];
if ((ret = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0)
return ret;
ff_update_cur_dts(s, st, ie->timestamp);
} else {
if ((ret = avio_seek(s->pb, s->internal->data_offset, SEEK_SET)) < 0)
return ret;
}
for (;;) {
int read_status;
do {
read_status = av_read_frame(s, &pkt);
} while (read_status == AVERROR(EAGAIN));
if (read_status < 0)
break;
if (stream_index == pkt.stream_index && pkt.dts > timestamp) {
if (pkt.flags & AV_PKT_FLAG_KEY) {
av_packet_unref(&pkt);
break;
}
if (nonkey++ > 1000 && st->codecpar->codec_id != AV_CODEC_ID_CDGRAPHICS) {
av_log(s, AV_LOG_ERROR,"seek_frame_generic failed as this stream seems to contain no keyframes after the target timestamp, %d non keyframes found\n", nonkey);
av_packet_unref(&pkt);
break;
}
}
av_packet_unref(&pkt);
}
index = av_index_search_timestamp(st, timestamp, flags);
}
if (index < 0)
return -1;
ff_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 = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0)
return ret;
ff_update_cur_dts(s, st, ie->timestamp);
return 0;
}
static int seek_frame_internal(AVFormatContext *s, int stream_index,
int64_t timestamp, int flags)
{
int ret;
AVStream *st;
if (flags & AVSEEK_FLAG_BYTE) {
if (s->iformat->flags & AVFMT_NO_BYTE_SEEK)
return -1;
ff_read_frame_flush(s);
return 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;
st = s->streams[stream_index];
/* 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) {
ff_read_frame_flush(s);
ret = s->iformat->read_seek(s, stream_index, timestamp, flags);