记录:利用 Agora 在 Unity3D MRTK场景中创建实时视频聊天应用

目录

    • 准备
    • 1. 安装Agora_Unity_RTC_SDK
    • 2. 创建UI
    • 3. script具体内容
    • 4. 使用测试

  • 本质是两部带摄像机的设备同时进入Agora聊天室内视频。

  • 去年实现过一次这个功能,用的是Agora_Unity_RTC_SDK 4.2.2版本的,今年使用失败,遂重新安装最新版本Agora_Unity_RTC_SDK(4.3.2)并按照官方教程配置,但发现不能正常使用。解决之后,特此记录。

  • 本文假设已经有了Unity3D Scene并安装好了MRTK。

准备

名称版本
设备A使用其浏览器
设备B安装了Unity
Unity2202.3.9.f1
MRTK2.8.3.0
Agora_Unity_RTC_SDK4.3.2

建议使用两台设备测试来避免出现相机占用的问题。

1. 安装Agora_Unity_RTC_SDK

SDK下载链接:https://docs.agora.io/en/sdks?platform=unity,点击import就可以导入project中。

2. 创建UI

英文版官方说明:https://docs.agora.io/en/video-calling/get-started/get-started-sdk?platform=unity

我们需要(名字都不可以改动,会影响SDK的使用):

名称属性
TestCanvasCanvas
LocalViewRaw Image
JoinButton
LeaveButton
RemoteViewCube 我设的size:(1,1,0.01)

其中,RemoteView会放入MixedRealitySceneContent中以实现可以在AR场景中看见RemoteView中的内容;其他都放在创建的TestCanvas中。
在这里插入图片描述

3. script具体内容

我创建了JoinChannelView.cs并将其挂在上一步创建的TestCanvas上。以下是JoinChannelView.cs的具体内容:

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Serialization;
using Agora.Rtc;

#if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
using UnityEngine.Android;
#endif

public class JoinChannelVideo : MonoBehaviour
{
    // Fill in your app ID
    private string _appID = "待填";
    // Fill in your channel name
    private string _channelName = "待填";
    // Fill in a temporary token
    private string _token = "待填";

    internal VideoSurface LocalView;
    internal VideoSurface RemoteView;
    internal IRtcEngine RtcEngine;
        
        
    #if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
    private ArrayList permissionList = new ArrayList() { Permission.Camera, Permission.Microphone };
    #endif

    // Start is called before the first frame update
    void Start()
    {
        SetupVideoSDKEngine();
        InitEventHandler(); 
        SetupUI();
    }

    private void InitEventHandler()
    {
        UserEventHandler handler = new UserEventHandler(this);
        RtcEngine.InitEventHandler(handler);
    }

    private void SetupUI()
    {
        GameObject go  = GameObject.Find("RemoteView");
        RemoteView = go.AddComponent<VideoSurface>();
        go.transform.Rotate(0.0f, 0.0f, -180.0f);

        go = GameObject.Find("LocalView");
        LocalView = go.AddComponent<VideoSurface>();
        go.transform.Rotate(0.0f, 0.0f, -180.0f);

        go = GameObject.Find("Leave");
        go.GetComponent<Button>().onClick.AddListener(Leave);
        go = GameObject.Find("Join");
        go.GetComponent<Button>().onClick.AddListener(Join);

        //Join();
    }

    public void Join(){
        // Enable the video module
        RtcEngine.EnableVideo();
        // Set channel media options
        ChannelMediaOptions options = new ChannelMediaOptions();
        // Start video rendering
        LocalView.SetEnable(true);
        // Automatically subscribe to all audio streams
        options.autoSubscribeAudio.SetValue(true);
        // Automatically subscribe to all video streams
        options.autoSubscribeVideo.SetValue(true);
        // Set the channel profile to live broadcast
        options.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_COMMUNICATION);
        //Set the user role as host
        options.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
        // Join a channel
        RtcEngine.JoinChannel(_token, _channelName, 0, options);
    }
    public void Leave() 
    {
        Debug.Log("Leaving _channelName");
        // Leave the channel
        RtcEngine.LeaveChannel();
        // Disable the video module
        RtcEngine.DisableVideo();
        // Stop remote video rendering
        RemoteView.SetEnable(false);
        // Stop local video rendering
        LocalView.SetEnable(false);
    }

    // Update is called once per frame
    void Update() {
        CheckPermissions();
    }


    private void CheckPermissions() {
        #if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
        foreach (string permission in permissionList) {
            if (!Permission.HasUserAuthorizedPermission(permission)) {
                Permission.RequestUserPermission(permission);
            }
        }
        #endif
    }

    private void SetupVideoSDKEngine()
    {
            // Create an IRtcEngine instance
            RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine();
            RtcEngineContext context = new RtcEngineContext();
            context.appId = _appID;
            context.context = 0;
            context.channelProfile = CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING;
            context.audioScenario = AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT;
            // Initialize the instance
            RtcEngine.Initialize(context);
    }

}

// Implement your own EventHandler class by inheriting the IRtcEngineEventHandler interface class implementation
internal class UserEventHandler : IRtcEngineEventHandler
{
    private readonly JoinChannelVideo _videoSample;
    internal UserEventHandler(JoinChannelVideo videoSample)
    {
        _videoSample = videoSample;
    }
    // error callback
    public override void OnError(int err, string msg)
    {
    }
    // Triggered when a local user successfully joins the channel
    public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
    {
        _videoSample.LocalView.SetForUser(0, "");
    }
    // When the SDK receives the first frame of a remote video stream and successfully decodes it, the OnUserJoined callback is triggered.
    public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
    {
        // Set the remote video display
        _videoSample.RemoteView.SetForUser(uid, connection.channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
        // Start video rendering
        _videoSample.RemoteView.SetEnable(true);
        Debug.Log("Remote user joined");
    }
}

这里需要注意的是函数SetupVideoSDKEngine,官方的写法是:

private void SetupVideoSDKEngine()
{
    // Create an IRtcEngine instance
    RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine();
    RtcEngineContext context = new RtcEngineContext(_appID, 0,CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING, AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT);
    // Initialize the instance
    RtcEngine.Initialize(context);
}

感觉和我的修改的代码没有什么本质区别,但我测试过,用官方的教程无法获得RemoteView内容。

此外,internal class UserEventHandler : IRtcEngineEventHandler {}函数需要放在public class JoinChannelVideo : MonoBehaviour {}外面,之前由于对c#不熟悉将internal class UserEventHandler放在public class JoinChannelVideo里,虽然不会报错,但也不能正常使用。

4. 使用测试

注册Agora,并创建Project,注册连接:https://console.agora.io/v2。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 把名为 My New Project 的 project 的 APP IDChannel NameToken 填入设备A打开的浏览器:Agora Basic Video Call 中连接:https://webdemo.agora.io/basicVideoCall/index.html#,点击 Join
    在这里插入图片描述
  2. 设备B 的Unity Project中,修改JoinChannelVideo内容:
public class JoinChannelVideo : MonoBehaviour
{
    // Fill in your app ID
    private string _appID = "e6fd32eba33741e68641f504580e7d29";
    // Fill in your channel name
    private string _channelName = "My New Project";
    // Fill in a temporary token
    private string _token = "007eJxTYHB7f9nyxhpbtncH1e8YsNm8qVX09fs7o6pqjvZ7A+u5wXoKDKlmaSnGRqlJicbG5iaGqWYWZiaGaaYGJqYWBqnmKUaW3zfkpjUEMjJwXz3LysgAgSA+H4NvpYJfarlCQFF+VmpyCQMDAIqPIqw=";

