From bd3ce28716793bc54d0d5ed59321ddee6bb7b07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Tue, 12 Nov 2024 22:22:24 +0100 Subject: [PATCH] clapper: Ability to request adaptive bitrates Add APIs to allow apps to select start, min and max bitrates for adaptive streaming. Combining min+max values can allow to implement a video quality selector, although possible bitrates/qualities are not communicated with an app yet. --- src/lib/clapper/clapper-player-private.h | 6 + src/lib/clapper/clapper-player.c | 263 ++++++++++++++++++++++- src/lib/clapper/clapper-player.h | 18 ++ 3 files changed, 285 insertions(+), 2 deletions(-) diff --git a/src/lib/clapper/clapper-player-private.h b/src/lib/clapper/clapper-player-private.h index b29f64dc..68f7d07d 100644 --- a/src/lib/clapper/clapper-player-private.h +++ b/src/lib/clapper/clapper-player-private.h @@ -87,6 +87,9 @@ struct _ClapperPlayer gboolean pending_eos; // when pausing due to EOS gint eos; // atomic integer + /* Set adaptive props immediately */ + GstElement *adaptive_demuxer; + /* Playbin2 compat */ gint n_video, n_audio, n_text; @@ -104,6 +107,9 @@ struct _ClapperPlayer gboolean subtitles_enabled; gchar *download_dir; gboolean download_enabled; + guint start_bitrate; + guint min_bitrate; + guint max_bitrate; gdouble audio_offset; gdouble subtitle_offset; }; diff --git a/src/lib/clapper/clapper-player.c b/src/lib/clapper/clapper-player.c index dc15c258..31879a7d 100644 --- a/src/lib/clapper/clapper-player.c +++ b/src/lib/clapper/clapper-player.c @@ -62,6 +62,7 @@ #define DEFAULT_AUDIO_ENABLED TRUE #define DEFAULT_SUBTITLES_ENABLED TRUE #define DEFAULT_DOWNLOAD_ENABLED FALSE +#define DEFAULT_ADAPTIVE_START_BITRATE 1600000 #define GST_CAT_DEFAULT clapper_player_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); @@ -93,6 +94,9 @@ enum PROP_SUBTITLES_ENABLED, PROP_DOWNLOAD_DIR, PROP_DOWNLOAD_ENABLED, + PROP_ADAPTIVE_START_BITRATE, + PROP_ADAPTIVE_MIN_BITRATE, + PROP_ADAPTIVE_MAX_BITRATE, PROP_AUDIO_OFFSET, PROP_SUBTITLE_OFFSET, PROP_SUBTITLE_FONT_DESC, @@ -727,6 +731,8 @@ clapper_player_reset (ClapperPlayer *self, gboolean pending_dispose) gst_clear_object (&self->audio_decoder); } + gst_clear_object (&self->adaptive_demuxer); + GST_OBJECT_UNLOCK (self); gst_clear_tag_list (&self->pending_tags); @@ -768,13 +774,15 @@ static void _element_setup_cb (GstElement *playbin, GstElement *element, ClapperPlayer *self) { GstElementFactory *factory = gst_element_get_factory (element); + const gchar *factory_name; if (G_UNLIKELY (factory == NULL)) return; - GST_INFO_OBJECT (self, "Element setup: %s", GST_OBJECT_NAME (factory)); + factory_name = g_intern_static_string (GST_OBJECT_NAME (factory)); + GST_INFO_OBJECT (self, "Element setup: %s", factory_name); - if (strcmp (GST_OBJECT_NAME (factory), "downloadbuffer") == 0) { + if (factory_name == g_intern_static_string ("downloadbuffer")) { gchar *download_template; /* Only set props if we have download template */ @@ -785,6 +793,27 @@ _element_setup_cb (GstElement *playbin, GstElement *element, ClapperPlayer *self NULL); g_free (download_template); } + } else if (factory_name == g_intern_static_string ("dashdemux2") + || factory_name == g_intern_static_string ("hlsdemux2")) { + guint start_bitrate, min_bitrate, max_bitrate; + + GST_OBJECT_LOCK (self); + + start_bitrate = self->start_bitrate; + min_bitrate = self->min_bitrate; + max_bitrate = self->max_bitrate; + + gst_object_replace ((GstObject **) &self->adaptive_demuxer, GST_OBJECT_CAST (element)); + + GST_OBJECT_UNLOCK (self); + + g_object_set (element, + "low-watermark-time", 3 * GST_SECOND, + "high-watermark-time", 10 * GST_SECOND, + "start-bitrate", start_bitrate, + "min-bitrate", min_bitrate, + "max-bitrate", max_bitrate, + NULL); } } @@ -1668,6 +1697,168 @@ clapper_player_get_download_enabled (ClapperPlayer *self) return enabled; } +static void +_set_adaptive_bitrate (ClapperPlayer *self, guint *internal_ptr, + const gchar *prop_name, guint bitrate, GParamSpec *pspec) +{ + GstElement *element = NULL; + gboolean changed; + + if (!self->use_playbin3) { + GST_WARNING_OBJECT (self, "Setting adaptive-%s when using playbin2" + " has no effect", prop_name); + } + + GST_OBJECT_LOCK (self); + if ((changed = (*internal_ptr != bitrate))) { + *internal_ptr = bitrate; + + if (self->adaptive_demuxer) + element = gst_object_ref (self->adaptive_demuxer); + } + GST_OBJECT_UNLOCK (self); + + if (changed) { + GST_INFO_OBJECT (self, "Set adaptive-%s: %u", prop_name, bitrate); + + if (element) + g_object_set (element, prop_name, bitrate, NULL); + + clapper_app_bus_post_prop_notify (self->app_bus, GST_OBJECT_CAST (self), pspec); + } + + gst_clear_object (&element); +} + +/** + * clapper_player_set_adaptive_start_bitrate: + * @player: a #ClapperPlayer + * @bitrate: a bitrate to set (bits/s) + * + * Set initial bitrate to select when starting adaptive + * streaming such as DASH or HLS. + * + * Since: 0.8 + */ +void +clapper_player_set_adaptive_start_bitrate (ClapperPlayer *self, guint bitrate) +{ + g_return_if_fail (CLAPPER_IS_PLAYER (self)); + + _set_adaptive_bitrate (self, &self->start_bitrate, + "start-bitrate", bitrate, param_specs[PROP_ADAPTIVE_START_BITRATE]); +} + +/** + * clapper_player_get_adaptive_start_bitrate: + * @player: a #ClapperPlayer + * + * Get currently set initial bitrate (bits/s) for adaptive streaming. + * + * Returns: the start bitrate value. + * + * Since: 0.8 + */ +guint +clapper_player_get_adaptive_start_bitrate (ClapperPlayer *self) +{ + guint bitrate; + + g_return_val_if_fail (CLAPPER_IS_PLAYER (self), 0); + + GST_OBJECT_LOCK (self); + bitrate = self->start_bitrate; + GST_OBJECT_UNLOCK (self); + + return bitrate; +} + +/** + * clapper_player_set_adaptive_min_bitrate: + * @player: a #ClapperPlayer + * @bitrate: a bitrate to set (bits/s) + * + * Set minimal bitrate to select for adaptive streaming + * such as DASH or HLS. + * + * Since: 0.8 + */ +void +clapper_player_set_adaptive_min_bitrate (ClapperPlayer *self, guint bitrate) +{ + g_return_if_fail (CLAPPER_IS_PLAYER (self)); + + _set_adaptive_bitrate (self, &self->min_bitrate, + "min-bitrate", bitrate, param_specs[PROP_ADAPTIVE_MIN_BITRATE]); +} + +/** + * clapper_player_get_adaptive_min_bitrate: + * @player: a #ClapperPlayer + * + * Get currently set minimal bitrate (bits/s) for adaptive streaming. + * + * Returns: the minimal bitrate value. + * + * Since: 0.8 + */ +guint +clapper_player_get_adaptive_min_bitrate (ClapperPlayer *self) +{ + guint bitrate; + + g_return_val_if_fail (CLAPPER_IS_PLAYER (self), 0); + + GST_OBJECT_LOCK (self); + bitrate = self->min_bitrate; + GST_OBJECT_UNLOCK (self); + + return bitrate; +} + +/** + * clapper_player_set_adaptive_max_bitrate: + * @player: a #ClapperPlayer + * @bitrate: a bitrate to set (bits/s) + * + * Set maximal bitrate to select for adaptive streaming + * such as DASH or HLS. + * + * Since: 0.8 + */ +void +clapper_player_set_adaptive_max_bitrate (ClapperPlayer *self, guint bitrate) +{ + g_return_if_fail (CLAPPER_IS_PLAYER (self)); + + _set_adaptive_bitrate (self, &self->max_bitrate, + "max-bitrate", bitrate, param_specs[PROP_ADAPTIVE_MAX_BITRATE]); +} + +/** + * clapper_player_get_adaptive_max_bitrate: + * @player: a #ClapperPlayer + * + * Get currently set maximal bitrate (bits/s) for adaptive streaming. + * + * Returns: the maximal bitrate value. + * + * Since: 0.8 + */ +guint +clapper_player_get_adaptive_max_bitrate (ClapperPlayer *self) +{ + guint bitrate; + + g_return_val_if_fail (CLAPPER_IS_PLAYER (self), 0); + + GST_OBJECT_LOCK (self); + bitrate = self->max_bitrate; + GST_OBJECT_UNLOCK (self); + + return bitrate; +} + /** * clapper_player_set_audio_offset: * @player: a #ClapperPlayer @@ -2031,6 +2222,7 @@ clapper_player_init (ClapperPlayer *self) self->audio_enabled = DEFAULT_AUDIO_ENABLED; self->subtitles_enabled = DEFAULT_SUBTITLES_ENABLED; self->download_enabled = DEFAULT_DOWNLOAD_ENABLED; + self->start_bitrate = DEFAULT_ADAPTIVE_START_BITRATE; } static void @@ -2164,6 +2356,15 @@ clapper_player_get_property (GObject *object, guint prop_id, case PROP_DOWNLOAD_ENABLED: g_value_set_boolean (value, clapper_player_get_download_enabled (self)); break; + case PROP_ADAPTIVE_START_BITRATE: + g_value_set_uint (value, clapper_player_get_adaptive_start_bitrate (self)); + break; + case PROP_ADAPTIVE_MIN_BITRATE: + g_value_set_uint (value, clapper_player_get_adaptive_min_bitrate (self)); + break; + case PROP_ADAPTIVE_MAX_BITRATE: + g_value_set_uint (value, clapper_player_get_adaptive_max_bitrate (self)); + break; case PROP_AUDIO_OFFSET: g_value_set_double (value, clapper_player_get_audio_offset (self)); break; @@ -2225,6 +2426,15 @@ clapper_player_set_property (GObject *object, guint prop_id, case PROP_DOWNLOAD_ENABLED: clapper_player_set_download_enabled (self, g_value_get_boolean (value)); break; + case PROP_ADAPTIVE_START_BITRATE: + clapper_player_set_adaptive_start_bitrate (self, g_value_get_uint (value)); + break; + case PROP_ADAPTIVE_MIN_BITRATE: + clapper_player_set_adaptive_min_bitrate (self, g_value_get_uint (value)); + break; + case PROP_ADAPTIVE_MAX_BITRATE: + clapper_player_set_adaptive_max_bitrate (self, g_value_get_uint (value)); + break; case PROP_AUDIO_OFFSET: clapper_player_set_audio_offset (self, g_value_get_double (value)); break; @@ -2476,6 +2686,55 @@ clapper_player_class_init (ClapperPlayerClass *klass) NULL, NULL, DEFAULT_DOWNLOAD_ENABLED, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /** + * ClapperPlayer:adaptive-start-bitrate: + * + * An initial bitrate (bits/s) to select during + * starting adaptive streaming such as DASH or HLS. + * + * If value is higher than lowest available bitrate in streaming + * manifest, then lowest possible bitrate will be selected. + * + * Since: 0.8 + */ + param_specs[PROP_ADAPTIVE_START_BITRATE] = g_param_spec_uint ("adaptive-start-bitrate", + NULL, NULL, 0, G_MAXUINT, DEFAULT_ADAPTIVE_START_BITRATE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * ClapperPlayer:adaptive-min-bitrate: + * + * A minimal allowed bitrate (bits/s) during adaptive streaming + * such as DASH or HLS. + * + * Setting this will prevent streaming from entering lower qualities + * (even when connection speed cannot keep up). When set together with + * [property@Clapper.Player:adaptive-max-bitrate] it can be used to + * enforce some specific quality. + * + * Since: 0.8 + */ + param_specs[PROP_ADAPTIVE_MIN_BITRATE] = g_param_spec_uint ("adaptive-min-bitrate", + NULL, NULL, 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * ClapperPlayer:adaptive-max-bitrate: + * + * A maximal allowed bitrate (bits/s) during adaptive streaming + * such as DASH or HLS (`0` for unlimited). + * + * Setting this will prevent streaming from entering qualities with + * higher bandwidth than the one set. When set together with + * [property@Clapper.Player:adaptive-min-bitrate] it can be used to + * enforce some specific quality. + * + * Since: 0.8 + */ + param_specs[PROP_ADAPTIVE_MAX_BITRATE] = g_param_spec_uint ("adaptive-max-bitrate", + NULL, NULL, 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /** * ClapperPlayer:audio-offset: * diff --git a/src/lib/clapper/clapper-player.h b/src/lib/clapper/clapper-player.h index 1c14c453..48650124 100644 --- a/src/lib/clapper/clapper-player.h +++ b/src/lib/clapper/clapper-player.h @@ -147,6 +147,24 @@ void clapper_player_set_download_enabled (ClapperPlayer *player, gboolean enable CLAPPER_API gboolean clapper_player_get_download_enabled (ClapperPlayer *player); +CLAPPER_API +void clapper_player_set_adaptive_start_bitrate (ClapperPlayer *player, guint bitrate); + +CLAPPER_API +guint clapper_player_get_adaptive_start_bitrate (ClapperPlayer *player); + +CLAPPER_API +void clapper_player_set_adaptive_min_bitrate (ClapperPlayer *player, guint bitrate); + +CLAPPER_API +guint clapper_player_get_adaptive_min_bitrate (ClapperPlayer *player); + +CLAPPER_API +void clapper_player_set_adaptive_max_bitrate (ClapperPlayer *player, guint bitrate); + +CLAPPER_API +guint clapper_player_get_adaptive_max_bitrate (ClapperPlayer *player); + CLAPPER_API void clapper_player_set_audio_offset (ClapperPlayer *player, gdouble offset);