AI检测例子和LVGL在OSD叠加显示的问题,求解

Viewed 90

问题描述


平台:RT-Smart
我看了下手册里的OSD和一些examples里的源码,lvgl的disp配置在OSD2,检测结果绘制在OSD0,我分别把VO输出层、OSD2、OSD0修改了透明度编译拷贝到RTOS上,但是始终无法显示lvgl的GUI,只能显示检测结果和摄像头图像。具体如图:
1.lvgl的OSD(用的OSD2):
image.png
2.lvgl的GUI配置(lvgl我单独编译运行过,单独运行时,OPA的高低有显示差距):
image.png
3.结果绘制的OSD0(这个改低了好像没效果):
image.png
4.VO层(这个改低了后有明显的变暗):
image.png
编译没问题(CMakeLists我自己改了一下,把lvgl的库头文件和libxxx包含进去了):
image.png

硬件板卡


01 studio的板子

软件版本


RtSmart-K230_01Studio_rtsmart_v1.5-legacy-12-g678e3f6_nncase_v2.9.0.img.gz

应该是哪儿我没配置好,求大佬指点一下

2 Answers

如果去掉osd的结果绘制呢,只显示gui。

去掉后就可以正常显示LVGL的GUI,并且更改OPA 透明度参数编译上机也确实有视觉上的差别

gui+video是可以显示的,只是gui+video+结果不显示?

GUI可与单独显示,vido+结果也可以单独显示,GUI+video+结果 只显示video + 结果。

examples\ai\face_detection\src\main.cc

/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
 */
#include <iostream>
#include <thread>
#include "ai_utils.h"
#include "face_detection.h"
#include "setting.h"
#include "video_pipeline.h"
#include "lvgl.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>

#include "lvgl.h"

#include "lv_k230_display.h"
#include "lv_k230_input_touch.h"

#include "k_gsdma_comm.h"
#include "mpi_vb_api.h"
#define MAX_DISPLATY_WIDTH  (1920)
#define MAX_DISPLATY_HEIGHT (1080)

// Global variables for signal handling
static volatile int  g_signal_received = 0;
static lv_display_t* g_display         = NULL;

// Signal handler for graceful shutdown
static void signal_handler(int signum)
{
    printf("\nReceived signal %d, shutting down...\n", signum);
    g_signal_received = 1;
}

std::atomic<bool> isp_stop(false);

void video_proc(char *argv[])
{
    int debug_mode = atoi(argv[5]);
    FrameCHWSize image_size={AI_FRAME_CHANNEL,AI_FRAME_HEIGHT, AI_FRAME_WIDTH};
    // 创建一个空的Mat对象,用于存储绘制的帧
    cv::Mat draw_frame(OSD_HEIGHT, OSD_WIDTH, CV_8UC4, cv::Scalar(0, 0, 0, 0));
    // 创建一个空的runtime_tensor对象,用于存储输入数据
    runtime_tensor input_tensor;
    dims_t in_shape { 1, AI_FRAME_CHANNEL, AI_FRAME_HEIGHT, AI_FRAME_WIDTH };

    // 创建一个PipeLine对象,用于处理视频流
    PipeLine pl(debug_mode);
    // 初始化PipeLine对象
    pl.Create();
    // 创建一个DumpRes对象,用于存储帧数据
    DumpRes dump_res;
    // 创建FaceDetection实例
    FaceDetection fd(argv[1], atof(argv[2]),atof(argv[3]), image_size, debug_mode);
    vector<FaceDetectionInfo> results;

    while(!isp_stop){
        // 创建一个ScopedTiming对象,用于计算总时间
        ScopedTiming st("total time", 1);
        // 从PipeLine中获取一帧数据,并创建tensor
        pl.GetFrame(dump_res);
        input_tensor = host_runtime_tensor::create(typecode_t::dt_uint8, in_shape, { (gsl::byte *)dump_res.virt_addr, compute_size(in_shape) },false, hrt::pool_shared, dump_res.phy_addr).expect("cannot create input tensor");
        hrt::sync(input_tensor, sync_op_t::sync_write_back, true).expect("sync write_back failed");
        //前处理,推理,后处理
        results.clear();
        fd.pre_process(input_tensor);
        fd.inference();
        fd.post_process(image_size,results);
        draw_frame.setTo(cv::Scalar(0, 0, 0, 0));
        fd.draw_result(draw_frame,results,false);
        // 将绘制的帧插入到PipeLine中
        pl.InsertFrame(draw_frame.data);
        // 释放帧数据
        pl.ReleaseFrame(dump_res);
    }
    pl.Destroy();
}

static void mygui() {
	// 根
    lv_obj_t *root_container = lv_obj_create(lv_scr_act());
    lv_obj_set_size(root_container, LV_PCT(100), LV_PCT(100));
    lv_obj_set_flex_flow(root_container, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_style_pad_all(root_container, 0, LV_PART_MAIN);
    lv_obj_set_style_pad_row(root_container, 0, LV_PART_MAIN);
    lv_obj_set_style_radius(root_container, 0, LV_PART_MAIN);
    
    lv_obj_set_style_opa(root_container, LV_OPA_20, LV_STATE_DEFAULT); //
    
    // bar
    lv_obj_t *top_bar = lv_obj_create(root_container);
    lv_obj_set_size(top_bar, LV_PCT(100), 25);
    lv_obj_set_style_pad_all(top_bar, 0, LV_PART_MAIN);
    lv_obj_set_style_margin_all(top_bar, 0, LV_PART_MAIN);
    lv_obj_set_style_radius(top_bar, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(top_bar, lv_color_hex(0x3b82f6), LV_PART_MAIN);
    lv_obj_t *bar_label = lv_label_create(top_bar);
    lv_label_set_text(bar_label, "Top bar");
    lv_obj_set_style_text_color(bar_label, lv_color_white(), LV_PART_MAIN);
    lv_obj_center(bar_label);

    // 下方
    lv_obj_t *content_container = lv_obj_create(root_container);
    lv_obj_set_width(content_container, LV_PCT(100));
    lv_obj_set_flex_grow(content_container, 1);
    lv_obj_set_style_pad_all(content_container, 0, LV_PART_MAIN);
    lv_obj_set_style_margin_all(content_container, 0, LV_PART_MAIN);
    lv_obj_set_style_radius(content_container, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(content_container, lv_color_hex(0xf3f4f6), LV_PART_MAIN);
    lv_obj_t *content_label = lv_label_create(content_container);
    lv_label_set_text(content_label, "main");
    lv_obj_center(content_label);
}
int vb_init(void)
{
    k_s32                  ret;
    k_vb_config            config;
    k_vb_supplement_config supplement_config;

    memset(&config, 0x00, sizeof(config));
    config.max_pool_cnt = VB_MAX_POOLS;

    // for gdma rotate
    config.comm_pool[0].blk_cnt  = 1;
    config.comm_pool[0].mode     = VB_REMAP_MODE_NOCACHE;
    config.comm_pool[0].blk_size = VB_ALIGN_UP(MAX_DISPLATY_WIDTH * MAX_DISPLATY_HEIGHT * 4, 4096);

    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;
    ret = kd_mpi_vb_set_supplement_config(&supplement_config);
    if (ret) {
        printf("vb_set_supplement_config failed ret:%d\n", ret);
        return ret;
    }

    ret = kd_mpi_vb_init();
    if (ret) {
        printf("vb_init failed ret:%d\n", ret);
        return ret;
    }

    return 0;
}

int vb_deinit(void)
{
    kd_mpi_vb_exit();

    return 0;
}

// Parse color format from argument
static lv_color_format_t parse_color_format(const char* format)
{
    if (strcmp(format, "rgb565") == 0) {
        return LV_COLOR_FORMAT_RGB565;
    } else if (strcmp(format, "rgb888") == 0) {
        return LV_COLOR_FORMAT_RGB888;
    } else if (strcmp(format, "argb8888") == 0) {
        return LV_COLOR_FORMAT_ARGB8888;
    } else {
        printf("Unknown color format: %s, using default: rgb888\n", format);
        return LV_COLOR_FORMAT_RGB888;
    }
}

// Get color format as string
static const char* color_format_to_string(lv_color_format_t format)
{
    switch (format) {
    case LV_COLOR_FORMAT_RGB565:
        return "RGB565";
    case LV_COLOR_FORMAT_RGB888:
        return "RGB888";
    case LV_COLOR_FORMAT_ARGB8888:
        return "ARGB8888";
    default:
        return "Unknown";
    }
}

// Convert degrees to LVGL display rotation type
static lv_display_rotation_t parse_rotation(int degrees)
{
    /* gdma rotate 90 with lvgl rotate is reversed */
    switch (degrees) {
    case 90:
        return LV_DISPLAY_ROTATION_270;
    case 180:
        return LV_DISPLAY_ROTATION_180;
    case 270:
        return LV_DISPLAY_ROTATION_90;
    default:
        return LV_DISPLAY_ROTATION_0;
    }
}

int main(int argc, char *argv[])
{
	// Setup signal handlers for graceful shutdown
	signal(SIGINT, signal_handler);
	lv_display_t*     disp;
	k_connector_type  connector_type = ST7701_V1_MIPI_2LAN_480X800_30FPS;
	k_vo_layer_id     osd_layer      = K_VO_LAYER_OSD2;
	lv_color_format_t color_format   = LV_COLOR_FORMAT_ARGB8888;
	
	vb_init();
	if (0x00 != kd_display_init(connector_type)) {
    	printf("failed int init connector\n");
    	return -1;
	}
	lv_init();
	disp = lv_k230_display_create(osd_layer);
	if (!disp) {
    	printf("Failed to create LVGL display\n");
    	return -1;
	}
	printf("LVGL display created successfully\n");
	// Set rotation
	lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);
	// Set color format
	printf("Setting color format to %s...\n", color_format_to_string(color_format));
	lv_display_set_color_format(disp, color_format);
	printf("Color format set successfully\n");
	lv_k230_touch_init(0);
	mygui();
    std::thread thread_isp(video_proc, argv);
    // Main loop
	while (!g_signal_received) {
    	// Handle LVGL tasks
    	uint32_t delay_ms = lv_task_handler();

    	if (100 < delay_ms) {
        	delay_ms = 100;
    	}

   		// Use a shorter sleep to be more responsive to signals
    	usleep(delay_ms * 1000);
	}
	
	// Cleanup
	printf("Cleaning up...\n");
	if (disp) {
    	lv_display_delete(disp);
    	g_display = NULL;
	}
	
    //while (getchar() != 'q')
    //{
     //   usleep(10000);
    //}
    isp_stop = true;
    thread_isp.join();
    return 0;
}

examples\ai\face_detection\src\videopipeline.cc

#include "video_pipeline.h"

/* 16字节对齐宏,用于硬件DMA/图像缓冲区对齐 */
#define ALIGN_UP_16(x)  (((x) + 15) & ~15)

/* 构造函数:初始化管线各模块的默认配置 */
PipeLine::PipeLine(int debug_mode)
{
    // ------------------------ 显示接口类型选择 ------------------------
    // 根据宏 DISPLAY_MODE 选择不同屏幕(MIPI/HDMI 等)
    if(DISPLAY_MODE==0){
        connector_type = LT9611_MIPI_4LAN_1920X1080_30FPS;
    }
    else if(DISPLAY_MODE==1){
        connector_type = ST7701_V1_MIPI_2LAN_480X800_30FPS;
    }
    else if(DISPLAY_MODE==2){
        connector_type = HX8377_V2_MIPI_4LAN_1080X1920_30FPS;
    }
    else{
        // 默认回退为 1080P HDMI 输出
        connector_type = LT9611_MIPI_4LAN_1920X1080_30FPS;
    }

    // ------------------------ VO(视频输出)相关 ID ------------------------
    vo_dev_id = K_VO_DISPLAY_DEV_ID;        // VO 设备 ID
    vi_vo_id  = K_VO_LAYER_VIDEO1;          // 用于显示摄像头视频的 VO layer
    osd_vo_id = K_VO_LAYER_OSD0;            // 用于叠加 OSD 的 VO layer

    // ------------------------ Sensor / VICAP 默认配置 ------------------------
    // 默认使用 GC2093,start() 中会根据探测结果自动适配
    sensor_type = GC2093_MIPI_CSI2_1920X1080_30FPS_10BIT_LINEAR;
    // VICAP 设备 ID
    vicap_dev = VICAP_DEV_ID_0;
    // VICAP → VO 通道(视频直通显示)
    vicap_chn_to_vo = VICAP_CHN_ID_0;
    // VICAP → AI 通道(用于算法推理)
    vicap_chn_to_ai = VICAP_CHN_ID_1;

    // 调试模式开关
    debug_mode_ = debug_mode;

    // OSD 所使用的 VB 内存池,初始化为无效
    osd_pool_id = VB_INVALID_POOLID;
}

PipeLine::~PipeLine()
{
}

/* 管线创建:初始化 VB → 屏幕 → VO → OSD → VICAP → 绑定关系 */
int PipeLine::Create()
{
    ScopedTiming st("PipeLine::Create", debug_mode_);
    k_s32 ret = 0;

    // =============================================================================================
    // 1. 配置 Video Buffer(VB)系统
    // =============================================================================================
    memset(&config, 0, sizeof(k_vb_config));
    config.max_pool_cnt = 64;  // 最多支持 64 个内存池

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

    // 设置 VB 附加配置(JPEG、ISP 统计等)
    //k_vb_supplement_config supplement_config;
    //memset(&supplement_config, 0, sizeof(supplement_config));
   // supplement_config.supplement_config |= VB_SUPPLEMENT_JPEG_MASK;
    //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;
    //}

    // =============================================================================================
    // 2. 创建 OSD 专用 VB 内存池(用于 ARGB8888 叠加图层)
    // =============================================================================================
    // 用于存放一帧 OSD 数据(如 AI 结果绘制)
    if(USE_OSD == 1){
        k_vb_pool_config pool_config;
        memset(&pool_config, 0, sizeof(pool_config));
        pool_config.blk_cnt = 3; // 3 个缓冲块,避免帧冲突
        pool_config.blk_size = VICAP_ALIGN_UP((OSD_WIDTH * OSD_HEIGHT * OSD_CHANNEL), VICAP_ALIGN_1K);
        pool_config.mode = VB_REMAP_MODE_NOCACHE; // 非 cache 映射,避免缓存一致性问题
        osd_pool_id = kd_mpi_vb_create_pool(&pool_config);
    }

    // =============================================================================================
    // 3. 屏幕(Connector)配置
    // =============================================================================================
    k_connector_info connector_info;
    memset(&connector_info, 0, sizeof(k_connector_info));

    // 根据 connector 类型获取硬件参数
    ret = kd_mpi_get_connector_info(connector_type, &connector_info);
    if (ret) {
        printf("the connector type not supported!\n");
        return ret;
    }

    // 打开 connector 设备
    k_s32 connector_fd = kd_mpi_connector_open(connector_info.connector_name);
    if (connector_fd < 0) {
        printf("%s, connector open failed.\n", __func__);
        return K_ERR_VO_NOTREADY;
    }

    // 打开电源
    ret = kd_mpi_connector_power_set(connector_fd, K_TRUE);
    if (ret) {
        printf("ERROR: kd_mpi_connector_power_set failed, ret=%d\n", ret);
        return ret;
    }

    // 初始化 connector(配置时序、分辨率等)
    ret = kd_mpi_connector_init(connector_fd, connector_info);
    if (ret) {
        printf("ERROR: kd_mpi_connector_init failed, ret=%d\n", ret);
        return ret;
    }

    // 关闭设备句柄(配置完成即可关闭)
    ret = kd_mpi_connector_close(connector_fd);
    if (ret) {
        printf("ERROR: kd_mpi_connector_close failed, ret=%d\n", ret);
        return ret;
    }

    // =============================================================================================
    // 4. 配置 VO(视频输出层:用于显示摄像头画面)
    // =============================================================================================
    kd_mpi_vo_disable_layer(vi_vo_id);  // 先关闭 layer,避免旧配置干扰

    memset(&vi_vo_attr, 0, sizeof(vi_vo_attr));
    vi_vo_attr.layer_id        = vi_vo_id;
    vi_vo_attr.position.x      = 0;
    vi_vo_attr.position.y      = 0;
    vi_vo_attr.img_size.width  = DISPLAY_WIDTH;
    vi_vo_attr.img_size.height = DISPLAY_HEIGHT;
    vi_vo_attr.pixel_format    = PIXEL_FORMAT_YUV_SEMIPLANAR_420; // NV12
    vi_vo_attr.global_alpha   = 0x3C;                            // 不透明
    // 根据 DISPLAY_MODE 是否需要旋转
    vi_vo_attr.func            = DISPLAY_MODE? GDMA_ROTATE_DEGREE_90 : GDMA_ROTATE_DEGREE_0;
    // 若旋转,需要额外的 DMA buffer
    vi_vo_attr.rot_buf_nr      = DISPLAY_MODE? 2 : 0;
    vi_vo_attr.rot_buf_bpp     = 0;

    ret = kd_mpi_vo_set_layer_attr(vi_vo_id, &vi_vo_attr);
    if (ret != K_SUCCESS) {
        printf("ERROR: kd_mpi_vo_set_layer_attr failed, ret=%d\n", ret);
        return ret;
    }

    ret = kd_mpi_vo_enable_layer(vi_vo_id);
    if (ret != K_SUCCESS) {
        printf("ERROR: kd_mpi_vo_enable_layer failed, ret=%d\n", ret);
        return ret;
    }

    printf("VICAP to VO: layer=%d configured for %ux%u NV12, rotate90=%d\n",
           vi_vo_id, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MODE ? 1 : 0);

    // =============================================================================================
    // 5. 配置 OSD 层(ARGB8888 叠加图层)
    // =============================================================================================
    if(USE_OSD == 1){
        kd_mpi_vo_disable_layer(osd_vo_id);

        memset(&osd_vo_attr, 0, sizeof(osd_vo_attr));
        osd_vo_attr.layer_id        = osd_vo_id;
        osd_vo_attr.position.x      = 0;
        osd_vo_attr.position.y      = 0;
        osd_vo_attr.img_size.width  = OSD_WIDTH;
        osd_vo_attr.img_size.height = OSD_HEIGHT;
        osd_vo_attr.pixel_format    = PIXEL_FORMAT_ARGB_8888;  // OSD 常用 BGRA/ARGB
        osd_vo_attr.global_alpha    = 0x64;
        osd_vo_attr.func            = DISPLAY_MODE? GDMA_ROTATE_DEGREE_90 : GDMA_ROTATE_DEGREE_0;
        osd_vo_attr.rot_buf_nr      = DISPLAY_MODE? 2 : 0;
        osd_vo_attr.rot_buf_bpp     = 0;

        ret = kd_mpi_vo_set_layer_attr(osd_vo_id, &osd_vo_attr);
        if (ret != K_SUCCESS) {
            printf("ERROR: kd_mpi_vo_set_layer_attr failed, ret=%d\n", ret);
            return ret;
        }

        ret = kd_mpi_vo_enable_layer(osd_vo_id);
        if (ret != K_SUCCESS) {
            printf("ERROR: kd_mpi_vo_enable_layer failed, ret=%d\n", ret);
            return ret;
        }

        printf("OSD to VO: layer=%d configured for %ux%u BGRA8888, rotate90=%d\n",
               osd_vo_id, OSD_WIDTH, OSD_HEIGHT, DISPLAY_ROTATE ? 1 : 0);

        // --------------------- 从 OSD VB 池获取一块缓存,用于写入叠加数据 ---------------------
        k_s32 size = VICAP_ALIGN_UP(OSD_HEIGHT * OSD_WIDTH * OSD_CHANNEL, VICAP_ALIGN_1K);

        // 从指定内存池中申请一块缓存
        handle = kd_mpi_vb_get_block(osd_pool_id, size, NULL);
        if (handle == VB_INVALID_HANDLE)
        {
            printf("%s get vb block error\n", __func__);
            return -1;
        }

        // 获取该缓存块的物理地址
        k_u64 phys_addr = kd_mpi_vb_handle_to_phyaddr(handle);
        if (phys_addr == 0)
        {
            printf("%s get phys addr error\n", __func__);
            return -1;
        }

        // 映射为用户态虚拟地址(非 cache)
        k_u32* virt_addr = (k_u32 *)kd_mpi_sys_mmap(phys_addr, size);
        if (virt_addr == NULL)
        {
            printf("%s mmap error\n", __func__);
            return -1;
        }

        // 初始化 OSD 帧描述结构
        memset(&osd_frame_info, 0, sizeof(osd_frame_info));
        osd_frame_info.v_frame.width        = OSD_WIDTH;
        osd_frame_info.v_frame.height       = OSD_HEIGHT;
        osd_frame_info.v_frame.stride[0]    = OSD_WIDTH*4;
        osd_frame_info.v_frame.pixel_format = PIXEL_FORMAT_BGRA_8888;
        osd_frame_info.mod_id               = K_ID_VO;
        osd_frame_info.pool_id              = osd_pool_id;
        osd_frame_info.v_frame.phys_addr[0] = phys_addr;

        // 保存虚拟地址,用于后续 memcpy 写入 OSD 数据
        insert_osd_vaddr = virt_addr;
        printf("phys_addr is %lx g_pool_id is %d \n", phys_addr, osd_pool_id);
    }

    // =============================================================================================
    // 6. 传感器探测 & VICAP 设备配置
    // =============================================================================================
    // 自动探测 Sensor
    k_vicap_probe_config probe_cfg;
    k_vicap_sensor_info sensor_info;
    probe_cfg.csi_num = CONFIG_MPP_SENSOR_DEFAULT_CSI;
    probe_cfg.width   = ISP_WIDTH;
    probe_cfg.height  = ISP_HEIGHT;
    probe_cfg.fps     = 30;
    if(0x00 != kd_mpi_sensor_adapt_get(&probe_cfg, &sensor_info)) {
        printf("vicap, can't probe sensor on %d, output %dx%d@%d\n",
               probe_cfg.csi_num, probe_cfg.width, probe_cfg.height, probe_cfg.fps);
        return -1;
    }

    sensor_type =  sensor_info.sensor_type;
    memset(&sensor_info, 0, sizeof(k_vicap_sensor_info));
    ret = kd_mpi_vicap_get_sensor_info(sensor_type, &sensor_info);
    if (ret) {
        printf("vicap, the sensor type not supported!\n");
        return ret;
    }

    // 配置 VICAP 设备属性(采集窗口、工作模式、ISP 功能等)
    k_vicap_dev_attr dev_attr;
    memset(&dev_attr, 0, sizeof(k_vicap_dev_attr));
    dev_attr.acq_win.h_start = 0;
    dev_attr.acq_win.v_start = 0;
    dev_attr.acq_win.width   = ISP_WIDTH;
    dev_attr.acq_win.height  = ISP_HEIGHT;
    dev_attr.mode            = VICAP_WORK_ONLINE_MODE;  // 在线模式
    dev_attr.pipe_ctrl.data  = 0xFFFFFFFF;
    dev_attr.pipe_ctrl.bits.af_enable   = 0;
    dev_attr.pipe_ctrl.bits.ahdr_enable = 0;
    dev_attr.pipe_ctrl.bits.dnr3_enable = 0;
    dev_attr.cpature_frame   = 0;
    dev_attr.sensor_info     = sensor_info;

    ret = kd_mpi_vicap_set_dev_attr(vicap_dev, dev_attr);
    if (ret) {
        printf("vicap, kd_mpi_vicap_set_dev_attr failed.\n");
        return ret;
    }

    // =============================================================================================
    // 7. VICAP 通道 0:输出到 VO 显示
    // =============================================================================================
    k_vicap_chn_attr chn0_attr;
    memset(&chn0_attr, 0, sizeof(k_vicap_chn_attr));
    chn0_attr.out_win.width  = DISPLAY_WIDTH;
    chn0_attr.out_win.height = DISPLAY_HEIGHT;
    chn0_attr.crop_win       = dev_attr.acq_win;
    chn0_attr.scale_win      = chn0_attr.out_win;
    chn0_attr.crop_enable    = K_FALSE;
    chn0_attr.scale_enable   = K_FALSE;
    chn0_attr.chn_enable     = K_TRUE;
    chn0_attr.pix_format     = PIXEL_FORMAT_YUV_SEMIPLANAR_420; // NV12
    chn0_attr.buffer_num     = VICAP_MAX_FRAME_COUNT;
    chn0_attr.buffer_size    = VICAP_ALIGN_UP((DISPLAY_WIDTH * DISPLAY_HEIGHT * 3 / 2), VICAP_ALIGN_1K);
    chn0_attr.buffer_pool_id = VB_INVALID_POOLID;

    printf("vicap ...kd_mpi_vicap_set_chn_attr, buffer_size[%d]\n", chn0_attr.buffer_size);
    ret = kd_mpi_vicap_set_chn_attr(vicap_dev, vicap_chn_to_vo, chn0_attr);
    if (ret) {
        printf("vicap, kd_mpi_vicap_set_chn_attr failed.\n");
        return ret;
    }

    // 绑定 VICAP → VO(视频直通显示)
    vicap_mpp_chn.mod_id = K_ID_VI;
    vicap_mpp_chn.dev_id = vicap_dev;
    vicap_mpp_chn.chn_id = vicap_chn_to_vo;
    vo_mpp_chn.mod_id    = K_ID_VO;
    vo_mpp_chn.dev_id    = vo_dev_id;
    vo_mpp_chn.chn_id    = vi_vo_id;
    ret = kd_mpi_sys_bind(&vicap_mpp_chn, &vo_mpp_chn);
    if (ret) {
        printf("kd_mpi_sys_bind failed:0x%x\n", ret);
    }

    // =============================================================================================
    // 8. VICAP 通道 1:输出给 AI 使用(RGB Planar)
    // =============================================================================================
    k_vicap_chn_attr chn1_attr;
    memset(&chn1_attr, 0, sizeof(k_vicap_chn_attr));
    chn1_attr.out_win.width  = AI_FRAME_WIDTH;
    chn1_attr.out_win.height = AI_FRAME_HEIGHT;
    chn1_attr.crop_win       = dev_attr.acq_win;
    chn1_attr.scale_win      = chn1_attr.out_win;
    chn1_attr.crop_enable    = K_FALSE;
    chn1_attr.scale_enable   = K_FALSE;
    chn1_attr.chn_enable     = K_TRUE;
    chn1_attr.pix_format     = PIXEL_FORMAT_RGB_888_PLANAR; // AI 常用输入格式
    chn1_attr.buffer_num     = VICAP_MAX_FRAME_COUNT;
    chn1_attr.buffer_size    = VICAP_ALIGN_UP((AI_FRAME_WIDTH * AI_FRAME_HEIGHT * 3 ), VICAP_ALIGN_1K);
    chn1_attr.buffer_pool_id = VB_INVALID_POOLID;

    printf("kd_mpi_vicap_set_chn_attr, buffer_size[%d]\n", chn1_attr.buffer_size);
    ret = kd_mpi_vicap_set_chn_attr(vicap_dev, vicap_chn_to_ai, chn1_attr);
    if (ret) {
        printf("kd_mpi_vicap_set_chn_attr failed.\n");
        return ret;
    }

    // 设置数据库解析模式(XML/JSON)
    ret = kd_mpi_vicap_set_database_parse_mode(vicap_dev, VICAP_DATABASE_PARSE_XML_JSON);
    if (ret) {
        printf("kd_mpi_vicap_set_database_parse_mode failed.\n");
        return ret;
    }

    // 初始化 VICAP
    printf("kd_mpi_vicap_init\n");
    ret = kd_mpi_vicap_init(vicap_dev);
    if (ret) {
        printf("kd_mpi_vicap_init failed.\n");
    }

    // 启动数据流
    printf("kd_mpi_vicap_start_stream\n");
    ret = kd_mpi_vicap_start_stream(vicap_dev);
    if (ret) {
        printf("kd_mpi_vicap_init failed.\n");
    }

    return ret;
}

/* 从 VICAP 通道 1 获取一帧,用于 AI 推理 */
void PipeLine::GetFrame(DumpRes &dump_res){
    ScopedTiming st("PipeLine::GetFrame", debug_mode_);
    int ret=0;
    memset(&dump_info, 0, sizeof(k_video_frame_info));

    // 从 VICAP dump 一帧(阻塞最多 1000ms)
    ret = kd_mpi_vicap_dump_frame(vicap_dev, VICAP_CHN_ID_1, VICAP_DUMP_YUV, &dump_info, 1000);
    if (ret)
    {
        printf("kd_mpi_vicap_dump_frame failed.\n");
    }

    // 将物理地址映射为虚拟地址,供 CPU 访问
    dump_res.virt_addr = reinterpret_cast<uintptr_t>(
        kd_mpi_sys_mmap(dump_info.v_frame.phys_addr[0],
                        AI_FRAME_CHANNEL*AI_FRAME_HEIGHT*AI_FRAME_WIDTH));
    dump_res.phy_addr = reinterpret_cast<uintptr_t>(dump_info.v_frame.phys_addr[0]);
}

/* 释放当前 dump 帧 */
int PipeLine::ReleaseFrame(DumpRes &dump_res){
    ScopedTiming st("PipeLine::ReleaseFrame", debug_mode_);
    int ret=0;

    // 解除虚拟地址映射
    kd_mpi_sys_munmap(reinterpret_cast<void*>(dump_res.virt_addr),
                      AI_FRAME_CHANNEL*AI_FRAME_HEIGHT*AI_FRAME_WIDTH);

    // 释放 VICAP dump 帧
    ret = kd_mpi_vicap_dump_release(vicap_dev, VICAP_CHN_ID_1, &dump_info);
    if (ret)
    {
        printf("kd_mpi_vicap_dump_release failed.\n");
    }
    return ret;
}

/* 向 OSD layer 插入一帧(用于叠加显示 AI 结果) */
int PipeLine::InsertFrame(void* osd_data){
    ScopedTiming st("PipeLine::InsertFrame", debug_mode_);
    int ret=0;

    // 将外部生成的 OSD 数据拷贝到 VB 映射的内存中
    memcpy(insert_osd_vaddr, osd_data, OSD_WIDTH * OSD_HEIGHT * OSD_CHANNEL);

    // 插入到 VO 的 OSD layer
    if (kd_mpi_vo_insert_frame(osd_vo_id, &osd_frame_info) != K_SUCCESS) {
        printf("ERROR: kd_mpi_vo_insert_frame failed for OSD\n");
        return ret;
    } 
    return ret;
}

/* 销毁管线,释放所有资源 */
int PipeLine::Destroy()
{
    ScopedTiming st("PipeLine::Destroy", debug_mode_);
    int ret=0;

    // ------------------ 关闭 OSD ------------------
    if(USE_OSD == 1)
    {
        ret = kd_mpi_vo_disable_layer(osd_vo_id);
        if (ret) {
            printf("kd_mpi_vo_disable_layer failed.\n");
            return ret;
        }
        ret = kd_mpi_vb_release_block(handle);
        if (ret) {
            printf("kd_mpi_vb_release_block failed.\n");
            return ret;
        }
    }
    printf("kd_mpi_vb_release_block\n");

    // ------------------ 停止 VICAP ------------------
    ret = kd_mpi_vicap_stop_stream(vicap_dev);
    if (ret) {
        printf("kd_mpi_vicap_stop_stream failed.\n");
        return ret;
    }

    // 反初始化 VICAP
    ret = kd_mpi_vicap_deinit(vicap_dev);
    if (ret) {
        printf("kd_mpi_vicap_deinit failed.\n");
        return ret;
    }

    // ------------------ 解除 VI → VO 绑定 ------------------
    ret = kd_mpi_vo_disable_layer(vi_vo_id);
    if (ret) {
        printf("kd_mpi_vo_disable_layer failed.\n");
        return ret;
    }

    vicap_mpp_chn.mod_id = K_ID_VI;
    vicap_mpp_chn.dev_id = vicap_dev;
    vicap_mpp_chn.chn_id = vicap_chn_to_vo;
    vo_mpp_chn.mod_id    = K_ID_VO;
    vo_mpp_chn.dev_id    = vo_dev_id;
    vo_mpp_chn.chn_id    = vi_vo_id;
    ret = kd_mpi_sys_unbind(&vicap_mpp_chn, &vo_mpp_chn);
    if (ret) {
        printf("kd_mpi_sys_unbind failed:0x%x\n", ret);
    }

    /* 等待一帧时间,确保 VO 释放 VB */
    k_u32 display_ms = 1000 / 33;
    usleep(1000 * display_ms);

    // ------------------ 销毁 OSD 内存池 ------------------
    if (osd_pool_id != VB_INVALID_POOLID){
        ret = kd_mpi_sys_munmap(reinterpret_cast<void*>(insert_osd_vaddr),
                                OSD_WIDTH * OSD_HEIGHT * OSD_CHANNEL);
        if (ret) {
            printf("kd_mpi_sys_munmap failed.\n");
            return ret;
        }
        ret = kd_mpi_vb_destory_pool(osd_pool_id);
        if (ret) {
            printf("kd_mpi_vb_destory_pool failed.\n");
            return ret;
        }
        osd_pool_id = VB_INVALID_POOLID;
    }

    // ------------------ 反初始化 VB ------------------
    ret = kd_mpi_vb_exit();
    if (ret) {
        printf("kd_mpi_vb_exit failed.\n");
        return ret;
    }

    return 0;
}

方便发一下压缩包吗?我这边编译看看。

https://kvftsfijpo.feishu.cn/file/IVk1bhArAo4jH5xXwVocXZtUnKe?from=from_copylink 你好,请使用这个例程看一下。(需要先更新一下sdk.