5 Commits

Author SHA1 Message Date
Rafał Dzięgiel
2575b61cd0 Require GTK 4.6.0 for new plugin 2022-03-01 09:36:46 +01:00
Rafał Dzięgiel
9abf2fb11d wip4 2022-03-01 09:36:46 +01:00
Rafał Dzięgiel
167be4c37b wip3 2022-03-01 09:36:46 +01:00
Rafał Dzięgiel
74c4a079e6 wip2 2022-03-01 09:36:46 +01:00
Rafał Dzięgiel
8a597fffe7 WIP 2022-03-01 09:36:46 +01:00
43 changed files with 1734 additions and 3961 deletions

View File

@@ -1,5 +1,4 @@
on: on:
workflow_dispatch:
schedule: schedule:
- cron: "0 0 * * *" - cron: "0 0 * * *"
name: "Flatpak Nightly" name: "Flatpak Nightly"
@@ -26,12 +25,8 @@ jobs:
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v1
with: with:
platforms: arm64 platforms: arm64
- name: Prepare Runtime
run: |
flatpak --system install -y --noninteractive flathub org.freedesktop.Sdk.Extension.rust-nightly/${{ matrix.arch }}/21.08
flatpak --system install -y --noninteractive flathub org.freedesktop.Sdk.Extension.llvm13/${{ matrix.arch }}/21.08
- uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v4 - uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v4
name: Build name: "Build"
with: with:
bundle: com.github.rafostar.Clapper.flatpak bundle: com.github.rafostar.Clapper.flatpak
manifest-path: pkgs/flatpak/com.github.rafostar.Clapper-nightly.json manifest-path: pkgs/flatpak/com.github.rafostar.Clapper-nightly.json

View File

@@ -1,5 +1,4 @@
on: on:
workflow_dispatch:
push: push:
branches: branches:
- master - master
@@ -12,7 +11,7 @@ jobs:
name: "Flatpak" name: "Flatpak"
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: bilelmoussaoui/flatpak-github-actions:gnome-42 image: bilelmoussaoui/flatpak-github-actions:gnome-41
options: --privileged options: --privileged
strategy: strategy:
matrix: matrix:

View File

@@ -2,8 +2,6 @@
[![Flatpak](https://github.com/Rafostar/clapper/actions/workflows/flatpak.yml/badge.svg?event=push)](https://github.com/Rafostar/clapper/actions/workflows/flatpak.yml) [![Flatpak](https://github.com/Rafostar/clapper/actions/workflows/flatpak.yml/badge.svg?event=push)](https://github.com/Rafostar/clapper/actions/workflows/flatpak.yml)
[![Flatpak Nightly](https://github.com/Rafostar/clapper/actions/workflows/flatpak-nightly.yml/badge.svg?event=schedule)](https://github.com/Rafostar/clapper/actions/workflows/flatpak-nightly.yml) [![Flatpak Nightly](https://github.com/Rafostar/clapper/actions/workflows/flatpak-nightly.yml/badge.svg?event=schedule)](https://github.com/Rafostar/clapper/actions/workflows/flatpak-nightly.yml)
[![Crowdin](https://badges.crowdin.net/clapper/localized.svg)](https://crowdin.com/project/clapper) [![Crowdin](https://badges.crowdin.net/clapper/localized.svg)](https://crowdin.com/project/clapper)
[![Matrix](https://img.shields.io/matrix/clapper-player:matrix.org?label=matrix)](https://matrix.to/#/#clapper-player:matrix.org)
[![Donate](https://img.shields.io/liberapay/receives/Clapper.svg?logo=liberapay)](https://liberapay.com/Clapper)
A GNOME media player built using [GJS](https://gitlab.gnome.org/GNOME/gjs) with [GTK4](https://www.gtk.org) toolkit. A GNOME media player built using [GJS](https://gitlab.gnome.org/GNOME/gjs) with [GTK4](https://www.gtk.org) toolkit.
The media player uses [GStreamer](https://gstreamer.freedesktop.org/) as a media backend and renders everything via [OpenGL](https://www.opengl.org). The media player uses [GStreamer](https://gstreamer.freedesktop.org/) as a media backend and renders everything via [OpenGL](https://www.opengl.org).
@@ -40,13 +38,17 @@ List of patches used in this version can be found [here](https://github.com/Rafo
<img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/> <img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/>
</a> </a>
## Packages in Linux Distributions ## Packages
[![Packaging status](https://repology.org/badge/vertical-allrepos/clapper.svg)](https://repology.org/project/clapper/versions) #### Fedora & openSUSE
Pre-built packages are available in [my repo](https://software.opensuse.org//download.html?project=home%3ARafostar&package=clapper) ([see status](https://build.opensuse.org/package/show/home:Rafostar/clapper)).<br>
Those are automatically built on each git commit, and are thus considered unstable.
Pre-built RPM packages are also available in [my repo](https://software.opensuse.org//download.html?project=home%3ARafostar&package=clapper) ([see status](https://build.opensuse.org/package/show/home:Rafostar/clapper)).<br> #### Arch Linux
Those are automatically built on each git commit, thus are considered unstable. You can get Clapper from the AUR:
* [clapper](https://aur.archlinux.org/packages/clapper) (stable version)
* [clapper-git](https://aur.archlinux.org/packages/clapper-git)
## Installation from Source Code ## Installation from source code
```sh ```sh
meson builddir --prefix=/usr/local meson builddir --prefix=/usr/local
sudo meson install -C builddir sudo meson install -C builddir

View File

@@ -1,374 +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 "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;
}

View File

@@ -1,69 +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/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

View File

@@ -1,464 +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 <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;
}

View File

@@ -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/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

View File

@@ -1,180 +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 <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);
}

View File

@@ -1,38 +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 "gstclapperdmabufbaseimport.h"
G_BEGIN_DECLS
#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)
#define GST_CLAPPER_DMABUF_IMPORT_CAST(obj) ((GstClapperDmabufImport *)(obj))
struct _GstClapperDmabufImport
{
GstClapperDmabufBaseImport parent;
};
GST_ELEMENT_REGISTER_DECLARE (clapperdmabufimport);
G_END_DECLS

View File

@@ -1,173 +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 "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);
}

View File

@@ -1,44 +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/gstbufferpool.h>
#include <gst/video/video.h>
#include "gstclappergdkmemory.h"
G_BEGIN_DECLS
#define GST_TYPE_CLAPPER_GDK_BUFFER_POOL (gst_clapper_gdk_buffer_pool_get_type())
G_DECLARE_FINAL_TYPE (GstClapperGdkBufferPool, gst_clapper_gdk_buffer_pool, GST, CLAPPER_GDK_BUFFER_POOL, GstBufferPool)
#define GST_CLAPPER_GDK_BUFFER_POOL_CAST(obj) ((GstClapperGdkBufferPool *)(obj))
struct _GstClapperGdkBufferPool
{
GstBufferPool parent;
GstClapperGdkAllocator *allocator;
GstVideoInfo info;
};
GstBufferPool * gst_clapper_gdk_buffer_pool_new (void);
G_END_DECLS

View File

@@ -1,141 +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 "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);
}

View File

@@ -1,67 +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/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

View File

@@ -1,414 +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 <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;
}

View File

@@ -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 <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

View File

@@ -1,178 +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 "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);
}

View File

@@ -1,38 +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 "gstclapperglbaseimport.h"
G_BEGIN_DECLS
#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_GL_IMPORT_CAST(obj) ((GstClapperGLImport *)(obj))
struct _GstClapperGLImport
{
GstClapperGLBaseImport parent;
};
GST_ELEMENT_REGISTER_DECLARE (clapperglimport);
G_END_DECLS

View File

@@ -1,128 +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 "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);
}

View File

@@ -1,38 +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 "gstclapperbaseimport.h"
G_BEGIN_DECLS
#define GST_TYPE_CLAPPER_IMPORT (gst_clapper_import_get_type())
G_DECLARE_FINAL_TYPE (GstClapperImport, gst_clapper_import, GST, CLAPPER_IMPORT, GstClapperBaseImport)
#define GST_CLAPPER_IMPORT_CAST(obj) ((GstClapperImport *)(obj))
struct _GstClapperImport
{
GstClapperBaseImport parent;
};
GST_ELEMENT_REGISTER_DECLARE (clapperimport);
G_END_DECLS

View File

@@ -1,635 +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 "gstclapperpaintable.h"
#include "gstclappergdkmemory.h"
#include "gstgtkutils.h"
#define DEFAULT_PAR_N 1
#define DEFAULT_PAR_D 1
#define GST_CAT_DEFAULT gst_clapper_paintable_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
typedef struct
{
GdkTexture *texture;
GstVideoOverlayRectangle *rectangle;
gint x, y;
guint width, height;
gboolean used;
} GstClapperGdkOverlay;
static void
gst_clapper_gdk_overlay_free (GstClapperGdkOverlay *overlay)
{
GST_TRACE ("Freeing overlay: %" GST_PTR_FORMAT, overlay);
g_object_unref (overlay->texture);
gst_video_overlay_rectangle_unref (overlay->rectangle);
g_slice_free (GstClapperGdkOverlay, overlay);
}
static void gst_clapper_paintable_iface_init (GdkPaintableInterface *iface);
static void gst_clapper_paintable_dispose (GObject *object);
static void gst_clapper_paintable_finalize (GObject *object);
#define parent_class gst_clapper_paintable_parent_class
G_DEFINE_TYPE_WITH_CODE (GstClapperPaintable, gst_clapper_paintable, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gst_clapper_paintable_iface_init));
static void
gst_clapper_paintable_class_init (GstClapperPaintableClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperpaintable", 0,
"Clapper Paintable");
gobject_class->dispose = gst_clapper_paintable_dispose;
gobject_class->finalize = gst_clapper_paintable_finalize;
}
static void
gst_clapper_paintable_init (GstClapperPaintable *self)
{
self->par_n = DEFAULT_PAR_N;
self->par_d = DEFAULT_PAR_D;
g_mutex_init (&self->lock);
gst_video_info_init (&self->v_info);
g_weak_ref_init (&self->widget, NULL);
self->overlays = g_ptr_array_new_with_free_func (
(GDestroyNotify) gst_clapper_gdk_overlay_free);
gdk_rgba_parse (&self->bg, "black");
}
static void
gst_clapper_paintable_dispose (GObject *object)
{
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE (object);
GST_CLAPPER_PAINTABLE_LOCK (self);
if (self->draw_id > 0) {
g_source_remove (self->draw_id);
self->draw_id = 0;
}
GST_CLAPPER_PAINTABLE_UNLOCK (self);
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
}
static void
gst_clapper_paintable_finalize (GObject *object)
{
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE (object);
GST_TRACE ("Finalize");
GST_CLAPPER_PAINTABLE_LOCK (self);
g_weak_ref_clear (&self->widget);
gst_clear_buffer (&self->pending_buffer);
gst_clear_buffer (&self->buffer);
g_ptr_array_unref (self->overlays);
GST_CLAPPER_PAINTABLE_UNLOCK (self);
g_mutex_clear (&self->lock);
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}
static gboolean
calculate_display_par (GstClapperPaintable *self, GstVideoInfo *info)
{
gint width, height, par_n, par_d, req_par_n, req_par_d;
gboolean success;
width = GST_VIDEO_INFO_WIDTH (info);
height = GST_VIDEO_INFO_HEIGHT (info);
/* Cannot apply aspect ratio if there is no video */
if (width == 0 || height == 0)
return FALSE;
par_n = GST_VIDEO_INFO_PAR_N (info);
par_d = GST_VIDEO_INFO_PAR_D (info);
req_par_n = self->par_n;
req_par_d = self->par_d;
if (par_n == 0)
par_n = 1;
/* Use defaults if user set zero */
if (req_par_n == 0 || req_par_d == 0) {
req_par_n = DEFAULT_PAR_N;
req_par_d = DEFAULT_PAR_D;
}
GST_LOG_OBJECT (self, "PAR: %u/%u, DAR: %u/%u", par_n, par_d, req_par_n, req_par_d);
if (!(success = gst_video_calculate_display_ratio (&self->display_ratio_num,
&self->display_ratio_den, width, height, par_n, par_d,
req_par_n, req_par_d))) {
GST_ERROR_OBJECT (self, "Could not calculate display ratio values");
}
return success;
}
static void
invalidate_paintable_size_internal (GstClapperPaintable *self)
{
gint video_width, video_height;
guint display_ratio_num, display_ratio_den;
GST_CLAPPER_PAINTABLE_LOCK (self);
video_width = GST_VIDEO_INFO_WIDTH (&self->v_info);
video_height = GST_VIDEO_INFO_HEIGHT (&self->v_info);
display_ratio_num = self->display_ratio_num;
display_ratio_den = self->display_ratio_den;
GST_CLAPPER_PAINTABLE_UNLOCK (self);
if (video_height % display_ratio_den == 0) {
GST_LOG ("Keeping video height");
self->display_width = (guint)
gst_util_uint64_scale_int (video_height, display_ratio_num, display_ratio_den);
self->display_height = video_height;
} else if (video_width % display_ratio_num == 0) {
GST_LOG ("Keeping video width");
self->display_width = video_width;
self->display_height = (guint)
gst_util_uint64_scale_int (video_width, display_ratio_den, display_ratio_num);
} else {
GST_LOG ("Approximating while keeping video height");
self->display_width = (guint)
gst_util_uint64_scale_int (video_height, display_ratio_num, display_ratio_den);
self->display_height = video_height;
}
self->display_aspect_ratio = ((gdouble) self->display_width
/ (gdouble) self->display_height);
GST_DEBUG_OBJECT (self, "Invalidate paintable size, display: %dx%d",
self->display_width, self->display_height);
gdk_paintable_invalidate_size ((GdkPaintable *) self);
}
static gboolean
invalidate_paintable_size_on_main_cb (GstClapperPaintable *self)
{
GST_CLAPPER_PAINTABLE_LOCK (self);
self->draw_id = 0;
GST_CLAPPER_PAINTABLE_UNLOCK (self);
invalidate_paintable_size_internal (self);
return G_SOURCE_REMOVE;
}
static void
comp_frame_unmap_and_free (GstVideoFrame *frame)
{
gst_video_frame_unmap (frame);
g_slice_free (GstVideoFrame, frame);
}
static GstClapperGdkOverlay *
_get_cached_overlay (GPtrArray *overlays, GstVideoOverlayRectangle *rectangle, guint *index)
{
guint i;
for (i = 0; i < overlays->len; i++) {
GstClapperGdkOverlay *overlay = g_ptr_array_index (overlays, i);
if (overlay->rectangle != rectangle)
continue;
*index = i;
return overlay;
}
return NULL;
}
static void
gst_clapper_paintable_prepare_overlays (GstClapperPaintable *self)
{
GstVideoOverlayCompositionMeta *comp_meta;
guint num_overlays, i;
/* As long as this is called from main thread, no need to lock here */
if (G_UNLIKELY (!self->buffer)
|| !(comp_meta = gst_buffer_get_video_overlay_composition_meta (self->buffer))) {
guint n_pending = self->overlays->len;
/* Remove all cached overlays if new buffer does not have any */
if (n_pending > 0) {
GST_TRACE ("No overlays in buffer, removing all cached ones");
g_ptr_array_remove_range (self->overlays, 0, n_pending);
}
return;
}
GST_LOG_OBJECT (self, "Preparing overlays...");
/* Mark all old overlays as unused */
for (i = 0; i < self->overlays->len; i++) {
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->overlays, i);
overlay->used = FALSE;
}
num_overlays = gst_video_overlay_composition_n_rectangles (comp_meta->overlay);
for (i = 0; i < num_overlays; i++) {
GdkTexture *texture;
GstBuffer *comp_buffer;
GstVideoFrame *comp_frame;
GstVideoMeta *vmeta;
GstVideoInfo vinfo;
GstVideoOverlayRectangle *rectangle;
GstClapperGdkOverlay *overlay;
GstVideoOverlayFormatFlags flags, alpha_flags = 0;
gint comp_x, comp_y;
guint comp_width, comp_height, cached_index;
rectangle = gst_video_overlay_composition_get_rectangle (comp_meta->overlay, i);
if ((overlay = _get_cached_overlay (self->overlays, rectangle, &cached_index))) {
overlay->used = TRUE;
/* Place overlay at expected position */
if (i != cached_index) {
GST_LOG ("Rearranging overlay position: %u => %u", cached_index, i);
overlay = g_ptr_array_steal_index_fast (self->overlays, cached_index);
g_ptr_array_insert (self->overlays, i, overlay);
}
GST_TRACE ("Reusing cached overlay: %" GST_PTR_FORMAT, overlay);
continue;
}
if (G_UNLIKELY (!gst_video_overlay_rectangle_get_render_rectangle (rectangle,
&comp_x, &comp_y, &comp_width, &comp_height))) {
GST_WARNING ("Invalid overlay rectangle dimensions: %" GST_PTR_FORMAT, rectangle);
continue;
}
flags = gst_video_overlay_rectangle_get_flags (rectangle);
if (flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)
alpha_flags |= GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
comp_buffer = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rectangle, alpha_flags);
comp_frame = g_slice_new (GstVideoFrame);
/* Update overlay video info from video meta */
if ((vmeta = gst_buffer_get_video_meta (comp_buffer))) {
gst_video_info_set_format (&vinfo, vmeta->format, vmeta->width, vmeta->height);
vinfo.stride[0] = vmeta->stride[0];
}
if (G_UNLIKELY (!gst_video_frame_map (comp_frame, &vinfo, comp_buffer, GST_MAP_READ))) {
g_slice_free (GstVideoFrame, comp_frame);
return;
}
if ((texture = gst_video_frame_into_gdk_texture (
comp_frame, (GDestroyNotify) comp_frame_unmap_and_free))) {
overlay = g_slice_new (GstClapperGdkOverlay);
overlay->texture = texture;
overlay->rectangle = gst_video_overlay_rectangle_ref (rectangle);
overlay->x = comp_x;
overlay->y = comp_y;
overlay->width = comp_width;
overlay->height = comp_height;
overlay->used = TRUE;
GST_TRACE ("Created overlay: %"
GST_PTR_FORMAT ", x: %i, y: %i, width: %u, height: %u",
overlay, overlay->x, overlay->y, overlay->width, overlay->height);
g_ptr_array_insert (self->overlays, i, overlay);
}
}
/* Remove all overlays that are not going to be used */
for (i = self->overlays->len; i > 0; i--) {
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->overlays, i - 1);
if (!overlay->used) {
GST_TRACE ("Removing unused cached overlay: %" GST_PTR_FORMAT, overlay);
g_ptr_array_remove (self->overlays, overlay);
}
}
if (G_UNLIKELY (num_overlays != self->overlays->len)) {
GST_WARNING_OBJECT (self, "Some overlays could not be prepared, %u != %u",
num_overlays, self->overlays->len);
}
GST_LOG_OBJECT (self, "Prepared overlays: %u", self->overlays->len);
}
static gboolean
update_paintable_on_main_cb (GstClapperPaintable *self)
{
gboolean size_changed;
GST_CLAPPER_PAINTABLE_LOCK (self);
/* Check if we will need to invalidate size */
if ((size_changed = self->pending_resize))
self->pending_resize = FALSE;
gst_clear_buffer (&self->buffer);
self->buffer = self->pending_buffer;
self->pending_buffer = NULL;
self->draw_id = 0;
GST_CLAPPER_PAINTABLE_UNLOCK (self);
gst_clapper_paintable_prepare_overlays (self);
if (size_changed)
invalidate_paintable_size_internal (self);
GST_LOG_OBJECT (self, "Invalidate paintable contents");
gdk_paintable_invalidate_contents ((GdkPaintable *) self);
return G_SOURCE_REMOVE;
}
GstClapperPaintable *
gst_clapper_paintable_new (void)
{
return g_object_new (GST_TYPE_CLAPPER_PAINTABLE, NULL);
}
void
gst_clapper_paintable_set_widget (GstClapperPaintable *self, GtkWidget *widget)
{
g_weak_ref_set (&self->widget, widget);
}
void
gst_clapper_paintable_set_buffer (GstClapperPaintable *self, GstBuffer *buffer)
{
GST_CLAPPER_PAINTABLE_LOCK (self);
if (self->draw_id > 0) {
GST_CLAPPER_PAINTABLE_UNLOCK (self);
GST_TRACE ("Already have pending buffer, skipping %" GST_PTR_FORMAT, buffer);
return;
}
gst_buffer_replace (&self->pending_buffer, buffer);
self->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
(GSourceFunc) update_paintable_on_main_cb, self, NULL);
GST_CLAPPER_PAINTABLE_UNLOCK (self);
}
gboolean
gst_clapper_paintable_set_video_info (GstClapperPaintable *self, GstVideoInfo *v_info)
{
GST_CLAPPER_PAINTABLE_LOCK (self);
if (gst_video_info_is_equal (&self->v_info, v_info)) {
GST_CLAPPER_PAINTABLE_UNLOCK (self);
return TRUE;
}
/* Reject info if values would cause integer overflow */
if (G_UNLIKELY (!calculate_display_par (self, v_info))) {
GST_CLAPPER_PAINTABLE_UNLOCK (self);
return FALSE;
}
self->pending_resize = TRUE;
self->v_info = *v_info;
GST_CLAPPER_PAINTABLE_UNLOCK (self);
return TRUE;
}
void
gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *self,
gint par_n, gint par_d)
{
gboolean success;
GST_CLAPPER_PAINTABLE_LOCK (self);
/* No change */
if (self->par_n == par_n && self->par_d == par_d) {
GST_CLAPPER_PAINTABLE_UNLOCK (self);
return;
}
self->par_n = par_n;
self->par_d = par_d;
/* Check if we can accept new values. This will update
* display `ratio_num` and `ratio_den` only when successful */
success = calculate_display_par (self, &self->v_info);
/* If paintable update is queued, wait for it, otherwise invalidate
* size only for change to be applied even when paused */
if (!success || self->draw_id > 0) {
GST_CLAPPER_PAINTABLE_UNLOCK (self);
return;
}
self->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
(GSourceFunc) invalidate_paintable_size_on_main_cb, self, NULL);
GST_CLAPPER_PAINTABLE_UNLOCK (self);
return;
}
/*
* GdkPaintableInterface
*/
static void
gst_clapper_paintable_snapshot_internal (GstClapperPaintable *self,
GdkSnapshot *snapshot, gdouble width, gdouble height,
gint widget_width, gint widget_height)
{
GstMemory *memory;
GstMapInfo info;
gfloat scale_x, scale_y;
guint i;
scale_x = (gfloat) width / self->display_width;
scale_y = (gfloat) height / self->display_height;
/* Apply black borders when keeping aspect ratio */
if (scale_x == scale_y || abs (scale_x - scale_y) <= FLT_EPSILON) {
if (widget_height - height > 0) {
gdouble bar_height = (widget_height - height) / 2;
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, -bar_height));
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, height, width, bar_height + 0.5));
} else if (widget_width - width > 0) {
gdouble bar_width = (widget_width - width) / 2;
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, -bar_width, height));
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (width, 0, bar_width + 0.5, height));
}
}
/* Buffer is accessed only from main thread, so no locking required */
if (!self->buffer) {
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
return;
}
GST_TRACE ("Snapshot %" GST_PTR_FORMAT, self->buffer);
memory = gst_buffer_peek_memory (self->buffer, 0);
/* If we cannot map, just draw black */
if (G_UNLIKELY (!gst_memory_map (memory, &info, GST_MAP_READ))) {
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
GST_WARNING_OBJECT (self, "Could not map %" GST_PTR_FORMAT, self->buffer);
return;
}
gtk_snapshot_append_texture (snapshot,
GST_CLAPPER_GDK_MEMORY_CAST (memory)->texture,
&GRAPHENE_RECT_INIT (0, 0, width, height));
gst_memory_unmap (memory, &info);
/* FIXME: Draw black BG here when import format has-alpha */
//gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
/* Finally append prepared overlays */
for (i = 0; i < self->overlays->len; i++) {
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->overlays, i);
gtk_snapshot_append_texture (snapshot, overlay->texture,
&GRAPHENE_RECT_INIT (overlay->x * scale_x, overlay->y * scale_y,
overlay->width * scale_x, overlay->height * scale_y));
}
}
static void
gst_clapper_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot, gdouble width, gdouble height)
{
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
GtkWidget *widget;
gint widget_width = 0, widget_height = 0;
if ((widget = g_weak_ref_get (&self->widget))) {
widget_width = gtk_widget_get_width (widget);
widget_height = gtk_widget_get_height (widget);
g_object_unref (widget);
}
gst_clapper_paintable_snapshot_internal (self, snapshot,
width, height, widget_width, widget_height);
}
static GdkPaintable *
gst_clapper_paintable_get_current_image (GdkPaintable *paintable)
{
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
if (self->buffer) {
GtkSnapshot *snapshot;
GdkPaintable *ret;
snapshot = gtk_snapshot_new ();
/* Snapshot without widget size in order to get
* paintable without black borders */
gst_clapper_paintable_snapshot_internal (self, snapshot,
self->display_width, self->display_height, 0, 0);
if ((ret = gtk_snapshot_free_to_paintable (snapshot, NULL)))
return ret;
}
return gdk_paintable_new_empty (0, 0);
}
static gint
gst_clapper_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
return self->display_width;
}
static gint
gst_clapper_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
return self->display_height;
}
static gdouble
gst_clapper_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
{
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
return self->display_aspect_ratio;
}
static void
gst_clapper_paintable_iface_init (GdkPaintableInterface *iface)
{
iface->snapshot = gst_clapper_paintable_snapshot;
iface->get_current_image = gst_clapper_paintable_get_current_image;
iface->get_intrinsic_width = gst_clapper_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gst_clapper_paintable_get_intrinsic_height;
iface->get_intrinsic_aspect_ratio = gst_clapper_paintable_get_intrinsic_aspect_ratio;
}

View File

@@ -1,74 +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_PAINTABLE (gst_clapper_paintable_get_type())
G_DECLARE_FINAL_TYPE (GstClapperPaintable, gst_clapper_paintable, GST, CLAPPER_PAINTABLE, GObject)
#define GST_CLAPPER_PAINTABLE_CAST(obj) ((GstClapperPaintable *)(obj))
#define GST_CLAPPER_PAINTABLE_GET_LOCK(obj) (&GST_CLAPPER_PAINTABLE_CAST(obj)->lock)
#define GST_CLAPPER_PAINTABLE_LOCK(obj) g_mutex_lock (GST_CLAPPER_PAINTABLE_GET_LOCK(obj))
#define GST_CLAPPER_PAINTABLE_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_PAINTABLE_GET_LOCK(obj))
struct _GstClapperPaintable
{
GObject parent;
GMutex lock;
GstBuffer *pending_buffer, *buffer;
GPtrArray *overlays;
GstVideoInfo v_info;
GdkRGBA bg;
GWeakRef widget;
/* Sink properties */
gint par_n, par_d;
/* Resize */
gboolean pending_resize;
guint display_ratio_num;
guint display_ratio_den;
/* GdkPaintableInterface */
gint display_width;
gint display_height;
gdouble display_aspect_ratio;
/* Pending draw signal id */
guint draw_id;
};
GstClapperPaintable * gst_clapper_paintable_new (void);
void gst_clapper_paintable_set_widget (GstClapperPaintable *paintable, GtkWidget *widget);
void gst_clapper_paintable_set_buffer (GstClapperPaintable *paintable, GstBuffer *buffer);
gboolean gst_clapper_paintable_set_video_info (GstClapperPaintable *paintable, GstVideoInfo *v_info);
void gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *paintable, gint par_n, gint par_d);
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,7 @@
/* /*
* Copyright (C) 2022 Rafał Dzięgiel <rafostar.github@gmail.com> * GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020-2022 Rafał Dzięgiel <rafostar.github@gmail.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@@ -8,7 +10,7 @@
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details. * Library General Public License for more details.
* *
* You should have received a copy of the GNU Library General Public * You should have received a copy of the GNU Library General Public
@@ -24,47 +26,49 @@
#include <gst/video/gstvideosink.h> #include <gst/video/gstvideosink.h>
#include <gst/video/video.h> #include <gst/video/video.h>
#include "gstclapperpaintable.h" #include "gtkclapperobject.h"
G_BEGIN_DECLS G_BEGIN_DECLS
#define GST_TYPE_CLAPPER_SINK (gst_clapper_sink_get_type()) #define GST_TYPE_CLAPPER_SINK (gst_clapper_sink_get_type ())
G_DECLARE_FINAL_TYPE (GstClapperSink, gst_clapper_sink, GST, CLAPPER_SINK, GstVideoSink) #define GST_IS_CLAPPER_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_SINK))
#define GST_IS_CLAPPER_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_SINK))
#define GST_CLAPPER_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_SINK, GstClapperSinkClass))
#define GST_CLAPPER_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_SINK, GstClapperSinkClass))
#define GST_CLAPPER_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_SINK, GstClapperSink))
#define GST_CLAPPER_SINK_CAST(obj) ((GstClapperSink*)(obj))
#define GST_CLAPPER_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_SINK, GstClapperSinkClass)) typedef struct _GstClapperSink GstClapperSink;
#define GST_CLAPPER_SINK_CAST(obj) ((GstClapperSink *)(obj)) typedef struct _GstClapperSinkClass GstClapperSinkClass;
#define GST_CLAPPER_SINK_GET_LOCK(obj) (&GST_CLAPPER_SINK_CAST(obj)->lock) #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
#define GST_CLAPPER_SINK_LOCK(obj) g_mutex_lock (GST_CLAPPER_SINK_GET_LOCK(obj)) G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperSink, gst_object_unref)
#define GST_CLAPPER_SINK_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_SINK_GET_LOCK(obj)) #endif
struct _GstClapperSink struct _GstClapperSink
{ {
GstVideoSink parent; GstVideoSink parent;
GMutex lock;
GstClapperPaintable *paintable;
GstVideoInfo v_info; GstVideoInfo v_info;
GtkWidget *widget; GtkClapperObject *obj;
GtkWindow *window; GtkWindow *window;
gboolean presented_window; /* properties */
/* Properties */
gboolean force_aspect_ratio; gboolean force_aspect_ratio;
gint par_n, par_d; gint par_n, par_d;
gboolean keep_last_frame;
/* Position coords */ gint display_width, display_height;
gdouble last_pos_x; gulong widget_destroy_id, window_destroy_id;
gdouble last_pos_y; };
gulong widget_destroy_id; struct _GstClapperSinkClass
gulong window_destroy_id; {
GstVideoSinkClass parent_class;
}; };
GST_ELEMENT_REGISTER_DECLARE (clappersink); GST_ELEMENT_REGISTER_DECLARE (clappersink);
GType gst_clapper_sink_get_type (void);
G_END_DECLS G_END_DECLS

View File

