gstdynudpsink.c 17.3 KB
Newer Older
Philippe Kalaf's avatar
Philippe Kalaf committed
1
2
/* GStreamer
 * Copyright (C) <2005> Philippe Khalaf <burger@speedy.org>
3
 * Copyright (C) <2005> Nokia Corporation <kai.vehmanen@nokia.com>
4
 * Copyright (C) <2006> Joni Valtanen <joni.valtanen@movial.fi>
Sebastian Dröge's avatar
Sebastian Dröge committed
5
6
 * Copyright (C) <2012> Collabora Ltd.
 *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
Philippe Kalaf's avatar
Philippe Kalaf committed
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
20
21
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Philippe Kalaf's avatar
Philippe Kalaf committed
22
23
24
25
26
27
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdynudpsink.h"
28

Wim Taymans's avatar
Wim Taymans committed
29
#include <gst/net/gstnetaddressmeta.h>
Philippe Kalaf's avatar
Philippe Kalaf committed
30

31
GST_DEBUG_CATEGORY_STATIC (dynudpsink_debug);
Philippe Kalaf's avatar
Philippe Kalaf committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#define GST_CAT_DEFAULT (dynudpsink_debug)

static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS_ANY);

/* DynUDPSink signals and args */
enum
{
  /* methods */
  SIGNAL_GET_STATS,

  /* signals */

  /* FILL ME */
  LAST_SIGNAL
};

Sebastian Dröge's avatar
Sebastian Dröge committed
51
52
#define UDP_DEFAULT_SOCKET		NULL
#define UDP_DEFAULT_CLOSE_SOCKET	TRUE
53
54
#define UDP_DEFAULT_BIND_ADDRESS	NULL
#define UDP_DEFAULT_BIND_PORT   	0
55

Philippe Kalaf's avatar
Philippe Kalaf committed
56
57
58
enum
{
  PROP_0,
Sebastian Dröge's avatar
Sebastian Dröge committed
59
  PROP_SOCKET,
60
  PROP_SOCKET_V6,
61
62
63
  PROP_CLOSE_SOCKET,
  PROP_BIND_ADDRESS,
  PROP_BIND_PORT
Philippe Kalaf's avatar
Philippe Kalaf committed
64
65
66
67
68
69
};

static void gst_dynudpsink_finalize (GObject * object);

static GstFlowReturn gst_dynudpsink_render (GstBaseSink * sink,
    GstBuffer * buffer);
Sebastian Dröge's avatar
Sebastian Dröge committed
70
71
72
73
static gboolean gst_dynudpsink_stop (GstBaseSink * bsink);
static gboolean gst_dynudpsink_start (GstBaseSink * bsink);
static gboolean gst_dynudpsink_unlock (GstBaseSink * bsink);
static gboolean gst_dynudpsink_unlock_stop (GstBaseSink * bsink);
Philippe Kalaf's avatar
Philippe Kalaf committed
74
75
76
77
78

static void gst_dynudpsink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_dynudpsink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
79
80
static GstStructure *gst_dynudpsink_get_stats (GstDynUDPSink * sink,
    const gchar * host, gint port);
Philippe Kalaf's avatar
Philippe Kalaf committed
81
82
83

static guint gst_dynudpsink_signals[LAST_SIGNAL] = { 0 };

Wim Taymans's avatar
Wim Taymans committed
84
85
#define gst_dynudpsink_parent_class parent_class
G_DEFINE_TYPE (GstDynUDPSink, gst_dynudpsink, GST_TYPE_BASE_SINK);
Philippe Kalaf's avatar
Philippe Kalaf committed
86
87

static void
Wim Taymans's avatar
Wim Taymans committed
88
gst_dynudpsink_class_init (GstDynUDPSinkClass * klass)
Philippe Kalaf's avatar
Philippe Kalaf committed
89
90
91
92
93
94
95
96
97
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
  GstBaseSinkClass *gstbasesink_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
  gstbasesink_class = (GstBaseSinkClass *) klass;

98
  parent_class = g_type_class_peek_parent (klass);
Philippe Kalaf's avatar
Philippe Kalaf committed
99
100
101
102
103
104

  gobject_class->set_property = gst_dynudpsink_set_property;
  gobject_class->get_property = gst_dynudpsink_get_property;
  gobject_class->finalize = gst_dynudpsink_finalize;

  gst_dynudpsink_signals[SIGNAL_GET_STATS] =
105
      g_signal_new ("get-stats", G_TYPE_FROM_CLASS (klass),
106
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
Philippe Kalaf's avatar
Philippe Kalaf committed
107
      G_STRUCT_OFFSET (GstDynUDPSinkClass, get_stats),
108
      NULL, NULL, g_cclosure_marshal_generic, GST_TYPE_STRUCTURE, 2,
Philippe Kalaf's avatar
Philippe Kalaf committed
109
110
      G_TYPE_STRING, G_TYPE_INT);

Sebastian Dröge's avatar
Sebastian Dröge committed
111
112
113
114
  g_object_class_install_property (gobject_class, PROP_SOCKET,
      g_param_spec_object ("socket", "Socket",
          "Socket to use for UDP sending. (NULL == allocate)",
          G_TYPE_SOCKET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
115
116
117
118
  g_object_class_install_property (gobject_class, PROP_SOCKET_V6,
      g_param_spec_object ("socket-v6", "Socket IPv6",
          "Socket to use for UDPv6 sending. (NULL == allocate)",
          G_TYPE_SOCKET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Sebastian Dröge's avatar
Sebastian Dröge committed
119
120
121
122
  g_object_class_install_property (gobject_class, PROP_CLOSE_SOCKET,
      g_param_spec_boolean ("close-socket", "Close socket",
          "Close socket if passed as property on state change",
          UDP_DEFAULT_CLOSE_SOCKET,
123
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
124
125
126
127
128
129
130
131
  g_object_class_install_property (gobject_class, PROP_BIND_ADDRESS,
      g_param_spec_string ("bind-address", "Bind Address",
          "Address to bind the socket to", UDP_DEFAULT_BIND_ADDRESS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_BIND_PORT,
      g_param_spec_int ("bind-port", "Bind Port",
          "Port to bind the socket to", 0, G_MAXUINT16,
          UDP_DEFAULT_BIND_PORT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
132

Wim Taymans's avatar
Wim Taymans committed
133
134
135
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&sink_template));

136
  gst_element_class_set_static_metadata (gstelement_class, "UDP packet sender",
Wim Taymans's avatar
Wim Taymans committed
137
      "Sink/Network",
138
139
      "Send data over the network via UDP with packet destinations picked up "
      "dynamically from meta on the buffers passed",
Wim Taymans's avatar
Wim Taymans committed
140
141
      "Philippe Khalaf <burger@speedy.org>");

Philippe Kalaf's avatar
Philippe Kalaf committed
142
  gstbasesink_class->render = gst_dynudpsink_render;
Sebastian Dröge's avatar
Sebastian Dröge committed
143
144
145
146
  gstbasesink_class->start = gst_dynudpsink_start;
  gstbasesink_class->stop = gst_dynudpsink_stop;
  gstbasesink_class->unlock = gst_dynudpsink_unlock;
  gstbasesink_class->unlock_stop = gst_dynudpsink_unlock_stop;
Philippe Kalaf's avatar
Philippe Kalaf committed
147

148
149
  klass->get_stats = gst_dynudpsink_get_stats;

Philippe Kalaf's avatar
Philippe Kalaf committed
150
151
152
153
154
155
  GST_DEBUG_CATEGORY_INIT (dynudpsink_debug, "dynudpsink", 0, "UDP sink");
}

static void
gst_dynudpsink_init (GstDynUDPSink * sink)
{
Sebastian Dröge's avatar
Sebastian Dröge committed
156
  sink->socket = UDP_DEFAULT_SOCKET;
157
  sink->socket_v6 = UDP_DEFAULT_SOCKET;
Sebastian Dröge's avatar
Sebastian Dröge committed
158
159
  sink->close_socket = UDP_DEFAULT_CLOSE_SOCKET;
  sink->external_socket = FALSE;
160
161
  sink->bind_address = UDP_DEFAULT_BIND_ADDRESS;
  sink->bind_port = UDP_DEFAULT_BIND_PORT;
162

Sebastian Dröge's avatar
Sebastian Dröge committed
163
  sink->used_socket = NULL;
164
  sink->used_socket_v6 = NULL;
Philippe Kalaf's avatar
Philippe Kalaf committed
165
166
167
168
169
}

static void
gst_dynudpsink_finalize (GObject * object)
{
Sebastian Dröge's avatar
Sebastian Dröge committed
170
  GstDynUDPSink *sink;
171

Sebastian Dröge's avatar
Sebastian Dröge committed
172
  sink = GST_DYNUDPSINK (object);
173

Sebastian Dröge's avatar
Sebastian Dröge committed
174
175
176
  if (sink->socket)
    g_object_unref (sink->socket);
  sink->socket = NULL;
177

178
179
180
181
  if (sink->socket_v6)
    g_object_unref (sink->socket_v6);
  sink->socket_v6 = NULL;

Sebastian Dröge's avatar
Sebastian Dröge committed
182
183
184
185
  if (sink->used_socket)
    g_object_unref (sink->used_socket);
  sink->used_socket = NULL;

186
187
188
189
  if (sink->used_socket_v6)
    g_object_unref (sink->used_socket_v6);
  sink->used_socket_v6 = NULL;

190
191
192
  g_free (sink->bind_address);
  sink->bind_address = NULL;

Sebastian Dröge's avatar
Sebastian Dröge committed
193
  G_OBJECT_CLASS (parent_class)->finalize (object);
Philippe Kalaf's avatar
Philippe Kalaf committed
194
195
196
197
198
199
}

static GstFlowReturn
gst_dynudpsink_render (GstBaseSink * bsink, GstBuffer * buffer)
{
  GstDynUDPSink *sink;
Sebastian Dröge's avatar
Sebastian Dröge committed
200
  gssize ret;
Wim Taymans's avatar
Wim Taymans committed
201
  GstMapInfo map;
Wim Taymans's avatar
Wim Taymans committed
202
  GstNetAddressMeta *meta;
Sebastian Dröge's avatar
Sebastian Dröge committed
203
204
  GSocketAddress *addr;
  GError *err = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
205
  GSocketFamily family;
206
  GSocket *socket;
Philippe Kalaf's avatar
Philippe Kalaf committed
207

Wim Taymans's avatar
Wim Taymans committed
208
  meta = gst_buffer_get_net_address_meta (buffer);
209
210

  if (meta == NULL) {
211
    GST_DEBUG ("Received buffer without GstNetAddressMeta, skipping");
Philippe Kalaf's avatar
Philippe Kalaf committed
212
213
214
215
216
    return GST_FLOW_OK;
  }

  sink = GST_DYNUDPSINK (bsink);

Sebastian Dröge's avatar
Sebastian Dröge committed
217
218
219
  /* let's get the address from the metadata */
  addr = meta->addr;

Sebastian Dröge's avatar
Sebastian Dröge committed
220
  family = g_socket_address_get_family (addr);
221
  if (family == G_SOCKET_FAMILY_IPV6 && !sink->used_socket_v6)
Sebastian Dröge's avatar
Sebastian Dröge committed
222
223
    goto invalid_family;

Wim Taymans's avatar
Wim Taymans committed
224
  gst_buffer_map (buffer, &map, GST_MAP_READ);
Philippe Kalaf's avatar
Philippe Kalaf committed
225

Wim Taymans's avatar
Wim Taymans committed
226
  GST_DEBUG ("about to send %" G_GSIZE_FORMAT " bytes", map.size);
Philippe Kalaf's avatar
Philippe Kalaf committed
227

Sebastian Dröge's avatar
Sebastian Dröge committed
228
229
230
231
232
233
234
#ifndef GST_DISABLE_GST_DEBUG
  {
    gchar *host;

    host =
        g_inet_address_to_string (g_inet_socket_address_get_address
        (G_INET_SOCKET_ADDRESS (addr)));
Wim Taymans's avatar
Wim Taymans committed
235
236
237
    GST_DEBUG ("sending %" G_GSIZE_FORMAT " bytes to client %s port %d",
        map.size, host,
        g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr)));
Sebastian Dröge's avatar
Sebastian Dröge committed
238
239
    g_free (host);
  }
240
#endif
Philippe Kalaf's avatar
Philippe Kalaf committed
241

242
243
244
245
246
247
  /* Select socket to send from for this address */
  if (family == G_SOCKET_FAMILY_IPV6 || !sink->used_socket)
    socket = sink->used_socket_v6;
  else
    socket = sink->used_socket;

Sebastian Dröge's avatar
Sebastian Dröge committed
248
  ret =
249
      g_socket_send_to (socket, addr, (gchar *) map.data, map.size,
Sebastian Dröge's avatar
Sebastian Dröge committed
250
      sink->cancellable, &err);
Wim Taymans's avatar
Wim Taymans committed
251
  gst_buffer_unmap (buffer, &map);
Wim Taymans's avatar
Wim Taymans committed
252

Sebastian Dröge's avatar
Sebastian Dröge committed
253
254
  if (ret < 0)
    goto send_error;
Philippe Kalaf's avatar
Philippe Kalaf committed
255

Sebastian Dröge's avatar
Sebastian Dröge committed
256
  GST_DEBUG ("sent %" G_GSSIZE_FORMAT " bytes", ret);
Philippe Kalaf's avatar
Philippe Kalaf committed
257
258
259
260
261

  return GST_FLOW_OK;

send_error:
  {
262
263
264
265
266
267
268
269
270
271
    GstFlowReturn flow_ret;

    if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
      GST_DEBUG_OBJECT (sink, "send cancelled");
      flow_ret = GST_FLOW_FLUSHING;
    } else {
      GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
          ("send error: %s", err->message));
      flow_ret = GST_FLOW_ERROR;
    }
Sebastian Dröge's avatar
Sebastian Dröge committed
272
    g_clear_error (&err);
273
    return flow_ret;
Sebastian Dröge's avatar
Sebastian Dröge committed
274
275
276
  }
invalid_family:
  {
277
    GST_DEBUG ("invalid address family (got %d)", family);
Philippe Kalaf's avatar
Philippe Kalaf committed
278
279
280
281
282
283
284
285
286
287
288
289
290
    return GST_FLOW_ERROR;
  }
}

static void
gst_dynudpsink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstDynUDPSink *udpsink;

  udpsink = GST_DYNUDPSINK (object);

  switch (prop_id) {
Sebastian Dröge's avatar
Sebastian Dröge committed
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
    case PROP_SOCKET:
      if (udpsink->socket != NULL && udpsink->socket != udpsink->used_socket &&
          udpsink->close_socket) {
        GError *err = NULL;

        if (!g_socket_close (udpsink->socket, &err)) {
          GST_ERROR ("failed to close socket %p: %s", udpsink->socket,
              err->message);
          g_clear_error (&err);
        }
      }
      if (udpsink->socket)
        g_object_unref (udpsink->socket);
      udpsink->socket = g_value_dup_object (value);
      GST_DEBUG ("setting socket to %p", udpsink->socket);
306
      break;
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
    case PROP_SOCKET_V6:
      if (udpsink->socket_v6 != NULL
          && udpsink->socket_v6 != udpsink->used_socket_v6
          && udpsink->close_socket) {
        GError *err = NULL;

        if (!g_socket_close (udpsink->socket_v6, &err)) {
          GST_ERROR ("failed to close socket %p: %s", udpsink->socket_v6,
              err->message);
          g_clear_error (&err);
        }
      }
      if (udpsink->socket_v6)
        g_object_unref (udpsink->socket_v6);
      udpsink->socket_v6 = g_value_dup_object (value);
      GST_DEBUG ("setting socket v6 to %p", udpsink->socket_v6);
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
324
325
326
    case PROP_CLOSE_SOCKET:
      udpsink->close_socket = g_value_get_boolean (value);
      break;
327
328
329
330
331
332
333
    case PROP_BIND_ADDRESS:
      g_free (udpsink->bind_address);
      udpsink->bind_address = g_value_dup_string (value);
      break;
    case PROP_BIND_PORT:
      udpsink->bind_port = g_value_get_int (value);
      break;
Philippe Kalaf's avatar
Philippe Kalaf committed
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_dynudpsink_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
  GstDynUDPSink *udpsink;

  udpsink = GST_DYNUDPSINK (object);

  switch (prop_id) {
Sebastian Dröge's avatar
Sebastian Dröge committed
349
350
    case PROP_SOCKET:
      g_value_set_object (value, udpsink->socket);
351
      break;
352
353
354
    case PROP_SOCKET_V6:
      g_value_set_object (value, udpsink->socket_v6);
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
355
356
357
    case PROP_CLOSE_SOCKET:
      g_value_set_boolean (value, udpsink->close_socket);
      break;
358
359
360
361
362
363
    case PROP_BIND_ADDRESS:
      g_value_set_string (value, udpsink->bind_address);
      break;
    case PROP_BIND_PORT:
      g_value_set_int (value, udpsink->bind_port);
      break;
Philippe Kalaf's avatar
Philippe Kalaf committed
364
365
366
367
368
369
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
static void
gst_dynudpsink_create_cancellable (GstDynUDPSink * sink)
{
  GPollFD pollfd;

  sink->cancellable = g_cancellable_new ();
  sink->made_cancel_fd = g_cancellable_make_pollfd (sink->cancellable, &pollfd);
}

static void
gst_dynudpsink_free_cancellable (GstDynUDPSink * sink)
{
  if (sink->made_cancel_fd) {
    g_cancellable_release_fd (sink->cancellable);
    sink->made_cancel_fd = FALSE;
  }
  g_object_unref (sink->cancellable);
  sink->cancellable = NULL;
}

Philippe Kalaf's avatar
Philippe Kalaf committed
390
391
/* create a socket for sending to remote machine */
static gboolean
Sebastian Dröge's avatar
Sebastian Dröge committed
392
gst_dynudpsink_start (GstBaseSink * bsink)
Philippe Kalaf's avatar
Philippe Kalaf committed
393
{
Sebastian Dröge's avatar
Sebastian Dröge committed
394
395
396
397
  GstDynUDPSink *udpsink;
  GError *err = NULL;

  udpsink = GST_DYNUDPSINK (bsink);
Philippe Kalaf's avatar
Philippe Kalaf committed
398

399
400
  gst_dynudpsink_create_cancellable (udpsink);

401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
  udpsink->external_socket = FALSE;

  if (udpsink->socket) {
    if (g_socket_get_family (udpsink->socket) == G_SOCKET_FAMILY_IPV6) {
      udpsink->used_socket_v6 = G_SOCKET (g_object_ref (udpsink->socket));
      udpsink->external_socket = TRUE;
    } else {
      udpsink->used_socket = G_SOCKET (g_object_ref (udpsink->socket));
      udpsink->external_socket = TRUE;
    }
  }

  if (udpsink->socket_v6) {
    g_return_val_if_fail (g_socket_get_family (udpsink->socket) !=
        G_SOCKET_FAMILY_IPV6, FALSE);

    if (udpsink->used_socket_v6
        && udpsink->used_socket_v6 != udpsink->socket_v6) {
      GST_ERROR_OBJECT (udpsink,
          "Provided different IPv6 sockets in socket and socket-v6 properties");
      return FALSE;
Sebastian Dröge's avatar
Sebastian Dröge committed
422
    }
423

424
    udpsink->used_socket_v6 = G_SOCKET (g_object_ref (udpsink->socket_v6));
Sebastian Dröge's avatar
Sebastian Dröge committed
425
    udpsink->external_socket = TRUE;
426
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
427

428
  if (!udpsink->used_socket && !udpsink->used_socket_v6) {
429
430
    GSocketAddress *bind_addr;
    GInetAddress *bind_iaddr;
431

432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
    if (udpsink->bind_address) {
      GSocketFamily family;

      bind_iaddr = g_inet_address_new_from_string (udpsink->bind_address);
      if (!bind_iaddr) {
        GList *results;
        GResolver *resolver;

        resolver = g_resolver_get_default ();
        results =
            g_resolver_lookup_by_name (resolver, udpsink->bind_address,
            udpsink->cancellable, &err);
        if (!results) {
          g_object_unref (resolver);
          goto name_resolve;
        }
        bind_iaddr = G_INET_ADDRESS (g_object_ref (results->data));
        g_resolver_free_addresses (results);
        g_object_unref (resolver);
      }

      bind_addr = g_inet_socket_address_new (bind_iaddr, udpsink->bind_port);
      g_object_unref (bind_iaddr);
      family = g_socket_address_get_family (G_SOCKET_ADDRESS (bind_addr));

      if ((udpsink->used_socket =
              g_socket_new (family, G_SOCKET_TYPE_DATAGRAM,
                  G_SOCKET_PROTOCOL_UDP, &err)) == NULL) {
        g_object_unref (bind_addr);
        goto no_socket;
      }

      g_socket_bind (udpsink->used_socket, bind_addr, TRUE, &err);
      if (err != NULL)
        goto bind_error;
467
    } else {
468
469
470
471
472
473
      /* create sender sockets if none available */
      if ((udpsink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV4,
                  G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL)
        goto no_socket;

      bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
474
      bind_addr = g_inet_socket_address_new (bind_iaddr, 0);
475
      g_socket_bind (udpsink->used_socket, bind_addr, TRUE, &err);
476
477
478
479
      g_object_unref (bind_addr);
      g_object_unref (bind_iaddr);
      if (err != NULL)
        goto bind_error;
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495

      if ((udpsink->used_socket_v6 = g_socket_new (G_SOCKET_FAMILY_IPV6,
                  G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP,
                  &err)) == NULL) {
        GST_INFO_OBJECT (udpsink, "Failed to create IPv6 socket: %s",
            err->message);
        g_clear_error (&err);
      } else {
        bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
        bind_addr = g_inet_socket_address_new (bind_iaddr, 0);
        g_socket_bind (udpsink->used_socket_v6, bind_addr, TRUE, &err);
        g_object_unref (bind_addr);
        g_object_unref (bind_iaddr);
        if (err != NULL)
          goto bind_error;
      }
496
497
498
499
500
501
502
    }
  }

  if (udpsink->used_socket)
    g_socket_set_broadcast (udpsink->used_socket, TRUE);
  if (udpsink->used_socket_v6)
    g_socket_set_broadcast (udpsink->used_socket_v6, TRUE);
Sebastian Dröge's avatar
Sebastian Dröge committed
503

Philippe Kalaf's avatar
Philippe Kalaf committed
504
505
506
507
508
  return TRUE;

  /* ERRORS */
no_socket:
  {
509
510
    GST_ERROR_OBJECT (udpsink, "Failed to create IPv4 socket: %s",
        err->message);
Sebastian Dröge's avatar
Sebastian Dröge committed
511
    g_clear_error (&err);
Philippe Kalaf's avatar
Philippe Kalaf committed
512
513
    return FALSE;
  }
514
515
516
517
518
519
520
bind_error:
  {
    GST_ELEMENT_ERROR (udpsink, RESOURCE, FAILED, (NULL),
        ("Failed to bind socket: %s", err->message));
    g_clear_error (&err);
    return FALSE;
  }
521
522
523
524
525
526
527
528
name_resolve:
  {
    GST_ELEMENT_ERROR (udpsink, RESOURCE, FAILED, (NULL),
        ("Failed to resolve bind address %s: %s", udpsink->bind_address,
            err->message));
    g_clear_error (&err);
    return FALSE;
  }
Philippe Kalaf's avatar
Philippe Kalaf committed
529
530
}

531
static GstStructure *
Philippe Kalaf's avatar
Philippe Kalaf committed
532
533
534
535
536
gst_dynudpsink_get_stats (GstDynUDPSink * sink, const gchar * host, gint port)
{
  return NULL;
}

Sebastian Dröge's avatar
Sebastian Dröge committed
537
538
static gboolean
gst_dynudpsink_stop (GstBaseSink * bsink)
Philippe Kalaf's avatar
Philippe Kalaf committed
539
{
Sebastian Dröge's avatar
Sebastian Dröge committed
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
  GstDynUDPSink *udpsink;

  udpsink = GST_DYNUDPSINK (bsink);

  if (udpsink->used_socket) {
    if (udpsink->close_socket || !udpsink->external_socket) {
      GError *err = NULL;

      if (!g_socket_close (udpsink->used_socket, &err)) {
        GST_ERROR_OBJECT (udpsink, "Failed to close socket: %s", err->message);
        g_clear_error (&err);
      }
    }

    g_object_unref (udpsink->used_socket);
    udpsink->used_socket = NULL;
  }

558
559
560
561
562
563
564
565
566
567
568
569
570
571
  if (udpsink->used_socket_v6) {
    if (udpsink->close_socket || !udpsink->external_socket) {
      GError *err = NULL;

      if (!g_socket_close (udpsink->used_socket_v6, &err)) {
        GST_ERROR_OBJECT (udpsink, "Failed to close socket: %s", err->message);
        g_clear_error (&err);
      }
    }

    g_object_unref (udpsink->used_socket_v6);
    udpsink->used_socket_v6 = NULL;
  }

572
573
  gst_dynudpsink_free_cancellable (udpsink);

Sebastian Dröge's avatar
Sebastian Dröge committed
574
  return TRUE;
Philippe Kalaf's avatar
Philippe Kalaf committed
575
576
}

Sebastian Dröge's avatar
Sebastian Dröge committed
577
578
static gboolean
gst_dynudpsink_unlock (GstBaseSink * bsink)
Philippe Kalaf's avatar
Philippe Kalaf committed
579
{
Sebastian Dröge's avatar
Sebastian Dröge committed
580
  GstDynUDPSink *udpsink;
Philippe Kalaf's avatar
Philippe Kalaf committed
581

Sebastian Dröge's avatar
Sebastian Dröge committed
582
  udpsink = GST_DYNUDPSINK (bsink);
Philippe Kalaf's avatar
Philippe Kalaf committed
583

Sebastian Dröge's avatar
Sebastian Dröge committed
584
  g_cancellable_cancel (udpsink->cancellable);
Philippe Kalaf's avatar
Philippe Kalaf committed
585

Sebastian Dröge's avatar
Sebastian Dröge committed
586
587
  return TRUE;
}
Philippe Kalaf's avatar
Philippe Kalaf committed
588

Sebastian Dröge's avatar
Sebastian Dröge committed
589
590
591
592
static gboolean
gst_dynudpsink_unlock_stop (GstBaseSink * bsink)
{
  GstDynUDPSink *udpsink;
Philippe Kalaf's avatar
Philippe Kalaf committed
593

Sebastian Dröge's avatar
Sebastian Dröge committed
594
595
  udpsink = GST_DYNUDPSINK (bsink);

596
597
  gst_dynudpsink_free_cancellable (udpsink);
  gst_dynudpsink_create_cancellable (udpsink);
Sebastian Dröge's avatar
Sebastian Dröge committed
598
599

  return TRUE;
Philippe Kalaf's avatar
Philippe Kalaf committed
600
}