You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android:如何在视频播放显示时实时获取其帧?

实时获取本地MP4视频播放帧的解决方案

需求背景

需要实现类似ProcessCameraProvider搭配ImageAnalyzer实时获取相机帧的功能——针对本地图库中的MP4视频,在播放过程中实时获取帧并转为Bitmap进行处理。现有MediaMetadataRetriever或MediaExtractor无法满足实时播放时逐帧获取的需求,需可行方案。

可行方案:基于ExoPlayer的视频帧实时监听

ExoPlayer是Android官方推荐的多媒体播放库,提供了VideoFrameMetadataListener接口,可在视频播放过程中实时回调每一帧的元数据与帧数据,完全匹配实时获取并处理帧的需求。

1. 添加ExoPlayer依赖

在Module级别的build.gradle中引入核心库与UI库:

implementation "com.google.android.exoplayer:exoplayer-core:2.19.1"
implementation "com.google.android.exoplayer:exoplayer-ui:2.19.1"

2. 初始化播放器并设置帧监听

以下是Kotlin实现示例,包含实时帧获取、转Bitmap及处理逻辑:

import android.graphics.Bitmap
import android.graphics.Surface
import android.graphics.SurfaceTexture
import android.os.Bundle
import android.view.TextureView
import androidx.appcompat.app.AppCompatActivity
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.video.VideoFrameMetadataListener

class VideoFrameCaptureActivity : AppCompatActivity(), TextureView.SurfaceTextureListener {
    private lateinit var exoPlayer: ExoPlayer
    private lateinit var textureView: TextureView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_video_frame_capture)
        textureView = findViewById(R.id.texture_view)
        textureView.surfaceTextureListener = this
    }

    override fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture, width: Int, height: Int) {
        // 初始化ExoPlayer
        exoPlayer = ExoPlayer.Builder(this).build()
        exoPlayer.setVideoSurface(Surface(surfaceTexture))

        // 注册视频帧监听,实时回调每一帧
        exoPlayer.addVideoFrameMetadataListener { _, _, _, surfaceTexture ->
            surfaceTexture?.let { st ->
                st.updateTexImage()
                // 将SurfaceTexture中的帧转为Bitmap
                val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
                val canvas = android.graphics.Canvas(bitmap)
                canvas.drawBitmap(bitmap, 0f, 0f, android.graphics.Paint().apply { isAntiAlias = true })
                
                // 这里执行你的帧处理逻辑,比如图像识别、滤镜等
                processCapturedFrame(bitmap)
                
                // 及时回收Bitmap,避免内存泄漏
                bitmap.recycle()
            }
        }

        // 设置要播放的本地MP4 Uri(替换为你的视频路径)
        val mediaItem = MediaItem.fromUri("content://media/external/video/media/123")
        exoPlayer.setMediaItem(mediaItem)
        exoPlayer.prepare()
        exoPlayer.play()
    }

    private fun processCapturedFrame(bitmap: Bitmap) {
        // 自定义帧处理逻辑
    }

    override fun onSurfaceTextureSizeChanged(surfaceTexture: SurfaceTexture, width: Int, height: Int) {}
    override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture): Boolean {
        exoPlayer.release()
        return true
    }
    override fun onSurfaceTextureUpdated(surfaceTexture: SurfaceTexture) {}
}

关键说明

  • 无画面播放场景:若不需要显示视频画面,可创建一个不可见的Surface替代TextureView,仅用于接收帧数据。
  • 线程注意事项:帧回调运行在ExoPlayer内部线程,处理耗时操作时需切换到后台线程,避免阻塞播放流程。
  • 与现有方案对比:MediaMetadataRetriever适合获取指定时间点单帧,MediaExtractor适合离线批量提取帧,但均无法实现播放过程中实时逐帧回调,ExoPlayer的监听正好解决了这个痛点。

内容的提问来源于stack exchange,提问作者leonel Z

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 03:33:18