这篇文章是 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
将窗体的可见性设置为 true
GetIdleRemoteVideoPosition
找到空闲的控件窗口,将视频渲染上去。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});
mPKUsers
LeaveRoom
的作用主要是:
id
RemoteUserInfo
经过我的测试,这 3 个事件的触发顺序是:
onRemoteUserEnterRoom ➡ onUserVideoAvailable(userId,true)➡onFirstVideoFrame
onUserVideoAvailable ➡ 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 个小时...
不过写文章比看代码的收获大多了,因为看代码的时候,非常容易囫囵吞枣,而写代码的时候就必须要仔细分析了。
最后,因为我也就陆陆续续看了一天,所以行文中应该会存在瑕疵,你觉得写的不对的,可以留言讨论。