前提准备
拥有一个【Agora 账户】
如果你还没有声网开发者账号,那么你需要在声网Agora 官网进行注册。
如果你已经注册过了声网开发者账号,那么可以通过声网官网直接登录账号。
选择 "项目管理 "下 "项目列表 "选项卡,点击蓝色的 "创建 "按钮,创建一个项目。(当提示使用 App ID+证书时,选择只使用 App ID。)记住你的 App ID,它将在开发 App 时用于授权你的请求。
完成步骤后,即可拥有一个账号。
文档中心
声网 Agora 官方文档地址:https://docs.agora.io/cn
集成 Agora 音频 SDK,实现高音质语音通话。通过美声、音效、混音等功能,你可以轻松实现语聊房、线上 KTV、语音会议等场景。搭配服务端 RESTful API,你还可以实现踢人、查询用户列表等功能。
业务场景
语音通话可以实现纯语音的一对一单聊和多人群聊,不具备视频通话功能,包体积更小,适用于各种语音社交、语音会议等场景。
语音负责场景,主要包括:伴奏混音、播放音效文件、变声和混响、听声辨位、使用双声道和高音质以及修改原始音
频数据。
伴奏混音
播放音频
变声和混响
听声辨位
听声辨位
双声道/高音质
业务实现:支持高音质、双声道的音频设置。
适用场景:音乐教学,FM 音频电台。
修改原始音频数据
关键特性
SDK 包体积:3.14 ~ 11.28 MB
音频属性
音频采样率:16 kHz - 48 kHz
支持单、双声道
音频抗丢包率:上下行抗丢包率 80%a
平台兼容
语音通话支持 iOS、Android、Windows、macOS、Electron、Unity、Web、小程序,并支持平台间互通,具体的兼容性要求见下表。
桌面端
Web SDK 对桌面端浏览器的支持情况详见下表:
Web SDK2.5 及以上版本支持 Windows XP 平台的 Chrome 49 版本浏览器(仅支持 VP8 编解码,不能与 Native SDK 互通)。Web SDK 理论上还支持 360 极速浏览器,但未经过验证,不保证全部功能正常工作。
移动端
Web SDK 对移动端浏览器的支持情况受到具体设备和浏览器的限制:
在 Android 4.1+ 上,Web SDK 支持 Google Chrome 58 及以上版本,且 Agora 建议使用 VP8 编解码格式。这是由于 Android Chrome 对 H.264 的支持依赖硬件,而部分 Android 设备不支持 H.264 编解码格式。
在 iOS 11+ 上,Web SDK 支持 Safari 11 及以上版本。但是由于 iOS Safari 存在较多已知问题,Agora 不推荐使用。你可以使用 Agora RTC iOS SDK 在 iOS 上实现实时音视频功能。
除上述浏览器外,对于移动端的一些应用内置浏览器,Web SDK 的支持情况较为复杂,详见移动端如何使用 Agora Web SDK?
服务端开发流程
使用 Token 鉴权
「使用 Token 鉴权」是指在用户访问你的系统前,对其进行身份校验。用户在使用 Agora 服务,如加入音视频通话或登录信令系统时,Agora 使用 Token 对其鉴权。
本文展示如何在服务端部署一个 Token 生成器,以及如何搭建一个使用 Token 鉴权的客户端。
鉴权原理
下图展示了鉴权的基本流程:
图片来源:https://web-cdn.agora.io/docs-files/1622017307186
Token 在 app 服务器上生成,其最大有效期为 24 小时。当用户从你的 app 客户端连接至 Agora 频道时,Agora 平台会读取该 Token 中包含的信息,并进行校验。Token 包含以下信息:
Agora 控制台创建项目时生成的 App ID
项目的 App 证书
频道名
用户 ID
用户权限,如是否能发流或收流
Token 过期的 Unix 时间戳
鉴权流程
代码 SDK 地址:Tools/DynamicKey/AgoraDynamicKey at master · AgoraIO/Tools (github.com)
获取 App ID 及 App 证书
本节介绍如何获取生成 Token 所需的安全信息,如你的项目的 App ID 及 App 证书。
(1) 获取 App ID
Agora 自动为每一个项目分配一个 App ID 作为该项目的唯一标识。
Agora 控制台 Project Management 页面中找到你的项目,点击 App ID 右边的“眼睛”图标即可复制 App ID。
图片来源:https://web-cdn.agora.io/docs-files/1602646621028
(2) 获取 App 证书
点击 Edit,进入 Edit Project 页面。 点击“眼睛”图标即可复制 App 证书。
图片来源:https://web-cdn.agora.io/docs-files/1592535534341
部署 Token 服务器
Token 需要在你的服务端部署生成。当客户端发送请求时,服务端部署的 Token Generator 会生成相应的 Token,再发送给客户端。
Token 生成相关的 token 信息(SDK)
SDK 服务实现:Agora.io (github.com),如果需要其他实现,可以参考该 github 项目的其他工具
package io.agora.sample;
import io.agora.media.RtcTokenBuilder;
import io.agora.media.RtcTokenBuilder.Role;
public class RtcTokenBuilderSample {
static String appId = "970CA35de60c44645bbae8a215061b33";
static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
static String channelName = "7d72365eb983485397e3e3f9d460bdda";
static String userAccount = "2082341273";
static int uid = 2082341273;
static int expirationTimeInSeconds = 3600;
public static void main(String[] args) throws Exception {
RtcTokenBuilder token = new RtcTokenBuilder();
int timestamp = (int)(System.currentTimeMillis() / 1000 + expirationTimeInSeconds);
String result = token.buildTokenWithUserAccount(appId, appCertificate,
channelName, userAccount, Role.Role_Publisher, timestamp);
System.out.println(result);
result = token.buildTokenWithUid(appId, appCertificate,
channelName, uid, Role.Role_Publisher, timestamp);
System.out.println(result);
result = token.buildTokenWithUid(appId, appCertificate, channelName, uid,
timestamp, timestamp, timestamp,
timestamp);
System.out.println(result);
result = token.buildTokenWithUserAccount(appId, appCertificate, channelName,
userAccount, timestamp, timestamp, timestamp,
timestamp);
System.out.println(result);
}
}
复制代码
现在以 Java 语言为例,还有其他的案例请见:raysandeep/AgoraToken (github.com)
具体方案可参考:使用Java构建Agora令牌服务器 - 专栏 - 声网 Agora RTC 开发者社区
Token 客户端介入(Http)
下列代码实现了 HTTP 基本认证并使用服务端 RESTful API 发送一个简单的请求,获取所有的 Agora 项目信息:
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;
// 基于 Java 实现的 Token 认证示例,使用 RTM 的用户事件 RESTful API
public class Base64Encoding {
public static void main(String[] args) throws IOException, InterruptedException {
// RTM Token
String tokenHeader = "Your RTM token";
// 生成 RTM Token 时使用的 user ID
String uidHeader = "test_user";
HttpClient client = HttpClient.newHttpClient();
// 构建请求对象
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.agora.io/dev/v2/project/<Your App ID>/rtm/vendor/user_events"))
.GET()
// 在 header 中添加 x-agora-token 字段
.header("x-agora-token", tokenHeader )
// 在 header 中添加 x-agora-uid 字段
.header("x-agora-uid", uidHeader)
.header("Content-Type", "application/json")
.build();
// 发送请求
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
复制代码
下列示例代码实现了 HTTP 基本认证并使用服务端 RESTful API 发送一个简单的请求,获取所有的 Agora 项目信息:
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;
// 基于 Java 实现的 HTTP 基本认证示例,使用 RTC 的服务端 RESTful API
public class Base64Encoding {
public static void main(String[] args) throws IOException, InterruptedException {
// 客户 ID
final String customerKey = "Your customer key";
// 客户密钥
final String customerSecret = "Your customer secret";
// 拼接客户 ID 和客户密钥并使用 base64 编码
String plainCredentials = customerKey + ":" + customerSecret;
String base64Credentials = new String(Base64.getEncoder().encode(plainCredentials.getBytes()));
// 创建 authorization header
String authorizationHeader = "Basic " + base64Credentials;
HttpClient client = HttpClient.newHttpClient();
// 创建 HTTP 请求对象
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.agora.io/dev/v1/projects"))
.GET()
.header("Authorization", authorizationHeader)
.header("Content-Type", "application/json")
.build();
// 发送 HTTP 请求
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
复制代码
语音通信服务端功能
除集成在客户端的音频/视频 SDK,Agora 还提供用于管理实时音视频频道的服务端 API,包括:
Agora 频道管理 RESTful API 提供以下功能:
封禁用户权限
Agora 频道管理 RESTful API 提供以下功能:
查询在线频道信息
Agora 频道管理 RESTful API 提供以下功能:
频道事件消息通知服务
Agora 提供频道事件消息通知服务,用于实时同步实时通信(RTC)业务中的频道状态。
开通消息通知服务后,当订阅的频道事件发生时,Agora RTC 业务服务器会将事件消息发送给 Agora 消息通知服务器,然后 Agroa 消息通知服务器会通过 HTTPS POST 请求方法将事件通知投递给你的服务器。
开通消息通知服务
功能配置
希望开通消息通知服务的业务,即实时音视频通信业务。
希望监听的频道事件,详见实时音视频通信频道事件类型。
如需配置 QPS (Query Per Second) 较高的事件,如实时通信中观众加入或离开直播频道,请确保你的服务器有足够的处理能力。
接收消息通知的 HTTPS 服务器地址。
为提高安全性,Agora 消息通知服务不再支持 HTTP 服务器地址。
为降低消息投递的延时,Agora 建议你的服务器支持 HTTPS 连接重用,即 keep-alive 模式,并进行如下设置:MaxKeepAliveRequests
:大于等于 100。KeepAliveTimeout
:大于等于 10 秒。
你的消息通知接收服务器所在的区域。Agora 会根据你提供的区域就近接入 Agora 节点服务器。目前,Agora 消息通知服务器部署在以下区域:
cn
:中国
sea
:东南亚
na
:美国
eu
:欧洲
获取密钥
为提高通信安全,Agora 消息通知服务使用签名机制验证身份。配置消息通知服务时,Agora 为你生成用于验证签名的密钥(secret),你需要向 Agora 技术支持获取并保存这个密钥。关于如何使用密钥验证签名,详见验证签名。
配置测试
配置完成后,你可以让 Agora 技术支持帮你测试该配置。如果测试成功,即可正式开通该服务。
消息通知回调格式与内容
成功开通 Agora 消息通知服务后,当订阅的频道事件发生时,Agora 消息服务器会以 HTTPS POST 请求的形式向你的服务器发送消息通知回调。频道事件消息回调的格式与内容,详见频道事件回调。
消息通知回调响应
接收到消息通知回调后,你的服务器需要在 10 秒内对 Agora 消息服务器作出响应。响应包体格式为 JSON,包体内容不作要求。
Agora 消息服务器发送通知后的 10 秒内,如果没有收到你的服务器的响应或响应状态码不是 200,会认为消息通知失败。失败后,Agora 消息通知服务器会立即重新发送消息通知,投递间隔随着重试次数的上升逐渐增加,直到第一次投递的 30 分钟后停止投递。
频道回调机制
Agora 提供频道事件消息通知服务,用于实时同步实时通信(RTC)业务中的频道状态。
开通消息通知服务后,当订阅的频道事件发生时,Agora RTC 业务服务器会将事件消息发送给 Agora 消息通知服务器,然后 Agroa 消息通知服务器会通过 HTTPS POST 请求方法将事件通知投递给你的服务器。
频道事件回调
Agora 消息通知服务器以 HTTPS POST 请求方法向你的服务器发送频道事件消息通知回调。数据格式为 JSON,字符编码为 UTF-8,签名算法为 HMAC/SHA1 或 HMAC/SHA256。
请求的 Header
消息通知回调的 header
中包含以下字段:
请求的 Body
消息通知回调的请求包体包含以下字段:
消息通知回调的请求包体示例:
{
"noticeId": "2000001428:4330:107",
"productId": 1,
"eventType": 101,
"notifyMs": 1611566412672,
"payload": {...}
}
复制代码
频道事件类型
Agora 消息通知服务可以通知实时通信业务中的以下频道事件类型:
具体参数类型详情可见:频道事件回调 (agora.io)
验证签名
为提高 Agora 消息服务器和你的服务器之间的通信安全,Agora 使用签名机制进行身份验证。签名验证流程如下:
在配置 Agora 消息通知服务时,Agora 会生成一个密钥(secret)。你需要在开通服务时,向 Agora 技术支持获取并保存该密钥。
当 Agora 向你的服务器发送消息通知回调时,会使用密钥通过 HMAC/SHA1 和 HMAC/SHA256 算法生成签名值,并分别添加在 HTTPS 请求 header
的 Agora-Signature
和 Agora-Signature-V2
字段中。
收到回调后,你可以使用密钥和请求包体里的参数,选用 HMAC/SHA1 或 HMAC/SHA256 算法计算签名值,以验证 Agora-Signature
或 Agora-Signature-V2
。
Java 实现客户端解析数据案例
HMAC/SHA1
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class HmacSha {
// 将加密后的字节数组转换成字符串
public static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() < 2) {
sb.append(0);
}
sb.append(hex);
}
return sb.toString();
}
//HMAC/SHA1 加密,返回加密后的字符串
public static String hmacSha1(String message, String secret) {
try {
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(
"utf-8"), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(message.getBytes("utf-8"));
return bytesToHex(rawHmac);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
//拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 request_body 是反序列化之前的 binary byte array,不是反序列化之后的 object
String request_body = "{\"eventMs\":1560408533119,\"eventType\":10,\"noticeId\":\"4eb720f0-8da7-11e9-a43e-53f411c2761f\",\"notifyMs\":1560408533119,\"payload\":{\"a\":\"1\",\"b\":2},\"productId\":1}";
String secret = "secret";
System.out.println(hmacSha1(request_body, secret)); //033c62f40f687675f17f0f41f91a40c71c0f134c
}
}
复制代码
HMAC/SHA256
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class HmacSha {
// 将加密后的字节数组转换成字符串
public static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() < 2) {
sb.append(0);
}
sb.append(hex);
}
return sb.toString();
}
//HMAC/SHA256 加密,返回加密后的字符串
public static String hmacSha256(String message, String secret) {
try {
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(
"utf-8"), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(message.getBytes("utf-8"));
return bytesToHex(rawHmac);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
//拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 request_body 是反序列化之前的 binary byte array,不是反序列化之后的 object
String request_body = "{\"eventMs\":1560408533119,\"eventType\":10,\"noticeId\":\"4eb720f0-8da7-11e9-a43e-53f411c2761f\",\"notifyMs\":1560408533119,\"payload\":{\"a\":\"1\",\"b\":2},\"productId\":1}";
String secret = "secret";
System.out.println(hmacSha256(request_body, secret)); //033c62f40f687675f17f0f41f91a40c71c0f134c
}
}
复制代码
评论