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<codecpar->codec_type; //汇总每一个流的流类型
            }
            if ((types & (1< 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 不会主动退出. 只能等超时或超过指定大小才退出了。

你可能感兴趣的:(#,ffmpeg,ffmpeg,stream_info)