avformat_find_stream_info 代码简明注释与理解


author: hjjdebug
date: 2025年 04月 02日 星期三 14:06:06 CST
description: avformat_find_stream_info 代码简明注释与理解


文章目录

    • 1. 前言
    • 2. 代码标注
    • 3 补充:
      • 3.1. 关于avctx->ticks_per_frame,
      • 3.2. 关于avctx->time_base 的计算, 由帧率导出.
      • 3.3. 关于 avctx->framerate 帧率的计算.

1. 前言

600行代码如何阅读, 单步调试有死循环,累到手抽筋,跟不出来,
采用重点标注之法,非重点用…标注, 只跟一层(就是本层).
把代码缩减到 200多行 并重点标注,
至此理解了avformat_find_stream_info 到底都找了什么东西,怎么找的.

2. 代码标注

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
{
	//设置max_analyze_duration 为5秒或7秒等
    if (!max_analyze_duration) {
	  .....
    }

	//第一次枚举流,100行代码
    for (i = 0; i < ic->nb_streams; i++) {
		//功能,初始化内部av codec context: avctx
		//把流的时基赋值给内部avctx
		//用流的信息更新流的参数信息及流内部信息
        //根据流参数codec_id,初始化分析器
		//把流codecpar copy给avctx
		//根据codec_id 找到codec
		//调用avcodec_open2打开codec
    }

	//初始化 internal->info 的 last_dts, fps(frame per stream)的 first dts,last dts
    for (i = 0; i < ic->nb_streams; i++) {
	......
    }
	//死循环,要找到其退出条件
	//其退出有正常退出, 超大小退出,超时间退出
    for (;;) {
        const AVPacket *pkt;
        int analyzed_all_streams;
        ff_check_interrupt(&ic->interrupt_callback); //回调一下用户设置的回调函数,没有则直接返回

        /* check if one codec still needs to be handled */
		//这个小循环总的功能是判断条件是否满足,不满足就break, 一旦break后面就还得取包,解包找信息
        for (i = 0; i < ic->nb_streams; i++) {
            st = ic->streams[i];
            if (!has_codec_parameters(st, NULL))  //还没有codec参数就退出
                break;

			//设置fps_analyze_framecount 数, 
            int fps_analyze_framecount = 20;

            int count; //到处用count,可不是好习惯,跟外边的count重名了,把外面的改名吧,外边叫count_w
            count = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ?   //有时间戳取值流内部 duration_count
                       st->internal->info->codec_info_duration_fields/2 :
                       st->internal->info->duration_count;

		    //如果视频流的额定帧率和平均帧率都为0, 帧数count 小于fps_analyze_framecount,就退出
			//换句话说,分析的帧数还不够就退出
            if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
                st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
                if (count < fps_analyze_framecount)
                    break;
            }
            // Look at the first 3 frames if there is evidence of frame delay
            // but the decoder delay is not set.
			//检查前3个frame, 是bframe, 有delay证据退出
			//有extradata且还没有初始化等等,退出.
			//流的first_dts无效并且流有时间戳并其还满足其它条件,退出.
        }
        analyzed_all_streams = 0;
        if (!missing_streams || !*missing_streams) //如果 missing_streams 为假,且i==ic->nb_streams, 就是找到了所有流信息
            if (i == ic->nb_streams) {  // 上个小循环提前退出,这个条件就不会满足了.
                analyzed_all_streams = 1; //分析了所有的流
                if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) { //format 有头部,认为信息已经找全
                    av_log(ic, AV_LOG_DEBUG, "All info found\n");
                    break;  //这个退出才是找到所有信息后的退出!!
                }
            }
        /* We did not get all the codec info, but we read too much data. */
        if (read_size >= probesize) {   // probesize 的作用
		    //读取大小超过探测大小,提示探测大小已经到达
			//如果视频流额定帧率为0并且duration_count<=1, 提示没有足够的帧来评估帧率
            break; // 退出死循环.
        }

        ret = read_frame_internal(ic, pkt1); //读一个包

        if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) { //如果ic 有缓存,送到packet_buffer中
            ret = avpriv_packet_list_put(&ic->internal->packet_buffer,
                                     &ic->internal->packet_buffer_end,
                                     pkt1, NULL, 0);
            pkt = &ic->internal->packet_buffer_end->pkt; //从缓存底部取包给pkt
        } else {
            pkt = pkt1; //无缓存直接给pkt
        }

        st = ic->streams[pkt->stream_index];
        if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
            read_size += pkt->size;  //如果流不是仅带一个封面的流,则read_size加包的大小

        avctx = st->internal->avctx;
        if (!st->internal->avctx_inited) { // avctx 还没有初始化,进行一次初始化,把流参数copy给它
            ret = avcodec_parameters_to_context(avctx, st->codecpar);
            st->internal->avctx_inited = 1;
        }
        //下面仅在包pkt-pts有效,且st->codec_info_nb_frames>1才能走到,就是说从第2个frame才进入
        if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) {
            /* check for non-increasing dts */
			//检查流的时间戳是否是连续递长的,不是则给警告.
			 //检查dts 是否连续,如果时差超过1000个包为不连续,不连续给警告
			//更新流的dts 值
        }
		//从第2个frame 才有意义
        if (st->codec_info_nb_frames>1) {
            int64_t t = 0;
            int64_t limit;
			//计算分析的时长 t
			//判定分析的包的时间是否大于限定的分析时间,超过给出提示,退出分析(max_duration 的作用)
            if (t >= limit) {
                av_log(ic, AV_LOG_VERBOSE, "max_analyze_duration %"PRId64" reached at %"PRId64" microseconds st:%d\n",
                       limit,
                       t, pkt->stream_index);
                if (ic->flags & AVFMT_FLAG_NOBUFFER)
                    av_packet_unref(pkt1);
                break;
            }
            if (pkt->duration) {
			//更新st->internal->info->codec_info_duration_fields // 时长成员变量
            }
        }
		//判断视频流是否是dts,pts不想等的类型(有bframe的流都这样),这叫解frame必需要等待的证据. 或者叫有b_frame的证据
        if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            ff_rfps_add_frame(ic, st, pkt->dts);
            if (pkt->dts != pkt->pts && pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE)
                st->internal->info->frame_delay_evidence = 1;
        }
        if (!st->internal->avctx->extradata) {
            ret = extract_extradata(st, pkt);  //解包的额外数据
        }
		//因为还没有找到信息,所以解码这个包.
        try_decode_frame(ic, st, pkt,
                         (options && i < orig_nb_streams) ? &options[i] : NULL);

        st->codec_info_nb_frames++; //解码个数加1, count_w加1
        count_w++;
    }
	//退出死循环后
    if (eof_reached) { 如果文件尾已经到达, 对于处理小文件的时候!
	//那就继续运行,如果没打开codec, 则调用avcodec_open2打开它,并从pts更新dts
    }

    if (flush_codecs) {
	  //如果刷空codec, 那就把空包给decoder 来刷空decoder, 一般不用刷新
		err = try_decode_frame(ic, st, empty_pkt,
								(options && i < orig_nb_streams)
								? &options[i] : NULL);
    }

    ff_rfps_calculate(ic); //计算额定frame
    //下面近100行代码,给出平均帧率和额定帧率的计算
    for (i = 0; i < ic->nb_streams; i++) {
        st = ic->streams[i];
        avctx = st->internal->avctx;
        if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) 
		{
            if (avctx->codec_id == AV_CODEC_ID_RAWVIDEO && !avctx->codec_tag && !avctx->bits_per_coded_sample) {
			    //更新一下codec_tag 如果需要的话, 我测试的流不需要
                uint32_t tag= avcodec_pix_fmt_to_codec_tag(avctx->pix_fmt);
                avctx->codec_tag= tag;
            }

            /* estimate average framerate if not set by demuxer */
            if (st->internal->info->codec_info_duration_fields &&
                !st->avg_frame_rate.num &&  // 还没有平均帧率?
                st->internal->info->codec_info_duration) {
                int best_fps      = 0;
                double best_error = 0.01;

				//平均帧率应该是帧数/时长, 时长换成了秒, 60000是最大输出值限制,分母中为啥有个2?
				//这是因为隔行扫描的视频一帧是按2场计算的,所以逐行扫描也应该符合它一帧按2场计算
                av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                          st->internal->info->codec_info_duration_fields * (int64_t) st->time_base.den,
                          st->internal->info->codec_info_duration * 2 * (int64_t) st->time_base.num, 60000);
				//下面代码是一个与400个频率构成的表比较,看看跟标准频率差别最小的是哪一个,找到最好的,
				//如果误差<0.01或更小, 那这个频率应该是标准频率,重新赋值给平均帧率
                for (j = 0; j < MAX_STD_TIMEBASES; j++) {
				......
                }
                if (best_fps)
                    av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                              best_fps, 12 * 1001, INT_MAX);
            }

			//还没有额定帧率? 由avctx->time_base 和ticks_per_frame 给出
			//ticks_per_frame 是一个frame中视频周期的次数,例如h264中,从SPS中获得该参数
			//我测试的数据是 time_base{1,50},ticks_per_frame=2, 计算得{25,1}
            if (!st->r_frame_rate.num) { 
                if (avctx->time_base.den * (int64_t) st->time_base.num
                    <= avctx->time_base.num * avctx->ticks_per_frame * (uint64_t) st->time_base.den) {
                    av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den,
                              avctx->time_base.den, (int64_t)avctx->time_base.num * avctx->ticks_per_frame, INT_MAX);
                } else {
                    st->r_frame_rate.num = st->time_base.den;
                    st->r_frame_rate.den = st->time_base.num;
                }
            }
            if (st->internal->display_aspect_ratio.num && st->internal->display_aspect_ratio.den) {
                AVRational hw_ratio = { avctx->height, avctx->width };
                st->sample_aspect_ratio = av_mul_q(st->internal->display_aspect_ratio,
                                                   hw_ratio);
            }
        } 
		else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) 
		{ //音频简单,只是赋值 bits_per_coded_sample;
            if (!avctx->bits_per_coded_sample)
                avctx->bits_per_coded_sample = av_get_bits_per_sample(avctx->codec_id);
            }
        }
    }

    if (probesize)
        estimate_timings(ic, old_offset);  //估算和打印流的开始时间,持续时间

	//判定信息是否找到
    for (i = 0; i < ic->nb_streams; i++) {
        st = ic->streams[i];
        if (!has_codec_parameters(st, &errmsg)) { //如果没有参数,则打印警告信息
            char buf[256];
            avcodec_string(buf, sizeof(buf), st->internal->avctx, 0);
            av_log(ic, AV_LOG_WARNING,
                   "Could not find codec parameters for stream %d (%s): %s\n"
                   "Consider increasing the value for the 'analyzeduration' (%"PRId64") and 'probesize' (%"PRId64") options\n",
                   i, buf, errmsg, ic->max_analyze_duration, ic->probesize);
        } 
    }

    ret = compute_chapters_end(ic); //计算章节

    /* update the stream parameters from the internal codec contexts */
	//把内部avctx 数据copy到 st->codecpar, 和 st->codec 
    for (i = 0; i < ic->nb_streams; i++) {
        st = ic->streams[i];
        if (st->internal->avctx_inited) {
        ret = avcodec_parameters_from_context(st->codecpar, st->internal->avctx);
        ret = add_coded_side_data(st, st->internal->avctx);
        ret = avcodec_parameters_to_context(st->codec, st->codecpar);

        st->codec->framerate = st->avg_frame_rate;

        // Fields unavailable in AVCodecParameters
        st->codec->coded_width = st->internal->avctx->coded_width;
        st->codec->coded_height = st->internal->avctx->coded_height;
        st->codec->properties = st->internal->avctx->properties;
    }

find_stream_info_err:  //非正常退出或正常退出
    for (i = 0; i < ic->nb_streams; i++) {
        st = ic->streams[i];
		释放内存对象
    }
    return ret;

}

如此我才第一次单步走过avformat_find_stream_info()

3 补充:

3.1. 关于avctx->ticks_per_frame,

经查, 发现对于h264的流,它直接就初始化其为2. avctx->ticks_per_frame = 2;

3.2. 关于avctx->time_base 的计算, 由帧率导出.

avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1}));
计算得到的是{1,50}, 因为avctx->framerate={25,1}, avctx->ticks_per_frame={2,1},相乘为{50,1}
取反为{1,50}, 这就是avctx 帧率到时基的计算过程

3.3. 关于 avctx->framerate 帧率的计算.

其在h264_slice_header_init 中, 由sps(sequence parameter set) 来设置,不过也用到了ticks_per_frame
int64_t den = sps->time_scale;
av_reduce(&h->avctx->framerate.den, &h->avctx->framerate.num,
sps->num_units_in_tick * h->avctx->ticks_per_frame, den, 1 << 30);
其中 sps->time_scale 为 50 => den=50
sps->num_units_in_tick = 1,
h->avctx->ticks_per_frame =2, 两者相乘为2,分母time_scale为50,约分简化
算得: avctx->framerate={25,1}

你可能感兴趣的:(#,ffmpeg,avformat_find_,strea,_info)