/* * Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2020. All rights reserved. * Description: encoder sample * Author: sdk * Create: 2019-08-13 */ #include #include #include #include #include #include "securec.h" #include "uapi_system.h" #include "uapi_memory.h" #include "uapi_venc.h" #undef VENC_PROPERTY_TEST #define QFRAME_NUM 6 #define VENC_MAX_CHN 32 #define FILE_PATH_LENGTH 128 #define FILE_NAME_LENGTH 64 #define FILE_MAX_LENGTH (FILE_PATH_LENGTH + FILE_NAME_LENGTH) #define RESOLUTION_ALIGN 16 #define YUV_8_BIT 1.0f #define YUV_10_BIT 1.25f #define RETRY_CNT 200 #define align_up(val, align) ((((td_u32)(val) + (align) - 1) / (align))*(align)) #define venc_err_printf(fmt, args...) printf("%s(%d): " fmt, __func__, __LINE__, ##args) #define DEBUG #ifdef DEBUG #define venc_warn_printf(fmt, args...) printf("%s(%d): " fmt, __func__, __LINE__, ##args) #else #define venc_warn_printf(fmt, args...) #endif #define sec_chk_neq_ret(func, ret) do { \ if ((func) != EOK) { \ venc_err_printf("call %s failed\n", #func); \ return ret; \ } \ } while (0) #define sec_chk_lt_ret(func, ret) do { \ if ((func) < 0) { \ venc_err_printf("call %s failed\n", #func); \ return ret; \ } \ } while (0) typedef struct { td_u32 real_chn_num; td_handle chan_id[VENC_MAX_CHN]; FILE *file_input[VENC_MAX_CHN]; FILE *file_output[VENC_MAX_CHN]; td_u32 pic_width[VENC_MAX_CHN]; td_u32 pic_height[VENC_MAX_CHN]; td_u32 time_out[VENC_MAX_CHN]; } sample_thread_info; typedef struct { td_u32 real_chn_num; td_handle chan_id; FILE *file_enc; td_u32 width; td_u32 height; td_u32 index; } queue_thread_info; typedef enum { VCODE_TYPE_H264 = 0, VCODE_TYPE_MJPEG, VCODE_TYPE_JPEG, VCODE_TYPE_H265, VCODE_TYPE_MAX } sample_vcode_type; enum { CMD_CONTINUE = -1, CMD_CHANGE_INPUT_FPS = 0, CMD_CHANGE_TARGET_FPS, CMD_CHANGE_BITRATE, CMD_CHANGE_GOP, CMD_CHANGE_MAX_QP, CMD_CHANGE_MIN_QP, CMD_CHANGE_PRIORITY, CMD_EXIT }; static pthread_t g_thread_send_frame[VENC_MAX_CHN]; queue_thread_info g_send_frame_info[VENC_MAX_CHN]; static td_bool g_send_frame_stop = TD_FALSE; static td_handle g_venc_chan_id[VENC_MAX_CHN]; static td_char g_input_file_names[VENC_MAX_CHN][FILE_MAX_LENGTH]; static td_char g_output_file_names[VENC_MAX_CHN][FILE_MAX_LENGTH]; static td_u32 g_jpeg_save_type[VENC_MAX_CHN] = {0}; uapi_mem_buf g_frame_buf[VENC_MAX_CHN][QFRAME_NUM]; typedef struct { td_handle chan_id; FILE *file_save; td_u32 time_out; td_char file_name[FILE_MAX_LENGTH]; } save_stream_thread_info; static pthread_t g_thread_save_stream[VENC_MAX_CHN]; static save_stream_thread_info g_save_stream_info[VENC_MAX_CHN]; static td_bool g_save_stream_stop = TD_FALSE; static td_s32 g_yuv_store_type = 0; static float g_yuv_bit_deep = YUV_8_BIT; typedef struct { td_u32 pic_width; td_u32 pic_height; td_u32 enc_width; td_u32 enc_height; } src_input_info; static td_void get_file_path(const td_char *file_name, td_char *path, td_s32 path_len, td_char *name, td_s32 name_len) { td_u32 file_name_len = strlen(file_name); td_s32 i = file_name_len; td_s32 cur_path_len, cur_name_len; if (path_len < 3) { /* 3: min path "./" */ printf("path len too small!\n"); return; } while (i > 0) { if (file_name[i] == '/') { break; } i--; } cur_path_len = (i > 0) ? (i + 1) : 3; /* 2: "/" + '/0' 3: "./" len */ cur_name_len = file_name_len - i + 1; if (i > 0 && cur_path_len < path_len && cur_name_len < name_len) { const td_char *cur_name = file_name + i; if (strncpy_s(path, path_len, file_name, i) != EOK) { printf("strncpy_s file path failed!"); return; } path[i] = '\0'; if (strncpy_s(name, name_len, cur_name, file_name_len - i) != EOK) { printf("strncpy_s file path failed!"); return; } name[cur_name_len - 1] = '\0'; } else { if (strncpy_s(path, path_len, "./", 3) != EOK) { /* 3: "./" len */ printf("strncpy_s file path failed!"); return; } if (strncpy_s(name, name_len, "/", 2) != EOK) { /* 2: "/" len */ printf("strncpy_s file path failed!"); return; } if (strncat_s(name, name_len, file_name, cur_name_len) != EOK) { printf("strncat_s file path failed!"); return; } } } static td_void write_bit_stream_to_file(FILE *file, const uapi_venc_stream *venc_stream) { td_u32 size; if (venc_stream->buf.size > 0) { size = (td_u32)fwrite(venc_stream->buf.data, 1, venc_stream->buf.size, file); if (size != venc_stream->buf.size) { venc_err_printf("fwrite err0, write size=%d, slclen=%d\n", size, venc_stream->buf.size); } } } static td_void *sample_save_stream_process(td_void *args) { save_stream_thread_info *thread_info = (save_stream_thread_info *)args; td_s32 ret; uapi_venc_stream venc_stream; while (!g_save_stream_stop) { ret = uapi_venc_acquire_stream(thread_info->chan_id, &venc_stream, thread_info->time_out); if (ret != TD_SUCCESS) { usleep(2 * 1000); /* 2, 1000: 2ms */ continue; } if (thread_info->file_save != TD_NULL) { /* save to file */ write_bit_stream_to_file(thread_info->file_save, &venc_stream); } else { printf("ERROR: the pFie is NULL!!\n"); } ret = uapi_venc_release_stream(thread_info->chan_id, &venc_stream); if (ret != TD_SUCCESS) { printf("uapi_venc_release_stream failed, u32Ret=%x\n", ret); } } return TD_NULL; } static td_void *sample_save_stream_process_mjpeg(td_void *args) { save_stream_thread_info *thread_info = (save_stream_thread_info *)args; td_s32 ret; td_u32 size; uapi_venc_stream venc_stream; td_handle chan_id = thread_info->chan_id; FILE *file = thread_info->file_save; while (!g_save_stream_stop) { sec_chk_neq_ret(memset_s(&venc_stream, sizeof(uapi_venc_stream), 0, sizeof(uapi_venc_stream)), TD_NULL); ret = uapi_venc_acquire_stream(chan_id, &venc_stream, 0); if (ret != TD_SUCCESS) { usleep(3 * 1000); /* 3, 1000: 3ms */ continue; } if (file != TD_NULL) { /* save to file */ size = venc_stream.buf.size; fwrite(&size, 1, sizeof(td_u32), file); write_bit_stream_to_file(file, &venc_stream); } else { printf("ERROR: the pFie is NULL!!\n"); } uapi_venc_release_stream(chan_id, &venc_stream); } return TD_NULL; } static td_u32 g_jpeg_num = 0; static td_void save_stream_to_file(const save_stream_thread_info *thread_info, const uapi_venc_stream *venc_stream) { td_s32 name_len; td_char file_name[FILE_MAX_LENGTH] = {0}; td_char name[FILE_NAME_LENGTH] = {0}; td_char path[FILE_PATH_LENGTH] = {0}; td_char file_id[FILE_NAME_LENGTH]; td_char real_path[PATH_MAX] = {0}; FILE *file = TD_NULL; if (memcpy_s(file_name, sizeof(file_name), thread_info->file_name, sizeof(thread_info->file_name)) != EOK) { printf("memcpy failed\n"); return; } if (snprintf_s(file_id, sizeof(file_id), sizeof(file_id) - 1, "_%d.jpg", g_jpeg_num) < 0) { printf("snprintf_s failed\n"); return; } if (strncat_s(file_name, sizeof(file_name), file_id, strlen(file_id)) != EOK) { printf("strncat_s failed\n"); return; } get_file_path(file_name, path, FILE_PATH_LENGTH, name, FILE_NAME_LENGTH); name_len = strlen(name) + 1; if (realpath(path, real_path) != TD_NULL) { if (strncat_s(real_path, PATH_MAX, name, name_len) != EOK) { return; } file = fopen(real_path, "wb+"); if (file != TD_NULL) { /* save to file */ write_bit_stream_to_file(file, venc_stream); } else { printf("ERROR: the pFie is NULL!!\n"); } g_jpeg_num++; if (g_jpeg_num >= 9999) { /* 9999: max jpeg number */ g_jpeg_num = 0; } } else { venc_err_printf("get real path %s failed\n", file_name); } if (file != TD_NULL) { fclose(file); file = TD_NULL; } } static td_void *sample_save_stream_process_jpeg(td_void *args) { save_stream_thread_info *thread_info = (save_stream_thread_info *)args; uapi_venc_stream venc_stream; td_handle chan_id = thread_info->chan_id; while (!g_save_stream_stop) { (td_void)memset_s(&venc_stream, sizeof(uapi_venc_stream), 0, sizeof(uapi_venc_stream)); if (uapi_venc_acquire_stream(chan_id, &venc_stream, 0) != TD_SUCCESS) { usleep(3 * 1000); /* 3, 1000: 3ms */ continue; } save_stream_to_file(thread_info, &venc_stream); uapi_venc_release_stream(chan_id, &venc_stream); } return TD_NULL; } enum { YUV_SEMIPLANNER_420_UV = 0, YUV_SEMIPLANNER_420_VU, YUV_PLANNER_420, YUV_PACKAGE_422_YUYV, YUV_PACKAGE_422_YVYU, YUV_PACKAGE_422_UYVY, COLOR_CHANNEL_ARGB_8888, YUV_SEMIPLANNER_420_UV_10BIT, YUV_SEMIPLANNER_420_VU_10BIT, YUV_SEMIPLANNER_422_UV, /* not support */ YUV_SEMIPLANNER_422_VU, /* not support */ YUV_UNKNOW_BUIT }; enum { V_U = 0, U_V = 1 }; enum { SAMPLE_VEDU_ARGB = 0, SAMPLE_VEDU_UY0VY1 = 0x8d, SAMPLE_VEDU_Y0UY1V = 0xd8, SAMPLE_VEDU_Y0VY1U = 0x78 }; typedef enum { SAMPLE_VEDU_YUV420 = 0, SAMPLE_VEDU_YUV422, SAMPLE_VEDU_YUV444, SAMPLE_VEDU_COLOR_CHANNEL } vedu_yuv_sample; typedef enum { SAMPLE_VEDU_SEMIPLANNAR = 0, SAMPLE_VEDU_PACKAGE, SAMPLE_VEDU_PLANNAR } vedu_yuv_store; #define READ_ERROR (-2) typedef struct { td_u32 width; td_u32 height; td_u32 y_stride; /* only by semiplannar, plannar, package */ td_u32 c_stride; /* only by semiplannar, plannar */ td_u8 *y_addr; /* only by plannar, semiplannar, package */ td_u8 *c_addr; /* only by plannar, semiplannar */ td_u8 *v_addr; /* only by plannar */ td_u32 y_size; td_u32 c_size; td_u32 v_size; vedu_yuv_sample sample; /* 420 422 444 */ vedu_yuv_store store; /* semiplannar, plannar, package */ td_u32 sel; } sample_yuv_info; static td_s32 get_c_width_height_by_sample(td_u32 *c_width, td_u32 *c_height, sample_yuv_info *yuv_info) { if (yuv_info->sample == SAMPLE_VEDU_YUV420) { *c_width = yuv_info->width >> 1; *c_height = yuv_info->height >> 1; } else if (yuv_info->sample == SAMPLE_VEDU_YUV422) { *c_width = yuv_info->width >> 1; *c_height = yuv_info->height >> 0; } else if (yuv_info->sample == SAMPLE_VEDU_YUV444) { *c_width = yuv_info->width >> 0; *c_height = yuv_info->height >> 0; } else { venc_err_printf("Unsupported sampling format(%d)\n", yuv_info->sample); return TD_FAILURE; } return TD_SUCCESS; } static td_s32 read_yuv_to_mem_planner(sample_yuv_info *yuv_info, FILE *yuv_file) { td_u32 c_width, c_height; td_u32 y_width = yuv_info->width; td_u32 y_height = yuv_info->height; td_s32 ret, j; td_u8 *y_ptr = yuv_info->y_addr; td_u8 *u_ptr = yuv_info->c_addr; td_u8 *v_ptr = yuv_info->v_addr; ret = get_c_width_height_by_sample(&c_width, &c_height, yuv_info); if (ret != TD_SUCCESS) { venc_err_printf("get_c_width_height_by_sample failed\n"); return TD_FAILURE; } for (j = 0; j < (td_s32)y_height; j++, y_ptr += yuv_info->y_stride) { if (fread(y_ptr, y_width, 1, yuv_file) != 1) { goto exit; } } for (j = 0; j < (td_s32)c_height; j++, u_ptr += yuv_info->c_stride) { if (fread(u_ptr, c_width, 1, yuv_file) != 1) { goto exit; } } for (j = 0; j < (td_s32)c_height; j++, v_ptr += yuv_info->c_stride) { if (fread(v_ptr, c_width, 1, yuv_file) != 1) { goto exit; } } return TD_SUCCESS; exit: printf("error:read yuv\n"); return READ_ERROR; } static td_s32 copy_uv_to_mem(sample_yuv_info *yuv_info, td_u32 c_width, td_u32 c_height) { td_s32 t0 = yuv_info->sel & 1; const td_s32 t1 = 1 - t0; td_u8 *u_ptr = yuv_info->c_addr; td_u8 *v_ptr = yuv_info->c_addr + yuv_info->c_stride / 2; /* 2: yuv store formal */ td_s32 j, i; td_u8 *uv_ptr; if ((uv_ptr = (td_u8 *)malloc(yuv_info->c_stride)) == TD_NULL) { venc_err_printf("error: malloc @ read yuv\n"); return TD_FAILURE; } for (j = 0; j < (td_s32)c_height; j++, u_ptr += yuv_info->c_stride, v_ptr += yuv_info->c_stride) { for (i = 0; i < (td_s32)c_width; i++) { uv_ptr[i * 2 + t0] = *(v_ptr + i); /* 2: yuv store formal */ uv_ptr[i * 2 + t1] = *(u_ptr + i); /* 2: yuv store formal */ } if (memcpy_s(u_ptr, yuv_info->c_size, uv_ptr, 2 * c_width) != EOK) { /* 2: yuv store formal */ venc_err_printf("memcpy failed\n"); free(uv_ptr); return TD_FAILURE; } } free(uv_ptr); return TD_SUCCESS; } static td_s32 read_yuv_to_mem_semi(sample_yuv_info *yuv_info, FILE *yuv_file) { td_u32 c_width, c_height; td_u32 y_width = yuv_info->width; td_u32 y_height = yuv_info->height; td_s32 j; td_u8 *y_ptr = yuv_info->y_addr; td_u8 *u_ptr = yuv_info->c_addr; td_u8 *v_ptr = yuv_info->v_addr; if (get_c_width_height_by_sample(&c_width, &c_height, yuv_info) != TD_SUCCESS) { venc_err_printf("get_c_width_height_by_sample failed\n"); return TD_FAILURE; } v_ptr = yuv_info->c_addr + yuv_info->c_stride / 2; /* 2: yuv store formal */ for (j = 0; j < (td_s32)y_height; j++, y_ptr += yuv_info->y_stride) { if (fread(y_ptr, y_width, 1, yuv_file) != 1) { goto exit; } } for (j = 0; j < (td_s32)c_height; j++, u_ptr += yuv_info->c_stride) { if (fread(u_ptr, c_width, 1, yuv_file) != 1) { goto exit; } } for (j = 0; j < (td_s32)c_height; j++, v_ptr += yuv_info->c_stride) { if (fread(v_ptr, c_width, 1, yuv_file) != 1) { goto exit; } } if (copy_uv_to_mem(yuv_info, c_width, c_height) != TD_SUCCESS) { venc_err_printf("copy_uv_to_mem failed\n"); return TD_FAILURE; } return TD_SUCCESS; exit: printf("error:read yuv\n"); return READ_ERROR; } static td_s32 read_uv_to_mem_package420(td_u8 *uv_ptr, sample_yuv_info *yuv_info, FILE *yuv_file) { td_u32 c_width, c_height; td_s32 j, i; td_u8 *u_ptr = yuv_info->c_addr; td_u8 *v_ptr = yuv_info->v_addr; td_s32 u0_sel = (yuv_info->sel >> 4) & 3; /* 4, 3: yuv store formal */ td_s32 v0_sel = (yuv_info->sel >> 6) & 3; /* 6, 3: yuv store formal */ if (get_c_width_height_by_sample(&c_width, &c_height, yuv_info) != TD_SUCCESS) { venc_err_printf("get_c_width_height_by_sample failed\n"); return TD_FAILURE; } u_ptr = yuv_info->y_addr + u0_sel; for (j = 0; j < (td_s32)c_height; j++, u_ptr += yuv_info->y_stride * 2) { /* 2: yuv store formal */ if (fread(uv_ptr, c_width, 1, yuv_file) != 1) { return READ_ERROR; } for (i = c_width - 1; i >= 0; i--) { *(u_ptr + i * 4) = *(uv_ptr + i); /* 4: yuv store formal */ } } v_ptr = yuv_info->y_addr + v0_sel; for (j = 0; j < (td_s32)c_height; j++, v_ptr += yuv_info->y_stride * 2) { /* 2: yuv store formal */ if (fread(uv_ptr, c_width, 1, yuv_file) != 1) { return READ_ERROR; } for (i = c_width - 1; i >= 0; i--) { *(v_ptr + i * 4) = *(uv_ptr + i); /* 4: yuv store formal */ } } return TD_SUCCESS; } static td_s32 read_yuv_to_mem_package420(sample_yuv_info *yuv_info, FILE *yuv_file) { td_s32 j, i; td_u32 y_width = yuv_info->width; td_u32 y_height = yuv_info->height; td_u8 *uv_ptr; td_u8 *y_ptr = yuv_info->y_addr; td_s32 y0_sel = (yuv_info->sel >> 0) & 3; /* 0, 3: yuv store formal */ td_s32 y1_sel = (yuv_info->sel >> 2) & 3; /* 2, 3: yuv store formal */ if ((uv_ptr = (td_u8 *)malloc(y_width)) == TD_NULL) { venc_err_printf("error: malloc @ read yuv\n"); return TD_FAILURE; } for (j = 0; j < (td_s32)y_height; j++, y_ptr += yuv_info->y_stride) { if (fread(uv_ptr, y_width, 1, yuv_file) != 1) { goto exit; } for (i = y_width / 2 - 1; i >= 0; i--) { /* 2: yuv store formal */ *(y_ptr + i * 4 + y0_sel) = *(uv_ptr + i * 2); /* 2, 4: yuv store formal */ *(y_ptr + i * 4 + y1_sel) = *(uv_ptr + i * 2 + 1); /* 2, 4: yuv store formal */ } } if (read_uv_to_mem_package420(uv_ptr, yuv_info, yuv_file) != TD_SUCCESS) { goto exit; } free(uv_ptr); return TD_SUCCESS; exit: printf("error:read yuv\n"); free(uv_ptr); return READ_ERROR; } static td_s32 read_uv_to_mem_package42x(td_u8 *uv_ptr, sample_yuv_info *yuv_info, FILE *yuv_file) { td_u32 c_width, c_height; td_u8 *u_ptr = yuv_info->c_addr; td_u8 *v_ptr = yuv_info->v_addr; td_s32 i, j; td_s32 u0_sel = (yuv_info->sel >> 4) & 3; /* 4, 3: yuv store formal */ td_s32 v0_sel = (yuv_info->sel >> 6) & 3; /* 6, 3: yuv store formal */ if (get_c_width_height_by_sample(&c_width, &c_height, yuv_info) != TD_SUCCESS) { venc_err_printf("get_c_width_height_by_sample failed\n"); return TD_FAILURE; } u_ptr = yuv_info->y_addr + u0_sel; for (j = 0; j < (td_s32)c_height; j++, u_ptr += yuv_info->y_stride) { if (j % 2 == 0) { /* 2: yuv store formal */ if (fread(uv_ptr, c_width, 1, yuv_file) != 1) { return READ_ERROR; } } for (i = c_width - 1; i >= 0; i--) { *(u_ptr + i * 4) = *(uv_ptr + i); /* 4: yuv store formal */ } } v_ptr = yuv_info->y_addr + v0_sel; for (j = 0; j < (td_s32)c_height; j++, v_ptr += yuv_info->y_stride) { if (j % 2 == 0) { /* 2: yuv store formal */ if (fread(uv_ptr, c_width, 1, yuv_file) != 1) { return READ_ERROR; } } for (i = c_width - 1; i >= 0; i--) { *(v_ptr + i * 4) = *(uv_ptr + i); /* 4: yuv store formal */ } } return TD_SUCCESS; } static td_s32 read_yuv_to_mem_package42x(sample_yuv_info *yuv_info, FILE *yuv_file) { td_s32 j, i; td_u32 y_width = yuv_info->width; td_u32 y_height = yuv_info->height; td_u8 *uv_ptr; td_u8 *y_ptr = yuv_info->y_addr; td_s32 y0_sel = (yuv_info->sel >> 0) & 3; /* 0, 3: yuv store formal */ td_s32 y1_sel = (yuv_info->sel >> 2) & 3; /* 2, 3: yuv store formal */ if ((uv_ptr = (td_u8 *)malloc(y_width)) == TD_NULL) { venc_err_printf("error: malloc @ read yuv\n"); return TD_FAILURE; } for (j = 0; j < (td_s32)y_height; j++, y_ptr += yuv_info->y_stride) { if (fread(uv_ptr, y_width, 1, yuv_file) != 1) { goto exit; } if (yuv_info->sample == SAMPLE_VEDU_YUV422) { for (i = y_width / 2 - 1; i >= 0; i--) { /* 2: yuv store formal */ *(y_ptr + i * 4 + y0_sel) = *(uv_ptr + i * 2); /* 2, 4: yuv store formal */ *(y_ptr + i * 4 + y1_sel) = *(uv_ptr + i * 2 + 1); /* 2, 4: yuv store formal */ } } else { for (i = y_width - 1; i >= 0; i--) { *(y_ptr + i * 4 + y0_sel) = *(uv_ptr + i); /* 4: yuv store formal */ } } } if (read_uv_to_mem_package42x(uv_ptr, yuv_info, yuv_file) != TD_SUCCESS) { goto exit; } free(uv_ptr); return TD_SUCCESS; exit: printf("error:read yuv\n"); free(uv_ptr); return READ_ERROR; } static td_s32 read_yuv_to_mem_package(sample_yuv_info *yuv_info, FILE *yuv_file) { td_s32 ret; if (yuv_info->sample == SAMPLE_VEDU_YUV420) { ret = read_yuv_to_mem_package420(yuv_info, yuv_file); } else if (yuv_info->sample == SAMPLE_VEDU_YUV422) { ret = read_yuv_to_mem_package42x(yuv_info, yuv_file); } else { venc_err_printf("unsupport sample type: %d\n", yuv_info->sample); return TD_FAILURE; } return ret; } static td_s32 yuvfile_to_memory(sample_yuv_info *yuv_info, FILE *yuv_file) { td_s32 ret; if (yuv_info->store == SAMPLE_VEDU_PLANNAR) { ret = read_yuv_to_mem_planner(yuv_info, yuv_file); if (ret == TD_FAILURE) { venc_err_printf("read_yuv_to_mem_planner failed\n"); } } else if (yuv_info->store == SAMPLE_VEDU_SEMIPLANNAR) { ret = read_yuv_to_mem_semi(yuv_info, yuv_file); if (ret == TD_FAILURE) { venc_err_printf("read_yuv_to_mem_semi failed\n"); } } else if (yuv_info->store == SAMPLE_VEDU_PACKAGE) { ret = read_yuv_to_mem_package(yuv_info, yuv_file); if (ret == TD_FAILURE) { venc_err_printf("read_yuv_to_mem_package failed\n"); } } else { venc_err_printf("unsupported yuv store type: %d\n", yuv_info->store); ret = TD_FAILURE; } return ret; } static td_void set_yuv_info_semi_420_uv(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { td_s32 offset_y_cb; yuv_info->y_stride = align_up(thread_info->width * g_yuv_bit_deep, RESOLUTION_ALIGN); yuv_info->c_stride = yuv_info->y_stride; offset_y_cb = yuv_info->y_stride * thread_info->height; yuv_info->sample = SAMPLE_VEDU_YUV420; yuv_info->store = SAMPLE_VEDU_SEMIPLANNAR; yuv_info->y_addr = data; yuv_info->c_addr = yuv_info->y_addr + offset_y_cb; yuv_info->v_addr = TD_NULL; yuv_info->y_size = offset_y_cb; yuv_info->c_size = buf_size - offset_y_cb; yuv_info->v_size = 0; yuv_info->sel = U_V; } static td_void set_yuv_info_semi_420_vu(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { td_s32 offset_y_cb; yuv_info->y_stride = align_up(thread_info->width * g_yuv_bit_deep, RESOLUTION_ALIGN); yuv_info->c_stride = yuv_info->y_stride; offset_y_cb = yuv_info->y_stride * thread_info->height; yuv_info->sample = SAMPLE_VEDU_YUV420; yuv_info->store = SAMPLE_VEDU_SEMIPLANNAR; yuv_info->y_addr = data; yuv_info->c_addr = yuv_info->y_addr + offset_y_cb; yuv_info->v_addr = TD_NULL; yuv_info->y_size = offset_y_cb; yuv_info->c_size = buf_size - offset_y_cb; yuv_info->v_size = 0; yuv_info->sel = V_U; } static td_void set_yuv_info_planner_420(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { td_s32 offset_y_cb; td_s32 offset_y_cr; yuv_info->y_stride = align_up(thread_info->width, 16); yuv_info->c_stride = align_up(thread_info->width / 2, 16); offset_y_cb = yuv_info->y_stride * thread_info->height; offset_y_cr = yuv_info->c_stride * thread_info->height / 2; /* 2: yuv store formal */ yuv_info->sample = SAMPLE_VEDU_YUV420; yuv_info->store = SAMPLE_VEDU_PLANNAR; yuv_info->y_addr = data; yuv_info->c_addr = yuv_info->y_addr + offset_y_cb; yuv_info->v_addr = yuv_info->c_addr + offset_y_cr; yuv_info->y_size = offset_y_cb; yuv_info->c_size = offset_y_cr; yuv_info->v_size = buf_size - offset_y_cb - offset_y_cr; yuv_info->sel = 0; } static td_void set_yuv_info_semi_422_uv(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { td_s32 offset_y_cb; yuv_info->y_stride = align_up(thread_info->width, 16); yuv_info->c_stride = align_up(thread_info->width, 16); offset_y_cb = yuv_info->y_stride * thread_info->height; yuv_info->sample = SAMPLE_VEDU_YUV422; yuv_info->store = SAMPLE_VEDU_SEMIPLANNAR; yuv_info->y_addr = data; yuv_info->c_addr = yuv_info->y_addr + offset_y_cb; yuv_info->v_addr = TD_NULL; yuv_info->y_size = offset_y_cb; yuv_info->c_size = buf_size - offset_y_cb; yuv_info->v_size = 0; yuv_info->sel = U_V; } static td_void set_yuv_info_semi_422_vu(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { td_s32 offset_y_cb; yuv_info->y_stride = align_up(thread_info->width, 16); yuv_info->c_stride = align_up(thread_info->width, 16); offset_y_cb = yuv_info->y_stride * thread_info->height; yuv_info->sample = SAMPLE_VEDU_YUV422; yuv_info->store = SAMPLE_VEDU_SEMIPLANNAR; yuv_info->y_addr = data; yuv_info->c_addr = yuv_info->y_addr + offset_y_cb; yuv_info->v_addr = TD_NULL; yuv_info->y_size = offset_y_cb; yuv_info->c_size = buf_size - offset_y_cb; yuv_info->v_size = 0; yuv_info->sel = V_U; } static td_void set_yuv_info_package_422_yuyv(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { yuv_info->y_stride = align_up(thread_info->width * 2, 16); yuv_info->c_stride = 0; // ?? yuv_info->sample = SAMPLE_VEDU_YUV422; yuv_info->store = SAMPLE_VEDU_PACKAGE; yuv_info->y_addr = data; yuv_info->c_addr = TD_NULL; yuv_info->v_addr = TD_NULL; yuv_info->y_size = buf_size; yuv_info->c_size = 0; yuv_info->v_size = 0; yuv_info->sel = SAMPLE_VEDU_Y0UY1V; // ?? } static td_void set_yuv_info_package_422_yvyu(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { yuv_info->y_stride = align_up(thread_info->width * 2, 16); yuv_info->c_stride = 0; // ?? yuv_info->sample = SAMPLE_VEDU_YUV422; yuv_info->store = SAMPLE_VEDU_PACKAGE; yuv_info->y_addr = data; yuv_info->c_addr = TD_NULL; yuv_info->v_addr = TD_NULL; yuv_info->y_size = buf_size; yuv_info->c_size = 0; yuv_info->v_size = 0; yuv_info->sel = SAMPLE_VEDU_Y0VY1U; // ?? } static td_void set_yuv_info_package_422_uyvy(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { yuv_info->y_stride = align_up(thread_info->width * 2, 16); yuv_info->c_stride = 0; // ?? yuv_info->sample = SAMPLE_VEDU_YUV422; yuv_info->store = SAMPLE_VEDU_PACKAGE; yuv_info->y_addr = data; yuv_info->c_addr = TD_NULL; yuv_info->v_addr = TD_NULL; yuv_info->y_size = buf_size; yuv_info->c_size = 0; yuv_info->v_size = 0; yuv_info->sel = SAMPLE_VEDU_UY0VY1; // ?? } static td_void set_yuv_info_argb_8888(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { yuv_info->y_stride = align_up(thread_info->width * 4, RESOLUTION_ALIGN); /* 4: argb */ yuv_info->c_stride = 0; yuv_info->sample = SAMPLE_VEDU_COLOR_CHANNEL; yuv_info->store = SAMPLE_VEDU_PACKAGE; yuv_info->y_addr = data; yuv_info->c_addr = TD_NULL; yuv_info->v_addr = TD_NULL; yuv_info->y_size = buf_size; yuv_info->c_size = 0; yuv_info->v_size = 0; yuv_info->sel = SAMPLE_VEDU_ARGB; // ?? } static td_s32 argb_file_to_memory(sample_yuv_info *file_info, queue_thread_info *thread_info) { td_u32 i; td_u8 *img_ptr = file_info->y_addr; for (i = 0; i < file_info->height; i++, img_ptr += file_info->y_stride) { if (fread(img_ptr, file_info->width * 4, 1, thread_info->file_enc) != 1) { /* 4: argb */ venc_err_printf("read error\n"); return READ_ERROR; } } return TD_SUCCESS; } static td_s32 sp420_10bit_to_memory(sample_yuv_info *yuv_info, FILE *yuv_file) { td_u8 *y_ptr = yuv_info->y_addr; td_u8 *uv_ptr = yuv_info->c_addr; td_u32 y_width = yuv_info->width * YUV_10_BIT; td_u32 y_height = yuv_info->height; td_u32 uv_width = y_width; td_u32 uv_height = y_height / 2; /* 2: sp420 uv height */ td_u32 i; for (i = 0; i < y_height; i++, y_ptr += yuv_info->y_stride) { if (fread(y_ptr, y_width, 1, yuv_file) != 1) { goto exit; } } for (i = 0; i < uv_height; i++, uv_ptr += yuv_info->c_stride) { if (fread(uv_ptr, uv_width, 1, yuv_file) != 1) { goto exit; } } return TD_SUCCESS; exit: printf("error:read yuv\n"); return READ_ERROR; } static td_u32 set_yuv_info(sample_yuv_info *yuv_info, queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { td_u32 frame_size = 0; switch (g_yuv_store_type) { case YUV_SEMIPLANNER_420_UV: case YUV_SEMIPLANNER_420_UV_10BIT: set_yuv_info_semi_420_uv(yuv_info, thread_info, data, buf_size); frame_size = yuv_info->y_stride * thread_info->height + yuv_info->c_stride * thread_info->height / 2; /* 2: yuv store formal */ break; case YUV_SEMIPLANNER_420_VU: case YUV_SEMIPLANNER_420_VU_10BIT: set_yuv_info_semi_420_vu(yuv_info, thread_info, data, buf_size); frame_size = yuv_info->y_stride * thread_info->height + yuv_info->c_stride * thread_info->height / 2; /* 2: yuv store formal */ break; case YUV_PLANNER_420: set_yuv_info_planner_420(yuv_info, thread_info, data, buf_size); frame_size = yuv_info->y_stride * thread_info->height + yuv_info->c_stride * thread_info->height; break; case YUV_SEMIPLANNER_422_UV: set_yuv_info_semi_422_uv(yuv_info, thread_info, data, buf_size); frame_size = yuv_info->y_stride * thread_info->height + yuv_info->c_stride * thread_info->height; break; case YUV_SEMIPLANNER_422_VU: set_yuv_info_semi_422_vu(yuv_info, thread_info, data, buf_size); frame_size = yuv_info->y_stride * thread_info->height + yuv_info->c_stride * thread_info->height; break; case YUV_PACKAGE_422_YUYV: set_yuv_info_package_422_yuyv(yuv_info, thread_info, data, buf_size); frame_size = yuv_info->y_stride * thread_info->height; break; case YUV_PACKAGE_422_YVYU: set_yuv_info_package_422_yvyu(yuv_info, thread_info, data, buf_size); frame_size = yuv_info->y_stride * thread_info->height; break; case YUV_PACKAGE_422_UYVY: set_yuv_info_package_422_uyvy(yuv_info, thread_info, data, buf_size); frame_size = yuv_info->y_stride * thread_info->height; break; case COLOR_CHANNEL_ARGB_8888: set_yuv_info_argb_8888(yuv_info, thread_info, data, buf_size); frame_size = yuv_info->y_stride * thread_info->height; break; } return frame_size; } static td_u32 read_buffer_from_yuv_file(queue_thread_info *thread_info, td_u8 *data, td_u32 buf_size) { sample_yuv_info yuv_info = {}; td_u32 frame_size = set_yuv_info(&yuv_info, thread_info, data, buf_size); yuv_info.width = thread_info->width; yuv_info.height = thread_info->height; if (g_yuv_store_type == COLOR_CHANNEL_ARGB_8888) { if (argb_file_to_memory(&yuv_info, thread_info) != TD_SUCCESS) { return 0; } } else if (g_yuv_store_type == YUV_SEMIPLANNER_420_UV_10BIT || g_yuv_store_type == YUV_SEMIPLANNER_420_VU_10BIT) { if (sp420_10bit_to_memory(&yuv_info, thread_info->file_enc) != TD_SUCCESS) { return 0; } } else { if (yuvfile_to_memory(&yuv_info, thread_info->file_enc) != TD_SUCCESS) { return 0; } } return frame_size; } td_u32 calc_per_frame_size(td_u32 width, td_u32 height, td_u32 format, float bit_deep) { td_u32 frame_buf_size = 0; if (format == YUV_PLANNER_420) { frame_buf_size = (align_up(width, RESOLUTION_ALIGN) + align_up(width / 2, RESOLUTION_ALIGN)) * height; /* 2: calculate frame size */ } else if (format == YUV_SEMIPLANNER_420_UV || format == YUV_SEMIPLANNER_420_VU || format == YUV_SEMIPLANNER_420_UV_10BIT || format == YUV_SEMIPLANNER_420_VU_10BIT) { frame_buf_size = align_up(width * bit_deep, RESOLUTION_ALIGN) * height * 3 / 2; /* 3, 2: calculate frame size */ } else if (format == YUV_PACKAGE_422_YUYV || format == YUV_PACKAGE_422_YVYU || format == YUV_PACKAGE_422_UYVY) { frame_buf_size = align_up(width, RESOLUTION_ALIGN) * height * 2; /* 2: calculate frame size */ } else if (format == YUV_SEMIPLANNER_422_UV || format == YUV_SEMIPLANNER_422_VU) { frame_buf_size = align_up(width, RESOLUTION_ALIGN) * height * 2; /* 2: calculate frame size */ } else if (format == COLOR_CHANNEL_ARGB_8888) { frame_buf_size = align_up(width, RESOLUTION_ALIGN) * height * 4; /* 4: argb */ } else { venc_err_printf("not support yuv store type!!!\n"); } return frame_buf_size; } td_s32 alloc_frame_buf(td_u32 chan_id, td_u32 width, td_u32 height) { uapi_mem_buf buf = {}; td_u32 i; td_u32 per_size; td_s32 ret; per_size = calc_per_frame_size(width, height, g_yuv_store_type, g_yuv_bit_deep); if (per_size == 0) { return TD_FAILURE; } sec_chk_lt_ret(snprintf_s(buf.buf_name, sizeof(buf.buf_name), sizeof(buf.buf_name) - 1, "VENC_MMZ_%d", chan_id), TD_FAILURE); buf.buf_size = per_size; for (i = 0; i < QFRAME_NUM; i++) { ret = uapi_mem_malloc(&buf); if (ret == TD_FAILURE) { printf("~~~~~~ERROR: uapi_mem_malloc(%d) Failed!! Ret:%d\n", chan_id, ret); return TD_FAILURE; } g_frame_buf[chan_id][i] = buf; } return TD_SUCCESS; } td_s32 free_frame_buf(td_u32 chan_id) { td_s32 ret; td_u32 i; for (i = 0; i < QFRAME_NUM; i++) { ret = uapi_mem_free(&g_frame_buf[chan_id][i]); if (ret == TD_FAILURE) { printf("~~~~~~ERROR: uapi_mem_free Failed!! Ret:%d\n", ret); return TD_FAILURE; } sec_chk_neq_ret(memset_s(&g_frame_buf[chan_id][i], sizeof(uapi_mem_buf), 0, sizeof(uapi_mem_buf)), TD_FAILURE); } return TD_SUCCESS; } static td_void set_frame_info_sp420(uapi_video_frame_info *frame_info, td_u32 width, td_u32 height, td_mem_handle_t buf_handle) { frame_info->frame_addr[0].stride_y = align_up((width * g_yuv_bit_deep), RESOLUTION_ALIGN); frame_info->frame_addr[0].stride_c = align_up((width * g_yuv_bit_deep), RESOLUTION_ALIGN); frame_info->frame_addr[0].start_addr = buf_handle; frame_info->frame_addr[0].y_offset = 0; frame_info->frame_addr[0].c_offset = frame_info->frame_addr[0].stride_y * height; frame_info->bit_depth = g_yuv_bit_deep == YUV_10_BIT ? UAPI_PIXEL_BIT_DEPTH_10BIT : UAPI_PIXEL_BIT_DEPTH_8BIT; if (g_yuv_store_type == YUV_SEMIPLANNER_420_VU || g_yuv_store_type == YUV_SEMIPLANNER_420_UV_10BIT) { frame_info->video_format = UAPI_FORMAT_YUV_SEMIPLANAR_420_VU; } else { frame_info->video_format = UAPI_FORMAT_YUV_SEMIPLANAR_420_UV; } } static td_void set_frame_info_planner_420(uapi_video_frame_info *frame_info, td_u32 width, td_u32 height, td_mem_handle_t buf_handle) { frame_info->frame_addr[0].stride_y = align_up(width, 16); frame_info->frame_addr[0].stride_c = align_up(width / 2, 16); frame_info->frame_addr[0].stride_cr = align_up(width / 2, 16); frame_info->frame_addr[0].start_addr = buf_handle; frame_info->frame_addr[0].y_offset = 0; frame_info->frame_addr[0].c_offset = frame_info->frame_addr[0].stride_y * height; frame_info->frame_addr[0].cr_offset = frame_info->frame_addr[0].c_offset + frame_info->frame_addr[0].stride_c * height / 2; /* 2: yuv store type */ frame_info->video_format = UAPI_FORMAT_YUV_PLANAR_420_UV; } static td_void set_frame_info_package_422(uapi_video_frame_info *frame_info, td_u32 width, td_mem_handle_t buf_handle) { frame_info->frame_addr[0].stride_y = align_up(width * 2, 16); frame_info->frame_addr[0].start_addr = buf_handle; frame_info->frame_addr[0].y_offset = 0; if (g_yuv_store_type == YUV_PACKAGE_422_YUYV) { frame_info->video_format = UAPI_FORMAT_YUV_PACKAGE_YUYV; } else if (g_yuv_store_type == YUV_PACKAGE_422_YVYU) { frame_info->video_format = UAPI_FORMAT_YUV_PACKAGE_YVYU; } else { frame_info->video_format = UAPI_FORMAT_YUV_PACKAGE_UYVY; } } static td_void set_frame_info_argb8888(uapi_video_frame_info *frame_info, td_u32 width, td_mem_handle_t buf_handle) { frame_info->frame_addr[0].stride_y = align_up(width * 4, RESOLUTION_ALIGN); /* 4: argb */ frame_info->frame_addr[0].start_addr = buf_handle; frame_info->frame_addr[0].y_offset = 0; frame_info->video_format = UAPI_FORMAT_ARGB8888; } static td_void set_video_frame_info(uapi_video_frame_info *frame_info, queue_thread_info *thread_info, uapi_mem_buf *buf) { frame_info->display_width = thread_info->width; frame_info->display_height = thread_info->height; frame_info->decode_width = thread_info->width; frame_info->decode_height = thread_info->height; frame_info->scan_type = UAPI_VIDEO_SCAN_TYPE_PROGRESSIVE; frame_info->field_mode = UAPI_VIDEO_FIELD_ALL; frame_info->frame_packing_type = UAPI_FRAME_PACKING_TYPE_2D; if (g_yuv_store_type == YUV_SEMIPLANNER_420_UV || g_yuv_store_type == YUV_SEMIPLANNER_420_VU || g_yuv_store_type == YUV_SEMIPLANNER_420_UV_10BIT || g_yuv_store_type == YUV_SEMIPLANNER_420_VU_10BIT) { set_frame_info_sp420(frame_info, thread_info->width, thread_info->height, buf->buf_handle); } else if (g_yuv_store_type == YUV_PLANNER_420) { set_frame_info_planner_420(frame_info, thread_info->width, thread_info->height, buf->buf_handle); } else if (g_yuv_store_type == YUV_PACKAGE_422_YUYV || g_yuv_store_type == YUV_PACKAGE_422_YVYU || g_yuv_store_type == YUV_PACKAGE_422_UYVY) { set_frame_info_package_422(frame_info, thread_info->width, buf->buf_handle); } else if (g_yuv_store_type == COLOR_CHANNEL_ARGB_8888) { set_frame_info_argb8888(frame_info, thread_info->width, buf->buf_handle); } } static uapi_mem_buf *find_memory_buffer(uapi_video_frame_info *frame_info_release, td_u32 index) { int i; uapi_mem_buf *buf_info = TD_NULL; for (i = 0; i < QFRAME_NUM; i++) { if (g_frame_buf[index][i].buf_handle == frame_info_release->frame_addr[0].start_addr) { buf_info = &g_frame_buf[index][i]; break; } } if (i == QFRAME_NUM) { printf("Can not find corresponding buffer\n"); return TD_NULL; } return buf_info; } static td_s64 g_pts = 1; static td_void send_frame_before_start(queue_thread_info *thread_info) { td_u32 queue_id; uapi_video_frame_info frame_info = {0}; td_u32 read_len; uapi_mem_buf *buf_info = TD_NULL; td_s32 ret; for (queue_id = 0; queue_id < QFRAME_NUM && thread_info->index < VENC_MAX_CHN; queue_id++) { buf_info = &g_frame_buf[thread_info->index][queue_id]; read_len = read_buffer_from_yuv_file(thread_info, buf_info->user_viraddr, (td_u32)buf_info->buf_size); if (read_len == 0) { printf("read vid file error!\n"); rewind(thread_info->file_enc); continue; } set_video_frame_info(&frame_info, thread_info, buf_info); frame_info.pts = g_pts++; ret = uapi_venc_queue_frame(thread_info->chan_id, &frame_info); if (ret != TD_SUCCESS) { venc_err_printf("uapi_venc_queue_frame failed!, queue_id = %d\n", queue_id); break; } } } #ifndef VENC_PROPERTY_TEST static td_s32 read_next_frame_to_memory(uapi_mem_buf *buf_info, queue_thread_info *thread_info) { td_u32 read_len; read_len = read_buffer_from_yuv_file(thread_info, buf_info->user_viraddr, (td_u32)buf_info->buf_size); if (read_len == 0) { printf("read vid file error!\n"); rewind(thread_info->file_enc); read_len = read_buffer_from_yuv_file(thread_info, buf_info->user_viraddr, (td_u32)buf_info->buf_size); if (read_len == 0) { printf("ERROR :: miss one Framebuffer!!\n"); return TD_FAILURE; } } return TD_SUCCESS; } #endif td_void *sample_send_frame_process(td_void *args) { td_s32 ret; uapi_video_frame_info frame_info_release = {0}; queue_thread_info *thread_info = (queue_thread_info *)args; uapi_mem_buf *buf_info = TD_NULL; if (thread_info == TD_NULL) { venc_err_printf("thread_info is null!\n"); return TD_NULL; } send_frame_before_start(thread_info); while (!g_send_frame_stop) { td_u32 retry_cnt = 0; do { ret = uapi_venc_dequeue_frame(thread_info->chan_id, &frame_info_release); retry_cnt++; usleep(5 * 1000); /* 5, 1000: 5ms */ } while (ret != TD_SUCCESS && retry_cnt < RETRY_CNT && (!g_send_frame_stop)); if (retry_cnt >= RETRY_CNT) { venc_warn_printf("dequeue timeout, restart send frame\n"); g_pts = 1; send_frame_before_start(thread_info); continue; } buf_info = find_memory_buffer(&frame_info_release, thread_info->index); if (buf_info == TD_NULL) { venc_err_printf("find_memory_buffer failed\n"); continue; } #ifndef VENC_PROPERTY_TEST ret = read_next_frame_to_memory(buf_info, thread_info); if (ret != TD_SUCCESS) { continue; } #endif frame_info_release.pts = g_pts++; if (g_send_frame_stop != TD_TRUE) { ret = uapi_venc_queue_frame(thread_info->chan_id, &frame_info_release); if (ret != TD_SUCCESS) { printf("QueueFrame err!ret = %x\n", ret); continue; } } } return TD_NULL; } static td_s32 venc_create(uapi_venc_attr* venc_attr, td_handle* venc_handle) { td_s32 ret; td_handle chan_id; if (venc_handle == TD_NULL) { printf("%s,%d,bad venc_handle !\n", __func__, __LINE__); return TD_FAILURE; } ret = uapi_venc_create(&chan_id, venc_attr); if (ret != TD_SUCCESS) { printf("UAPI_VENC_CreateChn failed!!\n"); uapi_venc_deinit(); return ret; } *venc_handle = chan_id; return ret; } static td_s32 venc_start(td_handle chan_id) { td_s32 ret; ret = uapi_venc_start(chan_id); if (ret != TD_SUCCESS) { uapi_venc_destroy(chan_id); uapi_venc_deinit(); return ret; } return ret; } static td_s32 venc_thread_create(sample_thread_info *input) { td_u32 i; if (input == TD_NULL) { venc_err_printf("bad input param\n"); return TD_FAILURE; } for (i = 0; i < input->real_chn_num; i++) { g_save_stream_info[i].chan_id = input->chan_id[i]; g_save_stream_info[i].file_save = input->file_output[i]; g_save_stream_info[i].time_out = input->time_out[i]; if (g_jpeg_save_type[i] == VCODE_TYPE_JPEG) { sec_chk_neq_ret(memcpy_s(g_save_stream_info[i].file_name, sizeof(g_save_stream_info[i].file_name), g_output_file_names[i], sizeof(g_output_file_names[i])), TD_FAILURE); } } g_save_stream_stop = TD_FALSE; for (i = 0; i < input->real_chn_num; i++) { if (g_jpeg_save_type[i] == VCODE_TYPE_MJPEG) { pthread_create(&g_thread_save_stream[i], TD_NULL, sample_save_stream_process_mjpeg, (td_void *)(&g_save_stream_info[i])); } else if ((g_jpeg_save_type[i] == VCODE_TYPE_H264) || (g_jpeg_save_type[i] == VCODE_TYPE_H265)) { pthread_create(&g_thread_save_stream[i], TD_NULL, sample_save_stream_process, (td_void *)(&g_save_stream_info[i])); } else if (g_jpeg_save_type[i] == VCODE_TYPE_JPEG) { /* jpeg */ pthread_create(&g_thread_save_stream[i], TD_NULL, sample_save_stream_process_jpeg, (td_void *)(&g_save_stream_info[i])); } usleep(2 * 1000); /* 2, 1000: 2ms */ } for (i = 0; i < input->real_chn_num; i++) { g_send_frame_info[i].chan_id = input->chan_id[i]; g_send_frame_info[i].width = input->pic_width[i]; g_send_frame_info[i].height = input->pic_height[i]; g_send_frame_info[i].file_enc = input->file_input[i]; } g_send_frame_stop = TD_FALSE; for (i = 0; i < input->real_chn_num; i++) { g_send_frame_info[i].index = i; pthread_create(&g_thread_send_frame[i], TD_NULL, sample_send_frame_process, (td_void *)(&g_send_frame_info[i])); usleep(2 * 1000); /* 2, 1000: 2ms */ } return TD_SUCCESS; } static td_void venc_thread_stop(td_u32 chn_num) { td_u32 i; if (!g_save_stream_stop) { g_save_stream_stop = TD_TRUE; for (i = 0; i < chn_num; i++) { pthread_join(g_thread_save_stream[i], TD_NULL); } } if (!g_send_frame_stop) { g_send_frame_stop = TD_TRUE; for (i = 0; i < chn_num; i++) { pthread_join(g_thread_send_frame[i], TD_NULL); } } printf("Thread stop OK!!\n"); } static td_s32 venc_stop(td_handle chan_id) { return uapi_venc_stop(chan_id); } static td_s32 venc_destroy(td_handle chan_id) { return uapi_venc_destroy(chan_id); } static td_void loop_function(td_void) { while (TD_TRUE) { td_char input_cmd; printf("\n************* input 'q' to exit! **************\n"); fflush(stdin); do { if (scanf_s("%c", &input_cmd, 1) < 0) { usleep(50000); /* 50000: sleep 50ms */ continue; } } while (input_cmd == '\n'); if (input_cmd == 'q' || input_cmd == 'Q') { break; } usleep(50000); /* 50000: sleep 50ms */ } } typedef struct { td_u32 width; td_u32 height; td_u32 priority; uapi_vcodec_type venc_type; } channel_info; static td_s32 set_output_file_name(uapi_vcodec_type *venc_type, td_u32 i) { td_s32 ret; td_char file_suffix[FILE_NAME_LENGTH]; if (g_jpeg_save_type[i] == VCODE_TYPE_MJPEG) { *venc_type = UAPI_VCODEC_TYPE_MJPEG; ret = snprintf_s(file_suffix, sizeof(file_suffix), sizeof(file_suffix) - 1, "_chn%02d.mjpeg", i); } else if (g_jpeg_save_type[i] == VCODE_TYPE_H264) { *venc_type = UAPI_VCODEC_TYPE_H264; ret = snprintf_s(file_suffix, sizeof(file_suffix), sizeof(file_suffix) - 1, "_chn%02d.h264", i); } else if (g_jpeg_save_type[i] == VCODE_TYPE_JPEG) { *venc_type = UAPI_VCODEC_TYPE_MJPEG; ret = snprintf_s(file_suffix, sizeof(file_suffix), sizeof(file_suffix) - 1, "_chn%02d", i); } else { *venc_type = UAPI_VCODEC_TYPE_H265; ret = snprintf_s(file_suffix, sizeof(file_suffix), sizeof(file_suffix) - 1, "_chn%02d.h265", i); } sec_chk_lt_ret(ret, TD_FAILURE); sec_chk_neq_ret(strncat_s(g_output_file_names[i], sizeof(g_output_file_names[i]), file_suffix, strlen(file_suffix)), TD_FAILURE); return TD_SUCCESS; } static td_s32 config_venc_channel_info(channel_info (*chan_info_ptr)[VENC_MAX_CHN], td_u32 i, sample_thread_info *thread_arg) { channel_info *chan_info = *chan_info_ptr; if (i >= VENC_MAX_CHN) { venc_err_printf("i(%d) >= VENC_MAX_CHN", i); return TD_FAILURE; } if (set_output_file_name(&chan_info[i].venc_type, i) != TD_SUCCESS) { venc_err_printf("set_input_output_file_name failed\n"); return TD_FAILURE; } printf("input the Picture width of channel[%d]:", i); fflush(stdin); sec_chk_lt_ret(scanf_s("%d", &thread_arg->pic_width[i]), TD_FAILURE); printf("input the Picture height of channel[%d]:", i); fflush(stdin); sec_chk_lt_ret(scanf_s("%d", &thread_arg->pic_height[i]), TD_FAILURE); printf("\ninput the Encode width of channel[%d]:", i); fflush(stdin); sec_chk_lt_ret(scanf_s("%d", &chan_info[i].width), TD_FAILURE); printf("input the Encode height of channel[%d]:", i); fflush(stdin); sec_chk_lt_ret(scanf_s("%d", &chan_info[i].height), TD_FAILURE); printf("input the priority of channel[%d]:", i); fflush(stdin); sec_chk_lt_ret(scanf_s("%d", &chan_info[i].priority), TD_FAILURE); return TD_SUCCESS; } static td_s32 init_input_file_info(const td_char *input_file_name, FILE **file) { FILE *file_input = TD_NULL; td_char real_path[PATH_MAX] = {}; if (realpath(input_file_name, real_path) == TD_NULL) { venc_err_printf("get real path %s failed\n", input_file_name); return TD_FAILURE; } file_input = fopen(real_path, "rb"); if (file_input == TD_NULL) { venc_err_printf("ERROR:open file :%s failed!!\n", input_file_name); return TD_FAILURE; } *file = file_input; return TD_SUCCESS; } static td_void deinit_file_info(FILE *file) { if (file != TD_NULL) { fclose(file); } } static td_s32 init_output_file_info(const td_char *output_file_name, td_u32 type, FILE **file) { FILE *file_output = TD_NULL; td_char file_path[FILE_PATH_LENGTH] = {}; td_char file_name[FILE_NAME_LENGTH] = {}; td_char real_path[PATH_MAX] = {}; td_u32 name_len; if (type != VCODE_TYPE_JPEG) { get_file_path(output_file_name, file_path, FILE_PATH_LENGTH, file_name, FILE_NAME_LENGTH); if (realpath(file_path, real_path) == TD_NULL) { venc_err_printf("get real path %s failed\n", output_file_name); return TD_FAILURE; } name_len = strlen(file_name) + 1; if (strncat_s(real_path, PATH_MAX, file_name, name_len) != EOK) { venc_err_printf("get real file name %s failed\n", output_file_name); return TD_FAILURE; } file_output = fopen(real_path, "wb+"); if (file_output == TD_NULL) { venc_err_printf("ERROR:open file :%s failed!!\n", real_path); return TD_FAILURE; } *file = file_output; } return TD_SUCCESS; } static td_u32 set_thread_info_user_defined(channel_info (*chan_info_ptr)[VENC_MAX_CHN], sample_thread_info *thread_arg) { td_u32 chn_num, i; td_char input_file_name[VENC_MAX_CHN][FILE_MAX_LENGTH] = {}; printf("input the num of VENC channel:"); fflush(stdin); sec_chk_lt_ret(scanf_s("%d", &chn_num), 0); if (chn_num > VENC_MAX_CHN) { printf("ERROR:: the num of VENC channel is invalid !(should < %d)\n", VENC_MAX_CHN); return 0; } thread_arg->real_chn_num = chn_num; for (i = 0; i < chn_num; i++) { printf("input the channel %d encode type (0->h264 1->mjpeg 2->jpeg 3->h265) :", i); fflush(stdin); sec_chk_lt_ret(scanf_s("%d", &g_jpeg_save_type[i]), 0); printf("\ninput the Input Filename of channel[%d]:", i); fflush(stdin); sec_chk_lt_ret(scanf_s("%s", input_file_name[i], sizeof(input_file_name[i])), 0); if (init_input_file_info(input_file_name[i], &thread_arg->file_input[i]) != TD_SUCCESS) { return 0; } if (config_venc_channel_info(chan_info_ptr, i, thread_arg) != TD_SUCCESS) { venc_err_printf("config_venc_channel_info failed"); return 0; } sec_chk_neq_ret(strncpy_s(g_output_file_names[i], sizeof(g_output_file_names[i]), input_file_name[i], (strlen(input_file_name[i]) - 4)), 0); /* 4: delete .yuv suffix */ if (init_output_file_info(g_output_file_names[i], g_jpeg_save_type[i], &thread_arg->file_output[i]) != TD_SUCCESS) { return 0; } } return chn_num; } static td_s32 get_encode_type(const td_char *code_type) { if (!strcmp(code_type, "H264") || !strcmp(code_type, "h264")) { g_jpeg_save_type[0] = VCODE_TYPE_H264; printf("H264 Type is success!\n"); } else if (!strcmp(code_type, "H265") || !strcmp(code_type, "h265")) { g_jpeg_save_type[0] = VCODE_TYPE_H265; printf("H265 Type is success!\n"); } else if (!strcmp(code_type, "JPG") || !strcmp(code_type, "jpg")) { g_jpeg_save_type[0] = VCODE_TYPE_JPEG; printf("JPG Type is success!\n"); } else { printf("Please input the VencType H264, H265, or JPG!\n"); return TD_FAILURE; } return TD_SUCCESS; } static td_u32 set_thread_info_defualt(channel_info (*chan_info_ptr)[VENC_MAX_CHN], sample_thread_info *thread_arg, td_char *argv[]) { channel_info *chan_info = *chan_info_ptr; td_char *file_name = argv[1]; td_char *width = argv[2]; /* 2: second param */ td_char *height = argv[3]; /* 3: third param */ td_u32 file_name_len = strlen(file_name); thread_arg->real_chn_num = 1; thread_arg->pic_width[0] = (td_u32)strtol(width, TD_NULL, 0); thread_arg->pic_height[0] = (td_u32)strtol(height, TD_NULL, 0); chan_info[0].width = (td_u32)strtol(width, TD_NULL, 0); chan_info[0].height = (td_u32)strtol(height, TD_NULL, 0); chan_info[0].priority = 0; if (get_encode_type(argv[4]) != TD_SUCCESS) { /* 4: code type index */ return 0; } if (file_name_len == 0) { return 0; } sec_chk_neq_ret(strncpy_s(g_input_file_names[0], sizeof(g_input_file_names[0]), file_name, file_name_len), 0); sec_chk_neq_ret(strncpy_s(g_output_file_names[0], sizeof(g_output_file_names[0]), file_name, file_name_len - 4), 0); /* 4: delete .yuv suffix */ if (set_output_file_name(&chan_info[0].venc_type, 0) != TD_SUCCESS) { return 0; } if (init_input_file_info(g_input_file_names[0], &thread_arg->file_input[0]) != TD_SUCCESS) { return 0; } if (init_output_file_info(g_output_file_names[0], g_jpeg_save_type[0], &thread_arg->file_output[0]) != TD_SUCCESS) { goto err; } return thread_arg->real_chn_num; err: deinit_file_info(thread_arg->file_input[0]); thread_arg->file_input[0] = TD_NULL; return 0; } static td_s32 venc_channel_start_encode(channel_info *chan_info, td_u32 chn_num, sample_thread_info *thread_arg) { td_u32 i; uapi_venc_attr venc_attr = {0}; for (i = 0; i < chn_num; i++) { uapi_venc_get_default_attr(&venc_attr); venc_attr.venc_type = chan_info[i].venc_type; venc_attr.max_width = chan_info[i].width; venc_attr.max_height = chan_info[i].height; venc_attr.stream_buf_size = venc_attr.max_width * venc_attr.max_height * 3 / 2; /* 3, 2: 1.5x resolution */ venc_attr.slice_split.enable = TD_FALSE; venc_attr.slice_split.mode = UAPI_VENC_SPLIT_MAX; venc_attr.slice_split.size = 0; venc_attr.gop_mode = UAPI_VENC_GOP_MODE_NORMALP; venc_attr.config.width = chan_info[i].width; venc_attr.config.height = chan_info[i].height; venc_attr.config.target_frame_rate = 25 * 1000; /* 25, 1000: 25.000 frame per second */ venc_attr.config.input_frame_rate = 25 * 1000; /* 25, 1000: 25.000 frame per second */ venc_attr.config.priority = chan_info[i].priority; venc_attr.config.gop = 100; /* 100: gop length */ venc_attr.config.sp_interval = 0; venc_attr.config.input_frame_rate_type = UAPI_VENC_FRAME_RATE_TYPE_USER; if (venc_attr.config.width > 2160 || venc_attr.config.height > 2160) { /* 2160: 2K */ venc_attr.config.target_bitrate = 8 * 1024 * 1024; /* 8, 1024: Recommended bit rate */ } else if (venc_attr.config.width > 1920 || venc_attr.config.height > 1088) { /* 1920x1088 */ venc_attr.config.target_bitrate = 5 * 1024 * 1024; /* 5, 1024: Recommended bit rate */ } else if (venc_attr.config.width > 1280) { /* 1280 resolusion */ venc_attr.config.target_bitrate = 5 * 1024 * 1024; /* 5, 1024: Recommended bit rate */ } else if (venc_attr.config.width > 720) { /* 720 resolusion */ venc_attr.config.target_bitrate = 3 * 1024 * 1024; /* 3, 1024: Recommended bit rate */ } else if (venc_attr.config.width > 352) { /* 352 resolusion */ venc_attr.config.target_bitrate = 3 / 2 * 1024 * 1024; /* 3, 2, 1024: Recommended bit rate */ } else { venc_attr.config.target_bitrate = 800 * 1024; /* 800, 1024: Recommended bit rate */ } if ((g_jpeg_save_type[i] == VCODE_TYPE_JPEG) || (g_jpeg_save_type[i] == VCODE_TYPE_MJPEG)) { venc_attr.config.qfactor = 70; /* 70: jpeg default factor */ } if (venc_create(&venc_attr, &g_venc_chan_id[i]) != TD_SUCCESS) { return TD_FAILURE; } thread_arg->chan_id[i] = g_venc_chan_id[i]; thread_arg->time_out[i] = 0; if (alloc_frame_buf(i, venc_attr.config.width, venc_attr.config.height) != TD_SUCCESS) { return TD_FAILURE; } venc_start(g_venc_chan_id[i]); } return TD_SUCCESS; } static td_void venc_channel_stop_encode(sample_thread_info *thread_arg, td_u32 chn_num) { td_u32 i; for (i = 0; i < chn_num; i++) { venc_stop(g_venc_chan_id[i]); venc_destroy(g_venc_chan_id[i]); if (g_jpeg_save_type[i] != VCODE_TYPE_JPEG) { fclose(thread_arg->file_output[i]); } fclose(thread_arg->file_input[i]); if (free_frame_buf(i) != TD_SUCCESS) { venc_err_printf("free_frame_buf(%d) failed\n", i); } } } static td_s32 get_yuv_file_store_type(td_void) { printf("*********************************************\n"); printf("please enter input YUV store/sample type\n"); printf("*********************************************\n"); printf("0 --> SP420_YCbCr\n"); printf("1 --> SP420_YCrCb\n"); printf("2 --> Planner 420\n"); printf("3 --> Package422_YUYV\n"); printf("4 --> Package422_YVYU\n"); printf("5 --> Package422_UYVY\n"); printf("6 --> ARGB8888\n"); printf("7 --> SP420_YCbCr_10bit\n"); fflush(stdin); sec_chk_lt_ret(scanf_s("%d", &g_yuv_store_type), TD_FAILURE); fflush(stdin); if (g_yuv_store_type >= YUV_SEMIPLANNER_420_VU_10BIT) { printf("Wrong option of YUV store/sample type! type(%d) not support!\n", g_yuv_store_type); return TD_FAILURE; } if (g_yuv_store_type == YUV_SEMIPLANNER_420_UV_10BIT || g_yuv_store_type == YUV_SEMIPLANNER_420_VU_10BIT) { g_yuv_bit_deep = YUV_10_BIT; } else { g_yuv_bit_deep = YUV_8_BIT; } return TD_SUCCESS; } static td_u32 get_user_input_info(td_s32 argc, td_char *argv[], channel_info (*chan_info_ptr)[VENC_MAX_CHN], sample_thread_info *thread_arg) { td_u32 chn_num = 0; if (argc == 2) { /* 2 parameters */ if (!strcmp("user-defined", argv[1])) { chn_num = set_thread_info_user_defined(chan_info_ptr, thread_arg); if (chn_num == 0) { venc_err_printf("set_thread_info_user_defined failed\n"); return chn_num; } } else { goto error_0; } } else if (argc == 5) { /* 5 parameters */ chn_num = set_thread_info_defualt(chan_info_ptr, thread_arg, argv); if (chn_num == 0) { venc_err_printf("set_thread_info_user_defined failed\n"); return chn_num; } } else { goto error_0; } return chn_num; error_0: printf("\nUsage1 : %s \n", argv[0]); printf(" Example: %s 1280x720.yuv 1280 720 H265\n", argv[0]); printf("Usage2: %s user-defined\n\n", argv[0]); return chn_num; } td_s32 main(td_s32 argc, td_char *argv[]) { td_s32 ret; td_u32 chn_num; sample_thread_info thread_arg = {0}; channel_info chan_info[VENC_MAX_CHN]; chn_num = get_user_input_info(argc, argv, &chan_info, &thread_arg); if (chn_num == 0) { return TD_FAILURE; } if (get_yuv_file_store_type() != TD_SUCCESS) { venc_err_printf("get_yuv_file_store_type failed\n"); return TD_FAILURE; } uapi_sys_init(); if (uapi_venc_init() != TD_SUCCESS) { venc_err_printf("venc init failed\n"); return TD_FAILURE; } chn_num = chn_num <= VENC_MAX_CHN ? chn_num : VENC_MAX_CHN; if (venc_channel_start_encode(chan_info, chn_num, &thread_arg) != TD_SUCCESS) { venc_err_printf("set_venc_channel_attr failed\n"); return TD_FAILURE; } if (venc_thread_create(&thread_arg) != TD_SUCCESS) { venc_err_printf("venc_thread_create failed\n"); return TD_FAILURE; } sleep(1); loop_function(); venc_thread_stop(chn_num); venc_channel_stop_encode(&thread_arg, chn_num); ret = uapi_venc_deinit(); if (ret != TD_SUCCESS) { printf("call uapi_venc_deinit() failed.\n"); } ret = uapi_sys_deinit(); if (ret != TD_SUCCESS) { printf("call uapi_sys_deinit() failed.\n"); } return ret; }