[求求大佬]在大核rtt使用窗口合成wbc合成vi画面导致卡死,附上vi vo main代码

Viewed 53

首先vi部分应该是没问题,利用dump保存3个摄像头的帧文件在电脑本地查看正常。
需求合成这三个流在同一个画面 最后是想编码264 给小核心uvc
main代码和卡死log在评论区

vi代码如下


#include "pengzhihao_vi.h"
#include "pengzhihao_log.h"
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

// 输出图像尺寸
#define OUT_WIDTH 960
#define OUT_HEIGHT 540
// 裁剪参数
#define CROP_X 0  // 裁剪窗口宽度(0表示不裁剪)
#define CROP_Y 0  // 裁剪窗口高度(0表示不裁剪)
#define X_START 0 // 裁剪窗口水平起始坐标
#define Y_START 0 // 裁剪窗口垂直起始坐标
// 图像变换参数
#define ROTATION 0 // 旋转角度(0表示不旋转,硬件支持0/90/180/270度)
#define MIRROR 0   // 镜像模式(0表示不镜像,1表示水平/垂直镜像)
#define FPS 30     // 帧率设置(30FPS)
// 数据对齐模式
#define DALIGN 0

//
#define WORK_MODE VICAP_WORK_OFFLINE_MODE

// VICAP 输入缓冲块数量
#define VICAP_INPUT_BUF_NUM 4 // 默认 3 块
// VICAP 输出缓冲块数量
#define VICAP_OUTPUT_BUF_NUM 6 // 默认 5 块,可调整
// GDMA 缓冲块数量,用于旋转等操作
#define GDMA_BUF_NUM 3 // 旋转时额外使用

#define WBC_WIDTH 1920
#define WBC_HEIGHT 1080
#define WBC_BLK_CNT 4

// 初始化 VB(Video Buffer)池,根据设备对象配置各缓冲区
k_s32 sample_vicap_vb_init(vicap_device_obj *dev_obj) {
  k_s32 ret = 0;                            // 返回值
  k_vb_config config;                       // VB 配置结构体
  k_vb_supplement_config supplement_config; // VB 补充配置

  memset(&config, 0, sizeof(config)); // 清零 config
  config.max_pool_cnt = 64;           // 最大池数量

  int k = 0; // 当前 comm_pool 索引
  for (int i = 0; i < VICAP_DEV_ID_MAX; i++) {
    if (!dev_obj[i].dev_enable) // 若设备未使能则跳过
      continue;

    printf("%s, enable dev(%d)\n", __func__, i);

    // 离线或瓦片模式需要输入缓冲
    if ((dev_obj[i].mode == VICAP_WORK_OFFLINE_MODE) ||
        (dev_obj[i].mode == VICAP_WORK_SW_TILE_MODE)) {
      config.comm_pool[k].blk_cnt = VICAP_INPUT_BUF_NUM; // 块数
      config.comm_pool[k].mode = VB_REMAP_MODE_NOCACHE;  // 无缓存模式
      config.comm_pool[k].blk_size = dev_obj[i].in_size; // 块大小
      printf("%s, dev(%d) pool(%d) in_size(%d) blk_cnt(%d)\n", __func__, i, k,
             dev_obj[i].in_size, config.comm_pool[k].blk_cnt);
      k++;
    }

    // 为每个使能的通道配置输出缓冲
    for (int j = 0; j < VICAP_CHN_ID_MAX; j++) {
      if (!dev_obj[i].chn_enable[j]) // 若通道未使能则跳过
        continue;
      printf("%s, enable chn(%d), k(%d)\n", __func__, j, k);
      config.comm_pool[k].blk_cnt = VICAP_OUTPUT_BUF_NUM; // 输出缓冲块数
      config.comm_pool[k].mode = VB_REMAP_MODE_NOCACHE;
      // 若使用 GDMA 旋转,额外分配缓冲
      if (dev_obj[i].preview[j] && dev_obj[i].rotation[j] > 16)
        config.comm_pool[k].blk_cnt += GDMA_BUF_NUM;

      // 根据输出格式和尺寸计算 blk_size
      k_pixel_format pix_format = dev_obj[i].out_format[j];
      k_u16 out_width = dev_obj[i].out_win[j].width;
      k_u16 out_height = dev_obj[i].out_win[j].height;
      k_u16 in_width = dev_obj[i].in_width;
      k_u16 in_height = dev_obj[i].in_height;

      switch (pix_format) {
      case PIXEL_FORMAT_YUV_SEMIPLANAR_420:
        // YUV420SP 每像素 1.5 字节,上对齐 1K
        config.comm_pool[k].blk_size =
            VICAP_ALIGN_UP((out_width * out_height * 3 / 2), VICAP_ALIGN_1K);
        break;
      case PIXEL_FORMAT_RGB_888:
      case PIXEL_FORMAT_RGB_888_PLANAR:
        // RGB888 每像素 3 字节
        config.comm_pool[k].blk_size =
            VICAP_ALIGN_UP((out_width * out_height * 3), VICAP_ALIGN_1K);
        break;
      case PIXEL_FORMAT_RGB_BAYER_10BPP:
        // RAW10 每像素 2 字节
        config.comm_pool[k].blk_size =
            VICAP_ALIGN_UP((in_width * in_height * 2), VICAP_ALIGN_1K);
        break;
      default:
        // 默认回退到 YUV420SP
        dev_obj[i].out_format[j] = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
        config.comm_pool[k].blk_size =
            VICAP_ALIGN_UP((out_width * out_height * 3 / 2), VICAP_ALIGN_1K);
        break;
      }
      // 保存 buf_size
      dev_obj[i].buf_size[j] = config.comm_pool[k].blk_size;
      printf("%s, dev(%d) chn(%d) pool(%d) buf_size(%d) blk_cnt(%d)\n",
             __func__, i, j, k, dev_obj[i].buf_size[j],
             config.comm_pool[k].blk_cnt);
      k++;
    }
    // 去畸变功能需额外缓冲
    if (dev_obj[i].dw_enable) {
      // another buffer for isp->dw
      config.comm_pool[k].blk_size = VICAP_ALIGN_UP(
          (dev_obj[i].in_width * dev_obj[i].in_height * 3 / 2), VICAP_ALIGN_1K);
      config.comm_pool[k].blk_cnt = VICAP_MIN_FRAME_COUNT * 2;
      config.comm_pool[k].mode = VB_REMAP_MODE_NOCACHE;
      dev_obj[i].buf_size[0] = config.comm_pool[k].blk_size;
      printf("%s, dev(%d) pool(%d) buf_size(%d)\n", __func__, i, k,
             dev_obj[i].buf_size[0]);
      k++;
    }
  }

  config.comm_pool[k].blk_cnt = WBC_BLK_CNT;
  config.comm_pool[k].blk_size =
      VICAP_ALIGN_UP(WBC_WIDTH * WBC_HEIGHT * 3 / 2, VICAP_ALIGN_1K);
  config.comm_pool[k].mode = VB_REMAP_MODE_NOCACHE;

  k++;

  // 应用 VB 配置
  ret = kd_mpi_vb_set_config(&config);
  if (ret) {
    printf("vb_set_config failed ret:%d\n", ret);
    return ret;
  }

  memset(&supplement_config, 0, sizeof(supplement_config));
  supplement_config.supplement_config |= VB_SUPPLEMENT_JPEG_MASK;
  // 设置附加 JPEG 支持
  ret = kd_mpi_vb_set_supplement_config(&supplement_config);
  if (ret) {
    printf("vb_set_supplement_config failed ret:%d\n", ret);
    return ret;
  }
  // 初始化 VB
  ret = kd_mpi_vb_init();
  if (ret) {
    printf("vb_init failed ret:%d\n", ret);
    return ret;
  }

  return 0;
}

