那些H5用到的技術(2)——音頻和視頻播放


前言

正常情況,除了非常簡陋的小功能H5,音樂播放是必不可少的,對於帶有互動的小游戲,還必須有各種酷炫的音效,這就必須用到音頻使用了,而對於視頻,主要用到過場動畫和品牌廣告宣傳。

audio標簽

audio 標簽是 HTML 5 的新標簽。
其中controls屬性可以向用戶顯示控件,是瀏覽器自帶的樣式,否則audio是隱藏不顯示的,但是每個瀏覽器自帶樣式都不一樣,例如在chrome下樣子是這樣

功能也比較單一,所以一般要做播放器啥的就不會用自帶的樣式了…取而代之的是使用HTMLAudioElement DOM對象來進行操作,里面提供了許多方法事件給我們使用。它繼承自HTMLMediaElement
PS:對於查看dom對象的方法,除了查看官方文檔,也可以這樣(我經常這樣)
chrome瀏覽器里面,直接在debug窗口pause腳本,然后再監視窗口new dom元素,直接展開查看即可

Web Audio API

Web Audio API提供了一套強大&通用的音頻操控接口,可以允許用戶選擇音源,給音頻添加效果,添加聲場特效,可視化音頻等等。基本上可以玩轉音頻!
一般情況下用不到……所以有興趣的同學可以研究一下,以后我有機會研究過后也會補充。

自動播放的問題

正常PC客戶端的瀏覽器audio都能夠正常使用,不幸的是,在移動設備中大多數瀏覽器都屏蔽了autoplay屬性,也就是無法實現默認的自動播放。
原因當然也很簡單,移動設備流量被你這樣一播放就去了一大半了,當然要保護起來。必須是要用戶主動出發play方法才行。
下面是引自Safari Reference

In Safari on iPhone OS (for all devices, including iPad), where the
user may be on a cellular network and be charged per data unit,
autobuffering and autoplay are disabled. No data is loaded until the
user initiates it. This means the JavaScript play() and load() methods
are also inactive until the user initiates playback, unless the play()
method is triggered by user action.

但是產品說了,一定要給播放背景音樂!那怎么辦?
1、對於android的webview

 
 
 
         
  1. // 設置4.2以后版本支持autoPlay,非用戶手勢促發
  2. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
  3. webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
  4. }

2、對於手機瀏覽器
這是我個人的測試,在手機Safari/Chrome中,如果調用play,報錯audio.play can only be initiated by a user gesture.
所以,目前沒有什么太好的方案,除非欺騙用戶主動點擊屏幕,然后播放。

3、微信瀏覽器
注冊使用微信JS-SDK的ready事件(前提是需要有公眾號,好在目前JS-SDK的權限基本都開放了)
在事件內調用play方法是沒有任何問題的。

 
 
 
         
  1. wx.ready(function(){
  2. // config信息驗證后會執行ready方法,所有接口調用都必須在config接口獲得結果之后,config是一個客戶端的異步操作,所以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。
  3. audio.play();
  4. });

理論是大多H5都是在微信傳播的,所以首選方案3,其他就沒有考慮那么多了。

背景音樂的實現

看起來是這個樣子的,左上角一個音樂圖標在旋轉(很多h5都是這么干的)

默認情況,一進入便加載背景音樂,這種情況一般不用SoundJS,在wx.ready中調用方法即可

 
 
 
         
  1. wx.ready(function(){
  2. music_player = new Player($('#btn_play_music'), "./audio/107192078.m4a");
  3. });

如果需要在正常瀏覽器也能使用,判斷一下是否是瀏覽器即可

 
 
 
         
  1. //判斷是否是微信瀏覽器
  2. function is_weixin() {
  3. var ua = navigator.userAgent.toLowerCase();
  4. if (ua.match(/MicroMessenger/i) == "micromessenger") {
  5. return true;
  6. } else {
  7. return false;
  8. }
  9. }

核心代碼如下,具體可以見demo
CSS樣式

 
 
 
         
  1. /*音樂按鈕*/
  2. #wraper_music {
  3. position: fixed;
  4. width: 40px;
  5. height: 40px;
  6. top: 10px;
  7. right: 10px;
  8. z-index: 999;
  9. border-radius: 40px;
  10. display: block;
  11. }
  12. #btn_play_music {
  13. width: 40px;
  14. height: 40px;
  15. cursor: pointer;
  16. background-size: 40px 40px;
  17. }
  18. .c_btn_play_music_playing {
  19. background: url(./img/music_playing.png) no-repeat;
  20. -webkit-animation: kf_music_playing 8s linear infinite;
  21. animation: kf_music_playing 8s linear infinite;
  22. }
  23. .c_btn_play_music_paused {
  24. background: url(./img/music_stop.png) no-repeat;
  25. }
  26. @keyframes kf_music_playing {
  27. 0% {
  28. -webkit-transform: rotateZ(0deg);
  29. }
  30. 100% {
  31. -webkit-transform: rotateZ(360deg);
  32. }
  33. }
  34. @-webkit-keyframes kf_music_playing {
  35. 0% {
  36. -webkit-transform: rotateZ(0deg);
  37. }
  38. 100% {
  39. -webkit-transform: rotateZ(360deg);
  40. }
  41. }
  42. @media screen and (max-width: 380px) {
  43. #btn_qtcs {
  44. font-size: small;
  45. }
  46. #btn_toumiao {
  47. font-size: small;
  48. }
  49. }
  50. @media screen and (max-width: 320px) {
  51. #btn_qtcs {
  52. font-size: x-small;
  53. }
  54. #btn_toumiao {
  55. font-size: x-small;
  56. }
  57. }

播放器JS

 
 
 
         
  1. //---------start播放器代碼---------
  2. function Player(el, src) {
  3. this.el = el;
  4. this.isPlay = true;
  5. if (src == undefined) {
  6. src = '';
  7. }
  8. this.init(src);
  9. }
  10. Player.prototype = {
  11. init: function(src) {
  12. var _this = this,
  13. attr = {
  14. loop: true,
  15. preload: "auto",
  16. autoplay: true,
  17. src: src
  18. };
  19. this._audio = new Audio;
  20. for (var i in attr) {
  21. attr.hasOwnProperty(i) && i in this._audio && (this._audio[i] = attr[i]);
  22. }
  23. this.inited = false;
  24. $(this._audio).on('durationchange', function() {
  25. // 播放加載
  26. if (_this._audio.duration > 1) {
  27. _this.inited = true;
  28. }
  29. });
  30. $(this._audio).on('ended', function() {
  31. // 播放結束
  32. _this._audio.currentTime = 0;
  33. _this.isPlay = false;
  34. _this._play();
  35. });
  36. if (src != '') {
  37. this._audio.load();
  38. }
  39. if (typeof this.el !== 'string') {
  40. this.el.on('click', function() {
  41. _this._play();
  42. });
  43. }
  44. },
  45. _load: function() {
  46. this._audio.load();
  47. },
  48. _src: function(src) {
  49. this._audio['src'] = src;
  50. },
  51. _isplay: function() {
  52. return this.isPlay;
  53. },
  54. _play: function() {
  55. if (!this.isPlay) {
  56. this._audio.play();
  57. if (typeof this.el !== 'string') {
  58. this.el.removeClass('c_btn_play_music_paused');
  59. this.el.addClass('c_btn_play_music_playing');
  60. }
  61. } else {
  62. this._audio.pause();
  63. if (typeof this.el !== 'string') {
  64. this.el.removeClass('c_btn_play_music_playing');
  65. this.el.addClass('c_btn_play_music_paused');
  66. }
  67. }
  68. this.isPlay = !this.isPlay;
  69. },
  70. _playOn: function() {
  71. this._audio.play();
  72. if (typeof this.el !== 'string') {
  73. this.el.removeClass('c_btn_play_music_paused');
  74. this.el.addClass('c_btn_play_music_playing');
  75. }
  76. this.isPlay = true;
  77. },
  78. _playOff: function() {
  79. this._audio.pause();
  80. if (typeof this.el !== 'string') {
  81. this.el.removeClass('c_btn_play_music_playing');
  82. this.el.addClass('c_btn_play_music_paused');
  83. }
  84. this.isPlay = false;
  85. },
  86. _volume: function(num) {
  87. this._audio.volume = num;
  88. }
  89. }
  90. //---------end播放器代碼---------

立即播放的問題

默認情況下,播放個單個背景音樂,不用太在乎加載播放延遲的問題,但是H5互動小游戲就不行了,會影響用戶體驗。

上面可以知道,在微信中是可以主動加載音頻的,這時候new Audio(src)的音頻加載方法就可以適用了,可以做到多個音頻的加載,加載完成再在需要的時候播放。但實際測試手機過程中發現,即使已經加載完畢了,在代碼用audio標簽更換src路徑,還是會有一定的延遲(PC端沒有這個問題,可能是由於手機內存限制,又要從本地緩沖讀取加載一次),然而就算是建立N個audio標簽,然后用到的時候用該標簽播放,也沒有用,還是有延遲,特別是Safari中,本身就有300毫秒的延遲,這樣更加明顯了。當時候就沒有什么頭緒了,最后還是找到了神器SoundJS,在H5互動小游戲上,完美解決音頻的播放問題。

SoundJS

SoundJS為我們提供了一套具有較高兼容性的簡單API,來解決H5互動游戲時的音頻操控問題。
當然是配合PreloadJS的效果更棒哦!

 
 
 
         
  1. //建議使用資源清單,方便管理,如果資源較多,可以考慮獨立出json文件,腳本自動生成。
  2. var manifest = [
  3. {
  4. id: '告白氣球',
  5. src: './audio/107192078.m4a'
  6. }, {
  7. id: '一生有你',
  8. src: './audio/95484.m4a'
  9. },
  10. ]
  11. var queue = new createjs.LoadQueue();
  12. //SoundJS默認用的是復雜的Web Audio接口,這會導致加載音頻變慢,所以顯示注冊使用html的audio即可
  13. createjs.Sound.registerPlugins([createjs.HTMLAudioPlugin]);
  14. //如果需要加載音頻,需要注冊SoundJs插件
  15. queue.installPlugin(createjs.Sound);
  16. queue.on("complete", function() {
  17. createjs.Sound.play("告白氣球");
  18. }, this);
  19. queue.on("progress", function(event) {
  20. console.log("Progress:", queue.progress, event.progress);
  21. });
  22. //設置並列加載,否則每次只加載一個,太耗時
  23. queue.setMaxConnections(5);
  24. queue.maintainScriptOrder = true;
  25. queue.loadManifest(manifest);

具體的操作可以查閱官方文檔,由於當時項目進度比較趕,到目前我也還未研究具體的實現原理。有知道的同學麻煩留言解答一下…

video標簽

video標簽同樣繼承自HTMLVideoElement,所以用法和audio大庭相徑,具體參考官方文檔即可。

播放樣式的問題

對於PC客戶端,使用controls屬性,不同瀏覽器的樣式也是不一樣的,下面是chrome的樣式,如果不加controls屬性,就只能通過dom控制播放了。

悲劇的是!!手機上就又各種坑了……默認都是彈框全屏播放的。
對於android微信,是不能自動播放的,即使在wx.ready中調用代碼

播出完成竟然還有廣告!
而對於IOS微信,也沒廣告,默認在wx.ready中是可以自動播放的。

對於一般沒啥高要求的H5來說,是可以接受的,但其實用戶體驗確實都不太好,最好是能統一樣式,沒有其他任何廣告!
那怎么辦?感謝Avin同學分享了《視頻H5のVideo標簽在微信里的坑和技巧》,研究如此透徹,想必也是花了很多的時間。

最后還有一個方案,用canvas實時讀video畫圖,但這個太耗CPU,PC端是可以考慮使用的。

格式的問題

需要注意的是,格式支持的問題,如果格式不對,要嘛就無法播放,要嘛就白屏只有聲音,具體可以參考這里《Media formats supported by the HTML audio and video elements》。而且!MP4的編碼其實是有很多種的,不一定是mp4就可以用, 轉之前我在這個問題就郁悶了挺久,最終的方案就是使用格式工廠,編碼選擇H.264

總結

才第二篇,已經各種坑了,想想前端其實也不容易啊~!各種兼容調試!什么時候是個頭呢?自己也有些慚愧,很多內部原理其實也沒深入去了解,只是做到符合業務需求,不過目前前端混亂之治,也是挺頭疼,技術變化挺快,沒太多時間研究。

啥時候大家才能統一標准呢?這種感覺就好像是我買了個充電器,電源本來都是統一標准的東西,結果在我家能充電,在你家就不能充,我難道還要去研究你家為什么電源不能充電?然后再配合拆開我的充電器研究一下原理?或許還有公司再生產一個萬能充電器來適應各種私自改的標准?好像沒啥意義,我只是想沖個電(就像我只是想播放個音視頻,咋就不能同意效果呢?)…有些浪費生命吶,但是標准終究是標准,人家就是願意改來自己用也沒啥辦法……

下一篇將介紹另一個H5神器!!swiper.js!用它我們就可以實現酷炫的上下左右滑動展示特特效了!

demo地址:
https://github.com/leestar54/h5-demo/blob/master/audio%26video.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM