普希金的笔

普希金的笔

0个粉丝

5

问答

0

专栏

0

资料

普希金的笔  发布于  2025-08-21 11:48:13
采纳率 0%
5个问答
458

VPSS 采样线程在读取 640×640 YUV420(NV21)帧并做 CPU 转换时触发段错误

基于MPP的 vio代码(VI → VPSS → VENC/VO),并在VPSS的CHN1额外开出 640×640 YUV420 输出,由独立线程实时取流 → CPU 转 RGB24 → 保存BMP
然后设计的护具链路是:
摄像头(OS04A10) → VI(pipe0) → VPSS(grp0)
├─ CHN0 → VENC(H.265) → 编码文件/RTSP
├─ CHN0 → VO(HDMI/MIPI) → 屏幕预览
└─ CHN1 → 640×640 NV21 → 采样线程 → RGB24 → BMP

这是开发板的打印信息:
~ # ./mpp_enc 0
Sample VIO Start!
vi vpss mode list:
(0) VI_ONLINE_VPSS_ONLINE, FMU OFF
(1) VI_ONLINE_VPSS_OFFLINE, FMU OFF
(2) VI_OFFLINE_VPSS_OFFLINE, FMU DIRECT
(3) VI_OFFLINE_VPSS_OFFLINE, FMU OFF
please select mode:
1

[MPP] Version: [HI3519DV500_MPP_V2.0.2.0 B050 Release], Build Time[Dec 20 2024, 16:23:07]

bus_id:3

linear mode

vi_pipe:0,== os04a10 24Mclk 4M30fps(MIPI) 12bit linear init success! ==

ISP Dev 0 running !
[INFO] configured VPSS chn1 for 640x640 YUV output (if SDK field names match)
[VPSS PROF] started on grp=0 chn=1
[VPSS PROF] grp=0 chn=1 cost=79519 us (w=640 h=640 fmt=38 )
[VPSS PROF][DBG] y_ptr=0x3c00 c_ptr=0x67c00 stride_y=640 stride_c=640
Segmentation fault (core dumped)

这是代码:

/*
  Copyright (c), 2001-2024, Shenshu Tech. Co., Ltd.
  This file is adapted from the user's original sample. Changes:
  - Configure VPSS to output a 640x640 YUV channel (OT_VPSS_CHN1)
  - Start a VPSS sampling thread that reads the 640x640 YUV frames
  - Convert YUV420 (Y + VU -> YVU_SEMIPLANAR / NV21) to RGB24 on CPU
  - Dump a few RGB frames as BMP files for verification

  NOTE: The exact field names of sample_vpss_cfg/ot_vpss_chn_attr are SDK-dependent.
  This file follows the structure from the user's original sample and makes minimal
 , reasonable assumptions about these structs. If your SDK uses slightly different
  field names (e.g., .frame_size -> .size, .vir_addr -> vir_addr[0], etc.), adapt
  the few marked lines below.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <time.h>            // === PROFILING: for clock_gettime
#include <inttypes.h>        // === PROFILING: for PRIu64
#include <stdint.h>

#include "sample_comm.h"
#include "sample_ipc.h"
#include "securec.h"
#include "ss_mpi_ae.h"

#define X_ALIGN 16
#define Y_ALIGN 2
#define out_ratio_1(x) ((x) / 3)
#define out_ratio_2(x) ((x) * 2 / 3)
#define out_ratio_3(x) ((x) / 2)
#define SAMPLE_VIO_MAX_ROUTE_NUM 4

static volatile sig_atomic_t g_sig_flag = 0;

/* === PROFILING: helper, monotonic time in microseconds === */
static inline uint64_t prof_now_us(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (uint64_t)ts.tv_sec * 1000000ULL + (uint64_t)(ts.tv_nsec / 1000ULL);
}

/* VB配置参数 (保持用户原值) */
static sample_vb_param g_vb_param = {
    .vb_size = {2688, 1520},  // 保持与传感器输出分辨率匹配(如400万像素对应2688x1520)
    .pixel_format = {OT_PIXEL_FORMAT_RGB_BAYER_12BPP, OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420},
    .compress_mode = {OT_COMPRESS_MODE_LINE, OT_COMPRESS_MODE_SEG},
    .video_format = {OT_VIDEO_FORMAT_LINEAR, OT_VIDEO_FORMAT_LINEAR},
    .blk_num = {8, 12}  // 增加内存块数量,满足ISP需求
};

/* 系统配置 */
static sampe_sys_cfg g_vio_sys_cfg = {
    .route_num = 1,  // 单路由(单摄像头)
    .mode_type = OT_VI_OFFLINE_VPSS_OFFLINE,
    .nr_pos = OT_3DNR_POS_VI,
    .vi_fmu = {0},
    .vpss_fmu = {0},
};

/* VO配置(屏幕预览) */
static sample_vo_cfg g_vo_cfg = {
    .vo_dev            = SAMPLE_VO_DEV_UHD,
    .vo_layer          = SAMPLE_VO_LAYER_VHD0,
    .vo_intf_type      = OT_VO_INTF_MIPI,
    .intf_sync         = OT_VO_OUT_1080P60,
    .bg_color          = COLOR_RGB_BLACK,
    .pix_format        = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420,
    .disp_rect         = {0, 0, 1920, 1080},
    .image_size        = {1920, 1080},
    .vo_part_mode      = OT_VO_PARTITION_MODE_SINGLE,
    .dis_buf_len       = 3,
    .dst_dynamic_range = OT_DYNAMIC_RANGE_SDR8,
    .vo_mode           = VO_MODE_1MUX,
    .compress_mode     = OT_COMPRESS_MODE_NONE,
};

/* MIPI TX配置 */
sample_mipi_tx_config g_mipi_tx_config = {
    .intf_sync = OT_MIPI_TX_OUT_1080P60,
};

/* 编码配置(H.265可改为H.264) */
static sample_comm_venc_chn_param g_venc_chn_param = {
    .frame_rate           = 30,
    .stats_time           = 2,
    .gop                  = 60,
    .venc_size            = {1920, 1080},
    .size                 = -1,
    .profile              = 0,
    .is_rcn_ref_share_buf = TD_FALSE,
    .gop_attr             = {
        .gop_mode = OT_VENC_GOP_MODE_NORMAL_P,
        .normal_p = {2},
    },
    .type                 = OT_PT_H265,  // 改为OT_PT_H264即可切换编码格式
    .rc_mode              = SAMPLE_RC_CBR,
};

/* === PROFILING: thread handler & param === */
typedef struct {
    ot_vpss_grp grp;
    ot_vpss_chn chn;    /* 选择用于采样的 VPSS 输出通道 */
} vpss_prof_arg_t;

static pthread_t g_prof_thread = 0;

/* ========== YUV(NV21/YVU_SEMIPLANAR_420) -> RGB24 conversion ========== */
static inline int clamp255(int x) {
    if (x < 0) return 0;
    if (x > 255) return 255;
    return x;
}

