glide缩略图存储 android,Glide 显示视频缩略图及遇到的坑
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 显示视频缩略图及遇到的坑相关推荐
- ExoPlayer拖动进度条时显示视频缩略图
如果ImageView位置没有超出SeekBar的宽度,则计算坐标并移动, 如果它向外延伸,坐标将设置在ImageView的左侧或右侧. ※ExoPlayer使用ExoPlayer release-v ...
- uni-app 组件video显示视频缩略图
uni-app video 组件显示视频缩略图 HTML代码 JS代码 话不多少直接上代码,需要注意的是,视频默认自动播放,获取到视频时候立马暂停视频 HTML代码 <template>& ...
- android获取网络视频缩略图,Android 获取视频(本地和网络)缩略图的解决方案
在Android 开发视频的时候,通常都需要显示视频列表,而视频列表通常都有一张视频缩略图,那么它是怎么获取的呢, 关于网络视频的缩略图的实现方案主要有两种: 1.后台返回视频时顺便连缩略图的路径都返 ...
- android获取网络视频缩略图,Android 获取缩略图,网络视频,或者本地视频
IM软件发送视频文件,需要显示缩略图: import android.graphics.Bitmap; import android.media.MediaMetadataRetriever; imp ...
- android glide圆形图片,Android Glide加载图片成圆形
释放双眼,带上耳机,听听看~! 今天,简单讲解android使用glide加载图片成圆形. 这个很简单,因为之前需要在RecyclerView里加载圆形图片,所以在网上查找了资料,很简单就解决了. 1 ...
- Android 微信分享视频缩略图不显示问题
最近再分享视频的时候出现一个问题缩略图不显示有些显示,虽然以前也遇到过但是忘了.今天这里再写一下方便记录 百度了一下很多!!! 微信分享功能,安卓手机分享图片不显示 android微信分享缩略图不显示 ...
- Android 阿里云 视频直播安全下载踩坑实录
最近项目里面集成了阿里云直播的内容,其中需要相应的下载视频的功能,遇到了一些问题,在这里贴出来跟大家分享一下, 我在项目中使用的是基于阿里云高级播放器3.4.8的sdk,具体怎么导入sdk ,请移步官 ...
- Kotlin拿Android本地视频缩略图
本文主要讨论如下三个问题: 如何拿到本地视频? 怎么拿视频缩略图? 缩略图如何压缩? 1 如何拿到本地视频? 1.1 定义数据结构 先定义媒体信息数据结构MediaInfo,以及视频信息数据结构Vid ...
- Android获取视频缩略图
一.通过本地url获取视频缩略图 /*** 通过本地url获取视频缩略图** @param url 文件路径* @param width 显示的宽度* @param height 显示的高度* @re ...
最新文章
- 使用Win32汇编开发一个dll并在C#中调用
- Qt双击桌面快捷方式激活并使程序窗口置于最前端
- Linux C目标文件
- 在Kubernetes上运行区块链服务(BaaS)
- 类型不匹配 java_java – 与泛型类型不匹配
- 最短路POJ 1062 	昂贵的聘礼
- mysql客户端介绍
- 编程时,如何在vs中更换舒服的代码字体
- 电子电路仿真软件中文版_电路仿真软件详谈(六),Proteus电路仿真软件的超级应用...
- 软考中高项学员:2016年4月6日作业
- C++:wchar_t 和C++新增类型:char16_t char32_t
- 苏州旅游骗局黑幕经历
- java区间并集_区间并集求解算法实现
- raster包—resample函数
- 旅行青蛙(旅かえる)的最全攻略(不懂日语的可以看看)
- Photoshop技巧:[2]如何抠头发?
- 如何实现 AppStore App 的自动下载
- python标准数据类型叮叮叮
- [量化-033]金融哲学-道德经解读-004-道德经最好理解的部分
- Ubuntu 使用 ffmpeg 将 webm 转换为 mp4 (批量)