The software AAC encoder is now an OMX component.
Yay. Change-Id: I74938a20b4e0a622836ea5184d3761180eb0f5de
This commit is contained in:
@ -38,7 +38,7 @@ static const int32_t kVideoBitRate = 512 * 1024;
|
|||||||
static const int32_t kAudioBitRate = 12200;
|
static const int32_t kAudioBitRate = 12200;
|
||||||
static const int64_t kDurationUs = 10000000LL; // 10 seconds
|
static const int64_t kDurationUs = 10000000LL; // 10 seconds
|
||||||
|
|
||||||
#if 1
|
#if 0
|
||||||
class DummySource : public MediaSource {
|
class DummySource : public MediaSource {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -318,7 +318,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
sp<MetaData> encMeta = new MetaData;
|
sp<MetaData> encMeta = new MetaData;
|
||||||
encMeta->setCString(kKeyMIMEType,
|
encMeta->setCString(kKeyMIMEType,
|
||||||
1 ? MEDIA_MIMETYPE_AUDIO_AMR_WB : MEDIA_MIMETYPE_AUDIO_AAC);
|
0 ? MEDIA_MIMETYPE_AUDIO_AMR_WB : MEDIA_MIMETYPE_AUDIO_AAC);
|
||||||
encMeta->setInt32(kKeySampleRate, kSampleRate);
|
encMeta->setInt32(kKeySampleRate, kSampleRate);
|
||||||
encMeta->setInt32(kKeyChannelCount, kNumChannels);
|
encMeta->setInt32(kKeyChannelCount, kNumChannels);
|
||||||
encMeta->setInt32(kKeyMaxInputSize, 8192);
|
encMeta->setInt32(kKeyMaxInputSize, 8192);
|
||||||
|
@ -172,6 +172,7 @@ private:
|
|||||||
uint32_t mFlags;
|
uint32_t mFlags;
|
||||||
|
|
||||||
bool mIsEncoder;
|
bool mIsEncoder;
|
||||||
|
bool mIsVideo;
|
||||||
char *mMIME;
|
char *mMIME;
|
||||||
char *mComponentName;
|
char *mComponentName;
|
||||||
sp<MetaData> mOutputFormat;
|
sp<MetaData> mOutputFormat;
|
||||||
|
@ -77,7 +77,6 @@ LOCAL_SHARED_LIBRARIES := \
|
|||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := \
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
libstagefright_color_conversion \
|
libstagefright_color_conversion \
|
||||||
libstagefright_aacenc \
|
|
||||||
libstagefright_amrnbenc \
|
libstagefright_amrnbenc \
|
||||||
libstagefright_amrwbenc \
|
libstagefright_amrwbenc \
|
||||||
libstagefright_avcenc \
|
libstagefright_avcenc \
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#define LOG_TAG "OMXCodec"
|
#define LOG_TAG "OMXCodec"
|
||||||
#include <utils/Log.h>
|
#include <utils/Log.h>
|
||||||
|
|
||||||
#include "include/AACEncoder.h"
|
|
||||||
#include "include/AMRNBEncoder.h"
|
#include "include/AMRNBEncoder.h"
|
||||||
#include "include/AMRWBEncoder.h"
|
#include "include/AMRWBEncoder.h"
|
||||||
#include "include/AVCEncoder.h"
|
#include "include/AVCEncoder.h"
|
||||||
@ -73,7 +72,6 @@ static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaDa
|
|||||||
|
|
||||||
FACTORY_CREATE_ENCODER(AMRNBEncoder)
|
FACTORY_CREATE_ENCODER(AMRNBEncoder)
|
||||||
FACTORY_CREATE_ENCODER(AMRWBEncoder)
|
FACTORY_CREATE_ENCODER(AMRWBEncoder)
|
||||||
FACTORY_CREATE_ENCODER(AACEncoder)
|
|
||||||
FACTORY_CREATE_ENCODER(AVCEncoder)
|
FACTORY_CREATE_ENCODER(AVCEncoder)
|
||||||
FACTORY_CREATE_ENCODER(M4vH263Encoder)
|
FACTORY_CREATE_ENCODER(M4vH263Encoder)
|
||||||
|
|
||||||
@ -88,7 +86,6 @@ static sp<MediaSource> InstantiateSoftwareEncoder(
|
|||||||
static const FactoryInfo kFactoryInfo[] = {
|
static const FactoryInfo kFactoryInfo[] = {
|
||||||
FACTORY_REF(AMRNBEncoder)
|
FACTORY_REF(AMRNBEncoder)
|
||||||
FACTORY_REF(AMRWBEncoder)
|
FACTORY_REF(AMRWBEncoder)
|
||||||
FACTORY_REF(AACEncoder)
|
|
||||||
FACTORY_REF(AVCEncoder)
|
FACTORY_REF(AVCEncoder)
|
||||||
FACTORY_REF(M4vH263Encoder)
|
FACTORY_REF(M4vH263Encoder)
|
||||||
};
|
};
|
||||||
@ -153,7 +150,7 @@ static const CodecInfo kEncoderInfo[] = {
|
|||||||
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.encode" },
|
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.encode" },
|
||||||
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBEncoder" },
|
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBEncoder" },
|
||||||
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
|
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
|
||||||
{ MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" },
|
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.encoder" },
|
||||||
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
|
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
|
||||||
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
|
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
|
||||||
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
|
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
|
||||||
@ -1487,6 +1484,7 @@ OMXCodec::OMXCodec(
|
|||||||
mQuirks(quirks),
|
mQuirks(quirks),
|
||||||
mFlags(flags),
|
mFlags(flags),
|
||||||
mIsEncoder(isEncoder),
|
mIsEncoder(isEncoder),
|
||||||
|
mIsVideo(!strncasecmp("video/", mime, 6)),
|
||||||
mMIME(strdup(mime)),
|
mMIME(strdup(mime)),
|
||||||
mComponentName(strdup(componentName)),
|
mComponentName(strdup(componentName)),
|
||||||
mSource(source),
|
mSource(source),
|
||||||
@ -2192,7 +2190,7 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t OMXCodec::retrieveDecodingTimeUs(bool isCodecSpecific) {
|
int64_t OMXCodec::retrieveDecodingTimeUs(bool isCodecSpecific) {
|
||||||
CHECK(mIsEncoder);
|
CHECK(mIsEncoder && mIsVideo);
|
||||||
|
|
||||||
if (mDecodingTimeList.empty()) {
|
if (mDecodingTimeList.empty()) {
|
||||||
CHECK(mSignalledEOS || mNoMoreOutputData);
|
CHECK(mSignalledEOS || mNoMoreOutputData);
|
||||||
@ -2387,7 +2385,7 @@ void OMXCodec::on_message(const omx_message &msg) {
|
|||||||
mNoMoreOutputData = true;
|
mNoMoreOutputData = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mIsEncoder) {
|
if (mIsEncoder && mIsVideo) {
|
||||||
int64_t decodingTimeUs = retrieveDecodingTimeUs(isCodecSpecific);
|
int64_t decodingTimeUs = retrieveDecodingTimeUs(isCodecSpecific);
|
||||||
buffer->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs);
|
buffer->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs);
|
||||||
}
|
}
|
||||||
@ -3249,7 +3247,7 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) {
|
|||||||
int64_t lastBufferTimeUs;
|
int64_t lastBufferTimeUs;
|
||||||
CHECK(srcBuffer->meta_data()->findInt64(kKeyTime, &lastBufferTimeUs));
|
CHECK(srcBuffer->meta_data()->findInt64(kKeyTime, &lastBufferTimeUs));
|
||||||
CHECK(lastBufferTimeUs >= 0);
|
CHECK(lastBufferTimeUs >= 0);
|
||||||
if (mIsEncoder) {
|
if (mIsEncoder && mIsVideo) {
|
||||||
mDecodingTimeList.push_back(lastBufferTimeUs);
|
mDecodingTimeList.push_back(lastBufferTimeUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,3 +85,29 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV7
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
SoftAACEncoder.cpp
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES := \
|
||||||
|
frameworks/base/media/libstagefright/include \
|
||||||
|
frameworks/base/include/media/stagefright/openmax \
|
||||||
|
frameworks/base/media/libstagefright/codecs/common/include \
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
|
||||||
|
|
||||||
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
|
libstagefright_aacenc
|
||||||
|
|
||||||
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
|
libstagefright_omx libstagefright_foundation libutils \
|
||||||
|
libstagefright_enc_common
|
||||||
|
|
||||||
|
LOCAL_MODULE := libstagefright_soft_aacenc
|
||||||
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
560
media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp
Normal file
560
media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp
Normal file
@ -0,0 +1,560 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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 "SoftAACEncoder"
|
||||||
|
#include <utils/Log.h>
|
||||||
|
|
||||||
|
#include "SoftAACEncoder.h"
|
||||||
|
|
||||||
|
#include "voAAC.h"
|
||||||
|
#include "cmnMemory.h"
|
||||||
|
|
||||||
|
#include <media/stagefright/foundation/ADebug.h>
|
||||||
|
#include <media/stagefright/foundation/hexdump.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static void InitOMXParams(T *params) {
|
||||||
|
params->nSize = sizeof(T);
|
||||||
|
params->nVersion.s.nVersionMajor = 1;
|
||||||
|
params->nVersion.s.nVersionMinor = 0;
|
||||||
|
params->nVersion.s.nRevision = 0;
|
||||||
|
params->nVersion.s.nStep = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SoftAACEncoder::SoftAACEncoder(
|
||||||
|
const char *name,
|
||||||
|
const OMX_CALLBACKTYPE *callbacks,
|
||||||
|
OMX_PTR appData,
|
||||||
|
OMX_COMPONENTTYPE **component)
|
||||||
|
: SimpleSoftOMXComponent(name, callbacks, appData, component),
|
||||||
|
mEncoderHandle(NULL),
|
||||||
|
mApiHandle(NULL),
|
||||||
|
mMemOperator(NULL),
|
||||||
|
mNumChannels(1),
|
||||||
|
mSampleRate(44100),
|
||||||
|
mBitRate(0),
|
||||||
|
mSentCodecSpecificData(false),
|
||||||
|
mInputSize(0),
|
||||||
|
mInputFrame(NULL),
|
||||||
|
mInputTimeUs(-1ll),
|
||||||
|
mSawInputEOS(false),
|
||||||
|
mSignalledError(false) {
|
||||||
|
initPorts();
|
||||||
|
CHECK_EQ(initEncoder(), (status_t)OK);
|
||||||
|
|
||||||
|
setAudioParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
SoftAACEncoder::~SoftAACEncoder() {
|
||||||
|
delete[] mInputFrame;
|
||||||
|
mInputFrame = NULL;
|
||||||
|
|
||||||
|
if (mEncoderHandle) {
|
||||||
|
CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
|
||||||
|
mEncoderHandle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete mApiHandle;
|
||||||
|
mApiHandle = NULL;
|
||||||
|
|
||||||
|
delete mMemOperator;
|
||||||
|
mMemOperator = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoftAACEncoder::initPorts() {
|
||||||
|
OMX_PARAM_PORTDEFINITIONTYPE def;
|
||||||
|
InitOMXParams(&def);
|
||||||
|
|
||||||
|
def.nPortIndex = 0;
|
||||||
|
def.eDir = OMX_DirInput;
|
||||||
|
def.nBufferCountMin = kNumBuffers;
|
||||||
|
def.nBufferCountActual = def.nBufferCountMin;
|
||||||
|
def.nBufferSize = kNumSamplesPerFrame * sizeof(int16_t) * 2;
|
||||||
|
def.bEnabled = OMX_TRUE;
|
||||||
|
def.bPopulated = OMX_FALSE;
|
||||||
|
def.eDomain = OMX_PortDomainAudio;
|
||||||
|
def.bBuffersContiguous = OMX_FALSE;
|
||||||
|
def.nBufferAlignment = 1;
|
||||||
|
|
||||||
|
def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
|
||||||
|
def.format.audio.pNativeRender = NULL;
|
||||||
|
def.format.audio.bFlagErrorConcealment = OMX_FALSE;
|
||||||
|
def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
|
||||||
|
|
||||||
|
addPort(def);
|
||||||
|
|
||||||
|
def.nPortIndex = 1;
|
||||||
|
def.eDir = OMX_DirOutput;
|
||||||
|
def.nBufferCountMin = kNumBuffers;
|
||||||
|
def.nBufferCountActual = def.nBufferCountMin;
|
||||||
|
def.nBufferSize = 8192;
|
||||||
|
def.bEnabled = OMX_TRUE;
|
||||||
|
def.bPopulated = OMX_FALSE;
|
||||||
|
def.eDomain = OMX_PortDomainAudio;
|
||||||
|
def.bBuffersContiguous = OMX_FALSE;
|
||||||
|
def.nBufferAlignment = 2;
|
||||||
|
|
||||||
|
def.format.audio.cMIMEType = const_cast<char *>("audio/aac");
|
||||||
|
def.format.audio.pNativeRender = NULL;
|
||||||
|
def.format.audio.bFlagErrorConcealment = OMX_FALSE;
|
||||||
|
def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
|
||||||
|
|
||||||
|
addPort(def);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t SoftAACEncoder::initEncoder() {
|
||||||
|
mApiHandle = new VO_AUDIO_CODECAPI;
|
||||||
|
|
||||||
|
if (VO_ERR_NONE != voGetAACEncAPI(mApiHandle)) {
|
||||||
|
ALOGE("Failed to get api handle");
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mMemOperator = new VO_MEM_OPERATOR;
|
||||||
|
mMemOperator->Alloc = cmnMemAlloc;
|
||||||
|
mMemOperator->Copy = cmnMemCopy;
|
||||||
|
mMemOperator->Free = cmnMemFree;
|
||||||
|
mMemOperator->Set = cmnMemSet;
|
||||||
|
mMemOperator->Check = cmnMemCheck;
|
||||||
|
|
||||||
|
VO_CODEC_INIT_USERDATA userData;
|
||||||
|
memset(&userData, 0, sizeof(userData));
|
||||||
|
userData.memflag = VO_IMF_USERMEMOPERATOR;
|
||||||
|
userData.memData = (VO_PTR) mMemOperator;
|
||||||
|
if (VO_ERR_NONE !=
|
||||||
|
mApiHandle->Init(&mEncoderHandle, VO_AUDIO_CodingAAC, &userData)) {
|
||||||
|
ALOGE("Failed to init AAC encoder");
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
OMX_ERRORTYPE SoftAACEncoder::internalGetParameter(
|
||||||
|
OMX_INDEXTYPE index, OMX_PTR params) {
|
||||||
|
switch (index) {
|
||||||
|
case OMX_IndexParamAudioPortFormat:
|
||||||
|
{
|
||||||
|
OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
|
||||||
|
(OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
|
||||||
|
|
||||||
|
if (formatParams->nPortIndex > 1) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formatParams->nIndex > 0) {
|
||||||
|
return OMX_ErrorNoMore;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatParams->eEncoding =
|
||||||
|
(formatParams->nPortIndex == 0)
|
||||||
|
? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAAC;
|
||||||
|
|
||||||
|
return OMX_ErrorNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OMX_IndexParamAudioAac:
|
||||||
|
{
|
||||||
|
OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
|
||||||
|
(OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
|
||||||
|
|
||||||
|
if (aacParams->nPortIndex != 1) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
aacParams->nBitRate = mBitRate;
|
||||||
|
aacParams->nAudioBandWidth = 0;
|
||||||
|
aacParams->nAACtools = 0;
|
||||||
|
aacParams->nAACERtools = 0;
|
||||||
|
aacParams->eAACProfile = OMX_AUDIO_AACObjectMain;
|
||||||
|
aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
|
||||||
|
aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;
|
||||||
|
|
||||||
|
aacParams->nChannels = mNumChannels;
|
||||||
|
aacParams->nSampleRate = mSampleRate;
|
||||||
|
aacParams->nFrameLength = 0;
|
||||||
|
|
||||||
|
return OMX_ErrorNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OMX_IndexParamAudioPcm:
|
||||||
|
{
|
||||||
|
OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
|
||||||
|
(OMX_AUDIO_PARAM_PCMMODETYPE *)params;
|
||||||
|
|
||||||
|
if (pcmParams->nPortIndex != 0) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pcmParams->eNumData = OMX_NumericalDataSigned;
|
||||||
|
pcmParams->eEndian = OMX_EndianBig;
|
||||||
|
pcmParams->bInterleaved = OMX_TRUE;
|
||||||
|
pcmParams->nBitPerSample = 16;
|
||||||
|
pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
|
||||||
|
pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
|
||||||
|
pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
|
||||||
|
|
||||||
|
pcmParams->nChannels = mNumChannels;
|
||||||
|
pcmParams->nSamplingRate = mSampleRate;
|
||||||
|
|
||||||
|
return OMX_ErrorNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SimpleSoftOMXComponent::internalGetParameter(index, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OMX_ERRORTYPE SoftAACEncoder::internalSetParameter(
|
||||||
|
OMX_INDEXTYPE index, const OMX_PTR params) {
|
||||||
|
switch (index) {
|
||||||
|
case OMX_IndexParamStandardComponentRole:
|
||||||
|
{
|
||||||
|
const OMX_PARAM_COMPONENTROLETYPE *roleParams =
|
||||||
|
(const OMX_PARAM_COMPONENTROLETYPE *)params;
|
||||||
|
|
||||||
|
if (strncmp((const char *)roleParams->cRole,
|
||||||
|
"audio_encoder.aac",
|
||||||
|
OMX_MAX_STRINGNAME_SIZE - 1)) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OMX_ErrorNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OMX_IndexParamAudioPortFormat:
|
||||||
|
{
|
||||||
|
const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
|
||||||
|
(const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
|
||||||
|
|
||||||
|
if (formatParams->nPortIndex > 1) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formatParams->nIndex > 0) {
|
||||||
|
return OMX_ErrorNoMore;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((formatParams->nPortIndex == 0
|
||||||
|
&& formatParams->eEncoding != OMX_AUDIO_CodingPCM)
|
||||||
|
|| (formatParams->nPortIndex == 1
|
||||||
|
&& formatParams->eEncoding != OMX_AUDIO_CodingAAC)) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OMX_ErrorNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OMX_IndexParamAudioAac:
|
||||||
|
{
|
||||||
|
OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
|
||||||
|
(OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
|
||||||
|
|
||||||
|
if (aacParams->nPortIndex != 1) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
mBitRate = aacParams->nBitRate;
|
||||||
|
mNumChannels = aacParams->nChannels;
|
||||||
|
mSampleRate = aacParams->nSampleRate;
|
||||||
|
|
||||||
|
if (setAudioParams() != OK) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OMX_ErrorNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OMX_IndexParamAudioPcm:
|
||||||
|
{
|
||||||
|
OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
|
||||||
|
(OMX_AUDIO_PARAM_PCMMODETYPE *)params;
|
||||||
|
|
||||||
|
if (pcmParams->nPortIndex != 0) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
mNumChannels = pcmParams->nChannels;
|
||||||
|
mSampleRate = pcmParams->nSamplingRate;
|
||||||
|
|
||||||
|
if (setAudioParams() != OK) {
|
||||||
|
return OMX_ErrorUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OMX_ErrorNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SimpleSoftOMXComponent::internalSetParameter(index, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t SoftAACEncoder::setAudioParams() {
|
||||||
|
// We call this whenever sample rate, number of channels or bitrate change
|
||||||
|
// in reponse to setParameter calls.
|
||||||
|
|
||||||
|
ALOGV("setAudioParams: %lu Hz, %lu channels, %lu bps",
|
||||||
|
mSampleRate, mNumChannels, mBitRate);
|
||||||
|
|
||||||
|
status_t err = setAudioSpecificConfigData();
|
||||||
|
|
||||||
|
if (err != OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
AACENC_PARAM params;
|
||||||
|
memset(¶ms, 0, sizeof(params));
|
||||||
|
params.sampleRate = mSampleRate;
|
||||||
|
params.bitRate = mBitRate;
|
||||||
|
params.nChannels = mNumChannels;
|
||||||
|
params.adtsUsed = 0; // We add adts header in the file writer if needed.
|
||||||
|
if (VO_ERR_NONE != mApiHandle->SetParam(
|
||||||
|
mEncoderHandle, VO_PID_AAC_ENCPARAM, ¶ms)) {
|
||||||
|
ALOGE("Failed to set AAC encoder parameters");
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static status_t getSampleRateTableIndex(int32_t sampleRate, int32_t &index) {
|
||||||
|
static const int32_t kSampleRateTable[] = {
|
||||||
|
96000, 88200, 64000, 48000, 44100, 32000,
|
||||||
|
24000, 22050, 16000, 12000, 11025, 8000
|
||||||
|
};
|
||||||
|
const int32_t tableSize =
|
||||||
|
sizeof(kSampleRateTable) / sizeof(kSampleRateTable[0]);
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < tableSize; ++i) {
|
||||||
|
if (sampleRate == kSampleRateTable[i]) {
|
||||||
|
index = i;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t SoftAACEncoder::setAudioSpecificConfigData() {
|
||||||
|
// The AAC encoder's audio specific config really only encodes
|
||||||
|
// number of channels and the sample rate (mapped to an index into
|
||||||
|
// a fixed sample rate table).
|
||||||
|
|
||||||
|
int32_t index;
|
||||||
|
status_t err = getSampleRateTableIndex(mSampleRate, index);
|
||||||
|
if (err != OK) {
|
||||||
|
ALOGE("Unsupported sample rate (%lu Hz)", mSampleRate);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mNumChannels > 2 || mNumChannels <= 0) {
|
||||||
|
ALOGE("Unsupported number of channels(%lu)", mNumChannels);
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OMX_AUDIO_AACObjectLC
|
||||||
|
mAudioSpecificConfigData[0] = ((0x02 << 3) | (index >> 1));
|
||||||
|
mAudioSpecificConfigData[1] = ((index & 0x01) << 7) | (mNumChannels << 3);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoftAACEncoder::onQueueFilled(OMX_U32 portIndex) {
|
||||||
|
if (mSignalledError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<BufferInfo *> &inQueue = getPortQueue(0);
|
||||||
|
List<BufferInfo *> &outQueue = getPortQueue(1);
|
||||||
|
|
||||||
|
if (!mSentCodecSpecificData) {
|
||||||
|
// The very first thing we want to output is the codec specific
|
||||||
|
// data. It does not require any input data but we will need an
|
||||||
|
// output buffer to store it in.
|
||||||
|
|
||||||
|
if (outQueue.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferInfo *outInfo = *outQueue.begin();
|
||||||
|
OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
|
||||||
|
outHeader->nFilledLen = sizeof(mAudioSpecificConfigData);
|
||||||
|
outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG;
|
||||||
|
|
||||||
|
uint8_t *out = outHeader->pBuffer + outHeader->nOffset;
|
||||||
|
memcpy(out, mAudioSpecificConfigData, sizeof(mAudioSpecificConfigData));
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
ALOGI("sending codec specific data.");
|
||||||
|
hexdump(out, sizeof(mAudioSpecificConfigData));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
outQueue.erase(outQueue.begin());
|
||||||
|
outInfo->mOwnedByUs = false;
|
||||||
|
notifyFillBufferDone(outHeader);
|
||||||
|
|
||||||
|
mSentCodecSpecificData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t numBytesPerInputFrame =
|
||||||
|
mNumChannels * kNumSamplesPerFrame * sizeof(int16_t);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
// We do the following until we run out of buffers.
|
||||||
|
|
||||||
|
while (mInputSize < numBytesPerInputFrame) {
|
||||||
|
// As long as there's still input data to be read we
|
||||||
|
// will drain "kNumSamplesPerFrame * mNumChannels" samples
|
||||||
|
// into the "mInputFrame" buffer and then encode those
|
||||||
|
// as a unit into an output buffer.
|
||||||
|
|
||||||
|
if (mSawInputEOS || inQueue.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferInfo *inInfo = *inQueue.begin();
|
||||||
|
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
|
||||||
|
|
||||||
|
const void *inData = inHeader->pBuffer + inHeader->nOffset;
|
||||||
|
|
||||||
|
size_t copy = numBytesPerInputFrame - mInputSize;
|
||||||
|
if (copy > inHeader->nFilledLen) {
|
||||||
|
copy = inHeader->nFilledLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mInputFrame == NULL) {
|
||||||
|
mInputFrame = new int16_t[kNumSamplesPerFrame * mNumChannels];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mInputSize == 0) {
|
||||||
|
mInputTimeUs = inHeader->nTimeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy);
|
||||||
|
mInputSize += copy;
|
||||||
|
|
||||||
|
inHeader->nOffset += copy;
|
||||||
|
inHeader->nFilledLen -= copy;
|
||||||
|
|
||||||
|
// "Time" on the input buffer has in effect advanced by the
|
||||||
|
// number of audio frames we just advanced nOffset by.
|
||||||
|
inHeader->nTimeStamp +=
|
||||||
|
(copy * 1000000ll / mSampleRate)
|
||||||
|
/ (mNumChannels * sizeof(int16_t));
|
||||||
|
|
||||||
|
if (inHeader->nFilledLen == 0) {
|
||||||
|
if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
|
||||||
|
ALOGV("saw input EOS");
|
||||||
|
mSawInputEOS = true;
|
||||||
|
|
||||||
|
// Pad any remaining data with zeroes.
|
||||||
|
memset((uint8_t *)mInputFrame + mInputSize,
|
||||||
|
0,
|
||||||
|
numBytesPerInputFrame - mInputSize);
|
||||||
|
|
||||||
|
mInputSize = numBytesPerInputFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
inQueue.erase(inQueue.begin());
|
||||||
|
inInfo->mOwnedByUs = false;
|
||||||
|
notifyEmptyBufferDone(inHeader);
|
||||||
|
|
||||||
|
inData = NULL;
|
||||||
|
inHeader = NULL;
|
||||||
|
inInfo = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we have all the input data necessary to encode
|
||||||
|
// a single frame, all we need is an output buffer to store the result
|
||||||
|
// in.
|
||||||
|
|
||||||
|
if (outQueue.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferInfo *outInfo = *outQueue.begin();
|
||||||
|
OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
|
||||||
|
|
||||||
|
VO_CODECBUFFER inputData;
|
||||||
|
memset(&inputData, 0, sizeof(inputData));
|
||||||
|
inputData.Buffer = (unsigned char *)mInputFrame;
|
||||||
|
inputData.Length = numBytesPerInputFrame;
|
||||||
|
CHECK(VO_ERR_NONE ==
|
||||||
|
mApiHandle->SetInputData(mEncoderHandle, &inputData));
|
||||||
|
|
||||||
|
VO_CODECBUFFER outputData;
|
||||||
|
memset(&outputData, 0, sizeof(outputData));
|
||||||
|
VO_AUDIO_OUTPUTINFO outputInfo;
|
||||||
|
memset(&outputInfo, 0, sizeof(outputInfo));
|
||||||
|
|
||||||
|
uint8_t *outPtr = (uint8_t *)outHeader->pBuffer + outHeader->nOffset;
|
||||||
|
size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
|
||||||
|
|
||||||
|
VO_U32 ret = VO_ERR_NONE;
|
||||||
|
size_t nOutputBytes = 0;
|
||||||
|
do {
|
||||||
|
outputData.Buffer = outPtr;
|
||||||
|
outputData.Length = outAvailable - nOutputBytes;
|
||||||
|
ret = mApiHandle->GetOutputData(
|
||||||
|
mEncoderHandle, &outputData, &outputInfo);
|
||||||
|
if (ret == VO_ERR_NONE) {
|
||||||
|
outPtr += outputData.Length;
|
||||||
|
nOutputBytes += outputData.Length;
|
||||||
|
}
|
||||||
|
} while (ret != VO_ERR_INPUT_BUFFER_SMALL);
|
||||||
|
|
||||||
|
outHeader->nFilledLen = nOutputBytes;
|
||||||
|
|
||||||
|
outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
|
||||||
|
|
||||||
|
if (mSawInputEOS) {
|
||||||
|
// We also tag this output buffer with EOS if it corresponds
|
||||||
|
// to the final input buffer.
|
||||||
|
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
outHeader->nTimeStamp = mInputTimeUs;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)",
|
||||||
|
nOutputBytes, mInputTimeUs, outHeader->nFlags);
|
||||||
|
|
||||||
|
hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
outQueue.erase(outQueue.begin());
|
||||||
|
outInfo->mOwnedByUs = false;
|
||||||
|
notifyFillBufferDone(outHeader);
|
||||||
|
|
||||||
|
outHeader = NULL;
|
||||||
|
outInfo = NULL;
|
||||||
|
|
||||||
|
mInputSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
android::SoftOMXComponent *createSoftOMXComponent(
|
||||||
|
const char *name, const OMX_CALLBACKTYPE *callbacks,
|
||||||
|
OMX_PTR appData, OMX_COMPONENTTYPE **component) {
|
||||||
|
return new android::SoftAACEncoder(name, callbacks, appData, component);
|
||||||
|
}
|
82
media/libstagefright/codecs/aacenc/SoftAACEncoder.h
Normal file
82
media/libstagefright/codecs/aacenc/SoftAACEncoder.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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 SOFT_AAC_ENCODER_H_
|
||||||
|
|
||||||
|
#define SOFT_AAC_ENCODER_H_
|
||||||
|
|
||||||
|
#include "SimpleSoftOMXComponent.h"
|
||||||
|
|
||||||
|
struct VO_AUDIO_CODECAPI;
|
||||||
|
struct VO_MEM_OPERATOR;
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
struct SoftAACEncoder : public SimpleSoftOMXComponent {
|
||||||
|
SoftAACEncoder(
|
||||||
|
const char *name,
|
||||||
|
const OMX_CALLBACKTYPE *callbacks,
|
||||||
|
OMX_PTR appData,
|
||||||
|
OMX_COMPONENTTYPE **component);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~SoftAACEncoder();
|
||||||
|
|
||||||
|
virtual OMX_ERRORTYPE internalGetParameter(
|
||||||
|
OMX_INDEXTYPE index, OMX_PTR params);
|
||||||
|
|
||||||
|
virtual OMX_ERRORTYPE internalSetParameter(
|
||||||
|
OMX_INDEXTYPE index, const OMX_PTR params);
|
||||||
|
|
||||||
|
virtual void onQueueFilled(OMX_U32 portIndex);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
kNumBuffers = 4,
|
||||||
|
kNumSamplesPerFrame = 1024,
|
||||||
|
};
|
||||||
|
|
||||||
|
void *mEncoderHandle;
|
||||||
|
VO_AUDIO_CODECAPI *mApiHandle;
|
||||||
|
VO_MEM_OPERATOR *mMemOperator;
|
||||||
|
|
||||||
|
OMX_U32 mNumChannels;
|
||||||
|
OMX_U32 mSampleRate;
|
||||||
|
OMX_U32 mBitRate;
|
||||||
|
|
||||||
|
bool mSentCodecSpecificData;
|
||||||
|
size_t mInputSize;
|
||||||
|
int16_t *mInputFrame;
|
||||||
|
int64_t mInputTimeUs;
|
||||||
|
|
||||||
|
bool mSawInputEOS;
|
||||||
|
|
||||||
|
uint8_t mAudioSpecificConfigData[2];
|
||||||
|
|
||||||
|
bool mSignalledError;
|
||||||
|
|
||||||
|
void initPorts();
|
||||||
|
status_t initEncoder();
|
||||||
|
|
||||||
|
status_t setAudioSpecificConfigData();
|
||||||
|
status_t setAudioParams();
|
||||||
|
|
||||||
|
DISALLOW_EVIL_CONSTRUCTORS(SoftAACEncoder);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // SOFT_AAC_ENCODER_H_
|
@ -35,6 +35,7 @@ static const struct {
|
|||||||
|
|
||||||
} kComponents[] = {
|
} kComponents[] = {
|
||||||
{ "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
|
{ "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
|
||||||
|
{ "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },
|
||||||
{ "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
|
{ "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
|
||||||
{ "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
|
{ "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
|
||||||
{ "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" },
|
{ "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" },
|
||||||
|
Reference in New Issue
Block a user