1. 简介
在如今的移动互联网时代,音视频相关应用已经深入到我们日常生活的方方面面。
而在它背后的音视频技术也是非常成熟了,短视频编辑、带货直播、视频语音通话等应用形式无不体现着音视频技术给我们生活带来的便利。
而这次就去实际体验一下,接入声网的音频 SDK ,并打造一个音视频通话应用。
2. 账户注册与项目创建
首先要做的就是在声网(https://www.agora.io/cn)上注册账号,并完成实名认证。
登录之后就进到管理界面了,在左侧有一系列选项可以操作,直接进到项目管理,创建我们的 VideoChat 项目。
在项目创建的安全模式上,选择 APPID + Token 的方式。为了提高项目的安全性,Agora 后续会取消对 APP ID 鉴权方案的支持。
当项目创建完成后就有了对应的 APP ID ,这个在后续代码开发中会用到的。
除此之外,我们还需要生成 Token ,在项目管理页去生成一个临时 Token 。
这个临时 Token 是有时效的,仅供测试使用,如果有自己的后端开发,到生产环境再去生成正式 Token。同样,这个 Token 字符串在后续开发中也会用到的。
3. SDK 下载与集成
创建项目并准备好 APP ID 和 Token 之后,就可以在官网上下载音频 SDK 了。
下载解压后的内容如上所示。libs 文件夹内有对应不同 CPU 架构的 so 动态库,还有动态库对应的头文件以及 Java 版本的 Jar 包,可分别进行 C++ 版本和 Java 版本的集成。
这里就只用到 Java 版本来演示了,其实 Java 版本里面很多方法都是走到 native 调用了,而 native 调用的就是 so 动态库里面的方法。用 C++ 版本集成的话,还需要自己写 JNI 代码将 Java 与 C++ 连接起来,不如直接用 Java 版本来的快。
SDK 集成有两种方式,一种是直接使用 JCenter 来集成,在 build.gradle 里面添加一行代码就行。
dependencies {
implementation 'io.agora.rtc:full-sdk:3.1.3'
}
版本号可以在官网上查询,目前最新版本就是 3.1.3 了。
当然还可以通过加载 jar 包和 so 动态库的方式进行集成,把 agora-rtc-sdk.jar 和各版本 so 拷贝到对应目录下,如下图所示:
另外别忘了在 app 目录下的 build.gradle 中添加如下代码:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
少了这行代码,可解析不到添加的 jar 包。
以上,就完成了整个 SDK 的工程接入,接下来就是代码开发环节了。
4. 应用开发
我们要开发的是一款音视频通话应用,就像微信视频一样,想想会有哪些内容。
首先要通过 Camera 采集我们的画面,然后通过麦克风录制我们的声音,再通过网络传输给到对方,并且能够听到对方的声音,在屏幕上显示画面。
想一想这些内容要是纯 Android 开发的话,那涉及的东西可多了,四五个人都不一定能 hold 住,而使用专业的音视频 SDK ,一个人就能搞定大部分工作了。
接下来就要去完成这样的开发工作,具体代码可以在 Github 上获取:
https://github.com/glumes/ago...
4.1 权限申请
首先是权限申请,涉及到 Camera、网络、存储、录制等等内容,在官网上也有给出具体权限列表,如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.agora.tutorials1v1acall">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
// 如果你的场景中涉及读取外部存储,需添加如下权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
// 如果你使用的是 Android 10.0 及以上设备,还需要添加如下权限:
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
...
</manifest>
具体代码可见 Github :
4.2 引擎创建
接下来就是创建 RtcEngine ,SDK 的很多方法都是通过 RtcEngine 来调用的。
// 声明 RtcEngine
private RtcEngine mRtcEngine;
// 创建 RtcEngine
private void initializeEngine() {
try {
mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler);
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e));
}
}
在创建 RtcEngine 的时候需要用到 APP ID ,前面已经提过,在创建项目时就已经有了。
最后一个参数是 mRtcEventHandler
,它是一个 IRtcEngineEventHandler
类型的抽象类,类里面定义了很多方法,去响应 RtcEngine 不同状态的回调,比如连接成功,连接失败、加入频道、离开频道、网络状态改变等等。如果有需要对应用的性能做一些监视,那么就可以在 IRtcEngineEventHandler 的回调方法做相关统计和埋点。
4.3 实现 Camera 画面预览
RtcEngine 把 Camera 相关操作都封装在 SDK 内了,通过几行代码就能实现 Camera 画面预览。
4.3.1 创建 Camera 画面预览控件 View
首先要有一个控件去承接显示 Camera 的画面输出内容,可以用 SurfaceView 也可以用 TextureView ,但并不需要我们去创建控件,而是用 Agora SDK 提供的方法。
// 使用 SurfaceView 的场景
SurfaceView mLocalView = RtcEngine.CreateRendererView(getBaseContext());
// 使用 TextureView 的场景
TextureView mLocalView = RtcEngine.CreateTextureView(getBaseContext());
TextureView 是 Agora SDK 在 3.1.0 版本才提供的,它与 SurfaceView 的区别在于 TextureView 可以对画面进行缩放、旋转和平移,而 SurfaceView 更适合在视频通话和直播场景使用。
有了显示的 View 之后,要把它添加到当前 Activity 的控件树上,后面 Camera 画面就会输出到这里。
4.3.2 配置 Camera 输出并开启预览
接下来就是让 Camera 输出画面了,可实际上不用写一行关于 Camera 的代码,三行代码就可以搞定。
// 开启视频
mRtcEngine.enableVideo();
// 初始化本地视图
mRtcEngine.setupLocalVideo(new VideoCanvas(mLocalView, VideoCanvas.RENDER_MODE_HIDDEN, 0));
// 开启预览
mRtcEngine.startPreview();
通过以上代码就能在屏幕上显示我们的画面了,默认是前置摄像头的内容。
具体代码可见 Github :
在代码开发中,首先要启用视频模块,默认是关闭的,通过 disableVideo 也可以关闭。而音频模块默认就是开启的,也可以通过 enableAudio 和 disableAudio 来开启关闭音频模块。
之后就是通过 setupLocalVideo 方法来初始化本地视图,主要是设置本地用户视频信息的,也就是我们的画面要在 SurfaceView 中如何显示,配置信息都是通过 VideoCanvas 类下发的,它有多种参数类型。
// VideoCanvas 构造函数类型
VideoCanvas (View view)
VideoCanvas (View view, int renderMode, int uid)
VideoCanvas (View view, int renderMode, String channelId, int uid)
VideoCanvas (View view, int renderMode, int uid, int mirrorMode)
VideoCanvas (View view, int renderMode, String channelId, int uid, int mirrorMode)
其中 renderMode 就是指定我们的画面在 SurfaceView 如何显示,有如下类型:
RENDER_MODE_HIDDEN
- 优先保证视窗被填满。视频尺寸等比缩放,直至整个视窗被视频填满。如果视频长宽与显示窗口不同,多出的视频将被截掉。
RENDER_MODE_FIT
- 优先保证视频内容全部显示。视频尺寸等比缩放,直至视频窗口的一边与视窗边框对齐。如果视频长宽与显示窗口不同,视窗上未被填满的区域将被涂黑
RENDER_MODE_FILL
- 视频尺寸进行缩放和拉伸以充满显示视窗
默认情况下都是使用 RENDER_MODE_HIDDEN 模式的。另外还有 mirrorMode 这样的属性来设置是否要画面镜像。
完成了画面显示配置之后,直接调用 startPreview 就能在屏幕上看到画面啦,是不是很简单!
4.3.3 Camera 更多的配置操作
简单的背后其实是 Agora SDK 做了很多封装工作,比如 Camera1 和 Camera2 的调用逻辑、Camera 输出的分辨率策略、前后摄像头选择等等。
我们可以在开启预览前通过 setCameraCapturerConfiguration 方法来配置自己想要的 Camera 信息。
// 配置 Camera 信息
mRtcEngine.setCameraCapturerConfiguration(new CameraCapturerConfiguration(CAPTURER_OUTPUT_PREFERENCE_AUTO,CAMERA_FRONT));
Camera 参数信息主要是在 CameraCapturerConfiguration 类中,它的两个参数都是枚举类型,其中第二个参数指定了使用前置还是后置摄像头。
第一个参数就是关于 Camera 输出分辨率的一些策略,Agora SDK 并没有给出接口让我们指定 Camera 输出宽是多少,长是多少,而且根据使用场景组合了三个策略,更方面我们去调用了。
CAPTURER_OUTPUT_PREFERENCE_AUTO
- (默认)自动调整采集参数。
CAPTURER_OUTPUT_PREFERENCE_PERFORMANCE
- 优先保证设备性能。
CAPTURER_OUTPUT_PREFERENCE_PREVIEW
- 优先保证视频预览质量。
通过以上操作,就完成了 Camera 画面预览显示。
4.4 加入通话频道
接下来就要去加入一个频道,并和同一频道内的朋友进行通信。
// 函数原型
joinChannel ( String token,String channelName,String optionalInfo,int optionalUid)
// 具体调用
private void joinChannel() {
// 得到 Token
String token = getString(R.string.agora_access_token);
// Token 和 频道名要匹配
mRtcEngine.joinChannel(token, "demoChannel1", "", 0);
}
通过 joinChannel
方法加入,其中 token 就是我们之前创建好的,而频道名称也是创建 token 时指定的,这两者要匹配起来。optionalInfo 是可选的字符串。optionalUid 是用户 ID,也是可选项,如果不指定(设为 0),SDK 会自动分配一个,并在 onJoinChannelSuccess 回调方法中返回。
当我们加入频道成功后,会回调 IRtcEngineEventHandler 中的 onJoinChannelSuccess 方法。
通过 leaveChannel
方法,我们可以离开当前频道,同样会回调 IRtcEngineEventHandler 中的 onLeaveChannel 方法。
4.5 音视频数据编码格式参数设置
加入频道后,我们就要和频道内的朋友们通信。要把我们的画面和声音发送给对方,那肯定要将数据进行编码,然后经过网络传输送给对方。
注意:音频和视频的参数都一定要在加入频道前设定好,也就是 joinChannel 方法调用之前,在其之后调用是不生效的。
4.5.1 视频编码参数设置
这里我们要指定视频编码的分辨率、帧率、码率、视频等信息,通过 setVideoEncoderConfiguration
方法。
mRtcEngine.setVideoEncoderConfiguration(new VideoEncoderConfiguration(
// 视频分辨率
VideoEncoderConfiguration.VD_640x360,
// 帧率
VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,
// 码率
VideoEncoderConfiguration.STANDARD_BITRATE,
// 视频方向
VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT));
在更早的 Agora SDK 版本中是通过 setVideoProfile 来设定的,还可以指定具体的宽高、帧率数值。
最新版本换成了 VideoEncoderConfiguration 来配置,并且配置的参数是都已经定义好了相关的常量,不用自己写 720、1280 这样的 Magic Number 了。
参数的设置并不是一成不变的,因为网络或者性能等因素会有一些波动,但还是会取最接近我们设定的值。
4.5.2 音频编码参数设置
而音频相关的参数,则是通过 setAudioProfile
方法来设置。
mRtcEngine.setAudioProfile(
// 设置音频采样率、码率、编码模式和声道数
Constants.AudioProfile.DEFAULT.ordinal(),
// 设置音频的应用场景
Constants.AudioScenario.DEFAULT.ordinal());
音频我们可以设置采样率、码率、编码模式以及声道数。和视频参数设置一样,我们不用指定具体的数值,Agora SDK 都根据业务使用场景做了封装,根据需要来设置就好啦。
音频 AudioProfile 有如下的配置:
DEFAULT
- 通信场景下,该选项代表指定 32 kHz 采样率,语音编码,单声道,编码码率最大值为 18 Kbps。
- 直播场景下,该选项代表指定 48 kHz 采样率,音乐编码,单声道,编码码率最大值为 64 Kbps。
SPEECH_STANDARD
- 指定 32 kHz 采样率,语音编码, 单声道,编码码率最大值为 18 Kbps。
MUSIC_STANDARD_STEREO
- 指定 48 kHz 采样率,音乐编码, 单声道,编码码率最大值为 64 Kbps。
MUSIC_HIGH_QUALITY
- 指定 48 kHz 采样率,音乐编码, 单声道,编码码率最大值为 96 Kbps。
MUSIC_HIGH_QUALITY_STEREO
- 指定 48 kHz 采样率,音乐编码, 双声道,编码码率最大值为 128 Kbps。
音频的应用场景也有如下:
DEFAULT
- 默认的音频应用场景
CHATROOM_ENTERTAINMENT
- 娱乐应用,需要频繁上下麦的场景
EDUCATION
- 教育应用,流畅度和稳定性优先
GAME_STREAMING
- 高音质语聊房应用
SHOWROOM
- 秀场应用,音质优先和更好的专业外设支持
CHATROOM_GAMING
- 游戏开黑
一般来说,我们使用默认的配置 DEFAULT 就好了。
4.6 用户加入并显示对方画面
当设置好了自己的数据编码参数,并且也成功加入了频道,接下来就是去接收频道内其他人的画面和信息了。
具体代码可见 Github:
4.6.1 判断是否有用户加入频道
当 IRtcEngineEventHandler 中的 onUserJoined 方法回调时,代表有人加入了当前频道,此时就可以建立并初始化远端用户视图了。
如果启用了视频录制功能,视频录制服务也会回调 onUserJoined 方法,相当于有个机器人加入该频道,此时要区分开来,不能为机器人建立远端视频,然后它不会发送视频流的,建立了也是黑屏的。
为了避免机器人加入带来的误判,在 2.9.0 版本后更建议在 onRemoteVideoStateChanged 方法回调中去建立远端用户视图。
onRemoteVideoStateChanged 方法顾名思义就是当远端用户状态发生改变时就会调用,其中定义了如下几个状态:
REMOTE_VIDEO_STATE_STARTING
- 本地用户已接收远端视频首包
REMOTE_VIDEO_STATE_DECODING
- 远端视频流正在解码,正常播放
REMOTE_VIDEO_STATE_STOPPED
- 远端视频默认初始状态
REMOTE_VIDEO_STATE_FROZEN
- 远端视频流卡顿
REMOTE_VIDEO_STATE_FAILED
- 远端视频流播放失败
当状态是 REMOTE_VIDEO_STATE_STARTING 或者 REMOTE_VIDEO_STATE_DECODING 时,我们认定有朋友加入频道了,此时建立远端用户视图。
@Override
public void onRemoteVideoStateChanged(final int uid, int state, int reason, int elapsed) {
if (state == Constants.REMOTE_VIDEO_STATE_STARTING){
runOnUiThread(new Runnable() {
@Override
public void run() {
setupRemoteVideo(uid);
}
});
}
}
其中 onRemoteVideoStateChanged 回调方法中 state 参数代表状态,uid 用来标识远端用户 id,如果有多个朋友加入了该频道,我们就要用 uid 来区分一下用户,避免漏了或者重复建立远端用户视图。
4.6.2 建立远端用户视图
接下来建立远端用户视图,和建立 Camera 预览视图方法基本一致。
// 由 Agora SDK 创建 SurfaceView
mRemoteView = RtcEngine.CreateRendererView(getBaseContext());
// 把 SurfaceView 添加到当前 Activity 的布局控件树上
mRemoteContainer.addView(mRemoteView);
// Camera 画面预览就用 setupLocalVideo 方法,远端用户就用 setupRemoteVideo 方法
mRtcEngine.setupRemoteVideo(new VideoCanvas(mRemoteView, VideoCanvas.RENDER_MODE_HIDDEN, uid));
同样是由 Agora SDK 去创建 SurfaceView ,把它添加到控件容器上,最后通过 setupRemoteVideo 方法完成建立,整个逻辑和 Camera 视图建立是类似的,就是最后调用的方法不一样了。
4.7 应用运行并视频通话
当建立好了远端用户视图,理论上现在就可以显示自己和对方的画面了,并互相听到声音了。
用两台手机分别运行程序,可以验证效果如下:
另外在官网上还可以查看当前项目的流量使用情况:
每个月有 10000 分钟的流量赠送,可以放心使用
5. 项目开发总结
通过以上的 SDK 接入和代码示例就已经完成了一个简单的双人音视频通话。
而 Agora SDK 提供的功能远非如此。
我们还可以对当前频道的音视频进行控制,选择是否静音、是否关闭画面等等;我们还可以切换频道,参与频道的通话;我们还可以将项目打造成多人的音视频通话,为每个加入频道的用户建立远端视图。
更多的音视频相关功能都可以通过 Agora SDK 来实现了。
总结一下整体的接入流程也是非常方便的:
SDK 集成 -> 权限设置 -> Camera 预览 -> 加入频道 -> 显示画面 -> 在线通话
以上的每个步骤在文章中都有讲解,希望在你的使用过程中会有一些帮助!!!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。