Make extractors separate libraries
instead of being integrated into libstagefright
Test: thoroughly
Change-Id: I11cdfde6d2daf248c8e0f99237db1c74be260eb0
diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp
new file mode 100644
index 0000000..5edc182
--- /dev/null
+++ b/media/extractors/flac/Android.bp
@@ -0,0 +1,44 @@
+cc_library_shared {
+
+ srcs: ["FLACExtractor.cpp"],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/include",
+ "external/flac/include",
+ ],
+
+ shared_libs: [
+ "libstagefright",
+ "libmedia",
+ "libstagefright_foundation",
+ "libutils",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libFLAC",
+ ],
+
+ name: "libflacextractor",
+ relative_install_path: "extractors",
+
+ compile_multilib: "first",
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-fvisibility=hidden",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ diag: {
+ cfi: true,
+ },
+ },
+
+}
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
new file mode 100644
index 0000000..dda8100
--- /dev/null
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -0,0 +1,1020 @@
+/*
+ * 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/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/Utils.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
diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/FLACExtractor.h
new file mode 100644
index 0000000..e945cf6
--- /dev/null
+++ b/media/extractors/flac/FLACExtractor.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef FLAC_EXTRACTOR_H_
+#define FLAC_EXTRACTOR_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class FLACParser;
+
+class FLACExtractor : public MediaExtractor {
+
+public:
+ // Extractor assumes ownership of source
+ explicit FLACExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+ virtual const char * name() { return "FLACExtractor"; }
+
+protected:
+ virtual ~FLACExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+ sp<FLACParser> mParser;
+ status_t mInitCheck;
+ sp<MetaData> mFileMetadata;
+
+ // There is only one track
+ sp<MetaData> mTrackMetadata;
+
+ status_t init();
+
+ FLACExtractor(const FLACExtractor &);
+ FLACExtractor &operator=(const FLACExtractor &);
+
+};
+
+bool SniffFLAC(const sp<DataSource> &source, String8 *mimeType,
+ float *confidence, sp<AMessage> *);
+
+} // namespace android
+
+#endif // FLAC_EXTRACTOR_H_
diff --git a/media/extractors/flac/MODULE_LICENSE_APACHE2 b/media/extractors/flac/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/extractors/flac/MODULE_LICENSE_APACHE2
diff --git a/media/extractors/flac/NOTICE b/media/extractors/flac/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/media/extractors/flac/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+