/// <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) { // If the NACK was not meant for us, send it to the NackManager so // we can avoid duplicating it. if (this.m_RtpSender.SSRC != nack.SenderSSRC) { 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.RtpSend(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()); } } } }
/// <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)) { using (MemoryStream stream = new MemoryStream()) { new BinaryFormatter().Serialize(stream, nack); //Debug.WriteLine(string.Format("NACKing frames {0}", // nack.MissingSequences.ToString()), this.GetType().ToString()); try { this.m_RtpSender.Send(new BufferChunk(stream.GetBuffer(), 0, (int)stream.Length)); } catch (Exception) { this.Reset(); } } } }
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)); } }
public void Send(RtpNackMessage message) { this.Post(this.m_SendNACKDelegate, new object[] { message }); }
/// <summary> /// Processes a message received over the RTP network interface. /// </summary> private void HandleFrameReceived(object sender, RtpStream.FrameReceivedEventArgs args) { Debug.Assert(args.RtpStream == this.m_RtpStream); BufferChunk chunk = args.Frame; try { // Attempt to deserialize the contents of the frame. using (MemoryStream stream = new MemoryStream(chunk.Buffer, chunk.Index, chunk.Length)) { object message = this.m_Serializer.Deserialize(stream); // If the message is a chunked, 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. if (message is Chunk) { Chunk chunked = ((Chunk)message); // 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_RtpStream.SSRC, 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_RtpStream.SSRC, new Range(ulong.MinValue, chunked.OldestRecoverableFrame - 1)); } message = this.m_Assembler.Add(chunked); } else if (message 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)message); this.m_Sender.ProcessNack(nack); 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: {0} bytes, type {1}", chunk.Length, 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()); } } } catch (Exception e) { // The application should not crash on account of malformed messages sent from remote clients. // Therefore, we catch all exceptions. We only want to print an error message. Trace.WriteLine("Error deserializing a message: " + e.ToString() + "\r\n" + e.StackTrace, this.GetType().ToString()); } }
/// <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) { // If the NACK was not meant for us, send it to the NackManager so // we can avoid duplicating it. if(this.m_RtpSender.SSRC != nack.SenderSSRC) { 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.RtpSend(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()); } } } }
/// <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"); } } }
public NetworkNACKMessageEvent(RtpNackMessage msg, long timeIndex, string source) : base(timeIndex, source) { this.message = msg; }