summaryrefslogtreecommitdiffstats
path: root/audio/out
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-03-10 10:34:41 +0100
committerwm4 <wm4@nowhere>2015-03-10 10:37:05 +0100
commit2f5e31cf47d8b340989395d84d966d1d1390fe3f (patch)
tree62cfb1dc8ccf7f00fcc54b3f5659aab9e86e95d1 /audio/out
parentfa75a7b6d7af4b6c68329e1acdaced38a71d0dd8 (diff)
downloadmpv-2f5e31cf47d8b340989395d84d966d1d1390fe3f.tar.bz2
mpv-2f5e31cf47d8b340989395d84d966d1d1390fe3f.tar.xz
ao_coreaudio_exclusive: port to pull API, fix latency calculations
Instead of maintaining a private ring buffer, use the generic support for audio APIs with pull callbacks (internally called AO pull API). This also fixes latency calculations: instead of just returning the ringbuffer status, the audio playback state is calculated better and includes interpolation. The main reason this wasn't done earlier was mid-stream format switching. The pull API can now handle it (in a way) by destroying and recreating the AO. This is a bit brutal, but quite simple. It's untested in this new AO, though. Some details might not be right, like how ot restores the old format when reloading.
Diffstat (limited to 'audio/out')
-rw-r--r--audio/out/ao_coreaudio_exclusive.c115
1 files changed, 37 insertions, 78 deletions
diff --git a/audio/out/ao_coreaudio_exclusive.c b/audio/out/ao_coreaudio_exclusive.c
index cccd4c5808..43c17c415c 100644
--- a/audio/out/ao_coreaudio_exclusive.c
+++ b/audio/out/ao_coreaudio_exclusive.c
@@ -35,21 +35,18 @@
* when you are wanting to do good buffering of audio).
*/
+#include <CoreAudio/HostTime.h>
+
#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 "audio/out/ao_coreaudio_properties.h"
#include "audio/out/ao_coreaudio_utils.h"
-static void audio_pause(struct ao *ao);
-static void audio_resume(struct ao *ao);
-static void reset(struct ao *ao);
-
static bool ca_format_is_digital(AudioStreamBasicDescription asbd)
{
switch (asbd.mFormatID)
@@ -310,8 +307,6 @@ struct priv {
bool paused;
- struct mp_ring *buffer;
-
// digital render callback
AudioDeviceIOProcID render_cb;
@@ -332,13 +327,10 @@ struct priv {
bool changed_mixing;
int stream_asbd_changed;
-};
+ bool reload_requested;
-static int get_ring_size(struct ao *ao)
-{
- return af_fmt_seconds_to_bytes(
- ao->format, 0.5, ao->channels.num, ao->samplerate);
-}
+ uint32_t hw_latency_us;
+};
static OSStatus render_cb_digital(
AudioDeviceID device, const AudioTimeStamp *ts,
@@ -350,7 +342,29 @@ static OSStatus render_cb_digital(
AudioBuffer buf = out_data->mBuffers[p->stream_idx];
int requested = buf.mDataByteSize;
- mp_ring_read(p->buffer, buf.mData, requested);
+ int pseudo_frames = requested / ao->sstride;
+
+ // we expect the callback to read full frames, which are aligned accordingly
+ if (pseudo_frames * ao->sstride != requested) {
+ MP_ERR(ao, "Unsupported unaligned read of %d bytes.\n", requested);
+ return kAudioHardwareUnspecifiedError;
+ }
+
+ int64_t end = mp_time_us();
+ end += p->hw_latency_us + ca_get_latency(ts)
+ + ca_frames_to_us(ao, pseudo_frames);
+
+ ao_read_data(ao, &buf.mData, pseudo_frames, end);
+
+ // Check whether we need to reset the digital output stream.
+ if (p->stream_asbd_changed) {
+ p->stream_asbd_changed = 0;
+ if (!p->reload_requested && ca_stream_supports_digital(ao, p->stream)) {
+ p->reload_requested = true;
+ ao_request_reload(ao);
+ MP_INFO(ao, "Stream format changed! Reloading.\n");
+ }
+ }
return noErr;
}
@@ -487,17 +501,22 @@ static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
(p->stream_asbd.mBytesPerPacket /
p->stream_asbd.mFramesPerPacket);
- p->buffer = mp_ring_new(p, get_ring_size(ao));
+ uint32_t latency_frames = 0;
+ err = CA_GET_O(p->device, kAudioDevicePropertyLatency, &latency_frames);
+ if (err != noErr) {
+ CHECK_CA_WARN("cannot get device latency");
+ latency_frames = 0;
+ }
+
+ p->hw_latency_us = ca_frames_to_us(ao, latency_frames);
+ MP_VERBOSE(ao, "base latency: %d microseconds\n", (int)p->hw_latency_us);
err = AudioDeviceCreateIOProcID(p->device,
(AudioDeviceIOProc)render_cb_digital,
(void *)ao,
&p->render_cb);
-
CHECK_CA_ERROR("failed to register digital render callback");
- reset(ao);
-
return CONTROL_TRUE;
coreaudio_error:
@@ -506,52 +525,6 @@ coreaudio_error:
return CONTROL_ERROR;
}
-static int play(struct ao *ao, void **data, int samples, int flags)
-{
- struct priv *p = ao->priv;
- void *output_samples = data[0];
- int num_bytes = samples * ao->sstride;
-
- // Check whether we need to reset the digital output stream.
- if (p->stream_asbd_changed) {
- p->stream_asbd_changed = 0;
- if (ca_stream_supports_digital(ao, p->stream)) {
- if (!ca_change_format(ao, p->stream, p->stream_asbd)) {
- MP_WARN(ao , "can't restore digital output\n");
- } else {
- MP_WARN(ao, "restoring digital output succeeded.\n");
- reset(ao);
- }
- }
- }
-
- int wrote = mp_ring_write(p->buffer, output_samples, num_bytes);
- audio_resume(ao);
-
- return wrote / ao->sstride;
-}
-
-static void reset(struct ao *ao)
-{
- struct priv *p = ao->priv;
- audio_pause(ao);
- mp_ring_reset(p->buffer);
-}
-
-static int get_space(struct ao *ao)
-{
- struct priv *p = ao->priv;
- return mp_ring_available(p->buffer) / ao->sstride;
-}
-
-static double get_delay(struct ao *ao)
-{
- // FIXME: should also report the delay of coreaudio itself (hardware +
- // internal buffers)
- struct priv *p = ao->priv;
- return mp_ring_buffered(p->buffer) / (double)ao->bps;
-}
-
static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
@@ -581,26 +554,16 @@ static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
- if (p->paused)
- return;
-
OSStatus err = AudioDeviceStop(p->device, p->render_cb);
CHECK_CA_WARN("can't stop digital device");
-
- p->paused = true;
}
static void audio_resume(struct ao *ao)
{
struct priv *p = ao->priv;
- if (!p->paused)
- return;
-
OSStatus err = AudioDeviceStart(p->device, p->render_cb);
CHECK_CA_WARN("can't start digital device");
-
- p->paused = false;
}
#define OPT_BASE_STRUCT struct priv
@@ -610,10 +573,6 @@ const struct ao_driver audio_out_coreaudio_exclusive = {
.name = "coreaudio_exclusive",
.uninit = uninit,
.init = init,
- .play = play,
- .get_space = get_space,
- .get_delay = get_delay,
- .reset = reset,
.pause = audio_pause,
.resume = audio_resume,
.list_devs = ca_get_device_list,