From 7a66b16d976468fcf72c2d1398fd637bdb4e348c Mon Sep 17 00:00:00 2001 From: Saunier Thibault Date: Thu, 3 Dec 2015 12:32:05 +0100 Subject: Import GstTranscoder --- gst/transcode/gst-cpu-throttling-clock.c | 220 +++++++++++ gst/transcode/gst-cpu-throttling-clock.h | 60 +++ gst/transcode/gsttranscodebin.c | 614 +++++++++++++++++++++++++++++++ gst/transcode/gsttranscoding.h | 31 ++ gst/transcode/gsturitranscodebin.c | 562 ++++++++++++++++++++++++++++ gst/transcode/meson.build | 13 + 6 files changed, 1500 insertions(+) create mode 100644 gst/transcode/gst-cpu-throttling-clock.c create mode 100644 gst/transcode/gst-cpu-throttling-clock.h create mode 100644 gst/transcode/gsttranscodebin.c create mode 100644 gst/transcode/gsttranscoding.h create mode 100644 gst/transcode/gsturitranscodebin.c create mode 100644 gst/transcode/meson.build (limited to 'gst/transcode') 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 +#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 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 + * + * 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 . + */ + + + +#ifndef __GST_CPU_THROTTLING_CLOCK_H__ +#define __GST_CPU_THROTTLING_CLOCK_H__ + +#include +#include + +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 +{ + /**/ + GstClockClass parent_class; +}; + +struct _GstCpuThrottlingClock +{ + /**/ + 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 + * + * 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 + +#include + +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 "); + + /** + * 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 + * + * 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 + +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 + * + * 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 + +#include + +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 "); + + /** + * 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 -- cgit v1.2.1