~ubuntu-branches/ubuntu/quantal/gst-plugins-bad-multiverse0.10/quantal

« back to all changes in this revision

Viewing changes to gst/rtpmanager/rtpsession.c

  • Committer: Bazaar Package Importer
  • Author(s): Onkar Shinde
  • Date: 2009-12-07 08:54:28 UTC
  • mfrom: (1.1.15 upstream)
  • Revision ID: james.westby@ubuntu.com-20091207085428-ml6aaukf0p2ph34d
Tags: 0.10.17-0ubuntu1
* New upstream release.
* Add myself to maintainer.
* Fix misc lintian warnings.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* GStreamer
2
 
 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3
 
 *
4
 
 * This library is free software; you can redistribute it and/or
5
 
 * modify it under the terms of the GNU Library General Public
6
 
 * License as published by the Free Software Foundation; either
7
 
 * version 2 of the License, or (at your option) any later version.
8
 
 *
9
 
 * This library is distributed in the hope that it will be useful,
10
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 
 * Library General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU Library General Public
15
 
 * License along with this library; if not, write to the
16
 
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
 
 * Boston, MA 02111-1307, USA.
18
 
 */
19
 
 
20
 
#include <string.h>
21
 
 
22
 
#include <gst/rtp/gstrtpbuffer.h>
23
 
#include <gst/rtp/gstrtcpbuffer.h>
24
 
#include <gst/netbuffer/gstnetbuffer.h>
25
 
 
26
 
#include "gstrtpbin-marshal.h"
27
 
#include "rtpsession.h"
28
 
 
29
 
GST_DEBUG_CATEGORY_STATIC (rtp_session_debug);
30
 
#define GST_CAT_DEFAULT rtp_session_debug
31
 
 
32
 
/* signals and args */
33
 
enum
34
 
{
35
 
  SIGNAL_GET_SOURCE_BY_SSRC,
36
 
  SIGNAL_ON_NEW_SSRC,
37
 
  SIGNAL_ON_SSRC_COLLISION,
38
 
  SIGNAL_ON_SSRC_VALIDATED,
39
 
  SIGNAL_ON_SSRC_ACTIVE,
40
 
  SIGNAL_ON_SSRC_SDES,
41
 
  SIGNAL_ON_BYE_SSRC,
42
 
  SIGNAL_ON_BYE_TIMEOUT,
43
 
  SIGNAL_ON_TIMEOUT,
44
 
  SIGNAL_ON_SENDER_TIMEOUT,
45
 
  LAST_SIGNAL
46
 
};
47
 
 
48
 
#define DEFAULT_INTERNAL_SOURCE      NULL
49
 
#define DEFAULT_BANDWIDTH            RTP_STATS_BANDWIDTH
50
 
#define DEFAULT_RTCP_FRACTION        RTP_STATS_RTCP_BANDWIDTH
51
 
#define DEFAULT_RTCP_MTU             1400
52
 
#define DEFAULT_SDES_CNAME           NULL
53
 
#define DEFAULT_SDES_NAME            NULL
54
 
#define DEFAULT_SDES_EMAIL           NULL
55
 
#define DEFAULT_SDES_PHONE           NULL
56
 
#define DEFAULT_SDES_LOCATION        NULL
57
 
#define DEFAULT_SDES_TOOL            NULL
58
 
#define DEFAULT_SDES_NOTE            NULL
59
 
#define DEFAULT_NUM_SOURCES          0
60
 
#define DEFAULT_NUM_ACTIVE_SOURCES   0
61
 
#define DEFAULT_SOURCES              NULL
62
 
 
63
 
enum
64
 
{
65
 
  PROP_0,
66
 
  PROP_INTERNAL_SSRC,
67
 
  PROP_INTERNAL_SOURCE,
68
 
  PROP_BANDWIDTH,
69
 
  PROP_RTCP_FRACTION,
70
 
  PROP_RTCP_MTU,
71
 
  PROP_SDES_CNAME,
72
 
  PROP_SDES_NAME,
73
 
  PROP_SDES_EMAIL,
74
 
  PROP_SDES_PHONE,
75
 
  PROP_SDES_LOCATION,
76
 
  PROP_SDES_TOOL,
77
 
  PROP_SDES_NOTE,
78
 
  PROP_NUM_SOURCES,
79
 
  PROP_NUM_ACTIVE_SOURCES,
80
 
  PROP_SOURCES,
81
 
  PROP_LAST
82
 
};
83
 
 
84
 
/* update average packet size, we keep this scaled by 16 to keep enough
85
 
 * precision. */
86
 
#define UPDATE_AVG(avg, val)            \
87
 
  if ((avg) == 0)                       \
88
 
   (avg) = (val) << 4;                  \
89
 
  else                                  \
90
 
   (avg) = ((val) + (15 * (avg))) >> 4;
91
 
 
92
 
/* The number RTCP intervals after which to timeout entries in the
93
 
 * collision table
94
 
 */
95
 
#define RTCP_INTERVAL_COLLISION_TIMEOUT 10
96
 
 
97
 
/* GObject vmethods */
98
 
static void rtp_session_finalize (GObject * object);
99
 
static void rtp_session_set_property (GObject * object, guint prop_id,
100
 
    const GValue * value, GParamSpec * pspec);
101
 
static void rtp_session_get_property (GObject * object, guint prop_id,
102
 
    GValue * value, GParamSpec * pspec);
103
 
 
104
 
static guint rtp_session_signals[LAST_SIGNAL] = { 0 };
105
 
 
106
 
G_DEFINE_TYPE (RTPSession, rtp_session, G_TYPE_OBJECT);
107
 
 
108
 
static RTPSource *obtain_source (RTPSession * sess, guint32 ssrc,
109
 
    gboolean * created, RTPArrivalStats * arrival, gboolean rtp);
110
 
static GstFlowReturn rtp_session_schedule_bye_locked (RTPSession * sess,
111
 
    const gchar * reason, GstClockTime current_time);
112
 
static GstClockTime calculate_rtcp_interval (RTPSession * sess,
113
 
    gboolean deterministic, gboolean first);
114
 
 
115
 
static void
116
 
rtp_session_class_init (RTPSessionClass * klass)
117
 
{
118
 
  GObjectClass *gobject_class;
119
 
 
120
 
  gobject_class = (GObjectClass *) klass;
121
 
 
122
 
  gobject_class->finalize = rtp_session_finalize;
123
 
  gobject_class->set_property = rtp_session_set_property;
124
 
  gobject_class->get_property = rtp_session_get_property;
125
 
 
126
 
  /**
127
 
   * RTPSession::get-source-by-ssrc:
128
 
   * @session: the object which received the signal
129
 
   * @ssrc: the SSRC of the RTPSource
130
 
   *
131
 
   * Request the #RTPSource object with SSRC @ssrc in @session.
132
 
   */
133
 
  rtp_session_signals[SIGNAL_GET_SOURCE_BY_SSRC] =
134
 
      g_signal_new ("get-source-by-ssrc", G_TYPE_FROM_CLASS (klass),
135
 
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (RTPSessionClass,
136
 
          get_source_by_ssrc), NULL, NULL, gst_rtp_bin_marshal_OBJECT__UINT,
137
 
      RTP_TYPE_SOURCE, 1, G_TYPE_UINT);
138
 
 
139
 
  /**
140
 
   * RTPSession::on-new-ssrc:
141
 
   * @session: the object which received the signal
142
 
   * @src: the new RTPSource
143
 
   *
144
 
   * Notify of a new SSRC that entered @session.
145
 
   */
146
 
  rtp_session_signals[SIGNAL_ON_NEW_SSRC] =
147
 
      g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
148
 
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_new_ssrc),
149
 
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
150
 
      RTP_TYPE_SOURCE);
151
 
  /**
152
 
   * RTPSession::on-ssrc-collision:
153
 
   * @session: the object which received the signal
154
 
   * @src: the #RTPSource that caused a collision
155
 
   *
156
 
   * Notify when we have an SSRC collision
157
 
   */
158
 
  rtp_session_signals[SIGNAL_ON_SSRC_COLLISION] =
159
 
      g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
160
 
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_collision),
161
 
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
162
 
      RTP_TYPE_SOURCE);
163
 
  /**
164
 
   * RTPSession::on-ssrc-validated:
165
 
   * @session: the object which received the signal
166
 
   * @src: the new validated RTPSource
167
 
   *
168
 
   * Notify of a new SSRC that became validated.
169
 
   */
170
 
  rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED] =
171
 
      g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
172
 
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_validated),
173
 
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
174
 
      RTP_TYPE_SOURCE);
175
 
  /**
176
 
   * RTPSession::on-ssrc-active:
177
 
   * @session: the object which received the signal
178
 
   * @src: the active RTPSource
179
 
   *
180
 
   * Notify of a SSRC that is active, i.e., sending RTCP.
181
 
   */
182
 
  rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE] =
183
 
      g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass),
184
 
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_active),
185
 
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
186
 
      RTP_TYPE_SOURCE);
187
 
  /**
188
 
   * RTPSession::on-ssrc-sdes:
189
 
   * @session: the object which received the signal
190
 
   * @src: the RTPSource
191
 
   *
192
 
   * Notify that a new SDES was received for SSRC.
193
 
   */
194
 
  rtp_session_signals[SIGNAL_ON_SSRC_SDES] =
195
 
      g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass),
196
 
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_sdes),
197
 
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
198
 
      RTP_TYPE_SOURCE);
199
 
  /**
200
 
   * RTPSession::on-bye-ssrc:
201
 
   * @session: the object which received the signal
202
 
   * @src: the RTPSource that went away
203
 
   *
204
 
   * Notify of an SSRC that became inactive because of a BYE packet.
205
 
   */
206
 
  rtp_session_signals[SIGNAL_ON_BYE_SSRC] =
207
 
      g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
208
 
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_ssrc),
209
 
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
210
 
      RTP_TYPE_SOURCE);
211
 
  /**
212
 
   * RTPSession::on-bye-timeout:
213
 
   * @session: the object which received the signal
214
 
   * @src: the RTPSource that timed out
215
 
   *
216
 
   * Notify of an SSRC that has timed out because of BYE
217
 
   */
218
 
  rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT] =
219
 
      g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
220
 
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_timeout),
221
 
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
222
 
      RTP_TYPE_SOURCE);
223
 
  /**
224
 
   * RTPSession::on-timeout:
225
 
   * @session: the object which received the signal
226
 
   * @src: the RTPSource that timed out
227
 
   *
228
 
   * Notify of an SSRC that has timed out
229
 
   */
230
 
  rtp_session_signals[SIGNAL_ON_TIMEOUT] =
231
 
      g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
232
 
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_timeout),
233
 
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
234
 
      RTP_TYPE_SOURCE);
235
 
  /**
236
 
   * RTPSession::on-sender-timeout:
237
 
   * @session: the object which received the signal
238
 
   * @src: the RTPSource that timed out
239
 
   *
240
 
   * Notify of an SSRC that was a sender but timed out and became a receiver.
241
 
   */
242
 
  rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT] =
243
 
      g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
244
 
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_sender_timeout),
245
 
      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
246
 
      RTP_TYPE_SOURCE);
