You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
899 lines
28 KiB
899 lines
28 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2010-2019. All rights reserved.
|
|
* Description: subtitle output module
|
|
* Author: dtv
|
|
* Create: 2010-04-07
|
|
*/
|
|
#include "uapi_so.h"
|
|
#include <pthread.h>
|
|
#include <sys/prctl.h>
|
|
#include "so_queue.h"
|
|
#include "dft_event.h"
|
|
|
|
|
|
#ifndef unused
|
|
#define unused(x) (void)x
|
|
#endif
|
|
|
|
#define SO_NORMAL_MAX_DISPLAY_TIME (10 * 1000) /* 10s */
|
|
#define SO_NORMAL_MIN_DISPLAY_TIME (2 * 1000) /* ss */
|
|
#define SO_PLAY_TIME_JUMP_JUDGE (5 * 1000) /* 5s */
|
|
#define SO_TRHEAD_SLEEP_TIME 10
|
|
#define SO_TIME_OFFSET 10
|
|
#define SO_TIME_ERROR_OFFSET 5000
|
|
#define SO_QUEUE_RESET_CHECK_TIME 100
|
|
#define SO_QUEUE_MAX_CLEAR_NODE_NUM 512
|
|
#define SO_SEND_OFFSET (SO_TIME_OFFSET)
|
|
#define SO_INVALID_HANDLE 0x0
|
|
#define SO_INSTANCE_MAX_NUM 96
|
|
#define SO_HANDLE_BASE 0xFFFF0000
|
|
#define FMEA_EVENT_ID_DRAW_ERR 955280004
|
|
#define PNAME_SUBT_MAX_LEN 64
|
|
#define FMEA_SUBT_EVENT_REPORT_COUNT 5
|
|
|
|
static td_s64 g_prev_pts = 0;
|
|
|
|
#define so_callback_lock() do { \
|
|
td_s32 defRet = pthread_mutex_lock(&member->mutex); \
|
|
if (defRet != TD_SUCCESS) { \
|
|
EXT_PRINT("SO call pthread_mutex_lock(mutex) failure,ret = 0x%x\n", defRet); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define so_callback_unlock() do { \
|
|
td_s32 defRet = pthread_mutex_unlock(&member->mutex); \
|
|
if (defRet != TD_SUCCESS) { \
|
|
EXT_PRINT("SO call pthread_mutex_unlock(mutex) failure, ret = 0x%x\n", defRet); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define so_queque_reset_lock(ret) do { \
|
|
(ret) = pthread_mutex_lock(&member->queue_reset_mutex); \
|
|
if ((ret) != TD_SUCCESS) { \
|
|
EXT_PRINT("SO call pthread_mutex_lock(queue_reset_mutex) failure,ret = 0x%x\n", ret); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define so_queque_reset_unlock(ret) do { \
|
|
(ret) = pthread_mutex_unlock(&member->queue_reset_mutex); \
|
|
if ((ret) != TD_SUCCESS) { \
|
|
EXT_PRINT("SO call pthread_mutex_unlock(queue_reset_mutex) failure, ret = 0x%x\n", ret); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define so_check_return(val) do { \
|
|
if (val) { \
|
|
return; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define so_check_return_ret(val, ret) do { \
|
|
if (val) { \
|
|
return ret; \
|
|
} \
|
|
} while (0)
|
|
|
|
static td_bool g_so_init = TD_FALSE;
|
|
static td_void *g_so_hdl[SO_INSTANCE_MAX_NUM];
|
|
|
|
typedef struct {
|
|
td_bool is_used;
|
|
uapi_so_clear_param clear_param;
|
|
td_u32 duration;
|
|
td_s64 node_pts;
|
|
} so_clear_node;
|
|
|
|
typedef struct {
|
|
uapi_so_getpts_callback_func get_pts_func; /* get local time */
|
|
uapi_so_ondraw_callback_func draw_func; /* draw subtitle */
|
|
uapi_so_onclear_callback_func clear_func; /* clear subtitle, called after draw_func */
|
|
td_handle surface; /* surface for subtitle output */
|
|
td_bool thread_exit; /* flag of so exit */
|
|
td_bool queue_reset; /* reset subtitle queue */
|
|
td_s64 reset_pts; /* reset subtitle queue by pts */
|
|
td_void *pts_user_data; /* user data of get_pts_func */
|
|
td_void *draw_user_data; /* user data of draw_func and clear_func */
|
|
td_u32 font; /* font of subtitle output */
|
|
td_s64 pts_offset;
|
|
td_u32 color; /* color of subtitle output */
|
|
td_u32 x, y; /* position of subtitle output */
|
|
SO_QUEUE_HANDLE queue_hdl; /* subtitle queue handle */
|
|
pthread_t task_id; /* main task handle */
|
|
pthread_mutex_t mutex; /* main task mutex */
|
|
pthread_mutex_t queue_reset_mutex; /* mutex of queue_reset */
|
|
pthread_attr_t struct_attr; /* main task attribute */
|
|
td_s64 last_pts; /* Last subt frame pts */
|
|
td_u32 interval_ms; /* subtitle max interval time */
|
|
so_clear_node clear_node_list[SO_QUEUE_MAX_CLEAR_NODE_NUM];
|
|
SO_INFO_S cur_sub_info;
|
|
} so_member;
|
|
|
|
typedef struct {
|
|
td_s64 cur_pts;
|
|
td_s64 cur_node_pts;
|
|
td_u32 duration;
|
|
td_u32 node_duration;
|
|
td_bool direct_out;
|
|
td_bool clear_sub;
|
|
} so_sync_info;
|
|
|
|
static td_u16 so_get_index_by_handle(td_handle so_hdl)
|
|
{
|
|
td_u16 handle_index = so_hdl & 0x0000FFFF; /* 0x0000FFFF handle index */
|
|
return handle_index;
|
|
}
|
|
|
|
static td_handle so_get_handle_by_index(td_u16 handle_index)
|
|
{
|
|
td_handle so_hdl;
|
|
so_hdl = SO_HANDLE_BASE | handle_index;
|
|
return so_hdl;
|
|
}
|
|
|
|
static td_void *so_get_memory_addr(td_handle so_hdl)
|
|
{
|
|
td_u16 handle_index = so_get_index_by_handle(so_hdl);
|
|
if (handle_index >= SO_INSTANCE_MAX_NUM) {
|
|
return TD_NULL;
|
|
}
|
|
|
|
if ((so_hdl & 0xFFFF0000) == SO_HANDLE_BASE) {
|
|
return g_so_hdl[handle_index];
|
|
} else {
|
|
return TD_NULL;
|
|
}
|
|
}
|
|
|
|
static td_s32 so_get_free_index(td_u16 *free_index)
|
|
{
|
|
td_u16 i;
|
|
|
|
for (i = 0; i < SO_INSTANCE_MAX_NUM; i++) {
|
|
if (g_so_hdl[i] == SO_INVALID_HANDLE) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= SO_INSTANCE_MAX_NUM) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
*free_index = i;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 so_get_node_clear_info(const SO_INFO_S *so_info, uapi_so_clear_param *clear_param)
|
|
{
|
|
clear_param->node_pts = 0;
|
|
clear_param->duration = 0;
|
|
|
|
if (so_info->subt_type == UAPI_SUBTITLE_BITMAP) {
|
|
clear_param->x = so_info->subtitle_param.gfx.x;
|
|
clear_param->y = so_info->subtitle_param.gfx.y;
|
|
clear_param->w = so_info->subtitle_param.gfx.w;
|
|
clear_param->h = so_info->subtitle_param.gfx.h;
|
|
clear_param->node_pts = so_info->subtitle_param.gfx.pts;
|
|
clear_param->duration = so_info->subtitle_param.gfx.duration;
|
|
} else if (so_info->subt_type == UAPI_SUBTITLE_TEXT) {
|
|
clear_param->x = so_info->subtitle_param.text.x;
|
|
clear_param->y = so_info->subtitle_param.text.y;
|
|
clear_param->w = so_info->subtitle_param.text.w;
|
|
clear_param->h = so_info->subtitle_param.text.h;
|
|
clear_param->node_pts = so_info->subtitle_param.text.pts;
|
|
clear_param->duration = so_info->subtitle_param.text.duration;
|
|
} else if (so_info->subt_type == UAPI_SUBTITLE_ASS) {
|
|
clear_param->x = 0;
|
|
clear_param->y = 0;
|
|
clear_param->w = 0;
|
|
clear_param->h = 0;
|
|
} else {
|
|
clear_param->x = 0;
|
|
clear_param->y = 0;
|
|
clear_param->w = 0;
|
|
clear_param->h = 0;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_void so_get_node_pts(const SO_INFO_S *so_info, td_s64 *pts, td_u32 *duration)
|
|
{
|
|
if (so_info->subt_type == UAPI_SUBTITLE_BITMAP) {
|
|
*pts = so_info->subtitle_param.gfx.pts;
|
|
*duration = so_info->subtitle_param.gfx.duration;
|
|
} else if (so_info->subt_type == UAPI_SUBTITLE_TEXT) {
|
|
*pts = so_info->subtitle_param.text.pts;
|
|
*duration = so_info->subtitle_param.text.duration;
|
|
} else if (so_info->subt_type == UAPI_SUBTITLE_ASS) {
|
|
*pts = so_info->subtitle_param.ass.pts;
|
|
*duration = so_info->subtitle_param.ass.duration;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* If soInfo rect is bigger than clearRect, return TD_TRUE */
|
|
static td_bool so_is_rect_ainb(const uapi_so_clear_param *clearRect, const SO_INFO_S *soInfo)
|
|
{
|
|
td_u32 ax = clearRect->x;
|
|
td_u32 ay = clearRect->y;
|
|
td_u32 aw = clearRect->w;
|
|
td_u32 ah = clearRect->h;
|
|
td_u32 bx, by, bw, bh;
|
|
if (soInfo->subt_type == UAPI_SUBTITLE_BITMAP) {
|
|
bx = soInfo->subtitle_param.gfx.x;
|
|
by = soInfo->subtitle_param.gfx.y;
|
|
bw = soInfo->subtitle_param.gfx.w;
|
|
bh = soInfo->subtitle_param.gfx.h;
|
|
} else if (soInfo->subt_type == UAPI_SUBTITLE_TEXT) {
|
|
bx = soInfo->subtitle_param.text.x;
|
|
by = soInfo->subtitle_param.text.y;
|
|
bw = soInfo->subtitle_param.text.w;
|
|
bh = soInfo->subtitle_param.text.h;
|
|
} else {
|
|
return TD_FALSE;
|
|
}
|
|
|
|
if (ax >= bx && ax + aw <= bx + bw &&
|
|
ay >= by && ax + ah <= bx + bh) {
|
|
return TD_TRUE;
|
|
}
|
|
return TD_FALSE;
|
|
}
|
|
|
|
static td_s32 so_insert_to_clear_list(so_member *member, const SO_INFO_S *soInfo, td_s64 nodePts, td_u32 duration)
|
|
{
|
|
td_u32 i;
|
|
|
|
for (i = 0; i < SO_QUEUE_MAX_CLEAR_NODE_NUM; i++) {
|
|
if (member->clear_node_list[i].is_used) {
|
|
if (so_is_rect_ainb(&member->clear_node_list[i].clear_param, soInfo)) {
|
|
member->clear_node_list[i].node_pts = nodePts;
|
|
member->clear_node_list[i].duration = duration;
|
|
(td_void) so_get_node_clear_info(soInfo, &member->clear_node_list[i].clear_param);
|
|
return TD_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < SO_QUEUE_MAX_CLEAR_NODE_NUM; i++) {
|
|
if (!member->clear_node_list[i].is_used) {
|
|
member->clear_node_list[i].node_pts = nodePts;
|
|
member->clear_node_list[i].duration = duration;
|
|
(td_void) so_get_node_clear_info(soInfo, &member->clear_node_list[i].clear_param);
|
|
member->clear_node_list[i].is_used = TD_TRUE;
|
|
return TD_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
static td_void so_reset_clear_list(so_member *member)
|
|
{
|
|
SO_CHK_FUN_RET_VOID(memset_s(member->clear_node_list, sizeof(so_clear_node) * SO_QUEUE_MAX_CLEAR_NODE_NUM, 0,
|
|
sizeof(so_clear_node) * SO_QUEUE_MAX_CLEAR_NODE_NUM));
|
|
}
|
|
|
|
static td_void so_clear_nodes(so_member *member, uapi_so_getpts_callback_func get_pts_func,
|
|
uapi_so_onclear_callback_func clear_func)
|
|
{
|
|
td_s64 node_pts = 0;
|
|
td_s64 cur_pts = 0;
|
|
td_u32 duration;
|
|
td_u32 i;
|
|
|
|
if (get_pts_func == NULL || clear_func == NULL) {
|
|
return;
|
|
}
|
|
|
|
(td_void)get_pts_func(member->pts_user_data, &cur_pts);
|
|
|
|
for (i = 0; i < SO_QUEUE_MAX_CLEAR_NODE_NUM; i++) {
|
|
if (!member->clear_node_list[i].is_used) {
|
|
continue;
|
|
}
|
|
|
|
duration = member->clear_node_list[i].duration;
|
|
node_pts = member->clear_node_list[i].node_pts;
|
|
if (cur_pts != (td_s64)UAPI_SO_NO_PTS &&
|
|
((cur_pts - node_pts) >= ((td_s64)(duration - SO_TIME_OFFSET)) || duration < SO_TIME_OFFSET)) {
|
|
/*
|
|
* clear this subtitle
|
|
* sdk add current time in clear_param,check it while clear pgs sub.
|
|
*/
|
|
member->clear_node_list[i].clear_param.clear_time = cur_pts;
|
|
if (clear_func != NULL) {
|
|
clear_func(member->draw_user_data,
|
|
(td_void *)&member->clear_node_list[i].clear_param);
|
|
}
|
|
member->clear_node_list[i].is_used = TD_FALSE;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
static td_s32 so_sync_proc_duration(const so_member *member, const SO_INFO_S *so_info,
|
|
so_sync_info *sync_info, SO_INFO_S *next_sub_info)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 duration;
|
|
td_s64 node_pts = 0;
|
|
td_bool clear_sub = sync_info->clear_sub;
|
|
td_bool direct_out = sync_info->direct_out;
|
|
td_s64 cur_pts = sync_info->cur_pts;
|
|
td_s64 cur_node_pts = sync_info->cur_node_pts;
|
|
td_u32 node_duration = sync_info->node_duration;
|
|
SO_INFO_S nextSub;
|
|
|
|
if (node_duration != 0) {
|
|
/* local time is larger than (pts + duration), skip this subtitle */
|
|
if (direct_out == TD_FALSE) {
|
|
SO_RETURN((cur_pts > cur_node_pts + (td_s64)node_duration), TD_FAILURE, NULL);
|
|
}
|
|
|
|
duration = (td_u32)((cur_node_pts + (td_s64)node_duration) - cur_pts);
|
|
duration = duration > node_duration ? node_duration : duration;
|
|
if (duration > SO_NORMAL_MAX_DISPLAY_TIME) {
|
|
duration = SO_NORMAL_MAX_DISPLAY_TIME;
|
|
} else if (duration < SO_NORMAL_MIN_DISPLAY_TIME) {
|
|
duration = SO_NORMAL_MIN_DISPLAY_TIME;
|
|
}
|
|
} else {
|
|
/* this subtitle has no duration, get pts of next subtitle in queue */
|
|
SO_CHK_FUN_RET_ERR(memset_s(&nextSub, sizeof(nextSub), 0, sizeof(nextSub));
|
|
ret = SO_QueueGetNodeInfoNotDel(member->queue_hdl, &nextSub));
|
|
|
|
if (ret != TD_SUCCESS) {
|
|
/* dufault duration */
|
|
duration = SO_NORMAL_MIN_DISPLAY_TIME;
|
|
} else {
|
|
so_get_node_pts(&nextSub, &node_pts, &node_duration);
|
|
node_pts += member->pts_offset;
|
|
|
|
/* local time is larger than this subtitle, skip last subtitle */
|
|
if (direct_out == TD_FALSE) {
|
|
SO_RETURN((cur_pts + SO_TIME_OFFSET >= node_pts), TD_FAILURE, NULL);
|
|
}
|
|
|
|
duration = (td_u32)(node_pts - cur_pts - SO_TIME_OFFSET);
|
|
duration = duration > SO_NORMAL_MAX_DISPLAY_TIME ? SO_NORMAL_MAX_DISPLAY_TIME : duration;
|
|
if (so_info->subt_type == UAPI_SUBTITLE_BITMAP && duration < SO_NORMAL_MAX_DISPLAY_TIME) {
|
|
clear_sub = TD_FALSE;
|
|
}
|
|
}
|
|
|
|
*next_sub_info = nextSub;
|
|
}
|
|
|
|
sync_info->duration = duration;
|
|
sync_info->clear_sub = clear_sub;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 so_sync_proc_msg_type(so_member *member, const SO_INFO_S *so_info, SO_INFO_S *next_sub_info,
|
|
uapi_so_getpts_callback_func get_pts_func)
|
|
{
|
|
/* pts of subtitle is less than or equal to local time */
|
|
if (so_info->subt_type == UAPI_SUBTITLE_BITMAP &&
|
|
so_info->subtitle_param.gfx.msg_type == UAPI_SO_DISP_MSG_ERASE) {
|
|
so_reset_clear_list(member);
|
|
|
|
/* if reset clear list, insert a empty node */
|
|
next_sub_info->subt_type = UAPI_SUBTITLE_MAX;
|
|
so_insert_to_clear_list(member, next_sub_info, 0, SO_TIME_OFFSET);
|
|
}
|
|
|
|
if (so_info->subt_type == UAPI_SUBTITLE_BITMAP &&
|
|
so_info->subtitle_param.gfx.msg_type == UAPI_SO_DISP_MSG_NORM) {
|
|
/* if no pix data in normal display message, just need to clear OSD only */
|
|
if (so_info->subtitle_param.gfx.x == 0 && so_info->subtitle_param.gfx.y == 0 &&
|
|
so_info->subtitle_param.gfx.w == 0 && so_info->subtitle_param.gfx.h == 0) {
|
|
so_reset_clear_list(member);
|
|
|
|
/* if reset clear list, insert a empty node */
|
|
next_sub_info->subt_type = UAPI_SUBTITLE_MAX;
|
|
so_insert_to_clear_list(member, next_sub_info, 0, SO_TIME_OFFSET);
|
|
so_clear_nodes(member, get_pts_func, member->clear_func);
|
|
|
|
return TD_FAILURE;
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_void so_reset_queue(so_member *member)
|
|
{
|
|
if (member->reset_pts) {
|
|
(td_void) SO_QueueResetByPts(member->queue_hdl, member->reset_pts);
|
|
member->reset_pts = 0;
|
|
} else {
|
|
SO_QueueReset(member->queue_hdl);
|
|
}
|
|
|
|
member->queue_reset = TD_FALSE;
|
|
so_reset_clear_list(member);
|
|
return;
|
|
}
|
|
|
|
static td_bool do_direct_output(td_s64 cur_pts, td_s64 cur_node_pts, td_u32 interval)
|
|
{
|
|
td_s64 max_pts, min_pts;
|
|
|
|
if (interval == 0) {
|
|
return TD_FALSE;
|
|
}
|
|
|
|
max_pts = cur_node_pts + (td_s64)interval;
|
|
min_pts = cur_node_pts - (td_s64)interval;
|
|
|
|
if ((cur_pts <= min_pts) || (cur_pts >= max_pts)) {
|
|
return TD_TRUE;
|
|
}
|
|
|
|
return TD_FALSE;
|
|
}
|
|
|
|
static td_bool so_is_directout(td_bool direct_out, td_s64 cur_pts, td_s64 cur_node_pts)
|
|
{
|
|
if ((cur_pts < g_prev_pts) && (g_prev_pts - cur_pts > SO_TIME_ERROR_OFFSET)) {
|
|
g_prev_pts = 0;
|
|
}
|
|
if ((direct_out == TD_TRUE) ||
|
|
((cur_pts > cur_node_pts) && (cur_node_pts > g_prev_pts)) ||
|
|
((cur_pts > cur_node_pts) && (cur_pts - cur_node_pts < SO_TIME_ERROR_OFFSET)) ||
|
|
((cur_node_pts > cur_pts) && (cur_node_pts - cur_pts > SO_TIME_ERROR_OFFSET))) {
|
|
g_prev_pts = cur_pts;
|
|
return TD_TRUE;
|
|
}
|
|
|
|
return TD_FALSE;
|
|
}
|
|
|
|
#ifdef ANDROID
|
|
td_s32 __attribute__((optnone)) subt_fix_draw_err(void)
|
|
#else
|
|
td_s32 __attribute__((optimize("-O0"))) subt_fix_draw_err(void)
|
|
#endif
|
|
{
|
|
const volatile td_s32 ret = 0;
|
|
EXT_INFO_SO("subt fit draw err test uninject\n");
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 subt_fix_draw_err_event_report(const char *func_name)
|
|
{
|
|
static td_u8 cnt = 0;
|
|
td_handle handle;
|
|
td_char name[PNAME_SUBT_MAX_LEN] = {0};
|
|
td_s32 ret = TD_SUCCESS;
|
|
|
|
if ((cnt % FMEA_SUBT_EVENT_REPORT_COUNT) == 0) {
|
|
prctl(PR_GET_NAME, name);
|
|
ret = dft_event_create(FMEA_EVENT_ID_DRAW_ERR, &handle);
|
|
if (ret == TD_SUCCESS) {
|
|
EXT_INFO_SO("subt fit draw err event,and report it\n");
|
|
dft_event_put_string(handle, "PNAME", name);
|
|
dft_event_put_string(handle, "F1NAME", func_name);
|
|
dft_event_report(handle);
|
|
dft_event_destroy(handle);
|
|
} else {
|
|
EXT_ERR_SO("subt draw err event not create!\n");
|
|
}
|
|
cnt = 1;
|
|
} else {
|
|
EXT_INFO_SO("subt fit draw err event,but not report\n");
|
|
cnt++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static td_void so_sync_output(so_member *member, uapi_so_getpts_callback_func get_pts_func,
|
|
uapi_so_ondraw_callback_func draw_func, uapi_so_onclear_callback_func clear_func, const SO_INFO_S *so_info)
|
|
{
|
|
td_s32 ret;
|
|
td_s64 cur_pts = 0, cur_node_pts = 0;
|
|
td_u32 node_duration = 0;
|
|
td_bool direct_out;
|
|
SO_INFO_S nextSub;
|
|
so_sync_info sync_info;
|
|
|
|
SO_CHK_FUN_RET_VOID(memset_s(&nextSub, sizeof(SO_INFO_S), 0, sizeof(SO_INFO_S)));
|
|
|
|
for (;;) {
|
|
so_check_return((member->thread_exit == TD_TRUE || member->queue_reset == TD_TRUE));
|
|
so_queque_reset_lock(ret);
|
|
so_clear_nodes(member, get_pts_func, clear_func);
|
|
so_queque_reset_unlock(ret);
|
|
|
|
(td_void) get_pts_func(member->pts_user_data, &cur_pts);
|
|
|
|
so_get_node_pts(so_info, &cur_node_pts, &node_duration);
|
|
|
|
cur_node_pts += member->pts_offset;
|
|
|
|
/* Throw away excess the last pts packets when flipping occurs */
|
|
if (((cur_pts + SO_PLAY_TIME_JUMP_JUDGE) < member->last_pts) && (cur_pts != UAPI_SO_NO_PTS)) {
|
|
SO_QueueResetAfterPts(member->queue_hdl, member->last_pts);
|
|
}
|
|
|
|
member->last_pts = cur_pts;
|
|
|
|
/* local time is invalid, do not output subtitle */
|
|
if (cur_pts == (td_s64)UAPI_SO_NO_PTS) {
|
|
SO_SLEEP(SO_TIME_OFFSET);
|
|
continue;
|
|
}
|
|
|
|
/* the subtitle display pts and video pts interval is too large,direct output app set interval time */
|
|
direct_out = do_direct_output(cur_pts, cur_node_pts, member->interval_ms);
|
|
if (so_is_directout(direct_out, cur_pts, cur_node_pts) == TD_TRUE) {
|
|
break;
|
|
}
|
|
|
|
/* pts of subtitle is larger than local time */
|
|
SO_SLEEP(SO_TIME_OFFSET);
|
|
}
|
|
|
|
sync_info.clear_sub = TD_TRUE;
|
|
sync_info.direct_out = direct_out;
|
|
sync_info.cur_node_pts = cur_node_pts;
|
|
sync_info.cur_pts = cur_pts;
|
|
sync_info.duration = 0;
|
|
sync_info.node_duration = node_duration;
|
|
|
|
ret = so_sync_proc_duration(member, so_info, &sync_info, &nextSub);
|
|
/* skip this subtitle */
|
|
so_check_return((ret != TD_SUCCESS));
|
|
|
|
ret = so_sync_proc_msg_type(member, so_info, &nextSub, get_pts_func);
|
|
/* skip this subtitle */
|
|
so_check_return((ret != TD_SUCCESS));
|
|
|
|
so_clear_nodes(member, get_pts_func, clear_func);
|
|
|
|
if (draw_func != NULL) {
|
|
if (subt_fix_draw_err() == 1) {
|
|
ret = TD_FAILURE;
|
|
} else {
|
|
ret = draw_func(member->draw_user_data, so_info, NULL);
|
|
}
|
|
if (ret == TD_FAILURE) {
|
|
subt_fix_draw_err_event_report("so_sync_output");
|
|
}
|
|
}
|
|
|
|
/* insert node into clear list */
|
|
if (sync_info.clear_sub) {
|
|
so_insert_to_clear_list(member, so_info, cur_pts, sync_info.duration);
|
|
}
|
|
}
|
|
|
|
static td_void *so_thread_main_function(td_void *arg)
|
|
{
|
|
td_s32 ret;
|
|
uapi_so_getpts_callback_func get_pts_func = NULL;
|
|
uapi_so_ondraw_callback_func draw_func = NULL;
|
|
uapi_so_onclear_callback_func clear_func = NULL;
|
|
|
|
SO_INFO_S stSubInfo;
|
|
so_member *member = (so_member *)arg;
|
|
|
|
(td_void) prctl(PR_SET_NAME, (unsigned long)"soc_so", 0, 0, 0);
|
|
memset_s(&stSubInfo, sizeof(SO_INFO_S), 0, sizeof(SO_INFO_S));
|
|
|
|
so_check_return_ret((member == NULL), NULL);
|
|
|
|
for (;;) {
|
|
if (member->thread_exit == TD_TRUE) {
|
|
break;
|
|
}
|
|
|
|
so_queque_reset_lock(ret);
|
|
if (member->queue_reset == TD_TRUE) {
|
|
so_reset_queue(member);
|
|
|
|
/* if reset clear list, insert a empty node */
|
|
stSubInfo.subt_type = UAPI_SUBTITLE_MAX;
|
|
so_insert_to_clear_list(member, &stSubInfo, 0, SO_TIME_OFFSET);
|
|
member->queue_reset = TD_FALSE;
|
|
}
|
|
so_queque_reset_unlock(ret);
|
|
|
|
so_callback_lock();
|
|
get_pts_func = member->get_pts_func;
|
|
draw_func = member->draw_func;
|
|
clear_func = member->clear_func;
|
|
so_callback_unlock();
|
|
so_clear_nodes(member, get_pts_func, clear_func);
|
|
|
|
memset_s(&stSubInfo, sizeof(stSubInfo), 0, sizeof(stSubInfo));
|
|
ret = SO_QueueGet(member->queue_hdl, &stSubInfo);
|
|
member->cur_sub_info = stSubInfo;
|
|
|
|
if (ret != TD_SUCCESS) {
|
|
SO_SLEEP(SO_TRHEAD_SLEEP_TIME);
|
|
continue;
|
|
}
|
|
|
|
/* if not set get_pts_func, skip all subtitle */
|
|
if (get_pts_func == NULL) {
|
|
SO_QueueFree(member->queue_hdl, &stSubInfo);
|
|
SO_SLEEP(SO_TRHEAD_SLEEP_TIME);
|
|
continue;
|
|
}
|
|
|
|
so_sync_output(member, get_pts_func, draw_func, clear_func, &stSubInfo);
|
|
|
|
so_queque_reset_lock(ret);
|
|
SO_QueueFree(member->queue_hdl, &stSubInfo);
|
|
so_queque_reset_unlock(ret);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
td_s32 uapi_so_init(td_void)
|
|
{
|
|
if (g_so_init == TD_TRUE) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
SO_CHK_FUN_RET_ERR(memset_s(g_so_hdl, sizeof(g_so_hdl), 0x0, sizeof(g_so_hdl)));
|
|
|
|
g_so_init = TD_TRUE;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_deinit(td_void)
|
|
{
|
|
td_u8 i;
|
|
|
|
if (g_so_init == TD_FALSE) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
for (i = 0; i < SO_INSTANCE_MAX_NUM; i++) {
|
|
if (g_so_hdl[i]) {
|
|
td_handle so_hdl = so_get_handle_by_index(i);
|
|
uapi_so_destroy(so_hdl);
|
|
}
|
|
}
|
|
|
|
SO_CHK_FUN_RET_ERR(memset_s(g_so_hdl, sizeof(g_so_hdl), 0, sizeof(g_so_hdl)));
|
|
|
|
g_so_init = TD_FALSE;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_create(td_handle *handle)
|
|
{
|
|
td_u16 idx = 0;
|
|
td_s32 ret;
|
|
so_member *member = NULL;
|
|
|
|
SO_RETURN(g_so_init == TD_FALSE, TD_FAILURE, "");
|
|
SO_RETURN(handle == NULL, TD_FAILURE, "");
|
|
|
|
ret = so_get_free_index(&idx);
|
|
if (ret != TD_SUCCESS) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
member = (so_member *)SO_MALLOC(sizeof(so_member));
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
memset_s(member, sizeof(so_member), 0, sizeof(so_member));
|
|
|
|
/* create the subtitle queue */
|
|
ret = SO_QueueInit(UAPI_SO_MAX_BUFFER_SIZE, UAPI_SO_MAX_NODE_NUM, &member->queue_hdl);
|
|
SO_CALL_RETURN(ret != TD_SUCCESS, SO_FREE(member), TD_FAILURE);
|
|
|
|
member->thread_exit = TD_FALSE;
|
|
member->queue_reset = TD_FALSE;
|
|
member->reset_pts = 0;
|
|
member->pts_offset = 0;
|
|
member->interval_ms = 0;
|
|
(td_void) pthread_mutex_init(&member->queue_reset_mutex, NULL);
|
|
(td_void) pthread_mutex_init(&member->mutex, NULL);
|
|
(td_void) pthread_attr_init(&member->struct_attr);
|
|
|
|
/* create the main task */
|
|
ret = pthread_create(&member->task_id, &member->struct_attr, so_thread_main_function, (td_void *)member);
|
|
if (ret != TD_SUCCESS) {
|
|
(td_void) SO_QueueDeinit(member->queue_hdl);
|
|
pthread_attr_destroy(&member->struct_attr);
|
|
(td_void) pthread_mutex_destroy(&member->mutex);
|
|
(td_void) pthread_mutex_destroy(&member->queue_reset_mutex);
|
|
SO_FREE(member);
|
|
member = NULL;
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
g_so_hdl[idx] = member;
|
|
*handle = so_get_handle_by_index(idx);
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_destroy(td_handle handle)
|
|
{
|
|
td_u16 idx;
|
|
idx = so_get_index_by_handle(handle);
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
member->queue_reset = TD_TRUE;
|
|
member->thread_exit = TD_TRUE;
|
|
pthread_join(member->task_id, NULL);
|
|
pthread_attr_destroy(&member->struct_attr);
|
|
(td_void) pthread_mutex_destroy(&member->mutex);
|
|
(td_void) pthread_mutex_destroy(&member->queue_reset_mutex);
|
|
|
|
(td_void) SO_QueueDeinit(member->queue_hdl);
|
|
SO_FREE(member);
|
|
member = NULL;
|
|
|
|
g_so_hdl[idx] = SO_INVALID_HANDLE;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_reg_getpts(td_handle handle, uapi_so_getpts_callback_func getpts_func, td_void *userdata)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN((member == NULL || getpts_func == NULL), TD_FAILURE, "");
|
|
so_callback_lock();
|
|
member->get_pts_func = getpts_func;
|
|
member->pts_user_data = userdata;
|
|
so_callback_unlock();
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_reg_draw(td_handle handle, uapi_so_ondraw_callback_func draw_func,
|
|
uapi_so_onclear_callback_func clear_func, td_void *userdata)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN((member == NULL || draw_func == NULL || clear_func == NULL), TD_FAILURE, "");
|
|
so_callback_lock();
|
|
member->draw_func = draw_func;
|
|
member->clear_func = clear_func;
|
|
member->draw_user_data = userdata;
|
|
so_callback_unlock();
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_set_surface(td_handle handle, td_handle surface_handle)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN((member == NULL || surface_handle == 0), TD_FAILURE, "");
|
|
so_callback_lock();
|
|
member->surface = surface_handle;
|
|
so_callback_unlock();
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_set_font(td_handle handle, td_handle font)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
member->font = (td_handle)font;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_set_offset(td_handle handle, td_s64 offsetMs)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
member->pts_offset = offsetMs;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_set_color(td_handle handle, td_u32 color)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
member->color = color;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_set_pos(td_handle handle, td_u32 x, td_u32 y)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
member->x = x;
|
|
member->y = y;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_get_remaining_subt(td_handle handle, td_u32 *subNum)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN(member == NULL || subNum == NULL, TD_FAILURE, "");
|
|
*subNum = SO_QueueNum(member->queue_hdl);
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_reset_buf(td_handle handle)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
td_u32 i = 0;
|
|
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
member->queue_reset = TD_TRUE;
|
|
member->last_pts = 0;
|
|
|
|
while (i < SO_QUEUE_RESET_CHECK_TIME) {
|
|
if (member->queue_reset == TD_FALSE) {
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
SO_SLEEP(SO_TIME_OFFSET);
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_reset_bufByPts(td_handle handle, td_s64 pts)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
member->reset_pts = pts;
|
|
return uapi_so_reset_buf(handle);
|
|
}
|
|
|
|
td_s32 uapi_so_send_data(td_handle handle, const uapi_so_subtitle_info *subInfo, td_u32 timeOut)
|
|
{
|
|
unused(timeOut);
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN((member == NULL || subInfo == NULL), TD_FAILURE, "");
|
|
SO_RETURN((subInfo->subt_type >= UAPI_SUBTITLE_MAX), TD_FAILURE, "");
|
|
|
|
return SO_QueuePut(member->queue_hdl, (const SO_INFO_S *)subInfo);
|
|
}
|
|
|
|
td_s32 uapi_so_get_cur_data(td_handle handle, uapi_so_subtitle_info *subInfo)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
SO_RETURN(subInfo == NULL, TD_FAILURE, "");
|
|
*subInfo = member->cur_sub_info;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 uapi_so_set_max_interval(td_handle handle, td_u32 interval_ms)
|
|
{
|
|
so_member *member = (so_member *)so_get_memory_addr(handle);
|
|
|
|
SO_RETURN(member == NULL, TD_FAILURE, "");
|
|
member->interval_ms = interval_ms;
|
|
|
|
return TD_SUCCESS;
|
|
}
|