gstreamer/ext/avtp/gstavtpcrfcheck.c
Vedang Patel e47fa2006f avtp: Introduce the CRF Check element
This commit introduces the AVTP Clock Reference Format (CRF) Checker
element. This element re-uses the GstAvtpCrfBase class introduced along
with the CRF Synchronizer element.

This element will typically be used along with the avtpsrc element to
ensure that the AVTP timestamp (and H264 timestamp in case of CVF-H264
packets) is "aligned" with the incoming CRF stream. Here, "aligned" means
that the timestamp value should be within 25% of the period of the media
clock recovered from the CRF stream.

The user can also set an option (drop-invalid) in order to drop any packet
whose timestamp is not within the thresholds of the incoming CRF stream.
2020-04-30 23:31:25 +00:00

263 lines
8.4 KiB
C

/*
* GStreamer AVTP Plugin
* Copyright (C) 2019 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-avtpcrfcheck
* @see_also: avtpcrfsync
*
* Validate whether the presentation time for the AVTPDU aligns with the CRF
* stream. For detailed information see Chapter 10 of
* https://standards.ieee.org/standard/1722-2016.html.
*
* <refsect2>
* <title>Example pipeline</title>
* |[
* gst-launch-1.0 avtpsrc ! avtpcrfcheck ! avtpaafdepay ! autoaudiosink
* ]| This example pipeline will validate AVTP timestamps for AVTPDUs. Refer to
* the avtpcrfsync to adjust the AVTP timestamps for the packet.
* </refsect2>
*/
#include <avtp.h>
#include <avtp_crf.h>
#include <avtp_cvf.h>
#include <glib.h>
#include <math.h>
#include "gstavtpcrfbase.h"
#include "gstavtpcrfcheck.h"
#include "gstavtpcrfutil.h"
GST_DEBUG_CATEGORY_STATIC (avtpcrfcheck_debug);
#define GST_CAT_DEFAULT (avtpcrfcheck_debug)
#define DEFAULT_DROP_INVALID FALSE
enum
{
PROP_0,
PROP_DROP_INVALID,
};
#define gst_avtp_crf_check_parent_class parent_class
G_DEFINE_TYPE (GstAvtpCrfCheck, gst_avtp_crf_check, GST_TYPE_AVTP_CRF_BASE);
static void gst_avtp_crf_check_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_avtp_crf_check_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GstFlowReturn gst_avtp_crf_check_transform_ip (GstBaseTransform * parent,
GstBuffer * buffer);
static void
gst_avtp_crf_check_class_init (GstAvtpCrfCheckClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_set_static_metadata (element_class,
"Clock Reference Format (CRF) Checker",
"Filter/Network/AVTP",
"Check if the AVTP presentation time is synchronized with clock provided by a CRF stream",
"Vedang Patel <vedang.patel@intel.com>");
object_class->get_property =
GST_DEBUG_FUNCPTR (gst_avtp_crf_check_get_property);
object_class->set_property =
GST_DEBUG_FUNCPTR (gst_avtp_crf_check_set_property);
GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
GST_DEBUG_FUNCPTR (gst_avtp_crf_check_transform_ip);
g_object_class_install_property (object_class, PROP_DROP_INVALID,
g_param_spec_boolean ("drop-invalid", "Drop invalid packets",
"Drop the packets which are not within 25%% of the sample period of the CRF timestamps",
DEFAULT_DROP_INVALID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
GST_DEBUG_CATEGORY_INIT (avtpcrfcheck_debug, "avtpcrfcheck", 0,
"CRF Checker");
}
static void
gst_avtp_crf_check_init (GstAvtpCrfCheck * avtpcrfcheck)
{
avtpcrfcheck->drop_invalid = DEFAULT_DROP_INVALID;
}
static void
gst_avtp_crf_check_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstAvtpCrfCheck *avtpcrfcheck = GST_AVTP_CRF_CHECK (object);
GST_DEBUG_OBJECT (avtpcrfcheck, "prop_id %u", prop_id);
switch (prop_id) {
case PROP_DROP_INVALID:
avtpcrfcheck->drop_invalid = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_avtp_crf_check_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstAvtpCrfCheck *avtpcrfcheck = GST_AVTP_CRF_CHECK (object);
GST_DEBUG_OBJECT (avtpcrfcheck, "prop_id %u", prop_id);
switch (prop_id) {
case PROP_DROP_INVALID:
g_value_set_boolean (value, avtpcrfcheck->drop_invalid);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
post_qos_message (GstBaseTransform * parent, GstBuffer * buffer)
{
GstAvtpCrfBase *avtpcrfbase = GST_AVTP_CRF_BASE (parent);
guint64 running_time =
gst_segment_to_running_time (&avtpcrfbase->element.segment,
GST_FORMAT_TIME, GST_BUFFER_DTS_OR_PTS (buffer));
guint64 stream_time =
gst_segment_to_running_time (&avtpcrfbase->element.segment,
GST_FORMAT_TIME, GST_BUFFER_DTS_OR_PTS (buffer));
guint64 timestamp = GST_BUFFER_DTS_OR_PTS (buffer);
guint64 duration = GST_BUFFER_DURATION (buffer);
GstMessage *qos_msg =
gst_message_new_qos (GST_OBJECT (parent), FALSE, running_time,
stream_time, timestamp, duration);
gst_element_post_message (GST_ELEMENT (parent), qos_msg);
}
static GstFlowReturn
gst_avtp_crf_check_transform_ip (GstBaseTransform * parent, GstBuffer * buffer)
{
GstAvtpCrfBase *avtpcrfbase = GST_AVTP_CRF_BASE (parent);
GstAvtpCrfCheck *avtpcrfcheck = GST_AVTP_CRF_CHECK (avtpcrfbase);
GstAvtpCrfThreadData *thread_data = &avtpcrfbase->thread_data;
GstClockTime current_ts = thread_data->current_ts;
gdouble avg_period = thread_data->average_period;
GstClockTime tstamp, adjusted_tstamp;
struct avtp_stream_pdu *pdu;
GstClockTime h264_time;
GstMapInfo info;
gboolean res;
if (!avg_period || !current_ts)
return GST_FLOW_OK;
res = gst_buffer_map (buffer, &info, GST_MAP_READ);
if (!res) {
GST_ELEMENT_ERROR (avtpcrfcheck, RESOURCE, OPEN_WRITE,
("cannot access buffer"), (NULL));
return GST_FLOW_ERROR;
}
if (!buffer_size_valid (&info)) {
GST_DEBUG_OBJECT (avtpcrfcheck, "Malformed AVTPDU, discarding it");
goto exit;
}
pdu = (struct avtp_stream_pdu *) info.data;
if (h264_tstamp_valid (pdu)) {
GstClockTime adjusted_h264_time;
res = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, &h264_time);
g_assert (res == 0);
/* Extrapolate tstamp to 64 bit and assume it's greater than CRF timestamp. */
h264_time |= current_ts & 0xFFFFFFFF00000000;
if (h264_time < current_ts)
h264_time += (1ULL << 32);
/*
* float typecasted to guint64 truncates the decimal part. So, round() it
* before casting.
*/
adjusted_h264_time =
(GstClockTime) roundl (current_ts + roundl ((h264_time -
current_ts) / avg_period) * avg_period);
if (llabs ((gint64) adjusted_h264_time - (gint64) h264_time) >
0.25 * thread_data->average_period) {
GST_LOG_OBJECT (avtpcrfcheck,
"H264 timestamp not synchronized. Expected: %" G_GUINT64_FORMAT
" Actual: %" G_GUINT64_FORMAT,
adjusted_h264_time & 0xFFFFFFFF, h264_time & 0xFFFFFFFF);
if (avtpcrfcheck->drop_invalid) {
post_qos_message (parent, buffer);
gst_buffer_unmap (buffer, &info);
return GST_BASE_TRANSFORM_FLOW_DROPPED;
}
}
}
tstamp = get_avtp_tstamp (avtpcrfbase, pdu);
if (tstamp == GST_CLOCK_TIME_NONE)
goto exit;
/*
* Extrapolate the 32-bit AVTP Timestamp to 64-bit and assume it's greater
* than the 64-bit CRF timestamp.
*/
tstamp |= current_ts & 0xFFFFFFFF00000000;
if (tstamp < current_ts)
tstamp += (1ULL << 32);
/*
* float typecasted to guint64 truncates the decimal part. So, round() it
* before casting.
*/
adjusted_tstamp =
(GstClockTime) roundl (current_ts + roundl ((tstamp -
current_ts) / avg_period) * avg_period);
if (llabs ((gint64) adjusted_tstamp - (gint64) tstamp) >
0.25 * thread_data->average_period) {
GST_LOG_OBJECT (avtpcrfcheck,
"AVTP Timestamp not synchronized. Expected: %" G_GUINT64_FORMAT
" Actual: %" G_GUINT64_FORMAT,
adjusted_tstamp & 0xFFFFFFFF, tstamp & 0xFFFFFFFF);
if (avtpcrfcheck->drop_invalid) {
post_qos_message (parent, buffer);
gst_buffer_unmap (buffer, &info);
return GST_BASE_TRANSFORM_FLOW_DROPPED;
}
}
exit:
gst_buffer_unmap (buffer, &info);
return GST_FLOW_OK;
}
gboolean
gst_avtp_crf_check_plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "avtpcrfcheck", GST_RANK_NONE,
GST_TYPE_AVTP_CRF_CHECK);
}