【FFmpeg】FFmpeg 内存结构 ⑥ ( 搭建开发环境 | AVPacket 创建与释放代码分析 | AVPacket 内存使用注意事项 )
文章目录
- 一、搭建开发环境
- 1、开发环境搭建参考
- 2、项目搭建
- 二、AVPacket 创建与释放代码分析
- 1、AVPacket 创建与释放代码
- 2、Qt 单步调试方法
- 3、单步调试 - 分析 AVPacket 创建与销毁代码
- 三、AVPacket 内存使用注意事项
- 1、谨慎使用 av_init_packet 函数
- 2、av_init_packet 函数弃用
- 3、av_init_packet 函数导致内存泄漏的反面示例
- 4、av_packet_move_ref 函数 后可以使用 av_init_packet 函数
- 5、av_packet_clone 函数 后不可以使用 av_init_packet 函数
- 6、av_packet_ref 函数 与 av_packet_unref 函数 成对使用
FFmpeg 4.0 版本源码地址 :
- GitHub : https://github.com/FFmpeg/FFmpeg/tree/release/4.0
- GitCode : https://gitcode.com/gh_mirrors/ff/FFmpeg/tree/release/4.0
- FFmpeg/libavcodec/avpacket.c 源码 : https://gitcode.com/gh_mirrors/ff/FFmpeg/blob/release/4.0/libavcodec/avpacket.c
一、搭建开发环境
开发前 , 先回顾下 开发环境 和 项目 搭建流程 ;
1、开发环境搭建参考
开发环境搭建参考如下博客 :
- 【FFmpeg】Windows 10 平台 FFmpeg 开发环境搭建 ① ( 安装 Visual Studio 2015 | JavaScript_ProjectSystem 安装包丢失或损坏 )
- 【FFmpeg】Windows 10 平台 FFmpeg 开发环境搭建 ② ( Qt 配置 MSVC2015 编译器 | 安装 VS2015 并配置 Qt 环境的 C/C++ 编译器 )
- 【FFmpeg】Windows 10 平台 FFmpeg 开发环境搭建 ③ ( CDB 调试器下载安装 | Qt 中配置 CDB 调试器 | Qt 中配置 32 位 / 64 位的构建套件 )
- 【FFmpeg】Windows 10 平台 FFmpeg 开发环境搭建 ④ ( FFmpeg 开发库 | 创建项目导入并配置 FFmpeg 开发库 | 拷贝 DLL 动态库到 SysWOW64 目录)
2、项目搭建
参考 【FFmpeg】Windows 10 平台 FFmpeg 开发环境搭建 ④ ( FFmpeg 开发库 | 创建项目导入并配置 FFmpeg 开发库 | 拷贝 DLL 动态库到 SysWOW64 目录) 博客后半部分内容 :
-
创建 Qt 项目 : 选择 " Non-Qt Project " 下的 " Plain C Application " 类型的项目 , 构建系统使用默认的 qmake , 构建套件选择 MSVC2015 套件 ;
-
拷贝头文件和库函数 : 将 ffmpeg-4.2.1-win32-dev 开发库目录 , 拷贝到 Qt 工程目录下 , 其中是 FFmpeg 的头文件和库函数 ;
-
配置 头文件和库函数 : 在 .pro 配置文件 , 配置 上面拷贝的 头文件 和 函数库 , 完整配置内容如下 :
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qtSOURCES += main.cwin32 {
INCLUDEPATH += $$PWD/ffmpeg-4.2.1-win32-dev/include
LIBS += $$PWD/ffmpeg-4.2.1-win32-dev/lib/avformat.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/avcodec.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/avdevice.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/avfilter.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/avutil.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/postproc.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/swresample.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/swscale.lib
}
- 代码引入头文件 : 引入 FFmpeg 库 , 并调用 av_version_info 函数 , 获取 FFmpeg 版本号 ;
#include <stdio.h> // 引入标准输入输出头文件
#include "libavutil/avutil.h" // 引入FFmpeg库中的avutil头文件,avutil包含一些工具函数int main()
{// 输出 "Hello World" 到控制台printf("Hello World\n");// 输出 FFmpeg 的版本信息,av_version_info() 函数返回当前FFmpeg库的版本信息printf("FFmpeg version is %s\n", av_version_info());// 返回0表示程序正常结束return 0;
}
- 代码运行库配置 : 将下面的文件拷贝到 C:\Windows\SysWOW64 目录 或者 项目的构建目录根目录 中 ;
二、AVPacket 创建与释放代码分析
1、AVPacket 创建与释放代码
下面的代码是 AVPacket 从声明 , 分配结构体内存 , 分配缓冲区数据内存 , 解除缓冲区数据引用 , 释放缓冲区内存 的完整过程 , 之后会单步调试 , 查看具体的数据信息 ;
void av_packet_1()
{AVPacket *pkt = NULL; // 定义一个指向 AVPacket 结构体的指针,用于存储数据包int ret = 0; // 定义一个整型变量 ret,用于存储函数的返回值// 分配一个新的 AVPacket,返回一个指向该数据包的指针pkt = av_packet_alloc();// 为数据包分配内存,并初始化引用计数为1ret = av_new_packet(pkt, MEM_ITEM_SIZE);// 使用 memccpy 函数将 av_packet_1 的数据拷贝到 pkt->data 中// 从 pkt->data 地址开始,复制 MEM_ITEM_SIZE 字节的数据// 复制的数据来源是当前函数 av_packet_1 的地址memccpy(pkt->data, (void *)&av_packet_1, 1, MEM_ITEM_SIZE);// 解除数据包的引用,减少引用计数av_packet_unref(pkt);// 释放数据包所占的内存,并将 pkt 指针设置为 NULLav_packet_free(&pkt);
}
2、Qt 单步调试方法
单步调试上述代码 :
-
在函数第一行代码位置打上断点 :
-
点击左下角的 " Start debugging of startup project " 按钮 ,
-
程序开始运行后 , 停留在 断点 位置 ;
-
切换到指令集操作模式 : 之前在代码模式中 , 无法进行 单步调试 , 参考 【错误记录】Qt 单步调试 F10 快捷键失灵 ( Start and Break on Main 和 单步调试 默认都是 F10 快捷键 | 单步调试技巧 - 转换汇编指令模式 ) 博客 ;
-
在 " 指令集操作模式 " 中 , 按 F10 单步运行 , 然后再切换回代码模式 , 即可进行单步调试 ;
3、单步调试 - 分析 AVPacket 创建与销毁代码
再次回到代码模式 , 即可进行单步调试 , 目前代码停留在函数的第三行代码处 ;
此时可以看到 , 声明的 AVPacket 结构体指针值为 0 , 暂时没有赋值 ;
继续下一步单步调试 , 执行 av_packet_alloc 函数 , 结果如下 , 该函数为 AVPacket 指针分配了内存 , AVPacket 结构体的成员都设置了默认值 , 其中 buf 缓冲区 被设置为了 0 , 也就是 NULL , 暂时没有进行初始化 ;
av_packet_alloc 函数 的 作用 是 : 在 堆内存中 , 为其分配内存空间 , 并为其成员设置默认值 ;
继续执行下一步 , 调用 av_new_packet 函数时 , 开始为 AVPacket 的数据分配内存 , 并将引用计数设置为 1 ;
此时 AVBuffer 结构体的 AVBufferRef *buf 成员 有了指向 , 不再是 NULL ;
真实的数据是 AVBufferRef 结构体的 AVBuffer *buffer 成员 中 , 其真实数据 和 引用计数 都在该结构体中 , 目前可以看到该结构体在堆内存中的地址是 0xec5240 ;
执行 memccpy 函数 , 设置数据缓冲区中的数据 , 执行后 , 发现数据发生了改变 ;
继续向后执行 , 执行 av_packet_unref 函数 , 解除 AVPacket 数据包引用 , 减少引用计数 ;
参考 【FFmpeg】FFmpeg 内存结构 ④ ( AVPacket 函数简介 | av_packet_unref 函数 | av_packet_move_ref 函数 ) 博客 中的 av_packet_unref 函数 分析章节 ;
最后 执行 av_packet_free 函数 , 释放 AVPacket 结构体 及其 内部的成员 所占用的内存空间 ;
执行完毕后 , 发现 AVPacket 结构体指针的值被置空 ;
三、AVPacket 内存使用注意事项
1、谨慎使用 av_init_packet 函数
av_init_packet 函数 内容如下 , 分析下面的代码 , 可知 该函数会将所有的字段都设置为 默认值 , 尤其是 pkt->buf 字段 , 这是数据缓冲区的引用 , 使用该函数不当 , 可以会导致数据缓冲区内存泄漏 ;
// 初始化 AVPacket 结构体
void av_init_packet(AVPacket *pkt)
{// 设置显示时间戳 (PTS) 为未定义值pkt->pts = AV_NOPTS_VALUE;// 设置解码时间戳 (DTS) 为未定义值pkt->dts = AV_NOPTS_VALUE;// 设置文件中的位置为 -1 (表示未知)pkt->pos = -1;// 设置帧的时长为 0pkt->duration = 0;#if FF_API_CONVERGENCE_DURATION// 以下代码处理弃用字段 convergence_duration 的初始化FF_DISABLE_DEPRECATION_WARNINGSpkt->convergence_duration = 0; // 设置帧的收敛时间为 0 (已废弃字段)FF_ENABLE_DEPRECATION_WARNINGS
#endif// 设置标志位为 0 (表示无标志)pkt->flags = 0;// 设置流索引为 0 (初始化为默认值)pkt->stream_index = 0;// 将引用的缓冲区指针设为 NULLpkt->buf = NULL;// 将侧数据的指针设为 NULLpkt->side_data = NULL;// 设置侧数据元素个数为 0pkt->side_data_elems = 0;
}
2、av_init_packet 函数弃用
av_init_packet 函数 已经弃用 , FFmpeg 官方不再建议调用该函数 , 可参考 【FFmpeg】FFmpeg 内存结构 ② ( AVPacket 函数简介 | av_packet_alloc 函数 | av_packet_free 函数 | av_new_packet 函数 ) 博客 ,
av_packet_alloc 函数 是 FFmpeg 官方推荐使用的 初始化 AVPacket 结构体的函数 , 并且建议与 av_packet_free 函数 配对使用 ;
av_packet_alloc 函数 中 调用了 av_init_packet 函数 , 已经包含了 av_init_packet 函数的功能 ;
3、av_init_packet 函数导致内存泄漏的反面示例
在下面的代码中 , 在数据释放之前的某个时间点 调用了 av_init_packet 函数 , 导致 AVPacket 的 buf 缓冲区提前置空 , 但是 buf 指向的数据缓冲区还没有释放 , 这段内存就出现了泄漏 , 再也无法进行释放 ;
void av_packet_1()
{AVPacket *pkt = NULL; // 定义一个指向 AVPacket 结构体的指针,用于存储数据包int ret = 0; // 定义一个整型变量 ret,用于存储函数的返回值// 分配一个新的 AVPacket,返回一个指向该数据包的指针pkt = av_packet_alloc();// 为数据包分配内存,并初始化引用计数为1ret = av_new_packet(pkt, MEM_ITEM_SIZE);// 使用 memccpy 函数将 av_packet_1 的数据拷贝到 pkt->data 中// 从 pkt->data 地址开始,复制 MEM_ITEM_SIZE 字节的数据// 复制的数据来源是当前函数 av_packet_1 的地址memccpy(pkt->data, (void *)&av_packet_1, 1, MEM_ITEM_SIZE);// 如果在数据释放之前的某个时间点 调用了 av_init_packet 函数// 就会导致 AVPacket 的 buf 缓冲区置空// 但是 buf 指向的数据缓冲区还没有释放 这段内存就出现了泄漏 再也无法进行释放// 此处调用 av_init_packet 函数 就会导致内存泄漏 ☆☆☆av_init_packet(pkt);// 解除数据包的引用,减少引用计数av_packet_unref(pkt);// 释放数据包所占的内存,并将 pkt 指针设置为 NULLav_packet_free(&pkt);
}
4、av_packet_move_ref 函数 后可以使用 av_init_packet 函数
av_packet_move_ref 函数 的 源码如下 :
void av_packet_move_ref(AVPacket *dst, AVPacket *src)
{*dst = *src;av_init_packet(src);src->data = NULL;src->size = 0;
}
参考 https://raw.gitcode.com/gh_mirrors/ff/FFmpeg/raw/release%2F4.0/libavcodec/avpacket.c 源码 ;
在 av_packet_move_ref 函数中 , 调用了 av_init_packet 函数 , 如果在之后继续调用一次 av_init_packet 函数 , 是不会对内存结构产生影响的 , 相当于调用了两次 av_init_packet 函数 ;
5、av_packet_clone 函数 后不可以使用 av_init_packet 函数
av_packet_clone 函数 源码如下 :
// 克隆一个 AVPacket
AVPacket *av_packet_clone(const AVPacket *src)
{// 分配一个新的 AVPacket 对象AVPacket *ret = av_packet_alloc();// 如果分配失败,直接返回 NULLif (!ret)return ret;// 复制源数据包 (src) 的内容到新分配的数据包 (ret)// 如果复制失败,释放新分配的数据包内存if (av_packet_ref(ret, src))av_packet_free(&ret);// 返回克隆的数据包指针 (如果成功,则指向新数据包;失败则返回 NULL)return ret;
}
av_packet_clone 函数 相当于 av_packet_alloc 函数 + av_packet_ref 函数 的 组合函数 ;
调用完 av_packet_clone 函数 后 , 引用计数会增加 1 , 如果原来引用计数是 1 , 则新的引用计数是 2 ;
如果在 av_packet_clone 函数 后 调用 av_init_packet 函数 , 会导致新的 AVPacket 的 buf 成员被置空 , 之后 调用 av_packet_free 函数 无法访问到 AVPacket 的 buf 成员 , 自然无法将引用计数自减 1 , 这样就导致了 内存泄漏 ;
6、av_packet_ref 函数 与 av_packet_unref 函数 成对使用
av_packet_ref 函数 和 av_packet_unref 函数 需要成对使用 , 它们的主要目的是对 AVPacket 对象的引用计数进行管理 , 从而避免内存泄漏或重复释放 ;
在使用 av_packet_ref 引用数据包时 , 引用计数会增加 ;
如果在某些地方不再需要这个引用 , 必须使用 av_packet_unref 释放它 ;
否则 , 会导致内存泄漏 ;
相关文章:

