背景需求
最近接到一個需求,就是用webview在加載帶視頻類的網(wǎng)頁的時候,使用本地的播放器去替換webview中的播放器,然后使用本地播放器去播放,如果對需求不太清楚可以去使用夸克瀏覽器,然后找一個視頻小站播放個視頻就清楚了。
面臨的問題有哪些?
1 如何獲取webview加載頁面中的視頻鏈接
2 如何控制webview原生播放器的(因為你需要暫停原始播放器的播放)
3 本地的播放器如何嵌入webview中原生播放器的位置,并跟隨滾動
獲取webview頁面中的視頻url
這個其實就相當(dāng)于瀏覽器的資源嗅探功能,實現(xiàn)方式有三種
- js獲取,在webview中注入下面這段js
var isPlayOnWeb=false;
var interval = setInterval(function videoTagFinder() {
var videoTags = document.getElementsByTagName("video");
for (var index =0; index < videoTags.length; index++) {
var videoTag = videoTags[index];
if (videoTag.getAttribute("marked") =="1") {
continue;
}
videoTag.addEventListener("webkitbeginfullscreen", onEnterFullScreenEvent,true);
videoTag.addEventListener("webkitendfullscreen", onExitFullScreenEvent,true);
videoTag.addEventListener("play", onVideoPlayEvent,true);
videoTag.addEventListener("pause", onVideoPauseEvent,true);
videoTag.setAttribute("position",index)
videoTag.setAttribute("marked","1");
}
function onEnterFullScreenEvent() {
}
function onExitFullScreenEvent() {
}
function onVideoPlayEvent() {
if(!isPlayOnWeb){
checkIsNeedUsePlayerOfApp(this)
}
}
function onVideoPauseEvent() {
}
function checkIsNeedUsePlayerOfApp(videoTag) {
var i = setInterval(function () {
notifyAppPlayerToPlay(videoTag);
videoTag.pause();
clearInterval(i)
},200)
}
/**
* notify app to play video
* @param videoTag
*/
function notifyAppPlayerToPlay(videoTag) {
var videoSrc = videoTag.currentSrc;
var sourceArr =new Array();
var sources = videoTag.getElementsByTagName("source");
if (sources !=null && sources.length >0) {
for (var index =0; index < sources.length; index++) {
var sourceInfo = {
'url': sources[index].getAttribute("src"),
'media': sources[index].getAttribute("media"),
'type': sources[index].getAttribute("type")
};
sourceArr.push(sourceInfo);
}
}
var videoInfo = {
'videoUrl': videoSrc,
'source': sourceArr,
'title': document.title,
'url': document.URL,
'position':videoTag.getAttribute('position')
};
// 調(diào)用native方法
window.JSToNative.webViewPlayVideoAtURL(JSON.stringify(videoInfo));
}
},300)
function setPlayOnWeb(){
isPlayOnWeb=true;
}
單單通過上面這段js僅僅可以獲取Video標(biāo)簽的視頻url,像一些盜版站點通常會采用IFrame引入其他站點的視頻,這樣這種方式就失效了,js功底比較弱,暫時沒有非常好的js解決方案。
- 攔截所有瀏覽器的請求,對請求結(jié)果進行分析(推薦)
(VBrowser)
這種方法獲取的視頻url比較準(zhǔn)確,但是速度稍微慢一些
這個開源項目就是類似的原理,獲取Webview發(fā)出的所有請求,然后對響應(yīng)頭進行分析,主要是對mime-type進行分析,其中可以通過Head請求進行探測優(yōu)化
- 瀏覽器內(nèi)核獲取(如果你的項目支持的自有內(nèi)核的話)
控制WebView原生播放器
在已經(jīng)獲取視頻地址的情況下,需要對Webview原生播放器進行控制。
這里分為兩種情況Video標(biāo)簽和IFrame標(biāo)簽。
針對video標(biāo)簽,可以繼續(xù)使用上面那段js,它里面已經(jīng)對Video標(biāo)簽進行了控制,關(guān)鍵是對IFrame標(biāo)簽的控制,我的解決方案也比較粗暴。
!function(){
var iframeTags = document.getElementsByTagName("iframe");
var ifmParent;
var parentHeight = 250;
for (var index = 0; index < iframeTags.length; index++) {
var iframeTag = iframeTags[index];
if(iframeTag.offsetHeight >500){
canPlay = false;
break;
}
if(iframeTag.src != "" && iframeTag.offsetHeight>150){
var url = window.location.href;
ifmParent = iframeTag.parentNode;
if(url.search('http://www.haitum.cn') != -1){
ifmParent = iframeTag.parentNode.parentNode;
}
if(ifmParent.style.paddingTop){
ifmParent.style.paddingTop="0%";
}
parentHeight = iframeTag.offsetHeight
iframeTag.remove();
break;
}
}
ifmParent.innerHTML = "<div id='player_area' style='background:#000; position:relative;width:100%; height:"+parentHeight+"px;'><img id='playimg_sg_webvideo' src='http://xxxx.webvideo.play' height='100px' width='100px' style='height:100px; width:100px; position:absolute;top:50%; left:50%;transform: translate(-50%,-50%); '/> </div>";
var playIcon = document.getElementById("playimg_sg_webvideo");
playIcon.onclick = function () {
window.JSToNative.iFramePlayVideo();
}
}()
原理是直接將Iframe標(biāo)簽移除,然后自己在原IFrame標(biāo)簽位置蓋上一個播放按鈕,這樣就可以自己造一個播放按鈕,捕獲用戶點擊播放的時機。
將本地播放器嵌入webview的播放器區(qū)域,并跟隨滾動
var videoTags
function startVideoScroll(isVideo,position){
videoTag = document.getElementsByTagName("VIDEO")[position];
if(videoTag==null){
videoTag = document.getElementById('player_area');
if(videoTag==null){
return;
}
}
//首先調(diào)用一次,作為初始化位置
getVideoPosition(videoTag)
}
// 滑動監(jiān)聽video標(biāo)簽位置
function getVideoPosition(video){
var scrollTop=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;//復(fù)雜模式
var pageWidth = document.body.clientWidth || document.body.offsetWidth;
//標(biāo)簽距離頂部-卷去高度=標(biāo)簽距離屏幕頂部高度
var videoPositionStatus = 0;//視頻在頁面位置的5種標(biāo)識
var marginPageTopHeight = getPosition(video)-scrollTop//距離頂部高度
if(marginPageTopHeight>=0){
if(marginPageTopHeight<window.innerHeight && (marginPageTopHeight+video.clientHeight)<window.innerHeight){
videoPositionStatus=1
//console.log("視頻完全在屏幕中")
}else{
if(window.innerHeight+scrollTop<getPosition(video)){
videoPositionStatus=1
//console.log("視頻在底部不可見")
}else{
videoPositionStatus=3
//console.log("視頻在底部遮擋一部分")
}
}
}else{
if((getPosition(video)+video.clientHeight)<scrollTop){
videoPositionStatus=4
//console.log("視頻在頂部不可見")
}else{
videoPositionStatus=5
//console.log("視頻在頂部遮擋一部分")
}
}
window.JSToNative.getVideoPosition(videoPositionStatus+":"+getPosition(video)+":"+video.clientWidth+":"+video.clientHeight+":"+getOffsetLeft(video) + ":" + pageWidth);
}
//元素距離頂部坐標(biāo)
function getPosition(el) {
var x = 0,
y = 0;
while (el != null && (el.tagName || '').toLowerCase() != 'html') {
x += el.offsetLeft || 0;
y += el.offsetTop || 0;
el = el.offsetParent;
}
return parseInt(y, 10) ;
}
//元素距離左邊距離
function getOffsetLeft(ele){
var p=ele.offsetParent;
var left=ele.offsetLeft;
while(p){
if(window.navigator.userAgent.indexOf("MSIE 8")>-1){
left+=p.offsetLeft;
}else{
left+=p.offsetLeft+p.clientLeft;
}
p=p.offsetParent;
//left+=p.offsetLeft;
// p=p.offsetParent;
}
var obj={};
obj.x=left;
return obj.x;
}
上面這段js可以用來獲取Video標(biāo)簽或者IFrame標(biāo)簽的初始化位置,然后在配合對Webview的滑動進行監(jiān)聽就可以做到將本地播放器嵌入Webview了。
功能實現(xiàn)了,但是感覺沒有夸克做的好,誰有更好的解決方案也可以留言討論下。