diff --git a/CMakeLists.txt b/CMakeLists.txt index 306354f7b..1d7a541a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -770,6 +770,10 @@ set(FAAC_FEATURE_TYPE "OPTIONAL") set(FAAC_FEATURE_PURPOSE "codec") set(FAAC_FEATURE_DESCRIPTION "FAAC AAC audio codec library") +set(SOXR_FEATURE_TYPE "OPTIONAL") +set(SOXR_FEATURE_PURPOSE "codec") +set(SOXR_FEATURE_DESCRIPTION "SOX audio resample library") + set(GSSAPI_FEATURE_TYPE "OPTIONAL") set(GSSAPI_FEATURE_PURPOSE "auth") set(GSSAPI_FEATURE_DESCRIPTION "add kerberos support") @@ -873,6 +877,7 @@ find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRI find_feature(LAME ${LAME_FEATURE_TYPE} ${LAME_FEATURE_PURPOSE} ${LAME_FEATURE_DESCRIPTION}) find_feature(FAAD2 ${FAAD2_FEATURE_TYPE} ${FAAD2_FEATURE_PURPOSE} ${FAAD2_FEATURE_DESCRIPTION}) find_feature(FAAC ${FAAC_FEATURE_TYPE} ${FAAC_FEATURE_PURPOSE} ${FAAC_FEATURE_DESCRIPTION}) +find_feature(soxr ${SOXR_FEATURE_TYPE} ${SOXR_FEATURE_PURPOSE} ${SOXR_FEATURE_DESCRIPTION}) find_feature(GSSAPI ${GSSAPI_FEATURE_TYPE} ${GSSAPI_FEATURE_PURPOSE} ${GSSAPI_FEATURE_DESCRIPTION}) diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c index 8b86830c1..b7ce33042 100644 --- a/channels/audin/client/audin_main.c +++ b/channels/audin/client/audin_main.c @@ -341,6 +341,7 @@ static UINT audin_receive_wave_data(const AUDIO_FORMAT* format, const BYTE* data, size_t size, void* user_data) { UINT error; + BOOL compatible; AUDIN_PLUGIN* audin; AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data; @@ -362,7 +363,8 @@ static UINT audin_receive_wave_data(const AUDIO_FORMAT* format, Stream_Write_UINT8(audin->data, MSG_SNDIN_DATA); - if (audin->device->FormatSupported(audin->device, audin->format)) + compatible = audio_format_compatible(format, audin->format); + if (compatible && audin->device->FormatSupported(audin->device, audin->format)) { if (!Stream_EnsureRemainingCapacity(audin->data, size)) return CHANNEL_RC_NO_MEMORY; @@ -408,8 +410,31 @@ static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callb if (!supported) { + /* Default sample rates supported by most backends. */ + const UINT32 samplerates[] = { + 96000, + 48000, + 44100, + 22050 + }; + BOOL test = FALSE; + format.wFormatTag = WAVE_FORMAT_PCM; format.wBitsPerSample = 16; + test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format); + if (!test) + { + size_t x; + for (x=0; xdevice->FormatSupported, audin->device, &format); + if (test) + break; + } + } + if (!test) + return FALSE; } IFCALLRET(audin->device->SetFormat, error, diff --git a/channels/audin/client/pulse/audin_pulse.c b/channels/audin/client/pulse/audin_pulse.c index 3a5ff734f..d22de87d2 100644 --- a/channels/audin/client/pulse/audin_pulse.c +++ b/channels/audin/client/pulse/audin_pulse.c @@ -306,7 +306,7 @@ static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length AudinPulseDevice* pulse = (AudinPulseDevice*) userdata; UINT error = CHANNEL_RC_OK; pa_stream_peek(stream, &data, &length); - error = pulse->receive(&pulse->format, data, length, pulse->user_data); + error = IFCALLRESULT(CHANNEL_RC_OK, pulse->receive, &pulse->format, data, length, pulse->user_data); pa_stream_drop(stream); if (error && pulse->rdpcontext) diff --git a/channels/rdpsnd/server/rdpsnd_main.c b/channels/rdpsnd/server/rdpsnd_main.c index 4be0b3078..53d4d5c32 100644 --- a/channels/rdpsnd/server/rdpsnd_main.c +++ b/channels/rdpsnd/server/rdpsnd_main.c @@ -301,8 +301,7 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context, AUDIO_FORMAT* format; UINT error = CHANNEL_RC_OK; - if ((client_format_index < 0) - || (client_format_index >= context->num_client_formats) + if ((client_format_index >= context->num_client_formats) || (!context->src_format)) { WLog_ERR(TAG, "index %d is not correct.", client_format_index); @@ -415,6 +414,10 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, ULONG written; wStream* s = context->priv->rdpsnd_pdu; UINT error = CHANNEL_RC_OK; + + if (context->selected_client_format >= context->num_client_formats) + return ERROR_INTERNAL_ERROR; + format = &context->client_formats[context->selected_client_format]; /* WaveInfo PDU */ Stream_SetPosition(s, 0); @@ -429,7 +432,7 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, src = context->priv->out_buffer; length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame; - if (!freerdp_dsp_encode(context->priv->dsp_context, format, src, length, s)) + if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s)) return ERROR_INTERNAL_ERROR; else { @@ -491,6 +494,10 @@ static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, ULONG written; wStream* s = context->priv->rdpsnd_pdu; UINT error = CHANNEL_RC_OK; + + if (context->selected_client_format >= context->num_client_formats) + return ERROR_INTERNAL_ERROR; + format = &context->client_formats[context->selected_client_format]; /* WaveInfo PDU */ Stream_SetPosition(s, 0); @@ -501,11 +508,11 @@ static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, Stream_Write_UINT16(s, context->selected_client_format); /* wFormatNo */ Stream_Write_UINT8(s, context->block_no); /* cBlockNo */ Stream_Seek(s, 3); /* bPad */ - Stream_Write_UINT16(s, wTimestamp); /* dwAudioTimeStamp */ + Stream_Write_UINT32(s, wTimestamp); /* dwAudioTimeStamp */ src = context->priv->out_buffer; length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame; - if (!freerdp_dsp_encode(context->priv->dsp_context, format, src, length, s)) + if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s)) error = ERROR_INTERNAL_ERROR; else { @@ -558,7 +565,7 @@ static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, UINT error = CHANNEL_RC_OK; EnterCriticalSection(&context->priv->lock); - if (context->selected_client_format < 0) + if (context->selected_client_format >= context->num_client_formats) { /* It's possible while format negotiation has not been done */ WLog_WARN(TAG, "Drop samples because client format has not been negotiated."); @@ -636,7 +643,7 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context) if (context->priv->out_pending_frames > 0) { - if (context->selected_client_format < 0) + if (context->selected_client_format >= context->num_client_formats) { WLog_ERR(TAG, "Pending audio frame exists while no format selected."); error = ERROR_INVALID_DATA; @@ -652,7 +659,7 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context) if (error) return error; - context->selected_client_format = -1; + context->selected_client_format = 0xFFFF; Stream_Write_UINT8(s, SNDC_CLOSE); Stream_Write_UINT8(s, 0); Stream_Seek_UINT16(s); @@ -807,7 +814,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) context->vcm = vcm; context->Start = rdpsnd_server_start; context->Stop = rdpsnd_server_stop; - context->selected_client_format = -1; + context->selected_client_format = 0xFFFF; context->Initialize = rdpsnd_server_initialize; context->SelectFormat = rdpsnd_server_select_format; context->SendSamples = rdpsnd_server_send_samples; diff --git a/cmake/Findsoxr.cmake b/cmake/Findsoxr.cmake new file mode 100644 index 000000000..8a19946cb --- /dev/null +++ b/cmake/Findsoxr.cmake @@ -0,0 +1,62 @@ +# Try to find the soxr library +# +# Copyright 2018 Thincast Technologies GmbH +# Copyright 2018 Armin Novak +# +# 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 +# +# Once done this will define +# +# SOXR_ROOT - A list of search hints +# +# SOXR_FOUND - system has soxr +# SOXR_INCLUDE_DIR - the soxr include directory +# SOXR_LIBRARIES - libsoxr library + +if (UNIX AND NOT ANDROID) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_SOXR QUIET soxr) +endif (UNIX AND NOT ANDROID) + +if (SOXR_INCLUDE_DIR AND SOXR_LIBRARY) + set(SOXR_FIND_QUIETLY TRUE) +endif (SOXR_INCLUDE_DIR AND SOXR_LIBRARY) + +find_path(SOXR_INCLUDE_DIR NAMES soxr.h + PATH_SUFFIXES include + HINTS ${SOXR_ROOT} ${PC_SOXR_INCLUDE_DIRS}) +find_library(SOXR_LIBRARY + NAMES soxr + PATH_SUFFIXES lib + HINTS ${SOXR_ROOT} ${PC_SOXR_LIBRARY_DIRS}) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(soxr DEFAULT_MSG SOXR_LIBRARY SOXR_INCLUDE_DIR) + +if (SOXR_INCLUDE_DIR AND SOXR_LIBRARY) + set(SOXR_FOUND TRUE) + set(SOXR_INCLUDE_DIRS ${SOXR_INCLUDE_DIR}) + set(SOXR_LIBRARIES ${SOXR_LIBRARY}) +endif (SOXR_INCLUDE_DIR AND SOXR_LIBRARY) + +if (SOXR_FOUND) + if (NOT SOXR_FIND_QUIETLY) + message(STATUS "Found soxr: ${SOXR_LIBRARIES}") + endif (NOT SOXR_FIND_QUIETLY) +else (SOXR_FOUND) + if (SOXR_FIND_REQUIRED) + message(FATAL_ERROR "soxr was not found") + endif(SOXR_FIND_REQUIRED) +endif (SOXR_FOUND) + +mark_as_advanced(SOXR_INCLUDE_DIR SOXR_LIBRARY) + diff --git a/config.h.in b/config.h.in index 26bc059f7..92f447e6b 100644 --- a/config.h.in +++ b/config.h.in @@ -57,6 +57,7 @@ #cmakedefine WITH_LAME #cmakedefine WITH_FAAD2 #cmakedefine WITH_FAAC +#cmakedefine WITH_SOXR #cmakedefine WITH_GFX_H264 #cmakedefine WITH_OPENH264 #cmakedefine WITH_FFMPEG diff --git a/libfreerdp/CMakeLists.txt b/libfreerdp/CMakeLists.txt index e71a180a0..fe05a79ef 100644 --- a/libfreerdp/CMakeLists.txt +++ b/libfreerdp/CMakeLists.txt @@ -162,6 +162,11 @@ if (WITH_DSP_FFMPEG) codec/dsp_ffmpeg.h) endif (WITH_DSP_FFMPEG) +if (WITH_SOXR) + freerdp_library_add(${SOXR_LIBRARIES}) + include_directories(${SOXR_INCLUDE_DIR}) +endif(WITH_SOXR) + if(GSM_FOUND) freerdp_library_add(${GSM_LIBRARIES}) include_directories(${GSM_INCLUDE_DIRS}) diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c index 5dac569d0..4e83791d7 100644 --- a/libfreerdp/codec/dsp.c +++ b/libfreerdp/codec/dsp.c @@ -21,6 +21,7 @@ #include "config.h" #endif +#include #include #include #include @@ -28,8 +29,10 @@ #include #include +#include #include +#if !defined(WITH_DSP_FFMPEG) #if defined(WITH_GSM) #include #endif @@ -38,10 +41,6 @@ #include #endif -#if defined(WITH_DSP_FFMPEG) -#include "dsp_ffmpeg.h" -#endif - #if defined(WITH_FAAD2) #include #endif @@ -50,8 +49,18 @@ #include #endif +#if defined(WITH_SOXR) +#include +#endif + +#else +#include "dsp_ffmpeg.h" +#endif + #define TAG FREERDP_TAG("dsp") +#if !defined(WITH_DSP_FFMPEG) + union _ADPCM { struct @@ -96,6 +105,10 @@ struct _FREERDP_DSP_CONTEXT unsigned long faacInputSamples; unsigned long faacMaxOutputBytes; #endif + +#if defined(WITH_SOXR) + soxr_t sox; +#endif }; /** @@ -104,47 +117,64 @@ struct _FREERDP_DSP_CONTEXT */ static BOOL freerdp_dsp_resample(FREERDP_DSP_CONTEXT* context, - const BYTE* src, size_t bytes_per_sample, - UINT32 schan, UINT32 srate, size_t sframes, - UINT32 rchan, UINT32 rrate) + const BYTE* src, size_t size, + const AUDIO_FORMAT* srcFormat, + const BYTE** data, size_t* length) { - BYTE* p; - int rframes; - int rsize; - int i, j; - int n1, n2; - int sbytes, rbytes; - sbytes = bytes_per_sample * schan; - rbytes = bytes_per_sample * rchan; - rframes = sframes * rrate / srate; - rsize = rbytes * rframes; +#if defined(WITH_SOXR) + soxr_error_t error; + size_t idone, odone; +#endif + size_t sframes, rframes; + size_t rsize; + size_t j; + size_t sbytes, rbytes; + size_t srcBytesPerFrame, dstBytesPerFrame; + size_t srcChannels, dstChannels; + AUDIO_FORMAT format; - if (!Stream_EnsureCapacity(context->resample, rsize + 1024)) - return FALSE; - - p = Stream_Buffer(context->resample); - - for (i = 0; i < rframes; i++) + if (srcFormat->wFormatTag != WAVE_FORMAT_PCM) { - n1 = i * srate / rrate; - - if (n1 >= sframes) - n1 = sframes - 1; - - n2 = (n1 * rrate == i * srate || n1 == sframes - 1 ? n1 : n1 + 1); - - for (j = 0; j < rbytes; j++) - { - /* Nearest Interpolation, probably the easiest, but works */ - *p++ = (i * srate - n1 * rrate > n2 * rrate - i * srate ? - src[n2 * sbytes + (j % sbytes)] : - src[n1 * sbytes + (j % sbytes)]); - } + WLog_ERR(TAG, "%s requires %s for sample input, got %s", __FUNCTION__, + audio_format_get_tag_string(WAVE_FORMAT_PCM), + audio_format_get_tag_string(srcFormat->wFormatTag)); + return FALSE; } - Stream_SetPointer(context->resample, p); - Stream_SealLength(context->resample); - return TRUE; + srcChannels = srcFormat->nChannels; + dstChannels = context->format.nChannels; + srcBytesPerFrame = (srcFormat->wBitsPerSample > 8) ? 2 : 1; + dstBytesPerFrame = (context->format.wBitsPerSample > 8) ? 2 : 1; + /* We want to ignore differences of source and destination format. */ + format = *srcFormat; + format.wFormatTag = WAVE_FORMAT_UNKNOWN; + + if (audio_format_compatible(&format, &context->format)) + return TRUE; + +#if defined(WITH_SOXR) + sbytes = srcChannels * srcBytesPerFrame; + sframes = size / sbytes; + rbytes = dstBytesPerFrame * dstChannels; + /* Integer rounding correct division */ + rframes = (sframes * context->format.nSamplesPerSec + (srcFormat->nSamplesPerSec + 1) / 2) / + srcFormat->nSamplesPerSec; + rsize = rframes * rbytes; + + if (!Stream_EnsureCapacity(context->resample, rsize)) + return FALSE; + + error = soxr_process(context->sox, src, sframes, &idone, + Stream_Buffer(context->resample), + Stream_Capacity(context->resample) / rbytes, &odone); + Stream_SetLength(context->resample, odone * rbytes); + *data = Stream_Buffer(context->resample); + *length = Stream_Length(context->resample); + return (error == 0) ? TRUE : FALSE; +#else + WLog_ERR(TAG, "Missing resample support, recompile -DWITH_SOXR=ON or -DWITH_DSP_FFMPEG=ON"); + return FALSE; +#endif } /** @@ -411,16 +441,18 @@ static BOOL freerdp_dsp_encode_faac(FREERDP_DSP_CONTEXT* context, { int16_t* inSamples = (int16_t*)src; int32_t* outSamples; + unsigned int bpp; unsigned int nrSamples, x; int rc; if (!context || !src || !out) return FALSE; - nrSamples = size / context->format.nChannels / context->format.wBitsPerSample / 8; + bpp = context->format.wBitsPerSample / 8 * context->format.nChannels; + nrSamples = size / bpp; if (!Stream_EnsureCapacity(context->buffer, - context->faacInputSamples * sizeof(int32_t) * context->format.nChannels)) + nrSamples * sizeof(int32_t) * context->format.nChannels)) return FALSE; if (!Stream_EnsureRemainingCapacity(out, context->faacMaxOutputBytes)) @@ -911,6 +943,8 @@ static BOOL freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE return TRUE; } +#endif + FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(BOOL encoder) { #if defined(WITH_DSP_FFMPEG) @@ -1015,6 +1049,9 @@ void freerdp_dsp_context_free(FREERDP_DSP_CONTEXT* context) if (context->faac) faacEncClose(context->faac); +#endif +#if defined(WITH_SOXR) + soxr_delete(context->sox); #endif free(context); } @@ -1032,7 +1069,8 @@ BOOL freerdp_dsp_encode(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* srcFor if (!context || !context->encoder || !srcFormat || !data || !out) return FALSE; - // TODO: Resample + if (!freerdp_dsp_resample(context, data, length, srcFormat, &data, &length)) + return FALSE; switch (context->format.wFormatTag) { @@ -1129,9 +1167,13 @@ BOOL freerdp_dsp_supports_format(const AUDIO_FORMAT* format, BOOL encode) switch (format->wFormatTag) { case WAVE_FORMAT_PCM: + return TRUE; +#if defined(WITH_DSP_EXPERIMENTAL) + case WAVE_FORMAT_ADPCM: case WAVE_FORMAT_DVI_ADPCM: return TRUE; +#endif #if defined(WITH_GSM) case WAVE_FORMAT_GSM610: @@ -1172,7 +1214,6 @@ BOOL freerdp_dsp_supports_format(const AUDIO_FORMAT* format, BOOL encode) #endif } - BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* targetFormat) { #if defined(WITH_DSP_FFMPEG) @@ -1206,6 +1247,18 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* faacEncSetConfiguration(context->faac, cfg); } +#endif +#if defined(WITH_SOXR) + { + soxr_io_spec_t iospec = soxr_io_spec(SOXR_INT16, SOXR_INT16); + soxr_error_t error; + soxr_delete(context->sox); + context->sox = soxr_create(context->format.nSamplesPerSec, targetFormat->nSamplesPerSec, + targetFormat->nChannels, &error, &iospec, NULL, NULL); + + if (!context->sox || (error != 0)) + return FALSE; + } #endif return TRUE; #endif