/* * Copyright (C) 2022 Rafał Dzięgiel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "gstclapperimporterloader.h" #include "gstclapperimporter.h" #include "gstclappercontexthandler.h" #define GST_CAT_DEFAULT gst_clapper_importer_loader_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); #define parent_class gst_clapper_importer_loader_parent_class G_DEFINE_TYPE (GstClapperImporterLoader, gst_clapper_importer_loader, GST_TYPE_OBJECT); typedef GstClapperImporter* (* MakeImporter) (GPtrArray *context_handlers); typedef GstCaps* (* MakeCaps) (gboolean is_template, GstRank *rank, GPtrArray *context_handlers); typedef struct { GModule *module; GstCaps *caps; GstRank rank; } GstClapperImporterData; static void gst_clapper_importer_data_free (GstClapperImporterData *data) { GST_TRACE ("Freeing importer data: %" GST_PTR_FORMAT, data); gst_clear_caps (&data->caps); g_free (data); } static GstClapperImporterData * _obtain_importer_data (GModule *module, gboolean is_template, GPtrArray *context_handlers) { MakeCaps make_caps; GstClapperImporterData *data; GST_DEBUG ("Found importer: %s", g_module_name (module)); if (!g_module_symbol (module, "make_caps", (gpointer *) &make_caps) || make_caps == NULL) { GST_WARNING ("Make caps function missing in importer"); return NULL; } data = g_new0 (GstClapperImporterData, 1); data->module = module; data->caps = make_caps (is_template, &data->rank, context_handlers); GST_TRACE ("Created importer data: %" GST_PTR_FORMAT, data); if (G_UNLIKELY (!data->caps)) { if (!is_template) { GST_ERROR ("Invalid importer without caps: %s", g_module_name (data->module)); } else { /* When importer cannot be actually used, due to e.g. unsupported HW */ GST_DEBUG ("No actual caps returned from importer"); } gst_clapper_importer_data_free (data); return NULL; } GST_DEBUG ("Importer caps: %" GST_PTR_FORMAT, data->caps); return data; } static GstClapperImporter * _obtain_importer_internal (GModule *module, GPtrArray *context_handlers) { MakeImporter make_importer; GstClapperImporter *importer; if (!g_module_symbol (module, "make_importer", (gpointer *) &make_importer) || make_importer == NULL) { GST_WARNING ("Make function missing in importer"); return NULL; } importer = make_importer (context_handlers); GST_TRACE ("Created importer: %" GST_PTR_FORMAT, importer); return importer; } static gpointer _obtain_available_modules_once (G_GNUC_UNUSED gpointer data) { GPtrArray *modules; GFile *dir; GFileEnumerator *dir_enum; GError *error = NULL; GST_INFO ("Preparing modules"); modules = g_ptr_array_new (); dir = g_file_new_for_path (CLAPPER_SINK_IMPORTER_PATH); if ((dir_enum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error))) { while (TRUE) { GFileInfo *info = NULL; GModule *module; gchar *module_path; const gchar *module_name; if (!g_file_enumerator_iterate (dir_enum, &info, NULL, NULL, &error) || !info) break; module_name = g_file_info_get_name (info); if (!g_str_has_suffix (module_name, G_MODULE_SUFFIX)) continue; module_path = g_module_build_path (CLAPPER_SINK_IMPORTER_PATH, module_name); module = g_module_open (module_path, G_MODULE_BIND_LAZY); g_free (module_path); if (!module) { GST_WARNING ("Could not read module: %s, reason: %s", module_name, g_module_error ()); continue; } GST_INFO ("Found module: %s", module_name); g_ptr_array_add (modules, module); } g_object_unref (dir_enum); } g_object_unref (dir); if (error) { GST_ERROR ("Could not load module, reason: %s", (error->message) ? error->message : "unknown"); g_error_free (error); } return modules; } static const GPtrArray * gst_clapper_importer_loader_get_available_modules (void) { static GOnce once = G_ONCE_INIT; g_once (&once, _obtain_available_modules_once, NULL); return (const GPtrArray *) once.retval; } static gint _sort_importers_cb (gconstpointer a, gconstpointer b) { GstClapperImporterData *data_a, *data_b; data_a = *((GstClapperImporterData **) a); data_b = *((GstClapperImporterData **) b); return (data_b->rank - data_a->rank); } static GPtrArray * _obtain_importers (gboolean is_template, GPtrArray *context_handlers) { const GPtrArray *modules; GPtrArray *importers; guint i; GST_DEBUG ("Checking %s importers", (is_template) ? "available" : "usable"); modules = gst_clapper_importer_loader_get_available_modules (); importers = g_ptr_array_new_with_free_func ( (GDestroyNotify) gst_clapper_importer_data_free); for (i = 0; i < modules->len; i++) { GModule *module = g_ptr_array_index (modules, i); GstClapperImporterData *data; if ((data = _obtain_importer_data (module, is_template, context_handlers))) g_ptr_array_add (importers, data); } g_ptr_array_sort (importers, (GCompareFunc) _sort_importers_cb); GST_DEBUG ("Found %i %s importers", importers->len, (is_template) ? "available" : "usable"); return importers; } GstClapperImporterLoader * gst_clapper_importer_loader_new (void) { return g_object_new (GST_TYPE_CLAPPER_IMPORTER_LOADER, NULL); } static GstCaps * _make_caps_for_importers (const GPtrArray *importers) { GstCaps *caps = gst_caps_new_empty (); guint i; for (i = 0; i < importers->len; i++) { GstClapperImporterData *data = g_ptr_array_index (importers, i); gst_caps_append (caps, gst_caps_ref (data->caps)); } return caps; } GstPadTemplate * gst_clapper_importer_loader_make_sink_pad_template (void) { GPtrArray *importers; GstCaps *caps; GstPadTemplate *templ; /* This is only called once from sink class init function */ GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperimporterloader", 0, "Clapper Importer Loader"); GST_DEBUG ("Making sink pad template"); importers = _obtain_importers (TRUE, NULL); caps = _make_caps_for_importers (importers); g_ptr_array_unref (importers); if (G_UNLIKELY (gst_caps_is_empty (caps))) gst_caps_append (caps, gst_caps_new_any ()); templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps); gst_caps_unref (caps); GST_TRACE ("Created sink pad template"); return templ; } GstCaps * gst_clapper_importer_loader_make_actual_caps (GstClapperImporterLoader *self) { return _make_caps_for_importers (self->importers); } gboolean gst_clapper_importer_loader_handle_context_query (GstClapperImporterLoader *self, GstBaseSink *bsink, GstQuery *query) { guint i; for (i = 0; i < self->context_handlers->len; i++) { GstClapperContextHandler *handler = g_ptr_array_index (self->context_handlers, i); if (gst_clapper_context_handler_handle_context_query (handler, bsink, query)) return TRUE; } return FALSE; } static const GstClapperImporterData * _get_importer_data_for_caps (const GPtrArray *importers, const GstCaps *caps) { guint i; for (i = 0; i < importers->len; i++) { GstClapperImporterData *data = g_ptr_array_index (importers, i); if (!gst_caps_is_always_compatible (caps, data->caps)) continue; return data; } return NULL; } gboolean gst_clapper_importer_loader_find_importer_for_caps (GstClapperImporterLoader *self, GstCaps *caps, GstClapperImporter **importer) { const GstClapperImporterData *data = NULL; GstClapperImporter *found_importer = NULL; GST_OBJECT_LOCK (self); GST_DEBUG_OBJECT (self, "Requested importer for caps: %" GST_PTR_FORMAT, caps); data = _get_importer_data_for_caps (self->importers, caps); GST_LOG_OBJECT (self, "Old importer path: %s, new path: %s", (self->last_module) ? g_module_name (self->last_module) : NULL, (data) ? g_module_name (data->module) : NULL); if (G_UNLIKELY (!data)) { gst_clear_object (importer); goto finish; } if (*importer && (self->last_module == data->module)) { GST_DEBUG_OBJECT (self, "No importer change"); gst_clapper_importer_set_caps (*importer, caps); goto finish; } found_importer = _obtain_importer_internal (data->module, self->context_handlers); gst_clear_object (importer); if (!found_importer) goto finish; gst_clapper_importer_set_caps (found_importer, caps); *importer = found_importer; finish: self->last_module = (*importer && data) ? data->module : NULL; GST_OBJECT_UNLOCK (self); return (*importer != NULL); } static void gst_clapper_importer_loader_init (GstClapperImporterLoader *self) { self->context_handlers = g_ptr_array_new_with_free_func ( (GDestroyNotify) gst_object_unref); self->importers = _obtain_importers (FALSE, self->context_handlers); } static void gst_clapper_importer_loader_finalize (GObject *object) { GstClapperImporterLoader *self = GST_CLAPPER_IMPORTER_LOADER_CAST (object); GST_TRACE ("Finalize"); if (self->importers) g_ptr_array_unref (self->importers); g_ptr_array_unref (self->context_handlers); GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); } static void gst_clapper_importer_loader_class_init (GstClapperImporterLoaderClass *klass) { GObjectClass *gobject_class = (GObjectClass *) klass; gobject_class->finalize = gst_clapper_importer_loader_finalize; }