JianGongYun/JianGongYun/TRTC/LiveClassroom.cs

514 lines
21 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;
namespace JianGongYun.TRTC
{
/// <summary>
/// 直播调用对象全放这里
/// </summary>
public static class LiveClassroom
{
const uint SDKAppID = 1400463444;
const string SDKAppKEY = "6ee2586282eb8ab5bff3f917b44500c4ffd9bbd3d820258b1fa8cdd470cfd1ee";
/// <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实例
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;
/// <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)
{
//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;
}
/// <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);
}
}
/// <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)
{
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;
}
/// <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);
}
}
/// <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);
}
/// <summary>
/// 启动麦克风
/// </summary>
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);
}
}
}
/// <summary>
/// 关闭麦克风
/// </summary>
public static void StopMic()
{
if (liveWinMode.MicRunning)
{
liveWinMode.MicRunning = false;
if (liveWinMode.AudioRecordRunning)
{
liveWinMode.AudioRecordRunning = false;
lTRTCCloud.stopAudioRecording();
}
lTRTCCloud.stopLocalAudio();
}
}
/// <summary>
/// 设置麦克风静音
/// </summary>
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<Mat> bitmaps = new ConcurrentQueue<Mat>();
//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);
}
/// <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() { }
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;
}
/// <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));
}
}
}