Glide 显示视频缩略图及遇到的坑

实现原理

Glide支持视频格式的文件,但是在3.x里会有些欠缺。其底层是通过 MediaMetadataRetriever实现的。

MediaMetadataRetriever拥有获取视频的第几帧的能力,Glide获取视频里的第n帧的代码如下:

VideoBitmapDecoder.java

@Override

public Bitmap decode(ParcelFileDescriptor resource, BitmapPool bitmapPool, int outWidth, int outHeight,

DecodeFormat decodeFormat)

throws IOException {

MediaMetadataRetriever mediaMetadataRetriever = factory.build();

mediaMetadataRetriever.setDataSource(resource.getFileDescriptor());

Bitmap result;

if (frame >= 0) {

result = mediaMetadataRetriever.getFrameAtTime(frame);

} else {

result = mediaMetadataRetriever.getFrameAtTime();

}

mediaMetadataRetriever.release();

resource.close();

return result;

}

提取核心代码

mediaMetadataRetriever获取Bitmap的代码:

val file = FileInputStream(File(path))

val s = MediaMetadataRetriever()

s.setDataSource(file.fd)

file.close()

val bitmap = s.getFrameAtTime(-1)

imageView.setImageBitmap(bitmap)

s.release()

Glide3.x的一个bug

但3.x的Glide缓存策略不能是Source我们来分析各种缓存策略Glide的内部的逻辑

Result缓存策略

EngineRunnable.java

private Resource> decode() throws Exception {

if (isDecodingFromCache()) {

return decodeFromCache();

} else {

return decodeFromSource();

}

}

EngineRunnable分走两次,第一次走isDecodingFromCache

private Resource> decodeFromCache() throws Exception {

Resource> result = null;

//...ignore code

//先从result缓存里获取

result = decodeJob.decodeResultFromCache();

if (result == null) {

//再从源文件缓存里获取

result = decodeJob.decodeSourceFromCache();

}

return result;

}

第一次加载显示decodeResultFromCache会为空,我们略过,重点看decodeSourceFromCache

public ResourcedecodeSourceFromCache() throws Exception {

//如果不是cacheSource直接返回空

if (!diskCacheStrategy.cacheSource()) {

return null;

}

//...ignore code

}

decode失败以后会把EngineRunnable 扔给另外一个线程

private void onLoadFailed(Exception e) {

if (isDecodingFromCache()) {

stage = Stage.SOURCE;

manager.submitForSource(this);

} else {

manager.onException(e);

}

}

因此它会再一次走到run()方法里的decode,而此次走的是decodeFromSource

DecodeJob.java

public ResourcedecodeFromSource() throws Exception {

Resourcedecoded = decodeSource();

return transformEncodeAndTranscode(decoded);

}

private ResourcedecodeSource() throws Exception {

Resourcedecoded = null;

try {

// 通过fetcher去加载Source

final A data = fetcher.loadData(priority);

//...ignore code

decoded = decodeFromSourceData(data);

} finally {

fetcher.cleanup();

}

return decoded;

}

DecodeJob.java

private ResourcedecodeFromSourceData(A data) {

final Resourcedecoded;

if (diskCacheStrategy.cacheSource()) {

decoded = cacheAndDecodeSourceData(data);

} else {

//因为缓存策略是Result,所以走的是该处

decoded = loadProvider.getSourceDecoder().decode(data, width, height);

//...ignore code

}

return decoded;

}

SourceDecode其实就是ImageVideoBitmapDecoder

public Resourcedecode(ImageVideoWrapper source

, int width, int height){

Resourceresult = null;

InputStream is = source.getStream();

if (is != null) {

//通过streamDecoder去decode图,它是StreamBitmapDecoder

result = streamDecoder.decode(is, width, height);

}

if (result == null) {

ParcelFileDescriptor fileDescriptor = source.getFileDescriptor();

if (fileDescriptor != null) {

//通过fileDescriptorDecoder去decode图,它是FileDescriptorBitmapDecoder

result = fileDescriptorDecoder.decode(fileDescriptor, width, height);

}

}

return result;

}

ImageVideoBitmapDecoder里有两种decoder

