时间:2021-08-22 09:49:41 | 栏目:Android代码 | 点击:次
话说2016年的直播比较火,2017年短视频又火了。但对于开发者来说隐藏在这背后的技术才是我们所关心的,毕竟我们是靠技术吃饭的。
现在在安卓中多媒体服务比较强大,而与视频有关的视频基本处理技术有必要学习一下。我前段时间也在做有关视频的一些需求,当然也涉及本文的标题内容。
经测试和研究发现在android中提取视频图片的方法只有MediaMetadataRetriever这个类比较靠谱简单实用。当然OpenGL-也可以做到哈!(后者不展开介绍),
最后会把完整的demo献上。
效果图
技术需求
调研
先看第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之间。
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