mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 04:31:06 +00:00
WIP: rtpjitterbuffer: Add RFC7273 media clock handling
This commit is contained in:
parent
17838829eb
commit
271501f657
3 changed files with 222 additions and 0 deletions
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue