python Agora SDK的简单使用
可以参考 AgoraRTC_local_face_regonization项目 进行理解
一、Agora RTC小试牛刀
参考 跑通示例项目
-
先在Agora控制台上创建自己的项目,拷贝你的appId; -
接着在主要证书那一栏,由于你没买agora服务(免费版每月会提供定量的音视频通话时长),此时需要点击”生成临时音视频token”,输入你的房间名称(比如TESTROOM),拷贝你的token -
接下来在 Web音视频快速体验 页面,输入所需要的四个参数: appId(必选),app token(必选),房间名称(必选),用户名(可选)
使用两台机子登录,效果显示如下:
二、AgoraRTC + pyQt5的初体验
三、python连接Agora(one2one代码解析)
agora sdk安装:
pip install agora-python-sdk
one2one代码解析:由于agora-python-sdk并没有提供python的API文档,这里参考C++ API文档进行理解。
参考
1、EnableVideo:启用视频模块
该方法可以在加入频道前或者通话中调用,在加入频道前调用则自动开启视频模块;在通话中调用则由音频模式切换为视频模式。调用 DisableVideo 方法可关闭视频模式。
public abstract int EnableVideo();
成功调用该方法后,远端会触发 OnRemoteVideoStateChanged 回调。
- 该方法设置的是内部引擎为启用状态,在离开频道后仍然有效。
- 该方法重置整个引擎,响应时间较慢,因此声网建议使用如下方法来控制视频模块:
2、SetupLocalVideo:通过VideoCanvas设置本地视图,绑定显示视窗
该方法初始化本地视图并设置本地用户视频显示属性,只影响本地用户看到的视频画面,不影响本地发布视频。调用该方法绑定本地视频流的显示视窗(view),并设置本地用户视图的渲染模式和镜像模式。
在 App 开发中,通常在初始化后调用该方法进行本地视频设置,然后再加入频道。退出频道后,绑定仍然有效,如果需要解除绑定,可以调用该方法将参数 view 设为 NULL。
public abstract int SetupLocalVideo(VideoCanvas canvas);
其中canvas为本地视频显示属性。详见 VideoCanvas。
3、SetupRemoteVideo:通过VideoCanvas设置远端用户视图,绑定显示视窗
该方法绑定远端用户和显示视图,并设置远端用户视图在本地显示时的渲染模式和镜像模式,只影响本地用户看到的视频画面。
调用该方法时需要指定远端视频的用户 ID,一般可以在进频道前提前设置好。如果无法在加入频道前得到远端用户的 ID,可以在收到 OnUserJoined 回调时调用该方法。
public abstract int SetupRemoteVideo(VideoCanvas canvas);
其中canvas为本地视频显示属性。详见 VideoCanvas。
4、VideoCanvas:视频显示设置
public class VideoCanvas
{
public VideoCanvas()
{
view = 0;
renderMode = (int) RENDER_MODE_TYPE.RENDER_MODE_HIDDEN;
channelId = "";
uid = 0;
mirrorMode = VIDEO_MIRROR_MODE_TYPE.VIDEO_MIRROR_MODE_AUTO;
}
}
-
view:用于显示视频的视图 -
renderMode:视频渲染模式,详见 RENDER_MODE_TYPE 。 -
channelId:()自从v3.0.0)频道名,即能够唯一标识频道、长度在 64 字节以内的字符。以下为支持的字符集范围(共 89 个字符) 该参数默认值为空字符 “”。如果用户是通过 IAgoraRtcEngine 类的 JoinChannel [2/2] 方法加入频道的,则将参数设为默认值。此时 VideoCanvas 设置的是该用户在频道内的视图。 -
uid:用户 ID。 -
mirrorMode: 对于本地用户:
- 如果使用前置摄像头,默认启动镜像模式。
- 如果使用后置摄像头,默认关闭镜像模式。
对于远端用户:默认关闭镜像模式。
5、具体python代码如下
class DisplayWindow(QtWidgets.QMainWindow, Ui_Display_MainWindow):
def __init__():
self.window1 = GLwindow()
self.window2 = GLwindow()
self.window3 = GLwindow()
self.gridLayout.addWidget(self.window1)
self.verticalLayout.addWidget(self.window2)
self.verticalLayout.addWidget(self.window3)
def joinChannel():
remoteWinId = self.window1.effectiveWinId().__int__()
localWinId = self.window2.effectiveWinId().__int__()
localWinId1 = self.window3.effectiveWinId().__int__()
self.rtc.initialize(appId, None, agorartc.AREA_CODE_GLOB & 0xFFFFFFFF)
self.rtc.enableVideo()
localVideoCanvas = agorartc.createVideoCanvas(localWinId)
localVideoCanvas1 = agorartc.createVideoCanvas(localWinId1)
res = self.rtc.setupLocalVideo(localVideoCanvas)
res = self.rtc.setupLocalVideo(localVideoCanvas1)
print(f"localVideo loading status = {res}")
self.rtc.joinChannel(token, chatroom_name, "", int(uid))
self.rtc.startPreview()
- 先是创建GLwindow()窗口视图,并与UI_Display_Window的布局控件进行绑定
- 接着获取GLwindow()窗口(视图)的Id。
- 使用createVideoCanvas和视图id(VideoCanvas.view = id)创建VideoCanvas画布(相当于Qlabel起到绘图的效果)
- 使用setupLocalVideo、setupRemoteVideo分别与本地视频流、remote视频流的绘制视窗进行绑定。
class MyRtcEngineEventHandler(agorartc.RtcEngineEventHandlerBase):
def onUserJoined(self, uid, elapsed):
global remoteWinId
if remoteWinId != -1:
remoteVideoCanvas = agorartc.createVideoCanvas(remoteWinId)
print(f"remoteVideoCanvas loading status = {remoteVideoCanvas}")
remoteVideoCanvas.uid = uid
remoteVideoCanvas.renderMode = agorartc.RENDER_MODE_FIT
res = self.rtc.setupRemoteVideo(remoteVideoCanvas)
print(f"remoteVideo loading status = {res}"
- 当监听到其他用户进入该channel,则根据上面GLWindow的effectiveId,创建画布
- 该画布需要设置uid,renderMode属性。
- 最后通过setupRemoteVideo 与View视窗进行绑定,完成绘制
6、另一种写法:使用MyVideoFrameObserver
Note:如果不对SDK返回的本地视频流或者remote视频流进行后处理的话,可以不使用上面的方法,直接先在界面中引入QLabel控件,用于图片的绘制。
class MyVideoFrameObserver(agorartc.VideoFrameObserver):
def onCaptureVideoFrame(self, width, height, ybuffer, ubuffer, vbuffer):
...
qImg_scaled = qImg.scaled(draw_label_1.width(), draw_label_1.height(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
pixmap = QtGui.QPixmap.fromImage(qImg_scaled)
draw_label_1.setPixmap(pixmap)
draw_label_2.setPixmap(pixmap)
def onRenderVideoFrame(self, uid, width, height, ybuffer, ubuffer, vbuffer):
global remoteRawDataCounter
...
假设窗口上用QLabel绘制本地的视频流和remote视频流,效果如下:
其中 thinkpad(教师端)和surface go(学生端)都是发流端,thinkpad设为大流,surface go设为小流。
这一点在下面的Agora SDK获取原生视频,Agora SDK音视频基本操作 这两小节有介绍。
四、通过Agora SDK获取原生视频
1、VideoFrameObserver 基础原理
参考Raw Video Data,Agora C++ API Reference for All Platforms
从音视频的采集,网络层对音视频的传输,以及客户端对接收到的音视频的渲染,raw视频数据的流程如下:
Follow these steps to implement the raw data functions in your project:
- Before joining the channel, call registerVideoFrameObserver to register a video frame observer.
- After successful registration, the SDK sends the raw video data via onCaptureVideoFrame, onPreEncodeVideoFrame, or onRenderVideoFrame.
- After acquiring the raw data, you can process the data based on your scenario and send the processed data to the SDK via the callbacks mentioned in step 2.
翻译过来就是
- 客户端在加入到房间(joinChannel)之前,会先注册 视频帧观测器(registerVideoFrameObserver)
- 注册成功后,agora SDK会通过onCaptureVideoFrame, onPreEncodeVideoFrame, 或者是onRenderVideoFrame 向客户端返回raw视频数据。
- 接收到raw视频数据之后,你可以根据你的应用场景处理该数据(人脸视频,疲劳检测等),然后通过回调函数发送处理好的视频数据给agora SDK,或者是你的渲染设备(显示器)。
现在介绍一下这三个函数:
- registerVideoFrameObserver:注册视频帧观测器,如果在创建该实例时传入NULL,则不会进行register.
- onCaptureVideoFrame:获取本地camera的raw视频流,可以进行预处理或后处理
- onRenderVideoFrame:获取remote用户的raw视频流,可以进行后处理
2、AgoraRTC + face_recognition项目
用如下命令导入项目
git clone https://github.com/AgoraIO-Community/Agora-Python-QuickStart.git
然后进入face_recognition文件夹,运行face_recog.py,但是在windows10上直接运行会存在以下两个问题:
-
存在问题1:该项目的qt界面显示不了remote user的视频流 经过比较one2one项目(上一节中,one2one.py能获取到remote user的视频流),发现主要原因不是出在remote user视频流获取不到的问题上: remoteVideo loading status = 0
而是出在pyqt的代码:MainWindow.py上,该文件创建了3个gridLayoutWidget控件,其中remote user的视频流窗口被黑色背景覆盖了,导致remote user视频流显示视频。 解决方法:去除掉该项目中下面的冗余代码
-
存在问题2:face_recognition人脸识别第三方包在windows上编译存在问题,在linux上则没问题 这里在获取本地原生视频流之后,使用SCRFD模型进行人脸定位。 解决方法:恢复之前去除掉的冗余代码 修改代码如下: class MyVideoFrameObserver(agorartc.VideoFrameObserver):
def onCaptureVideoFrame(self, width, height, ybuffer, ubuffer, vbuffer):
global localRawDataCounter
print("onCaptureVideoFrame: width {}, height {}, ybuffer {}, ubuffer {}, vbuffer {}".format(width, height,
ybuffer, ubuffer,
vbuffer))
'''Step1:用PIL Image从ybuffer中读取YUV数据'''
rgba_array = (ctypes.c_ubyte * (width * height * 4)).from_address(ybuffer)
rgba_img = Image.frombuffer('RGBA', (width, height), rgba_array, 'raw', 'RGBA', 0, 1)
'''Step2: 将PIL格式图片转化成cv2格式图片,并使用SCRFD模型进行人脸定位'''
cv2_img = cv2.cvtColor(np.asarray(rgba_img), cv2.COLOR_RGBA2BGR)
dets = scrfd_model.detect_faces(cv2_img)[0]
for det in dets:
x1, y1, x2, y2, _ = det
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
cv2.rectangle(cv2_img, (x1, y1), (x2, y2), color=(0, 0, 255), thickness=2)
'''Step3:根据draw_label和图片尺寸比例,绘制到QT GUI中'''
height, width, bytesPerComponent = cv2_img.shape
bytesPerLine = 3 * width
cv2.cvtColor(cv2_img, cv2.COLOR_BGR2RGB, cv2_img)
qImg = QImage(cv2_img.data, width, height, bytesPerLine, QImage.Format_RGB888)
qImg_scaled = qImg.scaled(draw_label.width(), draw_label.height(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
pixmap = QtGui.QPixmap.fromImage(qImg_scaled)
draw_label.setPixmap(pixmap)
def onRenderVideoFrame(self, uid, width, height, ybuffer, ubuffer, vbuffer):
global remoteRawDataCounter
print("onRenderVideoFrame: uid {}, width {}, height {}, ybuffer {}, ubuffer {}, vbuffer {}".format(uid, width, vbuffer))
并恢复问题1中的冗余代码: self.gridLayoutWidget_3 = QtWidgets.QWidget(self.centralwidget)
self.gridLayoutWidget_3.setGeometry(QtCore.QRect(520, 380, 251, 161))
self.gridLayoutWidget_3.setObjectName("gridLayoutWidget_3")
self.gridLayout_3 = QtWidgets.QGridLayout(self.gridLayoutWidget_3)
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
self.gridLayout_3.setObjectName("gridLayout_3")
self.draw_label = QtWidgets.QLabel(self.gridLayoutWidget_3)
self.draw_label.setText("")
self.draw_label.setObjectName("draw_label")
self.gridLayout_3.addWidget(self.draw_label, 0, 0, 1, 1)
在thinkpad端运行face_recognition项目,在surface go端运行one2one项目,效果如下:
-
Thinkpad端: -
surface go端: 可以发现onCaptureVideoFrame() 在获取本地原生视频流,并完成后处理之后,并不会将后处理视频推送到remote user,原因可以从上面的VideoFrameObserver 基础原理得知:onCaptureVideoFrame() 是在joinChannel() 之后执行的,在joinChannel阶段已经将raw 视频推送给remote user. -
存在问题3:face_recognition项目打包出现问题,exe无法执行 主要原因是SCRFD模型文件(scrfd_500m_kps.onnx)没有找到,把scrfd_500m_kps.onnx文件复制到dist文件夹中,仍然报错: 解决方法:这里使用相对路径来读取文件,而不是项目路径,修改代码如下: import sys
import os
from pathlib import Path
curPath = os.path.abspath(os.path.dirname(__file__))
class SCRFD():
def __init__(self, onnxmodel, confThreshold=0.5, nmsThreshold=0.5):
onnxmodel = onnxmodel
self.inpWidth = 640
self.inpHeight = 640
self.confThreshold = confThreshold
self.nmsThreshold = nmsThreshold
print(f"SCRFD: {onnxruntime.get_device()}")
self.ort_sess = onnxruntime.InferenceSession(onnxmodel,providers=['CPUExecutionProvider'])
self.keep_ratio = True
...
将项目打包成exe,surface go端检测效果如下(因为用的是pentium处理器进行人脸检测,延迟有点高)
整个项目已上传到gitee
git clone https://gitee.com/wangxiaoxi-1/AgoraRTC_local_face_regonization.git
五、Agora SDK对音视频的基本操作
1、创建房间并生成Token
参考
-
Token鉴权原理 鉴权是指在用户访问你的系统前,对其进行身份校验。用户在使用 Agora 服务,如加入音视频通话或登录信令系统时,Agora 使用 Token 对其鉴权。 下图展示了鉴权的基本流程: Token 在 app 服务器上生成,其最大有效期为 24 小时。当用户从你的 app 客户端连接至 Agora 频道时,Agora 平台会读取该 Token 中包含的信息,并进行校验。Token 包含以下信息:
- 你在 Agora 控制台创建项目时生成的 App ID
- 频道名
- 用户 ID
- 用户权限,如是否能发流或收流
- Token 过期的 Unix 时间戳
虽然 Token 最大有效期为 24 小时,为保证通信安全,Agora 建议按照你的业务场景需要设置较短的有效期,例如一小时。 -
部署 Token 服务器(每一个user生成一个token) Token 需要在你的服务端部署生成。当客户端发送请求时,服务端部署的 Token Generator 会生成相应的 Token,再发送给客户端。 Token 生成器代码 Agora 在 GitHub 上提供一个开源的 AgoraDynamicKey 仓库,支持使用 C++、Java、Go 等语言在你自己的服务器上生成 Token。 这里以python为例:
语言 | 算法 | 核心方法 | 示例代码 |
---|
Python | HMAC-SHA256 | buildTokenWithUid | RtcTokenBuilderSample.py | Python3 | HMAC-SHA256 | buildTokenWithUid | RtcTokenBuilderSample.py |
API 参考
参数 | 描述 |
---|
role | 用户权限,分为发流用户和接收用户。参数决定用户是否能在频道中发流。Role_Publisher(1):(默认)用户有发流权限,即用户可在频道中发流。Role_Subscriber(2):用户有接收权限,即用户可在频道中接收,但不可发流。该参数仅在开启连麦鉴权后才生效。详情参考如何使用连麦鉴权功能? | uid | 待鉴权用户的用户 ID 32 位无符号整数,范围为1到 (232 - 1), 并保证唯一性。 如不需对用户 ID 进行鉴权,即客户端使用任何 uid 都可加入频道,请把 uid 设为 0。 | privilegeExpiredTs | oken 过期的 Unix 时间戳,单位为秒。该值为当前时间戳和 Token 有效期的总和。 例如,如果你将 privilegeExpiredTs 设为当前时间戳再加 600 秒,则 Token 会在 10 分钟内过期。 Token 的最大有效期为 24 小时。 如果你将此参数设为 0,或时间长度超过 24 小时,Token 有效期依然为 24 小时。 | appId | 你在 Agora 控制台创建项目时生成的 App ID。 | appCertificate | 你的项目的 App 证书。 | channelName | 频道名称,长度在 64 个字节以内。以下为支持的字符集范围 ? 26 个小写英文字母 a-z;? 26 个大写英文字母 A-Z;? 10 个数字 0-9;? 空格;? 标点符号和其他符号, 包括: “!”, “#”, “$”, “%”, “&”, “(”, “)”, “+”, “-”, “:”, “;”, “<”, “=”, “.”, “>”, “?”, “@”, “[”, “]”, “^”, “_”, " {", “}”, “”, “~”, “,”. |
这里使用 python-token-builder 获取token from agora_token_builder import RtcTokenBuilder
import time
import random
appId = "ea8cafc8c9e943bcacc2f59802f72b9f"
appCertificate = "60bf2d9851ef4c079795df8f1193d410"
channelName = "TESTROOM"
uid = random.randint(1, 230)
expirationTimeInSeconds = 3600
currentTimeStamp = int(time.time())
privilegeExpiredTs = currentTimeStamp + expirationTimeInSeconds
role = 1
token = RtcTokenBuilder.buildTokenWithUid(appId, appCertificate, channelName, uid, role, privilegeExpiredTs)
print(f"token = {token}, uid = {uid}")
复制生成的token,随机生成的uid,以及你的appId,chatname,就可以通过声网SDK访问SD-RTN的实时传输网络了。 -
存在问题:其他笔记本无法通过该token和uid进入channel 问题描述: 在其他笔记本或者本地的 Basic Agora call 页面都无法识别该token和uid;这里先用RESTful API测一下该channel是否有users:实验证明,真的没有。。。 先使用 服务器 生成的token,再利用agora SDK获取本地视频流之后,通过 RESTful API 获取客户ID和密钥,通过**/dev/v1/channel/user/{appid}/{channelName}** 获取用户列表,会发现在该项目(appId)中,指定的房间(chatroom_name)内并没有user信息。
{
"success": true,
"data": {
"channel_exist": false
}
}
而在使用agora项目中生成的临时音视频token,就可以识别到用户列表信息。 只连接Thinkpad:uids = [3] {
"success": true,
"data": {
"channel_exist": true,
"mode": 1,
"total": 1,
"users": [
3
]
}
}
同时连接surface go之后:uids = [3, 12] {
"success": true,
"data": {
"channel_exist": true,
"mode": 1,
"total": 2,
"users": [
3,
12
]
}
}
解决方法: 主要原因是用错了token生成方法(用buildTokenWithAccount ,不用buildTokenWithUid )。 基于python的token服务器代码修改如下:
from agora_token_builder import RtcTokenBuilder
import time
import random
appId = "ea8cafc8c9e943bcacc2f59802f72b9f"
appCertificate = "60bf2d9851ef4c079795df8f1193d410"
channelName = "TESTROOM"
userAccount = "13"
expirationTimeInSeconds = 3600
currentTimeStamp = int(time.time())
privilegeExpiredTs = currentTimeStamp + expirationTimeInSeconds
role = 1
token = RtcTokenBuilder.buildTokenWithAccount(appId, appCertificate, channelName, userAccount, role, privilegeExpiredTs)
print(f"token = {token}, uid = {userAccount}")
这里主要区别就是uid是int类型,userAccount是字符串类型的数字。 这里使用服务器,在规定userAccount下创建两个token token = 006ea8cafc8c9e943bcacc2f59802f72b9fIABu6cf9QoH+5NEg0ehXRzzv3sWy+Yuf4WNIFw2PKM5WPOoIuoDNRFNPIgC6aJcDituNYgQAAQAamIxiAgAamIxiAwAamIxiBAAamIxi, uid = 12
token = 006ea8cafc8c9e943bcacc2f59802f72b9fIAAO3A+LLWVrjyA1xAWGInpNTupO5J+Kogf50fafMavwBOoIuoBbdFQ4IgB8rRIEoNyNYgQAAQAwmYxiAgAwmYxiAwAwmYxiBAAwmYxi, uid = 13
分别使用thinkpad,surface go以不同的token加入到房间中(TESTROOM) Thinkpad: surface go: 接着用RESTful API查看是否有users在TESTROOM房间内,调用**/dev/v1/channel/user/{appid}/{channelName}** ,输入appId,chatroom_name
{
"success": true,
"data": {
"channel_exist": true,
"mode": 1,
"total": 2,
"users": [
12,
13
]
}
}
这里搭建的token服务器 和 agora控制台生成的临时音视频token不同 的是:
- 自己搭建的token服务器需要指定uid或者userAccount才能生成token,是和指定用户绑定的,不同用户间的token是不同的。
- agora控制台生成的临时音视频token不需要传入uid,不同用户使用相同token之后,只需要输入不同的uid即可区分不同的用户。
换句话说自建的服务器是直接把uid编码成token,而agora控制台的则通过token + uid的拼接来区分用户。
2、关闭 / 打开本地视频(暂时没找到agora python sdk的源码)
-
参考资料
参考
EnumerateVideoDevices 获取系统中所有的视频设备列表。 public abstract DeviceInfo[] EnumerateVideoDevices();
返回值
- 方法调用成功:返回一个 DeviceInfo 数组,其中包含系统中所有视频设备。
- 方法调用失败: NULL。
GetDevice 获取当前使用的视频采集设备。 public abstract string GetDevice();
返回值:视频采集设备。 SetDevice 通过设备 ID 指定视频采集设备。 public abstract int SetDevice(string deviceId);
注意:插拔设备不会改变设备 ID。 参数deviceId设备 ID。可通过调用 EnumerateVideoDevices 方法获取。 -
尝试 deviceId_output = "USB\VID_5986&PID_0706&REV_0012&MI_00"
deviceName = "Integrated Camera"
devices = self.videoDeviceManager.getCurrentDevice(deviceId_output)
这么写会报错:#{TypeError}in method 'VideoDeviceManager_getCurrentDevice', argument 1 of type 'agora::common::VideoDeviceManager *' 主要原因是 VideoDeviceManager没有构造函数,然而在创建继承于该类的MyVideoDeviceManager之后,使用getCurrentDevice()依然报错。 参考 Switching audio input/output device 原来 VideoDeviceManager是通过如下创建的 self.rtc = agorartc.createRtcEngineBridge()
self.video_device_manager, err = self.rtc.createVideoDeviceManager()
但是对于*videoDeviceManager.getDevice() *等函数不会使用,如何关闭摄像头,这里暂时不考虑,先研究一下如何调节本地麦克风以及扬声器的音量。
3、调节本地 / 远端音频音量
1)音频播放设备(PlayBack)
-
参考资料 IAgoraRtcAudioPlaybackDeviceManager类,用于关于音频播放设备。
-
SetPlaybackDevice指定播放设备。 publicabstractint SetPlaybackDevice(string deviceId);
参数deviceId通过 deviceID 指定播放设备。由 EnumeratePlaybackDevices 获取。插拔设备不会影响 deviceId。 返回值
-
SetPlaybackDeviceVolume设置播放设备音量。 public abstract int SetPlaybackDeviceVolume(int volume);
参数volume播放设备音量。取值可在 [0,255]。 返回值
-
SetPlaybackDeviceMute 设置播放设备静音。 **public** **abstract** **int** SetPlaybackDeviceMute(**bool** mute);
参数mute是否设置播放设备为静音: ? true : 播放设备设为静音。 ? false : 播放设备不设为静音。 返回值
-
尝试(看agorartc源码) 这里注意参考的资料(3.6.2)和源码(2.9.0)的agoraSDK版本不一致 有效果:使用agorartc实例,直接通过setPlaybackDeviceVolume() 设置远程用户(PlayBack)的音量(The value ranges between 0 (lowest volume) and 255 (highest volume) ) audioDevice = self.rtc.setPlaybackDeviceVolume(volume=0)
没效果:使用agorartc内置函数(createAudioPlaybackDeviceManager)创建的audio_playback_device_manager对象,音量设置无效果
self.audio_playback_device_manager, err = self.rtc.createAudioPlaybackDeviceManager()
audioDevice = self.audio_playback_device_manager.setDeviceVolume(volume=0)
2)音频采集设备(Recording)
-
参考资料 IAgoraRtcAudioRecordingDeviceManager类,用于关于音频采集设备
-
EnumerateRecordingDevices 获取系统中所有的音频采集设备列表。 publicabstract DeviceInfo[] EnumerateRecordingDevices();
返回值
- 方法调用成功,返回一个 DeviceInfo 数组,包含所有音频采集设备的设备 ID 和设备名称。
- 方法调用失败:NULL。
-
SetRecordingDevice 指定音频采集设备。 publicabstractint SetRecordingDevice(string deviceId);
参数deviceId音频采集设备的 Device ID。可通过 EnumerateRecordingDevices 获取。插拔设备不会影响 deviceId。 返回值
-
尝试(看agorartc源码) 这里注意参考的资料(3.6.2)和源码(2.9.0)的agoraSDK版本不一致 有效果:使用agorartc实例,通过adjustRecordingSignalVolume() 直接设置本地用户(Recording)的音量(0: Mute. **100: Original volume. ) audioDevice = self.rtc.adjustRecordingSignalVolume(volume=0)
audioDevice = self.rtc.adjustRecordingSignalVolume(volume=15)
没效果:使用AudioRecordingDeviceManager.setDeviceMute() 设置静音无效果 self.audio_recording_device_manager, err = self.rtc.createAudioRecordingDeviceManager()
audioDevice = self.audio_recording_device_manager.setDeviceMute(mute=True)
使用AudioRecordingDeviceManager.setDeviceVolume() 设置音量无效果 self.audio_recording_device_manager, err = self.rtc.createAudioRecordingDeviceManager()
audioDevice = self.audio_recording_device_manager.setDeviceVolume(volume=5)
注意一点:当笔记本插入耳机时,agoraSDK会优先选择耳机作为音频采集设备。
4、多人聊天场景
参考
双流模式:多人视频通信或直播场景下,为降低带宽占用、确保通话流畅,Agora 建议所有发流端开启双流模式,接收端设置 1-N 订阅模式,即订阅一路大流和 N 路小流。
Agora 建议你在界面布局里采用一大多小的窗口布局:大窗口显示大流的画面,小窗口显示小流的画面。
-
双流模式实现原理: 1、开启双流模式: 各发流端在加入频道前或者后,都可以调用 enableDualStreamMode 方法开启双流模式。开启后,SDK 会在发送视频流的同时,额外发送一路分辨率低、码率低的视频流。其中,原视频流也称为大流,分辨率和码率更低的那路流则为小流。 2、设置订阅流类型: 接收端再调用 setRemoteVideoStreamType 方法,将订阅的一路视频流设为大流,其它路视频流均设置为小流。 3、设置小流视频属性: 为保证通信流畅,Agora 建议你联系技术支持自定义小流的视频属性,防止因小流码率过高而造成带宽压力。 小流最高可支持分辨率 640 × 480,帧率 30,码率 1000。但是为保证通信质量,Agora 建议自定义的小流分辨率不超过 480 × 360,码率不超过 200,且小流帧率不能超过大流帧率。 4、集成注意事项: 为保证通信质量,我们建议大流视频分辨率不超过 640 × 480,帧率不超过 15。 通话或直播过程中,如果有发流用户离开频道,接收端可以在收到该用户离线的回调后,调用 setupRemoteVideo 方法,将该用户的 view 设为空,app 即可彻底释放该用户占用的 view 。 大小流推荐属性: 在网络条件受限的情况下,如果发送端没有调用 EnableDualStreamMode (false) 关闭双流模式,接收端可以选择接收大流还是小流。其中,大流为高分辨率高码率的视频流,小流则是低分辨率低码率的视频流。 -
python SDK实现双流模式 通过文档的意思,需要发送端先设置双流模式,接收端才可以按需求选择小流还是大流。但这里只找到获取remote端双流模式的方法,如果有小伙伴知道发送端如何设置双流模式的话,可以在评论区里留言。
streamType = agorartc.REMOTE_VIDEO_STREAM_LOW
remote_uid = 13
remote_streamType_setting = self.rtc.setRemoteVideoStreamType(remote_uid,streamType)
print(f"remote_streamType_setting = {remote_streamType_setting}")
|