最近在移動端項(xiàng)目用到了video標(biāo)簽展示視頻,原本以為作為html5標(biāo)準(zhǔn)一員的video到了今天兼容性應(yīng)該沒什么問題了,可一用才知道還是有些坑的......那么在這里就由淺入深的論述一下使用video的心得吧(一些眾所周知的常用屬性和我沒怎么用過的在此均省略不表)。
屬性
-
src
要播放的視頻的 URL。只支持3種格式:MP4、WebM、Ogg。另外src除了可以使用放在服務(wù)器的絕對路徑和項(xiàng)目中的相對路徑(對于本地路徑在PC端有一些限制,移動端貌似無法取到本地視頻),還支持 base64碼(需添加前綴data:video/mp4;base64,注意有逗號) -
poster
規(guī)定視頻正在下載時顯示的圖像,直到用戶點(diǎn)擊播放按鈕。(也就是視頻播放之前顯示的一個預(yù)覽圖)其值和img標(biāo)簽的src屬性相同,填上一個圖片路徑或者base64。 -
playsinlinewebkit-playsinline
視頻在移動端播放時會自動全屏,而這個屬性就是為了阻止全屏動作的,添加-webkit-前綴增加在ios safari上的兼容性。(只用寫上屬性名就行,和autoplay,loop之類的類似) -
muted
如果出現(xiàn)該屬性,視頻的音頻輸出為靜音。 -
autoplay
自動播放,但是在ios上無法執(zhí)行自動播放......(添加muted可以自動播放)需要用戶執(zhí)行play方法。(具體可參考視頻播放--踩坑小計(jì))
以上幾種屬性是寫video標(biāo)簽時經(jīng)常設(shè)置的幾種屬性,以下的幾種則一般是需要在js中獲取的屬性,以便進(jìn)行一些操作。
-
networkState
返回音頻/視頻的當(dāng)前網(wǎng)絡(luò)狀態(tài)(activity)。
返回值如下- 0 = NETWORK_EMPTY - 音頻/視頻尚未初始化
- 1 = NETWORK_IDLE - 音頻/視頻是活動的且已選取資源,但并未使用網(wǎng)絡(luò)
- 2 = NETWORK_LOADING - 瀏覽器正在下載數(shù)據(jù)
- 3 = NETWORK_NO_SOURCE - 未找到音頻/視頻來源
一般用到的就是1和2,當(dāng)值為1的時候表示視頻已經(jīng)可以播放了(至少是當(dāng)前幀已經(jīng)加載好了,不過ios此時播放可能會白屏),值為2的時候播放下一幀會卡住(安卓)或者白屏(ios)。
注意:
- 返回的值是
Number類型,這使得在使用mint-ui這類的ui框架調(diào)試時可能會Toast一個空值,需要先轉(zhuǎn)為字符串才能顯示。 - 關(guān)于ios上返回2繼續(xù)播放會白屏的問題(僅僅出現(xiàn)在視頻第一次點(diǎn)擊播放的時候),這里我暫時沒發(fā)現(xiàn)是什么原因?qū)е碌模ㄓ锌赡苁且曨l太大??)
-
readyState
readyState 屬性返回音頻/視頻的當(dāng)前就緒狀態(tài)。
返回值如下- 0 = HAVE_NOTHING - 沒有關(guān)于音頻/視頻是否就緒的信息
- 1 = HAVE_METADATA - 關(guān)于音頻/視頻就緒的元數(shù)據(jù)
- 2 = HAVE_CURRENT_DATA - 關(guān)于當(dāng)前播放位置的數(shù)據(jù)是可用的,但沒有足夠的數(shù)據(jù)來播放下一幀/毫秒
- 3 = HAVE_FUTURE_DATA - 當(dāng)前及至少下一幀的數(shù)據(jù)是可用的
- 4 = HAVE_ENOUGH_DATA - 可用數(shù)據(jù)足以開始播放
這里的返回值也是Number類型的
我在項(xiàng)目中先使用的就是這個屬性,在返回1或2的時候代表剛進(jìn)入這個頁面瀏覽器還在加載視頻,返回4則代表整個視頻已經(jīng)加載完畢了??墒俏野l(fā)現(xiàn)ios在返回3的時候立即播放還是有時會出現(xiàn)白屏,而安卓在返回3的時候是可以播放的,因此最后我選擇了使用networkState來對視頻加載狀態(tài)進(jìn)行判斷。
- 不可思議的
currentTime
定義是這么說的:設(shè)置或返回音頻/視頻播放的當(dāng)前位置(以秒計(jì))。當(dāng)設(shè)置該屬性時,播放會跳躍到指定的位置。
而我使用這個屬性來判斷視頻是否能連續(xù)播放,當(dāng)視頻播放的時候如果這個屬性的值‘走動了’,可以認(rèn)為視頻已經(jīng)可以播放下一幀了。感謝h5 video 移動端填坑記這篇文章提供的方法。
方法
我一般用到的方法也就是play(),pause()這里不過多贅述。
事件
-
canplay
當(dāng)瀏覽器能夠開始播放指定的音頻/視頻時,會發(fā)生 canplay 事件。
乍一看有了這個事件似乎就不需要上面各種state去判斷視頻能否播放了,可惜萬惡的ios不支持這個事件,實(shí)測第一次進(jìn)入頁面ios在canplay事件觸發(fā)的時候視頻的networkState仍然處于2這個狀態(tài),也就是還未加載完成...... -
progress
當(dāng)瀏覽器正在下載指定的音頻/視頻時,會發(fā)生 progress 事件。
我使用這個事件的方向可能有點(diǎn)‘歪門邪道’,眾所周知一個視頻在頁面加載的時候等待時間或許會有點(diǎn)長,一般網(wǎng)站使用的是一個圖片或者gif去代替video標(biāo)簽,當(dāng)視頻加載好了的時候就讓video顯示出來。而上面的canplay用不了,所以我抱著試試的想法在progress事件觸發(fā)的時候讓loading圖隱藏起來(loading圖默認(rèn)顯示),結(jié)果實(shí)際效果讓我很滿意,progress事件觸發(fā)的時候視頻的poster已經(jīng)顯示出來了,我初步判斷第一次觸發(fā)這個事件應(yīng)該是視頻第一幀可能前幾幀都加載好了。 -
timeupdate
timeupdate 事件在音頻/視頻(audio/video)的播放位置發(fā)生改變時觸發(fā)。
這個事件一看就是結(jié)合上面的currentTime屬性用的。在后文我會詳述。 -
waiting
waiting 事件在視頻由于需要緩沖下一幀而停止時觸發(fā)。
在我的項(xiàng)目用它主要是因?yàn)閕os白屏的時候會觸發(fā)這個事件......這樣我可以在這個事件中讓視頻暫停。 -
ended
ended 事件在音頻/視頻(audio/video)播放完成后觸發(fā)。
由于我的視頻沒有用loop屬性所以我使用這個事件來提示用戶視頻播放結(jié)束。
應(yīng)用
項(xiàng)目基于vue,ui框架使用的是mint-ui,還是直接上代碼吧...
<div class="video-con fl">
<div class="video" @click="$_videoFromApp_getVideoRecord">
<!-- 后臺沒有視頻時顯示的內(nèi)容 -->
<img class="no-video" v-show="videoFromApp_noVideo" :src="require('@/images/icon/video.png')" alt="">
<div class="bg-gray" v-show="videoFromApp_noVideo"></div>
<!-- 視頻處于暫停時顯示的內(nèi)容 -->
<div v-show="!videoFromApp_playing">
<img class="play-video" v-show="!videoFromApp_noVideo"
:src="require('@/images/icon/play-video.png')" alt="">
<span class="play-time" v-show="!videoFromApp_noVideo">{{videoFromApp_videoTime}}</span>
</div>
<!-- loading -->
<mt-spinner v-show="videoFromApp_loading" class="loading-css" type="fading-circle"></mt-spinner>
<!-- 視頻 -->
<video webkit-playsinline playsinline
ref="indentVideo"
class="real-video"
:src="videoFromApp_videoSrc"
v-show="!videoFromApp_noVideo"
:poster="videoFromApp_videoImg"
@progress="$_videoFromApp_hasVideo"
@waiting="$_videoFromApp_waiting"
@ended="$_videoFromApp_endVideo"></video>
</div>
</div>
js部分
<script>
......
//播放視頻
$_videoFromApp_getVideoRecord(v){
......
if (this.videoFromApp_playing) {
this.videoFromApp_playing = false;
this.$refs.indentVideo.pause();
} else {
this.videoFromApp_playing = true;
let video = this.$refs.indentVideo;
let networkState = this.$refs.indentVideo.networkState;
let readyState = this.$refs.indentVideo.readyState;
if(networkState==1){
this.$refs.indentVideo.play();
this.videoFromApp_loading = true;
video.ontimeupdate = ()=>{
if(video.currentTime > 0.1){
this.videoFromApp_loading = false;
}
}
}else{
Toast({
message: '拼命加載中,請稍后',
position: 'bottom',
duration: 1000
});
this.videoFromApp_playing = false;
}
}
},
//視頻初步加載
$_videoFromApp_hasVideo(){
this.videoFromApp_loading = false;
},
//緩沖
$_videoFromApp_waiting(){
this.$refs.indentVideo.pause();
this.videoFromApp_playing = false;
Toast({
message: '拼命加載中,請稍后',
position: 'bottom',
duration: 1000
});
},
//視頻播放完成
$_videoFromApp_endVideo(){
this.videoFromApp_playing = false;
},
</script>
在此就簡述一下播放視頻方法,當(dāng)視頻處于暫停狀態(tài)時,點(diǎn)擊播放,判斷networkState,當(dāng)值為1的時候允許播放,此時監(jiān)聽timeupdate事件,(使用addEventListener監(jiān)聽會有毛病,如果看過我之前的一篇關(guān)于iframe的文章應(yīng)該會有所了解)先加上loading,如果currentTime > 0.1代表視頻已經(jīng)能流暢播放了,再隱藏loading。這個過程中如果無法播放的話就會走到waitting事件中,視頻會暫停。
再次點(diǎn)擊繼續(xù)重復(fù)這個過程直到視頻可以正常播放。
寫在后面
其實(shí)吧本文寫作的真正目的是為了拋磚引玉,文中所寫的一些方法只為解決項(xiàng)目中的燃眉之急,文中部分內(nèi)容僅僅為個人觀點(diǎn),如果各位讀者發(fā)現(xiàn)了文中的缺陷與問題,歡迎在評論區(qū)留言探討。
======================
2018-11-12更新
關(guān)于播放白屏的原因:最近發(fā)現(xiàn)安卓部分手機(jī)播放視頻又白屏了,最終總結(jié)出了避免白屏的方法。
1.播放視頻的格式最好是mp4 avc h.264格式的,不是這種格式的視頻用video播放很可能白屏。
2.如果視頻格式已經(jīng)是avch264的了那么就需要看看是后臺原因還是原生那邊的原因了,一般來說應(yīng)該是后臺的問題,ios目前獲取視頻的時候請求頭會帶一個與斷點(diǎn)續(xù)傳有關(guān)的信息,后臺需要對此進(jìn)行相應(yīng)的配置。而部分安卓手機(jī)也會用類似的方式請求視頻,目前項(xiàng)目里的后臺同事將返回的狀態(tài)碼從200改成了206就ok了。
最后,轉(zhuǎn)載請注明出處http://www.itdecent.cn/p/8f39050aa607