• 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  modify it
6  * under the terms of the GNU Library General Public  License as published by
7  * the Free Software Foundation; either  version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,  but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.
13  *
14  * See the GNU Library General Public License for more details.   You should
15  * have received a copy of the GNU Library General Public License along with
16  * this library; if not , write to the Free Software Foundation, Inc., 51
17  * Franklin St, Fifth Floor, Boston, MA 02110 - 1301, USA.
18  */
19 
20 #include <arpa/inet.h>
21 #include <avtp.h>
22 #include <avtp_crf.h>
23 #include <glib.h>
24 #include <math.h>
25 #include <net/ethernet.h>
26 #include <net/if.h>
27 #include <stdio.h>
28 #include <sys/socket.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include "gstavtpcrfutil.h"
32 #include "gstavtpcrfbase.h"
33 
34 GST_DEBUG_CATEGORY_STATIC (avtpcrfbase_debug);
35 #define GST_CAT_DEFAULT (avtpcrfbase_debug)
36 
37 #define CRF_TIMESTAMP_SIZE 8
38 #define MAX_AVTPDU_SIZE 1500
39 #define MAX_NUM_PERIODS_STORED 10
40 #define RECV_TIMEOUT 1          // in seconds
41 
42 #define DEFAULT_STREAMID 0xAABBCCDDEEFF1000
43 #define DEFAULT_IFNAME "eth0"
44 #define DEFAULT_ADDRESS "01:AA:AA:AA:AA:AA"
45 
46 enum
47 {
48   PROP_0,
49   PROP_STREAMID,
50   PROP_IFNAME,
51   PROP_ADDRESS,
52 };
53 
54 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
55     GST_PAD_SRC,
56     GST_PAD_ALWAYS,
57     GST_STATIC_CAPS ("application/x-avtp")
58     );
59 
60 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
61     GST_PAD_SINK,
62     GST_PAD_ALWAYS,
63     GST_STATIC_CAPS ("application/x-avtp")
64     );
65 
66 static void gst_avtp_crf_base_finalize (GObject * gobject);
67 static void
68 gst_avtp_crf_base_set_property (GObject * object, guint prop_id,
69     const GValue * value, GParamSpec * pspec);
70 static void
71 gst_avtp_crf_base_get_property (GObject * object, guint prop_id,
72     GValue * value, GParamSpec * pspec);
73 static GstStateChangeReturn gst_avtp_crf_base_change_state (GstElement *
74     element, GstStateChange transition);
75 static void crf_listener_thread_func (GstAvtpCrfBase * avtpcrfbase);
76 
77 #define gst_avtp_crf_base_parent_class parent_class
78 G_DEFINE_TYPE (GstAvtpCrfBase, gst_avtp_crf_base, GST_TYPE_BASE_TRANSFORM);
79 
80 static void
gst_avtp_crf_base_class_init(GstAvtpCrfBaseClass * klass)81 gst_avtp_crf_base_class_init (GstAvtpCrfBaseClass * klass)
82 {
83   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
84   GObjectClass *object_class = G_OBJECT_CLASS (klass);
85 
86   object_class->finalize = GST_DEBUG_FUNCPTR (gst_avtp_crf_base_finalize);
87   object_class->get_property =
88       GST_DEBUG_FUNCPTR (gst_avtp_crf_base_get_property);
89   object_class->set_property =
90       GST_DEBUG_FUNCPTR (gst_avtp_crf_base_set_property);
91 
92   g_object_class_install_property (object_class, PROP_STREAMID,
93       g_param_spec_uint64 ("streamid", "Stream ID",
94           "Stream ID associated with the CRF AVTPDU", 0, G_MAXUINT64,
95           DEFAULT_STREAMID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
96           GST_PARAM_MUTABLE_READY));
97   g_object_class_install_property (object_class, PROP_IFNAME,
98       g_param_spec_string ("ifname", "Interface Name",
99           "Network interface utilized to receive CRF AVTPDUs",
100           DEFAULT_IFNAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
101           GST_PARAM_MUTABLE_READY));
102   g_object_class_install_property (object_class, PROP_ADDRESS,
103       g_param_spec_string ("address", "Destination MAC address",
104           "Destination MAC address expected on the Ethernet frames",
105           DEFAULT_ADDRESS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
106           GST_PARAM_MUTABLE_READY));
107 
108   element_class->change_state =
109       GST_DEBUG_FUNCPTR (gst_avtp_crf_base_change_state);
110 
111   gst_element_class_add_static_pad_template (element_class, &sink_template);
112   gst_element_class_add_static_pad_template (element_class, &src_template);
113 
114   GST_DEBUG_CATEGORY_INIT (avtpcrfbase_debug, "avtpcrfbase", 0, "CRF Base");
115 
116   gst_type_mark_as_plugin_api (GST_TYPE_AVTP_CRF_BASE, 0);
117 }
118 
119 static void
gst_avtp_crf_base_init(GstAvtpCrfBase * avtpcrfbase)120 gst_avtp_crf_base_init (GstAvtpCrfBase * avtpcrfbase)
121 {
122   avtpcrfbase->streamid = DEFAULT_STREAMID;
123   avtpcrfbase->ifname = g_strdup (DEFAULT_IFNAME);
124   avtpcrfbase->address = g_strdup (DEFAULT_ADDRESS);
125 }
126 
127 static GstStateChangeReturn
gst_avtp_crf_base_change_state(GstElement * element,GstStateChange transition)128 gst_avtp_crf_base_change_state (GstElement * element, GstStateChange transition)
129 {
130   GstAvtpCrfBase *avtpcrfbase = GST_AVTP_CRF_BASE (element);
131   GstAvtpCrfThreadData *thread_data = &avtpcrfbase->thread_data;
132   GstStateChangeReturn res;
133   GError *error = NULL;
134 
135   GST_DEBUG_OBJECT (avtpcrfbase, "transition %d", transition);
136 
137   switch (transition) {
138     case GST_STATE_CHANGE_NULL_TO_READY:
139       thread_data->past_periods =
140           g_malloc0 (sizeof (thread_data->past_periods[0]) *
141           MAX_NUM_PERIODS_STORED);
142       thread_data->mr = -1;
143       thread_data->is_running = TRUE;
144       thread_data->thread =
145           g_thread_try_new ("crf-listener",
146           (GThreadFunc) crf_listener_thread_func, avtpcrfbase, &error);
147 
148       if (error) {
149         GST_ERROR_OBJECT (avtpcrfbase, "failed to start thread, %s",
150             error->message);
151         g_error_free (error);
152         g_free (thread_data->past_periods);
153         return GST_STATE_CHANGE_FAILURE;
154       }
155       break;
156     default:
157       break;
158   }
159 
160   res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
161 
162   switch (transition) {
163     case GST_STATE_CHANGE_READY_TO_NULL:
164       thread_data->is_running = FALSE;
165       g_thread_join (thread_data->thread);
166       g_free (thread_data->past_periods);
167       break;
168     default:
169       break;
170   }
171 
172   return res;
173 }
174 
175 static int
setup_socket(GstAvtpCrfBase * avtpcrfbase)176 setup_socket (GstAvtpCrfBase * avtpcrfbase)
177 {
178   struct sockaddr_ll sk_addr = { 0 };
179   struct packet_mreq mreq = { 0 };
180   struct timeval timeout = { 0 };
181   guint8 addr[ETH_ALEN];
182   int fd, res, ifindex;
183 
184   fd = socket (AF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
185   if (fd < 0) {
186     GST_ERROR_OBJECT (avtpcrfbase, "Failed to open socket: %s",
187         g_strerror (errno));
188     return fd;
189   }
190 
191   ifindex = if_nametoindex (avtpcrfbase->ifname);
192   if (!ifindex) {
193     res = -1;
194     GST_ERROR_OBJECT (avtpcrfbase, "Failed to get index for interface: %s",
195         g_strerror (errno));
196     goto err;
197   }
198 
199   sk_addr.sll_family = AF_PACKET;
200   sk_addr.sll_protocol = htons (ETH_P_ALL);
201   sk_addr.sll_ifindex = ifindex;
202 
203   res = bind (fd, (struct sockaddr *) &sk_addr, sizeof (sk_addr));
204   if (res < 0) {
205     GST_ERROR_OBJECT (avtpcrfbase, "Failed to bind socket: %s",
206         g_strerror (errno));
207     goto err;
208   }
209 
210   res = sscanf (avtpcrfbase->address, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
211       &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]);
212   if (res != 6) {
213     res = -1;
214     GST_ERROR_OBJECT (avtpcrfbase, "Destination MAC address format not valid");
215     goto err;
216   }
217 
218   mreq.mr_ifindex = ifindex;
219   mreq.mr_type = PACKET_MR_MULTICAST;
220   mreq.mr_alen = ETH_ALEN;
221   memcpy (&mreq.mr_address, addr, ETH_ALEN);
222   res = setsockopt (fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
223       sizeof (struct packet_mreq));
224   if (res < 0) {
225     GST_ERROR_OBJECT (avtpcrfbase, "Failed to set multicast address: %s",
226         g_strerror (errno));
227     goto err;
228   }
229 
230   timeout.tv_sec = RECV_TIMEOUT;
231   res =
232       setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, (void *) &timeout,
233       sizeof (struct timeval));
234   if (res < 0) {
235     GST_ERROR_OBJECT (avtpcrfbase, "Failed to set receive timeout: %s",
236         g_strerror (errno));
237     goto err;
238   }
239 
240   return fd;
241 
242 err:
243   close (fd);
244   return res;
245 }
246 
247 static gboolean
validate_crf_pdu(GstAvtpCrfBase * avtpcrfbase,struct avtp_crf_pdu * crf_pdu,int packet_size)248 validate_crf_pdu (GstAvtpCrfBase * avtpcrfbase, struct avtp_crf_pdu *crf_pdu,
249     int packet_size)
250 {
251   GstAvtpCrfThreadData *data = &avtpcrfbase->thread_data;
252   guint64 tstamp_interval, base_freq, pull, type;
253   guint64 streamid_valid, streamid, data_len;
254   guint32 subtype;
255   int res;
256 
257   if (packet_size < sizeof (struct avtp_crf_pdu))
258     return FALSE;
259 
260   res = avtp_pdu_get ((struct avtp_common_pdu *) crf_pdu, AVTP_FIELD_SUBTYPE,
261       &subtype);
262   g_assert (res == 0);
263   if (subtype != AVTP_SUBTYPE_CRF) {
264     GST_DEBUG_OBJECT (avtpcrfbase, "Not a CRF PDU.subtype: %u", subtype);
265     return FALSE;
266   }
267 
268   res = avtp_crf_pdu_get (crf_pdu, AVTP_CRF_FIELD_SV, &streamid_valid);
269   g_assert (res == 0);
270   res = avtp_crf_pdu_get (crf_pdu, AVTP_CRF_FIELD_STREAM_ID, &streamid);
271   g_assert (res == 0);
272   res = avtp_crf_pdu_get (crf_pdu, AVTP_CRF_FIELD_CRF_DATA_LEN, &data_len);
273   g_assert (res == 0);
274   res = avtp_crf_pdu_get (crf_pdu, AVTP_CRF_FIELD_TIMESTAMP_INTERVAL,
275       (guint64 *) & tstamp_interval);
276   g_assert (res == 0);
277   res = avtp_crf_pdu_get (crf_pdu, AVTP_CRF_FIELD_BASE_FREQ, &base_freq);
278   g_assert (res == 0);
279   res = avtp_crf_pdu_get (crf_pdu, AVTP_CRF_FIELD_PULL, &pull);
280   g_assert (res == 0);
281   res = avtp_crf_pdu_get (crf_pdu, AVTP_CRF_FIELD_TYPE, &type);
282   g_assert (res == 0);
283 
284   if (!streamid_valid || streamid != avtpcrfbase->streamid) {
285     GST_DEBUG_OBJECT (avtpcrfbase,
286         "Stream ID doesn't match. Discarding CRF packet");
287     return FALSE;
288   }
289 
290   if (G_UNLIKELY (data_len + sizeof (struct avtp_crf_pdu) > packet_size)) {
291     GST_DEBUG_OBJECT (avtpcrfbase,
292         "Packet size smaller than expected. Discarding CRF packet");
293     return FALSE;
294   }
295 
296   if (G_UNLIKELY (!data->timestamp_interval)) {
297     if (G_UNLIKELY (tstamp_interval == 0)) {
298       GST_DEBUG_OBJECT (avtpcrfbase,
299           "timestamp_interval should not be zero. Discarding CRF packet");
300       return FALSE;
301     }
302     data->timestamp_interval = tstamp_interval;
303 
304     if (G_UNLIKELY (base_freq == 0)) {
305       GST_DEBUG_OBJECT (avtpcrfbase,
306           "Base Frequency cannot be zero, Discarding CRF packet");
307       goto error;
308     }
309     data->base_freq = base_freq;
310 
311     if (G_UNLIKELY (pull > AVTP_CRF_PULL_MULT_BY_1_OVER_8)) {
312       GST_DEBUG_OBJECT (avtpcrfbase,
313           "Pull value invalid, Discarding CRF packet");
314       goto error;
315     }
316     data->pull = pull;
317 
318     if (G_UNLIKELY (type > AVTP_CRF_TYPE_MACHINE_CYCLE)) {
319       GST_DEBUG_OBJECT (avtpcrfbase,
320           "CRF timestamp type invalid, Discarding CRF packet");
321       goto error;
322     }
323     data->type = type;
324 
325     if (G_UNLIKELY (!data_len || data_len % 8 != 0)) {
326       GST_DEBUG_OBJECT (avtpcrfbase,
327           "Data Length should be a multiple of 8. Discarding CRF packet.");
328       goto error;
329     }
330     data->num_pkt_tstamps = data_len / CRF_TIMESTAMP_SIZE;
331   } else {
332     if (G_UNLIKELY (tstamp_interval != data->timestamp_interval)) {
333       GST_DEBUG_OBJECT (avtpcrfbase,
334           "Timestamp interval doesn't match, discarding CRF packet");
335       return FALSE;
336     }
337 
338     if (G_UNLIKELY (base_freq != data->base_freq)) {
339       GST_DEBUG_OBJECT (avtpcrfbase,
340           "Base Frequency doesn't match, discarding CRF packet");
341       return FALSE;
342     }
343 
344     if (G_UNLIKELY (pull != data->pull)) {
345       GST_DEBUG_OBJECT (avtpcrfbase,
346           "Pull value doesn't match, discarding CRF packet");
347       return FALSE;
348     }
349 
350     if (G_UNLIKELY (data->type != type)) {
351       GST_DEBUG_OBJECT (avtpcrfbase,
352           "CRF timestamp type doesn't match, Discarding CRF packet");
353       return FALSE;
354     }
355 
356     if (G_UNLIKELY (data_len / CRF_TIMESTAMP_SIZE != data->num_pkt_tstamps)) {
357       GST_DEBUG_OBJECT (avtpcrfbase,
358           "Number of timestamps doesn't match. discarding CRF packet");
359       return FALSE;
360     }
361   }
362 
363   /* Make sure all the timestamps are monotonically increasing. */
364   for (int i = 0; i < data->num_pkt_tstamps - 1; i++) {
365     GstClockTime tstamp, next_tstamp;
366 
367     tstamp = be64toh (crf_pdu->crf_data[i]);
368     next_tstamp = be64toh (crf_pdu->crf_data[i + 1]);
369     if (G_UNLIKELY (tstamp >= next_tstamp)) {
370       GST_DEBUG_OBJECT (avtpcrfbase,
371           "Timestamps are not monotonically increasing. discarding CRF packet");
372       return FALSE;
373     }
374   }
375 
376   return TRUE;
377 
378 error:
379   data->timestamp_interval = 0;
380   return FALSE;
381 }
382 
383 static gdouble
get_base_freq_multiplier(GstAvtpCrfBase * avtpcrfbase,guint64 pull)384 get_base_freq_multiplier (GstAvtpCrfBase * avtpcrfbase, guint64 pull)
385 {
386   switch (pull) {
387     case 0:
388       return 1.0;
389     case 1:
390       return 1 / 1.001;
391     case 2:
392       return 1.001;
393     case 3:
394       return 24.0 / 25;
395     case 4:
396       return 25.0 / 24;
397     case 5:
398       return 1.0 / 8;
399     default:
400       GST_ERROR_OBJECT (avtpcrfbase, "Invalid pull value");
401       return -1;
402   }
403 }
404 
405 static void
calculate_average_period(GstAvtpCrfBase * avtpcrfbase,struct avtp_crf_pdu * crf_pdu)406 calculate_average_period (GstAvtpCrfBase * avtpcrfbase,
407     struct avtp_crf_pdu *crf_pdu)
408 {
409   GstAvtpCrfThreadData *data = &avtpcrfbase->thread_data;
410   GstClockTime first_pkt_tstamp, last_pkt_tstamp;
411   int num_pkt_tstamps, past_periods_iter;
412   gdouble accumulate_period = 0;
413 
414   num_pkt_tstamps = data->num_pkt_tstamps;
415   past_periods_iter = data->past_periods_iter;
416   first_pkt_tstamp = be64toh (crf_pdu->crf_data[0]);
417   last_pkt_tstamp = be64toh (crf_pdu->crf_data[num_pkt_tstamps - 1]);
418 
419   /*
420    * If there is only one CRF Timestamp per CRF AVTPU, at least two packets are
421    * needed to calculate the period. Also, sequence number needs to be checked
422    * to ensure consecutive packets are being used to calculate the period.
423    * Otherwise, we will just use the nominal frequency to estimate period.
424    */
425   if (num_pkt_tstamps == 1) {
426     guint64 seqnum;
427     int res;
428 
429     res = avtp_crf_pdu_get (crf_pdu, AVTP_CRF_FIELD_SEQ_NUM, &seqnum);
430     g_assert (res == 0);
431 
432     if (!data->last_received_tstamp ||
433         ((data->last_seqnum + 1) % 255 != seqnum)) {
434       gdouble average_period = data->average_period;
435 
436       if (!data->last_received_tstamp) {
437         gdouble base_freq_mult;
438 
439         base_freq_mult = get_base_freq_multiplier (avtpcrfbase, data->pull);
440         if (base_freq_mult < 0)
441           return;
442 
443         average_period =
444             gst_util_uint64_scale (1.0, 1000000000,
445             (data->base_freq * base_freq_mult));
446       }
447       data->last_received_tstamp = first_pkt_tstamp;
448       data->last_seqnum = seqnum;
449       data->current_ts = first_pkt_tstamp;
450       data->average_period = average_period;
451       return;
452     }
453 
454     data->past_periods[past_periods_iter] =
455         (gdouble) (first_pkt_tstamp - data->last_received_tstamp) /
456         data->timestamp_interval;
457     data->last_received_tstamp = first_pkt_tstamp;
458     data->last_seqnum = seqnum;
459   } else {
460     data->past_periods[past_periods_iter] =
461         (gdouble) (last_pkt_tstamp - first_pkt_tstamp) /
462         (data->timestamp_interval * (num_pkt_tstamps - 1));
463   }
464 
465   if (data->periods_stored < MAX_NUM_PERIODS_STORED)
466     data->periods_stored++;
467 
468   data->past_periods_iter = (past_periods_iter + 1) % data->periods_stored;
469 
470   for (int i = 0; i < data->periods_stored; i++)
471     accumulate_period += data->past_periods[i];
472 
473   data->average_period = accumulate_period / data->periods_stored;
474   data->current_ts = first_pkt_tstamp;
475 }
476 
477 static void
crf_listener_thread_func(GstAvtpCrfBase * avtpcrfbase)478 crf_listener_thread_func (GstAvtpCrfBase * avtpcrfbase)
479 {
480   GstAvtpCrfThreadData *data = &avtpcrfbase->thread_data;
481   struct avtp_crf_pdu *crf_pdu = g_alloca (MAX_AVTPDU_SIZE);
482   guint64 media_clk_reset;
483   int fd, n, res;
484 
485   fd = setup_socket (avtpcrfbase);
486   if (fd < 0) {
487     GST_ELEMENT_ERROR (avtpcrfbase, RESOURCE, OPEN_READ,
488         ("Cannot open socket for CRF Listener"), (NULL));
489     return;
490   }
491 
492   while (data->is_running) {
493     n = recv (fd, crf_pdu, MAX_AVTPDU_SIZE, 0);
494 
495     if (n == -1) {
496       if (errno == EAGAIN || errno == EINTR)
497         continue;
498 
499       GST_ERROR_OBJECT (avtpcrfbase, "Failed to receive packet: %s",
500           g_strerror (errno));
501       break;
502     }
503 
504     if (!validate_crf_pdu (avtpcrfbase, crf_pdu, n))
505       continue;
506 
507     GST_DEBUG_OBJECT (avtpcrfbase, "Packet valid. Adding to buffer\n");
508 
509     res = avtp_crf_pdu_get (crf_pdu, AVTP_CRF_FIELD_MR, &media_clk_reset);
510     g_assert (res == 0);
511 
512     if (media_clk_reset != data->mr) {
513       memset (data->past_periods, 0,
514           sizeof (data->past_periods[0]) * MAX_NUM_PERIODS_STORED);
515       data->periods_stored = 0;
516       data->average_period = 0;
517       data->current_ts = 0;
518       data->last_received_tstamp = 0;
519       data->past_periods_iter = 0;
520       data->mr = media_clk_reset;
521     }
522 
523     calculate_average_period (avtpcrfbase, crf_pdu);
524   }
525 
526   close (fd);
527 }
528 
529 static void
gst_avtp_crf_base_finalize(GObject * object)530 gst_avtp_crf_base_finalize (GObject * object)
531 {
532   GstAvtpCrfBase *avtpcrfbase = GST_AVTP_CRF_BASE (object);
533 
534   g_free (avtpcrfbase->ifname);
535   g_free (avtpcrfbase->address);
536 
537   G_OBJECT_CLASS (parent_class)->finalize (object);
538 }
539 
540 static void
gst_avtp_crf_base_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)541 gst_avtp_crf_base_set_property (GObject * object, guint prop_id,
542     const GValue * value, GParamSpec * pspec)
543 {
544   GstAvtpCrfBase *avtpcrfbase = GST_AVTP_CRF_BASE (object);
545 
546   GST_DEBUG_OBJECT (avtpcrfbase, "prop_id %u", prop_id);
547 
548   switch (prop_id) {
549     case PROP_STREAMID:
550       avtpcrfbase->streamid = g_value_get_uint64 (value);
551       break;
552     case PROP_IFNAME:
553       g_free (avtpcrfbase->ifname);
554       avtpcrfbase->ifname = g_value_dup_string (value);
555       break;
556     case PROP_ADDRESS:
557       g_free (avtpcrfbase->address);
558       avtpcrfbase->address = g_value_dup_string (value);
559       break;
560     default:
561       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
562       break;
563   }
564 }
565 
566 static void
gst_avtp_crf_base_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)567 gst_avtp_crf_base_get_property (GObject * object, guint prop_id,
568     GValue * value, GParamSpec * pspec)
569 {
570   GstAvtpCrfBase *avtpcrfbase = GST_AVTP_CRF_BASE (object);
571 
572   GST_DEBUG_OBJECT (avtpcrfbase, "prop_id %u", prop_id);
573 
574   switch (prop_id) {
575     case PROP_STREAMID:
576       g_value_set_uint64 (value, avtpcrfbase->streamid);
577       break;
578     case PROP_IFNAME:
579       g_value_set_string (value, avtpcrfbase->ifname);
580       break;
581     case PROP_ADDRESS:
582       g_value_set_string (value, avtpcrfbase->address);
583       break;
584     default:
585       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
586       break;
587   }
588 }
589