av_read_frame 代码研究

------------------------------------------------------------
author: hjjdebug
date: 2024年 07月 05日 星期五 11:02:51 CST
av_read_frame 代码研究
------------------------------------------------------------

有人只标注一层,标注一层太肤浅了.不能了解底层之精妙.
有人给出调用框图, 框图又太笼统了,还是了解不了细节.
如果来一个完全的标注,那也有两个毛病.
1. 代码太多,篇幅太大.
2. 重点不突出, 一叶障目,窥不见森林.
那怎么做呢? 
捡一个最深的调用为纲,搞清其工作原理,再辅以上下的各个问题解释清楚。
做到纲举目张.

想读一个frame, 以ts文件为例, 它必需要读文件, 并且它要在
合适的位置停止读,因为数据已经构成了一个frame.

mpegts.c 就是ts 文件执行读写和分析的基础文件
我在mpegts.c 中设下了一个断点.
0 in mpegts_push_data of libavformat/mpegts.c:1377
1 in handle_packet of libavformat/mpegts.c:2846
2 in handle_packets of libavformat/mpegts.c:2975
3 in mpegts_read_packet of libavformat/mpegts.c:3219
4 in ff_read_packet of libavformat/utils.c:843
5 in read_frame_internal of libavformat/utils.c:1546
6 in av_read_frame of libavformat/utils.c:1750
7 in main of main.c:304

上层框架简单,就是接口,调用别人干活,主要看下层,是具体的操作:
handel_packes:
    它循环调用读包, 每次读188字节,处理包 handle_packet, 满足条件才会退出.
handle_packet:
    根据包的pid 可以拿到它的过滤器
    MpegTsFilter   tss = ts->pids[pid];
    过滤器的类型也是在分析section 数据时建立的,例如pes 的过滤器是分析pmt表时建立的.
    pmt表中记录了每个节目包含哪些流(最常见的是一个音频,一个视频), 这些流的ID 是多少
    如果过滤器的类型是PES, 就是说该小包数据是pes,就会回调pes_cb, 这就是mpegts_push_data
    if (tss->type == MPEGTS_PES) 
    {
        if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start,
                                            pos - ts->raw_packet_size)) < 0)
        return ret;
    }
    就是说从id拿到对应的id对象(tss),然后调用对应的回调函数tss->u.pes_filter.pes_cb = mpegts_push_data
    可见pes_cb并不是tss的直接成员,而是其下成员u.pes_filter的一个成员. 又多了一个中间管理者pes_filter
    可以认为pes_filter和section_filter 是并列的,是一个联合.
        union {
        MpegTSPESFilter pes_filter;
        MpegTSSectionFilter section_filter;
    } u;

    而tss是它们的上一级.
    struct MpegTSFilter {
    int pid;
    int es_id;
    int last_cc; /* last cc code (-1 if first packet) */
    int64_t last_pcr;
    int discard;
    enum MpegTSFilterType type;
    union {
        MpegTSPESFilter pes_filter;
        MpegTSSectionFilter section_filter;
    } u;
};

mpegts_push_data:
    它大部分时间是把负载直接copy到pes 数据区,但当数据copy够了时,就设置退出条件.
    所谓copy够就是拷贝的pes数据pes->data_index 加头部大小pes->header_size等于
    pes包总大小加上pes包起始大小
    switch(pes->state)
    {
    case MPEGTS_PAYLOAD
       memcpy(pes->buffer->data + pes->data_index, p, buf_size);
        pes->data_index += buf_size;
        if (!ts->stop_parse && pes->total_size < MAX_PES_PAYLOAD &&
            pes->pes_header_size + pes->data_index == pes->total_size + PES_START_SIZE) 
        { //数据copy够了,退出
            ts->stop_parse = 1;
            ret = new_pes_packet(pes, ts->pkt); //后面如果还有数,那就属于新包数据了
            if (ret < 0)
                return ret;
        }
    }
//下面是 gdb 打印的某一次结果
(gdb) p pes->pes_header_size
$20 = 14
(gdb) p pes->data_index //此处是负载的位置索引
$21 = 2304
(gdb) p pes->total_size
$22 = 2312

14+2304 = 2312 + 6

下面我们来分析一个这几个概念.
1. #define PES_START_SIZE  6
pes包时由固定的3个字节00 00 01 及后面3个字节stream_id(1)+包长度(packet_length)
2. pes->total_size
这就是上面pes 头部中的packet_length. 从码流中得到的大小
它在代码中什么位置赋值的?

    pes->total_size = AV_RB16(pes->header + 4);
    if (!pes->total_size)
        pes->total_size = MAX_PES_PAYLOAD; //200*1024
