一、rkmedia_test测试例程
RKMedia
的核心思想是把各个硬件资源独立成模块,模块开放出输入和输出端通过绑定的方式控制流从某个模块流出并且流入另外一个模块
目前 rk 平台可以支持的摄像头数据 yuv 和 raw 数据。 Raw 数据是没有经过 isp 信号处理的原始数据,需要 rk 芯片内部 isp 图像处理才能还原真实图像。 Yuv 数据是 camera 端的 isp 经过处理后送出来的数据。
rkmedia的使用:
代码目录:external/rkmedia/examples/
本文以rkmedia_vi_vo_test.c为例,还有许多的例程,大家可以自行研究。
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "common/sample_common.h"
#include "rkmedia_api.h"
static bool quit = false;
static void sigterm_handler(int sig) {
fprintf(stderr, "signal %d\n", sig);
quit = true;
}
static RK_CHAR optstr[] = "?::a::I:M:";
static const struct option long_options[] = {
{"aiq", optional_argument, NULL, 'a'},
{"camid", required_argument, NULL, 'I'},
{"multictx", required_argument, NULL, 'M'},
{"help", optional_argument, NULL, '?'},
{NULL, 0, NULL, 0},
};
static void print_usage(const RK_CHAR *name) {
printf("usage example:\n");
#ifdef RKAIQ
printf("\t%s [-a [iqfiles_dir]]"
"[-I 0] "
"[-M 0] "
"\n",
name);
printf("\t-a | --aiq: enable aiq with dirpath provided, eg:-a "
"/oem/etc/iqfiles/, "
"set dirpath empty to using path by default, without this option aiq "
"should run in other application\n");
printf("\t-M | --multictx: switch of multictx in isp, set 0 to disable, set "
"1 to enable. Default: 0\n");
#else
printf("\t%s [-I 0]\n", name);
#endif
printf("\t-I | --camid: camera ctx id, Default 0\n");
}
int main(int argc, char *argv[]) {
int ret = 0;
int video_width = 400;
int video_height = 400;
int disp_width = 720;
int disp_height = 1280;
RK_S32 s32CamId = 0;
#ifdef RKAIQ
RK_BOOL bMultictx = RK_FALSE;
#endif
int c;
char *iq_file_dir = NULL;
while ((c = getopt_long(argc, argv, optstr, long_options, NULL)) != -1) {
const char *tmp_optarg = optarg;
switch (c) {
case 'a':
if (!optarg && NULL != argv[optind] && '-' != argv[optind][0]) {
tmp_optarg = argv[optind++];
}
if (tmp_optarg) {
iq_file_dir = (char *)tmp_optarg;
} else {
iq_file_dir = "/oem/etc/iqfiles";
}
break;
case 'I':
s32CamId = atoi(optarg);
break;
#ifdef RKAIQ
case 'M':
if (atoi(optarg)) {
bMultictx = RK_TRUE;
}
break;
#endif
case '?':
default:
print_usage(argv[0]);
return 0;
}
}
printf("#zcy CameraIdx: %d\n\n", s32CamId);
if (iq_file_dir) {
#ifdef RKAIQ
printf("#Rkaiq XML DirPath: %s\n", iq_file_dir);
printf("#bMultictx: %d\n\n", bMultictx);
rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
int fps = 30;
SAMPLE_COMM_ISP_Init(s32CamId, hdr_mode, bMultictx, iq_file_dir);
SAMPLE_COMM_ISP_Run(s32CamId);
SAMPLE_COMM_ISP_SetFrameRate(s32CamId, fps);
#endif
}
RK_MPI_SYS_Init();
VI_CHN_ATTR_S vi_chn_attr;
vi_chn_attr.pcVideoNode = "rkispp_scale0";
vi_chn_attr.u32BufCnt = 3;
vi_chn_attr.u32Width = video_width;
vi_chn_attr.u32Height = video_height;
vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;
vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;
ret = RK_MPI_VI_SetChnAttr(s32CamId, 0, &vi_chn_attr);
ret |= RK_MPI_VI_EnableChn(s32CamId, 0);
if (ret) {
printf("Create vi[0] failed! ret=%d\n", ret);
return -1;
}
/* test rgn cover */
COVER_INFO_S CoverInfo;
OSD_REGION_INFO_S RngInfo;
memset(&CoverInfo, 0, sizeof(CoverInfo));
memset(&RngInfo, 0, sizeof(RngInfo));
CoverInfo.enPixelFormat = PIXEL_FORMAT_ARGB_8888;
CoverInfo.u32Color = 0xFFFF0000; // blue
RngInfo.enRegionId = REGION_ID_0;
RngInfo.u32PosX = 0;
RngInfo.u32PosY = 0;
RngInfo.u32Width = 100;
RngInfo.u32Height = 100;
RngInfo.u8Enable = 1;
RK_MPI_VI_RGN_SetCover(s32CamId, 1, &RngInfo, &CoverInfo);
// rga0 for primary plane
RGA_ATTR_S stRgaAttr;
memset(&stRgaAttr, 0, sizeof(stRgaAttr));
stRgaAttr.bEnBufPool = RK_TRUE;
stRgaAttr.u16BufPoolCnt = 3;
stRgaAttr.u16Rotaion = 90;
stRgaAttr.stImgIn.u32X = 0;
stRgaAttr.stImgIn.u32Y = 0;
stRgaAttr.stImgIn.imgType = IMAGE_TYPE_NV12;
stRgaAttr.stImgIn.u32Width = video_width;
stRgaAttr.stImgIn.u32Height = video_height;
stRgaAttr.stImgIn.u32HorStride = video_width;
stRgaAttr.stImgIn.u32VirStride = video_height;
stRgaAttr.stImgOut.u32X = 0;
stRgaAttr.stImgOut.u32Y = 0;
stRgaAttr.stImgOut.imgType = IMAGE_TYPE_RGB888;
stRgaAttr.stImgOut.u32Width = disp_width;
stRgaAttr.stImgOut.u32Height = disp_height;
stRgaAttr.stImgOut.u32HorStride = disp_width;
stRgaAttr.stImgOut.u32VirStride = disp_height;
ret = RK_MPI_RGA_CreateChn(0, &stRgaAttr);
if (ret) {
printf("Create rga[0] falied! ret=%d\n", ret);
return -1;
}
VO_CHN_ATTR_S stVoAttr = {0};
// VO[0] for primary plane
stVoAttr.pcDevNode = "/dev/dri/card0";
stVoAttr.emPlaneType = VO_PLANE_PRIMARY;
stVoAttr.enImgType = IMAGE_TYPE_RGB888;
stVoAttr.u16Zpos = 0;
stVoAttr.stDispRect.s32X = 0;
stVoAttr.stDispRect.s32Y = 0;
stVoAttr.stDispRect.u32Width = disp_width;
stVoAttr.stDispRect.u32Height = disp_height;
ret = RK_MPI_VO_CreateChn(0, &stVoAttr);
if (ret) {
printf("Create vo[0] failed! ret=%d\n", ret);
return -1;
}
MPP_CHN_S stSrcChn = {0};
MPP_CHN_S stDestChn = {0};
printf("#Bind VI[0] to RGA[0]....\n");
stSrcChn.enModId = RK_ID_VI;
stSrcChn.s32ChnId = 0;
stDestChn.enModId = RK_ID_RGA;
stDestChn.s32ChnId = 0;
ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
if (ret) {
printf("Bind vi[0] to rga[0] failed! ret=%d\n", ret);
return -1;
}
printf("# Bind RGA[0] to VO[0]....\n");
stSrcChn.enModId = RK_ID_RGA;
stSrcChn.s32ChnId = 0;
stDestChn.enModId = RK_ID_VO;
stDestChn.s32ChnId = 0;
ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
if (ret) {
printf("Bind rga[0] to vo[0] failed! ret=%d\n", ret);
return -1;
}
printf("%s initial finish\n", __func__);
signal(SIGINT, sigterm_handler);
while (!quit) {
usleep(500000);
}
printf("%s exit!\n", __func__);
stSrcChn.enModId = RK_ID_VI;
stSrcChn.s32ChnId = 0;
stDestChn.enModId = RK_ID_RGA;
stDestChn.s32ChnId = 0;
ret = RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
if (ret) {
printf("UnBind vi[0] to rga[0] failed! ret=%d\n", ret);
return -1;
}
stSrcChn.enModId = RK_ID_RGA;
stSrcChn.s32ChnId = 0;
stDestChn.enModId = RK_ID_VO;
stDestChn.s32ChnId = 0;
ret = RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
if (ret) {
printf("UnBind rga[0] to vo[0] failed! ret=%d\n", ret);
return -1;
}
RK_MPI_VO_DestroyChn(0);
RK_MPI_RGA_DestroyChn(0);
RK_MPI_VI_DisableChn(s32CamId, 0);
if (iq_file_dir) {
#ifdef RKAIQ
SAMPLE_COMM_ISP_Stop(s32CamId);
#endif
}
return 0;
}
具体地解释一下代码的功能。
- 首先,代码通过getopt_long函数解析命令行参数。其中,-a或--aiq参数可选择是否启用AIQ(图像质量增强),可以提供IQ文件的目录作为参数;-I或--camid参数用于指定摄像头的编号;-M或--multictx参数用于选择是否启用多通道配置。
- 如果启用了AIQ(定义了RKAIQ宏),则会调用SAMPLE_COMM_ISP_Init函数进行AIQ模块的初始化配置,并根据参数设置HDR模式和是否启用多通道配置。然后调用SAMPLE_COMM_ISP_Run函数开始运行ISP(图像信号处理)模块,并设置帧率。
- 调用RK_MPI_SYS_Init函数进行系统初始化。
- 设置视频输入通道属性,包括视频宽高、像素格式、工作模式等,并通过RK_MPI_VI_SetChnAttr和RK_MPI_VI_EnableChn函数使能视频输入通道。
- 测试遮挡区域(cover)。创建COVER_INFO_S和OSD_REGION_INFO_S结构体来设置遮挡区域的颜色、位置和大小等信息,然后调用RK_MPI_VI_RGN_SetCover函数将遮挡区域应用到视频输入通道上。
- 使用RGA(Rockchip Graphics Accelerator)模块进行图像转换。首先创建RGA_ATTR_S结构体,设置输入图像和输出图像的参数,包括图像类型、宽高、步长等。然后调用RK_MPI_RGA_CreateChn函数创建RGA通道。
- 创建VO(Video Output)通道用于显示视频到屏幕上。设置VO的设备节点、平面类型、图像类型、显示位置和大小等参数,并调用RK_MPI_VO_CreateChn函数创建VO通道。
- 通过RK_MPI_SYS_Bind函数将VI(Video Input)通道和RGA通道、RGA通道和VO通道进行绑定,从而实现视频输入、处理和输出的流程。
- 进入主循环,等待SIGINT信号(通过signal函数注册了SIGINT的处理函数)。主循环中使用usleep函数暂停程序500毫秒,直到接收到SIGINT信号时退出循环。
- 在退出之前,调用RK_MPI_SYS_UnBind函数解绑VI和RGA通道、RGA和VO通道。然后销毁RGA通道和VO通道,最后禁用视频输入通道。
- 如果启用了AIQ(定义了RKAIQ宏),则调用SAMPLE_COMM_ISP_Stop函数停止ISP模块的运行。
二、RKMedia例程测试注意事项
查看图像的stride,图像的stride(跨度、跨距、步长、步幅),表示在内存中每行像素所占的字节数。
在RKMedia下,设置采集的图像的宽度能被16整除,且stride=图像的宽度即可!
即:400x400
图像buffer宽度 = stride宽度
stride宽度 / 16 = 整数n
1.不能在开启ispserver服务的时候又指定 iqfiles,会报错,两者存在冲突,二者选其一。
1)查看ispserver服务是否已经开启了:
ps -aux | grep ispserver
2)开启ispserver服务:
ispserver --no-sync-db & // 表示不开启数据库服务dbserver
// 或者
ispserver &
3)关闭ispserver服务:
kill -9 3050
2.如果开启了摄像头应用(mediaserver),请先将其关闭后才可以使用RKMedia抓取图片。
开启和关闭mediaserver的脚本在开发板的/oem目录下:
// 开启摄像头应用(mediaserver)
/oem/RkLunch.sh &
// 关闭摄像头应用(mediaserver)
/oem/RkLunch-stop.sh