Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gtkgstsink.c

  Sprache: C
 

/*
 * GStreamer
 * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */


#include "config.h"

#include "gtkgstsinkprivate.h"

#include "gtkgstpaintableprivate.h"

#include "gdk/gdkmemoryformatprivate.h"

#if GST_GL_HAVE_WINDOW_X11 && (GST_GL_HAVE_PLATFORM_GLX || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_X11)
#define HAVE_GST_X11_SUPPORT
#include <gdk/x11/gdkx.h>
#if GST_GL_HAVE_PLATFORM_GLX
#include <gst/gl/x11/gstgldisplay_x11.h>
#endif
#endif

#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (GDK_WINDOWING_WAYLAND)
#define HAVE_GST_WAYLAND_SUPPORT
#include <gdk/wayland/gdkwayland.h>
#include <gst/gl/wayland/gstgldisplay_wayland.h>
#endif

#ifdef GDK_WINDOWING_WIN32
#include <gdk/win32/gdkwin32.h>
#include <gst/d3d12/gstd3d12.h>
#endif

#if GST_GL_HAVE_PLATFORM_EGL && (GST_GL_HAVE_WINDOW_WIN32 || GST_GL_HAVE_WINDOW_X11)
#include <gst/gl/egl/gstgldisplay_egl.h>
#endif

#ifdef GDK_WINDOWING_MACOS
#include <gdk/macos/gdkmacos.h>
#endif

#include <gst/gl/gstglfuncs.h>

#include <gdk/gdkdmabuffourccprivate.h>
#include <gst/allocators/gstdmabuf.h>

enum {
  PROP_0,
  PROP_PAINTABLE,
  PROP_GL_CONTEXT,
  PROP_USES_GL,
  PROP_DISPLAY,

  N_PROPS,
};

GST_DEBUG_CATEGORY (gtk_debug_gst_sink);
#define GST_CAT_DEFAULT gtk_debug_gst_sink

#define MEMORY_TEXTURE_CAPS GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)

#ifdef GDK_WINDOWING_WIN32
#define D3D12_TEXTURE_CAPS GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY, GST_VIDEO_FORMATS_ALL)
#endif

#define GL_TEXTURE_CAPS GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA")

#define DMABUF_TEXTURE_CAPS GST_VIDEO_DMA_DRM_CAPS_MAKE

#ifdef GDK_WINDOWING_WIN32
define GST_ALL_TEXTURE_CAPS DMABUF_TEXTURE_CAPS "; " D3D12_TEXTURE_CAPS "; " GL_TEXTURE_CAPS "; " MEMORY_TEXTURE_CAPS
#else
define GST_ALL_TEXTURE_CAPS DMABUF_TEXTURE_CAPS "; " GL_TEXTURE_CAPS "; " MEMORY_TEXTURE_CAPS
#endif

static GstStaticPadTemplate gtk_gst_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS(GST_ALL_TEXTURE_CAPS)
    );

#undef GST_ALL_TEXTURE_CAPS
#undef GST_VIDEO_DMA_DRM_CAPS_MAKE_STR

G_DEFINE_TYPE_WITH_CODE (GtkGstSink, gtk_gst_sink,
    GST_TYPE_VIDEO_SINK,
    GST_DEBUG_CATEGORY_INIT (gtk_debug_gst_sink,
        "gtkgstsink"0"GtkGstMediaFile Video Sink"));

static GParamSpec *properties[N_PROPS] = { NULL, };


static void
gtk_gst_sink_get_times (GstBaseSink  *bsink,
                        GstBuffer    *buf,
                        GstClockTime *start,
                        GstClockTime *end)
{
  GtkGstSink *gtk_sink;

  gtk_sink = GTK_GST_SINK (bsink);

  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf))
    {
      *start = GST_BUFFER_TIMESTAMP (buf);
      if (GST_BUFFER_DURATION_IS_VALID (buf))
        *end = *start + GST_BUFFER_DURATION (buf);
      else
        {
          if (GST_VIDEO_INFO_FPS_N (>k_sink->v_info) > 0)
            {
              *end = *start +
                  gst_util_uint64_scale_int (GST_SECOND,
                                             GST_VIDEO_INFO_FPS_D (>k_sink->v_info),
                                             GST_VIDEO_INFO_FPS_N (>k_sink->v_info));
            }
        }
    }
}

static void
add_drm_formats_and_modifiers (GstCaps          *caps,
                               GdkDmabufFormats *dmabuf_formats)
{
  GValue dmabuf_list = G_VALUE_INIT;
  size_t i;

  g_value_init (&dmabuf_list, GST_TYPE_LIST);

  for (i = 0; i < gdk_dmabuf_formats_get_n_formats (dmabuf_formats); i++)
    {
      GValue value = G_VALUE_INIT;
      gchar *drm_format_string;
      guint32 fmt;
      guint64 mod;

      gdk_dmabuf_formats_get_format (dmabuf_formats, i, &fmt, &mod);

      if (mod == DRM_FORMAT_MOD_INVALID)
        continue;

      drm_format_string = gst_video_dma_drm_fourcc_to_string (fmt, mod);
      if (!drm_format_string)
        continue;

      g_value_init (&value, G_TYPE_STRING);
      g_value_take_string (&value, drm_format_string);
      gst_value_list_append_and_take_value (&dmabuf_list, &value);
    }

    gst_structure_take_value (gst_caps_get_structure (caps, 0), "drm-format",
        &dmabuf_list);
}

