rtsp-stream.c 168 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
2
/* GStreamer
 * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3
4
 * Copyright (C) 2015 Centricular Ltd
 *     Author: Sebastian Dröge <sebastian@centricular.com>
Wim Taymans's avatar
Wim Taymans committed
5
6
7
8
9
10
11
12
13
14
15
16
17
 *
 * 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
18
19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Wim Taymans's avatar
Wim Taymans committed
20
 */
Wim Taymans's avatar
Wim Taymans committed
21
22
23
24
25
/**
 * SECTION:rtsp-stream
 * @short_description: A media stream
 * @see_also: #GstRTSPMedia
 *
Wim Taymans's avatar
Wim Taymans committed
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 * The #GstRTSPStream object manages the data transport for one stream. It
 * is created from a payloader element and a source pad that produce the RTP
 * packets for the stream.
 *
 * With gst_rtsp_stream_join_bin() the streaming elements are added to the bin
 * and rtpbin. gst_rtsp_stream_leave_bin() removes the elements again.
 *
 * The #GstRTSPStream will use the configured addresspool, as set with
 * gst_rtsp_stream_set_address_pool(), to allocate multicast addresses for the
 * stream. With gst_rtsp_stream_get_multicast_address() you can get the
 * configured address.
 *
 * With gst_rtsp_stream_get_server_port () you can get the port that the server
 * will use to receive RTCP. This is the part that the clients will use to send
 * RTCP to.
 *
 * With gst_rtsp_stream_add_transport() destinations can be added where the
 * stream should be sent to. Use gst_rtsp_stream_remove_transport() to remove
 * the destination again.
 *
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 * Each #GstRTSPStreamTransport spawns one queue that will serve as a backlog of a
 * controllable maximum size when the reflux from the TCP connection's backpressure
 * starts spilling all over.
 *
 * Unlike the backlog in rtspconnection, which we have decided should only contain
 * at most one RTP and one RTCP data message in order to allow control messages to
 * go through unobstructed, this backlog only consists of data messages, allowing
 * us to fill it up without concern.
 *
 * When multiple TCP transports exist, for example in the context of a shared media,
 * we only pop samples from our appsinks when at least one of the transports doesn't
 * experience back pressure: this allows us to pace our sample popping to the speed
 * of the fastest client.
 *
 * When a sample is popped, it is either sent directly on transports that don't
 * experience backpressure, or queued on the transport's backlog otherwise. Samples
 * are then popped from that backlog when the transport reports it has sent the message.
 *
 * Once the backlog reaches an overly large duration, the transport is dropped as
 * the client was deemed too slow.
Wim Taymans's avatar
Wim Taymans committed
66
 */
67
68
69
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Wim Taymans's avatar
Wim Taymans committed
70
71

#include <stdlib.h>
72
73
#include <stdio.h>
#include <string.h>
Wim Taymans's avatar
Wim Taymans committed
74
75
76
77
78
79

#include <gio/gio.h>

#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>

80
81
#include <gst/rtp/gstrtpbuffer.h>

Wim Taymans's avatar
Wim Taymans committed
82
#include "rtsp-stream.h"
83
#include "rtsp-server-internal.h"
Wim Taymans's avatar
Wim Taymans committed
84

85
86
87
88
struct _GstRTSPStreamPrivate
{
  GMutex lock;
  guint idx;
89
90
  /* Only one pad is ever set */
  GstPad *srcpad, *sinkpad;
91
92
  GstElement *payloader;
  guint buffer_size;
93
  GstBin *joined_bin;
94
95
96
97

  /* TRUE if this stream is running on
   * the client side of an RTSP link (for RECORD) */
  gboolean client_side;
98
  gchar *control;
99

100
101
102
  /* TRUE if stream is complete. This means that the receiver and the sender
   * parts are present in the stream. */
  gboolean is_complete;
103
  GstRTSPProfile profiles;
104
105
  GstRTSPLowerTrans allowed_protocols;
  GstRTSPLowerTrans configured_protocols;
Wim Taymans's avatar
Wim Taymans committed
106

107
108
  /* pads on the rtpbin */
  GstPad *send_rtp_sink;
109
  GstPad *recv_rtp_src;
110
111
112
113
114
115
  GstPad *recv_sink[2];
  GstPad *send_src[2];

  /* the RTPSession object */
  GObject *session;

Wim Taymans's avatar
Wim Taymans committed
116
117
118
  /* SRTP encoder/decoder */
  GstElement *srtpenc;
  GstElement *srtpdec;
119
  GHashTable *keys;
Wim Taymans's avatar
Wim Taymans committed
120

121
  /* for UDP unicast */
122
123
  GstElement *udpsrc_v4[2];
  GstElement *udpsrc_v6[2];
124
  GstElement *udpqueue[2];
125
  GstElement *udpsink[2];
126
127
  GSocket *socket_v4[2];
  GSocket *socket_v6[2];
128

129
130
131
132
133
  /* for UDP multicast */
  GstElement *mcast_udpsrc_v4[2];
  GstElement *mcast_udpsrc_v6[2];
  GstElement *mcast_udpqueue[2];
  GstElement *mcast_udpsink[2];
134
135
  GSocket *mcast_socket_v4[2];
  GSocket *mcast_socket_v6[2];
136
  GList *mcast_clients;
137

138
139
  /* for TCP transport */
  GstElement *appsrc[2];
140
  GstClockTime appsrc_base_time[2];
141
142
143
144
145
146
  GstElement *appqueue[2];
  GstElement *appsink[2];

  GstElement *tee[2];
  GstElement *funnel[2];

147
148
  /* retransmission */
  GstElement *rtxsend;
149
  GstElement *rtxreceive;
150
151
152
  guint rtx_pt;
  GstClockTime rtx_time;

153
154
155
  /* rate control */
  gboolean do_rate_control;

156
157
158
159
160
161
162
  /* Forward Error Correction with RFC 5109 */
  GstElement *ulpfec_decoder;
  GstElement *ulpfec_encoder;
  guint ulpfec_pt;
  gboolean ulpfec_enabled;
  guint ulpfec_percentage;

163
164
  /* pool used to manage unicast and multicast addresses */
  GstRTSPAddressPool *pool;
165

166
167
  /* unicast server addr/port */
  GstRTSPAddress *server_addr_v4;
168
  GstRTSPAddress *server_addr_v6;
169
170

  /* multicast addresses */
171
172
  GstRTSPAddress *mcast_addr_v4;
  GstRTSPAddress *mcast_addr_v6;
173

174
  gchar *multicast_iface;
175
  guint max_mcast_ttl;
176
  gboolean bind_mcast_address;
177

178
179
180
181
182
183
184
  /* the caps of the stream */
  gulong caps_sig;
  GstCaps *caps;

  /* transports we stream to */
  guint n_active;
  GList *transports;
185
  guint transports_cookie;
186
187
  GPtrArray *tr_cache;
  guint tr_cache_cookie;
188
189
  guint n_tcp_transports;
  gboolean have_buffer[2];
190

191
  gint dscp_qos;
Wim Taymans's avatar
Wim Taymans committed
192

193
194
195
196
197
198
199
200
201
202
203
204
  /* Sending logic for TCP */
  GThread *send_thread;
  GCond send_cond;
  GMutex send_lock;
  /* @send_lock is released when pushing data out, we use
   * a cookie to decide whether we should wait on @send_cond
   * before checking the transports' backlogs again
   */
  guint send_cookie;
  /* Used to control shutdown of @send_thread */
  gboolean continue_sending;

Wim Taymans's avatar
Wim Taymans committed
205
  /* stream blocking */
206
  gulong blocked_id[2];
Wim Taymans's avatar
Wim Taymans committed
207
  gboolean blocking;
208

209
210
211
  /* current stream postion */
  GstClockTime position;

212
213
  /* pt->caps map for RECORD streams */
  GHashTable *ptmap;
214
215

  GstRTSPPublishClockMode publish_clock_mode;
216
  GThreadPool *send_pool;
217
218
219
220
221
222

  /* Used to provide accurate rtpinfo when the stream is blocking */
  gboolean blocked_buffer;
  guint32 blocked_seqnum;
  guint32 blocked_rtptime;
  GstClockTime blocked_running_time;
223
  gint blocked_clock_rate;
224
225
226

  /* Whether we should send and receive RTCP */
  gboolean enable_rtcp;
227
228
229
230
231
232

  /* blocking early rtcp packets */
  GstPad *block_early_rtcp_pad;
  gulong block_early_rtcp_probe;
  GstPad *block_early_rtcp_pad_ipv6;
  gulong block_early_rtcp_probe_ipv6;
233
234
};

Wim Taymans's avatar
Wim Taymans committed
235
#define DEFAULT_CONTROL         NULL
236
#define DEFAULT_PROFILES        GST_RTSP_PROFILE_AVP
237
238
#define DEFAULT_PROTOCOLS       GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST | \
                                        GST_RTSP_LOWER_TRANS_TCP
239
#define DEFAULT_MAX_MCAST_TTL   255
240
#define DEFAULT_BIND_MCAST_ADDRESS FALSE
241
#define DEFAULT_DO_RATE_CONTROL TRUE
242
#define DEFAULT_ENABLE_RTCP TRUE
243