【FFmpeg】FFmpeg 内存结构 ⑥ ( 搭建开发环境 | AVPacket 创建与释放代码分析 | AVPacket 内存使用注意事项 )
文章目录 一、搭建开发环境1、开发环境搭建参考2、项目搭建 二、AVPacket 创建与释放代码分析1、AVPacket 创建与释放代码2、Qt 单步调试方法3、单步调试 - 分析 AVPacket 创建与销毁代码 三、AVPacket 内存使用注意事项1、谨慎使用 av_init_packet 函数2、av_init_packet 函数…...

【多模态文档智能】OCR-free感知多模态大模型技术链路及训练数据细节
目前的一些多模态大模型的工作倾向于使用MLLM进行推理任务,然而,纯OCR任务偏向于模型的感知能力,对于文档场景,由于文字密度较高,现有方法往往通过增加图像token的数量来提升性能。这种策略在增加新的语言时࿰…...

Mybatis动态sql执行过程
动态SQL的执行原理主要涉及到在运行时根据条件动态地生成SQL语句,然后将其发送给数据库执行。以下是动态SQL执行原理的详细解释: 一、接收参数 动态SQL首先会根据用户的输入或系统的条件接收参数。这些参数可以是查询条件、更新数据等,它们…...

leetcode 31 Next Permutation
题意 找到下一个permutation是什么,对于一个数组[1,2,3],下一个排列就是[1, 3, 2] 链接 https://leetcode.com/problems/next-permutation/ 思考 首先任何一个permutation满足一个性质,从某个位置往后一定是降序。…...

每日一练 | 华为 eSight 创建的缺省角色
01 真题题目 下列选项中,不属于华为 eSight 创建的缺省角色的是: A. Administrator B. Monitor C. Operator D. End-User 02 真题答案 D 03 答案解析 华为 eSight 是一款综合性的网络管理平台,提供了多种管理和监控功能。 为了确保不同用…...

PyTorch基本使用-自动微分模块
学习目的:掌握自动微分模块的使用 训练神经网络时,最常用的算法就是反向传播。在该算法中,参数(模型权重)会根据损失函数关于对应参数的梯度进行调整。为了计算这些梯度,PyTorch 内置了名为 torch.autogra…...

libevent-Reactor设计模式【1】
一、Libevent概述 1、简介 Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大&#…...

奇奇怪怪的错误-Tag和space不兼容
报错信息如下: TabError: inconsistent use of tabs and spaces in indentation make: *** [Makefile:24: train] Error 1不能按Tab,要老老实实按space 不过可以在编辑器里面改,把它们调整成一致的;...

29.攻防世界ics-06
ics-06 难度:1 方向:Web 题目描述: 云平台报表中心收集了设备管理基础服务的数据,但是数据被删除了,只有一处留下了入侵者的痕迹。 进入靶场 发现有一处能点动 多了个id1 我其实尝试改过id数,不过没什么变化…...

强化学习路径规划:基于SARSA算法的移动机器人路径规划,可以更改地图大小及起始点,可以自定义障碍物,MATLAB代码
一、SARSA算法概述 SARSA(State-Action-Reward-State-Action)是一种在线强化学习算法,用于解决决策问题,特别是在部分可观测的马尔可夫决策过程(POMDPs)中。SARSA算法的核心思想是通过与环境的交互来学习一…...

【MFC】如何读取rtf文件并进行展示
tf是微软的一个带格式的文件,比word简单,我们可以用写字板等程序打开编辑。下面以具体实例讲解如何在自己程序中展示rtf文件。 首先使用VS2022创建一个MFC的工程。 VIEW类需要选择richview类,用于展示,如下图: 运行效…...

Vulhub:Log4j[漏洞复现]
CVE-2017-5645(Log4j反序列化) 启动靶场环境 docker-compose up -d 靶机IPV4地址 ifconfig | grep eth0 -A 5 ┌──(root㉿kali)-[/home/kali/Desktop/temp] └─# ifconfig | grep eth0 -A 5 eth0: flags4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 in…...

面向预测性维护的TinyML技术栈全面综述
论文标题:A Holistic Review of the TinyML Stack for Predictive Maintenance(面向预测性维护的TinyML技术栈全面综述) 作者信息:Emil Njor, Mohammad Amin Hasanpour, Jan Madsen, Xenofon Fafoutis,均来自丹麦技术…...

