mirror of
https://github.com/morgan9e/grd
synced 2026-04-13 16:04:13 +09:00
407 lines
13 KiB
C
407 lines
13 KiB
C
/*
|
|
* Copyright (C) 2024 Pascal Nowack
|
|
*
|
|
* 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 "grd-encode-session-ca-sw.h"
|
|
|
|
#include <gio/gio.h>
|
|
|
|
#include "grd-bitstream.h"
|
|
#include "grd-encode-context.h"
|
|
#include "grd-image-view-rgb.h"
|
|
#include "grd-local-buffer.h"
|
|
#include "grd-rdp-sw-encoder-ca.h"
|
|
|
|
/*
|
|
* One surface is needed, when encoding a frame,
|
|
* one is needed, when preparing the view,
|
|
* one is needed for the submitted pending frame in the renderer,
|
|
* and one is needed to prepare a new pending frame before it is submitted to
|
|
* the renderer, where it then replaces the old pending frame
|
|
*
|
|
* In total, this makes four needed source surfaces
|
|
*/
|
|
#define N_SRC_SURFACES 4
|
|
|
|
/*
|
|
* One encode stream is needed, when submitting a frame,
|
|
* one is needed, when encoding a frame
|
|
*
|
|
* In total, this makes two needed encode streams
|
|
*/
|
|
#define N_ENCODE_STREAMS 2
|
|
|
|
#define INITIAL_STREAM_SIZE 16384
|
|
|
|
typedef struct
|
|
{
|
|
GrdImageView *image_view;
|
|
} RGBSurface;
|
|
|
|
struct _GrdEncodeSessionCaSw
|
|
{
|
|
GrdEncodeSession parent;
|
|
|
|
GrdRdpSwEncoderCa *encoder_ca;
|
|
|
|
uint32_t surface_width;
|
|
uint32_t surface_height;
|
|
|
|
GHashTable *surfaces;
|
|
|
|
GMutex pending_encodes_mutex;
|
|
GHashTable *pending_encodes;
|
|
|
|
GMutex acquired_encode_streams_mutex;
|
|
wStream *encode_streams[N_ENCODE_STREAMS];
|
|
GHashTable *acquired_encode_streams;
|
|
|
|
GMutex bitstreams_mutex;
|
|
GHashTable *bitstreams;
|
|
|
|
gboolean pending_header;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdEncodeSessionCaSw, grd_encode_session_ca_sw,
|
|
GRD_TYPE_ENCODE_SESSION)
|
|
|
|
static RGBSurface *
|
|
rgb_surface_new (void)
|
|
{
|
|
RGBSurface *rgb_surface = NULL;
|
|
GrdImageViewRGB *image_view_rgb;
|
|
|
|
rgb_surface = g_new0 (RGBSurface, 1);
|
|
|
|
image_view_rgb = grd_image_view_rgb_new ();
|
|
rgb_surface->image_view = GRD_IMAGE_VIEW (image_view_rgb);
|
|
|
|
return rgb_surface;
|
|
}
|
|
|
|
static void
|
|
rgb_surface_free (RGBSurface *rgb_surface)
|
|
{
|
|
g_clear_object (&rgb_surface->image_view);
|
|
|
|
g_free (rgb_surface);
|
|
}
|
|
|
|
static void
|
|
grd_encode_session_ca_sw_get_surface_size (GrdEncodeSession *encode_session,
|
|
uint32_t *surface_width,
|
|
uint32_t *surface_height)
|
|
{
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static GList *
|
|
grd_encode_session_ca_sw_get_image_views (GrdEncodeSession *encode_session)
|
|
{
|
|
GrdEncodeSessionCaSw *encode_session_ca =
|
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
|
|
|
return g_hash_table_get_keys (encode_session_ca->surfaces);
|
|
}
|
|
|
|
static gboolean
|
|
grd_encode_session_ca_sw_has_pending_frames (GrdEncodeSession *encode_session)
|
|
{
|
|
GrdEncodeSessionCaSw *encode_session_ca =
|
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
|
|
|
g_mutex_lock (&encode_session_ca->pending_encodes_mutex);
|
|
g_assert (g_hash_table_size (encode_session_ca->pending_encodes) == 0);
|
|
g_mutex_unlock (&encode_session_ca->pending_encodes_mutex);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
grd_encode_session_ca_sw_encode_frame (GrdEncodeSession *encode_session,
|
|
GrdEncodeContext *encode_context,
|
|
GrdImageView *image_view,
|
|
GError **error)
|
|
{
|
|
GrdEncodeSessionCaSw *encode_session_ca =
|
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&encode_session_ca->pending_encodes_mutex);
|
|
g_assert (!g_hash_table_contains (encode_session_ca->pending_encodes,
|
|
image_view));
|
|
|
|
g_hash_table_insert (encode_session_ca->pending_encodes,
|
|
image_view, encode_context);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static wStream *
|
|
acquire_encode_stream (GrdEncodeSessionCaSw *encode_session_ca)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
uint32_t i;
|
|
|
|
locker = g_mutex_locker_new (&encode_session_ca->acquired_encode_streams_mutex);
|
|
for (i = 0; i < N_ENCODE_STREAMS; ++i)
|
|
{
|
|
wStream *encode_stream = encode_session_ca->encode_streams[i];
|
|
|
|
if (g_hash_table_contains (encode_session_ca->acquired_encode_streams,
|
|
encode_stream))
|
|
continue;
|
|
|
|
g_hash_table_add (encode_session_ca->acquired_encode_streams,
|
|
encode_stream);
|
|
return encode_stream;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
release_encode_stream (GrdEncodeSessionCaSw *encode_session_ca,
|
|
wStream *encode_stream)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&encode_session_ca->acquired_encode_streams_mutex);
|
|
if (!g_hash_table_remove (encode_session_ca->acquired_encode_streams,
|
|
encode_stream))
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static GrdBitstream *
|
|
grd_encode_session_ca_sw_lock_bitstream (GrdEncodeSession *encode_session,
|
|
GrdImageView *image_view,
|
|
GError **error)
|
|
{
|
|
GrdEncodeSessionCaSw *encode_session_ca =
|
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
|
GrdImageViewRGB *image_view_rgb = GRD_IMAGE_VIEW_RGB (image_view);
|
|
GrdEncodeContext *encode_context = NULL;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
GrdLocalBuffer *local_buffer;
|
|
uint8_t *buffer;
|
|
uint32_t buffer_stride;
|
|
cairo_region_t *damage_region;
|
|
wStream *encode_stream;
|
|
GrdBitstream *bitstream;
|
|
|
|
locker = g_mutex_locker_new (&encode_session_ca->pending_encodes_mutex);
|
|
if (!g_hash_table_lookup_extended (encode_session_ca->pending_encodes,
|
|
image_view,
|
|
NULL, (gpointer *) &encode_context))
|
|
g_assert_not_reached ();
|
|
|
|
g_clear_pointer (&locker, g_mutex_locker_free);
|
|
g_assert (encode_context);
|
|
|
|
local_buffer = grd_image_view_rgb_get_local_buffer (image_view_rgb);
|
|
buffer = grd_local_buffer_get_buffer (local_buffer);
|
|
buffer_stride = grd_local_buffer_get_buffer_stride (local_buffer);
|
|
damage_region = grd_encode_context_get_damage_region (encode_context);
|
|
encode_stream = acquire_encode_stream (encode_session_ca);
|
|
|
|
grd_rdp_sw_encoder_ca_encode_progressive_frame (encode_session_ca->encoder_ca,
|
|
encode_session_ca->surface_width,
|
|
encode_session_ca->surface_height,
|
|
buffer,
|
|
buffer_stride,
|
|
damage_region,
|
|
encode_stream,
|
|
encode_session_ca->pending_header);
|
|
|
|
encode_session_ca->pending_header = FALSE;
|
|
|
|
bitstream = grd_bitstream_new (Stream_Buffer (encode_stream),
|
|
Stream_Length (encode_stream));
|
|
|
|
g_mutex_lock (&encode_session_ca->bitstreams_mutex);
|
|
g_hash_table_insert (encode_session_ca->bitstreams, bitstream, encode_stream);
|
|
g_mutex_unlock (&encode_session_ca->bitstreams_mutex);
|
|
|
|
locker = g_mutex_locker_new (&encode_session_ca->pending_encodes_mutex);
|
|
if (!g_hash_table_remove (encode_session_ca->pending_encodes, image_view))
|
|
g_assert_not_reached ();
|
|
|
|
return bitstream;
|
|
}
|
|
|
|
static gboolean
|
|
grd_encode_session_ca_sw_unlock_bitstream (GrdEncodeSession *encode_session,
|
|
GrdBitstream *bitstream,
|
|
GError **error)
|
|
{
|
|
GrdEncodeSessionCaSw *encode_session_ca =
|
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
|
wStream *encode_stream = NULL;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&encode_session_ca->bitstreams_mutex);
|
|
if (!g_hash_table_steal_extended (encode_session_ca->bitstreams,
|
|
bitstream,
|
|
NULL, (gpointer *) &encode_stream))
|
|
g_assert_not_reached ();
|
|
|
|
g_clear_pointer (&locker, g_mutex_locker_free);
|
|
g_assert (encode_stream);
|
|
|
|
grd_bitstream_free (bitstream);
|
|
release_encode_stream (encode_session_ca, encode_stream);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
init_surfaces_and_streams (GrdEncodeSessionCaSw *encode_session_ca,
|
|
GError **error)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < N_SRC_SURFACES; ++i)
|
|
{
|
|
RGBSurface *surface;
|
|
|
|
surface = rgb_surface_new ();
|
|
|
|
g_hash_table_insert (encode_session_ca->surfaces,
|
|
surface->image_view, surface);
|
|
}
|
|
|
|
for (i = 0; i < N_ENCODE_STREAMS; ++i)
|
|
{
|
|
wStream *encode_stream;
|
|
|
|
encode_stream = Stream_New (NULL, INITIAL_STREAM_SIZE);
|
|
if (!encode_stream)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create encode stream");
|
|
return FALSE;
|
|
}
|
|
encode_session_ca->encode_streams[i] = encode_stream;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GrdEncodeSessionCaSw *
|
|
grd_encode_session_ca_sw_new (GrdRdpSwEncoderCa *encoder_ca,
|
|
uint32_t source_width,
|
|
uint32_t source_height,
|
|
GError **error)
|
|
{
|
|
g_autoptr (GrdEncodeSessionCaSw) encode_session_ca = NULL;
|
|
|
|
encode_session_ca = g_object_new (GRD_TYPE_ENCODE_SESSION_CA_SW, NULL);
|
|
encode_session_ca->encoder_ca = encoder_ca;
|
|
encode_session_ca->surface_width = source_width;
|
|
encode_session_ca->surface_height = source_height;
|
|
|
|
if (!init_surfaces_and_streams (encode_session_ca, error))
|
|
return NULL;
|
|
|
|
return g_steal_pointer (&encode_session_ca);
|
|
}
|
|
|
|
static void
|
|
encode_stream_free (wStream *encode_stream)
|
|
{
|
|
Stream_Free (encode_stream, TRUE);
|
|
}
|
|
|
|
static void
|
|
grd_encode_session_ca_sw_dispose (GObject *object)
|
|
{
|
|
GrdEncodeSessionCaSw *encode_session_ca = GRD_ENCODE_SESSION_CA_SW (object);
|
|
uint32_t i;
|
|
|
|
g_assert (g_hash_table_size (encode_session_ca->pending_encodes) == 0);
|
|
g_assert (g_hash_table_size (encode_session_ca->acquired_encode_streams) == 0);
|
|
g_assert (g_hash_table_size (encode_session_ca->bitstreams) == 0);
|
|
|
|
for (i = 0; i < N_ENCODE_STREAMS; ++i)
|
|
g_clear_pointer (&encode_session_ca->encode_streams[i], encode_stream_free);
|
|
|
|
g_clear_pointer (&encode_session_ca->surfaces, g_hash_table_unref);
|
|
|
|
G_OBJECT_CLASS (grd_encode_session_ca_sw_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
grd_encode_session_ca_sw_finalize (GObject *object)
|
|
{
|
|
GrdEncodeSessionCaSw *encode_session_ca = GRD_ENCODE_SESSION_CA_SW (object);
|
|
|
|
g_mutex_clear (&encode_session_ca->bitstreams_mutex);
|
|
g_mutex_clear (&encode_session_ca->acquired_encode_streams_mutex);
|
|
g_mutex_clear (&encode_session_ca->pending_encodes_mutex);
|
|
|
|
g_clear_pointer (&encode_session_ca->bitstreams, g_hash_table_unref);
|
|
g_clear_pointer (&encode_session_ca->acquired_encode_streams, g_hash_table_unref);
|
|
g_clear_pointer (&encode_session_ca->pending_encodes, g_hash_table_unref);
|
|
|
|
G_OBJECT_CLASS (grd_encode_session_ca_sw_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
grd_encode_session_ca_sw_init (GrdEncodeSessionCaSw *encode_session_ca)
|
|
{
|
|
encode_session_ca->pending_header = TRUE;
|
|
|
|
encode_session_ca->surfaces =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL, (GDestroyNotify) rgb_surface_free);
|
|
encode_session_ca->pending_encodes = g_hash_table_new (NULL, NULL);
|
|
encode_session_ca->acquired_encode_streams = g_hash_table_new (NULL, NULL);
|
|
encode_session_ca->bitstreams = g_hash_table_new (NULL, NULL);
|
|
|
|
g_mutex_init (&encode_session_ca->pending_encodes_mutex);
|
|
g_mutex_init (&encode_session_ca->acquired_encode_streams_mutex);
|
|
g_mutex_init (&encode_session_ca->bitstreams_mutex);
|
|
}
|
|
|
|
static void
|
|
grd_encode_session_ca_sw_class_init (GrdEncodeSessionCaSwClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GrdEncodeSessionClass *encode_session_class =
|
|
GRD_ENCODE_SESSION_CLASS (klass);
|
|
|
|
object_class->dispose = grd_encode_session_ca_sw_dispose;
|
|
object_class->finalize = grd_encode_session_ca_sw_finalize;
|
|
|
|
encode_session_class->get_surface_size =
|
|
grd_encode_session_ca_sw_get_surface_size;
|
|
encode_session_class->get_image_views =
|
|
grd_encode_session_ca_sw_get_image_views;
|
|
encode_session_class->has_pending_frames =
|
|
grd_encode_session_ca_sw_has_pending_frames;
|
|
encode_session_class->encode_frame =
|
|
grd_encode_session_ca_sw_encode_frame;
|
|
encode_session_class->lock_bitstream =
|
|
grd_encode_session_ca_sw_lock_bitstream;
|
|
encode_session_class->unlock_bitstream =
|
|
grd_encode_session_ca_sw_unlock_bitstream;
|
|
}
|