mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
500 lines
14 KiB
C
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;
|
|
}
|