RV1126的OSD模块和SDL_TTF结合输出H264文件
- 电脑硬件
- 2025-09-14 07:45:02

目录
一.RV1126多线程处理输出OSD字符叠加图层的流程
1.1. VI模块的初始化
1.2. 初始化VENC模块:
1.3. 初始化RGN模块:
1.4. 绑定VI模块和VENC模块,伪代码如下
1.5. 创建多线程进行OSD字库的叠加:
1.6. 获取每一帧处理过后的VENC数据:
二.程序代码
三.运行结果
一.RV1126多线程处理输出OSD字符叠加图层的流程
用RV1126多线程输出OSD叠加需要经过上面几个重要步骤,分别是VI模块初始化、VENC模块初始化、RGN模块初始化、多线程进行OSD字库的叠加(里面主要是进行字库的渲染和RV1126的OSD模块叠加)、多线程获取每一帧OSD处理过后的编码数据
1.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);
1.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);
1.3. 初始化RGN模块:RGN是RV1126图层区域功能管理的功能,比方说OSD图层、Bitmap位图都属于RGN模块。所以开发者要用到OSD功能都需要初始化RGN,具体的API描述如下:
RK_S32_RK_MPI_VENC_RGN_Init(VENC_CHN_VeChn, VENC_COLOR_TBL_S *stColorTbl);
第一个传参数:VENC编码通道号
第二个传参数:VENC_COLOR_TBL_S结构体指针,默认是NULL
RK_MPI_VENC_RGN_Init(0, NULL);
1.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);
1.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是要对齐的数值。
1.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);
............................
}
二.程序代码 #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 "common/sample_common.h" #include "rkmedia_api.h" #include <stdio.h> #include "SDL.h" #include "SDL_ttf.h" #include <time.h> #define CAMERA_PATH "rkispp_scale0" #define CAMERA_ID 0 #define CAMERA_CHN 0 #define VENC_CHN 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; } void * bitmap_osd_handle_thread(void * args) { pthread_detach(pthread_self()); int ret ; TTF_Font * ttf_font; char *pstr = "2019-11-21 15:40:29"; SDL_Surface * text_surface; SDL_Surface * convert_text_surface; SDL_PixelFormat * pixel_format; //TTF模块的初始化 ret = TTF_Init(); if(ret < 0) { printf("TTF_Init Failed...\n"); } //打开TTF的字库 ttf_font = TTF_OpenFont("./fzlth.ttf", 48); if(ttf_font == NULL) { printf("TTF_OpenFont Failed...\n"); } //SDL_COLOR黑色,RGB(0,0,0) SDL_Color sdl_color; sdl_color.r = 0; sdl_color.g = 0; sdl_color.b = 0; text_surface = TTF_RenderText_Solid(ttf_font, pstr, sdl_color); 渲染文字 //ARGB_8888 pixel_format = (SDL_PixelFormat *)malloc(sizeof(SDL_PixelFormat)); pixel_format->BitsPerPixel = 32; //每个像素所占的比特位数 pixel_format->BytesPerPixel = 4; //每个像素所占的字节数 pixel_format->Amask = 0XFF000000;//ARGB的A掩码,A位0xff pixel_format->Rmask = 0X00FF0000;//ARGB的R掩码,R位0xff pixel_format->Gmask = 0X0000FF00;//ARGB的G掩码,G位0xff pixel_format->Bmask = 0X000000FF;//ARGB的B掩码,B位0xff convert_text_surface = SDL_ConvertSurface(text_surface, pixel_format, 0); if(convert_text_surface == NULL) { printf("convert_text_surface failed...\n"); } BITMAP_S bitmap;//Bitmap位图结构体 bitmap.u32Width = get_align16_value(convert_text_surface->w, 16);//Bitmap的宽度 bitmap.u32Height = get_align16_value(convert_text_surface->h, 16);//Bitmap的高度 bitmap.enPixelFormat = PIXEL_FORMAT_ARGB_8888;像素格式ARGB8888 bitmap.pData = malloc((bitmap.u32Width) * (bitmap.u32Height) * pixel_format->BytesPerPixel); bitmap的data的分配大小 memcpy(bitmap.pData, convert_text_surface->pixels, (convert_text_surface->w) * (convert_text_surface->h) *pixel_format->BytesPerPixel);bitmap的data赋值 OSD_REGION_INFO_S rgn_info; //OSD_RGN_INFO结构体 rgn_info.enRegionId = REGION_ID_0; //rgn的区域ID rgn_info.u32Width = bitmap.u32Width ; //osd的长度 rgn_info.u32Height = bitmap.u32Height ; //osd的高度 rgn_info.u32PosX = 128; //Osd的X轴方向 rgn_info.u32PosY = 128; //Osd的Y轴方向 rgn_info.u8Enable = 1; 使能OSD模块,1是使能,0为禁止。 rgn_info.u8Inverse = 0; //禁止翻转 ret = RK_MPI_VENC_RGN_SetBitMap(VENC_CHN, &rgn_info, &bitmap); //设置OSD位图 if(ret) { printf("RK_MPI_VENC_RGN_SetBitMap failed...\n"); } else { printf("RK_MPI_VENC_RGN_SetBitMap Success...\n"); } return NULL; } void * get_h264_data_thread(void * args) { pthread_detach(pthread_self()); FILE *h264_file = fopen("test_osd_venc.h264", "w+"); MEDIA_BUFFER mb ; while (1) { mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, VENC_CHN, -1); if(!mb) { printf("RK_MPI_SYS_GetMediaBuffer 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; 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获取类型 vi_chn_attr.u32BufCnt = 3; //设置缓冲数量 vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL; //设置VI工作类型 ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, CAMERA_CHN, &vi_chn_attr); if (ret) { printf("Vi Set Attr Failed.....\n"); return 0; } else { 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; } else { printf("Vi Enable Attr Success.....\n"); } 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;//设置编码等级 venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;//设置H264的CBR码率控制模式 venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 25; venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = 2000000; // 2Mb 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); //VENC模块的初始化 if (ret) { printf("ERROR: create VENC[0] error! ret=%d\n", ret); return 0; } else { printf("VENC SUCCESS\n"); } ret = RK_MPI_VENC_RGN_Init(VENC_CHN, NULL);//RGN模块的初始化 if(ret) { printf("Create VENC_RGN Failed .....\n"); return 0; } else { printf("Create VENC_RGN Success .....\n"); } MPP_CHN_S vi_chn_s; MPP_CHN_S venc_chn_s; vi_chn_s.enModId = RK_ID_VI; vi_chn_s.s32ChnId = 0; venc_chn_s.enModId = RK_ID_VENC; venc_chn_s.s32ChnId = 0; ret = RK_MPI_SYS_Bind(&vi_chn_s, &venc_chn_s); VI模块节点和VENC节点绑定 if(ret) { printf("RK_MPI_SYS_Bind Failed .....\n"); } else { printf("RK_MPI_SYS_Bind Success .....\n"); } 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); //获取H264码流线程 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; } 三.运行结果注意:假设出现水印显示不完整,这种情况下需要调整X轴和Y轴的坐标轴,让其水印显示完整。
RV1126的OSD模块和SDL_TTF结合输出H264文件由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“RV1126的OSD模块和SDL_TTF结合输出H264文件”
上一篇
C++:内联函数