/* NV21 (Y + VU interleaved) -> RGB24
   src_y: Y plane pointer
   src_vu: VU plane pointer (interleaved: V U V U ...)
   stride_y/stride_vu: bytes per row for each plane (use frame stride fields)
   width/height: target width & height (e.g. 640x640)
   dst_rgb: output buffer must be >= width*height*3
*/
void nv21_to_rgb24_uint8(const uint8_t *src_y, const uint8_t *src_vu,
                         int stride_y, int stride_vu,
                         int width, int height,
                         uint8_t *dst_rgb)
{
    int x, y;
    for (y = 0; y < height; y++) {
        const uint8_t *py = src_y + (size_t)y * stride_y;
        const uint8_t *pvu = src_vu + (size_t)(y / 2) * stride_vu;
        uint8_t *prgb = dst_rgb + (size_t)y * width * 3;
        for (x = 0; x < width; x++) {
            int Y = (int)py[x];
            int vu_index = (x & ~1);
            int V = (int)pvu[vu_index];       /* V */
            int U = (int)pvu[vu_index + 1];   /* U */

            int C = Y - 16;
            int D = U - 128;
            int E = V - 128;
            if (C < 0) C = 0;

            int R = (298 * C + 409 * E + 128) >> 8;
            int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
            int B = (298 * C + 516 * D + 128) >> 8;

            prgb[0] = (uint8_t)clamp255(R);
            prgb[1] = (uint8_t)clamp255(G);
            prgb[2] = (uint8_t)clamp255(B);

            prgb += 3;
        }
    }
}

/* Simple BMP writer for RGB24 image (no compression). Returns 0 on success */
static int write_rgb24_to_bmp(const char *filename, const uint8_t *rgb, int width, int height)
{
    FILE *fp = fopen(filename, "wb");
    if (!fp) return -1;

    int row_bytes = width * 3;
    int pad = (4 - (row_bytes % 4)) % 4;
    int bmp_row_bytes = row_bytes + pad;

    uint32_t file_size = 54 + bmp_row_bytes * height;

    uint8_t file_hdr[14] = {0};
    file_hdr[0] = 'B'; file_hdr[1] = 'M';
    memcpy(&file_hdr[2], &file_size, 4);
    uint8_t info_hdr[40] = {0};

    uint32_t planes = 1;
    uint32_t bpp = 24;

    memcpy(&info_hdr[0], "\0\0\0\0", 4); /* will set below via memcpy */
    memcpy(&info_hdr[0], & (uint32_t){40}, 4);
    memcpy(&info_hdr[4], & (int32_t){width}, 4);
    memcpy(&info_hdr[8], & (int32_t){height}, 4);
    memcpy(&info_hdr[12], &planes, 2);
    memcpy(&info_hdr[14], &bpp, 2);

    fwrite(file_hdr, 1, 14, fp);
    fwrite(info_hdr, 1, 40, fp);

    /* BMP stores rows bottom->top. Our 'rgb' is top->bottom, so write inverted */
    uint8_t *row = malloc(bmp_row_bytes);
    if (!row) { fclose(fp); return -1; }
    for (int y = height - 1; y >= 0; y--) {
        const uint8_t *src = rgb + (size_t)y * width * 3;
        /* BMP uses BGR order */
        for (int x = 0; x < width; x++) {
            row[x*3 + 0] = src[x*3 + 2]; /* B */
            row[x*3 + 1] = src[x*3 + 1]; /* G */
            row[x*3 + 2] = src[x*3 + 0]; /* R */
        }
        if (pad) memset(row + row_bytes, 0, pad);
        fwrite(row, 1, bmp_row_bytes, fp);
    }
    free(row);
    fclose(fp);
    return 0;
}

/* === PROFILING: vpss sampling thread (修改为采集 OT_VPSS_CHN1 的帧,并做 YUV->RGB 转换) === */
/* Replace current vpss_prof_thread with this safer version */
/* Helper: convert common pixel_format codes to human-readable (partial) */
static const char *pfmt_to_str(int pfmt)
{
    switch (pfmt) {
    case OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420: return "YVU_SEMIPLANAR_420(NV21)";
    case OT_PIXEL_FORMAT_YUV_SEMIPLANAR_420: return "YUV_SEMIPLANAR_420(NV12)";
    case OT_PIXEL_FORMAT_YVU_PLANAR_420:     return "YVU_PLANAR_420(YV12)";
    case OT_PIXEL_FORMAT_YVU_PLANAR_422:     return "YVU_PLANAR_422";
    case OT_PIXEL_FORMAT_YUV_SEMIPLANAR_422: return "YUV_SEMIPLANAR_422";
    default: return "UNKNOWN";
    }
}

/* NV12 (Y + UV interleaved) -> RGB24 (same interface as nv21_to_rgb24_uint8)
   We implement a function that handles both NV12 and NV21 by a flag.
*/
void semiplanar420_to_rgb24_uint8(const uint8_t *src_y, const uint8_t *src_chroma,
                                  int stride_y, int stride_c,
                                  int width, int height,
                                  uint8_t *dst_rgb,
                                  int nv21) /* nv21=1 => chroma is VU (NV21); nv21=0 => UV (NV12) */
{
    int x, y;
    for (y = 0; y < height; y++) {
        const uint8_t *py = src_y + (size_t)y * stride_y;
        const uint8_t *pvu = src_chroma + (size_t)(y / 2) * stride_c;
        uint8_t *prgb = dst_rgb + (size_t)y * width * 3;
        for (x = 0; x < width; x++) {
            int Y = (int)py[x];
            int idx = (x & ~1);
            int U, V;
            if (nv21) { V = (int)pvu[idx]; U = (int)pvu[idx + 1]; }
            else      { U = (int)pvu[idx]; V = (int)pvu[idx + 1]; }

            int C = Y - 16;
            int D = U - 128;
            int E = V - 128;
            if (C < 0) C = 0;

            int R = (298 * C + 409 * E + 128) >> 8;
            int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
            int B = (298 * C + 516 * D + 128) >> 8;

            prgb[0] = (uint8_t)((R < 0) ? 0 : (R > 255) ? 255 : R);
            prgb[1] = (uint8_t)((G < 0) ? 0 : (G > 255) ? 255 : G);
            prgb[2] = (uint8_t)((B < 0) ? 0 : (B > 255) ? 255 : B);

            prgb += 3;
        }
    }
}

