gstrtpbasedepayload.c 24.5 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

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

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

  gboolean negotiated;
53 54

  GstCaps *last_caps;
55 56
};

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

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

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

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

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

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

90
static GstElementClass *parent_class = NULL;
Wim Taymans's avatar
Wim Taymans committed
91
static void gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass *
92
    klass);
93
static void gst_rtp_base_depayload_init (GstRTPBaseDepayload * rtpbasepayload,
Wim Taymans's avatar
Wim Taymans committed
94
    GstRTPBaseDepayloadClass * klass);
95 96

GType
Wim Taymans's avatar
Wim Taymans committed
97
gst_rtp_base_depayload_get_type (void)
98
{
Wim Taymans's avatar
Wim Taymans committed
99
  static GType rtp_base_depayload_type = 0;
100

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

Wim Taymans's avatar
Wim Taymans committed
114 115 116
    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));
117
  }
Wim Taymans's avatar
Wim Taymans committed
118
  return rtp_base_depayload_type;
119 120 121
}

static void
Wim Taymans's avatar
Wim Taymans committed
122
gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass * klass)
123 124
{
  GObjectClass *gobject_class;
125
  GstElementClass *gstelement_class;
126 127

  gobject_class = G_OBJECT_CLASS (klass);
128
  gstelement_class = (GstElementClass *) klass;
129
  parent_class = g_type_class_peek_parent (klass);
130

Wim Taymans's avatar
Wim Taymans committed
131
  g_type_class_add_private (klass, sizeof (GstRTPBaseDepayloadPrivate));
132

Wim Taymans's avatar
Wim Taymans committed
133 134 135
  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;
136

Wim Taymans's avatar
Wim Taymans committed
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 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

  /**
   * 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
200
  gstelement_class->change_state = gst_rtp_base_depayload_change_state;
201

Wim Taymans's avatar
Wim Taymans committed
202 203
  klass->packet_lost = gst_rtp_base_depayload_packet_lost;
  klass->handle_event = gst_rtp_base_depayload_handle_event;
204

205
  GST_DEBUG_CATEGORY_INIT (rtpbasedepayload_debug, "rtpbasedepayload", 0,
206 207 208 209
      "Base class for RTP Depayloaders");
}

static void
Wim Taymans's avatar
Wim Taymans committed
210 211
gst_rtp_base_depayload_init (GstRTPBaseDepayload * filter,
    GstRTPBaseDepayloadClass * klass)
212 213
{
  GstPadTemplate *pad_template;
Wim Taymans's avatar
Wim Taymans committed
214
  GstRTPBaseDepayloadPrivate *priv;
215

Wim Taymans's avatar
Wim Taymans committed
216
  priv = GST_RTP_BASE_DEPAYLOAD_GET_PRIVATE (filter);
217
  filter->priv = priv;
218

219
  GST_DEBUG_OBJECT (filter, "init");
220 221

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

  pad_template =
231
      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src");
232 233
  g_return_if_fail (pad_template != NULL);
  filter->srcpad = gst_pad_new_from_template (pad_template, "src");
234
  gst_pad_use_fixed_caps (filter->srcpad);
235 236
  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);

Wim Taymans's avatar
Wim Taymans committed
237 238 239 240 241 242 243 244
  priv->npt_start = 0;
  priv->npt_stop = -1;
  priv->play_speed = 1.0;
  priv->play_scale = 1.0;
  priv->dts = -1;
  priv->pts = -1;
  priv->duration = -1;

245
  gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
246 247 248
}

static void
Wim Taymans's avatar
Wim Taymans committed
249
gst_rtp_base_depayload_finalize (GObject * object)
250
{
251
  G_OBJECT_CLASS (parent_class)->finalize (object);
252 253 254
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
255
gst_rtp_base_depayload_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps)
256
{
Wim Taymans's avatar
Wim Taymans committed
257 258
  GstRTPBaseDepayloadClass *bclass;
  GstRTPBaseDepayloadPrivate *priv;
259
  gboolean res;
260 261
  GstStructure *caps_struct;
  const GValue *value;
262

263
  priv = filter->priv;
264

Wim Taymans's avatar
Wim Taymans committed
265
  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
266

267 268 269 270 271 272 273 274 275 276 277
  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;
    }
  }
278 279 280

  caps_struct = gst_caps_get_structure (caps, 0);

281 282 283 284 285 286
  /* 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;
287
  GST_DEBUG_OBJECT (filter, "NPT start %" G_GUINT64_FORMAT, priv->npt_start);
288 289 290 291 292 293 294

  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;

295
  GST_DEBUG_OBJECT (filter, "NPT stop %" G_GUINT64_FORMAT, priv->npt_stop);
296

297 298 299 300 301 302 303 304 305 306 307 308
  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;

309
  if (bclass->set_caps) {
310
    res = bclass->set_caps (filter, caps);
311 312 313 314 315
    if (!res) {
      GST_WARNING_OBJECT (filter, "Subclass rejected caps %" GST_PTR_FORMAT,
          caps);
    }
  } else {
316
    res = TRUE;
317
  }
318

319 320
  priv->negotiated = res;

321 322 323
  if (priv->negotiated)
    priv->last_caps = gst_caps_ref (caps);

324
  return res;
325 326 327 328 329 330

caps_not_changed:
  {
    GST_DEBUG_OBJECT (filter, "Caps did not change");
    return res;
  }
331 332 333
}

static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
334
gst_rtp_base_depayload_chain (GstPad * pad, GstObject * parent, GstBuffer * in)
335
{
Wim Taymans's avatar
Wim Taymans committed
336 337 338
  GstRTPBaseDepayload *filter;
  GstRTPBaseDepayloadPrivate *priv;
  GstRTPBaseDepayloadClass *bclass;
339
  GstFlowReturn ret = GST_FLOW_OK;
340
  GstBuffer *out_buf;
Wim Taymans's avatar
Wim Taymans committed
341
  GstClockTime pts, dts;
342
  guint16 seqnum;
343
  guint32 rtptime;
344
  gboolean discont, buf_discont;
345
  gint gap;
346
  GstRTPBuffer rtp = { NULL };
347

Wim Taymans's avatar
Wim Taymans committed
348
  filter = GST_RTP_BASE_DEPAYLOAD (parent);
349 350 351 352 353
  priv = filter->priv;

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

355
  if (G_UNLIKELY (!gst_rtp_buffer_map (in, GST_MAP_READ, &rtp)))
356 357
    goto invalid_buffer;

358
  buf_discont = GST_BUFFER_IS_DISCONT (in);
359

Wim Taymans's avatar
Wim Taymans committed
360 361
  pts = GST_BUFFER_PTS (in);
  dts = GST_BUFFER_DTS (in);
362 363
  /* convert to running_time and save the timestamp, this is the timestamp
   * we put on outgoing buffers. */
Wim Taymans's avatar
Wim Taymans committed
364 365 366 367
  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;
368
  priv->duration = GST_BUFFER_DURATION (in);
369

Wim Taymans's avatar
Wim Taymans committed
370 371 372 373
  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
374 375 376
  priv->last_seqnum = seqnum;
  priv->last_rtptime = rtptime;

377
  discont = buf_discont;
378

Wim Taymans's avatar
Wim Taymans committed
379
  GST_LOG_OBJECT (filter, "discont %d, seqnum %u, rtptime %u, pts %"
380
      GST_TIME_FORMAT ", dts %" GST_TIME_FORMAT, buf_discont, seqnum, rtptime,
Wim Taymans's avatar
Wim Taymans committed
381
      GST_TIME_ARGS (pts), GST_TIME_ARGS (dts));
382 383 384 385

  /* 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. */
386
  if (G_LIKELY (priv->next_seqnum != -1)) {
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
    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;

414
  if (G_UNLIKELY (discont)) {
415
    priv->discont = TRUE;
416 417 418 419 420 421 422
    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);
    }
423 424
  }