247
 
 
248
 
  g_object_class_install_property (gobject_class, PROP_INTERNAL_SSRC,
249
 
      g_param_spec_uint ("internal-ssrc", "Internal SSRC",
250
 
          "The internal SSRC used for the session",
251
 
          0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
252
 
 
253
 
  g_object_class_install_property (gobject_class, PROP_INTERNAL_SOURCE,
254
 
      g_param_spec_object ("internal-source", "Internal Source",
255
 
          "The internal source element of the session",
256
 
          RTP_TYPE_SOURCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
257
 
 
258
 
  g_object_class_install_property (gobject_class, PROP_BANDWIDTH,
259
 
      g_param_spec_double ("bandwidth", "Bandwidth",
260
 
          "The bandwidth of the session",
261
 
          0.0, G_MAXDOUBLE, DEFAULT_BANDWIDTH,
262
 
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
263
 
 
264
 
  g_object_class_install_property (gobject_class, PROP_RTCP_FRACTION,
265
 
      g_param_spec_double ("rtcp-fraction", "RTCP Fraction",
266
 
          "The fraction of the bandwidth used for RTCP",
267
 
          0.0, G_MAXDOUBLE, DEFAULT_RTCP_FRACTION,
268
 
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
269
 
 
270
 
  g_object_class_install_property (gobject_class, PROP_RTCP_MTU,
271
 
      g_param_spec_uint ("rtcp-mtu", "RTCP MTU",
272
 
          "The maximum size of the RTCP packets",
273
 
          16, G_MAXINT16, DEFAULT_RTCP_MTU,
274
 
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
275
 
 
276
 
  g_object_class_install_property (gobject_class, PROP_SDES_CNAME,
277
 
      g_param_spec_string ("sdes-cname", "SDES CNAME",
278
 
          "The CNAME to put in SDES messages of this session",
279
 
          DEFAULT_SDES_CNAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
280
 
 
281
 
  g_object_class_install_property (gobject_class, PROP_SDES_NAME,
282
 
      g_param_spec_string ("sdes-name", "SDES NAME",
283
 
          "The NAME to put in SDES messages of this session",
284
 
          DEFAULT_SDES_NAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
285
 
 
286
 
  g_object_class_install_property (gobject_class, PROP_SDES_EMAIL,
287
 
      g_param_spec_string ("sdes-email", "SDES EMAIL",
288
 
          "The EMAIL to put in SDES messages of this session",
289
 
          DEFAULT_SDES_EMAIL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
290
 
 
291
 
  g_object_class_install_property (gobject_class, PROP_SDES_PHONE,
292
 
      g_param_spec_string ("sdes-phone", "SDES PHONE",
293
 
          "The PHONE to put in SDES messages of this session",
294
 
          DEFAULT_SDES_PHONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
295
 
 
296
 
  g_object_class_install_property (gobject_class, PROP_SDES_LOCATION,
297
 
      g_param_spec_string ("sdes-location", "SDES LOCATION",
298
 
          "The LOCATION to put in SDES messages of this session",
299
 
          DEFAULT_SDES_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
300
 
 
301
 
  g_object_class_install_property (gobject_class, PROP_SDES_TOOL,
302
 
      g_param_spec_string ("sdes-tool", "SDES TOOL",
303
 
          "The TOOL to put in SDES messages of this session",
304
 
          DEFAULT_SDES_TOOL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
305
 
 
306
 
  g_object_class_install_property (gobject_class, PROP_SDES_NOTE,
307
 
      g_param_spec_string ("sdes-note", "SDES NOTE",
308
 
          "The NOTE to put in SDES messages of this session",
309
 
          DEFAULT_SDES_NOTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
310
 
 
311
 
  g_object_class_install_property (gobject_class, PROP_NUM_SOURCES,
312
 
      g_param_spec_uint ("num-sources", "Num Sources",
313
 
          "The number of sources in the session", 0, G_MAXUINT,
314
 
          DEFAULT_NUM_SOURCES, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
315
 
 
316
 
  g_object_class_install_property (gobject_class, PROP_NUM_ACTIVE_SOURCES,
317
 
      g_param_spec_uint ("num-active-sources", "Num Active Sources",
318
 
          "The number of active sources in the session", 0, G_MAXUINT,
319
 
          DEFAULT_NUM_ACTIVE_SOURCES,
320
 
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
321
 
  /**
322
 
   * RTPSource::sources
323
 
   *
324
 
   * Get a GValue Array of all sources in the session.
325
 
   *
326
 
   * <example>
327
 
   * <title>Getting the #RTPSources of a session
328
 
   * <programlisting>
329
 
   * {
330
 
   *   GValueArray *arr;
331
 
   *   GValue *val;
332
 
   *   guint i;
333
 
   *
334
 
   *   g_object_get (sess, "sources", &arr, NULL);
335
 
   *
336
 
   *   for (i = 0; i < arr->n_values; i++) {
337
 
   *     RTPSource *source;
338
 
   *
339
 
   *     val = g_value_array_get_nth (arr, i);
340
 
   *     source = g_value_get_object (val);
341
 
   *   }
342
 
   *   g_value_array_free (arr);
343
 
   * }
344
 
   * </programlisting>
345
 
   * </example>
346
 
   */
347
 
  g_object_class_install_property (gobject_class, PROP_SOURCES,
348
 
      g_param_spec_boxed ("sources", "Sources",
349
 
          "An array of all known sources in the session",
350
 
          G_TYPE_VALUE_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
351
 
 
352
 
  klass->get_source_by_ssrc =
353
 
      GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc);
354
 
 
355
 
  GST_DEBUG_CATEGORY_INIT (rtp_session_debug, "rtpsession", 0, "RTP Session");
356
 
}
357
 
 
358
 
static void
359
 
rtp_session_init (RTPSession * sess)
360
 
{
361
 
  gint i;
362
 
  gchar *str;
363
 
 
364
 
  sess->lock = g_mutex_new ();
365
 
  sess->key = g_random_int ();
366
 
  sess->mask_idx = 0;
367
 
  sess->mask = 0;
368
 
 
369
 
  for (i = 0; i < 32; i++) {
370
 
    sess->ssrcs[i] =
371
 
        g_hash_table_new_full (NULL, NULL, NULL,
372
 
        (GDestroyNotify) g_object_unref);
373
 
  }
374
 
  sess->cnames = g_hash_table_new_full (NULL, NULL, g_free, NULL);
375
 
 
376
 
  rtp_stats_init_defaults (&sess->stats);
377
 
 
378
 
  /* create an active SSRC for this session manager */
379
 
  sess->source = rtp_session_create_source (sess);
380
 
  sess->source->validated = TRUE;
381
 
  sess->source->internal = TRUE;
382
 
  sess->stats.active_sources++;
383
 
 
384
 
  /* default UDP header length */
385
 
  sess->header_len = 28;
386
 
  sess->mtu = DEFAULT_RTCP_MTU;
387
 
 
388
 
  /* some default SDES entries */
389
 
  str = g_strdup_printf ("%s@%s", g_get_user_name (), g_get_host_name ());
390
 
  rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_CNAME, str);
391
 
  g_free (str);
392
 
 
393
 
  rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_NAME,
394
 
      g_get_real_name ());
395
 
  rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_TOOL, "GStreamer");
396
 
 
397
 
  sess->first_rtcp = TRUE;
398
 
 
399
 
  GST_DEBUG ("%p: session using SSRC: %08x", sess, sess->source->ssrc);
400
 
}
401
 
 
402
 
static void
403
 
rtp_session_finalize (GObject * object)
404
 
{
405
 
  RTPSession *sess;
406
 
  gint i;
407
 
 
408
 
  sess = RTP_SESSION_CAST (object);
409
 
 
410
 
  g_mutex_free (sess->lock);
411
 
  for (i = 0; i < 32; i++)
412
 
    g_hash_table_destroy (sess->ssrcs[i]);
413
 
 
414
 
  g_free (sess->bye_reason);
415
 
 
416
 
  g_hash_table_destroy (sess->cnames);
417
 
  g_object_unref (sess->source);
418
 
 
419
 
  G_OBJECT_CLASS (rtp_session_parent_class)->finalize (object);
420
 
}
421
 
 
422
 
static void
423
 
copy_source (gpointer key, RTPSource * source, GValueArray * arr)
424
 
{
425
 
  GValue value = { 0 };
426
 
 
427
 
  g_value_init (&value, RTP_TYPE_SOURCE);
428
 
  g_value_take_object (&value, source);
429
 
  g_value_array_append (arr, &value);
430
 
}
431
 
 
432
 
static GValueArray *
433
 
rtp_session_create_sources (RTPSession * sess)
434
 
{
435
 
  GValueArray *res;
436
 
  guint size;
437
 
 
438
 
  RTP_SESSION_LOCK (sess);
439
 
  /* get number of elements in the table */
440
 
  size = g_hash_table_size (sess->ssrcs[sess->mask_idx]);
441
 
  /* create the result value array */
442
 
  res = g_value_array_new (size);
443
 
 
444
 
  /* and copy all values into the array */
445
 
  g_hash_table_foreach (sess->ssrcs[sess->mask_idx], (GHFunc) copy_source, res);
446
 
  RTP_SESSION_UNLOCK (sess);
447
 
 
448
 
  return res;
449
 
}
450
 
 
451
 
static void
452
 
rtp_session_set_property (GObject * object, guint prop_id,
453
 
    const GValue * value, GParamSpec * pspec)
454
 
{
455
 
  RTPSession *sess;
456
 
 
457
 
  sess = RTP_SESSION (object);
458
 
 
459
 
  switch (prop_id) {
460
 
    case PROP_INTERNAL_SSRC:
461
 
      rtp_session_set_internal_ssrc (sess, g_value_get_uint (value));
462
 
      break;
463
 
    case PROP_BANDWIDTH:
464
 
      rtp_session_set_bandwidth (sess, g_value_get_double (value));
465
 
      break;
466
 
    case PROP_RTCP_FRACTION:
467
 
      rtp_session_set_rtcp_fraction (sess, g_value_get_double (value));
468
 
      break;
469
 
    case PROP_RTCP_MTU:
470
 
      sess->mtu = g_value_get_uint (value);
471
 
      break;
472
 
    case PROP_SDES_CNAME:
473
 
      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_CNAME,
474
 
          g_value_get_string (value));
475
 
      break;
476
 
    case PROP_SDES_NAME:
477
 
      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_NAME,
478
 
          g_value_get_string (value));
479
 
      break;
480
 
    case PROP_SDES_EMAIL:
481
 
      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_EMAIL,
482
 
          g_value_get_string (value));
483
 
      break;
484
 
    case PROP_SDES_PHONE:
485
 
      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_PHONE,
486
 
          g_value_get_string (value));
487
 
      break;
488
 
    case PROP_SDES_LOCATION:
489
 
      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_LOC,
490
 
          g_value_get_string (value));
491
 
      break;
492
 
    case PROP_SDES_TOOL:
493
 
      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_TOOL,
494
 
          g_value_get_string (value));
495
 
      break;
496
 
    case PROP_SDES_NOTE:
497
 
      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_NOTE,
498
 
          g_value_get_string (value));
499
 
      break;
500
 
    default:
501
 
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
502
 
      break;
503
 
  }
504
 
}
505
 
 
506
 
static void
507
 
rtp_session_get_property (GObject * object, guint prop_id,
508
 
    GValue * value, GParamSpec * pspec)
509
 
{
510
 
  RTPSession *sess;
511
 
 
512
 
  sess = RTP_SESSION (object);
513
 
 
514
 
  switch (prop_id) {
515
 
    case PROP_INTERNAL_SSRC:
516
 
      g_value_set_uint (value, rtp_session_get_internal_ssrc (sess));
517
 
      break;
518
 
    case PROP_INTERNAL_SOURCE:
519
 
      g_value_take_object (value, rtp_session_get_internal_source (sess));
520
 
      break;
521
 
    case PROP_BANDWIDTH:
522
 
      g_value_set_double (value, rtp_session_get_bandwidth (sess));
523
 
      break;
524
 
    case PROP_RTCP_FRACTION:
525
 
      g_value_set_double (value, rtp_session_get_rtcp_fraction (sess));
526
 
      break;
527
 
    case PROP_RTCP_MTU:
528
 
      g_value_set_uint (value, sess->mtu);
529
 
      break;
530
 
    case PROP_SDES_CNAME:
531
 
      g_value_take_string (value, rtp_session_get_sdes_string (sess,
532
 
              GST_RTCP_SDES_CNAME));
533
 
      break;
534
 
    case PROP_SDES_NAME:
535
 
      g_value_take_string (value, rtp_session_get_sdes_string (sess,
536
 
              GST_RTCP_SDES_NAME));
537
 
      break;
538
 
    case PROP_SDES_EMAIL:
539
 
      g_value_take_string (value, rtp_session_get_sdes_string (sess,
540
 
              GST_RTCP_SDES_EMAIL));
541
 
      break;
542
 
    case PROP_SDES_PHONE:
543
 
      g_value_take_string (value, rtp_session_get_sdes_string (sess,
544
 
              GST_RTCP_SDES_PHONE));
545
 
      break;
546
 
    case PROP_SDES_LOCATION:
547
 
      g_value_take_string (value, rtp_session_get_sdes_string (sess,
548
 
              GST_RTCP_SDES_LOC));
549
 
      break;
550
 
    case PROP_SDES_TOOL:
551
 
      g_value_take_string (value, rtp_session_get_sdes_string (sess,
552
 
              GST_RTCP_SDES_TOOL));
553
 
      break;
554
 
    case PROP_SDES_NOTE:
555
 
      g_value_take_string (value, rtp_session_get_sdes_string (sess,
556
 
              GST_RTCP_SDES_NOTE));
557
 
      break;
558
 
    case PROP_NUM_SOURCES:
559
 
      g_value_set_uint (value, rtp_session_get_num_sources (sess));
560
 
      break;
561
 
    case PROP_NUM_ACTIVE_SOURCES:
562
 
      g_value_set_uint (value, rtp_session_get_num_active_sources (sess));
563
 
      break;
564
 
    case PROP_SOURCES:
565
 
      g_value_take_boxed (value, rtp_session_create_sources (sess));
566
 
      break;
567
 
    default:
568
 
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
569
 
      break;
570
 
  }
571
 
}
572
 
 
573
 
static void
574
 
on_new_ssrc (RTPSession * sess, RTPSource * source)
575
 
{
576
 
  g_object_ref (source);
577
 
  RTP_SESSION_UNLOCK (sess);
578
 
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_NEW_SSRC], 0, source);
579
 
  RTP_SESSION_LOCK (sess);
580
 
  g_object_unref (source);
581
 
}
582
 
 
583
 
static void
584
 
on_ssrc_collision (RTPSession * sess, RTPSource * source)
585
 
{
586
 
  g_object_ref (source);
587
 
  RTP_SESSION_UNLOCK (sess);
588
 
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_COLLISION], 0,
589
 
      source);
590
 
  RTP_SESSION_LOCK (sess);
591
 
  g_object_unref (source);
592
 
}
593
 
 
594
 
static void
595
 
on_ssrc_validated (RTPSession * sess, RTPSource * source)
596
 
{
597
 
  g_object_ref (source);
598
 
  RTP_SESSION_UNLOCK (sess);
599
 
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED], 0,
600
 
      source);
601
 
  RTP_SESSION_LOCK (sess);
602
 
  g_object_unref (source);
603
 
}
604
 
 
605
 
static void
606
 
on_ssrc_active (RTPSession * sess, RTPSource * source)
607
 
{
608
 
  g_object_ref (source);
609
 
  RTP_SESSION_UNLOCK (sess);
610
 
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE], 0, source);
611
 
  RTP_SESSION_LOCK (sess);
612
 
  g_object_unref (source);
613
 
}
614
 
 
615
 
static void
616
 
on_ssrc_sdes (RTPSession * sess, RTPSource * source)
617
 
{
618
 
  g_object_ref (source);
619
 
  GST_DEBUG ("SDES changed for SSRC %08x", source->ssrc);
620
 
  RTP_SESSION_UNLOCK (sess);
621
 
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_SDES], 0, source);
622
 
  RTP_SESSION_LOCK (sess);
623
 
  g_object_unref (source);
624
 
}
625
 
 
626
 
static void
627
 
on_bye_ssrc (RTPSession * sess, RTPSource * source)
628
 
{
629
 
  g_object_ref (source);
630
 
  RTP_SESSION_UNLOCK (sess);
631
 
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_SSRC], 0, source);
632
 
  RTP_SESSION_LOCK (sess);
633
 
  g_object_unref (source);
634
 
}
635
 
 
636
 
static void
637
 
on_bye_timeout (RTPSession * sess, RTPSource * source)
638
 
{
639
 
  g_object_ref (source);
640
 
  RTP_SESSION_UNLOCK (sess);
641
 
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT], 0, source);
642
 
  RTP_SESSION_LOCK (sess);
643
 
  g_object_unref (source);
644
 
}
645
 
 
646
 
static void
647
 
on_timeout (RTPSession * sess, RTPSource * source)
648
 
{
649
 
  g_object_ref (source);
650
 
  RTP_SESSION_UNLOCK (sess);
651
 
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_TIMEOUT], 0, source);
652
 
  RTP_SESSION_LOCK (sess);
653
 
  g_object_unref (source);
654
 
}
655
 
 
656
 
static void
657
 
on_sender_timeout (RTPSession * sess, RTPSource * source)
658
 
{
659
 
  g_object_ref (source);
660
 
  RTP_SESSION_UNLOCK (sess);
661
 
  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
662
 
      source);
663
 
  RTP_SESSION_LOCK (sess);
664
 
  g_object_unref (source);
665
 
}
666
 
 
667
 
/**
668
 
 * rtp_session_new:
669
 
 *
670
 
 * Create a new session object.
671
 
 *
672
 
 * Returns: a new #RTPSession. g_object_unref() after usage.
673
 
 */
674
 
RTPSession *
675
 
rtp_session_new (void)
676
 
{
677
 
  RTPSession *sess;
678
 
 
679
 
  sess = g_object_new (RTP_TYPE_SESSION, NULL);
680
 
 
681
 
  return sess;
682
 
}
683
 
 
684
 
/**
685
 
 * rtp_session_set_callbacks:
686
 
 * @sess: an #RTPSession
687
 
 * @callbacks: callbacks to configure
688
 
 * @user_data: user data passed in the callbacks
689
 
 *
690
 
 * Configure a set of callbacks to be notified of actions.
691
 
 */
692
 
void
693
 
rtp_session_set_callbacks (RTPSession * sess, RTPSessionCallbacks * callbacks,
694
 
    gpointer user_data)
695
 
{
696
 
  g_return_if_fail (RTP_IS_SESSION (sess));
697
 
 
698
 
  if (callbacks->process_rtp) {
699
 
    sess->callbacks.process_rtp = callbacks->process_rtp;
700
 
    sess->process_rtp_user_data = user_data;
701
 
  }
702
 
  if (callbacks->send_rtp) {
703
 
    sess->callbacks.send_rtp = callbacks->send_rtp;
704
 
    sess->send_rtp_user_data = user_data;
705
 
  }
706
 
  if (callbacks->send_rtcp) {
707
 
    sess->callbacks.send_rtcp = callbacks->send_rtcp;
708
 
    sess->send_rtcp_user_data = user_data;
709
 
  }
710
 
  if (callbacks->sync_rtcp) {
711
 
    sess->callbacks.sync_rtcp = callbacks->sync_rtcp;
712
 
    sess->sync_rtcp_user_data = user_data;
713
 
  }
714
 
  if (callbacks->clock_rate) {
715
 
    sess->callbacks.clock_rate = callbacks->clock_rate;
716
 
    sess->clock_rate_user_data = user_data;
717
 
  }
718
 
  if (callbacks->reconsider) {
719
 
    sess->callbacks.reconsider = callbacks->reconsider;
720
 
    sess->reconsider_user_data = user_data;
721
 
  }
722
 
}
723
 
 
724
 
/**
725
 
 * rtp_session_set_process_rtp_callback:
726
 
 * @sess: an #RTPSession
727
 
 * @callback: callback to set
728
 
 * @user_data: user data passed in the callback
729
 
 *
730
 
 * Configure only the process_rtp callback to be notified of the process_rtp action.
731
 
 */
732
 
void
733
 
rtp_session_set_process_rtp_callback (RTPSession * sess,
734
 
    RTPSessionProcessRTP callback, gpointer user_data)
735
 
{
736
 
  g_return_if_fail (RTP_IS_SESSION (sess));
737
 
 
738
 
  sess->callbacks.process_rtp = callback;
739
 
  sess->process_rtp_user_data = user_data;
740
 
}
741
 
 
742
 
/**
743
 
 * rtp_session_set_send_rtp_callback:
744
 
 * @sess: an #RTPSession
745
 
 * @callback: callback to set
746
 
 * @user_data: user data passed in the callback
747
 
 *
748
 
 * Configure only the send_rtp callback to be notified of the send_rtp action.
749
 
 */
750
 
void
751
 
rtp_session_set_send_rtp_callback (RTPSession * sess,
752
 
    RTPSessionSendRTP callback, gpointer user_data)
753
 
{
754
 
  g_return_if_fail (RTP_IS_SESSION (sess));
755
 
 
756
 
  sess->callbacks.send_rtp = callback;
757
 
  sess->send_rtp_user_data = user_data;
758
 
}
759
 
 
760
 
/**
761
 
 * rtp_session_set_send_rtcp_callback:
762
 
 * @sess: an #RTPSession
763
 
 * @callback: callback to set
764
 
 * @user_data: user data passed in the callback
765
 
 *
766
 
 * Configure only the send_rtcp callback to be notified of the send_rtcp action.
767
 
 */
768
 
void
769
 
rtp_session_set_send_rtcp_callback (RTPSession * sess,
770
 
    RTPSessionSendRTCP callback, gpointer user_data)
771
 
{
772
 
  g_return_if_fail (RTP_IS_SESSION (sess));
773
 
 
774
 
  sess->callbacks.send_rtcp = callback;
775
 
  sess->send_rtcp_user_data = user_data;
776
 
}
777
 
 
778
 
/**
779
 
 * rtp_session_set_sync_rtcp_callback:
780
 
 * @sess: an #RTPSession
781
 
 * @callback: callback to set
782
 
 * @user_data: user data passed in the callback
783
 
 *
784
 
 * Configure only the sync_rtcp callback to be notified of the sync_rtcp action.
785
 
 */
786
 
void
787
 
rtp_session_set_sync_rtcp_callback (RTPSession * sess,
788
 
    RTPSessionSyncRTCP callback, gpointer user_data)
789
 
{
790
 
  g_return_if_fail (RTP_IS_SESSION (sess));
791
 
 
792
 
  sess->callbacks.sync_rtcp = callback;
793
 
  sess->sync_rtcp_user_data = user_data;
794
 
}
795
 
 
796
 
/**
797
 
 * rtp_session_set_clock_rate_callback:
798
 
 * @sess: an #RTPSession
799
 
 * @callback: callback to set
800
 
 * @user_data: user data passed in the callback
801
 
 *
802
 
 * Configure only the clock_rate callback to be notified of the clock_rate action.
803
 
 */
804
 
void
805
 
rtp_session_set_clock_rate_callback (RTPSession * sess,
806
 
    RTPSessionClockRate callback, gpointer user_data)
807
 
{
808
 
  g_return_if_fail (RTP_IS_SESSION (sess));
809
 
 
810
 
  sess->callbacks.clock_rate = callback;
811
 
  sess->clock_rate_user_data = user_data;
812
 
}
813
 
 
814
 
/**
815
 
 * rtp_session_set_reconsider_callback:
816
 
 * @sess: an #RTPSession
817
 
 * @callback: callback to set
818
 
 * @user_data: user data passed in the callback
819
 
 *
820
 
 * Configure only the reconsider callback to be notified of the reconsider action.
821
 
 */
822
 
void
823
 
rtp_session_set_reconsider_callback (RTPSession * sess,
824
 
    RTPSessionReconsider callback, gpointer user_data)
825
 
{
826
 
  g_return_if_fail (RTP_IS_SESSION (sess));
827
 
 
828
 
  sess->callbacks.reconsider = callback;
829
 
  sess->reconsider_user_data = user_data;
830
 
}
831
 
 
832
 
/**
833
 
 * rtp_session_set_bandwidth:
834
 
 * @sess: an #RTPSession
835
 
 * @bandwidth: the bandwidth allocated
836
 
 *
837
 
 * Set the session bandwidth in bytes per second.
838
 
 */
839
 
void
840
 
rtp_session_set_bandwidth (RTPSession * sess, gdouble bandwidth)
841
 
{
842
 
  g_return_if_fail (RTP_IS_SESSION (sess));
843
 
 
844
 
  RTP_SESSION_LOCK (sess);
845
 
  sess->stats.bandwidth = bandwidth;
846
 
  RTP_SESSION_UNLOCK (sess);
847
 
}
848
 
 
849
 
/**
850
 
 * rtp_session_get_bandwidth:
851
 
 * @sess: an #RTPSession
852
 
 *
853
 
 * Get the session bandwidth.
854
 
 *
855
 
 * Returns: the session bandwidth.
856
 
 */
857
 
gdouble
858
 
rtp_session_get_bandwidth (RTPSession * sess)
859
 
{
860
 
  gdouble result;
861
 
 
862
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), 0);
863
 
 
864
 
  RTP_SESSION_LOCK (sess);
