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;
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;
private static LiveWindowViewModel liveWinMode;
public static ClassroomEntity CurrentClassroomEntity { get; private set; }
public static TRTCCloudCallback TRTCCloudCallback = new TRTCCloudCallback();
///
/// 摄像头帧
///
public static Mat MainFrame = new Mat();
///
/// 屏幕帧
///
public static Mat SubFrame = new Mat();
///
/// 背景帧
///
public static Mat BackgroundFrame = null;
//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();
liveWinMode = CurrentLiveWindow.DataContext as LiveWindowViewModel;
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 = 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(null);
}
else
{
lTRTCCloud.stopSystemAudioLoopback();
}
//设备完结
//liveWinMode.LoadAllScreen();
_RecoderDir = $"{classroomEntity.ClassHead}_{classroomEntity.ClassSubHead}";
lTRTCCloud.addCallback(TRTCCloudCallback);//注册回调
callerWindow.Hide();//隐藏调用窗口
CurrentLiveWindow.Show();
}
private static string _RecoderDir;
///
/// 录制内容的具体路径
///
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();
///
/// 视频画面
///
public static Dictionary VideoViews = new Dictionary();
///
/// 启动摄像头
///
/// 视频容器
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)
{
//Stopwatch sw = new Stopwatch();
//VideoRecordTask(view, TRTCVideoStreamType.TRTCVideoStreamTypeBig);
view.OnRenderVideoFrameHandler += (data, w, h) =>
{
//sw.Restart();
lock (MainFrame)
{
MainFrame.Create(h, w, MatType.CV_8UC4);
Marshal.Copy(data, 0, MainFrame.Data, data.Length);
}
//sw.Stop();
//Debug.Print("main" + sw.ElapsedMilliseconds.ToString());
};
}
return view;
}
return null;
}
///
/// 停止摄像头
///
///
public static void StopVideoMain(Panel parent)
{
if (liveWinMode.CameraRunning)
{
liveWinMode.CameraRunning = false;
lTRTCCloud.stopLocalPreview();
RemoveCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeBig, true);
}
}
///
/// 启动屏幕分享
///
///
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)
{
Stopwatch sw = new Stopwatch();
//VideoRecordTask(view, TRTCVideoStreamType.TRTCVideoStreamTypeSub);
view.OnRenderVideoFrameHandler += (data, w, h) =>
{
sw.Restart();
lock (SubFrame)
{
SubFrame.Create(h, w, MatType.CV_8UC4);
Marshal.Copy(data, 0, SubFrame.Data, data.Length);
}
sw.Stop();
Debug.Print("sub" + sw.ElapsedMilliseconds.ToString());
};
}
return view;
}
return null;
}
///
/// 停止屏幕分享
///
///
public static void StopVideoSub(Panel parent)
{
if (liveWinMode.ScreenRunning)
{
liveWinMode.ScreenRunning = false;
lTRTCCloud.stopScreenCapture();
RemoveCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true);
}
}
///
/// 选择要分享的屏幕
///
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 StartMic()
{
if (!liveWinMode.MicRunning)
{
liveWinMode.MicRunning = true;
lTRTCCloud.startLocalAudio(settingWindowViewModel.LiveAudioLevel);
if (liveWinMode.IsLive && !liveWinMode.AudioRecordRunning)
{
liveWinMode.AudioRecordRunning = true;
var time = Util.TimeStr();
var pars = new TRTCAudioRecordingParams { filePath = Path.Combine(RecoderDir, $"{time}audio.wav") };
var res = lTRTCCloud.startAudioRecording(ref pars);
Console.WriteLine(res);
}
}
}
///
/// 关闭麦克风
///
public static void StopMic()
{
if (liveWinMode.MicRunning)
{
liveWinMode.MicRunning = false;
if (liveWinMode.AudioRecordRunning)
{
liveWinMode.AudioRecordRunning = false;
lTRTCCloud.stopAudioRecording();
}
lTRTCCloud.stopLocalAudio();
}
}
///
/// 设置麦克风静音
///
public static void SetMicMute(bool? mute = true)
{
if (liveWinMode.MicRunning)
{
liveWinMode.MicMute = mute.HasValue ? mute.Value : !liveWinMode.MicMute;
lTRTCCloud.muteLocalAudio(liveWinMode.MicMute);
}
}
public static void VideoRecordTask(ref Action onEnd)
{
var end = false;
onEnd = () =>
{
end = true;
BackgroundFrame?.Dispose();
BackgroundFrame = null;
};
var resolution = settingWindowViewModel.MainEncParams.videoResolution.ToString().Split('_');//屏幕分辨率
var fps = settingWindowViewModel.LiveFps;
BackgroundFrame = new Mat(int.Parse(resolution[1]), int.Parse(resolution[2]), MatType.CV_8UC3, Scalar.FromRgb(0x20, 0x20, 0x20));
//Stopwatch sw1 = new Stopwatch();
//Stopwatch sw2 = new Stopwatch();
//ConcurrentQueue bitmaps = new ConcurrentQueue();
//view.OnRenderVideoFrameHandler += (b) =>
//{
// sw1.Restart();
// bitmaps.Enqueue(b.ToMat());
// sw1.Stop();
// //Debug.Print("a" + sw1.Elapsed.TotalMilliseconds.ToString());
//};
////view.OnRenderVideoFrameHandler1 += (a, b, c) =>
////{
//// var bb = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(a[0]) * a.Length);
//// System.Runtime.InteropServices.Marshal.Copy(a, 0, bb, a.Length);
//// var mat = new Mat(c, b, MatType.CV_32SC4, bb);
//// mat.SaveImage("1.bmp");
////};
////view.OnViewRemove += () =>
////{
//// end = true;
////};
//var task = Task.Factory.StartNew(() =>
//{
// Console.WriteLine($"VideoRecordTask Start {streamType}");
// var _recoderDir = RecoderDir;
// var imgTemp = Path.Combine(_recoderDir, $"{streamType}.png");
// var videoFile = Path.Combine(_recoderDir, $"{Util.TimeStr()}_{streamType}.avi");
// VideoWriter vw = new VideoWriter();
// bool videoWriterInit = false;
// var frameCount = 0;
// Timer timer = new Timer((a) =>
// {
// //Console.WriteLine($"{streamType} fps {frameCount}");
// Debug.Print($"{streamType} fps {frameCount}");
// Interlocked.Exchange(ref frameCount, 0);
// }, null, 0, 1000);
// while (!end || bitmaps.Count > 0)
// {
// if (bitmaps.Count == 0)
// {
// Thread.Sleep(100);
// continue;
// }
// if (bitmaps.TryDequeue(out var mat))
// {
// sw2.Restart();
// //mat.SaveImage(imgTemp);
// //mat.Dispose();
// //mat = Cv2.ImRead(imgTemp, ImreadModes.AnyColor);
// //Cv2.ImShow(streamType.ToString(), mat);
// //Cv2.WaitKey(1);
// if (!videoWriterInit)
// {
// vw.Open(videoFile, FourCC.H264, settingWindowViewModel.LiveFps, mat.Size());
// videoWriterInit = true;
// }
// vw.Write(mat);
// mat.Dispose();
// Interlocked.Increment(ref frameCount);
// //Debug.Print("b"+sw2.Elapsed.TotalMilliseconds.ToString());
// }
// else
// {
// Thread.Sleep(100);
// }
// }
// vw?.Release();
// vw?.Dispose();
// if (File.Exists(imgTemp))
// {
// File.Delete(imgTemp);
// }
// timer.Dispose();
// Console.WriteLine($"VideoRecordTask End {streamType}");
//}, TaskCreationOptions.LongRunning);
}
///
/// 添加自定义渲染 View 并绑定渲染回调
///
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;
}
///
/// 获取屏幕列表需要暂停屏幕分享渲染
///
///
public static void WillGetScreens(Action action)
{
if (liveWinMode.ScreenRunning && VideoViews.TryGetValue($"{CurrentClassroomEntity.TeacherId}_{TRTCVideoStreamType.TRTCVideoStreamTypeSub}", out var view))
{
view.SetPause(true);
action.Invoke();
view.SetPause(false);
}
else
{
action.Invoke();
}
}
///
/// 移除自定义渲染 View 并解绑渲染回调
///
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);
}
}
///
/// 重启音频录制
///
public static void RestartAudio() { }
///
/// 重设主视频(摄像头)
///
public static void ResetVideoMain() { }
///
/// 重设副视频(录屏)
///
public static void ResetVideoSub() { }
private static void CurrentLiveWindow_Closed(object sender, EventArgs e)
{
CurrentLiveWindow = null;
CurrentClassroomEntity = null;
CallerWindow.Show();//还原调用者窗口
lTXDeviceManager.Dispose();
lTXDeviceManager = null;
lTRTCCloud.exitRoom();
lTRTCCloud.removeCallback(TRTCCloudCallback);//注册回调
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));
}
}
}