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 autoplay playsinline></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. 推送至 RV1106scp webrtc_streamer [email protected]:/usr/bin/
  2. 运行./webrtc_streamer,客户端打开 HTML 页面即可观看。

编译 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 

四、问题排查

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

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

Read more

5步搞定!用Ollama玩转Llama-3.2-3B文本生成

5步搞定!用Ollama玩转Llama-3.2-3B文本生成 你是不是也试过在本地跑大模型,结果被复杂的环境配置、显存报错、依赖冲突搞得头大?或者下载完模型发现根本不会用,对着空白输入框发呆?别担心——这次我们不搞虚的,就用最轻量的方式,5个清晰步骤,从零开始把Llama-3.2-3B真正“用起来”。 这不是一篇讲原理的论文,也不是堆参数的说明书。它是一份写给真实使用者的操作手记:没有Docker命令恐惧症,不碰CUDA版本焦虑,不查GPU显存表,连笔记本都能跑得动。重点就一个:让你今天下午就能写出第一句由Llama-3.2-3B生成的、像人话一样的文字。 Llama-3.2-3B是Meta最新发布的轻量级指令微调模型,30亿参数,专为多语言对话优化。它不像动辄几十GB的大块头那样吃资源,却在文案生成、逻辑推理、多轮问答等任务上表现扎实。更重要的是——它和Ollama是天生一对。Ollama把模型封装成“开箱即用”的服务,而Llama-3.2-3B则把能力稳稳装进这个盒子。我们不需要知道Transformer里有多少层注意力头,只需要知道:点一下、输一句、等两秒、看到结果。 下

语音转写文本润色:Llama-Factory助力ASR结果后处理

Llama-Factory助力ASR文本后处理:让语音转写真正“可用” 在智能会议系统、庭审记录数字化、远程医疗问诊等场景中,自动语音识别(ASR)早已不再是“能不能听清”的问题,而是“转出来的文字能不能直接用”的挑战。即便现代ASR引擎的词错率已低于10%,其原始输出仍常表现为无标点、断句混乱、同音错别字频出的“口语流”,例如: “那个我们明天三点开会然后讨论项目进度请各部门负责人参加” 这样的文本显然无法直接归档或生成纪要。用户需要额外投入大量人力进行校对和润色——这不仅抵消了自动化带来的效率优势,还可能引入新的错误。 于是,一个关键环节浮出水面:ASR后处理。而近年来,大语言模型(LLM)正成为这一环节的核心驱动力。不过,通用大模型如通义千问、ChatGLM虽然语法能力强,却往往对领域术语不敏感,容易“过度发挥”。真正的解法,是基于真实转写数据微调一个专用的文本修正模型。 这时,Llama-Factory 出现了。它不是一个简单的训练脚本集合,而是一套完整的大模型定制流水线,把从数据准备到模型部署的复杂工程封装成可操作的工具链。更重要的是,它让没有深度学习背景的工程师也

基于DeepSeek-R1-Distill-Llama-8B的OpenSpec协议分析

基于DeepSeek-R1-Distill-Llama-8B的OpenSpec协议分析 1. 协议分析新范式:当专业模型遇见标准化需求 在智能系统开发中,协议分析从来不是一件轻松的事。无论是网络通信、设备交互还是跨平台数据交换,开发者常常需要面对冗长的协议文档、晦涩的技术术语和大量边界条件测试。传统方式依赖人工阅读规范、编写解析脚本、反复调试验证,整个过程耗时且容易出错。 最近接触DeepSeek-R1-Distill-Llama-8B时,我尝试让它处理一份典型的OpenSpec协议文档——不是简单地摘要内容,而是真正理解协议结构、识别关键字段、推导安全风险点,并生成可执行的测试用例。结果令人意外:它不仅准确提取了协议版本、消息格式、状态码定义等核心要素,还能结合上下文指出潜在的兼容性隐患,比如某个字段在v2.1版本中新增但未明确说明向后兼容策略。 这让我意识到,协议分析正在经历一次静默变革。过去我们把协议当作静态文本处理,现在有了具备深度推理能力的模型,协议可以被“活”起来——理解其逻辑脉络、预判实施难点、甚至模拟不同厂商的实现差异。DeepSeek-R1-Distill-

在魔乐社区使用llama-factory微调Qwen3.5-4B模型

在魔乐社区使用llama-factory微调Qwen3.5-4B模型

微调前期准备 下载qwen3.5-4B模型 # 首先保证已安装git-lfs(https://git-lfs.com)git lfs installgit clone https://modelers.cn/Qwen-AI/Qwen3.5-4B.git 下载Llama-factory git clone --depth1 https://gh.llkk.cc/https://github.com/hiyouga/LlamaFactory.git 微调环境搭建 我们依然是搭建一个miniconda #清除当前shell会话中的PYTHONPATH环境变量unset PYTHONPATH # 安装minicondawget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh bash Miniconda3-latest-Linux-aarch64.sh conda config --set