@@ -2,7 +2,6 @@
* GStreamer * GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com> * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org> * Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
* Copyright (C) 2022 Rafał Dzięgiel <rafostar.github@gmail.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@@ -70,62 +69,3 @@ gst_gtk_invoke_on_main (GThreadFunc func, gpointer data)
return info.res; return info.res;
} }
/* For use with `GdkMemoryTexture` only! */
GdkMemoryFormat
gst_video_format_to_gdk_memory_format (GstVideoFormat format)
{
switch (format) {
case GST_VIDEO_FORMAT_RGBA64_LE:
case GST_VIDEO_FORMAT_RGBA64_BE:
return GDK_MEMORY_R16G16B16A16_PREMULTIPLIED;
case GST_VIDEO_FORMAT_RGBA:
return GDK_MEMORY_R8G8B8A8;
case GST_VIDEO_FORMAT_BGRA:
return GDK_MEMORY_B8G8R8A8;
case GST_VIDEO_FORMAT_ARGB:
return GDK_MEMORY_A8R8G8B8;
case GST_VIDEO_FORMAT_ABGR:
return GDK_MEMORY_A8B8G8R8;
case GST_VIDEO_FORMAT_RGBx:
return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
case GST_VIDEO_FORMAT_BGRx:
return GDK_MEMORY_B8G8R8A8_PREMULTIPLIED;
case GST_VIDEO_FORMAT_RGB:
return GDK_MEMORY_R8G8B8;
case GST_VIDEO_FORMAT_BGR:
return GDK_MEMORY_B8G8R8;
default:
break;
}
/* This should never happen as long as above switch statement
* is updated when new formats are added to caps */
g_assert_not_reached ();
/* Fallback format */
return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
}
GdkTexture *
gst_video_frame_into_gdk_texture (GstVideoFrame *frame, GDestroyNotify free_func)
{
GdkTexture *texture;
GBytes *bytes;
bytes = g_bytes_new_with_free_func (
GST_VIDEO_FRAME_PLANE_DATA (frame, 0),
GST_VIDEO_FRAME_HEIGHT (frame) * GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0),
free_func, frame);
texture = gdk_memory_texture_new (
GST_VIDEO_FRAME_WIDTH (frame),
GST_VIDEO_FRAME_HEIGHT (frame),
gst_video_format_to_gdk_memory_format (GST_VIDEO_FRAME_FORMAT (frame)),
bytes,
GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0));
g_bytes_unref (bytes);
return texture;
}

View File

@@ -2,7 +2,6 @@
* GStreamer * GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com> * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org> * Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
* Copyright (C) 2022 Rafał Dzięgiel <rafostar.github@gmail.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@@ -23,11 +22,5 @@
#pragma once #pragma once
#include <glib.h> #include <glib.h>
#include <gtk/gtk.h>
#include <gst/video/video.h>
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, GDestroyNotify free_func);

View File

@@ -21,25 +21,15 @@
#include "config.h" #include "config.h"
#endif #endif
#include "gstclapperimport.h"
#include "gstclapperglimport.h"
#include "gstclapperdmabufimport.h"
#include "gstclappersink.h" #include "gstclappersink.h"
#include "gstclappergdkmemory.h"
static gboolean static gboolean
plugin_init (GstPlugin *plugin) plugin_init (GstPlugin *plugin)
{ {
gboolean res = FALSE; gboolean res = FALSE;
res |= GST_ELEMENT_REGISTER (clapperimport, plugin);
res |= GST_ELEMENT_REGISTER (clapperglimport, plugin);
res |= GST_ELEMENT_REGISTER (clapperdmabufimport, plugin);
res |= GST_ELEMENT_REGISTER (clappersink, plugin); res |= GST_ELEMENT_REGISTER (clappersink, plugin);
if (res)
gst_clapper_gdk_memory_init_once ();
return res; return res;
} }

960
lib/gst/plugin/gtkclapperobject.c vendored Normal file
View File

