Android Coil3视频封面抽取封面帧存Disk缓存,Kotlin

Android Coil3视频封面抽取封面帧存Disk缓存,Kotlin

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /><uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@android:color/white"android:padding="1px"><ImageViewandroid:id="@+id/image"android:layout_width="match_parent"android:layout_height="200px"android:background="@android:color/darker_gray"android:scaleType="centerCrop" /></LinearLayout>

    implementation("io.coil-kt.coil3:coil:3.3.0")implementation("io.coil-kt.coil3:coil-core:3.3.0")

import android.content.ContentUris
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launchclass MainActivity : AppCompatActivity() {companion object {const val TAG = "fly/MainActivity"const val SPAN_COUNT = 4const val VIDEO = 1}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val rv = findViewById<RecyclerView>(R.id.rv)val layoutManager = GridLayoutManager(this, SPAN_COUNT)layoutManager.orientation = GridLayoutManager.VERTICALrv.layoutManager = layoutManagerval adapter = MyAdapter(this)rv.adapter = adapterrv.layoutManager = layoutManagerval ctx = thislifecycleScope.launch(Dispatchers.IO) {val videoList = readAllVideo(ctx)Log.d(TAG, "readAllVideo size=${videoList.size}")val lists = arrayListOf<MyData>()lists.addAll(videoList)lifecycleScope.launch(Dispatchers.Main) {adapter.dataChanged(lists)}}}private fun readAllVideo(ctx: Context): ArrayList<MyData> {val videos = ArrayList<MyData>()//读取视频Videoval cursor = ctx.contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,null,null,null,null)while (cursor!!.moveToNext()) {//路径val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))val id = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)val videoUri: Uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getLong(id))//名称//val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME))//大小//val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE))videos.add(MyData(videoUri, path, VIDEO))}cursor.close()return videos}
}


