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

Android快速视频解码抽帧FFmpegMediaMetadataRetriever,Kotlin(2)

Android快速视频解码抽帧FFmpegMediaMetadataRetriever,Kotlin(2)

Android MediaMetadataRetriever取视频封面,Kotlin(1)-CSDN博客 使用Android原生的MediaMetadataRetriever耗时太长,第三方的 https://github.com/wseemann/FFmpegMediaMetadataRetriever 可实现快速视频抽帧解码。

添加依赖:

implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.19'
implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native:1.0.19'

    <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="180px"android:background="@android:color/darker_gray"android:scaleType="centerCrop" /></LinearLayout>

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"const val SPAN_COUNT = 8const 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.graphics.Bitmap
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.collection.LruCache
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import wseemann.media.FFmpegMediaMetadataRetrieverclass MyAdapter : RecyclerView.Adapter<MyAdapter.VideoHolder> {private var mCtx: Context? = nullprivate var mItems = ArrayList<MyData>()private var mFailItemCount = 0private var mSuccessItemCount = 0private var mTotalCostTime = 0Lprivate val mCache = LruCache<String, Bitmap?>(1000)private var mIsDecoderCompleted = falseconstructor(ctx: Context) : super() {mCtx = ctx}fun dataChanged(items: ArrayList<MyData>) {this.mItems = itemsmSuccessItemCount = 0mTotalCostTime = 0mFailItemCount = 0notifyDataSetChanged()(mCtx as AppCompatActivity).lifecycleScope.launch(Dispatchers.IO) {mIsDecoderCompleted = falsemItems.forEachIndexed { idx, data ->var bmp: Bitmap? = mCache[data.toString()]if (bmp == null) {bmp = getFFMMRBmp(data)if (bmp != null) {mCache.put(data.toString(), bmp)}(mCtx as AppCompatActivity).lifecycleScope.launch(Dispatchers.Main) {notifyItemChanged(idx)}}}mIsDecoderCompleted = truewithContext(Dispatchers.Main) {notifyDataSetChanged()}}}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoHolder {val v = LayoutInflater.from(mCtx).inflate(R.layout.image_layout, null, false)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)image?.setImageResource(android.R.drawable.ic_menu_gallery)}}private fun getFFMMRBmp(data: MyData): Bitmap? {val ffMMR = FFmpegMediaMetadataRetriever()var bmp: Bitmap? = nullval t = System.currentTimeMillis()try {ffMMR.setDataSource(data.path)bmp = ffMMR.frameAtTimemSuccessItemCount++Log.d(MainActivity.TAG,"FFmpeg MMR:total success item count=$mSuccessItemCount")} catch (e: Exception) {Log.e(MainActivity.TAG, "FFmpeg MMR: total fail item count=${mFailItemCount++} , ${e.message}:$data")} finally {try {ffMMR.release()} catch (exc: Exception) {Log.e(MainActivity.TAG, "$exc")}}val costTime = System.currentTimeMillis() - tmTotalCostTime = mTotalCostTime + costTimeLog.d(MainActivity.TAG, "FFmpeg MMR:total cost time= $mTotalCostTime ms")return bmp}private fun loadVideoCover(data: MyData, image: ImageView?) {val bmp: Bitmap? = mCache[data.toString()]if (bmp != null) {image?.setImageBitmap(bmp)} else {if (mIsDecoderCompleted) {image?.setImageResource(android.R.drawable.stat_notify_error)}}}
}

import android.net.Uriopen 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 toString(): String {return "MyData(uri=$uri, path=$path, lastModified=$lastModified, width=$width, height=$height, position=$position, type=$type)"}override fun equals(other: Any?): Boolean {return (this.toString()) == other.toString()}
}

Android MediaMetadataRetriever取视频封面,Kotlin(1)-CSDN博客文章浏览阅读713次,点赞17次,收藏11次。该Android项目实现了一个视频缩略图展示功能,主要包含以下内容:1)声明了读写存储权限;2)使用RecyclerView以9列网格布局展示视频;3)通过MediaMetadataRetriever获取视频首帧作为缩略图;4)采用协程处理耗时操作,避免阻塞主线程。项目包含MainActivity、MyAdapter和MyData三个核心类,分别负责UI初始化、数据适配和数据封装。遇到视频损坏或0字节文件时,会显示错误图标并记录日志。整体实现了高效读取设备视频并生成缩略图展示的功能。 https://blog.csdn.net/zhangphil/article/details/150023739

http://www.lryc.cn/news/615779.html

相关文章:

  • 【软考中级网络工程师】知识点之 DCC 深度剖析
  • 【21】OpenCV C++实战篇——OpenCV C++案例实战二十七《角度测量》
  • Perplexity 为特朗普 Truth Social 提供技术支持
  • 如何培养自己工程化的能力(python项目)
  • Pytorch深度学习框架实战教程12:Pytorch混合精度推理,性能加速147%的技术实现
  • 若依前后端分离版学习笔记(八)——事务简介与使用
  • Apache Pulsar性能与可用性优化实践指南
  • NLP---IF-IDF案例分析
  • C++高频知识点(十九)
  • 【面试场景题】异地多活改造方案
  • 【Matplotlib】中文显示问题
  • 【论文阅读】Deep Adversarial Multi-view Clustering Network
  • Docker 镜像常见标签(如 `标准`、`slim`、`alpine` 和 `noble`)详细对比
  • 随想记——excel报表
  • Linux下的软件编程——标准IO
  • 编程基础之多维数组——矩阵交换行
  • `sk_buff` 结构体详解(包含全生命周期解析)
  • 如何回收内存对象,有哪些回收算法?
  • XML 指南
  • LeetCode_字符串
  • Jenkins | 账号及权限管理
  • Pytorch深度学习框架实战教程-番外篇02-Pytorch池化层概念定义、工作原理和作用
  • 怎么能更好的降低论文AI率呢?
  • 分布微服务电商订单系统Rust编码开发[下]
  • SpringBoot学习日记(三)
  • 【C++/STL】list模拟实现和迭代器失效问题
  • 基于 RabbitMQ 死信队列+TTL 实现延迟消息+延迟插件基本使用
  • 十、Linux Shell脚本:流程控制语句
  • [Julia] LinearAlgebra.jl 自带包
  • LeetCode 刷题【37. 解数独】