diff --git a/JianGongYun/TRTC/LiveClassroom.cs b/JianGongYun/TRTC/LiveClassroom.cs index 2d6aa0e..c441623 100644 --- a/JianGongYun/TRTC/LiveClassroom.cs +++ b/JianGongYun/TRTC/LiveClassroom.cs @@ -16,6 +16,7 @@ using System.Windows.Controls; using Window = System.Windows.Window; using System.Collections.Concurrent; using System.Windows.Data; +using System.Drawing; namespace JianGongYun.TRTC { @@ -46,7 +47,7 @@ namespace JianGongYun.TRTC public static ClassroomEntity CurrentClassroomEntity { get; private set; } public static TRTCCloudCallback TRTCCloudCallback = new TRTCCloudCallback(); - public static ConcurrentBag SaveFileTask = new ConcurrentBag(); + //private static ConcurrentBag VedioRecords = new ConcurrentBag(); //public static int UserCount = 999; /// /// 进入直播教室 @@ -138,7 +139,7 @@ namespace JianGongYun.TRTC /// /// 视频画面 /// - private static Dictionary VideoViews = new Dictionary(); + public static Dictionary VideoViews = new Dictionary(); /// /// 启动摄像头 /// @@ -149,20 +150,17 @@ namespace JianGongYun.TRTC { lTRTCCloud.startLocalPreview(IntPtr.Zero); liveWinMode.CameraRunning = true; - return AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeBig, true); + var view = AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeBig, true); + if (liveWinMode.IsLive) + { + VedioRecordTask(view, TRTCVideoStreamType.TRTCVideoStreamTypeBig); + } + return view; } return null; } - public static void ResizeVideoMain(Panel parent) - { - if (liveWinMode.CameraRunning) - { - StopVideoMain(parent); - StartVideoMain(parent); - } - } /// /// 停止摄像头 @@ -189,18 +187,15 @@ namespace JianGongYun.TRTC liveWinMode.ScreenRunning = true; SelectVieoSub(); lTRTCCloud.startScreenCapture(IntPtr.Zero, TRTCVideoStreamType.TRTCVideoStreamTypeSub, settingWindowViewModel.EncParams); - return AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true); + var view = AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true); + if (liveWinMode.IsLive) + { + VedioRecordTask(view, TRTCVideoStreamType.TRTCVideoStreamTypeSub); + } + return view; } return null; } - public static void ResizeVideoSub(Panel parent) - { - if (liveWinMode.ScreenRunning) - { - RemoveCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true); - AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true); - } - } /// /// 停止屏幕分享 /// @@ -238,7 +233,7 @@ namespace JianGongYun.TRTC if (liveWinMode.IsLive && !liveWinMode.AudioRecordRunning) { liveWinMode.AudioRecordRunning = true; - var time = DateTime.Now.ToString("yyyyMMddHHmmssfff"); + var time = Util.TimeStr(); var pars = new TRTCAudioRecordingParams { filePath = Path.Combine(RecoderDir, $"{time}audio.pcm") }; var res = lTRTCCloud.startAudioRecording(ref pars); Console.WriteLine(res); @@ -273,6 +268,65 @@ namespace JianGongYun.TRTC } } + + public static void VedioRecordTask(TXLiteAVVideoView view, TRTCVideoStreamType streamType) + { + var end = false; + ConcurrentQueue bitmaps = new ConcurrentQueue(); + view.OnRenderVideoFrameHandler += (b) => + { + bitmaps.Enqueue(b.ToBitmap()); + }; + view.OnViewRemove += () => + { + end = true; + }; + var task = Task.Factory.StartNew(() => + { + Console.WriteLine($"VedioRecordTask Start {streamType}"); + var _recoderDir = RecoderDir; + var imgTemp = Path.Combine(_recoderDir, $"{streamType}.png"); + var videoFile = Path.Combine(_recoderDir, $"{Util.TimeStr()}_{streamType}.avi"); + VideoWriter vw = new VideoWriter(); + Mat mat = default; + bool videoWriterInit = false; + while (!end || bitmaps.Count > 0) + { + if (bitmaps.Count == 0) + { + Thread.Sleep(100); + continue; + } + if (bitmaps.TryDequeue(out var bitmap)) + { + bitmap.Save(imgTemp, System.Drawing.Imaging.ImageFormat.Png); + bitmap.Dispose(); + mat = Cv2.ImRead(imgTemp, ImreadModes.AnyColor); + Cv2.ImShow(streamType.ToString(), mat); + Cv2.WaitKey(1); + if (!videoWriterInit) + { + vw.Open(videoFile, FourCC.H264, settingWindowViewModel.EncParams.videoFps, mat.Size()); + videoWriterInit = true; + } + vw.Write(mat); + } + else + { + Thread.Sleep(100); + } + } + mat?.Dispose(); + vw?.Release(); + vw?.Dispose(); + if (File.Exists(imgTemp)) + { + File.Delete(imgTemp); + } + Console.WriteLine($"VedioRecordTask End {streamType}"); + }, TaskCreationOptions.LongRunning); + } + /// /// 添加自定义渲染 View 并绑定渲染回调 /// @@ -294,6 +348,24 @@ namespace JianGongYun.TRTC return videoView; } + /// + /// 获取屏幕列表需要暂停屏幕分享渲染 + /// + /// + public static void WillGetScreens(Action action) + { + if (liveWinMode.ScreenRunning && VideoViews.TryGetValue($"{CurrentClassroomEntity.TeacherId}_{TRTCVideoStreamType.TRTCVideoStreamTypeSub}", out var view)) + { + view.SetPause(true); + action.Invoke(); + view.SetPause(false); + } + else + { + action.Invoke(); + } + } + /// /// 移除自定义渲染 View 并解绑渲染回调 /// diff --git a/JianGongYun/TRTC/Utils/Util.cs b/JianGongYun/TRTC/Utils/Util.cs index 8747f5f..f1b9559 100644 --- a/JianGongYun/TRTC/Utils/Util.cs +++ b/JianGongYun/TRTC/Utils/Util.cs @@ -83,12 +83,13 @@ namespace JianGongYun.TRTC.Utils public static WriteableBitmap ToWriteableBitmap(this TRTCImageBuffer data) { var writeableBitmap = new WriteableBitmap((int)data.width, (int)data.height, 96, 96, PixelFormats.Pbgra32, null); - writeableBitmap.Lock(); + writeableBitmap.Lock(); Marshal.Copy(data.buffer, 0, writeableBitmap.BackBuffer, (int)data.length); writeableBitmap.AddDirtyRect(new System.Windows.Int32Rect(0, 0, (int)data.width, (int)data.height)); writeableBitmap.Unlock(); return writeableBitmap; } + public static string TimeStr() => DateTime.Now.ToString("yyyyMMddHHmmssfff"); } } diff --git a/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs b/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs index d4b2c4d..2efc91c 100644 --- a/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs +++ b/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs @@ -229,7 +229,6 @@ namespace JianGongYun.TRTC.ViewModels } } - static object ScreenListLock = new object(); private SIZE thumbSize = new SIZE { cx = 300, cy = 200 }; private SIZE iconSize = new SIZE { cx = 50, cy = 50 }; /// @@ -237,7 +236,7 @@ namespace JianGongYun.TRTC.ViewModels /// public void LoadAllScreen() { - lock (ScreenListLock) + LiveClassroom.WillGetScreens(() => { //Console.WriteLine("get LiveScreens"); _LiveScreens.Clear(); @@ -254,7 +253,7 @@ namespace JianGongYun.TRTC.ViewModels _LiveScreens.Add(new TRTCScreenEntity { SourceId = info.sourceId, SourceName = info.sourceName, Type = info.type, Thumb = thumb, Info = info }); } temp.release(); - } + }); } diff --git a/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs b/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs index 1e7a4c1..095e695 100644 --- a/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs +++ b/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs @@ -640,6 +640,10 @@ namespace JianGongYun.TRTC.ViewModels #region 磁盘剩余空间 /// + /// 磁盘最低容量分界线 + /// + public const double DiskWarningSize = 5; + /// /// 磁盘剩余空间 /// public double DiskSize @@ -662,7 +666,7 @@ namespace JianGongYun.TRTC.ViewModels { get { - return DiskSize <= 20 ? Brushes.Orange : Brushes.LawnGreen; + return DiskSize <= DiskWarningSize ? Brushes.Orange : Brushes.LawnGreen; } } #endregion diff --git a/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs b/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs index cf119c4..83eb003 100644 --- a/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs +++ b/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs @@ -98,7 +98,9 @@ namespace JianGongYun.TRTC.Windows LiveClassroom.StopMic(); if (LiveWindowViewModel.IsLive) { - + LiveClassroom.StopVideoMain(AfterLiveViewWrap); + LiveClassroom.StopVideoSub(AfterLiveSubViewWrap); + LiveClassroom.StopMic(); } else { @@ -119,7 +121,7 @@ namespace JianGongYun.TRTC.Windows var start = Convert.ToBoolean(btn.Tag); if (start)//开始直播 { - if (SettingWindowViewModel.DiskSize <= 20) + if (SettingWindowViewModel.DiskSize <= ViewModels.SettingWindowViewModel.DiskWarningSize) { var res = AduMessageBox.Show("磁盘剩余空间不足,请选择其他磁盘", "提醒"); return; @@ -229,12 +231,12 @@ namespace JianGongYun.TRTC.Windows switch (LiveWindowViewModel.LiveType) { case Models.LiveTypeEnum.CameraAndScreen: - LiveClassroom.StopVideoSub(BeforeLiveSubViewWrap);//停止屏幕分享(直播中SDK再次获取窗口列表会卡死) + //LiveClassroom.StopVideoSub(BeforeLiveSubViewWrap);//停止屏幕分享(直播中SDK再次获取窗口列表会卡死) LiveWindowViewModel.ShowShareScreenList = true; this.Dispatcher.Invoke(new Action(() => LiveWindowViewModel.LoadAllScreen())); break; case Models.LiveTypeEnum.OnlyScreen: - LiveClassroom.StopVideoSub(BeforeLiveSubViewWrap);//停止屏幕分享(直播中SDK再次获取窗口列表会卡死) + //LiveClassroom.StopVideoSub(BeforeLiveSubViewWrap);//停止屏幕分享(直播中SDK再次获取窗口列表会卡死) LiveClassroom.StopVideoMain(BeforeLiveViewWrap); //停止摄像头分享 LiveWindowViewModel.ShowShareScreenList = true; this.Dispatcher.Invoke(new Action(() => LiveWindowViewModel.LoadAllScreen())); @@ -260,7 +262,7 @@ namespace JianGongYun.TRTC.Windows private void ShareList_Selected(object sender, RoutedEventArgs e) { CloseShareList_Click(sender, e); - if (LiveWindowViewModel.IsLive && LiveWindowViewModel.ScreenRunning) + if (LiveWindowViewModel.ScreenRunning) { LiveClassroom.SelectVieoSub(); } @@ -277,6 +279,7 @@ namespace JianGongYun.TRTC.Windows private void ChangeWin_Click(object sender, RoutedEventArgs e) { LiveWindowViewModel.ShowShareScreenList = true; + this.Dispatcher.Invoke(new Action(() => LiveWindowViewModel.LoadAllScreen())); } } }