WIP: rtpjitterbuffer: Add RFC7273 media clock handling

This commit is contained in:
Sebastian Dröge 2016-01-05 16:15:16 +02:00
parent 17838829eb
commit 271501f657
3 changed files with 222 additions and 0 deletions

View file

@ -100,8 +100,10 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <gst/rtp/gstrtpbuffer.h> #include <gst/rtp/gstrtpbuffer.h>
#include <gst/net/net.h>
#include "gstrtpjitterbuffer.h" #include "gstrtpjitterbuffer.h"
#include "rtpjitterbuffer.h" #include "rtpjitterbuffer.h"
@ -413,6 +415,8 @@ static GstPad *gst_rtp_jitter_buffer_request_new_pad (GstElement * element,
static void gst_rtp_jitter_buffer_release_pad (GstElement * element, static void gst_rtp_jitter_buffer_release_pad (GstElement * element,
GstPad * pad); GstPad * pad);
static GstClock *gst_rtp_jitter_buffer_provide_clock (GstElement * element); static GstClock *gst_rtp_jitter_buffer_provide_clock (GstElement * element);
static gboolean gst_rtp_jitter_buffer_set_clock (GstElement * element,
GstClock * clock);
/* pad overrides */ /* pad overrides */
static GstCaps *gst_rtp_jitter_buffer_getcaps (GstPad * pad, GstCaps * filter); static GstCaps *gst_rtp_jitter_buffer_getcaps (GstPad * pad, GstCaps * filter);
@ -820,6 +824,8 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_release_pad); GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_release_pad);
gstelement_class->provide_clock = gstelement_class->provide_clock =
GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_provide_clock); GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_provide_clock);
gstelement_class->set_clock =
GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_set_clock);
gst_element_class_add_pad_template (gstelement_class, gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gst_rtp_jitter_buffer_src_template)); gst_static_pad_template_get (&gst_rtp_jitter_buffer_src_template));
@ -1129,6 +1135,16 @@ gst_rtp_jitter_buffer_provide_clock (GstElement * element)
return gst_system_clock_obtain (); return gst_system_clock_obtain ();
} }
static gboolean
gst_rtp_jitter_buffer_set_clock (GstElement * element, GstClock * clock)
{
GstRtpJitterBuffer *jitterbuffer = GST_RTP_JITTER_BUFFER (element);
rtp_jitter_buffer_set_pipeline_clock (jitterbuffer->priv->jbuf, clock);
return TRUE;
}
static void static void
gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer) gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer)
{ {
@ -1233,6 +1249,7 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
GstStructure *caps_struct; GstStructure *caps_struct;
guint val; guint val;
GstClockTime tval; GstClockTime tval;
const gchar *ts_refclk, *mediaclk;
priv = jitterbuffer->priv; priv = jitterbuffer->priv;
@ -1297,6 +1314,75 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
"npt start/stop: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT, "npt start/stop: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
GST_TIME_ARGS (priv->npt_start), GST_TIME_ARGS (priv->npt_stop)); GST_TIME_ARGS (priv->npt_start), GST_TIME_ARGS (priv->npt_stop));
if ((ts_refclk = gst_structure_get_string (caps_struct, "a-ts-refclk"))) {
GstClock *clock = NULL;
guint64 clock_offset = -1;
GST_DEBUG_OBJECT (jitterbuffer, "Have timestamp reference clock %s",
ts_refclk);
if (g_str_has_prefix (ts_refclk, "ntp=")) {
if (g_str_has_prefix (ts_refclk, "ntp=/traceable/")) {
GST_FIXME_OBJECT (jitterbuffer, "Can't handle traceable NTP clocks");
} else {
const gchar *host, *portstr;
gchar *hostname;
guint port;
host = ts_refclk + sizeof ("ntp=") - 1;
if (host[0] == '[') {
/* IPv6 */
portstr = strchr (host, ']');
if (portstr && portstr[1] == ':')
portstr = portstr + 1;
else
portstr = NULL;
} else {
portstr = strrchr (host, ':');
}
if (!portstr || sscanf (portstr, ":%u", &port) != 1)
port = 123;
if (portstr)
hostname = g_strndup (host, (portstr - host));
else
hostname = g_strdup (host);
clock = gst_ntp_clock_new (NULL, hostname, port, 0);
g_free (hostname);
}
} else if (g_str_has_prefix (ts_refclk, "ptp=IEEE1588-2008:")) {
const gchar *domainstr =
ts_refclk + sizeof ("ptp=IEEE1588-2008:XX-XX-XX-XX-XX-XX-XX-XX") - 1;
guint domain;
if (domainstr[0] != ':' || sscanf (domainstr, ":%u", &domain) != 1)
domain = 0;
clock = gst_ptp_clock_new (NULL, domain);
} else {
GST_FIXME_OBJECT (jitterbuffer, "Unsupported timestamp reference clock");
}
if ((mediaclk = gst_structure_get_string (caps_struct, "a-mediaclk"))) {
GST_DEBUG_OBJECT (jitterbuffer, "Got media clock %s", mediaclk);
if (!g_str_has_prefix (mediaclk, "direct=")
|| sscanf (mediaclk, "direct=%" G_GUINT64_FORMAT, &clock_offset) != 1)
GST_FIXME_OBJECT (jitterbuffer, "Unsupported media clock");
if (strstr (mediaclk, "rate=") != NULL) {
GST_FIXME_OBJECT (jitterbuffer, "Rate property not supported");
clock_offset = -1;
}
}
rtp_jitter_buffer_set_media_clock (priv->jbuf, clock, clock_offset);
} else {
rtp_jitter_buffer_set_media_clock (priv->jbuf, NULL, -1);
}
return TRUE; return TRUE;
/* ERRORS */ /* ERRORS */

