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

2023-09-21 11:11:36

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 不会主动退出. 只能等超时或超过指定大小才退出了。

更多推荐

SpringSecurity

SpringSecurity从入门到精通参考代码0.简介​SpringSecurity是Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。​一般来说中大型的项目都是使用SpringSecurity来做安全框架。小项目有Shiro的比较多,因为相比与

深入JavaScript的运行原理

一、深入V8引擎原理1.JavaScript代码的执行JavaScript代码下载好之后,是如何一步步被执行的呢?我们知道,浏览器内核是由两部分组成的,以webkit为例:WebCore:负责HTML解析、布局、渲染等等相关的工作;JavaScriptCore:解析、执行JavaScript代码;另外一个强大的Java

邮件营销中为什么要细分联系人?

在电子商务行业,邮件营销成为了各大企业吸引客户、推广产品的主要方式之一。然而,有效进行邮件营销需要一套完善的联系人管理系统。本文将从以下五点详细探讨邮件营销联系人管理有必要吗?一、精确定位目标用户邮件营销联系人管理是通过收集、分析和管理用户信息的过程。通过建立一个详尽的联系人数据库,企业可以对客户进行细致的分类和分组。

计算机网络第五节 网络层

一,网络引入的目的1.网络层以下层次解决的问题,未解决的问题从7层结构上看,网络层下是数据链路层从4层结构上看,网络层下面是网络接口层至少我们看到的网络层下面是以太网以太网解决了什么问题?答:以太网解决了具体网络上主机间数据传输的问题;主机之间可以以物理地址,以广播的传输方式进行数据的交换传输没有解决人心不足答的问题:

Springboot定时任务 Spring task

文章目录SpringTask简单操作SpringBoot注解开始1.fixDelay2.fixedRate单线程3.fixedRate多线程4.initialDelay5.cron(推荐)6.任务调度配置SpringTask简单操作SpringBoot注解开始@EnableScheduling@SpringBootAp

【JavaEE】多线程(三)

多线程(三)续上文,多线程(二),我们已经讲了创建线程Thread的一些重要的属性和方法那么接下来,我们继续来体会了解多线程吧~文章目录多线程(三)线程启动startstart与run的区别中断线程interrupt方法一方法二线程等待join线程状态线程安全线程安全问题的原因synchronized线程启动start

Scrum敏捷开发企业培训大纲介绍-企业内训

课程简介Scrum是目前运用最为广泛的敏捷开发方法,是一个轻量级的项目管理和产品研发管理框架。这是一个两天的实训课程,面向研发管理者、项目经理、产品经理、研发团队等,旨在帮助学员全面系统地学习Scrum和敏捷开发,帮助企业快速启动敏捷实施。课程采用案例讲解+沙盘演练的方式授课,通过两天的强化训练学员将学会基于Scrum

Java笔记:JVM优化分析

1.我们为什么要对jvm做优化?在本地开发环境中我们很少会遇到需要对jvm进行优化的需求,但是到了生产环境,我们可能将有下面的需求:运行的应用“卡住了”,日志不输出,程序没有反应服务器的CPU负载突然升高在多线程应用下,如何分配线程的数量?……说明:使用的jdk版本为1.8。2.jvm的运行参数在jvm中有很多的参数可

iOS 17中的Safari配置文件改变了游戏规则,那么如何设置呢

Safari在iOS17中最大的升级是浏览配置文件——能够在一个应用程序中创建单独的选项卡和书签组。这些也可以跟随你的iPad和Mac,但在本指南中,我们将向你展示如何使用运行iOS17的iPhone。你可能有点困惑,为什么Safari中没有明显的位置可以添加个人资料,我们当然也是。诀窍是,你需要先在“设置”中添加配置

docker day04

Dockerfile:-FORM:1.指定基础镜像,可以起别名,也可以指定多个FROM指令,用于多阶段构建;2.加载触发器,加载ONBUILD指令;3.不指定基础镜像,声明当前镜像不依赖任何镜像,官方保留字:scratch-RUN:1.在容器中运行命令,同一个Dockerfile中可以有多个RUN指令并不会被覆盖;-C

基于Android高校智慧公寓选房APP的设计与实现

基于Android高校智慧公寓选房APP的设计与实现摘要学生宿舍选房是大学新生入学必须面临的问题。能够统计各宿舍的分配使用情况,清楚查看宿舍状态,图像化展示,一目了然。并与其它系统共用一个数据中心,共享信息,避免了数据的错漏、数据的重复冗余以及网络资源的浪费。本系统通过Android系统的设计的宿舍选房系统,主要包含系

热文推荐