[codec,dsp] add FDK-AAC support

Support fdk-aac library for sound encoding/decoding
Special thanks to myth0s who posted the working decoder configuration
sample on our matrix chat.
This commit is contained in:
Armin Novak 2024-07-10 14:28:26 +02:00
parent e54cd3ec13
commit 99f504640c
No known key found for this signature in database
GPG Key ID: 2CF4A2D2D3D72105
8 changed files with 912 additions and 2 deletions

View File

@ -96,6 +96,18 @@ macro (freerdp_compile_options_add)
set (LIBFREERDP_COMPILE_OPTIONS ${LIBFREERDP_COMPILE_OPTIONS} PARENT_SCOPE)
endmacro()
option(WITH_FDK_AAC "Enable FDK_AAC support" OFF)
if (WITH_FDK_AAC)
find_package(PkgConfig REQUIRED)
pkg_check_modules(FDK_AAC REQUIRED fdk-aac)
add_definitions(-DWITH_FDK_AAC)
include_directories(${FDK_AAC_INCLUDE_DIRS})
link_directories(${FDK_AAC_LIBRARY_DIRS})
freerdp_library_add(${FDK_AAC_LIBRARIES})
endif()
set(OPUS_DEFAULT OFF)
if (NOT WITH_DSP_FFMPEG)
find_package(Opus)
@ -160,8 +172,8 @@ foreach(${MODULE_PREFIX}_SUBMODULE ${${MODULE_PREFIX}_SUBMODULES})
add_subdirectory(${${MODULE_PREFIX}_SUBMODULE})
endforeach()
if (NOT WITH_DSP_FFMPEG AND NOT WITH_FAAC)
message(WARNING "Compiling without WITH_DSP_FFMPEG and WITH_FAAC, AAC encoder support disabled")
if (NOT WITH_DSP_FFMPEG AND NOT WITH_FAAC AND NOT WITH_FDK_AAC)
message(WARNING "Compiling without WITH_DSP_FFMPEG, WITH_FAAC and WITH_FDK_AAC. AAC encoder support disabled")
endif ()
add_subdirectory(codec)

View File

@ -111,6 +111,14 @@ if(LAME_FOUND)
include_directories(${LAME_INCLUDE_DIRS})
endif()
if (WITH_FDK_AAC)
list(APPEND CODEC_SRCS
dsp_fdk_impl.c
dsp_fdk_impl.h
dsp_fdk_aac.c
dsp_fdk_aac.h)
endif()
if(FAAD2_FOUND)
list(APPEND CODEC_LIBS ${FAAD2_LIBRARIES})
include_directories(${FAAD2_INCLUDE_DIRS})

View File

