gstrtpptdemux.c 20.5 KB
Newer Older
1 2 3 4 5 6 7
/* 
 * RTP Demux element
 *
 * Copyright (C) 2005 Nokia Corporation.
 * @author Kai Vehmanen <kai.vehmanen@nokia.com>
 *
 * Loosely based on GStreamer gstdecodebin
8
 * Copyright (C) <2004> Wim Taymans <wim.taymans@gmail.com>
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
22 23
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
24 25
 */

Wim Taymans's avatar
Wim Taymans committed
26
/**
27
 * SECTION:element-rtpptdemux
Wim Taymans's avatar
Wim Taymans committed
28
 *
29
 * rtpptdemux acts as a demuxer for RTP packets based on the payload type of
30 31 32
 * the packets. Its main purpose is to allow an application to easily receive
 * and decode an RTP stream with multiple payload types.
 * 
Wim Taymans's avatar
Wim Taymans committed
33
 * For each payload type that is detected, a new pad will be created and the
34 35 36 37
 * #GstRtpPtDemux::new-payload-type signal will be emitted. When the payload for
 * the RTP stream changes, the #GstRtpPtDemux::payload-type-change signal will be
 * emitted.
 * 
38 39 40
 * The element will try to set complete and unique application/x-rtp caps
 * on the output pads based on the result of the #GstRtpPtDemux::request-pt-map
 * signal.
41 42
 * 
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
43
 * <title>Example pipelines</title>
44
 * |[
45
 * gst-launch-1.0 udpsrc caps="application/x-rtp" ! rtpptdemux ! fakesink
46 47
 * ]| Takes an RTP stream and send the RTP packets with the first detected
 * payload type to fakesink, discarding the other payload types.
Wim Taymans's avatar
Wim Taymans committed
48 49 50
 * </refsect2>
 */

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
/*
 * Contributors:
 * Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
 */
/*
 * Status:
 *  - works with the test_rtpdemux.c tool
 *
 * Check:
 *  - is emitting a signal enough, or should we
 *    use GstEvent to notify downstream elements
 *    of the new packet... no?
 *
 * Notes:
 *  - emits event both for new PTs, and whenever
 *    a PT is changed
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

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

77 78
#include "gstrtpptdemux.h"

79 80 81 82 83
/* generic templates */
static GstStaticPadTemplate rtp_pt_demux_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
84
    GST_STATIC_CAPS ("application/x-rtp")
85 86 87
    );

static GstStaticPadTemplate rtp_pt_demux_src_template =
88
GST_STATIC_PAD_TEMPLATE ("src_%u",
89 90
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
91 92
    GST_STATIC_CAPS ("application/x-rtp, " "payload = (int) [ 0, 255 ]")
    );
93 94 95 96

GST_DEBUG_CATEGORY_STATIC (gst_rtp_pt_demux_debug);
#define GST_CAT_DEFAULT gst_rtp_pt_demux_debug

Stefan Kost's avatar
Stefan Kost committed
97
/*
98 99
 * Item for storing GstPad<->pt pairs.
 */
100
struct _GstRtpPtDemuxPad
101 102 103
{
  GstPad *pad;        /**< pointer to the actual pad */
  gint pt;             /**< RTP payload-type attached to pad */
104
  gboolean newcaps;
105 106 107 108 109
};

/* signals */
enum
{
110
  SIGNAL_REQUEST_PT_MAP,
111 112
  SIGNAL_NEW_PAYLOAD_TYPE,
  SIGNAL_PAYLOAD_TYPE_CHANGE,
Wim Taymans's avatar
Wim Taymans committed
113
  SIGNAL_CLEAR_PT_MAP,
114 115 116
  LAST_SIGNAL
};

117 118 119 120 121 122
enum
{
  PROP_0,
  PROP_IGNORED_PTS,
};

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
123 124
#define gst_rtp_pt_demux_parent_class parent_class
G_DEFINE_TYPE (GstRtpPtDemux, gst_rtp_pt_demux, GST_TYPE_ELEMENT);
125 126 127

static void gst_rtp_pt_demux_finalize (GObject * object);

128 129
static void gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux);
static gboolean gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux);
130

Wim Taymans's avatar
Wim Taymans committed
131 132 133 134
static gboolean gst_rtp_pt_demux_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
static GstFlowReturn gst_rtp_pt_demux_chain (GstPad * pad, GstObject * parent,
    GstBuffer * buf);
135 136
static GstStateChangeReturn gst_rtp_pt_demux_change_state (GstElement * element,
    GstStateChange transition);
137
static void gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux);
138

139
static GstPad *find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt);
140

Wim Taymans's avatar
Wim Taymans committed
141 142
static gboolean gst_rtp_pt_demux_src_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
143 144


145 146
static guint gst_rtp_pt_demux_signals[LAST_SIGNAL] = { 0 };

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
static void
gst_rtp_pt_demux_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstRtpPtDemux *rtpptdemux = GST_RTP_PT_DEMUX (object);

  switch (prop_id) {
    case PROP_IGNORED_PTS:
      g_value_copy (value, &rtpptdemux->ignored_pts);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_rtp_pt_demux_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstRtpPtDemux *rtpptdemux = GST_RTP_PT_DEMUX (object);

  switch (prop_id) {
    case PROP_IGNORED_PTS:
      g_value_copy (&rtpptdemux->ignored_pts, value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

179
static void
180
gst_rtp_pt_demux_class_init (GstRtpPtDemuxClass * klass)
181 182 183 184 185 186 187
{
  GObjectClass *gobject_klass;
  GstElementClass *gstelement_klass;

  gobject_klass = (GObjectClass *) klass;
  gstelement_klass = (GstElementClass *) klass;

188
  /**
189
   * GstRtpPtDemux::request-pt-map:
190 191 192 193 194 195 196
   * @demux: the object which received the signal
   * @pt: the payload type
   *
   * Request the payload type as #GstCaps for @pt.
   */
  gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP] =
      g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
197
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, request_pt_map),
198
      NULL, NULL, g_cclosure_marshal_generic, GST_TYPE_CAPS, 1, G_TYPE_UINT);
199 200

  /**
201
   * GstRtpPtDemux::new-payload-type:
202 203 204 205 206 207
   * @demux: the object which received the signal
   * @pt: the payload type
   * @pad: the pad with the new payload
   *
   * Emited when a new payload type pad has been created in @demux.
   */
208
  gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE] =
209
      g_signal_new ("new-payload-type", G_TYPE_FROM_CLASS (klass),
210
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, new_payload_type),
211 212
      NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT,
      GST_TYPE_PAD);
213 214

  /**
215
   * GstRtpPtDemux::payload-type-change:
216 217 218 219 220
   * @demux: the object which received the signal
   * @pt: the new payload type
   *
   * Emited when the payload type changed.
   */
221
  gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE] =
222
      g_signal_new ("payload-type-change", G_TYPE_FROM_CLASS (klass),
223
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass,
224 225
          payload_type_change), NULL, NULL, g_cclosure_marshal_VOID__UINT,
      G_TYPE_NONE, 1, G_TYPE_UINT);
226

Wim Taymans's avatar
Wim Taymans committed
227
  /**
228
   * GstRtpPtDemux::clear-pt-map:
Wim Taymans's avatar
Wim Taymans committed
229 230 231 232 233 234 235
   * @demux: the object which received the signal
   *
   * The application can call this signal to instruct the element to discard the
   * currently cached payload type map.
   */
  gst_rtp_pt_demux_signals[SIGNAL_CLEAR_PT_MAP] =
      g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
236
      G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass,
Wim Taymans's avatar
Wim Taymans committed
237 238 239
          clear_pt_map), NULL, NULL, g_cclosure_marshal_VOID__VOID,
      G_TYPE_NONE, 0, G_TYPE_NONE);

240 241 242
  gobject_klass->set_property = gst_rtp_pt_demux_set_property;
  gobject_klass->get_property = gst_rtp_pt_demux_get_property;

243 244 245 246 247 248
  /**
   * GstRtpPtDemux:ignored-payload-types:
   *
   * If specified, packets with an ignored payload type will be dropped,
   * instead of causing a new pad to be exposed for these to be pushed on.
   *
249 250 251 252 253
   * This is for example useful to drop FEC protection packets, as they
   * need to go through the #GstRtpJitterBuffer, but cease to be useful
   * past that point, #GstRtpBin will make use of this property for that
   * purpose.
   *
254 255
   * Since: 1.14
   */
256 257 258 259 260 261 262 263 264
  g_object_class_install_property (gobject_klass, PROP_IGNORED_PTS,
      gst_param_spec_array ("ignored-payload-types",
          "Ignored payload types",
          "Packets with these payload types will be dropped",
          g_param_spec_int ("payload-types", "payload-types", "Payload types",
              0, G_MAXINT, 0,
              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

265
  gobject_klass->finalize = gst_rtp_pt_demux_finalize;
266 267 268 269

  gstelement_klass->change_state =
      GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_change_state);

Wim Taymans's avatar
Wim Taymans committed
270 271
  klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_clear_pt_map);

272 273 274 275
  gst_element_class_add_static_pad_template (gstelement_klass,
      &rtp_pt_demux_sink_template);
  gst_element_class_add_static_pad_template (gstelement_klass,
      &rtp_pt_demux_src_template);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
276

277
  gst_element_class_set_static_metadata (gstelement_klass, "RTP Demux",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
278 279 280 281
      "Demux/Network/RTP",
      "Parses codec streams transmitted in the same RTP session",
      "Kai Vehmanen <kai.vehmanen@nokia.com>");

282 283 284 285 286
  GST_DEBUG_CATEGORY_INIT (gst_rtp_pt_demux_debug,
      "rtpptdemux", 0, "RTP codec demuxer");
}

static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
287
gst_rtp_pt_demux_init (GstRtpPtDemux * ptdemux)
288 289 290 291 292 293 294 295 296
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (ptdemux);

  ptdemux->sink =
      gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
          "sink"), "sink");
  g_assert (ptdemux->sink != NULL);

  gst_pad_set_chain_function (ptdemux->sink, gst_rtp_pt_demux_chain);
297
  gst_pad_set_event_function (ptdemux->sink, gst_rtp_pt_demux_sink_event);
298 299

  gst_element_add_pad (GST_ELEMENT (ptdemux), ptdemux->sink);
300 301

  g_value_init (&ptdemux->ignored_pts, GST_TYPE_ARRAY);
302 303 304 305 306
}

static void
gst_rtp_pt_demux_finalize (GObject * object)
{
307
  gst_rtp_pt_demux_release (GST_RTP_PT_DEMUX (object));
308

309 310
  g_value_unset (&GST_RTP_PT_DEMUX (object)->ignored_pts);

311 312 313
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

314 315 316 317 318 319
static GstCaps *
gst_rtp_pt_demux_get_caps (GstRtpPtDemux * rtpdemux, guint pt)
{
  GstCaps *caps;
  GValue ret = { 0 };
  GValue args[2] = { {0}, {0} };
320
  GstCaps *sink_caps;
321 322 323 324 325 326 327 328 329 330 331 332 333

  /* figure out the caps */
  g_value_init (&args[0], GST_TYPE_ELEMENT);
  g_value_set_object (&args[0], rtpdemux);
  g_value_init (&args[1], G_TYPE_UINT);
  g_value_set_uint (&args[1], pt);

  g_value_init (&ret, GST_TYPE_CAPS);
  g_value_set_boxed (&ret, NULL);

  g_signal_emitv (args, gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP], 0,
      &ret);

334 335 336 337
  g_value_unset (&args[0]);
  g_value_unset (&args[1]);
  caps = g_value_dup_boxed (&ret);
  g_value_unset (&ret);
338 339

  sink_caps = gst_pad_get_current_caps (rtpdemux->sink);
340

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
  if (sink_caps) {
    if (caps == NULL) {
      caps = gst_caps_ref (sink_caps);
    } else {
      GstStructure *s1;
      GstStructure *s2;
      guint ssrc;

      caps = gst_caps_make_writable (caps);
      s1 = gst_caps_get_structure (sink_caps, 0);
      s2 = gst_caps_get_structure (caps, 0);

      gst_structure_get_uint (s1, "ssrc", &ssrc);
      gst_structure_set (s2, "ssrc", G_TYPE_UINT, ssrc, NULL);
    }

    gst_caps_unref (sink_caps);
  }
359

360 361 362 363 364
  GST_DEBUG ("pt %d, got caps %" GST_PTR_FORMAT, pt, caps);

  return caps;
}

Wim Taymans's avatar
Wim Taymans committed
365
static void
366
gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux)
Wim Taymans's avatar
Wim Taymans committed
367
{
368 369 370 371 372 373 374 375 376 377
  GSList *walk;

  GST_OBJECT_LOCK (rtpdemux);
  GST_DEBUG ("clearing pt map");
  for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
    GstRtpPtDemuxPad *pad = walk->data;

    pad->newcaps = TRUE;
  }
  GST_OBJECT_UNLOCK (rtpdemux);
Wim Taymans's avatar
Wim Taymans committed
378 379
}

380 381 382 383 384 385 386 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 414 415 416
static gboolean
need_caps_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt)
{
  GSList *walk;
  gboolean ret = FALSE;

  GST_OBJECT_LOCK (rtpdemux);
  for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
    GstRtpPtDemuxPad *pad = walk->data;

    if (pad->pt == pt) {
      ret = pad->newcaps;
    }
  }
  GST_OBJECT_UNLOCK (rtpdemux);

  return ret;
}


static void
clear_newcaps_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt)
{
  GSList *walk;

  GST_OBJECT_LOCK (rtpdemux);
  for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
    GstRtpPtDemuxPad *pad = walk->data;

    if (pad->pt == pt) {
      pad->newcaps = FALSE;
      break;
    }
  }
  GST_OBJECT_UNLOCK (rtpdemux);
}

417 418 419 420 421
static gboolean
forward_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
{
  GstPad *srcpad = GST_PAD_CAST (user_data);

422 423 424
  /* Stream start and caps have already been pushed */
  if (GST_EVENT_TYPE (*event) >= GST_EVENT_SEGMENT)
    gst_pad_push_event (srcpad, gst_event_ref (*event));
425 426 427 428

  return TRUE;
}

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
static gboolean
gst_rtp_pt_demux_pt_is_ignored (GstRtpPtDemux * ptdemux, guint8 pt)
{
  gboolean ret = FALSE;
  guint i;

  for (i = 0; i < gst_value_array_get_size (&ptdemux->ignored_pts); i++) {
    const GValue *tmp = gst_value_array_get_value (&ptdemux->ignored_pts, i);

    if (g_value_get_int (tmp) == pt) {
      ret = TRUE;
      break;
    }
  }

  return ret;
}

447
static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
448
gst_rtp_pt_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
449 450
{
  GstFlowReturn ret = GST_FLOW_OK;
451
  GstRtpPtDemux *rtpdemux;
452 453
  guint8 pt;
  GstPad *srcpad;
454
  GstCaps *caps;
455
  GstRTPBuffer rtp = { NULL };
456

Wim Taymans's avatar
Wim Taymans committed
457
  rtpdemux = GST_RTP_PT_DEMUX (parent);
458

459
  if (!gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp))
460
    goto invalid_buffer;
461

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
462 463
  pt = gst_rtp_buffer_get_payload_type (&rtp);
  gst_rtp_buffer_unmap (&rtp);
464

465 466 467
  if (gst_rtp_pt_demux_pt_is_ignored (rtpdemux, pt))
    goto ignored;

468 469
  GST_DEBUG_OBJECT (rtpdemux, "received buffer for pt %d", pt);

470 471
  srcpad = find_pad_for_pt (rtpdemux, pt);
  if (srcpad == NULL) {
472
    /* new PT, create a src pad */
473
    GstRtpPtDemuxPad *rtpdemuxpad;
474 475 476 477
    GstElementClass *klass;
    GstPadTemplate *templ;
    gchar *padname;

478 479 480 481
    caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt);
    if (!caps)
      goto no_caps;

482 483 484
    if (gst_rtp_pt_demux_pt_is_ignored (rtpdemux, pt))
      goto ignored;

485
    klass = GST_ELEMENT_GET_CLASS (rtpdemux);
486 487
    templ = gst_element_class_get_pad_template (klass, "src_%u");
    padname = g_strdup_printf ("src_%u", pt);
488
    srcpad = gst_pad_new_from_template (templ, padname);
489
    gst_pad_use_fixed_caps (srcpad);
490
    g_free (padname);
491
    gst_pad_set_event_function (srcpad, gst_rtp_pt_demux_src_event);
492

493
    GST_DEBUG ("Adding pt=%d to the list.", pt);
494
    rtpdemuxpad = g_slice_new0 (GstRtpPtDemuxPad);
495
    rtpdemuxpad->pt = pt;
496
    rtpdemuxpad->newcaps = FALSE;
497
    rtpdemuxpad->pad = srcpad;
498
    gst_object_ref (srcpad);
499
    GST_OBJECT_LOCK (rtpdemux);
500
    rtpdemux->srcpads = g_slist_append (rtpdemux->srcpads, rtpdemuxpad);
501
    GST_OBJECT_UNLOCK (rtpdemux);
502

503
    gst_pad_set_active (srcpad, TRUE);
504

505 506 507 508

    /* First push the stream-start event, it must always come first */
    gst_pad_push_event (srcpad,
        gst_pad_get_sticky_event (rtpdemux->sink, GST_EVENT_STREAM_START, 0));
509 510

    /* Then caps event is sent */
511 512 513 514 515
    caps = gst_caps_make_writable (caps);
    gst_caps_set_simple (caps, "payload", G_TYPE_INT, pt, NULL);
    gst_pad_set_caps (srcpad, caps);
    gst_caps_unref (caps);

516 517 518 519
    /* First sticky events on sink pad are forwarded to the new src pad */
    gst_pad_sticky_events_foreach (rtpdemux->sink, forward_sticky_events,
        srcpad);

Wim Taymans's avatar
Wim Taymans committed
520
    gst_element_add_pad (GST_ELEMENT_CAST (rtpdemux), srcpad);
521

522
    GST_DEBUG ("emitting new-payload-type for pt %d", pt);
523 524
    g_signal_emit (G_OBJECT (rtpdemux),
        gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE], 0, pt, srcpad);
525 526 527 528 529 530 531 532 533 534 535 536
  }

  if (pt != rtpdemux->last_pt) {
    gint emit_pt = pt;

    /* our own signal with an extra flag that this is the only pad */
    rtpdemux->last_pt = pt;
    GST_DEBUG ("emitting payload-type-changed for pt %d", emit_pt);
    g_signal_emit (G_OBJECT (rtpdemux),
        gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE], 0, emit_pt);
  }

537 538
  while (need_caps_for_pt (rtpdemux, pt)) {
    GST_DEBUG ("need new caps for %d", pt);
539 540 541 542
    caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt);
    if (!caps)
      goto no_caps;

543 544
    clear_newcaps_for_pt (rtpdemux, pt);

545 546 547
    caps = gst_caps_make_writable (caps);
    gst_caps_set_simple (caps, "payload", G_TYPE_INT, pt, NULL);
    gst_pad_set_caps (srcpad, caps);
548
    gst_caps_unref (caps);
549 550
  }

551
  /* push to srcpad */
552
  ret = gst_pad_push (srcpad, buf);
553

554 555
  gst_object_unref (srcpad);

556
  return ret;
557

558 559 560 561 562 563 564
ignored:
  {
    GST_DEBUG_OBJECT (rtpdemux, "Dropped buffer for pt %d", pt);
    gst_buffer_unref (buf);
    return GST_FLOW_OK;
  }

565 566 567
  /* ERRORS */
invalid_buffer:
  {
568 569
    /* this should not be fatal */
    GST_ELEMENT_WARNING (rtpdemux, STREAM, DEMUX, (NULL),
570 571 572 573
        ("Dropping invalid RTP payload"));
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
574 575 576 577 578
no_caps:
  {
    GST_ELEMENT_ERROR (rtpdemux, STREAM, DECODE, (NULL),
        ("Could not get caps for payload"));
    gst_buffer_unref (buf);
579 580
    if (srcpad)
      gst_object_unref (srcpad);
581 582
    return GST_FLOW_ERROR;
  }
583 584
}

585
static GstPad *
586
find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt)
587
{
588
  GstPad *respad = NULL;
589
  GSList *walk;
590

591
  GST_OBJECT_LOCK (rtpdemux);
592 593
  for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
    GstRtpPtDemuxPad *pad = walk->data;
594 595

    if (pad->pt == pt) {
596
      respad = gst_object_ref (pad->pad);
597 598 599
      break;
    }
  }
600 601
  GST_OBJECT_UNLOCK (rtpdemux);

602 603 604
  return respad;
}

605
static gboolean
Wim Taymans's avatar
Wim Taymans committed
606
gst_rtp_pt_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
607 608 609 610
{
  GstRtpPtDemux *rtpdemux;
  gboolean res = FALSE;

Wim Taymans's avatar
Wim Taymans committed
611
  rtpdemux = GST_RTP_PT_DEMUX (parent);
612 613

  switch (GST_EVENT_TYPE (event)) {
614 615 616
    case GST_EVENT_CAPS:
    {
      gst_rtp_pt_demux_clear_pt_map (rtpdemux);
617 618
      /* don't forward the event, we cleared the ptmap and on the next buffer we
       * will add the pt to the caps and push a new caps event */
619 620 621 622
      gst_event_unref (event);
      res = TRUE;
      break;
    }
623 624 625 626 627 628 629
    case GST_EVENT_CUSTOM_DOWNSTREAM:
    {
      const GstStructure *s;

      s = gst_event_get_structure (event);

      if (gst_structure_has_name (s, "GstRTPPacketLost")) {
630
        GstPad *srcpad = find_pad_for_pt (rtpdemux, rtpdemux->last_pt);
631

632 633 634 635
        if (srcpad) {
          res = gst_pad_push_event (srcpad, event);
          gst_object_unref (srcpad);
        } else {
636
          gst_event_unref (event);
637
        }
638 639

      } else {
Wim Taymans's avatar
Wim Taymans committed
640
        res = gst_pad_event_default (pad, parent, event);
641
      }
642
      break;
643 644
    }
    default:
Wim Taymans's avatar
Wim Taymans committed
645
      res = gst_pad_event_default (pad, parent, event);
646 647
      break;
  }
648

649 650 651 652
  return res;
}


653
static gboolean
Wim Taymans's avatar
Wim Taymans committed
654
gst_rtp_pt_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
655 656 657 658
{
  GstRtpPtDemux *demux;
  const GstStructure *s;

Wim Taymans's avatar
Wim Taymans committed
659
  demux = GST_RTP_PT_DEMUX (parent);
660 661 662 663 664 665 666 667 668

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CUSTOM_UPSTREAM:
    case GST_EVENT_CUSTOM_BOTH:
    case GST_EVENT_CUSTOM_BOTH_OOB:
      s = gst_event_get_structure (event);
      if (s && !gst_structure_has_field (s, "payload")) {
        GSList *walk;

669
        GST_OBJECT_LOCK (demux);
670 671 672 673
        for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
          GstRtpPtDemuxPad *dpad = (GstRtpPtDemuxPad *) walk->data;

          if (dpad->pad == pad) {
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
674 675
            GstStructure *ws;

676 677 678
            event =
                GST_EVENT_CAST (gst_mini_object_make_writable
                (GST_MINI_OBJECT_CAST (event)));
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
679 680
            ws = gst_event_writable_structure (event);
            gst_structure_set (ws, "payload", G_TYPE_UINT, dpad->pt, NULL);
681 682 683
            break;
          }
        }
684
        GST_OBJECT_UNLOCK (demux);
685 686 687 688 689 690
      }
      break;
    default:
      break;
  }

Wim Taymans's avatar
Wim Taymans committed
691
  return gst_pad_event_default (pad, parent, event);
692 693
}

Stefan Kost's avatar
Stefan Kost committed
694
/*
695 696 697
 * Reserves resources for the object.
 */
static gboolean
698
gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux)
699
{
700 701
  ptdemux->srcpads = NULL;
  ptdemux->last_pt = 0xFFFF;
702

703
  return TRUE;
704 705
}

Stefan Kost's avatar
Stefan Kost committed
706
/*
707 708 709
 * Free resources for the object.
 */
static void
710
gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux)
711
{
712
  GSList *tmppads;
713 714
  GSList *walk;

715 716 717 718 719 720
  GST_OBJECT_LOCK (ptdemux);
  tmppads = ptdemux->srcpads;
  ptdemux->srcpads = NULL;
  GST_OBJECT_UNLOCK (ptdemux);

  for (walk = tmppads; walk; walk = g_slist_next (walk)) {
721
    GstRtpPtDemuxPad *pad = walk->data;
722

723 724
    gst_pad_set_active (pad->pad, FALSE);
    gst_element_remove_pad (GST_ELEMENT_CAST (ptdemux), pad->pad);
725
    g_slice_free (GstRtpPtDemuxPad, pad);
726
  }
727
  g_slist_free (tmppads);
728 729 730 731 732 733
}

static GstStateChangeReturn
gst_rtp_pt_demux_change_state (GstElement * element, GstStateChange transition)
{
  GstStateChangeReturn ret;
734
  GstRtpPtDemux *ptdemux;
735 736 737 738 739

  ptdemux = GST_RTP_PT_DEMUX (element);

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
740
      if (gst_rtp_pt_demux_setup (ptdemux) != TRUE)
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
        ret = GST_STATE_CHANGE_FAILURE;
      break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
    default:
      break;
  }

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

  switch (transition) {
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      break;
    case GST_STATE_CHANGE_READY_TO_NULL:
756
      gst_rtp_pt_demux_release (ptdemux);
757 758 759 760 761 762 763
      break;
    default:
      break;
  }

  return ret;
}