日期: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)簽:
愛滋病的早期癥狀是什么感覺_愛滋病的早期癥狀是什么
河南安陽:中國文字博物館開展“國際博物館日”主題活動(dòng)
熱文:金建高鐵、甬舟鐵路、杭紹甬高速……“浙”些工程有新進(jìn)展
深圳試行無人機(jī)送外賣 無人機(jī)行業(yè)市場深度分析 天天日?qǐng)?bào)
天天熱消息:安徽財(cái)經(jīng)大學(xué)2023年自考畢業(yè)論文怎么報(bào)名,報(bào)名流程是怎樣的?
世界簡訊:湖南航空首航七周年,目前全年運(yùn)營航線63條,通航41個(gè)城市
當(dāng)前要聞:湖人利好!哈姆放話2米35長臂男當(dāng)奇兵,球迷:約基奇轟45+15+12
國際物流業(yè)務(wù)員怎么找客戶_國際物流業(yè)務(wù)員怎樣找到自己的客戶|時(shí)訊
全球觀速訊丨高瀾股份:5月19日接受機(jī)構(gòu)調(diào)研,民生證券、東方阿爾法等多家機(jī)構(gòu)參與
舉信賴 敬真情 賴茅與世界羽聯(lián)再續(xù)合作情緣
焦點(diǎn)要聞:美聯(lián)儲(chǔ)6月加息概率降至15.7%
環(huán)球動(dòng)態(tài):1499元!暢享60 Pro即將發(fā)售
慶元旦演講稿3分鐘-慶元旦演講稿|每日精選
焦點(diǎn)信息:360路由器衛(wèi)士手機(jī)版安全下載_360路由衛(wèi)士
WE官博致謝前隊(duì)員:謝謝你們把WE帶到MSI決賽舞臺(tái)
六五環(huán)境日|濱州以督促改、以改促效,助力企業(yè)家成就夢想 環(huán)球?qū)崟r(shí)
環(huán)球報(bào)道:項(xiàng)目實(shí)戰(zhàn)-重塑企業(yè)求職新場景
“賣點(diǎn)”變“槽點(diǎn)”,長沙湘水郡中央空調(diào)成擺設(shè),業(yè)主吐槽:堪比“蒸桑拿”
環(huán)球關(guān)注:被辭退的艦長:希兒,敵人的陰謀(下)
每日信息:澤連斯基暗示烏克蘭東部城鎮(zhèn)巴赫穆特失守
057期李員外雙色球預(yù)測獎(jiǎng)號(hào):小復(fù)式9+3推薦 觀察
當(dāng)前短訊!銀翹解毒顆粒作用機(jī)制_銀翹解毒顆粒的作用
全球即時(shí)看!奇跡神戰(zhàn)什么職業(yè)厲害_奇跡戰(zhàn)神神賜等級(jí)列表
【環(huán)球快播報(bào)】1:0小勝主場大連人 河南隊(duì)艱難拿到聯(lián)賽首勝
當(dāng)前動(dòng)態(tài):蟲洞空間平臺(tái)_蟲洞空間