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
gugelfrei
Marco Nelissen 6 years ago
parent c7e85481ec
commit 103f5a6268

@ -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,96 +301,101 @@ 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;
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");
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;
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++;
if (decoderErr != OK && decoderErr != WOULD_BLOCK) {
ALOGE("onQueueFilled: FLACDecoder parseMetaData returns error %d", decoderErr);
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL);
return;
}
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
notifyEmptyBufferDone(inHeader);
if (decoderErr == WOULD_BLOCK) {
continue;
}
mStreamInfo = mFLACDecoder->getStreamInfo();
mHasStreamInfo = true;
// Only send out port settings changed event if both sample rate
// and numChannels are valid.
if (mStreamInfo.sample_rate && mStreamInfo.channels) {
ALOGD("onQueueFilled: initially configuring decoder: %d Hz, %d channels",
mStreamInfo.sample_rate, mStreamInfo.channels);
notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
mOutputPortSettingsChange = AWAITING_DISABLED;
}
return;
}
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
notifyEmptyBufferDone(inHeader);
return;
}
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) {
status_t decoderErr = mFLACDecoder->parseMetadata(inBuffer, inBufferLength);
mInputBufferCount++;
if (decoderErr != OK && decoderErr != WOULD_BLOCK) {
ALOGE("onQueueFilled: FLACDecoder parseMetaData returns error %d", decoderErr);
status_t decoderErr = mFLACDecoder->decodeOneFrame(
inBuffer, inBufferLength, outBuffer, &outBufferSize);
if (decoderErr != OK) {
ALOGE("onQueueFilled: FLACDecoder decodeOneFrame returns error %d", decoderErr);
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL);
return;
}
mInputBufferCount++;
timeStamp = inHeader->nTimeStamp;
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
notifyEmptyBufferDone(inHeader);
if (decoderErr == WOULD_BLOCK) {
if (outBufferSize == 0) {
ALOGV("no output, trying again");
continue;
}
mStreamInfo = mFLACDecoder->getStreamInfo();
mHasStreamInfo = true;
// Only send out port settings changed event if both sample rate
// and numChannels are valid.
if (mStreamInfo.sample_rate && mStreamInfo.channels) {
ALOGD("onQueueFilled: initially configuring decoder: %d Hz, %d channels",
mStreamInfo.sample_rate, mStreamInfo.channels);
notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
mOutputPortSettingsChange = AWAITING_DISABLED;
} 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;
}
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) {
ALOGE("onQueueFilled: FLACDecoder decodeOneFrame returns error %d", decoderErr);
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL);
return;
}
mInputBufferCount++;
int64_t ts = inHeader->nTimeStamp;
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
notifyEmptyBufferDone(inHeader);
if (endOfInput) {
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
} else if (outBufferSize == 0) {
continue;
} 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());

@ -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,

@ -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,54 +355,54 @@ void SoftFlacEncoder::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;
BufferInfo *outInfo = *outQueue.begin();
OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
inQueue.erase(inQueue.begin());
inInfo->mOwnedByUs = false;
notifyEmptyBufferDone(inHeader);
FLAC__bool ok = true;
outHeader->nFilledLen = 0;
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
while ((!inQueue.empty() || mSawInputEOS) && !outQueue.empty()) {
if (!inQueue.empty()) {
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
outQueue.erase(outQueue.begin());
outInfo->mOwnedByUs = false;
notifyFillBufferDone(outHeader);
if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
ALOGV("saw EOS on buffer of size %u", inHeader->nFilledLen);
mSawInputEOS = true;
}
return;
}
if (inHeader->nFilledLen > kMaxInputBufferSize) {
ALOGE("input buffer too large (%d).", inHeader->nFilledLen);
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
return;
}
if (inHeader->nFilledLen > kMaxInputBufferSize) {
ALOGE("input buffer too large (%d).", inHeader->nFilledLen);
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
return;
}
assert(mNumChannels != 0);
mEncoderWriteData = true;
mEncoderReturnedEncodedData = false;
mEncoderReturnedNbBytes = 0;
mCurrentInputTimeStamp = inHeader->nTimeStamp;
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);
const unsigned nbInputFrames = inHeader->nFilledLen / (2 * mNumChannels);
const unsigned nbInputSamples = inHeader->nFilledLen / 2;
const OMX_S16 * const pcm16 = reinterpret_cast<OMX_S16 *>(inHeader->pBuffer);
CHECK_LE(nbInputSamples, 2 * kMaxNumSamplesPerFrame);
for (unsigned i=0 ; i < nbInputSamples ; i++) {
mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
}
ALOGV(" about to encode %u samples per channel", nbInputFrames);
ok = FLAC__stream_encoder_process_interleaved(
mFlacStreamEncoder,
mInputBufferPcm32,
nbInputFrames /*samples per channel*/ );
CHECK_LE(nbInputSamples, 2 * kMaxNumSamplesPerFrame);
for (unsigned i=0 ; i < nbInputSamples ; i++) {
mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
}
ALOGV(" about to encode %u samples per channel", nbInputFrames);
FLAC__bool ok = FLAC__stream_encoder_process_interleaved(
mFlacStreamEncoder,
mInputBufferPcm32,
nbInputFrames /*samples per channel*/ );
BufferInfo *outInfo = *outQueue.begin();
OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
if (ok) {
if (mEncoderReturnedEncodedData && (mEncoderReturnedNbBytes != 0)) {
@ -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);
memcpy(mHeader + mHeaderOffset, buffer, bytes);
mHeaderOffset += bytes;// will contain header size when finished receiving header
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);

@ -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);
};

@ -423,22 +423,16 @@ 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");
}
status_t err = addDataToBuffer(inBuffer, inBufferLen);
if (err != OK) {
ALOGW("decodeOneFrame: addDataToBuffer returns error %d", err);
return err;
if (inBufferLen != 0) {
status_t err = addDataToBuffer(inBuffer, inBufferLen);
if (err != OK) {
ALOGW("decodeOneFrame: addDataToBuffer returns error %d", err);
return err;
}
}
mWriteRequested = true;

Loading…
Cancel
Save