using System; using System.Collections.Concurrent; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using Vinno.FIS.TRTCClient.Common.Enum; using Vinno.FIS.TRTCClient.Common.Log; using Vinno.FIS.TRTCClient.Common.Models; using Vinno.FIS.TRTCClient.Common.Pipe; namespace Vinno.FIS.TRTCClient { /// /// Interaction logic for App.xaml /// public partial class App : Application { private readonly ConcurrentDictionary _remoteImageList = new ConcurrentDictionary(); private readonly ConcurrentDictionary _remoteVideoPipeClientList = new ConcurrentDictionary(); private DefaultLogEngine _logEngine; private TRTCChatRoom _trtcChatRoom; private TRTCLivePusher _trtcPusher; private PipeServer _messageServer; private PipeClient _messageClient; private PipeServer _localVideoPipeServer; private CancellationTokenSource _tokenSource; private int _processId; private int _outputWidth; private int _outputHeight; private bool _isLiveMode; private EnumLiveChannelCategory _category; private bool _disposed; protected override void OnStartup(StartupEventArgs e) { try { #if DEBUG Thread.Sleep(5000); #endif _tokenSource = new CancellationTokenSource(); if (e.Args.Length > 2) { var parentPid = GetArgValue(e.Args[0], "pid"); var lpt = GetArgValue(e.Args[1], "lpt"); var roomInofLen = e.Args.Length - 2; var roomInfoBytes = new string[roomInofLen]; Array.Copy(e.Args, 2, roomInfoBytes, 0, roomInofLen); var roomInfo = TRTCRoomInfo.ConvertFromArgs(roomInfoBytes); _isLiveMode = roomInfo.IsLiveMode; _category = roomInfo.Category; _outputHeight = roomInfo.OutputHeight; _outputWidth = roomInfo.OutputWidth; string logPath; if (!string.IsNullOrEmpty(lpt)) { logPath = Path.Combine(lpt, "TRTCClientLogs"); } else { logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TRTCClientLogs"); } if (!Directory.Exists(logPath)) { Directory.CreateDirectory(logPath); } _logEngine = new DefaultLogEngine(logPath, _category); Logger.RegisterEngine(_logEngine); Logger.WriteLineInfo("TRTCClient Start Up......"); Logger.WriteLineInfo($"Parent Process Id:{parentPid},RoomInfo:{roomInfo}"); _processId = Process.GetCurrentProcess().Id; Logger.WriteLineInfo($"Current Process Id:{_processId}"); StartMessagePipe(); StartLocalVideoPipeServer(); StartCheckParentThread(parentPid); if (_isLiveMode) { _trtcPusher = new TRTCLivePusher(logPath, _category); _trtcPusher.SendFirstLocalVideoFrame += OnSendFirstLocalVideoFrame; _trtcPusher.EnterRoom(roomInfo); Logger.WriteLineInfo($"MessageClient Send BootUp"); var bootUp = new TRTCMessage(EnumMessageType.BootUp, new byte[0]); _messageClient?.SendBytes(bootUp.ToBytes()); } else { _trtcChatRoom = new TRTCChatRoom(logPath); _trtcChatRoom.RemoteVideoFrameArrived += OnRemoteVideoFrameReceived; _trtcChatRoom.OnTRTCEnterRoomError += OnEnterRoomError; _trtcChatRoom.RemoteUserLeaveRoomArrived += OnRemoteUserLeaveRoomArrived; _trtcChatRoom.TryToReconnect += OnTryToReconnect; _trtcChatRoom.Enter(roomInfo); if (roomInfo.TerminalIsPushing && roomInfo.TerminalRoomId > 0) { _trtcChatRoom.ConnectTerminalRoom(roomInfo.TerminalRoomId, roomInfo.TerminalId); if (roomInfo.IsMultiChannel && !string.IsNullOrEmpty(roomInfo.CameraId)) { _trtcChatRoom.ConnectTerminalCameraRoom(roomInfo.TerminalRoomId, roomInfo.CameraId); } } var bootUp = new TRTCMessage(EnumMessageType.BootUp, new byte[0]); _messageClient?.SendBytes(bootUp.ToBytes()); } } else { Process.GetCurrentProcess().Kill(); } } catch (Exception ex) { Logger.WriteLineError($"Start TrtcClient error:{ex}"); Process.GetCurrentProcess().Kill(); } } protected override void OnExit(ExitEventArgs e) { try { Dispose(); } catch (Exception ex) { Logger.WriteLineError($"Exit TrtcClient error:{ex}"); } finally { base.OnExit(e); } } public void Dispose() { if (_disposed) { return; } try { if (!_disposed) { _tokenSource.Cancel(); StopPusher(); StopRTCRoom(); StopMessagePipe(); StopLocalVideoPipeServer(); StopRemoteVideoPipeClient(); _disposed = true; } } catch (Exception ex) { Logger.WriteLineError($"TRTCClient Dispose Error:{ex}"); } finally { Process.GetCurrentProcess().Kill(); } } #region Start private void StartLocalVideoPipeServer() { _localVideoPipeServer = new PipeServer($"{_processId}_LocalImage"); _localVideoPipeServer.DataReceived += OnLocalVideoDataReceived; _localVideoPipeServer.LogMsgThrow += OnLogMsgThrow; Logger.WriteLineInfo($"Create Local Video Pipe Server :{_localVideoPipeServer.PipeName}"); _localVideoPipeServer.StartAndReceive(); } private void StartNewRemoteVideoPipeClient(string userId, int width, int height) { var remoteVideoPipeClient = new PipeClient($"{_processId}_{userId}_RemoteImage"); if (!_remoteVideoPipeClientList.TryAdd(userId, remoteVideoPipeClient)) { Logger.WriteLineError($"Remote Video Pipe Client List Try Add Failed, Id: {userId}"); return; } else { Logger.WriteLineInfo($"MessageClient Send AddRemoteVideoPipe Client :{remoteVideoPipeClient.PipeName},Width:{width},Height:{height}"); var remoteImageData = new TRTCRemoteImageSizeData(userId, width, height); var pipeMessageData = new TRTCMessage(EnumMessageType.AddRemoteVideoPipe, remoteImageData.ToBytes()); _messageClient?.SendBytes(pipeMessageData.ToBytes()); remoteVideoPipeClient.LogMsgThrow += OnLogMsgThrow; remoteVideoPipeClient.Start(); } } private void StartMessagePipe() { _messageServer = new PipeServer($"{_processId}_FIS2TRTCClientMessage"); _messageServer.LogMsgThrow += OnLogMsgThrow; _messageServer.DataReceived += OnMessageDataReceived; _messageServer.StartAndReceive(); _messageClient = new PipeClient($"{_processId}_TRTCClient2FISMessage"); _messageClient.LogMsgThrow += OnLogMsgThrow; _messageClient.Start(); } private void StartCheckParentThread(string parentProcessId) { if (string.IsNullOrEmpty(parentProcessId)) { return; } if (int.TryParse(parentProcessId, out var pid)) { Task.Run(() => { Logger.WriteLineInfo($"Start Check Thread"); while (true) { if (_tokenSource.IsCancellationRequested) { break; } try { if (pid == 0) { Process.GetCurrentProcess().Kill(); return; } var existApp = Process.GetProcessById(pid); if (existApp == null) { Logger.WriteLineInfo("Parent process not exit,kill myself!"); Process.GetCurrentProcess().Kill(); return; } } catch (Exception ex) { Logger.WriteLineInfo($"Check Parent Error:{ex}"); Process.GetCurrentProcess().Kill(); } finally { Thread.Sleep(5000); } } }, _tokenSource.Token); } } #endregion Start #region Stop private void StopMessagePipe() { if (_messageServer != null) { _messageServer.DataReceived -= OnMessageDataReceived; _messageServer.Dispose(); _messageServer.LogMsgThrow -= OnLogMsgThrow; _messageServer = null; } if (_messageClient != null) { _messageClient.Dispose(); _messageClient.LogMsgThrow -= OnLogMsgThrow; _messageClient = null; } } private void StopLocalVideoPipeServer() { if (_localVideoPipeServer != null) { Logger.WriteLineInfo($"Close Pipe Server Local Video{_localVideoPipeServer.PipeName}"); _localVideoPipeServer.DataReceived -= OnLocalVideoDataReceived; _localVideoPipeServer.Dispose(); _localVideoPipeServer.LogMsgThrow -= OnLogMsgThrow; _localVideoPipeServer = null; } } private void StopRemoteVideoPipeClient() { foreach (var userId in _remoteVideoPipeClientList.Keys) { Logger.WriteLineInfo($"Close Pipe Client Remote Video{_remoteVideoPipeClientList[userId].PipeName}"); _remoteVideoPipeClientList[userId].Dispose(); _remoteVideoPipeClientList[userId].LogMsgThrow -= OnLogMsgThrow; } _remoteVideoPipeClientList.Clear(); _remoteImageList.Clear(); } private void StopRTCRoom() { if (_trtcChatRoom != null) { _trtcChatRoom.RemoteVideoFrameArrived -= OnRemoteVideoFrameReceived; _trtcChatRoom.OnTRTCEnterRoomError -= OnEnterRoomError; _trtcChatRoom.RemoteUserLeaveRoomArrived -= OnRemoteUserLeaveRoomArrived; _trtcChatRoom.TryToReconnect -= OnTryToReconnect; _trtcChatRoom.Exit(); _trtcChatRoom = null; Logger.WriteLineInfo("Exit RTCRoom success"); } } private void StopPusher() { if (_trtcPusher != null) { _trtcPusher.SendFirstLocalVideoFrame -= OnSendFirstLocalVideoFrame; _trtcPusher.Dispose(); _trtcPusher = null; Logger.WriteLineInfo("Stop Pusher success"); } } #endregion Stop private void OnEnterRoomError(object sender, EventArgs e) { Logger.WriteLineInfo($"MessageClient Send EnterRoomError"); var trtcMessage = new TRTCMessage(EnumMessageType.EnterRoomError, new byte[0]); _messageClient?.SendBytes(trtcMessage.ToBytes()); } private void OnTryToReconnect(object sender, EventArgs e) { Logger.WriteLineInfo($"MessageClient Send TryToReconnect"); var pipeMessageData = new TRTCMessage(EnumMessageType.TryToReconnect, new byte[0]); _messageClient?.SendBytes(pipeMessageData.ToBytes()); } private void OnRemoteUserLeaveRoomArrived(object sender, RemoteUserLeaveRoomArgs args) { Logger.WriteLineInfo($"MessageClient Send RemoteUserLeaveRoom,userId:{args.UserId},reason:{args.Reason}"); var pipeRemoteUserLeaveRoomMessage = new TRTCRemoteUserLeaveRoomMessage(args.UserId, args.Reason); var pipeMessageData = new TRTCMessage(EnumMessageType.RemoteUserLeaveRoom, pipeRemoteUserLeaveRoomMessage.ToBytes()); _messageClient?.SendBytes(pipeMessageData.ToBytes()); } private void OnSendFirstLocalVideoFrame(object sender, bool e) { Logger.WriteLineInfo($"MessageClient Send FirstFrameReceived"); var firstImageMessage = new TRTCMessage(EnumMessageType.FirstFrameReceived, new byte[0]); _messageClient?.SendBytes(firstImageMessage.ToBytes()); } private void OnLocalVideoDataReceived(object sender, byte[] e) { if (e.Length != _outputWidth * _outputHeight * 3 / 2) { Logger.WriteLineError($"OnLocalVideoDataReceived Error,Byte Length:{e.Length},OutputHeight:{_outputHeight},OutputWidth:{_outputWidth},ImageSize Should be {_outputHeight * _outputWidth * 3 / 2}"); return; } if (_isLiveMode) { _trtcPusher?.SendData((uint)_outputWidth, (uint)_outputHeight, e); } else { _trtcChatRoom?.SendData((uint)_outputWidth, (uint)_outputHeight, e); } } private string GetArgValue(string arg, string tag) { var args = arg.Split('='); if (args.Count() == 2 && args[0] == tag) { return args[1]; } return string.Empty; } private void OnMessageDataReceived(object sender, byte[] e) { if (e == null) { return; } var trtcMessage = TRTCMessage.FromBytes(e); Logger.WriteLineInfo($"Message Pipe Server Receive PipeMessage{trtcMessage?.MessageType}"); switch (trtcMessage.MessageType) { case EnumMessageType.ExitRoom: Dispose(); break; case EnumMessageType.Mute: var muteMessage = TRTCBoolMessage.FromBytes(trtcMessage.MessageData); if (muteMessage != null) { if (_isLiveMode) { _trtcPusher?.SetMute(muteMessage.Value); } else { _trtcChatRoom?.Mute(muteMessage.Value); } } break; case EnumMessageType.AdjustMicVolume: var volumeMessage = TRTCIntMessage.FromBytes(trtcMessage.MessageData); if (volumeMessage != null) { if (_isLiveMode) { _trtcPusher?.AdjustMicVolume(volumeMessage.Value); } else { _trtcChatRoom?.AdjustMicVolume(volumeMessage.Value); } } break; case EnumMessageType.SwitchMic: var micMessage = TRTCStringMessage.FromBytes(trtcMessage.MessageData); if (micMessage != null) { if (_isLiveMode) { _trtcPusher?.SwitchMic(micMessage.Value); } else { _trtcChatRoom?.SwitchMic(micMessage.Value); } } 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; } } private void OnRemoteVideoFrameReceived(object sender, VideoFrameArrivedArgs e) { if (string.IsNullOrEmpty(e.UserId)) { return; } if (!_remoteVideoPipeClientList.ContainsKey(e.UserId)) { StartNewRemoteVideoPipeClient(e.UserId, (int)e.Frame.width, (int)e.Frame.height); } if (!_remoteImageList.ContainsKey(e.UserId)) { if (_remoteImageList.TryAdd(e.UserId, new TRTCRemoteImageSizeData(e.UserId, (int)e.Frame.width, (int)e.Frame.height))) { SendRemoteImageSizeChanged(e.UserId, (int)e.Frame.width, (int)e.Frame.height); } } if (_remoteImageList[e.UserId].Width != (int)e.Frame.width || _remoteImageList[e.UserId].Height != (int)e.Frame.height) { _remoteImageList[e.UserId].Width = (int)e.Frame.width; _remoteImageList[e.UserId].Height = (int)e.Frame.height; SendRemoteImageSizeChanged(e.UserId, (int)e.Frame.width, (int)e.Frame.height); } _remoteVideoPipeClientList[e.UserId].SendBytes(e.Frame.data); } private void SendRemoteImageSizeChanged(string userId, int width, int height) { Logger.WriteLineInfo($"MessageClient Send RemoteImageSizeChanged UserId:{userId},Width:{width},Height:{height}"); var remoteImageData = new TRTCRemoteImageSizeData(userId, width, height); var pipeMessageData = new TRTCMessage(EnumMessageType.RemoteImageSizeChanged, remoteImageData.ToBytes()); _messageClient?.SendBytes(pipeMessageData.ToBytes()); } private void OnLogMsgThrow(object sender, LogEventArgs e) { if (e == null) { return; } switch (e.LogType) { case DeviceLogCategory.Error: Logger.WriteLineError(e.Msg); break; case DeviceLogCategory.Warn: Logger.WriteLineWarn(e.Msg); break; case DeviceLogCategory.Verb: Logger.WriteLineVerbose(e.Msg); break; case DeviceLogCategory.Info: default: Logger.WriteLineInfo(e.Msg); break; } } } }