56 #define DBG (!self->silent)
60 #define ABSDIFF(a, b) (((a) > (b)) ? (a) - (b) : (b) - (a))
64 #define GST_CAT_DEFAULT gst_tensor_rate_debug
66 #define CAPS_STRING GST_TENSOR_CAP_DEFAULT "; " GST_TENSORS_CAP_DEFAULT
68 #define GST_TENSOR_RATE_SCALED_TIME(self, count)\
69 gst_util_uint64_scale (count,\
70 self->to_rate_denominator * GST_SECOND, self->to_rate_numerator)
73 #define DEFAULT_SILENT TRUE
74 #define DEFAULT_THROTTLE TRUE
94 static GstStaticPadTemplate
sink_factory = GST_STATIC_PAD_TEMPLATE (
"sink",
102 static GstStaticPadTemplate
src_factory = GST_STATIC_PAD_TEMPLATE (
"src",
110 #define gst_tensor_rate_parent_class parent_class
115 const GValue * value, GParamSpec * pspec);
117 GValue * value, GParamSpec * pspec);
124 GstPadDirection direction, GstCaps * caps, GstCaps * _rate);
126 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
128 GstCaps * incaps, GstCaps * outcaps);
130 GstBuffer * buffer, gint64 time);
132 gboolean duplicate, GstClockTime next_intime);
150 GObjectClass *gobject_class;
151 GstElementClass *gstelement_class;
152 GstBaseTransformClass *trans_class;
154 GST_DEBUG_CATEGORY_INIT (gst_tensor_rate_debug,
"tensor_rate", 0,
155 "Tensor Rate to control streams based on tensor(s) values");
157 trans_class = (GstBaseTransformClass *) klass;
158 gstelement_class = (GstElementClass *) trans_class;
159 gobject_class = (GObjectClass *) gstelement_class;
167 gst_element_class_set_details_simple (gstelement_class,
170 "Adjusts a framerate of incoming tensors",
171 "Dongju Chae <dongju.chae@samsung.com>");
173 gst_element_class_add_pad_template (gstelement_class,
175 gst_element_class_add_pad_template (gstelement_class,
178 trans_class->passthrough_on_same_caps =
TRUE;
179 trans_class->transform_ip_on_passthrough =
TRUE;
185 trans_class->transform_caps =
203 gboolean duplicate, GstClockTime next_intime)
206 GstClockTime push_ts;
209 GST_BUFFER_OFFSET (outbuf) =
self->out;
210 GST_BUFFER_OFFSET_END (outbuf) =
self->out + 1;
211 GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
214 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
216 GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_GAP);
219 push_ts =
self->next_ts;
222 self->out_frame_count++;
224 if (self->to_rate_numerator) {
225 GstClockTimeDiff duration;
230 self->next_ts =
self->segment.base +
self->segment.start +
231 self->base_ts + duration;
233 GST_BUFFER_DURATION (outbuf) =
self->next_ts - push_ts;
237 g_assert (GST_BUFFER_PTS_IS_VALID (outbuf));
238 g_assert (GST_BUFFER_DURATION_IS_VALID (outbuf));
239 g_assert (GST_BUFFER_DURATION (outbuf) != 0);
241 self->next_ts = GST_BUFFER_PTS (outbuf) + GST_BUFFER_DURATION (outbuf);
245 GST_BUFFER_TIMESTAMP (outbuf) = push_ts -
self->segment.base;
247 silent_debug (
self,
"old is best, dup, pushing buffer outgoing ts %"
248 GST_TIME_FORMAT, GST_TIME_ARGS (push_ts));
250 res = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (
self), outbuf);
260 GstClockTime next_intime)
264 if (!self->prevbuf) {
265 ml_logi (
"got EOS before any buffer was received");
269 outbuf = gst_buffer_ref (self->prevbuf);
271 outbuf = gst_buffer_make_writable (outbuf);
283 silent_debug (
self,
"swap_prev: storing buffer %p in prev", buffer);
286 gst_buffer_unref (self->prevbuf);
287 self->prevbuf = buffer != NULL ? gst_buffer_ref (buffer) : NULL;
288 self->prev_ts = time;
302 self->out_frame_count = 0;
305 self->next_ts = GST_CLOCK_TIME_NONE;
306 self->last_ts = GST_CLOCK_TIME_NONE;
308 self->sent_qos_on_passthrough =
FALSE;
325 self->from_rate_numerator = 0;
326 self->from_rate_denominator = 0;
327 self->to_rate_numerator = 0;
328 self->to_rate_denominator = 0;
334 gst_segment_init (&self->segment, GST_FORMAT_TIME);
343 G_OBJECT_CLASS (parent_class)->finalize (
object);
351 const GValue * value, GParamSpec * pspec)
355 GST_OBJECT_LOCK (
self);
359 self->silent = g_value_get_boolean (value);
362 self->throttle = g_value_get_boolean (value);
366 const gchar *str = g_value_get_string (value);
367 gchar **strv = g_strsplit (str,
"/", -1);
370 if (g_strv_length (strv) != 2) {
371 ml_loge (
"Please specify a proper 'framerate' property");
375 rate_n = (gint) g_ascii_strtoll (strv[0], NULL, 10);
376 if (errno == ERANGE || rate_n < 0) {
377 ml_loge (
"Invalid frame rate numerator in 'framerate'");
381 rate_d = (gint) g_ascii_strtoll (strv[1], NULL, 10);
382 if (errno == ERANGE || rate_d <= 0) {
383 ml_loge (
"Invalid frame rate denominator in 'framerate'");
387 self->rate_n = rate_n;
388 self->rate_d = rate_d;
396 G_OBJECT_WARN_INVALID_PROPERTY_ID (
object, prop_id, pspec);
400 GST_OBJECT_UNLOCK (
self);
408 GValue * value, GParamSpec * pspec)
412 GST_OBJECT_LOCK (
self);
416 g_value_set_uint64 (value, self->in);
419 g_value_set_uint64 (value, self->out);
422 g_value_set_uint64 (value, self->drop);
425 g_value_set_uint64 (value, self->dup);
428 g_value_set_boolean (value, self->silent);
431 g_value_set_boolean (value, self->throttle);
434 if (self->rate_n < 0 || self->rate_d <= 0) {
437 gchar *str = g_strdup_printf (
"%d/%d", self->rate_n, self->rate_d);
438 g_value_take_string (value, str);
442 G_OBJECT_WARN_INVALID_PROPERTY_ID (
object, prop_id, pspec);
446 GST_OBJECT_UNLOCK (
self);
449 #define THROTTLE_DELAY_RATIO (0.999)
457 GstPad *sinkpad = GST_BASE_TRANSFORM_SINK_PAD (&self->element);
458 GstClockTimeDiff delay;
464 event = gst_event_new_qos (GST_QOS_TYPE_THROTTLE,
465 0.9 , delay, timestamp);
467 silent_debug (
self,
"Send throttling event with delay: %" GST_TIME_FORMAT,
468 GST_TIME_ARGS (delay));
470 gst_pad_push_event (sinkpad, event);
480 GstFlowReturn res = GST_BASE_TRANSFORM_FLOW_DROPPED;
481 GstClockTime intime, in_ts, in_dur;
484 if (self->from_rate_denominator == 0 || self->to_rate_denominator == 0) {
485 ml_loge (
"No framerate negotiated");
486 return GST_FLOW_NOT_NEGOTIATED;
490 if (G_UNLIKELY (self->segment.rate < 0.0)) {
491 ml_loge (
"Unsupported reverse playback\n");
492 return GST_FLOW_ERROR;
495 in_ts = GST_BUFFER_TIMESTAMP (buffer);
496 in_dur = GST_BUFFER_DURATION (buffer);
498 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (in_ts))) {
499 in_ts =
self->last_ts;
500 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (in_ts))) {
501 ml_logw (
"Discard an invalid buffer");
502 return GST_BASE_TRANSFORM_FLOW_DROPPED;
509 self->last_ts = in_ts;
510 if (GST_CLOCK_TIME_IS_VALID (in_dur))
511 self->last_ts += in_dur;
513 silent_debug (
self,
"got buffer with timestamp %" GST_TIME_FORMAT,
514 GST_TIME_ARGS (in_ts));
516 intime = in_ts +
self->segment.base;
519 if (gst_base_transform_is_passthrough (trans)) {
520 if (!self->sent_qos_on_passthrough) {
521 self->sent_qos_on_passthrough =
TRUE;
530 if (self->prevbuf == NULL) {
532 if (!GST_CLOCK_TIME_IS_VALID (self->next_ts)) {
533 self->next_ts = intime;
534 self->base_ts = in_ts -
self->segment.start;
535 self->out_frame_count = 0;
538 GstClockTime prevtime;
539 gint64 diff1 = 0, diff2 = 0;
542 prevtime =
self->prev_ts;
544 silent_debug (
self,
"BEGINNING prev buf %" GST_TIME_FORMAT
" new buf %"
545 GST_TIME_FORMAT
" outgoing ts %" GST_TIME_FORMAT,
546 GST_TIME_ARGS (prevtime), GST_TIME_ARGS (intime),
547 GST_TIME_ARGS (self->next_ts));
550 if (intime < prevtime) {
551 silent_debug (
self,
"The new buffer (%" GST_TIME_FORMAT
") is before "
552 "the previous buffer (%" GST_TIME_FORMAT
553 "). Dropping new buffer.", GST_TIME_ARGS (intime),
554 GST_TIME_ARGS (prevtime));
560 return GST_BASE_TRANSFORM_FLOW_DROPPED;
565 GstClockTime next_ts;
568 if (!GST_BUFFER_DURATION_IS_VALID (self->prevbuf))
569 GST_BUFFER_DURATION (self->prevbuf) =
570 intime > prevtime ? intime - prevtime : 0;
572 next_ts =
self->base_ts + (
self->next_ts -
self->base_ts);
574 diff1 =
ABSDIFF (prevtime, next_ts);
575 diff2 =
ABSDIFF (intime, next_ts);
578 " diff with new %" GST_TIME_FORMAT
" outgoing ts %"
579 GST_TIME_FORMAT, GST_TIME_ARGS (diff1),
580 GST_TIME_ARGS (diff2), GST_TIME_ARGS (next_ts));
583 if (diff1 <= diff2) {
589 count > 1, intime)) != GST_FLOW_OK) {
598 }
while (diff1 < diff2);
602 self->dup += count - 1;
607 else if (count == 0) {
635 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
638 GstCaps *
result = gst_caps_new_empty ();
645 for (i = 0; i < gst_caps_get_size (caps); i++) {
646 GstStructure *s, *const_s = gst_caps_get_structure (caps, i);
648 s = gst_structure_copy (const_s);
651 if (direction == GST_PAD_SINK && self->rate_n >= 0 && self->rate_d > 0) {
652 gst_structure_set (s,
"framerate", GST_TYPE_FRACTION,
653 self->rate_n, self->rate_d, NULL);
655 gst_structure_set (s,
"framerate", GST_TYPE_FRACTION_RANGE,
656 0, 1, G_MAXINT, 1, NULL);
660 gst_caps_features_copy (gst_caps_get_features (caps, i)));
663 if (filter && gst_caps_get_size (filter) > 0) {
664 GstCaps *intersection =
665 gst_caps_intersect_full (filter,
result, GST_CAPS_INTERSECT_FIRST);
681 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
688 s = gst_caps_get_structure (caps, 0);
689 if (G_UNLIKELY (!gst_structure_get_fraction (s,
"framerate", &num, &denom)))
692 othercaps = gst_caps_truncate (othercaps);
693 othercaps = gst_caps_make_writable (othercaps);
695 s = gst_caps_get_structure (othercaps, 0);
696 gst_structure_fixate_field_nearest_fraction (s,
"framerate", num, denom);
698 return gst_caps_fixate (othercaps);
706 GstCaps * in_caps, GstCaps * out_caps)
709 GstStructure *structure;
710 gint rate_numerator, rate_denominator;
713 "setcaps called in: %" GST_PTR_FORMAT
" out: %" GST_PTR_FORMAT, in_caps,
716 structure = gst_caps_get_structure (in_caps, 0);
718 if (!gst_structure_get_fraction (structure,
"framerate",
719 &rate_numerator, &rate_denominator))
722 self->from_rate_numerator = rate_numerator;
723 self->from_rate_denominator = rate_denominator;
725 structure = gst_caps_get_structure (out_caps, 0);
727 if (!gst_structure_get_fraction (structure,
"framerate",
728 &rate_numerator, &rate_denominator))
731 if (self->to_rate_numerator)
734 self->out_frame_count = 0;
735 self->to_rate_numerator = rate_numerator;
736 self->to_rate_denominator = rate_denominator;
744 self->last_ts = GST_CLOCK_TIME_NONE;
760 g_object_notify_by_pspec ((GObject *)
self,
pspec_drop);
773 #define MAGIC_LIMIT 25
785 switch (GST_EVENT_TYPE (event)) {
786 case GST_EVENT_SEGMENT:
792 gst_event_type_get_name (GST_EVENT_TYPE (event)));
794 gst_event_copy_segment (event, &segment);
795 if (segment.format != GST_FORMAT_TIME) {
796 ml_loge (
"Got segment but doesn't have GST_FORMAT_TIME value");
812 && ((GST_CLOCK_TIME_IS_VALID (self->segment.stop)
813 && GST_CLOCK_TIME_IS_VALID (self->next_ts)
814 && self->next_ts - self->segment.base <
815 self->segment.stop) || count < 1)) {
821 self->dup += count - 1;
830 self->out_frame_count = 0;
831 self->next_ts = GST_CLOCK_TIME_NONE;
833 gst_segment_copy_into (&segment, &self->segment);
835 silent_debug (
self,
"updated segment: %" GST_SEGMENT_FORMAT,
838 seqnum = gst_event_get_seqnum (event);
839 gst_event_unref (event);
840 event = gst_event_new_segment (&segment);
841 gst_event_set_seqnum (event, seqnum);
845 case GST_EVENT_SEGMENT_DONE:
849 GstFlowReturn res = GST_FLOW_OK;
852 gst_event_type_get_name (GST_EVENT_TYPE (event)));
855 if (GST_CLOCK_TIME_IS_VALID (self->segment.stop)) {
862 && (GST_CLOCK_TIME_IS_VALID (self->segment.stop)
863 && GST_CLOCK_TIME_IS_VALID (self->next_ts)
864 && (self->next_ts - self->segment.base < self->segment.stop))) {
866 GST_CLOCK_TIME_NONE);
869 }
else if (self->prevbuf) {
874 if (GST_BUFFER_DURATION_IS_VALID (self->prevbuf)) {
875 GstClockTime end_ts =
876 self->next_ts + GST_BUFFER_DURATION (self->prevbuf);
878 while (res == GST_FLOW_OK && count <=
MAGIC_LIMIT &&
879 ((GST_CLOCK_TIME_IS_VALID (self->segment.stop)
880 && GST_CLOCK_TIME_IS_VALID (self->next_ts)
881 && self->next_ts - self->segment.base < end_ts)
885 GST_CLOCK_TIME_NONE);
895 self->dup += count - 1;
902 case GST_EVENT_FLUSH_STOP:
905 gst_event_type_get_name (GST_EVENT_TYPE (event)));
911 gst_event_type_get_name (GST_EVENT_TYPE (event)));
912 gst_event_unref (event);
919 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
956 g_object_class_install_property (object_class,
PROP_IN,
957 g_param_spec_uint64 (
"in",
"In",
958 "Number of input frames",
959 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
962 g_object_class_install_property (object_class,
PROP_OUT,
963 g_param_spec_uint64 (
"out",
"Out",
964 "Number of output frames",
965 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
969 "Number of duplicated frames", 0,
970 G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
975 g_param_spec_uint64 (
"drop",
"Drop",
"Number of dropped frames", 0,
976 G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
980 g_object_class_install_property (object_class,
PROP_SILENT,
981 g_param_spec_boolean (
"silent",
"Silent",
982 "Don't produce verbose output including dropped/duplicated frames",
986 g_object_class_install_property (object_class,
PROP_THROTTLE,
987 g_param_spec_boolean (
"throttle",
"Throttle",
988 "Send QoS events to upstream elements to limit a incoming data rate",
993 g_param_spec_string (
"framerate",
"Framerate",
994 "Specify a target framerate to adjust (e.g., framerate=10/1). "
995 "Otherwise, the latest processing time will be a target interval.",
996 "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));