mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-14 05:12:09 +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
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <gst/rtp/gstrtpbuffer.h>
|
||||
#include <gst/net/net.h>
|
||||
|
||||
#include "gstrtpjitterbuffer.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,
|
||||
GstPad * pad);
|
||||
static GstClock *gst_rtp_jitter_buffer_provide_clock (GstElement * element);
|
||||
static gboolean gst_rtp_jitter_buffer_set_clock (GstElement * element,
|
||||
GstClock * clock);
|
||||
|
||||
/* pad overrides */
|
||||
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);
|
||||
gstelement_class->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_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 ();
|
||||
}
|
||||
|
||||
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
|
||||
gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer)
|
||||
{
|
||||
|
@ -1233,6 +1249,7 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
|
|||
GstStructure *caps_struct;
|
||||
guint val;
|
||||
GstClockTime tval;
|
||||
const gchar *ts_refclk, *mediaclk;
|
||||
|
||||
priv = jitterbuffer->priv;
|
||||
|
||||
|
@ -1297,6 +1314,75 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
|
|||
"npt start/stop: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
|
||||
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;
|
||||
|
||||
/* ERRORS */
|
||||
|
|
|
@ -98,6 +98,15 @@ rtp_jitter_buffer_finalize (GObject * 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_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;
|
||||
}
|
||||
|
||||
/* 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:
|
||||
* @jbuf: an #RTPJitterBuffer
|
||||
|
@ -759,6 +856,37 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item,
|
|||
default:
|
||||
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
|
||||
* receive dts, this function will return the skew corrected rtptime. */
|
||||
item->pts = calculate_skew (jbuf, rtptime, dts);
|
||||
|
|
|
@ -102,6 +102,11 @@ struct _RTPJitterBuffer {
|
|||
gint64 skew;
|
||||
gint64 prev_send_diff;
|
||||
gboolean buffering_disabled;
|
||||
|
||||
GstClock *pipeline_clock;
|
||||
GstClock *media_clock;
|
||||
gulong media_clock_synced_id;
|
||||
guint64 media_clock_offset;
|
||||
};
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
gboolean rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf,
|
||||
|
|
Loading…
Reference in a new issue