gstrtpbasedepayload.c 27.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
  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);
81 82
static GstFlowReturn gst_rtp_base_depayload_chain_list (GstPad * pad,
    GstObject * parent, GstBufferList * list);
Wim Taymans's avatar
Wim Taymans committed
83
static gboolean gst_rtp_base_depayload_handle_sink_event (GstPad * pad,
Wim Taymans's avatar
Wim Taymans committed
84
    GstObject * parent, GstEvent * event);
85

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

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

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

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

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

Wim Taymans's avatar
Wim Taymans committed
120 121 122
    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));
123
  }
Wim Taymans's avatar
Wim Taymans committed
124
  return rtp_base_depayload_type;
125 126 127
}

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

  gobject_class = G_OBJECT_CLASS (klass);
134
  gstelement_class = (GstElementClass *) klass;
135
  parent_class = g_type_class_peek_parent (klass);
136

Wim Taymans's avatar
Wim Taymans committed
137
  g_type_class_add_private (klass, sizeof (GstRTPBaseDepayloadPrivate));
138

Wim Taymans's avatar
Wim Taymans committed
139 140 141
  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;
142

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

  /**
   * 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
206
  gstelement_class->change_state = gst_rtp_base_depayload_change_state;
207

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

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

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

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

225
  GST_DEBUG_OBJECT (filter, "init");
226 227

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

  pad_template =
239
      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src");
240 241
  g_return_if_fail (pad_template != NULL);
  filter->srcpad = gst_pad_new_from_template (pad_template, "src");
242
  gst_pad_use_fixed_caps (filter->srcpad);
243 244
  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);

Wim Taymans's avatar
Wim Taymans committed
245 246 247 248
  priv->npt_start = 0;
  priv->npt_stop = -1;
  priv->play_speed = 1.0;
  priv->play_scale = 1.0;
249
  priv->clock_base = -1;
Wim Taymans's avatar
Wim Taymans committed
250 251 252 253
  priv->dts = -1;
  priv->pts = -1;
  priv->duration = -1;

254
  gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
255 256 257
}

static void
Wim Taymans's avatar
Wim Taymans committed
258
gst_rtp_base_depayload_finalize (GObject * object)
259
{
260
  G_OBJECT_CLASS (parent_class)->finalize (object);
261 262 263
}

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

272
  priv = filter->priv;
273

Wim Taymans's avatar
Wim Taymans committed
274
  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
275

276 277 278 279 280 281 282 283 284 285 286
  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;
    }
  }
287 288 289

  caps_struct = gst_caps_get_structure (caps, 0);

290 291 292 293 294 295
  /* 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;
296
  GST_DEBUG_OBJECT (filter, "NPT start %" G_GUINT64_FORMAT, priv->npt_start);
297 298 299 300 301 302 303

  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;

304
  GST_DEBUG_OBJECT (filter, "NPT stop %" G_GUINT64_FORMAT, priv->npt_stop);
305

306 307 308 309 310 311 312 313 314 315 316 317
  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;

318 319 320 321 322 323
  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;

324
  if (bclass->set_caps) {
325
    res = bclass->set_caps (filter, caps);
326 327 328 329 330
    if (!res) {
      GST_WARNING_OBJECT (filter, "Subclass rejected caps %" GST_PTR_FORMAT,
          caps);
    }
  } else {
331
    res = TRUE;
332
  }
333

334 335
  priv->negotiated = res;

336 337 338
  if (priv->negotiated)
    priv->last_caps = gst_caps_ref (caps);

339
  return res;
340 341 342 343 344 345

caps_not_changed:
  {
    GST_DEBUG_OBJECT (filter, "Caps did not change");
    return res;
  }
346 347 348
}

static GstFlowReturn
349 350
gst_rtp_base_depayload_handle_buffer (GstRTPBaseDepayload * filter,
    GstRTPBaseDepayloadClass * bclass, GstBuffer * in)
351
{
352 353 354
  GstBuffer *(*process_rtp_packet_func) (GstRTPBaseDepayload * base,
      GstRTPBuffer * rtp_buffer);
  GstBuffer *(*process_func) (GstRTPBaseDepayload * base, GstBuffer * in);
Wim Taymans's avatar
Wim Taymans committed
355
  GstRTPBaseDepayloadPrivate *priv;
356
  GstFlowReturn ret = GST_FLOW_OK;
357
  GstBuffer *out_buf;
Wim Taymans's avatar
Wim Taymans committed
358
  GstClockTime pts, dts;
359
  guint16 seqnum;
360
  guint32 rtptime;
361
  gboolean discont, buf_discont;
362
  gint gap;
363
  GstRTPBuffer rtp = { NULL };
364

365 366
  priv = filter->priv;

367 368 369
  process_func = bclass->process;
  process_rtp_packet_func = bclass->process_rtp_packet;

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

374
  if (G_UNLIKELY (!gst_rtp_buffer_map (in, GST_MAP_READ, &rtp)))
375 376
    goto invalid_buffer;

377
  buf_discont = GST_BUFFER_IS_DISCONT (in);
378

Wim Taymans's avatar
Wim Taymans committed
379 380
  pts = GST_BUFFER_PTS (in);
  dts = GST_BUFFER_DTS (in);
381 382
  /* convert to running_time and save the timestamp, this is the timestamp
   * we put on outgoing buffers. */
Wim Taymans's avatar
Wim Taymans committed
383 384 385 386
  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;
387
  priv->duration = GST_BUFFER_DURATION (in);
388

Wim Taymans's avatar
Wim Taymans committed
389 390 391
  seqnum = gst_rtp_buffer_get_seq (&rtp);
  rtptime = gst_rtp_buffer_get_timestamp (&rtp);

Wim Taymans's avatar
Wim Taymans committed
392 393 394
  priv->last_seqnum = seqnum;
  priv->last_rtptime = rtptime;

395
  discont = buf_discont;
396

Wim Taymans's avatar
Wim Taymans committed
397
  GST_LOG_OBJECT (filter, "discont %d, seqnum %u, rtptime %u, pts %"
398
      GST_TIME_FORMAT ", dts %" GST_TIME_FORMAT, buf_discont, seqnum, rtptime,
Wim Taymans's avatar
Wim Taymans committed
399
      GST_TIME_ARGS (pts), GST_TIME_ARGS (dts));
400 401

  /* Check seqnum. This is a very simple check that makes sure that the seqnums
402
   * are strictly increasing, dropping anything that is out of the ordinary. We
403
   * can only do this when the next_seqnum is known. */
404
  if (G_LIKELY (priv->next_seqnum != -1)) {
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
    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;

432
  if (G_UNLIKELY (discont)) {
433
    priv->discont = TRUE;
434 435 436 437 438 439 440
    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);
    }
441 442
  }

443 444 445 446 447 448 449
  /* 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;
  }

450 451 452 453 454 455 456
  if (process_rtp_packet_func != NULL) {
    out_buf = process_rtp_packet_func (filter, &rtp);
    gst_rtp_buffer_unmap (&rtp);
  } else if (process_func != NULL) {
    gst_rtp_buffer_unmap (&rtp);
    out_buf = process_func (filter, in);
  } else {
457
    goto no_process;
458
  }
459

460 461
  /* let's send it out to processing */
  if (out_buf) {
Wim Taymans's avatar
Wim Taymans committed
462
    ret = gst_rtp_base_depayload_push (filter, out_buf);
463
  }
464

465
  return ret;
466 467

  /* ERRORS */
468 469 470
not_negotiated:
  {
    /* this is not fatal but should be filtered earlier */
Wim Taymans's avatar
Wim Taymans committed
471 472 473 474 475 476 477 478
    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"));
479 480
    return GST_FLOW_NOT_NEGOTIATED;
  }
481 482 483 484 485 486 487
invalid_buffer:
  {
    /* this is not fatal but should be filtered earlier */
    GST_ELEMENT_WARNING (filter, STREAM, DECODE, (NULL),
        ("Received invalid RTP payload, dropping"));
    return GST_FLOW_OK;
  }
