0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 12:02:23 +02:00

ao/wasapi: improve error messages and add more debug statements

also enforce more consistency in the exit codes and error handling

thanks to Jonathan Yong <10walls@gmail.com>
This commit is contained in:
Kevin Mitchell 2014-11-17 02:48:02 -08:00
parent d4393be0f9
commit e28102f1a8
4 changed files with 141 additions and 99 deletions

View File

@ -50,7 +50,8 @@ static double get_device_delay(struct wasapi_state *state) {
case S_OK: case S_FALSE:
break;
default:
MP_ERR(state, "IAudioClock::GetPosition returned %s\n", wasapi_explain_err(hr));
MP_ERR(state, "IAudioClock::GetPosition returned %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
}
LARGE_INTEGER qpc_count;
@ -65,7 +66,7 @@ static double get_device_delay(struct wasapi_state *state) {
double diff = sample_count - position;
double delay = diff / state->format.Format.nSamplesPerSec;
MP_TRACE(state, "device delay: %g samples (%g ms)\n", diff, delay * 1000);
MP_TRACE(state, "Device delay: %g samples (%g ms)\n", diff, delay * 1000);
return delay;
}
@ -103,7 +104,8 @@ static void thread_feed(struct ao *ao)
return;
exit_label:
MP_ERR(state, "thread_feed fails with %"PRIx32"!\n", (uint32_t)hr);
MP_ERR(state, "Error feeding audio: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
return;
}
@ -113,20 +115,27 @@ static DWORD __stdcall ThreadLoop(void *lpParameter)
if (!ao || !ao->priv)
return -1;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
if (wasapi_thread_init(ao))
int thread_ret;
state->init_ret = wasapi_thread_init(ao);
SetEvent(state->init_done);
if (state->init_ret != S_OK) {
thread_ret = -1;
goto exit_label;
}
MSG msg;
DWORD waitstatus = WAIT_FAILED;
DWORD waitstatus;
HANDLE playcontrol[] =
{state->hUninit, state->hFeed, state->hForceFeed, NULL};
MP_VERBOSE(ao, "Entering dispatch loop!\n");
MP_DBG(ao, "Entering dispatch loop\n");
while (1) { /* watch events */
waitstatus = MsgWaitForMultipleObjects(3, playcontrol, FALSE, INFINITE,
QS_POSTMESSAGE | QS_SENDMESSAGE);
switch (waitstatus) {
case WAIT_OBJECT_0: /*shutdown*/
wasapi_thread_uninit(ao);
thread_ret = 0;
goto exit_label;
case (WAIT_OBJECT_0 + 1): /* feed */
thread_feed(ao);
@ -140,8 +149,10 @@ static DWORD __stdcall ThreadLoop(void *lpParameter)
DispatchMessage(&msg);
}
break;
case WAIT_FAILED: /* ??? */
return -1;
default:
MP_ERR(ao, "Unhandled case in thread loop");
thread_ret = -1;
goto exit_label;
}
}
exit_label:
@ -149,7 +160,8 @@ exit_label:
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
DispatchMessage(&msg);
}
return state->init_ret;
return thread_ret;
}
static void closehandles(struct ao *ao)
@ -165,22 +177,22 @@ static void closehandles(struct ao *ao)
static void uninit(struct ao *ao)
{
MP_VERBOSE(ao, "uninit!\n");
MP_DBG(ao, "Uninit wasapi\n");
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
wasapi_release_proxies(state);
SetEvent(state->hUninit);
/* wait up to 10 seconds */
if (WaitForSingleObject(state->threadLoop, 10000) == WAIT_TIMEOUT)
MP_ERR(ao, "audio loop thread refuses to abort!");
MP_ERR(ao, "Audio loop thread refuses to abort");
if (state->VistaBlob.hAvrt)
FreeLibrary(state->VistaBlob.hAvrt);
closehandles(ao);
MP_VERBOSE(ao, "uninit END!\n");
MP_DBG(ao, "Uninit wasapi done\n");
}
static int init(struct ao *ao)
{
MP_VERBOSE(ao, "init!\n");
MP_DBG(ao, "Init wasapi\n");
ao->format = af_fmt_from_planar(ao->format);
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_waveext(&sel);
@ -212,23 +224,24 @@ static int init(struct ao *ao)
/* failed to init events */
return -1;
}
state->init_ret = -1;
state->init_ret = E_FAIL;
state->threadLoop = (HANDLE)CreateThread(NULL, 0, &ThreadLoop, ao, 0, NULL);
if (!state->threadLoop) {
/* failed to init thread */
MP_ERR(ao, "fail to create thread!\n");
MP_ERR(ao, "Failed to create thread\n");
return -1;
}
WaitForSingleObject(state->init_done, INFINITE); /* wait on init complete */
if (state->init_ret) {
if (state->init_ret != S_OK) {
if (!ao->probing) {
MP_ERR(ao, "thread_init failed!\n");
MP_ERR(ao, "Received failure from audio thread\n");
}
} else
MP_VERBOSE(ao, "Init Done!\n");
return -1;
}
MP_DBG(ao, "Init wasapi done\n");
wasapi_setup_proxies(state);
return state->init_ret;
return 0;
}
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
@ -249,7 +262,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
/* check to see if user manually changed volume through mixer;
this information is used in exclusive mode for restoring the mixer volume on uninit */
if (state->audio_volume != state->previous_volume) {
MP_VERBOSE(state, "mixer difference: %.2g now, expected %.2g\n",
MP_VERBOSE(state, "Mixer difference: %.2g now, expected %.2g\n",
state->audio_volume, state->previous_volume);
state->initial_volume = state->audio_volume;
}

View File

@ -35,7 +35,7 @@ typedef struct wasapi_state {
HANDLE threadLoop;
/* Init phase */
int init_ret;
HRESULT init_ret;
HANDLE init_done;
int share_mode;

View File

@ -85,6 +85,10 @@ const char *wasapi_explain_err(const HRESULT hr)
#define E(x) case x : return # x ;
switch (hr) {
E(S_OK)
E(E_FAIL)
E(E_OUTOFMEMORY)
E(E_POINTER)
E(E_INVALIDARG)
E(AUDCLNT_E_NOT_INITIALIZED)
E(AUDCLNT_E_ALREADY_INITIALIZED)
E(AUDCLNT_E_WRONG_ENDPOINT_TYPE)
@ -160,7 +164,7 @@ static int set_ao_format(struct wasapi_state *state,
WAVEFORMATEXTENSIBLE wformat)
{
if (wformat.SubFormat.Data1 != 1 && wformat.SubFormat.Data1 != 3) {
MP_ERR(ao, "unknown SubFormat %"PRIu32"\n",
MP_ERR(ao, "Unknown SubFormat %"PRIu32"\n",
(uint32_t)wformat.SubFormat.Data1);
return 0;
}
@ -196,7 +200,7 @@ static int try_format(struct wasapi_state *state,
if (!af_format)
return 0;
MP_VERBOSE(ao, "trying %dch %s @ %dhz\n",
MP_VERBOSE(ao, "Trying %dch %s @ %dhz\n",
channels.num, af_fmt_to_str(af_format), samplerate);
union WAVEFMT u;
@ -220,7 +224,7 @@ static int try_format(struct wasapi_state *state,
if (hr == S_FALSE) {
if (set_ao_format(state, ao, wformat)) {
MP_VERBOSE(ao, "accepted as %dch %s @ %dhz\n",
MP_VERBOSE(ao, "Accepted as %dch %s @ %dhz\n",
ao->channels.num, af_fmt_to_str(ao->format), ao->samplerate);
return 1;
@ -283,7 +287,7 @@ static int try_passthrough(struct wasapi_state *state,
union WAVEFMT u;
u.extensible = &wformat;
MP_VERBOSE(ao, "trying passthrough for %s...\n", af_fmt_to_str(ao->format));
MP_VERBOSE(ao, "Trying passthrough for %s...\n", af_fmt_to_str(ao->format));
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
state->share_mode,
@ -304,7 +308,7 @@ static int find_formats(struct ao *const ao)
if (try_passthrough(state, ao))
return 0;
MP_ERR(ao, "couldn't use passthrough!");
MP_ERR(ao, "Couldn't use passthrough");
if (!state->opt_exclusive)
MP_ERR(ao, " (try exclusive mode)");
MP_ERR(ao, "\n");
@ -326,7 +330,7 @@ static int find_formats(struct ao *const ao)
return 0;
}
MP_WARN(ao, "couldn't use default mix format!\n");
MP_WARN(ao, "Couldn't use default mix format\n");
}
/* Exclusive mode, we have to guess. */
@ -397,13 +401,13 @@ static int find_formats(struct ao *const ao)
bits = start_bits;
mp_chmap_from_channels(&ao->channels, 2);
} else {
MP_ERR(ao, "couldn't find acceptable audio format!\n");
MP_ERR(ao, "Couldn't find acceptable audio format\n");
return -1;
}
}
}
static int init_clock(struct wasapi_state *state) {
static HRESULT init_clock(struct wasapi_state *state) {
HRESULT hr;
hr = IAudioClient_GetService(state->pAudioClient,
@ -417,16 +421,17 @@ static int init_clock(struct wasapi_state *state) {
atomic_store(&state->sample_count, 0);
MP_VERBOSE(state, "IAudioClock::GetFrequency gave a frequency of %"PRIu64".\n", (uint64_t) state->clock_frequency);
MP_VERBOSE(state, "IAudioClock::GetFrequency gave a frequency of %"PRIu64".\n",
(uint64_t) state->clock_frequency);
return 0;
return S_OK;
exit_label:
MP_ERR(state, "init_clock failed with %s, unable to obtain the audio device's timing!\n",
wasapi_explain_err(hr));
return 1;
MP_ERR(state, "Error obtaining the audio device's timing: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
return hr;
}
static int init_session_display(struct wasapi_state *state) {
static HRESULT init_session_display(struct wasapi_state *state) {
HRESULT hr;
wchar_t path[MAX_PATH+12] = {0};
@ -443,15 +448,14 @@ static int init_session_display(struct wasapi_state *state) {
hr = IAudioSessionControl_SetIconPath(state->pSessionControl, path, NULL);
EXIT_ON_ERROR(hr);
return 0;
return S_OK;
exit_label:
MP_ERR(state, "init_session_display failed with %s.\n",
wasapi_explain_err(hr));
return 1;
MP_ERR(state, "Error setting audio session display name: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
return hr;
}
static int fix_format(struct wasapi_state *state)
static HRESULT fix_format(struct wasapi_state *state)
{
HRESULT hr;
double offset = 0.5;
@ -459,10 +463,13 @@ static int fix_format(struct wasapi_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
*/
MP_DBG(state, "IAudioClient::GetDevicePeriod\n");
hr = IAudioClient_GetDevicePeriod(state->pAudioClient,
&state->defaultRequestedDuration,
&state->minRequestedDuration);
reinit:
MP_DBG(state, "IAudioClient::Initialize\n");
hr = IAudioClient_Initialize(state->pAudioClient,
state->share_mode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
@ -472,10 +479,12 @@ reinit:
NULL);
/* something about buffer sizes on Win7, fixme might loop forever */
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s, used %lld * 100ns\n",
wasapi_explain_err(hr), state->defaultRequestedDuration);
if (offset > 10.0)
goto exit_label; /* is 10 enough to break out of the loop?*/
MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s (0x%"PRIx32"), used %lld * 100ns\n",
wasapi_explain_err(hr), (uint32_t)hr, state->defaultRequestedDuration);
if (offset > 10.0) {
hr = E_FAIL;
EXIT_ON_ERROR(hr);
}
IAudioClient_GetBufferSize(state->pAudioClient, &state->bufferFrameCount);
state->defaultRequestedDuration =
(REFERENCE_TIME)((10000.0 * 1000 / state->format.Format.nSamplesPerSec *
@ -489,19 +498,22 @@ reinit:
goto reinit;
}
EXIT_ON_ERROR(hr);
MP_DBG(state, "IAudioClient::Initialize pRenderClient\n");
hr = IAudioClient_GetService(state->pAudioClient,
&IID_IAudioRenderClient,
(void **)&state->pRenderClient);
EXIT_ON_ERROR(hr);
MP_DBG(state, "IAudioClient::Initialize pAudioVolume\n");
hr = IAudioClient_GetService(state->pAudioClient,
&IID_ISimpleAudioVolume,
(void **) &state->pAudioVolume);
EXIT_ON_ERROR(hr);
if (!state->hFeed)
goto exit_label;
MP_DBG(state, "IAudioClient::Initialize IAudioClient_SetEventHandle\n");
hr = IAudioClient_SetEventHandle(state->pAudioClient, state->hFeed);
EXIT_ON_ERROR(hr);
MP_DBG(state, "IAudioClient::Initialize IAudioClient_GetBufferSize\n");
hr = IAudioClient_GetBufferSize(state->pAudioClient,
&state->bufferFrameCount);
EXIT_ON_ERROR(hr);
@ -509,20 +521,22 @@ reinit:
state->format.Format.wBitsPerSample / 8 *
state->bufferFrameCount;
if (init_clock(state))
return 1;
if (init_session_display(state))
return 1;
hr = init_clock(state);
EXIT_ON_ERROR(hr);
hr = init_session_display(state);
EXIT_ON_ERROR(hr);
state->hTask =
state->VistaBlob.pAvSetMmThreadCharacteristicsW(L"Pro Audio", &state->taskIndex);
MP_VERBOSE(state, "fix_format OK, using %lld byte buffer block size!\n",
MP_VERBOSE(state, "Format fixed. Using %lld byte buffer block size\n",
(long long) state->buffer_block_size);
return 0;
return S_OK;
exit_label:
MP_ERR(state, "fix_format fails with %s, failed to determine buffer block size!\n",
wasapi_explain_err(hr));
return 1;
MP_ERR(state, "Error initializing device: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
return hr;
}
static char* get_device_id(IMMDevice *pDevice) {
@ -683,8 +697,8 @@ static HRESULT enumerate_with_state(struct mp_log *log, struct ao *ao,
talloc_free(defid);
SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator));
return hr;
return S_OK;
exit_label:
talloc_free(defid);
SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice));
@ -708,8 +722,8 @@ int wasapi_enumerate_devices(struct mp_log *log, struct ao *ao,
CoUninitialize();
return 0;
exit_label:
mp_err(log, "Error enumerating devices: HRESULT %08"PRIx32" \"%s\"\n",
(uint32_t)hr, wasapi_explain_err(hr));
mp_err(log, "Error enumerating devices: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
CoUninitialize();
return 1;
}
@ -746,16 +760,16 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
IMMDeviceCollection_GetCount(pDevices, &count);
if (devno >= count) {
MP_ERR(ao, "no device #%d!\n", devno);
MP_ERR(ao, "No device #%d\n", devno);
} else {
MP_VERBOSE(ao, "finding device #%d\n", devno);
MP_VERBOSE(ao, "Finding device #%d\n", devno);
hr = IMMDeviceCollection_Item(pDevices, devno, &pTempDevice);
EXIT_ON_ERROR(hr);
hr = IMMDevice_GetId(pTempDevice, &deviceID);
EXIT_ON_ERROR(hr);
MP_VERBOSE(ao, "found device #%d\n", devno);
MP_VERBOSE(ao, "Found device #%d\n", devno);
}
} else {
hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
@ -766,7 +780,7 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
int count;
IMMDeviceCollection_GetCount(pDevices, &count);
MP_VERBOSE(ao, "finding device %s\n", devid);
MP_VERBOSE(ao, "Finding device %s\n", devid);
IMMDevice *prevDevice = NULL;
@ -784,7 +798,7 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
if (deviceID) {
char *name;
if (!search_err) {
MP_ERR(ao, "multiple matching devices found!\n");
MP_ERR(ao, "Multiple matching devices found\n");
name = get_device_name(prevDevice);
MP_ERR(ao, "%s\n", name);
talloc_free(name);
@ -803,7 +817,7 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
}
if (deviceID == NULL) {
MP_ERR(ao, "could not find device %s!\n", devid);
MP_ERR(ao, "Could not find device %s\n", devid);
}
}
@ -813,12 +827,12 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
if (deviceID == NULL || search_err) {
hr = E_NOTFOUND;
} else {
MP_VERBOSE(ao, "loading device %S\n", deviceID);
MP_VERBOSE(ao, "Loading device %S\n", deviceID);
hr = IMMDeviceEnumerator_GetDevice(pEnumerator, deviceID, ppDevice);
if (FAILED(hr)) {
MP_ERR(ao, "could not load requested device!\n");
MP_ERR(ao, "Could not load requested device\n");
}
}
@ -838,7 +852,7 @@ int wasapi_validate_device(struct mp_log *log, const m_option_t *opt,
return M_OPT_EXIT;
}
mp_dbg(log, "validating device=%s\n", param.start);
mp_dbg(log, "Validating device=%s\n", param.start);
char *end;
int devno = (int) strtol(param.start, &end, 10);
@ -871,8 +885,8 @@ HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
exit_label:
if (hr != S_OK) {
MP_ERR(state, "error reading COM proxy: %08x %s\n", (unsigned int)hr,
wasapi_explain_err(hr));
MP_ERR(state, "Error reading COM proxy: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
}
return hr;
}
@ -907,10 +921,12 @@ exit_label:
return hr;
}
int wasapi_thread_init(struct ao *ao)
HRESULT wasapi_thread_init(struct ao *ao)
{
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
HRESULT hr;
MP_DBG(ao, "Init wasapi thread\n");
state->initial_volume = -1.0;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
char *device = state->opt_device;
@ -929,7 +945,7 @@ int wasapi_thread_init(struct ao *ao)
SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator));
char *id = get_device_id(state->pDevice);
MP_VERBOSE(ao, "default device ID: %s\n", id);
MP_VERBOSE(ao, "Default device ID: %s\n", id);
talloc_free(id);
} else {
hr = find_and_load_device(ao, &state->pDevice, device);
@ -937,43 +953,57 @@ int wasapi_thread_init(struct ao *ao)
EXIT_ON_ERROR(hr);
char *name = get_device_name(state->pDevice);
MP_VERBOSE(ao, "device loaded: %s\n", name);
MP_VERBOSE(ao, "Device loaded: %s\n", name);
talloc_free(name);
MP_DBG(ao, "Activating pAudioClient interface\n");
hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioClient,
CLSCTX_ALL, NULL, (void **)&state->pAudioClient);
EXIT_ON_ERROR(hr);
MP_DBG(ao, "Activating pEndpointVolume interface\n");
hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioEndpointVolume,
CLSCTX_ALL, NULL,
(void **)&state->pEndpointVolume);
EXIT_ON_ERROR(hr);
IAudioEndpointVolume_QueryHardwareSupport(state->pEndpointVolume,
&state->vol_hw_support);
state->init_ret = find_formats(ao); /* Probe support formats */
if (state->init_ret)
goto exit_label;
if (!fix_format(state)) { /* now that we're sure what format to use */
EXIT_ON_ERROR(create_proxies(state));
MP_DBG(ao, "Query hardware volume support\n");
hr = IAudioEndpointVolume_QueryHardwareSupport(state->pEndpointVolume,
&state->vol_hw_support);
if ( hr != S_OK )
MP_WARN(ao, "Query hardware volume control: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
if (state->opt_exclusive)
IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolume,
&state->initial_volume);
else
ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume,
&state->initial_volume);
state->previous_volume = state->initial_volume;
MP_VERBOSE(ao, "thread_init OK!\n");
SetEvent(state->init_done);
return state->init_ret;
MP_DBG(ao, "Probing formats\n");
if (find_formats(ao)){
hr = E_FAIL;
EXIT_ON_ERROR(hr);
}
MP_DBG(ao, "Fixing format\n");
hr = fix_format(state);
EXIT_ON_ERROR(hr);
MP_DBG(ao, "Creating proxies\n");
hr = create_proxies(state);
EXIT_ON_ERROR(hr);
MP_DBG(ao, "Read volume levels\n");
if (state->opt_exclusive)
IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolume,
&state->initial_volume);
else
ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume,
&state->initial_volume);
state->previous_volume = state->initial_volume;
MP_DBG(ao, "Init wasapi thread done\n");
return S_OK;
exit_label:
state->init_ret = -1;
SetEvent(state->init_done);
return -1;
MP_ERR(state, "Error setting up audio thread: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
return hr;
}
void wasapi_thread_uninit(struct ao *ao)
@ -997,6 +1027,5 @@ void wasapi_thread_uninit(struct ao *ao)
if (state->hTask)
state->VistaBlob.pAvRevertMmThreadCharacteristics(state->hTask);
CoUninitialize();
ExitThread(0);
MP_DBG(ao, "Thread uninit done\n");
}

View File

@ -37,7 +37,7 @@ int wasapi_enumerate_devices(struct mp_log *log, struct ao *ao,
int wasapi_validate_device(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param);
int wasapi_thread_init(struct ao *ao);
HRESULT wasapi_thread_init(struct ao *ao);
void wasapi_thread_uninit(struct ao *ao);
HRESULT wasapi_setup_proxies(wasapi_state *state);