Android SDK 开发指南
版本号:master-4.33.1
1 接入步骤
欢迎使用云游戏 SDK 。为方便 Android 开发者调试和接入海马云游戏多产品 API,这里向您介绍适用于 Android 开发的工程配置。
1.1 SDK集成
将 SDK 库文件复制到工程的 libs 目录下;
在 Module 的 build.gradle 文件中,添加依赖属性:
repositories { flatDir { dirs 'libs' } } dependencies { implementation 'com.android.volley:volley:1.0.0' implementation 'com.alibaba:fastjson:1.2.0' implementation (name: 'saas-sdk-latest.release', ext: 'aar') implementation 'com.jcodecraeer:xrecyclerview:1.3.2' // 只有TV工程需要添加 }
注:
- 海马云游戏客户端 SDK 目前只支持通过 gradle 集成 SDK 方式
- 其中 saas-sdk-latest.release 是海马的 SDK,其他为 海马SDK 用到的包, saas-sdk-latest.release 根据实际提供的 aar 文件进行修改(.aar文件的部分)。如果无法正常集成,请在Project的build.gradle文件中配置repositories,添加 jcenter
allprojects { repositories { jcenter() } }
1.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" /> <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
请避免混淆海马SDK,在Proguard混淆文件中增加以下配置:
-dontwarn org.codehaus.** -keep class org.codehaus.** {*;} -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 interface com.hmcp.saas.sdk.listeners.*{*;} -keep class com.hmcp.saas.sdk.beans.*{*;} -keep class com.hmcp.saas.sdk.enums.*{*;} -keep class com.hmcp.saas.sdk.SaasSDK{public <methods>;} -keep class de.tavendo.autobahn.**{*;} -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 { *; } -dontwarn org.openudid.** -keep class org.openudid.**{*;}
1.3 配置硬件加速
打开硬件加速可以提高渲染能力,提高用户体验
<application ……
android:hardwareAccelerated="true">
1.4 配置渠道信息
渠道信息包括以下两个字段:
HMCP_ACCESS_KEY_ID:接入商的唯一ID,用来区分不同的接入商。该值由海马云分配,该value必须是String类型。
HMCP_CHANNEL_ID:渠道号,由接入商配置。如果应用本身不区分渠道,可以设置为一个随机的字符串。
这两个参数需要写在AndroidManifest.xml中,代码示例如下:
<application ……>
<meta-data android:name="HMCP_ACCESS_KEY_ID" android:value="海马商户分配的id" />
<meta-data android:name="HMCP_CHANNEL_ID" android:value="渠道id" />
……
</application>
1.5 添加组件
在布局文件中添加 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" />
1.6 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);
}
@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) {
//网络变化后通知应用的接口
}
}
Activity 的 onResume()、onPause()、onDestroy() 必须重写,并在重写的方法中调用 HmcpVideoView 的相应方法,实现可以参考上面的示例代码。
Activity 需要 implements HmcpPlayerListener 接口
1.6.1 HmcpPlayerListener 接口说明
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);
}
device 参数类型
类型 类型描述 INPUT_DEVICE_NONE 检测到输入设备 INPUT_DEVICE_REMOTE_CONTROL 遥控器输入 INPUT_DEVICE_KEY_MOUSE 键盘鼠标输入 INPUT_DEVICE_GAMEPAD 实体手柄输入 INPUT_DEVICE_VIRTUAL_GAMEPAD 虚拟手柄输入 operationType 参数类型
类型 类型描述 INPUT_DEVICE_OPERATION_TYPE_BUTTON 实体按键 INPUT_DEVICE_OPERATION_TYPE_VIRTUAL_BUTTON 虚拟按键 INPUT_DEVICE_OPERATION_TYPE_VIRTUAL_TOUCH 虚拟触摸
1.7 配置云游戏服务地址
String saasAuthUrl = "https://saas-rel.haimawan.com/s/rest/api";//saasAuth url地址
Bundle bundle = new Bundle();
bundle.putString(HmcpManager.BUNDLE_HMCP_SAAS_AUTH_URL, saasAuthUrl);
HmcpManager.getInstance().setServiceUrl(bundle);
2 快速入门&接入步骤
为方便 Android 开发者调试和接入海马云游戏产品 API,这里向您介绍适用于 Android 开发的快速接入文档。
重要接口 | 接口含义 | 建议调用时机 |
---|---|---|
init() | 初始化 SDK | Application 的 onCreate() |
setUserInfo() | 设置用户登录信息 | init() 成功回调后 |
generateCToken() | 计算 cToken 的值 | play(bundle) 调用前 |
play(bundle) | 与海马云 Server 建立通讯、申请服务,成功后则可以开启游戏 | init() 成功回调后 |
说明:
- SDK使用前请对工程进行配置,否则 SDK 不生效。
- SDK 的接口调用要在同一个线程下。
- SDK 回调信息参考回调消息列表。
2.1 初始化SDK
若按照工程配置,已在 AndroidManifest.xml 文件中配置渠道信息:HMCP_ACCESS_KEY_ID 和 HMCP_CHANNEL_ID;
则直接调用 init 方法初始化,否则需进行 ACCESS_KEY_ID 与 CHANNEL_ID 通过Bundle赋值。
初始化方法建议在自定义 Application 类中进行,成功回调 success() 方法,则初始化成功;
函数原型
public class HmcpManager {
public void init(Context context, OnInitCallBackListener callBack)
public void init(Bundle bundle, Context context, OnInitCallBackListener callBack)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
mBID | String | 接入商的唯一 ID,用来区分不同的接入商,该值由海马云分配 |
mChannelID | String | 渠道号,由接入商配置。如果应用本身不区分渠道,可以设置为一个随机的字符串 |
bundle | Bundle | 使用协带 bundle 参数初始化 SDK ,渠道信息将使用 bundle 中携带信息, AndroidManifest.xml 中配置的信息将失效 |
context | Context | 应用程序上下文对象 |
callBack | OnInitCallBackListener | 初始化方法接口回调监听器 |
示例代码
HmcpManager manager = HmcpManager.getInstance();
// bundle.putString(HmcpManager.ACCESS_KEY_ID, mBid); // 设置ACCESS_KEY_ID
// bundle.putString(HmcpManager.CHANNEL_ID, mChannelID); // 设置CHANNEL_ID
// manager.init(bundle, this, new OnInitCallBackListener() { // 带bundle参数请调用该方法
manager.init(this, new OnInitCallBackListener() {
@Override
public void success() {
//初始化SDK成功,可以云游戏
}
@Override
public void fail(String s) {
//初始化SDK失败,不能云游戏
}
});
注:如使用 bundle 则 mBID、mChannelID 都不能为空,如有空值则bundle信息失效,渠道信息将使用AndroidManifest.xml中配置的信息
2.2 设置用户登陆信息
用户登陆信息包括 userId 和 userToken,userId 作为游戏客户端用户的唯一识别码,在开始游戏前必须设置用户登陆信息并且需要保证唯一性。该值原则上为App端的用户登陆账号,登陆账号在用户注册时已经保证了唯一性。
如果没有用户登陆账号,可以随机生成长度在64以内的字符串,但需要每台客户端上的账号保证唯一性。
userToken 用来校验 userId 的有效性,如果 userId 为随机生成,userToken 也可以随机生成。
HmcpVideoView 的 setUserInfo() 用来设置用户的登陆信息。
函数原型
public class HmcpVideoView {
public void setUserInfo(UserInfo userInfo)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
userInfo | UserInfo | 用户登录信息 |
示例代码
mUserInfo = new UserInfo();
mUserInfo.userId = USER_ID;
mUserInfo.userToken = USER_TOKEN;
mUserInfo.userType = 0;
hmcpVideoView.setUserInfo(mUserInfo);
注:如果两台客户端上的 userId 相同,将会导致游戏异常现象。userType 代表用户类型,可以不传,默认 0 ,0 代表普通用户,5 代表超级账号,需要后台配合生效。
2.3 开始游戏
调用 HmcpVideoView的play(bundle)函数后,SDK会与海马云的server通讯,申请服务成功后可以开始游戏。
函数原型
public class HmcpVideoView {
public void play(Bundle bundle)
}
参数说明
参数 | 类型 | 必选 | 说明 |
---|---|---|---|
bundle | Bundle | 是 | 需调用对应put***()方法,将下列所有参数赋值并打包到bundle中 |
orientaion | ScreenOrientation | 是 | 标识游戏是竖屏显示还是横屏; 竖屏: ScreenOrientation.PORTRAIT 横屏: ScreenOrientation.LANDSCAPE |
playTime | Int | 是 | 用户可以玩游戏的时长,单位为毫秒/ms。 |
priority | Int | 是 | 用户申请游戏服务的优先级,默认写0。数值越大优先级越高。(逻辑暂未生效) |
packageName | String | 是 | 游戏包名,传入要启动的对应游戏包名 |
appChannel | String | 是 | 如果存在多款游戏同包名的情况,可以通过appChannel区分。如果不存在则可以忽略。 |
cToken | String | 是 | 用来校验参数的有效性,cToken的计算方法请参考服务端SDK文档 |
extraId | String | 否 | 预留参数,默认为空字符串 |
payStr | String | 否 | 支付相关参数,默认为空字符串 |
archive | Boolean | 否 | 游戏是否存档 |
protoData | String | 否 | App业务系统关心的参数,需要 BASE64 编码 |
fpsPeriod | Int | 否 | 采集fps周期及上报周期,单位为秒/s |
bandWidthPeriod | Int | 否 | 采集带宽的周期及上报周期,单位为秒/s |
bandWidthPeak | Int | 否 | 采集带宽周期内的最大的几个值 |
decodeTimePeriod | Int | 否 | 采集解码时间的周期及上报周期,单位为s |
speed | Int | 否 | 设置该参数可以固定的码率开始游戏。该值单位为kB/s |
isShowTime | Boolean | 否 | 是否显示剩余游戏时间 |
clientISP | String | 否 | 运营商名称 |
clientProvince | String | 否 | 当前定位省份 |
clientCity | String | 否 | 当前定位城市 |
cid | String | 否 | 第一次游玩不需要传cid,若要重连未释放游戏,则需要查询未释放游戏信息(检测未释放游戏中的channelInfoList)中对应cid、包名、渠道,使用该参数申请重连 |
noInputLimitTime | Int | 否 | 无操作超时时长,单位为s |
isEnableIpv6 | Boolean | 否 | 是否支持IPV6功能,需要SDK版本支持,并且获取配置接口is_compatible_ipv6 字段为 true |
verticalBackground | Int | 否 | 播放器背景图,主要针对竖屏游戏黑色边框的优化 |
viewResolutionWidth | Int | 否 | 自定义游戏推流分辨率宽度,宽高可调换位置,不影响显示效果 |
viewResolutionHeight | Int | 否 | 自定义游戏推流分辨率高度,宽高可调换位置,不影响显示效果 |
示例代码
boolean isPortraitOrientation = false; // 是否竖屏
ScreenOrientation orientaion = isPortraitOrientation ? ScreenOrientation.PORTRAIT : ScreenOrientation.LANDSCAPE;
int playTime = 6000 * 1000;
int priority = 100;
int appId = 123;
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; // 无操作超时时间单位为秒
boolean isEnableIpv6 = false; // 是否支持IPV6功能
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.putInt(HmcpVideoView.APP_ID, appId);
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);
hmcpVideoView.play(bundle);
3 API 接口
3.1 初始化失败返回内容说明
初始化失败返回 JOSN 字符串:
{
"message":"",
"volleyMessage": "",
"startTime": "",
"errorTime": "",
}
参数说明
参数 | 说明 |
---|---|
message | 错误描述 |
volleyMessage | 网络请求失败时,错误信息 |
startTime | 网络超时会携带该字段,表示请求开始时间 |
errorTime | 网络超时会携带该字段,表示错误下发时间 |
3.2 设置配置信息
SDK 在获取云游戏服务时需要传送“配置信息“给服务器,目前为冗余字段;
免登录功能所需信息。如不使用,可传非空字符串
函数原型
public class HmcpVideoView {
public void setConfigInfo (String config)
}
3.3 暂停游戏
游戏启动后,通过 HmcpVideoView 的 pauseGame() 暂停游戏。
函数原型
public class HmcpVideoView {
public void pauseGame();
}
示例代码
hmcpVideoView.pauseGame();
3.4 断网重新连接游戏
通过 HmcpVideoView 的 reconnection() 进行重新连接游戏。
- 游戏启动后,断网重新连接游戏
- 游戏启动后,WIFI 切换到 4G
函数原型
public class HmcpVideoView {
public void reconnection();
}
示例代码
hmcpVideoView.reconnection();
3.5 继续游戏
游戏启动后,通过 HmcpVideoView 的 restartGame 接口继续游戏。
函数原型
public class HmcpVideoView {
public void restartGame(int time);
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
time | Int | 用户可以玩游戏的时长,单位为毫秒/ms |
示例代码
hmcpVideoView.restartGame(1000 * 60 * 10 ); // 游玩10分钟
3.6 重新申请游戏
服务请求完成后,通过 HmcpVideoView 的 startPlay() 接口重新申请游戏。
应用场景
- 在长时间无操作后,重新申请游戏
- 其他情况下需重新申请游戏场景
函数原型
public class HmcpVideoView {
public void startPlay();
}
示例代码
hmcpVideoView.startPlay();
3.7 插队申请游戏
服务请求完成后,可通过 HmcpVideoView 的 startPlay(boolean isAhead) 接口重新以插队方式申请游戏。
可在排队失败等报错场景下,以等待申请实例队列的第一位进行插队,等待有空闲实例进行分配游玩;
函数原型
public class HmcpVideoView {
public void startPlay(boolean isAhead);
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
isAhead | boolean | 是否插队进行申请游戏 |
示例代码
hmcpVideoView.startPlay(); // 注意:调用 play(bundle) 成功后,才可调用该方法
3.8 结束播放
结束播放,退出游戏。退出需要在Activity方法重载 onPause()、onDestroy()。
函数原型
public class HmcpVideoView {
public void onPause();
public void onDestroy();
}
示例代码
@Override
protected void onPause() {
super.onPause();
hmcpVideoView.onPause();
}
@Override
protected void onDestroy() {
if (hmcpVideoView!= null) {
hmcpVideoView. onDestroy();
}
super.onDestroy();
}
3.9 切换分辨率
ResolutionInfo 类的定义,参考:5.5分辨率信息 ResolutionInfo 类的定义
切换分辨率的调用时机参考:切换分辨率调用时机
函数原型
public class HmcpVideoView {
public void onSwitchResolution (int level, ResolutionInfo resolution, int rate)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
level | boolean | 验证版本号 |
resolution | ResolutionInfo | 要切换的分辨率信息 |
rate | Int | 冗余字段 |
示例代码
List<ResolutionInfo> mResolutionList = HmcpManager.getInstance().getResolutionDatas(); // 获取分辨率列表
hmcpVideoView.onSwitchResolution(level, resolution, rate); // 切换分辨率
3.10 添加控件
游戏启动后,通过 HmcpVideoView 的 addView() 添加控件,如背景图片、设置图标等。
函数原型
public class HmcpVideoView {
public void addView(View child);
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
child | View | 要添加控件的 View |
示例代码
hmcpVideoView.addView(view); //view 可以自定义
3.11 获取配置信息
获取相关配置信息
函数原型
public class HmcpVideoView {
public IntroImageInfo mIntroImageInfo;
public List<ResolutionInfo> mResolutionList;
}
示例代码
introImageInfos = ijkVideoView.mIntroImageInfo; // 背景图片配置信息
resolutionList = ijkVideoView.mResolutionList; // 分辨率配置信息
min = Integer.parseInt(Constants.MINIMUM_BITRATE); // 测速配置最小值
3.12 进入队列方法
获取需要排队信息后可以调用entryQueue进入队列。
函数原型
public class HmcpVideoView {
public void entryQueue()
}
示例代码
hmcpVideoView.entryQueue();
3.13 退出队列方法
获取需要排队信息后可以调用exitQueue退出队列。
函数原型
public class HmcpVideoView {
public void exitQueue ()
}
示例代码
hmcpVideoView.exitQueue();
3.14 IDC节点测速接口
游戏启动前,可以通过调用 HmcpManager 类的 testSpeed() 进行网络带宽测试。
可以通过 duration 参数控制测试的最长时间,单位为s。测试完成后通过注册的 callback 接口返回测试结果,单位为 KB/s。
函数原型
public class HmcpManager {
public void testSpeed(int duration, String clientISP, String clientProvince , String clientCity,
final OnSpeedTestCallBackListener listener)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
duration | Int | 检测速度所用时间,单位/ms |
clientISP | String | 运营商名称 |
clientProvince | String | 当前定位省份 |
clientCity | String | 当前定位城市 |
listener | OnSpeedTestCallBackListener | 节点测速接口回调监听器 |
示例代码
HmcpManager manager = HmcpManager.getInstance();
manager.testSpeed(5, "联通", "河北", "廊坊", new OnSpeedTestCallBackListener() {
@Override
public void success(int speed) {
Log.d(TAG, "test speed success-------" + speed);
}
@Override
public void fail(String msg) {
Log.d(TAG, "test speed fail-------" + msg);
}
});
3.15 点测速接口
游戏启动后,通过 HmcpVideoView 的点测速接口 getVideoLatency() 获取实时视频网络延迟,单位为ms。
函数原型
public class HmcpVideoView {
public int getVideoLatency()
}
示例代码
int latency = hmcpVideoView.getVideoLatency();
3.16 给 access 服务器发送消息的接口
游戏启动后,通过 HmcpVideoView 的 sendMessage() 可以向服务器发送消息。
函数原型
public class HmcpVideoView {
public void sendMessage(String payload, MessageType type, OnSendMessageListener listener)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
payload | String | 需要发送的消息 |
type | MessageType | 消息类型,只支持 MessageType.PAY_TYPE |
listener | listener | 发送消息接口回调监听器 |
示例代码
public void testSendMessage(View v) {
if (ijkVideoView != null) {
ijkVideoView.sendMessage("test send msg", MessageType.PAY_TYPE, new OnSendMessageListener() {
@Override
public void result(boolean success, String mid) {
}
});
}
}
3.17 access服务器的消息回调
实现 HmcpPlayerListener 接口后,在该接口的 onMessage() 函数中可以收到 access 服务器的消息回调。
函数原型
public interface HmcpPlayerListener {
void onMessage(Message message)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
message | Message | Message的定义以及参数说明详见消息回调 |
示例代码
public class HmcpPlayerActivity extends AppCompatActivity implements HmcpPlayerListener {
@Override
public void onMessage(Message message) {
//收到退出登录消息回调
}
}
3.18 获取实时视频信息
实现HmcpPlayerListener 接口后,在该接口的 onPlayStatus() 函数中可以收到实时数据的上报。
函数原型
public interface HmcpPlayerListener {
void onPlayStatus(int status, long value, String data);
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
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) {
}
}
3.19 更新UID和游戏时长接口
更新UID和游戏时长
函数原型
public class HmcpVideoView {
public void updateGameUID(Bundle bundle,final OnUpdataGameUIDListener listener)
}
参数说明
参数 | 类型 | 意义 |
---|---|---|
bundle | Bundle | 调用 put***() ,将下列所有参数赋值并打包到 bundle 中 |
playingTime | Long | 游戏时长 |
userID | String | 新的用户 id ,UID |
tip | String | 更新成功的 tip 显示 |
proto_data | String | 更新的 proto data |
cToken | 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中返回更新失败信息
}
});
hmcpVideoView. updateGameUID(bundle, listener);
3.20 获取Input地址
游戏收到第一帧后,通过HmcpVideoView的getInputUrl接口获取input地址。
函数原型
public class HmcpVideoView {
public String getInputUrl();
}
示例代码
String inputUrl = hmcpVideoView. getInputUrl();
3.21 获取 SDK 版本号
创建 HmcpManager 后,通过其 getSDKVersion() 获取版本号。
函数原型
public class HmcpManager{
public String getSDKVersion()
}
示例代码
String sdkVersion = HmcpManager.getInstance().getSDKVersion();
3.22 获取分辨率列表
获取分辨率列表
函数原型
public class HmcpManager{
public void getResolutionInfos(String packageName, String mAppChannel,
final OnGetResolutionsCallBackListener listener)
}
参数说明
参数 | 类型 | 意义 |
---|---|---|
packageName | String | 游戏包名 |
mAppChannel | String | 游戏配置的 AppChannel |
listener | 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);
3.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":""
}
字段说明
参数 | 类型 | 说明 |
---|---|---|
screenShotUrl | String | 截屏流地址 |
screenResolution | String | 视频分辨率 |
inputUrl | String | input 连接地址 |
orientation | Int | 横竖屏参数 |
encryption | String | 加密的 cid |
3.24 一键返回游戏界面
可以使用 HmcpVideoView 中的 backToGame() 执行返回按键操作;
请根据已有逻辑, 在合适的地方增加 backToGame() 方法调用, 对云端设备发送指令, 相当于手机中唤起游戏到前台;
适用场景: 针对有些第三方登陆多级,无法返回的情况
函数原型
public class HmcpVideoView {
public void backToGame()
}
示例代码
hmcpVideoView.backToGame();
3.25 云游戏延迟信息接口
通过该接口会返回当前云游戏的延迟信息,该算法的准确性基于App和实例的时钟校准。
函数原型
public class HmcpVideoView {
public VideoDelayInfo getClockDiffVideoLatencyInfo()
}
示例代码
hmcpVideoView.getClockDiffVideoLatencyInfo();
返回参数说明
方法 | 方法说明 |
---|---|
getNetDelay() | 获取帧网络耗时(最近1S内随机采样1帧) |
getDecodeDelay() | 获取帧解码耗时(最近1S内随机采样1帧) |
getRenderDelay() | 获取帧渲染耗时只包含解码后送入渲染队列的时间,不包含系统渲染部分(最近1S内随机采样1帧) |
getFrameSize() | 获取帧大小(最近1S内随机采样1帧) |
getVideoFps() | 获取视频帧率 |
getBitRate() | 获取视频码率 |
getReciveFrameCount() | 收到的总帧数 |
getReceiveFrameSize() | 收到帧的总大小 |
注意:该接口依赖于服务器打开时钟校准配置和 ROM 支持,当时钟校准成功时才会有数据返回, 之前会返回 null
在切换分辨率,前后台切换时,需要重新进行时钟校准,校准成功前会存在异常数据,需要在重新获取第一帧之后调用。
3.26 结束播放
游戏启动后,通过 HmcpVideoView 的 release() 结束播放。
可在长时间无操作后调用,也可在其他 error 时调用。
函数原型
public class HmcpVideoView {
public void release()
}
示例代码
hmcpVideoView.release();
3.27 一键返回游戏接口
游戏启动后,通过该接口一键返回到游戏界面
函数原型
public class HmcpVideoView {
public void backToGame()
}
示例代码
hmcpVideoView.backToGame();
3.28 获取用户最后一次操作时间戳
游戏启动后,通过该接口获取用户最后一次操作实例的时间戳
函数原型
public class HmcpVideoView {
public long getLastUserOperationTimestamp()
}
示例代码
hmcpVideoView.getLastUserOperationTimestamp(); // 默认返回 0,代表用户没有操作过实例,否则返回相应时间戳
3.29 重置无操作超时计时器
游戏启动后,如果想重置无操作超时计时器,调用此函数
函数原型
public class HmcpVideoView {
public int resetInputTimer()
public int resetInputTimer(boolean needUpdateTimestamp)
}
入参数说明
参数 | 类型 | 意义 |
---|---|---|
needUpdateTimestamp | String | true:重置计时器并且更新用户最后一次操作时间戳 false:重置计时器不更新用户最后一次操作时间戳 |
返回参数说明
参数 | 类型 | 意义 |
---|---|---|
result | Int | 0 : 代表和实例连接异常 1 : 重置无操作超时计数器成功 |
示例代码
hmcpVideoView.resetInputTimer();
hmcpVideoView.resetInputTimer(false);
3.30 获取当前播放器截屏
游戏启动后,如果想获取当前截屏和检测功能,调用此函数
函数原型
public class HmcpVideoView {
public Bitmap getShortcut()
}
返回参数说明
参数 | 类型 | 意义 |
---|---|---|
Bitmap | Int | 当前播放器截屏的 bitmap 对象 |
示例代码
Bitmap bitmao = hmcpVideoView.getShortcut();
3.31 检测当前截屏是否黑屏
如果检测当前截屏的bitmap对象是否黑屏功能,调用此函数
函数原型
public class HmcpVideoView {
public int checkBitmap (Bitmap bitmap)
}
入参数说明
参数 | 类型 | 说明 |
---|---|---|
bitmap | Bitmap | 需要检测的bitmap对象 |
返回参数说明
参数 | 类型 | 说明 |
---|---|---|
int | Int | 1: 检测结果为纯色 排除黑色和全透明色,因为播放器没开始工作,不通设备获取截屏有的是纯黑色有的是纯透明色 2: 检测结果为纯透明色 3: 检测结果为正常 4: 检测结果为纯黑色 |
示例代码
int result = hmcpVideoView.checkBitmap(bitmap);
3.32 静音功能开关
设置当前的播放是否为静音状态
函数原型
public class HmcpVideoView {
/**
* @param isMute true:全局静音 false:取消全局静音
*/
public void setAudioMute(boolean isMute){
}
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
isMute | Boolean | true:全局静音 false:取消全局静音 |
示例代码
hmcpVideoView. setAudioMute(false);
4 进阶API
4.1 获取视频流地址接口
游戏启动后,通过HmcpVideoView的getStreamUrl接口获取视频流地址。
函数原型
public class HmcpVideoView {
public String getStreamUrl()
}
示例代码
String streamUrl = hmcpVideoView.getStreamUrl();
4.2 获取CID接口
游戏启动后,通过HmcpManager的getCloudId接口获取cid。
函数原型
public class HmcpManager{
public String getCloudId()
}
示例代码
String cloudId = HmcpManager.getInstance().getCloudId();
4.3 获取游戏存档状态接口
获取存档状态的接口,判断是正在存档、存档完成、存档失败。
函数原型
public class HmcpManager{
public void getGameArchiveStatus(String packageName, UserInfo userInfo,
final OnSaveGameCallBackListener listener)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
packageName | String | 游戏包名 |
userInfo | UserInfo | 用户登录信息,见设置用户登录信息 |
listener | OnSaveGameCallBackListener | 查询结果回调监听器 |
示例代码
OnSaveGameCallBackListener listener = new OnSaveGameCallBackListener() {
@Override
public void success(boolean result) {
//success中返回查询结果,false(正在存档),true(存档完成)。
}
@Override
public void fail(String msg) {
//fail中返回查询失败信息。
}
});
HmcpManager.getInstance().getGameArchiveStatus(packageName, userInfo, listener);
4.4 查询是否存在游戏存档
获取当前用户是否存在存档的接口。
函数原型
public class HmcpManager{
public void gameArchived(String packageName, UserInfo userInfo,
final OnSaveGameCallBackListener listener)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
packageName | String | 游戏包名 |
userInfo | UserInfo | 用户登录信息,见设置用户登录信息 |
listener | OnSaveGameCallBackListener | 查询结果回调监听器 |
示例代码
OnSaveGameCallBackListener listener = new OnSaveGameCallBackListener() {
@Override
public void success(boolean result) {
//success中返回查询结果,false(没有游戏存档),true(有游戏存档)。
}
@Override
public void fail(String msg) {
//fail中返回查询失败信息
}
});
HmcpManager.getInstance().gameArchived(packageName, userInfo, listener);
4.5 检测未释放游戏接口
通过用户信息UserInfo查询,是否有未释放实例(仍在运行的游戏);
函数原型
public class HmcpManager{
/**
* @param userInfo 用户登录信息
* @param listener 回调函数
*/
public void checkPlayingGame(UserInfo userInfo, final OnGameIsAliveListener listener)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
userInfo | UserInfo | 用户登录信息,见设置用户登录信息 |
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.getInstance().checkPlayingGame (userInfo, listener);
4.6 释放cid
通过调用接口,释放指定的 cid
函数原型
public class HmcpManager{
public void setReleaseCid(String packageName, long cid, String cToken, String appChannel,
UserInfo2 userInfo2, final OnSaveGameCallBackListener listener);
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
packageName | String | 游戏包名,传入要释放的对应游戏包名 |
cid | OnSaveGameCallBackListener | 要释放的cid |
cToken | String | 用来校验参数的有效性,cToken的计算方法请参考服务端SDK文档 |
appChannel | String | 如果存在多款游戏同包名的情况,可以通过appChannel区分。如果不存在则可以忽略。 |
userInfo2 | UserInfo2 | 用户登录信息,包含字段 userId、token |
listener | OnSaveGameCallBackListener | 回调监听 |
返回参数
参数 | 类型 | 说明 |
---|---|---|
cid | Long | 释放的cid |
code | Int | 请求结果标识 0 :请求成功 1:请求失败 |
msg | String | 当 code = 0 时返回的信息 |
errorCode | String | 当 code = 1 时返回的错误码 |
errorMsg | String | 当 code = 1 时返回的错误信息 |
4.7 获取网络请求数据接口
可以使用 HmcpManager 中的 getNetText(HmcpVideoView hmcpVideoView) 接口获取网络请求结果数据。
函数原型
public class HmcpManager{
public String getNetText (HmcpVideoView hmcpVideoView)
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
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": "https://saasauth.cmgame.com/s/rest/api 网络请求的url"
}, {
"action": "108",
"index": 1,
"msg": "",
"packageName": "",
"responseTime": 1565684455511,
"result": "success",
"startTime": 1565684455015,
"time": 496,
"transId": "7082cbf20b8456c4a02a6e520a7f7982",
"url": "https://saasauth.cmgame.com/s/rest/api"
}]
字段参数 | 类型 | 说明 |
---|---|---|
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地址 |
4.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)
}
字段参数 | 类型 | 说明 |
---|---|---|
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);
4.9 获取云游戏状态码接口
云游戏异常结束时,通过该接口返回云游戏当前的状态码,可以分析统计游戏失败原因。
函数原型
public class HmcpVideoView {
public String getCloudPlayStatusCode()
}
返回参数说明
参数 | 类型 | 说明 |
---|---|---|
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失败 |
示例代码
String cloudPlayStatusCode = hmcpVideoView.getCloudPlayStatusCode();
4.9 开始直播
函数原型
public class HmcpVideoView {
public void startLiving(String livingId, String livingUrl, OnLivingListener listener);
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
livingId | String | 直播ID |
livingUrl | String | 直播地址 |
listener | OnLivingListener | 调用结果回调 |
示例代码
ijkVideoView.startLiving(livingId, livingUrl, new OnLivingListener() {
@Override
public void start(boolean success, String msg) {
if (success) {
//开始直播成功
}
}
@Override
public void stop(boolean success, String msg) {
if (success) {
//停止直播成功
}
}
});
4.10 停止直播
函数原型
public class HmcpVideoView {
public void stopLiving(String livingId, OnLivingListener listener);
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
listener | OnLivingListener | 调用结果回调 |
livingId | String | 直播ID |
示例代码
ijkVideoView.stopLiving(livingId, new OnLivingListener() {
@Override
public void start(boolean success, String msg) {
if (success) {
//开始直播成功
}
}
@Override
public void stop(boolean success, String msg) {
if (success) {
//停止直播成功
}
}
});
4.11 获取授权码
函数原型
public class HmcpVideoView {
public void getPinCode(OnContronListener listener);
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
listener | OnContronListener | 调用结果回调 |
示例代码
ijkVideoView.getPinCode(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() {
// 失去控制权
}
});
4.12 获取控制权
函数原型
public class HmcpVideoView {
public void contronPlay(String cid, String pinCode, OnContronListener listener);
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
cid | String | cid |
pinCode | String | 授权码 |
listener | OnContronListener | 结果回调 |
示例代码
ijkVideoView.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() {
// 失去控制权
}
});
4.13 是否支持直播
函数原型
public class HmcpVideoView {
public void ELivingCapabilityStatus getLivingCapabilityStatus();
}
参数说明
参数 | 类型 | 说明 |
---|---|---|
ELivingCapabilityStatus | ENUM | 是否支持直播状态 |
public enum ELivingCapabilityStatus {
//未知
UNKNOWN,
//支持
SUPPORTED,
//不支持
UNSUPPORTED
}
示例代码
ELivingCapabilityStatus livingCapabilityStatus = ijkVideoView.getLivingCapabilityStatus();
5 数据结构定义
5.1 场景切换
游戏过程中的各个状态切换都会通过onSceneChanged(String sceneMessage)接口通知应用,sceneMessage字符串是有Json串转换的,格式如下:
{
"sceneId": "切换到的场景Id" ,
"extraInfo" {"场景切换的扩展信息,参考sceneId说明"} ,
}
以下是游戏过程中各状态的sceneId
及描述:
onSceneChanged应用场景切换详细信息
sceneId | extraInfo{} |
---|---|
init | 初始化中,extraInfo为空 |
wait | 排队中,extraInfo为空 |
play | 开始游戏,extraInfo为空 |
stop | 游戏结束,extraInfo参考参数示例 |
mait | 云玩维护,extraInfo参考参数示例 |
crtp | 显示自动降低码率tips,extraInfo 参考参数示例 |
crst | 开始切换码率(当前码率,目标码率,自动手动模式),extraInfo参考参数示例 |
cred | 切换码率完成(成功,失败,当前码率),extraInfo参考参数示例 |
firstFrameArrival | 拿到游戏视频第一帧 |
以下是游戏过程中各状态对应的扩展信息extraInfo{}
描述:
stop : 游戏结束
"extraInfo" : { "interval" : 当次游戏时长,单位 秒 "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" : { "result" :"0- 失败 1-成功" "cur_rate" :"当前码率" }
5.2 消息回调
消息方法:
public void onMessage(Message message){}
Message类的定义如下:
public class Message implements Serializable {
public static final int ACK_CLIENT_SEND = 0;
public static final int TYPE_PAY_MESSAGE = 1;
public static final int TYPE_SYSTEM_MESSAGE = 2;
public static final int TYPE_AD_MESSAGE = 3;
public static final int TYPE_INTERACTIVE_MESSAGE = 4;
public String to; //目标接收者CID,全部推送"ALL"
public String mid; //消息ID
public String payload; //消息内容
public String from; //发送者 系统消息="System"
public int type; //消息类型 默认是传 1
public int ack; //客户端发送默认为0;服务端收到回复为1
public String uid; //可选参数,目标uid
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
参数定义如下:
参数名称 | 类型 | 说明 | 选项 |
---|---|---|---|
uid | String | 可选参数,目标uid | 可选 |
from | String | 消息发送方标识 | 必选 |
to | String | 消息接收方标识 | 必选 |
mid | String | 消息ID | 必选 |
type | Integer | 消息类型,固定值为 1 | 必选 |
ack | Integer | 固定值为0 | 必选 |
payload | String | 消息内容 | 必选 |
5.3 SDK状态回调说明
SDK的各个状态切换和sever下发通知都会通过 HmcpPlayerStatusCallback(String callback) 接口通知应用,callback字符串是有 JSON 串转换的,格式如下:
{
"status": "切换到的场景Id",
"data" {}, // 扩展信息
}
状态说明 | 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 | {“erroeCode”:””, “errorMessage”:””} |
进入队列消息 | 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 | {“erroeCode”:”” “errorMessage”:””} |
Tocken 过期服务终止 |
STATUS_OPERATION_FORCED_OFFLINE = 24 | {“errorMessage”:””} |
自动切换分辨率 | TATUS_AUTO_SWITCH_RESOLUTION = 25 | {“mCurrentSpeed”:”” “mMinResolutionLevel”:””} |
测速结果低于服务下限 | STATUS_SPEED_LOWER_BITRATE = 26 | 配置的弹框话术 |
游戏多开 | STATUS_OPERATION_OPEN_MORE_SAME_GAME = 27 | {“errorMessage”:””} |
服务连接错误 | STATUS_OPERATION_HMCP_ERROR = 29 | {“erroeCode”:””, “errorMessage”:””} |
游戏时长提示 | 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 | 无 |
Data参数说明:
状态说明 | status |
---|---|
erroeCode | 发生出错误的错误码。 |
errorMessage | 发生错误的错误说明。 |
currentNetType | 网络类型 |
currentApkType | 游戏池化状态 |
finishByServer | 游戏时长到,是否结束游戏(服务端配置) |
finishTip | 游戏时长到状态说明 |
defaultChoiceId | 切换分辨率id |
switchResolution | 是否为自动切换 |
mCurrentSpeed | 当前网络速度 |
mMinResolutionLevel | 配置的最低播流速度 |
ahead | 游戏时长提示的剩余时长 |
countDown | 是否为倒计时,countDown == 1 为倒计时 |
formatter | 游戏时长提示说明 |
Tip | 更新游戏时长说明 |
playingTime | 可玩游戏时长 |
5.4 多优先级用户排队参数增加说明
SDK状态回调增加多优先级用户排队状态返回。
状态说明 | 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
]
}
]
}
字段 | 类型 | 说明 |
---|---|---|
message | String | 返回的配置的弹框话术 |
queues | List | 队列信息 |
rank | Int | 队列等级 |
index | Int | 排队人数 |
time | String | 预估时间,单位秒 |
timeStr | String | “09分15秒”格式字符串 |
priorities | List | 用户优先级列表,即配置的,该等级队列中包含那些优先级的用户 |
5.5 分辨率信息ResolutionInfo类的定义
public class ResolutionInfo implements Serializable {
public String id; //分辨率信息id
public String name; //分辨率名称
public String resolution; //分辨率,形如<width>x<height>
public String peakBitRate; //峰值码率,单位KB/s
public String bitRate; //码率,单位为bit/s,类型为Long
public int frameRate; //帧率,类型为Integer
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().getResolutionData() 得到游戏的清晰都列表。
在调用hmcpVideoView.onSwitchRsolution()
接口后也会有场景回调,其中sceneId=”crst”表示开始切换分辨率。如果收到scenedId=”cred”的场景回调,则可以判断切换码率的结果。如果extraInfo.result=0则表示切换失败,如果extraInfo.result=1则表示切换成功,extraInfo.cur_rate
表示切换成功后的码率peakBitRate。
另外如果开启了自动切换码率,app还需要监听sceneId=”crtp”的场景切换,该场景切换如果extraInfo.minimum=1则表示当前为最低码率,如果 extraInfo.minimum=0
则表示当前不是最低码率,这种情况下启动切换码率后也会收到 scenedId=”cred”
的场景回调,需要app更新当前分辨率。
场景回调的参数详见5.1场景切换。
6 注意事项
6.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 {
// 不需要什么权限
}
}
6.2 虚拟导航栏兼容性设置问题
关于部分手机存在虚拟导航栏兼容性设置问题
部分存在虚拟导航栏手机会存在点击游戏画面错位现象,需要设置虚拟导航栏兼容性代码,添加如下代码即可:
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;
}
6.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;
}
6.4 一个登陆账号开启多个游戏的场景
登陆账号在“设置用户登陆信息”中已经提到设置方法,其设置的登陆账号可以在一个终端上同时打开多个窗口或者同时在多个终端上打开多个窗口,但是打开窗口的个数有限制。该个数限制由海马云server配置。现在的限制如下:
- 一个账号可以打开同一款游戏的个数限制为2
- 一个账号可以打开多款游戏的个数限制为5
如果超过该上限,游戏就不能正常开始,SDK并有相应的提示信息。
6.5 云游戏退出二次确认提示框设置
为了避免因为用户误操作点击手机Back按键导致的云游戏退出而增加云游戏二次退出确认信息,示例代码如下:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
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 false;
} else {
return super.onKeyDown(keyCode, 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>
6.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失败,不能云游戏,如果初始化失败请尝试重新进行初始化
}
});
6.7 保存上次使用的清晰度
记录用户上次使用的清晰度,下次使用该清晰度播流。
用户使用的清晰度会有场景切换的回调。详见5.1场景切换。回调传入的字符串中 “cur_rate” 表示当前使用的码率,单位为kB/s。在下次启动播流时把改值设置为 HmcpVideoView.INTERNET_SPEED 传入SDK即可以该码率波流。详见 2.3申请服务。
6.8 SDK错误码相关说明
错误码 | 说明 |
---|---|
100099001 | saas-webSocket 连接失败 |
100099002 | 连接实例参数错误 |
100099003 | 测速结果低于服务下限 |
100099004 | SDK记录游戏时长已结束 |
100099009 | 获取流地址超时 |
100099010 | 刷新stoken超过次数上限 |
100099100 | user info为空 |
100099011 | 检测到网络断开 |
7 场景说明
7.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"}]}
- 调用取消排队 videoView.exitQueue() 并直接退出游戏;
7.2 横竖屏切换
SaaS SDK提供动态切换横竖屏游戏画面功能。客户端通过SDK提供的接口,传入指定游戏横竖屏标识参数。
在 申请游戏 的时候传入 orientaion 字段,可以控制游戏画面横竖屏显示
设置横屏
bundle.putSerializable(HmcpVideoView.ORIENTATION, ScreenOrientationLANDSCAPE);
设置竖屏
bundle.putSerializable(HmcpVideoView.ORIENTATION, ScreenOrientation.PORTRAIT);
7.3 清晰度切换
在海马云游戏管理平台进行配置游戏的清晰度参数(高清、超清、流畅);
调用 开始游戏 后,如果成功会有 sceneId = “play” 的场景回调
onSceneChanged ===> {"extraInfo":{"cur_rate":"200"},"sceneId":"play"}
SDK 调用 HmcpManager.getInstance().getResolutionData() 获取当前游戏的清晰度列表;
"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"}
字段 类型 说明 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"}
参数 类型 说明 method int 切换码率模式
0 :手动切换码率
1 :自动切换码率des String 目标码率 ID - 如果手动调用 onSwitchResolution() 切换码率;
- onSceneChanged 接收到切换码率的消息回调 scenedId=”crst”
onSceneChanged 收到scenedId=”cred”切换码率后结果:
recordSceneEvent ===> cred {"cur_rate":"500","result":1}
字段 类型 说明 cur_rate String 当前码率是否是最低码率 否-“0” 是-“1” result Int 0 :失败 1 :成功