@ -32,6 +32,10 @@
#include "dsp.h"
#if defined(WITH_FDK_AAC)
#include "dsp_fdk_aac.h"
#endif
#if !defined(WITH_DSP_FFMPEG)
#if defined(WITH_GSM)
#include <gsm/gsm.h>
@ -1233,6 +1237,18 @@ BOOL freerdp_dsp_encode(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context,
const AUDIO_FORMAT* WINPR_RESTRICT srcFormat,
const BYTE* WINPR_RESTRICT data, size_t length, wStream* WINPR_RESTRICT out)
{
#if defined(WITH_FDK_AAC)
FREERDP_DSP_COMMON_CONTEXT* ctx = (FREERDP_DSP_COMMON_CONTEXT*)context;
WINPR_ASSERT(ctx);
switch (ctx->format.wFormatTag)
{
case WAVE_FORMAT_AAC_MS:
return fdk_aac_dsp_encode(ctx, srcFormat, data, length, out);
default:
break;
}
#endif
#if defined(WITH_DSP_FFMPEG)
return freerdp_dsp_ffmpeg_encode(context, srcFormat, data, length, out);
#else
@ -1299,6 +1315,18 @@ BOOL freerdp_dsp_decode(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context,
const AUDIO_FORMAT* WINPR_RESTRICT srcFormat,
const BYTE* WINPR_RESTRICT data, size_t length, wStream* WINPR_RESTRICT out)
{
#if defined(WITH_FDK_AAC)
FREERDP_DSP_COMMON_CONTEXT* ctx = (FREERDP_DSP_COMMON_CONTEXT*)context;
WINPR_ASSERT(ctx);
switch (ctx->format.wFormatTag)
{
case WAVE_FORMAT_AAC_MS:
return fdk_aac_dsp_decode(ctx, srcFormat, data, length, out);
default:
break;
}
#endif
#if defined(WITH_DSP_FFMPEG)
return freerdp_dsp_ffmpeg_decode(context, srcFormat, data, length, out);
#else
@ -1350,6 +1378,17 @@ BOOL freerdp_dsp_decode(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context,
BOOL freerdp_dsp_supports_format(const AUDIO_FORMAT* WINPR_RESTRICT format, BOOL encode)
{
#if defined(WITH_FDK_AAC)
switch (format->wFormatTag)
{
case WAVE_FORMAT_AAC_MS:
return TRUE;
default:
break;
}
#endif
#if defined(WITH_DSP_FFMPEG)
return freerdp_dsp_ffmpeg_supports_format(format, encode);
#else
@ -1417,6 +1456,17 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context,
const AUDIO_FORMAT* WINPR_RESTRICT targetFormat,
UINT32 FramesPerPacket)
{
#if defined(WITH_FDK_AAC)
WINPR_ASSERT(targetFormat);
if (targetFormat->wFormatTag == WAVE_FORMAT_AAC_MS)
{
FREERDP_DSP_COMMON_CONTEXT* ctx = (FREERDP_DSP_COMMON_CONTEXT*)context;
fdk_aac_dsp_uninit(ctx);
ctx->format = *targetFormat;
return fdk_aac_dsp_init(ctx, FramesPerPacket);
}
#endif
#if defined(WITH_DSP_FFMPEG)
return freerdp_dsp_ffmpeg_context_reset(context, targetFormat);
#else

View File

@ -32,6 +32,12 @@ typedef struct
ALIGN64 wStream* buffer;
ALIGN64 wStream* resample;
ALIGN64 wStream* channelmix;
#if defined(WITH_FDK_AAC)
ALIGN64 BOOL fdkSetup;
ALIGN64 void* fdkAacInstance;
ALIGN64 size_t buffersize;
ALIGN64 unsigned frames_per_packet;
#endif
} FREERDP_DSP_COMMON_CONTEXT;
BOOL freerdp_dsp_common_context_init(FREERDP_DSP_COMMON_CONTEXT* context, BOOL encode);

View File

@ -0,0 +1,154 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Digital Sound Processing
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "dsp_fdk_aac.h"
#include "dsp_fdk_impl.h"
#include <freerdp/log.h>
#define TAG FREERDP_TAG("dsp.fdk")
static void write_log(unsigned log_level, const char* fmt, ...)
{
wLog* log = WLog_Get(TAG);
if (WLog_IsLevelActive(log, log_level))
{
char buffer[1024] = { 0 };
va_list ap;
va_start(ap, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, __LINE__, __FILE__, __FUNCTION__, "%s",
buffer);
}
}
BOOL fdk_aac_dsp_encode(FREERDP_DSP_COMMON_CONTEXT* context, const AUDIO_FORMAT* srcFormat,
const BYTE* data, size_t length, wStream* out)
{
WINPR_ASSERT(context);
WINPR_ASSERT(srcFormat);
if (srcFormat->wFormatTag != WAVE_FORMAT_PCM)
{
WLog_WARN(TAG, "Feeding %s format data to encoder function, but require %s",
audio_format_get_tag_string(srcFormat->wFormatTag),
audio_format_get_tag_string(WAVE_FORMAT_PCM));
return FALSE;
}
if (!context->fdkSetup)
{
ssize_t rc = fdk_aac_dsp_impl_config(
context->fdkAacInstance, &context->buffersize, context->encoder,
context->format.nSamplesPerSec, context->format.nChannels,
context->format.nAvgBytesPerSec, context->frames_per_packet, write_log);
if (rc < 0)
return FALSE;
context->fdkSetup = TRUE;
}
if (!Stream_EnsureRemainingCapacity(out, context->buffersize))
return FALSE;
{
const ssize_t encoded =
fdk_aac_dsp_impl_encode(context->fdkAacInstance, data, length, Stream_Pointer(out),
Stream_GetRemainingCapacity(out), write_log);
if (encoded < 0)
return FALSE;
Stream_Seek(out, (size_t)encoded);
return TRUE;
}
}
BOOL fdk_aac_dsp_decode(FREERDP_DSP_COMMON_CONTEXT* context, const AUDIO_FORMAT* srcFormat,
const BYTE* data, size_t length, wStream* out)
{
WINPR_ASSERT(context);
WINPR_ASSERT(srcFormat);
if (srcFormat->wFormatTag != WAVE_FORMAT_AAC_MS)
{
WLog_WARN(TAG, "Feeding %s format data to encoder function, but require %s",
audio_format_get_tag_string(srcFormat->wFormatTag),
audio_format_get_tag_string(WAVE_FORMAT_AAC_MS));
return FALSE;
}
if (!context->fdkSetup)
{
ssize_t rc = fdk_aac_dsp_impl_config(
context->fdkAacInstance, &context->buffersize, context->encoder,
context->format.nSamplesPerSec, context->format.nChannels,
context->format.nAvgBytesPerSec, context->frames_per_packet, write_log);
if (rc < 0)
return FALSE;
context->fdkSetup = TRUE;
}
ssize_t rest = 0;
do
{
rest = fdk_aac_dsp_impl_decode_fill(context->fdkAacInstance, data, length, write_log);
if (rest < 0)
{
WLog_WARN(TAG, "DecodeFill() failed");
return FALSE;
}
ssize_t ret = -1;
do
{
const size_t expect = context->buffersize;
if (!Stream_EnsureRemainingCapacity(out, expect))
return FALSE;
ret = fdk_aac_dsp_impl_decode_read(context->fdkAacInstance, Stream_Pointer(out), expect,
write_log);
if (ret < 0)
return FALSE;
Stream_Seek(out, (size_t)ret);
} while (ret > 0);
} while (rest > 0);
return TRUE;
}
void fdk_aac_dsp_uninit(FREERDP_DSP_COMMON_CONTEXT* context)
{
WINPR_ASSERT(context);
fdk_aac_dsp_impl_uninit(&context->fdkAacInstance, context->encoder, write_log);
}
BOOL fdk_aac_dsp_init(FREERDP_DSP_COMMON_CONTEXT* context, size_t frames_per_packet)
{
WINPR_ASSERT(context);
context->fdkSetup = FALSE;
WINPR_ASSERT(frames_per_packet <= UINT_MAX);
context->frames_per_packet = (unsigned)frames_per_packet;
return fdk_aac_dsp_impl_init(&context->fdkAacInstance, context->encoder, write_log);
}

View File

@ -0,0 +1,38 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Digital Sound Processing
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_DSP_FDK_AAC_H_
#define FREERDP_DSP_FDK_AAC_H_
#include <winpr/stream.h>
#include <freerdp/codec/audio.h>
#include "dsp.h"
BOOL fdk_aac_dsp_init(FREERDP_DSP_COMMON_CONTEXT* context, size_t frames_per_packet);
void fdk_aac_dsp_uninit(FREERDP_DSP_COMMON_CONTEXT* context);
BOOL fdk_aac_dsp_encode(FREERDP_DSP_COMMON_CONTEXT* context, const AUDIO_FORMAT* srcFormat,
const BYTE* data, size_t length, wStream* out);
BOOL fdk_aac_dsp_decode(FREERDP_DSP_COMMON_CONTEXT* context, const AUDIO_FORMAT* srcFormat,
const BYTE* data, size_t length, wStream* out);
#endif

View File

@ -0,0 +1,597 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Digital Sound Processing
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <inttypes.h>
#include <fdk-aac/aacdecoder_lib.h>
#include <fdk-aac/aacenc_lib.h>
#include "dsp_fdk_impl.h"
#define WLOG_TRACE 0
#define WLOG_DEBUG 1
#define WLOG_INFO 2
#define WLOG_WARN 3
#define WLOG_ERROR 4
#define WLOG_FATAL 5
static const char* enc_err_str(AACENC_ERROR err)
{
switch (err)
{
case AACENC_OK:
return "AACENC_OK";
case AACENC_INVALID_HANDLE:
return "AACENC_INVALID_HANDLE";
case AACENC_MEMORY_ERROR:
return "AACENC_MEMORY_ERROR";
case AACENC_UNSUPPORTED_PARAMETER:
return "AACENC_UNSUPPORTED_PARAMETER";
case AACENC_INVALID_CONFIG:
return "AACENC_INVALID_CONFIG";
case AACENC_INIT_ERROR:
return "AACENC_INIT_ERROR";
case AACENC_INIT_AAC_ERROR:
return "AACENC_INIT_AAC_ERROR";
case AACENC_INIT_SBR_ERROR:
return "AACENC_INIT_SBR_ERROR";
case AACENC_INIT_TP_ERROR:
return "AACENC_INIT_TP_ERROR";
case AACENC_INIT_META_ERROR:
return "AACENC_INIT_META_ERROR";
#ifdef AACENC_INIT_MPS_ERROR
case AACENC_INIT_MPS_ERROR:
return "AACENC_INIT_MPS_ERROR";
#endif
case AACENC_ENCODE_ERROR:
return "AACENC_ENCODE_ERROR";
case AACENC_ENCODE_EOF:
return "AACENC_ENCODE_EOF";
default:
return "AACENC_UNKNOWN";
}
}
static const char* dec_err_str(AAC_DECODER_ERROR err)
{
switch (err)
{
case AAC_DEC_OK:
return "AAC_DEC_OK";
case AAC_DEC_OUT_OF_MEMORY:
return "AAC_DEC_OUT_OF_MEMORY";
case AAC_DEC_UNKNOWN:
return "AAC_DEC_UNKNOWN";
case aac_dec_sync_error_start:
return "aac_dec_sync_error_start";
case AAC_DEC_TRANSPORT_SYNC_ERROR:
return "AAC_DEC_TRANSPORT_SYNC_ERROR";
case AAC_DEC_NOT_ENOUGH_BITS:
return "AAC_DEC_NOT_ENOUGH_BITS";
case aac_dec_sync_error_end:
return "aac_dec_sync_error_end";
case aac_dec_init_error_start:
return "aac_dec_init_error_start";
case AAC_DEC_INVALID_HANDLE:
return "AAC_DEC_INVALID_HANDLE";
case AAC_DEC_UNSUPPORTED_FORMAT:
return "AAC_DEC_UNSUPPORTED_FORMAT";
case AAC_DEC_UNSUPPORTED_ER_FORMAT:
return "AAC_DEC_UNSUPPORTED_ER_FORMAT";
case AAC_DEC_UNSUPPORTED_EPCONFIG:
return "AAC_DEC_UNSUPPORTED_EPCONFIG";
case AAC_DEC_UNSUPPORTED_MULTILAYER:
return "AAC_DEC_UNSUPPORTED_MULTILAYER";
case AAC_DEC_UNSUPPORTED_CHANNELCONFIG:
return "AAC_DEC_UNSUPPORTED_CHANNELCONFIG";
case AAC_DEC_UNSUPPORTED_SAMPLINGRATE:
return "AAC_DEC_UNSUPPORTED_SAMPLINGRATE";
case AAC_DEC_INVALID_SBR_CONFIG:
return "AAC_DEC_INVALID_SBR_CONFIG";
case AAC_DEC_SET_PARAM_FAIL:
return "AAC_DEC_SET_PARAM_FAIL";
case AAC_DEC_NEED_TO_RESTART:
return "AAC_DEC_NEED_TO_RESTART";
case AAC_DEC_OUTPUT_BUFFER_TOO_SMALL:
return "AAC_DEC_OUTPUT_BUFFER_TOO_SMALL";
case aac_dec_init_error_end:
return "aac_dec_init_error_end";
case aac_dec_decode_error_start:
return "aac_dec_decode_error_start";
case AAC_DEC_TRANSPORT_ERROR:
return "AAC_DEC_TRANSPORT_ERROR";
case AAC_DEC_PARSE_ERROR:
return "AAC_DEC_PARSE_ERROR";
case AAC_DEC_UNSUPPORTED_EXTENSION_PAYLOAD:
return "AAC_DEC_UNSUPPORTED_EXTENSION_PAYLOAD";
case AAC_DEC_DECODE_FRAME_ERROR:
return "AAC_DEC_DECODE_FRAME_ERROR";
case AAC_DEC_CRC_ERROR:
return "AAC_DEC_CRC_ERROR";
case AAC_DEC_INVALID_CODE_BOOK:
return "AAC_DEC_INVALID_CODE_BOOK";
case AAC_DEC_UNSUPPORTED_PREDICTION:
return "AAC_DEC_UNSUPPORTED_PREDICTION";
case AAC_DEC_UNSUPPORTED_CCE:
return "AAC_DEC_UNSUPPORTED_CCE";
case AAC_DEC_UNSUPPORTED_LFE:
return "AAC_DEC_UNSUPPORTED_LFE";
case AAC_DEC_UNSUPPORTED_GAIN_CONTROL_DATA:
return "AAC_DEC_UNSUPPORTED_GAIN_CONTROL_DATA";
case AAC_DEC_UNSUPPORTED_SBA:
return "AAC_DEC_UNSUPPORTED_SBA";
case AAC_DEC_TNS_READ_ERROR:
return "AAC_DEC_TNS_READ_ERROR";
case AAC_DEC_RVLC_ERROR:
return "AAC_DEC_RVLC_ERROR";
case aac_dec_decode_error_end:
return "aac_dec_decode_error_end";
case aac_dec_anc_data_error_start:
return "aac_dec_anc_data_error_start";
case AAC_DEC_ANC_DATA_ERROR:
return "AAC_DEC_ANC_DATA_ERROR";
case AAC_DEC_TOO_SMALL_ANC_BUFFER:
return "AAC_DEC_TOO_SMALL_ANC_BUFFER";
case AAC_DEC_TOO_MANY_ANC_ELEMENTS:
return "AAC_DEC_TOO_MANY_ANC_ELEMENTS";
case aac_dec_anc_data_error_end:
return "aac_dec_anc_data_error_end";
default:
return "AAC_DEC unknown value";
}
}
static void log_dec_info(const CStreamInfo* info, void (*log)(const char* fmt, ...))
{
assert(info);
assert(log);
log("info:"
"aacSampleRate: %d, "
"frameSize: %d, "
"numChannels: %d, "
"pChannelType: %p, "
"pChannelIndices: %p, "
"aacSampleRate: %d, "
"profile: %d, "
"aot: %d, " /* TODO: Enum 2 string */
"channelConfig: %d, "
"bitRate: %d, "
"aacSamplesPerFrame: %d, "
"aacNumChannels: %d, "
"extAot: %d" /* TODO: Enum 2 string */
"extSamplingRate: %d, "
"outputDelay: %u, "
"flags: %u, "
"epConfig: %d, "
"numLostAccessUnits: %d, "
"numTotalBytes: %" PRIu64 ", "
"numBadBytes: %" PRIu64 ", "
"numTotalAccessUnits: %" PRIu64 ", "
"numBadAccessUnits: %" PRIu64 ", "
"drcProgRefLev: %d, "
"drcPresMode: %d, ",
info->aacSampleRate, info->frameSize, info->numChannels, info->pChannelType,
info->pChannelIndices, info->aacSampleRate, info->profile, info->aot, info->channelConfig,
info->bitRate, info->aacSamplesPerFrame, info->aacNumChannels, info->extAot,
info->extSamplingRate, info->outputDelay, info->flags, (int)info->epConfig,
info->numLostAccessUnits,
info->numTotalBytes, info->numBadBytes, info->numTotalAccessUnits, info->numBadAccessUnits,
(int)info->drcProgRefLev, (int)info->drcPresMode);
}
static void log_enc_info(const AACENC_InfoStruct* info, fdk_log_fkt_t log)
{
size_t x;
char confBuf[1024] = { 0 };
assert(info);
assert(log);
strcat(confBuf, "{");
for (x = 0; x < 64; x++)
{
char tmp[12] = { 0 };
sprintf(tmp, "0x%02x", (int)info->confBuf[x]);
if (x > 0)
strcat(confBuf, ", ");
strcat(confBuf, tmp);
}
strcat(confBuf, "}");
log(WLOG_DEBUG,
"[encoder info] "
"maxOutBufBytes : %u, "
"maxAncBytes : %u, "
"inBufFillLevel : %u, "
"inputChannels : %u, "
"frameLength : %u, "
#ifdef MODE_7_1_BACK
"nDelay : %u, "
"nDelayCore : %u, "
#endif
"confBuf[64] : %s, "
"confSize : %u",
info->maxOutBufBytes, info->maxAncBytes, info->inBufFillLevel, info->inputChannels,
info->frameLength,
#ifdef MODE_7_1_BACK
info->nDelay, info->nDelayCore,
#endif
confBuf, info->confSize);
}
static const char* aac_enc_param_str(AACENC_PARAM param)
{
switch (param)
{
case AACENC_AOT:
return "AACENC_AOT";
case AACENC_BITRATE:
return "AACENC_BITRATE";
case AACENC_BITRATEMODE:
return "AACENC_BITRATEMODE";
case AACENC_SAMPLERATE:
return "AACENC_SAMPLERATE";
case AACENC_SBR_MODE:
return "AACENC_SBR_MODE";
case AACENC_GRANULE_LENGTH:
return "AACENC_GRANULE_LENGTH";
case AACENC_CHANNELMODE:
return "AACENC_CHANNELMODE";
case AACENC_CHANNELORDER:
return "AACENC_CHANNELORDER";
case AACENC_SBR_RATIO:
return "AACENC_SBR_RATIO";
case AACENC_AFTERBURNER:
return "AACENC_AFTERBURNER";
case AACENC_BANDWIDTH:
return "AACENC_BANDWIDTH";
case AACENC_PEAK_BITRATE:
return "AACENC_PEAK_BITRATE";
case AACENC_TRANSMUX:
return "AACENC_TRANSMUX";
case AACENC_HEADER_PERIOD:
return "AACENC_HEADER_PERIOD";
case AACENC_SIGNALING_MODE:
return "AACENC_SIGNALING_MODE";
case AACENC_TPSUBFRAMES:
return "AACENC_TPSUBFRAMES";
case AACENC_AUDIOMUXVER:
return "AACENC_AUDIOMUXVER";
case AACENC_PROTECTION:
return "AACENC_PROTECTION";
case AACENC_ANCILLARY_BITRATE:
return "AACENC_ANCILLARY_BITRATE";
case AACENC_METADATA_MODE:
return "AACENC_METADATA_MODE";
case AACENC_CONTROL_STATE:
return "AACENC_CONTROL_STATE";
default:
return "AACENC_UNKNOWN";
}
}
int fdk_aac_dsp_impl_init(void** handle, int encoder, fdk_log_fkt_t log)
{
assert(handle);
assert(log);
if (encoder)
{
HANDLE_AACENCODER* h = (HANDLE_AACENCODER*)handle;
AACENC_ERROR err = aacEncOpen(h, 0, 0);
if (err != AACENC_OK)
{
log(WLOG_ERROR, "aacEncOpen failed with %s", enc_err_str(err));
return 0;
}
}
else
{
HANDLE_AACDECODER* h = (HANDLE_AACDECODER*)handle;
assert(NULL == *h);
*h = aacDecoder_Open(TT_MP4_RAW, 1);
if (!*h)
{
log(WLOG_ERROR, "aacDecoder_Open failed");
return 0;
}
}
return 1;
}
void fdk_aac_dsp_impl_uninit(void** handle, int encoder, fdk_log_fkt_t log)
{
assert(handle);
assert(log);
if (encoder)
{
HANDLE_AACENCODER* h = (HANDLE_AACENCODER*)handle;
AACENC_ERROR err = aacEncClose(h);
if (err != AACENC_OK)
log(WLOG_ERROR, "aacEncClose failed with %s", enc_err_str(err));
}
else
{
HANDLE_AACDECODER* h = (HANDLE_AACDECODER*)handle;
if (h)
aacDecoder_Close(*h);
}
*handle = NULL;
}
ssize_t fdk_aac_dsp_impl_decode_read(void* handle, void* dst, size_t dstSize, fdk_log_fkt_t log)
{
assert(handle);
assert((dstSize / sizeof(INT_PCM)) <= INT_MAX);
const INT nrsamples = (INT)(dstSize / sizeof(INT_PCM));
UINT flags = 0;
HANDLE_AACDECODER self = (HANDLE_AACDECODER)handle;
AAC_DECODER_ERROR err = aacDecoder_DecodeFrame(self, dst, nrsamples, flags);
switch (err)
{
case AAC_DEC_OK:
return fdk_aac_dsp_impl_stream_info(handle, 0, log);
case AAC_DEC_NOT_ENOUGH_BITS:
return 0;
default:
log(WLOG_ERROR, "aacDecoder_DecodeFrame failed with %s", dec_err_str(err));
return -1;
}
}
static unsigned get_channelmode(unsigned channels)
{
switch (channels)
{
case 1:
return MODE_1;
case 2:
return MODE_2;
case 3:
return MODE_1_2;
case 4:
return MODE_1_2_1;
case 5:
return MODE_1_2_2;
case 6:
return MODE_1_2_2_1;
case 7:
return MODE_1_2_2_2_1;
#ifdef MODE_7_1_BACK
case 8:
return MODE_7_1_BACK;
#endif
default:
return MODE_2;
}
}
int fdk_aac_dsp_impl_config(void* handle, size_t* pbuffersize, int encoder, unsigned samplerate,
unsigned channels, unsigned bytes_per_second,
unsigned frames_per_packet, fdk_log_fkt_t log)
{
assert(handle);
assert(log);
assert(pbuffersize);
log(WLOG_DEBUG,
"fdk_aac_dsp_impl_config: samplerate: %ld, channels: %ld, bytes_pers_second: %ld",
samplerate, channels, bytes_per_second);
size_t x;
AACENC_ERROR err;
struct t_param_pair
{
AACENC_PARAM param;
UINT value;
};
const struct t_param_pair params[] = { { AACENC_AOT, 2 },
{ AACENC_SAMPLERATE, samplerate },
{ AACENC_CHANNELMODE, get_channelmode(channels) },
{ AACENC_CHANNELORDER, 0 },
{ AACENC_BITRATE, bytes_per_second * 8 },
{ AACENC_TRANSMUX, 0 },
{ AACENC_AFTERBURNER, 1 } };
HANDLE_AACENCODER self;
if (encoder)
self = (HANDLE_AACENCODER)handle;
else
{
AACENC_ERROR err = aacEncOpen(&self, 0, channels);
if (err != AACENC_OK)
{
log(WLOG_ERROR, "aacEncOpen failed with %s", enc_err_str(err));
return -1;
}
}
for (x = 0; x < sizeof(params) / sizeof(params[0]); x++)
{
const struct t_param_pair* param = &params[x];
err = aacEncoder_SetParam(self, param->param, param->value);
if (err != AACENC_OK)
{
log(WLOG_ERROR, "aacEncoder_SetParam(%s, %d) failed with %s",
aac_enc_param_str(param->param), param->value, enc_err_str(err));
return -1;
}
}
err = aacEncEncode(self, NULL, NULL, NULL, NULL);
if (err != AACENC_OK)
{
log(WLOG_ERROR, "aacEncEncode failed with %s", enc_err_str(err));
return -1;
}
AACENC_InfoStruct info = { 0 };
err = aacEncInfo(self, &info);
if (err != AACENC_OK)
{
log(WLOG_ERROR, "aacEncInfo failed with %s", enc_err_str(err));
return -1;
}
if (encoder)
{
*pbuffersize = info.maxOutBufBytes;
log_enc_info(&info, log);
return 0;
}
else
{
err = aacEncClose(&self);
if (err != AACENC_OK)
log(WLOG_WARN, "aacEncClose failed with %s", enc_err_str(err));
*pbuffersize = info.frameLength * info.inputChannels * sizeof(INT_PCM);
AAC_DECODER_ERROR decerr;
HANDLE_AACDECODER aacdec = (HANDLE_AACDECODER)handle;
UCHAR* asc[] = { info.confBuf };
UINT ascSize[] = { info.confSize };
assert(handle);
decerr = aacDecoder_ConfigRaw(aacdec, asc, ascSize);
if (decerr != AAC_DEC_OK)
{
log(WLOG_ERROR, "aacDecoder_ConfigRaw failed with %s", dec_err_str(decerr));
return -1;
}
return 0;
}
}
ssize_t fdk_aac_dsp_impl_decode_fill(void* handle, const void* data, size_t size, fdk_log_fkt_t log)
{
assert(handle);
assert(log);
UINT leftBytes = size;
AAC_DECODER_ERROR err;
HANDLE_AACDECODER self = (HANDLE_AACDECODER)handle;
UCHAR* pBuffer[] = { data };
const UINT bufferSize[] = { size };
assert(handle);
assert(data || (size == 0));
err = aacDecoder_Fill(self, pBuffer, bufferSize, &leftBytes);
if (err != AAC_DEC_OK)
{
log(WLOG_ERROR, "aacDecoder_Fill failed with %s", dec_err_str(err));
return -1;
}
return leftBytes;
}
ssize_t fdk_aac_dsp_impl_stream_info(void* handle, int encoder, fdk_log_fkt_t log)
{
assert(handle);
assert(log);
if (encoder)
{
AACENC_InfoStruct info = { 0 };
HANDLE_AACENCODER self = (HANDLE_AACENCODER)handle;
AACENC_ERROR err = aacEncInfo(self, &info);
if (err != AAC_DEC_OK)
{
log(WLOG_ERROR, "aacEncInfo failed with %s", enc_err_str(err));
return -1;
}
return info.maxOutBufBytes;
}
else
{
HANDLE_AACDECODER self = (HANDLE_AACDECODER)handle;
CStreamInfo* info = aacDecoder_GetStreamInfo(self);
if (!info)
{
log(WLOG_ERROR, "aacDecoder_GetStreamInfo failed");
return -1;
}
return sizeof(INT_PCM) * info->numChannels * info->frameSize;
}
}
ssize_t fdk_aac_dsp_impl_encode(void* handle, const void* data, size_t size, void* dst,
size_t dstSize, fdk_log_fkt_t log)
{
AACENC_ERROR err;
INT inSizes[] = { size };
INT inElSizes[] = { sizeof(INT_PCM) };
INT inIdentifiers[] = { IN_AUDIO_DATA };
void* inBuffers[] = { data };
const AACENC_BufDesc inBufDesc = {
.numBufs = 1,
.bufs = inBuffers,
.bufferIdentifiers = inIdentifiers,
.bufSizes = inSizes,
.bufElSizes = inElSizes /* TODO: 8/16 bit input? */
};
INT outSizes[] = { dstSize };
INT outElSizes[] = { 1 };
INT outIdentifiers[] = { OUT_BITSTREAM_DATA };
void* outBuffers[] = { dst };
const AACENC_BufDesc outBufDesc = { .numBufs = 1,
.bufs = outBuffers,
.bufferIdentifiers = outIdentifiers,
.bufSizes = outSizes,
.bufElSizes = outElSizes };
const AACENC_InArgs inArgs = { .numInSamples =
size / sizeof(INT_PCM), /* TODO: 8/16 bit input? */
.numAncBytes = 0 };
AACENC_OutArgs outArgs = { 0 };
HANDLE_AACENCODER self = (HANDLE_AACENCODER)handle;
assert(handle);
assert(log);
err = aacEncEncode(self, &inBufDesc, &outBufDesc, &inArgs, &outArgs);
if (err != AACENC_OK)
{
log(WLOG_ERROR, "aacEncEncode failed with %s", enc_err_str(err));
return -1;
}
return outArgs.numOutBytes;
}

View File

@ -0,0 +1,45 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Digital Sound Processing
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_DSP_FDK_IMPL_H_
#define FREERDP_DSP_FDK_IMPL_H_
#include <stdlib.h>
typedef void (*fdk_log_fkt_t)(unsigned log_level, const char* fmt, ...);
int fdk_aac_dsp_impl_init(void** handle, int encoder, fdk_log_fkt_t log);
void fdk_aac_dsp_impl_uninit(void** handle, int encoder, fdk_log_fkt_t log);
ssize_t fdk_aac_dsp_impl_stream_info(void* handle, int encoder, fdk_log_fkt_t log);
int fdk_aac_dsp_impl_config(void* handle, size_t* pbuffersize, int encoder, unsigned samplerate,
unsigned channels, unsigned bytes_per_second,
unsigned frames_per_packet, fdk_log_fkt_t log);
ssize_t fdk_aac_dsp_impl_decode_fill(void* handle, const void* data, size_t size,
fdk_log_fkt_t log);
ssize_t fdk_aac_dsp_impl_encode(void* handle, const void* data, size_t size, void* dst,
size_t dstSize, fdk_log_fkt_t log);
ssize_t fdk_aac_dsp_impl_decode_read(void* handle, void* dst, size_t dstSize, fdk_log_fkt_t log);
#endif