【Cloud VR】 华为SDK集成3步曲
【摘要】 Cloud VR时代,终端实现无绳化、轻便、佩戴方便,和传统VR相比,没有场地限制、成本被分担、部署成本低,将应用部署到云上可以用提升更多的算力。华为CloudVR平台提供了一套规范的接入操作,简单易行,快让你的VR应用上云吧!!!!!!
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;
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)