rtpsession.c 98.9 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
23
/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
 * with newer GLib versions (>= 2.31.0) */
#define GLIB_DISABLE_DEPRECATION_WARNINGS

24
25
26
27
28
#include <string.h>

#include <gst/rtp/gstrtpbuffer.h>
#include <gst/rtp/gstrtcpbuffer.h>

29
30
#include <gst/glib-compat-private.h>

31
#include "gstrtpbin-marshal.h"
32
33
34
35
36
37
38
39
#include "rtpsession.h"

GST_DEBUG_CATEGORY_STATIC (rtp_session_debug);
#define GST_CAT_DEFAULT rtp_session_debug

/* signals and args */
enum
{
40
  SIGNAL_GET_SOURCE_BY_SSRC,
41
42
43
  SIGNAL_ON_NEW_SSRC,
  SIGNAL_ON_SSRC_COLLISION,
  SIGNAL_ON_SSRC_VALIDATED,
44
  SIGNAL_ON_SSRC_ACTIVE,
Wim Taymans's avatar
Wim Taymans committed
45
  SIGNAL_ON_SSRC_SDES,
46
  SIGNAL_ON_BYE_SSRC,
47
48
  SIGNAL_ON_BYE_TIMEOUT,
  SIGNAL_ON_TIMEOUT,
49
  SIGNAL_ON_SENDER_TIMEOUT,
50
  SIGNAL_ON_SENDING_RTCP,
51
  SIGNAL_ON_FEEDBACK_RTCP,
52
  SIGNAL_SEND_RTCP,
53
54
55
  LAST_SIGNAL
};

Wim Taymans's avatar
Wim Taymans committed
56
57
#define DEFAULT_INTERNAL_SOURCE      NULL
#define DEFAULT_BANDWIDTH            RTP_STATS_BANDWIDTH
58
#define DEFAULT_RTCP_FRACTION        (RTP_STATS_RTCP_FRACTION * RTP_STATS_BANDWIDTH)
59
60
#define DEFAULT_RTCP_RR_BANDWIDTH    -1
#define DEFAULT_RTCP_RS_BANDWIDTH    -1
61
#define DEFAULT_RTCP_MTU             1400
62
#define DEFAULT_SDES                 NULL
Wim Taymans's avatar
Wim Taymans committed
63
64
#define DEFAULT_NUM_SOURCES          0
#define DEFAULT_NUM_ACTIVE_SOURCES   0
65
#define DEFAULT_SOURCES              NULL
66
#define DEFAULT_RTCP_MIN_INTERVAL    (RTP_STATS_MIN_INTERVAL * GST_SECOND)
67
#define DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW (2 * GST_SECOND)
68
#define DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD (3)
69
#define DEFAULT_PROBATION            RTP_DEFAULT_PROBATION
70
71
72

enum
{
Wim Taymans's avatar
Wim Taymans committed
73
  PROP_0,
74
  PROP_INTERNAL_SSRC,
Wim Taymans's avatar
Wim Taymans committed
75
76
77
  PROP_INTERNAL_SOURCE,
  PROP_BANDWIDTH,
  PROP_RTCP_FRACTION,
78
79
  PROP_RTCP_RR_BANDWIDTH,
  PROP_RTCP_RS_BANDWIDTH,
80
  PROP_RTCP_MTU,
81
  PROP_SDES,
Wim Taymans's avatar
Wim Taymans committed
82
83
  PROP_NUM_SOURCES,
  PROP_NUM_ACTIVE_SOURCES,
84
  PROP_SOURCES,
85
  PROP_FAVOR_NEW,
86
  PROP_RTCP_MIN_INTERVAL,
87
  PROP_RTCP_FEEDBACK_RETENTION_WINDOW,
88
  PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD,
89
  PROP_PROBATION,
Wim Taymans's avatar
Wim Taymans committed
90
  PROP_LAST
91
92
};

93
94
95
/* update average packet size */
#define INIT_AVG(avg, val) \
   (avg) = (val);
Wim Taymans's avatar
Wim Taymans committed
96
97
#define UPDATE_AVG(avg, val)            \
  if ((avg) == 0)                       \
98
   (avg) = (val);                       \
Wim Taymans's avatar
Wim Taymans committed
99
  else                                  \
100
101
   (avg) = ((val) + (15 * (avg))) >> 4;

102

103
104
105
106
107
/* The number RTCP intervals after which to timeout entries in the
 * collision table
 */
#define RTCP_INTERVAL_COLLISION_TIMEOUT 10

108
109
110
111
112
113
114
/* GObject vmethods */
static void rtp_session_finalize (GObject * object);
static void rtp_session_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void rtp_session_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

115
116
static gboolean rtp_session_on_sending_rtcp (RTPSession * sess,
    GstBuffer * buffer, gboolean early);
117
118
static void rtp_session_send_rtcp (RTPSession * sess,
    GstClockTimeDiff max_delay);
119
120


121
122
123
124
static guint rtp_session_signals[LAST_SIGNAL] = { 0 };

G_DEFINE_TYPE (RTPSession, rtp_session, G_TYPE_OBJECT);

125
126
static RTPSource *obtain_source (RTPSession * sess, guint32 ssrc,
    gboolean * created, RTPArrivalStats * arrival, gboolean rtp);
127
static GstFlowReturn rtp_session_schedule_bye_locked (RTPSession * sess,
128
    const gchar * reason, GstClockTime current_time);
129
130
static GstClockTime calculate_rtcp_interval (RTPSession * sess,
    gboolean deterministic, gboolean first);
131

132
133
134
135
136
137
138
139
140
141
static gboolean
accumulate_trues (GSignalInvocationHint * ihint, GValue * return_accu,
    const GValue * handler_return, gpointer data)
{
  if (g_value_get_boolean (handler_return))
    g_value_set_boolean (return_accu, TRUE);

  return TRUE;
}