/* Safer vpss_prof_thread */
/* --- 安全版 vpss_prof_thread,直接替换你现有的实现 --- */
static void *vpss_prof_thread(void *p)
{
    vpss_prof_arg_t *arg = (vpss_prof_arg_t *)p;
    ot_video_frame_info vfi;

    uint64_t cnt = 0;
    double ema_us = 0.0;
    const double alpha = 0.1;

    while (!g_sig_flag) {
        memset(&vfi, 0, sizeof(vfi));

        uint64_t t_in_try = prof_now_us();

        if (ss_mpi_vpss_get_chn_frame(arg->grp, arg->chn, &vfi, 1000) == TD_SUCCESS) {
            uint64_t t_out = prof_now_us();
            uint64_t cost_us = (t_out >= t_in_try) ? (t_out - t_in_try) : 0;

            if ((cnt % 30) == 0) {
                printf("[VPSS PROF] grp=%d chn=%d cost=%" PRIu64 " us  (w=%u h=%u fmt=%d )\n",
                       arg->grp, arg->chn, cost_us,
                       vfi.video_frame.width, vfi.video_frame.height,
                       vfi.video_frame.pixel_format);
            }

            if (cnt == 0) ema_us = (double)cost_us;
            else ema_us = alpha * (double)cost_us + (1.0 - alpha) * ema_us;
            if ((cnt % 120) == 0 && cnt > 0) {
                printf("[VPSS PROF] EMA(%.0f samples) ~= %.1f us\n", (double)cnt, ema_us);
            }

            /* --- SAFETY CHECKS --- */
            unsigned int in_w = vfi.video_frame.width;
            unsigned int in_h = vfi.video_frame.height;
            int fmt = (int)vfi.video_frame.pixel_format;

            /* 常见字段名:vir_addr / virt_addr 等,按 SDK 修改下面两行 */
            const uint8_t *y_ptr = (const uint8_t *)vfi.video_frame.virt_addr[0];
            const uint8_t *c_ptr = (const uint8_t *)vfi.video_frame.virt_addr[1];
            int stride_y = (int)vfi.video_frame.stride[0];
            int stride_c = (int)vfi.video_frame.stride[1];

            if (in_w == 0 || in_h == 0) {
                printf("[VPSS PROF][ERR] invalid size %u x %u\n", in_w, in_h);
                ss_mpi_vpss_release_chn_frame(arg->grp, arg->chn, &vfi);
                continue;
            }
            if (!y_ptr || !c_ptr) {
                printf("[VPSS PROF][ERR] null plane ptr y=%p c=%p\n", (void*)y_ptr, (void*)c_ptr);
                ss_mpi_vpss_release_chn_frame(arg->grp, arg->chn, &vfi);
                continue;
            }
            if (stride_y <= 0 || stride_c <= 0) {
                printf("[VPSS PROF][ERR] bad stride y=%d c=%d\n", stride_y, stride_c);
                ss_mpi_vpss_release_chn_frame(arg->grp, arg->chn, &vfi);
                continue;
            }

            /* 当前示例仅处理 YVU_SEMIPLANAR_420 (NV21)。若不是该格式,打印并跳过。 */
            if (fmt != OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420) {
                printf("[VPSS PROF][WARN] unexpected px fmt=%d, skip conv\n", fmt);
                ss_mpi_vpss_release_chn_frame(arg->grp, arg->chn, &vfi);
                cnt++;
                continue;
            }

            /* 大小安全检查,防止乘法溢出或分配过大 */
            size_t pix = (size_t)in_w * (size_t)in_h;
            if (pix == 0 || pix > (size_t)2000 * 2000) { /* 保守上限:2000x2000 */
                printf("[VPSS PROF][ERR] unreasonable resolution %ux%u\n", in_w, in_h);
                ss_mpi_vpss_release_chn_frame(arg->grp, arg->chn, &vfi);
                continue;
            }

            /* 打印少量诊断信息,便于回溯问题 */
            if ((cnt % 10) == 0) {
                printf("[VPSS PROF][DBG] y_ptr=%p c_ptr=%p stride_y=%d stride_c=%d\n",
                       (void*)y_ptr, (void*)c_ptr, stride_y, stride_c);
                /* 打印每个平面首几个字节(十六进制)以便判断格式 */
                printf("[VPSS PROF][DBG] first Y bytes: %02x %02x %02x %02x\n",
                       (int)y_ptr[0], (int)y_ptr[1], (int)y_ptr[2], (int)y_ptr[3]);
                printf("[VPSS PROF][DBG] first C bytes: %02x %02x %02x %02x\n",
                       (int)c_ptr[0], (int)c_ptr[1], (int)c_ptr[2], (int)c_ptr[3]);
            }

            uint8_t *rgb = malloc(pix * 3);
            if (!rgb) {
                printf("[VPSS PROF][ERR] malloc rgb %zu failed\n", pix * 3);
                ss_mpi_vpss_release_chn_frame(arg->grp, arg->chn, &vfi);
                continue;
            }

            /* 执行转换 (NV21 -> RGB24) */
            nv21_to_rgb24_uint8(y_ptr, c_ptr, stride_y, stride_c, (int)in_w, (int)in_h, rgb);

            if (cnt < 5) {
                char fname[128];
                snprintf(fname, sizeof(fname), "vpss_frame_%ux%u_%llu.bmp", in_w, in_h, (unsigned long long)cnt);
                if (write_rgb24_to_bmp(fname, rgb, (int)in_w, (int)in_h) == 0) {
                    printf("[VPSS PROF] saved %s\n", fname);
                } else {
                    printf("[VPSS PROF] failed save %s\n", fname);
                }
            }

            free(rgb);

            cnt++;
            ss_mpi_vpss_release_chn_frame(arg->grp, arg->chn, &vfi);
        } else {
            /* no frame */
            usleep(1000);
        }
    }
    return NULL;
}




/* 信号处理 */
static td_void sample_get_char(td_void)
{
    if (g_sig_flag == 1) {
        return;
    }
    sample_pause();
}

/* FMU包装初始化 (保持原实现) */
static td_u32 sample_vio_get_fmu_wrap_num(ot_fmu_mode fmu_mode[], td_u32 len)
{
    td_u32 i;
    td_u32 cnt = 0;
    for (i = 0; i < len; i++) {
        if (fmu_mode[i] == OT_FMU_MODE_WRAP) {
            cnt++;
        }
    }
    return cnt;
}

static td_s32 sample_vio_fmu_wrap_init(sampe_sys_cfg *fmu_cfg, ot_size *in_size)
{
    td_u32 cnt;
    ot_fmu_attr fmu_attr;

    cnt = sample_vio_get_fmu_wrap_num(fmu_cfg->vi_fmu, fmu_cfg->route_num);
    if (cnt > 0) {
        fmu_attr.wrap_en = TD_TRUE;
        fmu_attr.page_num = MIN2(ot_common_get_fmu_wrap_page_num(OT_FMU_ID_VI,
            in_size->width, in_size->height) + (cnt - 1) * 3,
            OT_FMU_MAX_Y_PAGE_NUM);
    } else {
        fmu_attr.wrap_en = TD_FALSE;
    }
    if (ss_mpi_sys_set_fmu_attr(OT_FMU_ID_VI, &fmu_attr) != TD_SUCCESS) {
        return TD_FAILURE;
    }

    cnt = sample_vio_get_fmu_wrap_num(fmu_cfg->vpss_fmu, fmu_cfg->route_num);
    if (cnt > 0) {
        fmu_attr.wrap_en = TD_TRUE;
        fmu_attr.page_num = MIN2(ot_common_get_fmu_wrap_page_num(OT_FMU_ID_VPSS,
            in_size->width, in_size->height) + (cnt - 1) * 3,
            OT_FMU_MAX_Y_PAGE_NUM + OT_FMU_MAX_C_PAGE_NUM);
    } else {
        fmu_attr.wrap_en = TD_FALSE;
    }
    if (ss_mpi_sys_set_fmu_attr(OT_FMU_ID_VPSS, &fmu_attr) != TD_SUCCESS) {
        return TD_FAILURE;
    }

    return TD_SUCCESS;
}

