using Android.App; using Android.Content; using Android.OS; using Com.Tencent.Trtc; using System; using System.Collections.Concurrent; using System.IO; using Vinno.FIS.TRTCClient.Common; using Vinno.FIS.TRTCClient.Common.Enum; using Vinno.FIS.TRTCClient.Common.FileTransfer; using Vinno.FIS.TRTCClient.Common.Log; using Vinno.FIS.TRTCClient.Common.Models; using Vinno.vCloud.FIS.CrossPlatform.Android.LiveVideo.RTC; namespace Vinno.FIS.TRTCClient.Android { public class FISTRTCClient { private static readonly ConcurrentDictionary _remoteVideoFileWriter = new ConcurrentDictionary(); private static readonly ConcurrentDictionary _remoteImageList = new ConcurrentDictionary(); private static TRTCChatRoom _trtcChatRoom; private static TRTCLivePusher _trtcPusher; private static bool _isLiveMode; private static string _localId; private static int _processId; private static string _fisFolderPath; private static DefaultLogEngine _logEngine; private static FileTransferReader _localVideoReader; private static Context _context; private static FileTransferReader _fileReaderFromCommonToTRTC; private static FileTransferWriter _fileWriterFromTRTCToCommon; private static string _logPath; private static int _localImageWidth; private static int _localImageHeight; internal static string TempFolderPath { get; private set; } public static void Init(Context context, string fisLogFolderPath, string fisFolderPath, bool isLiveMode) { try { _context = context; _isLiveMode = isLiveMode; _processId = Process.MyPid(); if (string.IsNullOrEmpty(fisFolderPath)) { _fisFolderPath = Path.Combine(Application.Context.GetExternalFilesDir(null).Path, "FIS"); } else { _fisFolderPath = fisFolderPath; } if (string.IsNullOrEmpty(fisLogFolderPath)) { _logPath = Path.Combine(_fisFolderPath, "FISLogs", "TRTCClientLogs"); } else { _logPath = Path.Combine(fisLogFolderPath, "TRTCClientLogs"); } if (!Directory.Exists(_fisFolderPath)) { Directory.CreateDirectory(_fisFolderPath); } if (!Directory.Exists(_logPath)) { Directory.CreateDirectory(_logPath); } if (_isLiveMode) { _logEngine = new DefaultLogEngine(_logPath, EnumLiveChannelCategory.Auxiliary1); Logger.RegisterEngine(_logEngine); _fileReaderFromCommonToTRTC = new FileTransferReader(FISTRTCConsts.CommonToTRTCLiveVideoMessageTransferFolder, FISTRTCConsts.DataTransferFolderForTRTCLiveVideo, _fisFolderPath, FISTRTCConsts.CommonToTRTCLiveVideoReserveMessageTransferFolder); _fileReaderFromCommonToTRTC.LogMsgThrow += OnLogMsgThrow; _fileReaderFromCommonToTRTC.DataReceived += OnDataReceived; _fileReaderFromCommonToTRTC.StartContinuousRead(); _fileWriterFromTRTCToCommon = new FileTransferWriter(FISTRTCConsts.TRTCLiveVideoToCommonMessageTransferFolder, FISTRTCConsts.DataTransferFolderForTRTCLiveVideo, _fisFolderPath, null); _fileWriterFromTRTCToCommon.LogMsgThrow += OnLogMsgThrow; } else { _logEngine = new DefaultLogEngine(_logPath, EnumLiveChannelCategory.Main); Logger.RegisterEngine(_logEngine); _fileReaderFromCommonToTRTC = new FileTransferReader(FISTRTCConsts.CommonToTRTCConsultationMessageTransferFolder, FISTRTCConsts.DataTransferFolderForTRTCConsultation, _fisFolderPath, FISTRTCConsts.CommonToTRTCConsultationReserveMessageTransferFolder); _fileReaderFromCommonToTRTC.LogMsgThrow += OnLogMsgThrow; _fileReaderFromCommonToTRTC.DataReceived += OnDataReceived; _fileReaderFromCommonToTRTC.StartContinuousRead(); _fileWriterFromTRTCToCommon = new FileTransferWriter(FISTRTCConsts.TRTCConsultationToCommonMessageTransferFolder, FISTRTCConsts.DataTransferFolderForTRTCConsultation, _fisFolderPath, null); _fileWriterFromTRTCToCommon.LogMsgThrow += OnLogMsgThrow; } Logger.WriteLineInfo($"TRTCClient Init,Current Process Id:{_processId},IsLiveMode:{_isLiveMode}"); } catch (Exception ex) { Logger.WriteLineError($"FISTRTCClient Init Error:{ex}"); } } private static void OnLogMsgThrow(object sender, LogEventArgs e) { switch (e.LogType) { case DeviceLogCategory.Error: Logger.WriteLineError(e.Msg); break; case DeviceLogCategory.Info: Logger.WriteLineInfo(e.Msg); break; case DeviceLogCategory.Warn: Logger.WriteLineWarn(e.Msg); break; case DeviceLogCategory.Verb: Logger.WriteLineDebug(e.Msg); break; } } private static void OnDataReceived(object sender, byte[] e) { try { if (e == null) { return; } var trtcMessage = TRTCMessage.FromBytes(e); Logger.WriteLineInfo($"FISTRTCClient OnDataReceived:{trtcMessage.MessageType}"); switch (trtcMessage.MessageType) { case EnumMessageType.EnterRoom: var roomInfo = TRTCRoomInfo.FromBytes(trtcMessage?.MessageData); if (roomInfo != null) { Enter(roomInfo, _context); } break; case EnumMessageType.ImageSizeChanged: var imageSizeData = TRTCImageSizeData.FromBytes(trtcMessage?.MessageData); if (imageSizeData != null) { _localImageHeight = imageSizeData.Height; _localImageWidth = imageSizeData.Width; } break; case EnumMessageType.ExitRoom: Exit(); break; case EnumMessageType.Mute: var mute = BitConverter.ToBoolean(trtcMessage?.MessageData, 0); SetMute(mute); break; case EnumMessageType.UpdateCameraId: var cameraIdMessage = TRTCStringMessage.FromBytes(trtcMessage.MessageData); if (cameraIdMessage != null) { if (!_isLiveMode) { _trtcChatRoom?.UpdateCameraId(cameraIdMessage.Value); Logger.WriteLineInfo($"TrtcChatRoom UpdateCameraId {cameraIdMessage.Value}"); } } break; } } catch (Exception ex) { Logger.WriteLineError($"FISTRTCClient OnDataReceived Error:{ex}"); } } public static void Enter(TRTCRoomInfo roomInfo, Context context) { try { Logger.WriteLineInfo("TRTCClient Start Up......"); Logger.WriteLineInfo($"RoomInfo:{roomInfo}"); _localId = roomInfo.UserId; StartLocalVideoFileReader(); if (_isLiveMode) { _trtcPusher = new TRTCLivePusher(_logPath, context); _trtcPusher.FirstFrameSend += OnFirstFrameSend; var videoResolution = GetResolution(roomInfo.OutputWidth, roomInfo.OutputHeight); _trtcPusher.EnterRoom(roomInfo.UserId, (int)roomInfo.RoomId, roomInfo.UserSig, (int)roomInfo.AppId, videoResolution, (int)roomInfo.VideoFps, (int)roomInfo.VideoBitrate, (int)roomInfo.MinVideoBitrate, roomInfo.IsMute, null); } else { _trtcChatRoom = new TRTCChatRoom(_logPath, context); _trtcChatRoom.RemoteVideoFrameArrived += OnRemoteVideoFrameReceived; _trtcChatRoom.OnTRTCEnterRoomError += OnEnterRoomError; _trtcChatRoom.RemoteUserLeaveRoomArrived += OnRemoteUserLeaveRoomArrived; _trtcChatRoom.TryToReconnect += OnTryToReconnect; var videoResolution = GetResolution(roomInfo.OutputWidth, roomInfo.OutputHeight); _trtcChatRoom.Enter(roomInfo, videoResolution); if (roomInfo.TerminalIsPushing && roomInfo.TerminalRoomId > 0) { _trtcChatRoom.ConnectRoom(roomInfo.TerminalRoomId, roomInfo.TerminalId); } } } catch (Exception ex) { Logger.WriteLineError($"FISTRTCClient Start Error:{ex}"); } } private static void OnFirstFrameSend(object sender, EventArgs e) { Logger.WriteLineInfo($"OnFirstFrameSend Invoke"); var firstFrame = new TRTCMessage(EnumMessageType.FirstFrameReceived, new byte[0]); _fileWriterFromTRTCToCommon.WriteMessageToFileContinuous(firstFrame.ToBytes()); } private static int GetResolution(int width, int height) { if (width <= 640 && height <= 480) { return TRTCCloudDef.TrtcVideoResolution640480; } else if (width <= 960 && height <= 544) { return TRTCCloudDef.TrtcVideoResolution960540; } else if (width <= 960 && height <= 720) { return TRTCCloudDef.TrtcVideoResolution960720; } else if (width <= 1280 && height <= 720) { return TRTCCloudDef.TrtcVideoResolution1280720; } else { return TRTCCloudDef.TrtcVideoResolution19201080; } } private static void StartLocalVideoFileReader() { if (_isLiveMode) { if (_localVideoReader == null) { _localVideoReader = new FileTransferReader($"{FISTRTCConsts.ImageTransferFolderForTRTCConsultationLocalVideo}_{_localId}", FISTRTCConsts.DataTransferFolderForTRTCLiveVideo, _fisFolderPath, null); _localVideoReader.LogMsgThrow += OnLogMsgThrow; _localVideoReader.DataReceived += OnLocalVideoFrameReceived; _localVideoReader.StartContinuousRead(); Logger.WriteLineInfo($"Start Local Video File Reader:{_localVideoReader.Name}"); } } else { if (_localVideoReader == null) { _localVideoReader = new FileTransferReader($"{FISTRTCConsts.ImageTransferFolderForTRTCConsultationLocalVideo}_{_localId}", FISTRTCConsts.DataTransferFolderForTRTCConsultation, _fisFolderPath, null); _localVideoReader.LogMsgThrow += OnLogMsgThrow; _localVideoReader.DataReceived += OnLocalVideoFrameReceived; _localVideoReader.StartContinuousRead(); Logger.WriteLineInfo($"Start Local Video File Reader:{_localVideoReader.Name}"); } } } private static void OnLocalVideoFrameReceived(object sender, byte[] e) { if (e == null) { return; } if (e.Length != _localImageHeight * _localImageWidth * 3 / 2) { return; } if (_isLiveMode) { _trtcPusher?.SendData(_localImageWidth, _localImageHeight, e); } else { _trtcChatRoom?.SendData(_localImageWidth, _localImageHeight, e); } } private static void StopLocalVideoFileReader() { if (_localVideoReader != null) { _localVideoReader.DataReceived -= OnLocalVideoFrameReceived; _localVideoReader.Dispose(); _localVideoReader.LogMsgThrow -= OnLogMsgThrow; _localVideoReader = null; } } public static void Exit() { try { ExitRoom(); StopFileTransfer(); } catch (Exception ex) { Logger.WriteLineError($"FISTRTCClient Stop error:{ex}"); } } private static void ExitRoom() { try { if (_trtcChatRoom != null) { _trtcChatRoom.RemoteVideoFrameArrived -= OnRemoteVideoFrameReceived; _trtcChatRoom.OnTRTCEnterRoomError -= OnEnterRoomError; _trtcChatRoom.RemoteUserLeaveRoomArrived -= OnRemoteUserLeaveRoomArrived; _trtcChatRoom.TryToReconnect -= OnTryToReconnect; _trtcChatRoom.Exit(); _trtcChatRoom = null; Logger.WriteLineInfo("Exit Room success"); } if (_trtcPusher != null) { _trtcPusher.FirstFrameSend -= OnFirstFrameSend; _trtcPusher.ExitRoom(); _trtcPusher.Dispose(); _trtcPusher = null; } } catch (Exception ex) { Logger.WriteLineError($"FISTRTCClient Exit Room error:{ex}"); } } private static void StopFileTransfer() { try { Logger.WriteLineInfo("Stop File Writers"); StopLocalVideoFileReader(); foreach (var userId in _remoteVideoFileWriter.Keys) { Logger.WriteLineInfo($"Stop File Writer Remote Video{_remoteVideoFileWriter[userId].Name}"); _remoteVideoFileWriter[userId].Dispose(); _remoteVideoFileWriter[userId].LogMsgThrow -= OnLogMsgThrow; } _remoteVideoFileWriter.Clear(); } catch (Exception ex) { Logger.WriteLineError($"FISTRTCClient Stop error:{ex}"); } } public static void Destroy() { if (_fileReaderFromCommonToTRTC != null) { _fileReaderFromCommonToTRTC.DataReceived -= OnDataReceived; _fileReaderFromCommonToTRTC.Dispose(); _fileReaderFromCommonToTRTC.LogMsgThrow -= OnLogMsgThrow; _fileReaderFromCommonToTRTC = null; } if (_fileWriterFromTRTCToCommon != null) { _fileWriterFromTRTCToCommon.Dispose(); _fileWriterFromTRTCToCommon.LogMsgThrow -= OnLogMsgThrow; _fileWriterFromTRTCToCommon = null; } } public static void SetMute(bool isMute) { if (_isLiveMode) { if (_trtcPusher != null) { _trtcPusher.SetMute(isMute); } } else { if (_trtcChatRoom != null) { _trtcChatRoom.Mute(isMute); } } } private static void OnRemoteVideoFrameReceived(object sender, TRTCVideoFrameData e) { try { if (string.IsNullOrEmpty(e.UserId)) { return; } if (!_remoteVideoFileWriter.ContainsKey(e.UserId)) { StartNewRemoteVideoFileWriter(e.UserId, e.Width, e.Height); } if (!_remoteImageList.ContainsKey(e.UserId)) { if (_remoteImageList.TryAdd(e.UserId, new TRTCRemoteImageSizeData(e.UserId, e.Width, e.Height))) { Logger.WriteLineInfo($"RemoteImageSize Invoke,UserId:{e.UserId},Width:{e.Width},Height:{e.Height}"); var remoteImageSizeData = new TRTCRemoteImageSizeData(e.UserId, e.Width, e.Height); var trtcMessage = new TRTCMessage(EnumMessageType.RemoteImageSizeChanged, remoteImageSizeData.ToBytes()); _fileWriterFromTRTCToCommon.WriteMessageToFileContinuous(trtcMessage.ToBytes()); } } _remoteVideoFileWriter[e.UserId].WriteToFileContinuous(e.Data); } catch (Exception ex) { Logger.WriteLineError($"OnRemoteVideoFrameReceived error:userId:{e.UserId},{ex}"); } } private static void StartNewRemoteVideoFileWriter(string userId, int width, int height) { var remoteVideoFileWriter = new FileTransferWriter($"{FISTRTCConsts.ImageTransferFolderForTRTCConsultationRemoteVideo}_{userId}", FISTRTCConsts.DataTransferFolderForTRTCConsultation, _fisFolderPath, null); if (!_remoteVideoFileWriter.TryAdd(userId, remoteVideoFileWriter)) { OnLogMsgThrow(null, new LogEventArgs(DeviceLogCategory.Error, $"Remote Video File Writer List Try Add Failed, Id: {userId}")); } else { Logger.WriteLineInfo($"Start Remote Video File Writer Client :{remoteVideoFileWriter.Name},Width:{width},Height:{height}"); var remoteMessage = new TRTCRemoteImageSizeData(userId, width, height); var fileTransferMessageData = new TRTCMessage(EnumMessageType.AddRemoteFileTransfer, remoteMessage.ToBytes()); _fileWriterFromTRTCToCommon.WriteMessageToFileContinuous(fileTransferMessageData.ToBytes()); remoteVideoFileWriter.LogMsgThrow += OnLogMsgThrow; } } private static void OnEnterRoomError(object sender, EventArgs e) { var fileTransferMessageData = new TRTCMessage(EnumMessageType.EnterRoomError, null); _fileWriterFromTRTCToCommon.WriteMessageToFileContinuous(fileTransferMessageData.ToBytes()); Logger.WriteLineInfo($"OnEnterRoomError Invoked"); } private static void OnRemoteUserLeaveRoomArrived(object sender, TRTCRemoteUserLeaveRoomMessage e) { var fileTransferMessageData = new TRTCMessage(EnumMessageType.RemoteUserLeaveRoom, e.ToBytes()); _fileWriterFromTRTCToCommon.WriteMessageToFileContinuous(fileTransferMessageData.ToBytes()); Logger.WriteLineInfo($"OnRemoteUserLeaveRoomArrived Invoked,userId:{e.UserId},reason:{e.Reason}."); } private static void OnTryToReconnect(object sender, EventArgs e) { var fileTransferMessageData = new TRTCMessage(EnumMessageType.TryToReconnect, null); _fileWriterFromTRTCToCommon.WriteMessageToFileContinuous(fileTransferMessageData.ToBytes()); Logger.WriteLineInfo("OnTryToReconnect Invoked."); } } }