    internal VideoSurface LocalView;
    internal VideoSurface RemoteView;
    internal IRtcEngine RtcEngine;

点击按键 JoinLeave 控制视频开启和离开。

结果展示,在AR场景内只看见RemoteView的内容:

网页Unity

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/713076.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker安装消息队列mq中的rabbit服务

在现代化的分布式系统中&#xff0c;消息队列&#xff08;Message Queue, MQ&#xff09;已经成为了一种不可或缺的组件。RabbitMQ作为一款高性能、开源的消息队列软件&#xff0c;因其高可用性、可扩展性和易用性而广受欢迎。本文将详细介绍如何在Docker环境中安装RabbitMQ服务…

中文版svn怎么忽略文件

个人需求&#xff1a; 不上传dist&#xff0c;dist.7z&#xff0c;node_modules等文件夹 实际操作&#xff1a; 前言&#xff1a;在上传svn为避免操作失败导致丢失文件的情况&#xff0c;保险起见&#xff0c;先备份代码 1&#xff1a;右键点击 2&#xff1a;点击新建 – 其…

【C++】图1

并查集 template <class T> class UnionFindSet { public:UnionFindSet(size_t n):_ufs(n, -1){}void Union(int x1, int x2){int root1 FindRoot(x1);int root2 FindRoot(x2);if (root1 root2)return;if (root1 > root2)swap(root1, root2);_ufs[root1] _ufs[ro…

uniapp开发微信小程序问题汇总

1. 自定义校验规则validateFunction失效 2. 微信小程序不支持<Br>换行 在 <text></text> 标签中使用\n(必须 text 标签&#xff0c;view 标签无效 ) 3. 微信小程序无法使用本地静态资源图片的解决方法 (1) 将图片上传到服务器&#xff0c;小程序访问该图片…

非对称加密系统解析

目录 1. 概述 2. 非对称加密标准 2.1 RSA 2.2 SM2 2.2.1 SM2私钥 2.2.2 SM2公钥 2.2.3 加密数据格式 2.2.4 签名数据格式 1. 概述 非对称加密中&#xff0c;密钥分为加密密钥和解密密钥两种。发送者用加密密钥对消息进行加密&#xff0c;接收者用解密密钥对密文进行解密…

Leetcode - 132双周赛

目录 一、3174. 清除数字 二、3175. 找到连续赢 K 场比赛的第一位玩家 三、3176. 求出最长好子序列 I 四、3177. 求出最长好子序列 II 一、3174. 清除数字 本题可以使用栈来模拟&#xff0c;遇到数字弹出栈顶元素&#xff0c;遇到字母入栈。 代码如下&#xff1a; //使用字…

网络编程(二)TCP

一、TCP网络编程 网络编程模型&#xff1a; C/S模型&#xff1a;客户端服务器模型 优点&#xff1a; 客户端可以缓存一些数据&#xff0c;使用时直接在本地读取&#xff0c;无需每次重新下载&#xff1b; 由于客户端和服务器都是自己开发的&#xff0c;可以自定义协议 缺点&a…

基于carsim的线控转向仿真(1)--carsim车辆模型目标角度跟踪

一、Rwa转向执行总成建模 Rwa包括齿轮齿条机构、转向组件以及转向执行电机&#xff1b;如下图&#xff0c;电机输出轴通过齿轮减速增扭后&#xff0c;再经过一个半径为rp的小齿轮&#xff0c;直接带动齿条左右移动。齿条的移动通过转向摇臂&#xff0c;带动车轮转动&#xff0c…

Excel/WPS《超级处理器》功能介绍与安装下载

超级处理器是基于Excel或WPS开发的一款插件&#xff0c;拥有近300个功能&#xff0c;非常简单高效的处理表格数据&#xff0c;安装即可使用。 点击此处&#xff1a;超i处理器安装下载 Excel菜单&#xff0c;显示如下图所示&#xff1a; WPS菜单显示&#xff0c;如下图所示&am…

15. 第十五章 类和对象

15. 类和对象 到现在你已经知道如何使用函数组织代码, 以及如何使用内置类型来组织数据. 下一步将学习面向对象编程, 面向对象编程使用自定义的类型同时组织代码和数据. 面向对象编程是一个很大的话题, 需要好几章来讨论.本章的代码示例可以从↓下载, https://github.com/Alle…

嵌入式实训day3

1、 # 82261773 # y6ufuT9yGQxddpSzSe3zZpbP # BJsEfKFNGOwHtLuKoHsfVIWrGWjXVKut"""1、PC需要联网2、不能使用MicroPython解释器 """ from aip import AipFace import base64# 查看REST-API-SDK if __name__ "__main__":# 设置APP_I…

数字电路中二进制的数据表达

文章目录 1. 二进制数据表达 1.1 二进制简介 1.2 用二进制表达文字 1.2.1 最开始的表达方式 1.2.2 通讯系统的编码和解码 1.2.3 集成电路 1.2.4 ASCII编码 1.2.5 GBK编码 1.2.6 Unicode编码 2. 用二进制表达图像 2.1 图片像素化 2.2 像素数字化 2.3 二值图像 2.4…

C++ 43 之 自增运算符的重载

#include <iostream> #include <string> using namespace std;class MyInt{friend ostream& operator<< (ostream& cout , MyInt& int1); public:MyInt(){this->m_num 0;}// 前置自增&#xff1a; 成员函数实现运算符的重载 返回的是 引用&a…

ARTS Week 32

Algorithm 本周的算法题为 1512. 好数对的数目 给你一个整数数组 nums 。 如果一组数字 (i,j) 满足 nums[i] nums[j] 且 i < j &#xff0c;就可以认为这是一组 好数对 。 返回好数对的数目。 示例 1&#xff1a;输入&#xff1a;nums [1,2,3,1,1,3]输出&#xff1a;4解释…

使用python绘制三维散点图

使用python绘制三维散点图 三维散点图三维散点图的用途效果代码 三维散点图 三维散点图&#xff08;3D Scatter Plot&#xff09;是一种用于展示三维数据的图表。与二维散点图类似&#xff0c;三维散点图通过点在三维空间中的位置来表示数据点的三个特征。每个点在 x、y 和 z …

如何清除anaconda3缓存?

如果长期使用anaconda不清理缓存&#xff0c;会导致anaconda占用磁盘空间越来越多&#xff0c;甚至系统磁盘撑爆。 清除包缓存&#xff1a; 打开 Anaconda Prompt 或者命令行窗口。运行以下命令清除包缓存&#xff1a;conda clean --all这会清除所有的包缓存&#xff0c;释放磁…

一次基于 rebase 的 PR 提交

目录标题 基于 rebase 的 PR 提交git 命令idea 操作 基于 rebase 的 PR 提交 git 命令 &#xff11;・git fetch &#xff12;・git checkout -b dev2 origin/dev2 新拉分支dev2&#xff13;・date >> 1.txt && git add . && g…

Midjourney提示词终极指南(完整版)

在这篇博客中&#xff0c;我们深入研究了使用提示的艺术&#xff0c;以利用Midjourney的AI功能的力量。我们将探索各种技术&#xff0c;以创建个性化和迷人的图像&#xff0c;将你的创意想法转变为令人惊叹的视觉杰作。 1. 了解提示词 提示是简短的文字描述或关键词&#xff…

人工智能在风险管理中的创新之路及案例分析

随着科技的日新月异&#xff0c;人工智能&#xff08;AI&#xff09;技术已广泛应用于各个领域&#xff0c;特别是在风险管理方面&#xff0c;其展现出的巨大潜力和实际应用价值引人瞩目。本文将结合具体案例&#xff0c;深入探讨AI在风险管理中的创新应用及其带来的行业变革。…

springboot原理篇-配置优先级

springboot原理篇-配置优先级&#xff08;一&#xff09; springboot项目一个支持三种配置文件 application.propertiesapplication.ymlapplication.yaml 其中&#xff0c;优先级的顺序是&#xff1a; application.properties > application.yml > application.yaml 也…