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 1/8] 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); From ab659d0951f4cbc8c7f5304a5dcd215d5f530994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Wed, 13 Nov 2024 20:06:28 +0100 Subject: [PATCH 2/8] clapper: Use playbin3 by default New GStreamer adaptive demuxers work only within playbin3. In order to not introduce new APIs (setting adaptive bitrate) that do not work by default and to have the whole "clapper-enhancers" concept working correctly, its about time to move on to playbin3. --- src/lib/clapper/clapper-player.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clapper/clapper-player.c b/src/lib/clapper/clapper-player.c index 31879a7d..8649ef7e 100644 --- a/src/lib/clapper/clapper-player.c +++ b/src/lib/clapper/clapper-player.c @@ -2133,7 +2133,7 @@ clapper_player_thread_start (ClapperThreadedObject *threaded_object) if (!(env = g_getenv ("CLAPPER_USE_PLAYBIN3"))) // Clapper override env = g_getenv ("GST_CLAPPER_USE_PLAYBIN3"); // compat - self->use_playbin3 = (env && g_str_has_prefix (env, "1")); + self->use_playbin3 = (!env || g_str_has_prefix (env, "1")); playbin_str = (self->use_playbin3) ? "playbin3" : "playbin"; if (!(self->playbin = gst_element_factory_make (playbin_str, NULL))) { From 9fbba4d66aebbda551e562185cef12ea4119d2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Wed, 13 Nov 2024 20:16:56 +0100 Subject: [PATCH 3/8] meson: Bump min required GStreamer version Setting "start-bitrate" property requires at least GStreamer 1.24. We do not want to introduce APIs that work only if user has a certain dependency version, thus this bump instead of version check. Related work on GStreamer side: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3894 https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3895 Since these were contributed to GStreamer almost 2 years ago with the intention of using them in Clapper, I think its about time to start do so. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 377fe254..1d5298f3 100644 --- a/meson.build +++ b/meson.build @@ -9,7 +9,7 @@ project('clapper', 'c', ) glib_req = '>= 2.76.0' -gst_req = '>= 1.20.0' +gst_req = '>= 1.24.0' gtk4_req = '>= 4.10.0' adw_req = '>= 1.4.0' From a065b818463fdfe7ee008fa22b7323928024dd6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Thu, 14 Nov 2024 22:46:50 +0100 Subject: [PATCH 4/8] flatpak: Add GStreamer 1.24 for testing We do not yet have GStreamer 1.24 in Flathub repo, so for testing purposes add it here temporarily --- pkgs/flatpak/com.github.rafostar.Clapper.json | 5 +- pkgs/flatpak/testing/gstreamer_stable.json | 99 +++++++++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 pkgs/flatpak/testing/gstreamer_stable.json diff --git a/pkgs/flatpak/com.github.rafostar.Clapper.json b/pkgs/flatpak/com.github.rafostar.Clapper.json index 44d7611a..5dbb253f 100644 --- a/pkgs/flatpak/com.github.rafostar.Clapper.json +++ b/pkgs/flatpak/com.github.rafostar.Clapper.json @@ -40,15 +40,12 @@ "flathub/lib/libass.json", "flathub/lib/uchardet.json", "flathub/lib/libmicrodns.json", - "flathub/gstreamer-1.0/gstreamer.json", + "testing/gstreamer_stable.json", "testing/yt-dlp.json", "testing/libpeas.json", { "name": "clapper", "buildsystem": "meson", - "config-opts": [ - "-Dc_args=\"-DHAVE_GST_PATCHES=1\"" - ], "sources": [ { "type": "dir", diff --git a/pkgs/flatpak/testing/gstreamer_stable.json b/pkgs/flatpak/testing/gstreamer_stable.json new file mode 100644 index 00000000..a9140864 --- /dev/null +++ b/pkgs/flatpak/testing/gstreamer_stable.json @@ -0,0 +1,99 @@ +{ + "name": "gstreamer", + "buildsystem": "meson", + "config-opts": [ + "--buildtype=release", + "--wrap-mode=nodownload", + + "-Dbase=enabled", + "-Dgood=enabled", + "-Dbad=enabled", + "-Dugly=enabled", + "-Dlibav=enabled", + "-Dvaapi=enabled", + "-Dsharp=disabled", + "-Drs=disabled", + "-Dpython=disabled", + "-Ddevtools=disabled", + "-Dges=disabled", + "-Drtsp_server=disabled", + "-Dgst-examples=disabled", + "-Dqt5=disabled", + "-Dtests=disabled", + "-Dexamples=disabled", + "-Dintrospection=enabled", + "-Ddoc=disabled", + "-Dgtk_doc=disabled", + "-Dgpl=enabled", + + "-Dgstreamer:benchmarks=disabled", + "-Dgstreamer:gobject-cast-checks=disabled", + "-Dgstreamer:glib-asserts=disabled", + "-Dgstreamer:glib-checks=disabled", + "-Dgstreamer:extra-checks=disabled", + + "-Dgst-plugins-base:gobject-cast-checks=disabled", + "-Dgst-plugins-base:glib-asserts=disabled", + "-Dgst-plugins-base:glib-checks=disabled", + "-Dgst-plugins-base:gl_api=opengl,gles2", + "-Dgst-plugins-base:gl_platform=egl,glx", + + "-Dgst-plugins-good:gobject-cast-checks=disabled", + "-Dgst-plugins-good:glib-asserts=disabled", + "-Dgst-plugins-good:glib-checks=disabled", + "-Dgst-plugins-good:gtk3=disabled", + + "-Dgst-plugins-bad:gobject-cast-checks=disabled", + "-Dgst-plugins-bad:glib-asserts=disabled", + "-Dgst-plugins-bad:glib-checks=disabled", + "-Dgst-plugins-bad:extra-checks=disabled", + "-Dgst-plugins-bad:vulkan=disabled", + "-Dgst-plugins-bad:webrtc=disabled", + "-Dgst-plugins-bad:wasapi=disabled", + "-Dgst-plugins-bad:wasapi2=disabled", + "-Dgst-plugins-bad:winks=disabled", + "-Dgst-plugins-bad:winscreencap=disabled", + "-Dgst-plugins-bad:assrender=enabled", + "-Dgst-plugins-bad:nvcodec=enabled", + "-Dgst-plugins-bad:v4l2codecs=enabled", + "-Dgst-plugins-bad:va=enabled", + + "-Dgst-plugins-ugly:gobject-cast-checks=disabled", + "-Dgst-plugins-ugly:glib-asserts=disabled", + "-Dgst-plugins-ugly:glib-checks=disabled", + "-Dgst-plugins-ugly:mpeg2dec=enabled" + ], + "sources": [ + { + "type": "git", + "url": "https://gitlab.freedesktop.org/gstreamer/gstreamer.git", + "tag": "1.24.9", + "commit": "b309f90bfde36e6d175b70bfa0c941f2829dd6a5", + "disable-submodules": true + }, + { + "type": "patch", + "path": "../flathub/gstreamer-1.0/gst-libav-stop-caching-codecs.patch" + }, + { + "type": "patch", + "path": "../flathub/gstreamer-1.0/gst-plugins-base-autodetect-subtitle-text-encoding.patch" + }, + { + "type": "patch", + "path": "../flathub/gstreamer-1.0/gst-plugins-good-matroska-fix-attachments-detection.patch" + }, + { + "type": "patch", + "path": "../flathub/gstreamer-1.0/gst-plugins-good-dashdemux2-play-last-subfragment.patch" + }, + { + "type": "patch", + "path": "../flathub/gstreamer-1.0/gst-plugins-bad-dashdemux-sidx-range-download.patch" + }, + { + "type": "patch", + "path": "../flathub/gstreamer-1.0/gst-plugins-bad-dashdemux-improve-initial-representation-selection.patch" + } + ] +} From aa819cf2d350398f5797c13c341215f3e35bb654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Thu, 14 Nov 2024 23:19:38 +0100 Subject: [PATCH 5/8] flatpak: Update runtime to 47 --- pkgs/flatpak/com.github.rafostar.Clapper.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/flatpak/com.github.rafostar.Clapper.json b/pkgs/flatpak/com.github.rafostar.Clapper.json index 5dbb253f..d18c46c5 100644 --- a/pkgs/flatpak/com.github.rafostar.Clapper.json +++ b/pkgs/flatpak/com.github.rafostar.Clapper.json @@ -1,11 +1,11 @@ { "app-id": "com.github.rafostar.Clapper", "runtime": "org.gnome.Platform", - "runtime-version": "45", + "runtime-version": "47", "sdk": "org.gnome.Sdk", "add-extensions": { "org.freedesktop.Platform.ffmpeg-full": { - "version": "23.08", + "version": "24.08", "directory": "lib/ffmpeg", "add-ld-path": ".", "no-autodownload": false, From e4045b98b32a33f7a690f5a0b768737a0c558470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Fri, 15 Nov 2024 21:54:04 +0100 Subject: [PATCH 6/8] clapper: Add "adaptive-bandwidth" readable property Apps can use it to determine and set an optimal value for start bitrate instead of starting at some constant value. --- src/lib/clapper/clapper-player-private.h | 1 + src/lib/clapper/clapper-player.c | 83 +++++++++++++++++++++++- src/lib/clapper/clapper-player.h | 3 + 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/lib/clapper/clapper-player-private.h b/src/lib/clapper/clapper-player-private.h index 68f7d07d..43116cd2 100644 --- a/src/lib/clapper/clapper-player-private.h +++ b/src/lib/clapper/clapper-player-private.h @@ -110,6 +110,7 @@ struct _ClapperPlayer guint start_bitrate; guint min_bitrate; guint max_bitrate; + guint bandwidth; gdouble audio_offset; gdouble subtitle_offset; }; diff --git a/src/lib/clapper/clapper-player.c b/src/lib/clapper/clapper-player.c index 8649ef7e..de1728f8 100644 --- a/src/lib/clapper/clapper-player.c +++ b/src/lib/clapper/clapper-player.c @@ -97,6 +97,7 @@ enum PROP_ADAPTIVE_START_BITRATE, PROP_ADAPTIVE_MIN_BITRATE, PROP_ADAPTIVE_MAX_BITRATE, + PROP_ADAPTIVE_BANDWIDTH, PROP_AUDIO_OFFSET, PROP_SUBTITLE_OFFSET, PROP_SUBTITLE_FONT_DESC, @@ -716,6 +717,27 @@ clapper_player_playbin_update_current_decoders (ClapperPlayer *self) GST_DEBUG_OBJECT (self, "Active audio decoder not found"); } +static void +_adaptive_demuxer_bandwidth_changed_cb (GstElement *adaptive_demuxer, + GParamSpec *pspec G_GNUC_UNUSED, ClapperPlayer *self) +{ + guint bandwidth = 0; + gboolean changed; + + g_object_get (adaptive_demuxer, "current-bandwidth", &bandwidth, NULL); + + GST_OBJECT_LOCK (self); + if ((changed = bandwidth != self->bandwidth)) + self->bandwidth = bandwidth; + GST_OBJECT_UNLOCK (self); + + if (changed) { + GST_LOG_OBJECT (self, "Adaptive bandwidth: %u", bandwidth); + clapper_app_bus_post_prop_notify (self->app_bus, + GST_OBJECT_CAST (self), param_specs[PROP_ADAPTIVE_BANDWIDTH]); + } +} + void clapper_player_reset (ClapperPlayer *self, gboolean pending_dispose) { @@ -731,7 +753,11 @@ clapper_player_reset (ClapperPlayer *self, gboolean pending_dispose) gst_clear_object (&self->audio_decoder); } - gst_clear_object (&self->adaptive_demuxer); + if (self->adaptive_demuxer) { + g_signal_handlers_disconnect_by_func (self->adaptive_demuxer, + _adaptive_demuxer_bandwidth_changed_cb, self); + gst_clear_object (&self->adaptive_demuxer); + } GST_OBJECT_UNLOCK (self); @@ -803,8 +829,18 @@ _element_setup_cb (GstElement *playbin, GstElement *element, ClapperPlayer *self min_bitrate = self->min_bitrate; max_bitrate = self->max_bitrate; + if (self->adaptive_demuxer) { + g_signal_handlers_disconnect_by_func (self->adaptive_demuxer, + _adaptive_demuxer_bandwidth_changed_cb, self); + } + gst_object_replace ((GstObject **) &self->adaptive_demuxer, GST_OBJECT_CAST (element)); + if (self->adaptive_demuxer) { + g_signal_connect (self->adaptive_demuxer, "notify::current-bandwidth", + G_CALLBACK (_adaptive_demuxer_bandwidth_changed_cb), self); + } + GST_OBJECT_UNLOCK (self); g_object_set (element, @@ -1859,6 +1895,31 @@ clapper_player_get_adaptive_max_bitrate (ClapperPlayer *self) return bitrate; } +/** + * clapper_player_get_adaptive_bandwidth: + * @player: a #ClapperPlayer + * + * Get last fragment download bandwidth (bits/s) during + * adaptive streaming. + * + * Returns: the adaptive bandwidth. + * + * Since: 0.8 + */ +guint +clapper_player_get_adaptive_bandwidth (ClapperPlayer *self) +{ + guint bandwidth; + + g_return_val_if_fail (CLAPPER_IS_PLAYER (self), 0); + + GST_OBJECT_LOCK (self); + bandwidth = self->bandwidth; + GST_OBJECT_UNLOCK (self); + + return bandwidth; +} + /** * clapper_player_set_audio_offset: * @player: a #ClapperPlayer @@ -2365,6 +2426,9 @@ clapper_player_get_property (GObject *object, guint prop_id, case PROP_ADAPTIVE_MAX_BITRATE: g_value_set_uint (value, clapper_player_get_adaptive_max_bitrate (self)); break; + case PROP_ADAPTIVE_BANDWIDTH: + g_value_set_uint (value, clapper_player_get_adaptive_bandwidth (self)); + break; case PROP_AUDIO_OFFSET: g_value_set_double (value, clapper_player_get_audio_offset (self)); break; @@ -2735,6 +2799,23 @@ clapper_player_class_init (ClapperPlayerClass *klass) NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /** + * ClapperPlayer:adaptive-bandwidth: + * + * Last fragment download bandwidth (bits/s) during adaptive streaming. + * + * This property only changes when adaptive streaming and later stays + * at the last value until streaming some adaptive content again. + * + * Apps can use this to determine and set an optimal value for + * [property@Clapper.Player:adaptive-start-bitrate]. + * + * Since: 0.8 + */ + param_specs[PROP_ADAPTIVE_BANDWIDTH] = g_param_spec_uint ("adaptive-bandwidth", + NULL, NULL, 0, G_MAXUINT, 0, + G_PARAM_READABLE | 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 48650124..c47b1a78 100644 --- a/src/lib/clapper/clapper-player.h +++ b/src/lib/clapper/clapper-player.h @@ -165,6 +165,9 @@ void clapper_player_set_adaptive_max_bitrate (ClapperPlayer *player, guint bitra CLAPPER_API guint clapper_player_get_adaptive_max_bitrate (ClapperPlayer *player); +CLAPPER_API +guint clapper_player_get_adaptive_bandwidth (ClapperPlayer *player); + CLAPPER_API void clapper_player_set_audio_offset (ClapperPlayer *player, gdouble offset); From 22430620a82e773037b25226f84d42f909116f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Fri, 15 Nov 2024 23:18:38 +0100 Subject: [PATCH 7/8] clapper-app: Apply "adaptive-start-bitrate" on startup Watch for "adaptive-bandwidth" changes during adaptive streaming, use these to set "adaptive-start-bitrate" player property, so we do not always start streaming from some constant bitrate value which might not be the best for everyone. Additionally, store last value in GSettings on app exit and also add a command line arg to set this too. --- src/bin/clapper-app/clapper-app-application.c | 5 +++++ src/bin/clapper-app/clapper-app-window.c | 13 ++++++++++++- .../schemas/com.github.rafostar.Clapper.gschema.xml | 4 ++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/bin/clapper-app/clapper-app-application.c b/src/bin/clapper-app/clapper-app-application.c index 1232255a..703d8dec 100644 --- a/src/bin/clapper-app/clapper-app-application.c +++ b/src/bin/clapper-app/clapper-app-application.c @@ -144,6 +144,8 @@ clapper_app_apply_options_to_window (ClapperAppWindow *dest_window, GVariantDict clapper_player_set_mute (dest_player, option_bool); if (clapper_app_options_get ("speed", "d", options, src_player_obj, settings, &option_dbl)) clapper_player_set_speed (dest_player, PERCENTAGE_ROUND (CLAMP (option_dbl, 0.05, 2.0))); + if (clapper_app_options_get ("adaptive-start-bitrate", "i", options, src_player_obj, settings, &option_int)) + clapper_player_set_adaptive_start_bitrate (dest_player, option_int); if (clapper_app_options_get ("progression-mode", "i", options, src_queue_obj, settings, &option_int)) clapper_queue_set_progression_mode (clapper_player_get_queue (dest_player), CLAMP (option_int, 0, 4)); if (clapper_app_options_get ("subtitles-enabled", "b", NULL, src_player_obj, settings, &option_bool)) @@ -196,6 +198,8 @@ _store_settings_from_window (ClapperAppApplication *self, ClapperAppWindow *app_ g_settings_set_double (self->settings, "volume", clapper_player_get_volume (player)); g_settings_set_boolean (self->settings, "mute", clapper_player_get_mute (player)); g_settings_set_double (self->settings, "speed", clapper_player_get_speed (player)); + g_settings_set_int (self->settings, "adaptive-start-bitrate", + CLAMP (clapper_player_get_adaptive_bandwidth (player) * 0.8, 0, G_MAXINT)); g_settings_set_boolean (self->settings, "subtitles-enabled", clapper_player_get_subtitles_enabled (player)); g_settings_set_int (self->settings, "progression-mode", clapper_queue_get_progression_mode (queue)); @@ -676,6 +680,7 @@ clapper_app_application_constructed (GObject *object) { "enqueue", 0, 0, G_OPTION_ARG_NONE, NULL, _("Add media to queue in primary application instance"), NULL }, { "volume", 0, 0, G_OPTION_ARG_DOUBLE, NULL, _("Audio volume to set (0 - 2.0 range)"), NULL }, { "speed", 0, 0, G_OPTION_ARG_DOUBLE, NULL, _("Playback speed to set (0.05 - 2.0 range)"), NULL }, + { "adaptive-start-bitrate", 0, 0, G_OPTION_ARG_INT, NULL, _("Initial bitrate for adaptive streaming"), NULL }, { "progression-mode", 0, 0, G_OPTION_ARG_INT, NULL, _("Initial queue progression mode (0=none, 1=consecutive, 2=repeat-item, 3=carousel, 4=shuffle)"), NULL }, { "fullscreen", 'f', 0, G_OPTION_ARG_NONE, NULL, _("Set window to be fullscreen"), NULL }, { "video-filter", 0, 0, G_OPTION_ARG_STRING, NULL, _("Video filter to use (\"none\" to disable)"), NULL }, diff --git a/src/bin/clapper-app/clapper-app-window.c b/src/bin/clapper-app/clapper-app-window.c index c7d0dbdb..9157b1a2 100644 --- a/src/bin/clapper-app/clapper-app-window.c +++ b/src/bin/clapper-app/clapper-app-window.c @@ -151,6 +151,15 @@ _queue_current_item_changed_cb (ClapperQueue *queue, gst_clear_object (¤t_item); } +static void +_player_adaptive_bandwidth_changed_cb (ClapperPlayer *player, + GParamSpec *pspec G_GNUC_UNUSED, gpointer *user_data G_GNUC_UNUSED) +{ + /* Do not take whole bandwidth */ + clapper_player_set_adaptive_start_bitrate (player, + clapper_player_get_adaptive_bandwidth (player) * 0.8); +} + static gboolean _get_seek_method_mapping (GValue *value, GVariant *variant, gpointer user_data G_GNUC_UNUSED) @@ -1280,10 +1289,12 @@ clapper_app_window_constructed (GObject *object) clapper_player_set_autoplay (player, TRUE); - /* No need to also call this here, as item is selected + /* No need to also call these here, as they only change * after application window is contructed */ g_signal_connect (queue, "notify::current-item", G_CALLBACK (_queue_current_item_changed_cb), self); + g_signal_connect (player, "notify::adaptive-bandwidth", + G_CALLBACK (_player_adaptive_bandwidth_changed_cb), NULL); g_settings_bind (self->settings, "audio-offset", player, "audio-offset", G_SETTINGS_BIND_GET); diff --git a/src/bin/clapper-app/data/glib-2.0/schemas/com.github.rafostar.Clapper.gschema.xml b/src/bin/clapper-app/data/glib-2.0/schemas/com.github.rafostar.Clapper.gschema.xml index a73d1d55..9429f861 100644 --- a/src/bin/clapper-app/data/glib-2.0/schemas/com.github.rafostar.Clapper.gschema.xml +++ b/src/bin/clapper-app/data/glib-2.0/schemas/com.github.rafostar.Clapper.gschema.xml @@ -49,6 +49,10 @@ 1.0 Stores last speed value to apply on startup + + 1600000 + Stores initial adaptive streaming bitrate to apply on startup + 1 Stores last queue progression mode used to apply on startup From 303bda4d65253a0b27876d4d7d19d8d575acc7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Sat, 16 Nov 2024 15:25:01 +0100 Subject: [PATCH 8/8] clapper: Do not reset "adaptive-bandwidth" to zero When new instance of adaptive demuxer is created (different video is played) this prop value would become zero. We want to avoid that and instead report only bandwidth after fragment is downloaded, so when video will be unplayable, next one will not start from lowest quality. --- src/lib/clapper/clapper-player.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/clapper/clapper-player.c b/src/lib/clapper/clapper-player.c index de1728f8..52f4e22c 100644 --- a/src/lib/clapper/clapper-player.c +++ b/src/lib/clapper/clapper-player.c @@ -726,6 +726,11 @@ _adaptive_demuxer_bandwidth_changed_cb (GstElement *adaptive_demuxer, g_object_get (adaptive_demuxer, "current-bandwidth", &bandwidth, NULL); + /* Skip uncalculated bandwidth from + * new adaptive demuxer instance */ + if (bandwidth == 0) + return; + GST_OBJECT_LOCK (self); if ((changed = bandwidth != self->bandwidth)) self->bandwidth = bandwidth;