| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "FLACDecoder" |
| #include <utils/Log.h> |
| |
| #include "FLACDecoder.h" |
| |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/hexdump.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/MediaErrors.h> |
| #include <media/stagefright/MetaData.h> |
| |
| namespace android { |
| |
| // These are the corresponding callbacks with C++ calling conventions |
| FLAC__StreamDecoderReadStatus FLACDecoder::readCallback( |
| FLAC__byte buffer[], size_t *bytes) { |
| if (mBuffer == nullptr || mBufferLen == 0) { |
| *bytes = 0; |
| return FLAC__STREAM_DECODER_READ_STATUS_ABORT; |
| } |
| |
| size_t actual = *bytes; |
| if (actual > mBufferDataSize - mBufferPos) { |
| actual = mBufferDataSize - mBufferPos; |
| } |
| memcpy(buffer, mBuffer + mBufferPos, actual); |
| mBufferPos += actual; |
| *bytes = actual; |
| return (actual == 0 ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM |
| : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE); |
| } |
| |
| FLAC__StreamDecoderWriteStatus FLACDecoder::writeCallback( |
| const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) |
| { |
| if (!mWriteRequested) { |
| ALOGE("writeCallback: unexpected"); |
| return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; |
| } |
| |
| mWriteRequested = false; |
| // FLAC decoder doesn't free or realloc buffer until next frame or finish |
| mWriteHeader = frame->header; |
| memmove(mWriteBuffer, buffer, sizeof(const FLAC__int32 * const) * getChannels()); |
| mWriteCompleted = true; |
| return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; |
| } |
| |
| void FLACDecoder::metadataCallback(const FLAC__StreamMetadata *metadata) |
| { |
| switch (metadata->type) { |
| case FLAC__METADATA_TYPE_STREAMINFO: |
| { |
| if (mStreamInfoValid) { |
| ALOGE("metadataCallback: unexpected STREAMINFO"); |
| } else { |
| mStreamInfo = metadata->data.stream_info; |
| mStreamInfoValid = true; |
| } |
| break; |
| } |
| |
| /* TODO: enable metadata parsing below. |
| case FLAC__METADATA_TYPE_VORBIS_COMMENT: |
| { |
| const FLAC__StreamMetadata_VorbisComment *vc; |
| vc = &metadata->data.vorbis_comment; |
| for (FLAC__uint32 i = 0; i < vc->num_comments; ++i) { |
| FLAC__StreamMetadata_VorbisComment_Entry *vce; |
| vce = &vc->comments[i]; |
| if (mFileMetadata != 0 && vce->entry != NULL) { |
| parseVorbisComment(mFileMetadata, (const char *) vce->entry, |
| vce->length); |
| } |
| } |
| break; |
| } |
| |
| case FLAC__METADATA_TYPE_PICTURE: |
| { |
| if (mFileMetadata != 0) { |
| const FLAC__StreamMetadata_Picture *p = &metadata->data.picture; |
| mFileMetadata->setData(kKeyAlbumArt, |
| MetaData::TYPE_NONE, p->data, p->data_length); |
| mFileMetadata->setCString(kKeyAlbumArtMIME, p->mime_type); |
| } |
| break; |
| } |
| */ |
| |
| default: |
| ALOGW("metadataCallback: unexpected type %u", metadata->type); |
| break; |
| } |
| } |
| |
| void FLACDecoder::errorCallback(FLAC__StreamDecoderErrorStatus status) |
| { |
| ALOGE("errorCallback: status=%d", status); |
| mErrorStatus = status; |
| } |
| |
| // Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved. |
| // These are candidates for optimization if needed. |
| static void copyMono8( |
| short *dst, |
| const int * src[FLACDecoder::kMaxChannels], |
| unsigned nSamples, |
| unsigned /* nChannels */) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| *dst++ = src[0][i] << 8; |
| } |
| } |
| |
| static void copyStereo8( |
| short *dst, |
| const int * src[FLACDecoder::kMaxChannels], |
| unsigned nSamples, |
| unsigned /* nChannels */) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| *dst++ = src[0][i] << 8; |
| *dst++ = src[1][i] << 8; |
| } |
| } |
| |
| static void copyMultiCh8( |
| short *dst, |
| const int * src[FLACDecoder::kMaxChannels], |
| unsigned nSamples, |
| unsigned nChannels) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| for (unsigned c = 0; c < nChannels; ++c) { |
| *dst++ = src[c][i] << 8; |
| } |
| } |
| } |
| |
| static void copyMono16( |
| short *dst, |
| const int * src[FLACDecoder::kMaxChannels], |
| unsigned nSamples, |
| unsigned /* nChannels */) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| *dst++ = src[0][i]; |
| } |
| } |
| |
| static void copyStereo16( |
| short *dst, |
| const int * src[FLACDecoder::kMaxChannels], |
| unsigned nSamples, |
| unsigned /* nChannels */) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| *dst++ = src[0][i]; |
| *dst++ = src[1][i]; |
| } |
| } |
| |
| static void copyMultiCh16( |
| short *dst, |
| const int * src[FLACDecoder::kMaxChannels], |
| unsigned nSamples, |
| unsigned nChannels) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| for (unsigned c = 0; c < nChannels; ++c) { |
| *dst++ = src[c][i]; |
| } |
| } |
| } |
| |
| // TODO: 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger |
| static void copyMono24( |
| short *dst, |
| const int * src[FLACDecoder::kMaxChannels], |
| unsigned nSamples, |
| unsigned /* nChannels */) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| *dst++ = src[0][i] >> 8; |
| } |
| } |
| |
| static void copyStereo24( |
| short *dst, |
| const int * src[FLACDecoder::kMaxChannels], |
| unsigned nSamples, |
| unsigned /* nChannels */) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| *dst++ = src[0][i] >> 8; |
| *dst++ = src[1][i] >> 8; |
| } |
| } |
| |
| static void copyMultiCh24( |
| short *dst, |
| const int * src[FLACDecoder::kMaxChannels], |
| unsigned nSamples, |
| unsigned nChannels) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| for (unsigned c = 0; c < nChannels; ++c) { |
| *dst++ = src[c][i] >> 8; |
| } |
| } |
| } |
| |
| // static |
| sp<FLACDecoder> FLACDecoder::Create() { |
| sp<FLACDecoder> decoder = new FLACDecoder(); |
| if (decoder->init() != OK) { |
| return NULL; |
| } |
| return decoder; |
| } |
| |
| FLACDecoder::FLACDecoder() |
| : mDecoder(NULL), |
| mBuffer(NULL), |
| mBufferLen(0), |
| mBufferPos(0), |
| mBufferDataSize(0), |
| mStreamInfoValid(false), |
| mWriteRequested(false), |
| mWriteCompleted(false), |
| mErrorStatus((FLAC__StreamDecoderErrorStatus) -1), |
| mCopy(nullptr) { |
| ALOGV("ctor:"); |
| memset(&mStreamInfo, 0, sizeof(mStreamInfo)); |
| memset(&mWriteHeader, 0, sizeof(mWriteHeader)); |
| memset(&mWriteBuffer, 0, sizeof(mWriteBuffer)); |
| } |
| |
| FLACDecoder::~FLACDecoder() { |
| ALOGV("dtor:"); |
| if (mDecoder != NULL) { |
| FLAC__stream_decoder_delete(mDecoder); |
| mDecoder = NULL; |
| } |
| if (mBuffer != NULL) { |
| free(mBuffer); |
| } |
| } |
| |
| status_t FLACDecoder::init() { |
| ALOGV("init:"); |
| // setup libFLAC stream decoder |
| mDecoder = FLAC__stream_decoder_new(); |
| if (mDecoder == NULL) { |
| ALOGE("init: failed to create libFLAC stream decoder"); |
| return NO_INIT; |
| } |
| FLAC__stream_decoder_set_md5_checking(mDecoder, false); |
| FLAC__stream_decoder_set_metadata_ignore_all(mDecoder); |
| FLAC__stream_decoder_set_metadata_respond( |
| mDecoder, FLAC__METADATA_TYPE_STREAMINFO); |
| /* |
| FLAC__stream_decoder_set_metadata_respond( |
| mDecoder, FLAC__METADATA_TYPE_PICTURE); |
| FLAC__stream_decoder_set_metadata_respond( |
| mDecoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); |
| */ |
| static auto read_callback = |
| [] (const FLAC__StreamDecoder * /* decoder */, |
| FLAC__byte buffer[], |
| size_t *bytes, |
| void *client_data) -> FLAC__StreamDecoderReadStatus { |
| return ((FLACDecoder *) client_data)->readCallback(buffer, bytes); }; |
| |
| static auto write_callback = |
| [] (const FLAC__StreamDecoder * /* decoder */, |
| const FLAC__Frame *frame, |
| const FLAC__int32 * const buffer[], |
| void *client_data) -> FLAC__StreamDecoderWriteStatus { |
| return ((FLACDecoder *) client_data)->writeCallback(frame, buffer); }; |
| |
| static auto metadata_callback = |
| [] (const FLAC__StreamDecoder * /* decoder */, |
| const FLAC__StreamMetadata *metadata, |
| void *client_data) { |
| ((FLACDecoder *) client_data)->metadataCallback(metadata); }; |
| |
| static auto error_callback = |
| [] (const FLAC__StreamDecoder * /* decoder */, |
| FLAC__StreamDecoderErrorStatus status, |
| void *client_data) { |
| ((FLACDecoder *) client_data)->errorCallback(status); }; |
| |
| FLAC__StreamDecoderInitStatus initStatus = |
| FLAC__stream_decoder_init_stream( |
| mDecoder, |
| read_callback, |
| NULL /* seek_callback */, |
| NULL /* tell_callback */, |
| NULL /* length_callback */, |
| NULL /* eof_callback */, |
| write_callback, |
| metadata_callback, |
| error_callback, |
| (void *)this); |
| if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) { |
| ALOGE("init: init_stream failed, returned %d", initStatus); |
| return NO_INIT; |
| } |
| return OK; |
| } |
| |
| void FLACDecoder::flush() { |
| ALOGV("flush:"); |
| mBufferPos = 0; |
| mBufferDataSize = 0; |
| mStreamInfoValid = false; |
| if (!FLAC__stream_decoder_reset(mDecoder)) { |
| ALOGE("flush: failed to reset FLAC stream decoder"); |
| } |
| } |
| |
| status_t FLACDecoder::parseMetadata(const uint8_t *inBuffer, size_t inBufferLen) { |
| ALOGV("parseMetadata: input size(%zu)", inBufferLen); |
| //hexdump(inBuffer, inBufferLen); |
| |
| if (mStreamInfoValid) { |
| ALOGE("parseMetadata: already have full metadata blocks"); |
| return ERROR_MALFORMED; |
| } |
| |
| status_t err = addDataToBuffer(inBuffer, inBufferLen); |
| if (err != OK) { |
| ALOGE("parseMetadata: addDataToBuffer returns error %d", err); |
| return err; |
| } |
| |
| if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) { |
| if (!FLAC__stream_decoder_reset(mDecoder)) { |
| ALOGE("parseMetadata: failed to reset FLAC stream decoder"); |
| return FAILED_TRANSACTION; |
| } |
| mBufferPos = 0; |
| ALOGV("parseMetadata: do not have full metadata blocks yet"); |
| return WOULD_BLOCK; |
| } |
| |
| if (!mStreamInfoValid) { |
| ALOGE("parseMetadata: missing STREAMINFO"); |
| return ERROR_MALFORMED; |
| } |
| |
| // check block size |
| if (getMaxBlockSize() == 0) { |
| ALOGE("wrong max blocksize %u", getMaxBlockSize()); |
| mStreamInfoValid = false; |
| return ERROR_MALFORMED; |
| } |
| |
| // check channel count |
| if (getChannels() == 0 || getChannels() > kMaxChannels) { |
| ALOGE("unsupported channel count %u", getChannels()); |
| mStreamInfoValid = false; |
| return ERROR_MALFORMED; |
| } |
| |
| // check bit depth |
| switch (getBitsPerSample()) { |
| case 8: |
| case 16: |
| case 24: |
| break; |
| |
| default: |
| ALOGE("parseMetadata: unsupported bits per sample %u", getBitsPerSample()); |
| mStreamInfoValid = false; |
| return ERROR_MALFORMED; |
| } |
| |
| // configure the appropriate copy function, defaulting to trespass |
| static const struct { |
| unsigned mChannels; |
| unsigned mBitsPerSample; |
| void (*mCopy)(short *dst, const int * src[kMaxChannels], |
| unsigned nSamples, unsigned nChannels); |
| } table[] = { |
| { 1, 8, copyMono8 }, |
| { 2, 8, copyStereo8 }, |
| { 8, 8, copyMultiCh8 }, |
| { 1, 16, copyMono16 }, |
| { 2, 16, copyStereo16 }, |
| { 8, 16, copyMultiCh16 }, |
| { 1, 24, copyMono24 }, |
| { 2, 24, copyStereo24 }, |
| { 8, 24, copyMultiCh24 }, |
| }; |
| for (const auto &entry : table) { |
| if (entry.mChannels >= getChannels() && |
| entry.mBitsPerSample == getBitsPerSample()) { |
| mCopy = entry.mCopy; |
| break; |
| } |
| } |
| |
| // Now we have all metadata blocks. |
| mBufferPos = 0; |
| mBufferDataSize = 0; |
| |
| return OK; |
| } |
| |
| 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; |
| } |
| |
| mWriteRequested = true; |
| mWriteCompleted = false; |
| if (!FLAC__stream_decoder_process_single(mDecoder)) { |
| ALOGE("decodeOneFrame: process_single failed"); |
| return ERROR_MALFORMED; |
| } |
| if (!mWriteCompleted) { |
| ALOGV("decodeOneFrame: write did not complete"); |
| if (outBufferLen) { |
| *outBufferLen = 0; |
| } |
| return OK; |
| } |
| |
| // frame header should be consistent with STREAMINFO |
| unsigned blocksize = mWriteHeader.blocksize; |
| if (blocksize == 0 || blocksize > getMaxBlockSize()) { |
| ALOGE("decodeOneFrame: write invalid blocksize %u", blocksize); |
| return ERROR_MALFORMED; |
| } |
| if (mWriteHeader.sample_rate != getSampleRate() || |
| mWriteHeader.channels != getChannels() || |
| mWriteHeader.bits_per_sample != getBitsPerSample()) { |
| ALOGE("decodeOneFrame: parameters are changed mid-stream: %d/%d/%d -> %d/%d/%d", |
| getSampleRate(), getChannels(), getBitsPerSample(), |
| mWriteHeader.sample_rate, mWriteHeader.channels, mWriteHeader.bits_per_sample); |
| return ERROR_MALFORMED; |
| } |
| if (mWriteHeader.number_type != FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) { |
| ALOGE("decodeOneFrame: number type is %d, expected %d", |
| mWriteHeader.number_type, FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); |
| return ERROR_MALFORMED; |
| } |
| |
| size_t bufferSize = blocksize * getChannels() * sizeof(short); |
| if (bufferSize > *outBufferLen) { |
| ALOGW("decodeOneFrame: output buffer holds only partial frame %zu:%zu", |
| *outBufferLen, bufferSize); |
| blocksize = *outBufferLen / (getChannels() * sizeof(short)); |
| bufferSize = blocksize * getChannels() * sizeof(short); |
| } |
| |
| if (mCopy == nullptr) { |
| ALOGE("decodeOneFrame: format is not supported: channels(%d), BitsPerSample(%d)", |
| getChannels(), getBitsPerSample()); |
| return ERROR_UNSUPPORTED; |
| } |
| // copy PCM from FLAC write buffer to output buffer, with interleaving |
| (*mCopy)(outBuffer, mWriteBuffer, blocksize, getChannels()); |
| *outBufferLen = bufferSize; |
| return OK; |
| } |
| |
| status_t FLACDecoder::addDataToBuffer(const uint8_t *inBuffer, size_t inBufferLen) { |
| // mBufferPos should be no larger than mBufferDataSize |
| if (inBufferLen > SIZE_MAX - (mBufferDataSize - mBufferPos)) { |
| ALOGE("addDataToBuffer: input buffer is too large"); |
| return ERROR_MALFORMED; |
| } |
| |
| if (inBufferLen > mBufferLen - mBufferDataSize) { |
| if (mBufferPos > 0) { |
| memmove(mBuffer, mBuffer + mBufferPos, mBufferDataSize - mBufferPos); |
| mBufferDataSize -= mBufferPos; |
| mBufferPos = 0; |
| } |
| if (inBufferLen > mBufferLen - mBufferDataSize) { |
| mBuffer = (uint8_t*)realloc(mBuffer, mBufferDataSize + inBufferLen); |
| if (mBuffer == nullptr) { |
| mBufferDataSize = 0; |
| mBufferLen = 0; |
| ALOGE("decodeOneFrame: failed to allocate memory for input buffer"); |
| return NO_MEMORY; |
| } |
| mBufferLen = mBufferDataSize + inBufferLen; |
| } |
| } |
| |
| memcpy(mBuffer + mBufferDataSize, inBuffer, inBufferLen); |
| mBufferDataSize += inBufferLen; |
| return OK; |
| } |
| |
| } // namespace android |