2017-06-21 06:23:44 +00:00
|
|
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2015-2017 Red Hat Inc.
|
|
|
|
*
|
|
|
|
* 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 2 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, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
* 02111-1307, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "backends/meta-screen-cast-stream-src.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
2019-12-09 13:07:29 +00:00
|
|
|
#include <fcntl.h>
|
2017-06-21 06:23:44 +00:00
|
|
|
#include <pipewire/pipewire.h>
|
2018-01-24 07:25:59 +00:00
|
|
|
#include <spa/param/props.h>
|
|
|
|
#include <spa/param/format-utils.h>
|
|
|
|
#include <spa/param/video/format-utils.h>
|
2020-01-14 08:44:45 +00:00
|
|
|
#include <spa/utils/result.h>
|
2017-06-21 06:23:44 +00:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
|
2021-11-13 15:34:34 +00:00
|
|
|
#ifdef HAVE_NATIVE_BACKEND
|
|
|
|
#include <drm_fourcc.h>
|
|
|
|
#endif
|
|
|
|
|
2019-01-21 13:32:05 +00:00
|
|
|
#include "backends/meta-screen-cast-session.h"
|
2017-06-21 06:23:44 +00:00
|
|
|
#include "backends/meta-screen-cast-stream.h"
|
|
|
|
#include "clutter/clutter-mutter.h"
|
|
|
|
#include "core/meta-fraction.h"
|
|
|
|
#include "meta/boxes.h"
|
|
|
|
|
|
|
|
#define PRIVATE_OWNER_FROM_FIELD(TypeName, field_ptr, field_name) \
|
|
|
|
(TypeName *)((guint8 *)(field_ptr) - G_PRIVATE_OFFSET (TypeName, field_name))
|
|
|
|
|
2018-12-14 16:47:57 +00:00
|
|
|
#define CURSOR_META_SIZE(width, height) \
|
|
|
|
(sizeof (struct spa_meta_cursor) + \
|
|
|
|
sizeof (struct spa_meta_bitmap) + width * height * 4)
|
|
|
|
|
2023-02-03 19:33:57 +00:00
|
|
|
#define NUM_DAMAGED_RECTS 32
|
2021-02-01 09:41:57 +00:00
|
|
|
#define DEFAULT_SIZE SPA_RECTANGLE (1280, 720)
|
|
|
|
#define MIN_SIZE SPA_RECTANGLE (1, 1)
|
|
|
|
#define MAX_SIZE SPA_RECTANGLE (16384, 16386)
|
|
|
|
|
|
|
|
#define DEFAULT_FRAME_RATE SPA_FRACTION (60, 1)
|
|
|
|
#define MIN_FRAME_RATE SPA_FRACTION (1, 1)
|
|
|
|
#define MAX_FRAME_RATE SPA_FRACTION (1000, 1)
|
|
|
|
|
2017-06-21 06:23:44 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
|
|
|
|
PROP_STREAM,
|
|
|
|
};
|
|
|
|
|
2017-06-26 04:50:26 +00:00
|
|
|
enum
|
|
|
|
{
|
2017-06-28 07:17:32 +00:00
|
|
|
READY,
|
2017-06-26 04:50:26 +00:00
|
|
|
CLOSED,
|
|
|
|
|
|
|
|
N_SIGNALS
|
|
|
|
};
|
|
|
|
|
|
|
|
static guint signals[N_SIGNALS];
|
|
|
|
|
2017-06-21 06:23:44 +00:00
|
|
|
typedef struct _MetaPipeWireSource
|
|
|
|
{
|
2022-05-07 20:48:22 +00:00
|
|
|
GSource source;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2020-05-12 14:14:00 +00:00
|
|
|
MetaScreenCastStreamSrc *src;
|
2017-06-21 06:23:44 +00:00
|
|
|
struct pw_loop *pipewire_loop;
|
|
|
|
} MetaPipeWireSource;
|
|
|
|
|
|
|
|
typedef struct _MetaScreenCastStreamSrcPrivate
|
|
|
|
{
|
|
|
|
MetaScreenCastStream *stream;
|
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
struct pw_context *pipewire_context;
|
2017-06-21 06:23:44 +00:00
|
|
|
struct pw_core *pipewire_core;
|
2022-05-07 20:48:22 +00:00
|
|
|
GSource *pipewire_source;
|
2020-01-14 08:44:45 +00:00
|
|
|
struct spa_hook pipewire_core_listener;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2017-06-26 04:55:48 +00:00
|
|
|
gboolean is_enabled;
|
2020-05-12 14:14:00 +00:00
|
|
|
gboolean emit_closed_after_dispatch;
|
2017-06-26 04:55:48 +00:00
|
|
|
|
2017-06-21 06:23:44 +00:00
|
|
|
struct pw_stream *pipewire_stream;
|
|
|
|
struct spa_hook pipewire_stream_listener;
|
2020-01-14 08:44:45 +00:00
|
|
|
uint32_t node_id;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
|
|
|
struct spa_video_info_raw video_format;
|
2020-01-14 08:44:45 +00:00
|
|
|
int video_stride;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2020-07-03 21:50:28 +00:00
|
|
|
int64_t last_frame_timestamp_us;
|
2020-07-03 21:57:31 +00:00
|
|
|
guint follow_up_frame_source_id;
|
2018-11-22 10:59:10 +00:00
|
|
|
|
2019-12-09 13:07:29 +00:00
|
|
|
GHashTable *dmabuf_handles;
|
2023-02-03 19:33:57 +00:00
|
|
|
|
|
|
|
cairo_region_t *redraw_clip;
|
2017-06-21 06:23:44 +00:00
|
|
|
} MetaScreenCastStreamSrcPrivate;
|
|
|
|
|
2021-11-13 15:34:34 +00:00
|
|
|
static struct spa_pod *
|
|
|
|
push_format_object (struct spa_pod_builder *pod_builder,
|
|
|
|
enum spa_video_format format,
|
|
|
|
uint64_t *modifiers,
|
|
|
|
int n_modifiers,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
struct spa_pod_frame pod_frames[2];
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
spa_pod_builder_push_object (pod_builder, &pod_frames[0],
|
|
|
|
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
|
|
|
spa_pod_builder_add (pod_builder,
|
|
|
|
SPA_FORMAT_mediaType,
|
|
|
|
SPA_POD_Id (SPA_MEDIA_TYPE_video),
|
|
|
|
0);
|
|
|
|
spa_pod_builder_add (pod_builder,
|
|
|
|
SPA_FORMAT_mediaSubtype,
|
|
|
|
SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw),
|
|
|
|
0);
|
|
|
|
spa_pod_builder_add (pod_builder,
|
|
|
|
SPA_FORMAT_VIDEO_format, SPA_POD_Id (format),
|
|
|
|
0);
|
|
|
|
#ifdef HAVE_NATIVE_BACKEND
|
|
|
|
if (n_modifiers == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID)
|
|
|
|
{
|
|
|
|
spa_pod_builder_prop (pod_builder,
|
|
|
|
SPA_FORMAT_VIDEO_modifier,
|
|
|
|
SPA_POD_PROP_FLAG_MANDATORY);
|
|
|
|
spa_pod_builder_long (pod_builder, modifiers[0]);
|
|
|
|
}
|
|
|
|
else if (n_modifiers > 0)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
spa_pod_builder_prop (pod_builder,
|
|
|
|
SPA_FORMAT_VIDEO_modifier,
|
|
|
|
(SPA_POD_PROP_FLAG_MANDATORY |
|
|
|
|
SPA_POD_PROP_FLAG_DONT_FIXATE));
|
|
|
|
spa_pod_builder_push_choice (pod_builder, &pod_frames[1],
|
|
|
|
SPA_CHOICE_Enum,
|
|
|
|
0);
|
|
|
|
spa_pod_builder_long (pod_builder, modifiers[0]);
|
|
|
|
for (i = 0; i < n_modifiers; i++)
|
|
|
|
spa_pod_builder_long (pod_builder, modifiers[i]);
|
|
|
|
spa_pod_builder_pop (pod_builder, &pod_frames[1]);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_NATIVE_BACKEND */
|
|
|
|
|
|
|
|
va_start (args, n_modifiers);
|
|
|
|
spa_pod_builder_addv (pod_builder, args);
|
|
|
|
va_end (args);
|
|
|
|
return spa_pod_builder_pop (pod_builder, &pod_frames[0]);
|
|
|
|
}
|
|
|
|
|
2017-06-21 06:23:44 +00:00
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_init_initable_iface (GInitableIface *iface);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (MetaScreenCastStreamSrc,
|
|
|
|
meta_screen_cast_stream_src,
|
|
|
|
G_TYPE_OBJECT,
|
|
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
|
|
|
|
meta_screen_cast_stream_src_init_initable_iface)
|
|
|
|
G_ADD_PRIVATE (MetaScreenCastStreamSrc))
|
|
|
|
|
2021-02-01 09:41:57 +00:00
|
|
|
static gboolean
|
2017-06-21 06:23:44 +00:00
|
|
|
meta_screen_cast_stream_src_get_specs (MetaScreenCastStreamSrc *src,
|
|
|
|
int *width,
|
|
|
|
int *height,
|
|
|
|
float *frame_rate)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcClass *klass =
|
|
|
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
|
|
|
|
2021-02-01 09:41:57 +00:00
|
|
|
return klass->get_specs (src, width, height, frame_rate);
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
2018-11-22 10:59:10 +00:00
|
|
|
static gboolean
|
|
|
|
meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src,
|
|
|
|
MetaRectangle *crop_rect)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcClass *klass =
|
|
|
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
|
|
|
|
|
|
|
if (klass->get_videocrop)
|
|
|
|
return klass->get_videocrop (src, crop_rect);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-12-14 16:47:57 +00:00
|
|
|
static gboolean
|
2020-07-03 14:57:01 +00:00
|
|
|
meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src,
|
2021-02-01 08:17:15 +00:00
|
|
|
int width,
|
|
|
|
int height,
|
|
|
|
int stride,
|
2020-07-03 14:57:01 +00:00
|
|
|
uint8_t *data,
|
|
|
|
GError **error)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcClass *klass =
|
|
|
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
|
|
|
|
2021-02-01 08:17:15 +00:00
|
|
|
return klass->record_to_buffer (src, width, height, stride, data, error);
|
2018-12-14 16:47:57 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 13:07:29 +00:00
|
|
|
static gboolean
|
2020-07-03 14:57:01 +00:00
|
|
|
meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src,
|
|
|
|
CoglFramebuffer *framebuffer,
|
|
|
|
GError **error)
|
2019-12-09 13:07:29 +00:00
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcClass *klass =
|
|
|
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
|
|
|
|
2020-07-03 14:57:01 +00:00
|
|
|
return klass->record_to_framebuffer (src, framebuffer, error);
|
2019-12-09 13:07:29 +00:00
|
|
|
}
|
|
|
|
|
2020-07-03 21:57:31 +00:00
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_record_follow_up (MetaScreenCastStreamSrc *src)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcClass *klass =
|
|
|
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
|
|
|
|
|
|
|
klass->record_follow_up (src);
|
|
|
|
}
|
|
|
|
|
2018-12-14 16:47:57 +00:00
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
|
|
|
|
struct spa_meta_cursor *spa_meta_cursor)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcClass *klass =
|
|
|
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
|
|
|
|
|
|
|
if (klass->set_cursor_metadata)
|
|
|
|
klass->set_cursor_metadata (src, spa_meta_cursor);
|
|
|
|
}
|
|
|
|
|
2019-01-21 13:32:05 +00:00
|
|
|
static gboolean
|
|
|
|
draw_cursor_sprite_via_offscreen (MetaScreenCastStreamSrc *src,
|
|
|
|
CoglTexture *cursor_texture,
|
|
|
|
int bitmap_width,
|
|
|
|
int bitmap_height,
|
2022-07-02 16:50:45 +00:00
|
|
|
MetaMonitorTransform transform,
|
2019-01-24 17:36:34 +00:00
|
|
|
uint8_t *bitmap_data,
|
2019-01-21 13:32:05 +00:00
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
|
|
|
MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream);
|
|
|
|
MetaScreenCast *screen_cast =
|
|
|
|
meta_screen_cast_session_get_screen_cast (session);
|
|
|
|
MetaBackend *backend = meta_screen_cast_get_backend (screen_cast);
|
|
|
|
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
|
|
|
|
CoglContext *cogl_context =
|
|
|
|
clutter_backend_get_cogl_context (clutter_backend);
|
|
|
|
CoglTexture2D *bitmap_texture;
|
|
|
|
CoglOffscreen *offscreen;
|
|
|
|
CoglFramebuffer *fb;
|
|
|
|
CoglPipeline *pipeline;
|
|
|
|
CoglColor clear_color;
|
2022-07-02 16:50:45 +00:00
|
|
|
graphene_matrix_t matrix;
|
2019-01-21 13:32:05 +00:00
|
|
|
|
|
|
|
bitmap_texture = cogl_texture_2d_new_with_size (cogl_context,
|
|
|
|
bitmap_width, bitmap_height);
|
|
|
|
cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (bitmap_texture),
|
|
|
|
FALSE);
|
|
|
|
if (!cogl_texture_allocate (COGL_TEXTURE (bitmap_texture), error))
|
|
|
|
{
|
|
|
|
cogl_object_unref (bitmap_texture);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (bitmap_texture));
|
|
|
|
fb = COGL_FRAMEBUFFER (offscreen);
|
|
|
|
cogl_object_unref (bitmap_texture);
|
|
|
|
if (!cogl_framebuffer_allocate (fb, error))
|
|
|
|
{
|
2020-10-13 09:35:47 +00:00
|
|
|
g_object_unref (fb);
|
2019-01-21 13:32:05 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
pipeline = cogl_pipeline_new (cogl_context);
|
|
|
|
cogl_pipeline_set_layer_texture (pipeline, 0, cursor_texture);
|
|
|
|
cogl_pipeline_set_layer_filters (pipeline, 0,
|
|
|
|
COGL_PIPELINE_FILTER_LINEAR,
|
|
|
|
COGL_PIPELINE_FILTER_LINEAR);
|
2022-07-02 16:50:45 +00:00
|
|
|
|
|
|
|
graphene_matrix_init_identity (&matrix);
|
|
|
|
meta_monitor_transform_transform_matrix (transform,
|
|
|
|
&matrix);
|
|
|
|
cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
|
|
|
|
|
2019-01-21 13:32:05 +00:00
|
|
|
cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
|
|
|
|
cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
|
|
|
|
cogl_framebuffer_draw_rectangle (fb, pipeline,
|
|
|
|
-1, 1, 1, -1);
|
|
|
|
cogl_object_unref (pipeline);
|
|
|
|
|
|
|
|
cogl_framebuffer_read_pixels (fb,
|
|
|
|
0, 0,
|
|
|
|
bitmap_width, bitmap_height,
|
|
|
|
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
|
2019-01-24 17:36:34 +00:00
|
|
|
bitmap_data);
|
2020-10-13 09:35:47 +00:00
|
|
|
g_object_unref (fb);
|
2019-01-21 13:32:05 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2019-01-24 17:40:06 +00:00
|
|
|
gboolean
|
|
|
|
meta_screen_cast_stream_src_draw_cursor_into (MetaScreenCastStreamSrc *src,
|
|
|
|
CoglTexture *cursor_texture,
|
|
|
|
float scale,
|
2022-07-02 16:50:45 +00:00
|
|
|
MetaMonitorTransform transform,
|
2019-01-24 17:40:06 +00:00
|
|
|
uint8_t *data,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
int texture_width, texture_height;
|
|
|
|
int width, height;
|
|
|
|
|
|
|
|
texture_width = cogl_texture_get_width (cursor_texture);
|
|
|
|
texture_height = cogl_texture_get_height (cursor_texture);
|
|
|
|
width = texture_width * scale;
|
|
|
|
height = texture_height * scale;
|
|
|
|
|
|
|
|
if (texture_width == width &&
|
2022-07-02 16:50:45 +00:00
|
|
|
texture_height == height &&
|
|
|
|
transform == META_MONITOR_TRANSFORM_NORMAL)
|
2019-01-24 17:40:06 +00:00
|
|
|
{
|
|
|
|
cogl_texture_get_data (cursor_texture,
|
|
|
|
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
|
|
|
|
texture_width * 4,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!draw_cursor_sprite_via_offscreen (src,
|
|
|
|
cursor_texture,
|
|
|
|
width,
|
|
|
|
height,
|
2022-07-02 16:50:45 +00:00
|
|
|
transform,
|
2019-01-24 17:40:06 +00:00
|
|
|
data,
|
|
|
|
error))
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2019-01-21 13:32:05 +00:00
|
|
|
void
|
|
|
|
meta_screen_cast_stream_src_unset_cursor_metadata (MetaScreenCastStreamSrc *src,
|
|
|
|
struct spa_meta_cursor *spa_meta_cursor)
|
|
|
|
{
|
2020-08-27 13:08:34 +00:00
|
|
|
spa_meta_cursor->id = 0;
|
2019-01-21 13:32:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_screen_cast_stream_src_set_cursor_position_metadata (MetaScreenCastStreamSrc *src,
|
|
|
|
struct spa_meta_cursor *spa_meta_cursor,
|
|
|
|
int x,
|
|
|
|
int y)
|
|
|
|
{
|
|
|
|
spa_meta_cursor->id = 1;
|
|
|
|
spa_meta_cursor->position.x = x;
|
|
|
|
spa_meta_cursor->position.y = y;
|
|
|
|
spa_meta_cursor->hotspot.x = 0;
|
|
|
|
spa_meta_cursor->hotspot.y = 0;
|
|
|
|
spa_meta_cursor->bitmap_offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (MetaScreenCastStreamSrc *src,
|
|
|
|
struct spa_meta_cursor *spa_meta_cursor,
|
|
|
|
int x,
|
|
|
|
int y)
|
|
|
|
{
|
|
|
|
struct spa_meta_bitmap *spa_meta_bitmap;
|
|
|
|
|
|
|
|
spa_meta_cursor->id = 1;
|
|
|
|
spa_meta_cursor->position.x = x;
|
|
|
|
spa_meta_cursor->position.y = y;
|
|
|
|
|
|
|
|
spa_meta_cursor->bitmap_offset = sizeof (struct spa_meta_cursor);
|
|
|
|
|
|
|
|
spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
|
|
|
|
spa_meta_cursor->bitmap_offset,
|
|
|
|
struct spa_meta_bitmap);
|
2020-01-14 08:44:45 +00:00
|
|
|
spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA;
|
2019-01-21 13:32:05 +00:00
|
|
|
spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap);
|
|
|
|
|
|
|
|
spa_meta_cursor->hotspot.x = 0;
|
|
|
|
spa_meta_cursor->hotspot.y = 0;
|
|
|
|
|
|
|
|
*spa_meta_bitmap = (struct spa_meta_bitmap) { 0 };
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_screen_cast_stream_src_set_cursor_sprite_metadata (MetaScreenCastStreamSrc *src,
|
|
|
|
struct spa_meta_cursor *spa_meta_cursor,
|
|
|
|
MetaCursorSprite *cursor_sprite,
|
|
|
|
int x,
|
|
|
|
int y,
|
2022-07-02 16:50:45 +00:00
|
|
|
float scale,
|
|
|
|
MetaMonitorTransform transform)
|
2019-01-21 13:32:05 +00:00
|
|
|
{
|
|
|
|
CoglTexture *cursor_texture;
|
|
|
|
struct spa_meta_bitmap *spa_meta_bitmap;
|
|
|
|
int hotspot_x, hotspot_y;
|
|
|
|
int texture_width, texture_height;
|
|
|
|
int bitmap_width, bitmap_height;
|
2019-01-24 17:36:34 +00:00
|
|
|
uint8_t *bitmap_data;
|
2019-01-24 17:40:06 +00:00
|
|
|
GError *error = NULL;
|
2019-01-21 13:32:05 +00:00
|
|
|
|
|
|
|
cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
|
|
|
|
if (!cursor_texture)
|
|
|
|
{
|
|
|
|
meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (src,
|
|
|
|
spa_meta_cursor,
|
|
|
|
x, y);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spa_meta_cursor->id = 1;
|
|
|
|
spa_meta_cursor->position.x = x;
|
|
|
|
spa_meta_cursor->position.y = y;
|
|
|
|
|
|
|
|
spa_meta_cursor->bitmap_offset = sizeof (struct spa_meta_cursor);
|
|
|
|
|
|
|
|
spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
|
|
|
|
spa_meta_cursor->bitmap_offset,
|
|
|
|
struct spa_meta_bitmap);
|
2020-01-14 08:44:45 +00:00
|
|
|
spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA;
|
2019-01-21 13:32:05 +00:00
|
|
|
spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap);
|
|
|
|
|
|
|
|
meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y);
|
|
|
|
spa_meta_cursor->hotspot.x = (int32_t) roundf (hotspot_x * scale);
|
|
|
|
spa_meta_cursor->hotspot.y = (int32_t) roundf (hotspot_y * scale);
|
|
|
|
|
|
|
|
texture_width = cogl_texture_get_width (cursor_texture);
|
|
|
|
texture_height = cogl_texture_get_height (cursor_texture);
|
screen-cast/src: Ceil cursor buffer size
meta_screen_cast_stream_src_set_cursor_sprite_metadata() receives
the cursor sprite, position, and scale, and with that it downloads
the cursor sprite by drawing it into a separate framebuffer, then
calls cogl_framebuffer_read_pixels() in it - this is the offscren
path that is very common when using screen capturing applications
such as OBS Studio.
There's a sneaky issue in this code path though: the 'scale' value
is a float. The cursor size is then determined by multiplying the
sprite width and height - two integer variables - by scale, and
this relies on standard float-to-int conversions. This is problematic
as sometimes the rounded values disagree with what is expected by
cogl_framebuffer_read_pixels(). If the packing of either the cursor
width or height is off by one, glReadPixels() will try to write into
off bounds, which crashes.
This can be reproduced by enabling fractional scaling, setting a 150%
zoom level, on a 4K screen, and opening any commit with an image diff
in gitlab.gnome.org, all while screencasting. When hovering the new
image, the cursor sprite will be such that it triggers this code path,
and reproduces this issue.
Fix this by always ceiling the cursor sprite sizes.
Closes https://gitlab.gnome.org/GNOME/mutter/-/issues/2542
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2736>
2022-12-03 12:25:52 +00:00
|
|
|
bitmap_width = ceilf (texture_width * scale);
|
|
|
|
bitmap_height = ceilf (texture_height * scale);
|
2019-01-21 13:32:05 +00:00
|
|
|
|
|
|
|
spa_meta_bitmap->size.width = bitmap_width;
|
|
|
|
spa_meta_bitmap->size.height = bitmap_height;
|
|
|
|
spa_meta_bitmap->stride = bitmap_width * 4;
|
|
|
|
|
|
|
|
bitmap_data = SPA_MEMBER (spa_meta_bitmap,
|
|
|
|
spa_meta_bitmap->offset,
|
2019-01-24 17:36:34 +00:00
|
|
|
uint8_t);
|
2019-01-21 13:32:05 +00:00
|
|
|
|
2019-01-24 17:40:06 +00:00
|
|
|
if (!meta_screen_cast_stream_src_draw_cursor_into (src,
|
|
|
|
cursor_texture,
|
|
|
|
scale,
|
2022-07-02 16:50:45 +00:00
|
|
|
transform,
|
2019-01-24 17:40:06 +00:00
|
|
|
bitmap_data,
|
|
|
|
&error))
|
2019-01-21 13:32:05 +00:00
|
|
|
{
|
2019-01-24 17:40:06 +00:00
|
|
|
g_warning ("Failed to draw cursor: %s", error->message);
|
|
|
|
g_error_free (error);
|
|
|
|
spa_meta_cursor->id = 0;
|
2019-01-21 13:32:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-14 16:47:57 +00:00
|
|
|
static void
|
|
|
|
add_cursor_metadata (MetaScreenCastStreamSrc *src,
|
|
|
|
struct spa_buffer *spa_buffer)
|
|
|
|
{
|
|
|
|
struct spa_meta_cursor *spa_meta_cursor;
|
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
spa_meta_cursor = spa_buffer_find_meta_data (spa_buffer, SPA_META_Cursor,
|
|
|
|
sizeof (*spa_meta_cursor));
|
2018-12-14 16:47:57 +00:00
|
|
|
if (spa_meta_cursor)
|
|
|
|
meta_screen_cast_stream_src_set_cursor_metadata (src, spa_meta_cursor);
|
|
|
|
}
|
|
|
|
|
2023-01-24 15:03:05 +00:00
|
|
|
static MetaScreenCastRecordResult
|
2018-12-14 16:47:57 +00:00
|
|
|
maybe_record_cursor (MetaScreenCastStreamSrc *src,
|
2020-02-24 17:14:07 +00:00
|
|
|
struct spa_buffer *spa_buffer)
|
2018-12-14 16:47:57 +00:00
|
|
|
{
|
|
|
|
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
|
|
|
|
|
|
|
switch (meta_screen_cast_stream_get_cursor_mode (stream))
|
|
|
|
{
|
|
|
|
case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
|
2023-01-24 15:03:05 +00:00
|
|
|
return META_SCREEN_CAST_RECORD_RESULT_RECORDED_NOTHING;
|
2018-12-14 16:47:57 +00:00
|
|
|
case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
|
2023-01-24 15:03:05 +00:00
|
|
|
return META_SCREEN_CAST_RECORD_RESULT_RECORDED_CURSOR;
|
2018-12-14 16:47:57 +00:00
|
|
|
case META_SCREEN_CAST_CURSOR_MODE_METADATA:
|
|
|
|
add_cursor_metadata (src, spa_buffer);
|
2023-01-24 15:03:05 +00:00
|
|
|
return META_SCREEN_CAST_RECORD_RESULT_RECORDED_CURSOR;
|
2018-12-14 16:47:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_assert_not_reached ();
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 13:07:29 +00:00
|
|
|
static gboolean
|
2020-07-03 14:57:01 +00:00
|
|
|
do_record_frame (MetaScreenCastStreamSrc *src,
|
2021-12-27 19:39:34 +00:00
|
|
|
MetaScreenCastRecordFlag flags,
|
2020-07-03 14:57:01 +00:00
|
|
|
struct spa_buffer *spa_buffer,
|
|
|
|
uint8_t *data,
|
|
|
|
GError **error)
|
2019-12-09 13:07:29 +00:00
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
2021-12-27 19:39:34 +00:00
|
|
|
gboolean dmabuf_only;
|
2019-12-09 13:07:29 +00:00
|
|
|
|
2021-12-27 19:39:34 +00:00
|
|
|
dmabuf_only = flags & META_SCREEN_CAST_RECORD_FLAG_DMABUF_ONLY;
|
2022-12-03 22:15:19 +00:00
|
|
|
if (dmabuf_only && spa_buffer->datas[0].type != SPA_DATA_DmaBuf)
|
|
|
|
return FALSE;
|
|
|
|
|
2021-12-27 19:39:34 +00:00
|
|
|
if (!dmabuf_only &&
|
|
|
|
(spa_buffer->datas[0].data ||
|
|
|
|
spa_buffer->datas[0].type == SPA_DATA_MemFd))
|
2019-12-09 13:07:29 +00:00
|
|
|
{
|
2021-02-01 08:17:15 +00:00
|
|
|
int width = priv->video_format.size.width;
|
|
|
|
int height = priv->video_format.size.height;
|
|
|
|
int stride = priv->video_stride;
|
|
|
|
|
|
|
|
return meta_screen_cast_stream_src_record_to_buffer (src,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
stride,
|
|
|
|
data,
|
|
|
|
error);
|
2019-12-09 13:07:29 +00:00
|
|
|
}
|
|
|
|
else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf)
|
|
|
|
{
|
|
|
|
CoglDmaBufHandle *dmabuf_handle =
|
|
|
|
g_hash_table_lookup (priv->dmabuf_handles,
|
|
|
|
GINT_TO_POINTER (spa_buffer->datas[0].fd));
|
|
|
|
CoglFramebuffer *dmabuf_fbo =
|
|
|
|
cogl_dma_buf_handle_get_framebuffer (dmabuf_handle);
|
|
|
|
|
2020-07-03 14:42:45 +00:00
|
|
|
return meta_screen_cast_stream_src_record_to_framebuffer (src,
|
2020-07-03 14:57:01 +00:00
|
|
|
dmabuf_fbo,
|
|
|
|
error);
|
2019-12-09 13:07:29 +00:00
|
|
|
}
|
|
|
|
|
2020-07-03 14:57:01 +00:00
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
|
|
"Unknown SPA buffer type %u", spa_buffer->datas[0].type);
|
2019-12-09 13:07:29 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-07-03 21:57:31 +00:00
|
|
|
gboolean
|
|
|
|
meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
return priv->follow_up_frame_source_id != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
follow_up_frame_cb (gpointer user_data)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrc *src = user_data;
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
priv->follow_up_frame_source_id = 0;
|
|
|
|
meta_screen_cast_stream_src_record_follow_up (src);
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
maybe_schedule_follow_up_frame (MetaScreenCastStreamSrc *src,
|
|
|
|
int64_t timeout_us)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
if (priv->follow_up_frame_source_id)
|
|
|
|
return;
|
|
|
|
|
|
|
|
priv->follow_up_frame_source_id = g_timeout_add (us2ms (timeout_us),
|
|
|
|
follow_up_frame_cb,
|
|
|
|
src);
|
|
|
|
}
|
|
|
|
|
2022-07-13 14:40:11 +00:00
|
|
|
static int32_t
|
|
|
|
meta_screen_cast_stream_src_calculate_stride (MetaScreenCastStreamSrc *src,
|
|
|
|
struct spa_data *spa_data)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
CoglDmaBufHandle *dmabuf_handle = NULL;
|
|
|
|
|
|
|
|
if (spa_data->type == SPA_DATA_DmaBuf)
|
|
|
|
{
|
|
|
|
dmabuf_handle = g_hash_table_lookup (priv->dmabuf_handles,
|
|
|
|
GINT_TO_POINTER (spa_data->fd));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dmabuf_handle)
|
|
|
|
return cogl_dma_buf_handle_get_stride (dmabuf_handle);
|
|
|
|
else
|
|
|
|
return priv->video_stride;
|
|
|
|
}
|
|
|
|
|
2023-02-03 19:33:57 +00:00
|
|
|
static void
|
|
|
|
maybe_add_damaged_regions_metadata (MetaScreenCastStreamSrc *src,
|
|
|
|
struct spa_buffer *spa_buffer)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv;
|
|
|
|
struct spa_meta *spa_meta_video_damage;
|
|
|
|
struct spa_meta_region *meta_region;
|
|
|
|
|
|
|
|
spa_meta_video_damage =
|
|
|
|
spa_buffer_find_meta (spa_buffer, SPA_META_VideoDamage);
|
|
|
|
if (!spa_meta_video_damage)
|
|
|
|
return;
|
|
|
|
|
|
|
|
priv = meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
if (!priv->redraw_clip)
|
|
|
|
{
|
|
|
|
spa_meta_for_each (meta_region, spa_meta_video_damage)
|
|
|
|
{
|
|
|
|
meta_region->region = SPA_REGION (0, 0, priv->video_format.size.width,
|
|
|
|
priv->video_format.size.height);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int n_rectangles;
|
|
|
|
int num_buffers_available;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
n_rectangles = cairo_region_num_rectangles (priv->redraw_clip);
|
|
|
|
num_buffers_available = 0;
|
|
|
|
|
|
|
|
spa_meta_for_each (meta_region, spa_meta_video_damage)
|
|
|
|
{
|
|
|
|
++num_buffers_available;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_buffers_available < n_rectangles)
|
|
|
|
{
|
|
|
|
spa_meta_for_each (meta_region, spa_meta_video_damage)
|
|
|
|
{
|
|
|
|
g_warning ("Not enough buffers (%d) to accomodate damaged "
|
|
|
|
"regions (%d)", num_buffers_available, n_rectangles);
|
|
|
|
meta_region->region = SPA_REGION (0, 0,
|
|
|
|
priv->video_format.size.width,
|
|
|
|
priv->video_format.size.height);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spa_meta_for_each (meta_region, spa_meta_video_damage)
|
|
|
|
{
|
|
|
|
cairo_rectangle_int_t rect;
|
|
|
|
|
|
|
|
cairo_region_get_rectangle (priv->redraw_clip, i, &rect);
|
|
|
|
meta_region->region = SPA_REGION (rect.x, rect.y,
|
|
|
|
rect.width, rect.height);
|
|
|
|
|
|
|
|
if (++i == n_rectangles)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
|
|
|
|
}
|
|
|
|
|
2023-01-24 15:03:05 +00:00
|
|
|
MetaScreenCastRecordResult
|
2020-07-03 14:48:52 +00:00
|
|
|
meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src,
|
2023-02-03 19:33:57 +00:00
|
|
|
MetaScreenCastRecordFlag flags,
|
|
|
|
const cairo_region_t *redraw_clip)
|
2023-01-24 15:03:05 +00:00
|
|
|
{
|
|
|
|
int64_t now_us = g_get_monotonic_time ();
|
|
|
|
|
|
|
|
return meta_screen_cast_stream_src_maybe_record_frame_with_timestamp (src,
|
|
|
|
flags,
|
|
|
|
redraw_clip,
|
|
|
|
now_us);
|
|
|
|
}
|
|
|
|
|
|
|
|
MetaScreenCastRecordResult
|
|
|
|
meta_screen_cast_stream_src_maybe_record_frame_with_timestamp (MetaScreenCastStreamSrc *src,
|
|
|
|
MetaScreenCastRecordFlag flags,
|
|
|
|
const cairo_region_t *redraw_clip,
|
|
|
|
int64_t frame_timestamp_us)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
2023-01-24 15:03:05 +00:00
|
|
|
MetaScreenCastRecordResult record_result =
|
|
|
|
META_SCREEN_CAST_RECORD_RESULT_RECORDED_NOTHING;
|
2018-11-22 10:59:10 +00:00
|
|
|
MetaRectangle crop_rect;
|
2018-07-09 15:27:47 +00:00
|
|
|
struct pw_buffer *buffer;
|
|
|
|
struct spa_buffer *spa_buffer;
|
2022-07-15 07:13:52 +00:00
|
|
|
struct spa_meta_header *header;
|
2019-12-09 13:07:29 +00:00
|
|
|
uint8_t *data = NULL;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2023-02-03 19:33:57 +00:00
|
|
|
/* Accumulate the damaged region since we might not schedule a frame capture
|
|
|
|
* eventually but once we do, we should report all the previous damaged areas.
|
|
|
|
*/
|
|
|
|
if (redraw_clip)
|
|
|
|
{
|
|
|
|
if (priv->redraw_clip)
|
|
|
|
cairo_region_union (priv->redraw_clip, redraw_clip);
|
|
|
|
else
|
|
|
|
priv->redraw_clip = cairo_region_copy (redraw_clip);
|
|
|
|
}
|
|
|
|
|
2020-05-12 06:52:01 +00:00
|
|
|
if (priv->video_format.max_framerate.num > 0 &&
|
2020-07-03 21:57:31 +00:00
|
|
|
priv->last_frame_timestamp_us != 0)
|
|
|
|
{
|
|
|
|
int64_t min_interval_us;
|
|
|
|
int64_t time_since_last_frame_us;
|
|
|
|
|
2020-07-08 13:08:23 +00:00
|
|
|
min_interval_us =
|
2022-12-15 19:51:54 +00:00
|
|
|
((G_USEC_PER_SEC * ((int64_t) priv->video_format.max_framerate.denom)) /
|
|
|
|
((int64_t) priv->video_format.max_framerate.num));
|
2020-07-03 21:57:31 +00:00
|
|
|
|
2023-01-24 15:03:05 +00:00
|
|
|
time_since_last_frame_us = frame_timestamp_us - priv->last_frame_timestamp_us;
|
2020-07-03 21:57:31 +00:00
|
|
|
if (time_since_last_frame_us < min_interval_us)
|
|
|
|
{
|
|
|
|
int64_t timeout_us;
|
|
|
|
|
|
|
|
timeout_us = min_interval_us - time_since_last_frame_us;
|
|
|
|
maybe_schedule_follow_up_frame (src, timeout_us);
|
2022-12-01 21:18:32 +00:00
|
|
|
meta_topic (META_DEBUG_SCREEN_CAST,
|
|
|
|
"Skipped recording frame on stream %u, too early",
|
|
|
|
priv->node_id);
|
2023-01-24 15:03:05 +00:00
|
|
|
return record_result;
|
2020-07-03 21:57:31 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-21 06:23:44 +00:00
|
|
|
|
|
|
|
if (!priv->pipewire_stream)
|
2023-01-24 15:03:05 +00:00
|
|
|
return META_SCREEN_CAST_RECORD_RESULT_RECORDED_NOTHING;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2023-01-25 20:15:14 +00:00
|
|
|
meta_topic (META_DEBUG_SCREEN_CAST, "Recording %s frame on stream %u",
|
|
|
|
flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY ?
|
2023-02-03 19:33:57 +00:00
|
|
|
"cursor" : "full",
|
2022-12-01 21:18:32 +00:00
|
|
|
priv->node_id);
|
|
|
|
|
2018-07-09 15:27:47 +00:00
|
|
|
buffer = pw_stream_dequeue_buffer (priv->pipewire_stream);
|
2018-05-03 14:07:22 +00:00
|
|
|
if (!buffer)
|
2021-02-05 08:37:09 +00:00
|
|
|
{
|
|
|
|
meta_topic (META_DEBUG_SCREEN_CAST,
|
|
|
|
"Couldn't dequeue a buffer from pipewire stream (node id %u), "
|
|
|
|
"maybe your encoding is too slow?",
|
|
|
|
pw_stream_get_node_id (priv->pipewire_stream));
|
2023-01-24 15:03:05 +00:00
|
|
|
return record_result;
|
2021-02-05 08:37:09 +00:00
|
|
|
}
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2018-07-09 15:27:47 +00:00
|
|
|
spa_buffer = buffer->buffer;
|
2019-12-09 13:07:29 +00:00
|
|
|
data = spa_buffer->datas[0].data;
|
2018-07-09 15:27:47 +00:00
|
|
|
|
2022-07-15 07:13:52 +00:00
|
|
|
header = spa_buffer_find_meta_data (spa_buffer,
|
|
|
|
SPA_META_Header,
|
|
|
|
sizeof (*header));
|
|
|
|
|
2019-12-09 13:07:29 +00:00
|
|
|
if (spa_buffer->datas[0].type != SPA_DATA_DmaBuf && !data)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
2019-12-09 13:07:29 +00:00
|
|
|
g_critical ("Invalid buffer data");
|
2022-07-15 07:13:52 +00:00
|
|
|
if (header)
|
|
|
|
header->flags = SPA_META_HEADER_FLAG_CORRUPTED;
|
|
|
|
|
|
|
|
pw_stream_queue_buffer (priv->pipewire_stream, buffer);
|
2023-01-24 15:03:05 +00:00
|
|
|
return record_result;
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
2020-07-03 14:52:43 +00:00
|
|
|
if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY))
|
2018-11-22 10:59:10 +00:00
|
|
|
{
|
2023-01-24 17:46:43 +00:00
|
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
|
2020-07-03 21:57:31 +00:00
|
|
|
g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove);
|
2021-12-27 19:39:34 +00:00
|
|
|
if (do_record_frame (src, flags, spa_buffer, data, &error))
|
2020-07-03 14:52:43 +00:00
|
|
|
{
|
2023-02-03 19:33:57 +00:00
|
|
|
maybe_add_damaged_regions_metadata (src, spa_buffer);
|
2022-07-13 14:40:11 +00:00
|
|
|
struct spa_data *spa_data = &spa_buffer->datas[0];
|
2020-07-03 14:52:43 +00:00
|
|
|
struct spa_meta_region *spa_meta_video_crop;
|
2018-12-14 16:47:57 +00:00
|
|
|
|
2022-07-13 14:40:11 +00:00
|
|
|
spa_data->chunk->size = spa_data->maxsize;
|
|
|
|
spa_data->chunk->stride =
|
|
|
|
meta_screen_cast_stream_src_calculate_stride (src, spa_data);
|
2022-03-06 21:03:21 +00:00
|
|
|
spa_data->chunk->flags = SPA_CHUNK_FLAG_NONE;
|
2018-12-14 16:47:57 +00:00
|
|
|
|
2020-07-03 14:52:43 +00:00
|
|
|
/* Update VideoCrop if needed */
|
|
|
|
spa_meta_video_crop =
|
|
|
|
spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop,
|
|
|
|
sizeof (*spa_meta_video_crop));
|
|
|
|
if (spa_meta_video_crop)
|
2018-12-14 16:47:57 +00:00
|
|
|
{
|
2020-07-03 14:52:43 +00:00
|
|
|
if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect))
|
|
|
|
{
|
|
|
|
spa_meta_video_crop->region.position.x = crop_rect.x;
|
|
|
|
spa_meta_video_crop->region.position.y = crop_rect.y;
|
|
|
|
spa_meta_video_crop->region.size.width = crop_rect.width;
|
|
|
|
spa_meta_video_crop->region.size.height = crop_rect.height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spa_meta_video_crop->region.position.x = 0;
|
|
|
|
spa_meta_video_crop->region.position.y = 0;
|
2021-02-01 08:17:15 +00:00
|
|
|
spa_meta_video_crop->region.size.width =
|
|
|
|
priv->video_format.size.width;
|
|
|
|
spa_meta_video_crop->region.size.height =
|
|
|
|
priv->video_format.size.height;
|
2020-07-03 14:52:43 +00:00
|
|
|
}
|
2018-12-14 16:47:57 +00:00
|
|
|
}
|
2023-01-24 15:03:05 +00:00
|
|
|
|
|
|
|
record_result |= META_SCREEN_CAST_RECORD_RESULT_RECORDED_FRAME;
|
2018-11-22 10:59:10 +00:00
|
|
|
}
|
2020-07-03 14:52:43 +00:00
|
|
|
else
|
|
|
|
{
|
2022-12-03 22:15:19 +00:00
|
|
|
if (error)
|
|
|
|
g_warning ("Failed to record screen cast frame: %s", error->message);
|
2020-07-03 14:52:43 +00:00
|
|
|
spa_buffer->datas[0].chunk->size = 0;
|
2022-03-06 21:03:21 +00:00
|
|
|
spa_buffer->datas[0].chunk->flags = SPA_CHUNK_FLAG_CORRUPTED;
|
2020-07-03 14:52:43 +00:00
|
|
|
}
|
2018-11-22 10:59:10 +00:00
|
|
|
}
|
2018-12-14 16:47:57 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
spa_buffer->datas[0].chunk->size = 0;
|
2022-03-06 21:03:21 +00:00
|
|
|
spa_buffer->datas[0].chunk->flags = SPA_CHUNK_FLAG_CORRUPTED;
|
2018-12-14 16:47:57 +00:00
|
|
|
}
|
|
|
|
|
2023-01-24 15:03:05 +00:00
|
|
|
record_result |= maybe_record_cursor (src, spa_buffer);
|
2018-11-22 10:59:10 +00:00
|
|
|
|
2023-01-24 15:03:05 +00:00
|
|
|
priv->last_frame_timestamp_us = frame_timestamp_us;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2022-07-15 07:13:52 +00:00
|
|
|
if (header)
|
2022-07-15 07:34:00 +00:00
|
|
|
{
|
2023-01-24 15:03:05 +00:00
|
|
|
header->pts = frame_timestamp_us * SPA_NSEC_PER_USEC;
|
2022-07-15 07:34:00 +00:00
|
|
|
header->flags = 0;
|
|
|
|
}
|
2022-07-15 07:13:52 +00:00
|
|
|
|
2018-07-09 15:27:47 +00:00
|
|
|
pw_stream_queue_buffer (priv->pipewire_stream, buffer);
|
2023-01-24 15:03:05 +00:00
|
|
|
|
|
|
|
return record_result;
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
2022-03-02 10:24:40 +00:00
|
|
|
gboolean
|
2017-06-26 04:55:48 +00:00
|
|
|
meta_screen_cast_stream_src_is_enabled (MetaScreenCastStreamSrc *src)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
return priv->is_enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_enable (MetaScreenCastStreamSrc *src)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src)->enable (src);
|
|
|
|
|
|
|
|
priv->is_enabled = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_disable (MetaScreenCastStreamSrc *src)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src)->disable (src);
|
|
|
|
|
2020-07-10 07:06:33 +00:00
|
|
|
g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove);
|
|
|
|
|
2017-06-26 04:55:48 +00:00
|
|
|
priv->is_enabled = FALSE;
|
|
|
|
}
|
|
|
|
|
2021-02-01 08:54:56 +00:00
|
|
|
void
|
|
|
|
meta_screen_cast_stream_src_close (MetaScreenCastStreamSrc *src)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
if (meta_screen_cast_stream_src_is_enabled (src))
|
|
|
|
meta_screen_cast_stream_src_disable (src);
|
|
|
|
priv->emit_closed_after_dispatch = TRUE;
|
|
|
|
}
|
|
|
|
|
2017-06-21 06:23:44 +00:00
|
|
|
static void
|
|
|
|
on_stream_state_changed (void *data,
|
|
|
|
enum pw_stream_state old,
|
|
|
|
enum pw_stream_state state,
|
|
|
|
const char *error_message)
|
|
|
|
{
|
2017-06-26 04:50:26 +00:00
|
|
|
MetaScreenCastStreamSrc *src = data;
|
2017-06-28 07:17:32 +00:00
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
2017-06-26 04:50:26 +00:00
|
|
|
|
2022-12-01 21:18:32 +00:00
|
|
|
meta_topic (META_DEBUG_SCREEN_CAST,
|
|
|
|
"New PipeWire stream (%u) state '%s'",
|
|
|
|
priv->node_id,
|
|
|
|
pw_stream_state_as_string (state));
|
|
|
|
|
2017-06-26 04:50:26 +00:00
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case PW_STREAM_STATE_ERROR:
|
2023-02-16 17:01:31 +00:00
|
|
|
if (meta_screen_cast_stream_src_is_enabled (src))
|
|
|
|
meta_screen_cast_stream_src_disable (src);
|
2017-06-26 04:50:26 +00:00
|
|
|
break;
|
|
|
|
case PW_STREAM_STATE_PAUSED:
|
2020-01-14 08:44:45 +00:00
|
|
|
if (priv->node_id == SPA_ID_INVALID && priv->pipewire_stream)
|
|
|
|
{
|
|
|
|
priv->node_id = pw_stream_get_node_id (priv->pipewire_stream);
|
|
|
|
g_signal_emit (src, signals[READY], 0, (unsigned int) priv->node_id);
|
|
|
|
}
|
2017-06-26 04:55:48 +00:00
|
|
|
if (meta_screen_cast_stream_src_is_enabled (src))
|
|
|
|
meta_screen_cast_stream_src_disable (src);
|
|
|
|
break;
|
2017-06-26 04:50:26 +00:00
|
|
|
case PW_STREAM_STATE_STREAMING:
|
2017-06-26 04:55:48 +00:00
|
|
|
if (!meta_screen_cast_stream_src_is_enabled (src))
|
|
|
|
meta_screen_cast_stream_src_enable (src);
|
2017-06-26 04:50:26 +00:00
|
|
|
break;
|
2020-01-14 08:44:45 +00:00
|
|
|
case PW_STREAM_STATE_UNCONNECTED:
|
|
|
|
case PW_STREAM_STATE_CONNECTING:
|
|
|
|
break;
|
2017-06-26 04:50:26 +00:00
|
|
|
}
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
2023-02-03 19:33:57 +00:00
|
|
|
static void
|
|
|
|
add_video_damage_meta_param (struct spa_pod_builder *pod_builder,
|
|
|
|
const struct spa_pod **params,
|
|
|
|
int idx)
|
|
|
|
{
|
|
|
|
const size_t meta_region_size = sizeof (struct spa_meta_region);
|
|
|
|
|
|
|
|
params[idx] = spa_pod_builder_add_object (
|
|
|
|
pod_builder,
|
|
|
|
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
|
|
|
SPA_PARAM_META_type, SPA_POD_Id (SPA_META_VideoDamage),
|
|
|
|
SPA_PARAM_META_size,
|
|
|
|
SPA_POD_CHOICE_RANGE_Int (meta_region_size * NUM_DAMAGED_RECTS,
|
|
|
|
meta_region_size * 1,
|
|
|
|
meta_region_size * NUM_DAMAGED_RECTS));
|
|
|
|
}
|
|
|
|
|
2017-06-21 06:23:44 +00:00
|
|
|
static void
|
2020-01-14 08:44:45 +00:00
|
|
|
on_stream_param_changed (void *data,
|
|
|
|
uint32_t id,
|
|
|
|
const struct spa_pod *format)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrc *src = data;
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
2021-02-01 09:41:57 +00:00
|
|
|
MetaScreenCastStreamSrcClass *klass =
|
|
|
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
2018-01-24 07:25:59 +00:00
|
|
|
uint8_t params_buffer[1024];
|
|
|
|
int32_t width, height, stride, size;
|
|
|
|
struct spa_pod_builder pod_builder;
|
2023-02-03 19:33:57 +00:00
|
|
|
const struct spa_pod *params[5];
|
2023-02-03 19:18:47 +00:00
|
|
|
int n_params = 0;
|
2017-06-21 06:23:44 +00:00
|
|
|
const int bpp = 4;
|
2021-11-13 15:34:40 +00:00
|
|
|
int buffer_types;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
if (!format || id != SPA_PARAM_Format)
|
|
|
|
return;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
|
|
|
spa_format_video_raw_parse (format,
|
2020-01-14 08:44:45 +00:00
|
|
|
&priv->video_format);
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2018-01-24 07:25:59 +00:00
|
|
|
width = priv->video_format.size.width;
|
|
|
|
height = priv->video_format.size.height;
|
|
|
|
stride = SPA_ROUND_UP_N (width * bpp, 4);
|
|
|
|
size = height * stride;
|
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
priv->video_stride = stride;
|
|
|
|
|
2018-01-24 07:25:59 +00:00
|
|
|
pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer));
|
|
|
|
|
2022-04-22 09:31:48 +00:00
|
|
|
buffer_types = 1 << SPA_DATA_MemFd;
|
|
|
|
if (spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier))
|
|
|
|
buffer_types |= 1 << SPA_DATA_DmaBuf;
|
2021-11-13 15:34:40 +00:00
|
|
|
|
2023-02-03 19:18:47 +00:00
|
|
|
params[n_params++] = spa_pod_builder_add_object (
|
2018-01-24 07:25:59 +00:00
|
|
|
&pod_builder,
|
2020-01-14 08:44:45 +00:00
|
|
|
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
|
|
|
|
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (16, 2, 16),
|
|
|
|
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int (1),
|
|
|
|
SPA_PARAM_BUFFERS_size, SPA_POD_Int (size),
|
|
|
|
SPA_PARAM_BUFFERS_stride, SPA_POD_Int (stride),
|
2021-11-13 15:34:40 +00:00
|
|
|
SPA_PARAM_BUFFERS_align, SPA_POD_Int (16),
|
|
|
|
SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int (buffer_types));
|
2020-01-14 08:44:45 +00:00
|
|
|
|
2023-02-03 19:18:47 +00:00
|
|
|
params[n_params++] = spa_pod_builder_add_object (
|
2018-11-22 10:59:10 +00:00
|
|
|
&pod_builder,
|
2020-01-14 08:44:45 +00:00
|
|
|
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
|
|
|
SPA_PARAM_META_type, SPA_POD_Id (SPA_META_VideoCrop),
|
|
|
|
SPA_PARAM_META_size, SPA_POD_Int (sizeof (struct spa_meta_region)));
|
2018-11-22 10:59:10 +00:00
|
|
|
|
2023-02-03 19:18:47 +00:00
|
|
|
params[n_params++] = spa_pod_builder_add_object (
|
2018-12-14 16:47:57 +00:00
|
|
|
&pod_builder,
|
2020-01-14 08:44:45 +00:00
|
|
|
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
|
|
|
SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor),
|
2020-08-23 06:12:08 +00:00
|
|
|
SPA_PARAM_META_size, SPA_POD_Int (CURSOR_META_SIZE (384, 384)));
|
2018-12-14 16:47:57 +00:00
|
|
|
|
2023-02-03 19:18:47 +00:00
|
|
|
params[n_params++] = spa_pod_builder_add_object (
|
2022-07-15 07:13:52 +00:00
|
|
|
&pod_builder,
|
|
|
|
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
|
|
|
SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Header),
|
|
|
|
SPA_PARAM_META_size, SPA_POD_Int (sizeof (struct spa_meta_header)));
|
|
|
|
|
2023-02-03 19:33:57 +00:00
|
|
|
add_video_damage_meta_param (&pod_builder, params, n_params++);
|
|
|
|
|
|
|
|
pw_stream_update_params (priv->pipewire_stream, params, n_params);
|
2021-02-01 09:41:57 +00:00
|
|
|
|
|
|
|
if (klass->notify_params_updated)
|
|
|
|
klass->notify_params_updated (src, &priv->video_format);
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 13:07:29 +00:00
|
|
|
static void
|
|
|
|
on_stream_add_buffer (void *data,
|
|
|
|
struct pw_buffer *buffer)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrc *src = data;
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
2020-06-17 15:48:05 +00:00
|
|
|
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
|
|
|
MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream);
|
|
|
|
MetaScreenCast *screen_cast =
|
|
|
|
meta_screen_cast_session_get_screen_cast (session);
|
2019-12-09 13:07:29 +00:00
|
|
|
CoglDmaBufHandle *dmabuf_handle;
|
|
|
|
struct spa_buffer *spa_buffer = buffer->buffer;
|
|
|
|
struct spa_data *spa_data = spa_buffer->datas;
|
|
|
|
const int bpp = 4;
|
|
|
|
int stride;
|
|
|
|
|
|
|
|
stride = SPA_ROUND_UP_N (priv->video_format.size.width * bpp, 4);
|
|
|
|
|
|
|
|
spa_data[0].mapoffset = 0;
|
|
|
|
spa_data[0].maxsize = stride * priv->video_format.size.height;
|
2021-02-01 17:32:33 +00:00
|
|
|
spa_data[0].data = NULL;
|
2019-12-09 13:07:29 +00:00
|
|
|
|
2021-02-01 17:33:35 +00:00
|
|
|
if (spa_data[0].type & (1 << SPA_DATA_DmaBuf))
|
|
|
|
{
|
|
|
|
dmabuf_handle =
|
|
|
|
meta_screen_cast_create_dma_buf_handle (screen_cast,
|
|
|
|
priv->video_format.size.width,
|
|
|
|
priv->video_format.size.height);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dmabuf_handle = NULL;
|
|
|
|
}
|
2019-12-09 13:07:29 +00:00
|
|
|
|
|
|
|
if (dmabuf_handle)
|
|
|
|
{
|
2022-01-25 13:59:40 +00:00
|
|
|
meta_topic (META_DEBUG_SCREEN_CAST,
|
|
|
|
"Allocating DMA buffer for pw_stream %u",
|
|
|
|
pw_stream_get_node_id (priv->pipewire_stream));
|
|
|
|
|
2019-12-09 13:07:29 +00:00
|
|
|
spa_data[0].type = SPA_DATA_DmaBuf;
|
|
|
|
spa_data[0].flags = SPA_DATA_FLAG_READWRITE;
|
|
|
|
spa_data[0].fd = cogl_dma_buf_handle_get_fd (dmabuf_handle);
|
|
|
|
|
|
|
|
g_hash_table_insert (priv->dmabuf_handles,
|
|
|
|
GINT_TO_POINTER (spa_data[0].fd),
|
|
|
|
dmabuf_handle);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned int seals;
|
|
|
|
|
2021-02-01 17:33:35 +00:00
|
|
|
if (!(spa_data[0].type & (1 << SPA_DATA_MemFd)))
|
|
|
|
{
|
|
|
|
g_critical ("No supported PipeWire stream buffer data type could "
|
|
|
|
"be negotiated");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-25 13:59:40 +00:00
|
|
|
meta_topic (META_DEBUG_SCREEN_CAST,
|
|
|
|
"Allocating MemFd buffer for pw_stream %u",
|
|
|
|
pw_stream_get_node_id (priv->pipewire_stream));
|
|
|
|
|
2019-12-09 13:07:29 +00:00
|
|
|
/* Fallback to a memfd buffer */
|
|
|
|
spa_data[0].type = SPA_DATA_MemFd;
|
|
|
|
spa_data[0].flags = SPA_DATA_FLAG_READWRITE;
|
|
|
|
spa_data[0].fd = memfd_create ("mutter-screen-cast-memfd",
|
|
|
|
MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
|
|
|
if (spa_data[0].fd == -1)
|
|
|
|
{
|
|
|
|
g_critical ("Can't create memfd: %m");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
spa_data[0].mapoffset = 0;
|
|
|
|
spa_data[0].maxsize = stride * priv->video_format.size.height;
|
|
|
|
|
|
|
|
if (ftruncate (spa_data[0].fd, spa_data[0].maxsize) < 0)
|
|
|
|
{
|
2021-02-01 08:30:55 +00:00
|
|
|
close (spa_data[0].fd);
|
|
|
|
spa_data[0].fd = -1;
|
2019-12-09 13:07:29 +00:00
|
|
|
g_critical ("Can't truncate to %d: %m", spa_data[0].maxsize);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL;
|
|
|
|
if (fcntl (spa_data[0].fd, F_ADD_SEALS, seals) == -1)
|
|
|
|
g_warning ("Failed to add seals: %m");
|
|
|
|
|
|
|
|
spa_data[0].data = mmap (NULL,
|
|
|
|
spa_data[0].maxsize,
|
|
|
|
PROT_READ | PROT_WRITE,
|
|
|
|
MAP_SHARED,
|
|
|
|
spa_data[0].fd,
|
|
|
|
spa_data[0].mapoffset);
|
|
|
|
if (spa_data[0].data == MAP_FAILED)
|
|
|
|
{
|
2021-02-01 08:30:55 +00:00
|
|
|
close (spa_data[0].fd);
|
|
|
|
spa_data[0].fd = -1;
|
2019-12-09 13:07:29 +00:00
|
|
|
g_critical ("Failed to mmap memory: %m");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_stream_remove_buffer (void *data,
|
|
|
|
struct pw_buffer *buffer)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrc *src = data;
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
struct spa_buffer *spa_buffer = buffer->buffer;
|
|
|
|
struct spa_data *spa_data = spa_buffer->datas;
|
|
|
|
|
|
|
|
if (spa_data[0].type == SPA_DATA_DmaBuf)
|
|
|
|
{
|
|
|
|
if (!g_hash_table_remove (priv->dmabuf_handles, GINT_TO_POINTER (spa_data[0].fd)))
|
|
|
|
g_critical ("Failed to remove non-exported DMA buffer");
|
|
|
|
}
|
|
|
|
else if (spa_data[0].type == SPA_DATA_MemFd)
|
|
|
|
{
|
2021-02-01 08:30:55 +00:00
|
|
|
g_warn_if_fail (spa_data[0].fd > 0 || !spa_data[0].data);
|
|
|
|
|
|
|
|
if (spa_data[0].fd > 0)
|
|
|
|
{
|
|
|
|
munmap (spa_data[0].data, spa_data[0].maxsize);
|
|
|
|
close (spa_data[0].fd);
|
|
|
|
}
|
2019-12-09 13:07:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-21 06:23:44 +00:00
|
|
|
static const struct pw_stream_events stream_events = {
|
|
|
|
PW_VERSION_STREAM_EVENTS,
|
|
|
|
.state_changed = on_stream_state_changed,
|
2020-01-14 08:44:45 +00:00
|
|
|
.param_changed = on_stream_param_changed,
|
2019-12-09 13:07:29 +00:00
|
|
|
.add_buffer = on_stream_add_buffer,
|
|
|
|
.remove_buffer = on_stream_remove_buffer,
|
2017-06-21 06:23:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct pw_stream *
|
|
|
|
create_pipewire_stream (MetaScreenCastStreamSrc *src,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
2021-11-13 15:34:37 +00:00
|
|
|
#ifdef HAVE_NATIVE_BACKEND
|
|
|
|
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
|
|
|
MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream);
|
|
|
|
MetaScreenCast *screen_cast =
|
|
|
|
meta_screen_cast_session_get_screen_cast (session);
|
|
|
|
MetaBackend *backend = meta_screen_cast_get_backend (screen_cast);
|
|
|
|
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
|
|
|
|
CoglContext *cogl_context =
|
|
|
|
clutter_backend_get_cogl_context (clutter_backend);
|
|
|
|
CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context);
|
|
|
|
#endif /* HAVE_NATIVE_BACKEND */
|
2017-06-21 06:23:44 +00:00
|
|
|
struct pw_stream *pipewire_stream;
|
|
|
|
uint8_t buffer[1024];
|
|
|
|
struct spa_pod_builder pod_builder =
|
|
|
|
SPA_POD_BUILDER_INIT (buffer, sizeof (buffer));
|
2021-02-01 08:17:15 +00:00
|
|
|
int width;
|
|
|
|
int height;
|
2017-06-21 06:23:44 +00:00
|
|
|
float frame_rate;
|
2021-11-13 15:34:37 +00:00
|
|
|
const struct spa_pod *params[2];
|
|
|
|
int n_params = 0;
|
2018-05-03 14:07:22 +00:00
|
|
|
int result;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
priv->node_id = SPA_ID_INVALID;
|
|
|
|
|
|
|
|
pipewire_stream = pw_stream_new (priv->pipewire_core,
|
2017-06-21 06:23:44 +00:00
|
|
|
"meta-screen-cast-src",
|
|
|
|
NULL);
|
2018-05-03 14:07:22 +00:00
|
|
|
if (!pipewire_stream)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
|
|
"Failed to create PipeWire stream: %s",
|
|
|
|
strerror (errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2021-02-01 09:41:57 +00:00
|
|
|
if (meta_screen_cast_stream_src_get_specs (src, &width, &height, &frame_rate))
|
|
|
|
{
|
|
|
|
MetaFraction frame_rate_fraction;
|
|
|
|
struct spa_fraction max_framerate;
|
|
|
|
struct spa_fraction min_framerate;
|
|
|
|
|
|
|
|
frame_rate_fraction = meta_fraction_from_double (frame_rate);
|
|
|
|
|
|
|
|
min_framerate = SPA_FRACTION (1, 1);
|
|
|
|
max_framerate = SPA_FRACTION (frame_rate_fraction.num,
|
|
|
|
frame_rate_fraction.denom);
|
|
|
|
|
2021-11-13 15:34:37 +00:00
|
|
|
#ifdef HAVE_NATIVE_BACKEND
|
|
|
|
if (cogl_renderer_is_dma_buf_supported (cogl_renderer))
|
|
|
|
{
|
|
|
|
uint64_t modifier = DRM_FORMAT_MOD_INVALID;
|
|
|
|
|
|
|
|
params[n_params++] = push_format_object (
|
|
|
|
&pod_builder,
|
|
|
|
SPA_VIDEO_FORMAT_BGRx, &modifier, 1,
|
|
|
|
SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle (&SPA_RECTANGLE (width,
|
|
|
|
height)),
|
|
|
|
SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)),
|
|
|
|
SPA_FORMAT_VIDEO_maxFramerate,
|
|
|
|
SPA_POD_CHOICE_RANGE_Fraction (&max_framerate,
|
|
|
|
&min_framerate,
|
|
|
|
&max_framerate),
|
|
|
|
0);
|
|
|
|
params[n_params++] = push_format_object (
|
|
|
|
&pod_builder,
|
|
|
|
SPA_VIDEO_FORMAT_BGRx, NULL, 0,
|
|
|
|
SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle (&SPA_RECTANGLE (width,
|
|
|
|
height)),
|
|
|
|
SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)),
|
|
|
|
SPA_FORMAT_VIDEO_maxFramerate,
|
|
|
|
SPA_POD_CHOICE_RANGE_Fraction (&max_framerate,
|
|
|
|
&min_framerate,
|
|
|
|
&max_framerate),
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif /* HAVE_NATIVE_BACKEND */
|
|
|
|
{
|
|
|
|
params[n_params++] = push_format_object (
|
|
|
|
&pod_builder,
|
|
|
|
SPA_VIDEO_FORMAT_BGRx, NULL, 0,
|
|
|
|
SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle (&SPA_RECTANGLE (width,
|
|
|
|
height)),
|
|
|
|
SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)),
|
|
|
|
SPA_FORMAT_VIDEO_maxFramerate,
|
|
|
|
SPA_POD_CHOICE_RANGE_Fraction (&max_framerate,
|
|
|
|
&min_framerate,
|
|
|
|
&max_framerate),
|
|
|
|
0);
|
|
|
|
}
|
2021-02-01 09:41:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-13 15:34:37 +00:00
|
|
|
#ifdef HAVE_NATIVE_BACKEND
|
|
|
|
if (cogl_renderer_is_dma_buf_supported (cogl_renderer))
|
|
|
|
{
|
|
|
|
uint64_t modifier = DRM_FORMAT_MOD_INVALID;
|
|
|
|
|
|
|
|
params[n_params++] = push_format_object (
|
|
|
|
&pod_builder,
|
|
|
|
SPA_VIDEO_FORMAT_BGRx, &modifier, 1,
|
|
|
|
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle (&DEFAULT_SIZE,
|
|
|
|
&MIN_SIZE,
|
|
|
|
&MAX_SIZE),
|
|
|
|
SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)),
|
|
|
|
SPA_FORMAT_VIDEO_maxFramerate,
|
|
|
|
SPA_POD_CHOICE_RANGE_Fraction (&DEFAULT_FRAME_RATE,
|
|
|
|
&MIN_FRAME_RATE,
|
|
|
|
&MAX_FRAME_RATE),
|
|
|
|
0);
|
|
|
|
params[n_params++] = push_format_object (
|
|
|
|
&pod_builder,
|
|
|
|
SPA_VIDEO_FORMAT_BGRx, NULL, 0,
|
|
|
|
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle (&DEFAULT_SIZE,
|
|
|
|
&MIN_SIZE,
|
|
|
|
&MAX_SIZE),
|
|
|
|
SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)),
|
|
|
|
SPA_FORMAT_VIDEO_maxFramerate,
|
|
|
|
SPA_POD_CHOICE_RANGE_Fraction (&DEFAULT_FRAME_RATE,
|
|
|
|
&MIN_FRAME_RATE,
|
|
|
|
&MAX_FRAME_RATE),
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif /* HAVE_NATIVE_BACKEND */
|
|
|
|
{
|
|
|
|
params[n_params++] = push_format_object (
|
|
|
|
&pod_builder,
|
|
|
|
SPA_VIDEO_FORMAT_BGRx, NULL, 0,
|
|
|
|
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle (&DEFAULT_SIZE,
|
|
|
|
&MIN_SIZE,
|
|
|
|
&MAX_SIZE),
|
|
|
|
SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)),
|
|
|
|
SPA_FORMAT_VIDEO_maxFramerate,
|
|
|
|
SPA_POD_CHOICE_RANGE_Fraction (&DEFAULT_FRAME_RATE,
|
|
|
|
&MIN_FRAME_RATE,
|
|
|
|
&MAX_FRAME_RATE),
|
|
|
|
0);
|
|
|
|
}
|
2021-02-01 09:41:57 +00:00
|
|
|
}
|
2017-06-21 06:23:44 +00:00
|
|
|
|
|
|
|
pw_stream_add_listener (pipewire_stream,
|
|
|
|
&priv->pipewire_stream_listener,
|
|
|
|
&stream_events,
|
|
|
|
src);
|
|
|
|
|
2018-05-03 14:07:22 +00:00
|
|
|
result = pw_stream_connect (pipewire_stream,
|
|
|
|
PW_DIRECTION_OUTPUT,
|
2020-01-14 08:44:45 +00:00
|
|
|
SPA_ID_INVALID,
|
2018-07-09 15:27:47 +00:00
|
|
|
(PW_STREAM_FLAG_DRIVER |
|
2019-12-09 13:07:29 +00:00
|
|
|
PW_STREAM_FLAG_ALLOC_BUFFERS),
|
2021-11-13 15:34:37 +00:00
|
|
|
params, n_params);
|
2018-05-03 14:07:22 +00:00
|
|
|
if (result != 0)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
2018-05-03 14:07:22 +00:00
|
|
|
"Could not connect: %s", spa_strerror (result));
|
2017-06-21 06:23:44 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pipewire_stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-01-14 08:44:45 +00:00
|
|
|
on_core_error (void *data,
|
|
|
|
uint32_t id,
|
|
|
|
int seq,
|
|
|
|
int res,
|
|
|
|
const char *message)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrc *src = data;
|
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
g_warning ("pipewire remote error: id:%u %s", id, message);
|
|
|
|
|
|
|
|
if (id == PW_ID_CORE && res == -EPIPE)
|
2021-02-01 08:54:56 +00:00
|
|
|
meta_screen_cast_stream_src_close (src);
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2022-05-07 20:48:22 +00:00
|
|
|
pipewire_loop_source_prepare (GSource *source,
|
2017-06-21 06:23:44 +00:00
|
|
|
int *timeout)
|
|
|
|
{
|
|
|
|
*timeout = -1;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pipewire_loop_source_dispatch (GSource *source,
|
|
|
|
GSourceFunc callback,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
MetaPipeWireSource *pipewire_source = (MetaPipeWireSource *) source;
|
2020-05-12 14:14:00 +00:00
|
|
|
MetaScreenCastStreamSrc *src = pipewire_source->src;
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
2017-06-21 06:23:44 +00:00
|
|
|
int result;
|
|
|
|
|
|
|
|
result = pw_loop_iterate (pipewire_source->pipewire_loop, 0);
|
2018-01-24 07:25:59 +00:00
|
|
|
if (result < 0)
|
|
|
|
g_warning ("pipewire_loop_iterate failed: %s", spa_strerror (result));
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2020-05-12 14:14:00 +00:00
|
|
|
if (priv->emit_closed_after_dispatch)
|
|
|
|
g_signal_emit (src, signals[CLOSED], 0);
|
|
|
|
|
2017-06-21 06:23:44 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pipewire_loop_source_finalize (GSource *source)
|
|
|
|
{
|
|
|
|
MetaPipeWireSource *pipewire_source = (MetaPipeWireSource *) source;
|
|
|
|
|
|
|
|
pw_loop_leave (pipewire_source->pipewire_loop);
|
|
|
|
pw_loop_destroy (pipewire_source->pipewire_loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
static GSourceFuncs pipewire_source_funcs =
|
|
|
|
{
|
|
|
|
pipewire_loop_source_prepare,
|
|
|
|
NULL,
|
|
|
|
pipewire_loop_source_dispatch,
|
|
|
|
pipewire_loop_source_finalize
|
|
|
|
};
|
|
|
|
|
2022-05-07 20:48:22 +00:00
|
|
|
static GSource *
|
|
|
|
create_pipewire_source (MetaScreenCastStreamSrc *src,
|
|
|
|
struct pw_loop *pipewire_loop)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
2021-07-12 10:54:26 +00:00
|
|
|
GSource *source;
|
2017-06-21 06:23:44 +00:00
|
|
|
MetaPipeWireSource *pipewire_source;
|
|
|
|
|
2021-07-12 10:54:26 +00:00
|
|
|
source = g_source_new (&pipewire_source_funcs, sizeof (MetaPipeWireSource));
|
|
|
|
g_source_set_name (source, "[mutter] PipeWire");
|
2022-05-07 20:48:22 +00:00
|
|
|
|
2021-07-12 10:54:26 +00:00
|
|
|
pipewire_source = (MetaPipeWireSource *) source;
|
2020-05-12 14:14:00 +00:00
|
|
|
pipewire_source->src = src;
|
2022-05-07 20:48:22 +00:00
|
|
|
pipewire_source->pipewire_loop = pipewire_loop;
|
2018-05-03 14:07:22 +00:00
|
|
|
|
2022-05-07 20:48:22 +00:00
|
|
|
g_source_add_unix_fd (source,
|
2017-06-21 06:23:44 +00:00
|
|
|
pw_loop_get_fd (pipewire_source->pipewire_loop),
|
|
|
|
G_IO_IN | G_IO_ERR);
|
|
|
|
|
|
|
|
pw_loop_enter (pipewire_source->pipewire_loop);
|
2022-02-03 10:45:18 +00:00
|
|
|
g_source_attach (source, NULL);
|
|
|
|
g_source_unref (source);
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2022-05-07 20:48:22 +00:00
|
|
|
return source;
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
static const struct pw_core_events core_events = {
|
|
|
|
PW_VERSION_CORE_EVENTS,
|
|
|
|
.error = on_core_error,
|
2017-06-21 06:23:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
meta_screen_cast_stream_src_initable_init (GInitable *initable,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (initable);
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
2022-05-07 20:48:22 +00:00
|
|
|
struct pw_loop *pipewire_loop;
|
|
|
|
|
|
|
|
pipewire_loop = pw_loop_new (NULL);
|
|
|
|
if (!pipewire_loop)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
|
|
"Failed to create PipeWire loop");
|
|
|
|
return FALSE;
|
|
|
|
}
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2022-05-07 20:48:22 +00:00
|
|
|
priv->pipewire_source = create_pipewire_source (src, pipewire_loop);
|
2018-05-03 14:07:22 +00:00
|
|
|
if (!priv->pipewire_source)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
|
|
"Failed to create PipeWire source");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2022-05-07 20:48:22 +00:00
|
|
|
priv->pipewire_context = pw_context_new (pipewire_loop,
|
2020-01-14 08:44:45 +00:00
|
|
|
NULL, 0);
|
|
|
|
if (!priv->pipewire_context)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
2020-01-14 08:44:45 +00:00
|
|
|
"Failed to create pipewire context");
|
2017-06-21 06:23:44 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
priv->pipewire_core = pw_context_connect (priv->pipewire_context, NULL, 0);
|
|
|
|
if (!priv->pipewire_core)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
2020-01-14 08:44:45 +00:00
|
|
|
"Couldn't connect pipewire context");
|
2017-06-21 06:23:44 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
pw_core_add_listener (priv->pipewire_core,
|
|
|
|
&priv->pipewire_core_listener,
|
|
|
|
&core_events,
|
|
|
|
src);
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2020-01-14 08:44:45 +00:00
|
|
|
priv->pipewire_stream = create_pipewire_stream (src, error);
|
|
|
|
if (!priv->pipewire_stream)
|
|
|
|
return FALSE;
|
2017-06-21 06:23:44 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_init_initable_iface (GInitableIface *iface)
|
|
|
|
{
|
|
|
|
iface->init = meta_screen_cast_stream_src_initable_init;
|
|
|
|
}
|
|
|
|
|
|
|
|
MetaScreenCastStream *
|
|
|
|
meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
return priv->stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2022-02-03 10:45:18 +00:00
|
|
|
meta_screen_cast_stream_src_dispose (GObject *object)
|
2017-06-21 06:23:44 +00:00
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (object);
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
2017-06-26 04:55:48 +00:00
|
|
|
if (meta_screen_cast_stream_src_is_enabled (src))
|
|
|
|
meta_screen_cast_stream_src_disable (src);
|
|
|
|
|
2018-07-23 17:22:51 +00:00
|
|
|
g_clear_pointer (&priv->pipewire_stream, pw_stream_destroy);
|
2020-06-02 16:33:05 +00:00
|
|
|
g_clear_pointer (&priv->dmabuf_handles, g_hash_table_destroy);
|
2020-01-14 08:44:45 +00:00
|
|
|
g_clear_pointer (&priv->pipewire_core, pw_core_disconnect);
|
|
|
|
g_clear_pointer (&priv->pipewire_context, pw_context_destroy);
|
2022-05-07 20:48:22 +00:00
|
|
|
g_clear_pointer (&priv->pipewire_source, g_source_destroy);
|
2017-06-21 06:23:44 +00:00
|
|
|
|
2022-02-03 10:45:18 +00:00
|
|
|
G_OBJECT_CLASS (meta_screen_cast_stream_src_parent_class)->dispose (object);
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_set_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (object);
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_STREAM:
|
|
|
|
priv->stream = g_value_get_object (value);
|
2020-07-28 08:11:07 +00:00
|
|
|
break;
|
2017-06-21 06:23:44 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_get_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (object);
|
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_STREAM:
|
|
|
|
g_value_set_object (value, priv->stream);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_init (MetaScreenCastStreamSrc *src)
|
|
|
|
{
|
2019-12-09 13:07:29 +00:00
|
|
|
MetaScreenCastStreamSrcPrivate *priv =
|
|
|
|
meta_screen_cast_stream_src_get_instance_private (src);
|
|
|
|
|
|
|
|
priv->dmabuf_handles =
|
|
|
|
g_hash_table_new_full (NULL, NULL, NULL,
|
|
|
|
(GDestroyNotify) cogl_dma_buf_handle_free);
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_screen_cast_stream_src_class_init (MetaScreenCastStreamSrcClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
2022-02-03 10:45:18 +00:00
|
|
|
object_class->dispose = meta_screen_cast_stream_src_dispose;
|
2017-06-21 06:23:44 +00:00
|
|
|
object_class->set_property = meta_screen_cast_stream_src_set_property;
|
|
|
|
object_class->get_property = meta_screen_cast_stream_src_get_property;
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_STREAM,
|
|
|
|
g_param_spec_object ("stream",
|
|
|
|
"stream",
|
|
|
|
"MetaScreenCastStream",
|
|
|
|
META_TYPE_SCREEN_CAST_STREAM,
|
|
|
|
G_PARAM_READWRITE |
|
|
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
|
|
G_PARAM_STATIC_STRINGS));
|
2017-06-26 04:50:26 +00:00
|
|
|
|
2017-06-28 07:17:32 +00:00
|
|
|
signals[READY] = g_signal_new ("ready",
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
0,
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
G_TYPE_NONE, 1,
|
|
|
|
G_TYPE_UINT);
|
2017-06-26 04:50:26 +00:00
|
|
|
signals[CLOSED] = g_signal_new ("closed",
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
0,
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
G_TYPE_NONE, 0);
|
2017-06-21 06:23:44 +00:00
|
|
|
}
|