//当packet_length==0 时, 我们给最大的尺寸200K , 此时真正的大小要等到再遇到一个pes包头来确定大小.

    /* allocate pes buffer */
    pes->buffer = buffer_pool_get(ts, pes->total_size);
    if (!pes->buffer)
        return AVERROR(ENOMEM);

3. pes->pes_header_size
      if (pes->data_index == PES_HEADER_SIZE)  //#define PES_HEADER_SIZE 9
      {
        pes->pes_header_size = pes->header[8] + 9; //9字节是固定头,前面6字节是start头,后面3字节是可选头部,
        pes->state           = MPEGTS_PESHEADER_FILL;
      }
      
  其中:
  pes->header[6], 加扰说明,版权说明等
  pes->header[7], 7个标志位,说明是否有option跟随,例如pts,dts等等
  pes->header[8], 可选头部的长度,1byte 不会超过255

  头部大小范围,最大 MAX_PES_HEADER_SIZE (9+255)

4. pes->data_index
 当然是pes数据的位置索引了. 不过它的身份是变的, 一会是数据的索引,一会是负载的索引, 要看它的时机.
 变量吗,就是变着来, 不过这样容易混淆,用是简单了,读却费劲了. 最好是分两个或多个变量.角色应该唯一.
 就像变量i, 一会是这个的索引,一会是那个的索引,只要分的清,也可以不换名,因为用着简单.

 小结:
 读一个frame, 就是从原始数据中不停的读, 读到一个合适的位置停止读,把有效的数据作为一个frame反馈给上层.
 当然,如果你不是从数据文件中读,而是内存中已经有了,例如以链表形式或者数组形式,那copy给你就可以了.
 这就是av_read_frame的核心工作.
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/40006.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Lianwei 安全周报|2024.07.01

新的一周又开始了&#xff0c;以下是本周「Lianwei周报」&#xff0c;我们总结推荐了本周的政策/标准/指南最新动态、热点资讯和安全事件&#xff0c;保证大家不错过本周的每一个重点&#xff01; 政策/标准/指南最新动态 01 出于安全考虑&#xff0c;拜登下令禁用卡巴斯基杀毒…

【康复学习--LeetCode每日一题】3115. 质数的最大距离

题目&#xff1a; 给你一个整数数组 nums。 返回两个&#xff08;不一定不同的&#xff09;质数在 nums 中 下标 的 最大距离。 示例 1&#xff1a; 输入&#xff1a; nums [4,2,9,5,3] 输出&#xff1a; 3 解释&#xff1a; nums[1]、nums[3] 和 nums[4] 是质数。因此答案是…

SpringBoot各类数量限制及超出后抛出的异常

前言 在使用SpringBoot开发接口时&#xff0c;动不动的就发生各种超过默认值的限制&#xff0c;这里总结了下SpringBoot默认限制的设置以及可能会发生的异常&#xff0c;便于问题的排查和快速修改默认值。 配置项配置项说明默认值超过大小后抛出的异常spring.servlet.multipa…

系统管理(System Keeping):全新迭代,优化您的开发体验

随着科技的不断进步和用户需求的日益增长&#xff0c;系统管理&#xff08;System Keeping&#xff09;不断进行迭代更新&#xff0c;致力于为用户带来更加高效、便捷的开发体验。本次全新迭代&#xff0c;不仅在界面与交互上进行了革新&#xff0c;更在功能整合、个性化与安全…

ECOLOGY9重置系统管理员密码

ECOLOGY9系统管理员密码忘记需要重置&#xff1a; 1、KB2110之后版本加了防篡改逻辑&#xff0c;数据库中初始话密码需要将hashdata、signdata更新为空&#xff0c;执行如下语句初始化 update HrmResourceManager set password ‘C4CA4238A0B923820DCC509A6F75849B’,salt‘’…

Android --- Service

出自于此&#xff0c;写得很清楚。关于Android Service真正的完全详解&#xff0c;你需要知道的一切_android service-CSDN博客 出自【zejian的博客】 什么是Service? Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。 服务可由其他应用组件…

万字长文|关于 OpenAI 接口开发你应该知道的一切

这篇文章中个人结合自己的实践经验把 OpenAI 官方文档解读一遍。但是原文档涉及内容众多&#xff0c;包括微调&#xff0c;嵌入&#xff08;Embeddings&#xff09;等众多主题&#xff0c;我这里重点挑选自己开发高频使用到的&#xff0c;需要详细了解的可以自行前往官网阅读。…

Java中的文本搜索与全文检索引擎

Java中的文本搜索与全文检索引擎 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在现代应用程序中&#xff0c;处理和搜索大量文本数据是一项关键任务。传统的…

2024科技文化节程序设计竞赛

补题链接 https://www.luogu.com.cn/contest/178895#problems A. 签到题 忽略掉大小为1的环&#xff0c;答案是剩下环的大小和减环的数量 #include<bits/stdc.h> #include<iostream> #include<cstdio> #include<vector> #include<map> #incl…

c进阶篇(四):内存函数

内存函数以字节为单位更改 1.memcpy memcpy 是 C/C 中的一个标准库函数&#xff0c;用于内存拷贝操作。它的原型通常定义在 <cstring> 头文件中&#xff0c;其作用是将一块内存中的数据复制到另一块内存中。 函数原型&#xff1a;void *memcpy(void *dest, const void…

多模态融合算法应用:CT + 临床文本数据 + pyradiomics提取到的图像特征

多模态融合算法应用 CT 临床文本数据 pyradiomics提取图像特征 单模态建模临床数据建模pyradiomics提取图像特征建模CT建模 多模态建模前融合为什么能直接合并在一起&#xff1f; 后融合Med-CLIP&#xff1a;深度学习 可解释性 单模态建模 临床数据建模 临床文本数据&…

WPF Menu实现快捷键操作

很多小伙伴说&#xff0c;在Menu中&#xff0c;实现单个快捷键操作很简单&#xff0c;怎么实现多个快捷键操作和&#xff0c;组合快捷键呢&#xff0c;今天他来了。 上代码和效果图 一、Ctrl Shift 任意子母键实现快捷键组合 <Window x:Class"XH.TemplateLesson.M…

【测试开发】【postman】按顺序循环执行接口

postman按顺序循环执行接口 新建接口接口排序执行请求集合 新建接口 Request 001 Request 002 Request 003 接口排序 在Request 001的Tests中添加代码 postman.setNextRequest("Request 002");在Request 002的Tests中添加代码 postman.setNextRequest("Requ…

Redis 7.x 系列【17】四种持久化策略

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 案例演示2.1 无持久化2.2 RDB2.3 AOF2.4 混合模式2.4.1 方式一&#xff1a;…

线性代数|机器学习-P21概率定义和Markov不等式

文章目录 1. 样本期望和方差1.1 样本期望 E ( X ) \mathrm{E}(X) E(X)1.2 样本期望 D ( X ) \mathrm{D}(X) D(X) 2. Markov 不等式&Chebyshev不等式2.1 Markov不等式公式 概述2.2 Markov不等式公式 证明&#xff1a;2.3 Markov不等式公式 举例&#xff1a;2.4 Chebyshev不…

AI绘画 Stable Diffusion图像的脸部细节控制——采样器全解析

大家好&#xff0c;我是画画的小强 我们在运用AI绘画 Stable Diffusion 这一功能强大的AI绘图工具时&#xff0c;我们往往会发现自己对提示词的使用还不够充分。在这种情形下&#xff0c;我们应当如何调整自己的策略&#xff0c;以便更加精确、全面地塑造出理想的人物形象呢&a…

域环境提权

域内提权漏洞(1) Netlogon域权限提升 1.查看域控主机名称 net group "domain controllers" /domain 2.检测漏洞是否存在 https://github.com/SecuraBV/CVE-2020-1472.git python zerologon_tester.py OWA 192.168.52.138 3.漏洞利用&#xff0c;对域账号重置 ht…

《简历宝典》01 - 一文带你学会如何写一份糟糕透顶的简历

我们每个人几乎都会面对找工作这件事&#xff0c;而找工作或者说求职首先就是要写一份简历。今天狗哥将以一个不同的视角带你写一份无与伦比&#xff0c;糟糕透顶的求职简历&#xff0c;说实话&#xff0c;其实几年前&#xff0c;我就是这么写的。 目录 1. 文件名 2. 基本信…

【项目管理】项目风险管理(Word原件)

风险和机会管理就是在一个项目开发过程中对风险进行识别、跟踪、控制的手段。风险和机会管理提供了对可能出现的风险进行持续评估&#xff0c;确定重要的风险机会以及实施处理的策略的一种规范化的环境。包括识别、分析、制定处理和减缓行动、跟踪 。合理的风险和机会管理应尽力…

白骑士的Python教学进阶篇 2.4 高级数据结构

系列目录 上一篇&#xff1a;白骑士的Python教学进阶篇 2.3 文件操作​​​​​​​ 在Python中&#xff0c;掌握高级数据结构可以显著提升你的编程效率和代码可读性。高级数据结构包括列表推导式、生成器与迭代器以及装饰器。本文将详细介绍这些高级数据结构&#xff0c;帮助…