mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-29 23:32:04 +02:00
Compare commits
4 Commits
gluploader
...
dmabufimpo
Author | SHA1 | Date | |
---|---|---|---|
|
232a00aa59 | ||
|
23b57cd326 | ||
|
9a19e10542 | ||
|
0061e133f9 |
1
.github/workflows/flatpak-nightly.yml
vendored
1
.github/workflows/flatpak-nightly.yml
vendored
@@ -7,7 +7,6 @@ jobs:
|
||||
flatpak:
|
||||
name: "Flatpak"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 600
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:gnome-nightly
|
||||
options: --privileged
|
||||
|
1
.github/workflows/flatpak.yml
vendored
1
.github/workflows/flatpak.yml
vendored
@@ -11,7 +11,6 @@ jobs:
|
||||
flatpak:
|
||||
name: "Flatpak"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 600
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:gnome-42
|
||||
options: --privileged
|
||||
|
@@ -1,10 +0,0 @@
|
||||
rebuild_master:
|
||||
steps:
|
||||
- trigger_services:
|
||||
project: home:Rafostar
|
||||
package: clapper
|
||||
filters:
|
||||
event: push
|
||||
branches:
|
||||
only:
|
||||
- master
|
4
lib/gst/clapper/gstclapper.c
vendored
4
lib/gst/clapper/gstclapper.c
vendored
@@ -3222,10 +3222,6 @@ gst_clapper_main (gpointer data)
|
||||
|
||||
_update_from_env (&self->use_playbin3, "GST_CLAPPER_USE_PLAYBIN3");
|
||||
|
||||
/* Takes precedence over `GST_CLAPPER_USE_PLAYBIN3` as it
|
||||
* influences element factory behavior */
|
||||
_update_from_env (&self->use_playbin3, "USE_PLAYBIN3");
|
||||
|
||||
if (self->use_playbin3) {
|
||||
GST_DEBUG_OBJECT (self, "playbin3 enabled");
|
||||
self->playbin = gst_element_factory_make ("playbin3", "playbin3");
|
||||
|
31
lib/gst/clapper/meson.build
vendored
31
lib/gst/clapper/meson.build
vendored
@@ -34,36 +34,13 @@ gstclapper_defines = [
|
||||
'-DGST_USE_UNSTABLE_API',
|
||||
'-DHAVE_GTK_GL',
|
||||
]
|
||||
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')
|
||||
if not get_option('lib')
|
||||
subdir_done()
|
||||
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
|
||||
if not gir.found()
|
||||
error('Clapper lib requires GI bindings to be compiled')
|
||||
endif
|
||||
|
||||
if not have_gtk_gl_windowing
|
||||
|
5
lib/gst/meson.build
vendored
5
lib/gst/meson.build
vendored
@@ -1,5 +1,2 @@
|
||||
if get_option('lib')
|
||||
subdir('clapper')
|
||||
endif
|
||||
|
||||
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
|
464
lib/gst/plugin/gstclapperdmabufbaseimport.c
vendored
Normal file
464
lib/gst/plugin/gstclapperdmabufbaseimport.c
vendored
Normal file
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* 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 <gtk/gtk.h>
|
||||
#include <gst/allocators/gstdmabuf.h>
|
||||
#include <gst/gl/egl/gsteglimage.h>
|
||||
|
||||
#include "gstclapperdmabufbaseimport.h"
|
||||
#include "gstgtkutils.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_dmabuf_base_import_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
static const GLfloat vertices[] = {
|
||||
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
|
||||
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
|
||||
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
|
||||
1.0f, -1.0f, 0.0f, 1.0f, 1.0f
|
||||
};
|
||||
static const GLushort indices[] = {
|
||||
0, 1, 2, 0, 2, 3
|
||||
};
|
||||
|
||||
/* GTK4 renders things upside down ¯\_(ツ)_/¯ */
|
||||
static const gfloat vertical_flip_matrix[] = {
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstClapperDmabufBaseImport *dmabuf_bi;
|
||||
GLuint id;
|
||||
guint width;
|
||||
guint height;
|
||||
} GstClapperDmabufTexData;
|
||||
|
||||
#define parent_class gst_clapper_dmabuf_base_import_parent_class
|
||||
G_DEFINE_TYPE (GstClapperDmabufBaseImport, gst_clapper_dmabuf_base_import, GST_TYPE_CLAPPER_GL_BASE_IMPORT);
|
||||
|
||||
static void
|
||||
gst_clapper_dmabuf_base_import_init (GstClapperDmabufBaseImport *self)
|
||||
{
|
||||
g_mutex_init (&self->lock);
|
||||
|
||||
self->gst_tex_target = GST_GL_TEXTURE_TARGET_EXTERNAL_OES;
|
||||
self->gl_tex_target = gst_gl_texture_target_to_gl (self->gst_tex_target);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_dmabuf_base_import_finalize (GObject *object)
|
||||
{
|
||||
GstClapperDmabufBaseImport *self = GST_CLAPPER_DMABUF_BASE_IMPORT_CAST (object);
|
||||
|
||||
gst_clear_object (&self->shader);
|
||||
|
||||
g_mutex_clear (&self->lock);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_dmabuf_base_import_bind_buffer (GstClapperDmabufBaseImport *self)
|
||||
{
|
||||
GstClapperGLBaseImport *gl_bi = GST_CLAPPER_GL_BASE_IMPORT_CAST (self);
|
||||
const GstGLFuncs *gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gl->BindBuffer (GL_ARRAY_BUFFER, self->vertex_buffer);
|
||||
|
||||
/* Load the vertex position */
|
||||
gl->VertexAttribPointer (self->attr_position, 3, GL_FLOAT, GL_FALSE,
|
||||
5 * sizeof (GLfloat), (void *) 0);
|
||||
|
||||
/* Load the texture coordinate */
|
||||
gl->VertexAttribPointer (self->attr_texture, 2, GL_FLOAT, GL_FALSE,
|
||||
5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
|
||||
|
||||
gl->EnableVertexAttribArray (self->attr_position);
|
||||
gl->EnableVertexAttribArray (self->attr_texture);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_dmabuf_base_import_unbind_buffer (GstClapperDmabufBaseImport *self)
|
||||
{
|
||||
GstClapperGLBaseImport *gl_bi = GST_CLAPPER_GL_BASE_IMPORT_CAST (self);
|
||||
const GstGLFuncs *gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
|
||||
gl->DisableVertexAttribArray (self->attr_position);
|
||||
gl->DisableVertexAttribArray (self->attr_texture);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
prepare_on_main (GstClapperDmabufBaseImport *self)
|
||||
{
|
||||
GstClapperGLBaseImport *gl_bi = GST_CLAPPER_GL_BASE_IMPORT_CAST (self);
|
||||
GstGLSLStage *frag_stage, *vert_stage;
|
||||
GError *error = NULL;
|
||||
gchar *frag_str;
|
||||
const GstGLFuncs *gl;
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (gl_bi);
|
||||
|
||||
gdk_gl_context_make_current (gl_bi->gdk_context);
|
||||
gst_gl_context_activate (gl_bi->gst_context, TRUE);
|
||||
|
||||
if (!((vert_stage = gst_glsl_stage_new_with_string (gl_bi->gst_context,
|
||||
GL_VERTEX_SHADER, GST_GLSL_VERSION_NONE,
|
||||
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY,
|
||||
gst_gl_shader_string_vertex_mat4_vertex_transform)))) {
|
||||
gdk_gl_context_make_current (gl_bi->gdk_context);
|
||||
gst_gl_context_activate (gl_bi->gst_context, TRUE);
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (gl_bi);
|
||||
GST_ERROR ("Failed to retrieve vertex shader for texture target");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
frag_str = gst_gl_shader_string_fragment_external_oes_get_default (
|
||||
gl_bi->gst_context, GST_GLSL_VERSION_NONE,
|
||||
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY);
|
||||
frag_stage = gst_glsl_stage_new_with_string (gl_bi->gst_context,
|
||||
GL_FRAGMENT_SHADER, GST_GLSL_VERSION_NONE,
|
||||
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, frag_str);
|
||||
|
||||
g_free (frag_str);
|
||||
|
||||
if (!frag_stage) {
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (gl_bi);
|
||||
GST_ERROR ("Failed to retrieve fragment shader for texture target");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_CLAPPER_DMABUF_BASE_IMPORT_LOCK (self);
|
||||
|
||||
if (!((self->shader = gst_gl_shader_new_link_with_stages (gl_bi->gst_context,
|
||||
&error, vert_stage, frag_stage, NULL)))) {
|
||||
GST_CLAPPER_DMABUF_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (gl_bi);
|
||||
|
||||
GST_ERROR ("Failed to initialize shader: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->attr_position =
|
||||
gst_gl_shader_get_attribute_location (self->shader, "a_position");
|
||||
self->attr_texture =
|
||||
gst_gl_shader_get_attribute_location (self->shader, "a_texcoord");
|
||||
|
||||
gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
if (gl->GenVertexArrays) {
|
||||
gl->GenVertexArrays (1, &self->vao);
|
||||
gl->BindVertexArray (self->vao);
|
||||
}
|
||||
|
||||
gl->GenBuffers (1, &self->vertex_buffer);
|
||||
gl->BindBuffer (GL_ARRAY_BUFFER, self->vertex_buffer);
|
||||
gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices, GL_STATIC_DRAW);
|
||||
|
||||
if (gl->GenVertexArrays) {
|
||||
gst_clapper_dmabuf_base_import_bind_buffer (self);
|
||||
gl->BindVertexArray (0);
|
||||
}
|
||||
|
||||
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
|
||||
self->prepared = TRUE;
|
||||
|
||||
GST_CLAPPER_DMABUF_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (gl_bi);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ensure_prepared (GstClapperDmabufBaseImport *self)
|
||||
{
|
||||
GST_CLAPPER_DMABUF_BASE_IMPORT_LOCK (self);
|
||||
|
||||
if (self->prepared) {
|
||||
GST_CLAPPER_DMABUF_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (self->gst_tex_target != GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
|
||||
/* We do not need shaders if texture target is 2D */
|
||||
self->prepared = TRUE;
|
||||
GST_CLAPPER_DMABUF_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_CLAPPER_DMABUF_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
if (!(! !gst_gtk_invoke_on_main (
|
||||
(GThreadFunc) (GCallback) prepare_on_main, self))) {
|
||||
GST_ERROR_OBJECT (self, "Could not ensure prepared");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_clapper_dmabuf_base_import_change_state (GstElement *element, GstStateChange transition)
|
||||
{
|
||||
GstClapperDmabufBaseImport *self = GST_CLAPPER_DMABUF_BASE_IMPORT_CAST (element);
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
return ret;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
if (!ensure_prepared (self))
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_dmabuf_into_texture (GstClapperDmabufBaseImport *self, gint *fds, GstVideoInfo *v_info,
|
||||
gsize *offsets, GstClapperDmabufTexData *tex_data)
|
||||
{
|
||||
GstClapperGLBaseImport *gl_bi = GST_CLAPPER_GL_BASE_IMPORT_CAST (self);
|
||||
GstEGLImage *image;
|
||||
const GstGLFuncs *gl;
|
||||
|
||||
image = gst_egl_image_from_dmabuf_direct_target (gl_bi->gst_context,
|
||||
fds, offsets, v_info, self->gst_tex_target);
|
||||
|
||||
/* If HW colorspace conversion failed and there is only one
|
||||
* plane, we can just make it into single EGLImage as is */
|
||||
if (!image && GST_VIDEO_INFO_N_PLANES (v_info) == 1)
|
||||
image = gst_egl_image_from_dmabuf (gl_bi->gst_context,
|
||||
fds[0], v_info, 0, offsets[0]);
|
||||
|
||||
if (!image)
|
||||
return FALSE;
|
||||
|
||||
gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gl->GenTextures (1, &tex_data->id);
|
||||
tex_data->width = GST_VIDEO_INFO_WIDTH (v_info);
|
||||
tex_data->height = GST_VIDEO_INFO_HEIGHT (v_info);
|
||||
|
||||
gl->BindTexture (self->gl_tex_target, tex_data->id);
|
||||
|
||||
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
gl->EGLImageTargetTexture2D (self->gl_tex_target, gst_egl_image_get_image (image));
|
||||
|
||||
gl->BindTexture (GL_TEXTURE_2D, 0);
|
||||
gst_egl_image_unref (image);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_oes_texture_into_2d (GstClapperDmabufBaseImport *self, GstClapperDmabufTexData *tex_data)
|
||||
{
|
||||
GstClapperGLBaseImport *gl_bi = GST_CLAPPER_GL_BASE_IMPORT_CAST (self);
|
||||
GLuint framebuffer, tex_id;
|
||||
GLenum status;
|
||||
const GstGLFuncs *gl;
|
||||
|
||||
gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gl->GenFramebuffers (1, &framebuffer);
|
||||
gl->BindFramebuffer (GL_FRAMEBUFFER, framebuffer);
|
||||
|
||||
gl->GenTextures (1, &tex_id);
|
||||
gl->BindTexture (GL_TEXTURE_2D, tex_id);
|
||||
|
||||
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, tex_data->width, tex_data->height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, tex_id, 0);
|
||||
|
||||
status = gl->CheckFramebufferStatus (GL_FRAMEBUFFER);
|
||||
if (G_UNLIKELY (status != GL_FRAMEBUFFER_COMPLETE)) {
|
||||
GST_ERROR ("Invalid framebuffer status: %u", status);
|
||||
|
||||
gl->BindTexture (GL_TEXTURE_2D, 0);
|
||||
gl->DeleteTextures (1, &tex_id);
|
||||
|
||||
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
|
||||
gl->DeleteFramebuffers (1, &framebuffer);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gl->Viewport (0, 0, tex_data->width, tex_data->height);
|
||||
|
||||
gst_gl_shader_use (self->shader);
|
||||
|
||||
if (gl->BindVertexArray)
|
||||
gl->BindVertexArray (self->vao);
|
||||
|
||||
gst_clapper_dmabuf_base_import_bind_buffer (self);
|
||||
|
||||
gl->ActiveTexture (GL_TEXTURE0);
|
||||
gl->BindTexture (self->gl_tex_target, tex_data->id);
|
||||
|
||||
gst_gl_shader_set_uniform_1i (self->shader, "tex", 0);
|
||||
gst_gl_shader_set_uniform_matrix_4fv (self->shader,
|
||||
"u_transformation", 1, FALSE, vertical_flip_matrix);
|
||||
|
||||
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
|
||||
|
||||
if (gl->BindVertexArray)
|
||||
gl->BindVertexArray (0);
|
||||
else
|
||||
gst_clapper_dmabuf_base_import_unbind_buffer (self);
|
||||
|
||||
gl->BindTexture (self->gl_tex_target, 0);
|
||||
|
||||
/* Replace External OES texture with newly created 2D */
|
||||
gl->DeleteTextures (1, &tex_data->id);
|
||||
tex_data->id = tex_id;
|
||||
|
||||
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
|
||||
gl->DeleteFramebuffers (1, &framebuffer);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_tex_data_free (GstClapperDmabufTexData *tex_data)
|
||||
{
|
||||
GstClapperGLBaseImport *gl_bi = GST_CLAPPER_GL_BASE_IMPORT_CAST (tex_data->dmabuf_bi);
|
||||
|
||||
if (G_LIKELY (tex_data->id > 0)) {
|
||||
const GstGLFuncs *gl;
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (gl_bi);
|
||||
|
||||
gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gdk_gl_context_make_current (gl_bi->gdk_context);
|
||||
gst_gl_context_activate (gl_bi->gst_context, TRUE);
|
||||
|
||||
gl->DeleteTextures (1, &tex_data->id);
|
||||
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (gl_bi);
|
||||
}
|
||||
|
||||
gst_object_unref (tex_data->dmabuf_bi);
|
||||
g_slice_free (GstClapperDmabufTexData, tex_data);
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gst_clapper_dmabuf_base_import_fds_into_texture (GstClapperDmabufBaseImport *self, gint *fds, gsize *offsets)
|
||||
{
|
||||
GstClapperBaseImport *bi = GST_CLAPPER_BASE_IMPORT_CAST (self);
|
||||
GstClapperGLBaseImport *gl_bi = GST_CLAPPER_GL_BASE_IMPORT_CAST (self);
|
||||
GdkTexture *texture = NULL;
|
||||
GstClapperDmabufTexData *tex_data;
|
||||
|
||||
tex_data = g_slice_new (GstClapperDmabufTexData);
|
||||
tex_data->dmabuf_bi = gst_object_ref (self);
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (gl_bi);
|
||||
|
||||
gdk_gl_context_make_current (gl_bi->gdk_context);
|
||||
gst_gl_context_activate (gl_bi->gst_context, TRUE);
|
||||
|
||||
if (!_dmabuf_into_texture (self, fds, &bi->in_info, offsets, tex_data))
|
||||
goto finish;
|
||||
|
||||
/* GTK4 does not support External OES textures.
|
||||
* Make it into 2D using framebuffer + shader */
|
||||
if (self->gst_tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
|
||||
if (G_UNLIKELY (!_oes_texture_into_2d (self, tex_data)))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
texture = gdk_gl_texture_new (gl_bi->gdk_context,
|
||||
tex_data->id,
|
||||
tex_data->width,
|
||||
tex_data->height,
|
||||
(GDestroyNotify) _tex_data_free,
|
||||
tex_data);
|
||||
|
||||
finish:
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (gl_bi);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_dmabuf_base_import_class_init (GstClapperDmabufBaseImportClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperdmabufbaseimport", 0,
|
||||
"Clapper DMABuf Base Import");
|
||||
|
||||
gobject_class->finalize = gst_clapper_dmabuf_base_import_finalize;
|
||||
|
||||
gstelement_class->change_state = gst_clapper_dmabuf_base_import_change_state;
|
||||
}
|
75
lib/gst/plugin/gstclapperdmabufbaseimport.h
vendored
Normal file
75
lib/gst/plugin/gstclapperdmabufbaseimport.h
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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/gl/gstglfuncs.h>
|
||||
|
||||
#include "gstclapperglbaseimport.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_DMABUF_BASE_IMPORT (gst_clapper_dmabuf_base_import_get_type())
|
||||
#define GST_IS_CLAPPER_DMABUF_BASE_IMPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_DMABUF_BASE_IMPORT))
|
||||
#define GST_IS_CLAPPER_DMABUF_BASE_IMPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_DMABUF_BASE_IMPORT))
|
||||
#define GST_CLAPPER_DMABUF_BASE_IMPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_DMABUF_BASE_IMPORT, GstClapperDmabufBaseImportClass))
|
||||
#define GST_CLAPPER_DMABUF_BASE_IMPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_DMABUF_BASE_IMPORT, GstClapperDmabufBaseImport))
|
||||
#define GST_CLAPPER_DMABUF_BASE_IMPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_DMABUF_BASE_IMPORT, GstClapperDmabufBaseImportClass))
|
||||
#define GST_CLAPPER_DMABUF_BASE_IMPORT_CAST(obj) ((GstClapperDmabufBaseImport *)(obj))
|
||||
|
||||
#define GST_CLAPPER_DMABUF_BASE_IMPORT_GET_LOCK(obj) (&GST_CLAPPER_DMABUF_BASE_IMPORT_CAST(obj)->lock)
|
||||
#define GST_CLAPPER_DMABUF_BASE_IMPORT_LOCK(obj) g_mutex_lock (GST_CLAPPER_DMABUF_BASE_IMPORT_GET_LOCK(obj))
|
||||
#define GST_CLAPPER_DMABUF_BASE_IMPORT_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_DMABUF_BASE_IMPORT_GET_LOCK(obj))
|
||||
|
||||
typedef struct _GstClapperDmabufBaseImport GstClapperDmabufBaseImport;
|
||||
typedef struct _GstClapperDmabufBaseImportClass GstClapperDmabufBaseImportClass;
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperDmabufBaseImport, gst_object_unref)
|
||||
#endif
|
||||
|
||||
struct _GstClapperDmabufBaseImport
|
||||
{
|
||||
GstClapperGLBaseImport parent;
|
||||
|
||||
GMutex lock;
|
||||
|
||||
gboolean prepared;
|
||||
|
||||
GstGLTextureTarget gst_tex_target;
|
||||
guint gl_tex_target;
|
||||
|
||||
GstGLShader *shader;
|
||||
|
||||
GLuint vao;
|
||||
GLuint vertex_buffer;
|
||||
GLint attr_position;
|
||||
GLint attr_texture;
|
||||
};
|
||||
|
||||
struct _GstClapperDmabufBaseImportClass
|
||||
{
|
||||
GstClapperGLBaseImportClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_clapper_dmabuf_base_import_get_type (void);
|
||||
|
||||
GdkTexture * gst_clapper_dmabuf_base_import_fds_into_texture (GstClapperDmabufBaseImport *dmabuf_bi, gint *fds, gsize *offsets);
|
||||
|
||||
G_END_DECLS
|
180
lib/gst/plugin/gstclapperdmabufimport.c
vendored
Normal file
180
lib/gst/plugin/gstclapperdmabufimport.c
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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 <gtk/gtk.h>
|
||||
#include <gst/allocators/gstdmabuf.h>
|
||||
|
||||
#include "gstclapperdmabufimport.h"
|
||||
#include "gstclappergdkmemory.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_dmabuf_import_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#ifndef GST_CAPS_FEATURE_MEMORY_DMABUF
|
||||
#define GST_CAPS_FEATURE_MEMORY_DMABUF "memory:DMABuf"
|
||||
#endif
|
||||
|
||||
static GstStaticPadTemplate gst_clapper_dmabuf_import_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_MEMORY_DMABUF,
|
||||
"{ " GST_CLAPPER_GDK_GL_TEXTURE_FORMATS ", NV12 }")
|
||||
"; "
|
||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_DMABUF ", "
|
||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
|
||||
"{ " GST_CLAPPER_GDK_GL_TEXTURE_FORMATS ", NV12 }")));
|
||||
|
||||
static GstStaticPadTemplate gst_clapper_dmabuf_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_GL_TEXTURE_FORMATS ", NV12 }")
|
||||
"; "
|
||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_CLAPPER_GDK_MEMORY ", "
|
||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
|
||||
"{ " GST_CLAPPER_GDK_GL_TEXTURE_FORMATS ", NV12 }")));
|
||||
|
||||
#define parent_class gst_clapper_dmabuf_import_parent_class
|
||||
G_DEFINE_TYPE (GstClapperDmabufImport, gst_clapper_dmabuf_import, GST_TYPE_CLAPPER_DMABUF_BASE_IMPORT);
|
||||
GST_ELEMENT_REGISTER_DEFINE (clapperdmabufimport, "clapperdmabufimport", GST_RANK_NONE,
|
||||
GST_TYPE_CLAPPER_DMABUF_IMPORT);
|
||||
|
||||
static gboolean
|
||||
verify_dmabuf_memory (GstBuffer *buffer, GstVideoInfo *info, gint *fds, gsize *offsets)
|
||||
{
|
||||
guint i, n_planes = GST_VIDEO_INFO_N_PLANES (info);
|
||||
|
||||
for (i = 0; i < n_planes; i++) {
|
||||
GstMemory *memory;
|
||||
gsize plane_size, mem_skip;
|
||||
guint mem_idx, length;
|
||||
|
||||
plane_size = gst_gl_get_plane_data_size (info, NULL, i);
|
||||
|
||||
if (!gst_buffer_find_memory (buffer,
|
||||
GST_VIDEO_INFO_PLANE_OFFSET (info, i),
|
||||
plane_size, &mem_idx, &length, &mem_skip)) {
|
||||
GST_DEBUG ("Could not find memory %u", i);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* We cannot have more then one DMABuf per plane */
|
||||
if (length != 1) {
|
||||
GST_DEBUG ("Data for plane %u spans %u memories", i, length);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memory = gst_buffer_peek_memory (buffer, mem_idx);
|
||||
|
||||
offsets[i] = memory->offset + mem_skip;
|
||||
fds[i] = gst_dmabuf_memory_get_fd (memory);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstBufferPool *
|
||||
gst_clapper_dmabuf_import_create_upstream_pool (GstClapperBaseImport *bi, GstStructure **config)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_clapper_dmabuf_import_transform (GstBaseTransform *bt,
|
||||
GstBuffer *in_buf, GstBuffer *out_buf)
|
||||
{
|
||||
GstClapperDmabufBaseImport *dmabuf_bi = GST_CLAPPER_DMABUF_BASE_IMPORT_CAST (bt);
|
||||
GstClapperBaseImport *bi = GST_CLAPPER_BASE_IMPORT_CAST (bt);
|
||||
GstMapInfo info;
|
||||
GstMemory *memory;
|
||||
GstVideoMeta *meta;
|
||||
GstFlowReturn ret = GST_FLOW_ERROR;
|
||||
|
||||
GST_LOG_OBJECT (bt, "Transforming from %" GST_PTR_FORMAT
|
||||
" into %" GST_PTR_FORMAT, in_buf, out_buf);
|
||||
|
||||
if ((meta = gst_buffer_get_video_meta (in_buf))) {
|
||||
guint i;
|
||||
|
||||
GST_VIDEO_INFO_WIDTH (&bi->in_info) = meta->width;
|
||||
GST_VIDEO_INFO_HEIGHT (&bi->in_info) = meta->height;
|
||||
|
||||
for (i = 0; i < meta->n_planes; i++) {
|
||||
GST_VIDEO_INFO_PLANE_OFFSET (&bi->in_info, i) = meta->offset[i];
|
||||
GST_VIDEO_INFO_PLANE_STRIDE (&bi->in_info, i) = meta->stride[i];
|
||||
}
|
||||
}
|
||||
|
||||
memory = gst_buffer_peek_memory (out_buf, 0);
|
||||
|
||||
if (G_LIKELY (gst_memory_map (memory, &info, GST_MAP_WRITE))) {
|
||||
gint fds[GST_VIDEO_MAX_PLANES];
|
||||
gsize offsets[GST_VIDEO_MAX_PLANES];
|
||||
|
||||
if (verify_dmabuf_memory (in_buf, &bi->in_info, fds, offsets)) {
|
||||
GstClapperGdkMemory *clapper_memory = GST_CLAPPER_GDK_MEMORY_CAST (memory);
|
||||
|
||||
if (G_LIKELY ((clapper_memory->texture = gst_clapper_dmabuf_base_import_fds_into_texture (
|
||||
dmabuf_bi, fds, offsets))))
|
||||
ret = GST_FLOW_OK;
|
||||
}
|
||||
|
||||
gst_memory_unmap (memory, &info);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_dmabuf_import_init (GstClapperDmabufImport *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_dmabuf_import_class_init (GstClapperDmabufImportClass *klass)
|
||||
{
|
||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||
GstBaseTransformClass *gstbasetransform_class = (GstBaseTransformClass *) klass;
|
||||
GstClapperBaseImportClass *bi_class = (GstClapperBaseImportClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperdmabufimport", 0,
|
||||
"Clapper DMABuf Import");
|
||||
|
||||
gstbasetransform_class->transform = gst_clapper_dmabuf_import_transform;
|
||||
|
||||
bi_class->create_upstream_pool = gst_clapper_dmabuf_import_create_upstream_pool;
|
||||
|
||||
gst_element_class_set_metadata (gstelement_class,
|
||||
"Clapper DMABuf import",
|
||||
"Filter/Video", "Imports DMABuf into ClapperGdkMemory",
|
||||
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
||||
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_clapper_dmabuf_import_sink_template);
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_clapper_dmabuf_import_src_template);
|
||||
}
|
@@ -19,18 +19,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstclapperimporter.h"
|
||||
#include "gstclapperdmabufbaseimport.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GstPadTemplate * gst_clapper_importer_loader_make_sink_pad_template (void);
|
||||
#define GST_TYPE_CLAPPER_DMABUF_IMPORT (gst_clapper_dmabuf_import_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperDmabufImport, gst_clapper_dmabuf_import, GST, CLAPPER_DMABUF_IMPORT, GstClapperDmabufBaseImport)
|
||||
|
||||
gboolean gst_clapper_importer_loader_find_importer_for_caps (GstCaps *caps, GstClapperImporter **importer);
|
||||
#define GST_CLAPPER_DMABUF_IMPORT_CAST(obj) ((GstClapperDmabufImport *)(obj))
|
||||
|
||||
gboolean gst_clapper_importer_loader_find_importer_for_context_query (GstQuery *query, GstClapperImporter **importer);
|
||||
struct _GstClapperDmabufImport
|
||||
{
|
||||
GstClapperDmabufBaseImport parent;
|
||||
};
|
||||
|
||||
void gst_clapper_importer_loader_unload_all (void);
|
||||
GST_ELEMENT_REGISTER_DECLARE (clapperdmabufimport);
|
||||
|
||||
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);
|
||||
}
|
@@ -17,21 +17,28 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#pragma once
|
||||
|
||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
#define GST_GDK_MEMORY_ENDIAN_FORMATS "RGBA64_LE"
|
||||
#define GST_GDK_GL_TEXTURE_ENDIAN_FORMATS "RGBA64_LE"
|
||||
#elif G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#define GST_GDK_MEMORY_ENDIAN_FORMATS "RGBA64_BE"
|
||||
#define GST_GDK_GL_TEXTURE_ENDIAN_FORMATS "RGBA64_BE"
|
||||
#endif
|
||||
#include <gst/gstbufferpool.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#define GST_GDK_MEMORY_FORMATS \
|
||||
GST_GDK_MEMORY_ENDIAN_FORMATS ", " \
|
||||
"ABGR, BGRA, ARGB, RGBA, BGRx, RGBx, BGR, RGB"
|
||||
#include "gstclappergdkmemory.h"
|
||||
|
||||
/* Formats that `GdkGLTexture` supports */
|
||||
#define GST_GDK_GL_TEXTURE_FORMATS \
|
||||
GST_GDK_GL_TEXTURE_ENDIAN_FORMATS ", " \
|
||||
"RGBA, RGBx, RGB"
|
||||
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
|
414
lib/gst/plugin/gstclapperglbaseimport.c
vendored
Normal file
414
lib/gst/plugin/gstclapperglbaseimport.c
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* 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 <gtk/gtk.h>
|
||||
#include <gst/gl/gstglfuncs.h>
|
||||
|
||||
#include "gstclapperglbaseimport.h"
|
||||
#include "gstgtkutils.h"
|
||||
|
||||
#if GST_CLAPPER_GL_HAVE_WAYLAND
|
||||
#include <gdk/wayland/gdkwayland.h>
|
||||
#include <gst/gl/wayland/gstgldisplay_wayland.h>
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_HAVE_X11
|
||||
#include <gdk/x11/gdkx.h>
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_HAVE_X11_GLX
|
||||
#include <gst/gl/x11/gstgldisplay_x11.h>
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_HAVE_X11_EGL
|
||||
#include <gst/gl/egl/gstgldisplay_egl.h>
|
||||
#endif
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_gl_base_import_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_gl_base_import_parent_class
|
||||
G_DEFINE_TYPE (GstClapperGLBaseImport, gst_clapper_gl_base_import, GST_TYPE_CLAPPER_BASE_IMPORT);
|
||||
|
||||
static void
|
||||
gst_clapper_gl_base_import_init (GstClapperGLBaseImport *self)
|
||||
{
|
||||
g_mutex_init (&self->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_base_import_finalize (GObject *object)
|
||||
{
|
||||
GstClapperGLBaseImport *self = GST_CLAPPER_GL_BASE_IMPORT_CAST (object);
|
||||
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_context);
|
||||
gst_clear_object (&self->wrapped_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
g_mutex_clear (&self->lock);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static GstGLContext *
|
||||
wrap_current_gl (GstGLDisplay *display, GdkGLAPI gdk_gl_api, GstGLPlatform platform)
|
||||
{
|
||||
GstGLAPI gst_gl_api = GST_GL_API_NONE;
|
||||
|
||||
switch (gdk_gl_api) {
|
||||
case GDK_GL_API_GL:
|
||||
gst_gl_api = GST_GL_API_OPENGL | GST_GL_API_OPENGL3;
|
||||
break;
|
||||
case GDK_GL_API_GLES:
|
||||
gst_gl_api = GST_GL_API_GLES2;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
if (gst_gl_api != GST_GL_API_NONE) {
|
||||
guintptr gl_handle;
|
||||
|
||||
gst_gl_display_filter_gl_api (display, gst_gl_api);
|
||||
|
||||
if ((gl_handle = gst_gl_context_get_current_gl_context (platform)))
|
||||
return gst_gl_context_new_wrapped (display, gl_handle, platform, gst_gl_api);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
retrieve_gl_context_on_main (GstClapperGLBaseImport *self)
|
||||
{
|
||||
GstClapperGLBaseImportClass *gl_bi_class = GST_CLAPPER_GL_BASE_IMPORT_GET_CLASS (self);
|
||||
GdkDisplay *gdk_display;
|
||||
GdkGLContext *gdk_context;
|
||||
GError *error = NULL;
|
||||
GdkGLAPI gdk_gl_api;
|
||||
GstGLPlatform platform = GST_GL_PLATFORM_NONE;
|
||||
gint gl_major = 0, gl_minor = 0;
|
||||
|
||||
if (!gtk_init_check ()) {
|
||||
GST_ERROR_OBJECT (self, "Could not ensure GTK initialization");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gdk_display = gdk_display_get_default ();
|
||||
|
||||
if (!(gdk_context = gdk_display_create_gl_context (gdk_display, &error))) {
|
||||
GST_ERROR_OBJECT (self, "Error creating Gdk GL context: %s",
|
||||
error ? error->message : "No error set by Gdk");
|
||||
g_clear_error (&error);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gl_bi_class->gdk_context_realize (self, gdk_context)) {
|
||||
GST_ERROR_OBJECT (self, "Could not realize Gdk context: %" GST_PTR_FORMAT,
|
||||
gdk_context);
|
||||
g_object_unref (gdk_context);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
gdk_gl_api = gdk_gl_context_get_api (gdk_context);
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (self);
|
||||
|
||||
self->gdk_context = gdk_context;
|
||||
|
||||
#if GST_CLAPPER_GL_HAVE_WAYLAND
|
||||
if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) {
|
||||
struct wl_display *wayland_display =
|
||||
gdk_wayland_display_get_wl_display (gdk_display);
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_wayland_new_with_display (wayland_display);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_HAVE_X11
|
||||
if (GDK_IS_X11_DISPLAY (gdk_display)) {
|
||||
gpointer display_ptr;
|
||||
#if GST_CLAPPER_GL_HAVE_X11_EGL
|
||||
display_ptr = gdk_x11_display_get_egl_display (gdk_display);
|
||||
if (display_ptr) {
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_egl_new_with_egl_display (display_ptr);
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_HAVE_X11_GLX
|
||||
if (!self->gst_display) {
|
||||
display_ptr = gdk_x11_display_get_xdisplay (gdk_display);
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_x11_new_with_display (display_ptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Fallback to generic display */
|
||||
if (G_UNLIKELY (!self->gst_display)) {
|
||||
GST_WARNING_OBJECT (self, "Unknown Gdk display!");
|
||||
self->gst_display = gst_gl_display_new ();
|
||||
}
|
||||
|
||||
#if GST_CLAPPER_GL_HAVE_WAYLAND
|
||||
if (GST_IS_GL_DISPLAY_WAYLAND (self->gst_display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
GST_INFO_OBJECT (self, "Using EGL on Wayland");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_HAVE_X11_EGL
|
||||
if (GST_IS_GL_DISPLAY_EGL (self->gst_display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
GST_INFO_OBJECT (self, "Using EGL on x11");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_HAVE_X11_GLX
|
||||
if (GST_IS_GL_DISPLAY_X11 (self->gst_display)) {
|
||||
platform = GST_GL_PLATFORM_GLX;
|
||||
GST_INFO_OBJECT (self, "Using GLX on x11");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
GST_ERROR_OBJECT (self, "Unsupported GL platform");
|
||||
return FALSE;
|
||||
|
||||
have_display:
|
||||
gdk_gl_context_make_current (self->gdk_context);
|
||||
|
||||
self->wrapped_context = wrap_current_gl (self->gst_display, gdk_gl_api, platform);
|
||||
if (!self->wrapped_context) {
|
||||
GST_ERROR ("Could not retrieve Gdk OpenGL context");
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_INFO ("Retrieved Gdk OpenGL context %" GST_PTR_FORMAT, self->wrapped_context);
|
||||
gst_gl_context_activate (self->wrapped_context, TRUE);
|
||||
|
||||
if (!gst_gl_context_fill_info (self->wrapped_context, &error)) {
|
||||
GST_ERROR ("Failed to fill Gdk context info: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
|
||||
gst_gl_context_activate (self->wrapped_context, FALSE);
|
||||
|
||||
gst_clear_object (&self->wrapped_context);
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_gl_context_get_gl_version (self->wrapped_context, &gl_major, &gl_minor);
|
||||
GST_INFO ("Using OpenGL%s %i.%i", (gdk_gl_api == GDK_GL_API_GLES) ? " ES" : "",
|
||||
gl_major, gl_minor);
|
||||
|
||||
/* Deactivate in both places */
|
||||
gst_gl_context_activate (self->wrapped_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ensure_gl_context (GstClapperGLBaseImport *self)
|
||||
{
|
||||
GstGLDisplay *gst_display = NULL;
|
||||
GstGLContext *gst_context = NULL;
|
||||
GError *error = NULL;
|
||||
gboolean has_gdk_contexts;
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (self);
|
||||
has_gdk_contexts = (self->gdk_context && self->wrapped_context);
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
if (!has_gdk_contexts) {
|
||||
if (!(! !gst_gtk_invoke_on_main (
|
||||
(GThreadFunc) (GCallback) retrieve_gl_context_on_main, self))) {
|
||||
GST_ERROR_OBJECT (self, "Could not retrieve Gdk GL context");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (self);
|
||||
|
||||
gst_display = gst_object_ref (self->gst_display);
|
||||
|
||||
/* GstGLDisplay operations require object lock to be held */
|
||||
GST_OBJECT_LOCK (gst_display);
|
||||
|
||||
if (!self->gst_context) {
|
||||
GST_TRACE_OBJECT (self, "Creating new GstGLContext");
|
||||
|
||||
if (!gst_gl_display_create_context (gst_display, self->wrapped_context,
|
||||
&self->gst_context, &error)) {
|
||||
GST_WARNING ("Could not create OpenGL context: %s",
|
||||
error ? error->message : "Unknown");
|
||||
g_clear_error (&error);
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gst_context = gst_object_ref (self->gst_context);
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
/* Calls `set_context` internally, so we cannot be locked here */
|
||||
gst_gl_display_add_context (gst_display, gst_context);
|
||||
gst_gl_element_propagate_display_context (GST_ELEMENT_CAST (self), gst_display);
|
||||
|
||||
GST_OBJECT_UNLOCK (gst_display);
|
||||
|
||||
gst_object_unref (gst_display);
|
||||
gst_object_unref (gst_context);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_base_import_set_context (GstElement *element, GstContext *context)
|
||||
{
|
||||
GstClapperGLBaseImport *self = GST_CLAPPER_GL_BASE_IMPORT_CAST (element);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Set context");
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (self);
|
||||
gst_gl_handle_set_context (element, context, &self->gst_display,
|
||||
&self->wrapped_context);
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (self);
|
||||
|
||||
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_clapper_gl_base_import_change_state (GstElement *element, GstStateChange transition)
|
||||
{
|
||||
GstClapperGLBaseImport *self = GST_CLAPPER_GL_BASE_IMPORT_CAST (element);
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
return ret;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
if (!ensure_gl_context (self))
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_gl_base_import_query (GstBaseTransform *bt,
|
||||
GstPadDirection direction, GstQuery *query)
|
||||
{
|
||||
GstClapperGLBaseImport *self = GST_CLAPPER_GL_BASE_IMPORT_CAST (bt);
|
||||
gboolean res;
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_CONTEXT:
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (self);
|
||||
res = gst_gl_handle_context_query (GST_ELEMENT_CAST (self), query,
|
||||
self->gst_display, self->gst_context, self->wrapped_context);
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (self);
|
||||
break;
|
||||
default:
|
||||
res = GST_BASE_TRANSFORM_CLASS (parent_class)->query (bt, direction, query);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_gl_base_import_gdk_context_realize (GstClapperGLBaseImport *self, GdkGLContext *gdk_context)
|
||||
{
|
||||
GError *error = NULL;
|
||||
gboolean success;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Realizing GdkGLContext with default implementation");
|
||||
|
||||
gdk_gl_context_set_allowed_apis (gdk_context, GDK_GL_API_GLES);
|
||||
if (!(success = gdk_gl_context_realize (gdk_context, &error))) {
|
||||
GST_WARNING_OBJECT (self, "Could not realize Gdk context with GLES: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
if (!success) {
|
||||
gdk_gl_context_set_allowed_apis (gdk_context, GDK_GL_API_GL);
|
||||
if (!(success = gdk_gl_context_realize (gdk_context, &error))) {
|
||||
GST_WARNING_OBJECT (self, "Could not realize Gdk context with GL: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_base_import_class_init (GstClapperGLBaseImportClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||
GstBaseTransformClass *gstbasetransform_class = (GstBaseTransformClass *) klass;
|
||||
GstClapperGLBaseImportClass *gl_bi_class = (GstClapperGLBaseImportClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperglbaseimport", 0,
|
||||
"Clapper GL Base Import");
|
||||
|
||||
gobject_class->finalize = gst_clapper_gl_base_import_finalize;
|
||||
|
||||
gstelement_class->set_context = gst_clapper_gl_base_import_set_context;
|
||||
gstelement_class->change_state = gst_clapper_gl_base_import_change_state;
|
||||
|
||||
gstbasetransform_class->query = gst_clapper_gl_base_import_query;
|
||||
|
||||
gl_bi_class->gdk_context_realize = gst_clapper_gl_base_import_gdk_context_realize;
|
||||
}
|
75
lib/gst/plugin/gstclapperglbaseimport.h
vendored
Normal file
75
lib/gst/plugin/gstclapperglbaseimport.h
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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/gl/gl.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gstclapperbaseimport.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_GL_BASE_IMPORT (gst_clapper_gl_base_import_get_type())
|
||||
#define GST_IS_CLAPPER_GL_BASE_IMPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_GL_BASE_IMPORT))
|
||||
#define GST_IS_CLAPPER_GL_BASE_IMPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_GL_BASE_IMPORT))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_GL_BASE_IMPORT, GstClapperGLBaseImportClass))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_GL_BASE_IMPORT, GstClapperGLBaseImport))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_GL_BASE_IMPORT, GstClapperGLBaseImportClass))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORT_CAST(obj) ((GstClapperGLBaseImport *)(obj))
|
||||
|
||||
#define GST_CLAPPER_GL_BASE_IMPORT_GET_LOCK(obj) (&GST_CLAPPER_GL_BASE_IMPORT_CAST(obj)->lock)
|
||||
#define GST_CLAPPER_GL_BASE_IMPORT_LOCK(obj) g_mutex_lock (GST_CLAPPER_GL_BASE_IMPORT_GET_LOCK(obj))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORT_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_GL_BASE_IMPORT_GET_LOCK(obj))
|
||||
|
||||
#define GST_CLAPPER_GL_HAVE_WAYLAND (GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND))
|
||||
#define GST_CLAPPER_GL_HAVE_X11 (GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11))
|
||||
#define GST_CLAPPER_GL_HAVE_X11_GLX (GST_CLAPPER_GL_HAVE_X11 && GST_GL_HAVE_PLATFORM_GLX)
|
||||
#define GST_CLAPPER_GL_HAVE_X11_EGL (GST_CLAPPER_GL_HAVE_X11 && GST_GL_HAVE_PLATFORM_EGL)
|
||||
|
||||
typedef struct _GstClapperGLBaseImport GstClapperGLBaseImport;
|
||||
typedef struct _GstClapperGLBaseImportClass GstClapperGLBaseImportClass;
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperGLBaseImport, gst_object_unref)
|
||||
#endif
|
||||
|
||||
struct _GstClapperGLBaseImport
|
||||
{
|
||||
GstClapperBaseImport parent;
|
||||
|
||||
GMutex lock;
|
||||
|
||||
GdkGLContext *gdk_context;
|
||||
GstGLContext *gst_context;
|
||||
GstGLContext *wrapped_context;
|
||||
GstGLDisplay *gst_display;
|
||||
};
|
||||
|
||||
struct _GstClapperGLBaseImportClass
|
||||
{
|
||||
GstClapperBaseImportClass parent_class;
|
||||
|
||||
gboolean (* gdk_context_realize) (GstClapperGLBaseImport *gl_bi,
|
||||
GdkGLContext *gdk_context);
|
||||
};
|
||||
|
||||
GType gst_clapper_gl_base_import_get_type (void);
|
||||
|
||||
G_END_DECLS
|
178
lib/gst/plugin/gstclapperglimport.c
vendored
Normal file
178
lib/gst/plugin/gstclapperglimport.c
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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 "gstclapperglimport.h"
|
||||
#include "gstclappergdkmemory.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_gl_import_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
static GstStaticPadTemplate gst_clapper_gl_import_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_MEMORY_GL_MEMORY,
|
||||
"{ " GST_CLAPPER_GDK_GL_TEXTURE_FORMATS " }") ", "
|
||||
"texture-target = (string) { " GST_GL_TEXTURE_TARGET_2D_STR " }"
|
||||
"; "
|
||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_GL_MEMORY ", "
|
||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
|
||||
"{ " GST_CLAPPER_GDK_GL_TEXTURE_FORMATS " }") ", "
|
||||
"texture-target = (string) { " GST_GL_TEXTURE_TARGET_2D_STR " }"));
|
||||
|
||||
static GstStaticPadTemplate gst_clapper_gl_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_GL_TEXTURE_FORMATS " }")
|
||||
"; "
|
||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_CLAPPER_GDK_MEMORY ", "
|
||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
|
||||
"{ " GST_CLAPPER_GDK_GL_TEXTURE_FORMATS " }")));
|
||||
|
||||
#define parent_class gst_clapper_gl_import_parent_class
|
||||
G_DEFINE_TYPE (GstClapperGLImport, gst_clapper_gl_import, GST_TYPE_CLAPPER_GL_BASE_IMPORT);
|
||||
GST_ELEMENT_REGISTER_DEFINE (clapperglimport, "clapperglimport", GST_RANK_NONE,
|
||||
GST_TYPE_CLAPPER_GL_IMPORT);
|
||||
|
||||
static GstBufferPool *
|
||||
gst_clapper_gl_import_create_upstream_pool (GstClapperBaseImport *bi, GstStructure **config)
|
||||
{
|
||||
GstClapperGLBaseImport *gl_bi = GST_CLAPPER_GL_BASE_IMPORT_CAST (bi);
|
||||
GstBufferPool *pool;
|
||||
GstGLContext *context;
|
||||
|
||||
GST_DEBUG_OBJECT (bi, "Creating new pool");
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (gl_bi);
|
||||
context = gst_object_ref (gl_bi->gst_context);
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (gl_bi);
|
||||
|
||||
pool = gst_gl_buffer_pool_new (context);
|
||||
gst_object_unref (context);
|
||||
|
||||
*config = gst_buffer_pool_get_config (pool);
|
||||
|
||||
gst_buffer_pool_config_add_option (*config, GST_BUFFER_POOL_OPTION_GL_SYNC_META);
|
||||
|
||||
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_gl_import_transform (GstBaseTransform *bt,
|
||||
GstBuffer *in_buf, GstBuffer *out_buf)
|
||||
{
|
||||
GstClapperGLBaseImport *gl_bi = GST_CLAPPER_GL_BASE_IMPORT_CAST (bt);
|
||||
GstClapperBaseImport *bi = GST_CLAPPER_BASE_IMPORT_CAST (bt);
|
||||
GstVideoFrame *frame;
|
||||
GstMapInfo info;
|
||||
GstMemory *memory;
|
||||
GstClapperGdkMemory *clapper_memory;
|
||||
GstGLSyncMeta *sync_meta;
|
||||
|
||||
frame = g_slice_new (GstVideoFrame);
|
||||
|
||||
if (!gst_clapper_base_import_map_buffers (bi, in_buf, out_buf,
|
||||
GST_MAP_READ | GST_MAP_GL, GST_MAP_WRITE, frame, &info, &memory)) {
|
||||
g_slice_free (GstVideoFrame, frame);
|
||||
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
clapper_memory = GST_CLAPPER_GDK_MEMORY_CAST (memory);
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_LOCK (gl_bi);
|
||||
|
||||
/* Must have context active here for both sync meta
|
||||
* and Gdk texture format auto-detection to work */
|
||||
gdk_gl_context_make_current (gl_bi->gdk_context);
|
||||
gst_gl_context_activate (gl_bi->wrapped_context, TRUE);
|
||||
|
||||
sync_meta = gst_buffer_get_gl_sync_meta (in_buf);
|
||||
|
||||
/* Wait for all previous OpenGL commands to complete,
|
||||
* before we start using the input texture */
|
||||
if (sync_meta) {
|
||||
gst_gl_sync_meta_set_sync_point (sync_meta, gl_bi->gst_context);
|
||||
gst_gl_sync_meta_wait (sync_meta, gl_bi->wrapped_context);
|
||||
}
|
||||
|
||||
/* Keep input data alive as long as necessary,
|
||||
* unmap only after texture is destroyed */
|
||||
clapper_memory->texture = gdk_gl_texture_new (
|
||||
gl_bi->gdk_context,
|
||||
*(guint *) GST_VIDEO_FRAME_PLANE_DATA (frame, 0),
|
||||
GST_VIDEO_FRAME_WIDTH (frame),
|
||||
GST_VIDEO_FRAME_HEIGHT (frame),
|
||||
(GDestroyNotify) video_frame_unmap_and_free,
|
||||
frame);
|
||||
|
||||
gst_gl_context_activate (gl_bi->wrapped_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_CLAPPER_GL_BASE_IMPORT_UNLOCK (gl_bi);
|
||||
|
||||
gst_memory_unmap (memory, &info);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_import_init (GstClapperGLImport *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_import_class_init (GstClapperGLImportClass *klass)
|
||||
{
|
||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||
GstBaseTransformClass *gstbasetransform_class = (GstBaseTransformClass *) klass;
|
||||
GstClapperBaseImportClass *bi_class = (GstClapperBaseImportClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperglimport", 0,
|
||||
"Clapper GL Import");
|
||||
|
||||
gstbasetransform_class->transform = gst_clapper_gl_import_transform;
|
||||
|
||||
bi_class->create_upstream_pool = gst_clapper_gl_import_create_upstream_pool;
|
||||
|
||||
gst_element_class_set_metadata (gstelement_class,
|
||||
"Clapper GL import",
|
||||
"Filter/Video", "Imports GL memory into ClapperGdkMemory",
|
||||
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
||||
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_clapper_gl_import_sink_template);
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_clapper_gl_import_src_template);
|
||||
}
|
@@ -19,18 +19,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gst/plugin/gstclapperimporter.h"
|
||||
#include "gstclapperglbaseimport.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_RAW_IMPORTER (gst_clapper_raw_importer_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperRawImporter, gst_clapper_raw_importer, GST, CLAPPER_RAW_IMPORTER, GstClapperImporter)
|
||||
#define GST_TYPE_CLAPPER_GL_IMPORT (gst_clapper_gl_import_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperGLImport, gst_clapper_gl_import, GST, CLAPPER_GL_IMPORT, GstClapperGLBaseImport)
|
||||
|
||||
#define GST_CLAPPER_RAW_IMPORTER_CAST(obj) ((GstClapperRawImporter *)(obj))
|
||||
#define GST_CLAPPER_GL_IMPORT_CAST(obj) ((GstClapperGLImport *)(obj))
|
||||
|
||||
struct _GstClapperRawImporter
|
||||
struct _GstClapperGLImport
|
||||
{
|
||||
GstClapperImporter parent;
|
||||
GstClapperGLBaseImport parent;
|
||||
};
|
||||
|
||||
GST_ELEMENT_REGISTER_DECLARE (clapperglimport);
|
||||
|
||||
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);
|
||||
}
|
@@ -19,18 +19,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gstclapperglbaseimporter.h"
|
||||
#include "gstclapperbaseimport.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_GL_IMPORTER (gst_clapper_gl_importer_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperGLImporter, gst_clapper_gl_importer, GST, CLAPPER_GL_IMPORTER, GstClapperGLBaseImporter)
|
||||
#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_GL_IMPORTER_CAST(obj) ((GstClapperGLImporter *)(obj))
|
||||
#define GST_CLAPPER_IMPORT_CAST(obj) ((GstClapperImport *)(obj))
|
||||
|
||||
struct _GstClapperGLImporter
|
||||
struct _GstClapperImport
|
||||
{
|
||||
GstClapperGLBaseImporter parent;
|
||||
GstClapperBaseImport parent;
|
||||
};
|
||||
|
||||
GST_ELEMENT_REGISTER_DECLARE (clapperimport);
|
||||
|
||||
G_END_DECLS
|
439
lib/gst/plugin/gstclapperimporter.c
vendored
439
lib/gst/plugin/gstclapperimporter.c
vendored
@@ -1,439 +0,0 @@
|
||||
/*
|
||||
* 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 "gstclapperimporter.h"
|
||||
#include "gstgtkutils.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_importer_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_importer_parent_class
|
||||
G_DEFINE_TYPE (GstClapperImporter, gst_clapper_importer, GST_TYPE_OBJECT);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GstVideoOverlayRectangle *rectangle;
|
||||
|
||||
gint x, y;
|
||||
guint width, height;
|
||||
|
||||
gint index;
|
||||
gatomicrefcount ref_count;
|
||||
} GstClapperGdkOverlay;
|
||||
|
||||
static GstClapperGdkOverlay *
|
||||
gst_clapper_gdk_overlay_new (GdkTexture *texture, GstVideoOverlayRectangle *rectangle,
|
||||
gint x, gint y, guint width, guint height, guint index)
|
||||
{
|
||||
GstClapperGdkOverlay *overlay = g_slice_new (GstClapperGdkOverlay);
|
||||
|
||||
overlay->texture = g_object_ref (texture);
|
||||
overlay->rectangle = gst_video_overlay_rectangle_ref (rectangle);
|
||||
overlay->x = x;
|
||||
overlay->y = y;
|
||||
overlay->width = width;
|
||||
overlay->height = height;
|
||||
overlay->index = index;
|
||||
|
||||
g_atomic_ref_count_init (&overlay->ref_count);
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
static GstClapperGdkOverlay *
|
||||
gst_clapper_gdk_overlay_ref (GstClapperGdkOverlay *overlay)
|
||||
{
|
||||
g_atomic_ref_count_inc (&overlay->ref_count);
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gdk_overlay_unref (GstClapperGdkOverlay *overlay)
|
||||
{
|
||||
if (g_atomic_ref_count_dec (&overlay->ref_count)) {
|
||||
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 GstBufferPool *
|
||||
_default_create_pool (GstClapperImporter *self, GstStructure **config)
|
||||
{
|
||||
GST_FIXME_OBJECT (self, "Need to create buffer pool");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
_default_generate_texture (GstClapperImporter *self,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GST_FIXME_OBJECT (self, "GdkTexture generation not implemented");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_importer_init (GstClapperImporter *self)
|
||||
{
|
||||
gst_video_info_init (&self->pending_v_info);
|
||||
gst_video_info_init (&self->v_info);
|
||||
|
||||
self->pending_overlays = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify) gst_clapper_gdk_overlay_unref);
|
||||
self->overlays = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify) gst_clapper_gdk_overlay_unref);
|
||||
|
||||
gdk_rgba_parse (&self->bg, "black");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_importer_finalize (GObject *object)
|
||||
{
|
||||
GstClapperImporter *self = GST_CLAPPER_IMPORTER_CAST (object);
|
||||
|
||||
GST_TRACE ("Finalize");
|
||||
|
||||
gst_clear_buffer (&self->pending_buffer);
|
||||
gst_clear_buffer (&self->buffer);
|
||||
|
||||
g_ptr_array_unref (self->pending_overlays);
|
||||
g_ptr_array_unref (self->overlays);
|
||||
|
||||
g_clear_object (&self->texture);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_importer_class_init (GstClapperImporterClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstClapperImporterClass *importer_class = (GstClapperImporterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperimporter", 0,
|
||||
"Clapper Importer");
|
||||
|
||||
gobject_class->finalize = gst_clapper_importer_finalize;
|
||||
|
||||
importer_class->create_pool = _default_create_pool;
|
||||
importer_class->generate_texture = _default_generate_texture;
|
||||
}
|
||||
|
||||
static GstClapperGdkOverlay *
|
||||
_get_cached_overlay (GPtrArray *overlays, GstVideoOverlayRectangle *rectangle)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < overlays->len; i++) {
|
||||
GstClapperGdkOverlay *overlay = g_ptr_array_index (overlays, i);
|
||||
|
||||
if (overlay->rectangle == rectangle)
|
||||
return overlay;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gint
|
||||
_sort_overlays_cb (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
GstClapperGdkOverlay *overlay_a, *overlay_b;
|
||||
|
||||
overlay_a = *((GstClapperGdkOverlay **) a);
|
||||
overlay_b = *((GstClapperGdkOverlay **) b);
|
||||
|
||||
return (overlay_a->index - overlay_b->index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepares overlays to show with the next rendered buffer.
|
||||
*
|
||||
* In order for overlays caching to work correctly, this should be called for
|
||||
* every received buffer (even if its going to be disgarded), also must be
|
||||
* called together with pending buffer replacement within a single importer
|
||||
* locking, to make sure prepared overlays always match the pending buffer.
|
||||
*/
|
||||
static void
|
||||
gst_clapper_importer_prepare_overlays_locked (GstClapperImporter *self)
|
||||
{
|
||||
GstVideoOverlayCompositionMeta *comp_meta;
|
||||
guint num_overlays, i;
|
||||
|
||||
if (G_UNLIKELY (!self->pending_buffer)
|
||||
|| !(comp_meta = gst_buffer_get_video_overlay_composition_meta (self->pending_buffer))) {
|
||||
guint n_pending = self->pending_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->pending_overlays, 0, n_pending);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Preparing overlays...");
|
||||
|
||||
/* Mark all old overlays as unused by giving them negative index */
|
||||
for (i = 0; i < self->pending_overlays->len; i++) {
|
||||
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->pending_overlays, i);
|
||||
overlay->index = -1;
|
||||
}
|
||||
|
||||
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 *v_meta;
|
||||
GstVideoInfo v_info;
|
||||
GstVideoOverlayRectangle *rectangle;
|
||||
GstClapperGdkOverlay *overlay;
|
||||
GstVideoOverlayFormatFlags flags, alpha_flags = 0;
|
||||
gint comp_x, comp_y;
|
||||
guint comp_width, comp_height;
|
||||
|
||||
rectangle = gst_video_overlay_composition_get_rectangle (comp_meta->overlay, i);
|
||||
|
||||
if ((overlay = _get_cached_overlay (self->pending_overlays, rectangle))) {
|
||||
overlay->index = i;
|
||||
|
||||
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);
|
||||
|
||||
/* Update overlay video info from video meta */
|
||||
if ((v_meta = gst_buffer_get_video_meta (comp_buffer))) {
|
||||
gst_video_info_set_format (&v_info, v_meta->format, v_meta->width, v_meta->height);
|
||||
v_info.stride[0] = v_meta->stride[0];
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (!gst_video_frame_map (&comp_frame, &v_info, comp_buffer, GST_MAP_READ)))
|
||||
return;
|
||||
|
||||
if ((texture = gst_video_frame_into_gdk_texture (&comp_frame))) {
|
||||
overlay = gst_clapper_gdk_overlay_new (texture, rectangle, comp_x, comp_y,
|
||||
comp_width, comp_height, i);
|
||||
g_object_unref (texture);
|
||||
|
||||
GST_TRACE_OBJECT (self, "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->pending_overlays, i, overlay);
|
||||
}
|
||||
|
||||
gst_video_frame_unmap (&comp_frame);
|
||||
}
|
||||
|
||||
/* Remove all overlays that are not going to be used */
|
||||
for (i = self->pending_overlays->len; i > 0; i--) {
|
||||
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->pending_overlays, i - 1);
|
||||
|
||||
if (overlay->index < 0) {
|
||||
GST_TRACE ("Removing unused cached overlay: %" GST_PTR_FORMAT, overlay);
|
||||
g_ptr_array_remove (self->pending_overlays, overlay);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort remaining overlays */
|
||||
if (self->pending_overlays->len > 1) {
|
||||
GST_LOG_OBJECT (self, "Sorting overlays");
|
||||
g_ptr_array_sort (self->pending_overlays, (GCompareFunc) _sort_overlays_cb);
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (num_overlays != self->pending_overlays->len)) {
|
||||
GST_WARNING_OBJECT (self, "Some overlays could not be prepared, %u != %u",
|
||||
num_overlays, self->pending_overlays->len);
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Prepared overlays: %u", self->pending_overlays->len);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_clapper_importer_prepare (GstClapperImporter *self)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
if (importer_class->prepare) {
|
||||
if (!importer_class->prepare (self))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Importer prepared");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_share_data (GstClapperImporter *self, GstClapperImporter *dest)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
if (importer_class->share_data)
|
||||
importer_class->share_data (self, dest);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_set_caps (GstClapperImporter *self, GstCaps *caps)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->has_pending_v_info = gst_video_info_from_caps (&self->pending_v_info, caps);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (importer_class->set_caps)
|
||||
importer_class->set_caps (self, caps);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_set_buffer (GstClapperImporter *self, GstBuffer *buffer)
|
||||
{
|
||||
/* Both overlays and pending buffer must be
|
||||
* set within a single importer locking */
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
gst_buffer_replace (&self->pending_buffer, buffer);
|
||||
gst_clapper_importer_prepare_overlays_locked (self);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
|
||||
GstBufferPool *
|
||||
gst_clapper_importer_create_pool (GstClapperImporter *self, GstStructure **config)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
return importer_class->create_pool (self, config);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_add_allocation_metas (GstClapperImporter *self, GstQuery *query)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
if (importer_class->add_allocation_metas)
|
||||
importer_class->add_allocation_metas (self, query);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_clapper_importer_handle_context_query (GstClapperImporter *self,
|
||||
GstBaseSink *bsink, GstQuery *query)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
if (!importer_class->handle_context_query)
|
||||
return FALSE;
|
||||
|
||||
return importer_class->handle_context_query (self, bsink, query);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_snapshot (GstClapperImporter *self, GdkSnapshot *snapshot,
|
||||
gdouble width, gdouble height, gfloat scale_x, gfloat scale_y)
|
||||
{
|
||||
guint i;
|
||||
gboolean buffer_changed;
|
||||
|
||||
/* Collect all data that we need to snapshot pending buffer,
|
||||
* lock ourselves to make sure everything matches */
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
buffer_changed = gst_buffer_replace (&self->buffer, self->pending_buffer);
|
||||
|
||||
/* Only replace v_info when buffer changed, this way
|
||||
* we still use old (correct) v_info when resizing */
|
||||
if (buffer_changed && self->has_pending_v_info) {
|
||||
self->v_info = self->pending_v_info;
|
||||
self->has_pending_v_info = FALSE;
|
||||
}
|
||||
|
||||
/* Ref overlays associated with current buffer */
|
||||
for (i = 0; i < self->pending_overlays->len; i++) {
|
||||
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->pending_overlays, i);
|
||||
|
||||
g_ptr_array_insert (self->overlays, i, gst_clapper_gdk_overlay_ref (overlay));
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
/* Draw black BG when no buffer or imported format has alpha */
|
||||
if (!self->buffer || GST_VIDEO_INFO_HAS_ALPHA (&self->v_info))
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
|
||||
if (self->buffer) {
|
||||
if (buffer_changed || !self->texture) {
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
GST_TRACE_OBJECT (self, "Importing %" GST_PTR_FORMAT, self->buffer);
|
||||
|
||||
g_clear_object (&self->texture);
|
||||
self->texture = importer_class->generate_texture (self, self->buffer, &self->v_info);
|
||||
} else {
|
||||
GST_TRACE_OBJECT (self, "Reusing texture from %" GST_PTR_FORMAT, self->buffer);
|
||||
}
|
||||
|
||||
if (G_LIKELY (self->texture)) {
|
||||
gtk_snapshot_append_texture (snapshot, self->texture, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
|
||||
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));
|
||||
}
|
||||
} else {
|
||||
GST_ERROR_OBJECT (self, "Failed import of %" GST_PTR_FORMAT, self->buffer);
|
||||
|
||||
/* Draw black instead of texture on failure if not drawn already */
|
||||
if (!GST_VIDEO_INFO_HAS_ALPHA (&self->v_info))
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
}
|
||||
}
|
||||
|
||||
/* Unref all used overlays */
|
||||
if (self->overlays->len > 0)
|
||||
g_ptr_array_remove_range (self->overlays, 0, self->overlays->len);
|
||||
}
|
102
lib/gst/plugin/gstclapperimporter.h
vendored
102
lib/gst/plugin/gstclapperimporter.h
vendored
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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_IMPORTER (gst_clapper_importer_get_type())
|
||||
#define GST_IS_CLAPPER_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_IMPORTER))
|
||||
#define GST_IS_CLAPPER_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_IMPORTER))
|
||||
#define GST_CLAPPER_IMPORTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_IMPORTER, GstClapperImporterClass))
|
||||
#define GST_CLAPPER_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_IMPORTER, GstClapperImporter))
|
||||
#define GST_CLAPPER_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_IMPORTER, GstClapperImporterClass))
|
||||
#define GST_CLAPPER_IMPORTER_CAST(obj) ((GstClapperImporter *)(obj))
|
||||
|
||||
#define GST_CLAPPER_IMPORTER_DEFINE(camel,lower,type) \
|
||||
G_DEFINE_TYPE (camel, lower, type) \
|
||||
G_MODULE_EXPORT GstClapperImporter *make_importer (void); \
|
||||
G_MODULE_EXPORT GstCaps *make_caps (GstRank *rank, GStrv *context_types);
|
||||
|
||||
typedef struct _GstClapperImporter GstClapperImporter;
|
||||
typedef struct _GstClapperImporterClass GstClapperImporterClass;
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperImporter, gst_object_unref)
|
||||
#endif
|
||||
|
||||
struct _GstClapperImporter
|
||||
{
|
||||
GstObject parent;
|
||||
|
||||
GstBuffer *pending_buffer, *buffer;
|
||||
GPtrArray *pending_overlays, *overlays;
|
||||
GstVideoInfo pending_v_info, v_info;
|
||||
gboolean has_pending_v_info;
|
||||
|
||||
GdkTexture *texture;
|
||||
|
||||
GdkRGBA bg;
|
||||
};
|
||||
|
||||
struct _GstClapperImporterClass
|
||||
{
|
||||
GstObjectClass parent_class;
|
||||
|
||||
gboolean (* prepare) (GstClapperImporter *importer);
|
||||
|
||||
void (* share_data) (GstClapperImporter *src,
|
||||
GstClapperImporter *dest);
|
||||
|
||||
void (* set_caps) (GstClapperImporter *importer,
|
||||
GstCaps *caps);
|
||||
|
||||
gboolean (* handle_context_query) (GstClapperImporter *importer,
|
||||
GstBaseSink *bsink,
|
||||
GstQuery *query);
|
||||
|
||||
GstBufferPool * (* create_pool) (GstClapperImporter *importer,
|
||||
GstStructure **config);
|
||||
|
||||
void (* add_allocation_metas) (GstClapperImporter *importer,
|
||||
GstQuery *query);
|
||||
|
||||
GdkTexture * (* generate_texture) (GstClapperImporter *importer,
|
||||
GstBuffer *buffer,
|
||||
GstVideoInfo *v_info);
|
||||
};
|
||||
|
||||
GType gst_clapper_importer_get_type (void);
|
||||
|
||||
gboolean gst_clapper_importer_prepare (GstClapperImporter *importer);
|
||||
void gst_clapper_importer_share_data (GstClapperImporter *importer, GstClapperImporter *dest);
|
||||
gboolean gst_clapper_importer_handle_context_query (GstClapperImporter *importer, GstBaseSink *bsink, GstQuery *query);
|
||||
GstBufferPool * gst_clapper_importer_create_pool (GstClapperImporter *importer, GstStructure **config);
|
||||
void gst_clapper_importer_add_allocation_metas (GstClapperImporter *importer, GstQuery *query);
|
||||
|
||||
void gst_clapper_importer_set_caps (GstClapperImporter *importer, GstCaps *caps);
|
||||
void gst_clapper_importer_set_buffer (GstClapperImporter *importer, GstBuffer *buffer);
|
||||
|
||||
void gst_clapper_importer_snapshot (GstClapperImporter *importer, GdkSnapshot *snapshot, gdouble width, gdouble height, gfloat scale_x, gfloat scale_y);
|
||||
|
||||
G_END_DECLS
|
417
lib/gst/plugin/gstclapperimporterloader.c
vendored
417
lib/gst/plugin/gstclapperimporterloader.c
vendored
@@ -1,417 +0,0 @@
|
||||
/*
|
||||
* 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 <gmodule.h>
|
||||
|
||||
#include "gstclapperimporterloader.h"
|
||||
#include "gstclapperimporter.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_importer_loader_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
typedef GstClapperImporter* (* MakeImporter) (void);
|
||||
typedef GstCaps* (* MakeCaps) (GstRank *rank, GStrv *context_types);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *module_path;
|
||||
GModule *open_module;
|
||||
GstCaps *caps;
|
||||
GstRank rank;
|
||||
GStrv context_types;
|
||||
} GstClapperImporterData;
|
||||
|
||||
static void
|
||||
gst_clapper_importer_data_free (GstClapperImporterData *data)
|
||||
{
|
||||
g_free (data->module_path);
|
||||
|
||||
if (data->open_module)
|
||||
g_module_close (data->open_module);
|
||||
|
||||
gst_clear_caps (&data->caps);
|
||||
g_strfreev (data->context_types);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_open_importer (GstClapperImporterData *data)
|
||||
{
|
||||
g_return_val_if_fail (data && data->module_path, FALSE);
|
||||
|
||||
/* Already open */
|
||||
if (data->open_module)
|
||||
return TRUE;
|
||||
|
||||
GST_DEBUG ("Opening module: %s", data->module_path);
|
||||
data->open_module = g_module_open (data->module_path, G_MODULE_BIND_LAZY);
|
||||
|
||||
if (!data->open_module) {
|
||||
GST_WARNING ("Could not load importer: %s, reason: %s",
|
||||
data->module_path, g_module_error ());
|
||||
return FALSE;
|
||||
}
|
||||
GST_DEBUG ("Opened importer module");
|
||||
|
||||
/* Make sure module stays loaded. Seems to be needed for
|
||||
* reusing exported symbols from the same module again */
|
||||
g_module_make_resident (data->open_module);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_close_importer (GstClapperImporterData *data)
|
||||
{
|
||||
if (!data || !data->open_module)
|
||||
return;
|
||||
|
||||
if (G_LIKELY (g_module_close (data->open_module)))
|
||||
GST_DEBUG ("Closed module: %s", data->module_path);
|
||||
else
|
||||
GST_WARNING ("Could not close importer module");
|
||||
|
||||
data->open_module = NULL;
|
||||
}
|
||||
|
||||
static GstClapperImporter *
|
||||
_obtain_importer_internal (GstClapperImporterData *data)
|
||||
{
|
||||
MakeImporter make_importer;
|
||||
GstClapperImporter *importer = NULL;
|
||||
|
||||
if (!_open_importer (data))
|
||||
goto finish;
|
||||
|
||||
if (!g_module_symbol (data->open_module, "make_importer", (gpointer *) &make_importer)
|
||||
|| make_importer == NULL) {
|
||||
GST_WARNING ("Make function missing in importer");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Do not close the module, we are gonna continue using it */
|
||||
if ((importer = make_importer ()))
|
||||
goto finish;
|
||||
|
||||
fail:
|
||||
_close_importer (data);
|
||||
|
||||
finish:
|
||||
return importer;
|
||||
}
|
||||
|
||||
static GstClapperImporterData *
|
||||
_fill_importer_data (const gchar *module_path)
|
||||
{
|
||||
MakeCaps make_caps;
|
||||
GstClapperImporterData *data;
|
||||
|
||||
data = g_new0 (GstClapperImporterData, 1);
|
||||
data->module_path = g_strdup (module_path);
|
||||
data->open_module = g_module_open (data->module_path, G_MODULE_BIND_LAZY);
|
||||
|
||||
if (!data->open_module)
|
||||
goto fail;
|
||||
|
||||
if (!g_module_symbol (data->open_module, "make_caps", (gpointer *) &make_caps)
|
||||
|| make_caps == NULL) {
|
||||
GST_WARNING ("Make caps function missing in importer");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->caps = make_caps (&data->rank, &data->context_types);
|
||||
GST_DEBUG ("Caps reading %ssuccessful", data->caps ? "" : "un");
|
||||
|
||||
if (!data->caps)
|
||||
goto fail;
|
||||
|
||||
/* Once we obtain importer data, close module afterwards */
|
||||
_close_importer (data);
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
gst_clapper_importer_data_free (data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gint
|
||||
_sort_importers_cb (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
GstClapperImporterData *data_a, *data_b;
|
||||
|
||||
data_a = *((GstClapperImporterData **) a);
|
||||
data_b = *((GstClapperImporterData **) b);
|
||||
|
||||
return (data_b->rank - data_a->rank);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
_obtain_available_importers (G_GNUC_UNUSED gpointer data)
|
||||
{
|
||||
GPtrArray *importers;
|
||||
GFile *dir;
|
||||
GFileEnumerator *dir_enum;
|
||||
GError *error = NULL;
|
||||
|
||||
importers = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify) gst_clapper_importer_data_free);
|
||||
|
||||
GST_INFO ("Checking available clapper sink importers");
|
||||
|
||||
dir = g_file_new_for_path (CLAPPER_SINK_IMPORTER_PATH);
|
||||
|
||||
if ((dir_enum = g_file_enumerate_children (dir,
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error))) {
|
||||
while (TRUE) {
|
||||
GFileInfo *info = NULL;
|
||||
GstClapperImporterData *data;
|
||||
gchar *module_path;
|
||||
const gchar *module_name;
|
||||
|
||||
if (!g_file_enumerator_iterate (dir_enum, &info,
|
||||
NULL, NULL, &error) || !info)
|
||||
break;
|
||||
|
||||
module_name = g_file_info_get_name (info);
|
||||
|
||||
if (!g_str_has_suffix (module_name, G_MODULE_SUFFIX))
|
||||
continue;
|
||||
|
||||
module_path = g_module_build_path (CLAPPER_SINK_IMPORTER_PATH, module_name);
|
||||
data = _fill_importer_data (module_path);
|
||||
g_free (module_path);
|
||||
|
||||
if (!data) {
|
||||
GST_WARNING ("Could not read importer data: %s", module_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
GST_INFO ("Found importer: %s, caps: %" GST_PTR_FORMAT, module_name, data->caps);
|
||||
g_ptr_array_add (importers, data);
|
||||
}
|
||||
|
||||
g_object_unref (dir_enum);
|
||||
}
|
||||
|
||||
g_object_unref (dir);
|
||||
|
||||
if (error) {
|
||||
GST_ERROR ("Could not load importer, reason: %s",
|
||||
(error->message) ? error->message : "unknown");
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
g_ptr_array_sort (importers, (GCompareFunc) _sort_importers_cb);
|
||||
|
||||
return importers;
|
||||
}
|
||||
|
||||
static const GPtrArray *
|
||||
gst_clapper_importer_loader_get_available_importers (void)
|
||||
{
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
g_once (&once, _obtain_available_importers, NULL);
|
||||
return (const GPtrArray *) once.retval;
|
||||
}
|
||||
|
||||
static GstClapperImporterData *
|
||||
_find_open_importer_data (const GPtrArray *importers)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < importers->len; i++) {
|
||||
GstClapperImporterData *data = g_ptr_array_index (importers, i);
|
||||
|
||||
if (data->open_module)
|
||||
return data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GstClapperImporterData *
|
||||
_get_importer_data_for_caps (const GPtrArray *importers, const GstCaps *caps)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < importers->len; i++) {
|
||||
GstClapperImporterData *data = g_ptr_array_index (importers, i);
|
||||
|
||||
if (!gst_caps_is_always_compatible (caps, data->caps))
|
||||
continue;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GstClapperImporterData *
|
||||
_get_importer_data_for_context_type (const GPtrArray *importers, const gchar *context_type)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < importers->len; i++) {
|
||||
GstClapperImporterData *data = g_ptr_array_index (importers, i);
|
||||
guint j;
|
||||
|
||||
if (!data->context_types)
|
||||
continue;
|
||||
|
||||
for (j = 0; data->context_types[j]; j++) {
|
||||
if (strcmp (context_type, data->context_types[j]))
|
||||
continue;
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_loader_unload_all (void)
|
||||
{
|
||||
const GPtrArray *importers;
|
||||
guint i;
|
||||
|
||||
importers = gst_clapper_importer_loader_get_available_importers ();
|
||||
GST_TRACE ("Unloading all open modules");
|
||||
|
||||
for (i = 0; i < importers->len; i++) {
|
||||
GstClapperImporterData *data = g_ptr_array_index (importers, i);
|
||||
|
||||
_close_importer (data);
|
||||
}
|
||||
}
|
||||
|
||||
GstPadTemplate *
|
||||
gst_clapper_importer_loader_make_sink_pad_template (void)
|
||||
{
|
||||
const GPtrArray *importers;
|
||||
GstCaps *sink_caps;
|
||||
GstPadTemplate *templ;
|
||||
guint i;
|
||||
|
||||
/* This is only called once from sink class init function */
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperimporterloader", 0,
|
||||
"Clapper Importer Loader");
|
||||
|
||||
importers = gst_clapper_importer_loader_get_available_importers ();
|
||||
sink_caps = gst_caps_new_empty ();
|
||||
|
||||
for (i = 0; i < importers->len; i++) {
|
||||
GstClapperImporterData *data = g_ptr_array_index (importers, i);
|
||||
GstCaps *copied_caps;
|
||||
|
||||
copied_caps = gst_caps_copy (data->caps);
|
||||
gst_caps_append (sink_caps, copied_caps);
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (gst_caps_is_empty (sink_caps)))
|
||||
gst_caps_append (sink_caps, gst_caps_new_any ());
|
||||
|
||||
templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps);
|
||||
gst_caps_unref (sink_caps);
|
||||
|
||||
return templ;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_find_importer_internal (GstCaps *caps, GstQuery *query, GstClapperImporter **importer)
|
||||
{
|
||||
const GPtrArray *importers;
|
||||
GstClapperImporterData *old_data = NULL, *new_data = NULL;
|
||||
GstClapperImporter *found_importer = NULL;
|
||||
|
||||
importers = gst_clapper_importer_loader_get_available_importers ();
|
||||
old_data = _find_open_importer_data (importers);
|
||||
|
||||
if (caps) {
|
||||
GST_DEBUG ("Requested importer for caps: %" GST_PTR_FORMAT, caps);
|
||||
new_data = _get_importer_data_for_caps (importers, caps);
|
||||
} else if (query) {
|
||||
const gchar *context_type;
|
||||
|
||||
gst_query_parse_context_type (query, &context_type);
|
||||
|
||||
GST_DEBUG ("Requested importer for context: %s", context_type);
|
||||
new_data = _get_importer_data_for_context_type (importers, context_type);
|
||||
|
||||
/* In case missing importer for context query, leave the old one.
|
||||
* We should allow some queries to go through unresponded */
|
||||
if (!new_data)
|
||||
new_data = old_data;
|
||||
}
|
||||
GST_LOG ("Old importer path: %s, new path: %s",
|
||||
(old_data != NULL) ? old_data->module_path : NULL,
|
||||
(new_data != NULL) ? new_data->module_path : NULL);
|
||||
|
||||
if (old_data == new_data) {
|
||||
GST_DEBUG ("No importer change");
|
||||
|
||||
if (*importer && caps)
|
||||
gst_clapper_importer_set_caps (*importer, caps);
|
||||
|
||||
return (*importer != NULL);
|
||||
}
|
||||
|
||||
if (new_data) {
|
||||
found_importer = _obtain_importer_internal (new_data);
|
||||
|
||||
if (*importer && found_importer)
|
||||
gst_clapper_importer_share_data (*importer, found_importer);
|
||||
}
|
||||
|
||||
gst_clear_object (importer);
|
||||
_close_importer (old_data);
|
||||
|
||||
if (found_importer && gst_clapper_importer_prepare (found_importer)) {
|
||||
if (caps)
|
||||
gst_clapper_importer_set_caps (found_importer, caps);
|
||||
|
||||
*importer = found_importer;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gst_clear_object (&found_importer);
|
||||
_close_importer (new_data);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_clapper_importer_loader_find_importer_for_caps (GstCaps *caps, GstClapperImporter **importer)
|
||||
{
|
||||
return _find_importer_internal (caps, NULL, importer);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_clapper_importer_loader_find_importer_for_context_query (GstQuery *query, GstClapperImporter **importer)
|
||||
{
|
||||
return _find_importer_internal (NULL, query, importer);
|
||||
}
|
299
lib/gst/plugin/gstclapperpaintable.c
vendored
299
lib/gst/plugin/gstclapperpaintable.c
vendored
@@ -22,6 +22,8 @@
|
||||
#endif
|
||||
|
||||
#include "gstclapperpaintable.h"
|
||||
#include "gstclappergdkmemory.h"
|
||||
#include "gstgtkutils.h"
|
||||
|
||||
#define DEFAULT_PAR_N 1
|
||||
#define DEFAULT_PAR_D 1
|
||||
@@ -29,6 +31,27 @@
|
||||
#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);
|
||||
@@ -55,12 +78,13 @@ gst_clapper_paintable_init (GstClapperPaintable *self)
|
||||
{
|
||||
self->par_n = DEFAULT_PAR_N;
|
||||
self->par_d = DEFAULT_PAR_D;
|
||||
self->pixel_aspect = ((gdouble) self->par_d / self->par_n);
|
||||
|
||||
g_mutex_init (&self->lock);
|
||||
gst_video_info_init (&self->v_info);
|
||||
g_weak_ref_init (&self->widget, NULL);
|
||||
g_weak_ref_init (&self->importer, NULL);
|
||||
|
||||
self->overlays = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify) gst_clapper_gdk_overlay_free);
|
||||
|
||||
gdk_rgba_parse (&self->bg, "black");
|
||||
}
|
||||
@@ -89,15 +113,24 @@ gst_clapper_paintable_finalize (GObject *object)
|
||||
|
||||
GST_TRACE ("Finalize");
|
||||
|
||||
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||
|
||||
g_weak_ref_clear (&self->widget);
|
||||
g_weak_ref_clear (&self->importer);
|
||||
|
||||
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, const GstVideoInfo *info)
|
||||
calculate_display_par (GstClapperPaintable *self, GstVideoInfo *info)
|
||||
{
|
||||
gint width, height, par_n, par_d, req_par_n, req_par_d;
|
||||
gboolean success;
|
||||
@@ -149,8 +182,6 @@ invalidate_paintable_size_internal (GstClapperPaintable *self)
|
||||
display_ratio_num = self->display_ratio_num;
|
||||
display_ratio_den = self->display_ratio_den;
|
||||
|
||||
self->pixel_aspect = ((gdouble) self->par_d / self->par_n);
|
||||
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
if (video_height % display_ratio_den == 0) {
|
||||
@@ -193,6 +224,152 @@ invalidate_paintable_size_on_main_cb (GstClapperPaintable *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)
|
||||
{
|
||||
@@ -204,10 +381,16 @@ update_paintable_on_main_cb (GstClapperPaintable *self)
|
||||
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);
|
||||
|
||||
@@ -230,23 +413,18 @@ gst_clapper_paintable_set_widget (GstClapperPaintable *self, GtkWidget *widget)
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_paintable_set_importer (GstClapperPaintable *self, GstClapperImporter *importer)
|
||||
{
|
||||
g_weak_ref_set (&self->importer, importer);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_paintable_queue_draw (GstClapperPaintable *self)
|
||||
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 draw");
|
||||
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);
|
||||
|
||||
@@ -254,7 +432,7 @@ gst_clapper_paintable_queue_draw (GstClapperPaintable *self)
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_clapper_paintable_set_video_info (GstClapperPaintable *self, const GstVideoInfo *v_info)
|
||||
gst_clapper_paintable_set_video_info (GstClapperPaintable *self, GstVideoInfo *v_info)
|
||||
{
|
||||
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||
|
||||
@@ -301,9 +479,7 @@ gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *self,
|
||||
/* 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) {
|
||||
self->pending_resize = success;
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -311,6 +487,8 @@ gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *self,
|
||||
(GSourceFunc) invalidate_paintable_size_on_main_cb, self, NULL);
|
||||
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -321,10 +499,10 @@ gst_clapper_paintable_snapshot_internal (GstClapperPaintable *self,
|
||||
GdkSnapshot *snapshot, gdouble width, gdouble height,
|
||||
gint widget_width, gint widget_height)
|
||||
{
|
||||
GstClapperImporter *importer;
|
||||
GstMemory *memory;
|
||||
GstMapInfo info;
|
||||
gfloat scale_x, scale_y;
|
||||
|
||||
GST_LOG_OBJECT (self, "Snapshot");
|
||||
guint i;
|
||||
|
||||
scale_x = (gfloat) width / self->display_width;
|
||||
scale_y = (gfloat) height / self->display_height;
|
||||
@@ -332,28 +510,51 @@ gst_clapper_paintable_snapshot_internal (GstClapperPaintable *self,
|
||||
/* Apply black borders when keeping aspect ratio */
|
||||
if (scale_x == scale_y || abs (scale_x - scale_y) <= FLT_EPSILON) {
|
||||
if (widget_height - height > 0) {
|
||||
/* XXX: Top uses integer to work with GTK rounding (not going offscreen) */
|
||||
gint top_bar_height = (widget_height - height) / 2;
|
||||
gdouble bottom_bar_height = (widget_height - top_bar_height - height);
|
||||
gdouble bar_height = (widget_height - height) / 2;
|
||||
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, -top_bar_height));
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, height, width, bottom_bar_height));
|
||||
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) {
|
||||
gint left_bar_width = (widget_width - width) / 2;
|
||||
gdouble right_bar_width = (widget_width - left_bar_width - width);
|
||||
gdouble bar_width = (widget_width - width) / 2;
|
||||
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, -left_bar_width, height));
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (width, 0, right_bar_width, height));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if ((importer = g_weak_ref_get (&self->importer))) {
|
||||
gst_clapper_importer_snapshot (importer, snapshot, width, height,
|
||||
scale_x * self->pixel_aspect, scale_y);
|
||||
g_object_unref (importer);
|
||||
} else {
|
||||
GST_LOG_OBJECT (self, "No texture importer, drawing black");
|
||||
/* 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,11 +567,8 @@ gst_clapper_paintable_snapshot (GdkPaintable *paintable,
|
||||
gint widget_width = 0, widget_height = 0;
|
||||
|
||||
if ((widget = g_weak_ref_get (&self->widget))) {
|
||||
gint scale_factor;
|
||||
|
||||
scale_factor = gtk_widget_get_scale_factor (widget);
|
||||
widget_width = gtk_widget_get_width (widget) * scale_factor;
|
||||
widget_height = gtk_widget_get_height (widget) * scale_factor;
|
||||
widget_width = gtk_widget_get_width (widget);
|
||||
widget_height = gtk_widget_get_height (widget);
|
||||
|
||||
g_object_unref (widget);
|
||||
}
|
||||
@@ -383,14 +581,23 @@ static GdkPaintable *
|
||||
gst_clapper_paintable_get_current_image (GdkPaintable *paintable)
|
||||
{
|
||||
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||
GtkSnapshot *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 (self->buffer) {
|
||||
GtkSnapshot *snapshot;
|
||||
GdkPaintable *ret;
|
||||
|
||||
return gtk_snapshot_free_to_paintable (snapshot, NULL);
|
||||
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
|
||||
|
14
lib/gst/plugin/gstclapperpaintable.h
vendored
14
lib/gst/plugin/gstclapperpaintable.h
vendored
@@ -23,8 +23,6 @@
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#include "gstclapperimporter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_PAINTABLE (gst_clapper_paintable_get_type())
|
||||
@@ -42,18 +40,17 @@ struct _GstClapperPaintable
|
||||
|
||||
GMutex lock;
|
||||
|
||||
GstBuffer *pending_buffer, *buffer;
|
||||
GPtrArray *overlays;
|
||||
GstVideoInfo v_info;
|
||||
|
||||
GdkRGBA bg;
|
||||
|
||||
GWeakRef widget, importer;
|
||||
GWeakRef widget;
|
||||
|
||||
/* Sink properties */
|
||||
gint par_n, par_d;
|
||||
|
||||
/* For drawing overlays */
|
||||
gdouble pixel_aspect;
|
||||
|
||||
/* Resize */
|
||||
gboolean pending_resize;
|
||||
guint display_ratio_num;
|
||||
@@ -69,10 +66,9 @@ struct _GstClapperPaintable
|
||||
};
|
||||
|
||||
GstClapperPaintable * gst_clapper_paintable_new (void);
|
||||
void gst_clapper_paintable_queue_draw (GstClapperPaintable *paintable);
|
||||
void gst_clapper_paintable_set_widget (GstClapperPaintable *paintable, GtkWidget *widget);
|
||||
void gst_clapper_paintable_set_importer (GstClapperPaintable *paintable, GstClapperImporter *importer);
|
||||
gboolean gst_clapper_paintable_set_video_info (GstClapperPaintable *paintable, const GstVideoInfo *v_info);
|
||||
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
|
||||
|
170
lib/gst/plugin/gstclappersink.c
vendored
170
lib/gst/plugin/gstclappersink.c
vendored
@@ -22,7 +22,8 @@
|
||||
#endif
|
||||
|
||||
#include "gstclappersink.h"
|
||||
#include "gstclapperimporterloader.h"
|
||||
#include "gstclappergdkmemory.h"
|
||||
#include "gstclappergdkbufferpool.h"
|
||||
#include "gstgtkutils.h"
|
||||
|
||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
||||
@@ -45,6 +46,18 @@ enum
|
||||
#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 ", NV12 }")
|
||||
"; "
|
||||
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 ", NV12 }")));
|
||||
|
||||
static void gst_clapper_sink_navigation_interface_init (
|
||||
GstNavigationInterface *iface);
|
||||
|
||||
@@ -68,7 +81,6 @@ window_clear_no_lock (GstClapperSink *self)
|
||||
self->window_destroy_id = 0;
|
||||
}
|
||||
self->window = NULL;
|
||||
self->presented_window = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -170,16 +182,9 @@ gst_clapper_sink_widget_motion_event (GtkEventControllerMotion *motion,
|
||||
{
|
||||
GtkWidget *widget;
|
||||
gdouble stream_x, stream_y;
|
||||
gboolean is_inactive;
|
||||
|
||||
if (x == self->last_pos_x && y == self->last_pos_y)
|
||||
return;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
is_inactive = (GST_STATE (self) < GST_STATE_PLAYING);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (is_inactive)
|
||||
if ((x == self->last_pos_x && y == self->last_pos_y)
|
||||
|| GST_STATE (self) < GST_STATE_PLAYING)
|
||||
return;
|
||||
|
||||
self->last_pos_x = x;
|
||||
@@ -199,16 +204,11 @@ gst_clapper_sink_widget_button_event (GtkGestureClick *click,
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GdkEvent *event;
|
||||
gdouble stream_x, stream_y;
|
||||
GdkEventType event_type;
|
||||
const gchar *event_name;
|
||||
gdouble stream_x, stream_y;
|
||||
gboolean is_inactive;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
is_inactive = (GST_STATE (self) < GST_STATE_PLAYING);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (is_inactive)
|
||||
if (GST_STATE (self) < GST_STATE_PLAYING)
|
||||
return;
|
||||
|
||||
event = gtk_event_controller_get_current_event ((GtkEventController *) click);
|
||||
@@ -276,7 +276,7 @@ gst_clapper_sink_get_widget (GstClapperSink *self)
|
||||
/* Take floating ref */
|
||||
g_object_ref_sink (self->widget);
|
||||
|
||||
/* Set widget back pointer */
|
||||
/* Set back pointer */
|
||||
gst_clapper_paintable_set_widget (self->paintable, self->widget);
|
||||
|
||||
/* Set earlier remembered property */
|
||||
@@ -416,7 +416,7 @@ gst_clapper_sink_propose_allocation (GstBaseSink *bsink, GstQuery *query)
|
||||
GstBufferPool *pool = NULL;
|
||||
GstCaps *caps;
|
||||
GstVideoInfo info;
|
||||
guint size, min_buffers;
|
||||
guint size;
|
||||
gboolean need_pool;
|
||||
|
||||
gst_query_parse_allocation (query, &caps, &need_pool);
|
||||
@@ -434,79 +434,38 @@ gst_clapper_sink_propose_allocation (GstBaseSink *bsink, GstQuery *query)
|
||||
/* Normal size of a frame */
|
||||
size = GST_VIDEO_INFO_SIZE (&info);
|
||||
|
||||
/* We keep around current buffer and a pending one */
|
||||
min_buffers = 3;
|
||||
|
||||
if (need_pool) {
|
||||
GstStructure *config = NULL;
|
||||
GstStructure *config;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Need to create buffer pool");
|
||||
GST_DEBUG_OBJECT (self, "Creating new pool");
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
pool = gst_clapper_importer_create_pool (self->importer, &config);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
pool = gst_clapper_gdk_buffer_pool_new ();
|
||||
config = gst_buffer_pool_get_config (pool);
|
||||
|
||||
if (pool) {
|
||||
/* If we did not get config, use default one */
|
||||
if (!config)
|
||||
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);
|
||||
|
||||
gst_buffer_pool_config_set_params (config, caps, size, min_buffers, 0);
|
||||
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||
gst_object_unref (pool);
|
||||
|
||||
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||
gst_object_unref (pool);
|
||||
|
||||
GST_ERROR_OBJECT (self, "Failed to set config");
|
||||
return FALSE;
|
||||
}
|
||||
} else if (config) {
|
||||
GST_WARNING_OBJECT (self, "Got config without a pool to apply it");
|
||||
gst_structure_free (config);
|
||||
GST_DEBUG_OBJECT (self, "Failed to set config");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gst_query_add_allocation_pool (query, pool, size, min_buffers, 0);
|
||||
gst_query_add_allocation_pool (query, pool, size, 2, 0);
|
||||
if (pool)
|
||||
gst_object_unref (pool);
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
gst_clapper_importer_add_allocation_metas (self->importer, query);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
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_query (GstBaseSink *bsink, GstQuery *query)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||
gboolean res = FALSE;
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT) {
|
||||
gboolean is_inactive;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
is_inactive = (GST_STATE (self) < GST_STATE_PAUSED);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
/* Some random context query in the middle of playback
|
||||
* should not trigger importer replacement */
|
||||
if (is_inactive)
|
||||
gst_clapper_importer_loader_find_importer_for_context_query (query, &self->importer);
|
||||
if (self->importer)
|
||||
res = gst_clapper_importer_handle_context_query (self->importer, bsink, query);
|
||||
}
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
if (res)
|
||||
return TRUE;
|
||||
|
||||
return GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_start_on_main (GstClapperSink *self)
|
||||
{
|
||||
@@ -551,9 +510,7 @@ gst_clapper_sink_start_on_main (GstClapperSink *self)
|
||||
gtk_get_minor_version (),
|
||||
gtk_get_micro_version ());
|
||||
|
||||
/* Set some common default size, adding stock headerbar height
|
||||
* to it in order to display 4:3 aspect video widget */
|
||||
gtk_window_set_default_size (self->window, 640, 480 + 37);
|
||||
gtk_window_set_default_size (self->window, 640, 480);
|
||||
gtk_window_set_title (self->window, win_title);
|
||||
gtk_window_set_child (self->window, toplevel);
|
||||
|
||||
@@ -645,10 +602,8 @@ gst_clapper_sink_change_state (GstElement *element, GstStateChange transition)
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
if (!self->keep_last_frame && self->importer) {
|
||||
gst_clapper_importer_set_buffer (self->importer, NULL);
|
||||
gst_clapper_paintable_queue_draw (self->paintable);
|
||||
}
|
||||
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:
|
||||
@@ -716,10 +671,16 @@ 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,
|
||||
@@ -728,32 +689,7 @@ gst_clapper_sink_set_caps (GstBaseSink *bsink, GstCaps *caps)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_clapper_importer_loader_find_importer_for_caps (caps, &self->importer)) {
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("No importer for given caps found"), (NULL));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
gst_clapper_paintable_set_importer (self->paintable, self->importer);
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return GST_BASE_SINK_CLASS (parent_class)->set_caps (bsink, caps);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_set_info (GstVideoSink *vsink, GstCaps *caps, const GstVideoInfo *info)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (vsink);
|
||||
gboolean res;
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
self->v_info = *info;
|
||||
GST_DEBUG_OBJECT (self, "Video info changed");
|
||||
|
||||
res = gst_clapper_paintable_set_video_info (self->paintable, info);
|
||||
res = gst_clapper_paintable_set_video_info (self->paintable, &self->v_info);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return res;
|
||||
@@ -775,9 +711,7 @@ gst_clapper_sink_show_frame (GstVideoSink *vsink, GstBuffer *buffer)
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
gst_clapper_importer_set_buffer (self->importer, buffer);
|
||||
gst_clapper_paintable_queue_draw (self->paintable);
|
||||
|
||||
gst_clapper_paintable_set_buffer (self->paintable, buffer);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
@@ -821,7 +755,6 @@ gst_clapper_sink_dispose (GObject *object)
|
||||
widget_clear_no_lock (self);
|
||||
|
||||
g_clear_object (&self->paintable);
|
||||
gst_clear_object (&self->importer);
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
@@ -835,7 +768,6 @@ gst_clapper_sink_finalize (GObject *object)
|
||||
|
||||
GST_TRACE ("Finalize");
|
||||
|
||||
gst_clapper_importer_loader_unload_all ();
|
||||
g_mutex_clear (&self->lock);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
@@ -844,8 +776,6 @@ gst_clapper_sink_finalize (GObject *object)
|
||||
static void
|
||||
gst_clapper_sink_class_init (GstClapperSinkClass *klass)
|
||||
{
|
||||
GstPadTemplate *sink_pad_templ;
|
||||
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||
GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass;
|
||||
@@ -883,11 +813,9 @@ gst_clapper_sink_class_init (GstClapperSinkClass *klass)
|
||||
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->query = gst_clapper_sink_query;
|
||||
gstbasesink_class->start = gst_clapper_sink_start;
|
||||
gstbasesink_class->stop = gst_clapper_sink_stop;
|
||||
|
||||
gstvideosink_class->set_info = gst_clapper_sink_set_info;
|
||||
gstvideosink_class->show_frame = gst_clapper_sink_show_frame;
|
||||
|
||||
gst_element_class_set_metadata (gstelement_class,
|
||||
@@ -895,8 +823,8 @@ gst_clapper_sink_class_init (GstClapperSinkClass *klass)
|
||||
"Sink/Video", "A GTK4 video sink used by Clapper media player",
|
||||
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
||||
|
||||
sink_pad_templ = gst_clapper_importer_loader_make_sink_pad_template ();
|
||||
gst_element_class_add_pad_template (gstelement_class, sink_pad_templ);
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_clapper_sink_template);
|
||||
}
|
||||
|
||||
/*
|
||||
|
2
lib/gst/plugin/gstclappersink.h
vendored
2
lib/gst/plugin/gstclappersink.h
vendored
@@ -25,7 +25,6 @@
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#include "gstclapperpaintable.h"
|
||||
#include "gstclapperimporter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -46,7 +45,6 @@ struct _GstClapperSink
|
||||
GMutex lock;
|
||||
|
||||
GstClapperPaintable *paintable;
|
||||
GstClapperImporter *importer;
|
||||
GstVideoInfo v_info;
|
||||
|
||||
GtkWidget *widget;
|
||||
|
5
lib/gst/plugin/gstgtkutils.c
vendored
5
lib/gst/plugin/gstgtkutils.c
vendored
@@ -108,7 +108,7 @@ gst_video_format_to_gdk_memory_format (GstVideoFormat format)
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gst_video_frame_into_gdk_texture (GstVideoFrame *frame)
|
||||
gst_video_frame_into_gdk_texture (GstVideoFrame *frame, GDestroyNotify free_func)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GBytes *bytes;
|
||||
@@ -116,8 +116,7 @@ gst_video_frame_into_gdk_texture (GstVideoFrame *frame)
|
||||
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),
|
||||
(GDestroyNotify) gst_buffer_unref,
|
||||
gst_buffer_ref (frame->buffer));
|
||||
free_func, frame);
|
||||
|
||||
texture = gdk_memory_texture_new (
|
||||
GST_VIDEO_FRAME_WIDTH (frame),
|
||||
|
8
lib/gst/plugin/gstgtkutils.h
vendored
8
lib/gst/plugin/gstgtkutils.h
vendored
@@ -26,12 +26,8 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data);
|
||||
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);
|
||||
|
||||
G_END_DECLS
|
||||
GdkTexture * gst_video_frame_into_gdk_texture (GstVideoFrame *frame, GDestroyNotify free_func);
|
||||
|
21
lib/gst/plugin/gstplugin.c
vendored
21
lib/gst/plugin/gstplugin.c
vendored
@@ -21,21 +21,26 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gmodule.h>
|
||||
|
||||
#include "gstclapperimport.h"
|
||||
#include "gstclapperglimport.h"
|
||||
#include "gstclapperdmabufimport.h"
|
||||
#include "gstclappersink.h"
|
||||
#include "gstclappergdkmemory.h"
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin *plugin)
|
||||
{
|
||||
if (!g_module_supported ())
|
||||
return FALSE;
|
||||
gboolean res = FALSE;
|
||||
|
||||
gst_plugin_add_dependency_simple (plugin,
|
||||
NULL, CLAPPER_SINK_IMPORTER_PATH, NULL,
|
||||
GST_PLUGIN_DEPENDENCY_FLAG_NONE);
|
||||
res |= GST_ELEMENT_REGISTER (clapperimport, plugin);
|
||||
res |= GST_ELEMENT_REGISTER (clapperglimport, plugin);
|
||||
res |= GST_ELEMENT_REGISTER (clapperdmabufimport, plugin);
|
||||
res |= GST_ELEMENT_REGISTER (clappersink, plugin);
|
||||
|
||||
return GST_ELEMENT_REGISTER (clappersink, plugin);
|
||||
if (res)
|
||||
gst_clapper_gdk_memory_init_once ();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
|
||||
|
549
lib/gst/plugin/importers/gstclapperglbaseimporter.c
vendored
549
lib/gst/plugin/importers/gstclapperglbaseimporter.c
vendored
@@ -1,549 +0,0 @@
|
||||
/*
|
||||
* 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 "gstclapperglbaseimporter.h"
|
||||
#include "gst/plugin/gstgdkformats.h"
|
||||
#include "gst/plugin/gstgtkutils.h"
|
||||
|
||||
#include <gst/gl/gstglfuncs.h>
|
||||
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_WAYLAND
|
||||
#include <gdk/wayland/gdkwayland.h>
|
||||
#include <gst/gl/wayland/gstgldisplay_wayland.h>
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11
|
||||
#include <gdk/x11/gdkx.h>
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_GLX
|
||||
#include <gst/gl/x11/gstgldisplay_x11.h>
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_EGL
|
||||
#include <gst/gl/egl/gstgldisplay_egl.h>
|
||||
#endif
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_gl_base_importer_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_gl_base_importer_parent_class
|
||||
G_DEFINE_TYPE (GstClapperGLBaseImporter, gst_clapper_gl_base_importer, GST_TYPE_CLAPPER_IMPORTER);
|
||||
|
||||
static GstGLContext *
|
||||
wrap_current_gl (GstGLDisplay *display, GdkGLAPI gdk_gl_api, GstGLPlatform platform)
|
||||
{
|
||||
GstGLAPI gst_gl_api = GST_GL_API_NONE;
|
||||
|
||||
switch (gdk_gl_api) {
|
||||
case GDK_GL_API_GL:
|
||||
gst_gl_api = GST_GL_API_OPENGL | GST_GL_API_OPENGL3;
|
||||
break;
|
||||
case GDK_GL_API_GLES:
|
||||
gst_gl_api = GST_GL_API_GLES2;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
if (gst_gl_api != GST_GL_API_NONE) {
|
||||
guintptr gl_handle;
|
||||
|
||||
gst_gl_display_filter_gl_api (display, gst_gl_api);
|
||||
|
||||
if ((gl_handle = gst_gl_context_get_current_gl_context (platform)))
|
||||
return gst_gl_context_new_wrapped (display, gl_handle, platform, gst_gl_api);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
retrieve_gl_context_on_main (GstClapperGLBaseImporter *self)
|
||||
{
|
||||
GstClapperGLBaseImporterClass *gl_bi_class = GST_CLAPPER_GL_BASE_IMPORTER_GET_CLASS (self);
|
||||
GdkDisplay *gdk_display;
|
||||
GdkGLContext *gdk_context;
|
||||
GError *error = NULL;
|
||||
GdkGLAPI gdk_gl_api;
|
||||
GstGLPlatform platform = GST_GL_PLATFORM_NONE;
|
||||
gint gl_major = 0, gl_minor = 0;
|
||||
|
||||
if (!gtk_init_check ()) {
|
||||
GST_ERROR_OBJECT (self, "Could not ensure GTK initialization");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Make sure we are clean here, otherwise data sharing
|
||||
* between GL-based importers may lead to leaks */
|
||||
gst_clear_object (&self->wrapped_context);
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
gdk_display = gdk_display_get_default ();
|
||||
|
||||
if (!(gdk_context = gdk_display_create_gl_context (gdk_display, &error))) {
|
||||
GST_ERROR_OBJECT (self, "Error creating Gdk GL context: %s",
|
||||
error ? error->message : "No error set by Gdk");
|
||||
g_clear_error (&error);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gl_bi_class->gdk_context_realize (self, gdk_context)) {
|
||||
GST_ERROR_OBJECT (self, "Could not realize Gdk context: %" GST_PTR_FORMAT,
|
||||
gdk_context);
|
||||
g_object_unref (gdk_context);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
gdk_gl_api = gdk_gl_context_get_api (gdk_context);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
self->gdk_context = gdk_context;
|
||||
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_WAYLAND
|
||||
if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) {
|
||||
struct wl_display *wayland_display =
|
||||
gdk_wayland_display_get_wl_display (gdk_display);
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_wayland_new_with_display (wayland_display);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11
|
||||
if (GDK_IS_X11_DISPLAY (gdk_display)) {
|
||||
gpointer display_ptr;
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_EGL
|
||||
display_ptr = gdk_x11_display_get_egl_display (gdk_display);
|
||||
if (display_ptr) {
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_egl_new_with_egl_display (display_ptr);
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_GLX
|
||||
if (!self->gst_display) {
|
||||
display_ptr = gdk_x11_display_get_xdisplay (gdk_display);
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_x11_new_with_display (display_ptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Fallback to generic display */
|
||||
if (G_UNLIKELY (!self->gst_display)) {
|
||||
GST_WARNING_OBJECT (self, "Unknown Gdk display!");
|
||||
self->gst_display = gst_gl_display_new ();
|
||||
}
|
||||
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_WAYLAND
|
||||
if (GST_IS_GL_DISPLAY_WAYLAND (self->gst_display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
GST_INFO_OBJECT (self, "Using EGL on Wayland");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_EGL
|
||||
if (GST_IS_GL_DISPLAY_EGL (self->gst_display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
GST_INFO_OBJECT (self, "Using EGL on x11");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_GLX
|
||||
if (GST_IS_GL_DISPLAY_X11 (self->gst_display)) {
|
||||
platform = GST_GL_PLATFORM_GLX;
|
||||
GST_INFO_OBJECT (self, "Using GLX on x11");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
GST_ERROR_OBJECT (self, "Unsupported GL platform");
|
||||
return FALSE;
|
||||
|
||||
have_display:
|
||||
gdk_gl_context_make_current (self->gdk_context);
|
||||
|
||||
self->wrapped_context = wrap_current_gl (self->gst_display, gdk_gl_api, platform);
|
||||
if (!self->wrapped_context) {
|
||||
GST_ERROR ("Could not retrieve Gdk OpenGL context");
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_INFO ("Retrieved Gdk OpenGL context %" GST_PTR_FORMAT, self->wrapped_context);
|
||||
gst_gl_context_activate (self->wrapped_context, TRUE);
|
||||
|
||||
if (!gst_gl_context_fill_info (self->wrapped_context, &error)) {
|
||||
GST_ERROR ("Failed to fill Gdk context info: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
|
||||
gst_gl_context_activate (self->wrapped_context, FALSE);
|
||||
|
||||
gst_clear_object (&self->wrapped_context);
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_gl_context_get_gl_version (self->wrapped_context, &gl_major, &gl_minor);
|
||||
GST_INFO ("Using OpenGL%s %i.%i", (gdk_gl_api == GDK_GL_API_GLES) ? " ES" : "",
|
||||
gl_major, gl_minor);
|
||||
|
||||
/* Deactivate in both places */
|
||||
gst_gl_context_activate (self->wrapped_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
retrieve_gst_context (GstClapperGLBaseImporter *self)
|
||||
{
|
||||
GstGLDisplay *gst_display = NULL;
|
||||
GstGLContext *gst_context = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
gst_display = gst_object_ref (self->gst_display);
|
||||
|
||||
/* GstGLDisplay operations require display object lock to be held */
|
||||
GST_OBJECT_LOCK (gst_display);
|
||||
|
||||
if (!self->gst_context) {
|
||||
GST_TRACE_OBJECT (self, "Creating new GstGLContext");
|
||||
|
||||
if (!gst_gl_display_create_context (gst_display, self->wrapped_context,
|
||||
&self->gst_context, &error)) {
|
||||
GST_WARNING ("Could not create OpenGL context: %s",
|
||||
error ? error->message : "Unknown");
|
||||
g_clear_error (&error);
|
||||
|
||||
GST_OBJECT_UNLOCK (gst_display);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gst_context = gst_object_ref (self->gst_context);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
gst_gl_display_add_context (gst_display, gst_context);
|
||||
|
||||
GST_OBJECT_UNLOCK (gst_display);
|
||||
|
||||
gst_object_unref (gst_display);
|
||||
gst_object_unref (gst_context);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_gl_base_importer_prepare (GstClapperImporter *importer)
|
||||
{
|
||||
GstClapperGLBaseImporter *self = GST_CLAPPER_GL_BASE_IMPORTER_CAST (importer);
|
||||
gboolean need_invoke;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
need_invoke = (!self->gdk_context || !self->gst_display || !self->wrapped_context);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (need_invoke) {
|
||||
if (!(! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||
retrieve_gl_context_on_main, self)))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!retrieve_gst_context (self))
|
||||
return FALSE;
|
||||
|
||||
if (!GST_CLAPPER_IMPORTER_CLASS (parent_class)->prepare)
|
||||
return TRUE;
|
||||
|
||||
return GST_CLAPPER_IMPORTER_CLASS (parent_class)->prepare (importer);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_base_importer_share_data (GstClapperImporter *importer, GstClapperImporter *dest_importer)
|
||||
{
|
||||
GstClapperGLBaseImporter *self = GST_CLAPPER_GL_BASE_IMPORTER (importer);
|
||||
|
||||
if (GST_IS_CLAPPER_GL_BASE_IMPORTER (dest_importer)) {
|
||||
GstClapperGLBaseImporter *dest = GST_CLAPPER_GL_BASE_IMPORTER (dest_importer);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
GST_OBJECT_LOCK (dest);
|
||||
|
||||
/* Successfully prepared GL importer should have all three */
|
||||
if (self->gdk_context && self->gst_display && self->wrapped_context) {
|
||||
g_clear_object (&dest->gdk_context);
|
||||
dest->gdk_context = g_object_ref (self->gdk_context);
|
||||
|
||||
gst_clear_object (&dest->gst_display);
|
||||
dest->gst_display = gst_object_ref (self->gst_display);
|
||||
|
||||
gst_clear_object (&dest->wrapped_context);
|
||||
dest->wrapped_context = gst_object_ref (self->wrapped_context);
|
||||
}
|
||||
|
||||
/* This context is not required, we can create it ourselves
|
||||
* using gst_display and wrapped_context */
|
||||
if (self->gst_context) {
|
||||
gst_clear_object (&dest->gst_context);
|
||||
dest->gst_context = gst_object_ref (self->gst_context);
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (dest);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
|
||||
if (GST_CLAPPER_IMPORTER_CLASS (parent_class)->share_data)
|
||||
GST_CLAPPER_IMPORTER_CLASS (parent_class)->share_data (importer, dest_importer);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_gl_base_importer_handle_context_query (GstClapperImporter *importer,
|
||||
GstBaseSink *bsink, GstQuery *query)
|
||||
{
|
||||
GstClapperGLBaseImporter *self = GST_CLAPPER_GL_BASE_IMPORTER_CAST (importer);
|
||||
gboolean res;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
res = gst_gl_handle_context_query (GST_ELEMENT_CAST (bsink), query,
|
||||
self->gst_display, self->gst_context, self->wrapped_context);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstBufferPool *
|
||||
gst_clapper_gl_base_importer_create_pool (GstClapperImporter *importer, GstStructure **config)
|
||||
{
|
||||
GstClapperGLBaseImporter *self = GST_CLAPPER_GL_BASE_IMPORTER_CAST (importer);
|
||||
GstBufferPool *pool;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Creating new GL buffer pool");
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
pool = gst_gl_buffer_pool_new (self->gst_context);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
*config = gst_buffer_pool_get_config (pool);
|
||||
|
||||
gst_buffer_pool_config_add_option (*config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||
gst_buffer_pool_config_add_option (*config, GST_BUFFER_POOL_OPTION_GL_SYNC_META);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_base_importer_add_allocation_metas (GstClapperImporter *importer, GstQuery *query)
|
||||
{
|
||||
GstClapperGLBaseImporter *self = GST_CLAPPER_GL_BASE_IMPORTER_CAST (importer);
|
||||
|
||||
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);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
if (self->gst_context->gl_vtable->FenceSync)
|
||||
gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_gl_base_importer_gdk_context_realize (GstClapperGLBaseImporter *self, GdkGLContext *gdk_context)
|
||||
{
|
||||
GdkGLAPI allowed_apis;
|
||||
GError *error = NULL;
|
||||
const gchar *gl_env;
|
||||
gboolean success;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Realizing GdkGLContext with default implementation");
|
||||
|
||||
/* Use single "GST_GL_API" env to also influence Gdk GL selection */
|
||||
gl_env = g_getenv ("GST_GL_API");
|
||||
allowed_apis = (!gl_env || g_str_has_prefix (gl_env, "gles"))
|
||||
? GDK_GL_API_GLES
|
||||
: (g_str_has_prefix (gl_env, "opengl"))
|
||||
? GDK_GL_API_GL
|
||||
: GDK_GL_API_GL | GDK_GL_API_GLES;
|
||||
|
||||
gdk_gl_context_set_allowed_apis (gdk_context, allowed_apis);
|
||||
if (!(success = gdk_gl_context_realize (gdk_context, &error))) {
|
||||
GST_WARNING_OBJECT (self, "Could not realize Gdk context with %s: %s",
|
||||
(allowed_apis & GDK_GL_API_GL) ? "GL" : "GLES", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
if (!success && !gl_env) {
|
||||
gdk_gl_context_set_allowed_apis (gdk_context, GDK_GL_API_GL);
|
||||
if (!(success = gdk_gl_context_realize (gdk_context, &error))) {
|
||||
GST_WARNING_OBJECT (self, "Could not realize Gdk context with GL: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_base_importer_init (GstClapperGLBaseImporter *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_base_importer_finalize (GObject *object)
|
||||
{
|
||||
GstClapperGLBaseImporter *self = GST_CLAPPER_GL_BASE_IMPORTER_CAST (object);
|
||||
|
||||
g_clear_object (&self->gdk_context);
|
||||
|
||||
gst_clear_object (&self->gst_display);
|
||||
gst_clear_object (&self->wrapped_context);
|
||||
gst_clear_object (&self->gst_context);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_base_importer_class_init (GstClapperGLBaseImporterClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstClapperImporterClass *importer_class = (GstClapperImporterClass *) klass;
|
||||
GstClapperGLBaseImporterClass *gl_bi_class = (GstClapperGLBaseImporterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperglbaseimporter", 0,
|
||||
"Clapper GL Base Importer");
|
||||
|
||||
gobject_class->finalize = gst_clapper_gl_base_importer_finalize;
|
||||
|
||||
importer_class->prepare = gst_clapper_gl_base_importer_prepare;
|
||||
importer_class->share_data = gst_clapper_gl_base_importer_share_data;
|
||||
importer_class->handle_context_query = gst_clapper_gl_base_importer_handle_context_query;
|
||||
importer_class->create_pool = gst_clapper_gl_base_importer_create_pool;
|
||||
importer_class->add_allocation_metas = gst_clapper_gl_base_importer_add_allocation_metas;
|
||||
|
||||
gl_bi_class->gdk_context_realize = gst_clapper_gl_base_importer_gdk_context_realize;
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
gst_clapper_gl_base_importer_make_supported_gdk_gl_caps (void)
|
||||
{
|
||||
GstCaps *caps, *tmp;
|
||||
|
||||
tmp = gst_caps_from_string (
|
||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
|
||||
"{ " GST_GDK_GL_TEXTURE_FORMATS " }") ", "
|
||||
"texture-target = (string) { " GST_GL_TEXTURE_TARGET_2D_STR " }");
|
||||
|
||||
caps = gst_caps_copy (tmp);
|
||||
gst_caps_set_features_simple (caps, gst_caps_features_new (
|
||||
GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
|
||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, NULL));
|
||||
|
||||
gst_caps_append (caps, tmp);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
GStrv
|
||||
gst_clapper_gl_base_importer_make_gl_context_types (void)
|
||||
{
|
||||
GStrv context_types;
|
||||
GStrvBuilder *builder = g_strv_builder_new ();
|
||||
|
||||
g_strv_builder_add (builder, GST_GL_DISPLAY_CONTEXT_TYPE);
|
||||
g_strv_builder_add (builder, "gst.gl.app_context");
|
||||
g_strv_builder_add (builder, "gst.gl.local_context");
|
||||
|
||||
context_types = g_strv_builder_end (builder);
|
||||
g_strv_builder_unref (builder);
|
||||
|
||||
return context_types;
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gst_clapper_gl_base_importer_make_gl_texture (GstClapperGLBaseImporter *self,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GstGLSyncMeta *sync_meta;
|
||||
GstVideoFrame frame;
|
||||
|
||||
if (G_UNLIKELY (!gst_video_frame_map (&frame, v_info, buffer, GST_MAP_READ | GST_MAP_GL))) {
|
||||
GST_ERROR_OBJECT (self, "Could not map input buffer for reading");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
/* Must have context active here for both sync meta
|
||||
* and Gdk texture format auto-detection to work */
|
||||
gdk_gl_context_make_current (self->gdk_context);
|
||||
gst_gl_context_activate (self->wrapped_context, TRUE);
|
||||
|
||||
sync_meta = gst_buffer_get_gl_sync_meta (buffer);
|
||||
|
||||
/* Wait for all previous OpenGL commands to complete,
|
||||
* before we start using the input texture */
|
||||
if (sync_meta) {
|
||||
gst_gl_sync_meta_set_sync_point (sync_meta, self->gst_context);
|
||||
gst_gl_sync_meta_wait (sync_meta, self->wrapped_context);
|
||||
}
|
||||
|
||||
texture = gdk_gl_texture_new (
|
||||
self->gdk_context,
|
||||
*(guint *) GST_VIDEO_FRAME_PLANE_DATA (&frame, 0),
|
||||
GST_VIDEO_FRAME_WIDTH (&frame),
|
||||
GST_VIDEO_FRAME_HEIGHT (&frame),
|
||||
(GDestroyNotify) gst_buffer_unref,
|
||||
gst_buffer_ref (buffer));
|
||||
|
||||
gst_gl_context_activate (self->wrapped_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
gst_video_frame_unmap (&frame);
|
||||
|
||||
return texture;
|
||||
}
|
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* 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/gl/gl.h>
|
||||
|
||||
#include "gst/plugin/gstclapperimporter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_GL_BASE_IMPORTER (gst_clapper_gl_base_importer_get_type())
|
||||
#define GST_IS_CLAPPER_GL_BASE_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_GL_BASE_IMPORTER))
|
||||
#define GST_IS_CLAPPER_GL_BASE_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_GL_BASE_IMPORTER))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_GL_BASE_IMPORTER, GstClapperGLBaseImporterClass))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_GL_BASE_IMPORTER, GstClapperGLBaseImporter))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_GL_BASE_IMPORTER, GstClapperGLBaseImporterClass))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORTER_CAST(obj) ((GstClapperGLBaseImporter *)(obj))
|
||||
|
||||
#define GST_CLAPPER_GL_BASE_IMPORTER_HAVE_WAYLAND (GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11 (GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11))
|
||||
#define GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_GLX (GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11 && GST_GL_HAVE_PLATFORM_GLX)
|
||||
#define GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_EGL (GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11 && GST_GL_HAVE_PLATFORM_EGL)
|
||||
|
||||
typedef struct _GstClapperGLBaseImporter GstClapperGLBaseImporter;
|
||||
typedef struct _GstClapperGLBaseImporterClass GstClapperGLBaseImporterClass;
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperGLBaseImporter, gst_object_unref)
|
||||
#endif
|
||||
|
||||
struct _GstClapperGLBaseImporter
|
||||
{
|
||||
GstClapperImporter parent;
|
||||
|
||||
GdkGLContext *gdk_context;
|
||||
|
||||
GstGLDisplay *gst_display;
|
||||
GstGLContext *wrapped_context;
|
||||
GstGLContext *gst_context;
|
||||
};
|
||||
|
||||
struct _GstClapperGLBaseImporterClass
|
||||
{
|
||||
GstClapperImporterClass parent_class;
|
||||
|
||||
gboolean (* gdk_context_realize) (GstClapperGLBaseImporter *gl_bi,
|
||||
GdkGLContext *gdk_context);
|
||||
};
|
||||
|
||||
GType gst_clapper_gl_base_importer_get_type (void);
|
||||
|
||||
GstCaps * gst_clapper_gl_base_importer_make_supported_gdk_gl_caps (void);
|
||||
|
||||
GStrv gst_clapper_gl_base_importer_make_gl_context_types (void);
|
||||
|
||||
GdkTexture * gst_clapper_gl_base_importer_make_gl_texture (GstClapperGLBaseImporter *self, GstBuffer *buffer, GstVideoInfo *v_info);
|
||||
|
||||
G_END_DECLS
|
70
lib/gst/plugin/importers/gstclapperglimporter.c
vendored
70
lib/gst/plugin/importers/gstclapperglimporter.c
vendored
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* 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 "gstclapperglimporter.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_gl_importer_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_gl_importer_parent_class
|
||||
GST_CLAPPER_IMPORTER_DEFINE (GstClapperGLImporter, gst_clapper_gl_importer, GST_TYPE_CLAPPER_GL_BASE_IMPORTER);
|
||||
|
||||
static GdkTexture *
|
||||
gst_clapper_gl_importer_generate_texture (GstClapperImporter *importer,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (importer);
|
||||
|
||||
return gst_clapper_gl_base_importer_make_gl_texture (gl_bi, buffer, v_info);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_importer_init (GstClapperGLImporter *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_importer_class_init (GstClapperGLImporterClass *klass)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = (GstClapperImporterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperglimporter", 0,
|
||||
"Clapper GL Importer");
|
||||
|
||||
importer_class->generate_texture = gst_clapper_gl_importer_generate_texture;
|
||||
}
|
||||
|
||||
GstClapperImporter *
|
||||
make_importer (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CLAPPER_GL_IMPORTER, NULL);
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
make_caps (GstRank *rank, GStrv *context_types)
|
||||
{
|
||||
*rank = GST_RANK_SECONDARY;
|
||||
*context_types = gst_clapper_gl_base_importer_make_gl_context_types ();
|
||||
|
||||
return gst_clapper_gl_base_importer_make_supported_gdk_gl_caps ();
|
||||
}
|
646
lib/gst/plugin/importers/gstclappergluploader.c
vendored
646
lib/gst/plugin/importers/gstclappergluploader.c
vendored
@@ -1,646 +0,0 @@
|
||||
/*
|
||||
* 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 "gstclappergluploader.h"
|
||||
#include "gst/plugin/gstgtkutils.h"
|
||||
|
||||
#include <gst/gl/egl/gsteglimage.h>
|
||||
#include <gst/allocators/gstdmabuf.h>
|
||||
|
||||
static const GLfloat vertices[] = {
|
||||
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
|
||||
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
|
||||
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
|
||||
1.0f, -1.0f, 0.0f, 1.0f, 1.0f
|
||||
};
|
||||
static const GLushort indices[] = {
|
||||
0, 1, 2, 0, 2, 3
|
||||
};
|
||||
|
||||
/* GTK4 renders things upside down ¯\_(ツ)_/¯ */
|
||||
static const gfloat vertical_flip_matrix[] = {
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstClapperGLUploader *dmabuf_bi;
|
||||
GLuint id;
|
||||
guint width;
|
||||
guint height;
|
||||
} GstClapperDmabufTexData;
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_gl_uploader_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_gl_uploader_parent_class
|
||||
GST_CLAPPER_IMPORTER_DEFINE (GstClapperGLUploader, gst_clapper_gl_uploader, GST_TYPE_CLAPPER_GL_BASE_IMPORTER);
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_bind_buffer (GstClapperGLUploader *self)
|
||||
{
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (self);
|
||||
const GstGLFuncs *gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gl->BindBuffer (GL_ARRAY_BUFFER, self->vertex_buffer);
|
||||
|
||||
/* Load the vertex position */
|
||||
gl->VertexAttribPointer (self->attr_position, 3, GL_FLOAT, GL_FALSE,
|
||||
5 * sizeof (GLfloat), (void *) 0);
|
||||
|
||||
/* Load the texture coordinate */
|
||||
gl->VertexAttribPointer (self->attr_texture, 2, GL_FLOAT, GL_FALSE,
|
||||
5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
|
||||
|
||||
gl->EnableVertexAttribArray (self->attr_position);
|
||||
gl->EnableVertexAttribArray (self->attr_texture);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_unbind_buffer (GstClapperGLUploader *self)
|
||||
{
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (self);
|
||||
const GstGLFuncs *gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
|
||||
gl->DisableVertexAttribArray (self->attr_position);
|
||||
gl->DisableVertexAttribArray (self->attr_texture);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
prepare_dmabuf_support_on_main (GstClapperGLUploader *self)
|
||||
{
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (self);
|
||||
GstGLSLStage *frag_stage, *vert_stage;
|
||||
GError *error = NULL;
|
||||
gchar *frag_str;
|
||||
const GstGLFuncs *gl;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
/* FIXME: Return if already prepared */
|
||||
|
||||
gdk_gl_context_make_current (gl_bi->gdk_context);
|
||||
gst_gl_context_activate (gl_bi->gst_context, TRUE);
|
||||
|
||||
if (!((vert_stage = gst_glsl_stage_new_with_string (gl_bi->gst_context,
|
||||
GL_VERTEX_SHADER, GST_GLSL_VERSION_NONE,
|
||||
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY,
|
||||
gst_gl_shader_string_vertex_mat4_vertex_transform)))) {
|
||||
gdk_gl_context_make_current (gl_bi->gdk_context);
|
||||
gst_gl_context_activate (gl_bi->gst_context, TRUE);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
GST_ERROR ("Failed to retrieve vertex shader for texture target");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
frag_str = gst_gl_shader_string_fragment_external_oes_get_default (
|
||||
gl_bi->gst_context, GST_GLSL_VERSION_NONE,
|
||||
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY);
|
||||
frag_stage = gst_glsl_stage_new_with_string (gl_bi->gst_context,
|
||||
GL_FRAGMENT_SHADER, GST_GLSL_VERSION_NONE,
|
||||
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, frag_str);
|
||||
|
||||
g_free (frag_str);
|
||||
|
||||
if (!frag_stage) {
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
GST_ERROR ("Failed to retrieve fragment shader for texture target");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!((self->shader = gst_gl_shader_new_link_with_stages (gl_bi->gst_context,
|
||||
&error, vert_stage, frag_stage, NULL)))) {
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
GST_ERROR ("Failed to initialize shader: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->attr_position =
|
||||
gst_gl_shader_get_attribute_location (self->shader, "a_position");
|
||||
self->attr_texture =
|
||||
gst_gl_shader_get_attribute_location (self->shader, "a_texcoord");
|
||||
|
||||
gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
if (gl->GenVertexArrays) {
|
||||
gl->GenVertexArrays (1, &self->vao);
|
||||
gl->BindVertexArray (self->vao);
|
||||
}
|
||||
|
||||
gl->GenBuffers (1, &self->vertex_buffer);
|
||||
gl->BindBuffer (GL_ARRAY_BUFFER, self->vertex_buffer);
|
||||
gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices, GL_STATIC_DRAW);
|
||||
|
||||
if (gl->GenVertexArrays) {
|
||||
gst_clapper_gl_uploader_bind_buffer (self);
|
||||
gl->BindVertexArray (0);
|
||||
}
|
||||
|
||||
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
|
||||
//self->prepared = TRUE;
|
||||
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_tex_data_free (GstClapperDmabufTexData *tex_data)
|
||||
{
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (tex_data->dmabuf_bi);
|
||||
|
||||
if (G_LIKELY (tex_data->id > 0)) {
|
||||
const GstGLFuncs *gl;
|
||||
|
||||
GST_OBJECT_LOCK (gl_bi);
|
||||
|
||||
gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gdk_gl_context_make_current (gl_bi->gdk_context);
|
||||
gst_gl_context_activate (gl_bi->gst_context, TRUE);
|
||||
|
||||
gl->DeleteTextures (1, &tex_data->id);
|
||||
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_OBJECT_UNLOCK (gl_bi);
|
||||
}
|
||||
|
||||
gst_object_unref (tex_data->dmabuf_bi);
|
||||
g_slice_free (GstClapperDmabufTexData, tex_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_dmabuf_into_texture (GstClapperGLUploader *self, gint *fds, GstVideoInfo *v_info,
|
||||
gsize *offsets, GstClapperDmabufTexData *tex_data)
|
||||
{
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (self);
|
||||
GstEGLImage *image;
|
||||
const GstGLFuncs *gl;
|
||||
|
||||
image = gst_egl_image_from_dmabuf_direct_target (gl_bi->gst_context,
|
||||
fds, offsets, v_info, self->gst_tex_target);
|
||||
|
||||
/* If HW colorspace conversion failed and there is only one
|
||||
* plane, we can just make it into single EGLImage as is */
|
||||
if (!image && GST_VIDEO_INFO_N_PLANES (v_info) == 1)
|
||||
image = gst_egl_image_from_dmabuf (gl_bi->gst_context,
|
||||
fds[0], v_info, 0, offsets[0]);
|
||||
|
||||
if (!image)
|
||||
return FALSE;
|
||||
|
||||
gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gl->GenTextures (1, &tex_data->id);
|
||||
tex_data->width = GST_VIDEO_INFO_WIDTH (v_info);
|
||||
tex_data->height = GST_VIDEO_INFO_HEIGHT (v_info);
|
||||
|
||||
gl->BindTexture (self->gl_tex_target, tex_data->id);
|
||||
|
||||
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
gl->EGLImageTargetTexture2D (self->gl_tex_target, gst_egl_image_get_image (image));
|
||||
|
||||
gl->BindTexture (GL_TEXTURE_2D, 0);
|
||||
gst_egl_image_unref (image);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_oes_texture_into_2d (GstClapperGLUploader *self, GstClapperDmabufTexData *tex_data)
|
||||
{
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (self);
|
||||
GLuint framebuffer, tex_id;
|
||||
GLenum status;
|
||||
const GstGLFuncs *gl;
|
||||
|
||||
gl = gl_bi->gst_context->gl_vtable;
|
||||
|
||||
gl->GenFramebuffers (1, &framebuffer);
|
||||
gl->BindFramebuffer (GL_FRAMEBUFFER, framebuffer);
|
||||
|
||||
gl->GenTextures (1, &tex_id);
|
||||
gl->BindTexture (GL_TEXTURE_2D, tex_id);
|
||||
|
||||
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, tex_data->width, tex_data->height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, tex_id, 0);
|
||||
|
||||
status = gl->CheckFramebufferStatus (GL_FRAMEBUFFER);
|
||||
if (G_UNLIKELY (status != GL_FRAMEBUFFER_COMPLETE)) {
|
||||
GST_ERROR ("Invalid framebuffer status: %u", status);
|
||||
|
||||
gl->BindTexture (GL_TEXTURE_2D, 0);
|
||||
gl->DeleteTextures (1, &tex_id);
|
||||
|
||||
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
|
||||
gl->DeleteFramebuffers (1, &framebuffer);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gl->Viewport (0, 0, tex_data->width, tex_data->height);
|
||||
|
||||
gst_gl_shader_use (self->shader);
|
||||
|
||||
if (gl->BindVertexArray)
|
||||
gl->BindVertexArray (self->vao);
|
||||
|
||||
gst_clapper_gl_uploader_bind_buffer (self);
|
||||
|
||||
gl->ActiveTexture (GL_TEXTURE0);
|
||||
gl->BindTexture (self->gl_tex_target, tex_data->id);
|
||||
|
||||
gst_gl_shader_set_uniform_1i (self->shader, "tex", 0);
|
||||
gst_gl_shader_set_uniform_matrix_4fv (self->shader,
|
||||
"u_transformation", 1, FALSE, vertical_flip_matrix);
|
||||
|
||||
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
|
||||
|
||||
if (gl->BindVertexArray)
|
||||
gl->BindVertexArray (0);
|
||||
else
|
||||
gst_clapper_gl_uploader_unbind_buffer (self);
|
||||
|
||||
gl->BindTexture (self->gl_tex_target, 0);
|
||||
|
||||
/* Replace External OES texture with newly created 2D */
|
||||
gl->DeleteTextures (1, &tex_data->id);
|
||||
tex_data->id = tex_id;
|
||||
|
||||
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
|
||||
gl->DeleteFramebuffers (1, &framebuffer);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
dmabuf_into_gdk_texture (GstClapperGLUploader *self, GstVideoInfo *v_info, gint *fds, gsize *offsets)
|
||||
{
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (self);
|
||||
GdkTexture *texture = NULL;
|
||||
GstClapperDmabufTexData *tex_data;
|
||||
|
||||
tex_data = g_slice_new (GstClapperDmabufTexData);
|
||||
tex_data->dmabuf_bi = gst_object_ref (self);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
gdk_gl_context_make_current (gl_bi->gdk_context);
|
||||
gst_gl_context_activate (gl_bi->gst_context, TRUE);
|
||||
|
||||
if (!_dmabuf_into_texture (self, fds, v_info, offsets, tex_data))
|
||||
goto finish;
|
||||
|
||||
/* GTK4 does not support External OES textures.
|
||||
* Make it into 2D using framebuffer + shader */
|
||||
if (self->gst_tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
|
||||
if (G_UNLIKELY (!_oes_texture_into_2d (self, tex_data)))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
texture = gdk_gl_texture_new (gl_bi->gdk_context,
|
||||
tex_data->id,
|
||||
tex_data->width,
|
||||
tex_data->height,
|
||||
(GDestroyNotify) _tex_data_free,
|
||||
tex_data);
|
||||
|
||||
finish:
|
||||
gst_gl_context_activate (gl_bi->gst_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
verify_dmabuf_memory (GstBuffer *buffer, GstVideoInfo *info, gint *fds, gsize *offsets)
|
||||
{
|
||||
guint i, n_planes = GST_VIDEO_INFO_N_PLANES (info);
|
||||
|
||||
for (i = 0; i < n_planes; i++) {
|
||||
GstMemory *memory;
|
||||
gsize plane_size, mem_skip;
|
||||
guint mem_idx, length;
|
||||
|
||||
plane_size = gst_gl_get_plane_data_size (info, NULL, i);
|
||||
|
||||
if (!gst_buffer_find_memory (buffer,
|
||||
GST_VIDEO_INFO_PLANE_OFFSET (info, i),
|
||||
plane_size, &mem_idx, &length, &mem_skip)) {
|
||||
GST_DEBUG ("Could not find memory %u", i);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* We cannot have more then one DMABuf per plane */
|
||||
if (length != 1) {
|
||||
GST_DEBUG ("Data for plane %u spans %u memories", i, length);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memory = gst_buffer_peek_memory (buffer, mem_idx);
|
||||
|
||||
offsets[i] = memory->offset + mem_skip;
|
||||
fds[i] = gst_dmabuf_memory_get_fd (memory);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_update_elements_caps_locked (GstClapperGLUploader *self, GstCaps *upload_sink_caps)
|
||||
{
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (self);
|
||||
GstCaps *upload_src_caps, *color_sink_caps, *color_src_caps, *gdk_sink_caps;
|
||||
|
||||
GST_INFO_OBJECT (self, "Input caps: %" GST_PTR_FORMAT, upload_sink_caps);
|
||||
|
||||
upload_src_caps = gst_gl_upload_transform_caps (self->upload, gl_bi->gst_context,
|
||||
GST_PAD_SINK, upload_sink_caps, NULL);
|
||||
upload_src_caps = gst_caps_fixate (upload_src_caps);
|
||||
|
||||
GST_INFO_OBJECT (self, "GLUpload caps: %" GST_PTR_FORMAT, upload_src_caps);
|
||||
gst_gl_upload_set_caps (self->upload, upload_sink_caps, upload_src_caps);
|
||||
|
||||
gdk_sink_caps = gst_clapper_gl_base_importer_make_supported_gdk_gl_caps ();
|
||||
color_sink_caps = gst_gl_color_convert_transform_caps (gl_bi->gst_context,
|
||||
GST_PAD_SRC, upload_src_caps, gdk_sink_caps);
|
||||
gst_caps_unref (gdk_sink_caps);
|
||||
|
||||
/* Second caps arg is transfer-full */
|
||||
color_src_caps = gst_gl_color_convert_fixate_caps (gl_bi->gst_context,
|
||||
GST_PAD_SINK, upload_src_caps, color_sink_caps);
|
||||
|
||||
GST_INFO_OBJECT (self, "GLColorConvert caps: %" GST_PTR_FORMAT, color_src_caps);
|
||||
gst_gl_color_convert_set_caps (self->color_convert, upload_src_caps, color_src_caps);
|
||||
|
||||
self->has_pending_v_info = gst_video_info_from_caps (&self->pending_v_info, color_src_caps);
|
||||
|
||||
gst_caps_unref (upload_src_caps);
|
||||
gst_caps_unref (color_src_caps);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_set_caps (GstClapperImporter *importer, GstCaps *caps)
|
||||
{
|
||||
GstClapperGLUploader *self = GST_CLAPPER_GL_UPLOADER_CAST (importer);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
_update_elements_caps_locked (self, caps);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
|
||||
static void
|
||||
_uploader_reconfigure_locked (GstClapperGLUploader *self)
|
||||
{
|
||||
GstCaps *in_caps = NULL;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Reconfiguring upload");
|
||||
|
||||
gst_gl_upload_get_caps (self->upload, &in_caps, NULL);
|
||||
|
||||
if (G_LIKELY (in_caps)) {
|
||||
_update_elements_caps_locked (self, in_caps);
|
||||
gst_caps_unref (in_caps);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_gl_uploader_prepare (GstClapperImporter *importer)
|
||||
{
|
||||
gboolean res = GST_CLAPPER_IMPORTER_CLASS (parent_class)->prepare (importer);
|
||||
|
||||
if (res) {
|
||||
GstClapperGLUploader *self = GST_CLAPPER_GL_UPLOADER_CAST (importer);
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (importer);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
if (!self->upload)
|
||||
self->upload = gst_gl_upload_new (gl_bi->gst_context);
|
||||
if (!self->color_convert)
|
||||
self->color_convert = gst_gl_color_convert_new (gl_bi->gst_context);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (!(! !gst_gtk_invoke_on_main (
|
||||
(GThreadFunc) (GCallback) prepare_dmabuf_support_on_main, self))) {
|
||||
GST_WARNING_OBJECT (self, "Could not prepare DMABuf support");
|
||||
|
||||
/* FIXME: Continue to allow using glupload/cc as fallback */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
_upload_perform_locked (GstClapperGLUploader *self, GstBuffer *buffer)
|
||||
{
|
||||
GstBuffer *upload_buf = NULL;
|
||||
GstGLUploadReturn ret;
|
||||
|
||||
ret = gst_gl_upload_perform_with_buffer (self->upload, buffer, &upload_buf);
|
||||
|
||||
if (G_UNLIKELY (ret != GST_GL_UPLOAD_DONE)) {
|
||||
switch (ret) {
|
||||
case GST_GL_UPLOAD_RECONFIGURE:
|
||||
_uploader_reconfigure_locked (self);
|
||||
/* Retry with the same buffer after reconfiguring */
|
||||
return _upload_perform_locked (self, buffer);
|
||||
default:
|
||||
GST_ERROR_OBJECT (self, "Could not upload input buffer, returned: %i", ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return upload_buf;
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
gst_clapper_gl_uploader_generate_texture (GstClapperImporter *importer,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GstClapperGLUploader *self = GST_CLAPPER_GL_UPLOADER_CAST (importer);
|
||||
GstClapperGLBaseImporter *gl_bi = GST_CLAPPER_GL_BASE_IMPORTER_CAST (importer);
|
||||
GstBuffer *upload_buf, *color_buf;
|
||||
GstVideoMeta *meta;
|
||||
GdkTexture *texture;
|
||||
|
||||
/* XXX: We both upload and perform color conversion here, thus we skip
|
||||
* upload for buffers that are not going to be shown and gain more free
|
||||
* CPU time to prepare the next one. Improves performance on weak HW. */
|
||||
|
||||
if ((meta = gst_buffer_get_video_meta (buffer))) {
|
||||
guint i;
|
||||
|
||||
GST_VIDEO_INFO_WIDTH (v_info) = meta->width;
|
||||
GST_VIDEO_INFO_HEIGHT (v_info) = meta->height;
|
||||
|
||||
for (i = 0; i < meta->n_planes; i++) {
|
||||
GST_VIDEO_INFO_PLANE_OFFSET (v_info, i) = meta->offset[i];
|
||||
GST_VIDEO_INFO_PLANE_STRIDE (v_info, i) = meta->stride[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: if can do dmabuf and seems like we have dmabuf here */
|
||||
{
|
||||
gint fds[GST_VIDEO_MAX_PLANES];
|
||||
gsize offsets[GST_VIDEO_MAX_PLANES];
|
||||
|
||||
if (verify_dmabuf_memory (buffer, v_info, fds, offsets)) {
|
||||
if ((texture = dmabuf_into_gdk_texture (self, v_info, fds, offsets))) {
|
||||
GST_TRACE_OBJECT (self, "Got texture from DMABuf, skipping upload of %" GST_PTR_FORMAT, buffer);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Uploading %" GST_PTR_FORMAT, buffer);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
upload_buf = _upload_perform_locked (self, buffer);
|
||||
|
||||
if (G_UNLIKELY (!upload_buf)) {
|
||||
GST_ERROR_OBJECT (self, "Could not perform upload on input buffer");
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
GST_LOG_OBJECT (self, "Uploaded into %" GST_PTR_FORMAT, upload_buf);
|
||||
|
||||
color_buf = gst_gl_color_convert_perform (self->color_convert, upload_buf);
|
||||
gst_buffer_unref (upload_buf);
|
||||
|
||||
/* Use video info associated with converted buffer */
|
||||
if (self->has_pending_v_info) {
|
||||
self->v_info = self->pending_v_info;
|
||||
self->has_pending_v_info = FALSE;
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (G_UNLIKELY (!color_buf)) {
|
||||
GST_ERROR_OBJECT (self, "Could not perform color conversion on input buffer");
|
||||
return NULL;
|
||||
}
|
||||
GST_LOG_OBJECT (self, "Color converted into %" GST_PTR_FORMAT, color_buf);
|
||||
|
||||
texture = gst_clapper_gl_base_importer_make_gl_texture (gl_bi, color_buf, &self->v_info);
|
||||
gst_buffer_unref (color_buf);
|
||||
|
||||
done:
|
||||
return texture;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_init (GstClapperGLUploader *self)
|
||||
{
|
||||
gst_video_info_init (&self->pending_v_info);
|
||||
gst_video_info_init (&self->v_info);
|
||||
|
||||
self->gst_tex_target = GST_GL_TEXTURE_TARGET_EXTERNAL_OES;
|
||||
self->gl_tex_target = gst_gl_texture_target_to_gl (self->gst_tex_target);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_finalize (GObject *object)
|
||||
{
|
||||
GstClapperGLUploader *self = GST_CLAPPER_GL_UPLOADER_CAST (object);
|
||||
|
||||
gst_clear_object (&self->upload);
|
||||
gst_clear_object (&self->color_convert);
|
||||
|
||||
gst_clear_object (&self->shader);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_class_init (GstClapperGLUploaderClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstClapperImporterClass *importer_class = (GstClapperImporterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergluploader", 0,
|
||||
"Clapper GL Uploader");
|
||||
|
||||
gobject_class->finalize = gst_clapper_gl_uploader_finalize;
|
||||
|
||||
importer_class->prepare = gst_clapper_gl_uploader_prepare;
|
||||
importer_class->set_caps = gst_clapper_gl_uploader_set_caps;
|
||||
importer_class->generate_texture = gst_clapper_gl_uploader_generate_texture;
|
||||
}
|
||||
|
||||
GstClapperImporter *
|
||||
make_importer (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CLAPPER_GL_UPLOADER, NULL);
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
make_caps (GstRank *rank, GStrv *context_types)
|
||||
{
|
||||
*rank = GST_RANK_MARGINAL + 1;
|
||||
*context_types = gst_clapper_gl_base_importer_make_gl_context_types ();
|
||||
|
||||
return gst_gl_upload_get_input_template_caps ();
|
||||
}
|
55
lib/gst/plugin/importers/gstclappergluploader.h
vendored
55
lib/gst/plugin/importers/gstclappergluploader.h
vendored
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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 "gstclapperglbaseimporter.h"
|
||||
|
||||
#include <gst/gl/gstglfuncs.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_GL_UPLOADER (gst_clapper_gl_uploader_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperGLUploader, gst_clapper_gl_uploader, GST, CLAPPER_GL_UPLOADER, GstClapperGLBaseImporter)
|
||||
|
||||
#define GST_CLAPPER_GL_UPLOADER_CAST(obj) ((GstClapperGLUploader *)(obj))
|
||||
|
||||
struct _GstClapperGLUploader
|
||||
{
|
||||
GstClapperGLBaseImporter parent;
|
||||
|
||||
GstGLUpload *upload;
|
||||
GstGLColorConvert *color_convert;
|
||||
|
||||
GstVideoInfo pending_v_info, v_info;
|
||||
gboolean has_pending_v_info;
|
||||
|
||||
/* DMABuf fast-path */
|
||||
GstGLTextureTarget gst_tex_target;
|
||||
guint gl_tex_target;
|
||||
|
||||
GstGLShader *shader;
|
||||
|
||||
GLuint vao;
|
||||
GLuint vertex_buffer;
|
||||
GLint attr_position;
|
||||
GLint attr_texture;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
111
lib/gst/plugin/importers/gstclapperrawimporter.c
vendored
111
lib/gst/plugin/importers/gstclapperrawimporter.c
vendored
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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 "gstclapperrawimporter.h"
|
||||
#include "gst/plugin/gstgtkutils.h"
|
||||
#include "gst/plugin/gstgdkformats.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_raw_importer_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_raw_importer_parent_class
|
||||
GST_CLAPPER_IMPORTER_DEFINE (GstClapperRawImporter, gst_clapper_raw_importer, GST_TYPE_CLAPPER_IMPORTER);
|
||||
|
||||
static GstBufferPool *
|
||||
gst_clapper_raw_importer_create_pool (GstClapperImporter *importer, GstStructure **config)
|
||||
{
|
||||
GstClapperRawImporter *self = GST_CLAPPER_RAW_IMPORTER_CAST (importer);
|
||||
GstBufferPool *pool;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Creating new buffer pool");
|
||||
|
||||
pool = gst_video_buffer_pool_new ();
|
||||
*config = gst_buffer_pool_get_config (pool);
|
||||
|
||||
gst_buffer_pool_config_add_option (*config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_raw_importer_add_allocation_metas (GstClapperImporter *importer, GstQuery *query)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
gst_clapper_raw_importer_generate_texture (GstClapperImporter *importer,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GstVideoFrame frame;
|
||||
|
||||
if (G_UNLIKELY (!gst_video_frame_map (&frame, v_info, buffer, GST_MAP_READ))) {
|
||||
GST_ERROR_OBJECT (importer, "Could not map input buffer for reading");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
texture = gst_video_frame_into_gdk_texture (&frame);
|
||||
gst_video_frame_unmap (&frame);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_raw_importer_init (GstClapperRawImporter *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_raw_importer_class_init (GstClapperRawImporterClass *klass)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = (GstClapperImporterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperrawimporter", 0,
|
||||
"Clapper RAW Importer");
|
||||
|
||||
importer_class->create_pool = gst_clapper_raw_importer_create_pool;
|
||||
importer_class->add_allocation_metas = gst_clapper_raw_importer_add_allocation_metas;
|
||||
importer_class->generate_texture = gst_clapper_raw_importer_generate_texture;
|
||||
}
|
||||
|
||||
GstClapperImporter *
|
||||
make_importer (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CLAPPER_RAW_IMPORTER, NULL);
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
make_caps (GstRank *rank, GStrv *context_types)
|
||||
{
|
||||
*rank = GST_RANK_MARGINAL;
|
||||
|
||||
return gst_caps_from_string (
|
||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY ", "
|
||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
|
||||
"{ " GST_GDK_MEMORY_FORMATS " }")
|
||||
"; "
|
||||
GST_VIDEO_CAPS_MAKE (
|
||||
"{ " GST_GDK_MEMORY_FORMATS " }"));
|
||||
}
|
146
lib/gst/plugin/importers/meson.build
vendored
146
lib/gst/plugin/importers/meson.build
vendored
@@ -1,146 +0,0 @@
|
||||
gst_clapper_gl_base_importer_dep = dependency('', required: false)
|
||||
|
||||
all_importers = [
|
||||
'glimporter',
|
||||
'gluploader',
|
||||
'rawimporter',
|
||||
]
|
||||
|
||||
build_glbase = (
|
||||
not get_option('glimporter').disabled()
|
||||
or not get_option('gluploader').disabled()
|
||||
)
|
||||
gl_support_required = (
|
||||
get_option('glimporter').enabled()
|
||||
or get_option('gluploader').enabled()
|
||||
)
|
||||
|
||||
# We cannot build any importers without sink that they depend on
|
||||
if not gst_clapper_sink_dep.found()
|
||||
foreach imp : all_importers
|
||||
if get_option(imp).enabled()
|
||||
error('"@0@" option was enabled, but it requires building gstreamer plugin'.format(imp))
|
||||
endif
|
||||
endforeach
|
||||
endif
|
||||
|
||||
gst_plugin_gl_base_deps = [gst_clapper_sink_dep, gstgl_dep, gstglproto_dep]
|
||||
have_gtk_gl_windowing = false
|
||||
|
||||
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()
|
||||
gst_plugin_gl_base_deps += gtk_x11_dep
|
||||
if gst_gl_have_platform_glx
|
||||
gst_plugin_gl_base_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()
|
||||
gst_plugin_gl_base_deps += [gtk_wayland_dep, gstglwayland_dep]
|
||||
have_gtk_gl_windowing = true
|
||||
endif
|
||||
endif
|
||||
|
||||
if gl_support_required and not have_gtk_gl_windowing
|
||||
error('GL-based importer was enabled, but support for current GL windowing is missing')
|
||||
endif
|
||||
|
||||
if gst_gl_have_platform_egl
|
||||
gst_plugin_gl_base_deps += gstglegl_dep
|
||||
endif
|
||||
|
||||
foreach dep : gst_plugin_gl_base_deps
|
||||
if not dep.found()
|
||||
if gl_support_required
|
||||
error('GL-based importer was enabled, but required dependencies were not found')
|
||||
endif
|
||||
build_glbase = false
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if build_glbase
|
||||
gst_clapper_gl_base_importer_dep = declare_dependency(
|
||||
link_with: library('gstclapperglbaseimporter',
|
||||
'gstclapperglbaseimporter.c',
|
||||
c_args: gst_clapper_plugin_args,
|
||||
include_directories: configinc,
|
||||
dependencies: gst_plugin_gl_base_deps,
|
||||
version: libversion,
|
||||
install: true,
|
||||
),
|
||||
include_directories: configinc,
|
||||
dependencies: gst_plugin_gl_base_deps,
|
||||
)
|
||||
endif
|
||||
|
||||
build_glimporter = (
|
||||
not get_option('glimporter').disabled()
|
||||
and gst_clapper_gl_base_importer_dep.found()
|
||||
)
|
||||
|
||||
if build_glimporter
|
||||
library(
|
||||
'gstclapperglimporter',
|
||||
'gstclapperglimporter.c',
|
||||
dependencies: gst_clapper_gl_base_importer_dep,
|
||||
include_directories: configinc,
|
||||
c_args: gst_clapper_plugin_args,
|
||||
install: true,
|
||||
install_dir: gst_clapper_importers_libdir,
|
||||
)
|
||||
endif
|
||||
|
||||
build_gluploader = (
|
||||
not get_option('gluploader').disabled()
|
||||
and gst_clapper_gl_base_importer_dep.found()
|
||||
)
|
||||
|
||||
gluploader_deps = [
|
||||
gst_clapper_gl_base_importer_dep,
|
||||
gstallocators_dep,
|
||||
]
|
||||
|
||||
foreach dep : gluploader_deps
|
||||
if not dep.found()
|
||||
if get_option('gluploader').enabled()
|
||||
error('GL uploader was enabled, but required dependencies were not found')
|
||||
endif
|
||||
build_gluploader = false
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if build_gluploader
|
||||
library(
|
||||
'gstclappergluploader',
|
||||
'gstclappergluploader.c',
|
||||
dependencies: gluploader_deps,
|
||||
include_directories: configinc,
|
||||
c_args: gst_clapper_plugin_args,
|
||||
install: true,
|
||||
install_dir: gst_clapper_importers_libdir,
|
||||
)
|
||||
endif
|
||||
|
||||
# No need to auto build rawimporter if we are building gluploader
|
||||
build_rawimporter = (
|
||||
not get_option('rawimporter').disabled()
|
||||
and (not build_gluploader or get_option('rawimporter').enabled())
|
||||
and gst_clapper_sink_dep.found()
|
||||
)
|
||||
|
||||
if build_rawimporter
|
||||
library(
|
||||
'gstclapperrawimporter',
|
||||
'gstclapperrawimporter.c',
|
||||
dependencies: gst_clapper_sink_dep,
|
||||
include_directories: configinc,
|
||||
c_args: gst_clapper_plugin_args,
|
||||
install: true,
|
||||
install_dir: gst_clapper_importers_libdir,
|
||||
)
|
||||
endif
|
86
lib/gst/plugin/meson.build
vendored
86
lib/gst/plugin/meson.build
vendored
@@ -4,60 +4,68 @@ gst_clapper_plugin_args = [
|
||||
'-DHAVE_CONFIG_H',
|
||||
'-DGST_USE_UNSTABLE_API',
|
||||
]
|
||||
|
||||
gst_clapper_sink_dep = dependency('', required: false)
|
||||
gtk4_dep = dependency('gtk4', version: '>=4.6.0', required: false)
|
||||
|
||||
gmodule_dep = dependency('gmodule-2.0',
|
||||
version: glib_req,
|
||||
required: false,
|
||||
fallback: ['glib', 'libgmodule_dep'],
|
||||
)
|
||||
|
||||
gst_clapper_plugin_deps = [
|
||||
gtk4_dep,
|
||||
gst_dep,
|
||||
gstbase_dep,
|
||||
gstvideo_dep,
|
||||
gmodule_dep,
|
||||
gstallocators_dep,
|
||||
]
|
||||
|
||||
build_gst_plugin = not get_option('gst-plugin').disabled()
|
||||
foreach dep : gst_clapper_plugin_deps
|
||||
if not dep.found()
|
||||
if get_option('gst-plugin').enabled()
|
||||
error('GStreamer plugin was enabled, but required dependencies were not found')
|
||||
endif
|
||||
build_gst_plugin = false
|
||||
endif
|
||||
endforeach
|
||||
|
||||
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',
|
||||
'gstclapperglbaseimport.c',
|
||||
'gstclapperdmabufbaseimport.c',
|
||||
'gstclapperimport.c',
|
||||
'gstclapperglimport.c',
|
||||
'gstclapperdmabufimport.c',
|
||||
'gstclappersink.c',
|
||||
'gstclapperpaintable.c',
|
||||
'gstgtkutils.c',
|
||||
'gstplugin.c',
|
||||
'gstclapperimporter.c',
|
||||
'gstclapperimporterloader.c',
|
||||
]
|
||||
|
||||
if build_gst_plugin
|
||||
gst_clapper_sink_dep = declare_dependency(
|
||||
link_with: library('gstclapper',
|
||||
gst_clapper_plugin_sources,
|
||||
c_args: gst_clapper_plugin_args,
|
||||
include_directories: configinc,
|
||||
dependencies: gst_clapper_plugin_deps,
|
||||
install: true,
|
||||
install_dir: gst_plugins_libdir,
|
||||
),
|
||||
include_directories: configinc,
|
||||
dependencies: gst_clapper_plugin_deps,
|
||||
)
|
||||
endif
|
||||
|
||||
subdir('importers')
|
||||
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,
|
||||
)
|
||||
|
40
lib/meson.build
vendored
40
lib/meson.build
vendored
@@ -1,4 +1,4 @@
|
||||
glib_req = '>= 2.68.0'
|
||||
glib_req = '>= 2.56.0'
|
||||
gst_req = '>= 1.20.0'
|
||||
|
||||
api_version = '1.0'
|
||||
@@ -42,6 +42,10 @@ endif
|
||||
# Symbol visibility
|
||||
if cc.get_id() == 'msvc'
|
||||
export_define = '__declspec(dllexport) extern'
|
||||
elif cc.has_argument('-fvisibility=hidden')
|
||||
add_project_arguments('-fvisibility=hidden', language: 'c')
|
||||
add_project_arguments('-fvisibility=hidden', language: 'cpp')
|
||||
export_define = 'extern __attribute__ ((visibility ("default")))'
|
||||
else
|
||||
export_define = 'extern'
|
||||
endif
|
||||
@@ -261,9 +265,37 @@ gir_init_section = ['--add-init-section=extern void gst_init(gint*,gchar**);' +
|
||||
'gst_init(NULL,NULL);', '--quiet'
|
||||
]
|
||||
|
||||
gst_clapper_plugin_libdir = join_paths(get_option('prefix'), libdir, 'clapper-@0@'.format(api_version), 'gst', 'plugin')
|
||||
gst_clapper_importers_libdir = join_paths(gst_clapper_plugin_libdir, 'importers')
|
||||
cdata.set_quoted('CLAPPER_SINK_IMPORTER_PATH', gst_clapper_importers_libdir)
|
||||
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')
|
||||
configure_file(output: 'config.h', configuration: cdata)
|
||||
|
@@ -19,7 +19,9 @@ datadir = join_paths(get_option('prefix'), get_option('datadir'))
|
||||
pkglibdir = join_paths(libdir, meson.project_name())
|
||||
pkgdatadir = join_paths(datadir, meson.project_name())
|
||||
|
||||
subdir('lib')
|
||||
if get_option('lib') or not get_option('gst-plugin').disabled()
|
||||
subdir('lib')
|
||||
endif
|
||||
|
||||
if get_option('player')
|
||||
subdir('bin')
|
||||
|
@@ -13,23 +13,6 @@ option('gst-plugin',
|
||||
value: 'enabled',
|
||||
description: 'Build GStreamer plugin (includes GTK video sink element)'
|
||||
)
|
||||
|
||||
option('glimporter',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Build GL memory importer for clappersink'
|
||||
)
|
||||
option('gluploader',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Build GL uploader for clappersink'
|
||||
)
|
||||
option('rawimporter',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Build RAW system memory importer for clappersink'
|
||||
)
|
||||
|
||||
option('devel-checks',
|
||||
type: 'boolean',
|
||||
value: false,
|
||||
|
@@ -21,6 +21,7 @@
|
||||
],
|
||||
"modules": [
|
||||
"flathub/shared-modules/gudev/gudev.json",
|
||||
"flathub/lib/pango.json",
|
||||
"flathub/lib/libsass.json",
|
||||
"flathub/lib/sassc.json",
|
||||
"flathub/lib/liba52.json",
|
||||
|
@@ -1 +1 @@
|
||||
ar ca cs de es eu fr hu it ja nl pl pt pt_BR ru sv tr zh_CN
|
||||
ca cs de es fr hu it nl pl pt pt_BR ru sv zh_CN
|
||||
|
218
po/ar.po
218
po/ar.po
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-09-14 16:35+0200\n"
|
||||
"PO-Revision-Date: 2022-03-26 13:37\n"
|
||||
"PO-Revision-Date: 2021-09-14 15:25\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Arabic\n"
|
||||
"Language: ar_SA\n"
|
||||
@@ -19,441 +19,441 @@ msgstr ""
|
||||
|
||||
#: ui/clapper.ui:6
|
||||
msgid "Open Files…"
|
||||
msgstr "فتح الملفات…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:10
|
||||
msgid "Open URI…"
|
||||
msgstr "فتح عنوان URL…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:16 ui/preferences-window.ui:4
|
||||
msgid "Preferences"
|
||||
msgstr "الإعدادات"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:20
|
||||
msgid "Shortcuts"
|
||||
msgstr "الاختصارات"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:26
|
||||
msgid "About Clapper"
|
||||
msgstr "حول Clapper"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:27
|
||||
msgid "Speed"
|
||||
msgstr "السرعة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:83
|
||||
#: ui/preferences-window.ui:215
|
||||
msgid "Normal"
|
||||
msgstr "عادي"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:10 ui/preferences-window.ui:12
|
||||
msgid "General"
|
||||
msgstr "عام"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:13
|
||||
msgid "Show shortcuts"
|
||||
msgstr "إظهار الاختصارات"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:19
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "تبديل ملء الشاشة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:20
|
||||
msgid "Double tap | Double click"
|
||||
msgstr "نقرة مزدوجة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:26
|
||||
msgid "Leave fullscreen"
|
||||
msgstr "مغادرة ملء الشاشة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:32
|
||||
msgid "Reveal OSD (fullscreen only)"
|
||||
msgstr "إظهار المعلومات (ملء الشاشة فقط)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:33
|
||||
msgid "Tap"
|
||||
msgstr "نقرة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:39
|
||||
msgid "Quit"
|
||||
msgstr "خروج"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:47
|
||||
msgid "Media"
|
||||
msgstr "الوسائط"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:50
|
||||
msgid "Open files"
|
||||
msgstr "افتح ملفًا"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:56 src/dialogs.js:137
|
||||
msgid "Open URI"
|
||||
msgstr "فتح عنوان URL"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:64
|
||||
msgid "Playlist"
|
||||
msgstr "قوائم التشغيل"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:67
|
||||
msgid "Next item"
|
||||
msgstr "المحتوى التالي"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:68
|
||||
msgid "Double tap (right side)"
|
||||
msgstr "نقر مزدوج (الجانب الأيمن)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:74
|
||||
msgid "Previous item"
|
||||
msgstr "المحتوى السابق"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:75
|
||||
msgid "Double tap (left side)"
|
||||
msgstr "نقر مزدوج (الجانب الأيسر)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:81
|
||||
msgid "Change repeat mode"
|
||||
msgstr "تغيير وضع التكرار"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:87
|
||||
msgid "Export to file"
|
||||
msgstr "التصدير إلى مِلَفّ"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:95 ui/preferences-window.ui:119
|
||||
msgid "Playback"
|
||||
msgstr "المشغل"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:98
|
||||
msgid "Toggle play"
|
||||
msgstr "بَدْءّ / إيقاف"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:99
|
||||
msgid "Long press | Right click"
|
||||
msgstr "اضغط مطولاً | انقر بزر الفائرة الأيمن"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:105
|
||||
msgid "Seek forward"
|
||||
msgstr "التقدم للأمام"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:106
|
||||
msgid "Swipe right | Scroll right"
|
||||
msgstr "مرر لليمين"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:112
|
||||
msgid "Seek backward"
|
||||
msgstr "الرجوع للوراء"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:113
|
||||
msgid "Swipe left | Scroll left"
|
||||
msgstr "مرر لليسار"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:119
|
||||
msgid "Volume up"
|
||||
msgstr "رفع مستوى الصوت"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:120
|
||||
msgid "Swipe up | Scroll up"
|
||||
msgstr "مرر لأعلى"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:126
|
||||
msgid "Volume down"
|
||||
msgstr "خفض مستوى الصوت"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:127
|
||||
msgid "Swipe down | Scroll down"
|
||||
msgstr "مرر لأسفل"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr "كتم الصوت"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
msgstr "الفصل التالي"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:145
|
||||
msgid "Previous chapter"
|
||||
msgstr "الفصل السابق"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:11
|
||||
msgid "Decoders"
|
||||
msgstr "برامج فك التشفير"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:18
|
||||
msgid "Return to the preferences"
|
||||
msgstr "العودة إلى الإعدادات"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:16
|
||||
msgid "Behavior"
|
||||
msgstr "السلوك"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:19
|
||||
msgid "Auto fullscreen"
|
||||
msgstr "ملء الشاشة تلقائياً"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:20
|
||||
msgid "Enter fullscreen when playlist is replaced except floating mode"
|
||||
msgstr "أدخل ملء الشاشة عند استبدال قائمة التشغيل باستثناء الوضع العائم"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:26
|
||||
msgid "Ask to resume recent media"
|
||||
msgstr "اطلب استئناف الوسائط السابقة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:32
|
||||
msgid "Float on all workspaces"
|
||||
msgstr "عائم في جميع مساحات العمل"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:33
|
||||
msgid "This option only works on GNOME"
|
||||
msgstr "هذا الخِيار يعمل فقط على GNOME"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:39
|
||||
msgid "After playback"
|
||||
msgstr "بعد انتهاء"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:44
|
||||
msgid "Do nothing"
|
||||
msgstr "لا تفعل شيئًا"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:45
|
||||
msgid "Freeze last frame"
|
||||
msgstr "تجميد الأخر لقطة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:46
|
||||
msgid "Close the app"
|
||||
msgstr "أغلق التطبيق"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:56
|
||||
msgid "Volume"
|
||||
msgstr "الصوت"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:59
|
||||
msgid "Custom initial value"
|
||||
msgstr "قيمة الافتراضية مخصصة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:60
|
||||
msgid "Set custom volume at startup instead of restoring it"
|
||||
msgstr "تعيين مستوى صوت مخصص عند بَدْء التشغيل بدلاً من إعادته"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:64
|
||||
msgid "Volume percentage"
|
||||
msgstr "حجم الصوت"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:75
|
||||
msgid "Seeking"
|
||||
msgstr "الوضع"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:78
|
||||
msgid "Mode"
|
||||
msgstr "النمط"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:84
|
||||
msgid "Accurate"
|
||||
msgstr "دَقيق"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:85
|
||||
msgid "Fast"
|
||||
msgstr "سريع"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:93
|
||||
msgid "Unit"
|
||||
msgstr "الوحدة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:98
|
||||
msgid "Second"
|
||||
msgstr "ثواني"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:99
|
||||
msgid "Minute"
|
||||
msgstr "دقائق"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:100
|
||||
msgid "Percentage"
|
||||
msgstr "النسبة المئوية"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:108
|
||||
msgid "Value"
|
||||
msgstr "القيمة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:123
|
||||
msgid "Audio"
|
||||
msgstr "الصوت"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:126
|
||||
msgid "Offset in milliseconds"
|
||||
msgstr "إزاحة بالمللي ثانية"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:133
|
||||
msgid "Only native audio formats"
|
||||
msgstr "تنسيقات الصوت الأصلية فقط"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:141
|
||||
msgid "Subtitles"
|
||||
msgstr "التَّرْجَمَةً"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:144
|
||||
msgid "Default font"
|
||||
msgstr "الخط الافتراضي"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:154
|
||||
msgid "Network"
|
||||
msgstr "الشبكة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:158
|
||||
msgid "Client"
|
||||
msgstr "العميل"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:161
|
||||
msgid "Progressive download buffering"
|
||||
msgstr "التخزين المؤقت للتنزيل بالتدريج"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:169
|
||||
msgid "Server"
|
||||
msgstr "الخادم"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:172
|
||||
msgid "Control player remotely"
|
||||
msgstr "التحكم بالوسائط عن بعد"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:176
|
||||
msgid "Listening port"
|
||||
msgstr "منفذ الاستماع"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:183
|
||||
msgid "Run web application in background"
|
||||
msgstr "تشغيل تطبيق الويب في الخلفية"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:184
|
||||
msgid "Requires GTK compiled with Broadway backend"
|
||||
msgstr "يتطلب GTK مجمعة مع Broadway backend"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:190
|
||||
msgid "Web application port"
|
||||
msgstr "منفذ تطبيق الويب"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:204
|
||||
msgid "Prefer adaptive streaming"
|
||||
msgstr "تفضيل البث التكيفي"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:210
|
||||
msgid "Max quality"
|
||||
msgstr "الأعلى جودة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:228
|
||||
msgid "Tweaks"
|
||||
msgstr "تعديلات"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:232
|
||||
msgid "Appearance"
|
||||
msgstr "إعدادات المظهر"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:235
|
||||
msgid "Dark theme"
|
||||
msgstr "المظهر الداكن"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:241
|
||||
msgid "Render window shadows"
|
||||
msgstr "عرض ظلال النافذة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:242
|
||||
msgid "Disable to increase performance when windowed"
|
||||
msgstr "تعطيل لزيادة الأداء عند وضع النافذة"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:253
|
||||
msgid "Plugin ranking"
|
||||
msgstr "أعدادات الإضافات"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:254
|
||||
msgid "Alter default ranks of GStreamer plugins"
|
||||
msgstr "تغيير الأعدادات الافتراضية للأضافات GStreamer"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:259
|
||||
msgid "Use playbin3"
|
||||
msgstr "استخدام playbin3"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:260 ui/preferences-window.ui:269
|
||||
msgid "Requires player restart"
|
||||
msgstr "يتطلب إعادة التشغيل"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:262 ui/preferences-window.ui:271
|
||||
msgid "Experimental"
|
||||
msgstr "تجريبية"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:268
|
||||
msgid "Use PipeWire for audio output"
|
||||
msgstr "استخدام PipeWire لإخراج الصوت"
|
||||
msgstr ""
|
||||
|
||||
#: src/buttons.js:201
|
||||
#, javascript-format
|
||||
msgid "Decoder: %s"
|
||||
msgstr "فك التشفير: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:152
|
||||
msgid "Enter or drop URI here"
|
||||
msgstr "أدخل أو الصق URI هنا"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:157
|
||||
msgid "Cancel"
|
||||
msgstr "إلغاء"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:158
|
||||
msgid "Open"
|
||||
msgstr "فتح"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:226
|
||||
msgid "Title"
|
||||
msgstr "العنوان"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:227
|
||||
msgid "Completed"
|
||||
msgstr "تم مشاهدة"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:235
|
||||
msgid "Resume playback?"
|
||||
msgstr "استئناف التشغيل؟"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:289
|
||||
#, javascript-format
|
||||
msgid "GTK version: %s"
|
||||
msgstr "إصدار GTK: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:290
|
||||
#, javascript-format
|
||||
msgid "Adwaita version: %s"
|
||||
msgstr "إصدار Adwaita: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:291
|
||||
#, javascript-format
|
||||
msgid "GStreamer version: %s"
|
||||
msgstr "إصدار GStreamer: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:292
|
||||
#, javascript-format
|
||||
msgid "GJS version: %s"
|
||||
msgstr "إصدار GJS: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:300
|
||||
msgid "A GNOME media player powered by GStreamer"
|
||||
msgstr "مشغل وسائط GNOME مدعوم من GStreamer"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
|
||||
#: src/dialogs.js:305
|
||||
msgid "translator-credits"
|
||||
msgstr "Yousef Fawaz"
|
||||
msgstr ""
|
||||
|
||||
#: src/revealers.js:170
|
||||
#, javascript-format
|
||||
msgid "Ends at: %s"
|
||||
msgstr "ينتهي في: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:227 src/widget.js:236 src/widget.js:242 src/widget.js:248
|
||||
msgid "Undetermined"
|
||||
msgstr "غير محدّد"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:243
|
||||
msgid "Channels"
|
||||
msgstr "قنوات"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:261
|
||||
msgid "Disabled"
|
||||
msgstr "مُعطّل"
|
||||
msgstr ""
|
||||
|
||||
|
218
po/eu.po
218
po/eu.po
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-09-14 16:35+0200\n"
|
||||
"PO-Revision-Date: 2022-03-25 16:45\n"
|
||||
"PO-Revision-Date: 2022-01-16 16:58\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Basque\n"
|
||||
"Language: eu_ES\n"
|
||||
@@ -19,441 +19,441 @@ msgstr ""
|
||||
|
||||
#: ui/clapper.ui:6
|
||||
msgid "Open Files…"
|
||||
msgstr "Artxiboak ireki…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:10
|
||||
msgid "Open URI…"
|
||||
msgstr "Ireki URI…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:16 ui/preferences-window.ui:4
|
||||
msgid "Preferences"
|
||||
msgstr "Hobespenak"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:20
|
||||
msgid "Shortcuts"
|
||||
msgstr "Lasterbideak"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:26
|
||||
msgid "About Clapper"
|
||||
msgstr "Clapperi buruz"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:27
|
||||
msgid "Speed"
|
||||
msgstr "Abiadura"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:83
|
||||
#: ui/preferences-window.ui:215
|
||||
msgid "Normal"
|
||||
msgstr "Normala"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:10 ui/preferences-window.ui:12
|
||||
msgid "General"
|
||||
msgstr "Orokorra"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:13
|
||||
msgid "Show shortcuts"
|
||||
msgstr "Erakutsi lasterbideak"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:19
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "Aktibatu pantaila osoa"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:20
|
||||
msgid "Double tap | Double click"
|
||||
msgstr "Ukitu bikoitza / Bikoitza klik"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:26
|
||||
msgid "Leave fullscreen"
|
||||
msgstr "Pantaila osoa utzi"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:32
|
||||
msgid "Reveal OSD (fullscreen only)"
|
||||
msgstr "OSD errebelatu (pantaila osoan bakarrik)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:33
|
||||
msgid "Tap"
|
||||
msgstr "Ukitu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:39
|
||||
msgid "Quit"
|
||||
msgstr "Irten"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:47
|
||||
msgid "Media"
|
||||
msgstr "Media"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:50
|
||||
msgid "Open files"
|
||||
msgstr "Artxiboak ireki"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:56 src/dialogs.js:137
|
||||
msgid "Open URI"
|
||||
msgstr "Ireki URI"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:64
|
||||
msgid "Playlist"
|
||||
msgstr "Erreprodukzio-zerrenda"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:67
|
||||
msgid "Next item"
|
||||
msgstr "Hurrengo elementua"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:68
|
||||
msgid "Double tap (right side)"
|
||||
msgstr "Ukitu bikoitza (eskuineko aldea)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:74
|
||||
msgid "Previous item"
|
||||
msgstr "Aurreko elementua"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:75
|
||||
msgid "Double tap (left side)"
|
||||
msgstr "Ukitu bikoitza (ezkerreko aldea)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:81
|
||||
msgid "Change repeat mode"
|
||||
msgstr "Errepikapen modua aldatu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:87
|
||||
msgid "Export to file"
|
||||
msgstr "Esportatu fitxategira"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:95 ui/preferences-window.ui:119
|
||||
msgid "Playback"
|
||||
msgstr "Erreprodukzioa"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:98
|
||||
msgid "Toggle play"
|
||||
msgstr "Erreprodukzioa aktibatu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:99
|
||||
msgid "Long press | Right click"
|
||||
msgstr "Pultsazio luzea / Eskuineko klik"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:105
|
||||
msgid "Seek forward"
|
||||
msgstr "Aurrerapena bilatu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:106
|
||||
msgid "Swipe right | Scroll right"
|
||||
msgstr "Eskuinera irristatu / Eskuinera joan"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:112
|
||||
msgid "Seek backward"
|
||||
msgstr "Atzerantz bilatu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:113
|
||||
msgid "Swipe left | Scroll left"
|
||||
msgstr "Ezkerrera irristatu / Ezkerrera mugitu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:119
|
||||
msgid "Volume up"
|
||||
msgstr "Bolumena igo"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:120
|
||||
msgid "Swipe up | Scroll up"
|
||||
msgstr "Gorantz irristatu / Gorantz mugitu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:126
|
||||
msgid "Volume down"
|
||||
msgstr "Bolumena jeitsi"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:127
|
||||
msgid "Swipe down | Scroll down"
|
||||
msgstr "Beherantz irristatu / Beherantz mugitu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr "Aktibatu isiltasuna"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
msgstr "Hurrengo kapitulua"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:145
|
||||
msgid "Previous chapter"
|
||||
msgstr "Aurreko kapitulua"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:11
|
||||
msgid "Decoders"
|
||||
msgstr "Deskodetzaileak"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:18
|
||||
msgid "Return to the preferences"
|
||||
msgstr "Itzuli ezarpenetara"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:16
|
||||
msgid "Behavior"
|
||||
msgstr "Portaera"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:19
|
||||
msgid "Auto fullscreen"
|
||||
msgstr "Pantaila osoa automatikoa"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:20
|
||||
msgid "Enter fullscreen when playlist is replaced except floating mode"
|
||||
msgstr "Pantaila osoan sartu erreprodukzio-zerrenda ordezten denean, flotatzeko modua izan ezik"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:26
|
||||
msgid "Ask to resume recent media"
|
||||
msgstr "Azkenaldiko media berriro ekiteko eskatzea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:32
|
||||
msgid "Float on all workspaces"
|
||||
msgstr "Laneko espazio guztietan flotatzea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:33
|
||||
msgid "This option only works on GNOME"
|
||||
msgstr "Aukera honek GNOMEn bakarrik funtzionatzen du"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:39
|
||||
msgid "After playback"
|
||||
msgstr "Erreprodukzioaren ondoren"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:44
|
||||
msgid "Do nothing"
|
||||
msgstr "Ez egin ezer"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:45
|
||||
msgid "Freeze last frame"
|
||||
msgstr "Izoztu azken fotograma"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:46
|
||||
msgid "Close the app"
|
||||
msgstr "Aplikazioa itxi"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:56
|
||||
msgid "Volume"
|
||||
msgstr "Bolumena"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:59
|
||||
msgid "Custom initial value"
|
||||
msgstr "Hasierako balio pertsonalizatua"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:60
|
||||
msgid "Set custom volume at startup instead of restoring it"
|
||||
msgstr "Hasieran bolumen pertsonalizatua ezartzea, lehengoratu beharrean"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:64
|
||||
msgid "Volume percentage"
|
||||
msgstr "Bolumenaren ehunekoa"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:75
|
||||
msgid "Seeking"
|
||||
msgstr "Bilaketa"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:78
|
||||
msgid "Mode"
|
||||
msgstr "Modua"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:84
|
||||
msgid "Accurate"
|
||||
msgstr "Zehatza"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:85
|
||||
msgid "Fast"
|
||||
msgstr "Azkarra"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:93
|
||||
msgid "Unit"
|
||||
msgstr "Unitatea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:98
|
||||
msgid "Second"
|
||||
msgstr "Segundu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:99
|
||||
msgid "Minute"
|
||||
msgstr "Minutu"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:100
|
||||
msgid "Percentage"
|
||||
msgstr "Ehunekoa"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:108
|
||||
msgid "Value"
|
||||
msgstr "Balioa"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:123
|
||||
msgid "Audio"
|
||||
msgstr "Audioa"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:126
|
||||
msgid "Offset in milliseconds"
|
||||
msgstr "Desplazamendua milisegundotan"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:133
|
||||
msgid "Only native audio formats"
|
||||
msgstr "Jatorrizko audio-formatuak bakarrik"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:141
|
||||
msgid "Subtitles"
|
||||
msgstr "Azpitituluak"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:144
|
||||
msgid "Default font"
|
||||
msgstr "Hizki lehenetsia"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:154
|
||||
msgid "Network"
|
||||
msgstr "Sarea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:158
|
||||
msgid "Client"
|
||||
msgstr "Bezeroa"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:161
|
||||
msgid "Progressive download buffering"
|
||||
msgstr "Deskargak pixkanaka bufferizatzea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:169
|
||||
msgid "Server"
|
||||
msgstr "Zerbitzaria"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:172
|
||||
msgid "Control player remotely"
|
||||
msgstr "Urrutiko erreproduzitzailea kontrolatzea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:176
|
||||
msgid "Listening port"
|
||||
msgstr "Entzuteko ataka"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:183
|
||||
msgid "Run web application in background"
|
||||
msgstr "Web-aplikazioa bigarren planoan exekutatzea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:184
|
||||
msgid "Requires GTK compiled with Broadway backend"
|
||||
msgstr "GTK Broadwayko backend-arekin konpilatzea eskatzen du"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:190
|
||||
msgid "Web application port"
|
||||
msgstr "Web aplikazioaren ataka"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:204
|
||||
msgid "Prefer adaptive streaming"
|
||||
msgstr "Transmisio moldagarria lehenestea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:210
|
||||
msgid "Max quality"
|
||||
msgstr "Gehieneko kalitatea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:228
|
||||
msgid "Tweaks"
|
||||
msgstr "Ukituak"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:232
|
||||
msgid "Appearance"
|
||||
msgstr "Itxura"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:235
|
||||
msgid "Dark theme"
|
||||
msgstr "Gai iluna"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:241
|
||||
msgid "Render window shadows"
|
||||
msgstr "Leihoetako itzalak errenderizatzea"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:242
|
||||
msgid "Disable to increase performance when windowed"
|
||||
msgstr "Desaktibatu leihoa erabiltzen denean errendimendua handitzeko"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:253
|
||||
msgid "Plugin ranking"
|
||||
msgstr "Pluginen sailkapena"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:254
|
||||
msgid "Alter default ranks of GStreamer plugins"
|
||||
msgstr "Aldatu GStreamer-en pluginen lehenetsitako mailak"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:259
|
||||
msgid "Use playbin3"
|
||||
msgstr "Playbin3 erabili"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:260 ui/preferences-window.ui:269
|
||||
msgid "Requires player restart"
|
||||
msgstr "Berrabiaraztea eskatzen du"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:262 ui/preferences-window.ui:271
|
||||
msgid "Experimental"
|
||||
msgstr "Esperimentala"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:268
|
||||
msgid "Use PipeWire for audio output"
|
||||
msgstr "Erabili PipeWire audio-irteerarako"
|
||||
msgstr ""
|
||||
|
||||
#: src/buttons.js:201
|
||||
#, javascript-format
|
||||
msgid "Decoder: %s"
|
||||
msgstr "Deskodetzailea: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:152
|
||||
msgid "Enter or drop URI here"
|
||||
msgstr "Sartu edo utzi URIa erortzen hemen"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:157
|
||||
msgid "Cancel"
|
||||
msgstr "Ezeztatu"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:158
|
||||
msgid "Open"
|
||||
msgstr "Ireki"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:226
|
||||
msgid "Title"
|
||||
msgstr "Titulua"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:227
|
||||
msgid "Completed"
|
||||
msgstr "Osatuta"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:235
|
||||
msgid "Resume playback?"
|
||||
msgstr "Jarraitu erreprodukzioarekin?"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:289
|
||||
#, javascript-format
|
||||
msgid "GTK version: %s"
|
||||
msgstr "GTK bertsioa: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:290
|
||||
#, javascript-format
|
||||
msgid "Adwaita version: %s"
|
||||
msgstr "Adwaitaren bertsioa: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:291
|
||||
#, javascript-format
|
||||
msgid "GStreamer version: %s"
|
||||
msgstr "GStreamer-en bertsioa: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:292
|
||||
#, javascript-format
|
||||
msgid "GJS version: %s"
|
||||
msgstr "GJS bertsioa: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:300
|
||||
msgid "A GNOME media player powered by GStreamer"
|
||||
msgstr "GNOMEren multimedia erreproduzitzaile bat GStreamer-ekin"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
|
||||
#: src/dialogs.js:305
|
||||
msgid "translator-credits"
|
||||
msgstr "Sergio Varela (@IngrownMink4)"
|
||||
msgstr ""
|
||||
|
||||
#: src/revealers.js:170
|
||||
#, javascript-format
|
||||
msgid "Ends at: %s"
|
||||
msgstr "Amaiera: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:227 src/widget.js:236 src/widget.js:242 src/widget.js:248
|
||||
msgid "Undetermined"
|
||||
msgstr "Zehaztugabea"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:243
|
||||
msgid "Channels"
|
||||
msgstr "Kanalak"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:261
|
||||
msgid "Disabled"
|
||||
msgstr "Desgaituta"
|
||||
msgstr ""
|
||||
|
||||
|
218
po/ja.po
218
po/ja.po
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-09-14 16:35+0200\n"
|
||||
"PO-Revision-Date: 2022-04-01 09:06\n"
|
||||
"PO-Revision-Date: 2021-09-14 15:25\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja_JP\n"
|
||||
@@ -19,441 +19,441 @@ msgstr ""
|
||||
|
||||
#: ui/clapper.ui:6
|
||||
msgid "Open Files…"
|
||||
msgstr "ファイルを開く…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:10
|
||||
msgid "Open URI…"
|
||||
msgstr "URIを開く…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:16 ui/preferences-window.ui:4
|
||||
msgid "Preferences"
|
||||
msgstr "設定"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:20
|
||||
msgid "Shortcuts"
|
||||
msgstr "ショートカット"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:26
|
||||
msgid "About Clapper"
|
||||
msgstr "Clapperについて"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:27
|
||||
msgid "Speed"
|
||||
msgstr "速度"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:83
|
||||
#: ui/preferences-window.ui:215
|
||||
msgid "Normal"
|
||||
msgstr "標準"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:10 ui/preferences-window.ui:12
|
||||
msgid "General"
|
||||
msgstr "一般"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:13
|
||||
msgid "Show shortcuts"
|
||||
msgstr "ショートカットを表示する"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:19
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "フルスクリーン切り替え"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:20
|
||||
msgid "Double tap | Double click"
|
||||
msgstr "ダブルタップ | ダブルクリック"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:26
|
||||
msgid "Leave fullscreen"
|
||||
msgstr "フルスクリーンを終了"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:32
|
||||
msgid "Reveal OSD (fullscreen only)"
|
||||
msgstr "OSDを表示 (全画面表示の場合のみ)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:33
|
||||
msgid "Tap"
|
||||
msgstr "タップ"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:39
|
||||
msgid "Quit"
|
||||
msgstr "終了"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:47
|
||||
msgid "Media"
|
||||
msgstr "メディア"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:50
|
||||
msgid "Open files"
|
||||
msgstr "ファイルを開く"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:56 src/dialogs.js:137
|
||||
msgid "Open URI"
|
||||
msgstr "URIを開く"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:64
|
||||
msgid "Playlist"
|
||||
msgstr "プレイリスト"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:67
|
||||
msgid "Next item"
|
||||
msgstr "次の項目"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:68
|
||||
msgid "Double tap (right side)"
|
||||
msgstr "ダブルタップ(右端)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:74
|
||||
msgid "Previous item"
|
||||
msgstr "前の項目"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:75
|
||||
msgid "Double tap (left side)"
|
||||
msgstr "ダブルタップ(左端)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:81
|
||||
msgid "Change repeat mode"
|
||||
msgstr "リピートモードを変更"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:87
|
||||
msgid "Export to file"
|
||||
msgstr "ファイルへエクスポート"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:95 ui/preferences-window.ui:119
|
||||
msgid "Playback"
|
||||
msgstr "プレイバック"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:98
|
||||
msgid "Toggle play"
|
||||
msgstr "一時停止・再生"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:99
|
||||
msgid "Long press | Right click"
|
||||
msgstr "Long press | Right click"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:105
|
||||
msgid "Seek forward"
|
||||
msgstr "早送り"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:106
|
||||
msgid "Swipe right | Scroll right"
|
||||
msgstr "右スワイプ | 右スクロール"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:112
|
||||
msgid "Seek backward"
|
||||
msgstr "巻き戻し"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:113
|
||||
msgid "Swipe left | Scroll left"
|
||||
msgstr "左スワイプ | 左スクロール"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:119
|
||||
msgid "Volume up"
|
||||
msgstr "音量を上げる"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:120
|
||||
msgid "Swipe up | Scroll up"
|
||||
msgstr "上スワイプ | 上スクロール"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:126
|
||||
msgid "Volume down"
|
||||
msgstr "音量を下げる"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:127
|
||||
msgid "Swipe down | Scroll down"
|
||||
msgstr "下スワイプ | 下スクロール"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr "ミュート切り替え"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
msgstr "次のチャプターへ"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:145
|
||||
msgid "Previous chapter"
|
||||
msgstr "前のチャプターへ\""
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:11
|
||||
msgid "Decoders"
|
||||
msgstr "デコーダー"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:18
|
||||
msgid "Return to the preferences"
|
||||
msgstr "設定へ戻る"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:16
|
||||
msgid "Behavior"
|
||||
msgstr "挙動"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:19
|
||||
msgid "Auto fullscreen"
|
||||
msgstr "自動的にフルスクリーンに切り替える"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:20
|
||||
msgid "Enter fullscreen when playlist is replaced except floating mode"
|
||||
msgstr "フローティングモードを除き、プレイリストを入れ替えた場合に自動的にフルスクリーンにする"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:26
|
||||
msgid "Ask to resume recent media"
|
||||
msgstr "前回再生したメディアを続きから再生するか尋ねる"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:32
|
||||
msgid "Float on all workspaces"
|
||||
msgstr "すべてのワークスペースにフロートを配置する"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:33
|
||||
msgid "This option only works on GNOME"
|
||||
msgstr "このオプションはGnomeでのみ機能します"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:39
|
||||
msgid "After playback"
|
||||
msgstr "再生終了後"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:44
|
||||
msgid "Do nothing"
|
||||
msgstr "何もしない"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:45
|
||||
msgid "Freeze last frame"
|
||||
msgstr "最後のフレームで停止する"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:46
|
||||
msgid "Close the app"
|
||||
msgstr "アプリを閉じる"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:56
|
||||
msgid "Volume"
|
||||
msgstr "音量"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:59
|
||||
msgid "Custom initial value"
|
||||
msgstr "初期音量を設定する"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:60
|
||||
msgid "Set custom volume at startup instead of restoring it"
|
||||
msgstr "前回使用した音量を使用するのではなく、起動時に音量をカスタム値に設定します"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:64
|
||||
msgid "Volume percentage"
|
||||
msgstr "音量(%)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:75
|
||||
msgid "Seeking"
|
||||
msgstr "シーキング"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:78
|
||||
msgid "Mode"
|
||||
msgstr "モード"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:84
|
||||
msgid "Accurate"
|
||||
msgstr "精密"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:85
|
||||
msgid "Fast"
|
||||
msgstr "高速"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:93
|
||||
msgid "Unit"
|
||||
msgstr "ユニット"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:98
|
||||
msgid "Second"
|
||||
msgstr "秒"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:99
|
||||
msgid "Minute"
|
||||
msgstr "分"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:100
|
||||
msgid "Percentage"
|
||||
msgstr "パーセント"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:108
|
||||
msgid "Value"
|
||||
msgstr "値"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:123
|
||||
msgid "Audio"
|
||||
msgstr "オ−ディオ"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:126
|
||||
msgid "Offset in milliseconds"
|
||||
msgstr "オフセット(ミリ秒)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:133
|
||||
msgid "Only native audio formats"
|
||||
msgstr "ネイティブオーディオフォーマットのみを使用する"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:141
|
||||
msgid "Subtitles"
|
||||
msgstr "字幕"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:144
|
||||
msgid "Default font"
|
||||
msgstr "既定のフォント"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:154
|
||||
msgid "Network"
|
||||
msgstr "ネットワーク"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:158
|
||||
msgid "Client"
|
||||
msgstr "クライアント"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:161
|
||||
msgid "Progressive download buffering"
|
||||
msgstr "プログレッシブなバッファリングを使用する"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:169
|
||||
msgid "Server"
|
||||
msgstr "サーバー"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:172
|
||||
msgid "Control player remotely"
|
||||
msgstr "プレーヤーをリモートでコントロールする"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:176
|
||||
msgid "Listening port"
|
||||
msgstr "待ち受けポート"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:183
|
||||
msgid "Run web application in background"
|
||||
msgstr "Webアプリケーションをバックグラウンドで実行する"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:184
|
||||
msgid "Requires GTK compiled with Broadway backend"
|
||||
msgstr "Broadwayバックエンドでコンパイルされた GTK が必要です"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:190
|
||||
msgid "Web application port"
|
||||
msgstr "Webアプリケーションのポート"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:204
|
||||
msgid "Prefer adaptive streaming"
|
||||
msgstr "アダプティブストリーミングを優先する"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:210
|
||||
msgid "Max quality"
|
||||
msgstr "Max quality"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:228
|
||||
msgid "Tweaks"
|
||||
msgstr "詳細設定"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:232
|
||||
msgid "Appearance"
|
||||
msgstr "外観"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:235
|
||||
msgid "Dark theme"
|
||||
msgstr "ダークテーマ"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:241
|
||||
msgid "Render window shadows"
|
||||
msgstr "ウィンドウの影をレンダリングする"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:242
|
||||
msgid "Disable to increase performance when windowed"
|
||||
msgstr "無効化するとウィンドウモードでのパフォーマンスが向上します"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:253
|
||||
msgid "Plugin ranking"
|
||||
msgstr "プライグインの優先順位"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:254
|
||||
msgid "Alter default ranks of GStreamer plugins"
|
||||
msgstr "GStreamerプラグインの優先順位を変更します"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:259
|
||||
msgid "Use playbin3"
|
||||
msgstr "playbin3を使用"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:260 ui/preferences-window.ui:269
|
||||
msgid "Requires player restart"
|
||||
msgstr "プレイヤーの再起動が必要です"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:262 ui/preferences-window.ui:271
|
||||
msgid "Experimental"
|
||||
msgstr "試験運用機能"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:268
|
||||
msgid "Use PipeWire for audio output"
|
||||
msgstr "オーディオ出力にPipeWireを使用する"
|
||||
msgstr ""
|
||||
|
||||
#: src/buttons.js:201
|
||||
#, javascript-format
|
||||
msgid "Decoder: %s"
|
||||
msgstr "デコーダー: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:152
|
||||
msgid "Enter or drop URI here"
|
||||
msgstr "ここにURIを入力またはドロップ"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:157
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:158
|
||||
msgid "Open"
|
||||
msgstr "開く"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:226
|
||||
msgid "Title"
|
||||
msgstr "タイトル"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:227
|
||||
msgid "Completed"
|
||||
msgstr "再生済み"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:235
|
||||
msgid "Resume playback?"
|
||||
msgstr "前回の続きから再生しますか?"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:289
|
||||
#, javascript-format
|
||||
msgid "GTK version: %s"
|
||||
msgstr "GTKバージョン: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:290
|
||||
#, javascript-format
|
||||
msgid "Adwaita version: %s"
|
||||
msgstr "Adwaitaバージョン: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:291
|
||||
#, javascript-format
|
||||
msgid "GStreamer version: %s"
|
||||
msgstr "GStreamer バージョン: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:292
|
||||
#, javascript-format
|
||||
msgid "GJS version: %s"
|
||||
msgstr "GJS バージョン: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:300
|
||||
msgid "A GNOME media player powered by GStreamer"
|
||||
msgstr "GStreamerを使用したGNOME向けのメディアプレーヤー"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
|
||||
#: src/dialogs.js:305
|
||||
msgid "translator-credits"
|
||||
msgstr "翻訳者"
|
||||
msgstr ""
|
||||
|
||||
#: src/revealers.js:170
|
||||
#, javascript-format
|
||||
msgid "Ends at: %s"
|
||||
msgstr "再生終了時刻: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:227 src/widget.js:236 src/widget.js:242 src/widget.js:248
|
||||
msgid "Undetermined"
|
||||
msgstr "不明"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:243
|
||||
msgid "Channels"
|
||||
msgstr "チャンネル"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:261
|
||||
msgid "Disabled"
|
||||
msgstr "Disabled"
|
||||
msgstr ""
|
||||
|
||||
|
44
po/nl.po
44
po/nl.po
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-09-14 16:35+0200\n"
|
||||
"PO-Revision-Date: 2022-04-08 16:14\n"
|
||||
"PO-Revision-Date: 2021-09-14 16:21\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Dutch\n"
|
||||
"Language: nl_NL\n"
|
||||
@@ -39,7 +39,7 @@ msgstr "Over Clapper"
|
||||
|
||||
#: ui/elapsed-time-button.ui:27
|
||||
msgid "Speed"
|
||||
msgstr "Snelheid"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:83
|
||||
#: ui/preferences-window.ui:215
|
||||
@@ -164,7 +164,7 @@ msgstr "Omlaag vegen/scrollen"
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr "Geluid dempen"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
@@ -368,72 +368,72 @@ msgstr "Pas de standaardvolgorde van GStreamer-plug-ins aan"
|
||||
|
||||
#: ui/preferences-window.ui:259
|
||||
msgid "Use playbin3"
|
||||
msgstr "playbin3 gebruiken"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:260 ui/preferences-window.ui:269
|
||||
msgid "Requires player restart"
|
||||
msgstr "Herstart de speler om de wijziging toe te passen"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:262 ui/preferences-window.ui:271
|
||||
msgid "Experimental"
|
||||
msgstr "Experimenteel"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:268
|
||||
msgid "Use PipeWire for audio output"
|
||||
msgstr "PipeWire gebruiken voor audio-uitvoer"
|
||||
msgstr ""
|
||||
|
||||
#: src/buttons.js:201
|
||||
#, javascript-format
|
||||
msgid "Decoder: %s"
|
||||
msgstr "Decoder: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:152
|
||||
msgid "Enter or drop URI here"
|
||||
msgstr "Voer een uri in of sleep een uri hierheen"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:157
|
||||
msgid "Cancel"
|
||||
msgstr "Annuleren"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:158
|
||||
msgid "Open"
|
||||
msgstr "Openen"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:226
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:227
|
||||
msgid "Completed"
|
||||
msgstr "Voltooid"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:235
|
||||
msgid "Resume playback?"
|
||||
msgstr "Afspelen hervatten?"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:289
|
||||
#, javascript-format
|
||||
msgid "GTK version: %s"
|
||||
msgstr "GTK versie: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:290
|
||||
#, javascript-format
|
||||
msgid "Adwaita version: %s"
|
||||
msgstr "Adwaita-versie: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:291
|
||||
#, javascript-format
|
||||
msgid "GStreamer version: %s"
|
||||
msgstr "GStreamer-versie: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:292
|
||||
#, javascript-format
|
||||
msgid "GJS version: %s"
|
||||
msgstr "GJS-versie: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:300
|
||||
msgid "A GNOME media player powered by GStreamer"
|
||||
msgstr "Een door GStreamer aangedreven GNOME-mediaspeler"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
|
||||
#: src/dialogs.js:305
|
||||
@@ -447,13 +447,13 @@ msgstr "Eindigt op: %s"
|
||||
|
||||
#: src/widget.js:227 src/widget.js:236 src/widget.js:242 src/widget.js:248
|
||||
msgid "Undetermined"
|
||||
msgstr "onbekend"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:243
|
||||
msgid "Channels"
|
||||
msgstr "Kanalen"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:261
|
||||
msgid "Disabled"
|
||||
msgstr "Uitgeschakeld"
|
||||
msgstr ""
|
||||
|
||||
|
178
po/pt.po
178
po/pt.po
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-09-14 16:35+0200\n"
|
||||
"PO-Revision-Date: 2022-05-01 18:43\n"
|
||||
"PO-Revision-Date: 2021-10-21 00:29\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Portuguese\n"
|
||||
"Language: pt_PT\n"
|
||||
@@ -19,7 +19,7 @@ msgstr ""
|
||||
|
||||
#: ui/clapper.ui:6
|
||||
msgid "Open Files…"
|
||||
msgstr "Abrir ficheiros…"
|
||||
msgstr "Abrir Arquivos…"
|
||||
|
||||
#: ui/clapper.ui:10
|
||||
msgid "Open URI…"
|
||||
@@ -104,356 +104,356 @@ msgstr "Toque duplo (lado direito)"
|
||||
|
||||
#: ui/help-overlay.ui:74
|
||||
msgid "Previous item"
|
||||
msgstr "Item anterior"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:75
|
||||
msgid "Double tap (left side)"
|
||||
msgstr "Toque duplo (lado esquerdo)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:81
|
||||
msgid "Change repeat mode"
|
||||
msgstr "Alterar modo de repetição"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:87
|
||||
msgid "Export to file"
|
||||
msgstr "Exportar para ficheiro"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:95 ui/preferences-window.ui:119
|
||||
msgid "Playback"
|
||||
msgstr "Reproduzir"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:98
|
||||
msgid "Toggle play"
|
||||
msgstr "Alternar reprodução"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:99
|
||||
msgid "Long press | Right click"
|
||||
msgstr "Toque longo | Clique com o botão direito"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:105
|
||||
msgid "Seek forward"
|
||||
msgstr "Procurar para a frente"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:106
|
||||
msgid "Swipe right | Scroll right"
|
||||
msgstr "Deslizar para a direita | Deslocar para direita"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:112
|
||||
msgid "Seek backward"
|
||||
msgstr "Procurar para trás"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:113
|
||||
msgid "Swipe left | Scroll left"
|
||||
msgstr "Deslizar para a esquerda | Deslocar para a esquerda"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:119
|
||||
msgid "Volume up"
|
||||
msgstr "Aumentar o volume"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:120
|
||||
msgid "Swipe up | Scroll up"
|
||||
msgstr "Deslizar para cima | Deslocar para cima"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:126
|
||||
msgid "Volume down"
|
||||
msgstr "Diminuir o volume"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:127
|
||||
msgid "Swipe down | Scroll down"
|
||||
msgstr "Deslizar para baixo | Deslocar para baixo"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr "Ativar/Desativar Som"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
msgstr "Capítulo seguinte"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:145
|
||||
msgid "Previous chapter"
|
||||
msgstr "Capítulo anterior"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:11
|
||||
msgid "Decoders"
|
||||
msgstr "Descodificadores"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:18
|
||||
msgid "Return to the preferences"
|
||||
msgstr "Voltar para as preferências"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:16
|
||||
msgid "Behavior"
|
||||
msgstr "Comportamento"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:19
|
||||
msgid "Auto fullscreen"
|
||||
msgstr "Ecrã inteiro automático"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:20
|
||||
msgid "Enter fullscreen when playlist is replaced except floating mode"
|
||||
msgstr "Entrar em ecrã inteiro quando a lista de reprodução é substituída, excepto no modo flutuante"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:26
|
||||
msgid "Ask to resume recent media"
|
||||
msgstr "Pedir para retomar o ficheiro recente"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:32
|
||||
msgid "Float on all workspaces"
|
||||
msgstr "Flutuar em todas as áreas de trabalho"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:33
|
||||
msgid "This option only works on GNOME"
|
||||
msgstr "Esta opção apenas funciona no GNOME"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:39
|
||||
msgid "After playback"
|
||||
msgstr "Após reprodução"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:44
|
||||
msgid "Do nothing"
|
||||
msgstr "Não fazer nada"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:45
|
||||
msgid "Freeze last frame"
|
||||
msgstr "Suster o fotograma"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:46
|
||||
msgid "Close the app"
|
||||
msgstr "Fechar a aplicação"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:56
|
||||
msgid "Volume"
|
||||
msgstr "Volume"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:59
|
||||
msgid "Custom initial value"
|
||||
msgstr "Valor inicial personalizado"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:60
|
||||
msgid "Set custom volume at startup instead of restoring it"
|
||||
msgstr "Definir volume personalizado no arranque ao invés de restaurá-lo"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:64
|
||||
msgid "Volume percentage"
|
||||
msgstr "Percentagem de volume"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:75
|
||||
msgid "Seeking"
|
||||
msgstr "Procurando"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:78
|
||||
msgid "Mode"
|
||||
msgstr "Modo"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:84
|
||||
msgid "Accurate"
|
||||
msgstr "Preciso"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:85
|
||||
msgid "Fast"
|
||||
msgstr "Rápido"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:93
|
||||
msgid "Unit"
|
||||
msgstr "Unidade"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:98
|
||||
msgid "Second"
|
||||
msgstr "Segundo"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:99
|
||||
msgid "Minute"
|
||||
msgstr "Minuto"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:100
|
||||
msgid "Percentage"
|
||||
msgstr "Percentagem"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:108
|
||||
msgid "Value"
|
||||
msgstr "Valor"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:123
|
||||
msgid "Audio"
|
||||
msgstr "Áudio"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:126
|
||||
msgid "Offset in milliseconds"
|
||||
msgstr "Deslocamento em milissegundos"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:133
|
||||
msgid "Only native audio formats"
|
||||
msgstr "Apenas formatos de áudio nativos"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:141
|
||||
msgid "Subtitles"
|
||||
msgstr "Legendas"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:144
|
||||
msgid "Default font"
|
||||
msgstr "Tipo de letra predefinida"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:154
|
||||
msgid "Network"
|
||||
msgstr "Rede"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:158
|
||||
msgid "Client"
|
||||
msgstr "Cliente"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:161
|
||||
msgid "Progressive download buffering"
|
||||
msgstr "Buffering de transferência progressiva"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:169
|
||||
msgid "Server"
|
||||
msgstr "Servidor"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:172
|
||||
msgid "Control player remotely"
|
||||
msgstr "Controlar reprodutor remotamente"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:176
|
||||
msgid "Listening port"
|
||||
msgstr "Porta de escuta"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:183
|
||||
msgid "Run web application in background"
|
||||
msgstr "Executar aplicação web em segundo plano"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:184
|
||||
msgid "Requires GTK compiled with Broadway backend"
|
||||
msgstr "Requer GTK compilado com backend Broadway"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:190
|
||||
msgid "Web application port"
|
||||
msgstr "Porta de aplicação Web"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:204
|
||||
msgid "Prefer adaptive streaming"
|
||||
msgstr "Preferir transmissão adaptável"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:210
|
||||
msgid "Max quality"
|
||||
msgstr "Qualidade máxima"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:228
|
||||
msgid "Tweaks"
|
||||
msgstr "Ajustes"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:232
|
||||
msgid "Appearance"
|
||||
msgstr "Aparência"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:235
|
||||
msgid "Dark theme"
|
||||
msgstr "Tema escuro"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:241
|
||||
msgid "Render window shadows"
|
||||
msgstr "Renderizar sombras das janelas"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:242
|
||||
msgid "Disable to increase performance when windowed"
|
||||
msgstr "Desativar para aumentar o desempenho quando em janela"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:253
|
||||
msgid "Plugin ranking"
|
||||
msgstr "Classificação do plugin"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:254
|
||||
msgid "Alter default ranks of GStreamer plugins"
|
||||
msgstr "Altera as classificações predefinidas dos plugins GStreamer"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:259
|
||||
msgid "Use playbin3"
|
||||
msgstr "Usar playbin3"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:260 ui/preferences-window.ui:269
|
||||
msgid "Requires player restart"
|
||||
msgstr "Requer o reinício do reprodutor"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:262 ui/preferences-window.ui:271
|
||||
msgid "Experimental"
|
||||
msgstr "Experimental"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:268
|
||||
msgid "Use PipeWire for audio output"
|
||||
msgstr "Usar o PipeWire para a saída áudio"
|
||||
msgstr ""
|
||||
|
||||
#: src/buttons.js:201
|
||||
#, javascript-format
|
||||
msgid "Decoder: %s"
|
||||
msgstr "Descodificador: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:152
|
||||
msgid "Enter or drop URI here"
|
||||
msgstr "Introduzir ou largar o URI aqui"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:157
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:158
|
||||
msgid "Open"
|
||||
msgstr "Abrir"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:226
|
||||
msgid "Title"
|
||||
msgstr "Título"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:227
|
||||
msgid "Completed"
|
||||
msgstr "Concluído"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:235
|
||||
msgid "Resume playback?"
|
||||
msgstr "Retomar a reprodução?"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:289
|
||||
#, javascript-format
|
||||
msgid "GTK version: %s"
|
||||
msgstr "Versão do GTK: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:290
|
||||
#, javascript-format
|
||||
msgid "Adwaita version: %s"
|
||||
msgstr "Versão do Adwaita: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:291
|
||||
#, javascript-format
|
||||
msgid "GStreamer version: %s"
|
||||
msgstr "Versão do GStreamer: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:292
|
||||
#, javascript-format
|
||||
msgid "GJS version: %s"
|
||||
msgstr "Versão do GJS: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:300
|
||||
msgid "A GNOME media player powered by GStreamer"
|
||||
msgstr "Um reprodutor multimédia GNOME desenvolvido por GStreamer"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
|
||||
#: src/dialogs.js:305
|
||||
msgid "translator-credits"
|
||||
msgstr "Hugo Carvalho <hugokarvalho@hotmail.com>"
|
||||
msgstr ""
|
||||
|
||||
#: src/revealers.js:170
|
||||
#, javascript-format
|
||||
msgid "Ends at: %s"
|
||||
msgstr "Termina às: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:227 src/widget.js:236 src/widget.js:242 src/widget.js:248
|
||||
msgid "Undetermined"
|
||||
msgstr "Indeterminado"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:243
|
||||
msgid "Channels"
|
||||
msgstr "Canais"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:261
|
||||
msgid "Disabled"
|
||||
msgstr "Desativado"
|
||||
msgstr ""
|
||||
|
||||
|
88
po/tr.po
88
po/tr.po
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-09-14 16:35+0200\n"
|
||||
"PO-Revision-Date: 2022-04-02 01:43\n"
|
||||
"PO-Revision-Date: 2021-09-14 15:24\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Turkish\n"
|
||||
"Language: tr_TR\n"
|
||||
@@ -19,176 +19,176 @@ msgstr ""
|
||||
|
||||
#: ui/clapper.ui:6
|
||||
msgid "Open Files…"
|
||||
msgstr "Dosya Aç…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:10
|
||||
msgid "Open URI…"
|
||||
msgstr "URL Aç…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:16 ui/preferences-window.ui:4
|
||||
msgid "Preferences"
|
||||
msgstr "Tercihler"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:20
|
||||
msgid "Shortcuts"
|
||||
msgstr "Kısayollar"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:26
|
||||
msgid "About Clapper"
|
||||
msgstr "Clapper Hakkında"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:27
|
||||
msgid "Speed"
|
||||
msgstr "Hız"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:83
|
||||
#: ui/preferences-window.ui:215
|
||||
msgid "Normal"
|
||||
msgstr "Normal"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:10 ui/preferences-window.ui:12
|
||||
msgid "General"
|
||||
msgstr "Genel"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:13
|
||||
msgid "Show shortcuts"
|
||||
msgstr "Kısayolları göster"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:19
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "Tam ekran"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:20
|
||||
msgid "Double tap | Double click"
|
||||
msgstr "Çift dokun | Çift tıkla"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:26
|
||||
msgid "Leave fullscreen"
|
||||
msgstr "Tam ekrandan çık"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:32
|
||||
msgid "Reveal OSD (fullscreen only)"
|
||||
msgstr "Pencere Düğmelerini Göster (yalnızca tam ekran)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:33
|
||||
msgid "Tap"
|
||||
msgstr "Dokun"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:39
|
||||
msgid "Quit"
|
||||
msgstr "Çık"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:47
|
||||
msgid "Media"
|
||||
msgstr "Ortam"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:50
|
||||
msgid "Open files"
|
||||
msgstr "Dosyaları Aç"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:56 src/dialogs.js:137
|
||||
msgid "Open URI"
|
||||
msgstr "URL Aç"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:64
|
||||
msgid "Playlist"
|
||||
msgstr "Oynatma listesi"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:67
|
||||
msgid "Next item"
|
||||
msgstr "Sonraki öğe"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:68
|
||||
msgid "Double tap (right side)"
|
||||
msgstr "Çift tıkla (sağ tarafa)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:74
|
||||
msgid "Previous item"
|
||||
msgstr "Önceki öğe"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:75
|
||||
msgid "Double tap (left side)"
|
||||
msgstr "Çift tıkla (sol tarafa)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:81
|
||||
msgid "Change repeat mode"
|
||||
msgstr "Tekrarlı oynatma modunu değiştir"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:87
|
||||
msgid "Export to file"
|
||||
msgstr "Dosyaya aktar"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:95 ui/preferences-window.ui:119
|
||||
msgid "Playback"
|
||||
msgstr "Oynatma"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:98
|
||||
msgid "Toggle play"
|
||||
msgstr "Oynatmayı başlat"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:99
|
||||
msgid "Long press | Right click"
|
||||
msgstr "Uzun bas | Sağ tıkla"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:105
|
||||
msgid "Seek forward"
|
||||
msgstr "İleri sar"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:106
|
||||
msgid "Swipe right | Scroll right"
|
||||
msgstr "Sağa sürükle | Sağa kaydır"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:112
|
||||
msgid "Seek backward"
|
||||
msgstr "Geri sar"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:113
|
||||
msgid "Swipe left | Scroll left"
|
||||
msgstr "Sola sürükle | Sola kaydır"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:119
|
||||
msgid "Volume up"
|
||||
msgstr "Sesi artır"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:120
|
||||
msgid "Swipe up | Scroll up"
|
||||
msgstr "Yukarı sürükle | Yukarı kaydır"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:126
|
||||
msgid "Volume down"
|
||||
msgstr "Sesi kıs"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:127
|
||||
msgid "Swipe down | Scroll down"
|
||||
msgstr "Aşağı sürükle | Aşağı kaydır"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr "Sesi kapat"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
msgstr "Sonraki parça"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:145
|
||||
msgid "Previous chapter"
|
||||
msgstr "Önceki parça"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:11
|
||||
msgid "Decoders"
|
||||
msgstr "Çözücüler"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:18
|
||||
msgid "Return to the preferences"
|
||||
msgstr "Tercihlere geri dön"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:16
|
||||
msgid "Behavior"
|
||||
msgstr "Davranış"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:19
|
||||
msgid "Auto fullscreen"
|
||||
msgstr "Otomatik tam ekran"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:20
|
||||
msgid "Enter fullscreen when playlist is replaced except floating mode"
|
||||
|
18
po/zh_CN.po
18
po/zh_CN.po
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-09-14 16:35+0200\n"
|
||||
"PO-Revision-Date: 2022-04-20 18:10\n"
|
||||
"PO-Revision-Date: 2022-01-16 14:15\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Chinese Simplified\n"
|
||||
"Language: zh_CN\n"
|
||||
@@ -52,11 +52,11 @@ msgstr "常规"
|
||||
|
||||
#: ui/help-overlay.ui:13
|
||||
msgid "Show shortcuts"
|
||||
msgstr "显示快捷键"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:19
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "切换全屏"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:20
|
||||
msgid "Double tap | Double click"
|
||||
@@ -116,11 +116,11 @@ msgstr "更改循环模式"
|
||||
|
||||
#: ui/help-overlay.ui:87
|
||||
msgid "Export to file"
|
||||
msgstr "导出至文件"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:95 ui/preferences-window.ui:119
|
||||
msgid "Playback"
|
||||
msgstr "播放"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:98
|
||||
msgid "Toggle play"
|
||||
@@ -164,7 +164,7 @@ msgstr "向下滑动 | 向下滚动"
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr "切换静音"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
@@ -180,7 +180,7 @@ msgstr "解码器"
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:18
|
||||
msgid "Return to the preferences"
|
||||
msgstr "返回首选项"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:16
|
||||
msgid "Behavior"
|
||||
@@ -340,7 +340,7 @@ msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:228
|
||||
msgid "Tweaks"
|
||||
msgstr "调整"
|
||||
msgstr "微调"
|
||||
|
||||
#: ui/preferences-window.ui:232
|
||||
msgid "Appearance"
|
||||
@@ -438,7 +438,7 @@ msgstr ""
|
||||
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
|
||||
#: src/dialogs.js:305
|
||||
msgid "translator-credits"
|
||||
msgstr "刘韬"
|
||||
msgstr ""
|
||||
|
||||
#: src/revealers.js:170
|
||||
#, javascript-format
|
||||
|
@@ -20,8 +20,11 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
const use_legacy_sink = GLib.getenv('CLAPPER_USE_LEGACY_SINK');
|
||||
|
||||
if(!use_legacy_sink || use_legacy_sink != '1') {
|
||||
vsink = Gst.ElementFactory.make('clappersink', null);
|
||||
this.clappersink = vsink;
|
||||
vsink = Gst.parse_bin_from_description('glupload ! glcolorconvert'
|
||||
+ ' ! clapperglimport ! clappersink name=clappersink', true);
|
||||
|
||||
if(vsink)
|
||||
this.clappersink = vsink.get_by_name('clappersink');
|
||||
}
|
||||
|
||||
if(!vsink) {
|
||||
|
Reference in New Issue
Block a user