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