Wim Taymans's avatar
Wim Taymans committed
425
  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
426

427 428 429
  if (G_UNLIKELY (bclass->process == NULL))
    goto no_process;

430 431 432
  /* let's send it out to processing */
  out_buf = bclass->process (filter, in);
  if (out_buf) {
Wim Taymans's avatar
Wim Taymans committed
433
    ret = gst_rtp_base_depayload_push (filter, out_buf);
434
  }
435 436
  gst_buffer_unref (in);

437
  return ret;
438 439

  /* ERRORS */
440 441 442
not_negotiated:
  {
    /* this is not fatal but should be filtered earlier */
Wim Taymans's avatar
Wim Taymans committed
443 444 445 446 447 448 449 450
    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"));
451 452 453
    gst_buffer_unref (in);
    return GST_FLOW_NOT_NEGOTIATED;
  }
454 455 456 457 458 459 460 461
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;
  }
462 463 464 465 466 467 468 469 470 471 472 473 474 475
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;
  }
476 477
}

478
static gboolean
Wim Taymans's avatar
Wim Taymans committed
479
gst_rtp_base_depayload_handle_event (GstRTPBaseDepayload * filter,
480
    GstEvent * event)
481 482
{
  gboolean res = TRUE;
483
  gboolean forward = TRUE;
484 485

  switch (GST_EVENT_TYPE (event)) {
486 487 488
    case GST_EVENT_FLUSH_STOP:
      gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
      filter->need_newsegment = TRUE;
489
      filter->priv->next_seqnum = -1;
490
      break;
491 492 493 494 495 496
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);

Wim Taymans's avatar
Wim Taymans committed
497
      res = gst_rtp_base_depayload_setcaps (filter, caps);
498 499 500
      forward = FALSE;
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
501
    case GST_EVENT_SEGMENT:
502
    {
503
      gst_event_copy_segment (event, &filter->segment);
504 505
      /* don't pass the event downstream, we generate our own segment including
       * the NTP time and other things we receive in caps */
506
      forward = FALSE;
507
      break;
508
    }
509 510
    case GST_EVENT_CUSTOM_DOWNSTREAM:
    {
Wim Taymans's avatar
Wim Taymans committed
511
      GstRTPBaseDepayloadClass *bclass;
512

Wim Taymans's avatar
Wim Taymans committed
513
      bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
514 515 516 517

      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
518
         * implementation will make time progress by pushing out a GAP event.
519
         * Subclasses can override and do one of the following:
520 521 522 523 524 525 526 527
         *  - 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);
528
        forward = FALSE;
529 530 531
      }
      break;
    }