865
 
  result = sess->stats.bandwidth;
866
 
  RTP_SESSION_UNLOCK (sess);
867
 
 
868
 
  return result;
869
 
}
870
 
 
871
 
/**
872
 
 * rtp_session_set_rtcp_fraction:
873
 
 * @sess: an #RTPSession
874
 
 * @bandwidth: the RTCP bandwidth
875
 
 *
876
 
 * Set the bandwidth that should be used for RTCP
877
 
 * messages.
878
 
 */
879
 
void
880
 
rtp_session_set_rtcp_fraction (RTPSession * sess, gdouble bandwidth)
881
 
{
882
 
  g_return_if_fail (RTP_IS_SESSION (sess));
883
 
 
884
 
  RTP_SESSION_LOCK (sess);
885
 
  sess->stats.rtcp_bandwidth = bandwidth;
886
 
  RTP_SESSION_UNLOCK (sess);
887
 
}
888
 
 
889
 
/**
890
 
 * rtp_session_get_rtcp_fraction:
891
 
 * @sess: an #RTPSession
892
 
 *
893
 
 * Get the session bandwidth used for RTCP.
894
 
 *
895
 
 * Returns: The bandwidth used for RTCP messages.
896
 
 */
897
 
gdouble
898
 
rtp_session_get_rtcp_fraction (RTPSession * sess)
899
 
{
900
 
  gdouble result;
901
 
 
902
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), 0.0);
903
 
 
904
 
  RTP_SESSION_LOCK (sess);
905
 
  result = sess->stats.rtcp_bandwidth;
906
 
  RTP_SESSION_UNLOCK (sess);
907
 
 
908
 
  return result;
909
 
}
910
 
 
911
 
/**
912
 
 * rtp_session_set_sdes_string:
913
 
 * @sess: an #RTPSession
914
 
 * @type: the type of the SDES item
915
 
 * @item: a null-terminated string to set. 
916
 
 *
917
 
 * Store an SDES item of @type in @sess. 
918
 
 *
919
 
 * Returns: %FALSE if the data was unchanged @type is invalid.
920
 
 */
921
 
gboolean
922
 
rtp_session_set_sdes_string (RTPSession * sess, GstRTCPSDESType type,
923
 
    const gchar * item)
924
 
{
925
 
  gboolean result;
926
 
 
927
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE);
928
 
 
929
 
  RTP_SESSION_LOCK (sess);
930
 
  result = rtp_source_set_sdes_string (sess->source, type, item);
931
 
  RTP_SESSION_UNLOCK (sess);
932
 
 
933
 
  return result;
934
 
}
935
 
 
936
 
/**
937
 
 * rtp_session_get_sdes_string:
938
 
 * @sess: an #RTPSession
939
 
 * @type: the type of the SDES item
940
 
 *
941
 
 * Get the SDES item of @type from @sess. 
942
 
 *
943
 
 * Returns: a null-terminated copy of the SDES item or NULL when @type was not
944
 
 * valid. g_free() after usage.
945
 
 */
946
 
gchar *
947
 
rtp_session_get_sdes_string (RTPSession * sess, GstRTCPSDESType type)
948
 
{
949
 
  gchar *result;
950
 
 
951
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
952
 
 
953
 
  RTP_SESSION_LOCK (sess);
954
 
  result = rtp_source_get_sdes_string (sess->source, type);
955
 
  RTP_SESSION_UNLOCK (sess);
956
 
 
957
 
  return result;
958
 
}
959
 
 
960
 
static GstFlowReturn
961
 
source_push_rtp (RTPSource * source, GstBuffer * buffer, RTPSession * session)
962
 
{
963
 
  GstFlowReturn result = GST_FLOW_OK;
964
 
 
965
 
  if (source == session->source) {
966
 
    GST_LOG ("source %08x pushed sender RTP packet", source->ssrc);
967
 
 
968
 
    RTP_SESSION_UNLOCK (session);
969
 
 
970
 
    if (session->callbacks.send_rtp)
971
 
      result =
972
 
          session->callbacks.send_rtp (session, source, buffer,
973
 
          session->send_rtp_user_data);
974
 
    else
975
 
      gst_buffer_unref (buffer);
976
 
 
977
 
  } else {
978
 
    GST_LOG ("source %08x pushed receiver RTP packet", source->ssrc);
979
 
    RTP_SESSION_UNLOCK (session);
980
 
 
981
 
    if (session->callbacks.process_rtp)
982
 
      result =
983
 
          session->callbacks.process_rtp (session, source, buffer,
984
 
          session->process_rtp_user_data);
985
 
    else
986
 
      gst_buffer_unref (buffer);
987
 
  }
988
 
  RTP_SESSION_LOCK (session);
989
 
 
990
 
  return result;
991
 
}
992
 
 
993
 
static gint
994
 
source_clock_rate (RTPSource * source, guint8 pt, RTPSession * session)
995
 
{
996
 
  gint result;
997
 
 
998
 
  RTP_SESSION_UNLOCK (session);
999
 
 
1000
 
  if (session->callbacks.clock_rate)
1001
 
    result =
1002
 
        session->callbacks.clock_rate (session, pt,
1003
 
        session->clock_rate_user_data);
1004
 
  else
1005
 
    result = -1;
1006
 
 
1007
 
  RTP_SESSION_LOCK (session);
1008
 
 
1009
 
  GST_DEBUG ("got clock-rate %d for pt %d", result, pt);
1010
 
 
1011
 
  return result;
1012
 
}
1013
 
 
1014
 
static RTPSourceCallbacks callbacks = {
1015
 
  (RTPSourcePushRTP) source_push_rtp,
1016
 
  (RTPSourceClockRate) source_clock_rate,
1017
 
};
1018
 
 
1019
 
/**
1020
 
 * find_add_conflicting_addresses:
1021
 
 * @sess: The session to check in
1022
 
 * @arrival: The arrival stats for the buffer
1023
 
 *
1024
 
 * Checks if an address which has a conflict is already known,
1025
 
 *  otherwise remembers it to prevent loops.
1026
 
 *
1027
 
 * Returns: TRUE if it was a known conflict, FALSE otherwise
1028
 
 */
1029
 
 
1030
 
static gboolean
1031
 
find_add_conflicting_addresses (RTPSession * sess, RTPArrivalStats * arrival)
1032
 
{
1033
 
  GList *item;
1034
 
  RTPConflictingAddress *new_conflict;
1035
 
 
1036
 
  for (item = g_list_first (sess->conflicting_addresses);
1037
 
      item; item = g_list_next (item)) {
1038
 
    RTPConflictingAddress *known_conflict = item->data;
1039
 
 
1040
 
    if (gst_netaddress_equal (&arrival->address, &known_conflict->address)) {
1041
 
      known_conflict->time = arrival->time;
1042
 
      return TRUE;
1043
 
    }
1044
 
  }
1045
 
 
1046
 
  new_conflict = g_new0 (RTPConflictingAddress, 1);
1047
 
 
1048
 
  memcpy (&new_conflict->address, &arrival->address, sizeof (GstNetAddress));
1049
 
  new_conflict->time = arrival->time;
1050
 
 
1051
 
  sess->conflicting_addresses = g_list_prepend (sess->conflicting_addresses,
1052
 
      new_conflict);
1053
 
 
1054
 
  return FALSE;
1055
 
}
1056
 
 
1057
 
static gboolean
1058
 
check_collision (RTPSession * sess, RTPSource * source,
1059
 
    RTPArrivalStats * arrival, gboolean rtp)
1060
 
{
1061
 
  /* If we have no arrival address, we can't do collision checking */
1062
 
  if (!arrival->have_address)
1063
 
    return FALSE;
1064
 
 
1065
 
  if (sess->source != source) {
1066
 
    /* This is not our local source, but lets check if two remote
1067
 
     * source collide
1068
 
     */
1069
 
    if (rtp) {
1070
 
      if (source->have_rtp_from) {
1071
 
        if (gst_netaddress_equal (&source->rtp_from, &arrival->address))
1072
 
          /* Address is the same */
1073
 
          return FALSE;
1074
 
      } else {
1075
 
        /* We don't already have a from address for RTP, just set it */
1076
 
        rtp_source_set_rtp_from (source, &arrival->address);
1077
 
        return FALSE;
1078
 
      }
1079
 
    } else {
1080
 
      if (source->have_rtcp_from) {
1081
 
        if (gst_netaddress_equal (&source->rtcp_from, &arrival->address))
1082
 
          /* Address is the same */
1083
 
          return FALSE;
1084
 
      } else {
1085
 
        /* We don't already have a from address for RTCP, just set it */
1086
 
        rtp_source_set_rtcp_from (source, &arrival->address);
1087
 
        return FALSE;
1088
 
      }
1089
 
    }
1090
 
    /* We received RTP or RTCP from this source before but the network address
1091
 
     * changed. In this case, we have third-party collision or loop */
1092
 
    GST_DEBUG ("we have a third-party collision or loop");
1093
 
 
1094
 
    /* FIXME: Log 3rd party collision somehow
1095
 
     * Maybe should be done in upper layer, only the SDES can tell us
1096
 
     * if its a collision or a loop
1097
 
     */
1098
 
  } else {
1099
 
    /* This is sending with our ssrc, is it an address we already know */
1100
 
 
1101
 
    if (find_add_conflicting_addresses (sess, arrival)) {
1102
 
      /* Its a known conflict, its probably a loop, not a collision
1103
 
       * lets just drop the incoming packet
1104
 
       */
1105
 
      GST_DEBUG ("Our packets are being looped back to us, dropping");
1106
 
    } else {
1107
 
      /* Its a new collision, lets change our SSRC */
1108
 
 
1109
 
      GST_DEBUG ("Collision for SSRC %x", rtp_source_get_ssrc (source));
1110
 
      on_ssrc_collision (sess, source);
1111
 
 
1112
 
      rtp_session_schedule_bye_locked (sess, "SSRC Collision", arrival->time);
1113
 
 
1114
 
      sess->change_ssrc = TRUE;
1115
 
    }
1116
 
  }
1117
 
 
1118
 
  return TRUE;
1119
 
}
1120
 
 
1121
 
 
1122
 
/* must be called with the session lock, the returned source needs to be
1123
 
 * unreffed after usage. */
1124
 
static RTPSource *
1125
 
obtain_source (RTPSession * sess, guint32 ssrc, gboolean * created,
1126
 
    RTPArrivalStats * arrival, gboolean rtp)
1127
 
{
1128
 
  RTPSource *source;
1129
 
 
1130
 
  source =
1131
 
      g_hash_table_lookup (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc));
1132
 
  if (source == NULL) {
1133
 
    /* make new Source in probation and insert */
1134
 
    source = rtp_source_new (ssrc);
1135
 
 
1136
 
    /* for RTP packets we need to set the source in probation. Receiving RTCP
1137
 
     * packets of an SSRC, on the other hand, is a strong indication that we
1138
 
     * are dealing with a valid source. */
1139
 
    if (rtp)
1140
 
      source->probation = RTP_DEFAULT_PROBATION;
1141
 
    else
1142
 
      source->probation = 0;
1143
 
 
1144
 
    /* store from address, if any */
1145
 
    if (arrival->have_address) {
1146
 
      if (rtp)
1147
 
        rtp_source_set_rtp_from (source, &arrival->address);
1148
 
      else
1149
 
        rtp_source_set_rtcp_from (source, &arrival->address);
1150
 
    }
1151
 
 
1152
 
    /* configure a callback on the source */
1153
 
    rtp_source_set_callbacks (source, &callbacks, sess);
1154
 
 
1155
 
    g_hash_table_insert (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc),
1156
 
        source);
1157
 
 
1158
 
    /* we have one more source now */
1159
 
    sess->total_sources++;
1160
 
    *created = TRUE;
1161
 
  } else {
1162
 
    *created = FALSE;
1163
 
    /* check for collision, this updates the address when not previously set */
1164
 
    if (check_collision (sess, source, arrival, rtp)) {
1165
 
      return NULL;
1166
 
    }
1167
 
  }
1168
 
  /* update last activity */
1169
 
  source->last_activity = arrival->time;
1170
 
  if (rtp)
1171
 
    source->last_rtp_activity = arrival->time;
1172
 
  g_object_ref (source);
1173
 
 
1174
 
  return source;
1175
 
}
1176
 
 
1177
 
/**
1178
 
 * rtp_session_get_internal_source:
1179
 
 * @sess: a #RTPSession
1180
 
 *
1181
 
 * Get the internal #RTPSource of @sess.
1182
 
 *
1183
 
 * Returns: The internal #RTPSource. g_object_unref() after usage.
1184
 
 */
1185
 
RTPSource *
1186
 
rtp_session_get_internal_source (RTPSession * sess)
1187
 
{
1188
 
  RTPSource *result;
1189
 
 
1190
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
1191
 
 
1192
 
  result = g_object_ref (sess->source);
1193
 
 
1194
 
  return result;
1195
 
}
1196
 
 
1197
 
/**
1198
 
 * rtp_session_set_internal_ssrc:
1199
 
 * @sess: a #RTPSession
1200
 
 * @ssrc: an SSRC
1201
 
 *
1202
 
 * Set the SSRC of @sess to @ssrc.
1203
 
 */
1204
 
void
1205
 
rtp_session_set_internal_ssrc (RTPSession * sess, guint32 ssrc)
1206
 
{
1207
 
  RTP_SESSION_LOCK (sess);
1208
 
  if (ssrc != sess->source->ssrc) {
1209
 
    g_hash_table_steal (sess->ssrcs[sess->mask_idx],
1210
 
        GINT_TO_POINTER (sess->source->ssrc));
1211
 
 
1212
 
    GST_DEBUG ("setting internal SSRC to %08x", ssrc);
1213
 
    /* After this call, any receiver of the old SSRC either in RTP or RTCP
1214
 
     * packets will timeout on the old SSRC, we could potentially schedule a
1215
 
     * BYE RTCP for the old SSRC... */
1216
 
    sess->source->ssrc = ssrc;
1217
 
    rtp_source_reset (sess->source);
1218
 
 
1219
 
    /* rehash with the new SSRC */
1220
 
    g_hash_table_insert (sess->ssrcs[sess->mask_idx],
1221
 
        GINT_TO_POINTER (sess->source->ssrc), sess->source);
1222
 
  }
1223
 
  RTP_SESSION_UNLOCK (sess);
1224
 
 
1225
 
  g_object_notify (G_OBJECT (sess), "internal-ssrc");
1226
 
}
1227
 
 
1228
 
/**
1229
 
 * rtp_session_get_internal_ssrc:
1230
 
 * @sess: a #RTPSession
1231
 
 *
1232
 
 * Get the internal SSRC of @sess.
1233
 
 *
1234
 
 * Returns: The SSRC of the session. 
1235
 
 */
1236
 
guint32
1237
 
rtp_session_get_internal_ssrc (RTPSession * sess)
1238
 
{
1239
 
  guint32 ssrc;
1240
 
 
1241
 
  RTP_SESSION_LOCK (sess);
1242
 
  ssrc = sess->source->ssrc;
1243
 
  RTP_SESSION_UNLOCK (sess);
1244
 
 
1245
 
  return ssrc;
1246
 
}
1247
 
 
1248
 
/**
1249
 
 * rtp_session_add_source:
1250
 
 * @sess: a #RTPSession
1251
 
 * @src: #RTPSource to add
1252
 
 *
1253
 
 * Add @src to @session.
1254
 
 *
1255
 
 * Returns: %TRUE on success, %FALSE if a source with the same SSRC already
1256
 
 * existed in the session.
1257
 
 */
1258
 
gboolean
1259
 
rtp_session_add_source (RTPSession * sess, RTPSource * src)
1260
 
{
1261
 
  gboolean result = FALSE;
1262
 
  RTPSource *find;
1263
 
 
1264
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE);
1265
 
  g_return_val_if_fail (src != NULL, FALSE);
1266
 
 
1267
 
  RTP_SESSION_LOCK (sess);
1268
 
  find =
1269
 
      g_hash_table_lookup (sess->ssrcs[sess->mask_idx],
1270
 
      GINT_TO_POINTER (src->ssrc));
1271
 
  if (find == NULL) {
1272
 
    g_hash_table_insert (sess->ssrcs[sess->mask_idx],
1273
 
        GINT_TO_POINTER (src->ssrc), src);
1274
 
    /* we have one more source now */
1275
 
    sess->total_sources++;
1276
 
    result = TRUE;
1277
 
  }
1278
 
  RTP_SESSION_UNLOCK (sess);
1279
 
 
1280
 
  return result;
1281
 
}
1282
 
 
1283
 
/**
1284
 
 * rtp_session_get_num_sources:
1285
 
 * @sess: an #RTPSession
1286
 
 *
1287
 
 * Get the number of sources in @sess.
1288
 
 *
1289
 
 * Returns: The number of sources in @sess.
1290
 
 */
1291
 
guint
1292
 
rtp_session_get_num_sources (RTPSession * sess)
1293
 
{
1294
 
  guint result;
1295
 
 
1296
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE);
1297
 
 
1298
 
  RTP_SESSION_LOCK (sess);
1299
 
  result = sess->total_sources;
1300
 
  RTP_SESSION_UNLOCK (sess);
1301
 
 
1302
 
  return result;
1303
 
}
1304
 
 
1305
 
/**
1306
 
 * rtp_session_get_num_active_sources:
1307
 
 * @sess: an #RTPSession
1308
 
 *
1309
 
 * Get the number of active sources in @sess. A source is considered active when
1310
 
 * it has been validated and has not yet received a BYE RTCP message.
1311
 
 *
1312
 
 * Returns: The number of active sources in @sess.
1313
 
 */
1314
 
guint
1315
 
rtp_session_get_num_active_sources (RTPSession * sess)
1316
 