static GdkColorState *
gtk_gst_color_state_from_colorimetry (GtkGstSink                *self,
                                      const GstVideoColorimetry *colorimetry)
{
  GdkCicpParams *params;
  GdkColorState *color_state;
  GError *error = NULL;

  params = gdk_cicp_params_new ();

  if (colorimetry->primaries == GST_VIDEO_COLOR_PRIMARIES_UNKNOWN)
    gdk_cicp_params_set_color_primaries (params, 1);
  else
    gdk_cicp_params_set_color_primaries (params, gst_video_color_primaries_to_iso (colorimetry->primaries));

  if (colorimetry->transfer == GST_VIDEO_TRANSFER_UNKNOWN)
    gdk_cicp_params_set_transfer_function (params, 13);
  else
    gdk_cicp_params_set_transfer_function (params, gst_video_transfer_function_to_iso (colorimetry->transfer));

  if (colorimetry->matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN)
    gdk_cicp_params_set_matrix_coefficients (params, 6);
  else
    gdk_cicp_params_set_matrix_coefficients (params, gst_video_color_matrix_to_iso (colorimetry->matrix));

  gdk_cicp_params_set_range (params, colorimetry->range == GST_VIDEO_COLOR_RANGE_0_255 ? GDK_CICP_RANGE_FULL : GDK_CICP_RANGE_NARROW);

  color_state = gdk_cicp_params_build_color_state (params, &error);
  g_object_unref (params);

  if (color_state == NULL)
    {
      GST_ERROR_OBJECT (self, "Could not create GDK colorstate for given colorimetry: %s", error->message);
      g_clear_error (&error);
    }

  return color_state;
}

static GstCaps *
get_memory_formats (void)
{
  GstVideoFormat formats[GDK_MEMORY_N_FORMATS];
  gsize i, n_formats;

  n_formats = 0;

  for (i = 0; i < GDK_MEMORY_N_FORMATS; i++)
    {
      formats[n_formats] = gdk_memory_format_get_gst_video_format (i);
      if (formats[n_formats] != GST_VIDEO_FORMAT_UNKNOWN)
        n_formats++;
    }

  return gst_video_make_raw_caps (formats, n_formats);
}

static GstCaps *
gtk_gst_sink_get_caps (GstBaseSink *bsink,
                       GstCaps     *filter)
{
  GtkGstSink *self = GTK_GST_SINK (bsink);
  GstCaps *unfiltered, *tmp, *result;

  unfiltered = gst_caps_new_empty ();

#ifdef GDK_WINDOWING_WIN32
  tmp = gst_caps_from_string (D3D12_TEXTURE_CAPS);
  gst_caps_append (unfiltered, tmp);
#endif

  if (self->gdk_display)
    {
      GdkDmabufFormats *formats = gdk_display_get_dmabuf_formats (self->gdk_display);

      if (formats && gdk_dmabuf_formats_get_n_formats (formats) > 0)
        {
          tmp = gst_caps_from_string (DMABUF_TEXTURE_CAPS);
          add_drm_formats_and_modifiers (tmp, formats);
          gst_caps_append (unfiltered, tmp);
        }
    }

  if (self->gdk_context)
    {
      tmp = gst_caps_from_string (GL_TEXTURE_CAPS);
      gst_caps_append (unfiltered, tmp);
    }

  tmp = get_memory_formats ();
  gst_caps_append (unfiltered, tmp);

  GST_DEBUG_OBJECT (self, "advertising own caps %" GST_PTR_FORMAT, unfiltered);

  if (filter)
    {
      GST_DEBUG_OBJECT (self, "intersecting with filter caps %" GST_PTR_FORMAT, filter);

      result = gst_caps_intersect_full (filter, unfiltered, GST_CAPS_INTERSECT_FIRST);
      gst_caps_unref (unfiltered);
    }
  else
    {
      result = unfiltered;
    }

  GST_DEBUG_OBJECT (self, "returning caps: %" GST_PTR_FORMAT, result);

  return result;
}

static gboolean
gtk_gst_sink_set_caps (GstBaseSink *bsink,
                       GstCaps     *caps)
{
  GtkGstSink *self = GTK_GST_SINK (bsink);

  g_clear_object (&self->pool);

  GST_INFO_OBJECT (self, "set caps with %" GST_PTR_FORMAT, caps);

#ifdef GDK_WINDOWING_WIN32
  if (gst_caps_features_contains (gst_caps_get_features (caps, 0), GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY))
    {
      GST_DEBUG_OBJECT (self, "using D3D12");

      gst_video_info_dma_drm_init (&self->drm_info);

      if (!gst_video_info_from_caps (&self->v_info, caps))
        return FALSE;
    }
  else
#endif
  if (gst_video_is_dma_drm_caps (caps))
    {
      if (!gst_video_info_dma_drm_from_caps (&self->drm_info, caps))
        return FALSE;

      if (!gst_video_info_dma_drm_to_video_info (&self->drm_info, &self->v_info))
        return FALSE;

      GST_INFO_OBJECT (self, "using DMABuf, passthrough possible");
    }
  else
    {
      gst_video_info_dma_drm_init (&self->drm_info);

      if (!gst_video_info_from_caps (&self->v_info, caps))
        return FALSE;
    }

  g_clear_pointer (&self->color_state, gdk_color_state_unref);
  self->color_state = gtk_gst_color_state_from_colorimetry (self, &self->v_info.colorimetry);
  if (self->color_state == NULL)
    return FALSE;

  return TRUE;
}

static gboolean
gtk_gst_sink_query (GstBaseSink *bsink,
                    GstQuery    *query)
{
  GtkGstSink *self = GTK_GST_SINK (bsink);

  if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT &&
      self->gst_display != NULL &&
      gst_gl_handle_context_query (GST_ELEMENT (self), query, self->gst_display, self->gst_context, self->gst_gdk_context))
    return TRUE;

  return GST_BASE_SINK_CLASS (gtk_gst_sink_parent_class)->query (bsink, query);
}

static gboolean
gtk_gst_sink_propose_allocation (GstBaseSink *bsink,
                                 GstQuery    *query)
{
  GtkGstSink *self = GTK_GST_SINK (bsink);
  GstBufferPool *pool = NULL;
  GstStructure *config;
  GstCaps *caps;
  guint size;
  gboolean need_pool;
  GstVideoInfo info;

  gst_query_parse_allocation (query, &caps, &need_pool);

  if (caps == NULL)
    {
      GST_DEBUG_OBJECT (bsink, "no caps specified");
      return FALSE;
    }

  gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);

#ifdef GDK_WINDOWING_WIN32
  if (gst_caps_features_contains (gst_caps_get_features (caps, 0), GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY))
    {
      GstD3D12Device *device;

      if (!gst_video_info_from_caps (&info, caps))
        {
          GST_DEBUG_OBJECT (self, "invalid caps specified");
          return FALSE;
        }

      /* the normal size of a frame */
      size = info.size;

      if (need_pool &&
          (device = gst_d3d12_device_new (0)))
        {
          pool = gst_d3d12_buffer_pool_new (device);

          config = gst_buffer_pool_get_config (pool);
          gst_buffer_pool_config_set_params (config, caps, size, 20);
          gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);

          g_clear_object (&device);

          if (!gst_buffer_pool_set_config (pool, config))
            {
              GST_DEBUG_OBJECT (bsink, "failed setting config");
              gst_object_unref (pool);
              return FALSE;
            }
        }

      /* we need at least 2 buffer because we hold on to the last one */
      gst_query_add_allocation_pool (query, pool, size, 20);
      g_clear_object (&pool);

      return TRUE;
    }
#endif

  if (gst_caps_features_contains (gst_caps_get_features (caps, 0), GST_CAPS_FEATURE_MEMORY_GL_MEMORY))
    {
      if (!gst_video_info_from_caps (&info, caps))
        {
          GST_DEBUG_OBJECT (self, "invalid caps specified");
          return FALSE;
        }

      /* the normal size of a frame */
      size = info.size;

      if (need_pool)
        {
          GST_DEBUG_OBJECT (self, "create new pool");
          pool = gst_gl_buffer_pool_new (self->gst_context);

          config = gst_buffer_pool_get_config (pool);
          gst_buffer_pool_config_set_params (config, caps, size, 00);
          gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_GL_SYNC_META);

          if (!gst_buffer_pool_set_config (pool, config))
            {
              GST_DEBUG_OBJECT (bsink, "failed setting config");
              gst_object_unref (pool);
              return FALSE;
            }
        }

      /* we need at least 2 buffer because we hold on to the last one */
      gst_query_add_allocation_pool (query, pool, size, 20);
      g_clear_object (&pool);

      if (self->gst_context->gl_vtable->FenceSync)
        gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);

      return TRUE;
    }

  return TRUE;
}

static gboolean
gtk_gst_sink_event (GstBaseSink * bsink, GstEvent * event)
{
  GtkGstSink *self = GTK_GST_SINK (bsink);
  gboolean ret;

  if (GST_EVENT_TYPE (event) == GST_EVENT_TAG)
    {
      GstTagList *taglist;
      GstVideoOrientationMethod orientation;

      gst_event_parse_tag (event, &taglist);
      if (gst_video_orientation_from_tag (taglist, &orientation))
        {
          GST_DEBUG_OBJECT (self, "Setting orientation to %d", orientation);
          self->orientation = orientation;
        }
    }

  ret = GST_BASE_SINK_CLASS (gtk_gst_sink_parent_class)->event (bsink, event);

  return ret;
}

