summaryrefslogtreecommitdiffstats
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
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.
-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,