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