播放器视频后处理实践(二)氛围模式

作者:播放内核团队
来源:百度App技术

1. 前言

在日常视频播放中,我们经常会遇到这样的问题:视频的长宽比例与设备屏幕不一致,导致画面上下或左右出现黑边。虽然这并不影响视频的正常播放,但从用户体验的角度来看,这些黑边往往打断了视觉的沉浸感,显得格外突兀。

为了解决这一问题,业界主流播放器(如 YouTube、Netflix)引入了一种被称为氛围模式(Ambient Mode)的视觉增强效果。它的核心思路是:

通过实时识别视频画面的主色调,并动态将其填充到黑边区域,使边缘色彩与视频内容保持一致,提升整体视觉统一性,从而营造出与视频内容相协调的氛围效果,让观众的观看体验更加自然和沉浸。

下面是YouTube的氛围模式效果:

播放器视频后处理实践(二)氛围模式
youtube竖屏效果
播放器视频后处理实践(二)氛围模式
youtube横屏效果

百度播放内核团队也将氛围模式效果应用到了视频播放场景,用于提升用户观看视频沉浸感,同时在百度App、好看App两款产品完成上线。本文将详细说明视频场景氛围模式技术方案。

2. 整体技术方案

氛围模式通过在播放内核视频后处理通道(FilterChain)添加一个AmbientFilter滤镜实现,其核心思路:通过AmbientFilter滤镜先将视频帧数据从GPU下载到CPU,然后将视频帧数据按块进行区域划分,划分完成后再通过颜色量化算法提取每个区域主色调,最后将各个区域主色调传给平台层,平台层拿到主色调进行绘制视频四周氛围效果。整体方案流程大致如下图所示:

播放器视频后处理实践(二)氛围模式
氛围模式整体方案

2.1 视频帧采样

为了提取视频的主色调,需要获取视频帧数据。但提取主色调并不要求每帧都下载,太频繁下载会拖垮应用性能,在视觉上也不会带来特别好的体验。因此我们对视频帧进行采样下载:在 25 FPS 的视频下,每隔约 50 帧(约 2 秒)采集一次帧数据。同时,为了避免将视频帧数据从 GPU 下载到 CPU 时阻塞渲染线程,我们采取了以下优化:

  1. FBO 压缩:先将视频帧渲染到较低分辨率的 FBO(例如将 1080p 压缩到 108p),大幅减少待传输的数据量。
  2. PBO 异步传输:利用 PBO 异步将帧数据从 GPU 下载到 CPU,从而避免阻塞主渲染线程。

通过这种方式,我们既能保证主色调提取的效率,又不会影响视频的流畅播放。渲染线程和氛围模式工作线程两个线程工作流程如下图:

播放器视频后处理实践(二)氛围模式
线程核心职责

2.2 主色调提取

2.2.1 视频帧区域划分

拿到视频帧数据后,我们先将视频帧划分出几个区域。项目中我们是将视频帧画面划分为:TopLeft, TopCenter, TopRight, BottomLeft, BottomCenter, BottomRight 六个区域,如下图所示:

播放器视频后处理实践(二)氛围模式
视频区域块划分

接下来我们提取出每块区域的主色调。

2.2.2 提取主色调

要提取画面主色调,我们是通过颜色量化技术实现的。颜色量化(Color Quantization) 是一种图像处理技术,目的是减少图像中使用的颜色数量,同时尽量保持原图的视觉效果。代表性的颜色量化算法有:

  1. 中值切割法(Median Cut):将颜色空间递归分割成小立方体,取每个立方体的颜色中位数作为调色板颜色。
  2. K-means聚类:将颜色按相似性分组,取每组的中心作为调色板颜色。
  3. 八叉树算法:通过构建八叉树分层合并颜色,逐层减少叶子节点数量,最终保留高频颜色。
  4. 流行色算法(Popularity):统计原图颜色出现的频率,选取高频颜色作为调色板。

这几种算法从各维度对比情况如下:

播放器视频后处理实践(二)氛围模式

从算法的速度、精度以及实现复杂度等多维度考虑,氛围模式场景我们选用中值切割法完成视频画面主色调的提取。

2.2.3 中值切割法

中值切割法(Median Cut)是一种用于图像颜色量化的算法,算法核心思想是将颜色空间递归地分割成更小的区域,以减少图像中颜色数量。该算法的目标是在颜色空间中选择一组代表性的颜色,这些颜色可以用于生成调色板,从而减少图像的颜色数量,同时尽量保留图像的视觉效果。算法核心步骤如下:

1. 初始化颜色盒

  1. 首先,将所有颜色视为一个大的颜色盒(即整个颜色空间的一个区域)。
  2.  颜色盒包含图像中所有像素的颜色。