@@ -0,0 +1,960 @@
/*
* GStreamer
* 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 <gst/gl/gstglfuncs.h>
#include <gst/allocators/gstdmabuf.h>
#include "gtkclapperobject.h"
#include "gstgtkutils.h"
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
#include <gdk/x11/gdkx.h>
#if GST_GL_HAVE_PLATFORM_EGL
#include <gst/gl/egl/gstgldisplay_egl.h>
#endif
#endif
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
#include <gdk/wayland/gdkwayland.h>
#include <gst/gl/wayland/gstgldisplay_wayland.h>
#endif
#if GST_GL_HAVE_PLATFORM_EGL
#include <gst/gl/egl/gsteglimage.h>
#endif
GST_DEBUG_CATEGORY (gst_debug_clapper_object);
#define GST_CAT_DEFAULT gst_debug_clapper_object
static void gtk_clapper_object_paintable_iface_init (GdkPaintableInterface *iface);
static void gtk_clapper_object_finalize (GObject *object);
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,
};
#define gtk_clapper_object_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GtkClapperObject, gtk_clapper_object, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_clapper_object_paintable_iface_init)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkclapperobject", 0,
"GTK Clapper Object"));
static void
gtk_clapper_object_class_init (GtkClapperObjectClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->finalize = gtk_clapper_object_finalize;
}
static void
gtk_clapper_object_init (GtkClapperObject *self)
{
self->last_pos_x = 0;
self->last_pos_y = 0;
self->picture = (GtkPicture *) gtk_picture_new ();
/* We cannot do textures of 0x0px size */
gtk_widget_set_size_request (GTK_WIDGET (self->picture), 1, 1);
/* Center instead of fill to not draw empty space into framebuffer */
gtk_widget_set_halign (GTK_WIDGET (self->picture), GTK_ALIGN_CENTER);
gtk_widget_set_valign (GTK_WIDGET (self->picture), GTK_ALIGN_CENTER);
gtk_picture_set_paintable (self->picture, GDK_PAINTABLE (self));
gst_video_info_init (&self->v_info);
gst_video_info_init (&self->pending_v_info);
g_weak_ref_init (&self->element, NULL);
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
gtk_clapper_object_finalize (GObject *object)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT (object);
if (self->draw_id)
g_source_remove (self->draw_id);
gst_buffer_replace (&self->pending_buffer, NULL);
gst_buffer_replace (&self->buffer, NULL);
g_mutex_clear (&self->lock);
g_weak_ref_clear (&self->element);
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}
static void
_gdk_gl_context_set_active (GtkClapperObject *self, gboolean activate)
{
/* We wrap around a GDK context, so we need to make
* both GTK and GStreamer aware of its active state */
if (activate) {
gdk_gl_context_make_current (self->gdk_context);
gst_gl_context_activate (self->wrapped_context, TRUE);
} else {
gst_gl_context_activate (self->wrapped_context, FALSE);
gdk_gl_context_clear_current ();
}
}
static GdkMemoryFormat
video_format_to_gdk_memory_format (GstVideoFormat format)
{
switch (format) {
case GST_VIDEO_FORMAT_BGR:
return GDK_MEMORY_B8G8R8;
case GST_VIDEO_FORMAT_RGB:
return GDK_MEMORY_R8G8B8;
case GST_VIDEO_FORMAT_BGRA:
return GDK_MEMORY_B8G8R8A8;
case GST_VIDEO_FORMAT_RGBA:
return GDK_MEMORY_R8G8B8A8;
case GST_VIDEO_FORMAT_ABGR:
return GDK_MEMORY_A8B8G8R8;
case GST_VIDEO_FORMAT_ARGB:
return GDK_MEMORY_A8R8G8B8;
case GST_VIDEO_FORMAT_BGRx:
return GDK_MEMORY_B8G8R8A8_PREMULTIPLIED;
case GST_VIDEO_FORMAT_RGBx:
return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
case GST_VIDEO_FORMAT_RGBA64_LE:
case GST_VIDEO_FORMAT_RGBA64_BE:
return GDK_MEMORY_R16G16B16A16_PREMULTIPLIED;
default:
g_assert_not_reached ();
}
/* Number not belonging to any format */
return GDK_MEMORY_N_FORMATS;
}
static void
gtk_clapper_object_bind_buffer (GtkClapperObject *self)
{
const GstGLFuncs *gl = self->wrapped_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
gtk_clapper_object_unbind_buffer (GtkClapperObject *self)
{
const GstGLFuncs *gl = self->wrapped_context->gl_vtable;
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
gl->DisableVertexAttribArray (self->attr_position);
gl->DisableVertexAttribArray (self->attr_texture);
}
static void
gtk_clapper_object_init_redisplay (GtkClapperObject *self)
{
GstGLSLStage *frag_stage, *vert_stage;
GError *error = NULL;
gchar *frag_str;
const GstGLFuncs *gl;
if (self->gst_tex_target != GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
return;
if (!((vert_stage = gst_glsl_stage_new_with_string (self->wrapped_context,
GL_VERTEX_SHADER, GST_GLSL_VERSION_NONE,
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY,
gst_gl_shader_string_vertex_mat4_vertex_transform)))) {
GST_ERROR ("Failed to retrieve vertex shader for texture target");
return;
}
frag_str = gst_gl_shader_string_fragment_external_oes_get_default (
self->wrapped_context, GST_GLSL_VERSION_NONE,
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY);
frag_stage = gst_glsl_stage_new_with_string (self->wrapped_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_ERROR ("Failed to retrieve fragment shader for texture target");
return;
}
if (!((self->shader = gst_gl_shader_new_link_with_stages (self->wrapped_context,
&error, vert_stage, frag_stage, NULL)))) {
GST_ERROR ("Failed to initialize shader: %s", error->message);
g_clear_error (&error);
gst_object_unref (vert_stage);
gst_object_unref (frag_stage);
return;
}
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 = self->wrapped_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) {
gtk_clapper_object_bind_buffer (self);
gl->BindVertexArray (0);
}
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
self->initiated = TRUE;
}
static gboolean
_dmabuf_into_texture (GtkClapperObject *self, gint *fds, gsize *offsets)
{
GstEGLImage *image;
const GstGLFuncs *gl;
image = gst_egl_image_from_dmabuf_direct_target (self->wrapped_context,
fds, offsets, &self->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 (&self->v_info) == 1)
image = gst_egl_image_from_dmabuf (self->wrapped_context,
fds[0], &self->v_info, 0, offsets[0]);
/* Still no image? Give up then */
if (!image)
return FALSE;
gl = self->wrapped_context->gl_vtable;
if (!self->texture_id)
gl->GenTextures (1, &self->texture_id);
gl->BindTexture (self->gl_tex_target, self->texture_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
_ext_texture_into_2d (GtkClapperObject *self, guint tex_width, guint tex_height)
{
GLuint framebuffer, new_texture_id;
GLenum status;
const GstGLFuncs *gl;
if (!self->initiated)
gtk_clapper_object_init_redisplay (self);
gl = self->wrapped_context->gl_vtable;
gl->GenFramebuffers (1, &framebuffer);
gl->BindFramebuffer (GL_FRAMEBUFFER, framebuffer);
gl->GenTextures (1, &new_texture_id);
gl->BindTexture (GL_TEXTURE_2D, new_texture_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_width, tex_height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, new_texture_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, &new_texture_id);
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
gl->DeleteFramebuffers (1, &framebuffer);
return FALSE;
}
gl->Viewport (0, 0, tex_width, tex_height);
gst_gl_shader_use (self->shader);
if (gl->BindVertexArray)
gl->BindVertexArray (self->vao);
gtk_clapper_object_bind_buffer (self);
gl->ActiveTexture (GL_TEXTURE0);
gl->BindTexture (self->gl_tex_target, self->texture_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
gtk_clapper_object_unbind_buffer (self);
gl->BindTexture (self->gl_tex_target, 0);
/* Replace external OES texture with new 2D one */
gl->DeleteTextures (1, &self->texture_id);
self->texture_id = new_texture_id;
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
gl->DeleteFramebuffers (1, &framebuffer);
return TRUE;
}
static GdkTexture *
gtk_clapper_object_import_dmabuf (GtkClapperObject *self, gint *fds, gsize *offsets)
{
GdkTexture *texture;
guint tex_width, tex_height;
_gdk_gl_context_set_active (self, TRUE);
if (!_dmabuf_into_texture (self, fds, offsets)) {
_gdk_gl_context_set_active (self, FALSE);
return NULL;
}
switch (self->gst_tex_target) {
case GST_GL_TEXTURE_TARGET_2D:
tex_width = GST_VIDEO_INFO_WIDTH (&self->v_info);
tex_height = GST_VIDEO_INFO_HEIGHT (&self->v_info);
break;
case GST_GL_TEXTURE_TARGET_EXTERNAL_OES:{
GtkWidget *widget = (GtkWidget *) self->picture;
gint scale;
scale = gtk_widget_get_scale_factor (widget);
tex_width = gtk_widget_get_width (widget) * scale;
tex_height = gtk_widget_get_height (widget) * scale;
if (G_LIKELY (_ext_texture_into_2d (self, tex_width, tex_height)))
break;
return NULL;
}
default:
g_assert_not_reached ();
return NULL;
}
texture = gdk_gl_texture_new (self->gdk_context,
self->texture_id, tex_width, tex_height, NULL, NULL);
_gdk_gl_context_set_active (self, FALSE);
return texture;
}
typedef gboolean (*MemTypeCheckFunc) (gpointer data);
static gboolean
buffer_memory_type_check (GstBuffer *buffer, MemTypeCheckFunc func)
{
guint i, n_mems;
n_mems = gst_buffer_n_memory (buffer);
for (i = 0; i < n_mems; i++) {
if (!func (gst_buffer_peek_memory (buffer, i)))
return FALSE;
}
return n_mems > 0;
}
static gboolean
verify_dmabuf_memory (GtkClapperObject *self, guint n_planes,
gint *fds, gsize *offsets)
{
guint i;
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 (&self->v_info, NULL, i);
if (!gst_buffer_find_memory (self->buffer,
GST_VIDEO_INFO_PLANE_OFFSET (&self->v_info, i),
plane_size, &mem_idx, &length, &mem_skip)) {
GST_DEBUG_OBJECT (self, "Could not find memory %u", i);
return FALSE;
}
/* We can't have more then one DMABuf per plane */
if (length != 1) {
GST_DEBUG_OBJECT (self, "Data for plane %u spans %u memories",
i, length);
return FALSE;
}
memory = gst_buffer_peek_memory (self->buffer, mem_idx);
offsets[i] = memory->offset + mem_skip;
fds[i] = gst_dmabuf_memory_get_fd (memory);
}
return TRUE;
}
static GdkTexture *
obtain_texture_from_current_buffer (GtkClapperObject *self)
{
GdkTexture *texture = NULL;
GstVideoFrame frame;
/* DMABuf */
if (buffer_memory_type_check (self->buffer, (MemTypeCheckFunc) gst_is_dmabuf_memory)) {
gsize offsets[GST_VIDEO_MAX_PLANES];
gint fds[GST_VIDEO_MAX_PLANES];
guint n_planes;
n_planes = GST_VIDEO_INFO_N_PLANES (&self->v_info);
if (!verify_dmabuf_memory (self, n_planes, fds, offsets)) {
GST_ERROR ("DMABuf memory is invalid");
return NULL;
}
if (!((texture = gtk_clapper_object_import_dmabuf (self, fds, offsets))))
GST_ERROR ("Could not create texture from DMABuf");
return texture;
}
/* GL Memory */
if (buffer_memory_type_check (self->buffer, (MemTypeCheckFunc) gst_is_gl_memory)) {
if (gst_video_frame_map (&frame, &self->v_info, self->buffer, GST_MAP_READ | GST_MAP_GL)) {
GST_FIXME_OBJECT (self, "Handle GstGLMemory");
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),
NULL, NULL);
gst_video_frame_unmap (&frame);
}
return texture;
}
/* RAW */
if (gst_video_frame_map (&frame, &self->v_info, self->buffer, GST_MAP_READ)) {
GBytes *bytes;
/* Our ref on a buffer together with 2 buffers pool ensures that
* current buffer will not be freed while another one is prepared */
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),
NULL, NULL);
texture = gdk_memory_texture_new (
GST_VIDEO_FRAME_WIDTH (&frame),
GST_VIDEO_FRAME_HEIGHT (&frame),
video_format_to_gdk_memory_format (GST_VIDEO_FRAME_FORMAT (&frame)),
bytes,
GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0));
g_bytes_unref (bytes);
gst_video_frame_unmap (&frame);
}
return texture;
}
static gboolean
calculate_display_par (GtkClapperObject *self, GstVideoInfo *info)
{
gboolean success;
gint width, height;
gint par_n, par_d;
gint display_par_n, display_par_d;
width = GST_VIDEO_INFO_WIDTH (info);
height = GST_VIDEO_INFO_HEIGHT (info);
par_n = GST_VIDEO_INFO_PAR_N (info);
par_d = GST_VIDEO_INFO_PAR_D (info);
if (!par_n)
par_n = 1;
/* User set props */
if (self->par_n != 0 && self->par_d != 0) {
display_par_n = self->par_n;
display_par_d = self->par_d;
} else {
display_par_n = 1;
display_par_d = 1;
}
if ((success = gst_video_calculate_display_ratio (&self->display_ratio_num,
&self->display_ratio_den, width, height, par_n, par_d, display_par_n,
display_par_d))) {
GST_LOG ("PAR: %u/%u, DAR: %u/%u", par_n, par_d, display_par_n, display_par_d);
}
return success;
}
static void
update_display_size (GtkClapperObject *self)
{
guint display_ratio_num, display_ratio_den;
gint width, height;
display_ratio_num = self->display_ratio_num;
display_ratio_den = self->display_ratio_den;
width = GST_VIDEO_INFO_WIDTH (&self->v_info);
height = GST_VIDEO_INFO_HEIGHT (&self->v_info);
if (height % display_ratio_den == 0) {
GST_DEBUG ("Keeping video height");
self->display_width = (guint)
gst_util_uint64_scale_int (height, display_ratio_num, display_ratio_den);
self->display_height = height;
} else if (width % display_ratio_num == 0) {
GST_DEBUG ("Keeping video width");
self->display_width = width;
self->display_height = (guint)
gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
} else {
GST_DEBUG ("Approximating while keeping video height");
self->display_width = (guint)
gst_util_uint64_scale_int (height, display_ratio_num, display_ratio_den);
self->display_height = height;
}
self->display_aspect_ratio = ((gdouble) self->display_width
/ (gdouble) self->display_height);
GST_DEBUG ("Scaling to %dx%d", self->display_width, self->display_height);
}
static void
update_paintable (GtkClapperObject *self, GdkPaintable *paintable)
{
/* No change, so discard the new one */
if (self->paintable == paintable) {
if (paintable)
g_object_unref (paintable);
return;
}
if (self->paintable)
g_object_unref (self->paintable);
self->paintable = paintable;
if (self->pending_resize) {
update_display_size (self);
gdk_paintable_invalidate_size ((GdkPaintable *) self);
self->pending_resize = FALSE;
}
gdk_paintable_invalidate_contents ((GdkPaintable *) self);
}
static gboolean
draw_on_main_cb (GtkClapperObject *self)
{
GdkTexture *texture;
GTK_CLAPPER_OBJECT_LOCK (self);
/* Replace used buffer and set matching v_info */
gst_buffer_replace (&self->buffer, self->pending_buffer);
self->v_info = self->pending_v_info;
texture = obtain_texture_from_current_buffer (self);
if (texture)
update_paintable (self, (GdkPaintable *) texture);
self->draw_id = 0;
GTK_CLAPPER_OBJECT_UNLOCK (self);
return G_SOURCE_REMOVE;
}
void
gtk_clapper_object_set_element (GtkClapperObject *self, GstElement *element)
{
g_weak_ref_set (&self->element, element);
}
gboolean
gtk_clapper_object_set_format (GtkClapperObject *self, GstVideoInfo *v_info)
{
GTK_CLAPPER_OBJECT_LOCK (self);
if (gst_video_info_is_equal (&self->pending_v_info, v_info)) {
GTK_CLAPPER_OBJECT_UNLOCK (self);
return TRUE;
}
if (!calculate_display_par (self, v_info)) {
GTK_CLAPPER_OBJECT_UNLOCK (self);
return FALSE;
}
self->pending_resize = TRUE;
self->pending_v_info = *v_info;
GTK_CLAPPER_OBJECT_UNLOCK (self);
return TRUE;
}
void
gtk_clapper_object_set_buffer (GtkClapperObject *self, GstBuffer *buffer)
{
GstVideoMeta *meta = NULL;
GTK_CLAPPER_OBJECT_LOCK (self);
gst_buffer_replace (&self->pending_buffer, buffer);
if (self->draw_id) {
GTK_CLAPPER_OBJECT_UNLOCK (self);
return;
}
if (self->pending_buffer)
meta = gst_buffer_get_video_meta (self->pending_buffer);
/* Update pending info from video meta */
if (meta) {
guint i;
GST_VIDEO_INFO_WIDTH (&self->pending_v_info) = meta->width;
GST_VIDEO_INFO_HEIGHT (&self->pending_v_info) = meta->height;
for (i = 0; i < meta->n_planes; i++) {
GST_VIDEO_INFO_PLANE_OFFSET (&self->pending_v_info, i) = meta->offset[i];
GST_VIDEO_INFO_PLANE_STRIDE (&self->pending_v_info, i) = meta->stride[i];
}
}
self->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
(GSourceFunc) draw_on_main_cb, self, NULL);
GTK_CLAPPER_OBJECT_UNLOCK (self);
}
GtkClapperObject *
gtk_clapper_object_new (void)
{
return g_object_new (GTK_TYPE_CLAPPER_OBJECT, NULL);
}
GtkWidget *
gtk_clapper_object_get_widget (GtkClapperObject *self)
{
return (GtkWidget *) self->picture;
}
static gboolean
wrap_current_gl (GstGLDisplay *display, GstGLPlatform platform, GstGLContext **context)
{
GstGLAPI gl_api = GST_GL_API_NONE;
guint gl_major = 0, gl_minor = 0;
gl_api = gst_gl_context_get_current_gl_api (platform, &gl_major, &gl_minor);
if (gl_api) {
const gboolean is_es = gl_api & (GST_GL_API_GLES1 | GST_GL_API_GLES2);
gchar *gl_api_str = gst_gl_api_to_string (gl_api);
guintptr gl_handle = 0;
GST_INFO ("Using GL API: %s, ver: %d.%d", gl_api_str, gl_major, gl_minor);
g_free (gl_api_str);
if (is_es && platform == GST_GL_PLATFORM_EGL && !g_getenv ("GST_GL_API")) {
GST_DEBUG ("No GST_GL_API env and GTK is using EGL GLES2, enforcing it");
gst_gl_display_filter_gl_api (display, GST_GL_API_GLES2);
}
gl_handle = gst_gl_context_get_current_gl_context (platform);
if (gl_handle) {
if ((*context = gst_gl_context_new_wrapped (display,
gl_handle, platform, gl_api)))
return TRUE;
}
}
return FALSE;
}
static void
retrieve_gl_context_on_main (GtkClapperObject *self)
{
GdkDisplay *gdk_display;
GstGLPlatform platform = GST_GL_PLATFORM_NONE;
GError *error = NULL;
gst_clear_object (&self->wrapped_context);
g_clear_object (&self->gdk_context);
gtk_widget_realize (GTK_WIDGET (self->picture));
if (!((self->gdk_context = gdk_surface_create_gl_context (gtk_native_get_surface (
gtk_widget_get_native (GTK_WIDGET (self->picture))), &error)))) {
GST_ERROR_OBJECT (self, "Error creating Gdk GL context: %s",
error ? error->message : "No error set by Gdk");
g_clear_error (&error);
return;
}
//gdk_gl_context_set_use_es (self->gdk_context, TRUE);
//gdk_gl_context_realize (self->gdk_context, &error);
gdk_display = gdk_gl_context_get_display (self->gdk_context);
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
if (GDK_IS_X11_DISPLAY (gdk_display)) {
gpointer display_ptr;
#if GST_GL_HAVE_PLATFORM_EGL && GTK_CHECK_VERSION(4,3,1)
display_ptr = gdk_x11_display_get_egl_display (gdk_display);
if (display_ptr)
self->display = (GstGLDisplay *)
gst_gl_display_egl_new_with_egl_display (display_ptr);
#endif
}
#endif
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) {
struct wl_display *wayland_display =
gdk_wayland_display_get_wl_display (gdk_display);
self->display = (GstGLDisplay *)
gst_gl_display_wayland_new_with_display (wayland_display);
}
#endif
if (G_UNLIKELY (!self->display)) {
GST_WARNING_OBJECT (self, "Unknown Gdk display!");
self->display = gst_gl_display_new ();
}
#if GST_GL_HAVE_PLATFORM_EGL
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
if (GST_IS_GL_DISPLAY_WAYLAND (self->display)) {
platform = GST_GL_PLATFORM_EGL;
GST_DEBUG ("Using EGL on Wayland");
goto have_platform;
}
#endif
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
if (GST_IS_GL_DISPLAY_EGL (self->display)) {
platform = GST_GL_PLATFORM_EGL;
GST_DEBUG ("Using EGL on x11");
goto have_platform;
}
#endif
#endif /* GST_GL_HAVE_PLATFORM_EGL */
GST_ERROR ("Unsupported GL platform");
return;
have_platform:
g_object_ref (self->gdk_context);
gdk_gl_context_make_current (self->gdk_context);
if (!wrap_current_gl (self->display, platform, &self->wrapped_context)) {
GST_WARNING ("Could not retrieve Gdk OpenGL context");
return;
}
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 retrieve Gdk context info: %s", error->message);
g_clear_error (&error);
g_clear_object (&self->wrapped_context);
}
/* Deactivate in both places */
_gdk_gl_context_set_active (self, FALSE);
}
gboolean
gtk_clapper_object_init_winsys (GtkClapperObject *self)
{
GError *error = NULL;
GTK_CLAPPER_OBJECT_LOCK (self);
if (self->display && self->gdk_context && self->wrapped_context) {
GST_TRACE ("Have already initialized contexts");
GTK_CLAPPER_OBJECT_UNLOCK (self);
return TRUE;
}
if (!self->wrapped_context) {
GTK_CLAPPER_OBJECT_UNLOCK (self);
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback) retrieve_gl_context_on_main, self);
GTK_CLAPPER_OBJECT_LOCK (self);
}
if (!GST_IS_GL_CONTEXT (self->wrapped_context)) {
GST_FIXME ("Could not retrieve Gdk GL context");
GTK_CLAPPER_OBJECT_UNLOCK (self);
return FALSE;
}
GTK_CLAPPER_OBJECT_UNLOCK (self);
return TRUE;
}
/*
* GdkPaintableInterface
*/
static void
gtk_clapper_object_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot, gdouble width, gdouble height)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
if (self->paintable)
gdk_paintable_snapshot (self->paintable, snapshot, width, height);
}
static GdkPaintable *
gtk_clapper_object_paintable_get_current_image (GdkPaintable *paintable)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
return (self->paintable)
? g_object_ref (self->paintable)
: gdk_paintable_new_empty (0, 0);
}
static gint
gtk_clapper_object_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
return self->display_width;
}
static gint
gtk_clapper_object_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
return self->display_height;
}
static gdouble
gtk_clapper_object_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
return self->display_aspect_ratio;
}
static void
gtk_clapper_object_paintable_iface_init (GdkPaintableInterface *iface)
{
iface->snapshot = gtk_clapper_object_paintable_snapshot;
iface->get_current_image = gtk_clapper_object_paintable_get_current_image;
iface->get_intrinsic_width = gtk_clapper_object_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gtk_clapper_object_paintable_get_intrinsic_height;
iface->get_intrinsic_aspect_ratio = gtk_clapper_object_paintable_get_intrinsic_aspect_ratio;
}

139
lib/gst/plugin/gtkclapperobject.h vendored Normal file
View File

@@ -0,0 +1,139 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020-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>
#include <gst/gl/gl.h>
#include <gst/gl/gstglfuncs.h>
G_BEGIN_DECLS
#define GTK_TYPE_CLAPPER_OBJECT (gtk_clapper_object_get_type ())
#define GTK_IS_CLAPPER_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CLAPPER_OBJECT))
#define GTK_IS_CLAPPER_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CLAPPER_OBJECT))
#define GTK_CLAPPER_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CLAPPER_OBJECT, GtkClapperObjectClass))
#define GTK_CLAPPER_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CLAPPER_OBJECT, GtkClapperObjectClass))
#define GTK_CLAPPER_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CLAPPER_OBJECT, GtkClapperObject))
#define GTK_CLAPPER_OBJECT_CAST(obj) ((GtkClapperObject*)(obj))
#define GTK_CLAPPER_OBJECT_LOCK(w) g_mutex_lock(&((GtkClapperObject*)(w))->lock)
#define GTK_CLAPPER_OBJECT_UNLOCK(w) g_mutex_unlock(&((GtkClapperObject*)(w))->lock)
typedef struct _GtkClapperObject GtkClapperObject;
typedef struct _GtkClapperObjectClass GtkClapperObjectClass;
struct _GtkClapperObject
{
GObject parent;
GtkPicture *picture;
GdkPaintable *paintable;
GstGLDisplay *display;
GdkGLContext *gdk_context;
GstGLContext *wrapped_context;
GstGLContext *gst_context;
/* properties */
gboolean force_aspect_ratio;
gint par_n, par_d;
gboolean keep_last_frame;
gint display_width;
gint display_height;
gdouble display_aspect_ratio;
/* Object dimensions */
gint scaled_width;
gint scaled_height;
/* Position coords */
gdouble last_pos_x;
gdouble last_pos_y;
gboolean negotiated;
gboolean ignore_buffers;
GstBuffer *pending_buffer;
GstBuffer *buffer;
GstVideoInfo pending_v_info;
GstVideoInfo v_info;
guint texture_id, oes_texture_id, next_texture_id;
/* resize */
gboolean pending_resize;
guint display_ratio_num;
guint display_ratio_den;
/*< private >*/
GMutex lock;
GWeakRef element;
/* event controllers */
GtkEventController *key_controller;
GtkEventController *motion_controller;
GtkGesture *click_gesture;
/* Pending draw idles callback */
guint draw_id;
GstGLTextureTarget gst_tex_target;
guint gl_tex_target;
GstGLShader *shader;
GLuint vao;
GLuint vertex_buffer;
GLint attr_position;
GLint attr_texture;
gboolean initiated;
guint frame_buffer;
};
struct _GtkClapperObjectClass
{
GObjectClass parent_class;
};
GType gtk_clapper_object_get_type (void);
GtkClapperObject * gtk_clapper_object_new (void);
GtkWidget * gtk_clapper_object_get_widget (GtkClapperObject *object);
gboolean gtk_clapper_object_init_winsys (GtkClapperObject *object);
gboolean gtk_clapper_object_set_format (GtkClapperObject *object, GstVideoInfo *v_info);
void gtk_clapper_object_set_buffer (GtkClapperObject *object, GstBuffer *buffer);
void gtk_clapper_object_set_element (GtkClapperObject *object, GstElement *element);
G_END_DECLS

View File

@@ -47,16 +47,8 @@ if not gtk4_dep.version().version_compare('>=4.6.0')
endif endif
gst_clapper_plugin_sources = [ gst_clapper_plugin_sources = [
'gstclappergdkmemory.c',
'gstclappergdkbufferpool.c',
'gstclapperbaseimport.c',
'gstclapperglbaseimport.c',
'gstclapperdmabufbaseimport.c',
'gstclapperimport.c',
'gstclapperglimport.c',
'gstclapperdmabufimport.c',
'gstclappersink.c', 'gstclappersink.c',
'gstclapperpaintable.c', 'gtkclapperobject.c',
'gstgtkutils.c', 'gstgtkutils.c',
'gstplugin.c', 'gstplugin.c',
] ]

6
lib/meson.build vendored
View File

@@ -1,5 +1,5 @@
glib_req = '>= 2.56.0' glib_req = '>= 2.56.0'
gst_req = '>= 1.20.0' gst_req = '>= 1.19.1'
api_version = '1.0' api_version = '1.0'
libversion = meson.project_version() libversion = meson.project_version()
@@ -132,7 +132,7 @@ cdata.set('SIZEOF_SHORT', cc.sizeof('short'))
cdata.set('SIZEOF_VOIDP', cc.sizeof('void*')) cdata.set('SIZEOF_VOIDP', cc.sizeof('void*'))
cdata.set_quoted('VERSION', libversion) cdata.set_quoted('VERSION', libversion)
cdata.set_quoted('PACKAGE', 'clapper') cdata.set_quoted('PACKAGE', 'gst-plugins-clapper')
cdata.set_quoted('PACKAGE_VERSION', libversion) cdata.set_quoted('PACKAGE_VERSION', libversion)
cdata.set_quoted('PACKAGE_BUGREPORT', 'https://github.com/Rafostar/clapper/issues/new') cdata.set_quoted('PACKAGE_BUGREPORT', 'https://github.com/Rafostar/clapper/issues/new')
cdata.set_quoted('PACKAGE_NAME', 'GStreamer Clapper Libs') cdata.set_quoted('PACKAGE_NAME', 'GStreamer Clapper Libs')
@@ -184,7 +184,7 @@ foreach extra_arg : warning_flags
endif endif
endforeach endforeach
cdata.set_quoted('GST_PACKAGE_NAME', 'gst-plugin-clapper') cdata.set_quoted('GST_PACKAGE_NAME', 'GStreamer Plugins Clapper')
cdata.set_quoted('GST_PACKAGE_ORIGIN', 'https://github.com/Rafostar/clapper') cdata.set_quoted('GST_PACKAGE_ORIGIN', 'https://github.com/Rafostar/clapper')
# Mandatory GST deps # Mandatory GST deps

View File

