使用 Agora.io 在 Xamarin 应用中添加视频通话





0/5 (0投票)
我将帮助您开始使用 Agora.io Xamarin SDK 构建您的第一个视频通话应用程序。
Agora.io Xamarin SDK 是一个开源项目,使开发人员能够使用 C# 构建跨平台应用程序。在收到许多开发者的请求后,我们正式采用了 Xamarin 绑定并将其开源,以帮助其他人轻松地将实时通信集成到他们的移动应用程序中。
开源项目是我们都可以支持的,所以我们希望 **您**能与我们互动!加入我们,提交拉取请求,fork 项目,翻译它,或者通过改进我们的文档来帮助其他开发者。
在本文的其余部分,我将帮助您开始使用 Agora.io Xamarin SDK 进行第一次视频通话。让我们开始吧!
必备组件
- Xamarin iOS 和 Visual Studio
- C# 基本知识
- Agora.io 开发者账户
第一步:Agora.io 账户
如果还没有,请创建 Agora.io 开发者账户。完成注册过程后,您将被重定向到仪表板。点击左侧导航栏中的“项目”选项卡,即可查看您默认项目的 App ID。
第二步:集成 Agora SDK
创建一个新的空白原生应用解决方案。
第三步:将 Agora Nuget 包添加到 iOS 项目
为 AgoraTutorial.iOS 项目添加包 Xamarin.Agora.Full.iOS
第四步:相机和麦克风访问的隐私设置
在 Info.plist 文件中,确保为相机和麦克风添加隐私设置,以便设备可以访问它们。同时检查最低 iOS 版本是否为 9.3。并验证项目属性中的构建目标是否为 Armv7 + AMD64
,该库不支持 Armv7s
作为目标。
第五步:添加视图
首先,下载本教程中提供的资源。这些资源是本教程中添加的各种按钮的图标。将 ViewController.cs 文件重命名为 VideoCallViewController.cs 以获得更相关的文件名,因为这将是我们为视频通话设置的视图控制器。接下来,向项目中添加一个新的视图 (SetChannelViewController.cs),以便用户可以选择要加入的频道。在设置故事板之后,我们将深入研究每个文件的代码。
接下来,打开 Main.storyboard 文件,在标识检查器中,将自定义类设置为 VideoCallViewController
,以更新故事板 VC 链接与重命名的 cs 文件。将故事板 ID 设置为 VideoCallView
。
接下来,为远程视频流拖入一个视图组件。在远程视图内部,再添加一个用于本地视频流的视图。在大多数视频聊天应用程序中,此视图位于右上方。使用相同的**高度/宽度**和**x/y**值,创建一个图像视图并为其分配 cameramute.png 资源。此图像将用于在用户暂停视频流时覆盖远程视频流。在此之后,将一个图像视图拖到本地视频图像视图的中心,并为其分配 cameraoff_mainVideo.png 图像。添加另一个具有相同 cameraoff_mainVideo.png 图像的图像视图,并将其居中放在远程视图的中间。在屏幕底部,创建一个视图,其中包含四个按钮:暂停视频、静音音频、切换摄像头和挂断。使用适当的资源为每个按钮,并参考上图了解放置位置。
接下来,在 Main.storyboard 文件中拖入一个视图控制器。添加一个文本字段用于用户输入频道名称,以及一个开始视频通话的按钮。在标识检查器中,将自定义类设置为 SetChannelViewController
,以便将故事板 VC 与文件链接。
第六步:添加 Agora 功能
初始化 Agora 原生 SDK
using System; using DT.Xamarin.Agora; using Foundation; using UIKit; namespace AgoraTutorial.iOS { public partial class VideoCallViewController : UIViewController, IAgoraRtcEngineDelegate { private AgoraRtcEngineKit agoraKit; private const string AppID = "Your-App-ID"; private string channel = ""; //User inputs channel name (steps come later in this tutorial) public VideoCallViewController(IntPtr handle) : base(handle) { } public override void ViewDidLoad() { base.ViewDidLoad(); InitializeAgoraEngine(); } private void InitializeAgoraEngine() { agoraKit = AgoraRtcEngineKit.SharedEngineWithAppIdAndDelegate(AppID, this); } } }
AgoraRtcEngineKit
是 Agora 原生 SDK 的基本接口类。AgoraRtcEngineKit
对象使您能够使用 Agora 原生 SDK 的通信功能。创建一个 AgoraRtcEngineKit
对象的变量,并将其设为隐式解包可选类型。接下来,为您的控制器添加一个接口 (IAgoraRtcEngineDelegate
)。然后,创建一个方法 (InitializeAgoraEngine()
),该方法将 AgoraRtcEngineKit
类初始化为单例实例,以便在我们使用它之前初始化服务。在调用方法时,提供两个参数:AppId
和 delegate
。将您的 App ID 作为字符串提供,并将 self
作为代理传递,表示当前视图控制器(控制通话的视图控制器)。Agora 原生 SDK 使用委托来通知应用程序引擎运行时事件(加入/离开频道、新参与者等)。在 ViewDidLoad()
方法中调用 InitializeAgoraEngine()
方法。最后,添加一个字符串可选类型 (channel
) 用于将在本教程稍后构建的另一个视图控制器中由用户提供的频道名称。
启用视频模式
void SetupVideo() { agoraKit.EnableVideo(); // Enables video mode. agoraKit.SetVideoProfile(VideoProfile.Portrait360P, false); // Default video profile is 360P } public override void ViewDidLoad() { base.ViewDidLoad(); InitializeAgoraEngine(); SetupVideo(); }
创建一个方法 (SetupVideo()
) 并在该方法内启用视频模式。在本教程中,我们在加入频道之前启用视频模式,以便最终用户从视频模式开始。如果在通话过程中启用,则会从音频模式切换到视频模式。接下来,将视频编码配置文件设置为 360p,并将 swapWidthAndHeight
参数设置为 false。设置为 true 会导致视频流的宽度和高度互换。每个配置文件都包含一组参数,例如:分辨率、帧率、比特率等。当设备的相机不支持指定分辨率时,SDK 会自动选择合适相机的分辨率,但编码器分辨率仍使用 SetVideoProfile 指定的。
加入频道
void JoinChannel() { agoraKit.JoinChannelByToken(null, "demoChannel1", null, 0, (sid, uid, elapsed) => { agoraKit.SetEnableSpeakerphone(true); UIApplication.SharedApplication.IdleTimerDisabled = true; }); } public override void ViewDidLoad() { base.ViewDidLoad(); InitializeAgoraEngine(); SetupVideo(); JoinChannel(); }
此时,创建一个方法 (JoinChannel()
) 让用户加入特定频道。调用 agoraKit.JoinChannelByToken()
方法,并将 token
和 info
参数设置为 null
。对于频道名称,提供任何字符串(例如:“demoChannel1”),并将 UID 设置为 0,让 Agora 为频道 ID 选择一个随机 UID。
禁用应用程序的空闲计时器,并使用 Agora Kit 启用扬声器。同一频道内的用户可以互相交谈,但使用不同 App ID 的用户无法互相通话(即使他们加入同一个频道)。成功调用此方法后,SDK 将触发回调。本教程不实现它们,但它们将包含在我们未来的教程系列中。最后,在 ViewDidLoad()
方法中调用 JoinChannel()
。
设置本地视频
void SetupLocalVideo() { var videoCanvas = new AgoraRtcVideoCanvas(); videoCanvas.Uid = 0; videoCanvas.View = localVideo; videoCanvas.RenderMode = VideoRenderMode.Fit; agoraKit.SetupLocalVideo(videoCanvas); } public override void ViewDidLoad() { base.ViewDidLoad(); InitializeAgoraEngine(); SetupVideo(); JoinChannel(); SetupLocalVideo(); }
现在是时候创建本地视频流的视图了。创建一个方法 (SetupLocalVideo()
) 来初始化 AgoraRtcVideoCanvas
对象,该对象用于视频流。有几个对象属性需要正确设置。将 Uid
属性设置为 0,以允许 Agora 为流选择一个随机 UID。View
属性应设置为最近添加的 UIView (localVideo
)。RenderMode
属性应设置为 VideoRenderMode.Fit
,以确保如果视频尺寸与显示窗口不同,视频将按比例调整大小以适应窗口。
然后,调用 agoraKit.SetupLocalVideo(videoCanvas)
,传入刚刚创建的 AgoraRtcVideoCanvas 对象。最后,在 ViewDidLoad()
方法中调用 SetupLocalVideo()
。
委托方法
[Export("rtcEngine:firstRemoteVideoDecodedOfUid:size:elapsed:")] public void FirstRemoteVideoDecodedOfUid(AgoraRtcEngineKit engine, nuint uid, CoreGraphics.CGSize size, nint elapsed) { if (remoteVideo.Hidden) { remoteVideo.Hidden = false; } var videoCanvas = new AgoraRtcVideoCanvas(); videoCanvas.Uid = uid; videoCanvas.View = remoteVideo; videoCanvas.RenderMode = VideoRenderMode.Adaptive; agoraKit.SetupRemoteVideo(videoCanvas); } [Export("rtcEngine:didOfflineOfUid:reason:")] public void DidOfflineOfUid(AgoraRtcEngineKit engine, nuint uid, UserOfflineReason reason) { remoteVideo.Hidden = true; } [Export("rtcEngine:didVideoMuted:byUid:")] public void DidVideoMuted(AgoraRtcEngineKit engine, bool muted, nuint uid) { remoteVideo.Hidden = muted; remoteVideoMutedIndicator.Hidden = !muted; }
现在是时候创建远程视频流的视图了。和之前一样,在 Interface Builder 中,在 Main.storyboard 中向视图控制器添加一个 UIView,并在相应的视图控制器中创建一个指向它的 outlet。完成后,创建一个扩展 IAgoraRtcEngineDelegate
的 ViewController 扩展。添加 FirstRemoteVideoDecodedOfUid
委托方法,并带有显示的参数(AgoraRtcEngineKit engine
,nuint uid
,CoreGraphics.CGSize size
,nint elapsed
)。当另一个用户连接并接收到第一个远程视频帧并解码时,会触发此回调。在此方法内,如果 remoteVideo 隐藏,则显示它。
接下来,初始化 AgoraRtcVideoCanvas 对象,并像上面一样设置对象属性。将 Uid
属性设置为 0,以允许 Agora 为流选择一个随机 UID。View
属性应设置为最近添加的 UIView (remoteVideo
)。RenderMode
属性应设置为 VideoRenderMode.Adaptive
,以确保显示窗口尺寸不同的视频会按比例调整大小以适应窗口。
然后,调用 agoraKit.SetupRemoteVideo(videoCanvas)
,传入刚刚创建的 AgoraRtcVideoCanvas
对象。接下来,实现下一个 DidOfflineOfUid
委托方法,并带有参数(AgoraRtcEngineKit engine
,nuint uid
,UserOfflineReason reason
),当另一个用户离开频道时调用。在该方法内,当用户离开通话时,将 remoteVideo
视图设置为隐藏。最后,实现最后一个 DidVideoMuted
委托方法(AgoraRtcEngineKit engine
,bool muted
,nuint uid
),当远程用户暂停其视频流时调用。
第七步:添加其他功能
添加频道选择
为了让用户选择他们想加入的房间,您添加了一个简单的 UI 布局,包括一个用于频道名称输入的文本字段和一个开始通话的按钮。打开 Main.storyboard 文件,并将 SetChannelViewController
设置为初始视图控制器。现在,只需将文本字段和按钮添加为 outlet 和 action 分别即可。如果文本字段的文本属性不为空,则在按钮的 IBAction 函数中显示 VideoCallViewController
。如果文本字段的文本属性为空,则提示用户输入频道名称。最后,将用户输入的频道名称传递给 VideoCallViewController
。
partial void StartCallClick(NSObject sender) { if (string.IsNullOrWhiteSpace(channelNameTextField.Text)) { channelName.Text = "Please input channel name to proceed"; } else { var storyboard = UIStoryboard.FromName("Main", null); var controller = storyboard.InstantiateViewController("VideoCallView"); VideoCallViewController.Channel = channelNameTextField.Text; this.ShowViewController(controller, this); } }
更新 VideoCallViewController
中的 JoinChannel 方法,以使用从频道输入屏幕接收到的 Channel
字段。
void JoinChannel() { agoraKit.JoinChannelByToken(null, Channel, null, 0, (sid, uid, elapsed) => { agoraKit.SetEnableSpeakerphone(true); UIApplication.SharedApplication.IdleTimerDisabled = true; }); }
视频聊天控件
创建一个视图 (controlButtons
),该视图位于远程视图的底部。此视图将包含挂断按钮、音频静音按钮、视频暂停按钮和切换摄像头按钮。
挂断 / 结束通话
partial void HangUpButtonClick(NSObject sender) { LeaveChannel(); } void LeaveChannel() { agoraKit.LeaveChannel(null); HideControlButtons(); UIApplication.SharedApplication.IdleTimerDisabled = false; remoteVideo.RemoveFromSuperview(); localVideo.RemoveFromSuperview(); agoraKit = null; this.DismissViewController(true, null); } void HideControlButtons() { controlButtons.Hidden = true; }
创建一个方法 LeaveChannel()
,该方法使用户能够离开当前的视频通话(频道)。在该方法内,调用 agoraKit.LeaveChannel
,并将 null
作为参数传递。接下来,隐藏包含底部按钮的视图 (controlButtons
)。之后,以编程方式移除本地和远程视频视图,并将 agoraKit 设置为 null
,以结束 AgoraRtcEngineKit
对象的实例。在挂断按钮的 IBAction 中,调用我们刚刚创建的 LeaveChannel()
方法。最后,创建一个方法 HideControlButtons()
,该方法隐藏包含不同按钮的视图。
音频静音
partial void MuteButtonClick(NSObject sender) { var button = (UIButton)sender; button.Selected = !button.Selected; agoraKit.MuteLocalAudioStream(button.Selected); }
现在是时候为本地音频流添加静音功能了。在静音按钮的 IBAction 中,调用 agoraKit.MuteLocalAudioStream()
方法,并将 button.Selected
作为唯一参数传递。
暂停视频
partial void VideoMuteButtonClick(NSObject sender) { var button = (UIButton)sender; button.Selected = !button.Selected; agoraKit.MuteLocalVideoStream(button.Selected); localVideo.Hidden = button.Selected; localVideoMutedBg.Hidden = !button.Selected; localVideoMutedIndicator.Hidden = !button.Selected; }
有时,您可能穿着睡衣,不想让您的视频流给同事看。通过调用 agoraKit.MuteLocalVideoStream()
方法并将 button.Selected
作为唯一参数传递,为视频按钮的 IBAction 添加此功能。
切换摄像头
partial void SwitchCameraButtonClick(NSObject sender) { var button = (UIButton)sender; button.Selected = !button.Selected; agoraKit.SwitchCamera(); }
接下来,允许用户在前置摄像头和后置摄像头之间进行选择。在摄像头切换按钮的 IBAction 中,调用 agoraKit.SwitchCamera()
方法来添加摄像头切换功能。
隐藏静音图像
void HideVideoMuted() { remoteVideoMutedIndicator.Hidden = true; localVideoMutedBg.Hidden = true; localVideoMutedIndicator.Hidden = true; }
创建一个方法 HideVideoMuted()
来隐藏当远程或本地视频流暂停时应该出现的图像视图。在 ViewDidLoad()
方法中调用此方法,以确保图像在应用程序启动时隐藏。
设置按钮
void SetupButtons() { var tapGestureRecognizer = new UITapGestureRecognizer(ViewTapped); View.AddGestureRecognizer(tapGestureRecognizer); View.UserInteractionEnabled = true; } public override void ViewDidLoad() { base.ViewDidLoad(); InitializeAgoraEngine(); SetupVideo(); JoinChannel(); SetupLocalVideo(); HideVideoMuted(); SetupButtons(); }
为了考虑用户体验,当用户点击视图时,我们将隐藏/显示按钮(更确切地说是包含按钮的视图)。创建一个方法 (SetupButtons
),并在其中创建一个点击手势识别器(类型:UITapGestureRecognizer
),该识别器执行调用 ViewTapped()
方法的操作。将点击手势识别器添加到视图并启用视图的用户交互。
结论
您完成了!只需几个步骤就可以让视频通话应用程序正常运行。如果您有任何疑问,请随时通过电子邮件 sid.sharma@agora.io 或在 RTC Dev Slack 频道 与我们联系。