/* 系统初始化 (保持原实现) */
static td_s32 sample_vio_sys_init(td_void)
{
    ot_vb_cfg vb_cfg;
    td_u32 supplement_config = OT_VB_SUPPLEMENT_BNR_MOT_MASK | OT_VB_SUPPLEMENT_MOTION_DATA_MASK;

    sample_comm_sys_get_default_vb_cfg(&g_vb_param, &vb_cfg);
    if (sample_comm_sys_init_with_vb_supplement(&vb_cfg, supplement_config) != TD_SUCCESS) {
        return TD_FAILURE;
    }

#ifdef SAMPLE_MEM_SHARE_ENABLE
    sample_vio_init_mem_share();
#endif

    if (sample_comm_vi_set_vi_vpss_mode(g_vio_sys_cfg.mode_type, OT_VI_AIISP_MODE_DEFAULT) != TD_SUCCESS) {
        goto sys_exit;
    }

    if (ss_mpi_sys_set_3dnr_pos(g_vio_sys_cfg.nr_pos) != TD_SUCCESS) {
        goto sys_exit;
    }

    if (sample_vio_fmu_wrap_init(&g_vio_sys_cfg, &g_vb_param.vb_size) != TD_SUCCESS) {
        goto sys_exit;
    }

    return TD_SUCCESS;
sys_exit:
    sample_comm_sys_exit();
    return TD_FAILURE;
}

/* VPSS启动/停止 (保持原实现,但确保 chn0 depth = 2) */
static td_s32 sample_vio_start_vpss(ot_vpss_grp grp, sample_vpss_cfg *vpss_cfg)
{
    td_s32 ret;
    sample_vpss_chn_attr vpss_chn_attr = {0};

    (td_void)memcpy_s(&vpss_chn_attr.chn_attr[0], sizeof(ot_vpss_chn_attr) * OT_VPSS_MAX_PHYS_CHN_NUM,
        vpss_cfg->chn_attr, sizeof(ot_vpss_chn_attr) * OT_VPSS_MAX_PHYS_CHN_NUM);

    /* === PROFILING: 确保用于采样的通道 depth >= 1(这里统一把 ch0 的 depth 调到 2)=== */
    vpss_chn_attr.chn_attr[OT_VPSS_CHN0].depth = 2;

    if (vpss_cfg->chn_en[OT_VPSS_CHN1]) {              // 如果 CHN1 已启用
        vpss_chn_attr.chn_attr[OT_VPSS_CHN1].depth = 2;
    }
    if (g_vio_sys_cfg.vpss_fmu[grp] == OT_FMU_MODE_WRAP) {
        vpss_chn_attr.chn0_wrap = TD_TRUE;
    }
    (td_void)memcpy_s(vpss_chn_attr.chn_enable, sizeof(vpss_chn_attr.chn_enable),
        vpss_cfg->chn_en, sizeof(vpss_chn_attr.chn_enable));
    vpss_chn_attr.chn_array_size = OT_VPSS_MAX_PHYS_CHN_NUM;

    ret = sample_common_vpss_start(grp, &vpss_cfg->grp_attr, &vpss_chn_attr);
    if (ret != TD_SUCCESS) {
        return ret;
    }

    if (vpss_cfg->nr_attr.enable == TD_TRUE) {
        if (ss_mpi_vpss_set_grp_3dnr_attr(grp, &vpss_cfg->nr_attr) != TD_SUCCESS) {
            goto stop_vpss;
        }
    }

    if (g_vio_sys_cfg.vpss_fmu[grp] == OT_FMU_MODE_OFF) {
        const ot_low_delay_info low_delay_info = { TD_TRUE, 200, TD_FALSE };
        if (ss_mpi_vpss_set_chn_low_delay(grp, 0, &low_delay_info) != TD_SUCCESS) {
            goto stop_vpss;
        }
    } else if (g_vio_sys_cfg.vpss_fmu[grp] == OT_FMU_MODE_DIRECT) {
        if (ss_mpi_vpss_set_chn_fmu_mode(grp, OT_VPSS_DIRECT_CHN, g_vio_sys_cfg.vpss_fmu[grp]) != TD_SUCCESS) {
            goto stop_vpss;
        }
        if (ss_mpi_vpss_enable_chn(grp, OT_VPSS_DIRECT_CHN) != TD_SUCCESS) {
            goto stop_vpss;
        }
    }

    if (g_vio_sys_cfg.mode_type != OT_VI_ONLINE_VPSS_ONLINE) {
        ot_gdc_param gdc_param = {0};
        gdc_param.in_size.width  = g_vb_param.vb_size.width;
        gdc_param.in_size.height = g_vb_param.vb_size.height;
        gdc_param.cell_size = OT_LUT_CELL_SIZE_16;
        if (ss_mpi_vpss_set_grp_gdc_param(grp, &gdc_param) != TD_SUCCESS) {
            goto stop_vpss;
        }
    }

    return TD_SUCCESS;
stop_vpss:
    sample_common_vpss_stop(grp, vpss_cfg->chn_en, OT_VPSS_MAX_PHYS_CHN_NUM);
    return TD_FAILURE;
}

static td_void sample_vio_stop_vpss(ot_vpss_grp grp)
{
    td_bool chn_enable[OT_VPSS_MAX_PHYS_CHN_NUM] = {TD_TRUE, TD_FALSE, TD_FALSE, TD_FALSE};
    sample_common_vpss_stop(grp, chn_enable, OT_VPSS_MAX_PHYS_CHN_NUM);
}

/* VENC 启动/停止, VO 启动/停止 等保持原实现 (略) */
static td_s32 sample_vio_start_venc(ot_venc_chn venc_chn[], size_t size, td_u32 chn_num)
{
    td_s32 i;
    td_s32 ret;

    if (chn_num > size) {
        return TD_FAILURE;
    }

    sample_comm_vi_get_size_by_sns_type(SENSOR0_TYPE, &g_venc_chn_param.venc_size);
    for (i = 0; i < (td_s32)chn_num; i++) {
        ret = sample_comm_venc_start(venc_chn[i], &g_venc_chn_param);
        if (ret != TD_SUCCESS) {
            goto exit;
        }
    }

    ret = sample_comm_venc_start_get_stream(venc_chn, chn_num);
    if (ret != TD_SUCCESS) {
        goto exit;
    }

    return TD_SUCCESS;

exit:
    for (i = i - 1; i >= 0; i--) {
        sample_comm_venc_stop(venc_chn[i]);
    }
    return TD_FAILURE;
}

static td_void sample_vio_stop_venc(ot_venc_chn venc_chn[], size_t size, td_u32 chn_num)
{
    td_u32 i;
    if (chn_num > size) {
        return;
    }

    sample_comm_venc_stop_get_stream(chn_num);
    for (i = 0; i < chn_num; i++) {
        sample_comm_venc_stop(venc_chn[i]);
    }
}

