/* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "GoldfishHevcHelper.h" #define LOG_TAG "GoldfishHevcHelper" #include #include "ihevc_typedefs.h" #include "ihevcd_cxa.h" #define DEBUG 0 #if DEBUG #define DDD(...) ALOGD(__VA_ARGS__) #else #define DDD(...) ((void)0) #endif #include #define ivdec_api_function ihevcd_cxa_api_function #define ivdext_create_ip_t ihevcd_cxa_create_ip_t #define ivdext_create_op_t ihevcd_cxa_create_op_t #define ivdext_delete_ip_t ihevcd_cxa_delete_ip_t #define ivdext_delete_op_t ihevcd_cxa_delete_op_t #define ivdext_ctl_set_num_cores_ip_t ihevcd_cxa_ctl_set_num_cores_ip_t #define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t #define ivdext_ctl_get_vui_params_ip_t ihevcd_cxa_ctl_get_vui_params_ip_t #define ivdext_ctl_get_vui_params_op_t ihevcd_cxa_ctl_get_vui_params_op_t #define ALIGN128(x) ((((x) + 127) >> 7) << 7) #define MAX_NUM_CORES 4 #define IVDEXT_CMD_CTL_SET_NUM_CORES \ (IVD_CONTROL_API_COMMAND_TYPE_T) IHEVCD_CXA_CMD_CTL_SET_NUM_CORES #define MIN(a, b) (((a) < (b)) ? (a) : (b)) namespace android { static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) { (void) ctxt; return memalign(alignment, size); } static void ivd_aligned_free(void *ctxt, void *mem) { (void) ctxt; free(mem); } GoldfishHevcHelper::GoldfishHevcHelper(int w, int h):mWidth(w),mHeight(h) { createDecoder(); } GoldfishHevcHelper::~GoldfishHevcHelper() { destroyDecoder(); } void GoldfishHevcHelper::createDecoder() { ivdext_create_ip_t s_create_ip = {}; ivdext_create_op_t s_create_op = {}; s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t); s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE; s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0; s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat; s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc; s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free; s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr; s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t); IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, &s_create_ip, &s_create_op); if (status != IV_SUCCESS) { ALOGE("error in %s: 0x%x", __func__, s_create_op.s_ivd_create_op_t.u4_error_code); return; } mDecHandle = (iv_obj_t *)s_create_op.s_ivd_create_op_t.pv_handle; mDecHandle->pv_fxns = (void *)ivdec_api_function; mDecHandle->u4_size = sizeof(iv_obj_t); mStride = ALIGN128(mWidth); setNumCores(); } void GoldfishHevcHelper::destroyDecoder() { if (mDecHandle) { ivdext_delete_ip_t s_delete_ip = {}; ivdext_delete_op_t s_delete_op = {}; s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t); s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE; s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t); IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, &s_delete_ip, &s_delete_op); if (status != IV_SUCCESS) { ALOGE("error in %s: 0x%x", __func__, s_delete_op.s_ivd_delete_op_t.u4_error_code); } mDecHandle = nullptr; } } void GoldfishHevcHelper::setNumCores() { ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {}; ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {}; s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t); s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL; s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES; s_set_num_cores_ip.u4_num_cores = mNumCores; s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t); IV_API_CALL_STATUS_T status = ivdec_api_function( mDecHandle, &s_set_num_cores_ip, &s_set_num_cores_op); if (IV_SUCCESS != status) { DDD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code); } } void GoldfishHevcHelper::resetDecoder() { ivd_ctl_reset_ip_t s_reset_ip = {}; ivd_ctl_reset_op_t s_reset_op = {}; s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t); s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL; s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET; s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t); IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, &s_reset_ip, &s_reset_op); if (IV_SUCCESS != status) { ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code); } setNumCores(); } void GoldfishHevcHelper::setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode) { ihevcd_cxa_ctl_set_config_ip_t s_hevcd_set_dyn_params_ip = {}; ihevcd_cxa_ctl_set_config_op_t s_hevcd_set_dyn_params_op = {}; ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip = &s_hevcd_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t; ivd_ctl_set_config_op_t *ps_set_dyn_params_op = &s_hevcd_set_dyn_params_op.s_ivd_ctl_set_config_op_t; ps_set_dyn_params_ip->u4_size = sizeof(ihevcd_cxa_ctl_set_config_ip_t); ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL; ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS; ps_set_dyn_params_ip->u4_disp_wd = (UWORD32)stride; ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE; ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT; ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode; ps_set_dyn_params_op->u4_size = sizeof(ihevcd_cxa_ctl_set_config_op_t); IV_API_CALL_STATUS_T status = ivdec_api_function( mDecHandle, ps_set_dyn_params_ip, ps_set_dyn_params_op); if (status != IV_SUCCESS) { ALOGE("error in %s: 0x%x", __func__, ps_set_dyn_params_op->u4_error_code); } } bool GoldfishHevcHelper::isVpsFrame(const uint8_t* frame, int inSize) { if (inSize < 5) return false; if (frame[0] == 0 && frame[1] == 0 && frame[2] == 0 && frame[3] == 1) { const bool forbiddenBitIsInvalid = 0x80 & frame[4]; if (forbiddenBitIsInvalid) { return false; } // nalu type is the lower 6 bits after shiftting to right 1 bit uint8_t naluType = 0x3f & (frame[4] >> 1); if (naluType == 32 || naluType == 33 || naluType == 34 ) return true; else return false; } else { return false; } } bool GoldfishHevcHelper::decodeHeader(const uint8_t *frame, int inSize, bool &helperstatus) { helperstatus = true; // should we check the header for vps/sps/pps frame ? otherwise // there is no point calling decoder if (!isVpsFrame(frame, inSize)) { DDD("could not find valid vps frame"); return false; } else { DDD("found valid vps frame"); } ihevcd_cxa_video_decode_ip_t s_hevcd_decode_ip = {}; ihevcd_cxa_video_decode_op_t s_hevcd_decode_op = {}; ivd_video_decode_ip_t *ps_decode_ip = &s_hevcd_decode_ip.s_ivd_video_decode_ip_t; ivd_video_decode_op_t *ps_decode_op = &s_hevcd_decode_op.s_ivd_video_decode_op_t; // setup input/output arguments to decoder setDecodeArgs(ps_decode_ip, ps_decode_op, frame, mStride, 0, // offset inSize, // size 0 // time-stamp, does not matter ); setParams(mStride, IVD_DECODE_HEADER); // now kick off the decoding IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op); if (status != IV_SUCCESS) { ALOGE("failed to call decoder function for header\n"); ALOGE("error in %s: 0x%x", __func__, ps_decode_op->u4_error_code); helperstatus = false; return false; } if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) { DDD("resolution changed, reset decoder"); resetDecoder(); setParams(mStride, IVD_DECODE_HEADER); ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op); } // get the w/h and update if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) { DDD("success decode w/h %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht); DDD("existing w/h %d %d", mWidth, mHeight); if (ps_decode_op->u4_pic_wd != mWidth || ps_decode_op->u4_pic_ht != mHeight) { mWidth = ps_decode_op->u4_pic_wd; mHeight = ps_decode_op->u4_pic_ht; return true; } else { DDD("success decode w/h, but they are the same %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht); } } else { ALOGE("could not decode w/h"); } // get output delay if (ps_decode_op->i4_reorder_depth >= 0) { if (mOutputDelay != ps_decode_op->i4_reorder_depth) { mOutputDelay = ps_decode_op->i4_reorder_depth; DDD("New Output delay %d ", mOutputDelay); } else { DDD("same Output delay %d ", mOutputDelay); } } return false; } bool GoldfishHevcHelper::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip, ivd_video_decode_op_t *ps_decode_op, const uint8_t *inBuffer, uint32_t displayStride, size_t inOffset, size_t inSize, uint32_t tsMarker) { uint32_t displayHeight = mHeight; size_t lumaSize = displayStride * displayHeight; size_t chromaSize = lumaSize >> 2; if (mStride != displayStride) { mStride = displayStride; } // force decoder to always decode header and get dimensions, // hope this will be quick and cheap setParams(mStride, IVD_DECODE_HEADER); ps_decode_ip->u4_size = sizeof(ihevcd_cxa_video_decode_ip_t); ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE; if (inBuffer) { ps_decode_ip->u4_ts = tsMarker; ps_decode_ip->pv_stream_buffer = const_cast(inBuffer) + inOffset; ps_decode_ip->u4_num_Bytes = inSize; } else { ps_decode_ip->u4_ts = 0; ps_decode_ip->pv_stream_buffer = nullptr; ps_decode_ip->u4_num_Bytes = 0; } DDD("setting pv_stream_buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[0], ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[1], ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[2], ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[3], ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[4], ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[5], ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[6], ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[7] ); DDD("input bytes %d", ps_decode_ip->u4_num_Bytes); ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize; ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize; ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize; { ps_decode_ip->s_out_buffer.pu1_bufs[0] = nullptr; ps_decode_ip->s_out_buffer.pu1_bufs[1] = nullptr; ps_decode_ip->s_out_buffer.pu1_bufs[2] = nullptr; } ps_decode_ip->s_out_buffer.u4_num_bufs = 3; ps_decode_op->u4_size = sizeof(ihevcd_cxa_video_decode_op_t); ps_decode_op->u4_output_present = 0; return true; } } // namespace android