diff options
author | Dave Burke <daveburke@google.com> | 2012-04-19 00:14:27 -0700 |
---|---|---|
committer | Dave Burke <daveburke@google.com> | 2012-04-25 22:02:58 -0700 |
commit | aeb8fd460ed87d032b3fb8bb61e21eb542ce0f5b (patch) | |
tree | f8b459d895acef1717c96cb7133675dda3e458b7 | |
parent | e0fa467e1150c65a7b1b1ed904c579b40f97c9df (diff) |
Add Fraunhofer AAC encoder with AAC-ELD support.
Change-Id: I6cd499d257d72f50a5b508bed97796a591a51506
-rw-r--r-- | include/media/mediarecorder.h | 1 | ||||
-rw-r--r-- | include/media/stagefright/AACWriter.h | 1 | ||||
-rw-r--r-- | include/media/stagefright/ACodec.h | 2 | ||||
-rw-r--r-- | include/media/stagefright/MetaData.h | 1 | ||||
-rw-r--r-- | include/media/stagefright/OMXCodec.h | 2 | ||||
-rw-r--r-- | media/libmedia/MediaProfiles.cpp | 7 | ||||
-rw-r--r-- | media/libmediaplayerservice/StagefrightRecorder.cpp | 10 | ||||
-rw-r--r-- | media/libstagefright/AACWriter.cpp | 7 | ||||
-rw-r--r-- | media/libstagefright/ACodec.cpp | 13 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 22 | ||||
-rwxr-xr-x | media/libstagefright/OMXCodec.cpp | 12 | ||||
-rw-r--r-- | media/libstagefright/codecs/aacenc/Android.mk | 65 | ||||
-rw-r--r-- | media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp | 557 | ||||
-rw-r--r-- | media/libstagefright/codecs/aacenc/SoftAACEncoder2.h | 77 |
14 files changed, 732 insertions, 45 deletions
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index 30db6424..3891809f 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -77,6 +77,7 @@ enum audio_encoder { AUDIO_ENCODER_AAC = 3, AUDIO_ENCODER_AAC_PLUS = 4, AUDIO_ENCODER_EAAC_PLUS = 5, + AUDIO_ENCODER_AAC_ELD = 6, AUDIO_ENCODER_LIST_END // must be the last - used to validate the audio encoder type }; diff --git a/include/media/stagefright/AACWriter.h b/include/media/stagefright/AACWriter.h index 49397ee2..df1b0536 100644 --- a/include/media/stagefright/AACWriter.h +++ b/include/media/stagefright/AACWriter.h @@ -59,6 +59,7 @@ private: int64_t mEstimatedDurationUs; int32_t mChannelCount; int32_t mSampleRate; + int32_t mAACProfile; int32_t mFrameDurationUs; static void *ThreadWrapper(void *); diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index 7d7af63f..b8d925ef 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -182,7 +182,7 @@ private: status_t setupAACCodec( bool encoder, int32_t numChannels, int32_t sampleRate, int32_t bitRate, - bool isADTS); + int32_t aacProfile, bool isADTS); status_t selectAudioPortFormat( OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat); diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 8a87d83c..3c25a14d 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -49,6 +49,7 @@ enum { kKeyFrameRate = 'frmR', // int32_t (video frame rate fps) kKeyBitRate = 'brte', // int32_t (bps) kKeyESDS = 'esds', // raw data + kKeyAACProfile = 'aacp', // int32_t kKeyAVCC = 'avcc', // raw data kKeyD263 = 'd263', // raw data kKeyVorbisInfo = 'vinf', // raw data diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 055da5db..887ce5de 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -243,7 +243,7 @@ private: status_t setAACFormat( int32_t numChannels, int32_t sampleRate, int32_t bitRate, - bool isADTS); + int32_t aacProfile, bool isADTS); void setG711Format(int32_t numChannels); diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index c224f066..c08f033e 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -41,9 +41,10 @@ const MediaProfiles::NameToTagMap MediaProfiles::sVideoEncoderNameMap[] = { }; const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = { - {"amrnb", AUDIO_ENCODER_AMR_NB}, - {"amrwb", AUDIO_ENCODER_AMR_WB}, - {"aac", AUDIO_ENCODER_AAC}, + {"amrnb", AUDIO_ENCODER_AMR_NB}, + {"amrwb", AUDIO_ENCODER_AMR_WB}, + {"aac", AUDIO_ENCODER_AAC}, + {"aaceld", AUDIO_ENCODER_AAC_ELD}, }; const MediaProfiles::NameToTagMap MediaProfiles::sFileFormatMap[] = { diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 2c5644f6..b676cc7f 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -24,6 +24,7 @@ #include <binder/IServiceManager.h> #include <media/IMediaPlayerService.h> +#include <media/openmax/OMX_Audio.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/AudioSource.h> #include <media/stagefright/AMRWriter.h> @@ -817,6 +818,11 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { break; case AUDIO_ENCODER_AAC: mime = MEDIA_MIMETYPE_AUDIO_AAC; + encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectLC); + break; + case AUDIO_ENCODER_AAC_ELD: + mime = MEDIA_MIMETYPE_AUDIO_AAC; + encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectELD); break; default: ALOGE("Unknown audio encoder: %d", mAudioEncoder); @@ -852,7 +858,8 @@ status_t StagefrightRecorder::startAACRecording() { // Add support for OUTPUT_FORMAT_AAC_ADIF CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_AAC_ADTS); - CHECK_EQ(mAudioEncoder, AUDIO_ENCODER_AAC); + CHECK(mAudioEncoder == AUDIO_ENCODER_AAC || + mAudioEncoder == AUDIO_ENCODER_AAC_ELD); CHECK(mAudioSource != AUDIO_SOURCE_CNT); mWriter = new AACWriter(mOutputFd); @@ -1435,6 +1442,7 @@ status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) { case AUDIO_ENCODER_AMR_NB: case AUDIO_ENCODER_AMR_WB: case AUDIO_ENCODER_AAC: + case AUDIO_ENCODER_AAC_ELD: break; default: diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp index 9cdb4636..21c5428f 100644 --- a/media/libstagefright/AACWriter.cpp +++ b/media/libstagefright/AACWriter.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "AACWriter" #include <utils/Log.h> +#include <media/openmax/OMX_Audio.h> #include <media/stagefright/AACWriter.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -38,7 +39,8 @@ AACWriter::AACWriter(const char *filename) mPaused(false), mResumed(false), mChannelCount(-1), - mSampleRate(-1) { + mSampleRate(-1), + mAACProfile(OMX_AUDIO_AACObjectLC) { ALOGV("AACWriter Constructor"); @@ -96,6 +98,7 @@ status_t AACWriter::addSource(const sp<MediaSource> &source) { CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)); CHECK(meta->findInt32(kKeyChannelCount, &mChannelCount)); CHECK(meta->findInt32(kKeySampleRate, &mSampleRate)); + CHECK(meta->findInt32(kKeyAACProfile, &mAACProfile)); CHECK(mChannelCount >= 1 && mChannelCount <= 2); mSource = source; @@ -254,7 +257,7 @@ status_t AACWriter::writeAdtsHeader(uint32_t frameLength) { data |= kProtectionAbsense; write(mFd, &data, 1); - const uint8_t kProfileCode = 1; // AAC-LC + const uint8_t kProfileCode = mAACProfile - 1; uint8_t kSampleFreqIndex; CHECK(getSampleRateTableIndex(mSampleRate, &kSampleFreqIndex)); const uint8_t kPrivateStream = 0; diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 5ac34c9e..c303146f 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -852,13 +852,16 @@ status_t ACodec::configureCodec( || !msg->findInt32("sample-rate", &sampleRate)) { err = INVALID_OPERATION; } else { - int32_t isADTS; + int32_t isADTS, aacProfile; if (!msg->findInt32("is-adts", &isADTS)) { isADTS = 0; } + if (!msg->findInt32("aac-profile", &aacProfile)) { + aacProfile = OMX_AUDIO_AACObjectNull; + } err = setupAACCodec( - encoder, numChannels, sampleRate, bitRate, isADTS != 0); + encoder, numChannels, sampleRate, bitRate, aacProfile, isADTS != 0); } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { err = setupAMRCodec(encoder, false /* isWAMR */, bitRate); @@ -960,8 +963,8 @@ status_t ACodec::selectAudioPortFormat( } status_t ACodec::setupAACCodec( - bool encoder, - int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) { + bool encoder, int32_t numChannels, int32_t sampleRate, + int32_t bitRate, int32_t aacProfile, bool isADTS) { if (encoder && isADTS) { return -EINVAL; } @@ -1026,7 +1029,7 @@ status_t ACodec::setupAACCodec( profile.nFrameLength = 0; profile.nAACtools = OMX_AUDIO_AACToolAll; profile.nAACERtools = OMX_AUDIO_AACERNone; - profile.eAACProfile = OMX_AUDIO_AACObjectLC; + profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE) aacProfile; profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; err = mOMX->setParameter( diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 9385b8af..a5725417 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -28,6 +28,7 @@ #include <stdlib.h> #include <string.h> +#include <media/stagefright/foundation/ABitReader.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/DataSource.h> @@ -1824,26 +1825,23 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( return ERROR_MALFORMED; } - uint32_t objectType = csd[0] >> 3; + ABitReader br(csd, csd_size); + uint32_t objectType = br.getBits(5); - if (objectType == 31) { - return ERROR_UNSUPPORTED; + if (objectType == 31) { // AAC-ELD => additional 6 bits + objectType = 32 + br.getBits(6); } - uint32_t freqIndex = (csd[0] & 7) << 1 | (csd[1] >> 7); + uint32_t freqIndex = br.getBits(4); + int32_t sampleRate = 0; int32_t numChannels = 0; if (freqIndex == 15) { if (csd_size < 5) { return ERROR_MALFORMED; } - - sampleRate = (csd[1] & 0x7f) << 17 - | csd[2] << 9 - | csd[3] << 1 - | (csd[4] >> 7); - - numChannels = (csd[4] >> 3) & 15; + sampleRate = br.getBits(24); + numChannels = br.getBits(4); } else { static uint32_t kSamplingRate[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, @@ -1855,7 +1853,7 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( } sampleRate = kSamplingRate[freqIndex]; - numChannels = (csd[1] >> 3) & 15; + numChannels = br.getBits(4); } if (numChannels == 0) { diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 1d6f9275..245d9412 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -511,16 +511,20 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) { setAMRFormat(true /* isWAMR */, bitRate); } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) { - int32_t numChannels, sampleRate; + int32_t numChannels, sampleRate, aacProfile; CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); + if (!meta->findInt32(kKeyAACProfile, &aacProfile)) { + aacProfile = OMX_AUDIO_AACObjectNull; + } + int32_t isADTS; if (!meta->findInt32(kKeyIsADTS, &isADTS)) { isADTS = false; } - status_t err = setAACFormat(numChannels, sampleRate, bitRate, isADTS); + status_t err = setAACFormat(numChannels, sampleRate, bitRate, aacProfile, isADTS); if (err != OK) { CODEC_LOGE("setAACFormat() failed (err = %d)", err); return err; @@ -3395,7 +3399,7 @@ void OMXCodec::setAMRFormat(bool isWAMR, int32_t bitRate) { } status_t OMXCodec::setAACFormat( - int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) { + int32_t numChannels, int32_t sampleRate, int32_t bitRate, int32_t aacProfile, bool isADTS) { if (numChannels > 2) { ALOGW("Number of channels: (%d) \n", numChannels); } @@ -3453,7 +3457,7 @@ status_t OMXCodec::setAACFormat( profile.nFrameLength = 0; profile.nAACtools = OMX_AUDIO_AACToolAll; profile.nAACERtools = OMX_AUDIO_AACERNone; - profile.eAACProfile = OMX_AUDIO_AACObjectLC; + profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE) aacProfile; profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; err = mOMX->setParameter(mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk index 0ad3f6c9..98e702ec 100644 --- a/media/libstagefright/codecs/aacenc/Android.mk +++ b/media/libstagefright/codecs/aacenc/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) include frameworks/av/media/libstagefright/codecs/common/Config.mk - +AAC_LIBRARY = fraunhofer LOCAL_SRC_FILES := basic_op/basicop2.c basic_op/oper_32b.c @@ -90,24 +90,57 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - SoftAACEncoder.cpp +ifeq ($(AAC_LIBRARY), fraunhofer) -LOCAL_C_INCLUDES := \ - frameworks/av/media/libstagefright/include \ - frameworks/av/media/libstagefright/codecs/common/include \ - frameworks/native/include/media/openmax + include $(CLEAR_VARS) + + LOCAL_SRC_FILES := \ + SoftAACEncoder2.cpp + + LOCAL_C_INCLUDES := \ + frameworks/av/media/libstagefright/include \ + frameworks/native/include/media/openmax \ + external/aac/libAACenc/include \ + external/aac/libFDK/include \ + external/aac/libMpegTPEnc/include \ + external/aac/libSBRenc/include \ + external/aac/libSYS/include + + LOCAL_CFLAGS := + + LOCAL_STATIC_LIBRARIES := \ + libAACenc libMpegTPEnc libSBRenc libFDK libSYS + + LOCAL_SHARED_LIBRARIES := \ + libstagefright_omx libstagefright_foundation libutils + + LOCAL_MODULE := libstagefright_soft_aacenc + LOCAL_MODULE_TAGS := optional + + include $(BUILD_SHARED_LIBRARY) + +else # visualon + + LOCAL_SRC_FILES := \ + SoftAACEncoder.cpp + + LOCAL_C_INCLUDES := \ + frameworks/av/media/libstagefright/include \ + frameworks/av/media/libstagefright/codecs/common/include \ + frameworks/native/include/media/openmax + + LOCAL_CFLAGS := -DOSCL_IMPORT_REF= -LOCAL_CFLAGS := -DOSCL_IMPORT_REF= + LOCAL_STATIC_LIBRARIES := \ + libstagefright_aacenc -LOCAL_STATIC_LIBRARIES := \ - libstagefright_aacenc + LOCAL_SHARED_LIBRARIES := \ + libstagefright_omx libstagefright_foundation libutils \ + libstagefright_enc_common -LOCAL_SHARED_LIBRARIES := \ - libstagefright_omx libstagefright_foundation libutils \ - libstagefright_enc_common + LOCAL_MODULE := libstagefright_soft_aacenc + LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := libstagefright_soft_aacenc -LOCAL_MODULE_TAGS := optional + include $(BUILD_SHARED_LIBRARY) -include $(BUILD_SHARED_LIBRARY) +endif # $(AAC_LIBRARY) diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp new file mode 100644 index 00000000..4947fb25 --- /dev/null +++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2012 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 "SoftAACEncoder2" +#include <utils/Log.h> + +#include "SoftAACEncoder2.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/hexdump.h> + +namespace android { + +template<class T> +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftAACEncoder2::SoftAACEncoder2( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mAACEncoder(NULL), + mNumChannels(1), + mSampleRate(44100), + mBitRate(0), + mAACProfile(OMX_AUDIO_AACObjectLC), + mSentCodecSpecificData(false), + mInputSize(0), + mInputFrame(NULL), + mInputTimeUs(-1ll), + mSawInputEOS(false), + mSignalledError(false) { + initPorts(); + CHECK_EQ(initEncoder(), (status_t)OK); + setAudioParams(); +} + +SoftAACEncoder2::~SoftAACEncoder2() { + aacEncClose(&mAACEncoder); + + delete[] mInputFrame; + mInputFrame = NULL; +} + +void SoftAACEncoder2::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kNumSamplesPerFrame * sizeof(int16_t) * 2; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast<char *>("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast<char *>("audio/aac"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + addPort(def); +} + +status_t SoftAACEncoder2::initEncoder() { + if (AACENC_OK != aacEncOpen(&mAACEncoder, 0, 0)) { + ALOGE("Failed to init AAC encoder"); + return UNKNOWN_ERROR; + } + return OK; +} + +OMX_ERRORTYPE SoftAACEncoder2::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAAC; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (aacParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + aacParams->nBitRate = mBitRate; + aacParams->nAudioBandWidth = 0; + aacParams->nAACtools = 0; + aacParams->nAACERtools = 0; + aacParams->eAACProfile = (OMX_AUDIO_AACPROFILETYPE) mAACProfile; + aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; + aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo; + + aacParams->nChannels = mNumChannels; + aacParams->nSampleRate = mSampleRate; + aacParams->nFrameLength = 0; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = mNumChannels; + pcmParams->nSamplingRate = mSampleRate; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAACEncoder2::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + "audio_encoder.aac", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingAAC)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (aacParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + mBitRate = aacParams->nBitRate; + mNumChannels = aacParams->nChannels; + mSampleRate = aacParams->nSampleRate; + + if (aacParams->eAACProfile != OMX_AUDIO_AACObjectNull) { + mAACProfile = aacParams->eAACProfile; + } + + if (setAudioParams() != OK) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + mNumChannels = pcmParams->nChannels; + mSampleRate = pcmParams->nSamplingRate; + + if (setAudioParams() != OK) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +CHANNEL_MODE getChannelMode(OMX_U32 nChannels) { + CHANNEL_MODE chMode = MODE_INVALID; + switch (nChannels) { + case 1: chMode = MODE_1; break; + case 2: chMode = MODE_2; break; + case 3: chMode = MODE_1_2; break; + case 4: chMode = MODE_1_2_1; break; + case 5: chMode = MODE_1_2_2; break; + case 6: chMode = MODE_1_2_2_1; break; + default: chMode = MODE_INVALID; + } + return chMode; +} + +status_t SoftAACEncoder2::setAudioParams() { + // We call this whenever sample rate, number of channels or bitrate change + // in reponse to setParameter calls. + + ALOGV("setAudioParams: %lu Hz, %lu channels, %lu bps", + mSampleRate, mNumChannels, mBitRate); + + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_AOT, + mAACProfile == OMX_AUDIO_AACObjectELD ? AOT_ER_AAC_ELD : AOT_AAC_LC)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SAMPLERATE, mSampleRate)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_BITRATE, mBitRate)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_CHANNELMODE, + getChannelMode(mNumChannels))) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_TRANSMUX, TT_MP4_RAW)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + + return OK; +} + +void SoftAACEncoder2::onQueueFilled(OMX_U32 portIndex) { + if (mSignalledError) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + if (!mSentCodecSpecificData) { + // The very first thing we want to output is the codec specific + // data. It does not require any input data but we will need an + // output buffer to store it in. + + if (outQueue.empty()) { + return; + } + + if (AACENC_OK != aacEncEncode(mAACEncoder, NULL, NULL, NULL, NULL)) { + ALOGE("Failed to initialize AAC encoder"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + AACENC_InfoStruct encInfo; + if (AACENC_OK != aacEncInfo(mAACEncoder, &encInfo)) { + ALOGE("Failed to get AAC encoder info"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + outHeader->nFilledLen = encInfo.confSize; + outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG; + + uint8_t *out = outHeader->pBuffer + outHeader->nOffset; + memcpy(out, encInfo.confBuf, encInfo.confSize); + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + mSentCodecSpecificData = true; + } + + size_t numBytesPerInputFrame = + mNumChannels * kNumSamplesPerFrame * sizeof(int16_t); + + // BUGBUG: Fraunhofer's decoder chokes on large chunks of AAC-ELD + if (mAACProfile == OMX_AUDIO_AACObjectELD && numBytesPerInputFrame > 512) { + numBytesPerInputFrame = 512; + } + + for (;;) { + // We do the following until we run out of buffers. + + while (mInputSize < numBytesPerInputFrame) { + // As long as there's still input data to be read we + // will drain "kNumSamplesPerFrame * mNumChannels" samples + // into the "mInputFrame" buffer and then encode those + // as a unit into an output buffer. + + if (mSawInputEOS || inQueue.empty()) { + return; + } + + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + const void *inData = inHeader->pBuffer + inHeader->nOffset; + + size_t copy = numBytesPerInputFrame - mInputSize; + if (copy > inHeader->nFilledLen) { + copy = inHeader->nFilledLen; + } + + if (mInputFrame == NULL) { + mInputFrame = new int16_t[kNumSamplesPerFrame * mNumChannels]; + } + + if (mInputSize == 0) { + mInputTimeUs = inHeader->nTimeStamp; + } + + memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy); + mInputSize += copy; + + inHeader->nOffset += copy; + inHeader->nFilledLen -= copy; + + // "Time" on the input buffer has in effect advanced by the + // number of audio frames we just advanced nOffset by. + inHeader->nTimeStamp += + (copy * 1000000ll / mSampleRate) + / (mNumChannels * sizeof(int16_t)); + + if (inHeader->nFilledLen == 0) { + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEOS = true; + + // Pad any remaining data with zeroes. + memset((uint8_t *)mInputFrame + mInputSize, + 0, + numBytesPerInputFrame - mInputSize); + + mInputSize = numBytesPerInputFrame; + } + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + inData = NULL; + inHeader = NULL; + inInfo = NULL; + } + } + + // At this point we have all the input data necessary to encode + // a single frame, all we need is an output buffer to store the result + // in. + + if (outQueue.empty()) { + return; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + uint8_t *outPtr = (uint8_t *)outHeader->pBuffer + outHeader->nOffset; + size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset; + + AACENC_InArgs inargs; + AACENC_OutArgs outargs; + memset(&inargs, 0, sizeof(inargs)); + memset(&outargs, 0, sizeof(outargs)); + inargs.numInSamples = numBytesPerInputFrame / sizeof(int16_t); + + void* inBuffer[] = { (unsigned char *)mInputFrame }; + INT inBufferIds[] = { IN_AUDIO_DATA }; + INT inBufferSize[] = { numBytesPerInputFrame }; + INT inBufferElSize[] = { sizeof(int16_t) }; + + AACENC_BufDesc inBufDesc; + inBufDesc.numBufs = sizeof(inBuffer) / sizeof(void*); + inBufDesc.bufs = (void**)&inBuffer; + inBufDesc.bufferIdentifiers = inBufferIds; + inBufDesc.bufSizes = inBufferSize; + inBufDesc.bufElSizes = inBufferElSize; + + void* outBuffer[] = { outPtr }; + INT outBufferIds[] = { OUT_BITSTREAM_DATA }; + INT outBufferSize[] = { 0 }; + INT outBufferElSize[] = { sizeof(UCHAR) }; + + AACENC_BufDesc outBufDesc; + outBufDesc.numBufs = sizeof(outBuffer) / sizeof(void*); + outBufDesc.bufs = (void**)&outBuffer; + outBufDesc.bufferIdentifiers = outBufferIds; + outBufDesc.bufSizes = outBufferSize; + outBufDesc.bufElSizes = outBufferElSize; + + // Encode the mInputFrame, which is treated as a modulo buffer + AACENC_ERROR encoderErr = AACENC_OK; + size_t nOutputBytes = 0; + do { + memset(&outargs, 0, sizeof(outargs)); + + outBuffer[0] = outPtr; + outBufferSize[0] = outAvailable - nOutputBytes; + + encoderErr = aacEncEncode(mAACEncoder, + &inBufDesc, + &outBufDesc, + &inargs, + &outargs); + + if (encoderErr == AACENC_OK) { + outPtr += outargs.numOutBytes; + nOutputBytes += outargs.numOutBytes; + + if (outargs.numInSamples > 0) { + int numRemainingSamples = inargs.numInSamples - outargs.numInSamples; + if (numRemainingSamples > 0) { + memmove(mInputFrame, + &mInputFrame[outargs.numInSamples], + sizeof(int16_t) * numRemainingSamples); + } + inargs.numInSamples -= outargs.numInSamples; + } + } + } while (encoderErr == AACENC_OK && inargs.numInSamples > 0); + + outHeader->nFilledLen = nOutputBytes; + + outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; + + if (mSawInputEOS) { + // We also tag this output buffer with EOS if it corresponds + // to the final input buffer. + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + } + + outHeader->nTimeStamp = mInputTimeUs; + +#if 0 + ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)", + nOutputBytes, mInputTimeUs, outHeader->nFlags); + + hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen); +#endif + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + outHeader = NULL; + outInfo = NULL; + + mInputSize = 0; + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAACEncoder2(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.h b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.h new file mode 100644 index 00000000..2603f4f6 --- /dev/null +++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_AAC_ENCODER_2_H_ + +#define SOFT_AAC_ENCODER_2_H_ + +#include "SimpleSoftOMXComponent.h" + +#include "aacenc_lib.h" + +namespace android { + +struct SoftAACEncoder2 : public SimpleSoftOMXComponent { + SoftAACEncoder2( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAACEncoder2(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + +private: + enum { + kNumBuffers = 4, + kNumSamplesPerFrame = 1024 + }; + + HANDLE_AACENCODER mAACEncoder; + + OMX_U32 mNumChannels; + OMX_U32 mSampleRate; + OMX_U32 mBitRate; + OMX_U32 mAACProfile; + + bool mSentCodecSpecificData; + size_t mInputSize; + int16_t *mInputFrame; + int64_t mInputTimeUs; + + bool mSawInputEOS; + + bool mSignalledError; + + void initPorts(); + status_t initEncoder(); + + status_t setAudioParams(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftAACEncoder2); +}; + +} // namespace android + +#endif // SOFT_AAC_ENCODER_2_H_ |