2 * Copyright (C) <2005> Philippe Khalaf <burger@speedy.org>
3 * Copyright (C) <2005> Nokia Corporation <kai.vehmanen@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:gstrtpbasedepayload
23 * @title: GstRTPBaseDepayload
24 * @short_description: Base class for RTP depayloader
26 * Provides a base class for RTP depayloaders
32 #include "gstrtpbasedepayload.h"
33 #include "gstrtpmeta.h"
34 #include "gstrtphdrext.h"
36 GST_DEBUG_CATEGORY_STATIC (rtpbasedepayload_debug);
37 #define GST_CAT_DEFAULT (rtpbasedepayload_debug)
39 static GstStaticCaps ntp_reference_timestamp_caps =
40 GST_STATIC_CAPS ("timestamp/x-ntp");
42 struct _GstRTPBaseDepayloadPrivate
44 GstClockTime npt_start;
45 GstClockTime npt_stop;
54 GstClockTime duration;
63 gboolean auto_hdr_ext;
68 GstEvent *segment_event;
69 guint32 segment_seqnum; /* Note: this is a GstEvent seqnum */
72 GstBuffer *input_buffer;
74 GstFlowReturn process_flow_ret;
76 /* array of GstRTPHeaderExtension's * */
77 GPtrArray *header_exts;
80 /* Filter signals and args */
84 SIGNAL_REQUEST_EXTENSION,
86 SIGNAL_CLEAR_EXTENSIONS,
90 static guint gst_rtp_base_depayload_signals[LAST_SIGNAL] = { 0 };
92 #define DEFAULT_SOURCE_INFO FALSE
93 #define DEFAULT_MAX_REORDER 100
94 #define DEFAULT_AUTO_HEADER_EXTENSION TRUE
102 PROP_AUTO_HEADER_EXTENSION,
106 static void gst_rtp_base_depayload_finalize (GObject * object);
107 static void gst_rtp_base_depayload_set_property (GObject * object,
108 guint prop_id, const GValue * value, GParamSpec * pspec);
109 static void gst_rtp_base_depayload_get_property (GObject * object,
110 guint prop_id, GValue * value, GParamSpec * pspec);
112 static GstFlowReturn gst_rtp_base_depayload_chain (GstPad * pad,
113 GstObject * parent, GstBuffer * in);
114 static GstFlowReturn gst_rtp_base_depayload_chain_list (GstPad * pad,
115 GstObject * parent, GstBufferList * list);
116 static gboolean gst_rtp_base_depayload_handle_sink_event (GstPad * pad,
117 GstObject * parent, GstEvent * event);
119 static GstStateChangeReturn gst_rtp_base_depayload_change_state (GstElement *
120 element, GstStateChange transition);
122 static gboolean gst_rtp_base_depayload_packet_lost (GstRTPBaseDepayload *
123 filter, GstEvent * event);
124 static gboolean gst_rtp_base_depayload_handle_event (GstRTPBaseDepayload *
125 filter, GstEvent * event);
127 static GstElementClass *parent_class = NULL;
128 static gint private_offset = 0;
130 static void gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass *
132 static void gst_rtp_base_depayload_init (GstRTPBaseDepayload * rtpbasepayload,
133 GstRTPBaseDepayloadClass * klass);
134 static GstEvent *create_segment_event (GstRTPBaseDepayload * filter,
135 guint rtptime, GstClockTime position);
137 static void gst_rtp_base_depayload_add_extension (GstRTPBaseDepayload *
138 rtpbasepayload, GstRTPHeaderExtension * ext);
139 static void gst_rtp_base_depayload_clear_extensions (GstRTPBaseDepayload *
143 gst_rtp_base_depayload_get_type (void)
145 static GType rtp_base_depayload_type = 0;
147 if (g_once_init_enter ((gsize *) & rtp_base_depayload_type)) {
148 static const GTypeInfo rtp_base_depayload_info = {
149 sizeof (GstRTPBaseDepayloadClass),
152 (GClassInitFunc) gst_rtp_base_depayload_class_init,
155 sizeof (GstRTPBaseDepayload),
157 (GInstanceInitFunc) gst_rtp_base_depayload_init,
161 _type = g_type_register_static (GST_TYPE_ELEMENT, "GstRTPBaseDepayload",
162 &rtp_base_depayload_info, G_TYPE_FLAG_ABSTRACT);
165 g_type_add_instance_private (_type,
166 sizeof (GstRTPBaseDepayloadPrivate));
168 g_once_init_leave ((gsize *) & rtp_base_depayload_type, _type);
170 return rtp_base_depayload_type;
173 static inline GstRTPBaseDepayloadPrivate *
174 gst_rtp_base_depayload_get_instance_private (GstRTPBaseDepayload * self)
176 return (G_STRUCT_MEMBER_P (self, private_offset));
179 static GstRTPHeaderExtension *
180 gst_rtp_base_depayload_request_extension_default (GstRTPBaseDepayload *
181 depayload, guint ext_id, const gchar * uri)
183 GstRTPHeaderExtension *ext = NULL;
185 if (!depayload->priv->auto_hdr_ext)
188 ext = gst_rtp_header_extension_create_from_uri (uri);
190 GST_DEBUG_OBJECT (depayload,
191 "Automatically enabled extension %s for uri \'%s\'",
192 GST_ELEMENT_NAME (ext), uri);
194 gst_rtp_header_extension_set_id (ext, ext_id);
196 GST_DEBUG_OBJECT (depayload,
197 "Didn't find any extension implementing uri \'%s\'", uri);
204 extension_accumulator (GSignalInvocationHint * ihint,
205 GValue * return_accu, const GValue * handler_return, gpointer data)
209 /* Call default handler if user callback didn't create the extension */
210 ext = g_value_get_object (handler_return);
214 g_value_set_object (return_accu, ext);
219 gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass * klass)
221 GObjectClass *gobject_class;
222 GstElementClass *gstelement_class;
224 gobject_class = G_OBJECT_CLASS (klass);
225 gstelement_class = (GstElementClass *) klass;
226 parent_class = g_type_class_peek_parent (klass);
228 if (private_offset != 0)
229 g_type_class_adjust_private_offset (klass, &private_offset);
231 gobject_class->finalize = gst_rtp_base_depayload_finalize;
232 gobject_class->set_property = gst_rtp_base_depayload_set_property;
233 gobject_class->get_property = gst_rtp_base_depayload_get_property;
237 * GstRTPBaseDepayload:stats:
239 * Various depayloader statistics retrieved atomically (and are therefore
240 * synchroized with each other). This property return a GstStructure named
241 * application/x-rtp-depayload-stats containing the following fields relating to
242 * the last processed buffer and current state of the stream being depayloaded:
244 * * `clock-rate`: #G_TYPE_UINT, clock-rate of the stream
245 * * `npt-start`: #G_TYPE_UINT64, time of playback start
246 * * `npt-stop`: #G_TYPE_UINT64, time of playback stop
247 * * `play-speed`: #G_TYPE_DOUBLE, the playback speed
248 * * `play-scale`: #G_TYPE_DOUBLE, the playback scale
249 * * `running-time-dts`: #G_TYPE_UINT64, the last running-time of the
251 * * `running-time-pts`: #G_TYPE_UINT64, the last running-time of the
253 * * `seqnum`: #G_TYPE_UINT, the last seen seqnum
254 * * `timestamp`: #G_TYPE_UINT, the last seen RTP timestamp
256 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_STATS,
257 g_param_spec_boxed ("stats", "Statistics", "Various statistics",
258 GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
261 * GstRTPBaseDepayload:source-info:
263 * Add RTP source information found in RTP header as meta to output buffer.
267 g_object_class_install_property (gobject_class, PROP_SOURCE_INFO,
268 g_param_spec_boolean ("source-info", "RTP source information",
269 "Add RTP source information as buffer meta",
270 DEFAULT_SOURCE_INFO, G_PARAM_READWRITE));
273 * GstRTPBaseDepayload:max-reorder:
275 * Max seqnum reorder before the sender is assumed to have restarted.
277 * When max-reorder is set to 0 all reordered/duplicate packets are
278 * considered coming from a restarted sender.
282 g_object_class_install_property (gobject_class, PROP_MAX_REORDER,
283 g_param_spec_int ("max-reorder", "Max Reorder",
284 "Max seqnum reorder before assuming sender has restarted",
285 0, G_MAXINT, DEFAULT_MAX_REORDER, G_PARAM_READWRITE));
288 * GstRTPBaseDepayload:auto-header-extension:
290 * If enabled, the depayloader will automatically try to enable all the
291 * RTP header extensions provided in the sink caps, saving the application
292 * the need to handle these extensions manually using the
293 * GstRTPBaseDepayload::request-extension: signal.
297 g_object_class_install_property (G_OBJECT_CLASS (klass),
298 PROP_AUTO_HEADER_EXTENSION, g_param_spec_boolean ("auto-header-extension",
299 "Automatic RTP header extension",
300 "Whether RTP header extensions should be automatically enabled, if an implementation is available",
301 DEFAULT_AUTO_HEADER_EXTENSION,
302 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
305 * GstRTPBaseDepayload::request-extension:
306 * @object: the #GstRTPBaseDepayload
307 * @ext_id: the extension id being requested
308 * @ext_uri: (nullable): the extension URI being requested
310 * The returned @ext must be configured with the correct @ext_id and with the
311 * necessary attributes as required by the extension implementation.
313 * Returns: (transfer full) (nullable): the #GstRTPHeaderExtension for @ext_id, or %NULL
317 gst_rtp_base_depayload_signals[SIGNAL_REQUEST_EXTENSION] =
318 g_signal_new_class_handler ("request-extension",
319 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
320 G_CALLBACK (gst_rtp_base_depayload_request_extension_default),
321 extension_accumulator, NULL, NULL,
322 GST_TYPE_RTP_HEADER_EXTENSION, 2, G_TYPE_UINT, G_TYPE_STRING);
325 * GstRTPBaseDepayload::add-extension:
326 * @object: the #GstRTPBaseDepayload
327 * @ext: (transfer full): the #GstRTPHeaderExtension
329 * Add @ext as an extension for reading part of an RTP header extension from
330 * incoming RTP packets.
334 gst_rtp_base_depayload_signals[SIGNAL_ADD_EXTENSION] =
335 g_signal_new_class_handler ("add-extension", G_TYPE_FROM_CLASS (klass),
336 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
337 G_CALLBACK (gst_rtp_base_depayload_add_extension), NULL, NULL, NULL,
338 G_TYPE_NONE, 1, GST_TYPE_RTP_HEADER_EXTENSION);
341 * GstRTPBaseDepayload::clear-extensions:
342 * @object: the #GstRTPBaseDepayload
344 * Clear all RTP header extensions used by this depayloader.
348 gst_rtp_base_depayload_signals[SIGNAL_CLEAR_EXTENSIONS] =
349 g_signal_new_class_handler ("clear-extensions", G_TYPE_FROM_CLASS (klass),
350 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
351 G_CALLBACK (gst_rtp_base_depayload_clear_extensions), NULL, NULL, NULL,
354 gstelement_class->change_state = gst_rtp_base_depayload_change_state;
356 klass->packet_lost = gst_rtp_base_depayload_packet_lost;
357 klass->handle_event = gst_rtp_base_depayload_handle_event;
359 GST_DEBUG_CATEGORY_INIT (rtpbasedepayload_debug, "rtpbasedepayload", 0,
360 "Base class for RTP Depayloaders");
364 gst_rtp_base_depayload_init (GstRTPBaseDepayload * filter,
365 GstRTPBaseDepayloadClass * klass)
367 GstPadTemplate *pad_template;
368 GstRTPBaseDepayloadPrivate *priv;
370 priv = gst_rtp_base_depayload_get_instance_private (filter);
374 GST_DEBUG_OBJECT (filter, "init");
377 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "sink");
378 g_return_if_fail (pad_template != NULL);
379 filter->sinkpad = gst_pad_new_from_template (pad_template, "sink");
380 gst_pad_set_chain_function (filter->sinkpad, gst_rtp_base_depayload_chain);
381 gst_pad_set_chain_list_function (filter->sinkpad,
382 gst_rtp_base_depayload_chain_list);
383 gst_pad_set_event_function (filter->sinkpad,
384 gst_rtp_base_depayload_handle_sink_event);
385 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
388 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src");
389 g_return_if_fail (pad_template != NULL);
390 filter->srcpad = gst_pad_new_from_template (pad_template, "src");
391 gst_pad_use_fixed_caps (filter->srcpad);
392 gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
396 priv->play_speed = 1.0;
397 priv->play_scale = 1.0;
398 priv->clock_base = -1;
399 priv->onvif_mode = FALSE;
404 priv->source_info = DEFAULT_SOURCE_INFO;
405 priv->max_reorder = DEFAULT_MAX_REORDER;
406 priv->auto_hdr_ext = DEFAULT_AUTO_HEADER_EXTENSION;
408 gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
411 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
415 gst_rtp_base_depayload_finalize (GObject * object)
417 GstRTPBaseDepayload *rtpbasedepayload = GST_RTP_BASE_DEPAYLOAD (object);
419 g_ptr_array_unref (rtpbasedepayload->priv->header_exts);
420 rtpbasedepayload->priv->header_exts = NULL;
422 G_OBJECT_CLASS (parent_class)->finalize (object);
426 add_and_ref_item (GstRTPHeaderExtension * ext, GPtrArray * ret)
428 g_ptr_array_add (ret, gst_object_ref (ext));
432 remove_item_from (GstRTPHeaderExtension * ext, GPtrArray * ret)
434 g_ptr_array_remove_fast (ret, ext);
438 add_item_to (GstRTPHeaderExtension * ext, GPtrArray * ret)
440 g_ptr_array_add (ret, ext);
444 gst_rtp_base_depayload_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps)
446 GstRTPBaseDepayloadClass *bclass;
447 GstRTPBaseDepayloadPrivate *priv;
449 GstStructure *caps_struct;
454 bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
456 GST_DEBUG_OBJECT (filter, "Set caps %" GST_PTR_FORMAT, caps);
458 if (priv->last_caps) {
459 if (gst_caps_is_equal (priv->last_caps, caps)) {
461 goto caps_not_changed;
463 gst_caps_unref (priv->last_caps);
464 priv->last_caps = NULL;
468 caps_struct = gst_caps_get_structure (caps, 0);
470 value = gst_structure_get_value (caps_struct, "onvif-mode");
471 if (value && G_VALUE_HOLDS_BOOLEAN (value))
472 priv->onvif_mode = g_value_get_boolean (value);
474 priv->onvif_mode = FALSE;
475 GST_DEBUG_OBJECT (filter, "Onvif mode: %d", priv->onvif_mode);
477 if (priv->onvif_mode)
478 filter->need_newsegment = FALSE;
480 /* get other values for newsegment */
481 value = gst_structure_get_value (caps_struct, "npt-start");
482 if (value && G_VALUE_HOLDS_UINT64 (value))
483 priv->npt_start = g_value_get_uint64 (value);
486 GST_DEBUG_OBJECT (filter, "NPT start %" G_GUINT64_FORMAT, priv->npt_start);
488 value = gst_structure_get_value (caps_struct, "npt-stop");
489 if (value && G_VALUE_HOLDS_UINT64 (value))
490 priv->npt_stop = g_value_get_uint64 (value);
494 GST_DEBUG_OBJECT (filter, "NPT stop %" G_GUINT64_FORMAT, priv->npt_stop);
496 value = gst_structure_get_value (caps_struct, "play-speed");
497 if (value && G_VALUE_HOLDS_DOUBLE (value))
498 priv->play_speed = g_value_get_double (value);
500 priv->play_speed = 1.0;
502 value = gst_structure_get_value (caps_struct, "play-scale");
503 if (value && G_VALUE_HOLDS_DOUBLE (value))
504 priv->play_scale = g_value_get_double (value);
506 priv->play_scale = 1.0;
508 value = gst_structure_get_value (caps_struct, "clock-base");
509 if (value && G_VALUE_HOLDS_UINT (value))
510 priv->clock_base = g_value_get_uint (value);
512 priv->clock_base = -1;
515 /* ensure we have header extension implementations for the list in the
517 guint i, j, n_fields = gst_structure_n_fields (caps_struct);
518 GPtrArray *header_exts = g_ptr_array_new_with_free_func (gst_object_unref);
519 GPtrArray *to_add = g_ptr_array_new ();
520 GPtrArray *to_remove = g_ptr_array_new ();
522 GST_OBJECT_LOCK (filter);
523 g_ptr_array_foreach (filter->priv->header_exts,
524 (GFunc) add_and_ref_item, header_exts);
525 GST_OBJECT_UNLOCK (filter);
527 for (i = 0; i < n_fields; i++) {
528 const gchar *field_name = gst_structure_nth_field_name (caps_struct, i);
529 if (g_str_has_prefix (field_name, "extmap-")) {
531 const gchar *uri = NULL;
534 GstRTPHeaderExtension *ext = NULL;
537 ext_id = g_ascii_strtoull (&field_name[strlen ("extmap-")], &nptr, 10);
538 if (errno != 0 || (ext_id == 0 && field_name == nptr)) {
539 GST_WARNING_OBJECT (filter, "could not parse id from %s", field_name);
544 val = gst_structure_get_value (caps_struct, field_name);
545 if (G_VALUE_HOLDS_STRING (val)) {
546 uri = g_value_get_string (val);
547 } else if (GST_VALUE_HOLDS_ARRAY (val)) {
548 /* the uri is the second value in the array */
549 const GValue *str = gst_value_array_get_value (val, 1);
550 if (G_VALUE_HOLDS_STRING (str)) {
551 uri = g_value_get_string (str);
556 GST_WARNING_OBJECT (filter, "could not get extmap uri for "
557 "field %s", field_name);
562 /* try to find if this extension mapping already exists */
563 for (j = 0; j < header_exts->len; j++) {
564 ext = g_ptr_array_index (header_exts, j);
565 if (gst_rtp_header_extension_get_id (ext) == ext_id) {
566 if (g_strcmp0 (uri, gst_rtp_header_extension_get_uri (ext)) == 0) {
567 /* still matching, we're good, set attributes from caps in case
568 * the caps have changed */
569 if (!gst_rtp_header_extension_set_attributes_from_caps (ext,
571 GST_WARNING_OBJECT (filter,
572 "Failed to configure rtp header " "extension %"
573 GST_PTR_FORMAT " attributes from caps %" GST_PTR_FORMAT,
580 GST_DEBUG_OBJECT (filter, "extension id %u"
581 "was replaced with a different extension uri "
582 "original:\'%s' vs \'%s\'", ext_id,
583 gst_rtp_header_extension_get_uri (ext), uri);
584 g_ptr_array_add (to_remove, ext);
593 /* if no extension, attempt to request one */
595 GST_DEBUG_OBJECT (filter, "requesting extension for id %u"
596 " and uri %s", ext_id, uri);
597 g_signal_emit (filter,
598 gst_rtp_base_depayload_signals[SIGNAL_REQUEST_EXTENSION], 0,
600 GST_DEBUG_OBJECT (filter, "request returned extension %p \'%s\' "
601 "for id %u and uri %s", ext,
602 ext ? GST_OBJECT_NAME (ext) : "", ext_id, uri);
604 /* We require the caller to set the appropriate extension if it's required */
605 if (ext && gst_rtp_header_extension_get_id (ext) != ext_id) {
606 g_warning ("\'request-extension\' signal provided an rtp header "
607 "extension for uri \'%s\' that does not match the requested "
608 "extension id %u", uri, ext_id);
609 gst_clear_object (&ext);
612 if (ext && !gst_rtp_header_extension_set_attributes_from_caps (ext,
614 GST_WARNING_OBJECT (filter,
615 "Failed to configure rtp header " "extension %"
616 GST_PTR_FORMAT " attributes from caps %" GST_PTR_FORMAT,
619 g_clear_object (&ext);
624 g_ptr_array_add (to_add, ext);
629 /* Note: we intentionally don't remove extensions that are not listed
632 GST_OBJECT_LOCK (filter);
633 g_ptr_array_foreach (to_remove, (GFunc) remove_item_from,
634 filter->priv->header_exts);
635 g_ptr_array_foreach (to_add, (GFunc) add_item_to,
636 filter->priv->header_exts);
637 GST_OBJECT_UNLOCK (filter);
640 g_ptr_array_unref (to_add);
641 g_ptr_array_unref (to_remove);
642 g_ptr_array_unref (header_exts);
648 if (bclass->set_caps) {
649 res = bclass->set_caps (filter, caps);
651 GST_WARNING_OBJECT (filter, "Subclass rejected caps %" GST_PTR_FORMAT,
658 priv->negotiated = res;
660 if (priv->negotiated)
661 priv->last_caps = gst_caps_ref (caps);
667 GST_DEBUG_OBJECT (filter, "Caps did not change");
672 /* takes ownership of the input buffer */
674 gst_rtp_base_depayload_handle_buffer (GstRTPBaseDepayload * filter,
675 GstRTPBaseDepayloadClass * bclass, GstBuffer * in)
677 GstBuffer *(*process_rtp_packet_func) (GstRTPBaseDepayload * base,
678 GstRTPBuffer * rtp_buffer);
679 GstBuffer *(*process_func) (GstRTPBaseDepayload * base, GstBuffer * in);
680 GstRTPBaseDepayloadPrivate *priv;
685 gboolean discont, buf_discont;
687 GstRTPBuffer rtp = { NULL };
688 GstReferenceTimestampMeta *meta;
692 priv->process_flow_ret = GST_FLOW_OK;
694 process_func = bclass->process;
695 process_rtp_packet_func = bclass->process_rtp_packet;
697 /* we must have a setcaps first */
698 if (G_UNLIKELY (!priv->negotiated))
701 /* Check for duplicate reference timestamp metadata */
702 ref_caps = gst_static_caps_get (&ntp_reference_timestamp_caps);
703 meta = gst_buffer_get_reference_timestamp_meta (in, ref_caps);
704 gst_caps_unref (ref_caps);
706 guint64 ref_ts = meta->timestamp;
707 if (ref_ts == priv->ref_ts) {
708 /* Drop the redundant/duplicate reference timstamp metadata */
709 in = gst_buffer_make_writable (in);
710 gst_buffer_remove_meta (in, GST_META_CAST (meta));
712 priv->ref_ts = ref_ts;
716 if (G_UNLIKELY (!gst_rtp_buffer_map (in, GST_MAP_READ, &rtp)))
719 buf_discont = GST_BUFFER_IS_DISCONT (in);
721 priv->pts = GST_BUFFER_PTS (in);
722 priv->dts = GST_BUFFER_DTS (in);
723 priv->duration = GST_BUFFER_DURATION (in);
725 ssrc = gst_rtp_buffer_get_ssrc (&rtp);
726 seqnum = gst_rtp_buffer_get_seq (&rtp);
727 rtptime = gst_rtp_buffer_get_timestamp (&rtp);
729 priv->last_seqnum = seqnum;
730 priv->last_rtptime = rtptime;
732 discont = buf_discont;
734 GST_LOG_OBJECT (filter, "discont %d, seqnum %u, rtptime %u, pts %"
735 GST_TIME_FORMAT ", dts %" GST_TIME_FORMAT, buf_discont, seqnum, rtptime,
736 GST_TIME_ARGS (priv->pts), GST_TIME_ARGS (priv->dts));
738 /* Check seqnum. This is a very simple check that makes sure that the seqnums
739 * are strictly increasing, dropping anything that is out of the ordinary. We
740 * can only do this when the next_seqnum is known. */
741 if (G_LIKELY (priv->next_seqnum != -1)) {
742 if (ssrc != priv->last_ssrc) {
743 GST_LOG_OBJECT (filter,
744 "New ssrc %u (current ssrc %u), sender restarted",
745 ssrc, priv->last_ssrc);
748 gap = gst_rtp_buffer_compare_seqnum (seqnum, priv->next_seqnum);
750 /* if we have no gap, all is fine */
751 if (G_UNLIKELY (gap != 0)) {
752 GST_LOG_OBJECT (filter, "got packet %u, expected %u, gap %d", seqnum,
753 priv->next_seqnum, gap);
755 /* seqnum > next_seqnum, we are missing some packets, this is always a
757 GST_LOG_OBJECT (filter, "%d missing packets", gap);
760 /* seqnum < next_seqnum, we have seen this packet before, have a
761 * reordered packet or the sender could be restarted. If the packet
762 * is not too old, we throw it away as a duplicate. Otherwise we
763 * mark discont and continue assuming the sender has restarted. See
765 if (gap <= priv->max_reorder) {
766 GST_WARNING_OBJECT (filter, "got old packet %u, expected %u, "
767 "gap %d <= max_reorder (%d), dropping!",
768 seqnum, priv->next_seqnum, gap, priv->max_reorder);
771 GST_WARNING_OBJECT (filter, "got old packet %u, expected %u, "
772 "marking discont", seqnum, priv->next_seqnum);
778 priv->next_seqnum = (seqnum + 1) & 0xffff;
779 priv->last_ssrc = ssrc;
781 if (G_UNLIKELY (discont)) {
782 priv->discont = TRUE;
784 gpointer old_inbuf = in;
786 /* we detected a seqnum discont but the buffer was not flagged with a discont,
787 * set the discont flag so that the subclass can throw away old data. */
788 GST_LOG_OBJECT (filter, "mark DISCONT on input buffer");
789 in = gst_buffer_make_writable (in);
790 GST_BUFFER_FLAG_SET (in, GST_BUFFER_FLAG_DISCONT);
791 /* depayloaders will check flag on rtpbuffer->buffer, so if the input
792 * buffer was not writable already we need to remap to make our
793 * newly-flagged buffer current on the rtpbuffer */
794 if (in != old_inbuf) {
795 gst_rtp_buffer_unmap (&rtp);
796 if (G_UNLIKELY (!gst_rtp_buffer_map (in, GST_MAP_READ, &rtp)))
802 /* prepare segment event if needed */
803 if (filter->need_newsegment) {
804 priv->segment_event = create_segment_event (filter, rtptime,
805 GST_BUFFER_PTS (in));
806 filter->need_newsegment = FALSE;
809 priv->input_buffer = in;
811 if (process_rtp_packet_func != NULL) {
812 out_buf = process_rtp_packet_func (filter, &rtp);
813 gst_rtp_buffer_unmap (&rtp);
814 } else if (process_func != NULL) {
815 gst_rtp_buffer_unmap (&rtp);
816 out_buf = process_func (filter, in);
821 /* let's send it out to processing */
823 if (priv->process_flow_ret == GST_FLOW_OK)
824 priv->process_flow_ret = gst_rtp_base_depayload_push (filter, out_buf);
826 gst_buffer_unref (out_buf);
829 gst_buffer_unref (in);
830 priv->input_buffer = NULL;
832 return priv->process_flow_ret;
837 /* this is not fatal but should be filtered earlier */
838 GST_ELEMENT_ERROR (filter, CORE, NEGOTIATION,
839 ("No RTP format was negotiated."),
840 ("Input buffers need to have RTP caps set on them. This is usually "
841 "achieved by setting the 'caps' property of the upstream source "
842 "element (often udpsrc or appsrc), or by putting a capsfilter "
843 "element before the depayloader and setting the 'caps' property "
844 "on that. Also see http://cgit.freedesktop.org/gstreamer/"
845 "gst-plugins-good/tree/gst/rtp/README"));
846 gst_buffer_unref (in);
847 return GST_FLOW_NOT_NEGOTIATED;
851 /* this is not fatal but should be filtered earlier */
852 GST_ELEMENT_WARNING (filter, STREAM, DECODE, (NULL),
853 ("Received invalid RTP payload, dropping"));
854 gst_buffer_unref (in);
859 gst_rtp_buffer_unmap (&rtp);
860 gst_buffer_unref (in);
865 gst_rtp_buffer_unmap (&rtp);
866 /* this is not fatal but should be filtered earlier */
867 GST_ELEMENT_ERROR (filter, STREAM, NOT_IMPLEMENTED, (NULL),
868 ("The subclass does not have a process or process_rtp_packet method"));
869 gst_buffer_unref (in);
870 return GST_FLOW_ERROR;
875 gst_rtp_base_depayload_chain (GstPad * pad, GstObject * parent, GstBuffer * in)
877 GstRTPBaseDepayloadClass *bclass;
878 GstRTPBaseDepayload *basedepay;
879 GstFlowReturn flow_ret;
881 basedepay = GST_RTP_BASE_DEPAYLOAD_CAST (parent);
883 bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (basedepay);
885 flow_ret = gst_rtp_base_depayload_handle_buffer (basedepay, bclass, in);
891 gst_rtp_base_depayload_chain_list (GstPad * pad, GstObject * parent,
892 GstBufferList * list)
894 GstRTPBaseDepayloadClass *bclass;
895 GstRTPBaseDepayload *basedepay;
896 GstFlowReturn flow_ret;
900 basedepay = GST_RTP_BASE_DEPAYLOAD_CAST (parent);
902 bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (basedepay);
904 flow_ret = GST_FLOW_OK;
906 /* chain each buffer in list individually */
907 len = gst_buffer_list_length (list);
912 for (i = 0; i < len; i++) {
913 buffer = gst_buffer_list_get (list, i);
915 /* handle_buffer takes ownership of input buffer */
916 /* FIXME: add a way to steal buffers from list as we will unref it anyway */
917 gst_buffer_ref (buffer);
919 /* Should we fix up any missing timestamps for list buffers here
920 * (e.g. set to first or previous timestamp in list) or just assume
921 * the's a jitterbuffer that will have done that for us? */
922 flow_ret = gst_rtp_base_depayload_handle_buffer (basedepay, bclass, buffer);
923 if (flow_ret != GST_FLOW_OK)
929 gst_buffer_list_unref (list);
935 gst_rtp_base_depayload_handle_event (GstRTPBaseDepayload * filter,
939 gboolean forward = TRUE;
941 switch (GST_EVENT_TYPE (event)) {
942 case GST_EVENT_FLUSH_STOP:
943 GST_OBJECT_LOCK (filter);
944 gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
945 GST_OBJECT_UNLOCK (filter);
947 filter->need_newsegment = !filter->priv->onvif_mode;
948 filter->priv->next_seqnum = -1;
949 filter->priv->ref_ts = -1;
950 gst_event_replace (&filter->priv->segment_event, NULL);
956 gst_event_parse_caps (event, &caps);
958 res = gst_rtp_base_depayload_setcaps (filter, caps);
962 case GST_EVENT_SEGMENT:
966 GST_OBJECT_LOCK (filter);
967 gst_event_copy_segment (event, &segment);
969 if (segment.format != GST_FORMAT_TIME) {
970 GST_ERROR_OBJECT (filter, "Segment with non-TIME format not supported");
973 filter->priv->segment_seqnum = gst_event_get_seqnum (event);
974 filter->segment = segment;
975 GST_OBJECT_UNLOCK (filter);
977 /* In ONVIF mode, upstream is expected to send us the correct segment */
978 if (!filter->priv->onvif_mode) {
979 /* don't pass the event downstream, we generate our own segment including
980 * the NTP time and other things we receive in caps */
985 case GST_EVENT_CUSTOM_DOWNSTREAM:
987 GstRTPBaseDepayloadClass *bclass;
989 bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
991 if (gst_event_has_name (event, "GstRTPPacketLost")) {
992 /* we get this event from the jitterbuffer when it considers a packet as
993 * being lost. We send it to our packet_lost vmethod. The default
994 * implementation will make time progress by pushing out a GAP event.
995 * Subclasses can override and do one of the following:
996 * - Adjust timestamp/duration to something more accurate before
997 * calling the parent (default) packet_lost method.
998 * - do some more advanced error concealing on the already received
999 * (fragmented) packets.
1000 * - ignore the packet lost.
1002 if (bclass->packet_lost)
1003 res = bclass->packet_lost (filter, event);
1013 res = gst_pad_push_event (filter->srcpad, event);
1015 gst_event_unref (event);
1021 gst_rtp_base_depayload_handle_sink_event (GstPad * pad, GstObject * parent,
1024 gboolean res = FALSE;
1025 GstRTPBaseDepayload *filter;
1026 GstRTPBaseDepayloadClass *bclass;
1028 filter = GST_RTP_BASE_DEPAYLOAD (parent);
1029 bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
1030 if (bclass->handle_event)
1031 res = bclass->handle_event (filter, event);
1033 gst_event_unref (event);
1039 create_segment_event (GstRTPBaseDepayload * filter, guint rtptime,
1040 GstClockTime position)
1043 GstClockTime start, stop, running_time;
1044 GstRTPBaseDepayloadPrivate *priv;
1047 priv = filter->priv;
1049 /* We don't need the object lock around - the segment
1050 * can't change here while we're holding the STREAM_LOCK
1053 /* determining the start of the segment */
1054 start = filter->segment.start;
1055 if (priv->clock_base != -1 && position != -1) {
1056 GstClockTime exttime, gap;
1058 exttime = priv->clock_base;
1059 gst_rtp_buffer_ext_timestamp (&exttime, rtptime);
1060 gap = gst_util_uint64_scale_int (exttime - priv->clock_base,
1061 filter->clock_rate, GST_SECOND);
1063 /* account for lost packets */
1064 if (position > gap) {
1065 GST_DEBUG_OBJECT (filter,
1066 "Found gap of %" GST_TIME_FORMAT ", adjusting start: %"
1067 GST_TIME_FORMAT " = %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
1068 GST_TIME_ARGS (gap), GST_TIME_ARGS (position - gap),
1069 GST_TIME_ARGS (position), GST_TIME_ARGS (gap));
1070 start = position - gap;
1074 /* determining the stop of the segment */
1075 stop = filter->segment.stop;
1076 if (priv->npt_stop != -1)
1077 stop = start + (priv->npt_stop - priv->npt_start);
1082 running_time = gst_segment_to_running_time (&filter->segment,
1083 GST_FORMAT_TIME, start);
1085 gst_segment_init (&segment, GST_FORMAT_TIME);
1086 segment.rate = priv->play_speed;
1087 segment.applied_rate = priv->play_scale;
1088 segment.start = start;
1089 segment.stop = stop;
1090 segment.time = priv->npt_start;
1091 segment.position = position;
1092 segment.base = running_time;
1094 GST_DEBUG_OBJECT (filter, "Creating segment event %" GST_SEGMENT_FORMAT,
1096 event = gst_event_new_segment (&segment);
1097 if (filter->priv->segment_seqnum != GST_SEQNUM_INVALID)
1098 gst_event_set_seqnum (event, filter->priv->segment_seqnum);
1104 foreach_metadata_drop (GstBuffer * buffer, GstMeta ** meta, gpointer user_data)
1106 GType drop_api_type = (GType) user_data;
1107 const GstMetaInfo *info = (*meta)->info;
1109 if (info->api == drop_api_type)
1116 add_rtp_source_meta (GstBuffer * outbuf, GstBuffer * rtpbuf)
1118 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
1119 GstRTPSourceMeta *meta;
1121 GType source_meta_api = gst_rtp_source_meta_api_get_type ();
1123 if (!gst_rtp_buffer_map (rtpbuf, GST_MAP_READ, &rtp))
1126 ssrc = gst_rtp_buffer_get_ssrc (&rtp);
1128 /* remove any pre-existing source-meta */
1129 gst_buffer_foreach_meta (outbuf, foreach_metadata_drop,
1130 (gpointer) source_meta_api);
1132 meta = gst_buffer_add_rtp_source_meta (outbuf, &ssrc, NULL, 0);
1135 gint csrc_count = gst_rtp_buffer_get_csrc_count (&rtp);
1136 for (i = 0; i < csrc_count; i++) {
1137 guint32 csrc = gst_rtp_buffer_get_csrc (&rtp, i);
1138 gst_rtp_source_meta_append_csrc (meta, &csrc, 1);
1142 gst_rtp_buffer_unmap (&rtp);
1146 gst_rtp_base_depayload_add_extension (GstRTPBaseDepayload * rtpbasepayload,
1147 GstRTPHeaderExtension * ext)
1149 g_return_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext));
1150 g_return_if_fail (gst_rtp_header_extension_get_id (ext) > 0);
1152 /* XXX: check for duplicate ids? */
1153 GST_OBJECT_LOCK (rtpbasepayload);
1154 g_ptr_array_add (rtpbasepayload->priv->header_exts, gst_object_ref (ext));
1155 GST_OBJECT_UNLOCK (rtpbasepayload);
1159 gst_rtp_base_depayload_clear_extensions (GstRTPBaseDepayload * rtpbasepayload)
1161 GST_OBJECT_LOCK (rtpbasepayload);
1162 g_ptr_array_set_size (rtpbasepayload->priv->header_exts, 0);
1163 GST_OBJECT_UNLOCK (rtpbasepayload);
1167 read_rtp_header_extensions (GstRTPBaseDepayload * depayload,
1168 GstBuffer * input, GstBuffer * output)
1170 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
1171 guint16 bit_pattern;
1174 gboolean needs_src_caps_update = FALSE;
1177 GST_DEBUG_OBJECT (depayload, "no input buffer");
1178 return needs_src_caps_update;
1181 if (!gst_rtp_buffer_map (input, GST_MAP_READ, &rtp)) {
1182 GST_WARNING_OBJECT (depayload, "Failed to map buffer");
1183 return needs_src_caps_update;
1186 if (gst_rtp_buffer_get_extension_data (&rtp, &bit_pattern, (gpointer) & pdata,
1188 GstRTPHeaderExtensionFlags ext_flags = 0;
1189 gsize bytelen = wordlen * 4;
1190 guint hdr_unit_bytes;
1193 if (bit_pattern == 0xBEDE) {
1194 /* one byte extensions */
1196 ext_flags |= GST_RTP_HEADER_EXTENSION_ONE_BYTE;
1197 } else if (bit_pattern >> 4 == 0x100) {
1198 /* two byte extensions */
1200 ext_flags |= GST_RTP_HEADER_EXTENSION_TWO_BYTE;
1202 GST_DEBUG_OBJECT (depayload, "unknown extension bit pattern 0x%02x%02x",
1203 bit_pattern >> 8, bit_pattern & 0xff);
1208 guint8 read_id, read_len;
1209 GstRTPHeaderExtension *ext = NULL;
1212 if (offset + hdr_unit_bytes >= bytelen)
1213 /* not enough remaning data */
1216 if (ext_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE) {
1217 read_id = GST_READ_UINT8 (pdata + offset) >> 4;
1218 read_len = (GST_READ_UINT8 (pdata + offset) & 0x0F) + 1;
1226 /* special id for possible future expansion */
1229 read_id = GST_READ_UINT8 (pdata + offset);
1236 read_len = GST_READ_UINT8 (pdata + offset);
1239 GST_TRACE_OBJECT (depayload, "found rtp header extension with id %u and "
1240 "length %u", read_id, read_len);
1242 /* Ignore extension headers where the size does not fit */
1243 if (offset + read_len > bytelen) {
1244 GST_WARNING_OBJECT (depayload, "Extension length extends past the "
1245 "size of the extension data");
1249 GST_OBJECT_LOCK (depayload);
1250 for (i = 0; i < depayload->priv->header_exts->len; i++) {
1251 ext = g_ptr_array_index (depayload->priv->header_exts, i);
1252 if (read_id == gst_rtp_header_extension_get_id (ext)) {
1253 gst_object_ref (ext);
1260 if (!gst_rtp_header_extension_read (ext, ext_flags, &pdata[offset],
1261 read_len, output)) {
1262 GST_WARNING_OBJECT (depayload, "RTP header extension (%s) could "
1263 "not read payloaded data", GST_OBJECT_NAME (ext));
1264 #ifdef TIZEN_FEATURE_BUG_FIX
1265 GST_OBJECT_UNLOCK (depayload);
1267 gst_object_unref (ext);
1271 if (gst_rtp_header_extension_wants_update_non_rtp_src_caps (ext)) {
1272 needs_src_caps_update = TRUE;
1275 gst_object_unref (ext);
1277 GST_OBJECT_UNLOCK (depayload);
1284 gst_rtp_buffer_unmap (&rtp);
1286 return needs_src_caps_update;
1290 gst_rtp_base_depayload_set_headers (GstRTPBaseDepayload * depayload,
1293 GstRTPBaseDepayloadPrivate *priv = depayload->priv;
1294 GstClockTime pts, dts, duration;
1296 pts = GST_BUFFER_PTS (buffer);
1297 dts = GST_BUFFER_DTS (buffer);
1298 duration = GST_BUFFER_DURATION (buffer);
1300 /* apply last incoming timestamp and duration to outgoing buffer if
1301 * not otherwise set. */
1302 if (!GST_CLOCK_TIME_IS_VALID (pts))
1303 GST_BUFFER_PTS (buffer) = priv->pts;
1304 if (!GST_CLOCK_TIME_IS_VALID (dts))
1305 GST_BUFFER_DTS (buffer) = priv->dts;
1306 if (!GST_CLOCK_TIME_IS_VALID (duration))
1307 GST_BUFFER_DURATION (buffer) = priv->duration;
1309 if (G_UNLIKELY (depayload->priv->discont)) {
1310 GST_LOG_OBJECT (depayload, "Marking DISCONT on output buffer");
1311 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
1312 depayload->priv->discont = FALSE;
1315 /* make sure we only set the timestamp on the first packet */
1316 priv->pts = GST_CLOCK_TIME_NONE;
1317 priv->dts = GST_CLOCK_TIME_NONE;
1318 priv->duration = GST_CLOCK_TIME_NONE;
1320 if (priv->input_buffer) {
1321 if (priv->source_info)
1322 add_rtp_source_meta (buffer, priv->input_buffer);
1324 return read_rtp_header_extensions (depayload, priv->input_buffer, buffer);
1330 static GstFlowReturn
1331 gst_rtp_base_depayload_finish_push (GstRTPBaseDepayload * filter,
1332 gboolean is_list, gpointer obj)
1334 /* if this is the first buffer send a NEWSEGMENT */
1335 if (G_UNLIKELY (filter->priv->segment_event)) {
1336 gst_pad_push_event (filter->srcpad, filter->priv->segment_event);
1337 filter->priv->segment_event = NULL;
1338 GST_DEBUG_OBJECT (filter, "Pushed newsegment event on this first buffer");
1342 GstBufferList *blist = obj;
1343 return gst_pad_push_list (filter->srcpad, blist);
1345 GstBuffer *buf = obj;
1346 return gst_pad_push (filter->srcpad, buf);
1351 gst_rtp_base_depayload_set_src_caps_from_hdrext (GstRTPBaseDepayload * filter)
1353 gboolean update_ok = TRUE;
1354 GstCaps *src_caps = gst_pad_get_current_caps (filter->srcpad);
1360 new_caps = gst_caps_copy (src_caps);
1361 for (i = 0; i < filter->priv->header_exts->len; i++) {
1362 GstRTPHeaderExtension *ext;
1364 ext = g_ptr_array_index (filter->priv->header_exts, i);
1366 gst_rtp_header_extension_update_non_rtp_src_caps (ext, new_caps);
1369 GST_ELEMENT_ERROR (filter, STREAM, DECODE,
1370 ("RTP header extension (%s) could not update src caps",
1371 GST_OBJECT_NAME (ext)), (NULL));
1376 if (G_UNLIKELY (update_ok && !gst_caps_is_equal (src_caps, new_caps))) {
1377 gst_pad_set_caps (filter->srcpad, new_caps);
1380 gst_caps_unref (src_caps);
1381 gst_caps_unref (new_caps);
1387 static GstFlowReturn
1388 gst_rtp_base_depayload_do_push (GstRTPBaseDepayload * filter, gboolean is_list,
1394 GstBufferList *blist = obj;
1396 guint first_not_pushed_idx = 0;
1398 for (i = 0; i < gst_buffer_list_length (blist); ++i) {
1399 GstBuffer *buf = gst_buffer_list_get_writable (blist, i);
1401 if (G_UNLIKELY (gst_rtp_base_depayload_set_headers (filter, buf))) {
1402 /* src caps have changed; push the buffers preceding the current one,
1403 * then apply the new caps on the src pad */
1406 for (j = first_not_pushed_idx; j < i; ++j) {
1407 res = gst_rtp_base_depayload_finish_push (filter, FALSE,
1408 gst_buffer_ref (gst_buffer_list_get (blist, j)));
1409 if (G_UNLIKELY (res != GST_FLOW_OK)) {
1413 first_not_pushed_idx = i;
1415 if (!gst_rtp_base_depayload_set_src_caps_from_hdrext (filter)) {
1416 res = GST_FLOW_ERROR;
1422 if (G_LIKELY (first_not_pushed_idx == 0)) {
1423 res = gst_rtp_base_depayload_finish_push (filter, TRUE, blist);
1426 for (i = first_not_pushed_idx; i < gst_buffer_list_length (blist); ++i) {
1427 res = gst_rtp_base_depayload_finish_push (filter, FALSE,
1428 gst_buffer_ref (gst_buffer_list_get (blist, i)));
1429 if (G_UNLIKELY (res != GST_FLOW_OK)) {
1436 gst_clear_buffer_list (&blist);
1438 GstBuffer *buf = obj;
1439 if (G_UNLIKELY (gst_rtp_base_depayload_set_headers (filter, buf))) {
1440 if (!gst_rtp_base_depayload_set_src_caps_from_hdrext (filter)) {
1441 res = GST_FLOW_ERROR;
1446 res = gst_rtp_base_depayload_finish_push (filter, FALSE, buf);
1450 gst_clear_buffer (&buf);
1457 * gst_rtp_base_depayload_push:
1458 * @filter: a #GstRTPBaseDepayload
1459 * @out_buf: (transfer full): a #GstBuffer
1461 * Push @out_buf to the peer of @filter. This function takes ownership of
1464 * This function will by default apply the last incoming timestamp on
1465 * the outgoing buffer when it didn't have a timestamp already.
1467 * Returns: a #GstFlowReturn.
1470 gst_rtp_base_depayload_push (GstRTPBaseDepayload * filter, GstBuffer * out_buf)
1474 res = gst_rtp_base_depayload_do_push (filter, FALSE, out_buf);
1476 if (res != GST_FLOW_OK)
1477 filter->priv->process_flow_ret = res;
1483 * gst_rtp_base_depayload_push_list:
1484 * @filter: a #GstRTPBaseDepayload
1485 * @out_list: (transfer full): a #GstBufferList
1487 * Push @out_list to the peer of @filter. This function takes ownership of
1490 * Returns: a #GstFlowReturn.
1493 gst_rtp_base_depayload_push_list (GstRTPBaseDepayload * filter,
1494 GstBufferList * out_list)
1498 res = gst_rtp_base_depayload_do_push (filter, TRUE, out_list);
1500 if (res != GST_FLOW_OK)
1501 filter->priv->process_flow_ret = res;
1506 /* convert the PacketLost event from a jitterbuffer to a GAP event.
1507 * subclasses can override this. */
1509 gst_rtp_base_depayload_packet_lost (GstRTPBaseDepayload * filter,
1512 GstClockTime timestamp, duration;
1514 const GstStructure *s;
1515 gboolean might_have_been_fec;
1516 gboolean res = TRUE;
1518 s = gst_event_get_structure (event);
1520 /* first start by parsing the timestamp and duration */
1524 if (!gst_structure_get_clock_time (s, "timestamp", ×tamp) ||
1525 !gst_structure_get_clock_time (s, "duration", &duration)) {
1526 GST_ERROR_OBJECT (filter,
1527 "Packet loss event without timestamp or duration");
1531 sevent = gst_pad_get_sticky_event (filter->srcpad, GST_EVENT_SEGMENT, 0);
1532 if (G_UNLIKELY (!sevent)) {
1533 /* Typically happens if lost event arrives before first buffer */
1534 GST_DEBUG_OBJECT (filter,
1535 "Ignore packet loss because segment event missing");
1538 gst_event_unref (sevent);
1540 if (!gst_structure_get_boolean (s, "might-have-been-fec",
1541 &might_have_been_fec) || !might_have_been_fec) {
1542 /* send GAP event */
1543 sevent = gst_event_new_gap (timestamp, duration);
1544 gst_event_set_gap_flags (sevent, GST_GAP_FLAG_MISSING_DATA);
1545 res = gst_pad_push_event (filter->srcpad, sevent);
1551 static GstStateChangeReturn
1552 gst_rtp_base_depayload_change_state (GstElement * element,
1553 GstStateChange transition)
1555 GstRTPBaseDepayload *filter;
1556 GstRTPBaseDepayloadPrivate *priv;
1557 GstStateChangeReturn ret;
1559 filter = GST_RTP_BASE_DEPAYLOAD (element);
1560 priv = filter->priv;
1562 switch (transition) {
1563 case GST_STATE_CHANGE_NULL_TO_READY:
1565 case GST_STATE_CHANGE_READY_TO_PAUSED:
1566 filter->need_newsegment = TRUE;
1567 priv->npt_start = 0;
1568 priv->npt_stop = -1;
1569 priv->play_speed = 1.0;
1570 priv->play_scale = 1.0;
1571 priv->clock_base = -1;
1573 priv->onvif_mode = FALSE;
1574 priv->next_seqnum = -1;
1575 priv->negotiated = FALSE;
1576 priv->discont = FALSE;
1577 priv->segment_seqnum = GST_SEQNUM_INVALID;
1579 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1585 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1587 switch (transition) {
1588 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1590 case GST_STATE_CHANGE_PAUSED_TO_READY:
1591 gst_caps_replace (&priv->last_caps, NULL);
1592 gst_event_replace (&priv->segment_event, NULL);
1594 case GST_STATE_CHANGE_READY_TO_NULL:
1602 static GstStructure *
1603 gst_rtp_base_depayload_create_stats (GstRTPBaseDepayload * depayload)
1605 GstRTPBaseDepayloadPrivate *priv;
1607 GstClockTime pts = GST_CLOCK_TIME_NONE, dts = GST_CLOCK_TIME_NONE;
1609 priv = depayload->priv;
1611 GST_OBJECT_LOCK (depayload);
1612 if (depayload->segment.format != GST_FORMAT_UNDEFINED) {
1613 pts = gst_segment_to_running_time (&depayload->segment, GST_FORMAT_TIME,
1615 dts = gst_segment_to_running_time (&depayload->segment, GST_FORMAT_TIME,
1618 GST_OBJECT_UNLOCK (depayload);
1620 s = gst_structure_new ("application/x-rtp-depayload-stats",
1621 "clock_rate", G_TYPE_UINT, depayload->clock_rate,
1622 "npt-start", G_TYPE_UINT64, priv->npt_start,
1623 "npt-stop", G_TYPE_UINT64, priv->npt_stop,
1624 "play-speed", G_TYPE_DOUBLE, priv->play_speed,
1625 "play-scale", G_TYPE_DOUBLE, priv->play_scale,
1626 "running-time-dts", G_TYPE_UINT64, dts,
1627 "running-time-pts", G_TYPE_UINT64, pts,
1628 "seqnum", G_TYPE_UINT, (guint) priv->last_seqnum,
1629 "timestamp", G_TYPE_UINT, (guint) priv->last_rtptime, NULL);
1636 gst_rtp_base_depayload_set_property (GObject * object, guint prop_id,
1637 const GValue * value, GParamSpec * pspec)
1639 GstRTPBaseDepayload *depayload;
1640 GstRTPBaseDepayloadPrivate *priv;
1642 depayload = GST_RTP_BASE_DEPAYLOAD (object);
1643 priv = depayload->priv;
1646 case PROP_SOURCE_INFO:
1647 gst_rtp_base_depayload_set_source_info_enabled (depayload,
1648 g_value_get_boolean (value));
1650 case PROP_MAX_REORDER:
1651 priv->max_reorder = g_value_get_int (value);
1653 case PROP_AUTO_HEADER_EXTENSION:
1654 priv->auto_hdr_ext = g_value_get_boolean (value);
1657 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1663 gst_rtp_base_depayload_get_property (GObject * object, guint prop_id,
1664 GValue * value, GParamSpec * pspec)
1666 GstRTPBaseDepayload *depayload;
1667 GstRTPBaseDepayloadPrivate *priv;
1669 depayload = GST_RTP_BASE_DEPAYLOAD (object);
1670 priv = depayload->priv;
1674 g_value_take_boxed (value,
1675 gst_rtp_base_depayload_create_stats (depayload));
1677 case PROP_SOURCE_INFO:
1678 g_value_set_boolean (value,
1679 gst_rtp_base_depayload_is_source_info_enabled (depayload));
1681 case PROP_MAX_REORDER:
1682 g_value_set_int (value, priv->max_reorder);
1684 case PROP_AUTO_HEADER_EXTENSION:
1685 g_value_set_boolean (value, priv->auto_hdr_ext);
1688 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1694 * gst_rtp_base_depayload_set_source_info_enabled:
1695 * @depayload: a #GstRTPBaseDepayload
1696 * @enable: whether to add meta about RTP sources to buffer
1698 * Enable or disable adding #GstRTPSourceMeta to depayloaded buffers.
1703 gst_rtp_base_depayload_set_source_info_enabled (GstRTPBaseDepayload * depayload,
1706 depayload->priv->source_info = enable;
1710 * gst_rtp_base_depayload_is_source_info_enabled:
1711 * @depayload: a #GstRTPBaseDepayload
1713 * Queries whether #GstRTPSourceMeta will be added to depayloaded buffers.
1715 * Returns: %TRUE if source-info is enabled.
1720 gst_rtp_base_depayload_is_source_info_enabled (GstRTPBaseDepayload * depayload)
1722 return depayload->priv->source_info;