/* * Copyright (C) 2017 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 "C2SoftAacDec" #include #include #include #include #include #include #include #include #include #include #include #include "C2SoftAacDec.h" #define FILEREAD_MAX_LAYERS 2 #define DRC_DEFAULT_MOBILE_REF_LEVEL -16.0 /* 64*-0.25dB = -16 dB below full scale for mobile conf */ #define DRC_DEFAULT_MOBILE_DRC_CUT 1.0 /* maximum compression of dynamic range for mobile conf */ #define DRC_DEFAULT_MOBILE_DRC_BOOST 1.0 /* maximum compression of dynamic range for mobile conf */ #define DRC_DEFAULT_MOBILE_DRC_HEAVY C2Config::DRC_COMPRESSION_HEAVY /* switch for heavy compression for mobile conf */ #define DRC_DEFAULT_MOBILE_DRC_EFFECT 3 /* MPEG-D DRC effect type; 3 => Limited playback range */ #define DRC_DEFAULT_MOBILE_DRC_ALBUM 0 /* MPEG-D DRC album mode; 0 => album mode is disabled, 1 => album mode is enabled */ #define DRC_DEFAULT_MOBILE_OUTPUT_LOUDNESS (0.25) /* decoder output loudness; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */ #define DRC_DEFAULT_MOBILE_ENC_LEVEL (0.25) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */ #define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */ // names of properties that can be used to override the default DRC settings #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 PROP_DRC_OVERRIDE_EFFECT "ro.aac_drc_effect_type" namespace android { constexpr char COMPONENT_NAME[] = "c2.android.aac.decoder"; constexpr size_t kDefaultOutputPortDelay = 2; constexpr size_t kMaxOutputPortDelay = 16; class C2SoftAacDec::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) : SimpleInterface::BaseParams( helper, COMPONENT_NAME, C2Component::KIND_DECODER, C2Component::DOMAIN_AUDIO, MEDIA_MIMETYPE_AUDIO_AAC) { noPrivateBuffers(); noInputReferences(); noOutputReferences(); noInputLatency(); noTimeStretch(); addParameter( DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY) .withDefault(new C2PortActualDelayTuning::output(kDefaultOutputPortDelay)) .withFields({C2F(mActualOutputDelay, value).inRange(0, kMaxOutputPortDelay)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 44100)) .withFields({C2F(mSampleRate, value).oneOf({ 7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 })}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); addParameter( DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) .withFields({C2F(mChannelCount, value).inRange(1, MAX_CHANNEL_COUNT)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mMaxChannelCount, C2_PARAMKEY_MAX_CHANNEL_COUNT) .withDefault(new C2StreamMaxChannelCountInfo::input(0u, MAX_CHANNEL_COUNT)) .withFields({C2F(mMaxChannelCount, value).inRange(1, MAX_CHANNEL_COUNT)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mBitrate, C2_PARAMKEY_BITRATE) .withDefault(new C2StreamBitrateInfo::input(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(8000, 960000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); addParameter( DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192)) .build()); addParameter( DefineParam(mAacFormat, C2_PARAMKEY_AAC_PACKAGING) .withDefault(new C2StreamAacFormatInfo::input(0u, C2Config::AAC_PACKAGING_RAW)) .withFields({C2F(mAacFormat, value).oneOf({ C2Config::AAC_PACKAGING_RAW, C2Config::AAC_PACKAGING_ADTS })}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL) .withDefault(new C2StreamProfileLevelInfo::input(0u, C2Config::PROFILE_AAC_LC, C2Config::LEVEL_UNUSED)) .withFields({ C2F(mProfileLevel, profile).oneOf({ C2Config::PROFILE_AAC_LC, C2Config::PROFILE_AAC_HE, C2Config::PROFILE_AAC_HE_PS, C2Config::PROFILE_AAC_LD, C2Config::PROFILE_AAC_ELD, C2Config::PROFILE_AAC_ER_SCALABLE, C2Config::PROFILE_AAC_XHE}), C2F(mProfileLevel, level).oneOf({ C2Config::LEVEL_UNUSED }) }) .withSetter(ProfileLevelSetter) .build()); addParameter( DefineParam(mDrcCompressMode, C2_PARAMKEY_DRC_COMPRESSION_MODE) .withDefault(new C2StreamDrcCompressionModeTuning::input(0u, C2Config::DRC_COMPRESSION_HEAVY)) .withFields({ C2F(mDrcCompressMode, value).oneOf({ C2Config::DRC_COMPRESSION_ODM_DEFAULT, C2Config::DRC_COMPRESSION_NONE, C2Config::DRC_COMPRESSION_LIGHT, C2Config::DRC_COMPRESSION_HEAVY}) }) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mDrcTargetRefLevel, C2_PARAMKEY_DRC_TARGET_REFERENCE_LEVEL) .withDefault(new C2StreamDrcTargetReferenceLevelTuning::input(0u, DRC_DEFAULT_MOBILE_REF_LEVEL)) .withFields({C2F(mDrcTargetRefLevel, value).inRange(-31.75, 0.25)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mDrcEncTargetLevel, C2_PARAMKEY_DRC_ENCODED_TARGET_LEVEL) .withDefault(new C2StreamDrcEncodedTargetLevelTuning::input(0u, DRC_DEFAULT_MOBILE_ENC_LEVEL)) .withFields({C2F(mDrcEncTargetLevel, value).inRange(-31.75, 0.25)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mDrcBoostFactor, C2_PARAMKEY_DRC_BOOST_FACTOR) .withDefault(new C2StreamDrcBoostFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_BOOST)) .withFields({C2F(mDrcBoostFactor, value).inRange(0, 1.)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mDrcAttenuationFactor, C2_PARAMKEY_DRC_ATTENUATION_FACTOR) .withDefault(new C2StreamDrcAttenuationFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_CUT)) .withFields({C2F(mDrcAttenuationFactor, value).inRange(0, 1.)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mDrcEffectType, C2_PARAMKEY_DRC_EFFECT_TYPE) .withDefault(new C2StreamDrcEffectTypeTuning::input(0u, C2Config::DRC_EFFECT_LIMITED_PLAYBACK_RANGE)) .withFields({ C2F(mDrcEffectType, value).oneOf({ C2Config::DRC_EFFECT_ODM_DEFAULT, C2Config::DRC_EFFECT_OFF, C2Config::DRC_EFFECT_NONE, C2Config::DRC_EFFECT_LATE_NIGHT, C2Config::DRC_EFFECT_NOISY_ENVIRONMENT, C2Config::DRC_EFFECT_LIMITED_PLAYBACK_RANGE, C2Config::DRC_EFFECT_LOW_PLAYBACK_LEVEL, C2Config::DRC_EFFECT_DIALOG_ENHANCEMENT, C2Config::DRC_EFFECT_GENERAL_COMPRESSION}) }) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mDrcAlbumMode, C2_PARAMKEY_DRC_ALBUM_MODE) .withDefault(new C2StreamDrcAlbumModeTuning::input(0u, C2Config::DRC_ALBUM_MODE_OFF)) .withFields({ C2F(mDrcAlbumMode, value).oneOf({ C2Config::DRC_ALBUM_MODE_OFF, C2Config::DRC_ALBUM_MODE_ON}) }) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( DefineParam(mDrcOutputLoudness, C2_PARAMKEY_DRC_OUTPUT_LOUDNESS) .withDefault(new C2StreamDrcOutputLoudnessTuning::output(0u, DRC_DEFAULT_MOBILE_OUTPUT_LOUDNESS)) .withFields({C2F(mDrcOutputLoudness, value).inRange(-57.75, 0.25)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); } bool isAdts() const { return mAacFormat->value == C2Config::AAC_PACKAGING_ADTS; } static C2R ProfileLevelSetter(bool mayBlock, C2P &me) { (void)mayBlock; (void)me; // TODO: validate return C2R::Ok(); } int32_t getDrcCompressMode() const { return mDrcCompressMode->value == C2Config::DRC_COMPRESSION_HEAVY ? 1 : 0; } int32_t getDrcTargetRefLevel() const { return (mDrcTargetRefLevel->value <= 0 ? -mDrcTargetRefLevel->value * 4. + 0.5 : -1); } int32_t getDrcEncTargetLevel() const { return (mDrcEncTargetLevel->value <= 0 ? -mDrcEncTargetLevel->value * 4. + 0.5 : -1); } int32_t getDrcBoostFactor() const { return mDrcBoostFactor->value * 127. + 0.5; } int32_t getDrcAttenuationFactor() const { return mDrcAttenuationFactor->value * 127. + 0.5; } int32_t getDrcEffectType() const { return mDrcEffectType->value; } int32_t getDrcAlbumMode() const { return mDrcAlbumMode->value; } u_int32_t getMaxChannelCount() const { return mMaxChannelCount->value; } int32_t getDrcOutputLoudness() const { return (mDrcOutputLoudness->value <= 0 ? -mDrcOutputLoudness->value * 4. + 0.5 : -1); } private: std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; std::shared_ptr mAacFormat; std::shared_ptr mProfileLevel; std::shared_ptr mDrcCompressMode; std::shared_ptr mDrcTargetRefLevel; std::shared_ptr mDrcEncTargetLevel; std::shared_ptr mDrcBoostFactor; std::shared_ptr mDrcAttenuationFactor; std::shared_ptr mDrcEffectType; std::shared_ptr mDrcAlbumMode; std::shared_ptr mMaxChannelCount; std::shared_ptr mDrcOutputLoudness; // TODO Add : C2StreamAacSbrModeTuning }; C2SoftAacDec::C2SoftAacDec( const char *name, c2_node_id_t id, const std::shared_ptr &intfImpl) : SimpleC2Component(std::make_shared>(name, id, intfImpl)), mIntf(intfImpl), mAACDecoder(nullptr), mStreamInfo(nullptr), mSignalledError(false), mOutputPortDelay(kDefaultOutputPortDelay), mOutputDelayRingBuffer(nullptr) { } C2SoftAacDec::~C2SoftAacDec() { onRelease(); } c2_status_t C2SoftAacDec::onInit() { status_t err = initDecoder(); return err == OK ? C2_OK : C2_CORRUPTED; } c2_status_t C2SoftAacDec::onStop() { drainDecoder(); // reset the "configured" state mOutputDelayCompensated = 0; mOutputDelayRingBufferWritePos = 0; mOutputDelayRingBufferReadPos = 0; mOutputDelayRingBufferFilled = 0; mOutputDelayRingBuffer.reset(); mBuffersInfo.clear(); status_t status = UNKNOWN_ERROR; if (mAACDecoder) { aacDecoder_Close(mAACDecoder); status = initDecoder(); } mSignalledError = false; return status == OK ? C2_OK : C2_CORRUPTED; } void C2SoftAacDec::onReset() { (void)onStop(); } void C2SoftAacDec::onRelease() { if (mAACDecoder) { aacDecoder_Close(mAACDecoder); mAACDecoder = nullptr; } mOutputDelayRingBuffer.reset(); } status_t C2SoftAacDec::initDecoder() { ALOGV("initDecoder()"); status_t status = UNKNOWN_ERROR; mAACDecoder = aacDecoder_Open(TT_MP4_ADIF, /* num layers */ 1); if (mAACDecoder != nullptr) { mStreamInfo = aacDecoder_GetStreamInfo(mAACDecoder); if (mStreamInfo != nullptr) { status = OK; } } mOutputDelayCompensated = 0; mOutputDelayRingBufferSize = 2048 * MAX_CHANNEL_COUNT * kNumDelayBlocksMax; mOutputDelayRingBuffer.reset(new short[mOutputDelayRingBufferSize]); mOutputDelayRingBufferWritePos = 0; mOutputDelayRingBufferReadPos = 0; mOutputDelayRingBufferFilled = 0; if (mAACDecoder == nullptr) { ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code"); } //aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, 0); //init DRC wrapper mDrcWrap.setDecoderHandle(mAACDecoder); mDrcWrap.submitStreamData(mStreamInfo); // for streams that contain metadata, use the mobile profile DRC settings unless overridden by platform properties // TODO: change the DRC settings depending on audio output device type (HDMI, loadspeaker, headphone) // DRC_PRES_MODE_WRAP_DESIRED_TARGET int32_t targetRefLevel = mIntf->getDrcTargetRefLevel(); ALOGV("AAC decoder using desired DRC target reference level of %d", targetRefLevel); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, (unsigned)targetRefLevel); // DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR int32_t attenuationFactor = mIntf->getDrcAttenuationFactor(); ALOGV("AAC decoder using desired DRC attenuation factor of %d", attenuationFactor); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, (unsigned)attenuationFactor); // DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR int32_t boostFactor = mIntf->getDrcBoostFactor(); ALOGV("AAC decoder using desired DRC boost factor of %d", boostFactor); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, (unsigned)boostFactor); // DRC_PRES_MODE_WRAP_DESIRED_HEAVY int32_t compressMode = mIntf->getDrcCompressMode(); ALOGV("AAC decoder using desired DRC heavy compression switch of %d", compressMode); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, (unsigned)compressMode); // DRC_PRES_MODE_WRAP_ENCODER_TARGET int32_t encTargetLevel = mIntf->getDrcEncTargetLevel(); ALOGV("AAC decoder using encoder-side DRC reference level of %d", encTargetLevel); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, (unsigned)encTargetLevel); // AAC_UNIDRC_SET_EFFECT int32_t effectType = mIntf->getDrcEffectType(); ALOGV("AAC decoder using MPEG-D DRC effect type %d", effectType); aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_SET_EFFECT, effectType); // AAC_UNIDRC_ALBUM_MODE int32_t albumMode = mIntf->getDrcAlbumMode(); ALOGV("AAC decoder using MPEG-D DRC album mode %d", albumMode); aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_ALBUM_MODE, albumMode); // AAC_PCM_MAX_OUTPUT_CHANNELS u_int32_t maxChannelCount = mIntf->getMaxChannelCount(); ALOGV("AAC decoder using maximum output channel count %d", maxChannelCount); aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, maxChannelCount); return status; } bool C2SoftAacDec::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) { if (numSamples == 0) { return true; } if (outputDelayRingBufferSpaceLeft() < numSamples) { ALOGE("RING BUFFER WOULD OVERFLOW"); return false; } if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) { // faster memcopy loop without checks, if the preconditions allow this for (int32_t i = 0; i < numSamples; i++) { mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i]; } if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) { mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize; } } else { ALOGV("slow C2SoftAacDec::outputDelayRingBufferPutSamples()"); for (int32_t i = 0; i < numSamples; i++) { mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i]; mOutputDelayRingBufferWritePos++; if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) { mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize; } } } mOutputDelayRingBufferFilled += numSamples; return true; } int32_t C2SoftAacDec::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) { if (numSamples > mOutputDelayRingBufferFilled) { ALOGE("RING BUFFER WOULD UNDERRUN"); return -1; } if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize && (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos || mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) { // faster memcopy loop without checks, if the preconditions allow this if (samples != nullptr) { for (int32_t i = 0; i < numSamples; i++) { samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++]; } } else { mOutputDelayRingBufferReadPos += numSamples; } if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) { mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize; } } else { ALOGV("slow C2SoftAacDec::outputDelayRingBufferGetSamples()"); for (int32_t i = 0; i < numSamples; i++) { if (samples != nullptr) { samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos]; } mOutputDelayRingBufferReadPos++; if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) { mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize; } } } mOutputDelayRingBufferFilled -= numSamples; return numSamples; } int32_t C2SoftAacDec::outputDelayRingBufferSamplesAvailable() { return mOutputDelayRingBufferFilled; } int32_t C2SoftAacDec::outputDelayRingBufferSpaceLeft() { return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable(); } void C2SoftAacDec::drainRingBuffer( const std::unique_ptr &work, const std::shared_ptr &pool, bool eos) { while (!mBuffersInfo.empty() && outputDelayRingBufferSamplesAvailable() >= mStreamInfo->frameSize * mStreamInfo->numChannels) { Info &outInfo = mBuffersInfo.front(); ALOGV("outInfo.frameIndex = %" PRIu64, outInfo.frameIndex); int samplesize __unused = mStreamInfo->numChannels * sizeof(int16_t); int available = outputDelayRingBufferSamplesAvailable(); int numFrames = outInfo.decodedSizes.size(); int numSamples = numFrames * (mStreamInfo->frameSize * mStreamInfo->numChannels); if (available < numSamples) { if (eos) { numSamples = available; } else { break; } } ALOGV("%d samples available (%d), or %d frames", numSamples, available, numFrames); ALOGV("getting %d from ringbuffer", numSamples); std::shared_ptr block; std::function&)> fillWork = [&block, numSamples, pool, this]() -> std::function&)> { auto fillEmptyWork = []( const std::unique_ptr &work, c2_status_t err) { work->result = err; C2FrameData &output = work->worklets.front()->output; output.flags = work->input.flags; output.buffers.clear(); output.ordinal = work->input.ordinal; work->workletsProcessed = 1u; }; using namespace std::placeholders; if (numSamples == 0) { return std::bind(fillEmptyWork, _1, C2_OK); } // TODO: error handling, proper usage, etc. C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; size_t bufferSize = numSamples * sizeof(int16_t); c2_status_t err = pool->fetchLinearBlock(bufferSize, usage, &block); if (err != C2_OK) { ALOGD("failed to fetch a linear block (%d)", err); return std::bind(fillEmptyWork, _1, C2_NO_MEMORY); } C2WriteView wView = block->map().get(); // TODO INT_PCM *outBuffer = reinterpret_cast(wView.data()); int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples); if (ns != numSamples) { ALOGE("not a complete frame of samples available"); mSignalledError = true; return std::bind(fillEmptyWork, _1, C2_CORRUPTED); } return [buffer = createLinearBuffer(block, 0, bufferSize)]( const std::unique_ptr &work) { work->result = C2_OK; C2FrameData &output = work->worklets.front()->output; output.flags = work->input.flags; output.buffers.clear(); output.buffers.push_back(buffer); output.ordinal = work->input.ordinal; work->workletsProcessed = 1u; }; }(); if (work && work->input.ordinal.frameIndex == c2_cntr64_t(outInfo.frameIndex)) { fillWork(work); } else { finish(outInfo.frameIndex, fillWork); } ALOGV("out timestamp %" PRIu64 " / %u", outInfo.timestamp, block ? block->capacity() : 0); mBuffersInfo.pop_front(); } } void C2SoftAacDec::process( const std::unique_ptr &work, const std::shared_ptr &pool) { // Initialize output work work->result = C2_OK; work->workletsProcessed = 1u; work->worklets.front()->output.configUpdate.clear(); work->worklets.front()->output.flags = work->input.flags; if (mSignalledError) { return; } UCHAR* inBuffer[FILEREAD_MAX_LAYERS]; UINT inBufferLength[FILEREAD_MAX_LAYERS] = {0}; UINT bytesValid[FILEREAD_MAX_LAYERS] = {0}; INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT]; 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(); } bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0; bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0; //TODO #if 0 if (mInputBufferCount == 0 && !codecConfig) { ALOGW("first buffer should have FLAG_CODEC_CONFIG set"); codecConfig = true; } #endif if (codecConfig && size > 0u) { // const_cast because of libAACdec method signature. inBuffer[0] = const_cast(view.data() + offset); inBufferLength[0] = size; AAC_DECODER_ERROR decoderErr = aacDecoder_ConfigRaw(mAACDecoder, inBuffer, inBufferLength); if (decoderErr != AAC_DEC_OK) { ALOGE("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr); mSignalledError = true; work->result = C2_CORRUPTED; return; } work->worklets.front()->output.flags = work->input.flags; work->worklets.front()->output.ordinal = work->input.ordinal; work->worklets.front()->output.buffers.clear(); return; } Info inInfo; inInfo.frameIndex = work->input.ordinal.frameIndex.peeku(); inInfo.timestamp = work->input.ordinal.timestamp.peeku(); inInfo.bufferSize = size; inInfo.decodedSizes.clear(); while (size > 0u) { ALOGV("size = %zu", size); if (mIntf->isAdts()) { 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[0] = const_cast(adtsHeader + adtsHeaderSize); inBufferLength[0] = aac_frame_length - adtsHeaderSize; offset += adtsHeaderSize; size -= adtsHeaderSize; } } } if (signalError) { mSignalledError = true; work->result = C2_CORRUPTED; return; } } else { // const_cast because of libAACdec method signature. inBuffer[0] = const_cast(view.data() + offset); inBufferLength[0] = size; } // Fill and decode bytesValid[0] = inBufferLength[0]; INT prevSampleRate = mStreamInfo->sampleRate; INT prevNumChannels = mStreamInfo->numChannels; INT prevOutLoudness = mStreamInfo->outputLoudness; aacDecoder_Fill(mAACDecoder, inBuffer, inBufferLength, bytesValid); // run DRC check mDrcWrap.submitStreamData(mStreamInfo); // apply runtime updates // DRC_PRES_MODE_WRAP_DESIRED_TARGET int32_t targetRefLevel = mIntf->getDrcTargetRefLevel(); ALOGV("AAC decoder using desired DRC target reference level of %d", targetRefLevel); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, (unsigned)targetRefLevel); // DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR int32_t attenuationFactor = mIntf->getDrcAttenuationFactor(); ALOGV("AAC decoder using desired DRC attenuation factor of %d", attenuationFactor); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, (unsigned)attenuationFactor); // DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR int32_t boostFactor = mIntf->getDrcBoostFactor(); ALOGV("AAC decoder using desired DRC boost factor of %d", boostFactor); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, (unsigned)boostFactor); // DRC_PRES_MODE_WRAP_DESIRED_HEAVY int32_t compressMode = mIntf->getDrcCompressMode(); ALOGV("AAC decoder using desried DRC heavy compression switch of %d", compressMode); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, (unsigned)compressMode); // DRC_PRES_MODE_WRAP_ENCODER_TARGET int32_t encTargetLevel = mIntf->getDrcEncTargetLevel(); ALOGV("AAC decoder using encoder-side DRC reference level of %d", encTargetLevel); mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, (unsigned)encTargetLevel); // AAC_UNIDRC_SET_EFFECT int32_t effectType = mIntf->getDrcEffectType(); ALOGV("AAC decoder using MPEG-D DRC effect type %d", effectType); aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_SET_EFFECT, effectType); // AAC_UNIDRC_ALBUM_MODE int32_t albumMode = mIntf->getDrcAlbumMode(); ALOGV("AAC decoder using MPEG-D DRC album mode %d", albumMode); aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_ALBUM_MODE, albumMode); // AAC_PCM_MAX_OUTPUT_CHANNELS int32_t maxChannelCount = mIntf->getMaxChannelCount(); ALOGV("AAC decoder using maximum output channel count %d", maxChannelCount); aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, maxChannelCount); mDrcWrap.update(); UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; size -= inBufferUsedLength; offset += inBufferUsedLength; AAC_DECODER_ERROR decoderErr; do { if (outputDelayRingBufferSpaceLeft() < (mStreamInfo->frameSize * mStreamInfo->numChannels)) { ALOGV("skipping decode: not enough space left in ringbuffer"); // discard buffer size = 0; break; } int numConsumed = mStreamInfo->numTotalBytes; decoderErr = aacDecoder_DecodeFrame(mAACDecoder, tmpOutBuffer, 2048 * MAX_CHANNEL_COUNT, 0 /* flags */); numConsumed = mStreamInfo->numTotalBytes - numConsumed; if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { break; } inInfo.decodedSizes.push_back(numConsumed); if (decoderErr != AAC_DEC_OK) { ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr); } if (bytesValid[0] != 0) { ALOGE("bytesValid[0] != 0 should never happen"); mSignalledError = true; work->result = C2_CORRUPTED; return; } size_t numOutBytes = mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; if (decoderErr == AAC_DEC_OK) { if (!outputDelayRingBufferPutSamples(tmpOutBuffer, mStreamInfo->frameSize * mStreamInfo->numChannels)) { mSignalledError = true; work->result = C2_CORRUPTED; return; } } else { ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr); memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow if (!outputDelayRingBufferPutSamples(tmpOutBuffer, mStreamInfo->frameSize * mStreamInfo->numChannels)) { mSignalledError = true; work->result = C2_CORRUPTED; return; } // Discard input buffer. size = 0; aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); // After an error, replace bufferSize with the sum of the // decodedSizes to resynchronize the in/out lists. inInfo.bufferSize = std::accumulate( inInfo.decodedSizes.begin(), inInfo.decodedSizes.end(), 0); // fall through } /* * AAC+/eAAC+ streams can be signalled in two ways: either explicitly * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual * rate system and the sampling rate in the final output is actually * doubled compared with the core AAC decoder sampling rate. * * Explicit signalling is done by explicitly defining SBR audio object * type in the bitstream. Implicit signalling is done by embedding * SBR content in AAC extension payload specific to SBR, and hence * requires an AAC decoder to perform pre-checks on actual audio frames. * * Thus, we could not say for sure whether a stream is * AAC+/eAAC+ until the first data frame is decoded. */ if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) { // if ((mInputBufferCount > 2) && (mOutputBufferCount <= 1)) { ALOGD("Invalid AAC stream"); // TODO: notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); // mSignalledError = true; // } } else if ((mStreamInfo->sampleRate != prevSampleRate) || (mStreamInfo->numChannels != prevNumChannels)) { ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels", prevSampleRate, mStreamInfo->sampleRate, prevNumChannels, mStreamInfo->numChannels); C2StreamSampleRateInfo::output sampleRateInfo(0u, mStreamInfo->sampleRate); C2StreamChannelCountInfo::output channelCountInfo(0u, mStreamInfo->numChannels); std::vector> failures; c2_status_t err = mIntf->config( { &sampleRateInfo, &channelCountInfo }, C2_MAY_BLOCK, &failures); if (err == OK) { // TODO: this does not handle the case where the values are // altered during config. C2FrameData &output = work->worklets.front()->output; output.configUpdate.push_back(C2Param::Copy(sampleRateInfo)); output.configUpdate.push_back(C2Param::Copy(channelCountInfo)); } else { ALOGE("Config Update failed"); mSignalledError = true; work->result = C2_CORRUPTED; return; } } ALOGV("size = %zu", size); if (mStreamInfo->outputLoudness != prevOutLoudness) { C2StreamDrcOutputLoudnessTuning::output drcOutLoudness(0u, (float) (mStreamInfo->outputLoudness*-0.25)); std::vector> failures; c2_status_t err = mIntf->config( { &drcOutLoudness }, C2_MAY_BLOCK, &failures); if (err == OK) { work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(drcOutLoudness)); } else { ALOGE("Getting output loudness failed"); } } // update config with values used for decoding: // Album mode, target reference level, DRC effect type, DRC attenuation and boost // factor, DRC compression mode, encoder target level and max channel count // with input values as they were not modified by decoder C2StreamDrcAttenuationFactorTuning::input currentAttenuationFactor(0u, (C2FloatValue) (attenuationFactor/127.)); work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(currentAttenuationFactor)); C2StreamDrcBoostFactorTuning::input currentBoostFactor(0u, (C2FloatValue) (boostFactor/127.)); work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(currentBoostFactor)); if (android_get_device_api_level() < __ANDROID_API_S__) { // We used to report DRC compression mode in the output format // in Q and R, but stopped doing that in S C2StreamDrcCompressionModeTuning::input currentCompressMode(0u, (C2Config::drc_compression_mode_t) compressMode); work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(currentCompressMode)); } C2StreamDrcEncodedTargetLevelTuning::input currentEncodedTargetLevel(0u, (C2FloatValue) (encTargetLevel*-0.25)); work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(currentEncodedTargetLevel)); C2StreamDrcAlbumModeTuning::input currentAlbumMode(0u, (C2Config::drc_album_mode_t) albumMode); work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(currentAlbumMode)); C2StreamDrcTargetReferenceLevelTuning::input currentTargetRefLevel(0u, (float) (targetRefLevel*-0.25)); work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(currentTargetRefLevel)); C2StreamDrcEffectTypeTuning::input currentEffectype(0u, (C2Config::drc_effect_type_t) effectType); work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(currentEffectype)); C2StreamMaxChannelCountInfo::input currentMaxChannelCnt(0u, maxChannelCount); work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(currentMaxChannelCnt)); } while (decoderErr == AAC_DEC_OK); } int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels; size_t numSamplesInOutput = mStreamInfo->frameSize * mStreamInfo->numChannels; if (numSamplesInOutput > 0) { size_t actualOutputPortDelay = (outputDelay + numSamplesInOutput - 1) / numSamplesInOutput; if (actualOutputPortDelay > mOutputPortDelay) { mOutputPortDelay = actualOutputPortDelay; ALOGV("New Output port delay %zu ", mOutputPortDelay); C2PortActualDelayTuning::output outputPortDelay(mOutputPortDelay); std::vector> failures; c2_status_t err = mIntf->config({&outputPortDelay}, C2_MAY_BLOCK, &failures); if (err == OK) { work->worklets.front()->output.configUpdate.push_back( C2Param::Copy(outputPortDelay)); } else { ALOGE("Cannot set output delay"); mSignalledError = true; work->workletsProcessed = 1u; work->result = C2_CORRUPTED; return; } } } mBuffersInfo.push_back(std::move(inInfo)); work->workletsProcessed = 0u; if (!eos && mOutputDelayCompensated < outputDelay) { // discard outputDelay at the beginning int32_t toCompensate = outputDelay - mOutputDelayCompensated; int32_t discard = outputDelayRingBufferSamplesAvailable(); if (discard > toCompensate) { discard = toCompensate; } int32_t discarded = outputDelayRingBufferGetSamples(nullptr, discard); mOutputDelayCompensated += discarded; return; } if (eos) { drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work); } else { drainRingBuffer(work, pool, false /* not EOS */); } } c2_status_t C2SoftAacDec::drainInternal( uint32_t drainMode, const std::shared_ptr &pool, const std::unique_ptr &work) { 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; } bool eos = (drainMode == DRAIN_COMPONENT_WITH_EOS); drainDecoder(); drainRingBuffer(work, pool, eos); if (eos) { auto fillEmptyWork = [](const std::unique_ptr &work) { work->worklets.front()->output.flags = work->input.flags; work->worklets.front()->output.buffers.clear(); work->worklets.front()->output.ordinal = work->input.ordinal; work->workletsProcessed = 1u; }; while (mBuffersInfo.size() > 1u) { finish(mBuffersInfo.front().frameIndex, fillEmptyWork); mBuffersInfo.pop_front(); } if (work && work->workletsProcessed == 0u) { fillEmptyWork(work); } mBuffersInfo.clear(); } return C2_OK; } c2_status_t C2SoftAacDec::drain( uint32_t drainMode, const std::shared_ptr &pool) { return drainInternal(drainMode, pool, nullptr); } c2_status_t C2SoftAacDec::onFlush_sm() { drainDecoder(); mBuffersInfo.clear(); int avail; while ((avail = outputDelayRingBufferSamplesAvailable()) > 0) { if (avail > mStreamInfo->frameSize * mStreamInfo->numChannels) { avail = mStreamInfo->frameSize * mStreamInfo->numChannels; } int32_t ns = outputDelayRingBufferGetSamples(nullptr, avail); if (ns != avail) { ALOGW("not a complete frame of samples available"); break; } } mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos; return C2_OK; } void C2SoftAacDec::drainDecoder() { // flush decoder until outputDelay is compensated while (mOutputDelayCompensated > 0) { // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT]; // run DRC check mDrcWrap.submitStreamData(mStreamInfo); mDrcWrap.update(); AAC_DECODER_ERROR decoderErr = aacDecoder_DecodeFrame(mAACDecoder, tmpOutBuffer, 2048 * MAX_CHANNEL_COUNT, AACDEC_FLUSH); if (decoderErr != AAC_DEC_OK) { ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr); } int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels; if (tmpOutBufferSamples > mOutputDelayCompensated) { tmpOutBufferSamples = mOutputDelayCompensated; } outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples); mOutputDelayCompensated -= tmpOutBufferSamples; } } class C2SoftAacDecFactory : public C2ComponentFactory { public: C2SoftAacDecFactory() : mHelper(std::static_pointer_cast( GetCodec2PlatformComponentStore()->getParamReflector())) { } virtual c2_status_t createComponent( c2_node_id_t id, std::shared_ptr* const component, std::function deleter) override { *component = std::shared_ptr( new C2SoftAacDec(COMPONENT_NAME, id, std::make_shared(mHelper)), deleter); return C2_OK; } virtual c2_status_t createInterface( c2_node_id_t id, std::shared_ptr* const interface, std::function deleter) override { *interface = std::shared_ptr( new SimpleInterface( COMPONENT_NAME, id, std::make_shared(mHelper)), deleter); return C2_OK; } virtual ~C2SoftAacDecFactory() override = default; private: std::shared_ptr mHelper; }; } // namespace android __attribute__((cfi_canonical_jump_table)) extern "C" ::C2ComponentFactory* CreateCodec2Factory() { ALOGV("in %s", __func__); return new ::android::C2SoftAacDecFactory(); } __attribute__((cfi_canonical_jump_table)) extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) { ALOGV("in %s", __func__); delete factory; }