summaryrefslogtreecommitdiff
path: root/gst/transcode
diff options
context:
space:
mode:
authorSaunier Thibault <saunierthibault@gmail.com>2015-12-03 12:32:05 +0100
committerThibault Saunier <tsaunier@gnome.org>2019-08-28 13:02:13 +0000
commit7a66b16d976468fcf72c2d1398fd637bdb4e348c (patch)
tree6a2d09ec58ab6bd964135cca665814805bbcbfaa /gst/transcode
parent87311d404ef75c08fc8417fc7fb41e17002e80f6 (diff)
downloadgstreamer-plugins-bad-7a66b16d976468fcf72c2d1398fd637bdb4e348c.tar.gz
Import GstTranscoder
Diffstat (limited to 'gst/transcode')
-rw-r--r--gst/transcode/gst-cpu-throttling-clock.c220
-rw-r--r--gst/transcode/gst-cpu-throttling-clock.h60
-rw-r--r--gst/transcode/gsttranscodebin.c614
-rw-r--r--gst/transcode/gsttranscoding.h31
-rw-r--r--gst/transcode/gsturitranscodebin.c562
-rw-r--r--gst/transcode/meson.build13
6 files changed, 1500 insertions, 0 deletions
diff --git a/gst/transcode/gst-cpu-throttling-clock.c b/gst/transcode/gst-cpu-throttling-clock.c
new file mode 100644
index 000000000..45bb51a12
--- /dev/null
+++ b/gst/transcode/gst-cpu-throttling-clock.c
@@ -0,0 +1,220 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_GETRUSAGE
+#include "gst-cpu-throttling-clock.h"
+
+#include <unistd.h>
+#include <sys/resource.h>
+
+#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
diff --git a/gst/transcode/gst-cpu-throttling-clock.h b/gst/transcode/gst-cpu-throttling-clock.h
new file mode 100644
index 000000000..e946c1af2
--- /dev/null
+++ b/gst/transcode/gst-cpu-throttling-clock.h
@@ -0,0 +1,60 @@
+/*
+ * gst-cpu-throttling-clock.h
+ *
+ * Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __GST_CPU_THROTTLING_CLOCK_H__
+#define __GST_CPU_THROTTLING_CLOCK_H__
+
+#include <glib-object.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstCpuThrottlingClock GstCpuThrottlingClock;
+typedef struct _GstCpuThrottlingClockClass GstCpuThrottlingClockClass;
+typedef struct _GstCpuThrottlingClockPrivate GstCpuThrottlingClockPrivate;
+
+GType gst_cpu_throttling_clock_get_type (void) G_GNUC_CONST;
+
+#define GST_TYPE_CPU_THROTTLING_CLOCK (gst_cpu_throttling_clock_get_type ())
+#define GST_CPU_THROTTLING_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_CPU_THROTTLING_CLOCK, GstCpuThrottlingClock))
+#define GST_CPU_THROTTLING_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CPU_THROTTLING_CLOCK, GstCpuThrottlingClockClass))
+#define GST_IS_CPU_THROTTLING_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_CPU_THROTTLING_CLOCK))
+#define GST_IS_CPU_THROTTLING_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CPU_THROTTLING_CLOCK))
+#define GST_CPU_THROTTLING_CLOCK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CPU_THROTTLING_CLOCK, GstCpuThrottlingClockClass))
+
+struct _GstCpuThrottlingClockClass
+{
+ /*<private>*/
+ GstClockClass parent_class;
+};
+
+struct _GstCpuThrottlingClock
+{
+ /*<private>*/
+ GstClock parent;
+ GstCpuThrottlingClockPrivate *priv;
+};
+
+GstCpuThrottlingClock * gst_cpu_throttling_clock_new (guint cpu_usage);
+
+G_END_DECLS
+
+#endif /* #ifndef __GST_CPU_THROTTLING_CLOCK_H__*/
diff --git a/gst/transcode/gsttranscodebin.c b/gst/transcode/gsttranscodebin.c
new file mode 100644
index 000000000..51812f41d
--- /dev/null
+++ b/gst/transcode/gsttranscodebin.c
@@ -0,0 +1,614 @@
+/* GStreamer
+ * Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
+ *
+ * gsttranscodebin.c:
+ *
+ * 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
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gsttranscoding.h"
+#include <gst/pbutils/pbutils.h>
+
+#include <gst/pbutils/missing-plugins.h>
+
+GST_DEBUG_CATEGORY_STATIC (gst_transcodebin_debug);
+#define GST_CAT_DEFAULT gst_transcodebin_debug
+
+static GstStaticPadTemplate transcode_bin_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate transcode_bin_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+typedef struct
+{
+ GstBin parent;
+
+ GstElement *decodebin;
+ GstElement *encodebin;
+
+ GstEncodingProfile *profile;
+ gboolean avoid_reencoding;
+ GstPad *sinkpad;
+ GstPad *srcpad;
+
+ GstElement *audio_filter;
+ GstElement *video_filter;
+} GstTranscodeBin;
+
+typedef struct
+{
+ GstBinClass parent;
+
+} GstTranscodeBinClass;
+
+/* *INDENT-OFF* */
+#define GST_TYPE_TRANSCODE_BIN (gst_transcode_bin_get_type ())
+#define GST_TRANSCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TRANSCODE_BIN, GstTranscodeBin))
+#define GST_TRANSCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TRANSCODE_BIN_TYPE, GstTranscodeBinClass))
+#define GST_IS_TRANSCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TRANSCODE_BIN_TYPE))
+#define GST_IS_TRANSCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TRANSCODE_BIN_TYPE))
+#define GST_TRANSCODE_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TRANSCODE_BIN_TYPE, GstTranscodeBinClass))
+
+#define DEFAULT_AVOID_REENCODING FALSE
+
+G_DEFINE_TYPE (GstTranscodeBin, gst_transcode_bin, GST_TYPE_BIN)
+enum
+{
+ PROP_0,
+ PROP_PROFILE,
+ PROP_AVOID_REENCODING,
+ PROP_VIDEO_FILTER,
+ PROP_AUDIO_FILTER,
+ LAST_PROP
+};
+
+static void
+post_missing_plugin_error (GstElement * dec, const gchar * element_name)
+{
+ GstMessage *msg;
+
+ msg = gst_missing_element_message_new (dec, element_name);
+ gst_element_post_message (dec, msg);
+
+ GST_ELEMENT_ERROR (dec, CORE, MISSING_PLUGIN,
+ ("Missing element '%s' - check your GStreamer installation.",
+ element_name), (NULL));
+}
+/* *INDENT-ON* */
+
+static GstPad *
+_insert_filter (GstTranscodeBin * self, GstPad * sinkpad, GstPad * pad,
+ GstCaps * caps)
+{
+ GstPad *filter_src = NULL, *filter_sink = NULL;
+ GstElement *filter = NULL;
+ GstObject *filter_parent;
+
+ if (self->video_filter &&
+ !g_strcmp0 (gst_structure_get_name (gst_caps_get_structure (caps, 0)),
+ "video/x-raw")) {
+ filter = self->video_filter;
+ } else if (self->audio_filter &&
+ !g_strcmp0 (gst_structure_get_name (gst_caps_get_structure (caps, 0)),
+ "audio/x-raw")) {
+ filter = self->audio_filter;
+ }
+
+ if (!filter)
+ return pad;
+
+ if ((filter_parent = gst_object_get_parent (GST_OBJECT (filter)))) {
+ GST_WARNING_OBJECT (self,
+ "Filter already in use (inside %" GST_PTR_FORMAT ").", filter_parent);
+ GST_FIXME_OBJECT (self,
+ "Handle transcoding several streams of a same kind.");
+ gst_object_unref (filter_parent);
+
+ return pad;
+ }
+
+ /* We are guaranteed filters only have 1 unique sinkpad and srcpad */
+ GST_OBJECT_LOCK (filter);
+ filter_sink = filter->sinkpads->data;
+ filter_src = filter->srcpads->data;
+ GST_OBJECT_UNLOCK (filter);
+
+ gst_bin_add (GST_BIN (self), gst_object_ref (filter));
+ if (G_UNLIKELY (gst_pad_link (pad, filter_sink) != GST_PAD_LINK_OK)) {
+ GstCaps *othercaps = gst_pad_get_current_caps (sinkpad);
+ caps = gst_pad_get_current_caps (pad);
+
+ GST_ELEMENT_ERROR (self, CORE, PAD,
+ (NULL),
+ ("Couldn't link pads \n\n%" GST_PTR_FORMAT "\n\n and \n\n %"
+ GST_PTR_FORMAT "\n\n", caps, othercaps));
+
+ gst_caps_unref (caps);
+ gst_caps_unref (othercaps);
+ }
+
+ gst_element_sync_state_with_parent (filter);
+
+ return filter_src;
+}
+
+static void
+pad_added_cb (GstElement * decodebin, GstPad * pad, GstTranscodeBin * self)
+{
+ GstCaps *caps;
+ GstPad *sinkpad = NULL;
+ GstPadLinkReturn lret;
+
+ caps = gst_pad_query_caps (pad, NULL);
+
+ GST_DEBUG_OBJECT (decodebin, "Pad added, caps: %" GST_PTR_FORMAT, caps);
+
+ g_signal_emit_by_name (self->encodebin, "request-pad", caps, &sinkpad);
+
+ if (sinkpad == NULL) {
+ gchar *stream_id = gst_pad_get_stream_id (pad);
+
+ GST_ELEMENT_WARNING_WITH_DETAILS (self, STREAM, FORMAT,
+ (NULL), ("Stream with caps: %" GST_PTR_FORMAT " can not be"
+ " encoded in the defined encoding formats",
+ caps),
+ ("can-t-encode-stream", G_TYPE_BOOLEAN, TRUE,
+ "stream-caps", GST_TYPE_CAPS, caps,
+ "stream-id", G_TYPE_STRING, stream_id, NULL));
+
+ g_free (stream_id);
+ return;
+ }
+
+ if (caps)
+ gst_caps_unref (caps);
+
+ pad = _insert_filter (self, sinkpad, pad, caps);
+ lret = gst_pad_link (pad, sinkpad);
+ switch (lret) {
+ case GST_PAD_LINK_OK:
+ break;
+ case GST_PAD_LINK_WAS_LINKED:
+ GST_FIXME_OBJECT (self, "Pad %" GST_PTR_FORMAT " was already linked",
+ sinkpad);
+ break;
+ default:
+ {
+ GstCaps *othercaps = gst_pad_query_caps (sinkpad, NULL);
+ caps = gst_pad_get_current_caps (pad);
+
+ GST_ELEMENT_ERROR_WITH_DETAILS (self, CORE, PAD,
+ (NULL),
+ ("Couldn't link pads:\n %" GST_PTR_FORMAT ": %" GST_PTR_FORMAT
+ "\nand:\n"
+ " %" GST_PTR_FORMAT ": %" GST_PTR_FORMAT "\n\n",
+ pad, caps, sinkpad, othercaps),
+ ("linking-error", GST_TYPE_PAD_LINK_RETURN, lret,
+ "source-pad", GST_TYPE_PAD, pad,
+ "source-caps", GST_TYPE_CAPS, caps,
+ "sink-pad", GST_TYPE_PAD, sinkpad,
+ "sink-caps", GST_TYPE_CAPS, othercaps, NULL));
+
+ gst_caps_unref (caps);
+ if (othercaps)
+ gst_caps_unref (othercaps);
+ }
+ }
+
+ gst_object_unref (sinkpad);
+}
+
+static gboolean
+make_encodebin (GstTranscodeBin * self)
+{
+ GstPad *pad;
+ GST_INFO_OBJECT (self, "making new encodebin");
+
+ if (!self->profile)
+ goto no_profile;
+
+ self->encodebin = gst_element_factory_make ("encodebin", NULL);
+ if (!self->encodebin)
+ goto no_encodebin;
+
+ gst_bin_add (GST_BIN (self), self->encodebin);
+ g_object_set (self->encodebin, "profile", self->profile, NULL);
+
+ pad = gst_element_get_static_pad (self->encodebin, "src");
+ if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad)) {
+
+ gst_object_unref (pad);
+ GST_ERROR_OBJECT (self, "Could not ghost %" GST_PTR_FORMAT " srcpad",
+ self->encodebin);
+
+ return FALSE;
+ }
+ gst_object_unref (pad);
+
+ return gst_element_sync_state_with_parent (self->encodebin);
+
+ /* ERRORS */
+no_encodebin:
+ {
+ post_missing_plugin_error (GST_ELEMENT_CAST (self), "encodebin");
+
+ GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
+ ("No encodebin element, check your installation"));
+
+ return FALSE;
+ }
+ /* ERRORS */
+no_profile:
+ {
+ GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
+ ("No GstEncodingProfile set, can not run."));
+
+ return FALSE;
+ }
+}
+
+static gboolean
+make_decodebin (GstTranscodeBin * self)
+{
+ GstPad *pad;
+ GST_INFO_OBJECT (self, "making new decodebin");
+
+ self->decodebin = gst_element_factory_make ("decodebin", NULL);
+
+ if (!self->decodebin)
+ goto no_decodebin;
+
+ if (self->avoid_reencoding) {
+ GstCaps *decodecaps;
+
+ g_object_get (self->decodebin, "caps", &decodecaps, NULL);
+ if (GST_IS_ENCODING_CONTAINER_PROFILE (self->profile)) {
+ GList *tmp;
+
+ decodecaps = gst_caps_make_writable (decodecaps);
+ for (tmp = (GList *)
+ gst_encoding_container_profile_get_profiles
+ (GST_ENCODING_CONTAINER_PROFILE (self->profile)); tmp;
+ tmp = tmp->next) {
+ GstCaps *encodecaps = gst_encoding_profile_get_format (tmp->data);
+ GstCaps *restrictions =
+ gst_encoding_profile_get_restriction (tmp->data);
+
+ if (!restrictions)
+ gst_caps_append (decodecaps, encodecaps);
+ else
+ gst_caps_unref (restrictions);
+ }
+ }
+ g_object_set (self->decodebin, "caps", decodecaps, NULL);
+ gst_caps_unref (decodecaps);
+ }
+
+ g_signal_connect (self->decodebin, "pad-added", G_CALLBACK (pad_added_cb),
+ self);
+
+ gst_bin_add (GST_BIN (self), self->decodebin);
+ pad = gst_element_get_static_pad (self->decodebin, "sink");
+ if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad)) {
+
+ gst_object_unref (pad);
+ GST_ERROR_OBJECT (self, "Could not ghost %" GST_PTR_FORMAT " sinkpad",
+ self->decodebin);
+
+ return FALSE;
+ }
+
+ gst_object_unref (pad);
+ return TRUE;
+
+ /* ERRORS */
+no_decodebin:
+ {
+ post_missing_plugin_error (GST_ELEMENT_CAST (self), "decodebin");
+ GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
+ ("No decodebin element, check your installation"));
+
+ return FALSE;
+ }
+}
+
+static void
+remove_all_children (GstTranscodeBin * self)
+{
+ if (self->encodebin) {
+ gst_element_set_state (self->encodebin, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (self), self->encodebin);
+ self->encodebin = NULL;
+ }
+
+ if (self->video_filter && GST_OBJECT_PARENT (self->video_filter)) {
+ gst_element_set_state (self->video_filter, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (self), self->video_filter);
+ }
+
+ if (self->audio_filter && GST_OBJECT_PARENT (self->audio_filter)) {
+ gst_element_set_state (self->audio_filter, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (self), self->audio_filter);
+ }
+
+ if (self->decodebin) {
+ gst_element_set_state (self->decodebin, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (self), self->decodebin);
+ self->decodebin = NULL;
+ }
+}
+
+static GstStateChangeReturn
+gst_transcode_bin_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ GstTranscodeBin *self = GST_TRANSCODE_BIN (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+
+ if (!make_encodebin (self))
+ goto setup_failed;
+
+ if (!make_decodebin (self))
+ goto setup_failed;
+
+ break;
+ default:
+ break;
+ }
+
+ ret =
+ GST_ELEMENT_CLASS (gst_transcode_bin_parent_class)->change_state (element,
+ transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto beach;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ remove_all_children (self);
+ break;
+ default:
+ break;
+ }
+
+beach:
+ return ret;
+
+setup_failed:
+ remove_all_children (self);
+ return GST_STATE_CHANGE_FAILURE;
+}
+
+static void
+gst_transcode_bin_dispose (GObject * object)
+{
+ GstTranscodeBin *self = (GstTranscodeBin *) object;
+
+ g_clear_object (&self->video_filter);
+ g_clear_object (&self->audio_filter);
+
+ G_OBJECT_CLASS (gst_transcode_bin_parent_class)->dispose (object);
+}
+
+static void
+gst_transcode_bin_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ GstTranscodeBin *self = GST_TRANSCODE_BIN (object);
+
+ switch (prop_id) {
+ case PROP_PROFILE:
+ GST_OBJECT_LOCK (self);
+ g_value_set_object (value, self->profile);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_AVOID_REENCODING:
+ GST_OBJECT_LOCK (self);
+ g_value_set_boolean (value, self->avoid_reencoding);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_AUDIO_FILTER:
+ GST_OBJECT_LOCK (self);
+ g_value_set_object (value, self->audio_filter);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_VIDEO_FILTER:
+ GST_OBJECT_LOCK (self);
+ g_value_set_object (value, self->video_filter);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+_set_filter (GstTranscodeBin * self, GstElement * filter, GstElement ** mfilter)
+{
+ if (filter) {
+ GST_OBJECT_LOCK (filter);
+ if (filter->numsinkpads != 1) {
+ GST_ERROR_OBJECT (self, "Can not use %" GST_PTR_FORMAT
+ " as filter as it does not have "
+ " one and only one sinkpad", filter);
+ goto bail_out;
+ } else if (filter->numsrcpads != 1) {
+ GST_ERROR_OBJECT (self, "Can not use %" GST_PTR_FORMAT
+ " as filter as it does not have " " one and only one srcpad", filter);
+ goto bail_out;
+ }
+ GST_OBJECT_UNLOCK (filter);
+ }
+
+ GST_OBJECT_LOCK (self);
+ *mfilter = filter;
+ GST_OBJECT_UNLOCK (self);
+
+ return;
+
+bail_out:
+ GST_OBJECT_UNLOCK (filter);
+}
+
+static void
+gst_transcode_bin_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ GstTranscodeBin *self = GST_TRANSCODE_BIN (object);
+
+ switch (prop_id) {
+ case PROP_PROFILE:
+ GST_OBJECT_LOCK (self);
+ self->profile = g_value_dup_object (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_AVOID_REENCODING:
+ GST_OBJECT_LOCK (self);
+ self->avoid_reencoding = g_value_get_boolean (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_AUDIO_FILTER:
+ _set_filter (self, g_value_dup_object (value), &self->audio_filter);
+ break;
+ case PROP_VIDEO_FILTER:
+ _set_filter (self, g_value_dup_object (value), &self->video_filter);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gst_transcode_bin_class_init (GstTranscodeBinClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_klass;
+
+ object_class->dispose = gst_transcode_bin_dispose;
+ object_class->get_property = gst_transcode_bin_get_property;
+ object_class->set_property = gst_transcode_bin_set_property;
+
+ gstelement_klass = (GstElementClass *) klass;
+ gstelement_klass->change_state =
+ GST_DEBUG_FUNCPTR (gst_transcode_bin_change_state);
+
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&transcode_bin_sink_template));
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&transcode_bin_src_template));
+
+ gst_element_class_set_static_metadata (gstelement_klass,
+ "Transcode Bin", "Generic/Bin/Encoding",
+ "Autoplug and transcoder a stream",
+ "Thibault Saunier <tsaunier@igalia.com>");
+
+ /**
+ * GstTranscodeBin:profile:
+ *
+ * The #GstEncodingProfile to use. This property must be set before going
+ * to %GST_STATE_PAUSED or higher.
+ */
+ g_object_class_install_property (object_class, PROP_PROFILE,
+ g_param_spec_object ("profile", "Profile",
+ "The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstTranscodeBin:avoid-reencoding:
+ *
+ * See #encodebin:avoid-reencoding
+ */
+ g_object_class_install_property (object_class, PROP_AVOID_REENCODING,
+ g_param_spec_boolean ("avoid-reencoding", "Avoid re-encoding",
+ "Whether to re-encode portions of compatible video streams that lay on segment boundaries",
+ DEFAULT_AVOID_REENCODING,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstTranscodeBin:video-filter:
+ *
+ * Set the video filter element/bin to use.
+ */
+ g_object_class_install_property (object_class, PROP_VIDEO_FILTER,
+ g_param_spec_object ("video-filter", "Video filter",
+ "the video filter(s) to apply, if possible",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstTranscodeBin:audio-filter:
+ *
+ * Set the audio filter element/bin to use.
+ */
+ g_object_class_install_property (object_class, PROP_AUDIO_FILTER,
+ g_param_spec_object ("audio-filter", "Audio filter",
+ "the audio filter(s) to apply, if possible",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gst_transcode_bin_init (GstTranscodeBin * self)
+{
+ GstPadTemplate *pad_tmpl;
+
+ pad_tmpl = gst_static_pad_template_get (&transcode_bin_sink_template);
+ self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", pad_tmpl);
+ gst_pad_set_active (self->sinkpad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
+
+ gst_object_unref (pad_tmpl);
+
+ pad_tmpl = gst_static_pad_template_get (&transcode_bin_src_template);
+
+ self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", pad_tmpl);
+ gst_pad_set_active (self->srcpad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
+
+ gst_object_unref (pad_tmpl);
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gboolean res = TRUE;
+ gst_pb_utils_init ();
+
+ GST_DEBUG_CATEGORY_INIT (gst_transcodebin_debug, "transcodebin", 0,
+ "Transcodebin element");
+
+ res &= gst_element_register (plugin, "transcodebin", GST_RANK_NONE,
+ GST_TYPE_TRANSCODE_BIN);
+
+ res &= gst_element_register (plugin, "uritranscodebin", GST_RANK_NONE,
+ gst_uri_transcode_bin_get_type ());
+
+ return res;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ transcode,
+ "A plugin containing elements for transcoding", plugin_init, VERSION,
+ GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/gst/transcode/gsttranscoding.h b/gst/transcode/gsttranscoding.h
new file mode 100644
index 000000000..0e7f29c70
--- /dev/null
+++ b/gst/transcode/gsttranscoding.h
@@ -0,0 +1,31 @@
+/* GStreamer
+ * Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * gsttranscodebin.c:
+ *
+ * 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
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_TRANSCODING_H__
+#define __GST_TRANSCODING_H__
+
+
+#include <gst/gst.h>
+
+GType gst_transcode_bin_get_type (void);
+GType gst_uri_transcode_bin_get_type (void);
+
+#endif /* __GST_TRANSCODING_H__ */
diff --git a/gst/transcode/gsturitranscodebin.c b/gst/transcode/gsturitranscodebin.c
new file mode 100644
index 000000000..9b74ae469
--- /dev/null
+++ b/gst/transcode/gsturitranscodebin.c
@@ -0,0 +1,562 @@
+/* GStreamer
+ * Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
+ *
+ * gsturitranscodebin.c:
+ *
+ * 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
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gsttranscoding.h"
+#if HAVE_GETRUSAGE
+#include "gst-cpu-throttling-clock.h"
+#endif
+#include <gst/pbutils/pbutils.h>
+
+#include <gst/pbutils/missing-plugins.h>
+
+GST_DEBUG_CATEGORY_STATIC (gst_uri_transcodebin_debug);
+#define GST_CAT_DEFAULT gst_uri_transcodebin_debug
+
+typedef struct
+{
+ GstPipeline parent;
+
+ GstElement *src;
+ gchar *source_uri;
+
+ GstElement *transcodebin;
+
+ GstElement *audio_filter;
+ GstElement *video_filter;
+
+ GstEncodingProfile *profile;
+ gboolean avoid_reencoding;
+ guint wanted_cpu_usage;
+
+ GstElement *sink;
+ gchar *dest_uri;
+
+ GstClock *cpu_clock;
+
+} GstUriTranscodeBin;
+
+typedef struct
+{
+ GstPipelineClass parent;
+
+} GstUriTranscodeBinClass;
+
+/* *INDENT-OFF* */
+#define parent_class gst_uri_transcode_bin_parent_class
+#define GST_TYPE_URI_TRANSCODE_BIN (gst_uri_transcode_bin_get_type ())
+#define GST_URI_TRANSCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_URI_TRANSCODE_BIN, GstUriTranscodeBin))
+#define GST_URI_TRANSCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_URI_TRANSCODE_BIN_TYPE, GstUriTranscodeBinClass))
+#define GST_IS_TRANSCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_URI_TRANSCODE_BIN_TYPE))
+#define GST_IS_TRANSCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_URI_TRANSCODE_BIN_TYPE))
+#define GST_URI_TRANSCODE_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_URI_TRANSCODE_BIN_TYPE, GstUriTranscodeBinClass))
+
+#define DEFAULT_AVOID_REENCODING FALSE
+
+G_DEFINE_TYPE (GstUriTranscodeBin, gst_uri_transcode_bin, GST_TYPE_PIPELINE)
+enum
+{
+ PROP_0,
+ PROP_PROFILE,
+ PROP_SOURCE_URI,
+ PROP_DEST_URI,
+ PROP_AVOID_REENCODING,
+ PROP_SINK,
+ PROP_SRC,
+ PROP_CPU_USAGE,
+ PROP_VIDEO_FILTER,
+ PROP_AUDIO_FILTER,
+ LAST_PROP
+};
+
+static void
+post_missing_plugin_error (GstElement * dec, const gchar * element_name)
+{
+ GstMessage *msg;
+
+ msg = gst_missing_element_message_new (dec, element_name);
+ gst_element_post_message (dec, msg);
+
+ GST_ELEMENT_ERROR (dec, CORE, MISSING_PLUGIN,
+ ("Missing element '%s' - check your GStreamer installation.",
+ element_name), (NULL));
+}
+/* *INDENT-ON* */
+
+static gboolean
+make_transcodebin (GstUriTranscodeBin * self)
+{
+ GST_INFO_OBJECT (self, "making new transcodebin");
+
+ self->transcodebin = gst_element_factory_make ("transcodebin", NULL);
+ if (!self->transcodebin)
+ goto no_decodebin;
+
+ g_object_set (self->transcodebin, "profile", self->profile,
+ "video-filter", self->video_filter,
+ "audio-filter", self->audio_filter,
+ "avoid-reencoding", self->avoid_reencoding, NULL);
+
+ gst_bin_add (GST_BIN (self), self->transcodebin);
+ if (!gst_element_link (self->transcodebin, self->sink))
+ return FALSE;
+
+ return TRUE;
+
+ /* ERRORS */
+no_decodebin:
+ {
+ post_missing_plugin_error (GST_ELEMENT_CAST (self), "transcodebin");
+
+ GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
+ ("No transcodebin element, check your installation"));
+
+ return FALSE;
+ }
+}
+
+static gboolean
+make_dest (GstUriTranscodeBin * self)
+{
+ GError *err = NULL;
+
+ if (!gst_uri_is_valid (self->dest_uri))
+ goto invalid_uri;
+
+ self->sink = gst_element_make_from_uri (GST_URI_SINK, self->dest_uri,
+ "sink", &err);
+ if (!self->sink)
+ goto no_sink;
+
+ gst_bin_add (GST_BIN (self), self->sink);
+ g_object_set (self->sink, "sync", TRUE, "max-lateness", GST_CLOCK_TIME_NONE,
+ NULL);
+ return TRUE;
+
+invalid_uri:
+ {
+ GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
+ ("Invalid URI \"%s\".", self->dest_uri), (NULL));
+ g_clear_error (&err);
+ return FALSE;
+ }
+
+no_sink:
+ {
+ /* whoops, could not create the source element, dig a little deeper to
+ * figure out what might be wrong. */
+ if (err != NULL && err->code == GST_URI_ERROR_UNSUPPORTED_PROTOCOL) {
+ gchar *prot;
+
+ prot = gst_uri_get_protocol (self->dest_uri);
+ if (prot == NULL)
+ goto invalid_uri;
+
+ gst_element_post_message (GST_ELEMENT_CAST (self),
+ gst_missing_uri_source_message_new (GST_ELEMENT (self), prot));
+
+ GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN,
+ ("No URI handler implemented for \"%s\".", prot), (NULL));
+
+ g_free (prot);
+ } else {
+ GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
+ ("%s", (err) ? err->message : "URI was not accepted by any element"),
+ ("No element accepted URI '%s'", self->dest_uri));
+ }
+
+ g_clear_error (&err);
+
+ return FALSE;
+ }
+}
+
+static gboolean
+make_source (GstUriTranscodeBin * self)
+{
+ GError *err = NULL;
+
+ if (!gst_uri_is_valid (self->source_uri))
+ goto invalid_uri;
+
+ self->src = gst_element_make_from_uri (GST_URI_SRC, self->source_uri,
+ "src", &err);
+ if (!self->src)
+ goto no_sink;
+
+ gst_bin_add (GST_BIN (self), self->src);
+
+ if (!gst_element_link (self->src, self->transcodebin))
+ return FALSE;
+
+ return TRUE;
+
+invalid_uri:
+ {
+ GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
+ ("Invalid URI \"%s\".", self->source_uri), (NULL));
+ g_clear_error (&err);
+ return FALSE;
+ }
+
+no_sink:
+ {
+ /* whoops, could not create the source element, dig a little deeper to
+ * figure out what might be wrong. */
+ if (err != NULL && err->code == GST_URI_ERROR_UNSUPPORTED_PROTOCOL) {
+ gchar *prot;
+
+ prot = gst_uri_get_protocol (self->source_uri);
+ if (prot == NULL)
+ goto invalid_uri;
+
+ gst_element_post_message (GST_ELEMENT_CAST (self),
+ gst_missing_uri_source_message_new (GST_ELEMENT (self), prot));
+
+ GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN,
+ ("No URI handler implemented for \"%s\".", prot), (NULL));
+
+ g_free (prot);
+ } else {
+ GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
+ ("%s", (err) ? err->message : "URI was not accepted by any element"),
+ ("No element accepted URI '%s'", self->dest_uri));
+ }
+
+ g_clear_error (&err);
+
+ return FALSE;
+ }
+}
+
+static void
+remove_all_children (GstUriTranscodeBin * self)
+{
+ if (self->sink) {
+ gst_element_set_state (self->sink, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (self), self->sink);
+ self->sink = NULL;
+ }
+
+ if (self->transcodebin) {
+ gst_element_set_state (self->transcodebin, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (self), self->transcodebin);
+ self->transcodebin = NULL;
+ }
+
+ if (self->src) {
+ gst_element_set_state (self->src, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (self), self->src);
+ self->src = NULL;
+ }
+}
+
+static GstStateChangeReturn
+gst_uri_transcode_bin_change_state (GstElement * element,
+ GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ GstUriTranscodeBin *self = GST_URI_TRANSCODE_BIN (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+
+ if (!make_dest (self))
+ goto setup_failed;
+
+ if (!make_transcodebin (self))
+ goto setup_failed;
+
+ if (!make_source (self))
+ goto setup_failed;
+
+ if (gst_element_set_state (self->sink,
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (self,
+ "Could not set %" GST_PTR_FORMAT " state to PAUSED", self->sink);
+ goto setup_failed;
+ }
+
+ if (gst_element_set_state (self->transcodebin,
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (self,
+ "Could not set %" GST_PTR_FORMAT " state to PAUSED",
+ self->transcodebin);
+ goto setup_failed;
+ }
+
+ if (gst_element_set_state (self->src,
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (self,
+ "Could not set %" GST_PTR_FORMAT " state to PAUSED", self->src);
+ goto setup_failed;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto beach;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ remove_all_children (self);
+ break;
+ default:
+ break;
+ }
+
+beach:
+ return ret;
+
+setup_failed:
+ remove_all_children (self);
+ return GST_STATE_CHANGE_FAILURE;
+}
+
+static void
+gst_uri_transcode_bin_constructed (GObject * object)
+{
+#if HAVE_GETRUSAGE
+ GstUriTranscodeBin *self = GST_URI_TRANSCODE_BIN (object);
+
+ self->cpu_clock =
+ GST_CLOCK (gst_cpu_throttling_clock_new (self->wanted_cpu_usage));
+ gst_pipeline_use_clock (GST_PIPELINE (self), self->cpu_clock);
+#endif
+
+ ((GObjectClass *) parent_class)->constructed (object);
+}
+
+static void
+gst_uri_transcode_bin_dispose (GObject * object)
+{
+ GstUriTranscodeBin *self = (GstUriTranscodeBin *) object;
+
+ g_clear_object (&self->video_filter);
+ g_clear_object (&self->audio_filter);
+ g_clear_object (&self->cpu_clock);
+
+ G_OBJECT_CLASS (gst_uri_transcode_bin_parent_class)->dispose (object);
+}
+
+static void
+gst_uri_transcode_bin_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ GstUriTranscodeBin *self = GST_URI_TRANSCODE_BIN (object);
+
+ switch (prop_id) {
+ case PROP_PROFILE:
+ GST_OBJECT_LOCK (self);
+ g_value_set_object (value, self->profile);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_DEST_URI:
+ GST_OBJECT_LOCK (self);
+ g_value_set_string (value, self->dest_uri);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_SOURCE_URI:
+ GST_OBJECT_LOCK (self);
+ g_value_set_string (value, self->source_uri);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_AVOID_REENCODING:
+ GST_OBJECT_LOCK (self);
+ g_value_set_boolean (value, self->avoid_reencoding);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_CPU_USAGE:
+ GST_OBJECT_LOCK (self);
+ g_value_set_uint (value, self->wanted_cpu_usage);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_VIDEO_FILTER:
+ GST_OBJECT_LOCK (self);
+ g_value_set_object (value, self->video_filter);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_AUDIO_FILTER:
+ GST_OBJECT_LOCK (self);
+ g_value_set_object (value, self->audio_filter);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gst_uri_transcode_bin_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ GstUriTranscodeBin *self = GST_URI_TRANSCODE_BIN (object);
+
+ switch (prop_id) {
+ case PROP_PROFILE:
+ GST_OBJECT_LOCK (self);
+ self->profile = g_value_dup_object (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_DEST_URI:
+ GST_OBJECT_LOCK (self);
+ g_free (self->dest_uri);
+ self->dest_uri = g_value_dup_string (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_SOURCE_URI:
+ GST_OBJECT_LOCK (self);
+ g_free (self->source_uri);
+ self->source_uri = g_value_dup_string (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_AVOID_REENCODING:
+ GST_OBJECT_LOCK (self);
+ self->avoid_reencoding = g_value_get_boolean (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_CPU_USAGE:
+#if HAVE_GETRUSAGE
+ GST_OBJECT_LOCK (self);
+ self->wanted_cpu_usage = g_value_get_uint (value);
+ g_object_set (self->cpu_clock, "cpu-usage", self->wanted_cpu_usage, NULL);
+ GST_OBJECT_UNLOCK (self);
+#else
+ GST_ERROR_OBJECT (self,
+ "No CPU usage throttling support for that platform");
+#endif
+ break;
+ case PROP_AUDIO_FILTER:
+ GST_OBJECT_LOCK (self);
+ self->audio_filter = g_value_dup_object (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_VIDEO_FILTER:
+ GST_OBJECT_LOCK (self);
+ self->video_filter = g_value_dup_object (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gst_uri_transcode_bin_class_init (GstUriTranscodeBinClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_klass;
+
+ object_class->get_property = gst_uri_transcode_bin_get_property;
+ object_class->set_property = gst_uri_transcode_bin_set_property;
+ object_class->constructed = gst_uri_transcode_bin_constructed;
+ object_class->dispose = gst_uri_transcode_bin_dispose;
+
+ gstelement_klass = (GstElementClass *) klass;
+ gstelement_klass->change_state =
+ GST_DEBUG_FUNCPTR (gst_uri_transcode_bin_change_state);
+
+ GST_DEBUG_CATEGORY_INIT (gst_uri_transcodebin_debug, "uritranscodebin", 0,
+ "UriTranscodebin element");
+
+ gst_element_class_set_static_metadata (gstelement_klass,
+ "URITranscode Bin", "Generic/Bin/Encoding",
+ "Autoplug and transcoder media from uris",
+ "Thibault Saunier <tsaunier@igalia.com>");
+
+ /**
+ * GstUriTranscodeBin:profile:
+ *
+ * The #GstEncodingProfile to use. This property must be set before going
+ * to %GST_STATE_PAUSED or higher.
+ */
+ g_object_class_install_property (object_class, PROP_PROFILE,
+ g_param_spec_object ("profile", "Profile",
+ "The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstUriTranscodeBin:source-uri:
+ *
+ * The URI of the stream to encode
+ */
+ g_object_class_install_property (object_class, PROP_SOURCE_URI,
+ g_param_spec_string ("source-uri", "Source URI", "URI to decode",
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstUriTranscodeBin:dest-uri:
+ *
+ * The destination URI to which the stream should be encoded.
+ */
+ g_object_class_install_property (object_class, PROP_DEST_URI,
+ g_param_spec_string ("dest-uri", "URI", "URI to put output stream",
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstUriTranscodeBin:avoid-reencoding:
+ *
+ * See #encodebin:avoid-reencoding
+ */
+ g_object_class_install_property (object_class, PROP_AVOID_REENCODING,
+ g_param_spec_boolean ("avoid-reencoding", "Avoid re-encoding",
+ "Whether to re-encode portions of compatible video streams that lay on segment boundaries",
+ DEFAULT_AVOID_REENCODING,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, 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));
+
+ /**
+ * GstUriTranscodeBin:video-filter:
+ *
+ * Set the video filter element/bin to use.
+ */
+ g_object_class_install_property (object_class, PROP_VIDEO_FILTER,
+ g_param_spec_object ("video-filter", "Video filter",
+ "the video filter(s) to apply, if possible",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstUriTranscodeBin:audio-filter:
+ *
+ * Set the audio filter element/bin to use.
+ */
+ g_object_class_install_property (object_class, PROP_AUDIO_FILTER,
+ g_param_spec_object ("audio-filter", "Audio filter",
+ "the audio filter(s) to apply, if possible",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gst_uri_transcode_bin_init (GstUriTranscodeBin * self)
+{
+ self->wanted_cpu_usage = 100;
+}
diff --git a/gst/transcode/meson.build b/gst/transcode/meson.build
new file mode 100644
index 000000000..e3814b06e
--- /dev/null
+++ b/gst/transcode/meson.build
@@ -0,0 +1,13 @@
+gsttranscoder_plugin = library('gsttranscode',
+ 'gsttranscodebin.c',
+ 'gst-cpu-throttling-clock.c',
+ 'gsturitranscodebin.c',
+ install : true,
+ dependencies : [gst_dep, gstpbutils_dep],
+ c_args : gst_plugins_bad_args,
+ include_directories : [configinc],
+ install_dir : plugins_install_dir,
+)
+
+pkgconfig.generate(gsttranscoder_plugin, install_dir : plugins_pkgconfig_install_dir)
+plugins += [gsttranscoder_plugin] \ No newline at end of file