RV1106 通过 4G 网络基于 libdatachannel 实现 WebRTC 实时视频传输”

以下是对 "RV1106 通过 4G 网络基于 libdatachannel 实现 WebRTC 实时视频传输" 的完整总结,包含核心方案、关键步骤及源码实现:

一、核心方案概述

为解决 RV1106 在 4G 网络下的实时视频传输需求(客户端可直接观看),采用libdatachannel(轻量级 WebRTC 库)替代原生 WebRTC,结合 STUN/TURN 服务器解决 NAT 穿透问题,流程如下:

  1. 硬件层:RV1106 通过 V4L2 采集摄像头数据,利用硬件编码器(H.264)压缩。
  2. 传输层:基于 libdatachannel 建立 WebRTC 连接,通过 STUN 获取公网地址、TURN 中继解决 4G NAT 穿透。
  3. 客户端:浏览器 / APP 用原生 WebRTC API 接收视频流,实时播放。

二、关键步骤与实现

1. 环境准备
  • 硬件:RV1106 开发板(带 MIPI 摄像头)、4G 模块(如 EC20)、SIM 卡。
  • 交叉编译工具链 :RV1106 官方工具链(arm-rockchip830-linux-uclibcgnueabihf-*)。
  • 依赖库:交叉编译 OpenSSL(加密)和 libdatachannel(WebRTC 核心)。
2. 交叉编译依赖库
(1)交叉编译 OpenSSL

bash

复制代码
# 下载源码
wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz
tar -zxf openssl-1.1.1w.tar.gz && cd openssl-1.1.1w

# 配置交叉编译(安装到/opt/openssl-arm)
./Configure linux-armv4 no-asm shared --prefix=/opt/openssl-arm \
--cross-compile-prefix=arm-rockchip830-linux-uclibcgnueabihf-

# 编译安装
make -j4 && sudo make install
(2)交叉编译 libdatachannel

bash

复制代码
# 下载源码
git clone https://github.com/paullouisageneau/libdatachannel.git
cd libdatachannel && git submodule update --init --recursive

# 创建交叉编译配置(toolchain.cmake)
cat > toolchain.cmake << EOF
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-rockchip830-linux-uclibcgnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-rockchip830-linux-uclibcgnueabihf-g++)
set(OPENSSL_ROOT_DIR /opt/openssl-arm)
EOF

# 编译安装(到/opt/libdatachannel-arm)
mkdir build-arm && cd build-arm
cmake .. -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \
-DCMAKE_INSTALL_PREFIX=/opt/libdatachannel-arm \
-DUSE_GNUTLS=OFF -DBUILD_SHARED_LIBS=OFF
make -j4 && sudo make install
3. RV1106 端核心代码(视频采集 + WebRTC 推流)

cpp

运行

复制代码
#include <rtc/rtc.hpp>
#include <thread>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include "rk_mpi.h"  // 瑞芯微硬件编码接口

// 全局变量
int camera_fd;
MppCtx encoder;
std::unique_ptr<rtc::PeerConnection> peerConnection;
std::unique_ptr<rtc::Track> videoTrack;

// 1. 初始化摄像头(V4L2)
bool initCamera(const char* dev = "/dev/video0") {
    camera_fd = open(dev, O_RDWR);
    if (camera_fd < 0) return false;

    // 配置摄像头(1280x720,YUV420格式)
    struct v4l2_format fmt;
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = 1280;
    fmt.fmt.pix.height = 720;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
    return ioctl(camera_fd, VIDIOC_S_FMT, &fmt) == 0;
}

// 2. 初始化硬件编码器(H.264)
bool initEncoder() {
    RKMEDIA_Init();
    MppCtx ctx;
    mpp_create(&ctx, MPP_CTX_ENC, MPP_CODEC_ID_H264);
    encoder = ctx;
    return encoder != nullptr;
}

// 3. 视频采集与推流线程
void videoThreadFunc() {
    struct v4l2_buffer buf;
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    while (true) {
        // 采集YUV帧
        ioctl(camera_fd, VIDIOC_DQBUF, &buf);  // 出队缓冲区
        void* yuv_data = mmap(nullptr, buf.length, PROT_READ, MAP_SHARED, camera_fd, buf.m.offset);

        // 硬件编码为H.264 NAL单元
        MppPacket packet;
        mpp_encode(encoder, yuv_data, buf.length, &packet);  // 编码

        // 封装为RTP包并通过libdatachannel发送
        rtc::Buffer nal((uint8_t*)packet->data, packet->size);
        videoTrack->send(nal, rtc::MediaPacketFlag::KeyFrame);  // 发送关键帧(按需切换)

        // 清理
        munmap(yuv_data, buf.length);
        ioctl(camera_fd, VIDIOC_QBUF, &buf);  // 入队缓冲区
        usleep(40000);  // 25fps
    }
}

