| /* |
| * Copyright (C) 2018 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. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "C2SoftXaacDec" |
| #include <log/log.h> |
| |
| #include <inttypes.h> |
| |
| #include <cutils/properties.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/MediaDefs.h> |
| #include <media/stagefright/foundation/hexdump.h> |
| |
| #include <C2PlatformSupport.h> |
| #include <SimpleC2Interface.h> |
| |
| #include "C2SoftXaacDec.h" |
| |
| #define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */ |
| #define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */ |
| #define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */ |
| #define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 /* switch for heavy compression for mobile conf */ |
| #define DRC_DEFAULT_MOBILE_ENC_LEVEL (-1) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */ |
| |
| #define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level" |
| #define PROP_DRC_OVERRIDE_CUT "aac_drc_cut" |
| #define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost" |
| #define PROP_DRC_OVERRIDE_HEAVY "aac_drc_heavy" |
| #define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level" |
| |
| #define RETURN_IF_NE(returned, expected, retval, str) \ |
| if (returned != expected) { \ |
| ALOGE("Error in %s: Returned: %d Expected: %d", str, returned, \ |
| expected); \ |
| return retval; \ |
| } |
| |
| namespace android { |
| |
| constexpr char COMPONENT_NAME[] = "c2.android.xaac.decoder"; |
| |
| class C2SoftXaacDec::IntfImpl : public C2InterfaceHelper { |
| public: |
| explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper) |
| : C2InterfaceHelper(helper) { |
| |
| setDerivedInstance(this); |
| |
| addParameter( |
| DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) |
| .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) |
| .build()); |
| |
| addParameter( |
| DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) |
| .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) |
| .build()); |
| |
| addParameter( |
| DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) |
| .withConstValue(AllocSharedString<C2PortMimeConfig::input>( |
| MEDIA_MIMETYPE_AUDIO_AAC)) |
| .build()); |
| |
| addParameter( |
| DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) |
| .withConstValue(AllocSharedString<C2PortMimeConfig::output>( |
| MEDIA_MIMETYPE_AUDIO_RAW)) |
| .build()); |
| |
| addParameter( |
| DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) |
| .withDefault(new C2StreamSampleRateInfo::output(0u, 44100)) |
| .withFields({C2F(mSampleRate, value).oneOf({ |
| 7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 |
| })}) |
| .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps)) |
| .build()); |
| |
| addParameter( |
| DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) |
| .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) |
| .withFields({C2F(mChannelCount, value).inRange(1, 8)}) |
| .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps) |
| .build()); |
| |
| addParameter( |
| DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) |
| .withDefault(new C2BitrateTuning::input(0u, 64000)) |
| .withFields({C2F(mBitrate, value).inRange(8000, 960000)}) |
| .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps) |
| .build()); |
| |
| addParameter( |
| DefineParam(mAacFormat, C2_NAME_STREAM_AAC_FORMAT_SETTING) |
| .withDefault(new C2StreamAacFormatInfo::input(0u, C2AacStreamFormatRaw)) |
| .withFields({C2F(mAacFormat, value).oneOf({ |
| C2AacStreamFormatRaw, C2AacStreamFormatAdts |
| })}) |
| .withSetter(Setter<decltype(*mAacFormat)>::StrictValueWithNoDeps) |
| .build()); |
| } |
| |
| bool isAdts() const { return mAacFormat->value == C2AacStreamFormatAdts; } |
| uint32_t getBitrate() const { return mBitrate->value; } |
| |
| private: |
| std::shared_ptr<C2StreamFormatConfig::input> mInputFormat; |
| std::shared_ptr<C2StreamFormatConfig::output> mOutputFormat; |
| std::shared_ptr<C2PortMimeConfig::input> mInputMediaType; |
| std::shared_ptr<C2PortMimeConfig::output> mOutputMediaType; |
| std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate; |
| std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount; |
| std::shared_ptr<C2BitrateTuning::input> mBitrate; |
| |
| std::shared_ptr<C2StreamAacFormatInfo::input> mAacFormat; |
| }; |
| |
| C2SoftXaacDec::C2SoftXaacDec( |
| const char* name, |
| c2_node_id_t id, |
| const std::shared_ptr<IntfImpl> &intfImpl) |
| : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)), |
| mIntf(intfImpl), |
| mXheaacCodecHandle(nullptr), |
| mMemoryArray{nullptr}, |
| mOutputDrainBuffer(nullptr) { |
| } |
| |
| C2SoftXaacDec::~C2SoftXaacDec() { |
| onRelease(); |
| } |
| |
| c2_status_t C2SoftXaacDec::onInit() { |
| mInputBuffer = nullptr; |
| mOutputBuffer = nullptr; |
| mSampFreq = 0; |
| mNumChannels = 0; |
| mPcmWdSz = 0; |
| mChannelMask = 0; |
| mNumOutBytes = 0; |
| mCurFrameIndex = 0; |
| mCurTimestamp = 0; |
| mIsCodecInitialized = false; |
| mIsCodecConfigFlushRequired = false; |
| mSignalledOutputEos = false; |
| mSignalledError = false; |
| mOutputDrainBufferWritePos = 0; |
| |
| status_t err = initDecoder(); |
| return err == OK ? C2_OK : C2_CORRUPTED; |
| } |
| |
| c2_status_t C2SoftXaacDec::onStop() { |
| drainDecoder(); |
| // reset the "configured" state |
| mInputBuffer = nullptr; |
| mOutputBuffer = nullptr; |
| mSampFreq = 0; |
| mNumChannels = 0; |
| mPcmWdSz = 0; |
| mChannelMask = 0; |
| mNumOutBytes = 0; |
| mCurFrameIndex = 0; |
| mCurTimestamp = 0; |
| mIsCodecInitialized = false; |
| mIsCodecConfigFlushRequired = false; |
| for (int i = 0; i < MAX_MEM_ALLOCS; i++) mMemoryArray[i] = nullptr; |
| mSignalledOutputEos = false; |
| mSignalledError = false; |
| mOutputDrainBufferWritePos = 0; |
| |
| return C2_OK; |
| } |
| |
| void C2SoftXaacDec::onReset() { |
| (void)onStop(); |
| } |
| |
| void C2SoftXaacDec::onRelease() { |
| int errCode = deInitXAACDecoder(); |
| if (0 != errCode) ALOGE("deInitXAACDecoder() failed %d", errCode); |
| |
| if (mOutputDrainBuffer) { |
| delete[] mOutputDrainBuffer; |
| mOutputDrainBuffer = nullptr; |
| } |
| } |
| |
| status_t C2SoftXaacDec::initDecoder() { |
| ALOGV("initDecoder()"); |
| status_t status = UNKNOWN_ERROR; |
| IA_ERRORCODE err_code = IA_NO_ERROR; |
| int loop = 0; |
| |
| err_code = initXAACDecoder(); |
| if (err_code != IA_NO_ERROR) { |
| if (NULL == mXheaacCodecHandle) { |
| ALOGE("AAC decoder handle is null"); |
| } |
| |
| int temp_loop = 0; |
| for (loop = 0; loop < mMallocCount; loop++) { |
| if (mMemoryArray[loop]) { |
| free(mMemoryArray[loop]); |
| } else if (!temp_loop) { |
| temp_loop = loop; |
| } |
| } |
| if (temp_loop > 0) { |
| ALOGE(" memory allocation error %d\n", temp_loop); |
| } |
| ALOGE("initXAACDecoder Failed"); |
| |
| mMallocCount = 0; |
| return status; |
| } else { |
| status = OK; |
| } |
| |
| mOutputDrainBuffer = new short[kOutputDrainBufferSize]; |
| |
| initXAACDrc(); |
| |
| return status; |
| } |
| |
| static void fillEmptyWork(const std::unique_ptr<C2Work>& work) { |
| uint32_t flags = 0; |
| if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) { |
| flags |= C2FrameData::FLAG_END_OF_STREAM; |
| ALOGV("signalling eos"); |
| } |
| work->worklets.front()->output.flags = (C2FrameData::flags_t)flags; |
| work->worklets.front()->output.buffers.clear(); |
| work->worklets.front()->output.ordinal = work->input.ordinal; |
| work->workletsProcessed = 1u; |
| } |
| |
| void C2SoftXaacDec::finishWork(const std::unique_ptr<C2Work>& work, |
| const std::shared_ptr<C2BlockPool>& pool) { |
| ALOGV("mCurFrameIndex = %" PRIu64, mCurFrameIndex); |
| |
| std::shared_ptr<C2LinearBlock> block; |
| C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; |
| // TODO: error handling, proper usage, etc. |
| c2_status_t err = |
| pool->fetchLinearBlock(mOutputDrainBufferWritePos, usage, &block); |
| if (err != C2_OK) ALOGE("err = %d", err); |
| C2WriteView wView = block->map().get(); |
| int16_t* outBuffer = reinterpret_cast<int16_t*>(wView.data()); |
| memcpy(outBuffer, mOutputDrainBuffer, mOutputDrainBufferWritePos); |
| mOutputDrainBufferWritePos = 0; |
| |
| auto fillWork = [buffer = createLinearBuffer(block)]( |
| const std::unique_ptr<C2Work>& work) { |
| uint32_t flags = 0; |
| if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) { |
| flags |= C2FrameData::FLAG_END_OF_STREAM; |
| ALOGV("signalling eos"); |
| } |
| work->worklets.front()->output.flags = (C2FrameData::flags_t)flags; |
| work->worklets.front()->output.buffers.clear(); |
| work->worklets.front()->output.buffers.push_back(buffer); |
| work->worklets.front()->output.ordinal = work->input.ordinal; |
| work->workletsProcessed = 1u; |
| }; |
| if (work && work->input.ordinal.frameIndex == c2_cntr64_t(mCurFrameIndex)) { |
| fillWork(work); |
| } else { |
| finish(mCurFrameIndex, fillWork); |
| } |
| |
| ALOGV("out timestamp %" PRIu64 " / %u", mCurTimestamp, block->capacity()); |
| } |
| |
| void C2SoftXaacDec::process(const std::unique_ptr<C2Work>& work, |
| const std::shared_ptr<C2BlockPool>& pool) { |
| work->workletsProcessed = 0u; |
| work->result = C2_OK; |
| work->worklets.front()->output.configUpdate.clear(); |
| if (mSignalledError || mSignalledOutputEos) { |
| work->result = C2_BAD_VALUE; |
| return; |
| } |
| uint8_t* inBuffer = nullptr; |
| uint32_t inBufferLength = 0; |
| C2ReadView view = mDummyReadView; |
| size_t offset = 0u; |
| size_t size = 0u; |
| if (!work->input.buffers.empty()) { |
| view = work->input.buffers[0]->data().linearBlocks().front().map().get(); |
| size = view.capacity(); |
| } |
| if (size && view.error()) { |
| ALOGE("read view map failed %d", view.error()); |
| work->result = view.error(); |
| return; |
| } |
| |
| bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0; |
| bool codecConfig = |
| (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0; |
| |
| if (codecConfig) { |
| if (size == 0u) { |
| ALOGE("empty codec config"); |
| mSignalledError = true; |
| work->result = C2_CORRUPTED; |
| return; |
| } |
| // const_cast because of libAACdec method signature. |
| inBuffer = const_cast<uint8_t*>(view.data() + offset); |
| inBufferLength = size; |
| |
| /* GA header configuration sent to Decoder! */ |
| int err_code = configXAACDecoder(inBuffer, inBufferLength); |
| if (err_code) { |
| ALOGE("configXAACDecoder err_code = %d", err_code); |
| mSignalledError = true; |
| work->result = C2_CORRUPTED; |
| return; |
| } |
| work->worklets.front()->output.ordinal = work->input.ordinal; |
| work->worklets.front()->output.buffers.clear(); |
| |
| return; |
| } |
| |
| mCurFrameIndex = work->input.ordinal.frameIndex.peeku(); |
| mCurTimestamp = work->input.ordinal.timestamp.peeku(); |
| mOutputDrainBufferWritePos = 0; |
| while (size > 0u) { |
| if ((kOutputDrainBufferSize * sizeof(int16_t) - |
| mOutputDrainBufferWritePos) < |
| (mOutputFrameLength * sizeof(int16_t) * mNumChannels)) { |
| ALOGV("skipping decode: not enough space left in DrainBuffer"); |
| break; |
| } |
| |
| ALOGV("inAttribute size = %zu", size); |
| if (mIntf->isAdts()) { |
| ALOGV("ADTS"); |
| size_t adtsHeaderSize = 0; |
| // skip 30 bits, aac_frame_length follows. |
| // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? |
| |
| const uint8_t* adtsHeader = view.data() + offset; |
| bool signalError = false; |
| if (size < 7) { |
| ALOGE("Audio data too short to contain even the ADTS header. " |
| "Got %zu bytes.", size); |
| hexdump(adtsHeader, size); |
| signalError = true; |
| } else { |
| bool protectionAbsent = (adtsHeader[1] & 1); |
| unsigned aac_frame_length = ((adtsHeader[3] & 3) << 11) | |
| (adtsHeader[4] << 3) | |
| (adtsHeader[5] >> 5); |
| |
| if (size < aac_frame_length) { |
| ALOGE("Not enough audio data for the complete frame. " |
| "Got %zu bytes, frame size according to the ADTS " |
| "header is %u bytes.", size, aac_frame_length); |
| hexdump(adtsHeader, size); |
| signalError = true; |
| } else { |
| adtsHeaderSize = (protectionAbsent ? 7 : 9); |
| if (aac_frame_length < adtsHeaderSize) { |
| signalError = true; |
| } else { |
| // const_cast because of libAACdec method signature. |
| inBuffer = |
| const_cast<uint8_t*>(adtsHeader + adtsHeaderSize); |
| inBufferLength = aac_frame_length - adtsHeaderSize; |
| |
| offset += adtsHeaderSize; |
| size -= adtsHeaderSize; |
| } |
| } |
| } |
| |
| if (signalError) { |
| mSignalledError = true; |
| work->result = C2_CORRUPTED; |
| return; |
| } |
| } else { |
| ALOGV("Non ADTS"); |
| // const_cast because of libAACdec method signature. |
| inBuffer = const_cast<uint8_t*>(view.data() + offset); |
| inBufferLength = size; |
| } |
| |
| signed int prevSampleRate = mSampFreq; |
| signed int prevNumChannels = mNumChannels; |
| |
| /* XAAC decoder expects first frame to be fed via configXAACDecoder API |
| * which should initialize the codec. Once this state is reached, call the |
| * decodeXAACStream API with same frame to decode! */ |
| if (!mIsCodecInitialized) { |
| int err_code = configXAACDecoder(inBuffer, inBufferLength); |
| if (err_code) { |
| ALOGE("configXAACDecoder Failed 2 err_code = %d", err_code); |
| mSignalledError = true; |
| work->result = C2_CORRUPTED; |
| return; |
| } |
| mIsCodecConfigFlushRequired = true; |
| |
| if ((mSampFreq != prevSampleRate) || |
| (mNumChannels != prevNumChannels)) { |
| ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels", |
| prevSampleRate, mSampFreq, prevNumChannels, mNumChannels); |
| |
| C2StreamSampleRateInfo::output sampleRateInfo(0u, mSampFreq); |
| C2StreamChannelCountInfo::output channelCountInfo(0u, mNumChannels); |
| std::vector<std::unique_ptr<C2SettingResult>> failures; |
| c2_status_t err = mIntf->config( |
| { &sampleRateInfo, &channelCountInfo }, |
| C2_MAY_BLOCK, |
| &failures); |
| if (err == OK) { |
| work->worklets.front()->output.configUpdate.push_back( |
| C2Param::Copy(sampleRateInfo)); |
| work->worklets.front()->output.configUpdate.push_back( |
| C2Param::Copy(channelCountInfo)); |
| } else { |
| ALOGE("Cannot set width and height"); |
| mSignalledError = true; |
| work->result = C2_CORRUPTED; |
| return; |
| } |
| } |
| } |
| |
| signed int bytesConsumed = 0; |
| int errorCode = 0; |
| if (mIsCodecInitialized) { |
| errorCode = decodeXAACStream(inBuffer, inBufferLength, |
| &bytesConsumed, &mNumOutBytes); |
| } else { |
| ALOGE("Assumption that first frame after header initializes decoder Failed!"); |
| } |
| size -= bytesConsumed; |
| offset += bytesConsumed; |
| |
| if (inBufferLength != (uint32_t)bytesConsumed) |
| ALOGE("All data not consumed"); |
| |
| /* In case of error, decoder would have given out empty buffer */ |
| if ((0 != errorCode) && (0 == mNumOutBytes) && mIsCodecInitialized) |
| mNumOutBytes = mOutputFrameLength * (mPcmWdSz / 8) * mNumChannels; |
| |
| if (!bytesConsumed) { |
| ALOGE("bytesConsumed = 0 should never happen"); |
| mSignalledError = true; |
| work->result = C2_CORRUPTED; |
| return; |
| } |
| |
| if ((uint32_t)mNumOutBytes > |
| mOutputFrameLength * sizeof(int16_t) * mNumChannels) { |
| ALOGE("mNumOutBytes > mOutputFrameLength * sizeof(int16_t) * mNumChannels, should never happen"); |
| mSignalledError = true; |
| work->result = C2_CORRUPTED; |
| return; |
| } |
| |
| if (errorCode) { |
| // TODO: check for overflow, ASAN |
| memset(mOutputBuffer, 0, mNumOutBytes); |
| |
| // Discard input buffer. |
| size = 0; |
| |
| // fall through |
| } |
| memcpy(mOutputDrainBuffer, mOutputBuffer, mNumOutBytes); |
| mOutputDrainBufferWritePos += mNumOutBytes; |
| } |
| |
| if (mOutputDrainBufferWritePos) { |
| finishWork(work, pool); |
| } else { |
| fillEmptyWork(work); |
| } |
| if (eos) mSignalledOutputEos = true; |
| } |
| |
| c2_status_t C2SoftXaacDec::drain(uint32_t drainMode, |
| const std::shared_ptr<C2BlockPool>& pool) { |
| (void)pool; |
| if (drainMode == NO_DRAIN) { |
| ALOGW("drain with NO_DRAIN: no-op"); |
| return C2_OK; |
| } |
| if (drainMode == DRAIN_CHAIN) { |
| ALOGW("DRAIN_CHAIN not supported"); |
| return C2_OMITTED; |
| } |
| |
| return C2_OK; |
| } |
| |
| void C2SoftXaacDec::configflushDecode() { |
| IA_ERRORCODE err_code; |
| uint32_t ui_init_done; |
| uint32_t inBufferLength = 8203; |
| |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_INIT, |
| IA_CMD_TYPE_FLUSH_MEM, |
| NULL); |
| |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_INPUT_BYTES, |
| 0, |
| &inBufferLength); |
| |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_INIT, |
| IA_CMD_TYPE_FLUSH_MEM, |
| NULL); |
| |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_INIT, |
| IA_CMD_TYPE_INIT_DONE_QUERY, |
| &ui_init_done); |
| if (ui_init_done) { |
| err_code = getXAACStreamInfo(); |
| ALOGV("Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz %d\nchannelMask %d\noutputFrameLength %d", |
| mSampFreq, mNumChannels, mPcmWdSz, mChannelMask, mOutputFrameLength); |
| if(mNumChannels > MAX_CHANNEL_COUNT) { |
| ALOGE(" No of channels are more than max channels\n"); |
| mIsCodecInitialized = false; |
| } else |
| mIsCodecInitialized = true; |
| } |
| } |
| |
| c2_status_t C2SoftXaacDec::onFlush_sm() { |
| if (mIsCodecInitialized) configflushDecode(); |
| drainDecoder(); |
| |
| return C2_OK; |
| } |
| |
| int C2SoftXaacDec::drainDecoder() { |
| /* Output delay compensation logic should sit here. */ |
| /* Nothing to be done as XAAC decoder does not introduce output buffer delay */ |
| |
| return 0; |
| } |
| |
| int C2SoftXaacDec::initXAACDecoder() { |
| /* First part */ |
| /* Error Handler Init */ |
| /* Get Library Name, Library Version and API Version */ |
| /* Initialize API structure + Default config set */ |
| /* Set config params from user */ |
| /* Initialize memory tables */ |
| /* Get memory information and allocate memory */ |
| |
| mInputBufferSize = 0; |
| mInputBuffer = nullptr; |
| mOutputBuffer = nullptr; |
| mMallocCount = 0; |
| /* Process struct initing end */ |
| |
| /* ******************************************************************/ |
| /* Initialize API structure and set config params to default */ |
| /* ******************************************************************/ |
| /* API size */ |
| uint32_t pui_api_size; |
| /* Get the API size */ |
| IA_ERRORCODE err_code = ixheaacd_dec_api(NULL, |
| IA_API_CMD_GET_API_SIZE, |
| 0, |
| &pui_api_size); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_API_SIZE"); |
| |
| /* Allocate memory for API */ |
| if (!mMemoryArray[mMallocCount]) { |
| mMemoryArray[mMallocCount] = memalign(4, pui_api_size); |
| if (!mMemoryArray[mMallocCount]) { |
| ALOGE("malloc for pui_api_size + 4 >> %d Failed", pui_api_size + 4); |
| return IA_FATAL_ERROR; |
| } |
| } |
| |
| /* Set API object with the memory allocated */ |
| mXheaacCodecHandle = (pVOID)((WORD8*)mMemoryArray[mMallocCount]); |
| mMallocCount++; |
| |
| /* Set the config params to default values */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_INIT, |
| IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, |
| NULL); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS"); |
| |
| /* ******************************************************************/ |
| /* Set config parameters */ |
| /* ******************************************************************/ |
| uint32_t ui_mp4_flag = 1; |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4, |
| &ui_mp4_flag); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4"); |
| |
| /* ******************************************************************/ |
| /* Initialize Memory info tables */ |
| /* ******************************************************************/ |
| uint32_t ui_proc_mem_tabs_size; |
| /* Get memory info tables size */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_MEMTABS_SIZE, |
| 0, |
| &ui_proc_mem_tabs_size); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEMTABS_SIZE"); |
| |
| if (!mMemoryArray[mMallocCount]) { |
| mMemoryArray[mMallocCount] = memalign(4, ui_proc_mem_tabs_size); |
| if (!mMemoryArray[mMallocCount]) { |
| ALOGE("Malloc for size (ui_proc_mem_tabs_size + 4) = %d failed!", ui_proc_mem_tabs_size + 4); |
| return IA_FATAL_ERROR; |
| } |
| } |
| |
| /* Set pointer for process memory tables */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_MEMTABS_PTR, |
| 0, |
| (pVOID)((WORD8*)mMemoryArray[mMallocCount])); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_MEMTABS_PTR"); |
| mMallocCount++; |
| |
| /* initialize the API, post config, fill memory tables */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_INIT, |
| IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, |
| NULL); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS"); |
| |
| /* ******************************************************************/ |
| /* Allocate Memory with info from library */ |
| /* ******************************************************************/ |
| /* There are four different types of memories, that needs to be allocated */ |
| /* persistent,scratch,input and output */ |
| for (int i = 0; i < 4; i++) { |
| int ui_size = 0, ui_alignment = 0, ui_type = 0; |
| |
| /* Get memory size */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_MEM_INFO_SIZE, |
| i, |
| &ui_size); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEM_INFO_SIZE"); |
| |
| /* Get memory alignment */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_MEM_INFO_ALIGNMENT, |
| i, |
| &ui_alignment); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEM_INFO_ALIGNMENT"); |
| |
| /* Get memory type */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_MEM_INFO_TYPE, |
| i, |
| &ui_type); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEM_INFO_TYPE"); |
| |
| if (!mMemoryArray[mMallocCount]) { |
| mMemoryArray[mMallocCount] = memalign(ui_alignment, ui_size); |
| if (!mMemoryArray[mMallocCount]) { |
| ALOGE("Malloc for size (ui_size + ui_alignment) = %d failed!", |
| ui_size + ui_alignment); |
| return IA_FATAL_ERROR; |
| } |
| } |
| pVOID pv_alloc_ptr = (pVOID)((WORD8*)mMemoryArray[mMallocCount]); |
| mMallocCount++; |
| |
| /* Set the buffer pointer */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_MEM_PTR, |
| i, |
| pv_alloc_ptr); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_MEM_PTR"); |
| if (ui_type == IA_MEMTYPE_INPUT) { |
| mInputBuffer = (pWORD8)pv_alloc_ptr; |
| mInputBufferSize = ui_size; |
| } |
| if (ui_type == IA_MEMTYPE_OUTPUT) |
| mOutputBuffer = (pWORD8)pv_alloc_ptr; |
| } |
| /* End first part */ |
| |
| return IA_NO_ERROR; |
| } |
| |
| status_t C2SoftXaacDec::initXAACDrc() { |
| unsigned int ui_drc_val; |
| IA_ERRORCODE err_code = IA_NO_ERROR; |
| char value[PROPERTY_VALUE_MAX]; |
| if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) { |
| ui_drc_val = atoi(value); |
| ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d", |
| ui_drc_val, DRC_DEFAULT_MOBILE_REF_LEVEL); |
| } else { |
| ui_drc_val = DRC_DEFAULT_MOBILE_REF_LEVEL; |
| } |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL, |
| &ui_drc_val); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL"); |
| |
| if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) { |
| ui_drc_val = atoi(value); |
| ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", |
| ui_drc_val, DRC_DEFAULT_MOBILE_DRC_CUT); |
| } else { |
| ui_drc_val = DRC_DEFAULT_MOBILE_DRC_CUT; |
| } |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT, |
| &ui_drc_val); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT"); |
| |
| if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) { |
| ui_drc_val = atoi(value); |
| ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", |
| ui_drc_val, DRC_DEFAULT_MOBILE_DRC_BOOST); |
| } else { |
| ui_drc_val = DRC_DEFAULT_MOBILE_DRC_BOOST; |
| } |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST, |
| &ui_drc_val); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST"); |
| |
| if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) { |
| ui_drc_val = atoi(value); |
| ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", |
| ui_drc_val, DRC_DEFAULT_MOBILE_DRC_HEAVY); |
| } else { |
| ui_drc_val = DRC_DEFAULT_MOBILE_DRC_HEAVY; |
| } |
| |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP, |
| &ui_drc_val); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP"); |
| |
| return IA_NO_ERROR; |
| } |
| |
| int C2SoftXaacDec::deInitXAACDecoder() { |
| ALOGV("deInitXAACDecoder"); |
| |
| /* Error code */ |
| IA_ERRORCODE err_code = IA_NO_ERROR; |
| |
| /* Tell that the input is over in this buffer */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_INPUT_OVER, |
| 0, |
| NULL); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_INPUT_OVER"); |
| |
| for (int i = 0; i < mMallocCount; i++) { |
| if (mMemoryArray[i]) { |
| free(mMemoryArray[i]); |
| mMemoryArray[i] = nullptr; |
| } |
| } |
| mMallocCount = 0; |
| |
| return err_code; |
| } |
| |
| int C2SoftXaacDec::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { |
| if (mInputBufferSize < inBufferLength) { |
| ALOGE("Cannot config AAC, input buffer size %d < inBufferLength %d", mInputBufferSize, inBufferLength); |
| return false; |
| } |
| /* Copy the buffer passed by Android plugin to codec input buffer */ |
| memcpy(mInputBuffer, inBuffer, inBufferLength); |
| |
| /* Set number of bytes to be processed */ |
| IA_ERRORCODE err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_INPUT_BYTES, |
| 0, |
| &inBufferLength); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_INPUT_BYTES"); |
| |
| if (mIsCodecConfigFlushRequired) { |
| /* If codec is already initialized, then GA header is passed again */ |
| /* Need to call the Flush API instead of INIT_PROCESS */ |
| mIsCodecInitialized = false; /* Codec needs to be Reinitialized after flush */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_INIT, |
| IA_CMD_TYPE_GA_HDR, |
| NULL); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_GA_HDR"); |
| } else { |
| /* Initialize the process */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_INIT, |
| IA_CMD_TYPE_INIT_PROCESS, |
| NULL); |
| } |
| |
| uint32_t ui_init_done; |
| /* Checking for end of initialization */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_INIT, |
| IA_CMD_TYPE_INIT_DONE_QUERY, |
| &ui_init_done); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_INIT_DONE_QUERY"); |
| |
| /* How much buffer is used in input buffers */ |
| int32_t i_bytes_consumed; |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_CURIDX_INPUT_BUF, |
| 0, |
| &i_bytes_consumed); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF"); |
| |
| if (ui_init_done) { |
| err_code = getXAACStreamInfo(); |
| ALOGI("Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz %d\nchannelMask %d\noutputFrameLength %d", |
| mSampFreq, mNumChannels, mPcmWdSz, mChannelMask, mOutputFrameLength); |
| mIsCodecInitialized = true; |
| } |
| |
| return err_code; |
| } |
| |
| int C2SoftXaacDec::decodeXAACStream(uint8_t* inBuffer, |
| uint32_t inBufferLength, |
| int32_t* bytesConsumed, |
| int32_t* outBytes) { |
| if (mInputBufferSize < inBufferLength) { |
| ALOGE("Cannot config AAC, input buffer size %d < inBufferLength %d", mInputBufferSize, inBufferLength); |
| return -1; |
| } |
| /* Copy the buffer passed by Android plugin to codec input buffer */ |
| memcpy(mInputBuffer, inBuffer, inBufferLength); |
| |
| /* Set number of bytes to be processed */ |
| IA_ERRORCODE err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_SET_INPUT_BYTES, |
| 0, |
| &inBufferLength); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_INPUT_BYTES"); |
| |
| /* Execute process */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_EXECUTE, |
| IA_CMD_TYPE_DO_EXECUTE, |
| NULL); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_DO_EXECUTE"); |
| |
| /* Checking for end of processing */ |
| uint32_t ui_exec_done; |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_EXECUTE, |
| IA_CMD_TYPE_DONE_QUERY, |
| &ui_exec_done); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_DONE_QUERY"); |
| |
| /* How much buffer is used in input buffers */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_CURIDX_INPUT_BUF, |
| 0, |
| bytesConsumed); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF"); |
| |
| /* Get the output bytes */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_OUTPUT_BYTES, |
| 0, |
| outBytes); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_OUTPUT_BYTES"); |
| |
| return err_code; |
| } |
| |
| IA_ERRORCODE C2SoftXaacDec::getXAACStreamInfo() { |
| IA_ERRORCODE err_code = IA_NO_ERROR; |
| |
| /* Sampling frequency */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ, |
| &mSampFreq); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ"); |
| |
| /* Total Number of Channels */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS, |
| &mNumChannels); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS"); |
| |
| /* PCM word size */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ, |
| &mPcmWdSz); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ"); |
| |
| /* channel mask to tell the arrangement of channels in bit stream */ |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK, |
| &mChannelMask); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK"); |
| |
| /* Channel mode to tell MONO/STEREO/DUAL-MONO/NONE_OF_THESE */ |
| uint32_t ui_channel_mode; |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE, |
| &ui_channel_mode); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE"); |
| if (ui_channel_mode == 0) |
| ALOGV("Channel Mode: MONO_OR_PS\n"); |
| else if (ui_channel_mode == 1) |
| ALOGV("Channel Mode: STEREO\n"); |
| else if (ui_channel_mode == 2) |
| ALOGV("Channel Mode: DUAL-MONO\n"); |
| else |
| ALOGV("Channel Mode: NONE_OF_THESE or MULTICHANNEL\n"); |
| |
| /* Channel mode to tell SBR PRESENT/NOT_PRESENT */ |
| uint32_t ui_sbr_mode; |
| err_code = ixheaacd_dec_api(mXheaacCodecHandle, |
| IA_API_CMD_GET_CONFIG_PARAM, |
| IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE, |
| &ui_sbr_mode); |
| RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE"); |
| if (ui_sbr_mode == 0) |
| ALOGV("SBR Mode: NOT_PRESENT\n"); |
| else if (ui_sbr_mode == 1) |
| ALOGV("SBR Mode: PRESENT\n"); |
| else |
| ALOGV("SBR Mode: ILLEGAL\n"); |
| |
| /* mOutputFrameLength = 1024 * (1 + SBR_MODE) for AAC */ |
| /* For USAC it could be 1024 * 3 , support to query */ |
| /* not yet added in codec */ |
| mOutputFrameLength = 1024 * (1 + ui_sbr_mode); |
| ALOGI("mOutputFrameLength %d ui_sbr_mode %d", mOutputFrameLength, ui_sbr_mode); |
| |
| return IA_NO_ERROR; |
| } |
| |
| class C2SoftXaacDecFactory : public C2ComponentFactory { |
| public: |
| C2SoftXaacDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>( |
| GetCodec2PlatformComponentStore()->getParamReflector())) { |
| } |
| |
| virtual c2_status_t createComponent( |
| c2_node_id_t id, |
| std::shared_ptr<C2Component>* const component, |
| std::function<void(C2Component*)> deleter) override { |
| *component = std::shared_ptr<C2Component>( |
| new C2SoftXaacDec(COMPONENT_NAME, |
| id, |
| std::make_shared<C2SoftXaacDec::IntfImpl>(mHelper)), |
| deleter); |
| return C2_OK; |
| } |
| |
| virtual c2_status_t createInterface( |
| c2_node_id_t id, |
| std::shared_ptr<C2ComponentInterface>* const interface, |
| std::function<void(C2ComponentInterface*)> deleter) override { |
| *interface = std::shared_ptr<C2ComponentInterface>( |
| new SimpleInterface<C2SoftXaacDec::IntfImpl>( |
| COMPONENT_NAME, id, std::make_shared<C2SoftXaacDec::IntfImpl>(mHelper)), |
| deleter); |
| return C2_OK; |
| } |
| |
| virtual ~C2SoftXaacDecFactory() override = default; |
| |
| private: |
| std::shared_ptr<C2ReflectorHelper> mHelper; |
| }; |
| |
| } // namespace android |
| |
| extern "C" ::C2ComponentFactory* CreateCodec2Factory() { |
| ALOGV("in %s", __func__); |
| return new ::android::C2SoftXaacDecFactory(); |
| } |
| |
| extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) { |
| ALOGV("in %s", __func__); |
| delete factory; |
| } |