diff options
Diffstat (limited to 'media/libstagefright/codecs/aacenc/AACEncoder.cpp')
-rw-r--r-- | media/libstagefright/codecs/aacenc/AACEncoder.cpp | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp new file mode 100644 index 0000000..8b5007e --- /dev/null +++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2010 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 "AACEncoder" +#include <utils/Log.h> + +#include "AACEncoder.h" +#include "voAAC.h" +#include "cmnMemory.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MetaData.h> + +namespace android { + +AACEncoder::AACEncoder(const sp<MediaSource> &source, const sp<MetaData> &meta) + : mSource(source), + mMeta(meta), + mStarted(false), + mBufferGroup(NULL), + mInputBuffer(NULL), + mInputFrame(NULL), + mEncoderHandle(NULL), + mApiHandle(NULL), + mMemOperator(NULL) { +} + +status_t AACEncoder::initCheck() { + CHECK(mApiHandle == NULL && mEncoderHandle == NULL); + CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate)); + CHECK(mMeta->findInt32(kKeyChannelCount, &mChannels)); + CHECK(mChannels <= 2 && mChannels >= 1); + CHECK(mMeta->findInt32(kKeyBitRate, &mBitRate)); + + mApiHandle = new VO_AUDIO_CODECAPI; + CHECK(mApiHandle); + + if (VO_ERR_NONE != voGetAACEncAPI(mApiHandle)) { + ALOGE("Failed to get api handle"); + return UNKNOWN_ERROR; + } + + mMemOperator = new VO_MEM_OPERATOR; + CHECK(mMemOperator != NULL); + mMemOperator->Alloc = cmnMemAlloc; + mMemOperator->Copy = cmnMemCopy; + mMemOperator->Free = cmnMemFree; + mMemOperator->Set = cmnMemSet; + mMemOperator->Check = cmnMemCheck; + + VO_CODEC_INIT_USERDATA userData; + memset(&userData, 0, sizeof(userData)); + userData.memflag = VO_IMF_USERMEMOPERATOR; + userData.memData = (VO_PTR) mMemOperator; + if (VO_ERR_NONE != mApiHandle->Init(&mEncoderHandle, VO_AUDIO_CodingAAC, &userData)) { + ALOGE("Failed to init AAC encoder"); + return UNKNOWN_ERROR; + } + if (OK != setAudioSpecificConfigData()) { + ALOGE("Failed to configure AAC encoder"); + return UNKNOWN_ERROR; + } + + // Configure AAC encoder$ + AACENC_PARAM params; + memset(¶ms, 0, sizeof(params)); + params.sampleRate = mSampleRate; + params.bitRate = mBitRate; + params.nChannels = mChannels; + params.adtsUsed = 0; // We add adts header in the file writer if needed. + if (VO_ERR_NONE != mApiHandle->SetParam(mEncoderHandle, VO_PID_AAC_ENCPARAM, ¶ms)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + + return OK; +} + +static status_t getSampleRateTableIndex(int32_t sampleRate, int32_t &index) { + static const int32_t kSampleRateTable[] = { + 96000, 88200, 64000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, 11025, 8000 + }; + const int32_t tableSize = sizeof(kSampleRateTable) / sizeof(kSampleRateTable[0]); + for (int32_t i = 0; i < tableSize; ++i) { + if (sampleRate == kSampleRateTable[i]) { + index = i; + return OK; + } + } + + ALOGE("Sampling rate %d bps is not supported", sampleRate); + return UNKNOWN_ERROR; +} + +status_t AACEncoder::setAudioSpecificConfigData() { + ALOGV("setAudioSpecificConfigData: %d hz, %d bps, and %d channels", + mSampleRate, mBitRate, mChannels); + + int32_t index = 0; + CHECK_EQ((status_t)OK, getSampleRateTableIndex(mSampleRate, index)); + if (mChannels > 2 || mChannels <= 0) { + ALOGE("Unsupported number of channels(%d)", mChannels); + return UNKNOWN_ERROR; + } + + // OMX_AUDIO_AACObjectLC + mAudioSpecificConfigData[0] = ((0x02 << 3) | (index >> 1)); + mAudioSpecificConfigData[1] = ((index & 0x01) << 7) | (mChannels << 3); + return OK; +} + +AACEncoder::~AACEncoder() { + if (mStarted) { + stop(); + } +} + +status_t AACEncoder::start(MetaData *params) { + if (mStarted) { + ALOGW("Call start() when encoder already started"); + return OK; + } + + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer(new MediaBuffer(2048)); + + CHECK_EQ((status_t)OK, initCheck()); + + mNumInputSamples = 0; + mAnchorTimeUs = 0; + mFrameCount = 0; + + mInputFrame = new int16_t[mChannels * kNumSamplesPerFrame]; + CHECK(mInputFrame != NULL); + + status_t err = mSource->start(params); + if (err != OK) { + ALOGE("AudioSource is not available"); + return err; + } + + mStarted = true; + + return OK; +} + +status_t AACEncoder::stop() { + if (mInputBuffer) { + mInputBuffer->release(); + mInputBuffer = NULL; + } + + delete mBufferGroup; + mBufferGroup = NULL; + + if (mInputFrame) { + delete[] mInputFrame; + mInputFrame = NULL; + } + + if (!mStarted) { + ALOGW("Call stop() when encoder has not started"); + return ERROR_END_OF_STREAM; + } + + mSource->stop(); + if (mEncoderHandle) { + CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle)); + mEncoderHandle = NULL; + } + delete mApiHandle; + mApiHandle = NULL; + + delete mMemOperator; + mMemOperator = NULL; + + mStarted = false; + + return OK; +} + +sp<MetaData> AACEncoder::getFormat() { + sp<MetaData> srcFormat = mSource->getFormat(); + + mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); + + int64_t durationUs; + if (srcFormat->findInt64(kKeyDuration, &durationUs)) { + mMeta->setInt64(kKeyDuration, durationUs); + } + + mMeta->setCString(kKeyDecoderComponent, "AACEncoder"); + + return mMeta; +} + +status_t AACEncoder::read( + MediaBuffer **out, const ReadOptions *options) { + status_t err; + + *out = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &mode)); + + MediaBuffer *buffer; + CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK); + uint8_t *outPtr = (uint8_t *)buffer->data(); + bool readFromSource = false; + int64_t wallClockTimeUs = -1; + + if (mFrameCount == 0) { + memcpy(outPtr, mAudioSpecificConfigData, 2); + buffer->set_range(0, 2); + buffer->meta_data()->setInt32(kKeyIsCodecConfig, true); + *out = buffer; + ++mFrameCount; + return OK; + } else if (mFrameCount == 1) { + buffer->meta_data()->setInt32(kKeyIsCodecConfig, false); + } + + const int32_t nSamples = mChannels * kNumSamplesPerFrame; + while (mNumInputSamples < nSamples) { + if (mInputBuffer == NULL) { + if (mSource->read(&mInputBuffer, options) != OK) { + if (mNumInputSamples == 0) { + buffer->release(); + return ERROR_END_OF_STREAM; + } + memset(&mInputFrame[mNumInputSamples], + 0, + sizeof(int16_t) * (nSamples - mNumInputSamples)); + mNumInputSamples = 0; + break; + } + + size_t align = mInputBuffer->range_length() % sizeof(int16_t); + CHECK_EQ(align, (size_t)0); + + int64_t timeUs; + if (mInputBuffer->meta_data()->findInt64(kKeyDriftTime, &timeUs)) { + wallClockTimeUs = timeUs; + } + if (mInputBuffer->meta_data()->findInt64(kKeyAnchorTime, &timeUs)) { + mAnchorTimeUs = timeUs; + } + readFromSource = true; + } else { + readFromSource = false; + } + size_t copy = (nSamples - mNumInputSamples) * sizeof(int16_t); + + if (copy > mInputBuffer->range_length()) { + copy = mInputBuffer->range_length(); + } + + memcpy(&mInputFrame[mNumInputSamples], + (const uint8_t *) mInputBuffer->data() + + mInputBuffer->range_offset(), + copy); + + mInputBuffer->set_range( + mInputBuffer->range_offset() + copy, + mInputBuffer->range_length() - copy); + + if (mInputBuffer->range_length() == 0) { + mInputBuffer->release(); + mInputBuffer = NULL; + } + mNumInputSamples += copy / sizeof(int16_t); + if (mNumInputSamples >= nSamples) { + mNumInputSamples %= nSamples; + break; + } + } + + VO_CODECBUFFER inputData; + memset(&inputData, 0, sizeof(inputData)); + inputData.Buffer = (unsigned char*) mInputFrame; + inputData.Length = nSamples * sizeof(int16_t); + CHECK(VO_ERR_NONE == mApiHandle->SetInputData(mEncoderHandle,&inputData)); + + VO_CODECBUFFER outputData; + memset(&outputData, 0, sizeof(outputData)); + VO_AUDIO_OUTPUTINFO outputInfo; + memset(&outputInfo, 0, sizeof(outputInfo)); + + VO_U32 ret = VO_ERR_NONE; + size_t nOutputBytes = 0; + do { + outputData.Buffer = outPtr; + outputData.Length = buffer->size() - nOutputBytes; + ret = mApiHandle->GetOutputData(mEncoderHandle, &outputData, &outputInfo); + if (ret == VO_ERR_NONE) { + outPtr += outputData.Length; + nOutputBytes += outputData.Length; + } + } while (ret != VO_ERR_INPUT_BUFFER_SMALL); + buffer->set_range(0, nOutputBytes); + + int64_t mediaTimeUs = + ((mFrameCount - 1) * 1000000LL * kNumSamplesPerFrame) / mSampleRate; + + buffer->meta_data()->setInt64(kKeyTime, mAnchorTimeUs + mediaTimeUs); + if (readFromSource && wallClockTimeUs != -1) { + buffer->meta_data()->setInt64(kKeyDriftTime, mediaTimeUs - wallClockTimeUs); + } + ++mFrameCount; + + *out = buffer; + return OK; +} + +} // namespace android |