
要在一段时间前做一个项目,客户需要记录视频对话的整个过程,以便将来可以随时观看. 想要记录整个视频聊天过程. 这种功能应该是一个相对普遍的要求. 例如,基于网络语音视频的1: 1口语英语辅导. 如果您可以记录整个辅导过程以生成标准MP4文件,那么这是一个难得的信息,可以稍后轻松查看和共享. 我实现了1: 1视频对话录制的功能作为VideoChatRecorder组件,每个人都可以方便地重用. 并且,我在GG的最新版本4.3中使用了它,以便GG还具有视频聊天记录的功能.
(对于想要直接下载体验的朋友,请单击: “下载中心”)
如果您已经完成了录制单个人的相机和麦克风之类的程序,那么录制两个人的视频聊天将遇到两个新的困难:
(1)如何将两个人的视频图像集成到一个图像中?
(2)如何将两个人的声音融合在一起?
使用.NET提供的GDI +技术,我们可以将两张图片组合为一张. 在实现VideoChatRecorder组件时,我用来合成图片的规则如下:

(1)使用对方的视频作为录制的主题,您自己的视频将覆盖在对方的视频的右下角.
(2)另一方视频的大小是其摄像机的采集分辨率. 根据(1),我们知道,这也是播放记录生成的MP4文件时视频的大小.
(3)合成后您自己的视频图像的宽度和高度设置为对方视频宽度和高度的1/3.
合成视频的如下:

我们可以手动将自己的声音与另一方的声音混合. 在互联网上可以找到许多混合算法(例如直接加法,平均,归一化,衰减因子等),但是,混合算法的质量直接关系到最终的混合质量.

一个更简单的解决方案是直接使用OMCS提供的AudioInOutMixer组件,该组件可以混合麦克风收集的声音(即您自己的声音)和扬声器播放的声音(即另一方的声音)合而为一. 并通过AudioMixed事件公开混合数据.
解决了视频合成和音频合成的两个关键难题之后,我们可以将整个实现过程串联起来.
(1)使用摄像机连接器实例连接到另一方的摄像机,然后调用其GetCurrentImage方法以获取另一方的视频图像.
(2)使用另一个摄像机连接器实例连接到您自己的摄像机,然后调用其GetCurrentImage方法以获取您自己的视频图像.
(3)使用MFile提供的VideoFileMaker将语音和视频记录到标准MP4文件中.
(4)使用AudioInOutMixer实例进行混合. 安排其AudioMixed事件以获取混合语音数据,并将其提交给VideoFileMaker记录声音.

