summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
Diffstat (limited to 'audio')
-rw-r--r--audio/decode/dec_audio.c15
-rw-r--r--audio/out/ao.c23
-rw-r--r--audio/out/ao_alsa.c71
-rw-r--r--audio/out/ao_audiounit.m201
-rw-r--r--audio/out/ao_coreaudio_chmap.c4
-rw-r--r--audio/out/ao_coreaudio_chmap.h4
-rw-r--r--audio/out/ao_coreaudio_utils.c13
-rw-r--r--audio/out/ao_coreaudio_utils.h6
8 files changed, 299 insertions, 38 deletions
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
index 3028d9988e..39e867cf42 100644
--- a/audio/decode/dec_audio.c
+++ b/audio/decode/dec_audio.c
@@ -257,12 +257,15 @@ void audio_work(struct dec_audio *da)
struct demux_packet *new_segment = da->new_segment;
da->new_segment = NULL;
- // Could avoid decoder reinit; would still need flush.
- da->codec = new_segment->codec;
- if (da->ad_driver)
- da->ad_driver->uninit(da);
- da->ad_driver = NULL;
- audio_init_best_codec(da);
+ if (da->codec == new_segment->codec) {
+ audio_reset_decoding(da);
+ } else {
+ da->codec = new_segment->codec;
+ if (da->ad_driver)
+ da->ad_driver->uninit(da);
+ da->ad_driver = NULL;
+ audio_init_best_codec(da);
+ }
da->start = new_segment->start;
da->end = new_segment->end;
diff --git a/audio/out/ao.c b/audio/out/ao.c
index 15c0f8139e..b624f4196c 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -35,6 +35,7 @@
#include "common/global.h"
extern const struct ao_driver audio_out_oss;
+extern const struct ao_driver audio_out_audiounit;
extern const struct ao_driver audio_out_coreaudio;
extern const struct ao_driver audio_out_coreaudio_exclusive;
extern const struct ao_driver audio_out_rsound;
@@ -52,6 +53,9 @@ extern const struct ao_driver audio_out_sdl;
static const struct ao_driver * const audio_out_drivers[] = {
// native:
+#if HAVE_AUDIOUNIT
+ &audio_out_audiounit,
+#endif
#if HAVE_COREAUDIO
&audio_out_coreaudio,
#endif
@@ -525,14 +529,10 @@ struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
static void get_devices(struct ao *ao, struct ao_device_list *list)
{
- int num = list->num_devices;
if (ao->driver->list_devs) {
ao->driver->list_devs(ao, list);
} else {
- char name[80] = "Default";
- if (num > 1)
- mp_snprintf_cat(name, sizeof(name), " (%s)", ao->driver->name);
- ao_device_list_add(list, ao, &(struct ao_device_desc){"", name});
+ ao_device_list_add(list, ao, &(struct ao_device_desc){"", ""});
}
}
@@ -595,6 +595,19 @@ void ao_device_list_add(struct ao_device_list *list, struct ao *ao,
{
struct ao_device_desc c = *e;
const char *dname = ao->driver->name;
+ char buf[80];
+ if (!c.desc || !c.desc[0]) {
+ if (c.name && c.name[0]) {
+ c.desc = c.name;
+ } else if (list->num_devices) {
+ // Assume this is the default device.
+ snprintf(buf, sizeof(buf), "Default (%s)", dname);
+ c.desc = buf;
+ } else {
+ // First default device (and maybe the only one).
+ c.desc = "Default";
+ }
+ }
c.name = c.name[0] ? talloc_asprintf(list, "%s/%s", dname, c.name)
: talloc_strdup(list, dname);
c.desc = talloc_strdup(list, c.desc);
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index 90250c9855..201557697d 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -376,12 +376,18 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
struct mp_chmap entry;
if (mp_chmap_from_alsa(&entry, &maps[i]->map)) {
struct mp_chmap reorder = entry;
- if (maps[i]->type == SND_CHMAP_TYPE_VAR)
- mp_chmap_reorder_norm(&reorder);
- MP_DBG(ao, "Got supported channel map: %s (type %s) -> %s -> %s\n",
- aname, snd_pcm_chmap_type_name(maps[i]->type),
- mp_chmap_to_str(&entry), mp_chmap_to_str(&reorder));
- mp_chmap_sel_add_map(&chmap_sel, &reorder);
+ mp_chmap_reorder_norm(&reorder);
+
+ MP_DBG(ao, "got ALSA chmap: %s (%s) -> %s", aname,
+ snd_pcm_chmap_type_name(maps[i]->type),
+ mp_chmap_to_str(&entry));
+ if (!mp_chmap_equals(&entry, &reorder))
+ MP_DBG(ao, " -> %s", mp_chmap_to_str(&reorder));
+ MP_DBG(ao, "\n");
+
+ struct mp_chmap final =
+ maps[i]->type == SND_CHMAP_TYPE_VAR ? reorder : entry;
+ mp_chmap_sel_add_map(&chmap_sel, &final);
} else {
MP_VERBOSE(ao, "skipping unknown ALSA channel map: %s\n", aname);
}
@@ -482,10 +488,14 @@ static int set_chmap(struct ao *ao, struct mp_chmap *dev_chmap, int num_channels
} else if (chmap.num != num_channels) {
MP_WARN(ao, "ALSA channel map conflicts with channel count!\n");
} else {
- MP_VERBOSE(ao, "using the ALSA channel map.\n");
- if (mp_chmap_equals(&chmap, &ao->channels))
+ if (mp_chmap_equals(&chmap, &ao->channels)) {
MP_VERBOSE(ao, "which is what we requested.\n");
- ao->channels = chmap;
+ } else if (!mp_chmap_is_valid(dev_chmap)) {
+ MP_VERBOSE(ao, "ignoring the ALSA channel map.\n");
+ } else {
+ MP_VERBOSE(ao, "using the ALSA channel map.\n");
+ ao->channels = chmap;
+ }
}
free(alsa_chmap);
@@ -714,18 +724,27 @@ static int init_device(struct ao *ao, int mode)
CHECK_ALSA_ERROR("Unable to set format");
dump_hw_params(ao, MSGL_DEBUG, "HW params after format:\n", alsa_hwparams);
- struct mp_chmap dev_chmap = ao->channels;
- if (af_fmt_is_spdif(ao->format) || p->opts->ignore_chmap) {
- dev_chmap.num = 0; // disable chmap API
- } else if (dev_chmap.num == 1 && dev_chmap.speaker[0] == MP_SPEAKER_ID_FC) {
- // As yet another ALSA API inconsistency, mono is not reported correctly.
- dev_chmap.num = 0;
- } else if (query_chmaps(ao, &dev_chmap)) {
- ao->channels = dev_chmap;
- } else {
- // Assume only stereo and mono are supported.
- mp_chmap_from_channels(&ao->channels, MPMIN(2, dev_chmap.num));
- dev_chmap.num = 0;
+ // Stereo, or mono if input is 1 channel.
+ struct mp_chmap reduced;
+ mp_chmap_from_channels(&reduced, MPMIN(2, ao->channels.num));
+
+ struct mp_chmap dev_chmap = {0};
+ if (!af_fmt_is_spdif(ao->format) && !p->opts->ignore_chmap &&
+ !mp_chmap_equals(&ao->channels, &reduced))
+ {
+ struct mp_chmap res = ao->channels;
+ if (query_chmaps(ao, &res))
+ dev_chmap = res;
+
+ // Whatever it is, we dumb it down to mono or stereo. Some drivers may
+ // return things like bl-br, but the user (probably) still wants stereo.
+ // This also handles the failure case (dev_chmap.num==0).
+ if (dev_chmap.num <= 2) {
+ dev_chmap.num = 0;
+ ao->channels = reduced;
+ } else if (dev_chmap.num) {
+ ao->channels = dev_chmap;
+ }
}
int num_channels = ao->channels.num;
@@ -775,8 +794,13 @@ static int init_device(struct ao *ao, int mode)
if (num_channels != ao->channels.num) {
int req = ao->channels.num;
mp_chmap_from_channels(&ao->channels, MPMIN(2, num_channels));
+ mp_chmap_fill_na(&ao->channels, num_channels);
MP_ERR(ao, "Asked for %d channels, got %d - fallback to %s.\n", req,
num_channels, mp_chmap_to_str(&ao->channels));
+ if (num_channels != ao->channels.num) {
+ MP_FATAL(ao, "mismatching channel counts.\n");
+ goto alsa_error;
+ }
}
err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &p->buffersize);
@@ -1121,7 +1145,8 @@ static bool is_useless_device(char *name)
{
char *crap[] = {"front", "rear", "center_lfe", "side", "surround21",
"surround40", "surround41", "surround50", "surround51", "surround71",
- "sysdefault", "pulse", "null", "dsnoop", "dmix", "hw", "iec958"};
+ "sysdefault", "pulse", "null", "dsnoop", "dmix", "hw", "iec958",
+ "default"};
for (int i = 0; i < MP_ARRAY_SIZE(crap); i++) {
int l = strlen(crap[i]);
if (name && strncmp(name, crap[i], l) == 0 &&
@@ -1137,6 +1162,8 @@ static void list_devs(struct ao *ao, struct ao_device_list *list)
if (snd_device_name_hint(-1, "pcm", &hints) < 0)
return;
+ ao_device_list_add(list, ao, &(struct ao_device_desc){"", ""});
+
for (int n = 0; hints[n]; n++) {
char *name = snd_device_name_get_hint(hints[n], "NAME");
char *desc = snd_device_name_get_hint(hints[n], "DESC");
diff --git a/audio/out/ao_audiounit.m b/audio/out/ao_audiounit.m
new file mode 100644
index 0000000000..7411a1a1dd
--- /dev/null
+++ b/audio/out/ao_audiounit.m
@@ -0,0 +1,201 @@
+/*
+ * 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/>.
+ */
+
+#include "config.h"
+#include "ao.h"
+#include "internal.h"
+#include "audio/format.h"
+#include "osdep/timer.h"
+#include "options/m_option.h"
+#include "misc/ring.h"
+#include "common/msg.h"
+#include "ao_coreaudio_utils.h"
+#include "ao_coreaudio_chmap.h"
+
+#import <AudioUnit/AudioUnit.h>
+#import <CoreAudio/CoreAudioTypes.h>
+#import <AudioToolbox/AudioToolbox.h>
+#import <AVFoundation/AVFoundation.h>
+#import <mach/mach_time.h>
+
+struct priv {
+ AudioUnit audio_unit;
+ double device_latency;
+};
+
+static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
+ const AudioTimeStamp *ts, UInt32 bus,
+ UInt32 frames, AudioBufferList *buffer_list)
+{
+ struct ao *ao = ctx;
+ struct priv *p = ao->priv;
+ void *planes[MP_NUM_CHANNELS] = {0};
+
+ for (int n = 0; n < ao->num_planes; n++)
+ planes[n] = buffer_list->mBuffers[n].mData;
+
+ int64_t end = mp_time_us();
+ end += ca_frames_to_us(ao, frames);
+ end += p->device_latency * 1e6;
+ ao_read_data(ao, planes, frames, end);
+ return noErr;
+}
+
+static bool init_audiounit(struct ao *ao)
+{
+ AudioStreamBasicDescription asbd;
+ OSStatus err;
+ uint32_t size;
+ struct priv *p = ao->priv;
+ AVAudioSession *instance = AVAudioSession.sharedInstance;
+ AVAudioSessionPortDescription *port = nil;
+ NSInteger maxChannels = instance.maximumOutputNumberOfChannels;
+ NSInteger prefChannels = MIN(maxChannels, ao->channels.num);
+
+ [instance setCategory:AVAudioSessionCategoryPlayback error:nil];
+ [instance setMode:AVAudioSessionModeMoviePlayback error:nil];
+ [instance setActive:YES error:nil];
+ [instance setPreferredOutputNumberOfChannels:prefChannels error:nil];
+
+ AudioComponentDescription desc = (AudioComponentDescription) {
+ .componentType = kAudioUnitType_Output,
+ .componentSubType = kAudioUnitSubType_RemoteIO,
+ .componentManufacturer = kAudioUnitManufacturer_Apple,
+ .componentFlags = 0,
+ .componentFlagsMask = 0,
+ };
+
+ AudioComponent comp = AudioComponentFindNext(NULL, &desc);
+ if (comp == NULL) {
+ MP_ERR(ao, "unable to find audio component\n");
+ goto coreaudio_error;
+ }
+
+ err = AudioComponentInstanceNew(comp, &(p->audio_unit));
+ CHECK_CA_ERROR("unable to open audio component");
+
+ err = AudioUnitInitialize(p->audio_unit);
+ CHECK_CA_ERROR_L(coreaudio_error_component,
+ "unable to initialize audio unit");
+
+ if (af_fmt_is_spdif(ao->format) || instance.outputNumberOfChannels <= 2) {
+ ao->channels = (struct mp_chmap)MP_CHMAP_INIT_STEREO;
+ } else {
+ port = instance.currentRoute.outputs.firstObject;
+ if (port.channels.count == 2 &&
+ port.channels[0].channelLabel == kAudioChannelLabel_Unknown) {
+ // Special case when using an HDMI adapter. The iOS device will
+ // perform SPDIF conversion for us, so send all available channels
+ // using the AC3 mapping.
+ ao->channels = (struct mp_chmap)MP_CHMAP6(FL, FC, FR, SL, SR, LFE);
+ } else {
+ ao->channels.num = (uint8_t)port.channels.count;
+ for (AVAudioSessionChannelDescription *ch in port.channels) {
+ ao->channels.speaker[ch.channelNumber - 1] =
+ ca_label_to_mp_speaker_id(ch.channelLabel);
+ }
+ }
+ }
+
+ ca_fill_asbd(ao, &asbd);
+ size = sizeof(AudioStreamBasicDescription);
+ err = AudioUnitSetProperty(p->audio_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, 0, &asbd, size);
+
+ CHECK_CA_ERROR_L(coreaudio_error_audiounit,
+ "unable to set the input format on the audio unit");
+
+ AURenderCallbackStruct render_cb = (AURenderCallbackStruct) {
+ .inputProc = render_cb_lpcm,
+ .inputProcRefCon = ao,
+ };
+
+ err = AudioUnitSetProperty(p->audio_unit,
+ kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input, 0, &render_cb,
+ sizeof(AURenderCallbackStruct));
+
+ CHECK_CA_ERROR_L(coreaudio_error_audiounit,
+ "unable to set render callback on audio unit");
+
+ return true;
+
+coreaudio_error_audiounit:
+ AudioUnitUninitialize(p->audio_unit);
+coreaudio_error_component:
+ AudioComponentInstanceDispose(p->audio_unit);
+coreaudio_error:
+ return false;
+}
+
+static void stop(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ OSStatus err = AudioOutputUnitStop(p->audio_unit);
+ CHECK_CA_WARN("can't stop audio unit");
+}
+
+static void start(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ AVAudioSession *instance = AVAudioSession.sharedInstance;
+
+ p->device_latency = [instance outputLatency];
+
+ OSStatus err = AudioOutputUnitStart(p->audio_unit);
+ CHECK_CA_WARN("can't start audio unit");
+}
+
+static void uninit(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ AudioOutputUnitStop(p->audio_unit);
+ AudioUnitUninitialize(p->audio_unit);
+ AudioComponentInstanceDispose(p->audio_unit);
+
+ [AVAudioSession.sharedInstance
+ setActive:NO
+ withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
+ error:nil];
+}
+
+static int init(struct ao *ao)
+{
+ if (!init_audiounit(ao))
+ goto coreaudio_error;
+
+ return CONTROL_OK;
+
+coreaudio_error:
+ return CONTROL_ERROR;
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct ao_driver audio_out_audiounit = {
+ .description = "AudioUnit (iOS)",
+ .name = "audiounit",
+ .uninit = uninit,
+ .init = init,
+ .pause = stop,
+ .resume = start,
+ .priv_size = sizeof(struct priv),
+ .options = (const struct m_option[]){
+ {0}
+ },
+};
diff --git a/audio/out/ao_coreaudio_chmap.c b/audio/out/ao_coreaudio_chmap.c
index 3db2bdf3d5..13ac87e2ba 100644
--- a/audio/out/ao_coreaudio_chmap.c
+++ b/audio/out/ao_coreaudio_chmap.c
@@ -56,7 +56,7 @@ static const int speaker_map[][2] = {
{ 0, -1 },
};
-static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
+int ca_label_to_mp_speaker_id(AudioChannelLabel label)
{
for (int i = 0; speaker_map[i][1] >= 0; i++)
if (speaker_map[i][0] == label)
@@ -64,6 +64,7 @@ static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
return -1;
}
+#if HAVE_COREAUDIO
static void ca_log_layout(struct ao *ao, int l, AudioChannelLayout *layout)
{
if (!mp_msg_test(ao->log, l))
@@ -327,3 +328,4 @@ void ca_get_active_chmap(struct ao *ao, AudioDeviceID device, int channel_count,
MP_WARN(ao, "mismatching channels - falling back to %s\n",
mp_chmap_to_str(out_map));
}
+#endif
diff --git a/audio/out/ao_coreaudio_chmap.h b/audio/out/ao_coreaudio_chmap.h
index d58270fc47..d71dee02ce 100644
--- a/audio/out/ao_coreaudio_chmap.h
+++ b/audio/out/ao_coreaudio_chmap.h
@@ -22,8 +22,12 @@
struct mp_chmap;
+int ca_label_to_mp_speaker_id(AudioChannelLabel label);
+
+#if HAVE_COREAUDIO
bool ca_init_chmap(struct ao *ao, AudioDeviceID device);
void ca_get_active_chmap(struct ao *ao, AudioDeviceID device, int channel_count,
struct mp_chmap *out_map);
+#endif
#endif
diff --git a/audio/out/ao_coreaudio_utils.c b/audio/out/ao_coreaudio_utils.c
index 76f5402fd1..70b75ba8f7 100644
--- a/audio/out/ao_coreaudio_utils.c
+++ b/audio/out/ao_coreaudio_utils.c
@@ -22,15 +22,17 @@
* on CoreAudio but not the AUHAL (such as using AudioQueue services).
*/
-#include <CoreAudio/HostTime.h>
-
#include "audio/out/ao_coreaudio_utils.h"
-#include "audio/out/ao_coreaudio_properties.h"
#include "osdep/timer.h"
#include "osdep/endian.h"
#include "osdep/semaphore.h"
#include "audio/format.h"
+#if HAVE_COREAUDIO
+#include "audio/out/ao_coreaudio_properties.h"
+#include <CoreAudio/HostTime.h>
+#endif
+
CFStringRef cfstr_from_cstr(char *str)
{
return CFStringCreateWithCString(NULL, str, CA_CFSTR_ENCODING);
@@ -46,6 +48,7 @@ char *cfstr_get_cstr(CFStringRef cfstr)
return buffer;
}
+#if HAVE_COREAUDIO
static bool ca_is_output_device(struct ao *ao, AudioDeviceID dev)
{
size_t n_buffers;
@@ -142,6 +145,7 @@ OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device)
coreaudio_error:
return err;
}
+#endif
bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
{
@@ -306,6 +310,7 @@ int64_t ca_frames_to_us(struct ao *ao, uint32_t frames)
return frames / (float) ao->samplerate * 1e6;
}
+#if HAVE_COREAUDIO
int64_t ca_get_latency(const AudioTimeStamp *ts)
{
uint64_t out = AudioConvertHostTimeToNanos(ts->mHostTime);
@@ -522,4 +527,4 @@ coreaudio_error:
sem_destroy(&wakeup);
return format_set;
}
-
+#endif
diff --git a/audio/out/ao_coreaudio_utils.h b/audio/out/ao_coreaudio_utils.h
index 5cc7db53be..6bcf6c0552 100644
--- a/audio/out/ao_coreaudio_utils.h
+++ b/audio/out/ao_coreaudio_utils.h
@@ -49,7 +49,9 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message);
} while (0)
void ca_get_device_list(struct ao *ao, struct ao_device_list *list);
+#if HAVE_COREAUDIO
OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device);
+#endif
bool ca_formatid_is_compressed(uint32_t formatid);
void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd);
@@ -63,8 +65,11 @@ bool ca_asbd_is_better(AudioStreamBasicDescription *req,
AudioStreamBasicDescription *new);
int64_t ca_frames_to_us(struct ao *ao, uint32_t frames);
+#if HAVE_COREAUDIO
int64_t ca_get_latency(const AudioTimeStamp *ts);
+#endif
+#if HAVE_COREAUDIO
bool ca_stream_supports_compressed(struct ao *ao, AudioStreamID stream);
OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid);
OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid);
@@ -73,5 +78,6 @@ OSStatus ca_enable_mixing(struct ao *ao, AudioDeviceID device, bool changed);
int64_t ca_get_device_latency_us(struct ao *ao, AudioDeviceID device);
bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream,
AudioStreamBasicDescription change_format);
+#endif
#endif /* MPV_COREAUDIO_UTILS_H */