视频解码
本章讲用 MPP 框架在 RK182X 上做 H.264 / H.265 / VP9 / AV1 硬件视频解码。
整体框图
码流(文件 / 网络流)
↓
MPP Decoder(VPU 硬解)
↓
YUV 帧(NV12 / NV16)
↓
RGA 格式转换 → NPU 推理 / 显示 / 编码
(全程 DMA-BUF 零拷贝)RK182X 的 VPU 支持 H.264 / H.265 / VP9 / AV1 硬件解码。上限取决于所挂 Host SoC 的 VPU;以 RK3588 为参考,硬件解码能力最高 4K@60fps(H.265/VP9/AV1)与 8K@30fps(H.264/H.265)。
1. 支持的解码格式
| 格式 | Profile | 最大分辨率 | 最大帧率 |
|---|---|---|---|
| H.265 / HEVC | Main / Main10 | 4096×2304 | 60 fps |
| H.264 / AVC | High Profile | 4096×2304 | 60 fps |
| VP9 | Profile 0 / 2 | 4096×2304 | 60 fps |
| AV1 | Main Profile | 4096×2304 | 60 fps |
| MJPEG | Baseline | 8192×8192 | 30 fps |
2. 环境验证
MPP 头文件目录:
ls /usr/include/rockchip/ | head -15输出:
mpp_buffer.h
mpp_compat.h
mpp_err.h
mpp_frame.h
mpp_log_def.h
mpp_log.h
mpp_meta.h
mpp_packet.h
mpp_rc_api.h
mpp_rc_defs.h
mpp_sys_cfg.h
mpp_sys_cfg_st.h
mpp_task.h
rk_hdr_meta_com.h
rk_mpi_cmd.hMPP 解码控制命令:
grep -E "MPP_DEC_SET_EXT_BUF_GROUP|MPP_DEC_SET_OUTPUT_FORMAT" /usr/include/rockchip/rk_mpi_cmd.h | head -5输出:
MPP_DEC_SET_EXT_BUF_GROUP, /* IMPORTANT: set external buffer group to mpp decoder */
MPP_DEC_SET_OUTPUT_FORMAT,MPP 缓冲区类型:
grep -E "MPP_BUFFER_TYPE_DRM" /usr/include/rockchip/mpp_buffer.h | head -5输出:
* MPP_BUFFER_TYPE_DMA_HEAP > MPP_BUFFER_TYPE_DRM > MPP_BUFFER_TYPE_ION
MPP_BUFFER_TYPE_DRM,
* DRM SECURE buffer : MPP_BUFFER_TYPE_DRM | MPP_BUFFER_FLAGS_SECUREbuffer group 函数定义:
grep "mpp_buffer_group_get_internal" /usr/include/rockchip/mpp_buffer.h | head -5输出:
#define mpp_buffer_group_get_internal(group, type, ...) \MPP 库文件:
find /usr/lib -name "librockchip_mpp*"输出:
/usr/lib/aarch64-linux-gnu/librockchip_mpp.so
/usr/lib/aarch64-linux-gnu/librockchip_mpp.so.0
/usr/lib/aarch64-linux-gnu/librockchip_mpp.so.1VPU 设备节点:
ls /dev/mpp_service输出:
/dev/mpp_service交叉编译工具:
which aarch64-linux-gnu-gcc输出:
/usr/bin/aarch64-linux-gnu-gcc3. 完整解码 C 代码示例
七步流程:mpp_create → mpp_init(CTX_DEC, coding) → fopen 打开码流 → 申请 buffer group → 循环(decode_put_packet + decode_get_frame)→ 处理帧(fd)→ 清理。
#include <stdio.h>
#include <stdlib.h>
#include <rockchip/mpp_buffer.h>
#include <rockchip/mpp_frame.h>
#include <rockchip/mpp_packet.h>
#include <rockchip/rk_mpi.h>
#include <rockchip/rk_mpi_cmd.h> /* MPP_DEC_SET_OUTPUT_FORMAT 等 */
int main(int argc, char **argv)
{
if (argc < 2) {
printf("Usage: %s <input.h264>\n", argv[0]);
return -1;
}
// 创建 MPP 解码器 + 取 MppApi 指针
MppCtx ctx = NULL;
MppApi *mpi = NULL;
MPP_RET ret = mpp_create(&ctx, &mpi);
if (ret != MPP_OK) {
printf("mpp_create failed: %d\n", ret);
return -1;
}
// 初始化为 H.264 解码器
// 注意:解码类型由 mpp_init 的第三个参数 MppCodingType 传入
MppCodingType type = MPP_VIDEO_CodingAVC;
ret = mpp_init(ctx, MPP_CTX_DEC, type);
if (ret != MPP_OK) {
printf("mpp_init failed: %d\n", ret);
return -1;
}
// 打开输入文件
FILE *fp = fopen(argv[1], "rb");
if (!fp) { perror("fopen"); return -1; }
// 创建外部 buffer group,让 VPU 把解码帧直接写到这组 buffer
MppBufferGroup frame_group = NULL;
ret = mpp_buffer_group_get_internal(&frame_group, MPP_BUFFER_TYPE_DRM);
if (ret != MPP_OK) {
printf("mpp_buffer_group_get failed: %d\n", ret);
fclose(fp);
return -1;
}
// 把外部 group 推给解码器,零拷贝路径
mpi->control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, frame_group);
// 解码循环
unsigned char *pkt_buf = malloc(1024 * 1024);
MppPacket packet = NULL;
MppFrame frame = NULL;
RK_S64 pts = 0;
while (!feof(fp)) {
size_t read_len = fread(pkt_buf, 1, 1024 * 1024, fp);
if (read_len <= 0) break;
mpp_packet_init(&packet, pkt_buf, read_len);
mpp_packet_set_pts(packet, pts++); // PTS 是时间戳,不是字节数
ret = mpi->decode_put_packet(ctx, packet);
if (ret != MPP_OK) { printf("put_packet failed\n"); break; }
for (;;) {
ret = mpi->decode_get_frame(ctx, &frame);
if (ret != MPP_OK || !frame) break;
RK_U32 width = mpp_frame_get_width(frame);
RK_U32 height = mpp_frame_get_height(frame);
MppBuffer buf = mpp_frame_get_buffer(frame);
int fd = mpp_buffer_get_fd(buf); // DMA-BUF fd,可直传 RGA/NPU
printf("Decoded: %dx%d, fd=%d\n", width, height, fd);
// TODO: 传递给 RGA/NPU 处理
mpp_frame_deinit(&frame);
frame = NULL;
}
mpp_packet_deinit(&packet);
packet = NULL;
}
// 清理
free(pkt_buf);
fclose(fp);
mpp_buffer_group_put(frame_group);
mpp_destroy(ctx);
printf("Decode complete.\n");
return 0;
}3.1 关键 API 要点
| API | 说明 |
|---|---|
<rockchip/rk_mpi.h> | 头文件实际位置(不是 "rk_mpi.h") |
mpp_init(ctx, MPP_CTX_DEC, MppCodingType) | 解码类型由第三个参数传入;不存在 MPP_SET_DEC_CODING_TYPE 这个 control 命令 |
mpp_buffer_group_get_internal(&g, type) | 5 个参数版 mpp_buffer_group_get 太复杂,推荐用宏 |
mpp_packet_set_pts(pkt, ts) | 第二参数是 RK_S64 时间戳(不是字节数) |
MPP_DEC_SET_EXT_BUF_GROUP | 通过 mpi->control 把外部 buffer group 推给解码器走零拷贝 |
decode_put_packet + decode_get_frame | 都是 MppApi 成员(mpi->),不是顶层函数 |
4. 编译解码程序
VPU 解码能力已全部封进 librockchip_mpp,链接时只需要 -lrockchip_mpp。
aarch64-linux-gnu-gcc decode_test.c \
-I$RK182X_SDK/mpp/include \
-L/usr/lib/aarch64-linux-gnu \
-lrockchip_mpp \
-o decode_test
adb push decode_test /usr/bin/
adb shell /usr/bin/decode_test /tmp/test.h2645. 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
mpp_create failed | 库路径 / 设备权限 | ls -l /dev/mpp_service |
mpp_init failed | coding type 不支持 | 确认 MPP_VIDEO_CodingAVC/HEVC/VP9/AV1 |
| 解码输出乱码 | buffer group 未设 | 调 MPP_DEC_SET_EXT_BUF_GROUP |
MPP_DEC_SET_OUTPUT_FORMAT 不生效 | mpp_init 之前调 | 顺序:mpp_init → control |
mpp_buffer_group_get_internal 宏展开错 | 头文件版本旧 | 升级 librockchip-mpp-dev |
decode_get_frame 一直空 | PTS 没递增 | mpp_packet_set_pts(packet, pts++) |
