使用示例:
录像:
rkmedia_vi_get_frame_test -a /etc/iqfiles/ -I 1 -o 1080.nv12
然后用yuvplayer.exe可以播放。
录像10帧:
rkmedia_vi_get_frame_test -a /etc/iqfiles/ -I 1 -o 1080.nv12 -c 10
解析代码:
c
#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"
typedef struct { //用于传递输出参数
char *file_path;
int frame_cnt;
} OutputArgs;
static bool quit = false;
static void sigterm_handler(int sig) { //用于控制程序退出
fprintf(stderr, "signal %d\n", sig);
quit = true;
}
static void *GetMediaBuffer(void *arg) { //获取视频帧数据并保存到文件
OutputArgs *outArgs = (OutputArgs *)arg; //使用`OutputArgs`结构体指针`outArgs`来获取输出参数,其中包括保存路径`save_path`和帧计数`save_cnt`。
char *save_path = outArgs->file_path; //保存路径
int save_cnt = outArgs->frame_cnt; //帧计数
int frame_id = 0;
FILE *save_file = NULL;
if (save_path) {
save_file = fopen(save_path, "w"); //打开保存路径指定的文件
if (!save_file)
printf("ERROR: Open %s failed!\n", save_path);
}
MEDIA_BUFFER mb = NULL;
while (!quit) { //进入循环,当全局变量`quit`为假时持续循环。
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 0, -1); //获取视频帧的媒体缓冲区`mb`,并检查是否获取成功
if (!mb) {
printf("RK_MPI_SYS_GetMediaBuffer get null buffer!\n");
break;
}
//获取媒体缓冲区的图像信息`stImageInfo`,并打印相关信息,如帧指针、文件描述符、大小、时间戳等。
MB_IMAGE_INFO_S stImageInfo = {0};
int ret = RK_MPI_MB_GetImageInfo(mb, &stImageInfo);
if (ret)
printf("Warn: Get image info failed! ret = %d\n", ret);
printf("Get Frame:ptr:%p, fd:%d, size:%zu, mode:%d, channel:%d, "
"timestamp:%lld, ImgInfo:<wxh %dx%d, fmt 0x%x>\n",
RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetFD(mb), RK_MPI_MB_GetSize(mb),
RK_MPI_MB_GetModeID(mb), RK_MPI_MB_GetChannelID(mb),
RK_MPI_MB_GetTimestamp(mb), stImageInfo.u32Width,
stImageInfo.u32Height, stImageInfo.enImgType);
//如果已经打开了保存文件,则将媒体缓冲区中的帧数据写入到文件中,并打印保存信息。
if (save_file) {
fwrite(RK_MPI_MB_GetPtr(mb), 1, RK_MPI_MB_GetSize(mb), save_file);
printf("#Save frame-%d to %s\n", frame_id++, save_path);
}
RK_MPI_MB_ReleaseBuffer(mb); //释放媒体缓冲区。
if (save_cnt > 0) //如果设置了帧计数`save_cnt`且大于0,则递减计数。
save_cnt--;
// exit when complete
if (!save_cnt) { //当完成保存指定数量的帧后,设置全局变量`quit`为真,退出循环。
quit = true;
break;
}
}
if (save_file)
fclose(save_file);
return NULL;
}
static RK_CHAR optstr[] = "?::a::w:h:c:o:d:I:M:";
static const struct option long_options[] = {
{"aiq", optional_argument, NULL, 'a'},
{"device_name", required_argument, NULL, 'd'},
{"width", required_argument, NULL, 'w'},
{"height", required_argument, NULL, 'h'},
{"frame_cnt", required_argument, NULL, 'c'},
{"output", required_argument, NULL, 'o'},
{"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] "
"[-w 1920] "
"[-h 1080]"
"[-c 10] "
"[-d rkispp_scale0] "
"[-o out.nv12] \n",
name);
printf("\t-a | --aiq: enable aiq with dirpath provided, eg:-a "
"/oem/etc/iqfiles/, "
"set dirpath emtpty 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 [-w 1920] "
"[-h 1080] "
"[-c 10] "
"[-d rkispp_scale0] "
"[-I 0] "
"[-o out.nv12] \n",
name);
#endif
printf("\t-w | --width: VI width, Default:1920\n");
printf("\t-h | --heght: VI height, Default:1080\n");
printf(
"\t-d | --device_name: set device node(v4l2), Default:rkispp_scale0\n");
printf("\t-I | --camid: camera ctx id, Default 0\n");
printf("\t-c | --frame_cnt: record frame, Default:-1(unlimit)\n");
printf("\t-o | --output: output path, Default:NULL\n");
printf("Notice: fmt always NV12\n");
}
int main(int argc, char *argv[]) {
RK_U32 u32Width = 1920;
RK_U32 u32Height = 1080;
int frameCnt = -1;
RK_CHAR *pDeviceName = "rkispp_scale0";
RK_CHAR *pOutPath = NULL;
RK_CHAR *pIqfilesPath = NULL;
RK_S32 s32CamId = 0;
#ifdef RKAIQ
RK_BOOL bMultictx = RK_FALSE;
#endif
int c;
int ret = 0;
//使用了 getopt_long 函数对命令行参数进行解析。然后根据解析得到的参数配置相应的采集参数,比如视频宽高、帧率等。
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) {
pIqfilesPath = (char *)tmp_optarg;
} else {
pIqfilesPath = "/oem/etc/iqfiles/";
}
break;
case 'w':
u32Width = atoi(optarg);
break;
case 'h':
u32Height = atoi(optarg);
break;
case 'c':
frameCnt = atoi(optarg);
break;
case 'o':
pOutPath = optarg;
break;
case 'd':
pDeviceName = optarg;
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("#####Device: %s\n", pDeviceName);
printf("#####Resolution: %dx%d\n", u32Width, u32Height);
printf("#####Frame Count to save: %d\n", frameCnt);
printf("#####Output Path: %s\n", pOutPath);
printf("#CameraIdx: %d\n\n", s32CamId);
signal(SIGINT, sigterm_handler);
if (pIqfilesPath) {
#ifdef RKAIQ
printf("#####Aiq xml dirpath: %s\n\n", pIqfilesPath);
printf("#bMultictx: %d\n\n", bMultictx);
// rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_ISP_HDR2; //替换为HDR模式
int fps = 30;
SAMPLE_COMM_ISP_Init(s32CamId, hdr_mode, bMultictx, pIqfilesPath);
SAMPLE_COMM_ISP_Run(s32CamId);
SAMPLE_COMM_ISP_SetFrameRate(s32CamId, fps);
#endif
}
//在相机初始化部分,通过 RK_MPI_VI_SetChnAttr 和 RK_MPI_VI_EnableChn 函数配置并启用了视频输入通道。
RK_MPI_SYS_Init();
VI_CHN_ATTR_S vi_chn_attr;
vi_chn_attr.pcVideoNode = pDeviceName;
vi_chn_attr.u32BufCnt = 3;
vi_chn_attr.u32Width = u32Width;
vi_chn_attr.u32Height = u32Height;
vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;
vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;
vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;
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;
}
printf("%s initial finish\n", __func__);
//在视频采集和处理部分,创建了一个线程用于获取媒体缓冲区,并通过 RK_MPI_VI_StartStream 开始采集。然后通过一个循环来等待退出信号,最后在退出时释放资源。
pthread_t read_thread;
OutputArgs outArgs = {pOutPath, frameCnt};
pthread_create(&read_thread, NULL, GetMediaBuffer, &outArgs); //
ret = RK_MPI_VI_StartStream(s32CamId, 0);
if (ret) {
printf("Start VI[0] failed! ret=%d\n", ret);
return -1;
}
while (!quit) {
usleep(500000);
}
RK_MPI_VI_DisableChn(s32CamId, 0);
#ifdef RKAIQ
if (pIqfilesPath)
SAMPLE_COMM_ISP_Stop(s32CamId);
#endif
printf("%s exit!\n", __func__);
return 0;
}