Krpano全景漫游播放视频-解决iOS端卡在第一帧的问题

444 阅读4分钟

前言

最近做的全景漫游项目中,要求点击场景中热点实现播放视频功能,具体要求:

  1. 在全景场景中的大屏上添加播放按钮;
  2. 点击播放按钮时弹出要播放的视频在屏幕上展示,并且自动开始播放;
  3. 点击播放器界面视频暂停;
  4. 点击周边空白区域视频播放界面消失;

其实单纯视频播放视频的功能还挺简单的,可以选择两种方式:

  1. 使用js实现播放功能,将点击热点事件绑定为js函数调用,通过参数传递形式传递要播放的视频地址;
  2. 使用Krpano提供的原生播放视频插件功能,本篇使用的该种方式;

具体实现效果如下(PC端的效果):

QQ20230522-090226-HD.gif

先附上具体实现步骤吧,最后说下如何解决在移动端播放效果不一致问题

步骤一:

  1. 我播放的是本地视频,在工程根目录下创建videos文件夹,存放要播放的视频,如果你想播放远程链接视频也是可以的。

  2. 添加一张播放按钮的图片放到images(在根目录下自定义创建)文件夹下,示例图片如下: media_playback_start.png

步骤二:

在需要播放视频的场景标签中,添加热点按钮,用来响应用户点击时弹出播放窗口,具体代码如下:

<!-- 添加视频热点 -->
<hotspot name="videospot"
    url="./images/media_playback_start.png"
    scale="0.6"
    zoom="true"
    ath="0" atv="5"
    onclick="looktohotspot(get(name),90);"
    onloaded=""
/>

注意这里,播放热点的点击响应事件的onclick事件,我想在点击按钮时,全景窗口的视角朝向播放按钮,使用Krpano自带的函数,looktohotspot,参数中get(name)获取到的是朝向的热点,90表示朝向的角度。

步骤三:

在plugins文件夹下创建video-player.xml文件,添加代码如下,关于代码解释可看代码注释部分:

<krpano>
    //播放视频热点按钮onloaded时调用事件,创建好要用来播放视频的视图
    <action name="videoplayer_open">
	if(layer[videoplayer_bg],
	        trace('videoplayer_open - there is already a videoplayer!'-);,
                 <!-- 添加播放视图的半透明背景插件 -->
		addlayer(videoplayer_bg);
		set(layer[videoplayer_bg].type, container);
		set(layer[videoplayer_bg].zorder, 999999);
		set(layer[videoplayer_bg].safearea, false);
		set(layer[videoplayer_bg].align, lefttop);
		set(layer[videoplayer_bg].width, 100%);
		set(layer[videoplayer_bg].height, 100%);
		set(layer[videoplayer_bg].bgcolor, 0x000000);
		set(layer[videoplayer_bg].bgalpha, 0.8);
		set(layer[videoplayer_bg].bgcapture, true);
		set(layer[videoplayer_bg].handcursor, false);
		set(layer[videoplayer_bg].alpha, 0.0);
		set(layer[videoplayer_bg].visible, false);
		set(layer[videoplayer_bg].enabled, false);
		set(layer[videoplayer_bg].onclick, videoplayer_close() );
		tween(layer[videoplayer_bg].alpha, 1.0, 0.5, default,
                     <!-- 添加播放视图插件 -->
                    addlayer(videoplayer_plugin);
                    set(layer[videoplayer_plugin].parent, layer[videoplayer_bg]);
                    set(layer[videoplayer_plugin].align, center);
                    set(layer[videoplayer_plugin].loop, true);
                    set(layer[videoplayer_plugin].onclick, togglepause() );
                    set(layer[videoplayer_plugin].alpha, 0.0);
                    set(layer[videoplayer_plugin].scale, 0.0);
                    set(layer[videoplayer_plugin].visible, false);
                    set(layer[videoplayer_plugin].enabled, false);
		    <!-- 这里1%为该action被调用传入的第一个参数,%2第二个,%3第三个 -->	
                    set(layer[videoplayer_plugin].onloaded, videoplayer_plugin_ready(%1) );
                    if('%2' != null, set(layer[videoplayer_plugin].posterurl,'%2'); );
                    if('%3' != null, set(layer[videoplayer_plugin].volume,%3); );
                    set(layer[videoplayer_plugin].url,'%VIEWER%/plugins/videoplayer.js');
		 );
            );
    </action>
    
    <!-- 判断是否在钉钉内内 -->
    <action name="getIsDingTalkBroswer">
	jsget(dingitem,isDingTalkBrowser());					
    </action>
    
    <action name="videoplayer_plugin_ready">
        getIsDingTalkBroswer();
	if(layer[videoplayer_plugin],
            set(events[videoplayer_events].onresize, videoplayer_plugin_resize() );
	    set(layer[videoplayer_plugin].pausedonstart, true);
	    set(layer[videoplayer_plugin].onvideoready,
		videoplayer_plugin_resize();
		tween(scale,1,0.5,easeOutBack);
		tween(alpha,1,0.5,default, ifnot(device.ios, play()));
	);
        <!-- 判断苹果设备上视频界面提前加载 -->
        if(device.ios,
            if(layer[videoplayer_plugin].visible == false,
		layer[videoplayer_plugin].pause();
            );
            if(layer[videoplayer_plugin].visible,
                set(layer[videoplayer_plugin].onclick, togglepause());
                layer[videoplayer_plugin].playvideo(%1);
		<!-- iOS内钉钉浏览器的加载位置 -->
		if(dingitem=="1",layer[videoplayer_plugin].playvideo(%1););
	    );
	
            <!-- iOS内非钉钉浏览器的视频加载位置 -->
            if(dingitem=="2",layer[videoplayer_plugin].playvideo(%1););
	 );
					
       if(layer[videoplayer_plugin].visible,
		layer[videoplayer_plugin].playvideo(%1);
        ); 
		
        <!-- 播放识别到触摸事件时回调方法 -->
	set(layer[videoplayer_plugin].ongottouch,
            if(layer[videoplayer_plugin].visible == false,
		layer[videoplayer_plugin].pause();
	    );
   	    if(layer[videoplayer_plugin].visible,
		if(layer[videoplayer_plugin].onclick == null,
			set(layer[videoplayer_plugin].onclick, togglepause());
                );
		if(layer[videoplayer_plugin].ispaused AND layer[videoplayer_plugin].visible,
                    layer[videoplayer_plugin].play();
		);
                if(layer[videoplayer_plugin].ispaused==false,
                    layer[videoplayer_plugin].pause();
		);
            );
	);
    );			
    </action>
    <!-- 点击播放按钮事件 -->
    <action name="videoplayer_begin_play">
	  set(events[videoplayer_events].onresize, videoplayer_plugin_resize());
          set(layer[videoplayer_bg].visible, true);
	  set(layer[videoplayer_plugin].visible, true);
          set(layer[videoplayer_plugin].enabled, true);
          set(layer[videoplayer_bg].enabled, true);
          layer[videoplayer_plugin].playvideo(%1);
	  if(layer[videoplayer_plugin], layer[videoplayer_plugin].play());
    </action>
    
    <action name="videoplayer_plugin_resize">
        <!-- use 90% width or height of available screen size -->
	div(aspect, layer[videoplayer_plugin].imagewidth, layer[videoplayer_plugin].imageheight);
	mul(new_videowidth, stagewidth, 0.90);
	div(new_videoheight, new_videowidth, aspect);
	mul(max_videoheight, stageheight, 0.90);
	if(new_videoheight GT max_videoheight,
            copy(new_videoheight, max_videoheight);
            mul(new_videowidth, new_videoheight, aspect);
	);
	roundval(new_videowidth);
	roundval(new_videoheight);
	copy(layer[videoplayer_plugin].width, new_videowidth);
	copy(layer[videoplayer_plugin].height, new_videoheight);
    </action>
    
    <action name="videoplayer_close">
        set(layer[videoplayer_bg].visible, false);
	set(layer[videoplayer_plugin].visible, false);
	set(layer[videoplayer_plugin].enabled, false);
	set(layer[videoplayer_bg].enabled, false);
	if(layer[videoplayer_plugin], layer[videoplayer_plugin].stop());		
    </action>
</krpano>

在iOS系统,微信打开网页,使用krpano播放视频,会卡在第一帧,需要用手点击开始或者触屏才能播放。这是因为iOS系统原因,考虑到在移动端不浪费用户流量,但是为了保证用户交互的统一性,我们采用曲线救国的方式,实现在iOS端也能自动播放。另外本次还发现了一个问题:iOS端的微信浏览器和钉钉浏览器的模式不一样,微信浏览器不能自动播放,但是钉钉浏览器可以,所以需要区分一下是在哪个app中打开,完成不同的视频预加载策略,判断是钉钉的方案如下,因为代码已经实现了默认在iOS微信加载的策略,所以只需要判断在钉钉中打开即可:

//判断是否是钉钉或者支付宝内浏览器(krpano中调用)
function isDingTalkBrowser() {
    let ua = navigator.userAgent.toLowerCase();
    return ua.indexOf("dingtalk") !== -1 || ua.match(/Alipay/i) == "alipay" ? "1" : "2";
}

可将以上代码放到index.html文件中,这段代码的调用在video-player.xml中,具体调用如下:

    <!-- 判断是否在钉钉内内,如果是在钉钉中打开,dingitem被赋值1,否则返回2 -->
    <action name="getIsDingTalkBroswer">
	jsget(dingitem,isDingTalkBrowser());					
    </action>

解决在iOS端加载视频界面不能自动播放的核心逻辑如下:

<!-- 判断苹果设备上视频界面提前加载 -->
if(device.ios,
   if(layer[videoplayer_plugin].visible == false,
	layer[videoplayer_plugin].pause();
   );
   if(layer[videoplayer_plugin].visible,
        set(layer[videoplayer_plugin].onclick, togglepause());
        layer[videoplayer_plugin].playvideo(%1);
	<!-- iOS内钉钉浏览器的加载位置 -->
	if(dingitem=="1",layer[videoplayer_plugin].playvideo(%1););
   );
   <!-- iOS内非钉钉浏览器的视频加载位置 -->
   if(dingitem=="2",layer[videoplayer_plugin].playvideo(%1););
);			

步骤四:

完善视频播放热点按钮的回调事件,具体代码如下:

<!-- 添加视频热点 -->
<hotspot name="videospot"
    url="./images/media_playback_start.png"
    scale="0.6"
    zoom="true"
    ath="0" atv="5"
    onclick="looktohotspot(get(name),90);videoplayer_begin_play('%CURRENTXML%/videos/kkchina.mp4')"
    onloaded="videoplayer_open('%CURRENTXML%/videos/kkchina.mp4', '%CURRENTXML%/images/videoplay.png', 1);"
/>

以上方案即可实现在Krpano中实现视频播放功能,并且解决在iOS端微信或者Safari浏览器中浏览全景网页播放视频卡在第一帧的问题。 注意:不要忘记在tour.xml中引入video-player.xml插件

<include url="./plugins/video-player.xml" />

其实在Krpano中播放视频,没有那么复杂,本篇文章之所以复杂,是为了解决在iOS端播放视频时在微信或者Safari(官方浏览器内核)不能自动播放(卡在第一帧的问题),在微信中打开播放视图效果如下,点击播放按钮弹出播放视图后可以自动播放:

QQ20230522-104555-HD.gif