rtsp-media.c 108 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
/**
 * SECTION:rtsp-media
 * @short_description: The media pipeline
Wim Taymans's avatar
Wim Taymans committed
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
 * @see_also: #GstRTSPMediaFactory, #GstRTSPStream, #GstRTSPSession,
 *     #GstRTSPSessionMedia
 *
 * a #GstRTSPMedia contains the complete GStreamer pipeline to manage the
 * streaming to the clients. The actual data transfer is done by the
 * #GstRTSPStream objects that are created and exposed by the #GstRTSPMedia.
 *
 * The #GstRTSPMedia is usually created from a #GstRTSPMediaFactory when the
 * client does a DESCRIBE or SETUP of a resource.
 *
 * A media is created with gst_rtsp_media_new() that takes the element that will
 * provide the streaming elements. For each of the streams, a new #GstRTSPStream
 * object needs to be made with the gst_rtsp_media_create_stream() which takes
 * the payloader element and the source pad that produces the RTP stream.
 *
 * The pipeline of the media is set to PAUSED with gst_rtsp_media_prepare(). The
 * prepare method will add rtpbin and sinks and sources to send and receive RTP
 * and RTCP packets from the clients. Each stream srcpad is connected to an
 * input into the internal rtpbin.
 *
 * It is also possible to dynamically create #GstRTSPStream objects during the
 * prepare phase. With gst_rtsp_media_get_status() you can check the status of
 * the prepare phase.
 *
 * After the media is prepared, it is ready for streaming. It will usually be
 * managed in a session with gst_rtsp_session_manage_media(). See
 * #GstRTSPSession and #GstRTSPSessionMedia.
 *
 * The state of the media can be controlled with gst_rtsp_media_set_state ().
 * Seeking can be done with gst_rtsp_media_seek().
 *
 * With gst_rtsp_media_unprepare() the pipeline is stopped and shut down. When
 * gst_rtsp_media_set_eos_shutdown() an EOS will be sent to the pipeline to
 * cleanly shut down.
 *
 * With gst_rtsp_media_set_shared(), the media can be shared between multiple
 * clients. With gst_rtsp_media_set_reusable() you can control if the pipeline
 * can be prepared again after an unprepare.
Wim Taymans's avatar
Wim Taymans committed
62 63 64
 *
 * Last reviewed on 2013-07-11 (1.0.0)
 */
Wim Taymans's avatar
Wim Taymans committed
65

66
#include <stdio.h>
67
#include <string.h>
David Schleef's avatar
David Schleef committed
68
#include <stdlib.h>
69

70 71 72
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>

73 74 75 76 77 78 79 80 81
#include <gst/sdp/gstmikey.h>
#include <gst/rtp/gstrtppayloads.h>

#define AES_128_KEY_LEN 16
#define AES_256_KEY_LEN 32

#define HMAC_32_KEY_LEN 4
#define HMAC_80_KEY_LEN 10

Wim Taymans's avatar
Wim Taymans committed
82 83
#include "rtsp-media.h"

84 85 86 87 88 89 90 91
#define GST_RTSP_MEDIA_GET_PRIVATE(obj)  \
     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_MEDIA, GstRTSPMediaPrivate))

struct _GstRTSPMediaPrivate
{
  GMutex lock;
  GCond cond;

92
  /* protected by lock */
93
  GstRTSPPermissions *permissions;
94
  gboolean shared;
Wim Taymans's avatar
Wim Taymans committed
95
  gboolean suspend_mode;
96
  gboolean reusable;
97
  GstRTSPProfile profiles;
98 99 100 101 102
  GstRTSPLowerTrans protocols;
  gboolean reused;
  gboolean eos_shutdown;
  guint buffer_size;
  GstRTSPAddressPool *pool;
103
  gboolean blocked;
104
  GstRTSPTransportMode transport_mode;
105 106

  GstElement *element;
107 108 109 110
  GRecMutex state_lock;         /* locking order: state lock, lock */
  GPtrArray *streams;           /* protected by lock */
  GList *dynamic;               /* protected by lock */
  GstRTSPMediaStatus status;    /* protected by lock */
111
  gint prepare_count;
112 113 114 115 116
  gint n_active;
  gboolean adding;

  /* the pipeline for the media */
  GstElement *pipeline;
117
  GstElement *fakesink;         /* protected by lock */
118 119
  GSource *source;
  guint id;
120
  GstRTSPThread *thread;
121

122 123 124
  gboolean time_provider;
  GstNetTimeProvider *nettime;

125 126 127 128 129 130 131 132 133
  gboolean is_live;
  gboolean seekable;
  gboolean buffering;
  GstState target_state;

  /* RTP session manager */
  GstElement *rtpbin;

  /* the range of media */
134
  GstRTSPTimeRange range;       /* protected by lock */
135 136
  GstClockTime range_start;
  GstClockTime range_stop;
137 138 139

  GList *payloads;              /* protected by lock */
  GstClockTime rtx_time;        /* protected by lock */
140
  guint latency;                /* protected by lock */
141 142
};

143
#define DEFAULT_SHARED          FALSE
Wim Taymans's avatar
Wim Taymans committed
144
#define DEFAULT_SUSPEND_MODE    GST_RTSP_SUSPEND_MODE_NONE
145
#define DEFAULT_REUSABLE        FALSE
146
#define DEFAULT_PROFILES        GST_RTSP_PROFILE_AVP
147 148
#define DEFAULT_PROTOCOLS       GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST | \
                                        GST_RTSP_LOWER_TRANS_TCP
149 150
#define DEFAULT_EOS_SHUTDOWN    FALSE
#define DEFAULT_BUFFER_SIZE     0x80000
151
#define DEFAULT_TIME_PROVIDER   FALSE
152
#define DEFAULT_LATENCY         200
153
#define DEFAULT_TRANSPORT_MODE  GST_RTSP_TRANSPORT_MODE_PLAY
154

Wim Taymans's avatar
Wim Taymans committed
155 156 157
/* define to dump received RTCP packets */
#undef DUMP_STATS

158 159 160 161
enum
{
  PROP_0,
  PROP_SHARED,
Wim Taymans's avatar
Wim Taymans committed
162
  PROP_SUSPEND_MODE,
163
  PROP_REUSABLE,
164
  PROP_PROFILES,
165
  PROP_PROTOCOLS,
166
  PROP_EOS_SHUTDOWN,
167
  PROP_BUFFER_SIZE,
168
  PROP_ELEMENT,
169
  PROP_TIME_PROVIDER,
170
  PROP_LATENCY,
171
  PROP_TRANSPORT_MODE,
172 173 174
  PROP_LAST
};

175 176
enum
{
177
  SIGNAL_NEW_STREAM,
178
  SIGNAL_REMOVED_STREAM,
179
  SIGNAL_PREPARED,
180
  SIGNAL_UNPREPARED,
181
  SIGNAL_TARGET_STATE,
182
  SIGNAL_NEW_STATE,
183 184 185
  SIGNAL_LAST
};

186
GST_DEBUG_CATEGORY_STATIC (rtsp_media_debug);
187 188
#define GST_CAT_DEFAULT rtsp_media_debug

Wim Taymans's avatar
Wim Taymans committed
189 190 191 192
static void gst_rtsp_media_get_property (GObject * object, guint propid,
    GValue * value, GParamSpec * pspec);
static void gst_rtsp_media_set_property (GObject * object, guint propid,
    const GValue * value, GParamSpec * pspec);
Wim Taymans's avatar
Wim Taymans committed
193
static void gst_rtsp_media_finalize (GObject * obj);
Wim Taymans's avatar
Wim Taymans committed
194

Wim Taymans's avatar
Wim Taymans committed
195 196
static gboolean default_handle_message (GstRTSPMedia * media,
    GstMessage * message);
Wim Taymans's avatar
Wim Taymans committed
197
static void finish_unprepare (GstRTSPMedia * media);
198
static gboolean default_prepare (GstRTSPMedia * media, GstRTSPThread * thread);
Wim Taymans's avatar
Wim Taymans committed
199
static gboolean default_unprepare (GstRTSPMedia * media);
200 201
static gboolean default_suspend (GstRTSPMedia * media);
static gboolean default_unsuspend (GstRTSPMedia * media);
202 203
static gboolean default_convert_range (GstRTSPMedia * media,
    GstRTSPTimeRange * range, GstRTSPRangeUnit unit);
204 205 206
static gboolean default_query_position (GstRTSPMedia * media,
    gint64 * position);
static gboolean default_query_stop (GstRTSPMedia * media, gint64 * stop);
207
static GstElement *default_create_rtpbin (GstRTSPMedia * media);
208 209
static gboolean default_setup_sdp (GstRTSPMedia * media, GstSDPMessage * sdp,
    GstSDPInfo * info);
210
static gboolean default_handle_sdp (GstRTSPMedia * media, GstSDPMessage * sdp);
Wim Taymans's avatar
Wim Taymans committed
211

212 213
static gboolean wait_preroll (GstRTSPMedia * media);

214
static GstElement *find_payload_element (GstElement * payloader);
215

216 217
static guint gst_rtsp_media_signals[SIGNAL_LAST] = { 0 };

Wim Taymans's avatar
Wim Taymans committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
#define C_ENUM(v) ((gint) v)

GType
gst_rtsp_suspend_mode_get_type (void)
{
  static gsize id = 0;
  static const GEnumValue values[] = {
    {C_ENUM (GST_RTSP_SUSPEND_MODE_NONE), "GST_RTSP_SUSPEND_MODE_NONE", "none"},
    {C_ENUM (GST_RTSP_SUSPEND_MODE_PAUSE), "GST_RTSP_SUSPEND_MODE_PAUSE",
        "pause"},
    {C_ENUM (GST_RTSP_SUSPEND_MODE_RESET), "GST_RTSP_SUSPEND_MODE_RESET",
        "reset"},
    {0, NULL, NULL}
  };

  if (g_once_init_enter (&id)) {
    GType tmp = g_enum_register_static ("GstRTSPSuspendMode", values);
    g_once_init_leave (&id, tmp);
  }
  return (GType) id;
}

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
#define C_FLAGS(v) ((guint) v)

GType
gst_rtsp_transport_mode_get_type (void)
{
  static gsize id = 0;
  static const GFlagsValue values[] = {
    {C_FLAGS (GST_RTSP_TRANSPORT_MODE_PLAY), "GST_RTSP_TRANSPORT_MODE_PLAY",
        "play"},
    {C_FLAGS (GST_RTSP_TRANSPORT_MODE_RECORD), "GST_RTSP_TRANSPORT_MODE_RECORD",
        "record"},
    {0, NULL, NULL}
  };

  if (g_once_init_enter (&id)) {
    GType tmp = g_flags_register_static ("GstRTSPTransportMode", values);
    g_once_init_leave (&id, tmp);
  }
  return (GType) id;
}

Wim Taymans's avatar
Wim Taymans committed
261
G_DEFINE_TYPE (GstRTSPMedia, gst_rtsp_media, G_TYPE_OBJECT);
Wim Taymans's avatar
Wim Taymans committed
262 263

