驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
腾讯实时音视频 C# 的 Demo 解读(一)
/  

腾讯实时音视频 C# 的 Demo 解读(一)

开篇

因为公司项目需要,研究了下腾讯的实时音视频的C# Winform版本的Demo。

当然其他平台也有,不是我主要负责。

经过2天的摸索,对其代码和原理进行了一个简单的梳理。因为才接触腾讯的音视频直播,同时C# Winform相关的知识已经5年没碰了。

所以下面的内容,应该会出现一些偏差,仅供大家参考。

腾讯的Demo 下载地址:

核心类解读

其实整个项目的最为核心的文件是:TXLiteAVVideoViews。翻译过来的意思应该是腾讯轻量级的音视频视图

在这个文件中,包含了3个非常重要的类,我认为音视频在C#`层面上的核心。

  • TXLiteAVVideoView,继承自Pannel,将视频帧渲染到空间上。
  • TXLiteAVVideoViewManager,继承自ITRTCVideoRenderCallBack,承上启下,将底层传递的帧数据再传递给TXLiteAVVideoView
  • FrameBufferInfo帧数据的封装实体。

TXLiteAVVideoView

以下为我把非核心代码删除后的简单模型。

public class TXLiteAVVideoView: Panel {

	// 帧缓存,可以理解为视频暂停时候的一个画面
	private volatile FrameBufferInfo _mArgbFrame = new FrameBufferInfo();

    public bool AppendVideoFrame(byte[] data, int width, int height, TRTCVideoPixelFormat videoFormat, TRTCVideoRotation rotation) {
		//...
	}
    
	protected override void OnPaint(PaintEventArgs pe) {
		//....
	}
}
  • 该类继承了Pannl,又重写了OnPaint,所以可以猜测目的是为了根据Frame数据来绘图。

  • _mArgbFrame 的作用是保存的某一时刻的一帧数据,保存起来的目的是为了方便OnPaint来绘图,它由什么地方传递过来的了,我们看下面这段话?

  • AppendVideoFrameTXLiteAVVideoViewManager来调用,其中就传入了byte[] data这个还没有处理的的数据。

所以由此我们可以简单分析总结下:该类通过方法AppendVideoFrame接收TXLiteAVVideoViewManager传递过来的帧数据,在将帧数据保存到局部变量_mArgbFrame后调用refresh方法,该方法会调用重写后的OnPaint来画图。

TXLiteAVVideoViewManager

同样简化下代码:

class TXLiteAVVideoViewManager: ITRTCVideoRenderCallback {
    
	private volatile Dictionary<string,TXLiteAVVideoView> _mMapViews;

	public void onRenderVideoFrame(string userId, TRTCVideoStreamType streamType, TRTCVideoFrame frame) {
        //...
    }
}
  • 该类实现了接口ITRTCVideoRenderCallback的方法onRenderVideoFrame。通过签名,我大胆的猜测了从服务器拉数据的时候,数据中应该有远程用户的id,以及对应的数据帧和类型。
  • 其底层可能在不停的拉数据,然后不停的调用这个实现类来传递给对应的TXLiteAVVideoView进行视图渲染。
  • _mMapViews这个局部变量,通过userId-streamType来作为key,其TXLiteAVVideView作为value来保存的数据。

我们可以简单看看onRenderVideoFrame的实现

public void onRenderVideoFrame(string userId, TRTCVideoStreamType streamType, TRTCVideoFrame frame) {
	//....
	TXLiteAVVideoView view = null;
	lock(_mMapViews) {
		view = _mMapViews[GetKey(userId, streamType)];
	}
	//调用 AppendVideoFrame 进行帧的渲染
	view?.AppendVideoFrame(frame.data, (int) frame.width, (int) frame.height, frame.videoFormat, frame.rotation);
}

其本质也是从Dictionary中通过GetKey(userId, streamType)来构成key,获取到对应的view,然后进行AppendVideoFrame.

FrameBufferInfo

这个类的实现如下:

class FrameBufferInfo
{
        public byte[] data { get; set; }

        public int width { get; set; }

        public int height { get; set; }

        public bool newFrame { get; set; }

        /**
         * Rotation 是是否旋转
         */
        public TRTCVideoRotation rotation { get; set; }
}

表示的应该是这个帧如何处理。

结语

这是腾讯音视频实时通信的第一篇分析,后面会根据情况,看看有没有更多有意义的可以写文。

希望对大家有帮助。

不积跬步,无以至千里。不积小流,无以成江海。