填充与裁剪

 

fijkplayer 播放器库中负责显示视频内容的是 class FijkView

FijkView

FijkView 是一个 StatefulWidget,内部会根据视频宽高比以及裁剪模式参数计算出最终显示的视频相对位置,并在 State 类的 build 方法构造包含 Texture 的组件树。
在实际 App 应用中,根据需要可能会调整实际显示视频区域(后面称 Video 区域)和 FijkView 所在区域的相对关系。Video 区域可能会占满 FijkView 区域,也可能只占据 FijkView 的一部分,或者超出 FijkView 的显示区域(超出部分将被裁减)。

下图列举中最常见的3种情况,图中蓝色线框表示 FijkView 区域,灰色矩形和其中的圆形纹理表示 Video 区域。最右侧超出蓝色线框部分实际上不会显示出来。 fitmode

FijkView 的构造函数如下(略去了本节内容中不太关注的可选参数):

FijkView({
    @required FijkPlayer player,
    double width,
    double height,
    Color color = Colors.blueGrey,
    FijkFit fit = FijkFit.contain,
    FijkFit fsFit = FijkFit.contain,
});
  • 参数 player 是实际的内核播放器, FijkView 会调用 player 的视频输出接口,获取player 的视频画面显示。
  • width 控制 FijkView 的宽度,是可选参数,可以为空。 width == null 时,FijkView 宽度会尽可能变大,FijkView 的 BuildContext 必须有宽度上限。
  • height 控制 FijkView 的高度,是可选参数,可以为空。 height == null 时,FijkView 高度会尽可能变大,FijkView 的 BuildContext 必须有高度上限。
  • color 是 FijkView 的背景颜色,当 Video 区域小于 FijkView 区域时,FijkView 内 Video 之外的区域就显示为背景色 color。
  • fit 实际控制 FijkView 的显示填充和裁剪模式。是本节内容重点。
  • fsFit 参数控制 FijkView 在全屏模式下的填充和裁剪模式。fsFit 取值的含义和 fit 相同。

FijkFit

const FijkFit({
    double aspectRatio = -1,
    double sizeFactor = 1.0,
    Alignment alignment = Alignment.center,
});

布局计算三步走

FijkView 通过 FijkFit 中的三个参数完全控制实际视频内容在 FijkView 中的填充裁剪模式。布局计算也可以按照这三个参数分步进行。

  1. 计算 aspectRatio
    以实际代码为例说明最终 aspectRatio 如何计算。
    Video 区域会按照最终计算出的 aspectRatio 进行宽高比例调整。
    double getAspectRatio(BoxConstraints constraints) {
     double ar = widget.fit.aspectRatio;
     if (ar == null || ar < 0) {
         ar = _vWidth / _vHeight;
     } else if (ar.isInfinite) {
         ar = constraints.maxWidth / constraints.maxHeight;
     }
     return ar;
    }
    

    参数 constraints 是通过 LayoutBuilder 动态获取的 FijkView 的 BoxConstraints。
    _vWidth _vHeight 是实际的视频像素宽度和高度。
    如果 fit 中 aspectRatio 是负数或者 null, 则最终 aspectRatio 是实际的视频宽高比。
    如果 fit 中 aspectRatio 是 infinity,则最终 aspectRatio 是 FijkView 区域的宽高比。
    以上都不是,则最终 aspectRatio 就是 fit 中设定的 aspectRatio 值。

  2. 计算 size
    FijkView 的 size 是在 给定的 width 、 height 或者 BoxConstraints 限定内尽可能扩大。
    确定 FijkView 的 size 之后,再结合 aspectRatio 和 sizeFactor 确定 Video 区域 size。
    根据 aspectRatio 和 BoxConstraints 确定一个合适的 Video 区域 Size,具体实现参考了 Flutter sdk class RenderAspectRatio 中方法 Size _applyAspectRatio(BoxConstraints constraints);
    得到一个基础(基准)的 Video 区域 Size,和变换系数 sizeFactor 相乘之后,就是最终的 Video 区域 Size, 这个 size 可能会在一个轴甚至两个轴上超出 FijkView 的 size,超出部分在最终绘制中会被裁剪掉。
    sizeFactor 的几个特殊值,会在这一阶段重新计算:
    (-1.0, -0.0) => max(FijkView.size.width / video.size.width , FijkView.size.height / video.size.heigit)
    (-2.0, -1.0) => FijkView.size.width / video.size.width
    (-3.0, -2.0) => FijkView.size.height / video.size.heigit

  3. 调整位置
    确定 FijkView size 和 Video size 之后,最后一步就是确定 Video 区域在 FijkView 中的相对位置。
    final Alignment resolvedAlignment = widget.fit.alignment;
    final Offset diff = constraints.biggest - childSize;
    final Offset offset = resolvedAlignment.alongOffset(diff);
    final Rect pos = Rect.fromLTWH(offset.dx, offset.dy, childSize.width, childSize.height);
    

    上面片段中 constraints.biggest 就是 FijView 的 size,childSize 就是 Video 区域的 size。 计算出来的 pos 就是 Video 显示区域相对于 FijkView 的位置信息。
    通过使用 Stack widget 和 Positioned widget 实现相对位置布局。

    Positioned.fromRect(
     rect: pos,
     child: buildVideoDisplay(),
    );
    

布局效果样例

原始比例视频内容。

原始比例视频内容

  • FijkFit.contain
static const FijkFit contain = FijkFit(
    sizeFactor: 1.0,
    aspectRatio: -1,
    alignment: Alignment.center,
);


  • FijkFit.fill
static const FijkFit fill = FijkFit(
    sizeFactor: 1.0,
    aspectRatio: double.infinity,
    alignment: Alignment.center,
);


  • FijkFit.cover
static const FijkFit cover = FijkFit(
    sizeFactor: -0.5,
    aspectRatio: -1,
    alignment: Alignment.center,
);

理解 FijkFit 的布局过程之后,你可以随意的组合 FijkFit 中的构造参数来实现你想要的视频填充效果。


您的支持是我的动力。你可以通过以下方式支持我:

  • 给该项目点赞   给该项目点赞
  • 关注我的 Github   关注我的 Github
微信赞赏码 支付宝
微信赞赏码 支付宝二维码