/*********** UDP request ***********/

        public void InitializeRemotePlayChannel(Session session, IPEndPoint ps4Endpoint)
        {
            const int retry = 3;

            for (int i = 0; i < retry; i++)
            {
                Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                udpClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                udpClient.ExclusiveAddressUse = false;
                udpClient.ReceiveTimeout      = 5500;
                udpClient.Connect(ps4Endpoint.Address, RemotePlayPort);

                RemotePlayContext remotePlayContext = SendInitControlMessages(udpClient);
                if (remotePlayContext == null)
                {
                    udpClient.Close();
                    udpClient.Dispose();
                    Thread.Sleep(2500);
                    continue;
                }

                remotePlayContext = SendBigBangMessages(udpClient, session, remotePlayContext);
                if (remotePlayContext == null)
                {
                    udpClient.Close();
                    udpClient.Dispose();
                    Thread.Sleep(2500);
                    continue;
                }

                remotePlayContext = SendStreamInfo(udpClient, session, remotePlayContext);
                if (remotePlayContext == null)
                {
                    udpClient.Close();
                    udpClient.Dispose();
                    Thread.Sleep(2500);
                    continue;
                }

                OnPs4LogInfo?.Invoke(this, "!!!!!!!!!!!!! Stream initialization successfully completed" + Environment.NewLine);

                _udpClient = udpClient;
                RemotePlayAsyncState remotePlayAsyncState = new RemotePlayAsyncState(remotePlayContext, session);
                //HandleFeedbackData(_udpClient, remotePlayContext);
                HandleHeartBeat(_udpClient, remotePlayContext);
                _udpClient.BeginReceive(remotePlayAsyncState.Buffer, 0, remotePlayAsyncState.Buffer.Length, SocketFlags.None, HandleRemotePlayStream, remotePlayAsyncState);

                break;
            }
        }
        /*********** Stream handling ***********/

        private void HandleRemotePlayStream(IAsyncResult result)
        {
            try
            {
                RemotePlayAsyncState remotePlayAsyncState = (RemotePlayAsyncState)result.AsyncState;

                int bytesRead = _udpClient.EndReceive(result);
                if (bytesRead > 0)
                {
                    if (remotePlayAsyncState.Buffer[0] == 0) // Control message
                    {
                        // ToDo check takion_handle_packet_message

                        ControlMessage controlMessage = new ControlMessage();
                        using (MemoryStream memoryStream = new MemoryStream(remotePlayAsyncState.Buffer, 0, bytesRead))
                            using (BinaryReader binaryWriter = new BinaryReader(memoryStream))
                            {
                                controlMessage.Deserialize(binaryWriter);
                                OnPs4LogInfo?.Invoke(this, "Received remote play stream control package, crypto " + controlMessage.Crypto + ", tagPos " + controlMessage.TagPos);
                            }

                        byte[]         ackBangPayload     = HexUtil.Unhexlify("00000000");
                        ushort         ackBangPayloadSize = (ushort)(12 + ackBangPayload.Length);
                        ControlMessage ackControlMessage  = new ControlMessage(0, remotePlayAsyncState.RemotePlayContext.ReceiverId, 0, 0, 3, 0, ackBangPayloadSize, controlMessage.FuncIncr, 0x19000);
                        ackControlMessage.UnParsedPayload = ackBangPayload;


                        SendCryptedControlMessage(ackControlMessage, remotePlayAsyncState.RemotePlayContext, _udpClient);
                        //SendData(_udpClient, GetByteArrayForControlMessage(ackControlMessage));

                        /*if (controlMessage.ProtoBuffFlag == 1 && controlMessage.UnParsedPayload.Length > 0 && controlMessage.UnParsedPayload.Length != 7) WIP
                         * {
                         *  TakionMessage takionMessage = Serializer.Deserialize<TakionMessage>(new MemoryStream(controlMessage.UnParsedPayload));
                         *  if (takionMessage.Type == TakionMessage.PayloadType.Heartbeat)
                         *  {
                         *      byte[] heartbeatPayload = HexUtil.Unhexlify("000803");
                         *      short heartbeatPayloadSize = (short) (12 + heartbeatPayload.Length);
                         *      ControlMessage heartbeatControlMessage = new ControlMessage(0, remotePlayAsyncState.RemotePlayContext.ReceiverId, new Random().Next(), controlMessage.TagPos + 0x10, 0, 1, heartbeatPayloadSize, controlMessage.FuncIncr, 0x10000);
                         *      heartbeatControlMessage.UnParsedPayload = heartbeatPayload;
                         *
                         *      SendData(_udpClient, GetByteArrayForControlMessage(heartbeatControlMessage));
                         *  }
                         * }*/
                    }

                    _udpClient.BeginReceive(remotePlayAsyncState.Buffer, 0, remotePlayAsyncState.Buffer.Length, SocketFlags.None, HandleRemotePlayStream, remotePlayAsyncState);
                }
                else
                {
                    // Close connection
                }
            }
            catch (ObjectDisposedException)
            {
                // Ignore closed from outside
            }
            catch (Exception exception)
            {
                OnPs4ConnectionError?.Invoke(this, "Connection error while handling stream data. Exception: " + exception);
            }
        }