/* * Copyright (c) Hisilicon Technologies Co., Ltd. 2009-2019. All rights reserved. * Description: Defines avplay export function * Author: Hisilicon * Create: 2009-12-21 * History: */ #include "drv_avplay.h" #include "drv_module_ext.h" #include "drv_demux_struct.h" #include "drv_sync_ext.h" #include "drv_vdec_ext.h" #include "drv_policy_ext.h" #define big_endian_write(a, b) (((a) << 32) | (b)) #define DRV_AVPLAY_MAX_VIR_WIN_CNT 1 typedef enum { AVPLAY_PARAM_INC = 0, /* 递增 */ AVPLAY_PARAM_ADD, /* 累加 */ AVPLAY_PARAM_RES /* 覆盖 */ } avplay_param_type; typedef struct { demux_func_export *dmx_func; drv_vdec_export_func *vdec_func; void *win_func; } avplay_import_func; typedef struct { td_bool update; td_u64 param; avplay_param_type param_type; } avplay_event_info; typedef struct { td_handle self; td_handle vdec; td_handle vid_dmx; td_handle aud_dmx; td_handle pcr_dmx; td_handle win; td_handle virtual_win[DRV_AVPLAY_MAX_VIR_WIN_CNT]; td_u8 virtual_win_cnt; td_handle sync; td_bool vid_enable; td_bool aud_enable; avplay_resolution_param res; td_u64 event_mask; /* 一个bit对应一种事件, 用于控制是否立即上报事件, 0: disable; 1: enable */ avplay_event_info events[AVPLAY_EVENT_TYPE_MAX]; /* protected by instance->spin */ } avplay_drv_ctx; typedef struct { td_handle handle; avplay_drv_ctx *ctx; const void *unique; td_bool wait_cond; osal_wait wq; avplay_drv_spin_t spin; /* used in the context of interruption */ avplay_drv_mutex_t mutex; } avplay_drv_instance; static td_bool g_avplay_inited = TD_FALSE; static td_u32 g_avplay_instance_cnt = 0; static td_u32 g_avplay_play_cnt = 0; static avplay_drv_instance g_avplay_instance[AVPLAY_MAX_CNT]; static avplay_drv_spin_t g_avplay_spinlock; static avplay_import_func g_avplay_import_func = {TD_NULL}; static avplay_param_type g_avplay_param_types[AVPLAY_EVENT_TYPE_MAX] = { [AVPLAY_EVENT_VID_RENDER_UNDERLOAD] = AVPLAY_PARAM_ADD, [AVPLAY_EVENT_VID_ERR_FRAME] = AVPLAY_PARAM_RES, [AVPLAY_EVENT_VID_TYPE_ERR] = AVPLAY_PARAM_RES, [AVPLAY_EVENT_VID_DECODE_FORMAT_CHANGE] = AVPLAY_PARAM_RES, [AVPLAY_EVENT_VID_DECODE_PACKING_CHANGE] = AVPLAY_PARAM_RES, [AVPLAY_EVENT_VID_FIRST_PTS] = AVPLAY_PARAM_RES, [AVPLAY_EVENT_VID_SECOND_PTS] = AVPLAY_PARAM_RES, [AVPLAY_EVENT_VID_DECODE_EOS] = AVPLAY_PARAM_RES, [AVPLAY_EVENT_VID_NEW_DMX_IDX] = AVPLAY_PARAM_RES }; static td_s32 drv_wakeup(avplay_drv_instance *instance); static void ctx_init(avplay_drv_ctx *ctx, td_handle avplay) { td_u32 i; ctx->self = avplay; ctx->vdec = TD_INVALID_HANDLE; ctx->vid_dmx = TD_INVALID_HANDLE; ctx->aud_dmx = TD_INVALID_HANDLE; ctx->win = TD_INVALID_HANDLE; ctx->virtual_win_cnt = 0; for (i = 0; i < DRV_AVPLAY_MAX_VIR_WIN_CNT; i++) { ctx->virtual_win[i] = TD_INVALID_HANDLE; } ctx->sync = TD_INVALID_HANDLE; ctx->vid_enable = TD_FALSE; ctx->aud_enable = TD_FALSE; ctx->res.width = 0; ctx->res.height = 0; ctx->res.stride = 0; ctx->res.bit_depth = 0; ctx->res.frm_num = 0; ctx->res.frm_size = 0; ctx->res.meta_size = 0; ctx->event_mask = 0LL; for (i = 0; i < AVPLAY_EVENT_TYPE_MAX; i++) { ctx->events[i].update = TD_FALSE; ctx->events[i].param = 0LL; ctx->events[i].param_type = g_avplay_param_types[i]; } } static void delete_win_attach(avplay_drv_ctx *ctx) { td_s32 ret; td_u8 i = 0; ext_sys_policy_win_attach_info info; info.source_handle = ctx->self; if (ctx->win != TD_INVALID_HANDLE) { info.window_handle = ctx->win; ret = ext_drv_sys_policy_del_win_attach_info(&info); log_when_error(ret, ext_drv_sys_del_window_attach_info); if (ret == TD_SUCCESS) { ctx->win = TD_INVALID_HANDLE; } } for (; i < DRV_AVPLAY_MAX_VIR_WIN_CNT; i++) { if (ctx->virtual_win[i] != TD_INVALID_HANDLE) { info.window_handle = ctx->virtual_win[i]; ret = ext_drv_sys_policy_del_win_attach_info(&info); log_when_error(ret, ext_drv_sys_del_window_attach_info); if (ret == TD_SUCCESS) { ctx->virtual_win[i] = TD_INVALID_HANDLE; } } } } static void ctx_deinit(avplay_drv_instance *instance, avplay_drv_ctx **free_ctx) { td_ulong flags = 0; avplay_mutex_lock(&instance->mutex); avplay_spin_lock(&instance->spin, flags); if (instance->ctx != TD_NULL) { drv_wakeup(instance); delete_win_attach(instance->ctx); *free_ctx = instance->ctx; instance->ctx = TD_NULL; /* ctx is freed outside spinlock by caller */ } avplay_spin_unlock(&instance->spin, flags); avplay_mutex_unlock(&instance->mutex); } static void instance_init(void) { td_u32 i = 0; if (g_avplay_inited) { return; } for (; i < AVPLAY_MAX_CNT; i++) { g_avplay_instance[i].handle = TD_INVALID_HANDLE; g_avplay_instance[i].ctx = TD_NULL; g_avplay_instance[i].unique = TD_NULL; avplay_mutex_init(&g_avplay_instance[i].mutex); } g_avplay_instance_cnt = 0; g_avplay_play_cnt = 0; g_avplay_inited = TD_TRUE; } static avplay_drv_instance *instance_get(td_handle handle) { avplay_drv_instance *instance = TD_NULL; td_u32 index = avplay_get_id(handle); if (index >= AVPLAY_MAX_CNT || avplay_get_modid(handle) != SOC_ID_AVPLAY) { ext_err_avplay("Invalid avplay handle[0x%x]\n", handle); return TD_NULL; } if (!g_avplay_inited) { ext_err_avplay("AVPLAY not inited\n"); return TD_NULL; } if (g_avplay_instance[index].ctx == TD_NULL) { ext_warn_avplay("Invalid avplay handle[0x%x], instance context is NULL\n", handle); } else if (g_avplay_instance[index].handle != handle) { ext_err_avplay("Invalid avplay handle[0x%x], instance handle[0x%x] check failed\n", handle, g_avplay_instance[index].handle); } else { instance = &g_avplay_instance[index]; } return instance; } static td_s32 instance_alloc(td_handle *handle, const void *unique, avplay_drv_ctx **external_ctx) { td_u32 i = 0; if (!g_avplay_inited) { ext_err_avplay("AVPLAY not inited\n"); return SOC_ERR_AVPLAY_DEV_NO_INIT; } for (; i < AVPLAY_MAX_CNT; i++) { if (g_avplay_instance[i].handle == TD_INVALID_HANDLE) { break; } } if (i >= AVPLAY_MAX_CNT) { ext_err_avplay("Too many avplays are created, instance cnt = %u\n", g_avplay_instance_cnt); return SOC_ERR_AVPLAY_CREATE_ERR; } if (g_avplay_instance[i].ctx == TD_NULL) { g_avplay_instance[i].ctx = *external_ctx; *external_ctx = TD_NULL; } ctx_init(g_avplay_instance[i].ctx, avplay_get_handle(i)); g_avplay_instance[i].wait_cond = TD_FALSE; g_avplay_instance_cnt++; g_avplay_instance[i].handle = avplay_get_handle(i); g_avplay_instance[i].unique = unique; *handle = g_avplay_instance[i].handle; ext_dbg_avplay("Alloc drv avplay, index = 0x%x, instance_cnt = %u\n", i, g_avplay_instance_cnt); return TD_SUCCESS; } static td_s32 instance_del(td_handle handle, avplay_drv_ctx **free_ctx) { td_u32 index = avplay_get_id(handle); if (index >= AVPLAY_MAX_CNT) { ext_err_avplay("Invalid handle, index = %u\n", index); return TD_FAILURE; } ctx_deinit(&g_avplay_instance[index], free_ctx); g_avplay_instance[index].handle = TD_INVALID_HANDLE; g_avplay_instance[index].unique = TD_NULL; g_avplay_instance_cnt--; ext_dbg_avplay("Delete drv avplay, index = 0x%x\n", index); return TD_SUCCESS; } static td_s32 instance_clear(const void *unique, avplay_drv_ctx *free_ctx[], td_u32 num) { td_u32 i = 0; TD_UNUSED(num); for (; i < AVPLAY_MAX_CNT; i++) { if (g_avplay_instance[i].unique == unique) { ctx_deinit(&g_avplay_instance[i], &free_ctx[i]); g_avplay_instance[i].handle = TD_INVALID_HANDLE; g_avplay_instance[i].unique = TD_NULL; ext_dbg_avplay("Clear drv avplay, index = 0x%x\n", i); } } return TD_SUCCESS; } static void instance_deinit(avplay_drv_ctx *free_ctx[], td_u32 num) { td_u32 i = 0; TD_UNUSED(num); for (; i < AVPLAY_MAX_CNT; i++) { ctx_deinit(&g_avplay_instance[i], &free_ctx[i]); osal_wait_destroy(&g_avplay_instance[i].wq); avplay_spin_deinit(&g_avplay_instance[i].spin); avplay_mutex_deinit(&g_avplay_instance[i].mutex); g_avplay_instance[i].handle = TD_INVALID_HANDLE; g_avplay_instance[i].unique = TD_NULL; } } static void drv_instance_init(void) { td_u32 i = 0; td_ulong flags = 0; avplay_spin_lock(&g_avplay_spinlock, flags); instance_init(); avplay_spin_unlock(&g_avplay_spinlock, flags); for (; i < AVPLAY_MAX_CNT; i++) { avplay_spin_init(&g_avplay_instance[i].spin); osal_wait_init(&g_avplay_instance[i].wq); } } static avplay_drv_instance *drv_instance_get(td_handle handle) { td_ulong flags = 0; avplay_drv_instance *instance = TD_NULL; avplay_spin_lock(&g_avplay_spinlock, flags); instance = instance_get(handle); avplay_spin_unlock(&g_avplay_spinlock, flags); return instance; } static td_s32 drv_instance_alloc(td_handle *handle, const void *unique) { td_s32 ret; td_ulong flags = 0; avplay_drv_ctx *ctx = TD_NULL; ctx = (avplay_drv_ctx*)avplay_malloc(sizeof(avplay_drv_ctx)); check_null(ctx); avplay_spin_lock(&g_avplay_spinlock, flags); ret = instance_alloc(handle, unique, &ctx); avplay_spin_unlock(&g_avplay_spinlock, flags); if (ret != TD_SUCCESS || ctx != TD_NULL) { avplay_free(ctx); } return ret; } static td_s32 drv_instance_del(td_handle handle) { td_s32 ret; td_ulong flags = 0; avplay_drv_ctx *free_ctx = TD_NULL; avplay_spin_lock(&g_avplay_spinlock, flags); ret = instance_del(handle, &free_ctx); avplay_spin_unlock(&g_avplay_spinlock, flags); if (free_ctx != TD_NULL) { avplay_free(free_ctx); } return ret; } static td_s32 drv_instance_clear(const void *unique) { td_s32 ret; td_ulong flags = 0; td_u32 i = 0; avplay_drv_ctx *free_ctx[AVPLAY_MAX_CNT] = {TD_NULL}; avplay_spin_lock(&g_avplay_spinlock, flags); ret = instance_clear(unique, free_ctx, AVPLAY_MAX_CNT); avplay_spin_unlock(&g_avplay_spinlock, flags); for (; i < AVPLAY_MAX_CNT; i++) { if (free_ctx[i] != TD_NULL) { avplay_free(free_ctx[i]); } } return ret; } static void drv_instance_deinit(void) { td_ulong flags = 0; td_u32 i = 0; avplay_drv_ctx *free_ctx[AVPLAY_MAX_CNT] = {TD_NULL}; avplay_spin_lock(&g_avplay_spinlock, flags); instance_deinit(free_ctx, AVPLAY_MAX_CNT); avplay_spin_unlock(&g_avplay_spinlock, flags); for (; i < AVPLAY_MAX_CNT; i++) { if (free_ctx[i] != TD_NULL) { avplay_free(free_ctx[i]); } } } #define AVPLAY_INSTANCE (instance) #define AVPLAY_DRV_CTX (AVPLAY_INSTANCE->ctx) #define avplay_drv_do_func(avplay, func) do { \ td_s32 ret = TD_SUCCESS; \ td_ulong flags = 0; \ avplay_drv_instance *AVPLAY_INSTANCE = drv_instance_get(avplay); \ check_null(AVPLAY_INSTANCE); \ avplay_spin_lock(&AVPLAY_INSTANCE->spin, flags); \ \ if (AVPLAY_DRV_CTX != TD_NULL) { \ ret = func; \ } else { \ ret = SOC_ERR_AVPLAY_NULL_PTR; \ ext_err_avplay("Instance ctx is NULL\n"); \ } \ \ avplay_spin_unlock(&AVPLAY_INSTANCE->spin, flags); \ return ret; \ } while (0) #define avplay_drv_do_func_without_spin(avplay, func) do { \ td_s32 ret = TD_SUCCESS; \ avplay_drv_instance *AVPLAY_INSTANCE = drv_instance_get(avplay); \ check_null(AVPLAY_INSTANCE); \ \ if (AVPLAY_DRV_CTX != TD_NULL) { \ ret = func; \ } else { \ ret = SOC_ERR_AVPLAY_NULL_PTR; \ ext_err_avplay("Instance ctx is NULL\n"); \ } \ \ return ret; \ } while (0) static td_void set_resolution_param(const ext_vdec_size_change *vdec_param, avplay_resolution_param *param) { if (vdec_param == TD_NULL || param == TD_NULL) { return; } param->width = vdec_param->new_width; param->height = vdec_param->new_height; param->stride = vdec_param->new_stride; param->bit_depth = vdec_param->new_bit_depth; param->frm_num = vdec_param->new_frm_num; param->frm_size = vdec_param->new_frm_size; param->meta_size = vdec_param->new_meta_size; } static td_s32 event_process(avplay_drv_instance *instance, avplay_event_type event, td_u64 param) { avplay_drv_ctx *ctx = instance->ctx; check_null(ctx); check_param(event < AVPLAY_EVENT_TYPE_MAX); if (event == AVPLAY_EVENT_VID_DECODE_RESOLUTION_CHANGE) { const ext_vdec_size_change *res = (ext_vdec_size_change *)(uintptr_t)param; set_resolution_param(res, &ctx->res); } if (ctx->events[event].param_type == AVPLAY_PARAM_ADD) { ctx->events[event].param += param; } else if (ctx->events[event].param_type == AVPLAY_PARAM_RES) { ctx->events[event].param = param; } else { ctx->events[event].param++; } ctx->events[event].update = TD_TRUE; if (is_bit_enable(ctx->event_mask, event)) { return drv_wakeup(instance); } return TD_SUCCESS; } static td_s32 process_callback_event(td_handle avplay, avplay_event_type event_type, td_u64 event_param) { td_s32 ret; td_ulong flags = 0; avplay_drv_instance *instance = TD_NULL; instance = drv_instance_get(avplay); if (instance == TD_NULL) { ext_warn_avplay("Instance is not exist.\n"); return SOC_ERR_AVPLAY_NULL_PTR; } avplay_spin_lock(&instance->spin, flags); ret = event_process(instance, event_type, event_param); avplay_spin_unlock(&instance->spin, flags); return ret; } static td_s32 vid_dmx_callback(td_handle avplay, ext_dmx_evt_id evt_id, const void *param, td_u32 param_size) { td_u64 event_param = 0LL; avplay_event_type event_type = AVPLAY_EVENT_TYPE_MAX; switch (evt_id) { case EXT_DMX_EVT_ERR: event_type = AVPLAY_EVENT_VID_DMX_ERR; break; case EXT_DMX_EVT_NEW_PES: event_type = AVPLAY_EVENT_VID_NEW_PES; break; case EXT_DMX_EVT_EOS: event_type = AVPLAY_EVENT_VID_DMX_EOS; break; case EXT_DMX_EVT_OVFL: ext_warn_avplay("video demux event id[0x%x].\n", evt_id); return TD_SUCCESS; default: ext_err_avplay("Unsupport video demux event id[0x%x].\n", evt_id); return SOC_ERR_AVPLAY_NOT_SUPPORT; } soc_trace_avplay("Receive video demux event id[0x%x], avplay event id = 0x%x\n", evt_id, event_type); return process_callback_event(avplay, event_type, event_param); } static td_s32 aud_dmx_callback(td_handle avplay, ext_dmx_evt_id evt_id, const void *param, td_u32 param_size) { td_u64 event_param = 0LL; avplay_event_type event_type = AVPLAY_EVENT_TYPE_MAX; switch (evt_id) { case EXT_DMX_EVT_ERR: event_type = AVPLAY_EVENT_AUD_DMX_ERR; break; case EXT_DMX_EVT_NEW_PES: event_type = AVPLAY_EVENT_AUD_NEW_PES; break; case EXT_DMX_EVT_EOS: event_type = AVPLAY_EVENT_AUD_DMX_EOS; break; case EXT_DMX_EVT_OVFL: ext_warn_avplay("audio demux event id[0x%x].\n", evt_id); return TD_SUCCESS; default: ext_err_avplay("Unsupport audio demux event id[0x%x].\n", evt_id); return SOC_ERR_AVPLAY_NOT_SUPPORT; } soc_trace_avplay("Receive audio demux event id[0x%x], avplay event id = 0x%x\n", evt_id, event_type); return process_callback_event(avplay, event_type, event_param); } static td_s32 pcr_dmx_callback(td_handle avplay, ext_dmx_evt_id evt_id, const void *param, td_u32 param_size) { td_s32 ret = TD_SUCCESS; const dmx_sync_pcr *info = TD_NULL; check_null(param); if (evt_id == EXT_DMX_EVT_PCR) { info = (const dmx_sync_pcr *)param; ret = ext_drv_sync_pcr_proc(info->handle, info->pcr_value, info->scr_value); } return ret; } static td_s32 dmx_index_callback(td_handle avplay, ext_dmx_opt_type opt_type, const dmx_index_data *data) { check_null(data); if (opt_type != EXT_DMX_OPT_ADD) { return TD_SUCCESS; } else if (data->frame_type != DMX_FRAME_TYPE_I && data->frame_type != DMX_FRAME_TYPE_IDR) { return TD_SUCCESS; } ext_dbg_avplay("Receive demux index event: type[%d] size[%u] offset[%llu] pts[%lld] \n", data->frame_type, data->frame_size, data->global_offset, data->pts_us); return process_callback_event(avplay, AVPLAY_EVENT_VID_NEW_DMX_IDX, data->global_offset); } static void err_frame_info_to_u64(const void *param, td_u32 param_size, td_u64 *event_param) { const ext_vdec_err_frm *err_frame = (const ext_vdec_err_frm*)param; td_u64 a = err_frame->err_frm_type; td_u64 b = err_frame->err_ratio; *event_param = big_endian_write(a, b); } static td_s32 vdec_evt_map_with_param(ext_vdec_evt_id evt_id, const void *param, td_u32 param_size, avplay_event_type *avplay_evt, td_u64 *event_param) { check_null(param); switch (evt_id) { case EXT_VDEC_EVT_FIRST_PTS: { *avplay_evt = AVPLAY_EVENT_VID_FIRST_PTS; *event_param = *(const td_s64*)param; break; } case EXT_VDEC_EVT_SECOND_PTS: { *avplay_evt = AVPLAY_EVENT_VID_SECOND_PTS; *event_param = *(const td_s64*)param; break; } case EXT_VDEC_EVT_EOS: { const ext_vdec_eos_param *eos = param; *avplay_evt = AVPLAY_EVENT_VID_DECODE_EOS; *event_param = eos->last_frame_flag; break; } case EXT_VDEC_EVT_ERR_STANDARD: { *avplay_evt = AVPLAY_EVENT_VID_TYPE_ERR; *event_param = *(const td_u32*)param; break; } case EXT_VDEC_EVT_NORM_CHG: { const ext_vdec_norm_param *norm = param; *avplay_evt = AVPLAY_EVENT_VID_DECODE_FORMAT_CHANGE; *event_param = norm->norm_type; break; } case EXT_VDEC_EVT_SIZE_CHG: { const ext_vdec_size_change *res = param; *avplay_evt = AVPLAY_EVENT_VID_DECODE_RESOLUTION_CHANGE; *event_param = (td_u64)(uintptr_t)res; break; } case EXT_VDEC_EVT_FRM_PCK_CHG: { *avplay_evt = AVPLAY_EVENT_VID_DECODE_PACKING_CHANGE; *event_param = *(const td_u32*)param; break; } case EXT_VDEC_EVT_ERR_FRAME: { *avplay_evt = AVPLAY_EVENT_VID_ERR_FRAME; err_frame_info_to_u64(param, param_size, event_param); break; } default: ext_err_avplay("Unsupport vdec event id[0x%x]\n", evt_id); return SOC_ERR_AVPLAY_NOT_SUPPORT; } return TD_SUCCESS; } static td_s32 vdec_callback(td_handle avplay, ext_vdec_evt_id evt_id, const void *param, td_u32 param_size) { td_s32 ret; td_u64 event_param = 0LL; avplay_event_type event_type = AVPLAY_EVENT_TYPE_MAX; switch (evt_id) { case EXT_VDEC_EVT_NEW_FRAME: event_type = AVPLAY_EVENT_VID_NEW_FRAME; break; case EXT_VDEC_EVT_NEW_STREAM: event_type = AVPLAY_EVENT_VID_RLS_PES; break; case EXT_VDEC_EVT_NEW_USER_DATA: event_type = AVPLAY_EVENT_VID_NEW_USER_DATA; break; case EXT_VDEC_EVT_UNSUPPORT: event_type = AVPLAY_EVENT_VID_UNSUPPORT; break; default: ret = vdec_evt_map_with_param(evt_id, param, param_size, &event_type, &event_param); return_when_error(ret, vdec_evt_map_with_param); } soc_trace_avplay("Receive vdec event id[0x%x], avplay event id = 0x%x\n", evt_id, event_type); return process_callback_event(avplay, event_type, event_param); } static td_bool pop_event(avplay_drv_ctx *ctx, avplay_event_type *event, td_u64 *param) { td_s32 i; avplay_event_info *event_info = TD_NULL; if (ctx == TD_NULL) { return TD_FALSE; } for (i = 0; i < AVPLAY_EVENT_TYPE_MAX; i++) { event_info = &ctx->events[i]; if (event_info->update) { *event = i; *param = event_info->param; event_info->update = TD_FALSE; event_info->param = 0LL; return TD_TRUE; } } return TD_FALSE; } static td_bool drv_pop_event(avplay_drv_instance *instance, avplay_event_type *event, td_u64 *param) { td_bool ret; td_ulong flags = 0; avplay_spin_lock(&instance->spin, flags); ret = pop_event(instance->ctx, event, param); avplay_spin_unlock(&instance->spin, flags); return ret; } static td_s32 drv_reset(avplay_drv_ctx *ctx, avplay_channel_type chn) { td_u32 i; (void)chn; for (i = 0; i < AVPLAY_EVENT_TYPE_MAX; i++) { ctx->events[i].update = TD_FALSE; ctx->events[i].param = 0LL; } return TD_SUCCESS; } static td_s32 drv_set_channel_state(avplay_drv_ctx *ctx, avplay_channel_type chn, avplay_channel_state state) { if (chn == AVPLAY_CHANNEL_AUD || chn == AVPLAY_CHANNEL_MAX) { if (state == AVPLAY_CHANNEL_START) { ctx->aud_enable = TD_TRUE; } else if (state == AVPLAY_CHANNEL_STOP) { ctx->aud_enable = TD_FALSE; } } if (chn == AVPLAY_CHANNEL_VID || chn == AVPLAY_CHANNEL_MAX) { if (state == AVPLAY_CHANNEL_START) { ctx->vid_enable = TD_TRUE; } else if (state == AVPLAY_CHANNEL_STOP) { ctx->vid_enable = TD_FALSE; } } return TD_SUCCESS; } static void drv_set_dmx_index_callback(td_handle dmx, avplay_drv_instance *instance, drv_dmx_play_index_callback callback) { td_s32 ret; td_handle avplay; td_ulong flags = 0; avplay_spin_lock(&instance->spin, flags); if (instance->ctx == TD_NULL) { avplay_spin_unlock(&instance->spin, flags); return; } avplay = instance->ctx->self; avplay_spin_unlock(&instance->spin, flags); if (dmx == TD_INVALID_HANDLE || g_avplay_import_func.dmx_func == TD_NULL || g_avplay_import_func.dmx_func->dmx_set_play_index_callback == TD_NULL) { ext_err_avplay("dmx_set_play_index_callback is NULL, cannot set callback\n"); return; } ret = g_avplay_import_func.dmx_func->dmx_set_play_index_callback(dmx, avplay, callback); log_when_error(ret, dmx_set_play_index_callback); return; } static td_s32 drv_set_dmx_callback(avplay_drv_instance *instance, td_handle handle, avplay_resource_type type, drv_dmx_callback callback) { td_s32 ret = TD_SUCCESS; td_handle avplay; td_handle dmx_handle = TD_INVALID_HANDLE; td_ulong flags = 0; avplay_spin_lock(&instance->spin, flags); if (instance->ctx == TD_NULL) { avplay_spin_unlock(&instance->spin, flags); return TD_FAILURE; } avplay = instance->ctx->self; switch (type) { case AVPLAY_RESOURCE_TYPE_VID_DEMUX: { dmx_handle = instance->ctx->vid_dmx; instance->ctx->vid_dmx = handle; break; } case AVPLAY_RESOURCE_TYPE_AUD_DEMUX: { dmx_handle = instance->ctx->aud_dmx; instance->ctx->aud_dmx = handle; break; } case AVPLAY_RESOURCE_TYPE_PCR_DEMUX: { dmx_handle = instance->ctx->pcr_dmx; instance->ctx->pcr_dmx = handle; break; } default: { avplay_spin_unlock(&instance->spin, flags); return TD_FAILURE; } } avplay_spin_unlock(&instance->spin, flags); if (g_avplay_import_func.dmx_func == TD_NULL || g_avplay_import_func.dmx_func->dmx_set_callback == TD_NULL) { ext_err_avplay("drv_dmx_set_callback is NULL, cannot set resrouce\n"); return SOC_ERR_AVPLAY_INVALID_OPT; } if (handle != TD_INVALID_HANDLE) { ret = g_avplay_import_func.dmx_func->dmx_set_callback(handle, avplay, callback); } else if (dmx_handle != TD_INVALID_HANDLE) { ret = g_avplay_import_func.dmx_func->dmx_set_callback(dmx_handle, avplay, TD_NULL); } return ret; } static td_s32 drv_set_vdec_callback(avplay_drv_instance *instance, td_handle handle, fn_drv_vdec_callback callback) { td_s32 ret = TD_SUCCESS; td_ulong flags = 0; td_handle avplay; td_handle vdec_handle; fn_drv_vdec_set_callback vdec_set_cb = TD_NULL; avplay_spin_lock(&instance->spin, flags); if (instance->ctx == TD_NULL) { avplay_spin_unlock(&instance->spin, flags); return TD_FAILURE; } avplay = instance->ctx->self; vdec_handle = instance->ctx->vdec; instance->ctx->vdec = handle; avplay_spin_unlock(&instance->spin, flags); if (g_avplay_import_func.vdec_func != TD_NULL && g_avplay_import_func.vdec_func->pfn_drv_vdec_set_callback != TD_NULL) { vdec_set_cb = g_avplay_import_func.vdec_func->pfn_drv_vdec_set_callback; } else { ext_err_avplay("drv_vdec_set_callback is NULL, cannot set resrouce\n"); return SOC_ERR_AVPLAY_INVALID_OPT; } if (handle != TD_INVALID_HANDLE) { ret = vdec_set_cb(handle, avplay, vdec_callback); } else if (vdec_handle != TD_INVALID_HANDLE) { ret = vdec_set_cb(vdec_handle, avplay, TD_NULL); } return ret; } static td_bool find_virtual_win(const avplay_drv_instance *instance, td_handle handle, td_u32 *index) { td_u32 i; for (i = 0; i < DRV_AVPLAY_MAX_VIR_WIN_CNT; i++) { if (instance->ctx->virtual_win[i] == handle) { *index = i; return TD_TRUE; } } for (i = 0; i < DRV_AVPLAY_MAX_VIR_WIN_CNT; i++) { if (instance->ctx->virtual_win[i] == TD_INVALID_HANDLE) { *index = i; return TD_FALSE; } } return TD_FALSE; } static td_s32 set_win_resource(avplay_drv_instance *instance, td_handle handle, td_u32 param) { td_s32 ret = TD_SUCCESS; td_ulong flags = 0; td_u32 index = 0; td_bool is_win_valid; avplay_spin_lock(&instance->spin, flags); if (instance->ctx == TD_NULL) { avplay_spin_unlock(&instance->spin, flags); return TD_FAILURE; } if (param == TD_FALSE) { if (instance->ctx->win == TD_INVALID_HANDLE) { ext_dbg_avplay("attach master window[0x%x] to avplay[0x%x]\n", handle, instance->ctx->self); instance->ctx->win = handle; } else if (instance->ctx->win == handle) { ext_dbg_avplay("detach master window[0x%x] to avplay[0x%x]\n", handle, instance->ctx->self); instance->ctx->win = TD_INVALID_HANDLE; } else { ext_err_avplay("Only one master window is suppurted\n"); ret = TD_FAILURE; } } else { is_win_valid = find_virtual_win(instance, handle, &index); if (is_win_valid == TD_TRUE) { ext_dbg_avplay("detach virtual window[0x%x] to avplay[0x%x]\n", handle, instance->ctx->self); instance->ctx->virtual_win[index] = TD_INVALID_HANDLE; instance->ctx->virtual_win_cnt--; } else if (index < DRV_AVPLAY_MAX_VIR_WIN_CNT) { ext_dbg_avplay("attach virturl window[0x%x] to avplay[0x%x]\n", handle, instance->ctx->self); instance->ctx->virtual_win[index] = handle; instance->ctx->virtual_win_cnt++; } else { ext_err_avplay("Too many windwos[%zu] attached\n", instance->ctx->virtual_win_cnt); ret = TD_FAILURE; } } avplay_spin_unlock(&instance->spin, flags); return ret; } static td_s32 drv_set_resource(avplay_drv_instance *instance, td_handle handle, avplay_resource_type type, td_u32 param) { td_s32 ret = TD_SUCCESS; td_ulong flags = 0; switch (type) { case AVPLAY_RESOURCE_TYPE_VID_DEMUX: { ret = drv_set_dmx_callback(instance, handle, type, vid_dmx_callback); drv_set_dmx_index_callback(handle, instance, dmx_index_callback); break; } case AVPLAY_RESOURCE_TYPE_AUD_DEMUX: { ret = drv_set_dmx_callback(instance, handle, type, aud_dmx_callback); break; } case AVPLAY_RESOURCE_TYPE_PCR_DEMUX: { ret = drv_set_dmx_callback(instance, handle, type, pcr_dmx_callback); break; } case AVPLAY_RESOURCE_TYPE_VDEC: { ret = drv_set_vdec_callback(instance, handle, vdec_callback); break; } case AVPLAY_RESOURCE_TYPE_WINDOW: { ret = set_win_resource(instance, handle, param); break; } case AVPLAY_RESOURCE_TYPE_SYNC: { avplay_spin_lock(&instance->spin, flags); if (instance->ctx != TD_NULL) { instance->ctx->sync = handle; } avplay_spin_unlock(&instance->spin, flags); break; } default: { ext_err_avplay("Unsupport resource type[%d]\n", type); ret = SOC_ERR_AVPLAY_NOT_SUPPORT; break; } } return_when_error(ret, set_resource); return TD_SUCCESS; } static td_s32 drv_get_avplay_handle_by_window(td_handle win, td_handle *avplay) { td_u32 i = 0; td_ulong flags = 0; *avplay = TD_INVALID_HANDLE; avplay_spin_lock(&g_avplay_spinlock, flags); for (; i < AVPLAY_MAX_CNT; i++) { if (g_avplay_instance[i].ctx != TD_NULL && g_avplay_instance[i].ctx->win == win) { *avplay = g_avplay_instance[i].ctx->self; break; } } avplay_spin_unlock(&g_avplay_spinlock, flags); return (*avplay == TD_INVALID_HANDLE) ? SOC_ERR_AVPLAY_NO_DATA : TD_SUCCESS; } static td_s32 drv_get_resource(const avplay_drv_ctx *ctx, avplay_resource_type type, td_u32 param, td_handle *dst) { td_s32 ret = TD_SUCCESS; *dst = TD_INVALID_HANDLE; switch (type) { case AVPLAY_RESOURCE_TYPE_VID_DEMUX: { *dst = ctx->vid_dmx; break; } case AVPLAY_RESOURCE_TYPE_AUD_DEMUX: { *dst = ctx->aud_dmx; break; } case AVPLAY_RESOURCE_TYPE_PCR_DEMUX: { *dst = ctx->pcr_dmx; break; } case AVPLAY_RESOURCE_TYPE_VDEC: { *dst = ctx->vdec; break; } case AVPLAY_RESOURCE_TYPE_WINDOW: { *dst = ctx->win; break; } case AVPLAY_RESOURCE_TYPE_SYNC: { *dst = ctx->sync; break; } default: { ext_err_avplay("Unsupport resource type[%d]\n", type); ret = SOC_ERR_AVPLAY_NOT_SUPPORT; break; } } return_when_error(ret, get_resource); return TD_SUCCESS; } static td_s32 drv_get_global_play_info(const avplay_drv_ctx *ctx, avplay_global_play_info *info) { info->audio_enable = ctx->aud_enable; info->video_enable = ctx->vid_enable; return TD_SUCCESS; } static td_s32 drv_wakeup(avplay_drv_instance *instance) { instance->wait_cond = TD_TRUE; osal_wait_wakeup(&instance->wq); return TD_SUCCESS; } static int drv_wait_cond(const void *param) { if (*(const td_bool*)param) { return 1; } return 0; } static td_s32 drv_wait_event(avplay_drv_instance *instance, td_u64 event_mask, avplay_event_type *event, td_u64 *param, td_s64 timeout) { td_s32 ret = TD_FAILURE; td_ulong flags = 0; avplay_spin_lock(&instance->spin, flags); if (instance->ctx != TD_NULL) { instance->ctx->event_mask = event_mask; } avplay_spin_unlock(&instance->spin, flags); *event = AVPLAY_EVENT_TYPE_MAX; if (!drv_pop_event(instance, event, param)) { avplay_mutex_unlock(&instance->mutex); ret = osal_wait_timeout_interruptible(&instance->wq, drv_wait_cond, &instance->wait_cond, (td_ulong)timeout); avplay_mutex_lock(&instance->mutex); (void)drv_pop_event(instance, event, param); instance->wait_cond = TD_FALSE; } if (*event != AVPLAY_EVENT_TYPE_MAX) { ret = TD_SUCCESS; } else if (ret == 0) { ret = SOC_ERR_AVPLAY_TIMEOUT; } return ret; } static td_s32 drv_get_video_info(avplay_drv_instance *instance, avplay_video_info *info) { td_s32 ret; ext_vdec_feature feature = {0}; fn_drv_vdec_get_param vdec_get_param = TD_NULL; td_handle vdec; td_ulong flags = 0; if (instance == TD_NULL || instance->ctx == TD_NULL) { return TD_FAILURE; } avplay_spin_lock(&instance->spin, flags); vdec = instance->ctx->vdec; avplay_spin_unlock(&instance->spin, flags); if (g_avplay_import_func.vdec_func != TD_NULL && g_avplay_import_func.vdec_func->pfn_drv_vdec_get_param != TD_NULL) { vdec_get_param = g_avplay_import_func.vdec_func->pfn_drv_vdec_get_param; } if (vdec_get_param == TD_NULL || vdec == TD_INVALID_HANDLE) { return TD_SUCCESS; /* return success if vdec not exist */ } ret = vdec_get_param(vdec, EXT_VDEC_PARAM_FEATURE, &feature, sizeof(ext_vdec_feature)); if (ret != TD_SUCCESS) { return TD_FAILURE; } info->width = feature.dec_width; info->height = feature.dec_height; info->aspect_width = feature.aspect_width; info->aspect_height = feature.aspect_height; info->fps = feature.frame_rate; info->sampling_type = feature.sampling_type; return TD_SUCCESS; } static td_s32 drv_get_event_param(const avplay_drv_instance *instance, avplay_event_param *param) { const avplay_drv_ctx *ctx = instance->ctx; param->res_param.width = ctx->res.width; param->res_param.height = ctx->res.height; param->res_param.stride = ctx->res.stride; param->res_param.bit_depth = ctx->res.bit_depth; param->res_param.frm_num = ctx->res.frm_num; param->res_param.frm_size = ctx->res.frm_size; param->res_param.meta_size = ctx->res.meta_size; return TD_SUCCESS; } static td_s32 drv_avplay_import_function(void) { td_s32 ret; td_ulong flags = 0; demux_func_export *dmx_func = TD_NULL; drv_vdec_export_func *vdec_func = TD_NULL; ret = ext_drv_module_get_func(SOC_ID_DEMUX, (void **)&dmx_func); log_when_error(ret, ext_drv_module_get_func); ret = ext_drv_module_get_func(SOC_ID_VDEC, (void **)&vdec_func); log_when_error(ret, ext_drv_module_get_func); avplay_spin_lock(&g_avplay_spinlock, flags); g_avplay_import_func.dmx_func = dmx_func; g_avplay_import_func.vdec_func = vdec_func; avplay_spin_unlock(&g_avplay_spinlock, flags); return TD_SUCCESS; } static td_s32 drv_avplay_set_channel_state(td_handle avplay, avplay_channel_type chn, avplay_channel_state state) { avplay_drv_do_func(avplay, drv_set_channel_state(AVPLAY_DRV_CTX, chn, state)); } static td_s32 drv_avplay_get_resource(td_handle avplay, avplay_resource_type type, td_u32 param, td_handle *dst) { avplay_drv_do_func(avplay, drv_get_resource(AVPLAY_DRV_CTX, type, param, dst)); } static td_s32 drv_avplay_get_global_play_info(td_handle avplay, avplay_global_play_info *info) { avplay_drv_do_func(avplay, drv_get_global_play_info(AVPLAY_DRV_CTX, info)); } td_s32 ext_drv_avplay_create(td_handle *avplay, const void *unique) { check_null(avplay); return drv_instance_alloc(avplay, unique); } td_s32 ext_drv_avplay_destroy(td_handle avplay) { return drv_instance_del(avplay); } td_s32 ext_drv_avplay_reset(td_handle avplay, avplay_channel_type chn) { avplay_drv_do_func(avplay, drv_reset(AVPLAY_DRV_CTX, chn)); } td_s32 ext_drv_avplay_set_channel_state(td_handle avplay, avplay_channel_type chn, avplay_channel_state state) { td_ulong flags = 0; avplay_spin_lock(&g_avplay_spinlock, flags); if (state == AVPLAY_CHANNEL_START) { g_avplay_play_cnt++; } avplay_spin_unlock(&g_avplay_spinlock, flags); return drv_avplay_set_channel_state(avplay, chn, state); } td_s32 ext_drv_avplay_set_resource(td_handle avplay, td_handle handle, avplay_resource_type type, td_u32 param) { avplay_drv_do_func_without_spin(avplay, drv_set_resource(AVPLAY_INSTANCE, handle, type, param)); } td_s32 ext_drv_avplay_get_resource(td_handle src, avplay_resource_type type, td_u32 param, td_handle *dst) { check_null(dst); if (type == AVPLAY_RESOURCE_TYPE_AVPLAY) { return drv_get_avplay_handle_by_window(src, dst); } else { return drv_avplay_get_resource(src, type, param, dst); } } td_s32 ext_drv_avplay_wakeup(td_handle avplay) { avplay_drv_do_func_without_spin(avplay, drv_wakeup(AVPLAY_INSTANCE)); } td_s32 ext_drv_avplay_wait_event(td_handle avplay, td_u64 event_mask, avplay_event_type *event, td_u64 *param, td_s64 timeout) { check_null(event); check_null(param); avplay_drv_do_func_without_spin(avplay, drv_wait_event(AVPLAY_INSTANCE, event_mask, event, param, timeout)); } td_s32 ext_drv_avplay_get_video_info(td_handle avplay, avplay_video_info *info) { check_null(info); avplay_drv_do_func_without_spin(avplay, drv_get_video_info(AVPLAY_INSTANCE, info)); } td_s32 ext_drv_avplay_get_global_play_info(td_handle avplay, avplay_global_play_info *info) { check_null(info); info->content_count = g_avplay_play_cnt; return drv_avplay_get_global_play_info(avplay, info); } td_s32 ext_drv_avplay_get_event_param(td_handle avplay, avplay_event_param *param) { check_null(param); avplay_drv_do_func(avplay, drv_get_event_param(AVPLAY_INSTANCE, param)); } td_s32 avplay_init(void) { avplay_spin_init(&g_avplay_spinlock); drv_instance_init(); return TD_SUCCESS; } td_s32 avplay_deinit(void) { drv_instance_deinit(); avplay_spin_deinit(&g_avplay_spinlock); return TD_SUCCESS; } td_s32 avplay_open(void *private_data) { return drv_avplay_import_function(); } td_s32 avplay_release(void *private_data) { return drv_instance_clear(private_data); }