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

/*
* 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;
}