Android 使用Vitamio打造自己的萬(wàn)能播放器(10)—— 本地播放 (縮略圖、視頻信息、視頻掃描服務(wù))
前言
Vitamio是我們團(tuán)隊(duì)的誠(chéng)意之作,除了要將VPlayer打造成Android最好的播放器,也要將Vitamio打造成Android最好的播放器組件。新版發(fā)布內(nèi)測(cè)SDK雖然還有一些問(wèn)題,但已經(jīng)具備高可擴(kuò)展性、便捷性和許多內(nèi)置實(shí)用的功能,后續(xù)文章將繼續(xù)深挖和介紹Vitamio的使用細(xì)節(jié)和方法。本章將提供顯示縮略圖、視頻大小等視頻信息,并介紹使用Vitamio內(nèi)置的ContentProvider以及文件掃描服務(wù)。
注意
本章ContentProvider在2012-8-31發(fā)布的SDK中已經(jīng)不能使用,后續(xù)文章會(huì)更新用法。
系列
1、Android 使用Vitamio打造自己的萬(wàn)能播放器(1)——準(zhǔn)備
2、Android 使用Vitamio打造自己的萬(wàn)能播放器(2)—— 手勢(shì)控制亮度、音量、縮放
3、Android 使用Vitamio打造自己的萬(wàn)能播放器(3)——本地播放(主界面、視頻列表)
4、Android 使用Vitamio打造自己的萬(wàn)能播放器(4)——本地播放(快捷搜索、數(shù)據(jù)存儲(chǔ))
5、Android 使用Vitamio打造自己的萬(wàn)能播放器(5)——在線播放(播放優(yōu)酷視頻)
6、Android 使用Vitamio打造自己的萬(wàn)能播放器(6)——在線播放(播放列表)
7、Android 使用Vitamio打造自己的萬(wàn)能播放器(7)——在線播放(下載視頻)
8、Android 使用Vitamio打造自己的萬(wàn)能播放器(8)——細(xì)節(jié)優(yōu)化
9、Android 使用Vitamio打造自己的萬(wàn)能播放器(9)——在線播放(在線電視)
正文
一、目標(biāo)
1.1 獲取視頻縮略圖等
之前的文章都是自己掃描獲取的視頻文件信息,這里補(bǔ)充獲取視頻縮略圖、視頻寬帶的代碼,參加文章2.1部分。
1.2 調(diào)用Vitamio的掃描服務(wù)
新版SDK已經(jīng)內(nèi)置了視頻掃描服務(wù),提供了本地視頻播放所需的全部數(shù)據(jù)。