(5)使用后台线程,每100毫秒(即对应的帧频为10fps)调用前两个连接器的GetCurrentImage方法,并将返回的两个图像合成为一个,然后将其转换为“提交给VideoFileMaker”以记录图像.
这里的关键是使用GDI +进行图像合成的过程. 代码相对简单,如下所示:
Bitmap bmFriend = this.dynamicCameraConnector2Friend.GetCurrentImage(); if (bmFriend != null) { Bitmap bmMyself = this.cameraConnector2Myself.GetCurrentImage(); //合成图像 if (bmMyself != null) { Graphics g = Graphics.FromImage(bmFriend); g.DrawImage(bmMyself ,this.myVideoRect); g.Dispose(); } //录制图像 this.videoFileMaker.AddVideoFrame(bmFriend); }
注意: 如果您不想将自己的视频图像叠加到另一方的图像上,则可以略微修改以上代码. 您可以创建一个新的位图,然后在上面的不同区域中绘制对方的图像和您自己的图像. 当然,应正确设置新位图的大小以及另一方及其图像在新位图中的布局位置.
(6)停止记录后,用于合成图像的后台线程也将停止,并且VideoFileMaker也将关闭.
注意: 在某些配置较差的计算机上,生产速度可能比记录速度(即消费速度)快qq自动接收图片 video,因此,当VideoFileMaker关闭时,它将阻塞一段时间,直到所有视频全部缓存在返回之前将帧写入记录文件.

具有上述总体思想之后qq自动接收图片 video,很容易理解VideoChatRecorder的完整代码.
/// <summary> /// 视频聊天录制器。将视频聊天的完整过程录制成标准的MP4文件。 /// </summary> class VideoChatRecorder : IDisposable { private DynamicCameraConnector dynamicCameraConnector2Friend ; //连接到好友的连接器。 private CameraConnector cameraConnector2Myself; //连接到自己的连接器。 private IMultimediaManager multimediaManager; private VideoFileMaker videoFileMaker; private Size videoSize; private Rectangle myVideoRect; private volatile bool isRecording = false; private AudioInOutMixer audioInOutMixer; public VideoChatRecorder(IMultimediaManager mgr ,DynamicCameraConnector friend, CameraConnector myself) { this.multimediaManager = mgr; this.dynamicCameraConnector2Friend = friend; this.cameraConnector2Myself = myself; this.dynamicCameraConnector2Friend.Disconnected += new ESBasic.CbGeneric<ConnectorDisconnectedType>(dynamicCameraConnector2Friend_Disconnected); //混音器。将自己和对方的声音混成一路。 this.audioInOutMixer = new AudioInOutMixer(); this.audioInOutMixer.AudioMixed += new CbGeneric<byte[]>(audioInOutMixer_AudioMixed); } //得到混音数据,将其录制到文件。 void audioInOutMixer_AudioMixed(byte[] data) { if (this.isRecording) { this.videoFileMaker.AddAudioFrame(data); } } //连接器断开时,就停止录制。 void dynamicCameraConnector2Friend_Disconnected(ConnectorDisconnectedType obj) { if (!this.isRecording) { return; } this.Dispose(); } //初始化录像设备,并开始录制。 public void Initialize(string filePath) { if (!this.dynamicCameraConnector2Friend.Connected) { throw new Exception("连接器尚未连接到对方的!"); } this.videoSize = this.dynamicCameraConnector2Friend.VideoSize; Size myVideoSize = new Size(this.videoSize.Width / 3, this.videoSize.Height / 3); this.myVideoRect = new Rectangle(this.videoSize.Width - myVideoSize.Width, this.videoSize.Height - myVideoSize.Height, myVideoSize.Width, myVideoSize.Height); this.videoFileMaker = new VideoFileMaker(); this.videoFileMaker.AutoDisposeVideoFrame = true; this.videoFileMaker.Initialize(filePath, VideoCodecType.H264, this.videoSize.Width, this.videoSize.Height, 10, AudioCodecType.AAC, 16000, 1, true); this.audioInOutMixer.Initialize(this.multimediaManager); this.isRecording = true; CbGeneric cb = new CbGeneric(this.RecordThread); cb.BeginInvoke(null, null); } //录制线程。每隔100ms(对应VideoFileMaker的帧频为10fps)就合成一张图片,并录制它。 private void RecordThread() { while (this.isRecording) { Bitmap bmFriend = this.dynamicCameraConnector2Friend.GetCurrentImage(); if (bmFriend != null) { Bitmap bmMyself = this.cameraConnector2Myself.GetCurrentImage(); //合成图像 if (bmMyself != null) { Graphics g = Graphics.FromImage(bmFriend); g.DrawImage(bmMyself ,this.myVideoRect); g.Dispose(); } //录制图像 this.videoFileMaker.AddVideoFrame(bmFriend); } System.Threading.Thread.Sleep(100); } } /// <summary> /// 停止录制,并释放录制设备。 /// </summary> public void Dispose() { this.dynamicCameraConnector2Friend.Disconnected -= new ESBasic.CbGeneric<ConnectorDisconnectedType>(dynamicCameraConnector2Friend_Disconnected); this.audioInOutMixer.AudioMixed -= new CbGeneric<byte[]>(audioInOutMixer_AudioMixed); this.audioInOutMixer.Dispose(); if (!this.isRecording) { return; } this.isRecording = false; this.videoFileMaker.Close(true); } }
要下载最新版本,请转到此处.
在最新版本的GG中,上面的VideoChatRecorder类用于视频聊天记录,以生成MP4文件(默认为运行目录中名为VideoChat.mp4的文件),并且该文件与QQ视频播放器一起播放,效果如下:

________________________________________________________________________
欢迎讨论有关GG和GGMeeting的所有信息,我的QQ: 2027224508,请多多交流!
如果您有任何疑问或建议,可以留言或发送电子邮件至我的邮箱: 2027224508@qq.com.
如果您认为还可以,请关注我并再次尝试
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/bofangqi/article-271326-1.html
我们的卫星雷达预警系统