RTC SDK Android版开发指南

版本V3.1)

1、修订记录

*修订日期**描述**作者**版本号*
2019.6.5初始文档王龙渊3.0.0
2021.1.15 王龙渊3.1.0
2023.3.6 王龙渊3.2.0

2、概述

RTC SDK提供人与人实时沟通协作过程中需要用到的所有基本能力,涵盖了网络会议系统、IM即时通讯系统及直播系统三大类终端产品音视频通讯的主要功能。

RTC SDK由业界资深工程师精心打造,稳定可靠,第三方团队拿来就能用,不必自己去造“轮子”,从而降低了第三方团队的技术风险,减少了项目的开发投入,尤其是能大幅缩短第三方团队开发具有多方音视频+数据协作能力的App/Web应用的时间。

RTC SDK可用于几乎所有行业,很多业务场景中需要用到人与人实时沟通与协作的能力,而类似QQ,微信或会议系统这种通用沟通工具又不能直接使用或不能满足功能,这种情况下,RTC SDK就是您最好的选择。市场调研表明,RTC SDK在医疗、教育、金融、能源、交通等各个领域,都有巨大的市场需求。

2.1、简介

RTC SDK 为移动、桌面和互联网应用提供一个完善的音视频及数据互动开发框架,屏蔽掉互动系统的复杂细节,对外提供较为简洁的 API 接口,方便第三方应用快速集成互动功能。

RTC SDK Android版提供如下功能:

2.2、面向读者

本指南是提供给具有一定的Android编程经验和了解面向对象概念的产品经理及程序员使用,Open-AVD SDK已很好的封装了音视频相关的底层技术细节,因而读者不需要具备音视频开发方面的经验。

2.3、获取SDK Demo

