基于RK3588的四路RTSP硬解码全流程实战:从FFmpeg拉流到QT显示
拿到ArmSoM-W3开发板的第一时间,很多开发者都会想验证其强大的视频处理能力。四路1080P实时视频解码正是RK3588的典型应用场景之一,但实际开发中常会遇到FFmpeg交叉编译困难、MPP接口调用晦涩、RGA格式转换异常等问题。本文将用最简化的代码和清晰的流程,带你完整实现:
- FFmpeg拉取RTSP流并解封装
- MPP硬解码YUV数据
- RGA图像格式转换
- QT多窗口渲染
1. 开发环境准备
1.1 硬件清单
- ArmSoM-W3开发板(RK3588芯片)
- 支持H.264/H.265的4路RTSP源(如IPC摄像头)
- 5V/4A电源适配器
- 散热风扇(持续解码建议加装)
1.2 软件依赖
# 基础依赖库 sudo apt install build-essential cmake git # QT环境 sudo apt install qtbase5-dev qtmultimedia5-dev # FFmpeg基础库 sudo apt install libavcodec-dev libavformat-dev libswscale-dev注意:建议使用ArmSoM提供的Ubuntu 20.04镜像,已预装部分驱动
2. FFmpeg交叉编译与拉流实现
2.1 定制化编译FFmpeg
RK3588需要启用特定的硬件加速选项:
git clone https://git.ffmpeg.org/ffmpeg.git cd ffmpeg ./configure \ --enable-shared \ --enable-rkmpp \ --enable-libdrm \ --arch=aarch64 \ --target-os=linux make -j$(nproc) sudo make install2.2 RTSP拉流核心代码
AVFormatContext *pFormatCtx = avformat_alloc_context(); AVDictionary *options = nullptr; av_dict_set(&options, "rtsp_transport", "tcp", 0); // 强制TCP传输 av_dict_set(&options, "stimeout", "5000000", 0); // 5秒超时 const char *url = "rtsp://192.168.1.100/live/main"; if(avformat_open_input(&pFormatCtx, url, nullptr, &options) < 0) { qDebug() << "Failed to open RTSP stream"; return; } AVPacket *pkt = av_packet_alloc(); while(av_read_frame(pFormatCtx, pkt) >= 0) { if(pkt->stream_index == video_stream_idx) { emit newPacket(pkt); // 通过信号传递到解码线程 } av_packet_unref(pkt); }常见问题解决:
- 花屏问题:添加
av_dict_set(&options, "fflags", "nobuffer", 0) - 高延迟:尝试
av_dict_set(&options, "tune", "zerolatency", 0)
3. MPP硬解码实战
3.1 MPP初始化流程
MppCtx ctx = nullptr; MppApi *mpi = nullptr; mpp_create(&ctx, &mpi); mpp_init(ctx, MPP_CTX_DEC, MPP_VIDEO_CodingAVC); // H.264解码 mpp_set_prop(ctx, MPP_DEC_SET_EXT_BUF_GROUP, enable); // 启用外部缓冲 mpp_set_prop(ctx, MPP_DEC_SET_INFO_CHANGE_READY, ready_callback);3.2 解码循环实现
MppPacket packet = nullptr; MppFrame frame = nullptr; // 将FFmpeg的AVPacket转为MPP输入 mpp_packet_init(&packet, pkt->data, pkt->size); mpp_packet_set_pts(packet, pkt->pts); // 送入解码器 mpi->decode_put_packet(ctx, packet); // 获取解码结果 while(MPP_OK == mpi->decode_get_frame(ctx, &frame)) { if(frame) { processDecodedFrame(frame); // 处理解码后数据 mpp_frame_deinit(&frame); // 释放帧 } }关键参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
| MPP_VIDEO_CodingAVC | 枚举 | H.264解码类型 |
| MPP_VIDEO_CodingHEVC | 枚举 | H.265解码类型 |
| MPP_DEC_SET_PARSER_SPLIT_MODE | 属性 | 设置分片模式 |
4. RGA图像处理技巧
4.1 YUV420SP转RGB888
#include <rga/RgaApi.h> rga_info_t src, dst; memset(&src, 0, sizeof(rga_info_t)); memset(&dst, 0, sizeof(rga_info_t)); // 配置源图像(MPP输出) src.fd = -1; src.virAddr = yuv_data; src.mmuFlag = 1; src.rotation = 0; // 配置目标图像(QT输入) dst.fd = -1; dst.virAddr = rgb_buf; dst.mmuFlag = 1; // 执行转换 c_RkRgaBlit(&src, &dst, nullptr);4.2 常见转换模式
| 转换类型 | RGA格式标识 | 典型用途 |
|---|---|---|
| NV12转RGB | RK_FORMAT_YCbCr_420_SP → RK_FORMAT_RGB_888 | QT直接显示 |
| 缩放+格式转换 | SRC_1080P → DST_720P | 画质适配 |
| 旋转+镜像 | RGA_TRANSFORM_ROT_90 + FLIP_H | 特殊视角调整 |
5. QT多路显示方案
5.1 基于QLabel的简易实现
QPixmap pixmap = QPixmap::fromImage(QImage( rgb_data, width, height, QImage::Format_RGB888)); ui->label1->setPixmap(pixmap.scaled( ui->label1->size(), Qt::KeepAspectRatio));5.2 高性能OpenGL方案
class VideoWidget : public QOpenGLWidget { protected: void paintGL() override { glClear(GL_COLOR_BUFFER_BIT); glDrawPixels(width, height, GL_RGB, GL_UNSIGNED_BYTE, rgb_data); } };性能对比:
| 方案 | CPU占用 | 延迟 | 适用场景 |
|---|---|---|---|
| QLabel | 15%~20% | 50~100ms | 快速原型开发 |
| OpenGL | 5%~8% | <30ms | 生产环境 |
6. 项目优化建议
线程模型设计:
- 单拉流线程 + 四解码线程 + 一显示线程
- 使用QSharedPointer管理帧数据生命周期
零拷贝优化:
// MPP与RGA共享内存 mpp_buffer_group_get_external(group, &rga_buffer);动态码流适配:
if(av_packet->flags & AV_PKT_FLAG_KEY) { mpi->reset(ctx); // 关键帧重置解码器 }
实际测试中,四路1080P@30fps解码时RK3588的CPU占用约为40%,温度控制在65℃以下。建议通过cat /sys/kernel/debug/mpp/dec_status实时监控解码状态。