摘要:相關(guān)操作音頻的打斷音頻的打斷包括兩種情況組件重新上傳新的語音第一種情況,解綁相關(guān)事件,釋放內(nèi)存。當(dāng)瀏覽器預(yù)計(jì)能夠在不停下來進(jìn)行緩沖的情況下持續(xù)播放指定的音頻視頻時,會發(fā)生事件。
我的blog原文鏈接
最近公司迭代的項(xiàng)目中,新增了音頻播放功能,填了不少音頻的坑,總結(jié)一下:
需求:交互需求:
上傳:點(diǎn)擊按鈕上傳語音,返回文件id(上傳文件的范疇,本文不會闡述)
播放:點(diǎn)擊播放按鈕,異步獲取語音播放src,音頻載入之后播放
操作:語音支持播放、暫停、停止、進(jìn)度拖動等操作
當(dāng)然還有一些隱性需求:
一個界面可能存在多個播放文件
隨時播放一個語音,其它語音應(yīng)當(dāng)暫停
播放過程中,用戶重新上傳新的語音,此時播放應(yīng)停止
實(shí)現(xiàn)效果如下圖所示:(當(dāng)然,這只是項(xiàng)目用到的一部分,項(xiàng)目中還有其他頁面也用到了這個組件,那么就更考驗(yàn)組件的健壯性和可拓展性了。)
按需實(shí)現(xiàn) 一個界面可能存在多個播放文件我們對音頻的操作,通常是先獲取這個音頻 DOM Element,通過對它的操作,實(shí)現(xiàn)想要達(dá)到的效果,如果你只是設(shè)定一個audio這樣單薄的ref名稱,恐怕會有些問題,因此我給每個音頻設(shè)定了一個唯一的ref名稱。
暫停其他語音
注意到上面的代碼,我在給 audio 添加屬性的時候,多添加了一個 data-key的屬性,那就是為了暫停其他語音而使用的,作為我要操作頁面其他音頻而設(shè)置的標(biāo)識:
// 暫停其他語音的方法 pauseOthers (except) { var audios = document.querySelectorAll("audio") ;[].forEach.call(audios, audio => { if (audio.dataset["key"] !== except.uniqueId) { audio.pause() } }) } // 調(diào)用的時候 this.pauseOthers (this)異步獲取語音src,音頻載入之后播放
我想這就是項(xiàng)目坑點(diǎn)之一,因?yàn)橐纛lsrc并不是上傳語音就返回的,上傳語音只返回了語音id,我們需要通過id再去異步請求一次,才能獲取到src。
基于這樣的前提,播放操作做了兩點(diǎn)考慮(單例模式思維):
為什么點(diǎn)擊播放再獲取語音src?雖然也可以進(jìn)入界面就請求src,但是如果用戶不點(diǎn)擊播放,就白白浪費(fèi)了不需要的請求,基于性能的考慮,決定點(diǎn)擊播放后再進(jìn)行請求。
并不需要每次點(diǎn)擊都重新請求一次,只有未獲取過src的音頻需要重新請求。
具體實(shí)現(xiàn):
播放按鈕綁定togglePlay()事件
判斷audioSrc是否有值
如果有值,直接進(jìn)行播放,綁定相關(guān)事件,暫停其他語音
如果沒有值,設(shè)置loading并進(jìn)行異步請求,將返回結(jié)果賦值給audioSrc,監(jiān)聽音頻 canplay
監(jiān)聽音頻 canplay (這邊有一個坑點(diǎn),后面會提到)
在canplay的回調(diào)中,loading結(jié)束,綁定相關(guān)事件,暫停其他語音
為什么相關(guān)事件的綁定放在 canplay 中? 不然你可能會出現(xiàn)下面的報(bào)錯:
Uncaught (in promise) DOMException: The element has no supported sources.
所以,答應(yīng)我,基于audio播放的 事件 或是 屬性 ,都放在 canplay 的回調(diào)之后。
相關(guān)事件包括(本組件中):
監(jiān)聽事件 timeupdate : 控制進(jìn)度條展示
監(jiān)聽事件 pause : 監(jiān)聽按鈕 播放/暫停 樣式
設(shè)置屬性 currentTime : 控制進(jìn)度拖動或者停止語音
監(jiān)聽事件 error : 監(jiān)聽播放錯誤
音頻的操作 播放與暫停按鈕的樣式通過設(shè)置一個變量作為狀態(tài)值,paly() 和 pause() 的時候分別改變狀態(tài)值。
其它具體邏輯上文描述比較清楚,不再贅述。
停止、進(jìn)度拖動停止:暫停音頻,并將currentTime設(shè)置為0
進(jìn)度拖動:根據(jù)拖動位置計(jì)算currentTime值,并設(shè)置currentTime
兩個操作都涉及到了currentTime的設(shè)置,我們在這里遇到了兩個坑:
設(shè)置currentTime無效
查詢資料后發(fā)現(xiàn)是后端的鍋,具體解決辦法鏈接在這里:HTML5 audio ,在chrome中設(shè)置currentTime無效
設(shè)置currentTime繼續(xù)播放
一開始仍然以為是后端的鍋,因?yàn)楫?dāng)我靜態(tài)設(shè)置一個 audioSrc 的時候,是沒有問題的,但是當(dāng)我動態(tài)設(shè)置,就會出現(xiàn)這樣的問題:無論我是播放狀態(tài)還是暫停狀態(tài),設(shè)置到相對應(yīng)的currentTime都會繼續(xù)播放。
通過排查,發(fā)現(xiàn)當(dāng)我設(shè)置currentTime會再次觸發(fā)一次 canplay事件, canplay 的回調(diào)是綁定播放的相關(guān)操作,因此會繼續(xù)播放。
解決辦法,溫習(xí)了一遍addEventListener的語法,綁定canplay事件最多只調(diào)用一次。
this.audioElement.addEventListener("canplay", () => { // ...相關(guān)操作 }, { once: true })音頻的打斷
音頻的打斷包括兩種情況:
組件 destroyed
重新上傳新的語音
第一種情況,解綁相關(guān)事件,釋放內(nèi)存。
第二種情況,具體描述一下:
當(dāng)用戶重新上傳新的語音,不論此時語音暫停還是播放狀態(tài),都應(yīng)該停止。
我們通過 watch 監(jiān)聽 id (上傳返回來的音頻id),當(dāng)id變化的時候,將 audioSrc 清空,以免播放舊的音頻內(nèi)容。
然而,僅僅這樣是不夠的,如果監(jiān)聽 error 事件,就會發(fā)現(xiàn)報(bào)錯,解決的辦法還是解綁相關(guān)事件,即,我們在 canplay 回調(diào)中的綁定的相關(guān)事件,讓audio恢復(fù)初始狀態(tài),等到下一次播放的時候,需要重新請求新的src,回到上面播放的部分。
拓展在解決問題的過程中,也查詢到了一些實(shí)用的知識點(diǎn),雖然在應(yīng)用中沒有運(yùn)用到,但是這些知識點(diǎn)看起來似乎挺有用的,為了下次遇到其他坑能快速找到解決辦法,先把這些知識點(diǎn)記錄下來。
canplay 與 canplaythrough 辨析當(dāng)瀏覽器能夠開始播放指定的音頻/視頻時,會發(fā)生 canplay 事件。
當(dāng)瀏覽器預(yù)計(jì)能夠在不停下來進(jìn)行緩沖的情況下持續(xù)播放指定的音頻/視頻時,會發(fā)生 canplaythrough 事件。
了解其他媒體相關(guān)事件
HTMLMediaElement.play() 返回 Promise或 的 play()返回一個 Promise,如果播放成功,Promise狀態(tài)變成fulfilled,如果播放失敗,狀態(tài)變?yōu)?b>rejected并提供錯誤信息。
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錯誤
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);
暫時完。
后續(xù)如果測試妹妹發(fā)現(xiàn)了什么bug,我會繼續(xù)填坑記錄滴。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/97798.html
摘要:格式文件中不包含頭部信息,播放器無法知道采樣率,聲道數(shù),采樣位數(shù),音頻數(shù)據(jù)大小等信息,導(dǎo)致無法播放。 本文記錄一點(diǎn)工作經(jīng)歷,探討音頻文件的格式更多訪問我的博客 前言 最近在整理音視頻編程的知識,回憶起半年多,有一次需求是在后臺播放某來源的 pcm 文件,當(dāng)時處理方法用了點(diǎn)技巧,記錄下來 背景:業(yè)務(wù)需求,在web后臺里播放 pcm 文件,文件不大(約300KB,已知 pcm 的參數(shù)采樣...
閱讀 1101·2021-10-11 10:59
閱讀 3685·2021-09-26 09:55
閱讀 946·2019-08-30 15:55
閱讀 2713·2019-08-30 15:44
閱讀 486·2019-08-30 14:06
閱讀 775·2019-08-30 11:26
閱讀 3404·2019-08-30 10:49
閱讀 2621·2019-08-29 12:53