文章發表於我的部落格如何判斷螢石雲視訊是否可以播放
最近遇到一個問題,需要判斷視訊檔案是否是真正的視訊檔案。 什麼意思呢?螢石的攝像頭是將視訊寫入 TF 卡的:
通過螢石雲視訊平臺將TF卡格式化後,程式會採用預佔空間的方式預先將1/4的空間作為視訊或者圖片的儲存空間。
然後他預寫入的檔案是.mp4
字尾的,但是是不可播放的檔案。所以一旦播放器播放它,可能就會出錯了。為了避免這樣的情況發生,我們能否在檢索視訊的時候就識別出無法播放的視訊呢?
我一開始的思路是,能否通過判斷檔案型別的方式來判斷是否播放呢?有可能他預格式化的視訊檔案,雖然字尾是.mp4
,但是本質上不是一個視訊檔案呢?
判斷檔案型別
在 Java 中,比較常見的用來判斷檔案型別的庫,就是Apache Tika。 引入依賴後,使用起來也十分簡單:
File file = new File(System.getProperty("user.home")+ "/Downloads/");
if (file.exists()){
for (File file1 : Objects.requireNonNull(file.listFiles())){
if (file1.isDirectory()){
continue;
}
try {
String type = new Tika().detect(file1);
System.out.println(file1.getName() + " ======>>> " + type);
} catch (IOException e) {
e.printStackTrace();
}
}
}
複製程式碼
下面是輸出結果:
background.svg ======>>> image/svg+xml
new.json ======>>> application/json
mid_top_1.jpg ======>>> image/jpeg
.DS_Store ======>>> application/octet-stream
apache-maven-3.5.4-bin.tar.1.gz ======>>> application/x-gzip
這本來是張 png 圖片.mp4 ======>>> image/png
.localized ======>>> application/octet-stream
old.json ======>>> application/json
不學無數 — Java 中 IO 和 NIO - 掘金.pdf ======>>> application/pdf
Layout_Mobile_Whiteframe.ai ======>>> application/pdf
Snipaste_2018-09-22_22-36-21.png ======>>> image/png
background.png ======>>> image/png
hiv00014.mp4 ======>>> video/mp4
SeimiCrawler-master.1.zip ======>>> application/zip
cn_windows_10_business_editions_version_1803_updated_march_2018_x64_dvd_12063730.iso ======>>> application/x-iso9660-image
i_con_permission.png ======>>> image/png
錯誤.png ======>>> image/png
open_gapps-arm64-9.0-pico-20180923.zip ======>>> application/zip
forPush.sh ======>>> application/x-sh
adbIn.sh ======>>> application/x-sh
Havoc-OS-v2.0-20180923-oneplus3-Official.zip ======>>> application/zip
MockingBot.dmg ======>>> application/octet-stream
複製程式碼
可以看到,幾乎全部格式都識別出來了。而且我這裡有一個『渾水摸魚』的『傢伙』,那就是這本來是張 png 圖片.mp4
,人如其名。
這個也識別出來了(octet-stream
指的是二進位制檔案)。
那 Tika 究竟是怎麼做的呢?讓我們來看看原始碼唄。
篇幅所限,如果你對這個部分有興趣,可以移步我的另一篇文章Tika 原始碼淺析。
這裡假設你已經看過了 Tika 原始碼...
現在我們知道了 Tika 通過檔案的首部位元組、檔案字尾判斷檔案的型別。但是這樣依舊無法判斷視訊是否可以播放。 因為在實際使用中,我發現,有一些個視訊檔案,雖然是無法播放的,但是它們的首部已經被寫入了,成為另一個『空視訊檔案』。 真的蛋疼。如果單純使用 Tika 的話,顯然會有誤差。 那麼有沒有其他應用層面的 trick 呢?
答案就是 FFmpegMediaMetadataRetriever。
FFmpegMediaMetadataRetriever 是什麼?
之所以說是 trick...是因為 FFmpegMediaMetadataRetriever 是一個獲取媒體檔案資訊的庫。 我在使用時發現,如果一個視訊只被寫了頭部,但是沒有實際內容的話,該視訊是沒有編碼資訊的。 這個想法是來自MediaInfo這個軟體。因為我是先用這個軟體測試了一遍,發現是可行的。 然後我找到了在 Android 平臺可用的一個類似的庫,也就是 FFmpegMediaMetadataRetriever
使用
我們引入依賴
implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever:1.0.14'
複製程式碼
然後開始使用:
public static List<String> getAvailableVideoList(String SDCardPath){
if (TextUtils.isEmpty(SDCardPath)){
return null;
}
List<String> pathList = new ArrayList<>();
FFmpegMediaMetadataRetriever mediaMetadataRetriever = new FFmpegMediaMetadataRetriever();
File fileList = new File(SDCardPath);
if (fileList.exists()) {
try {
for (File file : fileList.listFiles()) {
if (file.getName().endsWith("mp4")){
mediaMetadataRetriever.setDataSource(file.getPath());
mediaMetadataRetriever.extractMetadata(METADATA_KEY_VIDEO_CODEC);
pathList.add(file.getPath());
}
}
} catch (IllegalArgumentException iae) {
iae.printStackTrace();
}
}
return pathList;
}
複製程式碼
如果視訊檔案無效的,FFmpegMediaMetadataRetriever 會丟擲一個異常:
java.lang.IllegalArgumentException: setDataSource failed: status = 0xFFFFFFFF
複製程式碼
然後我們捕獲這個異常,做一些其他的工作就可以。 這個方法就目前的使用來看,是最穩定和準確的方法。 缺點是:
-
setDataSource()
過程有一些耗時,實際上測試,256MB 的視訊檔案,呼叫一次需要將近 1s 的時間 -
會拋異常...
還有其他辦法嗎?
最一開始想的,其實是開一個 VideoPlayer,然後在onError()
設一個監聽器,如果播放錯誤就說明該檔案無效。
但是經過我自己的評估,這樣的效能實際上更差。
也不知道是否有其他更優的方法,如果有的話,還請不吝賜教~
參看