static GdkMemoryFormat
gtk_gst_memory_format_from_video_info (GstVideoInfo *info)
{
  GdkMemoryFormat result;
  gboolean premultiplied;

  premultiplied = GST_VIDEO_INFO_FLAGS (info) & GST_VIDEO_FLAG_PREMULTIPLIED_ALPHA;

  if (!gdk_memory_format_find_by_gst_video_format (GST_VIDEO_INFO_FORMAT (info),
                                                   premultiplied,
                                                   &result))
    {
      if (!GST_VIDEO_INFO_HAS_ALPHA (info))
        result = GDK_MEMORY_R8G8B8;
      else if (premultiplied)
        result = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
      else
        result = GDK_MEMORY_R8G8B8A8;
    }

  return result;
}

static void
video_frame_free (GstVideoFrame *frame)
{
  gst_video_frame_unmap (frame);
  g_free (frame);
}

/* using the last plane's subsampling works for all supported formats */
#define ROUND_UP_WIDTH(vinfo, width) GST_ROUND_UP_N ((width), 1 << GST_VIDEO_FORMAT_INFO_W_SUB ((vinfo)->finfo, GST_VIDEO_INFO_N_PLANES (vinfo) - 1))
#define ROUND_UP_HEIGHT(vinfo, height) GST_ROUND_UP_N ((height), 1 << GST_VIDEO_FORMAT_INFO_H_SUB ((vinfo)->finfo, GST_VIDEO_INFO_N_PLANES (vinfo) - 1))

static GstVideoFrame*
gtk_gst_sink_maybe_replace_frame (GtkGstSink     *self,
                                  GstVideoFrame  *frame)
{
  GstBuffer *tmp_buffer = NULL;
  GstVideoFrame *tmp_frame;

  /* Buffer is contiguous, GTK can use it */
  if (gst_buffer_n_memory (frame->buffer) == 1)
    return frame;

  GST_DEBUG_OBJECT (self, "Buffer is not contiguous, copy needed");

  if (!self->pool)
    {
      GstStructure *config;
      GstCaps *caps;
      guint size;

      GST_DEBUG_OBJECT (self, "Creating buffer pool for copies");

      self->pool = gst_video_buffer_pool_new ();
      config = gst_buffer_pool_get_config (self->pool);

      caps = gst_video_info_to_caps (&self->v_info);
      size = GST_VIDEO_INFO_SIZE (&self->v_info);

      gst_buffer_pool_config_set_params (config, caps, size, 20);

      if (!gst_buffer_pool_set_config (self->pool, config))
        {
          GST_ERROR_OBJECT (self, "Could not create buffer pool");
          gst_caps_unref (caps);
          g_clear_object (&self->pool);
          video_frame_free (frame);
          return NULL;
        }

      gst_caps_unref (caps);
    }

  if (!gst_buffer_pool_set_active (self->pool, TRUE))
    {
      video_frame_free (frame);
      GST_WARNING_OBJECT (self, "Can't set pool active");
      return NULL;
    }

  if (gst_buffer_pool_acquire_buffer (self->pool, &tmp_buffer, NULL) !=
       GST_FLOW_OK)
    {
      video_frame_free (frame);
      GST_ERROR_OBJECT (self, "Can't acquire buffer");
      return NULL;
    }
  g_assert (gst_buffer_n_memory (tmp_buffer) == 1);

  if (!gst_buffer_copy_into (tmp_buffer, frame->buffer,
                             GST_BUFFER_COPY_METADATA, 0, -1))
    {
      gst_buffer_unref (tmp_buffer);
      video_frame_free (frame);
      GST_ERROR_OBJECT (self, "Can't copy metadata");
      return NULL;
    }

  tmp_frame = g_new (GstVideoFrame, 1);
  if (!gst_video_frame_map (tmp_frame, &self->v_info, tmp_buffer,
                            GST_MAP_READWRITE))
    {
      gst_buffer_unref (tmp_buffer);
      video_frame_free (frame);
      g_free (tmp_frame);
      GST_ERROR_OBJECT (self, "Can't map new buffer");
      return NULL;
    }

  if (!gst_video_frame_copy (tmp_frame, frame))
    {
      gst_buffer_unref (tmp_buffer);
      video_frame_free (frame);
      video_frame_free (tmp_frame);
      GST_ERROR_OBJECT (self, "Can't copy frame");
      return NULL;
    }

  GST_DEBUG_OBJECT (self, "Copied and replaced frame");
  gst_buffer_unref (tmp_buffer);
  video_frame_free (frame);
  return tmp_frame;
}

static GdkTexture *
gtk_gst_sink_texture_from_buffer (GtkGstSink      *self,
                                  GstBuffer       *buffer,
                                  double          *pixel_aspect_ratio,
                                  graphene_rect_t *viewport)
{
  GstVideoFrame *frame = g_new (GstVideoFrame, 1);
  GstMemory *mem;
  GdkTexture *texture;

  viewport->origin.x = 0;
  viewport->origin.y = 0;
  viewport->size.width = GST_VIDEO_INFO_WIDTH (&self->v_info);
  viewport->size.height = GST_VIDEO_INFO_HEIGHT (&self->v_info);

  mem = gst_buffer_peek_memory (buffer, 0);

#ifdef GDK_WINDOWING_WIN32
  if (gst_is_d3d12_memory (mem) &&
      gst_video_frame_map (frame, &self->v_info, buffer, GST_MAP_READ_D3D12))
    {
      GstD3D12Memory *dmem = GST_D3D12_MEMORY_CAST (mem);
      GdkD3D12TextureBuilder *builder;
      ID3D12Fence *fence;
      guint64 fence_wait;
      GError *error = NULL;

      builder = gdk_d3d12_texture_builder_new ();
      gdk_d3d12_texture_builder_set_resource (builder, gst_d3d12_memory_get_resource_handle (dmem));
      if (gst_d3d12_memory_get_fence (dmem, &fence, &fence_wait))
        {
          gdk_d3d12_texture_builder_set_fence (builder, fence);
          ID3D12Fence_Release (fence);
          gdk_d3d12_texture_builder_set_fence_wait (builder, fence_wait);
        }
      gdk_d3d12_texture_builder_set_color_state (builder, self->color_state);

      texture = gdk_d3d12_texture_builder_build (builder,
                                                 (GDestroyNotify) video_frame_free,
                                                 frame,
                                                 &error);
      g_object_unref (builder);

      if (!texture)
        {
          GST_ERROR_OBJECT (self, "Failed to create d3d12 texture: %s", error->message);
          g_error_free (error);
        }

      *pixel_aspect_ratio = ((double) GST_VIDEO_INFO_PAR_N (&self->v_info) /
                             (double) GST_VIDEO_INFO_PAR_D (&self->v_info));
    }
  else
#endif
  if (gst_is_dmabuf_memory (mem) &&
      self->drm_info.drm_fourcc != DRM_FORMAT_INVALID)
    {
      GdkDmabufTextureBuilder *builder = NULL;
      const GstVideoMeta *vmeta = gst_buffer_get_video_meta (buffer);
      GError *error = NULL;
      int i;

      /* We don't map dmabufs */
      g_clear_pointer (&frame, g_free);

      g_return_val_if_fail (vmeta, NULL);

      builder = gdk_dmabuf_texture_builder_new ();
      gdk_dmabuf_texture_builder_set_display (builder, self->gdk_display);
      gdk_dmabuf_texture_builder_set_fourcc (builder, self->drm_info.drm_fourcc);
      gdk_dmabuf_texture_builder_set_modifier (builder, self->drm_info.drm_modifier);
      gdk_dmabuf_texture_builder_set_width (builder, ROUND_UP_WIDTH (&self->v_info, vmeta->width));
      gdk_dmabuf_texture_builder_set_height (builder, ROUND_UP_HEIGHT (&self->v_info, vmeta->height));
      gdk_dmabuf_texture_builder_set_n_planes (builder, vmeta->n_planes);
      gdk_dmabuf_texture_builder_set_color_state (builder, self->color_state);
      gdk_dmabuf_texture_builder_set_premultiplied (builder, GST_VIDEO_INFO_FLAGS (&self->v_info) & GST_VIDEO_FLAG_PREMULTIPLIED_ALPHA);

      for (i = 0; i < vmeta->n_planes; i++)
        {
          guint mem_idx, length;
          gsize skip;

          if (!gst_buffer_find_memory (buffer,
                                       vmeta->offset[i],
                                       1,
                                       &mem_idx,
                                       &length,
                                       &skip))
            {
              GST_ERROR_OBJECT (self, "Buffer data is bogus");
              return NULL;
            }

          mem = gst_buffer_peek_memory (buffer, mem_idx);

          gdk_dmabuf_texture_builder_set_fd (builder, i, gst_dmabuf_memory_get_fd (mem));
          gdk_dmabuf_texture_builder_set_offset (builder, i, mem->offset + skip);
          gdk_dmabuf_texture_builder_set_stride (builder, i, vmeta->stride[i]);
        }

      texture = gdk_dmabuf_texture_builder_build (builder,
                                                  (GDestroyNotify) gst_buffer_unref,
                                                  gst_buffer_ref (buffer),
                                                  &error);
      g_object_unref (builder);

      if (!texture)
        {
          GST_ERROR_OBJECT (self, "Failed to create dmabuf texture: %s", error->message);
          g_error_free (error);
        }

      *pixel_aspect_ratio = ((double) GST_VIDEO_INFO_PAR_N (&self->v_info) /
                             (double) GST_VIDEO_INFO_PAR_D (&self->v_info));
    }
  else if (gst_is_gl_memory (mem) &&
           gst_video_frame_map (frame, &self->v_info, buffer, GST_MAP_READ | GST_MAP_GL))
    {
      GstGLSyncMeta *sync_meta;
      GdkGLTextureBuilder *builder;

      sync_meta = gst_buffer_get_gl_sync_meta (buffer);
      if (sync_meta)
        gst_gl_sync_meta_set_sync_point (sync_meta, self->gst_context);

      /* Note: using the gdk_context here is a (harmless) lie,
       * since the texture really originates in the gst_context.
       * But that is not a GdkGLContext. It is harmless, because
       * we are never using the texture in the gdk_context, so we
       * never make the (erroneous) decision to ignore the sync.
       */

      builder = gdk_gl_texture_builder_new ();
      gdk_gl_texture_builder_set_context (builder, self->gdk_context);
      gdk_gl_texture_builder_set_format (builder, gtk_gst_memory_format_from_video_info (&frame-&gt;info));
      gdk_gl_texture_builder_set_id (builder, *(guint *) frame->data[0]);
      gdk_gl_texture_builder_set_width (builder, ROUND_UP_WIDTH (&self->v_info, frame->info.width));
      gdk_gl_texture_builder_set_height (builder, ROUND_UP_HEIGHT (&self->v_info, frame->info.height));
      gdk_gl_texture_builder_set_sync (builder, sync_meta ? sync_meta->data : NULL);
      gdk_gl_texture_builder_set_color_state (builder, self->color_state);

      texture = gdk_gl_texture_builder_build (builder,
                                              (GDestroyNotify) video_frame_free,
                                              frame);

      g_object_unref (builder);

      *pixel_aspect_ratio = ((double) frame->info.par_n) / ((double) frame->info.par_d);
    }
  else if (gst_video_frame_map (frame, &self->v_info, buffer, GST_MAP_READ))
    {
      GdkMemoryTextureBuilder *builder;
      GBytes *bytes;
      gsize i;

      frame = gtk_gst_sink_maybe_replace_frame (self, frame);
      if (!frame)
        return NULL;

      bytes = g_bytes_new_with_free_func (frame->map[0].data,
                                          frame->map[0].size,
                                          (GDestroyNotify) video_frame_free,
                                          frame);

      builder = gdk_memory_texture_builder_new ();
      gdk_memory_texture_builder_set_format (builder, gtk_gst_memory_format_from_video_info (&frame->info));
      gdk_memory_texture_builder_set_width (builder, ROUND_UP_WIDTH (&self->v_info, frame->info.width));
      gdk_memory_texture_builder_set_height (builder, ROUND_UP_HEIGHT (&self->v_info, frame->info.height));
      gdk_memory_texture_builder_set_color_state (builder, self->color_state);
      gdk_memory_texture_builder_set_bytes (builder, bytes);
      for (i = 0; i < frame->info.finfo->n_planes; i++)
        {
          gdk_memory_texture_builder_set_offset (builder, i, frame->info.offset[i]);
          gdk_memory_texture_builder_set_stride_for_plane (builder, i, frame->info.stride[i]);
        }

      texture = gdk_memory_texture_builder_build (builder);
      g_object_unref (builder);
      g_bytes_unref (bytes);

      *pixel_aspect_ratio = ((double) frame->info.par_n) / ((double) frame->info.par_d);
    }
  else
    {
      GST_ERROR_OBJECT (self, "Could not convert buffer to texture.");
      texture = NULL;
      g_free (frame);
    }

  return texture;
}

static GstFlowReturn
gtk_gst_sink_show_frame (GstVideoSink *vsink,
                         GstBuffer    *buf)
{
  GtkGstSink *self;
  GdkTexture *texture;
  double pixel_aspect_ratio;
  graphene_rect_t viewport;

  GST_TRACE ("rendering buffer:%p", buf);

  self = GTK_GST_SINK (vsink);

  GST_OBJECT_LOCK (self);

  texture = gtk_gst_sink_texture_from_buffer (self, buf, &pixel_aspect_ratio, &viewport);
  if (texture)
    {
      gtk_gst_paintable_queue_set_texture (self->paintable,
                                           texture,
                                           pixel_aspect_ratio,
                                           &viewport,
                                           self->orientation);
      g_object_unref (texture);
    }

  GST_OBJECT_UNLOCK (self);

  return GST_FLOW_OK;
}

static gboolean
gtk_gst_sink_initialize_gl (GtkGstSink *self)
{
  GdkDisplay *display;
  GError *error = NULL;
  GstGLPlatform platform = GST_GL_PLATFORM_NONE;
  GstGLAPI gl_api = GST_GL_API_NONE;
  guintptr gl_handle = 0;
  gboolean succeeded = FALSE;

  display = gdk_gl_context_get_display (self->gdk_context);

  gdk_gl_context_make_current (self->gdk_context);

#ifdef HAVE_GST_X11_SUPPORT

G_GNUC_BEGIN_IGNORE_DEPRECATIONS

  if (GDK_IS_X11_DISPLAY (display))
    {
      gpointer display_ptr;

#if GST_GL_HAVE_PLATFORM_EGL
      display_ptr = gdk_x11_display_get_egl_display (display);
      if (display_ptr)
        {
          GST_DEBUG_OBJECT (self, "got EGL on X11!");
          platform = GST_GL_PLATFORM_EGL;
          self->gst_display = GST_GL_DISPLAY (gst_gl_display_egl_new_with_egl_display (display_ptr));
        }
#endif
#if GST_GL_HAVE_PLATFORM_GLX
      if (!self->gst_display)
        {
          GST_DEBUG_OBJECT (self, "got GLX on X11!");
          platform = GST_GL_PLATFORM_GLX;
          display_ptr = gdk_x11_display_get_xdisplay (display);
          self->gst_display = GST_GL_DISPLAY (gst_gl_display_x11_new_with_display (display_ptr));
        }
#endif

      gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
      gl_handle = gst_gl_context_get_current_gl_context (platform);

      if (gl_handle)
        {
          self->gst_gdk_context = gst_gl_context_new_wrapped (self->gst_display, gl_handle, platform, gl_api);
        }
      else
        {
          GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext");
          return FALSE;
        }
    }
  else

G_GNUC_END_IGNORE_DEPRECATIONS

#endif
#ifdef HAVE_GST_WAYLAND_SUPPORT
  if (GDK_IS_WAYLAND_DISPLAY (display))
    {
      platform = GST_GL_PLATFORM_EGL;

      GST_DEBUG_OBJECT (self, "got EGL on Wayland!");

      gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
      gl_handle = gst_gl_context_get_current_gl_context (platform);

      if (gl_handle)
        {
          struct wl_display *wayland_display;

          wayland_display = gdk_wayland_display_get_wl_display (display);
          self->gst_display = GST_GL_DISPLAY (gst_gl_display_wayland_new_with_display (wayland_display));
          self->gst_gdk_context = gst_gl_context_new_wrapped (self->gst_display, gl_handle, platform, gl_api);
        }
      else
        {
          GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext, not using Wayland EGL");
          return FALSE;
        }
    }
  else
#endif
#if defined(GST_GL_HAVE_PLATFORM_CGL) && defined(GDK_WINDOWING_MACOS)
  if (GDK_IS_MACOS_DISPLAY (display))
    {
      platform = GST_GL_PLATFORM_CGL;

      GST_DEBUG_OBJECT (self, "got CGL on macOS!");

      gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
      gl_handle = gst_gl_context_get_current_gl_context (platform);

      if (gl_handle)
        {
          self->gst_display = gst_gl_display_new ();
          self->gst_gdk_context = gst_gl_context_new_wrapped (self->gst_display, gl_handle, platform, gl_api);
        }
      else
        {
          GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext, not using macOS CGL");
          return FALSE;
        }
    }
  else
#endif
#if GST_GL_HAVE_WINDOW_WIN32 && (GST_GL_HAVE_PLATFORM_WGL || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_WIN32)
  if (GDK_IS_WIN32_DISPLAY (display))
    {
      gboolean is_gles = gdk_gl_context_get_use_es (self->gdk_context);
      const char *gl_type = is_gles ? "EGL" : "WGL";

      platform = is_gles ? GST_GL_PLATFORM_EGL : GST_GL_PLATFORM_WGL;
      gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);

      GST_DEBUG_OBJECT (self, "got %s on Win32!", gl_type);

      gl_handle = gst_gl_context_get_current_gl_context (platform);

      if (gl_handle)
        {
          /*
           * We must force a win32 GstGL display type and if using desktop GL, the GL_Platform to be WGL
           * and an appropriate GstGL API depending on the gl_api we receive.  We also ensure that we use
           * an EGL GstGL API if we are using EGL in GDK.  Envvars are required, unless
           * gst_gl_display_new_with_type() is available, unfortunately, so that gst_gl_display_new() does
           * things correctly if we have GstGL built with both EGL and WGL support for the WGL case,
           * otherwise gst_gl_display_new() will assume an EGL display, which won't work for us
           */


          if (gl_api & (GST_GL_API_OPENGL3 | GST_GL_API_OPENGL))
            {
              self->gst_display = gst_gl_display_new_with_type (GST_GL_DISPLAY_TYPE_WIN32);
            }
#if GST_GL_HAVE_PLATFORM_EGL
          else
            {
              gpointer display_ptr = gdk_win32_display_get_egl_display (display);
              self->gst_display = GST_GL_DISPLAY (gst_gl_display_egl_new_with_egl_display (display_ptr));
            }
#endif

          gst_gl_display_filter_gl_api (self->gst_display, gl_api);
          self->gst_gdk_context = gst_gl_context_new_wrapped (self->gst_display, gl_handle, platform, gl_api);
        }
      else
        {
          GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext, not using %s", gl_type);
       return FALSE;
        }
    }
  else