488 489 490 491 492 493 494 495 496
dropping:
  {
    GST_WARNING_OBJECT (filter, "%d <= 100, dropping old packet", gap);
    return GST_FLOW_OK;
  }
no_process:
  {
    /* this is not fatal but should be filtered earlier */
    GST_ELEMENT_ERROR (filter, STREAM, NOT_IMPLEMENTED, (NULL),
497
        ("The subclass does not have a process or process_rtp_packet method"));
498 499
    return GST_FLOW_ERROR;
  }
500 501
}

502 503 504 505 506 507 508 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
static GstFlowReturn
gst_rtp_base_depayload_chain (GstPad * pad, GstObject * parent, GstBuffer * in)
{
  GstRTPBaseDepayloadClass *bclass;
  GstRTPBaseDepayload *basedepay;
  GstFlowReturn flow_ret;

  basedepay = GST_RTP_BASE_DEPAYLOAD_CAST (parent);

  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (basedepay);

  flow_ret = gst_rtp_base_depayload_handle_buffer (basedepay, bclass, in);

  gst_buffer_unref (in);

  return flow_ret;
}

static GstFlowReturn
gst_rtp_base_depayload_chain_list (GstPad * pad, GstObject * parent,
    GstBufferList * list)
{
  GstRTPBaseDepayloadClass *bclass;
  GstRTPBaseDepayload *basedepay;
  GstFlowReturn flow_ret;
  GstBuffer *buffer;
  guint i, len;

  basedepay = GST_RTP_BASE_DEPAYLOAD_CAST (parent);

  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (basedepay);

  flow_ret = GST_FLOW_OK;

  /* chain each buffer in list individually */
  len = gst_buffer_list_length (list);

  if (len == 0)
    goto done;

  for (i = 0; i < len; i++) {
    buffer = gst_buffer_list_get (list, i);

    /* Should we fix up any missing timestamps for list buffers here
     * (e.g. set to first or previous timestamp in list) or just assume
     * the's a jitterbuffer that will have done that for us? */
    flow_ret = gst_rtp_base_depayload_handle_buffer (basedepay, bclass, buffer);
    if (flow_ret != GST_FLOW_OK)
      break;
  }

done:

  gst_buffer_list_unref (list);

  return flow_ret;
}

560
static gboolean
Wim Taymans's avatar
Wim Taymans committed
561
gst_rtp_base_depayload_handle_event (GstRTPBaseDepayload * filter,
562
    GstEvent * event)
563 564
{
  gboolean res = TRUE;
565
  gboolean forward = TRUE;
566 567

  switch (GST_EVENT_TYPE (event)) {
568 569 570
    case GST_EVENT_FLUSH_STOP:
      gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
      filter->need_newsegment = TRUE;
571
      filter->priv->next_seqnum = -1;
572
      gst_event_replace (&filter->priv->segment_event, NULL);
573
      break;
574 575 576 577 578 579
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);

Wim Taymans's avatar
Wim Taymans committed
580
      res = gst_rtp_base_depayload_setcaps (filter, caps);
581 582 583
      forward = FALSE;
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
584
    case GST_EVENT_SEGMENT:
585
    {
586
      gst_event_copy_segment (event, &filter->segment);
587 588
      /* don't pass the event downstream, we generate our own segment including
       * the NTP time and other things we receive in caps */
589
      forward = FALSE;
590
      break;
591
    }
592 593
    case GST_EVENT_CUSTOM_DOWNSTREAM:
    {
Wim Taymans's avatar
Wim Taymans committed
594
      GstRTPBaseDepayloadClass *bclass;
595

Wim Taymans's avatar
Wim Taymans committed
596
      bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
597 598 599 600

      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
601
         * implementation will make time progress by pushing out a GAP event.
602
         * Subclasses can override and do one of the following:
603 604 605 606 607 608 609 610
         *  - 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);
611
        forward = FALSE;
612 613 614
      }
      break;
    }
615 616 617
    default:
      break;
  }
618 619 620 621 622 623

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

624 625 626
  return res;
}