static td_s32 sample_vio_start_vo(sample_vo_mode vo_mode)
{
    td_s32 ret;
    g_vo_cfg.vo_mode = vo_mode;

    ret = sample_comm_vo_start_vo(&g_vo_cfg);
    if (ret != TD_SUCCESS) {
        sample_print("start vo failed with 0x%x!\n", ret);
        return ret;
    }

    printf("start vo dhd%d.\n", g_vo_cfg.vo_dev);

    if ((g_vo_cfg.vo_intf_type & OT_VO_INTF_MIPI) ||
        (g_vo_cfg.vo_intf_type & OT_VO_INTF_MIPI_SLAVE)) {
        ret = sample_comm_start_mipi_tx(&g_mipi_tx_config);
        if (ret != TD_SUCCESS) {
            sample_print("start mipi tx failed with 0x%x!\n", ret);
            return ret;
        }
    }

    return TD_SUCCESS;
}

static td_void sample_vio_stop_vo(td_void)
{
    if((g_vo_cfg.vo_intf_type & OT_VO_INTF_MIPI) ||
        (g_vo_cfg.vo_intf_type & OT_VO_INTF_MIPI_SLAVE)) {
        sample_comm_stop_mipi_tx(g_vo_cfg.vo_intf_type);
    }
    sample_comm_vo_stop_vo(&g_vo_cfg);
}

/* 绑定VENC和VO (保持原实现) */
static td_s32 sample_vio_start_venc_and_vo(ot_vpss_grp vpss_grp[], size_t size, td_u32 grp_num)
{
    td_u32 i;
    td_s32 ret;
    sample_vo_mode vo_mode = VO_MODE_1MUX;  // 单路预览模式
    const ot_vo_layer vo_layer = 0;
    ot_vo_chn vo_chn[4] = {0, 1, 2, 3};
    ot_venc_chn venc_chn[4] = {0, 1, 2, 3};

    if (grp_num > size) {
        return TD_FAILURE;
    }

    ret = sample_vio_start_venc(venc_chn, sizeof(venc_chn) / sizeof(venc_chn[0]), grp_num);
    if (ret != TD_SUCCESS) {
        goto start_venc_failed;
    }
    for (i = 0; i < grp_num; i++) {
        if (g_vio_sys_cfg.vpss_fmu[i] == OT_FMU_MODE_DIRECT) {
            sample_comm_vpss_bind_venc(vpss_grp[i], OT_VPSS_DIRECT_CHN, venc_chn[i]);
        } else {
            sample_comm_vpss_bind_venc(vpss_grp[i], OT_VPSS_CHN0, venc_chn[i]);
        }
    }

    ret = sample_vio_start_vo(vo_mode);
    if (ret != TD_SUCCESS) {
        goto start_vo_failed;
    }
    for (i = 0; i < grp_num; i++) {
        if (g_vio_sys_cfg.vpss_fmu[i] == OT_FMU_MODE_WRAP) {
            sample_comm_vpss_bind_vo(vpss_grp[i], OT_VPSS_CHN1, vo_layer, vo_chn[i]);
        } else {
            sample_comm_vpss_bind_vo(vpss_grp[i], OT_VPSS_CHN0, vo_layer, vo_chn[i]);
        }
    }

    return TD_SUCCESS;

start_vo_failed:
    for (i = 0; i < grp_num; i++) {
        if (g_vio_sys_cfg.vpss_fmu[i] == OT_FMU_MODE_DIRECT) {
            sample_comm_vpss_un_bind_venc(vpss_grp[i], OT_VPSS_DIRECT_CHN, venc_chn[i]);
        } else {
            sample_comm_vpss_un_bind_venc(vpss_grp[i], OT_VPSS_CHN0, venc_chn[i]);
        }
    }
    sample_vio_stop_venc(venc_chn, sizeof(venc_chn) / sizeof(venc_chn[0]), grp_num);
start_venc_failed:
    return TD_FAILURE;
}

static td_void sample_vio_stop_venc_and_vo(ot_vpss_grp vpss_grp[], size_t size, td_u32 grp_num)
{
    td_u32 i;
    const ot_vo_layer vo_layer = 0;
    ot_vo_chn vo_chn[4] = {0, 1, 2, 3};
    ot_venc_chn venc_chn[4] = {0, 1, 2, 3};

    if (grp_num > size) {
        return;
    }

    for (i = 0; i < grp_num; i++) {
        if (g_vio_sys_cfg.vpss_fmu[i] == OT_FMU_MODE_WRAP) {
            sample_comm_vpss_un_bind_vo(vpss_grp[i], OT_VPSS_CHN1, vo_layer, vo_chn[i]);
        } else {
            sample_comm_vpss_un_bind_vo(vpss_grp[i], OT_VPSS_CHN0, vo_layer, vo_chn[i]);
        }
        if (g_vio_sys_cfg.vpss_fmu[i] == OT_FMU_MODE_DIRECT) {
            sample_comm_vpss_un_bind_venc(vpss_grp[i], OT_VPSS_DIRECT_CHN, venc_chn[i]);
        } else {
            sample_comm_vpss_un_bind_venc(vpss_grp[i], OT_VPSS_CHN0, venc_chn[i]);
        }
    }

    sample_vio_stop_venc(venc_chn, sizeof(venc_chn) / sizeof(venc_chn[0]), grp_num);
    sample_vio_stop_vo();
}