import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import coil3.memory.MemoryCache
import coil3.request.CachePolicy
import coil3.request.ErrorResult
import coil3.request.ImageRequest
import coil3.request.SuccessResult
import coil3.toBitmapclass MyAdapter : RecyclerView.Adapter<MyAdapter.VideoHolder> {companion object {const val TAG = "fly/MyAdapter"}private var mCtx: Context? = nullprivate var mItems = ArrayList<MyData>()constructor(ctx: Context) : super() {mCtx = ctx}fun dataChanged(items: ArrayList<MyData>) {this.mItems = itemsnotifyDataSetChanged()}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoHolder {val v = LayoutInflater.from(mCtx).inflate(R.layout.image_layout, null)return VideoHolder(v)}override fun onBindViewHolder(holder: VideoHolder, position: Int) {loadVideoCover(mItems[position], holder.image)}override fun getItemCount(): Int {return mItems.size}class VideoHolder : RecyclerView.ViewHolder {var image: ImageView? = nullconstructor(itemView: View) : super(itemView) {image = itemView.findViewById<ImageView>(R.id.image)}}private fun loadVideoCover(data: MyData, image: ImageView?) {val imageMemoryCacheKey = MemoryCache.Key(data.toString())val imageMemoryCache = MyCoilManager.Companion.INSTANCE.getImageLoader(mCtx!!).memoryCache?.get(imageMemoryCacheKey)if (imageMemoryCache != null) {Log.d(TAG, "命中内存缓存 $data")image?.setImageBitmap(imageMemoryCache.image.toBitmap())} else {//placeholderimage?.setImageResource(android.R.drawable.ic_menu_gallery)val imageReq = ImageRequest.Builder(mCtx!!).data(data).memoryCacheKey(imageMemoryCacheKey).memoryCachePolicy(CachePolicy.WRITE_ONLY).size(400).listener(object : ImageRequest.Listener {override fun onSuccess(request: ImageRequest, result: SuccessResult) {image?.setImageBitmap(result.image.toBitmap())}override fun onError(request: ImageRequest, result: ErrorResult) {Log.e(TAG, "onError ${request.data}")image?.setImageResource(android.R.drawable.stat_notify_error)}}).build()MyCoilManager.Companion.INSTANCE.getImageLoader(mCtx!!).enqueue(imageReq)}}
}


import android.app.Application
import coil3.ImageLoader
import coil3.PlatformContext
import coil3.SingletonImageLoaderclass MyApp : Application(), SingletonImageLoader.Factory {companion object {const val TAG = "fly/MyApp"}override fun newImageLoader(context: PlatformContext): ImageLoader {return MyCoilManager.Companion.INSTANCE.getImageLoader(this)}
}


import android.content.Context
import android.os.Environment
import android.util.Log
import coil3.ImageLoader
import coil3.disk.DiskCache
import coil3.disk.directory
import coil3.imageDecoderEnabled
import coil3.memory.MemoryCache
import coil3.request.CachePolicy
import java.io.Fileclass MyCoilManager {companion object {const val TAG = "fly/MyCoilManager"val INSTANCE by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { MyCoilManager() }}private var mImageLoader: ImageLoader? = nullprivate constructor() {Log.d(TAG, "constructor")}fun getImageLoader(ctx: Context): ImageLoader {if (mImageLoader != null) {return mImageLoader!!}Log.d(TAG, "初始化ImageLoader")//初始化加载器。mImageLoader = ImageLoader.Builder(ctx).imageDecoderEnabled(true).memoryCachePolicy(CachePolicy.ENABLED).memoryCache(initMemoryCache()).diskCachePolicy(CachePolicy.ENABLED).diskCache(initDiskCache()).components {add(MyVideoFetcher.Factory(ctx))}.build()return mImageLoader!!}private fun initMemoryCache(): MemoryCache {//内存缓存。val memoryCache = MemoryCache.Builder().maxSizeBytes(1024 * 1024 * 1024 * 2L) //2GB.build()return memoryCache}private fun initDiskCache(): DiskCache {//磁盘缓存。val diskCacheFolder = Environment.getExternalStorageDirectory()val diskCacheName = "fly_disk_cache"val cacheFolder = File(diskCacheFolder, diskCacheName)if (cacheFolder.exists()) {Log.d(TAG, "${cacheFolder.absolutePath} exists")} else {if (cacheFolder.mkdir()) {Log.d(TAG, "${cacheFolder.absolutePath} create OK")} else {Log.e(TAG, "${cacheFolder.absolutePath} create fail")}}val diskCache = DiskCache.Builder().maxSizeBytes(1024 * 1024 * 1024 * 2L) //2GB.directory(cacheFolder).build()Log.d(TAG, "cache folder = ${diskCache.directory.toFile().absolutePath}")return diskCache}
}

import android.net.Uri
import android.text.TextUtilsopen class MyData {var uri: Uri? = nullvar path: String? = nullvar lastModified = 0Lvar width = 0var height = 0var position = -1var type = -1  //-1未知。1,普通图。2,视频。constructor(uri: Uri?, path: String?, type: Int = -1) {this.uri = urithis.path = paththis.type = type}override fun equals(other: Any?): Boolean {return TextUtils.equals(this.toString(), other.toString())}override fun toString(): String {return "MyData(uri=$uri, path=$path, lastModified=$lastModified, width=$width, height=$height, position=$position, type=$type)"}
}

import android.content.Context
import android.graphics.Bitmap
import android.util.Log
import coil3.ImageLoader
import coil3.asImage
import coil3.decode.DataSource
import coil3.fetch.FetchResult
import coil3.fetch.Fetcher
import coil3.fetch.ImageFetchResult
import coil3.request.Optionsclass MyVideoFetcher(private val ctx: Context, private val item: MyData, private val options: Options) : Fetcher {companion object {const val TAG = "fly/MyVideoFetcher"}override suspend fun fetch(): FetchResult {var bitmap: Bitmap? = VideoUtil.readBmpDiskCache(MyCoilManager.INSTANCE.getImageLoader(ctx), item)if (bitmap == null) {val t1 = System.currentTimeMillis()bitmap = VideoUtil.getBmpBySysMMR(item)val t2 = System.currentTimeMillis()Log.d(TAG, "耗时 MMR: ${t2 - t1} ms")if (bitmap != null) {VideoUtil.writeBmpDiskCache(MyCoilManager.INSTANCE.getImageLoader(ctx), bitmap, item)}}return ImageFetchResult(bitmap?.asImage()!!,true,dataSource = DataSource.DISK)}class Factory(private val ctx: Context) : Fetcher.Factory<MyData> {override fun create(item: MyData,options: Options,imageLoader: ImageLoader,): Fetcher {return MyVideoFetcher(ctx, item, options)}}
}

import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.util.Log
import coil3.ImageLoader
import java.io.BufferedOutputStream
import java.io.FileOutputStreamobject VideoUtil {const val TAG = "fly/VideoUtil"fun readBmpDiskCache(il: ImageLoader?, item: MyData?): Bitmap? {var bitmap: Bitmap? = nullval snapShot = il?.diskCache?.openSnapshot(item.toString())if (snapShot != null) {Log.d(TAG, "命中Disk缓存 $item")val source = ImageDecoder.createSource(snapShot.data.toFile())try {bitmap = ImageDecoder.decodeBitmap(source)} catch (e: Exception) {Log.e(TAG, "读Disk缓存异常 $e $item")}}snapShot?.close()return bitmap}fun writeBmpDiskCache(il: ImageLoader?, bitmap: Bitmap?, item: MyData?): Any? {var bool = falseif (bitmap != null) {val editor = il?.diskCache?.openEditor(item.toString())var bos: BufferedOutputStream? = nulltry {bos = FileOutputStream(editor?.data?.toFile()).buffered(1024 * 32)bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)bos.flush()bos.close()editor?.commit()Log.d(TAG, "Bitmap写入Disk缓存 $item")bool = true} catch (e: Exception) {Log.e(TAG, "Bitmap写Disk磁盘异常 $e")} finally {try {bos?.close()} catch (e: Exception) {Log.e(TAG, "$e $item")}}}return bool}fun getBmpBySysMMR(item: MyData?): Bitmap? {var bitmap: Bitmap? = nullvar sysRetriever: android.media.MediaMetadataRetriever? = nulltry {sysRetriever = android.media.MediaMetadataRetriever()sysRetriever.setDataSource(item?.path)bitmap = sysRetriever.frameAtTime} catch (e: Exception) {Log.e(TAG, "${e.message} $item")} finally {try {sysRetriever?.release()sysRetriever?.close()} catch (e: Exception) {Log.e(TAG, "release ${e.message} $item")}}return bitmap}
}

Android MediaMetadataRetriever取视频封面,Kotlin(1)-CSDN博客文章浏览阅读801次,点赞17次,收藏11次。该Android项目实现了一个视频缩略图展示功能,主要包含以下内容:1)声明了读写存储权限;2)使用RecyclerView以9列网格布局展示视频;3)通过MediaMetadataRetriever获取视频首帧作为缩略图;4)采用协程处理耗时操作,避免阻塞主线程。项目包含MainActivity、MyAdapter和MyData三个核心类,分别负责UI初始化、数据适配和数据封装。遇到视频损坏或0字节文件时,会显示错误图标并记录日志。整体实现了高效读取设备视频并生成缩略图展示的功能。 https://blog.csdn.net/zhangphil/article/details/150023739Android快速视频解码抽帧FFmpegMediaMetadataRetriever,Kotlin(2)-CSDN博客文章浏览阅读294次。本文介绍了两种Android视频封面提取方案对比:1)原生MediaMetadataRetriever速度较慢;2)第三方FFmpegMediaMetadataRetriever(FFMMR)实现快速抽帧。详细说明了FFMMR的集成方法(添加依赖和权限),并提供了完整的Kotlin实现代码,包括视频列表读取、缓存管理、协程异步处理等核心功能。通过LruCache缓存缩略图提升性能,记录处理耗时和失败情况。相比前文介绍的原生方案,本文重点突出了FFMMR在解码效率和性能上的优势,为需要快速获取视频帧的场景提供 https://blog.csdn.net/zhangphil/article/details/150061648

