public void Send(RtpNackMessage message) { using (MemoryStream ms = new MemoryStream()) { this.m_bf.Serialize(ms, message); this.m_Sender.Send(new BufferChunk(ms.GetBuffer(), 0, (int)ms.Length)); } }
/// <summary> /// This method is intended to be called only by <see cref="RTPMessageReceiver.HandleAssemblerNack"/>. /// Serialize the NACK packet (as a single chunk), and /// broadcast it <i>immediately</i> (preempting all other prioritized /// chunks in the queue, except a chunk which is currently being sent). /// </summary> /// <param name="nack">The NACK packet to send</param> public void SendNack(RtpNackMessage nack) { using (Synchronizer.Lock(this)) { if (this.m_SsrcToSenderId.ContainsKey(nack.SenderSSRC)) { //Debug.WriteLine(string.Format("NACKing frames {0}", nack.MissingSequences.ToString()), this.GetType().ToString()); SendObject(new CapabilityMessageWrapper(nack, m_LocalId, this.m_SsrcToSenderId[nack.SenderSSRC], m_Capability.IsSender)); } else { Trace.WriteLine("!!!!!Warning: Failed to find sender SSRC in lookup table while trying to send NACK"); } } }
/// <summary> /// This method is intended to be called only by <see cref="RTPMessageReceiver.HandleFrameReceived"/>. /// If the NACK is addressed to this sender, the requested /// frames are retrieved from our buffer and resent, if available. Otherwise, /// the <see cref="RTPNackManager"/> is notified so we don't send duplicate NACKs /// to the addressee. /// </summary> /// <remarks> /// The frames are enqueued with <see cref="MessagePriority.RealTime"/>real-time priority</see>. /// The rationale behind this is that, even if a frame was originally low-priority, the clients /// have already waited "long enough". Also, a replacement frame shouldn't /// be subject to NACKing. /// </remarks> /// <param name="nack">The received/deserialized NACK message</param> internal void ProcessNack(RtpNackMessage nack, Guid intendedNackRecipientId) { // If the NACK was not meant for us, send it to the NackManager so // we can avoid duplicating it. if (!this.m_LocalId.Equals(intendedNackRecipientId)) { this.m_NackManager.Delay(nack.SenderSSRC, nack.MissingSequences); } else { // Otherwise send all frames which are not already queued and // which are still available in the frame buffer. //Debug.WriteLine(string.Format("Received NACK requesting frames {0}", // nack.MissingSequences.ToString()), this.GetType().ToString()); foreach (ulong missing in nack.MissingSequences) { Chunk chunk; if (this.m_FrameBuffer.Take(missing, out chunk)) { if (chunk != null) { //Debug.WriteLine(string.Format("Queueing REPLACEMENT for frame #{0} ({1} bytes, message #{2}, chunk #{3} of {4})", // chunk.FrameSequence, // chunk.Data.Length, // chunk.MessageSequence, // chunk.ChunkSequenceInMessage + 1, // chunk.NumberOfChunksInMessage), // this.GetType().ToString()); this.Post(delegate() { this.CapabilitySend(chunk, MessagePriority.RealTime); }, MessagePriority.RealTime); } else { //Debug.WriteLine(string.Format("NOT queueing replacement for frame #{0} (already in queue or missing)", // missing), this.GetType().ToString()); } } else { //Debug.WriteLine(string.Format("Frame #{0} not in buffer; giving up", // missing), this.GetType().ToString()); } } } }
public void Send(RtpNackMessage message) { this.Post(this.m_SendNACKDelegate, new object[] { message }); }
/// <summary> /// Processes a message received over a Capability channel /// </summary> public void Receive(object message) { // If the message is a chunk, process it with the Chunk Assembler. // (Messages don't have to be chunked, in which case they are processed // immediately below). If the chunk is the last remaining chunk composing // a message, then the Assembler returns the completed message. object unwrappedMessage = null; if (message is CapabilityMessageWrapper) { unwrappedMessage = ((CapabilityMessageWrapper)message).Message; if (unwrappedMessage is Chunk) { Chunk chunked = (Chunk)unwrappedMessage; // Ensure that no more NACKs are sent for this chunk, if it was recovered. if (chunked.FrameSequence > ulong.MinValue) { this.m_Sender.NackManager.Discard(this.m_RemoteSSRC, chunked.FrameSequence); } // Ensure that no futile NACKs are sent for unrecoverable chunks. if (chunked.OldestRecoverableFrame - 1 > ulong.MinValue) { this.m_Sender.NackManager.Discard(this.m_RemoteSSRC, new Range(ulong.MinValue, chunked.OldestRecoverableFrame - 1)); } message = this.m_Assembler.Add(chunked); } else if (unwrappedMessage is RtpNackMessage) { // Otherwise, if the message is a NACK, delegate it to the RTP sender, // which attempts to resend chunks from its buffer. RtpNackMessage nack = ((RtpNackMessage)unwrappedMessage); Guid destinationId = ((CapabilityMessageWrapper)message).RecipientId; this.m_Sender.ProcessNack(nack, destinationId); return; } } // If we have a valid/complete message, queue it for processing on the separate // message execution thread. if (message is IEnumerable <object> ) { foreach (object decoded in (IEnumerable <object>)message) { if (decoded is Message) { // NOTE: Added by CMPRINCE for testing network performance // Save this message this.m_Model.ViewerState.Diagnostic.LogMessageRecv((Message)decoded); Debug.WriteLine(string.Format("Received message: type {0}", decoded.GetType()), this.GetType().ToString()); Debug.Indent(); try { this.m_Queue.ProcessMessage((Message)decoded); } finally { Debug.Unindent(); } } else { Trace.WriteLine("Received invalid message: " + decoded != null ? decoded.GetType().ToString() : null, this.GetType().ToString()); } } } else if (message != null) { Trace.WriteLine("Received invalid message: " + message.GetType().ToString(), this.GetType().ToString()); } }
public NetworkNACKMessageEvent(RtpNackMessage msg, long timeIndex, string source) : base(timeIndex, source) { this.message = msg; }