142
143
144
145
146
147
148
149
150
151
152
static void
rtp_session_class_init (RTPSessionClass * klass)
{
  GObjectClass *gobject_class;

  gobject_class = (GObjectClass *) klass;

  gobject_class->finalize = rtp_session_finalize;
  gobject_class->set_property = rtp_session_set_property;
  gobject_class->get_property = rtp_session_get_property;

153
154
155
156
157
158
159
160
161
162
163
164
165
  /**
   * RTPSession::get-source-by-ssrc:
   * @session: the object which received the signal
   * @ssrc: the SSRC of the RTPSource
   *
   * Request the #RTPSource object with SSRC @ssrc in @session.
   */
  rtp_session_signals[SIGNAL_GET_SOURCE_BY_SSRC] =
      g_signal_new ("get-source-by-ssrc", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (RTPSessionClass,
          get_source_by_ssrc), NULL, NULL, gst_rtp_bin_marshal_OBJECT__UINT,
      RTP_TYPE_SOURCE, 1, G_TYPE_UINT);

166
167
168
169
170
171
172
173
174
175
176
  /**
   * RTPSession::on-new-ssrc:
   * @session: the object which received the signal
   * @src: the new RTPSource
   *
   * Notify of a new SSRC that entered @session.
   */
  rtp_session_signals[SIGNAL_ON_NEW_SSRC] =
      g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_new_ssrc),
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
177
      RTP_TYPE_SOURCE);
178
  /**
179
   * RTPSession::on-ssrc-collision:
180
181
182
183
184
185
186
187
188
   * @session: the object which received the signal
   * @src: the #RTPSource that caused a collision
   *
   * Notify when we have an SSRC collision
   */
  rtp_session_signals[SIGNAL_ON_SSRC_COLLISION] =
      g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_collision),
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
189
      RTP_TYPE_SOURCE);
190
  /**
191
   * RTPSession::on-ssrc-validated:
192
193
194
195
196
197
198
199
200
   * @session: the object which received the signal
   * @src: the new validated RTPSource
   *
   * Notify of a new SSRC that became validated.
   */
  rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED] =
      g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_validated),
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
201
      RTP_TYPE_SOURCE);
202
  /**
Wim Taymans's avatar
Wim Taymans committed
203
   * RTPSession::on-ssrc-active:
204
205
206
207
208
209
210
211
212
   * @session: the object which received the signal
   * @src: the active RTPSource
   *
   * Notify of a SSRC that is active, i.e., sending RTCP.
   */
  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 (RTPSessionClass, on_ssrc_active),
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
213
      RTP_TYPE_SOURCE);
Wim Taymans's avatar
Wim Taymans committed
214
215
216
217
218
219
220
221
222
223
224
  /**
   * RTPSession::on-ssrc-sdes:
   * @session: the object which received the signal
   * @src: the RTPSource
   *
   * Notify that a new SDES was received for SSRC.
   */
  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 (RTPSessionClass, on_ssrc_sdes),
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
225
      RTP_TYPE_SOURCE);
226
227
228
229
230
231
232
233
234
235
236
  /**
   * RTPSession::on-bye-ssrc:
   * @session: the object which received the signal
   * @src: the RTPSource that went away
   *
   * Notify of an SSRC that became inactive because of a BYE packet.
   */
  rtp_session_signals[SIGNAL_ON_BYE_SSRC] =
      g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_ssrc),
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
237
      RTP_TYPE_SOURCE);
238
239
240
241
242
243
244
245
246
247
248
  /**
   * RTPSession::on-bye-timeout:
   * @session: the object which received the signal
   * @src: the RTPSource that timed out
   *
   * Notify of an SSRC that has timed out because of BYE
   */
  rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT] =
      g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_timeout),
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
249
      RTP_TYPE_SOURCE);
250
251
252
253
254
255
256
257
258
259
260
  /**
   * RTPSession::on-timeout:
   * @session: the object which received the signal
   * @src: the RTPSource that timed out
   *
   * Notify of an SSRC that has timed out
   */
  rtp_session_signals[SIGNAL_ON_TIMEOUT] =
      g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_timeout),
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
261
      RTP_TYPE_SOURCE);
262
263
264
265
266
267
268
269
270
271
272
273
  /**
   * RTPSession::on-sender-timeout:
   * @session: the object which received the signal
   * @src: the RTPSource that timed out
   *
   * Notify of an SSRC that was a sender but timed out and became a receiver.
   */
  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 (RTPSessionClass, on_sender_timeout),
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
      RTP_TYPE_SOURCE);
274

275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  /**
   * RTPSession::on-sending-rtcp
   * @session: the object which received the signal
   * @buffer: the #GstBuffer containing the RTCP packet about to be sent
   * @early: %TRUE if the packet is early, %FALSE if it is regular
   *
   * This signal is emitted before sending an RTCP packet, it can be used
   * to add extra RTCP Packets.
   *
   * Returns: %TRUE if the RTCP buffer should NOT be suppressed, %FALSE
   * if suppressing it is acceptable
   */
  rtp_session_signals[SIGNAL_ON_SENDING_RTCP] =
      g_signal_new ("on-sending-rtcp", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_sending_rtcp),
Wim Taymans's avatar
Wim Taymans committed
290
291
292
      accumulate_trues, NULL, gst_rtp_bin_marshal_BOOLEAN__BOXED_BOOLEAN,
      G_TYPE_BOOLEAN, 2, GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE,
      G_TYPE_BOOLEAN);
293

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  /**
   * RTPSession::on-feedback-rtcp:
   * @session: the object which received the signal
   * @type: Type of RTCP packet, will be %GST_RTCP_TYPE_RTPFB or
   *  %GST_RTCP_TYPE_RTPFB
   * @fbtype: The type of RTCP FB packet, probably part of #GstRTCPFBType
   * @sender_ssrc: The SSRC of the sender
   * @media_ssrc: The SSRC of the media this refers to
   * @fci: a #GstBuffer with the FCI data from the FB packet or %NULL if
   * there was no FCI
   *
   * Notify that a RTCP feedback packet has been received
   */
  rtp_session_signals[SIGNAL_ON_FEEDBACK_RTCP] =
      g_signal_new ("on-feedback-rtcp", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_feedback_rtcp),
