一、简介
分析一下rkmedia的代码,给自己加深一下印象
二、代码
2.1 头文件以及定义
首先包含了必要的头文件,以及rkmedia底层的库
static bool quit 是定义了布尔类型的变量来表示这个程序的运行状态 false表示没停止 true表示停止
sigterm_handler(int sig)定义了一个信号处理函数,后面绑定信号,该函数内容是:
- fprintf: 这一行将接收到的信号编号打印到标准错误流(
stderr
)。 - quit = true: 这行代码将全局变量
quit
设置为true。即表示停止程序。
// Copyright 2020 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.#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;
}
这里的optstr 和 long_options 分别是短选项字符串、长选项结构数组。(用于后面解析命令,在main函数的getopt_long用到)
这里短选项字符串定义了?、a、I、h、w、M几个参数,字符后有::表示可以要参数也可以不要,比如这里-a是用于指定iqfile路径的,它后面有两个:表示可以不指定所以我们实用的时候可以-a后面不接参数,字符后面有一个:的表示调用这个属性后面需要指定参数
这里的长选项结构数组也是类似的long_options中分别为:名称、需不需要参数、如果为 NULL,则返回第四个参数的值,反之返回自己、返回值
下面是一个介绍改函数用法的函数,这里简述一下:
-a 指定rkaiq文件路径 -I指定摄像头是哪个 -M指定是否使用多个摄像头 -w 宽度 -h高度也描述了他们的默认值
static RK_CHAR optstr[] = "?::a::I:h:w: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 RKAIQprintf("\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");
#elseprintf("\t%s [-I 0]\n", name);
#endifprintf("\t-I | --camid: camera ctx id, Default 0\n");printf("\t-w: DisplayWidth, Default: 720\n");printf("\t-h: DisplayHeight, Default: 1280\n");
}
2.2main函数初始化参数
解释一下getopt_long函数:
传入参数 参数量、参数二级指针、短、长、NULL这是一个标准库函数
- 如果解析到一个短选项或长选项,返回其对应的字符。例如,
-a
或--aiq
返回'a'
。 - 如果遇到未知选项或选项参数缺失,返回
'?'
。 - 如果没有更多的选项可以解析,返回
-1
int main(int argc, char *argv[]) {int ret = 0;
//图片分辨率int video_width = 1920;int video_height = 1080;
//显示分辨率即显示屏的分辨率,根据自己显示屏确定int disp_width = 720;int disp_height = 1280;
//摄像头默认ID RK_S32 s32CamId = 0;
#ifdef RKAIQRK_BOOL bMultictx = RK_FALSE;
#endifint 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 {//没-a指定时候默认iqfile位置iq_file_dir = "/oem/etc/iqfiles";}break;case 'I':s32CamId = atoi(optarg);break;case 'w':disp_width = atoi(optarg);break;case 'h':disp_height = atoi(optarg);break;
#ifdef RKAIQcase 'M':if (atoi(optarg)) {bMultictx = RK_TRUE;}break;
#endifcase '?':default:print_usage(argv[0]);return 0;}}printf("#CameraIdx: %d\n\n", s32CamId);if (iq_file_dir) {
#ifdef RKAIQprintf("#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
2.3输入模块vi初始化
首先初始化了 RK_MPI_SYS_Init();函数
然后就是对vi模块的配置
例如输入通道、通道数、输入宽高、输入像素颜色格式、工作模式,并对输入模块管道进行设置以及使能
注意这里设置的视频流 是0通道,RK_MPI_VI_SetChnAttr的第二个参数
//初始化函数RK_MPI_SYS_Init();//定义 VI 通道属性结构体指针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);//VI管道配置后需要使能ret |= RK_MPI_VI_EnableChn(s32CamId, 0);if (ret) {printf("Create vi[0] failed! ret=%d\n", ret);return -1;}
2.4RGA模块
首先初始化了一个覆盖信息与覆盖位置的结构体,
并设了覆盖颜色信息(argb8888与蓝色)
然后设置了覆盖位置是(0,0)到(100,100),但是初始化的时候吧通道设置为零1,因此我们在测试的时候看不到覆盖区域的蓝色方块,如果把这里的1改为0即保持与上述输入模块的视频流一个通道即可见
最后的一大段是配置了RGA的信息,大家可以主要看一下角度为旋转90°,最后设置了通道0
/* 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//CoverInfo.u32Color = 0xFFFF0000; // blueRngInfo.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 planeRGA_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;}
2.5输出模块vo的初始化
VO_CHN_ATTR_S stVoAttr = {0};// VO[0] for primary planestVoAttr.pcDevNode = "/dev/dri/card0";stVoAttr.emPlaneType = VO_PLANE_PRIMARY;stVoAttr.enImgType = IMAGE_TYPE_RGB888;stVoAttr.u16Zpos = 0;stVoAttr.stImgRect.s32X = 0;stVoAttr.stImgRect.s32Y = 0;stVoAttr.stImgRect.u32Width = disp_width;stVoAttr.stImgRect.u32Height = disp_height;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;}
2.6连接管道
经过上述对vi rga vo模块配置后,并没有把视频流连接起来因此下面我们需要绑定起来各个模块
下面,分别绑定了vi - rga rga-vo这样整体视频流向成功
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__);
2.7判断程序运行
这里类似于qt中的connect函数,signal是c库中函数,表示连接SIGINT与sigterm_handler函数,SIGINT是程序中断的函数,即你按下ctrl+c时候程序中断会触发该信号,sigterm_handler函数也是在2.1中就分析介绍了
signal(SIGINT, sigterm_handler);while (!quit) {usleep(500000);}
2.8退出函数
这里类似于linux总线驱动设备的驱动函数的出口函数一样,先断开了管道,后面销毁了各个模块的chn
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 RKAIQSAMPLE_COMM_ISP_Stop(s32CamId);
#endif}return 0;
}