#endif
    {
      GST_INFO_OBJECT (self, "Unsupported GDK display %s for GL", G_OBJECT_TYPE_NAME (display));
      return FALSE;
    }

  g_assert (self->gst_gdk_context != NULL);

  gst_gl_context_activate (self->gst_gdk_context, TRUE);

  if (!gst_gl_context_fill_info (self->gst_gdk_context, &error))
    {
      GST_ERROR_OBJECT (self, "failed to retrieve GDK context info: %s", error->message);
      g_clear_error (&error);
      g_clear_object (&self->gst_gdk_context);
      g_clear_object (&self->gst_display);

      return FALSE;
    }
  else
    {
      gdk_gl_context_clear_current ();
      gst_gl_context_activate (self->gst_gdk_context, FALSE);
    }

  succeeded = gst_gl_display_create_context (self->gst_display, self->gst_gdk_context, &self->;gst_context, &error);

  if (!succeeded)
    {
      GST_ERROR_OBJECT (self, "Couldn't create GL context: %s", error->message);
      g_error_free (error);
      g_clear_object (&self->gst_gdk_context);
      g_clear_object (&self->gst_display);
    }

  return succeeded;
}

static void
gtk_gst_sink_set_property (GObject      *object,
                           guint         prop_id,
                           const GValue *value,
                           GParamSpec   *pspec)

