视频播放比例问题研究

背景:

针对视频满屏播放,有时候如果仅仅是设置某个控件宽高都是match_parent, 但是视频源却没有和设备的比例保持一致,就会存在画面被拉伸的情况,那么应该怎么避免呢?

解决方法:

第一步:确定视频源的比例:根据帧宽高算出比例大致是9:5

第二步:确定设备的宽高比例,我们设备屏幕是10寸,宽高是:1920*1200,比例是8:5

第三步:分析,从上面两个比值来看,视频源比例和设备比例不一致,在高度相同的情况下,视频源要宽一点,设备要窄一点。如果设置视频控件的宽高是match_parent, 在源和设备高度一致的情况下,会将视频做横向压缩。

第四步:计算,两种方案(a)控件宽度固定,高度适当做比例压缩:9/5 = 1920/h ===> h = 1920/( 9/5),高度从1200压缩至1065。(b)如果是控件高度固定,宽度做适当调整:9/5 = w/1200=====>w = 1200*(9/5),宽度从1920拉伸到2160。这个已经超出设备屏幕,所以采用方案a

实际需求中,或许也会要求其他缩放模式,那么可以借鉴exoplayer中的源码,做出适合项目需求的缩放比例。

分析 google 中exoplayer是如何计算视频播放比例:

exoplayer 中提供了如下几种尺寸适配方式:

  /**
   * Resize modes for {@link AspectRatioFrameLayout}.
   */
  @Retention(RetentionPolicy.SOURCE)
  @IntDef({RESIZE_MODE_FIT, RESIZE_MODE_FIXED_WIDTH, RESIZE_MODE_FIXED_HEIGHT, RESIZE_MODE_FILL,
      RESIZE_MODE_ZOOM})
  public @interface ResizeMode {}

/**
   * Either the width or height is decreased to obtain the desired aspect ratio.
   * 压缩宽或是高来适配所需的宽高比
   */
public static final int RESIZE_MODE_FIT = 0;


/**
   * The width is fixed and the height is increased or decreased to obtain the desired aspect ratio.
   * 宽是固定的,拉伸或是压缩高度来适配所需的宽高比
   */
public static final int RESIZE_MODE_FIXED_WIDTH = 1;

/**
   * The height is fixed and the width is increased or decreased to obtain the desired aspect ratio.
   * 高度是固定的,拉伸或是压缩宽度来适配所需的宽高比
   */
  public static final int RESIZE_MODE_FIXED_HEIGHT = 2;

/**
   * The specified aspect ratio is ignored.
   * 忽视指定的宽高比
   */
  public static final int RESIZE_MODE_FILL = 3;

 /**
   * Either the width or height is increased to obtain the desired aspect ratio.
   * 拉伸宽或是高来适配所需的宽高比
   */
  public static final int RESIZE_MODE_ZOOM = 4;

从源码中可以看到:当设置了ResizeMode之后,会调用onMesasure来重新计算控件的宽高:
查看onMeasure方法:

@Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //RESIZE_MODE_FILL 模式是忽略设置的宽高比,所以直接return,不用去测量
    if (resizeMode == RESIZE_MODE_FILL || videoAspectRatio <= 0) {
      // Aspect ratio not set.
      return;
    }

    int width = getMeasuredWidth();//获取控件的实际宽
    int height = getMeasuredHeight();//获取控件的实际高
    float viewAspectRatio = (float) width / height; //控件的宽高比

    /**
    *如果控件的宽高比和视频的宽高比在允许的范文内,也不用再次去测量
    */
    float aspectDeformation = videoAspectRatio / viewAspectRatio - 1;
    if (Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION) {
      // We're within the allowed tolerance.
      return;
    }

    switch (resizeMode) {
      case RESIZE_MODE_FIXED_WIDTH://宽度是固定的,高度要重新计算
      /**
      *比如说 视频的宽高比例是9/5,宽度是固定的,则高度 = width * (5/9) 也即 width / (9/5)
      */
        height = (int) (width / videoAspectRatio);
        break;
      case RESIZE_MODE_FIXED_HEIGHT://高度固定,重新计算控件的宽度
      /**
      * 计算公式是:9 /5 = width(未知) /height(已知) =》width= height* (9/5)
      */
        width = (int) (height * videoAspectRatio);
        break;
      case RESIZE_MODE_ZOOM://做拉伸
        if (aspectDeformation > 0) {//如果这个大于0,说明视频源宽高比大于控件宽高比,
        /**
        * 控件的高度固定,宽做拉伸,比如说:视频源是1200/600,控件是1000/700,做拉伸就是1400/700
        */
          width = (int) (height * videoAspectRatio);
        } else {//如果这个小于0,说明视频源宽高比小于控件宽高比
          /**
          *高度做拉伸
          */
          height = (int) (width / videoAspectRatio);
        }
        break;
      default://默认情况,做压缩
        if (aspectDeformation > 0) {
          /**
        * 控件的宽度固定,高度压缩,比如说:视频源是1200/600,控件是1000/700,做压缩就是1000/500
        */
          height = (int) (width / videoAspectRatio);
        } else {
          width = (int) (height * videoAspectRatio);
        }
        break;
    }
    super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值