主要介绍的函数:estimate_timings,estimate_timings_from_pts,update_stream_timings.
estimate_timings
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);
}
if ((!strcmp(ic->iformat->name, "mpeg") ||
!strcmp(ic->iformat->name, "mpegts")) &&
file_size && ic->pb->seekable) {
/* get accurate estimate from the PTSes */
//mpeg,mpegts文件通过pts获取时长
estimate_timings_from_pts(ic, old_offset);
ic->duration_estimation_method = AVFMT_DURATION_FROM_PTS;
} else if (has_duration(ic)) {
//MP4等会进入这种情况。
/* 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 {
//不准确,通过文件大小除以码率的方式获取,如aac音频文件。
/* 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);
}
}
获取ts的时长 estimate_timings_from_pts
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;
//DURATION_MAX_READ_SIZE在高码率时,会到不到最大PTS,以为B帧是先解码后播放,所以PTS不是递增的,导致得到时长变小。
offset = filesize - (DURATION_MAX_READ_SIZE << retry);
if (offset < 0)
offset = 0;
//从剩下的250K中找。
avio_seek(ic->pb, offset, SEEK_SET);
read_size = 0;
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) {
// pkt->duration都是0
ff_compute_frame_duration(ic, &num, &den, st, st->parser,pkt);
if (den && num) {
//pkt->duration等于单帧的时长,如当1秒25帧时为3600。
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;
//即timelen=当前PTS-开始PTS+单帧时长。音频算音频的,视频算视频的
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->duration为最大PTS-开始PTS+单帧时长
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 notset\n", i);
}
}
}
fill_all_stream_timings(ic);
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;
}
}
update_stream_timings
static void update_stream_timings(AVFormatContext *ic)
{
int64_t start_time, start_time1, start_time_text, end_time, end_time1;
int64_t duration, duration1, filesize;
int i;
AVStream *st;
AVProgram *p;
start_time = INT64_MAX;
start_time_text = INT64_MAX;
end_time = 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 && start_time1 <= INT64_MAX - end_time1) {
end_time1 += start_time1;
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);
}
}
// 如下,比如有三个音轨 ,这样duration就是最大的5015500。
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 (start_time != INT64_MAX) {
ic->start_time = start_time;
if (end_time != INT64_MIN) {
if (ic->nb_programs) {
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;
}
}