IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> python Agora SDK的简单使用 -> 正文阅读

[Python知识库]python Agora SDK的简单使用

python Agora SDK的简单使用

可以参考 AgoraRTC_local_face_regonization项目 进行理解

一、Agora RTC小试牛刀

参考 跑通示例项目

  • 先在Agora控制台上创建自己的项目,拷贝你的appId

  • 接着在主要证书那一栏,由于你没买agora服务(免费版每月会提供定量的音视频通话时长),此时需要点击”生成临时音视频token”,输入你的房间名称(比如TESTROOM),拷贝你的token

  • 接下来在 Web音视频快速体验 页面,输入所需要的四个参数

    appId(必选),app token(必选),房间名称(必选),用户名(可选)

使用两台机子登录,效果显示如下:

在这里插入图片描述

二、AgoraRTC + pyQt5的初体验

  • Pycharm配置External Tools: Qt DesignerpyUIC

    pyUIC配置:

    Qt Designer配置:

  • AgoraRTC + PyQt5项目打包成exe

    • pyInstaller的使用及优缺点

      pyinstaller -F main.py 打包exe(运行速度慢)
      pyinstaller -F-w main.py 不带控制台的打包
      pyinstaller -F-w-i app.ico main.py 打包指定exe图标打包
      pyinstaller --distpath Release/ -w -i x.ico --clean main.py  #如果不指定 --dispatch 的话,最后会默认发布到 dist 目录下
      

      Note:在使用pyinstaller进行打包时,最好是在conda虚拟环境下,这样导出来的包比较小

      参考

    • 打包 agora-python-quickStart 项目

      直接导入 agora-python-quickStart 项目(requirements.txt里的环境都装在了虚拟环境后)

      git clone https://github.com/AgoraIO-Community/Agora-Python-QuickStart.git
      

      进入虚拟环境后,安装pyinstaller

      pip install pyinstaller
      

      进入basic_one_to_one_video >> one2one.py 打包(选择主程序)

      pyinstaller -F one2one.py
      

      以上命令在打包过程中,会先生成one2one.spec文件(相当于pyi-makespec –F one2one.py),然后再完成exe打包(相当于pyinstaller one2one.spec)

      打包完成之后,会产生两个文件夹:build和dist:

      • 其中build/xref-one2one.html包含了已经打包好的第三方库,warn-one2one.txt包含了未打包的库。

      • 双击dist中main.exe即可运行,效果如下,说明已经进入agora创建的频道了:

        在这里插入图片描述

      • 使用另一台笔记本(Surface go),在web端打开Basic Video Call,这时windows桌面应用提示有第二个用户进入频道,效果如下:

        在这里插入图片描述

    • 存在问题:在其他笔记本上运行不起来,提示连接不上agora channel

      在本地笔记本(Thinkpad)上可以执行exe,但是把exe放到另一台笔记本(Surface go)上运行就有问题,AppID,token,chatname都输入了(两个机子所连接的房间参数是一致的!!!),但surface go上的两个窗口总是黑屏,并提示:

      在这里插入图片描述

      根据报错信息,我们在agora官网可以看到:

      101:The specified App ID is invalid. Please rejoin the channel with a valid App ID.

      • 解决方法:在另一台笔记本上登录agora官网,获取新的AppId(没错就是这么一个bug,之前AppId填得再一致都不行,需要重新在新机子上刷新AppId;后来才发现thinkpad登录的是未实名认证的账号,需要完成实名认证AppId才有效)

        在另一台笔记本(surface go)上登录agora RTC控制台,这时会发现AppId和之前(thinkpad)登录的是不同的,用新的AppId登录即可。

        在这里插入图片描述

        在这里插入图片描述

    Note

    • 在打包成exe之前,建议用print打印agora RTC连接的状态码,方便定位错误。

    • 打包后的exe可以在windows环境中直接使用,不需要安装VisualC++ 14环境,这里用3台电脑试了得出的结论:thinkpad(VC++),surface go,tianyi(VC++)

    • 在进行RTC连接的时候,核心语句是joinChannel(),用于加入房间

      res = self.rtc.joinChannel(token, channelName, "", int(uid))
      

    pyinstaller打包常见问题

三、python连接Agora(one2one代码解析)

agora sdk安装

pip install agora-python-sdk  #Use Agora RTC SDK with Python  注意先安装VisualC++ 2015及以上版本,用于编译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()   #remote 视频流
            self.window2 = GLwindow()      #local 视频流
            self.window3 = GLwindow()   #local 视频流
            self.gridLayout.addWidget(self.window1)   #窗口1放在gridLayout布局中 GLwindow()
            self.verticalLayout.addWidget(self.window2)  #窗口2放在verticalLayout布局中  GLwindow()
            self.verticalLayout.addWidget(self.window3)  #窗口2放在verticalLayout布局中  GLwindow()

        def joinChannel():
            remoteWinId = self.window1.effectiveWinId().__int__()  #teacher视频端
            localWinId = self.window2.effectiveWinId().__int__()   #local视频端
            localWinId1 = self.window3.effectiveWinId().__int__()   #local视频端

            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视频流的绘制视窗进行绑定。
#agora RTC事件处理器
class MyRtcEngineEventHandler(agorartc.RtcEngineEventHandlerBase):

		#监听其他用户是否进入该channel,如果有则绘图
    def onUserJoined(self, uid, elapsed):
        global remoteWinId
        if remoteWinId != -1:
            remoteVideoCanvas = agorartc.createVideoCanvas(remoteWinId)    #创建remote user视频窗口
            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):

 #获取本地camera 原生视频
 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)   #利用QPixmap组件绘制图片
     draw_label_2.setPixmap(pixmap)  #利用QPixmap组件绘制图片

		#获取remote用户的 原生视频
 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 DataAgora 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  #remote user视频流获取成功
    

    而是出在pyqt的代码:MainWindow.py上,该文件创建了3个gridLayoutWidget控件,其中remote user的视频流窗口被黑色背景覆盖了,导致remote user视频流显示视频。

    解决方法:去除掉该项目中下面的冗余代码

    # self.gridLayoutWidget_3 = QtWidgets.QWidget(self.centralwidget)
    # self.gridLayoutWidget_3.setGeometry(QtCore.QRect(20, 60, 471, 351))
    # 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)
    
  • 存在问题2:face_recognition人脸识别第三方包在windows上编译存在问题,在linux上则没问题

    这里在获取本地原生视频流之后,使用SCRFD模型进行人脸定位。

    解决方法:恢复之前去除掉的冗余代码

    修改代码如下:

    class MyVideoFrameObserver(agorartc.VideoFrameObserver):
    
        #获取本地camera 原生视频
        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)
            # print(f"img.size = {rgba_img.size}")   # PIL img尺寸
            # print(f"img.getbands = {rgba_img.getbands()}")  #PIL img通道 (RGBA)
    
            '''Step2: 将PIL格式图片转化成cv2格式图片,并使用SCRFD模型进行人脸定位'''
            cv2_img = cv2.cvtColor(np.asarray(rgba_img), cv2.COLOR_RGBA2BGR)
            # print(f"cv2_img.size = {cv2_img.size}")
    
            dets = scrfd_model.detect_faces(cv2_img)[0]  #return 边界框
            # print("inference fininshed")
            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)  # 目标的bbox
    
            '''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)  #利用QImage加载图片到组件中
            # 按照draw_label的尺寸缩放图片
            qImg_scaled = qImg.scaled(draw_label.width(), draw_label.height(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
            pixmap = QtGui.QPixmap.fromImage(qImg_scaled)
            draw_label.setPixmap(pixmap)   #利用QPixmap组件绘制图片
    
        #获取remote用户的 原生视频
        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(20, 60, 471, 351))  #绘制remote user视频流中的人脸边界框
    self.gridLayoutWidget_3.setGeometry(QtCore.QRect(520, 380, 251, 161))  #绘制local camera视频流中的人脸边界框
    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 = curPath + "/" + onnxmodel   #项目路径
            onnxmodel = onnxmodel  #相对路径
            self.inpWidth = 640
            self.inpHeight = 640
            self.confThreshold = confThreshold
            self.nmsThreshold = nmsThreshold
            # self.net = cv2.dnn.readNet(onnxmodel)  #对于SCRFD模型,使用原生的onnx比dnn快了10帧: 20:10
            print(f"SCRFD: {onnxruntime.get_device()}")
            self.ort_sess = onnxruntime.InferenceSession(onnxmodel,providers=['CPUExecutionProvider'])  #onnx加载推理模型
            # self.ort_sess = onnxruntime.InferenceSession(onnxmodel,providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])  # Create inference session using ort.InferenceSession
            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为例:

    语言算法核心方法示例代码
    PythonHMAC-SHA256buildTokenWithUidRtcTokenBuilderSample.py
    Python3HMAC-SHA256buildTokenWithUidRtcTokenBuilderSample.py

    API 参考

    参数描述
    role用户权限,分为发流用户和接收用户。参数决定用户是否能在频道中发流。Role_Publisher(1):(默认)用户有发流权限,即用户可在频道中发流。Role_Subscriber(2):用户有接收权限,即用户可在频道中接收,但不可发流。该参数仅在开启连麦鉴权后才生效。详情参考如何使用连麦鉴权功能?
    uid待鉴权用户的用户 ID 32 位无符号整数,范围为1到 (232 - 1), 并保证唯一性。 如不需对用户 ID 进行鉴权,即客户端使用任何 uid 都可加入频道,请把 uid 设为 0。
    privilegeExpiredTsoken 过期的 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}")   #输入的appId,appKey,uid,chatName必须和token一致,否则鉴权失败
    

    复制生成的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信息。

    #Response body 200
    {
      "success": true,
      "data": {
        "channel_exist": false
      }
    }
    

    而在使用agora项目中生成的临时音视频token,就可以识别到用户列表信息。

    只连接Thinkpad:uids = [3]

    {
      "success": true,
      "data": {
        "channel_exist": true,
        "mode": 1,
        "total": 1,
        "users": [
          3   #uid = 3
        ]
      }
    }
    

    同时连接surface go之后:uids = [3, 12]

    {
      "success": true,
      "data": {
        "channel_exist": true,
        "mode": 1,
        "total": 2,
        "users": [
          3,
          12
        ]
      }
    }
    

    解决方法

    主要原因是用错了token生成方法(用buildTokenWithAccount,不用buildTokenWithUid)。

    基于python的token服务器代码修改如下:

    #参考 https://github.com/AgoraIO-Community/python-token-builder
    from agora_token_builder import RtcTokenBuilder
    import time
    import random
    
    appId = "ea8cafc8c9e943bcacc2f59802f72b9f"
    appCertificate = "60bf2d9851ef4c079795df8f1193d410"
    channelName = "TESTROOM"
    # uid = random.randint(1, 230)
    userAccount = "13"
    expirationTimeInSeconds = 3600
    currentTimeStamp = int(time.time())
    privilegeExpiredTs = currentTimeStamp + expirationTimeInSeconds
    role = 1
    
    #Build token with userAccount
    token = RtcTokenBuilder.buildTokenWithAccount(appId, appCertificate, channelName, userAccount, role, privilegeExpiredTs)
    print(f"token = {token}, uid = {userAccount}")   #输入的appId,appKey,uid,chatName必须和token一致,否则鉴权失败
    

    这里主要区别就是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

    #responseBody
    {
      "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类,用于关于音频播放设备。

    1. SetPlaybackDevice指定播放设备。

      publicabstractint SetPlaybackDevice(string deviceId);
      

      参数deviceId通过 deviceID 指定播放设备。由 EnumeratePlaybackDevices 获取。插拔设备不会影响 deviceId。

      返回值

      • 0: 方法调用成功。
      • < 0: 方法调用失败。
    2. SetPlaybackDeviceVolume设置播放设备音量。

      public abstract int SetPlaybackDeviceVolume(int volume);
      

      参数volume播放设备音量。取值可在 [0,255]。

      返回值

      • 0: 方法调用成功。
      • < 0: 方法调用失败。
    3. SetPlaybackDeviceMute

      设置播放设备静音。

      **public** **abstract** **int** SetPlaybackDeviceMute(**bool** mute);
      

      参数mute是否设置播放设备为静音:
      ? true: 播放设备设为静音。
      ? false: 播放设备不设为静音。

      返回值

      • 0: 方法调用成功。
      • < 0: 方法调用失败。
  • 尝试(看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)   # 设置remote用户播放音量 (有效果)
    

    没效果:使用agorartc内置函数(createAudioPlaybackDeviceManager)创建的audio_playback_device_manager对象,音量设置无效果

    
    self.audio_playback_device_manager, err = self.rtc.createAudioPlaybackDeviceManager()
    audioDevice = self.audio_playback_device_manager.setDeviceVolume(volume=0)   # 设置remote用户播放音量 (不报错,但没效果)
    
2)音频采集设备(Recording)
  • 参考资料

    IAgoraRtcAudioRecordingDeviceManager类,用于关于音频采集设备

    1. EnumerateRecordingDevices

      获取系统中所有的音频采集设备列表。

      publicabstract DeviceInfo[] EnumerateRecordingDevices();
      

      返回值

      • 方法调用成功,返回一个 DeviceInfo 数组,包含所有音频采集设备的设备 ID 和设备名称。
      • 方法调用失败:NULL。
    2. SetRecordingDevice

      指定音频采集设备。

      publicabstractint SetRecordingDevice(string deviceId);
      

      参数deviceId音频采集设备的 Device ID。可通过 EnumerateRecordingDevices 获取。插拔设备不会影响 deviceId。

      返回值

      • 0: 方法调用成功。
      • < 0: 方法调用失败。
  • 尝试(看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)  #设置本地播放音量 0 < volume < 100  (有效果)
    

    没效果:使用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端双流模式的方法,如果有小伙伴知道发送端如何设置双流模式的话,可以在评论区里留言。

    #设置双流模式,指定某些uid为小流,某个uid为大流
    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}")   #0: 方法调用成功   < 0: 方法调用失败
    
  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-06-14 22:33:55  更:2022-06-14 22:35:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 14:41:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码