2. 选择分割轴

  1. 在每次迭代中,选择颜色分量(红、绿、蓝)中范围最大的分量作为分割轴。这是为了最大限度地减少颜色空间的不均匀性。

3. 按中值分割

  1. 沿着选定的分割轴,根据颜色值的中值,将颜色盒分成两个较小的盒。
  2.  这种方法确保每个新盒子中包含的颜色数量尽可能相等。

4. 递归分割

  1. 对每个新的颜色盒重复步骤2和3,直到达到所需的颜色盒数量(通常是所需调色板的大小)。

5. 生成调色板

  1. 一旦颜色盒的数量达到预期的数量,对每个盒子计算平均颜色或中值颜色,将其作为代表颜色添加到调色板中。

6. 颜色映射

  1. 使用生成的调色板,重新映射原始图像中的每个像素到最接近的调色板颜色。

中值切割算法核心流程如下图:

播放器视频后处理实践(二)氛围模式
中值切割算法

3. 平台渲染氛围效果

当native层提取完视频帧各区域主色调后,将色值传给平台层(Android/iOS)。平台层收到色值后,将色值渲染到视频四周以产生氛围效果。为保证各个区域色值过渡自然,以及前后两帧的色值平滑过渡,需要借助平台层渐变、动画、rgb插值等技术实现。 下面结合Android和iOS两个平台分别介绍具体思路。

3.1 Android平台

Android 使用自定义view技术,完成氛围色值的渲染。我们提供一个自定义view名为AmbientView 来完成这个功能。有了AmbientView之后,布局结构大致如下:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center">
        <com.baidu.cyberplayer.sdk.AmbientView
            android:id="@+id/left_ambient"
            android:layout_width="xxxdp"
            android:layout_height="match_parent"/>
        <FrameLayout
            android:id="@+id/video_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <com.baidu.cyberplayer.sdk.AmbientView
            android:id="@+id/right_ambient"
            android:layout_width="xxxdp"
            android:layout_height="match_parent"/>
</FrameLayout>

上面为视频横屏下布局大致情况,id为video_container的FrameLayout是播放器容器,在播放器容器左右各摆放一个AmbientView渲染氛围模式,AmbientView的宽度会根据播放器的尺寸的变化在代码中动态调整。

AmbientView核心功能

1. 相邻区域的主色调,使用LinearGradient拉出线形渐变。对于横屏视频,我们渐变方向就是从上至下。所以更新氛围色值的代码如下:

private void updateGradient() {
    mLinearGradient = new LinearGradient(0, 0, 0, getHeight(),
                        mColors, null, Shader.TileMode.CLAMP);
    mPaint.setShader(mLinearGradient);
    invalidate();
}

2. 前后两帧氛围色值的切换,为了颜色切换不显得生硬,我们借助Android属性动画以及RGB插值实现色值缓慢渐变效果,核心代码如下:

private void startColorAnimator() {
    int[] lastColors = new int[mLastColors.length];
    for (int i = 0; i < lastColors.length; i++) {
        lastColors[i] = mLastColors[i];
    }

    mColorAnimator = ValueAnimator.ofFloat(0, 1f);
    mColorAnimator.setDuration(1500);
    mColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(@NonNull ValueAnimator valueAnimator) {
            float progress = (float) valueAnimator.getAnimatedValue();
            interpolateColors(progress, lastColors);
            updateGradient();
        }
    });
    mColorAnimator.start();
}

/**
 * 插值计算color
 */
private void interpolateColors(float progress, int[] lastColors) {
    if (mCurColors == null || mCurColors.length <= 0) {
        return;
    }

    ArgbEvaluator evaluator = new ArgbEvaluator();
    for (int i = 0; i < mCurColors.length; i++) {
        mColors[i] = (int) evaluator.evaluate(progress, lastColors[i], mCurColors[i]);
    }
}

mColorAnimator是一个ValueAnimator对象,通过ValueAnimator我们创建一个1500ms的动画,在动画的更新函数里面,我们调用了interpolateColors,这个方法内部就是用ArgbEvaluator完成RGB颜色插值,更新到mColors数组中。最后调用updateGradient方法触发AmbientView重绘。

3. 渐变遮罩:最后我们还要在上面添加一层黑色渐变遮罩,保证氛围区域不要太突兀,以免过度吸引用户眼球,导致用户注意力不在视频内容本身上面。黑色遮罩实现也非常简单,代码如下所示:

float[] mPositions = {0.0f, 1.0f};
int[] mMaskColors = {0x88000000, 0xff000000};
// 从左到右渐变
mMaskLinearGradient = new LinearGradient(0, 0, getWidth(), 0,
                            mMaskColors, mPositions, Shader.TileMode.CLAMP);
