在进行Android音视频开发的时候,我们可能会遇到需要获取视频制定位置的图片的需求。针对这个问题,我们有几种解决方案:分别为Android官方提供的MediaMetadataRetriever、基于FFmpeg封装的FFmpegMediaMetadataRetriever、还有就是基于FFmpeg自研发。
下面我们基于这几个实现方式进行介绍和整理 :
一、MediaMetadataRetriever
/**
* 获取视频某帧的图像,但得到的图像并不一定是指定position的图像。
*
* @param path 视频的本地路径
* @return Bitmap 返回的视频图像
*/
public static Bitmap getVideoFrame(String path) {
Bitmap bmp = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(path);
String timeString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
// 获取总长度
long totalTime = Long.parseLong(timeString) * 1000;
if (totalTime > 0) {
// 这里为了实现简单,我们直接获取视频中间的画面
bmp = retriever.getFrameAtTime(totalTime / 2, MediaMetadataRetriever.OPTION_CLOSEST);
}
} catch (RuntimeException ex) {
ex.printStackTrace();
} finally {
try {
retriever.release();
} catch (RuntimeException ex) {
ex.printStackTrace();
}
}
return bmp;
}
1.1 本方案优点:
实现方便,因为使用的是系统Api, 不会增加包体积
1.2 本方案缺点:
支持的格式较少,对网络的视频的支持度较低,且在获取指定位置的视频画面的时候,可能因为GOP的大小导致获取的视频位置不准确。
且可能在使用中遇到以下问题:
可能遇到因为视频格式导致的异常:
mindmaptopicMediaMetadataRetrieverJNI: getFrameAtTime: videoFrame is a NULL pointer<br>
可能遇到获取网络图片失败的问题:
java.lang.IllegalArgumentException
at android.media.MediaMetadataRetriever.setDataSource(MediaMetadataRetriever.java:73)
或
java.lang.RuntimeException: setDataSource failed: status = 0x80000000
当使用MediaMetadataRetriever无法满足我们的需求实现的时候,这时候推荐使用FFmpegMediaMetadataRetriever。
二、FFmpegMediaMetadataRetriever
FFmpegMediaMetadataRetriever的开源项目地址为:https://github.com/wseemann/FFmpegMediaMetadataRetriever。
FFmpegMediaMetadataRetriever的作者很有心,提供了同MediaMetadataRetriever相同的Api。
2.1 本方案集成方式:
在需要使用的module的build.gradle文件中添加如下配置:
dependencies {
implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.15'
implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-armeabi-v7a:1.0.15'
implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-x86:1.0.15'
implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-x86_64:1.0.15'
implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-arm64-v8a:1.0.15'
}
使用方式非常像MediaMetadataRetriever,下面是使用方式的代码:
/**
* 获取视频某帧的图像
*
* @param path 视频的路径
* @return Bitmap 返回的视频图像
*/
public static Bitmap getVideoFrameByFMMR(String path) {
Bitmap bmp = null;
FFmpegMediaMetadataRetriever retriever = new FFmpegMediaMetadataRetriever();
try {
retriever.setDataSource(path);
String timeString = retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION);
// 获取总长度
long totalTime = Long.parseLong(timeString) * 1000;
if (totalTime > 0) {
bmp = retriever.getFrameAtTime(totalTime / 2, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
}
} catch (RuntimeException ex) {
ex.printStackTrace();
} finally {
try {
retriever.release();
} catch (RuntimeException ex) {
ex.printStackTrace();
}
}
return bmp;
}
需要注意的是,直播流的话,不能使用retriever.getFrameAtTime(long timeUs, int option)的方式获取指定时间的图像。但是可以使用retriever.getFrameAtTime()获取当前时间的画面。
2.2 本方案优点:
支持的格式多;
对网络的视频的支持度好;
且在获取指定位置的视频画面的时候,定位相对准确。
2.3 本方案缺点:
引入了FFmpeg库,会导致打包出来的Apk出现爆炸式的大小增加(native层的库可据实际需要进行精简);
当视频的时长较长或者分辨率较大的时候,可能会导致获取视频画面的耗时较长。
三、基于FFmpeg自研发
基于FFmpeg自研发的方式实现起来和实现播放器差不多,准确来说就是通过seek定位指定的Frame,然后解码YUV数据为Bitmap,后面有时间会对此方案进行开源。
方案优点:获取画面定位精确,且可根据实际需要实现库的裁剪,且实现流程可控,可定制逻辑。
方案优点缺点:实现起来难度较大,复杂度较高。