/* * 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 Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "../../../ext/avtp/gstavtpcrfsync.c" #undef GST_CAT_DEFAULT #include #include #include #include #include #include #include "../../../ext/avtp/gstavtpcrfutil.h" #define STREAM_ID 0xDEADC0DEDEADC0DE struct buffer_tstamps { GstClockTime buf_pts; GstClockTime buf_dts; GstClockTime avtp_ts; GstClockTime h264_ts; }; static GstHarness * setup_harness (void) { GstHarness *h; h = gst_harness_new_parse ("avtpcrfsync streamid=0xDEADC0DEDEADC0DE"); gst_harness_set_src_caps_str (h, "application/x-avtp"); return h; } static void fill_buffer_video_data (struct avtp_stream_pdu *pdu) { const gint DATA_LEN = sizeof (guint32) + 3; avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0); avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 0); avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); } static void fill_buffer_audio_data (struct avtp_stream_pdu *pdu) { const int DATA_LEN = 4; avtp_aaf_pdu_init (pdu); avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TV, 1); avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_STREAM_ID, 0xDEADC0DEDEADC0DE); avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_FORMAT, AVTP_AAF_FORMAT_INT_16BIT); avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_NSR, AVTP_AAF_PCM_NSR_48KHZ); avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME, 2); avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_BIT_DEPTH, 16); avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_SP, AVTP_AAF_PCM_SP_NORMAL); avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TIMESTAMP, 0); avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN, DATA_LEN); } static GstBuffer * create_input_buffer (GstHarness * h, guint32 subtype) { struct avtp_stream_pdu *pdu; GstMapInfo info; GstBuffer *buf; const gint DATA_LEN = sizeof (guint32) + 3; buf = gst_harness_create_buffer (h, sizeof (struct avtp_stream_pdu) + DATA_LEN); gst_buffer_map (buf, &info, GST_MAP_WRITE); pdu = (struct avtp_stream_pdu *) info.data; if (subtype == AVTP_SUBTYPE_AAF) fill_buffer_audio_data (pdu); else if (subtype == AVTP_SUBTYPE_CVF) fill_buffer_video_data (pdu); gst_buffer_unmap (buf, &info); return buf; } static void set_buffer_tstamps (GstBuffer * buf, struct buffer_tstamps *orig) { struct avtp_stream_pdu *pdu; GstMapInfo info; guint32 type; gst_buffer_map (buf, &info, GST_MAP_WRITE); pdu = (struct avtp_stream_pdu *) info.data; GST_BUFFER_PTS (buf) = orig->buf_pts; GST_BUFFER_DTS (buf) = orig->buf_dts; avtp_pdu_get ((struct avtp_common_pdu *) pdu, AVTP_FIELD_SUBTYPE, &type); if (type == AVTP_SUBTYPE_AAF) avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TIMESTAMP, orig->avtp_ts); else if (type == AVTP_SUBTYPE_CVF) { avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, orig->avtp_ts); avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, orig->h264_ts); } gst_buffer_unmap (buf, &info); } static void validate_tstamps (GstAvtpCrfBase * avtpcrfbase, GstBuffer * buf, struct buffer_tstamps *expected) { GstClockTime tstamp; struct avtp_stream_pdu *pdu; GstMapInfo info; int res; gst_buffer_map (buf, &info, GST_MAP_READ); pdu = (struct avtp_stream_pdu *) info.data; fail_unless_equals_uint64 (GST_BUFFER_PTS (buf), expected->buf_pts); fail_unless_equals_uint64 (GST_BUFFER_DTS (buf), expected->buf_dts); tstamp = get_avtp_tstamp (avtpcrfbase, pdu); fail_unless_equals_uint64 (tstamp, expected->avtp_ts); if (h264_tstamp_valid (pdu)) { res = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, &tstamp); fail_unless (res == 0); fail_unless_equals_uint64 (tstamp, expected->h264_ts); } gst_buffer_unmap (buf, &info); } static void test_crf_tstamps (GstHarness * h, GstBuffer * buf, struct buffer_tstamps *orig, struct buffer_tstamps *expected) { GstAvtpCrfBase *avtpcrfbase; GstBuffer *bufout; avtpcrfbase = (GstAvtpCrfBase *) gst_harness_find_element (h, "avtpcrfsync"); set_buffer_tstamps (buf, orig); bufout = gst_harness_push_and_pull (h, buf); validate_tstamps (avtpcrfbase, bufout, expected); gst_object_unref (avtpcrfbase); } GST_START_TEST (test_properties) { const guint64 streamid = 0xAABBCCDDEEFF0001; const gchar *address = "01:AA:BB:CC:DD:EE"; const gchar *ifname = "enp1s0"; GstElement *element; guint64 val64; gchar *str; element = gst_check_setup_element ("avtpcrfsync"); g_object_set (G_OBJECT (element), "ifname", ifname, NULL); g_object_get (G_OBJECT (element), "ifname", &str, NULL); fail_unless_equals_string (str, ifname); g_free (str); g_object_set (G_OBJECT (element), "address", address, NULL); g_object_get (G_OBJECT (element), "address", &str, NULL); fail_unless_equals_string (str, address); g_free (str); g_object_set (G_OBJECT (element), "streamid", streamid, NULL); g_object_get (G_OBJECT (element), "streamid", &val64, NULL); fail_unless_equals_uint64_hex (val64, streamid); gst_object_unref (element); } GST_END_TEST; GST_START_TEST (test_set_avtp_tstamp) { GstAvtpCrfSync *avtpcrfsync = g_object_new (GST_TYPE_AVTP_CRF_SYNC, NULL); struct avtp_stream_pdu pdu; GstClockTime tstamp; int res; avtp_aaf_pdu_init (&pdu); avtp_aaf_pdu_set (&pdu, AVTP_AAF_FIELD_TV, 1); set_avtp_tstamp (avtpcrfsync, &pdu, 12345); res = avtp_aaf_pdu_get (&pdu, AVTP_AAF_FIELD_TIMESTAMP, &tstamp); fail_unless (res == 0); fail_unless_equals_uint64 (tstamp, 12345); avtp_cvf_pdu_init (&pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); avtp_cvf_pdu_set (&pdu, AVTP_CVF_FIELD_TV, 1); set_avtp_tstamp (avtpcrfsync, &pdu, 12345); res = avtp_cvf_pdu_get (&pdu, AVTP_CVF_FIELD_TIMESTAMP, &tstamp); fail_unless (res == 0); fail_unless_equals_uint64 (tstamp, 12345); g_object_unref (avtpcrfsync); } GST_END_TEST; GST_START_TEST (test_set_avtp_mr_bit) { GstAvtpCrfSync *avtpcrfsync = g_object_new (GST_TYPE_AVTP_CRF_SYNC, NULL); struct avtp_stream_pdu pdu; guint64 mr_bit; int res; avtp_aaf_pdu_init (&pdu); set_avtp_mr_bit (avtpcrfsync, &pdu, 1); res = avtp_aaf_pdu_get (&pdu, AVTP_AAF_FIELD_MR, &mr_bit); fail_unless (res == 0); fail_unless_equals_uint64 (mr_bit, 1); avtp_cvf_pdu_init (&pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); set_avtp_mr_bit (avtpcrfsync, &pdu, 1); res = avtp_cvf_pdu_get (&pdu, AVTP_CVF_FIELD_MR, &mr_bit); fail_unless (res == 0); fail_unless_equals_uint64 (mr_bit, 1); g_object_unref (avtpcrfsync); } GST_END_TEST; GST_START_TEST (test_crf_cvf_data) { struct buffer_tstamps orig, expected; GstAvtpCrfBase *avtpcrfbase; GstBuffer *buf; GstHarness *h; h = setup_harness (); buf = create_input_buffer (h, AVTP_SUBTYPE_CVF); avtpcrfbase = (GstAvtpCrfBase *) gst_harness_find_element (h, "avtpcrfsync"); avtpcrfbase->thread_data.average_period = 3300; avtpcrfbase->thread_data.current_ts = 110000; gst_object_unref (avtpcrfbase); orig = (struct buffer_tstamps) { .buf_pts = 103000,.buf_dts = 100000,.avtp_ts = 110000,.h264_ts = 108000}; expected = (struct buffer_tstamps) { .buf_pts = 104204,.buf_dts = 100000,.avtp_ts = 110000,.h264_ts = 109204}; test_crf_tstamps (h, buf, &orig, &expected); orig = (struct buffer_tstamps) { .buf_pts = 107000,.buf_dts = 105000,.avtp_ts = 113000,.h264_ts = 118500}; expected = (struct buffer_tstamps) { .buf_pts = 108400,.buf_dts = 105300,.avtp_ts = 113300,.h264_ts = 119900}; test_crf_tstamps (h, buf, &orig, &expected); /* test for invalid DTS */ orig = (struct buffer_tstamps) { .buf_pts = 107000,.buf_dts = GST_CLOCK_TIME_NONE,.avtp_ts = 113000,.h264_ts = 118500}; expected = (struct buffer_tstamps) { .buf_pts = 108400,.buf_dts = GST_CLOCK_TIME_NONE,.avtp_ts = 113300,.h264_ts = 119900}; test_crf_tstamps (h, buf, &orig, &expected); gst_buffer_unref (buf); gst_harness_teardown (h); } GST_END_TEST; GST_START_TEST (test_crf_aaf_data) { struct buffer_tstamps orig, expected; GstAvtpCrfBase *avtpcrfbase; GstBuffer *buf; GstHarness *h; h = setup_harness (); buf = create_input_buffer (h, AVTP_SUBTYPE_AAF); avtpcrfbase = (GstAvtpCrfBase *) gst_harness_find_element (h, "avtpcrfsync"); avtpcrfbase->thread_data.average_period = 3300; avtpcrfbase->thread_data.current_ts = 110000; gst_object_unref (avtpcrfbase); orig = (struct buffer_tstamps) { .buf_pts = 108000,.buf_dts = 0,.avtp_ts = 110000,.h264_ts = 0}; expected = (struct buffer_tstamps) { .buf_pts = 108000,.buf_dts = 0,.avtp_ts = 110000,.h264_ts = 0}; test_crf_tstamps (h, buf, &orig, &expected); orig = (struct buffer_tstamps) { .buf_pts = 110000,.buf_dts = 0,.avtp_ts = 113000,.h264_ts = 0}; expected = (struct buffer_tstamps) { .buf_pts = 110300,.buf_dts = 0,.avtp_ts = 113300,.h264_ts = 0}; test_crf_tstamps (h, buf, &orig, &expected); gst_buffer_unref (buf); gst_harness_teardown (h); } GST_END_TEST; GST_START_TEST (test_crf_period_zero) { struct buffer_tstamps orig, expected; GstAvtpCrfBase *avtpcrfbase; GstBuffer *buf; GstHarness *h; h = setup_harness (); buf = create_input_buffer (h, AVTP_SUBTYPE_CVF); avtpcrfbase = (GstAvtpCrfBase *) gst_harness_find_element (h, "avtpcrfsync"); avtpcrfbase->thread_data.average_period = 0.0; avtpcrfbase->thread_data.current_ts = 110; gst_object_unref (avtpcrfbase); orig = (struct buffer_tstamps) { .buf_pts = 100,.buf_dts = 105,.avtp_ts = 112,.h264_ts = 110}; expected = (struct buffer_tstamps) { .buf_pts = 100,.buf_dts = 105,.avtp_ts = 112,.h264_ts = 110}; test_crf_tstamps (h, buf, &orig, &expected); gst_buffer_unref (buf); gst_harness_teardown (h); } GST_END_TEST; static Suite * avtpcrfsync_suite (void) { Suite *s = suite_create ("avtpcrfsync"); TCase *tc_chain = tcase_create ("general"); GST_DEBUG_CATEGORY_INIT (avtpcrfsync_debug, "avtpcrfsync", 0, "CRF Synchronizer"); suite_add_tcase (s, tc_chain); tcase_add_test (tc_chain, test_properties); tcase_add_test (tc_chain, test_set_avtp_tstamp); tcase_add_test (tc_chain, test_set_avtp_mr_bit); tcase_add_test (tc_chain, test_crf_cvf_data); tcase_add_test (tc_chain, test_crf_aaf_data); tcase_add_test (tc_chain, test_crf_period_zero); return s; } GST_CHECK_MAIN (avtpcrfsync);