summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/stagefright/DataSource.h2
-rw-r--r--include/media/stagefright/MediaExtractor.h4
-rw-r--r--include/media/stagefright/MetaData.h9
-rw-r--r--media/libstagefright/Android.mk10
-rw-r--r--media/libstagefright/AudioPlayer.cpp7
-rw-r--r--media/libstagefright/DataSource.cpp23
-rw-r--r--media/libstagefright/FileSource.cpp5
-rw-r--r--media/libstagefright/MP3Extractor.cpp61
-rw-r--r--media/libstagefright/MediaExtractor.cpp21
-rw-r--r--media/libstagefright/StagefrightMediaScanner.cpp163
-rw-r--r--media/libstagefright/StagefrightMetadataRetriever.cpp174
-rw-r--r--media/libstagefright/codecs/avc/dec/AVCDecoder.cpp2
-rw-r--r--media/libstagefright/id3/Android.mk27
-rw-r--r--media/libstagefright/id3/ID3.cpp465
-rw-r--r--media/libstagefright/id3/testid3.cpp156
-rw-r--r--media/libstagefright/include/ID3.h87
-rw-r--r--media/libstagefright/include/MP3Extractor.h2
-rw-r--r--media/libstagefright/include/StagefrightMetadataRetriever.h38
18 files changed, 1193 insertions, 63 deletions
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index b843cd9..f88666a 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -31,6 +31,8 @@ class String8;
class DataSource : public RefBase {
public:
+ static sp<DataSource> CreateFromURI(const char *uri);
+
DataSource() {}
virtual status_t initCheck() const = 0;
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index d56d4b3..4bc996e 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -43,6 +43,10 @@ public:
virtual sp<MetaData> getTrackMetaData(
size_t index, uint32_t flags = 0) = 0;
+ // Return container specific meta-data. The default implementation
+ // returns an empty metadata object.
+ virtual sp<MetaData> getMetaData();
+
protected:
MediaExtractor() {}
virtual ~MediaExtractor() {}
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index c6ac6c2..ef30b02 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -48,6 +48,15 @@ enum {
kKeyBufferID = 'bfID',
kKeyMaxInputSize = 'inpS',
kKeyThumbnailTime = 'thbT', // int64_t (usecs)
+
+ kKeyAlbum = 'albu', // cstring
+ kKeyArtist = 'arti', // cstring
+ kKeyComposer = 'comp', // cstring
+ kKeyGenre = 'genr', // cstring
+ kKeyTitle = 'titl', // cstring
+ kKeyYear = 'year', // cstring
+ kKeyAlbumArt = 'albA', // compressed image data
+ kKeyAlbumArtMIME = 'alAM', // cstring
};
enum {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 30b4506..e36e78c 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -44,14 +44,17 @@ endif
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
- $(TOP)/external/opencore/android
+ $(TOP)/external/opencore/android \
+ $(TOP)/external/tremor/Tremor
LOCAL_SHARED_LIBRARIES := \
libbinder \
libmedia \
libutils \
libcutils \
- libui
+ libui \
+ libsonivox \
+ libvorbisidec
ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
@@ -62,7 +65,8 @@ LOCAL_STATIC_LIBRARIES := \
libstagefright_amrwbdec \
libstagefright_avcdec \
libstagefright_m4vh263dec \
- libstagefright_mp3dec
+ libstagefright_mp3dec \
+ libstagefright_id3
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 4280683..14842c0 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -133,13 +133,14 @@ void AudioPlayer::stop() {
if (mAudioSink.get() != NULL) {
mAudioSink->stop();
+ mAudioSink->close();
} else {
mAudioTrack->stop();
delete mAudioTrack;
mAudioTrack = NULL;
}
-
+
// Make sure to release any buffer we hold onto so that the
// source is able to stop().
if (mInputBuffer != NULL) {
@@ -150,7 +151,7 @@ void AudioPlayer::stop() {
}
mSource->stop();
-
+
mNumFramesPlayed = 0;
mPositionTimeMediaUs = -1;
mPositionTimeRealUs = -1;
@@ -259,7 +260,7 @@ void AudioPlayer::fillBuffer(void *data, size_t size) {
mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
mInputBuffer->range_length() - copy);
-
+
size_done += copy;
size_remaining -= copy;
}
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 2a6dbc4..741e5e0 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -19,7 +19,10 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
+#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/String8.h>
@@ -91,4 +94,24 @@ void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffWAV);
}
+// static
+sp<DataSource> DataSource::CreateFromURI(const char *uri) {
+ sp<DataSource> source;
+ if (!strncasecmp("file://", uri, 7)) {
+ source = new FileSource(uri + 7);
+ } else if (!strncasecmp("http://", uri, 7)) {
+ source = new HTTPDataSource(uri);
+ source = new CachingDataSource(source, 64 * 1024, 10);
+ } else {
+ // Assume it's a filename.
+ source = new FileSource(uri);
+ }
+
+ if (source == NULL || source->initCheck() != OK) {
+ return NULL;
+ }
+
+ return source;
+}
+
} // namespace android
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index 37c2450..b6f1af2 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -58,7 +58,10 @@ ssize_t FileSource::readAt(off_t offset, void *data, size_t size) {
}
int err = fseeko(mFile, offset + mOffset, SEEK_SET);
- CHECK(err != -1);
+ if (err < 0) {
+ LOGE("seek to %lld failed", offset + mOffset);
+ return UNKNOWN_ERROR;
+ }
return fread(data, 1, size, mFile);
}
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index b8e76fd..5df1e00 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -20,6 +20,8 @@
#include "include/MP3Extractor.h"
+#include "include/ID3.h"
+
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
@@ -667,7 +669,7 @@ status_t MP3Source::read(
}
// Lost sync.
- LOGW("lost sync!\n");
+ LOGV("lost sync!\n");
off_t pos = mCurrentPos;
if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
@@ -706,6 +708,63 @@ status_t MP3Source::read(
return OK;
}
+sp<MetaData> MP3Extractor::getMetaData() {
+ sp<MetaData> meta = new MetaData;
+
+ meta->setCString(kKeyMIMEType, "audio/mpeg");
+
+ ID3 id3(mDataSource);
+
+ if (!id3.isValid()) {
+ return meta;
+ }
+
+ struct Map {
+ int key;
+ const char *tag1;
+ const char *tag2;
+ };
+ static const Map kMap[] = {
+ { kKeyAlbum, "TALB", "TAL" },
+ { kKeyArtist, "TPE1", "TP1" },
+ { kKeyComposer, "TCOM", "TCM" },
+ { kKeyGenre, "TCON", "TCO" },
+ { kKeyTitle, "TALB", "TAL" },
+ { kKeyYear, "TYE", "TYER" },
+ };
+ static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
+
+ for (size_t i = 0; i < kNumMapEntries; ++i) {
+ ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1);
+ if (it->done()) {
+ delete it;
+ it = new ID3::Iterator(id3, kMap[i].tag2);
+ }
+
+ if (it->done()) {
+ delete it;
+ continue;
+ }
+
+ String8 s;
+ it->getString(&s);
+ delete it;
+
+ meta->setCString(kMap[i].key, s);
+ }
+
+ size_t dataSize;
+ String8 mime;
+ const void *data = id3.getAlbumArt(&dataSize, &mime);
+
+ if (data) {
+ meta->setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
+ meta->setCString(kKeyAlbumArtMIME, mime.string());
+ }
+
+ return meta;
+}
+
bool SniffMP3(
const sp<DataSource> &source, String8 *mimeType, float *confidence) {
off_t pos = 0;
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 9d3deb7..e46f00e 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -23,16 +23,18 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
-#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/DataSource.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
#include <utils/String8.h>
namespace android {
+sp<MetaData> MediaExtractor::getMetaData() {
+ return new MetaData;
+}
+
// static
sp<MediaExtractor> MediaExtractor::Create(
const sp<DataSource> &source, const char *mime) {
@@ -40,7 +42,7 @@ sp<MediaExtractor> MediaExtractor::Create(
if (mime == NULL) {
float confidence;
if (!source->sniff(&tmp, &confidence)) {
- LOGE("FAILED to autodetect media content.");
+ LOGV("FAILED to autodetect media content.");
return NULL;
}
@@ -68,16 +70,7 @@ sp<MediaExtractor> MediaExtractor::Create(
// static
sp<MediaExtractor> MediaExtractor::CreateFromURI(
const char *uri, const char *mime) {
- sp<DataSource> source;
- if (!strncasecmp("file://", uri, 7)) {
- source = new FileSource(uri + 7);
- } else if (!strncasecmp("http://", uri, 7)) {
- source = new HTTPDataSource(uri);
- source = new CachingDataSource(source, 64 * 1024, 10);
- } else {
- // Assume it's a filename.
- source = new FileSource(uri);
- }
+ sp<DataSource> source = DataSource::CreateFromURI(uri);
if (source == NULL || source->initCheck() != OK) {
return NULL;
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 9b41929..4815db2 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -14,10 +14,21 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StagefrightMediaScanner"
+#include <utils/Log.h>
+
#include <media/stagefright/StagefrightMediaScanner.h>
#include "include/StagefrightMetadataRetriever.h"
+// Sonivox includes
+#include <libsonivox/eas.h>
+
+// Ogg Vorbis includes
+#include "ivorbiscodec.h"
+#include "ivorbisfile.h"
+
namespace android {
StagefrightMediaScanner::StagefrightMediaScanner()
@@ -26,12 +37,145 @@ StagefrightMediaScanner::StagefrightMediaScanner()
StagefrightMediaScanner::~StagefrightMediaScanner() {}
+static bool FileHasAcceptableExtension(const char *extension) {
+ static const char *kValidExtensions[] = {
+ ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
+ ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
+ ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota"
+ };
+ static const size_t kNumValidExtensions =
+ sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
+
+ for (size_t i = 0; i < kNumValidExtensions; ++i) {
+ if (!strcasecmp(extension, kValidExtensions[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static status_t HandleMIDI(
+ const char *filename, MediaScannerClient *client) {
+ // get the library configuration and do sanity check
+ const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config();
+ if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
+ LOGE("EAS library/header mismatch\n");
+ return UNKNOWN_ERROR;
+ }
+ EAS_I32 temp;
+
+ // spin up a new EAS engine
+ EAS_DATA_HANDLE easData = NULL;
+ EAS_HANDLE easHandle = NULL;
+ EAS_RESULT result = EAS_Init(&easData);
+ if (result == EAS_SUCCESS) {
+ EAS_FILE file;
+ file.path = filename;
+ file.fd = 0;
+ file.offset = 0;
+ file.length = 0;
+ result = EAS_OpenFile(easData, &file, &easHandle);
+ }
+ if (result == EAS_SUCCESS) {
+ result = EAS_Prepare(easData, easHandle);
+ }
+ if (result == EAS_SUCCESS) {
+ result = EAS_ParseMetaData(easData, easHandle, &temp);
+ }
+ if (easHandle) {
+ EAS_CloseFile(easData, easHandle);
+ }
+ if (easData) {
+ EAS_Shutdown(easData);
+ }
+
+ if (result != EAS_SUCCESS) {
+ return UNKNOWN_ERROR;
+ }
+
+ char buffer[20];
+ sprintf(buffer, "%ld", temp);
+ if (!client->addStringTag("duration", buffer)) return UNKNOWN_ERROR;
+
+ return OK;
+}
+
+static status_t HandleOGG(
+ const char *filename, MediaScannerClient *client) {
+ int duration;
+
+ FILE *file = fopen(filename,"r");
+ if (!file)
+ return UNKNOWN_ERROR;
+
+ OggVorbis_File vf;
+ if (ov_open(file, &vf, NULL, 0) < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ char **ptr=ov_comment(&vf,-1)->user_comments;
+ while(*ptr){
+ char *val = strstr(*ptr, "=");
+ if (val) {
+ int keylen = val++ - *ptr;
+ char key[keylen + 1];
+ strncpy(key, *ptr, keylen);
+ key[keylen] = 0;
+ if (!client->addStringTag(key, val)) goto failure;
+ }
+ ++ptr;
+ }
+
+ // Duration
+ duration = ov_time_total(&vf, -1);
+ if (duration > 0) {
+ char buffer[20];
+ sprintf(buffer, "%d", duration);
+ if (!client->addStringTag("duration", buffer)) goto failure;
+ }
+
+ ov_clear(&vf); // this also closes the FILE
+ return OK;
+
+failure:
+ ov_clear(&vf); // this also closes the FILE
+ return UNKNOWN_ERROR;
+}
+
status_t StagefrightMediaScanner::processFile(
const char *path, const char *mimeType,
MediaScannerClient &client) {
client.setLocale(locale());
client.beginFile();
+ const char *extension = strrchr(path, '.');
+
+ if (!extension) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (!FileHasAcceptableExtension(extension)) {
+ client.endFile();
+
+ return UNKNOWN_ERROR;
+ }
+
+ if (!strcasecmp(extension, ".mid")
+ || !strcasecmp(extension, ".smf")
+ || !strcasecmp(extension, ".imy")
+ || !strcasecmp(extension, ".midi")
+ || !strcasecmp(extension, ".xmf")
+ || !strcasecmp(extension, ".rtttl")
+ || !strcasecmp(extension, ".rtx")
+ || !strcasecmp(extension, ".ota")) {
+ return HandleMIDI(path, &client);
+ }
+
+ if (!strcasecmp(extension, ".ogg")) {
+ return HandleOGG(path, &client);
+ }
+
if (mRetriever->setDataSource(path) == OK
&& mRetriever->setMode(
METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) {
@@ -66,12 +210,27 @@ status_t StagefrightMediaScanner::processFile(
}
char *StagefrightMediaScanner::extractAlbumArt(int fd) {
- if (mRetriever->setDataSource(fd, 0, 0) == OK
+ off_t size = lseek(fd, 0, SEEK_END);
+ if (size < 0) {
+ return NULL;
+ }
+ lseek(fd, 0, SEEK_SET);
+
+ if (mRetriever->setDataSource(fd, 0, size) == OK
&& mRetriever->setMode(
METADATA_MODE_FRAME_CAPTURE_ONLY) == OK) {
MediaAlbumArt *art = mRetriever->extractAlbumArt();
- // TODO: figure out what format the result should be in.
+ if (art != NULL) {
+ char *data = (char *)malloc(art->mSize + 4);
+ *(int32_t *)data = art->mSize;
+ memcpy(&data[4], art->mData, art->mSize);
+
+ delete art;
+ art = NULL;
+
+ return data;
+ }
}
return NULL;
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 128e776..be4a9d9 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -1,19 +1,18 @@
/*
-**
-** Copyright 2009, 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.
-*/
+ * Copyright (C) 2009 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 "StagefrightMetadataRetriever"
@@ -33,7 +32,9 @@
namespace android {
-StagefrightMetadataRetriever::StagefrightMetadataRetriever() {
+StagefrightMetadataRetriever::StagefrightMetadataRetriever()
+ : mParsedMetaData(false),
+ mAlbumArt(NULL) {
LOGV("StagefrightMetadataRetriever()");
DataSource::RegisterDefaultSniffers();
@@ -42,23 +43,66 @@ StagefrightMetadataRetriever::StagefrightMetadataRetriever() {
StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
LOGV("~StagefrightMetadataRetriever()");
+
+ delete mAlbumArt;
+ mAlbumArt = NULL;
+
mClient.disconnect();
}
status_t StagefrightMetadataRetriever::setDataSource(const char *uri) {
LOGV("setDataSource(%s)", uri);
- mExtractor = MediaExtractor::CreateFromURI(uri);
+ mParsedMetaData = false;
+ mMetaData.clear();
+ delete mAlbumArt;
+ mAlbumArt = NULL;
+
+ mSource = DataSource::CreateFromURI(uri);
+
+ if (mSource == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ mExtractor = MediaExtractor::Create(mSource);
+
+ if (mExtractor == NULL) {
+ mSource.clear();
- return mExtractor.get() != NULL ? OK : UNKNOWN_ERROR;
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
}
+// Warning caller retains ownership of the filedescriptor! Dup it if necessary.
status_t StagefrightMetadataRetriever::setDataSource(
int fd, int64_t offset, int64_t length) {
+ fd = dup(fd);
+
LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
- mExtractor = MediaExtractor::Create(
- new FileSource(fd, offset, length));
+ mParsedMetaData = false;
+ mMetaData.clear();
+ delete mAlbumArt;
+ mAlbumArt = NULL;
+
+ mSource = new FileSource(fd, offset, length);
+
+ status_t err;
+ if ((err = mSource->initCheck()) != OK) {
+ mSource.clear();
+
+ return err;
+ }
+
+ mExtractor = MediaExtractor::Create(mSource);
+
+ if (mExtractor == NULL) {
+ mSource.clear();
+
+ return UNKNOWN_ERROR;
+ }
return OK;
}
@@ -184,14 +228,98 @@ VideoFrame *StagefrightMetadataRetriever::captureFrame() {
MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
+ if (mExtractor == NULL) {
+ return NULL;
+ }
+
+ if (!mParsedMetaData) {
+ parseMetaData();
+
+ mParsedMetaData = true;
+ }
+
+ if (mAlbumArt) {
+ return new MediaAlbumArt(*mAlbumArt);
+ }
+
return NULL;
}
const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
- LOGV("extractMetadata %d (extractor: %s)",
- keyCode, mExtractor.get() != NULL ? "YES" : "NO");
+ LOGV("extractMetadata %d", keyCode);
- return NULL;
+ if (mExtractor == NULL) {
+ return NULL;
+ }
+
+ if (!mParsedMetaData) {
+ parseMetaData();
+
+ mParsedMetaData = true;
+ }
+
+ ssize_t index = mMetaData.indexOfKey(keyCode);
+
+ if (index < 0) {
+ return NULL;
+ }
+
+ return strdup(mMetaData.valueAt(index).string());
}
+void StagefrightMetadataRetriever::parseMetaData() {
+ sp<MetaData> meta = mExtractor->getMetaData();
+
+ struct Map {
+ int from;
+ int to;
+ };
+ static const Map kMap[] = {
+ { kKeyAlbum, METADATA_KEY_ALBUM },
+ { kKeyArtist, METADATA_KEY_ARTIST },
+ { kKeyComposer, METADATA_KEY_COMPOSER },
+ { kKeyGenre, METADATA_KEY_GENRE },
+ { kKeyTitle, METADATA_KEY_TITLE },
+ { kKeyYear, METADATA_KEY_YEAR },
+ };
+ static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
+
+ for (size_t i = 0; i < kNumMapEntries; ++i) {
+ const char *value;
+ if (meta->findCString(kMap[i].from, &value)) {
+ mMetaData.add(kMap[i].to, String8(value));
+ }
+ }
+
+ const void *data;
+ uint32_t type;
+ size_t dataSize;
+ if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize)) {
+ mAlbumArt = new MediaAlbumArt;
+ mAlbumArt->mSize = dataSize;
+ mAlbumArt->mData = new uint8_t[dataSize];
+ memcpy(mAlbumArt->mData, data, dataSize);
+ }
+
+ // The overall duration is the duration of the longest track.
+ int64_t maxDurationUs = 0;
+ for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
+ sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
+
+ int64_t durationUs;
+ if (trackMeta->findInt64(kKeyDuration, &durationUs)) {
+ if (durationUs > maxDurationUs) {
+ maxDurationUs = durationUs;
+ }
+ }
+ }
+
+ // The duration value is a string representing the duration in ms.
+ char tmp[32];
+ sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
+
+ mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
+}
+
+
} // namespace android
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 229e933..d874224 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -383,7 +383,7 @@ status_t AVCDecoder::read(
return OK;
} else {
- LOGE("failed to decode frame (res = %d)", res);
+ LOGV("failed to decode frame (res = %d)", res);
return UNKNOWN_ERROR;
}
}
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
new file mode 100644
index 0000000..3c47e2e
--- /dev/null
+++ b/media/libstagefright/id3/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ ID3.cpp
+
+LOCAL_MODULE := libstagefright_id3
+
+include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ testid3.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright libutils libbinder
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_id3
+
+LOCAL_MODULE := testid3
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
new file mode 100644
index 0000000..2b3ef1a
--- /dev/null
+++ b/media/libstagefright/id3/ID3.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2010 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 "ID3"
+#include <utils/Log.h>
+
+#include "../include/ID3.h"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+ID3::ID3(const sp<DataSource> &source)
+ : mIsValid(false),
+ mData(NULL),
+ mSize(0),
+ mFirstFrameOffset(0),
+ mVersion(ID3_UNKNOWN) {
+ mIsValid = parse(source);
+}
+
+ID3::~ID3() {
+ if (mData) {
+ free(mData);
+ mData = NULL;
+ }
+}
+
+bool ID3::isValid() const {
+ return mIsValid;
+}
+
+ID3::Version ID3::version() const {
+ return mVersion;
+}
+
+bool ID3::parse(const sp<DataSource> &source) {
+ struct id3_header {
+ char id[3];
+ uint8_t version_major;
+ uint8_t version_minor;
+ uint8_t flags;
+ uint8_t enc_size[4];
+ };
+
+ id3_header header;
+ if (source->readAt(
+ 0, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
+ return false;
+ }
+
+ if (memcmp(header.id, "ID3", 3)) {
+ return false;
+ }
+
+ if (header.version_major == 0xff || header.version_minor == 0xff) {
+ return false;
+ }
+
+ if (header.version_major == 2) {
+ if (header.flags & 0x3f) {
+ // We only support the 2 high bits, if any of the lower bits are
+ // set, we cannot guarantee to understand the tag format.
+ return false;
+ }
+
+ if (header.flags & 0x40) {
+ // No compression scheme has been decided yet, ignore the
+ // tag if compression is indicated.
+
+ return false;
+ }
+ } else if (header.version_major == 3) {
+ if (header.flags & 0x1f) {
+ // We only support the 3 high bits, if any of the lower bits are
+ // set, we cannot guarantee to understand the tag format.
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ size_t size = 0;
+ for (int32_t i = 0; i < 4; ++i) {
+ if (header.enc_size[i] & 0x80) {
+ return false;
+ }
+
+ size = (size << 7) | header.enc_size[i];
+ }
+
+ mData = (uint8_t *)malloc(size);
+
+ if (mData == NULL) {
+ return false;
+ }
+
+ mSize = size;
+
+ if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
+ return false;
+ }
+
+ if (header.flags & 0x80) {
+ LOGI("removing unsynchronization");
+ removeUnsynchronization();
+ }
+
+ mFirstFrameOffset = 0;
+ if (header.version_major == 3 && (header.flags & 0x40)) {
+ // Version 2.3 has an optional extended header.
+
+ if (mSize < 4) {
+ return false;
+ }
+
+ size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
+
+ if (extendedHeaderSize > mSize) {
+ return false;
+ }
+
+ mFirstFrameOffset = extendedHeaderSize;
+
+ uint16_t extendedFlags = 0;
+ if (extendedHeaderSize >= 6) {
+ extendedFlags = U16_AT(&mData[4]);
+
+ if (extendedHeaderSize >= 10) {
+ size_t paddingSize = U32_AT(&mData[6]);
+
+ if (mFirstFrameOffset + paddingSize > mSize) {
+ return false;
+ }
+
+ mSize -= paddingSize;
+ }
+
+ if (extendedFlags & 0x8000) {
+ LOGI("have crc");
+ }
+ }
+ }
+
+ if (header.version_major == 2) {
+ mVersion = ID3_V2_2;
+ } else {
+ CHECK_EQ(header.version_major, 3);
+ mVersion = ID3_V2_3;
+ }
+
+ return true;
+}
+
+void ID3::removeUnsynchronization() {
+ for (size_t i = 0; i + 1 < mSize; ++i) {
+ if (mData[i] == 0xff && mData[i + 1] == 0x00) {
+ memmove(&mData[i + 1], &mData[i + 2], mSize - i - 2);
+ --mSize;
+ }
+ }
+}
+
+ID3::Iterator::Iterator(const ID3 &parent, const char *id)
+ : mParent(parent),
+ mID(NULL),
+ mOffset(mParent.mFirstFrameOffset),
+ mFrameData(NULL),
+ mFrameSize(0) {
+ if (id) {
+ mID = strdup(id);
+ }
+
+ findFrame();
+}
+
+ID3::Iterator::~Iterator() {
+ if (mID) {
+ free(mID);
+ mID = NULL;
+ }
+}
+
+bool ID3::Iterator::done() const {
+ return mFrameData == NULL;
+}
+
+void ID3::Iterator::next() {
+ if (mFrameData == NULL) {
+ return;
+ }
+
+ mOffset += mFrameSize;
+
+ findFrame();
+}
+
+void ID3::Iterator::getID(String8 *id) const {
+ id->setTo("");
+
+ if (mFrameData == NULL) {
+ return;
+ }
+
+ if (mParent.mVersion == ID3_V2_2) {
+ id->setTo((const char *)&mParent.mData[mOffset], 3);
+ } else {
+ CHECK_EQ(mParent.mVersion, ID3_V2_3);
+ id->setTo((const char *)&mParent.mData[mOffset], 4);
+ }
+}
+
+static void convertISO8859ToString8(
+ const uint8_t *data, size_t size,
+ String8 *s) {
+ size_t utf8len = 0;
+ for (size_t i = 0; i < size; ++i) {
+ if (data[i] < 0x80) {
+ ++utf8len;
+ } else {
+ utf8len += 2;
+ }
+ }
+
+ if (utf8len == size) {
+ // Only ASCII characters present.
+
+ s->setTo((const char *)data, size);
+ return;
+ }
+
+ char *tmp = new char[utf8len];
+ char *ptr = tmp;
+ for (size_t i = 0; i < size; ++i) {
+ if (data[i] < 0x80) {
+ *ptr++ = data[i];
+ } else if (data[i] < 0xc0) {
+ *ptr++ = 0xc2;
+ *ptr++ = data[i];
+ } else {
+ *ptr++ = 0xc3;
+ *ptr++ = data[i] - 64;
+ }
+ }
+
+ s->setTo(tmp, utf8len);
+
+ delete[] tmp;
+ tmp = NULL;
+}
+
+void ID3::Iterator::getString(String8 *id) const {
+ id->setTo("");
+
+ if (mFrameData == NULL) {
+ return;
+ }
+
+ size_t n = mFrameSize - getHeaderLength() - 1;
+
+ if (*mFrameData == 0x00) {
+ // ISO 8859-1
+ convertISO8859ToString8(mFrameData + 1, n, id);
+ } else {
+ // UCS-2
+ id->setTo((const char16_t *)(mFrameData + 1), n);
+ }
+}
+
+const uint8_t *ID3::Iterator::getData(size_t *length) const {
+ *length = 0;
+
+ if (mFrameData == NULL) {
+ return NULL;
+ }
+
+ *length = mFrameSize - getHeaderLength();
+
+ return mFrameData;
+}
+
+size_t ID3::Iterator::getHeaderLength() const {
+ if (mParent.mVersion == ID3_V2_2) {
+ return 6;
+ } else {
+ CHECK_EQ(mParent.mVersion, ID3_V2_3);
+ return 10;
+ }
+}
+
+void ID3::Iterator::findFrame() {
+ for (;;) {
+ mFrameData = NULL;
+ mFrameSize = 0;
+
+ if (mParent.mVersion == ID3_V2_2) {
+ if (mOffset + 6 > mParent.mSize) {
+ return;
+ }
+
+ if (!memcmp(&mParent.mData[mOffset], "\0\0\0", 3)) {
+ return;
+ }
+
+ mFrameSize =
+ (mParent.mData[mOffset + 3] << 16)
+ | (mParent.mData[mOffset + 4] << 8)
+ | mParent.mData[mOffset + 5];
+
+ mFrameSize += 6;
+
+ if (mOffset + mFrameSize > mParent.mSize) {
+ LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
+ mOffset, mFrameSize, mParent.mSize - mOffset - 6);
+ return;
+ }
+
+ mFrameData = &mParent.mData[mOffset + 6];
+
+ if (!mID) {
+ break;
+ }
+
+ char id[4];
+ memcpy(id, &mParent.mData[mOffset], 3);
+ id[3] = '\0';
+
+ if (!strcmp(id, mID)) {
+ break;
+ }
+ } else {
+ CHECK_EQ(mParent.mVersion, ID3_V2_3);
+
+ if (mOffset + 10 > mParent.mSize) {
+ return;
+ }
+
+ if (!memcmp(&mParent.mData[mOffset], "\0\0\0\0", 4)) {
+ return;
+ }
+
+ mFrameSize = 10 + U32_AT(&mParent.mData[mOffset + 4]);
+
+ if (mOffset + mFrameSize > mParent.mSize) {
+ LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
+ mOffset, mFrameSize, mParent.mSize - mOffset - 10);
+ return;
+ }
+
+ mFrameData = &mParent.mData[mOffset + 10];
+
+ if (!mID) {
+ break;
+ }
+
+ char id[5];
+ memcpy(id, &mParent.mData[mOffset], 4);
+ id[4] = '\0';
+
+ if (!strcmp(id, mID)) {
+ break;
+ }
+ }
+
+ mOffset += mFrameSize;
+ }
+}
+
+static size_t StringSize(const uint8_t *start, uint8_t encoding) {
+ if (encoding== 0x00) {
+ // ISO 8859-1
+ return strlen((const char *)start) + 1;
+ }
+
+ // UCS-2
+ size_t n = 0;
+ while (start[n] != '\0' || start[n + 1] != '\0') {
+ n += 2;
+ }
+
+ return n;
+}
+
+const void *
+ID3::getAlbumArt(size_t *length, String8 *mime) const {
+ *length = 0;
+ mime->setTo("");
+
+ Iterator it(*this, mVersion == ID3_V2_3 ? "APIC" : "PIC");
+
+ while (!it.done()) {
+ size_t size;
+ const uint8_t *data = it.getData(&size);
+
+ if (mVersion == ID3_V2_3) {
+ uint8_t encoding = data[0];
+ mime->setTo((const char *)&data[1]);
+ size_t mimeLen = strlen((const char *)&data[1]) + 1;
+
+ uint8_t picType = data[1 + mimeLen];
+#if 0
+ if (picType != 0x03) {
+ // Front Cover Art
+ it.next();
+ continue;
+ }
+#endif
+
+ size_t descLen = StringSize(&data[2 + mimeLen], encoding);
+
+ *length = size - 2 - mimeLen - descLen;
+
+ return &data[2 + mimeLen + descLen];
+ } else {
+ uint8_t encoding = data[0];
+
+ if (!memcmp(&data[1], "PNG", 3)) {
+ mime->setTo("image/png");
+ } else if (!memcmp(&data[1], "JPG", 3)) {
+ mime->setTo("image/jpeg");
+ } else if (!memcmp(&data[1], "-->", 3)) {
+ mime->setTo("text/plain");
+ } else {
+ return NULL;
+ }
+
+#if 0
+ uint8_t picType = data[4];
+ if (picType != 0x03) {
+ // Front Cover Art
+ it.next();
+ continue;
+ }
+#endif
+
+ size_t descLen = StringSize(&data[5], encoding);
+
+ *length = size - 5 - descLen;
+
+ return &data[5 + descLen];
+ }
+ }
+
+ return NULL;
+}
+
+
+} // namespace android
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
new file mode 100644
index 0000000..305b065
--- /dev/null
+++ b/media/libstagefright/id3/testid3.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "../include/ID3.h"
+
+#include <ctype.h>
+#include <dirent.h>
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaDebug.h>
+
+#define MAXPATHLEN 256
+
+using namespace android;
+
+static void hexdump(const void *_data, size_t size) {
+ const uint8_t *data = (const uint8_t *)_data;
+ size_t offset = 0;
+ while (offset < size) {
+ printf("0x%04x ", offset);
+
+ size_t n = size - offset;
+ if (n > 16) {
+ n = 16;
+ }
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (i == 8) {
+ printf(" ");
+ }
+
+ if (offset + i < size) {
+ printf("%02x ", data[offset + i]);
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf(" ");
+
+ for (size_t i = 0; i < n; ++i) {
+ if (isprint(data[offset + i])) {
+ printf("%c", data[offset + i]);
+ } else {
+ printf(".");
+ }
+ }
+
+ printf("\n");
+
+ offset += 16;
+ }
+}
+
+void scanFile(const char *path) {
+ sp<FileSource> file = new FileSource(path);
+ CHECK_EQ(file->initCheck(), OK);
+
+ ID3 tag(file);
+ if (!tag.isValid()) {
+ printf("FAIL %s\n", path);
+ } else {
+ printf("SUCCESS %s\n", path);
+
+ ID3::Iterator it(tag, NULL);
+ while (!it.done()) {
+ String8 id;
+ it.getID(&id);
+
+ CHECK(id.length() > 0);
+ if (id[0] == 'T') {
+ String8 text;
+ it.getString(&text);
+
+ printf(" found text frame '%s': %s\n", id.string(), text.string());
+ } else {
+ printf(" found frame '%s'.\n", id.string());
+ }
+
+ it.next();
+ }
+
+ size_t dataSize;
+ String8 mime;
+ const void *data = tag.getAlbumArt(&dataSize, &mime);
+
+ if (data) {
+ printf("found album art: size=%d mime='%s'\n", dataSize,
+ mime.string());
+
+ hexdump(data, dataSize > 128 ? 128 : dataSize);
+ }
+ }
+}
+
+void scan(const char *path) {
+ DIR *dir = opendir(path);
+
+ if (dir == NULL) {
+ return;
+ }
+
+ rewinddir(dir);
+
+ struct dirent *ent;
+ while ((ent = readdir(dir)) != NULL) {
+ if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name)) {
+ continue;
+ }
+
+ char newPath[MAXPATHLEN];
+ strcpy(newPath, path);
+ strcat(newPath, "/");
+ strcat(newPath, ent->d_name);
+
+ if (ent->d_type == DT_DIR) {
+ scan(newPath);
+ } else if (ent->d_type == DT_REG) {
+ size_t len = strlen(ent->d_name);
+
+ if (len >= 4
+ && !strcasecmp(ent->d_name + len - 4, ".mp3")) {
+ scanFile(newPath);
+ }
+ }
+ }
+
+ closedir(dir);
+ dir = NULL;
+}
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+ DataSource::RegisterDefaultSniffers();
+
+ for (int i = 1; i < argc; ++i) {
+ scan(argv[i]);
+ }
+
+ return 0;
+}
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
new file mode 100644
index 0000000..79931ac
--- /dev/null
+++ b/media/libstagefright/include/ID3.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 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 ID3_H_
+
+#define ID3_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct DataSource;
+struct String8;
+
+struct ID3 {
+ enum Version {
+ ID3_UNKNOWN,
+ ID3_V2_2,
+ ID3_V2_3
+ };
+
+ ID3(const sp<DataSource> &source);
+ ~ID3();
+
+ bool isValid() const;
+
+ Version version() const;
+
+ const void *getAlbumArt(size_t *length, String8 *mime) const;
+
+ struct Iterator {
+ Iterator(const ID3 &parent, const char *id);
+ ~Iterator();
+
+ bool done() const;
+ void getID(String8 *id) const;
+ void getString(String8 *s) const;
+ const uint8_t *getData(size_t *length) const;
+ void next();
+
+ private:
+ const ID3 &mParent;
+ char *mID;
+ size_t mOffset;
+
+ const uint8_t *mFrameData;
+ size_t mFrameSize;
+
+ void findFrame();
+
+ size_t getHeaderLength() const;
+
+ Iterator(const Iterator &);
+ Iterator &operator=(const Iterator &);
+ };
+
+private:
+ bool mIsValid;
+ uint8_t *mData;
+ size_t mSize;
+ size_t mFirstFrameOffset;
+ Version mVersion;
+
+ bool parse(const sp<DataSource> &source);
+ void removeUnsynchronization();
+
+ ID3(const ID3 &);
+ ID3 &operator=(const ID3 &);
+};
+
+} // namespace android
+
+#endif // ID3_H_
+
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index b5a6b3c..3ce6df3 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -34,6 +34,8 @@ public:
virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual sp<MetaData> getMetaData();
+
protected:
virtual ~MP3Extractor();
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index 16127d7..b80387f 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -1,19 +1,18 @@
/*
-**
-** Copyright 2009, 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.
-*/
+ * Copyright (C) 2010 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 STAGEFRIGHT_METADATA_RETRIEVER_H_
@@ -22,9 +21,11 @@
#include <media/MediaMetadataRetrieverInterface.h>
#include <media/stagefright/OMXClient.h>
+#include <utils/KeyedVector.h>
namespace android {
+struct DataSource;
class MediaExtractor;
struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface {
@@ -40,8 +41,15 @@ struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface {
private:
OMXClient mClient;
+ sp<DataSource> mSource;
sp<MediaExtractor> mExtractor;
+ bool mParsedMetaData;
+ KeyedVector<int, String8> mMetaData;
+ MediaAlbumArt *mAlbumArt;
+
+ void parseMetaData();
+
StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
StagefrightMetadataRetriever &operator=(