[gnome-remote-desktop] rdp: Add RDP DSP class
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-remote-desktop] rdp: Add RDP DSP class
- Date: Mon, 1 Aug 2022 17:34:42 +0000 (UTC)
commit c2461dfc2ff4a4b8b606033ccd6823b172df9ef0
Author: Pascal Nowack <Pascal Nowack gmx de>
Date: Mon May 23 06:17:46 2022 +0200
rdp: Add RDP DSP class
Add a class for processing audio data. The main purpose of this class
will be to encode raw audio data for audio output redirection.
Currently, only the AAC codec is supported, but in the future support
for other codecs might be added too.
.gitlab-ci.yml | 3 +-
config.h.meson | 3 +
meson.build | 14 +++
meson_options.txt | 5 +
src/grd-rdp-dsp.c | 293 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/grd-rdp-dsp.h | 49 +++++++++
src/meson.build | 8 ++
7 files changed, 374 insertions(+), 1 deletion(-)
---
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1b480ece..7fa29cc9 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,7 +9,7 @@ stages:
.gnome-remote-desktop.fedora:35@common:
variables:
FDO_DISTRIBUTION_VERSION: 36
- BASE_TAG: '2022-07-28.2'
+ BASE_TAG: '2022-07-28.3'
FDO_UPSTREAM_REPO: GNOME/gnome-remote-desktop
FDO_DISTRIBUTION_EXEC: |
dnf -y update && dnf -y upgrade &&
@@ -23,6 +23,7 @@ stages:
'pkgconfig(gudev-1.0)' &&
dnf install -y 'pkgconfig(ffnvcodec)' &&
dnf install -y 'pkgconfig(fuse3)' &&
+ dnf install -y 'pkgconfig(fdk-aac)' &&
dnf install -y asciidoc &&
# To test
diff --git a/config.h.meson b/config.h.meson
index e9f9ea85..9eed37d4 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -6,6 +6,9 @@
/* The prefix for our gettext translation domains. */
#mesondefine GETTEXT_PACKAGE
+/* Defined if FDK AAC is available */
+#mesondefine HAVE_FDK_AAC
+
/* Defined if RDP backend enabled */
#mesondefine HAVE_RDP
diff --git a/meson.build b/meson.build
index 1a30f4e3..d26e075d 100644
--- a/meson.build
+++ b/meson.build
@@ -26,6 +26,7 @@ libsecret_dep = dependency('libsecret-1')
pipewire_dep = dependency('libpipewire-0.3', version: '>= 0.3.49')
systemd_dep = dependency('systemd', required: get_option('systemd'))
+have_fdk_aac = get_option('fdk_aac')
have_rdp = get_option('rdp')
have_vnc = get_option('vnc')
@@ -33,6 +34,10 @@ if not have_rdp and not have_vnc
error('Must enable at least one backend')
endif
+if have_fdk_aac and not have_rdp
+ error('Support for audio output redirection using FDK AAC requires the RDP backend')
+endif
+
if have_rdp
add_global_arguments('-D_GNU_SOURCE', language : 'c')
@@ -45,6 +50,10 @@ if have_rdp
fuse_dep = dependency('fuse3', version: fuse_req)
winpr_dep = dependency('winpr2', version: freerdp_req)
xkbcommon_dep = dependency('xkbcommon', version: xkbcommon_req)
+
+ if have_fdk_aac
+ fdk_aac_dep = dependency('fdk-aac')
+ endif
endif
if have_vnc
@@ -66,6 +75,7 @@ cdata.set_quoted('VERSION', meson.project_version())
cdata.set('HAVE_RDP', have_rdp)
cdata.set('HAVE_VNC', have_vnc)
+cdata.set('HAVE_FDK_AAC', have_fdk_aac)
cdata.set_quoted('GRD_DATA_DIR', grd_datadir)
@@ -117,6 +127,10 @@ output = [
' RDP...................... ' + have_rdp.to_string(),
' VNC...................... ' + have_vnc.to_string(),
'',
+ ' Options for the RDP backend:',
+ '',
+ ' Support for audio output redirection using FDK AAC........' + have_fdk_aac.to_string(),
+ '',
' Now type \'ninja -C ' + meson.build_root() + '\' to build ' + meson.project_name(),
'',
'',
diff --git a/meson_options.txt b/meson_options.txt
index 2897f305..7ccaefb8 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -3,6 +3,11 @@ option('man',
value: true,
description: 'Generate man pages')
+option('fdk_aac',
+ type: 'boolean',
+ value: true,
+ description: 'Audio output redirection for the RDP backend')
+
option('rdp',
type: 'boolean',
value: true,
diff --git a/src/grd-rdp-dsp.c b/src/grd-rdp-dsp.c
new file mode 100644
index 00000000..01d87f54
--- /dev/null
+++ b/src/grd-rdp-dsp.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2022 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-rdp-dsp.h"
+
+#include <gio/gio.h>
+
+#ifdef HAVE_FDK_AAC
+#include <fdk-aac/aacenc_lib.h>
+#endif /* HAVE_FDK_AAC */
+
+struct _GrdRdpDsp
+{
+ GObject parent;
+
+#ifdef HAVE_FDK_AAC
+ HANDLE_AACENCODER aac_encoder;
+#endif /* HAVE_FDK_AAC */
+};
+
+G_DEFINE_TYPE (GrdRdpDsp, grd_rdp_dsp, G_TYPE_OBJECT)
+
+#ifdef HAVE_FDK_AAC
+static gboolean
+encode_aac (GrdRdpDsp *rdp_dsp,
+ int16_t *input_data,
+ int input_size,
+ int input_elem_size,
+ uint8_t **output_data,
+ uint32_t *output_size)
+{
+ AACENC_BufferIdentifier ident_in;
+ AACENC_BufferIdentifier ident_out;
+ AACENC_BufDesc buffer_descriptor_in = {};
+ AACENC_BufDesc buffer_descriptor_out = {};
+ AACENC_InArgs args_in = {};
+ AACENC_OutArgs args_out = {};
+ int output_allocation_size;
+ int output_elem_size;
+ AACENC_ERROR aac_error;
+
+ *output_size = 0;
+
+ ident_in = IN_AUDIO_DATA;
+
+ buffer_descriptor_in.numBufs = 1;
+ buffer_descriptor_in.bufs = (void **) &input_data;
+ buffer_descriptor_in.bufferIdentifiers = (int *) &ident_in;
+ buffer_descriptor_in.bufSizes = &input_size;
+ buffer_descriptor_in.bufElSizes = &input_elem_size;
+
+ ident_out = OUT_BITSTREAM_DATA;
+ output_elem_size = sizeof (uint8_t);
+
+ output_allocation_size = input_size;
+ *output_data = g_malloc0 (output_allocation_size);
+
+ buffer_descriptor_out.numBufs = 1;
+ buffer_descriptor_out.bufs = (void **) output_data;
+ buffer_descriptor_out.bufferIdentifiers = (int *) &ident_out;
+ buffer_descriptor_out.bufSizes = &output_allocation_size;
+ buffer_descriptor_out.bufElSizes = &output_elem_size;
+
+ args_in.numInSamples = input_size / input_elem_size;
+
+ aac_error = aacEncEncode (rdp_dsp->aac_encoder, &buffer_descriptor_in,
+ &buffer_descriptor_out, &args_in, &args_out);
+ if (aac_error != AACENC_OK)
+ {
+ g_warning ("[RDP.DSP] Failed to AAC encode samples");
+ g_clear_pointer (output_data, g_free);
+ return FALSE;
+ }
+
+ *output_size = args_out.numOutBytes;
+
+ return TRUE;
+}
+#endif /* HAVE_FDK_AAC */
+
+gboolean
+grd_rdp_dsp_encode (GrdRdpDsp *rdp_dsp,
+ GrdRdpDspCodec codec,
+ int16_t *input_data,
+ uint32_t input_size,
+ uint32_t input_elem_size,
+ uint8_t **output_data,
+ uint32_t *output_size)
+{
+ switch (codec)
+ {
+ case GRD_RDP_DSP_CODEC_NONE:
+ g_assert_not_reached ();
+ return FALSE;
+ case GRD_RDP_DSP_CODEC_AAC:
+#ifdef HAVE_FDK_AAC
+ return encode_aac (rdp_dsp, input_data, input_size, input_elem_size,
+ output_data, output_size);
+#else
+ g_assert_not_reached ();
+ return FALSE;
+#endif /* HAVE_FDK_AAC */
+ }
+
+ g_assert_not_reached ();
+
+ return FALSE;
+}
+
+#ifdef HAVE_FDK_AAC
+static gboolean
+create_aac_encoder (GrdRdpDsp *rdp_dsp,
+ uint32_t n_samples_per_sec,
+ uint32_t n_channels,
+ uint32_t bitrate,
+ GError **error)
+{
+ AACENC_InfoStruct info = {};
+ AACENC_ERROR aac_error;
+
+ /*
+ * Assert n_channels == 2 due to (currently) hardcoded
+ * AACENC_CHANNELMODE setting
+ */
+ g_assert (n_channels == 2);
+
+ aac_error = aacEncOpen (&rdp_dsp->aac_encoder, 0, n_channels);
+ if (!rdp_dsp->aac_encoder)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to create AAC encoder. AAC error %i", aac_error);
+ return FALSE;
+ }
+
+ aac_error = aacEncoder_SetParam (rdp_dsp->aac_encoder, AACENC_AOT,
+ AOT_AAC_LC);
+ if (aac_error != AACENC_OK)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to set AAC encoder param AACENC_AOT. "
+ "AAC error %i", aac_error);
+ return FALSE;
+ }
+
+ aac_error = aacEncoder_SetParam (rdp_dsp->aac_encoder, AACENC_BITRATE,
+ bitrate);
+ if (aac_error != AACENC_OK)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to set AAC encoder param AACENC_BITRATE. "
+ "AAC error %i", aac_error);
+ return FALSE;
+ }
+
+ aac_error = aacEncoder_SetParam (rdp_dsp->aac_encoder, AACENC_SAMPLERATE,
+ n_samples_per_sec);
+ if (aac_error != AACENC_OK)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to set AAC encoder param AACENC_SAMPLERATE. "
+ "AAC error %i", aac_error);
+ return FALSE;
+ }
+
+ aac_error = aacEncoder_SetParam (rdp_dsp->aac_encoder, AACENC_CHANNELMODE,
+ MODE_2);
+ if (aac_error != AACENC_OK)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to set AAC encoder param AACENC_CHANNELMODE. "
+ "AAC error %i", aac_error);
+ return FALSE;
+ }
+
+ aac_error = aacEncoder_SetParam (rdp_dsp->aac_encoder, AACENC_CHANNELORDER,
+ 1);
+ if (aac_error != AACENC_OK)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to set AAC encoder param AACENC_CHANNELORDER. "
+ "AAC error %i", aac_error);
+ return FALSE;
+ }
+
+ aac_error = aacEncoder_SetParam (rdp_dsp->aac_encoder, AACENC_TRANSMUX,
+ TT_MP4_RAW);
+ if (aac_error != AACENC_OK)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to set AAC encoder param AACENC_TRANSMUX. "
+ "AAC error %i", aac_error);
+ return FALSE;
+ }
+
+ aac_error = aacEncoder_SetParam (rdp_dsp->aac_encoder, AACENC_AFTERBURNER, 1);
+ if (aac_error != AACENC_OK)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to set AAC encoder param AACENC_AFTERBURNER. "
+ "AAC error %i", aac_error);
+ return FALSE;
+ }
+
+ aac_error = aacEncEncode (rdp_dsp->aac_encoder, NULL, NULL, NULL, NULL);
+ if (aac_error != AACENC_OK)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to initialize AAC encoder. AAC error %i", aac_error);
+ return FALSE;
+ }
+
+ aac_error = aacEncInfo (rdp_dsp->aac_encoder, &info);
+ if (aac_error != AACENC_OK)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to aquire AAC encoder info. AAC error %i",
+ aac_error);
+ return FALSE;
+ }
+
+ g_debug ("[RDP.DSP] AAC encoder: maxOutBufBytes: %u, maxAncBytes: %u, "
+ "inBufFillLevel: %u, inputChannels: %u, frameLength: %u, "
+ "nDelay: %u, nDelayCore: %u",
+ info.maxOutBufBytes, info.maxAncBytes, info.inBufFillLevel,
+ info.inputChannels, info.frameLength, info.nDelay, info.nDelayCore);
+
+ return TRUE;
+}
+#endif /* HAVE_FDK_AAC */
+
+GrdRdpDsp *
+grd_rdp_dsp_new (uint32_t n_samples_per_sec,
+ uint32_t n_channels,
+ uint32_t bitrate_aac,
+ GError **error)
+{
+ g_autoptr (GrdRdpDsp) rdp_dsp = NULL;
+
+ rdp_dsp = g_object_new (GRD_TYPE_RDP_DSP, NULL);
+
+#ifdef HAVE_FDK_AAC
+ if (!create_aac_encoder (rdp_dsp, n_samples_per_sec, n_channels, bitrate_aac,
+ error))
+ return NULL;
+#endif /* HAVE_FDK_AAC */
+
+ return g_steal_pointer (&rdp_dsp);
+}
+
+static void
+grd_rdp_dsp_dispose (GObject *object)
+{
+#ifdef HAVE_FDK_AAC
+ GrdRdpDsp *rdp_dsp = GRD_RDP_DSP (object);
+#endif /* HAVE_FDK_AAC */
+
+#ifdef HAVE_FDK_AAC
+ aacEncClose (&rdp_dsp->aac_encoder);
+#endif /* HAVE_FDK_AAC */
+
+ G_OBJECT_CLASS (grd_rdp_dsp_parent_class)->dispose (object);
+}
+
+static void
+grd_rdp_dsp_init (GrdRdpDsp *rdp_dsp)
+{
+}
+
+static void
+grd_rdp_dsp_class_init (GrdRdpDspClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = grd_rdp_dsp_dispose;
+}
diff --git a/src/grd-rdp-dsp.h b/src/grd-rdp-dsp.h
new file mode 100644
index 00000000..96c6c720
--- /dev/null
+++ b/src/grd-rdp-dsp.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef GRD_RDP_DSP_H
+#define GRD_RDP_DSP_H
+
+#include <glib-object.h>
+#include <stdint.h>
+
+#define GRD_TYPE_RDP_DSP (grd_rdp_dsp_get_type ())
+G_DECLARE_FINAL_TYPE (GrdRdpDsp, grd_rdp_dsp,
+ GRD, RDP_DSP, GObject)
+
+typedef enum _GrdRdpDspCodec
+{
+ GRD_RDP_DSP_CODEC_NONE,
+ GRD_RDP_DSP_CODEC_AAC,
+} GrdRdpDspCodec;
+
+GrdRdpDsp *grd_rdp_dsp_new (uint32_t n_samples_per_sec,
+ uint32_t n_channels,
+ uint32_t bitrate_aac,
+ GError **error);
+
+gboolean grd_rdp_dsp_encode (GrdRdpDsp *rdp_dsp,
+ GrdRdpDspCodec codec,
+ int16_t *input_data,
+ uint32_t input_size,
+ uint32_t input_elem_size,
+ uint8_t **output_data,
+ uint32_t *output_size);
+
+#endif /* GRD_RDP_DSP_H */
diff --git a/src/meson.build b/src/meson.build
index 31556d06..9f1b49ce 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -66,6 +66,8 @@ if have_rdp
'grd-rdp-damage-detector-memcmp.h',
'grd-rdp-display-control.c',
'grd-rdp-display-control.h',
+ 'grd-rdp-dsp.c',
+ 'grd-rdp-dsp.h',
'grd-rdp-event-queue.c',
'grd-rdp-event-queue.h',
'grd-rdp-frame-info.h',
@@ -106,6 +108,12 @@ if have_rdp
winpr_dep,
xkbcommon_dep,
]
+
+ if (have_fdk_aac)
+ deps += [
+ fdk_aac_dep,
+ ]
+ endif
endif
if have_vnc
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]