- 收藏
- 点赞
- 分享
- 举报
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]
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;
}
Markdown 语法
- 加粗**内容**
- 斜体*内容*
- 删除线~~内容~~
- 引用> 引用内容
- 代码`代码`
- 代码块```编程语言↵代码```
- 链接[链接标题](url)
- 无序列表- 内容
- 有序列表1. 内容
- 缩进内容
- 图片
-
2016-08-30 12:54:06
-
2025-08-26 17:24:29
-
2019-01-18 17:50:46
-
2017-10-31 03:48:52
-
2020-06-04 15:44:03
-
02019-09-18 14:53:40
-
2014-11-15 10:56:44
-
2017-01-20 14:16:46
-
2019-12-10 16:04:58
-
2025-09-04 16:26:13
-
2020-05-06 13:04:20
-
2016-10-28 15:32:24
-
2025-07-17 15:48:15
-
2018-09-16 08:52:39
-
2020-07-30 14:45:58
-
2023-04-21 16:06:25
-
2025-10-24 17:19:07
-
2017-01-21 11:15:30
-
82019-07-25 19:57:20
-
5hisi3516cv610 + gc4336p 夜晚很模糊
-
5AIISP(功能演示,SC4336P为BGGR,强制转RGGB,会导致颜色异常)
-
5rv1106使用luckfox的SDK,设备树和驱动都写好了,结果设备文件没有生成
-
5海思3516cv610中如何进行SD卡升级,根据官方文档操作,烧录进板子时,走的默认uboot,没有执行uboot升级。
-
5G610Q-IPC-38E 夜晚很暗 有什么办法解决吗 已经补光了
-
10转换模型时,SoC版本里没显示hi3516cv610芯片
-
5hisi3516cv610 使用 yolov8n 模型训练 要如何提高 这里识别的是人
-
10有人在海思平台接过SC035HGS吗
-
5关于hi3519dv500,以SD卡虚拟 U 盘操作
-
5ss928 sample_venc代码移植到openEuler24.03上执行报错 [sample_comm_vi_start_dev]-1068: vi set dev attr failed wi
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明

微信扫码分享
QQ好友