News Contents
信息中心

移動端HTML5 video 視頻播放優化實踐

發表日期:2020/1/29      瀏覽次數:

遇到的挑戰

移動端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

// 視頻時長

var duration = video.duration

 

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

function getEnd(video) {

  var end = 0

  try {

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

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

  } catch(e) {

  }

  return end

}

 

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

1

2

3

4

5

6

7

8

9

10

var timer = setInterval(function() {

  var end = 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()

 

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

  var timer = setInterval(function() {

    var end = getEnd(video),

        duration = video.duration

 

    if(end < duration) {

      return

    }

 

    var width = $(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', +new Date())

})

 

$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 = +new Date()

 

  // 播放時間

  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

參考資料


盛大建站承接個人、公司、企業的網站建設、網頁設計、網店制作、獨立商城制作、外貿網站制作,也承接同行介紹的業務,歡迎聯系!
本站承接主要業務如下:
1、提供免備案虛擬主機(網站空間),有獨立國內服務器、高速美國服務器、香港服務器、韓國服務器、日本服務器,>> http://www.fuanxx.com
2、網站建設:個人網店制作、企業建站、公司網站、新聞、下載等;論壇、博客網站搭建,承接外貿英文站制作
3、承接阿里巴巴、京東、淘寶、天貓等的商品數據采集
4、網站防釣魚,可以過360、QQ、搜狗、金山、微信攔截
5、要就發IP網站限制訪問系統,防止同行抄襲并保障海外客戶訪問不受影響,>> http://www.198ip.com
  下一篇:沒有了!
十年如一日,我們專注于提供高品質 定制網站建設服務!
您的信任,我們的責任,期待與您的合作!
广西11选5技巧