532 533 534
    default:
      break;
  }
535 536 537 538 539 540

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

541 542 543
  return res;
}

544
static gboolean
Wim Taymans's avatar
Wim Taymans committed
545 546
gst_rtp_base_depayload_handle_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
547 548
{
  gboolean res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
549 550
  GstRTPBaseDepayload *filter;
  GstRTPBaseDepayloadClass *bclass;
551

Wim Taymans's avatar
Wim Taymans committed
552
  filter = GST_RTP_BASE_DEPAYLOAD (parent);
Wim Taymans's avatar
Wim Taymans committed
553
  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
554 555
  if (bclass->handle_event)
    res = bclass->handle_event (filter, event);
556 557
  else
    gst_event_unref (event);
558 559 560 561

  return res;
}

562
static GstEvent *
563
create_segment_event (GstRTPBaseDepayload * filter, GstClockTime position)
564 565
{
  GstEvent *event;
566
  GstClockTime stop, running_time;
Wim Taymans's avatar
Wim Taymans committed
567
  GstRTPBaseDepayloadPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
568
  GstSegment segment;
569 570 571 572

  priv = filter->priv;

  if (priv->npt_stop != -1)
573
    stop = position + priv->npt_stop - priv->npt_start;
574 575 576
  else
    stop = -1;

577 578 579 580 581 582
  if (position == -1)
    position = 0;

  running_time = gst_segment_to_running_time (&filter->segment,
      GST_FORMAT_TIME, position);

Wim Taymans's avatar
Wim Taymans committed
583 584 585
  gst_segment_init (&segment, GST_FORMAT_TIME);
  segment.rate = priv->play_speed;
  segment.applied_rate = priv->play_scale;
586
  segment.start = position;
Wim Taymans's avatar
Wim Taymans committed
587 588 589
  segment.stop = stop;
  segment.time = priv->npt_start;
  segment.position = position;
590
  segment.base = running_time;
Wim Taymans's avatar
Wim Taymans committed
591 592

  event = gst_event_new_segment (&segment);
593 594 595 596

  return event;
}

