am 4edf85ab: Merge "Support for extracting G.711 a-law and mu-law audio from WAV files and a corresponding software decoder." into gingerbread

Merge commit '4edf85abc919b7d0e8db71c0c6166c7994269830' into gingerbread-plus-aosp

* commit '4edf85abc919b7d0e8db71c0c6166c7994269830':
  Support for extracting G.711 a-law and mu-law audio from WAV files and a corresponding software decoder.
This commit is contained in:
Andreas Huber
2010-08-09 19:53:01 -07:00
committed by Android Git Automerger
10 changed files with 374 additions and 41 deletions

View File

@@ -34,6 +34,8 @@ extern const char *MEDIA_MIMETYPE_AUDIO_MPEG;
extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW;
extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW;
extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;

View File

@@ -79,6 +79,7 @@ LOCAL_STATIC_LIBRARIES := \
libstagefright_httplive \
libstagefright_rtsp \
libstagefright_id3 \
libstagefright_g711dec \
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \

View File

@@ -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";

View File

@@ -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<MediaSource> 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" },

View File

@@ -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> &dataSource,
const sp<MetaData> &meta,
uint16_t waveFormat,
int32_t bitsPerSample,
off_t offset, size_t size);
@@ -63,6 +68,7 @@ private:
sp<DataSource> mDataSource;
sp<MetaData> mMeta;
uint16_t mWaveFormat;
int32_t mSampleRate;
int32_t mNumChannels;
int32_t mBitsPerSample;
@@ -108,7 +114,7 @@ sp<MediaSource> WAVExtractor::getTrack(size_t index) {
return new WAVSource(
mDataSource, mTrackMeta,
mBitsPerSample, mDataOffset, mDataSize);
mWaveFormat, mBitsPerSample, mDataOffset, mDataSize);
}
sp<MetaData> 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> &dataSource,
const sp<MetaData> &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->set_range(buffer->range_offset(), 2 * numSamples);
}
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->set_range(buffer->range_offset(), 2 * numSamples);
}
size_t bytesPerSample = mBitsPerSample >> 3;

View File

@@ -0,0 +1,4 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -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)

View File

@@ -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 <utils/Log.h>
#include "G711Decoder.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>
static const size_t kMaxNumSamplesPerFrame = 16384;
namespace android {
G711Decoder::G711Decoder(const sp<MediaSource> &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<MetaData> G711Decoder::getFormat() {
sp<MetaData> srcFormat = mSource->getFormat();
int32_t numChannels;
int32_t sampleRate;
CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels));
CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
sp<MetaData> 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<int16_t *>(outBuffer->data()),
inputPtr, inBuffer->range_length());
} else {
DecodeALaw(
static_cast<int16_t *>(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

View File

@@ -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 <media/stagefright/MediaSource.h>
namespace android {
struct MediaBufferGroup;
struct G711Decoder : public MediaSource {
G711Decoder(const sp<MediaSource> &source);
virtual status_t start(MetaData *params);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options);
protected:
virtual ~G711Decoder();
private:
sp<MediaSource> 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_

View File

@@ -43,6 +43,7 @@ private:
sp<DataSource> mDataSource;
status_t mInitCheck;
bool mValidFormat;
uint16_t mWaveFormat;
uint16_t mNumChannels;
uint32_t mSampleRate;
uint16_t mBitsPerSample;