当前位置: 首页 > ai >正文

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.xdnf.cn/news/17476.html

相关文章:

  • 自然语言处理实战:用LSTM打造武侠小说生成器
  • GraalVM !拥抱云原生的 JVM
  • Python 的浅拷贝 vs 深拷贝(含嵌套可变对象示例与踩坑场景)
  • 人工智能正在学习自我提升的方式
  • TF-IDF提取关键词(附实战案例)
  • 商业解决方案技术栈总结
  • CVPR医学图像三套创新方案:通用分割+3D高效解码+SSM肿瘤定位(附链接)
  • 算法训练营day44 动态规划⑪ 1143.最长公共子序列、1035.不相交的线、53. 最大子序和、392.判断子序列
  • 【Redis】持久化方案——RDB和AOF
  • Vue3从入门到精通: 2.5 Vue3组件库开发与设计系统构建
  • 海关 瑞数 失信企业 逆向 分析 后缀 rs
  • Java高并发场景下的缓存穿透问题定位与解决方案
  • MySQL的存储引擎:
  • Java中new的相关知识
  • TDengine IDMP 快速体验(方式二 通过 docker)
  • 系统测试讲解 - Java使用selenium实现滑块验证的处理详解
  • Qt 框架全面解析:从基础到应用
  • 亚麻云之静态资源管家——S3存储服务实战
  • 在Word和WPS文字一页中实现一栏与多栏混排
  • 瑞芯微 RK3588 平台驱动开发 学习计划
  • OpenEnler等Linux系统中安装git工具的方法
  • 机器学习-----K-means算法介绍
  • 数据类型全解析
  • PostgreSQL因为A/B switch以及group表过多导致WAL full的情况讨论
  • 冒泡排序实现以及优化
  • django基于Python的设计师作品平台的数据可视化系统设计与实现
  • 数字图像处理2——图像增强
  • Java设计模式之开闭原则介绍与说明
  • TypeScript中的type和interface的区别是什么?
  • 红楼梦文本数据分析