公司的github网址(https://github.com/3tee)上会提供各类基于RTC SDK的实例 Demo,包括基本音视频能力Demo。

2.4、技术支持

您在使用本SDK的过程中,遇到任何困难,请与我们联系,我们将热忱为你提供帮助。你可以通过如下方式与我们取得联系。

Ø 技术支持工程师: 186XXXXXXXX

3、编写说明

本指南编写目的是为了帮助使用RTC SDK的用户快速搭建SDK的开发环境、熟悉开发流程、掌握SDK开发功能接口而编写的。

本指南基于Android studio IDE开发,导入baseVideo Demo进行最简单的音视频能力进行编写,如果需要更多的功能,请参阅SDK API接口开发手册。

4、环境准备

4.1.1、开发工具Android studio

本指南开发工具采用Android studio 2023.1.1 Patch 2,build gradle版本为7.2.0,开发者可自行根据本地版本进行配置,也可按照Android studio编译提示自动完成相应工具的安装;若使用其他版本Android studio 在界面上会有一定的出入,功能类似,开发者可自行查找处理方法。

Android studio官方下载地址:https://developer.android.google.cn/studio/index.html

4.1.2、SDK Demo工程导入

2.3中下载的SDK Demo,通过下图所示导入工程:

选择菜单 File -> Open 打开Demo所在目录,等待工程自动编译完成。

4.1.2jpg

img

4.1.3、SDK Demo工程运行

a、等待build gradle自动编译完成即可运行工程,编译环境不一样时会提示安装相关工具(Android SDK、build tools、build gradle等),按照提示点击链接自动安装即可

img

b、 编译完成后Shift+F10或点击运行图标在真机上运行,注意:模拟器上运行时部分音视频功能可能会不可用!

img

c、

img

 

4.1.4、 SDK Demo工程结构以及组织方式

a、 libs目录导入avd_sdk.aar包

img

b、 build.gradle文件添加avd_sdk.aar包依赖

img

c、 build.gradle添加AVD SDK所需第三方库依赖(lambda语句支持、gson库等)

Lambda支持:

d、导入Gson和android注解库:

e、

img

f、

img

5、音视频基本功能开发流程

本开发流程包括鉴权、运行时权限、日志配置、配置引擎选项、引擎初始化、创建房间、加入房间、音视频处理、共享屏幕、视频渲染、白板(标注)、扩展功能等,更多详细信息可以参阅SDK API接口开发手册或咨询我们技术支持工程师。

5.1、鉴权

针对正式客户及测试客户,会提供一对有效的Access Key(默认值demo_access) 和Secret Key(默认值demo_secret), 这对密钥可以产生访问令牌(24小时有效),用于生成会议房间号,及加房间等操作。注:初始化引擎鉴权失败会报401错误!

a、 服务器地址,依据当前web服务协议(http,https)指定对应的端口:

b、 在初始化引擎时需要传入服务器地址和key,引擎初始化将会在后续详细讲解如下所示:

AVDEngine.instance().init(context, listener, serverURI, accessKey, secretKey);

5.2、运行时权限

app的build.gradle里把targetSdkVersion版本设为23(包括23)及其以上时,网络访问、SD卡读写权限、摄像头、录音权限需要动态获取,在此强烈推荐将targetSdkVersion版本设置为23以上(Android 6.0+),避免Android某些功能受限和导入第三方库版本报错。RTC SDK涉及到的权限如下表所示:

权限名称说明
android.permission.INTERNET网络权限
android.permission.CAMERA摄像头权限(不开启视频通话将不可用)
android.permission.RECORD_AUDIO录音权限(不开启语音通话将不可用)
android.permission.READ_PHONE_STATE用于获取设备唯一的标识符IMEI用于设备绑定
android.permission.ACCESS_NETWORK_STATE网络状态用于会议控制
android.permission.WRITE_EXTERNAL_STORAGE日志写操作(不开启日志功能将不可用)
android.permission.READ_EXTERNAL_STORAGE日志读操作

5.3、日志系统

在初始化引擎前配置日志,用于记录SDK运行状态,方便问题排查和运行流程追踪。包括的内容有日志路径配置、日志等级、日志时间标签格式、媒体统计等,日志参数params是一个拼接的字符串,参数之间用空格符号隔开,参数含义如下:

- debug:调试模式,建议开启。

- verbose:日志等级,支持的日志等级有verbose详细、info信息、warning报警、error错误,日志等级由低到高,等级越低日志输出越详细,建议测试阶段使用verbose等级,正式发版时使用info或更高等级,且需要应用层定期清理日志,避免占用较多空间。

- stats:将媒体统计信息(包含每路视频和音频的分辨率、帧率、码流等信息)输出到日志。

- append:将此次加会的日志拼接到上次离会的日志后面打印,初始化引擎后均会生成一个新的日志(前提是SDK引擎已释放)。

- realtstamp:每条日志的时间戳使用当前时间格式 [月-日 小时:分钟:秒]。

代码示例如下:

5.3.1、获取日志统计

调用Tlog类的方法startLogTrace(String param, AVDEngine.ILogListener listener) 用于获取打印的日志信息,param配置过滤日志(等级、时间戳格式、调试信息等),listener 日志回调监听。

开始采集日志:

停止采集日志:

Tlog.stopLogTrace();

5.4、配置引擎选项

用于控制会议中音视频编解码格式、音频采集回音消除、优先使用的分辨率帧率格式、数据通道格式(UDP/TCP)等,使用 AVDEngine.instance().setOption(AVDEngine.Option.[选项], [传入的值]) 配置引擎选项,示例如下:

5.5、引擎初始化(AVDEngine)

引擎初始化是一个异步操作,引擎初始化是否成功需要判断返回值,并在回调里对状态值做判断,为0(ErrorCode.AVD_OK)代表初始化成功,回调为0后才能做后续的入会操作,初始化方法AVDEngine.instance().init(Context context, AVDEngine.Listener listener, String severuri, String appkey, String secretkey)。

参数含义如下:

- context:建议使用application的上下文

- listener:初始化引擎回调

- severuri:服务器地址

-appkey:鉴权accessKey

-secretkey:鉴权secretKey

初始化失败错误码如下:

- 1014:网络错误,需要排查传入的服务器地址是否正确,设备网络是否正常连接,网络质量是否良好,服务器端部署是否正常。

- 401:传入的密匙Access Key 和Secret Key错误。

- 405:服务器授权过期,请联系我方续期。

- 436:AVD SDK版本和服务器版本不适配,请联系我方对服务器做适配调整。

- 1008:初始化引擎传入的上下文context无效(为空或未传)。

代码示例如下:

5.6、创建房间(Room)

5.5中初始化引擎回调成功后才能执行创建房间操作,创建房间是异步回调过程,需要做返回值判断,返回值为0创建成功,返回值为4000540003房间已存在。

 

5.7、加入房间(Room)

5.5中初始化引擎回调成功后且房间存在才能执行加入房间操作,如果未初始化引擎加入房间则获取房间实例返回为空,如果房间不存在加入该房间会返回404房间不存在错误。加入房间前可以配置房间参数,加入房间是异步回调,需要在回调里判断是否成功(0代表加入成功),必须要设置DTLS音视频加密选项才能加房间,room.enableStats(true)使能媒体统计后5.3节配置的媒体统计日志stats才会生效,加入房间流程如下:

A. 判断引擎是否初始化

B. 获取房间并设置房间参数(非必选项,可按照需求设置)

C. 创建用户并加入房间

5.8、音频视频处理(MVideo、MAudio)

本小节涉及音频和视频(麦克风和摄像头)开关操作,音视频的订阅,音视频的渲染,状态查询等功能。使用前需要先调用public static MVideo getVideo(Room room) 获取MVideo类对象,这里需要传入Room类对象,Room对象通过Room.obtain(roomId)获取。打开/关闭摄像头(或麦克风)后,其他用户会收到“发布/停止发布”回调通知,在回调通知里做“订阅/取消订阅”操作,而后会触发“订阅/取消订阅”回调通知,判断“订阅”成功后才能进一步“渲染视频”。

A. 音视频的开关操作(发布/停止发布音视频)

开的状态对应着开启摄像头(或麦克风)采集并发布视频(或音频)这样一个连贯的动作,同样的关的状态对应着停止摄像头(或麦克风)采集并停止发布视频(或音频)动作,开关操作均是异步方式回调。其中预览本地视频功能只会开启摄像头采集并预览,而不会将视频发布出去。开启摄像头会触发MVideo.Listener回调onPublishLocalResult,关闭摄像头会触发MVideo.Listener回调onUnpublishLocalResult;音频的开启/停止会相应触发MAudio.Listener回调。

开启摄像头:

public int publishLocalCamera(MVideo.CameraType type)

- MVideo.CameraType 摄像头类型,front 前置摄像头,back 后置摄像头。

- 返回值为0表示发布过程没有报错,具体是否发布成功需要到回调里判断返回值。

关闭摄像头:

public int unpublishLocalCamera()

- 返回值为0表示停止发布过程没有报错,具体是否发布成功需要到回调里判断返回值。

开启麦克风:

public int openMicrophone()

- 返回值为0没有报错,具体是否发布成功需要到回调里判断返回值。

关闭麦克风:

public int closeMicrophone()

- 返回值为0没有报错,具体是否发布成功需要到回调里判断返回值。

设置本地摄像头使用Android的Camera接口类型(Camera1或者Camera2):

public static void enableCamera2API(boolean enable)

- enable 为true代表使用Camera2,false使用Camera1接口,默认使用Camera2(推

荐)

查询本地摄像头是否已开启:

public boolean ispublishedLocalCamera()

- 返回值为0没有报错,具体是否发布成功需要到回调里判断返回值。

查询指定类型摄像头是否已发布:

public boolean isCameraPublished(MVideo.CameraType type)

- MVideo.CameraType 摄像头类型,front 前置摄像头,back 后置摄像头。

查询指定设备id的摄像头是否已发布:

public boolean isCameraPublished(String deviceId)

- deviceId 设备id,发布的音视频均由设备id标识。

设置发布视频的码流大小:

public int setVideoBitrate(String deviceId, int minBitrateBps, int maxBitrateBps)

- deviceId 设备id,发布的音视频均由设备id标识。

- minBitrateBps 最小码流,单位(bps)

- maxBitrateBps最大码流,单位(bps)

B. 订阅/取消订阅音视频

只能订阅已发布且非自己的视频(摄像头或共享屏幕视频),可以在“发布/停止发布”视频回调中动态去订阅,或者订阅已发布视频列表(public List<MVideo.Camera> getPublishedCameras()接口获取)中的设备。音频的订阅和取消订阅默认由SDK底层自动完成,其他参会者开启音频时自动订阅,其他参会者关闭音频时取消订阅,如果需要手动操作需要关闭自动订阅音频 room.setOption(Room.Option.ro_audio_auto_subscribe, "false")

订阅视频会触发MVideo.Listener回调onSubscribeResult,取消订阅视频会触发MVideo.Listener回调onUnsubscribeResult;音频的订阅/取消订阅相应触发MAudio.Listener回调。

订阅视频:

public int subscribe(String deviceId)

- deviceId 要订阅的设备id号

- 返回值为0没有报错,具体是否发布成功需要到回调里判断返回值。

订阅视频并传入视频的期望分辨率等级,分辨率等级分为低中高3个等级,高等级代表的分辨率是发布的分辨率,中等级代表高分辨率宽高的一半,同理低等级是中等级的一半;需要注意的是分辨率宽高不是严格按照一半来计算,涉及到16位对齐以及弱网情况下服务器自动调低分辨率的情况;如果有多人使用该接口订阅视频,则最终拿到的视频分辨率按照所有订阅用户中等级最高的视频:

public int subscribeWithVideoQuality(String deviceId, VideoOptions.VideoQuality quality)

- deviceId 要订阅的设备id号

- quality 订阅该视频的分辨率等级:quality_low 低、quality_normal 中、quality_high 高。

取消订阅视频:

public int unsubscribe(String deviceId)

- deviceId 要取消订阅的设备id号

- 返回值为0没有报错,具体是否发布成功需要到回调里判断返回值。

订阅音频:

public int subscribe(String userId)

- userId要订阅的用户id号

- 返回值为0没有报错,具体是否发布成功需要到回调里判断返回值。

取消订阅音频:

public int unsubscribe(String userId)

- deviceId 要取消订阅的用户id号

- 返回值为0没有报错,具体是否发布成功需要到回调里判断返回值。

C. 渲染视频

订阅视频异步回调成功后才能做视频渲染(视频显示到窗口)操作,同一路视频可以渲染到多个窗口显示,渲染的视频窗口可以基于SDK tee3\webrtc\TextureViewRenderer.class布局做自定义布局开发,TextureViewRenderer布局需要绑定\cn\tee3\avd\VideoRenderer.class类对象,渲染的时候使用VideoRenderer作为载体来加载视频,且能通过VideoRenderer设置第一帧视频到达回调通知,自定义视频窗口可以参考demo里T3VideoView布局代码具体实现。

创建VideoRenderer并绑定TextureViewRenderer布局:

VideoRenderer mRenderer = new VideoRenderer(textureViewRenderer);

- textureViewRendererTextureViewRenderer布局对象

判断是否窗口已渲染过视频,通过判空videoId 判断是否正在显示视频:

String videoId = mRenderer.getVideoId()

渲染视频:

public int attachRender(String deviceId, VideoRenderer render)

- deviceId 要显示的视频id号(设备id号)

- render 视频窗口

取消渲染视频:

public int detachRender(VideoRenderer render)

- render 视频窗口

D. 预览本地摄像头视频不发布视频

public int previewLocalCamera(CameraType type, VideoRenderer render)

- type 摄像头类型,设置前置或后置。

- render 视频窗口

E. 已发布视频列表查询,已订阅视频列表查询

已发布视频列表:

public List<Camera> getPublishedCameras()

- 返回已发布的摄像头列表

已订阅视频列表:

public List getSubscribedCameras()

- 返回已订阅视频列表

F. 将发布或预览的视频停顿在当前发布的这一帧画面:

停顿:

取消停顿:

G. 音频外放和听筒之间切换:

可以选择设备扬声器或者听筒作为会议音频播放设备,mAudio.setHandFree(true)使用扬声器外放播放声音,同理设置为false使用听筒播放声音。

H. 麦克风静音功能:

将麦克风静音,但音频流还是会发布到房间里。

静音:mAudio.muteMicrophone()

取消静音:mAudio.unmuteMicrophone()

I. 扬声器静音功能:

将扬声器静音,但音频流还是会发布到房间里。

静音:mAudio.muteSpeaker()

取消静音:mAudio.unmuteSpeaker()

J. 设置麦克风采集数据源:

部分机顶盒、TV、华为Pad等Android定制设备不支持通话音频AudioSource.VOICE_COMMUNICATION(SDK默认为该音频源)功能,会出现会议里听不到声音问题,此时需要先于打开麦克风之前调整麦克风数据源为非通话音频(AudioSource.MIC),示例代码如下:

WebRtcAudioRecord.setDefaultAudioSource(MediaRecorder.AudioSource.MIC);

5.9、共享屏幕(MScreen)

共享屏幕使用MScreen类进行操作,包括有共享屏幕的发布、订阅/取消订阅、渲染等内容。

A. 发布共享屏幕视频

发布共享屏幕需要初始化Android共享屏幕接口,重写Activity方法onActivityResult,发送intent启动共享屏幕服务,示例代码如下所示:

B. 订阅/取消订阅

房间里发布共享屏幕会触发MScreen.Listener类回调方法onPublishScreenNotify,在此方法中判断非自己的共享屏幕时可做订阅操作。同MVideo类一样,订阅后会触发onSubscribeResult回调,取消订阅会触发onUnSubscribeResult回调。

订阅共享屏幕视频:

public int subscribe(String deviceId)

- deviceId 要订阅的设备id号

- 返回值为0没有报错,具体是否发布成功需要到回调里判断返回值。

取消订阅共享屏幕视频:

public int unSubscribe(String deviceId)

- deviceId 要订阅的设备id号

- 返回值为0没有报错,具体是否发布成功需要到回调里判断返回值。

C. 渲染

同摄像头一样共享屏幕使用MVideo 类进行渲染操作,具体可查阅5.8小节部分文档。

5.10、参会者管理(MUserManager、Room、User)

用户管理类MUSerManager用于监听房间中用户加会操作、离会离会、用户调用updateUser更新用户名称/用户标签数据、用户摄像头/麦克风开关状态变更。MUserManager类对外提供updateUser更新用户数据接口,获取用户对象User实例,获取房间参会者列表,关联用户User和设备等作用。

房间管理类Room提供将会议中参会者踢出房间功能,以下对相应部分分别加以说明。

用户对象由User类实例加以描述,包含的成员变量有:用户ID号userId、用户名userName、用户数据(String类型自定义数据,用于描述标记等等)userData、用户是有数据userAgent、用户状态(用于记录该用户摄像头麦克风设备以及设备状态)status

A. 用户加会、离会回调通知:

当用户调用Roomjoin(User user, String password, Room.JoinResultListener joinresult)加入房间和destoryRoom(Room room) 离开房间成功时,其他参会者会分别触发void onUserJoinNotify(User user)用户加入房间和void onUserLeaveNotify(User user)、public void onUserLeaveNotify(int reason, User user)用户离开房间回调通知。

其中用户离会reason状态0代表正常离会,非0状态代表异常离会,状态码如下表所示:

状态码code离会原因
0正常离会,用户自己调用destoryRoom离会
804被相同userId用户入会挤下线
807UDP媒体通道建立超时,常见于join加会时。
808该用户被其他用户踢出房间
809服务没有授权用户入会
810用户关闭房间时,所有用户被踢出房间。
811服务器维护是关闭房间
812和服务器之间心跳超时,异常离会。例如:网络中断、APP被系统或后台杀掉。

回调接口:

B. 更新用户数据:

调用接口更新用户数据时,其他参会者会收到用户数据更新通知。

接口定义:public int updateUser(User user) ,返回0代表成功。

C. 摄像头/麦克风状态变更:

当用户摄像头或麦克风状态改变时(打开、关闭)会收到状态变更回调通知。

D. 参会者列表管理:

注:获取参会者列表和参会者人数均不包含自己

获取参会者列表: public List<User> getParticipants(int begindex, int ncount)

- begindex:获取的起始位置

- ncount:获取的长度

获取参会者人数:public int getParticipantsCount()

示例:

MUserManager mUserManager = MUserManager.getUserManager(room); // 获取MUserManager实例

mUserManager.getParticipants(0, mUserManager.getParticipantsCount()); // 获取房间参会者列表,不包含自己。

E. 用户实例User获取和User相关操作接口介绍:

- 获取用户Id对应的该用户User实例:public User getUser(String userId)

- 获取自己的用户User实例:public User getSelfUser()

- 获取自己的用户id:public String getSelfUserId()

F. Room将用户踢出房间:

userId用户踢出房间,如果正常返回0时,其他用户会收到该用户离会通知,离会原因reason808

踢出用户接口:public int kickoutUser(int reason, String userId)

- reason:踢出原因

- userI:用户id号

- 返回int:0代表踢出成功,非0踢出失败。

5.11、语音激励(MAudio)

语音激励用于获取房间里所有参会者语音能量值,排列出最大语音能量值用户,常用于将语音能量值最大的用户标记为发言者,将该用户视频投影到大屏上显示。语音激励功能需要房间Room开启语音激励功能,创建房间时房间信息RoomInfo变量enableVoiceActivated置位或在后台box配置当前房间开启语音激励功能。语音能量开启后会每隔1秒时间将房间用户语音能量列表回调(public void onAudioLevelMonitorNotify(AudioInfo info))给用户,语音能量值0~9,数值越大音量越高。

注:语音激励需要用户自己做缓存消除抖动处理,避免计算出来的最大语音能量用户(当前发言者)频繁抖动切换,在大屏上体现出来的闪屏问题(切换用户视频导致)。

A. 接口:

查询是否已打开语音激励功能(本地非服务器):public boolean ismonitorAudioLevel()

- 返回booleanture语音激励已开启,false已关闭。

开启语音激励功能:public int monitorAudioLevel()

- 返回int0代表成功,非0代表失败。

关闭语音激励功能:public int unmonitorAudioLevel()

- 返回int0代表成功,非0代表失败。

B. 回调:

C. 示例代码:

 

5.12、白板(MWhiteboard、WhiteboardView、WhiteboardDrawView、

WhiteboardToolView)

白板布局由“白板底层相对布局容器”和“白板画布”组成,画线时是画在画布上进行展示。白板支持多个用户同时画线,且同一时间只能显示一个白板,如果A先于B用户共享白板,此后B用户共享白板时A用户共享的白板会替换为B用户白板的内容,新用户白板会替换旧用户白板。白板支持缩放(隐藏工具栏后两个手指做缩放操作)操作、横竖屏显示(跟随界面横竖屏动态计算宽高值);白板支持两种显示格式,第一种方式显示的白板宽高比维持原发布白板的宽高比,第二种方式显示的白板铺满整个WhiteboardView布局大小;同时白板还支持背景色透明显示,即“白板底层相对布局容器”和“白板画布”均可设置为透明色,只有白板上画的图形不透明,这种方式方便直接在视频上或视频某一帧图片上作画,适应会议多种场景需求。MWhiteboard类处理会议相关白板逻辑,WhiteboardView是白板布局添加有子布局WhiteboardDrawViewWhiteboardToolViewWhiteboardDrawView处理白板具体画图逻辑,WhiteboardToolView处理工具栏业务逻辑。

A. 白板布局及其初始化:

xml文件中定义该白板布局,并初始化白板。

img

B. 发布和停止发布白板:

查询本地白板是否已发布:public boolean hasLocalWhiteboardShared()

- 返回booleantrue代表已发布,false代表未发布

发布本地白板:public String createLocalWhiteboard(final String title, final String desc, final int color)

- title:指定要发布白板的标题

- desc:设置要发布白板的描述

- color:设置发布白板的画布背景色,其他端会按照该颜色显示白板画布背景色。

- 返回String:返回创建的白板id号

停止本地已发布的白板:public void closeLocalBoard()

C. 白板工具栏:

用于选择白板操作的工具类型,包括画实线、画半透明线、设置画线颜色、设置画线的宽度、画单箭头、橡皮擦、激光笔。点击左边圆形图标控制工具栏的展开和收缩,工具栏展开时才能画线;工具栏收缩情况下不能画线,可以做白板画布的缩放操作。注:当工具栏不符合业务需求时,需要用户自己对WhiteboardToolView自定义布局做修改。

img

D. 白板的两种显示模式(WhiteboardView类管理):

public WhiteboardView setDrawType(DrawType type)

- type:显示模式,KEEP_WIDTH_HEIGHT_RATIO原始宽高比显示,FOLLOW_SETTING_SIZE铺满显示。

E. 白板的透明色配置:

whiteboardView.setWhiteboardColor(Color.TRANSPARENT); // 设置白板画布颜色为透明色

whiteboardView.setBackgroundColor(Color.TRANSPARENT); // 设置白板布局的背景为透明色

F. 设置白板背景图片:

动态设置方式:

可以将图片作为白板背景色上传到服务器(MWhiteboard类的方法public int setBackground(String id, String filePath)),其他端均会收到void onUpdateBoardNotify(MWhiteboard.Whiteboard board)回调通知,白板背景图片http url地址可以通过board._remoteBgUrl来获取,此时就可以把图片显示到WhiteboardView里的ImageView布局上了,或者将WhiteboardView设置为透明色,用户自己定义ImageView置于WhiteboardView布局底层显示该网页图片。

发布白板时指定白板背景图片方式:

在发布白板时指定本地SD卡上图片的路径地址,SDK会自动将该图片上传服务器,其他端在收到发布白板回调通知(public void onShareBoardNotify(MWhiteboard.Whiteboard board))后通过board._remoteBgUrl来获取白板背景图片url,创建白板接口如下:

public String createLocalWhiteboard(String title, String desc, String remoteBgUrl, int color)

- remoteBgUrl:存于设备SD卡上图片存储绝对路径地址

显示背景图片到Whiteboard布局:

5.13、视频渲染窗口(VideoRenderer、TextureViewRenderer)

视频渲染、取消渲染均需要使用VideoRenderer作为载体,实际呈现视频效果是由TextureViewRenderer布局来完成,这里需要VideoRenderer内部绑定TextureViewRenderer(传入TextureViewRenderer布局)。开发过程中用户可以对TextureViewRenderer做进一步封装,自定义布局。除了用于呈现视频功能,还提供第一帧视频到达回调通知和截取视频一帧画面的功能,以下将对这些功能一一说明。

A. VideoRenderer的创建和绑定TextureViewRenderer窗口:

获取布局:TextureViewRenderer textureView = view.findViewById(R.id.textureView);

创建渲染对象并绑定窗口:VideoRenderer mRenderer = new VideoRenderer(textureView);

注:以上两步需要在AVDEngine初始化之后去执行(查询AVDEngine.instance().isWorking()),通常情况下窗口TextureViewRenderer 是在xml布局里定义的,TextureViewRenderer 的构造方法在某些情况下会先于AVDEngine初始化运行。

B. 第一帧视频到达通知:mRenderer.setFirstFrameCallback(callback)

C. 截取视频一帧画面:

调用VideoRenderer类接口,从视频窗口中提取正在显示的视频一帧画面(Bitmap类对象),每调用一次提取接口均会回调一帧画面,需要确保窗口当前渲染过视频且第一帧已到达,否则提取会失败:

public boolean frameSampleCapture(EglRenderer.FrameListener listener)

5.14、透明通道、聊天(Room、MChat)

可以向会议中其他参会者发送透明通道数据(byte序列化数据)或聊天信息(String类型数据),消息类型分为公共广播消息(房间内所有人都能收到)和私有消息(只有指定用户能收到)。

透明通道(Room):

聊天消息(MChat):

5.15、导入/导出音视频数据

只有有导入H264视频编码格式功能(作为单独的一路视频发布出去,等同于摄像头视频),导出包括Camera1接口采集的原始数据NV21格式数据和麦克风采集的原始音频PCM格式数据,以下对相关功能加以说明。

A. 导入H264视频(FakeVideoCapturer类):

导入步骤包括创建虚拟摄像头并配置摄像头参数、创建FakeVideoCapturer对象并设置回调、发布该虚拟摄像头、传入H264每一帧数据。

创建FakeVideoCapturer对象接口:

public static FakeVideoCapturer Create(FourccType fourFormat, boolean isScreen, Listener listener)

- fourFormat:导入的视频格式,这里选择H264(FakeVideoCapturer.FourccType.ft_H264)。

- isScreen:是否是共享屏幕视频,推荐传false。

- listener:回调通知

传入H264H264每一帧数据接口:

public int inputEncodedFrame(long timestamp_ns, int w, int h, byte[] data, int len)

- timestamp_ns:ns时间戳

- w:视频宽

- h:视频高

- data:视频数据

- len:视频数据长度

示例代码:

MVideo.Camera camera = new MVideo.Camera(deviceId, deviceName); // 创建虚拟摄像头设备,传入设备id和设备名称

// 按照要发布的视频实际参数创建要发布摄像头数据:1920*1080分辨率,30帧,H264编码格式。

 

5.16、码流控制

针对使用默认的码流配置视频效果或带宽限制等不符合实际需求的情况,可手动配置码流大小。目前发布的所有视频(本地摄像头、共享屏幕、虚拟摄像头视频)共用一个码流参数,设置的码流大小需要动态根据发布的视频分辨率和路数情况来调整,否则存在码流偏大或偏小问题,会导致视频模糊马赛克等问题。通常情况下640×480分辨率对应码流为500kbps~1000kbps,1280×720分辨率对应码流为1000kbps~1500kbps,1920×1080分辨率对应码流为2000kbps~4000kbps。

注:发布本地视频(摄像头或共享屏幕)回调后再配置码流才会生效。

接口:

public int setVideoBitrate(String deviceId, int minBitrateBps, int maxBitrateBps)

- deviceId:设备id,这里可传空字符串””。

- minBitrateBps:最小码流(bps)

- maxBitrateBps:最大码流(bps)

示例代码如下:

5.17、媒体统计(Room、NetworkStats.MediaStats)

用于获取每一路订阅或自己发布的音频和视频信息,分为两种:一种是获取当前已订阅视频或自己已发布的视频分辨率、帧率、码流、丢包率等信息;另一种是将媒体统计信息打印到日志文件里,方便异常情况(视频黑屏、视频卡顿等)排查错误。需要注意的是媒体统计的支持需要Room开启媒体统计功能room.enableStats(true),以下对这两种方式分别加以说明。

A. 获取视频媒体信息:

B. 将媒体信息打印到日志里:

除了需要房间开启媒体统计功能外还需要日志等级设置为verbose等级并设置日志stats状态才会每间隔一秒的时间往日志里打印媒体统计信息,媒体统计信息日志如下图所示:

5.18、回调通知

SDK涉及到的回调接口有引擎AVDEngine回调、房间管理Room回调、视频管理MVideo回调、共享屏幕MScreen回调、用户管理MUserManager回调、白板(标注)MWhiteboard回调、音频管理MAudio回调、聊天功能MChat回调、视频渲染VideoRenderer第一帧视频到达通知。添加回调通过类实例对象方法setListener设置,例如:room.setListenermVideo.setListener 等。回调方法参数列表含义可以查阅SDK API文档。

A. AVDEngine引擎回调:

包括AVDEngine.Listener回调和音频采集PCM原始数据回调

设置AVDEngine.Listener回调:AVDEngine.instance().init(context, listener, serverURI, accessKey, secretKey)

设置音频采集PCM原始数据回调:AVDEngine.instance().setAudioFrameCallback(JavaAudioDeviceModule.SamplesReadyCallback callback)

AVDEngine.Listener:

B. 房间管理Room回调:

Room回调分为加入房间回调通知(JoinResultListener)和房间信息类回调(Listener)两种。

- 设置JoinResultListener回调:room.join(user, "", joinResultListener)

- 设置Listener回调:room.setListener(listener)

C. 视频管理MVideo回调:

分为视频状态回调,本地摄像头事件监听,本地摄像头使用Camera1接口时摄像头原始数据回调。

- 视频状态:public boolean ***\*setListener\****(Listener listener)

- 本地摄像头事件:public static void ***\*setCameraEventListener\****(CameraVideoCapturer.Ca

meraEventsHandler listener)

- 本地摄像头使用Camera1接口时摄像头原始数据:public void ***\*setPreviewCallback\****(NativeCap

turerObserver.PreviewCallback callback)

D. 共享屏幕MScreen回调:

共享屏幕状态更新,发布/停止发布共享屏幕,订阅/取消订阅共享屏幕回调通知。设置回调mScreen.setListener(listener),listener即MScreen.Listener对象。

E. 用户管理MUserManager回调:

示例:mUserManager.setListener(listaner)

F. 白板(标注)MWhiteboard回调:

示例:mWhiteboard.setListener(listaner)

G. 音频管理MAudio回调:

麦克风状态变更(开关、静音等),参会用户开关麦克风,用户语音激励(语音激励功能打开),会议里播放视频流TTS均会触发该回调接口。

示例:mAudio.setListener(listaner)

H. 聊天功能MChat回调:

当会议里其他用户向当前用户发送共有或私有消息时会触发以下回调

示例:mChat.setListener(listaner)

I. 视频渲染VideoRenderer第一帧视频到达通知

监听渲染视频帧到达通知,用于界面上窗口状态展示,例如:没有渲染视频时显示占位图片或用户头像,视频第一帧到达后隐藏占位图片显示视频内容等。

示例:public void setFirstFrameCallback(FirstFrameCallback value)

public void onFirstFrameArrived(VideoRenderer render); // 只在第一帧视频达到时回调一次,取消渲染再次渲

染时会再次回调。

 

5.19、扩展功能

扩展部分包括一些小的功能,包括:闪光灯开关控制(后置摄像头)、后置摄像头变焦(拉近拉远)、摄像头角度错误的矫正(图像是歪的未处于正方向)、设置代理等等。

A. 闪光灯控制:

Android手机目前只有后置摄像头才有闪光灯功能,即如果当前使用MVideo打开的前置摄像头则没有闪光灯功能,需要切换至后置摄像头,打开摄像头功能请查阅5.8小节内容。SDK支持基于Camera1接口和基于Camera2接口两种,可以通过mVideo.enableCamera2API(true)来选择使用Camera2接口,false使用Camera1接口,以下分别对两种接口控制闪光灯加以说明。

- 使用Camera1接口闪光灯控制:

- 使用Camera2接口闪光灯控制:

B. 摄像头变焦控制(拉近拉远):

只有开启的是后置摄像头才有变焦功能,在变焦之前需要判断是否是后置摄像头。

C. 摄像头角度错误的矫正:

只有在部分盒子、TV、外接摄像头等定制设备上才会出现摄像头画面角度错误画面颠倒的情况,此类情况是因为设备问题,读回来的摄像头角度和实际画面角度不匹配导致。此时可以通过SDK预设角度偏移量对摄像头视频做一个角度的补偿旋转到正方向(只支持Camera1接口),具体偏移量角度设置多大需要根据实测来设置,偏移量角度范围0~360度。

实例代码:

mVideo.setCameraOrientationOffset(90) // 逆时针旋转90度

D. AVDEngine代理功能:

代理接口:

public void setProxy(ProxyType type, String ip, int port, String userName, String password)

- type:代理的类型. 目前支持Https代理(音视频数据以TCP发送),Socks5代理(音视频数据以UDP发送,推荐使用该代理方式)。

- ip:代理的IP地址

- port:代理的端口

- userName:代理的认证账户(如果不认证,可传递空字符串)

- password:代理的认证密码(如果不认证,可传递空字符串)

代码示例:

5.20、离开/关闭房间并释放占用资源

离开房间而不释放SDK引擎:

Room.destoryRoom(room)

释放SDK:

 

6、快速开发流程

预先在Application里初始化好AVDEngine,这样可以避免加入会议时初始化引擎占用较多时间导致加房间慢的问题(多增加1秒左右时间)。而后依次执行获取动态权限、设置日志路径、设置引擎选项、初始化引擎(如有必要)、设置房间参数并加入房间、发布音视频、渲染视频等。具体操作步骤可以参考上一章节或参考demo代码具体实现。

7、常见错误处理

常见错误包括:引擎报错、房间报错、网络异常处理、音频问题、视频问题。

A. 引擎报错:

权限报错:

权限说明
android.permission.INTERNET没有网络权限初始化报1014错误
android.permission.ACCESS_NETWORK_STATE没有网络状态访问权限初始化时会报异常崩溃

初始化报错:

错误码说明
401传入的密匙Access Key 和Secret Key错误。
405服务器授权过期,请联系续期。
436AVD SDK版本和服务器版本不适配,请联系我方对服务器做适配调整。
1008初始化引擎传入的上下文context无效(为空或未传)。
1014网络错误,需要排查传入的服务器地址是否正确,设备网络是否正常连接,网络质量是否良好,服务器端部署是否正常。

B. 房间报错:

创建房间报错:

错误码说明
40003/40005重复创建房间,房间已存在。
其他非0错误码非0则是创建失败,这类错误码相对较少,出现时和我方确认解决。

加入房间:

错误码说明
401服务器认证失败
404房间不存在
405服务器授权过期,请联系续期。
1014网络异常,检查设备和服务器整个网络链路是否正常。

离会回调通知:

在以下情况下会收到Room类离会回调通知(void onLeaveIndication(int reason, String fromId)):和服务器中断连接(网络或其他连接异常)、房间内用户关闭房间、被其他用户踢出房间等。

收到离会回调后需要用户做离会操作,退出会议界面并释放房间(destroyRoom),具体被踢出房间原因可以根据回调reason值进行判断并在界面上给予提示,可对照下表排查原因:

特征码离会原因
0正常离会,用户自己调用destoryRoom离会
804被相同userId用户入会挤下线
807UDP媒体通道建立超时,常见于join加会时。
808该用户被其他用户踢出房间
809服务没有授权用户入会
810用户关闭房间时,所有用户被踢出房间。
811服务器维护是关闭房间
812和服务器之间心跳超时,异常离会。例如:网络中断、APP被系统或后台杀掉。

会议中网络异常处理:

在会议过程中网络波动会导致设备和服务器心跳包中断,此时设备会做断线重连操作,会触发Room回调方法void onConnectionStatus(ConnectionStatus status),需要用户对这里的ConnectionStatus 状态做判断。Room.ConnectionStatus.connecting代表正在重连状态,Room.ConnectionStatus.connectFailed代表重连失败,如果是后者重连失败情况下需要用户做离开房间操作,关闭会议界面;重连时间(单位ms)可以设置Room选项Room.Option.ro_room_rejoin_times来设置,不设置默认是重连3次(5秒),”-1”代表一直重连。收到网络正在重连状态时最好在页面给用户一个提示,避免音视频中断影响使用效果。

C. 音频问题:

音频问题常见于Android定制设备(机顶盒、一体机、TV)和部分Pad(华为),现象包括麦克风采集没有声音,此时需要调整麦克风采集数据源,将语音通话音频源修改为麦克风音频源,具体可参考5.8-J章节;杂音、丢字、音色不理想情况可能由于音频采样率不匹配或由于使用软件回音消除不彻底导致,此类问题可以咨询排查。

D. 视频问题:

常见视频问题包括视频黑屏、视频模糊花屏、视频卡顿、视频延时,出现黑屏或卡顿时可加入多个设备验证是发布(编码)问题还是接收视频(解码)这端有问题,例如:如果A发布视频,B看A发布的视频黑屏,C看A视频没有黑屏,则可能是B这端解码有问题导致;同理如果B、C看A发布视频均是黑屏,则可能是A端发布有问题。以下分别对几种情况加以说明:

黑屏:

如果是本地打开视频黑屏则需要排查编码和打开摄像头是否有报错(发布本地摄像头回调result值是否为0、查看日志分析是否有异常);显示其他端的视频黑屏,排查是否视频有正常发布出来,可以加入其他设备进来查看是否黑屏,如果发布端没有问题,则需要排查本地解码视频是否有报错或者当前是否是弱网情况。在网络比较差的情况下,渲染多路视频较大概率出现视频显示黑屏问题,此时需要实时监测视频的媒体统计信息(详见5.15-A),视频帧率恢复正常后再去做渲染操作。

模糊花屏:

视频模糊(晃动镜头的时候会更明显)一般由于码流设置偏低导致,需要将码流适当增加(详见5.15节)。通常情况下640×480分辨率对应码流为500kbps~1000kbps,1280×720分辨率对应码流为1000kbps~1500kbps,1920×1080分辨率对应码流为2000kbps~4000kbps。注:SDK所有发布的视频(摄像头、共享屏幕、虚拟摄像头)共用一个码流配置。例如:发布一路1280×720摄像头视频,码流设置为1200kbps,帧率20帧。此时再发布一路1280×720共享桌面视频,两路720P视频总码流是1200kbps,可能会出现共享屏幕模糊问题,则需要根据发布的视频路数动态设置码流大小。

卡顿:

卡顿通常情况是编解码帧率输出不稳定(编码或解码丢帧)和网络比较差丢包引起,需要查看SDK日志分析丢帧和丢包情况,结合设备CPU占用(CPU占用过大)情况,路由器带宽占用情况,服务器交互日志具体分析。

延时:

码流设置过大导致设备编解码压力增加或者由于网络环境差存在丢包均会出现视频延时问题,此类情况需要结合终端日志和服务器交互日志具体分析。

8、附录

8.1、名词解释

名词解释备注
room房间对象,是实时沟通功能的一个管理单元,房间中会有多个沟通参与者即用户,房间有各种沟通功能,如文字聊天、语音视频等,沟通是基于房间的。不同房间沟通是隔离的 
user用户对象,每个加入到房间的客户端作为一个房间用户,用户将会根据权限和设备情况执行房间中各种沟通功能。用户Id:唯一标示一个房间用户的Id,由应用层来设置。 

8.2 、错误码

*错误码**描述**出现原因**处理方法**备注*
1000基本错误一些基本的无法细分错误提供sdk日志排查 
1001参数错误参数是否填错检查传参 
1002没有初始化没有初始化引擎检查是否初始化 
1003已经初始化sdk内部逻辑通知对应用层无影响
1004未实现该方法未实现反馈客户端开发排查 
1005空指针代码逻辑反馈客户端开发排查 
1006未知异常代码逻辑反馈客户端开发排查 
1007内存越界代码逻辑反馈客户端开发排查 
1008非法参数代码逻辑反馈客户端开发排查 
1009操作无效代码逻辑反馈客户端开发排查 
     
1011对象没找到代码逻辑反馈客户端开发排查 
     
1014超时可能网络异常,或者服务器访问不了先检查网络或端口是否有问题 
1015对象错误状态代码逻辑反馈客户端开发排查 
1016网络错误1在媒体通道还未连接上时去调用发布视频导致错误2或者初始化引擎报错1等媒体通道连接上后再做音视频的操作2检查是否认证失败 
1017没有token未用
1018图像转换失败主要是导入视频时报错检测导入数据是否有问题 
1019缓存不够使用C接口返回错误c++到c转换数据时长度不够,反馈c++开发 
1020设备被占用比如摄像头已经被其他应用正在使用,当前sdk无法使用。先检查是哪个应用正常使用摄像头,先关闭掉后sdk再使用 
1021操作以及完成sdk内部通知,不影响应用层
     
1025函数未认证服务端验证是否有使用权限找客户端排查 
1026 未用
1027mcu服务器连接失败由于媒体服务未连上产生的发布视频错误检查下媒体链接是否有问题 
1028视频不支持的分辨率   
1029房间已经关闭未用
1030媒体流连接超时媒体服务连接超时检查下媒体链接是否有问题 
1031集群模式下获取mcu失败初始化引擎返回的错误  
1032房间信令连接失败连接服务端的信令通道失败检查自己网络或者服务器是否可连 
1033房间数据连接失败未用
1034等待数据抓图时,数据还没有重复调用即可 
     
     
     
*错误码**描述**出现原因**处理方法**备注*
401初始化引擎时未认证认证的token或者key有问题检查是否token或key使用错误,如果无误则需要找服务端排查 
402客户端重复加入房间内部逻辑,不会返回到应用层不影响应用层
     
404房间号不存在房间号在服务器是不存在的客户端先确认房间号,如果房间号正确,需要服务端排查 
405license 不够用户数超过最大license 个数找我们技术支持申请license 
406-422   未用到,可以不用关心
     
434  未出现过,可以不用关心
445主持人不在房间主要使用token初始化引擎的用户可以让主持人先加入房间或者修改后台设置 
     
503-508  未出现过,可以不用关心
     
601  未出现过,可以不用关心
602  未出现过,可以不用关心
     
612该mcu服务器没有找到指定的房间服务端的配置或者逻辑问题需要服务端排查 
613  未出现过,可以不用关心
     
700  未出现过,可以不用关心
701  未出现过,可以不用关心
     
800  未用到,可以不用关心
801  未用到,可以不用关心
802发布视频或音频时重复的设备id没有出现过如果出现反馈日志,需要检查sdk逻辑 
803房间错误的token没有出现过如果出现,需要sdk和服务端排查下 
804使用相同id加入房间,已经在房间的那个相同用户id会被踢出房间。多个用户使用重复id的,则会被服务器踢出重复的。保证每个加入房间用户id唯一 
805 暂未用
806 暂未用
807sdk无法链接上服务端的媒体通道,而被服务器踢出房间可能网络或者服务端端口配置问题,sdk无法连上服务端的媒体通道检查网络或者检查服务器端口 
808被其他用户踢出房间某个用户通过sdk接口把自己踢出房间了 
809    
810房间被关闭某个用户通过sdk接口关闭了房间 
811服务器关闭可能服务器正在重启等服务器重启成功后重连 
812自己收到其他用户连接超时后被踢出房间的通知某个用户可能网络异常导致无法链接服务器而被服务器踢出房间了忽略
     
815房间被关闭了可能是其他用户通过rest接口关闭了房间这个是正常逻辑,不影响应用层