• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer AVTP Plugin
3  * Copyright (C) 2019 Intel Corporation
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-avtpcrfsync
23  * @see_also: avtpcrfcheck
24  *
25  * Adjust the Presentation Time from AVTPDUs to align with the reference clock
26  * provided by the CRF stream. For detailed information see chapter 10 in
27  * https://standards.ieee.org/standard/1722-2016.html. A helpful aid for
28  * visualizing CRF and it's advantages can be found at
29  * http://grouper.ieee.org/groups/1722/contributions/2014/1722a-rsilfvast-Diagrams%20for%20Common%20Timing%20Grid%20and%20Presentation%20Time%20(for%20review%20and%20discussion).pdf
30  * (Look at page 1).
31  *
32  * <refsect2>
33  * <title>Example pipeline</title>
34  * |[
35  * gst-launch-1.0 audiotestsrc ! audioconvert ! avtpaafpay ! avtpcrfsync ! avtpsink
36  * ]| This example pipeline will adjust the timestamps for rawaudio payload.
37  * Refer to the avtpcrfcheck example to validate the adjusted timestamp.
38  * </refsect2>
39  */
40 
41 #include <avtp.h>
42 #include <avtp_aaf.h>
43 #include <avtp_crf.h>
44 #include <avtp_cvf.h>
45 #include <glib.h>
46 #include <math.h>
47 
48 #include "gstavtpcrfbase.h"
49 #include "gstavtpcrfsync.h"
50 #include "gstavtpcrfutil.h"
51 
52 GST_DEBUG_CATEGORY_STATIC (avtpcrfsync_debug);
53 #define GST_CAT_DEFAULT (avtpcrfsync_debug)
54 
55 #define gst_avtp_crf_sync_parent_class parent_class
56 G_DEFINE_TYPE (GstAvtpCrfSync, gst_avtp_crf_sync, GST_TYPE_AVTP_CRF_BASE);
57 GST_ELEMENT_REGISTER_DEFINE (avtpcrfsync, "avtpcrfsync", GST_RANK_NONE,
58     GST_TYPE_AVTP_CRF_SYNC);
59 static GstFlowReturn gst_avtp_crf_sync_transform_ip (GstBaseTransform * parent,
60     GstBuffer * buffer);
61 
62 static void
gst_avtp_crf_sync_class_init(GstAvtpCrfSyncClass * klass)63 gst_avtp_crf_sync_class_init (GstAvtpCrfSyncClass * klass)
64 {
65   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
66 
67   gst_element_class_set_static_metadata (element_class,
68       "Clock Reference Format (CRF) Synchronizer",
69       "Filter/Network/AVTP",
70       "Synchronize Presentation Time from AVTPDUs so they are phase-locked with clock provided by CRF stream",
71       "Vedang Patel <vedang.patel@intel.com>");
72 
73   GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
74       GST_DEBUG_FUNCPTR (gst_avtp_crf_sync_transform_ip);
75 
76   GST_DEBUG_CATEGORY_INIT (avtpcrfsync_debug, "avtpcrfsync", 0,
77       "CRF Synchronizer");
78 }
79 
80 static void
gst_avtp_crf_sync_init(GstAvtpCrfSync * avtpcrfsync)81 gst_avtp_crf_sync_init (GstAvtpCrfSync * avtpcrfsync)
82 {
83   /* Nothing to do here. */
84 }
85 
86 static void
set_avtp_tstamp(GstAvtpCrfSync * avtpcrfsync,struct avtp_stream_pdu * pdu,GstClockTime tstamp)87 set_avtp_tstamp (GstAvtpCrfSync * avtpcrfsync, struct avtp_stream_pdu *pdu,
88     GstClockTime tstamp)
89 {
90   int res;
91   guint32 type;
92 
93   res =
94       avtp_pdu_get ((struct avtp_common_pdu *) pdu, AVTP_FIELD_SUBTYPE, &type);
95   g_assert (res == 0);
96 
97   switch (type) {
98     case AVTP_SUBTYPE_AAF:
99       res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TIMESTAMP, tstamp);
100       g_assert (res == 0);
101       break;
102     case AVTP_SUBTYPE_CVF:
103       res = avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, tstamp);
104       g_assert (res == 0);
105       break;
106     default:
107       GST_ERROR_OBJECT (avtpcrfsync, "type 0x%x not supported.\n", type);
108       break;
109   }
110 }
111 
112 static void
set_avtp_mr_bit(GstAvtpCrfSync * avtpcrfsync,struct avtp_stream_pdu * pdu,guint64 mr)113 set_avtp_mr_bit (GstAvtpCrfSync * avtpcrfsync, struct avtp_stream_pdu *pdu,
114     guint64 mr)
115 {
116   int res;
117   guint32 type;
118 
119   res =
120       avtp_pdu_get ((struct avtp_common_pdu *) pdu, AVTP_FIELD_SUBTYPE, &type);
121   g_assert (res == 0);
122 
123   switch (type) {
124     case AVTP_SUBTYPE_AAF:
125       res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_MR, mr);
126       g_assert (res == 0);
127       break;
128     case AVTP_SUBTYPE_CVF:
129       res = avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_MR, mr);
130       g_assert (res == 0);
131       break;
132     default:
133       GST_ERROR_OBJECT (avtpcrfsync, "type 0x%x not supported.\n", type);
134       break;
135   }
136 }
137 
138 static GstFlowReturn
gst_avtp_crf_sync_transform_ip(GstBaseTransform * parent,GstBuffer * buffer)139 gst_avtp_crf_sync_transform_ip (GstBaseTransform * parent, GstBuffer * buffer)
140 {
141   GstClockTime tstamp, h264_time = 0, adjusted_tstamp, adjusted_h264_time = 0;
142   GstAvtpCrfBase *avtpcrfbase = GST_AVTP_CRF_BASE (parent);
143   GstAvtpCrfSync *avtpcrfsync = GST_AVTP_CRF_SYNC (avtpcrfbase);
144   GstAvtpCrfThreadData *thread_data = &avtpcrfbase->thread_data;
145   GstClockTime current_ts = thread_data->current_ts;
146   gdouble avg_period = thread_data->average_period;
147   struct avtp_stream_pdu *pdu;
148   gboolean h264_packet;
149   GstMapInfo info;
150   gboolean res;
151 
152   if (!avg_period || !current_ts) {
153     GST_WARNING_OBJECT (avtpcrfsync, "No CRF packet yet received!");
154     return GST_FLOW_OK;
155   }
156 
157   res = gst_buffer_map (buffer, &info, GST_MAP_READWRITE);
158   if (!res) {
159     GST_ELEMENT_ERROR (avtpcrfsync, RESOURCE, OPEN_WRITE,
160         ("cannot access buffer"), (NULL));
161     return GST_FLOW_ERROR;
162   }
163 
164   if (!buffer_size_valid (&info)) {
165     GST_DEBUG_OBJECT (avtpcrfsync, "Malformed AVTPDU, discarding it");
166     goto exit;
167   }
168 
169   pdu = (struct avtp_stream_pdu *) info.data;
170 
171   h264_packet = h264_tstamp_valid (pdu);
172 
173   if (h264_packet) {
174     res = avtp_cvf_pdu_get (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, &h264_time);
175     g_assert (res == 0);
176 
177     /*
178      * Extrapolate H264 tstamp to 64 bit and assume it's greater than CRF
179      * timestamp.
180      */
181     h264_time |= current_ts & 0xFFFFFFFF00000000;
182     if (h264_time < current_ts)
183       h264_time += (1ULL << 32);
184 
185     /*
186      * float typecasted to guint64 truncates the decimal part. So, round() it
187      * before casting.
188      */
189     adjusted_h264_time =
190         (GstClockTime) roundl (current_ts + ceill (((gdouble) h264_time -
191                 current_ts) / avg_period) * avg_period);
192     res =
193         avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP,
194         adjusted_h264_time);
195     g_assert (res == 0);
196 
197     GST_LOG_OBJECT (avtpcrfsync,
198         "Adjust H264 timestamp in CVF packet. tstamp: %" G_GUINT64_FORMAT
199         " adjusted_tstamp: %" G_GUINT64_FORMAT,
200         h264_time & 0xFFFFFFFF, adjusted_h264_time & 0xFFFFFFFF);
201   }
202 
203   tstamp = get_avtp_tstamp (avtpcrfbase, pdu);
204   if (tstamp == GST_CLOCK_TIME_NONE)
205     goto exit;
206 
207   /*
208    * Extrapolate the 32-bit AVTP Timestamp to 64-bit and assume it's greater
209    * than the 64-bit CRF timestamp.
210    */
211   tstamp |= current_ts & 0xFFFFFFFF00000000;
212   if (tstamp < current_ts)
213     tstamp += (1ULL << 32);
214 
215   /*
216    * float typecasted to guint64 truncates the decimal part. So, round() it
217    * before casting.
218    */
219   adjusted_tstamp =
220       (GstClockTime) roundl (current_ts + ceill ((tstamp -
221               current_ts) / avg_period) * avg_period);
222   set_avtp_tstamp (avtpcrfsync, pdu, adjusted_tstamp);
223   set_avtp_mr_bit (avtpcrfsync, pdu, thread_data->mr);
224   GST_LOG_OBJECT (avtpcrfsync,
225       "Adjust AVTP timestamp. tstamp: %" G_GUINT64_FORMAT
226       " Adjusted tstamp: %" G_GUINT64_FORMAT,
227       tstamp & 0xFFFFFFFF, adjusted_tstamp & 0xFFFFFFFF);
228 
229   /*
230    * Since we adjusted the AVTP/H264 presentation times in the AVTPDU, we also
231    * need to adjust buffer times by the same amount so that the buffer is
232    * transmitted at the right time.
233    */
234   if (h264_packet) {
235     if (GST_BUFFER_DTS (buffer) != GST_CLOCK_TIME_NONE)
236       GST_BUFFER_DTS (buffer) += adjusted_tstamp - tstamp;
237     GST_BUFFER_PTS (buffer) += adjusted_h264_time - h264_time;
238   } else {
239     GST_BUFFER_PTS (buffer) += adjusted_tstamp - tstamp;
240   }
241 
242 exit:
243   gst_buffer_unmap (buffer, &info);
244   return GST_FLOW_OK;
245 }
246