// 退出 VB
void vb_exit() { kd_mpi_vb_exit(); }

// 读取 RISC-V 时间戳,用于计算耗时
static uint64_t get_ticks() {
  static volatile uint64_t time_elapsed = 0;
  __asm__ __volatile__("rdtime %0" : "=r"(time_elapsed));
  return time_elapsed;
}

static void set_default_device_config(vicap_device_obj *obj, k_u8 dev_id) {
  memset(obj, 0, sizeof(vicap_device_obj));
  k_u8 cur_chn = 0;
  // 默认 AE/AWB 开启,3DNR/HDR 关闭
  obj->dev_num = dev_id;
  obj->dev_enable = K_TRUE;
  obj->ae_enable = K_TRUE;    // default enable ae
  obj->awb_enable = K_TRUE;   // default enable awb
  obj->dnr3_enable = K_FALSE; // default disable 3ndr
  obj->hdr_enable = K_FALSE;  // default disable hdr

  obj->chn_num[cur_chn] = cur_chn;
  obj->chn_enable[cur_chn] = K_TRUE;
  obj->preview[cur_chn] = K_FALSE;
  obj->out_win[cur_chn].h_start = X_START;
  obj->out_win[cur_chn].v_start = Y_START;
  obj->crop_win[cur_chn].width = CROP_X;
  obj->crop_win[cur_chn].height = CROP_Y;
  obj->out_win[cur_chn].width = VICAP_ALIGN_UP(OUT_WIDTH, 16);
  obj->out_win[cur_chn].height = OUT_HEIGHT;
  obj->rotation[cur_chn] = ROTATION;
  obj->sensor_mirror = MIRROR;
  obj->fps[cur_chn] = FPS;
  obj->crop_enable[cur_chn] = K_FALSE;
  obj->out_format[cur_chn] = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
  obj->dalign = DALIGN;
  obj->input_type = VICAP_INPUT_TYPE_SENSOR;
  obj->sensor_type = OV_OV5647_MIPI_CSI1_1920X1080_30FPS_10BIT_LINEAR_V2;
}

void vicap_dev_conf(vicap_device_obj *dev_obj, k_u8 dev_count) {
  if (dev_count > VICAP_DEV_ID_MAX)
    return;

  set_default_device_config(&dev_obj[0], 0);

  if (1 < dev_count) {
    dev_obj[1] = dev_obj[0];
    dev_obj[1].dev_num = 1;
    dev_obj[1].sensor_type =
        OV_OV5647_MIPI_CSI0_1920X1080_30FPS_10BIT_LINEAR_V2;
  }

  if (2 < dev_count) {
    dev_obj[2] = dev_obj[0];
    dev_obj[2].dev_num = 2;
    dev_obj[2].sensor_type = GC2093_MIPI_CSI2_1920X1080_30FPS_10BIT_LINEAR;
    // dev_obj[2].sensor_mirror = VICAP_MIRROR_VER;
    //  dev_obj[2].rotation[cur_chn] = ROTATION;
  }
  PZH_I("------------- vicap_dev_conf-------------------\r\n");
}

k_s32 vicap_devices_init(vicap_device_obj *device_obj,
                         k_vicap_dev_attr *dev_attr) {
  k_s32 ret = 0;
  k_u32 work_mode = WORK_MODE;
  k_u32 pipe_ctrl = 0xFFFFFFFF;
  // 屏幕宽高,显示相关尺寸(用于缓冲 VB 对齐和后续可能的拼接)
  k_u32 display_width = 1920;
  k_u32 display_height = 1080;
  // 宽度向上对齐到 16 的整数倍(硬件要求)
  display_width = VICAP_ALIGN_UP(display_width, 16);

  // 外层循环:遍历所有可能的设备编号(dev_num 从 0 到 VICAP_DEV_ID_MAX-1)
  for (int dev_num = 0; dev_num < VICAP_DEV_ID_MAX; dev_num++) {
    // 如果当前设备未启用,则跳过该设备
    if (!device_obj[dev_num].dev_enable)
      continue;
    // 传感器输入
    if (device_obj[dev_num].input_type == VICAP_INPUT_TYPE_SENSOR) {
      // 设置设备属性中的输入类型为传感器
      dev_attr->input_type = VICAP_INPUT_TYPE_SENSOR;
      // vicap get sensor info
      // 调用 MPI 接口获取指定传感器类型的信息
      ret = kd_mpi_vicap_get_sensor_info(device_obj[dev_num].sensor_type,
                                         &device_obj[dev_num].sensor_info);
      if (ret) {
        printf("sample_vicap, the sensor type not supported!\n");
        return ret;
      }
      // 将获取到的传感器信息复制到 dev_attr 结构体中,供后续设置使用
      memcpy(&dev_attr->sensor_info, &device_obj[dev_num].sensor_info,
             sizeof(k_vicap_sensor_info));
      // 更新设备对象的输入宽高为传感器的实际分辨率
      device_obj[dev_num].in_width = device_obj[dev_num].sensor_info.width;
      device_obj[dev_num].in_height = device_obj[dev_num].sensor_info.height;
    }

    printf("sample_vicap, dev[%d] in size[%dx%d]\n", dev_num,
           device_obj[dev_num].in_width, device_obj[dev_num].in_height);
    // 设置采集窗口(acquisition window)起始位置和大小
    dev_attr->acq_win.h_start = 0;                          // 水平起始偏移为0
    dev_attr->acq_win.v_start = 0;                          // 垂直起始偏移为0
    dev_attr->acq_win.width = device_obj[dev_num].in_width; // 宽度等于输入宽度
    dev_attr->acq_win.height =
        device_obj[dev_num].in_height; // 高度等于输入高度

    // 判断是否为离线模式
    if ((work_mode == VICAP_WORK_OFFLINE_MODE) ||
        (work_mode == VICAP_WORK_LOAD_IMAGE_MODE) ||
        (work_mode == VICAP_WORK_SW_TILE_MODE)) {
      // 设置设备工作模式
      dev_attr->mode = work_mode;
      // 设置输入缓冲区数量
      dev_attr->buffer_num = VICAP_INPUT_BUF_NUM;
      // 根据不同模式计算 buffer_size(缓冲区大小)
      // SW Tile 模式
      if (work_mode == VICAP_WORK_SW_TILE_MODE)
        dev_attr->buffer_size =
            VICAP_ALIGN_UP((device_obj[dev_num].in_width *
                            device_obj[dev_num].in_height * 12 / 8),
                           VICAP_ALIGN_1K);
      else
        // 其他离线模式
        dev_attr->buffer_size = VICAP_ALIGN_UP(
            (device_obj[dev_num].in_width * device_obj[dev_num].in_height * 2),
            VICAP_ALIGN_1K);
      // 保存计算出的输入缓冲区大小到设备对象
      device_obj[dev_num].in_size = dev_attr->buffer_size;

      // 记录当前设备的工作模式
      if (work_mode == VICAP_WORK_SW_TILE_MODE)
        device_obj[dev_num].mode = VICAP_WORK_SW_TILE_MODE;
      else
        device_obj[dev_num].mode = VICAP_WORK_OFFLINE_MODE;

      // 如果是加载图像模式(Load Image),设置图像模式相关参数
      if (work_mode == VICAP_WORK_LOAD_IMAGE_MODE) {
        dev_attr->image_pat = device_obj[dev_num].pattern;
        dev_attr->sensor_info.sensor_name = device_obj[dev_num].calib_file;
        device_obj[dev_num].image_data = NULL;
      }
    } else {
      // 默认为在线模式(实时从摄像头采集)
      dev_attr->mode = VICAP_WORK_ONLINE_MODE;
    }

    // Pipe 控制参数
    dev_attr->pipe_ctrl.data = pipe_ctrl; // 默认值
                                          //  关闭自动对焦(AF)
    dev_attr->pipe_ctrl.bits.af_enable = 0;
    // 根据配置决定是否启用自动曝光(AE)
    dev_attr->pipe_ctrl.bits.ae_enable = device_obj[dev_num].ae_enable;
    // 根据配置决定是否启用自动白平衡(AWB)
    dev_attr->pipe_ctrl.bits.awb_enable = device_obj[dev_num].awb_enable;

    // 在 SW_TILE 模式下强制开启 3D 降噪(DNR3)
    if (work_mode == VICAP_WORK_SW_TILE_MODE)
      dev_attr->pipe_ctrl.bits.dnr3_enable = 1;
    else
      // 否则根据配置决定是否开启 DNR3
      dev_attr->pipe_ctrl.bits.dnr3_enable = device_obj[dev_num].dnr3_enable;

    // 根据配置决定是否启用 HDR(多帧合成高动态范围)
    dev_attr->pipe_ctrl.bits.ahdr_enable = device_obj[dev_num].hdr_enable;

    // 设置捕获帧数(0 表示连续捕获)
    dev_attr->cpature_frame = 0;

    // 根据配置决定是否启用
    dev_attr->dw_enable = device_obj[dev_num].dw_enable;
    // 设置镜像(mirror)属性
    dev_attr->mirror = device_obj[dev_num].sensor_mirror;

    // 设置采集模式(在线/离线)、自动曝光、木马白平衡等参数, 应用设备属性
    // 调用 MPI 接口设置设备属性
    ret = kd_mpi_vicap_set_dev_attr(dev_num, *dev_attr);
    if (ret) {
      printf("sample_vicap, kd_mpi_vicap_set_dev_attr failed.\n");
      return ret;
    }

    if (work_mode == VICAP_WORK_LOAD_IMAGE_MODE) {
      printf("work_mode error\r\n");
    }
    // 内层循环:遍历该设备的所有通道(Channel,通常一个设备可支持多个输出流)
    for (int chn_num = 0; chn_num < VICAP_CHN_ID_MAX; chn_num++) {
      if (!device_obj[dev_num].chn_enable[chn_num])
        continue; // 如果当前通道未启用,则跳过

      // set default value
      // 如果未设置输出像素格式,则使用默认格式:YUV 半平面 420(I420/NV12)
      if (!device_obj[dev_num].out_format[chn_num]) {
        device_obj[dev_num].out_format[chn_num] =
            PIXEL_FORMAT_YUV_SEMIPLANAR_420;
      }

      // 如果未设置输出宽度,则默认与输入宽度一致
      if (!device_obj[dev_num].out_win[chn_num].width) {
        device_obj[dev_num].out_win[chn_num].width =
            device_obj[dev_num].in_width;
      }

      // 如果未设置输出高度,则默认与输入高度一致
      if (!device_obj[dev_num].out_win[chn_num].height) {
        device_obj[dev_num].out_win[chn_num].height =
            device_obj[dev_num].in_height;
      }

      // 如果输出窗口有水平或垂直偏移,则认为需要裁剪(Crop)
      if (device_obj[dev_num].out_win[chn_num].h_start ||
          device_obj[dev_num].out_win[chn_num].v_start) {
        device_obj[dev_num].crop_enable[chn_num] = K_TRUE;
      }

      // 如果输出分辨率大于显示区域,则不用于预览(preview)
      if ((device_obj[dev_num].out_win[chn_num].width > display_width) &&
          (device_obj[dev_num].out_win[chn_num].height > display_height)) {
        device_obj[dev_num].preview[chn_num] = K_FALSE;
      }

      // 如果未设置旋转,且输出宽度 > 显示宽 但 < 显示高,自动设置旋转
      if (!device_obj[dev_num].rotation[chn_num] &&
          ((device_obj[dev_num].out_win[chn_num].width > display_width) &&
           (device_obj[dev_num].out_win[chn_num].width < display_height))) {
        device_obj[dev_num].rotation[chn_num] = 1;
      }
      // 打印每个通道的详细配置信息(调试用)
      printf("sample_vicap, dev_num(%d), chn_num(%d), in_size[%dx%d], "
             "out_offset[%d:%d], out_size[%dx%d]\n",
             dev_num, chn_num, device_obj[dev_num].in_width,
             device_obj[dev_num].in_height,
             device_obj[dev_num].out_win[chn_num].h_start,
             device_obj[dev_num].out_win[chn_num].v_start,
             device_obj[dev_num].out_win[chn_num].width,
             device_obj[dev_num].out_win[chn_num].height);
    }
  }
  PZH_I("------------- vicap_devices_init-----------------\r\n");

  return ret;
}

k_s32 vicap_channels_init(vicap_device_obj *device_obj) {
  k_s32 ret = 0;
  k_vicap_chn_attr chn_attr;
  // 外层循环:遍历所有可能的VICAP设备(从0到VICAP_DEV_ID_MAX-1)
  for (int dev_num = 0; dev_num < VICAP_DEV_ID_MAX; dev_num++) {
    // 如果当前设备未启用,则跳过该设备
    if (!device_obj[dev_num].dev_enable)
      continue; // 跳过未启用的设备

    // 内层循环:遍历当前设备(dev_num)的所有通道(从0到VICAP_CHN_ID_MAX-1)
    for (int chn_num = 0; chn_num < VICAP_CHN_ID_MAX; chn_num++) {
      if (!device_obj[dev_num].chn_enable[chn_num])
        continue; // 跳过未启用的通道

      kd_mpi_vicap_set_dump_reserved(dev_num, chn_num, K_TRUE);
      // 通道属性初始化
      memset(&chn_attr, 0, sizeof(k_vicap_chn_attr));
      if (device_obj[dev_num].out_format[chn_num] ==
          PIXEL_FORMAT_RGB_BAYER_10BPP) {
        chn_attr.out_win.width = device_obj[dev_num].in_width;
        chn_attr.out_win.height = device_obj[dev_num].in_height;
      } else {
        chn_attr.out_win.width = device_obj[dev_num].out_win[chn_num].width;
        chn_attr.out_win.height = device_obj[dev_num].out_win[chn_num].height;
      }

      if (device_obj[dev_num].crop_enable[chn_num]) {
        chn_attr.crop_win.width = device_obj[dev_num]
                                      .crop_win[chn_num]
                                      .width; // chn_attr.out_win;1166;//
        chn_attr.crop_win.height =
            device_obj[dev_num].crop_win[chn_num].height; // 1944;//
        chn_attr.crop_win.h_start =
            device_obj[dev_num].out_win[chn_num].h_start; // 713;
        chn_attr.crop_win.v_start =
            device_obj[dev_num].out_win[chn_num].v_start; // 0;//
      } else {
        chn_attr.crop_win.width = device_obj[dev_num].in_width;
        chn_attr.crop_win.height = device_obj[dev_num].in_height;
      }

      chn_attr.scale_win = chn_attr.out_win;
      chn_attr.crop_enable = device_obj[dev_num].crop_enable[chn_num];
      chn_attr.scale_enable = K_FALSE;
      chn_attr.chn_enable = K_TRUE;

      chn_attr.pix_format = device_obj[dev_num].out_format[chn_num];
      chn_attr.buffer_num = VICAP_OUTPUT_BUF_NUM;
      chn_attr.buffer_size = device_obj[dev_num].buf_size[chn_num];
      chn_attr.fps = device_obj[dev_num].fps[chn_num];

      printf("sample_vicap, set dev(%d) chn(%d) attr, buffer_size(%d), out "
             "size[%dx%d]\n",
             dev_num, chn_num, chn_attr.buffer_size, chn_attr.out_win.width,
             chn_attr.out_win.height);

      printf("sample_vicap out_win h_start is %d ,v_start is %d \n",
             chn_attr.out_win.h_start, chn_attr.out_win.v_start);

      ret = kd_mpi_vicap_set_chn_attr(dev_num, chn_num, chn_attr);
      if (ret) {
        printf("sample_vicap, kd_mpi_vicap_set_chn_attr failed.\n");
        return ret;
      }
    }
  }
  PZH_I("------------ vicap_channels_init-----------\r\n");
  return ret;
}

