实时音视频 SparkRTC
实时音视频 SparkRTC
- 最新动态
- 服务公告
- 产品介绍
- 快速入门
- 用户指南
- 最佳实践
- API参考
-
客户端SDK参考
- 使用前必读
- SDK概述
- 隐私声明
- 合规使用指南
- Android SDK
- iOS/macOS SDK
- All Platform C++ SDK
- Web SDK
- 接入鉴权
- 附录
- 修订记录
-
常见问题
-
产品咨询
- SparkRTC支持哪些平台?
- SparkRTC Demo怎么体验?
- 在进行Windows的Demo使用或集成SDK时,提示微软C++标准库缺失,该如何获取?
- SparkRTC支持接入微信小程序吗?
- 实时音视频是什么?
- 实时音视频和直播有什么区别?
- SparkRTC是否支持在国外使用?
- SparkRTC RoomID是什么?取值区间值是多少?
- SparkRTC最多可以同时创建多少个房间?
- SparkRTC UserID是什么?取值范围是多少?
- 怎么理解SparkRTC的角色Role?
- SparkRTC最多可以支持多少个人同时视频通话?
- 实时音视频的时延是多少?
- SparkRTC是否支持连麦互动?
- SparkRTC是否支持双向的桌面推送?
- 实时音视频支持私有化部署吗?
- PC端实时音视频支持USB摄像头吗?
- 计费购买
- Signature相关
- 录制相关
-
SDK使用
- 移动端(Android/iOS)创建一个房间,是否支持PC端(MAC/Windows)接入?
- iOS平台如何缩减安装包体积?
- Android平台如何缩减安装包体积?
- iOS端是否可以监听远端离开房间?
- 背景音乐是否支持循环播放?
- SparkRTC是否支持调整背景音乐的播放进度?
- 支持监听房间成员进出房间吗?
- 如何监测断网和重连?
- 支持首帧渲染回调吗?能否监听画面开始渲染?
- 如何设置上行视频码率、分辨率和帧率?
- 有没有推荐的画面质量(码率、分辨率、帧率)相关参数配置?
- 如何实现横屏视频通话?
- 是否支持网络测速?如何操作?
- 是否支持音量大小提示?
- 自定义渲染支持哪些格式?
- SparkRTC支持iOS后台运行吗?
- 移动端如何实现录屏(屏幕分享)?
- 多人视频会议中,移动端和Web端可以进入同一房间吗?
- SparkRTC的Web端、移动端、PC端是不是同步的?
- Android SDK是否支持自定义采集数据?
- SparkRTC支持旁路推流吗?
- Windows端如何才能采集到被分享应用播放的声音?
- SDK是否支持断线重连机制?
- 如何创建房间?
- 两台手机同时运行SparkRTC APP,为什么无法互通?
- 鉴权是如何生成的?在什么时候生成?
- onVideoStats、onAudioStatus、onSubStreamStats回调触发时为什么程序会崩溃?
- 为什么本端听筒能听到自己的声音?
- setExternalAudioCapture(音频自采集)、setExternalVideoCapture(视频自采集)、setExternalMediaFrameOutput(视频自渲染)能在进入房间后开启吗?
- SparkRTC是否支持设置镜像画面?
- SparkRTC音视频过程中的上下行码率、分辨率、丢包率、音频采样率等信息怎么获取?
- SparkRTC是否支持设置本地采集音量?是否支持设置每个远端用户的播放音量?
- enableLocalVideo和muteLocalVideo有什么区别?
- enableLocalAudioStream和muteLocalAudio有什么区别?
- SparkRTC是否支持对房间进行权限校验?
- 接入PC端是否支持屏幕分享功能?
- SparkRTC是否支持将本地视频文件分享到实时音视频中?
- SparkRTC是否支持单纯的实时音频?
- 一个房间里可以同时有多路屏幕分享吗?
- SparkRTC是否支持1080P?
- 是否支持视频画面截图功能?
- 播放背景音乐是否支持在线音乐?
- 同一个页面中,可以创建N个RTC对象,通过N个UserID,分别登录到N个房间吗?
- Web端用宽高设置的推流分辨率适用于所有浏览器吗?
- Web端SDK在使用过程中拔掉摄像头,摄像头列表里面的数据如何清除?
- Web端SDK可以获取当前音量大小吗?
- Web端支持监听远端离开房间吗?
- Web端通话过程中为什么会出现回声、杂音、噪声、声音小?
- Web端如何切换摄像头和麦克风?
- SparkRTC支持白板功能吗?
- 相同UserID支持同时进入多个房间吗?
- SparkRTC房间的生命周期是多久?
- SparkRTC多人视频通话支持蓝牙耳机吗?
- SparkRTC多人视频通话支持仅订阅2/3/4人音频吗?
- 指定窗口分享,当窗口大小变化时,视频流的分辨率会跟着变化吗?
- 视频通话支持悬浮窗、大小画面切换功能吗?
- 发送自定义消息接口能实现聊天室、弹幕等功能吗?
- 支持查询所有的UserID吗?
- Web SDK中的checkSystemRequirements接口为什么会调用异常?
- 问题排查
-
产品咨询
- 文档下载
- 通用参考
链接复制成功!
接入鉴权
为保证SparkRTC的通信安全,当用户加入房间时,华为云SparkRTC服务需要对其进行接入鉴权。本章节主要介绍华为云SparkRTC接入鉴权的实现原理及鉴权签名的生成方法。
鉴权原理
华为云SparkRTC系统使用数字签名作为接入鉴权方式,需要在SDK加入房间时设置“signature”和“ctime”。“signature”为标识签名,由租户使用华为云SparkRTC提供的“app_key”,“app_id”以及当前的“room_id”,“user_id”,“ctime”,按照华为SparkRTC的签名生成样例自行生成。具体参数说明请参见表1。
//认证用的app_key和app_id硬编码至代码中或以明文形式存储会有极大风险。建议密文形式配置存储在文件或者环境变量中,使用时解密,以确保安全。本例以app_key和app_id存放至环境变量为例,运行前请先在本地环境中设置完成环境变量APP_KEY和APP_ID。 app_key = System.getenv("APP_KEY"); app_id = System.getenv("APP_ID"); signature = hmacSha256(app_key,(app_id + room_id + user_id + ctime))
参数 |
说明 |
---|---|
app_key |
华为云SparkRTC针对每个app生成的鉴权密钥,需要安全保存,谨防泄漏。 app_key的获取方法请参见如何获取密钥?。 |
app_id |
华为云SparkRTC生成的应用ID。 app_id请在实时音视频控制台的“应用管理”中获取。 |
room_id |
租户自行创建的房间ID。 |
user_id |
租户接入华为云SparkRTC系统的用户ID。 |
ctime |
签名鉴权的过期时间。是系统当前UTC时间(unix时间戳)加上鉴权过期时间(推荐2小时,最长需要小于12小时)。单位为秒。 ctime为创建时间+过期时间,例如,当前时间为9点,鉴权过期时间为30分钟,则ctime为9点30分。即超过9点30分后,signature签名将失效。 |
建议租户构建自己的应用签名分发服务器,以防止“app_key”下沉到终端APP的过程中造成不必要的泄漏,鉴权原理如图1所示。
签名生成方法
您可以参考如下方法生成对应的签名。
- 将“app_id”、“room_id”,“user_id ”和“ctime”拼接为一个字符串。
1 2
long ctime = System.currentTimeMillis() / 1000 + 60 * 60; //有效时间为1小时,单位是秒 String content = app_id + "+" + room_id + "+" + user_id + "+" + ctime;
- 使用“app_key”,通过HMAC-SHA256方式将字符串“content”进行加密,得到签名字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
String signatureStr = hmacSha(appKey, content, "HmacSHA256"); static String hmacSha(String KEY, String VALUE, String SHA_TYPE) { try { SecretKeySpec signingKey = new SecretKeySpec(KEY.getBytes("UTF-8"), SHA_TYPE); Mac mac = Mac.getInstance(SHA_TYPE); mac.init(signingKey); byte[] rawHmac = mac.doFinal(VALUE.getBytes("UTF-8")); byte[] hexArray = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' }; byte[] hexChars = new byte[rawHmac.length * 2]; for (int j = 0; j < rawHmac.length; j++) { int v = rawHmac[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } catch (Exception ex) { throw new RuntimeException(ex); } }
签名生成样例
为防止“app_key”密钥泄漏,建议您配置自己的应用签名分发服务器,向服务器传入“app_id”、“room_id”,“user_id”和“ctime”后,由服务器返回签名。详细代码示例如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
package com.xxx.xxx.utils; import androidx.annotation.NonNull; import com.alibaba.fastjson.JSON; import com.huawei.rtcdemo.Constants; import java.io.IOException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import okhttp3.Response; public class SignatureUtil { private static final String TAG = "SignatureUtil"; public interface onSignatureSuccess { void onSuccess(String signature); } public static void getSignature(String appid, String roomid, String userid, long ctime, String signatureKey, onSignatureSuccess callback) { if (Constants.RTC_SIGNATURE_USE_LOCAL) { getSignatureLocal(appid, roomid, userid, ctime, signatureKey, callback); } else { getSignatureRemote(appid, roomid, userid, ctime, callback); } } private static void getSignatureLocal(String appid, String roomid, String userid, long ctime, String signatureKey, @NonNull onSignatureSuccess callback) { String content = appid + "+" + roomid + "+" + userid + "+" + ctime; // here "+" is real char in content. String signature = SignatureUtil.hmacSha256(signatureKey, content); callback.onSuccess(signature); } private static void getSignatureRemote(String appid, String roomid, String userid, long ctime, @NonNull onSignatureSuccess callback) { new Thread(new Runnable() { @Override public void run() { HttpUtil httpUtil = new HttpUtil(); // Constants.RTC_SIGNATURE_URL:带用户自己应用签名的分发服务器地址 String url = Constants.RTC_SIGNATURE_URL + "?appid=" + appid + "&roomid=" + roomid + "&userid=" + userid + "&ctime=" + ctime; Response response = httpUtil.sendGetMethodWithHead(url, "X-AUTH-TOKEN", Constants.RTC_AUTH_TOKEN); if (response == null) { return; } if (response.isSuccessful()) { try { String json = response.body().string(); String signature = JSON.parseObject(json).getString("signature"); callback.onSuccess(signature); } catch (IOException e) { LogUtil.e(TAG, "getSignature failed:" + e.getMessage()); } } else { LogUtil.e(TAG, "getSignature failed!"); } } }).start(); } public static String hmacSha256(String KEY, String VALUE) { return hmacSha(KEY, VALUE, "HmacSHA256"); } private static String hmacSha(String KEY, String VALUE, String SHA_TYPE) { try { SecretKeySpec signingKey = new SecretKeySpec(KEY.getBytes("UTF-8"), SHA_TYPE); Mac mac = Mac.getInstance(SHA_TYPE); mac.init(signingKey); byte[] rawHmac = mac.doFinal(VALUE.getBytes("UTF-8")); byte[] hexArray = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' }; byte[] hexChars = new byte[rawHmac.length * 2]; for (int j = 0; j < rawHmac.length; j++) { int v = rawHmac[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } catch (Exception ex) { throw new RuntimeException(ex); } } } |