阅读RK_MPI的相关文档,裁剪缩放、旋转功能的实现依靠vpss模块
时间戳主要依靠RGN模块
如果编译vpss模块失败,要注意头文件要加上vpss模块的内容
#include "test_comm_vpss.h"
#include "../common/test_comm_vpss.cpp"
在test_vi_bind_vo_loop中
test_vi_init初始化vi后,创建vpss模块,并bind_vi_vpss
要使裁剪旋转等生效,要注意VpssChnMode,设置为VPSS_CHN_MODE_USER
/* vpss */
ctx->stVpssCfg.u32VpssChnCnt = 1;
ctx->stVpssCfg.stGrpVpssAttr.u32MaxW = 4096;
ctx->stVpssCfg.stGrpVpssAttr.u32MaxH = 4096;
ctx->stVpssCfg.stGrpVpssAttr.enPixelFormat = RK_FMT_RGB888;
ctx->stVpssCfg.stGrpVpssAttr.stFrameRate.s32SrcFrameRate = -1;
ctx->stVpssCfg.stGrpVpssAttr.stFrameRate.s32DstFrameRate = -1;
ctx->stVpssCfg.stGrpVpssAttr.enCompressMode = COMPRESS_MODE_NONE;
for (i = 0; i < VPSS_MAX_CHN_NUM; i ++) {
ctx->stVpssCfg.stVpssChnAttr[i].enChnMode = VPSS_CHN_MODE_USER; //Enable crop and rotation, change mode to VPSS_CHN_MODE_USER
ctx->stVpssCfg.stVpssChnAttr[i].enDynamicRange = DYNAMIC_RANGE_SDR8;
ctx->stVpssCfg.stVpssChnAttr[i].enPixelFormat = RK_FMT_RGB888;
ctx->stVpssCfg.stVpssChnAttr[i].stFrameRate.s32SrcFrameRate = -1;
ctx->stVpssCfg.stVpssChnAttr[i].stFrameRate.s32DstFrameRate = -1;
ctx->stVpssCfg.stVpssChnAttr[i].u32Width = ctx->width;
ctx->stVpssCfg.stVpssChnAttr[i].u32Height = ctx->height;
ctx->stVpssCfg.stVpssChnAttr[i].enCompressMode = COMPRESS_MODE_NONE;
}
// init vpss
s32Ret = create_vpss(&ctx->stVpssCfg, 0, ctx->stVpssCfg.u32VpssChnCnt);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("creat 0 grp vpss failed!");
goto __FAILED;
}
MPP_CHN_S stViChn, stVpssChn;
// bind vi to vpss
stViChn.enModId = RK_ID_VI;
stViChn.s32DevId = ctx->devId;
stViChn.s32ChnId = ctx->channelId;
stVpssChn.enModId = RK_ID_VPSS;
stVpssChn.s32DevId = 0;
stVpssChn.s32ChnId = 0;
RK_LOGD("vi to vpss ch %d vpss group %d", stVpssChn.s32ChnId , stVpssChn.s32DevId);
s32Ret = RK_MPI_SYS_Bind(&stViChn, &stVpssChn);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("vi and vpss bind error ");
goto __FAILED;
}
另外裁剪、缩放、旋转等要在创建组以后,使能Chn以前,另外旋转要在裁剪缩放以后
原sensor分辨率则为上面VpssChnarrt设置的分辨率,因为要显示在1280x800的屏幕上,所以要先裁剪原sensor分辨率,使之成为1280x800的倍数
设置裁剪参数时,3456x2160即为裁剪后的分辨率
static RK_S32 create_vpss(VPSS_CFG_S *pstVpssCfg, RK_S32 s32Grp, RK_S32 s32OutChnNum) {
RK_S32 s32Ret = RK_SUCCESS;
VPSS_CHN VpssChn[VPSS_MAX_CHN_NUM] = { VPSS_CHN0, VPSS_CHN1, VPSS_CHN2, VPSS_CHN3 };
VPSS_CROP_INFO_S stCropInfo;
s32Ret = RK_MPI_VPSS_CreateGrp(s32Grp, &pstVpssCfg->stGrpVpssAttr);
if (s32Ret != RK_SUCCESS) {
return s32Ret;
}
for (RK_S32 i = 0; i < s32OutChnNum; i++) {
s32Ret = RK_MPI_VPSS_SetChnAttr(s32Grp, VpssChn[i], &pstVpssCfg->stVpssChnAttr[i]);
if (s32Ret != RK_SUCCESS) {
return s32Ret;
}
stCropInfo.bEnable = RK_TRUE;
stCropInfo.enCropCoordinate = VPSS_CROP_RATIO_COOR;
stCropInfo.stCropRect.s32X = 0; //192 + 3456 + 192 = 3840, when x = 192, video on center
stCropInfo.stCropRect.s32Y = 0;
stCropInfo.stCropRect.u32Width = 3456;
stCropInfo.stCropRect.u32Height = 2160;
RK_MPI_VPSS_SetChnCrop(s32Grp, VpssChn[i], &stCropInfo);
s32Ret = RK_MPI_VPSS_SetChnRotation(s32Grp,VpssChn[i], ROTATION_270);
if (s32Ret != RK_SUCCESS) {
return s32Ret;
}
s32Ret = RK_MPI_VPSS_EnableChn(s32Grp, VpssChn[i]);
if (s32Ret != RK_SUCCESS) {
return s32Ret;
}
}
s32Ret = RK_MPI_VPSS_EnableBackupFrame(s32Grp);
if (s32Ret != RK_SUCCESS) {
return s32Ret;
}
s32Ret = RK_MPI_VPSS_StartGrp(s32Grp);
if (s32Ret != RK_SUCCESS) {
return s32Ret;
}
return RK_SUCCESS;
}
旋转则通过s32Ret = RK_MPI_VPSS_SetChnRotation(s32Grp,VpssChn[i], ROTATION_270);
ROTATION_90、180、270
在bind_vpss_vo后,创建一个时间戳线程
pthread_t tid333;
// 创建rgn线程
pthread_create(&tid333, NULL, rgn_overlay_thread, NULL);
void* rgn_overlay_thread(void* arg){
printf("========%s========\n", __func__);
RK_S32 s32Ret = RK_SUCCESS;
RGN_HANDLE RgnHandle = 1;
BITMAP_S stBitmap;
RGN_ATTR_S stRgnAttr;
RGN_CHN_ATTR_S stRgnChnAttr;
int u32Width = 100;
int u32Height = 500;
int s32X = 128;
int s32Y = 128;
RK_CHAR *filename = "/data/res/rgn/44";
MPP_CHN_S stMppChn;
stMppChn.enModId = RK_ID_VO;
stMppChn.s32DevId = RK356X_VOP_LAYER_ESMART_0;
stMppChn.s32ChnId = 0;
/****************************************
step 1: create overlay regions
****************************************/
stRgnAttr.enType = OVERLAY_RGN;
stRgnAttr.unAttr.stOverlay.enPixelFmt = (PIXEL_FORMAT_E)RK_FMT_RGBA8888;
stRgnAttr.unAttr.stOverlay.stSize.u32Width = u32Width;
stRgnAttr.unAttr.stOverlay.stSize.u32Height = u32Height;
stRgnAttr.unAttr.stOverlay.u32ClutNum = 0;
stRgnAttr.unAttr.stOverlay.u32CanvasNum =2;
s32Ret = RK_MPI_RGN_Create(RgnHandle, &stRgnAttr);
if (RK_SUCCESS != s32Ret) {
RK_LOGE("RK_MPI_RGN_Create (%d) failed with %#x!", RgnHandle, s32Ret);
RK_MPI_RGN_Destroy(RgnHandle);
}
RK_LOGI("The handle: %d, create success!", RgnHandle);
/*********************************************
step 2: display overlay regions to groups
*********************************************/
memset(&stRgnChnAttr, 0, sizeof(stRgnChnAttr));
stRgnChnAttr.bShow = RK_TRUE;
stRgnChnAttr.enType = OVERLAY_RGN;
stRgnChnAttr.unChnAttr.stOverlayChn.stPoint.s32X = s32X;
stRgnChnAttr.unChnAttr.stOverlayChn.stPoint.s32Y = s32Y;
stRgnChnAttr.unChnAttr.stOverlayChn.u32BgAlpha = 0;
stRgnChnAttr.unChnAttr.stOverlayChn.u32FgAlpha = 0;
stRgnChnAttr.unChnAttr.stOverlayChn.u32Layer = 0;
stRgnChnAttr.unChnAttr.stOverlayChn.stQpInfo.bEnable = RK_FALSE;
stRgnChnAttr.unChnAttr.stOverlayChn.stQpInfo.bForceIntra = RK_TRUE;
stRgnChnAttr.unChnAttr.stOverlayChn.stQpInfo.bAbsQp = RK_FALSE;
stRgnChnAttr.unChnAttr.stOverlayChn.stQpInfo.s32Qp = RK_FALSE;
stRgnChnAttr.unChnAttr.stOverlayChn.u32ColorLUT[0] = 0x00;
stRgnChnAttr.unChnAttr.stOverlayChn.u32ColorLUT[1] = 0xFFFFFF;
stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.bInvColEn = RK_FALSE;
stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Width = 16;
stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Height = 16;
stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.enChgMod = LESSTHAN_LUM_THRESH;
stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.u32LumThresh = 100;
s32Ret = RK_MPI_RGN_AttachToChn(RgnHandle, &stMppChn, &stRgnChnAttr);
if (RK_SUCCESS != s32Ret) {
RK_LOGE("RK_MPI_RGN_AttachToChn (%d) failed with %#x!", RgnHandle, s32Ret);
}
RK_LOGI("Display region to chn success!");
/*********************************************
step 3: show bitmap
*********************************************/
RK_S64 s64ShowBmpStart = TEST_COMM_GetNowUs();
stBitmap.enPixelFormat = (PIXEL_FORMAT_E)RK_FMT_RGBA8888;
stBitmap.u32Width = u32Width;
stBitmap.u32Height = u32Height;
RK_U16 ColorBlockSize = stBitmap.u32Height * stBitmap.u32Width;
stBitmap.pData = malloc(ColorBlockSize * TEST_ARGB32_PIX_SIZE);
RK_U8 *ColorData = (RK_U8 *)stBitmap.pData;
//if (filename) {
//s32Ret = load_file_osdmem(filename, stBitmap.pData, u32Width, u32Height,
// TEST_ARGB32_PIX_SIZE, 0);
RK_U32 mem_len = u32Width;
RK_U32 read_len = mem_len * 4 >> 0;
RK_U32 read_height;
FILE *file = NULL;
while(1){
file = fopen(filename, "rb");
if (file == NULL) {
RK_LOGE("open filename: %s file failed!", filename);
}
for (read_height = 0; read_height < u32Height; read_height++) {
fread((stBitmap.pData + (u32Width * read_height * 4 >> 0)), 1,
read_len, file);
}
fclose(file);
if (RK_SUCCESS != s32Ret) {
set_argb8888_buffer((RK_U32 *)ColorData, ColorBlockSize / 4, TEST_ARGB32_RED);
set_argb8888_buffer((RK_U32 *)(ColorData + ColorBlockSize),
ColorBlockSize / 4, TEST_ARGB32_GREEN);
set_argb8888_buffer((RK_U32 *)(ColorData + 2 * ColorBlockSize),
ColorBlockSize / 4, TEST_ARGB32_BLUE);
set_argb8888_buffer((RK_U32 *)(ColorData + 3 * ColorBlockSize),
ColorBlockSize / 4, 0x000000FF);
}
//}
s32Ret = RK_MPI_RGN_SetBitMap(RgnHandle, &stBitmap);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("RK_MPI_RGN_SetBitMap failed with %#x!", s32Ret);
}
RK_S64 s64ShowBmpEnd = TEST_COMM_GetNowUs();
RK_LOGI("Handle:%d, space time %lld us, load bmp success!", RgnHandle,
s64ShowBmpEnd - s64ShowBmpStart);
sleep(1);
}
//另一种刷osd的方式
#if 0
/*********************************************
step 4: use update canvas interface
*********************************************/
s64ShowBmpStart = TEST_COMM_GetNowUs();
RGN_CANVAS_INFO_S stCanvasInfo;
memset(&stCanvasInfo, 0, sizeof(RGN_CANVAS_INFO_S));
s32Ret = RK_MPI_RGN_GetCanvasInfo(RgnHandle, &stCanvasInfo);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("RK_MPI_RGN_GetCanvasInfo failed with %#x!", s32Ret);
//return RK_FAILURE;
}
memset(reinterpret_cast<void *>(stCanvasInfo.u64VirAddr), 0xff,
stCanvasInfo.u32VirWidth * stCanvasInfo.u32VirHeight >> 2);
s32Ret = RK_MPI_RGN_UpdateCanvas(RgnHandle);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("RK_MPI_RGN_UpdateCanvas failed with %#x!", s32Ret);
//return RK_FAILURE;
}
s64ShowBmpEnd = TEST_COMM_GetNowUs();
RK_LOGI("Handle:%d, space time %lld us, update canvas success!", RgnHandle, s64ShowBmpEnd - s64ShowBmpStart);
#endif
free(stBitmap.pData);
return 0;
}
此处的时间戳主要通过读取一个argb格式的文件实现的
通过打开这个argb格式文件,读取到stBitmap,随后通过RK_MPI_RGN_SetBitMap将数据送到画布中
另外通过一个本地运行的python_demo,获取本地时间戳生成argb格式的数据
from PIL import Image, ImageDraw, ImageFont
import os
import datetime
import time
# 获取DejaVuSans字体文件路径(如果不存在,您需要手动指定一个合适的字体文件路径)
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
if not os.path.isfile(font_path):
raise FileNotFoundError("DejaVuSans.ttf font file not found. Please provide a valid font file path.")
# 创建一个空白的RGBA模式图片,大小为500x100像素,背景透明
width, height = 500, 100
font_size = 40
font = ImageFont.truetype(font_path, font_size)
while True:
# 创建一个空白的RGBA模式图片,大小为500x100像素,背景透明
img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
# 在图片上绘制时间
draw = ImageDraw.Draw(img)
# 获取当前系统时间
now = datetime.datetime.now()
hour = now.hour
minute = now.minute
second = now.second
# 将时间转换为字符串
time_str = "{:02d}:{:02d}:{:02d}".format(hour, minute, second)
# 获取文本边界框(bounding box)
text_bbox = draw.textbbox((0, 0), time_str, font=font)
# 计算文本尺寸
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
# 计算文本位置以居中显示
x = (width - text_width) / 2
y = (height - text_height) / 2
# 绘制时间文本
draw.text((x, y), time_str, font=font, fill=(0, 0, 0, 255)) # 文本颜色为黑色
# 转换为BGRA8888格式
img_bgra = img.convert("RGBA").tobytes("raw", "BGRA")
# 将图片旋转90度
rotated_img = img.rotate(270, expand=True)
# 保存旋转后的图片
with open("/data/res/rgn/44", "wb") as f:
f.write(rotated_img.tobytes())
# 暂停一秒钟,避免频繁生成图片
time.sleep(1)
此处要注意大小端问题,虽说是argb格式,要保存为bgra格式,rk3568才能正常读取为argb8888
实际测试这种实现方法应该是有误差的,因为两个程序的生成argb格式图片与获取argb格式图片并不同步