首页 > 解决方案 > 使用并行协程工作程序运行 MediaMetadataRetriever getFrameAtIndex() 比使用一个协程运行慢

问题描述

我正在尝试使用MediaMetadataRetriever多个并行协程作业从视频文件中抓取帧,但与使用一个协程运行它相比,该过程需要更多时间。
我不明白问题出在哪里,可能MediaMetadataRetriever不适用于并行执行并且它会阻止getFrameAtIndex同一文件的方法调用?或者问题出在并行文件访问中?
我的目标是尽快从约 1 秒的视频文件中检索所有位图,这就是我决定并行抓取帧的原因。

这是具有并行和一个协程执行的时间日志。

与一个协程

LOG_TAG: frame range 0..51  calculation for job_1 = 2206 ms
LOG_TAG_FINISHED: total grab time 2224

带有 4 个并行协程

LOG_TAG: frame range 39..51  calculation for job_4 = 4484 ms
LOG_TAG: frame range 0..12   calculation for job_1 = 4524 ms
LOG_TAG: frame range 27..39  calculation for job_3 = 4634 ms
LOG_TAG: frame range 12..27  calculation for job_2 = 4716 ms
LOG_TAG_FINISHED: total grab time 4747

和代码

suspend fun startGrabbingFrames(src: String){

    val dataRetriever = MediaMetadataRetriever()
    dataRetriever.setDataSource(src)
    val totalFrames =
        dataRetriever.extractMetadata(METADATA_KEY_VIDEO_FRAME_COUNT)?.toLongOrNull() ?: 0L
    dataRetriever.release()

    val frameChunkSize = totalFrames / 4

    val offset1 = totalFrames - frameChunkSize * 2
    val offset2 = totalFrames - frameChunkSize

    val range1 = IntRange(0, frameChunkSize.toInt())
    val range2 = IntRange(frameChunkSize.toInt(), offset1.toInt())
    val range3 = IntRange(offset1.toInt(), offset2.toInt())
    val range4 = IntRange(offset2.toInt(), totalFrames.toInt()-1)
    val time = measureTimeMillis {
        val one = CoroutineScope(Dispatchers.Default).async {  grabFrames(src,range1,"job_1") }
        val two = CoroutineScope(Dispatchers.Default).async { grabFrames(src, range2,"job_2") }
        val three = CoroutineScope(Dispatchers.Default).async { grabFrames(src, range3,"job_3") }
        val four = CoroutineScope(Dispatchers.Default).async { grabFrames(src, range4,"job_4") }
        val frames1 = one.await()
        val frames2 = two.await()
        val frames3 = three.await()
        val frames4 = four.await()
    }
    println("LOG_TAG_FINISHED: total grab time $time")
}

这是grabFrames方法

private suspend fun grabFrames(src: String, range: IntRange, tag: String) = withContext(Dispatchers.Default) {
    val retriever = MediaMetadataRetriever()
    retriever.setDataSource(src)
    val frames = ArrayList<Bitmap?>()
    measureTimeMillis {
        for (i in range) {
            val frame = retriever.getFrameAtIndex(i)
            frames.add(frame)
        }
        retriever.release()
    }.also {
        Log.d("LOG_TAG", "frame range $range  calculation for $tag = ${it} ms")
    }
    frames
}

如果我只用一个协程运行它,它几乎快 2 倍,如日志所示。

val one = CoroutineScope(Dispatchers.Default).async {  grabFrames(src,IntRange(0,totalFrames-1),"job_1") }
val fullFrames = one.await()

还尝试制作该文件的 4 个副本并MediaMetadataRetriever为每个文件创建 4 个对象并运行 4 个并行作业,但结果是相同的。

标签: androidkotlinparallel-processingkotlin-coroutines

解决方案


推荐阅读