ANDROID解決音樂播放器沒法獲取專輯圖片問題

最近遇到一個問題,就是音樂播放器沒法拿到專輯圖片,總是報以下錯誤:java

---SkImageDecoder::Factory returned null

如下爲調試的一些思路及解決方法。android

  • MP3裏存放了專輯歌名等信息,這些信息使用ID3格式存儲。ID3有幾個版本,專輯圖片等使用ID3V2存儲。關於ID3V2的SPEC請戳這裏
  • 下載幾首歌,使用mp3tag軟件查看專輯圖片等信息。
  • 查看ID3裏關於專輯圖片的說明,能夠知道從歌曲裏的某個偏移地址讀N個字節獲得專輯圖片。
  • 上層獲取專輯圖片的代碼以下:
 1     public static Bitmap getArtwork(Context context, long song_id, long album_id,
 2             boolean allowdefault) {
 3 
 4         if (album_id < 0) {
 5             // This is something that is not in the database, so get the album art directly
 6             // from the file.
 7             if (song_id >= 0) {
 8                 Bitmap bm = getArtworkFromFile(context, song_id, -1);
 9                 if (bm != null) {
10                     return bm;
11                 }
12             }
13             if (allowdefault) {
14                 return getDefaultArtwork(context);
15             }
16             return null;
17         }
18 
19         ContentResolver res = context.getContentResolver();
20         Uri uri = ContentUris.withAppendedId(sArtworkUri, album_id);
21         if (uri != null) {
22             InputStream in = null;
23             try {
24                 in = res.openInputStream(uri);
25                 return BitmapFactory.decodeStream(in, null, sBitmapOptions);
26             } catch (FileNotFoundException ex) {
27                 // The album art thumbnail does not actually exist. Maybe the user deleted it, or
28                 // maybe it never existed to begin with.
29                 Bitmap bm = getArtworkFromFile(context, song_id, album_id);
30                 if (bm != null) {
31                     if (bm.getConfig() == null) {
32                         bm = bm.copy(Bitmap.Config.RGB_565, false);
33                         if (bm == null && allowdefault) {
34                             return getDefaultArtwork(context);
35                         }
36                     }
37                 } else if (allowdefault) {
38                     bm = getDefaultArtwork(context);
39                 }
40                 return bm;
41             } finally {
42                 try {
43                     if (in != null) {
44                         in.close();
45                     }
46                 } catch (IOException ex) {
47                 }
48             }
49         }
50         
51         return null;
52     }
  • 經過調試可知出錯的地方是:BitmapFactory.decodeFileDescriptor(fd)失敗。而上一句獲取fd能成功,那打開的是什麼,哪一個文件?仍是哪一個數據庫?
  • 通過一番查找,實現打開方法的地方是MediaProvider,路徑是:
android/packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java

  在此文件中有實現openFile函數。數據庫

  • 對openFile函數進行單步調試,得知打開以下路徑:
/mnt/sdcard/Android/data/com.android.providers.media/albumthumbs/1263093833711
  • adb pull這個文件下來,用UE打開,不知道是什麼。用手機查看相同目錄下的一個文件如1263093833832,用圖片方式打開,竟然能夠打開,並且就是專輯圖片(暫命名此文件爲mobile.jpg,此文件對應的mp3歌曲爲mobile.mp3)。
  • 用BC對比1263093833711與mobile.jpg。發現1263093833711文件前面多了2個0x00,0x00。
  • 用BC對比mobile.jpg與其對應的mobile.mp3,可知mobile.jpg是從mobile.mp3裏截出來的,無一點修改。與ID3說明的同樣。
  • 再對比1263093833711與mobile.mp3,開頭的地方確實多截了2個字節。
  • 此時,問題已找到,就是生成albumthumb時錯了。
  • 1263093833711此類文件在哪生成?在MediaScanner裏調用的native函數extractAlbumArt生成的。答案轉到jni層。
  • 通過層層解析,最終找到在:
android/frameworks/base/media/libstagefright/id3/ID3.cpp

  文件裏的getAlbumArt函數。ide

 

  • 反覆看此段代碼與ID3 SPEC的4.15&4.10節:
<Headerfor'Attached picture', ID: "APIC">
Text encoding $xx
MIME type <textstring> $00
Picture type $xx
Description <textstringaccordingtoencoding> $00 (00)
Picture data <binarydata>

$00 ISO-8859-1 character set is used => $00 is sync identifier.
$01 Unicode character set is used => $00 00 is sync identifier.
  • mobile.mp3的APIC段的description的encoding爲0x01,所以須要2個0x00做爲sync identifier。所以在getAlbumArt函數調用StringSize裏須要跳過這2個字節:函數

 

 1 static size_t StringSize(const uint8_t *start, uint8_t encoding) {
 2     if (encoding == 0x00 || encoding == 0x03) {
 3         // ISO 8859-1 or UTF-8
 4         return strlen((const char *)start) + 1;
 5     }
 6 
 7     // UCS-2
 8     size_t n = 0;
 9     while (start[n] != '\0' || start[n + 1] != '\0') {
10         n += 2;
11     }
12 
13     n += 2; //mark
14 
15     return n;
16 }

 

  • 問題解決,OVER。
相關文章
相關標籤/搜索