• 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 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