private static ICollection <IPEndPoint> getSortedReceivers(VenueState venueState) { List <IPEndPoint> list = new List <IPEndPoint>(venueState.ReceiverData.Keys); list.Sort(myComparer); return(list); }
private static ICollection <uint> getSortedSenders(VenueState venueState) { List <uint> list = new List <uint>(venueState.SenderData.Keys); list.Sort(); return(list); }
//Logging enable/disable protected void CheckBox2_CheckedChanged(object sender, EventArgs e) { bool isChecked = CheckBox2.Checked; VenueState vs = CurrentVenueState(); if (vs != null) { vs.UseLogging = isChecked; } }
private DataTable BuildAdvancedSenderTable(VenueState venueState) { DataTable senderTable = CreateEmptySenderTable(); lock (venueState.SenderData) { foreach (uint ssrc in getSortedSenders(venueState)) { SenderData senderData = venueState.SenderData[ssrc]; if (senderData == null) { continue; } DataRow row = senderTable.NewRow(); row["Sender"] = ssrc; row["IP Addr"] = senderData.Source.ToString(); row["CName"] = senderData.CName; row["Packets Sent"] = senderData.PacketsSent; row["Bytes Sent"] = senderData.BytesSent; double kb = senderData.DataRate; row["Data rate"] = kb.ToString("N") + " Kb/sec"; row["Packet rate"] = senderData.PacketRate.ToString("N") + " pk/sec"; if (senderData.LastSendUpdate.Equals(DateTime.MinValue)) { row["Last update*"] = "No sender data received."; } else { row["Last update*"] = senderData.LastSendUpdate.ToString(); } senderTable.Rows.Add(row); } // for each host in a the venue... } // unlock return(senderTable); }
private void BuildAdvancedReceiverTables(VenueState venueState, out DataTable thptTable, out DataTable lossTable) { thptTable = new DataTable(); lossTable = new DataTable(); thptTable.Columns.Add("Receiver IP", Type.GetType("System.String")); thptTable.Columns.Add("CName", Type.GetType("System.String")); lossTable.Columns.Add("Receiver IP", Type.GetType("System.String")); lossTable.Columns.Add("CName", Type.GetType("System.String")); // keep track of the packet sending rate for each sender; this avoids having // to hold multiple locks at the same time... IDictionary <uint, double> sendingRateMap = new Dictionary <uint, double>(); lock (venueState.SenderData) { // add a column for each sender... Keep the key set sorted so that elements don't // bounce around between rows foreach (uint ssrc in getSortedSenders(venueState)) { thptTable.Columns.Add(ssrc.ToString(), Type.GetType("System.String")); lossTable.Columns.Add(ssrc.ToString(), Type.GetType("System.String")); sendingRateMap[ssrc] = venueState.SenderData[ssrc].PacketRate; } } lock (venueState.ReceiverData) { // add a row for each receiver foreach (IPEndPoint endPoint in getSortedReceivers(venueState)) { ReceiverData receiverData = venueState.ReceiverData[endPoint]; if (receiverData == null) { continue; // shouldn't happen... } // process receiver summaries ICollection <ReceiverSummary> summaries = receiverData.ReceiverSummaries; if (summaries == null || summaries.Count == 0) { continue; } DataRow thptRow = thptTable.NewRow(); DataRow lossRow = lossTable.NewRow(); thptRow["Receiver IP"] = endPoint.ToString(); thptRow["CName"] = receiverData.CName; lossRow["Receiver IP"] = endPoint.ToString(); lossRow["CName"] = receiverData.CName; // initialize sender columns... foreach (uint ssrc in getSortedSenders(venueState)) { lossRow[ssrc.ToString()] = "no data"; thptRow[ssrc.ToString()] = "no data"; } // process receiver summaries //ICollection<ReceiverSummary> summaries = hostState.ReceiverSummaries; foreach (ReceiverSummary summary in summaries) { uint reporteeSSRC = summary.SSRC; if (thptTable.Columns.Contains(reporteeSSRC.ToString())) { double throughputDifferential = summary.PacketRate - sendingRateMap[reporteeSSRC]; thptRow[reporteeSSRC.ToString()] = String.Format("{0:N} pk/sec", throughputDifferential); lossRow[reporteeSSRC.ToString()] = String.Format("{0:P}", summary.LossRate); } } // for each receiver summary... thptTable.Rows.Add(thptRow); lossTable.Rows.Add(lossRow); } // for each receiver in the venue } // venue.receiverData lock }
private void BuildTables() { Label1.Text = "Last updated: " + System.DateTime.Now; if (DropDownList1.SelectedIndex < 0) { HyperLink1.Text = ""; return; } bool useAdvanedView = CheckBox1.Checked; VenueState venueState = CurrentVenueState(); if (venueState == null) { HyperLink1.Text = ""; return; } if (Global.LoggingEnabled) { if (venueState.LogFileName != null) { HyperLink1.NavigateUrl = HttpRuntime.AppDomainAppVirtualPath + venueState.LogFileName; HyperLink1.Text = venueState.LogFileName; HyperLink1.Visible = true; Label2.Visible = true; } else { HyperLink1.Visible = false; Label2.Visible = false; } CheckBox2.Checked = venueState.UseLogging; } else { CheckBox2.Visible = false; HyperLink1.Visible = false; Label2.Visible = false; } DataTable senderTable; DataTable receiverTable; DataTable lossTable; if (CheckBox1.Checked) { senderTable = BuildAdvancedSenderTable(venueState); BuildAdvancedReceiverTables(venueState, out receiverTable, out lossTable); } else { IDictionary <IPEndPoint, SenderData> senderSummaries; senderTable = BuildBasicSenderTable(venueState, out senderSummaries); BuildBasicReceiverTables(venueState, out receiverTable, out lossTable, senderSummaries); } ////////////////////////////////////////////// ////////////////////////////////////// // views and binding DataView view = new DataView(senderTable); view.AllowEdit = false; view.AllowNew = false; view.AllowDelete = false; GridView1.DataSource = view; DataView view2 = new DataView(receiverTable); view2.AllowEdit = false; view2.AllowNew = false; view2.AllowDelete = false; GridView2.DataSource = view2; DataView view3 = new DataView(lossTable); view3.AllowEdit = false; view3.AllowNew = false; view3.AllowDelete = false; GridView3.DataSource = view3; this.DataBind(); }
/// <summary> /// Merge all streams from a given IP,Port pair. Return the summarized sender data in a dictionary, /// which can be used to build the BasicReceiverTable /// </summary> /// <param name="venueState"></param> /// <returns></returns> private DataTable BuildBasicSenderTable(VenueState venueState, out IDictionary <IPEndPoint, SenderData> senderSummaries) { DataTable senderTable = new DataTable(); senderTable.Columns.Add("IP Addr", Type.GetType("System.String")); senderTable.Columns.Add("CName", Type.GetType("System.String")); senderTable.Columns.Add("Packets Sent", Type.GetType("System.Int64")); senderTable.Columns.Add("Bytes Sent", Type.GetType("System.Int64")); senderTable.Columns.Add("Data Rate", Type.GetType("System.String")); senderTable.Columns.Add("Packet Rate", Type.GetType("System.String")); senderTable.Columns.Add("Last update*", Type.GetType("System.String")); senderSummaries = new Dictionary <IPEndPoint, SenderData>(); lock (venueState.SenderData) { foreach (uint ssrc in getSortedSenders(venueState)) { SenderData senderData = venueState.SenderData[ssrc]; if (senderData == null) { continue; } IPEndPoint endPoint = senderData.Source; if (endPoint == null) { continue; } if (senderSummaries.ContainsKey(endPoint)) { // merge new data with old... SenderData existingData = senderSummaries[endPoint]; SenderData mergedData = new SenderData(senderData, existingData); senderSummaries[endPoint] = mergedData; } else { senderSummaries[endPoint] = senderData; } } } foreach (IPEndPoint endPoint in senderSummaries.Keys) { DataRow row = senderTable.NewRow(); SenderData senderData = senderSummaries[endPoint]; row["IP Addr"] = senderData.Source.ToString(); row["CName"] = senderData.CName; row["Packets Sent"] = senderData.PacketsSent; row["Bytes Sent"] = senderData.BytesSent; double kb = senderData.DataRate; row["Data rate"] = kb.ToString("N") + " Kb/sec"; row["Packet rate"] = senderData.PacketRate.ToString("N") + " pk/sec"; if (senderData.LastSendUpdate.Equals(DateTime.MinValue)) { row["Last update*"] = "No sender data received."; } else { row["Last update*"] = senderData.LastSendUpdate.ToString(); } senderTable.Rows.Add(row); } return(senderTable); }
private void BuildBasicReceiverTables(VenueState venueState, out DataTable thptTable, out DataTable lossTable, IDictionary <IPEndPoint, SenderData> senderSummaries) { thptTable = new DataTable(); lossTable = new DataTable(); thptTable.Columns.Add("Receiver IP", Type.GetType("System.String")); thptTable.Columns.Add("CName", Type.GetType("System.String")); lossTable.Columns.Add("Receiver IP", Type.GetType("System.String")); lossTable.Columns.Add("CName", Type.GetType("System.String")); // add a column for each sender... foreach (IPEndPoint endPoint in senderSummaries.Keys) { thptTable.Columns.Add(endPoint.ToString(), Type.GetType("System.String")); lossTable.Columns.Add(endPoint.ToString(), Type.GetType("System.String")); } lock (venueState.ReceiverData) { // add a row for each receiver foreach (IPEndPoint receiverEndpoint in getSortedReceivers(venueState)) { ReceiverData receiverData = venueState.ReceiverData[receiverEndpoint]; if (receiverData == null) { continue; // shouldn't happen... } // process receiver summaries ICollection <ReceiverSummary> summaries = receiverData.ReceiverSummaries; if (summaries == null || summaries.Count == 0) { continue; } DataRow thptRow = thptTable.NewRow(); DataRow lossRow = lossTable.NewRow(); thptRow["Receiver IP"] = receiverEndpoint.ToString(); thptRow["CName"] = receiverData.CName; lossRow["Receiver IP"] = receiverEndpoint.ToString(); lossRow["CName"] = receiverData.CName; // initialize sender columns... foreach (IPEndPoint endPoint in senderSummaries.Keys) { lossRow[endPoint.ToString()] = "no data"; thptRow[endPoint.ToString()] = "no data"; } // process receiver summaries, while merging data from co-located data streams // thus, we should end up with one column per sending host, regardless of its // number of output streams... IDictionary <IPEndPoint, ReceiverSummary> mergedReceiverSummaries = new Dictionary <IPEndPoint, ReceiverSummary>(); // this is the merge phase... foreach (ReceiverSummary summary in summaries) { SenderData senderData; if (!venueState.SenderData.TryGetValue(summary.SSRC, out senderData)) { continue; // no sender data for this receiver summary } IPEndPoint senderEndpoint = senderData.Source; if (senderEndpoint == null) { continue; } if (mergedReceiverSummaries.ContainsKey(senderEndpoint)) { // merge the summary data with existing data from this sender ReceiverSummary existingSummary = mergedReceiverSummaries[senderEndpoint]; ReceiverSummary updatedSummary = new ReceiverSummary(existingSummary, summary); mergedReceiverSummaries[senderEndpoint] = updatedSummary; } else { // otherwise, create a new summary entry for this EndPoint mergedReceiverSummaries[senderEndpoint] = summary; } } // At this point, we have merged data from co-located data streams. Populate // the columns corresponding to source IP addreses. foreach (IPEndPoint senderEndpoint in mergedReceiverSummaries.Keys) { if (thptTable.Columns.Contains(senderEndpoint.ToString())) { ReceiverSummary summary = mergedReceiverSummaries[senderEndpoint]; SenderData senderData = senderSummaries[senderEndpoint]; double throughputDifferential = summary.PacketRate - senderData.PacketRate; try { thptRow[senderEndpoint.ToString()] = String.Format("{0:N} pk/sec", throughputDifferential); //String.Format("{1:N} - {2:N} = {0:N} pk/sec", throughputDifferential, //summary.PacketRate , senderData.PacketRate); lossRow[senderEndpoint.ToString()] = String.Format("{0:P}", summary.LossRate); } catch (Exception) { // these operations can fail when sender and receiver tables are out of sync } } } // mark "loopback" data as such try { thptRow[receiverEndpoint.ToString()] = "loopback"; lossRow[receiverEndpoint.ToString()] = "loopback"; } catch (Exception) { // these operations can fail if sender, receiver tables are out of sync... } thptTable.Rows.Add(thptRow); lossTable.Rows.Add(lossRow); } // for each receiver in the venue } // venue.receiverData lock }
private void Start() { //BufferChunk chunk = new BufferChunk(2048); CompoundPacket compoundPacket = new CompoundPacket(); EndPoint endPoint = null; while (isRunning) { try { compoundPacket.Reset(); udpListener.ReceiveFrom(compoundPacket.Buffer, out endPoint); compoundPacket.ParseBuffer(); //IPAddress ipAddress = ((IPEndPoint)endPoint).Address; IPEndPoint ipEndpoint = (IPEndPoint)endPoint; // The compound packet enumerator destroys its list during enumeration, // so we keep track of packets that have yet to be processed IList <RtcpPacket> yetToBeProcessed = new List <RtcpPacket>(); String venueName = null; //uint ssrc = 0; long when = 0; // in units of "ticks" // scan through the compound packet, looking for key pieces of meta-data // first, look for the app packet that specifies the venue // also, obtain the ssrc and the time stamp foreach (RtcpPacket packet in compoundPacket) { if (packet.PacketType == (byte)Rtcp.PacketType.APP) { AppPacket appPacket = new AppPacket(packet); if (appPacket.Name.Equals(Rtcp.APP_PACKET_NAME) && appPacket.Subtype == Rtcp.VENUE_APP_PACKET_SUBTYPE) { BufferChunk chunk = new BufferChunk(appPacket.Data); when = chunk.NextInt64(); venueName = chunk.NextUtf8String(chunk.Length); int padIndex = venueName.IndexOf((char)0); if (padIndex > 0) { venueName = venueName.Substring(0, padIndex); } } } else { yetToBeProcessed.Add(packet); } } if (venueName == null) { continue; // can't do anything if we don't know the venue for this packet } if (when == 0) { continue; // need a timestamp } VenueState venueState = null; // compound operations must always be locked... lock (venueStateMap) { if (!venueStateMap.ContainsKey(venueName)) { venueState = new VenueState(venueName); } else { venueState = venueStateMap[venueName]; } } // scan again, this time processing the RTCP packets foreach (RtcpPacket packet in yetToBeProcessed) { switch (packet.PacketType) { case (byte)Rtcp.PacketType.SR: { SrPacket sr = new SrPacket(packet); SenderData senderData = venueState.GetSenderState(sr.SSRC); senderData.Source = ipEndpoint; senderData.updateSenderState(sr.SenderReport, when); // this "refreshes" the host state (so that it won't expire) venueState.SenderData[sr.SSRC] = senderData; break; } case (byte)Rtcp.PacketType.RR: { RrPacket rr = new RrPacket(packet); ReceiverData receiverData = venueState.GetReceiverData(ipEndpoint); // currently, we replace all receiver summaries with the data // from a single RR packet receiverData.updateReceiverState(rr.ReceiverReports, when, venueState); // this "refreshes" the host state (so that it won't expire) venueState.ReceiverData[ipEndpoint] = receiverData; break; } case (byte)Rtcp.PacketType.SDES: { SdesPacket sdp = new SdesPacket(packet); foreach (SdesReport report in sdp.Reports()) { SenderData senderData = venueState.GetSenderState(report.SSRC); senderData.CName = report.SdesData.CName; senderData.Source = ipEndpoint; // this "refreshes" the host state (so that it won't expire) venueState.SenderData[report.SSRC] = senderData; ReceiverData receiverData = venueState.GetReceiverDataWithoutCreating(ipEndpoint); if (receiverData != null) { receiverData.CName = report.SdesData.CName; } } break; } case (byte)Rtcp.PacketType.BYE: { //BYE packets occur when capabilities stop. Clean out sender data only for the //ssrc's affected. We leave receiver reports alone for now. ByePacket byePacket = new ByePacket(packet); foreach (uint ssrc in byePacket.SSRCs) { venueState.SenderData.Remove(ssrc); } //Set a flag to cause the matrix for this venue to be rebuilt on the next request. venueState.ParticipantChange = true; continue; } case (byte)Rtcp.PacketType.APP: { // ignored break; } } } // foreach packet... // refresh the venue state venueStateMap[venueName] = venueState; } catch (Exception e) { Console.Out.WriteLine("Exception : " + e.ToString()); writeEventLog("Exception in receive thread: " + e.ToString(), EventLogEntryType.Warning); } } // loop forever... }
public void updateReceiverState(ArrayList receiverReports, long when, VenueState venueState) { IDictionary <uint, ReceiverSummary> newSummaries = new Dictionary <uint, ReceiverSummary>(); if (when <= lastReceiverStateUpdate) { return; // duplicate information } String timeString = DateTime.Now.ToString("s"); foreach (ReceiverReport receiverReport in receiverReports) { ReceiverSummary summary = null; if (receiverSummaries.ContainsKey(receiverReport.SSRC) && lastReceiverStateUpdate > 0) { /// We have an existing receiver summary: update session statistics summary = receiverSummaries[receiverReport.SSRC]; double packetsReceivedThisInterval = receiverReport.ExtendedHighestSequence - summary.TotalPacketsReceived; double lossesThisInterval = ((ulong)receiverReport.PacketsLost) - summary.TotalLosses; double seconds = ((double)(when - lastReceiverStateUpdate)) / TimeSpan.TicksPerSecond; summary.PacketRate = packetsReceivedThisInterval / seconds; if (lossesThisInterval + packetsReceivedThisInterval == 0) { summary.LossRate = 0.0; //This can happen because some capabilities don't send much data. } else { summary.LossRate = lossesThisInterval / (lossesThisInterval + packetsReceivedThisInterval); } if (venueState.UseLogging) { SenderData senderState = venueState.GetSenderState(receiverReport.SSRC); venueState.WriteLog(timeString + " " + endpoint + " " + senderState.Source + "/" + receiverReport.SSRC + " " + summary.PacketRate + " " + lossesThisInterval); } } else { /// create a new summary summary = new ReceiverSummary(receiverReport.SSRC); } /// in either case, update the session statistics summary.TotalLosses = (ulong)receiverReport.PacketsLost; /// Note: we use EHS as total packets received! summary.TotalPacketsReceived = receiverReport.ExtendedHighestSequence; newSummaries[receiverReport.SSRC] = summary; } // for each receiver report... this.receiverSummaries = newSummaries; lastReceiverStateUpdate = when; }
private void Start() { //BufferChunk chunk = new BufferChunk(2048); CompoundPacket compoundPacket = new CompoundPacket(); EndPoint endPoint = null; while (isRunning) { try { compoundPacket.Reset(); udpListener.ReceiveFrom(compoundPacket.Buffer, out endPoint); compoundPacket.ParseBuffer(); //IPAddress ipAddress = ((IPEndPoint)endPoint).Address; IPEndPoint ipEndpoint = (IPEndPoint)endPoint; // The compound packet enumerator destroys its list during enumeration, // so we keep track of packets that have yet to be processed IList<RtcpPacket> yetToBeProcessed = new List<RtcpPacket>(); String venueName = null; //uint ssrc = 0; long when = 0; // in units of "ticks" // scan through the compound packet, looking for key pieces of meta-data // first, look for the app packet that specifies the venue // also, obtain the ssrc and the time stamp foreach (RtcpPacket packet in compoundPacket) { if (packet.PacketType == (byte)Rtcp.PacketType.APP) { AppPacket appPacket = new AppPacket(packet); if (appPacket.Name.Equals(Rtcp.APP_PACKET_NAME) && appPacket.Subtype == Rtcp.VENUE_APP_PACKET_SUBTYPE) { BufferChunk chunk = new BufferChunk(appPacket.Data); when = chunk.NextInt64(); venueName = chunk.NextUtf8String(chunk.Length); int padIndex = venueName.IndexOf((char)0); if (padIndex > 0) venueName = venueName.Substring(0, padIndex); } } else { yetToBeProcessed.Add(packet); } } if (venueName == null) continue; // can't do anything if we don't know the venue for this packet if (when == 0) continue; // need a timestamp VenueState venueState = null; // compound operations must always be locked... lock (venueStateMap) { if (!venueStateMap.ContainsKey(venueName)) venueState = new VenueState(venueName); else venueState = venueStateMap[venueName]; } // scan again, this time processing the RTCP packets foreach (RtcpPacket packet in yetToBeProcessed) { switch (packet.PacketType) { case (byte)Rtcp.PacketType.SR: { SrPacket sr = new SrPacket(packet); SenderData senderData = venueState.GetSenderState(sr.SSRC); senderData.Source = ipEndpoint; senderData.updateSenderState(sr.SenderReport, when); // this "refreshes" the host state (so that it won't expire) venueState.SenderData[sr.SSRC] = senderData; break; } case (byte)Rtcp.PacketType.RR: { RrPacket rr = new RrPacket(packet); ReceiverData receiverData = venueState.GetReceiverData (ipEndpoint); // currently, we replace all receiver summaries with the data // from a single RR packet receiverData.updateReceiverState(rr.ReceiverReports, when, venueState); // this "refreshes" the host state (so that it won't expire) venueState.ReceiverData[ipEndpoint] = receiverData; break; } case (byte)Rtcp.PacketType.SDES: { SdesPacket sdp = new SdesPacket(packet); foreach(SdesReport report in sdp.Reports()) { SenderData senderData = venueState.GetSenderState(report.SSRC); senderData.CName = report.SdesData.CName; senderData.Source = ipEndpoint; // this "refreshes" the host state (so that it won't expire) venueState.SenderData[report.SSRC] = senderData; ReceiverData receiverData = venueState.GetReceiverDataWithoutCreating(ipEndpoint); if (receiverData != null) receiverData.CName = report.SdesData.CName; } break; } case (byte)Rtcp.PacketType.BYE: { //BYE packets occur when capabilities stop. Clean out sender data only for the //ssrc's affected. We leave receiver reports alone for now. ByePacket byePacket = new ByePacket(packet); foreach (uint ssrc in byePacket.SSRCs) { venueState.SenderData.Remove(ssrc); } //Set a flag to cause the matrix for this venue to be rebuilt on the next request. venueState.ParticipantChange = true; continue; } case (byte)Rtcp.PacketType.APP: { // ignored break; } } } // foreach packet... // refresh the venue state venueStateMap[venueName] = venueState; } catch (Exception e) { Console.Out.WriteLine("Exception : " + e.ToString()); writeEventLog("Exception in receive thread: " + e.ToString(), EventLogEntryType.Warning); } } // loop forever... }
public void updateReceiverState(ArrayList receiverReports, long when,VenueState venueState) { IDictionary<uint, ReceiverSummary> newSummaries = new Dictionary<uint, ReceiverSummary>(); if (when <= lastReceiverStateUpdate) return; // duplicate information String timeString = DateTime.Now.ToString("s"); foreach (ReceiverReport receiverReport in receiverReports) { ReceiverSummary summary = null; if (receiverSummaries.ContainsKey(receiverReport.SSRC) && lastReceiverStateUpdate > 0) { /// We have an existing receiver summary: update session statistics summary = receiverSummaries[receiverReport.SSRC]; double packetsReceivedThisInterval = receiverReport.ExtendedHighestSequence - summary.TotalPacketsReceived; double lossesThisInterval = ((ulong) receiverReport.PacketsLost) - summary.TotalLosses; double seconds = ((double)(when - lastReceiverStateUpdate)) / TimeSpan.TicksPerSecond; summary.PacketRate = packetsReceivedThisInterval / seconds; if (lossesThisInterval + packetsReceivedThisInterval == 0) { summary.LossRate = 0.0; //This can happen because some capabilities don't send much data. } else { summary.LossRate = lossesThisInterval / (lossesThisInterval + packetsReceivedThisInterval); } if (venueState.UseLogging) { SenderData senderState = venueState.GetSenderState(receiverReport.SSRC); venueState.WriteLog(timeString + " " + endpoint + " " + senderState.Source + "/" + receiverReport.SSRC + " " + summary.PacketRate + " " + lossesThisInterval); } } else { /// create a new summary summary = new ReceiverSummary(receiverReport.SSRC); } /// in either case, update the session statistics summary.TotalLosses = (ulong)receiverReport.PacketsLost; /// Note: we use EHS as total packets received! summary.TotalPacketsReceived = receiverReport.ExtendedHighestSequence; newSummaries[receiverReport.SSRC] = summary; } // for each receiver report... this.receiverSummaries = newSummaries; lastReceiverStateUpdate = when; }
/// <summary> /// Build a new matrix /// </summary> /// <param name="venueState"></param> public ConnectivityMatrix(VenueState venueState) { // Aggregate total sender packet rates for each unique sender cname, and // compile a mapping of sender ssrc to cname which we will use for merging receiver reports. Dictionary<string, Row> matrix = new Dictionary<string, Row>(); Dictionary<uint, string> ssrc2cname = new Dictionary<uint, string>(); lock (venueState.SenderData) { foreach (uint ssrc in venueState.SenderData.Keys) { SenderData sd = venueState.SenderData[ssrc]; //Debug.WriteLine(" Building Matrix SenderData.cname=" + sd.CName + ";ssrc=" + ssrc.ToString()); if (matrix.ContainsKey(sd.CName)) { matrix[sd.CName].SenderPacketRate += sd.PacketRate; } else { matrix.Add(sd.CName, new Row(sd.CName, sd.PacketRate)); } ssrc2cname.Add(ssrc, sd.CName); } } ////Test code: Create and remove a bogus sender to test missing participant functionality. //if (DateTime.Now.Second < 30) { // matrix.Add("*****@*****.**", new Row("*****@*****.**", 50.15)); //} ////End test code // Create the NxN matrix with row and column for each cname foreach (Row r in matrix.Values) { r.CreateCells(matrix.Values); } lock (venueState.ReceiverData) { // For each receiver summary, find the sender cname and update the appropriate sender's row. foreach (ReceiverData rd in venueState.ReceiverData.Values) { foreach (ReceiverSummary rs in rd.ReceiverSummaries) { if (ssrc2cname.ContainsKey(rs.SSRC)) { string senderCname = ssrc2cname[rs.SSRC]; if (matrix.ContainsKey(senderCname)) { matrix[senderCname].UpdateReceiver(rd.CName, rs.PacketRate); } } } } } //Commit: copy to serialized arrays foreach (Row r in matrix.Values) { r.CommitRow(); } Rows = new Row[matrix.Count]; matrix.Values.CopyTo(Rows, 0); CreationTime = DateTime.Now; }