diff options
author | Kevin Mitchell <kevmitch@gmail.com> | 2014-11-17 05:02:18 -0800 |
---|---|---|
committer | Kevin Mitchell <kevmitch@gmail.com> | 2014-11-17 05:02:18 -0800 |
commit | 313f1954c527e4ff45dbc37a567b2d18d0a6c65b (patch) | |
tree | 5e75137dc37b583eaa78f40aead4a56091f82bfc /audio/out/ao_wasapi_utils.c | |
parent | f4804b0c45c4f63ea0f92e9dbaa83f8271beefa5 (diff) | |
parent | 4c8b841fc492e21d0c079dc1217424f06beb72b7 (diff) | |
download | mpv-313f1954c527e4ff45dbc37a567b2d18d0a6c65b.tar.bz2 mpv-313f1954c527e4ff45dbc37a567b2d18d0a6c65b.tar.xz |
Merge branch 'wasapi_fix2'
Diffstat (limited to 'audio/out/ao_wasapi_utils.c')
-rwxr-xr-x | audio/out/ao_wasapi_utils.c | 275 |
1 files changed, 168 insertions, 107 deletions
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c index 043183d32a..681c32ecde 100755 --- a/audio/out/ao_wasapi_utils.c +++ b/audio/out/ao_wasapi_utils.c @@ -30,14 +30,10 @@ #include "audio/format.h" #include "osdep/io.h" +#include "osdep/timer.h" #define MIXER_DEFAULT_LABEL L"mpv - video player" -#define EXIT_ON_ERROR(hres) \ - do { if (FAILED(hres)) { goto exit_label; } } while(0) -#define SAFE_RELEASE(unk, release) \ - do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0) - #ifndef PKEY_Device_FriendlyName DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, @@ -85,6 +81,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 +160,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 +196,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 +220,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 +283,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 +304,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 +326,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 +397,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 +417,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 +444,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 +459,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 +475,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 +494,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 +517,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) { @@ -635,7 +645,7 @@ static HRESULT enumerate_with_state(struct mp_log *log, struct ao *ao, EXIT_ON_ERROR(hr); hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, - eRender, eConsole, + eRender, eMultimedia, &pDevice); EXIT_ON_ERROR(hr); @@ -683,8 +693,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,17 +718,35 @@ 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; } -static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice, - char *search) +static HRESULT load_default_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator, + IMMDevice **ppDevice) +{ + HRESULT hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, + eRender, eMultimedia, + ppDevice); + EXIT_ON_ERROR(hr); + + char *id = get_device_id(*ppDevice); + MP_VERBOSE(ao, "Default device ID: %s\n", id); + talloc_free(id); + + return S_OK; +exit_label: + MP_ERR(ao , "Error loading default device: %s (0x%"PRIx32")\n", + wasapi_explain_err(hr), (uint32_t)hr); + return hr; +} + +static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator, + IMMDevice **ppDevice, char *search) { HRESULT hr; - IMMDeviceEnumerator *pEnumerator = NULL; IMMDeviceCollection *pDevices = NULL; IMMDevice *pTempDevice = NULL; LPWSTR deviceID = NULL; @@ -731,10 +759,6 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice, devid = search; } - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, - &IID_IMMDeviceEnumerator, (void**)&pEnumerator); - EXIT_ON_ERROR(hr); - int search_err = 0; if (devid == NULL) { @@ -746,16 +770,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 +790,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 +808,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 +827,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,19 +837,20 @@ 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"); } } exit_label: SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice)); SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices)); - SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator)); + + CoTaskMemFree(deviceID); return hr; } @@ -837,7 +862,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); @@ -870,8 +895,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; } @@ -906,81 +931,117 @@ exit_label: return hr; } -int wasapi_thread_init(struct ao *ao) +void wasapi_dispatch(void) +{ + /* dispatch any possible pending messages */ + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + DispatchMessage(&msg); +} + +HRESULT wasapi_thread_init(struct ao *ao) { struct wasapi_state *state = (struct wasapi_state *)ao->priv; HRESULT hr; - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + MP_DBG(ao, "Init wasapi thread\n"); + int64_t retry_wait = 1; +retry: + state->initial_volume = -1.0; + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, + &IID_IMMDeviceEnumerator, (void**)&state->pEnumerator); + EXIT_ON_ERROR(hr); char *device = state->opt_device; if (!device || !device[0]) device = ao->device; - if (!device || !device[0]) { - IMMDeviceEnumerator *pEnumerator; - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, - &IID_IMMDeviceEnumerator, (void**)&pEnumerator); - EXIT_ON_ERROR(hr); - - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, - eRender, eConsole, - &state->pDevice); - SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator)); - char *id = get_device_id(state->pDevice); - MP_VERBOSE(ao, "default device ID: %s\n", id); - talloc_free(id); - } else { - hr = find_and_load_device(ao, &state->pDevice, device); - } + if (!device || !device[0]) + hr = load_default_device(ao, state->pEnumerator, &state->pDevice); + else + hr = find_and_load_device(ao, state->pEnumerator, &state->pDevice, device); 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)); - if (state->opt_exclusive) - IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolume, - &state->initial_volume); - else - ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume, - &state->initial_volume); + 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); - state->previous_volume = state->initial_volume; + MP_DBG(ao, "Probing formats\n"); + if (find_formats(ao)){ + hr = E_FAIL; + EXIT_ON_ERROR(hr); + } - MP_VERBOSE(ao, "thread_init OK!\n"); - SetEvent(state->init_done); - return state->init_ret; + MP_DBG(ao, "Fixing format\n"); + hr = fix_format(state); + if ( (hr == AUDCLNT_E_DEVICE_IN_USE || + hr == AUDCLNT_E_DEVICE_INVALIDATED) && + retry_wait <= 8 ) { + wasapi_thread_uninit(ao); + MP_WARN(ao, "Retrying in %ld us\n", retry_wait); + mp_sleep_us(retry_wait); + retry_wait *= 2; + goto retry; } + 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; + + wasapi_change_init(ao); + + 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(wasapi_state *state) +void wasapi_thread_uninit(struct ao *ao) { + struct wasapi_state *state = (struct wasapi_state *)ao->priv; + + wasapi_dispatch(); + if (state->pAudioClient) IAudioClient_Stop(state->pAudioClient); - if (state->opt_exclusive) + wasapi_change_uninit(ao); + + if (state->opt_exclusive && + state->pEndpointVolume && + state->initial_volume > 0 ) IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolume, state->initial_volume, NULL); @@ -991,9 +1052,9 @@ void wasapi_thread_uninit(wasapi_state *state) SAFE_RELEASE(state->pSessionControl, IAudioSessionControl_Release(state->pSessionControl)); SAFE_RELEASE(state->pAudioClient, IAudioClient_Release(state->pAudioClient)); SAFE_RELEASE(state->pDevice, IMMDevice_Release(state->pDevice)); + SAFE_RELEASE(state->pEnumerator, IMMDeviceEnumerator_Release(state->pEnumerator)); if (state->hTask) state->VistaBlob.pAvRevertMmThreadCharacteristics(state->hTask); - CoUninitialize(); - ExitThread(0); + MP_DBG(ao, "Thread uninit done\n"); } |