mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-29 15:22:11 +02:00
clapper: Implement data cache
Add data cache functions and use them to store enhancer data into local cache file. This way we can restore all properties and interfaces used in enhancer without creating its instance. This avoids loading interpreters like Python at init time making startup a lot faster.
This commit is contained in:
93
src/lib/clapper/clapper-cache-private.h
Normal file
93
src/lib/clapper/clapper-cache-private.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2025 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_cache_initialize (void);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GMappedFile * clapper_cache_open (const gchar *filename, const gchar **data, GError **error);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_cache_read_boolean (const gchar **data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gint clapper_cache_read_int (const gchar **data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
guint clapper_cache_read_uint (const gchar **data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gdouble clapper_cache_read_double (const gchar **data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
const gchar * clapper_cache_read_string (const gchar **data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType clapper_cache_read_enum (const gchar **data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType clapper_cache_read_flags (const gchar **data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType clapper_cache_read_iface (const gchar **data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GParamSpec * clapper_cache_read_pspec (const gchar **data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GByteArray * clapper_cache_create (void);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_cache_store_boolean (GByteArray *bytes, gboolean val);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_cache_store_int (GByteArray *bytes, gint val);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_cache_store_uint (GByteArray *bytes, guint val);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_cache_store_double (GByteArray *bytes, gdouble val);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_cache_store_string (GByteArray *bytes, const gchar *val);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_cache_store_enum (GByteArray *bytes, GType enum_type);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_cache_store_flags (GByteArray *bytes, GType flags_type);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_cache_store_iface (GByteArray *bytes, GType iface);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_cache_store_pspec (GByteArray *bytes, GParamSpec *pspec);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_cache_write (const gchar *filename, GByteArray *bytes, GError **error);
|
||||
|
||||
G_END_DECLS
|
491
src/lib/clapper/clapper-cache.c
Normal file
491
src/lib/clapper/clapper-cache.c
Normal file
@@ -0,0 +1,491 @@
|
||||
/* Clapper Playback Library
|
||||
* Copyright (C) 2025 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 "clapper-cache-private.h"
|
||||
#include "clapper-version.h"
|
||||
#include "clapper-extractable.h"
|
||||
|
||||
#define CLAPPER_CACHE_HEADER "CLAPPER"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CLAPPER_CACHE_IFACE_EXTRACTABLE = 1,
|
||||
} ClapperCacheIfaces;
|
||||
|
||||
static GArray *enum_registry = NULL;
|
||||
static GArray *flags_registry = NULL;
|
||||
static gboolean cache_disabled = FALSE;
|
||||
|
||||
void
|
||||
clapper_cache_initialize (void)
|
||||
{
|
||||
const gchar *env = g_getenv ("CLAPPER_DISABLE_CACHE");
|
||||
|
||||
if (G_LIKELY (!env || !g_str_has_prefix (env, "1"))) {
|
||||
enum_registry = g_array_new (FALSE, TRUE, sizeof (GEnumValue *));
|
||||
flags_registry = g_array_new (FALSE, TRUE, sizeof (GFlagsValue *));
|
||||
} else {
|
||||
cache_disabled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
GMappedFile *
|
||||
clapper_cache_open (const gchar *filename, const gchar **data, GError **error)
|
||||
{
|
||||
GMappedFile *file;
|
||||
|
||||
if (G_UNLIKELY (cache_disabled))
|
||||
return NULL;
|
||||
|
||||
if (!(file = g_mapped_file_new (filename, FALSE, error)))
|
||||
return NULL;
|
||||
|
||||
if (G_UNLIKELY (g_mapped_file_get_length (file) == 0)) {
|
||||
g_mapped_file_unref (file);
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"File is empty");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*data = g_mapped_file_get_contents (file);
|
||||
|
||||
/* Header name check */
|
||||
if (G_UNLIKELY (g_strcmp0 (*data, CLAPPER_CACHE_HEADER) != 0)) {
|
||||
g_mapped_file_unref (file);
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"Invalid file header");
|
||||
return NULL;
|
||||
}
|
||||
*data += strlen (*data) + 1;
|
||||
|
||||
/* Header version check */
|
||||
if (clapper_cache_read_uint (data) != CLAPPER_VERSION_HEX) {
|
||||
g_mapped_file_unref (file);
|
||||
/* Just different version, so no error set */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
inline gboolean
|
||||
clapper_cache_read_boolean (const gchar **data)
|
||||
{
|
||||
gboolean val = *(const gboolean *) *data;
|
||||
*data += sizeof (gboolean);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
inline gint
|
||||
clapper_cache_read_int (const gchar **data)
|
||||
{
|
||||
gint val = *(const gint *) *data;
|
||||
*data += sizeof (gint);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
inline guint
|
||||
clapper_cache_read_uint (const gchar **data)
|
||||
{
|
||||
guint val = *(const guint *) *data;
|
||||
*data += sizeof (guint);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
inline gdouble
|
||||
clapper_cache_read_double (const gchar **data)
|
||||
{
|
||||
gdouble val = *(const gdouble *) *data;
|
||||
*data += sizeof (gdouble);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
inline const gchar *
|
||||
clapper_cache_read_string (const gchar **data)
|
||||
{
|
||||
const gboolean is_null = clapper_cache_read_boolean (data);
|
||||
const gchar *str = NULL;
|
||||
|
||||
if (!is_null) {
|
||||
str = *data;
|
||||
*data += strlen (str) + 1;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
inline GType
|
||||
clapper_cache_read_enum (const gchar **data)
|
||||
{
|
||||
GType type;
|
||||
const gchar *enum_name;
|
||||
guint i, n_values;
|
||||
|
||||
enum_name = clapper_cache_read_string (data);
|
||||
n_values = clapper_cache_read_uint (data);
|
||||
|
||||
/* If not registered yet */
|
||||
if ((type = g_type_from_name (enum_name)) == 0) {
|
||||
GEnumValue *values = g_new0 (GEnumValue, n_values + 1);
|
||||
|
||||
for (i = 0; i < n_values; ++i) {
|
||||
values[i].value = clapper_cache_read_int (data);
|
||||
values[i].value_name = g_intern_string (clapper_cache_read_string (data));
|
||||
values[i].value_nick = g_intern_string (clapper_cache_read_string (data));
|
||||
}
|
||||
g_array_append_val (enum_registry, values); // store statically
|
||||
|
||||
type = g_enum_register_static (g_intern_string (enum_name),
|
||||
g_array_index (enum_registry, GEnumValue *, enum_registry->len - 1));
|
||||
} else {
|
||||
/* Skip over data */
|
||||
for (i = 0; i < n_values; ++i) {
|
||||
clapper_cache_read_int (data); // value
|
||||
clapper_cache_read_string (data); // value_name
|
||||
clapper_cache_read_string (data); // value_nick
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
inline GType
|
||||
clapper_cache_read_flags (const gchar **data)
|
||||
{
|
||||
GType type;
|
||||
const gchar *flags_name;
|
||||
guint i, n_values;
|
||||
|
||||
flags_name = clapper_cache_read_string (data);
|
||||
n_values = clapper_cache_read_uint (data);
|
||||
|
||||
/* If not registered yet */
|
||||
if ((type = g_type_from_name (flags_name)) == 0) {
|
||||
GFlagsValue *values = g_new0 (GFlagsValue, n_values + 1);
|
||||
|
||||
for (i = 0; i < n_values; ++i) {
|
||||
values[i].value = clapper_cache_read_int (data);
|
||||
values[i].value_name = g_intern_string (clapper_cache_read_string (data));
|
||||
values[i].value_nick = g_intern_string (clapper_cache_read_string (data));
|
||||
}
|
||||
g_array_append_val (flags_registry, values); // store statically
|
||||
|
||||
type = g_flags_register_static (g_intern_string (flags_name),
|
||||
g_array_index (flags_registry, GFlagsValue *, flags_registry->len - 1));
|
||||
} else {
|
||||
/* Skip over data */
|
||||
for (i = 0; i < n_values; ++i) {
|
||||
clapper_cache_read_int (data); // value
|
||||
clapper_cache_read_string (data); // value_name
|
||||
clapper_cache_read_string (data); // value_nick
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
GType
|
||||
clapper_cache_read_iface (const gchar **data)
|
||||
{
|
||||
gint iface_id = clapper_cache_read_int (data);
|
||||
|
||||
switch (iface_id) {
|
||||
case CLAPPER_CACHE_IFACE_EXTRACTABLE:
|
||||
return CLAPPER_TYPE_EXTRACTABLE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
GParamSpec *
|
||||
clapper_cache_read_pspec (const gchar **data)
|
||||
{
|
||||
GParamSpec *pspec;
|
||||
GType value_type;
|
||||
const gchar *name, *nick, *blurb;
|
||||
GParamFlags flags;
|
||||
|
||||
value_type = *(const GType *) *data;
|
||||
*data += sizeof (GType);
|
||||
|
||||
name = clapper_cache_read_string (data);
|
||||
nick = clapper_cache_read_string (data);
|
||||
blurb = clapper_cache_read_string (data);
|
||||
|
||||
flags = *(const GParamFlags *) *data;
|
||||
*data += sizeof (GParamFlags);
|
||||
|
||||
/* NOTE: C does not guarantee order in which function arguments
|
||||
* are evaluated, so read into variables and then create pspec */
|
||||
|
||||
switch (value_type) {
|
||||
case G_TYPE_BOOLEAN:
|
||||
pspec = g_param_spec_boolean (name, nick, blurb,
|
||||
clapper_cache_read_boolean (data), flags);
|
||||
break;
|
||||
case G_TYPE_INT:{
|
||||
gint minimum = clapper_cache_read_int (data);
|
||||
gint maximum = clapper_cache_read_int (data);
|
||||
gint default_value = clapper_cache_read_int (data);
|
||||
|
||||
pspec = g_param_spec_int (name, nick, blurb,
|
||||
minimum, maximum, default_value, flags);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_UINT:{
|
||||
guint minimum = clapper_cache_read_uint (data);
|
||||
guint maximum = clapper_cache_read_uint (data);
|
||||
guint default_value = clapper_cache_read_uint (data);
|
||||
|
||||
pspec = g_param_spec_uint (name, nick, blurb,
|
||||
minimum, maximum, default_value, flags);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_DOUBLE:{
|
||||
gdouble minimum = clapper_cache_read_double (data);
|
||||
gdouble maximum = clapper_cache_read_double (data);
|
||||
gdouble default_value = clapper_cache_read_double (data);
|
||||
|
||||
pspec = g_param_spec_double (name, nick, blurb,
|
||||
minimum, maximum, default_value, flags);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_STRING:
|
||||
pspec = g_param_spec_string (name, nick, blurb,
|
||||
clapper_cache_read_string (data), flags);
|
||||
break;
|
||||
case G_TYPE_ENUM:{
|
||||
GType enum_type = clapper_cache_read_enum (data);
|
||||
gint default_value = clapper_cache_read_int (data);
|
||||
|
||||
pspec = g_param_spec_enum (name, nick, blurb,
|
||||
enum_type, default_value, flags);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_FLAGS:{
|
||||
GType flags_type = clapper_cache_read_flags (data);
|
||||
guint default_value = clapper_cache_read_uint (data);
|
||||
|
||||
pspec = g_param_spec_flags (name, nick, blurb,
|
||||
flags_type, default_value, flags);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_param_spec_ref_sink (pspec);
|
||||
}
|
||||
|
||||
GByteArray *
|
||||
clapper_cache_create (void)
|
||||
{
|
||||
GByteArray *bytes;
|
||||
|
||||
if (G_UNLIKELY (cache_disabled))
|
||||
return NULL;
|
||||
|
||||
bytes = g_byte_array_new ();
|
||||
|
||||
/* NOTE: We do not store whether string is NULL here, since it never is */
|
||||
g_byte_array_append (bytes, (const guint8 *) CLAPPER_CACHE_HEADER, 8); // 7 + 1
|
||||
clapper_cache_store_uint (bytes, CLAPPER_VERSION_HEX);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
inline void
|
||||
clapper_cache_store_boolean (GByteArray *bytes, gboolean val)
|
||||
{
|
||||
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gboolean));
|
||||
}
|
||||
|
||||
inline void
|
||||
clapper_cache_store_int (GByteArray *bytes, gint val)
|
||||
{
|
||||
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gint));
|
||||
}
|
||||
|
||||
inline void
|
||||
clapper_cache_store_uint (GByteArray *bytes, guint val)
|
||||
{
|
||||
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (guint));
|
||||
}
|
||||
|
||||
inline void
|
||||
clapper_cache_store_double (GByteArray *bytes, gdouble val)
|
||||
{
|
||||
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gdouble));
|
||||
}
|
||||
|
||||
inline void
|
||||
clapper_cache_store_string (GByteArray *bytes, const gchar *val)
|
||||
{
|
||||
/* Distinguish empty string from NULL */
|
||||
const gboolean is_null = (val == NULL);
|
||||
|
||||
clapper_cache_store_boolean (bytes, is_null);
|
||||
if (!is_null)
|
||||
g_byte_array_append (bytes, (const guint8 *) val, strlen (val) + 1);
|
||||
}
|
||||
|
||||
inline void
|
||||
clapper_cache_store_enum (GByteArray *bytes, GType enum_type)
|
||||
{
|
||||
GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_peek (enum_type));
|
||||
guint i;
|
||||
|
||||
clapper_cache_store_string (bytes, g_type_name (enum_type));
|
||||
clapper_cache_store_uint (bytes, enum_class->n_values);
|
||||
|
||||
for (i = 0; i < enum_class->n_values; ++i) {
|
||||
clapper_cache_store_int (bytes, enum_class->values[i].value);
|
||||
clapper_cache_store_string (bytes, enum_class->values[i].value_name);
|
||||
clapper_cache_store_string (bytes, enum_class->values[i].value_nick);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
clapper_cache_store_flags (GByteArray *bytes, GType flags_type)
|
||||
{
|
||||
GFlagsClass *flags_class = G_FLAGS_CLASS (g_type_class_peek (flags_type));
|
||||
guint i;
|
||||
|
||||
clapper_cache_store_string (bytes, g_type_name (flags_type));
|
||||
clapper_cache_store_uint (bytes, flags_class->n_values);
|
||||
|
||||
for (i = 0; i < flags_class->n_values; ++i) {
|
||||
clapper_cache_store_int (bytes, flags_class->values[i].value);
|
||||
clapper_cache_store_string (bytes, flags_class->values[i].value_name);
|
||||
clapper_cache_store_string (bytes, flags_class->values[i].value_nick);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
clapper_cache_store_iface (GByteArray *bytes, GType iface)
|
||||
{
|
||||
gint iface_id = 0;
|
||||
|
||||
if (iface == CLAPPER_TYPE_EXTRACTABLE)
|
||||
iface_id = CLAPPER_CACHE_IFACE_EXTRACTABLE;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
clapper_cache_store_int (bytes, iface_id);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
clapper_cache_store_pspec (GByteArray *bytes, GParamSpec *pspec)
|
||||
{
|
||||
GParamFlags flags;
|
||||
const gboolean is_enum = G_IS_PARAM_SPEC_ENUM (pspec);
|
||||
const gboolean is_flags = (!is_enum && G_IS_PARAM_SPEC_FLAGS (pspec));
|
||||
|
||||
if (is_enum) {
|
||||
GType enum_type = G_TYPE_ENUM;
|
||||
g_byte_array_append (bytes, (const guint8 *) &enum_type, sizeof (GType));
|
||||
} else if (is_flags) {
|
||||
GType flags_type = G_TYPE_FLAGS;
|
||||
g_byte_array_append (bytes, (const guint8 *) &flags_type, sizeof (GType));
|
||||
} else {
|
||||
g_byte_array_append (bytes, (const guint8 *) &pspec->value_type, sizeof (GType));
|
||||
}
|
||||
|
||||
clapper_cache_store_string (bytes, g_param_spec_get_name (pspec));
|
||||
clapper_cache_store_string (bytes, g_param_spec_get_nick (pspec));
|
||||
clapper_cache_store_string (bytes, g_param_spec_get_blurb (pspec));
|
||||
|
||||
flags = pspec->flags;
|
||||
flags &= ~G_PARAM_STATIC_STRINGS; // Data read from cache is never static
|
||||
g_byte_array_append (bytes, (const guint8 *) &flags, sizeof (GParamFlags));
|
||||
|
||||
switch (pspec->value_type) {
|
||||
case G_TYPE_BOOLEAN:{
|
||||
GParamSpecBoolean *p = (GParamSpecBoolean *) pspec;
|
||||
clapper_cache_store_boolean (bytes, p->default_value);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_INT:{
|
||||
GParamSpecInt *p = (GParamSpecInt *) pspec;
|
||||
clapper_cache_store_int (bytes, p->minimum);
|
||||
clapper_cache_store_int (bytes, p->maximum);
|
||||
clapper_cache_store_int (bytes, p->default_value);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_UINT:{
|
||||
GParamSpecUInt *p = (GParamSpecUInt *) pspec;
|
||||
clapper_cache_store_uint (bytes, p->minimum);
|
||||
clapper_cache_store_uint (bytes, p->maximum);
|
||||
clapper_cache_store_uint (bytes, p->default_value);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_DOUBLE:{
|
||||
GParamSpecDouble *p = (GParamSpecDouble *) pspec;
|
||||
clapper_cache_store_double (bytes, p->minimum);
|
||||
clapper_cache_store_double (bytes, p->maximum);
|
||||
clapper_cache_store_double (bytes, p->default_value);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_STRING:{
|
||||
GParamSpecString *p = (GParamSpecString *) pspec;
|
||||
clapper_cache_store_string (bytes, p->default_value);
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
if (is_enum) {
|
||||
GParamSpecEnum *p = (GParamSpecEnum *) pspec;
|
||||
clapper_cache_store_enum (bytes, pspec->value_type);
|
||||
clapper_cache_store_int (bytes, p->default_value);
|
||||
break;
|
||||
} else if (is_flags) {
|
||||
GParamSpecFlags *p = (GParamSpecFlags *) pspec;
|
||||
clapper_cache_store_flags (bytes, pspec->value_type);
|
||||
clapper_cache_store_uint (bytes, p->default_value);
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
clapper_cache_write (const gchar *filename, GByteArray *bytes, GError **error)
|
||||
{
|
||||
gchar *dirname = g_path_get_dirname (filename);
|
||||
gboolean has_dir;
|
||||
|
||||
has_dir = (g_mkdir_with_parents (dirname, 0755) == 0);
|
||||
g_free (dirname);
|
||||
|
||||
if (!has_dir) {
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"Could not create directory to store cache content");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Using "g_file_set_contents" to replace file atomically */
|
||||
return g_file_set_contents (filename, (const gchar *) bytes->data, bytes->len, error);
|
||||
}
|
@@ -38,6 +38,9 @@ gboolean clapper_enhancer_proxy_fill_from_cache (ClapperEnhancerProxy *proxy);
|
||||
G_GNUC_INTERNAL
|
||||
gboolean clapper_enhancer_proxy_fill_from_instance (ClapperEnhancerProxy *proxy, GObject *enhancer);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_enhancer_proxy_export_to_cache (ClapperEnhancerProxy *proxy);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GObject * clapper_enhancer_proxy_get_peas_info (ClapperEnhancerProxy *proxy);
|
||||
|
||||
|
@@ -45,6 +45,7 @@
|
||||
|
||||
#include "clapper.h"
|
||||
#include "clapper-enhancer-proxy-private.h"
|
||||
#include "clapper-cache-private.h"
|
||||
#include "clapper-extractable.h"
|
||||
#include "clapper-enums.h"
|
||||
|
||||
@@ -308,21 +309,183 @@ _init_schema (ClapperEnhancerProxy *self)
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
|
||||
static inline gchar *
|
||||
_build_cache_filename (ClapperEnhancerProxy *self)
|
||||
{
|
||||
return g_build_filename (g_get_user_cache_dir (), CLAPPER_API_NAME,
|
||||
"enhancers", self->module_name, "cache.bin", NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
clapper_enhancer_proxy_fill_from_cache (ClapperEnhancerProxy *self)
|
||||
{
|
||||
GST_FIXME_OBJECT (self, "Implement enhancer proxy caching");
|
||||
GMappedFile *mapped_file;
|
||||
GError *error = NULL;
|
||||
gchar *filename;
|
||||
const gchar *data;
|
||||
guint i;
|
||||
|
||||
filename = _build_cache_filename (self);
|
||||
mapped_file = clapper_cache_open (filename, &data, &error);
|
||||
g_free (filename);
|
||||
|
||||
if (!mapped_file) {
|
||||
/* No error if cache disabled or version mismatch */
|
||||
if (error) {
|
||||
if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT)
|
||||
GST_DEBUG_OBJECT (self, "No cache file found");
|
||||
else
|
||||
GST_ERROR_OBJECT (self, "Could not restore from cache, reason: %s", error->message);
|
||||
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Plugin version check */
|
||||
if (g_strcmp0 (clapper_cache_read_string (&data), self->version) != 0)
|
||||
return FALSE; // not an error
|
||||
|
||||
/* Restore Interfaces */
|
||||
if ((self->n_ifaces = clapper_cache_read_uint (&data)) > 0) {
|
||||
self->ifaces = g_new (GType, self->n_ifaces);
|
||||
for (i = 0; i < self->n_ifaces; ++i) {
|
||||
if (G_UNLIKELY ((self->ifaces[i] = clapper_cache_read_iface (&data)) == 0))
|
||||
goto abort_reading;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore ParamSpecs */
|
||||
if ((self->n_pspecs = clapper_cache_read_uint (&data)) > 0) {
|
||||
self->pspecs = g_new (GParamSpec *, self->n_pspecs);
|
||||
for (i = 0; i < self->n_pspecs; ++i) {
|
||||
if (G_UNLIKELY ((self->pspecs[i] = clapper_cache_read_pspec (&data)) == NULL))
|
||||
goto abort_reading;
|
||||
}
|
||||
}
|
||||
|
||||
g_mapped_file_unref (mapped_file);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Filled proxy \"%s\" from cache, n_ifaces: %u, n_pspecs: %u",
|
||||
self->friendly_name, self->n_ifaces, self->n_pspecs);
|
||||
|
||||
return TRUE;
|
||||
|
||||
abort_reading:
|
||||
GST_ERROR_OBJECT (self, "Cache file is corrupted or invalid");
|
||||
|
||||
g_free (self->ifaces);
|
||||
self->n_ifaces = 0;
|
||||
|
||||
for (i = 0; i < self->n_pspecs; ++i) {
|
||||
g_clear_pointer (&self->pspecs[i], g_param_spec_unref);
|
||||
}
|
||||
g_free (self->pspecs);
|
||||
self->n_pspecs = 0;
|
||||
|
||||
g_mapped_file_unref (mapped_file);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
clapper_enhancer_proxy_export_to_cache (ClapperEnhancerProxy *self)
|
||||
{
|
||||
GByteArray *bytes;
|
||||
GError *error = NULL;
|
||||
gchar *filename;
|
||||
gboolean data_ok = TRUE;
|
||||
guint i;
|
||||
|
||||
bytes = clapper_cache_create ();
|
||||
|
||||
/* If cache disabled */
|
||||
if (G_UNLIKELY (bytes == NULL))
|
||||
return;
|
||||
|
||||
filename = _build_cache_filename (self);
|
||||
GST_TRACE_OBJECT (self, "Exporting data to cache file: \"%s\"", filename);
|
||||
|
||||
/* Store version */
|
||||
clapper_cache_store_string (bytes, self->version);
|
||||
|
||||
/* Store Interfaces */
|
||||
clapper_cache_store_uint (bytes, self->n_ifaces);
|
||||
for (i = 0; i < self->n_ifaces; ++i) {
|
||||
/* This should never happen, as we only store Clapper interfaces */
|
||||
if (G_UNLIKELY (!(data_ok = clapper_cache_store_iface (bytes, self->ifaces[i])))) {
|
||||
g_warning ("Cannot cache enhancer \"%s\" (%s), as it contains"
|
||||
" unsupported interface type \"%s\"",
|
||||
self->friendly_name, self->module_name, g_type_name (self->ifaces[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data_ok) {
|
||||
/* Store ParamSpecs */
|
||||
clapper_cache_store_uint (bytes, self->n_pspecs);
|
||||
for (i = 0; i < self->n_pspecs; ++i) {
|
||||
/* Can happen if someone writes an enhancer with unsupported
|
||||
* param spec type with ClapperEnhancerParamFlags set */
|
||||
if (G_UNLIKELY (!(data_ok = clapper_cache_store_pspec (bytes, self->pspecs[i])))) {
|
||||
g_warning ("Cannot cache enhancer \"%s\" (%s), as it contains"
|
||||
" property \"%s\" of unsupported type",
|
||||
self->friendly_name, self->module_name, self->pspecs[i]->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data_ok && clapper_cache_write (filename, bytes, &error)) {
|
||||
GST_TRACE_OBJECT (self, "Successfully exported data to cache file");
|
||||
} else if (error) {
|
||||
GST_ERROR_OBJECT (self, "Could not cache data, reason: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
g_free (filename);
|
||||
g_byte_array_free (bytes, TRUE);
|
||||
}
|
||||
|
||||
gboolean
|
||||
clapper_enhancer_proxy_fill_from_instance (ClapperEnhancerProxy *self, GObject *enhancer)
|
||||
{
|
||||
self->ifaces = g_type_interfaces (G_OBJECT_TYPE (enhancer), &self->n_ifaces);
|
||||
self->pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (enhancer), &self->n_pspecs);
|
||||
GType enhancer_types[1] = { CLAPPER_TYPE_EXTRACTABLE };
|
||||
GType *ifaces;
|
||||
GParamSpec **pspecs;
|
||||
GParamFlags enhancer_flags;
|
||||
guint i, j, n, write_index = 0;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Filled proxy \"%s\", n_ifaces: %u, n_pspecs: %u",
|
||||
/* Filter to only Clapper interfaces */
|
||||
ifaces = g_type_interfaces (G_OBJECT_TYPE (enhancer), &n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
for (j = 0; j < G_N_ELEMENTS (enhancer_types); ++j) {
|
||||
if (ifaces[i] == enhancer_types[j]) {
|
||||
ifaces[write_index++] = ifaces[i];
|
||||
break; // match found, do next iface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Resize memory */
|
||||
self->n_ifaces = write_index;
|
||||
self->ifaces = g_realloc (ifaces, self->n_ifaces * sizeof (GType));
|
||||
|
||||
/* Filter to only Clapper param specs */
|
||||
write_index = 0;
|
||||
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (enhancer), &n);
|
||||
enhancer_flags = (CLAPPER_ENHANCER_PARAM_GLOBAL | CLAPPER_ENHANCER_PARAM_LOCAL);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (pspecs[i]->flags & enhancer_flags)
|
||||
pspecs[write_index++] = g_param_spec_ref (pspecs[i]);
|
||||
}
|
||||
|
||||
/* Resize memory */
|
||||
self->n_pspecs = write_index;
|
||||
self->pspecs = g_realloc (pspecs, self->n_pspecs * sizeof (GParamSpec *));
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Filled proxy \"%s\" from instance, n_ifaces: %u, n_pspecs: %u",
|
||||
self->friendly_name, self->n_ifaces, self->n_pspecs);
|
||||
|
||||
return TRUE;
|
||||
@@ -925,14 +1088,20 @@ static void
|
||||
clapper_enhancer_proxy_finalize (GObject *object)
|
||||
{
|
||||
ClapperEnhancerProxy *self = CLAPPER_ENHANCER_PROXY_CAST (object);
|
||||
guint i;
|
||||
|
||||
GST_TRACE_OBJECT (self, "Finalize");
|
||||
|
||||
g_object_unref (self->peas_info);
|
||||
g_free (self->ifaces);
|
||||
|
||||
for (i = 0; i < self->n_pspecs; ++i) {
|
||||
g_param_spec_unref (self->pspecs[i]);
|
||||
}
|
||||
g_free (self->pspecs);
|
||||
g_clear_pointer (&self->schema, g_settings_schema_unref);
|
||||
|
||||
gst_clear_structure (&self->local_config);
|
||||
g_clear_pointer (&self->schema, g_settings_schema_unref);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
@@ -128,7 +128,7 @@ clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies)
|
||||
filled = clapper_enhancer_proxy_fill_from_instance (proxy, enhancer);
|
||||
g_object_unref (enhancer);
|
||||
|
||||
GST_FIXME_OBJECT (proxy, "Save enhancer proxy data to cache");
|
||||
clapper_enhancer_proxy_export_to_cache (proxy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#include "clapper.h"
|
||||
#include "clapper-cache-private.h"
|
||||
#include "clapper-utils-private.h"
|
||||
#include "clapper-playbin-bus-private.h"
|
||||
#include "clapper-app-bus-private.h"
|
||||
@@ -48,6 +49,7 @@ clapper_init_check_internal (int *argc, char **argv[])
|
||||
|
||||
gst_pb_utils_init ();
|
||||
|
||||
clapper_cache_initialize ();
|
||||
clapper_utils_initialize ();
|
||||
clapper_playbin_bus_initialize ();
|
||||
clapper_app_bus_initialize ();
|
||||
|
@@ -54,6 +54,7 @@ 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_API_NAME', clapper_api_name)
|
||||
config_h.set_quoted('CLAPPER_ENHANCERS_ID', 'com.github.rafostar.Clapper.Enhancers')
|
||||
config_h.set_quoted('CLAPPER_ENHANCERS_PATH', clapper_enhancers_dir)
|
||||
|
||||
@@ -133,6 +134,7 @@ clapper_sources = [
|
||||
'clapper.c',
|
||||
'clapper-app-bus.c',
|
||||
'clapper-audio-stream.c',
|
||||
'clapper-cache.c',
|
||||
'clapper-enhancer-proxy.c',
|
||||
'clapper-enhancer-proxy-list.c',
|
||||
'clapper-extractable.c',
|
||||
|
Reference in New Issue
Block a user