/*
* 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;
}