SoftFlacEncoder.cpp revision f6d0c1fd6d9e697bb3a891fae14c7e9d4b685de6
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
30namespace android {
31
32template<class T>
33static void InitOMXParams(T *params) {
34    params->nSize = sizeof(T);
35    params->nVersion.s.nVersionMajor = 1;
36    params->nVersion.s.nVersionMinor = 0;
37    params->nVersion.s.nRevision = 0;
38    params->nVersion.s.nStep = 0;
39}
40
41SoftFlacEncoder::SoftFlacEncoder(
42        const char *name,
43        const OMX_CALLBACKTYPE *callbacks,
44        OMX_PTR appData,
45        OMX_COMPONENTTYPE **component)
46    : SimpleSoftOMXComponent(name, callbacks, appData, component),
47      mSignalledError(false),
48      mNumChannels(1),
49      mSampleRate(44100),
50      mCompressionLevel(FLAC_COMPRESSION_LEVEL_DEFAULT),
51      mEncoderWriteData(false),
52      mEncoderReturnedEncodedData(false),
53      mEncoderReturnedNbBytes(0),
54      mInputBufferPcm32(NULL)
55#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
56      , mHeaderOffset(0)
57      , mWroteHeader(false)
58#endif
59{
60    ALOGV("SoftFlacEncoder::SoftFlacEncoder(name=%s)", name);
61    initPorts();
62
63    mFlacStreamEncoder = FLAC__stream_encoder_new();
64    if (mFlacStreamEncoder == NULL) {
65        ALOGE("SoftFlacEncoder::SoftFlacEncoder(name=%s) error instantiating FLAC encoder", name);
66        mSignalledError = true;
67    }
68
69    if (!mSignalledError) { // no use allocating input buffer if we had an error above
70        mInputBufferPcm32 = (FLAC__int32*) malloc(sizeof(FLAC__int32) * 2 * kMaxNumSamplesPerFrame);
71        if (mInputBufferPcm32 == NULL) {
72            ALOGE("SoftFlacEncoder::SoftFlacEncoder(name=%s) error allocating internal input buffer", name);
73            mSignalledError = true;
74        }
75    }
76}
77
78SoftFlacEncoder::~SoftFlacEncoder() {
79    ALOGV("SoftFlacEncoder::~SoftFlacEncoder()");
80    if (mFlacStreamEncoder != NULL) {
81        FLAC__stream_encoder_delete(mFlacStreamEncoder);
82        mFlacStreamEncoder = NULL;
83    }
84    free(mInputBufferPcm32);
85    mInputBufferPcm32 = NULL;
86}
87
88OMX_ERRORTYPE SoftFlacEncoder::initCheck() const {
89    if (mSignalledError) {
90        if (mFlacStreamEncoder == NULL) {
91            ALOGE("initCheck() failed due to NULL encoder");
92        } else if (mInputBufferPcm32 == NULL) {
93            ALOGE("initCheck() failed due to error allocating internal input buffer");
94        }
95        return OMX_ErrorUndefined;
96    } else {
97        return SimpleSoftOMXComponent::initCheck();
98    }
99}
100
101void SoftFlacEncoder::initPorts() {
102    ALOGV("SoftFlacEncoder::initPorts()");
103
104    OMX_PARAM_PORTDEFINITIONTYPE def;
105    InitOMXParams(&def);
106
107    // configure input port of the encoder
108    def.nPortIndex = 0;
109    def.eDir = OMX_DirInput;
110    def.nBufferCountMin = kNumBuffers;// TODO verify that 1 is enough
111    def.nBufferCountActual = def.nBufferCountMin;
112    def.nBufferSize = kMaxInputBufferSize;
113    def.bEnabled = OMX_TRUE;
114    def.bPopulated = OMX_FALSE;
115    def.eDomain = OMX_PortDomainAudio;
116    def.bBuffersContiguous = OMX_FALSE;
117    def.nBufferAlignment = 2;
118
119    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
120    def.format.audio.pNativeRender = NULL;
121    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
122    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
123
124    addPort(def);
125
126    // configure output port of the encoder
127    def.nPortIndex = 1;
128    def.eDir = OMX_DirOutput;
129    def.nBufferCountMin = kNumBuffers;// TODO verify that 1 is enough
130    def.nBufferCountActual = def.nBufferCountMin;
131    def.nBufferSize = kMaxOutputBufferSize;
132    def.bEnabled = OMX_TRUE;
133    def.bPopulated = OMX_FALSE;
134    def.eDomain = OMX_PortDomainAudio;
135    def.bBuffersContiguous = OMX_FALSE;
136    def.nBufferAlignment = 1;
137
138    def.format.audio.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_AUDIO_FLAC);
139    def.format.audio.pNativeRender = NULL;
140    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
141    def.format.audio.eEncoding = OMX_AUDIO_CodingFLAC;
142
143    addPort(def);
144}
145
146OMX_ERRORTYPE SoftFlacEncoder::internalGetParameter(
147        OMX_INDEXTYPE index, OMX_PTR params) {
148    ALOGV("SoftFlacEncoder::internalGetParameter(index=0x%x)", index);
149
150    switch (index) {
151        case OMX_IndexParamAudioPcm:
152        {
153            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
154                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
155
156            if (pcmParams->nPortIndex > 1) {
157                return OMX_ErrorUndefined;
158            }
159
160            pcmParams->eNumData = OMX_NumericalDataSigned;
161            pcmParams->eEndian = OMX_EndianBig;
162            pcmParams->bInterleaved = OMX_TRUE;
163            pcmParams->nBitPerSample = 16;
164            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
165            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
166            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
167
168            pcmParams->nChannels = mNumChannels;
169            pcmParams->nSamplingRate = mSampleRate;
170
171            return OMX_ErrorNone;
172        }
173
174        case OMX_IndexParamAudioFlac:
175        {
176            OMX_AUDIO_PARAM_FLACTYPE *flacParams = (OMX_AUDIO_PARAM_FLACTYPE *)params;
177            flacParams->nCompressionLevel = mCompressionLevel;
178            flacParams->nChannels = mNumChannels;
179            flacParams->nSampleRate = mSampleRate;
180            return OMX_ErrorNone;
181        }
182
183        default:
184            return SimpleSoftOMXComponent::internalGetParameter(index, params);
185    }
186}
187
188OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter(
189        OMX_INDEXTYPE index, const OMX_PTR params) {
190    switch (index) {
191        case OMX_IndexParamAudioPcm:
192        {
193            ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamAudioPcm)");
194            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
195
196            if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) {
197                ALOGE("SoftFlacEncoder::internalSetParameter() Error #1");
198                return OMX_ErrorUndefined;
199            }
200
201            if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) {
202                return OMX_ErrorUndefined;
203            }
204
205            mNumChannels = pcmParams->nChannels;
206            mSampleRate = pcmParams->nSamplingRate;
207            ALOGV("will encode %d channels at %dHz", mNumChannels, mSampleRate);
208
209            return configureEncoder();
210        }
211
212        case OMX_IndexParamStandardComponentRole:
213        {
214            ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamStandardComponentRole)");
215            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
216                (const OMX_PARAM_COMPONENTROLETYPE *)params;
217
218            if (strncmp((const char *)roleParams->cRole,
219                    "audio_encoder.flac",
220                    OMX_MAX_STRINGNAME_SIZE - 1)) {
221                ALOGE("SoftFlacEncoder::internalSetParameter(OMX_IndexParamStandardComponentRole)"
222                        "error");
223                return OMX_ErrorUndefined;
224            }
225
226            return OMX_ErrorNone;
227        }
228
229        case OMX_IndexParamAudioFlac:
230        {
231            // used only for setting the compression level
232            OMX_AUDIO_PARAM_FLACTYPE *flacParams = (OMX_AUDIO_PARAM_FLACTYPE *)params;
233            mCompressionLevel = flacParams->nCompressionLevel; // range clamping done inside encoder
234            return OMX_ErrorNone;
235        }
236
237        case OMX_IndexParamPortDefinition:
238        {
239            OMX_PARAM_PORTDEFINITIONTYPE *defParams =
240                (OMX_PARAM_PORTDEFINITIONTYPE *)params;
241
242            if (defParams->nPortIndex == 0) {
243                if (defParams->nBufferSize > kMaxInputBufferSize) {
244                    ALOGE("Input buffer size must be at most %d bytes",
245                        kMaxInputBufferSize);
246                    return OMX_ErrorUnsupportedSetting;
247                }
248            }
249
250            // fall through
251        }
252
253        default:
254            ALOGV("SoftFlacEncoder::internalSetParameter(default)");
255            return SimpleSoftOMXComponent::internalSetParameter(index, params);
256    }
257}
258
259void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) {
260    //UNUSED_UNLESS_VERBOSE(portIndex);
261    ALOGV("SoftFlacEncoder::onQueueFilled(portIndex=%d)", portIndex);
262
263    if (mSignalledError) {
264        return;
265    }
266
267    List<BufferInfo *> &inQueue = getPortQueue(0);
268    List<BufferInfo *> &outQueue = getPortQueue(1);
269
270    while (!inQueue.empty() && !outQueue.empty()) {
271        BufferInfo *inInfo = *inQueue.begin();
272        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
273
274        BufferInfo *outInfo = *outQueue.begin();
275        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
276
277        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
278            inQueue.erase(inQueue.begin());
279            inInfo->mOwnedByUs = false;
280            notifyEmptyBufferDone(inHeader);
281
282            outHeader->nFilledLen = 0;
283            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
284
285            outQueue.erase(outQueue.begin());
286            outInfo->mOwnedByUs = false;
287            notifyFillBufferDone(outHeader);
288
289            return;
290        }
291
292        if (inHeader->nFilledLen > kMaxInputBufferSize) {
293            ALOGE("input buffer too large (%d).", inHeader->nFilledLen);
294            mSignalledError = true;
295            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
296            return;
297        }
298
299        assert(mNumChannels != 0);
300        mEncoderWriteData = true;
301        mEncoderReturnedEncodedData = false;
302        mEncoderReturnedNbBytes = 0;
303        mCurrentInputTimeStamp = inHeader->nTimeStamp;
304
305        const unsigned nbInputFrames = inHeader->nFilledLen / (2 * mNumChannels);
306        const unsigned nbInputSamples = inHeader->nFilledLen / 2;
307        const OMX_S16 * const pcm16 = reinterpret_cast<OMX_S16 *>(inHeader->pBuffer);
308
309        CHECK_LE(nbInputSamples, 2 * kMaxNumSamplesPerFrame);
310        for (unsigned i=0 ; i < nbInputSamples ; i++) {
311            mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
312        }
313        ALOGV(" about to encode %u samples per channel", nbInputFrames);
314        FLAC__bool ok = FLAC__stream_encoder_process_interleaved(
315                        mFlacStreamEncoder,
316                        mInputBufferPcm32,
317                        nbInputFrames /*samples per channel*/ );
318
319        if (ok) {
320            if (mEncoderReturnedEncodedData && (mEncoderReturnedNbBytes != 0)) {
321                ALOGV(" dequeueing buffer on output port after writing data");
322                outInfo->mOwnedByUs = false;
323                outQueue.erase(outQueue.begin());
324                outInfo = NULL;
325                notifyFillBufferDone(outHeader);
326                outHeader = NULL;
327                mEncoderReturnedEncodedData = false;
328            } else {
329                ALOGV(" encoder process_interleaved returned without data to write");
330            }
331        } else {
332            ALOGE(" error encountered during encoding");
333            mSignalledError = true;
334            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
335            return;
336        }
337
338        inInfo->mOwnedByUs = false;
339        inQueue.erase(inQueue.begin());
340        inInfo = NULL;
341        notifyEmptyBufferDone(inHeader);
342        inHeader = NULL;
343    }
344}
345
346
347FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable(
348            const FLAC__byte buffer[],
349            size_t bytes, unsigned samples, unsigned current_frame) {
350    ALOGV("SoftFlacEncoder::onEncodedFlacAvailable(bytes=%d, samples=%d, curr_frame=%d)",
351            bytes, samples, current_frame);
352
353#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
354    if (samples == 0) {
355        ALOGI(" saving %d bytes of header", bytes);
356        memcpy(mHeader + mHeaderOffset, buffer, bytes);
357        mHeaderOffset += bytes;// will contain header size when finished receiving header
358        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
359    }
360
361#endif
362
363    if ((samples == 0) || !mEncoderWriteData) {
364        // called by the encoder because there's header data to save, but it's not the role
365        // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined)
366        ALOGV("ignoring %zu bytes of header data (samples=%d)", bytes, samples);
367        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
368    }
369
370    List<BufferInfo *> &outQueue = getPortQueue(1);
371    CHECK(!outQueue.empty());
372    BufferInfo *outInfo = *outQueue.begin();
373    OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
374
375#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
376    if (!mWroteHeader) {
377        ALOGI(" writing %d bytes of header on output port", mHeaderOffset);
378        memcpy(outHeader->pBuffer + outHeader->nOffset + outHeader->nFilledLen,
379                mHeader, mHeaderOffset);
380        outHeader->nFilledLen += mHeaderOffset;
381        outHeader->nOffset    += mHeaderOffset;
382        mWroteHeader = true;
383    }
384#endif
385
386    // write encoded data
387    ALOGV(" writing %zu bytes of encoded data on output port", bytes);
388    if (bytes > outHeader->nAllocLen - outHeader->nOffset - outHeader->nFilledLen) {
389        ALOGE(" not enough space left to write encoded data, dropping %zu bytes", bytes);
390        // a fatal error would stop the encoding
391        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
392    }
393    memcpy(outHeader->pBuffer + outHeader->nOffset, buffer, bytes);
394
395    outHeader->nTimeStamp = mCurrentInputTimeStamp;
396    outHeader->nOffset = 0;
397    outHeader->nFilledLen += bytes;
398    outHeader->nFlags = 0;
399
400    mEncoderReturnedEncodedData = true;
401    mEncoderReturnedNbBytes += bytes;
402
403    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
404}
405
406
407OMX_ERRORTYPE SoftFlacEncoder::configureEncoder() {
408    ALOGV("SoftFlacEncoder::configureEncoder() numChannel=%d, sampleRate=%d",
409            mNumChannels, mSampleRate);
410
411    if (mSignalledError || (mFlacStreamEncoder == NULL)) {
412        ALOGE("can't configure encoder: no encoder or invalid state");
413        return OMX_ErrorInvalidState;
414    }
415
416    FLAC__bool ok = true;
417    FLAC__StreamEncoderInitStatus initStatus = FLAC__STREAM_ENCODER_INIT_STATUS_OK;
418    ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mNumChannels);
419    ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mSampleRate);
420    ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16);
421    ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder,
422            (unsigned)mCompressionLevel);
423    ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false);
424    if (!ok) { goto return_result; }
425
426    ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK ==
427            FLAC__stream_encoder_init_stream(mFlacStreamEncoder,
428                    flacEncoderWriteCallback    /*write_callback*/,
429                    NULL /*seek_callback*/,
430                    NULL /*tell_callback*/,
431                    NULL /*metadata_callback*/,
432                    (void *) this /*client_data*/);
433
434return_result:
435    if (ok) {
436        ALOGV("encoder successfully configured");
437        return OMX_ErrorNone;
438    } else {
439        ALOGE("unknown error when configuring encoder");
440        return OMX_ErrorUndefined;
441    }
442}
443
444
445// static
446FLAC__StreamEncoderWriteStatus SoftFlacEncoder::flacEncoderWriteCallback(
447            const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[],
448            size_t bytes, unsigned samples, unsigned current_frame, void *client_data) {
449    return ((SoftFlacEncoder*) client_data)->onEncodedFlacAvailable(
450            buffer, bytes, samples, current_frame);
451}
452
453}  // namespace android
454
455
456android::SoftOMXComponent *createSoftOMXComponent(
457        const char *name, const OMX_CALLBACKTYPE *callbacks,
458        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
459    return new android::SoftFlacEncoder(name, callbacks, appData, component);
460}
461
462