话说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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
读取视频信息 如需获取分P,需要以不同页码多次调用本接口 — @fython 调用地址 http://api.bilibili.cn/view 需要 App Key 参数 字段 必选 类型 说明 id true int AV号 page true int 页码 fav false int 是否读取会员收藏状态 (默认 0) 返回 返回值字段 字段类型 字段说明 play int 播放次数 revi
通过该接口可以获取指定用户的有效视频的信息,目前共六个版本,最新版本为V6: a.当地址为https://spark.bokecc.com/api/video/v6 需要传递以下参数 参数 说明 userid 用户 id,不可为空 videoid 视频 id,不可为空 imagetype 返回封面截图的类型,默认值:0(0:小图;1:大图) format 返回格式,xml 或 json 返回数据v
读取视频排行信息 调用地址 http://api.bilibili.cn/list 需要 App Key 参数 字段 必选 类型 说明 tid true int 分类编号 new排序为必填 其他为可选 exclude_tid false int 排除分类编号 不可与tid同时使用 mid false int 只显示该帐号投稿 begin false date 起始搜索日期 (格式:YYYY-mm-
通过该接口可以获取一个用户一个视频的弹幕信息, 地址为: https://spark.bokecc.com/api/video/barrage 需要传递以下参数: 参数 说明 userid 用户 id,不可为空 videoid 查询的videoid, 不可为空 starttime 开始时间,可选(yyyy-MM-dd HH:mm:ss) endtime 结束时间,可选(yyyy-MM-dd HH:
获取视频打点信息 a. 通过该接口可以获取单个视频打点信息, 地址为: http://spark.bokecc.com/api/mark/video 需要传递以下参数: 参数 说明 userid 用户 id,不可为空 videoid 视频 id,不可为空 返回数据videomark包括如下字段: 字段名 字段名 markList 打点列表 markList包括如下字段: 字段名 字段名 markT
通过该接口可以获取指定用户的一批有效视频(不包括删除、正在处理的视频)的信息,目前共七个版本,最新版本为V7: a.当地址为https://spark.bokecc.com/api/videos/v7 需要传递以下参数: 参数 说明 userid 用户 id,不可为空 mode 查询方式 0:指定要查询的视频的起始位置;1:指定一批视频进行查询(不传默认值为0) videoids 要查询的一批视频