Wim Taymans's avatar
Wim Taymans committed
310
      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT_UINT_UINT_BOXED,
311
      G_TYPE_NONE, 5, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT,
312
      GST_TYPE_BUFFER);
313

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  /**
   * RTPSession::send-rtcp:
   * @session: the object which received the signal
   * @max_delay: The maximum delay after which the feedback will not be useful
   *  anymore
   *
   * Requests that the #RTPSession initiate a new RTCP packet as soon as
   * possible within the requested delay.
   */

  rtp_session_signals[SIGNAL_SEND_RTCP] =
      g_signal_new ("send-rtcp", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (RTPSessionClass, send_rtcp), NULL, NULL,
      gst_rtp_bin_marshal_VOID__UINT64, G_TYPE_NONE, 1, G_TYPE_UINT64);

330
331
332
333
334
  g_object_class_install_property (gobject_class, PROP_INTERNAL_SSRC,
      g_param_spec_uint ("internal-ssrc", "Internal SSRC",
          "The internal SSRC used for the session",
          0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

Wim Taymans's avatar
Wim Taymans committed
335
336
337
  g_object_class_install_property (gobject_class, PROP_INTERNAL_SOURCE,
      g_param_spec_object ("internal-source", "Internal Source",
          "The internal source element of the session",
338
          RTP_TYPE_SOURCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
339
340
341

  g_object_class_install_property (gobject_class, PROP_BANDWIDTH,
      g_param_spec_double ("bandwidth", "Bandwidth",
342
          "The bandwidth of the session (0 for auto-discover)",
343
344
          0.0, G_MAXDOUBLE, DEFAULT_BANDWIDTH,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
345
346
347

  g_object_class_install_property (gobject_class, PROP_RTCP_FRACTION,
      g_param_spec_double ("rtcp-fraction", "RTCP Fraction",
348
          "The fraction of the bandwidth used for RTCP (or as a real fraction of the RTP bandwidth if < 1)",
349
350
          0.0, G_MAXDOUBLE, DEFAULT_RTCP_FRACTION,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
351

352
353
354
  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)",
355
356
          -1, G_MAXINT, DEFAULT_RTCP_RR_BANDWIDTH,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
357
358
359
360

  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)",
361
362
          -1, G_MAXINT, DEFAULT_RTCP_RS_BANDWIDTH,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
363

364
365
366
367
368
369
  g_object_class_install_property (gobject_class, PROP_RTCP_MTU,
      g_param_spec_uint ("rtcp-mtu", "RTCP MTU",
          "The maximum size of the RTCP packets",
          16, G_MAXINT16, DEFAULT_RTCP_MTU,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

370
371
372
373
  g_object_class_install_property (gobject_class, PROP_SDES,
      g_param_spec_boxed ("sdes", "SDES",
          "The SDES items of this session",
          GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
374
375
376
377

  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,
378
          DEFAULT_NUM_SOURCES, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
379
380
381
382

  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,
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
          DEFAULT_NUM_ACTIVE_SOURCES,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
  /**
   * RTPSource::sources
   *
   * Get a GValue Array of all sources in the session.
   *
   * <example>
   * <title>Getting the #RTPSources of a session
   * <programlisting>
   * {
   *   GValueArray *arr;
   *   GValue *val;
   *   guint i;
   *
   *   g_object_get (sess, "sources", &arr, NULL);
   *
   *   for (i = 0; i < arr->n_values; i++) {
   *     RTPSource *source;
   *
   *     val = g_value_array_get_nth (arr, i);
   *     source = g_value_get_object (val);
   *   }
   *   g_value_array_free (arr);
   * }
   * </programlisting>
   * </example>
   */
  g_object_class_install_property (gobject_class, PROP_SOURCES,
      g_param_spec_boxed ("sources", "Sources",
          "An array of all known sources in the session",
          G_TYPE_VALUE_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
415

416
417
418
419
420
  g_object_class_install_property (gobject_class, PROP_FAVOR_NEW,
      g_param_spec_boolean ("favor-new", "Favor new sources",
          "Resolve SSRC conflict in favor of new sources", FALSE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

421
422
423
424
425
  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));
426

427
428
429
430
431
432
433
434
  g_object_class_install_property (gobject_class,
      PROP_RTCP_FEEDBACK_RETENTION_WINDOW,
      g_param_spec_uint64 ("rtcp-feedback-retention-window",
          "RTCP Feedback retention window",
          "Duration during which RTCP Feedback packets are retained (in ns)",
          0, G_MAXUINT64, DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

435
436
437
438
439
440
441
442
  g_object_class_install_property (gobject_class,
      PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD,
      g_param_spec_uint ("rtcp-immediate-feedback-threshold",
          "RTCP Immediate Feedback threshold",
          "The maximum number of members of a RTP session for which immediate"
          " feedback is used",
          0, G_MAXUINT, DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
443

444
445
446
447
448
449
  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));

450
451
  klass->get_source_by_ssrc =
      GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc);
452
  klass->on_sending_rtcp = GST_DEBUG_FUNCPTR (rtp_session_on_sending_rtcp);
453
  klass->send_rtcp = GST_DEBUG_FUNCPTR (rtp_session_send_rtcp);
454

455
456
457
458
459
460
  GST_DEBUG_CATEGORY_INIT (rtp_session_debug, "rtpsession", 0, "RTP Session");
}

static void
rtp_session_init (RTPSession * sess)
{
461
  gint i;
Wim Taymans's avatar
Wim Taymans committed
462
  gchar *str;
463
  GstStructure *sdes;
464

Wim Taymans's avatar
Wim Taymans committed
465
  g_mutex_init (&sess->lock);
466
467
468
469
470
471
472
473
474
  sess->key = g_random_int ();
  sess->mask_idx = 0;
  sess->mask = 0;

  for (i = 0; i < 32; i++) {
    sess->ssrcs[i] =
        g_hash_table_new_full (NULL, NULL, NULL,
        (GDestroyNotify) g_object_unref);
  }
475
476
477

  rtp_stats_init_defaults (&sess->stats);

478
479
480
481
482
483
  sess->recalc_bandwidth = TRUE;
  sess->bandwidth = DEFAULT_BANDWIDTH;
  sess->rtcp_bandwidth = DEFAULT_RTCP_FRACTION;
  sess->rtcp_rr_bandwidth = DEFAULT_RTCP_RR_BANDWIDTH;
  sess->rtcp_rs_bandwidth = DEFAULT_RTCP_RS_BANDWIDTH;

484
485
  /* create an active SSRC for this session manager */
  sess->source = rtp_session_create_source (sess);
486
  sess->source->validated = TRUE;
487
  sess->source->internal = TRUE;
488
  sess->stats.active_sources++;
489
  INIT_AVG (sess->stats.avg_rtcp_packet_size, 100);
490
491
  sess->source->stats.prev_rtcptime = 0;
  sess->source->stats.last_rtcptime = 1;
492

493
494
495
  rtp_stats_set_min_interval (&sess->stats,
      (gdouble) DEFAULT_RTCP_MIN_INTERVAL / GST_SECOND);

496
497
  /* default UDP header length */
  sess->header_len = 28;
498
  sess->mtu = DEFAULT_RTCP_MTU;
499

500
501
  sess->probation = DEFAULT_PROBATION;

502
  /* some default SDES entries */
503
  sdes = gst_structure_new_empty ("application/x-rtp-source-sdes");
504
505

  /* we do not want to leak details like the username or hostname here */
506
  str = g_strdup_printf ("user%u@host-%x", g_random_int (), g_random_int ());
507
  gst_structure_set (sdes, "cname", G_TYPE_STRING, str, NULL);
Wim Taymans's avatar
Wim Taymans committed
508
509
  g_free (str);

510
511
512
#if 0
  /* we do not want to leak the user's real name here */
  str = g_strdup_printf ("Anon%u", g_random_int ());
513
  gst_structure_set (sdes, "name", G_TYPE_STRING, str, NULL);
514
515
516
  g_free (str);
#endif

517
518
519
520
  gst_structure_set (sdes, "tool", G_TYPE_STRING, "GStreamer", NULL);

  /* and configure in the source */
  rtp_source_set_sdes_struct (sess->source, sdes);
521

522
  sess->first_rtcp = TRUE;
523
524
  sess->next_rtcp_check_time = GST_CLOCK_TIME_NONE;

525
  sess->allow_early = TRUE;
526
  sess->next_early_rtcp_time = GST_CLOCK_TIME_NONE;
527
  sess->rtcp_feedback_retention_window = DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW;
528
529
  sess->rtcp_immediate_feedback_threshold =
      DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD;
530

531
  sess->last_keyframe_request = GST_CLOCK_TIME_NONE;
532

533
534
535
536
537
538
539
  GST_DEBUG ("%p: session using SSRC: %08x", sess, sess->source->ssrc);
}

static void
rtp_session_finalize (GObject * object)
{
  RTPSession *sess;
540
  gint i;
541
542
543

  sess = RTP_SESSION_CAST (object);

Wim Taymans's avatar
Wim Taymans committed
544
  g_mutex_clear (&sess->lock);
545

546
547
548
  for (i = 0; i < 32; i++)
    g_hash_table_destroy (sess->ssrcs[i]);

549
550
551
552
553
  g_object_unref (sess->source);

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

554
555
556
557
558
559
560
static void
copy_source (gpointer key, RTPSource * source, GValueArray * arr)
{
  GValue value = { 0 };

  g_value_init (&value, RTP_TYPE_SOURCE);
  g_value_take_object (&value, source);
Wim Taymans's avatar
Wim Taymans committed
561
  /* copies the value */
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  g_value_array_append (arr, &value);
}

static GValueArray *
rtp_session_create_sources (RTPSession * sess)
{
  GValueArray *res;
  guint size;

  RTP_SESSION_LOCK (sess);
  /* get number of elements in the table */
  size = g_hash_table_size (sess->ssrcs[sess->mask_idx]);
  /* create the result value array */
  res = g_value_array_new (size);

  /* and copy all values into the array */
  g_hash_table_foreach (sess->ssrcs[sess->mask_idx], (GHFunc) copy_source, res);
  RTP_SESSION_UNLOCK (sess);

  return res;
}

584
585
586
587
588
589
590
591
592
static void
rtp_session_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  RTPSession *sess;

  sess = RTP_SESSION (object);

  switch (prop_id) {
593
594
595
    case PROP_INTERNAL_SSRC:
      rtp_session_set_internal_ssrc (sess, g_value_get_uint (value));
      break;
Wim Taymans's avatar
Wim Taymans committed
596
    case PROP_BANDWIDTH:
597
      RTP_SESSION_LOCK (sess);
598
599
      sess->bandwidth = g_value_get_double (value);
      sess->recalc_bandwidth = TRUE;
600
      RTP_SESSION_UNLOCK (sess);
Wim Taymans's avatar
Wim Taymans committed
601
602
      break;
    case PROP_RTCP_FRACTION:
603
      RTP_SESSION_LOCK (sess);
604
605
      sess->rtcp_bandwidth = g_value_get_double (value);
      sess->recalc_bandwidth = TRUE;
606
      RTP_SESSION_UNLOCK (sess);
607
608
      break;
    case PROP_RTCP_RR_BANDWIDTH:
609
      RTP_SESSION_LOCK (sess);
610
611
      sess->rtcp_rr_bandwidth = g_value_get_int (value);
      sess->recalc_bandwidth = TRUE;
612
      RTP_SESSION_UNLOCK (sess);
613
614
      break;
    case PROP_RTCP_RS_BANDWIDTH:
615
      RTP_SESSION_LOCK (sess);
616
617
      sess->rtcp_rs_bandwidth = g_value_get_int (value);
      sess->recalc_bandwidth = TRUE;
618
      RTP_SESSION_UNLOCK (sess);
Wim Taymans's avatar
Wim Taymans committed
619
      break;
620
621
622
    case PROP_RTCP_MTU:
      sess->mtu = g_value_get_uint (value);
      break;
623
624
    case PROP_SDES:
      rtp_session_set_sdes_struct (sess, g_value_get_boxed (value));
Wim Taymans's avatar
Wim Taymans committed
625
      break;
626
627
628
    case PROP_FAVOR_NEW:
      sess->favor_new = g_value_get_boolean (value);
      break;
629
630
631
    case PROP_RTCP_MIN_INTERVAL:
      rtp_stats_set_min_interval (&sess->stats,
          (gdouble) g_value_get_uint64 (value) / GST_SECOND);
632
633
634
635
636
637
      /* trigger reconsideration */
      RTP_SESSION_LOCK (sess);
      sess->next_rtcp_check_time = 0;
      RTP_SESSION_UNLOCK (sess);
      if (sess->callbacks.reconsider)
        sess->callbacks.reconsider (sess, sess->reconsider_user_data);
638
      break;
639
640
641
    case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD:
      sess->rtcp_immediate_feedback_threshold = g_value_get_uint (value);
      break;
642
643
644
645
    case PROP_PROBATION:
      sess->probation = g_value_get_uint (value);
      g_object_set_property (G_OBJECT (sess->source), "probation", value);
      break;
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
rtp_session_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  RTPSession *sess;

  sess = RTP_SESSION (object);

  switch (prop_id) {
661
662
663
    case PROP_INTERNAL_SSRC:
      g_value_set_uint (value, rtp_session_get_internal_ssrc (sess));
      break;
Wim Taymans's avatar
Wim Taymans committed
664
665
666
667
    case PROP_INTERNAL_SOURCE:
      g_value_take_object (value, rtp_session_get_internal_source (sess));
      break;
    case PROP_BANDWIDTH:
668
      g_value_set_double (value, sess->bandwidth);
Wim Taymans's avatar
Wim Taymans committed
669
670
      break;
    case PROP_RTCP_FRACTION:
671
672
673
674
675
676
677
      g_value_set_double (value, sess->rtcp_bandwidth);
      break;
    case PROP_RTCP_RR_BANDWIDTH:
      g_value_set_int (value, sess->rtcp_rr_bandwidth);
      break;
    case PROP_RTCP_RS_BANDWIDTH:
      g_value_set_int (value, sess->rtcp_rs_bandwidth);
Wim Taymans's avatar
Wim Taymans committed
678
      break;
679
680
681
    case PROP_RTCP_MTU:
      g_value_set_uint (value, sess->mtu);
      break;
682
683
    case PROP_SDES:
      g_value_take_boxed (value, rtp_session_get_sdes_struct (sess));
Wim Taymans's avatar
Wim Taymans committed
684
685
686
687
688
689
690
      break;
    case PROP_NUM_SOURCES:
      g_value_set_uint (value, rtp_session_get_num_sources (sess));
      break;
    case PROP_NUM_ACTIVE_SOURCES:
      g_value_set_uint (value, rtp_session_get_num_active_sources (sess));
      break;
691
692
693
    case PROP_SOURCES:
      g_value_take_boxed (value, rtp_session_create_sources (sess));
      break;
694
695
696
    case PROP_FAVOR_NEW:
      g_value_set_boolean (value, sess->favor_new);
      break;
697
698
699
    case PROP_RTCP_MIN_INTERVAL:
      g_value_set_uint64 (value, sess->stats.min_interval * GST_SECOND);
      break;
700
701
702
    case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD:
      g_value_set_uint (value, sess->rtcp_immediate_feedback_threshold);
      break;
703
704
705
706
    case PROP_PROBATION:
      g_value_set_uint (value, sess->probation);
      g_object_get_property (G_OBJECT (sess->source), "probation", value);
      break;
707
708
709
710
711
712
713
714
715
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
on_new_ssrc (RTPSession * sess, RTPSource * source)
{
716
  g_object_ref (source);
717
  RTP_SESSION_UNLOCK (sess);
718
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_NEW_SSRC], 0, source);
719
  RTP_SESSION_LOCK (sess);
720
  g_object_unref (source);
721
722
723
724
725
}

static void
on_ssrc_collision (RTPSession * sess, RTPSource * source)
{
726
  g_object_ref (source);
727
  RTP_SESSION_UNLOCK (sess);
728
729
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_COLLISION], 0,
      source);
730
  RTP_SESSION_LOCK (sess);
731
  g_object_unref (source);
732
733
734
735
736
}

static void
on_ssrc_validated (RTPSession * sess, RTPSource * source)
{
737
  g_object_ref (source);
738
  RTP_SESSION_UNLOCK (sess);
739
740
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED], 0,
      source);
741
  RTP_SESSION_LOCK (sess);
742
  g_object_unref (source);
743
744
}

745
746
747
static void
on_ssrc_active (RTPSession * sess, RTPSource * source)
{
748
  g_object_ref (source);
749
750
751
  RTP_SESSION_UNLOCK (sess);
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE], 0, source);
  RTP_SESSION_LOCK (sess);
752
  g_object_unref (source);
753
754
}

Wim Taymans's avatar
Wim Taymans committed
755
756
757
static void
on_ssrc_sdes (RTPSession * sess, RTPSource * source)
{
758
  g_object_ref (source);
759
  GST_DEBUG ("SDES changed for SSRC %08x", source->ssrc);
Wim Taymans's avatar
Wim Taymans committed
760
761
762
  RTP_SESSION_UNLOCK (sess);
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_SDES], 0, source);
  RTP_SESSION_LOCK (sess);
763
  g_object_unref (source);
Wim Taymans's avatar
Wim Taymans committed
764
765
}

766
767
768
static void
on_bye_ssrc (RTPSession * sess, RTPSource * source)
{
769
  g_object_ref (source);
770
  RTP_SESSION_UNLOCK (sess);
771
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_SSRC], 0, source);
772
  RTP_SESSION_LOCK (sess);
773
  g_object_unref (source);
774
775
}

776
777
778
static void
on_bye_timeout (RTPSession * sess, RTPSource * source)
{
779
  g_object_ref (source);
780
  RTP_SESSION_UNLOCK (sess);
781
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT], 0, source);
782
  RTP_SESSION_LOCK (sess);
783
  g_object_unref (source);
784
785
786
787
788
}

static void
on_timeout (RTPSession * sess, RTPSource * source)
{
789
  g_object_ref (source);
790
  RTP_SESSION_UNLOCK (sess);
791
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_TIMEOUT], 0, source);
792
  RTP_SESSION_LOCK (sess);