/* 启动完整链路:在拿到默认 vpss_cfg 后,设置 chn1 为 640x640 输出 */
static td_s32 sample_vio_start_route(sample_vi_cfg *vi_cfg, sample_vpss_cfg *vpss_cfg, td_s32 route_num)
{
    td_s32 i, j, ret;
    ot_vpss_grp vpss_grp[SAMPLE_VIO_MAX_ROUTE_NUM] = {0, 1, 2, 3};

    sample_comm_vi_get_size_by_sns_type(SENSOR0_TYPE, &g_vb_param.vb_size);
    if (sample_vio_sys_init() != TD_SUCCESS) {
        return TD_FAILURE;
    }

    for (i = 0; i < route_num; i++) {
        ret = sample_comm_vi_start_vi(&vi_cfg[i]);
            if (ret != TD_SUCCESS) {
            goto start_vi_failed;
        }
    }

    /* 绑定 VI->VPSS(保持你的原流程) */
    for (i = 0; i < route_num; i++) {
        sample_comm_vi_bind_vpss(i, 0, vpss_grp[i], 0);
    }

    /* 在调用 sample_vio_start_vpss 之前,修改 vpss_cfg 使得 chn1 输出 640x640(YUV)
       注意:下面对 vpss_cfg 的字段修改基于常见 sample_vpss_cfg / ot_vpss_chn_attr 的命名
       如果你的 SDK 字段不同,请按实际结构名替换(注释已给出位置)。 */
    /* 示例修改:启用 chn1 并设定目标分辨率 640x640、YVU_SEMIPLANAR_420 */
    if (vpss_cfg) {
        /* enable channel 1 */
        vpss_cfg->chn_en[OT_VPSS_CHN1] = TD_TRUE;
        /* set output format and size for chn1 */
        vpss_cfg->chn_attr[OT_VPSS_CHN1].pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
        /* Many SDKs have size fields like .width/.height or .size.width/.size.height
           Try these common variants below (uncomment according to your SDK): */
        vpss_cfg->chn_attr[OT_VPSS_CHN1].width = (640 + X_ALIGN - 1) & ~(X_ALIGN - 1);       /* if field exists */
        vpss_cfg->chn_attr[OT_VPSS_CHN1].height = (640 + Y_ALIGN - 1) & ~(Y_ALIGN - 1);      /* if field exists */
        /* OR: vpss_cfg->chn_attr[OT_VPSS_CHN1].size.width = 640; */
        /* OR: vpss_cfg->chn_attr[OT_VPSS_CHN1].frame_size.width = 640; */
        /* 对齐(若 SDK 要求对齐) */
        /* vpss_cfg->chn_attr[OT_VPSS_CHN1].width = (vpss_cfg->chn_attr[OT_VPSS_CHN1].width + (X_ALIGN-1)) & ~(X_ALIGN-1); */
        /* vpss_cfg->chn_attr[OT_VPSS_CHN1].height = (vpss_cfg->chn_attr[OT_VPSS_CHN1].height + (Y_ALIGN-1)) & ~(Y_ALIGN-1); */

        printf("[INFO] configured VPSS chn1 for 640x640 YUV output (if SDK field names match)\n");
    }

    for (i = 0; i < route_num; i++) {
        ret = sample_vio_start_vpss(vpss_grp[i], vpss_cfg);
        if (ret != TD_SUCCESS) {
            goto start_vpss_failed;
        }
    }

    /* === PROFILING: 启动 VPSS 采样线程(以 grp0, ch1 为例) === */
    {
        static vpss_prof_arg_t arg;   /* 静态,避免栈变量失效 */
        arg.grp = vpss_grp[0];
        arg.chn = OT_VPSS_CHN1; /* 改为采样 chn1(期望为 640x640) */
        if (pthread_create(&g_prof_thread, NULL, vpss_prof_thread, &arg) != 0) {
            perror("pthread_create(vpss_prof_thread)");
        } else {
            printf("[VPSS PROF] started on grp=%d chn=%d\n", arg.grp, arg.chn);
        }
    }

    ret = sample_vio_start_venc_and_vo(vpss_grp, SAMPLE_VIO_MAX_ROUTE_NUM, route_num);
    if (ret != TD_SUCCESS) {
        goto start_venc_and_vo_failed;
    }

    return TD_SUCCESS;

start_venc_and_vo_failed:
start_vpss_failed:
    for (j = i - 1; j >= 0; j--) {
        sample_vio_stop_vpss(vpss_grp[j]);
    }
    for (i = 0; i < route_num; i++) {
        sample_comm_vi_un_bind_vpss(i, 0, vpss_grp[i], 0);
    }
start_vi_failed:
    for (j = i - 1; j >= 0; j--) {
        sample_comm_vi_stop_vi(&vi_cfg[j]);
    }
    sample_comm_sys_exit();
    return TD_FAILURE;
}

/* 停止完整链路 */
static td_void sample_vio_stop_route(sample_vi_cfg *vi_cfg, td_s32 route_num)
{
    td_s32 i;
    ot_vpss_grp vpss_grp[SAMPLE_VIO_MAX_ROUTE_NUM] = {0, 1, 2, 3};

    /* === PROFILING: 先停采样线程 === */
    if (g_prof_thread) {
        pthread_join(g_prof_thread, NULL);
        g_prof_thread = 0;
    }

    sample_vio_stop_venc_and_vo(vpss_grp, SAMPLE_VIO_MAX_ROUTE_NUM, route_num);
    for (i = 0; i < route_num; i++) {
        sample_vio_stop_vpss(vpss_grp[i]);
        sample_comm_vi_un_bind_vpss(i, 0, vpss_grp[i], 0);
        sample_comm_vi_stop_vi(&vi_cfg[i]);
    }
    sample_comm_sys_exit();
}

/* 模式选择相关 (保持原实现) */
static td_void sample_vio_print_vi_mode_list(td_bool is_wdr_mode)
{
    printf("vi vpss mode list: \n");
    printf("    (0) VI_ONLINE_VPSS_ONLINE, FMU OFF\n");
    printf("    (1) VI_ONLINE_VPSS_OFFLINE, FMU OFF\n");
    printf("    (2) VI_OFFLINE_VPSS_OFFLINE, FMU DIRECT\n");
    printf("    (3) VI_OFFLINE_VPSS_OFFLINE, FMU OFF\n");
    printf("please select mode:\n");
}

static td_void sample_vio_get_vi_vpss_mode_by_char(td_char ch, td_bool is_wdr)
{
    switch (ch) {
        case '0':
            g_vio_sys_cfg.mode_type = OT_VI_ONLINE_VPSS_ONLINE;
            g_vio_sys_cfg.vi_fmu[0] = OT_FMU_MODE_OFF;
            g_vb_param.blk_num[0] = 0;
            break;
        case '1':
            g_vio_sys_cfg.mode_type = OT_VI_ONLINE_VPSS_OFFLINE;
            g_vio_sys_cfg.vi_fmu[0] = OT_FMU_MODE_OFF;
            g_vb_param.blk_num[0] = 0;
            g_vb_param.blk_num[1] = 8;
            break;
        case '2':
            g_vio_sys_cfg.mode_type = OT_VI_OFFLINE_VPSS_OFFLINE;
            g_vio_sys_cfg.vi_fmu[0] = OT_FMU_MODE_DIRECT;
            g_vb_param.blk_num[0] = is_wdr ? 6 : 3;
            break;
        case '3':
            g_vio_sys_cfg.mode_type = OT_VI_OFFLINE_VPSS_OFFLINE;
            g_vio_sys_cfg.vi_fmu[0] = OT_FMU_MODE_OFF;
            g_vb_param.blk_num[0] = is_wdr ? 6 : 3;
            break;
        default:
            g_vio_sys_cfg.mode_type = OT_VI_ONLINE_VPSS_ONLINE;
            g_vio_sys_cfg.vi_fmu[0] = OT_FMU_MODE_OFF;
            g_vb_param.blk_num[0] = 0;
            break;
    }
}

static td_void sample_vio_get_vi_vpss_mode(td_bool is_wdr_mode)
{
    td_char ch = '0';
    td_char end_ch = '4';
    td_char input[3] = {0};
    td_s32 max_len = 3;

    sample_vio_print_vi_mode_list(is_wdr_mode);

    while (g_sig_flag == 0) {
        if (gets_s(input, max_len) != TD_NULL && strlen(input) == 1 && input[0] >= ch && input[0] <= end_ch) {
            break;
        } else {
            printf("\nInvalid param, please enter again!\n\n");
            sample_vio_print_vi_mode_list(is_wdr_mode);
        }
        (td_void)fflush(stdin);
    }

    sample_vio_get_vi_vpss_mode_by_char(input[0], is_wdr_mode);
}

/* 核心场景:单摄像头完整链路 */
static td_s32 sample_vio_all_mode(td_void)
{
    sample_vi_cfg vi_cfg[1];
    sample_vpss_cfg vpss_cfg;
    sample_sns_type sns_type = SENSOR0_TYPE;

    sample_vio_get_vi_vpss_mode(TD_FALSE);
    sample_comm_vi_get_vi_cfg_by_fmu_mode(sns_type, g_vio_sys_cfg.vi_fmu[0], &vi_cfg[0]);

    /* 获取默认 VPSS 配置并修改 chn1 输出为 640x640(见 sample_vio_start_route 中的注释) */
    sample_comm_vpss_get_default_vpss_cfg(&vpss_cfg, g_vio_sys_cfg.vpss_fmu[0]);

    if (sample_vio_start_route(vi_cfg, &vpss_cfg, g_vio_sys_cfg.route_num) != TD_SUCCESS) {
        return TD_FAILURE;
    }

    sample_get_char();  // 等待用户输入退出

    /* 通知退出(采样线程会在 join 前检查 g_sig_flag) */
    g_sig_flag = 1;
    sample_vio_stop_route(vi_cfg, g_vio_sys_cfg.route_num);
    return TD_SUCCESS;
}

/* 信号处理函数,用于捕获中断信号 */
static td_void sample_signal_handler(td_s32 signo)
{
    if (signo == SIGINT || signo == SIGTERM) {
        g_sig_flag = 1;  // 使用全局变量标记信号触发
        sample_print("catch signal %d, stop...\n", signo);
    }
}

/* 主函数入口 */
td_s32 main(td_s32 argc, td_char *argv[])
{
    td_s32 ret;

    /* 注册信号处理函数,捕获中断和终止信号 */
    signal(SIGINT, sample_signal_handler);    // 捕获Ctrl+C
    signal(SIGTERM, sample_signal_handler);   // 捕获终止信号

    printf("Sample VIO Start!\n");

    /* 启动VIO全模式功能 */
    ret = sample_vio_all_mode();  // 直接启动场景0

    printf("Sample VIO Exit!\n");
    return ret;
}
我来回答
回答5个
时间排序
认可量排序

UncleRoderick

59个粉丝

16

问答

4

专栏

20

资料

UncleRoderick 2025-08-21 13:38:10
认可0

video_frame.virt_addr不可以直接用,必须由ss_mpi_sys_mmap获取,代码可以参考\Hi3519DV500_SDK_V2.0.2.0\smp\a55_linux\source\mpp\tools\vpss_chn_dump.c

普希金的笔

0个粉丝

5

问答

0

专栏

0

资料

普希金的笔 2025-08-21 14:05:07
认可0

中午我发现了这个问题,目前写了新的函数
重新编译发生了报错:
~ # ./mpp_enc 0
Sample VIO Start!
vi vpss mode list:
(0) VI_ONLINE_VPSS_ONLINE, FMU OFF
(1) VI_ONLINE_VPSS_OFFLINE, FMU OFF
(2) VI_OFFLINE_VPSS_OFFLINE, FMU DIRECT
(3) VI_OFFLINE_VPSS_OFFLINE, FMU OFF
please select mode:
1

[MPP] Version: [HI3519DV500_MPP_V2.0.2.0 B050 Release], Build Time[Dec 20 2024, 16:23:07]

bus_id:3

linear mode

vi_pipe:0,== os04a10 24Mclk 4M30fps(MIPI) 12bit linear init success! ==

ISP Dev 0 running !
[INFO] configured VPSS chn1 for 640x640 YUV output (if SDK field names match)
[VPSS PROF] started on grp=0 chn=1
[VPSS PROF] grp=0 chn=1 cost=79829 us (w=640 h=640 fmt=38 )
[VPSS PROF][MAPPED] y_ptr=0x7f8d970100 c_ptr=0x7f8d9a1100
[VPSS PROF][MAPPED] first Y: 00 f0 0f 82
Segmentation fault
~ #

我目前是不知道该如何去解决这个问题

/**
 * VPSS 性能分析线程函数
 * 功能:从 VPSS 通道获取视频帧,测量处理耗时,并将 YUV420 半平面(NV21)数据转换为 RGB24 保存为 BMP 图像
 * 用于性能监控与图像数据验证
 */
