JianGongYun/JianGongYun/TRTC/LiveClassroom.cs

796 lines
32 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
using System.Collections.Concurrent;
using System.Windows.Data;
using System.Drawing;
using System.Diagnostics;
using System.Runtime.InteropServices;
using AduSkin.Controls.Metro;
using System.Configuration;
namespace JianGongYun.TRTC
{
/// <summary>
/// 直播调用对象全放这里
/// </summary>
public static class LiveClassroom
{
//const uint SDKAppID = 1400463444;
//const string SDKAppKEY = "6ee2586282eb8ab5bff3f917b44500c4ffd9bbd3d820258b1fa8cdd470cfd1ee";
//const uint LIVEAppID = 1252883401;
//const uint LIVEBizid = 126866;
static uint SDKAppID { get { return uint.Parse(ConfigurationManager.AppSettings[nameof(SDKAppID)]); } }
static string SDKAppKEY { get { return ConfigurationManager.AppSettings[nameof(SDKAppKEY)]; } }
static uint LIVEAppID { get { return uint.Parse(ConfigurationManager.AppSettings[nameof(LIVEAppID)]); } }
static uint LIVEBizid { get { return uint.Parse(ConfigurationManager.AppSettings[nameof(LIVEBizid)]); } }
/// <summary>
/// TRTC实例
/// </summary>
public static ITRTCCloud lTRTCCloud;
/// <summary>
/// 设备管理器
/// </summary>
public static ITXDeviceManager lTXDeviceManager;
/// <summary>
/// live窗口的调用窗口
/// </summary>
public static Window CallerWindow { get; private set; }
/// <summary>
/// 直播窗口
/// </summary>
public static Window CurrentLiveWindow;
private static LiveWindowViewModel liveWinMode;
public static ClassroomEntity CurrentClassroomEntity { get; private set; }
public static TRTCCloudCallback TRTCCloudCallback = new TRTCCloudCallback();
/// <summary>
/// 摄像头帧
/// </summary>
public static Mat MainFrame = new Mat();
/// <summary>
/// 屏幕帧
/// </summary>
public static Mat SubFrame = new Mat();
/// <summary>
/// 背景帧
/// </summary>
public static Mat BackgroundFrame = null;
//public static int UserCount = 999;
/// <summary>
/// 进入直播教室
/// </summary>
/// <param name="callerWindow">调用窗口</param>
/// <param name="classroomEntity">教室数据</param>
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();
liveWinMode = CurrentLiveWindow.DataContext as LiveWindowViewModel;
CurrentLiveWindow.Closed += CurrentLiveWindow_Closed;
lTRTCCloud = ITRTCCloud.getTRTCShareInstance();//创建TRTC实例
lTRTCCloud.addCallback(TRTCCloudCallback);//注册回调
var roomPars = new TRTCParams
{
sdkAppId = SDKAppID,
userId = CurrentClassroomEntity.TeacherId,
userSig = GenTestUserSig(CurrentClassroomEntity.TeacherId),
roomId = 0,//使用strRoomId
strRoomId = CurrentClassroomEntity.TeacherId,
role = TRTCRoleType.TRTCRoleAnchor,
streamId = CurrentClassroomEntity.TeacherId,//旁路CDN流名字为教师id
userDefineRecordId = CurrentClassroomEntity.TeacherId//指定id录制教师
};
lTRTCCloud.enterRoom(ref roomPars, TRTCAppScene.TRTCAppSceneLIVE);//创建房间
//设备
//var settingViewMode = ViewModels.SettingWindowViewModel.GetInstance();
lTXDeviceManager = lTRTCCloud.getDeviceManager();
var currentMic = settingWindowViewModel.CurrentMic;//麦克风
if (!string.IsNullOrEmpty(currentMic))
{
var res = lTXDeviceManager.setCurrentDevice(TRTCDeviceType.TXMediaDeviceTypeMic, currentMic);
}
lTXDeviceManager.setCurrentDeviceVolume(TRTCDeviceType.TXMediaDeviceTypeMic, settingWindowViewModel.MicVolume);//麦克风采集音量
lTRTCCloud.setSystemAudioLoopbackVolume(settingWindowViewModel.SytemGatherVolume);//系统声音采集音量
//设备完结
//liveWinMode.LoadAllScreen();
_RecoderDir = $"{classroomEntity.ClassHead}_{classroomEntity.ClassSubHead}";
callerWindow?.Hide();//隐藏调用窗口
CurrentLiveWindow.Show();
}
private static string _RecoderDir;
/// <summary>
/// 音频录制文件
/// </summary>
public static string AudioTempPath
{
get
{
return Path.Combine(RecoderDir, $"temp_audio.aac");
}
}
/// <summary>
/// 视频录制文件
/// </summary>
public static string VideoTempPath
{
get
{
return Path.Combine(RecoderDir, $"temp_video.avi");
}
}
/// <summary>
/// 合并后的文件
/// </summary>
public static string ResultFilePath
{
get
{
return Path.Combine(RecoderDir, $"result.avi");
}
}
/// <summary>
/// 录制内容的具体路径
/// </summary>
public static string RecoderDir
{
get
{
var temp = Path.Combine(settingWindowViewModel.ScreenRecordingDir, _RecoderDir);
if (!Directory.Exists(temp))
{
Directory.CreateDirectory(temp);
}
return temp;
}
}
private static SettingWindowViewModel settingWindowViewModel = SettingWindowViewModel.GetInstance();
/// <summary>
/// 视频画面
/// </summary>
public static Dictionary<string, TXLiteAVVideoView> VideoViews = new Dictionary<string, TXLiteAVVideoView>();
/// <summary>
/// 启动摄像头
/// </summary>
/// <param name="parent">视频容器</param>
public static TXLiteAVVideoView StartVideoMain(Panel parent)
{
if (!liveWinMode.CameraRunning)
{
var encParams = settingWindowViewModel.MainEncParams;
var qosParams = settingWindowViewModel.MainQosParams;
var renderParams = settingWindowViewModel.MainRenderParams;
lTRTCCloud.setVideoEncoderParam(ref encParams);
lTRTCCloud.setNetworkQosParam(ref qosParams);
lTRTCCloud.setLocalRenderParams(ref renderParams);
lTRTCCloud.startLocalPreview(IntPtr.Zero);
liveWinMode.CameraRunning = true;
var view = AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeBig, true);
if (liveWinMode.IsLive && settingWindowViewModel.LocalRecorder)
{
view.OnRenderVideoFrameHandler += (data, w, h) =>
{
lock (MainFrame)
{
if (MainFrame.Cols != w || MainFrame.Rows != h)
{
MainFrame.Create(h, w, MatType.CV_8UC4);
}
Marshal.Copy(data, 0, MainFrame.Data, data.Length);
}
};
}
return view;
}
return null;
}
/// <summary>
/// 停止摄像头
/// </summary>
/// <param name="parent"></param>
public static void StopVideoMain(Panel parent)
{
if (liveWinMode.CameraRunning)
{
liveWinMode.CameraRunning = false;
lTRTCCloud.stopLocalPreview();
RemoveCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeBig, true);
lock (MainFrame)
{
MainFrame.Create(1, 1, MatType.CV_8UC4);
}
}
}
/// <summary>
/// 启动屏幕分享
/// </summary>
/// <param name="parent"></param>
public static TXLiteAVVideoView StartVideoSub(Panel parent)
{
if (!liveWinMode.ScreenRunning)
{
liveWinMode.ScreenRunning = true;
SelectVieoSub();
lTRTCCloud.startScreenCapture(IntPtr.Zero, TRTCVideoStreamType.TRTCVideoStreamTypeSub, settingWindowViewModel.SubEncParams);
var view = AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true);
if (liveWinMode.IsLive && settingWindowViewModel.LocalRecorder)
{
view.OnRenderVideoFrameHandler += (data, w, h) =>
{
lock (SubFrame)
{
if (SubFrame.Cols != w || SubFrame.Rows != h)
{
SubFrame.Create(h, w, MatType.CV_8UC4);
}
Marshal.Copy(data, 0, SubFrame.Data, data.Length);
}
};
}
return view;
}
return null;
}
/// <summary>
/// 停止屏幕分享
/// </summary>
/// <param name="parent"></param>
public static void StopVideoSub(Panel parent)
{
if (liveWinMode.ScreenRunning)
{
liveWinMode.ScreenRunning = false;
lTRTCCloud.stopScreenCapture();
RemoveCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true);
lock (SubFrame)
{
SubFrame.Create(1, 1, MatType.CV_8UC4);
}
}
}
/// <summary>
/// 选择要分享的屏幕
/// </summary>
public static void SelectVieoSub()
{
var current = liveWinMode.CurrentShareScreen;
var rect = new RECT();
var property = new TRTCScreenCaptureProperty();
lTRTCCloud.selectScreenCaptureTarget(ref current, ref rect, ref property);
}
//开启音频后调用
public static void StartMUX(bool stop = false)
{
if (stop)
{
lTRTCCloud.setMixTranscodingConfig(null);
return;
}
var resolution = settingWindowViewModel.SubEncParams.videoResolution.ToString().Split('_');//屏幕分辨率,本地储存分辨率以屏幕分享分辨率为基准
lTRTCCloud.setMixTranscodingConfig(new TRTCTranscodingConfig //设置混流
{
mode = TRTCTranscodingConfigMode.TRTCTranscodingConfigMode_Template_ScreenSharing,
appId = LIVEAppID,
bizId = LIVEBizid,
videoWidth = uint.Parse(resolution[1]),
videoHeight = uint.Parse(resolution[2]),
videoBitrate = settingWindowViewModel.SubEncParams.videoBitrate,
videoFramerate = settingWindowViewModel.SubEncParams.videoFps,
videoGOP = 3,
backgroundColor = 0x202020,
audioSampleRate = 48000,
audioBitrate = 128,
audioChannels = 2
});
}
/// <summary>
/// 启动麦克风
/// </summary>
public static void StartMic()
{
if (!liveWinMode.MicRunning)
{
liveWinMode.MicRunning = true;
lTRTCCloud.startLocalAudio(settingWindowViewModel.LiveAudioLevel);
if (settingWindowViewModel.LocalRecorder)
{
StartRecordAudio();
}
StartMUX();
}
}
/// <summary>
/// 录音
/// </summary>
public static void StartRecordAudio()
{
if (liveWinMode.IsLive && !liveWinMode.AudioRecordRunning)
{
liveWinMode.AudioRecordRunning = true;
var pars = new TRTCAudioRecordingParams { filePath = AudioTempPath };
lTRTCCloud.startAudioRecording(ref pars);
}
}
public static void StopRecordAudio()
{
if (liveWinMode.AudioRecordRunning)
{
liveWinMode.AudioRecordRunning = false;
lTRTCCloud.stopAudioRecording();
}
}
public static void EnableAudio()
{
if (settingWindowViewModel.AudioSource == "1")
{
//lTRTCCloud.muteLocalAudio(false);
lTXDeviceManager.setCurrentDeviceVolume(ManageLiteAV.TRTCDeviceType.TXMediaDeviceTypeMic, settingWindowViewModel.MicVolume);
lTRTCCloud.stopSystemAudioLoopback();
}
else if (settingWindowViewModel.AudioSource == "2")
{
//lTRTCCloud.muteLocalAudio(true);
lTXDeviceManager.setCurrentDeviceVolume(ManageLiteAV.TRTCDeviceType.TXMediaDeviceTypeMic, 0);
lTRTCCloud.startSystemAudioLoopback(null);
}
else if (settingWindowViewModel.AudioSource == "3")
{
//lTRTCCloud.muteLocalAudio(false);
lTXDeviceManager.setCurrentDeviceVolume(ManageLiteAV.TRTCDeviceType.TXMediaDeviceTypeMic, settingWindowViewModel.MicVolume);
lTRTCCloud.startSystemAudioLoopback(null);
}
}
/// <summary>
/// 关闭麦克风
/// </summary>
public static void StopMic()
{
if (liveWinMode.MicRunning)
{
liveWinMode.MicRunning = false;
if (settingWindowViewModel.LocalRecorder)
{
StopRecordAudio();
}
lTRTCCloud.stopLocalAudio();
}
}
/// <summary>
/// 设置麦克风静音
/// </summary>
public static void SetMicMute(bool? mute = true)
{
if (liveWinMode.MicRunning)
{
liveWinMode.MicMute = mute.HasValue ? mute.Value : !liveWinMode.MicMute;
if (liveWinMode.MicMute)
{
//lTRTCCloud.muteLocalAudio(true);
lTXDeviceManager.setCurrentDeviceVolume(ManageLiteAV.TRTCDeviceType.TXMediaDeviceTypeMic, 0);
lTRTCCloud.stopSystemAudioLoopback();
}
else
{
EnableAudio();
}
//lTRTCCloud.muteLocalAudio(liveWinMode.MicMute);
}
}
public static void VideoRecordTask(ref Action onEnd)
{
var backColor = Scalar.FromRgb(0x20, 0x20, 0x20);
var resolution = settingWindowViewModel.SubEncParams.videoResolution.ToString().Split('_');//屏幕分辨率,本地储存分辨率以屏幕分享分辨率为基准
var fps = settingWindowViewModel.LiveFps;//视频采集的fps
BackgroundFrame = new Mat(int.Parse(resolution[2]), int.Parse(resolution[1]), MatType.CV_8UC4, backColor);//合成双路视频的背景
var delay = 1000 / (int)fps;//每帧时间
var _recoderDir = RecoderDir;
//var videoFrameTemp = Path.Combine(_recoderDir, $"videoFrameTemp.bmp");
var backHeight = BackgroundFrame.Rows;//画面高度
var backWidth = BackgroundFrame.Cols;//画面宽度
var end = false;
ConcurrentQueue<Mat> mats = new ConcurrentQueue<Mat>();
int runFps = 0;//实时帧
Timer timer = new Timer((a) =>
{
//Debug.Print($"runFps {runFps}. leavingsMat {mats.Count}.");
Interlocked.Exchange(ref runFps, 0);
}, null, 0, 1000);
onEnd = () =>
{
end = true;
timer.Dispose();
};
//帧合并线程
Task.Factory.StartNew(() =>
{
Stopwatch stopwatch = new Stopwatch();//计时器
bool onlyCameraInit = false;//只有摄像头的话背景填充为backColor变量标记只需设置一次
bool noImgInit = false;//没有画面背景也填充backColor变量标记只需设置一次
var delayEqualize = 0;//每帧时间补偿在性能和其他因素影响下delay的时间不一定充足
//屏幕分享画面
var screenRoi = BackgroundFrame[new OpenCvSharp.Rect(0, 0, backWidth, backHeight)];
//摄像头小画面定位
var cameraMargin = 20;//右下角偏移量
var smallCameraSize = 150;
var smallX = backWidth - smallCameraSize - cameraMargin;
var smallY = backHeight - smallCameraSize - cameraMargin;
var smallRoi = BackgroundFrame[new OpenCvSharp.Rect(smallX, smallY, smallCameraSize, smallCameraSize)];
//摄像头大画面位置
var bigRoi = BackgroundFrame[new OpenCvSharp.Rect((backWidth - backHeight) / 2, 0, backHeight, backHeight)];
Debug.Print("开始处理帧线程");
while (!end)
{
stopwatch.Restart();
if (!liveWinMode.ScreenRunning && !liveWinMode.CameraRunning)//有画面的中途关闭画面,清空上一帧
{
if (!noImgInit)
{
noImgInit = true;
BackgroundFrame.SetTo(backColor);
}
goto Skip2;
}
else
{
if (noImgInit)
{
noImgInit = false;
}
}
if (liveWinMode.ScreenRunning)//屏幕分享中
{
if (onlyCameraInit)
{
onlyCameraInit = false;
}
lock (SubFrame)
{
if (SubFrame.Cols != BackgroundFrame.Cols || SubFrame.Rows != BackgroundFrame.Rows)
{
goto Skip1;//图像数据不正常直接跳过
}
SubFrame.CopyTo(screenRoi);//屏幕分享和背景一样大,直接覆盖上去
}
}
else
{
if (!onlyCameraInit)//双画面的中途关闭屏幕画面,清空上一帧
{
onlyCameraInit = true;
BackgroundFrame.SetTo(backColor);
}
}
Skip1:
if (liveWinMode.CameraRunning)//摄像头分享中
{
lock (MainFrame)
{
if (MainFrame.Cols <= 0 || MainFrame.Rows <= 0)
{
goto Skip2;//图像数据不正常直接跳过
}
var mainHeight = MainFrame.Rows;//摄像头画面高度
var mainWidth = MainFrame.Cols;//摄像头画面宽度
var dst = MainFrame[new OpenCvSharp.Rect((mainWidth - mainHeight) / 2, 0, mainHeight, mainHeight)];//采集大小为原图高的居中正方形,程序分辨率只实现了横屏,因此宽大于高
if (liveWinMode.ScreenRunning)//屏幕分享中摄像头150正方形大小并悬浮再右下角
{
dst = dst.Resize(new OpenCvSharp.Size(smallCameraSize, smallCameraSize), interpolation: InterpolationFlags.Area);
dst.Rectangle(new OpenCvSharp.Rect(0, 0, dst.Cols, dst.Rows), Scalar.FromRgb(239, 239, 239), 1);
dst.CopyTo(smallRoi);
}
else//只有摄像头,摄像头画面全屏正方形
{
dst = dst.Resize(new OpenCvSharp.Size(backHeight, backHeight), interpolation: InterpolationFlags.Linear);
dst.CopyTo(bigRoi);
}
dst.Dispose();
}
}
Skip2:
mats.Enqueue(BackgroundFrame.CvtColor(ColorConversionCodes.BGRA2BGR));
Interlocked.Increment(ref runFps);
stopwatch.Stop();
var sleep = delay - (int)stopwatch.ElapsedMilliseconds - 1;//每帧时间减去每帧处理时间为sleep时间//视频实际时长偏短再减1是微调
if (sleep > 0)
{
if (delayEqualize == 0)
{
Thread.Sleep(sleep);
continue;
}
else
{
delayEqualize += sleep;
if (delayEqualize > 0)
{
Thread.Sleep(delayEqualize);
delayEqualize = 0;
continue;
}
}
}
else if (sleep < 0)//如果处理时间超过了每帧时间,记录下来
{
delayEqualize += sleep;
}
//Debug.Print($"video frame run {stopwatch.ElapsedMilliseconds}. sleep{sleep}. delayEqualize {delayEqualize}. delay {delay}.");
}
BackgroundFrame?.Dispose();
BackgroundFrame = null;
Debug.Print("结束处理帧线程");
}, TaskCreationOptions.LongRunning);//新开线程
//新开线程写文件,减少帧处理时间
Task.Factory.StartNew(() =>
{
VideoWriter vw = new VideoWriter(VideoTempPath, FourCC.H264, fps, BackgroundFrame.Size());
//Stopwatch stopwatch = new Stopwatch();//计时器
Debug.Print("开始写入视频线程");
while (!end || mats.Count > 0)
{
if (mats.Count > 0)
{
if (mats.TryDequeue(out var frame))
{
//stopwatch.Restart();
vw.Write(frame);
frame.Dispose();
//stopwatch.Stop();
//Debug.Print($"video frame write {stopwatch.ElapsedMilliseconds}");
}
else
{
Thread.Sleep(delay);
}
}
else
{
Thread.Sleep(delay);
}
}
vw.Dispose();
//合并视频
var arguments = $@"-i ""{VideoTempPath}"" -i ""{AudioTempPath}"" -c:v copy -c:a copy -strict experimental -y ""{ResultFilePath}""";
Process prc = new Process { StartInfo = new ProcessStartInfo { FileName = Path.Combine(Environment.CurrentDirectory, "ffmpeg.exe"), Arguments = arguments, CreateNoWindow = true, UseShellExecute = false } };
prc.Start();
prc.WaitForExit();
CurrentLiveWindow.Dispatcher.Invoke(new Action(() =>
{
NoticeManager.NotifiactionShow.AddNotifiaction(new NotifiactionModel()
{
Title = "提醒",
Content = $"成功保存直播视频,路径:{ResultFilePath}。",
NotifiactionType = AduSkin.Controls.EnumPromptType.Success
});
var res = AduMessageBox.ShowYesNoCancel("是否删除音视频源文件?", "提醒", "删除", "保留", "浏览");
if (res == MessageBoxResult.Cancel)
{
var process = new Process
{
StartInfo = new ProcessStartInfo { FileName = "explorer.exe", Arguments = RecoderDir }
};
process.Start();
process.WaitForExit();
}
else if (res == MessageBoxResult.Yes)
{
if (File.Exists(VideoTempPath))
{
File.Delete(VideoTempPath);
}
if (File.Exists(AudioTempPath))
{
File.Delete(AudioTempPath);
}
}
CurrentLiveWindow.Close();
}));
Debug.Print("结束写入视频线程");
}, TaskCreationOptions.LongRunning);
}
/// <summary>
/// 添加自定义渲染 View 并绑定渲染回调
/// </summary>
private static TXLiteAVVideoView AddCustomVideoView(Panel parent, string userId, TRTCVideoStreamType streamType, bool local = false)
{
TXLiteAVVideoView videoView = new TXLiteAVVideoView();
videoView.RegEngine(userId, streamType, lTRTCCloud, local);
videoView.SetRenderMode(streamType == TRTCVideoStreamType.TRTCVideoStreamTypeBig ? settingWindowViewModel.MainRenderParams.fillMode : settingWindowViewModel.SubRenderParams.fillMode);
//videoView.Width = parent.ActualWidth;
videoView.SetBinding(TXLiteAVVideoView.WidthProperty, new Binding("ActualWidth") { Source = parent });
//videoView.Height = parent.ActualHeight;
videoView.SetBinding(TXLiteAVVideoView.HeightProperty, new Binding("ActualHeight") { Source = parent });
parent.Dispatcher.Invoke(new Action(() =>
{
parent.Children.Add(videoView);
}));
string key = string.Format("{0}_{1}", userId, streamType);
VideoViews.Add(key, videoView);
return videoView;
}
/// <summary>
/// 获取屏幕列表需要暂停屏幕分享渲染
/// </summary>
/// <param name="action"></param>
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();
}
}
/// <summary>
/// 移除自定义渲染 View 并解绑渲染回调
/// </summary>
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.Dispatcher.Invoke(new Action(() =>
{
parent.Children.Remove(videoView);
}));
VideoViews.Remove(key);
}
}
/// <summary>
/// 重启音频录制
/// </summary>
//public static void RestartAudio() { }
/// <summary>
/// 重设主视频(摄像头)
/// </summary>
//public static void ResetVideoMain() { }
/// <summary>
/// 重设副视频(录屏)
/// </summary>
//public static void ResetVideoSub() { }
/// <summary>
/// 停止所有实时渲染
/// </summary>
/// <param name="pause"></param>
public static void PauseAllView(bool pause)
{
foreach (var item in VideoViews)
{
item.Value.SetPause(pause);
}
}
private static void CurrentLiveWindow_Closed(object sender, EventArgs e)
{
CurrentLiveWindow = null;
CurrentClassroomEntity = null;
lTXDeviceManager.Dispose();
lTXDeviceManager = null;
lTRTCCloud.exitRoom();
lTRTCCloud.removeCallback(TRTCCloudCallback);//注册回调
ITRTCCloud.destroyTRTCShareInstance();//销毁TRTC实例
lTRTCCloud.Dispose();
lTRTCCloud = null;
//if (CallerWindow == null)
//{
// //关闭程序
// Environment.Exit(0);
// return;
//}
//CallerWindow.Close();//直接关闭
Environment.Exit(0);
}
/// <summary>
/// 计算 UserSig 签名
///
/// 函数内部使用 HMAC-SHA256 非对称加密算法,对 SDKAPPID、userId 和 EXPIRETIME 进行加密
///
/// 该方案仅适合本地跑通demo和功能调试产品真正上线发布要使用服务器获取方案避免私钥被破解。
/// </summary>
/// <remarks>
/// 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
///
/// 本文件中的代码虽然能够正确计算出 UserSig但仅适合快速调通 SDK 的基本功能,不适合线上产品,
/// 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
/// 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
///
/// 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
/// 由于破解服务器的成本要高于破解客户端 App所以服务器计算的方案能够更好地保护您的加密密钥。
///
/// 文档https://cloud.tencent.com/document/product/647/17275#GetFromServer
/// </remarks>
public static string GenTestUserSig(string userId)
{
if (SDKAppID == 0 || string.IsNullOrEmpty(SDKAppKEY)) return null;
TLSSigAPIv2 api = new TLSSigAPIv2((int)SDKAppID, SDKAppKEY);
// 统一转换为UTF8SDK内部是用UTF8编码。
return api.GenSig(Util.UTF16To8(userId));
}
}
}