mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 904617: Part 1 - Add a way to get cube latency, add wasapi latency functions r=kinetik
This commit is contained in:
parent
7c615652fb
commit
0eb929dfa8
@ -120,6 +120,7 @@ cubeb_stream_get_position
|
||||
cubeb_stream_init
|
||||
cubeb_stream_start
|
||||
cubeb_stream_stop
|
||||
cubeb_stream_get_latency
|
||||
#endif
|
||||
#ifdef MOZ_OGG
|
||||
th_comment_clear
|
||||
|
@ -234,6 +234,15 @@ int cubeb_stream_stop(cubeb_stream * stream);
|
||||
@retval CUBEB_ERROR */
|
||||
int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
|
||||
|
||||
/** Get the latency for this stream, in frames. This is the number of frames
|
||||
between the time cubeb acquires the data in the callback and the listener
|
||||
can hear the sound.
|
||||
@param stream
|
||||
@param latency Current approximate stream latency in ms
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR */
|
||||
int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
@ -23,6 +23,7 @@ struct cubeb_ops {
|
||||
int (* stream_start)(cubeb_stream * stream);
|
||||
int (* stream_stop)(cubeb_stream * stream);
|
||||
int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
|
||||
int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
|
||||
};
|
||||
|
||||
#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */
|
||||
|
@ -224,3 +224,13 @@ cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
|
||||
|
||||
return stream->context->ops->stream_get_position(stream, position);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
|
||||
{
|
||||
if (!stream || !latency) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return stream->context->ops->stream_get_latency(stream, latency);
|
||||
}
|
||||
|
@ -1009,6 +1009,21 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
{
|
||||
snd_pcm_sframes_t delay;
|
||||
/* This function returns the delay in frames until a frame written using
|
||||
snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */
|
||||
if (snd_pcm_delay(stm->pcm, &delay)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*latency = delay;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const alsa_ops = {
|
||||
.init = alsa_init,
|
||||
.get_backend_id = alsa_get_backend_id,
|
||||
@ -1018,5 +1033,6 @@ static struct cubeb_ops const alsa_ops = {
|
||||
.stream_destroy = alsa_stream_destroy,
|
||||
.stream_start = alsa_stream_start,
|
||||
.stream_stop = alsa_stream_stop,
|
||||
.stream_get_position = alsa_stream_get_position
|
||||
.stream_get_position = alsa_stream_get_position,
|
||||
.stream_get_latency = alsa_stream_get_latency
|
||||
};
|
||||
|
@ -5,7 +5,9 @@
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#define NDEBUG
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
@ -424,6 +426,19 @@ audiotrack_stream_get_position(cubeb_stream * stream, uint64_t * position)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
audiotrack_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
|
||||
{
|
||||
assert(stream->instance && latency);
|
||||
|
||||
/* Android returns the latency in ms, we want it in frames. */
|
||||
*latency = stream->context->klass.latency(stream->instance);
|
||||
/* with rate <= 96000, we won't overflow until 44.739 seconds of latency */
|
||||
*latency = (*latency * stream->params.rate) / 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const audiotrack_ops = {
|
||||
.init = audiotrack_init,
|
||||
.get_backend_id = audiotrack_get_backend_id,
|
||||
@ -433,5 +448,6 @@ static struct cubeb_ops const audiotrack_ops = {
|
||||
.stream_destroy = audiotrack_stream_destroy,
|
||||
.stream_start = audiotrack_stream_start,
|
||||
.stream_stop = audiotrack_stream_stop,
|
||||
.stream_get_position = audiotrack_stream_get_position
|
||||
.stream_get_position = audiotrack_stream_get_position,
|
||||
.stream_get_latency = audiotrack_stream_get_latency
|
||||
};
|
||||
|
@ -10,8 +10,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
#include <CoreAudio/HostTime.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
#include "prtime.h"
|
||||
|
||||
#define NBUFS 4
|
||||
|
||||
@ -33,8 +35,23 @@ struct cubeb_stream {
|
||||
uint64_t frames_queued;
|
||||
int shutdown;
|
||||
int draining;
|
||||
uint64_t current_latency_frames;
|
||||
uint64_t hw_latency_frames;
|
||||
};
|
||||
|
||||
static int64_t
|
||||
audiotimestamp_to_latency(AudioTimeStamp const * tstamp, cubeb_stream * stream)
|
||||
{
|
||||
if (!(tstamp->mFlags & kAudioTimeStampHostTimeValid)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime);
|
||||
uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
|
||||
|
||||
return ((pres - now) * stream->sample_spec.mSampleRate) / PR_NSEC_PER_SEC;
|
||||
}
|
||||
|
||||
static OSStatus
|
||||
audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
|
||||
AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes,
|
||||
@ -52,6 +69,8 @@ audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
|
||||
|
||||
pthread_mutex_lock(&stm->mutex);
|
||||
|
||||
stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
|
||||
|
||||
if (stm->draining || stm->shutdown) {
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
if (stm->draining) {
|
||||
@ -255,6 +274,8 @@ audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stre
|
||||
|
||||
stm->frames_played = 0;
|
||||
stm->frames_queued = 0;
|
||||
stm->current_latency_frames = 0;
|
||||
stm->hw_latency_frames = UINT64_MAX;
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||||
r = OpenAComponent(comp, &stm->unit);
|
||||
@ -351,6 +372,95 @@ audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
{
|
||||
pthread_mutex_lock(&stm->mutex);
|
||||
if (stm->hw_latency_frames == UINT64_MAX) {
|
||||
UInt32 size;
|
||||
uint32_t device_latency_frames, device_safety_offset;
|
||||
double unit_latency_sec;
|
||||
AudioDeviceID output_device_id;
|
||||
OSStatus r;
|
||||
|
||||
AudioObjectPropertyAddress output_device_address = {
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
AudioObjectPropertyAddress latency_address = {
|
||||
kAudioDevicePropertyLatency,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
AudioObjectPropertyAddress safety_offset_address = {
|
||||
kAudioDevicePropertySafetyOffset,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
|
||||
size = sizeof(output_device_id);
|
||||
r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
||||
&output_device_address,
|
||||
0,
|
||||
0,
|
||||
&size,
|
||||
&output_device_id);
|
||||
if (r != noErr) {
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
size = sizeof(unit_latency_sec);
|
||||
r = AudioUnitGetProperty(stm->unit,
|
||||
kAudioUnitProperty_Latency,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&unit_latency_sec,
|
||||
&size);
|
||||
if (r != noErr) {
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
size = sizeof(device_latency_frames);
|
||||
r = AudioObjectGetPropertyData(output_device_id,
|
||||
&latency_address,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
&device_latency_frames);
|
||||
if (r != noErr) {
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
size = sizeof(device_safety_offset);
|
||||
r = AudioObjectGetPropertyData(output_device_id,
|
||||
&safety_offset_address,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
&device_safety_offset);
|
||||
if (r != noErr) {
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
// This part is fixed and depend on the stream parameter and the hardware.
|
||||
stm->hw_latency_frames =
|
||||
(uint32_t)(unit_latency_sec * stm->sample_spec.mSampleRate)
|
||||
+ device_latency_frames
|
||||
+ device_safety_offset;
|
||||
}
|
||||
|
||||
*latency = stm->hw_latency_frames + stm->current_latency_frames;
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const audiounit_ops = {
|
||||
.init = audiounit_init,
|
||||
.get_backend_id = audiounit_get_backend_id,
|
||||
@ -360,5 +470,6 @@ static struct cubeb_ops const audiounit_ops = {
|
||||
.stream_destroy = audiounit_stream_destroy,
|
||||
.stream_start = audiounit_stream_start,
|
||||
.stream_stop = audiounit_stream_stop,
|
||||
.stream_get_position = audiounit_stream_get_position
|
||||
.stream_get_position = audiounit_stream_get_position,
|
||||
.stream_get_latency = audiounit_stream_get_latency
|
||||
};
|
||||
|
@ -413,6 +413,14 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
{
|
||||
*latency = NBUFS * stm->queuebuf_len;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const opensl_ops = {
|
||||
.init = opensl_init,
|
||||
.get_backend_id = opensl_get_backend_id,
|
||||
@ -422,5 +430,6 @@ static struct cubeb_ops const opensl_ops = {
|
||||
.stream_destroy = opensl_stream_destroy,
|
||||
.stream_start = opensl_stream_start,
|
||||
.stream_stop = opensl_stream_stop,
|
||||
.stream_get_position = opensl_stream_get_position
|
||||
.stream_get_position = opensl_stream_get_position,
|
||||
.stream_get_latency = opensl_stream_get_latency
|
||||
};
|
||||
|
@ -568,6 +568,26 @@ pulse_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
{
|
||||
pa_usec_t r_usec;
|
||||
int negative, r;
|
||||
|
||||
if (!stm) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
r = WRAP(pa_stream_get_latency)(stm->stream, &r_usec, &negative);
|
||||
assert(!negative);
|
||||
if (r) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*latency = (r_usec * stm->sample_spec.rate) / PR_NSEC_PER_SEC;
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const pulse_ops = {
|
||||
.init = pulse_init,
|
||||
.get_backend_id = pulse_get_backend_id,
|
||||
@ -577,5 +597,6 @@ static struct cubeb_ops const pulse_ops = {
|
||||
.stream_destroy = pulse_stream_destroy,
|
||||
.stream_start = pulse_stream_start,
|
||||
.stream_stop = pulse_stream_stop,
|
||||
.stream_get_position = pulse_stream_get_position
|
||||
.stream_get_position = pulse_stream_get_position,
|
||||
.stream_get_latency = pulse_stream_get_latency
|
||||
};
|
||||
|
@ -314,6 +314,15 @@ sndio_stream_set_volume(cubeb_stream *s, float volume)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
{
|
||||
// http://www.openbsd.org/cgi-bin/man.cgi?query=sio_open
|
||||
// in the "Measuring the latency and buffers usage" paragraph.
|
||||
*latency = stm->wrpos - stm->rdpos;
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const sndio_ops = {
|
||||
.init = sndio_init,
|
||||
.get_backend_id = sndio_get_backend_id,
|
||||
@ -322,5 +331,6 @@ static struct cubeb_ops const sndio_ops = {
|
||||
.stream_destroy = sndio_stream_destroy,
|
||||
.stream_start = sndio_stream_start,
|
||||
.stream_stop = sndio_stream_stop,
|
||||
.stream_get_position = sndio_stream_get_position
|
||||
.stream_get_position = sndio_stream_get_position,
|
||||
.stream_get_latency = sndio_stream_get_latency
|
||||
};
|
||||
|
@ -878,8 +878,8 @@ cubeb_ops const wasapi_ops = {
|
||||
/*.stream_destroy =*/ wasapi_stream_destroy,
|
||||
/*.stream_start =*/ wasapi_stream_start,
|
||||
/*.stream_stop =*/ wasapi_stream_stop,
|
||||
/*.stream_get_position =*/ wasapi_stream_get_position
|
||||
///*.stream_get_latency =*/ wasapi_stream_get_latency
|
||||
/*.stream_get_position =*/ wasapi_stream_get_position,
|
||||
/*.stream_get_latency =*/ wasapi_stream_get_latency
|
||||
};
|
||||
} // namespace anonymous
|
||||
|
||||
|
@ -64,6 +64,7 @@ struct cubeb_stream {
|
||||
HANDLE event;
|
||||
HWAVEOUT waveout;
|
||||
CRITICAL_SECTION lock;
|
||||
uint64_t written;
|
||||
};
|
||||
|
||||
static size_t
|
||||
@ -144,6 +145,7 @@ winmm_refill_stream(cubeb_stream * stm)
|
||||
} else if (got < wanted) {
|
||||
stm->draining = 1;
|
||||
}
|
||||
stm->written += got;
|
||||
|
||||
assert(hdr->dwFlags & WHDR_PREPARED);
|
||||
|
||||
@ -389,6 +391,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
stm->data_callback = data_callback;
|
||||
stm->state_callback = state_callback;
|
||||
stm->user_ptr = user_ptr;
|
||||
stm->written = 0;
|
||||
|
||||
if (latency < context->minimum_latency) {
|
||||
latency = context->minimum_latency;
|
||||
@ -574,6 +577,24 @@ winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
{
|
||||
MMRESULT r;
|
||||
MMTIME time;
|
||||
uint64_t written;
|
||||
|
||||
EnterCriticalSection(&stm->lock);
|
||||
time.wType = TIME_SAMPLES;
|
||||
r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
|
||||
written = stm->written;
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
|
||||
*latency = written - time.u.sample;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const winmm_ops = {
|
||||
/*.init =*/ winmm_init,
|
||||
/*.get_backend_id =*/ winmm_get_backend_id,
|
||||
@ -583,5 +604,6 @@ static struct cubeb_ops const winmm_ops = {
|
||||
/*.stream_destroy =*/ winmm_stream_destroy,
|
||||
/*.stream_start =*/ winmm_stream_start,
|
||||
/*.stream_stop =*/ winmm_stream_stop,
|
||||
/*.stream_get_position =*/ winmm_stream_get_position
|
||||
/*.stream_get_position =*/ winmm_stream_get_position,
|
||||
/*.stream_get_latency = */ winmm_stream_get_latency
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user