* </para>
* <para>
* To use gstrtpbin as a sender, request a send_rtp_sink_%%d pad, which will
- * automatically create a send_rtp_src_%%d pad. The session number must be specified when
- * requesting the sink pad. The session manager will modify the
+ * automatically create a send_rtp_src_%%d pad. If the session number is not provided,
+ * the pad from the lowest available session will be returned. The session manager will modify the
* SSRC in the RTP packets to its own SSRC and wil forward the packets on the
* send_rtp_src_%%d pad after updating its internal state.
* </para>
LAST_SIGNAL
};
-#define DEFAULT_LATENCY_MS 200
+#define DEFAULT_LATENCY_MS 200
#define DEFAULT_SDES_CNAME NULL
#define DEFAULT_SDES_NAME NULL
#define DEFAULT_SDES_EMAIL NULL
#define DEFAULT_SDES_LOCATION NULL
#define DEFAULT_SDES_TOOL NULL
#define DEFAULT_SDES_NOTE NULL
+#define DEFAULT_DO_LOST FALSE
enum
{
PROP_SDES_LOCATION,
PROP_SDES_TOOL,
PROP_SDES_NOTE,
+ PROP_DO_LOST,
PROP_LAST
};
GstElement *demux;
gulong demux_newpad_sig;
gulong demux_ptreq_sig;
+ gulong demux_pt_change_sig;
/* the internal pad we use to get RTCP sync messages */
GstPad *sync_pad;
/* for lip-sync */
guint64 clock_base;
+ guint64 clock_base_time;
gint clock_rate;
gint64 ts_offset;
gint64 prev_ts_offset;
+ gint last_pt;
};
#define GST_RTP_SESSION_LOCK(sess) g_mutex_lock ((sess)->lock)
sess->bin = rtpbin;
sess->session = session;
sess->demux = demux;
- sess->ptmap = g_hash_table_new (NULL, NULL);
+ sess->ptmap = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) gst_caps_unref);
rtpbin->sessions = g_slist_prepend (rtpbin->sessions, sess);
/* set NTP base or new session */
gst_element_set_state (sess->session, GST_STATE_NULL);
gst_element_set_state (sess->demux, GST_STATE_NULL);
+ if (sess->recv_rtp_sink != NULL)
+ gst_element_release_request_pad (sess->session, sess->recv_rtp_sink);
+ if (sess->recv_rtp_src != NULL)
+ gst_object_unref (sess->recv_rtp_src);
+ if (sess->recv_rtcp_sink != NULL)
+ gst_element_release_request_pad (sess->session, sess->recv_rtcp_sink);
+ if (sess->sync_src != NULL)
+ gst_object_unref (sess->sync_src);
+ if (sess->send_rtp_sink != NULL)
+ gst_element_release_request_pad (sess->session, sess->send_rtp_sink);
+ if (sess->send_rtp_src != NULL)
+ gst_object_unref (sess->send_rtp_src);
+ if (sess->send_rtcp_src != NULL)
+ gst_element_release_request_pad (sess->session, sess->send_rtcp_src);
+
gst_bin_remove (GST_BIN_CAST (bin), sess->session);
gst_bin_remove (GST_BIN_CAST (bin), sess->demux);
/* first look in the cache */
caps = g_hash_table_lookup (session->ptmap, GINT_TO_POINTER (pt));
- if (caps)
+ if (caps) {
+ gst_caps_ref (caps);
goto done;
+ }
bin = session->bin;
g_signal_emitv (args, gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP], 0, &ret);
- caps = (GstCaps *) g_value_get_boxed (&ret);
+ g_value_unset (&args[0]);
+ g_value_unset (&args[1]);
+ g_value_unset (&args[2]);
+ caps = (GstCaps *) g_value_dup_boxed (&ret);
+ g_value_unset (&ret);
if (!caps)
goto no_caps;
GST_DEBUG ("caching pt %d as %" GST_PTR_FORMAT, pt, caps);
- /* store in cache */
- g_hash_table_insert (session->ptmap, GINT_TO_POINTER (pt), caps);
+ /* store in cache, take additional ref */
+ g_hash_table_insert (session->ptmap, GINT_TO_POINTER (pt),
+ gst_caps_ref (caps));
done:
- gst_caps_ref (caps);
GST_RTP_SESSION_UNLOCK (session);
return caps;
GST_RTP_BIN_UNLOCK (bin);
}
+static void
+gst_rtp_bin_propagate_property_to_jitterbuffer (GstRtpBin * bin,
+ const gchar * name, const GValue * value)
+{
+ GSList *sessions, *streams;
+
+ GST_RTP_BIN_LOCK (bin);
+ for (sessions = bin->sessions; sessions; sessions = g_slist_next (sessions)) {
+ GstRtpBinSession *session = (GstRtpBinSession *) sessions->data;
+
+ GST_RTP_SESSION_LOCK (session);
+ for (streams = session->streams; streams; streams = g_slist_next (streams)) {
+ GstRtpBinStream *stream = (GstRtpBinStream *) streams->data;
+
+ g_object_set_property (G_OBJECT (stream->buffer), name, value);
+ }
+ GST_RTP_SESSION_UNLOCK (session);
+ }
+ GST_RTP_BIN_UNLOCK (bin);
+}
+
+/* get a client with the given SDES name. Must be called with RTP_BIN_LOCK */
static GstRtpBinClient *
get_client (GstRtpBin * bin, guint8 len, guint8 * data, gboolean * created)
{
static void
free_client (GstRtpBinClient * client)
{
+ g_slist_free (client->streams);
g_free (client->cname);
g_free (client);
}
GSList *walk;
/* first find or create the CNAME */
+ GST_RTP_BIN_LOCK (bin);
client = get_client (bin, len, data, &created);
/* find stream in the client */
/* we can only continue if we know the local clock-base and clock-rate */
if (stream->clock_base == -1)
goto no_clock_base;
- if (stream->clock_rate <= 0)
- goto no_clock_rate;
+
+ if (stream->clock_rate <= 0) {
+ gint pt = -1;
+ GstCaps *caps = NULL;
+ GstStructure *s = NULL;
+
+ GST_RTP_SESSION_LOCK (stream->session);
+ pt = stream->last_pt;
+ GST_RTP_SESSION_UNLOCK (stream->session);
+
+ if (pt < 0)
+ goto no_clock_rate;
+
+ caps = get_pt_map (stream->session, pt);
+ if (!caps)
+ goto no_clock_rate;
+
+ s = gst_caps_get_structure (caps, 0);
+ gst_structure_get_int (s, "clock-rate", &stream->clock_rate);
+ gst_caps_unref (caps);
+
+ if (stream->clock_rate <= 0)
+ goto no_clock_rate;
+ }
/* map last RTP time to local timeline using our clock-base */
stream->local_rtp = stream->last_extrtptime - stream->clock_base;
stream->local_unix =
gst_util_uint64_scale_int (stream->local_rtp, GST_SECOND,
stream->clock_rate);
+ stream->local_unix += stream->clock_base_time;
/* calculate delta between server and receiver */
stream->unix_delta = stream->last_unix - stream->local_unix;
for (walk = client->streams; walk; walk = g_slist_next (walk)) {
GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
- if (ostream->unix_delta < min)
+ if (ostream->unix_delta && ostream->unix_delta < min)
min = ostream->unix_delta;
}
for (walk = client->streams; walk; walk = g_slist_next (walk)) {
GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
+ if (ostream->unix_delta == 0)
+ continue;
+
ostream->ts_offset = ostream->unix_delta - min;
/* delta changed, see how much */
ostream->ssrc, ostream->ts_offset);
}
}
+ GST_RTP_BIN_UNLOCK (bin);
return;
no_clock_base:
{
GST_WARNING_OBJECT (bin, "we have no clock-base");
+ GST_RTP_BIN_UNLOCK (bin);
return;
}
no_clock_rate:
{
GST_WARNING_OBJECT (bin, "we have no clock-rate");
+ GST_RTP_BIN_UNLOCK (bin);
return;
}
}
if (type == GST_RTCP_SDES_CNAME) {
stream->clock_base = GST_BUFFER_OFFSET (buffer);
+ stream->clock_base_time = GST_BUFFER_OFFSET_END (buffer);
/* associate the stream to CNAME */
gst_rtp_bin_associate (bin, stream, len, data);
}
stream->buffer = buffer;
stream->demux = demux;
stream->last_extrtptime = -1;
+ stream->last_pt = -1;
stream->have_sync = FALSE;
session->streams = g_slist_prepend (session->streams, stream);
g_signal_connect (buffer, "request-pt-map",
(GCallback) pt_map_requested, session);
- /* configure latency */
+ /* configure latency and packet lost */
g_object_set (buffer, "latency", session->bin->latency, NULL);
+ g_object_set (buffer, "do-lost", session->bin->do_lost, NULL);
gst_bin_add (GST_BIN_CAST (session->bin), buffer);
gst_element_set_state (buffer, GST_STATE_PLAYING);
"The NOTE to put in SDES messages of this session",
DEFAULT_SDES_NOTE, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_DO_LOST,
+ g_param_spec_boolean ("do-lost", "Do Lost",
+ "Send an event downstream when a packet is lost", DEFAULT_DO_LOST,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
gstelement_class->provide_clock =
GST_DEBUG_FUNCPTR (gst_rtp_bin_provide_clock);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
rtpbin->priv = GST_RTP_BIN_GET_PRIVATE (rtpbin);
rtpbin->priv->bin_lock = g_mutex_new ();
rtpbin->provided_clock = gst_system_clock_obtain ();
+
rtpbin->latency = DEFAULT_LATENCY_MS;
+ rtpbin->do_lost = DEFAULT_DO_LOST;
/* some default SDES entries */
str = g_strdup_printf ("%s@%s", g_get_user_name (), g_get_host_name ());
GST_RTP_BIN_LOCK (rtpbin);
rtpbin->latency = g_value_get_uint (value);
GST_RTP_BIN_UNLOCK (rtpbin);
+ /* propegate the property down to the jitterbuffer */
+ gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "latency", value);
break;
case PROP_SDES_CNAME:
gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_CNAME,
gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_NOTE,
g_value_get_string (value));
break;
+ case PROP_DO_LOST:
+ GST_RTP_BIN_LOCK (rtpbin);
+ rtpbin->do_lost = g_value_get_boolean (value);
+ GST_RTP_BIN_UNLOCK (rtpbin);
+ gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "do-lost", value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_take_string (value, gst_rtp_bin_get_sdes_string (rtpbin,
GST_RTCP_SDES_NOTE));
break;
+ case PROP_DO_LOST:
+ GST_RTP_BIN_LOCK (rtpbin);
+ g_value_set_boolean (value, rtpbin->do_lost);
+ GST_RTP_BIN_UNLOCK (rtpbin);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
GST_RTP_SESSION_UNLOCK (session);
}
+/* Stores the last payload type received on a particular stream */
+static void
+payload_type_change (GstElement * element, guint pt, GstRtpBinStream * stream)
+{
+ GST_RTP_SESSION_LOCK (stream->session);
+ stream->last_pt = pt;
+ GST_RTP_SESSION_UNLOCK (stream->session);
+}
+
/* a new pad (SSRC) was created in @session */
static void
new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad,
s = gst_caps_get_structure (caps, 0);
- if (!gst_structure_get_int (s, "clock-rate", &stream->clock_rate))
+ if (!gst_structure_get_int (s, "clock-rate", &stream->clock_rate)) {
stream->clock_rate = -1;
+ GST_WARNING_OBJECT (session->bin,
+ "Caps have no clock rate %s from pad %s:%s",
+ gst_caps_to_string (caps), GST_DEBUG_PAD_NAME (pad));
+ }
+
if (gst_structure_get_uint (s, "clock-base", &val))
stream->clock_base = val;
else
stream->clock_base = -1;
+
+ gst_caps_unref (caps);
}
/* get pad and link */
GST_DEBUG_OBJECT (session->bin, "linking jitterbuffer");
padname = g_strdup_printf ("src_%d", ssrc);
- srcpad = gst_element_get_pad (element, padname);
+ srcpad = gst_element_get_static_pad (element, padname);
g_free (padname);
sinkpad = gst_element_get_static_pad (stream->buffer, "sink");
gst_pad_link (srcpad, sinkpad);
gst_object_unref (sinkpad);
+ gst_object_unref (srcpad);
/* get the RTCP sync pad */
GST_DEBUG_OBJECT (session->bin, "linking sync pad");
padname = g_strdup_printf ("rtcp_src_%d", ssrc);
- srcpad = gst_element_get_pad (element, padname);
+ srcpad = gst_element_get_static_pad (element, padname);
g_free (padname);
gst_pad_link (srcpad, stream->sync_pad);
gst_object_unref (srcpad);
* depayloaders. */
stream->demux_ptreq_sig = g_signal_connect (stream->demux,
"request-pt-map", (GCallback) pt_map_requested, session);
+ /* connect to the payload-type-change signal so that we can know which
+ * PT is the current PT so that the jitterbuffer can be matched to the right
+ * offset. */
+ stream->demux_pt_change_sig = g_signal_connect (stream->demux,
+ "payload-type-change", (GCallback) payload_type_change, stream);
GST_RTP_SESSION_UNLOCK (session);
pad_it = gst_element_iterate_pads (GST_ELEMENT (element));
name_found = TRUE;
while (gst_iterator_next (pad_it, (gpointer) & pad) == GST_ITERATOR_OK) {
- if (strcmp (gst_pad_get_name (pad), pad_name) == 0)
+ gchar *name;
+
+ name = gst_pad_get_name (pad);
+ if (strcmp (name, pad_name) == 0)
name_found = FALSE;
+ g_free (name);
}
gst_iterator_free (pad_it);
}