View file

@ -98,6 +98,15 @@ rtp_jitter_buffer_finalize (GObject * object)
jbuf = RTP_JITTER_BUFFER_CAST (object); jbuf = RTP_JITTER_BUFFER_CAST (object);
if (jbuf->media_clock_synced_id)
g_signal_handler_disconnect (jbuf->media_clock,
jbuf->media_clock_synced_id);
if (jbuf->media_clock)
gst_object_unref (jbuf->media_clock);
if (jbuf->pipeline_clock)
gst_object_unref (jbuf->pipeline_clock);
g_queue_free (jbuf->packets); g_queue_free (jbuf->packets);
G_OBJECT_CLASS (rtp_jitter_buffer_parent_class)->finalize (object); G_OBJECT_CLASS (rtp_jitter_buffer_parent_class)->finalize (object);
@ -199,6 +208,94 @@ rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer * jbuf)
return jbuf->clock_rate; return jbuf->clock_rate;
} }
/* FIXME: This is called from another thread than anything else
* and will need some locking */
static void
media_clock_synced_cb (GstClock * clock, gboolean synced,
RTPJitterBuffer * jbuf)
{
GstClockTime internal, external;
if (!jbuf->pipeline_clock)
return;
internal = gst_clock_get_internal_time (jbuf->media_clock);
external = gst_clock_get_time (jbuf->pipeline_clock);
gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
}
/**
* rtp_jitter_buffer_set_media_clock:
* @jbuf: an #RTPJitterBuffer
* @clock: (transfer full): media #GstClock
* @clock_offset: RTP time at clock epoch or -1
*
* Sets the media clock for the media and the clock offset
*
*/
void
rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock,
guint64 clock_offset)
{
if (jbuf->media_clock) {
if (jbuf->media_clock_synced_id)
g_signal_handler_disconnect (jbuf->media_clock,
jbuf->media_clock_synced_id);
jbuf->media_clock_synced_id = 0;
gst_object_unref (jbuf->media_clock);
}
jbuf->media_clock = clock;
jbuf->media_clock_offset = clock_offset;
if (jbuf->pipeline_clock && jbuf->media_clock &&
jbuf->pipeline_clock != jbuf->media_clock) {
jbuf->media_clock_synced_id =
g_signal_connect (jbuf->media_clock, "synced",
G_CALLBACK (media_clock_synced_cb), jbuf);
if (gst_clock_is_synced (jbuf->media_clock)) {
GstClockTime internal, external;
internal = gst_clock_get_internal_time (jbuf->media_clock);
external = gst_clock_get_time (jbuf->pipeline_clock);
gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
}
gst_clock_set_master (jbuf->media_clock, jbuf->pipeline_clock);
}
}
/**
* rtp_jitter_buffer_set_pipeline_clock:
* @jbuf: an #RTPJitterBuffer
* @clock: (transfer full): pipeline #GstClock
*
* Sets the pipeline clock
*
*/
void
rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer * jbuf, GstClock * clock)
{
if (jbuf->pipeline_clock)
gst_object_unref (jbuf->pipeline_clock);
jbuf->pipeline_clock = clock;
if (jbuf->pipeline_clock && jbuf->media_clock &&
jbuf->pipeline_clock != jbuf->media_clock) {
if (gst_clock_is_synced (jbuf->media_clock)) {
GstClockTime internal, external;
internal = gst_clock_get_internal_time (jbuf->media_clock);
external = gst_clock_get_time (jbuf->pipeline_clock);
gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
}
gst_clock_set_master (jbuf->media_clock, jbuf->pipeline_clock);
}
}
/** /**
* rtp_jitter_buffer_reset_skew: * rtp_jitter_buffer_reset_skew:
* @jbuf: an #RTPJitterBuffer * @jbuf: an #RTPJitterBuffer
@ -759,6 +856,37 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item,
default: default:
break; break;
} }
if (gst_clock_is_synced (jbuf->media_clock)) {
GstClockTime ntptime, systime;
GstClockTime ntprtptime, rtpsystime;
ntptime = gst_clock_get_internal_time (jbuf->media_clock);
systime = gst_clock_get_time (jbuf->pipeline_clock);
g_print ("ntp %" GST_TIME_FORMAT " sys %" GST_TIME_FORMAT "\n",
GST_TIME_ARGS (ntptime), GST_TIME_ARGS (systime));
ntprtptime = gst_util_uint64_scale (ntptime, jbuf->clock_rate, GST_SECOND);
ntprtptime += jbuf->media_clock_offset;
ntprtptime &= 0xffffffff;
g_print ("rtptime now %lu then %u\n", ntprtptime, rtptime);
if (ntprtptime > rtptime)
ntptime -=
gst_util_uint64_scale (ntprtptime - rtptime, jbuf->clock_rate,
GST_SECOND);
else
ntptime +=
gst_util_uint64_scale (rtptime - ntprtptime, jbuf->clock_rate,
GST_SECOND);
rtpsystime = gst_clock_adjust_unlocked (jbuf->media_clock, ntptime);
g_print ("rtp %" GST_TIME_FORMAT " sys %" GST_TIME_FORMAT "\n",
GST_TIME_ARGS (rtpsystime), GST_TIME_ARGS (systime));
} else {
g_print ("not synced yet\n");
}
/* do skew calculation by measuring the difference between rtptime and the /* do skew calculation by measuring the difference between rtptime and the
* receive dts, this function will return the skew corrected rtptime. */ * receive dts, this function will return the skew corrected rtptime. */
item->pts = calculate_skew (jbuf, rtptime, dts); item->pts = calculate_skew (jbuf, rtptime, dts);

View file

@ -102,6 +102,11 @@ struct _RTPJitterBuffer {
gint64 skew; gint64 skew;
gint64 prev_send_diff; gint64 prev_send_diff;
gboolean buffering_disabled; gboolean buffering_disabled;
GstClock *pipeline_clock;
GstClock *media_clock;
gulong media_clock_synced_id;
guint64 media_clock_offset;
}; };
struct _RTPJitterBufferClass { struct _RTPJitterBufferClass {
@ -150,6 +155,9 @@ void rtp_jitter_buffer_set_delay (RTPJitterBuffer *jbuf,
void rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer *jbuf, guint32 clock_rate); void rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer *jbuf, guint32 clock_rate);
guint32 rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer *jbuf); guint32 rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer *jbuf);
void rtp_jitter_buffer_set_media_clock (RTPJitterBuffer *jbuf, GstClock * clock, guint64 clock_offset);
void rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer *jbuf, GstClock * clock);
void rtp_jitter_buffer_reset_skew (RTPJitterBuffer *jbuf); void rtp_jitter_buffer_reset_skew (RTPJitterBuffer *jbuf);
gboolean rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf, gboolean rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf,