mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-29 15:22:11 +02:00
clapper: Support libpeas based plugin system
Allow loading external plugins called "Enhancers" that as the name suggests, enhance Clapper library capabilities. Currently implemented is a "ClapperExtractable" interface meant to extract an actual media that GStreamer can later play from an initial input URI. Additionally, an internal GStreamer elements that work with it are ported/moved here from "gtuber" library that this functionality replaces.
This commit is contained in:
@@ -85,6 +85,11 @@ libadwaita_dep = dependency('libadwaita-1',
|
||||
required: false,
|
||||
)
|
||||
|
||||
# Optional
|
||||
peas_dep = dependency('libpeas-2',
|
||||
required: false,
|
||||
)
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
libm = cc.find_library('m', required: false)
|
||||
|
||||
@@ -147,6 +152,9 @@ summary('vapi', build_vapi ? 'Yes' : 'No', section: 'Build')
|
||||
summary('doc', build_doc ? 'Yes' : 'No', section: 'Build')
|
||||
|
||||
if build_clapper
|
||||
foreach name : clapper_possible_functionalities
|
||||
summary(name, clapper_available_functionalities.contains(name) ? 'Yes' : 'No', section: 'Functionalities')
|
||||
endforeach
|
||||
foreach name : clapper_possible_features
|
||||
summary(name, clapper_available_features.contains(name) ? 'Yes' : 'No', section: 'Features')
|
||||
endforeach
|
||||
|
@@ -35,6 +35,13 @@ option('doc',
|
||||
description: 'Build documentation'
|
||||
)
|
||||
|
||||
# Functionalities
|
||||
option('enhancers-loader',
|
||||
type: 'feature',
|
||||
value: 'enabled',
|
||||
description: 'Ability to load libpeas based plugins that enhance capabilities'
|
||||
)
|
||||
|
||||
# Features
|
||||
option('discoverer',
|
||||
type: 'feature',
|
||||
|
42
src/lib/clapper/clapper-enhancers-loader-private.h
Normal file
42
src/lib/clapper/clapper-enhancers-loader-private.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_enhancers_loader_initialize (void);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_enhancers_loader_has_enhancers (GType iface_type);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gchar ** clapper_enhancers_loader_get_schemes (GType iface_type);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_enhancers_loader_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GObject * clapper_enhancers_loader_create_enhancer_for_uri (GType iface_type, GUri *uri);
|
||||
|
||||
G_END_DECLS
|
388
src/lib/clapper/clapper-enhancers-loader.c
Normal file
388
src/lib/clapper/clapper-enhancers-loader.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <libpeas.h>
|
||||
|
||||
#include "clapper-enhancers-loader-private.h"
|
||||
|
||||
#define ENHANCER_INTERFACES "X-Interfaces"
|
||||
#define ENHANCER_SCHEMES "X-Schemes"
|
||||
#define ENHANCER_HOSTS "X-Hosts"
|
||||
#define ENHANCER_IFACE_NAME_FROM_TYPE(type) (g_type_name (type) + 7) // strip "Clapper" prefix
|
||||
|
||||
#define GST_CAT_DEFAULT clapper_enhancers_loader_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
static PeasEngine *_engine = NULL;
|
||||
static GMutex load_lock;
|
||||
|
||||
/*
|
||||
* clapper_enhancers_loader_initialize:
|
||||
*
|
||||
* Initializes #PeasEngine with directories that store enhancers.
|
||||
*/
|
||||
void
|
||||
clapper_enhancers_loader_initialize (void)
|
||||
{
|
||||
const gchar *enhancers_path;
|
||||
gchar **dir_paths;
|
||||
guint i;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperenhancersloader", 0,
|
||||
"Clapper Enhancer Loader");
|
||||
|
||||
enhancers_path = g_getenv ("CLAPPER_ENHANCERS_PATH");
|
||||
if (!enhancers_path || *enhancers_path == '\0')
|
||||
enhancers_path = CLAPPER_ENHANCERS_PATH;
|
||||
|
||||
GST_INFO ("Initializing Clapper enhancers with path: \"%s\"", enhancers_path);
|
||||
|
||||
_engine = peas_engine_new ();
|
||||
|
||||
/* Peas loaders are loaded lazily, so it should be fine
|
||||
* to just enable them all here (even if not installed) */
|
||||
peas_engine_enable_loader (_engine, "python");
|
||||
peas_engine_enable_loader (_engine, "gjs");
|
||||
|
||||
dir_paths = g_strsplit (enhancers_path, G_SEARCHPATH_SEPARATOR_S, 0);
|
||||
|
||||
for (i = 0; dir_paths[i]; ++i)
|
||||
peas_engine_add_search_path (_engine, dir_paths[i], NULL);
|
||||
|
||||
g_strfreev (dir_paths);
|
||||
|
||||
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_INFO) {
|
||||
GListModel *list = (GListModel *) _engine;
|
||||
guint n_items = g_list_model_get_n_items (list);
|
||||
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
|
||||
GST_INFO ("Found enhancer: %s (%s)", peas_plugin_info_get_name (info),
|
||||
peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES));
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
GST_INFO ("Clapper enhancers initialized, found: %u", n_items);
|
||||
}
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
_is_name_listed (const gchar *name, const gchar *list_str)
|
||||
{
|
||||
gsize name_len = strlen (name);
|
||||
guint i = 0;
|
||||
|
||||
while (list_str[i] != '\0') {
|
||||
guint end = i;
|
||||
|
||||
while (list_str[end] != ';' && list_str[end] != '\0')
|
||||
++end;
|
||||
|
||||
/* Compare letters count until separator and prefix of whole string */
|
||||
if (end - i == name_len && g_str_has_prefix (list_str + i, name))
|
||||
return TRUE;
|
||||
|
||||
i = end;
|
||||
|
||||
/* Move to the next letter after ';' */
|
||||
if (list_str[i] != '\0')
|
||||
++i;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* clapper_enhancers_loader_get_info:
|
||||
* @iface_type: an interface #GType
|
||||
* @scheme: an URI scheme
|
||||
* @host: (nullable): an URI host
|
||||
*
|
||||
* Returns: (transfer full) (nullable): available #PeasPluginInfo or %NULL.
|
||||
*/
|
||||
static PeasPluginInfo *
|
||||
clapper_enhancers_loader_get_info (GType iface_type, const gchar *scheme, const gchar *host)
|
||||
{
|
||||
GListModel *list = (GListModel *) _engine;
|
||||
PeasPluginInfo *found_info = NULL;
|
||||
guint i, n_plugins = g_list_model_get_n_items (list);
|
||||
const gchar *iface_name;
|
||||
gboolean is_https;
|
||||
|
||||
if (n_plugins == 0) {
|
||||
GST_INFO ("No Clapper enhancers found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
|
||||
|
||||
/* Strip common subdomains, so plugins do not
|
||||
* have to list all combinations */
|
||||
if (host) {
|
||||
if (g_str_has_prefix (host, "www."))
|
||||
host += 4;
|
||||
else if (g_str_has_prefix (host, "m."))
|
||||
host += 2;
|
||||
}
|
||||
|
||||
GST_INFO ("Enhancer check, iface: %s, scheme: %s, host: %s",
|
||||
iface_name, scheme, GST_STR_NULL (host));
|
||||
|
||||
is_https = (g_str_has_prefix (scheme, "http")
|
||||
&& (scheme[4] == '\0' || (scheme[4] == 's' && scheme[5] == '\0')));
|
||||
|
||||
if (!host && is_https)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < n_plugins; ++i) {
|
||||
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
|
||||
const gchar *iface_names, *schemes, *hosts;
|
||||
|
||||
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
|
||||
GST_DEBUG ("Skipping enhancer without interfaces: %s", peas_plugin_info_get_name (info));
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
if (!_is_name_listed (iface_name, iface_names)) {
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
if (!(schemes = peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES))) {
|
||||
GST_DEBUG ("Skipping enhancer without schemes: %s", peas_plugin_info_get_name (info));
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
if (!_is_name_listed (scheme, schemes)) {
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
if (is_https) {
|
||||
if (!(hosts = peas_plugin_info_get_external_data (info, ENHANCER_HOSTS))) {
|
||||
GST_DEBUG ("Skipping enhancer without hosts: %s", peas_plugin_info_get_name (info));
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
if (!_is_name_listed (host, hosts)) {
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
found_info = info;
|
||||
break;
|
||||
}
|
||||
|
||||
return found_info;
|
||||
}
|
||||
|
||||
/*
|
||||
* clapper_enhancers_loader_has_enhancers:
|
||||
* @iface_type: an interface #GType
|
||||
*
|
||||
* Check if any enhancer implementing given interface type is available.
|
||||
*
|
||||
* Returns: whether any valid enhancer was found.
|
||||
*/
|
||||
gboolean
|
||||
clapper_enhancers_loader_has_enhancers (GType iface_type)
|
||||
{
|
||||
GListModel *list = (GListModel *) _engine;
|
||||
const gchar *iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
|
||||
guint i, n_plugins;
|
||||
|
||||
GST_DEBUG ("Checking for any enhancers of type: \"%s\"", iface_name);
|
||||
|
||||
n_plugins = g_list_model_get_n_items (list);
|
||||
|
||||
for (i = 0; i < n_plugins; ++i) {
|
||||
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
|
||||
const gchar *iface_names;
|
||||
|
||||
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
if (!_is_name_listed (iface_name, iface_names)) {
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Additional validation */
|
||||
if (!peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES)
|
||||
|| !peas_plugin_info_get_external_data (info, ENHANCER_HOSTS)) {
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
|
||||
GST_DEBUG ("Found valid enhancers of type: \"%s\"", iface_name);
|
||||
g_object_unref (info);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_DEBUG ("No available enhancers of type: \"%s\"", iface_name);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* clapper_enhancers_loader_get_schemes:
|
||||
* @iface_type: an interface #GType
|
||||
*
|
||||
* Get all supported schemes for a given interface type.
|
||||
* The returned array consists of unique strings (no duplicates).
|
||||
*
|
||||
* Returns: (transfer full): all supported schemes by enhancers of type.
|
||||
*/
|
||||
gchar **
|
||||
clapper_enhancers_loader_get_schemes (GType iface_type)
|
||||
{
|
||||
GListModel *list = (GListModel *) _engine;
|
||||
GSList *found_schemes = NULL, *fs;
|
||||
const gchar *iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
|
||||
gchar **schemes_strv;
|
||||
guint i, n_plugins, n_schemes;
|
||||
|
||||
GST_DEBUG ("Checking supported URI schemes for \"%s\"", iface_name);
|
||||
|
||||
n_plugins = g_list_model_get_n_items (list);
|
||||
|
||||
for (i = 0; i < n_plugins; ++i) {
|
||||
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
|
||||
const gchar *iface_names, *schemes;
|
||||
gchar **tmp_strv;
|
||||
gint j;
|
||||
|
||||
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
if (!_is_name_listed (iface_name, iface_names)) {
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
if (!(schemes = peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES))) {
|
||||
g_object_unref (info);
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp_strv = g_strsplit (schemes, ";", 0);
|
||||
|
||||
for (j = 0; tmp_strv[j]; ++j) {
|
||||
const gchar *scheme = tmp_strv[j];
|
||||
|
||||
if (!found_schemes || !g_slist_find_custom (found_schemes,
|
||||
scheme, (GCompareFunc) strcmp)) {
|
||||
found_schemes = g_slist_append (found_schemes, g_strdup (scheme));
|
||||
GST_INFO ("Found supported URI scheme: %s", scheme);
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (tmp_strv);
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
n_schemes = g_slist_length (found_schemes);
|
||||
schemes_strv = g_new0 (gchar *, n_schemes + 1);
|
||||
|
||||
fs = found_schemes;
|
||||
for (i = 0; i < n_schemes; ++i) {
|
||||
schemes_strv[i] = fs->data;
|
||||
fs = fs->next;
|
||||
}
|
||||
|
||||
GST_DEBUG ("Total found URI schemes: %u", n_schemes);
|
||||
|
||||
/* Since string pointers were taken,
|
||||
* free list without content */
|
||||
g_slist_free (found_schemes);
|
||||
|
||||
return schemes_strv;
|
||||
}
|
||||
|
||||
/*
|
||||
* clapper_enhancers_loader_check:
|
||||
* @iface_type: a requested #GType
|
||||
* @scheme: an URI scheme
|
||||
* @host: (nullable): an URI host
|
||||
* @name: (out) (optional) (transfer none): return location for found enhancer name
|
||||
*
|
||||
* Checks if any enhancer can handle @uri without initializing loader
|
||||
* or creating enhancer instance, thus this can be used freely from any thread.
|
||||
*
|
||||
* Returns: whether enhancer for given scheme and host is available.
|
||||
*/
|
||||
gboolean
|
||||
clapper_enhancers_loader_check (GType iface_type,
|
||||
const gchar *scheme, const gchar *host, const gchar **name)
|
||||
{
|
||||
PeasPluginInfo *info;
|
||||
|
||||
if ((info = clapper_enhancers_loader_get_info (iface_type, scheme, host))) {
|
||||
if (name)
|
||||
*name = peas_plugin_info_get_name (info);
|
||||
|
||||
g_object_unref (info);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* clapper_enhancers_loader_create_enhancer_for_uri:
|
||||
* @iface_type: a requested #GType
|
||||
* @uri: a #GUri
|
||||
*
|
||||
* Creates a new enhancer object for given URI.
|
||||
*
|
||||
* Enhancer should only be created and used within single thread.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): a new enhancer instance.
|
||||
*/
|
||||
GObject *
|
||||
clapper_enhancers_loader_create_enhancer_for_uri (GType iface_type, GUri *uri)
|
||||
{
|
||||
GObject *enhancer = NULL;
|
||||
PeasPluginInfo *info;
|
||||
const gchar *scheme = g_uri_get_scheme (uri);
|
||||
const gchar *host = g_uri_get_host (uri);
|
||||
|
||||
if ((info = clapper_enhancers_loader_get_info (iface_type, scheme, host))) {
|
||||
g_mutex_lock (&load_lock);
|
||||
|
||||
if (!peas_plugin_info_is_loaded (info) && !peas_engine_load_plugin (_engine, info)) {
|
||||
GST_ERROR ("Could not load enhancer: %s", peas_plugin_info_get_name (info));
|
||||
} else if (!peas_engine_provides_extension (_engine, info, iface_type)) {
|
||||
GST_ERROR ("No \"%s\" enhancer in plugin: %s", ENHANCER_IFACE_NAME_FROM_TYPE (iface_type),
|
||||
peas_plugin_info_get_name (info));
|
||||
} else {
|
||||
enhancer = peas_engine_create_extension (_engine, info, iface_type, NULL);
|
||||
}
|
||||
|
||||
g_mutex_unlock (&load_lock);
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
return enhancer;
|
||||
}
|
33
src/lib/clapper/clapper-extractable-private.h
Normal file
33
src/lib/clapper/clapper-extractable-private.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "clapper-extractable.h"
|
||||
#include "clapper-harvest.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_extractable_extract (ClapperExtractable *extractable, GUri *uri, ClapperHarvest *harvest, GCancellable *cancellable, GError **error);
|
||||
|
||||
G_END_DECLS
|
61
src/lib/clapper/clapper-extractable.c
Normal file
61
src/lib/clapper/clapper-extractable.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ClapperExtractable:
|
||||
*
|
||||
* An interface for creating enhancers that resolve given URI into something playable.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "clapper-extractable-private.h"
|
||||
#include "clapper-harvest-private.h"
|
||||
|
||||
G_DEFINE_INTERFACE (ClapperExtractable, clapper_extractable, G_TYPE_OBJECT);
|
||||
|
||||
static gboolean
|
||||
clapper_extractable_default_extract (ClapperExtractable *self, GUri *uri,
|
||||
ClapperHarvest *harvest, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
if (*error == NULL) {
|
||||
g_set_error (error, GST_CORE_ERROR,
|
||||
GST_CORE_ERROR_NOT_IMPLEMENTED,
|
||||
"Extractable object did not implement extract function");
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_extractable_default_init (ClapperExtractableInterface *iface)
|
||||
{
|
||||
iface->extract = clapper_extractable_default_extract;
|
||||
}
|
||||
|
||||
gboolean
|
||||
clapper_extractable_extract (ClapperExtractable *self, GUri *uri,
|
||||
ClapperHarvest *harvest, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
ClapperExtractableInterface *iface = CLAPPER_EXTRACTABLE_GET_IFACE (self);
|
||||
|
||||
return iface->extract (self, uri, harvest, cancellable, error);
|
||||
}
|
69
src/lib/clapper/clapper-extractable.h
Normal file
69
src/lib/clapper/clapper-extractable.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
|
||||
#error "Only <clapper/clapper.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <clapper/clapper-visibility.h>
|
||||
#include <clapper/clapper-harvest.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CLAPPER_TYPE_EXTRACTABLE (clapper_extractable_get_type())
|
||||
#define CLAPPER_EXTRACTABLE_CAST(obj) ((ClapperExtractable *)(obj))
|
||||
|
||||
CLAPPER_API
|
||||
G_DECLARE_INTERFACE (ClapperExtractable, clapper_extractable, CLAPPER, EXTRACTABLE, GObject)
|
||||
|
||||
/**
|
||||
* ClapperExtractableInterface:
|
||||
* @parent_iface: The parent interface structure.
|
||||
* @extract: Extract data and fill harvest.
|
||||
*/
|
||||
struct _ClapperExtractableInterface
|
||||
{
|
||||
GTypeInterface parent_iface;
|
||||
|
||||
/**
|
||||
* ClapperExtractableInterface::extract:
|
||||
* @extractable: a #ClapperExtractable
|
||||
* @uri: a #GUri
|
||||
* @cancellable: a #GCancellable object
|
||||
* @error: a #GError
|
||||
*
|
||||
* Extract data and fill harvest.
|
||||
*
|
||||
* Returns: whether extraction was successful.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
gboolean (* extract) (ClapperExtractable *extractable, GUri *uri, ClapperHarvest *harvest, GCancellable *cancellable, GError **error);
|
||||
|
||||
/*< private >*/
|
||||
gpointer padding[8];
|
||||
};
|
||||
|
||||
G_END_DECLS
|
36
src/lib/clapper/clapper-functionalities-availability.h.in
Normal file
36
src/lib/clapper/clapper-functionalities-availability.h.in
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
|
||||
#error "Only <clapper/clapper.h> can be included directly."
|
||||
#endif
|
||||
|
||||
/**
|
||||
* CLAPPER_WITH_ENHANCERS_LOADER:
|
||||
*
|
||||
* Check if Clapper was compiled with Enhancers Loader functionality.
|
||||
*
|
||||
* Alternatively, apps before compiling can also check whether `pkgconfig`
|
||||
* variable named `functionalities` contains `enhancers-loader` string.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
#define CLAPPER_WITH_ENHANCERS_LOADER (@CLAPPER_WITH_ENHANCERS_LOADER@)
|
35
src/lib/clapper/clapper-harvest-private.h
Normal file
35
src/lib/clapper/clapper-harvest-private.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/tag/tag.h>
|
||||
|
||||
#include "clapper-harvest.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
ClapperHarvest * clapper_harvest_new (void);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_harvest_unpack (ClapperHarvest *harvest, GstBuffer **buffer, gsize *buf_size, GstCaps **caps, GstTagList **tags, GstToc **toc, GstStructure **headers);
|
||||
|
||||
G_END_DECLS
|
450
src/lib/clapper/clapper-harvest.c
Normal file
450
src/lib/clapper/clapper-harvest.c
Normal file
@@ -0,0 +1,450 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ClapperHarvest:
|
||||
*
|
||||
* An object storing data from enhancers that implement [iface@Clapper.Extractable] interface.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
|
||||
/*
|
||||
* NOTE: We cannot simply expose GstMiniObjects for
|
||||
* implementations to assemble TagList/Toc themselves, see:
|
||||
* https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2867
|
||||
*/
|
||||
|
||||
#include "clapper-harvest-private.h"
|
||||
|
||||
#define GST_CAT_DEFAULT clapper_harvest_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
struct _ClapperHarvest
|
||||
{
|
||||
GstObject parent;
|
||||
|
||||
GstCaps *caps;
|
||||
GstBuffer *buffer;
|
||||
gsize buf_size;
|
||||
|
||||
GstTagList *tags;
|
||||
GstToc *toc;
|
||||
GstStructure *headers;
|
||||
|
||||
guint16 n_chapters;
|
||||
guint16 n_tracks;
|
||||
};
|
||||
|
||||
#define parent_class clapper_harvest_parent_class
|
||||
G_DEFINE_TYPE (ClapperHarvest, clapper_harvest, GST_TYPE_OBJECT);
|
||||
|
||||
static inline void
|
||||
_ensure_tags (ClapperHarvest *self)
|
||||
{
|
||||
if (!self->tags) {
|
||||
self->tags = gst_tag_list_new_empty ();
|
||||
gst_tag_list_set_scope (self->tags, GST_TAG_SCOPE_GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
_ensure_toc (ClapperHarvest *self)
|
||||
{
|
||||
if (!self->toc)
|
||||
self->toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_ensure_headers (ClapperHarvest *self)
|
||||
{
|
||||
if (!self->headers)
|
||||
self->headers = gst_structure_new_empty ("request-headers");
|
||||
}
|
||||
|
||||
ClapperHarvest *
|
||||
clapper_harvest_new (void)
|
||||
{
|
||||
ClapperHarvest *harvest;
|
||||
|
||||
harvest = g_object_new (CLAPPER_TYPE_HARVEST, NULL);
|
||||
gst_object_ref_sink (harvest);
|
||||
|
||||
return harvest;
|
||||
}
|
||||
|
||||
gboolean
|
||||
clapper_harvest_unpack (ClapperHarvest *self,
|
||||
GstBuffer **buffer, gsize *buf_size, GstCaps **caps,
|
||||
GstTagList **tags, GstToc **toc, GstStructure **headers)
|
||||
{
|
||||
/* Not filled or already unpacked */
|
||||
if (!self->buffer)
|
||||
return FALSE;
|
||||
|
||||
*buffer = self->buffer;
|
||||
self->buffer = NULL;
|
||||
|
||||
*buf_size = self->buf_size;
|
||||
self->buf_size = 0;
|
||||
|
||||
*caps = self->caps;
|
||||
self->caps = NULL;
|
||||
|
||||
*tags = self->tags;
|
||||
self->tags = NULL;
|
||||
|
||||
*toc = self->toc;
|
||||
self->toc = NULL;
|
||||
|
||||
*headers = self->headers;
|
||||
self->headers = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_harvest_fill:
|
||||
* @harvest: a #ClapperHarvest
|
||||
* @media_type: media mime type
|
||||
* @data: (array length=size) (element-type guint8) (transfer full): data to fill @harvest
|
||||
* @size: allocated size of @data
|
||||
*
|
||||
* Fill harvest with extracted data. It can be anything that GStreamer
|
||||
* can parse and play such as single URI or a streaming manifest.
|
||||
*
|
||||
* Calling again this function will replace previously filled content.
|
||||
*
|
||||
* Commonly used media types are:
|
||||
*
|
||||
* * `application/dash+xml`
|
||||
*
|
||||
* * `application/x-hls`
|
||||
*
|
||||
* * `text/uri-list`
|
||||
*
|
||||
* Returns: %TRUE when filled successfully, %FALSE if taken data was empty.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
gboolean
|
||||
clapper_harvest_fill (ClapperHarvest *self, const gchar *media_type, gpointer data, gsize size)
|
||||
{
|
||||
g_return_val_if_fail (CLAPPER_IS_HARVEST (self), FALSE);
|
||||
g_return_val_if_fail (media_type != NULL, FALSE);
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
if (!data || size == 0) {
|
||||
g_free (data);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG) {
|
||||
gboolean is_printable = (strcmp (media_type, "application/dash+xml") == 0)
|
||||
|| (strcmp (media_type, "application/x-hls") == 0)
|
||||
|| (strcmp (media_type, "text/uri-list") == 0);
|
||||
|
||||
if (is_printable) {
|
||||
gchar *data_str;
|
||||
|
||||
data_str = g_new0 (gchar, size + 1);
|
||||
memcpy (data_str, data, size);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Filled with data:\n%s", data_str);
|
||||
|
||||
g_free (data_str);
|
||||
}
|
||||
}
|
||||
|
||||
gst_clear_buffer (&self->buffer);
|
||||
gst_clear_caps (&self->caps);
|
||||
|
||||
self->buffer = gst_buffer_new_wrapped (data, size);
|
||||
self->buf_size = size;
|
||||
self->caps = gst_caps_new_simple (media_type,
|
||||
"source", G_TYPE_STRING, "clapper-harvest", NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_harvest_fill_with_text:
|
||||
* @harvest: a #ClapperHarvest
|
||||
* @media_type: media mime type
|
||||
* @text: (transfer full): data to fill @harvest as %NULL terminated string
|
||||
*
|
||||
* A convenience method to fill @harvest using a %NULL terminated string.
|
||||
*
|
||||
* For more info, see [method@Clapper.Harvest.fill] documentation.
|
||||
*
|
||||
* Returns: %TRUE when filled successfully, %FALSE if taken data was empty.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
gboolean
|
||||
clapper_harvest_fill_with_text (ClapperHarvest *self, const gchar *media_type, gchar *text)
|
||||
{
|
||||
g_return_val_if_fail (text != NULL, FALSE);
|
||||
|
||||
return clapper_harvest_fill (self, media_type, text, strlen (text));
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_harvest_fill_with_bytes:
|
||||
* @harvest: a #ClapperHarvest
|
||||
* @media_type: media mime type
|
||||
* @bytes: (transfer full): a #GBytes to fill @harvest
|
||||
*
|
||||
* A convenience method to fill @harvest with data from #GBytes.
|
||||
*
|
||||
* For more info, see [method@Clapper.Harvest.fill] documentation.
|
||||
*
|
||||
* Returns: %TRUE when filled successfully, %FALSE if taken data was empty.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
gboolean
|
||||
clapper_harvest_fill_with_bytes (ClapperHarvest *self, const gchar *media_type, GBytes *bytes)
|
||||
{
|
||||
gpointer data;
|
||||
gsize size = 0;
|
||||
|
||||
g_return_val_if_fail (bytes != NULL, FALSE);
|
||||
|
||||
data = g_bytes_unref_to_data (bytes, &size);
|
||||
|
||||
return clapper_harvest_fill (self, media_type, data, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_harvest_tags_add:
|
||||
* @harvest: a #ClapperHarvest
|
||||
* @tag: a name of tag to set
|
||||
* @...: %NULL-terminated list of arguments
|
||||
*
|
||||
* Append one or more tags into the tag list.
|
||||
*
|
||||
* Variable arguments should be in the form of tag and value pairs.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
void
|
||||
clapper_harvest_tags_add (ClapperHarvest *self, const gchar *tag, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
g_return_if_fail (CLAPPER_IS_HARVEST (self));
|
||||
g_return_if_fail (tag != NULL);
|
||||
|
||||
_ensure_tags (self);
|
||||
|
||||
va_start (args, tag);
|
||||
gst_tag_list_add_valist (self->tags, GST_TAG_MERGE_APPEND, tag, args);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_harvest_tags_add_value: (rename-to clapper_harvest_tags_add)
|
||||
* @harvest: a #ClapperHarvest
|
||||
* @tag: a name of tag to set
|
||||
* @value: a #GValue of tag
|
||||
*
|
||||
* Append another tag into the tag list using #GValue.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
void
|
||||
clapper_harvest_tags_add_value (ClapperHarvest *self, const gchar *tag, const GValue *value)
|
||||
{
|
||||
g_return_if_fail (CLAPPER_IS_HARVEST (self));
|
||||
g_return_if_fail (tag != NULL);
|
||||
g_return_if_fail (G_IS_VALUE (value));
|
||||
|
||||
_ensure_tags (self);
|
||||
gst_tag_list_add_value (self->tags, GST_TAG_MERGE_APPEND, tag, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_harvest_toc_add:
|
||||
* @harvest: a #ClapperHarvest
|
||||
* @type: a #GstTocEntryType
|
||||
* @title: an entry title
|
||||
* @start: entry start time in seconds
|
||||
* @end: entry end time in seconds or -1 if none
|
||||
*
|
||||
* Append a chapter or track name into table of contents.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
void
|
||||
clapper_harvest_toc_add (ClapperHarvest *self, GstTocEntryType type,
|
||||
const gchar *title, gdouble start, gdouble end)
|
||||
{
|
||||
GstTocEntry *entry, *subentry;
|
||||
GstClockTime start_time, end_time;
|
||||
gchar edition[3]; // 2 + 1
|
||||
gchar id[14]; // 7 + 1 + 5 + 1
|
||||
const gchar *id_prefix;
|
||||
guint16 nth_entry;
|
||||
|
||||
g_return_if_fail (CLAPPER_IS_HARVEST (self));
|
||||
g_return_if_fail (type == GST_TOC_ENTRY_TYPE_CHAPTER || type == GST_TOC_ENTRY_TYPE_TRACK);
|
||||
g_return_if_fail (title != NULL);
|
||||
g_return_if_fail (start >= 0 && end >= start);
|
||||
|
||||
switch (type) {
|
||||
case GST_TOC_ENTRY_TYPE_CHAPTER:
|
||||
id_prefix = "chapter";
|
||||
nth_entry = ++(self->n_chapters);
|
||||
break;
|
||||
case GST_TOC_ENTRY_TYPE_TRACK:
|
||||
id_prefix = "track";
|
||||
nth_entry = ++(self->n_tracks);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return;
|
||||
}
|
||||
|
||||
start_time = start * GST_SECOND;
|
||||
end_time = (end >= 0) ? end * GST_SECOND : GST_CLOCK_TIME_NONE;
|
||||
|
||||
g_snprintf (edition, sizeof (edition), "0%i", type);
|
||||
g_snprintf (id, sizeof (id), "%s.%" G_GUINT16_FORMAT, id_prefix, nth_entry);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Inserting TOC %s: \"%s\""
|
||||
" (%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ")",
|
||||
id, title, start_time, end_time);
|
||||
|
||||
subentry = gst_toc_entry_new (type, id);
|
||||
gst_toc_entry_set_tags (subentry, gst_tag_list_new (GST_TAG_TITLE, title, NULL));
|
||||
gst_toc_entry_set_start_stop_times (subentry, start_time, end_time);
|
||||
|
||||
_ensure_toc (self);
|
||||
|
||||
find_entry:
|
||||
if (!(entry = gst_toc_find_entry (self->toc, edition))) {
|
||||
GstTocEntry *toc_entry;
|
||||
|
||||
toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, edition);
|
||||
gst_toc_entry_set_start_stop_times (toc_entry, 0, GST_CLOCK_TIME_NONE);
|
||||
gst_toc_append_entry (self->toc, toc_entry); // transfer full and must be writable
|
||||
|
||||
goto find_entry;
|
||||
}
|
||||
|
||||
gst_toc_entry_append_sub_entry (entry, subentry);
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_harvest_headers_set:
|
||||
* @harvest: a #ClapperHarvest
|
||||
* @key: a header name
|
||||
* @...: %NULL-terminated list of arguments
|
||||
*
|
||||
* Set one or more request headers named with @key to specified `value`.
|
||||
*
|
||||
* Arguments should be %NULL terminated list of `key+value` string pairs.
|
||||
*
|
||||
* Setting again the same key will update its value to the new one.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
void
|
||||
clapper_harvest_headers_set (ClapperHarvest *self, const gchar *key, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
g_return_if_fail (CLAPPER_IS_HARVEST (self));
|
||||
g_return_if_fail (key != NULL);
|
||||
|
||||
_ensure_headers (self);
|
||||
|
||||
va_start (args, key);
|
||||
|
||||
while (key != NULL) {
|
||||
const gchar *val = va_arg (args, const gchar *);
|
||||
GST_DEBUG_OBJECT (self, "Set header, \"%s\": \"%s\"", key, val);
|
||||
gst_structure_set (self->headers, key, G_TYPE_STRING, val, NULL);
|
||||
key = va_arg (args, const gchar *);
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_harvest_headers_set_value: (rename-to clapper_harvest_headers_set)
|
||||
* @harvest: a #ClapperHarvest
|
||||
* @key: a header name
|
||||
* @value: a string #GValue of header
|
||||
*
|
||||
* Set another header in the request headers list using #GValue.
|
||||
*
|
||||
* Setting again the same key will update its value to the new one.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
void
|
||||
clapper_harvest_headers_set_value (ClapperHarvest *self, const gchar *key, const GValue *value)
|
||||
{
|
||||
g_return_if_fail (CLAPPER_IS_HARVEST (self));
|
||||
g_return_if_fail (key != NULL);
|
||||
g_return_if_fail (G_IS_VALUE (value) && G_VALUE_HOLDS_STRING (value));
|
||||
|
||||
_ensure_headers (self);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Set header, \"%s\": \"%s\"", key, g_value_get_string (value));
|
||||
gst_structure_set_value (self->headers, key, value);
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_harvest_init (ClapperHarvest *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_harvest_finalize (GObject *object)
|
||||
{
|
||||
ClapperHarvest *self = CLAPPER_HARVEST_CAST (object);
|
||||
|
||||
GST_TRACE_OBJECT (self, "Finalize");
|
||||
|
||||
gst_clear_caps (&self->caps);
|
||||
gst_clear_buffer (&self->buffer);
|
||||
|
||||
if (self->tags)
|
||||
gst_tag_list_unref (self->tags);
|
||||
if (self->toc)
|
||||
gst_toc_unref (self->toc);
|
||||
if (self->headers)
|
||||
gst_structure_free (self->headers);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_harvest_class_init (ClapperHarvestClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperharvest", 0,
|
||||
"Clapper Harvest");
|
||||
|
||||
gobject_class->finalize = clapper_harvest_finalize;
|
||||
}
|
64
src/lib/clapper/clapper-harvest.h
Normal file
64
src/lib/clapper/clapper-harvest.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
|
||||
#error "Only <clapper/clapper.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/tag/tag.h>
|
||||
|
||||
#include <clapper/clapper-visibility.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CLAPPER_TYPE_HARVEST (clapper_harvest_get_type())
|
||||
#define CLAPPER_HARVEST_CAST(obj) ((ClapperHarvest *)(obj))
|
||||
|
||||
G_DECLARE_FINAL_TYPE (ClapperHarvest, clapper_harvest, CLAPPER, HARVEST, GstObject)
|
||||
|
||||
CLAPPER_API
|
||||
gboolean clapper_harvest_fill (ClapperHarvest *harvest, const gchar *media_type, gpointer data, gsize size);
|
||||
|
||||
CLAPPER_API
|
||||
gboolean clapper_harvest_fill_with_text (ClapperHarvest *harvest, const gchar *media_type, gchar *text);
|
||||
|
||||
CLAPPER_API
|
||||
gboolean clapper_harvest_fill_with_bytes (ClapperHarvest *harvest, const gchar *media_type, GBytes *bytes);
|
||||
|
||||
CLAPPER_API
|
||||
void clapper_harvest_tags_add (ClapperHarvest *harvest, const gchar *tag, ...) G_GNUC_NULL_TERMINATED;
|
||||
|
||||
CLAPPER_API
|
||||
void clapper_harvest_tags_add_value (ClapperHarvest *harvest, const gchar *tag, const GValue *value);
|
||||
|
||||
CLAPPER_API
|
||||
void clapper_harvest_toc_add (ClapperHarvest *harvest, GstTocEntryType type, const gchar *title, gdouble start, gdouble end);
|
||||
|
||||
CLAPPER_API
|
||||
void clapper_harvest_headers_set (ClapperHarvest *harvest, const gchar *key, ...) G_GNUC_NULL_TERMINATED;
|
||||
|
||||
CLAPPER_API
|
||||
void clapper_harvest_headers_set_value (ClapperHarvest *harvest, const gchar *key, const GValue *value);
|
||||
|
||||
G_END_DECLS
|
@@ -30,6 +30,12 @@
|
||||
#include "clapper-stream-private.h"
|
||||
#include "clapper-stream-list-private.h"
|
||||
|
||||
#include "clapper-functionalities-availability.h"
|
||||
|
||||
#if CLAPPER_WITH_ENHANCERS_LOADER
|
||||
#include "gst/clapper-enhancer-src-private.h"
|
||||
#endif
|
||||
|
||||
#define GST_CAT_DEFAULT clapper_playbin_bus_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
@@ -869,6 +875,7 @@ _handle_tag_msg (GstMessage *msg, ClapperPlayer *player)
|
||||
{
|
||||
GstObject *src = GST_MESSAGE_SRC (msg);
|
||||
GstTagList *tags = NULL;
|
||||
gboolean from_enhancer_src;
|
||||
|
||||
/* Tag messages should only be posted by sink elements */
|
||||
if (G_UNLIKELY (!src))
|
||||
@@ -879,8 +886,21 @@ _handle_tag_msg (GstMessage *msg, ClapperPlayer *player)
|
||||
GST_LOG_OBJECT (player, "Got tags from element: %s: %" GST_PTR_FORMAT,
|
||||
GST_OBJECT_NAME (src), tags);
|
||||
|
||||
if (G_LIKELY (player->played_item != NULL))
|
||||
#if CLAPPER_WITH_ENHANCERS_LOADER
|
||||
from_enhancer_src = CLAPPER_IS_ENHANCER_SRC (src);
|
||||
#else
|
||||
from_enhancer_src = FALSE;
|
||||
#endif
|
||||
|
||||
/* ClapperEnhancerSrc determines tags before stream start */
|
||||
if (from_enhancer_src) {
|
||||
if (player->pending_tags) {
|
||||
gst_tag_list_unref (player->pending_tags);
|
||||
}
|
||||
player->pending_tags = gst_tag_list_ref (tags);
|
||||
} else if (G_LIKELY (player->played_item != NULL)) {
|
||||
clapper_media_item_update_from_tag_list (player->played_item, tags, player);
|
||||
}
|
||||
|
||||
gst_tag_list_unref (tags);
|
||||
}
|
||||
@@ -890,11 +910,10 @@ _handle_toc_msg (GstMessage *msg, ClapperPlayer *player)
|
||||
{
|
||||
GstObject *src = GST_MESSAGE_SRC (msg);
|
||||
GstToc *toc = NULL;
|
||||
ClapperTimeline *timeline;
|
||||
gboolean updated = FALSE;
|
||||
gboolean from_enhancer_src, updated = FALSE;
|
||||
|
||||
/* TOC messages should only be posted by sink elements after start */
|
||||
if (G_UNLIKELY (!src || !player->played_item))
|
||||
/* TOC messages should only be posted by sink elements */
|
||||
if (G_UNLIKELY (!src))
|
||||
return;
|
||||
|
||||
/* Either new TOC was found or previous one was updated */
|
||||
@@ -904,11 +923,27 @@ _handle_toc_msg (GstMessage *msg, ClapperPlayer *player)
|
||||
" from element: %s, updated: %s",
|
||||
toc, GST_OBJECT_NAME (src), (updated) ? "yes" : "no");
|
||||
|
||||
timeline = clapper_media_item_get_timeline (player->played_item);
|
||||
#if CLAPPER_WITH_ENHANCERS_LOADER
|
||||
from_enhancer_src = CLAPPER_IS_ENHANCER_SRC (src);
|
||||
#else
|
||||
from_enhancer_src = FALSE;
|
||||
#endif
|
||||
|
||||
if (clapper_timeline_set_toc (timeline, toc, updated)) {
|
||||
clapper_app_bus_post_refresh_timeline (player->app_bus,
|
||||
GST_OBJECT_CAST (player->played_item));
|
||||
/* ClapperEnhancerSrc determines TOC before stream start */
|
||||
if (from_enhancer_src) {
|
||||
if (player->pending_toc) {
|
||||
gst_toc_unref (player->pending_toc);
|
||||
}
|
||||
player->pending_toc = gst_toc_ref (toc);
|
||||
} else if (G_LIKELY (player->played_item != NULL)) {
|
||||
ClapperTimeline *timeline;
|
||||
|
||||
timeline = clapper_media_item_get_timeline (player->played_item);
|
||||
|
||||
if (clapper_timeline_set_toc (timeline, toc, updated)) {
|
||||
clapper_app_bus_post_refresh_timeline (player->app_bus,
|
||||
GST_OBJECT_CAST (player->played_item));
|
||||
}
|
||||
}
|
||||
|
||||
gst_toc_unref (toc);
|
||||
@@ -1035,6 +1070,26 @@ _handle_stream_start_msg (GstMessage *msg, ClapperPlayer *player)
|
||||
/* With playbin2 we update all decoders at once after stream start */
|
||||
if (!player->use_playbin3)
|
||||
clapper_player_playbin_update_current_decoders (player);
|
||||
|
||||
if (player->pending_tags) {
|
||||
if (G_LIKELY (player->played_item != NULL))
|
||||
clapper_media_item_update_from_tag_list (player->played_item, player->pending_tags, player);
|
||||
|
||||
gst_clear_tag_list (&player->pending_tags);
|
||||
}
|
||||
if (player->pending_toc) {
|
||||
if (G_LIKELY (player->played_item != NULL)) {
|
||||
ClapperTimeline *timeline = clapper_media_item_get_timeline (player->played_item);
|
||||
|
||||
if (clapper_timeline_set_toc (timeline, player->pending_toc, FALSE)) {
|
||||
clapper_app_bus_post_refresh_timeline (player->app_bus,
|
||||
GST_OBJECT_CAST (player->played_item));
|
||||
}
|
||||
}
|
||||
|
||||
gst_toc_unref (player->pending_toc);
|
||||
player->pending_toc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@@ -19,6 +19,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/tag/tag.h>
|
||||
|
||||
#include "clapper-player.h"
|
||||
#include "clapper-queue.h"
|
||||
#include "clapper-enums.h"
|
||||
@@ -53,6 +55,11 @@ struct _ClapperPlayer
|
||||
* different thread, thus needs a lock */
|
||||
ClapperMediaItem *pending_item;
|
||||
|
||||
/* Pending tags/toc that arrive before stream start.
|
||||
* To be applied to "played_item", thus no lock needed. */
|
||||
GstTagList *pending_tags;
|
||||
GstToc *pending_toc;
|
||||
|
||||
GstElement *playbin;
|
||||
|
||||
GstBus *bus;
|
||||
|
@@ -729,6 +729,13 @@ clapper_player_reset (ClapperPlayer *self, gboolean pending_dispose)
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
gst_clear_tag_list (&self->pending_tags);
|
||||
|
||||
if (self->pending_toc) {
|
||||
gst_toc_unref (self->pending_toc);
|
||||
self->pending_toc = NULL;
|
||||
}
|
||||
|
||||
/* Emit notify when we are not going to be disposed */
|
||||
if (!pending_dispose) {
|
||||
/* Clear current decoders (next item might not have video/audio track) */
|
||||
|
@@ -17,6 +17,8 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
@@ -25,6 +27,11 @@
|
||||
#include "clapper-playbin-bus-private.h"
|
||||
#include "clapper-app-bus-private.h"
|
||||
#include "clapper-features-bus-private.h"
|
||||
#include "gst/clapper-plugin-private.h"
|
||||
|
||||
#if CLAPPER_WITH_ENHANCERS_LOADER
|
||||
#include "clapper-enhancers-loader-private.h"
|
||||
#endif
|
||||
|
||||
static gboolean is_initialized = FALSE;
|
||||
static GMutex init_lock;
|
||||
@@ -44,6 +51,22 @@ clapper_init_check_internal (int *argc, char **argv[])
|
||||
clapper_app_bus_initialize ();
|
||||
clapper_features_bus_initialize ();
|
||||
|
||||
#if CLAPPER_WITH_ENHANCERS_LOADER
|
||||
clapper_enhancers_loader_initialize ();
|
||||
#endif
|
||||
|
||||
gst_plugin_register_static (
|
||||
GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
PACKAGE "internal",
|
||||
PLUGIN_DESC,
|
||||
(GstPluginInitFunc) clapper_gst_plugin_init,
|
||||
PACKAGE_VERSION,
|
||||
PLUGIN_LICENSE,
|
||||
PACKAGE,
|
||||
PACKAGE,
|
||||
PACKAGE_ORIGIN);
|
||||
|
||||
is_initialized = TRUE;
|
||||
|
||||
finish:
|
||||
@@ -92,3 +115,52 @@ clapper_init_check (int *argc, char **argv[])
|
||||
{
|
||||
return clapper_init_check_internal (argc, argv);
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_enhancer_check:
|
||||
* @iface_type: an interface #GType
|
||||
* @scheme: an URI scheme
|
||||
* @host: (nullable): an URI host
|
||||
* @name: (out) (optional) (transfer none): return location for found enhancer name
|
||||
*
|
||||
* Check if an enhancer of @type is available for given @scheme and @host.
|
||||
*
|
||||
* A check that compares requested capabilites of all available Clapper enhancers,
|
||||
* thus it is fast but does not guarantee that the found one will succeed. Please note
|
||||
* that this function will always return %FALSE if Clapper was built without enhancers
|
||||
* loader functionality. To check that, use [const@Clapper.WITH_ENHANCERS_LOADER].
|
||||
*
|
||||
* This function can be used to quickly determine early if Clapper will at least try to
|
||||
* handle URI and with one of its enhancers and which one.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* gboolean supported = clapper_enhancer_check (CLAPPER_TYPE_EXTRACTABLE, "https", "example.com", NULL);
|
||||
* ```
|
||||
*
|
||||
* For self hosted services a custom URI @scheme without @host can be used. Enhancers should announce
|
||||
* support for such schemes by defining them in their plugin info files.
|
||||
*
|
||||
* ```c
|
||||
* gboolean supported = clapper_enhancer_check (CLAPPER_TYPE_EXTRACTABLE, "example", NULL, NULL);
|
||||
* ```
|
||||
*
|
||||
* Returns: whether a plausible enhancer was found.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
gboolean
|
||||
clapper_enhancer_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name)
|
||||
{
|
||||
gboolean success = FALSE;
|
||||
|
||||
g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_type), FALSE);
|
||||
g_return_val_if_fail (scheme != NULL, FALSE);
|
||||
|
||||
#if CLAPPER_WITH_ENHANCERS_LOADER
|
||||
success = clapper_enhancers_loader_check (iface_type, scheme, host, name);
|
||||
#endif
|
||||
|
||||
return success;
|
||||
}
|
||||
|
@@ -19,6 +19,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#define __CLAPPER_INSIDE__
|
||||
|
||||
#include <clapper/clapper-visibility.h>
|
||||
@@ -28,6 +31,7 @@
|
||||
|
||||
#include <clapper/clapper-audio-stream.h>
|
||||
#include <clapper/clapper-feature.h>
|
||||
#include <clapper/clapper-harvest.h>
|
||||
#include <clapper/clapper-marker.h>
|
||||
#include <clapper/clapper-media-item.h>
|
||||
#include <clapper/clapper-player.h>
|
||||
@@ -40,6 +44,9 @@
|
||||
#include <clapper/clapper-utils.h>
|
||||
#include <clapper/clapper-video-stream.h>
|
||||
|
||||
#include <clapper/clapper-extractable.h>
|
||||
|
||||
#include <clapper/clapper-functionalities-availability.h>
|
||||
#include <clapper/features/clapper-features-availability.h>
|
||||
|
||||
#if CLAPPER_HAVE_DISCOVERER
|
||||
@@ -60,6 +67,9 @@ void clapper_init (int *argc, char **argv[]);
|
||||
CLAPPER_API
|
||||
gboolean clapper_init_check (int *argc, char **argv[]);
|
||||
|
||||
CLAPPER_API
|
||||
gboolean clapper_enhancer_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#undef __CLAPPER_INSIDE__
|
||||
|
@@ -14,7 +14,7 @@ clapper_possible_features = [
|
||||
foreach feature_name : clapper_possible_features
|
||||
subdir(feature_name)
|
||||
features_availability_conf.set(
|
||||
'CLAPPER_HAVE_@0@'.format(feature_name.to_upper()),
|
||||
'CLAPPER_HAVE_@0@'.format(feature_name.replace('-', '_').to_upper()),
|
||||
clapper_available_features.contains(feature_name) ? 'TRUE' : 'FALSE'
|
||||
)
|
||||
endforeach
|
||||
|
43
src/lib/clapper/gst/clapper-enhancer-director-private.h
Normal file
43
src/lib/clapper/gst/clapper-enhancer-director-private.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "../clapper-threaded-object.h"
|
||||
#include "../clapper-harvest.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CLAPPER_TYPE_ENHANCER_DIRECTOR (clapper_enhancer_director_get_type())
|
||||
#define CLAPPER_ENHANCER_DIRECTOR_CAST(obj) ((ClapperEnhancerDirector *)(obj))
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
G_DECLARE_FINAL_TYPE (ClapperEnhancerDirector, clapper_enhancer_director, CLAPPER, ENHANCER_DIRECTOR, ClapperThreadedObject)
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
ClapperEnhancerDirector * clapper_enhancer_director_new (void);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
ClapperHarvest * clapper_enhancer_director_extract (ClapperEnhancerDirector *director, GUri *uri, GCancellable *cancellable, GError **error);
|
||||
|
||||
G_END_DECLS
|
171
src/lib/clapper/gst/clapper-enhancer-director.c
Normal file
171
src/lib/clapper/gst/clapper-enhancer-director.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "clapper-enhancer-director-private.h"
|
||||
#include "../clapper-enhancers-loader-private.h"
|
||||
#include "../clapper-extractable-private.h"
|
||||
#include "../clapper-harvest-private.h"
|
||||
#include "../../shared/clapper-shared-utils-private.h"
|
||||
|
||||
#define GST_CAT_DEFAULT clapper_enhancer_director_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
struct _ClapperEnhancerDirector
|
||||
{
|
||||
ClapperThreadedObject parent;
|
||||
};
|
||||
|
||||
#define parent_class clapper_enhancer_director_parent_class
|
||||
G_DEFINE_TYPE (ClapperEnhancerDirector, clapper_enhancer_director, CLAPPER_TYPE_THREADED_OBJECT);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GUri *uri;
|
||||
GCancellable *cancellable;
|
||||
GError **error;
|
||||
} ClapperEnhancerDirectorData;
|
||||
|
||||
static gpointer
|
||||
clapper_enhancer_director_extract_in_thread (ClapperEnhancerDirectorData *data)
|
||||
{
|
||||
ClapperExtractable *extractable = NULL;
|
||||
ClapperHarvest *harvest = clapper_harvest_new ();
|
||||
gboolean success = FALSE, cached = FALSE;
|
||||
|
||||
/* Cancelled during thread switching */
|
||||
if (g_cancellable_is_cancelled (data->cancellable))
|
||||
goto finish;
|
||||
|
||||
/* TODO: Cache lookup */
|
||||
if (cached) {
|
||||
// success = fill harvest from cache
|
||||
goto finish;
|
||||
}
|
||||
|
||||
extractable = CLAPPER_EXTRACTABLE_CAST (clapper_enhancers_loader_create_enhancer_for_uri (
|
||||
CLAPPER_TYPE_EXTRACTABLE, data->uri));
|
||||
|
||||
/* Check just before extract */
|
||||
if (g_cancellable_is_cancelled (data->cancellable))
|
||||
goto finish;
|
||||
|
||||
success = clapper_extractable_extract (extractable, data->uri,
|
||||
harvest, data->cancellable, data->error);
|
||||
|
||||
/* Cancelled during extract */
|
||||
if (g_cancellable_is_cancelled (data->cancellable)) {
|
||||
success = FALSE;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
finish:
|
||||
if (success) {
|
||||
if (!cached) {
|
||||
/* TODO: Store in cache */
|
||||
}
|
||||
} else {
|
||||
gst_clear_object (&harvest);
|
||||
|
||||
/* Ensure we have some error set on failure */
|
||||
if (*data->error == NULL) {
|
||||
const gchar *err_msg = (g_cancellable_is_cancelled (data->cancellable))
|
||||
? "Extraction was cancelled"
|
||||
: "Extraction failed";
|
||||
g_set_error (data->error, GST_RESOURCE_ERROR,
|
||||
GST_RESOURCE_ERROR_FAILED, "%s", err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
gst_clear_object (&extractable);
|
||||
|
||||
return harvest;
|
||||
}
|
||||
|
||||
/*
|
||||
* clapper_enhancer_director_new:
|
||||
*
|
||||
* Returns: (transfer full): a new #ClapperEnhancerDirector instance.
|
||||
*/
|
||||
ClapperEnhancerDirector *
|
||||
clapper_enhancer_director_new (void)
|
||||
{
|
||||
ClapperEnhancerDirector *director;
|
||||
|
||||
director = g_object_new (CLAPPER_TYPE_ENHANCER_DIRECTOR, NULL);
|
||||
gst_object_ref_sink (director);
|
||||
|
||||
return director;
|
||||
}
|
||||
|
||||
ClapperHarvest *
|
||||
clapper_enhancer_director_extract (ClapperEnhancerDirector *self, GUri *uri,
|
||||
GCancellable *cancellable, GError **error)
|
||||
{
|
||||
ClapperEnhancerDirectorData *data = g_new (ClapperEnhancerDirectorData, 1);
|
||||
|
||||
data->uri = uri;
|
||||
data->cancellable = cancellable;
|
||||
data->error = error;
|
||||
|
||||
return CLAPPER_HARVEST_CAST (clapper_shared_utils_context_invoke_sync_full (
|
||||
clapper_threaded_object_get_context (CLAPPER_THREADED_OBJECT_CAST (self)),
|
||||
(GThreadFunc) clapper_enhancer_director_extract_in_thread,
|
||||
data, (GDestroyNotify) g_free));
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_director_thread_start (ClapperThreadedObject *threaded_object)
|
||||
{
|
||||
GST_TRACE_OBJECT (threaded_object, "Enhancer director thread start");
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_director_thread_stop (ClapperThreadedObject *threaded_object)
|
||||
{
|
||||
GST_TRACE_OBJECT (threaded_object, "Enhancer director thread stop");
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_director_init (ClapperEnhancerDirector *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_director_finalize (GObject *object)
|
||||
{
|
||||
GST_TRACE_OBJECT (object, "Finalize");
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_director_class_init (ClapperEnhancerDirectorClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
ClapperThreadedObjectClass *threaded_object = (ClapperThreadedObjectClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperenhancerdirector", 0,
|
||||
"Clapper Enhancer Director");
|
||||
|
||||
gobject_class->finalize = clapper_enhancer_director_finalize;
|
||||
|
||||
threaded_object->thread_start = clapper_enhancer_director_thread_start;
|
||||
threaded_object->thread_stop = clapper_enhancer_director_thread_stop;
|
||||
}
|
36
src/lib/clapper/gst/clapper-enhancer-src-private.h
Normal file
36
src/lib/clapper/gst/clapper-enhancer-src-private.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstpushsrc.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CLAPPER_TYPE_ENHANCER_SRC (clapper_enhancer_src_get_type())
|
||||
#define CLAPPER_ENHANCER_SRC_CAST(obj) ((ClapperEnhancerSrc *)(obj))
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
G_DECLARE_FINAL_TYPE (ClapperEnhancerSrc, clapper_enhancer_src, CLAPPER, ENHANCER_SRC, GstPushSrc)
|
||||
|
||||
GST_ELEMENT_REGISTER_DECLARE (clapperenhancersrc)
|
||||
|
||||
G_END_DECLS
|
495
src/lib/clapper/gst/clapper-enhancer-src.c
Normal file
495
src/lib/clapper/gst/clapper-enhancer-src.c
Normal file
@@ -0,0 +1,495 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "clapper-enhancer-src-private.h"
|
||||
#include "clapper-enhancer-director-private.h"
|
||||
|
||||
#include "../clapper-extractable-private.h"
|
||||
#include "../clapper-harvest-private.h"
|
||||
#include "../clapper-enhancers-loader-private.h"
|
||||
|
||||
#define GST_CAT_DEFAULT clapper_enhancer_src_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
struct _ClapperEnhancerSrc
|
||||
{
|
||||
GstPushSrc parent;
|
||||
|
||||
GCancellable *cancellable;
|
||||
gsize buf_size;
|
||||
|
||||
ClapperEnhancerDirector *director;
|
||||
|
||||
gchar *uri;
|
||||
GUri *guri;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_URI,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
|
||||
|
||||
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
static GstURIType
|
||||
clapper_enhancer_src_uri_handler_get_type (GType type)
|
||||
{
|
||||
return GST_URI_SRC;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
_get_schemes_once (gpointer user_data G_GNUC_UNUSED)
|
||||
{
|
||||
return clapper_enhancers_loader_get_schemes (CLAPPER_TYPE_EXTRACTABLE);
|
||||
}
|
||||
|
||||
static const gchar *const *
|
||||
clapper_enhancer_src_uri_handler_get_protocols (GType type)
|
||||
{
|
||||
static GOnce schemes_once = G_ONCE_INIT;
|
||||
|
||||
g_once (&schemes_once, _get_schemes_once, NULL);
|
||||
return (const gchar *const *) schemes_once.retval;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
clapper_enhancer_src_uri_handler_get_uri (GstURIHandler *handler)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (handler);
|
||||
gchar *uri;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
uri = g_strdup (self->uri);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_enhancer_src_uri_handler_set_uri (GstURIHandler *handler,
|
||||
const gchar *uri, GError **error)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (handler);
|
||||
GUri *guri;
|
||||
const gchar *const *protocols;
|
||||
gboolean supported = FALSE;
|
||||
guint i;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Changing URI to: %s", uri);
|
||||
|
||||
if (!uri) {
|
||||
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
|
||||
"URI property cannot be NULL");
|
||||
return FALSE;
|
||||
}
|
||||
if (GST_STATE (GST_ELEMENT_CAST (self)) >= GST_STATE_PAUSED) {
|
||||
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
|
||||
"Cannot change URI property while element is running");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
protocols = gst_uri_handler_get_protocols (handler);
|
||||
for (i = 0; protocols[i]; ++i) {
|
||||
if ((supported = gst_uri_has_protocol (uri, protocols[i])))
|
||||
break;
|
||||
}
|
||||
if (!supported) {
|
||||
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_UNSUPPORTED_PROTOCOL,
|
||||
"URI protocol is not supported");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(guri = g_uri_parse (uri, G_URI_FLAGS_ENCODED, NULL))) {
|
||||
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
|
||||
"URI is invalid");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!clapper_enhancers_loader_check (CLAPPER_TYPE_EXTRACTABLE,
|
||||
g_uri_get_scheme (guri), g_uri_get_host (guri), NULL)) {
|
||||
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
|
||||
"None of the available enhancers can handle this URI");
|
||||
g_uri_unref (guri);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
g_set_str (&self->uri, uri);
|
||||
g_clear_pointer (&self->guri, g_uri_unref);
|
||||
self->guri = guri;
|
||||
|
||||
GST_INFO_OBJECT (self, "URI changed to: \"%s\"", self->uri);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_uri_handler_iface_init (GstURIHandlerInterface *iface)
|
||||
{
|
||||
iface->get_type = clapper_enhancer_src_uri_handler_get_type;
|
||||
iface->get_protocols = clapper_enhancer_src_uri_handler_get_protocols;
|
||||
iface->get_uri = clapper_enhancer_src_uri_handler_get_uri;
|
||||
iface->set_uri = clapper_enhancer_src_uri_handler_set_uri;
|
||||
}
|
||||
|
||||
#define parent_class clapper_enhancer_src_parent_class
|
||||
G_DEFINE_TYPE_WITH_CODE (ClapperEnhancerSrc, clapper_enhancer_src, GST_TYPE_PUSH_SRC,
|
||||
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, _uri_handler_iface_init));
|
||||
GST_ELEMENT_REGISTER_DEFINE (clapperenhancersrc, "clapperenhancersrc",
|
||||
512, CLAPPER_TYPE_ENHANCER_SRC);
|
||||
|
||||
static gboolean
|
||||
clapper_enhancer_src_start (GstBaseSrc *base_src)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (base_src);
|
||||
gboolean can_start;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Start");
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
can_start = (self->guri != NULL);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (G_UNLIKELY (!can_start)) {
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
|
||||
("No media URI"), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_enhancer_src_stop (GstBaseSrc *base_src)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (base_src);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stop");
|
||||
|
||||
self->buf_size = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_enhancer_src_get_size (GstBaseSrc *base_src, guint64 *size)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (base_src);
|
||||
|
||||
if (self->buf_size > 0) {
|
||||
*size = self->buf_size;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_enhancer_src_is_seekable (GstBaseSrc *base_src)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_enhancer_src_unlock (GstBaseSrc *base_src)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (base_src);
|
||||
|
||||
GST_LOG_OBJECT (self, "Cancel triggered");
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_enhancer_src_unlock_stop (GstBaseSrc *base_src)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (base_src);
|
||||
|
||||
GST_LOG_OBJECT (self, "Resetting cancellable");
|
||||
|
||||
g_object_unref (self->cancellable);
|
||||
self->cancellable = g_cancellable_new ();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Pushes tags, toc and request headers downstream (all transfer full) */
|
||||
static void
|
||||
_push_events (ClapperEnhancerSrc *self, GstTagList *tags, GstToc *toc,
|
||||
GstStructure *headers, gboolean updated)
|
||||
{
|
||||
GstEvent *event;
|
||||
|
||||
if (tags) {
|
||||
if (!gst_tag_list_is_empty (tags)) {
|
||||
GST_DEBUG_OBJECT (self, "Pushing %" GST_PTR_FORMAT, tags);
|
||||
|
||||
/* XXX: Normally, we should only be posting event to make it reach
|
||||
* the app after stream start, but currently it is lost that way */
|
||||
gst_element_post_message (GST_ELEMENT (self),
|
||||
gst_message_new_tag (GST_OBJECT_CAST (self), tags));
|
||||
} else {
|
||||
gst_tag_list_unref (tags);
|
||||
}
|
||||
}
|
||||
|
||||
if (toc) {
|
||||
if (g_list_length (gst_toc_get_entries (toc)) > 0) {
|
||||
GST_DEBUG_OBJECT (self, "Pushing TOC"); // TOC is not printable
|
||||
|
||||
/* XXX: Normally, we should only be posting event to make it reach
|
||||
* the app after stream start, but currently it is lost that way */
|
||||
gst_element_post_message (GST_ELEMENT (self),
|
||||
gst_message_new_toc (GST_OBJECT_CAST (self), toc, updated));
|
||||
}
|
||||
gst_toc_unref (toc);
|
||||
}
|
||||
|
||||
if (headers) {
|
||||
GstStructure *http_headers;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Pushing %" GST_PTR_FORMAT, headers);
|
||||
|
||||
http_headers = gst_structure_new ("http-headers",
|
||||
"request-headers", GST_TYPE_STRUCTURE, headers,
|
||||
NULL);
|
||||
gst_structure_free (headers);
|
||||
|
||||
event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, http_headers);
|
||||
gst_pad_push_event (GST_BASE_SRC_PAD (self), event);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Pushed all events");
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
clapper_enhancer_src_create (GstPushSrc *push_src, GstBuffer **outbuf)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (push_src);
|
||||
GUri *guri;
|
||||
GCancellable *cancellable;
|
||||
ClapperHarvest *harvest;
|
||||
GstCaps *caps = NULL;
|
||||
GstTagList *tags = NULL;
|
||||
GstToc *toc = NULL;
|
||||
GstStructure *headers = NULL;
|
||||
GError *error = NULL;
|
||||
gboolean unpacked;
|
||||
|
||||
/* When non-zero, we already returned complete data */
|
||||
if (self->buf_size > 0)
|
||||
return GST_FLOW_EOS;
|
||||
|
||||
/* Ensure director is created. Since it spins up its own
|
||||
* thread, create it here as we know that it will be used. */
|
||||
if (!self->director)
|
||||
self->director = clapper_enhancer_director_new ();
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
guri = g_uri_ref (self->guri);
|
||||
cancellable = g_object_ref (self->cancellable);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
harvest = clapper_enhancer_director_extract (self->director, guri, cancellable, &error);
|
||||
|
||||
g_uri_unref (guri);
|
||||
g_object_unref (cancellable);
|
||||
|
||||
if (!harvest) {
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("%s", error->message), (NULL));
|
||||
g_clear_error (&error);
|
||||
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
unpacked = clapper_harvest_unpack (harvest, outbuf, &self->buf_size,
|
||||
&caps, &tags, &toc, &headers);
|
||||
gst_object_unref (harvest);
|
||||
|
||||
if (!unpacked) {
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("Extraction harvest is empty"), (NULL));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
if (gst_base_src_set_caps (GST_BASE_SRC (self), caps))
|
||||
GST_INFO_OBJECT (self, "Using caps: %" GST_PTR_FORMAT, caps);
|
||||
else
|
||||
GST_ERROR_OBJECT (self, "Current caps could not be set");
|
||||
|
||||
gst_clear_caps (&caps);
|
||||
|
||||
/* Now push all events before buffer */
|
||||
_push_events (self, tags, toc, headers, FALSE);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
_handle_uri_query (GstQuery *query)
|
||||
{
|
||||
/* Since our URI does not actually lead to manifest data, we answer
|
||||
* with "nodata" equivalent, so upstream will not try to fetch it */
|
||||
gst_query_set_uri (query, "data:,");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_enhancer_src_query (GstBaseSrc *base_src, GstQuery *query)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_URI:
|
||||
ret = _handle_uri_query (query);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = GST_BASE_SRC_CLASS (parent_class)->query (base_src, query);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_src_init (ClapperEnhancerSrc *self)
|
||||
{
|
||||
self->cancellable = g_cancellable_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_src_dispose (GObject *object)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_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_enhancer_src_finalize (GObject *object)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (object);
|
||||
|
||||
GST_TRACE_OBJECT (self, "Finalize");
|
||||
|
||||
g_clear_object (&self->cancellable);
|
||||
g_free (self->uri);
|
||||
g_clear_pointer (&self->guri, g_uri_unref);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_src_set_property (GObject *object, guint prop_id,
|
||||
const GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_URI:{
|
||||
GError *error = NULL;
|
||||
if (!gst_uri_handler_set_uri (GST_URI_HANDLER (self),
|
||||
g_value_get_string (value), &error)) {
|
||||
GST_ERROR_OBJECT (self, "%s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_src_get_property (GObject *object, guint prop_id,
|
||||
GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
ClapperEnhancerSrc *self = CLAPPER_ENHANCER_SRC_CAST (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_URI:
|
||||
g_value_take_string (value, gst_uri_handler_get_uri (GST_URI_HANDLER (self)));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_enhancer_src_class_init (ClapperEnhancerSrcClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||
GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass;
|
||||
GstPushSrcClass *gstpushsrc_class = (GstPushSrcClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperenhancersrc", 0,
|
||||
"Clapper Enhancer Source");
|
||||
|
||||
gobject_class->set_property = clapper_enhancer_src_set_property;
|
||||
gobject_class->get_property = clapper_enhancer_src_get_property;
|
||||
gobject_class->dispose = clapper_enhancer_src_dispose;
|
||||
gobject_class->finalize = clapper_enhancer_src_finalize;
|
||||
|
||||
gstbasesrc_class->start = clapper_enhancer_src_start;
|
||||
gstbasesrc_class->stop = clapper_enhancer_src_stop;
|
||||
gstbasesrc_class->get_size = clapper_enhancer_src_get_size;
|
||||
gstbasesrc_class->is_seekable = clapper_enhancer_src_is_seekable;
|
||||
gstbasesrc_class->unlock = clapper_enhancer_src_unlock;
|
||||
gstbasesrc_class->unlock_stop = clapper_enhancer_src_unlock_stop;
|
||||
gstbasesrc_class->query = clapper_enhancer_src_query;
|
||||
|
||||
gstpushsrc_class->create = clapper_enhancer_src_create;
|
||||
|
||||
param_specs[PROP_URI] = g_param_spec_string ("uri",
|
||||
"URI", "URI", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
|
||||
|
||||
gst_element_class_add_static_pad_template (gstelement_class, &src_template);
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class, "Clapper Enhancer Source",
|
||||
"Source", "A source element that uses Clapper Enhancers to produce data",
|
||||
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
||||
}
|
30
src/lib/clapper/gst/clapper-plugin-private.h
Normal file
30
src/lib/clapper/gst/clapper-plugin-private.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_gst_plugin_init (GstPlugin *plugin);
|
||||
|
||||
G_END_DECLS
|
53
src/lib/clapper/gst/clapper-plugin.c
Normal file
53
src/lib/clapper/gst/clapper-plugin.c
Normal file
@@ -0,0 +1,53 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "clapper-plugin-private.h"
|
||||
#include "../clapper-functionalities-availability.h"
|
||||
|
||||
#if CLAPPER_WITH_ENHANCERS_LOADER
|
||||
#include "clapper-enhancer-src-private.h"
|
||||
#include "../clapper-extractable-private.h"
|
||||
#include "../clapper-enhancers-loader-private.h"
|
||||
#endif
|
||||
|
||||
#include "clapper-uri-list-demux-private.h"
|
||||
|
||||
gboolean
|
||||
clapper_gst_plugin_init (GstPlugin *plugin)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
|
||||
#if CLAPPER_WITH_ENHANCERS_LOADER
|
||||
gst_plugin_add_dependency_simple (plugin,
|
||||
"CLAPPER_ENHANCERS_PATH", CLAPPER_ENHANCERS_PATH, NULL,
|
||||
GST_PLUGIN_DEPENDENCY_FLAG_PATHS_ARE_DEFAULT_ONLY);
|
||||
|
||||
/* Avoid registering an URI handler without schemes */
|
||||
if (clapper_enhancers_loader_has_enhancers (CLAPPER_TYPE_EXTRACTABLE))
|
||||
res |= GST_ELEMENT_REGISTER (clapperenhancersrc, plugin);
|
||||
#endif
|
||||
|
||||
res |= GST_ELEMENT_REGISTER (clapperurilistdemux, plugin);
|
||||
|
||||
return res;
|
||||
}
|
36
src/lib/clapper/gst/clapper-uri-list-demux-private.h
Normal file
36
src/lib/clapper/gst/clapper-uri-list-demux-private.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/gstbin.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CLAPPER_TYPE_URI_LIST_DEMUX (clapper_uri_list_demux_get_type())
|
||||
#define CLAPPER_URI_LIST_DEMUX_CAST(obj) ((ClapperUriListDemux *)(obj))
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
G_DECLARE_FINAL_TYPE (ClapperUriListDemux, clapper_uri_list_demux, CLAPPER, URI_LIST_DEMUX, GstBin)
|
||||
|
||||
GST_ELEMENT_REGISTER_DECLARE (clapperurilistdemux)
|
||||
|
||||
G_END_DECLS
|
462
src/lib/clapper/gst/clapper-uri-list-demux.c
Normal file
462
src/lib/clapper/gst/clapper-uri-list-demux.c
Normal file
@@ -0,0 +1,462 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/base/gstadapter.h>
|
||||
|
||||
#include "clapper-uri-list-demux-private.h"
|
||||
|
||||
#define GST_CAT_DEFAULT clapper_uri_list_demux_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
struct _ClapperUriListDemux
|
||||
{
|
||||
GstBin parent;
|
||||
|
||||
GMutex lock;
|
||||
|
||||
GstAdapter *input_adapter;
|
||||
|
||||
GstElement *uri_handler;
|
||||
GstElement *typefind;
|
||||
|
||||
GstPad *typefind_src;
|
||||
|
||||
GstStructure *http_headers;
|
||||
};
|
||||
|
||||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("text/uri-list, source=(string)clapper-harvest"));
|
||||
|
||||
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_SOMETIMES,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
#define parent_class clapper_uri_list_demux_parent_class
|
||||
G_DEFINE_TYPE (ClapperUriListDemux, clapper_uri_list_demux, GST_TYPE_BIN);
|
||||
GST_ELEMENT_REGISTER_DEFINE (clapperurilistdemux, "clapperurilistdemux",
|
||||
512, CLAPPER_TYPE_URI_LIST_DEMUX);
|
||||
|
||||
static void
|
||||
_set_property (GstObject *obj, const gchar *prop_name, gpointer value)
|
||||
{
|
||||
g_object_set (G_OBJECT (obj), prop_name, value, NULL);
|
||||
|
||||
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG) {
|
||||
gchar *el_name;
|
||||
|
||||
el_name = gst_object_get_name (obj);
|
||||
GST_DEBUG ("Set %s %s", el_name, prop_name);
|
||||
|
||||
g_free (el_name);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
configure_deep_element (GQuark field_id, const GValue *value, GstElement *child)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
const GstStructure *substructure;
|
||||
|
||||
if (!GST_VALUE_HOLDS_STRUCTURE (value))
|
||||
return TRUE;
|
||||
|
||||
substructure = gst_value_get_structure (value);
|
||||
|
||||
if (!gst_structure_has_name (substructure, "request-headers"))
|
||||
return TRUE;
|
||||
|
||||
gobject_class = G_OBJECT_GET_CLASS (child);
|
||||
|
||||
if (g_object_class_find_property (gobject_class, "user-agent")) {
|
||||
const gchar *ua;
|
||||
|
||||
if ((ua = gst_structure_get_string (substructure, "User-Agent")))
|
||||
_set_property (GST_OBJECT_CAST (child), "user-agent", (gchar *) ua);
|
||||
}
|
||||
|
||||
if (g_object_class_find_property (gobject_class, "extra-headers")) {
|
||||
GstStructure *extra_headers;
|
||||
|
||||
extra_headers = gst_structure_copy (substructure);
|
||||
gst_structure_set_name (extra_headers, "extra-headers");
|
||||
gst_structure_remove_field (extra_headers, "User-Agent");
|
||||
|
||||
_set_property (GST_OBJECT_CAST (child), "extra-headers", extra_headers);
|
||||
|
||||
gst_structure_free (extra_headers);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_uri_list_demux_deep_element_added (GstBin *bin, GstBin *sub_bin, GstElement *child)
|
||||
{
|
||||
if (GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_FLAG_SOURCE)) {
|
||||
ClapperUriListDemux *self = CLAPPER_URI_LIST_DEMUX_CAST (bin);
|
||||
|
||||
g_mutex_lock (&self->lock);
|
||||
|
||||
if (self->http_headers) {
|
||||
gst_structure_foreach (self->http_headers,
|
||||
(GstStructureForeachFunc) configure_deep_element, child);
|
||||
}
|
||||
|
||||
g_mutex_unlock (&self->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_sometimes_pad_cb (GstElement *element, GstPad *pad, ClapperUriListDemux *self)
|
||||
{
|
||||
GstPadTemplate *template = gst_pad_get_pad_template (pad);
|
||||
GstPadPresence presence = GST_PAD_TEMPLATE_PRESENCE (template);
|
||||
|
||||
gst_object_unref (template);
|
||||
|
||||
if (presence == GST_PAD_SOMETIMES) {
|
||||
GST_DEBUG_OBJECT (self, "Removing src pad");
|
||||
|
||||
gst_pad_set_active (pad, FALSE);
|
||||
|
||||
if (G_UNLIKELY (!gst_element_remove_pad (element, pad)))
|
||||
g_critical ("Failed to remove pad from bin");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_uri_list_demux_reset (ClapperUriListDemux *self)
|
||||
{
|
||||
GstElement *element = GST_ELEMENT_CAST (self);
|
||||
|
||||
gst_element_foreach_pad (element, (GstElementForeachPadFunc) remove_sometimes_pad_cb, NULL);
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
clapper_uri_list_demux_change_state (GstElement *element, GstStateChange transition)
|
||||
{
|
||||
ClapperUriListDemux *self = CLAPPER_URI_LIST_DEMUX_CAST (element);
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
return ret;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
clapper_uri_list_demux_reset (self);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_feature_filter (GstPluginFeature *feature, const gchar *search_proto)
|
||||
{
|
||||
GstElementFactory *factory;
|
||||
const gchar *const *protocols;
|
||||
const gchar *feature_name;
|
||||
guint i;
|
||||
|
||||
if (!GST_IS_ELEMENT_FACTORY (feature))
|
||||
return FALSE;
|
||||
|
||||
factory = GST_ELEMENT_FACTORY_CAST (feature);
|
||||
|
||||
if (gst_element_factory_get_uri_type (factory) != GST_URI_SRC)
|
||||
return FALSE;
|
||||
|
||||
feature_name = gst_plugin_feature_get_name (feature);
|
||||
|
||||
/* Do not loop endlessly creating our own sources and demuxers */
|
||||
if (!feature_name || strcmp (feature_name, "clapperenhancersrc") == 0)
|
||||
return FALSE;
|
||||
|
||||
protocols = gst_element_factory_get_uri_protocols (factory);
|
||||
|
||||
if (protocols) {
|
||||
for (i = 0; protocols[i]; ++i) {
|
||||
if (g_ascii_strcasecmp (protocols[i], search_proto) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
_make_handler_for_uri (ClapperUriListDemux *self, const gchar *uri)
|
||||
{
|
||||
GstElement *element = NULL;
|
||||
GList *factories, *f;
|
||||
gchar *protocol;
|
||||
|
||||
if (!gst_uri_is_valid (uri)) {
|
||||
GST_ERROR_OBJECT (self, "Cannot create handler for invalid URI: \"%s\"", uri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
protocol = gst_uri_get_protocol (uri);
|
||||
factories = gst_registry_feature_filter (gst_registry_get (),
|
||||
(GstPluginFeatureFilter) _feature_filter, FALSE, protocol);
|
||||
g_free (protocol);
|
||||
|
||||
factories = g_list_sort (factories,
|
||||
(GCompareFunc) gst_plugin_feature_rank_compare_func);
|
||||
|
||||
for (f = factories; f; f = g_list_next (f)) {
|
||||
GstElementFactory *factory = f->data;
|
||||
|
||||
if ((element = gst_element_factory_create (factory, NULL))
|
||||
&& gst_uri_handler_set_uri (GST_URI_HANDLER (element), uri, NULL))
|
||||
break;
|
||||
|
||||
gst_clear_object (&element);
|
||||
}
|
||||
|
||||
gst_plugin_feature_list_free (factories);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Created URI handler: %s",
|
||||
GST_OBJECT_NAME (element));
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_uri_list_demux_process_buffer (ClapperUriListDemux *self, GstBuffer *buffer)
|
||||
{
|
||||
GstMemory *mem;
|
||||
GstMapInfo info;
|
||||
|
||||
mem = gst_buffer_peek_memory (buffer, 0);
|
||||
|
||||
if (mem && gst_memory_map (mem, &info, GST_MAP_READ)) {
|
||||
GstPad *uri_handler_src, *typefind_sink, *src_ghostpad;
|
||||
GstPadLinkReturn pad_link_ret;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stream URI: %s", (const gchar *) info.data);
|
||||
|
||||
if (self->uri_handler) {
|
||||
GST_DEBUG_OBJECT (self, "Trying to reuse existing URI handler");
|
||||
|
||||
if (gst_uri_handler_set_uri (GST_URI_HANDLER (self->uri_handler),
|
||||
(const gchar *) info.data, NULL)) {
|
||||
GST_DEBUG_OBJECT (self, "Reused existing URI handler");
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (self, "Could not reuse existing URI handler");
|
||||
|
||||
if (self->typefind_src) {
|
||||
gst_element_remove_pad (GST_ELEMENT_CAST (self), self->typefind_src);
|
||||
gst_clear_object (&self->typefind_src);
|
||||
}
|
||||
|
||||
gst_bin_remove (GST_BIN_CAST (self), self->uri_handler);
|
||||
gst_bin_remove (GST_BIN_CAST (self), self->typefind);
|
||||
|
||||
self->uri_handler = NULL;
|
||||
self->typefind = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self->uri_handler) {
|
||||
GST_DEBUG_OBJECT (self, "Creating new URI handler element");
|
||||
|
||||
self->uri_handler = _make_handler_for_uri (self, (const gchar *) info.data);
|
||||
|
||||
if (G_UNLIKELY (!self->uri_handler)) {
|
||||
GST_ERROR_OBJECT (self, "Could not create URI handler element");
|
||||
|
||||
GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN,
|
||||
("Missing plugin to handle URI: %s", info.data), (NULL));
|
||||
gst_memory_unmap (mem, &info);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_bin_add (GST_BIN_CAST (self), self->uri_handler);
|
||||
|
||||
self->typefind = gst_element_factory_make ("typefind", NULL);
|
||||
gst_bin_add (GST_BIN_CAST (self), self->typefind);
|
||||
|
||||
uri_handler_src = gst_element_get_static_pad (self->uri_handler, "src");
|
||||
typefind_sink = gst_element_get_static_pad (self->typefind, "sink");
|
||||
|
||||
pad_link_ret = gst_pad_link_full (uri_handler_src, typefind_sink,
|
||||
GST_PAD_LINK_CHECK_NOTHING);
|
||||
|
||||
if (pad_link_ret != GST_PAD_LINK_OK)
|
||||
g_critical ("Failed to link bin elements");
|
||||
|
||||
g_object_unref (uri_handler_src);
|
||||
g_object_unref (typefind_sink);
|
||||
|
||||
self->typefind_src = gst_element_get_static_pad (self->typefind, "src");
|
||||
|
||||
src_ghostpad = gst_ghost_pad_new_from_template ("src", self->typefind_src,
|
||||
gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self), "src"));
|
||||
|
||||
gst_pad_set_active (src_ghostpad, TRUE);
|
||||
|
||||
if (!gst_element_add_pad (GST_ELEMENT_CAST (self), src_ghostpad)) {
|
||||
g_critical ("Failed to add source pad to bin");
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (self, "Added src pad, signalling \"no-more-pads\"");
|
||||
gst_element_no_more_pads (GST_ELEMENT_CAST (self));
|
||||
}
|
||||
}
|
||||
|
||||
gst_memory_unmap (mem, &info);
|
||||
|
||||
gst_element_sync_state_with_parent (self->typefind);
|
||||
gst_element_sync_state_with_parent (self->uri_handler);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_uri_list_demux_sink_event (GstPad *pad, GstObject *parent, GstEvent *event)
|
||||
{
|
||||
ClapperUriListDemux *self = CLAPPER_URI_LIST_DEMUX_CAST (parent);
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_EOS:{
|
||||
GstBuffer *buffer;
|
||||
gsize size;
|
||||
gboolean success;
|
||||
|
||||
size = gst_adapter_available (self->input_adapter);
|
||||
|
||||
if (size == 0) {
|
||||
GST_WARNING_OBJECT (self, "Received EOS without URI data");
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = gst_adapter_take_buffer (self->input_adapter, size);
|
||||
success = clapper_uri_list_demux_process_buffer (self, buffer);
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
if (success) {
|
||||
gst_event_unref (event);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
|
||||
const GstStructure *structure = gst_event_get_structure (event);
|
||||
|
||||
if (structure && gst_structure_has_name (structure, "http-headers")) {
|
||||
GST_DEBUG_OBJECT (self, "Received \"http-headers\" custom event");
|
||||
g_mutex_lock (&self->lock);
|
||||
|
||||
gst_clear_structure (&self->http_headers);
|
||||
self->http_headers = gst_structure_copy (structure);
|
||||
|
||||
g_mutex_unlock (&self->lock);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return gst_pad_event_default (pad, parent, event);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
clapper_uri_list_demux_sink_chain (GstPad *pad, GstObject *parent, GstBuffer *buffer)
|
||||
{
|
||||
ClapperUriListDemux *self = CLAPPER_URI_LIST_DEMUX_CAST (parent);
|
||||
|
||||
gst_adapter_push (self->input_adapter, buffer);
|
||||
GST_DEBUG_OBJECT (self, "Received buffer, total collected: %" G_GSIZE_FORMAT " bytes",
|
||||
gst_adapter_available (self->input_adapter));
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_uri_list_demux_init (ClapperUriListDemux *self)
|
||||
{
|
||||
GstPad *sink_pad;
|
||||
|
||||
g_mutex_init (&self->lock);
|
||||
|
||||
self->input_adapter = gst_adapter_new ();
|
||||
|
||||
sink_pad = gst_pad_new_from_template (gst_element_class_get_pad_template (
|
||||
GST_ELEMENT_GET_CLASS (self), "sink"), "sink");
|
||||
gst_pad_set_event_function (sink_pad,
|
||||
GST_DEBUG_FUNCPTR (clapper_uri_list_demux_sink_event));
|
||||
gst_pad_set_chain_function (sink_pad,
|
||||
GST_DEBUG_FUNCPTR (clapper_uri_list_demux_sink_chain));
|
||||
|
||||
gst_pad_set_active (sink_pad, TRUE);
|
||||
|
||||
if (!gst_element_add_pad (GST_ELEMENT_CAST (self), sink_pad))
|
||||
g_critical ("Failed to add sink pad to bin");
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_uri_list_demux_finalize (GObject *object)
|
||||
{
|
||||
ClapperUriListDemux *self = CLAPPER_URI_LIST_DEMUX_CAST (object);
|
||||
|
||||
GST_TRACE_OBJECT (self, "Finalize");
|
||||
|
||||
g_object_unref (self->input_adapter);
|
||||
gst_clear_object (&self->typefind_src);
|
||||
gst_clear_structure (&self->http_headers);
|
||||
|
||||
g_mutex_clear (&self->lock);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_uri_list_demux_class_init (ClapperUriListDemuxClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||
GstBinClass *gstbin_class = (GstBinClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperurilistdemux", 0,
|
||||
"Clapper URI List Demux");
|
||||
|
||||
gobject_class->finalize = clapper_uri_list_demux_finalize;
|
||||
|
||||
gstbin_class->deep_element_added = clapper_uri_list_demux_deep_element_added;
|
||||
|
||||
gstelement_class->change_state = clapper_uri_list_demux_change_state;
|
||||
|
||||
gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
|
||||
gst_element_class_add_static_pad_template (gstelement_class, &src_template);
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class, "Clapper URI List Demux",
|
||||
"Demuxer", "A custom demuxer for URI lists",
|
||||
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
clapper_dep = dependency('', required: false)
|
||||
clapper_option = get_option('clapper')
|
||||
clapper_headers_dir = join_paths(includedir, clapper_api_name, 'clapper')
|
||||
clapper_enhancers_dir = join_paths(clapper_libdir, 'enhancers')
|
||||
clapper_with_enhancers_loader = false
|
||||
build_clapper = false
|
||||
|
||||
clapper_pkg_reqs = [
|
||||
@@ -38,6 +40,27 @@ foreach dep : clapper_deps
|
||||
endif
|
||||
endforeach
|
||||
|
||||
# libpeas is an optional dependency
|
||||
enhancers_option = get_option('enhancers-loader')
|
||||
clapper_with_enhancers_loader = (not enhancers_option.disabled() and peas_dep.found())
|
||||
|
||||
if not clapper_with_enhancers_loader and enhancers_option.enabled()
|
||||
error('enhancers-loader option was enabled, but required dependencies were not found')
|
||||
endif
|
||||
|
||||
config_h = configuration_data()
|
||||
config_h.set_quoted('PACKAGE', meson.project_name())
|
||||
config_h.set_quoted('PACKAGE_VERSION', meson.project_version())
|
||||
config_h.set_quoted('PACKAGE_ORIGIN', 'https://github.com/Rafostar/clapper')
|
||||
config_h.set_quoted('PLUGIN_DESC', 'Clapper elements')
|
||||
config_h.set_quoted('PLUGIN_LICENSE', 'LGPL')
|
||||
config_h.set_quoted('CLAPPER_ENHANCERS_PATH', clapper_enhancers_dir)
|
||||
|
||||
configure_file(
|
||||
output: 'config.h',
|
||||
configuration: config_h,
|
||||
)
|
||||
|
||||
visibility_conf = configuration_data()
|
||||
|
||||
visibility_conf.set(
|
||||
@@ -86,7 +109,9 @@ clapper_headers = [
|
||||
'clapper.h',
|
||||
'clapper-enums.h',
|
||||
'clapper-audio-stream.h',
|
||||
'clapper-extractable.h',
|
||||
'clapper-feature.h',
|
||||
'clapper-harvest.h',
|
||||
'clapper-marker.h',
|
||||
'clapper-media-item.h',
|
||||
'clapper-player.h',
|
||||
@@ -105,9 +130,11 @@ clapper_sources = [
|
||||
'clapper.c',
|
||||
'clapper-app-bus.c',
|
||||
'clapper-audio-stream.c',
|
||||
'clapper-extractable.c',
|
||||
'clapper-feature.c',
|
||||
'clapper-features-bus.c',
|
||||
'clapper-features-manager.c',
|
||||
'clapper-harvest.c',
|
||||
'clapper-marker.c',
|
||||
'clapper-media-item.c',
|
||||
'clapper-playbin-bus.c',
|
||||
@@ -120,6 +147,8 @@ clapper_sources = [
|
||||
'clapper-timeline.c',
|
||||
'clapper-utils.c',
|
||||
'clapper-video-stream.c',
|
||||
'gst/clapper-plugin.c',
|
||||
'gst/clapper-uri-list-demux.c',
|
||||
'../shared/clapper-shared-utils.c',
|
||||
]
|
||||
clapper_c_args = [
|
||||
@@ -132,6 +161,36 @@ if get_option('default_library') == 'static'
|
||||
clapper_c_args += ['-DCLAPPER_STATIC_COMPILATION']
|
||||
endif
|
||||
|
||||
clapper_possible_functionalities = [
|
||||
'enhancers-loader',
|
||||
]
|
||||
clapper_available_functionalities = []
|
||||
|
||||
if clapper_with_enhancers_loader
|
||||
clapper_deps += peas_dep
|
||||
clapper_sources += [
|
||||
'clapper-enhancers-loader.c',
|
||||
'gst/clapper-enhancer-src.c',
|
||||
'gst/clapper-enhancer-director.c',
|
||||
]
|
||||
clapper_available_functionalities += 'enhancers-loader'
|
||||
endif
|
||||
|
||||
functionalities_availability_conf = configuration_data()
|
||||
|
||||
foreach functionality_name : clapper_possible_functionalities
|
||||
functionalities_availability_conf.set(
|
||||
'CLAPPER_WITH_@0@'.format(functionality_name.replace('-', '_').to_upper()),
|
||||
clapper_available_functionalities.contains(functionality_name) ? 'TRUE' : 'FALSE'
|
||||
)
|
||||
endforeach
|
||||
|
||||
clapper_headers += configure_file(
|
||||
input: 'clapper-functionalities-availability.h.in',
|
||||
output: 'clapper-functionalities-availability.h',
|
||||
configuration: functionalities_availability_conf,
|
||||
)
|
||||
|
||||
subdir('features')
|
||||
|
||||
clapper_enums = gnome.mkenums_simple(
|
||||
@@ -205,7 +264,7 @@ if build_vapi
|
||||
sources: clapper_gir[0],
|
||||
packages: clapper_pkg_reqs,
|
||||
metadata_dirs: [
|
||||
join_paths (meson.current_source_dir(), 'metadata')
|
||||
join_paths(meson.current_source_dir(), 'metadata')
|
||||
],
|
||||
install: true,
|
||||
)
|
||||
@@ -213,7 +272,9 @@ if build_vapi
|
||||
endif
|
||||
|
||||
clapper_pkgconfig_variables = [
|
||||
'enhancersdir=${libdir}/' + clapper_api_name + '/enhancers',
|
||||
'features=' + ' '.join(clapper_available_features),
|
||||
'functionalities=' + ' '.join(clapper_available_functionalities),
|
||||
]
|
||||
|
||||
pkgconfig.generate(clapper_lib,
|
||||
|
Reference in New Issue
Block a user