ss928_framework/ss928sdk/common/sample_comm_vdec.c
2024-12-16 13:31:45 +08:00

862 lines
33 KiB
C
Executable File

/*
Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <math.h>
#include <unistd.h>
#include <signal.h>
#include <sys/prctl.h>
#include <limits.h>
#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;
}