TV SDK 开发指南
文档版本号:20220507-104
变更历史:
SDK版本号 | 日期 | 更新内容 |
---|---|---|
master-5.15 | 2022/5/7 | 1、修改发送消息接口函数为有返回boolean值函数,表示当前长链接是否连接成功 |
master-5.12 | 2022/3/23 | 1、新增视频帧卡顿通知接口 2、修改IDC节点测速接口、获取清晰度列表接口、获取游戏存档状态接口、查询是否存在游戏存档接口、检测未释放游戏接口、根据cid释放实例接口 增加带游戏BID参数方法 |
master-5.5.2 | 2021/11/1 | 1、支持RTC流类型,见申请服务 2、设置可存档最小游戏时间并退出游戏,见设置可存档最小游戏时间并退出游戏接口 3、游戏启动参数携带执行脚本,见游戏启动参数携带执行脚本接口 4、将数据写入云端的文本框,见将数据写入到云端的文本框接口 5、与云游戏传输数据,见与云游戏传输数据接口 |
1 产品简介
海马云游戏Android TV SDK是一款助力快速部署云游戏产品的软件开发工具包。 您可以通过调用海马云游戏Android TV SDK接口,实现云游戏的播放、停止、状态回调等各类控制操作和数据交互,在海马云游戏端到端全栈云服务能力基础上,为用户带来顺畅的云游戏体验。以下简称SDK。
2 工程配置
这里向您介绍适用于Android TV开发的工程配置。
2.1 集成SDK
在Module的build.gradle文件中,添加依赖属性:
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
implementation 'com.alibaba:fastjson:1.2.0'
implementation('io.socket:socket.io-client:1.0.0') {
exclude group: 'org.json', module: 'json'
}
implementation (name: 'saas-sdk-latest.release', ext: 'aar')
}
⚠注意:
SDK目前只支持通过gradle集成方式。
saas-sdk-latest.release是海马的SDK,其他为海马SDK的依赖包,saas-sdk-latest.release根据实际提供的aar文件进行修改(.aar文件的部分)。
如果无法正常集成,请在Project的build.gradle文件中配置repositories,添加 jcenter:
allprojects { repositories { jcenter() } }
2.2 基础配置
在AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
为避免混淆海马SDK,在Proguard混淆文件中增加以下配置:
-keep interface com.haima.hmcp.listeners.*{*;}
-keep class com.haima.hmcp.beans.*{*;}
-keep enum com.haima.hmcp.enums.*{*;}
-keep class com.haima.hmcp.**{*;}
-keep enum com.haima.hmcp.websocket.WebSocketCloseNotification{*;}
-keep interface com.haima.hmcp.websocket.WebSocket{*;}
-keep interface com.haima.hmcp.websocket.WebSocketConnectionObserver{*;}
-keep class com.haima.hmcp.websocket.WebSocketConnection{public <methods>;}
-keep class com.haima.hmcp.websocket.WebSocketOptions{public <methods>;}
-keep class com.haima.hmcp.websocket.WebSocketException{*;}
-keep class tv.haima.ijk.media.player.** { *; }
-keep interface tv.haima.ijk.media.player.listeners.*{*;}
-keep interface tv.haima.ijk.media.player.IMediaPlayer{*;}
-keep class com.netease.LDNetDiagnoService.LDNetDiagnoService{public <methods>;}
-keep interface com.netease.LDNetDiagnoService.LDNetDiagnoListener{public <methods>;}
-keep class com.netease.LDNetDiagnoService.LDNetTraceRoute { *; }
-keep class org.hmwebrtc.**{*;}
-keep class org.webrtc.haima.**{*;}
-keep class io.socket.**{*;}
-keep class com.haima.hmcp.rtc.widgets.RtcTextureViewRenderer{*;}
2.3 配置硬件加速
打开硬件加速可以提高渲染能力,提高用户体验。
<application ……
android:hardwareAccelerated="true">
2.4 添加组件
在布局文件中添加 HmcpVideoView 组件:
<com.haima.hmcp.widgets.HmcpVideoView
android:id="@+id/gameView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"/>
HmcpVideoView 组件所在 Acitvity 需要设置以下属性:
<activity
android:name=".xxxActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleTask"
android:screenOrientation="landscape" />
2.5 外接键鼠手柄操作
如果支持外接键鼠手柄操作,需进行如下操作:
public boolean onKeyDown(int keyCode, KeyEvent event) {
return true;
}
public boolean onKeyUp(final int keyCode, KeyEvent event) {
return true;
}
3 快速开始
这里向您介绍适用于Android-TV开发的快速接入文档。
表3-1 重要接口说明
重要接口 | 接口含义 | 建议调用时机 |
---|---|---|
init() | 初始化 SDK | Application 的 onCreate() |
setUserInfo() | 设置用户登录信息 | init() 成功回调后 |
generateCToken() | 计算 cToken 的值 | play(bundle) 调用前 |
play(bundle) | 与海马云 Server 建立通讯、申请服务,成功后则可以开启游戏 | play()方法调用前 |
play() | 开启游戏 | HmcpPlayerStatusCallback回调方法,返回JSON字符串中,status为1时 |
⭕️说明:
SDK使用前请对工程进行配置,否则SDK不生效。
SDK的接口调用要在同一个线程下。
SDK回调信息请参见消息回调接口。
3.1 初始化SDK
按照工程配置,已在AndroidManifest.xml文件中配置渠道信息HMCP_ACCESS_KEY_ID和HMCP_CHANNEL_ID,接入方需调用init方法初始化SDK,否则需通过Bundle对ACCESS_KEY_ID与CHANNEL_ID进行赋值。
初始化方法建议在自定义Application类中进行,如果成功回调success()方法,则初始化成功。
请求接口报错时返回的错误信息默认是普通字符串格式,若需要返回JSON格式,需要isJson参数传入true。
⚠注意:
isJson影响的接口除了init之外还有:更新uid和游戏时长接口、获取游戏存档状态接口、查询是否存在游戏存档接口、检测未释放游戏接口、根据cid释放实例接口、开始直播接口、停止直播接口、获取授权码接口。
函数原型:
public class HmcpManager {
public void init(Context context, OnInitCallBackListener callBack)
public void init(Bundle bundle, Context context, OnInitCallBackListener callBack)
public void init(Bundle bundle, Context context, OnInitCallBackListener callBack,boolean isJson)
}
表3-2 bundle参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
bundle | Bundle | 是 | 使用携带bundle的参数初始化SDK,渠道信息将使用bundle中携带信息 |
context | Context | 是 | 应用程序上下文对象 |
callBack | OnInitCallBackListener | 是 | 初始化方法接口回调监听器 |
表3-3 key说明
key | value 类型 | 必填项 | 说明 |
---|---|---|---|
HmcpManager.ACCESS_KEY_ID | String | 是 | 接入方的唯一ID,用来区分不同的接入方,该值由海马云分配 |
HmcpManager.CHANNEL_ID | String | 是 | 渠道号,由接入方配置。如果应用本身不区分渠道,可以设置为一个随机的字符串;参数由接入方自己定义,请注意不要跟appChannel混淆 |
示例代码:
HmcpManager manager = HmcpManager.getInstance();
// bundle.putString(HmcpManager.ACCESS_KEY_ID, mBid);
// 设置ACCESS_KEY_ID
// bundle.putString(HmcpManager.CHANNEL_ID, mChannelID);
// 设置CHANNEL_ID
// 带bundle参数请调用该方法
manager.init(this, new OnInitCallBackListener() {
@Override
public void success() {
// 初始化SDK成功,可以云游戏
}
@Override
public void fail(String s) {
// 初始化SDK失败,不能云游戏
}
});
//需要错误信息为JSON格式
manager.init(bundle,this, new OnInitCallBackListener() {
@Override
public void success() {
// 初始化SDK成功,可以云游戏
}
@Override
public void fail(String s) {
// 初始化SDK失败,不能云游戏
//{"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}
}
},true);
3.2 Activity中初始化组件
以下是 Activity 中的示例代码:
public class HmcpPlayerActivity extends AppCompatActivity implements HmcpPlayerListener {
private HmcpVideoView hmcpVideoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
// 获取 HmcpVideoView 组件
hmcpVideoView = (HmcpVideoView) this.findViewById(R.id.gameView);
}
// Activity需要implements HmcpPlayerListener接口
@Override
public void onError(ErrorType errorType, String s) {
// 出错信息回调
}
@Override
public void onSuccess() {
// SDK启动成功并且开始播流的回调
}
@Override
public void onMessage(Message message) {
// 收到退出登录消息回调
}
@Override
public void onPlayStatus(int status, long value, String data) {
// 帧率fps,带宽Bps上报。
}
@Override
public void onExitQueue() {
// 游戏内部弹窗退出消失回调
this.finish();
}
@Override
protected void onStart() {
hmcpVideoView.onStart();
super.onStart();
}
@Override
protected void onRestart() {
hmcpVideoView.onRestart(int time);
super.onRestart();
}
@Override
protected void onResume() {
hmcpVideoView.onResume();
super.onResume();
}
@Override
protected void onPause() {
hmcpVideoView.onPause();
super.onPause();
}
@Override
protected void onStop() {
hmcpVideoView.onStop();
super.onStop();
}
@Override
protected void onDestroy() {
if (hmcpVideoView != null) {
hmcpVideoView.onDestroy();
}
super.onDestroy();
}
@Override
public void onPlayerError(String errorCode, String errorMsg) {
//SDK上报错误信息回调接口
}
@Override
public void HmcpPlayerStatusCallback(String callback) {
//SDK使用状态回调接口
}
@Override
public void onSceneChanged(String sceneMessage) {
//SDK游戏过程中各状态发生变化时通知应用消息接口
}
@Override
public void onNetworkChanged(NetWorkState netWorkState) {
//网络变化后通知应用的接口
}
@Override
public void onPermissionNotGranted(String permission) {
//SDK没有敏感权限时回调(比如游戏途中需要语音连麦)
//在申请到连麦权限时需要调用hmcpVideoView.startRecord重新拉起录音
}
@Override
public void onCloudDeviceStatus(String status) {
//云游戏中分享、打开摄像头、打开系统相册、游戏内截图
}
@Override
public void onInterceptIntent(String intentData) {
//intent拦截回调
}
}
其中:
Activity的onResume()、onPause()、onDestroy()必须重写,并在重写的方法中调用HmcpVideoView的相应方法,可以参考上面的示例代码。
Activity需要实现(implement)HmcpPlayerListener接口。
onPlayerError(String errorCode,String errorMsg)是SDK上报播流错误信息回调接口,errorMsg是错误信息,errorCode是错误码。
HmcpPlayerStatusCallback(String callback)是SDK运行状态回调接口,参数callback为回调信息。详情请参见SDK状态回调。
onSceneChanged(String sceneMessage)是SDK游戏过程中各状态发生变化时通知应用的接口,参数sceneMessage是包括sceneId和extraInfo{}两个字段的JSON串。详情请参见场景切换回调。
onNetworkChanged(NetWorkState state)是SDK在监听到网络变化后通知应用的接口,参数NetWorkState为枚举类型,现在包括ISWIFI、NOTWIFI、NO_NETWORK三种网络状态。
onInputDevice(int device, int operationType)是SDK在监听到输入设备变化时通知应用的接口。
表3-4 device类型
类型 类型描述 INPUT_DEVICE_NONE 未检测到输入设备 INPUT_DEVICE_REMOTE_CONTROL 遥控器输入 INPUT_DEVICE_KEY_MOUSE 键盘鼠标输入 INPUT_DEVICE_GAMEPAD 实体手柄输入 INPUT_DEVICE_VIRTUAL_GAMEPAD 虚拟手柄输入 表3-5 operationType类型
类型 类型描述 INPUT_DEVICE_OPERATION_TYPE_VIRTUAL_BUTTON 虚拟按键 INPUT_DEVICE_OPERATION_TYPE_BUTTON 实体按键 INPUT_DEVICE_OPERATION_TYPE_VIRTUAL_TOUCH 虚拟触摸 onPermissionNotGranted(String permission)是SDK在没有相关敏感权限时的回调接口,需要APP申请对应权限。
onCloudDeviceStatus(String status)是云游戏中远端实例状态发生改变的回调接口,包括云游戏中分享、打开摄像头、打开系统相册、游戏内截图,详情请参见云游戏中远端实例通知接口。
onInterceptIntent(String intentData)是云游戏内实现免登等需求时进行Intent拦截的回调接口。详情请参见Intent拦截接口。
3.3 设置用户登录信息
用户登录信息包括userId和userToken。
- userId作为游戏客户端用户的唯一识别码,原则上为APP端的用户登录账号,在开始游戏前需设置用户登录信息,或随机生成长度在64以内的字符串,每台客户端上的账号需保证唯一性。
- userToken用来校验userId的有效性,如果userId为随机生成,userToken也可以随机生成。
- HmcpVideoView的setUserInfo()方法用来设置用户的登录信息。
函数原型:
public class HmcpVideoView {
public void setUserInfo(UserInfo userInfo)
}
表3-6 用户登录信息参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
userInfo | UserInfo | 是 | 用户登录信息(包含userID、usertoken等信息) |
示例代码:
mUserInfo = new UserInfo();
mUserInfo.userId = USER_ID;
mUserInfo.userToken = USER_TOKEN;
mUserInfo.userType = 0;
hmcpVideoView.setUserInfo(mUserInfo);
⚠注意:
如果两台客户端上的userId相同,将会导致游戏异常现象。userType代表用户类型,可以不传,默认其值为0。0代表示表普通用户,5表示超级账号,超级账号需要海马云后台配合生效。
3.4 申请服务
调用HmcpVideoView的play(bundle)函数,SDK会与海马云的server通讯,申请服务成功后即可开始游戏。
函数原型:
public class HmcpVideoView {
public void play(Bundle bundle)
}
表3-7 申请服务参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
bundle | Bundle | 是 | 需调用对应put***()方法,将下表所有参数赋值并打包到bundle中 |
表3-8 bundle key说明
key | value类型 | 必填项 | 说明 |
---|---|---|---|
HmcpVideoView.ORIENTATION | ScreenOrientation | 是 | 标识游戏是竖屏还是横屏显示。 竖屏:ScreenOrientation.PORTRAIT 横屏:ScreenOrientation.LANDSCAPE |
HmcpVideoView.PLAY_TIME | int | 是 | 游戏时长,单位为ms |
HmcpVideoView.PRIORITY | int | 是 | 用户申请游戏服务的优先级,默认为0。数值越大优先级越高 |
HmcpVideoView.APP_NAME | String | 是 | 游戏包名,传入对应的要启动的游戏包名 |
HmcpVideoView.APP_CHANNEL | String | 是 | 如果存在多款游戏同包名的情况,可以通过appChannel区分。此参数由海马云配置。需要注意不要与channelId(接入方自己定义,与海马云无关)混淆 |
HmcpVideoView.C_TOKEN | String | 是 | 用来校验参数的有效性,cToken的计算方法请参考cToken说明文档 |
HmcpVideoView.EXTRA_ID | String | 否 | 预留参数,默认为空字符串 |
HmcpVideoView.PAY_STR | String | 否 | 支付相关参数,默认为空字符串 |
HmcpVideoView.ARCHIVED | boolean | 否 | 游戏是否存档 |
HmcpVideoView.PAY_PROTO_DATA | String | 否 | APP业务系统关心的参数,需要base64编码 |
HmcpVideoView.FPS_PERIOD | int | 否 | 采集fps周期及上报周期,单位为s |
HmcpVideoView.BAND_WIDTH_PERIOD | int | 否 | 采集带宽的周期及上报周期,单位为s |
HmcpVideoView.BAND_WIDTH_PEAK | int | 否 | 采集带宽周期内的最大的几个值 |
HmcpVideoView.DECODE_TIME_PERIOD | int | 否 | 采集解码时间的周期及上报周期,单位为s |
HmcpVideoView.INTERNET_SPEED | int | 否 | 设置该参数可以固定的码率开始游戏,单位为KB/s |
HmcpVideoView.IS_SHOW_TIME | boolean | 否 | 是否显示剩余游戏时间 |
HmcpVideoView.CLIENT_ISP | String | 否 | 运营商名称 |
HmcpVideoView.CLIENT_PROVINCE | String | 否 | 当前定位省份 |
HmcpVideoView.CLIENT_CITY | String | 否 | 当前定位城市 |
HmcpVideoView.C_ID | String | 否 | 第一次游玩不需要传cid,若要重连未释放游戏,则需要查询未释放游戏信息(检测未释放游戏接口中的channelInfoList)中对应cid、包名、渠道,使用该参数申请重连 |
HmcpVideoView.NO_INPUT_LIMIT_TIME | int | 否 | 无操作超时时长,单位为s |
HmcpVideoView.ALLOW_COMPATIBLE_IPV6 | boolean | 否 | 是否支持IPV6功能,同时需海马云在后台配置 |
HmcpVideoView.VERTICAL_BACKGROUND | int | 否 | 播放器背景图,主要针对竖屏游戏黑色边框的优化 |
HmcpVideoView.VIEW_RESOLUTION_WIDTH | int | 否 | 自定义游戏推流清晰度宽度,宽度、高度可调换位置,不影响显示效果 |
HmcpVideoView.VIEW_RESOLUTION_HEIGHT | int | 否 | 自定义游戏推流清晰度高度,宽度、高度可调换位置,不影响显示效果 |
HmcpVideoView.AUDIO_RECORD_SOURCE | Source | 否 | 设置录音功能,录音数据由APP侧提供。 如果由SDK侧录音,不需要设置此参数 |
HmcpVideoView.STREAM_TYPE | int | 否 | 选择流类型。 0:表示RTMP 1:表示WEBRTC 不填写此参数时,默认为RTMP流类型 |
HmcpVideoView.ARCHIVE_FROM_USER_ID | String | 否 | 被读取的存档用户的userId |
HmcpVideoView.ARCHIVE_FROM_BID | String | 否 | 被读取的存档用户的bid |
示例代码:
boolean isPortraitOrientation = false;
// 是否竖屏
ScreenOrientation orientation = isPortraitOrientation ? ScreenOrientation.PORTRAIT : ScreenOrientation.LANDSCAPE;
int playTime = 6000 * 1000;
int priority = 100;
String packageNameGame = "com.yodo1tier1.skizgfTV.cmcc";
String appChannel = "";
String cToken = CryptoUtils.generateCToken(packageNameGame, userId, userToken, accessKeyID, channelID, accessKey);
String extraId = "";
String payStr = "";
boolean archive = true;
String protoData = "protoData";
int fpsPeriod = 1;
int bandWidthPeriod = 5;
int bandWidthPeak = 3;
int decodeTimePeriod = 1;
int speed = 1024;
boolean isShowTime = true;
String clientISP = "联通";
String clientProvince = "河北";
String clientCity = "廊坊";
// 根据查询未释放游戏对应信息赋值
String cid = "";
// 无操作超时时间单位为秒
int noInputLimitTime = 60;
// 是否支持IPV6功能
boolean isEnableIpv6 = false;
// 推流画面宽度
int viewResolutionWidth = 1080;
// 推流画面高度
int viewResolutionHeight = 1920;
// 传入参数
Bundle bundle = new Bundle();
bundle.putSerializable(HmcpVideoView.ORIENTATION, orientaion);
bundle.putInt(HmcpVideoView.PLAY_TIME, playTime);
bundle.putInt(HmcpVideoView.PRIORITY, priority);
bundle.putString(HmcpVideoView.APP_NAME, packageName);
bundle.putString(HmcpVideoView.APP_CHANNEL, appChannel);
bundle.putString(HmcpVideoView.C_TOKEN, cToken);
bundle.putString(HmcpVideoView.EXTRA_ID, extraId);
bundle.putString(HmcpVideoView.PAY_STR, payStr);
bundle.putBoolean(HmcpVideoView.ARCHIVED, archieve);
Bundle.putString(HmcpVideoView.PAY_PROTO_DATA, protoData);
bundle.putInt(HmcpVideoView.FPS_PERIOD, fpsPeriod);
bundle.putInt(HmcpVideoView.BAND_WIDTH_PERIOD, bandWidthPeriod);
bundle.putInt(HmcpVideoView.BAND_WIDTH_PEAK, bandWidthPeak);
bundle.putInt(HmcpVideoView.DECODE_TIME_PERIOD, decodeTimePeriod);
bundle.putInt(HmcpVideoView.INTERNET_SPEED, speed);
bundle.putBoolean(HmcpVideoView.IS_SHOW_TIME,isShowTime);
bundle.putBoolean(HmcpVideoView.CLIENT_ISP, clientISP);
bundle.putBoolean(HmcpVideoView.CLIENT_PROVINCE, clientProvince);
bundle.putBoolean(HmcpVideoView. CLIENT_CITY, clientCity);
bundle.putBoolean(HmcpVideoView.C_ID,cid);
bundle.putInt(HmcpVideoView.NO_INPUT_LIMIT_TIME, noInputLimitTime);
bundle.putInt(HmcpVideoView.ALLOW_COMPATIBLE_IPV6, isEnableIpv6);
bundle.putInt(HmcpVideoView.VIEW_RESOLUTION_WIDTH, viewResolutionWidth);
bundle.putInt(HmcpVideoView.VIEW_RESOLUTION_HEIGHT, viewResolutionHeight);
bundle.putSerializable(HmcpVideoView.AUDIO_RECORD_SOURCE, Source.APP);
hmcpVideoView.play(bundle);
3.5 开始游戏
服务请求完成后,通过HmcpVideoView的play()接口开始游戏。启动游戏时调用play()接口。
函数原型:
public class HmcpVideoView {
public void play();
}
示例代码:
@Override
public void HmcpPlayerStatusCallback(String callback) {
try {
JSONObject jsonObject = new JSONObject(callback);
String status = jsonObject.optString("status");
if (TextUtils.equals("1", status)) {
hmcpVideoView.play();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
4 API 接口
4.1 初始化失败返回内容说明
初始化失败返回 JOSN 字符串:
{
"message":"",
"volleyMessage": "",
"startTime": "",
"errorTime": "",
}
表4-1 初始化失败返回内容字段说明
参数 | 说明 |
---|---|
message | 错误描述 |
volleyMessage | 网络请求失败时,错误信息 |
startTime | 网络超时会携带该字段,表示请求开始时间 |
errorTime | 网络超时会携带该字段,表示错误下发时间 |
4.2 设置配置信息接口
配置信息为免登录功能所需信息,SDK在获取云游戏服务时需要传送“配置信息”给服务器。如不使用配置信息,可传非空字符串。
函数原型:
public class HmcpVideoView {
public void setConfigInfo (String config);
}
4.3 暂停游戏接口
游戏启动后,通过HmcpVideoView的pauseGame() 接口暂停游戏。
函数原型:
public class HmcpVideoView {
public void pauseGame();
}
示例代码:
hmcpVideoView.pauseGame();
4.4 继续游戏接口
暂停游戏后,通过HmcpVideoView的restartGame()接口继续游戏。
函数原型:
public class HmcpVideoView {
public void restartGame(int time);
}
表4-2 继续游戏参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
time | int | 是 | 用户可以玩游戏的时长,单位为ms |
示例代码:
hmcpVideoView.restartGame(1000 * 60 * 10 ); // 游玩10分钟
4.5 断网重新连接游戏接口
通过HmcpVideoView的reconnection()接口进行重新连接游戏,应用场景为:
- 游戏启动后,断网重新连接游戏。
- 游戏启动后,WIFI切换到4G。
函数原型:
public class HmcpVideoView {
public void reconnection();
}
示例代码:
hmcpVideoView.reconnection();
4.6 重新申请游戏接口
服务请求完成后,通过HmcpVideoView的startPlay()接口重新申请游戏。应用场景为:
- 长时间无操作后,重新申请游戏。
- 其他情况下需重新申请游戏场景。
函数原型:
public class HmcpVideoView {
public void startPlay();
}
示例代码:
hmcpVideoView.startPlay();
4.7 插队申请游戏接口
服务请求完成后,通过HmcpVideoView的startPlay(boolean isAhead)接口重新以插队方式申请游戏。可在排队失败等报错场景下,以等待申请实例队列的第一位进行插队,等待有空闲实例进行分配游玩。
函数原型:
public class HmcpVideoView {
public void startPlay(boolean isAhead);
}
表4-3 插队申请游戏参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
isAhead | boolean | 是 | 是否插队进行申请游戏 |
示例代码:
hmcpVideoView.startPlay(true); // 注意:调用 play(bundle) 成功后,才可调用该方法
4.8 退出游戏接口
结束播放,退出游戏。退出需要在Activity方法重载onPause()、onDestroy()接口。
函数原型:
public class HmcpVideoView {
public void onPause();
public void onDestroy();
}
示例代码:
@Override
protected void onStop() {
super.onStop();
hmcpVideoView.onStop();
}
@Override
protected void onDestroy() {
if (hmcpVideoView!= null) {
hmcpVideoView. onDestroy();
}
super.onDestroy();
}
4.9 切换清晰度接口
此接口用于切换清晰度,ResolutionInfo类的定义及切换清晰度的调用实际请参见清晰度信息ResolutionInfo类的定义。
函数原型:
public class HmcpVideoView {
public void onSwitchResolution (int level, ResolutionInfo resolution, int rate)
}
表4-4 切换清晰度参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
level | int | 是 | 预留字段,传0 |
resolution | ResolutionInfo | 是 | 要切换的清晰度信息 |
rate | int | 是 | 预留字段,传0 |
示例代码:
// 获取清晰度列表
List<ResolutionInfo> mResolutionList = HmcpManager.getInstance().getResolutionDatas();
// 切换清晰度
hmcpVideoView.onSwitchResolution(level, resolution, rate);
⚠注意:
切换清晰度接口仅可使用此方法。
4.10 添加控件接口
游戏启动后,通过HmcpVideoView的addView()接口添加控件,如背景图片、设置图标等。
函数原型:
public class HmcpVideoView {
public void addView(View child);
}
表4-5 添加控件参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
child | View | 是 | 要添加控件的View |
示例代码:
// view 可以自定义
hmcpVideoView.addView(view);
4.11 获取配置信息接口
获取图片度、清晰度、测速等相关配置信息。
函数原型:
public class HmcpVideoView {
public IntroImageInfo mIntroImageInfo;
public List<ResolutionInfo> mResolutionList;
}
示例代码:
// 背景图片配置信息
introImageInfos = ijkVideoView.mIntroImageInfo;
// 清晰度配置信息
resolutionList = ijkVideoView.mResolutionList;
// 测速配置最小值
min = Integer.parseInt(Constants.MINIMUM_BITRATE);
4.12 进入队列接口
获取需要排队信息后调用entryQueue()接口进入队列。
函数原型:
public class HmcpVideoView {
public void entryQueue()
}
示例代码:
hmcpVideoView.entryQueue();
4.13 退出队列接口
获取需要排队信息后调用exitQueue()接口退出队列。
函数原型:
public class HmcpVideoView {
public void exitQueue ()
}
示例代码:
hmcpVideoView.exitQueue();
4.14 IDC节点测速接口
游戏启动前,调用HmcpManager类的testSpeed()接口进行网络带宽测试。
通过duration参数控制测试的最长时间,单位为s。测试完成后通过注册的callback接口返回测试结果,单位为 KB/s。
函数原型:
public class HmcpManager {
public void testSpeed(boolean isIpv6, final int duration, final String clientISP,
final String clientProvince, final String clientCity, final OnSpeedTestCallBackListener listener)
public void testSpeed(boolean isIpv6, final int duration, final String clientISP,
final String clientProvince, final String clientCity, String gameBID,final OnSpeedTestCallBackListener listener)
}
表4-6 测速参数说明
参数 | 类型 | 说明 |
---|---|---|
isIpv6 | boolean | 是否是Ipv6 |
duration | Int | 检测速度所用时间,单位为s |
clientISP | String | 运营商名称 |
clientProvince | String | 当前定位省份 |
clientCity | String | 当前定位城市 |
gameBID | String | 游戏bid |
listener | OnSpeedTestCallBackListener | 节点测速接口回调监听器 |
表4-7 OnSpeedTestCallBackListener说明
方法名 | 参数 | 类型 | 说明 |
---|---|---|---|
onComplete | success | boolean | 测速结果。 true:表示测速通过 false:表示测试未通过 |
speed | int | 测速值,单位为KB/s | |
speedThreshold | int | 测速最低阈值,单位为KB/s |
HmcpManager manager = HmcpManager.getInstance();
manager.testSpeed(5, "联通", "河北", "廊坊", new OnSpeedTestCallBackListener() {
@Override
public void success(boolean success, int speed, int speedTheshold) {
//to do something
}
});
4.15 点测速接口
游戏启动后,通过HmcpVideoView的点测速接口getVideoLatency()获取实时视频网络延迟,单位为ms。
函数原型:
public class HmcpVideoView {
public int getVideoLatency()
}
示例代码:
int latency = hmcpVideoView.getVideoLatency();
4.16 发送消息接口
游戏启动后,通过HmcpVideoView的sendMessage()接口可以向access服务器发送消息。
函数返回值表示:当前长链接是否连接成功。
函数原型:
public class HmcpVideoView {
//返回当前长链接是否连接成功
public boolean sendMessage(String payload, MessageType type, OnSendMessageListener listener);
}
表4-8 发送消息参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
payload | String | 是 | 需要发送的消息 |
type | MessageType | 是 | 消息类型 |
listener | OnSendMessageListener | 是 | 发送消息接口回调监听器 |
示例代码:
public void testSendMessage(View v) {
if (hmcpVideoView != null) {
//isConnect true:长链接成功 false:长链接失败
boolean isConnect = hmcpVideoView.sendMessage("test send msg", MessageType.PAY_TYPE, new OnSendMessageListener() {
@Override
public void result(boolean success, String mid) {
//success:发送结果。true:发送成功;false:发送失败;
//mid:消息Id
}
});
}
}
4.17 消息回调接口
实现HmcpPlayerListener接口后,在该接口的onMessage()函数中可以收到access服务器的消息回调。
函数原型:
public interface HmcpPlayerListener {
void onMessage(Message message)
}
表4-9 消息回调返回值说明
返回值 | 类型 | 说明 |
---|---|---|
message | Message | Message的定义以及参数说明请参见消息回调 |
示例代码:
public class HmcpPlayerActivity extends AppCompatActivity implements HmcpPlayerListener {
@Override
public void onMessage(Message message) {
// 收到退出登录消息回调
}
}
4.18 获取实时视频信息
实现HmcpPlayerListener接口后,在该接口的onPlayStatus()函数中可以收到实时数据的上报。
函数原型:
public interface HmcpPlayerListener {
void onPlayStatus(int status, long value, String data);
}
表4-10 获取实时视频信息返回结果说明
返回结果 | 类型 | 说明 |
---|---|---|
status | int | 状态值,可为0、1、2,value根据status值不同而含义不同 |
value | long | status = 0时,value是采样周期内接受到的视频帧的总大小,单位为Byte status = 1时,value是采样周期内,渲染帧的总数 status = 2时,value是采样周期内,平均解码耗时,单位为ms |
data | String | 附加信息,status = 0时,data 为采样周期内最大的N帧大小,单位为Byte |
示例代码:
public class HmcpPlayerActivity extends AppCompatActivity implements HmcpPlayerListener {
@Override
public void onPlayStatus(int status, long value, String data) {
}
}
4.19 更新uid和游戏时长接口
更新uid和游戏时长的接口。
函数原型:
public class HmcpVideoView {
public void updateGameUID(Bundle bundle,final OnUpdataGameUIDListener listener)
}
表4-11 更新uid和游戏时长参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
bundle | Bundle | 是 | 调用 put***() ,将下列所有参数赋值并打包到bundle中 |
listener | OnUpdataGameUIDListener | 是 | 结果回调监听器 |
表4-12 bundle key说明
key | value类型 | 必填项 | 说明 |
---|---|---|---|
HmcpVideoView.PLAY_TIME | long | 是 | 游戏时长,单位为ms |
HmcpVideoView.USER_ID | String | 是 | 新的用户ID ,uid |
HmcpVideoView.TIPS_MSG | String | 是 | 更新成功的tip显示 |
HmcpVideoView.PAY_PROTO_DATA | String | 是 | 更新的proto data |
HmcpVideoView.C_TOKEN | String | 是 | 更新的cToken,用来校验参数的有效性 |
⚠注意:
更新uid成功后需要重新设置用户信息,详情请参见设置用户登录信息。
示例代码:
Bundle bundle = new Bundle();
bundle.putLong(HmcpVideoView.PLAY_TIME, playingTime);
bundle.putString(HmcpVideoView.USER_ID, userID);
bundle.putString(HmcpVideoView.TIPS_MSG, tip);
bundle.putString(HmcpVideoView.PAY_PROTO_DATA, proto_data);
bundle.putString(HmcpVideoView.C_TOKEN, cToken);
OnUpdataGameUIDListener listener = new OnUpdataGameUIDListener () {
@Override
public void success(boolean result) {
// success中返回查询结果,false(更新失败),true(更新成功)
}
@Override
public void fail(String msg) {
// fail中返回更新失败信息
//HmcpManager.init(...,true)时
//msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}
}
});
hmcpVideoView. updateGameUID(bundle, listener);
4.20 获取Input地址接口
游戏收到第一帧后,通过HmcpVideoView的getInputUrl()接口获取input地址。
函数原型:
public class HmcpVideoView {
public String getInputUrl();
}
示例代码:
String inputUrl = hmcpVideoView.getInputUrl();
4.21 获取SDK版本号接口
创建HmcpManager后,通过其getSDKVersion()获取版本号。
函数原型:
public class HmcpManager{
public String getSDKVersion()
}
示例代码:
String sdkVersion = HmcpManager.getInstance().getSDKVersion();
4.22 获取清晰度列表接口
获取清晰度列表的接口。
函数原型:
public class HmcpManager{
public void getResolutionInfos(String packageName, String mAppChannel,
final OnGetResolutionsCallBackListener listener)
public void getResolutionInfos(String packageName, String mAppChannel, String gameBID,final OnGetResolutionsCallBackListener listener)
}
表4-13 获取清晰度列表参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
packageName | String | 是 | 游戏包名 |
mAppChannel | String | 是 | 游戏配置的AppChannel |
gameBID | String | 是 | 游戏bid |
tener | String | 是 | 查询结果回调监听器 |
示例代码:
OnGetResolutionsCallBackListener listener = new OnGetResolutionsCallBackListener () {
@Override
public void success(List mResolutionList) {
// success查询成功会返回,查询结果为List。注:List可能为空
}
@Override
public void fail(String msg) {
// fail 如查询失败会返回String类型的错误信息。
}
});
HmcpManager.getInstance().getResolutionInfos (packageName, mAppChannel, listener);
4.23 获取视频流基本参数接口
通过HmcpVideoView中的getQRCodeData()接口获取参数。
函数原型:
public class HmcpVideoView {
public String getQRCodeData()
}
示例代码:
String qrCodeData = hmcpVideoView.getQRCodeData();
//返回数据格式:
{
"screenShotUrl":"ws://wss.haimawan.com:9480/websocket?url=10.20.32.79:7682&st=4a61796462746b37314e424235734b45724754416a6c752b7034396445646145",
"screenResolution":"1920x1080",
"inputUrl":"ws://115.159.241.62:10267/4a61796462746b37314e424235734b45724754416a6c752b7034396445646145",
"orientation":0,
"encryption":""
}
表4-14 视频流字段说明
字段 | 类型 | 说明 |
---|---|---|
screenShotUrl | String | 截屏流地址 |
screenResolution | String | 视频清晰度 |
inputUrl | String | input连接地址 |
orientation | int | 横竖屏参数 |
encryption | String | 加密的cid |
4.24 获取云游戏延迟信息接口
通过该接口会返回当前云游戏的延迟信息,该算法的准确性基于APP和实例的时钟校准。
函数原型:
public class HmcpVideoView {
public VideoDelayInfo getClockDiffVideoLatencyInfo()
}
示例代码:
hmcpVideoView.getClockDiffVideoLatencyInfo();
表4-15 云游戏延迟信息方法说明
方法 | 方法说明 |
---|---|
getNetDelay() | 获取帧网络耗时(最近1s内随机采样1帧) |
getDecodeDelay() | 获取帧解码耗时(最近1s内随机采样1帧) |
getRenderDelay() | 获取帧渲染耗时只包含解码后送入渲染队列的时间,不包含系统渲染部分(最近1s内随机采样1帧) |
getFrameSize() | 获取帧大小(最近1s内随机采样1帧) |
getVideoFps() | 获取视频帧率 |
getBitRate() | 获取视频码率 |
getReciveFrameCount() | 收到的总帧数 |
getReceiveFrameSize() | 收到帧的总大小 |
getPacketsLostRate() | 丢包率,该接口只支持webrtc,不支持rtmp,返回为空字符串 |
getJitterBuffer() | JitterBuffer,该接口只支持webrtc,不支持rtmp,返回为-1 |
getServerEncodeDelay() | 服务端编码延迟,该接口只支持webrtc,不支持rtmp,返回为-1 |
⚠注意:
- 如果是RTMP流类型,该接口依赖于服务器打开时钟校准配置和ROM支持,当时钟校准成功时才会有数据返回,之前会返回null。
- 在切换清晰度、前后台切换时,需要重新进行时钟校准,校准成功前会存在异常数据,需要在重新获取第一帧之后调用。
4.25 结束播放接口
游戏启动后,通过HmcpVideoView的release()接口结束播放。可在长时间无操作后调用,也可在其他error时调用。
函数原型:
public class HmcpVideoView {
public void release()
}
示例代码:
hmcpVideoView.release();
4.26 静音功能开关接口
设置当前的播放为静音状态或关闭静音状态。
函数原型:
public class HmcpVideoView {
// @param isMute true:全局静音 false:取消全局静音
public void setAudioMute(boolean isMute)
}
表4-16 静音功能开关参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
isMute | boolean | 是 | true:表示全局静音 false:表示取消全局静音 |
示例代码:
hmcpVideoView. setAudioMute(false);
4.27 设置鼠标初始坐标接口
游戏启动前,通过HmcpVideoView的setInitMousePoint接口设置鼠标初始坐标。
其中float x、float y为鼠标在屏幕x轴y轴比例,取值范围为0-1,成功返回true,失败返回false。
函数原型:
public class HmcpVideoView {
public boolean setInitMousePoint(float x, float y)
}
表4-17 参数说明
参数 | 类型 | 说明 |
---|---|---|
x | float | 鼠标坐标在屏幕x轴比例,取值范围为0到1之间 |
y | float | 鼠标坐标在屏幕y轴比例,取值范围为0到1之间 |
示例代码:
hmcpVideoView.setInitMousePoint(0.5f,0.5f);
4.28 获取键位映射数据接口
游戏启动后,通过HmcpVideoView的getButtonMappings接口获取键位映射数据。
其中List为键位映射配置列表集合,如有多套配置则list长度大于1。List为空或list.size为0则键位映射没有配置或配置格式错误。
获取键位映射列表方法为list.get(int index),表单格式为JSON格式,示例参考TV键位映射配置格式。
函数原型:
public class HmcpVideoView{
public List getButtonMappings ( )
}
示例代码:
List list = hmcpVideoView.getButtonMappings();
4.29 切换操作模式接口
在成功播流后,通过HmcpVideoView的setButtonMappingMode(int mode)接口修改操作模式,返回值为true表示切换成功,返回值为false表示切换失败。
函数原型:
public class HmcpVideoView{
public boolean setButtonMappingMode(int mode)
}
表4-18 参数说明
参数 | 类型 | 说明 |
---|---|---|
mode | int | 操作模式。 初始化模式:HmcpVideoView.GAMEPAD_MODE_NONE=1 鼠标操作模式:HmcpVideoView. GAMEPAD_MODE_MOUSE=2 键位映射操作模式:HmcpVideoView. GAMEPAD_MODE_MAP=4 |
示例代码:
hmcpVideoView.setButtonMappingMode (HmcpVideoView.GAMEPAD_MODE_MAP);//映射模式
⚠注意:
- 未正常起播时,无法修改操作模式,会返回失败。
- 未配置映射列表时,无法切换到映射模式,会返回失败。
4.30 获取当前操作模式接口
在成功播流后,通过HmcpVideoView的getButtonMappingMode()接口获取当前操作模式。
函数原型:
public class HmcpVideoView{
public int getButtonMappingMode()
}
表4-19 参数说明
参数 | 类型 | 说明 |
---|---|---|
mode | int | 操作模式。 初始化模式:HmcpVideoView.GAMEPAD_MODE_NONE=1 鼠标操作模式:HmcpVideoView. GAMEPAD_MODE_MOUSE=2 键位映射操作模式:HmcpVideoView. GAMEPAD_MODE_MAP=4 |
示例代码:
int mode = hmcpVideoView. getButtonMappingMode ();
4.31 设置鼠标灵敏度接口
游戏启动后,通过HmcpVideoView的setMouseMoveStep接口设置鼠标灵敏度。其中int step为鼠标灵敏度取值0-10,成功返回true,失败返回false。
函数原型:
public class HmcpVideoView{
public boolean setMouseMoveStep(int step);
}
表4-20 参数说明
参数 | 类型 | 说明 |
---|---|---|
step | int | 鼠标灵敏度,取值0~10 |
示例代码:
hmcpVideoView.setMouseMoveStep(5);
4.32 设置播放控件接口
可以使用HmcpManager中的setVideoView(int type)接口设置播放控件类型。
函数原型:
public class HmcpManager{
public void setVideoViewType(int type)
}
表4-21 参数说明
参数 | 类型 | 说明 |
---|---|---|
type | int | 播放器类型。 HmcpManager.RENDER_SURFACE_VIEW = 1:使用surfaceView HmcpManager.RENDER_TEXTURE_VIEW = 2:使用textureView 注:默认使用textureView,surfaceView不支持竖屏游戏,不支持进入后台不断流。 |
示例代码:
// 可在初始化成功后调用
HmcpManager manager = HmcpManager.getInstance();
manager.init(this, new OnInitCallBackListener() {
@Override
public void success() {
// 初始化SDK成功,可以云游戏
manager.setVideoViewType(HmcpManager.RENDER_TEXTURE_VIEW);
}
@Override
public void fail(String s) {
// 初始化SDK失败,不能云游戏
}
});
4.33 设置游戏前后台切换是否断流重连接口
可以使用HmcpVideoView中的setCloseStream(boolean close)接口设置前后台切换是否断流重连。
函数原型:
public class HmcpVideoView {
public void setCloseStream(boolean close);
}
表4-22 参数说明
参数 | 类型 | 说明 |
---|---|---|
close | boolean | true:断流重连 false:不断流重连 默认为true |
示例代码:
hmcpVideoView.setCloseStream(true);
4.34 云游戏静音接口
可以使用HmcpVideoView中的turnOffSound()方法关闭声音,turnOnSound()打开声音。
通过isOnsound()判断是否打开声音 true为声音已打开,false为声音未打开。
函数原型:
public class HmcpVideoView {
public void turnOffSound();
public void turnOnSound();
public boolean isOnSound();
}
示例代码:
hmcpVideoView.turnOffSound();
4.35 设置鼠标图标接口
可以使用HmcpVideoView中的setMouseIcon()方法设置鼠标图标。
函数原型:
public class HmcpVideoView {
//name:;type:图片(0),gif(1)
public void setMouseIcon(String name,int type);
}
表4-23 参数说明
参数 | 类型 | 说明 |
---|---|---|
name | String | 图片资源名称:”xxx.png” |
type | int | 图片类型。 0:普通图片 1:gif图片 |
示例代码:
hmcpVideoView.setMouseIcon("mouse.png",0);
5 进阶API
5.1 获取视频流地址接口
游戏启动后,通过HmcpVideoView的getStreamUrl()接口接口获取视频流地址。
函数原型:
public class HmcpVideoView {
public String getStreamUrl()
}
示例代码:
String streamUrl = hmcpVideoView.getStreamUrl();
5.2 获取cid接口
游戏启动后,通过HmcpManager的getCloudId接口获取cid。
函数原型:
public class HmcpManager{
public String getCloudId()
}
示例代码:
String cloudId = HmcpManager.getInstance().getCloudId();
5.3 获取游戏存档状态接口
获取存档状态的接口,判断游戏所处状态(正在存档、存档完成、存档失败)。
函数原型:
public class HmcpManager{
public void getGameArchiveStatus(String packageName, UserInfo userInfo,
final OnSaveGameCallBackListener listener)
public void getGameArchiveStatus(String packageName, UserInfo userInfo, String gameBID,final OnSaveGameCallBackListener listener)
}
表5-1 获取游戏存档状态参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
packageName | String | 是 | 游戏包名 |
userInfo | UserInfo | 是 | 用户登录信息,请参见设置用户登录信息 |
gameBID | String | 是 | 游戏bid |
listener | OnSaveGameCallBackListener | 是 | 查询存档结果回调监听器 |
示例代码:
OnSaveGameCallBackListener listener = new OnSaveGameCallBackListener() {
@Override
public void success(boolean result) {
// success中返回查询结果,false(正在存档),true(存档完成)。
}
@Override
public void fail(String msg) {
// fail(存档失败),msg为失败信息。
// HmcpManager.init(...,true)时,msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}
}
});
HmcpManager.getInstance().getGameArchiveStatus(packageName, userInfo, listener);
5.4 查询是否存在游戏存档接口
查询当前用户是否存在存档的接口。
函数原型:
public class HmcpManager{
public void gameArchived(String packageName, UserInfo userInfo,
final OnSaveGameCallBackListener listener)
public void gameArchived(String packageName, UserInfo userInfo, String gameBID,
final OnSaveGameCallBackListener listener)
}
表5-2 查询是否存在游戏存档参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
packageName | String | 是 | 游戏包名 |
userInfo | UserInfo | 是 | 用户登录信息,请参见设置用户登录信息 |
gameBID | String | 是 | 游戏bid |
listener | OnSaveGameCallBackListener | 是 | 查询结果回调监听器 |
示例代码:
OnSaveGameCallBackListener listener = new OnSaveGameCallBackListener() {
@Override
public void success(boolean result) {
// success中返回查询结果,false(没有游戏存档),true(有游戏存档)。
}
@Override
public void fail(String msg) {
// fail中返回查询失败信息
// HmcpManager.init(...,true)时
// msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}
}
});
HmcpManager.getInstance().gameArchived(packageName, userInfo, listener);
5.5 检测未释放游戏接口
通过用户信息UserInfo查询,是否有未释放实例(仍在运行的游戏)。
函数原型:
public class HmcpManager{
/**
* @param userInfo 用户登录信息
* @param listener 回调函数
*/
public void checkPlayingGame(UserInfo userInfo, final OnGameIsAliveListener listener)
public void checkPlayingGame(UserInfo userInfo, String gameBID, final OnGameIsAliveListener listener)
}
表5-3 检测未释放游戏参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
userInfo | UserInfo | 是 | 用户登录信息,请参见设置用户登录信息 |
gameBID | String | 是 | 游戏bid |
listener | OnSaveGameCallBackListener | 是 | 查询结果回调监听器 |
示例代码:
OnGameIsAliveListener listener = new OnGameIsAliveListener () {
@Override
public void success(List<CheckCloudServiceResult.ChannelInfo> channelInfoList) {
// success 中返回查询结果,channelInfoList,返回的未释放游戏信息,含 pkgName、cid、appChannel
// 如果有未释放游戏,则取集合第1个元素,如果没有则元素为0;
if (channelInfoList != null && channelInfoList.size() > 0) {
final CheckCloudServiceResult.ChannelInfo channelInfo = channelInfoList.get(0);
}
}
@Override
public void fail(String msg) {
// fail中返回查询失败信息
// HmcpManager.init(...,true)时,msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}
}
});
HmcpManager.getInstance().checkPlayingGame (userInfo, listener);
5.6 根据cid释放实例接口
检测到未释放的cid后,通过调用接口,根据cid释放实例。
函数原型:
public class HmcpManager{
public void setReleaseCid(String packageName, String cid, String cToken, String appChannel, UserInfo2 userInfo2, final OnSaveGameCallBackListener listener);
public void setReleaseCid(String packageName, String cloudId, String cToken, String appChannel, UserInfo2 userInfo2,String gameBID, final OnSaveGameCallBackListener listener)
}
表5-4 参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
packageName | String | 是 | 游戏包名,传入要释放的对应游戏包名 |
cid | String | 是 | 要释放的cid |
cToken | String | 是 | 用来校验参数的有效性,cToken的计算方法请参考cToken说明文档 |
appChannel | String | 是 | 如果存在多款游戏同包名的情况,可以通过appChannel区分。如果不存在则可以忽略。 |
userInfo2 | UserInfo2 | 是 | 用户登录信息,包含字段userId、token |
gameBID | String | 是 | 游戏bid |
listener | OnSaveGameCallBackListener | 是 | 存档结果回调监听器 |
表5-5 回调结果
回调结果 | 类型 | 说明 |
---|---|---|
code | int | 请求结果标识。 0:表示请求成功 1:表示请求失败 |
msg | String | 当code = 0时返回的信息 |
errorCode | String | 当code = 1时返回的错误码 |
errorMsg | String | 当code = 1时返回的错误信息 |
5.7 获取网络请求数据接口
可以使用HmcpManager中的getNetText(HmcpVideoView hmcpVideoView)接口获取网络请求结果数据。
函数原型:
public class HmcpManager{
public String getNetText (HmcpVideoView hmcpVideoView)
}
表5-6 获取网络请求数据参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
hmcpVideoView | HmcpVideoView | 是 | SDK播放组件,如果没有创建可以传null |
示例代码:
String netText = HmcpManager.getInstance().getNetText(hmcpVideoView)
返回值为字符串格式,可能为null要做为空判断。
返回数据格式见下方:
[{
"action": "104",
"index": 0,
// 结果详细描述
"msg": "",
"packageName": "",
"responseTime": 1565684454987,
// 请求结果
"result": "success",
"startTime": 1565684452774,
// 请求耗时
"time": 2213,
"transId": "7082cbf20b8456c4a02a6e520a7f7982",
// 网络请求的url
"url": "https://saasauth.cmgame.com/s/rest/api"
}, {
"action": "108",
"index": 1,
"msg": "",
"packageName": "",
"responseTime": 1565684455511,
"result": "success",
"startTime": 1565684455015,
"time": 496,
"transId": "7082cbf20b8456c4a02a6e520a7f7982",
"url": "https://saasauth.cmgame.com/s/rest/api"
}]
表5-7 返回值说明
返回值 | 类型 | 说明 |
---|---|---|
url | String | 网络请求的url |
time | long | 请求耗时 |
result | String | 请求结果 |
uid | String | 当前用户的uid |
cid | String | 当前云游戏的cid |
action | String | 请求的类型。 长连接:access、input 连接:input 视频流:videoUrl 音频流:audioUrl action:actionId |
transId | String | 当前申请的唯一id |
packageName | String | 请求的游戏包名 |
startTime | long | 开始请求的时间 |
responseTime | String | 请求结束的时间 |
DNS | String | DNS解析后的IP地址 |
5.8 云游戏结束信息上报接口
在APP判定云游戏异常结束但SDK未报错误码时,可调用该接口上报云游戏信息,用于SDK排查问题做优化。正常结束也可以调用该接口,该接口对业务逻辑无影响,参数取不到或未定义时可以传空。
函数原型:
public class HmcpVideoView {
public void reportFinishInfo(int finishCode, String cid, String pkgName, String appChannel, String appVersion, String imei, String deviceId, String gameId)
}
表5-8 云游戏结束信息上报参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
finishCode | String | 是 | 结束状态。 0:表示正常结束 1:表示超时结束 2:表示异常结束,有SDK错误码 3:表示异常结束,无SDK错误码 |
cid | long | 是 | 当前云游戏cid |
pkgName | String | 是 | 当前云游戏包名 |
appChannel | String | 是 | 当前云游戏渠道号 |
appVersion | String | 是 | APP版本 |
deviceId | String | 是 | 用户设备号 |
gameId | String | 是 | 当前云游戏id号 |
示例代码:
hmcpVideoView.reportFinishInfo(finishCode, cid, pkgName, appChannel, appVersion, imei, deviceId, gameId);
5.9 获取云游戏状态码接口
云游戏异常结束时,通过该接口返回云游戏当前的状态码,可以分析统计游戏失败原因。
函数原型:
public class HmcpVideoView {
public String getCloudPlayStatusCode()
}
表5-9 RTMP状态码
RTMP状态码 | 类型 | 说明 |
---|---|---|
100999001 | String | 无流地址,cid未获取成功(初始状态) |
100999002 | String | 无流地址,cid获取成功,Socket连接失败 |
100999003 | String | 无流地址,cid获取成功,Socket连接成功,乒乓状态异常 |
100999004 | String | 无流地址,cid获取成功,Socket连接成功,乒乓状态正常 |
100999005 | String | 无流地址,video成功 |
100999006 | String | 有流地址,video失败,audio成功 |
100999007 | String | 有流地址,video失败,audio失败,input成功 |
100999008 | String | 有流地址,video失败,audio失败,input失败 |
100999009 | String | 获取流地址超时 |
100999010 | String | 刷新stoken超过次数上限 |
100999100 | String | user info为空 |
示例代码:
String cloudPlayStatusCode = hmcpVideoView.getCloudPlayStatusCode();
5.10 开始直播接口
函数原型:
public class HmcpVideoView {
public void startLiving(String livingId, String livingUrl, OnLivingListener listener);
}
表5-10 开始直播参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
livingId | String | 是 | 直播id |
livingUrl | String | 是 | 直播地址 |
listener | OnLivingListener | 是 | 调用结果回调 |
示例代码:
hmcpVideoView.startLiving(livingId, livingUrl, new OnLivingListener() {
@Override
public void start(boolean success, String msg) {
if (success) {
//开始直播成功
}else{
//HmcpManager.init(...,true)时,msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}}
}
}
@Override
public void stop(boolean success, String msg) {
if (success) {
//停止直播成功
}else{
//HmcpManager.init(...,true)时,msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}}
}
}
});
5.11 停止直播接口
函数原型:
public class HmcpVideoView {
public void stopLiving(String livingId, OnLivingListener listener);
}
表5-11 停止直播参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
listener | OnLivingListener | 是 | 调用结果回调 |
livingId | String | 是 | 直播id |
示例代码:
hmcpVideoView.stopLiving(livingId, new OnLivingListener() {
@Override
public void start(boolean success, String msg) {
if (success) {
//开始直播成功
}else {
//HmcpManager.init(...,true)时,msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}}
}
}
@Override
public void stop(boolean success, String msg) {
if (success) {
//停止直播成功
}else {
//HmcpManager.init(...,true)时,msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}}
}
}
});
5.12 获取授权码接口
函数原型:
public class HmcpVideoView {
public void getPinCode(OnContronListener listener);
}
表5-12 获取授权码参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
listener | OnContronListener | 是 | 调用结果回调 |
示例代码:
hmcpVideoView.getPinCode(new OnContronListener() {
@Override
public void pinCodeResult(boolean success, String cid, String pinCode, String msg) {
if (success) {
// 获取授权码成功
} else {
// 获取授权码失败
//HmcpManager.init(...,true)时,msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}}
}
}
@Override
public void contronResult(boolean success, String msg) {
if (success) {
// 获取控制权成功
} else {
// 获取控制权失败
//HmcpManager.init(...,true)时,msg={"errorCode":"","errorCodeWithoutCid":"","errorMessage":""}}
}
}
@Override
public void contronLost() {
// 失去控制权
}
});
5.13 获取控制权接口
函数原型:
public class HmcpVideoView {
public void contronPlay(int streamType, Control controlPara, OnContronListener listener);
}
表5-13 获取控制权参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
streamType | int | 是 | 选择流类型。 0 :表示RTMP 1 :表示WEBRTC |
controlPara | Control | 是 | 获取控制权参数对象 |
listener | OnContronListener | 是 | 调用结果回调 |
表5-14 Control数据结构说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
cid | String | 是 | cid |
pinCode | String | 是 | 授权码 |
accessKeyID | String | 是 | 被转移客户端的bid(接入方的唯一ID,用来区分不同的接入方,该值由海马云分配) |
isIPV6 | boolean | 是 | 是否兼容IPV6环境 |
clientISP | String | 否 | 运营商名称 |
clientProvince | String | 否 | 当前定位省份 |
clientCity | String | 否 | 当前定位城市 |
ScreenOrientation.PORTRAIT | ScreenOrientation | 是 | 屏幕方向 竖屏:ScreenOrientation.PORTRAIT 横屏:ScreenOrientation.LANDSCAPE |
cameraOpenPermissionCheck | boolean | 否 | 每次开启摄像头时,向APP申请权限的开关,不传默认为关闭。 true:表示开启 false:表示关闭 权限申请回调请参见关键接口回调中onPermissionNotGranted接口, 权限结果通知请参见通知SDK权限结果接口 |
示例代码:
hmcpVideoView.contronPlay(contronCid, contronPinCode, new OnContronListener() {
@Override
public void pinCodeResult(boolean success, String cid, String pinCode, String msg) {
if (success) {
// 获取授权码成功
} else {
// 获取授权码失败
}
}
@Override
public void contronResult(boolean success, String msg) {
if (success) {
// 获取控制权成功
} else {
// 获取控制权失败
}
}
@Override
public void contronLost() {
// 失去控制权
}
});
5.14 是否支持直播接口
函数原型:
public class HmcpVideoView {
public ELivingCapabilityStatus getLivingCapabilityStatus();
}
表5-15 是否支持直播返回结果说明
返回结果 | 类型 | 说明 |
---|---|---|
ELivingCapabilityStatus | ENUM | 是否支持直播状态 |
示例代码:
public enum ELivingCapabilityStatus {
// 未知
UNKNOWN,
// 支持
SUPPORTED,
// 不支持
UNSUPPORTED
}
示例代码:
ELivingCapabilityStatus livingCapabilityStatus = hmcpVideoView.getLivingCapabilityStatus();
5.15 发送返回键事件给实例接口
在需要发送返回键事件给实例时,可以通过调用此方法发送返回键事件。
函数原型:
public class HmcpVideoView {
// KeyType 是枚举类,这里参数固定为 KeyType.KEY_BACK
public void sendKeyEvent(KeyType keyType);
}
表5-16 参数说明
参数 | 类型 | 说明 |
---|---|---|
KeyType | ENUM | 按键类型,KeyType.KEY_BACK是返回键 |
示例代码:
// 发送返回键事件,KeyType 是枚举类,这里参数固定为 KeyType.KEY_BACK
ijkVideoView.sendKeyEvent(KeyType.KEY_BACK);
5.16 设置录音回调接口
设置为APP侧后,需要设置录音回调,当触发录音的场景,sdk通过录音回调接口通知APP开始录音、停止录音。RecordParameter为APP侧录音,SDK回调APP的录音参数。
函数说明:
public class HmcpVideoView {
public void setAudioRecordCallback(AudioRecordSourceCallback callback)
}
表5-17 参数说明
参数 | 类型 | 说明 |
---|---|---|
callback | AudioRecordSourceCallback | 结果回调 |
表5-18 RecordParameter参数说明
方法 | 返回值 | 说明 |
---|---|---|
getFormat | Format | 录音数据格式,目前只支持Format.PCM |
getChannel | int | 通道数 |
getEncoding | int | 数据格式位宽 |
getSampleRate | int | 采样率 |
示例代码:
hmcpVideoView.setAudioRecordCallback(new AudioRecordSourceCallback() {
@Override
public void onAudioRecordStart(RecordParameter parameter) {
//SDK回调APP开始录音,并将录音参数RecordParameter传递给APP
}
@Override
public void onAudioRecordStop() {
//SDK回调APP录音停止
}
@Override
ublic void onAudioRecordError(String errMsg) {
//SDK回调APP录音出现异常
}
});
5.17 传输录音数据接口
申请服务时,设置了APP侧录音,SDK回调APP录音开始后,APP可以调用SDK的接口,传输录音数据到实例侧服务。
函数说明:
public class HmcpVideoView {
public void sendAudioData(byte[] data)
}
表5-19 参数说明
参数 | 类型 | 说明 |
---|---|---|
data | byte[] | 录音数据 |
示例代码:
hmcpVideoView.sendAudioData(data);
5.18 停止录音接口
申请服务时,设置了APP侧录音,录音过程中,APP可以停止录音。
函数说明:
public class HmcpVideoView {
public void stopRecord()
}
示例代码:
hmcpVideoView.stopRecord();
5.19 设置可存档最小游戏时间并退出游戏接口
使用此接口可设置可存档最小游戏时间并退出游戏。
函数原型:
public class HmcpVideoView{
public void stopAndDismiss(Activity activity, int archiveMinSeconds);
}
表5-20 设置可存档最小游戏时间并退出游戏参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
activity | Activity | 是 | APP播流页Activity对象,设置此参数,SDK会调用Activity的finish方法,关闭播流页 |
archiveMinSeconds | int | 是 | 可存档游戏的最小游戏时间,单位为s,小于0时当0处理 |
示例代码:
// 退出游戏,游戏时间大于 60 秒以上才可以存档,关闭activity
ijkVideoView.stopAndDismiss(activity, 60);
// 退出游戏,游戏时间大于 60 秒以上可以存档
ijkVideoView.stopAndDismiss(null, 60);
5.20 游戏启动参数携带执行脚本接口
在启动游戏组件时可以携带参数componentType、 action、componentName和extraData,其中:
- componentType、action和componentName通过bundle传递,详见申请服务。
- extraData通过方法setExtraData传递。
表5-21 参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
componentType | int | 否 | 组件类型。 Activity:HmcpVideoView.COMPONENT_TYPE_ACTIVITY Service:HmcpVideoView.COMPONENT_TYPE_SERVICE Broadcast:HmcpVideoView.COMPONENT_TYPE_BROADCAST |
action | String | 否 | 组件对应的action |
componentName | String | 否 | 指定的组件名,格式(packageName/componentName), 如(快手开心消消乐):com.happyelements.AndroidAnimal.kuaishou /com.happyelements.hellolua.MainActivity |
extraData | IntentExtraData | 否 | 额外参数 |
示例代码:
Bundle bundle = new Bundle();
bundle.putInt(HmcpVideoView.COMPONENT_TYPE, HmcpVideoView.COMPONENT_TYPE_ACTIVITY);
bundle.putString(HmcpVideoView.COMPONENT_ACTION, “android.intent.action.MAIN”);
bundle.putString(HmcpVideoView.COMPONENT_NAME, “com.happyelements.AndroidAnimal.kuaishou/com.happyelements.hellolua.MainActivity”);
hmcpVideoView.play(bundle);
表5-22 IntentExtraData结构定义
参数 | 类型 | 说明 |
---|---|---|
booleanExtra | HashMap<String,Boolean> | 向Intent中添加Boolean |
integerExtra | HashMap<String,Integer> | 向Intent中添加Integer |
integerArrayExtra | HashMap<String, Integer[]> | 向Intent中添加Integer数组 |
integerListExtra | HashMap<String,List |
向Intent中添加Integer List |
stringExtra | HashMap<String, String> | 向Intent中添加String |
stringArrayExtra | HashMap<String, String[]> | 向Intent中添加String数组 |
stringListExtra | HashMap<String, List |
向Intent中添加String List |
floatExtra | HashMap<String, Float> | 向Intent中添加Float |
floatArrayExtra | HashMap<String, Float[]> | 向Intent中添加Float数组 |
floatListExtra | HashMap<String, List |
向Intent中添加Float List |
longExtra | HashMap<String, Long> | 向Intent中添加Long |
longArrayExtra | HashMap<String, Long[]> | 向Intent中添加Long数组 |
longListExtra | HashMap<String, List |
向Intent中添加Long List |
componentNameExtra | HashMap<String, String> | 向Intent中添加componentName |
uriExtra | HashMap<String, String> | 向Intent中添加uri |
applink | String | 远程组队分享链接 |
函数原型:
public class HmcpVideoView{
public void setExtraData(IntentExtraData extraData);
}
示例代码:
IntentExtraData extraData = new IntentExtraData();
HashMap<String, String> map = new HashMap<>();
map.put("StringKey1", “StringValue1”);
extraData.setStringExtra(map);
hmcpVideoView.setExtraData(extraData);
5.21 帧延迟数据接口
APP开启SDK的帧延迟数据回调功能后,SDK会周期性(譬如1秒、2秒)回调APP帧延迟数据。以下帧延迟数据简称sei。
sei数据开启条件如下:
APP设置SDK开启sei开关。
saas侧配置sei数据回调间隔时间,具体配置请联系项目对接人。
saas侧配置sei开关,具体配置请联系项目对接人。
saas侧配置sei开关策略,具体配置请联系项目对接人。
函数原型:
public class HmcpVideoView{
public void setSeiListener(ISeiListener listener);
表5-23 参数说明
参数 | 类型 | 说明 |
---|---|---|
listener | ISeiListener | APP开启sei数据,SDK通过ISeiListener回调APP sei数据集合 |
表5-24 ISeiListener说明
方法 | 返回值 | 参数 | 说明 |
---|---|---|---|
onData | 无 | List |
sei数据集合 |
表5-25 RtcSeiData说明
方法 | 返回值 | 参数 | 说明 |
---|---|---|---|
getCid | String | 无 | cid |
getUid | String | 无 | uid |
getFrameId | int | 无 | 帧id |
getPreEncoderTimestamp | long | 无 | 编码前时间戳 |
getEncodedTimestamp | long | 无 | 编码后时间戳 |
getPreDecoderTimestamp | long | 无 | 解码前时间戳 |
getDecodedTimestamp | long | 无 | 解码后时间戳 |
saas侧配置sei数据回调间隔时间,譬如1秒。
saas侧配置sei开关打开。
SDK侧sei开关配置:申请服务接口中,增加sei开关参数,示例代码如下:
Bundle bundle = new Bundle(); bundle.putInt(HmcpVideoView.SEI, 1);//sei数据开启 hmcpVideoView.play(bundle);
APP设置sei数据回调,示例代码如下:
HmcpVideoView.setSeiListener(new ISeiListener() { public void onData(List<RtcSeiData> data) { //to do someting... } });
5.22 将数据写入到云端的文本框接口
函数原型:
public class HmcpVideoView {
public boolean inputText(String payload);
}
表5-26 参数说明
参数 | 类型 | 说明 |
---|---|---|
payload | String | 发送的内容(最大长度256个字符) |
示例代码:
ijkVideoView.inputText("this is test data");
5.23 与云游戏传输数据接口
播流成功之后可以与云游戏(游戏需接入MessageSDK-localPort)进行数据传输。
此功能需要配置开启,默认关闭,如需开启请联系项目对接人员。
函数原型:
public class TransferHelper{
public void connect();
public void disconnect();
public boolean isConnected();
public int send(byte[] buffer);
public void setCallback(Callback callback);
}
表5-27 参数说明
方法名 | 参数 | 类型 | 描述 |
---|---|---|---|
connect | 无 | 无 | 建立连接 |
disconnect | 无 | 无 | 断开连接 |
isConnected | 无 | 无 | 是否连接 |
send | buffer | byte[] | 发送数据 |
setCallback | callback | Callback | 设置回调接口 |
示例代码:
public void startTransfer(View view) {
if (ijkVideoView.getTransferDelegate() == null) {
Toast.makeText(this, "该游戏不支持数据传输", Toast.LENGTH_LONG).show();
return;
}
if (!ijkVideoView.getTransferDelegate().isConnected()) {
ijkVideoView.getTransferDelegate().connect();
ijkVideoView.getTransferDelegate().setCallback(mCallBack);
startTransferBtn.setText("关闭传输");
} else {
ijkVideoView.getTransferDelegate().disconnect();
startTransferBtn.setText("开启传输");
}
}
函数原型:
public interface Callback {
void onConnect(boolean isReconnect);
void onClose();
void onBinaryMessage(byte[] payload);
void onTextMessage(String payload);
}
表5-28 参数说明
方法名 | 参数 | 类型 | 描述 |
---|---|---|---|
onConnect | 无 | 无 | 连接建立回调 |
onClose | 无 | 无 | 连接断开回调 |
onBinaryMessage | payload | byte[] | 收到二进制数据 |
onTextMessage | payload | String | 收到文本数据 |
示例代码:
private TransferHelper.Callback mCallBack = new TransferHelper.Callback() {
@Override
public void onConnect(boolean isReconnect) {
Toast.makeText(BasePlayerActivity.this, "长连接成功", Toast.LENGTH_LONG).show();
startTransferBtn.setText("关闭传输");
}
@Override
public void onClose() {
Toast.makeText(BasePlayerActivity.this, "长连接断开", Toast.LENGTH_LONG).show();
startTransferBtn.setText("开启传输");
}
@Override
public void onBinaryMessage(byte[] payload) {
LogUtils.i(TAG,"payload: " + new String(payload));
}
@Override
public void onTextMessage(String payload) {
LogUtils.i(TAG,"payload: " + payload);
}
};
5.24 获取每帧数据
当前只提供每帧渲染完成的帧id、时间戳数据,数据通过子线程回调方式提供,非实时、间隔性回调数据。
函数原型:
public class HmcpVideoView {
public void setHmFrameCallback(HmFrameCallback callback);
}
表5-29 参数说明
方法名 | 参数 | 类型 | 描述 |
---|---|---|---|
setHmFrameCallback | callback | HmFrameCallback | 数据回调接口 |
表5-30 HmFrameCallback 说明
方法名 | 参数 | 类型 | 描述 |
---|---|---|---|
onFrame | infoData | String | JSON数据结构 { “id”: “”, “rendered_time_stamp”: “” } |
示例代码:
hmcpVideoView.setHmFrameCallback(new HmFrameCallback() {
@Override
public void onFrame(String infoData) {
Log.d(TAG, infoData);
}
});
5.25 一键返回游戏界面接口
通过HmcpVideoView中的backToGame()接口执行返回按键操作。请根据已有逻辑, 在合适的地方增加backToGame()方法调用, 对云端设备发送指令, 相当于手机中唤起游戏到前台。
适用场景:针对有些第三方登录多级,无法返回的情况。
函数原型:
public class HmcpVideoView {
public void backToGame()
}
示例代码:
hmcpVideoView.backToGame();
5.26 通知SDK权限结果接口
一些云游戏中功能,会触发一些权限的申请,譬如语音连麦需要录音权限、人脸识别功能需要摄像头权限。当SDK没有权限时,SDK回调通知APP申请权限(详情请参见消息回调中onPermissionNotGranted回调接口),用户允许或者拒绝权限后,APP要调用接口通知SDK权限结果。
如果APP没有摄像头权限,SDK会通知APP向用户申请摄像头权限,并一直等待授权结果,APP需要实现用户申请权限的界面和交互,并在用户同意或者不同意授权的时候将结果通知给SDK,以便摄像头的后续处理。
⚠注意:
当前只支持摄像头权限处理,SDK通过onPermissionNotGranted接口通知APP权限申请,SDK没有收到APP告知权限结果之前,不会再次通知APP相同权限申请。
函数原型:
public class HmcpVideoView {
public void handlePermissionResult(String permission, boolean isGranted);
}
示例代码:
@Override
public void onPermissionNotGranted(String permission) {
if (permission.equals(Manifest.permission.CAMERA)) {
// APP帮助SDK申请摄像头权限
ActivityCompat.requestPermissions(
BasePlayerActivity.this,
new String[]{Manifest.permission.CAMERA},
917
);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 917:
// 摄像头权限结果通知SDK
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
hmcpVideoView.handlePermissionResult(Manifest.permission.CAMERA, true);
} else {
hmcpVideoView.handlePermissionResult(Manifest.permission.CAMERA, false);
}
}
}
5.27 获取用户最后一次操作时间戳接口
游戏启动后,通过该接口获取用户最后一次操作实例的时间戳。
函数原型:
public class HmcpVideoView {
public long getLastUserOperationTimestamp()
}
示例代码:
// 默认返回0,代表用户没有操作过实例,否则返回相应时间戳
hmcpVideoView.getLastUserOperationTimestamp();
5.28 重置无操作超时计时器接口
游戏启动后,重置无操作超时计时器时调用此函数。
函数原型:
public class HmcpVideoView {
public int resetInputTimer()
public int resetInputTimer(boolean needUpdateTimestamp)
}
表5-31 重置无操作超时计时器参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
needUpdateTimestamp | boolean | 是 | true:表示重置计时器并且更新用户最后一次操作时间戳 false:表示重置计时器不更新用户最后一次操作时间戳 |
表5-32 返回结果说明
返回结果 | 类型 | 意义 |
---|---|---|
result | int | 0 : 表示和实例连接异常 1 : 表示重置无操作超时计数器成功 |
示例代码:
hmcpVideoView.resetInputTimer();
hmcpVideoView.resetInputTimer(false);
5.29 获取当前播放器截屏接口
游戏启动后,获取当前截屏和检测功能时调用此函数。
函数原型:
public class HmcpVideoView {
public Bitmap getShortcut()
}
表5-33 当前播放器截屏返回结果说明
返回结果 | 类型 | 说明 |
---|---|---|
Bitmap | Bitmap | 当前播放器截屏的 bitmap 对象 |
示例代码:
Bitmap bitmap = hmcpVideoView.getShortcut();
5.30 检测当前截屏是否黑屏接口
检测当前截屏的bitmap对象是否黑屏功能时调用此函数。
函数原型:
public class HmcpVideoView {
public int checkBitmap (Bitmap bitmap)
}
表5-29 检测当前截屏是否黑屏参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
bitmap | Bitmap | 是 | 需要检测的bitmap对象 |
表5-34 返回结果说明
返回结果 | 类型 | 说明 |
---|---|---|
1/2/3/4 | int | 1:表示检测结果为纯色 排除黑色和全透明色,因为播放器没开始工作,不通设备获取截屏有的是纯黑色有的是纯透明色 2:表示检测结果为纯透明色 3:表示检测结果为正常 4:表示检测结果为纯黑色 |
示例代码:
int result = hmcpVideoView.checkBitmap(bitmap);
5.31 视频帧卡顿通知接口
SDK监控视频帧在一定时间内处于卡顿的情况,通过回调接口通知接入方,譬如当前播流分辨率为高清,接入方收到通知后,可以提示用户降低分辨率。
⚠注意:
接口回调处于主线程上。
public class HmcpVideoView {
public int setStreamingCallback(StreamingCallback callback)
}
表5-35 视频卡顿通知参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
callback | StreamingCallback | 是 | 视频帧卡顿回调接口 |
表5-36 StreamingCallback说明
方法名 | 参数 | 类型 | 描述 |
---|---|---|---|
onFreeze | 无 | 无 | 视频帧出现卡顿情况 |
示例代码:
hmcpVideoView.setStreamingCallback(new StreamingCallback() {
@Override
public void onFreeze() {
//接入方处理自己的逻辑
}
});
6 场景说明
6.1 排队管理
排队管理的触发条件:接入方所购买云服务实例和并发数达到上限。
处理机制:
onSceneChanged接收到需要排队提示。
HmcpPlayerStatusCallback接收到status = STATUS_WAIT_CHOOSE状态回调:多优先级用户排队参数增加说明。
onSceneChanged ==> {"sceneId":"needWait"} // 收到排队消息 "status":7 "data":{"message":"当前排队 1 人,是否继续?--test", "queues":[{"index":1,"priorities":[0,1],"rank":0,"time":"300","timeStr":"5分钟"}, {"index":0,"priorities":[2],"rank":1,"time":"0","timeStr":"00:00"}]}
调用开启排队videoView.entryQueue(),然后会接收到status = STATUS_OPERATION_INTERVAL_TIME状态回调信息。
// 排队状态更新,返回该所有队列的排队信息 status:13 {"message":"当前排队1人,当前正在排队3秒人,请稍候…", "queues":[{"index":1,"priorities":[0,1],"rank":0,"time":"3","timeStr":"3秒"}, {"index":0,"priorities":[2],"rank":1,"time":"0","timeStr":"00:00"}]}
在海马云游戏管理平台进行配置禁止排队,则返回排队信息如下:
status:10 Data:{"message":"当前游戏太过火爆,请您稍后再试", "queues":[{"index":1,"priorities":[0,1],"rank":0,"time":"300","timeStr":"5分钟"}, {"index":0,"prioritie":[2],"rank":1,"time":"0","timeStr":"00:00"}]}
调用取消排队ideoView.exitQueue()并直接退出游戏。
6.2 横竖屏切换
SaaS SDK提供动态切换横竖屏游戏画面功能。客户端通过SDK提供的接口,传入指定游戏横竖屏标识参数。
在申请服务的时候传入orientation字段,可以控制游戏画面横竖屏显示。
设置横屏:
bundle.putSerializable(HmcpVideoView.ORIENTATION, ScreenOrientationLANDSCAPE);
设置竖屏:
bundle.putSerializable(HmcpVideoView.ORIENTATION, ScreenOrientation.PORTRAIT);
6.3 清晰度切换
在海马云游戏管理平台进行配置游戏的清晰度参数(高清、超清、流畅)。
调用开始游戏后,如果成功会有sceneId = “play”的场景回调。
onSceneChanged ===> {"extraInfo":{"cur_rate":"200"},"sceneId":"play"}
SDK调用HmcpManager.getInstance().getResolutionDatas()获取当前游戏的清晰度列表;
"resolution":[ {"bitRate":"9600000","defaultChoice":"0","frameRate":60,"id":"4","name":"1080P","resolution":"1920x1080"}, {"bitRate":"4000000","defaultChoice":"0","frameRate":60,"id":"2","name":"标清","resolution":"960x540"}, {"bitRate":"3200000","defaultChoice":"1","frameRate":60,"id":"3","name":"高清","resolution":"1280x720"}, {"bitRate":"2400000","defaultChoice":"0","frameRate":60,"id":"1","name":"流畅","resolution":"848x480"} ]
监听回调函数onSceneChanged并收到sceneId=”crtp”消息:
onSceneChanged ===> {"extraInfo": {"delay_less_minimum":"0","minimum":0},"sceneId":"crtp"}
表6-1 清晰度切换返回值说明
返回值 类型 说明 delay_less_minimum String 当前码率是否是最低码率。
0:表示否
1:表示是minimum int 当前码率是否低于下限。
0:表示否
1:表示是开始切换码率:
// 发起切换码率 recordSceneEvent ===> crst{"des":"1024","method":0,"source":"50000"} // 切换码率时回调 onSceneChanged ===> {"extraInfo":{"des":"1024","method":0,"source":"50000"},"sceneId":"crst"}
表6-2 参数说明
参数 类型 必填项 说明 method int 是 切换码率模式。
0:表示手动切换码率
1:表示自动切换码率des String 是 目标码率 ID - 如果手动调用onSwitchResolution()切换码率。
- onSceneChanged接收到切换码率的消息回调scenedId = “crst”。
onSceneChanged收到scenedId = “cred”切换码率后结果:
recordSceneEvent ===> cred {"cur_rate":"500","result":1}
表6-3 返回结果说明
返回结果 类型 说明 cur_rate String 当前码率是否是最低码率。
0:表示否
1:表示是result int 0:表示失败
1:表示成功
7 数据结构定义
7.1 场景切换回调
游戏过程中的各个状态切换都会通过onSceneChanged(String sceneMessage)接口通知应用,sceneMessage字符串是有JSON串转换的,格式如下:
{
// 切换到的场景ID
"sceneId": "" ,
// 场景切换的扩展信息,参考sceneId说明
"extraInfo" {} ,
}
表7-1 游戏过程中各状态的sceneId说明
sceneId | extraInfo{} |
---|---|
init | 初始化中,extraInfo为空 |
wait | 排队中,extraInfo为空 |
play | 开始游戏,extraInfo为空 |
stop | 游戏结束,extraInfo参考参数示例 |
mait | 云玩维护,extraInfo参考参数示例 |
crtp | 显示自动降低码率tips,extraInfo 参考参数示例 |
crst | 开始切换码率(当前码率,目标码率,自动手动模式),extraInfo参考参数示例 |
cred | 切换码率完成(成功,失败,当前码率),extraInfo参考参数示例 |
firstFrameArrival | 拿到游戏视频第一帧 |
以下是游戏过程中各状态对应的扩展信息extraInfo{}描述:
stop : 游戏结束。
"extraInfo" : { "interval" : 当次游戏时长,单位为s "reason" : "time_limit" :当次游戏事件到 "no_operation" :超时无操作 "instance_err" :实例出错 "forced" :强制下线 "token_expire" :Token失效 "log_off" :游戏内注销 "network_off" :网络不可用 "request_err" :网络请求出错 }
mait:云玩维护。
"extraInfo" : { "progress" : "soon" :即将, "start":进入, "done" :恢复 }
crtp:显示自动降低码率 tips。
"extraInfo" : { "minimum" : 当前码率是否最低码率 "0"-否 "1"-是 }
crst:开始切换码率(当前码率、目标码率、自动手动模式)。
"extraInfo" : { "source" : "当前码率" , "des" : "目标码率" , "method" : 自动手动模式 "0"-手动 "1"-自动 }
cred : 切换码率完成 (成功,失败,当前码率)。
"extraInfo" : { // "0"- 失败 "1"-成功 "result" : 0, // 当前码率 "cur_rate" : 1 }
7.2 消息回调
消息方法:
public void onMessage(Message message){}
Message类的定义如下:
public class Message implements Serializable {
// 目标接收者cid,全部推送"ALL"
public String to;
// 消息ID
public String mid;
// 消息内容
public String payload;
// 发送者,系统消息="System"
public String from;
// 消息类型,默认是传 1
public int type;
// 客户端发送默认为0;服务端收到回复为1
public int ack;
// 可选参数,目标uid
public String uid;
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
表7-2 消息回调参数说明
参数 | 类型 | 必填项 | 说明 |
---|---|---|---|
uid | String | 否 | 可选参数,目标uid |
from | String | 是 | 消息发送方标识 |
to | String | 是 | 消息接收方标识 |
mid | String | 是 | 消息id |
type | Integer | 是 | 消息类型 |
ack | Integer | 是 | 固定值为0 |
payload | String | 是 | 消息内容 |
7.3 SDK状态回调
SDK的各个状态切换和sever下发通知都会通过HmcpPlayerStatusCallback(String callback)接口通知应用,callback字符串是有 JSON 串转换的,格式如下:
{
// 切换到的场景ID
"status": "" ,
// 扩展信息
"data" {} ,
}
表7-3 状态说明
状态说明 | status | data |
---|---|---|
开始请求游戏 | STATUS_PLAY_INTERNAL = 1 | 无 |
开始播流 | STATUS_START_PLAY = 2 | 无 |
停止播放 | STATUS_STOP_PLAY = 3 | 无 |
4g切换到wifi | STATUS_TIPS_CHANGE_4G_TO_WIFI = 4 | 无 |
Wifi切换到4g | STATUS_TIPS_CHANGE_WIFI_TO_4G = 5 | 配置的弹框话术 |
网络断开 | STATUS_NETWORK_UNAVAILABLE = 6 | 配置的弹框话术 |
需要排队 | STATUS_WAIT_CHOOSE = 7 | 返回数据参照多优先级用户排队参数增加说明 |
开始重新连接 | STATUS_START_RECONNECTION = 8 | 无 |
重连失败 | STATUS_CONNECTION_ERROR = 9 | 配置的弹框话术 |
排队人数过多 | STATUS_OPERATION_REFUSE_QUEUE = 10 | 返回数据参照多优先级用户排队参数增加说明 |
无操作时间超过配置 | STATUS_TOAST_NO_INPUT = 11 | {“errorCode”:””,”errorCodeWithoutCid”:””, “errorMessage”:””,”status”:””} |
进入队列消息 | STATUS_OPERATION_INTERVAL_TIME = 13 | 返回数据参照多优先级用户排队参数增加说明 |
获取流地址成功 | STATUS_OPERATION_STREAM_URL = 14 | {“currentNetType”:””, “currentApkType”:””} |
游戏时间到 | STATUS_OPERATION_GAME_OVER = 15 | {“errorMessage”:””, “finishByServer”:””, “finishTip”:””} |
排队完成消息 | STATUS_OPERATION_WAITING = 16 | 配置的弹框话术 |
服务准备维护 | STATUS_OPERATION_READY_PAUSE_SAAS_SERVER=17 | {“errorMessage”:””} |
服务器开始维护 | STATUS_OPERATION_PAUSE_SAAS_SERVER = 18 | {“errorMessage”:””} |
服务维护中 | STATUS_OPERATION_PAUSED_SAAS_SERVER = 19 | 配置的弹框话术 |
切换分辨率 | STATUS_SWITCH_RESOLUTION = 20 | {“defaultChoiceId”:””, “switchResolution”:””} |
播放器创建失败 | STATUS_MEDIAPLAYER_ERROR = 22 | 无 |
获取流地址超时 | STATUS_TIME_OUT = 23 | {“errorCode”:””,”errorCodeWithoutCid”:””, “errorMessage”:””,”status”:””,”rootCause”} |
Tocken 过期服务终止 |
STATUS_OPERATION_FORCED_OFFLINE = 24 | {“errorCode”:””,”errorCodeWithoutCid”:””, “errorMessage”:””,”status”:””} |
自动切换分辨率 | TATUS_AUTO_SWITCH_RESOLUTION = 25 | {“mCurrentSpeed”:”” “mMinResolutionLevel”:””} |
测速结果低于服务下限 | STATUS_SPEED_LOWER_BITRATE = 26 | 配置的弹框话术 |
游戏多开 | STATUS_OPERATION_OPEN_MORE_SAME_GAME = 27 | {“errorCode”:””,”errorCodeWithoutCid”:””, “errorMessage”:””,”status”:””} |
服务连接错误 | STATUS_OPERATION_HMCP_ERROR = 29 | {“errorCode”:””,”errorCodeWithoutCid”:””, “errorMessage”:””,”status”:””} |
游戏时长提示 | STATUS_OPERATION_GAME_TIME_COUNT_DOWN = 30 | {“ahead”: “”, “countDown”: “”, “formatter”: “”} |
游戏内更新时长 | STATUS_OPERATION_GAME_TIME_UPDATE = 31 | {“tip”: “”} |
游戏可玩时长提示 | STATUS_OPERATION_GAME_TIME_HIGHLIGHT = 32 | {“playingTime”: “”} |
获取到sever配置信息 | STATUS_RECEIVE_META_INFOS = 33 | 无 |
暂停播放 | STATUS_PAUSE_PLAY = 35 | 无 |
(TV)菜单呼出 | STATUS_SHOW_SETTING_VIEW = 101 | 无 |
第一帧动画到达 | STATUS_FIRST_FRAME_ARRIVAL = 102 | 无 |
IDC信息 | STATUS_IDCINFO=44 | {“idcId”: “”,”idcName”: “”,”instanceTag”: “”,”instanceTagName”: “”} |
表7-4 返回结果说明
返回结果 | 类型 | 说明 |
---|---|---|
errorCode | String | 发生出错误的错误码 |
errorMessage | String | 发生错误的错误说明 |
errorCodeWithoutCid | String | 发生出错误的错误码(不包含”-cid”) |
rootCause | String | 对应请求描述 |
status | String | 错误信息状态 |
currentNetType | String | 网络类型 |
currentApkType | String | 游戏池化状态 |
finishByServer | String | 游戏时长到,是否结束游戏(服务端配置) |
finishTip | String | 游戏时长到状态说明 |
defaultChoiceId | String | 切换分辨率ID |
switchResolution | String | 是否为自动切换 |
mCurrentSpeed | String | 当前网络速度 |
mMinResolutionLevel | String | 配置的最低播流速度 |
ahead | String | 游戏时长提示的剩余时长 |
countDown | String | 是否为倒计时,countDown == 1 为倒计时 |
formatter | String | 游戏时长提示说明 |
tip | String | 更新游戏时长说明 |
playingTime | String | 可玩游戏时长 |
stateChangeReason | String | 接入方通过openApi退出游戏时自定义原因 |
7.4 关键接口回调
接入方必须设置此接口来监听云游戏过程中的状态变化,HmcpPlayerListener监听类定义如下:
public interface HmcpPlayerListener {
/**
* 启动游戏失败
*
* @param errorType 错误的类型名:NETWORK_ERROR,OTHER
* @param errorInfo 错误的消息
*/
void onError(ErrorType type, String errorInfo);
/**
* 启动游戏成功
*/
void onSuccess();
/**
* 游戏退出
* 需要在该回调内调用finish()时,退出Activity
*/
void onExitQueue();
/**
* SDK内消息通知,包括支付消息等
*
* @param message 消息内容
*/
void onMessage(Message message);
/**
* SDK游戏过程中各状态发生变化时的回调
*
* @param sceneMessage是JSON结构体,包含 sceneId 和 extraInfo{}。具体详细请参考“参数说明”中的“场景切换”章节。
*/
void onSceneChanged(String sceneMessage);
/**
* 监听到网络变化后的通知
*
* @param state 是enum类型,包含 ISWIFI, NOTWIFI, NO_NETWORK
*/
void onNetworkChanged(NetWorkState state);
/**
* @param status:状态值,可为 0、1、2
* @param value:
* status=0,value是采样周期内接受到的视频帧的总大小,单位:Byte;
* status=1,value是采样周期内,渲染帧的总数;
* status=2,value是采样周期内,平均解码耗时,单位是ms;
*
* @param data:附加信息,status=0,data为采样周期内最大的N帧大小,单位为Byte;status=其他,data无数据;
*/
void onPlayStatus(int status, long value, String data);
/**
* 使用状态回调接口
*
* @param callback 状态信息
*/
void HmcpPlayerStatusCallback(String callback);
/**
* 上报错误信息回调接口
*
* @param errorCode 错误码
* @param errorMsg 错误信息
*/
void onPlayerError(String errorCode, String errorMsg);
/**
* 上报错误信息回调接口
*
* @param errorCode 错误码
* @param errorMsg 错误信息
*/
void onInputMessage(String message);
/**
* 监听到输入设备变化的通知
*
* @param device 设备类型
* @param operationType 键盘类型
*/
void onInputDevice(int device, int operationType);
/**
* 没有相关敏感权限时回调
*
* @param permission 权限名称(如:Manifest.permission.RECORD_AUDIO)
*/
void onPermissionNotGranted(String permission);
/**
* 云游戏中远端实例状态发生改变
* 云游戏中分享、打开摄像头、打开系统相册、游戏内截图
*
* @param status 状态信息,详情请参见“云游戏中远端实例状态通知”
*/
void onCloudDeviceStatus(String status);
/**
* intent拦截回调
*
* @param intentData 拦截数据内容,详情请参见“Intent拦截”
*/
void onInterceptIntent(String intentData);
}
7.5 多优先级用户排队参数增加说明
SDK状态回调增加多优先级用户排队状态返回。
表7-5 状态说明
状态 | status | data |
---|---|---|
需要排队 | STATUS_WAIT_CHOOSE=7 | {“message”:“配置的弹框话术” “queues”:“队列消息”} |
进入队列消息 | STATUS_OPERATION_INTERVAL_TIME=13 | {“message”:“配置的弹框话术” “queues”:“队列消息”} |
排队人数过多 | STATUS_OPERATION_REFUSE_QUEUE=10 | {“message”:“配置的弹框话术” “queues”:“队列消息”} |
其中具体数据格式为:
{
"message": "配置的弹框话术",
"queues": [{
"rank": 1,
"timeStr": "09分15秒",
"time": "555",
"index": 1,
"priorities": [
0,
1
]
},
{
"rank": 2,
"timeStr": "09分15秒",
"time": "555",
"index": 1,
"priorities": [
2,
3
]
}
]
}
表7-6 返回值说明
返回值 | 类型 | 说明 |
---|---|---|
message | String | 返回的配置的弹框话术 |
queues | List | 队列信息 |
rank | int | 队列等级 |
index | int | 排队人数 |
time | String | 预估时间,单位为s |
timeStr | String | “09分15秒”格式字符串 |
priorities | List | 用户优先级列表,即配置的该等级队列中包含那些优先级的用户 |
7.6 清晰度信息ResolutionInfo类的定义
public class ResolutionInfo implements Serializable {
// 清晰度信息ID
public String ID;
// 清晰度名称
public String name;
// 清晰度,形如<width>x<height>
public String resolution;
// 峰值码率,单位KB/s
public String peakBitRate;
// 码率,单位为bit/s,类型为long
public String bitRate;
// 帧率,类型为Integer
public int frameRate;
// 是否是默认选择
public String defaultChoice;
// 是否开启
public String close;
public final String choiced = "1";
@Override
public String toString() {
return "ResolutionInfo{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", resolution='" + resolution + '\'' +
", peakBitRate='" + peakBitRate + '\'' +
", frameRate='" + frameRate + '\'' +
", bitRate=' " + bitRate + '\'' +
", defaultChoice='" + defaultChoice + '\'' +
", close='" + close + '\'' +
'}';
}
}
调用play()接口成功会有sceneId = “play” 的场景回调,此时可调用HmcpManager.getInstance().getResolutionDatas()得到游戏的清晰度列表。
调用hmcpVideoView.onSwitchRsolution()接口后也会有场景回调。
收到sceneId = “crst”的场景回调,表示开始切换清晰度。
收到scenedId = ”cred”的场景回调,表示切换清晰度完成。
extraInfo.cur_rate表示切换成功后的码率peakBitRate。
extraInfo.result = 0表示切换失败。
extraInfo.result = 1表示切换成功。
另外如果开启了自动切换码率,网络卡顿时会收到sceneId = ”crtp”的场景回调,该场景回调下:
- extraInfo.minimum=1表示当前为最低码率。
- extraInfo.minimum=0表示当前不是最低码率,这种情况下启动切换码率后会收到 scenedId = ”cred”的场景回调,需要APP更新当前清晰度。
场景回调的参数详情请参见场景切换回调。
7.7 TV键位映射配置格式
键位映射配置格式:
{"version": "2.0",
"defaultMouseMode": false,
"buttonMappingsList": [{
"mappingName": "侍魂",
"mapping": [
{"buttonName": "buttonX","x": 0.66,"y": 0.87,"keyCode": 99},
{"buttonName": "buttonY","x": 0.75,"y": 0.66,"keyCode": 100},
{"buttonName": "buttonA","x": 0.81,"y": 0.87,"keyCode": 96},
{"buttonName": "buttonB","x": 0.91,"y": 0.66,"keyCode": 97},
{"buttonName": "trackBallL","x": 0.17,"y": 0.76,"rx": 0.12,"ry": 0.2,"keyCode": 999},
{"buttonName": "buttonUp","x": 0.17,"y": 0.60,"keyCode": 997},
{"buttonName": "buttonDown","x": 0.17,"y": 0.92,"keyCode": 996},
{"buttonName": "buttonLeft","x": 0.05,"y": 0.76,"keyCode": 995},
{"buttonName": "buttonRight","x": 0.25,"y": 0.76,"keyCode": 994},
{"buttonName": "buttonL2","x": 0.09,"y": 0.17,"keyCode": 104},
{"buttonName": "buttonR2","x": 0.91,"y": 0.17,"keyCode": 105},
{"buttonName ": "buttonL1B","keyCode": 97,"fnKeyCode ": 102,
"pointList": [
{"x": 0.17,"y": 0.92,"keyType": 2,"runTime ": 100},
{"x": 0.22,"y": 0.86,"keyType ": 2,"runTime": 100},
{"x": 0.25,"y": 0.76,"keyType": 2,"runTime ": 100},
{"x ": 0.66,"y ": 0.87,"keyType ": 2,"runtime": 100},
{"x": 0.75,"y ": 0.66,"keyType ": 2,"runtime ": 0}
]},
{"buttonName": "buttonL1X","keyCode": 99,"fnKeyCode": 102,
"pointList":[
{"x":0.31,"y":0.99,"keyType":1,"runTime":500},
{"x":0.43,"y":0.95,"keyType":1,"runTime":500},
{"x":0.43,"y":0.77,"keyType":0,"runtime":0}
]},
{"buttonName ": "buttonL1A ","x": 0.31,"y": 0.99,"keyCode": 96,"fnKeyCode ": 102},
{"buttonName ": "buttonR1A ","x": 0.91,"y": 0.17,"keyCode": 96,"fnKeyCode ": 103}
]}]
}
defaultMouseMode:是否默认为鼠标模式。
mappingName:配置名称。
各键位详细说明见获取键位映射数据接口。
增加 version 参数,用来标记配置文件版本,2.0适用组合键和滑屏连续动作,不配则适用原按键配置。
增加 buttonName 参数,用来配置按键的名字,可以自行定义。
组合功能键配置参数将不会生效,所以可以不用配置,例如buttonL1,可以不用配置。
fnKeyCode是组合键功能键标记,例如组合键为L1+A键组合,则将buttonL1A的keyCode属性配置为A键的keyCode,将buttonL1A的fnKeyCode属性配置为L1键的keyCode。
keyCode与键位是一一对应的,可以根据手柄实际KeyCode修改,Android标准键位keyCode对应如下:
BUTTON_A = 96;
BUTTON_B = 97;
BUTTON_X = 99;
BUTTON_Y = 100;
BUTTON_L1 = 102;
BUTTON_R1 = 103;
BUTTON_L2 = 104;
BUTTON_R2 = 105;
十字键与摇杆为约定keycode不可修改
十字键上 = 997;
十字键下 = 996;
十字键左 = 995;
十字键右 = 994;
左摇杆 = 999;
右摇杆 = 998;
遥控器方向键模拟手柄摇杆 = 993;(不支持组合功能,也不支持配置连续操作功能)
增加pointList参数,在按键为连续操作模式时配置,pointList中每一个对象都是一个点,其中
x:点在x轴坐标;
y:点在y轴坐标;
keyType:点的操作类型,int类型参数,各数字代表含义为1:滑动、2:点击、0:结束;
runTime:从当前点动作开始到下一个点的时间设置(点击事件的按下持续时间不可设置,主要是设置滑动的动作时间);
手柄映射遥控器配置增加toKeyCode字段,表示映射遥控器键位keyCode。
7.8 TV键位映射数据返回示例
映射数据示例:
{"KeymappingItem":
[{"mappingName":"张飞",
"joyKeymappingList":[
{"name":"buttonX", "x": 0.73, "y": 0.86 },
{"name":"buttonY", "x": 0.78, "y": 0.68 },
{"name":"buttonB", "x": 0.89, "y": 0.58 },
{"name":"buttonA","x": 0.89, "y": 0.85},
{"name":"buttonUp","x": 0.09, "y": 0.42},
{"name":"buttonDown","x": 0.09, "y": 0.53},
{"name":"buttonLeft", "x": 0.58, "y": 0.89 },
{"name":"buttonRight", "x": 0.66, "y": 0.89 },
{"name":"buttonL1","x": 0.68, "y": 0.78},
{"name":"buttonL2","x": 0.74, "y": 0.61},
{"name":"buttonR2","x": 0.84, "y": 0.48},
{"name":"buttonR1","x": 0.97, "y": 0.15},
{"name":"trackBallL","x": 0.12, "y": 0.79, "rx": 0.05, "ry": 0.09},
{"name":"trackBallR","x": 0.12, "y": 0.79, "rx": 0.05, "ry": 0.09,"sp":0.00}
],
"dpadKeymappingList":[
{"name":"buttonNum0","x": 0.73, "y": 0.86},
{"name":"buttonNum1","x": 0.78, "y": 0.68},
{"name":"buttonNum2","x": 0.89, "y": 0.58},
{"name":"buttonNum3","x": 0.89, "y": 0.85},
{"name":"buttonNum4","x": 0.09, "y": 0.42},
{"name":"buttonNum5","x": 0.09, "y": 0.53},
{"name":"buttonNum6","x": 0.58, "y": 0.89},
{"name":"buttonNum7","x": 0.66, "y": 0.89},
{"name":"buttonNum8","x": 0.68, "y": 0.78},
{"name":"buttonNum9","x": 0.74, "y": 0.61},
{"name":"buttonDirection", "x": 0.12, "y": 0.79, "rx": 0.04, "ry": 0.07},
{"name":"buttonCenter","x": 0.89, "y": 0.85}
]},
{"mappingName":"赵云",
"joyKeymappingList":[
{"name":"buttonX", "x": 0.73, "y": 0.86 },
{"name":"buttonY", "x": 0.78, "y": 0.68 }
]
}],
"keyboardKeymappingList":
[{"buttonName": "buttonY","x": 0.66,"y": 0.87,"keyCode": 89},
{"buttonName": "buttonU","x": 0.75,"y": 0.66,"keyCode": 85},
{"buttonName": "buttonI","x": 0.81,"y": 0.87,"keyCode": 73},
{"buttonName": "buttonH","x": 0.91,"y": 0.66,"keyCode": 72},
{"buttonName": "JoyPad","x": 0.17,"y": 0.76,"rx": 0.12,"ry": 0.2,"keyCode": 888},
{"buttonName": "button1","x": 0.17,"y": 0.60,"keyCode": 49},
{"buttonName": "button2","x": 0.17,"y": 0.92,"keyCode": 50},
{"buttonName": "button3","x": 0.05,"y": 0.76,"keyCode": 51},
{"buttonName": "button4","x": 0.25,"y": 0.76,"keyCode": 52},
{"buttonName": "button5","x": 0.09,"y": 0.17,"keyCode": 53}]
}
其中KeymappingItem数据格式为:
class KeymappingItem {
String mappingName;
List<KeymappingButton> dpadKeymappingList;
List<KeymappingButton> joyKeymappingList;
}
mappingName:一套键位配置的名字
dpadKeymappingList:遥控器键位配置信息
joyKeymappingList:手柄键位配置信息
keyboardKeymappingList :键盘键位配置信息
KeymappingButton :数据格式如下所示。
class KeymappingButton {
/**
* 键位名称
*/
String name;
/**
* 屏幕 x 轴位置(0.00-1.00)
*/
float x;
/**
* 屏幕 y 轴位置(0.00-1.00)
*/
float y;
/**
* x 轴半径长度(手柄摇杆与遥控器方向键需要配置 0.00-1.00)
*/
float rx;
/**
* y 轴半径长度(手柄摇杆与遥控器方向键需要配置0.00-1.00)
*/
float ry;
/**
* 移动视野灵敏度配置(0.00-1.00配置为0则视为不操作视野移动,推荐为0.03左右)
*/
float sp;
}
表7-7 键位名称说明
键位 | 说明 |
---|---|
buttonX | 手柄x键 |
buttonB | 手柄b键 |
buttonA | 手柄b键 |
buttonUp | 手柄十字键上 |
buttonDown | 手柄十字键下 |
buttonLeft | 手柄十字键左 |
buttonRight | 手柄十字键右 |
buttonL1 | 手柄L1键 |
buttonL2 | 手柄L2键 |
buttonR1 | 手柄R1键 |
buttonR2 | 手柄R2键 |
trackballR | 手柄右摇杆 |
trackBallL | 手柄左摇杆 |
buttonNum0 | 遥控器数字键 0 |
buttonNum1 | 遥控器数字键 1 |
buttonNum2 | 遥控器数字键 2 |
buttonNum3 | 遥控器数字键 3 |
buttonNum4 | 遥控器数字键 4 |
buttonNum5 | 遥控器数字键 5 |
buttonNum6 | 遥控器数字键 6 |
buttonNum7 | 遥控器数字键 7 |
buttonNum8 | 遥控器数字键 8 |
buttonNum9 | 遥控器数字键 9 |
buttonDirection | 遥控器方向键 |
buttonCenter | 遥控器确定键 |
⚠注意:
新版键位配置映射数据返回没有 dpadKeymappingList 字段。
8 注意事项
8.1 安卓6.0+系统需要申请权限
申请权限代码如下:
private void requestCloudSdkPermission() {
if (Build.VERSION.SDK_INT >= 23 &&
ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
String[] mPermissionList = new String[]{Manifest.permission.READ_PHONE_STATE};
ActivityCompat.requestPermissions(CloudPlayActivity.this, mPermissionList, 100);
} else {
// 不需要权限
}
}
8.2 虚拟导航栏兼容性设置问题
部分存在虚拟导航栏的手机会存在点击游戏画面错位现象,需要设置虚拟导航栏兼容性代码,添加如下代码即可:
/**
** 设置VideoView的触摸监听事件
*/
hmcpVideoView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int eventAction = event.getAction();
switch (eventAction) {
// 按下动作
case MotionEvent.ACTION_DOWN:
Log.e("MainActivity", "down");
hideNavigationBar();
break;
// 移动
case MotionEvent.ACTION_MOVE:
Log.e("MainActivity", "move");
break;
// 抬起动作
case MotionEvent.ACTION_UP:
Log.e("MainActivity", "up");
break;
}
return false;
}
});
其中依赖的方法如下:
/**
* 隐藏导航栏
*/
private void hideNavigationBar(){
boolean isHas = hasNavigationBar(getApplicationContext());
if (isHas) {
// 此处需要根据系统版本做兼容处理
if ((Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB)) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE);
}
}
}
/**
* 是否有底部导航条
*/
public static boolean hasNavigationBar(Context context) {
boolean hasMenuKey = true, hasBackKey = true;
boolean ret = false;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
}
if ((!hasMenuKey) && (!hasBackKey)) {
ret = true;
}
} catch (Exception e) {
ret = false;
}
return ret;
}
8.3 全屏沉浸式效果
推荐设置游戏界面为全屏沉浸式效果,在onCreate()方法中添加如下代码:
public static boolean setTranslucentStatus(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//4.4以上,状态栏透明
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
return true;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//5.0以上状态栏透明
Window window = activity.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
return true;
}
return false;
}
8.4 一个登录账号开启多个游戏的场景
登录账号在设置用户登录信息中已经提到设置方法,其设置的登录账号可以在一个终端上同时打开多个窗口或者同时在多个终端上打开多个窗口,但是打开窗口的个数有限制。该个数限制由海马云server配置。现在的限制如下:
- 一个账号可以打开同一款游戏的个数限制为2。
- 一个账号可以打开多款游戏的个数限制为5。
如果超过上限,游戏就不能正常开始,SDK会有相应的提示信息。
8.5 云游戏退出二次确认提示框设置
为了避免因为用户误操作点击手机Back按键导致的云游戏退出而增加云游戏二次退出确认信息,示例代码如下:
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
final ExitDialog networkTipsDialog = new ExitDialog(this, R.style.CustomDialog);
networkTipsDialog.onWindowFocusChanged(true);
networkTipsDialog.show();
networkTipsDialog.getSureBtn().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
networkTipsDialog.dismiss();
finish();
}
});
networkTipsDialog.getCancelBtn().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
networkTipsDialog.dismiss();
}
});
}
return true;
}
return super.dispatchKeyEvent(event);
}
依赖相关代码如下:
ExitDialog.java
public class ExitDialog extends Dialog {
private Button cancelBtn, sureBtn;
public ExitDialog(Context context, int theme) {
super(context, theme);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.exit_dialog);
initView();
}
private void initView() {
cancelBtn = (Button) findViewById(R.id.cancelBtn);
sureBtn = (Button) findViewById(R.id.sureBtn);
}
public Button getCancelBtn() {
return cancelBtn;
}
public Button getSureBtn() {
return sureBtn;
}
}
Layout目录中增加exit_dialog.xml。
代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent">
<LinearLayout
android:layout_width="265dp"
android:layout_height="132dp"
android:layout_centerInParent="true"
android:background="#f2f2f2"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_horizontal|center_vertical"
android:text="是否确认退出云游戏?"
android:textColor="#666666"
android:textSize="13sp" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#dddddd" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<Button
android:id="@+id/sureBtn"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_weight="1"
android:background="@null"
android:text="确认退出"
android:textColor="#83b233"
android:textSize="13sp" />
<View
android:id="@+id/splitView"
android:layout_width="0.5dp"
android:layout_height="21dp"
android:background="#dddddd" />
<Button
android:id="@+id/cancelBtn"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_weight="1"
android:background="@null"
android:text="取消"
android:textColor="#666666"
android:textSize="13sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
style.xml中增加自定义样式
代码如下:
<!-- dialog样式 -->
<style name="CustomDialog" parent="@android:style/Theme.Dialog">
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimAmount">0.6</item>
</style>
8.6 云游戏片头正常播放
为了保证云游戏片头正常播放,请将SDK初始化操作在应用application中进行初始化(application中初始化成功之后,在集成云玩activity中不需要再次进行初始化),如果初始化失败请再次初始化,应用Application示例代码如下:
HmcpManager manager = HmcpManager.getInstance();
manager.init(this, new OnInitCallBackListener() {
@Override
public void success() {
// 初始化SDK成功,可以云游戏
}
@Override
public void fail(String s) {
// 初始化SDK失败,不能云游戏,如果初始化失败请尝试重新进行初始化
}
});
8.7 保存上次使用的清晰度
记录用户上次使用的清晰度,下次使用该清晰度播流。
用户使用的清晰度会有场景切换的回调,详情请参见场景切换回调。回调传入的字符串中”cur_rate”表示当前使用的码率,单位为kB/s。在下次启动播流时把改值设置为HmcpVideoView.INTERNET_SPEED传入SDK即可用该码率波流。详情请参见 申请服务。