Android 获取视频画面方式整理

在进行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,后面有时间会对此方案进行开源。

方案优点:获取画面定位精确,且可根据实际需要实现库的裁剪,且实现流程可控,可定制逻辑。

方案优点缺点:实现起来难度较大,复杂度较高。

原文地址:https://www.cnblogs.com/renhui/p/14179259.html