From 51a1046316dbbd2dd60cc75dfe0489d3c87f366a Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 6 Jun 2023 10:16:52 +0200 Subject: [PATCH] added utils/macos common macOS utilities --- configure.ac | 2 +- src/audio/capture/coreaudio.m | 25 ++----- src/audio/playback/coreaudio.cpp | 25 ++++--- src/audio/playback/coreaudio.h | 6 -- src/utils/macos.h | 64 ++++++++++++++++ src/utils/macos.m | 120 ++++++++++++++++++++++++++++++ src/video_capture/avfoundation.mm | 1 - 7 files changed, 203 insertions(+), 40 deletions(-) create mode 100644 src/utils/macos.h create mode 100644 src/utils/macos.m diff --git a/configure.ac b/configure.ac index 72b8f1470..c553b52d0 100644 --- a/configure.ac +++ b/configure.ac @@ -156,7 +156,7 @@ if test $system = MacOSX; then CUDA_FLAGS="$CUDA_FLAGS -Xcompiler -Wno-error=unused-command-line-argument-hard-error-in-future" fi LDFLAGS="$LDFLAGS $COMMON_OSX_FLAGS -headerpad_max_install_names" - OBJS="$OBJS src/utils/autorelease_pool.o" + OBJS="$OBJS src/utils/autorelease_pool.o src/utils/macos.o" AC_SUBST(MACOS_LEGACY) fi diff --git a/src/audio/capture/coreaudio.m b/src/audio/capture/coreaudio.m index 9ceb5233e..98fb97219 100644 --- a/src/audio/capture/coreaudio.m +++ b/src/audio/capture/coreaudio.m @@ -55,27 +55,12 @@ #include "debug.h" #include "host.h" #include "lib_common.h" +#include "utils/macos.h" #include "utils/macros.h" #include "utils/ring_buffer.h" #define MOD_NAME "[CoreAudio] " -/** - * @todo - * Currntly only error number is returned. Improve to return textual representation - */ -const char *get_ca_error_str(OSStatus err) { - static _Thread_local char buf[128]; - if (IS_FCC(err)) { - uint32_t fcc_be = htonl(err); - snprintf(buf, sizeof buf, "'%.4s'", (char *) &fcc_be); - } else { - snprintf(buf, sizeof buf, "0x%x", err); - } - snprintf(buf + strlen(buf), sizeof buf - strlen(buf), " (OSStatus error %d)", err); - return buf; -} - struct state_ca_capture { #ifndef __MAC_10_6 ComponentInstance @@ -171,7 +156,7 @@ static OSStatus InputProc(void *inRefCon, pthread_cond_signal(&s->cv); pthread_mutex_unlock(&s->lock); } else { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "writing buffer caused error %s.\n", get_ca_error_str(err)); + log_msg(LOG_LEVEL_ERROR, MOD_NAME "writing buffer caused error %s.\n", get_osstatus_str(err)); } return err; @@ -199,7 +184,7 @@ static void audio_cap_ca_help() #define CA_STRINGIFY(A) #A #define CHECK_OK(cmd, msg, action_failed) do { OSStatus ret = cmd; if (ret != noErr) {\ - log_msg(strlen(CA_STRINGIFY(action_failed)) == 0 ? LOG_LEVEL_WARNING : LOG_LEVEL_ERROR, MOD_NAME "%s: %s\n", (msg), get_ca_error_str(ret));\ + log_msg(strlen(CA_STRINGIFY(action_failed)) == 0 ? LOG_LEVEL_WARNING : LOG_LEVEL_ERROR, MOD_NAME "%s: %s\n", (msg), get_osstatus_str(ret));\ action_failed;\ }\ } while(0) @@ -248,7 +233,7 @@ static void * audio_cap_ca_init(struct module *parent, const char *cfg) propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; propertyAddress.mElement = kAudioObjectPropertyElementMain; if ((ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &device)) != 0) { - log_msg(LOG_LEVEL_ERROR, "Error finding default input device: %s.\n", get_ca_error_str(ret)); + log_msg(LOG_LEVEL_ERROR, "Error finding default input device: %s.\n", get_osstatus_str(ret)); return NULL; } } @@ -273,7 +258,7 @@ static void * audio_cap_ca_init(struct module *parent, const char *cfg) propertyAddress.mElement = kAudioObjectPropertyElementMain; ret = AudioObjectGetPropertyData(device, &propertyAddress, 0, NULL, &size, &rate); if (ret != noErr || rate == 0.0) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to get sample rate: %s. Wrong device index?\n", get_ca_error_str(ret)); + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to get sample rate: %s. Wrong device index?\n", get_osstatus_str(ret)); return NULL; } if (audio_capture_sample_rate != 0 && audio_capture_sample_rate != rate) { diff --git a/src/audio/playback/coreaudio.cpp b/src/audio/playback/coreaudio.cpp index fbf62ad88..52881e33e 100644 --- a/src/audio/playback/coreaudio.cpp +++ b/src/audio/playback/coreaudio.cpp @@ -62,8 +62,9 @@ #include "debug.h" #include "lib_common.h" #include "rang.hpp" -#include "utils/ring_buffer.h" #include "utils/audio_buffer.h" +#include "utils/macos.h" +#include "utils/ring_buffer.h" using namespace std::chrono; using rang::fg; @@ -168,13 +169,13 @@ static int audio_play_ca_reconfigure(void *state, struct audio_desc desc) if (s->initialized) { ret = AudioOutputUnitStop(s->auHALComponentInstance); if(ret) { - LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot stop AUHAL instance: " << get_ca_error_str(ret) << ".\n"; + LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot stop AUHAL instance: " << get_osstatus_str(ret) << ".\n"; goto error; } ret = AudioUnitUninitialize(s->auHALComponentInstance); if(ret) { - LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot uninitialize AUHAL instance: " << get_ca_error_str(ret) << ".\n"; + LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot uninitialize AUHAL instance: " << get_osstatus_str(ret) << ".\n"; goto error; } s->initialized = false; @@ -206,7 +207,7 @@ static int audio_play_ca_reconfigure(void *state, struct audio_desc desc) ret = AudioUnitGetProperty(s->auHALComponentInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &stream_desc, &size); if(ret) { - LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot get device format from AUHAL instance: " << get_ca_error_str(ret) << ".\n"; + LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot get device format from AUHAL instance: " << get_osstatus_str(ret) << ".\n"; goto error; } stream_desc.mSampleRate = desc.sample_rate; @@ -220,7 +221,7 @@ static int audio_play_ca_reconfigure(void *state, struct audio_desc desc) ret = AudioUnitSetProperty(s->auHALComponentInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc)); if(ret) { - LOG(LOG_LEVEL_ERROR) << "Cannot set device format to AUHAL instance: " << get_ca_error_str(ret) << ".\n"; + LOG(LOG_LEVEL_ERROR) << "Cannot set device format to AUHAL instance: " << get_osstatus_str(ret) << ".\n"; goto error; } @@ -229,19 +230,19 @@ static int audio_play_ca_reconfigure(void *state, struct audio_desc desc) ret = AudioUnitSetProperty(s->auHALComponentInstance, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderStruct, sizeof(AURenderCallbackStruct)); if(ret) { - LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot register audio processing callback: " << get_ca_error_str(ret) << ".\n"; + LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot register audio processing callback: " << get_osstatus_str(ret) << ".\n"; goto error; } ret = AudioUnitInitialize(s->auHALComponentInstance); if(ret) { - LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot initialize AUHAL: " << get_ca_error_str(ret) << ".\n"; + LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot initialize AUHAL: " << get_osstatus_str(ret) << ".\n"; goto error; } ret = AudioOutputUnitStart(s->auHALComponentInstance); if(ret) { - LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot start AUHAL: " << get_ca_error_str(ret) << ".\n"; + LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot start AUHAL: " << get_osstatus_str(ret) << ".\n"; goto error; } @@ -411,14 +412,14 @@ static void * audio_play_ca_init(const char *cfg) ret = AudioComponentInstanceNew(comp, &s->auHALComponentInstance); #endif if (ret != noErr) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot open audio component: %s\n", get_ca_error_str(ret)); + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot open audio component: %s\n", get_osstatus_str(ret)); goto error; } s->buffer = NULL; if ((ret = AudioUnitUninitialize(s->auHALComponentInstance)) != noErr) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot initialize audio unit: %s\n", get_ca_error_str(ret)); + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot initialize audio unit: %s\n", get_osstatus_str(ret)); goto error; } @@ -435,7 +436,7 @@ static void * audio_play_ca_init(const char *cfg) propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; propertyAddress.mElement = kAudioObjectPropertyElementMain; if ((ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &device)) != noErr) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot get default audio device: %s\n", get_ca_error_str(ret)); + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot get default audio device: %s\n", get_osstatus_str(ret)); goto error; } } @@ -454,7 +455,7 @@ static void * audio_play_ca_init(const char *cfg) &device, sizeof(device)); if (ret) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot set audio properties: %s\n", get_ca_error_str(ret)); + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot set audio properties: %s\n", get_osstatus_str(ret)); goto error; } diff --git a/src/audio/playback/coreaudio.h b/src/audio/playback/coreaudio.h index 64e5a65d2..0acb9b082 100644 --- a/src/audio/playback/coreaudio.h +++ b/src/audio/playback/coreaudio.h @@ -46,16 +46,10 @@ struct device_info; * @param dir -1 capture; 1 playback */ void audio_ca_probe(struct device_info **available_devices, int *count, int dir); -const char *get_ca_error_str(OSStatus err); #ifdef __cplusplus } #endif -#include -#ifndef __MAC_12_0 -#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster -#endif - /* vim: set expandtab sw=8: */ diff --git a/src/utils/macos.h b/src/utils/macos.h new file mode 100644 index 000000000..cc830bdcc --- /dev/null +++ b/src/utils/macos.h @@ -0,0 +1,64 @@ +/** + * @file utils/macos.h + * @author Martin Pulec + */ +/* + * Copyright (c) 2023 CESNET, z. s. p. o. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UTILS_MACOS_H_7D98CC2C_24D4_4DC7_8F07_007A55E4B120 +#define UTILS_MACOS_H_7D98CC2C_24D4_4DC7_8F07_007A55E4B120 + +#include +#include + +// prefer over __MAC__ because those may not be defined in older +// SDKs (https://gist.github.com/torarnv/4967079) and would be evaluated as 0 +#define MACOS_VERSION(major, minor) ((major) * 10000 + (minor) * 100) + +// compat +#if __MAC_OS_X_VERSION_MIN_REQUIRED < MACOS_VERSION(12, 0) +#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +const char *get_osstatus_str(OSStatus err); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // defined UTILS_MACOS_H_7D98CC2C_24D4_4DC7_8F07_007A55E4B120 + diff --git a/src/utils/macos.m b/src/utils/macos.m new file mode 100644 index 000000000..2ac03cd06 --- /dev/null +++ b/src/utils/macos.m @@ -0,0 +1,120 @@ +/** + * @file utils/macos.m + * @author Martin Pulec + */ +/* + * Copyright (c) 2023 CESNET, z. s. p. o. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#include "config_unix.h" +#include "config_win32.h" +#endif + +#include + +#include "utils/macos.h" +#include "utils/macros.h" + +static const char *get_mapped_osstatus_str(OSStatus err) { + static const struct osstatus_map { + OSStatus err; + const char *msg; + } osstatus_map[] = { + { 0, "noErr" }, + // AUComponent.h + { -10879, "kAudioUnitErr_InvalidProperty" }, + { -10878, "kAudioUnitErr_InvalidParameter" }, + { -10877, "kAudioUnitErr_InvalidElement" }, + { -10876, "kAudioUnitErr_NoConnection" }, + { -10875, "kAudioUnitErr_FailedInitialization" }, + { -10874, "kAudioUnitErr_TooManyFramesToProcess" }, + { -10871, "kAudioUnitErr_InvalidFile" }, + { -10870, "kAudioUnitErr_UnknownFileType" }, + { -10869, "kAudioUnitErr_FileNotSpecified" }, + { -10868, "kAudioUnitErr_FormatNotSupported" }, + { -10867, "kAudioUnitErr_Uninitialized" }, + { -10866, "kAudioUnitErr_InvalidScope" }, + { -10865, "kAudioUnitErr_PropertyNotWritable" }, + { -10863, "kAudioUnitErr_CannotDoInCurrentContext" }, + { -10851, "kAudioUnitErr_InvalidPropertyValue" }, + { -10850, "kAudioUnitErr_PropertyNotInUse" }, + { -10849, "kAudioUnitErr_Initialized" }, + { -10848, "kAudioUnitErr_InvalidOfflineRender" }, + { -10847, "kAudioUnitErr_Unauthorized" }, + { -66753, "kAudioUnitErr_MIDIOutputBufferFull" }, + { -66754, "kAudioComponentErr_InstanceTimedOut" }, + { -66749, "kAudioComponentErr_InstanceInvalidated" }, + { -66745, "kAudioUnitErr_RenderTimeout" }, + { -66744, "kAudioUnitErr_ExtensionNotFound" }, + { -66743, "kAudioUnitErr_InvalidParameterValue" }, + { -66742, "kAudioUnitErr_InvalidFilePath" }, + { -66741, "kAudioUnitErr_MissingKey" }, + // AudioHardwareBase.h + { 'stop', "kAudioHardwareNotRunningError" }, + { 'what', "kAudioHardwareUnspecifiedError" }, + { 'who?', "kAudioHardwareUnknownPropertyError" }, + { '!siz', "kAudioHardwareBadPropertySizeError" }, + { 'nope', "kAudioHardwareIllegalOperationError" }, + { '!obj', "kAudioHardwareBadObjectError" }, + { '!dev', "kAudioHardwareBadDeviceError" }, + { '!str', "kAudioHardwareBadStreamError" }, + { 'unop', "kAudioHardwareUnsupportedOperationError" }, + { 'nrdy', "kAudioHardwareNotReadyError" }, + { '!dat', "kAudioDeviceUnsupportedFormatError" }, + { '!hog', "kAudioDevicePermissionsError" }, + }; + + for (unsigned i = 0; i < sizeof osstatus_map / sizeof osstatus_map[0]; ++i) { + if (osstatus_map[i].err == err) { + return osstatus_map[i].msg; + } + } + return NULL; +} + +const char *get_osstatus_str(OSStatus err) { + static _Thread_local char buf[128]; + const char *mapped_err = get_mapped_osstatus_str(err); + if (mapped_err != NULL) { + snprintf(buf, sizeof buf, "%s", mapped_err); + } else if (IS_FCC(err)) { + uint32_t fcc_be = htonl(err); + snprintf(buf, sizeof buf, "'%.4s'", (char *) &fcc_be); + } else { + snprintf(buf, sizeof buf, "0x%x", err); + } + snprintf(buf + strlen(buf), sizeof buf - strlen(buf), " (OSStatus error %d)", err); + return buf; +} + diff --git a/src/video_capture/avfoundation.mm b/src/video_capture/avfoundation.mm index c2dc4ee37..874226163 100644 --- a/src/video_capture/avfoundation.mm +++ b/src/video_capture/avfoundation.mm @@ -62,7 +62,6 @@ #define NSAppKitVersionNumber10_8 1187 #define NSAppKitVersionNumber10_9 1265 -// see also https://gist.github.com/torarnv/4967079 #if defined __MAC_13_0 && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_13_0 #define DESK_VIEW_IF_DEFINED ,AVCaptureDeviceTypeDeskViewCamera #else