793
  g_object_unref (source);
794
795
}

796
797
798
static void
on_sender_timeout (RTPSession * sess, RTPSource * source)
{
799
  g_object_ref (source);
800
801
802
803
  RTP_SESSION_UNLOCK (sess);
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
      source);
  RTP_SESSION_LOCK (sess);
804
  g_object_unref (source);
805
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
/**
 * rtp_session_new:
 *
 * Create a new session object.
 *
 * Returns: a new #RTPSession. g_object_unref() after usage.
 */
RTPSession *
rtp_session_new (void)
{
  RTPSession *sess;

  sess = g_object_new (RTP_TYPE_SESSION, NULL);

  return sess;
}

/**
 * rtp_session_set_callbacks:
 * @sess: an #RTPSession
 * @callbacks: callbacks to configure
 * @user_data: user data passed in the callbacks
 *
 * Configure a set of callbacks to be notified of actions.
 */
void
rtp_session_set_callbacks (RTPSession * sess, RTPSessionCallbacks * callbacks,
    gpointer user_data)
{
  g_return_if_fail (RTP_IS_SESSION (sess));

838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
  if (callbacks->process_rtp) {
    sess->callbacks.process_rtp = callbacks->process_rtp;
    sess->process_rtp_user_data = user_data;
  }
  if (callbacks->send_rtp) {
    sess->callbacks.send_rtp = callbacks->send_rtp;
    sess->send_rtp_user_data = user_data;
  }
  if (callbacks->send_rtcp) {
    sess->callbacks.send_rtcp = callbacks->send_rtcp;
    sess->send_rtcp_user_data = user_data;
  }
  if (callbacks->sync_rtcp) {
    sess->callbacks.sync_rtcp = callbacks->sync_rtcp;
    sess->sync_rtcp_user_data = user_data;
  }
  if (callbacks->clock_rate) {
    sess->callbacks.clock_rate = callbacks->clock_rate;
    sess->clock_rate_user_data = user_data;
  }
  if (callbacks->reconsider) {
    sess->callbacks.reconsider = callbacks->reconsider;
    sess->reconsider_user_data = user_data;
  }
862
863
864
865
  if (callbacks->request_key_unit) {
    sess->callbacks.request_key_unit = callbacks->request_key_unit;
    sess->request_key_unit_user_data = user_data;
  }
866
867
868
869
  if (callbacks->request_time) {
    sess->callbacks.request_time = callbacks->request_time;
    sess->request_time_user_data = user_data;
  }
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
}

/**
 * rtp_session_set_process_rtp_callback:
 * @sess: an #RTPSession
 * @callback: callback to set
 * @user_data: user data passed in the callback
 *
 * Configure only the process_rtp callback to be notified of the process_rtp action.
 */
void
rtp_session_set_process_rtp_callback (RTPSession * sess,
    RTPSessionProcessRTP callback, gpointer user_data)
{
  g_return_if_fail (RTP_IS_SESSION (sess));

  sess->callbacks.process_rtp = callback;
  sess->process_rtp_user_data = user_data;
}

/**
 * rtp_session_set_send_rtp_callback:
 * @sess: an #RTPSession
 * @callback: callback to set
 * @user_data: user data passed in the callback
 *
 * Configure only the send_rtp callback to be notified of the send_rtp action.
 */
void
rtp_session_set_send_rtp_callback (RTPSession * sess,
    RTPSessionSendRTP callback, gpointer user_data)
{
  g_return_if_fail (RTP_IS_SESSION (sess));

  sess->callbacks.send_rtp = callback;
  sess->send_rtp_user_data = user_data;
}

/**
 * rtp_session_set_send_rtcp_callback:
 * @sess: an #RTPSession
 * @callback: callback to set
 * @user_data: user data passed in the callback
 *
 * Configure only the send_rtcp callback to be notified of the send_rtcp action.
 */
void
rtp_session_set_send_rtcp_callback (RTPSession * sess,
    RTPSessionSendRTCP callback, gpointer user_data)
{
  g_return_if_fail (RTP_IS_SESSION (sess));

  sess->callbacks.send_rtcp = callback;
  sess->send_rtcp_user_data = user_data;
}

/**
 * rtp_session_set_sync_rtcp_callback:
 * @sess: an #RTPSession
 * @callback: callback to set
 * @user_data: user data passed in the callback
 *
 * Configure only the sync_rtcp callback to be notified of the sync_rtcp action.
 */
void
rtp_session_set_sync_rtcp_callback (RTPSession * sess,
    RTPSessionSyncRTCP callback, gpointer user_data)
{
  g_return_if_fail (RTP_IS_SESSION (sess));

  sess->callbacks.sync_rtcp = callback;
  sess->sync_rtcp_user_data = user_data;
}

/**
 * rtp_session_set_clock_rate_callback:
 * @sess: an #RTPSession
 * @callback: callback to set
 * @user_data: user data passed in the callback
 *
 * Configure only the clock_rate callback to be notified of the clock_rate action.
 */
void
rtp_session_set_clock_rate_callback (RTPSession * sess,
    RTPSessionClockRate callback, gpointer user_data)
{
  g_return_if_fail (RTP_IS_SESSION (sess));

  sess->callbacks.clock_rate = callback;
  sess->clock_rate_user_data = user_data;
}

