mpv/audio/out/ao_coreaudio_utils.c

178 lines
5.6 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 "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;
asbd->mFormatID = AF_FORMAT_IS_AC3(ao->format) ?
kAudioFormat60958AC3 :
kAudioFormatLinearPCM;
asbd->mChannelsPerFrame = ao->channels.num;
asbd->mBitsPerChannel = af_fmt2bits(ao->format);
asbd->mFormatFlags = kAudioFormatFlagIsPacked;
if ((ao->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F)
asbd->mFormatFlags |= kAudioFormatFlagIsFloat;
if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI)
asbd->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
if ((ao->format & AF_FORMAT_END_MASK) == AF_FORMAT_BE)
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);
}