gstrtpsession.c 84.4 KB
Newer Older
1
/* GStreamer
2
 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * 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
16 17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
18 19 20
 */

/**
21 22
 * SECTION:element-rtpsession
 * @see_also: rtpjitterbuffer, rtpbin, rtpptdemux, rtpssrcdemux
23
 *
Wim Taymans's avatar
Wim Taymans committed
24
 * The RTP session manager models participants with unique SSRC in an RTP
Wim Taymans's avatar
Wim Taymans committed
25 26 27
 * session. This session can be used to send and receive RTP and RTCP packets.
 * Based on what REQUEST pads are requested from the session manager, specific
 * functionality can be activated.
28
 *
Wim Taymans's avatar
Wim Taymans committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42
 * The session manager currently implements RFC 3550 including:
 * <itemizedlist>
 *   <listitem>
 *     <para>RTP packet validation based on consecutive sequence numbers.</para>
 *   </listitem>
 *   <listitem>
 *     <para>Maintainance of the SSRC participant database.</para>
 *   </listitem>
 *   <listitem>
 *     <para>Keeping per participant statistics based on received RTCP packets.</para>
 *   </listitem>
 *   <listitem>
 *     <para>Scheduling of RR/SR RTCP packets.</para>
 *   </listitem>
Wim Taymans's avatar
Wim Taymans committed
43 44 45
 *   <listitem>
 *     <para>Support for multiple sender SSRC.</para>
 *   </listitem>
Wim Taymans's avatar
Wim Taymans committed
46
 * </itemizedlist>
47
 *
48
 * The rtpsession will not demux packets based on SSRC or payload type, nor will
49 50 51 52
 * it correct for packet reordering and jitter. Use #GstRtpsSrcDemux,
 * #GstRtpPtDemux and GstRtpJitterBuffer in addition to #GstRtpSession to
 * perform these tasks. It is usually a good idea to use #GstRtpBin, which
 * combines all these features in one element.
53
 *
54
 * To use #GstRtpSession as an RTP receiver, request a recv_rtp_sink pad, which will
Wim Taymans's avatar
Wim Taymans committed
55 56 57
 * automatically create recv_rtp_src pad. Data received on the recv_rtp_sink pad
 * will be processed in the session and after being validated forwarded on the
 * recv_rtp_src pad.
58
 *
59
 * To also use #GstRtpSession as an RTCP receiver, request a recv_rtcp_sink pad,
Wim Taymans's avatar
Wim Taymans committed
60 61 62 63
 * which will automatically create a sync_src pad. Packets received on the RTCP
 * pad will be used by the session manager to update the stats and database of
 * the other participants. SR packets will be forwarded on the sync_src pad
 * so that they can be used to perform inter-stream synchronisation when needed.
64
 *
Wim Taymans's avatar
Wim Taymans committed
65 66 67
 * If you want the session manager to generate and send RTCP packets, request
 * the send_rtcp_src pad. Packet pushed on this pad contain SR/RR RTCP reports
 * that should be sent to all participants in the session.
68
 *
69
 * To use #GstRtpSession as a sender, request a send_rtp_sink pad, which will
Wim Taymans's avatar
Wim Taymans committed
70 71
 * automatically create a send_rtp_src pad. The session manager will
 * forward the packets on the send_rtp_src pad after updating its internal state.
72
 *
Wim Taymans's avatar
Wim Taymans committed
73
 * The session manager needs the clock-rate of the payload types it is handling
74 75
 * and will signal the #GstRtpSession::request-pt-map signal when it needs such a
 * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map
Wim Taymans's avatar
Wim Taymans committed
76
 * signal.
77
 *
78
 * <refsect2>
79
 * <title>Example pipelines</title>
80
 * |[
81
 * gst-launch-1.0 udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink rtpsession .recv_rtp_src ! rtptheoradepay ! theoradec ! xvimagesink
82
 * ]| Receive theora RTP packets from port 5000 and send them to the depayloader,
Wim Taymans's avatar
Wim Taymans committed
83 84 85
 * decoder and display. Note that the application/x-rtp caps on udpsrc should be
 * configured based on some negotiation process such as RTSP for this pipeline
 * to work correctly.
86
 * |[
87
 * gst-launch-1.0 udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink rtpsession name=session \
Wim Taymans's avatar
Wim Taymans committed
88 89
 *        .recv_rtp_src ! rtptheoradepay ! theoradec ! xvimagesink \
 *     udpsrc port=5001 caps="application/x-rtcp" ! session.recv_rtcp_sink
90
 * ]| Receive theora RTP packets from port 5000 and send them to the depayloader,
Wim Taymans's avatar
Wim Taymans committed
91 92 93 94 95
 * decoder and display. Receive RTCP packets from port 5001 and process them in
 * the session manager.
 * Note that the application/x-rtp caps on udpsrc should be
 * configured based on some negotiation process such as RTSP for this pipeline
 * to work correctly.
96
 * |[
97
 * gst-launch-1.0 videotestsrc ! theoraenc ! rtptheorapay ! .send_rtp_sink rtpsession .send_rtp_src ! udpsink port=5000
98 99 100
 * ]| Send theora RTP packets through the session manager and out on UDP port
 * 5000.
 * |[
101
 * gst-launch-1.0 videotestsrc ! theoraenc ! rtptheorapay ! .send_rtp_sink rtpsession name=session .send_rtp_src \
Wim Taymans's avatar
Wim Taymans committed
102
 *     ! udpsink port=5000  session.send_rtcp_src ! udpsink port=5001
103 104
 * ]| Send theora RTP packets through the session manager and out on UDP port
 * 5000. Send RTCP packets on port 5001. Note that this pipeline will not preroll
Wim Taymans's avatar
Wim Taymans committed
105 106
 * correctly because the second udpsink will not preroll correctly (no RTCP
 * packets are sent in the PAUSED state). Applications should manually set and
107
 * keep (see gst_element_set_locked_state()) the RTCP udpsink to the PLAYING state.
108 109 110 111 112 113
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
114

115 116
#include <gst/rtp/gstrtpbuffer.h>

117 118
#include <gst/glib-compat-private.h>

119
#include "gstrtpsession.h"
120
#include "rtpsession.h"
121

122 123 124
GST_DEBUG_CATEGORY_STATIC (gst_rtp_session_debug);
#define GST_CAT_DEFAULT gst_rtp_session_debug

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
GType
gst_rtp_ntp_time_source_get_type (void)
{
  static GType type = 0;
  static const GEnumValue values[] = {
    {GST_RTP_NTP_TIME_SOURCE_NTP, "NTP time based on realtime clock", "ntp"},
    {GST_RTP_NTP_TIME_SOURCE_UNIX, "UNIX time based on realtime clock", "unix"},
    {GST_RTP_NTP_TIME_SOURCE_RUNNING_TIME,
          "Running time based on pipeline clock",
        "running-time"},
    {GST_RTP_NTP_TIME_SOURCE_CLOCK_TIME, "Pipeline clock time", "clock-time"},
    {0, NULL, NULL},
  };

  if (!type) {
    type = g_enum_register_static ("GstRtpNtpTimeSource", values);
  }
  return type;
}

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
/* sink pads */
static GstStaticPadTemplate rtpsession_recv_rtp_sink_template =
GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("application/x-rtp")
    );

static GstStaticPadTemplate rtpsession_recv_rtcp_sink_template =
GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("application/x-rtcp")
    );

static GstStaticPadTemplate rtpsession_send_rtp_sink_template =
GST_STATIC_PAD_TEMPLATE ("send_rtp_sink",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("application/x-rtp")
    );

/* src pads */
static GstStaticPadTemplate rtpsession_recv_rtp_src_template =
GST_STATIC_PAD_TEMPLATE ("recv_rtp_src",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS ("application/x-rtp")
    );

static GstStaticPadTemplate rtpsession_sync_src_template =
GST_STATIC_PAD_TEMPLATE ("sync_src",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS ("application/x-rtcp")
    );

static GstStaticPadTemplate rtpsession_send_rtp_src_template =
GST_STATIC_PAD_TEMPLATE ("send_rtp_src",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS ("application/x-rtp")
    );

189 190
static GstStaticPadTemplate rtpsession_send_rtcp_src_template =
GST_STATIC_PAD_TEMPLATE ("send_rtcp_src",
191 192 193 194 195 196 197 198
    GST_PAD_SRC,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("application/x-rtcp")
    );

/* signals and args */
enum
{
199
  SIGNAL_REQUEST_PT_MAP,
Wim Taymans's avatar
Wim Taymans committed
200
  SIGNAL_CLEAR_PT_MAP,
201 202 203 204

  SIGNAL_ON_NEW_SSRC,
  SIGNAL_ON_SSRC_COLLISION,
  SIGNAL_ON_SSRC_VALIDATED,
205
  SIGNAL_ON_SSRC_ACTIVE,
206
  SIGNAL_ON_SSRC_SDES,
207 208 209
  SIGNAL_ON_BYE_SSRC,
  SIGNAL_ON_BYE_TIMEOUT,
  SIGNAL_ON_TIMEOUT,
210
  SIGNAL_ON_SENDER_TIMEOUT,
211 212
  SIGNAL_ON_NEW_SENDER_SSRC,
  SIGNAL_ON_SENDER_SSRC_ACTIVE,
213 214 215
  LAST_SIGNAL
};

216 217
#define DEFAULT_BANDWIDTH            0
#define DEFAULT_RTCP_FRACTION        RTP_STATS_RTCP_FRACTION
218 219
#define DEFAULT_RTCP_RR_BANDWIDTH    -1
#define DEFAULT_RTCP_RS_BANDWIDTH    -1
Wim Taymans's avatar
Wim Taymans committed
220
#define DEFAULT_SDES                 NULL
221 222
#define DEFAULT_NUM_SOURCES          0
#define DEFAULT_NUM_ACTIVE_SOURCES   0
223
#define DEFAULT_USE_PIPELINE_CLOCK   FALSE
224
#define DEFAULT_RTCP_MIN_INTERVAL    (RTP_STATS_MIN_INTERVAL * GST_SECOND)
225
#define DEFAULT_PROBATION            RTP_DEFAULT_PROBATION
226 227
#define DEFAULT_MAX_DROPOUT_TIME     60000
#define DEFAULT_MAX_MISORDER_TIME    2000
228
#define DEFAULT_RTP_PROFILE          GST_RTP_PROFILE_AVP
229
#define DEFAULT_NTP_TIME_SOURCE      GST_RTP_NTP_TIME_SOURCE_NTP
230
#define DEFAULT_RTCP_SYNC_SEND_TIME  TRUE
231

232 233
enum
{
234
  PROP_0,
235 236
  PROP_BANDWIDTH,
  PROP_RTCP_FRACTION,
237 238
  PROP_RTCP_RR_BANDWIDTH,
  PROP_RTCP_RS_BANDWIDTH,
Wim Taymans's avatar
Wim Taymans committed
239
  PROP_SDES,
240 241
  PROP_NUM_SOURCES,
  PROP_NUM_ACTIVE_SOURCES,
242
  PROP_INTERNAL_SESSION,
243
  PROP_USE_PIPELINE_CLOCK,
244
  PROP_RTCP_MIN_INTERVAL,
245
  PROP_PROBATION,
246 247
  PROP_MAX_DROPOUT_TIME,
  PROP_MAX_MISORDER_TIME,
248
  PROP_STATS,
249
  PROP_RTP_PROFILE,
250 251
  PROP_NTP_TIME_SOURCE,
  PROP_RTCP_SYNC_SEND_TIME
252 253
};

254
#define GST_RTP_SESSION_GET_PRIVATE(obj)  \
255
	   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_SESSION, GstRtpSessionPrivate))
256

Wim Taymans's avatar
Wim Taymans committed
257 258
#define GST_RTP_SESSION_LOCK(sess)   g_mutex_lock (&(sess)->priv->lock)
#define GST_RTP_SESSION_UNLOCK(sess) g_mutex_unlock (&(sess)->priv->lock)
259

260 261 262
#define GST_RTP_SESSION_WAIT(sess)   g_cond_wait (&(sess)->priv->cond, &(sess)->priv->lock)
#define GST_RTP_SESSION_SIGNAL(sess) g_cond_signal (&(sess)->priv->cond)

263
struct _GstRtpSessionPrivate
264
{
Wim Taymans's avatar
Wim Taymans committed
265
  GMutex lock;
266
  GCond cond;
267
  GstClock *sysclock;
268

269
  RTPSession *session;
270

271 272 273 274
  /* thread for sending out RTCP */
  GstClockID id;
  gboolean stop_thread;
  GThread *thread;
275
  gboolean thread_stopped;
276
  gboolean wait_send;
277 278

  /* caps mapping */
279 280
  GHashTable *ptmap;

281 282
  GstClockTime send_latency;

283
  gboolean use_pipeline_clock;
284
  GstRtpNtpTimeSource ntp_time_source;
285
  gboolean rtcp_sync_send_time;
286 287

  guint rtx_count;
288 289 290 291 292 293
};

/* callbacks to handle actions from the session manager */
static GstFlowReturn gst_rtp_session_process_rtp (RTPSession * sess,
    RTPSource * src, GstBuffer * buffer, gpointer user_data);
static GstFlowReturn gst_rtp_session_send_rtp (RTPSession * sess,
294
    RTPSource * src, gpointer data, gpointer user_data);
295
static GstFlowReturn gst_rtp_session_send_rtcp (RTPSession * sess,
296
    RTPSource * src, GstBuffer * buffer, gboolean eos, gpointer user_data);
297
static GstFlowReturn gst_rtp_session_sync_rtcp (RTPSession * sess,
298
    GstBuffer * buffer, gpointer user_data);
299 300
static gint gst_rtp_session_clock_rate (RTPSession * sess, guint8 payload,
    gpointer user_data);
301
static void gst_rtp_session_reconsider (RTPSession * sess, gpointer user_data);
302
static void gst_rtp_session_request_key_unit (RTPSession * sess, guint32 ssrc,
303
    gboolean all_headers, gpointer user_data);
304 305
static GstClockTime gst_rtp_session_request_time (RTPSession * session,
    gpointer user_data);
