自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Chrome 66禁止聲音自動(dòng)播放之后

系統(tǒng) 瀏覽器
聲音無(wú)法自動(dòng)播放這個(gè)在IOS/Android上面一直是個(gè)慣例,桌面版的Safari在2017年的11版本也宣布禁掉帶有聲音的多媒體自動(dòng)播放功能,緊接著在2018年4月份發(fā)布的Chrome 66也正式關(guān)掉了聲音自動(dòng)播放。

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

最開始移動(dòng)端瀏覽器是完全禁止音視頻自動(dòng)播放的,考慮到了手機(jī)的帶寬以及對(duì)電池的消耗。但是后來(lái)又改了,因?yàn)闉g覽器廠商發(fā)現(xiàn)網(wǎng)頁(yè)開發(fā)人員可能會(huì)使用GIF動(dòng)態(tài)圖代替視頻實(shí)現(xiàn)自動(dòng)播放,正如IOS文檔所說,使用GIF的帶寬流量是Video(h264)格式的12倍,而播放性能消耗是2倍,所以這樣對(duì)用戶反而是不利的。又或者是使用Canvas進(jìn)行hack,如Android Chrome文檔提到。因此瀏覽器廠商放開了對(duì)多媒體自動(dòng)播放的限制,只要具備以下條件就能自動(dòng)播放:

[[229705]]

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

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

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

桌面版的瀏覽器在近期也使用了這個(gè)策略,如升級(jí)后的Safari 11的說明:

以及Chrome文檔的說明:

[[229706]]

這個(gè)策略無(wú)疑對(duì)視頻網(wǎng)站的沖擊***,如在Safari打開tudou的提示:

添加了一個(gè)設(shè)置向?qū)?。Chrome的禁止更加人性化,它有一個(gè)MEI的策略,這個(gè)策略大概是說只要用戶在當(dāng)前網(wǎng)頁(yè)主動(dòng)播放過超過7s的音視頻(視頻窗口不能小于200 x 140),就允許自動(dòng)播放。

對(duì)于網(wǎng)頁(yè)開發(fā)人員來(lái)說,應(yīng)當(dāng)如何有效地規(guī)避這個(gè)風(fēng)險(xiǎn)呢?

Chrome的文檔給了一個(gè)***實(shí)踐:先把音視頻加一個(gè)muted的屬性就可以自動(dòng)播放,然后再顯示一個(gè)聲音被關(guān)掉的按鈕,提示用戶點(diǎn)一下打開聲音。對(duì)于視頻來(lái)說,確實(shí)可以這樣處理,而對(duì)于音頻來(lái)說,很多人是監(jiān)聽頁(yè)面點(diǎn)擊事件,只要點(diǎn)一次了就開始播放聲音,一般就是播放個(gè)背景音樂。但是如果對(duì)于有多個(gè)聲音資源的頁(yè)面來(lái)說如何自動(dòng)播放多個(gè)聲音呢?

首先,如果用戶還沒進(jìn)行交互就調(diào)用播放聲音的API,Chrome會(huì)這么提示:

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

Safari會(huì)這么提示:

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

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

所以上面提到很多人是監(jiān)聽整個(gè)頁(yè)面的點(diǎn)擊事件進(jìn)行播放,不管點(diǎn)的哪里,只要點(diǎn)了就行,包括觸摸下滑。這種方法只適用于一個(gè)聲音資源,不適用多個(gè)聲音,多個(gè)聲音應(yīng)該怎么破呢?這里并不是說要和瀏覽器對(duì)著干,“逆天而行”,我們的目的還是為了提升用戶體驗(yàn),因?yàn)橛行﹫?chǎng)景如果能自動(dòng)播放確實(shí)比較好,如一些答題的場(chǎng)景,需要聽聲音進(jìn)行答題,如果用戶在答題的過程中能依次自動(dòng)播放相應(yīng)題目的聲音,確實(shí)比較方便。同時(shí)也是討論聲音播放的技術(shù)實(shí)現(xiàn)。

原生播放視頻應(yīng)該就只能使用video標(biāo)簽,而原生播放音頻除了使用audio標(biāo)簽之外,還有另外一個(gè)API叫AudioContext,它是能夠用來(lái)控制聲音播放并帶了很多豐富的操控接口。調(diào)audio.play必須在點(diǎn)擊事件里面響應(yīng),而使用AudioContext的區(qū)別在于只要用戶點(diǎn)過頁(yè)面任何一個(gè)地方之后就都能播放了。所以可以用AudioContext取代audio標(biāo)簽播放聲音。