k_s32 vicap_mpi_init(vicap_device_obj *device_obj) {
  k_s32 ret = 0;
  for (int dev_num = 0; dev_num < VICAP_DEV_ID_MAX; dev_num++) {
    if (!device_obj[dev_num].dev_enable)
      continue;
    printf("sample_vicap, vicap dev(%d) init\n", dev_num);
    ret = kd_mpi_vicap_init(dev_num);
    if (ret) {
      printf("sample_vicap, vicap dev(%d) init failed.\n", dev_num);
      return ret;
    }
  }
  PZH_I("------------- vicap_mpi_init-----------------\r\n");

  return ret;
}

k_s32 vicap_start_stream(vicap_device_obj *device_obj) {
  k_s32 ret = 0;
  for (int dev_num = 0; dev_num < VICAP_DEV_ID_MAX; dev_num++) {
    if (!device_obj[dev_num].dev_enable)
      continue;

    printf("sample_vicap, vicap dev(%d) start stream\n", dev_num);
    ret = kd_mpi_vicap_start_stream(dev_num);
    if (ret) {
      printf("sample_vicap, vicap dev(%d) start stream failed.\n", dev_num);
      return ret;
    }
  }
  PZH_I("------------- vicap_start_stream-----------------\r\n");

  return ret;
}

void vicap_dump_frame(vicap_device_obj *device_obj) {
  // 已配置设备计数、当前设备
  k_video_frame_info dump_info;
  k_s32 ret = 0;
  static k_u32 dump_count = 0;

  for (int dev_num = 0; dev_num < VICAP_DEV_ID_MAX; dev_num++) {
    if (!device_obj[dev_num].dev_enable)
      continue;

    for (int chn_num = 0; chn_num < VICAP_CHN_ID_MAX; chn_num++) {
      if (!device_obj[dev_num].chn_enable[chn_num])
        continue;

      printf("sample_vicap, dev(%d) chn(%d) dump frame.\n", dev_num, chn_num);

      memset(&dump_info, 0, sizeof(k_video_frame_info));
      uint64_t start = get_ticks();
      ret = kd_mpi_vicap_dump_frame(dev_num, chn_num, VICAP_DUMP_YUV,
                                    &dump_info, 1000);
      if (ret) {
        printf("sample_vicap, dev(%d) chn(%d) dump frame failed.\n", dev_num,
               chn_num);
        continue;
      }
      uint64_t end = get_ticks();
      printf("dump cost %lu us\n", (end - start) / 27);

      k_char *suffix;
      k_u32 data_size = 0;
      k_u8 lbit = 0;
      k_u8 *virt_addr = NULL;
      k_char filename[256];

      if (dump_info.v_frame.pixel_format == PIXEL_FORMAT_YUV_SEMIPLANAR_420) {
        suffix = "yuv420sp";
        data_size = dump_info.v_frame.width * dump_info.v_frame.height * 3 / 2;
      } else if (dump_info.v_frame.pixel_format ==
                 PIXEL_FORMAT_YUV_SEMIPLANAR_422) {
        suffix = "yuv422sp";
        data_size = dump_info.v_frame.width * dump_info.v_frame.height * 3 / 2;
      } else if (dump_info.v_frame.pixel_format == PIXEL_FORMAT_RGB_888) {
        suffix = "rgb888";
        data_size = dump_info.v_frame.width * dump_info.v_frame.height * 3;
      } else if (dump_info.v_frame.pixel_format ==
                 PIXEL_FORMAT_RGB_888_PLANAR) {
        suffix = "rgb888p";
        data_size = dump_info.v_frame.width * dump_info.v_frame.height * 3;
      } else if (dump_info.v_frame.pixel_format ==
                 PIXEL_FORMAT_RGB_BAYER_10BPP) {
        suffix = "raw10";
        lbit = 6;
        data_size = dump_info.v_frame.width * dump_info.v_frame.height * 2;
      } else if (dump_info.v_frame.pixel_format ==
                 PIXEL_FORMAT_RGB_BAYER_12BPP) {
        suffix = "raw12";
        lbit = 4;
        data_size = dump_info.v_frame.width * dump_info.v_frame.height * 2;
      } else if (dump_info.v_frame.pixel_format ==
                 PIXEL_FORMAT_RGB_BAYER_14BPP) {
        suffix = "raw14";
        lbit = 2;
        data_size = dump_info.v_frame.width * dump_info.v_frame.height * 2;
      } else if (dump_info.v_frame.pixel_format ==
                 PIXEL_FORMAT_RGB_BAYER_16BPP) {
        suffix = "raw16";
        data_size = dump_info.v_frame.width * dump_info.v_frame.height * 2;
      } else {
        suffix = "unkown";
      }

      virt_addr = kd_mpi_sys_mmap(dump_info.v_frame.phys_addr[0], data_size);
      if (virt_addr) {
        memset(filename, 0, sizeof(filename));

        snprintf(filename, sizeof(filename), "dev_%02d_chn_%02d_%dx%d_%04d.%s",
                 dev_num, chn_num, dump_info.v_frame.width,
                 dump_info.v_frame.height, dump_count, suffix);

        printf("save dump data to file(%s)\n", filename);
        FILE *file = fopen(filename, "wb+");
        if (file) {
          if (device_obj[dev_num].dalign && lbit) {
            for (k_u32 index = 0; index < data_size; index += 2) {
              k_u16 raw_data = (virt_addr[index + 1] << 8) | virt_addr[index];
              raw_data = raw_data << lbit;
              fwrite(&raw_data, sizeof(raw_data), 1, file);
            }
          } else {
            fwrite(virt_addr, 1, data_size, file);
          }
          fclose(file);
        } else {
          printf("sample_vicap, open dump file failed(%s)\n", strerror(errno));
        }

        kd_mpi_sys_munmap(virt_addr, data_size);
      } else {
        printf("sample_vicap, map dump addr failed.\n");
      }

      printf("sample_vicap, release dev(%d) chn(%d) dump frame.\n", dev_num,
             chn_num);

      ret = kd_mpi_vicap_dump_release(dev_num, chn_num, &dump_info);
      if (ret) {
        printf("sample_vicap, dev(%d) chn(%d) release dump frame failed.\n",
               dev_num, chn_num);
      }
      dump_count++;
    }
  }

  PZH_I("------------- vicap_dump_frame-----------------\r\n");
}

