ffmpeg 从avio_write 到 udp_write

这篇具有很好参考价值的文章主要介绍了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);


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

架构一般都采用对象,所以对象的初始化就会很关键,搞清数据的来源,函数指针的来源.
 文章来源地址https://www.toymoban.com/news/detail-843592.html

到了这里,关于ffmpeg 从avio_write 到 udp_write的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

原文地址:https://blog.csdn.net/hejinjing_tom_com/article/details/136622787

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ffmpeg推流rtmp指定udp传输

    RTMP (Real-Time Messaging Protocol) 是一个用于音频、视频和数据的传输协议。RTMP 协议本身可以支持 TCP 或 UDP 作为其底层传输协议。 在 RTMP 中,TCP 是默认的传输协议,它的稳定性和可靠性比 UDP 更好。但是,TCP 的延迟较高,对于实时性要求较高的音视频应用来说可能不太适合。 相

    2024年02月15日
    浏览(8)
  • ffmpeg点对点音视频udp协议传输

    参考:https://zhuanlan.zhihu.com/p/636152437?utm_id=0 ffmpeg查看可用设备: 局域网内两台设备间 局域网内两台设备间

    2024年04月10日
    浏览(10)
  • ffmpeg SDL播放器--播放udp组播流

    2024年02月14日
    浏览(9)
  • 利用c++基于ffmpeg库接收rtsp视频数据,并利用udp将以RTP协议将数据发送,同时利用udp接收RTP数据包,并基于ffmpeg库播放视频。

    这是两个不同的任务,需要分别实现。 任务一:基于ffmpeg库接收rtsp视频数据,并利用udp将以RTP协议将数据发送。 以下是基于ffmpeg库接收rtsp视频数据,然后使用udp发送RTP协议数据的示例代码: 任务二:利用c++利用udp接收RTP数据包,并基于ffmpeg库播放视频。 以下是利用c++利用

    2024年02月12日
    浏览(8)
  • c++调用ffmpeg api录屏 并进行udp组播推流

    代码及工程见https://download.csdn.net/download/daqinzl/88155241 开发工具:visual studio 2019 播放,采用ffmpeg工具集里的ffplay.exe, 执行命令 ffplay udp://224.1.1.1:5001 主要代码如下: #include \\\"pch.h\\\" #include iostream using namespace std; #include stdio.h #define __STDC_CONSTANT_MACROS extern \\\"C\\\" { #include \\\"include/libavcodec/

    2024年02月14日
    浏览(9)
  • c++调用ffmpeg api将视频文件内容进行udp推流

    代码及工程见https://download.csdn.net/download/daqinzl/88156926 开发工具:visual studio 2019 播放,采用ffmpeg工具集里的ffplay.exe, 执行命令 ffplay udp://238.1.1.10:6016 主要代码如下: #include \\\"pch.h\\\" #include iostream using namespace std; #include stdio.h #define __STDC_CONSTANT_MACROS extern \\\"C\\\" { #include \\\"include/libavcodec/

    2024年02月14日
    浏览(8)
  • 使用FFmpeg将本地文件通过UDP推流的音视频

    推流是指将音视频数据通过网络传输到指定的目标端,而FFmpeg是一个功能强大的跨平台多媒体处理工具,可以用于音视频编解码、转码、处理等操作。本文将介绍如何使用FFmpeg将本地文件通过UDP协议进行推流,实现音视频数据的传输。 首先,需要确保已经安装了FFmpeg工具。如

    2024年03月19日
    浏览(17)
  • FFmpeg命令行进行UDP、RTP推流(H264、TS),使用ffplay\VLC接收验证

            使用行FFmpeg命令进行UDP、RTP推流(H264、TS),ffplay接收我们在开发网络程序时经常用到UDP或RTP来发送和接收流媒体,而开发程序完毕需要搭建一个环境测试,这时候可能你需要一个推流端或接收端。对于推流端,我们可以借助FFmpeg工具轻松完成该功能,只需要敲一

    2024年02月05日
    浏览(13)
  • python ffmpeg将mp4文件实时转码为ts,并指定pid等信息,输出到udp

    要将MP4文件实时转码为TS格式,并将PID等信息指定为UDP输出,可以使用 subprocess 模块和ffmpeg命令行工具来实现。以下是一个示例代码,用于实时转码并将输出发送到UDP服务器: 在上述代码中,我们首先定义了输入文件、UDP服务器地址和PID等信息。然后,我们使用 subprocess.Pop

    2024年01月22日
    浏览(13)
  • R语言【utils】——write.table(),write.csv(),write.csv2():将数据写入文件

    Package  utils  version 4.2.0 参数【x】 :要写入的对象,最好是矩阵或数据帧。如果不是,则尝试将其强制转换为数据帧。 参数【file】 :命名文件的字符串或打开用于写入的连接。“”表示向控制台输出。 参数【append】 :逻辑值。只有当 参数【file】 是一个字符串时才相关。

    2024年01月22日
    浏览(8)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包