mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 00:36:51 +00:00
osxaudio: Interpolate clock by counting elapsed time since render calls
When advancing the ringbuffer, store the processed CoreAudio sample time, then interpolate the clock in the _get_delay() calls to smooth the clock. CoreAudio's "latency" report is always a constant and otherwise leads to the clock generating a latency-time staircase. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5140>
This commit is contained in:
parent
e22c7fb3e4
commit
461f943b52
4 changed files with 123 additions and 1 deletions
|
@ -544,7 +544,13 @@ gst_osx_audio_sink_io_proc (GstOsxAudioRingBuffer * buf,
|
||||||
gst_audio_ring_buffer_clear (GST_AUDIO_RING_BUFFER (buf), readseg);
|
gst_audio_ring_buffer_clear (GST_AUDIO_RING_BUFFER (buf), readseg);
|
||||||
|
|
||||||
/* we wrote one segment */
|
/* we wrote one segment */
|
||||||
|
CORE_AUDIO_TIMING_LOCK (buf->core_audio);
|
||||||
gst_audio_ring_buffer_advance (GST_AUDIO_RING_BUFFER (buf), 1);
|
gst_audio_ring_buffer_advance (GST_AUDIO_RING_BUFFER (buf), 1);
|
||||||
|
/* FIXME: Update the timestamp and reported frames in smaller increments
|
||||||
|
* when the segment size is larger than the total inNumberFrames */
|
||||||
|
gst_core_audio_update_timing (buf->core_audio, inTimeStamp,
|
||||||
|
inNumberFrames);
|
||||||
|
CORE_AUDIO_TIMING_UNLOCK (buf->core_audio);
|
||||||
|
|
||||||
buf->segoffset = 0;
|
buf->segoffset = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,7 +395,13 @@ gst_osx_audio_src_io_proc (GstOsxAudioRingBuffer * buf,
|
||||||
writeseg, ts);
|
writeseg, ts);
|
||||||
|
|
||||||
/* we wrote one segment */
|
/* we wrote one segment */
|
||||||
|
CORE_AUDIO_TIMING_LOCK (buf->core_audio);
|
||||||
gst_audio_ring_buffer_advance (GST_AUDIO_RING_BUFFER (buf), 1);
|
gst_audio_ring_buffer_advance (GST_AUDIO_RING_BUFFER (buf), 1);
|
||||||
|
/* FIXME: Update the timestamp and reported frames in smaller increments
|
||||||
|
* when the segment size is larger than the total inNumberFrames */
|
||||||
|
gst_core_audio_update_timing (buf->core_audio, inTimeStamp,
|
||||||
|
inNumberFrames);
|
||||||
|
CORE_AUDIO_TIMING_UNLOCK (buf->core_audio);
|
||||||
|
|
||||||
buf->segoffset = 0;
|
buf->segoffset = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,20 @@ G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT);
|
||||||
#include "gstosxcoreaudiohal.c"
|
#include "gstosxcoreaudiohal.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_core_audio_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstCoreAudio *core_audio = GST_CORE_AUDIO (object);
|
||||||
|
g_mutex_clear (&core_audio->timing_lock);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (gst_core_audio_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_core_audio_class_init (GstCoreAudioClass * klass)
|
gst_core_audio_class_init (GstCoreAudioClass * klass)
|
||||||
{
|
{
|
||||||
|
GObjectClass *object_klass = G_OBJECT_CLASS (klass);
|
||||||
|
object_klass->finalize = gst_core_audio_finalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -54,6 +64,8 @@ gst_core_audio_init (GstCoreAudio * core_audio)
|
||||||
core_audio->hog_pid = -1;
|
core_audio->hog_pid = -1;
|
||||||
core_audio->disabled_mixing = FALSE;
|
core_audio->disabled_mixing = FALSE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
g_mutex_init (&core_audio->timing_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -202,8 +214,94 @@ gboolean
|
||||||
gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
|
gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
|
||||||
gdouble rate, guint * samples, gdouble * latency)
|
gdouble rate, guint * samples, gdouble * latency)
|
||||||
{
|
{
|
||||||
return gst_core_audio_get_samples_and_latency_impl (core_audio, rate,
|
uint64_t now_ns = AudioConvertHostTimeToNanos (AudioGetCurrentHostTime ());
|
||||||
|
gboolean ret = gst_core_audio_get_samples_and_latency_impl (core_audio, rate,
|
||||||
samples, latency);
|
samples, latency);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
CORE_AUDIO_TIMING_LOCK (core_audio);
|
||||||
|
|
||||||
|
uint32_t samples_remain = 0;
|
||||||
|
uint64_t anchor_ns =
|
||||||
|
AudioConvertHostTimeToNanos (core_audio->anchor_hosttime);
|
||||||
|
|
||||||
|
if (core_audio->is_src) {
|
||||||
|
int64_t captured_ns =
|
||||||
|
core_audio->rate_scalar * (int64_t) (now_ns - anchor_ns);
|
||||||
|
|
||||||
|
/* src, the anchor time is the timestamp of the first sample in the last
|
||||||
|
* packet received, and we increment up from there, unless the device gets stopped. */
|
||||||
|
if (captured_ns > 0) {
|
||||||
|
if (core_audio->io_proc_active) {
|
||||||
|
samples_remain = (uint32_t) (captured_ns * rate / GST_SECOND);
|
||||||
|
} else {
|
||||||
|
samples_remain = core_audio->anchor_pend_samples;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Time went backward. This shouldn't happen for sources, but report something anyway */
|
||||||
|
samples_remain =
|
||||||
|
(uint32_t) (-captured_ns * rate / GST_SECOND) +
|
||||||
|
core_audio->anchor_pend_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio,
|
||||||
|
"now_ns %" G_GUINT64_FORMAT " anchor %" G_GUINT64_FORMAT " elapsed ns %"
|
||||||
|
G_GINT64_FORMAT " rate %f captured_ns %" G_GINT64_FORMAT
|
||||||
|
" anchor_pend_samples %u samples_remain %u", now_ns, anchor_ns,
|
||||||
|
now_ns - anchor_ns, rate, captured_ns, core_audio->anchor_pend_samples,
|
||||||
|
samples_remain);
|
||||||
|
} else {
|
||||||
|
/* Sink, the anchor time is the time the most recent buffer will commence play out,
|
||||||
|
* and we count down to 0 for unplayed samples beyond that */
|
||||||
|
int64_t unplayed_ns =
|
||||||
|
core_audio->rate_scalar * (int64_t) (anchor_ns - now_ns);
|
||||||
|
if (unplayed_ns > 0) {
|
||||||
|
samples_remain =
|
||||||
|
(uint32_t) (unplayed_ns * rate / GST_SECOND) +
|
||||||
|
core_audio->anchor_pend_samples;
|
||||||
|
} else {
|
||||||
|
uint32_t samples_played = (uint32_t) (-unplayed_ns * rate / GST_SECOND);
|
||||||
|
if (samples_played < core_audio->anchor_pend_samples) {
|
||||||
|
samples_remain = core_audio->anchor_pend_samples - samples_played;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio,
|
||||||
|
"now_ns %" G_GUINT64_FORMAT " anchor %" G_GUINT64_FORMAT " elapsed ns %"
|
||||||
|
G_GINT64_FORMAT " rate %f unplayed_ns %" G_GINT64_FORMAT
|
||||||
|
" anchor_pend_samples %u", now_ns, anchor_ns, now_ns - anchor_ns, rate,
|
||||||
|
unplayed_ns, core_audio->anchor_pend_samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
CORE_AUDIO_TIMING_UNLOCK (core_audio);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio, "samples = %u latency %f", samples_remain,
|
||||||
|
*latency);
|
||||||
|
|
||||||
|
*samples = samples_remain;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_core_audio_update_timing (GstCoreAudio * core_audio,
|
||||||
|
const AudioTimeStamp * inTimeStamp, unsigned int inNumberFrames)
|
||||||
|
{
|
||||||
|
AudioTimeStampFlags target_flags =
|
||||||
|
kAudioTimeStampSampleHostTimeValid | kAudioTimeStampRateScalarValid;
|
||||||
|
|
||||||
|
if ((inTimeStamp->mFlags & target_flags) == target_flags) {
|
||||||
|
core_audio->anchor_hosttime = inTimeStamp->mHostTime;
|
||||||
|
core_audio->anchor_pend_samples = inNumberFrames;
|
||||||
|
core_audio->rate_scalar = inTimeStamp->mRateScalar;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio,
|
||||||
|
"anchor hosttime_ns %" G_GUINT64_FORMAT
|
||||||
|
" scalar_rate %f anchor_pend_samples %u",
|
||||||
|
AudioConvertHostTimeToNanos (core_audio->anchor_hosttime),
|
||||||
|
core_audio->rate_scalar, core_audio->anchor_pend_samples);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
|
|
|
@ -77,6 +77,9 @@ G_BEGIN_DECLS
|
||||||
typedef struct _GstCoreAudio GstCoreAudio;
|
typedef struct _GstCoreAudio GstCoreAudio;
|
||||||
typedef struct _GstCoreAudioClass GstCoreAudioClass;
|
typedef struct _GstCoreAudioClass GstCoreAudioClass;
|
||||||
|
|
||||||
|
#define CORE_AUDIO_TIMING_LOCK(core_audio) (g_mutex_lock(&(core_audio->timing_lock)))
|
||||||
|
#define CORE_AUDIO_TIMING_UNLOCK(core_audio) (g_mutex_unlock(&(core_audio->timing_lock)))
|
||||||
|
|
||||||
struct _GstCoreAudio
|
struct _GstCoreAudio
|
||||||
{
|
{
|
||||||
GObject object;
|
GObject object;
|
||||||
|
@ -107,6 +110,11 @@ struct _GstCoreAudio
|
||||||
AudioStreamBasicDescription original_format, stream_format;
|
AudioStreamBasicDescription original_format, stream_format;
|
||||||
AudioDeviceIOProcID procID;
|
AudioDeviceIOProcID procID;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
GMutex timing_lock;
|
||||||
|
uint64_t anchor_hosttime;
|
||||||
|
uint32_t anchor_pend_samples;
|
||||||
|
float rate_scalar;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstCoreAudioClass
|
struct _GstCoreAudioClass
|
||||||
|
@ -143,6 +151,10 @@ gboolean gst_core_audio_get_samples_and_latency (GstCoreAudio * cor
|
||||||
guint *samples,
|
guint *samples,
|
||||||
gdouble *latency);
|
gdouble *latency);
|
||||||
|
|
||||||
|
void gst_core_audio_update_timing (GstCoreAudio * core_audio,
|
||||||
|
const AudioTimeStamp * inTimeStamp,
|
||||||
|
unsigned int inNumberFrames);
|
||||||
|
|
||||||
void gst_core_audio_set_volume (GstCoreAudio *core_audio,
|
void gst_core_audio_set_volume (GstCoreAudio *core_audio,
|
||||||
gfloat volume);
|
gfloat volume);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue