/// <summary> /// setup the initial buffers and write the basic stream data to the db /// </summary> /// <param name="participant">for this participant</param> /// <param name="stream">using this rtp stream</param> public StreamRecorder( int participantID, RtpStream stream, ConferenceRecorderPC conferencePerfCounter ) { this.rtpStream = stream; this.perfCounter = conferencePerfCounter; this.partID = participantID; // We do *not* re-use existing streams if a stream goes away and comes back. This decision was made // becuase it causes the client not to see the Rtcp data die & come back during playback, though it // did during recording. This inconsistency was viewed to cause problems, and was removed. InitStream(); buffers = new BufferRecorder[Constants.MaxBuffers]; bufferCount = Constants.InitialBuffers; for ( int i = 0; i < bufferCount; i++) { buffers[i] = new BufferRecorder( i, streamID ); buffers[i].Overflowed += new EventHandler(this.OnOverflowException); perfCounter.AddInstanceForCollection(buffers[i]); } curBufIndex = 0; currentBuffer = buffers[curBufIndex]; rtpStream.FrameReceived += new MSR.LST.Net.Rtp.RtpStream.FrameReceivedEventHandler(FrameReceived); }
void OnStreamRemoved(MSR.LST.Net.Rtp.RtpStream rtpStream) { //Debug.WriteLine("OnStreamRemoved ssrc=" + rtpStream.SSRC.ToString() + ";cname=" + rtpStream.Properties.CName); // Do not check (or care) whether the RTPMessageSender has been disposed, // because we will get lots of RtpStreamRemoved events when the RtpSession is closed. // Unregister the ParticipantModel and remove it from the classroom. string cname = rtpStream.Properties.CName; if (this.m_Participants.ContainsKey(cname)) { using (Synchronizer.Lock(this.m_Classroom.SyncRoot)) { if (this.m_Classroom.Participants.Contains(m_Participants[cname])) { this.m_Classroom.Participants.Remove(m_Participants[cname]); } } this.m_Participants.Remove(cname); } //If the stream being removed is our currently associated instructor: if (cname.Equals(m_AssociationCname)) { using (Synchronizer.Lock(this.m_Model.Network.SyncRoot)) { this.m_Model.Network.Association = null; } m_AssociationCname = null; } if (this.m_Receivers.ContainsKey(cname)) { uint remoteSsrc = this.m_Receivers[cname].RemoteSsrc; if (this.m_SsrcToSenderId.ContainsKey(remoteSsrc)) { this.m_SsrcToSenderId.Remove(remoteSsrc); } else { Trace.WriteLine("!!!!Warning: Failed to find SSRC in lookup table when removing stream."); } // Discard all pending Nacks so the NackManager doesn't keep nacking them forever. this.NackManager.Discard(remoteSsrc, Range.UNIVERSE); //Clean up the receiver //Debug.WriteLine("Removed receiver cname: " + cname + "; receiver SSRC: " + remoteSsrc.ToString()); this.m_Receivers[cname].Dispose(); this.m_Receivers.Remove(cname); } else { //Debug.WriteLine("Removed a stream for which we have no capability receiver cname: " + cname); } }
public static Guid IDFromRtpStream(RtpStream rtpStream) { string id = rtpStream.Properties.GetPrivateExtension(PEP_IDENTIFIER); if(id != null) { return new Guid(id); } else { throw new InvalidOperationException(Strings.StreamLacksACapabilityidentifier); } }
void OnStreamAdded(MSR.LST.Net.Rtp.RtpStream rtpStream) { //Debug.WriteLine("OnStreamAdded ssrc=" + rtpStream.SSRC.ToString() + ";cname=" + rtpStream.Properties.CName); using (Synchronizer.Lock(this.m_Classroom.SyncRoot)) { if (rtpStream.SSRC != this.m_Capability.RtpSender.SSRC) { //We need the participant Guid before we can set up the participant model. Do that on receipt of the first message from a given remote node. //Debug.WriteLine("Adding stream ssrc: " + rtpStream.SSRC.ToString()); } else { //Debug.WriteLine("Ignoring RTP Stream Add because the stream SSRC matches the RTPSender SSRC"); } } }
public RTPMessageReceiver(RTPMessageSender sender, RtpStream stream, PresenterModel model, ClassroomModel classroom, ParticipantModel participant) { this.m_Model = model; this.m_Sender = sender; this.m_RtpStream = stream; this.m_Context = new ReceiveContext(model, classroom, participant); this.m_ContextArgs = new object[] { this.m_Context }; this.m_Queue = new MessageProcessingQueue(this); this.m_Serializer = new BinaryFormatter(); this.m_Assembler = new ChunkAssembler(); this.m_Assembler.Nack += new ChunkAssembler.NackDelegate(this.HandleAssemblerNack); this.m_RtpStream.FrameReceived += new RtpStream.FrameReceivedEventHandler(this.HandleFrameReceived); }
static void stream_FrameReceived(object sender, RtpStream.FrameReceivedEventArgs ea) { Console.Out.WriteLine("Frame received of length: " + ea.Frame.Length); byte[] hash = MD5.Create().ComputeHash(ea.Frame.Buffer, ea.Frame.Index, ea.Frame.Length); // compare arrays: for (int i = 0; i < oracleHash.Length; i++) { if (oracleHash[i] != hash[i]) { Console.Out.WriteLine("Hash values not equal!"); return; } } Console.Out.WriteLine("Hash value is good!"); }
public static CapabilityType ChannelFromRtpStream(RtpStream rtpStream) { string chan = rtpStream.Properties.GetPrivateExtension(PEP_CHANNEL); if(chan != null) { if (Boolean.Parse(chan)) { return CapabilityType.Channel; } else { return CapabilityType.Owned; } } else { return CapabilityType.Unknown; } }
public static DynamicProperties DynaPropsFromRtpStream(RtpStream rtpStream) { DynamicProperties dynaProps = new DynamicProperties(); dynaProps.ID = IDFromRtpStream(rtpStream); dynaProps.Name = rtpStream.Properties.Name; dynaProps.SharedFormID = SharedFormIDFromRtpStream(rtpStream); // Establish the channelness or ownership of this capability CapabilityType streamType = ChannelFromRtpStream(rtpStream); // by default, capabilities are owned (i.e. the "unknown" streams are considered owned) dynaProps.Channel = (streamType == CapabilityType.Channel); if (streamType == CapabilityType.Owned) // the stream is being sent by the owner { dynaProps.OwnerName = rtpStream.Properties.CName; } return dynaProps; }
public FrameReceivedEventArgs(RtpStream rtpStream, BufferChunk frame) { RtpStream = rtpStream; Frame = frame; }
public IPStreamPair(IPAddress ip, RtpStream rtp) { senderIP = ip; stream = rtp; }
public void StopListening() { if( rtpStream != null ) { rtpStream.FrameReceived -= new RtpStream.FrameReceivedEventHandler(FrameReceived); this.rtpStream = null; } int curIndex = curBufIndex; if( buffers != null ) { do { BufferRecorder buf = buffers[curBufIndex]; perfCounter.RemoveInstanceForCollection(buf); buf.Dispose(); curBufIndex = (curBufIndex + 1) % bufferCount; } while ( curBufIndex != curIndex); this.buffers = null; } }
private void ValidateStream(RtpStream rtpStream) { // Make sure that the capability properties of the streams agree if((ChannelFromRtpStream(rtpStream) == CapabilityType.Channel) != Channel || IDFromRtpStream(rtpStream) != ID || SharedFormIDFromRtpStream(rtpStream) != SharedFormID) { throw new ArgumentException("Stream to add does not match the settings for this capability."); } }
/// <summary> /// DistributePackets is responsible for receiving all incoming packets on the Rtp Listener, /// casting them into RtpPackets, creating new RtpStreams if one doesn't exist for the RtpPacket's SSRC, /// then calling RtpStream.newPacket(rtpPacket) to place the RtpPacket into the RtpStream's Queue for processing. /// </summary> private void DistributePackets() { while (newPacket.WaitOne()) { while (receivedPackets.Count > 0) { object[] ao = (object[])receivedPackets.Dequeue(); BufferChunk bc = (BufferChunk)ao[0]; IPEndPoint ep = (IPEndPoint)ao[1]; try { //Turn the raw UDP packet data into an Rtp packet RtpPacketBase packet = new RtpPacketBase(bc); // For now, we support 2 types of packets - RtpPacket and RtpPacketFec. If // the number of packet types grows, we may need to push the casting onto // the streams, since they know what type of packets they expect. For now // we will handle the casting here. JVE 6/17/2004 if (packet.PayloadType == PayloadType.FEC) { packet = new RtpPacketFec(packet); } else { packet = new RtpPacket(packet); } #region Fault Injection #if FaultInjection if (rtpSession.DropPacketsPercent > 0) { if (rtpSession.Rnd.Next(1, 100) <= rtpSession.DropPacketsPercent) { if (packet.PayloadType == PayloadType.FEC) { RtpPacketFec fecPacket = (RtpPacketFec)packet; string msg = string.Format(CultureInfo.CurrentCulture, "Dropping fec packet: {0}, FecIndex: {1}, DataRangeMin: {2}", fecPacket.Sequence, fecPacket.FecIndex, fecPacket.DataRangeMin); Trace.WriteLine(msg); pcDroppedFecPackets++; pcDroppedFecBytes += packet.PayloadSize; } else { RtpPacket dataPacket = (RtpPacket)packet; string msg = string.Format(CultureInfo.CurrentCulture, "Dropping data packet: {0}, FecIndex: {1}, FrameIndex: {2}, TS: {3}", dataPacket.Sequence, dataPacket.FecIndex, dataPacket.FrameIndex, dataPacket.TimeStamp); Trace.WriteLine(msg); pcDroppedDataPackets++; pcDroppedDataBytes += packet.PayloadSize; } ReturnBuffer(bc); continue; } } #endif #endregion Fault Injection // Get the stream if it exists RtpStream stream = rtpSession.GetStream(packet.SSRC, ep.Address); if (stream != null) { stream.ProcessPacket(packet); } else { // Otherwise return the packet to the pool ReturnBuffer(bc); unchecked { pcStreamlessPackets++; } } } catch (ThreadAbortException) {} catch (InvalidRtpPacketException e) { HandleInvalidPacket(bc, ep, e.ToString()); ReturnBuffer(bc); } catch (PoolExhaustedException e) { LogEvent(e.ToString(), EventLogEntryType.Error, (int)RtpEL.ID.UnboundedGrowth); return; // Exit the thread gracefully } catch (Exception e) { ReturnBuffer(bc); LogEvent(e.ToString(), EventLogEntryType.Error, (int)RtpEL.ID.Error); } } } }
/// <summary> /// Overridden because we don't want to hook the frame received event /// </summary> /// <param name="rtpStream"></param> public override void StreamAdded(RtpStream rtpStream) { if(this.rtpStream != null) { this.rtpStream.DataStarted -= new MSR.LST.Net.Rtp.RtpStream.DataStartedEventHandler(rtpStream_DataStarted); this.rtpStream.DataStopped -= new MSR.LST.Net.Rtp.RtpStream.DataStoppedEventHandler(rtpStream_DataStopped); } lock(this) { this.rtpStream = rtpStream; this.rtpStreams.Add(rtpStream); } rtpStream.IsUsingNextFrame = true; // We only need to hook these events if we are remote players, not senders if(!IsSender) { rtpStream.DataStarted += new MSR.LST.Net.Rtp.RtpStream.DataStartedEventHandler(rtpStream_DataStarted); rtpStream.DataStopped += new MSR.LST.Net.Rtp.RtpStream.DataStoppedEventHandler(rtpStream_DataStopped); } }
// CF1 private void LeaveRtpSession() { UnhookRtpEvents(); if(rtpSession != null) { rtpSession.Dispose(); rtpSession = null; rtpSender = null; rtpStream = null; } }
public virtual void StreamRemoved(RtpStream rtpStream) { lock(this) { if(isPlaying) { rtpStream.FrameReceived -= new RtpStream.FrameReceivedEventHandler(frameReceived); } rtpStreams.Remove(rtpStream); } // We keep playing regardless of who leaves if it's a capability channel. // If it's not a channel, then close it if the owner leaves // (pfb, 21-Sep-04) Removing code here because Conference checks for no streams left. }
public virtual void StreamAdded(RtpStream rtpStream) { ValidateStream(rtpStream); lock(this) { if (isPlaying) { rtpStream.FrameReceived += new RtpStream.FrameReceivedEventHandler(frameReceived); } rtpStreams.Add(rtpStream); } }
private void ValidateStream(RtpStream rtpStream) { // Make sure that the capability properties of the streams agree if((ChannelFromRtpStream(rtpStream) == CapabilityType.Channel) != Channel || IDFromRtpStream(rtpStream) != ID || SharedFormIDFromRtpStream(rtpStream) != SharedFormID) { throw new ArgumentException(Strings.StreamDoesNotMatchSettings); } }
private void frameReceived(object sender, RtpStream.FrameReceivedEventArgs frea) { MemoryStream ms = new System.IO.MemoryStream((byte[])frea.Frame); object o = bf.Deserialize(ms); Participant participant = (Participant)Conference.participants[frea.RtpStream.Properties.CName]; Conference.FormInvoke(ObjectReceived, new object[] { this, new ObjectReceivedEventArgs(participant, o) } ); }
/// <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()); } }
internal void RaiseRtpStreamTimeoutEvent(RtpStream stream) { rtpStreamTimeoutEvents++; object[] args = {this, new RtpEvents.RtpStreamEventArgs(stream)}; EventThrower.QueueUserWorkItem(new RtpEvents.RaiseEvent(RtpEvents.RaiseRtpStreamTimeoutEvent), args); }
public void Add(uint ssrc, RtpStream rtpStream) { Dictionary.Add(ssrc, rtpStream); }
public InvalidPacketInFrameEventArgs(RtpStream rtpStream, string reason) { RtpStream = rtpStream; Reason = reason; }
// CF1, CF3 private void RtpStreamAdded(object sender, RtpEvents.RtpStreamEventArgs ea) { rtpStream = ea.RtpStream; }
public RtpStreamEventArgs(RtpStream rtpStream) { RtpStream = rtpStream; }
public static Guid IDFromRtpStream(RtpStream rtpStream) { string id = rtpStream.Properties.GetPrivateExtension(PEP_IDENTIFIER); if(id != null) { return new Guid(id); } else { throw new InvalidOperationException("Stream lacks a CapabilityIdentifier in the external private extensions. \n" + "Streams without this are not supported by the ConferenceXP API."); } }
public bool ContainsStream(RtpStream stream) { return (streamsAndIPs.GetStream(stream.SSRC) != null); }
public static Guid SharedFormIDFromRtpStream(RtpStream rtpStream) { string sfid = rtpStream.Properties.GetPrivateExtension(PEP_SHAREDFORM); if(sfid != null) { return new Guid(sfid); } else { return Guid.Empty; } }
public FrameOutOfSequenceEventArgs(RtpStream rtpStream, int lostFrames, string message) { RtpStream = rtpStream; LostFrames = lostFrames; Message = message; }
private void FrameReceived(object sender, RtpStream.FrameReceivedEventArgs ea) { ShowMessage(string.Format(CultureInfo.CurrentCulture, "{0}: {1}", ea.RtpStream.Properties.Name, (string)ea.Frame)); }
public PacketOutOfSequenceEventArgs ( RtpStream rtpStream, int lostPackets, string message ) { RtpStream = rtpStream; LostPackets = lostPackets; Message = message; }
private void FrameReceived(object sender, RtpStream.FrameReceivedEventArgs frea) { FrameWithTicks frame = new FrameWithTicks(frea.Frame); /* A number of considerations caused this design... * * Due to wanting the most accurate timestamp possible, the frame is stamped with the time * (by putting it in the FrameWithTicks structure), and then the ProcessFrame method is * called from the ThreadPool. * * However, this caused problems because processing the frames and saving them to disk were * contending for time on the threadpool. Due to the nature of the design of Archiver, it's * important that the frames are processed with higher priority than saving them to disk * (this minimizes the overhead of exceptions (cpu utilization) during high-stress periods, * causing the frames to be lost with the least amount of hiccups on the server. * * So here we choose to process the frame on this thread only if we know that it won't get * immediate attention on the threadpool. */ int workerThreads, ioThreads; ThreadPool.GetAvailableThreads(out workerThreads, out ioThreads); if( workerThreads > 0 ) { ThreadPool.QueueUserWorkItem( new WaitCallback(ProcessFrame), frame ); } else { Trace.WriteLine("No threads available. Processing frame on EventThrower thread."); ProcessFrame(frame); } }
public static void FrameIncomplete(RtpStream rtpStream, int framesLost) { // Event logging and perf counting are done in called method rtpStream.RaiseFrameOutOfSequenceEvent(framesLost, Strings.IncompleteFrameReceived); }
public static void FrameIncomplete(RtpStream rtpStream, int framesLost) { // Event logging and perf counting are done in called method rtpStream.RaiseFrameOutOfSequenceEvent(framesLost, "Incomplete Frame Received -- Frame Lost"); }