void slave_en(void) {
  // 开启多摄像头同步模式,多摄同步标志(1表示启用多摄同步,0表示禁用)
  k_u8 salve_en = SLAVE_EN;
  if (salve_en == SLAVE_EN) {
    k_vicap_slave_info slave_info;

    memset(&slave_info, 0, sizeof(k_vicap_slave_info));
    slave_info.vs_high =
        10; // 1 / 27 = 37ns // VSYNC 高电平脉宽,单位 = 1/27 MHz ≈ 37 ns
    slave_info.vs_cycle = 900902; // 33ms / 37ns = 900902  // 一个 VSYNC 周期,≈
                                  // 900 902×37 ns ≈ 33 ms → 30 fps
    kd_mpi_vicap_set_slave_attr(VICAP_SLAVE_ID1, &slave_info);

    k_vicap_slave_enable slave_en;

    memset(&slave_en, 0, sizeof(k_vicap_slave_enable));
    slave_en.vs_enable = 1; // 只开 VSYNC,同步行 (hs) 关闭
    slave_en.hs_enable = 0;
    kd_mpi_vicap_set_slave_enable(VICAP_SLAVE_ID1, &slave_en);
  }
}

void app_exit(vicap_device_obj *device_obj) {
  k_s32 ret = 0;
  k_u8 salve_en = SLAVE_EN;
  // 记录是否已经启动过 GDMA(图像旋转等),GDMA
  // 是否已启动(当前未启用旋转,故为0)
  k_u8 gdma_enable = 0;
  if (salve_en == SLAVE_EN) {
    k_vicap_slave_enable slave_en;

    memset(&slave_en, 0, sizeof(k_vicap_slave_enable));
    slave_en.vs_enable = 0;
    slave_en.hs_enable = 0;
    kd_mpi_vicap_set_slave_enable(VICAP_SLAVE_ID1, &slave_en);
  }

  for (int dev_num = 0; dev_num < VICAP_DEV_ID_MAX; dev_num++) {
    if (!device_obj[dev_num].dev_enable)
      continue;

    printf("sample_vicap, vicap dev(%d) stop stream\n", dev_num);
    ret = kd_mpi_vicap_stop_stream(dev_num);
    if (ret) {
      printf("sample_vicap, vicap dev(%d) stop stream failed.\n", dev_num);
    }

    printf("sample_vicap, vicap dev(%d) deinit\n", dev_num);
    ret = kd_mpi_vicap_deinit(dev_num);
    if (ret) {
      printf("sample_vicap, vicap dev(%d) deinit failed.\n", dev_num);
    }

#if (WORK_MODE != VICAP_WORK_OFFLINE_MODE)
    if (work_mode == VICAP_WORK_LOAD_IMAGE_MODE) {
      if (device_obj[dev_num].image_data != NULL) {
        free(device_obj[dev_num].image_data);
        device_obj[dev_num].image_data = NULL;
      }
    }
#endif
  }

  if (gdma_enable) {
    ret = kd_mpi_dma_stop_dev();
    if (ret != K_SUCCESS) {
      printf("stop dev error\r\n");
    }
  }

  printf("Press Enter to exit!!!!\n");
  getchar();

  /*Allow one frame time for the VO to release the VB block*/
  k_u32 display_ms = 1000 / 33;
  usleep(1000 * display_ms);
}

注意客户不需要屏幕,所以不用屏幕相关功能
下面是vo部分,这里开始就有可能有问题,麻烦大佬看看

#include "pengzhihao_vo.h"

#include "k_sys_comm.h"

#include "k_vb_comm.h"
#include "k_video_comm.h"

#include "mpi_sys_api.h"
#include "mpi_vb_api.h"
#include "mpi_vicap_api.h"
#include "mpi_vo_api.h"
#include "mpi_vvi_api.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/* ---------- 常量 ---------- */
#define MOSAIC_W 1920 /* 最终合成帧大小 */
#define MOSAIC_H 1080

typedef struct {
  k_u64 osd_phy_addr;
  void *osd_virt_addr;
  k_pixel_format format;
  k_vo_point offset;
  k_vo_size act_size;
  k_u32 size;
  k_u32 stride;
  k_u8 global_alptha;
} osd_info;

typedef struct {
  k_u64 layer_phy_addr;
  k_pixel_format format;
  k_vo_point offset;
  k_vo_size act_size;
  k_u32 size;
  k_u32 stride;
  k_u8 global_alptha;

  // only layer0、layer1
  k_u32 func;
  // only layer0
  k_vo_scaler_attr attr;

} layer_info;

// 绑定 VICAP 通道到 VO 通道
void sample_vicap_bind_vo(k_s32 vicap_dev, k_s32 vicap_chn, k_s32 vo_chn) {

  k_s32 ret;

  k_mpp_chn vicap_mpp_chn, vo_mpp_chn;
  vicap_mpp_chn.mod_id = K_ID_VI;   // VI 模块
  vicap_mpp_chn.dev_id = vicap_dev; // 设备 ID
  vicap_mpp_chn.chn_id = vicap_chn; // 通道 ID

  vo_mpp_chn.mod_id = K_ID_VO;             // VO 模块
  vo_mpp_chn.dev_id = K_VO_DISPLAY_DEV_ID; // VO 设备 ID
  vo_mpp_chn.chn_id = vo_chn;              // VO 通道 ID

  // 绑定
  ret = kd_mpi_sys_bind(&vicap_mpp_chn, &vo_mpp_chn);
  if (ret) {
    printf("kd_mpi_sys_unbind failed:0x%x\n", ret);
  }

  return;
}

// 解除绑定
void sample_vicap_unbind_vo(k_s32 vicap_dev, k_s32 vicap_chn, k_s32 vo_chn) {
  k_s32 ret;

  k_mpp_chn vicap_mpp_chn, vo_mpp_chn;

  vicap_mpp_chn.mod_id = K_ID_VI;
  vicap_mpp_chn.dev_id = vicap_dev;
  vicap_mpp_chn.chn_id = vicap_chn;

  vo_mpp_chn.mod_id = K_ID_VO;
  vo_mpp_chn.dev_id = K_VO_DISPLAY_DEV_ID;
  vo_mpp_chn.chn_id = vo_chn;
  // 解绑
  ret = kd_mpi_sys_unbind(&vicap_mpp_chn, &vo_mpp_chn);
  if (ret) {
    printf("kd_mpi_sys_unbind failed:0x%x\n", ret);
  }

  return;
}

// 函数:创建视频图层测试并配置属性
int vo_creat_layer_test(k_vo_layer chn_id, layer_info *info) {
  k_vo_video_layer_attr attr; // 视频图层属性结构体

  // check layer
  // 校验图层ID合法性
  // 条件:图层ID超出最大数量,或(启用缩放且非layer0),或(layer2且有功能配置)
  if ((chn_id >= K_MAX_VO_LAYER_NUM) ||
      ((info->func & K_VO_SCALER_ENABLE) && (chn_id != K_VO_LAYER0)) ||
      ((info->func != 0) && (chn_id == K_VO_LAYER2))) {
    printf("input layer num failed \n");
    return -1;
  }

  /* 新写法:NV12 */
  if (info->format != PIXEL_FORMAT_YUV_SEMIPLANAR_420) {
    printf("unsupported pixel format (%d)\n", info->format);
    return -1;
  }

  // 初始化图层属性结构体
  memset(&attr, 0, sizeof(attr));

  // set offset  // 设置显示偏移(位置)
  attr.display_rect = info->offset;
  // set act // 设置图像大小(实际分辨率)
  attr.img_size = info->act_size;
  // sget size// 计算图像数据大小(YUV420格式:宽×高×1.5)
  info->size = info->act_size.height * info->act_size.width * 3 / 2;
  // set pixel format // 设置像素格式
  attr.pixel_format = info->format;

  //   if (info->format != PIXEL_FORMAT_YVU_PLANAR_420) {
  //     printf("input pix format failed \n");
  //     return -1;
  //   }
  // set stride // 设置步长(自定义计算方式)
  attr.stride =
      (info->act_size.width / 8 - 1) + ((info->act_size.height - 1) << 16);
  // set function// 设置功能标志(如缩放、旋转等)
  attr.func = info->func;
  // set scaler attr // 设置缩放属性
  attr.scaler_attr = info->attr;
  // 应用视频图层属性配置
  // set video layer atrr
  kd_mpi_vo_set_video_layer_attr(chn_id, &attr);

  // enable layer // 使能视频图层
  kd_mpi_vo_enable_video_layer(chn_id);

  return 0;
}

// 函数:创建OSD测试图层并配置属性
k_u32 vo_creat_osd_test(k_vo_osd osd, osd_info *info) {
  k_vo_video_osd_attr attr; // OSD属性结构体

  // set attr 设置OSD全局透明度
  attr.global_alptha = info->global_alptha;
  // 根据像素格式计算OSD大小和步长(stride)
  if (info->format == PIXEL_FORMAT_ABGR_8888 ||
      info->format == PIXEL_FORMAT_ARGB_8888) {
    info->size = info->act_size.width * info->act_size.height * 4;
    info->stride = info->act_size.width * 4 / 8;
  } else if (info->format == PIXEL_FORMAT_RGB_565 ||
             info->format == PIXEL_FORMAT_BGR_565) {
    info->size = info->act_size.width * info->act_size.height * 2;
    info->stride = info->act_size.width * 2 / 8;
  } else if (info->format == PIXEL_FORMAT_RGB_888 ||
             info->format == PIXEL_FORMAT_BGR_888) {
    info->size = info->act_size.width * info->act_size.height * 3;
    info->stride = info->act_size.width * 3 / 8;
  } else if (info->format == PIXEL_FORMAT_ARGB_4444 ||
             info->format == PIXEL_FORMAT_ABGR_4444) {
    info->size = info->act_size.width * info->act_size.height * 2;
    info->stride = info->act_size.width * 2 / 8;
  } else if (info->format == PIXEL_FORMAT_ARGB_1555 ||
             info->format == PIXEL_FORMAT_ABGR_1555) {
    info->size = info->act_size.width * info->act_size.height * 2;
    info->stride = info->act_size.width * 2 / 8;
  } else {
    printf("set osd pixel format failed  \n");
  }
  // 配置OSD属性
  attr.stride = info->stride;       // 步长
  attr.pixel_format = info->format; // 像素格式
  attr.display_rect = info->offset; // 显示位置偏移
  attr.img_size = info->act_size;   // 图像大小
                                    // 应用OSD属性配置
  kd_mpi_vo_set_video_osd_attr(osd, &attr);
  // 使能OSD图层
  kd_mpi_vo_osd_enable(osd);

  return 0;
}

// 配置 VO 层:根据屏幕分辨率和每层需求,计算位置、大小并创建 VO 层
// 初始化 VO 层布局:基于 display 宽高和各层尺寸自动计算位置
k_s32 sample_vicap_vo_layer_init(k_vicap_vo_layer_conf *layer_conf,
                                 k_u32 display_width, k_u32 display_height) {

  k_s32 ret = 0;
  layer_info info[K_MAX_VO_LAYER_NUM];
  k_u16 margin = 0;          // 层间间隔
  k_u16 rotation = 0;        // 临时旋转值
  k_u16 relative_height = 0; // 已布局高度
  k_u16 total_height = 0;    // 累积高度
  osd_info osd_info;

  memset(&info, 0, sizeof(info));
  memset(&osd_info, 0, sizeof(osd_info));

  // 计算每一层输出的实际尺寸和旋转模式
  for (int i = 0; i < K_MAX_VO_LAYER_NUM; i++) {
    // 仅处理使能的层
    if (layer_conf->enable[i]) {
      // 处理旋转
      rotation = layer_conf->rotation[i];
      switch (rotation) {
      case 0:
        info[i].act_size.width = layer_conf->width[i];
        info[i].act_size.height = layer_conf->height[i];
        info[i].func = K_ROTATION_0;
        break;
      case 1: // 90° 旋转:宽高互换
        info[i].act_size.width = layer_conf->height[i];
        info[i].act_size.height = layer_conf->width[i];
        info[i].func = K_ROTATION_90;
        break;
      case 2:
        info[i].act_size.width = layer_conf->width[i];
        info[i].act_size.height = layer_conf->height[i];
        info[i].func = K_ROTATION_180;
        break;
      case 3:
        info[i].act_size.width = layer_conf->height[i];
        info[i].act_size.height = layer_conf->width[i];
        info[i].func = K_ROTATION_270;
        break;
      case 4: // 硬件不支持,作为占位
        info[i].act_size.width = layer_conf->width[i];
        info[i].act_size.height = layer_conf->height[i];
        info[i].func = 0;
        break;
      default:
        printf("invalid roation paramters.\n");
        return -1;
      }
      total_height += info[i].act_size.height;
      // 均分余下高度为边距
      margin = ((display_height - total_height) / (i + 2));
      // 检查尺寸是否超出
      if ((total_height > display_height) ||
          (info[i].act_size.width > display_width)) {
        printf("%s, the preview window size[%dx%d] exceeds the display window "
               "size[%dx%d].\n",
               __func__, info[i].act_size.width, total_height, display_width,
               display_height);
        return -1;
      }
      printf("%s, width(%d), height(%d), margin(%d), total_height(%d)\n",
             __func__, info[i].act_size.width, info[i].act_size.height, margin,
             total_height);
    }
  }

  // 设置每层的偏移和属性并创建 VO 层
  for (int i = 0; i < K_MAX_VO_LAYER_NUM - 1; i++) {
    if (layer_conf->enable[i]) {
      // 居中水平偏移
      info[i].offset.x = (display_width - info[i].act_size.width) / 2;
      // 纵向累积 + 边距
      info[i].offset.y = margin + relative_height;
      printf("%s, layer(%d), offset.x(%d), offset.y(%d), relative_height(%d)\n",
             __func__, layer_conf->layer[i], info[i].offset.x, info[i].offset.y,
             relative_height);
      relative_height += info[i].act_size.height + margin;

      info[i].format = PIXEL_FORMAT_YVU_PLANAR_420; // YUV420 默认格式
      info[i].global_alptha = 0xff;                 // 不透明
                                                    // 创建视频层
      vo_creat_layer_test(layer_conf->layer[i], &info[i]);
    }
  }

  // osd enable  // 如果第三层(OSD)使能,则创建 OSD,创建(显示文字/时间)
  if (layer_conf->enable[2]) {
    osd_info.act_size.width = layer_conf->width[2];
    ;
    osd_info.act_size.height = layer_conf->height[2];
    ;
    osd_info.offset.x = (display_width - layer_conf->width[2]) / 2;
    ;
    osd_info.offset.y = margin + relative_height;
    osd_info.global_alptha = 0xff;
    // OSD 使用 RGB888
    osd_info.format =
        PIXEL_FORMAT_RGB_888; // PIXEL_FORMAT_ARGB_4444;
                              // //PIXEL_FORMAT_ARGB_1555;//PIXEL_FORMAT_ARGB_8888;

    vo_creat_osd_test(layer_conf->layer[2], &osd_info);
  }
  return ret;
}

void vo_en(void) {

  k_vo_pub_attr pub = {
      .bg_color = 0x000000,          /* 黑底 */
      .intf_sync = K_VO_OUT_1080P30, /* 或 30,根据你 VI FPS */
  };
  kd_mpi_vo_init();
  kd_mpi_vo_set_dev_param(&pub);
  kd_mpi_vo_enable();
}

