diff --git a/JianGongYun/JianGongYun.csproj b/JianGongYun/JianGongYun.csproj index 3d696aa..aa6d4da 100644 --- a/JianGongYun/JianGongYun.csproj +++ b/JianGongYun/JianGongYun.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net45;net46 + netcoreapp3.1 true true true @@ -16,6 +16,8 @@ + + @@ -48,6 +50,11 @@ TRTC_SDK\Win32\lib\ManageLiteAV.dll + + + 4.5.1.20201226 + + diff --git a/JianGongYun/Properties/launchSettings.json b/JianGongYun/Properties/launchSettings.json new file mode 100644 index 0000000..81e5d4b --- /dev/null +++ b/JianGongYun/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "JianGongYun": { + "commandName": "Project", + "nativeDebugging": false + } + } +} \ No newline at end of file diff --git a/JianGongYun/Style/TRTCResource.xaml b/JianGongYun/Style/TRTCResource.xaml index d4856e1..32e7c32 100644 --- a/JianGongYun/Style/TRTCResource.xaml +++ b/JianGongYun/Style/TRTCResource.xaml @@ -7,6 +7,246 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JianGongYun/TRTC/Components/CountdownControl.xaml b/JianGongYun/TRTC/Components/CountdownControl.xaml new file mode 100644 index 0000000..c718f75 --- /dev/null +++ b/JianGongYun/TRTC/Components/CountdownControl.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/JianGongYun/TRTC/Components/CountdownControl.xaml.cs b/JianGongYun/TRTC/Components/CountdownControl.xaml.cs new file mode 100644 index 0000000..16b233a --- /dev/null +++ b/JianGongYun/TRTC/Components/CountdownControl.xaml.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace JianGongYun.TRTC.Components +{ + /// + /// CountdownControl.xaml 的交互逻辑 + /// + public partial class CountdownControl : UserControl + { + private Grid grid = null; + private Window wm; + public CountdownControl(Grid _grid, Window _wm) + { + InitializeComponent(); + grid = _grid; + wm = _wm; + } + + /// + /// 创建动画执行对象 + /// + private Storyboard sb = null; + /// + /// 给文本框赋值 + /// + private string txtValue = string.Empty; + public string TxtValue + { + get { return txtValue; } + set { txtValue = value; this.tb.Text = txtValue; } + } + private void UserControl_Loaded(object sender, RoutedEventArgs e) + { + //创建动画对象实例 + sb = new Storyboard(); + //ScaleX缩放动画 + //DoubleAnimation daX = new DoubleAnimation(); + //daX.Duration = TimeSpan.FromSeconds(0.6); + //daX.From = 1.35; + //daX.To = 1; + //Storyboard.SetTarget(daX, this); + //Storyboard.SetTargetProperty(daX, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)")); + ////ScaleY缩放动画 + //DoubleAnimation daY = new DoubleAnimation(); + //daY.Duration = TimeSpan.FromSeconds(0.6); + //daY.From = 1.35; + //daY.To = 1; + //Storyboard.SetTarget(daY, this); + //Storyboard.SetTargetProperty(daY, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)")); + //Opacity变换动画 + DoubleAnimation daO = new DoubleAnimation(); + daO.Duration = TimeSpan.FromSeconds(0.6); + daO.From = 0; + daO.To = 1; + Storyboard.SetTarget(daO, this); + Storyboard.SetTargetProperty(daO, new PropertyPath("(Opacity)")); + //sb.Children.Add(daX); + //sb.Children.Add(daY); + sb.Children.Add(daO); + + DoubleAnimation daTop2 = new DoubleAnimation(); + daTop2.BeginTime = TimeSpan.FromSeconds(0.7); + daTop2.Duration = TimeSpan.FromSeconds(0.2); + daTop2.EasingFunction = new BounceEase() { Bounces = 10, EasingMode = EasingMode.EaseInOut }; + Storyboard.SetTarget(daTop2, wm); + Storyboard.SetTargetProperty(daTop2, new PropertyPath("(Top)")); + sb.Children.Add(daTop2); + sb.Completed += new EventHandler(sb_Completed); + sb.Begin(this, true); + } + private void sb_Completed(object sender, EventArgs e) + { + //解除绑定 + sb.Remove(this); + sb.Children.Clear(); + grid.Children.Clear(); + } + } +} diff --git a/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml b/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml deleted file mode 100644 index e5cab13..0000000 --- a/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - diff --git a/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml.cs b/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml.cs deleted file mode 100644 index 8f5f274..0000000 --- a/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace JianGongYun.TRTC.Components -{ - /// - /// SytemGatherVolumeControl.xaml 的交互逻辑 - /// - public partial class SytemGatherVolumeControl : UserControl - { - public SytemGatherVolumeControl() - { - InitializeComponent(); - } - } -} diff --git a/JianGongYun/TRTC/Components/TXLiteAVVideoView.cs b/JianGongYun/TRTC/Components/TXLiteAVVideoView.cs new file mode 100644 index 0000000..eefed0e --- /dev/null +++ b/JianGongYun/TRTC/Components/TXLiteAVVideoView.cs @@ -0,0 +1,500 @@ +using ManageLiteAV; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace JianGongYun.TRTC.Components +{ + public class TXLiteAVVideoView : Canvas, IDisposable + { + private bool mOccupy = false; // view 是否已被占用 + private bool mLocalView = false; // 是否为本地画面 + private bool mPause = false; + private bool mFirstFrame = false; + + private string mUserId; + private TRTCVideoStreamType mStreamType; + private TRTCVideoFillMode mRenderMode = TRTCVideoFillMode.TRTCVideoFillMode_Fit; // 0:填充,1:适应 + + private volatile FrameBufferInfo mArgbFrame = new FrameBufferInfo(); // 帧缓存 + + // 位图缓存,防止GC频繁 + private WriteableBitmap mWriteableBitmap; + private Int32Rect mInt32Rect; + private Pen mPen; + + public TXLiteAVVideoView() + { + mPen = new Pen { Brush = Brushes.DarkGray, Thickness = 1 }; + } + + /// + /// 设置 View 绑定参数 + /// + /// 需要渲染画面的 userId,如果是本地画面,则传空字符串。 + /// 渲染类型 + /// TRTCCloud 实例,用户注册视频数据回调。 + /// 渲染本地画面,SDK 返回的 userId 为"" + /// true:绑定成功,false:绑定失败 + public bool RegEngine(string userId, TRTCVideoStreamType type, ITRTCCloud engine, bool local = false) + { + if (mOccupy) return false; + mLocalView = local; + mUserId = userId; + mStreamType = type; + int count = TXLiteAVVideoViewManager.GetInstance().Count; + if (engine != null) + { + if (count == 0) + { + engine.setLocalVideoRenderCallback(TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32, + TRTCVideoBufferType.TRTCVideoBufferType_Buffer, TXLiteAVVideoViewManager.GetInstance()); + } + if (!mLocalView) + { + engine.setRemoteVideoRenderCallback(userId, TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32, + TRTCVideoBufferType.TRTCVideoBufferType_Buffer, TXLiteAVVideoViewManager.GetInstance()); + } + } + if (mLocalView) + TXLiteAVVideoViewManager.GetInstance().AddView("", type, this); + else + TXLiteAVVideoViewManager.GetInstance().AddView(userId, type, this); + lock (mArgbFrame) + ReleaseBuffer(mArgbFrame); + mOccupy = true; + this.InvalidateVisual(); + return true; + } + + /// + /// 移除 View 绑定参数 + /// + /// TRTCCloud 实例,用户注册视频数据回调。 + public void RemoveEngine(ITRTCCloud engine) + { + if (mLocalView) + TXLiteAVVideoViewManager.GetInstance().RemoveView("", mStreamType, this); + else + TXLiteAVVideoViewManager.GetInstance().RemoveView(mUserId, mStreamType, this); + int count = TXLiteAVVideoViewManager.GetInstance().Count; + if (engine != null) + { + if (count == 0) + { + engine.setLocalVideoRenderCallback(TRTCVideoPixelFormat.TRTCVideoPixelFormat_Unknown, + TRTCVideoBufferType.TRTCVideoBufferType_Unknown, null); + } + if (!mLocalView && !TXLiteAVVideoViewManager.GetInstance().HasUserId(mUserId)) + { + engine.setRemoteVideoRenderCallback(mUserId, TRTCVideoPixelFormat.TRTCVideoPixelFormat_Unknown, + TRTCVideoBufferType.TRTCVideoBufferType_Unknown, null); + } + } + lock (mArgbFrame) + ReleaseBuffer(mArgbFrame); + mUserId = ""; + mOccupy = false; + mLocalView = false; + mFirstFrame = false; + mRenderMode = TRTCVideoFillMode.TRTCVideoFillMode_Fit; + this.InvalidateVisual(); + } + + /// + /// 设置 View 的渲染模式 + /// + /// 渲染模式 + public void SetRenderMode(TRTCVideoFillMode mode) + { + mRenderMode = mode; + } + + /// + /// 判断 View 是否被占用 + /// + /// true:当前 View 已被占用,false:当前 View 未被占用 + public bool IsViewOccupy() + { + return mOccupy; + } + + /// + /// 暂停渲染,显示默认图片 + /// + /// 是否暂停 + public void SetPause(bool pause) + { + if (mPause != pause) + { + mPause = pause; + if (mPause) + { + this.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20)); + } + else + { + this.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x00, 0x00, 0x00)); + // 避免刷新最后一帧数据 + lock (mArgbFrame) + ReleaseBuffer(mArgbFrame); + } + this.InvalidateVisual(); + } + } + + /// + /// 清除所有映射信息 + /// + public static void RemoveAllRegEngine() + { + TXLiteAVVideoViewManager.GetInstance().RemoveAllView(); + } + + public bool AppendVideoFrame(byte[] data, int width, int height, TRTCVideoPixelFormat videoFormat, TRTCVideoRotation rotation) + { + if (!mFirstFrame) + mFirstFrame = true; + if (mPause) + return false; + if (data == null || data.Length <= 0) + return false; + // data 数据有误 + if (videoFormat == TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32 && width * height * 4 != data.Length) + return false; + // 暂时不支持其他 YUV 类型 + if (videoFormat == TRTCVideoPixelFormat.TRTCVideoPixelFormat_I420 && width * height * 3 / 2 != data.Length) + return false; + + // 目前只实现了 BGRA32 的数据类型,如需其他类型请重写,并设置回调的数据类型 + if (videoFormat == TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32) + { + lock (mArgbFrame) + { + if (mArgbFrame.data == null || mArgbFrame.width != width || mArgbFrame.height != height) + { + ReleaseBuffer(mArgbFrame); + mArgbFrame.width = width; + mArgbFrame.height = height; + mArgbFrame.data = new byte[data.Length]; + } + Buffer.BlockCopy(data, 0, mArgbFrame.data, 0, (int)data.Length); + mArgbFrame.newFrame = true; + mArgbFrame.rotation = rotation; + } + } + + // 回到主线程刷新当前画面 + this.Dispatcher.Invoke(new Action(() => + { + this.InvalidateVisual(); + })); + + return true; + } + + protected override void OnRender(DrawingContext dc) + { + bool needDrawFrame = true; + if (mPause) + needDrawFrame = false; + if (mArgbFrame.data == null) + needDrawFrame = false; + if (!needDrawFrame) + { + return; + } + + lock (mArgbFrame) + { + if (mArgbFrame.data == null) + return; + if (mRenderMode == TRTCVideoFillMode.TRTCVideoFillMode_Fill) + { + RenderFillMode(dc, mArgbFrame.data, mArgbFrame.width, mArgbFrame.height, (int)mArgbFrame.rotation * 90); + } + else if (mRenderMode == TRTCVideoFillMode.TRTCVideoFillMode_Fit) + { + RenderFitMode(dc, mArgbFrame.data, mArgbFrame.width, mArgbFrame.height, (int)mArgbFrame.rotation * 90); + } + } + } + + /// + /// 渲染回调int width, int height, int rotation,WriteableBitmap image + /// + public event Action OnRenderVideoFrameHandler; + + private void RenderFillMode(DrawingContext dc, byte[] data, int width, int height, int rotation) + { + int viewWidth = (int)this.ActualWidth, viewHeight = (int)this.ActualHeight; + PixelFormat pixelFormat = PixelFormats.Pbgra32; + int bytesPerPixel = (pixelFormat.BitsPerPixel + 7) / 8; + int stride = bytesPerPixel * width; + if (mWriteableBitmap == null || mWriteableBitmap.PixelWidth != width || mWriteableBitmap.PixelHeight != height) + { + mWriteableBitmap = new WriteableBitmap(width, height, 96, 96, pixelFormat, null); + mInt32Rect = new Int32Rect(0, 0, width, height); + } + mWriteableBitmap.Lock(); + Marshal.Copy(data, 0, mWriteableBitmap.BackBuffer, data.Length); + mWriteableBitmap.AddDirtyRect(mInt32Rect); + mWriteableBitmap.Unlock(); + + //var tt = ConvertWriteableBitmapToBitmapImage(mWriteableBitmap); + //OpenCvSharp.Mat result = OpenCvSharp.Extensions.BitmapConverter.ToMat(tt); + //tt.Dispose(); + //result.Create(mWriteableBitmap.PixelHeight, mWriteableBitmap.PixelWidth, OpenCvSharp.MatType.CV_8UC4, 4); + //mWriteableBitmap.CopyPixels(Int32Rect.Empty, result.Data, data.Length, 4); + //LiveClassroom.bbb.Enqueue(result); + //result.ImWrite($"d:\\{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.png"); + //var a = ConvertWriteableBitmapToBitmapImage(mWriteableBitmap); + //a.Save($"d:\\{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.png"); + OnRenderVideoFrameHandler?.Invoke(width, height, rotation, mWriteableBitmap); + + ImageBrush brush = new ImageBrush(mWriteableBitmap); + if (rotation > 0) + { + Matrix transform = Matrix.Identity; + double scale = (double)viewWidth / (double)viewHeight; + if (rotation == 90 || rotation == 270) + transform.ScaleAt(scale, scale, 0.5, 0.5); + transform.RotateAt(rotation, 0.5, 0.5); + brush.RelativeTransform = new MatrixTransform(transform); + } + brush.Stretch = Stretch.UniformToFill; + Rect rect = new Rect(0, 0, viewWidth, viewHeight); + dc.DrawRectangle(brush, mPen, rect); + } + + + + private void RenderFitMode(DrawingContext dc, byte[] data, int width, int height, int rotation) + { + int viewWidth = (int)this.ActualWidth, viewHeight = (int)this.ActualHeight; + PixelFormat pixelFormat = PixelFormats.Pbgra32; + int bytesPerPixel = (pixelFormat.BitsPerPixel + 7) / 8; + int stride = bytesPerPixel * width; + if (mWriteableBitmap == null || mWriteableBitmap.PixelWidth != width || mWriteableBitmap.PixelHeight != height) + { + mWriteableBitmap = new WriteableBitmap(width, height, 96, 96, pixelFormat, null); + //Mat result = new Mat(); + //result.Create(mWriteableBitmap.PixelHeight, mWriteableBitmap.PixelWidth, MatType.CV_8U, 4); + //mWriteableBitmap.CopyPixels(Int32Rect.Empty, result.Data, (int)result.Step() * result.Rows, (int)result.Step()); + mInt32Rect = new Int32Rect(0, 0, width, height); + } + mWriteableBitmap.Lock(); + Marshal.Copy(data, 0, mWriteableBitmap.BackBuffer, data.Length); + mWriteableBitmap.AddDirtyRect(mInt32Rect); + mWriteableBitmap.Unlock(); + + OnRenderVideoFrameHandler?.Invoke(width, height, rotation, mWriteableBitmap); + + ImageBrush brush = new ImageBrush(mWriteableBitmap); + if (rotation > 0) + { + Matrix transform = Matrix.Identity; + double scale = (double)viewHeight / (double)viewWidth; + if (rotation == 90 || rotation == 270) + transform.ScaleAt(1, scale, 0.5, 0.5); + transform.RotateAt(rotation, 0.5, 0.5); + brush.RelativeTransform = new MatrixTransform(transform); + } + brush.Stretch = Stretch.Uniform; + Rect rect = new Rect(0, 0, viewWidth, viewHeight); + dc.DrawRectangle(brush, mPen, rect); + } + + private void ReleaseBuffer(FrameBufferInfo info) + { + if (info.data != null) + info.data = null; + info.width = 0; + info.height = 0; + info.newFrame = false; + info.rotation = TRTCVideoRotation.TRTCVideoRotation0; + } + + #region Dispose + private bool disposed = false; + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) return; + if (disposing) + { + ReleaseBuffer(mArgbFrame); + mWriteableBitmap = null; + } + disposed = true; + } + + ~TXLiteAVVideoView() + { + this.Dispose(false); + } + #endregion + } + + public class TXLiteAVVideoViewManager : ITRTCVideoRenderCallback + { + private volatile Dictionary mMapViews; + + public static TXLiteAVVideoViewManager sInstance; + + private static Object mLocker = new Object(); + + public static TXLiteAVVideoViewManager GetInstance() + { + if (sInstance == null) + { + lock (mLocker) + { + if (sInstance == null) + sInstance = new TXLiteAVVideoViewManager(); + } + } + return sInstance; + } + + private TXLiteAVVideoViewManager() + { + mMapViews = new Dictionary(); + } + + private string GetKey(string userId, TRTCVideoStreamType type) + { + return String.Format("{0}_{1}", userId, type); + } + + // 主要用于判断当前 user 是否还有存在流画面,存在则不移除监听。 + public bool HasUserId(string userId) + { + bool exit = false; + lock (mMapViews) + { + exit = mMapViews.ContainsKey(GetKey(userId, TRTCVideoStreamType.TRTCVideoStreamTypeBig)) || + mMapViews.ContainsKey(GetKey(userId, TRTCVideoStreamType.TRTCVideoStreamTypeSub)); + } + return exit; + } + + public void AddView(string userId, TRTCVideoStreamType type, TXLiteAVVideoView view) + { + lock (mMapViews) + { + bool find = false; + foreach (var item in mMapViews) + { + if (item.Key.Equals(GetKey(userId, type))) + { + find = true; + break; + } + } + if (!find) + { + mMapViews.Add(GetKey(userId, type), view); + } + } + } + + public void RemoveView(string userId, TRTCVideoStreamType type, TXLiteAVVideoView view) + { + lock (mMapViews) + { + foreach (var item in mMapViews.ToList()) + { + if (item.Key.Equals(GetKey(userId, type))) + { + if (item.Value != null) + { + item.Value.Dispose(); + } + mMapViews.Remove(item.Key); + break; + } + } + } + } + + public void RemoveAllView() + { + lock (mMapViews) + mMapViews.Clear(); + } + + public int Count + { + get + { + lock (mMapViews) + return mMapViews.Count; + } + } + + //public event Action OnRenderVideoFrameHandler; + /// + /// 自定义渲染回调,只能存在一个回调 + /// + /// + /// + /// + public void onRenderVideoFrame(string userId, TRTCVideoStreamType streamType, TRTCVideoFrame frame) + { + //OnRenderVideoFrameHandler?.Invoke(userId, streamType, frame); + // 大小视频是占一个视频位,底层支持动态切换。 + if (streamType == TRTCVideoStreamType.TRTCVideoStreamTypeSmall) + streamType = TRTCVideoStreamType.TRTCVideoStreamTypeBig; + TXLiteAVVideoView view = null; + lock (mMapViews) + { + foreach (var item in mMapViews) + { + if (item.Key.Equals(GetKey(userId, streamType)) && item.Value != null) + { + view = item.Value; + break; + } + } + } + if (view != null) + view.AppendVideoFrame(frame.data, (int)frame.width, (int)frame.height, frame.videoFormat, frame.rotation); + } + } + + class FrameBufferInfo + { + public byte[] data { get; set; } + + public int width { get; set; } + + public int height { get; set; } + + public bool newFrame { get; set; } + + public TRTCVideoRotation rotation { get; set; } + + public FrameBufferInfo() + { + rotation = TRTCVideoRotation.TRTCVideoRotation0; + newFrame = false; + width = 0; + height = 0; + data = null; + } + } +} diff --git a/JianGongYun/TRTC/LiveClassroom.cs b/JianGongYun/TRTC/LiveClassroom.cs index 42a415a..c8a129d 100644 --- a/JianGongYun/TRTC/LiveClassroom.cs +++ b/JianGongYun/TRTC/LiveClassroom.cs @@ -1,11 +1,19 @@ -using JianGongYun.TRTC.Models; +using JianGongYun.TRTC.Components; +using JianGongYun.TRTC.Models; using JianGongYun.TRTC.Utils; +using JianGongYun.TRTC.ViewModels; using JianGongYun.TRTC.Windows; using ManageLiteAV; +using OpenCvSharp; using System; using System.Collections.Generic; +using System.IO; using System.Text; +using System.Threading; +using System.Threading.Tasks; using System.Windows; +using System.Windows.Controls; +using Window = System.Windows.Window; namespace JianGongYun.TRTC { @@ -52,7 +60,6 @@ namespace JianGongYun.TRTC CurrentLiveWindow = new LiveWindow(); CurrentLiveWindow.Closed += CurrentLiveWindow_Closed; - lTRTCCloud = ITRTCCloud.getTRTCShareInstance();//创建TRTC实例 var roomPars = new TRTCParams { @@ -66,20 +73,102 @@ namespace JianGongYun.TRTC lTRTCCloud.enterRoom(ref roomPars, TRTCAppScene.TRTCAppSceneLIVE);//创建房间 //设备 - var settingViewMode = ViewModels.SettingWindowViewModel.GetInstance(); + //var settingViewMode = ViewModels.SettingWindowViewModel.GetInstance(); lTXDeviceManager = lTRTCCloud.getDeviceManager(); - var currentMic = settingViewMode.CurrentMic;//麦克风 + var currentMic = settingWindowViewModel.CurrentMic;//麦克风 if (!string.IsNullOrEmpty(currentMic)) { var res = lTXDeviceManager.setCurrentDevice(TRTCDeviceType.TXMediaDeviceTypeMic, currentMic); Console.WriteLine($"设置麦克风:{res}"); } + lTXDeviceManager.setCurrentDeviceVolume(TRTCDeviceType.TXMediaDeviceTypeMic, settingWindowViewModel.MicVolume);//麦克风采集音量 + lTRTCCloud.setSystemAudioLoopbackVolume(settingWindowViewModel.SytemGatherVolume);//系统声音采集音量 + if (settingWindowViewModel.AudioSource == "2") + { + lTRTCCloud.startSystemAudioLoopback(""); + } + else + { + lTRTCCloud.stopSystemAudioLoopback(); + } + var encParams = settingWindowViewModel.EncParams; + var qosParams = settingWindowViewModel.QosParams; + var renderParams = settingWindowViewModel.RenderParams; + lTRTCCloud.setVideoEncoderParam(ref encParams); + lTRTCCloud.setNetworkQosParam(ref qosParams); + lTRTCCloud.setLocalRenderParams(ref renderParams); //设备完结 - callerWindow.Hide();//隐藏调用窗口 CurrentLiveWindow.Show(); } + + + private static SettingWindowViewModel settingWindowViewModel = SettingWindowViewModel.GetInstance(); + + + /// + /// 视频画面 + /// + private static Dictionary VideoViews = new Dictionary(); + /// + /// 启动摄像头 + /// + /// 视频容器 + public static void StartVideoMain(Panel parent) + { + lTRTCCloud.startLocalPreview(IntPtr.Zero); + var liveWinMode = CurrentLiveWindow.DataContext as LiveWindowViewModel; + liveWinMode.CameraRunning = true; + AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeBig, true); + } + /// + /// 添加自定义渲染 View 并绑定渲染回调 + /// + private static void AddCustomVideoView(Panel parent, string userId, TRTCVideoStreamType streamType, bool local = false) + { + TXLiteAVVideoView videoView = new TXLiteAVVideoView(); + videoView.RegEngine(userId, streamType, lTRTCCloud, local); + videoView.SetRenderMode(settingWindowViewModel.RenderParams.fillMode); + videoView.Width = parent.ActualWidth; + videoView.Height = parent.ActualHeight; + parent.Children.Add(videoView); + string key = string.Format("{0}_{1}", userId, streamType); + VideoViews.Add(key, videoView); + } + + /// + /// 移除自定义渲染 View 并解绑渲染回调 + /// + private static void RemoveCustomVideoView(Panel parent, string userId, TRTCVideoStreamType streamType, bool local = false) + { + TXLiteAVVideoView videoView = null; + string key = string.Format("{0}_{1}", userId, streamType); + if (VideoViews.TryGetValue(key, out videoView)) + { + videoView.RemoveEngine(lTRTCCloud); + parent.Children.Remove(videoView); + VideoViews.Remove(key); + } + } + + /// + /// 重启音频录制 + /// + public static void RestartAudio() { } + /// + /// 重设主视频(摄像头) + /// + public static void ResetVideoMain() { } + /// + /// 重设主视频(摄像头)网络 + /// + public static void ResetVideoMainQos() { } + /// + /// 重设副视频(录屏) + /// + public static void ResetVideoSub() { } + private static void CurrentLiveWindow_Closed(object sender, EventArgs e) { diff --git a/JianGongYun/TRTC/Models/LiveTypeEnum.cs b/JianGongYun/TRTC/Models/LiveTypeEnum.cs new file mode 100644 index 0000000..98495c4 --- /dev/null +++ b/JianGongYun/TRTC/Models/LiveTypeEnum.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JianGongYun.TRTC.Models +{ + public enum LiveTypeEnum + { + CameraAndScreen = 0, + OnlyScreen, + OnlyCamera, + OnlyAudio, + } +} diff --git a/JianGongYun/TRTC/Utils/LiveTypeToBoolConverter.cs b/JianGongYun/TRTC/Utils/LiveTypeToBoolConverter.cs new file mode 100644 index 0000000..1d13bee --- /dev/null +++ b/JianGongYun/TRTC/Utils/LiveTypeToBoolConverter.cs @@ -0,0 +1,27 @@ +using JianGongYun.TRTC.Models; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Data; + +namespace JianGongYun.TRTC +{ + public class LiveTypeToBoolConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value.ToString() == parameter.ToString(); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + bool isChecked = (bool)value; + if (!isChecked) + { + return null; + } + return (LiveTypeEnum)Enum.Parse(typeof(LiveTypeEnum), parameter.ToString()); + } + } +} diff --git a/JianGongYun/TRTC/Utils/UnBooleanToVisibilityConverter.cs b/JianGongYun/TRTC/Utils/UnBooleanToVisibilityConverter.cs new file mode 100644 index 0000000..4601f2b --- /dev/null +++ b/JianGongYun/TRTC/Utils/UnBooleanToVisibilityConverter.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows; +using System.Windows.Data; + +namespace JianGongYun.TRTC +{ + /// + /// 把bool转换为相反的控件显示状态(内置的BooleanToVisibilityConverter的true为显示false为隐藏) + /// + public class UnBooleanToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + bool flag = false; + if (value is bool) + { + flag = (bool)value; + } + else if (value is bool?) + { + bool? nullable = (bool?)value; + flag = nullable.HasValue ? nullable.Value : false; + } + return (flag ? Visibility.Hidden : Visibility.Visible); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return null; + } + } +} diff --git a/JianGongYun/TRTC/Utils/WriteableBitmapToMat.cs b/JianGongYun/TRTC/Utils/WriteableBitmapToMat.cs new file mode 100644 index 0000000..6e8e8a4 --- /dev/null +++ b/JianGongYun/TRTC/Utils/WriteableBitmapToMat.cs @@ -0,0 +1,32 @@ +using OpenCvSharp; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Windows.Media.Imaging; + +namespace JianGongYun.TRTC.Utils +{ + public static class WriteableBitmapToMat + { + /// + /// WriteableBitmap转Mat + /// + /// + /// + public static Mat ToMat(this WriteableBitmap writeableBitmap) + { + using (MemoryStream outStream = new MemoryStream()) + { + BitmapEncoder enc = new JpegBitmapEncoder(); + enc.Frames.Add(BitmapFrame.Create(writeableBitmap)); + enc.Save(outStream); + using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(outStream)) + { + var mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(bmp); + return mat; + } + } + } + } +} diff --git a/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs b/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs index 57efc01..c39992c 100644 --- a/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs +++ b/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs @@ -1,8 +1,11 @@ -using System; +using JianGongYun.TRTC.Models; +using ManageLiteAV; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Text; +using System.Windows.Threading; namespace JianGongYun.TRTC.ViewModels { @@ -44,6 +47,15 @@ namespace JianGongYun.TRTC.ViewModels set { _IsLive = value; + if (value)//开始直播,清空时间并开始计时 + { + _LiveTimeCount = new DateTime(1970, 1, 1, 0, 0, 0); + timer.Start(); + } + else + { + timer.Stop(); + } if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("IsLive")); @@ -55,5 +67,147 @@ namespace JianGongYun.TRTC.ViewModels } } + public LiveWindowViewModel() + { + timer = new DispatcherTimer(); + timer.Interval = TimeSpan.FromSeconds(1); + timer.Tick += new EventHandler((a, b) => LiveTimeCount = ""); + } + /// + /// 直播计时器 + /// + private DispatcherTimer timer = null; + private DateTime _LiveTimeCount; + public string LiveTimeCount + { + get + { + return _LiveTimeCount.ToString("HH:mm:ss"); + } + set + { + _LiveTimeCount = _LiveTimeCount.AddSeconds(1); + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("LiveTimeCount")); + } + } + } + + /// + /// 直播类型 + /// + private LiveTypeEnum _LiveType = LiveTypeEnum.OnlyAudio; + public LiveTypeEnum LiveType + { + set + { + _LiveType = value; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("LiveType")); + } + } + get + { + return _LiveType; + } + } + + /// + /// 摄像头采集中 + /// + private bool _CameraRunning = false; + public bool CameraRunning + { + set + { + _CameraRunning = value; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("CameraRunning")); + } + } + get + { + return _CameraRunning; + } + } + + /// + /// 屏幕分享中 + /// + private bool _ScreenRunning = false; + public bool ScreenRunning + { + set + { + _ScreenRunning = value; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("ScreenRunning")); + } + } + get + { + return _ScreenRunning; + } + } + + /// + /// 麦克风使用中 + /// + private bool _MicRunning = false; + public bool MicRunning + { + set + { + _MicRunning = value; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("MicRunning")); + } + } + get + { + return _MicRunning; + } + } + + private SIZE thumbSize = new SIZE { cx = 300, cy = 200 }; + private SIZE iconSize = new SIZE { cx = 50, cy = 50 }; + private ObservableCollection _LiveScreens = new ObservableCollection(); + /// + /// 可分享桌面 + /// + public ObservableCollection LiveScreens + { + get + { + _LiveScreens.Clear(); + var temp = LiveClassroom.lTRTCCloud.getScreenCaptureSources(ref thumbSize, ref iconSize); + var count = temp.getCount(); + for (uint i = 0; i < count; i++) + { + _LiveScreens.Add(temp.getSourceInfo(i)); + } + temp.release(); + return _LiveScreens; + } + } + + private IntPtr _CurrentShareScreen; + public IntPtr CurrentShareScreen + { + get { return _CurrentShareScreen; } + set + { + _CurrentShareScreen = value; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("CurrentShareScreen")); + } + } + } } } diff --git a/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs b/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs index ecc163b..e5a9e07 100644 --- a/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs +++ b/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Windows.Controls; using JianGongYun.TRTC.Components; using System.Windows; +using ManageLiteAV; namespace JianGongYun.TRTC.ViewModels { @@ -40,7 +41,7 @@ namespace JianGongYun.TRTC.ViewModels } } - #region 麦克风 + #region 音频属性 /// /// 集合,实时获取 /// @@ -140,10 +141,6 @@ namespace JianGongYun.TRTC.ViewModels } - #endregion - - #region 音频来源 - /// /// 音频来源 /// @@ -169,7 +166,7 @@ namespace JianGongYun.TRTC.ViewModels storage.SetValue(INI_ROOT_KEY, INI_KEY_AUDIO_SOURCE, value);//生成保存本地 if (_AudioSource == "1")//只采集麦克风,关闭系统音采集 { - ShowSytemGatherSlider = Visibility.Hidden; + ShowSytemGatherSlider = Visibility.Collapsed; LiveClassroom.lTRTCCloud.stopSystemAudioLoopback(); } else if (_AudioSource == "2")//麦和系统音,开启采集系统音 @@ -187,18 +184,18 @@ namespace JianGongYun.TRTC.ViewModels /// /// 是否显示系统采集滑动条 /// - private Visibility _ShowSytemGatherSlider = Visibility.Hidden; + private Visibility _ShowSytemGatherSlider = Visibility.Collapsed; public Visibility ShowSytemGatherSlider { get { - if (AudioSource=="2") + if (AudioSource == "2") { _ShowSytemGatherSlider = Visibility.Visible; } else { - _ShowSytemGatherSlider = Visibility.Hidden; + _ShowSytemGatherSlider = Visibility.Collapsed; } return _ShowSytemGatherSlider; } @@ -246,6 +243,397 @@ namespace JianGongYun.TRTC.ViewModels #endregion + #region 录屏属性 + + /// + /// 视频帧率(fps) + /// + private string _ScreenRecordingFps = "60"; + public string ScreenRecordingFps + { + get + { + var screenRecordingFps = storage.GetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_FPS);//本地缓存 + if (string.IsNullOrEmpty(screenRecordingFps)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_FPS, _ScreenRecordingFps);//生成初始数据 + } + else + { + _ScreenRecordingFps = screenRecordingFps; + } + return _ScreenRecordingFps; + } + set + { + _ScreenRecordingFps = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_FPS, value);//生成保存本地 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("ScreenRecordingFps")); + } + } + } + + /// + /// 音频码率 + /// + private string _ScreenRecordingAudioBitrate = "320000"; + public string ScreenRecordingAudioBitrate + { + get + { + var screenRecordingAudioBitrate = storage.GetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_AUDIO_BITRATE);//本地缓存 + if (string.IsNullOrEmpty(screenRecordingAudioBitrate)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_AUDIO_BITRATE, _ScreenRecordingAudioBitrate);//生成初始数据 + } + else + { + _ScreenRecordingAudioBitrate = screenRecordingAudioBitrate; + } + return _ScreenRecordingAudioBitrate; + } + set + { + _ScreenRecordingAudioBitrate = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_AUDIO_BITRATE, value);//生成保存本地 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("ScreenRecordingAudioBitrate")); + } + } + } + + /// + /// 音频采样率 + /// + private string _ScreenRecordingAudioFreq = "48000"; + public string ScreenRecordingAudioFreq + { + get + { + var screenRecordingAudioFreq = storage.GetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_AUDIO_FREQ);//本地缓存 + if (string.IsNullOrEmpty(screenRecordingAudioFreq)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_AUDIO_FREQ, _ScreenRecordingAudioFreq);//生成初始数据 + } + else + { + _ScreenRecordingAudioFreq = screenRecordingAudioFreq; + } + return _ScreenRecordingAudioFreq; + } + set + { + _ScreenRecordingAudioFreq = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_AUDIO_FREQ, value);//生成保存本地 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("ScreenRecordingAudioFreq")); + } + } + } + + /// + /// 编码级别 + /// + private string _ScreenRecordingProfileLevel = "-profile:v high -level 5.1"; + public string ScreenRecordingProfileLevel + { + get + { + var screenRecordingProfileLevel = storage.GetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_PROFILE_LEVEL);//本地缓存 + if (string.IsNullOrEmpty(screenRecordingProfileLevel)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_PROFILE_LEVEL, _ScreenRecordingProfileLevel);//生成初始数据 + } + else + { + _ScreenRecordingProfileLevel = screenRecordingProfileLevel; + } + return _ScreenRecordingProfileLevel; + } + set + { + _ScreenRecordingProfileLevel = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_PROFILE_LEVEL, value);//生成保存本地 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("ScreenRecordingProfileLevel")); + } + } + } + + /// + /// 录制倒计时 + /// + private int _ScreenRecordingCountdown = 3; + public int ScreenRecordingCountdown + { + get + { + var screenRecordingCountdown = storage.GetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_COUNTDOWN);//本地缓存 + if (string.IsNullOrEmpty(screenRecordingCountdown)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_COUNTDOWN, _ScreenRecordingCountdown.ToString());//生成初始数据 + } + else + { + _ScreenRecordingCountdown = int.Parse(screenRecordingCountdown); + } + return _ScreenRecordingCountdown; + } + set + { + _ScreenRecordingCountdown = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_COUNTDOWN, value.ToString());//生成保存本地 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("ScreenRecordingCountdown")); + } + } + } + + /// + /// 视频保存路径 + /// + private string _ScreenRecordingDir; + public string ScreenRecordingDir + { + get + { + var screenRecordingDir = storage.GetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_DIR);//本地缓存 + if (string.IsNullOrEmpty(screenRecordingDir)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_DIR, _ScreenRecordingDir);//生成初始数据 + } + else + { + _ScreenRecordingDir = screenRecordingDir; + } + return _ScreenRecordingDir; + } + set + { + _ScreenRecordingDir = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_SCREEN_RECORDING_DIR, value.ToString());//生成保存本地 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("ScreenRecordingDir")); + } + } + } + + #endregion + + #region 直播设置属性 + + /// + /// 音频质量 + /// + private TRTCAudioQuality _LiveAudioLevel = TRTCAudioQuality.TRTCAudioQualityDefault; + public TRTCAudioQuality LiveAudioLevel + { + get + { + var liveAudioLevel = storage.GetValue(INI_ROOT_KEY, INI_KEY_LIVE_AUDIO_LEVEL);//本地缓存 + if (string.IsNullOrEmpty(liveAudioLevel)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_AUDIO_LEVEL, _LiveAudioLevel.ToString());//生成初始数据 + } + else + { + _LiveAudioLevel = (TRTCAudioQuality)Enum.Parse(typeof(TRTCAudioQuality), liveAudioLevel); + } + return _LiveAudioLevel; + } + set + { + _LiveAudioLevel = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_AUDIO_LEVEL, value.ToString());//生成保存本地 + LiveClassroom.RestartAudio();//重启音频 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("LiveAudioLevel")); + } + } + } + /// + /// 摄像画面帧率 + /// + private uint _LiveMainFps = 24; + public uint LiveMainFps + { + get + { + var liveMainFps = storage.GetValue(INI_ROOT_KEY, INI_KEY_LIVE_MAIN_FPS);//本地缓存 + if (string.IsNullOrEmpty(liveMainFps)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_MAIN_FPS, _LiveMainFps.ToString());//生成初始数据 + } + else + { + _LiveMainFps = uint.Parse(liveMainFps); + } + return _LiveMainFps; + } + set + { + _LiveMainFps = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_MAIN_FPS, value.ToString());//生成保存本地 + LiveClassroom.ResetVideoMain();//重设参数 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("LiveMainFps")); + } + } + } + /// + /// 摄像画面码率 + /// + private uint _LiveMainBitrate = 1600; + public uint LiveMainBitrate + { + get + { + var liveMainBitrate = storage.GetValue(INI_ROOT_KEY, INI_KEY_LIVE_MAIN_BITTRATE);//本地缓存 + if (string.IsNullOrEmpty(liveMainBitrate)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_MAIN_BITTRATE, _LiveMainBitrate.ToString());//生成初始数据 + } + else + { + _LiveMainBitrate = uint.Parse(liveMainBitrate); + } + return _LiveMainBitrate; + } + set + { + _LiveMainBitrate = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_MAIN_BITTRATE, value.ToString());//生成保存本地 + LiveClassroom.ResetVideoMain();//重设参数 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("LiveMainBitrate")); + } + } + } + /// + /// 摄像画面级别 + /// + private TRTCVideoQosPreference _LiveMainLevel = TRTCVideoQosPreference.TRTCVideoQosPreferenceClear; + public TRTCVideoQosPreference LiveMainLevel + { + get + { + var liveMainLevel = storage.GetValue(INI_ROOT_KEY, INI_KEY_LIVE_MAIN_LEVEL);//本地缓存 + if (string.IsNullOrEmpty(liveMainLevel)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_MAIN_LEVEL, _LiveMainLevel.ToString());//生成初始数据 + } + else + { + _LiveMainLevel = (TRTCVideoQosPreference)Enum.Parse(typeof(TRTCVideoQosPreference), liveMainLevel); + } + return _LiveMainLevel; + } + set + { + _LiveMainLevel = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_MAIN_LEVEL, value.ToString());//生成保存本地 + LiveClassroom.ResetVideoMainQos();//重设参数 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("LiveMainLevel")); + } + } + } + /// + /// 屏幕分享帧率 + /// + private uint _LiveSubFps = 24; + public uint LiveSubFps + { + get + { + var liveSubFps = storage.GetValue(INI_ROOT_KEY, INI_KEY_LIVE_SUB_FPS);//本地缓存 + if (string.IsNullOrEmpty(liveSubFps)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_SUB_FPS, _LiveSubFps.ToString());//生成初始数据 + } + else + { + _LiveSubFps = uint.Parse(liveSubFps); + } + return _LiveSubFps; + } + set + { + _LiveSubFps = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_SUB_FPS, value.ToString());//生成保存本地 + LiveClassroom.ResetVideoSub();//重新设置副画面参数 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("LiveSubFps")); + } + } + } + /// + /// 屏幕分享码率 + /// + private uint _LiveSubBitrate = 2000; + public uint LiveSubBitrate + { + get + { + var liveSubBitrate = storage.GetValue(INI_ROOT_KEY, INI_KEY_LIVE_SUB_BITTRATE);//本地缓存 + if (string.IsNullOrEmpty(liveSubBitrate)) + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_SUB_BITTRATE, _LiveSubBitrate.ToString());//生成初始数据 + } + else + { + _LiveSubBitrate = uint.Parse(liveSubBitrate); + } + return _LiveSubBitrate; + } + set + { + _LiveSubBitrate = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_LIVE_SUB_BITTRATE, value.ToString());//生成保存本地 + LiveClassroom.ResetVideoSub();//重启音频 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("LiveSubBitrate")); + } + } + } + #endregion + + #region 参数 + public TRTCVideoEncParam EncParams + { + get + { + return new TRTCVideoEncParam { enableAdjustRes = true, resMode = TRTCVideoResolutionMode.TRTCVideoResolutionModeLandscape, videoBitrate = LiveMainBitrate, videoFps = LiveMainFps }; + } + } + public TRTCNetworkQosParam QosParams + { + get + { + return new TRTCNetworkQosParam { preference = LiveMainLevel }; + } + } + public TRTCRenderParams RenderParams + { + get + { + return new TRTCRenderParams { fillMode = TRTCVideoFillMode.TRTCVideoFillMode_Fill, mirrorType = TRTCVideoMirrorType.TRTCVideoMirrorType_Enable, rotation = TRTCVideoRotation.TRTCVideoRotation0 }; + } + } + #endregion /// /// ini文件操作 @@ -254,6 +642,7 @@ namespace JianGongYun.TRTC.ViewModels private SettingWindowViewModel() { storage = new IniStorage(sPath); + _ScreenRecordingDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonVideos); } #region 配置持久化 @@ -261,44 +650,29 @@ namespace JianGongYun.TRTC.ViewModels //配置文件路径 private string sPath = ".\\TRTCConfig.ini"; //根节点 - public const string INI_ROOT_KEY = "TRTCLOCALCONFIG"; - // 设备 - public const string INI_KEY_CHOOSE_CAMERA = "INI_KEY_CHOOSE_CAMERA";//当前摄像头 - public const string INI_KEY_CHOOSE_MIC = "INI_KEY_CHOOSE_MIC";//当前麦克风 - // 音频 - public const string INI_KEY_AUDIO_MIC_VOLUME = "INI_KEY_AUDIO_MIC_VOLUME";//麦克风音量 - public const string INI_KEY_AUDIO_SPEAKER_VOLUME = "INI_KEY_AUDIO_SOURCE";//播放音量 - public const string INI_KEY_AUDIO_SOURCE = "INI_KEY_AUDIO_SOURCE";//音频来源 - public const string INI_KEY_AUDIO_SYSTEM_GATHER_VOLUME = "INI_KEY_AUDIO_SYSTEM_GATHER_VOLUME";//麦克风音量 - // 视频 - public const string INI_KEY_VIDEO_FPS = "INI_KEY_VIDEO_FPS";//视频FPS - public const string INI_KEY_VIDEO_BITRATE = "INI_KEY_VIDEO_BITRATE"; - public const string INI_KEY_VIDEO_RESOLUTION = "INI_KEY_VIDEO_RESOLUTION"; - public const string INI_KEY_VIDEO_RES_MODE = "INI_KEY_VIDEO_RES_MODE"; - public const string INI_KEY_VIDEO_QUALITY = "INI_KEY_VIDEO_QUALITY"; - public const string INI_KEY_VIDEO_QUALITY_CONTROL = "INI_KEY_VIDEO_QUALITY_CONTROL"; - public const string INI_KEY_VIDEO_APP_SCENE = "INI_KEY_VIDEO_APP_SCENE"; - public const string INI_KEY_VIDEO_FILL_MODE = "INI_KEY_VIDEO_FILL_MODE"; - public const string INI_KEY_VIDEO_ROTATION = "INI_KEY_VIDEO_ROTATION"; - // 美颜 - public const string INI_KEY_BEAUTY_OPEN = "INI_KEY_BEAUTY_OPEN"; - public const string INI_KEY_BEAUTY_STYLE = "INI_KEY_BEAUTY_STYLE"; - public const string INI_KEY_BEAUTY_VALUE = "INI_KEY_BEAUTY_VALUE"; - public const string INI_KEY_WHITE_VALUE = "INI_KEY_WHITE_VALUE"; - public const string INI_KEY_RUDDINESS_VALUE = "INI_KEY_RUDDINESS_VALUE"; - // 大小流 - public const string INI_KEY_SET_PUSH_SMALLVIDEO = "INI_KEY_SET_PUSH_SMALLVIDEO"; - public const string INI_KEY_SET_PLAY_SMALLVIDEO = "INI_KEY_SET_PLAY_SMALLVIDEO"; - // 测试 - public const string INI_KEY_SET_NETENV_STYLE = "INI_KEY_SET_NETENV_STYLE"; - public const string INI_KEY_ROOMCALL_STYLE = "INI_KEY_ROOMCALL_STYLE"; - // 镜像 - public const string INI_KEY_LOCAL_VIDEO_MIRROR = "INI_KEY_LOCAL_VIDEO_MIRROR"; - public const string INI_KEY_REMOTE_VIDEO_MIRROR = "INI_KEY_REMOTE_VIDEO_MIRROR"; - // 音量提示 - public const string INI_KEY_SHOW_AUDIO_VOLUME = "INI_KEY_SHOW_AUDIO_VOLUME"; - // 混流 - public const string INI_KEY_CLOUD_MIX_TRANSCODING = "INI_KEY_CLOUD_MIX_TRANSCODING"; + private const string INI_ROOT_KEY = "TRTCLOCALCONFIG"; + //音频 + private const string INI_KEY_CHOOSE_MIC = "INI_KEY_CHOOSE_MIC";//当前麦克风 + private const string INI_KEY_AUDIO_MIC_VOLUME = "INI_KEY_AUDIO_MIC_VOLUME";//麦克风音量 + private const string INI_KEY_AUDIO_SPEAKER_VOLUME = "INI_KEY_AUDIO_SOURCE";//播放音量 + private const string INI_KEY_AUDIO_SOURCE = "INI_KEY_AUDIO_SOURCE";//音频来源 + private const string INI_KEY_AUDIO_SYSTEM_GATHER_VOLUME = "INI_KEY_AUDIO_SYSTEM_GATHER_VOLUME";//麦克风音量 + //录屏 + private const string INI_KEY_SCREEN_RECORDING_FPS = "INI_KEY_SCREEN_RECORDING_FPS";//视频帧率(fps) + private const string INI_KEY_SCREEN_RECORDING_AUDIO_BITRATE = "INI_KEY_SCREEN_RECORDING_AUDIO_BITRATE";//音频码率 + private const string INI_KEY_SCREEN_RECORDING_AUDIO_FREQ = "INI_KEY_SCREEN_RECORDING_AUDIO_FREQ";//音频采样率 + private const string INI_KEY_SCREEN_RECORDING_PROFILE_LEVEL = "INI_KEY_SCREEN_RECORDING_PROFILE_LEVEL";//编码级别 + private const string INI_KEY_SCREEN_RECORDING_COUNTDOWN = "INI_KEY_SCREEN_RECORDING_COUNTDOWN";//录制倒计时 + private const string INI_KEY_SCREEN_RECORDING_DIR = "INI_KEY_SCREEN_RECORDING_DIR";//录制保存路径 + //直播 + private const string INI_KEY_LIVE_AUDIO_LEVEL = "INI_KEY_LIVE_AUDIO_LEVEL";//直播音频等级 + private const string INI_KEY_LIVE_MAIN_FPS = "INI_KEY_LIVE_MAIN_FPS";//直播主帧率 + private const string INI_KEY_LIVE_MAIN_BITTRATE = "INI_KEY_LIVE_MAIN_BITTRATE";//直播主码率 + private const string INI_KEY_LIVE_MAIN_LEVEL = "INI_KEY_LIVE_MAIN_LEVEL";//主画面级别 + private const string INI_KEY_LIVE_SUB_FPS = "INI_KEY_LIVE_SUB_FPS";//屏幕分享帧率 + private const string INI_KEY_LIVE_SUB_BITTRATE = "INI_KEY_LIVE_SUB_BITTRATE";//屏幕分享码率 + + #endregion } } diff --git a/JianGongYun/TRTC/Windows/LiveWindow.xaml b/JianGongYun/TRTC/Windows/LiveWindow.xaml index e5a6c52..e9e053f 100644 --- a/JianGongYun/TRTC/Windows/LiveWindow.xaml +++ b/JianGongYun/TRTC/Windows/LiveWindow.xaml @@ -14,7 +14,14 @@ Background="#232428"> - M512 884.363636c-50.059636 0-98.722909-9.890909-144.64-29.393454a372.922182 372.922182 0 0 1-118.365091-79.965091 372.922182 372.922182 0 0 1-79.965091-118.365091A367.872 367.872 0 0 1 139.636364 512c0-50.036364 9.890909-98.722909 29.393454-144.64a372.945455 372.945455 0 0 1 79.965091-118.365091 372.922182 372.922182 0 0 1 118.365091-79.965091A367.872 367.872 0 0 1 512 139.636364c50.059636 0 98.722909 9.890909 144.64 29.393454a372.945455 372.945455 0 0 1 118.365091 79.965091 372.922182 372.922182 0 0 1 79.965091 118.365091A367.872 367.872 0 0 1 884.363636 512c0 50.059636-9.890909 98.722909-29.393454 144.64a372.945455 372.945455 0 0 1-79.965091 118.365091 372.922182 372.922182 0 0 1-118.365091 79.965091A367.848727 367.848727 0 0 1 512 884.363636z m0-698.181818c-43.776 0-86.341818 8.634182-126.464 25.693091a326.516364 326.516364 0 0 0-103.633455 70.050909 326.516364 326.516364 0 0 0-70.027636 103.610182A321.629091 321.629091 0 0 0 186.181818 512c0 43.776 8.634182 86.341818 25.693091 126.464a326.516364 326.516364 0 0 0 70.050909 103.633455 326.516364 326.516364 0 0 0 103.610182 70.027636A321.652364 321.652364 0 0 0 512 837.818182c43.776 0 86.341818-8.657455 126.464-25.693091a326.516364 326.516364 0 0 0 103.633455-70.050909 326.516364 326.516364 0 0 0 70.027636-103.610182A321.652364 321.652364 0 0 0 837.818182 512c0-43.776-8.657455-86.341818-25.693091-126.464a326.516364 326.516364 0 0 0-70.050909-103.633455 326.516364 326.516364 0 0 0-103.610182-70.027636A321.652364 321.652364 0 0 0 512 186.181818z m-170.077091 512.162909a18.013091 18.013091 0 0 1-17.408-22.667636l70.865455-265.774546a18.013091 18.013091 0 0 1 12.753454-12.753454l265.774546-70.865455a18.013091 18.013091 0 0 1 22.039272 22.039273l-70.865454 265.774546a18.013091 18.013091 0 0 1-12.776727 12.753454L346.554182 697.716364a18.013091 18.013091 0 0 1-4.654546 0.628363z m85.573818-269.079272l-60.16 225.62909 225.605818-60.16 60.16-225.62909-225.605818 60.16z m111.755637 126.138181a17.966545 17.966545 0 0 1-12.730182-5.282909l-46.801455-46.778182a18.013091 18.013091 0 1 1 25.483637-25.506909l46.778181 46.801455a18.013091 18.013091 0 0 1-12.730181 30.766545z + M372.134088 1004.846981a509.94041 509.94041 0 0 1-159.298198-76.821527l2.164315-103.514738a93.088793 93.088793 0 0 0-74.238313-93.088794l-101.536602-20.991523A509.428422 509.428422 0 0 1 0.011636 537.983409l82.243949-62.834935a93.088793 93.088793 0 0 0 26.507034-116.104998l-46.89348-92.390627A514.152678 514.152678 0 0 1 172.225904 128.415991l100.419536 25.133974a93.088793 93.088793 0 0 0 107.284834-51.664281l43.007023-94.252403a515.246472 515.246472 0 0 1 88.434354-7.610009c30.137497 0 59.669917 2.606486 88.411081 7.610009l43.007023 94.252403a93.088793 93.088793 0 0 0 107.284834 51.664281l100.396264-25.133974a514.152678 514.152678 0 0 1 110.356765 138.236858l-46.870208 92.390627a93.088793 93.088793 0 0 0 26.483762 116.104998l82.243949 62.834935a509.428422 509.428422 0 0 1-39.213654 172.44699l-101.51333 20.968251a93.088793 93.088793 0 0 0-74.238312 93.088793l2.141042 103.538011a509.94041 509.94041 0 0 1-159.298198 76.798255l-79.684007-66.279221a93.088793 93.088793 0 0 0-119.060567 0l-79.684007 66.302493zM511.348378 721.461421a209.449785 209.449785 0 1 0 0-418.89957 209.449785 209.449785 0 0 0 0 418.89957z + M512 85.33312C323.499138 85.33312 170.66752 238.164738 170.66752 426.6656s152.831618 341.33248 341.33248 341.33248 341.33248-152.831618 341.33248-341.33248S700.500862 85.33312 512 85.33312zM85.3344 426.6656C85.3344 191.032411 276.366811 0 512 0c235.661633 0 426.6656 191.032411 426.6656 426.6656 0 235.661633-191.003967 426.6656-426.6656 426.6656-235.633189 0-426.6656-191.003967-426.6656-426.6656z m127.99968 554.66528A42.66656 42.66656 0 0 1 256.00064 938.66432h511.99872a42.66656 42.66656 0 1 1 0 85.33312H256.00064a42.66656 42.66656 0 0 1-42.66656-42.66656z M459.776131 775.166062a42.239894 42.239894 0 0 1 19.114618 56.888747l-85.560675 169.642242a42.951004 42.951004 0 0 1-56.774969 17.806178 42.239894 42.239894 0 0 1-19.71195-55.722527l85.560675-169.642243a42.951004 42.951004 0 0 1 57.372301-18.972397z m132.892112 0a42.239894 42.239894 0 0 0-19.114619 56.888747l85.560675 169.642242a42.951004 42.951004 0 0 0 56.774969 17.806178 42.239894 42.239894 0 0 0 19.711951-55.722527l-85.560675-169.642243a42.951004 42.951004 0 0 0-57.372301-18.972397zM526.222187 321.136975c-61.439846 0-111.2175 49.350988-111.2175 110.250391 0 60.927848 49.777653 110.278835 111.2175 110.278835s111.2175-49.350988 111.217499-110.278835c0-60.870959-49.777653-110.250391-111.217499-110.250391z m-196.806619 110.250391c0-107.747286 88.120669-195.099957 196.806619-195.099957 108.685951 0 196.806619 87.352671 196.806619 195.128401 0 107.747286-88.120669 195.099957-196.806619 195.099957-108.685951 0-196.806619-87.352671-196.806619-195.128401zM526.222187 28.444373c23.60883 0 42.780337 19.000841 42.780337 42.410561v63.630063c0 23.409719-19.143063 42.382116-42.780337 42.382116-23.60883 0-42.780337-18.972397-42.780338-42.382116V70.82649c0-23.409719 19.143063-42.410561 42.780338-42.410561z + M864 159.872L160 160c-17.696 0-32 14.176-32 31.872v448a32 32 0 0 0 32 32h704a32 32 0 0 0 32-32v-448a32 32 0 0 0-32-32zM864 640H160V191.872h704V640z M928 32H96a96 96 0 0 0-96 96v640a95.904 95.904 0 0 0 95.68 95.936H416v38.944l-199.744 25.952A31.968 31.968 0 0 0 224 991.872h576a32 32 0 0 0 7.744-63.072L608 902.88v-38.944h320.32A95.904 95.904 0 0 0 1024 768V128a96 96 0 0 0-96-96z m32 736c0 17.632-14.368 32-32 32H96c-17.664 0-32-14.368-32-32V128a32 32 0 0 1 32-32h832c17.632 0 32 14.336 32 32v640z + M486.4 972.8v-128.9728A332.8 332.8 0 0 1 179.2 512a25.6 25.6 0 0 1 51.2 0 281.6 281.6 0 0 0 563.2 0 25.6 25.6 0 1 1 51.2 0 332.8 332.8 0 0 1-307.2 331.8272V972.8h153.6a25.6 25.6 0 1 1 0 51.2h-358.4a25.6 25.6 0 1 1 0-51.2h153.6zM512 51.2a153.6 153.6 0 0 0-153.6 153.6v307.2a153.6 153.6 0 0 0 307.2 0V204.8a153.6 153.6 0 0 0-153.6-153.6z m0-51.2a204.8 204.8 0 0 1 204.8 204.8v307.2a204.8 204.8 0 1 1-409.6 0V204.8a204.8 204.8 0 0 1 204.8-204.8z + M511.626 1.896C229.572 1.896 0.927 230.541 0.927 512.595c0 282.055 228.645 510.699 510.699 510.699s510.698-228.645 510.698-510.699S793.68 1.896 511.626 1.896z m0 69.641c243.606 0 441.058 197.474 441.058 441.058 0 87.347-25.392 168.762-69.194 237.271-73.419-77.609-170.944-132.204-280.597-151.829 70.004-33.755 118.404-105.164 118.404-188.066 0-115.388-93.535-208.922-208.923-208.922S303.452 294.583 303.452 409.97c0 82.902 48.399 154.311 118.403 188.066-110.093 19.704-207.96 74.661-281.479 152.77-44.177-68.704-69.808-150.465-69.808-238.211 0-243.584 197.496-441.058 441.058-441.058z + + + @@ -34,6 +41,13 @@ + + + + + 直播中 + + @@ -44,12 +58,108 @@ - + - - + + + + + 请选择授课方式 + 您可在上课期间随时调整授课方式 + + + + + + + + + + + + + + + + + + 摄像头预览 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + 请选择要分享的屏幕或窗口 + + + + + + + + + + + + + + + + + diff --git a/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs b/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs index f59a34b..0c24bc8 100644 --- a/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs +++ b/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs @@ -12,6 +12,9 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Linq; +using System.Windows.Threading; +using JianGongYun.TRTC.Components; +using System.Runtime.InteropServices; namespace JianGongYun.TRTC.Windows { @@ -20,24 +23,38 @@ namespace JianGongYun.TRTC.Windows /// public partial class LiveWindow : MetroWindow { + [DllImport("Kernel32.dll", EntryPoint = "AttachConsole", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern void AttachConsole(int dwProcessId); + ViewModels.LiveWindowViewModel LiveWindowViewModel; + ViewModels.SettingWindowViewModel SettingWindowViewModel; public LiveWindow() { - InitializeComponent(); - LiveWindowViewModel =new ViewModels.LiveWindowViewModel(); + InitializeComponent(); + AttachConsole(-1); + LiveWindowViewModel = new ViewModels.LiveWindowViewModel(); + SettingWindowViewModel = ViewModels.SettingWindowViewModel.GetInstance(); this.DataContext = LiveWindowViewModel; BorderBrush = new SolidColorBrush(color: Color.FromRgb(42, 43, 48));//窗口标题背景颜色 - } - private void aaaa(object sender, RoutedEventArgs e) + + protected override void OnActivated(EventArgs e) { - LiveWindowViewModel.StudentCount++; + base.OnActivated(e); + //颜色前台设置无效,手动调用才行 + var color = (SolidColorBrush)(new BrushConverter().ConvertFrom("#cccccc")); + Rad1.Foreground = color; + Rad2.Foreground = color; + Rad3.Foreground = color; + Rad4.Foreground = color; } + private Window settingWindow; //设置按钮 private void Setting_Btn_Click(object sender, RoutedEventArgs e) { - var settingWindow = SettingWindow.GetInstance(); + settingWindow = SettingWindow.GetInstance(); + settingWindow.Closed += (a, b) => settingWindow = null; settingWindow.Show(); if (settingWindow.WindowState != WindowState.Normal) { @@ -45,5 +62,124 @@ namespace JianGongYun.TRTC.Windows } settingWindow.Topmost = true; } + + protected override void OnClosed(EventArgs e) + { + if (settingWindow != null) + { + settingWindow.Close(); + } + base.OnClosed(e); + } + + /// + /// 开始/结束直播 + /// + /// + /// + private void StartLive_Click(object sender, RoutedEventArgs e) + { + var btn = sender as AduFlatButton; + var start = Convert.ToBoolean(btn.Tag); + if (start)//开始直播 + { + this.Dispatcher.Invoke(new Action(() => + { + btn.Type = AduSkin.Controls.FlatButtonSkinEnum.warning; + btn.Content = "直播倒计时"; + })); + StartCountdown(() => + { + LiveWindowViewModel.IsLive = start; + this.Dispatcher.Invoke(new Action(() => + { + btn.Type = AduSkin.Controls.FlatButtonSkinEnum.info; + btn.Content = "开始直播"; + })); + }, SettingWindowViewModel.ScreenRecordingCountdown); + } + else + { + LiveWindowViewModel.IsLive = start; + } + + } + + /// + /// 开始倒计时 + /// + public void StartCountdown(Action countdownEnd, int time = 10) + { + CountdownEnd = countdownEnd; + count = time; + this.Dispatcher.Invoke(new Action(() => + { + CountdownWrap.Visibility = Visibility.Visible; + })); + timer = new DispatcherTimer(); + timer.Interval = TimeSpan.FromSeconds(1); + timer.Tick += new EventHandler(timer_Tick); + timer.Start(); + } + + /// + /// 倒计时结束执行 + /// + Action CountdownEnd; + /// + /// 倒计时时间 + /// + private int count; + /// + /// 创建Timer对象 + /// + private DispatcherTimer timer = null; + private void timer_Tick(object sender, EventArgs e) + { + if (count == 0) + { + timer.Stop(); + timer = null; + this.Dispatcher.Invoke(new Action(() => + { + CountdownWrap.Visibility = Visibility.Hidden; + })); + CountdownEnd?.Invoke(); + //count = 10; + //timer.Start(); + } + else + { + CountdownControl txt = new CountdownControl(this.CountdownWrap, this); + txt.TxtValue = count.ToString(); + txt.HorizontalAlignment = System.Windows.HorizontalAlignment.Center; + txt.VerticalAlignment = System.Windows.VerticalAlignment.Center; + this.CountdownWrap.Children.Add(txt); + count--; + } + + } + + private void Tpye_Click(object sender, RoutedEventArgs e) + { + switch (LiveWindowViewModel.LiveType) + { + case Models.LiveTypeEnum.CameraAndScreen: + break; + case Models.LiveTypeEnum.OnlyScreen: + + break; + case Models.LiveTypeEnum.OnlyCamera: + if (!LiveWindowViewModel.CameraRunning) + { + LiveClassroom.StartVideoMain(BeforeLiveViewWrap); + } + break; + case Models.LiveTypeEnum.OnlyAudio: + break; + default: + break; + } + } } } diff --git a/JianGongYun/TRTC/Windows/SettingWindow.xaml b/JianGongYun/TRTC/Windows/SettingWindow.xaml index 6d982e3..d2af72c 100644 --- a/JianGongYun/TRTC/Windows/SettingWindow.xaml +++ b/JianGongYun/TRTC/Windows/SettingWindow.xaml @@ -50,11 +50,125 @@ - - + + + + 视频帧率(fps) + + + + + + + + + + + + 音频码率 + + + + + + + + + + + + 音频采样率 + + + + + + + + + + + + 编码级别 + + + + + + + + 录制倒计时 + + + + + + + + + 保存到文件夹 + + + + + + + - - + + + + 音频质量 + + + + + + + + 摄像画面帧率(fps) + + + + + + + + + + 摄像画面码率 + + + + + + + + 摄像画面级别 + + + + + + + 屏幕分享帧率(fps) + + + + + + + + + + 屏幕分享码率 + + + + + + + diff --git a/JianGongYun/TRTC/Windows/SettingWindow.xaml.cs b/JianGongYun/TRTC/Windows/SettingWindow.xaml.cs index 083d118..a9b1bc9 100644 --- a/JianGongYun/TRTC/Windows/SettingWindow.xaml.cs +++ b/JianGongYun/TRTC/Windows/SettingWindow.xaml.cs @@ -1,6 +1,7 @@ using AduSkin.Controls.Metro; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; using System.Windows; using System.Windows.Controls; @@ -48,6 +49,10 @@ namespace JianGongYun.TRTC.Windows this.DataContext = mode; } + + /// + /// 测试麦克风 + /// bool micTesting = false; private void TestMic(object sender, RoutedEventArgs e) { @@ -87,5 +92,33 @@ namespace JianGongYun.TRTC.Windows Slider1.DecreaseColor = sliderColor; Slider2.DecreaseColor = sliderColor; } + + /// + /// 打开文件浏览器 + /// + /// + /// + private void OpenDir_Click(object sender, RoutedEventArgs e) + { + var process = new Process + { + StartInfo = new ProcessStartInfo { FileName = "explorer.exe", Arguments = mode.ScreenRecordingDir } + }; + process.Start(); + } + + /// + /// 修改路径 + /// + /// + /// + private void ChangeDir_Click(object sender, RoutedEventArgs e) + { + System.Windows.Forms.FolderBrowserDialog openFileDialog = new System.Windows.Forms.FolderBrowserDialog(); //选择文件夹 + if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)//注意,此处一定要手动引入System.Window.Forms空间,否则你如果使用默认的DialogResult会发现没有OK属性 + { + mode.ScreenRecordingDir = openFileDialog.SelectedPath; + } + } } } diff --git a/JianGongYun/TRTC_SDK/Win32/lib/openh264-1.8.0-win32.dll b/JianGongYun/TRTC_SDK/Win32/lib/openh264-1.8.0-win32.dll new file mode 100644 index 0000000..bd3df35 Binary files /dev/null and b/JianGongYun/TRTC_SDK/Win32/lib/openh264-1.8.0-win32.dll differ diff --git a/JianGongYun/TRTC_SDK/Win64/lib/openh264-1.8.0-win64.dll b/JianGongYun/TRTC_SDK/Win64/lib/openh264-1.8.0-win64.dll new file mode 100644 index 0000000..99fb7b2 Binary files /dev/null and b/JianGongYun/TRTC_SDK/Win64/lib/openh264-1.8.0-win64.dll differ