/**
 * rtp_session_set_reconsider_callback:
 * @sess: an #RTPSession
 * @callback: callback to set
 * @user_data: user data passed in the callback
 *
 * Configure only the reconsider callback to be notified of the reconsider action.
 */
void
rtp_session_set_reconsider_callback (RTPSession * sess,
    RTPSessionReconsider callback, gpointer user_data)
{
  g_return_if_fail (RTP_IS_SESSION (sess));

  sess->callbacks.reconsider = callback;
  sess->reconsider_user_data = user_data;
978
979
}

980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
/**
 * rtp_session_set_request_time_callback:
 * @sess: an #RTPSession
 * @callback: callback to set
 * @user_data: user data passed in the callback
 *
 * Configure only the request_time callback
 */
void
rtp_session_set_request_time_callback (RTPSession * sess,
    RTPSessionRequestTime callback, gpointer user_data)
{
  g_return_if_fail (RTP_IS_SESSION (sess));

  sess->callbacks.request_time = callback;
  sess->request_time_user_data = user_data;
}

998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
/**
 * rtp_session_set_bandwidth:
 * @sess: an #RTPSession
 * @bandwidth: the bandwidth allocated
 *
 * Set the session bandwidth in bytes per second.
 */
void
rtp_session_set_bandwidth (RTPSession * sess, gdouble bandwidth)
{
  g_return_if_fail (RTP_IS_SESSION (sess));

Wim Taymans's avatar
Wim Taymans committed
1010
  RTP_SESSION_LOCK (sess);
1011
  sess->stats.bandwidth = bandwidth;
Wim Taymans's avatar
Wim Taymans committed
1012
  RTP_SESSION_UNLOCK (sess);
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
}

/**
 * rtp_session_get_bandwidth:
 * @sess: an #RTPSession
 *
 * Get the session bandwidth.
 *
 * Returns: the session bandwidth.
 */
gdouble
rtp_session_get_bandwidth (RTPSession * sess)
{
Wim Taymans's avatar
Wim Taymans committed
1026
1027
  gdouble result;

1028
1029
  g_return_val_if_fail (RTP_IS_SESSION (sess), 0);

Wim Taymans's avatar
Wim Taymans committed
1030
1031
1032
1033
1034
  RTP_SESSION_LOCK (sess);
  result = sess->stats.bandwidth;
  RTP_SESSION_UNLOCK (sess);

  return result;
1035
1036
1037
}

/**
Wim Taymans's avatar
Wim Taymans committed
1038
 * rtp_session_set_rtcp_fraction:
1039
1040
1041
 * @sess: an #RTPSession
 * @bandwidth: the RTCP bandwidth
 *
1042
 * Set the bandwidth in bytes per second that should be used for RTCP
1043
 * messages.
1044
1045
 */
void
Wim Taymans's avatar
Wim Taymans committed
1046
rtp_session_set_rtcp_fraction (RTPSession * sess, gdouble bandwidth)
1047
1048
1049
{
  g_return_if_fail (RTP_IS_SESSION (sess));

Wim Taymans's avatar
Wim Taymans committed
1050
  RTP_SESSION_LOCK (sess);
1051
  sess->stats.rtcp_bandwidth = bandwidth;
Wim Taymans's avatar
Wim Taymans committed
1052
  RTP_SESSION_UNLOCK (sess);
1053
1054
1055
}

/**
Wim Taymans's avatar
Wim Taymans committed
1056
 * rtp_session_get_rtcp_fraction:
1057
1058
1059
1060
1061
1062
1063
 * @sess: an #RTPSession
 *
 * Get the session bandwidth used for RTCP.
 *
 * Returns: The bandwidth used for RTCP messages.
 */
gdouble
Wim Taymans's avatar
Wim Taymans committed
1064
rtp_session_get_rtcp_fraction (RTPSession * sess)
1065
{
Wim Taymans's avatar
Wim Taymans committed
1066
  gdouble result;
1067

Wim Taymans's avatar
Wim Taymans committed
1068
  g_return_val_if_fail (RTP_IS_SESSION (sess), 0.0);
1069

Wim Taymans's avatar
Wim Taymans committed
1070
1071
1072
  RTP_SESSION_LOCK (sess);
  result = sess->stats.rtcp_bandwidth;
  RTP_SESSION_UNLOCK (sess);
1073

Wim Taymans's avatar
Wim Taymans committed
1074
  return result;
1075
1076
}

1077
1078
1079
1080
1081
1082
/**
 * rtp_session_get_sdes_struct:
 * @sess: an #RTSPSession
 *
 * Get the SDES data as a #GstStructure
 *
1083
1084
 * Returns: a GstStructure with SDES items for @sess. This function returns a
 * copy of the SDES structure, use gst_structure_free() after usage.
1085
1086
1087
1088
 */
GstStructure *
rtp_session_get_sdes_struct (RTPSession * sess)
{
1089
1090
  const GstStructure *sdes;
  GstStructure *result = NULL;
1091
1092
1093
1094

  g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);

  RTP_SESSION_LOCK (sess);
1095
1096
1097
  sdes = rtp_source_get_sdes_struct (sess->source);
  if (sdes)
    result = gst_structure_copy (sdes);
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
  RTP_SESSION_UNLOCK (sess);

  return result;
}

