Andriod SDK参考 官网开发者

产品介绍    

简介

本文档为了指导开发者更快使用Android系统上的 “自助式网络直播服务SDK”,默认读者已经熟悉IDE的基本使用方法(本文以Android Studio为例),以及具有一定的编程知识基础等,集成本文档可以获得一键发起直播,看直播,看回放,美颜,聊天等功能。

主要功能

分类 特性名称 描述
发起直播 支持编码类型 支持推流协议 视频分辨率 屏幕朝向 闪光灯 静音 切换摄像头 目标码率 支持环境
观看直播 支持播放协议 延时 支持解码 RTMP RTMP:2-4秒 H.264
观看互动 多人连麦,多人互动 可以多人连麦并携带角色信息,可高度自定义
观看回放 支持协议 HLS,MP4
文档演示 支持文档演示 文档可与视频同步演示
权限 第三方K值认证 支持客户自己的权限验证机制来控制观看直播,观看回放权限
用户登录 支持微吼用户登录 主要用于聊天,问答等用户互动模块
聊天 支持发直播聊天 支持看直播聊天 用户登陆后可聊天 用户登录后可聊天
问答 支持看直播提问 用户登录后可提问
单音频切换 观看对音频转换 视频转音频,视频+文档转音频
应用签名 应用签名 保证应用安全防护

优势

直播一键发起 美颜一键发起 回放一键发起

接入流程    

接入步骤

1、联系客户经理开通权限[立即沟通权限申请],或400-888-9970电话立即沟通申请

2、获取API/SDK的使用权限信息,得到Acess key点击查看

3、[下载SDK]

4、依照文档进行功能接入

Access Key

权限申请通过后,用户可在http://e.vhall.com/home/vhallapi/authlist 页面获取appKey、 secretKey和AppSecretKey。 说明:

appkey: 用于标识客户身份,在网络请求中会以某种形式传输

secretkey: 作为私钥形式存储于客户方本地, 不在网络传递,它的作用是对客户方发起的请求进行数字签名,保证该请求是来自指定客户的请求,并且是合法的有效的。

AppSecretKey:用于SDK,使用方式同secretkey

使用说明    

使用流程

Sdk流程图

SDK接入准备

1. 申请Key

联系客户经理开通权限[立即沟通权限申请],或400-888-9970电话立即沟通申请

2. 绑定应用签名信息

使用SDK前集成前,务必先配置好此签名信息,否则使用时会出现"身份验证失败"提示信息,配置信息流程如下

绑定签名

  • AppKey,SecretKey,AppSecretKey 自动生成,初始化SDK时需要传入(不能有空格)

  • Android 集成需要填写 SHA1 和 包名 , 对应集成的App

  • SHA1分为DebugRelease版本 如果绑定签名时使用的是Debug签名,运行时也要使用debug.keystore.

Debug 版本 一般直接运行时使用的是Debug版 地址 C:\Users\用户名.android\debug.keystore

Release 版本 一般发版时使用的是Release版 使用打包时的KeyStore,使用对应的app_key

3. 获取直播Token

4. 添加保持

如果用户使用了混淆功能,可以加入如下的保持信息。

-dontwarn com.vhall.** -keep class com.vhall.** {*;}

-dontwarn vhall.com.vss2.** -keep class vhall.com.vss2.** {*;}

//互动用户需添加

-dontwarn org.webrtc.** -keep class org.webrtc.** {*;}

接入指南    

基础说明    

sdk接入指南

说明 为了方便用户sdk版本管理,现sdk依赖方式由原来的aar文件依赖改为maven依赖

使用sdk模块需要添加以下依赖

 maven {url 'https://dl.bintray.com/tiandiyinghun/maven'}

 //SaaSSDK
    api 'com.vhall.android.library:vh-saas-sdk:5.0.2'
    api 'com.vhall.android.library:vh-saas-interactive:5.0.1'
    //投屏相关
    api 'com.vhall.android.library:vh-saas-sdk-support:2.0.0'

房间类型区别说明

由于Adoble 计划从2020年底停止支持Flash,目前平台房间分为Flash 房间 和 H5房间两种;

房间类型对应关系

房间类型 对应文档版本 UI层控制类 SDK支持
Flash房间 文档1.0 *Presenter 2020年底前
H5房间 文档2.0 *PresenterVss 4.0+
H5房间 文档2.0 *Presenter 5.0+

** 4.0+Demo中Flash房间对应的Presenter保持不变为**Presenter
H5房间对应的Presenter为新增的**PresenterVss** 5.0之后删除 vss相关 还原为Presenter

当前SDK实现方式为Flash房间与H5独立处理,互不干扰,因此用户在接入使用时需根据接口返回的数据进行判断后选择使用;

SDK 房间类型判定标准

SDK内提供有initWatch 接口,返回数据为WebinarInfo; info中的vss_token为唯一识别标准,返回数据非空则为H5房间否则会为Flash房间

文档不同版本区分

文档 加载机制
1.0 加载Image
2.0 加载H5页面

文档1.0 基于ImageView 实现,将文档转换为图片进行加载,自定义绘制画笔数据,对文档图片不能过大,易发生OOM;

文档2.0 采用WebView加载H5页面实现,数据加载及绘制统一由H5完成,本地仅执行数据传输;

登录    

创建用户

第三方创建用户接口 如果使用聊天和问答等功能 , 这些功能必须注册,否则无法使用 , 需要用户提前调用WebApi进行创建用户标识操作。详细接口说明,请参照API地址。

登陆

当用户在vhall平台创建用户成功之后,调用VhallSDK中的login方法,如果用户需要使用如聊天,问答等功能则必须用户标识。如果不用户标识则默认是游客模式 (Demo里即使是游客也是可以聊天的,用户可以根据自己的场景控制。问答必须创建用户)

VhallSDK.login(username, userpass, new UserInfoDataSource.UserInfoCallback() {
	@Override
	public void onSuccess(UserInfo userInfo) {
		// 登陆成功
	}

	@Override
	public void onError(int errorCode, String reason) {
		// 登陆失败
	}
});

是否登陆

判断用户是否登陆方法: VhallSDK.isLogin(); // true 已登陆 false 未登陆

获取用户昵称

获取用户昵称方法: VhallSDK.getUserNickName();

返回错误码

错误码 描述
10501 用户不存在
10502 登陆密码不正确

发起直播    

准备工作

(1)屏幕保持常亮

getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

(2)横竖屏发起视频

/**
* 如果竖屏发起设置ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
* 如果横屏发起设置ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
*/
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);

(3)设置发起布局

<com.vhall.push.VHVideoCaptureView
        android:id="@+id/cameraview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

(4)本地回显适配模式设置

推流播放器初始化成功后设置生效

	camerview.setCameraDrawMode(VHLivePushFormat.DRAW_MODE_ASPECTFILL);
适配类型 描述
VHLivePushFormat.DRAW_MODE_NONE 默认
VHLivePushFormat.DRAW_MODE_ASPECTFIT 自适应居中
VHLivePushFormat.DRAW_MODE_ASPECTFILL 铺满全屏

(5)设置美颜等级

//打开美颜功能
cameraview.setFilterEnable(true);
//设置美颜等级 1-5
cameraview.setBeautyLevel(level);

(6)设置镜像

cameraview.setMirror(boolean mirror);

发直播

发直播必须获取Token,token时效是24小时,并且唯一性,如果调用成功后之前的Token无效 , 详见获取SDK推流TOKEN。 用户如果登陆默认是子账号发直播,如果不登录,默认是主账号发直播 。 使用的Token需要用Api重新生成,否则会返回身份验证失败

初始化直播间信息:

发起参数描述:

参数字段 描述
id 活动ID(9位) , 在网页上创建
accessToken 请求API获取的Token
LoadWebinarInfoCallback 回调信息

代码展示

        VhallSDK.initBroadcast(param.broId, param.broToken, new WebinarInfoDataSource.LoadWebinarInfoCallback() {
            @Override
            public void onWebinarInfoLoaded(String jsonStr, WebinarInfo webinarInfo) {
				//jsonStr  原始数据返回
				//WebinarInfo 解析后的 房间信息封装,当vss_token与vss_room_id 非空时为H5活动
            }

            @Override
            public void onError(int errorCode, String errorMsg) {

            }
        });

初始化推流参数 VHLivePushConfig

VHLivePushConfig config = new VHLivePushConfig(VHLivePushFormat.PUSH_MODE_XHD);
config.screenOri = VHLivePushFormat.SCREEN_ORI_LANDSPACE;
config.videoBitrate = 1500*1000;
//纯音频直播时设置
//config.streamType=VHLivePushFormat.STREAM_TYPE_A;

视频分辨率类型

参数字段 描述
VHLivePushFormat.PUSH_MODE_HD 640*480
VHLivePushFormat.PUSH_MODE_XHD 1280*720
VHLivePushFormat.PUSH_MODE_XXHD 1920*1080

内部变量说明

参数字段 描述 默认值
videoFrameRate 视频帧率 15(10-30)
videoBitrate 视频码率 800*1000 b/s(与视频清晰度相关)
screenOri 横竖屏 横屏 VHLivePushFormat.SCREEN_ORI_LANDSPACE
pushTimeout 推流超时时长 5*1000 ms
pushReconnectTimes 重连次数 5
streamType 推流类型 音视频 VHLivePushFormat.STREAM_TYPE_AV

直播间横竖屏参数

参数字段 描述
VHLivePushFormat.SCREEN_ORI_LANDSPACE 横屏
VHLivePushFormat.SCREEN_ORI_PORTRAIT 竖屏

推流类型

参数字段 描述
VHLivePushFormat.STREAM_TYPE_NONE 推流内容
VHLivePushFormat.STREAM_TYPE_AV 音视频
VHLivePushFormat.STREAM_TYPE_V 纯视频
VHLivePushFormat.STREAM_TYPE_A 纯音频

初始化推流实例

Broadcast.Builder builder = new Broadcast.Builder()
        .cameraView(mView.getCameraView())//视频渲染view
        .config(config)//直播配置
        .callback(new BroadcastEventCallback())//播放器事件回调
        .chatCallback(new ChatCallback());//聊天回调
Boradcast broadcast = builder.build();

开始直播:

broadcast.start();

结束直播

参数说明:

参数字段 描述
id 活动ID(9位)
accessToken 请求API获取的Token
broadcast 发直播实例
RequestCallback 回调信息
//结束成功后,内部已调用broadcast.stop();
VhallSDK.finishBroadcast(param.broId, param.broToken, getBroadcast(), new RequestCallback());

