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

如何在ExoPlayer的PlayerView中获取9:16视频帧的四角坐标

获取Android PlayerView中9:16视频帧的四角坐标

要定位文本覆盖层,核心是计算视频帧在PlayerView容器内的实际显示区域——因为PlayerView会自动按比例缩放视频并居中显示,所以需要结合视频原始比例和PlayerView的尺寸来推导坐标。

PlayerView中的9:16视频帧(红色区域)

步骤1:获取视频原始宽高

首先要在视频加载完成后拿到视频的真实宽高,以ExoPlayer为例,通过Player.Listener监听视频尺寸变化:

player.addListener(object : Player.Listener {
    override fun onVideoSizeChanged(videoSize: VideoSize) {
        super.onVideoSizeChanged(videoSize)
        val videoWidth = videoSize.width
        val videoHeight = videoSize.height
        // 校验是否为9:16竖屏视频(允许微小误差)
        val is916Portrait = Math.abs((videoHeight.toFloat() / videoWidth) - 16f/9f) < 0.01f
        if (is916Portrait) {
            calculateVideoFrame(playerView, videoWidth, videoHeight)
        }
    }
})

步骤2:计算视频帧的显示区域与四角坐标

PlayerView的尺寸需要在布局完成后才能准确获取,所以用ViewTreeObserver监听布局完成事件,再推导坐标:

private fun calculateVideoFrame(playerView: PlayerView, videoWidth: Int, videoHeight: Int) {
    playerView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            // 移除监听避免重复触发
            playerView.viewTreeObserver.removeOnGlobalLayoutListener(this)
            
            val containerWidth = playerView.width
            val containerHeight = playerView.height
            
            // 计算缩放比例:取容器与视频宽高比的较小值,保证视频完整显示
            val scaleX = containerWidth.toFloat() / videoWidth
            val scaleY = containerHeight.toFloat() / videoHeight
            val scale = min(scaleX, scaleY)
            
            // 视频实际显示的宽高
            val displayedWidth = videoWidth * scale
            val displayedHeight = videoHeight * scale
            
            // 居中显示时的偏移量(左右/上下边距)
            val offsetX = (containerWidth - displayedWidth) / 2f
            val offsetY = (containerHeight - displayedHeight) / 2f
            
            // 计算四个角的局部坐标(以PlayerView左上角为原点)
            val topLeft = Pair(offsetX, offsetY)
            val topRight = Pair(offsetX + displayedWidth, offsetY)
            val bottomLeft = Pair(offsetX, offsetY + displayedHeight)
            val bottomRight = Pair(offsetX + displayedWidth, offsetY + displayedHeight)
            
            // 这里可以将坐标用于文本覆盖层的定位
            // 如需屏幕绝对坐标,可通过playerView.getLocationOnScreen()转换
        }
    })
}

额外注意事项

  • 若PlayerView布局变化(如屏幕旋转、窗口调整),需要重新计算坐标,可以在onConfigurationChanged或View.OnLayoutChangeListener中重复执行计算逻辑。
  • 如果使用了非默认缩放模式(如resize_mode="fill"),缩放比例要取max(scaleX, scaleY),此时视频会被裁剪,需根据需求调整坐标计算。
  • 如需将局部坐标转为屏幕绝对坐标,可使用以下代码:
val containerLocation = IntArray(2)
playerView.getLocationOnScreen(containerLocation)
val screenTopLeftX = containerLocation[0] + topLeft.first
val screenTopLeftY = containerLocation[1] + topLeft.second

内容的提问来源于stack exchange,提问作者nervian tone

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 18:42:36