mp3 文件专辑封面 一点点知识

mp3 文件专辑封面 一点点知识

(2013-04-22 22:40:09)
标签:

android

mp3

id3

封面

it

分类: Android机制
首先,MP3文件的专辑封面数据是编码在MP3文件中的ID3中,需要在解析ID3 tag的时候将这段压缩的数据从MP3文件中copy出来,写到某个文件中去。

取出数据是在MP3Extractor.cpp中完成
sp MP3Extractor::getMetaData() {
  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;
}

如上面代码,获取到了封面图片的在文件中的首地址,size大小以及mime类型,有了前两个信息,就可以直接取出来了。
这些信息直接保存在meta的kKeyAlbumArt键值对中,供后面取出使用。

在StagefrightMetadataRetriever.cpp中,会在其parseMetaData函数中调用各个Extractor的getMetaData函数,对于MP3文件,调用的就是上面的getMetaData函数。
在parseMetaData函数中会通过getMetaData函数中解析出来的封面的首地址和长度,从文件中将这段数据copy出来,保存到内存中。
void StagefrightMetadataRetriever::parseMetaData() {
  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);
  }
}

在StagefrightMetadataRetriever的extractAlbumArt函数中会将内存中的封面数据赋给MediaAlbumArt对象
MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
  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;
}

StagefrightMetadataRetriever的extractAlbumArt函数一路被MediaScanner.cpp JNI MediaScanner.java de native 函数调用过来。
  public native byte[] extractAlbumArt(FileDescriptor fd);

最后应当是在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类的下面这个函数。
  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;
  }
展开阅读全文

没有更多推荐了,返回首页