From 520b2a7039792f03da11a8d54344f10175cebfbc Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 9 Aug 2010 09:54:59 -0700 Subject: Support for extracting G.711 a-law and mu-law audio from WAV files and a corresponding software decoder. Change-Id: I92685d09456c220b8c09842defb721bd55b0b9f6 related-to-bug: 2900021 --- media/libstagefright/Android.mk | 1 + media/libstagefright/MediaDefs.cpp | 2 + media/libstagefright/OMXCodec.cpp | 5 + media/libstagefright/WAVExtractor.cpp | 116 +++++++---- media/libstagefright/codecs/g711/Android.mk | 4 + media/libstagefright/codecs/g711/dec/Android.mk | 12 ++ .../libstagefright/codecs/g711/dec/G711Decoder.cpp | 213 +++++++++++++++++++++ media/libstagefright/include/G711Decoder.h | 57 ++++++ media/libstagefright/include/WAVExtractor.h | 1 + 9 files changed, 371 insertions(+), 40 deletions(-) create mode 100644 media/libstagefright/codecs/g711/Android.mk create mode 100644 media/libstagefright/codecs/g711/dec/Android.mk create mode 100644 media/libstagefright/codecs/g711/dec/G711Decoder.cpp create mode 100644 media/libstagefright/include/G711Decoder.h (limited to 'media') diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index fb85287..b8b2f3f 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -79,6 +79,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_httplive \ libstagefright_rtsp \ libstagefright_id3 \ + libstagefright_g711dec \ LOCAL_SHARED_LIBRARIES += \ libstagefright_amrnb_common \ diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 39d264c..7648d42 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -32,6 +32,8 @@ const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg"; const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp"; const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis"; +const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw"; +const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw"; const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4"; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 4f3bffd..11396ef 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -26,6 +26,7 @@ #include "include/AMRWBEncoder.h" #include "include/AVCDecoder.h" #include "include/AVCEncoder.h" +#include "include/G711Decoder.h" #include "include/M4vH263Decoder.h" #include "include/M4vH263Encoder.h" #include "include/MP3Decoder.h" @@ -77,6 +78,7 @@ FACTORY_CREATE(AMRNBDecoder) FACTORY_CREATE(AMRWBDecoder) FACTORY_CREATE(AACDecoder) FACTORY_CREATE(AVCDecoder) +FACTORY_CREATE(G711Decoder) FACTORY_CREATE(M4vH263Decoder) FACTORY_CREATE(VorbisDecoder) FACTORY_CREATE(VPXDecoder) @@ -124,6 +126,7 @@ static sp InstantiateSoftwareCodec( FACTORY_REF(AMRWBDecoder) FACTORY_REF(AACDecoder) FACTORY_REF(AVCDecoder) + FACTORY_REF(G711Decoder) FACTORY_REF(M4vH263Decoder) FACTORY_REF(VorbisDecoder) FACTORY_REF(VPXDecoder) @@ -155,6 +158,8 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" }, { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" }, // { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" }, + { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" }, + { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" }, diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp index 39b1b96..8d820c0 100644 --- a/media/libstagefright/WAVExtractor.cpp +++ b/media/libstagefright/WAVExtractor.cpp @@ -31,7 +31,11 @@ namespace android { -static uint16_t WAVE_FORMAT_PCM = 1; +enum { + WAVE_FORMAT_PCM = 1, + WAVE_FORMAT_ALAW = 6, + WAVE_FORMAT_MULAW = 7, +}; static uint32_t U32_LE_AT(const uint8_t *ptr) { return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0]; @@ -45,6 +49,7 @@ struct WAVSource : public MediaSource { WAVSource( const sp &dataSource, const sp &meta, + uint16_t waveFormat, int32_t bitsPerSample, off_t offset, size_t size); @@ -63,6 +68,7 @@ private: sp mDataSource; sp mMeta; + uint16_t mWaveFormat; int32_t mSampleRate; int32_t mNumChannels; int32_t mBitsPerSample; @@ -108,7 +114,7 @@ sp WAVExtractor::getTrack(size_t index) { return new WAVSource( mDataSource, mTrackMeta, - mBitsPerSample, mDataOffset, mDataSize); + mWaveFormat, mBitsPerSample, mDataOffset, mDataSize); } sp WAVExtractor::getTrackMetaData( @@ -160,8 +166,10 @@ status_t WAVExtractor::init() { return NO_INIT; } - uint16_t format = U16_LE_AT(formatSpec); - if (format != WAVE_FORMAT_PCM) { + mWaveFormat = U16_LE_AT(formatSpec); + if (mWaveFormat != WAVE_FORMAT_PCM + && mWaveFormat != WAVE_FORMAT_ALAW + && mWaveFormat != WAVE_FORMAT_MULAW) { return ERROR_UNSUPPORTED; } @@ -178,9 +186,17 @@ status_t WAVExtractor::init() { mBitsPerSample = U16_LE_AT(&formatSpec[14]); - if (mBitsPerSample != 8 && mBitsPerSample != 16 - && mBitsPerSample != 24) { - return ERROR_UNSUPPORTED; + if (mWaveFormat == WAVE_FORMAT_PCM) { + if (mBitsPerSample != 8 && mBitsPerSample != 16 + && mBitsPerSample != 24) { + return ERROR_UNSUPPORTED; + } + } else { + CHECK(mWaveFormat == WAVE_FORMAT_MULAW + || mWaveFormat == WAVE_FORMAT_ALAW); + if (mBitsPerSample != 8) { + return ERROR_UNSUPPORTED; + } } mValidFormat = true; @@ -190,7 +206,23 @@ status_t WAVExtractor::init() { mDataSize = chunkSize; mTrackMeta = new MetaData; - mTrackMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + + switch (mWaveFormat) { + case WAVE_FORMAT_PCM: + mTrackMeta->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + break; + case WAVE_FORMAT_ALAW: + mTrackMeta->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW); + break; + default: + CHECK_EQ(mWaveFormat, WAVE_FORMAT_MULAW); + mTrackMeta->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW); + break; + } + mTrackMeta->setInt32(kKeyChannelCount, mNumChannels); mTrackMeta->setInt32(kKeySampleRate, mSampleRate); @@ -217,10 +249,12 @@ const size_t WAVSource::kMaxFrameSize = 32768; WAVSource::WAVSource( const sp &dataSource, const sp &meta, + uint16_t waveFormat, int32_t bitsPerSample, off_t offset, size_t size) : mDataSource(dataSource), mMeta(meta), + mWaveFormat(waveFormat), mSampleRate(0), mNumChannels(0), mBitsPerSample(bitsPerSample), @@ -312,43 +346,45 @@ status_t WAVSource::read( buffer->set_range(0, n); - if (mBitsPerSample == 8) { - // Convert 8-bit unsigned samples to 16-bit signed. + if (mWaveFormat == WAVE_FORMAT_PCM) { + if (mBitsPerSample == 8) { + // Convert 8-bit unsigned samples to 16-bit signed. - MediaBuffer *tmp; - CHECK_EQ(mGroup->acquire_buffer(&tmp), OK); + MediaBuffer *tmp; + CHECK_EQ(mGroup->acquire_buffer(&tmp), OK); - // The new buffer holds the sample number of samples, but each - // one is 2 bytes wide. - tmp->set_range(0, 2 * n); + // The new buffer holds the sample number of samples, but each + // one is 2 bytes wide. + tmp->set_range(0, 2 * n); - int16_t *dst = (int16_t *)tmp->data(); - const uint8_t *src = (const uint8_t *)buffer->data(); - while (n-- > 0) { - *dst++ = ((int16_t)(*src) - 128) * 256; - ++src; - } + int16_t *dst = (int16_t *)tmp->data(); + const uint8_t *src = (const uint8_t *)buffer->data(); + while (n-- > 0) { + *dst++ = ((int16_t)(*src) - 128) * 256; + ++src; + } - buffer->release(); - buffer = tmp; - } else if (mBitsPerSample == 24) { - // Convert 24-bit signed samples to 16-bit signed. - - const uint8_t *src = - (const uint8_t *)buffer->data() + buffer->range_offset(); - int16_t *dst = (int16_t *)src; - - size_t numSamples = buffer->range_length() / 3; - for (size_t i = 0; i < numSamples; ++i) { - int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16); - x = (x << 8) >> 8; // sign extension - - x = x >> 8; - *dst++ = (int16_t)x; - src += 3; - } + buffer->release(); + buffer = tmp; + } else if (mBitsPerSample == 24) { + // Convert 24-bit signed samples to 16-bit signed. + + const uint8_t *src = + (const uint8_t *)buffer->data() + buffer->range_offset(); + int16_t *dst = (int16_t *)src; + + size_t numSamples = buffer->range_length() / 3; + for (size_t i = 0; i < numSamples; ++i) { + int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16); + x = (x << 8) >> 8; // sign extension - buffer->set_range(buffer->range_offset(), 2 * numSamples); + x = x >> 8; + *dst++ = (int16_t)x; + src += 3; + } + + buffer->set_range(buffer->range_offset(), 2 * numSamples); + } } size_t bytesPerSample = mBitsPerSample >> 3; diff --git a/media/libstagefright/codecs/g711/Android.mk b/media/libstagefright/codecs/g711/Android.mk new file mode 100644 index 0000000..2e43120 --- /dev/null +++ b/media/libstagefright/codecs/g711/Android.mk @@ -0,0 +1,4 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk new file mode 100644 index 0000000..cfb9fe4 --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + G711Decoder.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + +LOCAL_MODULE := libstagefright_g711dec + +include $(BUILD_STATIC_LIBRARY) diff --git a/media/libstagefright/codecs/g711/dec/G711Decoder.cpp b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp new file mode 100644 index 0000000..4414e4e --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp @@ -0,0 +1,213 @@ +/* + * 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 "G711Decoder" +#include + +#include "G711Decoder.h" + +#include +#include +#include +#include +#include + +static const size_t kMaxNumSamplesPerFrame = 16384; + +namespace android { + +G711Decoder::G711Decoder(const sp &source) + : mSource(source), + mStarted(false), + mBufferGroup(NULL) { +} + +G711Decoder::~G711Decoder() { + if (mStarted) { + stop(); + } +} + +status_t G711Decoder::start(MetaData *params) { + CHECK(!mStarted); + + const char *mime; + CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); + + mIsMLaw = false; + if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) { + mIsMLaw = true; + } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)) { + return ERROR_UNSUPPORTED; + } + + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer( + new MediaBuffer(kMaxNumSamplesPerFrame * sizeof(int16_t))); + + mSource->start(); + + mStarted = true; + + return OK; +} + +status_t G711Decoder::stop() { + CHECK(mStarted); + + delete mBufferGroup; + mBufferGroup = NULL; + + mSource->stop(); + + mStarted = false; + + return OK; +} + +sp G711Decoder::getFormat() { + sp srcFormat = mSource->getFormat(); + + int32_t numChannels; + int32_t sampleRate; + + CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels)); + CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); + + sp meta = new MetaData; + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + meta->setInt32(kKeyChannelCount, numChannels); + meta->setInt32(kKeySampleRate, sampleRate); + + int64_t durationUs; + if (srcFormat->findInt64(kKeyDuration, &durationUs)) { + meta->setInt64(kKeyDuration, durationUs); + } + + meta->setCString(kKeyDecoderComponent, "G711Decoder"); + + return meta; +} + +status_t G711Decoder::read( + MediaBuffer **out, const ReadOptions *options) { + status_t err; + + *out = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + CHECK(seekTimeUs >= 0); + } else { + seekTimeUs = -1; + } + + MediaBuffer *inBuffer; + err = mSource->read(&inBuffer, options); + + if (err != OK) { + return err; + } + + if (inBuffer->range_length() > kMaxNumSamplesPerFrame) { + LOGE("input buffer too large (%d).", inBuffer->range_length()); + + inBuffer->release(); + inBuffer = NULL; + + return ERROR_UNSUPPORTED; + } + + int64_t timeUs; + CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); + + const uint8_t *inputPtr = + (const uint8_t *)inBuffer->data() + inBuffer->range_offset(); + + MediaBuffer *outBuffer; + CHECK_EQ(mBufferGroup->acquire_buffer(&outBuffer), OK); + + if (mIsMLaw) { + DecodeMLaw( + static_cast(outBuffer->data()), + inputPtr, inBuffer->range_length()); + } else { + DecodeALaw( + static_cast(outBuffer->data()), + inputPtr, inBuffer->range_length()); + } + + // Each 8-bit byte is converted into a 16-bit sample. + outBuffer->set_range(0, inBuffer->range_length() * 2); + + outBuffer->meta_data()->setInt64(kKeyTime, timeUs); + + inBuffer->release(); + inBuffer = NULL; + + *out = outBuffer; + + return OK; +} + +// static +void G711Decoder::DecodeALaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize-- > 0) { + int32_t x = *in++; + + int32_t ix = x ^ 0x55; + ix &= 0x7f; + + int32_t iexp = ix >> 4; + int32_t mant = ix & 0x0f; + + if (iexp > 0) { + mant += 16; + } + + mant = (mant << 4) + 8; + + if (iexp > 1) { + mant = mant << (iexp - 1); + } + + *out++ = (x > 127) ? mant : -mant; + } +} + +// static +void G711Decoder::DecodeMLaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize-- > 0) { + int32_t x = *in++; + + int32_t mantissa = ~x; + int32_t exponent = (mantissa >> 4) & 7; + int32_t segment = exponent + 1; + mantissa &= 0x0f; + + int32_t step = 4 << segment; + + int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33; + + *out++ = (x < 0x80) ? -abs : abs; + } +} + +} // namespace android diff --git a/media/libstagefright/include/G711Decoder.h b/media/libstagefright/include/G711Decoder.h new file mode 100644 index 0000000..8b5143a --- /dev/null +++ b/media/libstagefright/include/G711Decoder.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef G711_DECODER_H_ + +#define G711_DECODER_H_ + +#include + +namespace android { + +struct MediaBufferGroup; + +struct G711Decoder : public MediaSource { + G711Decoder(const sp &source); + + virtual status_t start(MetaData *params); + virtual status_t stop(); + + virtual sp getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options); + +protected: + virtual ~G711Decoder(); + +private: + sp mSource; + bool mStarted; + bool mIsMLaw; + + MediaBufferGroup *mBufferGroup; + + static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize); + static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize); + + G711Decoder(const G711Decoder &); + G711Decoder &operator=(const G711Decoder &); +}; + +} // namespace android + +#endif // G711_DECODER_H_ diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h index 9384942..3e847b9 100644 --- a/media/libstagefright/include/WAVExtractor.h +++ b/media/libstagefright/include/WAVExtractor.h @@ -43,6 +43,7 @@ private: sp mDataSource; status_t mInitCheck; bool mValidFormat; + uint16_t mWaveFormat; uint16_t mNumChannels; uint32_t mSampleRate; uint16_t mBitsPerSample; -- cgit v1.1