1 /* GStreamer
2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
3 * Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
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:gstwebrtc-datachannel
23 * @short_description: RTCDataChannel object
24 * @title: GstWebRTCDataChannel
25 *
26 * <https://www.w3.org/TR/webrtc/#rtcdatachannel>
27 *
28 * Since: 1.18
29 */
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include "datachannel.h"
36 #include "webrtc-priv.h"
37
38 #define GST_CAT_DEFAULT gst_webrtc_data_channel_debug
39 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
40
41 #define gst_webrtc_data_channel_parent_class parent_class
42 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstWebRTCDataChannel, gst_webrtc_data_channel,
43 G_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_webrtc_data_channel_debug,
44 "webrtcdatachannel", 0, "webrtcdatachannel"););
45
46 enum
47 {
48 SIGNAL_0,
49 SIGNAL_ON_OPEN,
50 SIGNAL_ON_CLOSE,
51 SIGNAL_ON_ERROR,
52 SIGNAL_ON_MESSAGE_DATA,
53 SIGNAL_ON_MESSAGE_STRING,
54 SIGNAL_ON_BUFFERED_AMOUNT_LOW,
55 SIGNAL_SEND_DATA,
56 SIGNAL_SEND_STRING,
57 SIGNAL_CLOSE,
58 LAST_SIGNAL,
59 };
60
61 enum
62 {
63 PROP_0,
64 PROP_LABEL,
65 PROP_ORDERED,
66 PROP_MAX_PACKET_LIFETIME,
67 PROP_MAX_RETRANSMITS,
68 PROP_PROTOCOL,
69 PROP_NEGOTIATED,
70 PROP_ID,
71 PROP_PRIORITY,
72 PROP_READY_STATE,
73 PROP_BUFFERED_AMOUNT,
74 PROP_BUFFERED_AMOUNT_LOW_THRESHOLD,
75 };
76
77 static guint gst_webrtc_data_channel_signals[LAST_SIGNAL] = { 0 };
78
79 static void
gst_webrtc_data_channel_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)80 gst_webrtc_data_channel_set_property (GObject * object, guint prop_id,
81 const GValue * value, GParamSpec * pspec)
82 {
83 GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object);
84
85 GST_WEBRTC_DATA_CHANNEL_LOCK (channel);
86 switch (prop_id) {
87 case PROP_LABEL:
88 g_free (channel->label);
89 channel->label = g_value_dup_string (value);
90 break;
91 case PROP_ORDERED:
92 channel->ordered = g_value_get_boolean (value);
93 break;
94 case PROP_MAX_PACKET_LIFETIME:
95 channel->max_packet_lifetime = g_value_get_int (value);
96 break;
97 case PROP_MAX_RETRANSMITS:
98 channel->max_retransmits = g_value_get_int (value);
99 break;
100 case PROP_PROTOCOL:
101 g_free (channel->protocol);
102 channel->protocol = g_value_dup_string (value);
103 break;
104 case PROP_NEGOTIATED:
105 channel->negotiated = g_value_get_boolean (value);
106 break;
107 case PROP_ID:
108 channel->id = g_value_get_int (value);
109 break;
110 case PROP_PRIORITY:
111 channel->priority = g_value_get_enum (value);
112 break;
113 case PROP_BUFFERED_AMOUNT_LOW_THRESHOLD:
114 channel->buffered_amount_low_threshold = g_value_get_uint64 (value);
115 break;
116 default:
117 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118 break;
119 }
120 GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel);
121 }
122
123 static void
gst_webrtc_data_channel_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)124 gst_webrtc_data_channel_get_property (GObject * object, guint prop_id,
125 GValue * value, GParamSpec * pspec)
126 {
127 GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object);
128
129 GST_WEBRTC_DATA_CHANNEL_LOCK (channel);
130 switch (prop_id) {
131 case PROP_LABEL:
132 g_value_set_string (value, channel->label);
133 break;
134 case PROP_ORDERED:
135 g_value_set_boolean (value, channel->ordered);
136 break;
137 case PROP_MAX_PACKET_LIFETIME:
138 g_value_set_int (value, channel->max_packet_lifetime);
139 break;
140 case PROP_MAX_RETRANSMITS:
141 g_value_set_int (value, channel->max_retransmits);
142 break;
143 case PROP_PROTOCOL:
144 g_value_set_string (value, channel->protocol);
145 break;
146 case PROP_NEGOTIATED:
147 g_value_set_boolean (value, channel->negotiated);
148 break;
149 case PROP_ID:
150 g_value_set_int (value, channel->id);
151 break;
152 case PROP_PRIORITY:
153 g_value_set_enum (value, channel->priority);
154 break;
155 case PROP_READY_STATE:
156 g_value_set_enum (value, channel->ready_state);
157 break;
158 case PROP_BUFFERED_AMOUNT:
159 g_value_set_uint64 (value, channel->buffered_amount);
160 break;
161 case PROP_BUFFERED_AMOUNT_LOW_THRESHOLD:
162 g_value_set_uint64 (value, channel->buffered_amount_low_threshold);
163 break;
164 default:
165 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
166 break;
167 }
168 GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel);
169 }
170
171 static void
gst_webrtc_data_channel_finalize(GObject * object)172 gst_webrtc_data_channel_finalize (GObject * object)
173 {
174 GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object);
175
176 g_free (channel->label);
177 channel->label = NULL;
178
179 g_free (channel->protocol);
180 channel->protocol = NULL;
181
182 G_OBJECT_CLASS (parent_class)->finalize (object);
183 }
184
185 static void
gst_webrtc_data_channel_class_init(GstWebRTCDataChannelClass * klass)186 gst_webrtc_data_channel_class_init (GstWebRTCDataChannelClass * klass)
187 {
188 GObjectClass *gobject_class = (GObjectClass *) klass;
189
190 gobject_class->get_property = gst_webrtc_data_channel_get_property;
191 gobject_class->set_property = gst_webrtc_data_channel_set_property;
192 gobject_class->finalize = gst_webrtc_data_channel_finalize;
193
194 g_object_class_install_property (gobject_class,
195 PROP_LABEL,
196 g_param_spec_string ("label",
197 "Label", "Data channel label",
198 NULL,
199 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
200
201 g_object_class_install_property (gobject_class,
202 PROP_ORDERED,
203 g_param_spec_boolean ("ordered",
204 "Ordered", "Using ordered transmission mode",
205 FALSE,
206 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
207
208 g_object_class_install_property (gobject_class,
209 PROP_MAX_PACKET_LIFETIME,
210 g_param_spec_int ("max-packet-lifetime",
211 "Maximum Packet Lifetime",
212 "Maximum number of milliseconds that transmissions and "
213 "retransmissions may occur in unreliable mode (-1 = unset)",
214 -1, G_MAXUINT16, -1,
215 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
216
217 g_object_class_install_property (gobject_class,
218 PROP_MAX_RETRANSMITS,
219 g_param_spec_int ("max-retransmits",
220 "Maximum Retransmits",
221 "Maximum number of retransmissions attempted in unreliable mode",
222 -1, G_MAXUINT16, 0,
223 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
224
225 g_object_class_install_property (gobject_class,
226 PROP_PROTOCOL,
227 g_param_spec_string ("protocol",
228 "Protocol", "Data channel protocol",
229 "",
230 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
231
232 g_object_class_install_property (gobject_class,
233 PROP_NEGOTIATED,
234 g_param_spec_boolean ("negotiated",
235 "Negotiated",
236 "Whether this data channel was negotiated by the application", FALSE,
237 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
238
239 g_object_class_install_property (gobject_class,
240 PROP_ID,
241 g_param_spec_int ("id",
242 "ID",
243 "ID negotiated by this data channel (-1 = unset)",
244 -1, G_MAXUINT16, -1,
245 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
246
247 g_object_class_install_property (gobject_class,
248 PROP_PRIORITY,
249 g_param_spec_enum ("priority",
250 "Priority",
251 "The priority of data sent using this data channel",
252 GST_TYPE_WEBRTC_PRIORITY_TYPE,
253 GST_WEBRTC_PRIORITY_TYPE_LOW,
254 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
255
256 g_object_class_install_property (gobject_class,
257 PROP_READY_STATE,
258 g_param_spec_enum ("ready-state",
259 "Ready State",
260 "The Ready state of this data channel",
261 GST_TYPE_WEBRTC_DATA_CHANNEL_STATE,
262 GST_WEBRTC_DATA_CHANNEL_STATE_NEW,
263 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
264
265 g_object_class_install_property (gobject_class,
266 PROP_BUFFERED_AMOUNT,
267 g_param_spec_uint64 ("buffered-amount",
268 "Buffered Amount",
269 "The amount of data in bytes currently buffered",
270 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
271
272 g_object_class_install_property (gobject_class,
273 PROP_BUFFERED_AMOUNT_LOW_THRESHOLD,
274 g_param_spec_uint64 ("buffered-amount-low-threshold",
275 "Buffered Amount Low Threshold",
276 "The threshold at which the buffered amount is considered low and "
277 "the buffered-amount-low signal is emitted",
278 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
279
280 /**
281 * GstWebRTCDataChannel::on-open:
282 * @object: the #GstWebRTCDataChannel
283 */
284 gst_webrtc_data_channel_signals[SIGNAL_ON_OPEN] =
285 g_signal_new ("on-open", G_TYPE_FROM_CLASS (klass),
286 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
287
288 /**
289 * GstWebRTCDataChannel::on-close:
290 * @object: the #GstWebRTCDataChannel
291 */
292 gst_webrtc_data_channel_signals[SIGNAL_ON_CLOSE] =
293 g_signal_new ("on-close", G_TYPE_FROM_CLASS (klass),
294 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
295
296 /**
297 * GstWebRTCDataChannel::on-error:
298 * @object: the #GstWebRTCDataChannel
299 * @error: the #GError thrown
300 */
301 gst_webrtc_data_channel_signals[SIGNAL_ON_ERROR] =
302 g_signal_new ("on-error", G_TYPE_FROM_CLASS (klass),
303 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_ERROR);
304
305 /**
306 * GstWebRTCDataChannel::on-message-data:
307 * @object: the #GstWebRTCDataChannel
308 * @data: (nullable): a #GBytes of the data received
309 */
310 gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_DATA] =
311 g_signal_new ("on-message-data", G_TYPE_FROM_CLASS (klass),
312 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BYTES);
313
314 /**
315 * GstWebRTCDataChannel::on-message-string:
316 * @object: the #GstWebRTCDataChannel
317 * @data: (nullable): the data received as a string
318 */
319 gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_STRING] =
320 g_signal_new ("on-message-string", G_TYPE_FROM_CLASS (klass),
321 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING);
322
323 /**
324 * GstWebRTCDataChannel::on-buffered-amount-low:
325 * @object: the #GstWebRTCDataChannel
326 */
327 gst_webrtc_data_channel_signals[SIGNAL_ON_BUFFERED_AMOUNT_LOW] =
328 g_signal_new ("on-buffered-amount-low", G_TYPE_FROM_CLASS (klass),
329 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
330
331 /**
332 * GstWebRTCDataChannel::send-data:
333 * @object: the #GstWebRTCDataChannel
334 * @data: (nullable): a #GBytes with the data
335 */
336 gst_webrtc_data_channel_signals[SIGNAL_SEND_DATA] =
337 g_signal_new_class_handler ("send-data", G_TYPE_FROM_CLASS (klass),
338 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
339 G_CALLBACK (gst_webrtc_data_channel_send_data), NULL, NULL, NULL,
340 G_TYPE_NONE, 1, G_TYPE_BYTES);
341
342 /**
343 * GstWebRTCDataChannel::send-string:
344 * @object: the #GstWebRTCDataChannel
345 * @data: (nullable): the data to send as a string
346 */
347 gst_webrtc_data_channel_signals[SIGNAL_SEND_STRING] =
348 g_signal_new_class_handler ("send-string", G_TYPE_FROM_CLASS (klass),
349 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
350 G_CALLBACK (gst_webrtc_data_channel_send_string), NULL, NULL, NULL,
351 G_TYPE_NONE, 1, G_TYPE_STRING);
352
353 /**
354 * GstWebRTCDataChannel::close:
355 * @object: the #GstWebRTCDataChannel
356 *
357 * Close the data channel
358 */
359 gst_webrtc_data_channel_signals[SIGNAL_CLOSE] =
360 g_signal_new_class_handler ("close", G_TYPE_FROM_CLASS (klass),
361 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
362 G_CALLBACK (gst_webrtc_data_channel_close), NULL, NULL, NULL,
363 G_TYPE_NONE, 0);
364 }
365
366 static void
gst_webrtc_data_channel_init(GstWebRTCDataChannel * channel)367 gst_webrtc_data_channel_init (GstWebRTCDataChannel * channel)
368 {
369 g_mutex_init (&channel->lock);
370 }
371
372 /**
373 * gst_webrtc_data_channel_on_open:
374 * @channel: a #GstWebRTCDataChannel
375 *
376 * Signal that the data channel was opened. Should only be used by subclasses.
377 */
378 void
gst_webrtc_data_channel_on_open(GstWebRTCDataChannel * channel)379 gst_webrtc_data_channel_on_open (GstWebRTCDataChannel * channel)
380 {
381 g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel));
382
383 GST_WEBRTC_DATA_CHANNEL_LOCK (channel);
384 if (channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING ||
385 channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) {
386 GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel);
387 return;
388 }
389
390 if (channel->ready_state != GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
391 channel->ready_state = GST_WEBRTC_DATA_CHANNEL_STATE_OPEN;
392 GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel);
393 g_object_notify (G_OBJECT (channel), "ready-state");
394
395 GST_INFO_OBJECT (channel, "We are open and ready for data!");
396 } else {
397 GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel);
398 }
399
400 GST_INFO_OBJECT (channel, "Opened");
401
402 g_signal_emit (channel, gst_webrtc_data_channel_signals[SIGNAL_ON_OPEN], 0,
403 NULL);
404 }
405
406 /**
407 * gst_webrtc_data_channel_on_close:
408 * @channel: a #GstWebRTCDataChannel
409 *
410 * Signal that the data channel was closed. Should only be used by subclasses.
411 */
412 void
gst_webrtc_data_channel_on_close(GstWebRTCDataChannel * channel)413 gst_webrtc_data_channel_on_close (GstWebRTCDataChannel * channel)
414 {
415 g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel));
416
417 GST_INFO_OBJECT (channel, "Closed");
418
419 GST_WEBRTC_DATA_CHANNEL_LOCK (channel);
420 if (channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) {
421 GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel);
422 return;
423 }
424
425 channel->ready_state = GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED;
426 GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel);
427
428 g_object_notify (G_OBJECT (channel), "ready-state");
429 GST_INFO_OBJECT (channel, "We are closed for data");
430
431 g_signal_emit (channel, gst_webrtc_data_channel_signals[SIGNAL_ON_CLOSE], 0,
432 NULL);
433 }
434
435 /**
436 * gst_webrtc_data_channel_on_error:
437 * @channel: a #GstWebRTCDataChannel
438 * @error: (transfer full): a #GError
439 *
440 * Signal that the data channel had an error. Should only be used by subclasses.
441 */
442 void
gst_webrtc_data_channel_on_error(GstWebRTCDataChannel * channel,GError * error)443 gst_webrtc_data_channel_on_error (GstWebRTCDataChannel * channel,
444 GError * error)
445 {
446 g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel));
447 g_return_if_fail (error != NULL);
448
449 GST_WARNING_OBJECT (channel, "Error: %s", GST_STR_NULL (error->message));
450
451 g_signal_emit (channel, gst_webrtc_data_channel_signals[SIGNAL_ON_ERROR], 0,
452 error);
453 }
454
455 /**
456 * gst_webrtc_data_channel_on_message_data:
457 * @channel: a #GstWebRTCDataChannel
458 * @data: (nullable): a #GBytes or %NULL
459 *
460 * Signal that the data channel received a data message. Should only be used by subclasses.
461 */
462 void
gst_webrtc_data_channel_on_message_data(GstWebRTCDataChannel * channel,GBytes * data)463 gst_webrtc_data_channel_on_message_data (GstWebRTCDataChannel * channel,
464 GBytes * data)
465 {
466 g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel));
467
468 GST_LOG_OBJECT (channel, "Have data %p", data);
469 g_signal_emit (channel,
470 gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_DATA], 0, data);
471 }
472
473 /**
474 * gst_webrtc_data_channel_on_message_string:
475 * @channel: a #GstWebRTCDataChannel
476 * @str: (nullable): a string or %NULL
477 *
478 * Signal that the data channel received a string message. Should only be used by subclasses.
479 */
480 void
gst_webrtc_data_channel_on_message_string(GstWebRTCDataChannel * channel,const gchar * str)481 gst_webrtc_data_channel_on_message_string (GstWebRTCDataChannel * channel,
482 const gchar * str)
483 {
484 g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel));
485
486 GST_LOG_OBJECT (channel, "Have string %p", str);
487 g_signal_emit (channel,
488 gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_STRING], 0, str);
489 }
490
491 /**
492 * gst_webrtc_data_channel_on_buffered_amount_low:
493 * @channel: a #GstWebRTCDataChannel
494 *
495 * Signal that the data channel reached a low buffered amount. Should only be used by subclasses.
496 */
497 void
gst_webrtc_data_channel_on_buffered_amount_low(GstWebRTCDataChannel * channel)498 gst_webrtc_data_channel_on_buffered_amount_low (GstWebRTCDataChannel * channel)
499 {
500 g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel));
501
502 GST_LOG_OBJECT (channel, "Low threshold reached");
503 g_signal_emit (channel,
504 gst_webrtc_data_channel_signals[SIGNAL_ON_BUFFERED_AMOUNT_LOW], 0);
505 }
506
507 /**
508 * gst_webrtc_data_channel_send_data:
509 * @channel: a #GstWebRTCDataChannel
510 * @data: (nullable): a #GBytes or %NULL
511 *
512 * Send @data as a data message over @channel.
513 */
514 void
gst_webrtc_data_channel_send_data(GstWebRTCDataChannel * channel,GBytes * data)515 gst_webrtc_data_channel_send_data (GstWebRTCDataChannel * channel,
516 GBytes * data)
517 {
518 GstWebRTCDataChannelClass *klass;
519
520 g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel));
521
522 klass = GST_WEBRTC_DATA_CHANNEL_GET_CLASS (channel);
523 klass->send_data (channel, data);
524 }
525
526 /**
527 * gst_webrtc_data_channel_send_string:
528 * @channel: a #GstWebRTCDataChannel
529 * @str: (nullable): a string or %NULL
530 *
531 * Send @str as a string message over @channel.
532 */
533 void
gst_webrtc_data_channel_send_string(GstWebRTCDataChannel * channel,const gchar * str)534 gst_webrtc_data_channel_send_string (GstWebRTCDataChannel * channel,
535 const gchar * str)
536 {
537 GstWebRTCDataChannelClass *klass;
538
539 g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel));
540
541 klass = GST_WEBRTC_DATA_CHANNEL_GET_CLASS (channel);
542 klass->send_string (channel, str);
543 }
544
545 /**
546 * gst_webrtc_data_channel_close:
547 * @channel: a #GstWebRTCDataChannel
548 *
549 * Close the @channel.
550 */
551 void
gst_webrtc_data_channel_close(GstWebRTCDataChannel * channel)552 gst_webrtc_data_channel_close (GstWebRTCDataChannel * channel)
553 {
554 GstWebRTCDataChannelClass *klass;
555
556 g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel));
557
558 klass = GST_WEBRTC_DATA_CHANNEL_GET_CLASS (channel);
559 klass->close (channel);
560 }
561