| /* |
| * Copyright (C) 2011 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 "FLACExtractor" |
| #include <utils/Log.h> |
| |
| #include "FLACExtractor.h" |
| // libFLAC parser |
| #include "FLAC/stream_decoder.h" |
| |
| #include <media/DataSource.h> |
| #include <media/MediaSource.h> |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/base64.h> |
| #include <media/stagefright/foundation/ByteUtils.h> |
| #include <media/stagefright/MediaBufferGroup.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/MetaData.h> |
| #include <media/stagefright/MediaBuffer.h> |
| |
| namespace android { |
| |
| // also exists in OggExtractor, candidate for moving to utility/support library? |
| static void extractAlbumArt( |
| const sp<MetaData> &fileMeta, const void *data, size_t size) { |
| ALOGV("extractAlbumArt from '%s'", (const char *)data); |
| |
| sp<ABuffer> flacBuffer = decodeBase64(AString((const char *)data, size)); |
| if (flacBuffer == NULL) { |
| ALOGE("malformed base64 encoded data."); |
| return; |
| } |
| |
| size_t flacSize = flacBuffer->size(); |
| uint8_t *flac = flacBuffer->data(); |
| ALOGV("got flac of size %zu", flacSize); |
| |
| uint32_t picType; |
| uint32_t typeLen; |
| uint32_t descLen; |
| uint32_t dataLen; |
| char type[128]; |
| |
| if (flacSize < 8) { |
| return; |
| } |
| |
| picType = U32_AT(flac); |
| |
| if (picType != 3) { |
| // This is not a front cover. |
| return; |
| } |
| |
| typeLen = U32_AT(&flac[4]); |
| if (typeLen > sizeof(type) - 1) { |
| return; |
| } |
| |
| // we've already checked above that flacSize >= 8 |
| if (flacSize - 8 < typeLen) { |
| return; |
| } |
| |
| memcpy(type, &flac[8], typeLen); |
| type[typeLen] = '\0'; |
| |
| ALOGV("picType = %d, type = '%s'", picType, type); |
| |
| if (!strcmp(type, "-->")) { |
| // This is not inline cover art, but an external url instead. |
| return; |
| } |
| |
| if (flacSize < 32 || flacSize - 32 < typeLen) { |
| return; |
| } |
| |
| descLen = U32_AT(&flac[8 + typeLen]); |
| if (flacSize - 32 - typeLen < descLen) { |
| return; |
| } |
| |
| dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]); |
| |
| // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0 |
| if (flacSize - 32 - typeLen - descLen < dataLen) { |
| return; |
| } |
| |
| ALOGV("got image data, %zu trailing bytes", |
| flacSize - 32 - typeLen - descLen - dataLen); |
| |
| fileMeta->setData( |
| kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen); |
| |
| fileMeta->setCString(kKeyAlbumArtMIME, type); |
| } |
| |
| // also exists in OggExtractor, candidate for moving to utility/support library? |
| static void parseVorbisComment( |
| const sp<MetaData> &fileMeta, const char *comment, size_t commentLength) |
| { |
| struct { |
| const char *const mTag; |
| uint32_t mKey; |
| } kMap[] = { |
| { "TITLE", kKeyTitle }, |
| { "ARTIST", kKeyArtist }, |
| { "ALBUMARTIST", kKeyAlbumArtist }, |
| { "ALBUM ARTIST", kKeyAlbumArtist }, |
| { "COMPILATION", kKeyCompilation }, |
| { "ALBUM", kKeyAlbum }, |
| { "COMPOSER", kKeyComposer }, |
| { "GENRE", kKeyGenre }, |
| { "AUTHOR", kKeyAuthor }, |
| { "TRACKNUMBER", kKeyCDTrackNumber }, |
| { "DISCNUMBER", kKeyDiscNumber }, |
| { "DATE", kKeyDate }, |
| { "YEAR", kKeyYear }, |
| { "LYRICIST", kKeyWriter }, |
| { "METADATA_BLOCK_PICTURE", kKeyAlbumArt }, |
| { "ANDROID_LOOP", kKeyAutoLoop }, |
| }; |
| |
| for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) { |
| size_t tagLen = strlen(kMap[j].mTag); |
| if (!strncasecmp(kMap[j].mTag, comment, tagLen) |
| && comment[tagLen] == '=') { |
| if (kMap[j].mKey == kKeyAlbumArt) { |
| extractAlbumArt( |
| fileMeta, |
| &comment[tagLen + 1], |
| commentLength - tagLen - 1); |
| } else if (kMap[j].mKey == kKeyAutoLoop) { |
| if (!strcasecmp(&comment[tagLen + 1], "true")) { |
| fileMeta->setInt32(kKeyAutoLoop, true); |
| } |
| } else { |
| fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]); |
| } |
| } |
| } |
| |
| } |
| |
| class FLACParser; |
| |
| class FLACSource : public MediaSource { |
| |
| public: |
| FLACSource( |
| const sp<DataSource> &dataSource, |
| const sp<MetaData> &trackMetadata); |
| |
| virtual status_t start(MetaData *params); |
| virtual status_t stop(); |
| virtual sp<MetaData> getFormat(); |
| |
| virtual status_t read( |
| MediaBuffer **buffer, const ReadOptions *options = NULL); |
| |
| protected: |
| virtual ~FLACSource(); |
| |
| private: |
| sp<DataSource> mDataSource; |
| sp<MetaData> mTrackMetadata; |
| sp<FLACParser> mParser; |
| bool mInitCheck; |
| bool mStarted; |
| |
| status_t init(); |
| |
| // no copy constructor or assignment |
| FLACSource(const FLACSource &); |
| FLACSource &operator=(const FLACSource &); |
| |
| }; |
| |
| // FLACParser wraps a C libFLAC parser aka stream decoder |
| |
| class FLACParser : public RefBase { |
| |
| public: |
| enum { |
| kMaxChannels = 8, |
| }; |
| |
| explicit FLACParser( |
| const sp<DataSource> &dataSource, |
| // If metadata pointers aren't provided, we don't fill them |
| const sp<MetaData> &fileMetadata = 0, |
| const sp<MetaData> &trackMetadata = 0); |
| |
| status_t initCheck() const { |
| return mInitCheck; |
| } |
| |
| // stream properties |
| unsigned getMaxBlockSize() const { |
| return mStreamInfo.max_blocksize; |
| } |
| unsigned getSampleRate() const { |
| return mStreamInfo.sample_rate; |
| } |
| unsigned getChannels() const { |
| return mStreamInfo.channels; |
| } |
| unsigned getBitsPerSample() const { |
| return mStreamInfo.bits_per_sample; |
| } |
| FLAC__uint64 getTotalSamples() const { |
| return mStreamInfo.total_samples; |
| } |
| |
| // media buffers |
| void allocateBuffers(); |
| void releaseBuffers(); |
| MediaBuffer *readBuffer() { |
| return readBuffer(false, 0LL); |
| } |
| MediaBuffer *readBuffer(FLAC__uint64 sample) { |
| return readBuffer(true, sample); |
| } |
| |
| protected: |
| virtual ~FLACParser(); |
| |
| private: |
| sp<DataSource> mDataSource; |
| sp<MetaData> mFileMetadata; |
| sp<MetaData> mTrackMetadata; |
| bool mInitCheck; |
| |
| // media buffers |
| size_t mMaxBufferSize; |
| MediaBufferGroup *mGroup; |
| void (*mCopy)(short *dst, const int * src[kMaxChannels], unsigned nSamples, unsigned nChannels); |
| |
| // handle to underlying libFLAC parser |
| FLAC__StreamDecoder *mDecoder; |
| |
| // current position within the data source |
| off64_t mCurrentPos; |
| bool mEOF; |
| |
| // cached when the STREAMINFO metadata is parsed by libFLAC |
| FLAC__StreamMetadata_StreamInfo mStreamInfo; |
| bool mStreamInfoValid; |
| |
| // cached when a decoded PCM block is "written" by libFLAC parser |
| bool mWriteRequested; |
| bool mWriteCompleted; |
| FLAC__FrameHeader mWriteHeader; |
| FLAC__int32 const * mWriteBuffer[kMaxChannels]; |
| |
| // most recent error reported by libFLAC parser |
| FLAC__StreamDecoderErrorStatus mErrorStatus; |
| |
| status_t init(); |
| MediaBuffer *readBuffer(bool doSeek, FLAC__uint64 sample); |
| |
| // no copy constructor or assignment |
| FLACParser(const FLACParser &); |
| FLACParser &operator=(const FLACParser &); |
| |
| // FLAC parser callbacks as C++ instance methods |
| FLAC__StreamDecoderReadStatus readCallback( |
| FLAC__byte buffer[], size_t *bytes); |
| FLAC__StreamDecoderSeekStatus seekCallback( |
| FLAC__uint64 absolute_byte_offset); |
| FLAC__StreamDecoderTellStatus tellCallback( |
| FLAC__uint64 *absolute_byte_offset); |
| FLAC__StreamDecoderLengthStatus lengthCallback( |
| FLAC__uint64 *stream_length); |
| FLAC__bool eofCallback(); |
| FLAC__StreamDecoderWriteStatus writeCallback( |
| const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); |
| void metadataCallback(const FLAC__StreamMetadata *metadata); |
| void errorCallback(FLAC__StreamDecoderErrorStatus status); |
| |
| // FLAC parser callbacks as C-callable functions |
| static FLAC__StreamDecoderReadStatus read_callback( |
| const FLAC__StreamDecoder *decoder, |
| FLAC__byte buffer[], size_t *bytes, |
| void *client_data); |
| static FLAC__StreamDecoderSeekStatus seek_callback( |
| const FLAC__StreamDecoder *decoder, |
| FLAC__uint64 absolute_byte_offset, |
| void *client_data); |
| static FLAC__StreamDecoderTellStatus tell_callback( |
| const FLAC__StreamDecoder *decoder, |
| FLAC__uint64 *absolute_byte_offset, |
| void *client_data); |
| static FLAC__StreamDecoderLengthStatus length_callback( |
| const FLAC__StreamDecoder *decoder, |
| FLAC__uint64 *stream_length, |
| void *client_data); |
| static FLAC__bool eof_callback( |
| const FLAC__StreamDecoder *decoder, |
| void *client_data); |
| static FLAC__StreamDecoderWriteStatus write_callback( |
| const FLAC__StreamDecoder *decoder, |
| const FLAC__Frame *frame, const FLAC__int32 * const buffer[], |
| void *client_data); |
| static void metadata_callback( |
| const FLAC__StreamDecoder *decoder, |
| const FLAC__StreamMetadata *metadata, |
| void *client_data); |
| static void error_callback( |
| const FLAC__StreamDecoder *decoder, |
| FLAC__StreamDecoderErrorStatus status, |
| void *client_data); |
| |
| }; |
| |
| // The FLAC parser calls our C++ static callbacks using C calling conventions, |
| // inside FLAC__stream_decoder_process_until_end_of_metadata |
| // and FLAC__stream_decoder_process_single. |
| // We immediately then call our corresponding C++ instance methods |
| // with the same parameter list, but discard redundant information. |
| |
| FLAC__StreamDecoderReadStatus FLACParser::read_callback( |
| const FLAC__StreamDecoder * /* decoder */, FLAC__byte buffer[], |
| size_t *bytes, void *client_data) |
| { |
| return ((FLACParser *) client_data)->readCallback(buffer, bytes); |
| } |
| |
| FLAC__StreamDecoderSeekStatus FLACParser::seek_callback( |
| const FLAC__StreamDecoder * /* decoder */, |
| FLAC__uint64 absolute_byte_offset, void *client_data) |
| { |
| return ((FLACParser *) client_data)->seekCallback(absolute_byte_offset); |
| } |
| |
| FLAC__StreamDecoderTellStatus FLACParser::tell_callback( |
| const FLAC__StreamDecoder * /* decoder */, |
| FLAC__uint64 *absolute_byte_offset, void *client_data) |
| { |
| return ((FLACParser *) client_data)->tellCallback(absolute_byte_offset); |
| } |
| |
| FLAC__StreamDecoderLengthStatus FLACParser::length_callback( |
| const FLAC__StreamDecoder * /* decoder */, |
| FLAC__uint64 *stream_length, void *client_data) |
| { |
| return ((FLACParser *) client_data)->lengthCallback(stream_length); |
| } |
| |
| FLAC__bool FLACParser::eof_callback( |
| const FLAC__StreamDecoder * /* decoder */, void *client_data) |
| { |
| return ((FLACParser *) client_data)->eofCallback(); |
| } |
| |
| FLAC__StreamDecoderWriteStatus FLACParser::write_callback( |
| const FLAC__StreamDecoder * /* decoder */, const FLAC__Frame *frame, |
| const FLAC__int32 * const buffer[], void *client_data) |
| { |
| return ((FLACParser *) client_data)->writeCallback(frame, buffer); |
| } |
| |
| void FLACParser::metadata_callback( |
| const FLAC__StreamDecoder * /* decoder */, |
| const FLAC__StreamMetadata *metadata, void *client_data) |
| { |
| ((FLACParser *) client_data)->metadataCallback(metadata); |
| } |
| |
| void FLACParser::error_callback( |
| const FLAC__StreamDecoder * /* decoder */, |
| FLAC__StreamDecoderErrorStatus status, void *client_data) |
| { |
| ((FLACParser *) client_data)->errorCallback(status); |
| } |
| |
| // These are the corresponding callbacks with C++ calling conventions |
| |
| FLAC__StreamDecoderReadStatus FLACParser::readCallback( |
| FLAC__byte buffer[], size_t *bytes) |
| { |
| size_t requested = *bytes; |
| ssize_t actual = mDataSource->readAt(mCurrentPos, buffer, requested); |
| if (0 > actual) { |
| *bytes = 0; |
| return FLAC__STREAM_DECODER_READ_STATUS_ABORT; |
| } else if (0 == actual) { |
| *bytes = 0; |
| mEOF = true; |
| return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; |
| } else { |
| assert(actual <= requested); |
| *bytes = actual; |
| mCurrentPos += actual; |
| return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; |
| } |
| } |
| |
| FLAC__StreamDecoderSeekStatus FLACParser::seekCallback( |
| FLAC__uint64 absolute_byte_offset) |
| { |
| mCurrentPos = absolute_byte_offset; |
| mEOF = false; |
| return FLAC__STREAM_DECODER_SEEK_STATUS_OK; |
| } |
| |
| FLAC__StreamDecoderTellStatus FLACParser::tellCallback( |
| FLAC__uint64 *absolute_byte_offset) |
| { |
| *absolute_byte_offset = mCurrentPos; |
| return FLAC__STREAM_DECODER_TELL_STATUS_OK; |
| } |
| |
| FLAC__StreamDecoderLengthStatus FLACParser::lengthCallback( |
| FLAC__uint64 *stream_length) |
| { |
| off64_t size; |
| if (OK == mDataSource->getSize(&size)) { |
| *stream_length = size; |
| return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; |
| } else { |
| return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; |
| } |
| } |
| |
| FLAC__bool FLACParser::eofCallback() |
| { |
| return mEOF; |
| } |
| |
| FLAC__StreamDecoderWriteStatus FLACParser::writeCallback( |
| const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) |
| { |
| if (mWriteRequested) { |
| mWriteRequested = false; |
| // FLAC parser 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; |
| } else { |
| ALOGE("FLACParser::writeCallback unexpected"); |
| return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; |
| } |
| } |
| |
| void FLACParser::metadataCallback(const FLAC__StreamMetadata *metadata) |
| { |
| switch (metadata->type) { |
| case FLAC__METADATA_TYPE_STREAMINFO: |
| if (!mStreamInfoValid) { |
| mStreamInfo = metadata->data.stream_info; |
| mStreamInfoValid = true; |
| } else { |
| ALOGE("FLACParser::metadataCallback unexpected STREAMINFO"); |
| } |
| break; |
| 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("FLACParser::metadataCallback unexpected type %u", metadata->type); |
| break; |
| } |
| } |
| |
| void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) |
| { |
| ALOGE("FLACParser::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[FLACParser::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[FLACParser::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[FLACParser::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[FLACParser::kMaxChannels], |
| unsigned nSamples, |
| unsigned /* nChannels */) { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| *dst++ = src[0][i]; |
| } |
| } |
| |
| static void copyStereo16( |
| short *dst, |
| const int * src[FLACParser::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[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels) |
| { |
| for (unsigned i = 0; i < nSamples; ++i) { |
| for (unsigned c = 0; c < nChannels; ++c) { |
| *dst++ = src[c][i]; |
| } |
| } |
| } |
| |
| // 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger |
| |
| static void copyMono24( |
| short *dst, |
| const int * src[FLACParser::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[FLACParser::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[FLACParser::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 copyTrespass( |
| short * /* dst */, |
| const int *[FLACParser::kMaxChannels] /* src */, |
| unsigned /* nSamples */, |
| unsigned /* nChannels */) { |
| TRESPASS(); |
| } |
| |
| // FLACParser |
| |
| FLACParser::FLACParser( |
| const sp<DataSource> &dataSource, |
| const sp<MetaData> &fileMetadata, |
| const sp<MetaData> &trackMetadata) |
| : mDataSource(dataSource), |
| mFileMetadata(fileMetadata), |
| mTrackMetadata(trackMetadata), |
| mInitCheck(false), |
| mMaxBufferSize(0), |
| mGroup(NULL), |
| mCopy(copyTrespass), |
| mDecoder(NULL), |
| mCurrentPos(0LL), |
| mEOF(false), |
| mStreamInfoValid(false), |
| mWriteRequested(false), |
| mWriteCompleted(false), |
| mErrorStatus((FLAC__StreamDecoderErrorStatus) -1) |
| { |
| ALOGV("FLACParser::FLACParser"); |
| memset(&mStreamInfo, 0, sizeof(mStreamInfo)); |
| memset(&mWriteHeader, 0, sizeof(mWriteHeader)); |
| mInitCheck = init(); |
| } |
| |
| FLACParser::~FLACParser() |
| { |
| ALOGV("FLACParser::~FLACParser"); |
| if (mDecoder != NULL) { |
| FLAC__stream_decoder_delete(mDecoder); |
| mDecoder = NULL; |
| } |
| } |
| |
| status_t FLACParser::init() |
| { |
| // setup libFLAC parser |
| mDecoder = FLAC__stream_decoder_new(); |
| if (mDecoder == NULL) { |
| // The new should succeed, since probably all it does is a malloc |
| // that always succeeds in Android. But to avoid dependence on the |
| // libFLAC internals, we check and log here. |
| ALOGE("new failed"); |
| 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); |
| FLAC__StreamDecoderInitStatus initStatus; |
| initStatus = FLAC__stream_decoder_init_stream( |
| mDecoder, |
| read_callback, seek_callback, tell_callback, |
| length_callback, eof_callback, write_callback, |
| metadata_callback, error_callback, (void *) this); |
| if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) { |
| // A failure here probably indicates a programming error and so is |
| // unlikely to happen. But we check and log here similarly to above. |
| ALOGE("init_stream failed %d", initStatus); |
| return NO_INIT; |
| } |
| // parse all metadata |
| if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) { |
| ALOGE("end_of_metadata failed"); |
| return NO_INIT; |
| } |
| if (mStreamInfoValid) { |
| // check channel count |
| if (getChannels() == 0 || getChannels() > kMaxChannels) { |
| ALOGE("unsupported channel count %u", getChannels()); |
| return NO_INIT; |
| } |
| // check bit depth |
| switch (getBitsPerSample()) { |
| case 8: |
| case 16: |
| case 24: |
| break; |
| default: |
| ALOGE("unsupported bits per sample %u", getBitsPerSample()); |
| return NO_INIT; |
| } |
| // check sample rate |
| switch (getSampleRate()) { |
| case 8000: |
| case 11025: |
| case 12000: |
| case 16000: |
| case 22050: |
| case 24000: |
| case 32000: |
| case 44100: |
| case 48000: |
| case 88200: |
| case 96000: |
| break; |
| default: |
| ALOGE("unsupported sample rate %u", getSampleRate()); |
| return NO_INIT; |
| } |
| // 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 (unsigned i = 0; i < sizeof(table)/sizeof(table[0]); ++i) { |
| if (table[i].mChannels >= getChannels() && |
| table[i].mBitsPerSample == getBitsPerSample()) { |
| mCopy = table[i].mCopy; |
| break; |
| } |
| } |
| // populate track metadata |
| if (mTrackMetadata != 0) { |
| mTrackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); |
| mTrackMetadata->setInt32(kKeyChannelCount, getChannels()); |
| mTrackMetadata->setInt32(kKeySampleRate, getSampleRate()); |
| mTrackMetadata->setInt32(kKeyPcmEncoding, kAudioEncodingPcm16bit); |
| // sample rate is non-zero, so division by zero not possible |
| mTrackMetadata->setInt64(kKeyDuration, |
| (getTotalSamples() * 1000000LL) / getSampleRate()); |
| } |
| } else { |
| ALOGE("missing STREAMINFO"); |
| return NO_INIT; |
| } |
| if (mFileMetadata != 0) { |
| mFileMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_FLAC); |
| } |
| return OK; |
| } |
| |
| void FLACParser::allocateBuffers() |
| { |
| CHECK(mGroup == NULL); |
| mGroup = new MediaBufferGroup; |
| mMaxBufferSize = getMaxBlockSize() * getChannels() * sizeof(short); |
| mGroup->add_buffer(new MediaBuffer(mMaxBufferSize)); |
| } |
| |
| void FLACParser::releaseBuffers() |
| { |
| CHECK(mGroup != NULL); |
| delete mGroup; |
| mGroup = NULL; |
| } |
| |
| MediaBuffer *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) |
| { |
| mWriteRequested = true; |
| mWriteCompleted = false; |
| if (doSeek) { |
| // We implement the seek callback, so this works without explicit flush |
| if (!FLAC__stream_decoder_seek_absolute(mDecoder, sample)) { |
| ALOGE("FLACParser::readBuffer seek to sample %lld failed", (long long)sample); |
| return NULL; |
| } |
| ALOGV("FLACParser::readBuffer seek to sample %lld succeeded", (long long)sample); |
| } else { |
| if (!FLAC__stream_decoder_process_single(mDecoder)) { |
| ALOGE("FLACParser::readBuffer process_single failed"); |
| return NULL; |
| } |
| } |
| if (!mWriteCompleted) { |
| ALOGV("FLACParser::readBuffer write did not complete"); |
| return NULL; |
| } |
| // verify that block header keeps the promises made by STREAMINFO |
| unsigned blocksize = mWriteHeader.blocksize; |
| if (blocksize == 0 || blocksize > getMaxBlockSize()) { |
| ALOGE("FLACParser::readBuffer write invalid blocksize %u", blocksize); |
| return NULL; |
| } |
| if (mWriteHeader.sample_rate != getSampleRate() || |
| mWriteHeader.channels != getChannels() || |
| mWriteHeader.bits_per_sample != getBitsPerSample()) { |
| ALOGE("FLACParser::readBuffer write changed parameters mid-stream: %d/%d/%d -> %d/%d/%d", |
| getSampleRate(), getChannels(), getBitsPerSample(), |
| mWriteHeader.sample_rate, mWriteHeader.channels, mWriteHeader.bits_per_sample); |
| return NULL; |
| } |
| // acquire a media buffer |
| CHECK(mGroup != NULL); |
| MediaBuffer *buffer; |
| status_t err = mGroup->acquire_buffer(&buffer); |
| if (err != OK) { |
| return NULL; |
| } |
| size_t bufferSize = blocksize * getChannels() * sizeof(short); |
| CHECK(bufferSize <= mMaxBufferSize); |
| short *data = (short *) buffer->data(); |
| buffer->set_range(0, bufferSize); |
| // copy PCM from FLAC write buffer to our media buffer, with interleaving |
| (*mCopy)(data, mWriteBuffer, blocksize, getChannels()); |
| // fill in buffer metadata |
| CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); |
| FLAC__uint64 sampleNumber = mWriteHeader.number.sample_number; |
| int64_t timeUs = (1000000LL * sampleNumber) / getSampleRate(); |
| buffer->meta_data()->setInt64(kKeyTime, timeUs); |
| buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); |
| return buffer; |
| } |
| |
| // FLACsource |
| |
| FLACSource::FLACSource( |
| const sp<DataSource> &dataSource, |
| const sp<MetaData> &trackMetadata) |
| : mDataSource(dataSource), |
| mTrackMetadata(trackMetadata), |
| mParser(0), |
| mInitCheck(false), |
| mStarted(false) |
| { |
| ALOGV("FLACSource::FLACSource"); |
| mInitCheck = init(); |
| } |
| |
| FLACSource::~FLACSource() |
| { |
| ALOGV("~FLACSource::FLACSource"); |
| if (mStarted) { |
| stop(); |
| } |
| } |
| |
| status_t FLACSource::start(MetaData * /* params */) |
| { |
| ALOGV("FLACSource::start"); |
| |
| CHECK(!mStarted); |
| mParser->allocateBuffers(); |
| mStarted = true; |
| |
| return OK; |
| } |
| |
| status_t FLACSource::stop() |
| { |
| ALOGV("FLACSource::stop"); |
| |
| CHECK(mStarted); |
| mParser->releaseBuffers(); |
| mStarted = false; |
| |
| return OK; |
| } |
| |
| sp<MetaData> FLACSource::getFormat() |
| { |
| return mTrackMetadata; |
| } |
| |
| status_t FLACSource::read( |
| MediaBuffer **outBuffer, const ReadOptions *options) |
| { |
| MediaBuffer *buffer; |
| // process an optional seek request |
| int64_t seekTimeUs; |
| ReadOptions::SeekMode mode; |
| if ((NULL != options) && options->getSeekTo(&seekTimeUs, &mode)) { |
| FLAC__uint64 sample; |
| if (seekTimeUs <= 0LL) { |
| sample = 0LL; |
| } else { |
| // sample and total samples are both zero-based, and seek to EOF ok |
| sample = (seekTimeUs * mParser->getSampleRate()) / 1000000LL; |
| if (sample >= mParser->getTotalSamples()) { |
| sample = mParser->getTotalSamples(); |
| } |
| } |
| buffer = mParser->readBuffer(sample); |
| // otherwise read sequentially |
| } else { |
| buffer = mParser->readBuffer(); |
| } |
| *outBuffer = buffer; |
| return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM; |
| } |
| |
| status_t FLACSource::init() |
| { |
| ALOGV("FLACSource::init"); |
| // re-use the same track metadata passed into constructor from FLACExtractor |
| mParser = new FLACParser(mDataSource); |
| return mParser->initCheck(); |
| } |
| |
| // FLACExtractor |
| |
| FLACExtractor::FLACExtractor( |
| const sp<DataSource> &dataSource) |
| : mDataSource(dataSource), |
| mInitCheck(false) |
| { |
| ALOGV("FLACExtractor::FLACExtractor"); |
| mInitCheck = init(); |
| } |
| |
| FLACExtractor::~FLACExtractor() |
| { |
| ALOGV("~FLACExtractor::FLACExtractor"); |
| } |
| |
| size_t FLACExtractor::countTracks() |
| { |
| return mInitCheck == OK ? 1 : 0; |
| } |
| |
| sp<MediaSource> FLACExtractor::getTrack(size_t index) |
| { |
| if (mInitCheck != OK || index > 0) { |
| return NULL; |
| } |
| return new FLACSource(mDataSource, mTrackMetadata); |
| } |
| |
| sp<MetaData> FLACExtractor::getTrackMetaData( |
| size_t index, uint32_t /* flags */) { |
| if (mInitCheck != OK || index > 0) { |
| return NULL; |
| } |
| return mTrackMetadata; |
| } |
| |
| status_t FLACExtractor::init() |
| { |
| mFileMetadata = new MetaData; |
| mTrackMetadata = new MetaData; |
| // FLACParser will fill in the metadata for us |
| mParser = new FLACParser(mDataSource, mFileMetadata, mTrackMetadata); |
| return mParser->initCheck(); |
| } |
| |
| sp<MetaData> FLACExtractor::getMetaData() |
| { |
| return mFileMetadata; |
| } |
| |
| // Sniffer |
| |
| bool SniffFLAC( |
| const sp<DataSource> &source, String8 *mimeType, float *confidence, |
| sp<AMessage> *) |
| { |
| // first 4 is the signature word |
| // second 4 is the sizeof STREAMINFO |
| // 042 is the mandatory STREAMINFO |
| // no need to read rest of the header, as a premature EOF will be caught later |
| uint8_t header[4+4]; |
| if (source->readAt(0, header, sizeof(header)) != sizeof(header) |
| || memcmp("fLaC\0\0\0\042", header, 4+4)) |
| { |
| return false; |
| } |
| |
| *mimeType = MEDIA_MIMETYPE_AUDIO_FLAC; |
| *confidence = 0.5; |
| |
| return true; |
| } |
| |
| |
| extern "C" { |
| // This is the only symbol that needs to be exported |
| __attribute__ ((visibility ("default"))) |
| MediaExtractor::ExtractorDef GETEXTRACTORDEF() { |
| return { |
| MediaExtractor::EXTRACTORDEF_VERSION, |
| UUID("1364b048-cc45-4fda-9934-327d0ebf9829"), |
| 1, |
| "FLAC Extractor", |
| []( |
| const sp<DataSource> &source, |
| String8 *mimeType, |
| float *confidence, |
| sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc { |
| if (SniffFLAC(source, mimeType, confidence, meta)) { |
| return []( |
| const sp<DataSource> &source, |
| const sp<AMessage>& meta __unused) -> MediaExtractor* { |
| return new FLACExtractor(source);}; |
| } |
| return NULL; |
| } |
| }; |
| } |
| |
| } // extern "C" |
| |
| } // namespace android |