mMaskPaint.setShader(mMaskLinearGradient);
// 绘制黑色渐变蒙层
canvas.drawRect(0, 0, getWidth(), getHeight(), mMaskPaint);

3.2 iOS平台

iOS端同样提供了一个自定义的 AmbientView(氛围视图),为视频播放场景提供动态渐变背景和遮罩效果,增强视觉沉浸感。

1. 双图层架构设计:采用主渐变层与遮罩层分离的架构方案,确保色彩渲染与边缘遮罩效果互不干扰,提升整体渲染效率。

- (void)setupSubLayers {
    _gradientLayer = [CAGradientLayer layer];
    _gradientLayer.frame = self.bounds;
    [self.layer addSublayer:_gradientLayer];

    _maskLayer = [CAGradientLayer layer];
    _maskLayer.frame = self.bounds;
    [self.layer addSublayer:_maskLayer];
}

2. 流畅动画引擎:基于CADisplayLink构建动画循环,通过实时颜色插值计算,实现细腻流畅的色彩过渡效果。

- (void)startAnimation {
    // 核心功能代码
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateColors)];
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)updateColors {
    CGFloat progress = MIN(1.0, (CACurrentMediaTime() - self.startTime) / self.animationDuration);
    NSMutableArray *interpolated = [NSMutableArray array];
    for (NSUInteger i = 0; i < self.endColors.count; i++) {
        UIColor *from = i < self.startColors.count ? self.startColors[i] : [UIColor clearColor];
        UIColor *to = self.endColors[i];
        [interpolated addObject:(__bridge id)[self interpolateFrom:from to:to progress:progress].CGColor];
    }
    _gradientLayer.colors = interpolated;
}

- (UIColor *)interpolateFrom:(UIColor *)from to:(UIColor *)to progress:(CGFloat)progress {
    CGFloat fr, fg, fb, fa, tr, tg, tb, ta;
    [from getRed:&fr green:&fg blue:&fb alpha:&fa];
    [to getRed:&tr green:&tg blue:&tb alpha:&ta];
    return [UIColor colorWithRed:fr + (tr - fr) * progress
                           green:fg + (tg - fg) * progress
                            blue:fb + (tb - fb) * progress
                           alpha:fa + (ta - fa) * progress];
}

3. 渐变遮罩:采用多段式渐变遮罩配合加速曲线算法,打造自然的边缘过渡,有效增强视觉层次感。

- (void)makeMaskColorsAndLocations {
    const NSInteger steps = 6;
    for (NSInteger i = 0; i < steps; i++) {
        CGFloat t = (CGFloat)i / (steps - 1);
        CGFloat acceleratedT = t * t;
        CGFloat currentAlpha = a + (1.0 - a) * acceleratedT;

        UIColor *color = [UIColor colorWithRed:r green:g blue:b alpha:currentAlpha];
        [_maskColors addObject:(__bridge id)color.CGColor];
        [_maskColorsLocations addObject:@(t)];
    }
    _maskLayer.colors = _maskColors;
    _maskLayer.locations = _maskColorsLocations;
    _maskLayer.startPoint = CGPointMake(0, 0);
    _maskLayer.endPoint = CGPointMake(1, 0);
}

该实现确保了氛围渲染的高性能和优美视觉效果,为用户提供了沉浸式的观看体验。

4. 效果展示

氛围模式已在百度内包括百度App和好看App两款App完成上线,其中百度App主要集中在搜索三方影视场景,好看App所有视频横屏场景(排除广告视频)。同时在视频观看时长、分发、完播率等UBS指标取得了正向收益,说明氛围模式给用户带来了不错的沉浸式观影体验。

下面是百度App和好看App效果展示:

播放器视频后处理实践(二)氛围模式
百度App氛围模式

5. 总结

氛围模式是一种视觉增强功能,通过技术手段有效解决了视频比例不匹配导致的黑边问题,显著提升了用户视觉体验,主要表现在如下几个方面:

  1. 视觉沉浸:氛围模式通过在视频周围添加柔和的背景颜色,使屏幕的边缘与视频内容更好地融合。这种设计使得用户在观看视频时感觉更加沉浸,减少了视频与周围环境之间的视觉割裂
  2. 舒适观看:这种模式可以减少长时间观看视频时的眼睛疲劳。通过在视频周围使用柔和的色彩过渡,可以缓解亮度差异带来的视觉刺激,从而提高观看舒适度。
  3. 提升观感:氛围模式通过智能地调整背景色彩,使其与视频中的主要色调相匹配,提升整体观感。这使得视频内容更加突出,同时为观看者提供一种更为和谐的视觉体验。

通过本文介绍的技术方案,开发者可以实现类似主流视频平台的高质量氛围模式效果,为用户带来更加沉浸的观看体验。

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论