mirror of
https://github.com/mpv-player/mpv
synced 2025-03-08 07:08:12 +00:00
ao_wasapi0: use new mp_ring buffer
This commit is contained in:
parent
9dfc7daf79
commit
27d352afbd
@ -30,6 +30,7 @@
|
||||
#include "core/subopt-helper.h"
|
||||
#include "audio/format.h"
|
||||
#include "core/mp_msg.h"
|
||||
#include "core/mp_ring.h"
|
||||
#include "ao.h"
|
||||
|
||||
#define RING_BUFFER_COUNT 64
|
||||
@ -75,17 +76,13 @@ typedef struct wasapi0_state {
|
||||
CRITICAL_SECTION print_lock;
|
||||
|
||||
/* Buffers */
|
||||
CRITICAL_SECTION buffer_lock;
|
||||
struct mp_ring *ringbuff;
|
||||
size_t buffer_block_size; /* Size of each block in bytes */
|
||||
LONG read_block_ptr, write_block_ptr; /*Which block are we in?*/
|
||||
LONG write_ahead_count; /* how many blocks writer is ahead of reader? should be less than RING_BUFFER_COUNT*/
|
||||
uintptr_t write_offset; /*offset while writing partial blocks, used only in main thread */
|
||||
REFERENCE_TIME
|
||||
minRequestedDuration; /* minimum wasapi buffer block size, in 100-nanosecond units */
|
||||
REFERENCE_TIME
|
||||
defaultRequestedDuration; /* default wasapi default block size, in 100-nanosecond units */
|
||||
UINT32 bufferFrameCount; /* wasapi buffer block size, number of frames, frame size at format.nBlockAlign */
|
||||
void *ring_buffer[RING_BUFFER_COUNT]; /* each bufferFrameCount sized, owned by main thread */
|
||||
|
||||
/* WASAPI handles, owned by other thread */
|
||||
IMMDeviceEnumerator *pEnumerator;
|
||||
@ -317,7 +314,6 @@ static int fix_format(struct wasapi0_state *state)
|
||||
/* cargo cult code to negotiate buffer block size, affected by hardware/drivers combinations,
|
||||
gradually grow it to 10s, by 0.5s, consider failure if it still doesn't work
|
||||
*/
|
||||
EnterCriticalSection(&state->buffer_lock);
|
||||
hr = IAudioClient_GetDevicePeriod(state->pAudioClient,
|
||||
&state->defaultRequestedDuration,
|
||||
&state->minRequestedDuration);
|
||||
@ -365,7 +361,6 @@ reinit:
|
||||
EXIT_ON_ERROR(hr)
|
||||
state->buffer_block_size = state->format.Format.nBlockAlign *
|
||||
state->bufferFrameCount;
|
||||
LeaveCriticalSection(&state->buffer_lock);
|
||||
state->hTask =
|
||||
state->VistaBlob.pAvSetMmThreadCharacteristicsW(L"Pro Audio", &state->taskIndex);
|
||||
EnterCriticalSection(&state->print_lock);
|
||||
@ -380,7 +375,6 @@ exit_label:
|
||||
"ao-wasapi: fix_format fails with %s, failed to determine buffer block size!\n",
|
||||
explain_err(hr));
|
||||
LeaveCriticalSection(&state->print_lock);
|
||||
LeaveCriticalSection(&state->buffer_lock);
|
||||
SetEvent(state->fatal_error);
|
||||
return 1;
|
||||
}
|
||||
@ -440,22 +434,23 @@ static void thread_reset(wasapi0_state *state)
|
||||
IAudioClient_Reset(state->pAudioClient);
|
||||
}
|
||||
|
||||
static void thread_feed(wasapi0_state *state)
|
||||
/* force_feed - feed in even if available data is smaller than required buffer, to clear the buffer */
|
||||
static void thread_feed(wasapi0_state *state,int force_feed)
|
||||
{
|
||||
BYTE *pData;
|
||||
int buffer_size;
|
||||
HRESULT hr = IAudioRenderClient_GetBuffer(state->pRenderClient,
|
||||
state->bufferFrameCount, &pData);
|
||||
EXIT_ON_ERROR(hr)
|
||||
EnterCriticalSection(&state->buffer_lock);
|
||||
if (state->write_ahead_count > 0) { /* OK to copy! */
|
||||
memcpy(pData, state->ring_buffer[state->read_block_ptr],
|
||||
state->buffer_block_size);
|
||||
state->read_block_ptr++;
|
||||
state->read_block_ptr = state->read_block_ptr % RING_BUFFER_COUNT;
|
||||
state->write_ahead_count--;
|
||||
LeaveCriticalSection(&state->buffer_lock);
|
||||
buffer_size = mp_ring_buffered(state->ringbuff);
|
||||
if( buffer_size > state->buffer_block_size) { /* OK to copy! */
|
||||
mp_ring_read(state->ringbuff, (unsigned char *)pData,
|
||||
state->buffer_block_size);
|
||||
} else if(force_feed) {
|
||||
/* should be smaller than buffer block size by now */
|
||||
memset(pData,0,state->buffer_block_size);
|
||||
mp_ring_read(state->ringbuff, (unsigned char *)pData, buffer_size);
|
||||
} else {
|
||||
LeaveCriticalSection(&state->buffer_lock);
|
||||
/* buffer underrun?! abort */
|
||||
hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,
|
||||
state->bufferFrameCount,
|
||||
@ -475,7 +470,7 @@ exit_label:
|
||||
|
||||
static void thread_play(wasapi0_state *state)
|
||||
{
|
||||
thread_feed(state);
|
||||
thread_feed(state, 0);
|
||||
IAudioClient_Start(state->pAudioClient);
|
||||
return;
|
||||
}
|
||||
@ -499,11 +494,10 @@ static void thread_uninit(wasapi0_state *state)
|
||||
if (!state->immed) {
|
||||
/* feed until empty */
|
||||
while (1) {
|
||||
EnterCriticalSection(&state->buffer_lock);
|
||||
LONG ahead = state->write_ahead_count;
|
||||
LeaveCriticalSection(&state->buffer_lock);
|
||||
if (WaitForSingleObject(state->hFeed, 2000) == WAIT_OBJECT_0 && ahead) {
|
||||
thread_feed(state);
|
||||
if (WaitForSingleObject(state->hFeed,2000) == WAIT_OBJECT_0 &&
|
||||
mp_ring_buffered(state->ringbuff))
|
||||
{
|
||||
thread_feed(state, 1);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
@ -568,7 +562,7 @@ static unsigned int __stdcall ThreadLoop(void *lpParameter)
|
||||
break;
|
||||
case (WAIT_OBJECT_0 + 6): /* feed */
|
||||
feedwatch = 1;
|
||||
thread_feed(state);
|
||||
thread_feed(state, 0);
|
||||
break;
|
||||
case WAIT_TIMEOUT: /* Did our feed die? */
|
||||
if (feedwatch)
|
||||
@ -608,48 +602,22 @@ static void closehandles(struct ao *ao)
|
||||
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!ao || !ao->priv)
|
||||
return -1;
|
||||
struct wasapi0_state *state = (struct wasapi0_state *)ao->priv;
|
||||
EnterCriticalSection(&state->buffer_lock);
|
||||
LONG ahead = state->write_ahead_count;
|
||||
size_t block_size = state->buffer_block_size;
|
||||
LeaveCriticalSection(&state->buffer_lock);
|
||||
ret = (RING_BUFFER_COUNT - ahead) * block_size; /* rough */
|
||||
return ret - (block_size - state->write_offset); /* take offset into account */
|
||||
return mp_ring_available(state->ringbuff);
|
||||
}
|
||||
|
||||
static void reset_buffers(struct wasapi0_state *state)
|
||||
{
|
||||
EnterCriticalSection(&state->buffer_lock);
|
||||
state->read_block_ptr = state->write_block_ptr = 0;
|
||||
state->write_ahead_count = 0;
|
||||
state->write_offset = 0;
|
||||
LeaveCriticalSection(&state->buffer_lock);
|
||||
}
|
||||
|
||||
static void free_buffers(struct wasapi0_state *state)
|
||||
{
|
||||
int iter;
|
||||
for (iter = 0; iter < RING_BUFFER_COUNT; iter++) {
|
||||
free(state->ring_buffer[iter]);
|
||||
state->ring_buffer[iter] = NULL;
|
||||
}
|
||||
mp_ring_reset(state->ringbuff);
|
||||
}
|
||||
|
||||
static int setup_buffers(struct wasapi0_state *state)
|
||||
{
|
||||
int iter;
|
||||
reset_buffers(state);
|
||||
for (iter = 0; iter < RING_BUFFER_COUNT; iter++) {
|
||||
state->ring_buffer[iter] = malloc(state->buffer_block_size);
|
||||
if (!state->ring_buffer[iter]) {
|
||||
free_buffers(state);
|
||||
return 1; /* failed */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
state->ringbuff =
|
||||
mp_ring_new(state, RING_BUFFER_COUNT * state->buffer_block_size);
|
||||
return !state->ringbuff;
|
||||
}
|
||||
|
||||
static void uninit(struct ao *ao, bool immed)
|
||||
@ -663,9 +631,7 @@ static void uninit(struct ao *ao, bool immed)
|
||||
SetEvent(state->fatal_error);
|
||||
if (state->VistaBlob.hAvrt)
|
||||
FreeLibrary(state->VistaBlob.hAvrt);
|
||||
free_buffers(state);
|
||||
closehandles(ao);
|
||||
DeleteCriticalSection(&state->buffer_lock);
|
||||
DeleteCriticalSection(&state->print_lock);
|
||||
talloc_free(state);
|
||||
ao->priv = NULL;
|
||||
@ -694,7 +660,6 @@ static int init(struct ao *ao, char *params)
|
||||
state->hUninit = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||
state->fatal_error = CreateEventW(NULL, TRUE, FALSE, NULL);
|
||||
state->hFeed = CreateEvent(NULL, FALSE, FALSE, NULL); /* for wasapi event mode */
|
||||
InitializeCriticalSection(&state->buffer_lock);
|
||||
InitializeCriticalSection(&state->print_lock);
|
||||
if (!state->init_done || !state->fatal_error || !state->hPlay ||
|
||||
!state->hPause || !state->hFeed || !state->hReset || !state->hGetvol ||
|
||||
@ -772,7 +737,6 @@ static void reset(struct ao *ao)
|
||||
static int play(struct ao *ao, void *data, int len, int flags)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned char *dat = data;
|
||||
if (!ao || !ao->priv)
|
||||
return ret;
|
||||
struct wasapi0_state *state = (struct wasapi0_state *)ao->priv;
|
||||
@ -781,28 +745,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* round to nearest block size? */
|
||||
EnterCriticalSection(&state->buffer_lock);
|
||||
/* make sure write ahead does not bust buffer count */
|
||||
while ((RING_BUFFER_COUNT - 1) > state->write_ahead_count) {
|
||||
/* data left is larger than block size, do block by block copy */
|
||||
if ((len - ret) > state->buffer_block_size) {
|
||||
memcpy(state->ring_buffer[state->write_block_ptr], &dat[ret],
|
||||
state->buffer_block_size);
|
||||
} else if (flags & AOPLAY_FINAL_CHUNK) {
|
||||
/* zero out and fill with whatever that is left, but only if it is final block */
|
||||
memset(state->ring_buffer[state->write_block_ptr], 0,
|
||||
state->buffer_block_size);
|
||||
memcpy(state->ring_buffer[state->write_block_ptr], &dat[ret],
|
||||
(len - ret));
|
||||
} else
|
||||
break; /* otherwise leave buffers outside of block alignment and let player figure it out */
|
||||
state->write_block_ptr++;
|
||||
state->write_block_ptr %= RING_BUFFER_COUNT;
|
||||
state->write_ahead_count++;
|
||||
ret += state->buffer_block_size;
|
||||
}
|
||||
LeaveCriticalSection(&state->buffer_lock);
|
||||
ret = mp_ring_write(state->ringbuff, data, len);
|
||||
|
||||
if (!state->is_playing) {
|
||||
/* start playing */
|
||||
|
Loading…
Reference in New Issue
Block a user