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

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

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

  • 相关阅读:
    2017.1.16【初中部 】普及组模拟赛C组总结
    用Redis实现分布式锁 与 实现任务队列
    Mysql+Keepalived双主热备高可用操作记录
    Linux下防御DDOS攻击的操作梳理
    真正的ddos防御之道,简单干脆有效!
    ip黑白名单防火墙frdev的原理与实现
    一种简单的处理大流量访问的方法
    PHP解决网站大流量与高并发
    PHP反射机制实现自动依赖注入
    nginx 根据域名和地址跳转
  • 原文地址:https://www.cnblogs.com/renhui/p/14179259.html
Copyright © 2020-2023  润新知