static void *vpss_prof_thread(void *p)
{
    // 获取传入的线程参数(包含 VPSS 组号和通道号)
    vpss_prof_arg_t *arg = (vpss_prof_arg_t *)p;
    ot_video_frame_info vfi;  // 存储视频帧信息的结构体

    uint64_t cnt = 0;         // 帧计数器
    double ema_us = 0.0;      // 指数移动平均(EMA)耗时(单位:微秒)
    const double alpha = 0.1; // EMA 平滑系数,0.1 表示更关注近期值

    // 主循环:持续运行直到收到退出信号(g_sig_flag 被设置)
    while (!g_sig_flag) {
        // 清空帧信息结构体,避免残留数据
        memset(&vfi, 0, sizeof(vfi));

        // 记录尝试获取帧的起始时间(用于性能分析)
        uint64_t t_in_try = prof_now_us();

        // 尝试从指定的 VPSS 组和通道获取一帧图像,超时 1000ms
        if (ss_mpi_vpss_get_chn_frame(arg->grp, arg->chn, &vfi, 1000) == TD_SUCCESS) {
            // 成功获取帧,记录结束时间
            uint64_t t_out = prof_now_us();
            // 计算耗时(防止时钟回绕)
            uint64_t cost_us = (t_out >= t_in_try) ? (t_out - t_in_try) : 0;

            // 每 30 帧打印一次帧获取耗时和基本信息
            if ((cnt % 30) == 0) {
                printf("[VPSS PROF] grp=%d chn=%d cost=%" PRIu64 " us  (w=%u h=%u fmt=%d )\n",
                       arg->grp, arg->chn, cost_us,
                       vfi.video_frame.width, vfi.video_frame.height,
                       vfi.video_frame.pixel_format);
            }

            // 使用 EMA 计算平均耗时
            if (cnt == 0) {
                ema_us = (double)cost_us;  // 第一帧直接赋值
            } else {
                ema_us = alpha * (double)cost_us + (1.0 - alpha) * ema_us;
            }

            // 每 120 帧打印一次 EMA 平均值
            if ((cnt % 120) == 0 && cnt > 0) {
                printf("[VPSS PROF] EMA(%.0f samples) ~= %.1f us\n", (double)cnt, ema_us);
            }

            /* --- 安全检查与 mmap 映射 --- */

            // 提取图像尺寸和像素格式
            unsigned int in_w = vfi.video_frame.width;
            unsigned int in_h = vfi.video_frame.height;
            int fmt = (int)vfi.video_frame.pixel_format;

            // 获取 Y 和 C 平面的物理地址(用于 mmap 映射)
            uint64_t phys_addr_y = vfi.video_frame.phys_addr[0];
            uint64_t phys_addr_c = vfi.video_frame.phys_addr[1];

            // 获取虚拟地址偏移量(注意:不是指针,是偏移!)
            // 实际指针 = mmap 映射基址 + 此偏移
            uintptr_t offset_y = (uintptr_t)vfi.video_frame.virt_addr[0];  // 如 0x3c00
            uintptr_t offset_c = (uintptr_t)vfi.video_frame.virt_addr[1];  // 如 0x67c00

            // 获取 Y 和 C 平面的 stride(行字节数,可能大于宽度)
            int stride_y = (int)vfi.video_frame.stride[0];
            int stride_c = (int)vfi.video_frame.stride[1];

            // 安全检查 1:图像尺寸是否有效
            if (in_w == 0 || in_h == 0) {
                printf("[VPSS PROF][ERR] invalid size %u x %u\n", in_w, in_h);
                goto release;  // 错误处理:跳转到释放帧资源
            }

            // 安全检查 2:物理地址是否有效
            if (phys_addr_y == 0 || phys_addr_c == 0) {
                printf("[VPSS PROF][ERR] phys_addr_y=0x%llx phys_addr_c=0x%llx\n",
                       (unsigned long long)phys_addr_y, (unsigned long long)phys_addr_c);
                goto release;
            }

            // 安全检查 3:stride 是否合法
            if (stride_y <= 0 || stride_c <= 0) {
                printf("[VPSS PROF][ERR] bad stride y=%d c=%d\n", stride_y, stride_c);
                goto release;
            }

            // 安全检查 4:仅处理 YVU Semi-Planar 420 格式(即 NV21)
            if (fmt != OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420) {
                printf("[VPSS PROF][WARN] unexpected px fmt=%d, skip conv\n", fmt);
                goto release;  // 非目标格式,跳过转换
            }

            // 计算总像素数,用于后续内存分配
            size_t pix = (size_t)in_w * (size_t)in_h;

            // 安全检查 5:分辨率是否合理(防止过大导致内存溢出)
            if (pix == 0 || pix > (size_t)2000 * 2000) {
                printf("[VPSS PROF][ERR] unreasonable resolution %ux%u\n", in_w, in_h);
                goto release;
            }

            // 使用 ss_mpi_sys_mmap 将 Y 平面物理地址映射到用户空间
            void *base_y = ss_mpi_sys_mmap(phys_addr_y, stride_y * in_h);
            if (!base_y || base_y == MAP_FAILED) {
                printf("[VPSS PROF][ERR] ss_mpi_sys_mmap Y failed for phys=0x%llx\n",
                       (unsigned long long)phys_addr_y);
                goto release;
            }

            // 映射 C 平面(UV 交错,大小为 Y 的 1/2 高度)
            void *base_c = ss_mpi_sys_mmap(phys_addr_c, stride_c * in_h / 2);
            if (!base_c || base_c == MAP_FAILED) {
                printf("[VPSS PROF][ERR] ss_mpi_sys_mmap C failed for phys=0x%llx\n",
                       (unsigned long long)phys_addr_c);
                // 映射 C 失败时,需释放已映射的 Y
                ss_mpi_sys_munmap(base_y, stride_y * in_h);
                goto release;
            }

            // 计算真实数据指针:映射基址 + 偏移量
            const uint8_t *y_ptr = (const uint8_t *)((uintptr_t)base_y + offset_y);
            const uint8_t *c_ptr = (const uint8_t *)((uintptr_t)base_c + offset_c);

            // 每 10 帧打印一次映射后的指针地址和 Y 平面前 4 字节,用于验证映射成功
            if ((cnt % 10) == 0) {
                printf("[VPSS PROF][MAPPED] y_ptr=%p c_ptr=%p\n", (void*)y_ptr, (void*)c_ptr);
                printf("[VPSS PROF][MAPPED] first Y: %02x %02x %02x %02x\n",
                       y_ptr[0], y_ptr[1], y_ptr[2], y_ptr[3]);
            }

            // 分配 RGB24 数据缓冲区(每个像素 3 字节)
            uint8_t *rgb = malloc(pix * 3);
            if (!rgb) {
                printf("[VPSS PROF][ERR] malloc rgb %zu failed\n", pix * 3);
                // 分配失败,释放已映射的内存
                ss_mpi_sys_munmap(base_y, stride_y * in_h);
                ss_mpi_sys_munmap(base_c, stride_c * in_h / 2);
                goto release;
            }

            // 执行 YUV NV21 到 RGB24 的像素转换
            nv21_to_rgb24_uint8(y_ptr, c_ptr, stride_y, stride_c, (int)in_w, (int)in_h, rgb);

            // 保存前 3 帧为 BMP 文件(用于调试或验证)
            if (cnt < 3) {
                char fname[128];
                snprintf(fname, sizeof(fname), "frame_%ux%u_%llu.bmp", in_w, in_h, (unsigned long long)cnt);
                if (write_rgb24_to_bmp(fname, rgb, (int)in_w, (int)in_h) == 0) {
                    printf("[VPSS PROF] saved %s\n", fname);
                } else {
                    printf("[VPSS PROF] failed save %s\n", fname);
                }
            }

            // 释放 RGB 缓冲区
            free(rgb);
            cnt++;  // 帧计数递增

            // 释放 mmap 映射的内存
            ss_mpi_sys_munmap(base_y, stride_y * in_h);
            ss_mpi_sys_munmap(base_c, stride_c * in_h / 2);

        // 释放帧资源(必须调用,否则内存泄漏)
        release:
            ss_mpi_vpss_release_chn_frame(arg->grp, arg->chn, &vfi);

        } else {
            // 获取帧失败(超时或错误),休眠 1ms 后重试
            usleep(1000);
        }
    }

    return NULL;  // 线程正常退出
}

UncleRoderick

59个粉丝

16

问答

4

专栏

20

资料

UncleRoderick 2025-08-21 14:24:35
认可1

你可以用void base_y = ss_mpi_sys_mmap(phys_addr_y, stride_y in_h);获取到虚拟地址base_y后直接使用,
也可以vfi.video_frame.virt_addr[0] = ss_mpi_sys_mmap(phys_addr_y, stride_y * in_h);然后使用vfi.video_frame.virt_addr[0]

不要ss_mpi_sys_mmap映射虚拟地址给base_y后,又要加上uintptr_t offset_y = (uintptr_t)vfi.video_frame.virt_addr[0];这个操作,默认的vfi.video_frame.virt_addr[0]是垃圾值

UncleRoderick

59个粉丝

16

问答

4

专栏

20

资料

UncleRoderick 2025-08-21 14:42:29
认可1

// 计算真实数据指针:映射基址 + 偏移量
const uint8_t y_ptr = (const uint8_t )((uintptr_t)base_y + offset_y);
const uint8_t c_ptr = (const uint8_t )((uintptr_t)base_c + offset_c);
这个操作是有问题的,不要加上offset_y/offset_c

普希金的笔

0个粉丝

5

问答

0

专栏

0

资料

普希金的笔 2025-08-21 14:59:59
认可0

谢谢,问题解决了。
我待会整理一下这个过程

或将文件直接拖到这里
悬赏:
E币
网盘
* 网盘链接:
* 提取码:
悬赏:
E币

Markdown 语法

  • 加粗**内容**
  • 斜体*内容*
  • 删除线~~内容~~
  • 引用> 引用内容
  • 代码`代码`
  • 代码块```编程语言↵代码```
  • 链接[链接标题](url)
  • 无序列表- 内容
  • 有序列表1. 内容
  • 缩进内容
  • 图片![alt](url)
+ 添加网盘链接/附件

Markdown 语法

  • 加粗**内容**
  • 斜体*内容*
  • 删除线~~内容~~
  • 引用> 引用内容
  • 代码`代码`
  • 代码块```编程语言↵代码```
  • 链接[链接标题](url)
  • 无序列表- 内容
  • 有序列表1. 内容
  • 缩进内容
  • 图片![alt](url)
举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

易百纳技术社区