
这篇文章是 TRTC 在 C#的 Winform 的第二篇文章,主要聊聊下面的话题。
ITRTCCloud 核心的 trtc 操作类,非本文重点。ITRTCCloudCallBack 核心的回调,本文重点。这个类应该是腾讯基于底层 C++ 的封装,是 流媒体服务器 同本地交互的核心。
其中有不少重要的 API,具体的可以参考该地址:API文档
这个不是今天重点,所以以后抽空聊。
这个类通过名字看出,基本都是事件的回调,该类也是非常重要的,详情可以参考 API 地址:CallBack API
这里我说一个关键的回调事件,因为 API 文档上,对该 API 描述有 歧义 。
void onUserVideoAvailable (string userId, bool available)
这个 API 在原文的描述是:
用户是否开启摄像头视频[X]
当您收到 onUserVideoAvailable(userId, YES) 通知时,代表该路画面已经有可用的视频数据帧到达。
之后,您需要调用 startRemoteView(userId) 接口加载该用户的远程画面。
再之后,您还会收到名为 onFirstVideoFrame(userId) 的首帧画面渲染回调。
当您收到了 onUserVideoAvailable(userId, NO) 通知时,代表该路远程画面已经被关闭,这可能是 由于该用户调用了 muteLocalVideo() 或 stopLocalPreview() 所致。
这个 API 描述中,大部分都是正确的,但是提纲挈领的第一句话存在歧义: 用户是否开启摄像头视频 。根据下面的描述,我认为它应该是: 本地开始接收远端用户的视频帧的回调 。
既然是回调,那么形参中的 userId 和 available 都应该是 invoker 赋值好了的,所以我们要根据这些参数来进行不同的处理。
userId 表示的加入进本次通话的远端用户的 idavailable 是一个标注,可能是 true 或者 false
true :表示的用户进入本次聊天。false :表示远端用户退出本次聊天那么根据上述知识,我们可以看到这个方法中做了如下事情:
available 为 true 的时候,就是表示远程用户的视频收到了
SetVisableInfoView 将窗体的可见性设置为 trueGetIdleRemoteVideoPosition 找到空闲的控件窗口,将视频渲染上去。mRenderMode 的不同方式进行渲染。
index ,存储起来(TOOD:有什么用?)当开始渲染的时候,窗体渲染的代码是这样的:
if (mRenderMode == 1) {
IntPtr ptr = GetHandleAndSetUserId(pos, userId, false);
//Fit 和 Fill 。
_trtcCloud.setRemoteViewFillMode(userId, DataManager.GetInstance().videoFillMode);
//当用户的视频可用的时候,开启远程预览。
_trtcCloud.startRemoteView(userId, ptr);
}
//2表示TxLiteVideoView
else if (mRenderMode == 2) {
Panel panel = GetPanelAndSetUserId(pos, userId);
//Q:此处的句柄为什么是IntPtr.Zero了?
_trtcCloud.startRemoteView(userId, IntPtr.Zero);
if (panel != null)
AddCustomVideoView(panel, userId, TRTCVideoStreamType.TRTCVideoStreamTypeBig);
}
available 为 false 的时候,表示远程用户要退出了,此时进行了如下操作。
SetVisableInfoView 将其可见性设置为 false_trtcCloud.stopRemoteView(userId); 来停止远程视频渲染。2 这个模式下才需要处理if (mRenderMode == 2) {
Panel panel = GetPanelAndSetUserId(pos, userId, false, false);
if (panel != null) RemoveCustomVideoView(panel, userId, TRTCVideoStreamType.TRTCVideoStreamTypeBig);
}
综上所述:整体而言,在该事件中,主要就是: 找到对应的渲染控件,进行(移除)视频渲染
public void onFirstVideoFrame(string userId, TRTCVideoStreamType streamType, int width, int height)
这个回调会在以下 3 种情况下触发:
其中有形参需要说明下:
userId 如果为 空字符 那么就是由 startLocalPreview() 触发的,此时应该是开启了本地的摄像头。在腾讯给出的 Demo 中,这个回调主要是用来进行 混流 的,因为暂时没有用到,所以不做讲解。
用户进入房间和推出方法,还有 2 个类似的回调:
// 远程用户进入房间
public void onRemoteUserEnterRoom(string userId);
//远程用户离开房间
public void onRemoteUserLeaveRoom(string userId, int reason);
EnterRoom 其作用主要是:
id (其实 demo 是在 onUserVideoAvailable 中操作的,我觉得放这里更合适)RemoteUserInfo ,具体的代码如下 _remoteUsers.Add(new RemoteUserInfo() {userId = userId, position = -1});mPKUsersLeaveRoom 的作用主要是:
idRemoteUserInfo经过我的测试,这 3 个事件的触发顺序是:
onRemoteUserEnterRoom ➡ onUserVideoAvailable(userId,true)➡onFirstVideoFrameonUserVideoAvailable ➡ onRemoteUserLeaveRoom(userId,false)该 Demo 中,选择通过 onUserVideoAvailable 来进行视频渲染的处理,而不是 EnterRoom 或者 LeaveRoom 的时候处理,还是很有道理的,原因请自己思考下。
看了腾讯的这个 Demo,我有几点想说的。
C# 的 API 用的不够优美
List ,原本的代码是通过 for 循环根据 userId 查找,我认位可以用 更语义化 的 FirstOrDefault 查找。Dictionary 中的数据的时候,也是通过 for 循环去做的,这个时间复杂度可是 o(n) ,直接通过 key 去找不更好吗? o(1) 的复杂度不香吗?for 循环的时候,删除元素。这个在 Java 中是会报错的, C# 不会吗?List ,既然在频繁的查找,用 Dictionary 不是更好吗?不过可能写这个 Demo 的人是 C++ 出身,所以这些小问题你看代码的时候,应该都能解决。
啰啰嗦嗦的把这篇文写完了,花了我 2 个小时...
不过写文章比看代码的收获大多了,因为看代码的时候,非常容易囫囵吞枣,而写代码的时候就必须要仔细分析了。
最后,因为我也就陆陆续续看了一天,所以行文中应该会存在瑕疵,你觉得写的不对的,可以留言讨论。