01 前言
在日常視頻播放中,我們經常會遇到這樣的問題:視頻的長寬比例與設備屏幕不一致,導致畫面上下或左右出現黑邊。雖然這並不影響視頻的正常播放,但從用户體驗的角度來看,這些黑邊往往打斷了視覺的沉浸感,顯得格外突兀。
為了解決這一問題,業界主流播放器(如 YouTube、Netflix)引入了一種被稱為氛圍模式(Ambient Mode)的視覺增強效果。它的核心思路是:
通過實時識別視頻畫面的主色調,並動態將其填充到黑邊區域,使邊緣色彩與視頻內容保持一致,提升整體視覺統一性,從而營造出與視頻內容相協調的氛圍效果,讓觀眾的觀看體驗更加自然和沉浸。
下面是YouTube的氛圍模式效果:
youtube豎屏效果
youtube橫屏效果
百度播放內核團隊也將氛圍模式效果應用到了視頻播放場景,用於提升用户觀看視頻沉浸感,同時在百度App、好看App兩款產品完成上線。本文將詳細説明視頻場景氛圍模式技術方案。
02 整體技術方案
氛圍模式通過在播放內核視頻後處理通道(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. 初始化顏色盒
a. 首先,將所有顏色視為一個大的顏色盒(即整個顏色空間的一個區域)。
b. 顏色盒包含圖像中所有像素的顏色。
2. 選擇分割軸
a. 在每次迭代中,選擇顏色分量(紅、綠、藍)中範圍最大的分量作為分割軸。這是為了最大限度地減少顏色空間的不均勻性。
3. 按中值分割
a. 沿着選定的分割軸,根據顏色值的中值,將顏色盒分成兩個較小的盒。
b. 這種方法確保每個新盒子中包含的顏色數量儘可能相等。
4. 遞歸分割
a. 對每個新的顏色盒重複步驟2和3,直到達到所需的顏色盒數量(通常是所需調色板的大小)。
5. 生成調色板
a. 一旦顏色盒的數量達到預期的數量,對每個盒子計算平均顏色或中值顏色,將其作為代表顏色添加到調色板中。
6. 顏色映射
a. 使用生成的調色板,重新映射原始圖像中的每個像素到最接近的調色板顏色。
中值切割算法核心流程如下圖:
中值切割算法
03 平台渲染氛圍效果
當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);
}
該實現確保了氛圍渲染的高性能和優美視覺效果,為用户提供了沉浸式的觀看體驗。
04 效果展示
氛圍模式已在百度內包括百度App和好看App兩款App完成上線,其中百度App主要集中在搜索三方影視場景,好看App所有視頻橫屏場景(排除廣告視頻)。同時在視頻觀看時長、分發、完播率等UBS指標取得了正向收益,説明氛圍模式給用户帶來了不錯的沉浸式觀影體驗。
下面是百度App和好看App效果展示:
百度App氛圍模式
好看App氛圍模式
5. 總結
氛圍模式是一種視覺增強功能,通過技術手段有效解決了視頻比例不匹配導致的黑邊問題,顯著提升了用户視覺體驗,主要表現在如下幾個方面:
1. 視覺沉浸:氛圍模式通過在視頻周圍添加柔和的背景顏色,使屏幕的邊緣與視頻內容更好地融合。這種設計使得用户在觀看視頻時感覺更加沉浸,減少了視頻與周圍環境之間的視覺割裂
2. 舒適觀看:這種模式可以減少長時間觀看視頻時的眼睛疲勞。通過在視頻周圍使用柔和的色彩過渡,可以緩解亮度差異帶來的視覺刺激,從而提高觀看舒適度。
3. 提升觀感:氛圍模式通過智能地調整背景色彩,使其與視頻中的主要色調相匹配,提升整體觀感。這使得視頻內容更加突出,同時為觀看者提供一種更為和諧的視覺體驗。
通過本文介紹的技術方案,開發者可以實現類似主流視頻平台的高質量氛圍模式效果,為用户帶來更加沉浸的觀看體驗。