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