Wim Taymans's avatar
Wim Taymans committed
244
245
246
enum
{
  PROP_0,
247
  PROP_CONTROL,
248
  PROP_PROFILES,
Wim Taymans's avatar
Wim Taymans committed
249
  PROP_PROTOCOLS,
Wim Taymans's avatar
Wim Taymans committed
250
251
252
  PROP_LAST
};

253
254
255
256
enum
{
  SIGNAL_NEW_RTP_ENCODER,
  SIGNAL_NEW_RTCP_ENCODER,
257
  SIGNAL_NEW_RTP_RTCP_DECODER,
258
259
260
  SIGNAL_LAST
};

Wim Taymans's avatar
Wim Taymans committed
261
262
263
264
265
GST_DEBUG_CATEGORY_STATIC (rtsp_stream_debug);
#define GST_CAT_DEFAULT rtsp_stream_debug

static GQuark ssrc_stream_map_key;

266
267
268
269
270
static void gst_rtsp_stream_get_property (GObject * object, guint propid,
    GValue * value, GParamSpec * pspec);
static void gst_rtsp_stream_set_property (GObject * object, guint propid,
    const GValue * value, GParamSpec * pspec);

Wim Taymans's avatar
Wim Taymans committed
271
272
static void gst_rtsp_stream_finalize (GObject * obj);

273
274
275
276
static gboolean
update_transport (GstRTSPStream * stream, GstRTSPStreamTransport * trans,
    gboolean add);

277
278
static guint gst_rtsp_stream_signals[SIGNAL_LAST] = { 0 };

279
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPStream, gst_rtsp_stream, G_TYPE_OBJECT);
Wim Taymans's avatar
Wim Taymans committed
280
281
282
283
284
285
286
287

static void
gst_rtsp_stream_class_init (GstRTSPStreamClass * klass)
{
  GObjectClass *gobject_class;

  gobject_class = G_OBJECT_CLASS (klass);

288
289
  gobject_class->get_property = gst_rtsp_stream_get_property;
  gobject_class->set_property = gst_rtsp_stream_set_property;
Wim Taymans's avatar
Wim Taymans committed
290
291
  gobject_class->finalize = gst_rtsp_stream_finalize;

292
293
294
295
296
  g_object_class_install_property (gobject_class, PROP_CONTROL,
      g_param_spec_string ("control", "Control",
          "The control string for this stream", DEFAULT_CONTROL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

297
298
299
300
301
  g_object_class_install_property (gobject_class, PROP_PROFILES,
      g_param_spec_flags ("profiles", "Profiles",
          "Allowed transfer profiles", GST_TYPE_RTSP_PROFILE,
          DEFAULT_PROFILES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

Wim Taymans's avatar
Wim Taymans committed
302
303
304
305
306
  g_object_class_install_property (gobject_class, PROP_PROTOCOLS,
      g_param_spec_flags ("protocols", "Protocols",
          "Allowed lower transport protocols", GST_TYPE_RTSP_LOWER_TRANS,
          DEFAULT_PROTOCOLS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

307
308
  gst_rtsp_stream_signals[SIGNAL_NEW_RTP_ENCODER] =
      g_signal_new ("new-rtp-encoder", G_TYPE_FROM_CLASS (klass),
309
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
310
311
312

  gst_rtsp_stream_signals[SIGNAL_NEW_RTCP_ENCODER] =
      g_signal_new ("new-rtcp-encoder", G_TYPE_FROM_CLASS (klass),
313
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
314

315
316
  gst_rtsp_stream_signals[SIGNAL_NEW_RTP_RTCP_DECODER] =
      g_signal_new ("new-rtp-rtcp-decoder", G_TYPE_FROM_CLASS (klass),
317
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
318

Wim Taymans's avatar
Wim Taymans committed
319
320
321
322
323
324
  GST_DEBUG_CATEGORY_INIT (rtsp_stream_debug, "rtspstream", 0, "GstRTSPStream");

  ssrc_stream_map_key = g_quark_from_static_string ("GstRTSPServer.stream");
}

static void
Wim Taymans's avatar
Wim Taymans committed
325
gst_rtsp_stream_init (GstRTSPStream * stream)
Wim Taymans's avatar
Wim Taymans committed
326
{
327
  GstRTSPStreamPrivate *priv = gst_rtsp_stream_get_instance_private (stream);
328

Wim Taymans's avatar
Wim Taymans committed
329
330
  GST_DEBUG ("new stream %p", stream);

331
332
  stream->priv = priv;

333
334
  priv->dscp_qos = -1;
  priv->control = g_strdup (DEFAULT_CONTROL);
335
  priv->profiles = DEFAULT_PROFILES;
336
337
  priv->allowed_protocols = DEFAULT_PROTOCOLS;
  priv->configured_protocols = 0;
338
  priv->publish_clock_mode = GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK;
339
  priv->max_mcast_ttl = DEFAULT_MAX_MCAST_TTL;
340
  priv->bind_mcast_address = DEFAULT_BIND_MCAST_ADDRESS;
341
  priv->do_rate_control = DEFAULT_DO_RATE_CONTROL;
342
  priv->enable_rtcp = DEFAULT_ENABLE_RTCP;
343

344
  g_mutex_init (&priv->lock);
345

346
347
348
349
350
  priv->continue_sending = TRUE;
  priv->send_cookie = 0;
  g_cond_init (&priv->send_cond);
  g_mutex_init (&priv->send_lock);

351
352
  priv->keys = g_hash_table_new_full (g_direct_hash, g_direct_equal,
      NULL, (GDestroyNotify) gst_caps_unref);
353
354
  priv->ptmap = g_hash_table_new_full (NULL, NULL, NULL,
      (GDestroyNotify) gst_caps_unref);
355
  priv->send_pool = NULL;
356
357
358
359
  priv->block_early_rtcp_pad = NULL;
  priv->block_early_rtcp_probe = 0;
  priv->block_early_rtcp_pad_ipv6 = NULL;
  priv->block_early_rtcp_probe_ipv6 = 0;
Wim Taymans's avatar
Wim Taymans committed
360
361
}

362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
typedef struct _UdpClientAddrInfo UdpClientAddrInfo;

struct _UdpClientAddrInfo
{
  gchar *address;
  guint rtp_port;
  guint add_count;              /* how often this address has been added */
};

static void
free_mcast_client (gpointer data)
{
  UdpClientAddrInfo *client = data;

  g_free (client->address);
  g_free (client);
}

Wim Taymans's avatar
Wim Taymans committed
380
381
382
383
static void
gst_rtsp_stream_finalize (GObject * obj)
{
  GstRTSPStream *stream;
384
  GstRTSPStreamPrivate *priv;
385
  guint i;
Wim Taymans's avatar
Wim Taymans committed
386
387

  stream = GST_RTSP_STREAM (obj);
388
  priv = stream->priv;
Wim Taymans's avatar
Wim Taymans committed
389

Wim Taymans's avatar
Wim Taymans committed
390
391
  GST_DEBUG ("finalize stream %p", stream);

392
  /* we really need to be unjoined now */
393
  g_return_if_fail (priv->joined_bin == NULL);
Wim Taymans's avatar
Wim Taymans committed
394

395
396
  if (priv->send_pool)
    g_thread_pool_free (priv->send_pool, TRUE, TRUE);
397
398
399
400
  if (priv->mcast_addr_v4)
    gst_rtsp_address_free (priv->mcast_addr_v4);
  if (priv->mcast_addr_v6)
    gst_rtsp_address_free (priv->mcast_addr_v6);
401
402
403
404
  if (priv->server_addr_v4)
    gst_rtsp_address_free (priv->server_addr_v4);
  if (priv->server_addr_v6)
    gst_rtsp_address_free (priv->server_addr_v6);
405
406
  if (priv->pool)
    g_object_unref (priv->pool);
407
408
  if (priv->rtxsend)
    g_object_unref (priv->rtxsend);
409
410
  if (priv->rtxreceive)
    g_object_unref (priv->rtxreceive);
411
412
413
414
  if (priv->ulpfec_encoder)
    gst_object_unref (priv->ulpfec_encoder);
  if (priv->ulpfec_decoder)
    gst_object_unref (priv->ulpfec_decoder);
415

416
417
418
419
420
421
422
423
424
425
  for (i = 0; i < 2; i++) {
    if (priv->socket_v4[i])
      g_object_unref (priv->socket_v4[i]);
    if (priv->socket_v6[i])
      g_object_unref (priv->socket_v6[i]);
    if (priv->mcast_socket_v4[i])
      g_object_unref (priv->mcast_socket_v4[i]);
    if (priv->mcast_socket_v6[i])
      g_object_unref (priv->mcast_socket_v6[i]);
  }
426

427
  g_free (priv->multicast_iface);
428
  g_list_free_full (priv->mcast_clients, (GDestroyNotify) free_mcast_client);
429

430
  gst_object_unref (priv->payloader);
431
432
433
434
  if (priv->srcpad)
    gst_object_unref (priv->srcpad);
  if (priv->sinkpad)
    gst_object_unref (priv->sinkpad);
435
  g_free (priv->control);
436
  g_mutex_clear (&priv->lock);
Wim Taymans's avatar
Wim Taymans committed
437

438
  g_hash_table_unref (priv->keys);
439
  g_hash_table_destroy (priv->ptmap);
440

441
442
443
  g_mutex_clear (&priv->send_lock);
  g_cond_clear (&priv->send_cond);

444
445
446
447
448
449
450
451
452
453
454
455
  if (priv->block_early_rtcp_probe != 0) {
    gst_pad_remove_probe
        (priv->block_early_rtcp_pad, priv->block_early_rtcp_probe);
    gst_object_unref (priv->block_early_rtcp_pad);
  }

  if (priv->block_early_rtcp_probe_ipv6 != 0) {
    gst_pad_remove_probe
        (priv->block_early_rtcp_pad_ipv6, priv->block_early_rtcp_probe_ipv6);
    gst_object_unref (priv->block_early_rtcp_pad_ipv6);
  }

Wim Taymans's avatar
Wim Taymans committed
456
457
458
  G_OBJECT_CLASS (gst_rtsp_stream_parent_class)->finalize (obj);
}

459
460
461
462
463
464
465
466
467
468
static void
gst_rtsp_stream_get_property (GObject * object, guint propid,
    GValue * value, GParamSpec * pspec)
{
  GstRTSPStream *stream = GST_RTSP_STREAM (object);

  switch (propid) {
    case PROP_CONTROL:
      g_value_take_string (value, gst_rtsp_stream_get_control (stream));
      break;
469
470
471
    case PROP_PROFILES:
      g_value_set_flags (value, gst_rtsp_stream_get_profiles (stream));
      break;
Wim Taymans's avatar
Wim Taymans committed
472
473
474
    case PROP_PROTOCOLS:
      g_value_set_flags (value, gst_rtsp_stream_get_protocols (stream));
      break;
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
  }
}

static void
gst_rtsp_stream_set_property (GObject * object, guint propid,
    const GValue * value, GParamSpec * pspec)
{
  GstRTSPStream *stream = GST_RTSP_STREAM (object);

  switch (propid) {
    case PROP_CONTROL:
      gst_rtsp_stream_set_control (stream, g_value_get_string (value));
      break;
490
491
492
    case PROP_PROFILES:
      gst_rtsp_stream_set_profiles (stream, g_value_get_flags (value));
      break;
Wim Taymans's avatar
Wim Taymans committed
493
494
495
    case PROP_PROTOCOLS:
      gst_rtsp_stream_set_protocols (stream, g_value_get_flags (value));
      break;
496
497
498
499
500
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
  }
}

Wim Taymans's avatar
Wim Taymans committed
501
502
503
/**
 * gst_rtsp_stream_new:
 * @idx: an index
504
 * @pad: a #GstPad
Wim Taymans's avatar
Wim Taymans committed
505
506
507
 * @payloader: a #GstElement
 *
 * Create a new media stream with index @idx that handles RTP data on
508
509
 * @pad and has a payloader element @payloader if @pad is a source pad
 * or a depayloader element @payloader if @pad is a sink pad.
Wim Taymans's avatar
Wim Taymans committed
510
 *
511
 * Returns: (transfer full): a new #GstRTSPStream
Wim Taymans's avatar
Wim Taymans committed
512
513
 */
GstRTSPStream *
514
gst_rtsp_stream_new (guint idx, GstElement * payloader, GstPad * pad)
Wim Taymans's avatar
Wim Taymans committed
515
{
516
  GstRTSPStreamPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
517
518
519
  GstRTSPStream *stream;

  g_return_val_if_fail (GST_IS_ELEMENT (payloader), NULL);
520
  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
Wim Taymans's avatar
Wim Taymans committed
521
522

  stream = g_object_new (GST_TYPE_RTSP_STREAM, NULL);
523
524
525
  priv = stream->priv;
  priv->idx = idx;
  priv->payloader = gst_object_ref (payloader);
526
527
528
529
  if (GST_PAD_IS_SRC (pad))
    priv->srcpad = gst_object_ref (pad);
  else
    priv->sinkpad = gst_object_ref (pad);
Wim Taymans's avatar
Wim Taymans committed
530
531
532
533

  return stream;
}

534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
/**
 * gst_rtsp_stream_get_index:
 * @stream: a #GstRTSPStream
 *
 * Get the stream index.
 *
 * Return: the stream index.
 */
guint
gst_rtsp_stream_get_index (GstRTSPStream * stream)
{
  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), -1);

  return stream->priv->idx;
}

550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
/**
 * gst_rtsp_stream_get_pt:
 * @stream: a #GstRTSPStream
 *
 * Get the stream payload type.
 *
 * Return: the stream payload type.
 */
guint
gst_rtsp_stream_get_pt (GstRTSPStream * stream)
{
  GstRTSPStreamPrivate *priv;
  guint pt;

  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), -1);

  priv = stream->priv;

  g_object_get (G_OBJECT (priv->payloader), "pt", &pt, NULL);

  return pt;
}

573
/**
574
575
576
577
578
 * gst_rtsp_stream_get_srcpad:
 * @stream: a #GstRTSPStream
 *
 * Get the srcpad associated with @stream.
 *
579
 * Returns: (transfer full) (nullable): the srcpad. Unref after usage.
580
581
582
583
584
585
 */
GstPad *
gst_rtsp_stream_get_srcpad (GstRTSPStream * stream)
{
  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);

586
587
588
  if (!stream->priv->srcpad)
    return NULL;

589
590
591
  return gst_object_ref (stream->priv->srcpad);
}

592
593
594
595
596
597
/**
 * gst_rtsp_stream_get_sinkpad:
 * @stream: a #GstRTSPStream
 *
 * Get the sinkpad associated with @stream.
 *
598
 * Returns: (transfer full) (nullable): the sinkpad. Unref after usage.
599
600
601
602
603
604
605
606
607
608
609
610
 */
GstPad *
gst_rtsp_stream_get_sinkpad (GstRTSPStream * stream)
{
  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);

  if (!stream->priv->sinkpad)
    return NULL;

  return gst_object_ref (stream->priv->sinkpad);
}

611
612
613
614
615
616
/**
 * gst_rtsp_stream_get_control:
 * @stream: a #GstRTSPStream
 *
 * Get the control string to identify this stream.
 *
617
 * Returns: (transfer full) (nullable): the control string. g_free() after usage.
618
619
620
621
622
623
624
625
626
627
628
629
630
 */
gchar *
gst_rtsp_stream_get_control (GstRTSPStream * stream)
{
  GstRTSPStreamPrivate *priv;
  gchar *result;

  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);

  priv = stream->priv;

  g_mutex_lock (&priv->lock);
  if ((result = g_strdup (priv->control)) == NULL)
631
    result = g_strdup_printf ("stream=%u", priv->idx);
632
633
634
635
636
637
638
639
  g_mutex_unlock (&priv->lock);

  return result;
}

/**
 * gst_rtsp_stream_set_control:
 * @stream: a #GstRTSPStream
640
 * @control: (nullable): a control string
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
 *
 * Set the control string in @stream.
 */
void
gst_rtsp_stream_set_control (GstRTSPStream * stream, const gchar * control)
{
  GstRTSPStreamPrivate *priv;

  g_return_if_fail (GST_IS_RTSP_STREAM (stream));

  priv = stream->priv;

  g_mutex_lock (&priv->lock);
  g_free (priv->control);
  priv->control = g_strdup (control);
  g_mutex_unlock (&priv->lock);
}

659
660
661
/**
 * gst_rtsp_stream_has_control:
 * @stream: a #GstRTSPStream
662
 * @control: (nullable): a control string
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
 *
 * Check if @stream has the control string @control.
 *
 * Returns: %TRUE is @stream has @control as the control string
 */
gboolean
gst_rtsp_stream_has_control (GstRTSPStream * stream, const gchar * control)
{
  GstRTSPStreamPrivate *priv;
  gboolean res;

  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);

  priv = stream->priv;

  g_mutex_lock (&priv->lock);
  if (priv->control)
680
    res = (g_strcmp0 (priv->control, control) == 0);
681
682
  else {
    guint streamid;
683
684
685
686
687

    if (sscanf (control, "stream=%u", &streamid) > 0)
      res = (streamid == priv->idx);
    else
      res = FALSE;
688
689
690
691
692
693
  }
  g_mutex_unlock (&priv->lock);

  return res;
}

Wim Taymans's avatar
Wim Taymans committed
694
695
696
697
698
699
700
701
702
703
/**
 * gst_rtsp_stream_set_mtu:
 * @stream: a #GstRTSPStream
 * @mtu: a new MTU
 *
 * Configure the mtu in the payloader of @stream to @mtu.
 */
void
gst_rtsp_stream_set_mtu (GstRTSPStream * stream, guint mtu)
{
704
705
  GstRTSPStreamPrivate *priv;

Wim Taymans's avatar
Wim Taymans committed
706
707
  g_return_if_fail (GST_IS_RTSP_STREAM (stream));

708
709
  priv = stream->priv;

Wim Taymans's avatar
Wim Taymans committed
710
711
  GST_LOG_OBJECT (stream, "set MTU %u", mtu);

712
  g_object_set (G_OBJECT (priv->payloader), "mtu", mtu, NULL);
Wim Taymans's avatar
Wim Taymans committed
713
714
715
716
717
718
719
720
721
722
723
724
725
}

/**
 * gst_rtsp_stream_get_mtu:
 * @stream: a #GstRTSPStream
 *
 * Get the configured MTU in the payloader of @stream.
 *
 * Returns: the MTU of the payloader.
 */
guint
gst_rtsp_stream_get_mtu (GstRTSPStream * stream)
{
726
  GstRTSPStreamPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
727
728
729
730
  guint mtu;

  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), 0);

