1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
/*
* This file is part of mpv.
*
* Original author: Jonathan Yong <10walls@gmail.com>
*
* 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/>.
*/
#ifndef MP_AO_WASAPI_H_
#define MP_AO_WASAPI_H_
#include <stdlib.h>
#include <stdbool.h>
#include <windows.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <audiopolicy.h>
#include <endpointvolume.h>
#include "common/msg.h"
#include "osdep/atomics.h"
#include "internal.h"
#include "ao.h"
typedef struct change_notify {
IMMNotificationClient client; // this must be first in the structure!
IMMDeviceEnumerator *pEnumerator; // object where client is registered
LPWSTR monitored; // Monitored device
bool is_hotplug;
struct ao *ao;
} change_notify;
HRESULT wasapi_change_init(struct ao* ao, bool is_hotplug);
void wasapi_change_uninit(struct ao* ao);
#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)
enum wasapi_thread_state {
WASAPI_THREAD_FEED = 0,
WASAPI_THREAD_RESUME,
WASAPI_THREAD_RESET,
WASAPI_THREAD_SHUTDOWN
};
typedef struct wasapi_state {
struct mp_log *log;
// Thread handles
HRESULT init_ret; // status of init phase
HANDLE hInitDone; // set when init is complete in audio thread
HANDLE hAudioThread; // the audio thread itself
HANDLE hWake; // thread wakeup event
atomic_int thread_state; // enum wasapi_thread_state (what to do on wakeup)
// for setting the audio thread priority
HANDLE hTask;
// ID of the device to use
LPWSTR deviceID;
// WASAPI object handles owned and used by audio thread
IMMDevice *pDevice;
IAudioClient *pAudioClient;
IAudioRenderClient *pRenderClient;
// WASAPI internal clock information, for estimating delay
IAudioClock *pAudioClock;
atomic_ullong sample_count; // samples per channel written by GetBuffer
UINT64 clock_frequency; // scale for position returned by GetPosition
LARGE_INTEGER qpc_frequency; // frequency of Windows' high resolution timer
// WASAPI control (handles owned by audio thread but used by main thread)
IAudioSessionControl *pSessionControl; // setting the stream title
IAudioEndpointVolume *pEndpointVolume; // exclusive mode volume/mute
ISimpleAudioVolume *pAudioVolume; // shared mode volume/mute
DWORD vol_hw_support; // is hardware volume supported for exclusive-mode?
// Streams used to marshal the proxy objects. The thread owning the actual
// objects needs to marshal proxy objects into these streams, and the thread
// that wants the proxies unmarshals them from here.
IStream *sSessionControl;
IStream *sEndpointVolume;
IStream *sAudioVolume;
// WASAPI proxy handles, for Single-Threaded Apartment communication. One is
// needed for each audio thread object that's accessed from the main thread.
IAudioSessionControl *pSessionControlProxy;
IAudioEndpointVolume *pEndpointVolumeProxy;
ISimpleAudioVolume *pAudioVolumeProxy;
// ao options
int opt_exclusive;
int opt_list;
char *opt_device;
// format info
WAVEFORMATEXTENSIBLE format;
AUDCLNT_SHAREMODE share_mode; // AUDCLNT_SHAREMODE_EXCLUSIVE / SHARED
UINT32 bufferFrameCount; // number of frames in buffer
change_notify change;
} wasapi_state;
char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid);
char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey);
char *mp_HRESULT_to_str_buf(char *buf, size_t buf_size, HRESULT hr);
#define mp_GUID_to_str(guid) mp_GUID_to_str_buf((char[40]){0}, 40, (guid))
#define mp_PKEY_to_str(pkey) mp_PKEY_to_str_buf((char[42]){0}, 42, (pkey))
#define mp_HRESULT_to_str(hr) mp_HRESULT_to_str_buf((char[60]){0}, 60, (hr))
#define mp_LastError_to_str() mp_HRESULT_to_str(HRESULT_FROM_WIN32(GetLastError()))
void wasapi_list_devs(struct ao *ao, struct ao_device_list *list);
LPWSTR find_deviceID(struct ao *ao);
void wasapi_dispatch(struct ao *ao);
HRESULT wasapi_thread_init(struct ao *ao);
void wasapi_thread_uninit(struct ao *ao);
void wasapi_receive_proxies(wasapi_state *state);
void wasapi_release_proxies(wasapi_state *state);
#endif
|