国产免费无码不卡A片视频喷水_3p一女两男三飞做爰视频_91九色视频在线观看_14小箩洗澡裸体高清视频_影音先锋成人网

當(dāng)前位置: 首頁 > 資訊

C# 實(shí)現(xiàn) Linux 視頻會(huì)議(源碼,支持信創(chuàng)環(huán)境,銀河麒麟,統(tǒng)信UOS)

日期:2023-05-16 09:34:43 來源:博客園

信創(chuàng)是現(xiàn)階段國家發(fā)展的重要戰(zhàn)略之一,面對(duì)這一趨勢,所有的軟件應(yīng)用只有支持信創(chuàng)國產(chǎn)化的基礎(chǔ)軟硬件設(shè)施,在未來才不會(huì)被淘汰。那么,如何可以使用C#來實(shí)現(xiàn)支持信創(chuàng)環(huán)境的視頻會(huì)議系統(tǒng)嗎?答案是肯定的。


【資料圖】

本文講述如何使用C#來實(shí)現(xiàn)視頻會(huì)議系統(tǒng)的Linux服務(wù)端與Linux客戶端,并讓其支持國產(chǎn)操作系統(tǒng)(如銀河麒麟,統(tǒng)信UOS)和國產(chǎn)CPU(如鯤鵬、龍芯、海光、兆芯、飛騰等)。

先看看該Demo在統(tǒng)信UOS上的運(yùn)行效果:

一.功能介紹1.基本功能

(1)主持人:當(dāng)進(jìn)入同一房間的第一個(gè)用戶默認(rèn)成為主持人,默認(rèn)打開麥克風(fēng)。

(2)當(dāng)進(jìn)入會(huì)議房間的每個(gè)人,都能自由選擇是否開啟攝像頭、揚(yáng)聲器和麥克風(fēng)。

(3)當(dāng)同一房間內(nèi)無人開啟桌面共享時(shí),所有用戶均可開啟桌面共享,供其他用戶觀看其桌面,同一時(shí)間內(nèi)只允許一個(gè)用戶開啟桌面共享。

(4)當(dāng)用戶為主持人時(shí),可以選擇是否開啟電子白板;當(dāng)主持人開啟電子白板后,所有用戶均可自由切換電子白板和會(huì)議視頻。

(5)每個(gè)用戶的視頻窗口上方均顯示聲音分貝條,根據(jù)聲音大小自動(dòng)渲染。

(6)當(dāng)用戶關(guān)閉攝像頭或者用戶數(shù)量超過9個(gè),不顯示視頻。

(7)所有用戶均可收發(fā)文字消息,包括帶表情的文字消息。

2.功能演示

在銀河麒麟上運(yùn)行:

3.布局風(fēng)格

(1)當(dāng)只有一個(gè)人開啟視頻時(shí),采用大視窗顯示

(2)當(dāng)2~4人開啟視頻時(shí),使用2x2布局

(3)當(dāng)超過4人開啟視頻時(shí),使用3x3布局

二.開發(fā)環(huán)境1.開發(fā)工具:

Visual Studio 2022

2. 開發(fā)框架:

.NET Core 3.1,.NET 6,.NET 7

3.開發(fā)語言:

C#

4.其它框架:

CPF.net UI 框架、OMCS 語音視頻框架

三.具體實(shí)現(xiàn)1. 新用戶進(jìn)入會(huì)議房間

(1)視頻顯示窗口控件VideoPanel

預(yù)定SomeoneJoin事件,當(dāng)新的用戶加入房間時(shí),將觸發(fā)該事件:

this.chatGroup.SomeoneJoin += new CbGeneric(chatGroup_SomeoneJoin);
void chatGroup_SomeoneJoin(IChatUnit unit){    if (!Dispatcher.CheckAccess())    {        Dispatcher.BeginInvoke(new CbGeneric(this.chatGroup_SomeoneJoin), unit);    }    else    {        VideoPanel panel = new VideoPanel();        panel.Initialize(unit, false);        VideoHidePanel videoHidePanel = new VideoHidePanel();        videoHidePanel.Initialize(false, unit,false);        VideoAllPanel videoAllPanel;        videoAllPanel.videoPanel = panel;        videoAllPanel.videoHidePanel = videoHidePanel;        if (panel.ConnectCameraResult != ConnectResult.Succeed)        {            this.flowLayoutPanel2.Children.Insert(0, videoHidePanel);            videoHidePanel.cameraVisibilityChange(false);        }        else        {            if (this.isVideoShowBeyond)            {                this.flowLayoutPanel2.Children.Insert(0, videoHidePanel);                videoHidePanel.cameraVisibilityChange(true);            }            else            {                this.cameraViewbox.FlowLayoutPanel.Children.Insert(this.videoShowCount, panel);            }        }        this.TotalNumChange(this.chatGroup.GetOtherMembers().Count + 1);        panel.ChangeState += Panel_ChangeState;        panel.CameraStateChange += Panel_CameraStateChange;        panel.VideoByCount += Panel_VideoByCount;        panel.VideoConnect += Panel_VideoConnect;        unit.Tag = videoAllPanel;        this.VideoNumBeyond();        this.VideoSizeChange(new System.Windows.Size(this.cameraViewbox.FlowLayoutPanel.Width, this.cameraViewbox.FlowLayoutPanel.Height));    }}

其中VideoPanel 是視頻窗口顯示控件,初始化如下:

public void Initialize(IChatUnit unit, bool myself){    this.pictureBox_Mic.RenderSize = new System.Windows.Size(24, 24);    this.chatUnit = unit;    this.isMySelf = myself;    this.toolStrip1.Text = chatUnit.MemberID;    //初始化麥克風(fēng)連接器                this.chatUnit.MicrophoneConnector.ConnectEnded += new CbGeneric(MicrophoneConnector_ConnectEnded);    this.chatUnit.MicrophoneConnector.OwnerOutputChanged += new CbGeneric(MicrophoneConnector_OwnerOutputChanged);        if (!this.isMySelf)    {        this.chatUnit.MicrophoneConnector.BeginConnect(unit.MemberID);    }    if (this.isMySelf)    {        this.videoSizeShow.Visibility = Visibility.Collapsed;    }    //初始化攝像頭連接器    this.chatUnit.DynamicCameraConnector.SetViewer(this.cameraPanel1);    this.chatUnit.DynamicCameraConnector.VideoDrawMode = VideoDrawMode.Scale;    this.chatUnit.DynamicCameraConnector.ConnectEnded += new CbGeneric(DynamicCameraConnector_ConnectEnded);    this.chatUnit.DynamicCameraConnector.OwnerOutputChanged += new CbGeneric(DynamicCameraConnector_OwnerOutputChanged);    this.chatUnit.DynamicCameraConnector.Disconnected += new CbGeneric(DynamicCameraConnector_Disconnected);    this.chatUnit.DynamicCameraConnector.OwnerVideoSizeChanged += DynamicCameraConnector_OwnerVideoSizeChanged;    this.chatUnit.DynamicCameraConnector.BeginConnect(unit.MemberID);}

當(dāng)新用戶進(jìn)入房間時(shí),房間內(nèi)其他用戶通過MicrophoneConnector麥克風(fēng)連接器和DynamicCameraConnector攝像頭連接器連接到該用戶的麥克風(fēng)和攝像頭

(2)開啟或關(guān)閉攝像頭、麥克風(fēng)、揚(yáng)聲器

以開啟或關(guān)閉攝像頭為例:

private void skinCheckBox_camera_MouseDown(object sender, MouseButtonEventArgs e){    this.isMyselfVideo = !this.isMyselfVideo;    System.Windows.Controls.Image imageVideo = sender as System.Windows.Controls.Image;    imageVideo.Source = this.isMyselfVideo ? ResourceManager.Singleton.CameraOpenImage : ResourceManager.Singleton.CameraCloseImage;    imageVideo.ToolTip = this.isMyselfVideo ? "攝像頭:開" : "攝像頭:關(guān)";    VideoPanel myPanel = ((VideoAllPanel)this.chatGroup.GetMember(this.loginId).Tag).videoPanel;    VideoHidePanel myHidePanel = ((VideoAllPanel)this.chatGroup.GetMember(this.loginId).Tag).videoHidePanel;    this.VideoNumBeyond();    if (this.isVideoShowBeyond)    {        myHidePanel.cameraVisibilityChange(this.isMyselfVideo);    }    else    {        if (!isMyselfVideo)        {            this.cameraViewbox.FlowLayoutPanel.Children.Remove(myPanel);            myPanel.cameraPanel1.ClearImage();            this.flowLayoutPanel2.Children.Insert(0, myHidePanel);            myHidePanel.cameraVisibilityChange(false);        }        else        {            this.flowLayoutPanel2.Children.Remove(myHidePanel);            this.cameraViewbox.FlowLayoutPanel.Children.Insert(this.videoShowCount, myPanel);        }    }        if (IsInitialized)    {        this.multimediaManager.OutputVideo = this.isMyselfVideo;        this.VideoSizeChange(new System.Windows.Size(this.cameraViewbox.FlowLayoutPanel.Width, this.cameraViewbox.FlowLayoutPanel.Height));    }}

其中通過多媒體管理器multimediaManager的OutputVideo屬性,設(shè)置是否將采集到的視頻輸出,進(jìn)而控制攝像頭的開啟或關(guān)閉。

2.布局切換

(1)根據(jù)開啟視頻的用戶數(shù)量可分為 1x1、2x2、3x3三種布局格式

(2)根據(jù)不同的布局格式以及外部控件容器的寬高,手動(dòng)計(jì)算視頻控件的寬高。

private void VideoSizeChange(Size size){    if (this.cameraViewbox.FlowLayoutPanel.Children.Count > 4)    {        foreach (VideoPanel panel in this.cameraViewbox.FlowLayoutPanel.Children)        {            panel.Height = (size.Height - 6) / 3;            panel.Width = (size.Width - 12) / 3;        }    }    else if (this.cameraViewbox.FlowLayoutPanel.Children.Count <= 4 && this.cameraViewbox.FlowLayoutPanel.Children.Count > 1)    {        foreach (VideoPanel panel in this.cameraViewbox.FlowLayoutPanel.Children)        {            panel.Height = (size.Height - 4) / 2;            panel.Width = (size.Width - 8) / 2;        }    }    else if (this.cameraViewbox.FlowLayoutPanel.Children.Count == 1)    {        foreach (VideoPanel panel in this.cameraViewbox.FlowLayoutPanel.Children)        {            panel.Height = size.Height - 2;            panel.Width = size.Width - 4;        }    }}

通過流式布局控件的特性:超過流式控件的寬度,子控件將自動(dòng)換行,修改視頻控件的寬高;

外部容器實(shí)際容納所有視頻控件的寬高為:外部容器的寬高減去所有視頻控件的外邊距;

當(dāng)只有一個(gè)用戶開啟視頻,即將使用1x1布局時(shí),視頻控件寬高即為外部容器實(shí)際容納所有視頻控件的寬高;

當(dāng)2~4人開啟視頻,即將使用2x2布局時(shí),視頻控件寬高即為外部容器實(shí)際容納所有視頻控件的寬高的1/2,此時(shí)每個(gè)視頻控件將占外部控件的1/4;

當(dāng)超過4人開啟視頻,即將使用3x3布局時(shí),視頻控件寬高即為外部容器實(shí)際容納所有視頻控件的寬高的1/3,此時(shí)每個(gè)視頻控件將占外部控件的1/9

3.自定義消息類型
public static class InformationTypes{    #region 廣播消息    ///     /// 廣播聊天信息    ///     public const int BroadcastChat = 0;    ///     /// 廣播共享桌面    ///     public const int BroadcastShareDesk = 1;    ///     /// 廣播白板    ///     public const int BroadcastWhiteBoard = 2;    #endregion    #region 給新加入成員發(fā)送消息   ///     /// 共享桌面    ///     public const int ShareDesk = 53;    ///     /// 電子白板    ///     public const int WhiteBoard = 54;    #endregion    ///     /// 獲取服務(wù)端 組擴(kuò)展信息    ///     public const int GetGroupExtension = 101;}

(1)當(dāng)用戶發(fā)送聊天消息時(shí),將通過BroadcastChat向所有在線用戶廣播聊天消息;當(dāng)用戶開啟桌面共享時(shí),將通過BroadcastShareDesk向所有在線用戶廣播桌面共享消息;當(dāng)主持人開啟電子白板時(shí),將通過BroadcastWhiteBoard

向所有在線用戶廣播電子白板消息。

(2)當(dāng)用戶上線時(shí),如果有用戶開啟桌面共享,就將通過ShareDesk向新用戶發(fā)送桌面共享消息;如果主持人開啟電子白板,就將通過WhiteBoard向新用戶發(fā)送電子白板消息。

(3)用戶將通過GetGroupExtension向服務(wù)端獲取組擴(kuò)展信息。

4.組擴(kuò)展信息
public class GroupExtension{                   ///     /// 主持人ID    ///     public string ModeratorID { get; set; }        ///     /// 正在共享遠(yuǎn)程桌面的用戶ID    ///     public string DesktopSharedUserID { get; set; }    ///     /// 主持人是否開啟白板    ///     public bool IsModeratorWhiteBoardNow { get; set; }}

(1)ModeratorID 表示當(dāng)前房間主持人的ID;

(2)DesktopSharedUserID 正在桌面共享的用戶ID;若值為null,表示當(dāng)前房間內(nèi)無人開啟桌面共享,客戶端通過該值判斷當(dāng)前是否有用戶開啟桌面共享;當(dāng)用戶開啟或關(guān)閉桌面共享時(shí),都將手動(dòng)修改該值;

(3)IsModeratorWhiteBoardNow表示當(dāng)前主持人是否開啟電子白板;當(dāng)主持人開啟或關(guān)閉電子白板時(shí),都將手動(dòng)修改該值。

四.源碼下載1. 源碼項(xiàng)目說明

源碼下載

(1)OVCS.ServerLinux :視頻會(huì)議 Linux 服務(wù)端

(2)OVCS.ClientLinux :視頻會(huì)議 Linux 客戶端

注:Linux客戶端內(nèi)置的是x86/x64非托管so庫,若需要其它架構(gòu)的so,請(qǐng)聯(lián)系QQ:2027224508 獲取。

(3)另附上Android客戶端的源碼:Android端 。

2.部署運(yùn)行說明

在部署之前,需要在linux服務(wù)端和客戶端上分別安裝 .Net core 3.1版本,命令行安裝命令如下:

yum install dotnet-sdk-3.1

檢查版本安裝情況

dotnet --version

運(yùn)行:

(1)在CentOS上啟動(dòng)OVCS.ServerLinux服務(wù)端:拷貝OVCS.ServerLinux項(xiàng)目下的Debug文件夾,到CentOS操作系統(tǒng)上,打開Debug -> netcoreapp3.1目錄 ,在目錄下打開終端,執(zhí)行以下命令啟動(dòng)服務(wù)端

dotnet OVCS.ServerLinux.dll

(2)在麒麟或統(tǒng)信UOS、Ubuntu上運(yùn)行OVCS.ClientLinux客戶端:拷貝OVCS.ClientLinux項(xiàng)目下的Debug文件夾,到麒麟或統(tǒng)信UOS、Ubuntu操作系統(tǒng)上,打開Debug -> netcoreapp3.1目錄 ,在目錄下打開終端,執(zhí)行以下命令啟動(dòng)客戶端

dotnet OVCS.ClientLinux.dll

標(biāo)簽:

熱門推薦

猜你喜歡

市場