@@ -3,10 +3,6 @@
"runtime": "org.gnome.Platform", "runtime": "org.gnome.Platform",
"runtime-version": "master", "runtime-version": "master",
"sdk": "org.gnome.Sdk", "sdk": "org.gnome.Sdk",
"sdk-extensions": [
"org.freedesktop.Sdk.Extension.rust-nightly",
"org.freedesktop.Sdk.Extension.llvm13"
],
"command": "com.github.rafostar.Clapper", "command": "com.github.rafostar.Clapper",
"finish-args": [ "finish-args": [
"--share=ipc", "--share=ipc",
@@ -23,10 +19,6 @@
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0", "--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0",
"--env=GST_VAAPI_ALL_DRIVERS=1" "--env=GST_VAAPI_ALL_DRIVERS=1"
], ],
"build-options": {
"append-path": "/usr/lib/sdk/rust-nightly/bin:/usr/lib/sdk/llvm13/bin",
"prepend-ld-library-path": "/usr/lib/sdk/llvm13/lib"
},
"modules": [ "modules": [
"flathub/shared-modules/gudev/gudev.json", "flathub/shared-modules/gudev/gudev.json",
"flathub/lib/libsass.json", "flathub/lib/libsass.json",
@@ -40,8 +32,8 @@
"flathub/lib/libdvdnav.json", "flathub/lib/libdvdnav.json",
"flathub/lib/libass.json", "flathub/lib/libass.json",
"flathub/lib/ffmpeg.json", "flathub/lib/ffmpeg.json",
"testing/libsoup3.json",
"testing/gstreamer.json", "testing/gstreamer.json",
"testing/gst-plugins-rs.json",
"testing/gtuber.json", "testing/gtuber.json",
{ {
"name": "clapper", "name": "clapper",

View File

@@ -1,7 +1,7 @@
{ {
"app-id": "com.github.rafostar.Clapper", "app-id": "com.github.rafostar.Clapper",
"runtime": "org.gnome.Platform", "runtime": "org.gnome.Platform",
"runtime-version": "42", "runtime-version": "41",
"sdk": "org.gnome.Sdk", "sdk": "org.gnome.Sdk",
"command": "com.github.rafostar.Clapper", "command": "com.github.rafostar.Clapper",
"finish-args": [ "finish-args": [
@@ -33,8 +33,9 @@
"flathub/lib/libass.json", "flathub/lib/libass.json",
"flathub/lib/ffmpeg.json", "flathub/lib/ffmpeg.json",
"flathub/lib/uchardet.json", "flathub/lib/uchardet.json",
"testing/libsoup3.json",
"flathub/gstreamer-1.0/gstreamer.json", "flathub/gstreamer-1.0/gstreamer.json",
"testing/gtk4.json", "flathub/lib/gtk4.json",
"flathub/lib/libadwaita.json", "flathub/lib/libadwaita.json",
"testing/gtuber.json", "testing/gtuber.json",
{ {

View File

@@ -1,23 +0,0 @@
{
"name": "gst-plugins-rs",
"buildsystem": "simple",
"build-options": {
"build-args": [
"--share=network"
],
"env": {
"CARGO_HOME": "/run/build/gst-plugins-rs/cargo"
}
},
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git",
"branch": "main"
}
],
"build-commands": [
"cargo install cargo-c",
"cargo cinstall --prefix=/app -p gst-plugin-dav1d"
]
}

View File

@@ -2,7 +2,7 @@
"name": "gstreamer", "name": "gstreamer",
"buildsystem": "meson", "buildsystem": "meson",
"config-opts": [ "config-opts": [
"--wrap-mode=nodownload", "--wrap-mode=nofallback",
"-Dbase=enabled", "-Dbase=enabled",
"-Dgood=enabled", "-Dgood=enabled",
@@ -26,12 +26,26 @@
"-Dgpl=enabled", "-Dgpl=enabled",
"-Dgstreamer:benchmarks=disabled", "-Dgstreamer:benchmarks=disabled",
"-Dgstreamer:gobject-cast-checks=disabled",
"-Dgstreamer:glib-asserts=disabled",
"-Dgstreamer:glib-checks=disabled",
"-Dgstreamer:extra-checks=disabled",
"-Dgst-plugins-base:gobject-cast-checks=disabled",
"-Dgst-plugins-base:glib-asserts=disabled",
"-Dgst-plugins-base:glib-checks=disabled",
"-Dgst-plugins-base:gl_api=opengl,gles2", "-Dgst-plugins-base:gl_api=opengl,gles2",
"-Dgst-plugins-base:gl_platform=egl,glx", "-Dgst-plugins-base:gl_platform=egl,glx",
"-Dgst-plugins-good:gobject-cast-checks=disabled",
"-Dgst-plugins-good:glib-asserts=disabled",
"-Dgst-plugins-good:glib-checks=disabled",
"-Dgst-plugins-good:gtk3=disabled", "-Dgst-plugins-good:gtk3=disabled",
"-Dgst-plugins-bad:gobject-cast-checks=disabled",
"-Dgst-plugins-bad:glib-asserts=disabled",
"-Dgst-plugins-bad:glib-checks=disabled",
"-Dgst-plugins-bad:extra-checks=disabled",
"-Dgst-plugins-bad:vulkan=disabled", "-Dgst-plugins-bad:vulkan=disabled",
"-Dgst-plugins-bad:webrtc=disabled", "-Dgst-plugins-bad:webrtc=disabled",
"-Dgst-plugins-bad:wasapi=disabled", "-Dgst-plugins-bad:wasapi=disabled",
@@ -43,14 +57,16 @@
"-Dgst-plugins-bad:v4l2codecs=enabled", "-Dgst-plugins-bad:v4l2codecs=enabled",
"-Dgst-plugins-bad:va=enabled", "-Dgst-plugins-bad:va=enabled",
"-Dgst-plugins-ugly:gobject-cast-checks=disabled",
"-Dgst-plugins-ugly:glib-asserts=disabled",
"-Dgst-plugins-ugly:glib-checks=disabled",
"-Dgst-plugins-ugly:mpeg2dec=enabled" "-Dgst-plugins-ugly:mpeg2dec=enabled"
], ],
"sources": [ "sources": [
{ {
"type": "git", "type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gstreamer.git", "url": "https://gitlab.freedesktop.org/gstreamer/gstreamer.git",
"branch": "main", "branch": "main"
"disable-submodules": true
} }
] ]
} }

View File

@@ -1,31 +0,0 @@
From b413ee2c7d458c7005d3d3d1da8822cd86893ac0 Mon Sep 17 00:00:00 2001
From: Rafostar <40623528+Rafostar@users.noreply.github.com>
Date: Fri, 4 Dec 2020 19:25:34 +0100
Subject: [PATCH] popover: Call unrealize on hide
When popover is shown "realize" method is called which creates a new
surface for popup. Unfortunately this causes performance drop on Wayland until that
surface is destroyed what happens inside "unrealize" method during popover destruction.
This commit changes default behavior in a way that surface will be destroyed
when popover is closed and app will ragain the performance it lost when
popover was shown.
---
gtk/gtkpopover.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c
index 504dcd6cc1..a7a764d483 100644
--- a/gtk/gtkpopover.c
+++ b/gtk/gtkpopover.c
@@ -951,6 +951,7 @@ gtk_popover_hide (GtkWidget *widget)
gtk_popover_set_mnemonics_visible (GTK_POPOVER (widget), FALSE);
_gtk_widget_set_visible_flag (widget, FALSE);
+ gtk_widget_unrealize (widget);
gtk_widget_unmap (widget);
g_signal_emit (widget, signals[CLOSED], 0);
}
--
GitLab

View File

@@ -1,37 +0,0 @@
{
"name": "gtk",
"buildsystem": "meson",
"build-options": {
"build-args": [
"--share=network"
]
},
"config-opts": [
"--buildtype=release",
"-Dwin32-backend=false",
"-Dmacos-backend=false",
"-Dmedia-ffmpeg=disabled",
"-Dprint-cups=disabled",
"-Dintrospection=enabled",
"-Ddemos=false",
"-Dbuild-examples=false",
"-Dbuild-tests=false"
],
"cleanup": [
"/bin/gtk4-builder-tool",
"/bin/gtk4-encode-symbolic-svg"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.gnome.org/GNOME/gtk.git",
"tag": "4.6.2",
"commit": "aec7ca82007dbe07faee6be084d20758ebac2b91"
},
{
"type": "patch",
"path": "gtk4-popover-unrealize.patch"
}
]
}

View File

@@ -0,0 +1,24 @@
{
"name": "libsoup3",
"buildsystem": "meson",
"config-opts": [
"-Dintrospection=enabled",
"-Dvapi=disabled",
"-Dtests=false",
"-Dsysprof=disabled",
"-Dhttp2_tests=disabled",
"-Dpkcs11_tests=disabled"
],
"cleanup": [
"/include",
"/lib/pkgconfig"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.gnome.org/GNOME/libsoup.git",
"tag": "3.0.4",
"commit": "25a728020c4b53b5db4c4c675070e92f947fbd4d"
}
]
}

3
pkgs/rpm/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.osc/
clapper/
.lock

View File

@@ -0,0 +1 @@
addFilter("explicit-lib-dependency")

193
pkgs/rpm/clapper.spec Normal file
View File

@@ -0,0 +1,193 @@
#
# spec file for package clapper
#
# Copyright (C) 2020 sp1rit
# Copyright (C) 2020-21 Rafostar
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
%define debug_package %{nil}
%global appname com.github.rafostar.Clapper
%global gst_version 1.18.0
%global gtk4_version 4.0.0
%global meson_version 0.50
%global glib2_version 2.56.0
%global soup_version 3.0.0
Name: clapper
Version: 0.4.1
Release: 1%{?dist}
Summary: Simple and modern GNOME media player
License: GPL-3.0
URL: https://github.com/Rafostar/clapper
BuildRoot: %{_builddir}/%{name}-%{version}-build
Source0: _service
BuildRequires: meson >= %{meson_version}
BuildRequires: gtk4-devel >= %{gtk4_version}
BuildRequires: glib2-devel >= %{glib2_version}
BuildRequires: gobject-introspection-devel
BuildRequires: gjs
BuildRequires: gcc-c++
BuildRequires: desktop-file-utils
BuildRequires: hicolor-icon-theme
Requires: gjs
Requires: gtk4 >= %{gtk4_version}
Requires: libadwaita
Requires: hicolor-icon-theme
%if 0%{?suse_version}
# SUSE recommends group tag, while Fedora discourages their use
Group: Productivity/Multimedia/Video/Players
BuildRequires: update-desktop-files
BuildRequires: gstreamer-devel >= %{gst_version}
BuildRequires: gstreamer-plugins-base-devel >= %{gst_version}
BuildRequires: Mesa-libGLESv2-devel
BuildRequires: Mesa-libGLESv3-devel
Requires: libsoup-devel >= %{soup_version}
Requires: gstreamer >= %{gst_version}
Requires: gstreamer-plugins-base >= %{gst_version}
Requires: gstreamer-plugins-good >= %{gst_version}
Requires: gstreamer-plugins-bad >= %{gst_version}
# Popular video decoders
Recommends: gstreamer-plugins-libav >= %{gst_version}
# CD Playback
Suggests: gstreamer-plugins-ugly
# Intel/AMD video acceleration
Suggests: gstreamer-plugins-vaapi
%else
BuildRequires: glibc-all-langpacks
BuildRequires: gstreamer1-devel >= %{gst_version}
BuildRequires: gstreamer1-plugins-base-devel >= %{gst_version}
BuildRequires: mesa-libGL-devel
BuildRequires: mesa-libGLES-devel
BuildRequires: mesa-libGLU-devel
BuildRequires: mesa-libEGL-devel
Requires: libsoup3-devel
Requires: gstreamer1 >= %{gst_version}
Requires: gstreamer1-plugins-base >= %{gst_version}
Requires: gstreamer1-plugins-good >= %{gst_version}
Requires: gstreamer1-plugins-bad-free >= %{gst_version}
# ASS subtitles (assrender)
Recommends: gstreamer1-plugins-bad-free-extras >= %{gst_version}
# CD Playback
Suggests: gstreamer1-plugins-ugly-free
# Intel/AMD video acceleration
Suggests: gstreamer1-vaapi
%endif
%description
A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering.
%prep
%setup -q -n %{_sourcedir}/%{name}-%{version} -T -D
%build
%meson
%meson_build
%install
%meson_install
%if 0%{?suse_version}
%suse_update_desktop_file %{appname}
%endif
%check
desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop
%files
%license COPYING
%doc README.md
%{_bindir}/%{appname}*
%{_bindir}/clapper
%{_datadir}/%{appname}/
%{_datadir}/icons/hicolor/*/apps/*.svg
%{_datadir}/glib-2.0/schemas/%{appname}.gschema.xml
%{_datadir}/mime/packages/%{appname}.xml
%{_datadir}/applications/*.desktop
%{_datadir}/metainfo/*.metainfo.xml
%{_datadir}/gir-1.0/GstClapper-1.0.gir
%{_datadir}/locale/*/LC_MESSAGES/%{appname}.mo
%{_libdir}/%{appname}/
%changelog
* Thu Feb 17 2022 Rafostar <rafostar.github@gmail.com> - 0.4.1-2
- Require libsoup3
* Mon Dec 20 2021 Rafostar <rafostar.github@gmail.com> - 0.4.1-1
- New version
* Sun Sep 12 2021 Rafostar <rafostar.github@gmail.com> - 0.4.0-1
- New version
* Thu Aug 26 2021 Rafostar <rafostar.github@gmail.com> - 0.3.0-4
- Install translations
* Thu Aug 26 2021 Rafostar <rafostar.github@gmail.com> - 0.3.0-3
- Install clapper symlink
* Mon Aug 23 2021 Rafostar <rafostar.github@gmail.com> - 0.3.0-2
- Require libadwaita
* Fri Jun 18 2021 Rafostar <rafostar.github@gmail.com> - 0.3.0-1
- New version
* Mon Apr 19 2021 Rafostar <rafostar.github@gmail.com> - 0.2.1-1
- New version
* Tue Apr 13 2021 Rafostar <rafostar.github@gmail.com> - 0.2.0-1
- New version
* Fri Feb 26 2021 Rafostar <rafostar.github@gmail.com> - 0.1.0-1
- New version
* Sun Feb 7 2021 Rafostar <rafostar.github@gmail.com> - 0.0.0-10
- Install gstclapper libs to app named subdirectory
* Fri Feb 5 2021 Rafostar <rafostar.github@gmail.com> - 0.0.0-9
- Update build with gstclapper libs support
* Thu Jan 21 2021 Rafostar <rafostar.github@gmail.com> - 0.0.0-8
- Use metainfo instead of deprecated appdata
* Mon Jan 18 2021 Rafostar <rafostar.github@gmail.com> - 0.0.0-7
- Remove gjs-1.0 files
* Sun Dec 20 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-6
- Include additional app binaries
* Sat Oct 31 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-5
- Added metainfo
* Sun Oct 25 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-4
- Added gschema
* Wed Oct 14 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-3
- Update to GTK4
* Sat Sep 19 22:02:00 CEST 2020 sp1rit - 0.0.0-2
- Added suse_update_desktop_file macro for SuSE packages
* Fri Sep 18 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-1
- Initial package

View File

@@ -1,4 +1,4 @@
const { Adw, Gdk, Gio, GLib, GObject, Gst, GstClapper, Gtk } = imports.gi; const { Adw, Gdk, Gio, GObject, Gst, GstClapper, Gtk } = imports.gi;
const ByteArray = imports.byteArray; const ByteArray = imports.byteArray;
const Debug = imports.src.debug; const Debug = imports.src.debug;
const Misc = imports.src.misc; const Misc = imports.src.misc;
@@ -16,31 +16,14 @@ class ClapperPlayer extends GstClapper.Clapper
{ {
_init() _init()
{ {
let vsink = null; const gtk4plugin = new GstClapper.ClapperGtk4Plugin();
const use_legacy_sink = GLib.getenv('CLAPPER_USE_LEGACY_SINK'); const glsinkbin = Gst.ElementFactory.make('glsinkbin', null);
glsinkbin.sink = gtk4plugin.video_sink;
if(!use_legacy_sink || use_legacy_sink != '1') {
vsink = Gst.parse_bin_from_description('glupload ! glcolorconvert'
+ ' ! clapperglimport ! clappersink name=clappersink', true);
if(vsink)
this.clappersink = vsink.get_by_name('clappersink');
}
if(!vsink) {
vsink = Gst.ElementFactory.make('glsinkbin', null);
const gtk4plugin = new GstClapper.ClapperGtk4Plugin();
warn('using legacy video sink');
this.clappersink = gtk4plugin.video_sink;
vsink.sink = this.clappersink;
}
super._init({ super._init({
signal_dispatcher: new GstClapper.ClapperGMainContextSignalDispatcher(), signal_dispatcher: new GstClapper.ClapperGMainContextSignalDispatcher(),
video_renderer: new GstClapper.ClapperVideoOverlayVideoRenderer({ video_renderer: new GstClapper.ClapperVideoOverlayVideoRenderer({
video_sink: vsink, video_sink: glsinkbin,
}), }),
mpris: new GstClapper.ClapperMpris({ mpris: new GstClapper.ClapperMpris({
own_name: `org.mpris.MediaPlayer2.${Misc.appName}`, own_name: `org.mpris.MediaPlayer2.${Misc.appName}`,
@@ -53,7 +36,7 @@ class ClapperPlayer extends GstClapper.Clapper
use_pipewire: settings.get_boolean('use-pipewire'), use_pipewire: settings.get_boolean('use-pipewire'),
}); });
this.widget = this.clappersink.widget; this.widget = gtk4plugin.video_sink.widget;
this.widget.add_css_class('videowidget'); this.widget.add_css_class('videowidget');
this.visualization_enabled = false; this.visualization_enabled = false;
@@ -632,7 +615,7 @@ class ClapperPlayer extends GstClapper.Clapper
switch(key) { switch(key) {
case 'after-playback': case 'after-playback':
this.clappersink.keep_last_frame = (settings.get_int(key) === 1); this.widget.keep_last_frame = (settings.get_int(key) === 1);
break; break;
case 'seeking-mode': case 'seeking-mode':
switch(settings.get_int(key)) { switch(settings.get_int(key)) {

View File

@@ -345,8 +345,7 @@ class ClapperWidget extends Gtk.Grid
const currTime = GLib.DateTime.new_now_local(); const currTime = GLib.DateTime.new_now_local();
const endTime = currTime.add_seconds( const endTime = currTime.add_seconds(
(this.controls.positionAdjustment.get_upper() - this.controls.currentPosition) this.controls.positionAdjustment.get_upper() - this.controls.currentPosition
/ this.controls.elapsedButton.speedScale.get_value()
); );
const nextUpdate = this.revealerTop.setTimes(currTime, endTime, this.isSeekable); const nextUpdate = this.revealerTop.setTimes(currTime, endTime, this.isSeekable);