沈阳理工大学《2024年811自动控制原理真题》 (完整版)
本文内容,全部选自自动化考研联盟的:《沈阳理工大学811自控考研资料》的真题篇。后续会持续更新更多学校,更多年份的真题,记得关注哦~ 目录 2024年真题 Part1:2024年完整版真题 2024年真题...

用前端html如何实现2024烟花效果
用HTML、CSS和JavaScript编写的网页,主要用于展示“2024新年快乐!”的文字形式烟花效果。下面是对代码主要部分的分析: HTML结构 包含三个<canvas>元素,用于绘制动画。引入百度统计的脚本。 CSS样式 设置body的背景为黑…...

Redis应用-在用户数据里的应用
1.社区电商的业务闭环 接下来介绍的社区电商是以Redis作为主体技术、以MySQL和RocketMQ作为辅助技术实现的。 (1)社区电商运作模式 社区电商的关键点在于社区,而电商则是辅助性质(次要地位,流量变现)。社区可以分成很多种社区,比如美食社区、美妆社区、影评社区、妈妈社区…...

C++ 中面向对象编程如实现数据隐藏
在C中,面向对象编程(OOP)通过封装(Encapsulation)来实现数据隐藏。封装是OOP的一个核心概念,它允许将对象的属性和行为(即数据和方法)组合在一起,并对外隐藏对象的内部实…...

JavaEE 【知识改变命运】04 多线程(3)
文章目录 多线程带来的风险-线程安全线程不安全的举例分析产出线程安全的原因:1.线程是抢占式的2. 多线程修改同一个变量(程序的要求)3. 原子性4. 内存可见性5. 指令重排序 总结线程安全问题产生的原因解决线程安全问题1. synchronized关键字…...

