rkmedia_vi_get_frame_test.c 代码解析

使用示例:

录像:

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;
}
相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
Yana.nice2 小时前
Linux 只保留 30 天内日志(find命令删除日志文件)
linux·运维·chrome
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
DFT计算杂谈6 小时前
无 Root 权限在 Tesla K80 零门槛部署 DeepSeek 大模型
linux·服务器·网络·数据库·机器学习
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai6 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang