整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          H5播放器源碼解讀 (video.js)

          在前面

          現在視頻業務越來越流行了,播放器也比較多,作為前端工程師如何打造一個屬于自己的播放器呢?最快最有效的方式是基于開源播放器深度定制,至于選擇哪個開源播放器仁者見仁智者見智,可以參考開源播放器列表(https://www.awesomes.cn/subject/videos)選擇適合自己業務的播放器。

          我們的播放器選擇了排名第一的video.js播放器,截至目前該播放器在Github擁有13,991 star, 4,059 fork,流行程度可見一斑。為了讓大家更多的了解它,我們細數下優點:

          1. 免費開源

            這個意味著什么就不多說了,附上項目地址(https://github.com/videojs/video.js)

          2. 兼容主流瀏覽器

            在國內的前端開發環境往往需要支持到低級版本的IE瀏覽器,然后隨著Flash的退化,很多公司沒有配備Flash開發工程師,video.js提供了流暢的Flash播放器,而且在UI層面做到了和video的效果,實屬難得,比如全屏。

          3. UI自定義

            不管開源項目在UI方面做的如何漂亮,對于各具特色的業務來說都要自定義UI,一個方便簡單的自定義方式顯得格外重要,更何況它還自帶了編譯工具,只能用一個”贊“字形容。具體怎么實現的,這里先簡單描述下是使用JavaScript(es6)構建對象,通過Less編寫樣式規則,最后借助Grunt編譯。

          4. 靈活插件機制

            video.js提供一個插件定義的接口,使插件開發簡單易行。而且社區論壇也提供了一些好用的插件供開發者使用。附插件列表

          5. 比較完善的文檔

            完善的文檔對于一個穩定的開源項目是多么的重要,video.js提供了教程、API文檔、插件、示例、論壇等。官方地址

          6. 項目熱度

            開源作者對項目的維護比較積極,提出的問題也能很快給予響應。開發者在使用過程中出現問題算是有一定保障。

          書歸正傳,要想更自由的駕馭video.js,必然要了解內部原理。本文的宗旨就是通過核心代碼演示講解源碼運行機制,如果有興趣,不要離開,我們馬上開始了……

          組織結構

          由于源碼量較大,很多同學不知道從何入手,我們先來說下它的組織結構。

          其中control-bar,menu,popup,slider,tech,tracks,utils是目錄,其他是文件。video.js是個非常優秀的面向對象的典型,所有的UI都是通過JavaScript對象組織實現的。

          video.js是個入口文件,看源碼可以從這個文件開始。

          setup.js處理播放器的配置安裝即data-setup屬性。

          poster-image.js處理播放器貼片。

          plugins.js實現了插件機制。

          player.js構造了播放器類也是video.js的核心。

          modal-dialog.js處理彈層相關。

          media-error.js定義了各種錯誤描述,如果想理解video.js對各語言的支持,這個文件是必看的,它是橋梁。

          loading-spinner.js實現了播放器加載的標志,如果不喜歡默認加載圖標在這里修改吧。

          fullscreen-api.js實現各個瀏覽器的全屏方案。

          extend.js是對node 繼承 and babel's 繼承的整合。

          event-target.js 是event類和原生事件的兼容處理。

          error-display.js 主要處理展示錯誤的樣式設置。

          component.js 是video.js框架中最重要的類,是所有類的基類,也是實現組件化的基石。

          close-button.js 是對關閉按鈕的封裝,功能比較單一。

          clickable-component.js 如果想實現一個支持點擊事件和鍵盤事件具備交互功能的組件可以繼承該類,它幫你做了細致的處理。

          button.js 如果想實現一個按鈕了解下這個類是必要的。

          big-play-button.js 這個按鈕是視頻還未播放時顯示的按鈕,官方將此按鈕放置在播放器左上角。

          utils目錄顧名思義是一些常用的功能性類和函數。

          tracks目錄處理的是音軌、字幕之類的功能。

          tech目錄也是非常核心的類,包括對video封裝、flash的支持。

          slider目錄主要是UI層面可拖動組件的實現,如進度條,音量條都是繼承的此類。

          popup目錄包含了對彈層相關的類。

          menu目錄包含了對菜單UI的實現。

          control-bar目錄是非常核心的UI類的集合了,播放器下方的控制器都在此目錄中。

          通過對組織結構的描述,大家可以,想了解video.js的哪一部分內容可以快速入手。如果還想更深入的了解如何正確使用這些類,請繼續閱讀繼承關系一節。

          繼承關系

          video.js是JavaScript面向對象實現很經典的案例,你一定會好奇在頁面上一個DOM節點加上data-setup屬性簡單配置就能生成一個復雜的播放器,然而在代碼中看不到對應的HTML”模板“。其實這都要歸功于”繼承“關系以及作者巧妙的構思。

          在組織結構一節有提到,所有類的基類都是Component類,在基類中有個createEl方法這個就是JavaScript對象和DOM進行關聯的方法。在具體的類中也可以重寫該方法自定義DOM內容,然后父類和子類的DOM關系也因JavaScript對象的繼承關系被組織起來。

          為了方便大家查閱video.js所有的繼承關系,整理了兩個圖表,一個是完整版,一個是核心版。

          • 完整版

          • 核心板

          運行機制

          video.js源碼代碼量比較大,我們要了解它的運行機制,首先確定它的主線是video.js文件的videojs方法,videojs方法調用player.js的Player類,Player繼承component.js文件的Component類,最后播放器成功運行。

          我們來看下videojs方法的代碼、Player的構造函數、Component的構造函數,通過對代碼的講解基本整個運行機制就有了基本的了解,注意里面用到的所有方法和其他類對象參照組織結構一節細細閱讀就可以掌握更多的運行細節。

          • videojs方法

          • Player的構造函數

          • Component的構造函數

          這里通過主線把基本的流程演示一下,輪廓出來了,更多細節還請繼續閱讀。

          插件機制

          一個完善和強大的框架都會繼承插件運行功能,給更多的開發者參與開發的機會進而實現框架功能的補充和延伸。我們來看下video.js的插件是如何運作的。

          • 插件的定義

          如果之前用過video.js插件的同學或者看過插件源碼,一定有看到有這句話videojs.plugin= pluginName,我們來看下源碼:

          不難看出,原理就是將插件(函數)掛載到Player對象的原型上,接下來看下是怎么執行的。

          • 插件的運行

          在Player的構造函數里判斷是否有插件這個配置,如果有則遍歷執行。

          UI"繼承"的原理

          在繼承關系一節中有提到video.js的所有DOM生成都不是采用的傳統模板的方式,都是通過JavaScript對象的繼承關系來實現的。

          在Component基類中有個createEl方法,在這里可以使用DOM類生成DOM實例。每個UI類都會有一個el屬性,會在實例化的時候自動生成,源代碼在Component的構造函數中:

          每個UI類有一個children屬性,用于添加子類,子類有可能扔具有children屬性,以此類推,播放器的DOM結構就是通過這樣的JavaScript對象結構實現的。

          在Player的構造函數里有一句代碼this.initChildren();啟動了UI的實例化。這個方法是在Component基類中定義的,我們來看下:

          通過這段代碼不難看出大概的意思是通過initChildren獲取children屬性,然后遍歷通過addChild將子類實例化,實例化的過程會自動重復上述過程從而達到了”繼承“的效果。不得不為作者的構思點贊。如果你要問并沒看到DOM是怎么關聯起來的,請繼續看addChild方法的源碼:

          這段代碼的大意就是提取子類的名稱,然后獲取類并實例化,最后通過最關鍵的一句話this.contentEl().insertBefore(component.el(), refNode);完成了父類和子類的DOM關聯。相信inserBefore大家并不陌生吧,原生的DOM操作方法。

          總結

          至此,video.js的精華部分都描述完了,不知道大家是否有收獲。這里簡單的總結一些閱讀video.js框架源碼的心得:

          1. 找準播放器實現的主線流程,方便我們有條理的閱讀代碼

          2. 了解框架代碼的組織結構,有的放矢的研究相關功能的代碼

          3. 理解類與類的繼承關系,方便自己構造插件或者修改源碼的時候知道從哪個類繼承

          4. 理解播放器的運行原理,有利于基于Component構造一個新類的實現

          5. 理解插件的運行機制,學會自己構造插件還是有必要的

          6. 理解UI的實現原理,就知道自己如何為播放器添加視覺層面的東西了

          7. 看看我的源碼解讀吧,能幫一點是一點,哈哈

          所周知,16年無疑是直播行業的春天,同時也是H5的一次高潮。

          so,到現在用H5技術在移動端做網頁直播也是見怪不怪了,但是!!!

          今天我們的主角是webApp下播放視頻

          參考文獻:

          1)HTML5+CSS3+JQuery打造自定義視頻播放器

          簡介

          HTML5的<video>標簽已經被目前大多數主流瀏覽器所支持,包括還未正式發布的IE9也聲明將支持<video>標簽,利用瀏覽器原生特性嵌入視頻有很多好處,所以很多開發者想盡快用上,但是真正使用前還有些問題要考慮,尤其是 Opera/Firefox 和IE/Safari瀏覽器所支持的視頻編碼不同的問題,Google幾個月前發布的開源視頻編碼VP8有望能解決這一問題,另外Google還發布了開放網絡媒體項目WebM,旨在幫助開發者為開放網絡制作出世界級媒體格式,Opera, Firefox, Chrome和IE9都將支持VP8,而且Flash Player也將可以播放VP8,這就意味著我們很快就可以只制作一個版本的視頻然后在所有主流瀏覽器上播放了。另外一個主要的問題就是如何構建自定義的HTML5<video>播放器,這是目前Flash Player的優勢所在,利用Flash的IDE所提供的接口可以很方便的構建一個個性化的視頻播放器,那HTML5的<video>標簽要怎樣才能實現呢?這個問題就是本文所要解決的!我們將開發一個HTML5<video>視頻播放器的jQuery插件,并且可以很方便的進行自定義,將分為以下幾個部分:

          1.視頻控制工具條

          2.視頻控制按鈕

          3.打包成jQuery插件

          4.外觀和體驗

          5.自定義皮膚

          視頻控制工具條

          做為一個專業的web開發人員,我們創建一個視頻播放器時一定希望它的外觀在各個瀏覽器中看起來一致(consistent),但是通過下面的圖可以看到目前各個瀏覽器提供的視頻控制工具條外觀各不相同:

          那就沒辦法了,我們得自己從頭來創建這個控制工具條,利用HTML和CSS再加上一些圖片實現起來并不算很難,另外通過HTML5多媒體元素提供的API我們可以很方便將創建的任何按鈕與播放/暫停等事件進行綁定。

          視頻控制按鈕

          基本的視頻控制工具條要包含一個播放/暫停按鈕,一個進度條,一個計時器和一個音量控制按鈕,我們將這些按鈕放在<video>元素下面,并用一個div作為父容器:

          Java代碼

          1. <div class="ghinda-video-controls">

          2. <a class="ghinda-video-play" title="Play/Pause"></a>

          3. <div class="ghinda-video-seek"></div>

          4. <div class="ghinda-video-timer">00:00</div>

          5. <div class="ghinda-volume-box">

          6. <div class="ghinda-volume-slider"></div>

          7. <a class="ghinda-volume-button" title="Mute/Unmute"></a>

          8. </div>

          9. </div>

          復制代碼

          注意,我們使用元素的class屬性來代替ID屬性是為了方便在一個頁面上使用多個播放器。

          打包成jQuery插件

          創建好控制按鈕后我們需要配合多媒體元素的API來實現視頻控制的目的,正如前面提到的一樣我們將我們的播放器打包成jQuery插件,這樣可以很好的實現復用,代碼如下:

          Java代碼

          1. $.fn.gVideo = function(options) {

          2. // build main options before element iteration

          3. var defaults = {

          4. theme: 'simpledark',

          5. childtheme: ''

          6. };

          7. var options = $.extend(defaults, options);

          8. // iterate and reformat each matched element

          9. return this.each(function() {

          10. var $gVideo = $(this);

          11. //create html structure

          12. //main wrapper

          13. var $video_wrap = $('<div></div>').addClass('ghinda-video-player').addClass(options.theme).addClass(options.childtheme);

          14. //controls wraper

          15. var $video_controls = $('<div class="ghinda-video-controls"><a class="ghinda-video-play" title="Play/Pause"></a><div class="ghinda-video-seek"></div><div class="ghinda-video-timer">00:00</div><div class="ghinda-volume-box"><div class="ghinda-volume-slider"></div><a class="ghinda-volume-button" title="Mute/Unmute"></a></div></div>');

          16. $gVideo.wrap($video_wrap);

          17. $gVideo.after($video_controls);

          這里先假設您了解jQuery并知道如何創建一個jQuery插件,因為這個不在本文的討論范圍之內,在上面這段腳本中我們使用jQuery動態創建視頻控制工具條的元素,接下來為了綁定事件我們需要獲取對應的元素:

          Java代碼

          1. //get newly created elements

          2. var $video_container = $gVideo.parent('.ghinda-video-player');

          3. var $video_controls = $('.ghinda-video-controls', $video_container);

          4. var $ghinda_play_btn = $('.ghinda-video-play', $video_container);

          5. var $ghinda_video_seek = $('.ghinda-video-seek', $video_container);

          6. var $ghinda_video_timer = $('.ghinda-video-timer', $video_container);

          7. var $ghinda_volume = $('.ghinda-volume-slider', $video_container);

          8. var $ghinda_volume_btn = $('.ghinda-volume-button', $video_container);

          9. $video_controls.hide(); // keep the controls hidden

          這里我們通過className方式獲取,先讓工具條隱藏直到所有資源加載完成,現在來實現播放/暫停按鈕:

          Java代碼

          1. var gPlay = function() {

          2. if($gVideo.attr('paused') == false) {

          3. $gVideo[0].pause();

          4. } else {

          5. $gVideo[0].play();

          6. }

          7. };

          8. $ghinda_play_btn.click(gPlay);

          9. $gVideo.click(gPlay);

          10. $gVideo.bind('play', function() {

          11. $ghinda_play_btn.addClass('ghinda-paused-button');

          12. });

          13. $gVideo.bind('pause', function() {

          14. $ghinda_play_btn.removeClass('ghinda-paused-button');

          15. });

          16. $gVideo.bind('ended', function() {

          17. $ghinda_play_btn.removeClass('ghinda-paused-button');

          18. });

          19. 大多數瀏覽器在右鍵點擊視頻時會提供一個獨立的菜單,它也提供了視頻控制功能,如果用戶通過這個右鍵菜單控制視頻那就會跟我們的自定義控件沖突,所以為了避免這一點我們需要綁定視頻播放器自身的“播放”,“暫停”和“結束”事件,在事件處理函數中處理播放/暫停按鈕,控制按鈕的樣式。

            為了創建進度條的拖動塊,我們使用了jQuery UI的Slider組件:

            Java代碼

            1. var createSeek = function() {

            2. if($gVideo.attr('readyState')) {

            3. var video_duration = $gVideo.attr('duration');

            4. $ghinda_video_seek.slider({

            5. value: 0,

            6. step: 0.01,

            7. orientation: "horizontal",

            8. range: "min",

            9. max: video_duration,

            10. animate: true,

            11. slide: function(){

            12. seeksliding = true;

            13. },

            14. stop:function(e,ui){

            15. seeksliding = false;

            16. $gVideo.attr("currentTime",ui.value);

            17. }

            18. });

            19. $video_controls.show();

            20. } else {

            21. setTimeout(createSeek, 150);

            22. }

            23. };

            24. createSeek();

            正如你所看到的,這里我們寫了一個遞歸函數,通過循環比較video的readyState屬性來判斷視頻是否已經準備好,否則我們就不能獲得視頻的時長也無法創建滑動塊,當視頻準備好后我們初始化滑動塊并顯示控制工具條,下一步我們通過綁定video元素的timeupdate事件實現計時器功能:

            Java代碼

            1. var gTimeFormat=function(seconds){

            2. var m=Math.floor(seconds/60)<10?"0"+Math.floor(seconds/60):Math.floor(seconds/60);

            3. var s=Math.floor(seconds-(m*60))<10?"0"+Math.floor(seconds-(m*60)):Math.floor(seconds-(m*60));

            4. return m+":"+s;

            5. };

            6. var seekUpdate = function() {

            7. var currenttime = $gVideo.attr('currentTime');

            8. if(!seeksliding) $ghinda_video_seek.slider('value', currenttime);

            9. $ghinda_video_timer.text(gTimeFormat(currenttime));

            10. };

            11. $gVideo.bind('timeupdate', seekUpdate);

            這里我們用seekUpdate函數獲取video的currentTime屬性值然后調用gTimeFormat函數進行格式化后得到當前播放的時間點。

            至于音量控制控件我們還是利用jQuery UI的Slider組件然后利用自定義函數實現靜音和取消靜音的功能:

            Java代碼

            1. $ghinda_volume.slider({

            2. value: 1,

            3. orientation: "vertical",

            4. range: "min",

            5. max: 1,

            6. step: 0.05,

            7. animate: true,

            8. slide:function(e,ui){

            9. $gVideo.attr('muted',false);

            10. video_volume = ui.value;

            11. $gVideo.attr('volume',ui.value);

            12. }

            13. });

            14. var muteVolume = function() {

            15. if($gVideo.attr('muted')==true) {

            16. $gVideo.attr('muted', false);

            17. $ghinda_volume.slider('value', video_volume);

            18. $ghinda_volume_btn.removeClass('ghinda-volume-mute');

            19. } else {

            20. $gVideo.attr('muted', true);

            21. $ghinda_volume.slider('value', '0');

            22. $ghinda_volume_btn.addClass('ghinda-volume-mute');

            23. };

            24. };

            25. $ghinda_volume_btn.click(muteVolume);

            26. 最后當我們自己的自定義視頻控制工具條構造完成后需要移除<video>標簽的controls屬性,這樣瀏覽器默認的工具條就被去掉了。

              好了,我們的插件功能已經全部完成了,調用方法:

              Java代碼

              1. $('video').gVideo();

              這會將我們的插件應用到頁面上每一個video元素上。

              外觀和體驗

              好的,現在到了比較有意思的部分,也就是播放器的外觀和體驗了。當插件功能已經完成后利用一點CSS就可以很容易地自定義樣式了,我們將全部使用CSS3來實現。

              首先,我們給播放器主容器加一些樣式:

              Java代碼

              1. .ghinda-video-player {

              2. float: left;

              3. padding: 10px;

              4. border: 5px solid #61625d;

              5. -moz-border-radius: 5px; /* FF1+ */

              6. -ms-border-radius: 5px; /* IE future proofing */

              7. -webkit-border-radius: 5px; /* Saf3+, Chrome */

              8. border-radius: 5px; /* Opera 10.5, IE 9 */

              9. background: #000000;

              10. background-image: -moz-linear-gradient(top, #313131, #000000); /* FF3.6 */

              11. background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #313131),color-stop(1, #000000)); /* Saf4+, Chrome */

              12. box-shadow: inset 0 15px 35px #535353;

              13. }

              14. 下一步,我們設置視頻控制工具條左邊浮動使它們水平對齊,利用CSS3的opacity和transitions我們給播放/暫停和靜音/取消靜音按鈕添加了非常不錯的懸浮效果:

                Java代碼

                1. .ghinda-video-play {

                2. display: block;

                3. width: 22px;

                4. height: 22px;

                5. margin-right: 15px;

                6. background: url(../images/play-icon.png) no-repeat;

                7. opacity: 0.7;

                8. -moz-transition: all 0.2s ease-in-out; /* Firefox */

                9. -ms-transition: all 0.2s ease-in-out; /* IE future proofing */

                10. -o-transition: all 0.2s ease-in-out; /* Opera */

                11. -webkit-transition: all 0.2s ease-in-out; /* Safari and Chrome */

                12. transition: all 0.2s ease-in-out;

                13. }

                14. .ghinda-paused-button {

                15. background: url(../images/pause-icon.png) no-repeat;

                16. }

                17. .ghinda-video-play:hover {

                18. opacity: 1;

                19. }

                20. 如果您仔細看了前面那段根據視頻播放狀態(Playing/Paused)添加和移除播放/暫停按鈕樣式的JavaScript代碼,就會明白為什么.ghinda-paused-button為什么要重寫.ghinda-video-play的背景屬性了。

                  現在輪到滑動塊了,我們進度條和音量控制的滑動塊的實現都是利用了jQuery UI的Slider組件,這個組件它本身自帶了樣式,定義在jQuery UI對應的css文件中,但是為了使滑動塊和播放器其他控件外觀保持一致我們全部重寫了它的樣式:

                  Java代碼

                  1. .ghinda-video-seek .ui-slider-handle {

                  2. width: 15px;

                  3. height: 15px;

                  4. border: 1px solid #333;

                  5. top: -4px;

                  6. -moz-border-radius:10px;

                  7. -ms-border-radius:10px;

                  8. -webkit-border-radius:10px;

                  9. border-radius:10px;

                  10. background: #e6e6e6;

                  11. background-image: -moz-linear-gradient(top, #e6e6e6, #d5d5d5);

                  12. background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #e6e6e6),color-stop(1, #d5d5d5));

                  13. box-shadow: inset 0 -3px 3px #d5d5d5;

                  14. }

                  15. .ghinda-video-seek .ui-slider-handle.ui-state-hover {

                  16. background: #fff;

                  17. }

                  18. .ghinda-video-seek .ui-slider-range {

                  19. -moz-border-radius:15px;

                  20. -ms-border-radius:15px;

                  21. -webkit-border-radius:15px;

                  22. border-radius:15px;

                  23. background: #4cbae8;

                  24. background-image: -moz-linear-gradient(top, #4cbae8, #39a2ce);

                  25. background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #4cbae8),color-stop(1, #39a2ce));

                  26. box-shadow: inset 0 -3px 3px #39a2ce;

                  27. }

                  28. 這時候音量控制的滑動塊一直顯示在音量按鈕旁邊,我們需要將它改成默認隱藏,當鼠標懸浮在音量按鈕上再動態顯示出來,使用transitions來實現這個效果會是個不錯的的選擇:

                    Java代碼

                    1. .ghinda-volume-box {

                    2. height: 30px;

                    3. -moz-transition: all 0.1s ease-in-out; /* Firefox */

                    4. -ms-transition: all 0.1s ease-in-out; /* IE future proofing */

                    5. -o-transition: all 0.2s ease-in-out; /* Opera */

                    6. -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */

                    7. transition: all 0.1s ease-in-out;

                    8. }

                    9. .ghinda-volume-box:hover {

                    10. height: 135px;

                    11. padding-top: 5px;

                    12. }

                    13. .ghinda-volume-slider {

                    14. visibility: hidden;

                    15. opacity: 0;

                    16. -moz-transition: all 0.1s ease-in-out; /* Firefox */

                    17. -ms-transition: all 0.1s ease-in-out; /* IE future proofing */

                    18. -o-transition: all 0.1s ease-in-out; /* Opera */

                    19. -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */

                    20. transition: all 0.1s ease-in-out;

                    21. }

                    22. .ghinda-volume-box:hover .ghinda-volume-slider {

                    23. position: relative;

                    24. visibility: visible;

                    25. opacity: 1;

                    26. }

                    27. 利用一些基礎的CSS屬性以及CSS3提供的新屬性我們打造了一個全新的播放器外觀,它看起來是這個樣子:

                      自定義皮膚

                      可能您已經注意到,我們在編寫插件的時候已經定義了一些默認選項,它們是theme和childtheme,可以在調用插件的時候根據需要方便的應用自定義皮膚。

                      這里解釋下theme就是所有控件的一整套樣式定義,childtheme就是在theme基礎上重寫某些樣式,我們在調用插件的時候可以同時指定這兩個選項或者其中的一個:

                      Java代碼

                      1. $('video').gVideo({

                      2. childtheme:'smalldark'

                      3. });

                      我們寫了一個示例的皮膚smalldark,它只重寫了部分的樣式,顯示效果是這樣的:

                      總結

                      利用HTML5 video,JavaScript和CSS3打造自定義的視頻播放器真的非常容易,t實現工具條功能用JavaScrip,外觀和體驗交給CSS3,我們得到了一個功能強大并且易于定制的解決方案!

                      enjoy!

                      2)mui Html5 Video 實現方案

                      前言: 最近項目中需要用到html5 視頻播放功能,于是稍微研究了解了下,遇到了很多坑,特此記錄下.

                      一、 Html5 Video

                      參考來源: http://www.xuanfengge.com/html5-video-play.html

                      (這篇博文確實幫助很大)

                      1.1、 目的

                      將Html5 Video功能應用到實際項目中,針對不同的平臺和環境,進行個性化處理。

                      基本只考慮webkit瀏覽器兼容問題

                      1.2、 Html5 Video支持格式

                      只支持: .mp4后綴(.h264編碼格式),和.webm后綴(專用web視頻格式),以及.ogg后綴(音頻文件)

                      注意: Html5 Video 可以添加多個source源來進行兼容適配,這樣,當第一個源讀取出問題時會自動讀取下一個源. 比如可以同時在前面加上.webm和.mp4源,這樣一個出錯時會自動讀取另一個可用源(因為不同瀏覽器,支持的格式是不一樣的)

                      但是,Hybird模式的 Android 下,有些機型只能讀取第一個source來源(測試華為和聯想都是),所以也就是說在這種情況下,要確保第一個source源是正確的

                      各大瀏覽器兼容如圖所示:

                      見圖1

                      1.3、 不同平臺環境和對應實現方案

                      說明: 這里分為兩大塊,普通瀏覽器環境(pc和手機,主要是移動端,pc不做特別處理)和Hybird模式的APP環境(Android和iOS).

                      注: Html5 video可以播放本地視頻或者網絡視頻

                      1.3.1、 普通瀏覽器環境

                      *用Html5 Video 自帶的播放欄控件

                      *用 Video 視頻統一處理方法處理后,點擊圖片手動隱藏圖片,設置視頻大小,手動播放視頻.

                      注: 播放效果則由各大瀏覽器自行實現

                      手機端瀏覽器實現的不同效果,比如:

                      QQ瀏覽器(包括QQ客戶端內置的瀏覽器):播放時會自動進入全屏

                      華為自帶瀏覽器: 正常小窗口播放

                      1.3.2、 Hybird App環境

                      說明: 內聯播放是指直接在video標簽中播放視頻,沒有必要進入全屏

                      1.3.2.1、 Android內聯播放

                      *用Html5 Video 自帶的播放欄控件

                      *用 Video 視頻統一處理方法處理后,點擊圖片手動隱藏圖片,設置視頻大小,手動播放視頻.

                      *Android內聯播放需要注意,必須開啟硬件加速,由于有些Android手機 webview是默認關閉硬件加速的,所以必須在創建這個帶視頻播放的webview時手動添加 硬件加速屬性才行.(詳情見plus創建webview的style)

                      style.hardwareAccelerated = true;

                      1.3.2.2、 iOS內聯播放

                      *用Html5 Video 自帶的播放欄控件

                      *用 Video 視頻統一處理方法處理后,點擊圖片手動隱藏圖片,設置視頻大小,手動播放視頻.

                      *內聯播放注意要點,由于iOS下默認是全屏播放的,所以需要經過設置才能正常內聯播放

                      第一步:在項目的manifest里面配置允許webview內聯播放

                      "plus": {
                       "splashscreen": {
                       "autoclose": true,/*是否自動關閉程序啟動界面,true表示應用加載應用入口頁面后自動關閉;false則需調plus.navigator.closeSplashscreen()關閉*/
                       "waiting": true/*是否在程序啟動界面顯示等待雪花,true表示顯示,false表示不顯示。*/
                       },
                       "allowsInlineMediaPlayback": true,/*設置ios下允許內聯播放*/
                       "popGesture": "close"

                      第二步: 創建video標簽時,手動加上內聯播放的屬性(iOS不支持preload)

                      <!--
                       讓ios支持內聯播放,必須添加 webkit-playsinline 標簽
                       -->
                       <video webkit-playsinline id="videoMedia" controls="controls" preload>

                      這樣iOS手機在播放的時候才會采用內聯播放

                      1.3.2.3、 Android非內聯播放

                      *通過NJS使用原生播放器來播放視頻,傳入的url可以是本地的或網絡的地址

                      *用 Video 視頻統一處理方法處理后,點擊圖片之后,圖片保持不變(所以沒有必要隱藏圖片),直接獲取視頻的資源地址,傳給原生播放器播放

                      注: 這種模式下,性能要比直接html5自帶播放器播放高

                      1.3.2.4、 iOS非內聯播放

                      *用Html5 Video 自帶的播放欄控件(非內聯播放需要去除特定內聯屬性”webkit-playsinline”,這樣才能全屏播放)

                      if(!isInlinePlay){
                       //如果是非內斂,ios需要去除內聯樣式
                       mediaTarget.removeAttribute('webkit-playsinline');
                       }

                      *用 Video 視頻統一處理方法處理后,點擊圖片之后,圖片保持不變(所以沒有必要隱藏圖片),直接調用video.play()播放視頻(這時候會用一個全屏播放器來播放視頻)

                      1.3.3、 注意要點

                      如果采用NJS通過Android原生播放器播放視頻,目前無法監聽到視頻的一些自定義事件.(如下載中,播放完畢,暫停,播放時間等)

                      而如果采用Html5 Video自帶播放,這些是可以通過腳本控制的.

                      所以選定方案時需要進行衡量

                      *另外,在Html5 Video播放時,無法監聽到規定的結束事件seeked,只能在timeUpdate里面判斷,如果ended為true就代表結束了.

                      *在NJS通過Android原生播放器播放時,可以通過document監聽resume和pause事件判斷是否進入播放和退出播放

                      1.4、 Tips

                      1.4.1、 關于Video 視頻統一處理的方案

                      說明: 由于將一個<Video>直接顯示在頁面中,會有各種五花八門的播放器效果,如圖:

                      (這里引用了參考來源的圖)

                      見圖2

                      顯然,體驗效果并不好,所以現在的做法是用一張模擬播放的圖片來替代<Video>所在的地方,而將Video元素設置為1*1像素大小.然后給圖片設置點擊監聽,監聽到點擊時,播放視頻.

                      注意:

                      *這里不要用{display: none}或者{width:0;height:0;}的方式,因為這樣視頻元素會處于未激活的狀態,給后續的處理帶來麻煩.

                      *這里沒有考慮ios<6和一些低版本的Android的兼容性問題了(這些版本里,無法直接通過video.play()來播放),因為項目環境基本上要求Android>4.0 iOS 7.0以上的.

                      *關于點擊圖片播放視頻后,如果是內聯播放模式下(或者是普通瀏覽器),就應該將圖片隱藏,然后將視頻大小設置為本來的大小(一般為圖片大小);如果是非內聯播放模式(全屏模式),就沒有必要隱藏圖片了,因為iOS下會自動打開一個全屏播放器來播放視頻,Android下考慮到Html5 video較卡,所以也會通過NJS使用原生播放器來全屏播放視頻.

                      1.4.2、 Android NJS播放視屏的實現代碼

                      說明: 這個是Dcloud論壇中有人分享的

                      //非內聯模式下的plus下的android才用到
                       var Intent = plus.android.importClass("android.content.Intent");
                       var Uri = plus.android.importClass("android.net.Uri");
                       var main = plus.android.runtimeMainActivity();
                       var intent = new Intent(Intent.ACTION_VIEW);
                       var uri = Uri.parse(url);
                       intent.setDataAndType(uri, "video/*");
                       main.startActivity(intent);

                      1.4.3、 如何讀取元素的寬高

                      *在獲取視頻的寬度時,發現用 video.style.width無法獲取到寬度.

                      后來查了資料,發現dom.style.width(height)只能獲取在stye直接賦予的值.而如果是通過css樣式表賦予的值是無法直接獲取的,只能通過dom.offsetWidth(offsetHeight)獲取.

                      *設置元素寬和高是不要直接在style里設置,而是最好通過css樣式表賦予

                      *讀取元素寬和高時,用offsetWidth(offsetHeight)

                      1.4.4、 關于全屏播放的問題

                      在PC端webkit瀏覽器下,全屏代碼如下:

                      進入全屏: videoContainer(對應的dom).webkitRequestFullscreen();

                      退出全屏: document.webkitCancelFullScreen();

                      *但是經測試,在手機瀏覽器和Hybird模式下的手機環境中都無法使用,

                      應該是手機瀏覽器中video 播放器的全屏模式和pc端的有區別,已經脫離了webkit的全屏組件,而是用原生自己實現的.

                      1.5、 遇到問題及解決方法

                      1.5.1、 Video.currentTime 設置值時設置無效,或者變為0

                      原因分析:

                      與測試的服務器和端口有關,測試環境是放在hbuild本地瀏覽器的,沒有處理好視頻快進問題,所以會導致每次快進后,視頻都會重置-在某些測試服務器上,則出現快進無效,但不會重置

                      解決方法:

                      將網頁用其它正式服務器打開均可正常,如tomcat,wampserver,甚至直接在本地打開也行.

                      1.5.2、 無法通過代碼Video.src獲取資源路徑

                      原因分析:

                      本實例中,Video是通過source添加src的,無法直接讀取video的src

                      解決方法:

                      可以通過讀取到第一個source的標簽,再獲取source的src

                      注:本來這個方法有一個缺點就是有可能第一個source的src不可用.但是由于Android中第一個source必須有用才行.否則無法正常播放.所以在確保第一個source正確的情況下能這樣用.

                      1.5.3、 部分Android機型無法退出全屏

                      描述:

                      在使用Html5 Video自帶播放器播放時,部分Android機型(如聯想K860點擊全屏按鈕進入全屏后,無法退出全屏-因為進入全屏后,全屏按鈕不見了)

                      原因分析: 可能是手機廠商擅自劫持了瀏覽器或者篡改了瀏覽器實現方式

                      解決方法:

                      目前無法解決,在這類機型中,建議直接采用非內聯模式播放或者是盡量不要手動進入全屏

                      3)移動端HTML5<video>視頻播放優化實踐

                      遇到的挑戰

                      移動端HTML5使用原生<video>標簽播放視頻,要做到兩個基本原則,速度快和體驗佳,先來分析一下這兩個問題。

                      下載速度

                      以一個8s短視頻為例,wifi環境下提供的高清視頻達到1000kbps,文件大小大約1MB;非wifi環境下提供的低碼率視頻是500kbps左右,文件大小大約500KB;參考QzoneTouch多普勒測速,2g網絡的平均速度是14KB/s,那么下載一個低碼率視頻耗時35s;那么要想流暢播放視頻,就需要一個加載等待的過程,這個過程要有明確的反饋,不能讓用戶有“壞掉了”的感覺。

                      多普勒測速數據參考

                      #dns(s)conn(s)rtt(s)tran(kb/s)
                      2g3.857852.334822.5747814.0374
                      3g1.606430.7431090.60804760.1967
                      wifi0.9869210.5502080.44433270.8728

                      用戶體驗

                      視頻是否可以自動播放,是否能循環播放,是否能顯示下載進度,播放的時候如何隱藏控制條,暫停的時候又能顯示出來呢。這些問題看上去貌似簡單,但是由于PC/iOS/Android這些不同平臺、不同的瀏覽器內核、甚至相同內核的不同版本,所實現的<video>屬性、方法和事件差異較大,解決兼容性問題又給開發造成了很大困擾。

                      分析原因

                      事件差異

                      下面是播放一個短視頻,在不同平臺觸發事件和獲取屬性的差異表現。

                      PC

                      #eventreadyStatecurrentTime (s)buffered (s)duration (s)視頻狀態
                      1loadstartNOTHING0
                      2suspendNOTHING0
                      3playNOTHING0
                      4waitingNOTHING0
                      5durationchangeMETADATA05.357.91獲取到視頻長度
                      6loadedmetadataMETADATA00.667.91獲取到元數據
                      7loadeddataENOUGHDATA00.667.91
                      8canplayENOUGH_DATA00.667.91
                      9playingENOUGH_DATA00.667.91開始播放
                      10canplaythroughENOUGH_DATA00.667.91可以流暢播放
                      11progressENOUGH_DATA0.113.687.91持續下載
                      12timeupdateENOUGH_DATA0.144.447.91播放進度變化
                      23progressENOUGH_DATA1.777.917.91下載完畢
                      24suspendENOUGH_DATA1.777.917.91
                      25timeupdateENOUGH_DATA1.97.917.91繼續播放中
                      48timeupdateENOUGH_DATA7.77.917.91
                      49timeupdateENOUGH_DATA07.917.91
                      50seekingMETADATA07.917.91
                      51waitingMETADATA07.917.91
                      52timeupdateENOUGH_DATA07.917.91
                      53seekedENOUGH_DATA07.917.91播放完畢進度回到起點
                      54canplayENOUGH_DATA07.917.91
                      55playingENOUGH_DATA07.917.91循環播放
                      56canplaythroughENOUGH_DATA07.917.91
                      57timeupdateENOUGH_DATA0.197.917.91

                      iOS

                      #eventreadyStatecurrentTime (s)buffered (s)duration (s)視頻狀態
                      1loadstartNOTHING0
                      2playNOTHING0
                      3waitingNOTHING0
                      4durationchangeMETADATA07.91獲取到視頻長度
                      5loadedmetadataMETADATA07.91獲取到元數據
                      6loadeddataENOUGHDATA07.91
                      7canplayENOUGH_DATA07.917.91
                      8canplaythroughENOUGH_DATA07.917.91可以流暢播放
                      9playingENOUGH_DATA07.917.91開始播放
                      10progressENOUGH_DATA07.917.91下載完畢
                      11suspendENOUGH_DATA07.917.91
                      12timeupdateENOUGH_DATA0.027.917.91播放進度變化
                      43timeupdateENOUGH_DATA7.87.917.91
                      44timeupdateENOUGH_DATA07.917.91
                      45seekedENOUGH_DATA07.917.91播放完畢進度回到起點
                      46timeupdateENOUGH_DATA0.227.917.91循環播放

                      Android

                      #eventreadyStatecurrentTime (s)buffered (s)duration (s)視頻狀態
                      1loadstartNOTHING0
                      2playNOTHING0
                      3waitingNOTHING00
                      4durationchangeENOUGH_DATA000
                      5durationchangeENOUGH_DATA007.91獲取到視頻長度
                      6loadedmetadataENOUGH_DATA007.91獲取到元數據
                      7loadeddataENOUGHDATA007.91
                      8canplayENOUGH_DATA007.91
                      9canplaythroughENOUGH_DATA007.91
                      10playingENOUGH_DATA007.91
                      11timeupdateENOUGH_DATA007.91
                      12progressENOUGH_DATA03.577.91下載中
                      13timeupdateENOUGH_DATA0.26.897.91開始播放
                      14progressENOUGH_DATA07.917.91下載完畢
                      49timeupdateENOUGH_DATA7.797.917.91
                      50progressENOUGH_DATA7.877.917.91
                      51timeupdateENOUGH_DATA07.917.91
                      52seekingENOUGH_DATA07.917.91播放完畢進度回到起點
                      53timeupdateENOUGH_DATA07.917.91
                      54seekedENOUGH_DATA07.917.91循環播放失敗卡住了
                      55progressENOUGH_DATA07.917.91
                      56stalledENOUGH_DATA07.917.91

                      一些常用且需要重點關注的<video>事件

                      eventiOSAndroid
                      ****************************************************************************************************************
                      play只是要播放視頻,響應的是video.play()方法,并不代表已經開始播放和iOS一樣,僅是響應video.play()方法
                      durationchange會執行一次,一定會獲取到視頻的duration可能會執行多次,只有最后一次才能獲取到真實的duration,前面的duration都是0;但低版本Android可能獲取到的duration是0或1;(本文提到的低版本Android大部分是4.1以下)
                      canplay可以認為是視頻元素沒有問題,可以運行,沒有更多含義了,基本用不上同iOS
                      canplaythrough會有明確的緩沖,表示可以流暢播放了;沒有什么用,視頻仍然會卡住,數據可能還沒有開始加載;
                      playing明確表示播放開始了;依然沒有用,視頻可能并沒有開始播放;
                      progress有明確的下載,可以獲取到當前的buffer,并且全部下載完畢后不在觸發;不一定有明確的數據下載,并且全部下載完畢后依然繼續觸發;
                      timeupdate會有明確的進度變化,可以獲取到currentTime;進度不一定變化,currentTime可能總是0,但是第一次有currentTime變化的timeupdate事件一定代表了視頻開始播放了;
                      erroriOS中會有明確的錯誤拋出;Android中某些瀏覽器會莫名其妙的拋出error;
                      stalled網絡狀況不佳,導致視頻下載中斷;在沒有play之前,也可能會拋出該事件。

                      屬性差異

                      attributesiOSandroid
                      ****************************************************************************************************************
                      poster

                      封面圖片

                      支持,但是加載速度明顯比在<img>中要慢;不一定支持(瀏覽器廠商的實現標準不統一);
                      preload

                      預加載

                      iPhone不支持;可能支持;
                      autoplay

                      自動播放

                      iPhone Safari中不支持,但在webview中可能被開啟;iOS開發文檔明確說明蜂窩網絡下不允許autoplay;可能支持;
                      loop

                      循環播放

                      支持可能支持;
                      controls

                      控制條

                      支持,但是需要開始播放了才顯示基本都支持顯示或者不顯示
                      width和height一定給出明確的屬性設置,切不能為0;如果不設置,僅僅通過CSS樣式去控制視頻大小,可能會導致標簽失效。

                      其他怪異bug和不友好表現

                      iOSandroid
                      ******************************************************************************************************************
                      物理位置覆蓋在<video>區域上的元素,click和touch等事件會失效,比如一個<a>鏈接如果覆蓋在<video>上,那么點擊后沒有任何效果。
                      iOS8.0+中,單頁面播放視頻超過16個,再播放的視頻全部MediaError解碼異常無法播放。
                      iPhone的Safari會彈出一個全屏的播放器來播放視頻,iPad則支持內聯播放。iOS7+ 如果webview(比如微信)開啟了webview.allowsInlineMediaPlayback = YES;,可以通過設置webkit-playsinline屬性支持內聯播放;支持內聯播放,但某些廠商會用自己的播放器劫持原生的視頻播放;
                      下載視頻時,會先發送一個2字節的請求來獲取視頻元數據(比如時長),然后再不斷的發送分包續傳(206)請求來下載視頻,抓包顯示請求數和請求量至少有一倍的冗余(x2),這個嚴重的bug在iOS8中有明顯的修復,但是分包的206請求仍然會有冗余數據的下載,浪費了流量。比iOS的處理方式好,沒有第一個2字節請求,沒有流量損耗;
                      低版本Android(<=4.0.4)中,<video>如果在有相對和決定定位的層中,可能會導致整個頁面錯位。
                      某些瀏覽器廠商會劫持<video>,用其“自己”的播放器來播放視頻,“破壞”了產品本身的播放體驗,那么只能case by case的解決了。
                      加載視頻時沒有進度提示,視覺上看不出是播放完了還是卡住了;加載視頻時,大都會顯示一個自帶的loading UI(菊花)。

                      最佳實踐

                      視頻初始化

                      如果將一個<video>直接顯示在頁面中,那么就會看到各種五花八門的播放器初始效果;

                      這顯然不是一個好的視覺體驗,那么通常的做法是制作一個模擬的視頻播放視圖,比如一個封面加一個播放按鈕。

                      而真實的<video>視頻元素要隱藏起來,如何隱藏呢?最好不要用{display: none}或者{width:0;height:0;}的方式,因為這樣視頻元素會處于未激活的狀態,給后續的處理帶來麻煩。最佳的方式是將視頻設置成1×1像素大小,放在視覺邊緣的位置。

                      1

                      2

                      3

                      4

                      5

                      <!--iOS-->

                      <video webkit-playsinline width="1"height="1"class="vplayinside notaplink"x-webkit-airplay controls loop="loop"src="<%=src%>"></video>

                      <!--Android-->

                      <video width="1"height="1"controls loop="loop"src="<%=src%>"></video>

                      自動播放

                      autoplay的支持依賴內核和網絡狀況,比如iPhone在蜂窩網絡下明確禁用了autoplay;

                      經過試驗,在沒有明確的用戶操作的情況下,直接通過video.play()也是無法激活播放的;

                      并且在產品設計上,自動播放也不是一個舒服的用戶體驗,所以產品設計上盡量避免使用自動播放。

                      點擊播放

                      之前提到,視頻最好通過1px大小隱藏起來,那么這時如何觸發播放呢?

                      經過試驗,當在明確的用戶操作(touch、click)時,通過這些用戶行為事件的回調函數,用video.play()是可以觸發視頻播放的,那么能否在用戶操作后,再去同步的創建和播放視頻呢?答案是肯定的,這無疑是一個視頻元素初始化的最佳實踐,但是有些差異需要注意。

                      iOS6+

                      可以在用戶的touch時間中動態創建并播放視頻。

                      iOS < 6

                      可以在用戶的touch時間中動態創建視頻,但不能播放;要再追加一個click事件來啟動播放;也就是說,給偽造的視頻播放按鈕同時綁定tap和click事件,在tap的時候創建,在之后300毫秒的click中去播放。

                      Android

                      大部分高版本Android可以像iOS6+那樣去處理,但是低版本的不行,必須要通過click事件去傳遞video.play(),為了保持兼容,最好是用幫tap和click兩個事件來分別完成視頻的初始化和播放。

                      我們還發現,有些低版本Android中,無法通過video.play()來播放視頻,必須有真實的用戶點擊視頻元素才能播放;這種情況,有一個技巧就是在tap的時候初始化并放大視頻覆蓋在播放視圖中,讓300毫秒后的真實點擊行為穿透點擊在視頻元素上來實現播放。

                      循環播放

                      如果視頻需要循環播放,那么就增加loop屬性,是否能循環播放就看瀏覽器是否支持了,因為還沒有找到hack技巧來強制循環播放;

                      即使,在不支持循環播放的Android中,通過監聽seeked事件知道了播放進度到了終點或起點暫停了,此時也無法通過video.play()來讓視頻重新播放。

                      監控下載進度

                      如何獲取視頻時長和已經下載的時長?

                      1

                      2

                      3

                      4

                      5

                      6

                      7

                      8

                      9

                      10

                      11

                      12

                      13

                      // 視頻時長

                      varduration=video.duration

                      // 獲取視頻已經下載的時長

                      functiongetEnd(video){

                      varend=0

                      try{

                      end=video.buffered.end(0)||0

                      end=parseInt(end*1000+1)/1000

                      }catch(e){

                      }

                      returnend

                      }

                      progress事件表示視頻在加載,但是它的觸發頻率和時機并不規律,最佳做法是通過一個定時器去實時獲取end,當end >= duration時,表示已經下載完畢,再終止定時器。

                      1

                      2

                      3

                      4

                      5

                      6

                      7

                      8

                      9

                      10

                      vartimer=setInterval(function(){

                      varend=getEnd(video),

                      duration=video.duration

                      if(end<duration){

                      return

                      }

                      clearInterval(timer)

                      },1000)

                      全部下載后再播放

                      假設播放短視頻,如果網絡不佳,會造成播放斷斷續續,在iOS中這種停頓還沒有一個明確的等待提示,這不是一個好的體驗,那么是否可以將視頻全部下載完畢再播放呢?

                      在iOS中,可以在視頻剛開始下載的時候馬上暫停,此時下載還將繼續,可以做一個loading的菊花告知視頻正在加載,然后等到視頻全部下載完再開始播放。

                      1

                      2

                      3

                      4

                      5

                      6

                      7

                      8

                      9

                      10

                      11

                      12

                      13

                      14

                      15

                      16

                      17

                      18

                      19

                      20

                      21

                      22

                      23

                      24

                      25

                      $(video).one('loadeddata',function(){

                      // 暫停,但下載還在繼續

                      video.pause()

                      // 啟動定時器檢測視頻下載進度

                      vartimer=setInterval(function(){

                      varend=getEnd(video),

                      duration=video.duration

                      if(end<duration){

                      return

                      }

                      varwidth=$(video).parent().width()

                      // 下載完了,開始播放吧

                      $(video).attr{

                      width:width,

                      height:width

                      }

                      video.play()

                      clearInterval(timer)

                      },1000)

                      })

                      緩沖播放——邊下邊播時,選擇開始播放的最佳時間點

                      當視頻越來越長或者網絡慢時,等待視頻全部下載完再播放也不是好的體驗,最好能邊下邊播,緩沖到流暢狀態就開始播放,那什么時候播放才是最佳時間點呢?

                      在iOS中,canplaythrough事件就是這個最佳時間點,它是通過動態計算緩沖量和下載速度得出的視頻可以流暢播放的狀態反饋。

                      canplaythrough event: The user agent estimates that if playback were to be started now, the media resource could be rendered at the current playback rate all the way to its end without having to stop for further buffering.

                      注意:下載完再播放和緩沖播放只適用于iOS。

                      統計播放時間和播放次數

                      要統計實際的播放時間,要累加timeupdate事件變化的時間,再減去中間可能暫停的時間。

                      1

                      2

                      3

                      4

                      5

                      6

                      7

                      8

                      9

                      10

                      11

                      12

                      13

                      14

                      15

                      16

                      17

                      18

                      19

                      20

                      21

                      22

                      23

                      24

                      25

                      26

                      27

                      28

                      29

                      30

                      $video.on('playing',function(){

                      // 開始播放是打點

                      $video.attr('data-updateTime',+newDate())

                      })

                      $video.on('pause',function(){

                      // 暫停播放時清除打點

                      $video.removeAttr('data-updateTime')

                      })

                      // 累加播放時間

                      $video.on('timeupdate',function(event){

                      var$video=$(event.target),

                      updateTime=parseInt($video.attr('data-updateTime')||0),

                      playingTime=parseInt($video.attr('data-playingTime')||0),

                      times=parseInt($video.attr('data-times')||0),

                      newtimes=0,

                      video=$video.get(0),

                      duration=parseFloat($video.attr('data-duration')||0),

                      now=+newDate()

                      // 播放時間

                      playingTime=playingTime+now-updateTime

                      // 播放次數

                      newtimes=Math.ceil(playingTime/1000/duration)

                      $video.attr('data-playingTime',playingTime)

                      $video.attr('data-updateTime',now)

                      })

                      異常處理

                      對error事件做詳細的上報;

                      對stalled事件做統計上報,并提示用戶網絡慢等。

                      參考數據

                      微視觸屏版iOS視頻測速

                      網絡環境視頻碼率獲取到視頻時長

                      時間點(s)

                      開始流暢播放

                      時間點(s)

                      全部下載完畢

                      時間點(s)

                      視頻長度(s)
                      wifi1000kbps2.863.975.858.69
                      非wifi500kbps4.56810.628.67

                      搬好凳子看HTML

                      首先我們在HB下創建一個新的app項目,名稱為 欠債

                      新建一個video.html

                      webkit-playsinline : 在ios中,加入此屬性,可以關閉自動全屏播放

                      object-fit:fill : 視頻充滿video容器的大小

                      詳細理由請看參考文獻2or3

                      在此我們向項目里放置一個mp4格式的視頻,視頻內容不限,可以是小動畫,也可以是

                      ps:要在meta中加上,否則視頻會擴充變形哦

                      <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />

                      OK,現在布局已經完成,一個視頻已經在頁面中了

                      旁白:尼瑪,點了沒反應,那這怎么播放?

                      樓主:你們這群家伙看別的小視頻等個1小時都行。。。

                      旁白:一個簡單的播放器,至少要有 暫停/播放,進度條,視頻時長,全屏等控件吧

                      樓主:來來來,不要急,先來個播放按鈕寫在video標簽后面

                       <div class="bad-video">
                       <video class="" webkit-playsinline style="object-fit:fill;">
                       <source src='xx.mp4' type="video/mp4"></source>
                       <p>設備不支持</p>
                       <video>
                       <img src="img/play.png"/>
                       </div>

                      寫好樣式、

                       .bad-video { position: relative; overflow: hidden; background-color: #CCCCCC;
                       } 
                       .bad-video .vplay{ position: absolute; width: 15%; z-index: 99; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%);
                       }

                      樓主:當當當

                      再在后面加一個控制條

                       <img src="img/play.png" class="vplay" /> <div class="controls">
                       <div>
                       <div class="progressBar">
                       <div class="timeBar"></div>
                       </div>
                       </div>
                       <div><span class="current">00:00</span>/<span class="duration">00:00</span></div>
                       <div><span class="fill">全屏</span></div>
                       </div>
                      .bad-video .controls { width: 100%; height: 2rem; line-height: 2rem; font-size: 0.8rem; color: white; display: block; position: absolute; bottom: 0; background-color: rgba(0, 0, 0, .55); display: -webkit-flex; display: flex;
                      }.bad-video .controls>* { flex: 1;
                      }.bad-video .controls>*:nth-child(1) { flex: 6;
                      }.bad-video .controls>*:nth-child(2) { flex: 2; text-align: center;
                      }.bad-video .controls .progressBar { margin: .75rem 5%; position: relative; width: 90%; height: .5rem; background-color: rgba(200, 200, 200, .55); border-radius: 10px;
                      }.bad-video .controls .timeBar { position: absolute; top: 0; left: 0; width: 0; height: 100%; background-color: rgba(99, 110, 225, .85); border-radius: 10px;
                      }

                      總算有個看起來像樣的了

                      旁白:樓主,可是還是不能播放啊

                      樓主:叫你別急,要不你先去擼一把,我寫好了文字@你

                      旁白:好啊,早說嘛,我先走了,記得@我

                      樓主:你走,省的我精神分裂碼兩個人的字

                      好,現在Html元素已經基本上弄好啦,看起來不是那么low了

          # 一、前言

          之前就已經具備了本地回放、遠程回放、設備播放3個模塊,其中本地回放用來回放存儲在軟件本地電腦上的視頻文件;遠程回放需要通過調用廠家sdk或者GB28181(沒實現,后期考慮增加)從NVR回放視頻;設備播放通過通用的rtsp規則視頻流的形式調用nvr存儲錄像或者實時錄像回放;近期增加了個圖片回放模塊,是用來回放圖片文件夾中的圖片文件的,排隊繪制圖片序列,看起來有點視頻的感覺。

          有一種場景是,用戶自己的人工智能算法,能夠拿到一堆圖片序列,自定義規則存儲,觸發報警后也拿到一堆圖片序列,這樣可以通過這個模塊直接針對圖片序列進行播放,也支持導入導出,播放的速度可控制調節,拉動播放進度條直接定位到對應的圖片序列播放。

          圖片存儲規則

          - 默認存儲主目錄 image_normal。

          - 主目錄下按照日期目錄存放 比如 2021-04-07 2021-04-08。

          - 日期目錄下是單個的圖片文件 比如 Ch1_2021-04-07-14-08-11-222.jpg。

          - 拓展功能可以存儲對應的數據文件比如警情文字和圖片文件一個目錄 名稱一樣 拓展名可以是 txt。

          ## 二、功能特點

          ### (一)軟件模塊

          1. 視頻監控模塊,各種停靠小窗體子模塊,包括設備列表、圖文警情、窗口信息、云臺控制、預置位、巡航設置、設備控制、懸浮地圖、網頁瀏覽等。

          2. 視頻回放模塊,包括本地回放、遠程回放、設備播放、圖片回放、視頻上傳等。

          3. 電子地圖模塊,包括圖片地圖、在線地圖、離線地圖、路徑規劃等。

          4. 日志查詢模塊,包括本地日志、設備日志等。

          5. 系統設置模塊,包括系統設置(基本設置、視頻參數、數據庫設置、地圖配置、串口配置等)、錄像機管理、攝像機管理、輪詢配置、用戶管理等。

          ### (二)基礎功能

          1. 支持各種視頻流(rtsp、rtmp、http等)、視頻文件(mp4、rmvb、avi等)、本地USB攝像機播放。

          2. 支持多畫面切換,包括1、4、6、8、9、13、16、25、36、64畫面切換。

          3. 支持全屏切換,多種切換方式包括鼠標右鍵菜單、工具欄按鈕、快捷鍵(alt+enter全屏,esc退出全屏)。

          4. 支持視頻輪詢,包括1、4、9、16畫面輪詢,可設置輪詢分組(輪詢預案)、輪詢間隔、碼流類型等。

          5. 支持onvif協議,包括設備搜索、云臺控制、設備控制(圖片參數、校對時間、系統重啟,抓拍圖片等)。

          6. 支持權限管理,不同的用戶可以對應不同的模塊權限,比如刪除日志、關閉系統等。

          7. 數據庫支持多種,包括sqlite、mysql、sqlserver、postgresql、oracle、人大金倉等。

          8. 本地USB攝像機支持設置分辨率、幀率等參數。

          9. 所有停靠模塊都自動生成對應的菜單用來控制顯示和隱藏,在標題欄右鍵可以彈出。

          10. 支持顯示所有模塊、隱藏所有模塊、復位普通布局、復位全屏布局。

          11. 雙擊設備彈出實時預覽視頻,支持圖片地圖、在線地圖、離線地圖等。

          12. 攝像機節點拖曳到對應窗體播放視頻,同時支持拖曳本地文件直接播放。

          13. 刪除視頻支持鼠標右鍵刪除、懸浮條關閉刪除、拖曳到視頻監控面板外刪除等多種方式。

          14. 圖片地圖上設備按鈕可自由拖動,自動保存位置信息。百度地圖上可以鼠標單擊獲取經緯度信息,用來更新設備位置。

          15. 視頻監控面板窗體中任意通道支持拖曳交換,瞬間響應。

          16. 封裝了百度地圖,視圖切換,運動軌跡,設備點位,鼠標按下獲取經緯度等。

          17. 雙擊節點、拖曳節點、拖曳窗體交換位置等操作,均自動更新保存最后的播放地址,下次軟件打開自動應用。

          18. 右下角音量條控件,失去焦點自動隱藏,音量條帶靜音圖標。

          19. 支持視頻截圖,可指定單個或者對所有通道截圖,底部小工具欄也有截圖按鈕。

          20. 支持超時自動隱藏鼠標指針、自動全屏機制。

          21. 支持onvif云臺控制,可上下左右移動云臺攝像機,包括復位和焦距調整等。

          22. 支持任意onvif攝像機,包括但不限于海康、大華、宇視、天地偉業、華為等。

          23. 可保存視頻,可選定時存儲或者單文件存儲,可選存儲間隔時間。

          24. 可設置視頻流通信方式tcp+udp,可設置視頻解碼是速度優先、質量優先、均衡等。

          25. 可設置軟件中文名稱、英文名稱、LOGO圖標等。

          26. 存儲的視頻文件支持導出到指定目錄,支持批量上傳到服務器。

          ### (三)特色功能

          1. 主界面采用停靠窗體模式,各種組件以小模塊的形式加入,可自定義任意模塊加入。

          2. 停靠模塊可拖動任意位置嵌入和懸浮,支持最大化全屏,支持多屏幕。

          3. 雙重布局文件存儲機制,正常模式、全屏模式都對應不同的布局方案,自動切換和保存,比如全屏模式可以突出幾個模塊透明顯示在指定位置,更具科幻感現代化。

          4. 原創onvif協議機制,采用底層協議解析(udp廣播搜索+http請求執行命令)更輕量易懂易學習拓展,不依賴任何第三方組件比如gsoap。

          5. 原創數據導入導出機制,跨平臺不依賴任何組件,瞬間導出數據。

          6. 內置多個原創組件,宇宙超值超級牛逼,包括數據導入導出組件(導出到xls、pdf、打印)、數據庫組件(數據庫管理線程、自動清理數據線程、萬能分頁、數據請求等)、地圖組件、視頻監控組件、文件多線程收發組件、onvif通信組件、通用瀏覽器內核組件等。

          7. 自定義信息框+錯誤框+詢問框+右下角提示框(包含多種格式)等。

          8. 精美換膚,高達17套皮膚樣式隨意更換,所有樣式全部統一,包括菜單等。

          9. 視頻控件懸浮條可以自行增加多個按鈕,監控界面底部小工具欄也可自行增加按鈕。

          10. 雙擊攝像機節點自動播放視頻,雙擊節點自動依次添加視頻,會自動跳到下一個,雙擊父節點自動添加該節點下的所有視頻。可選主碼流、子碼流。

          11. 錄像機管理、攝像機管理,可添加刪除修改導入導出打印信息,立即應用新的設備信息生成樹狀列表,不需重啟。

          12. 可選多種內核自由切換,ffmpeg、vlc、mpv等,均可在pro中設置。推薦用ffmpeg,跨平臺最多,默認提供好了linux和mac平臺上編譯好的庫。

          13. 支持硬解碼,可設置硬解碼類型(qsv、dxva2、d3d11va等)。

          14. 默認采用opengl繪制視頻,超低的CPU資源占用,支持yuyv和nv12兩種格式繪制,很牛逼。

          15. 高度可定制化,用戶可以很方便的在此基礎上衍生自己的功能,比如增加自定義模塊,增加運行模式、機器人監控、無人機監控、挖掘機監控等。

          16. 支持xp、win7、win10、linux、mac、各種國產系統(UOS、中標麒麟、銀河麒麟等)、嵌入式linux等系統。

          17. 注釋完整,項目結構清晰,超級詳細完整的使用開發手冊,精確到每個代碼文件的功能說明,不斷持續迭代版本。

          ## 三、體驗地址

          1. 體驗地址:[https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g](https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g) 提取碼:01jf 文件名:bin_video_system.zip。

          2. 國內站點:[https://gitee.com/feiyangqingyun](https://gitee.com/feiyangqingyun)

          3. 國際站點:[https://github.com/feiyangqingyun](https://github.com/feiyangqingyun)

          4. 個人主頁:[https://blog.csdn.net/feiyangqingyun](https://blog.csdn.net/feiyangqingyun)

          5. 知乎主頁:[https://www.zhihu.com/people/feiyangqingyun/](https://www.zhihu.com/people/feiyangqingyun/)

          6. 在線文檔:[https://feiyangqingyun.gitee.io/qwidgetdemo/video_system/](https://feiyangqingyun.gitee.io/qwidgetdemo/video_system/)

          ## 四、效果圖

          ## 五、核心代碼


          主站蜘蛛池模板: 亚洲一区二区三区四区视频 | 国产拳头交一区二区| 中文字幕精品一区二区| 亚洲图片一区二区| 麻豆一区二区99久久久久| 国产一区二区在线看| 国产精品高清一区二区三区| 免费一区二区三区| 亚洲爆乳精品无码一区二区三区| 国产亚洲一区二区三区在线观看| 中文字幕无码一区二区免费| 精品少妇一区二区三区视频| 国产精品一区二区久久国产| 无码一区二区三区在线观看| 日韩精品无码一区二区三区 | 日韩精品电影一区亚洲| 国产精品亚洲一区二区三区久久 | 久久久精品人妻一区亚美研究所 | 中文字幕一区二区在线播放| 国产午夜精品一区二区| 国产激情з∠视频一区二区| 男女久久久国产一区二区三区| 亚洲国产高清在线精品一区| 国产精品第一区揄拍| 国产成人av一区二区三区不卡| 人成精品视频三区二区一区| 成人区精品人妻一区二区不卡 | 精品一区二区三区AV天堂| 国产福利电影一区二区三区久久久久成人精品综合 | 亚洲成AV人片一区二区密柚| 精品人妻码一区二区三区| 日韩成人无码一区二区三区| 欧洲精品码一区二区三区免费看 | 波多野结衣av高清一区二区三区| 一区二区三区在线免费观看视频| 亚洲人成人一区二区三区| 精品深夜AV无码一区二区老年| 国产一区二区三区不卡在线看| 久久久久女教师免费一区| 精品人无码一区二区三区| 在线视频一区二区日韩国产|