627
static gboolean
Wim Taymans's avatar
Wim Taymans committed
628 629
gst_rtp_base_depayload_handle_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
630 631
{
  gboolean res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
632 633
  GstRTPBaseDepayload *filter;
  GstRTPBaseDepayloadClass *bclass;
634

Wim Taymans's avatar
Wim Taymans committed
635
  filter = GST_RTP_BASE_DEPAYLOAD (parent);
Wim Taymans's avatar
Wim Taymans committed
636
  bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
637 638
  if (bclass->handle_event)
    res = bclass->handle_event (filter, event);
639 640
  else
    gst_event_unref (event);
641 642 643 644

  return res;
}

645
static GstEvent *
646 647
create_segment_event (GstRTPBaseDepayload * filter, guint rtptime,
    GstClockTime position)
648 649
{
  GstEvent *event;
650
  GstClockTime start, stop, running_time;
Wim Taymans's avatar
Wim Taymans committed
651
  GstRTPBaseDepayloadPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
652
  GstSegment segment;
653 654 655

  priv = filter->priv;

656
  /* determining the start of the segment */
657
  start = filter->segment.start;
658 659 660 661 662 663 664 665 666
  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 */
667 668 669 670 671 672
    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));
673
      start = position - gap;
674
    }
675 676
  }

677
  /* determining the stop of the segment */
678
  stop = filter->segment.stop;
679
  if (priv->npt_stop != -1)
680
    stop = start + (priv->npt_stop - priv->npt_start);
681

682
  if (position == -1)
683
    position = start;
684 685

  running_time = gst_segment_to_running_time (&filter->segment,
686
      GST_FORMAT_TIME, start);
687

Wim Taymans's avatar
Wim Taymans committed
688 689 690
  gst_segment_init (&segment, GST_FORMAT_TIME);
  segment.rate = priv->play_speed;
  segment.applied_rate = priv->play_scale;
691
  segment.start = start;
Wim Taymans's avatar
Wim Taymans committed
692 693 694
  segment.stop = stop;
  segment.time = priv->npt_start;
  segment.position = position;
695
  segment.base = running_time;
Wim Taymans's avatar
Wim Taymans committed
696

697 698
  GST_DEBUG_OBJECT (filter, "Creating segment event %" GST_SEGMENT_FORMAT,
      &segment);
Wim Taymans's avatar
Wim Taymans committed
699
  event = gst_event_new_segment (&segment);
700 701 702 703

  return event;
}

704
typedef struct
705
{
Wim Taymans's avatar
Wim Taymans committed
706 707
  GstRTPBaseDepayload *depayload;
  GstRTPBaseDepayloadClass *bclass;
708
} HeaderData;
709

Wim Taymans's avatar
Wim Taymans committed
710 711
static gboolean
set_headers (GstBuffer ** buffer, guint idx, HeaderData * data)
712
{
Wim Taymans's avatar
Wim Taymans committed
713 714
  GstRTPBaseDepayload *depayload = data->depayload;
  GstRTPBaseDepayloadPrivate *priv = depayload->priv;
Wim Taymans's avatar
Wim Taymans committed
715
  GstClockTime pts, dts, duration;
716

Wim Taymans's avatar
Wim Taymans committed
717
  *buffer = gst_buffer_make_writable (*buffer);
718

Wim Taymans's avatar
Wim Taymans committed
719 720 721 722 723 724 725 726 727 728 729 730
  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;
731

732 733 734 735 736
  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;
  }
737

Wim Taymans's avatar
Wim Taymans committed
738 739 740 741 742
  /* 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
743
  return TRUE;
744 745 746
}

static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
747
gst_rtp_base_depayload_prepare_push (GstRTPBaseDepayload * filter,
Wim Taymans's avatar
Wim Taymans committed
748
    gboolean is_list, gpointer obj)
749 750 751 752
{
  HeaderData data;

  data.depayload = filter;
Wim Taymans's avatar
Wim Taymans committed
753
  data.bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
754 755

  if (is_list) {
Wim Taymans's avatar
Wim Taymans committed
756 757
    GstBufferList **blist = obj;
    gst_buffer_list_foreach (*blist, (GstBufferListFunc) set_headers, &data);
758
  } else {
Wim Taymans's avatar
Wim Taymans committed
759
    GstBuffer **buf = obj;
Wim Taymans's avatar
Wim Taymans committed
760
    set_headers (buf, 0, &data);
761
  }
762

763
  /* if this is the first buffer send a NEWSEGMENT */