{
1317
 
  guint result;
1318
 
 
1319
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), 0);
1320
 
 
1321
 
  RTP_SESSION_LOCK (sess);
1322
 
  result = sess->stats.active_sources;
1323
 
  RTP_SESSION_UNLOCK (sess);
1324
 
 
1325
 
  return result;
1326
 
}
1327
 
 
1328
 
/**
1329
 
 * rtp_session_get_source_by_ssrc:
1330
 
 * @sess: an #RTPSession
1331
 
 * @ssrc: an SSRC
1332
 
 *
1333
 
 * Find the source with @ssrc in @sess.
1334
 
 *
1335
 
 * Returns: a #RTPSource with SSRC @ssrc or NULL if the source was not found.
1336
 
 * g_object_unref() after usage.
1337
 
 */
1338
 
RTPSource *
1339
 
rtp_session_get_source_by_ssrc (RTPSession * sess, guint32 ssrc)
1340
 
{
1341
 
  RTPSource *result;
1342
 
 
1343
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
1344
 
 
1345
 
  RTP_SESSION_LOCK (sess);
1346
 
  result =
1347
 
      g_hash_table_lookup (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc));
1348
 
  if (result)
1349
 
    g_object_ref (result);
1350
 
  RTP_SESSION_UNLOCK (sess);
1351
 
 
1352
 
  return result;
1353
 
}
1354
 
 
1355
 
/**
1356
 
 * rtp_session_get_source_by_cname:
1357
 
 * @sess: a #RTPSession
1358
 
 * @cname: an CNAME
1359
 
 *
1360
 
 * Find the source with @cname in @sess.
1361
 
 *
1362
 
 * Returns: a #RTPSource with CNAME @cname or NULL if the source was not found.
1363
 
 * g_object_unref() after usage.
1364
 
 */
1365
 
RTPSource *
1366
 
rtp_session_get_source_by_cname (RTPSession * sess, const gchar * cname)
1367
 
{
1368
 
  RTPSource *result;
1369
 
 
1370
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
1371
 
  g_return_val_if_fail (cname != NULL, NULL);
1372
 
 
1373
 
  RTP_SESSION_LOCK (sess);
1374
 
  result = g_hash_table_lookup (sess->cnames, cname);
1375
 
  if (result)
1376
 
    g_object_ref (result);
1377
 
  RTP_SESSION_UNLOCK (sess);
1378
 
 
1379
 
  return result;
1380
 
}
1381
 
 
1382
 
static guint32
1383
 
rtp_session_create_new_ssrc (RTPSession * sess)
1384
 
{
1385
 
  guint32 ssrc;
1386
 
 
1387
 
  while (TRUE) {
1388
 
    ssrc = g_random_int ();
1389
 
 
1390
 
    /* see if it exists in the session, we're done if it doesn't */
1391
 
    if (g_hash_table_lookup (sess->ssrcs[sess->mask_idx],
1392
 
            GINT_TO_POINTER (ssrc)) == NULL)
1393
 
      break;
1394
 
  }
1395
 
 
1396
 
  return ssrc;
1397
 
}
1398
 
 
1399
 
 
1400
 
/**
1401
 
 * rtp_session_create_source:
1402
 
 * @sess: an #RTPSession
1403
 
 *
1404
 
 * Create an #RTPSource for use in @sess. This function will create a source
1405
 
 * with an ssrc that is currently not used by any participants in the session.
1406
 
 *
1407
 
 * Returns: an #RTPSource.
1408
 
 */
1409
 
RTPSource *
1410
 
rtp_session_create_source (RTPSession * sess)
1411
 
{
1412
 
  guint32 ssrc;
1413
 
  RTPSource *source;
1414
 
 
1415
 
  RTP_SESSION_LOCK (sess);
1416
 
  ssrc = rtp_session_create_new_ssrc (sess);
1417
 
  source = rtp_source_new (ssrc);
1418
 
  rtp_source_set_callbacks (source, &callbacks, sess);
1419
 
  /* we need an additional ref for the source in the hashtable */
1420
 
  g_object_ref (source);
1421
 
  g_hash_table_insert (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc),
1422
 
      source);
1423
 
  /* we have one more source now */
1424
 
  sess->total_sources++;
1425
 
  RTP_SESSION_UNLOCK (sess);
1426
 
 
1427
 
  return source;
1428
 
}
1429
 
 
1430
 
/* update the RTPArrivalStats structure with the current time and other bits
1431
 
 * about the current buffer we are handling.
1432
 
 * This function is typically called when a validated packet is received.
1433
 
 * This function should be called with the SESSION_LOCK
1434
 
 */
1435
 
static void
1436
 
update_arrival_stats (RTPSession * sess, RTPArrivalStats * arrival,
1437
 
    gboolean rtp, GstBuffer * buffer, GstClockTime current_time,
1438
 
    GstClockTime running_time, guint64 ntpnstime)
1439
 
{
1440
 
  /* get time of arrival */
1441
 
  arrival->time = current_time;
1442
 
  arrival->running_time = running_time;
1443
 
  arrival->ntpnstime = ntpnstime;
1444
 
 
1445
 
  /* get packet size including header overhead */
1446
 
  arrival->bytes = GST_BUFFER_SIZE (buffer) + sess->header_len;
1447
 
 
1448
 
  if (rtp) {
1449
 
    arrival->payload_len = gst_rtp_buffer_get_payload_len (buffer);
1450
 
  } else {
1451
 
    arrival->payload_len = 0;
1452
 
  }
1453
 
 
1454
 
  /* for netbuffer we can store the IP address to check for collisions */
1455
 
  arrival->have_address = GST_IS_NETBUFFER (buffer);
1456
 
  if (arrival->have_address) {
1457
 
    GstNetBuffer *netbuf = (GstNetBuffer *) buffer;
1458
 
 
1459
 
    memcpy (&arrival->address, &netbuf->from, sizeof (GstNetAddress));
1460
 
  }
1461
 
}
1462
 
 
1463
 
/**
1464
 
 * rtp_session_process_rtp:
1465
 
 * @sess: and #RTPSession
1466
 
 * @buffer: an RTP buffer
1467
 
 * @current_time: the current system time
1468
 
 * @ntpnstime: the NTP arrival time in nanoseconds
1469
 
 *
1470
 
 * Process an RTP buffer in the session manager. This function takes ownership
1471
 
 * of @buffer.
1472
 
 *
1473
 
 * Returns: a #GstFlowReturn.
1474
 
 */
1475
 
GstFlowReturn
1476
 
rtp_session_process_rtp (RTPSession * sess, GstBuffer * buffer,
1477
 
    GstClockTime current_time, GstClockTime running_time, guint64 ntpnstime)
1478
 
{
1479
 
  GstFlowReturn result;
1480
 
  guint32 ssrc;
1481
 
  RTPSource *source;
1482
 
  gboolean created;
1483
 
  gboolean prevsender, prevactive;
1484
 
  RTPArrivalStats arrival;
1485
 
 
1486
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
1487
 
  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
1488
 
 
1489
 
  if (!gst_rtp_buffer_validate (buffer))
1490
 
    goto invalid_packet;
1491
 
 
1492
 
  RTP_SESSION_LOCK (sess);
1493
 
  /* update arrival stats */
1494
 
  update_arrival_stats (sess, &arrival, TRUE, buffer, current_time,
1495
 
      running_time, ntpnstime);
1496
 
 
1497
 
  /* ignore more RTP packets when we left the session */
1498
 
  if (sess->source->received_bye)
1499
 
    goto ignore;
1500
 
 
1501
 
  /* get SSRC and look up in session database */
1502
 
  ssrc = gst_rtp_buffer_get_ssrc (buffer);
1503
 
  source = obtain_source (sess, ssrc, &created, &arrival, TRUE);
1504
 
  if (!source)
1505
 
    goto collision;
1506
 
 
1507
 
  prevsender = RTP_SOURCE_IS_SENDER (source);
1508
 
  prevactive = RTP_SOURCE_IS_ACTIVE (source);
1509
 
 
1510
 
  /* we need to ref so that we can process the CSRCs later */
1511
 
  gst_buffer_ref (buffer);
1512
 
 
1513
 
  /* let source process the packet */
1514
 
  result = rtp_source_process_rtp (source, buffer, &arrival);
1515
 
 
1516
 
  /* source became active */
1517
 
  if (prevactive != RTP_SOURCE_IS_ACTIVE (source)) {
1518
 
    sess->stats.active_sources++;
1519
 
    GST_DEBUG ("source: %08x became active, %d active sources", ssrc,
1520
 
        sess->stats.active_sources);
1521
 
    on_ssrc_validated (sess, source);
1522
 
  }
1523
 
  if (prevsender != RTP_SOURCE_IS_SENDER (source)) {
1524
 
    sess->stats.sender_sources++;
1525
 
    GST_DEBUG ("source: %08x became sender, %d sender sources", ssrc,
1526
 
        sess->stats.sender_sources);
1527
 
  }
1528
 
 
1529
 
  if (created)
1530
 
    on_new_ssrc (sess, source);
1531
 
 
1532
 
  if (source->validated) {
1533
 
    guint8 i, count;
1534
 
    gboolean created;
1535
 
 
1536
 
    /* for validated sources, we add the CSRCs as well */
1537
 
    count = gst_rtp_buffer_get_csrc_count (buffer);
1538
 
 
1539
 
    for (i = 0; i < count; i++) {
1540
 
      guint32 csrc;
1541
 
      RTPSource *csrc_src;
1542
 
 
1543
 
      csrc = gst_rtp_buffer_get_csrc (buffer, i);
1544
 
 
1545
 
      /* get source */
1546
 
      csrc_src = obtain_source (sess, csrc, &created, &arrival, TRUE);
1547
 
      if (!csrc_src)
1548
 
        continue;
1549
 
 
1550
 
      if (created) {
1551
 
        GST_DEBUG ("created new CSRC: %08x", csrc);
1552
 
        rtp_source_set_as_csrc (csrc_src);
1553
 
        if (RTP_SOURCE_IS_ACTIVE (csrc_src))
1554
 
          sess->stats.active_sources++;
1555
 
        on_new_ssrc (sess, csrc_src);
1556
 
      }
1557
 
      g_object_unref (csrc_src);
1558
 
    }
1559
 
  }
1560
 
  g_object_unref (source);
1561
 
  gst_buffer_unref (buffer);
1562
 
 
1563
 
  RTP_SESSION_UNLOCK (sess);
1564
 
 
1565
 
  return result;
1566
 
 
1567
 
  /* ERRORS */
1568
 
invalid_packet:
1569
 
  {
1570
 
    gst_buffer_unref (buffer);
1571
 
    GST_DEBUG ("invalid RTP packet received");
1572
 
    return GST_FLOW_OK;
1573
 
  }
1574
 
ignore:
1575
 
  {
1576
 
    gst_buffer_unref (buffer);
1577
 
    RTP_SESSION_UNLOCK (sess);
1578
 
    GST_DEBUG ("ignoring RTP packet because we are leaving");
1579
 
    return GST_FLOW_OK;
1580
 
  }
1581
 
collision:
1582
 
  {
1583
 
    gst_buffer_unref (buffer);
1584
 
    RTP_SESSION_UNLOCK (sess);
1585
 
    GST_DEBUG ("ignoring packet because its collisioning");
1586
 
    return GST_FLOW_OK;
1587
 
  }
1588
 
}
1589
 
 
1590
 
static void
1591
 
rtp_session_process_rb (RTPSession * sess, RTPSource * source,
1592
 
    GstRTCPPacket * packet, RTPArrivalStats * arrival)
1593
 