void disable_vo_layer(void) { kd_mpi_vo_disable_video_layer(K_VO_LAYER0); }

/* === 四宫格固定布局:3 路摄像头 + 1 留空 === */
void vicap_vo_layer_init(void) {
  /* layer 顺序固定:Cam0→layer0、Cam1→layer1、Cam2→layer2 */
  const k_vo_layer layers[3] = {K_VO_LAYER0, K_VO_LAYER1, K_VO_LAYER2};
  /* 每格在 1920×1080 里的左上角坐标 */
  const k_vo_point offsets[3] = {{0, 0}, {960, 0}, {0, 540}};

  layer_info info;
  memset(&info, 0, sizeof(info));

  /* 公共参数 */
  info.act_size.width = 960;
  info.act_size.height = 540;
  info.format = PIXEL_FORMAT_YUV_SEMIPLANAR_420; /* VI 默认出 NV12/YUV420 */
  info.global_alptha = 0xff;

  for (int i = 0; i < 3; ++i) {
    info.offset = offsets[i];
    info.func = 0;
    vo_creat_layer_test(layers[i], &info); /* 设置属性并 enable layer */
    sample_vicap_bind_vo(i, 0, layers[i]);
  }
}

/*  配置并打开 WBC */
int wbc_enable(void) {
  k_vo_wbc_attr attr;
  memset(&attr, 0, sizeof(attr));
  attr.target_size.width = MOSAIC_W;  /* 1920 */
  attr.target_size.height = MOSAIC_H; /* 1080 */
  attr.pixel_format = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
  attr.stride = (MOSAIC_W / 8 - 1) + ((MOSAIC_H - 1) << 16);
  attr.stride = 0;     /*  0 = 交给驱动自己算 */
  attr.y_phy_addr = 0; /* 驱动内部分 VB */
  kd_mpi_vo_set_wbc_attr(&attr);
  return kd_mpi_vo_enable_wbc();
}

/*  取帧示例(阻塞 1000 ms)*/
void *wbc_dump_task(void *arg) {
  k_video_frame_info vf_info;
  while (1) {
    if (!kd_mpi_wbc_dump_frame(&vf_info, 1000)) { /* 成功拿到混合帧 */
      /* TODO: 这里把 vf 直接写进 USB-UVC(V4L2)队列即可 */
      kd_mpi_wbc_dump_release(&vf_info); /* 用完务必释放 */
    }
  }
  return NULL;
}

int save_nv12_frame(const k_video_frame_info *vf, const char *filepath) {
  const k_video_frame *frm = &vf->v_frame;
  int width = frm->width;
  int height = frm->height;

  /* 1. 把物理地址映射到虚拟地址(仅当前帧生效) */
  void *vaddr_y = kd_mpi_sys_mmap(frm->phys_addr[0], frm->stride[0] * height);
  void *vaddr_uv =
      kd_mpi_sys_mmap(frm->phys_addr[1], frm->stride[1] * height / 2);
  if (!vaddr_y || !vaddr_uv) {
    printf("mmap failed\n");
    return -1;
  }

  /* 2. 逐行拷贝到本地缓冲,去掉 stride 填充 */
  size_t frame_size = width * height * 3 / 2;
  k_u8 *buf = (k_u8 *)malloc(frame_size);
  if (!buf) {
    printf("malloc failed\n");
    goto unmap;
  }

  /* --- Y --- */
  for (int h = 0; h < height; ++h) {
    memcpy(buf + h * width, (k_u8 *)vaddr_y + h * frm->stride[0], width);
  }
  /* --- UV --- */
  k_u8 *dst_uv = buf + width * height;
  for (int h = 0; h < height / 2; ++h) {
    memcpy(dst_uv + h * width, (k_u8 *)vaddr_uv + h * frm->stride[1], width);
  }

  /* 3. 写文件 */
  int fd = open(filepath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
  if (fd < 0) {
    printf("open %s failed\n", filepath);
    goto free_buf;
  }
  if (write(fd, buf, frame_size) != (ssize_t)frame_size) {
    printf("write failed\n");
  }
  close(fd);
  printf("save frame ok → %s (%zu bytes)\n", filepath, frame_size);

free_buf:
  free(buf);
unmap:
  kd_mpi_sys_munmap(vaddr_y, frm->stride[0] * height);
  kd_mpi_sys_munmap(vaddr_uv, frm->stride[1] * height / 2);
  return 0;
}
3 Answers

main

#include "mpi_vo_api.h"
#include "pengzhihao_log.h"
#include "pengzhihao_vi.h"
#include "pengzhihao_vo.h"
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

int main(void) {

  k_s32 ret = 0;
  // 设备属性
  k_vicap_dev_attr dev_attr;

  // 清零 dev_attr
  memset(&dev_attr, 0, sizeof(k_vicap_dev_attr));

  // 存放每路摄像头的配置,最多 VICAP_DEV_ID_MAX 路
  vicap_device_obj device_obj[VICAP_DEV_ID_MAX];
  vicap_dev_conf(device_obj, VICAP_DEV_ID_MAX);

  ret = vicap_devices_init(device_obj, &dev_attr);
  if (ret) {
    PZH_E("--------- vicap_devices_init failed---------\n");
    return -1;
  }

  // 初始化 VB
  ret = sample_vicap_vb_init(device_obj);
  if (ret) {
    printf(" sample_vicap_vb_init failed\n");
    return -1;
  }
  atexit(vb_exit);

  ret = vicap_channels_init(device_obj);
  if (ret) {
    goto vb_exit;
  }

  ret = vicap_mpi_init(device_obj);
  if (ret) {
    goto app_exit;
  }

  ret = vicap_start_stream(device_obj);
  if (ret) {
    goto app_exit;
  }

  slave_en();

  vo_en();
  vicap_vo_layer_init();
  wbc_enable();

  k_char select = 0;
  while (K_TRUE) {
    if (select != '\n') {
      printf("---------------------------------------\n");
      printf(" d: dump data addr test\n");
      printf(" q: to exit\n");
      printf("---------------------------------------\n");
      PZH_I("please Input:\n\n");
    }
    select = (k_char)getchar();
    switch (select) {
    case 'd':
      printf("sample_vicap... dump frame.\n");
      vicap_dump_frame(device_obj);
      k_video_frame_info vf_info;
      if (!kd_mpi_wbc_dump_frame(&vf_info, 1000)) {
        save_nv12_frame(&vf_info, "/sharefs/pzh/0805/wbc_nv12_1080p.yuv.yuv");
        kd_mpi_wbc_dump_release(&vf_info);
      }

      break;
    case 'q':
      goto app_exit;
    default:
      break;
    }
    sleep(1);
  }

app_exit:
  app_exit(device_obj);

vb_exit:

  return ret;
}

打印log

msh /sharefs/0805>./p.elf
sample_vicap, dev[0] in size[1920x1080]
sample_vicap, dev_num(0), chn_num(0), in_size[1920x1080], out_offset[0:0], out_size[960x540]
sample_vicap, dev[1] in size[1920x1080]
sample_vicap, dev_num(1), chn_num(0), in_size[1920x1080], out_offset[0:0], out_size[960x540]
sample_vicap, dev[2] in size[1920x1080]
sample_vicap, dev_num(2), chn_num(0), in_size[1920x1080], out_offset[0:0], out_size[960x540]
pengzhihao_sample_vicap_vb_init, enable dev(0)
pengzhihao_sample_vicap_vb_init, dev(0) pool(0) in_size(4147200) blk_cnt(4)
pengzhihao_sample_vicap_vb_init, enable chn(0), k(1)
pengzhihao_sample_vicap_vb_init, dev(0) chn(0) pool(1) buf_size(778240) blk_cnt(6)
pengzhihao_sample_vicap_vb_init, enable dev(1)
pengzhihao_sample_vicap_vb_init, dev(1) pool(2) in_size(4147200) blk_cnt(4)
pengzhihao_sample_vicap_vb_init, enable chn(0), k(3)
pengzhihao_sample_vicap_vb_init, dev(1) chn(0) pool(3) buf_size(778240) blk_cnt(6)
pengzhihao_sample_vicap_vb_init, enable dev(2)
pengzhihao_sample_vicap_vb_init, dev(2) pool(4) in_size(4147200) blk_cnt(4)
pengzhihao_sample_vicap_vb_init, enable chn(0), k(5)
pengzhihao_sample_vicap_vb_init, dev(2) chn(0) pool(5) buf_size(778240) blk_cnt(6)
sample_vicap, set dev(0) chn(0) attr, buffer_size(778240), out size[960x540]
sample_vicap out_win h_start is 0 ,v_start is 0
sample_vicap, set dev(1) chn(0) attr, buffer_size(778240), out size[960x540]
sample_vicap out_win h_start is 0 ,v_start is 0
sample_vicap, set dev(2) chn(0) attr, buffer_size(778240), out size[960x540]
sample_vicap out_win h_start is 0 ,v_start is 0
sample_vicap, vicap dev(0) init
mirror mirror is 0 , sensor tpye is 38
ov5647_power_rest OV5647_CAM_PIN_CSI1 is 10
set output err, set default format ISP_PIX_FMT_YUV420SP
[tuning] dev: 0
acq_win.width: 1920
acq_win.height: 1080
pipe_ctrl: 4261412857
sensor_fd: 5
sensor_type: 38
sensor_name: ov5647_csi1
database_name: ov5647-1920x1080
buffer_num: 4
buffer_size: 4147200
[tuning] chn: 0
out_win.width: 960
out_win.height: 540
bit_width: 0
pix_format: 5
buffer_num: 6
buffer_size: 778240
yraw_size: 0
uv_size: 0
v_size: 0
block_type: 1
wait_time: 500
chn_enable: 1
isp_3dnr_en is 1 g_isp_dev_ctx[dev_num].dev_attr.pipe_ctrl.bits.dnr3_enable is 0
VsiCamDeviceCreate hw:0-vt:0 created!
ov5647_power_rest OV5647_CAM_PIN_CSI1 is 10
kd_mpi_isp_set_output_chn_format, width(960), height(540), pix_format(5)
[dw] init, version Mar 14 2025 16:36:59
sample_vicap, vicap dev(1) init
mirror mirror is 0 , sensor tpye is 37
set output err, set default format ISP_PIX_FMT_YUV420SP
[tuning] dev: 1
acq_win.width: 1920
acq_win.height: 1080
pipe_ctrl: 4261412857
sensor_fd: 9
sensor_type: 37
sensor_name: ov5647
database_name: ov5647-1920x1080
buffer_num: 4
buffer_size: 4147200
[tuning] chn: 0
out_win.width: 960
out_win.height: 540
bit_width: 0
pix_format: 5
buffer_num: 6
buffer_size: 778240
yraw_size: 0
uv_size: 0
v_size: 0
block_type: 1
wait_time: 500
chn_enable: 1
isp_3dnr_en is 1 g_isp_dev_ctx[dev_num].dev_attr.pipe_ctrl.bits.dnr3_enable is 0
VsiCamDeviceCreate hw:0-vt:0 created!
ov5647_power_rest OV5647_CAM_PIN is 45
kd_mpi_isp_set_output_chn_format, width(960), height(540), pix_format(5)
[dw] init, version Mar 14 2025 16:36:59
sample_vicap, vicap dev(2) init
mirror mirror is 0 , sensor tpye is 52
<0>[6] [vi] wait stop timeout

set output err, set default format ISP_PIX_FMT_YUV420SP
[tuning] dev: 2
acq_win.width: 1920
acq_win.height: 1080
pipe_ctrl: 4261412857
sensor_fd: 10
sensor_type: 52
sensor_name: gc2093_csi2
database_name: gc2093-1920x1080
buffer_num: 4
buffer_size: 4147200
[tuning] chn: 0
out_win.width: 960
out_win.height: 540
bit_width: 0
pix_format: 5
buffer_num: 6
buffer_size: 778240
yraw_size: 0
uv_size: 0
v_size: 0
block_type: 1
wait_time: 500
chn_enable: 1
isp_3dnr_en is 1 g_isp_dev_ctx[dev_num].dev_attr.pipe_ctrl.bits.dnr3_enable is 0
VsiCamDeviceCreate hw:0-vt:0 created!
kd_mpi_isp_set_output_chn_format, width(960), height(540), pix_format(5)
[dw] init, version Mar 14 2025 16:36:59
sample_vicap, vicap dev(0) start stream
sample_vicap, vicap dev(1) start stream
sample_vicap, vicap dev(2) start stream
info.id is 2 info.vsync is 900902 info vsync high is 10
info.id is 2  info vs enable is 1 info hs enable is 0
yaddr is 0 uvaddr is 1fb000
---------------------------------------
 d: dump data addr test
 q: to exit
---------------------------------------
d
sample_vicap... dump frame.
sample_vicap, dev(0) chn(0) dump frame.
dump cost 5 us
save dump data to file(dev_00_chn_00_960x540_0000.yuv420sp)
sample_vicap, release dev(0) chn(0) dump frame.
sample_vicap, dev(1) chn(0) dump frame.
dump cost 5 us
save dump data to file(dev_01_chn_00_960x540_0001.yuv420sp)
sample_vicap, release dev(1) chn(0) dump frame.
sample_vicap, dev(2) chn(0) dump frame.
dump cost 6 us
save dump data to file(dev_02_chn_00_960x540_0002.yuv420sp)
sample_vicap, release dev(2) chn(0) dump frame.
<3>[4] [Func]:vb_is_blk_valid [Line]:1119 [Info]:Pool ID [-1] is great than max pool Id [64]!
(call_vb_is_blk_valid(attr->wbc_frame.pool_id, attr->wbc_frame.v_frame.phys_addr[0])) assertion failed at function:                    wbc_program, line number:279
riscv64-unknown-linux-musl-addr2line -e rtthread.elf -a -f 000000000033361a 00000000002be21c 0000000000334c32


参照smart_ipc 修改。

smart_ipc只在纯rtos有,我这个好像是api不一样

我这个版本是 k230 sdk release v1.9

用的是什么系统?rtos上api接口是一样的。有代码不去参考,出了问题概率太大。

image.png wbc启用,对应模块的vb 要配置,看你的报错可能没有配置vb导致,可以尝试修改看看。参照smart_ipc的思路去修改,结构体的修改不影响整体的接口调用流程。

有配置vb呀,在这个页面直接搜sample_vicap_vb_init这个函数里面有这一段

config.comm_pool[k].blk_cnt = WBC_BLK_CNT;
config.comm_pool[k].blk_size =
VICAP_ALIGN_UP(WBC_WIDTH * WBC_HEIGHT * 3 / 2, VICAP_ALIGN_1K);
config.comm_pool[k].mode = VB_REMAP_MODE_NOCACHE;

k++;

我看这和你给的那一部分几乎一样

.blk_size大小不对,解决不了就把其他无用的代码先去掉,只保留容易出问题的代码再去查。