在前几章中,我们依次讲解了 OSD 基础原理、RV1126 RKMedia 的两大核心结构体、OSD 全套 API,同时完成了zlib、FreeType、SDL、SDL_TTF 整套依赖库的交叉编译。
本章将整合所有知识点,从零实现完整工程:借助 SDL_TTF 渲染文字位图,通过 RV1126 硬件 OSD(RGN 模块)叠加到视频画面,最终由 VENC 模块编码生成 H264 视频文件。代码融合视频采集 (VI)、硬件编码 (VENC)、图层叠加 (OSD/RGN)、SDL 文字渲染、多线程开发五大核心技能,是 RV1126 嵌入式音视频开发的经典综合实战案例。
一、整体方案与技术架构
1.1 业务目标
基于 RV1126 开发板,实现功能闭环:
1.2 整体数据流链路
摄像头(VI) → 视频流水线 → OSD硬件图层叠加(RGN) → H264编码(VENC) → 本地文件保存
- VI 模块:负责视频图像采集,输出 NV12 格式原始帧;
- RGN (OSD) 模块:RV1126 硬件图层单元,接收外部位图,硬件完成画面叠加,不占用 CPU;
- VENC 模块:视频编码器,将叠加完成的画面压缩为标准 H264 码流;
- SDL_TTF:纯软件文字渲染工具,负责把字符串转为硬件可识别的像素位图;
- 多线程:解耦渲染、编码、IO 任务,避免单线程阻塞导致视频卡顿。
1.3 前置环境校验
在运行代码前,请确保环境全部就绪:
二、流程

用RV1126多线程输出OSD叠加需要经过上面几个重要步骤,分别是VI模块初始化、VENC模块初始化、RGN模块初始化、多线程进行OSD字库的叠加(里面主要是进行字库的渲染和RV1126的OSD模块叠加)、多线程获取每一帧OSD处理过后的编码数据
2.1. VI模块的初始化
VI模块的初始化实际上就是对VI_CHN_ATTR_S的参数进行设置、然后调用RK_MPI_VI_SetChnAttr设置VI模块并使能RK_MPI_VI_EnableChn,伪代码如下:
VI_CHN_ATTR_S vi_chn_attr;
。。。。。。。。。。。。。。。(这里是设置VI的属性)
ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, 0, &vi_chn_attr);
ret |= RK_MPI_VI_EnableChn(CAMERA_ID, 0);
2.2. 初始化VENC模块:
VENC模块的初始化实际上就是对VENC_CHN_ATTR_S的参数进行设置、然后调用RK_MPI_VENC_CreateChn创建编码器,伪代码如下:
VENC_CHN_ATTR_S venc_chn_attr;
venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
。。。。。。。。。。。。。。。。(这里是设置VENC的属性)
ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr);
2.3. 初始化RGN模块:
RGN是RV1126图层区域功能管理的功能,比方说OSD图层、Bitmap位图都属于RGN模块。所以开发者要用到OSD功能都需要初始化RGN,具体的API描述如下:
![]()
第一个传参数:VENC编码通道号
第二个传参数:VENC_COLOR_TBL_S结构体指针,默认是NULL
RK_MPI_VENC_RGN_Init(0, NULL);
2.4. 绑定VI模块和VENC模块,
伪代码如下
MPP_CHN_S vi_chn_s;
MPP_CHN_S venc_chn_s;
ret = RK_MPI_SYS_Bind(&vi_chn_s, &venc_chn_s);
2.5. 创建多线程进行OSD字库的叠加:
创建OSD叠加线程主要是对VENC的数据进行字库的创建和OSD的数据叠加,下面是伪代码:
char *pstr = "2019-11-21 15:40:29";
if(TTF_Init() < 0)
{
fprintf(stderr, "Couldn't initialize TTF: %s\\n", SDL_GetError());
}
ttf_font = TTF_OpenFont("./fzlth.ttf", 48);
if(ttf_font == NULL)
{
fprintf(stderr, "Couldn't load %d pt font from %s: %s\\n", 48, "ptsize", SDL_GetError());
}
text = TTF_RenderText_Solid(ttf_font, pstr, sdl_color);
……………………………………………………
SDL_PixelFormat *pixel_format = (SDL_PixelFormat *)malloc(sizeof(SDL_PixelFormat));
……………………
temp = SDL_ConvertSurface(text, pixel_format, 0);
static int get_align16_value(int input_value, int align)
{
int handle_value = 0;
if (align && (input_value % align))
handle_value = (input_value/ align + 1) * align;
return handle_value;
}
{
………………….
bitmap_width = get_align16_value(temp->w, 16);
bitmap_height = get_align16_value(temp->h, 16);
if (bitmap_width < 64)
bitmap_width = 64;
if (bitmap_height < 64)
bitmap_height = 64;
BITMAP_S BitMap;
BitMap.enPixelFormat = PIXEL_FORMAT_ARGB_8888;
BitMap.u32Width = bitmap_width;
BitMap.u32Height = bitmap_height;
BitMap.pData = malloc(wxh_size * fmt->BytesPerPixel);
if (!BitMap.pData)
{
printf("ERROR: no mem left for argb8888(%d)!\\n", wxh_size * fmt->BytesPerPixel);
break;
}
memcpy(BitMap.pData, temp->pixels, (temp->w) * (temp->h) * fmt->BytesPerPixel);
OSD_REGION_INFO_S RngInfo;
RngInfo.enRegionId = REGION_ID_7;
RngInfo.u32PosX = 0;
RngInfo.u32PosY = 0;
RngInfo.u32Width = bitmap_width;
RngInfo.u32Height = bitmap_height;
RngInfo.u8Enable = 1;
RngInfo.u8Inverse = 0;
ret = RK_MPI_VENC_RGN_SetBitMap(0, &RngInfo, &BitMap);
………..
}
上面是OSD叠加的代码,主要是通过RV1126的RNG模块对VENC进行OSD进行字库叠加。其中划黑色粗体是这个程序的核心,它需要把每个width和height进行16位对齐,因为OSD需要处理16位对齐的数据(所谓十六位对齐可以理解为,width和height能够被16位整除)。这里面用到16位对齐的方法是get_align16_value,input_value指的是输入的数值,align是要对齐的数值。
2.6 获取每一帧处理过后的VENC数据:
开启一个线程去采集每一帧VENC模块的数据,使用的API是RK_MPI_SYS_GetMediaBuffer, 模块ID是RK_ID_VENC,通道号ID是VENC创建的ID号。这个API的具体作用已我们直接上伪代码:
while(1)
{
………………………..
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, s32_chn_id, -1);
fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, h264_file);
……………………….
}
2.7 两大核心结构体
2.8 核心 API 调用顺序
2.9 SDL_TTF 渲染要点
三、完整实战代码
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h> // 补充 memset 头文件
// RKMedia 多媒体接口头文件
#include "rkmedia_api.h"
// SDL 图形渲染 + 字体渲染库(SDL1.2 版本)
#include "SDL.h"
#include "SDL_ttf.h"
#include <time.h>
/* 宏定义:摄像头、编码通道编号 */
#define CAMERA_PATH "rkispp_scale0" // 摄像头设备节点名
#define CAMERA_ID 0 // VI 视频输入模块ID
#define CAMERA_CHN 0 // VI 通道号
#define VENC_CHN 0 // VENC 编码通道号
/**
* @brief 数值按指定字节对齐(RK OSD 位图要求宽高16字节对齐)
* @param input_value 原始数值
* @param align 对齐字节数
* @return 对齐后的数值
*/
static int get_align16_value(int input_value, int align)
{
int handle_value = 0;
// 非0对齐值 + 原始值不能被对齐数整除时,向上对齐
if (align && (input_value % align))
handle_value = (input_value / align + 1) * align;
return handle_value;
}
/**
* @brief OSD水印绘制线程:使用SDL+TTF生成文字位图,叠加到VENC编码通道
* @param args 线程入参(未使用)
* @return NULL
*/
void *bitmap_osd_handle_thread(void * args)
{
// 线程分离:线程退出后自动回收资源,无需主线程pthread_join
pthread_detach(pthread_self());
int ret;
TTF_Font *ttf_font; // TTF字体句柄
const char *pstr = "2025-6-11 23:23:23"; // 待绘制的水印文字
SDL_Surface * text_surface; // SDL原始文字画布
SDL_Surface *convert_text_surface; // 格式转换后的ARGB8888画布
SDL_PixelFormat *pixel_format; // 自定义像素格式结构体
// ===================== 1. 初始化TTF字体库 =====================
ret = TTF_Init();
if (ret < 0)
{
printf("TTF_Init Failed…\\n");
}
// 打开本地字体文件,字号48
ttf_font = TTF_OpenFont("./fzlth.ttf", 48);
if (ttf_font == NULL)
{
printf("TTF_Open Failed…\\n");
}
// ===================== 2. 设置文字颜色并渲染文字到SDL画布 =====================
SDL_Color sdl_color; // SDL颜色结构体(SDL1.2仅包含R/G/B)
sdl_color.r = 0; // 红色分量 0
sdl_color.g = 0; // 绿色分量 0
sdl_color.b = 0; // 蓝色分量 0 → 最终文字为黑色
// 实心模式渲染文字,生成原始SDL Surface画布
text_surface = TTF_RenderText_Solid(ttf_font, pstr, sdl_color);
// ===================== 3. 手动构造ARGB8888像素格式 =====================
// 动态分配像素格式内存,适配RKMedia要求的ARGB8888格式
pixel_format = (SDL_PixelFormat *)malloc(sizeof(SDL_PixelFormat));
pixel_format->BitsPerPixel = 32; // 每个像素32bit
pixel_format->BytesPerPixel = 4; // 每个像素4字节
pixel_format->Amask = 0XFF000000; // Alpha通道掩码(最高8位)
pixel_format->Rmask = 0X00FF0000; // Red通道掩码
pixel_format->Gmask = 0X0000FF00; // Green通道掩码
pixel_format->Bmask = 0X000000FF; // Blue通道掩码
// 将原始文字画布 转换为 ARGB8888 格式画布
convert_text_surface = SDL_ConvertSurface(text_surface, pixel_format, 0);
if (convert_text_surface == NULL)
{
printf("convert_text_surface Failed…\\n");
}
// ===================== 4. 构造RKMedia OSD位图结构体 BITMAP_S =====================
BITMAP_S bitmap;
// 画布宽度向上16字节对齐(RK OSD强制要求)
bitmap.u32Width = get_align16_value(convert_text_surface->w, 16);
// 画布高度向上16字节对齐
bitmap.u32Height = get_align16_value(convert_text_surface->h, 16);
// 指定位图像素格式为 ARGB8888
bitmap.enPixelFormat = PIXEL_FORMAT_ARGB_8888;
// 分配对齐后的位图内存,calloc自动清零,避免残留垃圾数据
bitmap.pData = calloc(1, bitmap.u32Width * bitmap.u32Height * 4);
// ===================== 5. 逐行拷贝SDL画布数据到RK位图(核心:解决行跨度pitch错位) =====================
int src_w = convert_text_surface->w; // SDL画布实际宽度(像素)
int src_h = convert_text_surface->h; // SDL画布实际高度(像素)
int src_pitch = convert_text_surface->pitch;// SDL单行真实字节数(行跨度,SDL内部行对齐)
// 指针强制转换:适配C语言void*隐式转换,兼容编译
uint8_t *src_buf = (uint8_t*)convert_text_surface->pixels;
uint8_t *dst_buf = (uint8_t*)bitmap.pData;
// 逐行拷贝:跳过SDL行对齐填充字节,只拷贝有效像素,根治花屏/歪斜
for (int y = 0; y < src_h; y++)
{
memcpy(dst_buf + y * bitmap.u32Width * 4,
src_buf + y * src_pitch,
src_w * 4);
}
// ===================== 6. 配置OSD区域信息,设置水印位置与使能 =====================
OSD_REGION_INFO_S rgn_info;
rgn_info.enRegionId = REGION_ID_0; // OSD区域编号0
rgn_info.u32Width = bitmap.u32Width;// OSD区域宽度
rgn_info.u32Height = bitmap.u32Height;// OSD区域高度
rgn_info.u32PosX = 128; // 水印左上角X坐标
rgn_info.u32PosY = 128; // 水印左上角Y坐标
rgn_info.u8Enable = 1; // 使能OSD叠加
rgn_info.u8Inverse = 0; // 不做颜色翻转
// ===================== 7. 将位图设置到VENC编码通道,完成水印叠加 =====================
ret = RK_MPI_VENC_RGN_SetBitMap(VENC_CHN, &rgn_info, &bitmap);
if (ret)
{
printf("RK_MPI_VENC_RGN_SetBitMap Failed…\\n");
}
else
{
printf("RK_MPI_VENC_RGN_SetBitMap Success…\\n");
}
// ===================== 8. 统一释放所有动态内存与SDL资源(防止内存泄漏) =====================
free(bitmap.pData); // 释放RK位图内存
free(pixel_format); // 释放自定义像素格式
SDL_FreeSurface(text_surface); // 释放原始SDL画布
SDL_FreeSurface(convert_text_surface); // 释放转换后SDL画布
TTF_CloseFont(ttf_font); // 关闭字体文件
TTF_Quit(); // 退出TTF字体库
return NULL;
}
/**
* @brief H264码流获取线程:从编码通道取码流并写入本地文件
* @param args 线程入参(未使用)
* @return NULL
*/
void *get_h264_data_thread(void *args)
{
pthread_detach(pthread_self());
// 以读写模式创建本地h264文件
FILE *h264_file = fopen("test_osd_venc.h264", "w+");
MEDIA_BUFFER mb; // RK媒体缓冲区句柄
while (1)
{
// 阻塞获取VENC编码后的码流缓冲区
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, VENC_CHN, -1);
if (!mb)
{
printf("get media_buffer failed…\\n");
break;
}
printf("get OSD_buffer success…\\n");
// 将码流数据写入文件
fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, h264_file);
// 释放媒体缓冲区(必须释放,否则内存溢出)
RK_MPI_MB_ReleaseBuffer(mb);
}
return NULL;
}
int main()
{
int ret;
VI_CHN_ATTR_S vi_chn_attr;
// ===================== 1. 配置并初始化VI视频输入(摄像头) =====================
vi_chn_attr.pcVideoNode = CAMERA_PATH;
vi_chn_attr.u32Width = 1920;
vi_chn_attr.u32Height = 1080;
vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;
vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;
vi_chn_attr.u32BufCnt = 3;
vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;
ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, CAMERA_CHN, &vi_chn_attr);
if (ret)
{
printf("Vi Set Attr Failed…..\\n");
return 0;
}
printf("Vi Set Attr Success…..\\n");
ret = RK_MPI_VI_EnableChn(CAMERA_ID, CAMERA_CHN);
if (ret)
{
printf("Vi Enable Attr Failed…..\\n");
return 0;
}
printf("Vi Enable Attr Success…..\\n");
// ===================== 2. 配置并初始化VENC视频编码(H264) =====================
VENC_CHN_ATTR_S venc_chn_attr;
memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));
// 编码基础属性
venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;
venc_chn_attr.stVencAttr.u32PicWidth = 1920;
venc_chn_attr.stVencAttr.u32PicHeight = 1080;
venc_chn_attr.stVencAttr.u32VirWidth = 1920;
venc_chn_attr.stVencAttr.u32VirHeight = 1080;
venc_chn_attr.stVencAttr.u32Profile = 77;
// 码率控制:CBR恒定码率
venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 25;
venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = 2000000; // 2Mbps码率
venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;
venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;
venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;
ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr);
if (ret)
{
printf("ERROR: create VENC[0] error! ret=%d\\n", ret);
return 0;
}
printf("VENC SUCCESS\\n");
// ===================== 3. 初始化VENC OSD区域模块 =====================
ret = RK_MPI_VENC_RGN_Init(VENC_CHN, NULL);
if (ret)
{
printf("ERROR: create VENC_RGN error! ret=%d\\n", ret);
return 0;
}
printf("VENC SUCCESS\\n");
// ===================== 4. 绑定VI(摄像头) → VENC(编码器) 数据流 =====================
MPP_CHN_S vi_chn_s;
MPP_CHN_S venc_chn_s;
vi_chn_s.enModId = RK_ID_VI;
vi_chn_s.s32ChnId = CAMERA_CHN;
venc_chn_s.enModId = RK_ID_VENC;
venc_chn_s.s32ChnId = VENC_CHN;
ret = RK_MPI_SYS_Bind(&vi_chn_s, &venc_chn_s);
if (ret)
{
printf("RK_MPI_SYS_Bind Failed…\\n");
return 0;
}
printf("RK_MPI_SYS_Bind SUCCESS…\\n");
// ===================== 5. 启动两个工作线程 =====================
pthread_t bitmap_pid, venc_pid;
pthread_create(&bitmap_pid, NULL, bitmap_osd_handle_thread, NULL); // OSD水印线程
pthread_create(&venc_pid, NULL, get_h264_data_thread, NULL); // 码流保存线程
// 主线程死循环阻塞,维持程序运行
while (1)
{
sleep(2);
}
// 以下代码实际永远不会执行
RK_MPI_SYS_UnBind(&vi_chn_s, &venc_chn_s);
RK_MPI_VENC_DestroyChn(VENC_CHN);
RK_MPI_VI_DisableChn(CAMERA_ID, CAMERA_CHN);
return 0;
}
四、代码分模块深度解析
整体分为 5 大模块,重点详解 OSD 水印线程,其余模块简略说明。
4.1 模块 1:基础头文件 + 工具函数
4.2 模块 2:核心重点 —— bitmap_osd_handle_thread 水印线程
4.2.1 线程基础设置
pthread_detach(pthread_self());
- 作用:分离线程。线程退出后系统自动回收栈 / 资源,主线程不需要调用 pthread_join 等待,嵌入式多线程标准写法。
4.2.2 TTF 字体库初始化 & 打开字体文件
TTF_Init();
TTF_OpenFont("./fzlth.ttf", 48);
- 依赖:本地目录必须存在 fzlth.ttf 字体文件,否则文字渲染失败。
- 逻辑:SDL_TTF 专门用于在 SDL 画布上绘制矢量文字,必须先初始化库、再打开字体。
4.2.3 文字颜色 + 渲染原始 SDL 画布
SDL_Color sdl_color;
text_surface = TTF_RenderText_Solid(ttf_font, pstr, sdl_color);
- SDL_Color:SDL1.2 仅支持 R/G/B 三通道,无 Alpha。
- TTF_RenderText_Solid:实心渲染模式,生成一张纯文字的 SDL 画布(原始格式非 ARGB8888)。
4.2.4 手动构造 ARGB8888 像素格式
pixel_format = malloc(…);
pixel_format->Amask/Rmask/Gmask/Bmask…
convert_text_surface = SDL_ConvertSurface(…)
- 背景:RKMedia OSD 只识别 ARGB8888 位图格式,原始文字画布格式不匹配,必须转换。
- 做法:手动定义 32 位 ARGB 掩码规则,调用 SDL_ConvertSurface 完成格式转换。
- 坑点:SDL1.2 没有现成 ARGB8888 接口,只能手动构造格式结构体。
4.2.5 构建 RK 标准位图 BITMAP_S
BITMAP_S bitmap;
bitmap.u32Width = 16对齐宽度;
bitmap.u32Height = 16对齐高度;
bitmap.enPixelFormat = PIXEL_FORMAT_ARGB_8888;
bitmap.pData = calloc(…);
- BITMAP_S:RKMedia 定义的标准位图结构体,给 OSD 模块使用。
- 关键点:
- 宽高必须 16 字节对齐,否则驱动解析异常、花屏;
- calloc 分配内存并清零,避免随机垃圾像素;
- 格式严格指定为 PIXEL_FORMAT_ARGB_8888。
4.2.6 【最核心】逐行拷贝数据(解决歪斜 / 花屏)
int src_pitch = convert_text_surface->pitch;
for (int y = 0; y < src_h; y++)
{
memcpy(…, src_buf + y * src_pitch, src_w * 4);
}
关键概念解释:pitch(行跨度)
SDL 为了内存访问效率,每行像素末尾会额外填充字节做对齐:
- w * 4 = 单行有效像素字节数;
- pitch = 单行实际占用总字节数(有效像素 + 填充字节)。
为什么会歪 / 花?
- 旧代码:memcpy(整块内存),会把 每行末尾的填充垃圾字节 也拷贝进去 → 像素串行、错位、花屏。
- 现在写法:逐行拷贝,只复制有效像素,跳过 SDL 填充字节 → 图像正常。
4.2.7 配置 OSD 区域并叠加水印
OSD_REGION_INFO_S rgn_info;
RK_MPI_VENC_RGN_SetBitMap(VENC_CHN, &rgn_info, &bitmap);
1.OSD_REGION_INFO_S:配置水印位置、大小、开关。
- u32PosX/u32PosY:水印在画面上的左上角坐标;
- u8Enable = 1:开启 OSD 叠加。
2.RK_MPI_VENC_RGN_SetBitMap:RKMedia 核心接口,将位图绑定到编码通道,摄像头画面编码时自动叠加水印。
4.2.8 资源释放(嵌入式必做)
依次释放:位图内存 → 格式结构体 → SDL 画布 → 字体 → 退出 TTF 库。 不释放会造成内存泄漏,长时间运行程序崩溃。
4.3 模块 3:H264 码流获取线程
逻辑很简单:
4.4 模块 4:VI 视频输入初始化
标准 RKMedia 摄像头初始化流程:
4.5 模块 5:VENC 编码 + OSD 初始化 + 数据流绑定
4.6 核心流程总梳理
摄像头(VI) → 数据流绑定 → 编码器(VENC)
↓
提前初始化OSD模块
↓
子线程1:SDL+TTF生成文字位图 → 设置到OSD → 画面叠加水印
子线程2:循环取出H264码流 → 写入本地文件
四、补充关键注意点(踩坑总结)
五、编译配置与板卡部署
chmod +x osd_h264_demo
./osd_h264_demo
六、常见问题与排错方案(实战踩坑总结)
结合调试过程中高频问题,汇总原因与解决方案:
问题:水印区域出现彩色噪点 / 花屏 原因:宽高、坐标未做 16 对齐;结构体未memset清零产生脏数据。 解决:统一使用get_align16_value函数,所有局部结构体强制清零。
问题:文字颜色错乱、重影、变色 原因:未做ARGB → BGRA像素转换,SDL 格式与 RV1126 硬件格式不匹配。 解决:补全逐像素格式转换逻辑。
问题:SDL/TTF 初始化、字体加载失败 原因:库文件缺失、字体文件路径错误、交叉编译版本不匹配。 解决:检查动态库部署,确保fzlth.ttf与程序同目录。
问题:程序运行一段时间后卡死 原因:媒体缓冲区MEDIA_BUFFER未释放,或 SDL Surface 未释放导致内存泄漏。 解决:严格执行资源释放逻辑。
问题:水印只显示一帧,后续消失 原因:RK_MPI_VENC_RGN_SetBitMap仅执行一次,未放入死循环。 解决:在循环中持续推送位图数据。
七、本章总结与知识点汇总
7.1 核心知识点复盘
7.2 拓展学习方向
本案例是 RV1126 嵌入式音视频开发的综合入门项目,吃透本代码后,可延伸学习视频截图、动态贴纸、多路 OSD 叠加等进阶功能。



