2 * gstrtponviftimestamp.h
4 * Copyright (C) 2014 Axis Communications AB
5 * Author: Guillaume Desmottes <guillaume.desmottes@collabora.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
29 #include <gst/rtp/gstrtpbuffer.h>
31 #include "gstrtponviftimestamp.h"
33 #define GST_ONVIF_TIMESTAMP_EVENT_NAME "GstOnvifTimestamp"
35 #define DEFAULT_NTP_OFFSET GST_CLOCK_TIME_NONE
36 #define DEFAULT_CSEQ 0
37 #define DEFAULT_SET_E_BIT FALSE
38 #define DEFAULT_SET_T_BIT FALSE
39 #define DEFAULT_DROP_OUT_OF_SEGMENT TRUE
40 #define DEFAULT_USE_REFERENCE_TIMESTAMPS FALSE
42 GST_DEBUG_CATEGORY_STATIC (rtponviftimestamp_debug);
43 #define GST_CAT_DEFAULT (rtponviftimestamp_debug)
45 static GstFlowReturn gst_rtp_onvif_timestamp_chain (GstPad * pad,
46 GstObject * parent, GstBuffer * buf);
47 static GstFlowReturn gst_rtp_onvif_timestamp_chain_list (GstPad * pad,
48 GstObject * parent, GstBufferList * list);
50 static GstFlowReturn handle_and_push_buffer (GstRtpOnvifTimestamp * self,
52 static GstFlowReturn handle_and_push_buffer_list (GstRtpOnvifTimestamp * self,
53 GstBufferList * list);
55 static GstStaticPadTemplate sink_template_factory =
56 GST_STATIC_PAD_TEMPLATE ("sink",
59 GST_STATIC_CAPS ("application/x-rtp")
62 static GstStaticPadTemplate src_template_factory =
63 GST_STATIC_PAD_TEMPLATE ("src",
66 GST_STATIC_CAPS ("application/x-rtp")
76 PROP_DROP_OUT_OF_SEGMENT,
77 PROP_USE_REFERENCE_TIMESTAMPS
80 /*static guint gst_rtp_onvif_timestamp_signals[LAST_SIGNAL] = { 0 }; */
82 G_DEFINE_TYPE (GstRtpOnvifTimestamp, gst_rtp_onvif_timestamp, GST_TYPE_ELEMENT);
83 GST_ELEMENT_REGISTER_DEFINE (rtponviftimestamp, "rtponviftimestamp",
84 GST_RANK_NONE, GST_TYPE_RTP_ONVIF_TIMESTAMP);
87 gst_rtp_onvif_timestamp_get_property (GObject * object,
88 guint prop_id, GValue * value, GParamSpec * pspec)
90 GstRtpOnvifTimestamp *self = GST_RTP_ONVIF_TIMESTAMP (object);
94 g_value_set_uint64 (value, self->prop_ntp_offset);
97 g_value_set_uint (value, self->prop_cseq);
100 g_value_set_boolean (value, self->prop_set_e_bit);
103 g_value_set_boolean (value, self->prop_set_t_bit);
105 case PROP_DROP_OUT_OF_SEGMENT:
106 g_value_set_boolean (value, self->prop_drop_out_of_segment);
108 case PROP_USE_REFERENCE_TIMESTAMPS:
109 g_value_set_boolean (value, self->prop_use_reference_timestamps);
112 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118 gst_rtp_onvif_timestamp_set_property (GObject * object,
119 guint prop_id, const GValue * value, GParamSpec * pspec)
121 GstRtpOnvifTimestamp *self = GST_RTP_ONVIF_TIMESTAMP (object);
124 case PROP_NTP_OFFSET:
125 self->prop_ntp_offset = g_value_get_uint64 (value);
128 self->prop_cseq = g_value_get_uint (value);
131 self->prop_set_e_bit = g_value_get_boolean (value);
134 self->prop_set_t_bit = g_value_get_boolean (value);
136 case PROP_DROP_OUT_OF_SEGMENT:
137 self->prop_drop_out_of_segment = g_value_get_boolean (value);
139 case PROP_USE_REFERENCE_TIMESTAMPS:
140 self->prop_use_reference_timestamps = g_value_get_boolean (value);
143 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
148 /* send cached buffer or list, and events, if present */
150 send_cached_buffer_and_events (GstRtpOnvifTimestamp * self)
152 GstFlowReturn ret = GST_FLOW_OK;
154 g_assert (!(self->buffer && self->list));
157 GST_DEBUG_OBJECT (self, "pushing %" GST_PTR_FORMAT, self->buffer);
158 ret = handle_and_push_buffer (self, self->buffer);
162 GST_DEBUG_OBJECT (self, "pushing %" GST_PTR_FORMAT, self->list);
163 ret = handle_and_push_buffer_list (self, self->list);
167 if (ret != GST_FLOW_OK)
170 while (!g_queue_is_empty (self->event_queue)) {
173 event = GST_EVENT_CAST (g_queue_pop_head (self->event_queue));
174 GST_LOG_OBJECT (self->sinkpad, "sending %" GST_PTR_FORMAT, event);
175 (void) gst_pad_send_event (self->sinkpad, event);
183 purge_cached_buffer_and_events (GstRtpOnvifTimestamp * self)
185 g_assert (!(self->buffer && self->list));
188 GST_DEBUG_OBJECT (self, "purging %" GST_PTR_FORMAT, self->buffer);
189 gst_buffer_unref (self->buffer);
193 GST_DEBUG_OBJECT (self, "purging %" GST_PTR_FORMAT, self->list);
194 gst_buffer_list_unref (self->list);
198 while (!g_queue_is_empty (self->event_queue)) {
201 event = GST_EVENT_CAST (g_queue_pop_head (self->event_queue));
202 gst_event_unref (event);
206 static GstStateChangeReturn
207 gst_rtp_onvif_timestamp_change_state (GstElement * element,
208 GstStateChange transition)
210 GstRtpOnvifTimestamp *self = GST_RTP_ONVIF_TIMESTAMP (element);
211 GstStateChangeReturn ret;
213 switch (transition) {
214 case GST_STATE_CHANGE_READY_TO_PAUSED:
215 if (self->prop_use_reference_timestamps &&
216 self->prop_ntp_offset != DEFAULT_NTP_OFFSET) {
217 GST_WARNING_OBJECT (self, "ntp-offset should not be set if reference "
218 "timestamps are used");
219 self->ntp_offset = DEFAULT_NTP_OFFSET;
220 } else if (self->prop_use_reference_timestamps) {
221 GST_DEBUG_OBJECT (self, "using reference timestamp meta");
223 self->ntp_offset = self->prop_ntp_offset;
224 GST_DEBUG_OBJECT (self, "ntp-offset: %" GST_TIME_FORMAT,
225 GST_TIME_ARGS (self->ntp_offset));
227 self->set_d_bit = TRUE;
228 self->set_e_bit = FALSE;
229 self->set_t_bit = FALSE;
235 ret = GST_ELEMENT_CLASS (gst_rtp_onvif_timestamp_parent_class)->change_state
236 (element, transition);
238 if (ret == GST_STATE_CHANGE_FAILURE)
241 switch (transition) {
242 case GST_STATE_CHANGE_PAUSED_TO_READY:
243 purge_cached_buffer_and_events (self);
244 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
254 gst_rtp_onvif_timestamp_finalize (GObject * object)
256 GstRtpOnvifTimestamp *self = GST_RTP_ONVIF_TIMESTAMP (object);
258 g_queue_free (self->event_queue);
259 gst_caps_replace (&self->reference_timestamp_id, NULL);
261 G_OBJECT_CLASS (gst_rtp_onvif_timestamp_parent_class)->finalize (object);
265 gst_rtp_onvif_timestamp_class_init (GstRtpOnvifTimestampClass * klass)
267 GObjectClass *gobject_class;
268 GstElementClass *gstelement_class;
270 gobject_class = G_OBJECT_CLASS (klass);
271 gstelement_class = GST_ELEMENT_CLASS (klass);
273 gobject_class->get_property = gst_rtp_onvif_timestamp_get_property;
274 gobject_class->set_property = gst_rtp_onvif_timestamp_set_property;
275 gobject_class->finalize = gst_rtp_onvif_timestamp_finalize;
277 g_object_class_install_property (gobject_class, PROP_NTP_OFFSET,
278 g_param_spec_uint64 ("ntp-offset", "NTP offset",
279 "Offset between the pipeline running time and the absolute UTC time, "
280 "in nano-seconds since 1900 (-1 for automatic computation)",
282 DEFAULT_NTP_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
284 g_object_class_install_property (gobject_class, PROP_CSEQ,
285 g_param_spec_uint ("cseq", "CSeq",
286 "The RTSP CSeq which initiated the playback",
288 DEFAULT_CSEQ, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
290 g_object_class_install_property (gobject_class, PROP_SET_E_BIT,
291 g_param_spec_boolean ("set-e-bit", "Set 'E' bit",
292 "If the element should set the 'E' bit as defined in the ONVIF RTP "
293 "extension. This increases latency by one packet",
294 DEFAULT_SET_E_BIT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
296 g_object_class_install_property (gobject_class, PROP_SET_T_BIT,
297 g_param_spec_boolean ("set-t-bit", "Set 'T' bit",
298 "If the element should set the 'T' bit as defined in the ONVIF RTP "
299 "extension. This increases latency by one packet",
300 DEFAULT_SET_T_BIT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
302 g_object_class_install_property (gobject_class, PROP_DROP_OUT_OF_SEGMENT,
303 g_param_spec_boolean ("drop-out-of-segment", "Drop out of segment",
304 "Whether the element should drop buffers that fall outside the segment, "
305 "not part of the specification but allows full reverse playback.",
306 DEFAULT_DROP_OUT_OF_SEGMENT,
307 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
310 * GstRtpOnvifTimestamp:use-reference-timestamps:
312 * Whether to obtain timestamps from reference timestamp meta instead of using
313 * the ntp-offset method. If enabled then timestamps are expected to be
314 * attached to the buffers, and in that case ntp-offset should not be
317 * Default value is FALSE, meaning that the ntp-offset property is used.
318 * If neither is set then the element calculates an ntp-offset.
322 g_object_class_install_property (gobject_class, PROP_USE_REFERENCE_TIMESTAMPS,
323 g_param_spec_boolean ("use-reference-timestamps",
324 "Use reference timestamps",
325 "Whether the element should use reference UTC timestamps from the "
326 "buffers instead of using the ntp-offset mechanism.",
327 DEFAULT_USE_REFERENCE_TIMESTAMPS,
328 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
329 GST_PARAM_MUTABLE_READY));
332 gst_element_class_add_static_pad_template (gstelement_class,
333 &sink_template_factory);
334 gst_element_class_add_static_pad_template (gstelement_class,
335 &src_template_factory);
337 gst_element_class_set_static_metadata (gstelement_class,
338 "ONVIF NTP timestamps RTP extension", "Effect/RTP",
339 "Add absolute timestamps and flags of recorded data in a playback "
340 "session", "Guillaume Desmottes <guillaume.desmottes@collabora.com>");
342 gstelement_class->change_state =
343 GST_DEBUG_FUNCPTR (gst_rtp_onvif_timestamp_change_state);
345 GST_DEBUG_CATEGORY_INIT (rtponviftimestamp_debug, "rtponviftimestamp",
346 0, "ONVIF NTP timestamps RTP extension");
350 parse_event_ntp_offset (GstRtpOnvifTimestamp * self, GstEvent * event,
351 GstClockTime * offset, gboolean * discont)
353 const GstStructure *structure = gst_event_get_structure (event);
354 GstClockTime event_offset;
355 gboolean event_discont;
357 if (!gst_structure_get_clock_time (structure, "ntp-offset", &event_offset)) {
358 GST_ERROR_OBJECT (self, "no ntp-offset in %" GST_PTR_FORMAT, event);
361 if (!gst_structure_get_boolean (structure, "discont", &event_discont)) {
362 GST_ERROR_OBJECT (self, "no discontinue in %" GST_PTR_FORMAT, event);
367 *offset = event_offset;
370 *discont = event_discont;
376 gst_rtp_onvif_timestamp_sink_event (GstPad * pad, GstObject * parent,
379 GstRtpOnvifTimestamp *self = GST_RTP_ONVIF_TIMESTAMP (parent);
380 gboolean drop = FALSE;
383 GST_DEBUG_OBJECT (pad, "handling event %s", GST_EVENT_TYPE_NAME (event));
385 /* handle serialized events, which, should not be enqueued */
386 switch (GST_EVENT_TYPE (event)) {
387 case GST_EVENT_CUSTOM_DOWNSTREAM:
388 /* if the "set-e-bit" property is set, an offset event might mark the
389 * stream as discontinued. We need to check if the currently cached buffer
390 * or buffer list needs the e-bit before it's pushed */
391 if ((self->buffer != NULL || self->list != NULL) && self->prop_set_e_bit
392 && gst_event_has_name (event, GST_ONVIF_TIMESTAMP_EVENT_NAME)) {
394 if (parse_event_ntp_offset (self, event, NULL, &discont)) {
395 GST_DEBUG_OBJECT (self, "stream %s discontinued",
396 (discont ? "is" : "is not"));
397 self->set_e_bit = discont;
409 /* Push pending buffers, if any */
410 self->set_e_bit = TRUE;
411 if (self->prop_set_t_bit)
412 self->set_t_bit = TRUE;
413 res = send_cached_buffer_and_events (self);
414 if (res != GST_FLOW_OK) {
421 case GST_EVENT_FLUSH_STOP:
422 purge_cached_buffer_and_events (self);
423 self->set_d_bit = TRUE;
424 self->set_e_bit = FALSE;
425 self->set_t_bit = FALSE;
426 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
432 /* enqueue serialized events if there is a cached buffer */
433 if (GST_EVENT_IS_SERIALIZED (event) && (self->buffer || self->list)) {
434 GST_WARNING ("enqueueing serialized event");
435 g_queue_push_tail (self->event_queue, event);
440 /* handle rest of the events */
441 switch (GST_EVENT_TYPE (event)) {
442 case GST_EVENT_CUSTOM_DOWNSTREAM:
443 /* update the ntp-offset after any cached buffer/buffer list has been
444 * pushed. the d-bit of the next buffer/buffer list should be set if
445 * the stream is discontinued */
446 if (gst_event_has_name (event, GST_ONVIF_TIMESTAMP_EVENT_NAME)) {
449 if (parse_event_ntp_offset (self, event, &offset, &discont)) {
450 GST_DEBUG_OBJECT (self, "new ntp-offset: %" GST_TIME_FORMAT
451 ", stream %s discontinued", GST_TIME_ARGS (offset),
452 (discont ? "is" : "is not"));
453 self->ntp_offset = offset;
454 self->set_d_bit = discont;
461 case GST_EVENT_SEGMENT:
462 gst_event_copy_segment (event, &self->segment);
470 gst_event_unref (event);
472 ret = gst_pad_event_default (pad, parent, event);
478 gst_rtp_onvif_timestamp_init (GstRtpOnvifTimestamp * self)
481 gst_pad_new_from_static_template (&sink_template_factory, "sink");
482 gst_pad_set_chain_function (self->sinkpad, gst_rtp_onvif_timestamp_chain);
483 gst_pad_set_chain_list_function (self->sinkpad,
484 gst_rtp_onvif_timestamp_chain_list);
485 gst_pad_set_event_function (self->sinkpad,
486 gst_rtp_onvif_timestamp_sink_event);
487 gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
488 GST_PAD_SET_PROXY_CAPS (self->sinkpad);
489 GST_PAD_SET_PROXY_ALLOCATION (self->sinkpad);
492 gst_pad_new_from_static_template (&src_template_factory, "src");
493 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
495 self->prop_use_reference_timestamps = DEFAULT_USE_REFERENCE_TIMESTAMPS;
496 self->reference_timestamp_id = gst_caps_new_empty_simple ("timestamp/x-unix");
498 self->prop_ntp_offset = DEFAULT_NTP_OFFSET;
499 self->prop_set_e_bit = DEFAULT_SET_E_BIT;
500 self->prop_set_t_bit = DEFAULT_SET_T_BIT;
501 self->prop_drop_out_of_segment = DEFAULT_DROP_OUT_OF_SEGMENT;
503 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
505 self->event_queue = g_queue_new ();
510 #define EXTENSION_ID 0xABAC
511 #define EXTENSION_SIZE 3
514 get_utc_from_reference_timestamp (GstRtpOnvifTimestamp * self, GstBuffer * buf)
516 GstReferenceTimestampMeta *meta;
519 meta = gst_buffer_get_reference_timestamp_meta (buf,
520 self->reference_timestamp_id);
522 /* the reference timestamp is expressed in unix times so add the difference
523 * between unix and ntp epochs */
524 time = meta->timestamp + G_GUINT64_CONSTANT (2208988800) * GST_SECOND;
525 GST_TRACE_OBJECT (self, "UTC reference timestamp found: %" GST_TIME_FORMAT,
526 GST_TIME_ARGS (time));
528 GST_ERROR_OBJECT (self, "UTC reference timestamp not found");
529 time = GST_CLOCK_TIME_NONE;
536 get_utc_from_offset (GstRtpOnvifTimestamp * self, GstBuffer * buf)
538 guint64 time = GST_CLOCK_TIME_NONE;
540 if (GST_BUFFER_PTS_IS_VALID (buf)) {
541 time = gst_segment_to_stream_time (&self->segment, GST_FORMAT_TIME,
542 GST_BUFFER_PTS (buf));
543 } else if (GST_BUFFER_DTS_IS_VALID (buf)) {
544 time = gst_segment_to_stream_time (&self->segment, GST_FORMAT_TIME,
545 GST_BUFFER_DTS (buf));
547 g_assert_not_reached ();
550 /* add the offset (in seconds) */
551 if (time != GST_CLOCK_TIME_NONE) {
552 time += self->ntp_offset;
559 handle_buffer (GstRtpOnvifTimestamp * self, GstBuffer * buf)
561 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
568 if (!self->prop_use_reference_timestamps &&
569 !GST_CLOCK_TIME_IS_VALID (self->ntp_offset)) {
570 GstClock *clock = gst_element_get_clock (GST_ELEMENT (self));
573 GstClockTime clock_time = gst_clock_get_time (clock);
574 guint64 real_time = g_get_real_time ();
575 GstClockTime running_time = clock_time -
576 gst_element_get_base_time (GST_ELEMENT (self));
578 /* convert microseconds to nanoseconds */
581 /* add constant to convert from 1970 based time to 1900 based time */
582 real_time += (G_GUINT64_CONSTANT (2208988800) * GST_SECOND);
584 self->ntp_offset = real_time - running_time;
586 GST_DEBUG_OBJECT (self, "new ntp-offset: %" GST_TIME_FORMAT,
587 GST_TIME_ARGS (self->ntp_offset));
589 gst_object_unref (clock);
591 GST_ELEMENT_ERROR (self, STREAM, FAILED, ("No ntp-offset present"),
592 ("Can not guess ntp-offset with no clock."));
593 /* Received a buffer in PAUSED, so we can't guess the match
594 * between the running time and the NTP clock yet.
600 if (self->segment.format != GST_FORMAT_TIME) {
601 GST_ELEMENT_ERROR (self, STREAM, FAILED,
602 ("did not receive a time segment yet"), (NULL));
606 if (!gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtp)) {
607 GST_ELEMENT_ERROR (self, STREAM, FAILED,
608 ("Failed to map RTP buffer"), (NULL));
612 if (!gst_rtp_buffer_set_extension_data (&rtp, EXTENSION_ID, EXTENSION_SIZE)) {
613 GST_ELEMENT_ERROR (self, STREAM, FAILED, ("Failed to set extension data"),
615 gst_rtp_buffer_unmap (&rtp);
619 if (!gst_rtp_buffer_get_extension_data (&rtp, &bits, (gpointer) & data,
621 GST_ELEMENT_ERROR (self, STREAM, FAILED, ("Failed to get extension data"),
623 gst_rtp_buffer_unmap (&rtp);
627 if (self->prop_use_reference_timestamps) {
628 time = get_utc_from_reference_timestamp (self, buf);
629 if (time == GST_CLOCK_TIME_NONE) {
630 gst_rtp_buffer_unmap (&rtp);
633 } else if (GST_BUFFER_PTS_IS_VALID (buf) || GST_BUFFER_DTS_IS_VALID (buf)) {
634 time = get_utc_from_offset (self, buf);
635 if (self->prop_drop_out_of_segment && time == GST_CLOCK_TIME_NONE) {
636 GST_ERROR_OBJECT (self, "Failed to get stream time");
637 gst_rtp_buffer_unmap (&rtp);
641 GST_INFO_OBJECT (self,
642 "Buffer doesn't contain any valid DTS or PTS timestamp");
646 if (time == GST_CLOCK_TIME_NONE) {
647 GST_ERROR_OBJECT (self, "failed calculating timestamp");
648 gst_rtp_buffer_unmap (&rtp);
652 /* convert to NTP time. upper 32 bits should contain the seconds
653 * and the lower 32 bits, the fractions of a second. */
654 time = gst_util_uint64_scale (time, (G_GINT64_CONSTANT (1) << 32),
657 GST_DEBUG_OBJECT (self, "timestamp: %" G_GUINT64_FORMAT, time);
659 GST_WRITE_UINT64_BE (data, time);
661 /* The next byte is composed of: C E D T mbz (4 bits) */
663 /* Set C if the buffer does *not* have the DELTA_UNIT flag as it means
664 * that's a key frame (or 'clean point'). */
665 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
666 GST_DEBUG_OBJECT (self, "set C flag");
670 /* Set E if this the last buffer of a contiguous section of recording */
671 if (self->set_e_bit) {
672 GST_DEBUG_OBJECT (self, "set E flag");
674 self->set_e_bit = FALSE;
677 /* Set D if the buffer has the DISCONT flag */
678 if (self->set_d_bit || GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
679 GST_DEBUG_OBJECT (self, "set D flag");
681 self->set_d_bit = FALSE;
684 /* Set T if we have received EOS */
685 if (self->set_t_bit) {
686 GST_DEBUG_OBJECT (self, "set T flag");
688 self->set_t_bit = FALSE;
691 GST_WRITE_UINT8 (data + 8, field);
693 /* CSeq (low-order byte) */
694 GST_WRITE_UINT8 (data + 9, (guchar) self->prop_cseq);
696 memset (data + 10, 0, 3);
699 gst_rtp_buffer_unmap (&rtp);
703 /* @buf: (transfer full) */
705 handle_and_push_buffer (GstRtpOnvifTimestamp * self, GstBuffer * buf)
707 if (!handle_buffer (self, buf)) {
708 gst_buffer_unref (buf);
709 return GST_FLOW_ERROR;
712 return gst_pad_push (self->srcpad, buf);
716 gst_rtp_onvif_timestamp_chain (GstPad * pad, GstObject * parent,
719 GstRtpOnvifTimestamp *self = GST_RTP_ONVIF_TIMESTAMP (parent);
720 GstFlowReturn result = GST_FLOW_OK;
722 if (!self->prop_set_e_bit && !self->prop_set_t_bit) {
723 /* Modify and push this buffer right away */
724 return handle_and_push_buffer (self, buf);
727 /* send any previously cached item(s), this leaves an empty queue */
728 result = send_cached_buffer_and_events (self);
730 /* enqueue the new item, as the only item in the queue */
736 do_handle_buffer (GstBuffer ** buffer, guint idx, GstRtpOnvifTimestamp * self)
738 return handle_buffer (self, *buffer);
741 /* @buf: (transfer full) */
743 handle_and_push_buffer_list (GstRtpOnvifTimestamp * self, GstBufferList * list)
745 if (!gst_buffer_list_foreach (list, (GstBufferListFunc) do_handle_buffer,
747 gst_buffer_list_unref (list);
748 return GST_FLOW_ERROR;
751 return gst_pad_push_list (self->srcpad, list);
754 /* gst_pad_chain_list_default() refs the buffer when passing it to the chain
755 * function, making it not writable. We implement our own chain_list function
756 * to avoid having to copy each buffer. */
758 gst_rtp_onvif_timestamp_chain_list (GstPad * pad, GstObject * parent,
759 GstBufferList * list)
761 GstRtpOnvifTimestamp *self = GST_RTP_ONVIF_TIMESTAMP (parent);
762 GstFlowReturn result = GST_FLOW_OK;
764 if (!self->prop_set_e_bit && !self->prop_set_t_bit) {
765 return handle_and_push_buffer_list (self, list);
768 /* send any previously cached item(s), this leaves an empty queue */
769 result = send_cached_buffer_and_events (self);
771 /* enqueue the new item, as the only item in the queue */