gstrtpsession: Implement a number of feedback packet statistics

Fixes https://bugzilla.gnome.org/show_bug.cgi?id=711693
This commit is contained in:
Torrie Fischer 2013-11-15 15:20:14 +01:00 committed by Wim Taymans
parent cfdadd4114
commit acf74435e3
4 changed files with 94 additions and 0 deletions

View file

@ -218,6 +218,7 @@ enum
PROP_USE_PIPELINE_CLOCK, PROP_USE_PIPELINE_CLOCK,
PROP_RTCP_MIN_INTERVAL, PROP_RTCP_MIN_INTERVAL,
PROP_PROBATION, PROP_PROBATION,
PROP_STATS,
PROP_LAST PROP_LAST
}; };
@ -251,6 +252,8 @@ struct _GstRtpSessionPrivate
GstClockTime send_latency; GstClockTime send_latency;
gboolean use_pipeline_clock; gboolean use_pipeline_clock;
guint rtx_count;
}; };
/* callbacks to handle actions from the session manager */ /* callbacks to handle actions from the session manager */
@ -305,6 +308,8 @@ static gboolean gst_rtp_session_setcaps_send_rtp (GstPad * pad,
static void gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession); static void gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession);
static GstStructure *gst_rtp_session_create_stats (GstRtpSession * rtpsession);
static guint gst_rtp_session_signals[LAST_SIGNAL] = { 0 }; static guint gst_rtp_session_signals[LAST_SIGNAL] = { 0 };
static void static void
@ -605,6 +610,26 @@ gst_rtp_session_class_init (GstRtpSessionClass * klass)
0, G_MAXUINT, DEFAULT_PROBATION, 0, G_MAXUINT, DEFAULT_PROBATION,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpSession::stats:
*
* Various session statistics. This property returns a GstStructure
* with name application/x-rtp-session-stats with the following fields:
*
* "rtx-count" G_TYPE_UINT The number of retransmission events
* received from downstream (in receiver mode)
* "rtx-drop-count" G_TYPE_UINT The number of retransmission events
* dropped (due to bandwidth constraints)
* "sent-nack-count" G_TYPE_UINT Number of NACKs sent
* "recv-nack-count" G_TYPE_UINT Number of NACKs received
*
* Since: 1.3.1
*/
g_object_class_install_property (gobject_class, PROP_STATS,
g_param_spec_boxed ("stats", "Statistics",
"Various statistics", GST_TYPE_STRUCTURE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state = gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_rtp_session_change_state); GST_DEBUG_FUNCPTR (gst_rtp_session_change_state);
gstelement_class->request_new_pad = gstelement_class->request_new_pad =
@ -678,6 +703,8 @@ gst_rtp_session_init (GstRtpSession * rtpsession)
gst_segment_init (&rtpsession->send_rtp_seg, GST_FORMAT_UNDEFINED); gst_segment_init (&rtpsession->send_rtp_seg, GST_FORMAT_UNDEFINED);
rtpsession->priv->thread_stopped = TRUE; rtpsession->priv->thread_stopped = TRUE;
rtpsession->priv->rtx_count = 0;
} }
static void static void
@ -788,12 +815,27 @@ gst_rtp_session_get_property (GObject * object, guint prop_id,
case PROP_PROBATION: case PROP_PROBATION:
g_object_get_property (G_OBJECT (priv->session), "probation", value); g_object_get_property (G_OBJECT (priv->session), "probation", value);
break; break;
case PROP_STATS:
g_value_take_boxed (value, gst_rtp_session_create_stats (rtpsession));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
} }
static GstStructure *
gst_rtp_session_create_stats (GstRtpSession * rtpsession)
{
GstStructure *s;
g_object_get (rtpsession->priv->session, "stats", &s, NULL);
gst_structure_set (s, "rtx-count", G_TYPE_UINT, rtpsession->priv->rtx_count,
NULL);
return s;
}
static void static void
get_current_times (GstRtpSession * rtpsession, GstClockTime * running_time, get_current_times (GstRtpSession * rtpsession, GstClockTime * running_time,
guint64 * ntpnstime) guint64 * ntpnstime)
@ -1511,6 +1553,10 @@ gst_rtp_session_event_recv_rtp_src (GstPad * pad, GstObject * parent,
GstClockTime running_time; GstClockTime running_time;
guint seqnum, delay, deadline, max_delay; guint seqnum, delay, deadline, max_delay;
GST_RTP_SESSION_LOCK (rtpsession);
rtpsession->priv->rtx_count++;
GST_RTP_SESSION_UNLOCK (rtpsession);
if (!gst_structure_get_clock_time (s, "running-time", &running_time)) if (!gst_structure_get_clock_time (s, "running-time", &running_time))
running_time = -1; running_time = -1;
if (!gst_structure_get_uint (s, "ssrc", &ssrc)) if (!gst_structure_get_uint (s, "ssrc", &ssrc))

View file

@ -86,6 +86,7 @@ enum
PROP_RTCP_FEEDBACK_RETENTION_WINDOW, PROP_RTCP_FEEDBACK_RETENTION_WINDOW,
PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD, PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD,
PROP_PROBATION, PROP_PROBATION,
PROP_STATS,
PROP_LAST PROP_LAST
}; };
@ -442,6 +443,24 @@ rtp_session_class_init (RTPSessionClass * klass)
0, G_MAXUINT, DEFAULT_PROBATION, 0, G_MAXUINT, DEFAULT_PROBATION,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* RTPSession::stats:
*
* Various session statistics. This property returns a GstStructure
* with name application/x-rtp-session-stats with the following fields:
*
* "rtx-drop-count" G_TYPE_UINT The number of retransmission events
* dropped (due to bandwidth constraints)
* "sent-nack-count" G_TYPE_UINT Number of NACKs sent
* "recv-nack-count" G_TYPE_UINT Number of NACKs received
*
* Since: 1.3.1
*/
g_object_class_install_property (gobject_class, PROP_STATS,
g_param_spec_boxed ("stats", "Statistics",
"Various statistics", GST_TYPE_STRUCTURE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
klass->get_source_by_ssrc = klass->get_source_by_ssrc =
GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc); GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc);
klass->send_rtcp = GST_DEBUG_FUNCPTR (rtp_session_send_rtcp); klass->send_rtcp = GST_DEBUG_FUNCPTR (rtp_session_send_rtcp);
@ -563,6 +582,19 @@ rtp_session_create_sources (RTPSession * sess)
return res; return res;
} }
static GstStructure *
rtp_session_create_stats (RTPSession * sess)
{
GstStructure *s;
s = gst_structure_new ("application/x-rtp-session-stats",
"rtx-drop-count", G_TYPE_UINT, sess->stats.nacks_dropped,
"sent-nack-count", G_TYPE_UINT, sess->stats.nacks_sent,
"recv-nack-count", G_TYPE_UINT, sess->stats.nacks_received, NULL);
return s;
}
static void static void
rtp_session_set_property (GObject * object, guint prop_id, rtp_session_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec) const GValue * value, GParamSpec * pspec)
@ -684,6 +716,9 @@ rtp_session_get_property (GObject * object, guint prop_id,
case PROP_PROBATION: case PROP_PROBATION:
g_value_set_uint (value, sess->probation); g_value_set_uint (value, sess->probation);
break; break;
case PROP_STATS:
g_value_take_boxed (value, rtp_session_create_stats (sess));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -2260,6 +2295,8 @@ rtp_session_process_feedback (RTPSession * sess, GstRTCPPacket * packet,
GST_BUFFER_TIMESTAMP (fci_buffer) = pinfo->running_time; GST_BUFFER_TIMESTAMP (fci_buffer) = pinfo->running_time;
} }
sess->stats.nacks_received++;
RTP_SESSION_UNLOCK (sess); RTP_SESSION_UNLOCK (sess);
g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_FEEDBACK_RTCP], 0, g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_FEEDBACK_RTCP], 0,
type, fbtype, sender_ssrc, media_ssrc, fci_buffer); type, fbtype, sender_ssrc, media_ssrc, fci_buffer);
@ -2771,6 +2808,7 @@ typedef struct
gboolean is_early; gboolean is_early;
gboolean may_suppress; gboolean may_suppress;
GQueue output; GQueue output;
guint nacked_seqnums;
} ReportData; } ReportData;
static void static void
@ -3000,6 +3038,7 @@ session_nack (const gchar * key, RTPSource * source, ReportData * data)
for (i = 0; i < n_nacks; i++) { for (i = 0; i < n_nacks; i++) {
GST_WRITE_UINT32_BE (fci_data, nacks[i]); GST_WRITE_UINT32_BE (fci_data, nacks[i]);
fci_data += 4; fci_data += 4;
data->nacked_seqnums++;
} }
rtp_source_clear_nacks (source); rtp_source_clear_nacks (source);
@ -3417,6 +3456,7 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time,
data.running_time = running_time; data.running_time = running_time;
data.num_to_report = 0; data.num_to_report = 0;
data.may_suppress = FALSE; data.may_suppress = FALSE;
data.nacked_seqnums = 0;
g_queue_init (&data.output); g_queue_init (&data.output);
RTP_SESSION_LOCK (sess); RTP_SESSION_LOCK (sess);
@ -3492,10 +3532,12 @@ done:
result = result =
sess->callbacks.send_rtcp (sess, source, buffer, output->is_bye, sess->callbacks.send_rtcp (sess, source, buffer, output->is_bye,
sess->send_rtcp_user_data); sess->send_rtcp_user_data);
sess->stats.nacks_sent += data.nacked_seqnums;
} else { } else {
GST_DEBUG ("freeing packet callback: %p" GST_DEBUG ("freeing packet callback: %p"
" do_not_suppress: %d may_suppress: %d", " do_not_suppress: %d may_suppress: %d",
sess->callbacks.send_rtcp, do_not_suppress, data.may_suppress); sess->callbacks.send_rtcp, do_not_suppress, data.may_suppress);
sess->stats.nacks_dropped += data.nacked_seqnums;
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
} }
g_object_unref (source); g_object_unref (source);

View file

@ -31,6 +31,9 @@ rtp_stats_init_defaults (RTPSessionStats * stats)
rtp_stats_set_bandwidths (stats, -1, -1, -1, -1); rtp_stats_set_bandwidths (stats, -1, -1, -1, -1);
stats->min_interval = RTP_STATS_MIN_INTERVAL; stats->min_interval = RTP_STATS_MIN_INTERVAL;
stats->bye_timeout = RTP_STATS_BYE_TIMEOUT; stats->bye_timeout = RTP_STATS_BYE_TIMEOUT;
stats->nacks_dropped = 0;
stats->nacks_sent = 0;
stats->nacks_received = 0;
} }
/** /**

View file

@ -205,6 +205,9 @@ typedef struct {
guint active_sources; guint active_sources;
guint avg_rtcp_packet_size; guint avg_rtcp_packet_size;
guint bye_members; guint bye_members;
guint nacks_dropped;
guint nacks_sent;
guint nacks_received;
} RTPSessionStats; } RTPSessionStats;
void rtp_stats_init_defaults (RTPSessionStats *stats); void rtp_stats_init_defaults (RTPSessionStats *stats);