From 330f7ae0a2b3b2a1ebdcad28669aa4e0a40b96f7 Mon Sep 17 00:00:00 2001 From: Pascal Nowack Date: Sat, 18 Nov 2023 11:43:21 +0100 Subject: [PATCH] codec/dsp: Add support for decoding Opus encoded streams The Opus codec is a modern free audio codec, that is also royalty-free. Adding support for it will allow clients and servers supporting it to transfer audio in similar efficient way like with AAC. So, add support it. --- include/config/config.h.in | 1 + libfreerdp/CMakeLists.txt | 5 +++ libfreerdp/codec/dsp.c | 69 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/include/config/config.h.in b/include/config/config.h.in index 8a6e836cd..eae198610 100644 --- a/include/config/config.h.in +++ b/include/config/config.h.in @@ -34,6 +34,7 @@ #cmakedefine WITH_OPENSLES #cmakedefine WITH_GSM #cmakedefine WITH_LAME +#cmakedefine WITH_OPUS #cmakedefine WITH_FAAD2 #cmakedefine WITH_FAAC #cmakedefine WITH_SOXR diff --git a/libfreerdp/CMakeLists.txt b/libfreerdp/CMakeLists.txt index 74b47ae9c..303427e68 100644 --- a/libfreerdp/CMakeLists.txt +++ b/libfreerdp/CMakeLists.txt @@ -216,6 +216,11 @@ if(LAME_FOUND) include_directories(${LAME_INCLUDE_DIRS}) endif() +if(OPUS_FOUND) + freerdp_library_add(${OPUS_LIBRARIES}) + include_directories(${OPUS_INCLUDE_DIRS}) +endif() + if(FAAD2_FOUND) freerdp_library_add(${FAAD2_LIBRARIES}) include_directories(${FAAD2_INCLUDE_DIRS}) diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c index 8b7d8dea3..c91b89ff1 100644 --- a/libfreerdp/codec/dsp.c +++ b/libfreerdp/codec/dsp.c @@ -39,6 +39,12 @@ #include #endif +#if defined(WITH_OPUS) +#include + +#define OPUS_MAX_FRAMES 5760 +#endif + #if defined(WITH_FAAD2) #include #endif @@ -94,6 +100,9 @@ struct S_FREERDP_DSP_CONTEXT lame_t lame; hip_t hip; #endif +#if defined(WITH_OPUS) + OpusDecoder* opus_decoder; +#endif #if defined(WITH_FAAD2) NeAACDecHandle faad; BOOL faadSetup; @@ -565,6 +574,31 @@ static BOOL freerdp_dsp_encode_faac(FREERDP_DSP_CONTEXT* context, const BYTE* sr } #endif +#if defined(WITH_OPUS) +static BOOL freerdp_dsp_decode_opus(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size, + wStream* out) +{ + size_t max_size = 5760; + int frames; + + if (!context || !src || !out) + return FALSE; + + /* Max packet duration is 120ms (5760 at 48KHz) */ + max_size = OPUS_MAX_FRAMES * context->format.nChannels * sizeof(int16_t); + if (!Stream_EnsureRemainingCapacity(context->buffer, max_size)) + return FALSE; + + frames = opus_decode(context->opus_decoder, src, size, Stream_Pointer(out), OPUS_MAX_FRAMES, 0); + if (frames < 0) + return FALSE; + + Stream_Seek(out, frames * context->format.nChannels * sizeof(int16_t)); + + return TRUE; +} +#endif + #if defined(WITH_FAAD2) static BOOL freerdp_dsp_decode_faad(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size, wStream* out) @@ -1121,6 +1155,12 @@ void freerdp_dsp_context_free(FREERDP_DSP_CONTEXT* context) else hip_decode_exit(context->hip); +#endif +#if defined(WITH_OPUS) + + if (context->opus_decoder) + opus_decoder_destroy(context->opus_decoder); + #endif #if defined(WITH_FAAD2) @@ -1207,6 +1247,11 @@ BOOL freerdp_dsp_encode(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* srcFor BOOL freerdp_dsp_decode(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* srcFormat, const BYTE* data, size_t length, wStream* out) { +#if defined(WITH_OPUS) + if (context->format.wFormatTag == WAVE_FORMAT_OPUS) + return freerdp_dsp_decode_opus(context, data, length, out); +#endif + #if defined(WITH_DSP_FFMPEG) return freerdp_dsp_ffmpeg_decode(context, srcFormat, data, length, out); #else @@ -1254,6 +1299,14 @@ BOOL freerdp_dsp_decode(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* srcFor BOOL freerdp_dsp_supports_format(const AUDIO_FORMAT* format, BOOL encode) { +#if defined(WITH_OPUS) + if (format->wFormatTag == WAVE_FORMAT_OPUS && !encode && + (format->nSamplesPerSec == 8000 || format->nSamplesPerSec == 12000 || + format->nSamplesPerSec == 16000 || format->nSamplesPerSec == 24000 || + format->nSamplesPerSec == 48000)) + return TRUE; +#endif + #if defined(WITH_DSP_FFMPEG) return freerdp_dsp_ffmpeg_supports_format(format, encode); #else @@ -1339,6 +1392,22 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* Stream_SetPosition(context->buffer, 0); } +#if defined(WITH_OPUS) + + if (!context->encoder && + (context->format.nSamplesPerSec == 8000 || context->format.nSamplesPerSec == 12000 || + context->format.nSamplesPerSec == 16000 || context->format.nSamplesPerSec == 24000 || + context->format.nSamplesPerSec == 48000)) + { + int opus_error = OPUS_OK; + + context->opus_decoder = opus_decoder_create(context->format.nSamplesPerSec, + context->format.nChannels, &opus_error); + if (opus_error != OPUS_OK) + return FALSE; + } + +#endif #if defined(WITH_FAAD2) context->faadSetup = FALSE; #endif