bohu | 9c42d7b | 2022-05-08 21:53:32 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2022 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "GoldfishHevcHelper.h" |
| 18 | |
| 19 | #define LOG_TAG "GoldfishHevcHelper" |
| 20 | #include <log/log.h> |
| 21 | |
| 22 | #include "ihevc_typedefs.h" |
| 23 | #include "ihevcd_cxa.h" |
| 24 | |
| 25 | #define DEBUG 0 |
| 26 | #if DEBUG |
| 27 | #define DDD(...) ALOGD(__VA_ARGS__) |
| 28 | #else |
| 29 | #define DDD(...) ((void)0) |
| 30 | #endif |
| 31 | |
| 32 | |
| 33 | #include <Codec2Mapper.h> |
| 34 | |
| 35 | #define ivdec_api_function ihevcd_cxa_api_function |
| 36 | #define ivdext_create_ip_t ihevcd_cxa_create_ip_t |
| 37 | #define ivdext_create_op_t ihevcd_cxa_create_op_t |
| 38 | #define ivdext_delete_ip_t ihevcd_cxa_delete_ip_t |
| 39 | #define ivdext_delete_op_t ihevcd_cxa_delete_op_t |
| 40 | #define ivdext_ctl_set_num_cores_ip_t ihevcd_cxa_ctl_set_num_cores_ip_t |
| 41 | #define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t |
| 42 | #define ivdext_ctl_get_vui_params_ip_t ihevcd_cxa_ctl_get_vui_params_ip_t |
| 43 | #define ivdext_ctl_get_vui_params_op_t ihevcd_cxa_ctl_get_vui_params_op_t |
| 44 | #define ALIGN128(x) ((((x) + 127) >> 7) << 7) |
| 45 | #define MAX_NUM_CORES 4 |
| 46 | #define IVDEXT_CMD_CTL_SET_NUM_CORES \ |
| 47 | (IVD_CONTROL_API_COMMAND_TYPE_T) IHEVCD_CXA_CMD_CTL_SET_NUM_CORES |
| 48 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
| 49 | |
| 50 | namespace android { |
| 51 | |
| 52 | static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) { |
| 53 | (void) ctxt; |
| 54 | return memalign(alignment, size); |
| 55 | } |
| 56 | |
| 57 | static void ivd_aligned_free(void *ctxt, void *mem) { |
| 58 | (void) ctxt; |
| 59 | free(mem); |
| 60 | } |
| 61 | |
| 62 | |
| 63 | GoldfishHevcHelper::GoldfishHevcHelper(int w, int h):mWidth(w),mHeight(h) { createDecoder(); } |
| 64 | |
| 65 | GoldfishHevcHelper::~GoldfishHevcHelper() { |
| 66 | destroyDecoder(); |
bohu | 9c42d7b | 2022-05-08 21:53:32 -0700 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | void GoldfishHevcHelper::createDecoder() { |
| 70 | ivdext_create_ip_t s_create_ip = {}; |
| 71 | ivdext_create_op_t s_create_op = {}; |
| 72 | |
| 73 | s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t); |
| 74 | s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE; |
| 75 | s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0; |
| 76 | s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat; |
| 77 | s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc; |
| 78 | s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free; |
| 79 | s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr; |
| 80 | s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t); |
| 81 | IV_API_CALL_STATUS_T status = |
| 82 | ivdec_api_function(mDecHandle, &s_create_ip, &s_create_op); |
| 83 | if (status != IV_SUCCESS) { |
| 84 | ALOGE("error in %s: 0x%x", __func__, |
| 85 | s_create_op.s_ivd_create_op_t.u4_error_code); |
| 86 | return; |
| 87 | } |
| 88 | mDecHandle = (iv_obj_t *)s_create_op.s_ivd_create_op_t.pv_handle; |
| 89 | mDecHandle->pv_fxns = (void *)ivdec_api_function; |
| 90 | mDecHandle->u4_size = sizeof(iv_obj_t); |
| 91 | |
| 92 | mStride = ALIGN128(mWidth); |
| 93 | |
| 94 | setNumCores(); |
| 95 | } |
| 96 | |
| 97 | void GoldfishHevcHelper::destroyDecoder() { |
| 98 | if (mDecHandle) { |
| 99 | ivdext_delete_ip_t s_delete_ip = {}; |
| 100 | ivdext_delete_op_t s_delete_op = {}; |
| 101 | |
| 102 | s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t); |
| 103 | s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE; |
| 104 | s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t); |
| 105 | IV_API_CALL_STATUS_T status = |
| 106 | ivdec_api_function(mDecHandle, &s_delete_ip, &s_delete_op); |
| 107 | if (status != IV_SUCCESS) { |
| 108 | ALOGE("error in %s: 0x%x", __func__, |
| 109 | s_delete_op.s_ivd_delete_op_t.u4_error_code); |
| 110 | } |
| 111 | mDecHandle = nullptr; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | void GoldfishHevcHelper::setNumCores() { |
| 116 | ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {}; |
| 117 | ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {}; |
| 118 | |
| 119 | s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t); |
| 120 | s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL; |
| 121 | s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES; |
| 122 | s_set_num_cores_ip.u4_num_cores = mNumCores; |
| 123 | s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t); |
| 124 | IV_API_CALL_STATUS_T status = ivdec_api_function( |
| 125 | mDecHandle, &s_set_num_cores_ip, &s_set_num_cores_op); |
| 126 | if (IV_SUCCESS != status) { |
| 127 | DDD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | void GoldfishHevcHelper::resetDecoder() { |
| 132 | ivd_ctl_reset_ip_t s_reset_ip = {}; |
| 133 | ivd_ctl_reset_op_t s_reset_op = {}; |
| 134 | |
| 135 | s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t); |
| 136 | s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL; |
| 137 | s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET; |
| 138 | s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t); |
| 139 | IV_API_CALL_STATUS_T status = |
| 140 | ivdec_api_function(mDecHandle, &s_reset_ip, &s_reset_op); |
| 141 | if (IV_SUCCESS != status) { |
| 142 | ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code); |
| 143 | } |
| 144 | setNumCores(); |
| 145 | } |
| 146 | |
| 147 | void GoldfishHevcHelper::setParams(size_t stride, |
| 148 | IVD_VIDEO_DECODE_MODE_T dec_mode) { |
| 149 | ihevcd_cxa_ctl_set_config_ip_t s_hevcd_set_dyn_params_ip = {}; |
| 150 | ihevcd_cxa_ctl_set_config_op_t s_hevcd_set_dyn_params_op = {}; |
| 151 | ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip = |
| 152 | &s_hevcd_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t; |
| 153 | ivd_ctl_set_config_op_t *ps_set_dyn_params_op = |
| 154 | &s_hevcd_set_dyn_params_op.s_ivd_ctl_set_config_op_t; |
| 155 | |
| 156 | ps_set_dyn_params_ip->u4_size = sizeof(ihevcd_cxa_ctl_set_config_ip_t); |
| 157 | ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL; |
| 158 | ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS; |
| 159 | ps_set_dyn_params_ip->u4_disp_wd = (UWORD32)stride; |
| 160 | ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE; |
| 161 | ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT; |
| 162 | ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode; |
| 163 | ps_set_dyn_params_op->u4_size = sizeof(ihevcd_cxa_ctl_set_config_op_t); |
| 164 | IV_API_CALL_STATUS_T status = ivdec_api_function( |
| 165 | mDecHandle, ps_set_dyn_params_ip, ps_set_dyn_params_op); |
| 166 | if (status != IV_SUCCESS) { |
| 167 | ALOGE("error in %s: 0x%x", __func__, |
| 168 | ps_set_dyn_params_op->u4_error_code); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | bool GoldfishHevcHelper::isVpsFrame(const uint8_t* frame, int inSize) { |
| 173 | if (inSize < 5) return false; |
| 174 | if (frame[0] == 0 && frame[1] == 0 && frame[2] == 0 && frame[3] == 1) { |
| 175 | const bool forbiddenBitIsInvalid = 0x80 & frame[4]; |
| 176 | if (forbiddenBitIsInvalid) { |
| 177 | return false; |
| 178 | } |
| 179 | // nalu type is the lower 6 bits after shiftting to right 1 bit |
| 180 | uint8_t naluType = 0x3f & (frame[4] >> 1); |
| 181 | if (naluType == 32 |
| 182 | || naluType == 33 |
| 183 | || naluType == 34 |
| 184 | ) return true; |
| 185 | else return false; |
| 186 | } else { |
| 187 | return false; |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | bool GoldfishHevcHelper::decodeHeader(const uint8_t *frame, int inSize) { |
| 192 | // should we check the header for vps/sps/pps frame ? otherwise |
| 193 | // there is no point calling decoder |
| 194 | if (!isVpsFrame(frame, inSize)) { |
| 195 | DDD("could not find valid vps frame"); |
| 196 | return false; |
| 197 | } else { |
| 198 | DDD("found valid vps frame"); |
| 199 | } |
| 200 | |
| 201 | ihevcd_cxa_video_decode_ip_t s_hevcd_decode_ip = {}; |
| 202 | ihevcd_cxa_video_decode_op_t s_hevcd_decode_op = {}; |
| 203 | ivd_video_decode_ip_t *ps_decode_ip = |
| 204 | &s_hevcd_decode_ip.s_ivd_video_decode_ip_t; |
| 205 | ivd_video_decode_op_t *ps_decode_op = |
| 206 | &s_hevcd_decode_op.s_ivd_video_decode_op_t; |
| 207 | |
| 208 | // setup input/output arguments to decoder |
| 209 | setDecodeArgs(ps_decode_ip, ps_decode_op, frame, mStride, |
| 210 | 0, // offset |
| 211 | inSize, // size |
| 212 | 0 // time-stamp, does not matter |
| 213 | ); |
| 214 | |
| 215 | setParams(mStride, IVD_DECODE_HEADER); |
| 216 | |
| 217 | // now kick off the decoding |
| 218 | IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op); |
| 219 | if (status != IV_SUCCESS) { |
| 220 | ALOGE("failed to call decoder function for header\n"); |
| 221 | ALOGE("error in %s: 0x%x", __func__, |
| 222 | ps_decode_op->u4_error_code); |
| 223 | } |
| 224 | |
| 225 | if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) { |
| 226 | DDD("resolution changed, reset decoder"); |
| 227 | resetDecoder(); |
| 228 | setParams(mStride, IVD_DECODE_HEADER); |
| 229 | ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op); |
| 230 | } |
| 231 | |
| 232 | // get the w/h and update |
| 233 | if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) { |
| 234 | DDD("success decode w/h %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht); |
| 235 | DDD("existing w/h %d %d", mWidth, mHeight); |
| 236 | if (ps_decode_op->u4_pic_wd != mWidth || ps_decode_op->u4_pic_ht != mHeight) { |
| 237 | mWidth = ps_decode_op->u4_pic_wd; |
| 238 | mHeight = ps_decode_op->u4_pic_ht; |
| 239 | return true; |
| 240 | } else { |
| 241 | DDD("success decode w/h, but they are the same %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht); |
| 242 | } |
| 243 | } else { |
| 244 | ALOGE("could not decode w/h"); |
| 245 | } |
| 246 | |
| 247 | // get output delay |
| 248 | if (ps_decode_op->i4_reorder_depth >= 0) { |
| 249 | if (mOutputDelay != ps_decode_op->i4_reorder_depth) { |
| 250 | mOutputDelay = ps_decode_op->i4_reorder_depth; |
| 251 | DDD("New Output delay %d ", mOutputDelay); |
| 252 | } else { |
| 253 | DDD("same Output delay %d ", mOutputDelay); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | return false; |
| 258 | } |
| 259 | |
| 260 | bool GoldfishHevcHelper::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip, |
| 261 | ivd_video_decode_op_t *ps_decode_op, |
| 262 | const uint8_t *inBuffer, |
| 263 | uint32_t displayStride, size_t inOffset, |
| 264 | size_t inSize, uint32_t tsMarker) { |
| 265 | uint32_t displayHeight = mHeight; |
| 266 | size_t lumaSize = displayStride * displayHeight; |
| 267 | size_t chromaSize = lumaSize >> 2; |
| 268 | |
| 269 | if (mStride != displayStride) { |
| 270 | mStride = displayStride; |
| 271 | } |
| 272 | |
| 273 | // force decoder to always decode header and get dimensions, |
| 274 | // hope this will be quick and cheap |
| 275 | setParams(mStride, IVD_DECODE_HEADER); |
| 276 | |
| 277 | ps_decode_ip->u4_size = sizeof(ihevcd_cxa_video_decode_ip_t); |
| 278 | ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE; |
| 279 | if (inBuffer) { |
| 280 | ps_decode_ip->u4_ts = tsMarker; |
| 281 | ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer) + inOffset; |
| 282 | ps_decode_ip->u4_num_Bytes = inSize; |
| 283 | } else { |
| 284 | ps_decode_ip->u4_ts = 0; |
| 285 | ps_decode_ip->pv_stream_buffer = nullptr; |
| 286 | ps_decode_ip->u4_num_Bytes = 0; |
| 287 | } |
| 288 | DDD("setting pv_stream_buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", |
| 289 | ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[0], |
| 290 | ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[1], |
| 291 | ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[2], |
| 292 | ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[3], |
| 293 | ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[4], |
| 294 | ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[5], |
| 295 | ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[6], |
| 296 | ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[7] |
| 297 | ); |
| 298 | DDD("input bytes %d", ps_decode_ip->u4_num_Bytes); |
| 299 | |
| 300 | ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize; |
| 301 | ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize; |
| 302 | ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize; |
bohu | 9c42d7b | 2022-05-08 21:53:32 -0700 | [diff] [blame] | 303 | { |
bohu | fe82255 | 2022-05-13 13:59:26 -0700 | [diff] [blame] | 304 | ps_decode_ip->s_out_buffer.pu1_bufs[0] = nullptr; |
| 305 | ps_decode_ip->s_out_buffer.pu1_bufs[1] = nullptr; |
| 306 | ps_decode_ip->s_out_buffer.pu1_bufs[2] = nullptr; |
bohu | 9c42d7b | 2022-05-08 21:53:32 -0700 | [diff] [blame] | 307 | } |
| 308 | ps_decode_ip->s_out_buffer.u4_num_bufs = 3; |
| 309 | ps_decode_op->u4_size = sizeof(ihevcd_cxa_video_decode_op_t); |
| 310 | ps_decode_op->u4_output_present = 0; |
| 311 | |
| 312 | return true; |
| 313 | } |
| 314 | |
| 315 | } // namespace android |