Android (Java)
本文以实现音视频通话为例,结合 SDK API 和示例代码,介绍如何快速接入。
- 如需查看完整 SDK API 列表,请点击左侧导航 SDK API 部分。
- [注意] Android SDK 不支持模拟器调试,请使用真机。
导入SDK
自动集成
在项目的 app/build.gradle 文件的 dependencies
部分,配置:
dependencies {
......
implementation 'video.pano:rtc:x.y.z' // x.y.z 表示 SDK 版本号,例如:1.5.5
/* video.pano:rtc 全功能
video.pano:rtc-lite-whiteboard 包含音视频和基础白板等功能(白板内不支持 ExoPlayer 和 PDF 功能)
video.pano:rtc-simple 音视频(不包含白板等功能)
video.pano:rtc-audio 纯音频
*/
}
请到 Maven仓库 查看可自动集成的 SDK 最新版本号。
手工集成
- 请到 下载中心 下载 Android SDK压缩包。
- 将其解压,获取 Android 目录下的 panortc.aar,将 panortc.aar 放到你的项目中。
- 在 Module 的 build.gradle - dependencies 中,添加:
implementation fileTree(dir: 'sdk_path', include: ['*.aar'])
请将其中 sdk_path 替换为可以定位到 panortc.aar 的实际路径。
防止混淆
如果你的项目需要混淆,请在 proguard-rules.pro 文件中配置以下内容防止混淆 Pano SDK。
-keep class com.pano.** {*;}
-keep class video.pano.** {*;}
如果将 Pano SDK 混淆,可能会出现报错,例如:
java.lang.ClassNotFoundException: Didn't find class"video.pano..." on path: DexPathList[[zip file "..."] ...
配置权限
请在 AndroidManifest.xml 文件的 manifest
根元素内添加必要的权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 音频需要此权限 -->
<uses-permission android:name="android.permission.CAMERA" /> <!-- 视频需要此权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <!-- 音频需要此权限 -->
<uses-feature android:name="android.hardware.camera" /> <!-- 视频需要此配置 -->
<uses-feature android:name="android.hardware.camera.autofocus" /> <!-- 视频需要此配置 -->
当设备系统版本为 Android 6.0 及以上,并且 targetSdkVersion >= 23 时,还需要在运行时请求权限。
如果同时需要音频和视频权限,可以通过 RtcEngine.checkPermission(Context context)
方法获取缺失的权限(需要申请的权限),具体用法可以参考:
注册通知
通过实现 RtcEngineCallback
接口的回调方法来处理 Pano SDK 返回的通知。
注意:必须尽早注册通知,以免错过某些通知。
public class MyPanoEngineCallback implements RtcEngineCallback {
public void onChannelJoinConfirm(Constants.QResult result) {
// 处理 加入频道 的结果
}
public void onChannelLeaveIndication(Constants.QResult result) {
// 处理 被动离开频道 的结果
}
......
}
- 完整的回调方法列表与介绍,请查看 RtcEngineCallback接口。
- 为结合介绍 SDK 接口,本文会提供部分 示例代码 中的片段,请开发者注意根据实际情况调整。
初始化
通过 RtcEngine
的 create
方法初始化实例。
public static final String APPID = "..."; // 替换为你的应用ID
private RtcEngine mRtcEngine;
// PanoEngineCallback 为"注册通知"章节中创建的 RtcEngineCallback 接口的子类示例
private PanoEngineCallback mRtcCallback = new PanoEngineCallback();
RtcEngineConfig engineConfig = new RtcEngineConfig();
engineConfig.appId = APPID; // 应用ID
engineConfig.context = getApplicationContext();
engineConfig.callback = mRtcCallback; // RtcEngineCallback 回调对象
mRtcEngine = RtcEngine.create(engineConfig);
- 请登录 Pano控制台,点击左侧导航 应用管理 ,查看应用ID(即
appId
)。- 建议在 app 的
Application
的onCreate
中进行初始化。只有当初始化完成后,才可以使用RtcView
显示视频画面。如果尚未初始化就开始在 UI 中使用RtcView
将出现报错。
本地预览
通过 RtcEngine
实例的 setLocalVideoRender
方法设置本地画布用来显示自己的画面,startPreview
方法开启本地摄像头预览。
/* 画布类型为 com.pano.rtc.api.RtcView,可以动态创建,也可以通过 XML 文件静态配置(建议静态配置会比较简单)。
[注意] RtcView 外面必须包裹一层 Layout (建议使用 ConstraintLayout),
而且 RtcView 自身宽高需要设置为 wrap_content,请参考:
https://github.com/PanoVideo/video-call-samples/blob/master/Android/BasicVideoCall/app/src/main/res/layout/content_call.xml#L11
*/
mLocalView = new RtcView(this); // 动态创建(如果动态创建,请注意设置宽高等属性并将其添加到父级 Layout 中)
/* 调用 init 方法来初始化画布,可以在此处理 RendererEvents;
如果不需要处理 RendererEvents,可以不调用 init 方法。
----------
mLocalView.init(new RendererCommon.RendererEvents() {
@Override
public void onFirstFrameRendered() {
// 可以按需处理
}
@Override
public void onFrameResolutionChanged(int i, int i1, int i2) {
// 可以按需处理
}
});
----------
画布不再使用时,调用 release 方法进行释放;如果创建过多的 RtcView 而未释放,可能出现报错:
java.lang.RuntimeException: Failed to create EGL context: 0x3003
----------
从 1.4.4.9 版本开始:
如果 RtcView 被动态添加,或者包含 RtcView 的 Activity 被创建时,RtcView 会自动初始化;
RtcView 被动态移除,或者相应的 Activity 被销毁时,RtcView 会自动释放。
*/
// 除 setMirror 外,RtcView 还支持 setScalingType 设置视频缩放类型,等
mLocalView.setMirror(mFrontCamera); // 通常前置摄像头需要开启镜像
mRtcEngine.setLocalVideoRender(mLocalView); // 设置本地画布用来显示自己的画面
// 分辨率:1280 x 720,实际使用请按需配置
mRtcEngine.startPreview(Constants.VideoProfileType.HD720P, true);
- 如果设置的分辨率高于摄像头支持的最大分辨率,则自动降低为摄像头支持的最大分辨率。
- 如需切换预览不同的摄像头,先通过
RtcEngine
实例的stopPreview
方法停止当前预览,再通过startPreview
方法开启新的预览,无需操作 View。- 如果不需要在 加入频道 前进行本地预览,可以不调用
startPreview
接口。
加入频道
加入频道
通过 RtcEngine
实例的 joinChannel
方法加入频道。
RtcChannelConfig config = new RtcChannelConfig();
config.userName = ""; // 用户名,可选参数,可为空,建议配置以区分不同用户
config.mode_1v1 = false; // 一对一模式/会议模式;如果有 Web 端参与,或者需要进行直播/录制,请设置为 false
/* serviceFlags 频道标志:音视频 | 白板 | 实时消息 (按位或)
config.serviceFlags =
kChannelServiceMedia | kChannelServiceWhiteboard | kChannelServiceMessage;
*/
config.serviceFlags = Constants.kChannelServiceMedia; // 只使用音视频
config.subscribeAudioAll = true; // 自动订阅音频,可以配置为 false 来主动订阅音频
Constants.QResult ret = mRtcEngine.joinChannel(mAppToken, mChannelId, mUserId, config);
if (ret != Constants.QResult.OK) {
// 调用失败,请根据 QResult 进行处理
}
自己加入频道的通知
调用 joinChannel
方法后,将触发 onChannelJoinConfirm
通知。
public void onChannelJoinConfirm(Constants.QResult result) {
// result 表示自己加入频道的结果
}
joinChannel
(加入频道) 接口返回的QResult
仅表示调用该接口的结果,而onChannelJoinConfirm
(自己加入频道的通知) 返回的QResult
表示和服务器交互后的结果。
也就是说当onChannelJoinConfirm
返回OK
才表示加入频道成功。
其他用户加入频道的通知
其他用户加入频道后,将触发 onUserJoinIndication
通知。
public void onUserJoinIndication(long userId, String userName) {
// 其他用户加入频道的通知。userId 和 userName 都是该用户在 joinChannel 时传入的
// 此时可以将其加入到与会者列表中
}
倒计时通知
加入频道后,如果存在倒计时限制,将触发 onChannelCountDown
通知。
public void onChannelCountDown(long remain) {
// 频道倒计时通知,单位:秒;请保存用于展示倒计时,此回调仅在加入频道后回调一次
}
- 已实名认证,并且使用正式token,Pano不限制频道时长
- 未实名认证,或者使用临时token,最大频道时长为 30 分钟
操作视频
如果你使用纯音频SDK,请忽略本节,直接跳转到下一节:操作声音。
发送本地视频
通过 RtcEngine
实例的 startVideo
方法发送本地视频。
// 调用此方法后,其他用户才能看到你的画面;请在 onChannelJoinConfirm 回调加入频道成功后再调用
// 请先调用 setLocalVideoRender 方法设置本地画布用来显示自己的画面
// 分辨率:1280 x 720,实际使用请按需配置
mRtcEngine.startVideo(Constants.VideoProfileType.HD720P, true);
/* startVideo 还有重载方法 startVideo(boolean frontCamera, RtcVideoConfig config),
在 RtcVideoConfig 中,除 VideoProfileType 外,
还可以指定 sourceMirror 参数,表示是否启用视频源镜像。
VideoProfileType 和 sourceMirror 会同时影响发送和渲染。
------
通过 RtcView 的 setMirror 方法设置镜像,只影响渲染不影响发送。
------
RtcVideoConfig 的 sourceMirror 参数,和 RtcView 的 setMirror 方法,
只要其中一个参数为 true,本地渲染即为镜像模式。
*/
- 调用此方法,会触发
onVideoStartResult
回调。startVideo
设置的分辨率表示期望的发送分辨率,实际过程中可能会因硬件支持情况、网络情况和订阅情况触发 SDK 自动调整而降低(后文会介绍订阅)。- 如果发送本地视频之前,已设置本地画布并开启了本地预览,请先调用
stopPreview
方法停止本地预览,再调用startVideo
发送本地视频,无需重新设置本地画布,startVideo
成功后,SDK 会自动将自己的画面渲染到本地画布上。
切换摄像头
在通话过程中,可以通过 RtcEngine
实例的 switchCamera
方法切换摄像头。
mRtcEngine.switchCamera(); // 此方法将切换发送出去的摄像头画面
停止发送本地视频
通过 RtcEngine
实例的 stopVideo
方法停止发送本地视频。
mRtcEngine.stopVideo(); // 调用此方法后,其他用户将看不到你的画面;本地预览不会被停止
其他用户开启视频的通知
当其他用户发送视频时,将触发 onUserVideoStart
通知。
public void onUserVideoStart(long userId, Constants.VideoProfileType maxProfile) {
// maxProfile 表示该用户的最大可发送分辨率
// 取决于 摄像头支持的最大分辨率 和 设置的发送分辨率 这两个值中的较小值
}
订阅其他用户的视频
通过 RtcEngine
实例的 setRemoteVideoRender
方法设置显示其他用户画面的视频画布,然后通过 subscribeVideo
方法订阅该用户的视频。
// 请在收到其他用户的 onUserVideoStart 通知后,先设置画布,再调用订阅方法
mRtcEngine.setRemoteVideoRender(userId, render);
// 可以根据显示窗口的大小来决定订阅分辨率,比如大窗口订阅高分辨率,小窗口订阅低分辨率
mRtcEngine.subscribeVideo(userId, profileType);
- Pano采用订阅模式来接收视频,参与者之间需要相互订阅,才可以看到彼此的视频画面。
- 默认 正式token 最多可以订阅 10 路视频,临时token 最多可以订阅 3 路视频。
- 如果订阅失败,会触发
onUserVideoSubscribe
通知。- 使用
RtcVideoStreamManager
的开发者,请换用RtcVideoStreamManager.subscribeVideo
方法来订阅其他用户的视频流。
视频首帧的通知
当自己发送或收到其他用户第一个视频帧时,将触发 onFirstVideoFrameRendered
通知。
public void onFirstVideoFrameRendered(long userId) {
// 第一个视频帧通知,可以开始显示视频画面
}
取消订阅其他用户的视频
通过 RtcEngine
实例的 unsubscribeVideo
方法取消订阅其他用户的视频。
mRtcEngine.unsubscribeVideo(userId); // 调用此方法后,你将看不到该用户的画面
- 当其他用户停止视频或者离开频道的时候,将被自动取消订阅。
- 取消订阅后重新订阅的情况,如果使用同一
render
,无须重新调用setRemoteVideoRender
方法。- 使用
RtcVideoStreamManager
的开发者,请换用RtcVideoStreamManager.unsubscribeVideo
方法来取消订阅。
操作声音
发送本地声音
通过 RtcEngine
实例的 startAudio
方法发送本地声音。
mRtcEngine.startAudio(); // 调用此方法后,其他用户才能听到你的声音;请在加入频道成功后再调用
调用此方法,会触发
onAudioStartResult
回调。
停止发送本地声音
通过 RtcEngine
实例的 stopAudio
方法停止发送本地声音。
// 调用此方法后,其他用户将听不到你的声音
// 不影响你接收其他用户的声音
// 可以调用 startAudio 重新发送
mRtcEngine.stopAudio();
静音麦克风
通过 RtcEngine
实例的 muteAudio
方法静音本地麦克风。
mRtcEngine.muteAudio(); // 调用此方法后,其他用户将听不到你的麦克风的声音
停止发送本地声音(
stopAudio
) 和 静音本地麦克风(muteAudio
) 这两个接口都可以将自己静音(不让其他用户听到你的声音)。它们的区别在于:
1 "停止发送本地声音"会断开音频通道连接(同时停止发送麦克风和伴音的声音,会取消其他用户订阅你的声音,如需重建连接需要消耗时间)
2 "静音本地麦克风"会发送静音包(不影响伴音发送,不影响订阅关系,静音包数据量很小,临时静音建议使用此方式,取消静音能够迅速恢复发送声音)
取消静音麦克风
通过 RtcEngine
实例的 unmuteAudio
方法取消静音本地麦克风。
mRtcEngine.unmuteAudio(); // 静音麦克风后,需要取消静音,其他用户才能听到你的麦克风的声音
其他用户开启声音的通知
当其他用户发送声音时,将触发 onUserAudioStart
通知。
public void onUserAudioStart(long userId) {
...
}
订阅其他用户的声音
如果在 joinChannel
(加入频道)时,配置了自动订阅音频(例如:config.subscribeAudioAll = true
),则开发者无需再主动订阅其他用户的声音。否则,需要通过 RtcEngine
实例的 subscribeAudio
方法订阅其他用户的声音。
// 如果 加入频道 时配置了自动订阅音频,则无需调用此接口
// 请在收到其他用户的 onUserAudioStart 通知后,再调用订阅
mRtcEngine.subscribeAudio(userId);
- 如果希望在通话过程中,不收听某位与会者,或者不收听所有其他与会者,请在加入频道时,设置不自动订阅音频;随后按需订阅或取消订阅某位或所有其他与会者。
- 如果订阅失败,会触发
onUserAudioSubscribe
通知。
收到其他用户声音的通知
当收到其他用户的声音时,将触发 onFirstAudioDataReceived
通知。
public void onFirstAudioDataReceived(long userId) {
// 收到其他用户的首个声音数据包
}
取消订阅其他用户的声音
通过 RtcEngine
实例的 unsubscribeAudio
方法取消订阅其他用户的声音。
mRtcEngine.unsubscribeAudio(userId); // 调用此方法后,你将听不到该用户的声音
当其他用户停止声音或者离开频道的时候,将被自动取消订阅。
离开频道
离开频道
通过 RtcEngine
实例的 leaveChannel
方法离开频道。
mRtcEngine.leaveChannel();
注意:此接口为同步接口;调用此接口主动离开频道,自己不会收到
onChannelLeaveIndication
回调。
其他用户离开频道的通知
其他用户离开频道后,将触发 onUserLeaveIndication
通知。
onUserLeaveIndication(long userId, Constants.UserLeaveReason reason) {
// 可将其从与会者列表中移除
};
释放资源
通过 destroy
方法销毁 RtcEngine
实例。
mRtcEngine.destroy();
反馈问题
我们强烈建议您集成反馈功能,以便用户遇到问题时,可以提交信息给我们分析排查,具体请 点此查看。
SDK API 指南
本文仅介绍接入Pano Android SDK实现视频通话的基本流程和接口。
实际场景可能需要结合使用更多接口,请 点此查看 API 列表。
示例代码
为方便开发者了解丰富的接口使用方式,我们还提供示例代码,请体验参考: