gstrtpbasedepayload.c 25.7 KB
Newer Older
1
/* GStreamer
Stefan Kost's avatar
Stefan Kost committed
2
 * Copyright (C) <2005> Philippe Khalaf <burger@speedy.org>
3
 * Copyright (C) <2005> Nokia Corporation <kai.vehmanen@nokia.com>
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
17 18
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
19 20
 */

21
/**
22
 * SECTION:gstrtpbasedepayload
23 24 25 26 27
 * @short_description: Base class for RTP depayloader
 *
 * Provides a base class for RTP depayloaders
 */

28
#include "gstrtpbasedepayload.h"
29

30 31
GST_DEBUG_CATEGORY_STATIC (rtpbasedepayload_debug);
#define GST_CAT_DEFAULT (rtpbasedepayload_debug)
32

Wim Taymans's avatar
Wim Taymans committed
33 34
#define GST_RTP_BASE_DEPAYLOAD_GET_PRIVATE(obj)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_BASE_DEPAYLOAD, GstRTPBaseDepayloadPrivate))
35

Wim Taymans's avatar
Wim Taymans committed
36
struct _GstRTPBaseDepayloadPrivate
37
{
38 39 40 41
  GstClockTime npt_start;
  GstClockTime npt_stop;
  gdouble play_speed;
  gdouble play_scale;
42
  guint clock_base;
43

44
  gboolean discont;
Wim Taymans's avatar
Wim Taymans committed
45 46
  GstClockTime pts;
  GstClockTime dts;
47
  GstClockTime duration;
48

Wim Taymans's avatar
Wim Taymans committed
49 50
  guint32 last_seqnum;
  guint32 last_rtptime;
51
  guint32 next_seqnum;
52 53

  gboolean negotiated;
54 55

  GstCaps *last_caps;
56
  GstEvent *segment_event;
57 58
};

59 60 61 62 63 64 65 66 67
/* Filter signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

enum
{
68
  PROP_0,
Wim Taymans's avatar
Wim Taymans committed
69
  PROP_STATS,
70
  PROP_LAST
71 72
};

Wim Taymans's avatar
Wim Taymans committed
73 74
static void gst_rtp_base_depayload_finalize (GObject * object);
static void gst_rtp_base_depayload_set_property (GObject * object,
75
    guint prop_id, const GValue * value, GParamSpec * pspec);
Wim Taymans's avatar
Wim Taymans committed
76
static void gst_rtp_base_depayload_get_property (GObject * object,
77 78
    guint prop_id, GValue * value, GParamSpec * pspec);

Wim Taymans's avatar
Wim Taymans committed
79
static GstFlowReturn gst_rtp_base_depayload_chain (GstPad * pad,
Wim Taymans's avatar
Wim Taymans committed
80
    GstObject * parent, GstBuffer * in);
Wim Taymans's avatar
Wim Taymans committed
81
static gboolean gst_rtp_base_depayload_handle_sink_event (GstPad * pad,
Wim Taymans's avatar
Wim Taymans committed
82
    GstObject * parent, GstEvent * event);
83

Wim Taymans's avatar
Wim Taymans committed
84
static GstStateChangeReturn gst_rtp_base_depayload_change_state (GstElement *
85
    element, GstStateChange transition);
86

Wim Taymans's avatar
Wim Taymans committed
87
static gboolean gst_rtp_base_depayload_packet_lost (GstRTPBaseDepayload *
88
    filter, GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
89
static gboolean gst_rtp_base_depayload_handle_event (GstRTPBaseDepayload *
90
    filter, GstEvent * event);
91

92
static GstElementClass *parent_class = NULL;
Wim Taymans's avatar
Wim Taymans committed
93
static void gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass *
94
    klass);
95
static void gst_rtp_base_depayload_init (GstRTPBaseDepayload * rtpbasepayload,
Wim Taymans's avatar
Wim Taymans committed
96
    GstRTPBaseDepayloadClass * klass);
97 98
static GstEvent *create_segment_event (GstRTPBaseDepayload * filter,
    guint rtptime, GstClockTime position);
99 100

GType
Wim Taymans's avatar
Wim Taymans committed
101
gst_rtp_base_depayload_get_type (void)
102
{
Wim Taymans's avatar
Wim Taymans committed
103
  static GType rtp_base_depayload_type = 0;
104

Wim Taymans's avatar
Wim Taymans committed
105 106 107
  if (g_once_init_enter ((gsize *) & rtp_base_depayload_type)) {
    static const GTypeInfo rtp_base_depayload_info = {
      sizeof (GstRTPBaseDepayloadClass),
108 109
      NULL,
      NULL,
Wim Taymans's avatar
Wim Taymans committed
110
      (GClassInitFunc) gst_rtp_base_depayload_class_init,
111 112
      NULL,
      NULL,
Wim Taymans's avatar
Wim Taymans committed
113
      sizeof (GstRTPBaseDepayload),
114
      0,
Wim Taymans's avatar
Wim Taymans committed
115
      (GInstanceInitFunc) gst_rtp_base_depayload_init,
116 117
    };

Wim Taymans's avatar
Wim Taymans committed
118 119 120
    g_once_init_leave ((gsize *) & rtp_base_depayload_type,
        g_type_register_static (GST_TYPE_ELEMENT, "GstRTPBaseDepayload",
            &rtp_base_depayload_info, G_TYPE_FLAG_ABSTRACT));
121
  }
Wim Taymans's avatar
Wim Taymans committed
122
  return rtp_base_depayload_type;
123 124 125
}

static void
Wim Taymans's avatar
Wim Taymans committed
126
gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass * klass)
127 128
{
  GObjectClass *gobject_class;
129
  GstElementClass *gstelement_class;
130 131

  gobject_class = G_OBJECT_CLASS (klass);
132
  gstelement_class = (GstElementClass *) klass;
133
  parent_class = g_type_class_peek_parent (klass);
134

Wim Taymans's avatar
Wim Taymans committed
135
  g_type_class_add_private (klass, sizeof (GstRTPBaseDepayloadPrivate));
136

Wim Taymans's avatar
Wim Taymans committed
137 138 139
  gobject_class->finalize = gst_rtp_base_depayload_finalize;
  gobject_class->set_property = gst_rtp_base_depayload_set_property;
  gobject_class->get_property = gst_rtp_base_depayload_get_property;
140

Wim Taymans's avatar
Wim Taymans committed
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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

  /**
   * GstRTPBaseDepayload:stats:
   *
   * Various depayloader statistics retrieved atomically (and are therefore
   * synchroized with each other). This property return a GstStructure named
   * application/x-rtp-depayload-stats containing the following fields relating to
   * the last processed buffer and current state of the stream being depayloaded:
   *
   * <variablelist>
   *   <varlistentry>
   *     <term>clock-rate</term>
   *     <listitem><para>#G_TYPE_UINT, clock-rate of the
   *     stream</para></listitem>
   *   </varlistentry>
   *   <varlistentry>
   *     <term>npt-start</term>
   *     <listitem><para>#G_TYPE_UINT64, time of playback start
   *     </para></listitem>
   *   </varlistentry>
   *   <varlistentry>
   *     <term>npt-stop</term>
   *     <listitem><para>#G_TYPE_UINT64, time of playback stop
   *     </para></listitem>
   *   </varlistentry>
   *   <varlistentry>
   *     <term>play-speed</term>
   *     <listitem><para>#G_TYPE_DOUBLE, the playback speed
   *     </para></listitem>
   *   </varlistentry>
   *   <varlistentry>
   *     <term>play-scale</term>
   *     <listitem><para>#G_TYPE_DOUBLE, the playback scale
   *     </para></listitem>
   *   </varlistentry>
   *   <varlistentry>
   *     <term>running-time-dts</term>
   *     <listitem><para>#G_TYPE_UINT64, the last running-time of the
   *      last DTS
   *     </para></listitem>
   *   </varlistentry>
   *   <varlistentry>
   *     <term>running-time-pts</term>
   *     <listitem><para>#G_TYPE_UINT64, the last running-time of the
   *      last PTS
   *     </para></listitem>
   *   </varlistentry>
   *   <varlistentry>
   *     <term>seqnum</term>
   *     <listitem><para>#G_TYPE_UINT, the last seen seqnum
   *     </para></listitem>
   *   </varlistentry>
   *   <varlistentry>
   *     <term>timestamp</term>
   *     <listitem><para>#G_TYPE_UINT, the last seen RTP timestamp
   *     </para></listitem>
   *   </varlistentry>
   * </variablelist>
   **/
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_STATS,
      g_param_spec_boxed ("stats", "Statistics", "Various statistics",
          GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

Wim Taymans's avatar
Wim Taymans committed
204
  gstelement_class->change_state = gst_rtp_base_depayload_change_state;
205

Wim Taymans's avatar
Wim Taymans committed
206 207
  klass->packet_lost = gst_rtp_base_depayload_packet_lost;
  klass->handle_event = gst_rtp_base_depayload_handle_event;
208

209
  GST_DEBUG_CATEGORY_INIT (rtpbasedepayload_debug, "rtpbasedepayload", 0,
210 211 212 213
      "Base class for RTP Depayloaders");
}

static void
Wim Taymans's avatar
Wim Taymans committed
214 215
gst_rtp_base_depayload_init (GstRTPBaseDepayload * filter,
    GstRTPBaseDepayloadClass * klass)
216 217
{
  GstPadTemplate *pad_template;
Wim Taymans's avatar
Wim Taymans committed
218
  GstRTPBaseDepayloadPrivate *priv;
219

Wim Taymans's avatar
Wim Taymans committed
220
  priv = GST_RTP_BASE_DEPAYLOAD_GET_PRIVATE (filter);
221
  filter->priv = priv;
222

223
  GST_DEBUG_OBJECT (filter, "init");
224 225

  pad_template =
226
      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "sink");
227 228
  g_return_if_fail (pad_template != NULL);
  filter->sinkpad = gst_pad_new_from_template (pad_template, "sink");
Wim Taymans's avatar
Wim Taymans committed
229
  gst_pad_set_chain_function (filter->sinkpad, gst_rtp_base_depayload_chain);
230
  gst_pad_set_event_function (filter->sinkpad,
Wim Taymans's avatar
Wim Taymans committed
231
      gst_rtp_base_depayload_handle_sink_event);
232 233 234
  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);

  pad_template =
235
      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src");
236 237
  g_return_if_fail (pad_template != NULL);
  filter->srcpad = gst_pad_new_from_template (pad_template, "src");
238
  gst_pad_use_fixed_caps (filter->srcpad);
239 240
  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);

Wim Taymans's avatar
Wim Taymans committed
241 242 243 244
  priv->npt_start = 0;
  priv->npt_stop = -1;
  priv->play_speed = 1.0;
  priv->play_scale = 1.0;
245
  priv->clock_base = -1;
Wim Taymans's avatar
Wim Taymans committed
246 247 248 249
  priv->dts = -1;
  priv->pts = -1;
  priv->duration = -1;

250
  gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
251 252 253
}

static void
Wim Taymans's avatar
Wim Taymans committed
254
gst_rtp_base_depayload_finalize (GObject * object)
255
{
256
  G_OBJECT_CLASS (parent_class)->finalize (object);
257 258 259
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
260
gst_rtp_base_depayload_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps)
261
{
Wim Taymans's avatar
Wim Taymans committed
262 263
  GstRTPBaseDepayloadClass *bclass;
  GstRTPBaseDepayloadPrivate *priv;
264
  gboolean res;
265 266
  GstStructure *caps_struct;
  const GValue *value;
267

268
  priv = filter->priv;
269

Wim Taymans's avatar
Wim Taymans committed
270
  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
271

272 273 274 275 276 277 278 279 280 281 282
  GST_DEBUG_OBJECT (filter, "Set caps %" GST_PTR_FORMAT, caps);

  if (priv->last_caps) {
    if (gst_caps_is_equal (priv->last_caps, caps)) {
      res = TRUE;
      goto caps_not_changed;
    } else {
      gst_caps_unref (priv->last_caps);
      priv->last_caps = NULL;
    }
  }
283 284 285

  caps_struct = gst_caps_get_structure (caps, 0);

286 287 288 289 290 291
  /* get other values for newsegment */
  value = gst_structure_get_value (caps_struct, "npt-start");
  if (value && G_VALUE_HOLDS_UINT64 (value))
    priv->npt_start = g_value_get_uint64 (value);
  else
    priv->npt_start = 0;
292
  GST_DEBUG_OBJECT (filter, "NPT start %" G_GUINT64_FORMAT, priv->npt_start);
293 294 295 296 297 298 299

  value = gst_structure_get_value (caps_struct, "npt-stop");
  if (value && G_VALUE_HOLDS_UINT64 (value))
    priv->npt_stop = g_value_get_uint64 (value);
  else
    priv->npt_stop = -1;

300
  GST_DEBUG_OBJECT (filter, "NPT stop %" G_GUINT64_FORMAT, priv->npt_stop);
301

302 303 304 305 306 307 308 309 310 311 312 313
  value = gst_structure_get_value (caps_struct, "play-speed");
  if (value && G_VALUE_HOLDS_DOUBLE (value))
    priv->play_speed = g_value_get_double (value);
  else
    priv->play_speed = 1.0;

  value = gst_structure_get_value (caps_struct, "play-scale");
  if (value && G_VALUE_HOLDS_DOUBLE (value))
    priv->play_scale = g_value_get_double (value);
  else
    priv->play_scale = 1.0;

314 315 316 317 318 319
  value = gst_structure_get_value (caps_struct, "clock-base");
  if (value && G_VALUE_HOLDS_UINT (value))
    priv->clock_base = g_value_get_uint (value);
  else
    priv->clock_base = -1;

320
  if (bclass->set_caps) {
321
    res = bclass->set_caps (filter, caps);
322 323 324 325 326
    if (!res) {
      GST_WARNING_OBJECT (filter, "Subclass rejected caps %" GST_PTR_FORMAT,
          caps);
    }
  } else {
327
    res = TRUE;
328
  }
329

330 331
  priv->negotiated = res;

332 333 334
  if (priv->negotiated)
    priv->last_caps = gst_caps_ref (caps);

335
  return res;
336 337 338 339 340 341

caps_not_changed:
  {
    GST_DEBUG_OBJECT (filter, "Caps did not change");
    return res;
  }
342 343 344
}

static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
345
gst_rtp_base_depayload_chain (GstPad * pad, GstObject * parent, GstBuffer * in)
346
{
Wim Taymans's avatar
Wim Taymans committed
347 348 349
  GstRTPBaseDepayload *filter;
  GstRTPBaseDepayloadPrivate *priv;
  GstRTPBaseDepayloadClass *bclass;
350
  GstFlowReturn ret = GST_FLOW_OK;
351
  GstBuffer *out_buf;
Wim Taymans's avatar
Wim Taymans committed
352
  GstClockTime pts, dts;
353
  guint16 seqnum;
354
  guint32 rtptime;
355
  gboolean discont, buf_discont;
356
  gint gap;
357
  GstRTPBuffer rtp = { NULL };
358

Wim Taymans's avatar
Wim Taymans committed
359
  filter = GST_RTP_BASE_DEPAYLOAD (parent);
360 361 362 363 364
  priv = filter->priv;

  /* we must have a setcaps first */
  if (G_UNLIKELY (!priv->negotiated))
    goto not_negotiated;
365

366
  if (G_UNLIKELY (!gst_rtp_buffer_map (in, GST_MAP_READ, &rtp)))
367 368
    goto invalid_buffer;

369
  buf_discont = GST_BUFFER_IS_DISCONT (in);
370

Wim Taymans's avatar
Wim Taymans committed
371 372
  pts = GST_BUFFER_PTS (in);
  dts = GST_BUFFER_DTS (in);
373 374
  /* convert to running_time and save the timestamp, this is the timestamp
   * we put on outgoing buffers. */
Wim Taymans's avatar
Wim Taymans committed
375 376 377 378
  pts = gst_segment_to_running_time (&filter->segment, GST_FORMAT_TIME, pts);
  dts = gst_segment_to_running_time (&filter->segment, GST_FORMAT_TIME, dts);
  priv->pts = pts;
  priv->dts = dts;
379
  priv->duration = GST_BUFFER_DURATION (in);
380

Wim Taymans's avatar
Wim Taymans committed
381 382 383 384
  seqnum = gst_rtp_buffer_get_seq (&rtp);
  rtptime = gst_rtp_buffer_get_timestamp (&rtp);
  gst_rtp_buffer_unmap (&rtp);

Wim Taymans's avatar
Wim Taymans committed
385 386 387
  priv->last_seqnum = seqnum;
  priv->last_rtptime = rtptime;

388
  discont = buf_discont;
389

Wim Taymans's avatar
Wim Taymans committed
390
  GST_LOG_OBJECT (filter, "discont %d, seqnum %u, rtptime %u, pts %"
391
      GST_TIME_FORMAT ", dts %" GST_TIME_FORMAT, buf_discont, seqnum, rtptime,
Wim Taymans's avatar
Wim Taymans committed
392
      GST_TIME_ARGS (pts), GST_TIME_ARGS (dts));
393 394 395 396

  /* Check seqnum. This is a very simple check that makes sure that the seqnums
   * are striclty increasing, dropping anything that is out of the ordinary. We
   * can only do this when the next_seqnum is known. */
397
  if (G_LIKELY (priv->next_seqnum != -1)) {
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
    gap = gst_rtp_buffer_compare_seqnum (seqnum, priv->next_seqnum);

    /* if we have no gap, all is fine */
    if (G_UNLIKELY (gap != 0)) {
      GST_LOG_OBJECT (filter, "got packet %u, expected %u, gap %d", seqnum,
          priv->next_seqnum, gap);
      if (gap < 0) {
        /* seqnum > next_seqnum, we are missing some packets, this is always a
         * DISCONT. */
        GST_LOG_OBJECT (filter, "%d missing packets", gap);
        discont = TRUE;
      } else {
        /* seqnum < next_seqnum, we have seen this packet before or the sender
         * could be restarted. If the packet is not too old, we throw it away as
         * a duplicate, otherwise we mark discont and continue. 100 misordered
         * packets is a good threshold. See also RFC 4737. */
        if (gap < 100)
          goto dropping;

        GST_LOG_OBJECT (filter,
            "%d > 100, packet too old, sender likely restarted", gap);
        discont = TRUE;
      }
    }
  }
  priv->next_seqnum = (seqnum + 1) & 0xffff;

425
  if (G_UNLIKELY (discont)) {
426
    priv->discont = TRUE;
427 428 429 430 431 432 433
    if (!buf_discont) {
      /* we detected a seqnum discont but the buffer was not flagged with a discont,
       * set the discont flag so that the subclass can throw away old data. */
      GST_LOG_OBJECT (filter, "mark DISCONT on input buffer");
      in = gst_buffer_make_writable (in);
      GST_BUFFER_FLAG_SET (in, GST_BUFFER_FLAG_DISCONT);
    }
434 435
  }

436 437 438 439 440 441 442
  /* prepare segment event if needed */
  if (filter->need_newsegment) {
    priv->segment_event = create_segment_event (filter, rtptime,
        GST_BUFFER_PTS (in));
    filter->need_newsegment = FALSE;
  }

Wim Taymans's avatar
Wim Taymans committed
443
  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
444

445 446 447
  if (G_UNLIKELY (bclass->process == NULL))
    goto no_process;

448 449 450
  /* let's send it out to processing */
  out_buf = bclass->process (filter, in);
  if (out_buf) {
Wim Taymans's avatar
Wim Taymans committed
451
    ret = gst_rtp_base_depayload_push (filter, out_buf);
452
  }
453 454
  gst_buffer_unref (in);

455
  return ret;
456 457

  /* ERRORS */
458 459 460
not_negotiated:
  {
    /* this is not fatal but should be filtered earlier */
Wim Taymans's avatar
Wim Taymans committed
461 462 463 464 465 466 467 468
    GST_ELEMENT_ERROR (filter, CORE, NEGOTIATION,
        ("No RTP format was negotiated."),
        ("Input buffers need to have RTP caps set on them. This is usually "
            "achieved by setting the 'caps' property of the upstream source "
            "element (often udpsrc or appsrc), or by putting a capsfilter "
            "element before the depayloader and setting the 'caps' property "
            "on that. Also see http://cgit.freedesktop.org/gstreamer/"
            "gst-plugins-good/tree/gst/rtp/README"));
469 470 471
    gst_buffer_unref (in);
    return GST_FLOW_NOT_NEGOTIATED;
  }
472 473 474 475 476 477 478 479
invalid_buffer:
  {
    /* this is not fatal but should be filtered earlier */
    GST_ELEMENT_WARNING (filter, STREAM, DECODE, (NULL),
        ("Received invalid RTP payload, dropping"));
    gst_buffer_unref (in);
    return GST_FLOW_OK;
  }
480 481 482 483 484 485 486 487 488 489 490 491 492 493
dropping:
  {
    GST_WARNING_OBJECT (filter, "%d <= 100, dropping old packet", gap);
    gst_buffer_unref (in);
    return GST_FLOW_OK;
  }
no_process:
  {
    /* this is not fatal but should be filtered earlier */
    GST_ELEMENT_ERROR (filter, STREAM, NOT_IMPLEMENTED, (NULL),
        ("The subclass does not have a process method"));
    gst_buffer_unref (in);
    return GST_FLOW_ERROR;
  }
494 495
}

