diff --git a/src/lib/clapper/clapper-player.c b/src/lib/clapper/clapper-player.c index 75b382e0..d01868c5 100644 --- a/src/lib/clapper/clapper-player.c +++ b/src/lib/clapper/clapper-player.c @@ -826,7 +826,8 @@ _element_setup_cb (GstElement *playbin, GstElement *element, ClapperPlayer *self factory_name = g_intern_static_string (GST_OBJECT_NAME (factory)); GST_INFO_OBJECT (self, "Element setup: %s", factory_name); - if (factory_name == g_intern_static_string ("clapperextractablesrc")) { + if (factory_name == g_intern_static_string ("clapperextractablesrc") + || factory_name == g_intern_static_string ("clapperplaylistdemux")) { g_object_set (element, "enhancer-proxies", self->enhancer_proxies, NULL); diff --git a/src/lib/clapper/gst/clapper-enhancer-director-private.h b/src/lib/clapper/gst/clapper-enhancer-director-private.h index 9386d12b..8bab2695 100644 --- a/src/lib/clapper/gst/clapper-enhancer-director-private.h +++ b/src/lib/clapper/gst/clapper-enhancer-director-private.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "../clapper-threaded-object.h" #include "../clapper-harvest.h" @@ -39,4 +40,7 @@ ClapperEnhancerDirector * clapper_enhancer_director_new (void); G_GNUC_INTERNAL ClapperHarvest * clapper_enhancer_director_extract (ClapperEnhancerDirector *director, GList *filtered_proxies, GUri *uri, GCancellable *cancellable, GError **error); +G_GNUC_INTERNAL +GListStore * clapper_enhancer_director_parse (ClapperEnhancerDirector *director, GList *filtered_proxies, GUri *uri, GstBuffer *buffer, GCancellable *cancellable, GError **error); + G_END_DECLS diff --git a/src/lib/clapper/gst/clapper-enhancer-director.c b/src/lib/clapper/gst/clapper-enhancer-director.c index 455df25f..17211bb0 100644 --- a/src/lib/clapper/gst/clapper-enhancer-director.c +++ b/src/lib/clapper/gst/clapper-enhancer-director.c @@ -25,7 +25,9 @@ #include "../clapper-cache-private.h" #include "../clapper-enhancer-proxy-private.h" #include "../clapper-extractable-private.h" +#include "../clapper-playlistable-private.h" #include "../clapper-harvest-private.h" +#include "../clapper-media-item.h" #include "../clapper-utils.h" #include "../../shared/clapper-shared-utils-private.h" @@ -53,6 +55,7 @@ typedef struct ClapperEnhancerDirector *director; GList *filtered_proxies; GUri *uri; + GstBuffer *buffer; GCancellable *cancellable; GError **error; } ClapperEnhancerDirectorData; @@ -99,7 +102,7 @@ clapper_enhancer_director_extract_in_thread (ClapperEnhancerDirectorData *data) success = clapper_extractable_extract (extractable, data->uri, harvest, data->cancellable, data->error); - gst_object_unref (extractable); + g_object_unref (extractable); /* We are done with extractable, but keep harvest and try to cache it */ if (success) { @@ -138,6 +141,101 @@ clapper_enhancer_director_extract_in_thread (ClapperEnhancerDirectorData *data) return harvest; } +static gpointer +clapper_enhancer_director_parse_in_thread (ClapperEnhancerDirectorData *data) +{ + ClapperEnhancerDirector *self = data->director; + GstMemory *mem; + GstMapInfo info; + GBytes *bytes; + GList *el; + GListStore *playlist = NULL; + gboolean success = FALSE; + + GST_DEBUG_OBJECT (self, "Parse start"); + + /* Cancelled during thread switching */ + if (g_cancellable_is_cancelled (data->cancellable)) + return NULL; + + GST_DEBUG_OBJECT (self, "Enhancer proxies for buffer: %u", + g_list_length (data->filtered_proxies)); + + mem = gst_buffer_peek_memory (data->buffer, 0); + if (!mem || !gst_memory_map (mem, &info, GST_MAP_READ)) { + g_set_error (data->error, GST_RESOURCE_ERROR, + GST_RESOURCE_ERROR_FAILED, "Could not read playlist buffer data"); + return NULL; + } + + bytes = g_bytes_new_static (info.data, info.size); + + for (el = data->filtered_proxies; el; el = g_list_next (el)) { + ClapperEnhancerProxy *proxy = CLAPPER_ENHANCER_PROXY_CAST (el->data); + ClapperPlaylistable *playlistable = NULL; + + if (g_cancellable_is_cancelled (data->cancellable)) // Check before loading enhancer + break; + +#if CLAPPER_WITH_ENHANCERS_LOADER + playlistable = CLAPPER_PLAYLISTABLE_CAST ( + clapper_enhancers_loader_create_enhancer (proxy, CLAPPER_TYPE_PLAYLISTABLE)); +#endif + + if (playlistable) { + GstStructure *config; + + if ((config = clapper_enhancer_proxy_make_current_config (proxy))) { + clapper_enhancer_proxy_apply_config_to_enhancer (proxy, config, (GObject *) playlistable); + gst_structure_free (config); + } + + if (g_cancellable_is_cancelled (data->cancellable)) { // Check before parse + g_object_unref (playlistable); + break; + } + + playlist = g_list_store_new (CLAPPER_TYPE_MEDIA_ITEM); // fresh list store for each iteration + + success = clapper_playlistable_parse (playlistable, data->uri, bytes, + playlist, data->cancellable, data->error); + g_object_unref (playlistable); + + /* We are done with playlistable, but keep playlist */ + if (success) + break; + + /* Cleanup to try again with next enhancer */ + g_clear_object (&playlist); + } + } + + /* Unref bytes, then unmap their data */ + g_bytes_unref (bytes); + gst_memory_unmap (mem, &info); + + /* Cancelled during parsing */ + if (g_cancellable_is_cancelled (data->cancellable)) + success = FALSE; + + if (!success) { + g_clear_object (&playlist); + + /* Ensure we have some error set on failure */ + if (*data->error == NULL) { + const gchar *err_msg = (g_cancellable_is_cancelled (data->cancellable)) + ? "Playlist parsing was cancelled" + : "Could not parse playlist"; + g_set_error (data->error, GST_RESOURCE_ERROR, + GST_RESOURCE_ERROR_FAILED, "%s", err_msg); + } + } + + GST_DEBUG_OBJECT (self, "Parse finish"); + + return playlist; +} + static inline void _harvest_delete_if_expired (ClapperEnhancerDirector *self, ClapperEnhancerProxy *proxy, GFile *file, const gint64 epoch_now) @@ -331,6 +429,7 @@ clapper_enhancer_director_extract (ClapperEnhancerDirector *self, data->director = self; data->filtered_proxies = filtered_proxies; data->uri = uri; + data->buffer = NULL; data->cancellable = cancellable; data->error = error; @@ -348,6 +447,31 @@ clapper_enhancer_director_extract (ClapperEnhancerDirector *self, return harvest; } +GListStore * +clapper_enhancer_director_parse (ClapperEnhancerDirector *self, + GList *filtered_proxies, GUri *uri, GstBuffer *buffer, + GCancellable *cancellable, GError **error) +{ + ClapperEnhancerDirectorData *data = g_new (ClapperEnhancerDirectorData, 1); + GMainContext *context; + GListStore *playlist; + + data->director = self; + data->filtered_proxies = filtered_proxies; + data->uri = uri; + data->buffer = buffer; + data->cancellable = cancellable; + data->error = error; + + context = clapper_threaded_object_get_context (CLAPPER_THREADED_OBJECT_CAST (self)); + + playlist = (GListStore *) clapper_shared_utils_context_invoke_sync_full (context, + (GThreadFunc) clapper_enhancer_director_parse_in_thread, + data, (GDestroyNotify) g_free); + + return playlist; +} + static void clapper_enhancer_director_thread_start (ClapperThreadedObject *threaded_object) { diff --git a/src/lib/clapper/gst/clapper-playlist-demux-private.h b/src/lib/clapper/gst/clapper-playlist-demux-private.h new file mode 100644 index 00000000..79bd17d2 --- /dev/null +++ b/src/lib/clapper/gst/clapper-playlist-demux-private.h @@ -0,0 +1,38 @@ +/* Clapper Playback Library + * Copyright (C) 2025 Rafał Dzięgiel + * + * 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 + * . + */ + +#pragma once + +#include +#include +#include + +#include "clapper-uri-base-demux-private.h" + +G_BEGIN_DECLS + +#define CLAPPER_TYPE_PLAYLIST_DEMUX (clapper_playlist_demux_get_type()) +#define CLAPPER_PLAYLIST_DEMUX_CAST(obj) ((ClapperPlaylistDemux *)(obj)) + +G_GNUC_INTERNAL +G_DECLARE_FINAL_TYPE (ClapperPlaylistDemux, clapper_playlist_demux, CLAPPER, PLAYLIST_DEMUX, ClapperUriBaseDemux) + +GST_TYPE_FIND_REGISTER_DECLARE (clapperplaylistdemux) +GST_ELEMENT_REGISTER_DECLARE (clapperplaylistdemux) + +G_END_DECLS diff --git a/src/lib/clapper/gst/clapper-playlist-demux.c b/src/lib/clapper/gst/clapper-playlist-demux.c new file mode 100644 index 00000000..cf617117 --- /dev/null +++ b/src/lib/clapper/gst/clapper-playlist-demux.c @@ -0,0 +1,383 @@ +/* Clapper Playback Library + * Copyright (C) 2025 Rafał Dzięgiel + * + * 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 + * . + */ + +#include "clapper-playlist-demux-private.h" +#include "clapper-enhancer-director-private.h" + +#include "../clapper-basic-functions.h" +#include "../clapper-enhancer-proxy.h" +#include "../clapper-enhancer-proxy-list.h" +#include "../clapper-media-item.h" +#include "../clapper-playlistable.h" + +#define CLAPPER_PLAYLIST_MEDIA_TYPE "application/clapper-playlist" +#define DATA_CHUNK_SIZE 4096 + +#define GST_CAT_DEFAULT clapper_playlist_demux_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +struct _ClapperPlaylistDemux +{ + ClapperUriBaseDemux parent; + + GstCaps *caps; + + ClapperEnhancerDirector *director; + ClapperEnhancerProxyList *enhancer_proxies; +}; + +enum +{ + PROP_0, + PROP_ENHANCER_PROXIES, + PROP_LAST +}; + +static GParamSpec *param_specs[PROP_LAST] = { NULL, }; + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (CLAPPER_PLAYLIST_MEDIA_TYPE)); + +static GstStaticCaps clapper_playlist_caps = GST_STATIC_CAPS (CLAPPER_PLAYLIST_MEDIA_TYPE); + +static void +clapper_playlist_type_find (GstTypeFind *tf, ClapperEnhancerProxy *proxy) +{ + const gchar *prefix, *contains, *regex, *module_name; + + if (!clapper_enhancer_proxy_get_target_creation_allowed (proxy)) + return; + + if ((prefix = clapper_enhancer_proxy_get_extra_data (proxy, "X-Data-Prefix"))) { + size_t len = strlen (prefix); + const gchar *data = (const gchar *) gst_type_find_peek (tf, 0, (guint) len); + + if (!data || memcmp (data, prefix, len) != 0) + return; + } + + contains = clapper_enhancer_proxy_get_extra_data (proxy, "X-Data-Contains"); + regex = clapper_enhancer_proxy_get_extra_data (proxy, "X-Data-Regex"); + + if (contains || regex) { + const gchar *data; + guint data_size = DATA_CHUNK_SIZE; + + if (!(data = (const gchar *) gst_type_find_peek (tf, 0, data_size))) { + guint64 data_len = gst_type_find_get_length (tf); + + if (G_LIKELY (data_len < DATA_CHUNK_SIZE)) { // likely, since whole chunk read failed + data_size = (guint) data_len; + data = (const gchar *) gst_type_find_peek (tf, 0, data_size); + } + } + + if (G_UNLIKELY (data == NULL)) { + GST_ERROR ("Could not read data!"); + return; + } + + if (contains && !g_strstr_len (data, data_size, contains)) + return; + + if (regex) { + GRegex *reg; + GError *error = NULL; + gboolean matched; + + if (!(reg = g_regex_new (regex, 0, 0, &error))) { + GST_ERROR ("Could not compile regex, reason: %s", error->message); + g_error_free (error); + + return; + } + + matched = g_regex_match_full (reg, data, (gssize) data_size, 0, 0, NULL, NULL); + g_regex_unref (reg); + + if (!matched) + return; + } + } + + module_name = clapper_enhancer_proxy_get_module_name (proxy); + GST_INFO ("Suggesting likely type: " CLAPPER_PLAYLIST_MEDIA_TYPE + ", enhancer: %s", module_name); + + gst_type_find_suggest_simple (tf, GST_TYPE_FIND_LIKELY, + CLAPPER_PLAYLIST_MEDIA_TYPE, "enhancer", G_TYPE_STRING, module_name, NULL); +} + +static gboolean +type_find_register (GstPlugin *plugin) +{ + ClapperEnhancerProxyList *global_proxies = clapper_get_global_enhancer_proxies (); + GstCaps *reg_caps = NULL; + guint i, n_proxies = clapper_enhancer_proxy_list_get_n_proxies (global_proxies); + gboolean res = FALSE; + + for (i = 0; i < n_proxies; ++i) { + ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (global_proxies, i); + + if (clapper_enhancer_proxy_target_has_interface (proxy, CLAPPER_TYPE_PLAYLISTABLE) + && (clapper_enhancer_proxy_get_extra_data (proxy, "X-Data-Prefix") + || clapper_enhancer_proxy_get_extra_data (proxy, "X-Data-Contains") + || clapper_enhancer_proxy_get_extra_data (proxy, "X-Data-Regex"))) { + if (!reg_caps) + reg_caps = gst_static_caps_get (&clapper_playlist_caps); + + res |= gst_type_find_register (plugin, clapper_enhancer_proxy_get_module_name (proxy), + GST_RANK_MARGINAL + 1, (GstTypeFindFunction) clapper_playlist_type_find, + NULL, reg_caps, proxy, NULL); + } + } + + gst_clear_caps (®_caps); + + return res; +} + +#define parent_class clapper_playlist_demux_parent_class +G_DEFINE_TYPE (ClapperPlaylistDemux, clapper_playlist_demux, CLAPPER_TYPE_URI_BASE_DEMUX); +GST_TYPE_FIND_REGISTER_DEFINE_CUSTOM (clapperplaylistdemux, type_find_register); +GST_ELEMENT_REGISTER_DEFINE (clapperplaylistdemux, "clapperplaylistdemux", + 512, CLAPPER_TYPE_PLAYLIST_DEMUX); + +static void +clapper_playlist_demux_handle_caps (ClapperUriBaseDemux *uri_bd, GstCaps *caps) +{ + ClapperPlaylistDemux *self = CLAPPER_PLAYLIST_DEMUX_CAST (uri_bd); + + gst_caps_replace (&self->caps, caps); + GST_DEBUG_OBJECT (self, "Set caps: %" GST_PTR_FORMAT, caps); +} + +static GList * +_filter_playlistables (ClapperPlaylistDemux *self, GstCaps *caps, ClapperEnhancerProxyList *proxies) +{ + GList *sublist = NULL; + GstStructure *structure; + ClapperEnhancerProxy *proxy; + + if (caps && (structure = gst_caps_get_structure (self->caps, 0))) { + const gchar *module_name = gst_structure_get_string (structure, "enhancer"); + + if (module_name && (proxy = clapper_enhancer_proxy_list_get_proxy_by_module (proxies, module_name))) + sublist = g_list_append (sublist, proxy); + } + + return sublist; +} + +static inline gboolean +_handle_playlist (ClapperPlaylistDemux *self, GListStore *playlist, GCancellable *cancellable) +{ + ClapperMediaItem *item = g_list_model_get_item (G_LIST_MODEL (playlist), 0); + const gchar *uri; + gboolean success; + + if (G_UNLIKELY (item == NULL)) { + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, + ("This playlist appears to be empty"), (NULL)); + return FALSE; + } + + uri = clapper_media_item_get_uri (item); + success = clapper_uri_base_demux_set_uri (CLAPPER_URI_BASE_DEMUX_CAST (self), uri, NULL); + gst_object_unref (item); + + if (G_UNLIKELY (!success)) { + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, + ("Resolved item URI was rejected"), (NULL)); + return FALSE; + } + + if (!g_cancellable_is_cancelled (cancellable)) { + GstStructure *structure = gst_structure_new ("ClapperPlaylistParsed", + "playlist", G_TYPE_LIST_STORE, playlist, NULL); + + gst_element_post_message (GST_ELEMENT_CAST (self), + gst_message_new_element (GST_OBJECT_CAST (self), structure)); + } + + return TRUE; +} + +static gboolean +clapper_playlist_demux_process_buffer (ClapperUriBaseDemux *uri_bd, + GstBuffer *buffer, GCancellable *cancellable) +{ + ClapperPlaylistDemux *self = CLAPPER_PLAYLIST_DEMUX_CAST (uri_bd); + ClapperEnhancerProxyList *proxies; + GList *filtered_proxies; + GstPad *sink_pad; + GstQuery *query; + GUri *uri = NULL; + GListStore *playlist; + GError *error = NULL; + gboolean handled; + + sink_pad = gst_element_get_static_pad (GST_ELEMENT_CAST (self), "sink"); + query = gst_query_new_uri (); + + if (gst_pad_peer_query (sink_pad, query)) { + gchar *query_uri; + + gst_query_parse_uri (query, &query_uri); + GST_DEBUG_OBJECT (self, "Source URI: %s", query_uri); + + if (query_uri) { + uri = g_uri_parse (query_uri, G_URI_FLAGS_ENCODED, NULL); + g_free (query_uri); + } + } + + gst_query_unref (query); + gst_object_unref (sink_pad); + + if (G_UNLIKELY (uri == NULL)) { + GST_ERROR_OBJECT (self, "Could not query source URI"); + return FALSE; + } + + if (!self->director) + self->director = clapper_enhancer_director_new (); + + GST_OBJECT_LOCK (self); + + if (G_LIKELY (self->enhancer_proxies != NULL)) { + GST_INFO_OBJECT (self, "Using enhancer proxies: %" GST_PTR_FORMAT, self->enhancer_proxies); + proxies = gst_object_ref (self->enhancer_proxies); + } else { + /* Compat for old ClapperDiscoverer feature that does not set this property */ + GST_WARNING_OBJECT (self, "Falling back to using global enhancer proxy list!"); + proxies = gst_object_ref (clapper_get_global_enhancer_proxies ()); + } + + GST_OBJECT_UNLOCK (self); + + filtered_proxies = _filter_playlistables (self, self->caps, proxies); + gst_object_unref (proxies); + + playlist = clapper_enhancer_director_parse (self->director, + filtered_proxies, uri, buffer, cancellable, &error); + + g_clear_list (&filtered_proxies, gst_object_unref); + g_uri_unref (uri); + + if (!playlist) { + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, + ("%s", error->message), (NULL)); + g_clear_error (&error); + + return FALSE; + } + + handled = _handle_playlist (self, playlist, cancellable); + g_object_unref (playlist); + + return handled; +} + +static void +clapper_playlist_demux_set_enhancer_proxies (ClapperPlaylistDemux *self, + ClapperEnhancerProxyList *enhancer_proxies) +{ + GST_OBJECT_LOCK (self); + gst_object_replace ((GstObject **) &self->enhancer_proxies, + GST_OBJECT_CAST (enhancer_proxies)); + GST_OBJECT_UNLOCK (self); +} + +static void +clapper_playlist_demux_init (ClapperPlaylistDemux *self) +{ +} + +static void +clapper_playlist_demux_dispose (GObject *object) +{ + ClapperPlaylistDemux *self = CLAPPER_PLAYLIST_DEMUX_CAST (object); + + GST_OBJECT_LOCK (self); + g_clear_object (&self->director); + GST_OBJECT_UNLOCK (self); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +clapper_playlist_demux_finalize (GObject *object) +{ + ClapperPlaylistDemux *self = CLAPPER_PLAYLIST_DEMUX_CAST (object); + + GST_TRACE_OBJECT (self, "Finalize"); + + gst_clear_caps (&self->caps); + gst_clear_object (&self->enhancer_proxies); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +clapper_playlist_demux_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + ClapperPlaylistDemux *self = CLAPPER_PLAYLIST_DEMUX_CAST (object); + + switch (prop_id) { + case PROP_ENHANCER_PROXIES: + clapper_playlist_demux_set_enhancer_proxies (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clapper_playlist_demux_class_init (ClapperPlaylistDemuxClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + ClapperUriBaseDemuxClass *clapperuribd_class = (ClapperUriBaseDemuxClass *) klass; + + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperplaylistdemux", 0, + "Clapper Playlist Demux"); + + gobject_class->set_property = clapper_playlist_demux_set_property; + gobject_class->dispose = clapper_playlist_demux_dispose; + gobject_class->finalize = clapper_playlist_demux_finalize; + + clapperuribd_class->handle_caps = clapper_playlist_demux_handle_caps; + clapperuribd_class->process_buffer = clapper_playlist_demux_process_buffer; + + param_specs[PROP_ENHANCER_PROXIES] = g_param_spec_object ("enhancer-proxies", + NULL, NULL, CLAPPER_TYPE_ENHANCER_PROXY_LIST, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, PROP_LAST, param_specs); + + gst_element_class_add_static_pad_template (gstelement_class, &sink_template); + + gst_element_class_set_static_metadata (gstelement_class, "Clapper Playlist Demux", + "Demuxer", "A custom demuxer for playlists", + "Rafał Dzięgiel "); +} diff --git a/src/lib/clapper/gst/clapper-plugin.c b/src/lib/clapper/gst/clapper-plugin.c index 43a5ae45..a44fb3c7 100644 --- a/src/lib/clapper/gst/clapper-plugin.c +++ b/src/lib/clapper/gst/clapper-plugin.c @@ -24,15 +24,17 @@ #include "../clapper-enhancer-proxy.h" #include "../clapper-enhancer-proxy-list-private.h" #include "../clapper-extractable.h" +#include "../clapper-playlistable.h" #include "clapper-plugin-private.h" #include "clapper-extractable-src-private.h" +#include "clapper-playlist-demux-private.h" #include "clapper-uri-list-demux-private.h" gboolean clapper_gst_plugin_init (GstPlugin *plugin) { - gboolean res = FALSE; + gboolean res = TRUE; ClapperEnhancerProxyList *global_proxies; gst_plugin_add_dependency_simple (plugin, @@ -42,10 +44,14 @@ clapper_gst_plugin_init (GstPlugin *plugin) global_proxies = clapper_get_global_enhancer_proxies (); /* Avoid registering an URI handler without schemes */ - if (clapper_enhancer_proxy_list_has_proxy_with_interface (global_proxies, CLAPPER_TYPE_EXTRACTABLE)) - res |= GST_ELEMENT_REGISTER (clapperextractablesrc, plugin); + if (clapper_enhancer_proxy_list_has_proxy_with_interface (global_proxies, CLAPPER_TYPE_EXTRACTABLE)) { + res &= (GST_ELEMENT_REGISTER (clapperextractablesrc, plugin) + && GST_ELEMENT_REGISTER (clapperurilistdemux, plugin)); + } - res |= GST_ELEMENT_REGISTER (clapperurilistdemux, plugin); + /* Type find will only register if there are playlistable enhancers */ + if (GST_TYPE_FIND_REGISTER (clapperplaylistdemux, plugin)) + GST_ELEMENT_REGISTER (clapperplaylistdemux, plugin); return res; } diff --git a/src/lib/clapper/meson.build b/src/lib/clapper/meson.build index 48578965..703e796a 100644 --- a/src/lib/clapper/meson.build +++ b/src/lib/clapper/meson.build @@ -164,6 +164,7 @@ clapper_sources = [ 'gst/clapper-enhancer-director.c', 'gst/clapper-uri-base-demux.c', 'gst/clapper-uri-list-demux.c', + 'gst/clapper-playlist-demux.c', '../shared/clapper-shared-utils.c', ] clapper_c_args = [