306
static void gst_rtp_session_notify_nack (RTPSession * sess,
307
    guint16 seqnum, guint16 blp, guint32 ssrc, gpointer user_data);
308
static void gst_rtp_session_reconfigure (RTPSession * sess, gpointer user_data);
309 310 311 312

static RTPSessionCallbacks callbacks = {
  gst_rtp_session_process_rtp,
  gst_rtp_session_send_rtp,
313
  gst_rtp_session_sync_rtcp,
314
  gst_rtp_session_send_rtcp,
315
  gst_rtp_session_clock_rate,
316
  gst_rtp_session_reconsider,
317
  gst_rtp_session_request_key_unit,
318
  gst_rtp_session_request_time,
319 320
  gst_rtp_session_notify_nack,
  gst_rtp_session_reconfigure
321 322
};

323 324 325 326 327 328 329 330 331 332 333
/* GObject vmethods */
static void gst_rtp_session_finalize (GObject * object);
static void gst_rtp_session_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_rtp_session_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

/* GstElement vmethods */
static GstStateChangeReturn gst_rtp_session_change_state (GstElement * element,
    GstStateChange transition);
static GstPad *gst_rtp_session_request_new_pad (GstElement * element,
334
    GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
335 336
static void gst_rtp_session_release_pad (GstElement * element, GstPad * pad);

337 338 339 340
static gboolean gst_rtp_session_sink_setcaps (GstPad * pad,
    GstRtpSession * rtpsession, GstCaps * caps);
static gboolean gst_rtp_session_setcaps_send_rtp (GstPad * pad,
    GstRtpSession * rtpsession, GstCaps * caps);
341

342
static void gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession);
Wim Taymans's avatar
Wim Taymans committed
343

344 345
static GstStructure *gst_rtp_session_create_stats (GstRtpSession * rtpsession);

346
static guint gst_rtp_session_signals[LAST_SIGNAL] = { 0 };
347

348
static void
349
on_new_ssrc (RTPSession * session, RTPSource * src, GstRtpSession * sess)
350 351 352 353 354 355
{
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_NEW_SSRC], 0,
      src->ssrc);
}

static void
356
on_ssrc_collision (RTPSession * session, RTPSource * src, GstRtpSession * sess)
357
{
358
  GstPad *send_rtp_sink;
359

360 361
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_COLLISION], 0,
      src->ssrc);
362 363

  GST_RTP_SESSION_LOCK (sess);
364 365
  if ((send_rtp_sink = sess->send_rtp_sink))
    gst_object_ref (send_rtp_sink);
366 367
  GST_RTP_SESSION_UNLOCK (sess);

368
  if (send_rtp_sink) {
369 370 371 372 373 374 375 376 377 378
    GstStructure *structure;
    GstEvent *event;
    RTPSource *internal_src;
    guint32 suggested_ssrc;

    structure = gst_structure_new ("GstRTPCollision", "ssrc", G_TYPE_UINT,
        (guint) src->ssrc, NULL);

    /* if there is no source using the suggested ssrc, most probably because
     * this ssrc has just collided, suggest upstream to use it */
379
    suggested_ssrc = rtp_session_suggest_ssrc (session, NULL);
380 381 382 383 384 385 386 387
    internal_src = rtp_session_get_source_by_ssrc (session, suggested_ssrc);
    if (!internal_src)
      gst_structure_set (structure, "suggested-ssrc", G_TYPE_UINT,
          (guint) suggested_ssrc, NULL);
    else
      g_object_unref (internal_src);

    event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, structure);
388 389 390
    gst_pad_push_event (send_rtp_sink, event);
    gst_object_unref (send_rtp_sink);
  }
391 392 393
}

static void
394
on_ssrc_validated (RTPSession * session, RTPSource * src, GstRtpSession * sess)
395 396 397 398 399
{
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED], 0,
      src->ssrc);
}

400 401 402 403 404 405 406
static void
on_ssrc_active (RTPSession * session, RTPSource * src, GstRtpSession * sess)
{
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE], 0,
      src->ssrc);
}

407 408 409
static void
on_ssrc_sdes (RTPSession * session, RTPSource * src, GstRtpSession * sess)
{
410 411 412 413 414
  GstStructure *s;
  GstMessage *m;

  /* convert the new SDES info into a message */
  RTP_SESSION_LOCK (session);
415
  g_object_get (src, "sdes", &s, NULL);
416
  RTP_SESSION_UNLOCK (session);
417

418 419 420
  m = gst_message_new_custom (GST_MESSAGE_ELEMENT, GST_OBJECT (sess), s);
  gst_element_post_message (GST_ELEMENT_CAST (sess), m);

421 422 423 424
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_SDES], 0,
      src->ssrc);
}

425
static void
426
on_bye_ssrc (RTPSession * session, RTPSource * src, GstRtpSession * sess)
427 428 429 430 431 432
{
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_BYE_SSRC], 0,
      src->ssrc);
}

static void
433
on_bye_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess)
434 435 436 437 438 439
{
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT], 0,
      src->ssrc);
}

static void
440
on_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess)
441 442 443 444 445
{
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_TIMEOUT], 0,
      src->ssrc);
}

446 447 448 449 450 451 452
static void
on_sender_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess)
{
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
      src->ssrc);
}

453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
static void
on_new_sender_ssrc (RTPSession * session, RTPSource * src, GstRtpSession * sess)
{
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_NEW_SENDER_SSRC], 0,
      src->ssrc);
}

static void
on_sender_ssrc_active (RTPSession * session, RTPSource * src,
    GstRtpSession * sess)
{
  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SENDER_SSRC_ACTIVE], 0,
      src->ssrc);
}

468
static void
469 470
on_notify_stats (RTPSession * session, GParamSpec * spec,
    GstRtpSession * rtpsession)
471 472 473 474
{
  g_object_notify (G_OBJECT (rtpsession), "stats");
}

475 476
#define gst_rtp_session_parent_class parent_class
G_DEFINE_TYPE (GstRtpSession, gst_rtp_session, GST_TYPE_ELEMENT);
477 478

static void
479
gst_rtp_session_class_init (GstRtpSessionClass * klass)
480 481 482 483 484 485 486
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

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

487
  g_type_class_add_private (klass, sizeof (GstRtpSessionPrivate));
488

489 490 491 492
  gobject_class->finalize = gst_rtp_session_finalize;
  gobject_class->set_property = gst_rtp_session_set_property;
  gobject_class->get_property = gst_rtp_session_get_property;

493
  /**
494
   * GstRtpSession::request-pt-map:
495 496 497 498 499 500 501
   * @sess: the object which received the signal
   * @pt: the pt
   *
   * Request the payload type as #GstCaps for @pt.
   */
  gst_rtp_session_signals[SIGNAL_REQUEST_PT_MAP] =
      g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
502
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, request_pt_map),
503
      NULL, NULL, g_cclosure_marshal_generic, GST_TYPE_CAPS, 1, G_TYPE_UINT);