StreamBitmapDecoder直接将输入流转成Bitmap

FileDescriptorBitmapDecoder能将流通过VideoBitmapDecoder去转Bitmap

而VideoBitmapDecoder底层就是通过MediaMetadataRetriever去获取第一帧

如果源文件是视频,它将先通过StreamBitmapDecoder去decode,结果decode失败result=null

然后通过FileDescriptorBitmapDecoder去decode,可见这里Glide内部并不知道源文件是图片还是视频,所以先用图片的方式解

而图片方式解的时候也是去读它的头文件:

@Override

public Resourcedecode(InputStream source, int width, int height) {

//通过downsampler去decode

Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);

return BitmapResource.obtain(bitmap, bitmapPool);

}

@Override

public Bitmap decode(InputStream is, BitmapPool pool, int outWidth,

int outHeight, DecodeFormat decodeFormat) {

final ByteArrayPool byteArrayPool = ByteArrayPool.get();

final byte[] bytesForOptions = byteArrayPool.getBytes();

final byte[] bytesForStream = byteArrayPool.getBytes();

//此处用了Option池,值得学习!

final BitmapFactory.Options options = getDefaultOptions();

//...ignore code

final Bitmap downsampled =

downsampleWithSize(invalidatingStream, bufferedStream,

options, pool, inWidth, inHeight, sampleSize,

decodeFormat);

private static Bitmap decodeStream(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,

BitmapFactory.Options options) {

if (options.inJustDecodeBounds) {

is.mark(MARK_POSITION);

} else {

bufferedStream.fixMarkLimit();

}

//如果是视频文件此处无法decode出Bitmap

final Bitmap result = BitmapFactory.decodeStream(is, null, options);

//...ignore code

return result;

}

第一种decode失败会用第二种,也就是最终会走到VideoBitmapDecoder去解析

Source缓存策略

如果缓存策略是Source

private ResourcedecodeSource() throws Exception {

Resourcedecoded = null;

try {

final A data = fetcher.loadData(priority);

//...ignore code

decoded = decodeFromSourceData(data);

} finally {

fetcher.cleanup();

}

return decoded;

}

private ResourcedecodeFromSourceData(A data) {

final Resourcedecoded;

//因为是缓存Source因为走cacheAndDecodeSourceData方法

if (diskCacheStrategy.cacheSource()) {

decoded = cacheAndDecodeSourceData(data);

} else {

long startTime = LogTime.getLogTime();

decoded = loadProvider.getSourceDecoder().decode(data, width, height);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Decoded from source", startTime);

}

}

return decoded;

}

而加载缓存的Decoder是CacheDecoder,非常不幸它不是ImageVideoBitmapDecoder

private ResourceloadFromCache(Key key) {

File cacheFile = diskCacheProvider.getDiskCache().get(key);

if (cacheFile == null) {

return null;

}

Resourceresult = null;

try {

result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);

} finally {

if (result == null) {

diskCacheProvider.getDiskCache().delete(key);

}

}

return result;

}

它是FileToStreamDecoder,以下是截图

而FileToStreamDecoder的Decoder是StreamBitmapDecoder,最终它是把该文件当成图片解析

因此导致它失败,如果result==null,它还会走

diskCacheProvider.getDiskCache().delete(key);

所以如果是视频流,缓存策略是Source,它会很让你失望,不停的copy文件,再解,解失败再删除,一直这样下去

如果该源文件很大,会影响Glide的性能!

为了验证这一猜想,我们可以通过抓包工具看一下发生了什么:

结果它确实把这个视频存下来了,不过发现decode失败就会把它删除

所以,显示视频图的时候,一定切记不要用Source缓存策略,它不仅无法加载成功,而且会给你带来很大隐患

结束!

