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;
}
}
}
}