二、實(shí)現(xiàn)
2.1 自己掃描獲取視頻的縮略圖、視頻寬高
public static ArrayList<PFile> batchBuildThumbnail(final Context ctx, final ArrayList<File> files) {
ArrayList<PFile> result = new ArrayList<PFile>();
for (File f : files) {
PFile pf = new PFile();
try {
if (f.exists() && f.canRead()) {
//取出視頻的一幀圖像
Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(ctx, f.getAbsolutePath(), Video.Thumbnails.MINI_KIND);
if (bitmap == null) {
//縮略圖創(chuàng)建失敗
bitmap = Bitmap.createBitmap(ThumbnailUtils.TARGET_SIZE_MINI_THUMBNAIL_WIDTH, ThumbnailUtils.TARGET_SIZE_MINI_THUMBNAIL_HEIGHT, Bitmap.Config.RGB_565);
Log.e(TAG, "batchBuildThumbnail createBitmap faild : " + f.getAbsolutePath());
}
pf.width = bitmap.getWidth();
pf.height = bitmap.getHeight();
//縮略圖
bitmap = ThumbnailUtils.extractThumbnail(bitmap, ThumbnailUtils.dipToPX(ctx, ThumbnailUtils.TARGET_SIZE_MICRO_THUMBNAIL_WIDTH), ThumbnailUtils.dipToPX(ctx, ThumbnailUtils.TARGET_SIZE_MICRO_THUMBNAIL_HEIGHT), ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
if (bitmap != null) {
File thum = new File(f.getParent(), f.getName() + ".jpg");
pf.thumb = thum.getAbsolutePath();
//thum.createNewFile();
FileOutputStream iStream = new FileOutputStream(thum);
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, iStream);
iStream.close();
}
if (bitmap != null)
bitmap.recycle();
}
} catch (Exception e) {
Log.e(TAG, e);
continue;
} finally {
result.add(pf);
}
}
return result;
}
代碼說(shuō)明:
a). 縮略圖的尺寸是可以調(diào)整的,大家可以看一下這個(gè)默認(rèn)大小。
b). 這里的代碼是縮略圖默認(rèn)存在當(dāng)前視頻路徑下面,并且文件名和視頻名稱一樣,多加了一個(gè)jpg的后綴。
2.2 調(diào)用Vitamio的視頻掃描服務(wù)
2.2.1 AndroidManifest.xml
<receiver android:name="io.vov.vitamio.MediaScannerReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.MEDIA_MOUNTED" /> <data android:scheme="file" /> </intent-filter> <intent-filter> <action android:name="com.yixia.vitamio.action.MEDIA_SCANNER_SCAN_FILE" /> <action android:name="com.yixia.vitamio.action.MEDIA_SCANNER_SCAN_DIRECTORY" /> <data android:scheme="file" /> </intent-filter> </receiver> <service android:name="io.vov.vitamio.MediaScannerService" android:exported="false" > <intent-filter> <action android:name="io.vov.vitamio.IMediaScannerService" /> </intent-filter> </service> <service android:name="io.vov.vitamio.VitamioService" android:exported="false" > <intent-filter> <action android:name="io.vov.vitamio.IVitamioService" /> </intent-filter> </service>
代碼說(shuō)明:
主要是MediaScannerService服務(wù)和MediaScannerReceiver監(jiān)聽(tīng)器的注冊(cè)。
2.2.2 啟動(dòng)掃描服務(wù)(MainFragmentActivity)
if (!io.vov.vitamio.LibsChecker.checkVitamioLibs(this, getClass().getName(), R.string.init_decoders, R.raw.libarm))
return;
OPreference pref = new OPreference(this);
//首次運(yùn)行,掃描SD卡
if (pref.getBoolean(PREF_KEY_FIRST, true)) {
getApplicationContext().startService(new Intent(getApplicationContext(), MediaScannerService.class).putExtra(MediaScannerService.EXTRA_DIRECTORY, Environment.getExternalStorageDirectory().getAbsolutePath()));
pref.putBooleanAndCommit(PREF_KEY_FIRST, false);
}
代碼說(shuō)明:
OPreference對(duì)象封裝了SharedPreferences 存儲(chǔ)數(shù)據(jù)。這里判斷了是否首次啟動(dòng)應(yīng)用,是的話啟動(dòng)視頻掃描服務(wù)。
2.2.3 獲取數(shù)據(jù)(FragmentFile)
private static final String[] PROJECTION_MEDIA = new String[] { Video.Media._ID, Video.Media.TITLE, Video.Media.TITLE_KEY, Video.Media.SIZE, Video.Media.DURATION, Video.Media.DATA, Video.Media.WIDTH, Video.Media.HEIGHT };
private static final String ORDER_MEDIA_TITLE = Video.Media.TITLE_KEY + " COLLATE NOCASE ASC";
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
Log.e(TAG, "onCreateLoader");
return new CursorLoader(mParent, Video.Media.CONTENT_URI, PROJECTION_MEDIA, null, null, ORDER_MEDIA_TITLE);
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor newCursor) {
Log.e(TAG, "onLoadFinished" + newCursor.getCount());
mAdapter.swapCursor(newCursor);
mListView.setSelectionFromTop(mVisiablePosition, mVisiableTop);
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
Log.e(TAG, "onLoaderReset");
mAdapter.swapCursor(null);
}
代碼說(shuō)明:
a). FragmentFile繼承了LoaderCallbacks<Cursor>,相關(guān)的資料大家可以搜一下。
b). 其中Video.Media.DATA 存的是視頻的路徑。
c). 取縮略圖的方法:
Video.Thumbnails.getThumbnail(ctx.getApplicationContext(), ctx.getContentResolver(), _id, Video.Thumbnails.MICRO_KIND, null)
這個(gè)"_id"就是Video.Media._ID,縮略圖存在SD里,大家可以自己加一些緩存處理。
三、注意事項(xiàng)
3.1 啟動(dòng)視頻服務(wù)掃描前,需要先確保解碼庫(kù)已經(jīng)被解壓,即調(diào)用代碼:LibsChecker.checkVitamioLibs
3.2 確保MediaScannerService與當(dāng)前應(yīng)用程序在一個(gè)進(jìn)程中,即不要指定其android:process,否則無(wú)法使用視頻掃描服務(wù)。
3.3 目前獲取掃描完成廣播接收還有問(wèn)題,即無(wú)法獲知是否已經(jīng)掃描完成。目前的辦法是每次數(shù)據(jù)變更后5秒再去判斷是否有該Service,沒(méi)有了就表示已經(jīng)掃描完成。參見(jiàn)方法MediaScannerReceiver.isServiceRunning。
解碼庫(kù)
https://bitbucket.org/ABitNo/ffmpegandroid
http://vplayer.net 的首頁(yè)底部已經(jīng)給出鏈接,注意代碼并不會(huì)實(shí)時(shí)同步,有需要的朋友可以參考一下。
四、OPlayer
4.1 下載
請(qǐng)移步#Taocode(SVN):
項(xiàng)目地址:http://code.taobao.org/p/oplayer
SVN地址:http://code.taobao.org/svn/oplayer/
4.2 更新
a). 移動(dòng)顯示SD剩余容量至底部,并在其左邊顯示正在加載的圖標(biāo)。
b). 保留原FragmentFile為FragmentFildOld,新版已經(jīng)切換至使用Vitamio內(nèi)置的視頻掃描服務(wù)。
c). 更換Adapter為CursorAdapter,同時(shí)更新A-Z的功能,參加代碼更新。
d). 新增了Preference,用于存儲(chǔ)一般設(shè)置數(shù)據(jù),例如是否首次啟動(dòng)。
五、補(bǔ)充說(shuō)明
已經(jīng)在SDK發(fā)布版本里面強(qiáng)調(diào)了許多,這里再?gòu)?qiáng)調(diào)一下:
5.1 Vitamio相關(guān)項(xiàng)目不能同時(shí)存在同一個(gè)設(shè)備上,也不能與VPlayer共存,這是一個(gè)已知的BUG,下一版本會(huì)解決。
5.2 Vitamio的示例工程需要在API Level14以上編譯,但是最低版本可以支持7以上(android:minSdkVersion="7")??梢园裿alues-v11和values-v14兩個(gè)文件夾刪除,Vitamio在目前項(xiàng)目使用中是兼容Android2.1以上版本,更低的沒(méi)有試過(guò),大家可以試一下。
5.3 網(wǎng)絡(luò)視頻播放不了、很慢可能有以下幾個(gè)原因:
a). 你的網(wǎng)速不行
b). 視頻服務(wù)器不行,或者其帶寬不夠
c). 可能是播放組件的問(wèn)題,我們還在持續(xù)的改進(jìn),加大再網(wǎng)絡(luò)播放這部分的優(yōu)化,感謝理解和支持。
遇到這種情況的時(shí)候,如果方便請(qǐng)把視頻測(cè)試地址發(fā)給我們,并附上簡(jiǎn)要說(shuō)明。(vplayer@yixia.com)
5.4 一直停留在初始化解碼包界面的問(wèn)題
一般出現(xiàn)這種情況,只有一個(gè)原因:你的設(shè)備上還有其他Vitamio的相關(guān)項(xiàng)目,參照5.1。
結(jié)束
這篇文章耗費(fèi)了我至少10個(gè)小時(shí)以上,昨晚熬到2點(diǎn),為的是解決使用者心中的疑慮,最終更好的服務(wù)于用戶。在文檔不夠全的情況下通過(guò)這種方式來(lái)與大家分享使用方法和技巧以及注意點(diǎn),還請(qǐng)大家諒解。
相關(guān)文章
Android實(shí)現(xiàn)ViewPage輪播圖效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)ViewPage輪播圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
Android中Spinner控件之鍵值對(duì)用法實(shí)例分析
這篇文章主要介紹了Android中Spinner控件之鍵值對(duì)用法,實(shí)例分析了Spinner控件控件的鍵值對(duì)實(shí)用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09
Android里實(shí)現(xiàn)退出主程序的提示代碼
當(dāng)用戶選擇"確定",就退出當(dāng)前的對(duì)話框。其中,有個(gè)很重要的函數(shù),Activity.finish(),通過(guò)調(diào)用這個(gè)函數(shù),退出當(dāng)前運(yùn)行的整個(gè)Android程序2013-06-06
Android RecyclerView實(shí)現(xiàn)多種item布局的方法
本篇文章主要介紹了Android RecyclerView實(shí)現(xiàn)多種item布局的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
深入理解Android中的Handler異步通信機(jī)制
這篇文章主要介紹了Android中的Handler異步通信機(jī)制,文中舉了兩個(gè)例子介紹了Handler里面實(shí)用的API中的方法,需要的朋友可以參考下2016-03-03
Android判斷是Wifi還是4G網(wǎng)絡(luò)代碼
這篇文章主要為大家詳細(xì)介紹了Android判斷網(wǎng)絡(luò)類型的方法,判斷是Wifi還是4G網(wǎng)絡(luò)代碼分享,感興趣的小伙伴們可以參考一下2016-07-07