glide缩略图存储 android,Glide 显示视频缩略图及遇到的坑相关推荐

  1. ExoPlayer拖动进度条时显示视频缩略图

    如果ImageView位置没有超出SeekBar的宽度,则计算坐标并移动, 如果它向外延伸,坐标将设置在ImageView的左侧或右侧. ※ExoPlayer使用ExoPlayer release-v ...

  2. uni-app 组件video显示视频缩略图

    uni-app video 组件显示视频缩略图 HTML代码 JS代码 话不多少直接上代码,需要注意的是,视频默认自动播放,获取到视频时候立马暂停视频 HTML代码 <template>& ...

  3. android获取网络视频缩略图,Android 获取视频(本地和网络)缩略图的解决方案

    在Android 开发视频的时候,通常都需要显示视频列表,而视频列表通常都有一张视频缩略图,那么它是怎么获取的呢, 关于网络视频的缩略图的实现方案主要有两种: 1.后台返回视频时顺便连缩略图的路径都返 ...

  4. android获取网络视频缩略图,Android 获取缩略图,网络视频,或者本地视频

    IM软件发送视频文件,需要显示缩略图: import android.graphics.Bitmap; import android.media.MediaMetadataRetriever; imp ...

  5. android glide圆形图片,Android Glide加载图片成圆形

    释放双眼,带上耳机,听听看~! 今天,简单讲解android使用glide加载图片成圆形. 这个很简单,因为之前需要在RecyclerView里加载圆形图片,所以在网上查找了资料,很简单就解决了. 1 ...

  6. Android 微信分享视频缩略图不显示问题

    最近再分享视频的时候出现一个问题缩略图不显示有些显示,虽然以前也遇到过但是忘了.今天这里再写一下方便记录 百度了一下很多!!! 微信分享功能,安卓手机分享图片不显示 android微信分享缩略图不显示 ...

  7. Android 阿里云 视频直播安全下载踩坑实录

    最近项目里面集成了阿里云直播的内容,其中需要相应的下载视频的功能,遇到了一些问题,在这里贴出来跟大家分享一下, 我在项目中使用的是基于阿里云高级播放器3.4.8的sdk,具体怎么导入sdk ,请移步官 ...

  8. Kotlin拿Android本地视频缩略图

    本文主要讨论如下三个问题: 如何拿到本地视频? 怎么拿视频缩略图? 缩略图如何压缩? 1 如何拿到本地视频? 1.1 定义数据结构 先定义媒体信息数据结构MediaInfo,以及视频信息数据结构Vid ...

  9. Android获取视频缩略图

    一.通过本地url获取视频缩略图 /*** 通过本地url获取视频缩略图** @param url 文件路径* @param width 显示的宽度* @param height 显示的高度* @re ...

最新文章

  1. 使用Win32汇编开发一个dll并在C#中调用
  2. Qt双击桌面快捷方式激活并使程序窗口置于最前端
  3. Linux C目标文件
  4. 在Kubernetes上运行区块链服务(BaaS)
  5. 类型不匹配 java_java – 与泛型类型不匹配
  6. 最短路POJ 1062 昂贵的聘礼
  7. mysql客户端介绍
  8. 编程时,如何在vs中更换舒服的代码字体
  9. 电子电路仿真软件中文版_电路仿真软件详谈(六),Proteus电路仿真软件的超级应用...
  10. 软考中高项学员:2016年4月6日作业
  11. C++:wchar_t 和C++新增类型:char16_t char32_t
  12. 苏州旅游骗局黑幕经历
  13. java区间并集_区间并集求解算法实现
  14. raster包—resample函数
  15. 旅行青蛙(旅かえる)的最全攻略(不懂日语的可以看看)
  16. Photoshop技巧:[2]如何抠头发?
  17. 如何实现 AppStore App 的自动下载
  18. python标准数据类型叮叮叮
  19. [量化-033]金融哲学-道德经解读-004-道德经最好理解的部分
  20. Ubuntu 使用 ffmpeg 将 webm 转换为 mp4 (批量)

热门文章

  1. 如何在Github网页端处理不同分支之间的冲突
  2. SAP Spartacus central Travis build的lint环节
  3. SAP UI5 Input字段live change事件的一个例子
  4. SAP Spartacus的全局配置
  5. 使用ant执行Java代码
  6. 在SAP分析云里利用词云技术显示大段文本里的关键词
  7. Visual Studio Code里关于ESLint的错误消息
  8. SAP Hybris Commerce帮助文档的结构
  9. SAP Fiori s2 controller init
  10. SAP Cloud for Customer的跳转链接制作navigation link