您尚未登录

登录

推荐您使用PC浏览器访问

确定
  • 开发者中心
  • >
  • 云游戏
  • >
  • SDK开发指南
  • >
  • 云游戏基础SDK
  • >
  • Android
  • >
  • v4.33.1

Android SDK 开发指南

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工程需要添加
      }

    注:

    1. 海马云游戏客户端 SDK 目前只支持通过 gradle 集成 SDK 方式
    2. 其中 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 的接口调用要在同一个线程下。

2.1 初始化SDK

若按照工程配置,已在 AndroidManifest.xml 文件中配置渠道信息:HMCP_ACCESS_KEY_IDHMCP_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失败,不能云游戏
    }
});

注:如使用 bundlemBIDmChannelID 都不能为空,如有空值则bundle信息失效,渠道信息将使用AndroidManifest.xml中配置的信息

2.2 设置用户登陆信息

用户登陆信息包括 userIduserToken,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 代表用户类型,可以不传,默认 00 代表普通用户,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 排队管理

  1. 触发条件:

    接入商所购买云服务实例和并发数达到上限;

  2. 处理机制:

    1. onSceneChanged 接收到需要排队提示;

    2. 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"}]}
    3. 调用开启排队 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"}]}
    4. 在海马云游戏管理平台进行配置禁止排队,则返回排队信息如下:

      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"}]}
  1. 调用取消排队 videoView.exitQueue() 并直接退出游戏;

7.2 横竖屏切换

SaaS SDK提供动态切换横竖屏游戏画面功能。客户端通过SDK提供的接口,传入指定游戏横竖屏标识参数。

申请游戏 的时候传入 orientaion 字段,可以控制游戏画面横竖屏显示

设置横屏

 bundle.putSerializable(HmcpVideoView.ORIENTATION, ScreenOrientationLANDSCAPE);

设置竖屏

 bundle.putSerializable(HmcpVideoView.ORIENTATION, ScreenOrientation.PORTRAIT);

7.3 清晰度切换

  1. 在海马云游戏管理平台进行配置游戏的清晰度参数(高清、超清、流畅);

  2. 调用 开始游戏 后,如果成功会有 sceneId = “play” 的场景回调

    onSceneChanged ===> {"extraInfo":{"cur_rate":"200"},"sceneId":"play"}
  1. 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"}
    ]
  2. 监听回调函数onSceneChanged 并收到 sceneId=”crtp” 消息:

    onSceneChanged ===> {"extraInfo": {"delay_less_minimum":"0","minimum":0},"sceneId":"crtp"}
    字段 类型 说明
    delay_less_minimum String 当前码率是否是最低码率 否-“0” 是-“1”
    minimum Int 当前码率是否低于下限 否 - 0 是 - 1
  3. 开始切换码率:

    // 发起切换码率
    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
    1. 如果手动调用 onSwitchResolution() 切换码率;
    2. onSceneChanged 接收到切换码率的消息回调 scenedId=”crst”
  4. onSceneChanged 收到scenedId=”cred”切换码率后结果:

    recordSceneEvent ===> cred {"cur_rate":"500","result":1}
    字段 类型 说明
    cur_rate String 当前码率是否是最低码率 否-“0” 是-“1”
    result Int 0 :失败 1 :成功