764 765 766
  if (G_UNLIKELY (filter->priv->segment_event)) {
    gst_pad_push_event (filter->srcpad, filter->priv->segment_event);
    filter->priv->segment_event = NULL;
767 768 769
    GST_DEBUG_OBJECT (filter, "Pushed newsegment event on this first buffer");
  }

770
  return GST_FLOW_OK;
771 772 773
}

/**
Wim Taymans's avatar
Wim Taymans committed
774 775
 * gst_rtp_base_depayload_push:
 * @filter: a #GstRTPBaseDepayload
776 777 778 779 780
 * @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
781 782
 * This function will by default apply the last incomming timestamp on
 * the outgoing buffer when it didn't have a timestamp already.
783
 *
784
 * Returns: a #GstFlowReturn.
785 786
 */
GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
787
gst_rtp_base_depayload_push (GstRTPBaseDepayload * filter, GstBuffer * out_buf)
788
{
789 790
  GstFlowReturn res;

Wim Taymans's avatar
Wim Taymans committed
791
  res = gst_rtp_base_depayload_prepare_push (filter, FALSE, &out_buf);
792 793 794 795 796 797 798 799 800 801

  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
802 803
 * gst_rtp_base_depayload_push_list:
 * @filter: a #GstRTPBaseDepayload
804 805 806 807 808 809 810 811
 * @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
812
gst_rtp_base_depayload_push_list (GstRTPBaseDepayload * filter,
813 814 815 816
    GstBufferList * out_list)
{
  GstFlowReturn res;

Wim Taymans's avatar
Wim Taymans committed
817
  res = gst_rtp_base_depayload_prepare_push (filter, TRUE, &out_list);
818 819 820 821 822 823 824

  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;
825 826
}

827
/* convert the PacketLost event from a jitterbuffer to a GAP event.
828 829
 * subclasses can override this.  */
static gboolean
Wim Taymans's avatar
Wim Taymans committed
830
gst_rtp_base_depayload_packet_lost (GstRTPBaseDepayload * filter,
831 832
    GstEvent * event)
{
833
  GstClockTime timestamp, duration;
834 835 836 837 838 839 840 841 842 843 844 845
  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);

846 847
  /* send GAP event */
  sevent = gst_event_new_gap (timestamp, duration);
848 849 850 851

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

852
static GstStateChangeReturn
Wim Taymans's avatar
Wim Taymans committed
853
gst_rtp_base_depayload_change_state (GstElement * element,
854
    GstStateChange transition)
855
{
Wim Taymans's avatar
Wim Taymans committed
856 857
  GstRTPBaseDepayload *filter;
  GstRTPBaseDepayloadPrivate *priv;
858
  GstStateChangeReturn ret;
859

Wim Taymans's avatar
Wim Taymans committed
860
  filter = GST_RTP_BASE_DEPAYLOAD (element);
861
  priv = filter->priv;
862 863

  switch (transition) {
864
    case GST_STATE_CHANGE_NULL_TO_READY:
865
      break;
866
    case GST_STATE_CHANGE_READY_TO_PAUSED:
867
      filter->need_newsegment = TRUE;
868 869 870 871
      priv->npt_start = 0;
      priv->npt_stop = -1;
      priv->play_speed = 1.0;
      priv->play_scale = 1.0;
872
      priv->clock_base = -1;
873 874
      priv->next_seqnum = -1;
      priv->negotiated = FALSE;
875
      priv->discont = FALSE;
876
      break;
877
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
878 879 880 881 882
      break;
    default:
      break;
  }

883 884
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

885
  switch (transition) {
886
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
887
      break;
888
    case GST_STATE_CHANGE_PAUSED_TO_READY:
889
      gst_caps_replace (&priv->last_caps, NULL);