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

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

相关文章:

  • 大模型开发工具的汇总
  • SQL Server从入门到项目实践(超值版)读书笔记 23
  • cursor, vscode黄色波浪线警告问题
  • 从零到精通:嵌入式BLE开发实战指南
  • 计算机网络:(十四)传输层(下)详细讲解TCP报文段的首部格式,TCP 可靠传输的实现与TCP 的流量控制
  • Mybatis和MybatisPlus的对比区分理解法
  • 基于 RabbitMQ 死信队列+TTL 实现延迟消息+延迟插件基本使用
  • 给AI装上“翻译聚光灯”:注意力机制的机器翻译革命
  • Docker 镜像常见标签(如 `标准`、`slim`、`alpine` 和 `noble`)详细对比
  • 编程基础之字符串——统计数字字符个数
  • TypeScript 中的as const是什么?
  • React:useEffect 与副作用
  • token危机解决?扩散模型数据潜力3倍于自回归,重训480次性能仍攀升
  • 浏览器CEFSharp88+X86+win7 之多页面展示(四)
  • LLaMA-Adapter Efficient Fine-tuning of Language Models with Zero-init Attention
  • Redis - 使用 Redis HyperLogLog 进行高效基数统计
  • Spring Boot与WebSocket构建物联网实时通信系统
  • 基于Spring Boot和WebSocket的实时聊天系统
  • go语言运算符
  • 遇到前端导出 Excel 文件出现乱码或文件损坏的问题
  • Linux 管道命令及相关命令练习与 Shell 编程、Tomcat 安装
  • 基于Ubuntu20.04的环境,编译QT5.15.17源码
  • Lua语言元表、协同程序
  • JavaWeb(苍穹外卖)--学习笔记17(Apache Echarts)
  • LightGBM 与 GBDT 在机器学习中的性能与特点比较
  • Graph-R1:一种用于结构化多轮推理的智能图谱检索框架,并结合端到端强化学习
  • 【最后203篇系列】031 构建MCP尝试
  • Docker Compose 部署高可用 MongoDB 副本集集群(含 Keepalived + HAProxy 负载均衡)
  • 从零学习three.js官方文档(二)——图元
  • 去除Edge微软浏览器与Chrome谷歌浏览器顶部出现“此版本的Windows不再支持升级Windows 10”的烦人提示