diff options
Diffstat (limited to 'audio')
-rw-r--r-- | audio/decode/dec_audio.c | 15 | ||||
-rw-r--r-- | audio/out/ao.c | 23 | ||||
-rw-r--r-- | audio/out/ao_alsa.c | 71 | ||||
-rw-r--r-- | audio/out/ao_audiounit.m | 201 | ||||
-rw-r--r-- | audio/out/ao_coreaudio_chmap.c | 4 | ||||
-rw-r--r-- | audio/out/ao_coreaudio_chmap.h | 4 | ||||
-rw-r--r-- | audio/out/ao_coreaudio_utils.c | 13 | ||||
-rw-r--r-- | audio/out/ao_coreaudio_utils.h | 6 |
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 */ |