/**
 * rtp_session_set_sdes_struct:
 * @sess: an #RTSPSession
 * @sdes: a #GstStructure
 *
1108
 * Set the SDES data as a #GstStructure. This function makes a copy of @sdes.
1109
1110
1111
1112
 */
void
rtp_session_set_sdes_struct (RTPSession * sess, const GstStructure * sdes)
{
1113
  g_return_if_fail (sdes);
1114
1115
1116
  g_return_if_fail (RTP_IS_SESSION (sess));

  RTP_SESSION_LOCK (sess);
1117
  rtp_source_set_sdes_struct (sess->source, gst_structure_copy (sdes));
1118
1119
1120
  RTP_SESSION_UNLOCK (sess);
}

1121
static GstFlowReturn
1122
source_push_rtp (RTPSource * source, gpointer data, RTPSession * session)
1123
1124
1125
{
  GstFlowReturn result = GST_FLOW_OK;

1126
  if (source->internal) {
1127
    GST_LOG ("source %08x pushed sender RTP packet", source->ssrc);
1128

1129
    RTP_SESSION_UNLOCK (session);
1130

1131
1132
    if (session->callbacks.send_rtp)
      result =
1133
          session->callbacks.send_rtp (session, source, data,
1134
          session->send_rtp_user_data);
1135
1136
1137
    else {
      gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
    }
1138
  } else {
1139
    GST_LOG ("source %08x pushed receiver RTP packet", source->ssrc);
1140
1141
    RTP_SESSION_UNLOCK (session);

1142
1143
    if (session->callbacks.process_rtp)
      result =
1144
1145
          session->callbacks.process_rtp (session, source,
          GST_BUFFER_CAST (data), session->process_rtp_user_data);
1146
    else
1147
      gst_buffer_unref (GST_BUFFER_CAST (data));
1148
  }
1149
1150
  RTP_SESSION_LOCK (session);

1151
1152
1153
1154
1155
1156
1157
1158
  return result;
}

static gint
source_clock_rate (RTPSource * source, guint8 pt, RTPSession * session)
{
  gint result;

1159
1160
  RTP_SESSION_UNLOCK (session);

1161
  if (session->callbacks.clock_rate)
1162
1163
1164
    result =
        session->callbacks.clock_rate (session, pt,
        session->clock_rate_user_data);
1165
1166
1167
  else
    result = -1;

1168
1169
  RTP_SESSION_LOCK (session);

1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
  GST_DEBUG ("got clock-rate %d for pt %d", result, pt);

  return result;
}

static RTPSourceCallbacks callbacks = {
  (RTPSourcePushRTP) source_push_rtp,
  (RTPSourceClockRate) source_clock_rate,
};

1180
1181
1182
1183
static gboolean
check_collision (RTPSession * sess, RTPSource * source,
    RTPArrivalStats * arrival, gboolean rtp)
{
1184
  /* If we have no arrival address, we can't do collision checking */
Sebastian Dröge's avatar
Sebastian Dröge committed
1185
  if (!arrival->address)
1186
1187
    return FALSE;

1188
  if (!source->internal) {
Sebastian Dröge's avatar
Sebastian Dröge committed
1189
    GSocketAddress *from;
1190

1191
    /* This is not our local source, but lets check if two remote
Wim Taymans's avatar
Wim Taymans committed
1192
     * source collide */
1193
    if (rtp) {
Sebastian Dröge's avatar
Sebastian Dröge committed
1194
      from = source->rtp_from;
1195
    } else {
Sebastian Dröge's avatar
Sebastian Dröge committed
1196
      from = source->rtcp_from;
1197
1198
    }

Sebastian Dröge's avatar
Sebastian Dröge committed
1199
1200
    if (from) {
      if (__g_socket_address_equal (from, arrival->address)) {
1201
        /* Address is the same */
1202
        return FALSE;
1203
1204
1205
1206
1207
      } else {
        GST_LOG ("we have a third-party collision or loop ssrc:%x",
            rtp_source_get_ssrc (source));
        if (sess->favor_new) {
          if (rtp_source_find_conflicting_address (source,
Sebastian Dröge's avatar
Sebastian Dröge committed
1208
1209
1210
1211
                  arrival->address, arrival->current_time)) {
            gchar *buf1;

            buf1 = __g_socket_address_to_string (arrival->address);
1212
1213
            GST_LOG ("Known conflict on %x for %s, dropping packet",
                rtp_source_get_ssrc (source), buf1);
Sebastian Dröge's avatar
Sebastian Dröge committed
1214
1215
            g_free (buf1);

1216
1217
            return TRUE;
          } else {
Sebastian Dröge's avatar
Sebastian Dröge committed
1218
            gchar *buf1, *buf2;
1219
1220
1221
1222
1223
1224
1225

            /* Current address is not a known conflict, lets assume this is
             * a new source. Save old address in possible conflict list
             */
            rtp_source_add_conflicting_address (source, from,
                arrival->current_time);

Sebastian Dröge's avatar
Sebastian Dröge committed
1226
1227
1228
            buf1 = __g_socket_address_to_string (from);
            buf2 = __g_socket_address_to_string (arrival->address);

1229
1230
1231
1232
1233
            GST_DEBUG ("New conflict for ssrc %x, replacing %s with %s,"
                " saving old as known conflict",
                rtp_source_get_ssrc (source), buf1, buf2);

            if (rtp)
Sebastian Dröge's avatar
Sebastian Dröge committed
1234
              rtp_source_set_rtp_from (source, arrival->address);
1235
            else
Sebastian Dröge's avatar
Sebastian Dröge committed
1236
1237
1238
1239
1240
              rtp_source_set_rtcp_from (source, arrival->address);

            g_free (buf1);
            g_free (buf2);

1241
1242
1243
1244
1245
1246
            return FALSE;
          }
        } else {
          /* Don't need to save old addresses, we ignore new sources */
          return TRUE;
        }