731
732
733
  priv = stream->priv;

  g_object_get (G_OBJECT (priv->payloader), "mtu", &mtu, NULL);
Wim Taymans's avatar
Wim Taymans committed
734
735
736
737

  return mtu;
}

738
739
/* Update the dscp qos property on the udp sinks */
static void
740
update_dscp_qos (GstRTSPStream * stream, GstElement ** udpsink)
741
742
743
744
745
{
  GstRTSPStreamPrivate *priv;

  priv = stream->priv;

746
747
  if (*udpsink) {
    g_object_set (G_OBJECT (*udpsink), "qos-dscp", priv->dscp_qos, NULL);
748
749
750
751
752
753
754
755
756
757
758
  }
}

/**
 * gst_rtsp_stream_set_dscp_qos:
 * @stream: a #GstRTSPStream
 * @dscp_qos: a new dscp qos value (0-63, or -1 to disable)
 *
 * Configure the dscp qos of the outgoing sockets to @dscp_qos.
 */
void
759
gst_rtsp_stream_set_dscp_qos (GstRTSPStream * stream, gint dscp_qos)
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
{
  GstRTSPStreamPrivate *priv;

  g_return_if_fail (GST_IS_RTSP_STREAM (stream));

  priv = stream->priv;

  GST_LOG_OBJECT (stream, "set DSCP QoS %d", dscp_qos);

  if (dscp_qos < -1 || dscp_qos > 63) {
    GST_WARNING_OBJECT (stream, "trying to set illegal dscp qos %d", dscp_qos);
    return;
  }

  priv->dscp_qos = dscp_qos;

776
  update_dscp_qos (stream, priv->udpsink);
777
778
779
780
781
782
783
784
785
786
787
}

/**
 * gst_rtsp_stream_get_dscp_qos:
 * @stream: a #GstRTSPStream
 *
 * Get the configured DSCP QoS in of the outgoing sockets.
 *
 * Returns: the DSCP QoS value of the outgoing sockets, or -1 if disbled.
 */
gint
788
gst_rtsp_stream_get_dscp_qos (GstRTSPStream * stream)
789
790
791
792
793
794
795
796
797
798
{
  GstRTSPStreamPrivate *priv;

  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), -1);

  priv = stream->priv;

  return priv->dscp_qos;
}

799
800
801
/**
 * gst_rtsp_stream_is_transport_supported:
 * @stream: a #GstRTSPStream
802
 * @transport: (transfer none): a #GstRTSPTransport
803
804
805
806
807
808
809
810
811
812
813
 *
 * Check if @transport can be handled by stream
 *
 * Returns: %TRUE if @transport can be handled by @stream.
 */
gboolean
gst_rtsp_stream_is_transport_supported (GstRTSPStream * stream,
    GstRTSPTransport * transport)
{
  GstRTSPStreamPrivate *priv;

Wim Taymans's avatar
Wim Taymans committed
814
  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
815
  g_return_val_if_fail (transport != NULL, FALSE);
816
817
818
819
820
821
822

  priv = stream->priv;

  g_mutex_lock (&priv->lock);
  if (transport->trans != GST_RTSP_TRANS_RTP)
    goto unsupported_transmode;

823
  if (!(transport->profile & priv->profiles))
824
825
    goto unsupported_profile;

826
  if (!(transport->lower_transport & priv->allowed_protocols))
827
828
829
830
831
832
833
834
835
836
    goto unsupported_ltrans;

  g_mutex_unlock (&priv->lock);

  return TRUE;

  /* ERRORS */
unsupported_transmode:
  {
    GST_DEBUG ("unsupported transport mode %d", transport->trans);
837
    g_mutex_unlock (&priv->lock);
838
839
840
841
842
    return FALSE;
  }
unsupported_profile:
  {
    GST_DEBUG ("unsupported profile %d", transport->profile);
843
    g_mutex_unlock (&priv->lock);
844
845
846
847
848
    return FALSE;
  }
unsupported_ltrans:
  {
    GST_DEBUG ("unsupported lower transport %d", transport->lower_transport);
849
    g_mutex_unlock (&priv->lock);
850
851
852
853
    return FALSE;
  }
}

854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
/**
 * gst_rtsp_stream_set_profiles:
 * @stream: a #GstRTSPStream
 * @profiles: the new profiles
 *
 * Configure the allowed profiles for @stream.
 */
void
gst_rtsp_stream_set_profiles (GstRTSPStream * stream, GstRTSPProfile profiles)
{
  GstRTSPStreamPrivate *priv;

  g_return_if_fail (GST_IS_RTSP_STREAM (stream));

  priv = stream->priv;

  g_mutex_lock (&priv->lock);
  priv->profiles = profiles;
  g_mutex_unlock (&priv->lock);
}

/**
 * gst_rtsp_stream_get_profiles:
 * @stream: a #GstRTSPStream
 *
 * Get the allowed profiles of @stream.
 *
 * Returns: a #GstRTSPProfile
 */
GstRTSPProfile
gst_rtsp_stream_get_profiles (GstRTSPStream * stream)
{
  GstRTSPStreamPrivate *priv;
  GstRTSPProfile res;

  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), GST_RTSP_PROFILE_UNKNOWN);

  priv = stream->priv;

  g_mutex_lock (&priv->lock);
  res = priv->profiles;
  g_mutex_unlock (&priv->lock);

  return res;
}

Wim Taymans's avatar
Wim Taymans committed
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
/**
 * gst_rtsp_stream_set_protocols:
 * @stream: a #GstRTSPStream
 * @protocols: the new flags
 *
 * Configure the allowed lower transport for @stream.
 */
void
gst_rtsp_stream_set_protocols (GstRTSPStream * stream,
    GstRTSPLowerTrans protocols)
{
  GstRTSPStreamPrivate *priv;

  g_return_if_fail (GST_IS_RTSP_STREAM (stream));

  priv = stream->priv;

  g_mutex_lock (&priv->lock);
918
  priv->allowed_protocols = protocols;
Wim Taymans's avatar
Wim Taymans committed
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
  g_mutex_unlock (&priv->lock);
}

/**
 * gst_rtsp_stream_get_protocols:
 * @stream: a #GstRTSPStream
 *
 * Get the allowed protocols of @stream.
 *
 * Returns: a #GstRTSPLowerTrans
 */
GstRTSPLowerTrans
gst_rtsp_stream_get_protocols (GstRTSPStream * stream)
{
  GstRTSPStreamPrivate *priv;
  GstRTSPLowerTrans res;

  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream),
      GST_RTSP_LOWER_TRANS_UNKNOWN);

  priv = stream->priv;

  g_mutex_lock (&priv->lock);
942
  res = priv->allowed_protocols;
Wim Taymans's avatar
Wim Taymans committed
943
944
945
946
  g_mutex_unlock (&priv->lock);

  return res;
}
947

948
949
950
/**
 * gst_rtsp_stream_set_address_pool:
 * @stream: a #GstRTSPStream
951
 * @pool: (transfer none) (nullable): a #GstRTSPAddressPool
952
953
954
955
956
957
958
 *
 * configure @pool to be used as the address pool of @stream.
 */
void
gst_rtsp_stream_set_address_pool (GstRTSPStream * stream,
    GstRTSPAddressPool * pool)
{
959
  GstRTSPStreamPrivate *priv;
960
961
962
963
  GstRTSPAddressPool *old;

  g_return_if_fail (GST_IS_RTSP_STREAM (stream));

964
965
  priv = stream->priv;

Wim Taymans's avatar
Wim Taymans committed
966
967
  GST_LOG_OBJECT (stream, "set address pool %p", pool);

968
969
970
  g_mutex_lock (&priv->lock);
  if ((old = priv->pool) != pool)
    priv->pool = pool ? g_object_ref (pool) : NULL;
971
972
  else
    old = NULL;
973
  g_mutex_unlock (&priv->lock);
974
975
976
977
978
979
980
981
982
983
984

  if (old)
    g_object_unref (old);
}

/**
 * gst_rtsp_stream_get_address_pool:
 * @stream: a #GstRTSPStream
 *
 * Get the #GstRTSPAddressPool used as the address pool of @stream.
 *
985
986
 * Returns: (transfer full) (nullable): the #GstRTSPAddressPool of @stream.
 * g_object_unref() after usage.
987
988
989
990
 */
GstRTSPAddressPool *
gst_rtsp_stream_get_address_pool (GstRTSPStream * stream)
{
991
  GstRTSPStreamPrivate *priv;
992
993
994
995
  GstRTSPAddressPool *result;

  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);

996
997
998
999
  priv = stream->priv;

  g_mutex_lock (&priv->lock);
  if ((result = priv->pool))
1000
    g_object_ref (result);