夏日花火,談談移動端背景視頻實現

八月,依然須要空調續命,這個季節二次元大機率在放煙火,給我映像最深的應該是《聲之形》中硝子墜落時的煙火片斷,絢麗而安靜。javascript

先來看看上面的圖片,這是使用 ffmpeg 截取視頻片斷轉成的 gif,用 gif 來實現背景視頻也是一種選擇,一張圖片就能解決的事,爲何還要研究背景視頻呢?css

爲何不使用 GIF

先來看一組數據:html

源視頻分辨率爲 1920×1080,25fps 時長爲 6s,體積是 1.4M,轉成同分辨率同幀率的 gif 圖片,體積竟然要 26M !!java

並且因爲 gif 格式只支持 256 色,mp4 轉 gif 畫面的分辨率雖然不變但畫質有很大損失,上圖能看到明顯的像素塊效果。git

因此平常視頻轉 gif 時都會進行必定的壓縮處理,頁首 gif 通過 640×360 12fps 的壓縮處理過的體積是 2.3M,仍是大於視頻的體積。github

# mp4 轉 gif
ffmpeg -y -i demo.mp4 -s 640x360 -r 12 demo_mini.gif
複製代碼

相較於視頻 gif 有兩個比較明顯的缺點:web

  • 同等視覺效果的 gif 圖片體積遠大於視頻文件
  • gif 受限於 256色沒法保證視覺效果的細節

因此說時代變了,gif 這一頁能夠揭過了,看看視頻~chrome

video 元素的背景實現

咱們先來看看使用視頻來實現背景的關鍵要素:npm

  1. 自動播放
  2. 去除掉 video 的播放控件
  3. video 元素須要放置於底層

好像沒什麼問題,能夠開整了:canvas

<section>
    <video loop autoplay="autoplay" preload="auto" >
        <source src="./demo.mp4" type="video/mp4">
    </video>
    <div>mask</div>
</section>
複製代碼

video 元素上面絕對定位改了一個遮罩層,loop 循環播放,且不配置 controls 是不會展現控制組件的,嗯,看起來正常,不過視頻並無自動播放,機智的你又查查資料,發現視頻自動播放是有限制的,只有無音軌的視頻或者靜音 video 元素才能播放,因而乎加上了 muted 你寫下了:

<section>
    <video loop muted autoplay="autoplay" preload="auto" >
        <source src="./demo.mp4" type="video/mp4" >
    </video>
    <div>mask</div>
</section>
複製代碼

視頻自動播放了,電腦上看起來沒什麼問題,要不換手機試試?

打開 charles 掛上代理,用果子的 Safari 打開,看起來沒啥問題,換微信試試,而後你驚訝的發現:

  • 視頻元素的層級錯亂
  • 視頻沒法自動播放了

好吧找找解決方法

視頻元素的層級錯亂解決方案

爲 video 設置內聯播放標識 playsinline,而後爲了兼容須要加上各類前綴:

  • playsinline="true"
  • webkit-playsinline="true"
  • mtt-playsinline="true"

指定微信端的頁面播放器類型:

  • x5-video-player-type="h5-page"

視頻沒法自動播放

微信端能夠監聽頁面的 WeixinJSBridgeReady 來觸發視頻播放

<section>
    <video muted loop class="player" autoplay="autoplay" preload="auto" playsinline="true" webkit-playsinline="true" mtt-playsinline="true" x5-video-player-type="h5-page" >
        <source src="./demo.mp4" type="video/mp4">
    </video>
    <div class="mask">
        遮罩層
    </div>
</section>
<script> window.onload = function () { const player = document.querySelector('.player'); // 自動播放 document.addEventListener('WeixinJSBridgeReady', player.play()); } </script>
複製代碼

微信試試,穩得很。保險起見試試安卓端的各類瀏覽器吧。

果不其然,video 仍是那個 video,瀏覽器已經不是那個 chrome 了。

幾乎每一個國產品牌的安卓機自帶瀏覽器都有對 video 元素都有些定製化處理,常見且不限於:

  • 靜音視頻沒法自動播放
  • 沒法隱藏控制條(即便未設置 controls
  • 視頻爲頂級元素沒法被覆蓋
  • 視頻默認爲彈層窗口播放
  • 更有甚者給你加廣告
  • ...

video 元素在安卓端特性並不統一,目前並無找到很好解決方案。

既然 video 不行,換 canvas 不就行了,video draw canvas 不是分分鐘的事。

video draw canvas ?

放個圖大家感覺下:

同志們,要把 video 繪製到 canvas 得先播放呀!

即便是有些能夠自動播放的視頻的瀏覽器,將 video 繪製 canvas 時,video 元素必須是可見的,因此隱藏的 video 元素也是作不到的。

既然這樣,咱們能夠將視頻截取一張張的圖片,而後打個壓縮文件,經過 canvas 繪製出來?

等等這不就是視頻文件嗎?還犯得上去截圖播放嗎?因此咱們須要的是將視頻文件解碼成可播放的圖片幀!

video decode

視頻解碼的方案簡單來講就是將視頻文件解碼成一一幀幀的圖片,在 canvas 上按必定速率繪製出來。

前人栽樹,後人乘涼,github 蒐羅下找到幾個可用的庫

WasmVideoPlayer 原型演示,ffmepg + wasm 實現,移動端性能較差

WXInlinePlayer 依賴於 flv, 且體積較大

Broadway 做者實現了一套安卓端的 h264 解碼器(c 語言實現 + wasm),支持特定編碼的 mp4 格式的視頻,且支持不完善有黑屏狀況,且沒法按照視頻幀率進行播放(未對視頻進行細顆粒度播放)

jsmpeg 做者手擼了一個 mpeg-ts 的解碼器(支持 JavaScript 與 wasm 版本),支持 ts 格式(mpeg1 編碼)的視頻,支持較爲穩定,可支持細顆粒度播放

由於 MP4 格式的視頻較爲常見,若是能直接播放 MP4 是最理想的,但 WasmVideoPlayerBroadway 的實現都不太理想,一個是移動端性能差,一個解碼支持不完善。

上面的工具都未提供 npm 包支持,若是想在生產使用還須要本身作一下二次封裝。

最終的選用的是 jsmpeg,jsmpeg 支持 ts 格式的視頻有個優點。

ts 是日本高清攝像機拍攝下進行的封裝格式,全稱爲 MPEG2-TS 。ts 即 "Transport Stream" 的縮寫。MPEG2-TS 格式的特色就是要求從視頻流的任一片斷開始都是能夠獨立解碼的。

例如一個背景視頻素材 10M 咱們不須要將整個視頻請求下來再進行播放,能夠經過 Content-Range 拆分紅多個片斷來加速視頻載入播放。jsmpeg 也支持視頻的分片請求。

jsmpeg 自己模塊拆分的比較清楚,因此二次包裝成 npm 也比較方便。

silent-film-player 是我對 jsmpeg 作的簡單二次封裝,考慮是作背景視頻播放的,因此去掉音頻解碼相關的模塊,新增了 Web Workers 的支持:

  • 拆分模塊支持 npm 包
  • 移除聲音模塊
  • 抽離核心的解析模塊
  • 新增 Web Workers 支持

感興趣的同窗能夠試試~

使用:

<template>
    <section>
        <canvas ref="canvas"></canvas>
        <div class="mask">
            遮罩層
        </div>
    </section>
</template>

<script> import Player from 'silent-film-player'; export default { data() { return { url: 'https://xxx.ts', }; }, mounted() { const { url, $refs: { canvas }, } = this; window.player = new Player(url, { canvas, loop: true, autoplay: true, disableWebAssembly: true, // 分片大小 chunkSize: 1 * 1024 * 1024, videoBufferSize: 512 * 1024, }); }, }; </script>
複製代碼

看幾個示例:

示例倉庫在這:github.com/kinglisky/b…

至此大概是個比較靠譜的背景視頻實現方案。

前景視頻實現

是否是以爲背景視頻就只能放在視覺的底層,先來看個例子:

是否是很神奇,結構大概以下,canvas 覆蓋在一個圖片元素之上,但視頻的內容卻透出了底部圖片。

<section class="container">
    <img class="view-bg" src="./demo.jpeg" />
    <canvas ref="canvas" class="view-canvas" >
    </canvas>
    <div class="view-mask">MASK</div>
</section>
複製代碼

具體實現爲在 canvas 上附加上一個 css 屬性

mix-blend-mode: screen;
複製代碼

對的,只須要設置上這個屬性,視頻中的黑色部分便可透出下層圖片。

關於 mix-blend-mode 屬性的詳解能夠參考張鑫旭的 深刻理解CSS mix-blend-mode濾色screen混合模式

其餘

  • 視頻解碼比較耗性能,注意控制播放的視頻個數,能夠對可視區域內視頻元素作播放控制,不可見的視頻不播放

  • wasm 解碼在移動端支持和性能較差(特別是安卓端),不太建議使用

  • 對支持的標準 video 行爲的瀏覽器,如桌面端瀏覽器能夠直接使用 video 實現背景視頻

  • iOS 雖然大部分瀏覽器對 video 元素支持不錯,但有些特例如:夸克UC 極速版ALOOK 視頻默認是彈層播放的,並且 ALOOK 瀏覽器的 userAgent 竟然是和 Safari 同樣的,因此你根本無法區分它倆

  • 微信頁面的 WeixinJSBridgeReady 沒法觸發異步加載的視頻

  • MP4 轉 TS 會形成視頻質量降低,建議提升轉碼的比率

簡單講講實現的思路,目前這套方案已經在線上跑了,感興趣的同窗能夠看看:

編輯模板:www.gaoding.com/template/18…

成品頁面:g.gd-share.cn/p/gpmjb2gs

下期講講:阿里雲函數計算 + OSS 觸發器實現視頻文件批量轉碼,

over~

參考資料

相關文章
相關標籤/搜索