597
typedef struct
598
{
Wim Taymans's avatar
Wim Taymans committed
599 600
  GstRTPBaseDepayload *depayload;
  GstRTPBaseDepayloadClass *bclass;
601
} HeaderData;
602

Wim Taymans's avatar
Wim Taymans committed
603 604
static gboolean
set_headers (GstBuffer ** buffer, guint idx, HeaderData * data)
605
{
Wim Taymans's avatar
Wim Taymans committed
606 607
  GstRTPBaseDepayload *depayload = data->depayload;
  GstRTPBaseDepayloadPrivate *priv = depayload->priv;
Wim Taymans's avatar
Wim Taymans committed
608
  GstClockTime pts, dts, duration;
609

Wim Taymans's avatar
Wim Taymans committed
610
  *buffer = gst_buffer_make_writable (*buffer);
611

Wim Taymans's avatar
Wim Taymans committed
612 613 614 615 616 617 618 619 620 621 622 623
  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;
624

625 626 627 628 629
  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;
  }
630

Wim Taymans's avatar
Wim Taymans committed
631 632 633 634 635
  /* 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
636
  return TRUE;
637 638 639
}

static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
640
gst_rtp_base_depayload_prepare_push (GstRTPBaseDepayload * filter,
Wim Taymans's avatar
Wim Taymans committed
641
    gboolean is_list, gpointer obj)
642 643 644 645
{
  HeaderData data;

  data.depayload = filter;
Wim Taymans's avatar
Wim Taymans committed
646
  data.bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
647 648

  if (is_list) {
Wim Taymans's avatar
Wim Taymans committed
649 650
    GstBufferList **blist = obj;
    gst_buffer_list_foreach (*blist, (GstBufferListFunc) set_headers, &data);
651
  } else {
Wim Taymans's avatar
Wim Taymans committed
652
    GstBuffer **buf = obj;
Wim Taymans's avatar
Wim Taymans committed
653
    set_headers (buf, 0, &data);
654
  }
655

656 657 658
  /* if this is the first buffer send a NEWSEGMENT */
  if (G_UNLIKELY (filter->need_newsegment)) {
    GstEvent *event;
659 660 661 662 663 664 665 666 667 668 669
    GstClockTime pts;

    if (is_list) {
      GstBufferList **blist = obj;
      GstBuffer *buf = gst_buffer_list_get (*blist, 0);
      pts = GST_BUFFER_PTS (buf);
    } else {
      GstBuffer **buf = obj;
      set_headers (buf, 0, &data);
      pts = GST_BUFFER_PTS (*buf);
    }
670

671
    event = create_segment_event (filter, pts);
672 673 674 675 676 677 678

    gst_pad_push_event (filter->srcpad, event);

    filter->need_newsegment = FALSE;
    GST_DEBUG_OBJECT (filter, "Pushed newsegment event on this first buffer");
  }

679
  return GST_FLOW_OK;
680 681 682
}

/**
Wim Taymans's avatar
Wim Taymans committed
683 684
 * gst_rtp_base_depayload_push:
 * @filter: a #GstRTPBaseDepayload
685 686 687 688 689
 * @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
690 691
 * This function will by default apply the last incomming timestamp on
 * the outgoing buffer when it didn't have a timestamp already.
692
 *
693
 * Returns: a #GstFlowReturn.
694 695
 */
GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
696
gst_rtp_base_depayload_push (GstRTPBaseDepayload * filter, GstBuffer * out_buf)
697
{
698 699
  GstFlowReturn res;

Wim Taymans's avatar
Wim Taymans committed
700
  res = gst_rtp_base_depayload_prepare_push (filter, FALSE, &out_buf);
701 702 703 704 705 706 707 708 709 710

  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
711 712
 * gst_rtp_base_depayload_push_list:
 * @filter: a #GstRTPBaseDepayload
713 714 715 716 717 718 719 720
 * @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
721
gst_rtp_base_depayload_push_list (GstRTPBaseDepayload * filter,
722 723 724 725
    GstBufferList * out_list)
{
  GstFlowReturn res;

Wim Taymans's avatar
Wim Taymans committed
726
  res = gst_rtp_base_depayload_prepare_push (filter, TRUE, &out_list);
727 728 729 730 731 732 733

  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;
734 735
}

736
/* convert the PacketLost event from a jitterbuffer to a GAP event.
737 738
 * subclasses can override this.  */
static gboolean
Wim Taymans's avatar
Wim Taymans committed
739
gst_rtp_base_depayload_packet_lost (GstRTPBaseDepayload * filter,
740 741
    GstEvent * event)
{
742
  GstClockTime timestamp, duration;
743 744 745 746 747 748 749 750 751 752 753 754
  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);

755 756
  /* send GAP event */
  sevent = gst_event_new_gap (timestamp, duration);
757 758 759 760

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

761
static GstStateChangeReturn
Wim Taymans's avatar
Wim Taymans committed
762
gst_rtp_base_depayload_change_state (GstElement * element,
763
    GstStateChange transition)
764
{
Wim Taymans's avatar
Wim Taymans committed
765 766
  GstRTPBaseDepayload *filter;
  GstRTPBaseDepayloadPrivate *priv;
767
  GstStateChangeReturn ret;
768

Wim Taymans's avatar
Wim Taymans committed
769
  filter = GST_RTP_BASE_DEPAYLOAD (element);
770
  priv = filter->priv;
771 772

  switch (transition) {
773
    case GST_STATE_CHANGE_NULL_TO_READY:
774
      break;
775
    case GST_STATE_CHANGE_READY_TO_PAUSED:
776
      filter->need_newsegment = TRUE;
777 778 779 780
      priv->npt_start = 0;
      priv->npt_stop = -1;
      priv->play_speed = 1.0;
      priv->play_scale = 1.0;
781 782
      priv->next_seqnum = -1;
      priv->negotiated = FALSE;
783
      priv->discont = FALSE;
784
      break;
785
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
786 787 788 789 790
      break;
    default:
      break;
  }

791 792
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

793
  switch (transition) {
794
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
795
      break;
796
    case GST_STATE_CHANGE_PAUSED_TO_READY:
797
      gst_caps_replace (&priv->last_caps, NULL);
798
      break;
799
    case GST_STATE_CHANGE_READY_TO_NULL:
800
      break;
801 802
    default:
      break;
803
  }
804
  return ret;
805 806
}

Wim Taymans's avatar
Wim Taymans committed
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
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;
}


830
static void
Wim Taymans's avatar
Wim Taymans committed
831
gst_rtp_base_depayload_set_property (GObject * object, guint prop_id,
832 833 834 835 836 837 838 839 840 841
    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
842
gst_rtp_base_depayload_get_property (GObject * object, guint prop_id,
843 844
    GValue * value, GParamSpec * pspec)
{
Wim Taymans's avatar
Wim Taymans committed
845 846 847 848
  GstRTPBaseDepayload *depayload;

  depayload = GST_RTP_BASE_DEPAYLOAD (object);

849
  switch (prop_id) {
Wim Taymans's avatar
Wim Taymans committed
850 851 852 853
    case PROP_STATS:
      g_value_take_boxed (value,
          gst_rtp_base_depayload_create_stats (depayload));
      break;
854 855 856 857 858
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}