diff options
Diffstat (limited to 'media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp')
-rw-r--r-- | media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp new file mode 100644 index 0000000..b6d7ea3 --- /dev/null +++ b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "AMRNBEncoder.h" + +#include "gsmamr_enc.h" + +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MetaData.h> + +namespace android { + +static const int32_t kNumSamplesPerFrame = 160; +static const int32_t kSampleRate = 8000; + +AMRNBEncoder::AMRNBEncoder(const sp<MediaSource> &source) + : mSource(source), + mStarted(false), + mBufferGroup(NULL), + mEncState(NULL), + mSidState(NULL), + mAnchorTimeUs(0), + mNumFramesOutput(0), + mInputBuffer(NULL), + mMode(MR475), + mNumInputSamples(0) { +} + +AMRNBEncoder::~AMRNBEncoder() { + if (mStarted) { + stop(); + } +} + +static Mode PickModeFromBitrate(int32_t bps) { + if (bps <= 4750) { + return MR475; + } else if (bps <= 5150) { + return MR515; + } else if (bps <= 5900) { + return MR59; + } else if (bps <= 6700) { + return MR67; + } else if (bps <= 7400) { + return MR74; + } else if (bps <= 7950) { + return MR795; + } else if (bps <= 10200) { + return MR102; + } else { + return MR122; + } +} + +status_t AMRNBEncoder::start(MetaData *params) { + CHECK(!mStarted); + + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer(new MediaBuffer(32)); + + CHECK_EQ(AMREncodeInit( + &mEncState, &mSidState, false /* dtx_enable */), + 0); + + mSource->start(); + + mAnchorTimeUs = 0; + mNumFramesOutput = 0; + mStarted = true; + mNumInputSamples = 0; + + int32_t bitrate; + if (params && params->findInt32(kKeyBitRate, &bitrate)) { + mMode = PickModeFromBitrate(bitrate); + } else { + mMode = MR475; + } + + return OK; +} + +status_t AMRNBEncoder::stop() { + CHECK(mStarted); + + if (mInputBuffer) { + mInputBuffer->release(); + mInputBuffer = NULL; + } + + delete mBufferGroup; + mBufferGroup = NULL; + + mSource->stop(); + + AMREncodeExit(&mEncState, &mSidState); + mEncState = mSidState = NULL; + + mStarted = false; + + return OK; +} + +sp<MetaData> AMRNBEncoder::getFormat() { + sp<MetaData> srcFormat = mSource->getFormat(); + + int32_t numChannels; + int32_t sampleRate; + + CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels)); + CHECK_EQ(numChannels, 1); + + CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); + CHECK_EQ(sampleRate, kSampleRate); + + sp<MetaData> meta = new MetaData; + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_NB); + meta->setInt32(kKeyChannelCount, numChannels); + meta->setInt32(kKeySampleRate, sampleRate); + + int64_t durationUs; + if (srcFormat->findInt64(kKeyDuration, &durationUs)) { + meta->setInt64(kKeyDuration, durationUs); + } + + return meta; +} + +status_t AMRNBEncoder::read( + MediaBuffer **out, const ReadOptions *options) { + status_t err; + + *out = NULL; + + int64_t seekTimeUs; + CHECK(options == NULL || !options->getSeekTo(&seekTimeUs)); + + while (mNumInputSamples < kNumSamplesPerFrame) { + if (mInputBuffer == NULL) { + err = mSource->read(&mInputBuffer, options); + + if (err != OK) { + if (mNumInputSamples == 0) { + return ERROR_END_OF_STREAM; + } + memset(&mInputFrame[mNumInputSamples], + 0, + sizeof(int16_t) + * (kNumSamplesPerFrame - mNumInputSamples)); + mNumInputSamples = kNumSamplesPerFrame; + break; + } + + size_t align = mInputBuffer->range_length() % sizeof(int16_t); + CHECK_EQ(align, 0); + + int64_t timeUs; + if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) { + mAnchorTimeUs = timeUs; + mNumFramesOutput = 0; + } + } + + size_t copy = + (kNumSamplesPerFrame - 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); + + mNumInputSamples += copy / sizeof(int16_t); + + mInputBuffer->set_range( + mInputBuffer->range_offset() + copy, + mInputBuffer->range_length() - copy); + + if (mInputBuffer->range_length() == 0) { + mInputBuffer->release(); + mInputBuffer = NULL; + } + } + + MediaBuffer *buffer; + CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK); + + uint8_t *outPtr = (uint8_t *)buffer->data(); + + Frame_Type_3GPP frameType; + int res = AMREncode( + mEncState, mSidState, (Mode)mMode, + mInputFrame, outPtr, &frameType, AMR_TX_WMF); + + CHECK(res >= 0); + CHECK((size_t)res < buffer->size()); + + // Convert header byte from WMF to IETF format. + outPtr[0] = ((outPtr[0] << 3) | 4) & 0x7c; + + buffer->set_range(0, res); + + // Each frame of 160 samples is 20ms long. + buffer->meta_data()->setInt64( + kKeyTime, mAnchorTimeUs + mNumFramesOutput * 20000); + + ++mNumFramesOutput; + + *out = buffer; + + mNumInputSamples = 0; + + return OK; +} + +} // namespace android |