`hotplug_cb` was registered only in `hotplug_init()`.
This commit make it registered in `init()` as well,
so that the ao can listen for latency change
in playback.
The device latency may change during hotplugging.
This commit updates p->hw_latency_ns each time
hotplug_cb is called so that it can reflect
updated device latency.
Commit 39f7f83 changed ao_driver.reset to use AudioUnitReset instead of
AudioOutputUnitStop. The problem with calling AudioOutputUnitStop was
that AudioOutputUnitStart takes a significant amount of time after a
stop when a wireless audio device is being used. This resulted in
lagging that was noticeable to users during seeking and short
pause/resume cycles. Switching to AudioUnitReset eliminated this
lagging.
However with the switch to AudioUnitReset the macOS daemon coreaudiod
continued to consume CPU time and did not release a powerd assertion
that it created on behalf of mpv, preventing macOS from sleeping.
This commit will change ao_coreaudio.reset to call AudioOutputUnitStop
after a delay if playback has not resumed. This preserves the faster
restart of playback for seeking and short pause/resume cycles and avoids
preventing sleep and needless CPU consumption.
Fixes#11617
The code changes were authored by @orion1vi and @lhc70000.
Co-authored-by: Collider LI <lhc199652@gmail.com>
Pull AOs work off of a callback that relies on mpv's internal timer. So
like with the related video changes, convert all of these to nanoseconds
instead. In many cases, the underlying audio API does actually provide
nanosecond resolution as well.
c784820454 introduced a bool option type
as a replacement for the flag type, but didn't actually transition and
remove the flag type because it would have been too much mundane work.
ao-volume is represented in the code with a `struct ao_control_vol_t`
which contains volumes for two channels, left and right.
However the code implementing this property in command.c never treats
these values individually. They are always averaged together.
On the other hand the code in the AOs handling these values also has to
handle the case where *not* exactly two channels are handled.
So let's remove the `struct ao_control_vol_t` and replace it with a
simple float.
This makes the semantics clear to AO authors and allows us to drop some code from the AOs and command.c.
[motivation]
Seeking on MacOS appears to be lagged when users connect
to wireless audio output (airpods for example).
This commit attempts to fixmpv-player/mpv#10270
[observation]
1. When using other media player (VLC to be exact) simultaneously,
the lagging on seek disappear. We could guess that the AudioDevice
is on some sort of "warm-up" state.
See mpv-player/mpv#9243 for detailed description.
2. `AudioOutputUnitStart` takes significant longer time after each seek
or pause/play when using wireless output devices compares to wired devices.
[rationale]
After investigate codes in ao_coreaudio.c, it appears that the the `stop`
function was used as `ao_driver.reset` function. Therefore every seek
and pause would call `AudioOutputUnitStop`.
It turns out that `ao_driver.reset` function is used in `ao_reset`.
And `ao_reset` function is used to clean up the state of current `ao`
so I think `AudioUnitReset` is more proper than `AudioOutputUnitStop`
under this semantics.
Since ao_coreaudio use pull base mechanism, audio playback behaviors
upon pause/seek could be handled by callback function
(streaming silence when paused) so there is no need to stop AudioUnit when resetting.
Therefore using `AudioUnitReset` as `ao_driver.reset` looks proper.
Additionally, after using proper reset, the AudioUnit that represents
hardware I/O devices doesn't need to be restart everytime seek/pause actions happen.
Restarting wireless devices simply takes longer in MacOS which is
the root cause of lagging observed by users when they seek or pause/play media.
[method]
Use `AudioUnitReset` for ao_driver.reset.
This affects "pull" AOs only: ao_alsa, ao_pulse, ao_openal, ao_pcm,
ao_lavc. There are changes to the other AOs too, but that's only about
renaming ao_driver.resume to ao_driver.start.
ao_openal is broken because I didn't manage to fix it, so it exits with
an error message. If you want it, why don't _you_ put effort into it? I
see no reason to waste my own precious lifetime over this (I realize the
irony).
ao_alsa loses the poll() mechanism, but it was mostly broken and didn't
really do what it was supposed to. There doesn't seem to be anything in
the ALSA API to watch the playback status without polling (unless you
want to use raw UNIX signals).
No idea if ao_pulse is correct, or whether it's subtly broken now. There
is no documentation, so I can't tell what is correct, without reverse
engineering the whole project. I recommend using ALSA.
This was supposed to be just a simple fix, but somehow it expanded scope
like a train wreck. Very high chance of regressions, but probably only
for the AOs listed above. The rest you can figure out from reading the
diff.
Change all OPT_* macros such that they don't define the entire m_option
initializer, and instead expand only to a part of it, which sets certain
fields. This requires changing almost every option declaration, because
they all use these macros. A declaration now always starts with
{"name", ...
followed by designated initializers only (possibly wrapped in macros).
The OPT_* macros now initialize the .offset and .type fields only,
sometimes also .priv and others.
I think this change makes the option macros less tricky. The old code
had to stuff everything into macro arguments (and attempted to allow
setting arbitrary fields by letting the user pass designated
initializers in the vararg parts). Some of this was made messy due to
C99 and C11 not allowing 0-sized varargs with ',' removal. It's also
possible that this change is pointless, other than cosmetic preferences.
Not too happy about some things. For example, the OPT_CHOICE()
indentation I applied looks a bit ugly.
Much of this change was done with regex search&replace, but some places
required manual editing. In particular, code in "obscure" areas (which I
didn't include in compilation) might be broken now.
In wayland_common.c the author of some option declarations confused the
flags parameter with the default value (though the default value was
also properly set below). I fixed this with this change.
This was all dead code. Commit 995c47da9a (over 3 years ago) removed all
uses of the controls.
It would be nice if AOs could apply a linear gain volume, that only
affects the AO's audio stream for low-latency volume adjust and muting.
AOCONTROL_HAS_SOFT_VOLUME was supposed to signal this, but to use it,
we'd have to thoroughly check whether it really uses the expected
semantics, so there's really nothing useful left in this old code.
All authors have agreed to the relicensing.
The code was pretty much rewritten by Stefano Pigozzi. Since the rewrite
happened incrementally, and seems to include refactored portions of
older code, this relicensing was done on the pre-refactor code do.
The original commit adding this AO (as ao_macosx.c) credits Timothy J.
Wood as original author. He was asked and agreed to LGPL. It's not
entirely sure from which project this code came from, but it's probably
libao. In that project, Stanley Seibert made some changes to it (who as
a major developer of libao was asked just to be sure), and also Ralph
Giles and Ben Hines made two small changes. The latter were not asked,
but none of their code survived anyway.
Long planned. Leads to some sanity.
There still are some rather gross things. Especially g_groups is ugly,
and a hack that can hopefully be removed. (There is a plan for it, but
whether it's implemented depends on how much energy is left.)
And introduce a global option which does this. Or more precisely, this
deprecates the global wasapi and coreaudio options, and adds a new one
that merges their functionality. (Due to the way the sub-option
deprecation mechanism works, this is simpler.)
Setting this here is a race condition. It's called from a CoreAudio
callbacks, and there are no locks. It's a string, so this can be
potentially severe.
It's hard to fix and only CoreAudio supported it, so remove it.
This causes the "audio-out-detected-device" property to return nothing
on all platforms.
ao_coreaudio (using AudioUnit) accounted only for part of the latency -
move the code in ao_coreaudio_exclusive to utils, and use that for the
AudioUnit code.
(There's still the question why CoreAudio and AudioUnit require you to
jump through hoops this much, but apparently that's how it is.)
May help with (supposedly) bad drivers, which can put the device into
some sort of broken state when trying to set a different physical
format. When the previous format is restored, it apparently recovers.
This might make the change-physical-format suboption more robust.
Replace all the check macros with function calls. Give them all the
same case and naming schema.
Drop af_fmt2bits(). Only af_fmt2bps() survives as af_fmt_to_bytes().
Introduce af_fmt_is_pcm(), and use it in situations that used
!AF_FORMAT_IS_SPECIAL. Nobody really knew what a "special" format
was. It simply meant "not PCM".
This may or may not fix some issues with the format switching
code. Actually, it seems somewhat unlikely, but then checking
the stream type isn't incorrect either, and is probably
something the API user should always be doing.
Move all of the channel map retrieval/negotiation code to a separate
file. This will (probably) be helpful when extending
ao_coreaudio_exclusive.c.
Nothing else changes, other than some minor cosmetics and renaming,
and changing some details for decoupling it from the ao_coreaudio.c
internals.
If for example the physical format is set to stereo, the reported
multichannel layout will actually be stereo. It fixes itself only after
the physical format is changed.
ao_coreaudio uses AudioUnit - the OSX software mixer. In theory, it
supports multichannel audio just fine. But in practice, this might be
disabled by default, and the user is supposed to select a multichannel
base format in the "Audio MIDI Setup" utility.
This option attempts to change this setting automatically. Some possible
disadvantages and caveats are listed in the manpage additions. It is off
by default, since changing this might be rather bad behavior for a
normal application.
If for example the audio settings are set to 5.1 output, but the
hardware does 8 channels natively (HDMI), the reported channel
layout will have 2 dummy channels. To avoid falling back to stereo,
we have to write audio in this format to the device.
ca_label_to_mp_speaker_id() checked whether the last entry was >= 0, but
actually this condition was never true, and MP_SPEAKER_ID_UNKNOWN0 is
not negative.
This commit adds notifications for hot plugging of devices. It also extends
the old behaviour of the `audio-out-detected-device` property which is now
backed by the hotplugging code. This allows clients to be notified when the
actual audio output device changes.
Maybe hotplugging should be supported for ao_coreaudio_exclusive too, but it's
device selection code is a bit fragile.
Previously we let the user use the audio device ID, but this is not persistent
and can change when plugging in new devices. That of course made it quite
worthless for storing it as a user setting for GUIs, or for user scripts.
In theory getting the kAudioDevicePropertyDeviceUID can fail but it doesn't
on any of my devices, so I'm leaving the error reporting quite high and see if
someone complains.