예제 #1
0
        /// <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());
                    }
                }
            }
        }
예제 #2
0
        /// <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();
                    }
                }
            }
        }
예제 #3
0
 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));
     }
 }
예제 #4
0
 public void Send(RtpNackMessage message)
 {
     this.Post(this.m_SendNACKDelegate, new object[] { message });
 }
예제 #5
0
        /// <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());
            }
        }
예제 #6
0
        /// <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());
                    }
                }
            }
        }
예제 #7
0
        /// <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();
                    }
                }
            }
        }
 /// <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");
         }
     }
 }
예제 #9
0
 public NetworkNACKMessageEvent(RtpNackMessage msg, long timeIndex, string source)
     : base(timeIndex, source)
 {
     this.message = msg;
 }