首先,MP3文件的专辑封面数据是编码在MP3文件中的ID3中,需要在解析ID3 tag的时候将这段压缩的数据从MP3文件中copy出来,写到某个文件中去。
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;
const void *data;
uint32_t type;
size_t dataSize;
if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize)
&& mAlbumArt == NULL) {
mAlbumArt = new MediaAlbumArt;
mAlbumArt->mSize = dataSize;
mAlbumArt->mData = new uint8_t[dataSize];
memcpy(mAlbumArt->mData, data, dataSize);
}
ALOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
if (mExtractor == NULL) {
return NULL;
}
if (!mParsedMetaData) {
parseMetaData();
mParsedMetaData = true;
}
if (mAlbumArt) {
return new MediaAlbumArt(*mAlbumArt);
A simple buffer to hold binary data
}
return NULL;
public native byte[] extractAlbumArt(FileDescriptor fd);
Get album art for specified album. This method will not try to
fall back to getting artwork directly from the file, nor will
it attempt to repair the database.
private static Bitmap getArtworkQuick(Context context, long album_id, int w, int h) {
NOTE: There is in fact a 1 pixel border on the right side in the ImageView
used to display this drawable. Take it into account now, so we don't have to
scale later.
w -= 1;
ContentResolver res = context.getContentResolver();
Uri uri = ContentUris.withAppendedId(sArtworkUri, album_id);
if (uri != null) {
ParcelFileDescriptor fd = null;
try {
fd = res.openFileDescriptor(uri, "r");
int sampleSize = 1;
Compute the closest power-of-two scale factor
and pass that to sBitmapOptionsCache.inSampleSize, which will
result in faster decoding and better quality
sBitmapOptionsCache.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(
fd.getFileDescriptor(), null, sBitmapOptionsCache);
int nextWidth = sBitmapOptionsCache.outWidth >> 1;
int nextHeight = sBitmapOptionsCache.outHeight >> 1;
while (nextWidth>w && nextHeight>h) {
sampleSize <<= 1;
nextWidth >>= 1;
nextHeight >>= 1;
}
sBitmapOptionsCache.inSampleSize = sampleSize;
sBitmapOptionsCache.inJustDecodeBounds = false;
Bitmap b = BitmapFactory.decodeFileDescriptor(
fd.getFileDescriptor(), null, sBitmapOptionsCache);
if (b != null) {
finally rescale to exactly the size we need
if (sBitmapOptionsCache.outWidth != w || sBitmapOptionsCache.outHeight != h) {
Bitmap tmp = Bitmap.createScaledBitmap(b, w, h, true);
Bitmap.createScaledBitmap() can return the same bitmap
if (tmp != b) b.recycle();
b = tmp;
}
}
return b;
} catch (FileNotFoundException e) {
} finally {
try {
if (fd != null)
fd.close();
} catch (IOException e) {
}
}
}
return null;
}
取出数据是在MP3Extractor.cpp中完成
sp MP3Extractor::getMetaData() {
}
如上面代码,获取到了封面图片的在文件中的首地址,size大小以及mime类型,有了前两个信息,就可以直接取出来了。
这些信息直接保存在meta的
kKeyAlbumArt键值对中,供后面取出使用。
在StagefrightMetadataRetri
ever.cpp中,会在其parseMetaData函数中调用各个Extractor的getMetaData函数,对于MP3文件,调用的就是上面的getMetaData函数。
在parseMetaData函数中会通过getMetaData函数中解析出来的封面的首地址和长度,从文件中将这段数据copy出来,保存到内存中。
void StagefrightMetadataRetri
ever::parseMetaData() {
}
在StagefrightMetadataRetri
ever的extractAlbumArt函数中会将内存中的封面数据赋给MediaAlbumArt对象
MediaAlbumArt *StagefrightMetadataRetri
ever::extractAlbumArt() {
}
StagefrightMetadataRetri
ever的extractAlbumArt函数一路被MediaScanner.cpp JNI MediaScanner.java de native 函数调用过来。
最后应当是在MediaProvider.java中调用下去的,调用的时候传递了一个文件fd,这个文件就是最终封面图片数据写到地方。
略过MediaProvider
最终这个文件会保存在/sdcard/Android/data/com.android.provider.media/albumthumbnail/路径下面。
那么MP3是如何和相对应的封面相关联的呢
所有的MP3会扫描到mediaprovider数据库中的Audio表中,这个表有一个域是album_id,这个值对应数据库中的另一张表album_info的主键。
在album_info表中记录了每一个封面文件的绝对路径,所以任何一首MP3歌曲可以通过album_id值找到对应的封面文件,取出解码生成一个bitmap就可以用来显示了。
这里可以参考Google Music中的MusicUtils.java类的下面这个函数。