【Cloud VR】 华为SDK集成3步曲
1 认知CloudVR
VR云化后,云端可以提升逻辑计算、图像处理能力。超多核服务器、GPU集群、云的分布式计算能力均能得到很好体现,利用最新的GPU技术做渲染和人工智能做分析的能力来弥补独立VR终端的不足。
Cloud VR时代,终端实现无绳化,轻便、佩戴方便,便于更多类型终端接入。同时,用户还可以体验到完美的多屏融合和多屏分享,网络化多人互动VR。
华为CloudVR平台将游戏和应用的渲染搬到云上,只在终端上做上传游戏操作流和播放远程渲染的游戏画面。本文旨在指导开发者将应用集成到CloudVR平台上需要如何集成华为提供的云渲染SDK。
2 华为端侧SDK的开放API
2.1 HuaweiCloudRender类
这个类旨在提供给开发者,如何去设置参数和远程渲染服务器做交互,上传数据流和将渲染画面从服务器下发下来。
init | 初始化SDK。 |
setConsoleLog | 设置adb logcat输出级别。 |
setFileLog | 设置日志文件路径和级别。 |
create | 创建SDK实例。 |
setOnInfoListener | 设置Info信息通知监听。 |
setreplaceStringListener | 设置Error信息通知监听。 |
setVrSurface | 设置游戏播放的画面surface。 |
startRender | 开始渲染应用。 |
sendHmdTracking | 上报头部姿态数据。 |
sendControllerTracking | 上报手柄姿态和按键数据。 |
stopRender | 停止渲染应用。 |
destroy | 销毁SDK实例。 |
2.2 配套Unity中的API
SDK提供的libUnityMediaSurface.so,需要在C#代码中显式导入。这个库的主要作用是设置和刷新播放器的承载画面的view,设置给播放器的view是不同于普通2D视频的,需要将View进行特殊设置。此类的主要作用是将从服务器上的视频流,刷新到播放器的View。
Unity_Media_Surface_Init() | MediaSurface库初始化接口 |
Unity_Media_Surface_SetTextureId | 给MediaSurface设置获取到的Unity的TextureID |
Unity_Media_Surface_SetEventBase | 设置事件的基数,在Init后调用,后续发送GL.IssuePluginEvent事件时以此base为基数。 |
Unity_Media_Surface_SetTextureParms | 设置Texture的宽高,与实际的界面宽高一致。 |
Unity_Media_GetTextureObject | 获取SurfaceTexture对象实例。 |
Unity_Media_Surface_Update | 对Surface的内容进行Update操作,返回对应图像的frameIndex值,用于刷新图像和界面模型。 |
3 Android UI模块集成HuaweiCloudRender类
集成HuaweiCloudRender类,初始化SDK。此调用需要尽早,建议在application类或者主Activity的onCreate方法中调用HuaweiCloudRender.setGlobalConfig设置全局配置变量,HuaweiCloudRender.setFileLog设置日志打印路径。创建SDK实例,对HuaweiCloudRender类的实例,设置Info信息通知监听,设置Error信息通知监听。不赘述了比较直观。
private void initCloudRender() { HuaweiCloudRender.init(this); HuaweiCloudRender.setGlobalConfig("APIGW_PARA.APIGW_UPLOAD_URL", mConfig.getApigwUploadUrl()); HuaweiCloudRender.setGlobalConfig("APP_DEMO_PARA.RENDER_SERVICE_URL", mConfig.getServiceUrl()); HuaweiCloudRender.setFileLog(HCRLogLevel.HCR_LOG_LEVEL_DEBUG, "/sdcard/CloudVRGame/log/"); mPlayer = HuaweiCloudRender.create(); mPlayer.setreplaceStringListener(this); mPlayer.setOnInfoListener(this); mPlayer.authenticate(mConfig.getAppServerUrl(), "myName", "myUserID"); }
设置Error信息通知监听,当发生一些Error事件时,回调给UI界面。设置Info信息通知监听,当有Info事件发生时,回调到UI界面。Authenticate去向服务器校验是否ip地址在白名单。每次在unity里面刷新3D场景的时候,通过调用头盔和时间。mPlayer.sendHmdTracking发送头盔数据到服务端。
private void sendControllerTrackingInfo(int leftEnable, int leftBtn, float[] leftFloat, int rightEnable, int rightBtn, float[] rightFloat) { mPlayer.sendControllerTracking(left_Enable, left_Btn, left_Float, right_Enable, right_Btn, right_Float); }
mPlayer.sendControllerTracking发送手柄元数据到服务端。
private void sendHmdTrackingInfo(int frameIndex, float[] pose) { mPlayer.sendHmdTracking(frameIndex,pose); Utils.writeLog("oooo", printFloatArray1(hmd_float)); }
4 Unity模块设置交互java模块[Z(3]
集成SDK提供的libUnityMediaSurface.so,设置播放器view,和每帧刷新展示效果,每帧调用java模块去上传数据和拉流。如下所示。这个模块当时开发的时候也很蒙,最后找出一个合理的解释那就是,通过unity调用这个动态库生成一个异于2D的view,然后设置给播放器。每帧去刷游戏渲染的画面
1、在unity工程里面每帧刷新的时候调用通过AndroidJNIHelper,在untiy里面调用java代码,每次去上传手柄数据和头盔元数据到服务端[Z(4] 。见如下代码段。
private void sendCtrollerTrack(HandInfo leftHand, HandInfo rightHand) { float[] leftFloat = { leftHand.triggerValue, leftHand.touchpadAxis.x, leftHand.touchpadAxis.y, leftHand.rotation.x, leftHand.rotation.y, leftHand.rotation.z, leftHand.rotation.w, leftHand.position.x, leftHand.position.y, leftHand.position.z }; float[] rightFloat = { rightHand.triggerValue, rightHand.touchpadAxis.x, rightHand.touchpadAxis.y, rightHand.rotation.x, rightHand.rotation.y, rightHand.rotation.z, rightHand.rotation.w, rightHand.position.x, rightHand.position.y, rightHand.position.z }; IntPtr jLeft = AndroidJNIHelper.ConvertToJNIArray(leftFloat); IntPtr jRight = AndroidJNIHelper.ConvertToJNIArray(rightFloat); jvalue[] param = new jvalue[6]; param[0].i = leftHand.enable ? 1 : 0; param[1].i = (int)leftHand.button; param[2].l = jLeft; param[3].i = rightHand.enable ? 1 : 0; param[4].i = (int)rightHand.button; param[5].l = jRight; if (controllerMethod == IntPtr.Zero) { controllerMethod = AndroidJNIHelper.GetMethodID(currentActivity.GetRawClass(), "sendControllerTrackingInfo"); } AndroidJNI.CallVoidMethod(currentActivity.GetRawObject(), controllerMethod, param); }
2、设置播放器需要展示view,然后在view里面去初始化播放器。见如下代码段。
void initUnityMediaSurface() { Debug.Log("xxx vrplayer surface initUnityMediaSurface"); Unity_Media_Surface_Init(); eventBase = 15; Renderer renderer = GetComponent<Renderer>(); if (renderer.material == null || renderer.material.mainTexture == null) { Debug.LogError("xxx vrplayer surface Can't GetNativeTextureID()"); } nativeTextureID = renderer.material.mainTexture.GetNativeTextureID(); IssuePluginEvent(MediaSurfaceEventType.Initialize); }
其中MediaSurface库用来初始化播放器需要的view,并且每次在view上刷新服务器传送过来的流。在Unity的C#代码中导入下面函数,并且进行合理顺序设置。详细使用参考3中3.2 untiy库调用接口说明。
[DllImport("UnityMediaSurface")] private static extern void Unity_Media_Surface_Init();
3、需要在C#中先获取当前Unity Activity(包含UnityPlayer的Activity),即是1中集成HuaweiCloudRender类的Activity类。
需要在Activity中添加函数:void setSurfaceTexture(SurfaceTexture texture)。需要为SurfaceTexture添加onFrameAvailable回调,在回调中通知C#代码刷新TexImage。这里很重延的奥,这边是将untiy里面设置的view传给java代码去设置给播放器奥。
private void setSurfaceTexture(SurfaceTexture texture) { mSurfaceTexture = texture; mSurfaceTexture.setDefaultBufferSize(mConfig.getVideoWidth(), mConfig.getVideoHeight()); mSurface = new Surface(mSurfaceTexture); mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { sendMessageToUnity("UpdateTexImage", ""); // if someTime no new frame, stop game sendHandlerMessage(MSG_NO_FRAME_DISPLAY, mDefaultExitGameDelayTime * 1000); } }); sendHandlerMessage(MSG_SURFACE_READY); }
在Unity模块去设置view,然后通过java模块去调用HuaweiCloudRender和后台服务器进行交互奥。
void OnEnable() { Debug.Log("kkk vrplayer surface start "); initUnityMediaSurface(); currentActivity = Utils.GetActivity(); Debug.Log("xxx Surface currentActivity:" + currentActivity); mSurfaceWidth = currentActivity.Call<int>("getVideoWidth"); mSurfaceHeight = currentActivity.Call<int>("getVideoHeight"); StartCoroutine(DelayedStartVideo()); }
如下设置view参数。
private void InitSurfaceTexture(int textureId) { Debug.Log("xxx vrplayer surface InitSurfaceTexture "); Unity_Media_Surface_SetTextureId(textureId); Unity_Media_Surface_SetTextureParms(mSurfaceWidth, mSurfaceHeight); //this size should be same as the texture IntPtr surfaceTexture = Unity_Media_GetTextureObject(); IntPtr setTextureMethodId = AndroidJNI.GetMethodID(currentActivity.GetRawClass(), "setSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)V"); if ((int)setTextureMethodId == 0) { surfaceInited = false; return; } jvalue[] parms = new jvalue[1]; parms[0] = new jvalue(); parms[0].l = surfaceTexture; AndroidJNI.CallVoidMethod(currentActivity.GetRawObject(), setTextureMethodId, parms); surfaceInited = true; }
每帧刷新页面。
public long UpdateSurface() { if (surfaceInited) { long frameIndex = Unity_Media_Surface_Update() / 1000000; return frameIndex; } return 0; }
- 点赞
- 收藏
- 关注作者
评论(0)