会议视频传输、Demo实现及哥伦布编码说明
一、会议视频的传输
视频通话通常采用1对1通信模型,而会议场景则需要实现多对多通信。以下以WebSocket为例,说明3个用户进行多方会议视频时的传输设计逻辑:
当3个用户参与多方会议时,每个用户端会同时存在1个service端和2个client端。该设计的核心目的是:通过自身的service端,与另外两个用户的client端建立连接,从而实现3个用户端之间的视频数据双向互通,确保每一方都能接收其他所有方的视频数据。
二、Demo的实现方法
本Demo实现不考虑服务器作为中转下发IP的场景,核心设计思路为:单个设备需同时维护1个SocketService实例和1个存储SocketClient的List集合,通过接口解耦实现数据存储与下发,配合多解码器完成多视频通讯。具体实现步骤如下:
2.1 SocketService与SocketClient的设计
每个设备对应1个SocketService和1个SocketClient列表(List)。其中:
-
SocketService负责监听连接、接收数据,并通过接口与AC(载体)交互,完成IP存储和数据下发;
-
SocketClient列表用于管理当前设备已连接的其他用户端,实现与多方的通信。
2.2 连接建立与IP管理
在SocketService的open方法中,当连接成功后,需将对方client的IP地址进行保存。可通过定义专门的接口方法(如saveClientIp())来实现IP存储,AC(载体)通过实现该接口,即可获取并存储所有连接的client IP,成为IP管理和数据交互的核心载体。
2.3 数据接收与下发
在SocketService的onMessage方法中,接收来自其他client的消息后,通过接口的另一个方法(如下发data数据),将client的IP地址和对应的data数据一同下发至AC载体。AC载体获取到数据后,通过在本地创建多个解码器(codec),对不同client下发的数据进行分别解码,变相实现多视频同步通讯。
2.4 IP过滤与Client连接技巧
可通过本地写死3个目标IP,配合过滤逻辑,避免重复连接和自身连接,具体代码示例如下(修正语法错误,优化逻辑表述):
List<String> ipList; // 存储目标IP列表(可本地写死)
List<Client> clientList; // 存储已连接的Client实例
for (String ip : ipList) {
// 过滤自身IP,避免连接自己
if (ip.equals(getUrl())) { // getUrl()方法用于获取本地service对应的自身IP
continue;
}
// 过滤已连接的IP,避免重复连接
boolean isSame = false;
for (Client c : clientList) {
if (ip.equals(c.getIp())) {
isSame = true;
break; // 找到已连接的Client,跳出循环
}
}
if (isSame) {
continue;
}
// 对未连接的IP,创建Client并尝试连接
URI u = null;
try {
u = new URI(ip); // 修正语法错误:Uri改为URI
Client c = new Client(ip, prot); // prot为端口号,需提前定义
c.connect(); // 发起连接
if (c.isOpen()) { // 修正语法错误:isopen()改为isOpen()
clientList.add(c); // 连接成功后,加入Client列表
}
} catch (Exception e) {
// 补充异常处理逻辑(可选),如连接失败的重试、日志打印等
e.printStackTrace();
}
// 后续可补充Client断开连接后的处理逻辑(如从clientList中移除、重新连接等)
}
2.5 数据群发与解码实现
-
数据群发:在SocketLive的SendData()方法中,通过循环遍历Client列表,将数据群发给所有已连接的client。数据源为本地摄像头采集的数据,经YUV编码后下发。
-
多解码器实现:在AC页面的解码层,根据自身的surface,创建与client数量对应的解码器(有多少个client连接,就创建多少个解码器),并为每个解码器分配对应的surface,确保不同client的视频数据能分别解码、正常显示。
-
数据流转:其他client的数据通过SocketService的onMessage方法接收后,经接口下发至AC页面,AC页面调用对应解码器完成解码,最终实现多视频同步展示。
三、哥伦布编码
在H264编码中,除了通过固定分隔符区分不同帧类型外,官方文档还采用了哥伦布编码来优化数据传输效率。以下对哥伦布编码进行简单说明,重点理解其核心原理和应用场景。
3.1 编码分类基础
编码方式主要分为两类,核心区别在于数据占用字节长度是否固定:
-
定长编码:数据占用的字节长度固定,例如一个int类型数据固定占用4个字节,无论数据本身大小,均按固定长度存储。
-
变长编码:不同数据占用的字节长度不同,根据数据本身的大小动态分配存储长度,可有效节省内存空间。
注:哥伦布编码、哈夫曼编码均属于变长编码(修正原有错误),适用于数据量小、传输频繁的场景,H264中常用于宏块相关数据的编码,可大幅降低内存占用。
3.2 哥伦布编码核心原理(以数据4为例)
哥伦布编码的核心逻辑的是通过“补0分隔+数值偏移”的方式,实现数据的紧凑存储,具体步骤如下(以数据4为例):
-
获取原数据的二进制:数据4对应的二进制为100;
-
原数据加1:100 + 1 = 101(核心目的是区分边界数据0和1,避免编码冲突);
-
补0分隔:以二进制中的“1”为分隔符,统计分隔符后面的有效位数(此处101中,分隔符“1”后面有2位有效数据),在分隔符前面补对应数量的0(补2个0);
-
最终编码结果:00101(前面2个0 + 分隔符1 + 后面2位有效数据01)。
3.3 关键疑问解答
-
为什么要加1? 核心原因是区分边界数据0和1。若不加1,0和1的二进制分别为0、1,无法通过“补0分隔”的逻辑进行有效区分,加1后可避免编码冲突,确保每个数据都有唯一的编码结果。
-
哥伦布编码的作用是什么? 在H264中,视频数据多为小数据量、高频率传输(如宏块数据),若使用定长编码或传统分隔符(如串口分隔位、XML标签、MMKV的protobuf协议),会在原数据基础上增加额外的分隔符开销,占用更多内存。哥伦布编码无需额外添加分隔符,通过自身编码逻辑即可实现数据区分,能大幅节省内存,提升传输效率。
-
解码方式是什么? 解码与编码逻辑反向对应,步骤如下:① 先读取编码数据前面的所有0,统计0的个数(记为n);② 从第一个“1”开始,读取后面n位数据,组成完整的二进制数;③ 将该二进制数减1,即可得到原数据。 示例:编码数据001011,可拆分为00101(对应原数据4)和01(对应原数据0),解码后得到两个数据:4和0。