#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_GETRUSAGE #include "gst-cpu-throttling-clock.h" #include #include #include "gst-cpu-throttling-clock.h" /** * SECTION: gst-cpu-throttling-clock * @title: GstCpuThrottlingClock * @short_description: TODO * * TODO */ /* *INDENT-OFF* */ GST_DEBUG_CATEGORY_STATIC (gst_cpu_throttling_clock_debug); #define GST_CAT_DEFAULT gst_cpu_throttling_clock_debug struct _GstCpuThrottlingClockPrivate { guint wanted_cpu_usage; GstClock *sclock; GstClockTime current_wait_time; GstPoll *timer; struct rusage last_usage; GstClockID evaluate_wait_time; GstClockTime time_between_evals; }; #define parent_class gst_cpu_throttling_clock_parent_class G_DEFINE_TYPE_WITH_CODE (GstCpuThrottlingClock, gst_cpu_throttling_clock, GST_TYPE_CLOCK, G_ADD_PRIVATE(GstCpuThrottlingClock)) enum { PROP_FIRST, PROP_CPU_USAGE, PROP_LAST }; static GParamSpec *param_specs[PROP_LAST] = { NULL, }; /* *INDENT-ON* */ static void gst_cpu_throttling_clock_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (object); switch (property_id) { case PROP_CPU_USAGE: g_value_set_uint (value, self->priv->wanted_cpu_usage); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gst_cpu_throttling_clock_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (object); switch (property_id) { case PROP_CPU_USAGE: self->priv->wanted_cpu_usage = g_value_get_uint (value); if (self->priv->wanted_cpu_usage == 0) self->priv->wanted_cpu_usage = 100; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gboolean gst_transcoder_adjust_wait_time (GstClock * sync_clock, GstClockTime time, GstClockID id, GstCpuThrottlingClock * self) { struct rusage ru; float delta_usage, usage, coef; GstCpuThrottlingClockPrivate *priv = self->priv; getrusage (RUSAGE_SELF, &ru); delta_usage = GST_TIMEVAL_TO_TIME (ru.ru_utime) - GST_TIMEVAL_TO_TIME (self->priv->last_usage.ru_utime); usage = ((float) delta_usage / self->priv->time_between_evals * 100) / g_get_num_processors (); self->priv->last_usage = ru; coef = GST_MSECOND / 10; if (usage < (gfloat) priv->wanted_cpu_usage) { coef = -coef; } priv->current_wait_time = CLAMP (0, (GstClockTime) priv->current_wait_time + coef, GST_SECOND); GST_DEBUG_OBJECT (self, "Avg is %f (wanted %d) => %" GST_TIME_FORMAT, usage, self->priv->wanted_cpu_usage, GST_TIME_ARGS (priv->current_wait_time)); return TRUE; } static GstClockReturn _wait (GstClock * clock, GstClockEntry * entry, GstClockTimeDiff * jitter) { GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (clock); if (!self->priv->evaluate_wait_time) { if (!(self->priv->sclock)) { GST_ERROR_OBJECT (clock, "Could not find any system clock" " to start the wait time evaluation task"); } else { self->priv->evaluate_wait_time = gst_clock_new_periodic_id (self->priv->sclock, gst_clock_get_time (self->priv->sclock), self->priv->time_between_evals); gst_clock_id_wait_async (self->priv->evaluate_wait_time, (GstClockCallback) gst_transcoder_adjust_wait_time, (gpointer) self, NULL); } } if (G_UNLIKELY (GST_CLOCK_ENTRY_STATUS (entry) == GST_CLOCK_UNSCHEDULED)) return GST_CLOCK_UNSCHEDULED; if (gst_poll_wait (self->priv->timer, self->priv->current_wait_time)) { GST_INFO_OBJECT (self, "Something happened on the poll"); } return GST_CLOCK_ENTRY_STATUS (entry); } static GstClockTime _get_internal_time (GstClock * clock) { GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (clock); return gst_clock_get_internal_time (self->priv->sclock); } static void gst_cpu_throttling_clock_dispose (GObject * object) { GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (object); if (self->priv->evaluate_wait_time) { gst_clock_id_unschedule (self->priv->evaluate_wait_time); gst_clock_id_unref (self->priv->evaluate_wait_time); self->priv->evaluate_wait_time = 0; } } static void gst_cpu_throttling_clock_class_init (GstCpuThrottlingClockClass * klass) { GObjectClass *oclass = G_OBJECT_CLASS (klass); GstClockClass *clock_klass = GST_CLOCK_CLASS (klass); GST_DEBUG_CATEGORY_INIT (gst_cpu_throttling_clock_debug, "cpuclock", 0, "UriTranscodebin element"); oclass->get_property = gst_cpu_throttling_clock_get_property; oclass->set_property = gst_cpu_throttling_clock_set_property; oclass->dispose = gst_cpu_throttling_clock_dispose; /** * GstCpuThrottlingClock:cpu-usage: * * Since: UNRELEASED */ param_specs[PROP_CPU_USAGE] = g_param_spec_uint ("cpu-usage", "cpu-usage", "The percentage of CPU to try to use with the processus running the " "pipeline driven by the clock", 0, 100, 100, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (oclass, PROP_LAST, param_specs); clock_klass->wait = GST_DEBUG_FUNCPTR (_wait); clock_klass->get_internal_time = _get_internal_time; } static void gst_cpu_throttling_clock_init (GstCpuThrottlingClock * self) { self->priv = gst_cpu_throttling_clock_get_instance_private (self); self->priv->current_wait_time = GST_MSECOND; self->priv->wanted_cpu_usage = 100; self->priv->timer = gst_poll_new_timer (); self->priv->time_between_evals = GST_SECOND / 4; self->priv->sclock = GST_CLOCK (gst_system_clock_obtain ()); getrusage (RUSAGE_SELF, &self->priv->last_usage); } GstCpuThrottlingClock * gst_cpu_throttling_clock_new (guint cpu_usage) { return g_object_new (GST_TYPE_CPU_THROTTLING_CLOCK, "cpu-usage", cpu_usage, NULL); } #endif