ffmpeg 从avio_write 到 udp_write

ffmpeg 从avio_write 到 udp_write
---------------------------------------------
author: hjjdebug
date: 2024年 03月 11日 星期一 14:16:44 CST
description: ffmpeg 从avio_write 到 udp_write
---------------------------------------------

文章目录:

1. main 调用avio_write
2. avio_write 调用flush_buffer
3. flush_buffer 调用的writeout函数
3.1, 挖掘一下为什么h->max_packet_size 是1472
3.2, 可见1472又由s->pkt_size决定,而s是h->priv_data,所以要看h是如何构建的!! 还要关心h->priv_data是如何构建的.
3.3, 协议的发现, 其中filename="udp://239.1.1.51:8001"
4. writeout 函数调用了ffurl_write 函数, 
5. ffurl_write 直接调用了retry_transfer_wrapper
6. udp_write 函数参数, 有一个内存handle, 数据指针和大小, 是实际传递数据的地方.
 

int nRet = avio_open(&pWriteCtx, "udp://239.1.1.51:8001), AVIO_FLAG_WRITE);
只分析一句话.
avio_write(pWriteCtx, buf, sizeof(buf));

在gdb中, 中断在udp_write 函数处, 打bt 命令显示调用栈如小.
#0  udp_write (h=0x5555555622c0, buf=0x5555555729c0 <incomplete sequence \370\200>, size=1472) at libavformat/udp.c:1204
#1  0x00007ffff7cf7bed in retry_transfer_wrapper (h=0x5555555622c0, buf=0x5555555729c0 <incomplete sequence \370\200>, size=1472, size_min=1472, transfer_func=0x7ffff7eb3f2b <udp_write>) at libavformat/avio.c:370
#2  0x00007ffff7cf7de7 in ffurl_write (h=0x5555555622c0, buf=0x5555555729c0 <incomplete sequence \370\200>, size=1472) at libavformat/avio.c:423
#3  0x00007ffff7cf8cd9 in writeout (s=0x555555573000, data=0x5555555729c0 <incomplete sequence \370\200>, len=1472) at libavformat/aviobuf.c:170
#4  0x00007ffff7cf8e2e in flush_buffer (s=0x555555573000) at libavformat/aviobuf.c:191
#5  0x00007ffff7cf909e in avio_write (s=0x555555573000, buf=0x7fffffffc610 <incomplete sequence \370\200>, size=5824) at libavformat/aviobuf.c:238
#6  0x0000555555555571 in main () at main.cpp:54

调用层次分析: 我们需要重点关心哪些内容?
1. main 调用avio_write, 要把buf地址开始,size=5824的数据发送出去, 同时还传了一个地址s=0x555555573000.
这个s 是什么呢? 是一个内存handle, 实际就是对象地址, 有什么用途,从这个地址可以找到很多有用的信息.

2. avio_write 调用flush_buffer, 只给了内存handle, 还是给它起个名吧,它叫AVIOContext. 要求把它的缓存刷新出去,
  要想把5824个数据都刷出去,也许需要刷新好几次缓存吧.

3. flush_buffer 调用的writeout函数, 
   writeout的参数仍然是AVIOContext, 包括data,len, 这个data应该是缓存的地址,长度1472是缓存的长度. 
   现在来确认.
   这个缓存指针和长度是在哪里赋值的? 应该在初始化时赋值的. 具体位置:

int ffio_fdopen(AVIOContext **s, URLContext *h)  //只需要认识URLContext 就可以了.
{
    uint8_t *buffer = NULL;
    int buffer_size, max_packet_size;

    max_packet_size = h->max_packet_size;  // h->max_packet_size 是1472
    if (max_packet_size) {
        buffer_size = max_packet_size;  // buffer_size 由 max_packet_size 决定, max_packet_size 由h->max_packet_size 决定
    } else {
        buffer_size = IO_BUFFER_SIZE;
    }
    if (!(h->flags & AVIO_FLAG_WRITE) && h->is_streamed) {
        if (buffer_size > INT_MAX/2)
            return AVERROR(EINVAL);
        buffer_size *= 2;
    }
    buffer = av_malloc(buffer_size);  // buffer 地址和大小由该语句确定.
    if (!buffer)
        return AVERROR(ENOMEM);

    *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h,
                            (int (*)(void *, uint8_t *, int))  ffurl_read,
                            (int (*)(void *, uint8_t *, int))  ffurl_write,
                            (int64_t (*)(void *, int64_t, int))ffurl_seek);

    (*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL;
    (*s)->max_packet_size = max_packet_size;
    (*s)->min_packet_size = h->min_packet_size;
    if(h->prot) {
        (*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause;
        (*s)->read_seek  =
            (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek;

        if (h->prot->url_read_seek)
            (*s)->seekable |= AVIO_SEEKABLE_TIME;
    }
    (*s)->short_seek_get = (int (*)(void *))ffurl_get_short_seek;
    (*s)->av_class = &ff_avio_class;
    return 0;
}

3.1, 挖掘一下为什么h->max_packet_size 是1472
0 in udp_open of libavformat/udp.c:830
1 in ffurl_connect of libavformat/avio.c:213
2 in ffurl_open_whitelist of libavformat/avio.c:347
3 in ffio_open_whitelist of libavformat/aviobuf.c:1152
4 in avio_open2 of libavformat/aviobuf.c:1166
5 in avio_open of libavformat/aviobuf.c:1139
6 in main of main.cpp:45

    UDPContext *s = h->priv_data; // 其中s 是UDPContext 
    if (s->pkt_size > 0) 
            h->max_packet_size = s->pkt_size; // 1472

3.2, 可见1472又由s->pkt_size决定,而s是h->priv_data,所以要看h是如何构建的!! 还要关心h->priv_data是如何构建的.
这里h指的是URLContext, h->priv_data是UDPContext
0 in url_alloc_for_protocol of libavformat/avio.c:120
1 in ffurl_alloc of libavformat/avio.c:303
2 in ffurl_open_whitelist of libavformat/avio.c:316
3 in ffio_open_whitelist of libavformat/aviobuf.c:1152
4 in avio_open2 of libavformat/aviobuf.c:1166
5 in avio_open of libavformat/aviobuf.c:1139
6 in main of main.cpp:44

其中协议(up, urlprotocal)的私有类赋值给私有数据变量,然后设置给类的默认数据
    *(const AVClass **)uc->priv_data = up->priv_data_class;
    av_opt_set_defaults(uc->priv_data); // 这个私有数据uc->priv_data就是UDPContext
其中up->priv_data_class 就是 udp_class, up 是UrlProtocol 指针

3.3, 协议的发现, 其中filename="udp://239.1.1.51:8001"
p = url_find_protocol(filename);
根据名称,找到的是下面这个协议
const URLProtocol ff_udp_protocol = {
    .name                = "udp",
    .url_open            = udp_open,
    .url_read            = udp_read,
    .url_write           = udp_write,
    .url_close           = udp_close,
    .url_get_file_handle = udp_get_file_handle,
    .priv_data_size      = sizeof(UDPContext),
    .priv_data_class     = &udp_class,            // 这个是私有数据类
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
};
下面是udp_class
static const AVClass udp_class = {  //UDPContext 第一个成员变量就是udp_class
    .class_name = "udp",
    .item_name  = av_default_item_name,
    .option     = options,    // 该options 描述了UDPContext 的默认的成员变量的值
    .version    = LIBAVUTIL_VERSION_INT,
};
其option 的默认选项是该文件udp.c 下定义的options 选项
其中有一项为pkt_size, 默认1472, 刨根刨到底了.
    { "pkt_size",       "Maximum UDP packet size",  OFFSET(pkt_size),  AV_OPT_TYPE_INT, { .i64 = 1472 },  -1, INT_MAX,
    .flags = D|E },


4. writeout 函数调用了ffurl_write 函数, 
    ffurl_write 函数的调用参数与writeout 的调用参数数据没有改变,但内存handle 变了,从s 变成了h.
    经查,h 是URLContext, h=s->opaque
    所以要关注一下s->opaque 是怎样赋值的. 请参考avio_alloc_context函数, 它保留了h,并用
    ffurl_read, ffurl_write, ffurl_seek 给s的函数指针赋值.有点多此一举吗?  非也,

   ffurl_write 函数在avio.c中, 并不在aviobuf.c中,不是一个文件, 当写aviobuf.c时,avio.c文件已经存在,所以可以调用
   但直接调用会显得耦合太紧, 所以通过函数指针调用的. 只需要在创建对象时,将地址付给函数指针即可.

5. ffurl_write 直接调用了retry_transfer_wrapper
   retry_transfer_wrapper 内存handle 没有变是URLContext, 数据没有变,但多了一个transfer_func, 
    transfer_func 的地址是h->prot->url_write, 实际指向是udp_write 地址

6. udp_write 函数参数, 有一个内存handle, 数据指针和大小, 是实际传递数据的地方.
   为什么一直传递这个URLContext 指针? 就是可以从它那里拿到UDPContext, 然后才实际发送数据.

    例如udp_write 的实现
        UDPContext *s=h->priv_data;  //要关注一下h->priv_data是怎样赋值的.
        ret = sendto (s->udp_fd, buf, size, 0,
                      (struct sockaddr *) &s->dest_addr,
                      s->dest_addr_len);


架构明显把整体搞复杂了, 它把一个整体强制划分为不同的层,层与层之间靠接口或架构来衔接
但对于调用者来说又是把事情搞简单了. 调用者只关心本层代码就可以了.

架构一般都采用对象,所以对象的初始化就会很关键,搞清数据的来源,函数指针的来源.
 

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

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

相关文章

第十八章 配置 Apache 以与 Web 网关配合使用 (UNIX® Linux macOS)

文章目录 第十八章 配置 Apache 以与 Web 网关配合使用 (UNIX Linux macOS)AssumptionsApache for UNIX、Linux、macOS 的安装位置&#xff08;推荐选项&#xff09; 第十八章 配置 Apache 以与 Web 网关配合使用 (UNIX Linux macOS) 本页介绍如何配置 Apache Web 服务器以与 …

C#多态例讲

在 C# 中&#xff0c;多态性&#xff08;polymorphism&#xff09;是指不同的类可以有相同名称但不同实现的方法。通过多态性&#xff0c;你可以在运行时根据对象的实际类型来调用相应的方法&#xff0c;从而实现更加灵活和可扩展的代码。比较类似子类方法重写&#xff0c;如py…

[密码学]Base64编码

一、相关指令 1. 查看工具版本号 base64 --version2. 对字符串加密 echo 字符串 | base64 echo "Hello base64" | base643. 对字符串解密 echo 字符串 |base64 -d echo "SGVsbG8gTGV0aWFuLVJTQQo" | base64 -d4. 对文件加密 base64 文件名 base64 tex…

搭建交换机模拟环境及SSH连接,华为NSP软件入门使用教程

搭建交换机模拟环境及SSH连接&#xff0c;华为NSP软件入门使用教程 如果你是通过搜索搜到了这篇文章&#xff0c;那么一定是工作或者学习中需要用交换机&#xff0c;但是又没物理机测试学习&#xff0c;所以需要搭建本地的虚拟环境学习。 这篇文章是我进行交换机命令入门学习写…

使用Flask快速搭建轻量级Web应用【第119篇—Flask】

使用Flask快速搭建轻量级Web应用 在Web开发领域&#xff0c;选择适合项目需求的框架至关重要。Flask&#xff0c;一个轻量级的Python Web框架&#xff0c;以其简洁、灵活和易扩展的特性而备受开发者青睐。本文将介绍如何使用Flask迅速搭建一个轻量级的Web应用&#xff0c;并通过…

js导出的excel文件无法打开/打开乱码,excel无法打开xxx.xlsx因为文件格式或文件扩展无效

excel无法打开xxx.xlsx因为文件格式或文件扩展无效 使用 a 标签导出这里就不细说了&#xff0c;直接说上述问题解决方案 在调用导出接口的时候加上两个参数 responseType: “blob” responseEncoding: “utf8” export function test(data) {return util({url: /test,method: …

04-微服务 面试题

1.Spring Cloud 常见的组件有哪些? Spring Cloud 5大组件有哪些? 基础的内容考察回答原则:简单的问题不能答错(一道面试题就能淘汰一个人)新手和老手都要注意面试参考回答: 面试官:Spring Cloud 5大组件有哪些? 候选人:早期我们一般认为的Spring Cloud五大组件是 …

【C++】反向迭代器仿函数模板进阶

反向迭代器&仿函数&模板进阶 一&#xff0c;反向迭代器1. 什么是反向迭代器2. 模拟实现3. 如何使用 二&#xff0c;仿函数1. 仿函数的概念2. 仿函数的用法 三&#xff0c;模板1. 非类型模板参数2. 模板的特化2.1 特化概念2.2 函数模板特化2.3 类模板特化2.3.1 全特化2.…

Flink 性能优化总结(内存配置篇)

内存配置优化 Flink 内存模型 内存模型详解 进程内存&#xff08;Total Process Memory&#xff09;&#xff1a;Flink 进程内存分为堆上内存和堆外内存&#xff0c;堆上内存和 堆外内存的主要区别在于它们的管理方式不同和使用方式不同&#xff0c;这些会影响到它们的性能和…

矩阵最大权值

题目描述 现有一个n∗m大小的矩阵&#xff0c;矩阵中的每个元素表示该位置的权值。现需要从矩阵左上角出发到达右下角&#xff0c;每次移动只能向上下左右移动一格&#xff08;不允许移动到曾经经过的位置&#xff09;。求最后到达右下角时路径上所有位置的权值之和的最大值。…

python实现--折半查找

python实现–顺序查找 python实现–折半查找 python实现–分块查找 python实现B/B树 折半查找&#xff08;Binary Search&#xff09;&#xff0c;也称为二分查找&#xff0c;是一种高效的查找算法&#xff0c;适用于有序数组或列表。它的基本思想是通过每次将查找范围缩小为原…

vue 渲染pdf并盖章之后生成新的pdf

目录 1.渲染pdf 1.页面准备渲染的容器,使用canvas渲染 2.把文件流渲染到canvas上面 3.下载pdf.js插件 4.使用 2.盖章 1.印章图片 2.自定义指令拖拽 3.生成新的pdf 1.下载生成pdf插件 2.使用 4.页面效果图 1.展示pdf 2.拖拽盖章 3.生成pdf文件 5.整体代码 …

蓝月亮,蓝禾,奇安信,三七互娱,顺丰,康冠科技,金证科技24春招内推

蓝月亮&#xff0c;蓝禾&#xff0c;奇安信&#xff0c;三七互娱&#xff0c;顺丰&#xff0c;康冠科技&#xff0c;金证科技24春招内推 ①蓝月亮 【招聘岗位】技术&#xff0c;研发&#xff0c;lT&#xff0c;供应链&#xff0c;市场&#xff0c;职能等 【网申链接】https://s…

机器学习是什么?

机器学习是一门多学科交叉专业&#xff0c;涵盖概率论知识、统计学知识、近似理论知识和复杂算法知识等。它是人工智能领域的关键技术&#xff0c;专门研究计算机如何模拟或实现人类的学习行为&#xff0c;以获取新的知识或技能&#xff0c;并重新组织已有的知识结构&#xff0…

中兴R5300G4无法识别全部硬盘与服务器Smart31002100RAID卡修改端口模式配置方法

中兴R5300G4无法识别全部硬盘&#xff0c;需要启动UEFI模式。 问题描述 硬盘配置RAID或者HBA直通模式需要修改RAID卡的端口模式。 本文介绍服务器分别在legacy、UEFI模式下的配置方法。 适用产品 R5300 G4、R5500 G4、R8500 G4 解决方案 一&#xff0e;Legacy启动模式&#x…

编程笔记 html5cssjs 009 HTML链接 我的网址簿

编程笔记 html5&css&js 009 HTML链接 我的网址簿 一、代码二、解释 这段代码的主要功能是展示一个包含多个分类和网址的网址簿&#xff0c;每个分类下有多个网址链接。通过a标签的href属性&#xff0c;用户可以点击链接跳转到相应的网址。同时&#xff0c;通过style标签…

《剑指 Offer》专项突破版 - 面试题 77 和 78 : 详解归并排序(C++ 实现)

目录 归并排序详解 递归实现 迭代实现 面试题 77 : 链表排序 面试题 78 : 合并排序链表 法一、利用最小堆选取值最小的节点 法二、按照归并排序的思路合并链表 归并排序详解 归并排序就是将两个或两个以上的有序表合并成一个有序表的过程。将两个有序表合并成一个有序表…

机器学习-04-分类算法-01决策树案例

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中分类算法&#xff0c;本篇为分类算法开篇与决策树部分。 本门课程的目标 完成一个特定行业的算法应用全过程&#xff1a; 懂业务会选择合适的算法数据处理算法训练算法调优算法融合 算法评估持续调优工程…

redis 缓冲区详解(性能优化缓冲区优化)

目录 前言 客户端输入缓冲区 输出缓冲区 集群缓冲区 全量复制缓冲区问题 增量复制缓冲区问题 前言 在我的《Redis 为啥那么快》这篇文章中&#xff0c;详细总结了Redis 为啥那么快。今天当我要详细阐述Redis 的缓冲区时&#xff0c;意识到应该加上Redis 的缓冲区。我们假…

初级爬虫实战——巴黎圣母院新闻

文章目录 发现宝藏一、 目标二、简单分析网页1. 寻找所有新闻2. 分析模块、版面和文章三、爬取新闻1. 爬取模块2. 爬取版面3. 爬取文章四、完整代码五、效果展示发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【宝藏入口】。 …