summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp')
-rw-r--r--media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp234
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