/* Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sample_comm.h" #define SEND_STREAM_CNT 5 #define VDEC_SECOND 1000000 #define SAMPLE_VDEC_LOW_DELAY_LINE_CNT 16 static ot_vb_src g_vdec_vb_src = OT_VB_SRC_MOD; static td_bool g_vdec_line_ldy_en = TD_FALSE; static ot_vb_pool g_pic_vb_pool[OT_VB_MAX_POOLS] = { [0 ... (OT_VB_MAX_POOLS - 1)] = OT_VB_INVALID_POOL_ID }; static ot_vb_pool g_tmv_vb_pool[OT_VB_MAX_POOLS] = { [0 ... (OT_VB_MAX_POOLS - 1)] = OT_VB_INVALID_POOL_ID }; td_void sample_comm_vdec_print_chn_status(td_s32 chn, ot_vdec_chn_status status) { printf("\033[0;33m ---------------------------------------------------------------\033[0;39m\n"); printf("\033[0;33m chn:%d, type:%d, start:%d, decode_frames:%d, left_pics:%d, left_bytes:%d, " "left_frames:%d, recv_frames:%d \033[0;39m\n", chn, (status).type, (status).is_started, (status).dec_stream_frames, (status).left_decoded_frames, (status).left_stream_bytes, (status).left_stream_frames, (status).recv_stream_frames); printf("\033[0;33m format_err:%d, pic_size_err_set:%d, stream_unsprt:%d, pack_err:%d, " "set_pic_size_err:%d, ref_err_set:%d, pic_buf_size_err_set:%d \033[0;39m\n", (status).dec_err.format_err, (status).dec_err.set_pic_size_err, (status).dec_err.stream_unsupport, (status).dec_err.pack_err, (status).dec_err.set_protocol_num_err, (status).dec_err.set_ref_num_err, (status).dec_err.set_pic_buf_size_err); printf("\033[0;33m -----------------------------------------------------------------\033[0;39m\n"); return; } td_bool sample_comm_vdec_get_lowdelay_en(td_void) { return g_vdec_line_ldy_en; } td_void sample_comm_vdec_set_lowdelay_en(td_bool enable) { g_vdec_line_ldy_en = enable; } td_s32 sample_comm_vdec_init_user_vb_pool(td_u32 chn_num, struct_vdec_buf *vdec_buf, struct_vdec_attr *sample_vdec, td_u32 array_len) { td_u32 i; ot_vb_pool_cfg vb_pool_cfg; for (i = 0; (i < chn_num) && (i < array_len) && (i < OT_VB_MAX_POOLS); i++) { if ((vdec_buf[i].pic_buf_size != 0) && (sample_vdec[i].frame_buf_cnt != 0)) { (td_void)memset_s(&vb_pool_cfg, sizeof(ot_vb_pool_cfg), 0, sizeof(ot_vb_pool_cfg)); vb_pool_cfg.blk_size = vdec_buf[i].pic_buf_size; vb_pool_cfg.blk_cnt = sample_vdec[i].frame_buf_cnt; vb_pool_cfg.remap_mode = OT_VB_REMAP_MODE_NONE; g_pic_vb_pool[i] = ss_mpi_vb_create_pool(&vb_pool_cfg); if (g_pic_vb_pool[i] == OT_VB_INVALID_POOL_ID) { return TD_FAILURE; } } if (vdec_buf[i].tmv_buf_size != 0) { (td_void)memset_s(&vb_pool_cfg, sizeof(ot_vb_pool_cfg), 0, sizeof(ot_vb_pool_cfg)); vb_pool_cfg.blk_size = vdec_buf[i].tmv_buf_size; vb_pool_cfg.blk_cnt = sample_vdec[i].member_vdec_video.ref_frame_num + 1; vb_pool_cfg.remap_mode = OT_VB_REMAP_MODE_NONE; g_tmv_vb_pool[i] = ss_mpi_vb_create_pool(&vb_pool_cfg); if (g_tmv_vb_pool[i] == OT_VB_INVALID_POOL_ID) { return TD_FAILURE; } } } return TD_SUCCESS; } td_void sample_comm_handle_init_vb_fail(td_s32 idx) { td_s32 ret; td_s32 i = idx; for (; (i >= 0) && (i < OT_VB_MAX_POOLS); i--) { if (g_pic_vb_pool[i] != OT_VB_INVALID_POOL_ID) { ret = ss_mpi_vb_destroy_pool(g_pic_vb_pool[i]); if (ret != TD_SUCCESS) { printf("vb destroy pool %d fail!\n", g_pic_vb_pool[i]); } g_pic_vb_pool[i] = OT_VB_INVALID_POOL_ID; } if (g_tmv_vb_pool[i] != OT_VB_INVALID_POOL_ID) { ret = ss_mpi_vb_destroy_pool(g_tmv_vb_pool[i]); if (ret != TD_SUCCESS) { printf("vb destroy pool %d fail!\n", g_tmv_vb_pool[i]); } g_tmv_vb_pool[i] = OT_VB_INVALID_POOL_ID; } } return; } td_void sample_comm_vdec_cal_vb_size(td_u32 chn_num, struct_vdec_attr *sample_vdec, td_u32 sample_vdec_arr_len, struct_vdec_buf *vdec_buf) { td_u32 i; ot_pic_buf_attr buf_attr = { 0 }; for (i = 0; (i < chn_num) && (i < sample_vdec_arr_len); i++) { buf_attr.align = 0; buf_attr.height = sample_vdec[i].height; buf_attr.width = sample_vdec[i].width; if (sample_vdec[i].type == OT_PT_H265) { buf_attr.bit_width = sample_vdec[i].member_vdec_video.bit_width; buf_attr.pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420; vdec_buf[i].pic_buf_size = ot_vdec_get_pic_buf_size(sample_vdec[i].type, &buf_attr); vdec_buf[i].tmv_buf_size = ot_vdec_get_tmv_buf_size(sample_vdec[i].type, sample_vdec[i].width, sample_vdec[i].height); } else if (sample_vdec[i].type == OT_PT_H264) { buf_attr.bit_width = sample_vdec[i].member_vdec_video.bit_width; buf_attr.pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420; vdec_buf[i].pic_buf_size = ot_vdec_get_pic_buf_size(sample_vdec[i].type, &buf_attr); if (sample_vdec[i].member_vdec_video.dec_mode == OT_VIDEO_DEC_MODE_IPB) { vdec_buf[i].tmv_buf_size = ot_vdec_get_tmv_buf_size(sample_vdec[i].type, sample_vdec[i].width, sample_vdec[i].height); } } else { buf_attr.bit_width = OT_DATA_BIT_WIDTH_8; buf_attr.pixel_format = sample_vdec[i].member_vdec_picture.pixel_format; vdec_buf[i].pic_buf_size = ot_vdec_get_pic_buf_size(sample_vdec[i].type, &buf_attr); } } return; } td_s32 sample_comm_vdec_config_vb_pool(td_u32 chn_num, struct_vdec_attr *sample_vdec, td_u32 arr_len, struct_vdec_buf *vdec_buf, ot_vb_cfg *vb_conf) { td_u32 i, j; td_bool find_flag; td_s32 pos = 0; /* pic_buffer */ for (j = 0; j < OT_VB_MAX_COMMON_POOLS; j++) { find_flag = TD_FALSE; for (i = 0; (i < chn_num) && (i < arr_len); i++) { if ((find_flag == TD_FALSE) && (vdec_buf[i].pic_buf_size != 0) && (vdec_buf[i].pic_buf_alloc == TD_FALSE)) { vb_conf->common_pool[j].blk_size = vdec_buf[i].pic_buf_size; vb_conf->common_pool[j].blk_cnt = sample_vdec[i].frame_buf_cnt; vdec_buf[i].pic_buf_alloc = TD_TRUE; find_flag = TD_TRUE; pos = j; } if ((find_flag == TD_TRUE) && (vdec_buf[i].pic_buf_alloc == TD_FALSE) && (vb_conf->common_pool[j].blk_size == vdec_buf[i].pic_buf_size)) { vb_conf->common_pool[j].blk_cnt += sample_vdec[i].frame_buf_cnt; vdec_buf[i].pic_buf_alloc = TD_TRUE; } } } /* tmv_buffer */ for (j = pos + 1; j < OT_VB_MAX_COMMON_POOLS; j++) { find_flag = TD_FALSE; for (i = 0; (i < chn_num) && (i < arr_len); i++) { if ((find_flag == TD_FALSE) && (vdec_buf[i].tmv_buf_size != 0) && (vdec_buf[i].tmv_buf_alloc == TD_FALSE)) { vb_conf->common_pool[j].blk_size = vdec_buf[i].tmv_buf_size; vb_conf->common_pool[j].blk_cnt = sample_vdec[i].member_vdec_video.ref_frame_num + 1; vdec_buf[i].tmv_buf_alloc = TD_TRUE; find_flag = TD_TRUE; pos = j; } if ((find_flag == TD_TRUE) && (vdec_buf[i].tmv_buf_alloc == TD_FALSE) && (vb_conf->common_pool[j].blk_size == vdec_buf[i].tmv_buf_size)) { vb_conf->common_pool[j].blk_cnt += sample_vdec[i].member_vdec_video.ref_frame_num + 1; vdec_buf[i].tmv_buf_alloc = TD_TRUE; } } } vb_conf->max_pool_cnt = pos + 1; return i - 1; } td_s32 sample_comm_vdec_init_vb_pool(td_u32 chn_num, struct_vdec_attr *sample_vdec, td_u32 arr_len) { ot_vb_cfg vb_conf; td_s32 i, ret; struct_vdec_buf vdec_buf[OT_VDEC_MAX_CHN_NUM]; check_null_ptr_return(sample_vdec); if (arr_len > OT_VDEC_MAX_CHN_NUM) { printf("struct_vdec_attr array len Invalid \n"); return TD_FAILURE; } (td_void)memset_s(vdec_buf, sizeof(vdec_buf), 0, sizeof(struct_vdec_buf) * OT_VDEC_MAX_CHN_NUM); (td_void)memset_s(&vb_conf, sizeof(ot_vb_cfg), 0, sizeof(ot_vb_cfg)); sample_comm_vdec_cal_vb_size(chn_num, sample_vdec, arr_len, vdec_buf); i = sample_comm_vdec_config_vb_pool(chn_num, sample_vdec, arr_len, vdec_buf, &vb_conf); if (g_vdec_vb_src == OT_VB_SRC_MOD) { ss_mpi_vb_exit_mod_common_pool(OT_VB_UID_VDEC); check_return(ss_mpi_vb_set_mod_pool_cfg(OT_VB_UID_VDEC, &vb_conf), "vb set mod pool config"); ret = ss_mpi_vb_init_mod_common_pool(OT_VB_UID_VDEC); if (ret != TD_SUCCESS) { printf("vb exit mod common pool fail for 0x%x\n", ret); ss_mpi_vb_exit_mod_common_pool(OT_VB_UID_VDEC); return TD_FAILURE; } } else if (g_vdec_vb_src == OT_VB_SRC_USER) { if (sample_comm_vdec_init_user_vb_pool(chn_num, &vdec_buf[0], &sample_vdec[0], OT_VDEC_MAX_CHN_NUM) != TD_SUCCESS) { goto fail; } } return TD_SUCCESS; fail: sample_comm_handle_init_vb_fail(i); return TD_FAILURE; } td_void sample_comm_vdec_exit_user_vb_pool(td_void) { td_s32 i, ret; for (i = OT_VB_MAX_POOLS - 1; i >= 0; i--) { if (g_pic_vb_pool[i] != OT_VB_INVALID_POOL_ID) { ret = ss_mpi_vb_destroy_pool(g_pic_vb_pool[i]); if (ret != TD_SUCCESS) { printf("vb destroy pool %d fail!\n", g_pic_vb_pool[i]); } g_pic_vb_pool[i] = OT_VB_INVALID_POOL_ID; } if (g_tmv_vb_pool[i] != OT_VB_INVALID_POOL_ID) { ret = ss_mpi_vb_destroy_pool(g_tmv_vb_pool[i]); if (ret != TD_SUCCESS) { printf("vb destroy pool %d fail!\n", g_tmv_vb_pool[i]); } g_tmv_vb_pool[i] = OT_VB_INVALID_POOL_ID; } } return; } td_void sample_comm_vdec_exit_vb_pool(td_void) { if (g_vdec_vb_src == OT_VB_SRC_MOD) { ss_mpi_vb_exit_mod_common_pool(OT_VB_UID_VDEC); } else if (g_vdec_vb_src == OT_VB_SRC_USER) { sample_comm_vdec_exit_user_vb_pool(); } return; } td_void sample_comm_vdec_send_h264_frame_process(td_s32 *read_len, td_u8 *buf, struct_vdec_thread_param *thread_param, td_s32 used_bytes) { td_s32 i; td_bool find_start = TD_FALSE; td_bool find_end = TD_FALSE; td_bool new_pic; /* H264 frame start marker */ if (*read_len > thread_param->min_buf_size) { sample_print("chn %d read_len %d is bigger than buf_size %u!\n", thread_param->chn_id, *read_len, thread_param->min_buf_size); return; } for (i = 0; i < *read_len - 8; i++) { /* 8:h264 frame start code length */ int tmp = buf[i + 3] & 0x1F; /* 3:index 0x1F:frame start marker */ new_pic = (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 && /* 1 2:index */ (((tmp == 0x5 || tmp == 0x1) && ((buf[i + 4] & 0x80) == 0x80)) || /* 4:index 0x5 0x80:frame start mark */ (tmp == 20 && (buf[i + 7] & 0x80) == 0x80))); /* 20 0x1 0x80:frame start marker 7:index */ if (new_pic == TD_TRUE) { find_start = TD_TRUE; i += 8; /* 8:h264 frame start code length */ break; } } for (; i < *read_len - 8; i++) { /* 8:h264 frame start code length */ int tmp = buf[i + 3] & 0x1F; /* 3:index 0x1F:frame start marker */ new_pic = (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 && /* 1 2:index */ (tmp == 15 || tmp == 7 || tmp == 8 || tmp == 6 || /* 15 7 8 6:frame start marker */ ((tmp == 5 || tmp == 1) && ((buf[i + 4] & 0x80) == 0x80)) || /* 4:index 5 0x80:frame start marker */ (tmp == 20 && (buf[i + 7] & 0x80) == 0x80))); /* 7:index 20 0x80:frame start marker */ if (new_pic == TD_TRUE) { find_end = TD_TRUE; break; } } if (i > 0) { *read_len = i; } if (find_start == TD_FALSE) { sample_print("chn %d can not find H264 start code! read_len %d, used_bytes %d!\n", thread_param->chn_id, *read_len, used_bytes); } if (find_end == TD_FALSE) { *read_len = i + 8; /* 8:h264 frame start code length */ } return; } td_void sample_comm_vdec_send_mpeg4_frame_process(td_s32 *read_len, td_u8 *buf, struct_vdec_thread_param *thread_param, td_s32 used_bytes) { td_s32 i; td_bool find_start = TD_FALSE; td_bool find_end = TD_FALSE; /* MPEG4 frame start marker */ if (*read_len > thread_param->min_buf_size) { sample_print("chn %d read_len %d is bigger than buf_size %u!\n", thread_param->chn_id, *read_len, thread_param->min_buf_size); return; } for (i = 0; i < *read_len - 4; i++) { /* 4:mpeg4 frame start code length */ if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 && buf[i + 3] == 0xB6) { /* 0xB6:frame start marker 2 3:index */ find_start = TD_TRUE; i += 4; /* 4:mpeg4 frame start code length */ break; } } for (; i < *read_len - 4; i++) { /* 4:mpeg4 frame start code length */ if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 && buf[i + 3] == 0xB6) { /* 2 3:index 0xB6:frame start marker */ find_end = TD_TRUE; break; } } if (i > 0) { *read_len = i; } if (find_start == TD_FALSE) { sample_print("chn %d can not find MPEG4 start code! read_len %d, used_bytes %d!\n", thread_param->chn_id, *read_len, used_bytes); } if (find_end == TD_FALSE) { *read_len = i + 4; /* 4:mpeg4 frame start code length */ } return; } td_void sample_comm_vdec_send_h265_frame_process(td_s32 *read_len, td_u8 *buf, struct_vdec_thread_param *thread_param, td_s32 used_bytes) { td_s32 i; td_bool find_start = TD_FALSE; td_bool find_end = TD_FALSE; /* H265 frame start marker */ td_bool new_pic; if (*read_len > thread_param->min_buf_size) { sample_print("chn %d read_len %d is bigger than buf_size %u!\n", thread_param->chn_id, *read_len, thread_param->min_buf_size); return; } for (i = 0; i < *read_len - 6; i++) { /* 6:h265 frame start code length */ td_u32 tmp = (buf[i + 3] & 0x7E) >> 1; /* 0x7E:frame start marker 3:index */ new_pic = (buf[i + 0] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 && /* 1 2:index */ (tmp <= 21) && ((buf[i + 5] & 0x80) == 0x80)); /* 5:index 21 0x80:frame start marker */ if (new_pic) { find_start = TD_TRUE; i += 6; /* 6:h265 frame start code length */ break; } } for (; i < *read_len - 6; i++) { /* 6:h265 frame start code length */ td_u32 tmp = (buf[i + 3] & 0x7E) >> 1; /* 0x7E:frame start marker 3:index */ new_pic = (buf[i + 0] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 && /* 1 2:index */ (tmp == 32 || tmp == 33 || tmp == 34 || tmp == 39 || tmp == 40 || /* 32 33 34 39 40:frame start marker */ ((tmp <= 21) && (buf[i + 5] & 0x80) == 0x80))); /* 5:index 21 0x80:frame start marker */ if (new_pic) { find_end = TD_TRUE; break; } } if (i > 0) { *read_len = i; } if (find_start == TD_FALSE) { sample_print("chn %d can not find H265 start code! read_len %d, used_bytes %d!\n", thread_param->chn_id, *read_len, used_bytes); } if (find_end == TD_FALSE) { *read_len = i + 6; /* 6:h265 frame start code length */ } return; } td_u32 sample_comm_vdec_send_jpeg_frame_process(td_s32 *read_len, td_u8 *buf, struct_vdec_thread_param *thread_param, td_s32 used_bytes) { td_s32 i; td_u32 len; td_u32 start = 0; td_bool find_start = TD_FALSE; if (*read_len > thread_param->min_buf_size) { sample_print("chn %d read_len %d is bigger than buf_size %u!\n", thread_param->chn_id, *read_len, thread_param->min_buf_size); return start; } /* JPEG frame start marker */ for (i = 0; i < *read_len - 1; i++) { if (buf[i] == 0xFF && buf[i + 1] == 0xD8) { /* 0xFF 0xD8:frame start marker */ start = i; find_start = TD_TRUE; i = i + 2; /* 2:offset */ break; } } for (; i < *read_len - 3; i++) { /* 3:jpeg frame start code length */ if ((buf[i] == 0xFF) && (buf[i + 1] & 0xF0) == 0xE0) { /* 0xFF 0xF0 0xE0:frame start marker */ len = (buf[i + 2] << 8) + buf[i + 3]; /* 2 3:index 8:left shift length */ i += 1 + len; } else { break; } } for (; i < *read_len - 1; i++) { if (buf[i] == 0xFF && buf[i + 1] == 0xD9) { /* 0xFF 0xD9:frame start marker */ break; } } *read_len = i + 2; /* 2:offset */ if (find_start == TD_FALSE) { sample_print("chn %d can not find JPEG start code! read_len %d, used_bytes %d!\n", thread_param->chn_id, *read_len, used_bytes); } return start; } td_u32 sample_comm_vdec_cut_frame(td_s32 *read_len, td_u8 *buf, struct_vdec_thread_param *thread_param, td_s32 used_bytes, td_bool *end_of_stream) { td_u32 start = 0; if ((thread_param->stream_mode == OT_VDEC_SEND_MODE_FRAME || thread_param->stream_mode == OT_VDEC_SEND_MODE_COMPAT) && thread_param->type == OT_PT_H264) { sample_comm_vdec_send_h264_frame_process(read_len, buf, thread_param, used_bytes); } else if ((thread_param->stream_mode == OT_VDEC_SEND_MODE_FRAME || thread_param->stream_mode == OT_VDEC_SEND_MODE_COMPAT) && thread_param->type == OT_PT_H265) { sample_comm_vdec_send_h265_frame_process(read_len, buf, thread_param, used_bytes); } else if ((thread_param->stream_mode == OT_VDEC_SEND_MODE_FRAME || thread_param->stream_mode == OT_VDEC_SEND_MODE_COMPAT) && thread_param->type == OT_PT_MP4VIDEO) { sample_comm_vdec_send_mpeg4_frame_process(read_len, buf, thread_param, used_bytes); } else if ((thread_param->stream_mode == OT_VDEC_SEND_MODE_FRAME || thread_param->stream_mode == OT_VDEC_SEND_MODE_COMPAT) && (thread_param->type == OT_PT_MJPEG || thread_param->type == OT_PT_JPEG)) { start = sample_comm_vdec_send_jpeg_frame_process(read_len, buf, thread_param, used_bytes); } else { if ((*read_len != 0) && (*read_len < thread_param->min_buf_size)) { *end_of_stream = TD_TRUE; } } return start; } td_void sample_comm_vdec_send_stream_proc(struct_vdec_thread_param *thread_param, ot_vdec_stream *stream, td_bool end_of_stream) { td_s32 ret; td_s32 total_len = stream->len; td_s32 cur_len = 0; td_s32 i; td_u8 *base = stream->addr; td_s32 cnt = (thread_param->stream_mode == OT_VDEC_SEND_MODE_COMPAT) ? SEND_STREAM_CNT : 1; for (i = 0; i < cnt; i++) { stream->addr = base + cur_len; if (i == cnt - 1) { stream->len = total_len - cur_len; stream->end_of_frame = TD_TRUE; stream->end_of_stream = end_of_stream; } else { stream->len = total_len / cnt; cur_len += stream->len; stream->end_of_frame = TD_FALSE; stream->end_of_stream = TD_FALSE; } ret = ss_mpi_vdec_send_stream(thread_param->chn_id, stream, thread_param->milli_sec); while ((ret != TD_SUCCESS) && (thread_param->e_thread_ctrl == THREAD_CTRL_START)) { usleep(thread_param->interval_time); ret = ss_mpi_vdec_send_stream(thread_param->chn_id, stream, thread_param->milli_sec); } } } td_void sample_comm_vdec_handle_send_stream(struct_vdec_thread_param *thread_param, ot_vdec_stream *stream, td_bool *end_of_stream, td_u64 *pts) { td_u64 cur_time; while (1) { if (thread_param->e_thread_ctrl != THREAD_CTRL_START) { break; } ss_mpi_sys_get_cur_pts(&cur_time); if ((thread_param->last_time == 0) || ((cur_time - thread_param->last_time) >= (VDEC_SECOND / thread_param->fps - thread_param->time_gap))) { sample_comm_vdec_send_stream_proc(thread_param, stream, *end_of_stream); *end_of_stream = TD_FALSE; *pts += thread_param->pts_increase; if (thread_param->last_time != 0) { thread_param->time_gap = ((cur_time - thread_param->last_time) >= (VDEC_SECOND / thread_param->fps)) ? (cur_time - thread_param->last_time - (VDEC_SECOND / thread_param->fps)) : 0; thread_param->time_gap = (thread_param->time_gap > (VDEC_SECOND / thread_param->fps)) ? (VDEC_SECOND / thread_param->fps) : thread_param->time_gap; } thread_param->last_time = cur_time; break; } else { usleep(thread_param->interval_time); } } return; } td_void sample_comm_vdec_send_stream_process(struct_vdec_thread_param *thread_param, FILE *fp_strm, td_u8 *buf) { td_bool end_of_stream; td_s32 used_bytes = 0; td_s32 read_len; td_u64 pts = thread_param->pts_init; td_u32 start; ot_vdec_stream stream; thread_param->last_time = 0; thread_param->time_gap = 0; while (1) { if (thread_param->e_thread_ctrl == THREAD_CTRL_STOP) { break; } else if (thread_param->e_thread_ctrl == THREAD_CTRL_PAUSE) { sleep(1); continue; } end_of_stream = TD_FALSE; fseek(fp_strm, used_bytes, SEEK_SET); read_len = fread(buf, 1, thread_param->min_buf_size, fp_strm); if (read_len == 0) { if (thread_param->circle_send == TD_TRUE) { (td_void)memset_s(&stream, sizeof(ot_vdec_stream), 0, sizeof(ot_vdec_stream)); stream.end_of_stream = TD_TRUE; ss_mpi_vdec_send_stream(thread_param->chn_id, &stream, -1); used_bytes = 0; fseek(fp_strm, 0, SEEK_SET); read_len = fread(buf, 1, thread_param->min_buf_size, fp_strm); } else { break; } } start = sample_comm_vdec_cut_frame(&read_len, buf, thread_param, used_bytes, &end_of_stream); stream.pts = pts; stream.addr = buf + start; stream.len = read_len; stream.end_of_frame = (thread_param->stream_mode == OT_VDEC_SEND_MODE_FRAME || thread_param->stream_mode == OT_VDEC_SEND_MODE_COMPAT) ? TD_TRUE : TD_FALSE; stream.end_of_stream = end_of_stream; stream.need_display = 1; sample_comm_vdec_handle_send_stream(thread_param, &stream, &end_of_stream, &pts); used_bytes += read_len + start; } return; } td_s32 sample_comm_vdec_check_send_stream_param(struct_vdec_thread_param *thread_param, td_char *c_stream_file, td_u32 arr_len) { if (thread_param->c_file_path == TD_NULL || thread_param->c_file_name == TD_NULL) { sample_print("chn %d stream file path or stream file name is NULL\n", thread_param->chn_id); return TD_FAILURE; } if (arr_len <= 1) { sample_print("chn %d arr length might be overflow\n", thread_param->chn_id); return TD_FAILURE; } if (snprintf_s(c_stream_file, arr_len, arr_len - 1, "%s/%s", thread_param->c_file_path, thread_param->c_file_name) < 0) { sample_print("chn %d config stream file failed!\n", thread_param->chn_id); return TD_FAILURE; } if (thread_param->min_buf_size <= 0) { sample_print("chn %d min_buf_size should greater than zero!\n", thread_param->chn_id); return TD_FAILURE; } if (thread_param->fps <= 0 || thread_param->fps > 300) { /* 0~300:frame rate limit */ sample_print("chn %d fps should be [1, 300]!\n", thread_param->chn_id); return TD_FAILURE; } return TD_SUCCESS; } td_void *sample_comm_vdec_send_stream(td_void *args) { struct_vdec_thread_param *thread_param = (struct_vdec_thread_param *)args; FILE *fp_strm = TD_NULL; td_u8 *buf = TD_NULL; ot_vdec_stream stream; td_char c_stream_file[FILE_NAME_LEN]; td_char *path = TD_NULL; prctl(PR_SET_NAME, "video_send_stream", 0, 0, 0); if (sample_comm_vdec_check_send_stream_param(thread_param, c_stream_file, FILE_NAME_LEN) != TD_SUCCESS) { return (td_void *)(TD_FAILURE); } path = realpath(c_stream_file, TD_NULL); if (path == TD_NULL) { sample_print("chn %d Invalid stream path. Please check!\n", thread_param->chn_id); return (td_void *)(TD_FAILURE); } fp_strm = fopen(path, "rb"); if (fp_strm == TD_NULL) { sample_print("chn %d can't open file %s in send stream thread!\n", thread_param->chn_id, c_stream_file); goto end1; } printf("\n \033[0;36m chn %d, stream file:%s, userbufsize: %d \033[0;39m\n", thread_param->chn_id, thread_param->c_file_name, thread_param->min_buf_size); buf = malloc(thread_param->min_buf_size); if (buf == TD_NULL) { sample_print("chn %d can't alloc %d in send stream thread!\n", thread_param->chn_id, thread_param->min_buf_size); goto end; } fflush(stdout); sample_comm_vdec_send_stream_process(thread_param, fp_strm, buf); /* send the flag of stream end */ (td_void)memset_s(&stream, sizeof(ot_vdec_stream), 0, sizeof(ot_vdec_stream)); stream.end_of_stream = TD_TRUE; ss_mpi_vdec_send_stream(thread_param->chn_id, &stream, -1); printf("\033[0;35m chn %d send steam thread return ... \033[0;39m\n", thread_param->chn_id); fflush(stdout); if (buf != TD_NULL) { free(buf); buf = TD_NULL; } end: fclose(fp_strm); fp_strm = TD_NULL; end1: free(path); path = TD_NULL; return (td_void *)TD_SUCCESS; } td_void sample_comm_vdec_cmd_not_circle_send(td_u32 chn_num, struct_vdec_thread_param *vdec_send, pthread_t *vdec_thread, td_u32 send_arr_len, td_u32 thread_arr_len) { td_u32 i; td_s32 ret; ot_vdec_chn_status status; printf("decoding.............."); for (i = 0; (i < chn_num) && (i < send_arr_len) && (i < thread_arr_len); i++) { if (vdec_thread[i] != 0) { ret = pthread_join(vdec_thread[i], TD_NULL); if (ret == 0) { vdec_thread[i] = 0; } } vdec_thread[i] = 0; while (1) { ret = ss_mpi_vdec_query_status(vdec_send[i].chn_id, &status); if (ret != TD_SUCCESS) { printf("chn %d vdec query status fail!!\n", ret); return; } if ((status.left_stream_bytes == 0) && (status.left_stream_frames == 0)) { sample_comm_vdec_print_chn_status(vdec_send[i].chn_id, status); break; } usleep(1000); /* 1000:Decoding wait time */ } } printf("end!\n"); return; } td_void sample_comm_vdec_start_send_stream(td_s32 chn_num, struct_vdec_thread_param *vdec_send, pthread_t *vdec_thread, td_u32 send_arr_len, td_u32 thread_arr_len) { td_u32 i; if ((vdec_send == TD_NULL) || (vdec_thread == TD_NULL)) { printf("vdec_send or vdec_thread can't be NULL!\n"); return; } for (i = 0; (i < (td_u32)chn_num) && (i < send_arr_len) && (i < thread_arr_len); i++) { vdec_thread[i] = 0; pthread_create(&vdec_thread[i], 0, sample_comm_vdec_send_stream, (td_void *)&vdec_send[i]); } } td_void sample_comm_vdec_stop_send_stream(td_s32 chn_num, struct_vdec_thread_param *vdec_send, pthread_t *vdec_thread, td_u32 send_arr_len, td_u32 thread_arr_len) { td_u32 i; if ((vdec_send == TD_NULL) || (vdec_thread == TD_NULL)) { printf("vdec_send or vdec_thread can't be NULL!\n"); return; } for (i = 0; (i < (td_u32)chn_num) && (i < send_arr_len) && (i < thread_arr_len); i++) { vdec_send[i].e_thread_ctrl = THREAD_CTRL_STOP; if (vdec_thread[i] != 0) { pthread_join(vdec_thread[i], TD_NULL); vdec_thread[i] = 0; } ss_mpi_vdec_stop_recv_stream(i); } } td_void sample_comm_vdec_config_attr(ot_vdec_chn_attr *chn_attr, struct_vdec_attr *sample_vdec) { ot_pic_buf_attr buf_attr = { 0 }; chn_attr->type = sample_vdec->type; chn_attr->mode = sample_vdec->mode; chn_attr->pic_width = sample_vdec->width; chn_attr->pic_height = sample_vdec->height; chn_attr->stream_buf_size = sample_vdec->width * sample_vdec->height; chn_attr->frame_buf_cnt = sample_vdec->frame_buf_cnt; buf_attr.align = 0; buf_attr.height = sample_vdec->height; buf_attr.width = sample_vdec->width; if (sample_vdec->type == OT_PT_H264 || sample_vdec->type == OT_PT_H265) { buf_attr.bit_width = sample_vdec->member_vdec_video.bit_width; buf_attr.pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420; chn_attr->video_attr.ref_frame_num = sample_vdec->member_vdec_video.ref_frame_num; chn_attr->video_attr.temporal_mvp_en = 1; if ((sample_vdec->type == OT_PT_H264) && (sample_vdec->member_vdec_video.dec_mode != OT_VIDEO_DEC_MODE_IPB)) { chn_attr->video_attr.temporal_mvp_en = 0; } chn_attr->frame_buf_size = ot_vdec_get_pic_buf_size(chn_attr->type, &buf_attr); } else if (sample_vdec->type == OT_PT_JPEG || sample_vdec->type == OT_PT_MJPEG) { chn_attr->mode = OT_VDEC_SEND_MODE_FRAME; buf_attr.bit_width = OT_DATA_BIT_WIDTH_8; buf_attr.pixel_format = sample_vdec->member_vdec_picture.pixel_format; chn_attr->frame_buf_size = ot_vdec_get_pic_buf_size(chn_attr->type, &buf_attr); } return; } td_s32 sample_comm_config_ldy_attr(td_s32 i, td_u32 height) { ot_low_delay_info ldy_attr; if (g_vdec_line_ldy_en == TD_TRUE) { check_chn_return(ss_mpi_vdec_get_low_delay_attr(i, &ldy_attr), i, "ss_mpi_vdec_get_low_delay_attr"); ldy_attr.enable = TD_TRUE; ldy_attr.line_cnt = SAMPLE_VDEC_LOW_DELAY_LINE_CNT; check_chn_return(ss_mpi_vdec_set_low_delay_attr(i, &ldy_attr), i, "ss_mpi_vdec_set_low_delay_attr"); check_chn_return(ss_mpi_vdec_set_display_mode(i, OT_VIDEO_DISPLAY_MODE_PREVIEW), i, "ss_mpi_vdec_set_display_mode"); } return TD_SUCCESS; } td_s32 sample_comm_vdec_start(td_s32 chn_num, struct_vdec_attr *sample_vdec, td_u32 arr_len) { td_s32 i, ret; ot_vdec_chn_attr chn_attr[OT_VDEC_MAX_CHN_NUM]; ot_vdec_chn_pool pool; ot_vdec_chn_param chn_param; ot_vdec_mod_param mod_param; check_null_ptr_return(sample_vdec); if (arr_len > OT_VDEC_MAX_CHN_NUM) { sample_print("array size(%u) of chn_attr need < %u!\n", arr_len, OT_VDEC_MAX_CHN_NUM); return TD_FAILURE; } check_return(ss_mpi_vdec_get_mod_param(&mod_param), "vdec get mod param"); mod_param.vb_src = g_vdec_vb_src; check_return(ss_mpi_vdec_set_mod_param(&mod_param), "vdec set mod param"); for (i = 0; (i < chn_num) && (i < (td_s32)arr_len); i++) { sample_comm_vdec_config_attr(&chn_attr[i], &sample_vdec[i]); check_chn_return(ss_mpi_vdec_create_chn(i, &chn_attr[i]), i, "vdec create chn"); if ((g_vdec_vb_src == OT_VB_SRC_USER) && (i < OT_VB_MAX_POOLS)) { pool.pic_vb_pool = g_pic_vb_pool[i]; pool.tmv_vb_pool = g_tmv_vb_pool[i]; check_chn_return(ss_mpi_vdec_attach_vb_pool(i, &pool), i, "vdec attach vb pool"); } check_chn_return(ss_mpi_vdec_get_chn_param(i, &chn_param), i, "vdec get chn param"); if (sample_vdec[i].type == OT_PT_H264 || sample_vdec[i].type == OT_PT_H265 || sample_vdec[i].type == OT_PT_MP4VIDEO) { chn_param.video_param.dec_mode = sample_vdec[i].member_vdec_video.dec_mode; chn_param.video_param.compress_mode = OT_COMPRESS_MODE_NONE; chn_param.video_param.video_format = OT_VIDEO_FORMAT_LINEAR; if (chn_param.video_param.dec_mode == OT_VIDEO_DEC_MODE_IPB) { chn_param.video_param.out_order = OT_VIDEO_OUT_ORDER_DISPLAY; } else { chn_param.video_param.out_order = OT_VIDEO_OUT_ORDER_DEC; } chn_param.video_param.slice_input_en = sample_comm_vdec_get_lowdelay_en(); } else { chn_param.pic_param.pixel_format = sample_vdec[i].member_vdec_picture.pixel_format; chn_param.pic_param.alpha = sample_vdec[i].member_vdec_picture.alpha; } chn_param.display_frame_num = sample_vdec[i].display_frame_num; check_chn_return(ss_mpi_vdec_set_chn_param(i, &chn_param), i, "vdec set chn param"); ret = sample_comm_config_ldy_attr(i, chn_attr[i].pic_height); if (ret != TD_SUCCESS) { return ret; } check_chn_return(ss_mpi_vdec_start_recv_stream(i), i, "vdec start recv stream"); } return TD_SUCCESS; } td_s32 sample_comm_vdec_stop(td_s32 chn_num) { td_s32 i; for (i = 0; i < chn_num; i++) { check_chn_return(ss_mpi_vdec_stop_recv_stream(i), i, "vdec stop recv stream"); check_chn_return(ss_mpi_vdec_destroy_chn(i), i, "vdec destroy chn"); } return TD_SUCCESS; }