diff options
author | Jean-Michel Trivi <jmtrivi@google.com> | 2012-04-30 10:38:58 -0700 |
---|---|---|
committer | Jean-Michel Trivi <jmtrivi@google.com> | 2012-05-09 18:50:18 -0700 |
commit | 2f74ef3cdc192f817ee1121f41765f0852c1d81e (patch) | |
tree | a088a328c59fc8fba57269e9b0dc899eb5182b35 | |
parent | c150ca7dda844891fa684f6898da7f7e0c40329d (diff) | |
download | frameworks_av-2f74ef3cdc192f817ee1121f41765f0852c1d81e.zip frameworks_av-2f74ef3cdc192f817ee1121f41765f0852c1d81e.tar.gz frameworks_av-2f74ef3cdc192f817ee1121f41765f0852c1d81e.tar.bz2 |
OMX IL wrapper for FLAC encoder
Add wrapper around libFLAC for FLAC encoding in OpenMAX IL.
Declare FLAC encoder in OMX component roles.
Bug 5525503
Change-Id: I19bbce41c216870669d09365693f4ea89f8bc0f7
-rw-r--r-- | include/media/stagefright/ACodec.h | 3 | ||||
-rw-r--r-- | media/libstagefright/ACodec.cpp | 55 | ||||
-rwxr-xr-x | media/libstagefright/OMXCodec.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/codecs/flac/Android.mk | 4 | ||||
-rw-r--r-- | media/libstagefright/codecs/flac/enc/Android.mk | 21 | ||||
-rw-r--r-- | media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp | 444 | ||||
-rw-r--r-- | media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h | 102 | ||||
-rw-r--r-- | media/libstagefright/omx/SoftOMXPlugin.cpp | 1 |
8 files changed, 631 insertions, 1 deletions
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index 7536d10..dd18e95 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -198,6 +198,9 @@ private: status_t setupAMRCodec(bool encoder, bool isWAMR, int32_t bitRate); status_t setupG711Codec(bool encoder, int32_t numChannels); + status_t setupFlacCodec( + bool encoder, int32_t numChannels, int32_t sampleRate, int32_t compressionLevel); + status_t setupRawAudioFormat( OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 1e00c5d..3fd6cef 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -774,6 +774,8 @@ status_t ACodec::setComponentRole( "video_decoder.vpx", "video_encoder.vpx" }, { MEDIA_MIMETYPE_AUDIO_RAW, "audio_decoder.raw", "audio_encoder.raw" }, + { MEDIA_MIMETYPE_AUDIO_FLAC, + "audio_decoder.flac", "audio_encoder.flac" }, }; static const size_t kNumMimeToRole = @@ -834,7 +836,9 @@ status_t ACodec::configureCodec( } int32_t bitRate = 0; - if (encoder && !msg->findInt32("bitrate", &bitRate)) { + // FLAC encoder doesn't need a bitrate, other encoders do + if (encoder && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC) + && !msg->findInt32("bitrate", &bitRate)) { return INVALID_OPERATION; } @@ -882,6 +886,27 @@ status_t ACodec::configureCodec( } else { err = setupG711Codec(encoder, numChannels); } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) { + int32_t numChannels, sampleRate, compressionLevel = -1; + if (encoder && + (!msg->findInt32("channel-count", &numChannels) + || !msg->findInt32("sample-rate", &sampleRate))) { + ALOGE("missing channel count or sample rate for FLAC encoder"); + err = INVALID_OPERATION; + } else { + if (encoder) { + if (!msg->findInt32("flac-compression-level", &compressionLevel)) { + compressionLevel = 5;// default FLAC compression level + } else if (compressionLevel < 0) { + ALOGW("compression level %d outside [0..8] range, using 0", compressionLevel); + compressionLevel = 0; + } else if (compressionLevel > 8) { + ALOGW("compression level %d outside [0..8] range, using 8", compressionLevel); + compressionLevel = 8; + } + } + err = setupFlacCodec(encoder, numChannels, sampleRate, compressionLevel); + } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { int32_t numChannels, sampleRate; if (encoder @@ -1163,6 +1188,34 @@ status_t ACodec::setupG711Codec(bool encoder, int32_t numChannels) { kPortIndexInput, 8000 /* sampleRate */, numChannels); } +status_t ACodec::setupFlacCodec( + bool encoder, int32_t numChannels, int32_t sampleRate, int32_t compressionLevel) { + + if (encoder) { + OMX_AUDIO_PARAM_FLACTYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexOutput; + + // configure compression level + status_t err = mOMX->getParameter(mNode, OMX_IndexParamAudioFlac, &def, sizeof(def)); + if (err != OK) { + ALOGE("setupFlacCodec(): Error %d getting OMX_IndexParamAudioFlac parameter", err); + return err; + } + def.nCompressionLevel = compressionLevel; + err = mOMX->setParameter(mNode, OMX_IndexParamAudioFlac, &def, sizeof(def)); + if (err != OK) { + ALOGE("setupFlacCodec(): Error %d setting OMX_IndexParamAudioFlac parameter", err); + return err; + } + } + + return setupRawAudioFormat( + encoder ? kPortIndexInput : kPortIndexOutput, + sampleRate, + numChannels); +} + status_t ACodec::setupRawAudioFormat( OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) { OMX_PARAM_PORTDEFINITIONTYPE def; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 604d0e0..6083cd4 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1364,6 +1364,8 @@ void OMXCodec::setComponentRole( "video_decoder.vpx", "video_encoder.vpx" }, { MEDIA_MIMETYPE_AUDIO_RAW, "audio_decoder.raw", "audio_encoder.raw" }, + { MEDIA_MIMETYPE_AUDIO_FLAC, + "audio_decoder.flac", "audio_encoder.flac" }, }; static const size_t kNumMimeToRole = diff --git a/media/libstagefright/codecs/flac/Android.mk b/media/libstagefright/codecs/flac/Android.mk new file mode 100644 index 0000000..2e43120 --- /dev/null +++ b/media/libstagefright/codecs/flac/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/flac/enc/Android.mk b/media/libstagefright/codecs/flac/enc/Android.mk new file mode 100644 index 0000000..546a357 --- /dev/null +++ b/media/libstagefright/codecs/flac/enc/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftFlacEncoder.cpp + +LOCAL_C_INCLUDES := \ + frameworks/av/media/libstagefright/include \ + frameworks/native/include/media/openmax \ + external/flac/include + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_STATIC_LIBRARIES := \ + libFLAC \ + +LOCAL_MODULE := libstagefright_soft_flacenc +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp new file mode 100644 index 0000000..233aed3 --- /dev/null +++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp @@ -0,0 +1,444 @@ +/* + * 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 "SoftFlacEncoder" +#include <utils/Log.h> + +#include "SoftFlacEncoder.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> + +#define FLAC_COMPRESSION_LEVEL_MIN 0 +#define FLAC_COMPRESSION_LEVEL_DEFAULT 5 +#define FLAC_COMPRESSION_LEVEL_MAX 8 + +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; +} + +SoftFlacEncoder::SoftFlacEncoder( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mSignalledError(false), + mNumChannels(1), + mSampleRate(44100), + mCompressionLevel(FLAC_COMPRESSION_LEVEL_DEFAULT), + mEncoderWriteData(false), + mEncoderReturnedEncodedData(false), + mEncoderReturnedNbBytes(0), + mInputBufferPcm32(NULL) +#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER + , mHeaderOffset(0) + , mWroteHeader(false) +#endif +{ + ALOGV("SoftFlacEncoder::SoftFlacEncoder(name=%s)", name); + initPorts(); + + mFlacStreamEncoder = FLAC__stream_encoder_new(); + if (mFlacStreamEncoder == NULL) { + ALOGE("SoftFlacEncoder::SoftFlacEncoder(name=%s) error instantiating FLAC encoder", name); + mSignalledError = true; + } + + if (!mSignalledError) { // no use allocating input buffer if we had an error above + mInputBufferPcm32 = (FLAC__int32*) malloc(sizeof(FLAC__int32) * 2 * kMaxNumSamplesPerFrame); + if (mInputBufferPcm32 == NULL) { + ALOGE("SoftFlacEncoder::SoftFlacEncoder(name=%s) error allocating internal input buffer", name); + mSignalledError = true; + } + } +} + +SoftFlacEncoder::~SoftFlacEncoder() { + ALOGV("SoftFlacEncoder::~SoftFlacEncoder()"); + if (mFlacStreamEncoder != NULL) { + FLAC__stream_encoder_delete(mFlacStreamEncoder); + mFlacStreamEncoder = NULL; + } + free(mInputBufferPcm32); + mInputBufferPcm32 = NULL; +} + +OMX_ERRORTYPE SoftFlacEncoder::initCheck() const { + if (mSignalledError) { + if (mFlacStreamEncoder == NULL) { + ALOGE("initCheck() failed due to NULL encoder"); + } else if (mInputBufferPcm32 == NULL) { + ALOGE("initCheck() failed due to error allocating internal input buffer"); + } + return OMX_ErrorUndefined; + } else { + return SimpleSoftOMXComponent::initCheck(); + } +} + +void SoftFlacEncoder::initPorts() { + ALOGV("SoftFlacEncoder::initPorts()"); + + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + // configure input port of the encoder + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers;// TODO verify that 1 is enough + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t) * 2; + 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/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); + + // configure output port of the encoder + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers;// TODO verify that 1 is enough + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxOutputBufferSize; + 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 *>(MEDIA_MIMETYPE_AUDIO_FLAC); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingFLAC; + + addPort(def); +} + +OMX_ERRORTYPE SoftFlacEncoder::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + ALOGV("SoftFlacEncoder::internalGetParameter(index=0x%x)", index); + + switch (index) { + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex > 1) { + 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; + } + + case OMX_IndexParamAudioFlac: + { + OMX_AUDIO_PARAM_FLACTYPE *flacParams = (OMX_AUDIO_PARAM_FLACTYPE *)params; + flacParams->nCompressionLevel = mCompressionLevel; + flacParams->nChannels = mNumChannels; + flacParams->nSampleRate = mSampleRate; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPcm: + { + ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamAudioPcm)"); + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) { + ALOGE("SoftFlacEncoder::internalSetParameter() Error #1"); + return OMX_ErrorUndefined; + } + + if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) { + return OMX_ErrorUndefined; + } + + mNumChannels = pcmParams->nChannels; + mSampleRate = pcmParams->nSamplingRate; + ALOGV("will encode %ld channels at %ldHz", mNumChannels, mSampleRate); + + return configureEncoder(); + } + + case OMX_IndexParamStandardComponentRole: + { + ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamStandardComponentRole)"); + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + "audio_encoder.flac", + OMX_MAX_STRINGNAME_SIZE - 1)) { + ALOGE("SoftFlacEncoder::internalSetParameter(OMX_IndexParamStandardComponentRole)" + "error"); + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioFlac: + { + // used only for setting the compression level + OMX_AUDIO_PARAM_FLACTYPE *flacParams = (OMX_AUDIO_PARAM_FLACTYPE *)params; + mCompressionLevel = flacParams->nCompressionLevel; // range clamping done inside encoder + return OMX_ErrorNone; + } + + default: + ALOGV("SoftFlacEncoder::internalSetParameter(default)"); + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) { + + ALOGV("SoftFlacEncoder::onQueueFilled(portIndex=%ld)", portIndex); + + if (mSignalledError) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(0); + List<BufferInfo *> &outQueue = getPortQueue(1); + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + return; + } + + if (inHeader->nFilledLen > kMaxNumSamplesPerFrame * sizeof(FLAC__int32) * 2) { + ALOGE("input buffer too large (%ld).", inHeader->nFilledLen); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + assert(mNumChannels != 0); + mEncoderWriteData = true; + mEncoderReturnedEncodedData = false; + mEncoderReturnedNbBytes = 0; + mCurrentInputTimeStamp = inHeader->nTimeStamp; + + const unsigned nbInputFrames = inHeader->nFilledLen / (2 * mNumChannels); + const unsigned nbInputSamples = inHeader->nFilledLen / 2; + const OMX_S16 * const pcm16 = reinterpret_cast<OMX_S16 *>(inHeader->pBuffer); + + for (unsigned i=0 ; i < nbInputSamples ; i++) { + mInputBufferPcm32[i] = (FLAC__int32) pcm16[i]; + } + ALOGV(" about to encode %u samples per channel", nbInputFrames); + FLAC__bool ok = FLAC__stream_encoder_process_interleaved( + mFlacStreamEncoder, + mInputBufferPcm32, + nbInputFrames /*samples per channel*/ ); + + if (ok) { + if (mEncoderReturnedEncodedData && (mEncoderReturnedNbBytes != 0)) { + ALOGV(" dequeueing buffer on output port after writing data"); + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + mEncoderReturnedEncodedData = false; + } else { + ALOGV(" encoder process_interleaved returned without data to write"); + } + } else { + ALOGE(" error encountered during encoding"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } +} + + +FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable( + const FLAC__byte buffer[], + size_t bytes, unsigned samples, unsigned current_frame) { + ALOGV("SoftFlacEncoder::onEncodedFlacAvailable(bytes=%d, samples=%d, curr_frame=%d)", + bytes, samples, current_frame); + +#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER + if (samples == 0) { + ALOGI(" saving %d bytes of header", bytes); + memcpy(mHeader + mHeaderOffset, buffer, bytes); + mHeaderOffset += bytes;// will contain header size when finished receiving header + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + +#endif + + if ((samples == 0) || !mEncoderWriteData) { + // called by the encoder because there's header data to save, but it's not the role + // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined) + ALOGV("ignoring %d bytes of header data (samples=%d)", bytes, samples); + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + + List<BufferInfo *> &outQueue = getPortQueue(1); + CHECK(!outQueue.empty()); + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + +#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER + if (!mWroteHeader) { + ALOGI(" writing %d bytes of header on output port", mHeaderOffset); + memcpy(outHeader->pBuffer + outHeader->nOffset + outHeader->nFilledLen, + mHeader, mHeaderOffset); + outHeader->nFilledLen += mHeaderOffset; + outHeader->nOffset += mHeaderOffset; + mWroteHeader = true; + } +#endif + + // write encoded data + ALOGV(" writing %d bytes of encoded data on output port", bytes); + if (bytes > outHeader->nAllocLen - outHeader->nOffset - outHeader->nFilledLen) { + ALOGE(" not enough space left to write encoded data, dropping %u bytes", bytes); + // a fatal error would stop the encoding + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + memcpy(outHeader->pBuffer + outHeader->nOffset, buffer, bytes); + + outHeader->nTimeStamp = mCurrentInputTimeStamp; + outHeader->nOffset = 0; + outHeader->nFilledLen += bytes; + outHeader->nFlags = 0; + + mEncoderReturnedEncodedData = true; + mEncoderReturnedNbBytes += bytes; + + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; +} + + +OMX_ERRORTYPE SoftFlacEncoder::configureEncoder() { + ALOGV("SoftFlacEncoder::configureEncoder() numChannel=%ld, sampleRate=%ld", + mNumChannels, mSampleRate); + + if (mSignalledError || (mFlacStreamEncoder == NULL)) { + ALOGE("can't configure encoder: no encoder or invalid state"); + return OMX_ErrorInvalidState; + } + + FLAC__bool ok = true; + FLAC__StreamEncoderInitStatus initStatus = FLAC__STREAM_ENCODER_INIT_STATUS_OK; + ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mNumChannels); + ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mSampleRate); + ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16); + ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, + (unsigned)mCompressionLevel); + ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false); + if (!ok) { goto return_result; } + + ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK == + FLAC__stream_encoder_init_stream(mFlacStreamEncoder, + flacEncoderWriteCallback /*write_callback*/, + NULL /*seek_callback*/, + NULL /*tell_callback*/, + NULL /*metadata_callback*/, + (void *) this /*client_data*/); + +return_result: + if (ok) { + ALOGV("encoder successfully configured"); + return OMX_ErrorNone; + } else { + ALOGE("unknown error when configuring encoder"); + return OMX_ErrorUndefined; + } +} + + +// static +FLAC__StreamEncoderWriteStatus SoftFlacEncoder::flacEncoderWriteCallback( + const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], + size_t bytes, unsigned samples, unsigned current_frame, void *client_data) { + return ((SoftFlacEncoder*) client_data)->onEncodedFlacAvailable( + buffer, bytes, samples, current_frame); +} + +} // namespace android + + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftFlacEncoder(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h new file mode 100644 index 0000000..1e0148a --- /dev/null +++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h @@ -0,0 +1,102 @@ +/* + * 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_FLAC_ENC_H_ + +#define SOFT_FLAC_ENC_H_ + +#include "SimpleSoftOMXComponent.h" + +#include "FLAC/stream_encoder.h" + +// use this symbol to have the first output buffer start with FLAC frame header so a dump of +// all the output buffers can be opened as a .flac file +//#define WRITE_FLAC_HEADER_IN_FIRST_BUFFER + +namespace android { + +struct SoftFlacEncoder : public SimpleSoftOMXComponent { + SoftFlacEncoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + virtual OMX_ERRORTYPE initCheck() const; + +protected: + virtual ~SoftFlacEncoder(); + + 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 = 2, + kMaxNumSamplesPerFrame = 1152, + kMaxOutputBufferSize = 65536, //TODO check if this can be reduced + }; + + bool mSignalledError; + + OMX_U32 mNumChannels; + OMX_U32 mSampleRate; + OMX_U32 mCompressionLevel; + + // should the data received by the callback be written to the output port + bool mEncoderWriteData; + bool mEncoderReturnedEncodedData; + size_t mEncoderReturnedNbBytes; + OMX_TICKS mCurrentInputTimeStamp; + + FLAC__StreamEncoder* mFlacStreamEncoder; + + void initPorts(); + + OMX_ERRORTYPE configureEncoder(); + + // FLAC encoder callbacks + // maps to encoderEncodeFlac() + static FLAC__StreamEncoderWriteStatus flacEncoderWriteCallback( + const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], + size_t bytes, unsigned samples, unsigned current_frame, void *client_data); + + FLAC__StreamEncoderWriteStatus onEncodedFlacAvailable( + const FLAC__byte buffer[], + size_t bytes, unsigned samples, unsigned current_frame); + + // FLAC takes samples aligned on 32bit boundaries, use this buffer for the conversion + // before passing the input data to the encoder + FLAC__int32* mInputBufferPcm32; + +#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER + unsigned mHeaderOffset; + bool mWroteHeader; + char mHeader[128]; +#endif + + DISALLOW_EVIL_CONSTRUCTORS(SoftFlacEncoder); +}; + +} // namespace android + +#endif // SOFT_FLAC_ENC_H_ + diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 6e53095..3747b3b 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -52,6 +52,7 @@ static const struct { { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" }, { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" }, { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" }, + { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" }, }; static const size_t kNumComponents = |