1. 为什么说Rockit是RK3568视频与Qt融合的“终极奥义”?
如果你正在RK3568平台上折腾视频播放,想在流畅解码的同时,还能在画面上叠加一个漂亮的Qt界面,比如做个带控制按钮的监控客户端、或者一个带数据叠加的播放器,那你肯定已经踩过不少坑了。我当初接手一个工业平板项目,需求就是实时播放多路摄像头画面,同时界面上要有各种图表、按钮和状态信息。一开始我也试遍了网上能找到的所有方案,什么OpenCV+GStreamer、FFmpeg+MPP转QImage,要么是CPU占用率飙升,要么是界面卡成PPT,要么就是视频和UI死活没法完美叠加。
折腾了一大圈,掉了一大把头发,最后发现,Rockit渲染框架才是那个能真正解决问题的“终极方案”。为什么这么说?因为它直接绕开了所有中间环节,让RK3568的硬件解码器(VDEC)和显示控制器(VO)直接对话,解码后的视频数据通过DMA-BUF内存,直接送到显示层的帧缓冲区,整个过程几乎不经过CPU。而Qt界面运行在另一个独立的显示层上,通过设置透明度,让两个层在屏幕上“叠加”显示。这就像是在玻璃上画画(Qt层),玻璃后面放着电影(视频层),两者互不干扰,又完美融合。
简单来说,Rockit方案的核心优势就三点:性能极致、延迟最低、叠加最稳。它不像其他方案需要把解码后的YUV或RGB数据从内核空间拷贝到用户空间,再交给Qt去绘制,避免了大量的内存拷贝和格式转换开销。实测下来,用Rockit方案播放4路1080P@30fps的H.265视频,CPU占用率能稳定在15%以下,而用软件渲染的方案,单路就可能吃掉30%以上的CPU。对于嵌入式设备来说,这省下来的每一分算力都无比珍贵。
2. 环境准备与Rockit库的“正确打开方式”
在开始写代码之前,环境搭建是第一个大坑。RK官方提供的SDK和文档,用过的都知道,版本管理有点混乱,不同版本的库和内核驱动可能完全不兼容。我踩的第一个坑就是用了旧的Rockit库,结果初始化一直失败,查了半天才发现是库版本和内核驱动对不上。
2.1 系统与内核版本确认
首先,确保你的系统是Linux 5.10或以上内核,并且已经包含了RK的MPP(Media Process Platform)和Rockit驱动。你可以通过以下命令检查:
uname -r
# 应该输出类似 5.10.66 的内核版本
# 检查Rockit相关内核模块是否加载
lsmod | grep -E "mpp|rga|vop"
# 应该能看到 mpp_service, mpp_vcodec, rga 等模块
如果你的内核版本太旧,或者没有这些模块,大概率需要更新你的SDK或自行编译内核。我强烈建议直接使用RK官方为RK3568提供的Buildroot或Debian镜像,它们通常已经集成了所需的内核驱动和用户态库。
2.2 Rockit SDK的获取与部署
Rockit SDK通常不包含在标准的RK SDK里,需要单独向原厂或代理商索取。拿到SDK后,重点关注以下几个库和头文件:
- 头文件目录:通常是
rockchip/下的rk_mpi.h,rk_comm_vdec.h,rk_comm_vo.h等。这些定义了所有关键的API和数据结构。 - 动态库:主要是
librkmedia.so,librkmpi.so。你需要把它们部署到目标板的/usr/lib/目录下,或者设置LD_LIBRARY_PATH环境变量。 - 编译工具链:确保你的交叉编译工具链与SDK匹配。RK官方通常推荐使用
gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu这个版本。
一个常见的部署错误是库的版本不匹配。我遇到过一种情况,编译链接通过了,但运行时提示“undefined symbol”,就是因为开发机上编译链接的库版本和目标板上的运行时库版本不一致。最稳妥的办法是,用目标板文件系统里的头文件和库,在开发机上搭建一个完全一致的sysroot来编译。
2.3 Qt环境的特殊配置
我们的目标是让Qt界面和视频层叠加。默认的Qt for Weston(Wayland)配置,其窗口合成器可能不支持真正的透明度穿透。关键一步是修改Weston的配置文件 /etc/xdg/weston/weston.ini。
你需要确保在 [core] 部分添加或修改以下关键配置:
[core]
# 允许与下层的DRM平面进行混合,这是实现透明叠加的基础
gbm-format=argb8888
# 关闭屏幕休眠
idle-time=0
# 关闭输入设备检测(对于无触摸屏的设备很有用)
require-input=false
这里的 gbm-format=argb8888 至关重要,它告诉Weston的DRM后端使用带Alpha通道的ARGB格式来创建缓冲区,这样Qt窗口的透明区域才能真正“透”下去,看到下面的视频层。没有这个配置,无论你怎么设置Qt窗口的透明度,视频层都会被完全遮盖。
3. Rockit核心流程拆解:从解码到显示的每一步
理解了环境,我们来看代码。Rockit的使用有一套固定的“流水线”:初始化系统 -> 创建解码通道 -> 创建显示层 -> 送流解码 -> 获取帧并送显。下面我们一步步拆解。
3.1 系统与视频输出(VO)初始化
任何Rockit操作之前,必须先初始化MPI系统。这相当于给整个多媒体硬件子系统上电。
// 初始化MPI系统,这是所有Rockit操作的第一步
RK_S32 ret = RK_MPI_SYS_Init();
if (ret != RK_SUCCESS) {
std::cerr << "RK_MPI_SYS_Init failed with error: 0x" << std::hex << ret << std::endl;
return false;
}
std::cout << "MPI系统初始化成功!" << std::endl;
接下来是初始化视频输出设备(VO Device)和图层(VO Layer)。你可以把VO Device理解为显示接口(比如HDMI、LVDS),把VO Layer理解为这个接口上的一个“画布”。RK3568通常支持多个Layer,我们需要为视频分配一个专用的Layer。
bool init_video_layer(VO_LAYER layer_id, int x, int y, int width, int height) {
RK_S32 ret;
VO_DEV dev_id = 0; // 通常使用设备0
VO_PUB_ATTR_S pub_attr;
// 1. 将图层绑定到显示设备
ret = RK_MPI_VO_BindLayer(layer_id, dev_id, RK356X_VOP_LAYER_MODE);
if (ret != RK_SUCCESS) {
std::cerr << "绑定图层失败!" << std::endl;
return false;
}
// 2. 设置显示设备的公共属性(分辨率、刷新率等)
memset(&pub_attr, 0, sizeof(VO_PUB_ATTR_S));
pub_attr.enIntfType = VO_INTF_LVDS; // 根据你的屏幕接口类型修改,如HDMI
pub_attr.enIntfSync = VO_OUTPUT_1920x1080_60; // 输出分辨率和刷新率
pub_attr.u32BgColor = 0x00000000; // 背景色设为黑色透明
ret = RK_MPI_VO_SetPubAttr(dev_id, &pub_attr);
if (ret != RK_SUCCESS) {
std::cerr << "设置显示设备属性失败!" << std::endl;
return false;
}
// 3. 启用显示设备
ret = RK_MPI_VO_Enable(dev_id);
if (ret != RK_SUCCESS) {
std::cerr << "启用显示设备失败!" << std::endl;
return false;
}
// 4. 设置图层的具体属性
VO_VIDEO_LAYER_ATTR_S layer_attr;
ret = RK_MPI_VO_GetLayerAttr(layer_id, &layer_attr); // 先获取默认属性
layer_attr.enPixFormat = RK_FMT_RGBA8888; // 关键!使用带Alpha通道的格式
layer_attr.stDispRect.s32X = x; // 图层在屏幕上的起始X坐标
layer_attr.stDispRect.s32Y = y; // 起始Y坐标
layer_attr.stDispRect.u32Width = width;
layer_attr.stDispRect.u32Height = height;
layer_attr.stImageSize.u32Width = width; // 图像缓冲区大小(通常与显示区域一致)
layer_attr.stImageSize.u32Height = height;


411

被折叠的 条评论
为什么被折叠?



