成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

音頻組件開(kāi)發(fā)實(shí)踐

qpwoeiru96 / 3119人閱讀

摘要:相關(guān)操作音頻的打斷音頻的打斷包括兩種情況組件重新上傳新的語(yǔ)音第一種情況,解綁相關(guān)事件,釋放內(nèi)存。當(dāng)瀏覽器預(yù)計(jì)能夠在不停下來(lái)進(jìn)行緩沖的情況下持續(xù)播放指定的音頻視頻時(shí),會(huì)發(fā)生事件。

我的blog原文鏈接

最近公司迭代的項(xiàng)目中,新增了音頻播放功能,填了不少音頻的坑,總結(jié)一下:

需求:

交互需求:

上傳:點(diǎn)擊按鈕上傳語(yǔ)音,返回文件id(上傳文件的范疇,本文不會(huì)闡述)

播放:點(diǎn)擊播放按鈕,異步獲取語(yǔ)音播放src,音頻載入之后播放

操作:語(yǔ)音支持播放、暫停、停止、進(jìn)度拖動(dòng)等操作

當(dāng)然還有一些隱性需求:

一個(gè)界面可能存在多個(gè)播放文件

隨時(shí)播放一個(gè)語(yǔ)音,其它語(yǔ)音應(yīng)當(dāng)暫停

播放過(guò)程中,用戶重新上傳新的語(yǔ)音,此時(shí)播放應(yīng)停止

實(shí)現(xiàn)效果如下圖所示:(當(dāng)然,這只是項(xiàng)目用到的一部分,項(xiàng)目中還有其他頁(yè)面也用到了這個(gè)組件,那么就更考驗(yàn)組件的健壯性和可拓展性了。)

按需實(shí)現(xiàn) 一個(gè)界面可能存在多個(gè)播放文件

我們對(duì)音頻的操作,通常是先獲取這個(gè)音頻 DOM Element,通過(guò)對(duì)它的操作,實(shí)現(xiàn)想要達(dá)到的效果,如果你只是設(shè)定一個(gè)audio這樣單薄的ref名稱,恐怕會(huì)有些問(wèn)題,因此我給每個(gè)音頻設(shè)定了一個(gè)唯一的ref名稱。


暫停其他語(yǔ)音

注意到上面的代碼,我在給 audio 添加屬性的時(shí)候,多添加了一個(gè) data-key的屬性,那就是為了暫停其他語(yǔ)音而使用的,作為我要操作頁(yè)面其他音頻而設(shè)置的標(biāo)識(shí):

// 暫停其他語(yǔ)音的方法
pauseOthers (except) {
  var audios = document.querySelectorAll("audio")
  ;[].forEach.call(audios, audio => {
    if (audio.dataset["key"] !== except.uniqueId) {
      audio.pause()
    }
  })
}
// 調(diào)用的時(shí)候
this.pauseOthers (this)
異步獲取語(yǔ)音src,音頻載入之后播放

我想這就是項(xiàng)目坑點(diǎn)之一,因?yàn)橐纛lsrc并不是上傳語(yǔ)音就返回的,上傳語(yǔ)音只返回了語(yǔ)音id,我們需要通過(guò)id再去異步請(qǐng)求一次,才能獲取到src。

基于這樣的前提,播放操作做了兩點(diǎn)考慮(單例模式思維):

為什么點(diǎn)擊播放再獲取語(yǔ)音src?雖然也可以進(jìn)入界面就請(qǐng)求src,但是如果用戶不點(diǎn)擊播放,就白白浪費(fèi)了不需要的請(qǐng)求,基于性能的考慮,決定點(diǎn)擊播放后再進(jìn)行請(qǐng)求。

并不需要每次點(diǎn)擊都重新請(qǐng)求一次,只有未獲取過(guò)src的音頻需要重新請(qǐng)求。

具體實(shí)現(xiàn):

播放按鈕綁定togglePlay()事件

判斷audioSrc是否有值

如果有值,直接進(jìn)行播放,綁定相關(guān)事件,暫停其他語(yǔ)音

如果沒(méi)有值,設(shè)置loading并進(jìn)行異步請(qǐng)求,將返回結(jié)果賦值給audioSrc,監(jiān)聽(tīng)音頻 canplay

監(jiān)聽(tīng)音頻 canplay (這邊有一個(gè)坑點(diǎn),后面會(huì)提到)
在canplay的回調(diào)中,loading結(jié)束,綁定相關(guān)事件,暫停其他語(yǔ)音

為什么相關(guān)事件的綁定放在 canplay 中? 不然你可能會(huì)出現(xiàn)下面的報(bào)錯(cuò):

Uncaught (in promise) DOMException: The element has no supported sources.

所以,答應(yīng)我,基于audio播放的 事件 或是 屬性 ,都放在 canplay 的回調(diào)之后。

相關(guān)事件包括(本組件中):

監(jiān)聽(tīng)事件 timeupdate : 控制進(jìn)度條展示

監(jiān)聽(tīng)事件 pause : 監(jiān)聽(tīng)按鈕 播放/暫停 樣式

設(shè)置屬性 currentTime : 控制進(jìn)度拖動(dòng)或者停止語(yǔ)音

監(jiān)聽(tīng)事件 error : 監(jiān)聽(tīng)播放錯(cuò)誤

音頻的操作 播放與暫停

按鈕的樣式通過(guò)設(shè)置一個(gè)變量作為狀態(tài)值,paly()pause() 的時(shí)候分別改變狀態(tài)值。

其它具體邏輯上文描述比較清楚,不再贅述。

停止、進(jìn)度拖動(dòng)

停止:暫停音頻,并將currentTime設(shè)置為0

進(jìn)度拖動(dòng):根據(jù)拖動(dòng)位置計(jì)算currentTime值,并設(shè)置currentTime

兩個(gè)操作都涉及到了currentTime的設(shè)置,我們?cè)谶@里遇到了兩個(gè)坑:

設(shè)置currentTime無(wú)效
查詢資料后發(fā)現(xiàn)是后端的鍋,具體解決辦法鏈接在這里:HTML5 audio ,在chrome中設(shè)置currentTime無(wú)效

設(shè)置currentTime繼續(xù)播放

一開(kāi)始仍然以為是后端的鍋,因?yàn)楫?dāng)我靜態(tài)設(shè)置一個(gè) audioSrc 的時(shí)候,是沒(méi)有問(wèn)題的,但是當(dāng)我動(dòng)態(tài)設(shè)置,就會(huì)出現(xiàn)這樣的問(wèn)題:無(wú)論我是播放狀態(tài)還是暫停狀態(tài),設(shè)置到相對(duì)應(yīng)的currentTime都會(huì)繼續(xù)播放。

通過(guò)排查,發(fā)現(xiàn)當(dāng)我設(shè)置currentTime會(huì)再次觸發(fā)一次 canplay事件, canplay 的回調(diào)是綁定播放的相關(guān)操作,因此會(huì)繼續(xù)播放。

解決辦法,溫習(xí)了一遍addEventListener的語(yǔ)法,綁定canplay事件最多只調(diào)用一次。

this.audioElement.addEventListener("canplay", () => {
      // ...相關(guān)操作
}, {
  once: true
})

音頻的打斷

音頻的打斷包括兩種情況:

組件 destroyed

重新上傳新的語(yǔ)音

第一種情況,解綁相關(guān)事件,釋放內(nèi)存。

第二種情況,具體描述一下:

當(dāng)用戶重新上傳新的語(yǔ)音,不論此時(shí)語(yǔ)音暫停還是播放狀態(tài),都應(yīng)該停止。

我們通過(guò) watch 監(jiān)聽(tīng) id (上傳返回來(lái)的音頻id),當(dāng)id變化的時(shí)候,將 audioSrc 清空,以免播放舊的音頻內(nèi)容。

然而,僅僅這樣是不夠的,如果監(jiān)聽(tīng) error 事件,就會(huì)發(fā)現(xiàn)報(bào)錯(cuò),解決的辦法還是解綁相關(guān)事件,即,我們?cè)?canplay 回調(diào)中的綁定的相關(guān)事件,讓audio恢復(fù)初始狀態(tài),等到下一次播放的時(shí)候,需要重新請(qǐng)求新的src,回到上面播放的部分。

拓展

在解決問(wèn)題的過(guò)程中,也查詢到了一些實(shí)用的知識(shí)點(diǎn),雖然在應(yīng)用中沒(méi)有運(yùn)用到,但是這些知識(shí)點(diǎn)看起來(lái)似乎挺有用的,為了下次遇到其他坑能快速找到解決辦法,先把這些知識(shí)點(diǎn)記錄下來(lái)。

canplaycanplaythrough 辨析

當(dāng)瀏覽器能夠開(kāi)始播放指定的音頻/視頻時(shí),會(huì)發(fā)生 canplay 事件。

當(dāng)瀏覽器預(yù)計(jì)能夠在不停下來(lái)進(jìn)行緩沖的情況下持續(xù)播放指定的音頻/視頻時(shí),會(huì)發(fā)生 canplaythrough 事件。

了解其他媒體相關(guān)事件

HTMLMediaElement.play() 返回 Promise

play()返回一個(gè) Promise,如果播放成功,Promise狀態(tài)變成fulfilled,如果播放失敗,狀態(tài)變?yōu)?b>rejected并提供錯(cuò)誤信息。

var playPromise = document.querySelector("video").play();

// In browsers that don’t yet support this functionality,
// playPromise won’t be defined.
if (playPromise !== undefined) {
  playPromise.then(function() {
    // Automatic playback started!
  }).catch(function(error) {
    // Automatic playback failed.
    // Show a UI element to let the user manually start playback.
  });
}
video 412錯(cuò)誤
412 一般是因?yàn)榉?wù)器的 If-Unmodified-Since 或 If-None-Match 未實(shí)現(xiàn)
// 解決辦法
media.addEventListener("error", function (e) {
   var date = new Date();
   var milliSecs = date.getMilliseconds();
   var curr_src = $(media[0]).attr("src");
   var curr_src_arr = curr_src.split("?");
   var new_src = curr_src_arr[0]+"?t="+milliSecs;

   $(media[0]).attr("src",new_src);
   $(media[0]).find("source").attr("src",new_src);
   media[0].load();
}, false);

暫時(shí)完。

后續(xù)如果測(cè)試妹妹發(fā)現(xiàn)了什么bug,我會(huì)繼續(xù)填坑記錄滴。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/97798.html

相關(guān)文章

  • 如何在瀏覽器中播放pcm音頻

    摘要:格式文件中不包含頭部信息,播放器無(wú)法知道采樣率,聲道數(shù),采樣位數(shù),音頻數(shù)據(jù)大小等信息,導(dǎo)致無(wú)法播放。 本文記錄一點(diǎn)工作經(jīng)歷,探討音頻文件的格式更多訪問(wèn)我的博客 前言 最近在整理音視頻編程的知識(shí),回憶起半年多,有一次需求是在后臺(tái)播放某來(lái)源的 pcm 文件,當(dāng)時(shí)處理方法用了點(diǎn)技巧,記錄下來(lái) 背景:業(yè)務(wù)需求,在web后臺(tái)里播放 pcm 文件,文件不大(約300KB,已知 pcm 的參數(shù)采樣...

    zxhaaa 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<