using ManageLiteAV; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.IO; using System.Threading; using System.Threading.Tasks; using Vinno.FIS.TRTCClient.Common.Log; using Vinno.FIS.TRTCClient.Common.Models; using static Vinno.FIS.TRTCClient.TRTCLivePusher; namespace Vinno.FIS.TRTCClient { internal class TRTCChatRoom : ITRTCCloudCallback { private const int JoinAndLeaveRoomTimeout = 8000; private readonly RemoteVideoRenderCallback _remoteVideoRenderCallback; private readonly AutoResetEvent _joinRoomWiator = new AutoResetEvent(false); private readonly AutoResetEvent _leafRoomWiator = new AutoResetEvent(false); private ITXDeviceManager _deviceManager; private TRTCVideoFrame _videoFrame; private ITRTCCloud _tRTCCloud; private bool _isOldServerMode; private uint _roomId; private bool _chatUserEnterRoomSuccess = false; private uint _connectTerminalRoomId; private uint _connectTerminalCameraRoomId; private int _reconnectTryCount; private string _connectTerminalId; private string _connectTerminalCameralId; private string _terminalId; private string _cameraId; public event EventHandler RemoteUserLeaveRoomArrived; public event EventHandler TryToReconnect; /// /// Remote device video frame arrived /// public event EventHandler RemoteVideoFrameArrived; public event EventHandler OnTRTCEnterRoomError; public TRTCChatRoom(string logPath) { if (!Directory.Exists(logPath)) { Directory.CreateDirectory(logPath); } _tRTCCloud = ITRTCCloud.getTRTCShareInstance(); _tRTCCloud.setLogCompressEnabled(false); _tRTCCloud.setLogLevel(TRTCLogLevel.TRTCLogLevelError); _tRTCCloud.setLogDirPath(logPath); _remoteVideoRenderCallback = new RemoteVideoRenderCallback(OnRemoteVideoFrameArrived); _tRTCCloud.addCallback(this); _tRTCCloud.setLocalVideoRenderCallback(TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32, TRTCVideoBufferType.TRTCVideoBufferType_Buffer, null); } public void Mute(bool isMute) { _tRTCCloud?.muteLocalAudio(isMute); } /// /// Enter chat room /// /// /// /// chat or terminal /// check audio or video chat public void Enter(TRTCRoomInfo roomInfo) { Logger.WriteLineInfo($"RtcChatRoom {roomInfo.RoomId} Enter begin"); _roomId = roomInfo.RoomId; _terminalId = roomInfo.TerminalId; _cameraId = roomInfo.CameraId; _isOldServerMode = roomInfo.IsOldServerMode; Logger.WriteLineInfo($"_tRTCCloud version:{_tRTCCloud.getSDKVersion()}"); _deviceManager = _tRTCCloud.getDeviceManager(); var micDeviceId = GetCorrectDeviceId(roomInfo.MicDeviceId, TXMediaDeviceType.TXMediaDeviceTypeMic); if (string.IsNullOrEmpty(micDeviceId)) { Logger.WriteLineError($"Can not get correct mic device Id:{roomInfo.MicDeviceId}"); } var speakerDeviceId = GetCorrectDeviceId(roomInfo.SpeakerDeviceId, TXMediaDeviceType.TXMediaDeviceTypeSpeaker); if (string.IsNullOrEmpty(speakerDeviceId)) { Logger.WriteLineError($"Can not get correct video device Id:{roomInfo.SpeakerDeviceId}"); } _deviceManager.setCurrentDevice(TXMediaDeviceType.TXMediaDeviceTypeMic, micDeviceId); _deviceManager.setCurrentDevice(TXMediaDeviceType.TXMediaDeviceTypeSpeaker, speakerDeviceId); var videoParam = new TRTCVideoEncParam { videoResolution = TRTCVideoResolution.TRTCVideoResolution_640_480, videoFps = 15, videoBitrate = 1000, resMode = TRTCVideoResolutionMode.TRTCVideoResolutionModeLandscape, enableAdjustRes = false }; _tRTCCloud.setVideoEncoderParam(ref videoParam); var qosParams = new TRTCNetworkQosParam { preference = TRTCVideoQosPreference.TRTCVideoQosPreferenceClear, controlMode = TRTCQosControlMode.TRTCQosControlModeServer }; _tRTCCloud.setNetworkQosParam(ref qosParams); TRTCParams trtcParams = new TRTCParams { sdkAppId = roomInfo.AppId, roomId = roomInfo.RoomId, userId = roomInfo.UserId, userSig = roomInfo.UserSig, // 如果您有进房权限保护的需求,则参考文档{https://cloud.tencent.com/document/product/647/32240}完成该功能。 // 在有权限进房的用户中的下面字段中添加在服务器获取到的privateMapKey。 privateMapKey = "", businessInfo = "", role = TRTCRoleType.TRTCRoleAnchor }; //trtcParams.role = // 若您的项目有纯音频的旁路直播需求,请配置参数。 // 配置该参数后,音频达到服务器,即开始自动旁路; // 否则无此参数,旁路在收到第一个视频帧之前,会将收到的音频包丢弃。 //if (DataManager.GetInstance().pureAudioStyle) // trtcParams.businessInfo = "{\"Str_uc_params\":{\"pure_audio_push_mod\": 1}}"; //else // trtcParams.businessInfo = ""; _tRTCCloud.enterRoom(ref trtcParams, TRTCAppScene.TRTCAppSceneVideoCall); Logger.WriteLineInfo($"RtcChatRoom JoinRoom exectuted"); _tRTCCloud.enableCustomVideoCapture(TRTCVideoStreamType.TRTCVideoStreamTypeBig, true); _tRTCCloud.startLocalAudio(TRTCAudioQuality.TRTCAudioQualityDefault); Task.Run(() => { if (!_joinRoomWiator.WaitOne(JoinAndLeaveRoomTimeout)) { Logger.WriteLineWarn("RtcChatRoom Joint Room timeout"); } }); Logger.WriteLineInfo($"RtcChatRoom {_roomId} Enter end"); } internal void UpdateCameraId(string cameraId) { _cameraId = cameraId; } internal void AdjustMicVolume(int value) { _tRTCCloud?.setAudioCaptureVolume(value); } /// /// 发送要推流的数据 /// /// /// /// public void SendData(uint width, uint height, byte[] frameBuffer) { var len = width * height * 3 / 2; if (_videoFrame == null) { _videoFrame = new TRTCVideoFrame { videoFormat = TRTCVideoPixelFormat.TRTCVideoPixelFormat_I420, bufferType = TRTCVideoBufferType.TRTCVideoBufferType_Buffer, data = new byte[len], width = width, height = height, length = len, timestamp = 0 }; } _videoFrame.data = frameBuffer; _tRTCCloud?.sendCustomVideoData(TRTCVideoStreamType.TRTCVideoStreamTypeBig, _videoFrame); } /// /// Exit chat room /// public void Exit() { try { if (_tRTCCloud == null) { return; } Logger.WriteLineInfo($"RtcChatRoom {_roomId} Exit begin"); _tRTCCloud.stopAllRemoteView(); _tRTCCloud.stopLocalPreview(); _tRTCCloud.stopLocalAudio(); _tRTCCloud.muteLocalAudio(true); _tRTCCloud.muteLocalVideo(TRTCVideoStreamType.TRTCVideoStreamTypeBig, true); _roomId = 0; _connectTerminalRoomId = 0; _connectTerminalId = string.Empty; _connectTerminalCameralId = string.Empty; _tRTCCloud.exitRoom(); if (_chatUserEnterRoomSuccess && !_leafRoomWiator.WaitOne(JoinAndLeaveRoomTimeout)) { Logger.WriteLineWarn("RtcChatRoom exit Room timeout"); } _chatUserEnterRoomSuccess = false; _tRTCCloud.removeCallback(this); _tRTCCloud.setLogCallback(null); _tRTCCloud = null; ITRTCCloud.destroyTRTCShareInstance(); Logger.WriteLineInfo($"RtcChatRoom {_roomId} Exit and destroy tRTCCloud end "); } catch (Exception ex) { Logger.WriteLineError($"TRTCChatRoom Exit Error:{ex}"); } } private static uint ParseRoomId(string roomId) { Guid value = new Guid(roomId); byte[] b = value.ToByteArray(); //int bint = BitConverter.ToInt32(b, 0); //var bint1 = BitConverter.ToInt32(b, 4); //var bint2 = BitConverter.ToInt32(b, 8); var bint3 = Math.Abs(BitConverter.ToInt32(b, 12)); Logger.WriteLineInfo($"ParseRoomId {roomId} to {bint3}"); return (uint)bint3; } private void OnRemoteVideoFrameArrived(TRTCVideoFrame data, string userId) { if (_isOldServerMode || (_terminalId != userId && _cameraId != userId))//超声机画面过滤,只拉取远端用户画面 { RemoteVideoFrameArrived?.Invoke(this, new VideoFrameArrivedArgs(data, userId)); } } public void onError(TXLiteAVError errCode, string errMsg, IntPtr arg) { Logger.WriteLineError($"onError errCode:{errCode}, msg:{errMsg}, arg:{arg}"); } public void onWarning(TXLiteAVWarning warningCode, string warningMsg, IntPtr arg) { Logger.WriteLineWarn($"onWarning errCode: {warningCode}, msg:{warningMsg}, arg:{arg}"); } public void onEnterRoom(int result) { Logger.WriteLineInfo($"onEnterRoom result: {result}"); _joinRoomWiator.Set(); if (result >= 0) { _chatUserEnterRoomSuccess = true; } else { OnTRTCEnterRoomError?.Invoke(this, EventArgs.Empty); } } public void onExitRoom(int reason) { Logger.WriteLineInfo($"onExitRoom reason: {reason}"); _leafRoomWiator.Set(); } public void onSwitchRole(TXLiteAVError errCode, string errMsg) { Logger.WriteLineInfo($"onSwitchRole errCode: {errCode.ToString()}, errMsg: {errMsg}"); } public void onConnectOtherRoom(string userId, TXLiteAVError errCode, string errMsg) { Logger.WriteLineInfo($"onConnectOtherRoom userId :{userId}, errCode: {errCode.ToString()}, errMsg: {errMsg}"); if (errCode != TXLiteAVError.ERR_NULL && _reconnectTryCount <= 3) { ReconnectOtherRoom("onConnectOtherRoom failed", userId); } } public void onDisconnectOtherRoom(TXLiteAVError errCode, string errMsg) { Logger.WriteLineInfo($"onDisconnectOtherRoom errCode: {errCode}, errMsg: {errMsg}"); } public void onRemoteUserEnterRoom(string userId) { } public void onRemoteUserLeaveRoom(string userId, int reason) { Logger.WriteLineInfo($"Remote User Leave Room,userId:{userId},reason:{reason}"); RemoteUserLeaveRoomArrived?.Invoke(this, new RemoteUserLeaveRoomArgs(userId, reason)); } public void onUserVideoAvailable(string userId, bool available) { if (available) { // 远端主流自定义渲染 View 动态绑定和监听 SDK 渲染回调 _tRTCCloud.startRemoteView(userId, TRTCVideoStreamType.TRTCVideoStreamTypeBig, IntPtr.Zero); _tRTCCloud.setRemoteVideoRenderCallback(userId, TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32, TRTCVideoBufferType.TRTCVideoBufferType_Buffer, _remoteVideoRenderCallback); } else { // 远端主流自定义渲染 View 移除绑定 _tRTCCloud.stopRemoteView(userId, TRTCVideoStreamType.TRTCVideoStreamTypeBig); _tRTCCloud.setRemoteVideoRenderCallback(userId, TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32, TRTCVideoBufferType.TRTCVideoBufferType_Buffer, null); if (!string.IsNullOrEmpty(_connectTerminalId) && _connectTerminalId == userId && _reconnectTryCount <= 3) { Task.Run(() => { ReconnectOtherRoom("onUserVideoAvailable false", userId); }); } else if (!string.IsNullOrEmpty(_connectTerminalCameralId) && _connectTerminalCameralId == userId && _reconnectTryCount <= 3) { Task.Run(() => { ReconnectOtherRoom("onUserVideoAvailable false", userId); }); } } Logger.WriteLineInfo($"onUserVideoAvailable userId: {userId}, available: {available}"); } private async void ReconnectOtherRoom(string reason, string userId) { await Task.Delay(1000); if (_chatUserEnterRoomSuccess) { if (userId == _connectTerminalId) { Logger.WriteLineInfo($"Reason:{reason}, OnReconnect Other Room userId: {_connectTerminalId}, roomId {_connectTerminalRoomId}"); ConnectTerminalRoom(_connectTerminalRoomId, _connectTerminalId); _reconnectTryCount++; } else if (userId == _connectTerminalCameralId) { Logger.WriteLineInfo($"Reason:{reason}, OnReconnect Other Room userId: {_connectTerminalCameralId}, roomId {_connectTerminalCameraRoomId}"); ConnectTerminalCameraRoom(_connectTerminalCameraRoomId, _connectTerminalCameralId); _reconnectTryCount++; } } } public void onUserSubStreamAvailable(string userId, bool available) { } public void onUserAudioAvailable(string userId, bool available) { Logger.WriteLineInfo($"onUserAudioAvailable userId: {userId}, available: {available}"); } public void onFirstVideoFrame(string userId, TRTCVideoStreamType streamType, int width, int height) { Logger.WriteLineInfo($"onFirstVideoFrame userId: {userId}, streamType: {streamType.ToString()}, width:{width}, height:{height}"); } public void onFirstAudioFrame(string userId) { Logger.WriteLineInfo($"onFirstAudioFrame userId: {userId}"); } public void onSendFirstLocalVideoFrame(TRTCVideoStreamType streamType) { Logger.WriteLineInfo($"onSendFirstLocalVideoFrame successed"); } public void onSendFirstLocalAudioFrame() { } public void onUserEnter(string userId) { } public void onUserExit(string userId, int reason) { } public void onNetworkQuality(TRTCQualityInfo localQuality, TRTCQualityInfo[] remoteQuality, uint remoteQualityCount) { } public void onStatistics(TRTCStatistics statis) { } public void onConnectionLost() { Logger.WriteLineInfo($"onConnectionLost"); } public void onTryToReconnect() { Logger.WriteLineInfo($"onTryToReconnect"); TryToReconnect?.Invoke(this, EventArgs.Empty); } public void onConnectionRecovery() { Logger.WriteLineInfo($"onConnectionRecovery"); } public void onSpeedTest(TRTCSpeedTestResult currentResult, uint finishedCount, uint totalCount) { } public void onCameraDidReady() { Logger.WriteLineInfo($"onCameraDidReady"); } public void onMicDidReady() { Logger.WriteLineInfo($"onMicDidReady"); } public void onUserVoiceVolume(TRTCVolumeInfo[] userVolumes, uint userVolumesCount, uint totalVolume) { } public void onDeviceChange(string deviceId, TXMediaDeviceType type, TRTCDeviceState state) { } public void onTestMicVolume(uint volume) { } public void onTestSpeakerVolume(uint volume) { } public void onRecvCustomCmdMsg(string userId, int cmdID, uint seq, byte[] msg, uint msgSize) { } public void onMissCustomCmdMsg(string userId, int cmdId, int errCode, int missed) { } public void onRecvSEIMsg(string userId, byte[] message, uint msgSize) { } public void onStartPublishCDNStream(int errCode, string errMsg) { } public void onStopPublishCDNStream(int errCode, string errMsg) { } public void onSetMixTranscodingConfig(int errCode, string errMsg) { } public void onAudioEffectFinished(int effectId, int code) { } public void onScreenCaptureCovered() { } public void onScreenCaptureStarted() { } public void onScreenCapturePaused(int reason) { } public void onScreenCaptureResumed(int reason) { } public void onScreenCaptureStoped(int reason) { } public void onPlayBGMBegin(TXLiteAVError errCode) { } public void onPlayBGMProgress(uint progressMS, uint durationMS) { } public void onPlayBGMComplete(TXLiteAVError errCode) { } /// /// Connect tarminal room /// /// /// internal void ConnectTerminalRoom(uint roomId, string terminalId) { //_tRTCCloud.connectOtherRoom("{\"roomId\":\"1767270833\",\"userId\":\"" + accountId + "\"}"); // Thread.Sleep(200); dynamic jsonObj = new JObject(); jsonObj["roomId"] = roomId; jsonObj["userId"] = terminalId; _connectTerminalRoomId = roomId; _reconnectTryCount = 0; _connectTerminalId = terminalId; var jsonData = JsonConvert.SerializeObject(jsonObj); Logger.WriteLineInfo($"TRTC ConnectRoom Room id {roomId} -Terminal id: {terminalId}"); _tRTCCloud.connectOtherRoom(jsonData); } /// /// Connect tarminal room /// /// /// internal void ConnectTerminalCameraRoom(uint roomId, string cameraId) { //_tRTCCloud.connectOtherRoom("{\"roomId\":\"1767270833\",\"userId\":\"" + accountId + "\"}"); // Thread.Sleep(200); dynamic jsonObj = new JObject(); jsonObj["roomId"] = roomId; jsonObj["userId"] = cameraId; _connectTerminalCameralId = cameraId; _connectTerminalCameraRoomId = roomId; var jsonData = JsonConvert.SerializeObject(jsonObj); Logger.WriteLineInfo($"TRTC ConnectRoom Room id {roomId} -Terminal Camera id: {cameraId}"); _tRTCCloud.connectOtherRoom(jsonData); } public void onStartPublishing(int errCode, string errMsg) { Logger.WriteLineInfo($"onStartPublishing errCode: {errCode}, errMsg: {errMsg}"); } public void onStopPublishing(int errCode, string errMsg) { Logger.WriteLineInfo($"onStopPublishing errCode: {errCode}, errMsg: {errMsg}"); } public void onSwitchRoom(TXLiteAVError errCode, string errMsg) { Logger.WriteLineInfo($"onSwitchRoom: {errCode}, errMsg: {errMsg}"); } public void onAudioDeviceCaptureVolumeChanged(uint volume, bool muted) { Logger.WriteLineInfo($"onAudioDeviceCaptureVolumeChanged: {volume}, errMsg: {muted}"); } public void onAudioDevicePlayoutVolumeChanged(uint volume, bool muted) { Logger.WriteLineInfo($"onAudioDevicePlayoutVolumeChanged: {volume}, errMsg: {muted}"); } private string GetCorrectDeviceId(string deviceId, TXMediaDeviceType deviceType) { if (string.IsNullOrEmpty(deviceId)) { return string.Empty; } var deviceList = _deviceManager.getDevicesList(deviceType); try { var count = deviceList.getCount(); for (uint i = 0; i < count; i++) { var id = deviceList.getDevicePID(i); if (deviceId.Contains(id) || id.Contains(deviceId)) { return id; } } } finally { deviceList.release(); } return string.Empty; } internal void SwitchMic(string micId) { if (_tRTCCloud != null) { _tRTCCloud.stopLocalAudio(); var correctMicId = GetCorrectId(HardwareType.Mic, micId); if (!string.IsNullOrEmpty(correctMicId)) { var deviceManager = _tRTCCloud.getDeviceManager(); deviceManager.setCurrentDevice(TXMediaDeviceType.TXMediaDeviceTypeMic, correctMicId); _tRTCCloud.startLocalAudio(TRTCAudioQuality.TRTCAudioQualityDefault);//开启本地音频的采集和上行 } } } /// /// Get the Correct Id /// /// /// /// private string GetCorrectId(HardwareType type, string deviceId) { switch (type) { case HardwareType.Camera: var cameraList = _deviceManager.getDevicesList(TXMediaDeviceType.TXMediaDeviceTypeCamera); try { var count = cameraList.getCount(); for (uint i = 0; i < count; i++) { var id = cameraList.getDevicePID(i); if (deviceId.Contains(id) || id.Contains(deviceId)) { return id; } } } finally { cameraList.release(); } break; case HardwareType.Mic: var micList = _deviceManager.getDevicesList(TXMediaDeviceType.TXMediaDeviceTypeMic); try { var count = micList.getCount(); for (uint i = 0; i < count; i++) { var id = micList.getDevicePID(i); if (deviceId.Contains(id) || id.Contains(deviceId)) { return id; } } } finally { micList.release(); } break; case HardwareType.Speaker: var speakerList = _deviceManager.getDevicesList(TXMediaDeviceType.TXMediaDeviceTypeSpeaker); try { var count = speakerList.getCount(); for (uint i = 0; i < count; i++) { var id = speakerList.getDevicePID(i); if (deviceId.Contains(id) || id.Contains(deviceId)) { return id; } } } finally { speakerList.release(); } break; } return string.Empty; } public void onRemoteVideoStatusUpdated(string userId, TRTCVideoStreamType streamType, TRTCAVStatusType status, TRTCAVStatusChangeReason reason, IntPtr extrainfo) { } public void onSpeedTestResult(TRTCSpeedTestResult result) { } public void onStartPublishMediaStream(string taskId, int code, string message, IntPtr extraInfo) { } public void onUpdatePublishMediaStream(string taskId, int code, string message, IntPtr extraInfo) { } public void onStopPublishMediaStream(string taskId, int code, string message, IntPtr extraInfo) { } public void onCdnStreamStateChanged(string cdnUrl, int status, int code, string msg, IntPtr extraInfo) { } public void onLocalRecordBegin(int errCode, string storagePath) { } public void onLocalRecording(int duration, string storagePath) { } public void onLocalRecordComplete(int errCode, string storagePath) { } public void onSnapshotComplete(string userId, TRTCVideoStreamType type, byte[] data, uint length, uint width, uint height, TRTCVideoPixelFormat format) { } } }