Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d49b526d authored by Andreas Huber's avatar Andreas Huber
Browse files

Initial checkin of software AMR NB encoder based on PV source code.

parent 8eac1637
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ enum {
    kKeyHeight            = 'heig',
    kKeyChannelCount      = '#chn',
    kKeySampleRate        = 'srte',
    kKeyBitRate           = 'brte',
    kKeyBitRate           = 'brte',  // int32_t (bps)
    kKeyESDS              = 'esds',  // raw data
    kKeyAVCC              = 'avcc',  // raw data
    kKeyWantsNALFragments = 'NALf',
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
LOCAL_STATIC_LIBRARIES := \
        libstagefright_aacdec \
        libstagefright_amrnbdec \
        libstagefright_amrnbenc \
        libstagefright_amrwbdec \
        libstagefright_avcdec \
        libstagefright_mp3dec
+3 −3
Original line number Diff line number Diff line
@@ -366,7 +366,7 @@ MP3Extractor::MP3Extractor(const sp<DataSource> &source)

        mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
        mMeta->setInt32(kKeySampleRate, sample_rate);
        mMeta->setInt32(kKeyBitRate, bitrate);
        mMeta->setInt32(kKeyBitRate, bitrate * 1000);
        mMeta->setInt32(kKeyChannelCount, num_channels);

        off_t fileSize;
@@ -462,14 +462,14 @@ status_t MP3Source::read(
    if (options != NULL && options->getSeekTo(&seekTimeUs)) {
        int32_t bitrate;
        if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
            // bitrate is in kbits/sec.
            // bitrate is in bits/sec.
            LOGI("no bitrate");

            return ERROR_UNSUPPORTED;
        }

        mCurrentTimeUs = seekTimeUs;
        mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 1000000 * 125;
        mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 8000000;
    }

    MediaBuffer *buffer;
+5 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#if BUILD_WITH_FULL_STAGEFRIGHT
#include "include/AACDecoder.h"
#include "include/AMRNBDecoder.h"
#include "include/AMRNBEncoder.h"
#include "include/AMRWBDecoder.h"
#include "include/AVCDecoder.h"
#include "include/MP3Decoder.h"
@@ -306,6 +307,10 @@ sp<MediaSource> OMXCodec::Create(
                    && (flags & kPreferSoftwareCodecs)) {
            return new AVCDecoder(source);
        }
    } else {
        if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
            return new AMRNBEncoder(source);
        }
    }
#endif

+234 −0
Original line number Diff line number Diff line
/*
 * 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
Loading