• 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 /**
23  * SECTION:element-avtpaafpay
24  * @see_also: avtpaafdepay
25  *
26  * Payload raw audio into AVTPDUs according to IEEE 1722-2016. For detailed
27  * information see https://standards.ieee.org/standard/1722-2016.html.
28  *
29  * <refsect2>
30  * <title>Example pipeline</title>
31  * |[
32  * gst-launch-1.0 audiotestsrc ! audioconvert ! avtpaafpay ! avtpsink
33  * ]| This example pipeline will payload raw audio. Refer to the avtpaafdepay
34  * example to depayload and play the AVTP stream.
35  * </refsect2>
36  */
37 
38 #include <avtp.h>
39 #include <avtp_aaf.h>
40 #include <gst/audio/audio-format.h>
41 
42 #include "gstavtpaafpay.h"
43 
44 GST_DEBUG_CATEGORY_STATIC (avtpaafpay_debug);
45 #define GST_CAT_DEFAULT (avtpaafpay_debug)
46 
47 #define DEFAULT_TIMESTAMP_MODE GST_AVTP_AAF_TIMESTAMP_MODE_NORMAL
48 
49 enum
50 {
51   PROP_0,
52   PROP_TIMESTAMP_MODE,
53 };
54 
55 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
56     GST_PAD_SINK,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS ("audio/x-raw, "
59         "format = (string) { S16BE, S24BE, S32BE, F32BE }, "
60         "rate = (int) { 8000, 16000, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }, "
61         "channels = " GST_AUDIO_CHANNELS_RANGE ", "
62         "layout = (string) interleaved")
63     );
64 
65 #define GST_TYPE_AVTP_AAF_TIMESTAMP_MODE (gst_avtp_aaf_timestamp_mode_get_type())
66 static GType
gst_avtp_aaf_timestamp_mode_get_type(void)67 gst_avtp_aaf_timestamp_mode_get_type (void)
68 {
69   static const GEnumValue timestamp_mode_types[] = {
70     {GST_AVTP_AAF_TIMESTAMP_MODE_NORMAL, "Normal timestamping mode", "normal"},
71     {GST_AVTP_AAF_TIMESTAMP_MODE_SPARSE, "Sparse timestamping mode", "sparse"},
72     {0, NULL, NULL},
73   };
74   static gsize id = 0;
75 
76   if (g_once_init_enter (&id)) {
77     GType new_type;
78 
79     new_type =
80         g_enum_register_static ("GstAvtpAafTimestampMode",
81         timestamp_mode_types);
82 
83     g_once_init_leave (&id, (gsize) new_type);
84   }
85 
86   return (GType) id;
87 }
88 
89 #define gst_avtp_aaf_pay_parent_class parent_class
90 G_DEFINE_TYPE (GstAvtpAafPay, gst_avtp_aaf_pay, GST_TYPE_AVTP_BASE_PAYLOAD);
91 GST_ELEMENT_REGISTER_DEFINE (avtpaafpay, "avtpaafpay", GST_RANK_NONE,
92     GST_TYPE_AVTP_AAF_PAY);
93 
94 static void gst_avtp_aaf_pay_set_property (GObject * object, guint prop_id,
95     const GValue * value, GParamSpec * pspec);
96 static void gst_avtp_aaf_pay_get_property (GObject * object, guint prop_id,
97     GValue * value, GParamSpec * pspec);
98 
99 static GstStateChangeReturn gst_avtp_aaf_pay_change_state (GstElement * element,
100     GstStateChange transition);
101 
102 static GstFlowReturn gst_avtp_aaf_pay_chain (GstPad * pad, GstObject * parent,
103     GstBuffer * buffer);
104 static gboolean gst_avtp_aaf_pay_sink_event (GstPad * pad, GstObject * parent,
105     GstEvent * event);
106 
107 static void
gst_avtp_aaf_pay_class_init(GstAvtpAafPayClass * klass)108 gst_avtp_aaf_pay_class_init (GstAvtpAafPayClass * klass)
109 {
110   GObjectClass *object_class = G_OBJECT_CLASS (klass);
111   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
112   GstAvtpBasePayloadClass *avtpbasepayload_class =
113       GST_AVTP_BASE_PAYLOAD_CLASS (klass);
114 
115   object_class->set_property = gst_avtp_aaf_pay_set_property;
116   object_class->get_property = gst_avtp_aaf_pay_get_property;
117 
118   g_object_class_install_property (object_class, PROP_TIMESTAMP_MODE,
119       g_param_spec_enum ("timestamp-mode", "Timestamping Mode",
120           "AAF timestamping mode", GST_TYPE_AVTP_AAF_TIMESTAMP_MODE,
121           DEFAULT_TIMESTAMP_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
122           GST_PARAM_MUTABLE_PAUSED));
123 
124   element_class->change_state =
125       GST_DEBUG_FUNCPTR (gst_avtp_aaf_pay_change_state);
126 
127   gst_element_class_add_static_pad_template (element_class, &sink_template);
128 
129   gst_element_class_set_static_metadata (element_class,
130       "AVTP Audio Format (AAF) payloader",
131       "Codec/Payloader/Network/AVTP",
132       "Payload-encode Raw audio into AAF AVTPDU (IEEE 1722)",
133       "Andre Guedes <andre.guedes@intel.com>");
134 
135   avtpbasepayload_class->chain = GST_DEBUG_FUNCPTR (gst_avtp_aaf_pay_chain);
136   avtpbasepayload_class->sink_event =
137       GST_DEBUG_FUNCPTR (gst_avtp_aaf_pay_sink_event);
138 
139   GST_DEBUG_CATEGORY_INIT (avtpaafpay_debug, "avtpaafpay", 0,
140       "AAF AVTP Payloader");
141 
142   gst_type_mark_as_plugin_api (GST_TYPE_AVTP_AAF_TIMESTAMP_MODE, 0);
143 }
144 
145 static void
gst_avtp_aaf_pay_init(GstAvtpAafPay * avtpaafpay)146 gst_avtp_aaf_pay_init (GstAvtpAafPay * avtpaafpay)
147 {
148   avtpaafpay->timestamp_mode = DEFAULT_TIMESTAMP_MODE;
149 
150   avtpaafpay->header = NULL;
151   avtpaafpay->channels = 0;
152   avtpaafpay->depth = 0;
153   avtpaafpay->rate = 0;
154   avtpaafpay->format = 0;
155 }
156 
157 static void
gst_avtp_aaf_pay_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)158 gst_avtp_aaf_pay_set_property (GObject * object, guint prop_id,
159     const GValue * value, GParamSpec * pspec)
160 {
161   GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (object);
162 
163   GST_DEBUG_OBJECT (avtpaafpay, "prop_id %u", prop_id);
164 
165   switch (prop_id) {
166     case PROP_TIMESTAMP_MODE:
167       avtpaafpay->timestamp_mode = g_value_get_enum (value);
168       break;
169     default:
170       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
171       break;
172   }
173 }
174 
175 static void
gst_avtp_aaf_pay_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)176 gst_avtp_aaf_pay_get_property (GObject * object, guint prop_id,
177     GValue * value, GParamSpec * pspec)
178 {
179   GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (object);
180 
181   GST_DEBUG_OBJECT (avtpaafpay, "prop_id %u", prop_id);
182 
183   switch (prop_id) {
184     case PROP_TIMESTAMP_MODE:
185       g_value_set_enum (value, avtpaafpay->timestamp_mode);
186       break;
187     default:
188       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
189       break;
190   }
191 }
192 
193 static GstStateChangeReturn
gst_avtp_aaf_pay_change_state(GstElement * element,GstStateChange transition)194 gst_avtp_aaf_pay_change_state (GstElement * element, GstStateChange transition)
195 {
196   GstStateChangeReturn ret;
197   GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (element);
198 
199   GST_DEBUG_OBJECT (avtpaafpay, "transition %d", transition);
200 
201   switch (transition) {
202     case GST_STATE_CHANGE_NULL_TO_READY:{
203       GstMemory *mem;
204 
205       mem = gst_allocator_alloc (NULL, sizeof (struct avtp_stream_pdu), NULL);
206       if (!mem) {
207         GST_ERROR_OBJECT (avtpaafpay, "Failed to allocate GstMemory");
208         return GST_STATE_CHANGE_FAILURE;
209       }
210       avtpaafpay->header = mem;
211       break;
212     }
213     case GST_STATE_CHANGE_READY_TO_PAUSED:{
214       int res;
215       GstMapInfo info;
216       struct avtp_stream_pdu *pdu;
217       GstMemory *mem = avtpaafpay->header;
218       GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (element);
219 
220       if (!gst_memory_map (mem, &info, GST_MAP_WRITE)) {
221         GST_ERROR_OBJECT (avtpaafpay, "Failed to map GstMemory");
222         return GST_STATE_CHANGE_FAILURE;
223       }
224       pdu = (struct avtp_stream_pdu *) info.data;
225       res = avtp_aaf_pdu_init (pdu);
226       g_assert (res == 0);
227       res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_MR, 0);
228       g_assert (res == 0);
229       res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TV, 1);
230       g_assert (res == 0);
231       res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TU, 0);
232       g_assert (res == 0);
233       res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_STREAM_ID,
234           avtpbasepayload->streamid);
235       g_assert (res == 0);
236       res =
237           avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_SP, avtpaafpay->timestamp_mode);
238       g_assert (res == 0);
239       gst_memory_unmap (mem, &info);
240       break;
241     }
242     default:
243       break;
244   }
245 
246   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
247   if (ret == GST_STATE_CHANGE_FAILURE) {
248     GST_ERROR_OBJECT (avtpaafpay, "Parent failed to handle state transition");
249     return ret;
250   }
251 
252   switch (transition) {
253     case GST_STATE_CHANGE_READY_TO_NULL:
254       gst_memory_unref (avtpaafpay->header);
255       break;
256     default:
257       break;
258   }
259 
260   return ret;
261 }
262 
263 static GstFlowReturn
gst_avtp_aaf_pay_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)264 gst_avtp_aaf_pay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
265 {
266   int res;
267   GstMemory *mem;
268   GstMapInfo info;
269   gsize data_len;
270   GstClockTime ptime;
271   struct avtp_stream_pdu *pdu;
272   GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (parent);
273   GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (parent);
274 
275   ptime = gst_avtp_base_payload_calc_ptime (avtpbasepayload, buffer);
276   data_len = gst_buffer_get_size (buffer);
277 
278   mem = gst_memory_copy (avtpaafpay->header, 0, -1);
279   if (!gst_memory_map (mem, &info, GST_MAP_WRITE)) {
280     GST_ELEMENT_ERROR (avtpaafpay, RESOURCE, WRITE, ("Failed to map memory"),
281         (NULL));
282     gst_buffer_unref (buffer);
283     return GST_FLOW_ERROR;
284   }
285   pdu = (struct avtp_stream_pdu *) info.data;
286   res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TIMESTAMP, ptime);
287   g_assert (res == 0);
288   res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_NSR, avtpaafpay->rate);
289   g_assert (res == 0);
290   res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_FORMAT, avtpaafpay->format);
291   g_assert (res == 0);
292   res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_BIT_DEPTH, avtpaafpay->depth);
293   g_assert (res == 0);
294   res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN, data_len);
295   g_assert (res == 0);
296   res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME,
297       avtpaafpay->channels);
298   g_assert (res == 0);
299   res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_SEQ_NUM,
300       avtpbasepayload->seqnum++);
301   g_assert (res == 0);
302   gst_memory_unmap (mem, &info);
303 
304   gst_buffer_prepend_memory (buffer, mem);
305   return gst_pad_push (avtpbasepayload->srcpad, buffer);
306 }
307 
308 static int
gst_to_avtp_rate(gint rate)309 gst_to_avtp_rate (gint rate)
310 {
311   switch (rate) {
312     case 8000:
313       return AVTP_AAF_PCM_NSR_8KHZ;
314     case 16000:
315       return AVTP_AAF_PCM_NSR_16KHZ;
316     case 24000:
317       return AVTP_AAF_PCM_NSR_24KHZ;
318     case 32000:
319       return AVTP_AAF_PCM_NSR_32KHZ;
320     case 44100:
321       return AVTP_AAF_PCM_NSR_44_1KHZ;
322     case 48000:
323       return AVTP_AAF_PCM_NSR_48KHZ;
324     case 88200:
325       return AVTP_AAF_PCM_NSR_88_2KHZ;
326     case 96000:
327       return AVTP_AAF_PCM_NSR_96KHZ;
328     case 176400:
329       return AVTP_AAF_PCM_NSR_176_4KHZ;
330     case 192000:
331       return AVTP_AAF_PCM_NSR_192KHZ;
332     default:
333       return AVTP_AAF_PCM_NSR_USER;
334   }
335 }
336 
337 static int
gst_to_avtp_format(GstAudioFormat format)338 gst_to_avtp_format (GstAudioFormat format)
339 {
340   switch (format) {
341     case GST_AUDIO_FORMAT_S16BE:
342       return AVTP_AAF_FORMAT_INT_16BIT;
343     case GST_AUDIO_FORMAT_S24BE:
344       return AVTP_AAF_FORMAT_INT_24BIT;
345     case GST_AUDIO_FORMAT_S32BE:
346       return AVTP_AAF_FORMAT_INT_32BIT;
347     case GST_AUDIO_FORMAT_F32BE:
348       return AVTP_AAF_FORMAT_FLOAT_32BIT;
349     default:
350       return AVTP_AAF_FORMAT_USER;
351   }
352 }
353 
354 static gboolean
gst_avtp_aaf_pay_new_caps(GstAvtpAafPay * avtpaafpay,GstCaps * caps)355 gst_avtp_aaf_pay_new_caps (GstAvtpAafPay * avtpaafpay, GstCaps * caps)
356 {
357   GstAudioInfo info;
358 
359   gst_audio_info_init (&info);
360   if (!gst_audio_info_from_caps (&info, caps)) {
361     GST_ERROR_OBJECT (avtpaafpay, "Failed to get info from caps");
362     return FALSE;
363   }
364 
365   avtpaafpay->channels = info.channels;
366   avtpaafpay->depth = info.finfo->depth;
367   avtpaafpay->rate = gst_to_avtp_rate (info.rate);
368   avtpaafpay->format = gst_to_avtp_format (info.finfo->format);
369 
370   GST_DEBUG_OBJECT (avtpaafpay, "channels %d, depth %d, rate %d, format %s",
371       info.channels, info.finfo->depth, info.rate,
372       gst_audio_format_to_string (info.finfo->format));
373   return TRUE;
374 }
375 
376 static gboolean
gst_avtp_aaf_pay_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)377 gst_avtp_aaf_pay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
378 {
379   GstCaps *caps;
380   GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (parent);
381   gboolean ret;
382 
383   GST_DEBUG_OBJECT (avtpaafpay, "event %s", GST_EVENT_TYPE_NAME (event));
384 
385   switch (GST_EVENT_TYPE (event)) {
386     case GST_EVENT_CAPS:
387       gst_event_parse_caps (event, &caps);
388       ret = gst_avtp_aaf_pay_new_caps (avtpaafpay, caps);
389       gst_event_unref (event);
390       return ret;
391     default:
392       return GST_AVTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (pad,
393           parent, event);
394   }
395 }
396