Wim Taymans's avatar
Wim Taymans committed
504
  /**
505
   * GstRtpSession::clear-pt-map:
Wim Taymans's avatar
Wim Taymans committed
506 507
   * @sess: the object which received the signal
   *
508
   * Clear the cached pt-maps requested with #GstRtpSession::request-pt-map.
Wim Taymans's avatar
Wim Taymans committed
509 510 511
   */
  gst_rtp_session_signals[SIGNAL_CLEAR_PT_MAP] =
      g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
512
      G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpSessionClass, clear_pt_map),
Wim Taymans's avatar
Wim Taymans committed
513
      NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
514

515
  /**
516
   * GstRtpSession::on-new-ssrc:
517
   * @sess: the object which received the signal
518
   * @ssrc: the SSRC
519 520 521 522 523
   *
   * Notify of a new SSRC that entered @session.
   */
  gst_rtp_session_signals[SIGNAL_ON_NEW_SSRC] =
      g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
524
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_new_ssrc),
525 526
      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
  /**
527
   * GstRtpSession::on-ssrc_collision:
528
   * @sess: the object which received the signal
529
   * @ssrc: the SSRC
530 531 532 533 534
   *
   * Notify when we have an SSRC collision
   */
  gst_rtp_session_signals[SIGNAL_ON_SSRC_COLLISION] =
      g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
535
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
536 537 538
          on_ssrc_collision), NULL, NULL, g_cclosure_marshal_VOID__UINT,
      G_TYPE_NONE, 1, G_TYPE_UINT);
  /**
539
   * GstRtpSession::on-ssrc_validated:
540
   * @sess: the object which received the signal
541
   * @ssrc: the SSRC
542 543 544 545 546
   *
   * Notify of a new SSRC that became validated.
   */
  gst_rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED] =
      g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
547
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
548 549
          on_ssrc_validated), NULL, NULL, g_cclosure_marshal_VOID__UINT,
      G_TYPE_NONE, 1, G_TYPE_UINT);
550
  /**
551
   * GstRtpSession::on-ssrc-active:
552 553 554 555 556 557 558 559 560 561
   * @sess: the object which received the signal
   * @ssrc: the SSRC
   *
   * Notify of a SSRC that is active, i.e., sending RTCP.
   */
  gst_rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE] =
      g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
          on_ssrc_active), NULL, NULL, g_cclosure_marshal_VOID__UINT,
      G_TYPE_NONE, 1, G_TYPE_UINT);
562 563 564 565 566 567 568 569 570 571 572
  /**
   * GstRtpSession::on-ssrc-sdes:
   * @session: the object which received the signal
   * @src: the SSRC
   *
   * Notify that a new SDES was received for SSRC.
   */
  gst_rtp_session_signals[SIGNAL_ON_SSRC_SDES] =
      g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_ssrc_sdes),
      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
573 574

  /**
575
   * GstRtpSession::on-bye-ssrc:
576
   * @sess: the object which received the signal
577
   * @ssrc: the SSRC
578 579 580 581 582
   *
   * Notify of an SSRC that became inactive because of a BYE packet.
   */
  gst_rtp_session_signals[SIGNAL_ON_BYE_SSRC] =
      g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
583
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_bye_ssrc),
584 585
      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
  /**
586
   * GstRtpSession::on-bye-timeout:
587
   * @sess: the object which received the signal
588
   * @ssrc: the SSRC
589 590 591 592 593
   *
   * Notify of an SSRC that has timed out because of BYE
   */
  gst_rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT] =
      g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
594
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_bye_timeout),
595 596
      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
  /**
597
   * GstRtpSession::on-timeout:
598
   * @sess: the object which received the signal
599
   * @ssrc: the SSRC
600 601 602 603 604
   *
   * Notify of an SSRC that has timed out
   */
  gst_rtp_session_signals[SIGNAL_ON_TIMEOUT] =
      g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
605
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_timeout),
606
      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
607 608 609
  /**
   * GstRtpSession::on-sender-timeout:
   * @sess: the object which received the signal
610
   * @ssrc: the SSRC
611 612 613 614 615 616 617 618
   *
   * Notify of a sender SSRC that has timed out and became a receiver
   */
  gst_rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT] =
      g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
          on_sender_timeout), NULL, NULL, g_cclosure_marshal_VOID__UINT,
      G_TYPE_NONE, 1, G_TYPE_UINT);
619

620 621 622 623 624 625
  /**
   * GstRtpSession::on-new-sender-ssrc:
   * @sess: the object which received the signal
   * @ssrc: the sender SSRC
   *
   * Notify of a new sender SSRC that entered @session.
626 627
   *
   * Since: 1.8
628 629 630 631 632 633 634 635 636 637 638 639
   */
  gst_rtp_session_signals[SIGNAL_ON_NEW_SENDER_SSRC] =
      g_signal_new ("on-new-sender-ssrc", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_new_ssrc),
      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);

  /**
   * GstRtpSession::on-sender-ssrc-active:
   * @sess: the object which received the signal
   * @ssrc: the sender SSRC
   *
   * Notify of a sender SSRC that is active, i.e., sending RTCP.
640 641
   *
   * Since: 1.8
642 643 644 645 646 647 648
   */
  gst_rtp_session_signals[SIGNAL_ON_SENDER_SSRC_ACTIVE] =
      g_signal_new ("on-sender-ssrc-active", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
          on_ssrc_active), NULL, NULL, g_cclosure_marshal_VOID__UINT,
      G_TYPE_NONE, 1, G_TYPE_UINT);

649 650
  g_object_class_install_property (gobject_class, PROP_BANDWIDTH,
      g_param_spec_double ("bandwidth", "Bandwidth",
651
          "The bandwidth of the session in bytes per second (0 for auto-discover)",
652 653
          0.0, G_MAXDOUBLE, DEFAULT_BANDWIDTH,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
654 655 656

  g_object_class_install_property (gobject_class, PROP_RTCP_FRACTION,
      g_param_spec_double ("rtcp-fraction", "RTCP Fraction",
Wim Taymans's avatar
Wim Taymans committed
657 658
          "The RTCP bandwidth of the session in bytes per second "
          "(or as a real fraction of the RTP bandwidth if < 1.0)",
659 660
          0.0, G_MAXDOUBLE, DEFAULT_RTCP_FRACTION,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
661

662 663 664
  g_object_class_install_property (gobject_class, PROP_RTCP_RR_BANDWIDTH,
      g_param_spec_int ("rtcp-rr-bandwidth", "RTCP RR bandwidth",
          "The RTCP bandwidth used for receivers in bytes per second (-1 = default)",
665 666
          -1, G_MAXINT, DEFAULT_RTCP_RR_BANDWIDTH,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
667 668 669 670

  g_object_class_install_property (gobject_class, PROP_RTCP_RS_BANDWIDTH,
      g_param_spec_int ("rtcp-rs-bandwidth", "RTCP RS bandwidth",
          "The RTCP bandwidth used for senders in bytes per second (-1 = default)",
671 672
          -1, G_MAXINT, DEFAULT_RTCP_RS_BANDWIDTH,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
673

Wim Taymans's avatar
Wim Taymans committed
674 675 676
  g_object_class_install_property (gobject_class, PROP_SDES,
      g_param_spec_boxed ("sdes", "SDES",
          "The SDES items of this session",
677
          GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
678 679 680 681

  g_object_class_install_property (gobject_class, PROP_NUM_SOURCES,
      g_param_spec_uint ("num-sources", "Num Sources",
          "The number of sources in the session", 0, G_MAXUINT,
682
          DEFAULT_NUM_SOURCES, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
683 684 685 686

  g_object_class_install_property (gobject_class, PROP_NUM_ACTIVE_SOURCES,
      g_param_spec_uint ("num-active-sources", "Num Active Sources",
          "The number of active sources in the session", 0, G_MAXUINT,
687 688
          DEFAULT_NUM_ACTIVE_SOURCES,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
689

690 691 692
  g_object_class_install_property (gobject_class, PROP_INTERNAL_SESSION,
      g_param_spec_object ("internal-session", "Internal Session",
          "The internal RTPSession object", RTP_TYPE_SESSION,
693
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
694

695 696
  g_object_class_install_property (gobject_class, PROP_USE_PIPELINE_CLOCK,
      g_param_spec_boolean ("use-pipeline-clock", "Use pipeline clock",
697
          "Use the pipeline running-time to set the NTP time in the RTCP SR messages "
698
          "(DEPRECATED: Use ntp-time-source property)",
699
          DEFAULT_USE_PIPELINE_CLOCK,
700
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED));
701

702 703 704 705 706 707
  g_object_class_install_property (gobject_class, PROP_RTCP_MIN_INTERVAL,
      g_param_spec_uint64 ("rtcp-min-interval", "Minimum RTCP interval",
          "Minimum interval between Regular RTCP packet (in ns)",
          0, G_MAXUINT64, DEFAULT_RTCP_MIN_INTERVAL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

708 709 710 711 712 713
  g_object_class_install_property (gobject_class, PROP_PROBATION,
      g_param_spec_uint ("probation", "Number of probations",
          "Consecutive packet sequence numbers to accept the source",
          0, G_MAXUINT, DEFAULT_PROBATION,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

714 715 716 717 718 719 720 721 722 723 724 725
  g_object_class_install_property (gobject_class, PROP_MAX_DROPOUT_TIME,
      g_param_spec_uint ("max-dropout-time", "Max dropout time",
          "The maximum time (milliseconds) of missing packets tolerated.",
          0, G_MAXUINT, DEFAULT_MAX_DROPOUT_TIME,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_MAX_MISORDER_TIME,
      g_param_spec_uint ("max-misorder-time", "Max misorder time",
          "The maximum time (milliseconds) of misordered packets tolerated.",
          0, G_MAXUINT, DEFAULT_MAX_MISORDER_TIME,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

726 727 728 729 730 731 732 733 734 735 736 737
  /**
   * GstRtpSession::stats:
   *
   * Various session statistics. This property returns a GstStructure
   * with name application/x-rtp-session-stats with the following fields:
   *
   *  "rtx-count"       G_TYPE_UINT   The number of retransmission events
   *      received from downstream (in receiver mode)
   *  "rtx-drop-count"  G_TYPE_UINT   The number of retransmission events
   *      dropped (due to bandwidth constraints)
   *  "sent-nack-count" G_TYPE_UINT   Number of NACKs sent
   *  "recv-nack-count" G_TYPE_UINT   Number of NACKs received
738 739
   *  "source-stats"    G_TYPE_BOXED  GValueArray of #RTPSource::stats for all
   *      RTP sources (Since 1.8)
740
   *
741
   * Since: 1.4
742 743 744 745 746 747
   */
  g_object_class_install_property (gobject_class, PROP_STATS,
      g_param_spec_boxed ("stats", "Statistics",
          "Various statistics", GST_TYPE_STRUCTURE,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

748 749 750 751 752
  g_object_class_install_property (gobject_class, PROP_RTP_PROFILE,
      g_param_spec_enum ("rtp-profile", "RTP Profile",
          "RTP profile to use", GST_TYPE_RTP_PROFILE, DEFAULT_RTP_PROFILE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

753 754 755 756 757 758
  g_object_class_install_property (gobject_class, PROP_NTP_TIME_SOURCE,
      g_param_spec_enum ("ntp-time-source", "NTP Time Source",
          "NTP time source for RTCP packets",
          gst_rtp_ntp_time_source_get_type (), DEFAULT_NTP_TIME_SOURCE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

759 760 761 762 763 764 765
  g_object_class_install_property (gobject_class, PROP_RTCP_SYNC_SEND_TIME,
      g_param_spec_boolean ("rtcp-sync-send-time", "RTCP Sync Send Time",
          "Use send time or capture time for RTCP sync "
          "(TRUE = send time, FALSE = capture time)",
          DEFAULT_RTCP_SYNC_SEND_TIME,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

766 767 768 769 770 771
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_rtp_session_change_state);
  gstelement_class->request_new_pad =
      GST_DEBUG_FUNCPTR (gst_rtp_session_request_new_pad);
  gstelement_class->release_pad =
      GST_DEBUG_FUNCPTR (gst_rtp_session_release_pad);
772

Wim Taymans's avatar
Wim Taymans committed
773 774
  klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_session_clear_pt_map);

775
  /* sink pads */
776 777 778 779 780 781
  gst_element_class_add_static_pad_template (gstelement_class,
      &rtpsession_recv_rtp_sink_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &rtpsession_recv_rtcp_sink_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &rtpsession_send_rtp_sink_template);
782 783

  /* src pads */
784 785 786 787 788 789 790 791
  gst_element_class_add_static_pad_template (gstelement_class,
      &rtpsession_recv_rtp_src_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &rtpsession_sync_src_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &rtpsession_send_rtp_src_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &rtpsession_send_rtcp_src_template);
792

793
  gst_element_class_set_static_metadata (gstelement_class, "RTP Session",
794 795 796
      "Filter/Network/RTP",
      "Implement an RTP session", "Wim Taymans <wim.taymans@gmail.com>");

797 798
  GST_DEBUG_CATEGORY_INIT (gst_rtp_session_debug,
      "rtpsession", 0, "RTP Session");
799 800 801
}

static void
802
gst_rtp_session_init (GstRtpSession * rtpsession)
803
{
804
  rtpsession->priv = GST_RTP_SESSION_GET_PRIVATE (rtpsession);
Wim Taymans's avatar
Wim Taymans committed
805
  g_mutex_init (&rtpsession->priv->lock);
806
  g_cond_init (&rtpsession->priv->cond);
807
  rtpsession->priv->sysclock = gst_system_clock_obtain ();
808
  rtpsession->priv->session = rtp_session_new ();
809
  rtpsession->priv->use_pipeline_clock = DEFAULT_USE_PIPELINE_CLOCK;
810
  rtpsession->priv->rtcp_sync_send_time = DEFAULT_RTCP_SYNC_SEND_TIME;
811

812 813
  /* configure callbacks */
  rtp_session_set_callbacks (rtpsession->priv->session, &callbacks, rtpsession);
814 815 816 817 818 819 820
  /* configure signals */
  g_signal_connect (rtpsession->priv->session, "on-new-ssrc",
      (GCallback) on_new_ssrc, rtpsession);
  g_signal_connect (rtpsession->priv->session, "on-ssrc-collision",
      (GCallback) on_ssrc_collision, rtpsession);
  g_signal_connect (rtpsession->priv->session, "on-ssrc-validated",
      (GCallback) on_ssrc_validated, rtpsession);
821 822
  g_signal_connect (rtpsession->priv->session, "on-ssrc-active",
      (GCallback) on_ssrc_active, rtpsession);
823 824
  g_signal_connect (rtpsession->priv->session, "on-ssrc-sdes",
      (GCallback) on_ssrc_sdes, rtpsession);
825 826 827 828 829 830
  g_signal_connect (rtpsession->priv->session, "on-bye-ssrc",
      (GCallback) on_bye_ssrc, rtpsession);
  g_signal_connect (rtpsession->priv->session, "on-bye-timeout",
      (GCallback) on_bye_timeout, rtpsession);
  g_signal_connect (rtpsession->priv->session, "on-timeout",
      (GCallback) on_timeout, rtpsession);
831 832
  g_signal_connect (rtpsession->priv->session, "on-sender-timeout",
      (GCallback) on_sender_timeout, rtpsession);
833 834 835 836
  g_signal_connect (rtpsession->priv->session, "on-new-sender-ssrc",
      (GCallback) on_new_sender_ssrc, rtpsession);
  g_signal_connect (rtpsession->priv->session, "on-sender-ssrc-active",
      (GCallback) on_sender_ssrc_active, rtpsession);
837 838
  g_signal_connect (rtpsession->priv->session, "notify::stats",
      (GCallback) on_notify_stats, rtpsession);
839 840
  rtpsession->priv->ptmap = g_hash_table_new_full (NULL, NULL, NULL,
      (GDestroyNotify) gst_caps_unref);
841 842 843

  gst_segment_init (&rtpsession->recv_rtp_seg, GST_FORMAT_UNDEFINED);
  gst_segment_init (&rtpsession->send_rtp_seg, GST_FORMAT_UNDEFINED);
844 845

  rtpsession->priv->thread_stopped = TRUE;
846 847

  rtpsession->priv->rtx_count = 0;
848 849

  rtpsession->priv->ntp_time_source = DEFAULT_NTP_TIME_SOURCE;
850 851 852 853 854
}

static void
gst_rtp_session_finalize (GObject * object)
{
855
  GstRtpSession *rtpsession;
856 857

  rtpsession = GST_RTP_SESSION (object);
858 859

  g_hash_table_destroy (rtpsession->priv->ptmap);
Wim Taymans's avatar
Wim Taymans committed
860
  g_mutex_clear (&rtpsession->priv->lock);
861
  g_cond_clear (&rtpsession->priv->cond);
862
  g_object_unref (rtpsession->priv->sysclock);
863
  g_object_unref (rtpsession->priv->session);
864 865 866 867 868 869 870 871

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gst_rtp_session_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
872
  GstRtpSession *rtpsession;
873
  GstRtpSessionPrivate *priv;
874 875

  rtpsession = GST_RTP_SESSION (object);
876
  priv = rtpsession->priv;
877 878

  switch (prop_id) {
879
    case PROP_BANDWIDTH:
880
      g_object_set_property (G_OBJECT (priv->session), "bandwidth", value);
881 882
      break;
    case PROP_RTCP_FRACTION:
883 884 885 886 887 888 889 890 891
      g_object_set_property (G_OBJECT (priv->session), "rtcp-fraction", value);
      break;
    case PROP_RTCP_RR_BANDWIDTH:
      g_object_set_property (G_OBJECT (priv->session), "rtcp-rr-bandwidth",
          value);
      break;
    case PROP_RTCP_RS_BANDWIDTH:
      g_object_set_property (G_OBJECT (priv->session), "rtcp-rs-bandwidth",
          value);
892
      break;
Wim Taymans's avatar
Wim Taymans committed
893 894
    case PROP_SDES:
      rtp_session_set_sdes_struct (priv->session, g_value_get_boxed (value));
895
      break;
896 897 898
    case PROP_USE_PIPELINE_CLOCK:
      priv->use_pipeline_clock = g_value_get_boolean (value);
      break;
899 900 901 902
    case PROP_RTCP_MIN_INTERVAL:
      g_object_set_property (G_OBJECT (priv->session), "rtcp-min-interval",
          value);
      break;
903 904 905
    case PROP_PROBATION:
      g_object_set_property (G_OBJECT (priv->session), "probation", value);
      break;
906 907 908 909 910 911 912 913
    case PROP_MAX_DROPOUT_TIME:
      g_object_set_property (G_OBJECT (priv->session), "max-dropout-time",
          value);
      break;
    case PROP_MAX_MISORDER_TIME:
      g_object_set_property (G_OBJECT (priv->session), "max-misorder-time",
          value);
      break;
914 915 916
    case PROP_RTP_PROFILE:
      g_object_set_property (G_OBJECT (priv->session), "rtp-profile", value);
      break;
917 918 919
    case PROP_NTP_TIME_SOURCE:
      priv->ntp_time_source = g_value_get_enum (value);
      break;
920 921 922
    case PROP_RTCP_SYNC_SEND_TIME:
      priv->rtcp_sync_send_time = g_value_get_boolean (value);
      break;
923 924 925 926 927 928 929 930 931 932
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_rtp_session_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
933
  GstRtpSession *rtpsession;
934
  GstRtpSessionPrivate *priv;
935 936

  rtpsession = GST_RTP_SESSION (object);
937
  priv = rtpsession->priv;
938 939

  switch (prop_id) {
940
    case PROP_BANDWIDTH:
941
      g_object_get_property (G_OBJECT (priv->session), "bandwidth", value);
942 943
      break;
    case PROP_RTCP_FRACTION:
944 945 946 947 948 949 950 951 952
      g_object_get_property (G_OBJECT (priv->session), "rtcp-fraction", value);
      break;
    case PROP_RTCP_RR_BANDWIDTH:
      g_object_get_property (G_OBJECT (priv->session), "rtcp-rr-bandwidth",
          value);
      break;
    case PROP_RTCP_RS_BANDWIDTH:
      g_object_get_property (G_OBJECT (priv->session), "rtcp-rs-bandwidth",
          value);
953
      break;
Wim Taymans's avatar
Wim Taymans committed
954 955
    case PROP_SDES:
      g_value_take_boxed (value, rtp_session_get_sdes_struct (priv->session));
956 957 958 959 960 961 962 963
      break;
    case PROP_NUM_SOURCES:
      g_value_set_uint (value, rtp_session_get_num_sources (priv->session));
      break;
    case PROP_NUM_ACTIVE_SOURCES:
      g_value_set_uint (value,
          rtp_session_get_num_active_sources (priv->session));
      break;
964 965 966
    case PROP_INTERNAL_SESSION:
      g_value_set_object (value, priv->session);
      break;
967 968 969
    case PROP_USE_PIPELINE_CLOCK:
      g_value_set_boolean (value, priv->use_pipeline_clock);
      break;
970 971 972 973
    case PROP_RTCP_MIN_INTERVAL:
      g_object_get_property (G_OBJECT (priv->session), "rtcp-min-interval",
          value);
      break;
974 975 976
    case PROP_PROBATION:
      g_object_get_property (G_OBJECT (priv->session), "probation", value);
      break;
977 978 979 980 981 982 983 984
    case PROP_MAX_DROPOUT_TIME:
      g_object_get_property (G_OBJECT (priv->session), "max-dropout-time",
          value);
      break;
    case PROP_MAX_MISORDER_TIME:
      g_object_get_property (G_OBJECT (priv->session), "max-misorder-time",
          value);
      break;
985 986 987
    case PROP_STATS:
      g_value_take_boxed (value, gst_rtp_session_create_stats (rtpsession));
      break;
988 989 990
    case PROP_RTP_PROFILE:
      g_object_get_property (G_OBJECT (priv->session), "rtp-profile", value);
      break;
991 992 993
    case PROP_NTP_TIME_SOURCE:
      g_value_set_enum (value, priv->ntp_time_source);
      break;
994 995 996
    case PROP_RTCP_SYNC_SEND_TIME:
      g_value_set_boolean (value, priv->rtcp_sync_send_time);
      break;
997 998 999 1000 1001 1002
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
static GstStructure *
gst_rtp_session_create_stats (GstRtpSession * rtpsession)
{
  GstStructure *s;

  g_object_get (rtpsession->priv->session, "stats", &s, NULL);
  gst_structure_set (s, "rtx-count", G_TYPE_UINT, rtpsession->priv->rtx_count,
      NULL);

  return s;
}

1015
static void
1016 1017
get_current_times (GstRtpSession * rtpsession, GstClockTime * running_time,
    guint64 * ntpnstime)
1018
{
1019
  guint64 ntpns = -1;
1020
  GstClock *clock;
Wim Taymans's avatar
Wim Taymans committed
1021
  GstClockTime base_time, rt, clock_time;
1022 1023 1024 1025 1026 1027

  GST_OBJECT_LOCK (rtpsession);
  if ((clock = GST_ELEMENT_CLOCK (rtpsession))) {
    base_time = GST_ELEMENT_CAST (rtpsession)->base_time;
    gst_object_ref (clock);
    GST_OBJECT_UNLOCK (rtpsession);
1028

Wim Taymans's avatar
Wim Taymans committed
1029
    /* get current clock time and convert to running time */
Wim Taymans's avatar
Wim Taymans committed
1030
    clock_time = gst_clock_get_time (clock);
Wim Taymans's avatar
Wim Taymans committed
1031
    rt = clock_time - base_time;
Wim Taymans's avatar
Wim Taymans committed
1032

1033
    if (rtpsession->priv->use_pipeline_clock) {
Wim Taymans's avatar
Wim Taymans committed
1034
      ntpns = rt;
1035 1036
      /* add constant to convert from 1970 based time to 1900 based time */
      ntpns += (2208988800LL * GST_SECOND);
1037
    } else {
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
      switch (rtpsession->priv->ntp_time_source) {
        case GST_RTP_NTP_TIME_SOURCE_NTP:
        case GST_RTP_NTP_TIME_SOURCE_UNIX:{
          GTimeVal current;

          /* get current NTP time */
          g_get_current_time (&current);
          ntpns = GST_TIMEVAL_TO_TIME (current);

          /* add constant to convert from 1970 based time to 1900 based time */
          if (rtpsession->priv->ntp_time_source == GST_RTP_NTP_TIME_SOURCE_NTP)
            ntpns += (2208988800LL * GST_SECOND);
          break;
        }
        case GST_RTP_NTP_TIME_SOURCE_RUNNING_TIME:
          ntpns = rt;
          break;
        case GST_RTP_NTP_TIME_SOURCE_CLOCK_TIME:
          ntpns = clock_time;
          break;
        default:
1059
          ntpns = -1;
1060 1061 1062
          g_assert_not_reached ();
          break;
      }
1063
    }
1064

1065 1066 1067
    gst_object_unref (clock);
  } else {
    GST_OBJECT_UNLOCK (rtpsession);
1068 1069
    rt = -1;
    ntpns = -1;
1070
  }
1071 1072 1073 1074
  if (running_time)
    *running_time = rt;
  if (ntpnstime)
    *ntpnstime = ntpns;
1075 1076
}

1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
/* must be called with GST_RTP_SESSION_LOCK */
static void
signal_waiting_rtcp_thread_unlocked (GstRtpSession * rtpsession)
{
  if (rtpsession->priv->wait_send) {
    GST_LOG_OBJECT (rtpsession, "signal RTCP thread");
    rtpsession->priv->wait_send = FALSE;
    GST_RTP_SESSION_SIGNAL (rtpsession);
  }
}

1088
static void
1089
rtcp_thread (GstRtpSession * rtpsession)
1090 1091
{
  GstClockID id;
1092
  GstClockTime current_time;
1093
  GstClockTime next_timeout;
1094
  guint64 ntpnstime;
1095
  GstClockTime running_time;
Wim Taymans's avatar
Wim Taymans committed
1096 1097
  RTPSession *session;
  GstClock *sysclock;
1098 1099 1100 1101

  GST_DEBUG_OBJECT (rtpsession, "entering RTCP thread");

  GST_RTP_SESSION_LOCK (rtpsession);
1102

1103
  while (rtpsession->priv->wait_send) {
1104
    GST_LOG_OBJECT (rtpsession, "waiting for getting started");
1105 1106 1107 1108
    GST_RTP_SESSION_WAIT (rtpsession);
    GST_LOG_OBJECT (rtpsession, "signaled...");
  }

Wim Taymans's avatar
Wim Taymans committed
1109 1110 1111 1112
  sysclock = rtpsession->priv->sysclock;
  current_time = gst_clock_get_time (sysclock);

  session = rtpsession->priv->session;
1113

1114 1115 1116 1117
  GST_DEBUG_OBJECT (rtpsession, "starting at %" GST_TIME_FORMAT,
      GST_TIME_ARGS (current_time));
  session->start_time = current_time;

1118
  while (!rtpsession->priv->stop_thread) {
1119
    GstClockReturn res;
1120

1121
    /* get initial estimate */
Wim Taymans's avatar
Wim Taymans committed
1122
    next_timeout = rtp_session_next_timeout (session, current_time);
1123

1124
    GST_DEBUG_OBJECT (rtpsession, "next check time %" GST_TIME_FORMAT,
1125 1126 1127 1128 1129
        GST_TIME_ARGS (next_timeout));

    /* leave if no more timeouts, the session ended */
    if (next_timeout == GST_CLOCK_TIME_NONE)
      break;
1130

1131
    id = rtpsession->priv->id =
Wim Taymans's avatar
Wim Taymans committed
1132
        gst_clock_new_single_shot_id (sysclock, next_timeout);
1133
    GST_RTP_SESSION_UNLOCK (rtpsession);
1134

1135
    res = gst_clock_id_wait (id, NULL);
1136

1137 1138 1139 1140 1141 1142 1143
    GST_RTP_SESSION_LOCK (rtpsession);
    gst_clock_id_unref (id);
    rtpsession->priv->id = NULL;

    if (rtpsession->priv->stop_thread)
      break;

1144
    /* update current time */
Wim Taymans's avatar
Wim Taymans committed
1145
    current_time = gst_clock_get_time (sysclock);
1146 1147

    /* get current NTP time */
1148
    get_current_times (rtpsession, &running_time, &ntpnstime);
1149 1150 1151 1152 1153 1154

    /* we get unlocked because we need to perform reconsideration, don't perform
     * the timeout but get a new reporting estimate. */
    GST_DEBUG_OBJECT (rtpsession, "unlocked %d, current %" GST_TIME_FORMAT,
        res, GST_TIME_ARGS (current_time));

1155 1156
    /* perform actions, we ignore result. Release lock because it might push. */
    GST_RTP_SESSION_UNLOCK (rtpsession);
Wim Taymans's avatar
Wim Taymans committed
1157
    rtp_session_on_timeout (session, current_time, ntpnstime, running_time);
1158
    GST_RTP_SESSION_LOCK (rtpsession);
1159
  }
1160 1161
  /* mark the thread as stopped now */
  rtpsession->priv->thread_stopped = TRUE;
1162 1163 1164 1165 1166 1167
  GST_RTP_SESSION_UNLOCK (rtpsession);

  GST_DEBUG_OBJECT (rtpsession, "leaving RTCP thread");
}

static gboolean
1168
start_rtcp_thread (GstRtpSession * rtpsession)
1169 1170 1171 1172 1173 1174 1175 1176
{
  GError *error = NULL;
  gboolean res;

  GST_DEBUG_OBJECT (rtpsession, "starting RTCP thread");

  GST_RTP_SESSION_LOCK (rtpsession);
  rtpsession->priv->stop_thread = FALSE;
1177
  if (rtpsession->priv->thread_stopped) {
1178 1179 1180 1181 1182
    /* if the thread stopped, and we still have a handle to the thread, join it
     * now. We can safely join with the lock held, the thread will not take it
     * anymore. */
    if (rtpsession->priv->thread)
      g_thread_join (rtpsession->priv->thread);
1183 1184
    /* only create a new thread if the old one was stopped. Otherwise we can
     * just reuse the currently running one. */
1185 1186
    rtpsession->priv->thread = g_thread_try_new ("rtpsession-rtcp-thread",
        (GThreadFunc) rtcp_thread, rtpsession, &error);
1187 1188
    rtpsession->priv->thread_stopped = FALSE;
  }
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
  GST_RTP_SESSION_UNLOCK (rtpsession);

  if (error != NULL) {
    res = FALSE;
    GST_DEBUG_OBJECT (rtpsession, "failed to start thread, %s", error->message);
    g_error_free (error);
  } else {
    res = TRUE;
  }
  return res;
}

static void
1202
stop_rtcp_thread (GstRtpSession * rtpsession)
1203 1204 1205 1206 1207
{
  GST_DEBUG_OBJECT (rtpsession, "stopping RTCP thread");

  GST_RTP_SESSION_LOCK (rtpsession);
  rtpsession->priv->stop_thread = TRUE;
1208
  signal_waiting_rtcp_thread_unlocked (rtpsession);
1209 1210 1211
  if (rtpsession->priv->id)
    gst_clock_id_unschedule (rtpsession->priv->id);
  GST_RTP_SESSION_UNLOCK (rtpsession);
1212
}
1213

1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
static void
join_rtcp_thread (GstRtpSession * rtpsession)
{
  GST_RTP_SESSION_LOCK (rtpsession);
  /* don't try to join when we have no thread */
  if (rtpsession->priv->thread != NULL) {
    GST_DEBUG_OBJECT (rtpsession, "joining RTCP thread");
    GST_RTP_SESSION_UNLOCK (rtpsession);

    g_thread_join (rtpsession->priv->thread);

    GST_RTP_SESSION_LOCK (rtpsession);
    /* after the join, take the lock and clear the thread structure. The caller
     * is supposed to not concurrently call start and join. */
    rtpsession->priv->thread = NULL;
  }
  GST_RTP_SESSION_UNLOCK (rtpsession);
1231 1232
}

1233 1234 1235 1236
static GstStateChangeReturn
gst_rtp_session_change_state (GstElement * element, GstStateChange transition)
{
  GstStateChangeReturn res;
1237
  GstRtpSession *rtpsession;
1238 1239 1240 1241 1242 1243 1244

  rtpsession = GST_RTP_SESSION (element);

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
1245
      GST_RTP_SESSION_LOCK (rtpsession);
1246 1247
      if (rtpsession->send_rtp_src)
        rtpsession->priv->wait_send = TRUE;
1248
      GST_RTP_SESSION_UNLOCK (rtpsession);
1249 1250 1251
      break;
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
      break;
1252
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1253 1254 1255 1256
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      /* no need to join yet, we might want to continue later. Also, the
       * dataflow could block downstream so that a join could just block
       * forever. */
1257
      stop_rtcp_thread (rtpsession);
1258
      break;
1259 1260 1261 1262
    default:
      break;
  }

1263
  res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1264 1265

  switch (transition) {
1266 1267 1268 1269
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
      if (!start_rtcp_thread (rtpsession))
        goto failed_thread;
      break;
1270 1271 1272
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
      break;
    case GST_STATE_CHANGE_PAUSED_TO_READY:
1273 1274
      /* downstream is now releasing the dataflow and we can join. */
      join_rtcp_thread (rtpsession);
1275 1276 1277 1278 1279 1280 1281
      break;
    case GST_STATE_CHANGE_READY_TO_NULL:
      break;
    default:
      break;
  }
  return res;
1282 1283 1284 1285 1286 1287 1288 1289

  /* ERRORS */
failed_thread:
  {
    return GST_STATE_CHANGE_FAILURE;
  }
}

1290 1291 1292 1293 1294 1295
static gboolean
return_true (gpointer key, gpointer value, gpointer user_data)
{
  return TRUE;
}

Wim Taymans's avatar
Wim Taymans committed
1296
static void
1297
gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession)
Wim Taymans's avatar
Wim Taymans committed
1298
{
1299
  g_hash_table_foreach_remove (rtpsession->priv->ptmap, return_true, NULL);
Wim Taymans's avatar
Wim Taymans committed
1300 1301
}

1302 1303
/* called when the session manager has an RTP packet or a list of packets
 * ready for further processing */
1304 1305 1306 1307 1308
static GstFlowReturn
gst_rtp_session_process_rtp (RTPSession * sess, RTPSource * src,
    GstBuffer * buffer, gpointer user_data)
{
  GstFlowReturn result;
1309
  GstRtpSession *rtpsession;
1310
  GstPad *rtp_src;
1311 1312 1313

  rtpsession = GST_RTP_SESSION (user_data);

1314 1315 1316 1317 1318 1319
  GST_RTP_SESSION_LOCK (rtpsession);
  if ((rtp_src = rtpsession->recv_rtp_src))
    gst_object_ref (rtp_src);
  GST_RTP_SESSION_UNLOCK (rtpsession);

  if (rtp_src) {
1320
    GST_LOG_OBJECT (rtpsession, "pushing received RTP packet");
1321 1322
    result = gst_pad_push (rtp_src, buffer);
    gst_object_unref (rtp_src);
1323