{
  GtkGstSink *self = GTK_GST_SINK (object);

  switch (prop_id)
    {
    case PROP_PAINTABLE:
      self->paintable = g_value_dup_object (value);
      if (self->paintable == NULL)
        self->paintable = GTK_GST_PAINTABLE (gtk_gst_paintable_new ());
      break;

    case PROP_GL_CONTEXT:
      self->gdk_context = g_value_dup_object (value);
      if (self->gdk_context != NULL && !gtk_gst_sink_initialize_gl (self))
        g_clear_object (&self->gdk_context);
      self->uses_gl = self->gdk_context != NULL;
      break;

    case PROP_DISPLAY:
      self->gdk_display = g_value_dup_object (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_gst_sink_get_property (GObject    *object,
                           guint       prop_id,
                           GValue     *value,
                           GParamSpec *pspec)
{
  GtkGstSink *self = GTK_GST_SINK (object);

  switch (prop_id)
    {
    case PROP_PAINTABLE:
      g_value_set_object (value, self->paintable);
      break;

    case PROP_GL_CONTEXT:
      g_value_set_object (value, self->gdk_context);
      break;

    case PROP_DISPLAY:
      g_value_set_object (value, self->gdk_display);
      break;

    case PROP_USES_GL:
      g_value_set_boolean (value, self->uses_gl);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_gst_sink_dispose (GObject *object)
{
  GtkGstSink *self = GTK_GST_SINK (object);

  g_clear_pointer (&self->color_state, gdk_color_state_unref);
  g_clear_object (&self->paintable);
  g_clear_object (&self->gst_gdk_context);
  g_clear_object (&self->gst_display);
  g_clear_object (&self->gdk_context);
  g_clear_object (&self->gdk_display);
  g_clear_object (&self->pool);

  G_OBJECT_CLASS (gtk_gst_sink_parent_class)->dispose (object);
}

static void
gtk_gst_sink_class_init (GtkGstSinkClass * klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
  GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
  GstVideoSinkClass *gstvideosink_class = GST_VIDEO_SINK_CLASS (klass);

  gobject_class->set_property = gtk_gst_sink_set_property;
  gobject_class->get_property = gtk_gst_sink_get_property;
  gobject_class->dispose = gtk_gst_sink_dispose;

  gstbasesink_class->set_caps = gtk_gst_sink_set_caps;
  gstbasesink_class->get_times = gtk_gst_sink_get_times;
  gstbasesink_class->query = gtk_gst_sink_query;
  gstbasesink_class->propose_allocation = gtk_gst_sink_propose_allocation;
  gstbasesink_class->get_caps = gtk_gst_sink_get_caps;
  gstbasesink_class->event = gtk_gst_sink_event;

  gstvideosink_class->show_frame = gtk_gst_sink_show_frame;

  /**
   * GtkGstSink:paintable:
   *
   * The paintable that provides the picture for this sink.
   */

  properties[PROP_PAINTABLE] =
    g_param_spec_object ("paintable", NULL, NULL,
                         GTK_TYPE_GST_PAINTABLE,
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME);

  /**
   * GtkGstSink:gl-context:
   *
   * The #GdkGLContext to use for GL rendering.
   */

  properties[PROP_GL_CONTEXT] =
    g_param_spec_object ("gl-context", NULL, NULL,
                         GDK_TYPE_GL_CONTEXT,
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME);

  properties[PROP_DISPLAY] =
    g_param_spec_object ("display", NULL, NULL,
                         GDK_TYPE_DISPLAY,
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME);

  properties[PROP_USES_GL] =
    g_param_spec_boolean ("uses-gl", NULL, NULL,
                          TRUE,
                          G_PARAM_READABLE | G_PARAM_STATIC_NAME);

  g_object_class_install_properties (gobject_class, N_PROPS, properties);

  gst_element_class_set_metadata (gstelement_class,
      "GtkMediaStream Video Sink",
      "Sink/Video""The video sink used by GtkMediaStream",
      "Matthew Waters <matthew@centricular.com>, "
      "Benjamin Otte <otte@gnome.org>");

  gst_element_class_add_static_pad_template (gstelement_class,
      >k_gst_sink_template);
}

static void
gtk_gst_sink_init (GtkGstSink * gtk_sink)
{
}


Messung V0.5 in Prozent
C=100 H=98 G=98

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet am  2026-07-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik