Android多媒體分析(一)MediaScanner

Android平臺上的媒體文件管理和桌面系統不同。在桌面系統上,不同目錄下的媒體文件呈樹狀結構顯示給用戶,用戶需要進入不同目錄尋找該目錄下的文件。而在Android平臺上,不同目錄下的媒體文件則以一層列表方式顯示給用戶,用戶不需進入子目錄就可以列出(某種類型的)所有媒體文件。

在Android上,爲了實現這種模式的媒體文件管理,對所有管理的媒體文件抽取其元數據,也就是ID3(mp3文件包含的元數據可參考http://en.wikipedia.org/wiki/ID3),存儲在數據庫中,並作爲一個content provider提供給其他應用使用。用戶的每一次顯示媒體文件的操作,就是對這個數據庫的一次查詢操作。在多媒體管理模塊中,主要分成三個模塊:

多媒體數據庫

MediaStore這個類是android系統提供的一個多媒體數據庫,android中多媒體信息都可以從這裏提取。這個MediaStore包括了多媒體數據庫的所有信息,包括音頻,視頻和圖像,android把所有的多媒體數據庫接口進行了封裝,所有的數據庫不用自己進行創建,直接調用利用ContentResolver去掉用那些封裝好的接口就可以進行數據庫的操作,多媒體數據庫的使用方法和SQLITE3的方法是一樣的。

MediaStore中的數據是在MediaScanner掃描後通過MediaProvider中的一個service進行更新的。框架圖如下:

MediaScanner

在Android系統中,多媒體庫是通過MediaScanner去掃描磁盤文件,對元信息的處理,並通過MediaProvider保存到MediaStore中。下圖爲MediaScannerr 框架:

             圖1-1  MediaScanner框架流程

MediaScanner可以通過手動控制,在ANDROID系統中,已經定製了三種事件會觸發MediaScanner去掃描磁盤文件:ACTION_BOOT_COMPLETED、ACTION_MEDIA_MOUNTED、 ACTION_MEDIA_SCANNER_SCAN_FILE。其中ACTION_BOOT_COMPLETED是系統啓動完後發出這個消息,ACTION_MEDIA_MOUNTED是插卡事件觸發的消息,ACTION_MEDIA_SCANNER_SCAN_FILE消息一般是在一些文件操作後,開發人員手動發出的一個重新掃描多媒體文件的消息。發送消息通過sendBroadcast函數完成,比如廣播一個ACTION_MEDIA_MOUNTED消息:

sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"

                       + Environment.getExternalStorageDirectory())));

     由上可知是通過發送了一個廣播(傳遞對應的掃描要求)來觸發重新掃描磁盤事件,那麼可以猜測系統肯定存在一個廣播接收器(何時何地註冊?),在收到這個廣播消息後,通過對應參數啓動MediaScannerService。MediaScannerService調用一個公用類MediaScanner去處理真正的工作。MediaScannerReceiver維持兩種掃描目錄:一種是內部卷(internal volume)指向$(ANDROID_ROOT)/media. 另一種是外部卷(external volume)指向$(EXTERNAL_STORAGE),掃描的位置可以修改(一般外部不用修改,默認爲SDCARD,內部根據驅動命名的INAND路經名做對應的修改),對應圖1-1系統源碼具體位置:

MediaScannerReceiver: /mydroid/packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerReceiver.java

Scanner事件接收,繼承了BroadcastReceiver類,收到掃描消息後啓動MediaScannerService,但有一點要注意的是: Service的onCreate的方法只會被調用一次,就是你無論多少次的startService又 bindService,Service只被創建一次。如果先是bind了,那麼start的時候就直接運行Service的 onStart方法,如果先是start,那麼bind的時候就直接運行onBind方法。如果你先bind上了,就stop不掉了,對啊,就是stopService不好使了,只能先UnbindService, 再StopService,所以是先start還是先bind行爲是有區別的。(關於BINDLE接口請參考其它文檔)

MediaScannerService:

/mydroid/packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerService.java

通過此服務,去調用MediaScanner的具體實現方法。

MediaScanner:  (方法)

\frameworks\base\media\java\android\media\ MediaScanner.java

MediaPlayer整體流程:

Thumbnails縮略圖概述

縮略圖保存位置:/sdcard/DCIM/.thumbnails

縮略圖(大):一張張的JPEG文件

所有的都存入同一個二進制文件.thumbdata+version+”-”+hashcode,字節大小均爲 10000bytes

詳解:

對於每一張圖片,都會生成大縮略圖和小縮略圖,大縮略圖保存在(外部設備)/sdcard/DCIM/.thumbnails/ 目錄下,大縮略圖大小上限是512 X 384,下限1 X1。小縮略圖被統一保存到一個隨機訪問文件 (外部設備)/sdcard/DCIM/.thumbnails/ +".thumbdata" + version + "-"+ mUri.hashcode()

縮略圖存儲到數據庫裏面

掃描一個文件的最後endfile()會mMediaProvider.insert()或者mMediaProvider.update()。在mMediaProvider.insert()時對於IMAGES_MEDIA 和VIDEO_MEDIA類型:requestMediaThumbnail()--->mCurrentThumbRequest.execute(); 在執行時會首先會去縮略圖的數據庫中查詢,查詢到就返回,未查詢到會ThumbnailUtil.createVideoThumbnail(mPath)或者 ThumbnailUtil.createImageThumbnail(mCr, mPath, mUri, mOrigId,Images.Thumbnails.MICRO_KIND, true/*saveMini*/));

創建圖片縮略圖:

創建thumbnail時調用ThumbnailUtil.makeBitmap()創建;如果saveMini爲真會保存縮略圖ThumbnailUtil.storeThumbnail(cr, origId, thumbData, bitmap.getWidth(), bitmap.getHeight());; 保存時會通過origId向數據庫查詢,查到返回對應的uri,未查找到就插入數據庫並返回uri,最終返回bitmap。對於創建視頻縮略圖:直接在視頻文件中取一幀得到bintmap並返回保存大縮略圖到文件: bitmap.compress(Bitmap.CompressFormat.JPEG, 75, miniOutStream);保存小縮略圖到隨機訪問文件:data = miniOutStream.toByteArray(); miniThumbFile.saveMiniThumbToFile(data, mOrigId, magic);

在mMediaProvider.update()時,對IMAGES_MEDIA,IMAGES_MEDIA_ID,VIDEO_MEDIA,VIDEO_MEDIA_ID類型會查詢magic,如果沒查到會執行requestMediaThumbnail(),流程同上。

MediaProvider是對上面整個流程的實現。可參考裏面的代碼。

圖3 MediaProvider 代碼架構

轉自:http://wenku.baidu.com/view/537b9b8ecc22bcd126ff0c97.html#

Android稍有熟悉的人都知道,Android Media Scanner只對SD卡上的媒體文件進行掃描。其掃描的策略,請參考《Android Media Scanner Process》。假如我們的硬件平臺上面沒有提供SD卡槽,難道Android就不能進行對媒體文件播放了嗎?當然不是的,否則Android系統將不會成爲一個完善的Framework。本文結合本人的經驗介紹一下,怎樣修改多媒體文件的掃描路徑。

     根據《Android Media Scanner Process》的介紹我們可以知道,Android scanner掃描媒體完成之後,會把媒體文件存放在數據庫中,由MediaProvider爲上層的應用程序提供服務。

    經過研究Media scanner的代碼發現,他的掃描路徑爲android.os.Environment.EXTERNAL_STORAGE_DIRECTORY。定義該變量文件位於:

frameworks/base/core/java/android/os/Environment.java

默認情況下,Android將會搜索/sddisk目錄:

    private static final File EXTERNAL_STORAGE_DIRECTORY

            = getDirectory("EXTERNAL_STORAGE", "/sddisk");

爲了讓其進行搜索我們自定義的路徑,可以修改該變量的定義,加入我們希望掃描/external目錄。代碼修改如下:

    private static final File EXTERNAL_STORAGE_DIRECTORY

            = getDirectory("EXTERNAL_STORAGE", "/external");

這樣Android Media Scanner將會搜索/external目錄來查找媒體文件。

     下一步我們需要保證這個文件一定要存在,我們需要修改init.rc文件。增加如下的定義:

mkdir /external 0777 system system

這樣在開機的時候,如果/external目錄不存在,則會創建一個。如果已經存在,則不會有任何動作。

     另外怎樣觸發Media Scanner?根據《Android Media Scanner Process》的介紹,當收到ACTION_BOOT_COMPLETEDACTION_MEDIA_MOUNTEDACTION_MEDIA_SCANNER_SCAN_FILE消息的時候纔會進行掃描。以前是掃描SD卡,當SDmount的時候Android系統會有ACTION_MEDIA_MOUNTED消息通知,Media Scanner開始掃描媒體文件。但是我們的/external目錄修改之後,怎樣通知Android media scanner掃描呢?一個辦法是重啓,沒有人樂意這樣做。另外一個辦法是運行menu->dev tools->Media Scan,這樣將會進行掃描。目前我還沒有讓目錄修改之後,自動掃描的辦法。如果你有好的點子,請你給我留言。

     通過以上的步驟,可以在Android/external目錄存放媒體文件,並且被music應用程序播放了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章