###销毁直播实例

broadcast.destroy();

直播事件回调

private class BroadcastEventCallback implements VHPlayerListener {
        @Override
        public void onError(int errorCode, int innerErrorCode,String reason) {
            // 直播时发生错误回调,查看错误信息
        }

        @Override
        public void onStateChanged(Constants.State state) {
            // 直播时发生的状态回调
            switch (state) {
                case START:/** 开始直播*/
                    break;
                case STOP:/** 停止直播*/
                    break;
            }
        }

        @Override
        public void onEvent(int eventCode, String eventMsg) {
            switch (eventCode) {
                case Constants.Event.EVENT_UPLOAD_SPEED:
                    //推流速度
                    mView.setSpeedText(eventMsg + "/kbps");
                    break;
                case Constants.Event.EVENT_NETWORK_UNOBS:
                    mView.showMsg("网络通畅!");
                    break;
                case Constants.Event.EVENT_NETWORK_OBS:
                    mView.showMsg("网络环境差!");
                    break;
            }
        }
    }

事件码

常量 描述
Constants.Event.EVENT_UPLOAD_SPEED 推流速率(/kbps)
Constants.Event.EVENT_NETWORK_UNOBS 网络通畅
Constants.Event.EVENT_NETWORK_OBS 网络环境差

错误码

错误码 描述
10401 活动结束失败
10402 当前活动ID错误
10403 活动不属于自己
10409 第三方用户对象不存在
10411 用户套餐余额不足
20101 正在直播
20102 初始化视频信息失败
20103 预览失败,无法直播
20104 直播地址有误
20105 连接服务器失败

观看直播    

准备工作

  1. 屏幕保持常亮
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  1. 设置观看布局
<RelativeLayout
        android:id="@+id/rl_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

看直播

初始化房间信息:

参数字段 描述
id 活动ID(9位)
email 唯一标识
nickname 用户名
password K值
status 活动状态
WebinarInfoDataSource.LoadWebinarInfoCallback 返回房间信息回调

备注:如果email和nickname为空,则必须登陆,如果不登录,则email和nickname不能为空,如果都传,默认取登陆信息。

        VhallSDK.initWatch(params.watchId, customeId, customNickname, params.key, watchType, new WebinarInfoDataSource.LoadWebinarInfoCallback() {
            @Override
            public void onWebinarInfoLoaded(String jsonStr, WebinarInfo webinarInfo) {
					//jsonStr 原始数据返回, webinarInfo 解析后的房间信息封装
            }

            @Override
            public void onError(int errorCode, String errorMsg) {
                                
            }
        });
    

初始化看直播实例:

WatchLive watchLive;
WatchLive.Builder builder = new WatchLive.Builder()
        .context(watchView.getActivity().getApplicationContext())
        .containerLayout(containview) /**传入观看布局*/
        .bufferDelay(6)/**缓冲几秒的BUFFER*/
        .callback(new WatchCallback())/**观看回调*/
        .messageCallback(new MessageEventCallback()) /**连接消息服务器*/
        .connectTimeoutMils(5000)//连接超时,时长
        .chatCallback(new ChatCallback());/**使用聊天功能就加上*/
watchLive = builder.build();

设置视频适配模式

watchLive.setScaleType(Constants.DrawMode.kVHallDrawModeAspectFit.getValue());

开始播放

watchLive.setWebinarInfo(webinarInfo);//将房间信息传给播放器
watchLive.start();

分辨率切换

可用分辨率以播放器回调返回的分辨率列表为准:事件码EVENT_DPI_LIST

分辨率(String) 描述
same 原画
360p 标清
480p 高清
720p 超清
a 纯音频
watchLive.setDefinition(dpi);

停止观看

watchLive.stop();

销毁播放器

watchLive.release();

观看事件回调

    /**
     * 观看过程中事件监听
     */
    private class WatchCallback implements VHPlayerListener {
        @Override
        public void onStateChanged(com.vhall.player.Constants.State state) {
            switch (state) {
                case START://开始播放
 
                    break;
                case BUFFER://数据缓冲

                    break;
                case STOP://停止播放

                    break;
            }
        }

        @Override
        public void onEvent(int event, String msg) {
            switch (event) {
                case com.vhall.player.Constants.Event.EVENT_DOWNLOAD_SPEED://下载速率 kb/s
                    break;
                case com.vhall.player.Constants.Event.EVENT_DPI_CHANGED:
                    //分辨率切换
                    break;
                case com.vhall.player.Constants.Event.EVENT_DPI_LIST:
                    //支持的分辨率 msg,返回数据类型JSONArray
                    
                    break;
                case com.vhall.player.Constants.Event.EVENT_VIDEO_SIZE_CHANGED:

                    break;
                case com.vhall.player.Constants.Event.EVENT_STREAM_START:
                	//发起端开始推流 Flash直播有效,H5直播在消息监听中获取
                    
                    break;
                case com.vhall.player.Constants.Event.EVENT_STREAM_STOP:
                	//发起端停止推流 Flash直播有效,H5直播在消息监听中获取
                    
                    break;
            }
        }

        @Override
        public void onError(int errorCode, int innerCode, String msg) {
            switch (errorCode) {
                case Constants.ErrorCode.ERROR_CONNECT:
                		//连接错误
                    break;
                default:
                		//其他错误
                		break;

            }
        }
    }
事件码 描述
EVENT_DPI_LIST 当前是视频支持的分辨率
EVENT_DPI_CHANGED 视频分辨率改变
EVENT_URL 视频地址
EVENT_DOWNLOAD_SPEED 拉流速率
EVENT_VIDEO_SIZE_CHANGED 视频大小改变
EVENT_STREAM_START 发起端开始推流(Flash活动有效)
EVENT_STREAM_STOP 发起端停止推流(Flash活动有效)

接口错误码说明

错误码 描述
10030 身份验证出错
10402 当前活动ID错误
10049 访客数据信息不全
10404 KEY值验证出错
10046 当前活动已结束
10405 微吼用户ID出错
10047 您已被踢出,请联系活动组织者
10048 活动现场太火爆,已超过人数上限
10410 用户信息不存在

观看回放    

准备工作

  1. 屏幕保持常亮
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  1. 设置观看布局
	<!--封装TextureView 支持截屏,内置水印浮层-->
	<com.vhall.player.vod.VodPlayerView
        android:id="@+id/rl_video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <!--仅用于视图渲染-->    
    <SurfaceView
        android:id="@+id/rl_video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

看回放

初始化房间信息:

参数字段 描述
id 活动ID(9位)
email 唯一标识
nickname 用户名
password K值
status 活动状态
WebinarInfoDataSource.LoadWebinarInfoCallback 返回房间信息回调

备注:如果email和nickname为空,则必须登陆,如果不登录,则email和nickname不能为空,如果都传,默认取登陆信息。

        VhallSDK.initWatch(params.watchId, customeId, customNickname, params.key, watchType, new WebinarInfoDataSource.LoadWebinarInfoCallback() {
            @Override
            public void onWebinarInfoLoaded(String jsonStr, WebinarInfo webinarInfo) {
					//jsonStr 原始数据返回, webinarInfo 解析后的房间信息封装
            }

            @Override
            public void onError(int errorCode, String errorMsg) {
                                
            }
        });
    

初始化播放器实例:

WatchPlayback watchPlayback;
WatchPlayback.Builder builder = new WatchPlayback.Builder()
		.context(watchView.getActivity())
		.vodPlayView(playbackView.getVideoView())/**需要截屏,添加浮层时使用*/
//                    .surfaceView(playbackView.getVideoView())//与vodPlayerView 2选1即可
		.callback(new WatchCallback())
		.docCallback(new DocCallback());/**如果使用文档,加此回调*/
watchPlayback = builder.build();
}

开始播放

watchPlayback.start();

设置缩放模式

缩放类型 描述
Constants.VideoMode.DRAW_MODE_NONE 原始大小
Constants.VideoMode.DRAW_MODE_FIT 等比缩放居中
Constants.VideoMode.DRAW_MODE_FILL 等比拉伸居中
watchPlayerback.setScaleType(type);

暂停播放

watchPlayback.pause();

停止播放

watchPlayback.stop();

销毁播放器释放资源

watchPlayback.releasePlayer();

获取播放进度

watchPlayback.seekTo(playerCurrentPosition);

获取当前播放进度

watchPlayback.getCurrentPosition();

获取播放时长:

watchPlayback.getDuration();

是否正在播放

watchPlayback.isPlaying();

设置倍速播放(推荐使用0.5-2倍速)

watchPlayback.setSpeed(float speed);

回放事件回调


	private class WatchCallback implements VHPlayerListener {

        @Override
        public void onStateChanged(Constants.State state) {
            switch (state) {
                case IDLE://待机状态
                    break;
                case START://播放中
                    break;
                case BUFFER://缓冲中
                    break;
                case STOP://停止(等同PAUSE 可调用resume方法恢复播放)
                    break;
                case END://播放结束(内容播放结束,恢复时默认从初始位置开始)
                    break;
            }
        }

        @Override
        public void onEvent(int event, String msg) {
            switch (event) {
                case Constants.Event.EVENT_DPI_LIST:
                /** 服务器支持的视频分辨率列表*/

                    break;
                case Constants.Event.EVENT_DPI_CHANGED:
                /** 分辨率被切换为msg*/
                    break;
            }
        }

        @Override
        public void onError(int errorCode, int innerErrorCode, String msg) {
            switch (errorCode) {
                case Constants.ErrorCode.ERROR_INIT:
                /** 初始化错误*/

                    break;
                case Constants.ErrorCode.ERROR_INIT_FIRST:
                /** 未初始化*/

                    break;
            }
        }
    }

播放器状态码

状态 描叙
IDLE 待机(PREPARING)
START 开始播放(STATE_READY)
BUFFER 正在缓冲
STOP 停止播放(等同PAUSE 可调用resume方法恢复播放)
END 播放结束

播放器事件码

事件(Constants.Event) 描叙
EVENT_INIT_SUCCESS 初始化成功(仅H5播放器触发)
EVENT_DPI_LIST 支持的分辨率列表
EVENT_DPI_CHANGED 视频分辨率改变(网络变化可能引起分辨率自动切换)

错误码

错误码 描述
-1 未初始化视频信息
-2 初始化视频信息错误

互动直播    

准备工作

Flash 互动

创建互动直播

