/* Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd. */ #include #include #include #include "securec.h" #include "ss_mpi_sys.h" #include "ss_mpi_vb.h" #include "ss_mpi_vi.h" #include "ss_mpi_vgs.h" #include "ot_buffer.h" #define MAX_FRM_CNT 64 #define MAX_FRM_WIDTH 4096 #define FILE_NAME_LENGTH 128 #define PIXEL_FORMAT_NAME_LENGTH 10 #define VI_DUMP_ARG_NUMBER_BASE 10 typedef struct { ot_vb_blk block; ot_vb_pool pool; td_u32 pool_id; td_phys_addr_t phys_addr; td_u8 *virt_addr; td_s32 m_dev; } dump_mem_buf; static volatile sig_atomic_t g_quit = TD_FALSE; /* g_quit may be set in the signal handler */ ot_video_frame_info g_frame_info; dump_mem_buf g_dump_mem = { 0 }; ot_vb_pool g_vb_pool = OT_VB_INVALID_POOL_ID; ot_vgs_handle g_vgs_handle = -1; FILE *g_pfd = TD_NULL; td_u32 g_blk_size = 0; td_char *g_user_page_addr = TD_NULL; ot_vi_chn g_vi_chn; td_s32 g_size; ot_video_frame_info g_frame[MAX_FRM_CNT]; static td_s32 g_mem_dev = -1; td_void vi_dump_set_signal(void (*func)(int)) { #ifndef __LITEOS__ struct sigaction sa = { 0 }; sa.sa_handler = func; sa.sa_flags = 0; sigaction(SIGINT, &sa, TD_NULL); sigaction(SIGTERM, &sa, TD_NULL); #endif } static td_s32 mem_dev_open(td_void) { if (g_mem_dev <= 0) { g_mem_dev = open("/dev/mem", O_CREAT | O_RDWR | O_SYNC, S_IRUSR | S_IWUSR); if (g_mem_dev < 0) { perror("Open dev/mem error"); return g_mem_dev; } } return TD_SUCCESS; } static td_s32 mem_dev_close(td_void) { td_s32 ret = TD_SUCCESS; if (g_mem_dev > 0) { ret = close(g_mem_dev); if (ret != TD_SUCCESS) { perror("Close mem/dev Fail"); } g_mem_dev = 0; } return ret; } static void usage(void) { printf("\n" "*************************************************\n" "Usage: ./vi_dump [vi_chn] [FrmCnt]\n" "1)vi_chn: \n" " which chn to be dump\n" "Default: 0\n" "2)FrmCnt: \n" " the count of frame to be dump\n" "Default: 1\n" "*)Example:\n" "e.g : ./vi_dump 0 1 (dump one YUV)\n" "*************************************************\n" "\n"); } static td_s32 vi_restore(ot_vi_chn vi_chn) { td_s32 i; for (i = 0; i < MAX_FRM_CNT; i++) { if (g_frame[i].video_frame.phys_addr[0] != 0) { ss_mpi_vi_release_frame(vi_chn, &g_frame[i]); g_frame[i].video_frame.phys_addr[0] = 0; } } if (g_vgs_handle != -1) { ss_mpi_vgs_cancel_job(g_vgs_handle); g_vgs_handle = -1; } if (g_dump_mem.virt_addr != TD_NULL) { ss_mpi_sys_munmap(g_dump_mem.virt_addr, g_blk_size); g_dump_mem.phys_addr = TD_NULL; g_dump_mem.virt_addr = TD_NULL; } if (g_dump_mem.pool != OT_VB_INVALID_POOL_ID) { ss_mpi_vb_release_blk(g_dump_mem.block); g_dump_mem.pool = OT_VB_INVALID_POOL_ID; } if (g_vb_pool != OT_VB_INVALID_POOL_ID) { ss_mpi_vb_destroy_pool(g_vb_pool); g_vb_pool = OT_VB_INVALID_POOL_ID; } if (g_pfd != TD_NULL) { fclose(g_pfd); g_pfd = TD_NULL; } return TD_SUCCESS; } static void sig_handler(td_s32 signo) { if (g_quit == TD_TRUE) { return; } if (signo == SIGINT || signo == SIGTERM) { g_quit = TD_TRUE; } } static td_s32 vi_dump_get_frame_uv_height(const ot_video_frame *video_buf, td_u32 *uv_height) { ot_pixel_format pixel_format = video_buf->pixel_format; if (pixel_format == OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420) { g_size = (video_buf->stride[0] * video_buf->height) * 3 / 2; /* 3 / 2 : 420 calc size */ *uv_height = (video_buf->height >> 1); } else if (pixel_format == OT_PIXEL_FORMAT_YVU_SEMIPLANAR_422) { g_size = ((video_buf->stride[0] * video_buf->height) << 1); *uv_height = video_buf->height; } else if (pixel_format == OT_PIXEL_FORMAT_YUV_400) { g_size = (video_buf->stride[0] * video_buf->height); *uv_height = 0; } else { printf("not support pixelformat: %d\n", pixel_format); return TD_FAILURE; } return TD_SUCCESS; } static td_void vi_dump_save_y(const ot_video_frame *video_buf, FILE *pfd) { unsigned int h; char *mem_content = TD_NULL; char *buf_virt_y = TD_NULL; buf_virt_y = g_user_page_addr; (td_void)fprintf(stderr, "saving......Y......"); (td_void)fflush(stderr); for (h = 0; h < video_buf->height; h++) { mem_content = buf_virt_y + h * video_buf->stride[0]; (td_void)(td_void)fwrite(mem_content, video_buf->width, 1, pfd); } (td_void)fflush(pfd); } static td_void vi_dump_save_u(const ot_video_frame *video_buf, FILE *pfd, td_u32 uv_height) { unsigned int w, h; char *buf_virt_y = TD_NULL; char *buf_virt_c = TD_NULL; char *mem_content = TD_NULL; unsigned char temp_buf[MAX_FRM_WIDTH]; buf_virt_y = g_user_page_addr; buf_virt_c = buf_virt_y + (video_buf->stride[0]) * (video_buf->height); (td_void)fprintf(stderr, "U......"); (td_void)fflush(stderr); for (h = 0; h < uv_height; h++) { mem_content = buf_virt_c + h * video_buf->stride[1]; mem_content += 1; for (w = 0; w < (video_buf->width >> 1); w++) { temp_buf[w] = *mem_content; mem_content += 2; /* 2bytes */ } (td_void)(td_void)fwrite(temp_buf, video_buf->width >> 1, 1, pfd); } (td_void)fflush(pfd); } static td_void vi_dump_save_v(const ot_video_frame *video_buf, FILE *pfd, td_u32 uv_height) { unsigned int w, h; char *buf_virt_y = TD_NULL; char *buf_virt_c = TD_NULL; char *mem_content = TD_NULL; unsigned char temp_buf[MAX_FRM_WIDTH]; buf_virt_y = g_user_page_addr; buf_virt_c = buf_virt_y + (video_buf->stride[0]) * (video_buf->height); (td_void)fprintf(stderr, "V......"); (td_void)fflush(stderr); for (h = 0; h < uv_height; h++) { mem_content = buf_virt_c + h * video_buf->stride[1]; for (w = 0; w < (video_buf->width >> 1); w++) { temp_buf[w] = *mem_content; mem_content += 2; /* 2bytes */ } (td_void)(td_void)fwrite(temp_buf, video_buf->width >> 1, 1, pfd); } (td_void)fflush(pfd); (td_void)fprintf(stderr, "done %d!\n", video_buf->time_ref); (td_void)fflush(stderr); } static td_s32 vi_dump_save_one_frame(const ot_video_frame *video_buf, FILE *pfd) { td_phys_addr_t phy_addr; td_u32 uv_height; /* UV height for planar format */ if (video_buf->width > MAX_FRM_WIDTH) { printf("Over max frame width: %d, can't support.\n", MAX_FRM_WIDTH); return TD_FAILURE; } if (vi_dump_get_frame_uv_height(video_buf, &uv_height) != TD_SUCCESS) { return TD_FAILURE; } phy_addr = video_buf->phys_addr[0]; g_user_page_addr = (td_char *)ss_mpi_sys_mmap(phy_addr, g_size); if (g_user_page_addr == TD_NULL) { return TD_FAILURE; } printf("stride: %d,%d\n", video_buf->stride[0], video_buf->stride[1]); /* save Y ---------------------------------------------------------------- */ vi_dump_save_y(video_buf, pfd); /* save U ---------------------------------------------------------------- */ vi_dump_save_u(video_buf, pfd, uv_height); /* save V ---------------------------------------------------------------- */ vi_dump_save_v(video_buf, pfd, uv_height); if (g_user_page_addr != TD_NULL) { ss_mpi_sys_munmap(g_user_page_addr, g_size); g_user_page_addr = TD_NULL; } return TD_SUCCESS; } static td_s32 vi_dump_create_pool(ot_vi_chn vi_chn, const ot_video_frame_info *video_frame_in, ot_vb_calc_cfg *cal_cfg) { const td_u32 align = 0; ot_compress_mode compress_mode; ot_pic_buf_attr buf_attr; ot_pixel_format pixel_format; ot_data_bit_width bit_width; ot_vb_pool_cfg vb_pool_cfg; compress_mode = OT_COMPRESS_MODE_NONE; pixel_format = video_frame_in->video_frame.pixel_format; bit_width = (video_frame_in->video_frame.dynamic_range == OT_DYNAMIC_RANGE_SDR8) ? OT_DATA_BIT_WIDTH_8 : OT_DATA_BIT_WIDTH_10; buf_attr.width = video_frame_in->video_frame.width; buf_attr.height = video_frame_in->video_frame.height; buf_attr.pixel_format = pixel_format; buf_attr.bit_width = bit_width; buf_attr.compress_mode = compress_mode; buf_attr.align = align; ot_common_get_pic_buf_cfg(&buf_attr, cal_cfg); g_blk_size = cal_cfg->vb_size; (td_void)memset_s(&vb_pool_cfg, sizeof(vb_pool_cfg), 0, sizeof(ot_vb_pool_cfg)); vb_pool_cfg.blk_size = g_blk_size; vb_pool_cfg.blk_cnt = 1; vb_pool_cfg.remap_mode = OT_VB_REMAP_MODE_NONE; g_vb_pool = ss_mpi_vb_create_pool(&vb_pool_cfg); if (g_vb_pool == OT_VB_INVALID_POOL_ID) { printf("OT_MPI_VB_CreatePool failed! \n"); vi_restore(vi_chn); return TD_FAILURE; } return TD_SUCCESS; } static td_s32 vi_dump_set_frame_info(ot_vi_chn vi_chn, ot_video_frame_info *frame_info, const ot_video_frame_info *video_frame_in, const ot_vb_calc_cfg *cal_cfg) { g_dump_mem.pool = g_vb_pool; g_dump_mem.block = ss_mpi_vb_get_blk(g_dump_mem.pool, g_blk_size, TD_NULL); if (g_dump_mem.block == OT_VB_INVALID_HANDLE) { printf("get vb blk fail\n"); return TD_FAILURE; } g_dump_mem.phys_addr = ss_mpi_vb_handle_to_phys_addr(g_dump_mem.block); g_dump_mem.virt_addr = (td_u8 *)ss_mpi_sys_mmap(g_dump_mem.phys_addr, g_blk_size); if (g_dump_mem.virt_addr == TD_NULL) { printf("Mem dev may not open\n"); vi_restore(vi_chn); return TD_FAILURE; } (td_void)memset_s(&frame_info->video_frame, sizeof(ot_video_frame), 0, sizeof(ot_video_frame)); frame_info->video_frame.phys_addr[0] = g_dump_mem.phys_addr; frame_info->video_frame.phys_addr[1] = frame_info->video_frame.phys_addr[0] + cal_cfg->main_y_size; frame_info->video_frame.virt_addr[0] = (td_void *)(td_uintptr_t)g_dump_mem.virt_addr; frame_info->video_frame.virt_addr[1] = (frame_info->video_frame.virt_addr[0] + cal_cfg->main_y_size); frame_info->video_frame.width = video_frame_in->video_frame.width; frame_info->video_frame.height = video_frame_in->video_frame.height; frame_info->video_frame.stride[0] = cal_cfg->main_stride; frame_info->video_frame.stride[1] = cal_cfg->main_stride; frame_info->video_frame.compress_mode = OT_COMPRESS_MODE_NONE; frame_info->video_frame.pixel_format = video_frame_in->video_frame.pixel_format; frame_info->video_frame.video_format = OT_VIDEO_FORMAT_LINEAR; frame_info->video_frame.dynamic_range = video_frame_in->video_frame.dynamic_range; frame_info->video_frame.pts = 0; frame_info->video_frame.time_ref = 0; frame_info->pool_id = g_vb_pool; frame_info->mod_id = OT_ID_VGS; return TD_SUCCESS; } td_void vi_dump_destroy_pool(td_void) { g_vgs_handle = -1; ss_mpi_vb_release_blk(g_dump_mem.block); g_dump_mem.pool = OT_VB_INVALID_POOL_ID; g_vgs_handle = -1; if (g_dump_mem.virt_addr != TD_NULL) { ss_mpi_sys_munmap(g_dump_mem.virt_addr, g_blk_size); g_dump_mem.phys_addr = TD_NULL; g_dump_mem.virt_addr = TD_NULL; } if (g_vb_pool != OT_VB_INVALID_POOL_ID) { ss_mpi_vb_destroy_pool(g_vb_pool); g_vb_pool = OT_VB_INVALID_POOL_ID; } } static td_s32 vi_dump_proc_save_file(ot_vi_chn vi_chn, td_bool send_to_vgs, FILE *pfd, const ot_video_frame_info *video_frame_in) { td_s32 ret; ot_vgs_task_attr vgs_task; ot_video_frame_info frame_info; ot_vb_calc_cfg cal_cfg; if (send_to_vgs) { ret = vi_dump_create_pool(vi_chn, video_frame_in, &cal_cfg); if (ret != TD_SUCCESS) { return ret; } ret = vi_dump_set_frame_info(vi_chn, &frame_info, video_frame_in, &cal_cfg); if (ret != TD_SUCCESS) { vi_restore(vi_chn); return ret; } ret = ss_mpi_vgs_begin_job(&g_vgs_handle); if (ret != TD_SUCCESS) { printf("OT_MPI_VGS_BeginJob failed\n"); g_vgs_handle = -1; vi_restore(vi_chn); return TD_FAILURE; } (td_void)memcpy_s(&vgs_task.img_in, sizeof(ot_video_frame_info), video_frame_in, sizeof(ot_video_frame_info)); (td_void)memcpy_s(&vgs_task.img_out, sizeof(ot_video_frame_info), &frame_info, sizeof(ot_video_frame_info)); ret = ss_mpi_vgs_add_scale_task(g_vgs_handle, &vgs_task, OT_VGS_SCALE_COEF_NORM); if (ret != TD_SUCCESS) { printf("ss_mpi_vgs_add_scale_task failed\n"); vi_restore(vi_chn); return TD_FAILURE; } ret = ss_mpi_vgs_end_job(g_vgs_handle); if (ret != TD_SUCCESS) { printf("ss_mpi_vgs_end_job failed\n"); vi_restore(vi_chn); return TD_FAILURE; } ret = vi_dump_save_one_frame(&frame_info.video_frame, pfd); if (ret != TD_SUCCESS) { printf("vi_dump_save_one_frame failed\n"); vi_restore(vi_chn); return TD_FAILURE; } vi_dump_destroy_pool(); } else { ret = vi_dump_save_one_frame(&video_frame_in->video_frame, pfd); } return ret; } static td_void sample_misc_prepare_restore_save_fd(td_void) { if (g_pfd != TD_NULL) { fclose(g_pfd); g_pfd = TD_NULL; } } static td_s32 sample_misc_prepare_save_video_fd(ot_vi_chn vi_chn, td_bool *send_to_vgs, td_u32 cnt) { td_char yuv_name[FILE_NAME_LENGTH] = { 0 }; td_char pixel_format[PIXEL_FORMAT_NAME_LENGTH] = {0}; ot_video_frame_info video_frame; td_s32 ret; if (g_quit == TD_TRUE) { return TD_FAILURE; } if (ss_mpi_vi_get_frame(vi_chn, &video_frame, 2000) != TD_SUCCESS) { /* 2000 : waiting time */ printf("ss_mpi_vi_get_frame err, vi chn %d \n", vi_chn); return TD_FAILURE; } *send_to_vgs = (video_frame.video_frame.compress_mode != OT_COMPRESS_MODE_NONE); /* make file name */ if (video_frame.video_frame.pixel_format == OT_PIXEL_FORMAT_YUV_400) { ret = snprintf_s(pixel_format, PIXEL_FORMAT_NAME_LENGTH, PIXEL_FORMAT_NAME_LENGTH - 1, "single"); } else if (video_frame.video_frame.pixel_format == OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420) { ret = snprintf_s(pixel_format, PIXEL_FORMAT_NAME_LENGTH, PIXEL_FORMAT_NAME_LENGTH - 1, "p420"); } else { ret = snprintf_s(pixel_format, PIXEL_FORMAT_NAME_LENGTH, PIXEL_FORMAT_NAME_LENGTH - 1, "p422"); } if (ret < 0) { printf("vi chn %d set pixel format fail\n", vi_chn); (td_void)ss_mpi_vi_release_frame(vi_chn, &video_frame); return TD_FAILURE; } ret = snprintf_s(yuv_name, sizeof(yuv_name), sizeof(yuv_name) - 1, "./vi_chn_%d_%dx%d_%s_%d.yuv", vi_chn, video_frame.video_frame.width, video_frame.video_frame.height, pixel_format, cnt); if (ret < 0) { printf("vi chn %d set yuv name fail\n", vi_chn); (td_void)ss_mpi_vi_release_frame(vi_chn, &video_frame); return TD_FAILURE; } ret = ss_mpi_vi_release_frame(vi_chn, &video_frame); if (ret != TD_SUCCESS) { printf("release frame failed\n"); return TD_FAILURE; } /* open file */ g_pfd = fopen(yuv_name, "w+b"); if (g_pfd == TD_NULL) { printf("open file failed:%d!\n", errno); return TD_FAILURE; } printf("Dump YUV frame of vi chn %d to file: \"%s\"\n", vi_chn, yuv_name); return TD_SUCCESS; } static td_void sample_misc_get_vi_frame(ot_vi_chn vi_chn, td_u32 cnt, td_u32 *frame_cnt) { td_u32 j; const td_s32 milli_sec = 2000; /* 2000 : waiting time */ for (j = 0; j < cnt; j++) { g_frame[j].video_frame.phys_addr[0] = 0; } for (*frame_cnt = 0; (*frame_cnt) < cnt; (*frame_cnt)++) { if (g_quit == TD_TRUE) { break; } if (ss_mpi_vi_get_frame(vi_chn, &g_frame[*frame_cnt], milli_sec) < 0) { printf("get vi chn %d frame err\n", vi_chn); printf("only get %d frame\n", *frame_cnt); break; } } } static td_s32 sample_misc_save_frame_to_file(ot_vi_chn vi_chn, td_bool send_to_vgs, td_u32 frame_cnt) { td_u32 j; td_s32 ret; for (j = 0; j < frame_cnt; j++) { if (g_quit == TD_TRUE) { break; } ret = vi_dump_proc_save_file(vi_chn, send_to_vgs, g_pfd, &g_frame[j]); if (ret != TD_SUCCESS) { printf("save frame %d failed", j); return TD_FAILURE; } /* release frame after using */ ss_mpi_vi_release_frame(vi_chn, &g_frame[j]); g_frame[j].video_frame.phys_addr[0] = 0; } return TD_SUCCESS; } td_s32 sample_misc_vi_dump(ot_vi_chn vi_chn, td_u32 cnt) { td_u32 frame_cnt, j; td_s32 ret = TD_SUCCESS; td_u32 old_depth = -1U; td_bool send_to_vgs; if (ss_mpi_vi_get_frame_depth(vi_chn, &old_depth)) { printf("ss_mpi_vi_get_frame_depth err, vi chn %d \n", vi_chn); return TD_FAILURE; } if (ss_mpi_vi_set_frame_depth(vi_chn, 1) != TD_SUCCESS) { printf("ss_mpi_vi_set_frame_depth err, vi chn %d \n", vi_chn); return TD_FAILURE; } usleep(90000); /* 90000 : wait depth work */ if (sample_misc_prepare_save_video_fd(vi_chn, &send_to_vgs, cnt) != TD_SUCCESS) { ret = TD_FAILURE; goto restore_depth; } /* get VI frame */ sample_misc_get_vi_frame(vi_chn, cnt, &frame_cnt); if ((g_quit == TD_TRUE) || (frame_cnt <= 0)) { goto restore_fd; } if (sample_misc_save_frame_to_file(vi_chn, cnt, frame_cnt) != TD_SUCCESS) { printf("misc_save_frame_to_file failed!\n"); } for (j = 0; j < frame_cnt; j++) { if (g_frame[j].video_frame.phys_addr[0] != 0) { ss_mpi_vi_release_frame(vi_chn, &g_frame[j]); g_frame[j].video_frame.phys_addr[0] = 0; } } restore_fd: sample_misc_prepare_restore_save_fd(); restore_depth: if (old_depth != -1U) { ss_mpi_vi_set_frame_depth(vi_chn, old_depth); } return ret; } static td_s32 vi_dump_get_argv_val(char *argv[], td_s32 index, td_s32 *val) { td_char *end_ptr = TD_NULL; td_s32 result; result = (td_s32)strtol(argv[index], &end_ptr, VI_DUMP_ARG_NUMBER_BASE); if (*end_ptr != '\0') { printf("Failure: input arg[%d] is invalid!\n", index); usage(); return TD_FAILURE; } *val = result; return TD_SUCCESS; } static td_s32 vi_dump_get_chn_num(char *argv[], td_s32 index) { td_s32 ret = TD_FAILURE; td_s32 vi_chn; ret = vi_dump_get_argv_val(argv, index, &vi_chn); if (ret != TD_SUCCESS) { return ret; } if (vi_chn < 0 || vi_chn >= OT_VI_MAX_CHN_NUM) { printf("invalid chn %d, chn range from 0 to %d\n", vi_chn, OT_VI_MAX_CHN_NUM - 1); return TD_FAILURE; } return vi_chn; } static td_s32 vi_dump_get_frame_cnt(char *argv[], td_s32 index) { td_s32 ret = TD_FAILURE; td_s32 frame_cnt; ret = vi_dump_get_argv_val(argv, index, &frame_cnt); if (ret != TD_SUCCESS) { return ret; } if (frame_cnt < 1 || frame_cnt > MAX_FRM_CNT) { printf("invalid FrmCnt %d, FrmCnt range from 1 to %d\n", frame_cnt, MAX_FRM_CNT); return TD_FAILURE; } return frame_cnt; } td_s32 main(int argc, char *argv[]) { ot_vi_chn vi_chn; td_s32 frame_cnt; printf("\nNOTICE: This tool only can be used for TESTING !!!\n"); printf("\t To see more usage, please enter: ./vi_dump -h\n\n"); if (argc > 1) { if (!strncmp(argv[1], "-h", 2)) { /* 2 help */ usage(); exit(0); } } if (argc != 3) { /* 3: 3 arg num */ usage(); exit(-1); } vi_chn = vi_dump_get_chn_num(argv, 1); if (vi_chn == TD_FAILURE) { exit(-1); } g_vi_chn = vi_chn; frame_cnt = vi_dump_get_frame_cnt(argv, 2); /* 2nd arg : frame number that need to capture */ if (frame_cnt == TD_FAILURE) { exit(-1); } if (mem_dev_open()) { exit(-1); } vi_dump_set_signal(&sig_handler); if (sample_misc_vi_dump(vi_chn, frame_cnt) != TD_SUCCESS) { printf("sample_misc_vi_dump err!\n"); } if (g_quit == TD_TRUE) { fclose(stdin); vi_restore(g_vi_chn); (td_void)mem_dev_close(); printf("\033[0;31mprogram exit abnormally!\033[0;39m\n"); exit(-1); } if (mem_dev_close()) { exit(-1); } return TD_SUCCESS; }