gz中生成模型
生成模型 通过服务调用生成 还记得parameter_bridge 吗? 我们在生成桥接的时候调用了这个cpp文件。 一个 parameter_bridge 实例用于消息传递(传感器数据)。之前的例子 另一个 parameter_bridge 实例用于服务桥接(动态生成模型…...

前端(Axios和Promis)
Promise 语法 <script>// 创建promise对象// 此函数需要再传入两个参数,都是函数类型let pnew Promise((resolve,reject)>{if(3>2){resolve({name:"李思蕾",age:23,地址:"河南省"});}else{reject("error");}});console.log(p);p.th…...

AI Agent:重塑业务流程自动化的未来力量(2/30)
《AI Agent:重塑业务流程自动化的未来力量》 摘要:整体思路是先介绍 AI Agent 的基本情况,再深入阐述其实现业务流程自动化的方法和在不同领域的应用,接着分析其价值和面临的挑战,最后得出结论,为读者全面…...

前端页面导出word
html-docx-js bug: vite使用html-docx.js会报错,点击下载上方文件替换即可 正文 npm install html-docx-js -S npm install file-saver -S<template><div id"managerReport">word内容......</div> </template><script>&l…...

【考前预习】1.计算机网络概述
往期推荐 子网掩码、网络地址、广播地址、子网划分及计算-CSDN博客 一文搞懂大数据流式计算引擎Flink【万字详解,史上最全】-CSDN博客 浅学React和JSX-CSDN博客 浅谈云原生--微服务、CICD、Serverless、服务网格_云原生 serverless-CSDN博客 浅谈维度建模、数据分析…...

ubuntu20.04复现 Leg-KILO
这里写目录标题 opencv版本问题下载3.2.0源代码进入解压后的目录创建构建目录运行 CMake 配置 配置时指定一个独立的安装目录,例如 /opt/opencv-3.2:出错: 使用多线程编译错误1: stdlib.h: 没有那个文件或目录错误2:er…...

Ensembl数据库下载参考基因组(常见模式植物)bioinfomatics 工具37
拟南芥参考基因组_拟南芥数据库-CSDN博客 1 Ensembl数据库网址 http://plants.ensembl.org/index.html #官网 如拟南芥等 那么问题来了,基因组fa文件和gff文件在哪里? 2 参考案例 拟南芥基因组fa在这里 注释gff文件在这里...

简单介绍web开发和HTML CSS_web网站开发流程
一、Web 开发:探索互联网世界的基石 1.1 什么是 Web 开发 Web 开发,简单来说,就是构建能够通过浏览器访问的网站的过程。Web 代表着全球广域网,也就是我们熟知的万维网(www),它连接着世界各地的…...

Docker 中使用 PHP 通过 Canal 同步 Mysql 数据到 ElasticSearch
一、Mysql 的安装和配置 1.使用 docker 安装 mysql,并且映射端口和 root 账号的密码 # 获取镜像 docker pull mysql:8.0.40-debian# 查看镜像是否下载成功 docker images# 运行msyql镜像 docker run -d -p 3388:3306 --name super-mysql -e MYSQL_ROOT_PASSWORD12…...

数据结构之五:排序
void*类型的实现:排序(void*类型)-CSDN博客 一、插入排序 1、直接插入排序 思想:把待排序的数据逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。 单趟&#x…...

科研绘图系列:R语言绘制热图和散点图以及箱线图(pheatmap, scatterplot boxplot)
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载图1图2图3系统信息参考介绍 R语言绘制热图和散点图以及箱线图(pheatmap, scatterplot & boxplot) 加载R包 library(magrittr) library(dplyr) library(ve…...

基于 webRTC Vue 的局域网 文件传输工具
文件传输工具,匿名加密,只需访问网页,即可连接到其他设备,基于 webRTC 和 Vue.js coturn TURN 服务器 docker pull coturn/coturn docker run -d --networkhost \-v $(pwd)/my.conf:/etc/coturn/turnserver.conf \coturn/coturn…...