Interactive 主要实现互动相关的功能模块,获取实例的方式如下:

Interactive 参数 描述
context 上下文
messageCallback 消息回调,用于接收互动的Socket消息,必须实现
roomCallback 房间回调,用于接收互动房间中的一些订阅消息,比如流的添加,移除 必须实现
private InterActive interactive;
interactive = new InterActive(interActView.getContext(), new RoomCallback(), new MessageEventCallback());

互动房间回调

class RoomCallback implements InterActive.RoomCallback {
        @Override
        public void onDidConnect() {
			//进入房间
        }

        @Override
        public void onDidError() {
			//进入房间失败
        }

        @Override
        public void onDidPublishStream() {
			// 调用publish方法,上麦成功后回调
        }

        @Override
        public void onDidUnPublishStream() {
			//调用unpublish,下麦成功后回调
        }

        @Override
        public void onDidSubscribeStream(Stream stream, final VHRenderView newRenderView) {
			//接收订阅的Stream ,VHRenderView渲染View,直接显示就可以使用
        }

        @Override
        public void onDidRoomStatus(Room room, Room.VHRoomStatus vhRoomStatus) {
            switch (vhRoomStatus) {
                case VHRoomStatusDisconnected:// 异常退出
                    
                    break;
                case VHRoomStatusError:
                    break;
                case VHRoomStatusReady:
                    break;
                case VHRoomStatusConnected: // 重连进房间
					break;
                default:
                    break;
            }
        }

        @Override
        public void onDidRemoveStream(Room room, Stream stream) {
            //有流退出时回调,用于取消订阅该流
        }
    }
}

获取互动房间信息

获取互动房间信息 VhallSDK.initInteractive 调用方式如下:

initInteractive 参数 描述
webinarId 活动ID
nickname 用户名称 (如果第三方登陆可以传递空)
email 用户标识 (如果第三方登陆可以传递空)
password K值 (暂不可用)
RequestCallback 接口请求回调信息 成功or失败
		interactive.init(mParam.watchId, "", "", "", new RequestCallback() {
            @Override
            public void onSuccess() {
            }

            @Override
            public void onError(int errorCode, String errorMsg) {

            }
        });

###设置互动的分辨率

interactive.setDefinition(definition);
分辨率常量 描述
VHILSS.SD 低清分辨率 192*144(默认)
VHILSS.HD 标清分辨率 320*240
VHILSS.UHD 高清分辨率 480*360

此方法必须在setLocalView 之前调用 如自定义参数option中有设置,则该方法失效

设置本地预览

参考InteractivePresenter

//VHRenderView 互动渲染的View,主要用于采集Camera  
VHRenderView renderView = new VHRenderView(context);
renderView.setScalingMode(scaleType);//设置缩放模式
renderView.init(interactive.getEglBase().getEglBaseContext(),null);//初始化渲染视图,渲染事件监听如有需要可自行实现;
/**
 * 互动控制器添加本地渲染视图
 *
 * @param localView  本地预览渲染视图
 * @param streamType 互动流类型
 * @param attributes 流信息自定义内容,订阅端原样返回;
 */
interactive.setLocalView(renderView,Stream.VhallStreamType.VhallStreamTypeAudioAndVideo,null);
//将新建的本地渲染视图,添加到布局界面进行展示;
interFraView.addLocalView(renderView);

VHRenderView的渲染填充模式

SurfaceViewRenderer.VHRenderViewScalingMode 说明
kVHRenderViewScalingModeNone 原始大小
kVHRenderViewScalingModeAspectFit 自适应居中
kVHRenderViewScalingModeAspectFill 铺满视图

互动流类型

VhallStreamType 说明
VhallStreamTypeOnlyAudio 纯音频
VhallStreamTypeOnlyVideo 纯视频
VhallStreamTypeAudioAndVideo 音视频
VhallStreamTypeScreen 桌面共享(pc端发起)
VhallStreamTypeFile 文件插播(pc插播)

本地预览镜像设置

如果不希望本地预览时旋转手机产生镜像效果,可在本地渲染视图上添加镜像设置;

VHRenderView.setMirror(true);

进入房间

实现互动功能必须调用此方法

interactive.enterRoom();

下麦,当调用携带CallBack参数方法时,当接口调用成功时,会发送一条下麦消息,PC端也能收到下麦的消息,当调用空参数方法时,不会发送消息,SDK直接下麦

interactive.unpublish();
interactive.unpublish(new RequestCallback() {
            @Override
            public void onSuccess() {
                interActView.finish();
            }

            @Override
            public void onError(int errorCode, String errorMsg) {

            }
        });

上麦

房间连接成功后,可根据业务需求,自定义上麦时机;
回调onDidConnect()成功后生效;

interactive.onpublish();

下麦

为保证各设备信息同步,请保证下麦接口调用成功后再执行下麦操作;

interactive.unpublish(new RequestCallback(){});

设备状态切换

切换摄像头

切换本地采集前后置摄像头

interactive.onSwitchCamera();

切换本地禁音状态

interactive.onSwitchAudio(isOpen);

切换本地视频开关状态

interactive.onSwitchVideo(isOpen);

销毁互动模块

interactive.onDestroy();

聊天服务    

概述

当用户在创建发起直播实例或者观看直播实例时的.ChatCallback中传入chatCallback回调,聊天服务器就已经开启了,具体参考快速接入介绍中的发起观看创建实例的描述

private class ChatCallback implements ChatServer.Callback {
	@Override
	public void onChatServerConnected() {
		// 聊天服务器建立
	}
	@Override
	public void onConnectFailed() {
		// 聊天服务器连接失败
	} 
	@Override
	public void onChatMessageReceived(ChatServer.ChatInfo chatInfo) {//消息接收
		switch (chatInfo.event) {
			case ChatServer.eventMsgKey: // 聊天消息通知
				break;
			case ChatServer.eventCustomKey: // 自定义消息通知
				break;
			case ChatServer.eventOnlineKey: // 上线消息通知
				break;
			case ChatServer.eventOfflineKey: // 下线消息通知
				break;
			case ChatServer.eventQuestion: // 问答消息
				break;
		}
	}
	@Override
	public void onChatServerClosed() {
		
	}
}


//聊天服务器其他方法
//连接聊天服务器:
getBroadcast().connectChatServer ();
//关闭聊天服务器:
getBroadcast().disconnectChatServer ();

字段 描述
account_id 用户ID
user_name 用户昵称
avater 头像id
room 活动id
event 消息类型 (用于区分消息用途)
time 发送时间

上下线消息

消息体event字段说明:chatInfo.event = “online offline” 用户接受消息可以自定义上下线的内容

字段 描述
event online offline
OnlineData 描述
role 用户类型 host:主持人 guest:嘉宾 assistant:助手 user:观众
concurrent_user 房间内当前用户数
is_gag 是否被禁言
attend_count 参会人数

聊天消息

消息体event字段说明:chatInfo.event = “msg” 目前发起直播和观看直播中可以聊天。发起直播调用getBroadCast().sendChat()方法 。观看直播调用getWatchLive().sendChat()。

字段 描述
text 聊天内容 offline
VhallSDK.RequestCallback 回调信息

发起直播代码展示 && 观看直播代码展示(默认登录):

getBroadcast().sendChat(text, new VhallSDK.RequestCallback() { // 发起直播时聊天
	@Override
	public void success() {
		//发送成功
	}
	@Override
	public void failed(int errorCode, String reason) {
		//发送失败
	}
});
getWatchLive().sendChat(text, new VhallSDK.RequestCallback() {// 观看直播时聊天
	@Override
	public void success() {
		//发送成功
	}
	@Override
	public void failed(int errorCode, String reason) {
		//发送失败
	}
});

自定义消息

消息体event字段说明:chatInfo.event = “custom_broadcast

字段 描述
text 固定JSON格式

getBroadcast().sendCustom(text, new VhallSDK.RequestCallback() {//发直播自定义消息
	@Override
	public void success() {
		//发送成功
	}
	@Override
	public void failed(int errorCode, String reason) {
		//发送失败
	}
});

getWatchLive().sendCustom(text, new VhallSDK.RequestCallback() {//看直播自定义消息
	@Override
	public void success() {
		//发送成功
	}
	@Override
	public void failed(int errorCode, String reason) {
		//发送失败
	}
});

/**
 * 功能演示 模拟点击发送
 */
send_custom.setOnClickListener(new View.OnClickListener() {  
	@Override
	public void onClick(View v) {
		JSONObject json = new JSONObject();
		try {
			json.put("key0", "value");
			json.put("key1", "0000");
			json.put("key2", "微吼");
		} catch (JSONException e) {
			e.printStackTrace();
		}
		mPresenter.sendCustom(json);
	}
});

聊天记录

获取SDK聊天记录。聊天记录只在观看直播时候获取,调用acquireChatRecord()

字段 描述
showAll 显示当次直播聊天最多为20条,true显示所有聊天最条为20条
ChatRecordCallback 聊天记录回调

getWatchLive().acquireChatRecord(false, new ChatServer.ChatRecordCallback() {
	@Override
	public void onDataLoaded(List<ChatServer.ChatInfo> list) {
		//回调获取成功
	}
	@Override
	public void onFailed(int errorcode, String messaage) {
		//回调获取失败,查看错误信息
	}
});

错误码 描述
10030 身份验证出错
10402 当前活动ID错误
10403 活动不属于自己
10407 查询数据为空
10408 当前活动非直播状态
10409 参会信息不存在
10410 活动开始时间不存在

问答消息

消息体event字段说明:chatInfo.event = “question” 发送活动问答
目前只支持观看端发送活动问答。在用户登陆成功的情况下可以发送问答,问答每5分钟发送一次,避免一些用户恶意发送。调用sendQues()方法,发送问答信息。

getWatchLive().sendQuestion (text, new VhallSDK.RequestCallback() {
	@Override
	public void success() {
		// 发送成功 
	}
	@Override
	public void failed(int errorCode, String reason) {
		// 发送失败
	}
});
QuestionData 描述
id 问题的id
nick_name 昵称
content 提问内容
join_id 参会ID
created_at 创建时间
role_name 角色
is_open 是否为私密回答
QuestionData answer 参数和此消息体相同
错误码 描述
10601 不是直播
10602 参会者不存在
10603 5分钟内不可提问
10604 活动ID不能为空
10605 问题不能为空
10606 用户不能为空

聊天消息字段说明    

ChatInfo 字段说明