Android Coli 3 ImageView load two suit Bitmap thumb and formal,Kotlin(七)-CSDN博客文章浏览阅读589次,点赞4次,收藏6次。本文在之前的基础上,进一步优化了Android应用中Coil 3.2.0版本加载缩略图和正式图的实现。主要改进点在于,当正式图加载完成后,主动删除缓存中的缩略图,以节省内存资源。文章提供了相关的Kotlin代码示例,并指出尽管配置了磁盘缓存路径,但实际运行时缓存文件为空,表明磁盘缓存未生效。作者建议将缩略图和正图的内存缓存合并为单一缓存系统,以提升性能。此外,文章还列出了所需的权限声明和Coil库的依赖项,包括对GIF、视频和SVG格式的支持。更多细节可参考CSDN博客链接。 https://blog.csdn.net/zhangphil/article/details/147983753

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/2755.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python网络爬虫(一) - 爬取静态网页

文章目录一、静态网页概述1. 静态网页介绍2. 静态网页爬取技术Requests介绍二、安装 Requests 库三、发送请求并获取响应1. 发送 GET 请求1.1 get() 方法介绍1.2 get() 方法签名介绍1.3 get() 方法参数介绍1.4 示例&#xff1a;发送get请求2. 发送 POST 请求2.1 post() 方法介绍…

企业级WEB应用服务器TOMCAT

企业级WEB应用服务器TOMCAT 一、WEB技术 1.1 HTTP协议和B/S结构 操作系统有进程子系统&#xff0c;使用多进程就可以充分利用硬件资源。进程中可以多个线程&#xff0c;每一个线程可以被CPU调度执行&#xff0c;这样就可以让程序并行的执行。这样一台主机就可以作为一个服务器为…

Linux 系统中,如何处理信号以避免竞态条件并确保程序稳定性?

在 Unix/Linux 系统中&#xff0c;处理信号时避免竞态条件&#xff08;Race Conditions&#xff09;并确保程序稳定性需要遵循关键原则和技巧。以下是核心方法&#xff1a; 1. 保持信号处理函数&#xff08;Signal Handler&#xff09;简单 仅设置标志位&#xff1a;在信号处理…

【Matplotlib】中文显示问题

中文显示问题本地Mac上作图&#xff0c;可以方便地实现中文字体显示。比如在Jupter中&#xff0c;通过&#xff1a;方法一&#xff1a;不下载字体库即可实现中文显示 (MAC)plt.rcParams[font.family][Arial Unicode MS]方法二&#xff1a;下载指定字体训即可实现中文显示plt.rc…

【Linux指南】Vim的全面解析与深度应用

引言 在Linux的命令行宇宙中&#xff0c;Vim如同一位全能的工匠&#xff0c;以独特的模式化操作和高度定制化能力&#xff0c;成为开发者与运维人员不可或缺的工具。从基础的文本编辑到复杂的代码开发&#xff0c;Vim通过灵活切换的多种模式&#xff0c;将每一个按键转化为高效…

Excel版经纬度和百分度互转v1.1

很多童鞋在工作中经常需要用到坐标&#xff0c;其中百分度和度分秒的转换常常需要依赖各种软件&#xff0c;但这些软件往往不如excel表格方便&#xff0c;因此本人开发了一个依据vba的度分秒和百分度转换表格&#xff0c;这个表格基于office2010开发&#xff0c;非常简单&#…

计算机网络:如何理解目的网络不再是一个完整的分类网络

这一理解主要源于无分类域间路由&#xff08;CIDR&#xff09;技术的广泛应用&#xff0c;它打破了传统的基于类的IP地址分配方式。具体可从以下方面理解&#xff1a; 传统分类网络的局限性&#xff1a;在早期互联网中&#xff0c;IP地址被分为A、B、C等固定类别&#xff0c;每…

FFmpeg实现音视频转码

以下是基于 FFmpeg 库实现 MP4 转码的详细步骤&#xff08;以 C 语言为例&#xff09;&#xff1a; 一、环境准备 集成 FFmpeg 库 编译 FFmpeg 生成动态库&#xff08;avformat、avcodec、avutil、swscale、swresample等&#xff09; 在 SDK 项目中配置头文件路径和库文件链接…

09 【C++ 初阶】C/C++内存管理

文章目录前言1. C/C内存分布2. C语言中动态内存管理方式malloc&#xff1a;calloc&#xff1a;realloc&#xff1a;free&#xff1a;3. C内存管理方式3.1 定位new表达式(placement-new)3.2 new/delete3.2.1 new和delete操作自定义类型3.2.2 new和delete操作内置类型3.2.3 new和…

Datawhale+AI夏令营_让AI读懂财报PDF task2深入赛题笔记

1.深入理解baseline方案 1.1 赛题任务 项目背景 本次赛题的核心目标是打造一个能看懂图片、读懂文字、并将两者关联起来思考的AI助手&#xff0c;构建一个先进的智能问答系统&#xff0c;以应对真实世界中复杂的、图文混排的信息环境。 (1)让AI模型能够阅读并理解包含大量图标、…

OpenAI开源大模型 GPT-OSS 开放权重语言模型解析:技术特性、部署应用及产业影响

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》&#xff08;跟我一起学人工智能&#xff09;【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷…

前端懒加载技术全面解析

懒加载(Lazy Loading)是一种优化前端性能的重要技术,核心思想是延迟加载非关键资源,只在需要时加载它们。 一、懒加载的基本原理 懒加载的核心思想是通过以下方式优化性能: 减少初始加载实践: 只加载首屏所需资源 节省带宽和内存: 避免加载用户可能不会查看的内容 提高…