我們先用audio.play檢測(cè)頁(yè)面是否支持自動(dòng)播放,以便決定我們播放的時(shí)機(jī)。

1. 頁(yè)面自動(dòng)播放檢測(cè)

方法很簡(jiǎn)單,就是創(chuàng)建一個(gè)audio元素,給它賦一個(gè)src,append到dom里面,然后調(diào)用它的play,看是否會(huì)拋異常,如果捕獲到異常則說明不支持,如下代碼所示:

  1. function testAutoPlay () { 
  2.     // 返回一個(gè)promise以告訴調(diào)用者檢測(cè)結(jié)果 
  3.     return new Promise(resolve => { 
  4.         let audio = document.createElement('audio'); 
  5.         // require一個(gè)本地文件,會(huì)變成base64格式 
  6.         audio.src = require('@/assets/empty-audio.mp3'); 
  7.         document.body.appendChild(audio); 
  8.         let autoplay = true
  9.         // play返回的是一個(gè)promise 
  10.         audio.play().then(() => { 
  11.             // 支持自動(dòng)播放 
  12.             autoplay = true
  13.         }).catch(err => { 
  14.             // 不支持自動(dòng)播放 
  15.             autoplay = false
  16.         }).finally(() => { 
  17.             audio.remove(); 
  18.             // 告訴調(diào)用者結(jié)果 
  19.             resolve(autoplay); 
  20.         }); 
  21.     }); 

這里使用一個(gè)空的音頻文件,它是一個(gè)時(shí)間長(zhǎng)度為0s的mp3文件,大小只有4kb,并且通過webpack打包成本地的base64格式,所以不用在canplay事件之后才調(diào)用play,直接寫成同步代碼,如果src是一個(gè)遠(yuǎn)程的url,那么就得監(jiān)聽canplay事件,然后在里面play.

在告訴調(diào)用者結(jié)果時(shí),使用Promise resolve的方式,因?yàn)閜lay的結(jié)果是異步的,并且?guī)旌瘮?shù)里面不推薦使用await。

2. 監(jiān)聽頁(yè)面交互點(diǎn)擊

如果當(dāng)前頁(yè)面能夠自動(dòng)播放,那么可以毫無(wú)顧忌地讓聲音自動(dòng)播放了,否則就得等到用戶開始和這個(gè)頁(yè)面交互了即有點(diǎn)擊操作了之后才能自動(dòng)播放,如下代碼所示:

  1. let audioInfo = { 
  2.     autoplay: false
  3.     testAutoPlay () { 
  4.         // 代碼同,略...  
  5.     }, 
  6.     // 監(jiān)聽頁(yè)面的點(diǎn)擊事件,一旦點(diǎn)過了就能autoplay了 
  7.     setAutoPlayWhenClick () { 
  8.         function setAutoPlay () { 
  9.             // 設(shè)置自動(dòng)播放為true 
  10.             audioInfo.autoplay = true
  11.             document.removeEventListener('click', setAutoPlay); 
  12.             document.removeEventListener('touchend', setAutoPlay); 
  13.         } 
  14.         document.addEventListener('click', setCallback); 
  15.         document.addEventListener('touchend', setCallback); 
  16.     }, 
  17.     init () { 
  18.         // 檢測(cè)是否能自動(dòng)播放 
  19.         audioInfo.testAutoPlay().then(autoplay => { 
  20.             if (!audioInfo.autoplay) { 
  21.                 audioInfo.autoplay = autoplay; 
  22.             } 
  23.         }); 
  24.         // 用戶點(diǎn)擊交互之后,設(shè)置成能自動(dòng)播放 
  25.         audioInfo.setAutoPlayWhenClick(); 
  26.     } 
  27. }; 
  28. audioInfo.init(); 
  29. export default audioInfo; 

上面代碼主要監(jiān)聽document的click事件,在click事件里面把a(bǔ)utoplay值置為true。換句話說,只要用戶點(diǎn)過了,我們就能隨時(shí)調(diào)AudioContext的播放API了,即使不是在點(diǎn)擊事件響應(yīng)函數(shù)里面,雖然無(wú)法在異步回調(diào)里面調(diào)用audio.play,但是AudioContext可以做到。

代碼***通過調(diào)用audioInfo.init,把能夠自動(dòng)播放的信息存儲(chǔ)在了audioInfo.autoplay這個(gè)變量里面。當(dāng)需要播放聲音的時(shí)候,例如切到了下一題,需要自動(dòng)播放當(dāng)前題的幾個(gè)音頻資源,就取這個(gè)變量判斷是否能自動(dòng)播放,如果能就播,不能就等用戶點(diǎn)聲音圖標(biāo)自己去播,并且如果他點(diǎn)過了一次之后就都能自動(dòng)播放了。

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

3. AudioContext播放聲音

先請(qǐng)求音頻文件,放到ArrayBuffer里面,然后用AudioContext的API進(jìn)行decode解碼,解碼完了再讓它去play,就行了。

我們先寫一個(gè)請(qǐng)求音頻文件的ajax:

  1. function request (url) { 
  2.     return new Promise (resolve => { 
  3.         let xhr = new XMLHttpRequest(); 
  4.         xhr.open('GET', url); 
  5.         // 這里需要設(shè)置xhr response的格式為arraybuffer 
  6.         // 否則默認(rèn)是二進(jìn)制的文本格式 
  7.         xhr.responseType = 'arraybuffer'
  8.         xhr.onreadystatechange = function () { 
  9.             // 請(qǐng)求完成,并且成功 
  10.             if (xhr.readyState === 4 && xhr.status === 200) { 
  11.                 resolve(xhr.response); 
  12.             } 
  13.         }; 
  14.         xhr.send(); 
  15.     }); 

這里需要注意的是要把xhr響應(yīng)類型改成arraybuffer,因?yàn)閐ecode需要使用這種存儲(chǔ)格式,這樣設(shè)置之后,xhr.response就是一個(gè)ArrayBuffer格式了。

接著實(shí)例化一個(gè)AudioContext,讓它去解碼然后play,如下代碼所示:

  1. // Safari是使用webkit前綴 
  2. let context = new (window.AudioContext || window.webkitAudioContext)(); 
  3. // 請(qǐng)求音頻數(shù)據(jù) 
  4. let audioMedia = await request(url); 
  5. // 進(jìn)行decode和play 
  6. context.decodeAudioData(audioMedia, decode => play(context, decode)); 

play的函數(shù)實(shí)現(xiàn)如下:

  1. function play (context, decodeBuffer) { 
  2.     let source = context.createBufferSource(); 
  3.     source.buffer = decodeBuffer; 
  4.     source.connect(context.destination); 
  5.     // 從0s開始播放 
  6.     source.start(0); 

這樣就實(shí)現(xiàn)了AudioContext播放音頻的基本功能。

如果當(dāng)前頁(yè)面是不能autoplay,那么在 new AudioContext的時(shí)候,Chrome控制臺(tái)會(huì)報(bào)一個(gè)警告:

這個(gè)的意思是說,用戶還沒有和頁(yè)面交互你就初始化了一個(gè)AudioContext,我是不會(huì)讓你play的,你需要在用戶點(diǎn)擊了之后resume恢復(fù)這個(gè)context才能夠進(jìn)行play.

假設(shè)我們不管這個(gè)警告,直接調(diào)用play沒有報(bào)錯(cuò),但是沒有聲音。所以這個(gè)時(shí)候就要用到上一步audioInfo.autoplay的信息,如果這個(gè)為true,那么可以play,否則不能play,需要讓用戶自己點(diǎn)聲音圖標(biāo)進(jìn)行播放。所以,把代碼重新組織一下:

  1. function play (context, decodeBuffer) { 
  2.     // 調(diào)用resume恢復(fù)播放 
  3.     context.resume(); 
  4.     let source = context.createBufferSource(); 
  5.     source.buffer = decodeBuffer; 
  6.     source.connect(context.destination); 
  7.     source.start(0); 
  8.   
  9. function playAudio (context, url) { 
  10.     let audioMedia = await request(url); 
  11.     context.decodeAudioData(audioMedia, decode => play(context, decode)); 
  12.   
  13. let context = new (window.AudioContext || window.webkitAudioContext)(); 
  14. // 如果能夠自動(dòng)播放 
  15. if (audioInfo.autoplay) { 
  16.     playAudio(url); 
  17. // 支持用戶點(diǎn)擊聲音圖標(biāo)自行播放 
  18. $('.audio-icon').on('click'function () { 
  19.     playAudio($(this).data('url')); 
  20. }); 

調(diào)了resume之后,如果之前有被禁止播放的音頻就會(huì)開始播放,如果沒有則直接恢復(fù)context的自動(dòng)播放功能。這樣就達(dá)到基本目的,如果支持自動(dòng)播放就在代碼里面直接play,不支持就等點(diǎn)擊。只要點(diǎn)了一次,不管點(diǎn)的哪里接下來(lái)的都能夠自動(dòng)播放了。就能實(shí)現(xiàn)類似于每隔3s自動(dòng)播下一題的音頻的目的:

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

這里還有一個(gè)問題,怎么知道每個(gè)聲音播完了,然后再隔個(gè)3s播放下一個(gè)聲音呢?可以通過兩個(gè)參數(shù),一個(gè)是解碼后的decodeBuffer有當(dāng)前音頻的時(shí)長(zhǎng)duration屬性,而通過context.currentTime可以知道當(dāng)前播放時(shí)間精度,然后就可以弄一個(gè)計(jì)時(shí)器,每隔100ms比較一下context.currentTime是否大于docode.duration,如果是的話說明播完了。soundjs這個(gè)庫(kù)就是這么實(shí)現(xiàn)的,我們可以利用這個(gè)庫(kù)以方便對(duì)聲音的操作。

這樣就實(shí)現(xiàn)了利用AudioContext自動(dòng)播放多個(gè)音頻的目的,限制是用戶***打開頁(yè)面是不能自動(dòng)播放的,但是一旦用戶點(diǎn)過頁(yè)面的任何一個(gè)地方就可以了。

AudioContext還有其它的一些操作。

4. AudioContext控制聲音屬性

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

[[229707]]

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

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

見這個(gè)codepen:Web Audio API: parametric equalizer.

***,一直以來(lái)都是只有移動(dòng)端的瀏覽器禁掉了音視頻的自動(dòng)播放,現(xiàn)在桌面版的瀏覽器也開始下手了。瀏覽器這樣做的目的在于,不想讓用戶打開一個(gè)頁(yè)面就各種廣告或者其它亂七八糟的聲音在播,營(yíng)造一個(gè)純靜的環(huán)境。但是瀏覽器也不是一刀切,至少允許音視頻靜音的播放。所以對(duì)于視頻來(lái)說,可以靜音自動(dòng)播放,然后加個(gè)聲音被關(guān)掉的圖標(biāo)讓用戶點(diǎn)擊打開,再加添加設(shè)置向?qū)е惖姆椒ㄒ龑?dǎo)用戶設(shè)置允許當(dāng)前網(wǎng)站自動(dòng)播放。而對(duì)于聲音可以用AudioContext的API,只要頁(yè)面被點(diǎn)過一次AudioContext就被激活了,就能直接在代碼里面控制播放了。

以上可作為當(dāng)前網(wǎng)頁(yè)多媒體播放的***實(shí)踐參考。

【本文是51CTO專欄作者“人人網(wǎng)FED”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過51CTO聯(lián)系原作者獲取授權(quán)】

戳這里,看該作者更多好文 

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專欄
相關(guān)推薦

2015-09-01 17:19:02

Chrome音頻

2013-11-15 14:16:49

ChromeChrome 32 B標(biāo)簽頁(yè)

2015-09-01 17:29:11

谷歌瀏覽器Flash

2017-03-27 09:00:09

Windows 7Windows自動(dòng)播放

2018-05-22 10:34:06

ChromeHTTPS自動(dòng)播放

2011-03-21 10:13:22

Ubuntu自動(dòng)播放

2022-08-26 12:13:08

APIjavascript視頻

2009-02-09 09:03:31

自動(dòng)播放病毒木馬

2022-03-06 20:02:21

監(jiān)聽視頻播放

2009-11-24 09:01:21

Windows 7自動(dòng)播放

2009-10-20 10:05:22

Windows 7策略組關(guān)閉自動(dòng)播放

2011-08-03 13:30:08

組策略自動(dòng)播放

2009-06-02 08:55:43

2010-01-13 08:46:21

Windows 7USB自動(dòng)播放

2021-08-30 07:49:32

Javascript西瓜視頻

2009-09-28 08:52:29

Windows 7自動(dòng)加速自動(dòng)播放

2021-07-26 05:32:47

微軟Edge瀏覽器

2011-08-23 09:47:20

UbuntuU盤

2021-02-04 08:19:47

微軟Edge瀏覽器

2012-12-27 14:29:38

Android開發(fā)流媒體
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)