rtpsource.c 51.7 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
16
17
18
19
20
21
22
23
24
25
26
27
28
 *
 * 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
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include <string.h>

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

#include "rtpsource.h"

GST_DEBUG_CATEGORY_STATIC (rtp_source_debug);
#define GST_CAT_DEFAULT rtp_source_debug

29
#define RTP_MAX_PROBATION_LEN  32
30
31
32
33
34
35
36

/* signals and args */
enum
{
  LAST_SIGNAL
};

Wim Taymans's avatar
Wim Taymans committed
37
38
39
40
#define DEFAULT_SSRC                 0
#define DEFAULT_IS_CSRC              FALSE
#define DEFAULT_IS_VALIDATED         FALSE
#define DEFAULT_IS_SENDER            FALSE
41
#define DEFAULT_SDES                 NULL
Wim Taymans's avatar
Wim Taymans committed
42

43
44
enum
{
Wim Taymans's avatar
Wim Taymans committed
45
46
47
48
49
  PROP_0,
  PROP_SSRC,
  PROP_IS_CSRC,
  PROP_IS_VALIDATED,
  PROP_IS_SENDER,
50
  PROP_SDES,
51
  PROP_STATS,
Wim Taymans's avatar
Wim Taymans committed
52
  PROP_LAST
53
54
55
56
};

/* GObject vmethods */
static void rtp_source_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
57
58
59
60
static void rtp_source_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void rtp_source_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
61
62
63
64
65
66
67
68
69
70
71
72
73
74

/* static guint rtp_source_signals[LAST_SIGNAL] = { 0 }; */

G_DEFINE_TYPE (RTPSource, rtp_source, G_TYPE_OBJECT);

static void
rtp_source_class_init (RTPSourceClass * klass)
{
  GObjectClass *gobject_class;

  gobject_class = (GObjectClass *) klass;

  gobject_class->finalize = rtp_source_finalize;

Wim Taymans's avatar
Wim Taymans committed
75
76
77
78
79
  gobject_class->set_property = rtp_source_set_property;
  gobject_class->get_property = rtp_source_get_property;

  g_object_class_install_property (gobject_class, PROP_SSRC,
      g_param_spec_uint ("ssrc", "SSRC",
80
          "The SSRC of this source", 0, G_MAXUINT, DEFAULT_SSRC,
81
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
82
83
84
85

  g_object_class_install_property (gobject_class, PROP_IS_CSRC,
      g_param_spec_boolean ("is-csrc", "Is CSRC",
          "If this SSRC is acting as a contributing source",
86
          DEFAULT_IS_CSRC, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
87
88
89

  g_object_class_install_property (gobject_class, PROP_IS_VALIDATED,
      g_param_spec_boolean ("is-validated", "Is Validated",
90
91
          "If this SSRC is validated", DEFAULT_IS_VALIDATED,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
92
93
94

  g_object_class_install_property (gobject_class, PROP_IS_SENDER,
      g_param_spec_boolean ("is-sender", "Is Sender",
95
96
          "If this SSRC is a sender", DEFAULT_IS_SENDER,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
97

98
99
100
  /**
   * RTPSource::sdes
   *
101
102
   * The current SDES items of the source. Returns a structure with name
   * application/x-rtp-source-sdes and may contain the following fields:
103
   *
104
105
106
107
108
109
110
111
112
113
   *  'cname'       G_TYPE_STRING  : The canonical name
   *  'name'        G_TYPE_STRING  : The user name
   *  'email'       G_TYPE_STRING  : The user's electronic mail address
   *  'phone'       G_TYPE_STRING  : The user's phone number
   *  'location'    G_TYPE_STRING  : The geographic user location
   *  'tool'        G_TYPE_STRING  : The name of application or tool
   *  'note'        G_TYPE_STRING  : A notice about the source
   *
   *  other fields may be present and these represent private items in
   *  the SDES where the field name is the prefix.
114
115
116
117
118
   */
  g_object_class_install_property (gobject_class, PROP_SDES,
      g_param_spec_boxed ("sdes", "SDES",
          "The SDES information for this source",
          GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
119
120
121
122
123
124

  /**
   * RTPSource::stats
   *
   * The statistics of the source. This property returns a GstStructure with
   * name application/x-rtp-source-stats with the following fields:
Wim Taymans's avatar
Wim Taymans committed
125
   *
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
   *  "ssrc"         G_TYPE_UINT     The SSRC of this source
   *  "internal"     G_TYPE_BOOLEAN  If this source is the source of the session
   *  "validated"    G_TYPE_BOOLEAN  If the source is validated
   *  "received-bye" G_TYPE_BOOLEAN  If we received a BYE from this source
   *  "is-csrc"      G_TYPE_BOOLEAN  If this source was found as CSRC
   *  "is-sender"    G_TYPE_BOOLEAN  If this source is a sender
   *  "seqnum-base"  G_TYPE_INT      first seqnum if known
   *  "clock-rate"   G_TYPE_INT      the clock rate of the media
   *
   * The following two fields are only present when known.
   *
   *  "rtp-from"     G_TYPE_STRING   where we received the last RTP packet from
   *  "rtcp-from"    G_TYPE_STRING   where we received the last RTCP packet from
   *
   * The following fields make sense for internal sources and will only increase
   * when "is-sender" is TRUE:
   *
   *  "octets-sent"  G_TYPE_UINT64   number of bytes we sent
   *  "packets-sent" G_TYPE_UINT64   number of packets we sent
   *
   * The following fields make sense for non-internal sources and will only
   * increase when "is-sender" is TRUE.
   *
   *  "octets-received"  G_TYPE_UINT64  total number of bytes received
   *  "packets-received" G_TYPE_UINT64  total number of packets received
   *
   * Following fields are updated when "is-sender" is TRUE.
   *
   *  "bitrate"      G_TYPE_UINT64   bitrate in bits per second
   *  "jitter"       G_TYPE_UINT     estimated jitter
   *  "packets-lost" G_TYPE_INT      estimated amount of packets lost
   *
   * The last SR report this source sent. This only updates when "is-sender" is
   * TRUE.
   *
   *  "have-sr"         G_TYPE_BOOLEAN  the source has sent SR
   *  "sr-ntptime"      G_TYPE_UINT64   ntptime of SR
   *  "sr-rtptime"      G_TYPE_UINT     rtptime of SR
   *  "sr-octet-count"  G_TYPE_UINT     the number of bytes in the SR
   *  "sr-packet-count" G_TYPE_UINT     the number of packets in the SR
   *
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
   * The following fields are only present for non-internal sources and
   * represent the content of the last RB packet that was sent to this source.
   * These values are only updated when the source is sending.
   *
   *  "sent-rb"               G_TYPE_BOOLEAN  we have sent an RB
   *  "sent-rb-fractionlost"  G_TYPE_UINT     calculated lost fraction
   *  "sent-rb-packetslost"   G_TYPE_INT      lost packets
   *  "sent-rb-exthighestseq" G_TYPE_UINT     last seen seqnum
   *  "sent-rb-jitter"        G_TYPE_UINT     jitter
   *  "sent-rb-lsr"           G_TYPE_UINT     last SR time
   *  "sent-rb-dlsr"          G_TYPE_UINT     delay since last SR
   *
   * The following fields are only present for non-internal sources and
   * represents the last RB that this source sent. This is only updated
   * when the source is receiving data and sending RB blocks.
182
183
184
185
186
187
188
189
190
191
   *
   *  "have-rb"          G_TYPE_BOOLEAN  the source has sent RB
   *  "rb-fractionlost"  G_TYPE_UINT     lost fraction
   *  "rb-packetslost"   G_TYPE_INT      lost packets
   *  "rb-exthighestseq" G_TYPE_UINT     highest received seqnum
   *  "rb-jitter"        G_TYPE_UINT     reception jitter
   *  "rb-lsr"           G_TYPE_UINT     last SR time
   *  "rb-dlsr"          G_TYPE_UINT     delay since last SR
   *
   * The round trip of this source. This is calculated from the last RB
192
193
   * values and the recption time of the last RB packet. Only present for
   * non-internal sources.
194
195
   *
   *  "rb-round-trip"    G_TYPE_UINT     the round trip time in nanoseconds
196
197
198
199
200
   */
  g_object_class_install_property (gobject_class, PROP_STATS,
      g_param_spec_boxed ("stats", "Stats",
          "The stats of this source", GST_TYPE_STRUCTURE,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
201

202
203
204
  GST_DEBUG_CATEGORY_INIT (rtp_source_debug, "rtpsource", 0, "RTP Source");
}

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/**
 * rtp_source_reset:
 * @src: an #RTPSource
 *
 * Reset the stats of @src.
 */
void
rtp_source_reset (RTPSource * src)
{
  src->received_bye = FALSE;

  src->stats.cycles = -1;
  src->stats.jitter = 0;
  src->stats.transit = -1;
  src->stats.curr_sr = 0;
  src->stats.curr_rr = 0;
}

223
224
225
226
227
228
static void
rtp_source_init (RTPSource * src)
{
  /* sources are initialy on probation until we receive enough valid RTP
   * packets or a valid RTCP packet */
  src->validated = FALSE;
229
  src->internal = FALSE;
230
  src->probation = RTP_DEFAULT_PROBATION;
231
  src->closing = FALSE;
232

Wim Taymans's avatar
Wim Taymans committed
233
  src->sdes = gst_structure_empty_new ("application/x-rtp-source-sdes");
234

235
  src->payload = -1;
236
237
  src->clock_rate = -1;
  src->packets = g_queue_new ();
238
  src->seqnum_base = -1;
239
  src->last_rtptime = -1;
240

241
242
  src->retained_feedback = g_queue_new ();

243
  rtp_source_reset (src);
244
245
246
247
248
249
250
251
252
253
254
255
256
257
}

static void
rtp_source_finalize (GObject * object)
{
  RTPSource *src;
  GstBuffer *buffer;

  src = RTP_SOURCE_CAST (object);

  while ((buffer = g_queue_pop_head (src->packets)))
    gst_buffer_unref (buffer);
  g_queue_free (src->packets);

258
  gst_structure_free (src->sdes);
Wim Taymans's avatar
Wim Taymans committed
259

Wim Taymans's avatar
Wim Taymans committed
260
261
  g_free (src->bye_reason);

262
263
  gst_caps_replace (&src->caps, NULL);

264
265
266
  g_list_foreach (src->conflicting_addresses, (GFunc) g_free, NULL);
  g_list_free (src->conflicting_addresses);

267
268
269
270
  while ((buffer = g_queue_pop_head (src->retained_feedback)))
    gst_buffer_unref (buffer);
  g_queue_free (src->retained_feedback);

271
272
273
  G_OBJECT_CLASS (rtp_source_parent_class)->finalize (object);
}

274
275
276
277
278
279
static GstStructure *
rtp_source_create_stats (RTPSource * src)
{
  GstStructure *s;
  gboolean is_sender = src->is_sender;
  gboolean internal = src->internal;
280
  gchar address_str[GST_NETADDRESS_MAX_LEN];
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  gboolean have_rb;
  guint8 fractionlost = 0;
  gint32 packetslost = 0;
  guint32 exthighestseq = 0;
  guint32 jitter = 0;
  guint32 lsr = 0;
  guint32 dlsr = 0;
  guint32 round_trip = 0;
  gboolean have_sr;
  GstClockTime time = 0;
  guint64 ntptime = 0;
  guint32 rtptime = 0;
  guint32 packet_count = 0;
  guint32 octet_count = 0;

296
297
298
299
300
301
302
303

  /* common data for all types of sources */
  s = gst_structure_new ("application/x-rtp-source-stats",
      "ssrc", G_TYPE_UINT, (guint) src->ssrc,
      "internal", G_TYPE_BOOLEAN, internal,
      "validated", G_TYPE_BOOLEAN, src->validated,
      "received-bye", G_TYPE_BOOLEAN, src->received_bye,
      "is-csrc", G_TYPE_BOOLEAN, src->is_csrc,
304
305
306
      "is-sender", G_TYPE_BOOLEAN, is_sender,
      "seqnum-base", G_TYPE_INT, src->seqnum_base,
      "clock-rate", G_TYPE_INT, src->clock_rate, NULL);
307

308
309
  /* add address and port */
  if (src->have_rtp_from) {
310
311
    gst_netaddress_to_string (&src->rtp_from, address_str,
        sizeof (address_str));
312
313
314
    gst_structure_set (s, "rtp-from", G_TYPE_STRING, address_str, NULL);
  }
  if (src->have_rtcp_from) {
315
316
    gst_netaddress_to_string (&src->rtcp_from, address_str,
        sizeof (address_str));
317
318
319
    gst_structure_set (s, "rtcp-from", G_TYPE_STRING, address_str, NULL);
  }

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  gst_structure_set (s,
      "octets-sent", G_TYPE_UINT64, src->stats.octets_sent,
      "packets-sent", G_TYPE_UINT64, src->stats.packets_sent,
      "octets-received", G_TYPE_UINT64, src->stats.octets_received,
      "packets-received", G_TYPE_UINT64, src->stats.packets_received,
      "bitrate", G_TYPE_UINT64, src->bitrate,
      "packets-lost", G_TYPE_INT,
      (gint) rtp_stats_get_packets_lost (&src->stats), "jitter", G_TYPE_UINT,
      (guint) (src->stats.jitter >> 4), NULL);

  /* get the last SR. */
  have_sr = rtp_source_get_last_sr (src, &time, &ntptime, &rtptime,
      &packet_count, &octet_count);
  gst_structure_set (s,
      "have-sr", G_TYPE_BOOLEAN, have_sr,
      "sr-ntptime", G_TYPE_UINT64, ntptime,
      "sr-rtptime", G_TYPE_UINT, (guint) rtptime,
      "sr-octet-count", G_TYPE_UINT, (guint) octet_count,
      "sr-packet-count", G_TYPE_UINT, (guint) packet_count, NULL);

340
341
342
  if (!internal) {
    /* get the last RB we sent */
    gst_structure_set (s,
343
        "sent-rb", G_TYPE_BOOLEAN, src->last_rr.is_valid,
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
        "sent-rb-fractionlost", G_TYPE_UINT, (guint) src->last_rr.fractionlost,
        "sent-rb-packetslost", G_TYPE_INT, (gint) src->last_rr.packetslost,
        "sent-rb-exthighestseq", G_TYPE_UINT,
        (guint) src->last_rr.exthighestseq, "sent-rb-jitter", G_TYPE_UINT,
        (guint) src->last_rr.jitter, "sent-rb-lsr", G_TYPE_UINT,
        (guint) src->last_rr.lsr, "sent-rb-dlsr", G_TYPE_UINT,
        (guint) src->last_rr.dlsr, NULL);

    /* get the last RB */
    have_rb = rtp_source_get_last_rb (src, &fractionlost, &packetslost,
        &exthighestseq, &jitter, &lsr, &dlsr, &round_trip);

    gst_structure_set (s,
        "have-rb", G_TYPE_BOOLEAN, have_rb,
        "rb-fractionlost", G_TYPE_UINT, (guint) fractionlost,
        "rb-packetslost", G_TYPE_INT, (gint) packetslost,
        "rb-exthighestseq", G_TYPE_UINT, (guint) exthighestseq,
        "rb-jitter", G_TYPE_UINT, (guint) jitter,
        "rb-lsr", G_TYPE_UINT, (guint) lsr,
        "rb-dlsr", G_TYPE_UINT, (guint) dlsr,
        "rb-round-trip", G_TYPE_UINT, (guint) round_trip, NULL);
  }
366
367
368
369

  return s;
}

370
371
/**
 * rtp_source_get_sdes_struct:
372
 * @src: an #RTPSource
373
 *
374
 * Get the SDES from @src. See the SDES property for more details.
375
 *
376
377
 * Returns: %GstStructure of type "application/x-rtp-source-sdes". The result is
 * valid until the SDES items of @src are modified.
378
 */
379
const GstStructure *
380
rtp_source_get_sdes_struct (RTPSource * src)
381
{
382
  g_return_val_if_fail (RTP_IS_SOURCE (src), NULL);
383

384
  return src->sdes;
385
}
386

387
388
389
390
static gboolean
sdes_struct_compare_func (GQuark field_id, const GValue * value,
    gpointer user_data)
{
391
392
393
394
395
  GstStructure *old;
  const gchar *field;

  old = GST_STRUCTURE (user_data);
  field = g_quark_to_string (field_id);
396
397
398
399
400

  if (!gst_structure_has_field (old, field))
    return FALSE;

  g_assert (G_VALUE_HOLDS_STRING (value));
401

402
403
  return strcmp (g_value_get_string (value), gst_structure_get_string (old,
          field)) == 0;
404
405
}

406
/**
407
408
409
 * rtp_source_set_sdes:
 * @src: an #RTPSource
 * @sdes: the SDES structure
410
 *
411
412
413
 * Store the @sdes in @src. @sdes must be a structure of type
 * "application/x-rtp-source-sdes", see the SDES property for more details.
 *
414
415
 * This function takes ownership of @sdes.
 *
416
 * Returns: %FALSE if the SDES was unchanged.
417
 */
418
gboolean
419
rtp_source_set_sdes_struct (RTPSource * src, GstStructure * sdes)
420
{
421
  gboolean changed;
422

423
424
425
426
427
428
429
430
  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
  g_return_val_if_fail (strcmp (gst_structure_get_name (sdes),
          "application/x-rtp-source-sdes") == 0, FALSE);

  changed = !gst_structure_foreach (sdes, sdes_struct_compare_func, src->sdes);

  if (changed) {
    gst_structure_free (src->sdes);
431
432
433
    src->sdes = sdes;
  } else {
    gst_structure_free (sdes);
434
  }
435
436

  return changed;
437
438
}

Wim Taymans's avatar
Wim Taymans committed
439
440
441
442
443
444
445
446
447
static void
rtp_source_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  RTPSource *src;

  src = RTP_SOURCE (object);

  switch (prop_id) {
448
449
450
    case PROP_SSRC:
      src->ssrc = g_value_get_uint (value);
      break;
Wim Taymans's avatar
Wim Taymans committed
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
rtp_source_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  RTPSource *src;

  src = RTP_SOURCE (object);

  switch (prop_id) {
    case PROP_SSRC:
      g_value_set_uint (value, rtp_source_get_ssrc (src));
      break;
    case PROP_IS_CSRC:
      g_value_set_boolean (value, rtp_source_is_as_csrc (src));
      break;
    case PROP_IS_VALIDATED:
      g_value_set_boolean (value, rtp_source_is_validated (src));
      break;
    case PROP_IS_SENDER:
      g_value_set_boolean (value, rtp_source_is_sender (src));
      break;
478
    case PROP_SDES:
479
      g_value_set_boxed (value, rtp_source_get_sdes_struct (src));
Wim Taymans's avatar
Wim Taymans committed
480
      break;
481
482
483
    case PROP_STATS:
      g_value_take_boxed (value, rtp_source_create_stats (src));
      break;
Wim Taymans's avatar
Wim Taymans committed
484
485
486
487
488
489
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
/**
 * rtp_source_new:
 * @ssrc: an SSRC
 *
 * Create a #RTPSource with @ssrc.
 *
 * Returns: a new #RTPSource. Use g_object_unref() after usage.
 */
RTPSource *
rtp_source_new (guint32 ssrc)
{
  RTPSource *src;

  src = g_object_new (RTP_TYPE_SOURCE, NULL);
  src->ssrc = ssrc;

  return src;
}

Wim Taymans's avatar
Wim Taymans committed
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
/**
 * rtp_source_set_callbacks:
 * @src: an #RTPSource
 * @cb: callback functions
 * @user_data: user data
 *
 * Set the callbacks for the source.
 */
void
rtp_source_set_callbacks (RTPSource * src, RTPSourceCallbacks * cb,
    gpointer user_data)
{
  g_return_if_fail (RTP_IS_SOURCE (src));

  src->callbacks.push_rtp = cb->push_rtp;
  src->callbacks.clock_rate = cb->clock_rate;
  src->user_data = user_data;
}

/**
 * rtp_source_get_ssrc:
 * @src: an #RTPSource
 *
 * Get the SSRC of @source.
 *
 * Returns: the SSRC of src.
 */
guint32
rtp_source_get_ssrc (RTPSource * src)
{
  guint32 result;

  g_return_val_if_fail (RTP_IS_SOURCE (src), 0);

  result = src->ssrc;

  return result;
}

/**
 * rtp_source_set_as_csrc:
 * @src: an #RTPSource
 *
 * Configure @src as a CSRC, this will also validate @src.
 */
void
rtp_source_set_as_csrc (RTPSource * src)
{
  g_return_if_fail (RTP_IS_SOURCE (src));

  src->validated = TRUE;
  src->is_csrc = TRUE;
}

/**
 * rtp_source_is_as_csrc:
 * @src: an #RTPSource
 *
 * Check if @src is a contributing source.
 *
 * Returns: %TRUE if @src is acting as a contributing source.
 */
gboolean
rtp_source_is_as_csrc (RTPSource * src)
{
  gboolean result;

  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);

  result = src->is_csrc;

  return result;
}

/**
 * rtp_source_is_active:
 * @src: an #RTPSource
 *
 * Check if @src is an active source. A source is active if it has been
 * validated and has not yet received a BYE packet
 *
 * Returns: %TRUE if @src is an qactive source.
 */
gboolean
rtp_source_is_active (RTPSource * src)
{
  gboolean result;

  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);

  result = RTP_SOURCE_IS_ACTIVE (src);

  return result;
}

/**
 * rtp_source_is_validated:
 * @src: an #RTPSource
 *
 * Check if @src is a validated source.
 *
 * Returns: %TRUE if @src is a validated source.
 */
gboolean
rtp_source_is_validated (RTPSource * src)
{
  gboolean result;

  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);

  result = src->validated;

  return result;
}

/**
 * rtp_source_is_sender:
 * @src: an #RTPSource
 *
 * Check if @src is a sending source.
 *
 * Returns: %TRUE if @src is a sending source.
 */
gboolean
rtp_source_is_sender (RTPSource * src)
{
  gboolean result;

  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);

  result = RTP_SOURCE_IS_SENDER (src);

  return result;
}

/**
 * rtp_source_received_bye:
 * @src: an #RTPSource
 *
 * Check if @src has receoved a BYE packet.
 *
 * Returns: %TRUE if @src has received a BYE packet.
 */
gboolean
rtp_source_received_bye (RTPSource * src)
{
  gboolean result;

  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);

  result = src->received_bye;

  return result;
}


/**
 * rtp_source_get_bye_reason:
 * @src: an #RTPSource
 *
 * Get the BYE reason for @src. Check if the source receoved a BYE message first
 * with rtp_source_received_bye().
 *
 * Returns: The BYE reason or NULL when no reason was given or the source did
 * not receive a BYE message yet. g_fee() after usage.
 */
gchar *
rtp_source_get_bye_reason (RTPSource * src)
{
  gchar *result;

  g_return_val_if_fail (RTP_IS_SOURCE (src), NULL);

  result = g_strdup (src->bye_reason);

  return result;
}

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
/**
 * rtp_source_update_caps:
 * @src: an #RTPSource
 * @caps: a #GstCaps
 *
 * Parse @caps and store all relevant information in @source.
 */
void
rtp_source_update_caps (RTPSource * src, GstCaps * caps)
{
  GstStructure *s;
  guint val;
  gint ival;

  /* nothing changed, return */
702
  if (caps == NULL || src->caps == caps)
703
704
705
706
707
708
    return;

  s = gst_caps_get_structure (caps, 0);

  if (gst_structure_get_int (s, "payload", &ival))
    src->payload = ival;
709
710
  else
    src->payload = -1;
711
712
  GST_DEBUG ("got payload %d", src->payload);

713
714
715
716
717
  if (gst_structure_get_int (s, "clock-rate", &ival))
    src->clock_rate = ival;
  else
    src->clock_rate = -1;

718
719
720
721
  GST_DEBUG ("got clock-rate %d", src->clock_rate);

  if (gst_structure_get_uint (s, "seqnum-base", &val))
    src->seqnum_base = val;
722
723
724
  else
    src->seqnum_base = -1;

725
726
727
728
729
  GST_DEBUG ("got seqnum-base %" G_GINT32_FORMAT, src->seqnum_base);

  gst_caps_replace (&src->caps, caps);
}

730
/**
Wim Taymans's avatar
Wim Taymans committed
731
 * rtp_source_set_sdes_string:
732
 * @src: an #RTPSource
Wim Taymans's avatar
Wim Taymans committed
733
734
 * @type: the type of the SDES item
 * @data: the SDES data
735
 *
736
 * Store an SDES item of @type in @src.
Wim Taymans's avatar
Wim Taymans committed
737
738
 *
 * Returns: %FALSE if the SDES item was unchanged or @type is unknown.
739
 */
Wim Taymans's avatar
Wim Taymans committed
740
741
742
gboolean
rtp_source_set_sdes_string (RTPSource * src, GstRTCPSDESType type,
    const gchar * data)
743
{
744
745
  const gchar *old;
  const gchar *field;
746

747
  field = gst_rtcp_sdes_type_to_name (type);
Wim Taymans's avatar
Wim Taymans committed
748

749
750
751
752
  if (gst_structure_has_field (src->sdes, field))
    old = gst_structure_get_string (src->sdes, field);
  else
    old = NULL;
Wim Taymans's avatar
Wim Taymans committed
753

754
755
  if (old == NULL && data == NULL)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
756

757
  if (old != NULL && data != NULL && strcmp (old, data) == 0)
Wim Taymans's avatar
Wim Taymans committed
758
759
    return FALSE;

760
761
762
763
  if (data == NULL)
    gst_structure_remove_field (src->sdes, field);
  else
    gst_structure_set (src->sdes, field, G_TYPE_STRING, data, NULL);
Wim Taymans's avatar
Wim Taymans committed
764
765
766
767
768
769
770
771
772

  return TRUE;
}

/**
 * rtp_source_get_sdes_string:
 * @src: an #RTPSource
 * @type: the type of the SDES item
 *
Wim Taymans's avatar
Wim Taymans committed
773
 * Get the SDES item of @type from @src.
Wim Taymans's avatar
Wim Taymans committed
774
775
 *
 * Returns: a null-terminated copy of the SDES item or NULL when @type was not
776
 * valid or the SDES item was unset. g_free() after usage.
Wim Taymans's avatar
Wim Taymans committed
777
778
779
780
781
 */
gchar *
rtp_source_get_sdes_string (RTPSource * src, GstRTCPSDESType type)
{
  gchar *result;
782
  const gchar *type_name;
Wim Taymans's avatar
Wim Taymans committed
783
784
785

  g_return_val_if_fail (RTP_IS_SOURCE (src), NULL);

786
787
788
789
790
791
  if (type < 0 || type > GST_RTCP_SDES_PRIV - 1)
    return NULL;

  type_name = gst_rtcp_sdes_type_to_name (type);

  if (!gst_structure_has_field (src->sdes, type_name))
Wim Taymans's avatar
Wim Taymans committed
792
793
    return NULL;

794
  result = g_strdup (gst_structure_get_string (src->sdes, type_name));
Wim Taymans's avatar
Wim Taymans committed
795
796

  return result;
797
798
799
800
801
802
803
804
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
838
839
840
841
}

/**
 * rtp_source_set_rtp_from:
 * @src: an #RTPSource
 * @address: the RTP address to set
 *
 * Set that @src is receiving RTP packets from @address. This is used for
 * collistion checking.
 */
void
rtp_source_set_rtp_from (RTPSource * src, GstNetAddress * address)
{
  g_return_if_fail (RTP_IS_SOURCE (src));

  src->have_rtp_from = TRUE;
  memcpy (&src->rtp_from, address, sizeof (GstNetAddress));
}

/**
 * rtp_source_set_rtcp_from:
 * @src: an #RTPSource
 * @address: the RTCP address to set
 *
 * Set that @src is receiving RTCP packets from @address. This is used for
 * collistion checking.
 */
void
rtp_source_set_rtcp_from (RTPSource * src, GstNetAddress * address)
{
  g_return_if_fail (RTP_IS_SOURCE (src));

  src->have_rtcp_from = TRUE;
  memcpy (&src->rtcp_from, address, sizeof (GstNetAddress));
}

static GstFlowReturn
push_packet (RTPSource * src, GstBuffer * buffer)
{
  GstFlowReturn ret = GST_FLOW_OK;

  /* push queued packets first if any */
  while (!g_queue_is_empty (src->packets)) {
    GstBuffer *buffer = GST_BUFFER_CAST (g_queue_pop_head (src->packets));

842
    GST_LOG ("pushing queued packet");
843
844
845
846
847
    if (src->callbacks.push_rtp)
      src->callbacks.push_rtp (src, buffer, src->user_data);
    else
      gst_buffer_unref (buffer);
  }
848
  GST_LOG ("pushing new packet");
849
850
851
852
853
854
855
856
857
858
859
860
  /* push packet */
  if (src->callbacks.push_rtp)
    ret = src->callbacks.push_rtp (src, buffer, src->user_data);
  else
    gst_buffer_unref (buffer);

  return ret;
}

static gint
get_clock_rate (RTPSource * src, guint8 payload)
{
861
862
863
864
865
866
  if (src->payload == -1) {
    /* first payload received, nothing was in the caps, lock on to this payload */
    src->payload = payload;
    GST_DEBUG ("first payload %d", payload);
  } else if (payload != src->payload) {
    /* we have a different payload than before, reset the clock-rate */
867
868
869
870
871
872
    GST_DEBUG ("new payload %d", payload);
    src->payload = payload;
    src->clock_rate = -1;
    src->stats.transit = -1;
  }

873
  if (src->clock_rate == -1) {
874
875
876
877
878
    gint clock_rate = -1;

    if (src->callbacks.clock_rate)
      clock_rate = src->callbacks.clock_rate (src, payload, src->user_data);

879
    GST_DEBUG ("got clock-rate %d", clock_rate);
880
881
882
883
884
885

    src->clock_rate = clock_rate;
  }
  return src->clock_rate;
}

886
887
888
889
890
/* Jitter is the variation in the delay of received packets in a flow. It is
 * measured by comparing the interval when RTP packets were sent to the interval
 * at which they were received. For instance, if packet #1 and packet #2 leave
 * 50 milliseconds apart and arrive 60 milliseconds apart, then the jitter is 10
 * milliseconds. */
891
892
893
894
static void
calculate_jitter (RTPSource * src, GstBuffer * buffer,
    RTPArrivalStats * arrival)
{
895
  GstClockTime running_time;
896
897
898
899
  guint32 rtparrival, transit, rtptime;
  gint32 diff;
  gint clock_rate;
  guint8 pt;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
900
  GstRTPBuffer rtp;
901
902

  /* get arrival time */
903
  if ((running_time = arrival->running_time) == GST_CLOCK_TIME_NONE)
904
905
    goto no_time;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
906
907
  gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
  pt = gst_rtp_buffer_get_payload_type (&rtp);
908

909
  GST_LOG ("SSRC %08x got payload %d", src->ssrc, pt);
910

911
  /* get clockrate */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
912
913
  if ((clock_rate = get_clock_rate (src, pt)) == -1) {
    gst_rtp_buffer_unmap (&rtp);
914
    goto no_clock_rate;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
915
  }
916

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
917
  rtptime = gst_rtp_buffer_get_timestamp (&rtp);
918

919
920
  /* convert arrival time to RTP timestamp units, truncate to 32 bits, we don't
   * care about the absolute value, just the difference. */
921
  rtparrival = gst_util_uint64_scale_int (running_time, clock_rate, GST_SECOND);
922
923
924

  /* transit time is difference with RTP timestamp */
  transit = rtparrival - rtptime;
925
926
927
928
929
930
931
932

  /* get ABS diff with previous transit time */
  if (src->stats.transit != -1) {
    if (transit > src->stats.transit)
      diff = transit - src->stats.transit;
    else
      diff = src->stats.transit - transit;
  } else
933
    diff = 0;
934

935
  src->stats.transit = transit;
936
937

  /* update jitter, the value we store is scaled up so we can keep precision. */
938
939
940
941
942
  src->stats.jitter += diff - ((src->stats.jitter + 8) >> 4);

  src->stats.prev_rtptime = src->stats.last_rtptime;
  src->stats.last_rtptime = rtparrival;

943
  GST_LOG ("rtparrival %u, rtptime %u, clock-rate %d, diff %d, jitter: %f",
944
      rtparrival, rtptime, clock_rate, diff, (src->stats.jitter) / 16.0);
945

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
946
  gst_rtp_buffer_unmap (&rtp);
947
948
949
950
951
  return;

  /* ERRORS */
no_time:
  {
952
    GST_WARNING ("cannot get current running_time");
953
954
955
956
957
958
959
960
961
    return;
  }
no_clock_rate:
  {
    GST_WARNING ("cannot get clock-rate for pt %d", pt);
    return;
  }
}

962
963
964
965
966
967
968
969
970
971
972
973
static void
init_seq (RTPSource * src, guint16 seq)
{
  src->stats.base_seq = seq;
  src->stats.max_seq = seq;
  src->stats.bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */
  src->stats.cycles = 0;
  src->stats.packets_received = 0;
  src->stats.octets_received = 0;
  src->stats.bytes_received = 0;
  src->stats.prev_received = 0;
  src->stats.prev_expected = 0;
974
975

  GST_DEBUG ("base_seq %d", seq);
976
977
}

978
979
#define BITRATE_INTERVAL (2 * GST_SECOND)

980
981
982
983
984
985
986
987
988
static void
do_bitrate_estimation (RTPSource * src, GstClockTime running_time,
    guint64 * bytes_handled)
{
  guint64 elapsed;

  if (src->prev_rtime) {
    elapsed = running_time - src->prev_rtime;

989
    if (elapsed > BITRATE_INTERVAL) {
990
991
      guint64 rate;

992
      rate = gst_util_uint64_scale (*bytes_handled, 8 * GST_SECOND, elapsed);
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011

      GST_LOG ("Elapsed %" G_GUINT64_FORMAT ", bytes %" G_GUINT64_FORMAT
          ", rate %" G_GUINT64_FORMAT, elapsed, *bytes_handled, rate);

      if (src->bitrate == 0)
        src->bitrate = rate;
      else
        src->bitrate = ((src->bitrate * 3) + rate) / 4;

      src->prev_rtime = running_time;
      *bytes_handled = 0;
    }
  } else {
    GST_LOG ("Reset bitrate measurement");
    src->prev_rtime = running_time;
    src->bitrate = 0;
  }
}

1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
/**
 * rtp_source_process_rtp:
 * @src: an #RTPSource
 * @buffer: an RTP buffer
 *
 * Let @src handle the incomming RTP @buffer.
 *
 * Returns: a #GstFlowReturn.
 */
GstFlowReturn
rtp_source_process_rtp (RTPSource * src, GstBuffer * buffer,
    RTPArrivalStats * arrival)
{
  GstFlowReturn result = GST_FLOW_OK;
1026
1027
  guint16 seqnr, udelta;
  RTPSourceStats *stats;
Laurent Glayal's avatar
Laurent Glayal committed
1028
  guint16 expected;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1029
  GstRTPBuffer rtp;
1030
1031
1032
1033

  g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR);
  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);

1034
1035
  stats = &src->stats;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1036
1037
1038
  gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
  seqnr = gst_rtp_buffer_get_seq (&rtp);
  gst_rtp_buffer_unmap (&rtp);
1039

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1040
1041
1042
1043
  /* FIXME-0.11
   * would be nice to be able to pass along with buffer */
  g_assert_not_reached ();
  /* rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer)); */
1044

1045
  if (stats->cycles == -1) {
1046
    GST_DEBUG ("received first buffer");
1047
1048
1049
1050
1051
1052
1053
1054
    /* first time we heard of this source */
    init_seq (src, seqnr);
    src->stats.max_seq = seqnr - 1;
    src->probation = RTP_DEFAULT_PROBATION;
  }

  udelta = seqnr - stats->max_seq;

1055
1056
  /* if we are still on probation, check seqnum */
  if (src->probation) {
1057
    expected = src->stats.max_seq + 1;
1058
1059
1060
1061
1062

    /* when in probation, we require consecutive seqnums */
    if (seqnr == expected) {
      /* expected packet */
      GST_DEBUG ("probation: seqnr %d == expected %d", seqnr, expected);
1063
1064
1065
      src->probation--;
      src->stats.max_seq = seqnr;
      if (src->probation == 0) {
1066
        GST_DEBUG ("probation done!");
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
        init_seq (src, seqnr);
      } else {
        GstBuffer *q;

        GST_DEBUG ("probation %d: queue buffer", src->probation);
        /* when still in probation, keep packets in a list. */
        g_queue_push_tail (src->packets, buffer);
        /* remove packets from queue if there are too many */
        while (g_queue_get_length (src->packets) > RTP_MAX_PROBATION_LEN) {
          q = g_queue_pop_head (src->packets);
1077
          gst_buffer_unref (q);
1078
1079
1080
        }
        goto done;
      }
1081
    } else {
Laurent Glayal's avatar
Laurent Glayal committed
1082
1083
      /* unexpected seqnum in probation */
      goto probation_seqnum;
1084
    }
1085
1086
1087
1088
  } else if (udelta < RTP_MAX_DROPOUT) {
    /* in order, with permissible gap */
    if (seqnr < stats->max_seq) {
      /* sequence number wrapped - count another 64K cycle. */
1089
      stats->cycles += RTP_SEQ_MOD;
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
    }
    stats->max_seq = seqnr;
  } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
    /* the sequence number made a very large jump */
    if (seqnr == stats->bad_seq) {
      /* two sequential packets -- assume that the other side
       * restarted without telling us so just re-sync
       * (i.e., pretend this was the first packet).  */
      init_seq (src, seqnr);
    } else {
      /* unacceptable jump */
      stats->bad_seq = (seqnr + 1) & (RTP_SEQ_MOD - 1);
      goto bad_sequence;
1103
1104
    }
  } else {
1105
    /* duplicate or reordered packet, will be filtered by jitterbuffer. */
1106
    GST_WARNING ("duplicate or reordered packet");
1107
  }
1108

1109
1110
1111
  src->stats.octets_received += arrival->payload_len;
  src->stats.bytes_received += arrival->bytes;
  src->stats.packets_received++;
1112
  /* for the bitrate estimation */
1113
  src->bytes_received += arrival->payload_len;
1114
1115
1116
  /* the source that sent the packet must be a sender */
  src->is_sender = TRUE;
  src->validated = TRUE;
1117

1118
  do_bitrate_estimation (src, arrival->running_time, &src->bytes_received);
1119

1120
  GST_LOG ("seq %d, PC: %" G_GUINT64_FORMAT ", OC: %" G_GUINT64_FORMAT,
1121
      seqnr, src->stats.packets_received, src->stats.octets_received);
1122

1123
  /* calculate jitter for the stats */
1124
1125
1126
1127
1128
1129
  calculate_jitter (src, buffer, arrival);

  /* we're ready to push the RTP packet now */
  result = push_packet (src, buffer);

done:
1130
  return result;
1131
1132
1133
1134
1135

  /* ERRORS */
bad_sequence:
  {
    GST_WARNING ("unacceptable seqnum received");
1136
    gst_buffer_unref (buffer);
1137
1138
    return GST_FLOW_OK;
  }
Laurent Glayal's avatar
Laurent Glayal committed
1139
1140
1141
1142
1143
1144
1145
1146
probation_seqnum:
  {
    GST_WARNING ("probation: seqnr %d != expected %d", seqnr, expected);
    src->probation = RTP_DEFAULT_PROBATION;
    src->stats.max_seq = seqnr;
    gst_buffer_unref (buffer);
    return GST_FLOW_OK;
  }
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
}

/**
 * rtp_source_process_bye:
 * @src: an #RTPSource
 * @reason: the reason for leaving
 *
 * Notify @src that a BYE packet has been received. This will make the source
 * inactive.
 */
void
rtp_source_process_bye (RTPSource * src, const gchar * reason)
{
  g_return_if_fail (RTP_IS_SOURCE (src));

  GST_DEBUG ("marking SSRC %08x as BYE, reason: %s", src->ssrc,
      GST_STR_NULL (reason));

  /* copy the reason and mark as received_bye */
  g_free (src->bye_reason);
  src->bye_reason = g_strdup (reason);
  src->received_bye = TRUE;
}

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1171
1172
static gboolean
set_ssrc (GstBuffer ** buffer, guint idx, RTPSource * src)
1173
{
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1174
1175
  GstRTPBuffer rtp;

1176
  *buffer = gst_buffer_make_writable (*buffer);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1177
1178
1179
1180
  gst_rtp_buffer_map (*buffer, GST_MAP_WRITE, &rtp);
  gst_rtp_buffer_set_ssrc (&rtp, src->ssrc);
  gst_rtp_buffer_unmap (&rtp);
  return TRUE;
1181
1182
}

1183
1184
1185
/**
 * rtp_source_send_rtp:
 * @src: an #RTPSource
1186
1187
 * @data: an RTP buffer or a list of RTP buffers
 * @is_list: if @data is a buffer or list
1188
 * @running_time: the running time of @data
1189
 *
1190
1191
1192
 * Send @data (an RTP buffer or list of buffers) originating from @src.
 * This will make @src a sender. This function takes ownership of @data and
 * modifies the SSRC in the RTP packet to that of @src when needed.
1193
1194
1195
1196
 *
 * Returns: a #GstFlowReturn.
 */
GstFlowReturn
1197
rtp_source_send_rtp (RTPSource * src, gpointer data, gboolean is_list,
1198
    GstClockTime running_time)
1199
{
1200
  GstFlowReturn result;
1201
  guint len;
1202
1203
  guint32 rtptime;
  guint64 ext_rtptime;
1204
  guint64 rt_diff, rtp_diff;
1205
1206
1207
1208
  GstBufferList *list = NULL;
  GstBuffer *buffer = NULL;
  guint packets;
  guint32 ssrc;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1209
  GstRTPBuffer rtp;
1210
1211

  g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR);
1212
  g_return_val_if_fail (is_list || GST_IS_BUFFER (data), GST_FLOW_ERROR);
1213

1214
1215
  if (is_list) {
    list = GST_BUFFER_LIST_CAST (data);
1216

1217
1218
    /* We can grab the caps from the first group, since all
     * groups of a buffer list have same caps. */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1219
    buffer = gst_buffer_list_get (list, 0);
1220
1221
1222
1223
1224
    if (!buffer)
      goto no_buffer;
  } else {
    buffer = GST_BUFFER_CAST (data);
  }
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1225
1226
1227
1228

  /* FIXME-0.11 */
  g_assert_not_reached ();
  /* rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer)); */
1229

1230
1231
1232
  /* we are a sender now */
  src->is_sender = TRUE;

1233
  if (is_list) {
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1234
1235
    gint i;

1236
    /* Each group makes up a network packet. */
Wim Taymans's avatar
Wim Taymans committed
1237
    packets = gst_buffer_list_length (list);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1238
1239
1240
1241
1242
1243
1244
    for (i = 0, len = 0; i < packets; i++) {
      gst_rtp_buffer_map (gst_buffer_list_get (list, i), GST_MAP_READ, &rtp);
      len += gst_rtp_buffer_get_payload_len (&rtp);
      gst_rtp_buffer_unmap (&rtp);
    }
    /* subsequent info taken from first list member */
    gst_rtp_buffer_map (gst_buffer_list_get (list, 0), GST_MAP_READ, &rtp);
1245
1246
  } else {
    packets = 1;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1247
1248
    gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
    len = gst_rtp_buffer_get_payload_len (&rtp);
1249
1250
  }

1251
  /* update stats for the SR */
1252
  src->stats.packets_sent += packets;
1253
  src->stats.octets_sent += len;
1254
1255
  src->bytes_sent += len;

1256
  do_bitrate_estimation (src, running_time, &src->bytes_sent);
1257

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1258
  rtptime = gst_rtp_buffer_get_timestamp (&rtp);
1259
1260
1261
  ext_rtptime = src->last_rtptime;
  ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);

1262
1263
  GST_LOG ("SSRC %08x, RTP %" G_GUINT64_FORMAT ", running_time %"
      GST_TIME_FORMAT, src->ssrc, ext_rtptime, GST_TIME_ARGS (running_time));
1264
1265
1266

  if (ext_rtptime > src->last_rtptime) {
    rtp_diff = ext_rtptime - src->last_rtptime;
1267
    rt_diff = running_time - src->last_rtime;
1268
1269
1270
1271

    /* calc the diff so we can detect drift at the sender. This can also be used
     * to guestimate the clock rate if the NTP time is locked to the RTP
     * timestamps (as is the case when the capture device is providing the clock). */
1272
1273
    GST_LOG ("SSRC %08x, diff RTP %" G_GUINT64_FORMAT ", diff running_time %"
        GST_TIME_FORMAT, src->ssrc, rtp_diff, GST_TIME_ARGS (rt_diff));
1274
1275
  }

1276
  /* we keep track of the last received RTP timestamp and the corresponding
1277
1278
   * buffer running_time so that we can use this info when constructing SR reports */
  src->last_rtime = running_time;
1279
  src->last_rtptime = ext_rtptime;
1280

1281
  /* push packet */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1282
1283
  if (!src->callbacks.push_rtp) {
    gst_rtp_buffer_unmap (&rtp);
1284
1285
1286
    goto no_callback;
  }

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1287
1288
1289
  ssrc = gst_rtp_buffer_get_ssrc (&rtp);
  gst_rtp_buffer_unmap (&rtp);

1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
  if (ssrc != src->ssrc) {
    /* the SSRC of the packet is not correct, make a writable buffer and
     * update the SSRC. This could involve a complete copy of the packet when
     * it is not writable. Usually the payloader will use caps negotiation to
     * get the correct SSRC from the session manager before pushing anything. */

    /* FIXME, we don't want to warn yet because we can't inform any payloader
     * of the changes SSRC yet because we don't implement pad-alloc. */
    GST_LOG ("updating SSRC from %08x to %08x, fix the payloader", ssrc,
        src->ssrc);

    if (is_list) {
      list = gst_buffer_list_make_writable (list);
      gst_buffer_list_foreach (list, (GstBufferListFunc) set_ssrc, src);
    } else {
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
1305
      set_ssrc (&buffer, 0, src);
1306
    }
1307
  }
1308
1309
1310
1311
  GST_LOG ("pushing RTP %s %" G_GUINT64_FORMAT, is_list ? "list" : "packet",
      src->stats.packets_sent);

  result = src->callbacks.push_rtp (src, data, src->user_data);
1312
1313

  return result;
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327

  /* ERRORS */
no_buffer:
  {
    GST_WARNING ("no buffers in buffer list");
    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
    return GST_FLOW_OK;
  }
no_callback:
  {
    GST_WARNING ("no callback installed, dropping packet");
    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
    return GST_FLOW_OK;
  }
1328
1329
1330
1331
1332
}

/**
 * rtp_source_process_sr:
 * @src: an #RTPSource
1333
 * @time: time of packet arrival
Wim Taymans's avatar
Wim Taymans committed
1334
 * @ntptime: the NTP time in 32.32 fixed point
1335
1336
1337
1338
1339
1340
1341
 * @rtptime: the RTP time
 * @packet_count: the packet count
 * @octet_count: the octect count
 *
 * Update the sender report in @src.
 */
void
1342
1343
rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime,
    guint32 rtptime, guint32 packet_count, guint32 octet_count)
1344
1345
1346
1347
1348
1349
{
  RTPSenderReport *curr;
  gint curridx;

  g_return_if_fail (RTP_IS_SOURCE (src));

1350
1351
1352
1353
  GST_DEBUG ("got SR packet: SSRC %08x, NTP %08x:%08x, RTP %" G_GUINT32_FORMAT
      ", PC %" G_GUINT32_FORMAT ", OC %" G_GUINT32_FORMAT, src->ssrc,
      (guint32) (ntptime >> 32), (guint32) (ntptime & 0xffffffff), rtptime,
      packet_count, octet_count);
1354
1355
1356
1357

  curridx = src->stats.curr_sr ^ 1;
  curr = &src->stats.sr[curridx];

1358
1359
1360
  /* this is a sender now */
  src->is_sender = TRUE;

1361
1362
1363
1364
1365
1366
  /* update current */
  curr->is_valid = TRUE;
  curr->ntptime = ntptime;
  curr->rtptime = rtptime;
  curr->packet_count = packet_count;
  curr->octet_count = octet_count;
1367
  curr->time = time;
1368
1369
1370

  /* make current */
  src->stats.curr_sr = curridx;
1371
1372
1373

  src->stats.prev_rtcptime = src->stats.last_rtcptime;
  src->stats.last_rtcptime = time;
1374
1375
1376
1377
1378
}

/**
 * rtp_source_process_rb:
 * @src: an #RTPSource
1379
 * @ntpnstime: the current time in nanoseconds since 1970
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
 * @fractionlost: fraction lost since last SR/RR
 * @packetslost: the cumululative number of packets lost
 * @exthighestseq: the extended last sequence number received
 * @jitter: the interarrival jitter
 * @lsr: the last SR packet from this source
 * @dlsr: the delay since last SR packet
 *
 * Update the report block in @src.
 */
void
Wim Taymans's avatar
Wim Taymans committed
1390
rtp_source_process_rb (RTPSource * src, guint64 ntpnstime,
1391
1392
    guint8 fractionlost, gint32 packetslost, guint32 exthighestseq,
    guint32 jitter, guint32 lsr, guint32 dlsr)
1393
1394
1395
{
  RTPReceiverReport *curr;
  gint curridx;
1396
  guint32 ntp, A;
1397
  guint64 f_ntp;
1398
1399
1400

  g_return_if_fail (RTP_IS_SOURCE (src));

1401
1402
1403
1404
  GST_DEBUG ("got RB packet: SSRC %08x, FL %2x, PL %d, HS %" G_GUINT32_FORMAT
      ", jitter %" G_GUINT32_FORMAT ", LSR %04x:%04x, DLSR %04x:%04x",
      src->ssrc, fractionlost, packetslost, exthighestseq, jitter, lsr >> 16,
      lsr & 0xffff, dlsr >> 16, dlsr & 0xffff);
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417

  curridx = src->stats.curr_rr ^ 1;
  curr = &src->stats.rr[curridx];

  /* update current */
  curr->is_valid = TRUE;
  curr->fractionlost = fractionlost;
  curr->packetslost = packetslost;
  curr->exthighestseq = exthighestseq;
  curr->jitter = jitter;
  curr->lsr = lsr;
  curr->dlsr = dlsr;