blob: 1b93a0d9ddcd463169c628faffc6e76b27ea6a48 [file] [log] [blame]
/*
* 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 <log/log.h>
#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 <Codec2Mapper.h>
#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) {
// 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);
}
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<uint8_t *>(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