字段名称 说明
account_id 发送者id
user_name 发送者昵称
avatar 发送者头像
room 房间id
event 事件类型
time 发送时间
role 角色类型 host 主持人,user 观众,guest 助理,assistant 嘉宾
rolename 角色类型 1 主持人,2 观众,3 助理,4 嘉宾
id 消息id
unDifinedData 未知数据(事件类型不支持时,原样返回发送数据)
msgData 聊天类容
onlineData 在线数据
questionData 文档数据
favData 点赞数据
replyMsg 回复信息

OnlineData 字段说明

字段名称 说明
role 角色类型 host 主持人,user 观众,guest 助理,assistant 嘉宾
rolename 角色类型 1 主持人,2 观众,3 助理,4 嘉宾
concurrent_user 房间内当前用户数
is_gag 是否被禁言
attend_count 参会人数
tracksNum 观看次数

QuestionData 字段说明

字段名称 说明
id 问题的id
nick_name 提问者昵称
avatar 提问者头像
role_name 提问者角色
content 提问内容
join_id 提问者id
created_at 创建时间
is_open 是否为私密回答
answer 问题数据

ReplyMsgBean 字段说明

字段名称 说明
isHistoryMsg 是否历史消息
sendId 发送者id
nickName 回复者昵称
avatar 回复者头像
type 回复类型
content 回复类容
sendTime 发送时间
roleName 发送者角色

AtListBean 字段说明

字段名称 说明
accountId 用户id
nickName 用户昵称

消息服务    

概述

活动消息服务

消息服务器,目前只用于观看直播时使用,用于接收观看直播时的一些消息处理

private class MessageEventCallback implements MessageServer.Callback {
	@Override
	public void onEvent(MessageServer.MsgInfo messageInfo) {
		switch (messageInfo.event) {
			case MessageServer.EVENT_PPT_CHANGED://PPT翻页消息
				documentView.showDoc(messageInfo.pptUrl);
				break;
			case MessageServer.EVENT_DISABLE_CHAT://禁言
				break;
			case MessageServer.EVENT_KICKOUT://踢出
				break;
			case MessageServer.EVENT_OVER://直播结束
				break;
			case MessageServer.EVENT_PERMIT_CHAT://解除禁言  
				break;
			case MessageServer.EVENT_START_LOTTERY://抽奖开始
				break;
			case MessageServer.EVENT_END_LOTTERY://抽奖结束
				break;
			case MessageServer.EVENT_NOTICE: //公告消息
				break;
			case MessageServer.EVENT_SIGNIN: //签到消息
				break;
			case MessageServer.EVENT_QUESTION: // 问答开关
				break;
			case MessageServer.EVENT_DIFINITION_CHANGED// PC端切换分辨率
				break;
			case MessageServer.EVENT_SURVEY:// 问卷消息
				break;
			/** 白板的操作*/
			case MessageServer.EVENT_SHOWBOARD:// 白板消息 1 开始白板 0关闭白板
				break;
			case MessageServer.EVENT_INITBOARD:// 初始化消息
				break;
			case MessageServer.EVENT_PAINTBOARD: // 绘制白板
				break;
			case MessageServer.EVENT_DELETEBOARD: // 删除白板内容
				break;
			case MessageServer.EVENT_CLEARBOARD:// 清空白板操作
				break;
			/** 文档的操作*/
			case MessageServer.EVENT_PAINTDOC:// 文档绘制
				break;
			case MessageServer.EVENT_DELETEDOC: // 删除绘制
				break;
			case MessageServer.EVENT_CLEARDOC://清楚绘制
				break;
		}
	}
	@Override
	public void onConnectFailed() {
		//连接失败
	}
	@Override
	public void onMsgServerClosed() {
		//消息关闭
	}
}

//连接消息服务器
getWatchLive().connectMsgServer ();
关闭消息服务器:
getWatchLive().disconnectMsgServer ();


1、活动结束消息

event = “MessageServer.EVENT_OVER” 当主播关闭直播的时候触发此消息

2、抽奖消息

观看直播时播主可以发起抽奖,而在移动端观看时可以接受抽奖消息,目前默认游客和参会用户都可以抽奖,而且只能被中奖一次

开始抽奖消息体 event = “MessageServer. EVENT_START_LOTTERY”

字段 描述
Num 可以中奖的数量
type 消息类型键

结束抽奖消息体msgInfo.event = “MessageServer. EVENT_END_LOTTERY”

字段 描述
type 可以中奖的数量
Msginfo.Lists 返回中奖的数组
IsLottery 是否中奖
id 当前中奖人的参会ID
LotteryID 抽奖ID

Msginfo.Lists 结构体

字段 描述
nickname 中奖昵称
user_id 用户ID
third_user_id 登陆用户名

当用户中奖后,需要提交信息,访问SDK中的submitLotteryInfo() , 所需参数如下

字段 描述
id 当前中奖人的参会ID
LotteryID 抽奖ID
name 中奖人姓名
phone 中奖人电话

VhallSDK.getInstance().submitLotteryInfo(joid_id, lottery_id, nickname, phone, 
new VhallSDK.RequestCallback() {
	@Override
	public void success() {
	
	}
	@Override
	public void failed(int errorCode, String reason) {
	
	}
});
字段 描述
10030 身份验证失败
10409 参会ID不能为空
10410 抽奖ID不能为空
10411 用户名称不能为空
10412 用户手机不能为空

3、公告消息

当PC段发送一段公共消息,消息服务器会接受到这句公共消息,用户自定义显示消息样式 event = “MessageServer.EVENT_NOTICE”

字段 描述
content 公告内容
publish_release_time 公告发布时间

4、签到消息

event = “MessageServer.EVENT_SIGNIN” 当主播正在直播,可以发布签到,观看用户必须登陆才可以签到,签到后web端会进行展示 字段 描述

字段 描述
webinar_id 活动ID
sign_id 签到ID
user_id 用户ID
sign_show_time 签到初始时间

5、问卷消息

event = “MessageServer. EVENT_SURVEY” 当主播正在直播,可以发送问卷,消息服务器会接收到survey_id ,需要使用这个survey_id 获取问卷信息

VhallSDK.getInstance().getSurveyInfo(surveyid, new SurveyDataSource.SurveyInfoCallback() {
	@Override
	public void onSuccess(Survey survey) {
		watchView.showSurvey(survey); //UI层显示
	}
	@Override
	public void onError(int errorCode, String errorMsg) {
		chatView.showToast(errorMsg);
	}
});

6、白板消息

event = “MessageServer.EVENT_SHOWBOARD” // 开启或者关闭白板 event = “MessageServer.EVENT_INITBOARD” // 初始化白板 event = “MessageServer.EVENT_PAINTBOARD” // 绘制白板 event = “MessageServer.EVENT_DELETEBOARD // 删除白板内容 event = “MessageServer.EVENT_CLEARBOARD” // 清空白板

1配置白板XML (在需要显示的界面中加入, 具体详见文档)

<com.vhall.business.widget.WhiteBoardView
        android:id="@+id/board"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

2获取到白板的2获取到白板的实例调用setStep方法 , 将消息服务器获取的信息传递进方法里就可以了

public void paintBoard(MessageServer.MsgInfo msgInfo) {
	board.setStep(msgInfo);
}
public void paintBoard(String key, List<MessageServer.MsgInfo> msgInfos) {
	board.setSteps(key, msgInfos);
}

7、PPT消息

msgInfo.event = “MessageServer.EVENT_CHANGEDOC” // 切换文档页面 msgInfo.event = “MessageServer.EVENT_PAINTDOC” // 绘制文档信息 msgInfo.event = “MessageServer.EVENT_DELETEDOC” // 删除绘制信息 msgInfo.event = “MessageServer.EVENT_CLEARDOC” // 清空绘制信息

1 配置文档XML (在需要显示的界面中加入, 具体详见文档)

<com.vhall.business.widget.PPTView
        android:id="@+id/iv_doc"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

2 获取到文档的实例调用setStep方法 , 将消息服务器获取的信息传递进方法里就可以了

  @Override
    public void paintPPT(MessageServer.MsgInfo msgInfo) {
        doc.setStep(msgInfo);
    }
 
    @Override
    public void paintPPT(String key, List<MessageServer.MsgInfo> msgInfos) {
        doc.setSteps(key, msgInfos);
    }

文档服务    

Flash 文档

初始化文档白板实例

<com.vhall.business.widget.WhiteBoardView
        android:id="@+id/board"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
        
<com.vhall.business.widget.PPTView
		android:id="@+id/iv_doc"
		android:layout_width="match_parent"
		android:layout_height="match_parent" />
		
<FrameLayout
        android:id="@+id/fl_h5_doc"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible" />

执行文档操作

文档消息来源于消息服务

消息事件 描叙
MessageServer.EVENT_INITBOARD 初始化白板
MessageServer.EVENT_SHOWBOARD 开启或关闭白板
MessageServer.EVENT_PAINTBOARD 绘制白板
MessageServer.EVENT_DELETEBOARD 删除白板内容
MessageServer.EVENT_CLEARBOARD 清空白板
MessageServer.EVENT_CHANGEDOC 切换文档页面
MessageServer.EVENT_PAINTDOC 绘制文档信息
MessageServer.EVENT_DELETEDOC 删除文档绘制信息
MessageServer.EVENT_CLEARDOC 清空文档绘制信息
MessageServer.EVENT_SHOWH5DOC h5文档 1 开启文档 0 关闭文档
MessageServer.EVENT_PAINTH5DOC 文档画面加载
//详细使用参考Demo
board.setStep(msgInfo);

board.setSteps(key,msgInfo);

doc.setStep(msgInfo);

doc.setSteps(key,msgInfos);

#新增 5.0.0 h5文档 ###看直播文档

MessageServer.Callback里面添加
//EVENT_SHOWH5DOC 展示或隐藏 h5文档

case MessageServer.EVENT_SHOWH5DOC:
    if (documentView!=null){
        if (messageInfo.watchType==1){
            documentView.showType(3);
 }else {
            documentView.showType(2);
 }
    }
    break;
//给h5文档添加正在演示的view

