|
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Net;
using LumiSoft.Net;
using LumiSoft.Net.Media.Codec;
using LumiSoft.Net.RTP;
namespace LumiSoft.Net.RTP.Debug
{
/// <summary>
/// This class implements RTP multimedia session debugger/monitoring UI.
/// </summary>
public class wfrm_RTP_Debug : Form
{
#region method ComboBoxItem
/// <summary>
/// This class implements ComboBaox item.
/// </summary>
private class ComboBoxItem
{
private string m_Text = "";
private object m_pTag = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="text">Text.</param>
/// <param name="tag">User data.</param>
public ComboBoxItem(string text,object tag)
{
m_Text = text;
m_pTag = tag;
}
#region method ToString
/// <summary>
/// Returns ComboBox text.
/// </summary>
/// <returns>eturns ComboBox text.</returns>
public override string ToString()
{
return m_Text;
}
#endregion
#region Properties implementation
/// <summary>
/// Gets text.
/// </summary>
public string Text
{
get{ return m_Text; }
}
/// <summary>
/// Gets user data.
/// </summary>
public object Tag
{
get{ return m_pTag; }
}
#endregion
}
#endregion
#region class RTP_SessionStatistics
/// <summary>
/// This class provides data for RTP global statistic property grid.
/// </summary>
private class RTP_SessionStatistics
{
private RTP_Session m_pSession = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="session">RTP session.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>session</b></exception>
public RTP_SessionStatistics(RTP_Session session)
{
if(session == null){
throw new ArgumentNullException("session");
}
m_pSession = session;
}
#region Properties implementation
/// <summary>
/// Gets total members count.
/// </summary>
public long Members
{
get{ return m_pSession.Members.Length; }
}
/// <summary>
/// Gets total members who send RPT data.
/// </summary>
public long Senders
{
get{ return m_pSession.Senders.Length; }
}
/// <summary>
/// Gets total of RTP packets sent by this session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtpPacketsSent
{
get{
return m_pSession.RtpPacketsSent;
}
}
/// <summary>
/// Gets total of RTP bytes(RTP headers included) sent by this session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtpBytesSent
{
get{
return m_pSession.RtpBytesSent;
}
}
/// <summary>
/// Gets total of RTP packets received by this session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtpPacketsReceived
{
get{
return m_pSession.RtpPacketsReceived;
}
}
/// <summary>
/// Gets total of RTP bytes(RTP headers included) received by this session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtpBytesReceived
{
get{
return m_pSession.RtpBytesReceived;
}
}
/// <summary>
/// Gets number of times RTP packet sending has failed.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtpFailedTransmissions
{
get{
return m_pSession.RtpFailedTransmissions;
}
}
/// <summary>
/// Gets total of RTCP packets sent by this session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtcpPacketsSent
{
get{
return m_pSession.RtcpPacketsSent;
}
}
/// <summary>
/// Gets total of RTCP bytes(RTCP headers included) sent by this session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtcpBytesSent
{
get{
return m_pSession.RtcpBytesSent;
}
}
/// <summary>
/// Gets total of RTCP packets received by this session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtcpPacketsReceived
{
get{
return m_pSession.RtcpPacketsReceived;
}
}
/// <summary>
/// Gets total of RTCP bytes(RTCP headers included) received by this session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtcpBytesReceived
{
get{
return m_pSession.RtcpBytesReceived;
}
}
/// <summary>
/// Gets number of times RTCP packet sending has failed.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtcpFailedTransmissions
{
get{
return m_pSession.RtcpFailedTransmissions;
}
}
/// <summary>
/// Current RTCP reporting interval in seconds.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public int RtcpInterval
{
get{
return m_pSession.RtcpInterval;
}
}
/// <summary>
/// Gets time when last RTCP report was sent.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public string RtcpLastTransmission
{
get{
return m_pSession.RtcpLastTransmission.ToString("HH:mm:ss");
}
}
/// <summary>
/// Gets number of times local SSRC collision dedected.
/// </summary>
public long LocalCollisions
{
get{
return m_pSession.LocalCollisions;
}
}
/// <summary>
/// Gets number of times remote SSRC collision dedected.
/// </summary>
public long RemoteCollisions
{
get{
return m_pSession.RemoteCollisions;
}
}
/// <summary>
/// Gets number of times local packets loop dedected.
/// </summary>
public long LocalPacketsLooped
{
get{
return m_pSession.LocalPacketsLooped;
}
}
/// <summary>
/// Gets number of times remote packets loop dedected.
/// </summary>
public long RemotePacketsLooped
{
get{
return m_pSession.RemotePacketsLooped;
}
}
/// <summary>
/// Gets RTP payload.
/// </summary>
public string Payload
{
get{
int paylaod = m_pSession.Payload;
Codec codec = null;
m_pSession.Payloads.TryGetValue(paylaod,out codec);
if(codec == null){
return paylaod.ToString();
}
else{
return paylaod.ToString() + " - " + codec.Name;
}
}
}
/// <summary>
/// Gets RTP session targets.
/// </summary>
public string[] Targets
{
get{
List<string> retVal = new List<string>();
foreach(RTP_Address target in m_pSession.Targets){
retVal.Add(target.IP + ":" + target.DataPort + "/" + target.ControlPort);
}
return retVal.ToArray();
}
}
/// <summary>
/// Gets RTP local end point.
/// </summary>
public string LocalEP
{
get{
return m_pSession.LocalEP.IP + ":" + m_pSession.LocalEP.DataPort + "/" + m_pSession.LocalEP.ControlPort;
}
}
/// <summary>
/// Gets RTP stream mode.
/// </summary>
public string StreamMode
{
get{
return m_pSession.StreamMode.ToString();
}
}
#endregion
}
#endregion
#region class RTP_ParticipantInfo
/// <summary>
/// This class provides data for RTP participant property grid.
/// </summary>
private class RTP_ParticipantInfo
{
private RTP_Participant m_pParticipant = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="participant">RTP local participant.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>participant</b> null reference.</exception>
public RTP_ParticipantInfo(RTP_Participant participant)
{
if(participant == null){
throw new ArgumentNullException("participant");
}
m_pParticipant = participant;
}
#region Properties implementation
/// <summary>
/// Gets or sets the real name, eg. "John Doe". Value null means not specified.
/// </summary>
public string Name
{
get{
if(m_pParticipant is RTP_Participant_Local){
return ((RTP_Participant_Local)m_pParticipant).Name;
}
else{
return ((RTP_Participant_Remote)m_pParticipant).Name;
}
}
}
/// <summary>
/// Gets or sets email address. For example "John.Doe@example.com". Value null means not specified.
/// </summary>
public string Email
{
get{
if(m_pParticipant is RTP_Participant_Local){
return ((RTP_Participant_Local)m_pParticipant).Email;
}
else{
return ((RTP_Participant_Remote)m_pParticipant).Email;
}
}
}
/// <summary>
/// Gets or sets phone number. For example "+1 908 555 1212". Value null means not specified.
/// </summary>
public string Phone
{
get{
if(m_pParticipant is RTP_Participant_Local){
return ((RTP_Participant_Local)m_pParticipant).Phone;
}
else{
return ((RTP_Participant_Remote)m_pParticipant).Phone;
}
}
}
/// <summary>
/// Gets or sets location string. It may be geographic address or for example chat room name.
/// Value null means not specified.
/// </summary>
public string Location
{
get{
if(m_pParticipant is RTP_Participant_Local){
return ((RTP_Participant_Local)m_pParticipant).Location;
}
else{
return ((RTP_Participant_Remote)m_pParticipant).Location;
}
}
}
/// <summary>
/// Gets or sets streaming application name/version.
/// Value null means not specified.
/// </summary>
public string Tool
{
get{
if(m_pParticipant is RTP_Participant_Local){
return ((RTP_Participant_Local)m_pParticipant).Tool;
}
else{
return ((RTP_Participant_Remote)m_pParticipant).Tool;
}
}
}
/// <summary>
/// Gets or sets note text. The NOTE item is intended for transient messages describing the current state
/// of the source, e.g., "on the phone, can't talk". Value null means not specified.
/// </summary>
public string Note
{
get{
if(m_pParticipant is RTP_Participant_Local){
return ((RTP_Participant_Local)m_pParticipant).Note;
}
else{
return ((RTP_Participant_Remote)m_pParticipant).Note;
}
}
}
#endregion
}
#endregion
#region class RTP_SourceInfo
/// <summary>
/// This class provides data for RTP "source" property grid.
/// </summary>
private class RTP_SourceInfo
{
private RTP_Source m_pSource = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="source">RTP source.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>source</b> is null reference.</exception>
public RTP_SourceInfo(RTP_Source source)
{
if(source == null){
throw new ArgumentNullException("source");
}
m_pSource = source;
}
#region Properties implementation
/// <summary>
/// Gets source state.
/// </summary>
public RTP_SourceState State
{
get{ return m_pSource.State; }
}
/// <summary>
/// Gets owner RTP session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public int Session
{
get{ return m_pSource.Session.GetHashCode(); }
}
/// <summary>
/// Gets synchronization source ID.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public uint SSRC
{
get{ return m_pSource.SSRC; }
}
/// <summary>
/// Gets source RTCP end point. Value null means source haven't sent any RTCP packet.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public IPEndPoint RtcpEP
{
get{ return m_pSource.RtcpEP; }
}
/// <summary>
/// Gets source RTP end point. Value null means source haven't sent any RTCP packet.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public IPEndPoint RtpEP
{
get{ return m_pSource.RtpEP; }
}
/// <summary>
/// Gets last time when source sent RTP or RCTP packet.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public string LastActivity
{
get{ return m_pSource.LastActivity.ToString("HH:mm:ss"); }
}
/// <summary>
/// Gets last time when source sent RTCP packet.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public string LastRtcpPacket
{
get{ return m_pSource.LastRtcpPacket.ToString("HH:mm:ss"); }
}
/// <summary>
/// Gets last time when source sent RTP packet.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public string LastRtpPacket
{
get{ return m_pSource.LastRtpPacket.ToString("HH:mm:ss"); }
}
#endregion
}
#endregion
#region class RTP_ReceiveStreamInfo
/// <summary>
/// This class provides data for RTP "receive stream" property grid.
/// </summary>
private class RTP_ReceiveStreamInfo
{
private RTP_ReceiveStream m_pStream = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="stream">RTP receive stream.</param>
public RTP_ReceiveStreamInfo(RTP_ReceiveStream stream)
{
if(stream == null){
throw new ArgumentNullException("stream");
}
m_pStream = stream;
}
#region Properties implementation
/// <summary>
/// Gets stream owner RTP session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public int Session
{
get{ return m_pStream.Session.GetHashCode(); }
}
/// <summary>
/// Gets number of times <b>SeqNo</b> has wrapped around.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public int SeqNoWrapCount
{
get{ return m_pStream.SeqNoWrapCount; }
}
/// <summary>
/// Gets first sequence number what this stream got.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public int FirstSeqNo
{
get{ return m_pStream.FirstSeqNo; }
}
/// <summary>
/// Gets maximum sequnce number that stream has got.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public int MaxSeqNo
{
get{ return m_pStream.MaxSeqNo; }
}
/// <summary>
/// Gets how many RTP packets has received by this stream.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long PacketsReceived
{
get{ return m_pStream.PacketsReceived; }
}
/// <summary>
/// Gets how many RTP misorder packets has received by this stream.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long PacketsMisorder
{
get{ return m_pStream.PacketsMisorder; }
}
/// <summary>
/// Gets how many RTP data has received by this stream.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long BytesReceived
{
get{ return m_pStream.BytesReceived; }
}
/// <summary>
/// Gets how many RTP packets has lost during transmission.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long PacketsLost
{
get{ return m_pStream.PacketsLost; }
}
/// <summary>
/// Gets inter arrival jitter.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public double Jitter
{
get{ return m_pStream.Jitter; }
}
/// <summary>
/// Gets time when last SR(sender report) was received. Returns <b>DateTime.MinValue</b> if no SR received.
/// </summary>
public string LastSRTime
{
get{ return m_pStream.LastSRTime.ToString("HH:mm:ss"); }
}
/// <summary>
/// Gets delay between las SR(sender report) and now in seconds.
/// </summary>
public int DelaySinceLastSR
{
get{ return m_pStream.DelaySinceLastSR / 1000; }
}
#endregion
}
#endregion
#region method RTP_SendStreamInfo
/// <summary>
/// This class provides data for RTP "send stream" property grid.
/// </summary>
private class RTP_SendStreamInfo
{
private RTP_SendStream m_pStream = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="stream">RTP send stream.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null reference.</exception>
public RTP_SendStreamInfo(RTP_SendStream stream)
{
if(stream == null){
throw new ArgumentNullException("stream");
}
m_pStream = stream;
}
#region Properties implementation
/// <summary>
/// Gets stream owner RTP session.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public int Session
{
get{ return m_pStream.Session.GetHashCode(); }
}
/// <summary>
/// Gets number of times <b>SeqNo</b> has wrapped around.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public int SeqNoWrapCount
{
get{ return m_pStream.SeqNoWrapCount; }
}
/// <summary>
/// Gets next packet sequence number.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public int SeqNo
{
get{ return m_pStream.SeqNo; }
}
/// <summary>
/// Gets last packet send time.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public string LastPacketTime
{
get{ return m_pStream.LastPacketTime.ToString("HH:mm:ss"); }
}
/// <summary>
/// Gets last sent RTP packet RTP timestamp header value.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public uint LastPacketRtpTimestamp
{
get{ return m_pStream.LastPacketRtpTimestamp; }
}
/// <summary>
/// Gets how many RTP packets has sent by this stream.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtpPacketsSent
{
get{ return m_pStream.RtpPacketsSent; }
}
/// <summary>
/// Gets how many RTP bytes has sent by this stream.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtpBytesSent
{
get{ return m_pStream.RtpBytesSent; }
}
/// <summary>
/// Gets how many RTP data(no RTP header included) bytes has sent by this stream.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
public long RtpDataBytesSent
{
get{ return m_pStream.RtpDataBytesSent; }
}
#endregion
}
#endregion
private TabControl m_pTab = null;
private SplitContainer m_pParticipantsSplitter = null;
private TreeView m_pParticipants = null;
private PropertyGrid m_pParticipantData = null;
private ComboBox m_pSessions = null;
private PropertyGrid m_pGlobalSessionInfo = null;
private ListView m_pErrors = null;
private bool m_IsDisposed = false;
private RTP_MultimediaSession m_pSession = null;
private Timer m_pTimer = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="session">RTP multimedia session.</param>
public wfrm_RTP_Debug(RTP_MultimediaSession session)
{
if(session == null){
throw new ArgumentNullException("session");
}
m_pSession = session;
InitUI();
// Windows must be visible, otherwise we may get "window handle not created" if RTP session rises events before window gets visible.
this.Visible = true;
m_pSession.Error += new EventHandler<LumiSoft.Net.ExceptionEventArgs>(m_pSession_Error);
m_pSession.SessionCreated += new EventHandler<LumiSoft.Net.EventArgs<RTP_Session>>(m_pSession_SessionCreated);
m_pSession.NewParticipant += new EventHandler<RTP_ParticipantEventArgs>(m_pSession_NewParticipant);
m_pSession.LocalParticipant.SourceAdded += new EventHandler<RTP_SourceEventArgs>(Participant_SourceAdded);
m_pSession.LocalParticipant.SourceRemoved += new EventHandler<RTP_SourceEventArgs>(Participant_SourceRemoved);
//m_pSession.Disposed
m_pTimer = new Timer();
m_pTimer.Interval = 1000;
m_pTimer.Tick += new EventHandler(m_pTimer_Tick);
m_pTimer.Enabled = true;
foreach(RTP_Session s in m_pSession.Sessions){
ComboBoxItem item = new ComboBoxItem("Session: " + s.GetHashCode(),new RTP_SessionStatistics(s));
m_pSessions.Items.Add(item);
}
if(m_pSessions.Items.Count > 0){
m_pSessions.SelectedIndex = 0;
}
}
#region method InitUI
/// <summary>
/// Creates and initializes UI.
/// </summary>
private void InitUI()
{
this.ClientSize = new Size(400,500);
this.Text = "RTP debug";
//this.Icon = ; TODO:
this.FormClosing += new FormClosingEventHandler(wfrm_RTP_Debug_FormClosing);
m_pTab = new TabControl();
m_pTab.Dock = DockStyle.Fill;
m_pTab.TabPages.Add("participants","Participants");
m_pParticipantsSplitter = new SplitContainer();
m_pParticipantsSplitter.Dock = DockStyle.Fill;
m_pParticipantsSplitter.Orientation = Orientation.Vertical;
m_pParticipantsSplitter.SplitterDistance = 60;
m_pTab.TabPages["participants"].Controls.Add(m_pParticipantsSplitter);
m_pParticipants = new TreeView();
m_pParticipants.Dock = DockStyle.Fill;
m_pParticipants.BorderStyle = BorderStyle.None;
m_pParticipants.FullRowSelect = true;
m_pParticipants.HideSelection = false;
m_pParticipants.AfterSelect += new TreeViewEventHandler(m_pParticipants_AfterSelect);
TreeNode nodeParticipant = new TreeNode(m_pSession.LocalParticipant.CNAME);
nodeParticipant.Tag = new RTP_ParticipantInfo(m_pSession.LocalParticipant);
nodeParticipant.Nodes.Add("Sources");
m_pParticipants.Nodes.Add(nodeParticipant);
m_pParticipantsSplitter.Panel1.Controls.Add(m_pParticipants);
m_pParticipantData = new PropertyGrid();
m_pParticipantData.Dock = DockStyle.Fill;
m_pParticipantsSplitter.Panel2.Controls.Add(m_pParticipantData);
m_pTab.TabPages.Add("global_statistics","Global statistics");
m_pGlobalSessionInfo = new PropertyGrid();
m_pGlobalSessionInfo.Dock = DockStyle.Fill;
m_pTab.TabPages["global_statistics"].Controls.Add(m_pGlobalSessionInfo);
m_pSessions = new ComboBox();
m_pSessions.Size = new Size(200,20);
m_pSessions.Location = new Point(100,2);
m_pSessions.DropDownStyle = ComboBoxStyle.DropDownList;
m_pSessions.SelectedIndexChanged += new EventHandler(m_pSessions_SelectedIndexChanged);
m_pTab.TabPages["global_statistics"].Controls.Add(m_pSessions);
m_pSessions.BringToFront();
m_pTab.TabPages.Add("errors","Errors");
m_pErrors = new ListView();
m_pErrors.Dock = DockStyle.Fill;
m_pErrors.View = View.Details;
m_pErrors.FullRowSelect = true;
m_pErrors.HideSelection = false;
m_pErrors.Columns.Add("Message",300);
m_pErrors.DoubleClick += new EventHandler(m_pErrors_DoubleClick);
m_pTab.TabPages["errors"].Controls.Add(m_pErrors);
this.Controls.Add(m_pTab);
}
#endregion
#region Events handling
#region method m_pParticipants_AfterSelect
private void m_pParticipants_AfterSelect(object sender,TreeViewEventArgs e)
{
m_pParticipantData.SelectedObject = e.Node.Tag;
}
#endregion
#region method m_pSessions_SelectedIndexChanged
private void m_pSessions_SelectedIndexChanged(object sender,EventArgs e)
{
m_pGlobalSessionInfo.SelectedObject = ((ComboBoxItem)m_pSessions.SelectedItem).Tag;
}
#endregion
#region method m_pErrors_DoubleClick
private void m_pErrors_DoubleClick(object sender,EventArgs e)
{
if(m_pErrors.SelectedItems.Count > 0){
MessageBox.Show(this,"Error: " + ((Exception)m_pErrors.SelectedItems[0].Tag).ToString(),"Error:",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}
#endregion
#region method m_pSession_Error
/// <summary>
/// Is called when RTP session gets unhandled error.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">Event data.</param>
private void m_pSession_Error(object sender,LumiSoft.Net.ExceptionEventArgs e)
{
if(m_IsDisposed){
return;
}
// Move processing to UI thread.
this.BeginInvoke(new MethodInvoker(delegate(){
ListViewItem item = new ListViewItem(e.Exception.Message);
item.Tag = e.Exception;
m_pErrors.Items.Add(item);
}));
}
#endregion
#region method m_pSession_SessionCreated
/// <summary>
/// Is called when RTP multimedia session creates new session.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">Event data.</param>
private void m_pSession_SessionCreated(object sender,EventArgs<RTP_Session> e)
{
if(m_IsDisposed){
return;
}
// Move processing to UI thread.
this.BeginInvoke(new MethodInvoker(delegate(){
ComboBoxItem item = new ComboBoxItem("Session: " + e.Value.GetHashCode(),new RTP_SessionStatistics(e.Value));
m_pSessions.Items.Add(item);
if(m_pSessions.Items.Count > 0){
m_pSessions.SelectedIndex = 0;
}
}));
}
#endregion
#region method m_pSession_NewParticipant
/// <summary>
/// This method is called when RTP session sees new remote participant.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">Event data.</param>
private void m_pSession_NewParticipant(object sender,RTP_ParticipantEventArgs e)
{
if(m_IsDisposed){
return;
}
e.Participant.Removed += new EventHandler(Participant_Removed);
e.Participant.SourceAdded += new EventHandler<RTP_SourceEventArgs>(Participant_SourceAdded);
e.Participant.SourceRemoved += new EventHandler<RTP_SourceEventArgs>(Participant_SourceRemoved);
// Move processing to UI thread.
this.BeginInvoke(new MethodInvoker(delegate(){
TreeNode nodeParticipant = new TreeNode(e.Participant.CNAME);
nodeParticipant.Tag = new RTP_ParticipantInfo(e.Participant);
nodeParticipant.Nodes.Add("Sources");
m_pParticipants.Nodes.Add(nodeParticipant);
}));
}
#endregion
#region method Participant_Removed
/// <summary>
/// This method is called when RTP remote participant has disjoined the multimedia session.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">Event data.</param>
private void Participant_Removed(object sender,EventArgs e)
{
if(m_IsDisposed){
return;
}
// Move processing to UI thread.
this.BeginInvoke(new MethodInvoker(delegate(){
TreeNode nodeParticipant = FindParticipantNode((RTP_Participant)sender);
if(nodeParticipant != null){
nodeParticipant.Remove();
}
}));
}
#endregion
#region method Participant_SourceAdded
/// <summary>
/// This method is called when participant creates new source.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">Event data.</param>
private void Participant_SourceAdded(object sender,RTP_SourceEventArgs e)
{
if(m_IsDisposed){
return;
}
e.Source.StateChanged += new EventHandler(Source_StateChanged);
// Move processing to UI thread.
this.BeginInvoke(new MethodInvoker(delegate(){
TreeNode nodeParticipant = null;
if(e.Source is RTP_Source_Remote){
nodeParticipant = FindParticipantNode(((RTP_Source_Remote)e.Source).Participant);
}
else{
nodeParticipant = FindParticipantNode(((RTP_Source_Local)e.Source).Participant);
}
TreeNode nodeSource = nodeParticipant.Nodes[0].Nodes.Add(e.Source.SSRC.ToString());
nodeSource.Tag = new RTP_SourceInfo(e.Source);
if(e.Source.State == RTP_SourceState.Active){
TreeNode nodeSourceStream = nodeSource.Nodes.Add("RTP Stream");
if(e.Source is RTP_Source_Local){
nodeSourceStream.Tag = new RTP_SendStreamInfo(((RTP_Source_Local)e.Source).Stream);
}
else{
nodeSourceStream.Tag = new RTP_ReceiveStreamInfo(((RTP_Source_Remote)e.Source).Stream);
}
}
}));
}
#endregion
#region method Source_StateChanged
/// <summary>
/// This method is called when participant source state changes.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">Event data.</param>
private void Source_StateChanged(object sender,EventArgs e)
{
if(m_IsDisposed){
return;
}
RTP_Source source = (RTP_Source)sender;
if(source.State == RTP_SourceState.Disposed){
return;
}
// Move processing to UI thread.
this.BeginInvoke(new MethodInvoker(delegate(){
TreeNode nodeParticipant = null;
if(source is RTP_Source_Remote){
nodeParticipant = FindParticipantNode(((RTP_Source_Remote)source).Participant);
}
else{
nodeParticipant = FindParticipantNode(((RTP_Source_Local)source).Participant);
}
if(nodeParticipant != null){
foreach(TreeNode nodeSource in nodeParticipant.Nodes[0].Nodes){
if(nodeSource.Text == source.SSRC.ToString()){
if(source.State == RTP_SourceState.Active){
TreeNode nodeSourceStream = nodeSource.Nodes.Add("RTP Stream");
if(source is RTP_Source_Local){
nodeSourceStream.Tag = new RTP_SendStreamInfo(((RTP_Source_Local)source).Stream);
}
else{
nodeSourceStream.Tag = new RTP_ReceiveStreamInfo(((RTP_Source_Remote)source).Stream);
}
}
break;
}
}
}
}));
}
#endregion
#region method Participant_SourceRemoved
/// <summary>
/// This method is called when participant closes source.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">Event data.</param>
private void Participant_SourceRemoved(object sender,RTP_SourceEventArgs e)
{
if(m_IsDisposed){
return;
}
// Get SSRC here, BeginInvoke is anynchronous and source may dispose at same time.
uint ssrc = e.Source.SSRC;
// Move processing to UI thread.
this.BeginInvoke(new MethodInvoker(delegate(){
TreeNode nodeParticipant = FindParticipantNode((RTP_Participant)sender);
if(nodeParticipant != null){
foreach(TreeNode nodeSource in nodeParticipant.Nodes[0].Nodes){
if(nodeSource.Text == ssrc.ToString()){
nodeSource.Remove();
break;
}
}
}
}));
}
#endregion
#region method m_pTimer_Tick
private void m_pTimer_Tick(object sender,EventArgs e)
{
if(m_IsDisposed){
return;
}
if(m_pSession.IsDisposed){
this.Visible = false;
return;
}
m_pParticipantData.Refresh();
m_pGlobalSessionInfo.Refresh();
}
#endregion
#region method wfrm_RTP_Debug_FormClosing
private void wfrm_RTP_Debug_FormClosing(object sender,FormClosingEventArgs e)
{
m_IsDisposed = true;
m_pSession.Error -= new EventHandler<LumiSoft.Net.ExceptionEventArgs>(m_pSession_Error);
m_pSession.SessionCreated -= new EventHandler<LumiSoft.Net.EventArgs<RTP_Session>>(m_pSession_SessionCreated);
m_pSession.NewParticipant -= new EventHandler<RTP_ParticipantEventArgs>(m_pSession_NewParticipant);
m_pSession.LocalParticipant.SourceAdded -= new EventHandler<RTP_SourceEventArgs>(Participant_SourceAdded);
m_pSession.LocalParticipant.SourceRemoved -= new EventHandler<RTP_SourceEventArgs>(Participant_SourceRemoved);
m_pTimer.Dispose();
}
#endregion
#endregion
#region FindParticipantNode
/// <summary>
/// Searches specified participant tree node.
/// </summary>
/// <param name="participant">RTP participant.</param>
/// <returns>Returns specified participant tree node or null if no matching node.</returns>
private TreeNode FindParticipantNode(RTP_Participant participant)
{
if(participant == null){
throw new ArgumentNullException("participant");
}
foreach(TreeNode node in m_pParticipants.Nodes){
if(node.Text == participant.CNAME){
return node;
}
}
return null;
}
#endregion
#region Properties implementation
/// <summary>
/// Gets RTP session what UI debugs.
/// </summary>
public RTP_MultimediaSession Session
{
get{ return m_pSession; }
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.