diff options
5 files changed, 443 insertions, 41 deletions
diff --git a/system/codecs/c2/decoders/hevcdec/Android.bp b/system/codecs/c2/decoders/hevcdec/Android.bp index fddd276d..f1b9c82d 100644 --- a/system/codecs/c2/decoders/hevcdec/Android.bp +++ b/system/codecs/c2/decoders/hevcdec/Android.bp @@ -15,6 +15,7 @@ cc_library_shared { ], srcs: ["C2GoldfishHevcDec.cpp", + "GoldfishHevcHelper.cpp", "MediaHevcDecoder.cpp", ], @@ -28,5 +29,8 @@ cc_library_shared { "libgralloc_cb.ranchu", ], + static_libs: ["libhevcdec", + ], + } diff --git a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp index 51934d5d..b2f684a6 100644 --- a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp +++ b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp @@ -457,6 +457,7 @@ status_t C2GoldfishHevcDec::createDecoder() { } mContext->initHevcContext(mWidth, mHeight, mWidth, mHeight, MediaHevcDecoder::PixelFormat::YUV420P); + return OK; } @@ -884,11 +885,6 @@ void C2GoldfishHevcDec::process(const std::unique_ptr<C2Work> &work, return; } - if (false == mHeaderDecoded) { - /* Decode header and get dimensions */ - setParams(mStride); - } - DDD("flag is %x", work->input.flags); if (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) { hasPicture = false; @@ -903,9 +899,50 @@ void C2GoldfishHevcDec::process(const std::unique_ptr<C2Work> &work, removePts(mPts); } + bool whChanged = false; + if (GoldfishHevcHelper::isVpsFrame(mInPBuffer, mInPBufferSize)) { + mHevcHelper.reset(new GoldfishHevcHelper(mWidth, mHeight)); + whChanged = mHevcHelper->decodeHeader(mInPBuffer, mInPBufferSize); + if (whChanged) { + DDD("w changed from old %d to new %d\n", mWidth, mHevcHelper->getWidth()); + DDD("h changed from old %d to new %d\n", mHeight, mHevcHelper->getHeight()); + if (1) { + drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work); + resetDecoder(); + resetPlugin(); + work->workletsProcessed = 0u; + } + { + mWidth = mHevcHelper->getWidth(); + mHeight = mHevcHelper->getHeight(); + C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); + std::vector<std::unique_ptr<C2SettingResult>> failures; + c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); + if (err == OK) { + work->worklets.front()->output.configUpdate.push_back( + C2Param::Copy(size)); + ensureDecoderState(pool); + } else { + ALOGE("Cannot set width and height"); + mSignalledError = true; + work->workletsProcessed = 1u; + work->result = C2_CORRUPTED; + return; + } + } + if (!mContext) { + DDD("creating decoder context to host in process work"); + checkMode(pool); + createDecoder(); + } + continue;//return; + } // end of whChanged + } // end of isVpsFrame + uint32_t delay; GETTIME(&mTimeStart, nullptr); TIME_DIFF(mTimeEnd, mTimeStart, delay); + (void)delay; //(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op); DDD("decoding"); hevc_result_t hevcRes = @@ -922,44 +959,9 @@ void C2GoldfishHevcDec::process(const std::unique_ptr<C2Work> &work, uint32_t decodeTime; GETTIME(&mTimeEnd, nullptr); TIME_DIFF(mTimeStart, mTimeEnd, decodeTime); - } - // TODO: handle res change - if (0) { - DDD("resolution changed"); - drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work); - resetDecoder(); - resetPlugin(); - work->workletsProcessed = 0u; - - /* Decode header and get new dimensions */ - setParams(mStride); - // (void) ivdec_api_function(mDecHandle, &s_decode_ip, - // &s_decode_op); + (void)decodeTime; } if (mImg.data != nullptr) { - // check for new width and height - auto decodedW = mImg.width; - auto decodedH = mImg.height; - if (decodedW != mWidth || decodedH != mHeight) { - mWidth = decodedW; - mHeight = decodedH; - - C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); - std::vector<std::unique_ptr<C2SettingResult>> failures; - c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); - if (err == OK) { - work->worklets.front()->output.configUpdate.push_back( - C2Param::Copy(size)); - ensureDecoderState(pool); - } else { - ALOGE("Cannot set width and height"); - mSignalledError = true; - work->workletsProcessed = 1u; - work->result = C2_CORRUPTED; - return; - } - } - DDD("got data %" PRIu64 " with pts %" PRIu64, getWorkIndex(mImg.pts), mImg.pts); mHeaderDecoded = true; copyImageData(mImg); diff --git a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h index 575689a1..fe080cfa 100644 --- a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h +++ b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h @@ -22,6 +22,7 @@ #include <media/stagefright/foundation/ColorUtils.h> #include "MediaHevcDecoder.h" +#include "GoldfishHevcHelper.h" #include <SimpleC2Component.h> #include <atomic> #include <map> @@ -152,6 +153,8 @@ class C2GoldfishHevcDec : public SimpleC2Component { std::vector<uint8_t> mCsd1; void decodeHeaderAfterFlush(); + std::unique_ptr<GoldfishHevcHelper> mHevcHelper; + C2_DO_NOT_COPY(C2GoldfishHevcDec); }; diff --git a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp new file mode 100644 index 00000000..5bf2672d --- /dev/null +++ b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp @@ -0,0 +1,326 @@ +/* + * 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(); + if (mOutBufferFlush) { + ivd_aligned_free(nullptr, mOutBufferFlush); + mOutBufferFlush = nullptr; + } +} + +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; + if (!mOutBufferFlush) { + uint32_t displayStride = mStride; + uint32_t displayHeight = mHeight; + uint32_t bufferSize = displayStride * displayHeight * 3 / 2; + mOutBufferFlush = (uint8_t *)ivd_aligned_malloc(nullptr, 128, bufferSize); + } + { + ps_decode_ip->s_out_buffer.pu1_bufs[0] = mOutBufferFlush; + ps_decode_ip->s_out_buffer.pu1_bufs[1] = mOutBufferFlush + lumaSize; + ps_decode_ip->s_out_buffer.pu1_bufs[2] = + mOutBufferFlush + lumaSize + chromaSize; + } + 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 diff --git a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h new file mode 100644 index 00000000..22615433 --- /dev/null +++ b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#ifndef GOLDFISH_HEVC_HELPER_H_ +#define GOLDFISH_HEVC_HELPER_H_ + +#include <inttypes.h> +#include "ihevc_typedefs.h" +#include "ihevcd_cxa.h" + + +namespace android { + +// this class is just to provide some functions to decode header +// so that we know w/h of each sps +class GoldfishHevcHelper { + public: + GoldfishHevcHelper(int w, int h); + ~GoldfishHevcHelper(); + + // check whether the frame is vps; typical hevc will have + // a frame that is vps/sps/pps together + static bool isVpsFrame(const uint8_t* frame, int inSize); + public: + // return true if decoding finds out w/h changed; + // otherwise false + bool decodeHeader(const uint8_t *frame, int inSize); + int getWidth() const { return mWidth; } + int getHeight() const { return mHeight; } + + private: + void createDecoder(); + void destroyDecoder(); + void resetDecoder(); + void setNumCores(); + void setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode); + bool 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); + + private: + iv_obj_t *mDecHandle = nullptr; + int mWidth = 320; + int mHeight = 240; + int mNumCores = 1; + int mStride = 16; + int mOutputDelay = 8; // default + uint8_t* mOutBufferFlush = nullptr; + IV_COLOR_FORMAT_T mIvColorformat = IV_YUV_420P; +}; + +} // namespace android +#endif |