|
Packit Service |
85e68b |
/*
|
|
Packit Service |
85e68b |
* Copyright (C) 2018 Red Hat Inc.
|
|
Packit Service |
85e68b |
*
|
|
Packit Service |
85e68b |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
85e68b |
* modify it under the terms of the GNU General Public License as
|
|
Packit Service |
85e68b |
* published by the Free Software Foundation; either version 2 of the
|
|
Packit Service |
85e68b |
* License, or (at your option) any later version.
|
|
Packit Service |
85e68b |
*
|
|
Packit Service |
85e68b |
* This program is distributed in the hope that it will be useful, but
|
|
Packit Service |
85e68b |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
85e68b |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
85e68b |
* General Public License for more details.
|
|
Packit Service |
85e68b |
*
|
|
Packit Service |
85e68b |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
85e68b |
* along with this program; if not, write to the Free Software
|
|
Packit Service |
85e68b |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
Packit Service |
85e68b |
* 02111-1307, USA.
|
|
Packit Service |
85e68b |
*
|
|
Packit Service |
85e68b |
*/
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
#include "config.h"
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
#include "grd-vnc-pipewire-stream.h"
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
#include <linux/dma-buf.h>
|
|
Packit Service |
85e68b |
#include <pipewire/pipewire.h>
|
|
Packit Service |
85e68b |
#include <spa/param/props.h>
|
|
Packit Service |
85e68b |
#include <spa/param/format-utils.h>
|
|
Packit Service |
85e68b |
#include <spa/param/video/format-utils.h>
|
|
Packit Service |
85e68b |
#include <spa/utils/result.h>
|
|
Packit Service |
85e68b |
#include <sys/ioctl.h>
|
|
Packit Service |
85e68b |
#include <sys/mman.h>
|
|
Packit Service |
85e68b |
#include <sys/syscall.h>
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
#include "grd-vnc-cursor.h"
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
enum
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
CLOSED,
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
N_SIGNALS
|
|
Packit Service |
85e68b |
};
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
guint signals[N_SIGNALS];
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
typedef struct _GrdPipeWireSource
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GSource base;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
struct pw_loop *pipewire_loop;
|
|
Packit Service |
85e68b |
} GrdPipeWireSource;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
typedef struct _GrdVncFrame
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
void *data;
|
|
Packit Service |
85e68b |
rfbCursorPtr rfb_cursor;
|
|
Packit Service |
85e68b |
gboolean cursor_moved;
|
|
Packit Service |
85e68b |
int cursor_x;
|
|
Packit Service |
85e68b |
int cursor_y;
|
|
Packit Service |
85e68b |
} GrdVncFrame;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
struct _GrdVncPipeWireStream
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GObject parent;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
GrdSessionVnc *session;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
GSource *pipewire_source;
|
|
Packit Service |
85e68b |
struct pw_context *pipewire_context;
|
|
Packit Service |
85e68b |
struct pw_core *pipewire_core;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
struct spa_hook pipewire_core_listener;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
GMutex frame_mutex;
|
|
Packit Service |
85e68b |
GrdVncFrame *pending_frame;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
struct pw_stream *pipewire_stream;
|
|
Packit Service |
85e68b |
struct spa_hook pipewire_stream_listener;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
uint32_t src_node_id;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
struct spa_video_info_raw spa_format;
|
|
Packit Service |
85e68b |
};
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
G_DEFINE_TYPE (GrdVncPipeWireStream, grd_vnc_pipewire_stream,
|
|
Packit Service |
85e68b |
G_TYPE_OBJECT)
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
#define CURSOR_META_SIZE(width, height) \
|
|
Packit Service |
85e68b |
(sizeof(struct spa_meta_cursor) + \
|
|
Packit Service |
85e68b |
sizeof(struct spa_meta_bitmap) + width * height * 4)
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static gboolean
|
|
Packit Service |
85e68b |
pipewire_loop_source_prepare (GSource *base,
|
|
Packit Service |
85e68b |
int *timeout)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
*timeout = -1;
|
|
Packit Service |
85e68b |
return FALSE;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static gboolean
|
|
Packit Service |
85e68b |
pipewire_loop_source_dispatch (GSource *source,
|
|
Packit Service |
85e68b |
GSourceFunc callback,
|
|
Packit Service |
85e68b |
gpointer user_data)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GrdPipeWireSource *pipewire_source = (GrdPipeWireSource *) source;
|
|
Packit Service |
85e68b |
int result;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
result = pw_loop_iterate (pipewire_source->pipewire_loop, 0);
|
|
Packit Service |
85e68b |
if (result < 0)
|
|
Packit Service |
85e68b |
g_warning ("pipewire_loop_iterate failed: %s", spa_strerror (result));
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
return TRUE;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static void
|
|
Packit Service |
85e68b |
pipewire_loop_source_finalize (GSource *source)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GrdPipeWireSource *pipewire_source = (GrdPipeWireSource *) source;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pw_loop_leave (pipewire_source->pipewire_loop);
|
|
Packit Service |
85e68b |
pw_loop_destroy (pipewire_source->pipewire_loop);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static GSourceFuncs pipewire_source_funcs =
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
pipewire_loop_source_prepare,
|
|
Packit Service |
85e68b |
NULL,
|
|
Packit Service |
85e68b |
pipewire_loop_source_dispatch,
|
|
Packit Service |
85e68b |
pipewire_loop_source_finalize
|
|
Packit Service |
85e68b |
};
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static GrdPipeWireSource *
|
|
Packit Service |
85e68b |
create_pipewire_source (void)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GrdPipeWireSource *pipewire_source;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pipewire_source =
|
|
Packit Service |
85e68b |
(GrdPipeWireSource *) g_source_new (&pipewire_source_funcs,
|
|
Packit Service |
85e68b |
sizeof (GrdPipeWireSource));
|
|
Packit Service |
85e68b |
pipewire_source->pipewire_loop = pw_loop_new (NULL);
|
|
Packit Service |
85e68b |
if (!pipewire_source->pipewire_loop)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_source_destroy ((GSource *) pipewire_source);
|
|
Packit Service |
85e68b |
return NULL;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
g_source_add_unix_fd (&pipewire_source->base,
|
|
Packit Service |
85e68b |
pw_loop_get_fd (pipewire_source->pipewire_loop),
|
|
Packit Service |
85e68b |
G_IO_IN | G_IO_ERR);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pw_loop_enter (pipewire_source->pipewire_loop);
|
|
Packit Service |
85e68b |
g_source_attach (&pipewire_source->base, NULL);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
return pipewire_source;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static void
|
|
Packit Service |
85e68b |
on_stream_state_changed (void *user_data,
|
|
Packit Service |
85e68b |
enum pw_stream_state old,
|
|
Packit Service |
85e68b |
enum pw_stream_state state,
|
|
Packit Service |
85e68b |
const char *error)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_debug ("Pipewire stream state changed from %s to %s",
|
|
Packit Service |
85e68b |
pw_stream_state_as_string (old),
|
|
Packit Service |
85e68b |
pw_stream_state_as_string (state));
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
switch (state)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
case PW_STREAM_STATE_ERROR:
|
|
Packit Service |
85e68b |
g_warning ("PipeWire stream error: %s", error);
|
|
Packit Service |
85e68b |
break;
|
|
Packit Service |
85e68b |
case PW_STREAM_STATE_PAUSED:
|
|
Packit Service |
85e68b |
case PW_STREAM_STATE_STREAMING:
|
|
Packit Service |
85e68b |
case PW_STREAM_STATE_UNCONNECTED:
|
|
Packit Service |
85e68b |
case PW_STREAM_STATE_CONNECTING:
|
|
Packit Service |
85e68b |
break;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static void
|
|
Packit Service |
85e68b |
on_stream_param_changed (void *user_data,
|
|
Packit Service |
85e68b |
uint32_t id,
|
|
Packit Service |
85e68b |
const struct spa_pod *format)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data);
|
|
Packit Service |
85e68b |
uint8_t params_buffer[1024];
|
|
Packit Service |
85e68b |
struct spa_pod_builder pod_builder;
|
|
Packit Service |
85e68b |
int width;
|
|
Packit Service |
85e68b |
int height;
|
|
Packit Service |
85e68b |
const struct spa_pod *params[3];
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (!format || id != SPA_PARAM_Format)
|
|
Packit Service |
85e68b |
return;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
spa_format_video_raw_parse (format, &stream->spa_format);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer));
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
width = stream->spa_format.size.width;
|
|
Packit Service |
85e68b |
height = stream->spa_format.size.height;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
grd_session_vnc_queue_resize_framebuffer (stream->session, width, height);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
params[0] = spa_pod_builder_add_object (
|
|
Packit Service |
85e68b |
&pod_builder,
|
|
Packit Service |
85e68b |
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
|
|
Packit Service |
85e68b |
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (8, 1, 8),
|
|
Packit Service |
85e68b |
0);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
params[1] = spa_pod_builder_add_object (
|
|
Packit Service |
85e68b |
&pod_builder,
|
|
Packit Service |
85e68b |
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
|
Packit Service |
85e68b |
SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Header),
|
|
Packit Service |
85e68b |
SPA_PARAM_META_size, SPA_POD_Int (sizeof (struct spa_meta_header)),
|
|
Packit Service |
85e68b |
0);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
params[2] = spa_pod_builder_add_object(
|
|
Packit Service |
85e68b |
&pod_builder,
|
|
Packit Service |
85e68b |
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
|
Packit Service |
85e68b |
SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor),
|
|
Packit Service |
85e68b |
SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int (CURSOR_META_SIZE (64,64),
|
|
Packit Service |
85e68b |
CURSOR_META_SIZE (1,1),
|
|
Packit Service |
85e68b |
CURSOR_META_SIZE (256,256)),
|
|
Packit Service |
85e68b |
0);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pw_stream_update_params (stream->pipewire_stream,
|
|
Packit Service |
85e68b |
params, G_N_ELEMENTS (params));
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static gboolean
|
|
Packit Service |
85e68b |
spa_pixel_format_to_grd_pixel_format (uint32_t spa_format,
|
|
Packit Service |
85e68b |
GrdPixelFormat *out_format)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
if (spa_format == SPA_VIDEO_FORMAT_RGBA)
|
|
Packit Service |
85e68b |
*out_format = GRD_PIXEL_FORMAT_RGBA8888;
|
|
Packit Service |
85e68b |
else
|
|
Packit Service |
85e68b |
return FALSE;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
return TRUE;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static void
|
|
Packit Service |
85e68b |
sync_dma_buf (int fd,
|
|
Packit Service |
85e68b |
uint64_t start_or_end)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
struct dma_buf_sync sync = { 0 };
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
sync.flags = start_or_end | DMA_BUF_SYNC_READ;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
while (TRUE)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
int ret;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
ret = ioctl (fd, DMA_BUF_IOCTL_SYNC, &sync);
|
|
Packit Service |
85e68b |
if (ret == -1 && errno == EINTR)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
continue;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
else if (ret == -1)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_warning ("Failed to synchronize DMA buffer: %s",
|
|
Packit Service |
85e68b |
g_strerror (errno));
|
|
Packit Service |
85e68b |
break;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
else
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
break;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static int
|
|
Packit Service |
85e68b |
do_render (struct spa_loop *loop,
|
|
Packit Service |
85e68b |
bool async,
|
|
Packit Service |
85e68b |
uint32_t seq,
|
|
Packit Service |
85e68b |
const void *data,
|
|
Packit Service |
85e68b |
size_t size,
|
|
Packit Service |
85e68b |
void *user_data)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data);
|
|
Packit Service |
85e68b |
GrdVncFrame *frame;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
g_mutex_lock (&stream->frame_mutex);
|
|
Packit Service |
85e68b |
frame = g_steal_pointer (&stream->pending_frame);
|
|
Packit Service |
85e68b |
g_mutex_unlock (&stream->frame_mutex);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (!frame)
|
|
Packit Service |
85e68b |
return 0;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (frame->rfb_cursor)
|
|
Packit Service |
85e68b |
grd_session_vnc_set_cursor (stream->session, frame->rfb_cursor);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (frame->cursor_moved)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
grd_session_vnc_move_cursor (stream->session,
|
|
Packit Service |
85e68b |
frame->cursor_x,
|
|
Packit Service |
85e68b |
frame->cursor_y);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (frame->data)
|
|
Packit Service |
85e68b |
grd_session_vnc_take_buffer (stream->session, frame->data);
|
|
rpm-build |
47d5e1 |
else
|
|
rpm-build |
47d5e1 |
grd_session_vnc_flush (stream->session);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
g_free (frame);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
return 0;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static GrdVncFrame *
|
|
Packit Service |
85e68b |
process_buffer (GrdVncPipeWireStream *stream,
|
|
Packit Service |
85e68b |
struct spa_buffer *buffer)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
size_t size;
|
|
Packit Service |
85e68b |
uint8_t *map;
|
|
Packit Service |
85e68b |
void *src_data;
|
|
Packit Service |
85e68b |
struct spa_meta_cursor *spa_meta_cursor;
|
|
Packit Service |
85e68b |
g_autofree GrdVncFrame *frame = NULL;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
frame = g_new0 (GrdVncFrame, 1);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (buffer->datas[0].chunk->size == 0)
|
|
Packit Service |
85e68b |
{
|
|
rpm-build |
47d5e1 |
map = NULL;
|
|
rpm-build |
47d5e1 |
src_data = NULL;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
else if (buffer->datas[0].type == SPA_DATA_MemFd)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset;
|
|
Packit Service |
85e68b |
map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, buffer->datas[0].fd, 0);
|
|
rpm-build |
60d84b |
if (map == MAP_FAILED)
|
|
rpm-build |
60d84b |
{
|
|
rpm-build |
60d84b |
g_warning ("Failed to mmap buffer: %s", g_strerror (errno));
|
|
rpm-build |
60d84b |
return NULL;
|
|
rpm-build |
60d84b |
}
|
|
Packit Service |
85e68b |
src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
else if (buffer->datas[0].type == SPA_DATA_DmaBuf)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
int fd;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
fd = buffer->datas[0].fd;
|
|
Packit Service |
85e68b |
size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
rpm-build |
60d84b |
if (map == MAP_FAILED)
|
|
rpm-build |
60d84b |
{
|
|
rpm-build |
60d84b |
g_warning ("Failed to mmap DMA buffer: %s", g_strerror (errno));
|
|
rpm-build |
60d84b |
return NULL;
|
|
rpm-build |
60d84b |
}
|
|
Packit Service |
85e68b |
sync_dma_buf (fd, DMA_BUF_SYNC_START);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
else if (buffer->datas[0].type == SPA_DATA_MemPtr)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset;
|
|
Packit Service |
85e68b |
map = NULL;
|
|
Packit Service |
85e68b |
src_data = buffer->datas[0].data;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
else
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
return NULL;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
rpm-build |
4acd8c |
if (src_data)
|
|
rpm-build |
ef508a |
{
|
|
rpm-build |
4acd8c |
int src_stride;
|
|
rpm-build |
4acd8c |
int dst_stride;
|
|
rpm-build |
4acd8c |
int height;
|
|
rpm-build |
47d5e1 |
int y;
|
|
rpm-build |
4acd8c |
|
|
rpm-build |
4acd8c |
src_stride = buffer->datas[0].chunk->stride;
|
|
rpm-build |
4acd8c |
dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session);
|
|
rpm-build |
4acd8c |
height = stream->spa_format.size.height;
|
|
rpm-build |
4acd8c |
|
|
rpm-build |
4acd8c |
frame->data = g_malloc (height * dst_stride);
|
|
rpm-build |
4acd8c |
for (y = 0; y < height; y++)
|
|
rpm-build |
4acd8c |
{
|
|
rpm-build |
4acd8c |
memcpy (((uint8_t *) frame->data) + y * dst_stride,
|
|
rpm-build |
4acd8c |
((uint8_t *) src_data) + y * src_stride,
|
|
rpm-build |
4acd8c |
dst_stride);
|
|
rpm-build |
4acd8c |
}
|
|
rpm-build |
ef508a |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (map)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
if (buffer->datas[0].type == SPA_DATA_DmaBuf)
|
|
Packit Service |
85e68b |
sync_dma_buf (buffer->datas[0].fd, DMA_BUF_SYNC_END);
|
|
Packit Service |
85e68b |
munmap (map, size);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
spa_meta_cursor = spa_buffer_find_meta_data (buffer, SPA_META_Cursor,
|
|
Packit Service |
85e68b |
sizeof *spa_meta_cursor);
|
|
Packit Service |
85e68b |
if (spa_meta_cursor && spa_meta_cursor_is_valid (spa_meta_cursor))
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
struct spa_meta_bitmap *spa_meta_bitmap;
|
|
Packit Service |
85e68b |
GrdPixelFormat format;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (spa_meta_cursor->bitmap_offset)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
|
|
Packit Service |
85e68b |
spa_meta_cursor->bitmap_offset,
|
|
Packit Service |
85e68b |
struct spa_meta_bitmap);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
else
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
spa_meta_bitmap = NULL;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (spa_meta_bitmap &&
|
|
Packit Service |
85e68b |
spa_meta_bitmap->size.width > 0 &&
|
|
Packit Service |
85e68b |
spa_meta_bitmap->size.height > 0 &&
|
|
Packit Service |
85e68b |
spa_pixel_format_to_grd_pixel_format (spa_meta_bitmap->format,
|
|
Packit Service |
85e68b |
&format))
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
uint8_t *buf;
|
|
Packit Service |
85e68b |
rfbCursorPtr rfb_cursor;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
buf = SPA_MEMBER (spa_meta_bitmap, spa_meta_bitmap->offset, uint8_t);
|
|
Packit Service |
85e68b |
rfb_cursor = grd_vnc_create_cursor (spa_meta_bitmap->size.width,
|
|
Packit Service |
85e68b |
spa_meta_bitmap->size.height,
|
|
Packit Service |
85e68b |
spa_meta_bitmap->stride,
|
|
Packit Service |
85e68b |
format,
|
|
Packit Service |
85e68b |
buf);
|
|
Packit Service |
85e68b |
rfb_cursor->xhot = spa_meta_cursor->hotspot.x;
|
|
Packit Service |
85e68b |
rfb_cursor->yhot = spa_meta_cursor->hotspot.y;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
frame->rfb_cursor = rfb_cursor;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
else if (spa_meta_bitmap)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
frame->rfb_cursor = grd_vnc_create_empty_cursor (1, 1);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
frame->cursor_moved = TRUE;
|
|
Packit Service |
85e68b |
frame->cursor_x = spa_meta_cursor->position.x;
|
|
Packit Service |
85e68b |
frame->cursor_y = spa_meta_cursor->position.y;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
return g_steal_pointer (&frame);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static void
|
|
Packit Service |
85e68b |
on_stream_process (void *user_data)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data);
|
|
Packit Service |
85e68b |
GrdPipeWireSource *pipewire_source =
|
|
Packit Service |
85e68b |
(GrdPipeWireSource *) stream->pipewire_source;
|
|
Packit Service |
85e68b |
struct pw_buffer *next_buffer;
|
|
Packit Service |
85e68b |
struct pw_buffer *buffer = NULL;
|
|
Packit Service |
85e68b |
GrdVncFrame *frame;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
next_buffer = pw_stream_dequeue_buffer (stream->pipewire_stream);
|
|
Packit Service |
85e68b |
while (next_buffer)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
buffer = next_buffer;
|
|
Packit Service |
85e68b |
next_buffer = pw_stream_dequeue_buffer (stream->pipewire_stream);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (next_buffer)
|
|
Packit Service |
85e68b |
pw_stream_queue_buffer (stream->pipewire_stream, buffer);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
if (!buffer)
|
|
Packit Service |
85e68b |
return;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
frame = process_buffer (stream, buffer->buffer);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
g_mutex_lock (&stream->frame_mutex);
|
|
Packit Service |
85e68b |
if (stream->pending_frame)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_free (stream->pending_frame->data);
|
|
Packit Service |
85e68b |
g_clear_pointer (&stream->pending_frame, g_free);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
stream->pending_frame = frame;
|
|
Packit Service |
85e68b |
g_mutex_unlock (&stream->frame_mutex);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pw_stream_queue_buffer (stream->pipewire_stream, buffer);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pw_loop_invoke (pipewire_source->pipewire_loop, do_render,
|
|
Packit Service |
85e68b |
SPA_ID_INVALID, NULL, 0,
|
|
Packit Service |
85e68b |
false, stream);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static const struct pw_stream_events stream_events = {
|
|
Packit Service |
85e68b |
PW_VERSION_STREAM_EVENTS,
|
|
Packit Service |
85e68b |
.state_changed = on_stream_state_changed,
|
|
Packit Service |
85e68b |
.param_changed = on_stream_param_changed,
|
|
Packit Service |
85e68b |
.process = on_stream_process,
|
|
Packit Service |
85e68b |
};
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static gboolean
|
|
Packit Service |
85e68b |
connect_to_stream (GrdVncPipeWireStream *stream,
|
|
Packit Service |
85e68b |
GError **error)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
struct pw_stream *pipewire_stream;
|
|
Packit Service |
85e68b |
uint8_t params_buffer[1024];
|
|
Packit Service |
85e68b |
struct spa_pod_builder pod_builder;
|
|
Packit Service |
85e68b |
struct spa_rectangle min_rect;
|
|
Packit Service |
85e68b |
struct spa_rectangle max_rect;
|
|
Packit Service |
85e68b |
struct spa_fraction min_framerate;
|
|
Packit Service |
85e68b |
struct spa_fraction max_framerate;
|
|
Packit Service |
85e68b |
const struct spa_pod *params[2];
|
|
Packit Service |
85e68b |
int ret;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pipewire_stream = pw_stream_new (stream->pipewire_core,
|
|
Packit Service |
85e68b |
"grd-vnc-pipewire-stream",
|
|
Packit Service |
85e68b |
NULL);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
min_rect = SPA_RECTANGLE (1, 1);
|
|
Packit Service |
85e68b |
max_rect = SPA_RECTANGLE (INT32_MAX, INT32_MAX);
|
|
Packit Service |
85e68b |
min_framerate = SPA_FRACTION (1, 1);
|
|
Packit Service |
85e68b |
max_framerate = SPA_FRACTION (30, 1);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer));
|
|
Packit Service |
85e68b |
params[0] = spa_pod_builder_add_object (
|
|
Packit Service |
85e68b |
&pod_builder,
|
|
Packit Service |
85e68b |
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
|
Packit Service |
85e68b |
SPA_FORMAT_mediaType, SPA_POD_Id (SPA_MEDIA_TYPE_video),
|
|
Packit Service |
85e68b |
SPA_FORMAT_mediaSubtype, SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw),
|
|
Packit Service |
85e68b |
SPA_FORMAT_VIDEO_format, SPA_POD_Id (SPA_VIDEO_FORMAT_BGRx),
|
|
Packit Service |
85e68b |
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle (&min_rect,
|
|
Packit Service |
85e68b |
&min_rect,
|
|
Packit Service |
85e68b |
&max_rect),
|
|
Packit Service |
85e68b |
SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION(0, 1)),
|
|
Packit Service |
85e68b |
SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction (&min_framerate,
|
|
Packit Service |
85e68b |
&min_framerate,
|
|
Packit Service |
85e68b |
&max_framerate),
|
|
Packit Service |
85e68b |
0);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
stream->pipewire_stream = pipewire_stream;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pw_stream_add_listener (pipewire_stream,
|
|
Packit Service |
85e68b |
&stream->pipewire_stream_listener,
|
|
Packit Service |
85e68b |
&stream_events,
|
|
Packit Service |
85e68b |
stream);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
ret = pw_stream_connect (stream->pipewire_stream,
|
|
Packit Service |
85e68b |
PW_DIRECTION_INPUT,
|
|
Packit Service |
85e68b |
stream->src_node_id,
|
|
Packit Service |
85e68b |
(PW_STREAM_FLAG_RT_PROCESS |
|
|
Packit Service |
85e68b |
PW_STREAM_FLAG_AUTOCONNECT),
|
|
Packit Service |
85e68b |
params, 1);
|
|
Packit Service |
85e68b |
if (ret < 0)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
|
Packit Service |
85e68b |
strerror (-ret));
|
|
Packit Service |
85e68b |
return FALSE;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
return TRUE;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static void
|
|
Packit Service |
85e68b |
on_core_error (void *user_data,
|
|
Packit Service |
85e68b |
uint32_t id,
|
|
Packit Service |
85e68b |
int seq,
|
|
Packit Service |
85e68b |
int res,
|
|
Packit Service |
85e68b |
const char *message)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
g_warning ("Pipewire core error: id:%u %s", id, message);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (id == PW_ID_CORE && res == -EPIPE)
|
|
Packit Service |
85e68b |
g_signal_emit (stream, signals[CLOSED], 0);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static const struct pw_core_events core_events = {
|
|
Packit Service |
85e68b |
PW_VERSION_CORE_EVENTS,
|
|
Packit Service |
85e68b |
.error = on_core_error,
|
|
Packit Service |
85e68b |
};
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
GrdVncPipeWireStream *
|
|
Packit Service |
85e68b |
grd_vnc_pipewire_stream_new (GrdSessionVnc *session_vnc,
|
|
Packit Service |
85e68b |
uint32_t src_node_id,
|
|
Packit Service |
85e68b |
GError **error)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_autoptr (GrdVncPipeWireStream) stream = NULL;
|
|
Packit Service |
85e68b |
GrdPipeWireSource *pipewire_source;
|
|
Packit Service |
85e68b |
static gboolean is_pipewire_initialized = FALSE;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (!is_pipewire_initialized)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
pw_init (NULL, NULL);
|
|
Packit Service |
85e68b |
is_pipewire_initialized = TRUE;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
stream = g_object_new (GRD_TYPE_VNC_PIPEWIRE_STREAM, NULL);
|
|
Packit Service |
85e68b |
stream->session = session_vnc;
|
|
Packit Service |
85e68b |
stream->src_node_id = src_node_id;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pipewire_source = create_pipewire_source ();
|
|
Packit Service |
85e68b |
if (!pipewire_source)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
Packit Service |
85e68b |
"Failed to create PipeWire source");
|
|
Packit Service |
85e68b |
return NULL;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
stream->pipewire_source = (GSource *) pipewire_source;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
stream->pipewire_context = pw_context_new (pipewire_source->pipewire_loop,
|
|
Packit Service |
85e68b |
NULL, 0);
|
|
Packit Service |
85e68b |
if (!stream->pipewire_context)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
Packit Service |
85e68b |
"Failed to create pipewire context");
|
|
Packit Service |
85e68b |
return NULL;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
stream->pipewire_core = pw_context_connect (stream->pipewire_context, NULL, 0);
|
|
Packit Service |
85e68b |
if (!stream->pipewire_core)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
Packit Service |
85e68b |
"Failed to connect pipewire context");
|
|
Packit Service |
85e68b |
return NULL;
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
pw_core_add_listener (stream->pipewire_core,
|
|
Packit Service |
85e68b |
&stream->pipewire_core_listener,
|
|
Packit Service |
85e68b |
&core_events,
|
|
Packit Service |
85e68b |
stream);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
if (!connect_to_stream (stream, error))
|
|
Packit Service |
85e68b |
return NULL;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
return g_steal_pointer (&stream);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static void
|
|
Packit Service |
85e68b |
grd_vnc_pipewire_stream_finalize (GObject *object)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (object);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
/*
|
|
Packit Service |
85e68b |
* We can't clear stream->pipewire_stream before destroying it, as the data
|
|
Packit Service |
85e68b |
* thread in PipeWire might access the variable during destruction.
|
|
Packit Service |
85e68b |
*/
|
|
Packit Service |
85e68b |
if (stream->pipewire_stream)
|
|
Packit Service |
85e68b |
pw_stream_destroy (stream->pipewire_stream);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
g_clear_pointer (&stream->pipewire_core, pw_core_disconnect);
|
|
Packit Service |
85e68b |
g_clear_pointer (&stream->pipewire_context, pw_context_destroy);
|
|
Packit Service |
85e68b |
g_clear_pointer (&stream->pipewire_source, g_source_destroy);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
G_OBJECT_CLASS (grd_vnc_pipewire_stream_parent_class)->finalize (object);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static void
|
|
Packit Service |
85e68b |
grd_vnc_pipewire_stream_init (GrdVncPipeWireStream *stream)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
g_mutex_init (&stream->frame_mutex);
|
|
Packit Service |
85e68b |
}
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
static void
|
|
Packit Service |
85e68b |
grd_vnc_pipewire_stream_class_init (GrdVncPipeWireStreamClass *klass)
|
|
Packit Service |
85e68b |
{
|
|
Packit Service |
85e68b |
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
object_class->finalize = grd_vnc_pipewire_stream_finalize;
|
|
Packit Service |
85e68b |
|
|
Packit Service |
85e68b |
signals[CLOSED] = g_signal_new ("closed",
|
|
Packit Service |
85e68b |
G_TYPE_FROM_CLASS (klass),
|
|
Packit Service |
85e68b |
G_SIGNAL_RUN_LAST,
|
|
Packit Service |
85e68b |
0,
|
|
Packit Service |
85e68b |
NULL, NULL, NULL,
|
|
Packit Service |
85e68b |
G_TYPE_NONE, 0);
|
|
Packit Service |
85e68b |
}
|