android提取视频多张图片和视频信息实例
话说2016年的直播比较火,2017年短视频又火了。但对于开发者来说隐藏在这背后的技术才是我们所关心的,毕竟我们是靠技术吃饭的。
现在在安卓中多媒体服务比较强大,而与视频有关的视频基本处理技术有必要学习一下。我前段时间也在做有关视频的一些需求,当然也涉及本文的标题内容。
经测试和研究发现在android中提取视频图片的方法只有MediaMetadataRetriever这个类比较靠谱简单实用。当然OpenGL-也可以做到哈!(后者不展开介绍),
最后会把完整的demo献上。
效果图
技术需求
- 需求一:给你一个视频提取第一个关键帧图片作为视频的封面,好处呢!?就是当没有播放的时候显示视频的首帧图片,当点击播放按钮时(或者自动播放)视频和首帧图片能连惯起来,是个很不错的体验。
- 需求二:提取多张图片,用户可以滑动这些缩略图来查看视频的大体内容,然后截取指定的视频区域进行编辑,这个需求类似:微信的小视频编辑界面。这个会在第二篇文章中实现。实现高速度提取视频图片。
- 需求三:提取视频的基本信息,比如宽高,视频的时长。这个主要用于上传视频文件时的参数提交,然后展示的时候用视频宽高做UI特效,比如不规则之美-瀑布流
调研
先看第3个问题,这个比较简单。主要是MediaMetadataRetriever这个系统API类。
大体意思就是:给定一个媒体文件,它能检索/输出一些元数据和帧。有点意思哈,它加载了动态库media_jni,可见它是一个十分接近底层的一个类。
技术实现-视频基本信息的获取
看一下类结构,有一些常量,作为一线码农,我懂你哦。
//1.初始化 mMetadataRetriever = new MediaMetadataRetriever(); //2.设置视频源文件mMetadataRetriever.setDataSource(file.getAbsolutePath()); //3.获取视频的宽 String w = mMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); //获取视频的高 String h = mMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); //获取视频的时长,ms mMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); // 等等...一些其它属性,比如媒体类型(3gp,mp4) ,视频旋转角度 //4.释放资源,防止内存泄漏,养成好习惯 mMetadataRetrieve.release();
我自己简单了写了一个util类,最后会献上。
技术实现-获取视频关键帧
直接上代码-我懂你
public Bitmap extractFrame(long timeMs) { //第一个参数是传入时间,只能是us(微秒) //OPTION_CLOSEST ,在给定的时间,检索最近一个帧,这个帧不一定是关键帧。 //OPTION_CLOSEST_SYNC 在给定的时间,检索最近一个同步与数据源相关联的的帧(关键帧) //OPTION_NEXT_SYNC 在给定时间之后检索一个同步与数据源相关联的关键帧。 //OPTION_PREVIOUS_SYNC 在给定时间之前检索一个同步与数据源相关联的关键帧。 // Bitmap bitmap = mMetadataRetriever.getFrameAtTime(timeMs * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC); Bitmap bitmap = null; for (long i = timeMs; i < fileLength; i += 1000) { bitmap = mMetadataRetriever.getFrameAtTime(i * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC); if (bitmap != null) { break; } } return bitmap; }
实用的时候直接: Bitmap bitmap = extractFrame(0); fileLength为视频的时长( 毫秒数),返回bitmap ,然后你用可以操作bitmap了,比如保存到sd卡上,看你爱好了。有人问:咿,怎么for循环啊?是这样的,我遇到过有时候你传入的0,它有时候返回的bitmap为null,找不到关键帧,因此你需要多次递增1秒获取。
那么需求一的实现也OK了,但是有一点需要注意就是展示视频的控件的宽和高和盖在上面的图片的控件的宽和高一样才有你播放的时候就连贯了。
技术实现-获取视频多张图片
我相信大家都能想到需求一的for循环一下不就可以了吗,是的,没错。
首先提取图片遇到高清视频可能耗时,我经过测试用vivo-xplay-5a ,1080高清视频,提取一张图片也就是mMetadataRetriever.getFrameAtTime()方法大概90-120ms之间。
- 方案:因此你需要一个工作线程+handler,每提取一张保存到sd卡中,然后用handeler发送保存图片的路径展示到UI上。demo中会展示到recycleview上,然后用glide图片加载框架一张一张的显示出来。当然你也可以自己缓存mMetadataRetriever.getFrameAtTime 返回的bitmap,不过不建议这么做。
- 怎么循环提取,假设视频文件长度是60s,需要提取10张图片,也就是1张图片代表了6s,60/10,
for (int i = 0; i < thumbnailsCount; i++) { if (stop) { metadataRetriever.release(); break; } long time = startPosition + interval * i; if (i == thumbnailsCount - 1) { if (interval > 1000) { String path = extractFrame(metadataRetriever, endPosition - 800, OutPutFileDirPath); sendAPic(path, endPosition - 800); } else { String path = extractFrame(metadataRetriever, endPosition, OutPutFileDirPath); sendAPic(path, endPosition); } } else { String path = extractFrame(metadataRetriever, time, OutPutFileDirPath); sendAPic(path, time); } }
stop是停止提取图片的标志,extractFrame方法就是metadataRetriever.getFrameAtTime()返回bitmap然后 bitmap.compress()返回保存到sd卡上的图片路径,最后sendAPic将提取的图片路径和时间加工为一个对象借助handler更新到UI线程中去,然后展示到recycleview上去,具体代码可以参考demo。这里保存的图片没有进行压缩,第二篇文章会具体讲到。
demo地址:https://github.com/ta893115871/MediaMetadataRetrieverDemo