Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 103f5a62 authored by Marco Nelissen's avatar Marco Nelissen
Browse files

Fix flac encoder and decoder EOS handling

When the flac encoder saw EOS on its input, it would output an empty
buffer with EOS set, without decoding the current buffer or flushing
pending output buffers. The flac encoder also didn't output CSD, making
its output unsuitable for feeding back into the flac decoder.
When the flac decoder saw EOS, it would tag the next output buffer
with EOS, but didn't flush pending output buffers.

Bug: 75963284
Test: CTS
Change-Id: I53ac2f26fe77e50c899587fc62fc66cf0b85d167
parent c7e85481
Loading
Loading
Loading
Loading
+77 −71
Original line number Diff line number Diff line
@@ -45,9 +45,11 @@ SoftFlacDecoder::SoftFlacDecoder(
        OMX_COMPONENTTYPE **component)
    : SimpleSoftOMXComponent(name, callbacks, appData, component),
      mFLACDecoder(NULL),
      mHasStreamInfo(false),
      mInputBufferCount(0),
      mHasStreamInfo(false),
      mSignalledError(false),
      mSawInputEOS(false),
      mFinishedDecoder(false),
      mOutputPortSettingsChange(NONE) {
    ALOGV("ctor:");
    memset(&mStreamInfo, 0, sizeof(mStreamInfo));
@@ -292,7 +294,6 @@ bool SoftFlacDecoder::isConfigured() const {
}

void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) {
    ALOGV("onQueueFilled:");
    if (mSignalledError || mOutputPortSettingsChange != NONE) {
        return;
    }
@@ -300,35 +301,31 @@ void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) {
    List<BufferInfo *> &inQueue = getPortQueue(0);
    List<BufferInfo *> &outQueue = getPortQueue(1);

    while (!inQueue.empty() && !outQueue.empty()) {
        BufferInfo *inInfo = *inQueue.begin();
        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
    ALOGV("onQueueFilled %d/%d:", inQueue.empty(), outQueue.empty());
    while ((!inQueue.empty() || mSawInputEOS) && !outQueue.empty()) {
        BufferInfo *outInfo = *outQueue.begin();
        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
        short *outBuffer = reinterpret_cast<short *>(outHeader->pBuffer + outHeader->nOffset);
        size_t outBufferSize = outHeader->nAllocLen - outHeader->nOffset;
        int64_t timeStamp = 0;

        if (!inQueue.empty()) {
            BufferInfo *inInfo = *inQueue.begin();
            OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
            uint8_t* inBuffer = inHeader->pBuffer + inHeader->nOffset;
            uint32_t inBufferLength = inHeader->nFilledLen;
        bool endOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0;

        if (inHeader->nFilledLen == 0) {
            if (endOfInput) {
                outHeader->nFilledLen = 0;
                outHeader->nFlags = OMX_BUFFERFLAG_EOS;
                outInfo->mOwnedByUs = false;
                outQueue.erase(outQueue.begin());
                notifyFillBufferDone(outHeader);
            } else {
                ALOGE("onQueueFilled: emptyInputBuffer received");
            }
            inInfo->mOwnedByUs = false;
            inQueue.erase(inQueue.begin());
            notifyEmptyBufferDone(inHeader);
            return;
            ALOGV("input: %u bytes", inBufferLength);
            if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
                ALOGV("saw EOS");
                mSawInputEOS = true;
            }

            if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
                ALOGE("onQueueFilled: first buffer should have OMX_BUFFERFLAG_CODECCONFIG set");
                inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
            }
            if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) {
                ALOGV("received config buffer of size %u", inBufferLength);
                status_t decoderErr = mFLACDecoder->parseMetadata(inBuffer, inBufferLength);
                mInputBufferCount++;

@@ -361,10 +358,6 @@ void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) {
                return;
            }

        short *outBuffer =
                reinterpret_cast<short *>(outHeader->pBuffer + outHeader->nOffset);
        size_t outBufferSize = outHeader->nAllocLen - outHeader->nOffset;

            status_t decoderErr = mFLACDecoder->decodeOneFrame(
                    inBuffer, inBufferLength, outBuffer, &outBufferSize);
            if (decoderErr != OK) {
@@ -375,21 +368,34 @@ void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) {
            }

            mInputBufferCount++;
        int64_t ts = inHeader->nTimeStamp;
            timeStamp = inHeader->nTimeStamp;
            inInfo->mOwnedByUs = false;
            inQueue.erase(inQueue.begin());
            notifyEmptyBufferDone(inHeader);

        if (endOfInput) {
            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
        } else if (outBufferSize == 0) {
            if (outBufferSize == 0) {
                ALOGV("no output, trying again");
                continue;
            }
        } else if (mSawInputEOS && !mFinishedDecoder) {
            status_t decoderErr = mFLACDecoder->decodeOneFrame(NULL, 0, outBuffer, &outBufferSize);
            mFinishedDecoder = true;
            if (decoderErr != OK) {
                ALOGE("onQueueFilled: FLACDecoder finish returns error %d", decoderErr);
                mSignalledError = true;
                notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL);
                return;
            }
            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
        } else {
            outHeader->nFlags = 0;
            ALOGE("no input buffer but did not get EOS");
            mSignalledError = true;
            notify(OMX_EventError, OMX_ErrorStreamCorrupt, 0, NULL);
            return;
        }

        outHeader->nFilledLen = outBufferSize;
        outHeader->nTimeStamp = ts;
        outHeader->nTimeStamp = timeStamp;

        outInfo->mOwnedByUs = false;
        outQueue.erase(outQueue.begin());
+3 −1
Original line number Diff line number Diff line
@@ -52,9 +52,11 @@ private:

    FLACDecoder *mFLACDecoder;
    FLAC__StreamMetadata_StreamInfo mStreamInfo;
    bool mHasStreamInfo;
    size_t mInputBufferCount;
    bool mHasStreamInfo;
    bool mSignalledError;
    bool mSawInputEOS;
    bool mFinishedDecoder;

    enum {
        NONE,
+85 −61
Original line number Diff line number Diff line
@@ -56,12 +56,13 @@ SoftFlacEncoder::SoftFlacEncoder(
      mCompressionLevel(FLAC_COMPRESSION_LEVEL_DEFAULT),
      mEncoderWriteData(false),
      mEncoderReturnedEncodedData(false),
      mSawInputEOS(false),
      mSentOutputEOS(false),
      mEncoderReturnedNbBytes(0),
      mInputBufferPcm32(NULL)
#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
      , mHeaderOffset(0)
      , mWroteHeader(false)
#endif
      mInputBufferPcm32(NULL),
      mHeaderOffset(0),
      mHeaderComplete(false),
      mWroteHeader(false)
{
    ALOGV("SoftFlacEncoder::SoftFlacEncoder(name=%s)", name);
    initPorts();
@@ -354,26 +355,16 @@ void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) {
    List<BufferInfo *> &inQueue = getPortQueue(0);
    List<BufferInfo *> &outQueue = getPortQueue(1);

    while (!inQueue.empty() && !outQueue.empty()) {
    FLAC__bool ok = true;

    while ((!inQueue.empty() || mSawInputEOS) && !outQueue.empty()) {
        if (!inQueue.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;
                ALOGV("saw EOS on buffer of size %u", inHeader->nFilledLen);
                mSawInputEOS = true;
            }

            if (inHeader->nFilledLen > kMaxInputBufferSize) {
@@ -398,11 +389,21 @@ void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) {
                mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
            }
            ALOGV(" about to encode %u samples per channel", nbInputFrames);
        FLAC__bool ok = FLAC__stream_encoder_process_interleaved(
            ok = FLAC__stream_encoder_process_interleaved(
                            mFlacStreamEncoder,
                            mInputBufferPcm32,
                            nbInputFrames /*samples per channel*/ );

            inInfo->mOwnedByUs = false;
            inQueue.erase(inQueue.begin());
            inInfo = NULL;
            notifyEmptyBufferDone(inHeader);
            inHeader = NULL;
        }

        BufferInfo *outInfo = *outQueue.begin();
        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;

        if (ok) {
            if (mEncoderReturnedEncodedData && (mEncoderReturnedNbBytes != 0)) {
                ALOGV(" dequeueing buffer on output port after writing data");
@@ -414,6 +415,21 @@ void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) {
                mEncoderReturnedEncodedData = false;
            } else {
                ALOGV(" encoder process_interleaved returned without data to write");
                if (mSawInputEOS && !mSentOutputEOS) {
                    ALOGV("finishing encoder");
                    mSentOutputEOS = true;
                    FLAC__stream_encoder_finish(mFlacStreamEncoder);
                    if (mEncoderReturnedEncodedData && (mEncoderReturnedNbBytes != 0)) {
                        ALOGV(" dequeueing residual buffer on output port after writing data");
                        outInfo->mOwnedByUs = false;
                        outQueue.erase(outQueue.begin());
                        outInfo = NULL;
                        outHeader->nFlags = OMX_BUFFERFLAG_EOS;
                        notifyFillBufferDone(outHeader);
                        outHeader = NULL;
                        mEncoderReturnedEncodedData = false;
                    }
                }
            }
        } else {
            ALOGE(" error encountered during encoding");
@@ -422,11 +438,6 @@ void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) {
            return;
        }

        inInfo->mOwnedByUs = false;
        inQueue.erase(inQueue.begin());
        inInfo = NULL;
        notifyEmptyBufferDone(inHeader);
        inHeader = NULL;
    }
}

@@ -438,16 +449,22 @@ FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable(
    ALOGV("SoftFlacEncoder::onEncodedFlacAvailable(bytes=%zu, samples=%u, curr_frame=%u)",
            bytes, samples, current_frame);

#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
    if (samples == 0) {
        ALOGI(" saving %zu bytes of header", bytes);
        ALOGV("saving %zu bytes of header", bytes);
        if (mHeaderOffset + bytes > sizeof(mHeader) || mHeaderComplete) {
            ALOGW("header is too big, or header already received");
            mSignalledError = true;
            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
        } else {
            memcpy(mHeader + mHeaderOffset, buffer, bytes);
            mHeaderOffset += bytes;// will contain header size when finished receiving header
            if (buffer[0] & 0x80) {
                mHeaderComplete = true;
            }
        }
        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)
@@ -460,16 +477,23 @@ FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable(
    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);
    if (mHeaderComplete && !mWroteHeader) {
        ALOGV(" 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;
        outInfo->mOwnedByUs = false;
        outQueue.erase(outQueue.begin());
        outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG;
        notifyFillBufferDone(outHeader);
        outInfo = NULL;
        outHeader = NULL;
        // get the next buffer for the rest of the data
        CHECK(!outQueue.empty());
        outInfo = *outQueue.begin();
        outHeader = outInfo->mHeader;
    }
#endif

    // write encoded data
    ALOGV(" writing %zu bytes of encoded data on output port", bytes);
+3 −6
Original line number Diff line number Diff line
@@ -22,10 +22,6 @@

#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 {
@@ -62,6 +58,8 @@ private:
    // should the data received by the callback be written to the output port
    bool        mEncoderWriteData;
    bool        mEncoderReturnedEncodedData;
    bool        mSawInputEOS;
    bool        mSentOutputEOS;
    size_t      mEncoderReturnedNbBytes;
    OMX_TICKS  mCurrentInputTimeStamp;

@@ -85,11 +83,10 @@ private:
    // before passing the input data to the encoder
    FLAC__int32* mInputBufferPcm32;

#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
    unsigned mHeaderOffset;
    bool mHeaderComplete;
    bool mWroteHeader;
    char mHeader[128];
#endif

    DISALLOW_EVIL_CONSTRUCTORS(SoftFlacEncoder);
};
+6 −12
Original line number Diff line number Diff line
@@ -423,23 +423,17 @@ status_t FLACDecoder::decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen
        short *outBuffer, size_t *outBufferLen) {
    ALOGV("decodeOneFrame: input size(%zu)", inBufferLen);

    if (inBufferLen == 0) {
        ALOGV("decodeOneFrame: no input data");
        if (outBufferLen) {
            *outBufferLen = 0;
        }
        return OK;
    }

    if (!mStreamInfoValid) {
        ALOGW("decodeOneFrame: no streaminfo metadata block");
    }

    if (inBufferLen != 0) {
        status_t err = addDataToBuffer(inBuffer, inBufferLen);
        if (err != OK) {
            ALOGW("decodeOneFrame: addDataToBuffer returns error %d", err);
            return err;
        }
    }

    mWriteRequested = true;
    mWriteCompleted = false;