367 lines
14 KiB
C#
367 lines
14 KiB
C#
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;
|
||
|
||
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();
|
||
|
||
public static ConcurrentBag<Task> SaveFileTask = new ConcurrentBag<Task>();
|
||
//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();
|
||
}
|
||
var encParams = settingWindowViewModel.EncParams;
|
||
var qosParams = settingWindowViewModel.QosParams;
|
||
var renderParams = settingWindowViewModel.RenderParams;
|
||
lTRTCCloud.setVideoEncoderParam(ref encParams);
|
||
lTRTCCloud.setNetworkQosParam(ref qosParams);
|
||
lTRTCCloud.setLocalRenderParams(ref renderParams);
|
||
//设备完结
|
||
|
||
//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>
|
||
private static Dictionary<string, TXLiteAVVideoView> VideoViews = new Dictionary<string, TXLiteAVVideoView>();
|
||
/// <summary>
|
||
/// 启动摄像头
|
||
/// </summary>
|
||
/// <param name="parent">视频容器</param>
|
||
public static TXLiteAVVideoView StartVideoMain(Panel parent)
|
||
{
|
||
if (!liveWinMode.CameraRunning)
|
||
{
|
||
lTRTCCloud.startLocalPreview(IntPtr.Zero);
|
||
liveWinMode.CameraRunning = true;
|
||
return AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeBig, true);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
|
||
public static void ResizeVideoMain(Panel parent)
|
||
{
|
||
if (liveWinMode.CameraRunning)
|
||
{
|
||
StopVideoMain(parent);
|
||
StartVideoMain(parent);
|
||
}
|
||
}
|
||
|
||
/// <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.EncParams);
|
||
return AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true);
|
||
}
|
||
return null;
|
||
}
|
||
public static void ResizeVideoSub(Panel parent)
|
||
{
|
||
if (liveWinMode.ScreenRunning)
|
||
{
|
||
RemoveCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true);
|
||
AddCustomVideoView(parent, CurrentClassroomEntity.TeacherId, TRTCVideoStreamType.TRTCVideoStreamTypeSub, true);
|
||
}
|
||
}
|
||
/// <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)
|
||
{
|
||
var pars = new TRTCAudioRecordingParams { filePath = "" };
|
||
lTRTCCloud.startAudioRecording(ref pars);
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 关闭麦克风
|
||
/// </summary>
|
||
public static void StopMic()
|
||
{
|
||
if (liveWinMode.MicRunning)
|
||
{
|
||
liveWinMode.MicRunning = false;
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <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.RenderParams.fillMode : TRTCVideoFillMode.TRTCVideoFillMode_Fit);
|
||
videoView.Width = parent.ActualWidth;
|
||
videoView.Height = parent.ActualHeight;
|
||
parent.Dispatcher.Invoke(new Action(() =>
|
||
{
|
||
parent.Children.Add(videoView);
|
||
}));
|
||
string key = string.Format("{0}_{1}", userId, streamType);
|
||
VideoViews.Add(key, videoView);
|
||
return videoView;
|
||
}
|
||
|
||
/// <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 ResetVideoMainQos() { }
|
||
/// <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);
|
||
// 统一转换为UTF8,SDK内部是用UTF8编码。
|
||
return api.GenSig(Util.UTF16To8(userId));
|
||
}
|
||
}
|
||
}
|