Chrome 66 禁止聲音自動播放,開發怎麼應對?

2020-12-19 開源中國

聲音無法自動播放這個在IOS/Android上面一直是個慣例,桌面版的Safari在2017年的11版本也宣布禁掉帶有聲音的多媒體自動播放功能,緊接著在2018年4月份發布的Chrome 66也正式關掉了聲音自動播放,也就是說<audio autopaly></audio> <video autoplay></video>在桌面版瀏覽器也將失效。

最開始移動端瀏覽器是完全禁止音視頻自動播放的,考慮到了手機的帶寬以及對電池的消耗。但是後來又改了,因為瀏覽器廠商發現網頁開發人員可能會使用GIF動態圖代替視頻實現自動播放,正如IOS文檔所說,使用GIF的帶寬流量是Video(h264)格式的12倍,而播放性能消耗是2倍,所以這樣對用戶反而是不利的。又或者是使用Canvas進行hack,如Android Chrome文檔提到。因此瀏覽器廠商放開了對多媒體自動播放的限制,只要具備以下條件就能自動播放:

(1)沒音頻軌道,或者設置了muted屬性

(2)在視圖裡面是可見的,要插入到DOM裡面並且不是display: none或者visibility: hidden的,沒有滑出可視區域。

換句話說,只要你不開聲音擾民,且對用戶可見,就讓你自動播放,不需要你去使用GIF的方法進行hack.

桌面版的瀏覽器在近期也使用了這個策略,如升級後的Safari 11的說明:

以及Chrome文檔的說明:

這個策略無疑對視頻網站的衝擊最大,如在Safari打開tudou的提示:

添加了一個設置嚮導。Chrome的禁止更加人性化,它有一個MEI的策略,這個策略大概是說只要用戶在當前網頁主動播放過超過7s的音視頻(視頻窗口不能小於200 x 140),就允許自動播放。

對於網頁開發人員來說,應當如何有效地規避這個風險呢?

Chrome的文檔給了一個最佳實踐:先把音視頻加一個muted的屬性就可以自動播放,然後再顯示一個聲音被關掉的按鈕,提示用戶點一下打開聲音。對於視頻來說,確實可以這樣處理,而對於音頻來說,很多人是監聽頁面點擊事件,只要點一次了就開始播放聲音,一般就是播放個背景音樂。但是如果對於有多個聲音資源的頁面來說如何自動播放多個聲音呢?

首先,如果用戶還沒進行交互就調用播放聲音的API,Chrome會這麼提示:

DOMException: play() failed because the user didn't interact with the document first.

Safari會這麼提示:

NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.

Chrome報錯提示最為友善,意思是說,用戶還沒有交互,不能調play。用戶的交互包括哪些呢?包括用戶觸發的touchend, click, doubleclick或者是 keydown事件,在這些事件裡面就能調play.

所以上面提到很多人是監聽整個頁面的點擊事件進行播放,不管點的哪裡,只要點了就行,包括觸摸下滑。這種方法只適用於一個聲音資源,不適用多個聲音,多個聲音應該怎麼破呢?這裡並不是說要和瀏覽器對著幹,「逆天而行」,我們的目的還是為了提升用戶體驗,因為有些場景如果能自動播放確實比較好,如一些答題的場景,需要聽聲音進行答題,如果用戶在答題的過程中能依次自動播放相應題目的聲音,確實比較方便。同時也是討論聲音播放的技術實現。

原生播放視頻應該就只能使用video標籤,而原生播放音頻除了使用audio標籤之外,還有另外一個API叫AudioContext,它是能夠用來控制聲音播放並帶了很多豐富的操控接口。調audio.play必須在點擊事件裡面響應,而使用AudioContext的區別在於只要用戶點過頁面任何一個地方之後就都能播放了。所以可以用AudioContext取代audio標籤播放聲音。

我們先用audio.play檢測頁面是否支持自動播放,以便決定我們播放的時機。

1. 頁面自動播放檢測

方法很簡單,就是創建一個audio元素,給它賦一個src,append到dom裡面,然後調用它的play,看是否會拋異常,如果捕獲到異常則說明不支持,如下代碼所示:

function testAutoPlay () {    // 返回一個promise以告訴調用者檢測結果    return new Promise(resolve => {        let audio = document.createElement('audio');        // require一個本地文件,會變成base64格式        audio.src = require('@/assets/empty-audio.mp3');        document.body.appendChild(audio);        let autoplay = true;        // play返回的是一個promise        audio.play().then(() => {            // 支持自動播放            autoplay = true;        }).catch(err => {            // 不支持自動播放            autoplay = false;        }).finally(() => {            audio.remove();            // 告訴調用者結果            resolve(autoplay);        });    });}

這裡使用一個空的音頻文件,它是一個時間長度為0s的mp3文件,大小只有4kb,並且通過webpack打包成本地的base64格式,所以不用在canplay事件之後才調用play,直接寫成同步代碼,如果src是一個遠程的url,那麼就得監聽canplay事件,然後在裡面play.

在告訴調用者結果時,使用Promise resolve的方式,因為play的結果是異步的,並且不用await,是因為在給別人調用的庫函數裡面不應該使用await,由調用者自行決定是否要await,不然庫函數就變成同步的代碼,就得強制別人去await你這個庫函數。

2. 監聽頁面交互點擊

如果當前頁面能夠自動播放,那麼可以毫無顧忌地讓聲音自動播放了,否則就得等到用戶開始和這個頁面交互了即有點擊操作了之後才能自動播放,如下代碼所示:

let audioInfo = {    autoplay: false,    testAutoPlay () {        // 代碼同,略...     },    // 監聽頁面的點擊事件,一旦點過了就能autoplay了    setAutoPlayWhenClick () {        function setAutoPlay () {            // 設置自動播放為true            audioInfo.autoplay = true;            document.removeEventListener('click', setAutoPlay);            document.removeEventListener('touchend', setAutoPlay);        }        document.addEventListener('click', setCallback);        document.addEventListener('touchend', setCallback);    },    init () {        // 檢測是否能自動播放        audioInfo.testAutoPlay().then(autoplay => {            if (!audioInfo.autoplay) {                audioInfo.autoplay = autoplay;            }        });        // 用戶點擊交互之後,設置成能自動播放        audioInfo.setAutoPlayWhenClick();    }};audioInfo.init();export default audioInfo;

上面代碼主要監聽document的click事件,在click事件裡面把autoplay值置為true。換句話說,只要用戶點過了,我們就能隨時調AudioContext的播放API了,即使不是在點擊事件響應函數裡面,雖然無法在異步回調裡面調用audio.play,但是AudioContext可以做到。

代碼最後通過調用audioInfo.init,把能夠自動播放的信息存儲在了audioInfo.autoplay這個變量裡面。當需要播放聲音的時候,例如切到了下一題,需要自動播放當前題的幾個音頻資源,就取這個變量判斷是否能自動播放,如果能就播,不能就等用戶點聲音圖標自己去播,並且如果他點過了一次之後就都能自動播放了。

那麼怎麼用AudioContext播放聲音呢?

3. AudioContext播放聲音

先請求音頻文件,放到ArrayBuffer裡面,然後用AudioContext的API進行decode解碼,解碼完了再讓它去play,就行了。

我們先寫一個請求音頻文件的ajax:

function request (url) {    return new Promise (resolve => {        let xhr = new XMLHttpRequest();        xhr.open('GET', url);        // 這裡需要設置xhr response的格式為arraybuffer        // 否則默認是二進位的文本格式        xhr.responseType = 'arraybuffer';        xhr.onreadystatechange = function () {            // 請求完成,並且成功            if (xhr.readyState === 4 && xhr.status === 200) {                resolve(xhr.response);            }        };        xhr.send();    });}

這裡需要注意的是要把xhr響應類型改成arraybuffer,因為decode需要使用這種存儲格式,這樣設置之後,xhr.response就是一個ArrayBuffer格式了。

接著實例化一個AudioContext,讓它去解碼然後play,如下代碼所示:

// Safari是使用webkit前綴let context = new (window.AudioContext || window.webkitAudioContext)();// 請求音頻數據let audioMedia = await request(url);// 進行decode和playcontext.decodeAudioData(audioMedia, decode => play(context, decode));

play的函數實現如下:

function play (context, decodeBuffer) {    let source = context.createBufferSource();    source.buffer = decodeBuffer;    source.connect(context.destination);    // 從0s開始播放    source.start(0);}

這樣就實現了AudioContext播放音頻的基本功能。

如果當前頁面是不能autoplay,那麼在 new AudioContext的時候,Chrome控制臺會報一個警告:

這個的意思是說,用戶還沒有和頁面交互你就初始化了一個AudioContext,我是不會讓你play的,你需要在用戶點擊了之後resume恢復這個context才能夠進行play.

假設我們不管這個警告,直接調用play沒有報錯,但是沒有聲音。所以這個時候就要用到上一步audioInfo.autoplay的信息,如果這個為true,那麼可以play,否則不能play,需要讓用戶自己點聲音圖標進行播放。所以,把代碼重新組織一下:

function play (context, decodeBuffer) {    // 調用resume恢復播放    context.resume();    let source = context.createBufferSource();    source.buffer = decodeBuffer;    source.connect(context.destination);    source.start(0);}function playAudio (context, url) {    let audioMedia = await request(url);    context.decodeAudioData(audioMedia, decode => play(context, decode));}let context = new (window.AudioContext || window.webkitAudioContext)();// 如果能夠自動播放if (audioInfo.autoplay) {    playAudio(url);}// 支持用戶點擊聲音圖標自行播放$('.audio-icon').on('click', function () {    playAudio($(this).data('url'));});

調了resume之後,如果之前有被禁止播放的音頻就會開始播放,如果沒有則直接恢復context的自動播放功能。這樣就達到基本目的,如果支持自動播放就在代碼裡面直接play,不支持就等點擊。只要點了一次,不管點的哪裡接下來的都能夠自動播放了。就能實現類似於每隔3s自動播下一題的音頻的目的:

// 每隔3秒自動播放一個聲音playAudio('question-1.mp3');setTimeout(() => playAudio(context, 'question-2.mp3'), 3000);setTimeout(() => playAudio(context, 'question-3.mp3'), 3000);

這裡還有一個問題,怎麼知道每個聲音播完了,然後再隔個3s播放下一個聲音呢?可以通過兩個參數,一個是解碼後的decodeBuffer有當前音頻的時長duration屬性,而通過context.currentTime可以知道當前播放時間精度,然後就可以弄一個計時器,每隔100ms比較一下context.currentTime是否大於docode.duration,如果是的話說明播完了。soundjs這個庫就是這麼實現的,我們可以利用這個庫以方便對聲音的操作。

這樣就實現了利用AudioContext自動播放多個音頻的目的,限制是用戶首次打開頁面是不能自動播放的,但是一旦用戶點過頁面的任何一個地方就可以了。

AudioContext還有其它的一些操作。

4. AudioContext控制聲音屬性

例如這個CSS Tricks列了幾個例子,其中一個是利用AudioContext的振蕩器oscillator寫了一個電子木琴:

這個例子沒有用到任何一個音頻資源,都是直接合成的,感受如這個Demo:Play the Xylophone (Web Audio API).

還有這種混響均衡器的例子:

見這個codepen:Web Audio API: parametric equalizer.

最後,一直以來都是只有移動端的瀏覽器禁掉了音視頻的自動播放,現在桌面版的瀏覽器也開始下手了。瀏覽器這樣做的目的在於,不想讓用戶打開一個頁面就各種廣告或者其它亂七八糟的聲音在播,營造一個純靜的環境。但是瀏覽器也不是一刀切,至少允許音視頻靜音的播放。所以對於視頻來說,可以靜音自動播放,然後加個聲音被關掉的圖標讓用戶點擊打開,再加添加設置嚮導之類的方法引導用戶設置允許當前網站自動播放。而對於聲音可以用AudioContext的API,只要頁面被點過一次AudioContext就被激活了,就能直接在代碼裡面控制播放了。

以上可作為當前網頁多媒體播放的最佳實踐參考。

作者:人人網FED
連結:https://juejin.im/post/5af7129bf265da0b8262df4c
來源:掘金

相關焦點

  • 斯坦福開發全息聲音合成系統,自動為計算機動畫渲染逼真聲音
    文章相關引用及參考:sciencedaily斯坦福研究人員開發的全息聲音合成系統可以自動為計算機動畫渲染逼真的聲音(映維網 2018年08月13日)通過計算快速移動和振動表面(例如鈸)所產生的壓力波,斯坦福研究人員開發的全息聲音合成系統可以自動為計算機動畫渲染逼真的聲音
  • 討厭自動播放?Chrome 以後可以把煩人網站徹底靜音
    現在上網最煩的事兒之一,就是突然開始自動播放視頻的網頁。不僅有可能被突然出聲的電腦嚇一跳,而且還有可能壓根找不著視頻怎麼關掉。Google Chrome 開發團隊看起來也受不了這個問題了。中文網站中,以騰訊新聞最為典型,在不久之前,幾乎所有的騰訊新聞頁面下方都會加入一個相關的視頻窗口,只要打開就會開始自動播放。因為騰訊視頻升級了 Html5 播放器,禁用 Flash 也是沒用的。不過這種狀況現在已經沒有了。
  • 2020公需課自動播放與答題腳本(廣東教師&華醫網)
    3、之前《使用Python自動刷華醫網視頻、自動考試(2020公需課)》的方法依然可用,而且可以最快可以1小時刷完。下面介紹一個新的2020公需課自動播放與答題腳本,適用於華醫網及廣東省教師公需課學習平臺,適合想要省錢的電腦小白,以及不得不幫上級刷課的同學。
  • 谷歌chrome怎麼翻譯網頁?平板電腦手機安卓chrome怎麼翻譯教程
    可以選擇翻譯即可一般的時候Chrome都是自動提示您是否要翻譯網頁。但是如果Chrome翻譯不彈出提示,我們可以到chrome設置中打開自帶的網頁翻譯功能。當然我們還可以點擊地址欄翻譯圖標,也可以在要翻譯的網頁滑鼠右鍵進行翻譯,以前是電腦版谷歌chrome怎麼翻譯網頁的全部內容了,下面我們講解一下安卓平板電腦或手機安卓版chrome怎麼翻譯網頁呢?
  • Mac設備如何禁止Safari自動播放視頻?
    Safari 瀏覽器允許網站自動播放視頻,但是禁用這個功能並不是那麼簡單。 Mac 用戶時不時就會遇到一個未經同意就自動播放視頻的網頁,雖然有時候視頻自動播放是有好處的,因為它能夠提前加載視頻,但有時候這個功能會讓我們分心。
  • 獵豹來了,給你點顏色(chrome)看看...
    ▲登陸淘寶時會自動加載網購環境,不過這個時間比較長。希望新版本能提提速...▲這麼看,還真是敢的...頁面靜音 設置項中有別致的「頁面靜音」按鈕...我經常在火影中文網看最新火影漫畫,可以關掉網頁內嵌的音頻文件聲音,非常貼心啊!
  • 谷歌Chrome正在工具欄上測試播放按鈕 可控制音樂和視頻播放進度
    站長之家(ChinaZ.com) 7月8日 消息:據theverge報導,谷歌似乎將很快在Chrome瀏覽器中新增一個播放按鈕。該按鈕將位於Chrome工具欄右側,允許用戶播放或暫停標籤中播放的視頻或音樂。據報導,該按鈕被稱為Global Media Controls,目前正在Chrome development瀏覽器Canary上進行測試。啟用該功能後,按鈕圖標將出現在網址欄位旁邊,並突出顯示正在播放的內容,如下圖:
  • YouTube推出自動播放服務 用戶瀏覽主頁時將自動播放無聲視頻
    YouTube推出自動播放服務 用戶瀏覽主頁時將自動播放無聲視頻 2018年12月05日 15:19作者:陳麗萍編輯:陳麗萍文章出處:泡泡網原創
  • Chrome 沒插件,香味少一半,用Chrome瀏覽器這些插件怎麼能沒有
    俗話說Chrome 沒插件,香味少一半,Chrome 最大的優勢還是其支持眾多強大好用的擴展程序,使用Chrome瀏覽器的最大魅力就是插件了,今天,小編就來一起盤點一下chrome上那些相見恨晚的擴展。
  • 微信怎麼開啟使用聽筒播放聲音
    微信語音如果沒有啟動聽筒播放模式的話,一般只能選擇揚聲器模式了。那麼,微信怎麼開啟使用聽筒播放聲音呢?讓我們一起來看看。1.打開手機上的微信軟體,登錄自己的帳號。  5.進入聊天頁面後,我們發現使用聽筒播放語音的開關按鈕是灰色的,表示處於關閉狀態。
  • Chrome OS 80穩定版發布
    Chrome OS 80的穩定版本已經發布,第80版的一項重要創新是更新的平板電腦界面,可以在以下「標誌」中啟用該界面:chrome://標誌/#webui-tab-stripchrome://標誌/#new-tabstrip-animationchrome://標誌/#scrollable-tabstrip同樣在平板電腦模式下,添加了幾個方便的手勢,這些手勢已在chrome中激活://標誌/#架子座位。
  • python+Chrome爬取動態異步生成的頁面內容
    直接用urllib.request爬取,發現得到的剛好沒有這些資源,怎麼回事?一、PhantomJS暫停開發二、新版本的Selenium不再支持PhantomJS我們可以用chrome的headless模式,當然對chrome有要求,版本需要>=60(windows)>=59(linux
  • Chrome的5大神級插件,讓你擁有全世界最好用的瀏覽器!
    但是,有很多人在使用了Chrome之後,覺得並沒有大家誇獎的那麼好用,覺得名不符實~而實際上,Chrome瀏覽器本身並不具有太多複雜的功能,但是配合chrome豐富的擴展插件,足以讓你的chrome擁有無限的可能,更能讓你擁有全世界最好用的瀏覽器!
  • iPhoneX動態壁紙怎麼自動播放 蘋果手機設置方法
    iPhoneX裡面不僅僅支持靜態壁紙,而且還可以支持動態壁紙,那麼怎麼進行自動播放動態壁紙呢?不知道的話小編就告訴你!  1、下載/更新最新版【騰訊手機管家】,進入【實用工具】裡選擇【視頻壁紙】。
  • DevTools(Chrome 85)的新功能
    // 每日前端夜話 第371篇// 正文共:2700 字// 預計閱讀時間:10 分鐘DevTools 是開發和測試 Web 應用時最有用的工具之一。在撰寫本文時(2020年7月30 日),你只能通過下載 Chrome 的開發版本[6]來獲得 Chrome 85。你可以在 Chrome 發行渠道[7] 頁面上了解有關 Chrome 發行版本的更多信息。CSS-in-JS 框架的樣式編輯通過即時編輯代碼或樣式來實時查看是 DevTools 最有用的功能之一。
  • 電腦沒聲音了如何恢復 電腦突然沒聲音的解決教程
    電腦作業系統在使用的過程可能會出現各種各樣的情況,例如近日有網友就反映說,自己的電腦在使用時突然出現了沒聲音了的情況,不知道怎麼回事也不清楚如何恢復,很是苦惱。那針對這一問題,今天小編就來為大家分享關於電腦突然沒聲音的解決教程。
  • Chrome(谷歌)瀏覽器使用必備的15款神級插件提高你的工作效率
    1、谷歌上網助手推薦理由:這款插件是chrome內核瀏覽器專用插件,它可以輕鬆解決chrome擴展無法自動更新的問題。另外,你還可以通過它,來訪問google搜索,gmail郵箱,google+等等,谷歌相關的產品。
  • 兩大瀏覽器將停止自動播放視頻,這對廣告行業有什麼影響?
    Google 的 Chrome 和蘋果的 Safari 瀏覽器都在對自動播放的廣告採取措施,這對廣告行業來說無疑是個威脅。自 10 月份開始的 63 版本更新中,Google 已經預告會先提供一個可完全靜音單一網頁的功能(這一點在 Safari 中也能實現),而自明年 1 月起的 64 版更新後,瀏覽器將主動偵測並停止那些突然自動播放發出聲音的視頻。這一做法破壞了不少存在已久的網絡廣告生態,首先自動播放的廣告能夠吸引更多用戶的注意,而即使廣告沒有被用戶認真觀看,實際播放的次數依然會被計費。
  • Win10正式版怎麼禁止強制自動更新系統?
    那麼Win10正式版怎麼禁止強制自動更新系統?一起來看看Win10正式版所提供的強制自動更新系統功能,在一定程度上會影響我們的正常上網速度,同時在重啟或關閉系統時都需要等待煩人的更新進度,尤其是第三方程序的更新可能會造成系統的不穩定性。而為了防止windows系統老是出現更新提示,我們會把自動更新設置為關閉自動更新。那麼Win10正式版怎麼禁止強制自動更新系統?