{
1594
 
  guint count, i;
1595
 
 
1596
 
  count = gst_rtcp_packet_get_rb_count (packet);
1597
 
  for (i = 0; i < count; i++) {
1598
 
    guint32 ssrc, exthighestseq, jitter, lsr, dlsr;
1599
 
    guint8 fractionlost;
1600
 
    gint32 packetslost;
1601
 
 
1602
 
    gst_rtcp_packet_get_rb (packet, i, &ssrc, &fractionlost,
1603
 
        &packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
1604
 
 
1605
 
    GST_DEBUG ("RB %d: SSRC %08x, jitter %" G_GUINT32_FORMAT, i, ssrc, jitter);
1606
 
 
1607
 
    if (ssrc == sess->source->ssrc) {
1608
 
      /* only deal with report blocks for our session, we update the stats of
1609
 
       * the sender of the RTCP message. We could also compare our stats against
1610
 
       * the other sender to see if we are better or worse. */
1611
 
      rtp_source_process_rb (source, arrival->time, fractionlost, packetslost,
1612
 
          exthighestseq, jitter, lsr, dlsr);
1613
 
 
1614
 
      on_ssrc_active (sess, source);
1615
 
    }
1616
 
  }
1617
 
}
1618
 
 
1619
 
/* A Sender report contains statistics about how the sender is doing. This
1620
 
 * includes timing informataion such as the relation between RTP and NTP
1621
 
 * timestamps and the number of packets/bytes it sent to us.
1622
 
 *
1623
 
 * In this report is also included a set of report blocks related to how this
1624
 
 * sender is receiving data (in case we (or somebody else) is also sending stuff
1625
 
 * to it). This info includes the packet loss, jitter and seqnum. It also
1626
 
 * contains information to calculate the round trip time (LSR/DLSR).
1627
 
 */
1628
 
static void
1629
 
rtp_session_process_sr (RTPSession * sess, GstRTCPPacket * packet,
1630
 
    RTPArrivalStats * arrival)
1631
 
{
1632
 
  guint32 senderssrc, rtptime, packet_count, octet_count;
1633
 
  guint64 ntptime;
1634
 
  RTPSource *source;
1635
 
  gboolean created, prevsender;
1636
 
 
1637
 
  gst_rtcp_packet_sr_get_sender_info (packet, &senderssrc, &ntptime, &rtptime,
1638
 
      &packet_count, &octet_count);
1639
 
 
1640
 
  GST_DEBUG ("got SR packet: SSRC %08x, time %" GST_TIME_FORMAT,
1641
 
      senderssrc, GST_TIME_ARGS (arrival->time));
1642
 
 
1643
 
  source = obtain_source (sess, senderssrc, &created, arrival, FALSE);
1644
 
  if (!source)
1645
 
    return;
1646
 
 
1647
 
  prevsender = RTP_SOURCE_IS_SENDER (source);
1648
 
 
1649
 
  /* first update the source */
1650
 
  rtp_source_process_sr (source, arrival->time, ntptime, rtptime, packet_count,
1651
 
      octet_count);
1652
 
 
1653
 
  if (prevsender != RTP_SOURCE_IS_SENDER (source)) {
1654
 
    sess->stats.sender_sources++;
1655
 
    GST_DEBUG ("source: %08x became sender, %d sender sources", senderssrc,
1656
 
        sess->stats.sender_sources);
1657
 
  }
1658
 
 
1659
 
  if (created)
1660
 
    on_new_ssrc (sess, source);
1661
 
 
1662
 
  rtp_session_process_rb (sess, source, packet, arrival);
1663
 
  g_object_unref (source);
1664
 
}
1665
 
 
1666
 
/* A receiver report contains statistics about how a receiver is doing. It
1667
 
 * includes stuff like packet loss, jitter and the seqnum it received last. It
1668
 
 * also contains info to calculate the round trip time.
1669
 
 *
1670
 
 * We are only interested in how the sender of this report is doing wrt to us.
1671
 
 */
1672
 
static void
1673
 
rtp_session_process_rr (RTPSession * sess, GstRTCPPacket * packet,
1674
 
    RTPArrivalStats * arrival)
1675
 
{
1676
 
  guint32 senderssrc;
1677
 
  RTPSource *source;
1678
 
  gboolean created;
1679
 
 
1680
 
  senderssrc = gst_rtcp_packet_rr_get_ssrc (packet);
1681
 
 
1682
 
  GST_DEBUG ("got RR packet: SSRC %08x", senderssrc);
1683
 
 
1684
 
  source = obtain_source (sess, senderssrc, &created, arrival, FALSE);
1685
 
  if (!source)
1686
 
    return;
1687
 
 
1688
 
  if (created)
1689
 
    on_new_ssrc (sess, source);
1690
 
 
1691
 
  rtp_session_process_rb (sess, source, packet, arrival);
1692
 
  g_object_unref (source);
1693
 
}
1694
 
 
1695
 
/* Get SDES items and store them in the SSRC */
1696
 
static void
1697
 
rtp_session_process_sdes (RTPSession * sess, GstRTCPPacket * packet,
1698
 
    RTPArrivalStats * arrival)
1699
 
{
1700
 
  guint items, i, j;
1701
 
  gboolean more_items, more_entries;
1702
 
 
1703
 
  items = gst_rtcp_packet_sdes_get_item_count (packet);
1704
 
  GST_DEBUG ("got SDES packet with %d items", items);
1705
 
 
1706
 
  more_items = gst_rtcp_packet_sdes_first_item (packet);
1707
 
  i = 0;
1708
 
  while (more_items) {
1709
 
    guint32 ssrc;
1710
 
    gboolean changed, created;
1711
 
    RTPSource *source;
1712
 
 
1713
 
    ssrc = gst_rtcp_packet_sdes_get_ssrc (packet);
1714
 
 
1715
 
    GST_DEBUG ("item %d, SSRC %08x", i, ssrc);
1716
 
 
1717
 
    changed = FALSE;
1718
 
 
1719
 
    /* find src, no probation when dealing with RTCP */
1720
 
    source = obtain_source (sess, ssrc, &created, arrival, FALSE);
1721
 
    if (!source)
1722
 
      return;
1723
 
 
1724
 
    more_entries = gst_rtcp_packet_sdes_first_entry (packet);
1725
 
    j = 0;
1726
 
    while (more_entries) {
1727
 
      GstRTCPSDESType type;
1728
 
      guint8 len;
1729
 
      guint8 *data;
1730
 
 
1731
 
      gst_rtcp_packet_sdes_get_entry (packet, &type, &len, &data);
1732
 
 
1733
 
      GST_DEBUG ("entry %d, type %d, len %d, data %.*s", j, type, len, len,
1734
 
          data);
1735
 
 
1736
 
      changed |= rtp_source_set_sdes (source, type, data, len);
1737
 
 
1738
 
      more_entries = gst_rtcp_packet_sdes_next_entry (packet);
1739
 
      j++;
1740
 
    }
1741
 
 
1742
 
    source->validated = TRUE;
1743
 
 
1744
 
    if (created)
1745
 
      on_new_ssrc (sess, source);
1746
 
    if (changed)
1747
 
      on_ssrc_sdes (sess, source);
1748
 
 
1749
 
    g_object_unref (source);
1750
 
 
1751
 
    more_items = gst_rtcp_packet_sdes_next_item (packet);
1752
 
    i++;
1753
 
  }
1754
 
}
1755
 
 
1756
 
/* BYE is sent when a client leaves the session
1757
 
 */
1758
 
static void
1759
 
rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet,
1760
 
    RTPArrivalStats * arrival)
1761
 
{
1762
 
  guint count, i;
1763
 
  gchar *reason;
1764
 
 
1765
 
  reason = gst_rtcp_packet_bye_get_reason (packet);
1766
 
  GST_DEBUG ("got BYE packet (reason: %s)", GST_STR_NULL (reason));
1767
 
 
1768
 
  count = gst_rtcp_packet_bye_get_ssrc_count (packet);
1769
 
  for (i = 0; i < count; i++) {
1770
 
    guint32 ssrc;
1771
 
    RTPSource *source;
1772
 
    gboolean created, prevactive, prevsender;
1773
 
    guint pmembers, members;
1774
 
 
1775
 
    ssrc = gst_rtcp_packet_bye_get_nth_ssrc (packet, i);
1776
 
    GST_DEBUG ("SSRC: %08x", ssrc);
1777
 
 
1778
 
    /* find src and mark bye, no probation when dealing with RTCP */
1779
 
    source = obtain_source (sess, ssrc, &created, arrival, FALSE);
1780
 
    if (!source)
1781
 
      return;
1782
 
 
1783
 
    /* store time for when we need to time out this source */
1784
 
    source->bye_time = arrival->time;
1785
 
 
1786
 
    prevactive = RTP_SOURCE_IS_ACTIVE (source);
1787
 
    prevsender = RTP_SOURCE_IS_SENDER (source);
1788
 
 
1789
 
    /* let the source handle the rest */
1790
 
    rtp_source_process_bye (source, reason);
1791
 
 
1792
 
    pmembers = sess->stats.active_sources;
1793
 
 
1794
 
    if (prevactive && !RTP_SOURCE_IS_ACTIVE (source)) {
1795
 
      sess->stats.active_sources--;
1796
 
      GST_DEBUG ("source: %08x became inactive, %d active sources", ssrc,
1797
 
          sess->stats.active_sources);
1798
 
    }
1799
 
    if (prevsender && !RTP_SOURCE_IS_SENDER (source)) {
1800
 
      sess->stats.sender_sources--;
1801
 
      GST_DEBUG ("source: %08x became non sender, %d sender sources", ssrc,
1802
 
          sess->stats.sender_sources);
1803
 
    }
1804
 
    members = sess->stats.active_sources;
1805
 
 
1806
 
    if (!sess->source->received_bye && members < pmembers) {
1807
 
      /* some members went away since the previous timeout estimate.
1808
 
       * Perform reverse reconsideration but only when we are not scheduling a
1809
 
       * BYE ourselves. */
1810
 
      if (arrival->time < sess->next_rtcp_check_time) {
1811
 
        GstClockTime time_remaining;
1812
 
 
1813
 
        time_remaining = sess->next_rtcp_check_time - arrival->time;
1814
 
        sess->next_rtcp_check_time =
1815
 
            gst_util_uint64_scale (time_remaining, members, pmembers);
1816
 
 
1817
 
        GST_DEBUG ("reverse reconsideration %" GST_TIME_FORMAT,
1818
 
            GST_TIME_ARGS (sess->next_rtcp_check_time));
1819
 
 
1820
 
        sess->next_rtcp_check_time += arrival->time;
1821
 
 
1822
 
        RTP_SESSION_UNLOCK (sess);
1823
 
        /* notify app of reconsideration */
1824
 
        if (sess->callbacks.reconsider)
1825
 
          sess->callbacks.reconsider (sess, sess->reconsider_user_data);
1826
 
        RTP_SESSION_LOCK (sess);
1827
 
      }
1828
 
    }
1829
 
 
1830
 
    if (created)
1831
 
      on_new_ssrc (sess, source);
1832
 
 
1833
 
    on_bye_ssrc (sess, source);
1834
 
 
1835
 
    g_object_unref (source);
1836
 
  }
1837
 
  g_free (reason);
1838
 
}
1839
 
 
1840
 
static void
1841
 
rtp_session_process_app (RTPSession * sess, GstRTCPPacket * packet,
1842
 
    RTPArrivalStats * arrival)
1843
 
{
1844
 
  GST_DEBUG ("received APP");
1845
 
}
1846
 
 
1847
 
/**
1848
 
 * rtp_session_process_rtcp:
1849
 
 * @sess: and #RTPSession
1850
 
 * @buffer: an RTCP buffer
1851
 
 * @current_time: the current system time
1852
 
 *
1853
 
 * Process an RTCP buffer in the session manager. This function takes ownership
1854
 
 * of @buffer.
1855
 
 *
1856
 
 * Returns: a #GstFlowReturn.
1857
 
 */
1858
 
GstFlowReturn
1859
 
rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer,
1860
 
    GstClockTime current_time)
1861
 
{
1862
 
  GstRTCPPacket packet;
1863
 
  gboolean more, is_bye = FALSE, is_sr = FALSE;
1864
 
  RTPArrivalStats arrival;
1865
 
  GstFlowReturn result = GST_FLOW_OK;
1866
 
 
1867
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
1868
 
  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
1869
 
 
1870
 
  if (!gst_rtcp_buffer_validate (buffer))
1871
 
    goto invalid_packet;
1872
 
 
1873
 
  GST_DEBUG ("received RTCP packet");
1874
 
 
1875
 
  RTP_SESSION_LOCK (sess);
1876
 
  /* update arrival stats */
1877
 
  update_arrival_stats (sess, &arrival, FALSE, buffer, current_time, -1, -1);
1878
 
 
1879
 
  if (sess->sent_bye)
1880
 
    goto ignore;
1881
 
 
1882
 
  /* make writable, we might want to change the buffer */
1883
 
  buffer = gst_buffer_make_metadata_writable (buffer);
1884
 
 
1885
 
  /* start processing the compound packet */
1886
 
  more = gst_rtcp_buffer_get_first_packet (buffer, &packet);
1887
 
  while (more) {
1888
 
    GstRTCPType type;
1889
 
 
1890
 
    type = gst_rtcp_packet_get_type (&packet);
1891
 
 
1892
 
    /* when we are leaving the session, we should ignore all non-BYE messages */
1893
 
    if (sess->source->received_bye && type != GST_RTCP_TYPE_BYE) {
1894
 
      GST_DEBUG ("ignoring non-BYE RTCP packet because we are leaving");
1895
 
      goto next;
1896
 
    }
1897
 
 
1898
 
    switch (type) {
1899
 
      case GST_RTCP_TYPE_SR:
1900
 
        rtp_session_process_sr (sess, &packet, &arrival);
1901
 
        is_sr = TRUE;
1902
 
        break;
1903
 
      case GST_RTCP_TYPE_RR:
1904
 
        rtp_session_process_rr (sess, &packet, &arrival);
1905
 
        break;
1906
 
      case GST_RTCP_TYPE_SDES:
1907
 
        rtp_session_process_sdes (sess, &packet, &arrival);
1908
 
        break;
1909
 
      case GST_RTCP_TYPE_BYE:
1910
 
        is_bye = TRUE;
1911
 
        rtp_session_process_bye (sess, &packet, &arrival);
1912
 
        break;
1913
 
      case GST_RTCP_TYPE_APP:
1914
 
        rtp_session_process_app (sess, &packet, &arrival);
1915
 
        break;
1916
 
      default:
1917
 
        GST_WARNING ("got unknown RTCP packet");
1918
 
        break;
1919
 
    }
1920
 
  next:
1921
 
    more = gst_rtcp_packet_move_to_next (&packet);
1922
 
  }
1923
 
 
1924
 
  /* if we are scheduling a BYE, we only want to count bye packets, else we
1925
 
   * count everything */
1926
 
  if (sess->source->received_bye) {
1927
 
    if (is_bye) {
1928
 
      sess->stats.bye_members++;
1929
 
      UPDATE_AVG (sess->stats.avg_rtcp_packet_size, arrival.bytes);
1930
 
    }
1931
 
  } else {
1932
 
    /* keep track of average packet size */
1933
 
    UPDATE_AVG (sess->stats.avg_rtcp_packet_size, arrival.bytes);
1934
 
  }
1935
 
  RTP_SESSION_UNLOCK (sess);
1936
 
 
1937
 
  /* notify caller of sr packets in the callback */
1938
 
  if (is_sr && sess->callbacks.sync_rtcp)
1939
 
    result = sess->callbacks.sync_rtcp (sess, sess->source, buffer,
1940
 
        sess->sync_rtcp_user_data);
1941
 
  else
1942
 
    gst_buffer_unref (buffer);
1943
 
 
1944
 
  return result;
1945
 
 
1946
 
  /* ERRORS */
1947
 
invalid_packet:
1948
 
  {
1949
 
    GST_DEBUG ("invalid RTCP packet received");
1950
 
    gst_buffer_unref (buffer);
1951
 
    return GST_FLOW_OK;
1952
 
  }
1953
 
ignore:
1954
 
  {
1955
 
    gst_buffer_unref (buffer);
1956
 
    RTP_SESSION_UNLOCK (sess);
1957
 
    GST_DEBUG ("ignoring RTP packet because we left");
1958
 
    return GST_FLOW_OK;
1959
 
  }
1960
 
}
1961
 
 
1962
 
/**
1963
 
 * rtp_session_send_rtp:
1964
 
 * @sess: an #RTPSession
1965
 
 * @buffer: an RTP buffer
1966
 
 * @current_time: the current system time
1967
 
 * @ntpnstime: the NTP time in nanoseconds of when this buffer was captured.
1968
 
 * This is the buffer timestamp converted to NTP time.
1969
 
 *
1970
 
 * Send the RTP buffer in the session manager. This function takes ownership of
1971
 
 * @buffer.
1972
 
 *
1973
 
 * Returns: a #GstFlowReturn.
1974
 
 */
1975
 
GstFlowReturn
1976
 
rtp_session_send_rtp (RTPSession * sess, GstBuffer * buffer,
1977
 
    GstClockTime current_time, guint64 ntpnstime)
1978
 
{
1979
 
  GstFlowReturn result;
1980
 
  RTPSource *source;
1981
 
  gboolean prevsender;
1982
 
 
1983
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
1984
 
  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
1985
 
 
1986
 
  if (!gst_rtp_buffer_validate (buffer))
1987
 
    goto invalid_packet;
1988
 
 
1989
 
  GST_LOG ("received RTP packet for sending");
1990
 
 
1991
 
  RTP_SESSION_LOCK (sess);
1992
 
  source = sess->source;
1993
 
 
1994
 
  /* update last activity */
1995
 
  source->last_rtp_activity = current_time;
1996
 
 
1997
 
  prevsender = RTP_SOURCE_IS_SENDER (source);
1998
 
 
1999
 
  /* we use our own source to send */
2000
 
  result = rtp_source_send_rtp (source, buffer, ntpnstime);
2001
 
 
2002
 
  if (RTP_SOURCE_IS_SENDER (source) && !prevsender)
2003
 
    sess->stats.sender_sources++;
2004
 
  RTP_SESSION_UNLOCK (sess);
2005
 
 
2006
 
  return result;
2007
 
 
2008
 
  /* ERRORS */
2009
 
invalid_packet:
2010
 
  {
2011
 
    gst_buffer_unref (buffer);
2012
 
    GST_DEBUG ("invalid RTP packet received");
2013
 
    return GST_FLOW_OK;
2014
 
  }
2015
 
}
2016
 
 
2017
 
static GstClockTime
2018
 
calculate_rtcp_interval (RTPSession * sess, gboolean deterministic,
2019
 
    gboolean first)
2020
 
{
2021
 
  GstClockTime result;
2022
 
 
2023
 
  if (sess->source->received_bye) {
2024
 
    result = rtp_stats_calculate_bye_interval (&sess->stats);
2025
 
  } else {
2026
 
    result = rtp_stats_calculate_rtcp_interval (&sess->stats,
2027
 
        RTP_SOURCE_IS_SENDER (sess->source), first);
2028
 
  }
2029
 
 
2030
 
  GST_DEBUG ("next deterministic interval: %" GST_TIME_FORMAT ", first %d",
2031
 
      GST_TIME_ARGS (result), first);
2032
 
 
2033
 
  if (!deterministic)
2034
 
    result = rtp_stats_add_rtcp_jitter (&sess->stats, result);
2035
 
 
2036
 
  GST_DEBUG ("next interval: %" GST_TIME_FORMAT, GST_TIME_ARGS (result));
2037
 
 
2038
 
  return result;
2039
 
}
2040
 
 
2041
 
/* Stop the current @sess and schedule a BYE message for the other members.
2042
 
 * One must have the session lock to call this function
2043
 
 */
2044
 
static GstFlowReturn
2045
 
rtp_session_schedule_bye_locked (RTPSession * sess, const gchar * reason,
2046
 
    GstClockTime current_time)
2047
 
{
2048
 
  GstFlowReturn result = GST_FLOW_OK;
2049
 
  RTPSource *source;
2050
 
  GstClockTime interval;
2051
 
 
2052
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
2053
 
 
2054
 
  source = sess->source;
2055
 
 
2056
 
  /* ignore more BYEs */
2057
 
  if (source->received_bye)
2058
 
    goto done;
2059
 
 
2060
 
  /* we have BYE now */
2061
 
  source->received_bye = TRUE;
2062
 
  /* at least one member wants to send a BYE */
2063
 
  g_free (sess->bye_reason);
2064
 
  sess->bye_reason = g_strdup (reason);
2065
 
  sess->stats.avg_rtcp_packet_size = 100;
2066
 
  sess->stats.bye_members = 1;
2067
 
  sess->first_rtcp = TRUE;
2068
 
  sess->sent_bye = FALSE;
2069
 
 
2070
 
  /* reschedule transmission */
2071
 
  sess->last_rtcp_send_time = current_time;
2072
 
  interval = calculate_rtcp_interval (sess, FALSE, TRUE);
2073
 
  sess->next_rtcp_check_time = current_time + interval;
2074
 
 
2075
 
  GST_DEBUG ("Schedule BYE for %" GST_TIME_FORMAT ", %" GST_TIME_FORMAT,
2076
 
      GST_TIME_ARGS (interval), GST_TIME_ARGS (sess->next_rtcp_check_time));
2077
 
 
2078
 
  RTP_SESSION_UNLOCK (sess);
2079
 
  /* notify app of reconsideration */
2080
 
  if (sess->callbacks.reconsider)
2081
 
    sess->callbacks.reconsider (sess, sess->reconsider_user_data);
2082
 
  RTP_SESSION_LOCK (sess);
2083
 
done:
2084
 
 
2085
 
  return result;
2086
 
}
2087
 
 
2088
 
/**
2089
 
 * rtp_session_schedule_bye:
2090
 
 * @sess: an #RTPSession
2091
 
 * @reason: a reason or NULL
2092
 
 * @current_time: the current system time
2093
 
 *
2094
 
 * Stop the current @sess and schedule a BYE message for the other members.
2095
 
 *
2096
 
 * Returns: a #GstFlowReturn.
2097
 
 */
2098
 
GstFlowReturn
2099
 
rtp_session_schedule_bye (RTPSession * sess, const gchar * reason,
2100
 
    GstClockTime current_time)
2101
 
{
2102
 
  GstFlowReturn result = GST_FLOW_OK;
2103
 
 
2104
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
2105
 
 
2106
 
  RTP_SESSION_LOCK (sess);
2107
 
  result = rtp_session_schedule_bye_locked (sess, reason, current_time);
2108
 
  RTP_SESSION_UNLOCK (sess);
2109
 
 
2110
 
  return result;
2111
 
}
2112
 
 
2113
 
/**
2114
 
 * rtp_session_next_timeout:
2115
 
 * @sess: an #RTPSession
2116
 
 * @current_time: the current system time
2117
 
 *
2118
 
 * Get the next time we should perform session maintenance tasks.
2119
 
 *
2120
 
 * Returns: a time when rtp_session_on_timeout() should be called with the
2121
 
 * current system time.
2122
 
 */
2123
 
GstClockTime
2124
 
rtp_session_next_timeout (RTPSession * sess, GstClockTime current_time)
2125
 
{
2126
 
  GstClockTime result;
2127
 
 
2128
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
2129
 
 
2130
 
  RTP_SESSION_LOCK (sess);
2131
 
 
2132
 
  result = sess->next_rtcp_check_time;
2133
 
 
2134
 
  GST_DEBUG ("current time: %" GST_TIME_FORMAT ", next :%" GST_TIME_FORMAT,
2135
 
      GST_TIME_ARGS (current_time), GST_TIME_ARGS (result));
2136
 
 
2137
 
  if (result < current_time) {
2138
 
    GST_DEBUG ("take current time as base");
2139
 
    /* our previous check time expired, start counting from the current time
2140
 
     * again. */
2141
 
    result = current_time;
2142
 
  }
2143
 
 
2144
 
  if (sess->source->received_bye) {
2145
 
    if (sess->sent_bye) {
2146
 
      GST_DEBUG ("we sent BYE already");
2147
 
      result = GST_CLOCK_TIME_NONE;
2148
 
    } else if (sess->stats.active_sources >= 50) {
2149
 
      GST_DEBUG ("reconsider BYE, more than 50 sources");
2150
 
      /* reconsider BYE if members >= 50 */
2151
 
      result += calculate_rtcp_interval (sess, FALSE, TRUE);
2152
 
    }
2153
 
  } else {
2154
 
    if (sess->first_rtcp) {
2155
 
      GST_DEBUG ("first RTCP packet");
2156
 
      /* we are called for the first time */
2157
 
      result += calculate_rtcp_interval (sess, FALSE, TRUE);
2158
 
    } else if (sess->next_rtcp_check_time < current_time) {
2159
 
      GST_DEBUG ("old check time expired, getting new timeout");
2160
 
      /* get a new timeout when we need to */
2161
 
      result += calculate_rtcp_interval (sess, FALSE, FALSE);
2162
 
    }
2163
 
  }
2164
 
  sess->next_rtcp_check_time = result;
2165
 
 
2166
 
  GST_DEBUG ("next timeout: %" GST_TIME_FORMAT, GST_TIME_ARGS (result));
2167
 
  RTP_SESSION_UNLOCK (sess);
2168
 
 
2169
 
  return result;
2170
 
}
2171
 
 
2172
 
typedef struct
2173
 
{
2174
 
  RTPSession *sess;
2175
 
  GstBuffer *rtcp;
2176
 
  GstClockTime current_time;
2177
 
  guint64 ntpnstime;
2178
 
  GstClockTime interval;
2179
 
  GstRTCPPacket packet;
2180
 
  gboolean is_bye;
2181
 
  gboolean has_sdes;
2182
 
} ReportData;
2183
 
 
2184
 
static void
2185
 
session_start_rtcp (RTPSession * sess, ReportData * data)
2186
 
{
2187
 
  GstRTCPPacket *packet = &data->packet;
2188
 
  RTPSource *own = sess->source;
2189
 
 
2190
 
  data->rtcp = gst_rtcp_buffer_new (sess->mtu);
2191
 
 
2192
 
  if (RTP_SOURCE_IS_SENDER (own)) {
2193
 
    guint64 ntptime;
2194
 
    guint32 rtptime;
2195
 
    guint32 packet_count, octet_count;
2196
 
 
2197
 
    /* we are a sender, create SR */
2198
 
    GST_DEBUG ("create SR for SSRC %08x", own->ssrc);
2199
 
    gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SR, packet);
2200
 
 
2201
 
    /* get latest stats */
2202
 
    rtp_source_get_new_sr (own, data->ntpnstime, &ntptime, &rtptime,
2203
 
        &packet_count, &octet_count);
2204
 
    /* store stats */
2205
 
    rtp_source_process_sr (own, data->current_time, ntptime, rtptime,
2206
 
        packet_count, octet_count);
2207
 
 
2208
 
    /* fill in sender report info */
2209
 
    gst_rtcp_packet_sr_set_sender_info (packet, own->ssrc,
2210
 
        ntptime, rtptime, packet_count, octet_count);
2211
 
  } else {
2212
 
    /* we are only receiver, create RR */
2213
 
    GST_DEBUG ("create RR for SSRC %08x", own->ssrc);
2214
 
    gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_RR, packet);
2215
 
    gst_rtcp_packet_rr_set_ssrc (packet, own->ssrc);
2216
 
  }
2217
 
}
2218
 
 
2219
 
/* construct a Sender or Receiver Report */
2220
 
static void
2221
 
session_report_blocks (const gchar * key, RTPSource * source, ReportData * data)
2222
 
{
2223
 
  RTPSession *sess = data->sess;
2224
 
  GstRTCPPacket *packet = &data->packet;
2225
 
 
2226
 
  /* create a new buffer if needed */
2227
 
  if (data->rtcp == NULL) {
2228
 
    session_start_rtcp (sess, data);
2229
 
  }
2230
 
  if (gst_rtcp_packet_get_rb_count (packet) < GST_RTCP_MAX_RB_COUNT) {
2231
 
    /* only report about other sender sources */
2232
 
    if (source != sess->source && RTP_SOURCE_IS_SENDER (source)) {
2233
 
      guint8 fractionlost;
2234
 
      gint32 packetslost;
2235
 
      guint32 exthighestseq, jitter;
2236
 
      guint32 lsr, dlsr;
2237
 
 
2238
 
      /* get new stats */
2239
 
      rtp_source_get_new_rb (source, data->current_time, &fractionlost,
2240
 
          &packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
2241
 
 
2242
 
      /* packet is not yet filled, add report block for this source. */
2243
 
      gst_rtcp_packet_add_rb (packet, source->ssrc, fractionlost, packetslost,
2244
 
          exthighestseq, jitter, lsr, dlsr);
2245
 
    }
2246
 
  }
2247
 
}
2248
 
 
2249
 
/* perform cleanup of sources that timed out */
2250
 
static gboolean
2251
 
session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
2252
 
{
2253
 
  gboolean remove = FALSE;
2254
 
  gboolean byetimeout = FALSE;
2255
 
  gboolean sendertimeout = FALSE;
2256
 
  gboolean is_sender, is_active;
2257
 
  RTPSession *sess = data->sess;
2258
 
  GstClockTime interval;
2259
 
 
2260
 
  is_sender = RTP_SOURCE_IS_SENDER (source);
2261
 
  is_active = RTP_SOURCE_IS_ACTIVE (source);
2262
 
 
2263
 
  /* check for our own source, we don't want to delete our own source. */
2264
 
  if (!(source == sess->source)) {
2265
 
    if (source->received_bye) {
2266
 
      /* if we received a BYE from the source, remove the source after some
2267
 
       * time. */
2268
 
      if (data->current_time > source->bye_time &&
2269
 
          data->current_time - source->bye_time > sess->stats.bye_timeout) {
2270
 
        GST_DEBUG ("removing BYE source %08x", source->ssrc);
2271
 
        remove = TRUE;
2272
 
        byetimeout = TRUE;
2273
 
      }
2274
 
    }
2275
 
    /* sources that were inactive for more than 5 times the deterministic reporting
2276
 
     * interval get timed out. the min timeout is 5 seconds. */
2277
 
    if (data->current_time > source->last_activity) {
2278
 
      interval = MAX (data->interval * 5, 5 * GST_SECOND);
2279
 
      if (data->current_time - source->last_activity > interval) {
2280
 
        GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT,
2281
 
            source->ssrc, GST_TIME_ARGS (source->last_activity));
2282
 
        remove = TRUE;
2283
 
      }
2284
 
    }
2285
 
  }
2286
 
 
2287
 
  /* senders that did not send for a long time become a receiver, this also
2288
 
   * holds for our own source. */
2289
 
  if (is_sender) {
2290
 
    if (data->current_time > source->last_rtp_activity) {
2291
 
      interval = MAX (data->interval * 2, 5 * GST_SECOND);
2292
 
      if (data->current_time - source->last_rtp_activity > interval) {
2293
 
        GST_DEBUG ("sender source %08x timed out and became receiver, last %"
2294
 
            GST_TIME_FORMAT, source->ssrc,
2295
 
            GST_TIME_ARGS (source->last_rtp_activity));
2296
 
        source->is_sender = FALSE;
2297
 
        sess->stats.sender_sources--;
2298
 
        sendertimeout = TRUE;
2299
 
      }
2300
 
    }
2301
 
  }
2302
 
 
2303
 
  if (remove) {
2304
 
    sess->total_sources--;
2305
 
    if (is_sender)
2306
 
      sess->stats.sender_sources--;
2307
 
    if (is_active)
2308
 
      sess->stats.active_sources--;
2309
 
 
2310
 
    if (byetimeout)
2311
 
      on_bye_timeout (sess, source);
2312
 
    else
2313
 
      on_timeout (sess, source);
2314
 
  } else {
2315
 
    if (sendertimeout)
2316
 
      on_sender_timeout (sess, source);
2317
 
  }
2318
 
  return remove;
2319
 
}
2320
 
 
2321
 
static void
2322
 
session_sdes (RTPSession * sess, ReportData * data)
2323
 
{
2324
 
  GstRTCPPacket *packet = &data->packet;
2325
 
  guint8 *sdes_data;
2326
 
  guint sdes_len;
2327
 
 
2328
 
  /* add SDES packet */
2329
 
  gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SDES, packet);
2330
 
 
2331
 
  gst_rtcp_packet_sdes_add_item (packet, sess->source->ssrc);
2332
 
 
2333
 
  rtp_source_get_sdes (sess->source, GST_RTCP_SDES_CNAME, &sdes_data,
2334
 
      &sdes_len);
2335
 
  gst_rtcp_packet_sdes_add_entry (packet, GST_RTCP_SDES_CNAME, sdes_len,
2336
 
      sdes_data);
2337
 
 
2338
 
  /* other SDES items must only be added at regular intervals and only when the
2339
 
   * user requests to since it might be a privacy problem */
2340
 
#if 0
2341
 
  gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_NAME,
2342
 
      strlen (sess->name), (guint8 *) sess->name);
2343
 
  gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_TOOL,
2344
 
      strlen (sess->tool), (guint8 *) sess->tool);
2345
 
#endif
2346
 
 
2347
 
  data->has_sdes = TRUE;
2348
 
}
2349
 
 
2350
 
/* schedule a BYE packet */
2351
 
static void
2352
 
session_bye (RTPSession * sess, ReportData * data)
2353
 
{
2354
 
  GstRTCPPacket *packet = &data->packet;
2355
 
 
2356
 
  /* open packet */
2357
 
  session_start_rtcp (sess, data);
2358
 
 
2359
 
  /* add SDES */
2360
 
  session_sdes (sess, data);
2361
 
 
2362
 
  /* add a BYE packet */
2363
 
  gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_BYE, packet);
2364
 
  gst_rtcp_packet_bye_add_ssrc (packet, sess->source->ssrc);
2365
 
  if (sess->bye_reason)
2366
 
    gst_rtcp_packet_bye_set_reason (packet, sess->bye_reason);
2367
 
 
2368
 
  /* we have a BYE packet now */
2369
 
  data->is_bye = TRUE;
2370
 
}
2371
 
 
2372
 
static gboolean
2373
 
is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data)
2374
 
{
2375
 
  GstClockTime new_send_time, elapsed;
2376
 
  gboolean result;
2377
 
 
2378
 
  /* no need to check yet */
2379
 
  if (sess->next_rtcp_check_time > current_time) {
2380
 
    GST_DEBUG ("no check time yet, next %" GST_TIME_FORMAT " > now %"
2381
 
        GST_TIME_FORMAT, GST_TIME_ARGS (sess->next_rtcp_check_time),
2382
 
        GST_TIME_ARGS (current_time));
2383
 
    return FALSE;
2384
 
  }
2385
 
 
2386
 
  /* get elapsed time since we last reported */
2387
 
  elapsed = current_time - sess->last_rtcp_send_time;
2388
 
 
2389
 
  /* perform forward reconsideration */
2390
 
  new_send_time = rtp_stats_add_rtcp_jitter (&sess->stats, data->interval);
2391
 
 
2392
 
  GST_DEBUG ("forward reconsideration %" GST_TIME_FORMAT ", elapsed %"
2393
 
      GST_TIME_FORMAT, GST_TIME_ARGS (new_send_time), GST_TIME_ARGS (elapsed));
2394
 
 
2395
 
  new_send_time += sess->last_rtcp_send_time;
2396
 
 
2397
 
  /* check if reconsideration */
2398
 
  if (current_time < new_send_time) {
2399
 
    GST_DEBUG ("reconsider RTCP for %" GST_TIME_FORMAT,
2400
 
        GST_TIME_ARGS (new_send_time));
2401
 
    result = FALSE;
2402
 
    /* store new check time */
2403
 
    sess->next_rtcp_check_time = new_send_time;
2404
 
  } else {
2405
 
    result = TRUE;
2406
 
    new_send_time = calculate_rtcp_interval (sess, FALSE, FALSE);
2407
 
 
2408
 
    GST_DEBUG ("can send RTCP now, next interval %" GST_TIME_FORMAT,
2409
 
        GST_TIME_ARGS (new_send_time));
2410
 
    sess->next_rtcp_check_time = current_time + new_send_time;
2411
 
  }
2412
 
  return result;
2413
 
}
2414
 
 
2415
 
/**
2416
 
 * rtp_session_on_timeout:
2417
 
 * @sess: an #RTPSession
2418
 
 * @current_time: the current system time
2419
 
 * @ntpnstime: the current NTP time in nanoseconds
2420
 
 *
2421
 
 * Perform maintenance actions after the timeout obtained with
2422
 
 * rtp_session_next_timeout() expired.
2423
 
 *
2424
 
 * This function will perform timeouts of receivers and senders, send a BYE
2425
 
 * packet or generate RTCP packets with current session stats.
2426
 
 *
2427
 
 * This function can call the #RTPSessionSendRTCP callback, possibly multiple
2428
 
 * times, for each packet that should be processed.
2429
 
 *
2430
 
 * Returns: a #GstFlowReturn.
2431
 
 */
2432
 
GstFlowReturn
2433
 
rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time,
2434
 
    guint64 ntpnstime)
2435
 
{
2436
 
  GstFlowReturn result = GST_FLOW_OK;
2437
 
  GList *item;
2438
 
  ReportData data;
2439
 
  RTPSource *own;
2440
 
  gboolean notify = FALSE;
2441
 
 
2442
 
  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
2443
 
 
2444
 
  GST_DEBUG ("reporting at %" GST_TIME_FORMAT ", NTP time %" GST_TIME_FORMAT,
2445
 
      GST_TIME_ARGS (current_time), GST_TIME_ARGS (ntpnstime));
2446
 
 
2447
 
  data.sess = sess;
2448
 
  data.rtcp = NULL;
2449
 
  data.current_time = current_time;
2450
 
  data.ntpnstime = ntpnstime;
2451
 
  data.is_bye = FALSE;
2452
 
  data.has_sdes = FALSE;
2453
 
 
2454
 
  own = sess->source;
2455
 
 
2456
 
  RTP_SESSION_LOCK (sess);
2457
 
  /* get a new interval, we need this for various cleanups etc */
2458
 
  data.interval = calculate_rtcp_interval (sess, TRUE, sess->first_rtcp);
2459
 
 
2460
 
  /* first perform cleanups */
2461
 
  g_hash_table_foreach_remove (sess->ssrcs[sess->mask_idx],
2462
 
      (GHRFunc) session_cleanup, &data);
2463
 
 
2464
 
  /* see if we need to generate SR or RR packets */
2465
 
  if (is_rtcp_time (sess, current_time, &data)) {
2466
 
    if (own->received_bye) {
2467
 
      /* generate BYE instead */
2468
 
      GST_DEBUG ("generating BYE message");
2469
 
      session_bye (sess, &data);
2470
 
      sess->sent_bye = TRUE;
2471
 
    } else {
2472
 
      /* loop over all known sources and do something */
2473
 
      g_hash_table_foreach (sess->ssrcs[sess->mask_idx],
2474
 
          (GHFunc) session_report_blocks, &data);
2475
 
    }
2476
 
  }
2477
 
 
2478
 
  if (data.rtcp) {
2479
 
    guint size;
2480
 
 
2481
 
    /* we keep track of the last report time in order to timeout inactive
2482
 
     * receivers or senders */
2483
 
    sess->last_rtcp_send_time = data.current_time;
2484
 
    sess->first_rtcp = FALSE;
2485
 
 
2486
 
    /* add SDES for this source when not already added */
2487
 
    if (!data.has_sdes)
2488
 
      session_sdes (sess, &data);
2489
 
 
2490
 
    /* update average RTCP size before sending */
2491
 
    size = GST_BUFFER_SIZE (data.rtcp) + sess->header_len;
2492
 
    UPDATE_AVG (sess->stats.avg_rtcp_packet_size, size);
2493
 
  }
2494
 
 
2495
 
  /* check for outdated collisions */
2496
 
  GST_DEBUG ("checking collision list");
2497
 
  item = g_list_first (sess->conflicting_addresses);
2498
 
  while (item) {
2499
 
    RTPConflictingAddress *known_conflict = item->data;
2500
 
    GList *next_item = g_list_next (item);
2501
 
 
2502
 
    if (known_conflict->time < current_time - (data.interval *
2503
 
            RTCP_INTERVAL_COLLISION_TIMEOUT)) {
2504
 
      sess->conflicting_addresses =
2505
 
          g_list_delete_link (sess->conflicting_addresses, item);
2506
 
      GST_DEBUG ("collision %p timed out", known_conflict);
2507
 
      g_free (known_conflict);
2508
 
    }
2509
 
    item = next_item;
2510
 
  }
2511
 
 
2512
 
  if (sess->change_ssrc) {
2513
 
    GST_DEBUG ("need to change our SSRC (%08x)", own->ssrc);
2514
 
    g_hash_table_steal (sess->ssrcs[sess->mask_idx],
2515
 
        GINT_TO_POINTER (own->ssrc));
2516
 
 
2517
 
    own->ssrc = rtp_session_create_new_ssrc (sess);
2518
 
    rtp_source_reset (own);
2519
 
 
2520
 
    g_hash_table_insert (sess->ssrcs[sess->mask_idx],
2521
 
        GINT_TO_POINTER (own->ssrc), own);
2522
 
 
2523
 
    g_free (sess->bye_reason);
2524
 
    sess->bye_reason = NULL;
2525
 
    sess->sent_bye = FALSE;
2526
 
    sess->change_ssrc = FALSE;
2527
 
    notify = TRUE;
2528
 
    GST_DEBUG ("changed our SSRC to %08x", own->ssrc);
2529
 
  }
2530
 
  RTP_SESSION_UNLOCK (sess);
2531
 
 
2532
 
  if (notify)
2533
 
    g_object_notify (G_OBJECT (sess), "internal-ssrc");
2534
 
 
2535
 
  /* push out the RTCP packet */
2536
 
  if (data.rtcp) {
2537
 
    /* close the RTCP packet */
2538
 
    gst_rtcp_buffer_end (data.rtcp);
2539
 
 
2540
 
    GST_DEBUG ("sending packet");
2541
 
    if (sess->callbacks.send_rtcp)
2542
 
      result = sess->callbacks.send_rtcp (sess, own, data.rtcp,
2543
 
          sess->sent_bye, sess->send_rtcp_user_data);
2544
 
    else {
2545
 
      GST_DEBUG ("freeing packet");
2546
 
      gst_buffer_unref (data.rtcp);
2547
 
    }
2548
 
  }
2549
 
 
2550
 
  return result;
2551
 
}