Home | History | Annotate | Download | only in enc
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "SoftFlacEncoder"
     19 #include <utils/Log.h>
     20 
     21 #include "SoftFlacEncoder.h"
     22 
     23 #include <media/stagefright/foundation/ADebug.h>
     24 #include <media/stagefright/MediaDefs.h>
     25 
     26 #define FLAC_COMPRESSION_LEVEL_MIN     0
     27 #define FLAC_COMPRESSION_LEVEL_DEFAULT 5
     28 #define FLAC_COMPRESSION_LEVEL_MAX     8
     29 
     30 #if LOG_NDEBUG
     31 #define UNUSED_UNLESS_VERBOSE(x) (void)(x)
     32 #else
     33 #define UNUSED_UNLESS_VERBOSE(x)
     34 #endif
     35 
     36 namespace android {
     37 
     38 template<class T>
     39 static void InitOMXParams(T *params) {
     40     params->nSize = sizeof(T);
     41     params->nVersion.s.nVersionMajor = 1;
     42     params->nVersion.s.nVersionMinor = 0;
     43     params->nVersion.s.nRevision = 0;
     44     params->nVersion.s.nStep = 0;
     45 }
     46 
     47 SoftFlacEncoder::SoftFlacEncoder(
     48         const char *name,
     49         const OMX_CALLBACKTYPE *callbacks,
     50         OMX_PTR appData,
     51         OMX_COMPONENTTYPE **component)
     52     : SimpleSoftOMXComponent(name, callbacks, appData, component),
     53       mSignalledError(false),
     54       mNumChannels(1),
     55       mSampleRate(44100),
     56       mCompressionLevel(FLAC_COMPRESSION_LEVEL_DEFAULT),
     57       mEncoderWriteData(false),
     58       mEncoderReturnedEncodedData(false),
     59       mSawInputEOS(false),
     60       mSentOutputEOS(false),
     61       mEncoderReturnedNbBytes(0),
     62       mInputBufferPcm32(NULL),
     63       mHeaderOffset(0),
     64       mHeaderComplete(false),
     65       mWroteHeader(false)
     66 {
     67     ALOGV("SoftFlacEncoder::SoftFlacEncoder(name=%s)", name);
     68     initPorts();
     69 
     70     mFlacStreamEncoder = FLAC__stream_encoder_new();
     71     if (mFlacStreamEncoder == NULL) {
     72         ALOGE("SoftFlacEncoder::SoftFlacEncoder(name=%s) error instantiating FLAC encoder", name);
     73         mSignalledError = true;
     74     }
     75 
     76     if (!mSignalledError) { // no use allocating input buffer if we had an error above
     77         mInputBufferPcm32 = (FLAC__int32*) malloc(sizeof(FLAC__int32) * 2 * kMaxNumSamplesPerFrame);
     78         if (mInputBufferPcm32 == NULL) {
     79             ALOGE("SoftFlacEncoder::SoftFlacEncoder(name=%s) error allocating internal input buffer", name);
     80             mSignalledError = true;
     81         }
     82     }
     83 }
     84 
     85 SoftFlacEncoder::~SoftFlacEncoder() {
     86     ALOGV("SoftFlacEncoder::~SoftFlacEncoder()");
     87     if (mFlacStreamEncoder != NULL) {
     88         FLAC__stream_encoder_delete(mFlacStreamEncoder);
     89         mFlacStreamEncoder = NULL;
     90     }
     91     free(mInputBufferPcm32);
     92     mInputBufferPcm32 = NULL;
     93 }
     94 
     95 OMX_ERRORTYPE SoftFlacEncoder::initCheck() const {
     96     if (mSignalledError) {
     97         if (mFlacStreamEncoder == NULL) {
     98             ALOGE("initCheck() failed due to NULL encoder");
     99         } else if (mInputBufferPcm32 == NULL) {
    100             ALOGE("initCheck() failed due to error allocating internal input buffer");
    101         }
    102         return OMX_ErrorUndefined;
    103     } else {
    104         return SimpleSoftOMXComponent::initCheck();
    105     }
    106 }
    107 
    108 void SoftFlacEncoder::initPorts() {
    109     ALOGV("SoftFlacEncoder::initPorts()");
    110 
    111     OMX_PARAM_PORTDEFINITIONTYPE def;
    112     InitOMXParams(&def);
    113 
    114     // configure input port of the encoder
    115     def.nPortIndex = 0;
    116     def.eDir = OMX_DirInput;
    117     def.nBufferCountMin = kNumBuffers;// TODO verify that 1 is enough
    118     def.nBufferCountActual = def.nBufferCountMin;
    119     def.nBufferSize = kMaxInputBufferSize;
    120     def.bEnabled = OMX_TRUE;
    121     def.bPopulated = OMX_FALSE;
    122     def.eDomain = OMX_PortDomainAudio;
    123     def.bBuffersContiguous = OMX_FALSE;
    124     def.nBufferAlignment = 2;
    125 
    126     def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
    127     def.format.audio.pNativeRender = NULL;
    128     def.format.audio.bFlagErrorConcealment = OMX_FALSE;
    129     def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
    130 
    131     addPort(def);
    132 
    133     // configure output port of the encoder
    134     def.nPortIndex = 1;
    135     def.eDir = OMX_DirOutput;
    136     def.nBufferCountMin = kNumBuffers;// TODO verify that 1 is enough
    137     def.nBufferCountActual = def.nBufferCountMin;
    138     def.nBufferSize = kMaxOutputBufferSize;
    139     def.bEnabled = OMX_TRUE;
    140     def.bPopulated = OMX_FALSE;
    141     def.eDomain = OMX_PortDomainAudio;
    142     def.bBuffersContiguous = OMX_FALSE;
    143     def.nBufferAlignment = 1;
    144 
    145     def.format.audio.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_AUDIO_FLAC);
    146     def.format.audio.pNativeRender = NULL;
    147     def.format.audio.bFlagErrorConcealment = OMX_FALSE;
    148     def.format.audio.eEncoding = OMX_AUDIO_CodingFLAC;
    149 
    150     addPort(def);
    151 }
    152 
    153 OMX_ERRORTYPE SoftFlacEncoder::internalGetParameter(
    154         OMX_INDEXTYPE index, OMX_PTR params) {
    155     ALOGV("SoftFlacEncoder::internalGetParameter(index=0x%x)", index);
    156 
    157     switch (index) {
    158         case OMX_IndexParamAudioPortFormat:
    159         {
    160             OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
    161                 (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
    162 
    163             if (!isValidOMXParam(formatParams)) {
    164                 return OMX_ErrorBadParameter;
    165             }
    166 
    167             if (formatParams->nPortIndex > 1) {
    168                 return OMX_ErrorUndefined;
    169             }
    170 
    171             if (formatParams->nIndex > 0) {
    172                 return OMX_ErrorNoMore;
    173             }
    174 
    175             formatParams->eEncoding =
    176                 (formatParams->nPortIndex == 0)
    177                     ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingFLAC;
    178 
    179             return OMX_ErrorNone;
    180         }
    181 
    182         case OMX_IndexParamAudioPcm:
    183         {
    184             OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
    185                 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
    186 
    187             if (!isValidOMXParam(pcmParams)) {
    188                 return OMX_ErrorBadParameter;
    189             }
    190 
    191             if (pcmParams->nPortIndex != 0) {
    192                 return OMX_ErrorUndefined;
    193             }
    194 
    195             pcmParams->eNumData = OMX_NumericalDataSigned;
    196             pcmParams->eEndian = OMX_EndianBig;
    197             pcmParams->bInterleaved = OMX_TRUE;
    198             pcmParams->nBitPerSample = 16;
    199             pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
    200             pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
    201             pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
    202 
    203             pcmParams->nChannels = mNumChannels;
    204             pcmParams->nSamplingRate = mSampleRate;
    205 
    206             return OMX_ErrorNone;
    207         }
    208 
    209         case OMX_IndexParamAudioFlac:
    210         {
    211             OMX_AUDIO_PARAM_FLACTYPE *flacParams = (OMX_AUDIO_PARAM_FLACTYPE *)params;
    212 
    213             if (!isValidOMXParam(flacParams)) {
    214                 return OMX_ErrorBadParameter;
    215             }
    216 
    217             if (flacParams->nPortIndex != 1) {
    218                 return OMX_ErrorUndefined;
    219             }
    220 
    221             flacParams->nCompressionLevel = mCompressionLevel;
    222             flacParams->nChannels = mNumChannels;
    223             flacParams->nSampleRate = mSampleRate;
    224             return OMX_ErrorNone;
    225         }
    226 
    227         default:
    228             return SimpleSoftOMXComponent::internalGetParameter(index, params);
    229     }
    230 }
    231 
    232 OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter(
    233         OMX_INDEXTYPE index, const OMX_PTR params) {
    234     switch (index) {
    235         case OMX_IndexParamAudioPortFormat:
    236         {
    237             const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
    238                 (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
    239 
    240             if (!isValidOMXParam(formatParams)) {
    241                 return OMX_ErrorBadParameter;
    242             }
    243 
    244             if (formatParams->nPortIndex > 1) {
    245                 return OMX_ErrorUndefined;
    246             }
    247 
    248             if ((formatParams->nPortIndex == 0
    249                         && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
    250                 || (formatParams->nPortIndex == 1
    251                         && formatParams->eEncoding != OMX_AUDIO_CodingFLAC)) {
    252                 return OMX_ErrorUndefined;
    253             }
    254 
    255             return OMX_ErrorNone;
    256         }
    257 
    258         case OMX_IndexParamAudioPcm:
    259         {
    260             ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamAudioPcm)");
    261             OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
    262 
    263             if (!isValidOMXParam(pcmParams)) {
    264                 return OMX_ErrorBadParameter;
    265             }
    266 
    267             if (pcmParams->nPortIndex != 0) {
    268                 ALOGE("SoftFlacEncoder::internalSetParameter() Error #1");
    269                 return OMX_ErrorUndefined;
    270             }
    271 
    272             if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) {
    273                 return OMX_ErrorUndefined;
    274             }
    275 
    276             mNumChannels = pcmParams->nChannels;
    277             mSampleRate = pcmParams->nSamplingRate;
    278             ALOGV("will encode %d channels at %dHz", mNumChannels, mSampleRate);
    279 
    280             return configureEncoder();
    281         }
    282 
    283         case OMX_IndexParamStandardComponentRole:
    284         {
    285             ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamStandardComponentRole)");
    286             const OMX_PARAM_COMPONENTROLETYPE *roleParams =
    287                 (const OMX_PARAM_COMPONENTROLETYPE *)params;
    288 
    289             if (!isValidOMXParam(roleParams)) {
    290                 return OMX_ErrorBadParameter;
    291             }
    292 
    293             if (strncmp((const char *)roleParams->cRole,
    294                     "audio_encoder.flac",
    295                     OMX_MAX_STRINGNAME_SIZE - 1)) {
    296                 ALOGE("SoftFlacEncoder::internalSetParameter(OMX_IndexParamStandardComponentRole)"
    297                         "error");
    298                 return OMX_ErrorUndefined;
    299             }
    300 
    301             return OMX_ErrorNone;
    302         }
    303 
    304         case OMX_IndexParamAudioFlac:
    305         {
    306             // used only for setting the compression level
    307             OMX_AUDIO_PARAM_FLACTYPE *flacParams = (OMX_AUDIO_PARAM_FLACTYPE *)params;
    308 
    309             if (!isValidOMXParam(flacParams)) {
    310                 return OMX_ErrorBadParameter;
    311             }
    312 
    313             if (flacParams->nPortIndex != 1) {
    314                 return OMX_ErrorUndefined;
    315             }
    316 
    317             mCompressionLevel = flacParams->nCompressionLevel; // range clamping done inside encoder
    318             return OMX_ErrorNone;
    319         }
    320 
    321         case OMX_IndexParamPortDefinition:
    322         {
    323             OMX_PARAM_PORTDEFINITIONTYPE *defParams =
    324                 (OMX_PARAM_PORTDEFINITIONTYPE *)params;
    325 
    326             if (!isValidOMXParam(defParams)) {
    327                 return OMX_ErrorBadParameter;
    328             }
    329 
    330             if (defParams->nPortIndex == 0) {
    331                 if (defParams->nBufferSize > kMaxInputBufferSize) {
    332                     ALOGE("Input buffer size must be at most %d bytes",
    333                         kMaxInputBufferSize);
    334                     return OMX_ErrorUnsupportedSetting;
    335                 }
    336             }
    337 
    338             // fall through
    339         }
    340 
    341         default:
    342             ALOGV("SoftFlacEncoder::internalSetParameter(default)");
    343             return SimpleSoftOMXComponent::internalSetParameter(index, params);
    344     }
    345 }
    346 
    347 void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) {
    348     UNUSED_UNLESS_VERBOSE(portIndex);
    349     ALOGV("SoftFlacEncoder::onQueueFilled(portIndex=%d)", portIndex);
    350 
    351     if (mSignalledError) {
    352         return;
    353     }
    354 
    355     List<BufferInfo *> &inQueue = getPortQueue(0);
    356     List<BufferInfo *> &outQueue = getPortQueue(1);
    357 
    358     FLAC__bool ok = true;
    359 
    360     while ((!inQueue.empty() || mSawInputEOS) && !outQueue.empty() && !mSentOutputEOS) {
    361         if (!inQueue.empty()) {
    362             BufferInfo *inInfo = *inQueue.begin();
    363             OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
    364 
    365             if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
    366                 ALOGV("saw EOS on buffer of size %u", inHeader->nFilledLen);
    367                 mSawInputEOS = true;
    368             }
    369 
    370             if (inHeader->nFilledLen > kMaxInputBufferSize) {
    371                 ALOGE("input buffer too large (%d).", inHeader->nFilledLen);
    372                 mSignalledError = true;
    373                 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
    374                 return;
    375             }
    376 
    377             assert(mNumChannels != 0);
    378             mEncoderWriteData = true;
    379             mEncoderReturnedEncodedData = false;
    380             mEncoderReturnedNbBytes = 0;
    381             mCurrentInputTimeStamp = inHeader->nTimeStamp;
    382 
    383             const unsigned nbInputFrames = inHeader->nFilledLen / (2 * mNumChannels);
    384             const unsigned nbInputSamples = inHeader->nFilledLen / 2;
    385             const OMX_S16 * const pcm16 = reinterpret_cast<OMX_S16 *>(inHeader->pBuffer);
    386 
    387             CHECK_LE(nbInputSamples, 2 * kMaxNumSamplesPerFrame);
    388             for (unsigned i=0 ; i < nbInputSamples ; i++) {
    389                 mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
    390             }
    391             ALOGV(" about to encode %u samples per channel", nbInputFrames);
    392             ok = FLAC__stream_encoder_process_interleaved(
    393                             mFlacStreamEncoder,
    394                             mInputBufferPcm32,
    395                             nbInputFrames /*samples per channel*/ );
    396 
    397             inInfo->mOwnedByUs = false;
    398             inQueue.erase(inQueue.begin());
    399             inInfo = NULL;
    400             notifyEmptyBufferDone(inHeader);
    401             inHeader = NULL;
    402         }
    403 
    404         BufferInfo *outInfo = *outQueue.begin();
    405         OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
    406 
    407         if (ok) {
    408             if (mEncoderReturnedEncodedData && (mEncoderReturnedNbBytes != 0)) {
    409                 ALOGV(" dequeueing buffer on output port after writing data");
    410                 outInfo->mOwnedByUs = false;
    411                 outQueue.erase(outQueue.begin());
    412                 outInfo = NULL;
    413                 notifyFillBufferDone(outHeader);
    414                 outHeader = NULL;
    415                 mEncoderReturnedEncodedData = false;
    416             } else {
    417                 ALOGV(" encoder process_interleaved returned without data to write");
    418                 if (mSawInputEOS) {
    419                     ALOGV("finishing encoder");
    420                     mSentOutputEOS = true;
    421                     FLAC__stream_encoder_finish(mFlacStreamEncoder);
    422                     if (mEncoderReturnedEncodedData && (mEncoderReturnedNbBytes != 0)) {
    423                         ALOGV(" dequeueing residual buffer on output port after writing data");
    424                         outInfo->mOwnedByUs = false;
    425                         outQueue.erase(outQueue.begin());
    426                         outInfo = NULL;
    427                         outHeader->nFlags = OMX_BUFFERFLAG_EOS;
    428                         notifyFillBufferDone(outHeader);
    429                         outHeader = NULL;
    430                         mEncoderReturnedEncodedData = false;
    431                     }
    432                 }
    433             }
    434         } else {
    435             ALOGE(" error encountered during encoding");
    436             mSignalledError = true;
    437             notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
    438             return;
    439         }
    440 
    441     }
    442 }
    443 
    444 FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable(
    445             const FLAC__byte buffer[],
    446             size_t bytes, unsigned samples,
    447             unsigned current_frame) {
    448     UNUSED_UNLESS_VERBOSE(current_frame);
    449     ALOGV("SoftFlacEncoder::onEncodedFlacAvailable(bytes=%zu, samples=%u, curr_frame=%u)",
    450             bytes, samples, current_frame);
    451 
    452     if (samples == 0) {
    453         ALOGV("saving %zu bytes of header", bytes);
    454         if (mHeaderOffset + bytes > sizeof(mHeader) || mHeaderComplete) {
    455             ALOGW("header is too big, or header already received");
    456             mSignalledError = true;
    457             notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
    458         } else {
    459             memcpy(mHeader + mHeaderOffset, buffer, bytes);
    460             mHeaderOffset += bytes;// will contain header size when finished receiving header
    461             if (buffer[0] & 0x80) {
    462                 mHeaderComplete = true;
    463             }
    464         }
    465         return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
    466     }
    467 
    468     if ((samples == 0) || !mEncoderWriteData) {
    469         // called by the encoder because there's header data to save, but it's not the role
    470         // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined)
    471         ALOGV("ignoring %zu bytes of header data (samples=%d)", bytes, samples);
    472         return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
    473     }
    474 
    475     List<BufferInfo *> &outQueue = getPortQueue(1);
    476     CHECK(!outQueue.empty());
    477     BufferInfo *outInfo = *outQueue.begin();
    478     OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
    479 
    480     if (mHeaderComplete && !mWroteHeader) {
    481         ALOGV(" writing %d bytes of header on output port", mHeaderOffset);
    482         memcpy(outHeader->pBuffer + outHeader->nOffset + outHeader->nFilledLen,
    483                 mHeader, mHeaderOffset);
    484         outHeader->nFilledLen += mHeaderOffset;
    485         mWroteHeader = true;
    486         outInfo->mOwnedByUs = false;
    487         outQueue.erase(outQueue.begin());
    488         outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG;
    489         notifyFillBufferDone(outHeader);
    490         outInfo = NULL;
    491         outHeader = NULL;
    492         // get the next buffer for the rest of the data
    493         CHECK(!outQueue.empty());
    494         outInfo = *outQueue.begin();
    495         outHeader = outInfo->mHeader;
    496     }
    497 
    498     // write encoded data
    499     ALOGV(" writing %zu bytes of encoded data on output port", bytes);
    500     if (bytes > outHeader->nAllocLen - outHeader->nOffset - outHeader->nFilledLen) {
    501         ALOGE(" not enough space left to write encoded data, dropping %zu bytes", bytes);
    502         // a fatal error would stop the encoding
    503         return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
    504     }
    505     memcpy(outHeader->pBuffer + outHeader->nOffset, buffer, bytes);
    506 
    507     outHeader->nTimeStamp = mCurrentInputTimeStamp;
    508     outHeader->nOffset = 0;
    509     outHeader->nFilledLen += bytes;
    510     outHeader->nFlags = 0;
    511 
    512     mEncoderReturnedEncodedData = true;
    513     mEncoderReturnedNbBytes += bytes;
    514 
    515     return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
    516 }
    517 
    518 
    519 OMX_ERRORTYPE SoftFlacEncoder::configureEncoder() {
    520     ALOGV("SoftFlacEncoder::configureEncoder() numChannel=%d, sampleRate=%d",
    521             mNumChannels, mSampleRate);
    522 
    523     if (mSignalledError || (mFlacStreamEncoder == NULL)) {
    524         ALOGE("can't configure encoder: no encoder or invalid state");
    525         return OMX_ErrorInvalidState;
    526     }
    527 
    528     FLAC__bool ok = true;
    529     ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mNumChannels);
    530     ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mSampleRate);
    531     ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16);
    532     ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder,
    533             (unsigned)mCompressionLevel);
    534     ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false);
    535     if (!ok) { goto return_result; }
    536 
    537     ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK ==
    538             FLAC__stream_encoder_init_stream(mFlacStreamEncoder,
    539                     flacEncoderWriteCallback    /*write_callback*/,
    540                     NULL /*seek_callback*/,
    541                     NULL /*tell_callback*/,
    542                     NULL /*metadata_callback*/,
    543                     (void *) this /*client_data*/);
    544 
    545 return_result:
    546     if (ok) {
    547         ALOGV("encoder successfully configured");
    548         return OMX_ErrorNone;
    549     } else {
    550         ALOGE("unknown error when configuring encoder");
    551         return OMX_ErrorUndefined;
    552     }
    553 }
    554 
    555 
    556 // static
    557 FLAC__StreamEncoderWriteStatus SoftFlacEncoder::flacEncoderWriteCallback(
    558             const FLAC__StreamEncoder * /* encoder */,
    559             const FLAC__byte buffer[],
    560             size_t bytes,
    561             unsigned samples,
    562             unsigned current_frame,
    563             void *client_data) {
    564     return ((SoftFlacEncoder*) client_data)->onEncodedFlacAvailable(
    565             buffer, bytes, samples, current_frame);
    566 }
    567 
    568 }  // namespace android
    569 
    570 
    571 android::SoftOMXComponent *createSoftOMXComponent(
    572         const char *name, const OMX_CALLBACKTYPE *callbacks,
    573         OMX_PTR appData, OMX_COMPONENTTYPE **component) {
    574     return new android::SoftFlacEncoder(name, callbacks, appData, component);
    575 }
    576 
    577