mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-30 16:02:00 +02:00
plugin: Add clapper GStreamer plugin
Add new GStreamer plugin that consists of multiple elements for Clapper video player. The main difference is that unlike the old one, this does not operate using `GtkGLArea` anymore, but processes and displays `GdkTextures` directly through `GtkPicture` widget. Also this one is installed like any other GStreamer plugin, thus can be used via `gst-launch-1.0` binary or even used by other GTK4 apps if they wish to integrate it. This commit adds new video sink that uses a new kind of memory as input, called `ClapperGdkMemory`. With it comes a simple dedicated memory allocator, buffer pool and a `clapperimport` element that converts system mapped memory frames into `GdkTextures` that are later passed in our `ClapperGdkMemory` for the sink to display.
This commit is contained in:
31
lib/gst/clapper/meson.build
vendored
31
lib/gst/clapper/meson.build
vendored
@@ -34,36 +34,13 @@ gstclapper_defines = [
|
|||||||
'-DGST_USE_UNSTABLE_API',
|
'-DGST_USE_UNSTABLE_API',
|
||||||
'-DHAVE_GTK_GL',
|
'-DHAVE_GTK_GL',
|
||||||
]
|
]
|
||||||
gtk_deps = [gstgl_dep, gstglproto_dep]
|
|
||||||
have_gtk_gl_windowing = false
|
|
||||||
|
|
||||||
gtk4_dep = dependency('gtk4', required: true)
|
if not get_option('lib')
|
||||||
|
subdir_done()
|
||||||
if not gtk4_dep.version().version_compare('>=4.0.0')
|
|
||||||
error('GTK4 version on this system is too old')
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if gst_gl_have_window_x11 and (gst_gl_have_platform_egl or gst_gl_have_platform_glx)
|
if not gir.found()
|
||||||
gtk_x11_dep = dependency('gtk4-x11', required: false)
|
error('Clapper lib requires GI bindings to be compiled')
|
||||||
if gtk_x11_dep.found()
|
|
||||||
gtk_deps += gtk_x11_dep
|
|
||||||
if gst_gl_have_platform_glx
|
|
||||||
gtk_deps += gstglx11_dep
|
|
||||||
endif
|
|
||||||
have_gtk_gl_windowing = true
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
if gst_gl_have_window_wayland and gst_gl_have_platform_egl
|
|
||||||
gtk_wayland_dep = dependency('gtk4-wayland', required: false)
|
|
||||||
if gtk_wayland_dep.found()
|
|
||||||
gtk_deps += [gtk_wayland_dep, gstglwayland_dep]
|
|
||||||
have_gtk_gl_windowing = true
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
if gst_gl_have_platform_egl
|
|
||||||
gtk_deps += gstglegl_dep
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if not have_gtk_gl_windowing
|
if not have_gtk_gl_windowing
|
||||||
|
1
lib/gst/meson.build
vendored
1
lib/gst/meson.build
vendored
@@ -1 +1,2 @@
|
|||||||
subdir('clapper')
|
subdir('clapper')
|
||||||
|
subdir('plugin')
|
||||||
|
374
lib/gst/plugin/gstclapperbaseimport.c
vendored
Normal file
374
lib/gst/plugin/gstclapperbaseimport.c
vendored
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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 "gstclapperbaseimport.h"
|
||||||
|
#include "gstclappergdkmemory.h"
|
||||||
|
#include "gstclappergdkbufferpool.h"
|
||||||
|
|
||||||
|
#define GST_CAT_DEFAULT gst_clapper_base_import_debug
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||||
|
|
||||||
|
#define parent_class gst_clapper_base_import_parent_class
|
||||||
|
G_DEFINE_TYPE (GstClapperBaseImport, gst_clapper_base_import, GST_TYPE_BASE_TRANSFORM);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_base_import_init (GstClapperBaseImport *self)
|
||||||
|
{
|
||||||
|
g_mutex_init (&self->lock);
|
||||||
|
|
||||||
|
gst_video_info_init (&self->in_info);
|
||||||
|
gst_video_info_init (&self->out_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_base_import_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GstClapperBaseImport *self = GST_CLAPPER_BASE_IMPORT_CAST (object);
|
||||||
|
|
||||||
|
GST_TRACE ("Finalize");
|
||||||
|
g_mutex_clear (&self->lock);
|
||||||
|
|
||||||
|
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_clapper_base_import_change_state (GstElement *element, GstStateChange transition)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (element, "Changing state: %s => %s",
|
||||||
|
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
|
||||||
|
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
|
||||||
|
|
||||||
|
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_base_import_start (GstBaseTransform *bt)
|
||||||
|
{
|
||||||
|
GST_INFO_OBJECT (bt, "Start");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_base_import_stop (GstBaseTransform *bt)
|
||||||
|
{
|
||||||
|
GST_INFO_OBJECT (bt, "Stop");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_clapper_base_import_transform_caps (GstBaseTransform *bt,
|
||||||
|
GstPadDirection direction, GstCaps *caps, GstCaps *filter)
|
||||||
|
{
|
||||||
|
GstCaps *result, *tmp;
|
||||||
|
|
||||||
|
tmp = (direction == GST_PAD_SINK)
|
||||||
|
? gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SRC_PAD (bt))
|
||||||
|
: gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SINK_PAD (bt));
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
GST_DEBUG ("Intersecting with filter caps: %" GST_PTR_FORMAT, filter);
|
||||||
|
result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
|
||||||
|
gst_caps_unref (tmp);
|
||||||
|
} else {
|
||||||
|
result = tmp;
|
||||||
|
}
|
||||||
|
GST_DEBUG ("Returning %s caps: %" GST_PTR_FORMAT,
|
||||||
|
(direction == GST_PAD_SINK) ? "src" : "sink", result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_structure_filter_cb (GQuark field_id, GValue *value,
|
||||||
|
G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
const gchar *str = g_quark_to_string (field_id);
|
||||||
|
|
||||||
|
if (!strcmp (str, "format")
|
||||||
|
|| !strcmp (str, "width")
|
||||||
|
|| !strcmp (str, "height")
|
||||||
|
|| !strcmp (str, "pixel-aspect-ratio")
|
||||||
|
|| !strcmp (str, "framerate"))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_clapper_base_import_fixate_caps (GstBaseTransform *bt,
|
||||||
|
GstPadDirection direction, GstCaps *caps, GstCaps *othercaps)
|
||||||
|
{
|
||||||
|
GstCaps *fixated;
|
||||||
|
|
||||||
|
fixated = (!gst_caps_is_any (caps))
|
||||||
|
? gst_caps_fixate (gst_caps_ref (caps))
|
||||||
|
: gst_caps_copy (caps);
|
||||||
|
|
||||||
|
if (direction == GST_PAD_SINK) {
|
||||||
|
guint i, n = gst_caps_get_size (fixated);
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
GstCapsFeatures *features;
|
||||||
|
GstStructure *structure;
|
||||||
|
gboolean had_overlay_comp;
|
||||||
|
|
||||||
|
features = gst_caps_get_features (fixated, i);
|
||||||
|
had_overlay_comp = gst_caps_features_contains (features,
|
||||||
|
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
|
||||||
|
|
||||||
|
features = gst_caps_features_new (GST_CAPS_FEATURE_CLAPPER_GDK_MEMORY, NULL);
|
||||||
|
if (had_overlay_comp)
|
||||||
|
gst_caps_features_add (features, GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
|
||||||
|
|
||||||
|
gst_caps_set_features (fixated, i, features);
|
||||||
|
|
||||||
|
/* Remove fields that do not apply to our memory */
|
||||||
|
if ((structure = gst_caps_get_structure (fixated, i))) {
|
||||||
|
gst_structure_filter_and_map_in_place (structure,
|
||||||
|
(GstStructureFilterMapFunc) _structure_filter_cb, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GST_DEBUG ("Fixated %s caps: %" GST_PTR_FORMAT,
|
||||||
|
(direction == GST_PAD_SRC) ? "sink" : "src", fixated);
|
||||||
|
|
||||||
|
return fixated;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_base_import_set_caps (GstBaseTransform *bt,
|
||||||
|
GstCaps *incaps, GstCaps *outcaps)
|
||||||
|
{
|
||||||
|
GstClapperBaseImport *self = GST_CLAPPER_BASE_IMPORT_CAST (bt);
|
||||||
|
gboolean has_sink_info, has_src_info;
|
||||||
|
|
||||||
|
if ((has_sink_info = gst_video_info_from_caps (&self->in_info, incaps)))
|
||||||
|
GST_INFO_OBJECT (self, "Set sink caps: %" GST_PTR_FORMAT, incaps);
|
||||||
|
if ((has_src_info = gst_video_info_from_caps (&self->out_info, outcaps)))
|
||||||
|
GST_INFO_OBJECT (self, "Set src caps: %" GST_PTR_FORMAT, outcaps);
|
||||||
|
|
||||||
|
return (has_sink_info && has_src_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_base_import_import_propose_allocation (GstBaseTransform *bt,
|
||||||
|
GstQuery *decide_query, GstQuery *query)
|
||||||
|
{
|
||||||
|
GstClapperBaseImport *self = GST_CLAPPER_BASE_IMPORT_CAST (bt);
|
||||||
|
GstClapperBaseImportClass *bi_class = GST_CLAPPER_BASE_IMPORT_GET_CLASS (self);
|
||||||
|
GstBufferPool *pool = NULL;
|
||||||
|
GstCaps *caps;
|
||||||
|
GstVideoInfo info;
|
||||||
|
guint size;
|
||||||
|
gboolean need_pool;
|
||||||
|
|
||||||
|
if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (bt,
|
||||||
|
decide_query, query))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Passthrough, nothing to do */
|
||||||
|
if (!decide_query)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
gst_query_parse_allocation (query, &caps, &need_pool);
|
||||||
|
|
||||||
|
if (!caps) {
|
||||||
|
GST_DEBUG_OBJECT (self, "No caps specified");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gst_video_info_from_caps (&info, caps)) {
|
||||||
|
GST_DEBUG_OBJECT (self, "Invalid caps specified");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Normal size of a frame */
|
||||||
|
size = GST_VIDEO_INFO_SIZE (&info);
|
||||||
|
|
||||||
|
if (need_pool) {
|
||||||
|
GstStructure *config = NULL;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Need to create upstream pool");
|
||||||
|
pool = bi_class->create_upstream_pool (self, &config);
|
||||||
|
|
||||||
|
if (pool) {
|
||||||
|
/* If we did not get config, use default one */
|
||||||
|
if (!config)
|
||||||
|
config = gst_buffer_pool_get_config (pool);
|
||||||
|
|
||||||
|
gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
|
||||||
|
|
||||||
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||||
|
gst_object_unref (pool);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Failed to set config");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
} else if (config) {
|
||||||
|
GST_WARNING_OBJECT (self, "Got pool config without a pool to apply it!");
|
||||||
|
gst_structure_free (config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_query_add_allocation_pool (query, pool, size, 2, 0);
|
||||||
|
if (pool)
|
||||||
|
gst_object_unref (pool);
|
||||||
|
|
||||||
|
gst_query_add_allocation_meta (query,
|
||||||
|
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
|
||||||
|
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_base_import_decide_allocation (GstBaseTransform *bt, GstQuery *query)
|
||||||
|
{
|
||||||
|
GstClapperBaseImport *self = GST_CLAPPER_BASE_IMPORT_CAST (bt);
|
||||||
|
GstBufferPool *pool = NULL;
|
||||||
|
GstCaps *caps;
|
||||||
|
GstVideoInfo info;
|
||||||
|
guint size = 0, min = 0, max = 0;
|
||||||
|
gboolean update_pool, need_pool = TRUE;
|
||||||
|
|
||||||
|
gst_query_parse_allocation (query, &caps, NULL);
|
||||||
|
|
||||||
|
if (!caps) {
|
||||||
|
GST_DEBUG_OBJECT (self, "No caps specified");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gst_video_info_from_caps (&info, caps)) {
|
||||||
|
GST_DEBUG_OBJECT (self, "Invalid caps specified");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((update_pool = gst_query_get_n_allocation_pools (query) > 0)) {
|
||||||
|
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
|
||||||
|
if (pool) {
|
||||||
|
if ((need_pool = !GST_IS_CLAPPER_GDK_BUFFER_POOL (pool)))
|
||||||
|
gst_clear_object (&pool);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
size = GST_VIDEO_INFO_SIZE (&info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_pool) {
|
||||||
|
GstStructure *config;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Creating new downstream pool");
|
||||||
|
|
||||||
|
pool = gst_clapper_gdk_buffer_pool_new ();
|
||||||
|
config = gst_buffer_pool_get_config (pool);
|
||||||
|
|
||||||
|
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||||
|
gst_buffer_pool_config_set_params (config, caps, size, min, max);
|
||||||
|
|
||||||
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||||
|
gst_object_unref (pool);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Failed to set config");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update_pool)
|
||||||
|
gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
|
||||||
|
else
|
||||||
|
gst_query_add_allocation_pool (query, pool, size, min, max);
|
||||||
|
|
||||||
|
if (pool)
|
||||||
|
gst_object_unref (pool);
|
||||||
|
|
||||||
|
return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (bt, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstBufferPool *
|
||||||
|
gst_clapper_base_import_create_upstream_pool (GstClapperBaseImport *self, GstStructure **config)
|
||||||
|
{
|
||||||
|
GST_FIXME_OBJECT (self, "Need to create upstream buffer pool");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_base_import_class_init (GstClapperBaseImportClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||||
|
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||||
|
GstBaseTransformClass *gstbasetransform_class = (GstBaseTransformClass *) klass;
|
||||||
|
GstClapperBaseImportClass *bi_class = (GstClapperBaseImportClass *) klass;
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperbaseimport", 0,
|
||||||
|
"Clapper Base Import");
|
||||||
|
|
||||||
|
gobject_class->finalize = gst_clapper_base_import_finalize;
|
||||||
|
|
||||||
|
gstelement_class->change_state = gst_clapper_base_import_change_state;
|
||||||
|
|
||||||
|
gstbasetransform_class->passthrough_on_same_caps = TRUE;
|
||||||
|
gstbasetransform_class->transform_ip_on_passthrough = FALSE;
|
||||||
|
gstbasetransform_class->start = gst_clapper_base_import_start;
|
||||||
|
gstbasetransform_class->stop = gst_clapper_base_import_stop;
|
||||||
|
gstbasetransform_class->transform_caps = gst_clapper_base_import_transform_caps;
|
||||||
|
gstbasetransform_class->fixate_caps = gst_clapper_base_import_fixate_caps;
|
||||||
|
gstbasetransform_class->set_caps = gst_clapper_base_import_set_caps;
|
||||||
|
gstbasetransform_class->propose_allocation = gst_clapper_base_import_import_propose_allocation;
|
||||||
|
gstbasetransform_class->decide_allocation = gst_clapper_base_import_decide_allocation;
|
||||||
|
|
||||||
|
bi_class->create_upstream_pool = gst_clapper_base_import_create_upstream_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maps input video frame and output memory from in/out buffers
|
||||||
|
* using flags passed to this method.
|
||||||
|
*
|
||||||
|
* Remember to unmap both using `gst_video_frame_unmap` and
|
||||||
|
* `gst_memory_unmap` when done with the data.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_clapper_base_import_map_buffers (GstClapperBaseImport *self,
|
||||||
|
GstBuffer *in_buf, GstBuffer *out_buf, GstMapFlags in_flags, GstMapFlags out_flags,
|
||||||
|
GstVideoFrame *frame, GstMapInfo *info, GstMemory **mem)
|
||||||
|
{
|
||||||
|
GST_LOG_OBJECT (self, "Transforming from %" GST_PTR_FORMAT
|
||||||
|
" into %" GST_PTR_FORMAT, in_buf, out_buf);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!gst_video_frame_map (frame, &self->in_info, in_buf, in_flags))) {
|
||||||
|
GST_ERROR_OBJECT (self, "Could not map input buffer for reading");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*mem = gst_buffer_peek_memory (out_buf, 0);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!gst_memory_map (*mem, info, out_flags))) {
|
||||||
|
GST_ERROR_OBJECT (self, "Could not map output memory for writing");
|
||||||
|
gst_video_frame_unmap (frame);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
69
lib/gst/plugin/gstclapperbaseimport.h
vendored
Normal file
69
lib/gst/plugin/gstclapperbaseimport.h
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gst/base/gstbasetransform.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_CLAPPER_BASE_IMPORT (gst_clapper_base_import_get_type())
|
||||||
|
#define GST_IS_CLAPPER_BASE_IMPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_BASE_IMPORT))
|
||||||
|
#define GST_IS_CLAPPER_BASE_IMPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_BASE_IMPORT))
|
||||||
|
#define GST_CLAPPER_BASE_IMPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_BASE_IMPORT, GstClapperBaseImportClass))
|
||||||
|
#define GST_CLAPPER_BASE_IMPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_BASE_IMPORT, GstClapperBaseImport))
|
||||||
|
#define GST_CLAPPER_BASE_IMPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_BASE_IMPORT, GstClapperBaseImportClass))
|
||||||
|
#define GST_CLAPPER_BASE_IMPORT_CAST(obj) ((GstClapperBaseImport *)(obj))
|
||||||
|
|
||||||
|
#define GST_CLAPPER_BASE_IMPORT_GET_LOCK(obj) (&GST_CLAPPER_BASE_IMPORT_CAST(obj)->lock)
|
||||||
|
#define GST_CLAPPER_BASE_IMPORT_LOCK(obj) g_mutex_lock (GST_CLAPPER_BASE_IMPORT_GET_LOCK(obj))
|
||||||
|
#define GST_CLAPPER_BASE_IMPORT_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_BASE_IMPORT_GET_LOCK(obj))
|
||||||
|
|
||||||
|
typedef struct _GstClapperBaseImport GstClapperBaseImport;
|
||||||
|
typedef struct _GstClapperBaseImportClass GstClapperBaseImportClass;
|
||||||
|
|
||||||
|
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperBaseImport, gst_object_unref)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct _GstClapperBaseImport
|
||||||
|
{
|
||||||
|
GstBaseTransform parent;
|
||||||
|
|
||||||
|
GMutex lock;
|
||||||
|
|
||||||
|
GstVideoInfo in_info, out_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstClapperBaseImportClass
|
||||||
|
{
|
||||||
|
GstBaseTransformClass parent_class;
|
||||||
|
|
||||||
|
GstBufferPool * (* create_upstream_pool) (GstClapperBaseImport *bi,
|
||||||
|
GstStructure **config);
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_clapper_base_import_get_type (void);
|
||||||
|
|
||||||
|
gboolean gst_clapper_base_import_map_buffers (GstClapperBaseImport *bi,
|
||||||
|
GstBuffer *in_buf, GstBuffer *out_buf, GstMapFlags in_flags, GstMapFlags out_flags,
|
||||||
|
GstVideoFrame *frame, GstMapInfo *info, GstMemory **mem);
|
||||||
|
|
||||||
|
G_END_DECLS
|
173
lib/gst/plugin/gstclappergdkbufferpool.c
vendored
Normal file
173
lib/gst/plugin/gstclappergdkbufferpool.c
vendored
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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 "gstclappergdkbufferpool.h"
|
||||||
|
|
||||||
|
#define GST_CAT_DEFAULT gst_clapper_gdk_buffer_pool_debug
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||||
|
|
||||||
|
#define parent_class gst_clapper_gdk_buffer_pool_parent_class
|
||||||
|
G_DEFINE_TYPE (GstClapperGdkBufferPool, gst_clapper_gdk_buffer_pool, GST_TYPE_BUFFER_POOL);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gdk_buffer_pool_init (GstClapperGdkBufferPool *pool)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static const gchar **
|
||||||
|
gst_clapper_gdk_buffer_pool_get_options (GstBufferPool *pool)
|
||||||
|
{
|
||||||
|
static const gchar *options[] = {
|
||||||
|
GST_BUFFER_POOL_OPTION_VIDEO_META,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_gdk_buffer_pool_set_config (GstBufferPool *pool, GstStructure *config)
|
||||||
|
{
|
||||||
|
GstClapperGdkBufferPool *self = GST_CLAPPER_GDK_BUFFER_POOL_CAST (pool);
|
||||||
|
GstCaps *caps = NULL;
|
||||||
|
guint size, min_buffers, max_buffers;
|
||||||
|
GstVideoInfo info;
|
||||||
|
GstClapperGdkMemory *clapper_mem;
|
||||||
|
|
||||||
|
if (!gst_buffer_pool_config_get_params (config, &caps, &size,
|
||||||
|
&min_buffers, &max_buffers)) {
|
||||||
|
GST_WARNING_OBJECT (self, "Invalid buffer pool config");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!caps || !gst_video_info_from_caps (&info, caps)) {
|
||||||
|
GST_WARNING_OBJECT (pool, "Could not parse caps into video info");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_clear_object (&self->allocator);
|
||||||
|
self->allocator = GST_CLAPPER_GDK_ALLOCATOR_CAST (
|
||||||
|
gst_allocator_find (GST_CLAPPER_GDK_MEMORY_TYPE_NAME));
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!self->allocator)) {
|
||||||
|
GST_ERROR_OBJECT (self, "ClapperGdkAllocator is unavailable");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
clapper_mem = GST_CLAPPER_GDK_MEMORY_CAST (
|
||||||
|
gst_clapper_gdk_allocator_alloc (self->allocator, &info));
|
||||||
|
if (G_UNLIKELY (!clapper_mem)) {
|
||||||
|
GST_ERROR_OBJECT (self, "Cannot create ClapperGdkMemory");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_buffer_pool_config_set_params (config, caps,
|
||||||
|
GST_VIDEO_INFO_SIZE (&clapper_mem->info), min_buffers, max_buffers);
|
||||||
|
gst_memory_unref (GST_MEMORY_CAST (clapper_mem));
|
||||||
|
|
||||||
|
self->info = info;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Set buffer pool config: %" GST_PTR_FORMAT, config);
|
||||||
|
|
||||||
|
return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_clapper_gdk_buffer_pool_alloc (GstBufferPool *pool, GstBuffer **buffer,
|
||||||
|
GstBufferPoolAcquireParams *params)
|
||||||
|
{
|
||||||
|
GstClapperGdkBufferPool *self = GST_CLAPPER_GDK_BUFFER_POOL_CAST (pool);
|
||||||
|
GstMemory *mem;
|
||||||
|
GstClapperGdkMemory *clapper_mem;
|
||||||
|
|
||||||
|
mem = gst_clapper_gdk_allocator_alloc (self->allocator, &self->info);
|
||||||
|
if (G_UNLIKELY (!mem)) {
|
||||||
|
GST_ERROR_OBJECT (self, "Cannot create ClapperGdkMemory");
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
clapper_mem = GST_CLAPPER_GDK_MEMORY_CAST (mem);
|
||||||
|
|
||||||
|
*buffer = gst_buffer_new ();
|
||||||
|
gst_buffer_append_memory (*buffer, mem);
|
||||||
|
|
||||||
|
gst_buffer_add_video_meta_full (*buffer, GST_VIDEO_FRAME_FLAG_NONE,
|
||||||
|
GST_VIDEO_INFO_FORMAT (&self->info), GST_VIDEO_INFO_WIDTH (&self->info),
|
||||||
|
GST_VIDEO_INFO_HEIGHT (&self->info), GST_VIDEO_INFO_N_PLANES (&self->info),
|
||||||
|
clapper_mem->info.offset, clapper_mem->info.stride);
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (self, "Allocated %" GST_PTR_FORMAT, *buffer);
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gdk_buffer_reset_buffer (GstBufferPool *pool, GstBuffer *buffer)
|
||||||
|
{
|
||||||
|
GstClapperGdkMemory *clapper_mem;
|
||||||
|
|
||||||
|
GST_TRACE ("Reset %" GST_PTR_FORMAT, buffer);
|
||||||
|
|
||||||
|
clapper_mem = GST_CLAPPER_GDK_MEMORY_CAST (gst_buffer_peek_memory (buffer, 0));
|
||||||
|
g_clear_object (&clapper_mem->texture);
|
||||||
|
|
||||||
|
return GST_BUFFER_POOL_CLASS (parent_class)->reset_buffer (pool, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gdk_buffer_pool_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GstClapperGdkBufferPool *self = GST_CLAPPER_GDK_BUFFER_POOL_CAST (object);
|
||||||
|
|
||||||
|
gst_clear_object (&self->allocator);
|
||||||
|
|
||||||
|
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gdk_buffer_pool_class_init (GstClapperGdkBufferPoolClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||||
|
GstBufferPoolClass *bufferpool_class = (GstBufferPoolClass *) klass;
|
||||||
|
|
||||||
|
gobject_class->dispose = gst_clapper_gdk_buffer_pool_dispose;
|
||||||
|
|
||||||
|
bufferpool_class->get_options = gst_clapper_gdk_buffer_pool_get_options;
|
||||||
|
bufferpool_class->set_config = gst_clapper_gdk_buffer_pool_set_config;
|
||||||
|
bufferpool_class->alloc_buffer = gst_clapper_gdk_buffer_pool_alloc;
|
||||||
|
bufferpool_class->reset_buffer = gst_clapper_gdk_buffer_reset_buffer;
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergdkbufferpool", 0,
|
||||||
|
"Clapper Gdk Buffer Pool");
|
||||||
|
}
|
||||||
|
|
||||||
|
GstBufferPool *
|
||||||
|
gst_clapper_gdk_buffer_pool_new (void)
|
||||||
|
{
|
||||||
|
GstClapperGdkBufferPool *self;
|
||||||
|
|
||||||
|
self = g_object_new (GST_TYPE_CLAPPER_GDK_BUFFER_POOL, NULL);
|
||||||
|
gst_object_ref_sink (self);
|
||||||
|
|
||||||
|
return GST_BUFFER_POOL_CAST (self);
|
||||||
|
}
|
44
lib/gst/plugin/gstclappergdkbufferpool.h
vendored
Normal file
44
lib/gst/plugin/gstclappergdkbufferpool.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gst/gstbufferpool.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
#include "gstclappergdkmemory.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_CLAPPER_GDK_BUFFER_POOL (gst_clapper_gdk_buffer_pool_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE (GstClapperGdkBufferPool, gst_clapper_gdk_buffer_pool, GST, CLAPPER_GDK_BUFFER_POOL, GstBufferPool)
|
||||||
|
|
||||||
|
#define GST_CLAPPER_GDK_BUFFER_POOL_CAST(obj) ((GstClapperGdkBufferPool *)(obj))
|
||||||
|
|
||||||
|
struct _GstClapperGdkBufferPool
|
||||||
|
{
|
||||||
|
GstBufferPool parent;
|
||||||
|
|
||||||
|
GstClapperGdkAllocator *allocator;
|
||||||
|
GstVideoInfo info;
|
||||||
|
};
|
||||||
|
|
||||||
|
GstBufferPool * gst_clapper_gdk_buffer_pool_new (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
141
lib/gst/plugin/gstclappergdkmemory.c
vendored
Normal file
141
lib/gst/plugin/gstclappergdkmemory.c
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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 "gstclappergdkmemory.h"
|
||||||
|
#include "gstgtkutils.h"
|
||||||
|
|
||||||
|
#define GST_CAT_DEFAULT gst_clapper_gdk_allocator_debug
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||||
|
|
||||||
|
static GstAllocator *_gst_clapper_gdk_allocator = NULL;
|
||||||
|
|
||||||
|
#define parent_class gst_clapper_gdk_allocator_parent_class
|
||||||
|
G_DEFINE_TYPE (GstClapperGdkAllocator, gst_clapper_gdk_allocator, GST_TYPE_ALLOCATOR);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gdk_allocator_free (GstAllocator *self, GstMemory *memory)
|
||||||
|
{
|
||||||
|
GstClapperGdkMemory *mem = GST_CLAPPER_GDK_MEMORY_CAST (memory);
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (self, "Freeing ClapperGdkMemory: %" GST_PTR_FORMAT, mem);
|
||||||
|
|
||||||
|
g_clear_object (&mem->texture);
|
||||||
|
g_free (mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
gst_clapper_gdk_mem_map_full (GstMemory *memory, GstMapInfo *info, gsize maxsize)
|
||||||
|
{
|
||||||
|
GstClapperGdkMemory *mem = GST_CLAPPER_GDK_MEMORY_CAST (memory);
|
||||||
|
|
||||||
|
return &mem->texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gdk_mem_unmap_full (GstMemory *memory, GstMapInfo *info)
|
||||||
|
{
|
||||||
|
/* NOOP */
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstMemory *
|
||||||
|
gst_clapper_gdk_mem_copy (GstMemory *memory, gssize offset, gssize size)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstMemory *
|
||||||
|
gst_clapper_gdk_mem_share (GstMemory *memory, gssize offset, gssize size)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_gdk_mem_is_span (GstMemory *mem1, GstMemory *mem2, gsize *offset)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gdk_allocator_init (GstClapperGdkAllocator *self)
|
||||||
|
{
|
||||||
|
GstAllocator *alloc = GST_ALLOCATOR_CAST (self);
|
||||||
|
|
||||||
|
alloc->mem_type = GST_CLAPPER_GDK_MEMORY_TYPE_NAME;
|
||||||
|
alloc->mem_map_full = gst_clapper_gdk_mem_map_full;
|
||||||
|
alloc->mem_unmap_full = gst_clapper_gdk_mem_unmap_full;
|
||||||
|
alloc->mem_copy = gst_clapper_gdk_mem_copy;
|
||||||
|
alloc->mem_share = gst_clapper_gdk_mem_share;
|
||||||
|
alloc->mem_is_span = gst_clapper_gdk_mem_is_span;
|
||||||
|
|
||||||
|
GST_OBJECT_FLAG_SET (self, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gdk_allocator_class_init (GstClapperGdkAllocatorClass *klass)
|
||||||
|
{
|
||||||
|
GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
|
||||||
|
|
||||||
|
allocator_class->alloc = NULL;
|
||||||
|
allocator_class->free = GST_DEBUG_FUNCPTR (gst_clapper_gdk_allocator_free);
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergdkallocator", 0,
|
||||||
|
"Clapper Gdk Allocator");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_clapper_gdk_memory_init_once (void)
|
||||||
|
{
|
||||||
|
static gsize _alloc_init = 0;
|
||||||
|
|
||||||
|
if (g_once_init_enter (&_alloc_init)) {
|
||||||
|
_gst_clapper_gdk_allocator = GST_ALLOCATOR_CAST (
|
||||||
|
g_object_new (GST_TYPE_CLAPPER_GDK_ALLOCATOR, NULL));
|
||||||
|
gst_object_ref_sink (_gst_clapper_gdk_allocator);
|
||||||
|
|
||||||
|
gst_allocator_register (GST_CLAPPER_GDK_MEMORY_TYPE_NAME, _gst_clapper_gdk_allocator);
|
||||||
|
g_once_init_leave (&_alloc_init, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_is_clapper_gdk_memory (GstMemory *memory)
|
||||||
|
{
|
||||||
|
return (memory != NULL && memory->allocator != NULL
|
||||||
|
&& GST_IS_CLAPPER_GDK_ALLOCATOR (memory->allocator));
|
||||||
|
}
|
||||||
|
|
||||||
|
GstMemory *
|
||||||
|
gst_clapper_gdk_allocator_alloc (GstClapperGdkAllocator *self, const GstVideoInfo *info)
|
||||||
|
{
|
||||||
|
GstClapperGdkMemory *mem;
|
||||||
|
|
||||||
|
mem = g_new0 (GstClapperGdkMemory, 1);
|
||||||
|
mem->info = *info;
|
||||||
|
|
||||||
|
gst_memory_init (GST_MEMORY_CAST (mem), 0, GST_ALLOCATOR_CAST (self),
|
||||||
|
NULL, GST_VIDEO_INFO_SIZE (info), 0, 0, GST_VIDEO_INFO_SIZE (info));
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (self, "Allocated new ClapperGdkMemory: %" GST_PTR_FORMAT, mem);
|
||||||
|
|
||||||
|
return GST_MEMORY_CAST (mem);
|
||||||
|
}
|
67
lib/gst/plugin/gstclappergdkmemory.h
vendored
Normal file
67
lib/gst/plugin/gstclappergdkmemory.h
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gst/gstmemory.h>
|
||||||
|
#include <gst/gstallocator.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_CLAPPER_GDK_ALLOCATOR (gst_clapper_gdk_allocator_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE (GstClapperGdkAllocator, gst_clapper_gdk_allocator, GST, CLAPPER_GDK_ALLOCATOR, GstAllocator)
|
||||||
|
|
||||||
|
#define GST_CLAPPER_GDK_ALLOCATOR_CAST(obj) ((GstClapperGdkAllocator *)(obj))
|
||||||
|
#define GST_CLAPPER_GDK_MEMORY_CAST(mem) ((GstClapperGdkMemory *)(mem))
|
||||||
|
|
||||||
|
#define GST_CLAPPER_GDK_MEMORY_TYPE_NAME "gst.clapper.gdk.memory"
|
||||||
|
|
||||||
|
#define GST_CAPS_FEATURE_CLAPPER_GDK_MEMORY "memory:ClapperGdkMemory"
|
||||||
|
|
||||||
|
#define GST_CLAPPER_GDK_MEMORY_FORMATS \
|
||||||
|
"RGBA64_LE, RGBA64_BE, ABGR, BGRA, " \
|
||||||
|
"ARGB, RGBA, BGRx, RGBx, BGR, RGB" \
|
||||||
|
|
||||||
|
/* Formats that `GdkGLTexture` supports */
|
||||||
|
#define GST_CLAPPER_GDK_GL_TEXTURE_FORMATS \
|
||||||
|
"RGBA64_LE, RGBA64_BE, RGBA, RGBx, RGB" \
|
||||||
|
|
||||||
|
typedef struct _GstClapperGdkMemory GstClapperGdkMemory;
|
||||||
|
struct _GstClapperGdkMemory
|
||||||
|
{
|
||||||
|
GstMemory mem;
|
||||||
|
|
||||||
|
GdkTexture *texture;
|
||||||
|
GstVideoInfo info;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstClapperGdkAllocator
|
||||||
|
{
|
||||||
|
GstAllocator parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
void gst_clapper_gdk_memory_init_once (void);
|
||||||
|
|
||||||
|
gboolean gst_is_clapper_gdk_memory (GstMemory *memory);
|
||||||
|
|
||||||
|
GstMemory * gst_clapper_gdk_allocator_alloc (GstClapperGdkAllocator *allocator, const GstVideoInfo *info);
|
||||||
|
|
||||||
|
G_END_DECLS
|
128
lib/gst/plugin/gstclapperimport.c
vendored
Normal file
128
lib/gst/plugin/gstclapperimport.c
vendored
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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 "gstclapperimport.h"
|
||||||
|
#include "gstclappergdkmemory.h"
|
||||||
|
#include "gstgtkutils.h"
|
||||||
|
|
||||||
|
#define GST_CAT_DEFAULT gst_clapper_import_debug
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||||
|
|
||||||
|
static GstStaticPadTemplate gst_clapper_import_sink_template =
|
||||||
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
|
GST_PAD_SINK,
|
||||||
|
GST_PAD_ALWAYS,
|
||||||
|
GST_STATIC_CAPS (
|
||||||
|
GST_VIDEO_CAPS_MAKE ("{ " GST_CLAPPER_GDK_MEMORY_FORMATS " }")));
|
||||||
|
|
||||||
|
static GstStaticPadTemplate gst_clapper_import_src_template =
|
||||||
|
GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
|
GST_PAD_SRC,
|
||||||
|
GST_PAD_ALWAYS,
|
||||||
|
GST_STATIC_CAPS (
|
||||||
|
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_CLAPPER_GDK_MEMORY,
|
||||||
|
"{ " GST_CLAPPER_GDK_MEMORY_FORMATS " }")));
|
||||||
|
|
||||||
|
#define parent_class gst_clapper_import_parent_class
|
||||||
|
G_DEFINE_TYPE (GstClapperImport, gst_clapper_import, GST_TYPE_CLAPPER_BASE_IMPORT);
|
||||||
|
GST_ELEMENT_REGISTER_DEFINE (clapperimport, "clapperimport", GST_RANK_NONE,
|
||||||
|
GST_TYPE_CLAPPER_IMPORT);
|
||||||
|
|
||||||
|
static GstBufferPool *
|
||||||
|
gst_clapper_import_create_upstream_pool (GstClapperBaseImport *bi, GstStructure **config)
|
||||||
|
{
|
||||||
|
GstClapperImport *self = GST_CLAPPER_IMPORT_CAST (bi);
|
||||||
|
GstBufferPool *pool;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Creating new upstream pool");
|
||||||
|
|
||||||
|
pool = gst_video_buffer_pool_new ();
|
||||||
|
*config = gst_buffer_pool_get_config (pool);
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
video_frame_unmap_and_free (GstVideoFrame *frame)
|
||||||
|
{
|
||||||
|
gst_video_frame_unmap (frame);
|
||||||
|
g_slice_free (GstVideoFrame, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_clapper_import_transform (GstBaseTransform *bt,
|
||||||
|
GstBuffer *in_buf, GstBuffer *out_buf)
|
||||||
|
{
|
||||||
|
GstClapperBaseImport *bi = GST_CLAPPER_BASE_IMPORT_CAST (bt);
|
||||||
|
GstVideoFrame *frame;
|
||||||
|
GstMapInfo info;
|
||||||
|
GstMemory *memory;
|
||||||
|
|
||||||
|
frame = g_slice_new (GstVideoFrame);
|
||||||
|
|
||||||
|
if (!gst_clapper_base_import_map_buffers (bi, in_buf, out_buf,
|
||||||
|
GST_MAP_READ, GST_MAP_WRITE, frame, &info, &memory)) {
|
||||||
|
g_slice_free (GstVideoFrame, frame);
|
||||||
|
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep frame data alive as long as necessary,
|
||||||
|
* unmap only after bytes are destroyed */
|
||||||
|
GST_CLAPPER_GDK_MEMORY_CAST (memory)->texture = gst_video_frame_into_gdk_texture (
|
||||||
|
frame, (GDestroyNotify) video_frame_unmap_and_free);
|
||||||
|
|
||||||
|
gst_memory_unmap (memory, &info);
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_import_init (GstClapperImport *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_import_class_init (GstClapperImportClass *klass)
|
||||||
|
{
|
||||||
|
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||||
|
GstBaseTransformClass *gstbasetransform_class = (GstBaseTransformClass *) klass;
|
||||||
|
GstClapperBaseImportClass *bi_class = (GstClapperBaseImportClass *) klass;
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperimport", 0,
|
||||||
|
"Clapper Import");
|
||||||
|
|
||||||
|
gstbasetransform_class->transform = gst_clapper_import_transform;
|
||||||
|
|
||||||
|
bi_class->create_upstream_pool = gst_clapper_import_create_upstream_pool;
|
||||||
|
|
||||||
|
gst_element_class_set_metadata (gstelement_class,
|
||||||
|
"Clapper import",
|
||||||
|
"Filter/Video", "Imports RAW video data into ClapperGdkMemory",
|
||||||
|
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
||||||
|
|
||||||
|
gst_element_class_add_static_pad_template (gstelement_class,
|
||||||
|
&gst_clapper_import_sink_template);
|
||||||
|
gst_element_class_add_static_pad_template (gstelement_class,
|
||||||
|
&gst_clapper_import_src_template);
|
||||||
|
}
|
38
lib/gst/plugin/gstclapperimport.h
vendored
Normal file
38
lib/gst/plugin/gstclapperimport.h
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gstclapperbaseimport.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_CLAPPER_IMPORT (gst_clapper_import_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE (GstClapperImport, gst_clapper_import, GST, CLAPPER_IMPORT, GstClapperBaseImport)
|
||||||
|
|
||||||
|
#define GST_CLAPPER_IMPORT_CAST(obj) ((GstClapperImport *)(obj))
|
||||||
|
|
||||||
|
struct _GstClapperImport
|
||||||
|
{
|
||||||
|
GstClapperBaseImport parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
GST_ELEMENT_REGISTER_DECLARE (clapperimport);
|
||||||
|
|
||||||
|
G_END_DECLS
|
635
lib/gst/plugin/gstclapperpaintable.c
vendored
Normal file
635
lib/gst/plugin/gstclapperpaintable.c
vendored
Normal file
@@ -0,0 +1,635 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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 "gstclapperpaintable.h"
|
||||||
|
#include "gstclappergdkmemory.h"
|
||||||
|
#include "gstgtkutils.h"
|
||||||
|
|
||||||
|
#define DEFAULT_PAR_N 1
|
||||||
|
#define DEFAULT_PAR_D 1
|
||||||
|
|
||||||
|
#define GST_CAT_DEFAULT gst_clapper_paintable_debug
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GdkTexture *texture;
|
||||||
|
GstVideoOverlayRectangle *rectangle;
|
||||||
|
|
||||||
|
gint x, y;
|
||||||
|
guint width, height;
|
||||||
|
|
||||||
|
gboolean used;
|
||||||
|
} GstClapperGdkOverlay;
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gdk_overlay_free (GstClapperGdkOverlay *overlay)
|
||||||
|
{
|
||||||
|
GST_TRACE ("Freeing overlay: %" GST_PTR_FORMAT, overlay);
|
||||||
|
|
||||||
|
g_object_unref (overlay->texture);
|
||||||
|
gst_video_overlay_rectangle_unref (overlay->rectangle);
|
||||||
|
g_slice_free (GstClapperGdkOverlay, overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gst_clapper_paintable_iface_init (GdkPaintableInterface *iface);
|
||||||
|
static void gst_clapper_paintable_dispose (GObject *object);
|
||||||
|
static void gst_clapper_paintable_finalize (GObject *object);
|
||||||
|
|
||||||
|
#define parent_class gst_clapper_paintable_parent_class
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (GstClapperPaintable, gst_clapper_paintable, G_TYPE_OBJECT,
|
||||||
|
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||||
|
gst_clapper_paintable_iface_init));
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_paintable_class_init (GstClapperPaintableClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperpaintable", 0,
|
||||||
|
"Clapper Paintable");
|
||||||
|
|
||||||
|
gobject_class->dispose = gst_clapper_paintable_dispose;
|
||||||
|
gobject_class->finalize = gst_clapper_paintable_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_paintable_init (GstClapperPaintable *self)
|
||||||
|
{
|
||||||
|
self->par_n = DEFAULT_PAR_N;
|
||||||
|
self->par_d = DEFAULT_PAR_D;
|
||||||
|
|
||||||
|
g_mutex_init (&self->lock);
|
||||||
|
gst_video_info_init (&self->v_info);
|
||||||
|
g_weak_ref_init (&self->widget, NULL);
|
||||||
|
|
||||||
|
self->overlays = g_ptr_array_new_with_free_func (
|
||||||
|
(GDestroyNotify) gst_clapper_gdk_overlay_free);
|
||||||
|
|
||||||
|
gdk_rgba_parse (&self->bg, "black");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_paintable_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE (object);
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||||
|
|
||||||
|
if (self->draw_id > 0) {
|
||||||
|
g_source_remove (self->draw_id);
|
||||||
|
self->draw_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
|
||||||
|
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_paintable_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE (object);
|
||||||
|
|
||||||
|
GST_TRACE ("Finalize");
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||||
|
|
||||||
|
g_weak_ref_clear (&self->widget);
|
||||||
|
|
||||||
|
gst_clear_buffer (&self->pending_buffer);
|
||||||
|
gst_clear_buffer (&self->buffer);
|
||||||
|
|
||||||
|
g_ptr_array_unref (self->overlays);
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
|
||||||
|
g_mutex_clear (&self->lock);
|
||||||
|
|
||||||
|
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
calculate_display_par (GstClapperPaintable *self, GstVideoInfo *info)
|
||||||
|
{
|
||||||
|
gint width, height, par_n, par_d, req_par_n, req_par_d;
|
||||||
|
gboolean success;
|
||||||
|
|
||||||
|
width = GST_VIDEO_INFO_WIDTH (info);
|
||||||
|
height = GST_VIDEO_INFO_HEIGHT (info);
|
||||||
|
|
||||||
|
/* Cannot apply aspect ratio if there is no video */
|
||||||
|
if (width == 0 || height == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
par_n = GST_VIDEO_INFO_PAR_N (info);
|
||||||
|
par_d = GST_VIDEO_INFO_PAR_D (info);
|
||||||
|
|
||||||
|
req_par_n = self->par_n;
|
||||||
|
req_par_d = self->par_d;
|
||||||
|
|
||||||
|
if (par_n == 0)
|
||||||
|
par_n = 1;
|
||||||
|
|
||||||
|
/* Use defaults if user set zero */
|
||||||
|
if (req_par_n == 0 || req_par_d == 0) {
|
||||||
|
req_par_n = DEFAULT_PAR_N;
|
||||||
|
req_par_d = DEFAULT_PAR_D;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (self, "PAR: %u/%u, DAR: %u/%u", par_n, par_d, req_par_n, req_par_d);
|
||||||
|
|
||||||
|
if (!(success = gst_video_calculate_display_ratio (&self->display_ratio_num,
|
||||||
|
&self->display_ratio_den, width, height, par_n, par_d,
|
||||||
|
req_par_n, req_par_d))) {
|
||||||
|
GST_ERROR_OBJECT (self, "Could not calculate display ratio values");
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
invalidate_paintable_size_internal (GstClapperPaintable *self)
|
||||||
|
{
|
||||||
|
gint video_width, video_height;
|
||||||
|
guint display_ratio_num, display_ratio_den;
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||||
|
|
||||||
|
video_width = GST_VIDEO_INFO_WIDTH (&self->v_info);
|
||||||
|
video_height = GST_VIDEO_INFO_HEIGHT (&self->v_info);
|
||||||
|
|
||||||
|
display_ratio_num = self->display_ratio_num;
|
||||||
|
display_ratio_den = self->display_ratio_den;
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
|
||||||
|
if (video_height % display_ratio_den == 0) {
|
||||||
|
GST_LOG ("Keeping video height");
|
||||||
|
|
||||||
|
self->display_width = (guint)
|
||||||
|
gst_util_uint64_scale_int (video_height, display_ratio_num, display_ratio_den);
|
||||||
|
self->display_height = video_height;
|
||||||
|
} else if (video_width % display_ratio_num == 0) {
|
||||||
|
GST_LOG ("Keeping video width");
|
||||||
|
|
||||||
|
self->display_width = video_width;
|
||||||
|
self->display_height = (guint)
|
||||||
|
gst_util_uint64_scale_int (video_width, display_ratio_den, display_ratio_num);
|
||||||
|
} else {
|
||||||
|
GST_LOG ("Approximating while keeping video height");
|
||||||
|
|
||||||
|
self->display_width = (guint)
|
||||||
|
gst_util_uint64_scale_int (video_height, display_ratio_num, display_ratio_den);
|
||||||
|
self->display_height = video_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->display_aspect_ratio = ((gdouble) self->display_width
|
||||||
|
/ (gdouble) self->display_height);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Invalidate paintable size, display: %dx%d",
|
||||||
|
self->display_width, self->display_height);
|
||||||
|
gdk_paintable_invalidate_size ((GdkPaintable *) self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
invalidate_paintable_size_on_main_cb (GstClapperPaintable *self)
|
||||||
|
{
|
||||||
|
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||||
|
self->draw_id = 0;
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
|
||||||
|
invalidate_paintable_size_internal (self);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
comp_frame_unmap_and_free (GstVideoFrame *frame)
|
||||||
|
{
|
||||||
|
gst_video_frame_unmap (frame);
|
||||||
|
g_slice_free (GstVideoFrame, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstClapperGdkOverlay *
|
||||||
|
_get_cached_overlay (GPtrArray *overlays, GstVideoOverlayRectangle *rectangle, guint *index)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < overlays->len; i++) {
|
||||||
|
GstClapperGdkOverlay *overlay = g_ptr_array_index (overlays, i);
|
||||||
|
|
||||||
|
if (overlay->rectangle != rectangle)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*index = i;
|
||||||
|
return overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_paintable_prepare_overlays (GstClapperPaintable *self)
|
||||||
|
{
|
||||||
|
GstVideoOverlayCompositionMeta *comp_meta;
|
||||||
|
guint num_overlays, i;
|
||||||
|
|
||||||
|
/* As long as this is called from main thread, no need to lock here */
|
||||||
|
if (G_UNLIKELY (!self->buffer)
|
||||||
|
|| !(comp_meta = gst_buffer_get_video_overlay_composition_meta (self->buffer))) {
|
||||||
|
guint n_pending = self->overlays->len;
|
||||||
|
|
||||||
|
/* Remove all cached overlays if new buffer does not have any */
|
||||||
|
if (n_pending > 0) {
|
||||||
|
GST_TRACE ("No overlays in buffer, removing all cached ones");
|
||||||
|
g_ptr_array_remove_range (self->overlays, 0, n_pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (self, "Preparing overlays...");
|
||||||
|
|
||||||
|
/* Mark all old overlays as unused */
|
||||||
|
for (i = 0; i < self->overlays->len; i++) {
|
||||||
|
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->overlays, i);
|
||||||
|
overlay->used = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_overlays = gst_video_overlay_composition_n_rectangles (comp_meta->overlay);
|
||||||
|
|
||||||
|
for (i = 0; i < num_overlays; i++) {
|
||||||
|
GdkTexture *texture;
|
||||||
|
GstBuffer *comp_buffer;
|
||||||
|
GstVideoFrame *comp_frame;
|
||||||
|
GstVideoMeta *vmeta;
|
||||||
|
GstVideoInfo vinfo;
|
||||||
|
GstVideoOverlayRectangle *rectangle;
|
||||||
|
GstClapperGdkOverlay *overlay;
|
||||||
|
GstVideoOverlayFormatFlags flags, alpha_flags = 0;
|
||||||
|
gint comp_x, comp_y;
|
||||||
|
guint comp_width, comp_height, cached_index;
|
||||||
|
|
||||||
|
rectangle = gst_video_overlay_composition_get_rectangle (comp_meta->overlay, i);
|
||||||
|
|
||||||
|
if ((overlay = _get_cached_overlay (self->overlays, rectangle, &cached_index))) {
|
||||||
|
overlay->used = TRUE;
|
||||||
|
|
||||||
|
/* Place overlay at expected position */
|
||||||
|
if (i != cached_index) {
|
||||||
|
GST_LOG ("Rearranging overlay position: %u => %u", cached_index, i);
|
||||||
|
|
||||||
|
overlay = g_ptr_array_steal_index_fast (self->overlays, cached_index);
|
||||||
|
g_ptr_array_insert (self->overlays, i, overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_TRACE ("Reusing cached overlay: %" GST_PTR_FORMAT, overlay);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!gst_video_overlay_rectangle_get_render_rectangle (rectangle,
|
||||||
|
&comp_x, &comp_y, &comp_width, &comp_height))) {
|
||||||
|
GST_WARNING ("Invalid overlay rectangle dimensions: %" GST_PTR_FORMAT, rectangle);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = gst_video_overlay_rectangle_get_flags (rectangle);
|
||||||
|
|
||||||
|
if (flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)
|
||||||
|
alpha_flags |= GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
|
||||||
|
|
||||||
|
comp_buffer = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rectangle, alpha_flags);
|
||||||
|
comp_frame = g_slice_new (GstVideoFrame);
|
||||||
|
|
||||||
|
/* Update overlay video info from video meta */
|
||||||
|
if ((vmeta = gst_buffer_get_video_meta (comp_buffer))) {
|
||||||
|
gst_video_info_set_format (&vinfo, vmeta->format, vmeta->width, vmeta->height);
|
||||||
|
vinfo.stride[0] = vmeta->stride[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!gst_video_frame_map (comp_frame, &vinfo, comp_buffer, GST_MAP_READ))) {
|
||||||
|
g_slice_free (GstVideoFrame, comp_frame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((texture = gst_video_frame_into_gdk_texture (
|
||||||
|
comp_frame, (GDestroyNotify) comp_frame_unmap_and_free))) {
|
||||||
|
overlay = g_slice_new (GstClapperGdkOverlay);
|
||||||
|
overlay->texture = texture;
|
||||||
|
overlay->rectangle = gst_video_overlay_rectangle_ref (rectangle);
|
||||||
|
overlay->x = comp_x;
|
||||||
|
overlay->y = comp_y;
|
||||||
|
overlay->width = comp_width;
|
||||||
|
overlay->height = comp_height;
|
||||||
|
overlay->used = TRUE;
|
||||||
|
|
||||||
|
GST_TRACE ("Created overlay: %"
|
||||||
|
GST_PTR_FORMAT ", x: %i, y: %i, width: %u, height: %u",
|
||||||
|
overlay, overlay->x, overlay->y, overlay->width, overlay->height);
|
||||||
|
|
||||||
|
g_ptr_array_insert (self->overlays, i, overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove all overlays that are not going to be used */
|
||||||
|
for (i = self->overlays->len; i > 0; i--) {
|
||||||
|
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->overlays, i - 1);
|
||||||
|
|
||||||
|
if (!overlay->used) {
|
||||||
|
GST_TRACE ("Removing unused cached overlay: %" GST_PTR_FORMAT, overlay);
|
||||||
|
g_ptr_array_remove (self->overlays, overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY (num_overlays != self->overlays->len)) {
|
||||||
|
GST_WARNING_OBJECT (self, "Some overlays could not be prepared, %u != %u",
|
||||||
|
num_overlays, self->overlays->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (self, "Prepared overlays: %u", self->overlays->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
update_paintable_on_main_cb (GstClapperPaintable *self)
|
||||||
|
{
|
||||||
|
gboolean size_changed;
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||||
|
|
||||||
|
/* Check if we will need to invalidate size */
|
||||||
|
if ((size_changed = self->pending_resize))
|
||||||
|
self->pending_resize = FALSE;
|
||||||
|
|
||||||
|
gst_clear_buffer (&self->buffer);
|
||||||
|
self->buffer = self->pending_buffer;
|
||||||
|
self->pending_buffer = NULL;
|
||||||
|
|
||||||
|
self->draw_id = 0;
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
|
||||||
|
gst_clapper_paintable_prepare_overlays (self);
|
||||||
|
|
||||||
|
if (size_changed)
|
||||||
|
invalidate_paintable_size_internal (self);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (self, "Invalidate paintable contents");
|
||||||
|
gdk_paintable_invalidate_contents ((GdkPaintable *) self);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstClapperPaintable *
|
||||||
|
gst_clapper_paintable_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (GST_TYPE_CLAPPER_PAINTABLE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_clapper_paintable_set_widget (GstClapperPaintable *self, GtkWidget *widget)
|
||||||
|
{
|
||||||
|
g_weak_ref_set (&self->widget, widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_clapper_paintable_set_buffer (GstClapperPaintable *self, GstBuffer *buffer)
|
||||||
|
{
|
||||||
|
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||||
|
|
||||||
|
if (self->draw_id > 0) {
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
GST_TRACE ("Already have pending buffer, skipping %" GST_PTR_FORMAT, buffer);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_buffer_replace (&self->pending_buffer, buffer);
|
||||||
|
self->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
|
||||||
|
(GSourceFunc) update_paintable_on_main_cb, self, NULL);
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_clapper_paintable_set_video_info (GstClapperPaintable *self, GstVideoInfo *v_info)
|
||||||
|
{
|
||||||
|
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||||
|
|
||||||
|
if (gst_video_info_is_equal (&self->v_info, v_info)) {
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reject info if values would cause integer overflow */
|
||||||
|
if (G_UNLIKELY (!calculate_display_par (self, v_info))) {
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->pending_resize = TRUE;
|
||||||
|
self->v_info = *v_info;
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *self,
|
||||||
|
gint par_n, gint par_d)
|
||||||
|
{
|
||||||
|
gboolean success;
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||||
|
|
||||||
|
/* No change */
|
||||||
|
if (self->par_n == par_n && self->par_d == par_d) {
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->par_n = par_n;
|
||||||
|
self->par_d = par_d;
|
||||||
|
|
||||||
|
/* Check if we can accept new values. This will update
|
||||||
|
* display `ratio_num` and `ratio_den` only when successful */
|
||||||
|
success = calculate_display_par (self, &self->v_info);
|
||||||
|
|
||||||
|
/* If paintable update is queued, wait for it, otherwise invalidate
|
||||||
|
* size only for change to be applied even when paused */
|
||||||
|
if (!success || self->draw_id > 0) {
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
|
||||||
|
(GSourceFunc) invalidate_paintable_size_on_main_cb, self, NULL);
|
||||||
|
|
||||||
|
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GdkPaintableInterface
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
gst_clapper_paintable_snapshot_internal (GstClapperPaintable *self,
|
||||||
|
GdkSnapshot *snapshot, gdouble width, gdouble height,
|
||||||
|
gint widget_width, gint widget_height)
|
||||||
|
{
|
||||||
|
GstMemory *memory;
|
||||||
|
GstMapInfo info;
|
||||||
|
gfloat scale_x, scale_y;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
scale_x = (gfloat) width / self->display_width;
|
||||||
|
scale_y = (gfloat) height / self->display_height;
|
||||||
|
|
||||||
|
/* Apply black borders when keeping aspect ratio */
|
||||||
|
if (scale_x == scale_y || abs (scale_x - scale_y) <= FLT_EPSILON) {
|
||||||
|
if (widget_height - height > 0) {
|
||||||
|
gdouble bar_height = (widget_height - height) / 2;
|
||||||
|
|
||||||
|
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, -bar_height));
|
||||||
|
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, height, width, bar_height + 0.5));
|
||||||
|
} else if (widget_width - width > 0) {
|
||||||
|
gdouble bar_width = (widget_width - width) / 2;
|
||||||
|
|
||||||
|
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, -bar_width, height));
|
||||||
|
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (width, 0, bar_width + 0.5, height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buffer is accessed only from main thread, so no locking required */
|
||||||
|
if (!self->buffer) {
|
||||||
|
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_TRACE ("Snapshot %" GST_PTR_FORMAT, self->buffer);
|
||||||
|
|
||||||
|
memory = gst_buffer_peek_memory (self->buffer, 0);
|
||||||
|
|
||||||
|
/* If we cannot map, just draw black */
|
||||||
|
if (G_UNLIKELY (!gst_memory_map (memory, &info, GST_MAP_READ))) {
|
||||||
|
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||||
|
GST_WARNING_OBJECT (self, "Could not map %" GST_PTR_FORMAT, self->buffer);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_snapshot_append_texture (snapshot,
|
||||||
|
GST_CLAPPER_GDK_MEMORY_CAST (memory)->texture,
|
||||||
|
&GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||||
|
gst_memory_unmap (memory, &info);
|
||||||
|
|
||||||
|
/* FIXME: Draw black BG here when import format has-alpha */
|
||||||
|
//gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||||
|
|
||||||
|
/* Finally append prepared overlays */
|
||||||
|
for (i = 0; i < self->overlays->len; i++) {
|
||||||
|
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->overlays, i);
|
||||||
|
|
||||||
|
gtk_snapshot_append_texture (snapshot, overlay->texture,
|
||||||
|
&GRAPHENE_RECT_INIT (overlay->x * scale_x, overlay->y * scale_y,
|
||||||
|
overlay->width * scale_x, overlay->height * scale_y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_paintable_snapshot (GdkPaintable *paintable,
|
||||||
|
GdkSnapshot *snapshot, gdouble width, gdouble height)
|
||||||
|
{
|
||||||
|
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||||
|
GtkWidget *widget;
|
||||||
|
gint widget_width = 0, widget_height = 0;
|
||||||
|
|
||||||
|
if ((widget = g_weak_ref_get (&self->widget))) {
|
||||||
|
widget_width = gtk_widget_get_width (widget);
|
||||||
|
widget_height = gtk_widget_get_height (widget);
|
||||||
|
|
||||||
|
g_object_unref (widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_clapper_paintable_snapshot_internal (self, snapshot,
|
||||||
|
width, height, widget_width, widget_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GdkPaintable *
|
||||||
|
gst_clapper_paintable_get_current_image (GdkPaintable *paintable)
|
||||||
|
{
|
||||||
|
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||||
|
|
||||||
|
if (self->buffer) {
|
||||||
|
GtkSnapshot *snapshot;
|
||||||
|
GdkPaintable *ret;
|
||||||
|
|
||||||
|
snapshot = gtk_snapshot_new ();
|
||||||
|
|
||||||
|
/* Snapshot without widget size in order to get
|
||||||
|
* paintable without black borders */
|
||||||
|
gst_clapper_paintable_snapshot_internal (self, snapshot,
|
||||||
|
self->display_width, self->display_height, 0, 0);
|
||||||
|
|
||||||
|
if ((ret = gtk_snapshot_free_to_paintable (snapshot, NULL)))
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gdk_paintable_new_empty (0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
gst_clapper_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
||||||
|
{
|
||||||
|
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||||
|
|
||||||
|
return self->display_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
gst_clapper_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
||||||
|
{
|
||||||
|
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||||
|
|
||||||
|
return self->display_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gdouble
|
||||||
|
gst_clapper_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
|
||||||
|
{
|
||||||
|
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||||
|
|
||||||
|
return self->display_aspect_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_paintable_iface_init (GdkPaintableInterface *iface)
|
||||||
|
{
|
||||||
|
iface->snapshot = gst_clapper_paintable_snapshot;
|
||||||
|
iface->get_current_image = gst_clapper_paintable_get_current_image;
|
||||||
|
iface->get_intrinsic_width = gst_clapper_paintable_get_intrinsic_width;
|
||||||
|
iface->get_intrinsic_height = gst_clapper_paintable_get_intrinsic_height;
|
||||||
|
iface->get_intrinsic_aspect_ratio = gst_clapper_paintable_get_intrinsic_aspect_ratio;
|
||||||
|
}
|
74
lib/gst/plugin/gstclapperpaintable.h
vendored
Normal file
74
lib/gst/plugin/gstclapperpaintable.h
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_CLAPPER_PAINTABLE (gst_clapper_paintable_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE (GstClapperPaintable, gst_clapper_paintable, GST, CLAPPER_PAINTABLE, GObject)
|
||||||
|
|
||||||
|
#define GST_CLAPPER_PAINTABLE_CAST(obj) ((GstClapperPaintable *)(obj))
|
||||||
|
|
||||||
|
#define GST_CLAPPER_PAINTABLE_GET_LOCK(obj) (&GST_CLAPPER_PAINTABLE_CAST(obj)->lock)
|
||||||
|
#define GST_CLAPPER_PAINTABLE_LOCK(obj) g_mutex_lock (GST_CLAPPER_PAINTABLE_GET_LOCK(obj))
|
||||||
|
#define GST_CLAPPER_PAINTABLE_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_PAINTABLE_GET_LOCK(obj))
|
||||||
|
|
||||||
|
struct _GstClapperPaintable
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
GMutex lock;
|
||||||
|
|
||||||
|
GstBuffer *pending_buffer, *buffer;
|
||||||
|
GPtrArray *overlays;
|
||||||
|
GstVideoInfo v_info;
|
||||||
|
|
||||||
|
GdkRGBA bg;
|
||||||
|
|
||||||
|
GWeakRef widget;
|
||||||
|
|
||||||
|
/* Sink properties */
|
||||||
|
gint par_n, par_d;
|
||||||
|
|
||||||
|
/* Resize */
|
||||||
|
gboolean pending_resize;
|
||||||
|
guint display_ratio_num;
|
||||||
|
guint display_ratio_den;
|
||||||
|
|
||||||
|
/* GdkPaintableInterface */
|
||||||
|
gint display_width;
|
||||||
|
gint display_height;
|
||||||
|
gdouble display_aspect_ratio;
|
||||||
|
|
||||||
|
/* Pending draw signal id */
|
||||||
|
guint draw_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
GstClapperPaintable * gst_clapper_paintable_new (void);
|
||||||
|
void gst_clapper_paintable_set_widget (GstClapperPaintable *paintable, GtkWidget *widget);
|
||||||
|
void gst_clapper_paintable_set_buffer (GstClapperPaintable *paintable, GstBuffer *buffer);
|
||||||
|
gboolean gst_clapper_paintable_set_video_info (GstClapperPaintable *paintable, GstVideoInfo *v_info);
|
||||||
|
void gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *paintable, gint par_n, gint par_d);
|
||||||
|
|
||||||
|
G_END_DECLS
|
838
lib/gst/plugin/gstclappersink.c
vendored
Normal file
838
lib/gst/plugin/gstclappersink.c
vendored
Normal file
@@ -0,0 +1,838 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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 "gstclappersink.h"
|
||||||
|
#include "gstclappergdkmemory.h"
|
||||||
|
#include "gstclappergdkbufferpool.h"
|
||||||
|
#include "gstgtkutils.h"
|
||||||
|
|
||||||
|
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
||||||
|
#define DEFAULT_PAR_N 1
|
||||||
|
#define DEFAULT_PAR_D 1
|
||||||
|
#define DEFAULT_KEEP_LAST_FRAME FALSE
|
||||||
|
|
||||||
|
#define WINDOW_CSS_CLASS_NAME "clappersinkwindow"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_WIDGET,
|
||||||
|
PROP_FORCE_ASPECT_RATIO,
|
||||||
|
PROP_PIXEL_ASPECT_RATIO,
|
||||||
|
PROP_KEEP_LAST_FRAME,
|
||||||
|
PROP_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GST_CAT_DEFAULT gst_clapper_sink_debug
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||||
|
|
||||||
|
static GstStaticPadTemplate gst_clapper_sink_template =
|
||||||
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
|
GST_PAD_SINK,
|
||||||
|
GST_PAD_ALWAYS,
|
||||||
|
GST_STATIC_CAPS (
|
||||||
|
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_CLAPPER_GDK_MEMORY,
|
||||||
|
"{ " GST_CLAPPER_GDK_MEMORY_FORMATS " }")
|
||||||
|
"; "
|
||||||
|
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_CLAPPER_GDK_MEMORY ", "
|
||||||
|
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
|
||||||
|
"{ " GST_CLAPPER_GDK_MEMORY_FORMATS " }")));
|
||||||
|
|
||||||
|
static void gst_clapper_sink_navigation_interface_init (
|
||||||
|
GstNavigationInterface *iface);
|
||||||
|
|
||||||
|
#define parent_class gst_clapper_sink_parent_class
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (GstClapperSink, gst_clapper_sink, GST_TYPE_VIDEO_SINK,
|
||||||
|
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
|
||||||
|
gst_clapper_sink_navigation_interface_init));
|
||||||
|
GST_ELEMENT_REGISTER_DEFINE (clappersink, "clappersink", GST_RANK_NONE,
|
||||||
|
GST_TYPE_CLAPPER_SINK);
|
||||||
|
|
||||||
|
static void
|
||||||
|
window_clear_no_lock (GstClapperSink *self)
|
||||||
|
{
|
||||||
|
if (!self->window)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (self, "Window clear");
|
||||||
|
|
||||||
|
if (self->window_destroy_id) {
|
||||||
|
g_signal_handler_disconnect (self->window, self->window_destroy_id);
|
||||||
|
self->window_destroy_id = 0;
|
||||||
|
}
|
||||||
|
self->window = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
widget_clear_no_lock (GstClapperSink *self)
|
||||||
|
{
|
||||||
|
if (!self->widget)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (self, "Widget clear");
|
||||||
|
|
||||||
|
if (self->widget_destroy_id) {
|
||||||
|
g_signal_handler_disconnect (self->widget, self->widget_destroy_id);
|
||||||
|
self->widget_destroy_id = 0;
|
||||||
|
}
|
||||||
|
g_clear_object (&self->widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
widget_destroy_cb (GtkWidget *widget, GstClapperSink *self)
|
||||||
|
{
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
widget_clear_no_lock (self);
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
window_destroy_cb (GtkWidget *window, GstClapperSink *self)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (self, "Window destroy");
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
|
||||||
|
widget_clear_no_lock (self);
|
||||||
|
window_clear_no_lock (self);
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
calculate_stream_coords (GstClapperSink *self, GtkWidget *widget,
|
||||||
|
gdouble x, gdouble y, gdouble *stream_x, gdouble *stream_y)
|
||||||
|
{
|
||||||
|
GstVideoRectangle result;
|
||||||
|
gint scaled_width, scaled_height, scale_factor;
|
||||||
|
gint video_width, video_height;
|
||||||
|
gboolean force_aspect_ratio;
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
|
||||||
|
video_width = GST_VIDEO_INFO_WIDTH (&self->v_info);
|
||||||
|
video_height = GST_VIDEO_INFO_HEIGHT (&self->v_info);
|
||||||
|
force_aspect_ratio = self->force_aspect_ratio;
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
scale_factor = gtk_widget_get_scale_factor (widget);
|
||||||
|
scaled_width = gtk_widget_get_width (widget) * scale_factor;
|
||||||
|
scaled_height = gtk_widget_get_height (widget) * scale_factor;
|
||||||
|
|
||||||
|
if (force_aspect_ratio) {
|
||||||
|
GstVideoRectangle src, dst;
|
||||||
|
|
||||||
|
src.x = 0;
|
||||||
|
src.y = 0;
|
||||||
|
src.w = gdk_paintable_get_intrinsic_width ((GdkPaintable *) self->paintable);
|
||||||
|
src.h = gdk_paintable_get_intrinsic_height ((GdkPaintable *) self->paintable);
|
||||||
|
|
||||||
|
dst.x = 0;
|
||||||
|
dst.y = 0;
|
||||||
|
dst.w = scaled_width;
|
||||||
|
dst.h = scaled_height;
|
||||||
|
|
||||||
|
gst_video_center_rect (&src, &dst, &result, TRUE);
|
||||||
|
} else {
|
||||||
|
result.x = 0;
|
||||||
|
result.y = 0;
|
||||||
|
result.w = scaled_width;
|
||||||
|
result.h = scaled_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display coordinates to stream coordinates */
|
||||||
|
*stream_x = (result.w > 0)
|
||||||
|
? (x - result.x) / result.w * video_width
|
||||||
|
: 0;
|
||||||
|
*stream_y = (result.h > 0)
|
||||||
|
? (y - result.y) / result.h * video_height
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
/* Clip to stream size */
|
||||||
|
*stream_x = CLAMP (*stream_x, 0, video_width);
|
||||||
|
*stream_y = CLAMP (*stream_y, 0, video_height);
|
||||||
|
|
||||||
|
GST_LOG ("Transform coords %fx%f => %fx%f", x, y, *stream_x, *stream_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_widget_motion_event (GtkEventControllerMotion *motion,
|
||||||
|
gdouble x, gdouble y, GstClapperSink *self)
|
||||||
|
{
|
||||||
|
GtkWidget *widget;
|
||||||
|
gdouble stream_x, stream_y;
|
||||||
|
|
||||||
|
if ((x == self->last_pos_x && y == self->last_pos_y)
|
||||||
|
|| GST_STATE (self) < GST_STATE_PLAYING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self->last_pos_x = x;
|
||||||
|
self->last_pos_y = y;
|
||||||
|
|
||||||
|
widget = gtk_event_controller_get_widget ((GtkEventController *) motion);
|
||||||
|
calculate_stream_coords (self, widget, x, y, &stream_x, &stream_y);
|
||||||
|
GST_LOG ("Event \"mouse-move\", x: %f, y: %f", stream_x, stream_y);
|
||||||
|
|
||||||
|
gst_navigation_send_mouse_event ((GstNavigation *) self, "mouse-move",
|
||||||
|
0, stream_x, stream_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_widget_button_event (GtkGestureClick *click,
|
||||||
|
gint n_press, gdouble x, gdouble y, GstClapperSink *self)
|
||||||
|
{
|
||||||
|
GtkWidget *widget;
|
||||||
|
GdkEvent *event;
|
||||||
|
gdouble stream_x, stream_y;
|
||||||
|
GdkEventType event_type;
|
||||||
|
const gchar *event_name;
|
||||||
|
|
||||||
|
if (GST_STATE (self) < GST_STATE_PLAYING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
event = gtk_event_controller_get_current_event ((GtkEventController *) click);
|
||||||
|
event_type = gdk_event_get_event_type (event);
|
||||||
|
|
||||||
|
/* FIXME: Touchscreen handling should probably use new touch events from GStreamer 1.22 */
|
||||||
|
event_name = (event_type == GDK_BUTTON_PRESS || event_type == GDK_TOUCH_BEGIN)
|
||||||
|
? "mouse-button-press"
|
||||||
|
: (event_type == GDK_BUTTON_RELEASE || event_type == GDK_TOUCH_END)
|
||||||
|
? "mouse-button-release"
|
||||||
|
: NULL;
|
||||||
|
|
||||||
|
/* Can be NULL on touch */
|
||||||
|
if (!event_name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
widget = gtk_event_controller_get_widget ((GtkEventController *) click);
|
||||||
|
calculate_stream_coords (self, widget, x, y, &stream_x, &stream_y);
|
||||||
|
GST_LOG ("Event \"%s\", x: %f, y: %f", event_name, stream_x, stream_y);
|
||||||
|
|
||||||
|
/* Gesture is set to handle only primary button, so we do not have to check */
|
||||||
|
gst_navigation_send_mouse_event ((GstNavigation *) self, event_name,
|
||||||
|
1, stream_x, stream_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Must call from main thread only with a lock */
|
||||||
|
static GtkWidget *
|
||||||
|
gst_clapper_sink_get_widget (GstClapperSink *self)
|
||||||
|
{
|
||||||
|
if (G_UNLIKELY (!self->widget)) {
|
||||||
|
GtkEventController *controller;
|
||||||
|
GtkGesture *gesture;
|
||||||
|
|
||||||
|
/* Make sure GTK is initialized */
|
||||||
|
if (!gtk_init_check ()) {
|
||||||
|
GST_ERROR_OBJECT (self, "Could not ensure GTK initialization");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->widget = gtk_picture_new ();
|
||||||
|
|
||||||
|
/* Otherwise widget in grid will appear as a 1x1px
|
||||||
|
* video which might be misleading for users */
|
||||||
|
gtk_widget_set_hexpand (self->widget, TRUE);
|
||||||
|
gtk_widget_set_vexpand (self->widget, TRUE);
|
||||||
|
|
||||||
|
gtk_widget_set_focusable (self->widget, TRUE);
|
||||||
|
gtk_widget_set_can_focus (self->widget, TRUE);
|
||||||
|
|
||||||
|
controller = gtk_event_controller_motion_new ();
|
||||||
|
g_signal_connect (controller, "motion",
|
||||||
|
G_CALLBACK (gst_clapper_sink_widget_motion_event), self);
|
||||||
|
gtk_widget_add_controller (self->widget, controller);
|
||||||
|
|
||||||
|
gesture = gtk_gesture_click_new ();
|
||||||
|
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 1);
|
||||||
|
g_signal_connect (gesture, "pressed",
|
||||||
|
G_CALLBACK (gst_clapper_sink_widget_button_event), self);
|
||||||
|
g_signal_connect (gesture, "released",
|
||||||
|
G_CALLBACK (gst_clapper_sink_widget_button_event), self);
|
||||||
|
gtk_widget_add_controller (self->widget, GTK_EVENT_CONTROLLER (gesture));
|
||||||
|
|
||||||
|
/* TODO: Implement touch events once we depend on GStreamer 1.22 */
|
||||||
|
|
||||||
|
/* Take floating ref */
|
||||||
|
g_object_ref_sink (self->widget);
|
||||||
|
|
||||||
|
/* Set back pointer */
|
||||||
|
gst_clapper_paintable_set_widget (self->paintable, self->widget);
|
||||||
|
|
||||||
|
/* Set earlier remembered property */
|
||||||
|
gtk_picture_set_keep_aspect_ratio (GTK_PICTURE (self->widget),
|
||||||
|
self->force_aspect_ratio);
|
||||||
|
|
||||||
|
gtk_picture_set_paintable (GTK_PICTURE (self->widget), GDK_PAINTABLE (self->paintable));
|
||||||
|
|
||||||
|
self->widget_destroy_id = g_signal_connect (self->widget,
|
||||||
|
"destroy", G_CALLBACK (widget_destroy_cb), self);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self->widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GtkWidget *
|
||||||
|
gst_clapper_sink_obtain_widget (GstClapperSink *self)
|
||||||
|
{
|
||||||
|
GtkWidget *widget;
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
widget = gst_clapper_sink_get_widget (self);
|
||||||
|
if (widget)
|
||||||
|
g_object_ref (widget);
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_get_property (GObject *object, guint prop_id,
|
||||||
|
GValue *value, GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (object);
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_WIDGET:
|
||||||
|
if (self->widget) {
|
||||||
|
g_value_set_object (value, self->widget);
|
||||||
|
} else {
|
||||||
|
GtkWidget *widget;
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
widget = gst_gtk_invoke_on_main ((GThreadFunc) gst_clapper_sink_obtain_widget, self);
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
|
||||||
|
g_value_set_object (value, widget);
|
||||||
|
g_object_unref (widget);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROP_FORCE_ASPECT_RATIO:
|
||||||
|
g_value_set_boolean (value, self->force_aspect_ratio);
|
||||||
|
break;
|
||||||
|
case PROP_PIXEL_ASPECT_RATIO:
|
||||||
|
gst_value_set_fraction (value, self->par_n, self->par_d);
|
||||||
|
break;
|
||||||
|
case PROP_KEEP_LAST_FRAME:
|
||||||
|
g_value_set_boolean (value, self->keep_last_frame);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_set_property (GObject *object, guint prop_id,
|
||||||
|
const GValue *value, GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (object);
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_FORCE_ASPECT_RATIO:
|
||||||
|
self->force_aspect_ratio = g_value_get_boolean (value);
|
||||||
|
|
||||||
|
if (self->widget) {
|
||||||
|
gtk_picture_set_keep_aspect_ratio (GTK_PICTURE (self->widget),
|
||||||
|
self->force_aspect_ratio);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROP_PIXEL_ASPECT_RATIO:
|
||||||
|
self->par_n = gst_value_get_fraction_numerator (value);
|
||||||
|
self->par_d = gst_value_get_fraction_denominator (value);
|
||||||
|
|
||||||
|
gst_clapper_paintable_set_pixel_aspect_ratio (self->paintable,
|
||||||
|
self->par_n, self->par_d);
|
||||||
|
break;
|
||||||
|
case PROP_KEEP_LAST_FRAME:
|
||||||
|
self->keep_last_frame = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_navigation_send_event (GstNavigation *navigation,
|
||||||
|
GstStructure *structure)
|
||||||
|
{
|
||||||
|
GstClapperSink *sink = GST_CLAPPER_SINK_CAST (navigation);
|
||||||
|
GstEvent *event;
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (sink, "Navigation event: %" GST_PTR_FORMAT, structure);
|
||||||
|
event = gst_event_new_navigation (structure);
|
||||||
|
|
||||||
|
if (G_LIKELY (event)) {
|
||||||
|
GstPad *pad;
|
||||||
|
|
||||||
|
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
|
||||||
|
|
||||||
|
if (G_LIKELY (pad)) {
|
||||||
|
if (!gst_pad_send_event (pad, gst_event_ref (event))) {
|
||||||
|
/* If upstream didn't handle the event we'll post a message with it
|
||||||
|
* for the application in case it wants to do something with it */
|
||||||
|
gst_element_post_message (GST_ELEMENT_CAST (sink),
|
||||||
|
gst_navigation_message_new_event (GST_OBJECT_CAST (sink), event));
|
||||||
|
}
|
||||||
|
gst_object_unref (pad);
|
||||||
|
}
|
||||||
|
gst_event_unref (event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_sink_propose_allocation (GstBaseSink *bsink, GstQuery *query)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||||
|
GstBufferPool *pool = NULL;
|
||||||
|
GstCaps *caps;
|
||||||
|
GstVideoInfo info;
|
||||||
|
guint size;
|
||||||
|
gboolean need_pool;
|
||||||
|
|
||||||
|
gst_query_parse_allocation (query, &caps, &need_pool);
|
||||||
|
|
||||||
|
if (!caps) {
|
||||||
|
GST_DEBUG_OBJECT (self, "No caps specified");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gst_video_info_from_caps (&info, caps)) {
|
||||||
|
GST_DEBUG_OBJECT (self, "Invalid caps specified");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Normal size of a frame */
|
||||||
|
size = GST_VIDEO_INFO_SIZE (&info);
|
||||||
|
|
||||||
|
if (need_pool) {
|
||||||
|
GstStructure *config;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Creating new pool");
|
||||||
|
|
||||||
|
pool = gst_clapper_gdk_buffer_pool_new ();
|
||||||
|
config = gst_buffer_pool_get_config (pool);
|
||||||
|
|
||||||
|
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||||
|
gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
|
||||||
|
|
||||||
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||||
|
gst_object_unref (pool);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Failed to set config");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_query_add_allocation_pool (query, pool, size, 2, 0);
|
||||||
|
if (pool)
|
||||||
|
gst_object_unref (pool);
|
||||||
|
|
||||||
|
gst_query_add_allocation_meta (query,
|
||||||
|
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
|
||||||
|
|
||||||
|
/* We also support various metadata */
|
||||||
|
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_sink_start_on_main (GstClapperSink *self)
|
||||||
|
{
|
||||||
|
GtkWidget *widget;
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
|
||||||
|
/* Make sure widget is created */
|
||||||
|
if (!(widget = gst_clapper_sink_get_widget (self))) {
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When no toplevel window, make our own */
|
||||||
|
if (G_UNLIKELY (!gtk_widget_get_root (widget) && !self->window)) {
|
||||||
|
GtkWidget *toplevel, *parent;
|
||||||
|
GtkCssProvider *provider;
|
||||||
|
gchar *win_title;
|
||||||
|
|
||||||
|
if ((parent = gtk_widget_get_parent (widget))) {
|
||||||
|
GtkWidget *temp_parent;
|
||||||
|
|
||||||
|
while ((temp_parent = gtk_widget_get_parent (parent)))
|
||||||
|
parent = temp_parent;
|
||||||
|
}
|
||||||
|
toplevel = (parent) ? parent : widget;
|
||||||
|
|
||||||
|
self->window = (GtkWindow *) gtk_window_new ();
|
||||||
|
gtk_widget_add_css_class (GTK_WIDGET (self->window), WINDOW_CSS_CLASS_NAME);
|
||||||
|
|
||||||
|
provider = gtk_css_provider_new ();
|
||||||
|
gtk_css_provider_load_from_data (provider,
|
||||||
|
"." WINDOW_CSS_CLASS_NAME " { background: none; }", -1);
|
||||||
|
gtk_style_context_add_provider_for_display (
|
||||||
|
gdk_display_get_default (), GTK_STYLE_PROVIDER (provider),
|
||||||
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||||
|
g_object_unref (provider);
|
||||||
|
|
||||||
|
win_title = g_strdup_printf ("Clapper Sink - GTK %u.%u.%u Window",
|
||||||
|
gtk_get_major_version (),
|
||||||
|
gtk_get_minor_version (),
|
||||||
|
gtk_get_micro_version ());
|
||||||
|
|
||||||
|
gtk_window_set_default_size (self->window, 640, 480);
|
||||||
|
gtk_window_set_title (self->window, win_title);
|
||||||
|
gtk_window_set_child (self->window, toplevel);
|
||||||
|
|
||||||
|
g_free (win_title);
|
||||||
|
|
||||||
|
self->window_destroy_id = g_signal_connect (self->window,
|
||||||
|
"destroy", G_CALLBACK (window_destroy_cb), self);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
window_present_on_main_idle (GtkWindow *window)
|
||||||
|
{
|
||||||
|
GST_INFO ("Presenting window");
|
||||||
|
gtk_window_present (window);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_sink_start (GstBaseSink *bsink)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (self, "Start");
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!(! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||||
|
gst_clapper_sink_start_on_main, self)))) {
|
||||||
|
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||||
|
("GtkWidget could not be created"), (NULL));
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_sink_stop_on_main (GstClapperSink *self)
|
||||||
|
{
|
||||||
|
GtkWindow *window = NULL;
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
if (self->window)
|
||||||
|
window = g_object_ref (self->window);
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
if (window) {
|
||||||
|
gtk_window_destroy (window);
|
||||||
|
g_object_unref (window);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_sink_stop (GstBaseSink *bsink)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||||
|
gboolean has_window;
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (self, "Stop");
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
has_window = (self->window != NULL);
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (has_window)) {
|
||||||
|
return (! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||||
|
gst_clapper_sink_stop_on_main, self));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_clapper_sink_change_state (GstElement *element, GstStateChange transition)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (element);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Changing state: %s => %s",
|
||||||
|
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
|
||||||
|
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
if (!self->keep_last_frame)
|
||||||
|
gst_clapper_paintable_set_buffer (self->paintable, NULL);
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
break;
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
if (G_UNLIKELY (self->window && !self->presented_window)) {
|
||||||
|
g_idle_add_full (G_PRIORITY_DEFAULT,
|
||||||
|
(GSourceFunc) window_present_on_main_idle,
|
||||||
|
g_object_ref (self->window), (GDestroyNotify) g_object_unref);
|
||||||
|
self->presented_window = TRUE;
|
||||||
|
}
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_get_times (GstBaseSink *bsink, GstBuffer *buffer,
|
||||||
|
GstClockTime *start, GstClockTime *end)
|
||||||
|
{
|
||||||
|
if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
|
||||||
|
return;
|
||||||
|
|
||||||
|
*start = GST_BUFFER_TIMESTAMP (buffer);
|
||||||
|
|
||||||
|
if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
|
||||||
|
*end = *start + GST_BUFFER_DURATION (buffer);
|
||||||
|
} else {
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||||
|
gint fps_n, fps_d;
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
fps_n = GST_VIDEO_INFO_FPS_N (&self->v_info);
|
||||||
|
fps_d = GST_VIDEO_INFO_FPS_D (&self->v_info);
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
if (fps_n > 0)
|
||||||
|
*end = *start + gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_clapper_sink_get_caps (GstBaseSink *bsink, GstCaps *filter)
|
||||||
|
{
|
||||||
|
GstCaps *result, *tmp;
|
||||||
|
|
||||||
|
tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
GST_DEBUG ("Intersecting with filter caps: %" GST_PTR_FORMAT, filter);
|
||||||
|
result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
|
||||||
|
gst_caps_unref (tmp);
|
||||||
|
} else {
|
||||||
|
result = tmp;
|
||||||
|
}
|
||||||
|
GST_DEBUG ("Returning caps: %" GST_PTR_FORMAT, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_sink_set_caps (GstBaseSink *bsink, GstCaps *caps)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||||
|
gboolean res;
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (self, "Set caps: %" GST_PTR_FORMAT, caps);
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
|
||||||
|
if (!gst_video_info_from_caps (&self->v_info, caps)) {
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!self->widget)) {
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||||
|
("Output widget was destroyed"), (NULL));
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = gst_clapper_paintable_set_video_info (self->paintable, &self->v_info);
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_clapper_sink_show_frame (GstVideoSink *vsink, GstBuffer *buffer)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (vsink);
|
||||||
|
|
||||||
|
GST_TRACE ("Got %" GST_PTR_FORMAT, buffer);
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!self->widget)) {
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||||
|
("Output widget was destroyed"), (NULL));
|
||||||
|
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_clapper_paintable_set_buffer (self->paintable, buffer);
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_init (GstClapperSink *self)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class;
|
||||||
|
|
||||||
|
gobject_class = (GObjectClass *) GST_CLAPPER_SINK_GET_CLASS (self);
|
||||||
|
|
||||||
|
/* HACK: install here instead of class init to avoid GStreamer
|
||||||
|
* plugin scanner GObject type conflicts with older GTK versions */
|
||||||
|
if (!g_object_class_find_property (gobject_class, "widget")) {
|
||||||
|
g_object_class_install_property (gobject_class, PROP_WIDGET,
|
||||||
|
g_param_spec_object ("widget", "GTK Widget",
|
||||||
|
"The GtkWidget to place in the widget hierarchy",
|
||||||
|
GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||||
|
}
|
||||||
|
|
||||||
|
self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
||||||
|
self->par_n = DEFAULT_PAR_N;
|
||||||
|
self->par_d = DEFAULT_PAR_D;
|
||||||
|
self->keep_last_frame = DEFAULT_KEEP_LAST_FRAME;
|
||||||
|
|
||||||
|
g_mutex_init (&self->lock);
|
||||||
|
gst_video_info_init (&self->v_info);
|
||||||
|
|
||||||
|
self->paintable = gst_clapper_paintable_new ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (object);
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_LOCK (self);
|
||||||
|
|
||||||
|
window_clear_no_lock (self);
|
||||||
|
widget_clear_no_lock (self);
|
||||||
|
|
||||||
|
g_clear_object (&self->paintable);
|
||||||
|
|
||||||
|
GST_CLAPPER_SINK_UNLOCK (self);
|
||||||
|
|
||||||
|
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GstClapperSink *self = GST_CLAPPER_SINK_CAST (object);
|
||||||
|
|
||||||
|
GST_TRACE ("Finalize");
|
||||||
|
|
||||||
|
g_mutex_clear (&self->lock);
|
||||||
|
|
||||||
|
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_class_init (GstClapperSinkClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||||
|
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||||
|
GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass;
|
||||||
|
GstVideoSinkClass *gstvideosink_class = (GstVideoSinkClass *) klass;
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappersink", 0,
|
||||||
|
"Clapper Sink");
|
||||||
|
|
||||||
|
gobject_class->get_property = gst_clapper_sink_get_property;
|
||||||
|
gobject_class->set_property = gst_clapper_sink_set_property;
|
||||||
|
gobject_class->dispose = gst_clapper_sink_dispose;
|
||||||
|
gobject_class->finalize = gst_clapper_sink_finalize;
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
|
||||||
|
g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
|
||||||
|
"When enabled, scaling will respect original aspect ratio",
|
||||||
|
DEFAULT_FORCE_ASPECT_RATIO,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
|
||||||
|
gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
|
||||||
|
"The pixel aspect ratio of the device",
|
||||||
|
DEFAULT_PAR_N, DEFAULT_PAR_D,
|
||||||
|
G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, PROP_KEEP_LAST_FRAME,
|
||||||
|
g_param_spec_boolean ("keep-last-frame", "Keep last frame",
|
||||||
|
"Keep showing last video frame after playback instead of black screen",
|
||||||
|
DEFAULT_KEEP_LAST_FRAME,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
gstelement_class->change_state = gst_clapper_sink_change_state;
|
||||||
|
|
||||||
|
gstbasesink_class->get_caps = gst_clapper_sink_get_caps;
|
||||||
|
gstbasesink_class->set_caps = gst_clapper_sink_set_caps;
|
||||||
|
gstbasesink_class->get_times = gst_clapper_sink_get_times;
|
||||||
|
gstbasesink_class->propose_allocation = gst_clapper_sink_propose_allocation;
|
||||||
|
gstbasesink_class->start = gst_clapper_sink_start;
|
||||||
|
gstbasesink_class->stop = gst_clapper_sink_stop;
|
||||||
|
|
||||||
|
gstvideosink_class->show_frame = gst_clapper_sink_show_frame;
|
||||||
|
|
||||||
|
gst_element_class_set_metadata (gstelement_class,
|
||||||
|
"Clapper video sink",
|
||||||
|
"Sink/Video", "A GTK4 video sink used by Clapper media player",
|
||||||
|
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
||||||
|
|
||||||
|
gst_element_class_add_static_pad_template (gstelement_class,
|
||||||
|
&gst_clapper_sink_template);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GstNavigationInterface
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
gst_clapper_sink_navigation_interface_init (GstNavigationInterface *iface)
|
||||||
|
{
|
||||||
|
/* TODO: Port to "send_event_simple" once we depend on GStreamer 1.22 */
|
||||||
|
iface->send_event = gst_clapper_sink_navigation_send_event;
|
||||||
|
}
|
70
lib/gst/plugin/gstclappersink.h
vendored
Normal file
70
lib/gst/plugin/gstclappersink.h
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/video/gstvideosink.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
#include "gstclapperpaintable.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_CLAPPER_SINK (gst_clapper_sink_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE (GstClapperSink, gst_clapper_sink, GST, CLAPPER_SINK, GstVideoSink)
|
||||||
|
|
||||||
|
#define GST_CLAPPER_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_SINK, GstClapperSinkClass))
|
||||||
|
#define GST_CLAPPER_SINK_CAST(obj) ((GstClapperSink *)(obj))
|
||||||
|
|
||||||
|
#define GST_CLAPPER_SINK_GET_LOCK(obj) (&GST_CLAPPER_SINK_CAST(obj)->lock)
|
||||||
|
#define GST_CLAPPER_SINK_LOCK(obj) g_mutex_lock (GST_CLAPPER_SINK_GET_LOCK(obj))
|
||||||
|
#define GST_CLAPPER_SINK_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_SINK_GET_LOCK(obj))
|
||||||
|
|
||||||
|
struct _GstClapperSink
|
||||||
|
{
|
||||||
|
GstVideoSink parent;
|
||||||
|
|
||||||
|
GMutex lock;
|
||||||
|
|
||||||
|
GstClapperPaintable *paintable;
|
||||||
|
GstVideoInfo v_info;
|
||||||
|
|
||||||
|
GtkWidget *widget;
|
||||||
|
GtkWindow *window;
|
||||||
|
|
||||||
|
gboolean presented_window;
|
||||||
|
|
||||||
|
/* Properties */
|
||||||
|
gboolean force_aspect_ratio;
|
||||||
|
gint par_n, par_d;
|
||||||
|
gboolean keep_last_frame;
|
||||||
|
|
||||||
|
/* Position coords */
|
||||||
|
gdouble last_pos_x;
|
||||||
|
gdouble last_pos_y;
|
||||||
|
|
||||||
|
gulong widget_destroy_id;
|
||||||
|
gulong window_destroy_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
GST_ELEMENT_REGISTER_DECLARE (clappersink);
|
||||||
|
|
||||||
|
G_END_DECLS
|
131
lib/gst/plugin/gstgtkutils.c
vendored
Normal file
131
lib/gst/plugin/gstgtkutils.c
vendored
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
||||||
|
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
||||||
|
* Copyright (C) 2022 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 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gstgtkutils.h"
|
||||||
|
|
||||||
|
struct invoke_context
|
||||||
|
{
|
||||||
|
GThreadFunc func;
|
||||||
|
gpointer data;
|
||||||
|
GMutex lock;
|
||||||
|
GCond cond;
|
||||||
|
gboolean fired;
|
||||||
|
|
||||||
|
gpointer res;
|
||||||
|
};
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_gtk_invoke_func (struct invoke_context *info)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&info->lock);
|
||||||
|
info->res = info->func (info->data);
|
||||||
|
info->fired = TRUE;
|
||||||
|
g_cond_signal (&info->cond);
|
||||||
|
g_mutex_unlock (&info->lock);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpointer
|
||||||
|
gst_gtk_invoke_on_main (GThreadFunc func, gpointer data)
|
||||||
|
{
|
||||||
|
GMainContext *main_context = g_main_context_default ();
|
||||||
|
struct invoke_context info;
|
||||||
|
|
||||||
|
g_mutex_init (&info.lock);
|
||||||
|
g_cond_init (&info.cond);
|
||||||
|
info.fired = FALSE;
|
||||||
|
info.func = func;
|
||||||
|
info.data = data;
|
||||||
|
|
||||||
|
g_main_context_invoke (main_context, (GSourceFunc) gst_gtk_invoke_func,
|
||||||
|
&info);
|
||||||
|
|
||||||
|
g_mutex_lock (&info.lock);
|
||||||
|
while (!info.fired)
|
||||||
|
g_cond_wait (&info.cond, &info.lock);
|
||||||
|
g_mutex_unlock (&info.lock);
|
||||||
|
|
||||||
|
g_mutex_clear (&info.lock);
|
||||||
|
g_cond_clear (&info.cond);
|
||||||
|
|
||||||
|
return info.res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For use with `GdkMemoryTexture` only! */
|
||||||
|
GdkMemoryFormat
|
||||||
|
gst_video_format_to_gdk_memory_format (GstVideoFormat format)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case GST_VIDEO_FORMAT_RGBA64_LE:
|
||||||
|
case GST_VIDEO_FORMAT_RGBA64_BE:
|
||||||
|
return GDK_MEMORY_R16G16B16A16_PREMULTIPLIED;
|
||||||
|
case GST_VIDEO_FORMAT_RGBA:
|
||||||
|
return GDK_MEMORY_R8G8B8A8;
|
||||||
|
case GST_VIDEO_FORMAT_BGRA:
|
||||||
|
return GDK_MEMORY_B8G8R8A8;
|
||||||
|
case GST_VIDEO_FORMAT_ARGB:
|
||||||
|
return GDK_MEMORY_A8R8G8B8;
|
||||||
|
case GST_VIDEO_FORMAT_ABGR:
|
||||||
|
return GDK_MEMORY_A8B8G8R8;
|
||||||
|
case GST_VIDEO_FORMAT_RGBx:
|
||||||
|
return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
|
||||||
|
case GST_VIDEO_FORMAT_BGRx:
|
||||||
|
return GDK_MEMORY_B8G8R8A8_PREMULTIPLIED;
|
||||||
|
case GST_VIDEO_FORMAT_RGB:
|
||||||
|
return GDK_MEMORY_R8G8B8;
|
||||||
|
case GST_VIDEO_FORMAT_BGR:
|
||||||
|
return GDK_MEMORY_B8G8R8;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This should never happen as long as above switch statement
|
||||||
|
* is updated when new formats are added to caps */
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
/* Fallback format */
|
||||||
|
return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
GdkTexture *
|
||||||
|
gst_video_frame_into_gdk_texture (GstVideoFrame *frame, GDestroyNotify free_func)
|
||||||
|
{
|
||||||
|
GdkTexture *texture;
|
||||||
|
GBytes *bytes;
|
||||||
|
|
||||||
|
bytes = g_bytes_new_with_free_func (
|
||||||
|
GST_VIDEO_FRAME_PLANE_DATA (frame, 0),
|
||||||
|
GST_VIDEO_FRAME_HEIGHT (frame) * GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0),
|
||||||
|
free_func, frame);
|
||||||
|
|
||||||
|
texture = gdk_memory_texture_new (
|
||||||
|
GST_VIDEO_FRAME_WIDTH (frame),
|
||||||
|
GST_VIDEO_FRAME_HEIGHT (frame),
|
||||||
|
gst_video_format_to_gdk_memory_format (GST_VIDEO_FRAME_FORMAT (frame)),
|
||||||
|
bytes,
|
||||||
|
GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0));
|
||||||
|
|
||||||
|
g_bytes_unref (bytes);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
33
lib/gst/plugin/gstgtkutils.h
vendored
Normal file
33
lib/gst/plugin/gstgtkutils.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
||||||
|
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
||||||
|
* Copyright (C) 2022 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 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data);
|
||||||
|
|
||||||
|
GdkMemoryFormat gst_video_format_to_gdk_memory_format (GstVideoFormat format);
|
||||||
|
|
||||||
|
GdkTexture * gst_video_frame_into_gdk_texture (GstVideoFrame *frame, GDestroyNotify free_func);
|
44
lib/gst/plugin/gstplugin.c
vendored
Normal file
44
lib/gst/plugin/gstplugin.c
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 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 "gstclapperimport.h"
|
||||||
|
#include "gstclappersink.h"
|
||||||
|
#include "gstclappergdkmemory.h"
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
plugin_init (GstPlugin *plugin)
|
||||||
|
{
|
||||||
|
gboolean res = FALSE;
|
||||||
|
|
||||||
|
res |= GST_ELEMENT_REGISTER (clapperimport, plugin);
|
||||||
|
res |= GST_ELEMENT_REGISTER (clappersink, plugin);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
gst_clapper_gdk_memory_init_once ();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
|
||||||
|
clapper, "Clapper elements", plugin_init, VERSION, "LGPL",
|
||||||
|
GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
67
lib/gst/plugin/meson.build
vendored
Normal file
67
lib/gst/plugin/meson.build
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
gst_plugins_libdir = join_paths(prefix, libdir, 'gstreamer-1.0')
|
||||||
|
|
||||||
|
gst_clapper_plugin_args = [
|
||||||
|
'-DHAVE_CONFIG_H',
|
||||||
|
'-DGST_USE_UNSTABLE_API',
|
||||||
|
]
|
||||||
|
gst_clapper_plugin_deps = [
|
||||||
|
gtk4_dep,
|
||||||
|
gst_dep,
|
||||||
|
gstbase_dep,
|
||||||
|
gstvideo_dep,
|
||||||
|
gstallocators_dep,
|
||||||
|
]
|
||||||
|
|
||||||
|
if get_option('default_library') == 'static'
|
||||||
|
gst_clapper_plugin_args += ['-DGST_STATIC_COMPILATION']
|
||||||
|
endif
|
||||||
|
|
||||||
|
gst_clapper_plugin_option = get_option('gst-plugin')
|
||||||
|
if gst_clapper_plugin_option.disabled()
|
||||||
|
subdir_done()
|
||||||
|
endif
|
||||||
|
|
||||||
|
foreach dep : gst_clapper_plugin_deps
|
||||||
|
if not dep.found()
|
||||||
|
if gst_clapper_plugin_option.enabled()
|
||||||
|
error('GStreamer plugin was enabled, but required dependencies were not found')
|
||||||
|
endif
|
||||||
|
subdir_done()
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
if not have_gtk_gl_windowing
|
||||||
|
if gst_clapper_plugin_option.enabled()
|
||||||
|
error('GTK4 widget requires GL windowing')
|
||||||
|
else
|
||||||
|
subdir_done()
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if not gtk4_dep.version().version_compare('>=4.6.0')
|
||||||
|
if gst_clapper_plugin_option.enabled()
|
||||||
|
error('GTK4 version on this system is too old, plugin needs 4.6.0+')
|
||||||
|
else
|
||||||
|
subdir_done()
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
gst_clapper_plugin_sources = [
|
||||||
|
'gstclappergdkmemory.c',
|
||||||
|
'gstclappergdkbufferpool.c',
|
||||||
|
'gstclapperbaseimport.c',
|
||||||
|
'gstclapperimport.c',
|
||||||
|
'gstclappersink.c',
|
||||||
|
'gstclapperpaintable.c',
|
||||||
|
'gstgtkutils.c',
|
||||||
|
'gstplugin.c',
|
||||||
|
]
|
||||||
|
|
||||||
|
library('gstclapper',
|
||||||
|
gst_clapper_plugin_sources,
|
||||||
|
c_args: gst_clapper_plugin_args,
|
||||||
|
include_directories: configinc,
|
||||||
|
dependencies: gst_clapper_plugin_deps + gtk_deps,
|
||||||
|
install: true,
|
||||||
|
install_dir: gst_plugins_libdir,
|
||||||
|
)
|
48
lib/meson.build
vendored
48
lib/meson.build
vendored
@@ -1,5 +1,5 @@
|
|||||||
glib_req = '>= 2.56.0'
|
glib_req = '>= 2.56.0'
|
||||||
gst_req = '>= 1.18.0'
|
gst_req = '>= 1.20.0'
|
||||||
|
|
||||||
api_version = '1.0'
|
api_version = '1.0'
|
||||||
libversion = meson.project_version()
|
libversion = meson.project_version()
|
||||||
@@ -132,7 +132,7 @@ cdata.set('SIZEOF_SHORT', cc.sizeof('short'))
|
|||||||
cdata.set('SIZEOF_VOIDP', cc.sizeof('void*'))
|
cdata.set('SIZEOF_VOIDP', cc.sizeof('void*'))
|
||||||
|
|
||||||
cdata.set_quoted('VERSION', libversion)
|
cdata.set_quoted('VERSION', libversion)
|
||||||
cdata.set_quoted('PACKAGE', 'gst-plugins-clapper')
|
cdata.set_quoted('PACKAGE', 'clapper')
|
||||||
cdata.set_quoted('PACKAGE_VERSION', libversion)
|
cdata.set_quoted('PACKAGE_VERSION', libversion)
|
||||||
cdata.set_quoted('PACKAGE_BUGREPORT', 'https://github.com/Rafostar/clapper/issues/new')
|
cdata.set_quoted('PACKAGE_BUGREPORT', 'https://github.com/Rafostar/clapper/issues/new')
|
||||||
cdata.set_quoted('PACKAGE_NAME', 'GStreamer Clapper Libs')
|
cdata.set_quoted('PACKAGE_NAME', 'GStreamer Clapper Libs')
|
||||||
@@ -184,7 +184,7 @@ foreach extra_arg : warning_flags
|
|||||||
endif
|
endif
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
cdata.set_quoted('GST_PACKAGE_NAME', 'GStreamer Plugins Clapper')
|
cdata.set_quoted('GST_PACKAGE_NAME', 'gst-plugin-clapper')
|
||||||
cdata.set_quoted('GST_PACKAGE_ORIGIN', 'https://github.com/Rafostar/clapper')
|
cdata.set_quoted('GST_PACKAGE_ORIGIN', 'https://github.com/Rafostar/clapper')
|
||||||
|
|
||||||
# Mandatory GST deps
|
# Mandatory GST deps
|
||||||
@@ -200,6 +200,8 @@ gsttag_dep = dependency('gstreamer-tag-1.0', version: gst_req,
|
|||||||
fallback: ['gst-plugins-base', 'tag_dep'])
|
fallback: ['gst-plugins-base', 'tag_dep'])
|
||||||
gstvideo_dep = dependency('gstreamer-video-1.0', version: gst_req,
|
gstvideo_dep = dependency('gstreamer-video-1.0', version: gst_req,
|
||||||
fallback: ['gst-plugins-base', 'video_dep'])
|
fallback: ['gst-plugins-base', 'video_dep'])
|
||||||
|
gstallocators_dep = dependency('gstreamer-allocators-1.0', version: gst_req,
|
||||||
|
fallback : ['gst-plugins-base', 'allocators_dep'])
|
||||||
|
|
||||||
# GStreamer OpenGL
|
# GStreamer OpenGL
|
||||||
gstgl_dep = dependency('gstreamer-gl-1.0', version: gst_req,
|
gstgl_dep = dependency('gstreamer-gl-1.0', version: gst_req,
|
||||||
@@ -251,21 +253,49 @@ giounix_dep = dependency('gio-unix-2.0', version: glib_req, fallback: ['glib', '
|
|||||||
|
|
||||||
cdata.set('DISABLE_ORC', 1)
|
cdata.set('DISABLE_ORC', 1)
|
||||||
cdata.set('GST_ENABLE_EXTRA_CHECKS', get_option('devel-checks'))
|
cdata.set('GST_ENABLE_EXTRA_CHECKS', get_option('devel-checks'))
|
||||||
cdata.set_quoted('GST_PACKAGE_RELEASE_DATETIME', 'Unknown')
|
|
||||||
|
|
||||||
configinc = include_directories('.')
|
configinc = include_directories('.')
|
||||||
libsinc = include_directories('gst')
|
libsinc = include_directories('gst')
|
||||||
|
|
||||||
gir = find_program('g-ir-scanner', required: true)
|
gir = find_program('g-ir-scanner')
|
||||||
if not gir.found()
|
|
||||||
error('Clapper requires GI bindings to be compiled')
|
|
||||||
endif
|
|
||||||
|
|
||||||
gir_init_section = ['--add-init-section=extern void gst_init(gint*,gchar**);' + \
|
gir_init_section = ['--add-init-section=extern void gst_init(gint*,gchar**);' + \
|
||||||
'g_setenv("GST_REGISTRY_1.0", "@0@", TRUE);'.format(meson.current_build_dir() + '/gir_empty_registry.reg') + \
|
'g_setenv("GST_REGISTRY_1.0", "@0@", TRUE);'.format(meson.current_build_dir() + '/gir_empty_registry.reg') + \
|
||||||
'g_setenv("GST_PLUGIN_PATH_1_0", "", TRUE);' + \
|
'g_setenv("GST_PLUGIN_PATH_1_0", "", TRUE);' + \
|
||||||
'g_setenv("GST_PLUGIN_SYSTEM_PATH_1_0", "", TRUE);' + \
|
'g_setenv("GST_PLUGIN_SYSTEM_PATH_1_0", "", TRUE);' + \
|
||||||
'gst_init(NULL,NULL);', '--quiet'
|
'gst_init(NULL,NULL);', '--quiet'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
gtk_deps = [gstgl_dep, gstglproto_dep]
|
||||||
|
have_gtk_gl_windowing = false
|
||||||
|
|
||||||
|
gtk4_dep = dependency('gtk4', required: true)
|
||||||
|
|
||||||
|
if not gtk4_dep.version().version_compare('>=4.0.0')
|
||||||
|
error('GTK4 version on this system is too old')
|
||||||
|
endif
|
||||||
|
|
||||||
|
if gst_gl_have_window_x11 and (gst_gl_have_platform_egl or gst_gl_have_platform_glx)
|
||||||
|
gtk_x11_dep = dependency('gtk4-x11', required: false)
|
||||||
|
if gtk_x11_dep.found()
|
||||||
|
gtk_deps += gtk_x11_dep
|
||||||
|
if gst_gl_have_platform_glx
|
||||||
|
gtk_deps += gstglx11_dep
|
||||||
|
endif
|
||||||
|
have_gtk_gl_windowing = true
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if gst_gl_have_window_wayland and gst_gl_have_platform_egl
|
||||||
|
gtk_wayland_dep = dependency('gtk4-wayland', required: false)
|
||||||
|
if gtk_wayland_dep.found()
|
||||||
|
gtk_deps += [gtk_wayland_dep, gstglwayland_dep]
|
||||||
|
have_gtk_gl_windowing = true
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if gst_gl_have_platform_egl
|
||||||
|
gtk_deps += gstglegl_dep
|
||||||
|
endif
|
||||||
|
|
||||||
subdir('gst')
|
subdir('gst')
|
||||||
configure_file(output: 'config.h', configuration: cdata)
|
configure_file(output: 'config.h', configuration: cdata)
|
||||||
|
@@ -19,7 +19,7 @@ datadir = join_paths(get_option('prefix'), get_option('datadir'))
|
|||||||
pkglibdir = join_paths(libdir, meson.project_name())
|
pkglibdir = join_paths(libdir, meson.project_name())
|
||||||
pkgdatadir = join_paths(datadir, meson.project_name())
|
pkgdatadir = join_paths(datadir, meson.project_name())
|
||||||
|
|
||||||
if get_option('lib')
|
if get_option('lib') or not get_option('gst-plugin').disabled()
|
||||||
subdir('lib')
|
subdir('lib')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@@ -8,6 +8,11 @@ option('lib',
|
|||||||
value: true,
|
value: true,
|
||||||
description: 'Build GstClapper lib'
|
description: 'Build GstClapper lib'
|
||||||
)
|
)
|
||||||
|
option('gst-plugin',
|
||||||
|
type: 'feature',
|
||||||
|
value: 'enabled',
|
||||||
|
description: 'Build GStreamer plugin (includes GTK video sink element)'
|
||||||
|
)
|
||||||
option('devel-checks',
|
option('devel-checks',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
value: false,
|
value: false,
|
||||||
|
Reference in New Issue
Block a user