Files
grd/grd-rdp-dsp.c
2026-02-13 13:06:50 +09:00

500 lines
14 KiB
C

/*
* 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 <fdk-aac/aacenc_lib.h>
#include <gio/gio.h>
#include <opus/opus.h>
#define G711_QUANT_MASK 0xF
#define G711_SEG_MASK 0x70
#define G711_SEG_SHIFT 4
struct _GrdRdpDsp
{
GObject parent;
GrdRdpDspCreateFlag create_flags;
uint32_t n_channels;
HANDLE_AACENCODER aac_encoder;
uint32_t aac_frame_length;
OpusEncoder *opus_encoder;
uint32_t opus_frame_length;
};
G_DEFINE_TYPE (GrdRdpDsp, grd_rdp_dsp, G_TYPE_OBJECT)
const char *
grd_rdp_dsp_codec_to_string (GrdRdpDspCodec dsp_codec)
{
switch (dsp_codec)
{
case GRD_RDP_DSP_CODEC_NONE:
return "PCM";
case GRD_RDP_DSP_CODEC_AAC:
return "AAC";
case GRD_RDP_DSP_CODEC_ALAW:
return "A-law";
case GRD_RDP_DSP_CODEC_OPUS:
return "Opus";
}
g_assert_not_reached ();
}
uint32_t
grd_rdp_dsp_get_frames_per_packet (GrdRdpDsp *rdp_dsp,
GrdRdpDspCodec codec)
{
g_assert (rdp_dsp->create_flags & GRD_RDP_DSP_CREATE_FLAG_ENCODER);
switch (codec)
{
case GRD_RDP_DSP_CODEC_NONE:
g_assert_not_reached ();
case GRD_RDP_DSP_CODEC_AAC:
return rdp_dsp->aac_frame_length;
case GRD_RDP_DSP_CODEC_ALAW:
g_assert_not_reached ();
case GRD_RDP_DSP_CODEC_OPUS:
return rdp_dsp->opus_frame_length;
}
g_assert_not_reached ();
}
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;
}
static gboolean
encode_opus (GrdRdpDsp *rdp_dsp,
int16_t *input_data,
uint32_t input_size,
uint32_t input_elem_size,
uint8_t **output_data,
uint32_t *output_size)
{
uint32_t frames;
int32_t length;
int output_allocation_size;
frames = input_size / rdp_dsp->n_channels / input_elem_size;
g_assert (frames > 0);
output_allocation_size = input_size;
*output_data = g_malloc0 (output_allocation_size);
length = opus_encode (rdp_dsp->opus_encoder, input_data, frames,
*output_data, output_allocation_size);
if (length < 0)
{
g_warning ("[RDP.DSP] Failed to Opus encode samples: %s",
opus_strerror (length));
g_clear_pointer (output_data, g_free);
return FALSE;
}
*output_size = length;
return TRUE;
}
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)
{
g_assert (rdp_dsp->create_flags & GRD_RDP_DSP_CREATE_FLAG_ENCODER);
switch (codec)
{
case GRD_RDP_DSP_CODEC_NONE:
g_assert_not_reached ();
return FALSE;
case GRD_RDP_DSP_CODEC_AAC:
return encode_aac (rdp_dsp, input_data, input_size, input_elem_size,
output_data, output_size);
case GRD_RDP_DSP_CODEC_ALAW:
g_assert_not_reached ();
return FALSE;
case GRD_RDP_DSP_CODEC_OPUS:
return encode_opus (rdp_dsp, input_data, input_size, input_elem_size,
output_data, output_size);
}
g_assert_not_reached ();
return FALSE;
}
static int16_t
alaw_to_s16 (uint8_t alaw_value)
{
int16_t segment;
int16_t temp;
alaw_value ^= 0x55;
temp = (alaw_value & G711_QUANT_MASK) << 4;
temp += 8;
segment = (alaw_value & G711_SEG_MASK) >> G711_SEG_SHIFT;
if (segment > 0)
temp += 0x100;
if (segment > 1)
temp <<= segment - 1;
return alaw_value > 127 ? temp : -temp;
}
static gboolean
decode_alaw (GrdRdpDsp *rdp_dsp,
uint8_t *input_data,
uint32_t input_size,
int16_t **output_data,
uint32_t *output_size)
{
uint32_t i;
g_assert (output_data);
g_assert (output_size);
g_assert (input_data);
g_assert (input_size > 0);
*output_size = input_size / sizeof (uint8_t) * sizeof (int16_t);
*output_data = g_malloc0 (*output_size);
for (i = 0; i < input_size; ++i)
(*output_data)[i] = alaw_to_s16 (input_data[i]);
return TRUE;
}
gboolean
grd_rdp_dsp_decode (GrdRdpDsp *rdp_dsp,
GrdRdpDspCodec codec,
uint8_t *input_data,
uint32_t input_size,
int16_t **output_data,
uint32_t *output_size)
{
g_assert (rdp_dsp->create_flags & GRD_RDP_DSP_CREATE_FLAG_DECODER);
switch (codec)
{
case GRD_RDP_DSP_CODEC_NONE:
case GRD_RDP_DSP_CODEC_AAC:
g_assert_not_reached ();
return FALSE;
case GRD_RDP_DSP_CODEC_ALAW:
return decode_alaw (rdp_dsp, input_data, input_size,
output_data, output_size);
case GRD_RDP_DSP_CODEC_OPUS:
g_assert_not_reached ();
return FALSE;
}
g_assert_not_reached ();
return FALSE;
}
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 (aac_error != AACENC_OK)
{
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 acquire AAC encoder info. AAC error %i",
aac_error);
return FALSE;
}
rdp_dsp->aac_frame_length = info.frameLength;
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;
}
static gboolean
create_opus_encoder (GrdRdpDsp *rdp_dsp,
uint32_t n_samples_per_sec,
uint32_t n_channels,
uint32_t bitrate,
GError **error)
{
int opus_error = OPUS_OK;
rdp_dsp->opus_encoder = opus_encoder_create (n_samples_per_sec, n_channels,
OPUS_APPLICATION_AUDIO,
&opus_error);
if (opus_error != OPUS_OK)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to create Opus encoder: %s",
opus_strerror (opus_error));
return FALSE;
}
g_assert (rdp_dsp->opus_encoder);
opus_error = opus_encoder_ctl (rdp_dsp->opus_encoder,
OPUS_SET_BITRATE(bitrate));
if (opus_error != OPUS_OK)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to set Opus encoder bitrate: %s",
opus_strerror (opus_error));
return FALSE;
}
rdp_dsp->opus_frame_length = n_samples_per_sec / 50;
return TRUE;
}
static gboolean
create_encoders (GrdRdpDsp *rdp_dsp,
const GrdRdpDspDescriptor *dsp_descriptor,
GError **error)
{
rdp_dsp->n_channels = dsp_descriptor->n_channels;
if (!create_aac_encoder (rdp_dsp,
dsp_descriptor->n_samples_per_sec_aac,
dsp_descriptor->n_channels,
dsp_descriptor->bitrate_aac,
error))
return FALSE;
if (!create_opus_encoder (rdp_dsp,
dsp_descriptor->n_samples_per_sec_opus,
dsp_descriptor->n_channels,
dsp_descriptor->bitrate_opus,
error))
return FALSE;
return TRUE;
}
GrdRdpDsp *
grd_rdp_dsp_new (const GrdRdpDspDescriptor *dsp_descriptor,
GError **error)
{
g_autoptr (GrdRdpDsp) rdp_dsp = NULL;
rdp_dsp = g_object_new (GRD_TYPE_RDP_DSP, NULL);
rdp_dsp->create_flags = dsp_descriptor->create_flags;
if (dsp_descriptor->create_flags & GRD_RDP_DSP_CREATE_FLAG_ENCODER &&
!create_encoders (rdp_dsp, dsp_descriptor, error))
return NULL;
return g_steal_pointer (&rdp_dsp);
}
static void
grd_rdp_dsp_dispose (GObject *object)
{
GrdRdpDsp *rdp_dsp = GRD_RDP_DSP (object);
g_clear_pointer (&rdp_dsp->opus_encoder, opus_encoder_destroy);
aacEncClose (&rdp_dsp->aac_encoder);
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;
}