FFmpeg5.0源码阅读——avformat_find_stream_info

  摘要:在使用FFmpeg库时通常使用avformat_find_stream_info相关函数来探测流的基本信息,为了更加深入理解FFmpeg的基本流程,本文根据FFmpeg 5.0的源码详细描述了该函数的具体实现。
  关键字:FFmpeg
  读者须知:读者需要了解FFmpeg的基本使用流程,以及一些FFmpeg的基本常识,了解FFmpegIO相关的内容,以及大致的解码流程。

1 avformat_find_stream_info

  avformat_open_input的主要工作时打开流并且对流进行初步的检测设置一些当前流的基本属性。而avformat_find_stream_info在调用时其耗时是比较久做的流信息探测的工作更多。avformat_find_stream_info比仅仅会读取文件来获取流信息,而且会尝试进行部分解码,根据AVPcaketAVFrame更加准确的判断流的基本信息。部分场景下虽然跳过解码过程可以提升avformat_find_stream_info的执行速度,但是也会导致流探测的信息不准确。

/**
 * Read packets of a media file to get stream information. This
 * is useful for file formats with no headers such as MPEG. This
 * function also computes the real framerate in case of MPEG-2 repeat
 * frame mode.
 * The logical file position is not changed by this function;
 * examined packets may be buffered for later processing.
 *
 * @param ic media file handle
 * @param options  If non-NULL, an ic.nb_streams long array of pointers to
 *                 dictionaries, where i-th member contains options for
 *                 codec corresponding to i-th stream.
 *                 On return each dictionary will be filled with options that were not found.
 * @return >=0 if OK, AVERROR_xxx on error
 *
 * @note this function isn't guaranteed to open all the codecs, so
 *       options being non-empty at return is a perfectly normal behavior.
 *
 * @todo Let the user decide somehow what information is needed so that
 *       we do not waste time getting stuff the user does not need.
 */
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

  avformat_find_stream_info的最终目的就是获取目标流的详细信息如果拿不到就会尝试获取码流AVPacket,从码流中解析相关流信息,如果码流中也拿不到就会尝试解码从AVFrame中获取。
在这里插入图片描述

2 详细调用过程

  avformat_find_stream_info代码的内容非常难以阅读,这里不贴对应的代码了,我们简单分析下。
  在进行探测前下面这部分代码用来设置探测的时长,可以看到部分时长是和格式强相关的,应该和文件本身关系比较大。

max_stream_analyze_duration = max_analyze_duration;
max_subtitle_analyze_duration = max_analyze_duration;
if (!max_analyze_duration) {
    max_stream_analyze_duration =
    max_analyze_duration        = 5*AV_TIME_BASE;
    max_subtitle_analyze_duration = 30*AV_TIME_BASE;
    if (!strcmp(ic->iformat->name, "flv"))
        max_stream_analyze_duration = 90*AV_TIME_BASE;
    if (!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts"))
        max_stream_analyze_duration = 7*AV_TIME_BASE;
}

  然后就是循环遍历打开每个流的解码器,为后续解码做准备,这部分就是调用avcodec_find_decoderavcodec_open2查找解码器和打开解码器,具体实现等到讲解码时再说。
  之后便是不断循环检测,循环内部会不断判断当前是否已经成功获取完整的流信息,成功或者超出探测时长上限的话就结束;否则就会从读取AVPacket和解码帧AVFrame,读取AVPacket和解码AVFrame分别是调用ff_read_packetavcodec_send_packet,avcodec_receive_frame进行码流的读取和解码。
  estimate_timings用于估算当前媒体文件的时长,根据不同的格式会采取不同的方式:

  1. mpegmpegts会采用pts的方式估算,即调用estimate_timings_from_pts估算;
  2. 如果非情况1且流本身已经探测到一部分时长信息,则调用fill_all_stream_timings根据已有的时长相关信息进行统一;
  3. 其他情况调用estimate_timings_from_bit_rate,利用文件码流大小和码率估算;

  estimate_timings_from_pts核心就是累加AVPacket的时长,如下:

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->internal->first_dts  != AV_NOPTS_VALUE)) {
        if (pkt->duration == 0) {
            ff_compute_frame_duration(ic, &num, &den, st, st->internal->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->internal->first_dts;
        if (duration > 0) {
            if (st->duration == AV_NOPTS_VALUE || st->internal->info->last_duration<= 0 ||
                (st->duration < duration && FFABS(duration - st->internal->info->last_duration) < 60LL*st->time_base.den / st->time_base.num))
                st->duration = duration;
            st->internal->info->last_duration = duration;
        }
    }
    av_packet_unref(pkt);
}

  而estimate_timings_from_bit_rate代码比较简单就是duration=filesize/bitrate

 duration = av_rescale(filesize, 8LL * st->time_base.den,
                                          ic->bit_rate *
                                          (int64_t) st->time_base.num);
                    st->duration = duration;

  最后根据已经探测的的信息统一对时间等信息进行统一。

3 参考文献

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值