/* * This file is part of mpv. * * mpv 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. * * mpv 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 mpv. If not, see . */ /* * This file contains functions interacting with the CoreAudio framework * that are not specific to the AUHAL. These are split in a separate file for * the sake of readability. In the future the could be used by other AOs based * on CoreAudio but not the AUHAL (such as using AudioQueue services). */ #include "audio/out/ao_coreaudio_utils.h" #include "audio/out/ao_coreaudio_properties.h" #include "osdep/timer.h" #include "osdep/endian.h" #include "audio/format.h" void ca_print_device_list(struct ao *ao) { char *help = talloc_strdup(NULL, "Available output devices:\n"); AudioDeviceID *devs; size_t n_devs; OSStatus err = CA_GET_ARY(kAudioObjectSystemObject, kAudioHardwarePropertyDevices, &devs, &n_devs); CHECK_CA_ERROR("Failed to get list of output devices."); for (int i = 0; i < n_devs; i++) { char *name; err = CA_GET_STR(devs[i], kAudioObjectPropertyName, &name); if (err == noErr) talloc_steal(devs, name); else name = "Unknown"; help = talloc_asprintf_append( help, " * %s (id: %" PRIu32 ")\n", name, devs[i]); } talloc_free(devs); coreaudio_error: MP_INFO(ao, "%s", help); talloc_free(help); } OSStatus ca_select_device(struct ao *ao, int selection, AudioDeviceID *device) { OSStatus err = noErr; *device = 0; if (selection < 0) { // device not set by user, get the default one err = CA_GET(kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice, device); CHECK_CA_ERROR("could not get default audio device"); } else { *device = selection; } if (mp_msg_test(ao->log, MSGL_V)) { char *name; err = CA_GET_STR(*device, kAudioObjectPropertyName, &name); CHECK_CA_ERROR("could not get selected audio device name"); MP_VERBOSE(ao, "selected audio output device: %s (%" PRIu32 ")\n", name, *device); talloc_free(name); } coreaudio_error: return err; } char *fourcc_repr(void *talloc_ctx, uint32_t code) { // Extract FourCC letters from the uint32_t and finde out if it's a valid // code that is made of letters. unsigned char fcc[4] = { (code >> 24) & 0xFF, (code >> 16) & 0xFF, (code >> 8) & 0xFF, code & 0xFF, }; bool valid_fourcc = true; for (int i = 0; i < 4; i++) if (fcc[i] >= 32 && fcc[i] < 128) valid_fourcc = false; char *repr; if (valid_fourcc) repr = talloc_asprintf(talloc_ctx, "'%c%c%c%c'", fcc[0], fcc[1], fcc[2], fcc[3]); else repr = talloc_asprintf(NULL, "%d", code); return repr; } bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message) { if (code == noErr) return true; char *error_string = fourcc_repr(NULL, code); mp_msg(ao->log, level, "%s (%s)\n", message, error_string); talloc_free(error_string); return false; } void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd) { asbd->mSampleRate = ao->samplerate; // Set "AC3" for other spdif formats too - unknown if that works. asbd->mFormatID = AF_FORMAT_IS_IEC61937(ao->format) ? kAudioFormat60958AC3 : kAudioFormatLinearPCM; asbd->mChannelsPerFrame = ao->channels.num; asbd->mBitsPerChannel = af_fmt2bits(ao->format); asbd->mFormatFlags = kAudioFormatFlagIsPacked; if ((ao->format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_F) asbd->mFormatFlags |= kAudioFormatFlagIsFloat; if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI) asbd->mFormatFlags |= kAudioFormatFlagIsSignedInteger; if (BYTE_ORDER == BIG_ENDIAN) asbd->mFormatFlags |= kAudioFormatFlagIsBigEndian; asbd->mFramesPerPacket = 1; asbd->mBytesPerPacket = asbd->mBytesPerFrame = asbd->mFramesPerPacket * asbd->mChannelsPerFrame * (asbd->mBitsPerChannel / 8); } void ca_print_asbd(struct ao *ao, const char *description, const AudioStreamBasicDescription *asbd) { uint32_t flags = asbd->mFormatFlags; char *format = fourcc_repr(NULL, asbd->mFormatID); MP_VERBOSE(ao, "%s %7.1fHz %" PRIu32 "bit [%s]" "[%" PRIu32 "][%" PRIu32 "][%" PRIu32 "]" "[%" PRIu32 "][%" PRIu32 "] " "%s %s %s%s%s%s\n", description, asbd->mSampleRate, asbd->mBitsPerChannel, format, asbd->mFormatFlags, asbd->mBytesPerPacket, asbd->mFramesPerPacket, asbd->mBytesPerFrame, asbd->mChannelsPerFrame, (flags & kAudioFormatFlagIsFloat) ? "float" : "int", (flags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE", (flags & kAudioFormatFlagIsSignedInteger) ? "S" : "U", (flags & kAudioFormatFlagIsPacked) ? " packed" : "", (flags & kAudioFormatFlagIsAlignedHigh) ? " aligned" : "", (flags & kAudioFormatFlagIsNonInterleaved) ? " P" : ""); talloc_free(format); }