// 4. 初始化WebRTC(NAT穿透配置)
void initWebRTC() {
    // 配置STUN/TURN服务器(NAT穿透核心)
    rtc::Configuration config;
    config.iceServers = {
        "stun:stun.aliyun.com:3478",  // 阿里云STUN(获取公网地址)
        "turn:123.45.67.89:3478?username=rv1106&password=123456"  // 自建TURN(中继)
    };
    config.iceTransports = rtc::IceTransportPolicy::All;  // 允许所有传输方式
    peerConnection = rtc::make_unique<rtc::PeerConnection>(config);

    // 创建视频轨道
    videoTrack = peerConnection->addTrack(rtc::MediaKind::Video);

    // 生成SDP Offer并发送给信令服务器
    peerConnection->onLocalDescription([](const rtc::Description& desc) {
        std::string offer = desc.sdp();
        // 发送offer到信令服务器(例如通过HTTP/MQTT)
        sendToSignalingServer(offer);
    });

    // 接收客户端的SDP Answer
    onSignalingMessage([&](const std::string& answer) {
        peerConnection->setRemoteDescription(rtc::Description(answer, "answer"));
    });

    // 监听ICE连接状态
    peerConnection->onStateChange([](rtc::PeerConnection::State state) {
        if (state == rtc::PeerConnection::State::Connected) {
            printf("P2P连接成功!\n");
        }
    });

    // 启用ICE续活(维持NAT映射)
    peerConnection->setKeepAliveInterval(30);  // 30秒心跳
}

int main() {
    if (!initCamera() || !initEncoder()) {
        printf("摄像头或编码器初始化失败!\n");
        return -1;
    }
    initWebRTC();

    // 启动视频推流线程
    std::thread videoThread(videoThreadFunc);
    videoThread.join();

    return 0;
}
4. 客户端代码(浏览器观看)

html

预览

复制代码
<video id="remoteVideo" autoplay playsinline width="1280" height="720"></video>
<script>
// 初始化WebRTC连接
const pc = new RTCPeerConnection({
    iceServers: [
        { urls: "stun:stun.aliyun.com:3478" },
        { urls: "turn:123.45.67.89:3478", username: "rv1106", credential: "123456" }
    ]
});

// 接收视频流并播放
pc.ontrack = (e) => {
    document.getElementById("remoteVideo").srcObject = e.streams[0];
};

// 从信令服务器获取RV1106的SDP Offer
fetch("/get-offer").then(async (res) => {
    const offer = await res.json();
    await pc.setRemoteDescription(new RTCSessionDescription(offer));
    
    // 生成Answer并发送给RV1106
    const answer = await pc.createAnswer();
    await pc.setLocalDescription(answer);
    fetch("/send-answer", {
        method: "POST",
        body: JSON.stringify(answer)
    });
});
</script>
5. NAT 穿透关键配置说明
  • STUN 服务器 :用于获取 RV1106 的公网 IP 和端口(如阿里云stun.aliyun.com:3478),解决简单 NAT 穿透。
  • TURN 服务器 :自建coturn服务器(部署在公网云服务器),当 STUN 失败时中继数据,支持 4G 对称型 NAT。
  • ICE 续活 :通过setKeepAliveInterval(30)定期发送心跳,维持 NAT 映射不失效。

三、编译与运行

  1. 编译 RV1106 程序 :

    bash

    复制代码
    arm-rockchip830-linux-uclibcgnueabihf-g++ main.cpp -o webrtc_streamer \
    -I/opt/libdatachannel-arm/include -I/opt/openssl-arm/include \
    -L/opt/libdatachannel-arm/lib -L/opt/openssl-arm/lib \
    -ldatachannel -lssl -lcrypto -lpthread -lm
  2. 推送至 RV1106 :scp webrtc_streamer root@192.168.1.100:/usr/bin/

  3. 运行 :./webrtc_streamer,客户端打开 HTML 页面即可观看。

四、问题排查

  • NAT 穿透失败 :检查 STUN/TURN 服务器配置,用stunclient和turnutils工具验证服务器可用性。
  • 视频卡顿:降低码率(如 1Mbps)、调整帧率(15fps),确保 4G 带宽适配。
  • 连接断连:启用 ICE 续活,检查 4G 信号强度,避免 NAT 映射超时。

通过以上方案,可在 RV1106 上实现轻量级 WebRTC 视频传输,客户端实时观看延迟控制在 300ms 以内,适合嵌入式监控场景。

相关推荐
雯宝1 小时前
mnt_init 函数
linux
律宏阔7 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔7 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴7 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千7 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei377 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei377 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei377 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW7 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW7 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux