[vlc-commits] [Git][videolan/vlc][master] 10 commits: mmdevice: add a timer API
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Sat Oct 1 09:50:41 UTC 2022
Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC
Commits:
abda3b3f by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: add a timer API
Use "aout" internal threading to implement a timer.
This avoid to create an extra thread/timer.
This will also simplify locking since aout_stream callbacks,
aout_stream_timer_Trigger() and the timer callback need to be protected
by a mutex.
Furthermore, this leaves the MTA handling to the aout module.
- - - - -
00bc0230 by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: implement aout_stream Timer
By using the existing MM thread.
- - - - -
9797eb3e by Thomas Guillem at 2022-10-01T09:32:34+00:00
winstore: implement aout_stream Timer
By using the existing Playback thread.
- - - - -
91bc13e3 by Thomas Guillem at 2022-10-01T09:32:34+00:00
wasapi: use the aout_stream_timer API
- - - - -
53b03077 by Thomas Guillem at 2022-10-01T09:32:34+00:00
wasapi: remove an atomic
All aout_stream and timer callbacks are protected by a mutex.
- - - - -
daa5c893 by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: add aout_stream_TimingReport
- - - - -
6c54c3b2 by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: s->time_get is not mandatory anymore
Streams API can now use aout_stream_TimingReport().
- - - - -
a261132a by Thomas Guillem at 2022-10-01T09:32:34+00:00
winstore: s->time_get is not mandatory anymore
Streams API can now use aout_stream_TimingReport().
- - - - -
ee35c6fe by Thomas Guillem at 2022-10-01T09:32:34+00:00
wasapi: use aout_TimingReport
Refs #27023
- - - - -
829d66b6 by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: remove now unused time_get
- - - - -
4 changed files:
- modules/audio_output/mmdevice.c
- modules/audio_output/mmdevice.h
- modules/audio_output/wasapi.c
- modules/audio_output/winstore.c
Changes:
=====================================
modules/audio_output/mmdevice.c
=====================================
@@ -123,20 +123,6 @@ static int vlc_FromHR(audio_output_t *aout, HRESULT hr)
}
/*** VLC audio output callbacks ***/
-static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
-{
- aout_sys_t *sys = aout->sys;
- HRESULT hr;
-
- EnterMTA();
- vlc_mutex_lock(&sys->lock);
- hr = aout_stream_owner_TimeGet(sys->stream, delay);
- vlc_mutex_unlock(&sys->lock);
- LeaveMTA();
-
- return SUCCEEDED(hr) ? 0 : -1;
-}
-
static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
{
aout_sys_t *sys = aout->sys;
@@ -844,17 +830,24 @@ static void MMSessionMainloop(audio_output_t *aout, ISimpleAudioVolume *volume)
}
}
+ DWORD wait_ms = INFINITE;
DWORD ev_count = 1;
HANDLE events[2] = {
sys->work_event,
NULL
};
- /* Don't listen to the stream event if the block fifo is empty */
- if (sys->stream != NULL && sys->stream->chain != NULL)
- events[ev_count++] = sys->stream->buffer_ready_event;
+
+ if (sys->stream != NULL)
+ {
+ wait_ms = aout_stream_owner_ProcessTimer(sys->stream);
+
+ /* Don't listen to the stream event if the block fifo is empty */
+ if (sys->stream->chain != NULL)
+ events[ev_count++] = sys->stream->buffer_ready_event;
+ }
vlc_mutex_unlock(&sys->lock);
- WaitForMultipleObjects(ev_count, events, FALSE, INFINITE);
+ WaitForMultipleObjects(ev_count, events, FALSE, wait_ms);
vlc_mutex_lock(&sys->lock);
if (sys->stream != NULL)
@@ -1374,7 +1367,6 @@ static int Open(vlc_object_t *obj)
aout->start = Start;
aout->stop = Stop;
- aout->time_get = TimeGet;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
=====================================
modules/audio_output/mmdevice.h
=====================================
@@ -37,7 +37,6 @@ struct aout_stream
void *sys;
void (*stop)(aout_stream_t *);
- HRESULT (*time_get)(aout_stream_t *, vlc_tick_t *);
HRESULT (*play)(aout_stream_t *, block_t *, vlc_tick_t);
HRESULT (*pause)(aout_stream_t *, bool);
HRESULT (*flush)(aout_stream_t *);
@@ -49,9 +48,15 @@ struct aout_stream_owner
void *device;
HRESULT (*activate)(void *device, REFIID, PROPVARIANT *, void **);
HANDLE buffer_ready_event;
+ struct {
+ vlc_tick_t deadline;
+ void (*callback)(aout_stream_t *);
+ } timer;
block_t *chain;
block_t **last;
+
+ audio_output_t *aout;
};
/*
@@ -82,22 +87,6 @@ void aout_stream_owner_Stop(struct aout_stream_owner *owner)
owner->s.stop(&owner->s);
}
-static inline
-HRESULT aout_stream_owner_TimeGet(struct aout_stream_owner *owner,
- vlc_tick_t *delay)
-{
- HRESULT hr = owner->s.time_get(&owner->s, delay);
-
- if (SUCCEEDED(hr))
- {
- /* Add the block chain delay */
- vlc_tick_t length;
- block_ChainProperties(owner->chain, NULL, NULL, &length);
- *delay += length;
- }
- return hr;
-}
-
static inline
HRESULT aout_stream_owner_Play(struct aout_stream_owner *owner,
block_t *block, vlc_tick_t date)
@@ -162,6 +151,34 @@ HRESULT aout_stream_owner_PlayAll(struct aout_stream_owner *owner)
return S_OK;
}
+static inline
+DWORD aout_stream_owner_ProcessTimer(struct aout_stream_owner *owner)
+{
+ if (owner->timer.deadline != VLC_TICK_INVALID)
+ {
+ vlc_tick_t now = vlc_tick_now();
+ /* Subtract 1 ms since WaitForMultipleObjects will likely sleep
+ * a little less than requested */
+ if (now >= owner->timer.deadline - VLC_TICK_FROM_MS(1))
+ {
+ assert(owner->timer.callback != NULL);
+ owner->timer.deadline = VLC_TICK_INVALID;
+ owner->timer.callback(&owner->s);
+
+ /* timer.deadline might be updated from timer.callback */
+ if (owner->timer.deadline != VLC_TICK_INVALID)
+ {
+ now = vlc_tick_now();
+ return MS_FROM_VLC_TICK(owner->timer.deadline - now);
+ }
+ }
+ else
+ return MS_FROM_VLC_TICK(owner->timer.deadline - now);
+ }
+
+ return INFINITE;
+}
+
static inline
void *aout_stream_owner_New(audio_output_t *aout, size_t size,
HRESULT (*activate)(void *device, REFIID, PROPVARIANT *, void **))
@@ -173,9 +190,12 @@ void *aout_stream_owner_New(audio_output_t *aout, size_t size,
return NULL;
struct aout_stream_owner *owner = obj;
+ owner->aout = aout;
owner->chain = NULL;
owner->last = &owner->chain;
owner->activate = activate;
+ owner->timer.deadline = VLC_TICK_INVALID;
+ owner->timer.callback = NULL;
owner->buffer_ready_event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (unlikely(owner->buffer_ready_event == NULL))
@@ -198,6 +218,14 @@ void aout_stream_owner_Delete(struct aout_stream_owner *owner)
* "aout stream" helpers
*/
+static inline
+void aout_stream_TimingReport(aout_stream_t *s, vlc_tick_t system_ts,
+ vlc_tick_t audio_ts)
+{
+ struct aout_stream_owner *owner = aout_stream_owner(s);
+ aout_TimingReport(owner->aout, system_ts, audio_ts);
+}
+
static inline
HRESULT aout_stream_Activate(aout_stream_t *s, REFIID iid,
PROPVARIANT *actparms, void **pv)
@@ -212,4 +240,23 @@ HANDLE aout_stream_GetBufferReadyEvent(aout_stream_t *s)
struct aout_stream_owner *owner = aout_stream_owner(s);
return owner->buffer_ready_event;
}
+
+static inline
+void aout_stream_TriggerTimer(aout_stream_t *s,
+ void (*callback)(aout_stream_t *),
+ vlc_tick_t deadline)
+{
+ struct aout_stream_owner *owner = aout_stream_owner(s);
+ owner->timer.deadline = deadline;
+ owner->timer.callback = callback;
+}
+
+static inline
+void aout_stream_DisarmTimer(aout_stream_t *s)
+{
+ struct aout_stream_owner *owner = aout_stream_owner(s);
+ owner->timer.deadline = VLC_TICK_INVALID;
+ owner->timer.callback = NULL;
+}
+
#endif
=====================================
modules/audio_output/wasapi.c
=====================================
@@ -39,6 +39,8 @@
#include <audioclient.h>
#include "audio_output/mmdevice.h"
+#define TIMING_REPORT_DELAY VLC_TICK_FROM_MS(1000)
+
/* 00000092-0000-0010-8000-00aa00389b71 */
DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL,
WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00,
@@ -107,12 +109,11 @@ static BOOL CALLBACK InitFreq(INIT_ONCE *once, void *param, void **context)
typedef struct aout_stream_sys
{
IAudioClient *client;
- vlc_timer_t timer;
#define STARTED_STATE_INIT 0
#define STARTED_STATE_OK 1
#define STARTED_STATE_ERROR 2
- atomic_char started_state;
+ uint8_t started_state;
uint8_t chans_table[AOUT_CHAN_MAX];
uint8_t chans_to_reorder;
@@ -126,21 +127,20 @@ typedef struct aout_stream_sys
} aout_stream_sys_t;
/*** VLC audio output callbacks ***/
-static HRESULT TimeGet(aout_stream_t *s, vlc_tick_t *restrict delay)
+static void TimingReport(aout_stream_t *s)
{
aout_stream_sys_t *sys = s->sys;
void *pv;
UINT64 pos, qpcpos, clock_freq;
HRESULT hr;
- if (atomic_load(&sys->started_state) != STARTED_STATE_OK)
- return E_FAIL;
+ assert(sys->started_state == STARTED_STATE_OK);
hr = IAudioClient_GetService(sys->client, &IID_IAudioClock, &pv);
if (FAILED(hr))
{
msg_Err(s, "cannot get clock (error 0x%lX)", hr);
- return hr;
+ return;
}
IAudioClock *clock = pv;
@@ -152,18 +152,16 @@ static HRESULT TimeGet(aout_stream_t *s, vlc_tick_t *restrict delay)
if (FAILED(hr))
{
msg_Err(s, "cannot get position (error 0x%lX)", hr);
- return hr;
+ return;
}
- vlc_tick_t written = vlc_tick_from_frac(sys->written, sys->rate);
- vlc_tick_t tick_pos = vlc_tick_from_frac(pos, clock_freq);
-
- static_assert((10000000 % CLOCK_FREQ) == 0, "Frequency conversion broken");
+ aout_stream_TimingReport(s, VLC_TICK_FROM_MSFTIME(qpcpos),
+ vlc_tick_from_frac(pos, clock_freq));
- *delay = written - tick_pos
- - VLC_TICK_FROM_MSFTIME(get_qpc() - qpcpos);
+ aout_stream_TriggerTimer(s, TimingReport,
+ vlc_tick_now() + TIMING_REPORT_DELAY);
- return hr;
+ static_assert((10000000 % CLOCK_FREQ) == 0, "Frequency conversion broken");
}
static HRESULT StartNow(aout_stream_t *s)
@@ -172,50 +170,41 @@ static HRESULT StartNow(aout_stream_t *s)
HRESULT hr = IAudioClient_Start(sys->client);
- atomic_store(&sys->started_state,
- SUCCEEDED(hr) ? STARTED_STATE_OK : STARTED_STATE_ERROR);
+ sys->started_state = SUCCEEDED(hr) ? STARTED_STATE_OK : STARTED_STATE_ERROR;
- if (FAILED(hr))
+ if (SUCCEEDED(hr))
+ TimingReport(s);
+ else
msg_Err(s, "stream failed to start: 0x%lX", hr);
return hr;
}
-static void StartDeferredCallback(void *val)
+static void StartDeferredCallback(aout_stream_t *s)
{
- aout_stream_t *s = val;
-
- HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
- if (unlikely(FAILED(hr)))
- {
- msg_Err(s, "cannot initialize COM (error 0x%lX)", hr);
- return;
- }
-
StartNow(s);
-
- CoUninitialize();
}
static HRESULT StartDeferred(aout_stream_t *s, vlc_tick_t date)
{
aout_stream_sys_t *sys = s->sys;
vlc_tick_t written = vlc_tick_from_frac(sys->written, sys->rate);
- vlc_tick_t start_delay = date - vlc_tick_now() - written;
+ vlc_tick_t start_date = date - written;
+ vlc_tick_t start_delay = start_date - vlc_tick_now();
/* Create or update the current timer */
if (start_delay > 0)
{
- vlc_timer_schedule( sys->timer, false, start_delay, 0);
+ aout_stream_TriggerTimer(s, StartDeferredCallback, start_date);
msg_Dbg(s, "deferring start (%"PRId64" us)", start_delay);
}
else
{
- vlc_timer_disarm(sys->timer);
+ aout_stream_DisarmTimer(s);
/* Check started_state again, since the timer callback could have been
* called before or while disarming the timer */
- if (atomic_load(&sys->started_state) == STARTED_STATE_INIT)
+ if (sys->started_state == STARTED_STATE_INIT)
return StartNow(s);
}
@@ -229,13 +218,12 @@ static HRESULT Play(aout_stream_t *s, block_t *block, vlc_tick_t date)
void *pv;
HRESULT hr;
- char started_state = atomic_load(&sys->started_state);
- if (unlikely(started_state == STARTED_STATE_ERROR))
+ if (unlikely(sys->started_state == STARTED_STATE_ERROR))
{
hr = E_FAIL;
goto out;
}
- else if (started_state == STARTED_STATE_INIT)
+ else if (sys->started_state == STARTED_STATE_INIT)
{
hr = StartDeferred(s, date);
if (FAILED(hr))
@@ -332,8 +320,9 @@ static HRESULT Pause(aout_stream_t *s, bool paused)
if (paused)
{
- vlc_timer_disarm(sys->timer);
- if (atomic_load(&sys->started_state) == STARTED_STATE_OK)
+ aout_stream_DisarmTimer(s);
+
+ if (sys->started_state == STARTED_STATE_OK)
{
hr = IAudioClient_Stop(sys->client);
if (FAILED(hr))
@@ -353,10 +342,11 @@ static HRESULT Flush(aout_stream_t *s)
aout_stream_sys_t *sys = s->sys;
HRESULT hr;
- vlc_timer_disarm(sys->timer);
+ aout_stream_DisarmTimer(s);
/* Reset the timer state, the next start need to be deferred. */
- if (atomic_exchange(&sys->started_state, STARTED_STATE_INIT) == STARTED_STATE_OK)
+ if (sys->started_state == STARTED_STATE_OK)
{
+ sys->started_state = STARTED_STATE_INIT;
IAudioClient_Stop(sys->client);
hr = IAudioClient_Reset(sys->client);
}
@@ -611,14 +601,13 @@ static void Stop(aout_stream_t *s)
{
aout_stream_sys_t *sys = s->sys;
- vlc_timer_disarm(sys->timer);
+ aout_stream_DisarmTimer(s);
- if (atomic_load(&sys->started_state) == STARTED_STATE_OK)
+ if (sys->started_state == STARTED_STATE_OK)
IAudioClient_Stop(sys->client);
IAudioClient_Release(sys->client);
- vlc_timer_destroy(sys->timer);
free(sys);
}
@@ -781,13 +770,7 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
if (unlikely(sys == NULL))
return E_OUTOFMEMORY;
sys->client = NULL;
- atomic_init(&sys->started_state, STARTED_STATE_INIT);
- if (unlikely(vlc_timer_create( &sys->timer, StartDeferredCallback, s ) != 0))
- {
- msg_Err(s, "failed to create the delayed start timer");
- free(sys);
- return E_UNEXPECTED;
- }
+ sys->started_state = STARTED_STATE_INIT;
/* Configure audio stream */
WAVEFORMATEXTENSIBLE_IEC61937 wf_iec61937;
@@ -943,7 +926,6 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
*pfmt = fmt;
sys->written = 0;
s->sys = sys;
- s->time_get = TimeGet;
s->play = Play;
s->pause = Pause;
s->flush = Flush;
@@ -953,7 +935,6 @@ error:
CoTaskMemFree(pwf_mix);
if (sys->client != NULL)
IAudioClient_Release(sys->client);
- vlc_timer_destroy(sys->timer);
free(sys);
return hr;
}
=====================================
modules/audio_output/winstore.c
=====================================
@@ -355,28 +355,6 @@ done:
return SUCCEEDED(hr) ? 0 : -1;
}
-static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
-{
- aout_sys_t *sys = aout->sys;
- HRESULT hr;
-
- EnterMTA();
- vlc_mutex_lock(&sys->lock);
- if (unlikely(sys->client == NULL))
- {
- vlc_mutex_unlock(&sys->lock);
- LeaveMTA();
- return -1;
- }
-
- hr = aout_stream_owner_TimeGet(sys->stream, delay);
-
- vlc_mutex_unlock(&sys->lock);
- LeaveMTA();
-
- return SUCCEEDED(hr) ? 0 : -1;
-}
-
static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
{
aout_sys_t *sys = aout->sys;
@@ -451,17 +429,24 @@ static void *PlaybackThread(void *data)
while (true)
{
+ DWORD wait_ms = INFINITE;
DWORD ev_count = 1;
HANDLE events[2] = {
sys->work_event,
NULL
};
- /* Don't listen to the stream event if the block fifo is empty */
- if (sys->stream != NULL && sys->stream->chain != NULL)
- events[ev_count++] = owner->buffer_ready_event;
+
+ if (sys->stream != NULL)
+ {
+ wait_ms = aout_stream_owner_ProcessTimer(sys->stream);
+
+ /* Don't listen to the stream event if the block fifo is empty */
+ if (sys->stream->chain != NULL)
+ events[ev_count++] = sys->stream->buffer_ready_event;
+ }
vlc_mutex_unlock(&sys->lock);
- WaitForMultipleObjects(ev_count, events, FALSE, INFINITE);
+ WaitForMultipleObjects(ev_count, events, FALSE, wait_ms);
vlc_mutex_lock(&sys->lock);
if (sys->stopping)
@@ -724,7 +709,6 @@ static int Open(vlc_object_t *obj)
sys->client = NULL;
aout->start = Start;
aout->stop = Stop;
- aout->time_get = TimeGet;
aout->volume_set = VolumeSet;
aout->mute_set = MuteSet;
aout->play = Play;
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f9111b4ef5ca066f4ffe72e07f731e9bd8e13614...829d66b69d8ae3d8ced33363e5b0c71e99e292ac
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f9111b4ef5ca066f4ffe72e07f731e9bd8e13614...829d66b69d8ae3d8ced33363e5b0c71e99e292ac
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list