case MessageServer.EVENT_PAINTH5DOC:
    if (documentView!=null){
        documentView.paintH5DocView(messageInfo.h5DocView);
 }
    break;
	```
具体 文档演示可参考 DocumentFragment

###回放文档 
DocumentEventCallback 里面添加
![](http://doc.vhall.com/uploads/202010/5f9c0ace88d23_5f9c0ace.png)


void onEvent(String event, String type, View docView);

void onError(int errorCode, int innerError, String errorMsg); 实例 @Override public void onEvent(String event, String type, View docView) { if (documentView != null) { if (event.equals(KEY_OPERATE)) { if (type.equals(TYPE_ACTIVE)) { documentView.paintH5DocView(docView); } else if (type.equals(TYPE_SWITCHOFF)) { documentView.showType(2); } else if (type.equals(TYPE_SWITCHON)) { documentView.showType(3); } } } } @Override public void onError(int errorCode, int innerError, String errorMsg) { switch (errorCode) { case ERROR_CONNECT://文档服务链接错误 case ERROR_SEND://文档信息发送错误,演示端生效 break; case ERROR_DOC_INFO://文档加载错误 try { JSONObject obj = new JSONObject(errorMsg); String msg = obj.optString("msg"); String cid = obj.optString("cid");

} catch (JSONException e) { e.printStackTrace(); } break; default: break; } }


详情参考demo

问卷服务    

推荐使用Web页嵌入方式实现,支持获取问卷详情本地绘制实现;

实例化问卷SurveyView

xml布局文件添加

    <com.vhall.uilibs.util.SurveyView
        android:id="@+id/wv_survey"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
        
    SurveyView wvSurvey = findViewById(R.id.wv_survey);

直接创建

SurveyView wvSurvey = new SurveyView(context);
//设置布局参数
...

设置问卷事件监听

wvSurvey.setEventListener(new SurveyView.EventListener(){});

事件类型说明

(SurveyView)事件码 描述
EVENT_JS_BACK 不答题情况下退出问卷
EVENT_JS_FINASH 提交答案并退出问卷

Web嵌入版本无需本地提交答案,默认将 EVENT_JS_FINASH 事件转换成EVENT_JS_BACK事件用于通知结束或关闭问卷视图;

获取问卷地址

参数 描述
surveyId 问卷ID(收到发布问卷事件时返回)
webinar_id 房间ID(WatchId 或 WebinarInfo.webinar_id)
userId 答卷用户Id(WebinarInfo.Join_id)

WebinarInfo 初始化房间返回的房间信息( VhallSDK.initWatch())

//userId 传空默认获取当前登录用户id
String survyUrl = VhallSDK.getSurveyUrl(surveyId,webinarId,"");

加载问卷详情

wvSurvey.loadUrl(surveyUrl);

房间信息字段说明    

WebinarInfo 字段说明

字段名称 说明
chatforbid 是否被禁言
role_name host 主持人 guest 嘉宾 user 观众 assistant 助理
nick_name 昵称
webinar_id 活动Id
broadcastToken token外部传入
join_id 参会Id
status 活动状态
user_id 用户ID
notice 通知信息
cheat_num 在线人数显示系数
use_white_board 是否正在使用白板1开启 0 未开启(Flash)
use_doc 是否开启了文档 1 开启 0 未开启
is_publish_vr 是否是VR直播 0普通、1VR
is_interact 是否是互动活动 1互动活动,0非互动活动
hands_up 是否开启举手 1开启举手,0未开启举手
enterToken 参与互动的Token(Flash)
vss_token 进入H5房间的Token
vss_room_id H5房间的房间Id
question_status 问答状态 1 开启问答,0未开启问答(仅直播有效)
cast_screen 投屏权限 1有投屏权限,0没有投屏权限

ResponseRoomInfo 字段说明

字段名称 说明
room_id 房间Id
subject 标题
account_id 房主id
inav_id 互动is
channel_id 渠道id
record_id 回放id
start_time 开始时间
introduction 介绍
category 直播视频类别
cover_image 封面图片地址
topics 活动话题
layout 布局
status 状态,0预约1直播2结束
is_delete 是否删除 1 删除,0 未删除
message_approval 消息数超过200.1,自动发送,2.自动阻止 3,关闭审核
created_at 创建时间
like 点赞数
third_party_user_id 用户id
paas_access_token paas token
role_name 角色名称 1主持人2观众3助理4嘉宾
sign_id 签到id
sign_life_time 签到剩余时间
advert_info 广告信息
attributes 房间状态信息
userStatus userStatus

ResponseAttributes 房间状态字段说明

字段名称 说明
is_board 开关白板,1开0关
is_doc 开关文档,1开0关
is_handsup 开关举手,1开0关
main_screen 主画面
doc_permission 文档权限(主讲人)
speaker_list 上麦列表
start_type 1 web 2 app 3 sdk 4 推拉流 5 定时 6 admin后台 7第三方8 助手
rebroadcast 转播源房间id
all_banned 全体禁言1禁言0取消禁言
is_desktop 开关桌面演示,1开0关
is_qa 是否开启问答,1开0关
is_invitecard 是否开启邀请卡,1开0关
rebroadcast_channel_id 转播源房间频道ID

ResponseUserStatus 用户信息 字段说明

字段名称 说明
is_banned 是否禁言,1是0否
is_kicked 是否踢出,1是0否

VR直播    

如果发起的直播时VR直播 , 可以使用陀螺仪功能

if (!getWatchLive().isVR()) {  // 判断是否是VR活动
	watchView.showToast("当前活动为非VR活动,不可使用陀螺仪");
	return;
 }
 getWatchLive().setVRHeadTracker(!getWatchLive().isVRHeadTracker()); // 设置陀螺仪

投屏功能演示    

投屏功能

备注: 新增Support包 Suppot包中包含投屏功能,不需要的可以删掉,uilibs中注释掉投屏相关代码即可;

功能描述

投屏是基于DLNA功能实现的, 使用的ClingDLNA类库 , 确保使用的机器是具备DLNA功能的手机 理论上支持API16以上的机型, 实际测试有部分API 16以上的机型不支持

DLNA 协议中包含多项协议及标准, 其中UPnP协议是最重要的部分 UPnP协议定义了设备之间,设备和控制点,控制点之间通信的协议

投屏依赖配置

maven {
            url 'http://4thline.org/m2'
        }
	api 'org.eclipse.jetty:jetty-server:8.1.8.v20121106'
    api 'org.eclipse.jetty:jetty-servlet:8.1.8.v20121106'
    api 'org.eclipse.jetty:jetty-client:8.1.8.v20121106'
    api 'org.fourthline.cling:cling-core:2.1.1'
    api 'org.fourthline.cling:cling-support:2.1.1'

设备注册监听实现

    private BrowseRegistryListener registryListener = new BrowseRegistryListener();

private DevicePopu devicePopu;//demo层展示投屏操作的pop
protected class BrowseRegistryListener extends DefaultRegistryListener {

        @Override
        public void remoteDeviceDiscoveryStarted(Registry registry, RemoteDevice device) {
//            deviceAdded(device);
        }

        @Override
        public void remoteDeviceDiscoveryFailed(Registry registry, final RemoteDevice device, final Exception ex) {
//            deviceRemoved(device);
        }
        /* End of optimization, you can remove the whole block if your Android handset is fast (>= 600 Mhz) */

        @Override
        public void remoteDeviceAdded(Registry registry, RemoteDevice device) {
            if (device.getType().getNamespace().equals("schemas-upnp-org") && device.getType().getType().equals("MediaRenderer")) {
                deviceAdded(device);
            }

        }

        @Override
        public void remoteDeviceRemoved(Registry registry, RemoteDevice device) {
            deviceRemoved(device);
        }

        @Override
        public void localDeviceAdded(Registry registry, LocalDevice device) {
//            deviceAdded(device);
        }

        @Override
        public void localDeviceRemoved(Registry registry, LocalDevice device) {
//            deviceRemoved(device);
        }

        public void deviceAdded(final Device device) {
            runOnUiThread(new Runnable() {
                public void run() {
                    if (devicePopu == null) {
                        devicePopu = new DevicePopu(WatchActivity.this);
                        devicePopu.setOnItemClickListener(new OnItemClick());
                    }
                    devicePopu.deviceAdded(device);
                }
            });
        }

        public void deviceRemoved(final Device device) {
            runOnUiThread(new Runnable() {
                public void run() {
                    if (devicePopu == null) {
                        devicePopu = new DevicePopu(WatchActivity.this);
                        devicePopu.setOnItemClickListener(new OnItemClick());
                    }
                    devicePopu.deviceRemoved(device);
                }
            });
        }
    }

服务连接监听实现

private AndroidUpnpService upnpService;
private ServiceConnection serviceConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder service) {
            Log.e("Service ", "mUpnpServiceConnection onServiceConnected");
            upnpService = (AndroidUpnpService) service;
            // Clear the list
            if (devicePopu != null)
                devicePopu.clear();
            // Get ready for future device advertisements
            upnpService.getRegistry().addListener(registryListener);
            // Now add all devices to the list we already know about
            for (Device device : upnpService.getRegistry().getDevices()) {
                registryListener.deviceAdded(device);
            }
            // Search asynchronously for all devices, they will respond soon
            upnpService.getControlPoint().search(); // 搜索设备
        }

        public void onServiceDisconnected(ComponentName className) {
            upnpService = null;
        }
    };

绑定AndroidUpnpService 服务

bindService(
                new Intent(this, AndroidUpnpServiceImpl.class),
                serviceConnection,
                Context.BIND_AUTO_CREATE
        )

离开页面时一定要注销服务及监听,避免内存泄漏

if (upnpService != null) {
            upnpService.getRegistry().removeListener(registryListener);
        }
        this.unbindService(serviceConnection);

获取支持投屏的设备

/**
     * 获取支持投屏的设备
     *
     * @return 设备列表
     */
    @Nullable
    public Collection<Device> getDmrDevices() {
        if (upnpService == null)
            return null;
        Collection<Device> devices = upnpService.getRegistry().getDevices(DMR_DEVICE_TYPE);
        return devices;
    }

实例化投屏控制对象

    /**
     * 
     * @param paramDeviceItem  投屏设备
     * @param paramAndroidUpnpService 通信服务
     * @param paramString1 资源地址
     * @param webinarInfo 房间信息
     */
        DMCControl dmcControl = new DMCControl(deviceDisplay, service, mPlayer.getOriginalUrl(), webinarInfo);

设置投屏控制回调

   public void setDMCControlListener(DMCControlListener controlListener) {}

回调说明

public interface DMCControlListener {
   int ERROR_PLAY = 50001;
   int ERROR_PAUSE = 50002;
   int ERROR_STOP = 50003;
   int ERROR_GET_POSITION = 50004;
   int ERROR_SEEK = 50005;
   int ERROR_PERMISSION = 50006;

   String ERROR_MSG_PERMISSION="无投屏权限,如需使用请咨询您的销售人员或拨打客服电话:400-888-9970";

   void onStart();//投屏开始

   void onPause();//暂停投屏

   void onStop();//停止投屏

   void currentPosition(String curTIme, String duration);//播放进度

   void onError(int errorCode, String errorMsg);//错误回调
}

开始播放(投屏)

public void play() {}

暂停播放

 public void pause() {}

重播

public void rePlayControl() {}

停止播放(停止投屏)

public void stop() {}

设置进度

 public void seekToPosition(String target) {}

DEMO    

简介

本Demo包含uiLibs包,是使用MVP搭建的一个简易的模板,可以根据uiLibs的功能演示去集成,也可以直接使用uiLibs快速开发。

地址

Github : https://github.com/vhall/vhallsdk_live_android

配置

1. 开发环境要求

Pc操作系统:64window系统 JDK: 1.8以上
Android studio : 建议使用Android studio 2.0以上
Android: 5.0以上
备注: Android设备操作系统需要5.0以上, 需要访问手机硬件,暂不支持模拟器开发

2. 导入vh依赖(见*/uiLibs/build.gradle)

api 'com.vhall.android.library:vh-saas-interactive:5.0.0'  互动相关  
api 'com.vhall.android.library:vh-saas-sdk:5.0.0'  直播、点播、聊天、基础服务相关  
api 'com.vhall.android.library:vh-saas-sdk-support:2.0.0' 移动端适配pc去flash化兼容包

3. 添加三方依赖

api 'com.github.bumptech.glide:glide:3.7.0' // 用于加载PPT
//弹幕
    implementation 'com.github.ctiao:DanmakuFlameMaster:0.6.4'
    implementation 'com.github.ctiao:ndkbitmap-armv7a:0.6.4'
    implementation 'com.github.ctiao:ndkbitmap-armv5:0.6.4'
    implementation 'com.github.ctiao:ndkbitmap-x86:0.6.4'

4. 开发环境要求

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.RECORD_VIDEO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

参数描述

发起直播参数

参数字段 描述
直播Token 请求API获取的Token
活动ID 活动ID(9位) , 在网页上创建
发起分辨率 默认640
码率 视频码率
帧率 视频帧率

观看直播参数

参数字段 描述
活动ID 活动ID(9位) , 在网页上创建
K值 第三方校验
用户ID 不登陆的情况下必填
昵称 不登陆的情况下必填

常见问题    

|初始化报异常:java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.vhall.framework.connect.VHallConnectService$ConnectBinder

产生原因:初始化方法在其他进程内执行; 解决方案:确保初始化方法在app进程内运行;

@Override
public void onCreate() {
    super.onCreate();
    context = this;
    if(isAppProcess()){
        VhallSDK.init(this, getResources().getString(R.string.vhall_app_key), getResources().getString(R.string.vhall_app_secret_key));
        VssSdk.getInstance().init(getApplicationContext(), getUserId());
    }
}
	
/**
 * 判断该进程是否是app进程
 * @return
 */
public boolean isAppProcess() {
    String processName = getProcessName();
    if (processName == null || !processName.equalsIgnoreCase(this.getPackageName())) {
        return false;
    }else {
        return true;
    }
}

/**
 * 获取运行该方法的进程的进程名
 * @return 进程名称
 */
public static String getProcessName() {
    int processId = android.os.Process.myPid();
    String processName = null;
    ActivityManager manager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
    Iterator iterator = manager.getRunningAppProcesses().iterator();
    while (iterator.hasNext()) {
        ActivityManager.RunningAppProcessInfo processInfo = (ActivityManager.RunningAppProcessInfo) (iterator.next());
        try {
            if (processInfo.pid == processId) {
                processName = processInfo.processName;
                return processName;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return processName;
} 

登录账号问题    

测试登录demo的时候 会提示账号不存在的问题?**** 因为我们会默认可能用自己的主账号密码去登录demo,这样就会提示账号不存在(错误用法) 正确的用法为 1.如果只是观看的话,可以用 创建用户接口 https://www.vhall.com/saas/doc/70.html 中的 third_user_id 为 账号 pass 为密码 去进行登录

2.如果要发起直播的话 要在1的基础上再去调用 更改用户权限 https://www.vhall.com/saas/doc/76.html 去用userid 更改为子账号 ,且分配相应的并发或者流量,就能做为发起直播的账号了 3.给子账号创建一个直播间,注意一定要传userid userid是用创建第三方账号接口返回的 4.发直播需要一个token 有效时间为24小时 https://www.vhall.com/saas/doc/69.html

身份验证失败    

运行demo 如果出现这种情况 提示身份验证失败 10030 可能是以下几种 情况 Android 和 ios 一样

身份验证失败 10030 身份验证失败 的错误原因大概有以下几点,咱们参考排查一下: 1.发起直播的 accessToken  过期,有效期为24小时,失效会出现这种情况。 2.在API调用说明中,获取API/SDK的使用权限信息中 安卓没有编辑发行版本安全码SHA1和包名,或者IOS没有编辑安全码Bundle ID,或者和注册的需要和SDK获取到的一致否则报错。 3.测试的时候使用没有带签名的包 ,包名没有和应用对应上,appkey 等参数没有填写。 4.正式和测试环境的使用同一一个应用,包名没有进行更新。 5.获取verify/access-token 获取SDK直播操作token的时候,accessToken只适用于第1个应用;当用户开通多个app_key时,该参数为必选,在传入参数的时候需要传app_key,详情请看 http://e.vhall.com/home/vhallapi/audience#verify/access-token_获取SDK直播操作token。

初始化异常    

|初始化报异常:java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.vhall.framework.connect.VHallConnectService$ConnectBinder

产生原因:初始化方法在其他进程内执行; 解决方案:确保初始化方法在app进程内运行;

@Override
public void onCreate() {
    super.onCreate();
    context = this;
    if(isAppProcess()){
        VhallSDK.init(this, getResources().getString(R.string.vhall_app_key), getResources().getString(R.string.vhall_app_secret_key));
        VssSdk.getInstance().init(getApplicationContext(), getUserId());
    }
}
	
/**
 * 判断该进程是否是app进程
 * @return
 */
public boolean isAppProcess() {
    String processName = getProcessName();
    if (processName == null || !processName.equalsIgnoreCase(this.getPackageName())) {
        return false;
    }else {
        return true;
    }
}

/**
 * 获取运行该方法的进程的进程名
 * @return 进程名称
 */
public static String getProcessName() {
    int processId = android.os.Process.myPid();
    String processName = null;
    ActivityManager manager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
    Iterator iterator = manager.getRunningAppProcesses().iterator();
    while (iterator.hasNext()) {
        ActivityManager.RunningAppProcessInfo processInfo = (ActivityManager.RunningAppProcessInfo) (iterator.next());
        try {
            if (processInfo.pid == processId) {
                processName = processInfo.processName;
                return processName;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return processName;
} 

版本更新记录    

版本:v6.0.0 更新时间 2021.02.08

1.整体优化;
2.重做抽奖
3.更新问卷地址
4.支持RSA接口校验
5.支持第三方id登录
6.支持发直播更新昵称
说明:6.0.0以上版本更新请访问:https://saas-doc.vhall.com/docs/show/1225

版本:v5.0.2 更新时间 2020.12.02

1.bug 修复;

版本:v5.0.1 更新时间 2020.11.20

1.增加上报额外参数接口;
2.增加直播镜像接口;
3.优化基础功能;

版本:v5.0.0 更新时间 2020.10.30

1.优化用户使用 h5和flash统一使用方法;
2.优化日志上报;
3.添加h5文档显示 具体可以参考demo实现 或者查看大版本更新说明;
4.优化基础功能;

版本:v4.3.1 更新时间 2020.07.02

1.支持预告活动,进入后可收到开始直播消息;
2.Demo 问答显示隐藏优化;

版本:v4.3.0 更新时间 2020.06.15

1.支持水印显示;
2.修复角色问题;
3.修复回复空数据问题;
4.优化demo;

版本:v4.2.0 更新时间 2020.04.29

1.完善禁言状态判断;
2.WebinarInfoRemoteDataSource 类引用路径变更;删除旧引用重新import即可;
3.升级互动模块,新增互动美颜;

//废弃
renderView.setStream(Stream)

//替代
stream.removeAllRenderView();
stream.addRenderView(renderView)

//美颜设置,仅本地流可用
localStream.setEnableBeautify(true);//默认等级2
//美颜等级设置,建议渲染可见后使用
//localStream.setBeautifyLevel(2);//美颜等级1-4 默认 2

4.完善房间信息数据返回(新增问答开启状态,投屏权限);
webinarInfo 新增字段;

字段名称 说明
question_status 问答状态 1 开启问答,0未开启问答(仅直播有效)
cast_screen 投屏权限 1有投屏权限,0没有投屏权限

ResponseRoomInfo 新增字段

字段名称 说明
attributes 房间状态信息
userStatus 用户状态信息

ResponseAttributes 房间状态字段说明

字段名称 说明
is_board 开关白板,1开0关
is_doc 开关文档,1开0关
is_handsup 开关举手,1开0关
main_screen 主画面
doc_permission 文档权限(主讲人)
speaker_list 上麦列表
start_type 1 web 2 app 3 sdk 4 推拉流 5 定时 6 admin后台 7第三方8 助手
rebroadcast 转播源房间id
all_banned 全体禁言1禁言0取消禁言
is_desktop 开关桌面演示,1开0关
is_qa 是否开启问答,1开0关
is_invitecard 是否开启邀请卡,1开0关
rebroadcast_channel_id 转播源房间频道ID

ResponseUserStatus 用户状态信息 字段说明

字段名称 说明
is_banned 是否禁言,1是0否
is_kicked 是否踢出,1是0否

5.新增聊天回复、@操作数据返回;
Flash ChatInfo ReplyMsg 说明

字段名称 说明
account_id 发送者id
nickName 回复者昵称
avatar 回复者头像
type 回复类型
content 回复类容
time 发送时间
role 发送者角色

H5 VssMessageChatData ReplyMsgBean 说明

字段名称 说明
isHistoryMsg 是否历史消息
sendId 发送者id
nickName 回复者昵称
avatar 回复者头像
type 回复类型
content 回复类容
sendTime 发送时间
roleName 发送者角色

H5 VssMessageChatData AtListBean 说明

字段名称 说明
accountId 用户id
nickName 用户昵称

6.升级投屏依赖包,完善投屏暂停、停止、重播、获取进度操作;

V4.1.0 更新时间2020.03.13

更新内容:
1.修复播放器弱网卡死问题;
2.升级直播美颜;
3.完善点播播放器屏幕自动适配;
4.完善直播回复显示;
5.新增SDK混淆配置;
6.优化数据统计;

v4.0.0 更新时间: 2019.09.06

更新内容:
1.配合PC版,H5类型房间服务对SDK进行升级;
2.升级聊天服务;
3.升级文档服务;
4.升级基础服务包,提升SDK稳定性;

v3.4.4 更新时间:2019.08.09

更新内容:
1.修复回放拖动进度条引起的崩溃;

v3.4.3 更新时间:2019.06.11

更新内容:
1.新增文档开关功能;
2.修复看直播全屏异常;
3.优化消息服务;

v3.4.2 更新时间:2019.05.17

更新内容:
1.点播新增倍速播放支持;

版本:v3.4.1 更新时间:2019.05.09

更新内容:
1.更新直播底层库,修复偶现直播崩溃问题;
2.完善demo逻辑;

v3.4.0 更新时间:2019.04.19

更新内容:

  1. 更新互动底层库,支持双流设置;
  2. 新增互动邀请功能;
  3. 完善点播播放器缩放模式支持;

v3.3.1 更新时间:2019.03.16

更新内容:
1.修复9.0以上机型横竖屏切换bug;

v3.3.0 更新时间:2019.02.26

更新内容:
1、功能优化
2、新增被邀请上麦功能

v3.2.3 更新时间:2018.12.21

更新内容:
1、添加踢出、禁言、全员禁言等功能
2、修复已知Bug

v3.2.2 更新时间:2018.11.16

更新内容:
1、修复已知BUG

v3.2.1 更新时间:2018.10.25

更新内容: 1、互动模块拆分 2、修复已知BUG

v3.2.0 更新时间:2018.08.09

更新内容: 1、添加互动模块 2、uiLibs 添加互动模块演示

v3.1.1 更新时间:2018.07.27

更新内容: 1、添加历史问答接口 (看直播、看回放通用)

v3.1.0.2 更新时间:2018.06.26

更新内容: 1、添加回放切换分辨率功能

v3.1.0 更新时间:2018.0604

更新内容: 1、提高SDK编译环境、更换权限申请模式

2、发直播、看直播、看回放添加调度BU

3、添加数据上报

4、bug修复等

v3.0.3 更新时间:2018.01.11

更新内容: 1、防盗链重构

2、自定义消息BUG修改

v3.0.2 更新时间:2017.11.22

更新内容: 1、发直播发送自定义消息 2、防盗链重构

v3.0.1 更新时间:2017.10.24

更新内容: 1、投屏显示 (新增投屏Support) 2、自定义消息

v3.0 更新时间:2017.08.18

更新内容: 1、丰富SDK数据统计 2、SDK降噪与增益 3、更改SDK设置滤镜方案及API 4、性能优化及bug修复

v2.9.0 更新时间:2017.06.12

更新内容:

1、添加https支持 2、添加推流地址调度 3、添加用户信息数据统计 4、添加自定义推流数据、自定义渲染 5、修复部分bug

v2.8.0 更新时间:2017.04.13

更新内容:

1、支持观看VR直播 2、升级依赖库socket.io 0.8.3 release 3、添加部分异常数据兼容

v2.7.1 更新时间:2017.03.31

更新内容:

1、添加白板功能 2、添加文档画笔功能

v2.7.0 更新时间:2017.03.13

更新内容:

1、业务层添加问卷功能 2、部分回调和log优化

v2.6.0 更新时间:2017.03.01

更新内容: 1、业务层和UI层分离

2、UI层增加弹幕功能、表情

3、业务层增加签到、公告

v2.5.4 更新时间:2016.12.30

更新内容: 1、SDK观看回放的次数、人数、时长计入微吼管理中心的数据统计

2、支持对回放发表评论和获取评论

3、支持美颜

4、支持前置摄像头发起直播

5、支持子账号结束自己的活动

6、正在推流的活动不能重复发起

7、支持MP4格式播放

v2.5.0 更新时间:2016.11.01

更新内容:

1、增加子账号功能

2、增加抽奖功能

3、增加获取聊天记录

v2.4.0 更新时间:2016.9.27

更新内容:

1、增加聊天问答功能

2、增加应用包名签名身份验证

3、增加用户体系

4、增加观看语音直播

5、优化已知BUG

v2.3.2 更新时间:2016.8.9

更新内容:

1、优化播放集成方案

v2.3.1 更新时间:2016.8.1

更新内容:

1、多分辨率切换

2、防盗链

3、多种展示方案

自助式网络直播SDK(Android版)

v2.2.2 更新时间:2016.6.3

更新内容:

1、优化横竖屏切换后的全屏观看

v2.2.1 更新时间:2016.5.13

更新内容:

1、新增帧率配置

v2.2.0 更新时间:2016.5.6

更新内容:

1、新增文档演示

2、优化观看体验

大版本更新说明    

4.1.0    

版本更新说明: for 4.1.0

优化H5互动 IVssRtcLister 接口

新增

	/**
         * 互动房间重连
         *
         * @param i  总重连次数
         * @param i1 当前重连次数
         */
	void onReconnect(int i, int i1);

		/**
         * 房间连接状态变化
         *
         * @param room
         * @param status
         */
    void roomStatusMessageChange(Room room, int status);

        /**
         * 房间内成员状态变化
         *
         * @param jsonObject  包含用户音视频状态
         */
    void onRefreshMembers(JSONObject jsonObject);
	
    //有流加入并开始混流,如需开启旁路直播,可在此回调中调用开启
    void onStreamMixed(JSONObject jsonObject);
		/**
         *
         * 房间内人数更新
         */
    void onRefreshMemberState();

移除

 	void onMessage(JSONObject jsonObject);
	
 	void messageChange(int status);

4.0.0    

版本更新说明 for v4.0.0

由于Adoble 计划从2020年底停止支持Flash,PC端新增H5相关播放器实现;

SaaS SDK 新增VSS组件以支持H5相关播放功能;

VSS SDK 为处理业务层操作独立封装的组件库,需添加业务处理所需三方库如下;

    implementation 'com.squareup.retrofit2:retrofit:2.5.0'
    implementation 'com.alibaba:fastjson:1.2.47'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    implementation "io.reactivex.rxjava2:rxjava:2.2.6"
    implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
    implementation 'com.android.support:multidex:1.0.3'
    implementation 'org.ligboy.retrofit2:converter-fastjson-android:2.1.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'

新增权限配置

    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

房间类型识别

初始化房间信息返回的WebinarInfo 中的vss_token 字段为唯一识别依据,如果vss_token 有值且非"" 则代表当前房间为H5房间,需配合PresenterVss实现房间内控制逻辑

UI层变更

关于presenter的变更

新增基于VSS实现的PresenterVss,开发者可根据后端发起房间类型使用情况自行选择同时支持或者单独支持;(当前demo为同时支持Flash及H5)

Presenter为PC端发起直播类型为Flash房间使用;
PresenterVss为PC端发起直播类型为H5房间使用;

BroadcastActiviy

发直播界面内部不再做初始化直播操作,初始化直播信息迁移至demo层 MainActivity 点击事件中:

	//初始化直播房间信息
	public void initPush(Param params, WebinarInfoDataSource.LoadWebinarInfoCallback callback) {
	        String vhallId = getUserId();
	        WebinarInfoRepository repository = WebinarInfoRepository.getInstance(WebinarInfoRemoteDataSource.getInstance());
	        repository.getBroadcastWebinarInfo(params.broId, params.broToken, vhallId, callback);
	    }
	    
	    //横屏发直播按钮点击事件,竖屏同理
	    public void onBroadcastLandspace(View view) {
        Intent intent = new Intent(this, BroadcastActivity.class);
        initPush(param, new WebinarInfoDataSource.LoadWebinarInfoCallback() {
            @Override
            public void onWebinarInfoLoaded(String jsonStr, WebinarInfo webinarInfo) {
                param.vssToken = webinarInfo.vss_token;
                param.vssRoomId = webinarInfo.vss_room_id;
                param.join_id = webinarInfo.join_id;
                param.webinar_id = webinarInfo.webinar_id;
                param.screenOri = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                intent.putExtra("param", param);
                startActivity(intent);
            }

            @Override
            public void onError(int errorCode, String errorMsg) {
                Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_SHORT).show();
            }
        });
    }

ChatContract

由于聊天服务升级,新增问卷实现H5实现方式,对该接口类进行以下调整:

interface ChatView

  1. 移除接口
        void notifyDataChanged(ChatServer.ChatInfo data);
        void notifyDataChanged(int type , List<ChatServer.ChatInfo> list);

  1. 新增接口
		//聊天消息更新
        void notifyDataChangedChat(MessageChatData data);
        //问卷消息更新
        void notifyDataChangedQe(ChatServer.ChatInfo data);
        //聊天列表更新
        void notifyDataChangedChat(int type, List<MessageChatData> list);
        //问卷列表更新
        void notifyDataChangedQe(int type, List<ChatServer.ChatInfo> list);
        Context getContext();

interface ChatPresenter

  • 移除接口
        void onFreshData();
  • 新增接口
		//问卷功能H5实现方式使用
        void showSurvey(String url, String title);

InteractiveContract

互动功能升级,内部实现音频输出自动切换功能,新增互动流状态更新机制;

  • 移除接口
        void setSpeakerphoneOn(boolean on);
  • 新增接口
		//流状态变化,使用详情参考uilibs
        void updateStream(Stream stream);

WatchContract

  • 新增接口
		//新版问卷展示接口
        void showSurvey(String url, String title);
  • 修改接口
		//原入参MessageServer.MsgInfo 更改为MessageLotteryData
        void showLottery(MessageLotteryData data);

新增 interface DocumentViewVss

H5房间内文档服务升级,新增新版文档服务事件回调处理

    interface DocumentViewVss extends BaseView<BasePresenter> {
        void refreshView(com.vhall.document.DocumentView view);

        void switchType(String type);
    }

SDK层变更说明

VhallSDK

新增根据问卷id获取问卷H5地址接口;

    /**
     * @param survey_id  接收到的问卷消息的问卷id
     * @param webinar_id 跟随WebinarInfo返回的webinar_id
     * @param userId     跟随WebinarInfo返回的join_id
     * @return 返回WebView可直接加载的url
     */
    public static String getSurveyUrl(String survey_id, String webinar_id, String userId) {}

WatchLive

修改默认的渲染视图承载容器,弃用原有ContainerLayout,使用系统的RelativeLayout:

    public static class Builder {
    	***
    	private RelativeLayout videoContainer;
    	***
    	public Builder containerLayout(RelativeLayout container) {}
    	}

3.3.0    

版本更新说明 for v3.3.0

使用变更

本次更新保持类名不变,但存在包地址变更情况,如遇类引用不正确情况,删除后重新引入即可;

发直播部分

  1. 使用VHVideoCaptureView 替代原有CameraFilterView进行视图渲染
  2. 使用setCameraDrawMode(VHLivePushFormat.DRAW_MODE_NONE) 替代原有 setDrawMode
  3. 原有setAutioCloseFilterCallback将不再需要,直接删除即可
  4. 切换摄像头cameraview.switchCarmera();或者getBroadcast().changeCamera();
  5. 关闭滤镜cameraview.setFilter(null);
  6. 发直播配置通过VHLivePushConfig进行设置。
  7. 直播事件回调使用VHPlayerListener替代Broadcast.BroadcastEventCallback

看直播部分

  1. 使用VHPlayerListener替代WatchLive.WatchEventCallback
  2. 关于分辨率类型变换:由原有int类型更改为String 类型; 原默认DPI_DEFAULT更改为DPI_SAME (原画);

点播部分

  1. 使用VHPlayerListener替代WatchPlayback.WatchEventCallback
  2. setScaleType 参数改为使用 Constants.VideoMode.DRAW_MODE_NONE

文档变更

发直播部分

SaaS文档 发起直播

准备工作

(3)设置默认发起布局(默认集成滤镜版)

原有设置发起布局需修改为:

<com.vhall.push.VHVideoCaptureView
android:id="@+id/cameraview"
android:layout_width="match_parent"
android:layout_height="match_parent" />

(4)发直播必需调用方法 将不再需要

发直播

Broadcast 实例:

    private Broadcast getBroadcast() {
        if (broadcast == null) {
        //原有视频相关配置改由VHLivePushConfig 进行配置
            VHLivePushConfig config = new VHLivePushConfig(param.pixel_type);
            //可不设置
           config.videoFrameRate = param.videoFrameRate;//帧率
           config.videoBitrate = param.videoBitrate;//码率
            Broadcast.Builder builder = new Broadcast.Builder()
                    .cameraView(mView.getCameraView())
                    .config(config)
                    .callback(new BroadcastEventCallback())// 直播事件回调
                    .chatCallback(new ChatCallback());//使用聊天,加上这个回调
            broadcast = builder.build();
        }

        return broadcast;
    }
直播回调
private class BroadcastEventCallback implements VHPlayerListener {

        @Override
        public void onStateChanged(Constants.State state) {
            switch (state) {
                case START:/** 直播开始*/
                    
                    break;
                case STOP:/** 直播停止*/
                    
                    break;
            }
        }

        @Override
        public void onEvent(int eventCode, String eventMsg) {
            switch (eventCode) {
                case Constants.Event.EVENT_UPLOAD_SPEED:/** 推流速度 eventMsg kbps*/
                    break;
                case Constants.Event.EVENT_NETWORK_UNOBS:/** 网络通畅 */
                    break;
                case Constants.Event.EVENT_NETWORK_OBS:/** 网络异常*/
                    break;
            }
        }

        @Override
        public void onError(int errorCode, int innerErrorCode, String errorMsg) {
            /**直播时发生错误回调,查看错误信息errorCode 错误码,innerErrorCode 内部错误码(一般为0),errorMsg 错误信息*/
        }
    }  
状态码
状态码 描叙
START 直播开始
STOP 直播停止

看直播部分

观看事件回调
    /**
     * 观看过程中事件监听
     */
    private class WatchCallback implements VHPlayerListener {
        @Override
        public void onStateChanged(com.vhall.player.Constants.State state) {
            switch (state) {
                case START://开始播放(缓冲结束)
                    isWatching = true;
                    liveView.showLoading(false);
                    liveView.setPlayPicture(isWatching);
                    break;
                case BUFFER://缓冲中
                    if (isWatching) {
                        liveView.showLoading(true);
                    }
                    break;
                case STOP:
                    isWatching = false;
                    liveView.showLoading(false);
                    liveView.setPlayPicture(isWatching);
                    break;
            }
        }

        @Override
        public void onEvent(int event, String msg) {
            switch (event) {
                case com.vhall.player.Constants.Event.EVENT_DOWNLOAD_SPEED:
                    liveView.setDownSpeed("速率" + msg + "/kbps");
                    break;
                case com.vhall.player.Constants.Event.EVENT_DPI_CHANGED:
                    //分辨率切换
                    Log.i(TAG, msg);
                    break;
                case com.vhall.player.Constants.Event.EVENT_DPI_LIST:
                    //支持的分辨率 msg
                    try {
                        JSONArray array = new JSONArray(msg);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    break;
                case com.vhall.player.Constants.Event.EVENT_VIDEO_SIZE_CHANGED:
                    Log.i(TAG, msg);
                    break;
                case com.vhall.player.Constants.Event.EVENT_STREAM_START:
                	//发起端开始推流(主播开始推流业务处理)
                    
                    break;
                case com.vhall.player.Constants.Event.EVENT_STREAM_STOP:
                	//发起端停止推流(主播停止推流业务处理)
                    
                    break;
            }
        }

        @Override
        public void onError(int errorCode, int innerCode, String msg) {
            switch (errorCode) {
                case com.vhall.player.Constants.ErrorCode.ERROR_CONNECT:
                    /** 连接失败*/
                    break;
                default:
                    watchView.showToast(msg);
            }
        }
    }
状态码
状态码 描叙
START 开始播放
BUFFER 正在缓冲
STOP 停止播放

观看回放部分

观看回放移除exoPlayer封装,采用外部引用方式调用,需要额外添加依赖

api 'com.google.android.exoplayer:exoplayer-core:2.9.1'
api 'com.google.android.exoplayer:exoplayer-hls:2.9.1'
    private class WatchCallback implements VHPlayerListener {

        @Override
        public void onStateChanged(Constants.State state) {
            switch (state) {
                case IDLE://待机状态
                    break;
                case START://播放中
                    break;
                case BUFFER://缓冲中
                    break;
                case STOP://停止(等同PAUSE 可调用resume方法恢复播放)
                    break;
                case END://播放结束(内容播放结束,恢复时默认从初始位置开始)
                    break;
            }
        }

        @Override
        public void onEvent(int event, String msg) {
            switch (event) {
                case Constants.Event.EVENT_DPI_LIST:
                /** 服务器支持的视频分辨率列表*/

                    break;
                case Constants.Event.EVENT_DPI_CHANGED:
                /** 分辨率被切换为msg*/
                    break;
            }
        }

        @Override
        public void onError(int errorCode, int innerErrorCode, String msg) {
            switch (errorCode) {
                case Constants.ErrorCode.ERROR_INIT:
                /** 初始化错误*/

                    break;
                case Constants.ErrorCode.ERROR_INIT_FIRST:
                /** 未初始化*/

                    break;
            }
        }
    }

VideoScaleType 说明

Type 描述
Constants.VideoMode.DRAW_MODE_NONE 铺满全屏
Constants.VideoMode.DRAW_MODE_FIT 等比缩放居中
Constants.VideoMode.DRAW_MODE_FILL 等比拉伸居中
状态码
状态码 描叙
IDLE 待机(PREPARING)
START 开始播放(STATE_READY)
BUFFER 正在缓冲
STOP 停止播放(等同PAUSE 可调用resume方法恢复播放)
END 播放结束
错误码
错误码 描述
-260 未初始化视频信息
-261 初始化视频信息错误

5.0.0    

4.x升级5.0

1.删除vss

BroadcastPresenterVss InteractivePresenterVss DocumentFragmentVss
WatchLivePresenterVss WatchPlaybackPresenterVss 删除这几个类 和删除所有带vss的使用

2.互动方法初始化改变

InteractivePresenter里面 vhRenderView.init(interactive.getEglBase().getEglBaseContext(), null); 改变成 vhRenderView.init(null, null);

3.回放文档

DocumentEventCallback 里面添加

void onEvent(String event, String type, View docView);

void onError(int errorCode, int innerError, String errorMsg);
实例
@Override
public void onEvent(String event, String type, View docView) {
    if (documentView != null) {
        if (event.equals(KEY_OPERATE)) {
            if (type.equals(TYPE_ACTIVE)) {
                documentView.paintH5DocView(docView);
 } else if (type.equals(TYPE_SWITCHOFF)) {
                documentView.showType(2);
 } else if (type.equals(TYPE_SWITCHON)) {
                documentView.showType(3);
 }
        }
    }
}
@Override
public void onError(int errorCode, int innerError, String errorMsg) {
    switch (errorCode) {
        case ERROR_CONNECT://文档服务链接错误
 case ERROR_SEND://文档信息发送错误,演示端生效
 break;
 case ERROR_DOC_INFO://文档加载错误
 try {
                JSONObject obj = new JSONObject(errorMsg);
 String msg = obj.optString("msg");
 String cid = obj.optString("cid");

 } catch (JSONException e) {
                e.printStackTrace();
 }
            break;
 default:
            break;
 }
}

详情参考demo

4.看直播文档

MessageServer.Callback里面添加
//EVENT_SHOWH5DOC 展示或隐藏 h5文档

case MessageServer.EVENT_SHOWH5DOC:
    if (documentView!=null){
        if (messageInfo.watchType==1){
            documentView.showType(3);
 }else {
            documentView.showType(2);
 }
    }
    break;
//给h5文档添加正在演示的view

case MessageServer.EVENT_PAINTH5DOC:
    if (documentView!=null){
        documentView.paintH5DocView(messageInfo.h5DocView);
 }
    break;
	```
具体 文档演示可参考 DocumentFragment
5.看直播互动
看直播收到互动邀请 

//超时应该传3
//1同意 2拒绝 3 超时不处理 invitedDialog.setNegativeOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mPresenter.replyInvite(3); invitedDialog.dismiss(); //邀请超时上麦信息 } });


6.注意事项
方法名和其他提示优化
 onDestroy

sdk只支持在当前包名的主进程中运行。请勿在其他进程中调用。且不支持模拟器