496
static gboolean
Wim Taymans's avatar
Wim Taymans committed
497
gst_rtp_base_depayload_handle_event (GstRTPBaseDepayload * filter,
498
    GstEvent * event)
499 500
{
  gboolean res = TRUE;
501
  gboolean forward = TRUE;
502 503

  switch (GST_EVENT_TYPE (event)) {
504 505 506
    case GST_EVENT_FLUSH_STOP:
      gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
      filter->need_newsegment = TRUE;
507
      filter->priv->next_seqnum = -1;
508
      gst_event_replace (&filter->priv->segment_event, NULL);
509
      break;
510 511 512 513 514 515
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);

Wim Taymans's avatar
Wim Taymans committed
516
      res = gst_rtp_base_depayload_setcaps (filter, caps);
517 518 519
      forward = FALSE;
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
520
    case GST_EVENT_SEGMENT:
521
    {
522
      gst_event_copy_segment (event, &filter->segment);
523 524
      /* don't pass the event downstream, we generate our own segment including
       * the NTP time and other things we receive in caps */
525
      forward = FALSE;
526
      break;
527
    }
528 529
    case GST_EVENT_CUSTOM_DOWNSTREAM:
    {
Wim Taymans's avatar
Wim Taymans committed
530
      GstRTPBaseDepayloadClass *bclass;
531

Wim Taymans's avatar
Wim Taymans committed
532
      bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
533 534 535 536

      if (gst_event_has_name (event, "GstRTPPacketLost")) {
        /* we get this event from the jitterbuffer when it considers a packet as
         * being lost. We send it to our packet_lost vmethod. The default
537
         * implementation will make time progress by pushing out a GAP event.
538
         * Subclasses can override and do one of the following:
539 540 541 542 543 544 545 546
         *  - Adjust timestamp/duration to something more accurate before
         *    calling the parent (default) packet_lost method.
         *  - do some more advanced error concealing on the already received
         *    (fragmented) packets.
         *  - ignore the packet lost.
         */
        if (bclass->packet_lost)
          res = bclass->packet_lost (filter, event);
547
        forward = FALSE;
548 549 550
      }
      break;
    }
551 552 553
    default:
      break;
  }
554 555 556 557 558 559

  if (forward)
    res = gst_pad_push_event (filter->srcpad, event);
  else
    gst_event_unref (event);

560 561 562
  return res;
}

563
static gboolean
Wim Taymans's avatar
Wim Taymans committed
564 565
gst_rtp_base_depayload_handle_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
566 567
{
  gboolean res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
568 569
  GstRTPBaseDepayload *filter;
  GstRTPBaseDepayloadClass *bclass;
570

Wim Taymans's avatar
Wim Taymans committed
571
  filter = GST_RTP_BASE_DEPAYLOAD (parent);
Wim Taymans's avatar
Wim Taymans committed
572
  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
573 574
  if (bclass->handle_event)
    res = bclass->handle_event (filter, event);
575 576
  else
    gst_event_unref (event);
577 578 579 580

  return res;
}

581
static GstEvent *
582 583
create_segment_event (GstRTPBaseDepayload * filter, guint rtptime,
    GstClockTime position)
584 585
{
  GstEvent *event;
586
  GstClockTime start, stop, running_time;
Wim Taymans's avatar
Wim Taymans committed
587
  GstRTPBaseDepayloadPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
588
  GstSegment segment;
589 590 591

  priv = filter->priv;

592
  /* determining the start of the segment */
593 594 595 596 597 598 599 600 601 602
  start = 0;
  if (priv->clock_base != -1 && position != -1) {
    GstClockTime exttime, gap;

    exttime = priv->clock_base;
    gst_rtp_buffer_ext_timestamp (&exttime, rtptime);
    gap = gst_util_uint64_scale_int (exttime - priv->clock_base,
        filter->clock_rate, GST_SECOND);

    /* account for lost packets */
603 604 605 606 607 608
    if (position > gap) {
      GST_DEBUG_OBJECT (filter,
          "Found gap of %" GST_TIME_FORMAT ", adjusting start: %"
          GST_TIME_FORMAT " = %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
          GST_TIME_ARGS (gap), GST_TIME_ARGS (position - gap),
          GST_TIME_ARGS (position), GST_TIME_ARGS (gap));
609
      start = position - gap;
610
    }
611 612
  }

613
  /* determining the stop of the segment */
614
  stop = -1;
615
  if (priv->npt_stop != -1)
616
    stop = start + (priv->npt_stop - priv->npt_start);
617

618 619 620 621
  if (position == -1)
    position = 0;

  running_time = gst_segment_to_running_time (&filter->segment,
622
      GST_FORMAT_TIME, start);
623

Wim Taymans's avatar
Wim Taymans committed
624 625 626
  gst_segment_init (&segment, GST_FORMAT_TIME);
  segment.rate = priv->play_speed;
  segment.applied_rate = priv->play_scale;
627
  segment.start = start;
Wim Taymans's avatar
Wim Taymans committed
628 629 630
  segment.stop = stop;
  segment.time = priv->npt_start;
  segment.position = position;
631
  segment.base = running_time;
Wim Taymans's avatar
Wim Taymans committed
632

633 634
  GST_DEBUG_OBJECT (filter, "Creating segment event %" GST_SEGMENT_FORMAT,
      &segment);
Wim Taymans's avatar
Wim Taymans committed
635
  event = gst_event_new_segment (&segment);
636 637 638 639

  return event;
}

640
typedef struct
641
{
Wim Taymans's avatar
Wim Taymans committed
642 643
  GstRTPBaseDepayload *depayload;
  GstRTPBaseDepayloadClass *bclass;
644
} HeaderData;
645

Wim Taymans's avatar
Wim Taymans committed
646 647
static gboolean
set_headers (GstBuffer ** buffer, guint idx, HeaderData * data)
648
{
Wim Taymans's avatar
Wim Taymans committed
649 650
  GstRTPBaseDepayload *depayload = data->depayload;
  GstRTPBaseDepayloadPrivate *priv = depayload->priv;
Wim Taymans's avatar
Wim Taymans committed
651
  GstClockTime pts, dts, duration;
652

Wim Taymans's avatar
Wim Taymans committed
653
  *buffer = gst_buffer_make_writable (*buffer);
654

Wim Taymans's avatar
Wim Taymans committed
655 656 657 658 659 660 661 662 663 664 665 666
  pts = GST_BUFFER_PTS (*buffer);
  dts = GST_BUFFER_DTS (*buffer);
  duration = GST_BUFFER_DURATION (*buffer);

  /* apply last incomming timestamp and duration to outgoing buffer if
   * not otherwise set. */
  if (!GST_CLOCK_TIME_IS_VALID (pts))
    GST_BUFFER_PTS (*buffer) = priv->pts;
  if (!GST_CLOCK_TIME_IS_VALID (dts))
    GST_BUFFER_DTS (*buffer) = priv->dts;
  if (!GST_CLOCK_TIME_IS_VALID (duration))
    GST_BUFFER_DURATION (*buffer) = priv->duration;
667

668 669 670 671 672
  if (G_UNLIKELY (depayload->priv->discont)) {
    GST_LOG_OBJECT (depayload, "Marking DISCONT on output buffer");
    GST_BUFFER_FLAG_SET (*buffer, GST_BUFFER_FLAG_DISCONT);
    depayload->priv->discont = FALSE;
  }
673

Wim Taymans's avatar
Wim Taymans committed
674 675 676 677 678
  /* make sure we only set the timestamp on the first packet */
  priv->pts = GST_CLOCK_TIME_NONE;
  priv->dts = GST_CLOCK_TIME_NONE;
  priv->duration = GST_CLOCK_TIME_NONE;

Wim Taymans's avatar
Wim Taymans committed
679
  return TRUE;
680 681 682
}

static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
683
gst_rtp_base_depayload_prepare_push (GstRTPBaseDepayload * filter,
Wim Taymans's avatar
Wim Taymans committed
684
    gboolean is_list, gpointer obj)
685 686 687 688
{
  HeaderData data;

  data.depayload = filter;
Wim Taymans's avatar
Wim Taymans committed
689
  data.bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
690 691

  if (is_list) {
Wim Taymans's avatar
Wim Taymans committed
692 693
    GstBufferList **blist = obj;
    gst_buffer_list_foreach (*blist, (GstBufferListFunc) set_headers, &data);
694
  } else {
Wim Taymans's avatar
Wim Taymans committed
695
    GstBuffer **buf = obj;
Wim Taymans's avatar
Wim Taymans committed
696
    set_headers (buf, 0, &data);
697
  }
698

699
  /* if this is the first buffer send a NEWSEGMENT */
700 701 702
  if (G_UNLIKELY (filter->priv->segment_event)) {
    gst_pad_push_event (filter->srcpad, filter->priv->segment_event);
    filter->priv->segment_event = NULL;
703 704 705
    GST_DEBUG_OBJECT (filter, "Pushed newsegment event on this first buffer");
  }

706
  return GST_FLOW_OK;
707 708 709
}

/**
Wim Taymans's avatar
Wim Taymans committed
710 711
 * gst_rtp_base_depayload_push:
 * @filter: a #GstRTPBaseDepayload
712 713 714 715 716
 * @out_buf: a #GstBuffer
 *
 * Push @out_buf to the peer of @filter. This function takes ownership of
 * @out_buf.
 *
Wim Taymans's avatar
Wim Taymans committed
717 718
 * This function will by default apply the last incomming timestamp on
 * the outgoing buffer when it didn't have a timestamp already.
719
 *
720
 * Returns: a #GstFlowReturn.
721 722
 */
GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
723
gst_rtp_base_depayload_push (GstRTPBaseDepayload * filter, GstBuffer * out_buf)
724
{
725 726
  GstFlowReturn res;

Wim Taymans's avatar
Wim Taymans committed
727
  res = gst_rtp_base_depayload_prepare_push (filter, FALSE, &out_buf);
728 729 730 731 732 733 734 735 736 737

  if (G_LIKELY (res == GST_FLOW_OK))
    res = gst_pad_push (filter->srcpad, out_buf);
  else
    gst_buffer_unref (out_buf);

  return res;
}

/**
Wim Taymans's avatar
Wim Taymans committed
738 739
 * gst_rtp_base_depayload_push_list:
 * @filter: a #GstRTPBaseDepayload
740 741 742 743 744 745 746 747
 * @out_list: a #GstBufferList
 *
 * Push @out_list to the peer of @filter. This function takes ownership of
 * @out_list.
 *
 * Returns: a #GstFlowReturn.
 */
GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
748
gst_rtp_base_depayload_push_list (GstRTPBaseDepayload * filter,
749 750 751 752
    GstBufferList * out_list)
{
  GstFlowReturn res;

Wim Taymans's avatar
Wim Taymans committed
753
  res = gst_rtp_base_depayload_prepare_push (filter, TRUE, &out_list);
754 755 756 757 758 759 760

  if (G_LIKELY (res == GST_FLOW_OK))
    res = gst_pad_push_list (filter->srcpad, out_list);
  else
    gst_buffer_list_unref (out_list);

  return res;
761 762
}

763
/* convert the PacketLost event from a jitterbuffer to a GAP event.
764 765
 * subclasses can override this.  */
static gboolean
Wim Taymans's avatar
Wim Taymans committed
766
gst_rtp_base_depayload_packet_lost (GstRTPBaseDepayload * filter,
767 768
    GstEvent * event)
{
769
  GstClockTime timestamp, duration;
770 771 772 773 774 775 776 777 778 779 780 781
  GstEvent *sevent;
  const GstStructure *s;

  s = gst_event_get_structure (event);

  /* first start by parsing the timestamp and duration */
  timestamp = -1;
  duration = -1;

  gst_structure_get_clock_time (s, "timestamp", &timestamp);
  gst_structure_get_clock_time (s, "duration", &duration);

782 783
  /* send GAP event */
  sevent = gst_event_new_gap (timestamp, duration);
784 785 786 787

  return gst_pad_push_event (filter->srcpad, sevent);
}

788
static GstStateChangeReturn
Wim Taymans's avatar
Wim Taymans committed
789
gst_rtp_base_depayload_change_state (GstElement * element,
790
    GstStateChange transition)
791
{
Wim Taymans's avatar
Wim Taymans committed
792 793
  GstRTPBaseDepayload *filter;
  GstRTPBaseDepayloadPrivate *priv;
794
  GstStateChangeReturn ret;
795

Wim Taymans's avatar
Wim Taymans committed
796
  filter = GST_RTP_BASE_DEPAYLOAD (element);
797
  priv = filter->priv;
798 799

  switch (transition) {
800
    case GST_STATE_CHANGE_NULL_TO_READY:
801
      break;
802
    case GST_STATE_CHANGE_READY_TO_PAUSED:
803
      filter->need_newsegment = TRUE;
804 805 806 807
      priv->npt_start = 0;
      priv->npt_stop = -1;
      priv->play_speed = 1.0;
      priv->play_scale = 1.0;
808
      priv->clock_base = -1;
809 810
      priv->next_seqnum = -1;
      priv->negotiated = FALSE;
811
      priv->discont = FALSE;
812
      gst_event_replace (&filter->priv->segment_event, NULL);
813
      break;
814
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
815 816 817 818 819
      break;
    default:
      break;
  }

820 821
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

822
  switch (transition) {
823
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
824
      break;
825
    case GST_STATE_CHANGE_PAUSED_TO_READY:
826
      gst_caps_replace (&priv->last_caps, NULL);
827
      break;
828
    case GST_STATE_CHANGE_READY_TO_NULL:
829
      break;
830 831
    default:
      break;
832
  }
833
  return ret;
834 835
}

Wim Taymans's avatar
Wim Taymans committed
836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
static GstStructure *
gst_rtp_base_depayload_create_stats (GstRTPBaseDepayload * depayload)
{
  GstRTPBaseDepayloadPrivate *priv;
  GstStructure *s;

  priv = depayload->priv;

  s = gst_structure_new ("application/x-rtp-depayload-stats",
      "clock_rate", G_TYPE_UINT, depayload->clock_rate,
      "npt-start", G_TYPE_UINT64, priv->npt_start,
      "npt-stop", G_TYPE_UINT64, priv->npt_stop,
      "play-speed", G_TYPE_DOUBLE, priv->play_speed,
      "play-scale", G_TYPE_DOUBLE, priv->play_scale,
      "running-time-dts", G_TYPE_UINT64, priv->dts,
      "running-time-pts", G_TYPE_UINT64, priv->pts,
      "seqnum", G_TYPE_UINT, (guint) priv->last_seqnum,
      "timestamp", G_TYPE_UINT, (guint) priv->last_rtptime, NULL);

  return s;
}


859
static void
Wim Taymans's avatar
Wim Taymans committed
860
gst_rtp_base_depayload_set_property (GObject * object, guint prop_id,
861 862 863 864 865 866 867 868 869 870
    const GValue * value, GParamSpec * pspec)
{
  switch (prop_id) {
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
Wim Taymans's avatar
Wim Taymans committed
871
gst_rtp_base_depayload_get_property (GObject * object, guint prop_id,
872 873
    GValue * value, GParamSpec * pspec)
{
Wim Taymans's avatar
Wim Taymans committed
874 875 876 877
  GstRTPBaseDepayload *depayload;

  depayload = GST_RTP_BASE_DEPAYLOAD (object);

878
  switch (prop_id) {