private void handleVideoFrameAckMessage(int recipientId, byte[] message) { // watch out: sender and recipient semantics are inverted! int senderId = BitConverter.ToInt32(message, 1); byte frameId = message[5]; lock (outboundVideoFrameHistories) { Dictionary <int, OutboundVideoFrameHistory> senderHistories; if (!outboundVideoFrameHistories.TryGetValue(senderId, out senderHistories)) { senderHistories = new Dictionary <int, OutboundVideoFrameHistory>(); outboundVideoFrameHistories.Add(senderId, senderHistories); } OutboundVideoFrameHistory history; if (!senderHistories.TryGetValue(recipientId, out history)) { history = new OutboundVideoFrameHistory(); senderHistories.Add(recipientId, history); } history.ClearHistoryUntilThisFrame(frameId); } }
private void compressAndSendFrame(int senderId, int recipientId, byte[] frame) { byte oldestFrameId; byte[] oldestFrameData; lock (outboundVideoFrameHistories) { Dictionary <int, OutboundVideoFrameHistory> senderHistories; if (!outboundVideoFrameHistories.TryGetValue(senderId, out senderHistories)) { senderHistories = new Dictionary <int, OutboundVideoFrameHistory>(); outboundVideoFrameHistories.Add(senderId, senderHistories); } OutboundVideoFrameHistory history; if (!senderHistories.TryGetValue(recipientId, out history)) { history = new OutboundVideoFrameHistory(); senderHistories.Add(recipientId, history); } oldestFrameData = history.OldestFrameData; oldestFrameId = (oldestFrameData != null ? history.OldestFrameId : (byte)0); } byte[] encodedData; byte frameId; if (recipientId == hostingPlayerId) { // no compression needed encodedData = frame; lock (outboundVideoFrameHistories) { frameId = outboundVideoFrameHistories[senderId][recipientId].AddFrame(frame); } } else { byte[] frameBuffer = (byte[])frame.Clone(); unsafe { fixed(byte *frameBufferPtr = frameBuffer) { byte *compressedBuffer = stackalloc byte[4096 * 3]; int byteCount; if (oldestFrameData == null) { // no reference frame videoCodec.Encode((IntPtr)frameBufferPtr, (IntPtr)compressedBuffer, out byteCount); } else { // reference frame fixed(byte *referenceFramePtr = oldestFrameData) { videoCodec.Encode((IntPtr)referenceFramePtr, (IntPtr)frameBufferPtr, (IntPtr)compressedBuffer, out byteCount); } } encodedData = new byte[byteCount]; for (int i = 0; i < encodedData.Length; ++i) { encodedData[i] = compressedBuffer[i]; } } } lock (outboundVideoFrameHistories) { frameId = outboundVideoFrameHistories[senderId][recipientId].AddFrame(frameBuffer); } } using (NetworkPacket packet = new NetworkPacket(encodedData.Length + 7)) { packet.Write((byte)ReservedMessageType.VideoFrame); packet.Write((int)senderId); packet.Write((byte)frameId); packet.Write((byte)(oldestFrameData != null ? oldestFrameId : frameId)); packet.Write(encodedData); // use a timeout equal to 10 times the capture period (15 times per second) // TODO : use measured average time between sends try { server.SendTo(recipientId, packet, 10 * 1000 / 15, SendFlags.NoComplete | SendFlags.Coalesce | SendFlags.PriorityLow); } catch (Microsoft.DirectX.DirectPlay.InvalidPlayerException) { } } }
/// <summary>Connect to a server.</summary> /// <param name="serverName">IP address or hostname of the server.</param> /// <param name="serverPort">IP port on which the server is listening.</param> /// <remarks>The first client to connect becomes the hosting player.</remarks> public void Connect(string serverName, int serverPort) { Debug.Assert(status == NetworkStatus.Disconnected); serverIsOnSameComputer = (serverName == "localhost"); outboundVideoFrameHistory = new OutboundVideoFrameHistory(); inboundVideoFrameHistories = new Dictionary <int, InboundVideoFrameHistory>(); soundBuffers = new Dictionary <int, Buffer3D>(); client = new Microsoft.DirectX.DirectPlay.Client(InitializeFlags.DisableParameterValidation); client.ConnectComplete += new ConnectCompleteEventHandler(onConnectComplete); client.Receive += new ReceiveEventHandler(onReceive); client.SessionTerminated += new SessionTerminatedEventHandler(onSessionTerminated); status = NetworkStatus.Connecting; // trigger NAT traversal EnabledAddresses enabledAddresses = NatResolver.TestNatTraversal(serverName, serverPort); ApplicationDescription description = new ApplicationDescription(); description.GuidApplication = new Guid("{920BAF09-A06C-47d8-BCE0-21B30D0C3586}"); // try first using the host's public address using (Address hostAddress = (enabledAddresses == null ? new Address(serverName, serverPort) : new Address(enabledAddresses.HostPublicAddress, enabledAddresses.HostPublicPort))) { hostAddress.ServiceProvider = Address.ServiceProviderTcpIp; using (Address device = new Address()) { device.ServiceProvider = Address.ServiceProviderTcpIp; device.AddComponent(Address.KeyTraversalMode, Address.TraversalModeNone); if (enabledAddresses != null) { device.AddComponent(Address.KeyPort, enabledAddresses.ClientPrivatePort); } using (NetworkPacket packet = new NetworkPacket()) { try { client.Connect(description, hostAddress, device, packet, 0); } catch (Exception e) { status = NetworkStatus.Disconnected; ConnectionFailureCause cause = (e is NoConnectionException ? ConnectionFailureCause.NoConnection : (e is NotHostException ? ConnectionFailureCause.NotHost : (e is SessionFullException ? ConnectionFailureCause.SessionFull : ConnectionFailureCause.Other))); // try again using the host's private address if (enabledAddresses != null) { using (Address hostPrivateAddress = new Address(enabledAddresses.HostPrivateAddress, enabledAddresses.HostPrivatePort)) { try { client.Connect(description, hostAddress, device, packet, 0); } catch { NetworkMessage message = new NetworkMessage(0, (byte)ReservedMessageType.ConnectionFailed, new byte[1] { (byte)cause }); lock (networkMessages) { networkMessages.Enqueue(message); } return; } } } else { NetworkMessage message = new NetworkMessage(0, (byte)ReservedMessageType.ConnectionFailed, new byte[1] { (byte)cause }); lock (networkMessages) { networkMessages.Enqueue(message); } return; } } } } } // launch a timer to monitor timeout timeoutTimer = new System.Threading.Timer(onTimeout, client, 4000, 0); }