estimate_timings方法
estimate_timings
方法在ffmepg
中不是一个api方法, 只是在avformat_find_stream_info
中调用,但这个方法的作用却是很大的, 它解出了三个参数, 分别如下
- start time:第一帧数据的PTS, 当前时间的计算是,
当前显示帧的PTS - start time
。 - duration:视频的总长度。
- bitrate:视频的码率
计算
duration
的方法分为2种,estimate_timings_from_pts
,estimate_timings_from_bit_rate
, 这两种是在demux
时没有解出来duration
的情况下去做的。
static void estimate_timings(AVFormatContext *ic, int64_t old_offset)
{
int64_t file_size;
/* get the file size, if possible */
if (ic->iformat->flags & AVFMT_NOFILE) {
file_size = 0;
} else {
file_size = avio_size(ic->pb);
file_size = FFMAX(0, file_size);
}
/*当demux是mpegts时,就使用 estimate_timings_from_pts方法去解出duration*/
if ((!strcmp(ic->iformat->name, "mpeg") ||
!strcmp(ic->iformat->name, "mpegts")) &&
file_size && ic->pb->seekable) {
/* get accurate estimate from the PTSes */
estimate_timings_from_pts(ic, old_offset);
ic->duration_estimation_method = AVFMT_DURATION_FROM_PTS;
} else if (has_duration(ic)) {
/* 在demux的read header的时候已经解出来了duration, 譬如mp4, hls rtsp */
/* at least one component has timings - we use them for all
* the components */
fill_all_stream_timings(ic);
ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM;
} else {
/* 通过码率和file size去计算duration */
/* less precise: use bitrate info */
estimate_timings_from_bit_rate(ic);
ic->duration_estimation_method = AVFMT_DURATION_FROM_BITRATE;
}
update_stream_timings(ic);
{
int i;
AVStream av_unused *st;
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
av_log(ic, AV_LOG_TRACE, "stream %d: start_time: %0.3f duration: %0.3f\n", i,
(double) st->start_time * av_q2d(st->time_base),
(double) st->duration * av_q2d(st->time_base));
}
av_log(ic, AV_LOG_TRACE,
"format: start_time: %0.3f duration: %0.3f bitrate=%"PRId64" kb/s\n",
(double) ic->start_time / AV_TIME_BASE,
(double) ic->duration / AV_TIME_BASE,
(int64_t)ic->bit_rate / 1000);
}
}
estimate_timings_from_pts方法
其实
estimate_timings_from_pts
方法说简单点, 就是通过最后一帧的pts-第一帧的pts
来计算duration
。
static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
{
AVPacket pkt1, *pkt = &pkt1;
AVStream *st;
int num, den, read_size, i, ret;
int found_duration = 0;
int is_end;
int64_t filesize, offset, duration;
int retry = 0;
/* flush packet queue */
flush_packet_queue(ic);
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time == AV_NOPTS_VALUE &&
st->first_dts == AV_NOPTS_VALUE &&
st->codecpar->codec_type != AVMEDIA_TYPE_UNKNOWN)
av_log(ic, AV_LOG_WARNING,
"start time for stream %d is not set in estimate_timings_from_pts\n", i);
if (st->parser) {
av_parser_close(st->parser);
st->parser = NULL;
}
}
av_opt_set(ic, "skip_changes", "1", AV_OPT_SEARCH_CHILDREN);
/* estimate the end time (duration) */
/* XXX: may need to support wrapping */
filesize = ic->pb ? avio_size(ic->pb) : 0;
do {
is_end = found_duration;
// 找到一个接近文件结尾的值
offset = filesize - (DURATION_MAX_READ_SIZE << retry);
if (offset < 0)
offset = 0;
avio_seek(ic->pb, offset, SEEK_SET);
read_size = 0;
// 循环, 尽可能的接近文件结尾, 获得最后一帧数据的Pts, 来计算druation
for (;;) {
if (read_size >= DURATION_MAX_READ_SIZE << (FFMAX(retry - 1, 0)))
break;
do {
ret = ff_read_packet(ic, pkt);
} while (ret == AVERROR(EAGAIN));
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 ||
st->first_dts != AV_NOPTS_VALUE)) {
if (pkt->duration == 0) {
ff_compute_frame_duration(ic, &num, &den, st, st->parser, pkt);
if (den && num) {
pkt->duration = av_rescale_rnd(1,
num * (int64_t) st->time_base.den,
den * (int64_t) st->time_base.num,
AV_ROUND_DOWN);
}
}
duration = pkt->pts + pkt->duration;
found_duration = 1;
if (st->start_time != AV_NOPTS_VALUE)
duration -= st->start_time;
else
duration -= st->first_dts;
if (duration > 0) {
if (st->duration == AV_NOPTS_VALUE || st->info->last_duration<= 0 ||
(st->duration < duration && FFABS(duration - st->info->last_duration) < 60LL*st->time_base.den / st->time_base.num))
st->duration = duration;
st->info->last_duration = duration;
}
}
av_packet_unref(pkt);
}
/* check if all audio/video streams have valid duration */
if (!is_end) {
is_end = 1;
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_VIDEO:
case AVMEDIA_TYPE_AUDIO:
if (st->duration == AV_NOPTS_VALUE)
is_end = 0;
}
}
}
} while (!is_end &&
offset &&
++retry <= DURATION_MAX_RETRY);
av_opt_set(ic, "skip_changes", "0", AV_OPT_SEARCH_CHILDREN);
/* warn about audio/video streams which duration could not be estimated */
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->duration == AV_NOPTS_VALUE) {
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_VIDEO:
case AVMEDIA_TYPE_AUDIO:
if (st->start_time != AV_NOPTS_VALUE || st->first_dts != AV_NOPTS_VALUE) {
av_log(ic, AV_LOG_DEBUG, "stream %d : no PTS found at end of file, duration not set\n", i);
} else
av_log(ic, AV_LOG_DEBUG, "stream %d : no TS found at start of file, duration not set\n", i);
}
}
}
fill_all_stream_timings(ic);
/* 重新将文件的position置到old_offset位置上 */
avio_seek(ic->pb, old_offset, SEEK_SET);
for (i = 0; i < ic->nb_streams; i++) {
int j;
st = ic->streams[i];
st->cur_dts = st->first_dts;
st->last_IP_pts = AV_NOPTS_VALUE;
st->last_dts_for_order_check = AV_NOPTS_VALUE;
for (j = 0; j < MAX_REORDER_DELAY + 1; j++)
st->pts_buffer[j] = AV_NOPTS_VALUE;
}
}
estimate_timings_from_bit_rate
这个方法是当
码率
和file size
已知的情况下去计算duration
计算公式如下:
duration = file size(字节为单位) * 8 / bit rate(bps为单位)
static void estimate_timings_from_bit_rate(AVFormatContext *ic)
{
int64_t filesize, duration;
int i, show_warning = 0;
AVStream *st;
/* if bit_rate is already set, we believe it */
if (ic->bit_rate <= 0) {
int64_t bit_rate = 0;
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->codecpar->bit_rate <= 0 && st->internal->avctx->bit_rate > 0)
st->codecpar->bit_rate = st->internal->avctx->bit_rate;
if (st->codecpar->bit_rate > 0) {
if (INT64_MAX - st->codecpar->bit_rate < bit_rate) {
bit_rate = 0;
break;
}
bit_rate += st->codecpar->bit_rate;
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && st->codec_info_nb_frames > 1) {
// If we have a videostream with packets but without a bitrate
// then consider the sum not known
bit_rate = 0;
break;
}
}
ic->bit_rate = bit_rate;
}
/* if duration is already set, we believe it */
if (ic->duration == AV_NOPTS_VALUE &&
ic->bit_rate != 0) {
filesize = ic->pb ? avio_size(ic->pb) : 0;
if (filesize > ic->internal->data_offset) {
filesize -= ic->internal->data_offset;
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if ( st->time_base.num <= INT64_MAX / ic->bit_rate
&& st->duration == AV_NOPTS_VALUE) {
duration = av_rescale(8 * filesize, st->time_base.den,
ic->bit_rate *
(int64_t) st->time_base.num);
st->duration = duration;
show_warning = 1;
}
}
}
}
if (show_warning)
av_log(ic, AV_LOG_WARNING,
"Estimating duration from bitrate, this may be inaccurate\n");
}
update_stream_timings
update_stream_timings
方法用来计算统一的的start time
、duration
.
- start time:比较每个流的
start time
, 获得最小的,赋值给ic->start_time
- duration:先获取每个节目的中最小的
start time
, 再获取每个节目的最大的end time
, 然后相减,就得到了每个节目的duraion
, 再对比每个节目中的duration
, 将最大的赋值给ic->duration
。- bitrate:通过
file size
和duration
计算出
static void update_stream_timings(AVFormatContext *ic)
{
int64_t start_time, start_time1, start_time_text, end_time, end_time1, end_time_text;
int64_t duration, duration1, filesize;
int i;
AVStream *st;
AVProgram *p;
start_time = INT64_MAX;
start_time_text = INT64_MAX;
end_time = INT64_MIN;
end_time_text = INT64_MIN;
duration = INT64_MIN;
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 (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
if (start_time1 < start_time_text)
start_time_text = start_time1;
} else
start_time = FFMIN(start_time, start_time1);
end_time1 = av_rescale_q_rnd(st->duration, st->time_base,
AV_TIME_BASE_Q,
AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
if (end_time1 != AV_NOPTS_VALUE && (end_time1 > 0 ? start_time1 <= INT64_MAX - end_time1 : start_time1 >= INT64_MIN - end_time1)) {
end_time1 += start_time1;
if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
end_time_text = FFMAX(end_time_text, end_time1);
else
end_time = FFMAX(end_time, end_time1);
}
for (p = NULL; (p = av_find_program_from_stream(ic, p, i)); ) {
if (p->start_time == AV_NOPTS_VALUE || p->start_time > start_time1)
p->start_time = start_time1;
if (p->end_time < end_time1)
p->end_time = end_time1;
}
}
if (st->duration != AV_NOPTS_VALUE) {
duration1 = av_rescale_q(st->duration, st->time_base,
AV_TIME_BASE_Q);
duration = FFMAX(duration, duration1);
}
}
if (start_time == INT64_MAX || (start_time > start_time_text && start_time - start_time_text < AV_TIME_BASE))
start_time = start_time_text;
else if (start_time > start_time_text)
av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream starttime %f\n", start_time_text / (float)AV_TIME_BASE);
if (end_time == INT64_MIN || (end_time < end_time_text && end_time_text - end_time < AV_TIME_BASE)) {
end_time = end_time_text;
} else if (end_time < end_time_text) {
av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream endtime %f\n", end_time_text / (float)AV_TIME_BASE);
}
if (start_time != INT64_MAX) {
ic->start_time = start_time;
if (end_time != INT64_MIN) {
if (ic->nb_programs > 1) {
for (i = 0; i < ic->nb_programs; i++) {
p = ic->programs[i];
if (p->start_time != AV_NOPTS_VALUE && p->end_time > p->start_time)
duration = FFMAX(duration, p->end_time - p->start_time);
}
} else
duration = FFMAX(duration, end_time - start_time);
}
}
if (duration != INT64_MIN && duration > 0 && ic->duration == AV_NOPTS_VALUE) {
ic->duration = duration;
}
if (ic->pb && (filesize = avio_size(ic->pb)) > 0 && ic->duration > 0) {
/* compute the bitrate */
double bitrate = (double) filesize * 8.0 * AV_TIME_BASE /
(double) ic->duration;
if (bitrate >= 0 && bitrate <= INT64_MAX)
ic->bit_rate = bitrate;
}
}