static void
Wim Taymans's avatar
Wim Taymans committed
264
gst_rtsp_media_class_init (GstRTSPMediaClass * klass)
Wim Taymans's avatar
Wim Taymans committed
265 266 267
{
  GObjectClass *gobject_class;

268 269
  g_type_class_add_private (klass, sizeof (GstRTSPMediaPrivate));

Wim Taymans's avatar
Wim Taymans committed
270 271
  gobject_class = G_OBJECT_CLASS (klass);

272 273
  gobject_class->get_property = gst_rtsp_media_get_property;
  gobject_class->set_property = gst_rtsp_media_set_property;
Wim Taymans's avatar
Wim Taymans committed
274
  gobject_class->finalize = gst_rtsp_media_finalize;
275 276

  g_object_class_install_property (gobject_class, PROP_SHARED,
Wim Taymans's avatar
Wim Taymans committed
277 278 279
      g_param_spec_boolean ("shared", "Shared",
          "If this media pipeline can be shared", DEFAULT_SHARED,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
280

Wim Taymans's avatar
Wim Taymans committed
281 282 283 284 285
  g_object_class_install_property (gobject_class, PROP_SUSPEND_MODE,
      g_param_spec_enum ("suspend-mode", "Suspend Mode",
          "How to suspend the media in PAUSED", GST_TYPE_RTSP_SUSPEND_MODE,
          DEFAULT_SUSPEND_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

286 287 288 289 290
  g_object_class_install_property (gobject_class, PROP_REUSABLE,
      g_param_spec_boolean ("reusable", "Reusable",
          "If this media pipeline can be reused after an unprepare",
          DEFAULT_REUSABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

291 292 293 294 295
  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));

296 297 298 299 300
  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));

301 302 303 304 305
  g_object_class_install_property (gobject_class, PROP_EOS_SHUTDOWN,
      g_param_spec_boolean ("eos-shutdown", "EOS Shutdown",
          "Send an EOS event to the pipeline before unpreparing",
          DEFAULT_EOS_SHUTDOWN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

306 307 308 309 310
  g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
      g_param_spec_uint ("buffer-size", "Buffer Size",
          "The kernel UDP buffer size to use", 0, G_MAXUINT,
          DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

311 312 313 314 315
  g_object_class_install_property (gobject_class, PROP_ELEMENT,
      g_param_spec_object ("element", "The Element",
          "The GstBin to use for streaming the media", GST_TYPE_ELEMENT,
          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));

316
  g_object_class_install_property (gobject_class, PROP_TIME_PROVIDER,
317 318 319 320
      g_param_spec_boolean ("time-provider", "Time Provider",
          "Use a NetTimeProvider for clients",
          DEFAULT_TIME_PROVIDER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

321 322 323 324 325
  g_object_class_install_property (gobject_class, PROP_LATENCY,
      g_param_spec_uint ("latency", "Latency",
          "Latency used for receiving media in milliseconds", 0, G_MAXUINT,
          DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

326 327
  g_object_class_install_property (gobject_class, PROP_TRANSPORT_MODE,
      g_param_spec_flags ("transport-mode", "Transport Mode",
328
          "If this media pipeline can be used for PLAY or RECORD",
329 330
          GST_TYPE_RTSP_TRANSPORT_MODE, DEFAULT_TRANSPORT_MODE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
331

332 333 334 335 336
  gst_rtsp_media_signals[SIGNAL_NEW_STREAM] =
      g_signal_new ("new-stream", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstRTSPMediaClass, new_stream), NULL, NULL,
      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_RTSP_STREAM);

337 338 339 340 341 342
  gst_rtsp_media_signals[SIGNAL_REMOVED_STREAM] =
      g_signal_new ("removed-stream", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaClass, removed_stream),
      NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
      GST_TYPE_RTSP_STREAM);

343 344 345
  gst_rtsp_media_signals[SIGNAL_PREPARED] =
      g_signal_new ("prepared", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstRTSPMediaClass, prepared), NULL, NULL,
346
      g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
347

348 349 350
  gst_rtsp_media_signals[SIGNAL_UNPREPARED] =
      g_signal_new ("unprepared", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstRTSPMediaClass, unprepared), NULL, NULL,
351
      g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
352

353 354
  gst_rtsp_media_signals[SIGNAL_TARGET_STATE] =
      g_signal_new ("target-state", G_TYPE_FROM_CLASS (klass),
355 356
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaClass, target_state),
      NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT);
357

358 359 360
  gst_rtsp_media_signals[SIGNAL_NEW_STATE] =
      g_signal_new ("new-state", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstRTSPMediaClass, new_state), NULL, NULL,
361
      g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT);
362

363 364
  GST_DEBUG_CATEGORY_INIT (rtsp_media_debug, "rtspmedia", 0, "GstRTSPMedia");

Wim Taymans's avatar
Wim Taymans committed
365
  klass->handle_message = default_handle_message;
366
  klass->prepare = default_prepare;
367
  klass->unprepare = default_unprepare;
368 369
  klass->suspend = default_suspend;
  klass->unsuspend = default_unsuspend;
370
  klass->convert_range = default_convert_range;
371 372
  klass->query_position = default_query_position;
  klass->query_stop = default_query_stop;
373
  klass->create_rtpbin = default_create_rtpbin;
374
  klass->setup_sdp = default_setup_sdp;
375
  klass->handle_sdp = default_handle_sdp;
Wim Taymans's avatar
Wim Taymans committed
376 377 378
}

static void
Wim Taymans's avatar
Wim Taymans committed
379
gst_rtsp_media_init (GstRTSPMedia * media)
Wim Taymans's avatar
Wim Taymans committed
380
{
381 382 383 384 385 386 387 388 389 390
  GstRTSPMediaPrivate *priv = GST_RTSP_MEDIA_GET_PRIVATE (media);

  media->priv = priv;

  priv->streams = g_ptr_array_new_with_free_func (g_object_unref);
  g_mutex_init (&priv->lock);
  g_cond_init (&priv->cond);
  g_rec_mutex_init (&priv->state_lock);

  priv->shared = DEFAULT_SHARED;
Wim Taymans's avatar
Wim Taymans committed
391
  priv->suspend_mode = DEFAULT_SUSPEND_MODE;
392
  priv->reusable = DEFAULT_REUSABLE;
393
  priv->profiles = DEFAULT_PROFILES;
394 395 396
  priv->protocols = DEFAULT_PROTOCOLS;
  priv->eos_shutdown = DEFAULT_EOS_SHUTDOWN;
  priv->buffer_size = DEFAULT_BUFFER_SIZE;
397
  priv->time_provider = DEFAULT_TIME_PROVIDER;
398
  priv->transport_mode = DEFAULT_TRANSPORT_MODE;
399 400
}

Wim Taymans's avatar
Wim Taymans committed
401
static void
Wim Taymans's avatar
Wim Taymans committed
402
gst_rtsp_media_finalize (GObject * obj)
Wim Taymans's avatar
Wim Taymans committed
403
{
404
  GstRTSPMediaPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
405
  GstRTSPMedia *media;
Wim Taymans's avatar
Wim Taymans committed
406

Wim Taymans's avatar
Wim Taymans committed
407
  media = GST_RTSP_MEDIA (obj);
408
  priv = media->priv;
Wim Taymans's avatar
Wim Taymans committed
409

410
  GST_INFO ("finalize media %p", media);
411

412 413 414
  if (priv->permissions)
    gst_rtsp_permissions_unref (priv->permissions);

415
  g_ptr_array_unref (priv->streams);
Wim Taymans's avatar
Wim Taymans committed
416

417
  g_list_free_full (priv->dynamic, gst_object_unref);
418

419 420
  if (priv->pipeline)
    gst_object_unref (priv->pipeline);
421 422
  if (priv->nettime)
    gst_object_unref (priv->nettime);
Wim Taymans's avatar
Wim Taymans committed
423
  gst_object_unref (priv->element);
424 425
  if (priv->pool)
    g_object_unref (priv->pool);
426 427
  if (priv->payloads)
    g_list_free (priv->payloads);
428 429 430
  g_mutex_clear (&priv->lock);
  g_cond_clear (&priv->cond);
  g_rec_mutex_clear (&priv->state_lock);
Wim Taymans's avatar
Wim Taymans committed
431

Wim Taymans's avatar
Wim Taymans committed
432
  G_OBJECT_CLASS (gst_rtsp_media_parent_class)->finalize (obj);
Wim Taymans's avatar
Wim Taymans committed
433 434
}

435
static void
Wim Taymans's avatar
Wim Taymans committed
436 437
gst_rtsp_media_get_property (GObject * object, guint propid,
    GValue * value, GParamSpec * pspec)
438 439 440 441
{
  GstRTSPMedia *media = GST_RTSP_MEDIA (object);

  switch (propid) {
442 443 444
    case PROP_ELEMENT:
      g_value_set_object (value, media->priv->element);
      break;
445 446 447
    case PROP_SHARED:
      g_value_set_boolean (value, gst_rtsp_media_is_shared (media));
      break;
Wim Taymans's avatar
Wim Taymans committed
448 449 450
    case PROP_SUSPEND_MODE:
      g_value_set_enum (value, gst_rtsp_media_get_suspend_mode (media));
      break;
451 452 453
    case PROP_REUSABLE:
      g_value_set_boolean (value, gst_rtsp_media_is_reusable (media));
      break;
454 455 456
    case PROP_PROFILES:
      g_value_set_flags (value, gst_rtsp_media_get_profiles (media));
      break;
457 458 459
    case PROP_PROTOCOLS:
      g_value_set_flags (value, gst_rtsp_media_get_protocols (media));
      break;
460 461 462
    case PROP_EOS_SHUTDOWN:
      g_value_set_boolean (value, gst_rtsp_media_is_eos_shutdown (media));
      break;
463 464 465
    case PROP_BUFFER_SIZE:
      g_value_set_uint (value, gst_rtsp_media_get_buffer_size (media));
      break;
466 467 468
    case PROP_TIME_PROVIDER:
      g_value_set_boolean (value, gst_rtsp_media_is_time_provider (media));
      break;
469 470 471
    case PROP_LATENCY:
      g_value_set_uint (value, gst_rtsp_media_get_latency (media));
      break;
472 473
    case PROP_TRANSPORT_MODE:
      g_value_set_flags (value, gst_rtsp_media_get_transport_mode (media));
474
      break;
475 476 477 478 479 480
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
  }
}

static void
Wim Taymans's avatar
Wim Taymans committed
481 482
gst_rtsp_media_set_property (GObject * object, guint propid,
    const GValue * value, GParamSpec * pspec)
483 484 485 486
{
  GstRTSPMedia *media = GST_RTSP_MEDIA (object);

  switch (propid) {
487 488
    case PROP_ELEMENT:
      media->priv->element = g_value_get_object (value);
489
      gst_object_ref_sink (media->priv->element);
490
      break;
491 492 493
    case PROP_SHARED:
      gst_rtsp_media_set_shared (media, g_value_get_boolean (value));
      break;
Wim Taymans's avatar
Wim Taymans committed
494 495 496
    case PROP_SUSPEND_MODE:
      gst_rtsp_media_set_suspend_mode (media, g_value_get_enum (value));
      break;
497 498 499
    case PROP_REUSABLE:
      gst_rtsp_media_set_reusable (media, g_value_get_boolean (value));
      break;
500 501 502
    case PROP_PROFILES:
      gst_rtsp_media_set_profiles (media, g_value_get_flags (value));
      break;
503 504 505
    case PROP_PROTOCOLS:
      gst_rtsp_media_set_protocols (media, g_value_get_flags (value));
      break;
506 507 508
    case PROP_EOS_SHUTDOWN:
      gst_rtsp_media_set_eos_shutdown (media, g_value_get_boolean (value));
      break;
509 510 511
    case PROP_BUFFER_SIZE:
      gst_rtsp_media_set_buffer_size (media, g_value_get_uint (value));
      break;
512 513 514
    case PROP_TIME_PROVIDER:
      gst_rtsp_media_use_time_provider (media, g_value_get_boolean (value));
      break;
515 516 517
    case PROP_LATENCY:
      gst_rtsp_media_set_latency (media, g_value_get_uint (value));
      break;
518 519
    case PROP_TRANSPORT_MODE:
      gst_rtsp_media_set_transport_mode (media, g_value_get_flags (value));
520
      break;
521 522 523 524 525
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
  }
}

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
typedef struct
{
  gint64 position;
  gboolean ret;
} DoQueryPositionData;

static void
do_query_position (GstRTSPStream * stream, DoQueryPositionData * data)
{
  gint64 tmp;

  if (gst_rtsp_stream_query_position (stream, &tmp)) {
    data->position = MAX (data->position, tmp);
    data->ret = TRUE;
  }
}

543 544 545
static gboolean
default_query_position (GstRTSPMedia * media, gint64 * position)
{
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
  GstRTSPMediaPrivate *priv;
  DoQueryPositionData data;

  priv = media->priv;

  data.position = -1;
  data.ret = FALSE;

  g_ptr_array_foreach (priv->streams, (GFunc) do_query_position, &data);

  *position = data.position;

  return data.ret;
}

typedef struct
{
  gint64 stop;
  gboolean ret;
} DoQueryStopData;

static void
do_query_stop (GstRTSPStream * stream, DoQueryStopData * data)
{
  gint64 tmp;

  if (gst_rtsp_stream_query_stop (stream, &tmp)) {
    data->stop = MAX (data->stop, tmp);
    data->ret = TRUE;
  }
576 577 578 579 580
}

static gboolean
default_query_stop (GstRTSPMedia * media, gint64 * stop)
{
581 582 583 584 585 586 587 588 589 590 591 592 593
  GstRTSPMediaPrivate *priv;
  DoQueryStopData data;

  priv = media->priv;

  data.stop = -1;
  data.ret = FALSE;

  g_ptr_array_foreach (priv->streams, (GFunc) do_query_stop, &data);

  *stop = data.stop;

  return data.ret;
594 595
}

596 597 598 599 600 601 602 603 604 605
static GstElement *
default_create_rtpbin (GstRTSPMedia * media)
{
  GstElement *rtpbin;

  rtpbin = gst_element_factory_make ("rtpbin", NULL);

  return rtpbin;
}

606
/* must be called with state lock */
Wim Taymans's avatar
Wim Taymans committed
607
static void
Wim Taymans's avatar
Wim Taymans committed
608
collect_media_stats (GstRTSPMedia * media)
Wim Taymans's avatar
Wim Taymans committed
609
{
610
  GstRTSPMediaPrivate *priv = media->priv;
611
  gint64 position = 0, stop = -1;
Wim Taymans's avatar
Wim Taymans committed
612

Wim Taymans's avatar
Wim Taymans committed
613 614 615 616
  if (priv->status != GST_RTSP_MEDIA_STATUS_PREPARED &&
      priv->status != GST_RTSP_MEDIA_STATUS_PREPARING)
    return;

617
  priv->range.unit = GST_RTSP_RANGE_NPT;
Wim Taymans's avatar
Wim Taymans committed
618

619 620
  GST_INFO ("collect media stats");

621 622 623 624 625 626 627
  if (priv->is_live) {
    priv->range.min.type = GST_RTSP_TIME_NOW;
    priv->range.min.seconds = -1;
    priv->range_start = -1;
    priv->range.max.type = GST_RTSP_TIME_END;
    priv->range.max.seconds = -1;
    priv->range_stop = -1;
Wim Taymans's avatar
Wim Taymans committed
628
  } else {
629 630 631 632 633
    GstRTSPMediaClass *klass;
    gboolean ret;

    klass = GST_RTSP_MEDIA_GET_CLASS (media);

Wim Taymans's avatar
Wim Taymans committed
634
    /* get the position */
635 636 637 638 639
    ret = FALSE;
    if (klass->query_position)
      ret = klass->query_position (media, &position);

    if (!ret) {
640
      GST_INFO ("position query failed");
Wim Taymans's avatar
Wim Taymans committed
641
      position = 0;
Wim Taymans's avatar
Wim Taymans committed
642
    }
Wim Taymans's avatar
Wim Taymans committed
643

644
    /* get the current segment stop */
645 646 647 648 649 650
    ret = FALSE;
    if (klass->query_stop)
      ret = klass->query_stop (media, &stop);

    if (!ret) {
      GST_INFO ("stop query failed");
651
      stop = -1;
Wim Taymans's avatar
Wim Taymans committed
652 653
    }

654 655
    GST_INFO ("stats: position %" GST_TIME_FORMAT ", stop %"
        GST_TIME_FORMAT, GST_TIME_ARGS (position), GST_TIME_ARGS (stop));
Wim Taymans's avatar
Wim Taymans committed
656

657
    if (position == -1) {
658 659 660
      priv->range.min.type = GST_RTSP_TIME_NOW;
      priv->range.min.seconds = -1;
      priv->range_start = -1;
Wim Taymans's avatar
Wim Taymans committed
661
    } else {
662 663 664
      priv->range.min.type = GST_RTSP_TIME_SECONDS;
      priv->range.min.seconds = ((gdouble) position) / GST_SECOND;
      priv->range_start = position;
Wim Taymans's avatar
Wim Taymans committed
665
    }
666
    if (stop == -1) {
667 668 669
      priv->range.max.type = GST_RTSP_TIME_END;
      priv->range.max.seconds = -1;
      priv->range_stop = -1;
Wim Taymans's avatar
Wim Taymans committed
670
    } else {
671
      priv->range.max.type = GST_RTSP_TIME_SECONDS;
672 673
      priv->range.max.seconds = ((gdouble) stop) / GST_SECOND;
      priv->range_stop = stop;
Wim Taymans's avatar
Wim Taymans committed
674 675 676 677
    }
  }
}

678 679
/**
 * gst_rtsp_media_new:
680
 * @element: (transfer full): a #GstElement
681
 *
682 683
 * Create a new #GstRTSPMedia instance. @element is the bin element that
 * provides the different streams. The #GstRTSPMedia object contains the
684
 * element to produce RTP data for one or more related (audio/video/..)
685 686
 * streams.
 *
687 688
 * Ownership is taken of @element.
 *
689
 * Returns: (transfer full): a new #GstRTSPMedia object.
690 691
 */
GstRTSPMedia *
692
gst_rtsp_media_new (GstElement * element)
693 694 695
{
  GstRTSPMedia *result;

696 697
  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);

698
  result = g_object_new (GST_TYPE_RTSP_MEDIA, "element", element, NULL);
699 700 701 702

  return result;
}

703 704 705 706 707 708
/**
 * gst_rtsp_media_get_element:
 * @media: a #GstRTSPMedia
 *
 * Get the element that was used when constructing @media.
 *
709
 * Returns: (transfer full): a #GstElement. Unref after usage.
710 711 712 713 714 715 716 717 718
 */
GstElement *
gst_rtsp_media_get_element (GstRTSPMedia * media)
{
  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);

  return gst_object_ref (media->priv->element);
}

719
/**
Wim Taymans's avatar
Wim Taymans committed
720
 * gst_rtsp_media_take_pipeline:
721 722 723 724 725 726 727 728 729 730 731
 * @media: a #GstRTSPMedia
 * @pipeline: (transfer full): a #GstPipeline
 *
 * Set @pipeline as the #GstPipeline for @media. Ownership is
 * taken of @pipeline.
 */
void
gst_rtsp_media_take_pipeline (GstRTSPMedia * media, GstPipeline * pipeline)
{
  GstRTSPMediaPrivate *priv;
  GstElement *old;
732
  GstNetTimeProvider *nettime;
733 734 735 736 737 738 739 740 741

  g_return_if_fail (GST_IS_RTSP_MEDIA (media));
  g_return_if_fail (GST_IS_PIPELINE (pipeline));

  priv = media->priv;

  g_mutex_lock (&priv->lock);
  old = priv->pipeline;
  priv->pipeline = GST_ELEMENT_CAST (pipeline);
742 743
  nettime = priv->nettime;
  priv->nettime = NULL;
744 745 746 747 748
  g_mutex_unlock (&priv->lock);

  if (old)
    gst_object_unref (old);

749 750 751
  if (nettime)
    gst_object_unref (nettime);

752 753 754
  gst_bin_add (GST_BIN_CAST (pipeline), priv->element);
}

755 756 757
/**
 * gst_rtsp_media_set_permissions:
 * @media: a #GstRTSPMedia
758
 * @permissions: (transfer none): a #GstRTSPPermissions
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
 *
 * Set @permissions on @media.
 */
void
gst_rtsp_media_set_permissions (GstRTSPMedia * media,
    GstRTSPPermissions * permissions)
{
  GstRTSPMediaPrivate *priv;

  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

  priv = media->priv;

  g_mutex_lock (&priv->lock);
  if (priv->permissions)
    gst_rtsp_permissions_unref (priv->permissions);
  if ((priv->permissions = permissions))
    gst_rtsp_permissions_ref (permissions);
  g_mutex_unlock (&priv->lock);
}

/**
 * gst_rtsp_media_get_permissions:
 * @media: a #GstRTSPMedia
 *
 * Get the permissions object from @media.
 *
 * Returns: (transfer full): a #GstRTSPPermissions object, unref after usage.
 */
GstRTSPPermissions *
gst_rtsp_media_get_permissions (GstRTSPMedia * media)
{
  GstRTSPMediaPrivate *priv;
  GstRTSPPermissions *result;

  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);

  priv = media->priv;

  g_mutex_lock (&priv->lock);
  if ((result = priv->permissions))
    gst_rtsp_permissions_ref (result);
  g_mutex_unlock (&priv->lock);

  return result;
}

Wim Taymans's avatar
Wim Taymans committed
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
/**
 * gst_rtsp_media_set_suspend_mode:
 * @media: a #GstRTSPMedia
 * @mode: the new #GstRTSPSuspendMode
 *
 * Control how @ media will be suspended after the SDP has been generated and
 * after a PAUSE request has been performed.
 *
 * Media must be unprepared when setting the suspend mode.
 */
void
gst_rtsp_media_set_suspend_mode (GstRTSPMedia * media, GstRTSPSuspendMode mode)
{
  GstRTSPMediaPrivate *priv;

  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

  priv = media->priv;

  g_rec_mutex_lock (&priv->state_lock);
  if (priv->status == GST_RTSP_MEDIA_STATUS_PREPARED)
    goto was_prepared;
  priv->suspend_mode = mode;
  g_rec_mutex_unlock (&priv->state_lock);

  return;

  /* ERRORS */
was_prepared:
  {
    GST_WARNING ("media %p was prepared", media);
    g_rec_mutex_unlock (&priv->state_lock);
  }
}

/**
 * gst_rtsp_media_get_suspend_mode:
 * @media: a #GstRTSPMedia
 *
 * Get how @media will be suspended.
 *
 * Returns: #GstRTSPSuspendMode.
 */
GstRTSPSuspendMode
gst_rtsp_media_get_suspend_mode (GstRTSPMedia * media)
{
  GstRTSPMediaPrivate *priv;
  GstRTSPSuspendMode res;

  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_RTSP_SUSPEND_MODE_NONE);

  priv = media->priv;

  g_rec_mutex_lock (&priv->state_lock);
  res = priv->suspend_mode;
  g_rec_mutex_unlock (&priv->state_lock);

  return res;
}

866 867 868 869 870 871 872 873 874 875
/**
 * gst_rtsp_media_set_shared:
 * @media: a #GstRTSPMedia
 * @shared: the new value
 *
 * Set or unset if the pipeline for @media can be shared will multiple clients.
 * When @shared is %TRUE, client requests for this media will share the media
 * pipeline.
 */
void
Wim Taymans's avatar
Wim Taymans committed
876
gst_rtsp_media_set_shared (GstRTSPMedia * media, gboolean shared)
877
{
878 879
  GstRTSPMediaPrivate *priv;

880 881
  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

882 883 884 885 886
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  priv->shared = shared;
  g_mutex_unlock (&priv->lock);
887 888 889 890 891 892 893 894 895 896 897
}

/**
 * gst_rtsp_media_is_shared:
 * @media: a #GstRTSPMedia
 *
 * Check if the pipeline for @media can be shared between multiple clients.
 *
 * Returns: %TRUE if the media can be shared between clients.
 */
gboolean
Wim Taymans's avatar
Wim Taymans committed
898
gst_rtsp_media_is_shared (GstRTSPMedia * media)
899
{
900
  GstRTSPMediaPrivate *priv;
901 902
  gboolean res;

903 904
  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);

905 906 907 908 909
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  res = priv->shared;
  g_mutex_unlock (&priv->lock);
910 911

  return res;
912
}
913

914 915 916 917 918 919 920 921 922
/**
 * gst_rtsp_media_set_reusable:
 * @media: a #GstRTSPMedia
 * @reusable: the new value
 *
 * Set or unset if the pipeline for @media can be reused after the pipeline has
 * been unprepared.
 */
void
Wim Taymans's avatar
Wim Taymans committed
923
gst_rtsp_media_set_reusable (GstRTSPMedia * media, gboolean reusable)
924
{
925 926
  GstRTSPMediaPrivate *priv;

927 928
  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

929 930 931 932 933
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  priv->reusable = reusable;
  g_mutex_unlock (&priv->lock);
934 935 936 937 938 939 940 941 942 943 944
}

/**
 * gst_rtsp_media_is_reusable:
 * @media: a #GstRTSPMedia
 *
 * Check if the pipeline for @media can be reused after an unprepare.
 *
 * Returns: %TRUE if the media can be reused
 */
gboolean
Wim Taymans's avatar
Wim Taymans committed
945
gst_rtsp_media_is_reusable (GstRTSPMedia * media)
946
{
947
  GstRTSPMediaPrivate *priv;
948 949
  gboolean res;

950 951
  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);

952 953 954 955 956
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  res = priv->reusable;
  g_mutex_unlock (&priv->lock);
957 958

  return res;
959 960
}

961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
static void
do_set_profiles (GstRTSPStream * stream, GstRTSPProfile * profiles)
{
  gst_rtsp_stream_set_profiles (stream, *profiles);
}

/**
 * gst_rtsp_media_set_profiles:
 * @media: a #GstRTSPMedia
 * @profiles: the new flags
 *
 * Configure the allowed lower transport for @media.
 */
void
gst_rtsp_media_set_profiles (GstRTSPMedia * media, GstRTSPProfile profiles)
{
  GstRTSPMediaPrivate *priv;

  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

  priv = media->priv;

  g_mutex_lock (&priv->lock);
  priv->profiles = profiles;
  g_ptr_array_foreach (priv->streams, (GFunc) do_set_profiles, &profiles);
  g_mutex_unlock (&priv->lock);
}

/**
 * gst_rtsp_media_get_profiles:
 * @media: a #GstRTSPMedia
 *
 * Get the allowed profiles of @media.
 *
 * Returns: a #GstRTSPProfile
 */
GstRTSPProfile
gst_rtsp_media_get_profiles (GstRTSPMedia * media)
{
  GstRTSPMediaPrivate *priv;
  GstRTSPProfile res;

  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_RTSP_PROFILE_UNKNOWN);

  priv = media->priv;

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

  return res;
}

1014 1015 1016 1017 1018 1019
static void
do_set_protocols (GstRTSPStream * stream, GstRTSPLowerTrans * protocols)
{
  gst_rtsp_stream_set_protocols (stream, *protocols);
}

1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
/**
 * gst_rtsp_media_set_protocols:
 * @media: a #GstRTSPMedia
 * @protocols: the new flags
 *
 * Configure the allowed lower transport for @media.
 */
void
gst_rtsp_media_set_protocols (GstRTSPMedia * media, GstRTSPLowerTrans protocols)
{
1030 1031
  GstRTSPMediaPrivate *priv;

1032 1033
  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

1034 1035 1036 1037
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  priv->protocols = protocols;
1038
  g_ptr_array_foreach (priv->streams, (GFunc) do_set_protocols, &protocols);
1039
  g_mutex_unlock (&priv->lock);
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
}

/**
 * gst_rtsp_media_get_protocols:
 * @media: a #GstRTSPMedia
 *
 * Get the allowed protocols of @media.
 *
 * Returns: a #GstRTSPLowerTrans
 */
GstRTSPLowerTrans
gst_rtsp_media_get_protocols (GstRTSPMedia * media)
{
1053
  GstRTSPMediaPrivate *priv;
1054 1055
  GstRTSPLowerTrans res;

1056 1057
  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media),
      GST_RTSP_LOWER_TRANS_UNKNOWN);
1058

1059 1060 1061 1062 1063
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  res = priv->protocols;
  g_mutex_unlock (&priv->lock);
1064 1065

  return res;
1066 1067
}

1068 1069 1070 1071 1072 1073 1074 1075 1076
/**
 * gst_rtsp_media_set_eos_shutdown:
 * @media: a #GstRTSPMedia
 * @eos_shutdown: the new value
 *
 * Set or unset if an EOS event will be sent to the pipeline for @media before
 * it is unprepared.
 */
void
1077
gst_rtsp_media_set_eos_shutdown (GstRTSPMedia * media, gboolean eos_shutdown)
1078
{
1079 1080
  GstRTSPMediaPrivate *priv;

1081 1082
  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

1083 1084 1085 1086 1087
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  priv->eos_shutdown = eos_shutdown;
  g_mutex_unlock (&priv->lock);
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
}

/**
 * gst_rtsp_media_is_eos_shutdown:
 * @media: a #GstRTSPMedia
 *
 * Check if the pipeline for @media will send an EOS down the pipeline before
 * unpreparing.
 *
 * Returns: %TRUE if the media will send EOS before unpreparing.
 */
gboolean
1100
gst_rtsp_media_is_eos_shutdown (GstRTSPMedia * media)
1101
{
1102
  GstRTSPMediaPrivate *priv;
1103 1104
  gboolean res;

1105 1106
  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);

1107 1108 1109 1110 1111
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  res = priv->eos_shutdown;
  g_mutex_unlock (&priv->lock);
1112 1113

  return res;
1114 1115
}

1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
/**
 * gst_rtsp_media_set_buffer_size:
 * @media: a #GstRTSPMedia
 * @size: the new value
 *
 * Set the kernel UDP buffer size.
 */
void
gst_rtsp_media_set_buffer_size (GstRTSPMedia * media, guint size)
{
1126
  GstRTSPMediaPrivate *priv;
1127
  guint i;
1128

1129 1130
  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

Wim Taymans's avatar
Wim Taymans committed
1131 1132
  GST_LOG_OBJECT (media, "set buffer size %u", size);

1133 1134 1135 1136
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  priv->buffer_size = size;
1137 1138 1139 1140 1141

  for (i = 0; i < priv->streams->len; i++) {
    GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
    gst_rtsp_stream_set_buffer_size (stream, size);
  }
1142
  g_mutex_unlock (&priv->lock);
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
}

/**
 * gst_rtsp_media_get_buffer_size:
 * @media: a #GstRTSPMedia
 *
 * Get the kernel UDP buffer size.
 *
 * Returns: the kernel UDP buffer size.
 */
guint
gst_rtsp_media_get_buffer_size (GstRTSPMedia * media)
{
1156
  GstRTSPMediaPrivate *priv;
1157 1158
  guint res;

1159 1160
  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);

1161 1162
  priv = media->priv;

1163
  g_mutex_lock (&priv->lock);
1164 1165
  res = priv->buffer_size;
  g_mutex_unlock (&priv->lock);
1166 1167

  return res;
1168 1169
}

1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
/**
 * gst_rtsp_media_set_retransmission_time:
 * @media: a #GstRTSPMedia
 * @time: the new value
 *
 * Set the amount of time to store retransmission packets.
 */
void
gst_rtsp_media_set_retransmission_time (GstRTSPMedia * media, GstClockTime time)
{
  GstRTSPMediaPrivate *priv;
  guint i;

  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

  GST_LOG_OBJECT (media, "set retransmission time %" G_GUINT64_FORMAT, time);

  priv = media->priv;

  g_mutex_lock (&priv->lock);
  priv->rtx_time = time;
  for (i = 0; i < priv->streams->len; i++) {
    GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);

    gst_rtsp_stream_set_retransmission_time (stream, time);
  }

  if (priv->rtpbin)
    g_object_set (priv->rtpbin, "do-retransmission", time > 0, NULL);
  g_mutex_unlock (&priv->lock);
}

/**
 * gst_rtsp_media_get_retransmission_time:
 * @media: a #GstRTSPMedia
 *
 * Get the amount of time to store retransmission data.
 *
 * Returns: the amount of time to store retransmission data.
 */
GstClockTime
gst_rtsp_media_get_retransmission_time (GstRTSPMedia * media)
{
  GstRTSPMediaPrivate *priv;
  GstClockTime res;

  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);

  priv = media->priv;

1220
  g_mutex_lock (&priv->lock);
1221 1222 1223 1224 1225 1226
  res = priv->rtx_time;
  g_mutex_unlock (&priv->lock);

  return res;
}

1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
/**
 * gst_rtsp_media_set_latncy:
 * @media: a #GstRTSPMedia
 * @latency: latency in milliseconds
 *
 * Configure the latency used for receiving media.
 */
void
gst_rtsp_media_set_latency (GstRTSPMedia * media, guint latency)
{
  GstRTSPMediaPrivate *priv;

  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

  GST_LOG_OBJECT (media, "set latency %ums", latency);

  priv = media->priv;

  g_mutex_lock (&priv->lock);
  priv->latency = latency;
  if (priv->rtpbin)
    g_object_set (priv->rtpbin, "latency", latency, NULL);
  g_mutex_unlock (&priv->lock);
}

/**
 * gst_rtsp_media_get_latency:
 * @media: a #GstRTSPMedia
 *
 * Get the latency that is used for receiving media.
 *
 * Returns: latency in milliseconds
 */
guint
gst_rtsp_media_get_latency (GstRTSPMedia * media)
{
  GstRTSPMediaPrivate *priv;
  guint res;

  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);

  priv = media->priv;

1270
  g_mutex_lock (&priv->lock);
1271 1272 1273 1274 1275 1276
  res = priv->latency;
  g_mutex_unlock (&priv->lock);

  return res;
}

1277 1278 1279
/**
 * gst_rtsp_media_use_time_provider:
 * @media: a #GstRTSPMedia
Wim Taymans's avatar
Wim Taymans committed
1280
 * @time_provider: if a #GstNetTimeProvider should be used
1281
 *
Wim Taymans's avatar
Wim Taymans committed
1282
 * Set @media to provide a #GstNetTimeProvider.
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317
 */
void
gst_rtsp_media_use_time_provider (GstRTSPMedia * media, gboolean time_provider)
{
  GstRTSPMediaPrivate *priv;

  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

  priv = media->priv;

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

/**
 * gst_rtsp_media_is_time_provider:
 * @media: a #GstRTSPMedia
 *
 * Check if @media can provide a #GstNetTimeProvider for its pipeline clock.
 *
 * Use gst_rtsp_media_get_time_provider() to get the network clock.
 *
 * Returns: %TRUE if @media can provide a #GstNetTimeProvider.
 */
gboolean
gst_rtsp_media_is_time_provider (GstRTSPMedia * media)
{
  GstRTSPMediaPrivate *priv;
  gboolean res;

  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);

  priv = media->priv;

1318
  g_mutex_lock (&priv->lock);
1319 1320 1321 1322 1323 1324
  res = priv->time_provider;
  g_mutex_unlock (&priv->lock);

  return res;
}

1325
/**
Wim Taymans's avatar
Wim Taymans committed
1326
 * gst_rtsp_media_set_address_pool:
1327
 * @media: a #GstRTSPMedia
1328
 * @pool: (transfer none): a #GstRTSPAddressPool
1329
 *
Wim Taymans's avatar
Wim Taymans committed
1330
 * configure @pool to be used as the address pool of @media.
1331 1332
 */
void
Wim Taymans's avatar
Wim Taymans committed
1333 1334
gst_rtsp_media_set_address_pool (GstRTSPMedia * media,
    GstRTSPAddressPool * pool)
1335
{
1336
  GstRTSPMediaPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
1337
  GstRTSPAddressPool *old;
1338 1339 1340

  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

1341 1342
  priv = media->priv;

Wim Taymans's avatar
Wim Taymans committed
1343 1344
  GST_LOG_OBJECT (media, "set address pool %p", pool);

1345 1346 1347
  g_mutex_lock (&priv->lock);
  if ((old = priv->pool) != pool)
    priv->pool = pool ? g_object_ref (pool) : NULL;
1348 1349
  else
    old = NULL;
1350
  g_ptr_array_foreach (priv->streams, (GFunc) gst_rtsp_stream_set_address_pool,
1351
      pool);
1352
  g_mutex_unlock (&priv->lock);
1353

1354 1355
  if (old)
    g_object_unref (old);
1356 1357 1358
}

/**
Wim Taymans's avatar
Wim Taymans committed
1359
 * gst_rtsp_media_get_address_pool:
1360 1361
 * @media: a #GstRTSPMedia
 *
Wim Taymans's avatar
Wim Taymans committed
1362
 * Get the #GstRTSPAddressPool used as the address pool of @media.
1363
 *
Wim Taymans's avatar
Wim Taymans committed
1364
 * Returns: (transfer full): the #GstRTSPAddressPool of @media. g_object_unref() after
1365 1366
 * usage.
 */
Wim Taymans's avatar
Wim Taymans committed
1367 1368
GstRTSPAddressPool *
gst_rtsp_media_get_address_pool (GstRTSPMedia * media)
1369
{
1370
  GstRTSPMediaPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
1371
  GstRTSPAddressPool *result;
1372 1373 1374

  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);

1375 1376 1377 1378
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  if ((result = priv->pool))
1379
    g_object_ref (result);
1380
  g_mutex_unlock (&priv->lock);
1381 1382 1383 1384

  return result;
}

1385 1386 1387 1388
static GList *
_find_payload_types (GstRTSPMedia * media)
{
  gint i, n;
1389
  GQueue queue = G_QUEUE_INIT;
1390 1391 1392 1393 1394 1395

  n = media->priv->streams->len;
  for (i = 0; i < n; i++) {
    GstRTSPStream *stream = g_ptr_array_index (media->priv->streams, i);
    guint pt = gst_rtsp_stream_get_pt (stream);

1396
    g_queue_push_tail (&queue, GUINT_TO_POINTER (pt));
1397 1398
  }

1399
  return queue.head;
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415
}

static guint
_next_available_pt (GList * payloads)
{
  guint i;

  for (i = 96; i <= 127; i++) {
    GList *iter = g_list_find (payloads, GINT_TO_POINTER (i));
    if (!iter)
      return GPOINTER_TO_UINT (i);
  }

  return 0;
}

Wim Taymans's avatar
Wim Taymans committed
1416 1417 1418 1419
/**
 * gst_rtsp_media_collect_streams:
 * @media: a #GstRTSPMedia
 *
1420
 * Find all payloader elements, they should be named pay\%d in the
Wim Taymans's avatar
Wim Taymans committed
1421 1422
 * element of @media, and create #GstRTSPStreams for them.
 *
1423
 * Collect all dynamic elements, named dynpay\%d, and add them to
Wim Taymans's avatar
Wim Taymans committed
1424
 * the list of dynamic elements.
1425 1426 1427
 *
 * Find all depayloader elements, they should be named depay\%d in the
 * element of @media, and create #GstRTSPStreams for them.
Wim Taymans's avatar
Wim Taymans committed
1428 1429 1430 1431
 */
void
gst_rtsp_media_collect_streams (GstRTSPMedia * media)
{
1432
  GstRTSPMediaPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
1433 1434 1435 1436
  GstElement *element, *elem;
  GstPad *pad;
  gint i;
  gboolean have_elem;
1437
  gboolean more_elem_remaining = TRUE;
1438
  GstRTSPTransportMode mode = 0;
Wim Taymans's avatar
Wim Taymans committed
1439 1440 1441

  g_return_if_fail (GST_IS_RTSP_MEDIA (media));

1442 1443
  priv = media->priv;
  element = priv->element;
Wim Taymans's avatar
Wim Taymans committed
1444

1445 1446
  have_elem = FALSE;
  for (i = 0; more_elem_remaining; i++) {
Wim Taymans's avatar
Wim Taymans committed
1447 1448
    gchar *name;

1449
    more_elem_remaining = FALSE;
Wim Taymans's avatar
Wim Taymans committed
1450 1451 1452

    name = g_strdup_printf ("pay%d", i);
    if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
1453
      GstElement *pay;
Wim Taymans's avatar
Wim Taymans committed
1454 1455 1456 1457
      GST_INFO ("found stream %d with payloader %p", i, elem);

      /* take the pad of the payloader */
      pad = gst_element_get_static_pad (elem, "src");
1458 1459 1460 1461

      /* find the real payload element in case elem is a GstBin */
      pay = find_payload_element (elem);

Wim Taymans's avatar
Wim Taymans committed
1462
      /* create the stream */
1463 1464 1465 1466 1467 1468 1469 1470
      if (pay == NULL) {
        GST_WARNING ("could not find real payloader, using bin");
        gst_rtsp_media_create_stream (media, elem, pad);
      } else {
        gst_rtsp_media_create_stream (media, pay, pad);
        gst_object_unref (pay);
      }

1471
      gst_object_unref (pad);
Wim Taymans's avatar
Wim Taymans committed
1472 1473 1474
      gst_object_unref (elem);

      have_elem = TRUE;
1475
      more_elem_remaining = TRUE;
1476
      mode |= GST_RTSP_TRANSPORT_MODE_PLAY;
Wim Taymans's avatar
Wim Taymans committed
1477 1478 1479 1480 1481 1482 1483 1484
    }
    g_free (name);

    name = g_strdup_printf ("dynpay%d", i);
    if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
      /* a stream that will dynamically create pads to provide RTP packets */
      GST_INFO ("found dynamic element %d, %p", i, elem);

1485 1486 1487
      g_mutex_lock (&priv->lock);
      priv->dynamic = g_list_prepend (priv->dynamic, elem);
      g_mutex_unlock (&priv->lock);
Wim Taymans's avatar
Wim Taymans committed
1488 1489

      have_elem = TRUE;
1490
      more_elem_remaining = TRUE;
1491
      mode |= GST_RTSP_TRANSPORT_MODE_PLAY;
Wim Taymans's avatar
Wim Taymans committed
1492 1493
    }
    g_free (name);
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506

    name = g_strdup_printf ("depay%d", i);
    if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
      GST_INFO ("found stream %d with depayloader %p", i, elem);

      /* take the pad of the payloader */
      pad = gst_element_get_static_pad (elem, "sink");
      /* create the stream */
      gst_rtsp_media_create_stream (media, elem, pad);
      gst_object_unref (pad);
      gst_object_unref (elem);

      have_elem = TRUE;
1507
      more_elem_remaining = TRUE;
1508
      mode |= GST_RTSP_TRANSPORT_MODE_RECORD;
1509 1510
    }
    g_free (name);
Wim Taymans's avatar
Wim Taymans committed
1511
  }
1512 1513 1514 1515 1516 1517

  if (have_elem) {
    if (priv->transport_mode != mode)
      GST_WARNING ("found different mode than expected (0x%02x != 0x%02d)",
          priv->transport_mode, mode);
  }
Wim Taymans's avatar
Wim Taymans committed
1518 1519 1520 1521 1522 1523
}

/**
 * gst_rtsp_media_create_stream:
 * @media: a #GstRTSPMedia
 * @payloader: a #GstElement
1524
 * @pad: a #GstPad
Wim Taymans's avatar
Wim Taymans committed
1525
 *
1526 1527
 * Create a new stream in @media that provides RTP data on @pad.
 * @pad should be a pad of an element inside @media->element.
Wim Taymans's avatar
Wim Taymans committed
1528
 *
1529
 * Returns: (transfer none): a new #GstRTSPStream that remains valid for as long
1530
 * as @media exists.
Wim Taymans's avatar
Wim Taymans committed
1531 1532 1533 1534 1535
 */
GstRTSPStream *
gst_rtsp_media_create_stream (GstRTSPMedia * media, GstElement * payloader,
    GstPad * pad)
{
1536
  GstRTSPMediaPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
1537
  GstRTSPStream *stream;
1538
  GstPad *ghostpad;
Wim Taymans's avatar
Wim Taymans committed
1539 1540 1541 1542 1543 1544 1545
  gchar *name;
  gint idx;

  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
  g_return_val_if_fail (GST_IS_ELEMENT (payloader), NULL);
  g_return_val_if_fail (GST_IS_PAD (pad), NULL);

1546 1547 1548 1549 1550 1551
  priv = media->priv;

  g_mutex_lock (&priv->lock);
  idx = priv->streams->len;

  GST_DEBUG ("media %p: creating stream with index %d", media, idx);
Wim Taymans's avatar
Wim Taymans committed
1552