diff --git a/JianGongYun/App.xaml b/JianGongYun/App.xaml index 1a00d43..0b1630d 100644 --- a/JianGongYun/App.xaml +++ b/JianGongYun/App.xaml @@ -12,6 +12,7 @@ + diff --git a/JianGongYun/JianGongYun.csproj b/JianGongYun/JianGongYun.csproj index f9244b0..3d696aa 100644 --- a/JianGongYun/JianGongYun.csproj +++ b/JianGongYun/JianGongYun.csproj @@ -15,6 +15,8 @@ + + diff --git a/JianGongYun/Style/TRTCResource.xaml b/JianGongYun/Style/TRTCResource.xaml new file mode 100644 index 0000000..d4856e1 --- /dev/null +++ b/JianGongYun/Style/TRTCResource.xaml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml b/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml new file mode 100644 index 0000000..e5cab13 --- /dev/null +++ b/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml @@ -0,0 +1,10 @@ + + diff --git a/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml.cs b/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml.cs new file mode 100644 index 0000000..8f5f274 --- /dev/null +++ b/JianGongYun/TRTC/Components/SytemGatherVolumeControl.xaml.cs @@ -0,0 +1,26 @@ +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/LiveClassroom.cs b/JianGongYun/TRTC/LiveClassroom.cs new file mode 100644 index 0000000..42a415a --- /dev/null +++ b/JianGongYun/TRTC/LiveClassroom.cs @@ -0,0 +1,127 @@ +using JianGongYun.TRTC.Models; +using JianGongYun.TRTC.Utils; +using JianGongYun.TRTC.Windows; +using ManageLiteAV; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; + +namespace JianGongYun.TRTC +{ + /// + /// 直播调用对象全放这里 + /// + public static class LiveClassroom + { + const uint SDKAppID = 1400463444; + const string SDKAppKEY = "6ee2586282eb8ab5bff3f917b44500c4ffd9bbd3d820258b1fa8cdd470cfd1ee"; + /// + /// TRTC实例 + /// + public static ITRTCCloud lTRTCCloud; + /// + /// 设备管理器 + /// + public static ITXDeviceManager lTXDeviceManager; + /// + /// live窗口的调用窗口 + /// + public static Window CallerWindow { get; private set; } + /// + /// 直播窗口 + /// + public static Window CurrentLiveWindow; + public static ClassroomEntity CurrentClassroomEntity { get; private set; } + //public static int UserCount = 999; + /// + /// 进入直播教室 + /// + /// 调用窗口 + /// 教室数据 + public static void EnterTheClassroom(Window callerWindow, ClassroomEntity classroomEntity) + { + if (CurrentLiveWindow != null) + { + _ = AduSkin.Controls.Metro.AduMessageBox.Show("已经进入教室", "提醒"); + CurrentLiveWindow.Topmost = true; + return; + } + CallerWindow = callerWindow; + CurrentClassroomEntity = classroomEntity; + CurrentLiveWindow = new LiveWindow(); + CurrentLiveWindow.Closed += CurrentLiveWindow_Closed; + + + lTRTCCloud = ITRTCCloud.getTRTCShareInstance();//创建TRTC实例 + var roomPars = new TRTCParams + { + sdkAppId = SDKAppID, + userId = CurrentClassroomEntity.TeacherId, + userSig = GenTestUserSig(CurrentClassroomEntity.TeacherId), + roomId = 0,//使用strRoomId + strRoomId = CurrentClassroomEntity.TeacherId, + role = TRTCRoleType.TRTCRoleAnchor + }; + lTRTCCloud.enterRoom(ref roomPars, TRTCAppScene.TRTCAppSceneLIVE);//创建房间 + + //设备 + var settingViewMode = ViewModels.SettingWindowViewModel.GetInstance(); + lTXDeviceManager = lTRTCCloud.getDeviceManager(); + var currentMic = settingViewMode.CurrentMic;//麦克风 + if (!string.IsNullOrEmpty(currentMic)) + { + var res = lTXDeviceManager.setCurrentDevice(TRTCDeviceType.TXMediaDeviceTypeMic, currentMic); + Console.WriteLine($"设置麦克风:{res}"); + } + //设备完结 + + + callerWindow.Hide();//隐藏调用窗口 + CurrentLiveWindow.Show(); + } + + private static void CurrentLiveWindow_Closed(object sender, EventArgs e) + { + CurrentLiveWindow = null; + CurrentClassroomEntity = null; + CallerWindow.Show();//还原调用者窗口 + + lTXDeviceManager.Dispose(); + lTXDeviceManager = null; + lTRTCCloud.exitRoom(); + ITRTCCloud.destroyTRTCShareInstance();//销毁TRTC实例 + lTRTCCloud.Dispose(); + lTRTCCloud = null; + } + + + + /// + /// 计算 UserSig 签名 + /// + /// 函数内部使用 HMAC-SHA256 非对称加密算法,对 SDKAPPID、userId 和 EXPIRETIME 进行加密 + /// + /// 该方案仅适合本地跑通demo和功能调试,产品真正上线发布,要使用服务器获取方案避免私钥被破解。 + /// + /// + /// 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下: + /// + /// 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品, + /// 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。 + /// 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。 + /// + /// 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。 + /// 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。 + /// + /// 文档:https://cloud.tencent.com/document/product/647/17275#GetFromServer + /// + public static string GenTestUserSig(string userId) + { + if (SDKAppID == 0 || string.IsNullOrEmpty(SDKAppKEY)) return null; + TLSSigAPIv2 api = new TLSSigAPIv2((int)SDKAppID, SDKAppKEY); + // 统一转换为UTF8,SDK内部是用UTF8编码。 + return api.GenSig(Util.UTF16To8(userId)); + } + } +} diff --git a/JianGongYun/TRTC/Models/ClassroomEntity.cs b/JianGongYun/TRTC/Models/ClassroomEntity.cs new file mode 100644 index 0000000..7b2b44d --- /dev/null +++ b/JianGongYun/TRTC/Models/ClassroomEntity.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JianGongYun.TRTC.Models +{ + /// + /// 教室实体数据 + /// + public class ClassroomEntity + { + /// + /// 教师id(长度不超过32字符,教师id同时视为直播教室id) + /// + public string TeacherId { get; set; } + /// + /// 教师名称 + /// + public string TeacherName { get; set; } + /// + /// 课程名称 + /// + public string ClassHead { get; set; } + /// + /// 章节名称 + /// + public string ClassSubHead { get; set; } + + } +} diff --git a/JianGongYun/TRTC/Models/ComboBoxEntity.cs b/JianGongYun/TRTC/Models/ComboBoxEntity.cs new file mode 100644 index 0000000..485eb75 --- /dev/null +++ b/JianGongYun/TRTC/Models/ComboBoxEntity.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JianGongYun.TRTC.Models +{ + public class ComboBoxEntity + { + public string Id { get; set; } + public string Text { get; set; } + } +} diff --git a/JianGongYun/TRTC/Utils/IniStorage.cs b/JianGongYun/TRTC/Utils/IniStorage.cs new file mode 100644 index 0000000..99c3020 --- /dev/null +++ b/JianGongYun/TRTC/Utils/IniStorage.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace JianGongYun.TRTC.Utils +{ + public class IniStorage + { // 声明INI文件的写操作函数 + [DllImport("kernel32")] + private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); + + // 声明INI文件的读操作函数 + [DllImport("kernel32")] + private static extern int GetPrivateProfileString(string section, string key, string def, System.Text.StringBuilder retVal, int size, string filePath); + + private string sPath = null; + + public IniStorage(string path) + { + this.sPath = path; + } + + public void SetValue(string section, string key, string value) + { + // section=配置节,key=键名,value=键值,path=路径 + WritePrivateProfileString(section, key, value, sPath); + } + + public string GetValue(string section, string key) + { + // 每次从ini中读取多少字节 + System.Text.StringBuilder temp = new System.Text.StringBuilder(255); + // section=配置节,key=键名,temp=上面,path=路径 + GetPrivateProfileString(section, key, "", temp, 255, sPath); + return temp.ToString(); + } + } +} diff --git a/JianGongYun/TRTC/Utils/TLSSigAPIv2.cs b/JianGongYun/TRTC/Utils/TLSSigAPIv2.cs new file mode 100644 index 0000000..cd70105 --- /dev/null +++ b/JianGongYun/TRTC/Utils/TLSSigAPIv2.cs @@ -0,0 +1,123 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using zlib; + +namespace JianGongYun.TRTC.Utils +{ + public class TLSSigAPIv2 + { + private readonly int sdkappid; + private readonly string key; + + public TLSSigAPIv2(int sdkappid, string key) + { + this.sdkappid = sdkappid; + this.key = key; + } + + private static byte[] CompressBytes(byte[] sourceByte) + { + MemoryStream inputStream = new MemoryStream(sourceByte); + Stream outStream = CompressStream(inputStream); + byte[] outPutByteArray = new byte[outStream.Length]; + outStream.Position = 0; + outStream.Read(outPutByteArray, 0, outPutByteArray.Length); + return outPutByteArray; + } + + private static Stream CompressStream(Stream sourceStream) + { + MemoryStream streamOut = new MemoryStream(); + ZOutputStream streamZOut = new ZOutputStream(streamOut, zlibConst.Z_DEFAULT_COMPRESSION); + CopyStream(sourceStream, streamZOut); + streamZOut.finish(); + return streamOut; + } + + public static void CopyStream(System.IO.Stream input, System.IO.Stream output) + { + byte[] buffer = new byte[2000]; + int len; + while ((len = input.Read(buffer, 0, 2000)) > 0) + { + output.Write(buffer, 0, len); + } + output.Flush(); + } + + private string HMACSHA256(string identifier, long currTime, int expire, string base64UserBuf, bool userBufEnabled) + { + string rawContentToBeSigned = "TLS.identifier:" + identifier + "\n" + + "TLS.sdkappid:" + sdkappid + "\n" + + "TLS.time:" + currTime + "\n" + + "TLS.expire:" + expire + "\n"; + if (true == userBufEnabled) + { + rawContentToBeSigned += "TLS.userbuf:" + base64UserBuf + "\n"; + } + using (HMACSHA256 hmac = new HMACSHA256()) + { + UTF8Encoding encoding = new UTF8Encoding(); + Byte[] textBytes = encoding.GetBytes(rawContentToBeSigned); + Byte[] keyBytes = encoding.GetBytes(key); + Byte[] hashBytes; + using (HMACSHA256 hash = new HMACSHA256(keyBytes)) + hashBytes = hash.ComputeHash(textBytes); + return Convert.ToBase64String(hashBytes); + } + } + + private string GenSig(string identifier, int expire, byte[] userbuf, bool userBufEnabled) + { + DateTime epoch = new DateTime(1970, 1, 1); // unix 时间戳 + Int64 currTime = (Int64)(DateTime.UtcNow - epoch).TotalMilliseconds / 1000; + + string base64UserBuf; + string jsonData; + if (true == userBufEnabled) + { + base64UserBuf = Convert.ToBase64String(userbuf); + string base64sig = HMACSHA256(identifier, currTime, expire, base64UserBuf, userBufEnabled); + var jsonObj = new JObject(); + jsonObj["TLS.ver"] = "2.0"; + jsonObj["TLS.identifier"] = identifier; + jsonObj["TLS.sdkappid"] = sdkappid; + jsonObj["TLS.expire"] = expire; + jsonObj["TLS.time"] = currTime; + jsonObj["TLS.sig"] = base64sig; + jsonObj["TLS.userbuf"] = base64UserBuf; + jsonData = JsonConvert.SerializeObject(jsonObj); + } + else + { + string base64sig = HMACSHA256(identifier, currTime, expire, "", false); + var jsonObj = new JObject(); + jsonObj["TLS.ver"] = "2.0"; + jsonObj["TLS.identifier"] = identifier; + jsonObj["TLS.sdkappid"] = sdkappid; + jsonObj["TLS.expire"] = expire; + jsonObj["TLS.time"] = currTime; + jsonObj["TLS.sig"] = base64sig; + jsonData = JsonConvert.SerializeObject(jsonObj); + } + byte[] buffer = Encoding.UTF8.GetBytes(jsonData); + return Convert.ToBase64String(CompressBytes(buffer)) + .Replace('+', '*').Replace('/', '-').Replace('=', '_'); + } + + public string GenSig(string identifier, int expire = 180 * 86400) + { + return GenSig(identifier, expire, null, false); + } + + public string GenSigWithUserBuf(string identifier, int expire, byte[] userbuf) + { + return GenSig(identifier, expire, userbuf, true); + } + } +} diff --git a/JianGongYun/TRTC/Utils/Util.cs b/JianGongYun/TRTC/Utils/Util.cs new file mode 100644 index 0000000..267ea9a --- /dev/null +++ b/JianGongYun/TRTC/Utils/Util.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace JianGongYun.TRTC.Utils +{ + public class Util + { + public static string UTF16To8(string str) + { + byte[] utf16Bytes = Encoding.Unicode.GetBytes(str); + byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Bytes); + return Encoding.UTF8.GetString(utf8Bytes); + } + + public static bool IsSys64bit() + { + return IntPtr.Size == 8; + } + + public static string GetRandomString(int iLength) + { + string buffer = "0123456789"; + StringBuilder sb = new StringBuilder(); + Random r = new Random(iLength); + int range = buffer.Length; + for (int i = 0; i < iLength; i++) + { + sb.Append(buffer.Substring(r.Next(range), 1)); + } + return sb.ToString(); + } + + public static string MD5(string str) + { + MD5 md5 = System.Security.Cryptography.MD5.Create(); + byte[] byteOld = Encoding.UTF8.GetBytes(str); + byte[] byteNew = md5.ComputeHash(byteOld); + StringBuilder sb = new StringBuilder(); + foreach (byte b in byteNew) + { + // 将字节转换成16进制表示的字符串 + sb.Append(b.ToString("x2")); + } + return sb.ToString(); + } + + public static bool IsTestEnv() + { + string path = Environment.CurrentDirectory + "\\ShowTestEnv.txt"; + return File.Exists(path); + } + + public static string ConvertMSToTime(int lCurMS, int lDurationMS) + { + string strTime = "00:00/00:00"; + + int nTotalDurationSecond = lDurationMS / 1000; + int nDurationMinute = nTotalDurationSecond / 60; + int nDurationSecond = nTotalDurationSecond % 60; + + int nTotalCurSecond = lCurMS / 1000; + int nCurMinute = nTotalCurSecond / 60; + int nCurSecond = nTotalCurSecond % 60; + + string format = "{0:00}:{1:00}/{2:00}:{3:00}"; + + strTime = string.Format(format, nCurMinute, nCurSecond, nDurationMinute, nDurationSecond); + + return strTime; + } + } +} diff --git a/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs b/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs new file mode 100644 index 0000000..57efc01 --- /dev/null +++ b/JianGongYun/TRTC/ViewModels/LiveWindowViewModel.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Text; + +namespace JianGongYun.TRTC.ViewModels +{ + public class LiveWindowViewModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// 教室信息 + /// + public Models.ClassroomEntity ClassroomEntity { get { return LiveClassroom.CurrentClassroomEntity; } } + + /// + /// 学生总数 + /// + private int _StudentCount = 0; + public int StudentCount + { + set + { + _StudentCount = value; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("StudentCount"));//对StudentCount进行监听 + } + } + get + { + return _StudentCount; + } + } + + /// + /// 是否在直播中 + /// + private bool _IsLive = false; + public bool IsLive + { + set + { + _IsLive = value; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("IsLive")); + } + } + get + { + return _IsLive; + } + } + + } +} diff --git a/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs b/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs new file mode 100644 index 0000000..ecc163b --- /dev/null +++ b/JianGongYun/TRTC/ViewModels/SettingWindowViewModel.cs @@ -0,0 +1,304 @@ +using JianGongYun.TRTC.Models; +using JianGongYun.TRTC.Utils; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Text; +using System.Linq; +using System.Windows.Controls; +using JianGongYun.TRTC.Components; +using System.Windows; + +namespace JianGongYun.TRTC.ViewModels +{ + public class SettingWindowViewModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + /// + /// 单例 + /// + private static SettingWindowViewModel _Instance; + + public static SettingWindowViewModel GetInstance() + { + if (_Instance == null) + { + _Instance = new SettingWindowViewModel(); + } + return _Instance; + } + + /// + /// 直播主窗口viewmodel + /// + private LiveWindowViewModel LiveWindowViewModel + { + get + { + return LiveClassroom.CurrentLiveWindow.DataContext == null ? null : LiveClassroom.CurrentLiveWindow.DataContext as LiveWindowViewModel; + } + } + + #region 麦克风 + /// + /// 集合,实时获取 + /// + public List MicList + { + get + { + var mics = LiveClassroom.lTXDeviceManager.getDevicesList(ManageLiteAV.TRTCDeviceType.TXMediaDeviceTypeMic); + List micList = new List(); + var count = mics.getCount(); + for (uint i = 0; i < count; i++) + { + var temp = new ComboBoxEntity { Id = mics.getDevicePID(i), Text = mics.getDeviceName(i) }; + micList.Add(temp); + } + return micList; + } + } + + /// + /// 当前 + /// + private string _CurrentMic; + public string CurrentMic + { + get + { + var tempList = this.MicList; + if (tempList.Count == 0) + { + return null; + } + if (string.IsNullOrEmpty(_CurrentMic) || !tempList.Any(a => a.Id == _CurrentMic))//第一次取或者设备发生变动找不到上次的选择 + { + var localMic = storage.GetValue(INI_ROOT_KEY, INI_KEY_CHOOSE_MIC);//本地缓存 + if (string.IsNullOrEmpty(localMic))//本地没有就取第一个 + { + _CurrentMic = tempList.First().Id; + storage.SetValue(INI_ROOT_KEY, INI_KEY_CHOOSE_MIC, _CurrentMic);//生成初始数据 + } + else + { + _CurrentMic = localMic; + var temp = tempList.Where(a => a.Id == localMic).FirstOrDefault(); + if (temp == null)//找不到上次缓存的设备就取第一个 + { + _CurrentMic = tempList.First().Id; + storage.SetValue(INI_ROOT_KEY, INI_KEY_CHOOSE_MIC, _CurrentMic);//生成初始数据 + } + } + } + return _CurrentMic; + } + set + { + _CurrentMic = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_CHOOSE_MIC, value);//生成保存本地 + LiveClassroom.lTXDeviceManager.setCurrentDevice(ManageLiteAV.TRTCDeviceType.TXMediaDeviceTypeMic, _CurrentMic);//设置麦克 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("CurrentMic"));//对CurrentMic进行监听 + } + } + } + + + /// + /// 麦克风采集音量 + /// + private uint _MicVolume = 100; + public uint MicVolume + { + get + { + var micVolume = storage.GetValue(INI_ROOT_KEY, INI_KEY_AUDIO_MIC_VOLUME);//本地缓存 + if (string.IsNullOrEmpty(micVolume))//本地没有就取第一个 + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_AUDIO_MIC_VOLUME, _MicVolume.ToString());//生成初始数据 + } + else + { + _MicVolume = uint.Parse(micVolume); + } + return _MicVolume; + } + set + { + _MicVolume = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_AUDIO_MIC_VOLUME, value.ToString());//生成保存本地 + var res = LiveClassroom.lTXDeviceManager.setCurrentDeviceVolume(ManageLiteAV.TRTCDeviceType.TXMediaDeviceTypeMic, _MicVolume);//设置麦克音量 + Console.WriteLine("设置麦克风采集音量" + res); + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("MicVolume")); + } + } + } + + + #endregion + + #region 音频来源 + + /// + /// 音频来源 + /// + private string _AudioSource = "1"; + public string AudioSource + { + get + { + var audioSource = storage.GetValue(INI_ROOT_KEY, INI_KEY_AUDIO_SOURCE);//本地缓存 + if (string.IsNullOrEmpty(audioSource))//本地没有就取第一个 + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_AUDIO_SOURCE, _AudioSource);//生成初始数据 + } + else + { + _AudioSource = audioSource; + } + return _AudioSource; + } + set + { + _AudioSource = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_AUDIO_SOURCE, value);//生成保存本地 + if (_AudioSource == "1")//只采集麦克风,关闭系统音采集 + { + ShowSytemGatherSlider = Visibility.Hidden; + LiveClassroom.lTRTCCloud.stopSystemAudioLoopback(); + } + else if (_AudioSource == "2")//麦和系统音,开启采集系统音 + { + ShowSytemGatherSlider = Visibility.Visible; + LiveClassroom.lTRTCCloud.startSystemAudioLoopback(null); + } + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("AudioSource")); + } + } + } + + /// + /// 是否显示系统采集滑动条 + /// + private Visibility _ShowSytemGatherSlider = Visibility.Hidden; + public Visibility ShowSytemGatherSlider + { + get + { + if (AudioSource=="2") + { + _ShowSytemGatherSlider = Visibility.Visible; + } + else + { + _ShowSytemGatherSlider = Visibility.Hidden; + } + return _ShowSytemGatherSlider; + } + set + { + _ShowSytemGatherSlider = value; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("ShowSytemGatherSlider")); + } + } + } + + /// + /// 系统采集音量 + /// + private uint _SytemGatherVolume = 100; + public uint SytemGatherVolume + { + get + { + var sytemGatherVolume = storage.GetValue(INI_ROOT_KEY, INI_KEY_AUDIO_SYSTEM_GATHER_VOLUME);//本地缓存 + if (string.IsNullOrEmpty(sytemGatherVolume))//本地没有就取第一个 + { + storage.SetValue(INI_ROOT_KEY, INI_KEY_AUDIO_SYSTEM_GATHER_VOLUME, _SytemGatherVolume.ToString());//生成初始数据 + } + else + { + _SytemGatherVolume = uint.Parse(sytemGatherVolume); + } + return _SytemGatherVolume; + } + set + { + _SytemGatherVolume = value; + storage.SetValue(INI_ROOT_KEY, INI_KEY_AUDIO_SYSTEM_GATHER_VOLUME, value.ToString());//生成保存本地 + LiveClassroom.lTRTCCloud.setSystemAudioLoopbackVolume(_SytemGatherVolume);//设置系统采集音量 + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("SytemGatherVolume")); + } + } + } + + + #endregion + + + /// + /// ini文件操作 + /// + private IniStorage storage; + private SettingWindowViewModel() + { + storage = new IniStorage(sPath); + } + + #region 配置持久化 + + //配置文件路径 + 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"; + #endregion + } +} diff --git a/JianGongYun/TRTC/Windows/LiveWindow.xaml b/JianGongYun/TRTC/Windows/LiveWindow.xaml new file mode 100644 index 0000000..e5a6c52 --- /dev/null +++ b/JianGongYun/TRTC/Windows/LiveWindow.xaml @@ -0,0 +1,55 @@ + + + + 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 + + + + + + + + + + 《》 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs b/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs new file mode 100644 index 0000000..f59a34b --- /dev/null +++ b/JianGongYun/TRTC/Windows/LiveWindow.xaml.cs @@ -0,0 +1,49 @@ +using AduSkin.Controls.Metro; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +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.Shapes; +using System.Linq; + +namespace JianGongYun.TRTC.Windows +{ + /// + /// LiveWindow.xaml 的交互逻辑 + /// + public partial class LiveWindow : MetroWindow + { + ViewModels.LiveWindowViewModel LiveWindowViewModel; + public LiveWindow() + { + InitializeComponent(); + LiveWindowViewModel =new ViewModels.LiveWindowViewModel(); + this.DataContext = LiveWindowViewModel; + BorderBrush = new SolidColorBrush(color: Color.FromRgb(42, 43, 48));//窗口标题背景颜色 + + } + private void aaaa(object sender, RoutedEventArgs e) + { + LiveWindowViewModel.StudentCount++; + } + + //设置按钮 + private void Setting_Btn_Click(object sender, RoutedEventArgs e) + { + var settingWindow = SettingWindow.GetInstance(); + settingWindow.Show(); + if (settingWindow.WindowState != WindowState.Normal) + { + settingWindow.WindowState = WindowState.Normal; + } + settingWindow.Topmost = true; + } + } +} diff --git a/JianGongYun/TRTC/Windows/SettingWindow.xaml b/JianGongYun/TRTC/Windows/SettingWindow.xaml new file mode 100644 index 0000000..6d982e3 --- /dev/null +++ b/JianGongYun/TRTC/Windows/SettingWindow.xaml @@ -0,0 +1,62 @@ + + + + + 音频设置 + + + 录屏设置 + + + 直播设置 + + + + + + + + + 麦克风 + + + + + 麦采集音量 + + + + 音频来源 + + + + + + + 系统采集音量 + + + + + + + + + + + + + + diff --git a/JianGongYun/TRTC/Windows/SettingWindow.xaml.cs b/JianGongYun/TRTC/Windows/SettingWindow.xaml.cs new file mode 100644 index 0000000..083d118 --- /dev/null +++ b/JianGongYun/TRTC/Windows/SettingWindow.xaml.cs @@ -0,0 +1,91 @@ +using AduSkin.Controls.Metro; +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.Shapes; + +namespace JianGongYun.TRTC.Windows +{ + /// + /// SettingWindow.xaml 的交互逻辑 + /// + public partial class SettingWindow : MetroWindow + { + /// + /// 单例 + /// + private static SettingWindow _Instance; + public static SettingWindow GetInstance() + { + if (_Instance == null) + { + _Instance = new SettingWindow(); + } + return _Instance; + } + + protected override void OnClosed(EventArgs e) + { + _Instance = null; + if (micTesting)//关闭麦克风测试 + { + LiveClassroom.lTXDeviceManager.stopMicDeviceTest(); + } + base.OnClosed(e); + } + ViewModels.SettingWindowViewModel mode = ViewModels.SettingWindowViewModel.GetInstance(); + private SettingWindow() + { + InitializeComponent(); + BorderBrush = new SolidColorBrush(color: Color.FromRgb(42, 43, 48));//窗口标题背景颜色 + this.DataContext = mode; + } + + bool micTesting = false; + private void TestMic(object sender, RoutedEventArgs e) + { + if (micTesting) + { + LiveClassroom.lTXDeviceManager.stopMicDeviceTest(); + this.Dispatcher.Invoke(new Action(() => + { + this.MicTestBtn.Content = "检测麦克风"; + this.MicTestBtn.Type = AduSkin.Controls.FlatButtonSkinEnum.info; + micTesting = false; + })); + } + else + { + if (LiveClassroom.lTXDeviceManager.startMicDeviceTest(1000) == 0) + { + this.Dispatcher.Invoke(new Action(() => + { + this.MicTestBtn.Content = "停止检测"; + this.MicTestBtn.Type = AduSkin.Controls.FlatButtonSkinEnum.error; + micTesting = true; + })); + } + else + { + AduMessageBox.Show("测试失败", "提醒"); + } + } + } + + protected override void OnActivated(EventArgs e) + { + base.OnActivated(e); + //滑动条颜色前台设置无效,手动调用才行 + var sliderColor = (SolidColorBrush)(new BrushConverter().ConvertFrom("#3e7fff")); + Slider1.DecreaseColor = sliderColor; + Slider2.DecreaseColor = sliderColor; + } + } +} diff --git a/JianGongYun/Views/TeachingInfo.xaml b/JianGongYun/Views/TeachingInfo.xaml index 586c7fc..b638fb9 100644 --- a/JianGongYun/Views/TeachingInfo.xaml +++ b/JianGongYun/Views/TeachingInfo.xaml @@ -21,40 +21,45 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/JianGongYun/Views/TeachingInfo.xaml.cs b/JianGongYun/Views/TeachingInfo.xaml.cs index 32dad7d..52828ed 100644 --- a/JianGongYun/Views/TeachingInfo.xaml.cs +++ b/JianGongYun/Views/TeachingInfo.xaml.cs @@ -1,4 +1,5 @@ -using System; +using JianGongYun.TRTC; +using System; using System.Collections.Generic; using System.Text; using System.Windows; @@ -21,5 +22,17 @@ namespace JianGongYun.Views { InitializeComponent(); } + + private void LiveTest_Click(object sender, RoutedEventArgs e) + { + LiveClassroom.EnterTheClassroom(Window.GetWindow(this), + new TRTC.Models.ClassroomEntity + { + ClassHead = "课程1", + ClassSubHead = "章节1", + TeacherId = "53245345", + TeacherName = "王大锤" + }); + } } -} +} \ No newline at end of file