驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
腾讯实时音视频的回调-ITRTCCloudCallBack
/  

腾讯实时音视频的回调-ITRTCCloudCallBack

开篇

这篇文章是 TRTC 在 C#的 Winform 的第二篇文章,主要聊聊下面的话题。

  • ITRTCCloud 核心的 trtc 操作类,非本文重点。
  • ITRTCCloudCallBack 核心的回调,本文重点。

ITRTCCloud

这个类应该是腾讯基于底层 C++ 的封装,是 流媒体服务器 同本地交互的核心。

其中有不少重要的 API,具体的可以参考该地址:API文档

这个不是今天重点,所以以后抽空聊。

ITRTCCloudCallBack

这个类通过名字看出,基本都是事件的回调,该类也是非常重要的,详情可以参考 API 地址:CallBack API

onUserVideoAvailable

这里我说一个关键的回调事件,因为 API 文档上,对该 API 描述有 歧义

void onUserVideoAvailable (string userId, bool available)

这个 API 在原文的描述是:

用户是否开启摄像头视频[X]

当您收到 onUserVideoAvailable(userId, YES) 通知时,代表该路画面已经有可用的视频数据帧到达。

之后,您需要调用 startRemoteView(userId) 接口加载该用户的远程画面。

再之后,您还会收到名为 onFirstVideoFrame(userId) 的首帧画面渲染回调。

当您收到了 onUserVideoAvailable(userId, NO) 通知时,代表该路远程画面已经被关闭,这可能是 由于该用户调用了 muteLocalVideo() 或 stopLocalPreview() 所致。

这个 API 描述中,大部分都是正确的,但是提纲挈领的第一句话存在歧义: 用户是否开启摄像头视频 。根据下面的描述,我认为它应该是: 本地开始接收远端用户的视频帧的回调

既然是回调,那么形参中的 userIdavailable 都应该是 invoker 赋值好了的,所以我们要根据这些参数来进行不同的处理。

  • userId 表示的加入进本次通话的远端用户的 id
  • available 是一个标注,可能是 true 或者 false
    • true :表示的用户进入本次聊天。
    • false :表示远端用户退出本次聊天

那么根据上述知识,我们可以看到这个方法中做了如下事情:

  • availabletrue 的时候,就是表示远程用户的视频收到了
    • 调用 SetVisableInfoView 将窗体的可见性设置为 true
    • 此时调用尝试调用 GetIdleRemoteVideoPosition 找到空闲的控件窗口,将视频渲染上去。
    • 如果没有找到,那么直接退出该方法(有些粗暴,自己实现的时候,可以更加灵活)
    • 在找到可用于渲染视频的控件的时候,根据 mRenderMode 的不同方式进行渲染。
      • 1 为真窗口渲染(通过窗口句柄传入 SDK),默认为 1.
      • 2 为自定义渲染(使用 TXLiteAVVideoView 渲染)
    • 然后将窗体的 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);
}
  • availablefalse 的时候,表示远程用户要退出了,此时进行了如下操作。
    • 找到此用户在主窗体的视频渲染控件。
    • 调用 SetVisableInfoView 将其可见性设置为 false
    • 然后调用 _trtcCloud.stopRemoteView(userId); 来停止远程视频渲染。
    • 最后根据渲染模式的不同来做处理,具体的代码如下所示,很奇怪的是它指出只有 2 这个模式下才需要处理
    • 剩下的操作(暂时用不到,就不具体分析了)
if (mRenderMode == 2) {
	Panel panel = GetPanelAndSetUserId(pos, userId, false, false);
	if (panel != null) RemoveCustomVideoView(panel, userId, TRTCVideoStreamType.TRTCVideoStreamTypeBig);
}

综上所述:整体而言,在该事件中,主要就是: 找到对应的渲染控件,进行(移除)视频渲染

onFirstVideoFrame

public void onFirstVideoFrame(string userId, TRTCVideoStreamType streamType, int width, int height)

这个回调会在以下 3 种情况下触发:

  • startLocalPreview()
  • startRemoteView()
  • startRemoteSubStreamView()

其中有形参需要说明下:

  • userId 如果为 空字符 那么就是由 startLocalPreview() 触发的,此时应该是开启了本地的摄像头。

在腾讯给出的 Demo 中,这个回调主要是用来进行 混流 的,因为暂时没有用到,所以不做讲解。

onRemoteUserEnterRoom

用户进入房间和推出方法,还有 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# 不会吗?
  • Demo 中保存用户的信息用的是 List ,既然在频繁的查找,用 Dictionary 不是更好吗?

不过可能写这个 Demo 的人是 C++ 出身,所以这些小问题你看代码的时候,应该都能解决。

结语

啰啰嗦嗦的把这篇文写完了,花了我 2 个小时...

不过写文章比看代码的收获大多了,因为看代码的时候,非常容易囫囵吞枣,而写代码的时候就必须要仔细分析了。

最后,因为我也就陆陆续续看了一天,所以行文中应该会存在瑕疵,你觉得写的不对的,可以留言讨论。

积土成山,风雨兴焉。积水成渊,蛟龙生焉。