avformat_find_stream_info 为什么总是等到超时或超过大小才退出?

avformat_find_stream_info 为什么总是等到超时或超过大小才退出?
/* author: hjjdebug
*  date  : 2023年 09月 21日 星期四 11:05:56 CST
*  description: avformat_find_stream_info 为什么不能正常退出了?
*/

查文档:
mpegts:
scan_all_pmts
    Scan and combine all PMTs. The value is an integer with value from -1 to 1 (-1 means automatic setting, 1 means enabled, 0 means disabled). Default value is -1.

阅读及调试avformat_find_stream_info 代码
avformat_find_stream_info 满足下面3个条件之一即可退出循环.
被动退出:
1. 超时退出
2. 超过分析的数据大小退出.

主动退出:
3. 查到了所有流信息并且ctx_flag 不包含 AVFMTCTX_NOHEADER 标志

   这个标志定义有点绕, 是说上下文没有头是假, 就是说上下文是找到了头的.
   代码是这样的.
    if (i == ic->nb_streams) {  // 分析的流个数已经等于所有的流个数
        analyzed_all_streams = 1;
        /* NOTE: If the format has no header, then we need to read some
         * packets to get most of the streams, so we cannot stop here. */
         // 退出还需要一个条件, 标志ctx_flag 不能包含 AVFMTCTX_NOHEADER 标志.
        if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) {
            /* If we found the info for all the codecs, we can stop. */
            ret = o_count;
            av_log(ic, AV_LOG_DEBUG, "All info found\n");
            flush_codecs = 0;
            break;
        }
    }

我们来观察一下: ic->ctx_flags 变量,就是这里的avf->ctx_flags变量, 测试文件 ts流
可以正常退出的代码是这样的。
-----------------------------------------------------------
const char *filename="/opt/test/caitiao_same_10s.ts";
AVFormatContext *avf = avformat_alloc_context();
avformat_open_input(&avf, filename, NULL, NULL);
avformat_find_stream_info(avf, NULL);
-----------------------------------------------------------

(gdb) watch avf->ctx_flags
Hardware watchpoint 2: avf->ctx_flags
(gdb) c
Continuing.
Hardware watchpoint 2: avf->ctx_flags
Old value = 0
New value = 1
0x00007ffff7df671f in mpegts_read_header (s=0x555555559d00) at libavformat/mpegts.c:3109
3109            s->ctx_flags |= AVFMTCTX_NOHEADER;  // 初始化时, 先假定还没有找到头.
(gdb)

此时的调用栈为:
0 in mpegts_read_header of libavformat/mpegts.c:3109
1 in avformat_open_input of libavformat/utils.c:609
2 in main of main.c:37

说明在read_header 的时候, mpegts 将ctx_flags标志初始化成1.


继续观察, 何时去掉的AVFMTCTX_NOHEADER 标志?
(gdb) c
Continuing.
Hardware watchpoint 2: avf->ctx_flags
Old value = 1
New value = 0
0x00007ffff7df59fc in handle_packet (ts=0x55555556ad40, packet=0x555555562d00 "G@\021\020", pos=188) at libavformat/mpegts.c:2846
2846                        ts->stream->ctx_flags &= ~AVFMTCTX_NOHEADER;

此时调用栈:
0 in handle_packet of libavformat/mpegts.c:2846
1 in handle_packets of libavformat/mpegts.c:2984
2 in mpegts_read_packet of libavformat/mpegts.c:3228
3 in ff_read_packet of libavformat/utils.c:844
4 in read_frame_internal of libavformat/utils.c:1547
5 in avformat_find_stream_info of libavformat/utils.c:3814
6 in main of main.c:46
代码:
    // stop find_stream_info from waiting for more streams
    // when all programs have received a PMT
    // 如果它有AVFMTCTX_NOHEADER 标志,并且 ts->scan_all_pmts 等于0或者-1, 才有可能去掉 NO_HEADER标志
    if (ts->stream->ctx_flags & AVFMTCTX_NOHEADER && ts->scan_all_pmts <= 0) { // ******* 关键判断
        int i;
        for (i = 0; i < ts->nb_prg; i++) {
            if (!ts->prg[i].pmt_found) //每一个节目的pmt表要都找到
                break;
        }
        if (i == ts->nb_prg && ts->nb_prg > 0) { //有节目,且所有节目pmt都找到. 后面还要再判断
            int types = 0;
            for (i = 0; i < ts->stream->nb_streams; i++) { // 此处的ts->stream 就是AVFormatContext
                AVStream *st = ts->stream->streams[i];
                if (st->codecpar->codec_type >= 0)
                    types |= 1<<st->codecpar->codec_type; //汇总每一个流的流类型
            }
            if ((types & (1<<AVMEDIA_TYPE_AUDIO) && types & (1<<AVMEDIA_TYPE_VIDEO)) || pos > 100000) { //音频类型,视频类型都找到
                av_log(ts->stream, AV_LOG_DEBUG, "All programs have pmt, headers found\n");
                ts->stream->ctx_flags &= ~AVFMTCTX_NOHEADER;  // *********** 关键代码, 取消AVFMTCTX_NOHEADER 标志
            }
        }
    }

程序就是一堆判断和运算.

在handle_packet 函数中,当满足以下3个条件时,将去掉 AVFMTCTX_NOHEADER 标志
1. ts->scan_all_pmts 要为0或者-1, 否则去不掉该标记
2. 所有节目的pmt表都找到了.
3. 检查所有流,必需包含音频流,视频流 (pos 超过100000也可以,这个条件回头再说)

不能正常退出的代码是这样的。
-----------------------------------------------------------
const char *filename="/opt/test/caitiao_same_10s.ts";
AVFormatContext *avf = avformat_alloc_context();
AVDictionary *m_options=NULL;
av_dict_set(&m_options, "scan_all_pmts", "1", 0); // 这个scan_all_pmts 选项,会经过千回百转设置到mpegts的scan_all_pmts选项中
avformat_open_input(&avf, filename, NULL, &m_options);
avformat_find_stream_info(avf, NULL);
-----------------------------------------------------------

对于 ts 流文件,如果你设置了scan_all_pmts 选项, 则主动退出条件将不会满足,因为它的ctx_flags一直是1而不能清0.
所以find_stream_info 不会主动退出. 只能等超时或超过指定大小才退出了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值