diff options
author | Diogo Franco (Kovensky) <diogomfranco@gmail.com> | 2013-07-20 14:13:39 -0300 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2013-07-22 02:42:38 +0200 |
commit | d5adaed9d84317aff546c71c28a338944a68a770 (patch) | |
tree | fa58afa3189fa1c3083f9884582cac67dfbc41ec /audio/out/ao_wasapi0.c | |
parent | 553ed6b32fd6eb6659ba7081cdcab3ad8b82646c (diff) | |
download | mpv-d5adaed9d84317aff546c71c28a338944a68a770.tar.bz2 mpv-d5adaed9d84317aff546c71c28a338944a68a770.tar.xz |
ao_wasapi0: Rename to ao_wasapi
Nobody knows what the 0 was for. There's no "WASAPI version 0". Just take
it out.
Diffstat (limited to 'audio/out/ao_wasapi0.c')
-rw-r--r-- | audio/out/ao_wasapi0.c | 1194 |
1 files changed, 0 insertions, 1194 deletions
diff --git a/audio/out/ao_wasapi0.c b/audio/out/ao_wasapi0.c deleted file mode 100644 index 8f40386aad..0000000000 --- a/audio/out/ao_wasapi0.c +++ /dev/null @@ -1,1194 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#define COBJMACROS 1 -#define _WIN32_WINNT 0x600 - -#include <stdlib.h> -#include <inttypes.h> -#include <process.h> -#include <initguid.h> -#include <audioclient.h> -#include <endpointvolume.h> -#include <mmdeviceapi.h> -#include <avrt.h> - -#include "config.h" -#include "core/subopt-helper.h" -#include "core/m_option.h" -#include "core/m_config.h" -#include "audio/format.h" -#include "core/mp_msg.h" -#include "core/mp_ring.h" -#include "ao.h" - -#ifndef BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE -#define BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE 0x00000001 -#endif - -#ifndef PKEY_Device_FriendlyName -DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, - 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, - 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); -#endif - -#define RING_BUFFER_COUNT 64 - -/* 20 millisecond buffer? */ -#define BUFFER_TIME 20000000.0 -#define EXIT_ON_ERROR(hres) \ - if (FAILED(hres)) { goto exit_label; } -#define SAFE_RELEASE(unk, release) \ - if ((unk) != NULL) { release; (unk) = NULL; } - -/* Supposed to use __uuidof, but it is C++ only, declare our own */ -static const GUID local_KSDATAFORMAT_SUBTYPE_PCM = { - 0x1, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -}; -static const GUID local_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { - 0x3, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -}; - -union WAVEFMT { - WAVEFORMATEX *ex; - WAVEFORMATEXTENSIBLE *extensible; -}; - -typedef struct wasapi0_state { - HANDLE threadLoop; - - /* Init phase */ - int init_ret; - HANDLE init_done; - HANDLE fatal_error; /* signal to indicate unrecoverable error */ - int share_mode; - - /* Events */ - HANDLE hPause; - - /* Play */ - HANDLE hPlay; - int is_playing; - - /* Reset */ - HANDLE hReset; - - /* uninit */ - HANDLE hUninit; - LONG immed; - - /* volume control */ - HANDLE hGetvol, hSetvol, hDoneVol; - DWORD vol_hw_support, status; - float audio_volume; - - /* Prints, for in case line buffers are disabled */ - CRITICAL_SECTION print_lock; - - /* Buffers */ - struct mp_ring *ringbuff; - size_t buffer_block_size; /* Size of each block in bytes */ - 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 */ - - /* WASAPI handles, owned by other thread */ - IMMDeviceEnumerator *pEnumerator; - IMMDevice *pDevice; - IAudioClient *pAudioClient; - IAudioRenderClient *pRenderClient; - IAudioEndpointVolume *pEndpointVolume; - HANDLE hFeed; /* wasapi event */ - HANDLE hTask; /* AV thread */ - DWORD taskIndex; /* AV task ID */ - WAVEFORMATEXTENSIBLE format; - - int opt_exclusive; - int opt_list; - char *opt_device; - - /* We still need to support XP, don't use these functions directly, blob owned by main thread */ - struct { - HMODULE hAvrt; - HANDLE (WINAPI *pAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD); - WINBOOL (WINAPI *pAvRevertMmThreadCharacteristics)(HANDLE); - } VistaBlob; -} wasapi0_state; - -static int fill_VistaBlob(wasapi0_state *state) -{ - if (!state) - return 1; - HMODULE hkernel32 = GetModuleHandleW(L"kernel32.dll"); - if (!hkernel32) - return 1; - WINBOOL (WINAPI *pSetDllDirectory)(LPCWSTR lpPathName) = - (WINBOOL (WINAPI *)(LPCWSTR))GetProcAddress(hkernel32, "SetDllDirectoryW"); - WINBOOL (WINAPI *pSetSearchPathMode)(DWORD Flags) = - (WINBOOL (WINAPI *)(DWORD))GetProcAddress(hkernel32, "SetSearchPathMode"); - if (pSetSearchPathMode) - pSetDllDirectory(L""); /* Attempt to use safe search paths */ - if (pSetSearchPathMode) - pSetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE); - state->VistaBlob.hAvrt = LoadLibraryW(L"avrt.dll"); - if (!state->VistaBlob.hAvrt) - goto exit_label; - state->VistaBlob.pAvSetMmThreadCharacteristicsW = - (HANDLE (WINAPI *)(LPCWSTR, LPDWORD)) - GetProcAddress(state->VistaBlob.hAvrt, "AvSetMmThreadCharacteristicsW"); - state->VistaBlob.pAvRevertMmThreadCharacteristics = - (WINBOOL (WINAPI *)(HANDLE)) - GetProcAddress(state->VistaBlob.hAvrt, "AvRevertMmThreadCharacteristics"); - return 0; -exit_label: - if (state->VistaBlob.hAvrt) - FreeLibrary(state->VistaBlob.hAvrt); - if (pSetSearchPathMode) - pSetDllDirectory(NULL); - return 1; -} - -static const char *explain_err(const HRESULT hr) -{ - switch (hr) { - case S_OK: - return "S_OK"; - case AUDCLNT_E_NOT_INITIALIZED: - return "AUDCLNT_E_NOT_INITIALIZED"; - case AUDCLNT_E_ALREADY_INITIALIZED: - return "AUDCLNT_E_ALREADY_INITIALIZED"; - case AUDCLNT_E_WRONG_ENDPOINT_TYPE: - return "AUDCLNT_E_WRONG_ENDPOINT_TYPE"; - case AUDCLNT_E_DEVICE_INVALIDATED: - return "AUDCLNT_E_DEVICE_INVALIDATED"; - case AUDCLNT_E_NOT_STOPPED: - return "AUDCLNT_E_NOT_STOPPED"; - case AUDCLNT_E_BUFFER_TOO_LARGE: - return "AUDCLNT_E_BUFFER_TOO_LARGE"; - case AUDCLNT_E_OUT_OF_ORDER: - return "AUDCLNT_E_OUT_OF_ORDER"; - case AUDCLNT_E_UNSUPPORTED_FORMAT: - return "AUDCLNT_E_UNSUPPORTED_FORMAT"; - case AUDCLNT_E_INVALID_SIZE: - return "AUDCLNT_E_INVALID_SIZE"; - case AUDCLNT_E_DEVICE_IN_USE: - return "AUDCLNT_E_DEVICE_IN_USE"; - case AUDCLNT_E_BUFFER_OPERATION_PENDING: - return "AUDCLNT_E_BUFFER_OPERATION_PENDING"; - case AUDCLNT_E_THREAD_NOT_REGISTERED: - return "AUDCLNT_E_THREAD_NOT_REGISTERED"; - case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: - return "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; - case AUDCLNT_E_ENDPOINT_CREATE_FAILED: - return "AUDCLNT_E_ENDPOINT_CREATE_FAILED"; - case AUDCLNT_E_SERVICE_NOT_RUNNING: - return "AUDCLNT_E_SERVICE_NOT_RUNNING"; - case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: - return "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; - case AUDCLNT_E_EXCLUSIVE_MODE_ONLY: - return "AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; - case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: - return "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; - case AUDCLNT_E_EVENTHANDLE_NOT_SET: - return "AUDCLNT_E_EVENTHANDLE_NOT_SET"; - case AUDCLNT_E_INCORRECT_BUFFER_SIZE: - return "AUDCLNT_E_INCORRECT_BUFFER_SIZE"; - case AUDCLNT_E_BUFFER_SIZE_ERROR: - return "AUDCLNT_E_BUFFER_SIZE_ERROR"; - case AUDCLNT_E_CPUUSAGE_EXCEEDED: - return "AUDCLNT_E_CPUUSAGE_EXCEEDED"; - case AUDCLNT_E_BUFFER_ERROR: - return "AUDCLNT_E_BUFFER_ERROR"; - case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: - return "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; - case AUDCLNT_E_INVALID_DEVICE_PERIOD: - return "AUDCLNT_E_INVALID_DEVICE_PERIOD"; - case AUDCLNT_E_INVALID_STREAM_FLAG: - return "AUDCLNT_E_INVALID_STREAM_FLAG"; - case AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: - return "AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE"; - case AUDCLNT_E_RESOURCES_INVALIDATED: - return "AUDCLNT_E_RESOURCES_INVALIDATED"; - case AUDCLNT_S_BUFFER_EMPTY: - return "AUDCLNT_S_BUFFER_EMPTY"; - case AUDCLNT_S_THREAD_ALREADY_REGISTERED: - return "AUDCLNT_S_THREAD_ALREADY_REGISTERED"; - case AUDCLNT_S_POSITION_STALLED: - return "AUDCLNT_S_POSITION_STALLED"; - default: - return "<Unknown>"; - } -} - -static void set_format(WAVEFORMATEXTENSIBLE *wformat, WORD bytepersample, - DWORD samplerate, WORD channels, DWORD chanmask) -{ - wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; /* Only PCM is supported */ - wformat->Format.nChannels = channels; - wformat->Format.nSamplesPerSec = samplerate; - wformat->Format.nAvgBytesPerSec = wformat->Format.nChannels * - bytepersample * - wformat->Format.nSamplesPerSec; - wformat->Format.nBlockAlign = wformat->Format.nChannels * bytepersample; - wformat->Format.wBitsPerSample = bytepersample * 8; - wformat->Format.cbSize = - 22; /* must be at least 22 for WAVE_FORMAT_EXTENSIBLE */ - if (bytepersample == 4) - wformat->SubFormat = local_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - else - wformat->SubFormat = local_KSDATAFORMAT_SUBTYPE_PCM; - wformat->Samples.wValidBitsPerSample = wformat->Format.wBitsPerSample; - wformat->dwChannelMask = chanmask; -} - -static int format_set_bits(int old_format, int bits, int fp) { - int format = old_format; - format &= (~AF_FORMAT_BITS_MASK) & (~AF_FORMAT_POINT_MASK) & (~AF_FORMAT_SIGN_MASK); - format |= AF_FORMAT_SI; - - switch (bits) { - case 32: - format |= AF_FORMAT_32BIT; - break; - case 24: - format |= AF_FORMAT_24BIT; - break; - case 16: - format |= AF_FORMAT_16BIT; - break; - default: - abort(); // (should be) unreachable - } - - if (fp) { - format |= AF_FORMAT_F; - } else { - format |= AF_FORMAT_I; - } - - return format; -} - -static int set_ao_format(struct wasapi0_state *state, - struct ao *const ao, - WAVEFORMATEXTENSIBLE wformat) { - // .Data1 == 1 is PCM, .Data1 == 3 is IEEE_FLOAT - int format = format_set_bits(ao->format, - wformat.Format.wBitsPerSample, wformat.SubFormat.Data1 == 3); - - if (wformat.SubFormat.Data1 != 1 && wformat.SubFormat.Data1 != 3) { - mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: unknown SubFormat %d\n", - wformat.SubFormat.Data1); - return 0; - } - - ao->samplerate = wformat.Format.nSamplesPerSec; - ao->bps = wformat.Format.nAvgBytesPerSec; - ao->format = format; - - if (ao->channels.num != wformat.Format.nChannels) { - mp_chmap_from_channels(&ao->channels, wformat.Format.nChannels); - } - - state->format = wformat; - return 1; -} - -static int try_format(struct wasapi0_state *state, - struct ao *const ao, - int bits, int samplerate, - const struct mp_chmap channels) -{ - WAVEFORMATEXTENSIBLE wformat; - set_format(&wformat, bits / 8, samplerate, channels.num, mp_chmap_to_waveext(&channels)); - - int af_format = format_set_bits(ao->format, bits, bits == 32); - - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: trying %dch %s @ %dhz\n", - channels.num, af_fmt2str_short(af_format), samplerate); - LeaveCriticalSection(&state->print_lock); - - union WAVEFMT u; - u.extensible = &wformat; - - WAVEFORMATEX *closestMatch; - HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient, - state->share_mode, - u.ex, &closestMatch); - - if (closestMatch) { - if (closestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - u.ex = closestMatch; - wformat = *u.extensible; - } else { - wformat.Format = *closestMatch; - } - - CoTaskMemFree(closestMatch); - } - - if (hr == S_FALSE) { - if (set_ao_format(state, ao, wformat)) { - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: accepted as %dch %s @ %dhz\n", - ao->channels.num, af_fmt2str_short(ao->format), ao->samplerate); - LeaveCriticalSection(&state->print_lock); - - return 1; - } - } if (hr == S_OK || (!state->opt_exclusive && hr == AUDCLNT_E_UNSUPPORTED_FORMAT)) { - // AUDCLNT_E_UNSUPPORTED_FORMAT here means "works in shared, doesn't in exclusive" - if (set_ao_format(state, ao, wformat)) { - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: %dch %s @ %dhz accepted\n", - ao->channels.num, af_fmt2str_short(af_format), samplerate); - LeaveCriticalSection(&state->print_lock); - return 1; - } - } - return 0; -} - -static int try_mix_format(struct wasapi0_state *state, - struct ao *const ao) -{ - WAVEFORMATEX *deviceFormat = NULL; - WAVEFORMATEX *closestMatch = NULL; - int ret = 0; - - HRESULT hr = IAudioClient_GetMixFormat(state->pAudioClient, &deviceFormat); - EXIT_ON_ERROR(hr) - - union WAVEFMT u; - u.ex = deviceFormat; - WAVEFORMATEXTENSIBLE wformat = *u.extensible; - - ret = try_format(state, ao, wformat.Format.wBitsPerSample, - wformat.Format.nSamplesPerSec, ao->channels); - if (ret) - state->format = wformat; - -exit_label: - SAFE_RELEASE(deviceFormat, CoTaskMemFree(deviceFormat)); - SAFE_RELEASE(closestMatch, CoTaskMemFree(closestMatch)); - return ret; -} - -static int find_formats(struct ao *const ao) -{ - struct wasapi0_state *state = (struct wasapi0_state *)ao->priv; - - /* See if the format works as-is */ - int bits = af_fmt2bits(ao->format); - /* don't try 8bits -- there are various 8bit modes other than PCM (*-law et al); - let's just stick to PCM or float here. */ - if (bits == 8) { - bits = 16; - } else if (try_format(state, ao, bits, ao->samplerate, ao->channels)) { - return 0; - } - if (!state->opt_exclusive) { - /* shared mode, we can use the system default mix format. */ - if (try_mix_format(state, ao)) { - return 0; - } - - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_WARN, "ao-wasapi: couldn't use default mix format!\n"); - LeaveCriticalSection(&state->print_lock); - } - - /* Exclusive mode, we have to guess. */ - - /* as far as testing shows, only PCM 16/24LE (44100Hz - 192kHz) is supported - * Tested on Realtek High Definition Audio, (Realtek Semiconductor Corp. 6.0.1.6312) - * Drivers dated 2/18/2011 - */ - - /* try float first for non-16bit audio */ - if (bits != 16) { - bits = 32; - } - - int start_bits = bits; - while (1) { // not infinite -- returns at bottom - for (; bits > 8; bits -= 8) { - int samplerate = ao->samplerate; - if (try_format(state, ao, bits, samplerate, ao->channels)) { - return 0; - } - - // make samplerate fit in [44100 192000] - // we check for samplerate > 96k so that we can upsample instead of downsampling later - if (samplerate < 44100 || samplerate > 96000) { - if (samplerate < 44100) - samplerate = 44100; - if (samplerate > 96000) - samplerate = 192000; - - if (try_format(state, ao, bits, samplerate, ao->channels)) { - return 0; - } - } - - // try bounding to 96kHz - if (samplerate > 48000) { - samplerate = 96000; - if (try_format(state, ao, bits, samplerate, ao->channels)) { - return 0; - } - } - - // try bounding to 48kHz - if (samplerate > 44100) { - samplerate = 48000; - if (try_format(state, ao, bits, samplerate, ao->channels)) { - return 0; - } - } - - /* How bad is this? try 44100hz, but only on 16bit */ - if (bits == 16 && samplerate != 44100) { - samplerate = 44100; - - if (try_format(state, ao, bits, samplerate, ao->channels)) { - return 0; - } - } - } - if (ao->channels.num > 6) { - /* Maybe this is 5.1 hardware with no support for more. */ - bits = start_bits; - mp_chmap_from_channels(&ao->channels, 6); - } else if (ao->channels.num != 2) { - /* Poor quality hardware? Try stereo mode, go through the list again. */ - bits = start_bits; - mp_chmap_from_channels(&ao->channels, 2); - } else { - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: couldn't find acceptable audio format!\n"); - LeaveCriticalSection(&state->print_lock); - return -1; - } - } -} - -static int fix_format(struct wasapi0_state *state) -{ - HRESULT hr; - double offset = 0.5; - - /* 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 - */ - hr = IAudioClient_GetDevicePeriod(state->pAudioClient, - &state->defaultRequestedDuration, - &state->minRequestedDuration); -reinit: - hr = IAudioClient_Initialize(state->pAudioClient, - state->share_mode, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - state->defaultRequestedDuration, - state->defaultRequestedDuration, - &(state->format.Format), - NULL); - /* something about buffer sizes on Win7, fixme might loop forever */ - if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { - EnterCriticalSection(&state->print_lock); - mp_msg( - MSGT_AO, MSGL_V, - "ao-wasapi: IAudioClient::Initialize negotiation failed with %s, used %lld * 100ns\n", - explain_err(hr), state->defaultRequestedDuration); - LeaveCriticalSection(&state->print_lock); - if (offset > 10.0) - goto exit_label; /* is 10 enough to break out of the loop?*/ - IAudioClient_GetBufferSize(state->pAudioClient, &state->bufferFrameCount); - state->defaultRequestedDuration = - (REFERENCE_TIME)((BUFFER_TIME / state->format.Format.nSamplesPerSec * - state->bufferFrameCount) + offset); - offset += 0.5; - IAudioClient_Release(state->pAudioClient); - state->pAudioClient = NULL; - hr = IMMDeviceActivator_Activate(state->pDevice, - &IID_IAudioClient, CLSCTX_ALL, - NULL, (void **)&state->pAudioClient); - goto reinit; - } - EXIT_ON_ERROR(hr) - hr = IAudioClient_GetService(state->pAudioClient, - &IID_IAudioRenderClient, - (void **)&state->pRenderClient); - EXIT_ON_ERROR(hr) - if (!state->hFeed) - goto exit_label; - hr = IAudioClient_SetEventHandle(state->pAudioClient, state->hFeed); - EXIT_ON_ERROR(hr) - hr = IAudioClient_GetBufferSize(state->pAudioClient, - &state->bufferFrameCount); - EXIT_ON_ERROR(hr) - state->buffer_block_size = state->format.Format.nChannels * - state->format.Format.wBitsPerSample / 8 * - state->bufferFrameCount; - state->hTask = - state->VistaBlob.pAvSetMmThreadCharacteristicsW(L"Pro Audio", &state->taskIndex); - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_V, - "ao-wasapi: fix_format OK, using %lld byte buffer block size!\n", - (long long) state->buffer_block_size); - LeaveCriticalSection(&state->print_lock); - return 0; -exit_label: - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_ERR, - "ao-wasapi: fix_format fails with %s, failed to determine buffer block size!\n", - explain_err(hr)); - LeaveCriticalSection(&state->print_lock); - SetEvent(state->fatal_error); - return 1; -} - -static HRESULT enumerate_with_state(char *header, int status, int with_id) { - HRESULT hr; - IMMDeviceEnumerator *pEnumerator = NULL; - IMMDeviceCollection *pDevices = NULL; - IMMDevice *pDevice = NULL; - IPropertyStore *pProps = NULL; - LPWSTR idStr = NULL; - - CoInitialize(NULL); - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, - &IID_IMMDeviceEnumerator, (void **)&pEnumerator); - EXIT_ON_ERROR(hr) - - hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender, - status, &pDevices); - EXIT_ON_ERROR(hr) - - int count; - IMMDeviceCollection_GetCount(pDevices, &count); - if (count > 0) { - mp_msg(MSGT_AO, MSGL_INFO, "ao-wasapi: %s\n", header); - } - - for (int i = 0; i < count; i++) { - hr = IMMDeviceCollection_Item(pDevices, i, &pDevice); - EXIT_ON_ERROR(hr) - - hr = IMMDevice_GetId(pDevice, &idStr); - EXIT_ON_ERROR(hr) - - hr = IMMDevice_OpenPropertyStore(pDevice, STGM_READ, &pProps); - EXIT_ON_ERROR(hr) - - PROPVARIANT varName; - PropVariantInit(&varName); - - hr = IPropertyStore_GetValue(pProps, &PKEY_Device_FriendlyName, &varName); - EXIT_ON_ERROR(hr) - - if (with_id) { - mp_msg(MSGT_AO, MSGL_INFO, "ao-wasapi: Endpoint #%d: \"%S\" (ID \"%S\")\n", - i, varName.pwszVal, idStr); - } else { - mp_msg(MSGT_AO, MSGL_INFO, "ao-wasapi: Endpoint: \"%S\" (ID \"%S\")\n", - varName.pwszVal, idStr); - } - - CoTaskMemFree(idStr); - idStr = NULL; - PropVariantClear(&varName); - SAFE_RELEASE(pProps, IPropertyStore_Release(pProps)); - SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice)); - } - SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices)); - SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator)); - return hr; - -exit_label: - CoTaskMemFree(idStr); - SAFE_RELEASE(pProps, IPropertyStore_Release(pProps)); - SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice)); - SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices)); - SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator)); - return hr; -} - -static int enumerate_devices(void) { - HRESULT hr; - hr = enumerate_with_state("Active devices:", DEVICE_STATE_ACTIVE, 1); - EXIT_ON_ERROR(hr) - hr = enumerate_with_state("Unplugged devices:", DEVICE_STATE_UNPLUGGED, 0); - EXIT_ON_ERROR(hr) - return 0; -exit_label: - mp_msg(MSGT_AO, MSGL_ERR, "Error enumerating devices: HRESULT %08x \"%s\"\n", - hr, explain_err(hr)); - return 1; -} - -static HRESULT find_and_load_device(wasapi0_state *state, int devno, char *devid) { - HRESULT hr; - IMMDeviceCollection *pDevices = NULL; - IMMDevice *pTempDevice = NULL; - LPWSTR deviceID = NULL; - LPWSTR tmpID = NULL; - char *tmpStr = NULL; - - hr = IMMDeviceEnumerator_EnumAudioEndpoints(state->pEnumerator, eRender, - DEVICE_STATE_ACTIVE, &pDevices); - EXIT_ON_ERROR(hr) - - int count; - IMMDeviceCollection_GetCount(pDevices, &count); - - if (devid == NULL) { - if (devno >= count) { - mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: no endpoind #%d!\n", devno); - } else { - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: finding endpoint #%d\n", devno); - hr = IMMDeviceCollection_Item(pDevices, devno, &pTempDevice); - EXIT_ON_ERROR(hr) - - hr = IMMDevice_GetId(pTempDevice, &deviceID); - EXIT_ON_ERROR(hr) - - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: found endpoint #%d\n", devno); - } - } else { - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: finding endpoint \"%s\"\n", devid); - - for (int i = 0; i < count; i++) { - hr = IMMDeviceCollection_Item(pDevices, i, &pTempDevice); - EXIT_ON_ERROR(hr) - - hr = IMMDevice_GetId(pTempDevice, &tmpID); - EXIT_ON_ERROR(hr) - - int len = WideCharToMultiByte(CP_UTF8, 0, tmpID, -1, NULL, 0, NULL, NULL); - tmpStr = malloc(len); - WideCharToMultiByte(CP_UTF8, 0, tmpID, -1, tmpStr, len, NULL, NULL); - - if (strcmp(devid, tmpStr) == 0) { - deviceID = tmpID; - break; - } - - SAFE_RELEASE(tmpStr, free(tmpStr)); - SAFE_RELEASE(tmpID, CoTaskMemFree(tmpID)); - SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice)); - } - if (deviceID == NULL) { - mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: could not find endpoint \"%s\"!\n", devid); - } - } - - SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice)); - SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices)) - - if (deviceID == NULL) { - mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: no endpoint to load!\n"); - } else { - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: loading endpoint %S\n", deviceID); - - hr = IMMDeviceEnumerator_GetDevice(state->pEnumerator, deviceID, &state->pDevice); - - if (FAILED(hr)) { - mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: could not load requested endpoint!\n"); - } - } - -exit_label: - SAFE_RELEASE(tmpStr, free(tmpStr)); - SAFE_RELEASE(tmpID, CoTaskMemFree(tmpID)); - SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice)); - SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices)); - return hr; -} - -static int thread_init(struct ao *ao) -{ - struct wasapi0_state *state = (struct wasapi0_state *)ao->priv; - HRESULT hr; - CoInitialize(NULL); - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, - &IID_IMMDeviceEnumerator, (void **)&state->pEnumerator); - EXIT_ON_ERROR(hr) - - if (!state->opt_device) { - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: using default endpoint\n"); - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(state->pEnumerator, - eRender, eConsole, - &state->pDevice); - } else { - int devno = -1; - char *devid = NULL; - - if (state->opt_device[0] == '{') { // ID as printed by list - devid = state->opt_device; - } else { // assume integer - char *end; - devno = (int) strtol(state->opt_device, &end, 10); - - if (*end != '\0' || devno < 0) { - mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: invalid device number %s!", state->opt_device); - goto exit_label; - } - } - hr = find_and_load_device(state, devno, devid); - } - EXIT_ON_ERROR(hr) - - hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioClient, - CLSCTX_ALL, NULL, (void **)&state->pAudioClient); - EXIT_ON_ERROR(hr) - - 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 */ - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: thread_init OK!\n"); - LeaveCriticalSection(&state->print_lock); - SetEvent(state->init_done); - return state->init_ret; - } -exit_label: - state->init_ret = -1; - SetEvent(state->init_done); - return -1; -} - -static void thread_pause(wasapi0_state *state) -{ - state->is_playing = 0; - IAudioClient_Stop(state->pAudioClient); -} - -/* 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; - - UINT32 frame_count = state->bufferFrameCount; - UINT32 client_buffer = state->buffer_block_size; - - if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) { - UINT32 padding = 0; - hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding); - EXIT_ON_ERROR(hr) - - frame_count -= padding; - client_buffer = state->format.Format.nBlockAlign * frame_count; - } - - hr = IAudioRenderClient_GetBuffer(state->pRenderClient, - frame_count, &pData); - EXIT_ON_ERROR(hr) - buffer_size = mp_ring_buffered(state->ringbuff); - if(buffer_size > client_buffer) { /* OK to copy! */ - mp_ring_read(state->ringbuff, (unsigned char *)pData, - client_buffer); - } else if(force_feed) { - /* should be smaller than buffer block size by now */ - memset(pData,0,client_buffer); - mp_ring_read(state->ringbuff, (unsigned char *)pData, client_buffer); - } else { - /* buffer underrun?! abort */ - hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient, - frame_count, - AUDCLNT_BUFFERFLAGS_SILENT); - EXIT_ON_ERROR(hr) - return; - } - hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient, - frame_count, 0); - EXIT_ON_ERROR(hr) - return; -exit_label: - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: thread_feed fails with %"PRIx32"!\n", hr); - LeaveCriticalSection(&state->print_lock); - return; -} - -static void thread_play(wasapi0_state *state) -{ - thread_feed(state, 0); - state->is_playing = 1; - IAudioClient_Start(state->pAudioClient); - return; -} - -static void thread_reset(wasapi0_state *state) -{ - IAudioClient_Stop(state->pAudioClient); - IAudioClient_Reset(state->pAudioClient); - if (state->is_playing) { - thread_play(state); - } -} - -static void thread_getVol(wasapi0_state *state) -{ - IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolume, - &state->audio_volume); - SetEvent(state->hDoneVol); -} - -static void thread_setVol(wasapi0_state *state) -{ - IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolume, - state->audio_volume, NULL); - SetEvent(state->hDoneVol); -} - -static void thread_uninit(wasapi0_state *state) -{ - if (!state->immed) { - /* feed until empty */ - while (1) { - if (WaitForSingleObject(state->hFeed,2000) == WAIT_OBJECT_0 && - mp_ring_buffered(state->ringbuff)) - { - thread_feed(state, 1); - } else - break; - } - } - if (state->pAudioClient) - IAudioClient_Stop(state->pAudioClient); - if (state->pRenderClient) - IAudioRenderClient_Release(state->pRenderClient); - if (state->pAudioClient) - IAudioClient_Release(state->pAudioClient); - if (state->pEnumerator) - IMMDeviceEnumerator_Release(state->pEnumerator); - if (state->pDevice) - IMMDevice_Release(state->pDevice); - if (state->hTask) - state->VistaBlob.pAvRevertMmThreadCharacteristics(state->hTask); - CoUninitialize(); - ExitThread(0); -} - -static unsigned int __stdcall ThreadLoop(void *lpParameter) -{ - struct ao *ao = lpParameter; - int feedwatch = 0; - if (!ao || !ao->priv) - return -1; - struct wasapi0_state *state = (struct wasapi0_state *)ao->priv; - if (thread_init(ao)) - goto exit_label; - - DWORD waitstatus = WAIT_FAILED; - HANDLE playcontrol[] = - {state->hUninit, state->hPause, state->hReset, state->hGetvol, - state->hSetvol, state->hPlay, state->hFeed, NULL}; - EnterCriticalSection(&state->print_lock); - mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: Entering dispatch loop!\n"); - LeaveCriticalSection(&state->print_lock); - while (1) { /* watch events, poll at least every 2 seconds */ - waitstatus = WaitForMultipleObjects(7, playcontrol, FALSE, 2000); - switch (waitstatus) { - case WAIT_OBJECT_0: /*shutdown*/ - feedwatch = 0; - thread_uninit(state); - goto exit_label; - case (WAIT_OBJECT_0 + 1): /* pause */ - feedwatch = 0; - thread_pause(state); - break; - case (WAIT_OBJECT_0 + 2): /* reset */ - feedwatch = 0; - thread_reset(state); - break; - case (WAIT_OBJECT_0 + 3): /* getVolume */ - thread_getVol(state); - break; - case (WAIT_OBJECT_0 + 4): /* setVolume */ - thread_setVol(state); - break; - case (WAIT_OBJECT_0 + 5): /* play */ - feedwatch = 0; - thread_play(state); - break; - case (WAIT_OBJECT_0 + 6): /* feed */ - feedwatch = 1; - thread_feed(state, 0); |