最近在side project中遇到了需要从视频中抓取多张图片的需求。安卓已经提供了从视频获取预览图片的ThumbnailUtils, 但此类不能根据timestamp获取bitmap。

以下记录自己找出的解决方案。

需求

在本地视频根据时间戳(timestamp)抓取bitmap图像。

解决方案

配合使用这些flag可以达到不同的程度的时间精确度

OPTION_PREVIOUS_SYNC: 前一个i-frame

OPTION_NEXT_SYNC: 后一个 i-frame

OPTION_CLOSEST_SYNC: 最近的i-frame,不管前后

OPTION_CLOSEST: 最近的frame,不一定是i-frame。上面三个flag只需要decode一张frame即可(而且是i-frame)。但这个flag需要decode多张frame才能接近输入的timestamp,因此速度会慢些。

值得一提的是此类会用binder IBC给system media service发送请求, 因此decoder是在系统服务中进行的,并非在我自己的app进程中。

方法调用时logcat可看到如下log

918 13498 I OMXMaster: makeComponentInstance(OMX.google.h264.decoder) in omx@1.0-service process

918 28062 I OMXMaster: makeComponentInstance(OMX.google.h264.decoder) in omx@1.0-service process

918 1854 I OMXMaster: makeComponentInstance(OMX.google.h264.decoder) in omx@1.0-service process

918 13498 I OMXMaster: makeComponentInstance(OMX.google.h264.decoder) in omx@1.0-service process

需要注意的坑

如果只看文档就会以为用getFrameAtTime + OPTION_CLOSEST就能非常精准的返回在输入timestamp附近的视频帧,但在不同device上跑过代码才发现其行为其实很不统一。:(

高端机基本都能做到预期效果,返回的bitmap在输入timestamp附近。但低端机型直接无视OPTION_CLOSEST 返回附近I-frame的bitmap,因此有时得到的图像跟输入的timestamp相差甚远。例如输入的是3.3sec,返回的有可能是2.8sec的i-frame图像。

出于好奇,我在低端机上做了个小实验。扫描一个视频文件的video timestamp,再把他们逐一用来调用getFrameAtTime,看看返回的bitmap有没有重复。

很明显的看到高端机基本无重复的bitmap,低端机却有大把大把的重复。猜测应该是厂商为了性能而只做了附近i-frame的解码就返回了。有点坑。:(

如果需要在所有机器上返回精准timestamp的bitmap,恐怕只能自己操作decoder了。估计非常复杂。

此外 API28+ 新增加了MediaMetadataRetriever.getFramesAtIndex() 。 这应该是比pts更加稳定的抓取方式,毕竟index是连续的int。但不知低端机型会不会继续坑。

==高端

D/MediaMetadataRetrieverRunner: pts 33375 digest :ym71cu9iO1H94190FWVgeg==

D/MediaMetadataRetrieverRunner: pts 66625 digest :y9hG93MqABw3ILMta/noUg==

D/MediaMetadataRetrieverRunner: pts 100000 digest :0l2CkOYDNyxvZCfCeLuq9A==

D/MediaMetadataRetrieverRunner: pts 133375 digest :OHSVQQOxrO77mh9f7tTorQ==

D/MediaMetadataRetrieverRunner: pts 166625 digest :XZ8kDFxsdjgBkUzKl41dgA==

D/MediaMetadataRetrieverRunner: pts 200000 digest :0/13IPmw1gP9FLdpsJc/Wg==

D/MediaMetadataRetrieverRunner: pts 233375 digest :TeHChk9OWbb8nLbrFphlEg==

D/MediaMetadataRetrieverRunner: pts 266625 digest :GEzE3LtFzG88bT7WtMTbxA==

D/MediaMetadataRetrieverRunner: pts 300000 digest :QjdeoZYwYgNxV144kKLTNw==

D/MediaMetadataRetrieverRunner: pts 333375 digest :0DZCQer3BqMPfSYTJaafMg==

D/MediaMetadataRetrieverRunner: pts 366625 digest :mfjEap/awknngWIeyPAScg==

D/MediaMetadataRetrieverRunner: pts 400000 digest :Q1oSKau7n9boi0vDSFZJFA==

D/MediaMetadataRetrieverRunner: pts 433375 digest :kW0vjMra+boztvH+PXQrlw==

D/MediaMetadataRetrieverRunner: pts 466625 digest :a6C/BpHAcr4BxSDg7Gt4bQ==

D/MediaMetadataRetrieverRunner: pts 500000 digest :odAEN5ESKWrLmXwDu09arA==

D/MediaMetadataRetrieverRunner: pts 533375 digest :+zTTQsil/s6on0EVqFuH/Q==

D/MediaMetadataRetrieverRunner: pts 566625 digest :ZqsNHZd1r12UL7cFHdc9vw==

D/MediaMetadataRetrieverRunner: pts 600000 digest :CgUZWadeCe+S+e28C/4qkA==

D/MediaMetadataRetrieverRunner: pts 633375 digest :hUaJy2jWWa9RU4dR24Om7w==

D/MediaMetadataRetrieverRunner: pts 666625 digest :a5HlUYUJcz3xaQrn/hNsIg==

D/MediaMetadataRetrieverRunner: pts 700000 digest :YF70StAojixZxjk8epZL3w==

D/MediaMetadataRetrieverRunner: pts 733375 digest :rolF61sMxRMSP09ePtUGcQ==

D/MediaMetadataRetrieverRunner: pts 766625 digest :K+qZUFNP5EgzWPscmmnFew==

D/MediaMetadataRetrieverRunner: pts 800000 digest :d8iVGhHf3VpMn+vMnBdmng==

D/MediaMetadataRetrieverRunner: pts 833375 digest :cKREwaki8AJmOuSVa8Zvbw==

D/MediaMetadataRetrieverRunner: pts 866625 digest :b3QXkX+wTE1CuCe79JK7Ww==

D/MediaMetadataRetrieverRunner: pts 900000 digest :atzAyrZNcOf8Ghgf04lftw==

D/MediaMetadataRetrieverRunner: pts 933375 digest :hS9ukOCLMCobHplBeRNdOA==

D/MediaMetadataRetrieverRunner: pts 966625 digest :rZyr6vVt5ae+TiMMVTCRrg==

=====

==低端

D/MediaMetadataRetrieverRunner: pts 1601625 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1635000 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1668375 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1701750 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1735000 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1768375 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1801750 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1835125 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1868500 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1901875 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1935250 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 1968625 digest :Ba29dyKehU1o6EanhG0wvg==

D/MediaMetadataRetrieverRunner: pts 2002000 digest :dcMXdpTton6/YQHJA2zCjg==

D/MediaMetadataRetrieverRunner: pts 2035375 digest :830odrRgw9UyAhNKeBUWAA==

D/MediaMetadataRetrieverRunner: pts 2068750 digest :8WFabowQKX+I2q3XNC2HJg==

D/MediaMetadataRetrieverRunner: pts 2102125 digest :8WFabowQKX+I2q3XNC2HJg==

D/MediaMetadataRetrieverRunner: pts 2135500 digest :8WFabowQKX+I2q3XNC2HJg==

D/MediaMetadataRetrieverRunner: pts 2168875 digest :dcMXdpTton6/YQHJA2zCjg==

D/MediaMetadataRetrieverRunner: pts 2202250 digest :dcMXdpTton6/YQHJA2zCjg==

D/MediaMetadataRetrieverRunner: pts 2235500 digest :dcMXdpTton6/YQHJA2zCjg==

D/MediaMetadataRetrieverRunner: pts 2268875 digest :dcMXdpTton6/YQHJA2zCjg==

D/MediaMetadataRetrieverRunner: pts 2302250 digest :dcMXdpTton6/YQHJA2zCjg==

D/MediaMetadataRetrieverRunner: pts 2335625 digest :h5ZNxcxO6WrbYFQPsknjnw==

D/MediaMetadataRetrieverRunner: pts 2369000 digest :KXZQ788bP25Oqi7sHxWPLg==

D/MediaMetadataRetrieverRunner: pts 2402375 digest :KXZQ788bP25Oqi7sHxWPLg==

D/MediaMetadataRetrieverRunner: pts 2435750 digest :KXZQ788bP25Oqi7sHxWPLg==

D/MediaMetadataRetrieverRunner: pts 2469125 digest :KXZQ788bP25Oqi7sHxWPLg==

D/MediaMetadataRetrieverRunner: pts 2502500 digest :2h341j90Scn3kRtA4Fr+IA==

D/MediaMetadataRetrieverRunner: pts 2535875 digest :2h341j90Scn3kRtA4Fr+IA==

D/MediaMetadataRetrieverRunner: pts 2569250 digest :OOdPxTb4/NWbUrC8HDzpng==

D/MediaMetadataRetrieverRunner: pts 2602625 digest :OOdPxTb4/NWbUrC8HDzpng==

D/MediaMetadataRetrieverRunner: pts 2636000 digest :OOdPxTb4/NWbUrC8HDzpng==

D/MediaMetadataRetrieverRunner: pts 2669375 digest :OOdPxTb4/NWbUrC8HDzpng==

D/MediaMetadataRetrieverRunner: pts 2702750 digest :OOdPxTb4/NWbUrC8HDzpng==

相关代码

public class MediaMetadataRetrieverRunner {

private String TAG = MediaMetadataRetrieverRunner.class.getSimpleName();

void scanAndDigest(String path) throws Exception {

List ptsUsList = VideoPtsScanner.scanPts(path);

Log.d(TAG, "pts list in micro sec: " + ptsUsList);

MediaMetadataRetriever retriever = new MediaMetadataRetriever();

retriever.setDataSource(path);

for (long ptsUs : ptsUsList) {

Bitmap bitmap = retriever.getFrameAtTime(ptsUs, MediaMetadataRetriever.OPTION_CLOSEST);

if (bitmap == null) {

Log.e(TAG, "got null in pts " + ptsUs);

continue;

}

int size = bitmap.getAllocationByteCount();

ByteBuffer buffer = ByteBuffer.allocateDirect(size);

bitmap.copyPixelsToBuffer(buffer);

buffer.flip();

String digest = makeString(buffer);

Log.d(TAG, "pts " + ptsUs + " \t digest :" + digest);

}

retriever.release();

}

private String makeString(ByteBuffer buffer) throws NoSuchAlgorithmException {

MessageDigest md5 = MessageDigest.getInstance("MD5");

md5.update(buffer);

byte[] digest = md5.digest();

// covert into base64 string, since it's already provided in android

return Base64.encodeToString(digest, Base64.DEFAULT);

}

}

-----

public class VideoPtsScanner {

public static List scanPts(String path) throws IOException {

MediaExtractor mediaExtractor = new MediaExtractor();

mediaExtractor.setDataSource(path);

mediaExtractor.selectTrack(getVideoTrack(mediaExtractor));

List ret = new ArrayList();

long pts = -1;

while ((pts = mediaExtractor.getSampleTime()) >= 0) {

ret.add(pts);

mediaExtractor.advance();

}

mediaExtractor.release();

return ret;

}

private static int getVideoTrack(MediaExtractor extractor) {

for (int i = 0; i < extractor.getTrackCount(); i++) {

MediaFormat format = extractor.getTrackFormat(i);

if (isVideo(format)) {

return i;

}

}

return -1;

}

private static boolean isVideo(MediaFormat format) {

return format.getString(MediaFormat.KEY_MIME).toLowerCase().contains("video");

}

}

android爬取视频播放地址,Android 视频中抓取图像 - MediaMetadataRetriever相关推荐

  1. python爬取地图地址_用Python抓取百度地图里的店名,地址和联系方式

    原标题:用Python抓取百度地图里的店名,地址和联系方式 每日干货好文分享丨请点击+关注 欢迎关注天善智能微信公众号,我们是专注于商业智能BI,大数据,数据分析领域的垂直社区. 对商业智能BI.大数 ...

  2. matlab弹幕抓取,B站某视频弹幕抓取

    爬虫这东西很实用,意义不下于你学会做PPT和Excel.真正掌握方法论很难,需要时间和不断的实践.但掌握一门小工具,投入和产出比在我看来是比较惊人的. 爬虫其实没有这么难.最近在三节课上上了一堂陈大欣 ...

  3. 利用python_opencv和dlib实现从视频中抓取人脸照片并保存(亲测有效)

    系统:win10 编译环境:pycharm python库:cv2.dlib(自己安装) 代码: import dlib import cv2 # 加载并初始化检测器 detector = dlib. ...

  4. 【Android+OkHttp3+Jsoup】 模拟登录教务系统 抓取课表和成绩

    原文链接:https://blog.csdn.net/u013347241/article/details/52711018 今天这篇文章为大家带来的是模拟登录教务系统并抓取课表和成绩的详细实现过程. ...

  5. 【转】 asp.net从视频文件中抓取一桢并生成图像文件的方法 实现多语言本地化应用程序 自动返回上次请求页面...

    asp.net从视频文件中抓取一桢并生成图像文件的方法 http://www.bianceng.cn/webkf/aspx/201012/21428.htm WebUIValidation.js ht ...

  6. python爬虫怎么爬同一个网站的多页数据-如何用Python爬数据?(一)网页抓取

    如何用Python爬数据?(一)网页抓取 你期待已久的Python网络数据爬虫教程来了.本文为你演示如何从网页里找到感兴趣的链接和说明文字,抓取并存储到Excel. 需求 我在公众号后台,经常可以收到 ...

  7. python爬网站数据实例-如何用Python爬数据?(一)网页抓取

    如何用Python爬数据?(一)网页抓取 你期待已久的Python网络数据爬虫教程来了.本文为你演示如何从网页里找到感兴趣的链接和说明文字,抓取并存储到Excel. 需求 我在公众号后台,经常可以收到 ...

  8. 抓取百度页面html,百度会抓取页面代码中的注释内容吗

    百度会抓取页面代码中的注释内容吗 内容导读:百度会抓取页面代码中的注释内容吗?答案是百度会抓取,但是在提取正文的时候会直接忽略掉,也就是说页面代码的注释内容不会影响到页面质量,所以大家尽管放心. 问题 ...

  9. python将视频像素抓取替换导出mosaic效果的文本视频

    python将视频像素抓取替换导出mosaic效果的文本视频 第一次写博客,关于python的. 全网关于这种视频的的合成代码几乎没有,所以我提供了一套这样的思路. 完成过程很痛苦.因为全网类似的太少 ...

最新文章

  1. Camera Calibration 相机标定
  2. 正态分布随机数 C语言,C语言产生满足正态分布的随机数
  3. 二、入门爬虫,爬取百度图片
  4. EXCEL——批量生成中国各省省会经纬度JSON的一种方法
  5. iOS Hacker LLDB 和 debugserver 实例调试
  6. ubuntu-14.04.2-desktop-i386.iso:ubuntu-14.04.2-desktop-i386:安装Oracle11gR2
  7. 进程间通信IPC(二)(共享内存、信号、信号量)
  8. vi 编辑器跳转到指定行数
  9. UVA-1045 - The Great Wall Game(二分图最佳匹配)
  10. docker fdfs修改storage ip
  11. scriptalert(1)/script
  12. 2019年开源安全现状调查报告发布
  13. java 代码压缩javascript_利用Java来压缩 JavaScript 代码详解
  14. keep dark about sth
  15. 安全数组-简易矩阵容器-STL及迭代器仿写作业
  16. [LeetCode] NO. 242 Valid Anagram
  17. Object C中创建线程的方法有NSThread,GCD, NSOperation
  18. Jquery常用开发插件收集
  19. 朴素贝叶斯算法优缺点
  20. ubuntu的使用--系统目录篇(文末附Desktop目录位置)

热门文章

  1. T94 EndNote Journal List批量更新方案
  2. 伯俊软件完成1亿元A轮融资,康煦投资领投,峰尚资本跟投
  3. 利用python抓取网页各种类型内容(静态、动态)
  4. 定期存款可以提前取出来吗_定期存款可以提前取吗 定期存款没到期怎么取出来?...
  5. 共享池 shared pool
  6. 一位父亲给女儿的九条人生忠告
  7. 环境质量现状调查与分析
  8. android 11.0 去掉未知来源弹窗 默认授予安装未知来源权限
  9. 文字图片滚动scrollbox插件
  10. PHP用户注册页面测试用例,用户注册界面测试用例