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 Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later
9 * version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
20 */
21
22 #include "gstavtpbasedepayload.h"
23
24 GST_DEBUG_CATEGORY_STATIC (avtpbasedepayload_debug);
25 #define GST_CAT_DEFAULT (avtpbasedepayload_debug)
26
27 #define DEFAULT_STREAMID 0xAABBCCDDEEFF0000
28
29 enum
30 {
31 PROP_0,
32 PROP_STREAMID,
33 };
34
35 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
36 GST_PAD_SINK,
37 GST_PAD_ALWAYS,
38 GST_STATIC_CAPS ("application/x-avtp")
39 );
40
41 static void gst_avtp_base_depayload_class_init (GstAvtpBaseDepayloadClass *
42 klass);
43 static void gst_avtp_base_depayload_init (GstAvtpBaseDepayload *
44 avtpbasedepayload, gpointer g_class);
45
46 static void gst_avtp_base_depayload_set_property (GObject * object,
47 guint prop_id, const GValue * value, GParamSpec * pspec);
48 static void gst_avtp_base_depayload_get_property (GObject * object,
49 guint prop_id, GValue * value, GParamSpec * pspec);
50
51 static gboolean gst_avtp_base_depayload_sink_event (GstPad * pad,
52 GstObject * parent, GstEvent * event);
53
54 GType
gst_avtp_base_depayload_get_type(void)55 gst_avtp_base_depayload_get_type (void)
56 {
57 static GType avtpbasedepayload_type = 0;
58
59 if (g_once_init_enter ((gsize *) & avtpbasedepayload_type)) {
60 static const GTypeInfo avtpbasedepayload_info = {
61 sizeof (GstAvtpBaseDepayloadClass),
62 NULL,
63 NULL,
64 (GClassInitFunc) gst_avtp_base_depayload_class_init,
65 NULL,
66 NULL,
67 sizeof (GstAvtpBaseDepayload),
68 0,
69 (GInstanceInitFunc) gst_avtp_base_depayload_init,
70 };
71 GType _type;
72
73 _type = g_type_register_static (GST_TYPE_ELEMENT, "GstAvtpBaseDepayload",
74 &avtpbasedepayload_info, G_TYPE_FLAG_ABSTRACT);
75
76 g_once_init_leave ((gsize *) & avtpbasedepayload_type, _type);
77 }
78 return avtpbasedepayload_type;
79 }
80
81 static void
gst_avtp_base_depayload_class_init(GstAvtpBaseDepayloadClass * klass)82 gst_avtp_base_depayload_class_init (GstAvtpBaseDepayloadClass * klass)
83 {
84 GObjectClass *object_class = G_OBJECT_CLASS (klass);
85
86 object_class->set_property = gst_avtp_base_depayload_set_property;
87 object_class->get_property = gst_avtp_base_depayload_get_property;
88
89 g_object_class_install_property (object_class, PROP_STREAMID,
90 g_param_spec_uint64 ("streamid", "Stream ID",
91 "Stream ID associated with the AVTPDU", 0, G_MAXUINT64,
92 DEFAULT_STREAMID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
93 GST_PARAM_MUTABLE_PAUSED));
94
95 klass->chain = NULL;
96 klass->sink_event = GST_DEBUG_FUNCPTR (gst_avtp_base_depayload_sink_event);
97
98 GST_DEBUG_CATEGORY_INIT (avtpbasedepayload_debug, "avtpbasedepayload", 0,
99 "Base class for AVTP depayloaders");
100
101 gst_type_mark_as_plugin_api (GST_TYPE_AVTP_BASE_DEPAYLOAD, 0);
102 }
103
104 static void
gst_avtp_base_depayload_init(GstAvtpBaseDepayload * avtpbasedepayload,gpointer g_class)105 gst_avtp_base_depayload_init (GstAvtpBaseDepayload * avtpbasedepayload,
106 gpointer g_class)
107 {
108 GstPadTemplate *templ;
109 GstElement *element = GST_ELEMENT (avtpbasedepayload);
110 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
111 GstAvtpBaseDepayloadClass *avtpbasedepayload_class =
112 GST_AVTP_BASE_DEPAYLOAD_CLASS (g_class);
113
114 g_assert (avtpbasedepayload_class->chain != NULL);
115
116 templ = gst_element_class_get_pad_template (element_class, "src");
117 g_assert (templ != NULL);
118 avtpbasedepayload->srcpad = gst_pad_new_from_template (templ, "src");
119 gst_pad_use_fixed_caps (avtpbasedepayload->srcpad);
120 gst_element_add_pad (element, avtpbasedepayload->srcpad);
121
122 avtpbasedepayload->sinkpad =
123 gst_pad_new_from_static_template (&sink_template, "sink");
124 gst_pad_set_chain_function (avtpbasedepayload->sinkpad,
125 avtpbasedepayload_class->chain);
126 gst_pad_set_event_function (avtpbasedepayload->sinkpad,
127 avtpbasedepayload_class->sink_event);
128 gst_element_add_pad (element, avtpbasedepayload->sinkpad);
129
130 avtpbasedepayload->streamid = DEFAULT_STREAMID;
131
132 avtpbasedepayload->prev_ptime = 0;
133 avtpbasedepayload->seqnum = 0;
134 }
135
136 static void
gst_avtp_base_depayload_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)137 gst_avtp_base_depayload_set_property (GObject * object, guint prop_id,
138 const GValue * value, GParamSpec * pspec)
139 {
140 GstAvtpBaseDepayload *avtpbasedepayload = GST_AVTP_BASE_DEPAYLOAD (object);
141
142 GST_DEBUG_OBJECT (avtpbasedepayload, "prop_id %u", prop_id);
143
144 switch (prop_id) {
145 case PROP_STREAMID:
146 avtpbasedepayload->streamid = g_value_get_uint64 (value);
147 break;
148 default:
149 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150 break;
151 }
152 }
153
154 static void
gst_avtp_base_depayload_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)155 gst_avtp_base_depayload_get_property (GObject * object, guint prop_id,
156 GValue * value, GParamSpec * pspec)
157 {
158 GstAvtpBaseDepayload *avtpbasedepayload = GST_AVTP_BASE_DEPAYLOAD (object);
159
160 GST_DEBUG_OBJECT (avtpbasedepayload, "prop_id %u", prop_id);
161
162 switch (prop_id) {
163 case PROP_STREAMID:
164 g_value_set_uint64 (value, avtpbasedepayload->streamid);
165 break;
166 default:
167 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
168 break;
169 }
170 }
171
172 static gboolean
gst_avtp_base_depayload_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)173 gst_avtp_base_depayload_sink_event (GstPad * pad, GstObject * parent,
174 GstEvent * event)
175 {
176 GstAvtpBaseDepayload *avtpbasedepayload = GST_AVTP_BASE_DEPAYLOAD (parent);
177
178 GST_DEBUG_OBJECT (avtpbasedepayload, "event %s", GST_EVENT_TYPE_NAME (event));
179
180 switch (GST_EVENT_TYPE (event)) {
181 case GST_EVENT_SEGMENT:
182 /* Once the first AVTPDU is received, proper CAPS and SEGMENT events are
183 * pushed downstream. These events are expected to be pushed in that
184 * order by GStreamer. Since the default handling implemented by
185 * gst_pad_event_default() pushes the SEGMENT event downstream right
186 * away, it doesn't work for us and we have to handle it ourselves.
187 *
188 * Our handling is very straightforward: we discard this event and send
189 * a proper segment event once the first AVTPDU is received. See
190 * gst_avtp_base_depayload_push_segment_event() for more information.
191 */
192 gst_event_unref (event);
193 return TRUE;
194 default:
195 return gst_pad_event_default (pad, parent, event);
196 }
197 }
198
199 /* Helper function to convert AVTP timestamp to AVTP presentation time. Since
200 * AVTP timestamp represents the lower 32-bit part from AVTP presentation time,
201 * the helper requires a reference time ('ref' argument) to convert it properly.
202 * The reference time must be in gstreamer clock-time coordinate.
203 */
204 GstClockTime
gst_avtp_base_depayload_tstamp_to_ptime(GstAvtpBaseDepayload * avtpbasedepayload,guint32 tstamp,GstClockTime ref)205 gst_avtp_base_depayload_tstamp_to_ptime (GstAvtpBaseDepayload *
206 avtpbasedepayload, guint32 tstamp, GstClockTime ref)
207 {
208 GstClockTime ptime;
209
210 ptime = (ref & 0xFFFFFFFF00000000ULL) | tstamp;
211
212 /* If 'ptime' is less than the our reference time, it means the higher part
213 * from 'ptime' needs to be incremented by 1 in order reflect the correct
214 * presentation time.
215 */
216 if (ptime < ref)
217 ptime += (1ULL << 32);
218
219 GST_LOG_OBJECT (avtpbasedepayload, "AVTP presentation time %" GST_TIME_FORMAT,
220 GST_TIME_ARGS (ptime));
221 return ptime;
222 }
223
224 gboolean
gst_avtp_base_depayload_push_segment_event(GstAvtpBaseDepayload * avtpbasedepayload,guint32 avtp_tstamp)225 gst_avtp_base_depayload_push_segment_event (GstAvtpBaseDepayload *
226 avtpbasedepayload, guint32 avtp_tstamp)
227 {
228 GstClock *clock;
229 GstEvent *event;
230 GstSegment segment;
231 GstClockTime now, base_time, avtp_ptime;
232
233 clock = GST_ELEMENT_CLOCK (avtpbasedepayload);
234
235 now = gst_clock_get_time (clock);
236 avtp_ptime =
237 gst_avtp_base_depayload_tstamp_to_ptime (avtpbasedepayload, avtp_tstamp,
238 now);
239 base_time = gst_element_get_base_time (GST_ELEMENT (avtpbasedepayload));
240
241 gst_segment_init (&segment, GST_FORMAT_TIME);
242 segment.base = avtp_ptime - base_time;
243 segment.start = avtp_ptime;
244 segment.stop = -1;
245
246 event = gst_event_new_segment (&segment);
247 if (!event) {
248 GST_ERROR_OBJECT (avtpbasedepayload, "Failed to create SEGMENT event");
249 return FALSE;
250 }
251
252 if (!gst_pad_push_event (avtpbasedepayload->srcpad, event)) {
253 GST_ERROR_OBJECT (avtpbasedepayload, "Failed to push SEGMENT event");
254 return FALSE;
255 }
256
257 GST_DEBUG_OBJECT (avtpbasedepayload, "SEGMENT event pushed: %"
258 GST_SEGMENT_FORMAT, &segment);
259
260 avtpbasedepayload->prev_ptime = avtp_ptime;
261 return TRUE;
262 }
263