Android MediaMetadataRetriever取视频封面,Kotlin(1)

Android MediaMetadataRetriever取视频封面,Kotlin(1)

XML 复制代码
    <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 复制代码
<?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">

    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="180px"
        android:background="@android:color/darker_gray"
        android:scaleType="centerCrop" />

</LinearLayout>
Kotlin 复制代码
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.launch


class MainActivity : AppCompatActivity() {
    companion object {
        const val TAG = "fly"

        const val SPAN_COUNT = 9
        const 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.VERTICAL
        rv.layoutManager = layoutManager

        val adapter = MyAdapter(this)

        rv.adapter = adapter
        rv.layoutManager = layoutManager

        val ctx = this
        lifecycleScope.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>()

        //读取视频Video
        val 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
    }
}
Kotlin 复制代码
import android.content.Context
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
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


class MyAdapter : RecyclerView.Adapter<MyAdapter.VideoHolder> {
    private var mCtx: Context? = null
    private var mItems = ArrayList<MyData>()

    private var mFailItemCount = 0
    private var mSuccessItemCount = 0
    private var mTotalCostTime = 0L
    private val mCache = LruCache<String, Bitmap?>(1000)
    private var mIsDecoderCompleted = false

    constructor(ctx: Context) : super() {
        mCtx = ctx
    }

    fun dataChanged(items: ArrayList<MyData>) {
        this.mItems = items

        mSuccessItemCount = 0
        mTotalCostTime = 0
        mFailItemCount = 0

        notifyDataSetChanged()

        (mCtx as AppCompatActivity).lifecycleScope.launch(Dispatchers.IO) {
            mIsDecoderCompleted = false

            mItems.forEachIndexed { idx, data ->
                var bmp: Bitmap? = mCache[data.toString()]
                if (bmp == null) {
                    bmp = getSysMMRBmp(data)

                    if (bmp != null) {
                        mCache.put(data.toString(), bmp)
                    }

                    (mCtx as AppCompatActivity).lifecycleScope.launch(Dispatchers.Main) {
                        notifyItemChanged(idx)
                    }
                }
            }

            mIsDecoderCompleted = true
            withContext(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? = null

        constructor(itemView: View) : super(itemView) {
            image = itemView.findViewById<ImageView>(R.id.image)
            image?.setImageResource(android.R.drawable.ic_menu_gallery)
        }
    }

    private fun getSysMMRBmp(data: MyData): Bitmap? {
        val sysMMR = MediaMetadataRetriever()

        var bmp: Bitmap? = null
        val t = System.currentTimeMillis()
        try {
            sysMMR.setDataSource(data.path)
            bmp = sysMMR.frameAtTime

            mSuccessItemCount++

            Log.d(
                MainActivity.TAG,
                "android MMR:total success item count=$mSuccessItemCount"
            )
        } catch (e: Exception) {
            Log.e(MainActivity.TAG, "android MMR: total fail item count=${mFailItemCount++} , ${e.message}:$data")
        } finally {
            try {
                sysMMR.release()
                sysMMR.close()
            } catch (exc: Exception) {
                Log.e(MainActivity.TAG, "$exc")
            }
        }

        val costTime = System.currentTimeMillis() - t
        mTotalCostTime = mTotalCostTime + costTime
        Log.d(MainActivity.TAG, "android 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)
            }
        }
    }
}
Kotlin 复制代码
import android.net.Uri

open class MyData {
    var uri: Uri? = null
    var path: String? = null
    var lastModified = 0L
    var width = 0
    var height = 0

    var position = -1
    var type = -1  //-1未知。1,普通图。2,视频。

    constructor(uri: Uri?, path: String?, type: Int = -1) {
        this.uri = uri
        this.path = path
        this.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()
    }
}
复制代码
MediaMetadataRetriever抽帧的耗时太长,对于异常或者超规格的视频,基本上10+秒。通常5、6秒。

500个超规格、残破视频,解码耗时和成功率情况:

android MMR: total fail item count=110

android MMR:total success item count=389

android MMR:total cost time= 198679 ms

3分钟+,500个视频才解码完,成功解码389个,解码失败110个。(0开始计数)

Android MediaMetadataRetriever获取视频宽高,Java_retriever.extractmetadata-CSDN博客文章浏览阅读1k次,点赞3次,收藏5次。文章浏览阅读914次。【Android设置头像,手机拍照或从本地相册选取图片作为头像】像微信、QQ、微博等社交类的APP,通常都有设置头像的功能,设置头像通常有两种方式:1,让用户通过选择本地相册之类的图片库中已有的图像,裁剪后作为头像。文章浏览阅读124次。【Android设置头像,手机拍照或从本地相册选取图片作为头像】像微信、QQ、微博等社交类的APP,通常都有设置头像的功能,设置头像通常有两种方式:1,让用户通过选择本地相册之类的图片库中已有的图像,裁剪后作为头像。_retriever.extractmetadatahttps://blog.csdn.net/zhangphil/article/details/139521977Android MediaMetadataRetriever setDataSource failed: status = 0xFFFFFFEA-CSDN博客文章浏览阅读1.6k次。文章讲述了在Android应用中使用Glide加载视频时,遇到MediaMetadataRetriever的setDataSource抛出异常的情况,原因可能是视频文件损坏或大小为0。作者介绍了如何自定义AppGlideModule来处理这种错误,例如通过添加模型筛选和容错机制来改进加载逻辑。https://blog.csdn.net/zhangphil/article/details/133890245

相关推荐
云存储小天使2 小时前
安卓蛙、苹果蛙为什么难互通?
android
陈大头铃儿响叮当4 小时前
Android Studio升级后,Flutter运行android设备报错
android·flutter·android studio
勤劳打代码4 小时前
isar_flutter_libs 引发 Namespace not specified
android·flutter·groovy
奔跑吧 android6 小时前
【android bluetooth 协议分析 18】【PBAP详解 2】【车机为何不显示电话号码为空的联系人信息】
android·蓝牙电话·hfp·pbap·电话簿
深盾科技6 小时前
安卓二次打包技术深度拆解:从逆向篡改到防护逻辑
android
4Forsee6 小时前
【Android】消息机制
android·java·前端
2501_915921437 小时前
iOS 虚拟位置设置实战,多工具协同打造精准调试与场景模拟环境
android·ios·小程序·https·uni-app·iphone·webview
龚礼鹏7 小时前
Android 图像显示框架三——演示demo以及解析
android·交互
QuantumLeap丶8 小时前
《Flutter全栈开发实战指南:从零到高级》- 11 -状态管理Provider
android·flutter·ios
百锦再8 小时前
第6章 结构体与方法
android·java·c++·python·rust·go