视频尺寸适配

Posted by CoXier on February 16, 2019

一、问题背景

Android 设备的物理屏幕尺寸和播放的视频尺寸的比例关系不对等,例如下面这幅图,很明显是由于视频显示时被拉伸了,看上去十分别扭。不仅仅是 Android 设备,任何设备在播放视频时都存在尺寸拉伸等问题,本文从问题出发,提出在日常开发中经常用到的适配策略。

二、尺寸适配

上图的实验环境是:

  • Nexus5 1080x1920 (adb shell wm size)
  • Video 1080x608

为了方便描述,将视频的宽高比例记为 VideoAR,将手机的宽高比例记为 ViewAR,那么上图的 VideoAR 和 ViewAR 关系如下图:

屏幕快照 2019-02-16 下午12.18.49

显然视频的比例无法发生变化,所以只能改变 View 显示出来的比例关系,所以再引入一个 NewViewAR 表示经过适配之后的宽高比。那么视频的尺寸适配就等价于 ViewAR 和 NewViewAR 之间的关系了。常见的适配模式:

  • Fill:NewViewAR = ViewAR,此时新的 View 的宽高和之前 View 的宽高一样,未发生变化。
  • Fit: NewViewAR > ViewAR,按照视频的比例缩小显示,这种模式下看到的视频一般是左右或者上下留有黑边的。在上图这种情况下,会保持 View 的宽度不变,View 的高度会变小。
  • Crop: NewViewAR > ViewAR,按比例放大显示,因为放大之后超出屏幕外的无法显示,造成一种被裁剪的视觉效果。在上图这种情况下,会保持 View 的高度不变,View 的宽度会变大。

如下图:

屏幕快照 2019-02-16 下午1.52.00

核心代码如下:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (videoAspectRatio <= 0) {
            // Aspect ratio not set.
            return;
        }

        int width = getMeasuredWidth();
        int height = getMeasuredHeight();

        float viewAspectRatio = (float) width / height;
        switch (resizeMode) {
            case RESIZE_FIT:
                if (videoAspectRatio > viewAspectRatio) {
                    height = (int) (width / videoAspectRatio);
                } else {
                    width = (int) (height * videoAspectRatio);
                }
                break;
            case RESIZE_CROP:
                if (videoAspectRatio > viewAspectRatio) {
                    width = (int) (height * videoAspectRatio);
                } else {
                    height = (int) (width / videoAspectRatio);
                }
            default:
            case RESIZE_FILL:
                break;
        }

        super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));

    }

代码传送门

适配之后的效果图:

Fit:

Crop: