Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
29747377
Commit
29747377
authored
Dec 02, 2020
by
Shen-Ta Hsieh
Committed by
Max Kellermann
Mar 04, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
src/win32: Add ComWorker to run all COM function on same thread
parent
b1d75672
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
153 additions
and
132 deletions
+153
-132
WasapiMixerPlugin.cxx
src/mixer/plugins/WasapiMixerPlugin.cxx
+84
-73
WasapiOutputPlugin.cxx
src/output/plugins/WasapiOutputPlugin.cxx
+69
-59
No files found.
src/mixer/plugins/WasapiMixerPlugin.cxx
View file @
29747377
...
@@ -19,7 +19,7 @@
...
@@ -19,7 +19,7 @@
#include "mixer/MixerInternal.hxx"
#include "mixer/MixerInternal.hxx"
#include "output/plugins/WasapiOutputPlugin.hxx"
#include "output/plugins/WasapiOutputPlugin.hxx"
#include "win32/Com.hxx"
#include "win32/Com
Worker
.hxx"
#include "win32/HResult.hxx"
#include "win32/HResult.hxx"
#include <cmath>
#include <cmath>
...
@@ -28,92 +28,103 @@
...
@@ -28,92 +28,103 @@
class
WasapiMixer
final
:
public
Mixer
{
class
WasapiMixer
final
:
public
Mixer
{
WasapiOutput
&
output
;
WasapiOutput
&
output
;
std
::
optional
<
COM
>
com
;
public
:
public
:
WasapiMixer
(
WasapiOutput
&
_output
,
MixerListener
&
_listener
)
WasapiMixer
(
WasapiOutput
&
_output
,
MixerListener
&
_listener
)
:
Mixer
(
wasapi_mixer_plugin
,
_listener
),
output
(
_output
)
{}
:
Mixer
(
wasapi_mixer_plugin
,
_listener
),
output
(
_output
)
{}
void
Open
()
override
{
com
.
emplace
();
}
void
Open
()
override
{}
void
Close
()
noexcept
override
{
com
.
reset
();
}
void
Close
()
noexcept
override
{}
int
GetVolume
()
override
{
int
GetVolume
()
override
{
HRESULT
result
;
auto
future
=
COMWorker
::
Async
([
&
]()
->
int
{
float
volume_level
;
HRESULT
result
;
float
volume_level
;
if
(
wasapi_is_exclusive
(
output
))
{
ComPtr
<
IAudioEndpointVolume
>
endpoint_volume
;
if
(
wasapi_is_exclusive
(
output
))
{
result
=
wasapi_output_get_device
(
output
)
->
Activate
(
ComPtr
<
IAudioEndpointVolume
>
endpoint_volume
;
__uuidof
(
IAudioEndpointVolume
),
CLSCTX_ALL
,
nullptr
,
result
=
wasapi_output_get_device
(
output
)
->
Activate
(
endpoint_volume
.
AddressCast
());
__uuidof
(
IAudioEndpointVolume
),
CLSCTX_ALL
,
if
(
FAILED
(
result
))
{
nullptr
,
endpoint_volume
.
AddressCast
());
throw
FormatHResultError
(
if
(
FAILED
(
result
))
{
result
,
"Unable to get device endpoint volume"
);
throw
FormatHResultError
(
result
,
"Unable to get device "
"endpoint volume"
);
}
result
=
endpoint_volume
->
GetMasterVolumeLevelScalar
(
&
volume_level
);
if
(
FAILED
(
result
))
{
throw
FormatHResultError
(
result
,
"Unable to get master "
"volume level"
);
}
}
else
{
ComPtr
<
ISimpleAudioVolume
>
session_volume
;
result
=
wasapi_output_get_client
(
output
)
->
GetService
(
__uuidof
(
ISimpleAudioVolume
),
session_volume
.
AddressCast
<
void
>
());
if
(
FAILED
(
result
))
{
throw
FormatHResultError
(
result
,
"Unable to get client "
"session volume"
);
}
result
=
session_volume
->
GetMasterVolume
(
&
volume_level
);
if
(
FAILED
(
result
))
{
throw
FormatHResultError
(
result
,
"Unable to get master volume"
);
}
}
}
result
=
endpoint_volume
->
GetMasterVolumeLevelScalar
(
return
std
::
lround
(
volume_level
*
100.0
f
);
&
volume_level
);
});
if
(
FAILED
(
result
))
{
return
future
.
get
();
throw
FormatHResultError
(
result
,
"Unable to get master volume level"
);
}
}
else
{
ComPtr
<
ISimpleAudioVolume
>
session_volume
;
result
=
wasapi_output_get_client
(
output
)
->
GetService
(
__uuidof
(
ISimpleAudioVolume
),
session_volume
.
AddressCast
<
void
>
());
if
(
FAILED
(
result
))
{
throw
FormatHResultError
(
result
,
"Unable to get client session volume"
);
}
result
=
session_volume
->
GetMasterVolume
(
&
volume_level
);
if
(
FAILED
(
result
))
{
throw
FormatHResultError
(
result
,
"Unable to get master volume"
);
}
}
return
std
::
lround
(
volume_level
*
100.0
f
);
}
}
void
SetVolume
(
unsigned
volume
)
override
{
void
SetVolume
(
unsigned
volume
)
override
{
HRESULT
result
;
COMWorker
::
Async
([
&
]()
{
const
float
volume_level
=
volume
/
100.0
f
;
HRESULT
result
;
const
float
volume_level
=
volume
/
100.0
f
;
if
(
wasapi_is_exclusive
(
output
))
{
ComPtr
<
IAudioEndpointVolume
>
endpoint_volume
;
if
(
wasapi_is_exclusive
(
output
))
{
result
=
wasapi_output_get_device
(
output
)
->
Activate
(
ComPtr
<
IAudioEndpointVolume
>
endpoint_volume
;
__uuidof
(
IAudioEndpointVolume
),
CLSCTX_ALL
,
nullptr
,
result
=
wasapi_output_get_device
(
output
)
->
Activate
(
endpoint_volume
.
AddressCast
());
__uuidof
(
IAudioEndpointVolume
),
CLSCTX_ALL
,
if
(
FAILED
(
result
))
{
nullptr
,
endpoint_volume
.
AddressCast
());
throw
FormatHResultError
(
if
(
FAILED
(
result
))
{
result
,
"Unable to get device endpoint volume"
);
throw
FormatHResultError
(
}
result
,
"Unable to get device endpoint volume"
);
result
=
endpoint_volume
->
SetMasterVolumeLevelScalar
(
volume_level
,
}
nullptr
);
if
(
FAILED
(
result
))
{
result
=
endpoint_volume
->
SetMasterVolumeLevelScalar
(
throw
FormatHResultError
(
volume_level
,
nullptr
);
result
,
"Unable to set master volume level"
);
if
(
FAILED
(
result
))
{
}
throw
FormatHResultError
(
}
else
{
result
,
ComPtr
<
ISimpleAudioVolume
>
session_volume
;
"Unable to set master volume level"
);
result
=
wasapi_output_get_client
(
output
)
->
GetService
(
}
__uuidof
(
ISimpleAudioVolume
),
}
else
{
session_volume
.
AddressCast
<
void
>
());
ComPtr
<
ISimpleAudioVolume
>
session_volume
;
if
(
FAILED
(
result
))
{
result
=
wasapi_output_get_client
(
output
)
->
GetService
(
throw
FormatHResultError
(
__uuidof
(
ISimpleAudioVolume
),
result
,
"Unable to get client session volume"
);
session_volume
.
AddressCast
<
void
>
());
}
if
(
FAILED
(
result
))
{
throw
FormatHResultError
(
result
=
session_volume
->
SetMasterVolume
(
volume_level
,
nullptr
);
result
,
if
(
FAILED
(
result
))
{
"Unable to get client session volume"
);
throw
FormatHResultError
(
result
,
}
"Unable to set master volume"
);
result
=
session_volume
->
SetMasterVolume
(
volume_level
,
nullptr
);
if
(
FAILED
(
result
))
{
throw
FormatHResultError
(
result
,
"Unable to set master volume"
);
}
}
}
}
}
).
get
();
}
}
};
};
...
...
src/output/plugins/WasapiOutputPlugin.cxx
View file @
29747377
...
@@ -32,6 +32,7 @@
...
@@ -32,6 +32,7 @@
#include "util/ScopeExit.hxx"
#include "util/ScopeExit.hxx"
#include "win32/Com.hxx"
#include "win32/Com.hxx"
#include "win32/ComHeapPtr.hxx"
#include "win32/ComHeapPtr.hxx"
#include "win32/ComWorker.hxx"
#include "win32/HResult.hxx"
#include "win32/HResult.hxx"
#include "win32/WinEvent.hxx"
#include "win32/WinEvent.hxx"
...
@@ -144,7 +145,6 @@ public:
...
@@ -144,7 +145,6 @@ public:
private
:
private
:
std
::
shared_ptr
<
WinEvent
>
event
;
std
::
shared_ptr
<
WinEvent
>
event
;
std
::
optional
<
COM
>
com
;
ComPtr
<
IAudioClient
>
client
;
ComPtr
<
IAudioClient
>
client
;
ComPtr
<
IAudioRenderClient
>
render_client
;
ComPtr
<
IAudioRenderClient
>
render_client
;
const
UINT32
frame_size
;
const
UINT32
frame_size
;
...
@@ -174,9 +174,17 @@ class WasapiOutput final : public AudioOutput {
...
@@ -174,9 +174,17 @@ class WasapiOutput final : public AudioOutput {
public
:
public
:
static
AudioOutput
*
Create
(
EventLoop
&
,
const
ConfigBlock
&
block
);
static
AudioOutput
*
Create
(
EventLoop
&
,
const
ConfigBlock
&
block
);
WasapiOutput
(
const
ConfigBlock
&
block
);
WasapiOutput
(
const
ConfigBlock
&
block
);
void
Enable
()
override
;
void
Enable
()
override
{
void
Disable
()
noexcept
override
;
COMWorker
::
Aquire
();
void
Open
(
AudioFormat
&
audio_format
)
override
;
COMWorker
::
Async
([
&
]()
{
OpenDevice
();
}).
get
();
}
void
Disable
()
noexcept
override
{
COMWorker
::
Async
([
&
]()
{
DoDisable
();
}).
get
();
COMWorker
::
Release
();
}
void
Open
(
AudioFormat
&
audio_format
)
override
{
COMWorker
::
Async
([
&
]()
{
DoOpen
(
audio_format
);
}).
get
();
}
void
Close
()
noexcept
override
;
void
Close
()
noexcept
override
;
std
::
chrono
::
steady_clock
::
duration
Delay
()
const
noexcept
override
;
std
::
chrono
::
steady_clock
::
duration
Delay
()
const
noexcept
override
;
size_t
Play
(
const
void
*
chunk
,
size_t
size
)
override
;
size_t
Play
(
const
void
*
chunk
,
size_t
size
)
override
;
...
@@ -196,7 +204,6 @@ private:
...
@@ -196,7 +204,6 @@ private:
std
::
string
device_config
;
std
::
string
device_config
;
std
::
vector
<
std
::
pair
<
unsigned
int
,
AllocatedString
>>
device_desc
;
std
::
vector
<
std
::
pair
<
unsigned
int
,
AllocatedString
>>
device_desc
;
std
::
shared_ptr
<
WinEvent
>
event
;
std
::
shared_ptr
<
WinEvent
>
event
;
std
::
optional
<
COM
>
com
;
ComPtr
<
IMMDeviceEnumerator
>
enumerator
;
ComPtr
<
IMMDeviceEnumerator
>
enumerator
;
ComPtr
<
IMMDevice
>
device
;
ComPtr
<
IMMDevice
>
device
;
ComPtr
<
IAudioClient
>
client
;
ComPtr
<
IAudioClient
>
client
;
...
@@ -209,6 +216,10 @@ private:
...
@@ -209,6 +216,10 @@ private:
friend
IMMDevice
*
wasapi_output_get_device
(
WasapiOutput
&
output
)
noexcept
;
friend
IMMDevice
*
wasapi_output_get_device
(
WasapiOutput
&
output
)
noexcept
;
friend
IAudioClient
*
wasapi_output_get_client
(
WasapiOutput
&
output
)
noexcept
;
friend
IAudioClient
*
wasapi_output_get_client
(
WasapiOutput
&
output
)
noexcept
;
void
DoDisable
()
noexcept
;
void
DoOpen
(
AudioFormat
&
audio_format
);
void
OpenDevice
();
void
FindExclusiveFormatSupported
(
AudioFormat
&
audio_format
);
void
FindExclusiveFormatSupported
(
AudioFormat
&
audio_format
);
void
FindSharedFormatSupported
(
AudioFormat
&
audio_format
);
void
FindSharedFormatSupported
(
AudioFormat
&
audio_format
);
void
EnumerateDevices
();
void
EnumerateDevices
();
...
@@ -234,15 +245,7 @@ IAudioClient *wasapi_output_get_client(WasapiOutput &output) noexcept {
...
@@ -234,15 +245,7 @@ IAudioClient *wasapi_output_get_client(WasapiOutput &output) noexcept {
void
WasapiOutputThread
::
Work
()
noexcept
{
void
WasapiOutputThread
::
Work
()
noexcept
{
SetThreadName
(
"Wasapi Output Worker"
);
SetThreadName
(
"Wasapi Output Worker"
);
FormatDebug
(
wasapi_output_domain
,
"Working thread started"
);
FormatDebug
(
wasapi_output_domain
,
"Working thread started"
);
try
{
COM
com
{
true
};
com
.
emplace
();
}
catch
(...)
{
std
::
unique_lock
<
Mutex
>
lock
(
error
.
mutex
);
error
.
error_ptr
=
std
::
current_exception
();
error
.
cond
.
wait
(
lock
);
assert
(
error
.
error_ptr
==
nullptr
);
return
;
}
while
(
true
)
{
while
(
true
)
{
try
{
try
{
event
->
Wait
(
INFINITE
);
event
->
Wait
(
INFINITE
);
...
@@ -316,41 +319,8 @@ WasapiOutput::WasapiOutput(const ConfigBlock &block)
...
@@ -316,41 +319,8 @@ WasapiOutput::WasapiOutput(const ConfigBlock &block)
enumerate_devices
(
block
.
GetBlockValue
(
"enumerate"
,
false
)),
enumerate_devices
(
block
.
GetBlockValue
(
"enumerate"
,
false
)),
device_config
(
block
.
GetBlockValue
(
"device"
,
""
))
{}
device_config
(
block
.
GetBlockValue
(
"device"
,
""
))
{}
void
WasapiOutput
::
Enable
()
{
/// run inside COMWorkerThread
com
.
emplace
();
void
WasapiOutput
::
DoDisable
()
noexcept
{
event
=
std
::
make_shared
<
WinEvent
>
();
enumerator
.
CoCreateInstance
(
__uuidof
(
MMDeviceEnumerator
),
nullptr
,
CLSCTX_INPROC_SERVER
);
device_desc
.
clear
();
device
.
reset
();
if
(
enumerate_devices
&&
SafeTry
([
this
]()
{
EnumerateDevices
();
}))
{
for
(
const
auto
&
desc
:
device_desc
)
{
FormatNotice
(
wasapi_output_domain
,
"Device
\"
%u
\"
\"
%s
\"
"
,
desc
.
first
,
desc
.
second
.
c_str
());
}
}
unsigned
int
id
=
kErrorId
;
if
(
!
device_config
.
empty
())
{
if
(
!
SafeSilenceTry
([
this
,
&
id
]()
{
id
=
std
::
stoul
(
device_config
);
}))
{
id
=
SearchDevice
(
device_config
);
}
}
if
(
id
!=
kErrorId
)
{
SafeTry
([
this
,
id
]()
{
GetDevice
(
id
);
});
}
if
(
!
device
)
{
GetDefaultDevice
();
}
device_desc
.
clear
();
}
void
WasapiOutput
::
Disable
()
noexcept
{
if
(
thread
)
{
if
(
thread
)
{
try
{
try
{
thread
->
Finish
();
thread
->
Finish
();
...
@@ -369,7 +339,8 @@ void WasapiOutput::Disable() noexcept {
...
@@ -369,7 +339,8 @@ void WasapiOutput::Disable() noexcept {
event
.
reset
();
event
.
reset
();
}
}
void
WasapiOutput
::
Open
(
AudioFormat
&
audio_format
)
{
/// run inside COMWorkerThread
void
WasapiOutput
::
DoOpen
(
AudioFormat
&
audio_format
)
{
if
(
audio_format
.
channels
==
0
)
{
if
(
audio_format
.
channels
==
0
)
{
throw
FormatInvalidArgument
(
"channels should > 0"
);
throw
FormatInvalidArgument
(
"channels should > 0"
);
}
}
...
@@ -497,9 +468,11 @@ void WasapiOutput::Close() noexcept {
...
@@ -497,9 +468,11 @@ void WasapiOutput::Close() noexcept {
Pause
();
Pause
();
thread
->
Finish
();
thread
->
Finish
();
thread
->
Join
();
thread
->
Join
();
thread
.
reset
();
COMWorker
::
Async
([
&
]()
{
thread
.
reset
();
client
.
reset
();
}).
get
();
spsc_buffer
.
reset
();
spsc_buffer
.
reset
();
client
.
reset
();
}
}
std
::
chrono
::
steady_clock
::
duration
WasapiOutput
::
Delay
()
const
noexcept
{
std
::
chrono
::
steady_clock
::
duration
WasapiOutput
::
Delay
()
const
noexcept
{
...
@@ -534,13 +507,14 @@ size_t WasapiOutput::Play(const void *chunk, size_t size) {
...
@@ -534,13 +507,14 @@ size_t WasapiOutput::Play(const void *chunk, size_t size) {
is_started
=
true
;
is_started
=
true
;
thread
->
Play
();
thread
->
Play
();
COMWorker
::
Async
([
&
]()
{
HRESULT
result
;
HRESULT
result
;
result
=
client
->
Start
();
result
=
client
->
Start
();
if
(
FAILED
(
result
))
{
if
(
FAILED
(
result
))
{
throw
FormatHResultError
(
result
,
throw
FormatHResultError
(
"Failed to start client"
);
result
,
"Failed to start client"
);
}
}
}).
wait
();
}
}
thread
->
CheckException
();
thread
->
CheckException
();
...
@@ -575,6 +549,37 @@ bool WasapiOutput::Pause() {
...
@@ -575,6 +549,37 @@ bool WasapiOutput::Pause() {
return
true
;
return
true
;
}
}
/// run inside COMWorkerThread
void
WasapiOutput
::
OpenDevice
()
{
enumerator
.
CoCreateInstance
(
__uuidof
(
MMDeviceEnumerator
),
nullptr
,
CLSCTX_INPROC_SERVER
);
if
(
enumerate_devices
&&
SafeTry
([
this
]()
{
EnumerateDevices
();
}))
{
for
(
const
auto
&
desc
:
device_desc
)
{
FormatNotice
(
wasapi_output_domain
,
"Device
\"
%u
\"
\"
%s
\"
"
,
desc
.
first
,
desc
.
second
.
c_str
());
}
}
unsigned
int
id
=
kErrorId
;
if
(
!
device_config
.
empty
())
{
if
(
!
SafeSilenceTry
([
this
,
&
id
]()
{
id
=
std
::
stoul
(
device_config
);
}))
{
id
=
SearchDevice
(
device_config
);
}
}
if
(
id
!=
kErrorId
)
{
SafeTry
([
this
,
id
]()
{
GetDevice
(
id
);
});
}
if
(
!
device
)
{
GetDefaultDevice
();
}
device_desc
.
clear
();
}
/// run inside COMWorkerThread
void
WasapiOutput
::
FindExclusiveFormatSupported
(
AudioFormat
&
audio_format
)
{
void
WasapiOutput
::
FindExclusiveFormatSupported
(
AudioFormat
&
audio_format
)
{
SetFormat
(
device_format
,
audio_format
);
SetFormat
(
device_format
,
audio_format
);
...
@@ -641,6 +646,7 @@ void WasapiOutput::FindExclusiveFormatSupported(AudioFormat &audio_format) {
...
@@ -641,6 +646,7 @@ void WasapiOutput::FindExclusiveFormatSupported(AudioFormat &audio_format) {
}
while
(
true
);
}
while
(
true
);
}
}
/// run inside COMWorkerThread
void
WasapiOutput
::
FindSharedFormatSupported
(
AudioFormat
&
audio_format
)
{
void
WasapiOutput
::
FindSharedFormatSupported
(
AudioFormat
&
audio_format
)
{
HRESULT
result
;
HRESULT
result
;
ComHeapPtr
<
WAVEFORMATEX
>
mixer_format
;
ComHeapPtr
<
WAVEFORMATEX
>
mixer_format
;
...
@@ -724,6 +730,7 @@ void WasapiOutput::FindSharedFormatSupported(AudioFormat &audio_format) {
...
@@ -724,6 +730,7 @@ void WasapiOutput::FindSharedFormatSupported(AudioFormat &audio_format) {
}
}
}
}
/// run inside COMWorkerThread
void
WasapiOutput
::
EnumerateDevices
()
{
void
WasapiOutput
::
EnumerateDevices
()
{
if
(
!
device_desc
.
empty
())
{
if
(
!
device_desc
.
empty
())
{
return
;
return
;
...
@@ -776,6 +783,7 @@ void WasapiOutput::EnumerateDevices() {
...
@@ -776,6 +783,7 @@ void WasapiOutput::EnumerateDevices() {
}
}
}
}
/// run inside COMWorkerThread
void
WasapiOutput
::
GetDevice
(
unsigned
int
index
)
{
void
WasapiOutput
::
GetDevice
(
unsigned
int
index
)
{
HRESULT
result
;
HRESULT
result
;
...
@@ -792,6 +800,7 @@ void WasapiOutput::GetDevice(unsigned int index) {
...
@@ -792,6 +800,7 @@ void WasapiOutput::GetDevice(unsigned int index) {
}
}
}
}
/// run inside COMWorkerThread
unsigned
int
WasapiOutput
::
SearchDevice
(
std
::
string_view
name
)
{
unsigned
int
WasapiOutput
::
SearchDevice
(
std
::
string_view
name
)
{
if
(
!
SafeTry
([
this
]()
{
EnumerateDevices
();
}))
{
if
(
!
SafeTry
([
this
]()
{
EnumerateDevices
();
}))
{
return
kErrorId
;
return
kErrorId
;
...
@@ -809,6 +818,7 @@ unsigned int WasapiOutput::SearchDevice(std::string_view name) {
...
@@ -809,6 +818,7 @@ unsigned int WasapiOutput::SearchDevice(std::string_view name) {
return
iter
->
first
;
return
iter
->
first
;
}
}
/// run inside COMWorkerThread
void
WasapiOutput
::
GetDefaultDevice
()
{
void
WasapiOutput
::
GetDefaultDevice
()
{
HRESULT
result
;
HRESULT
result
;
result
=
enumerator
->
GetDefaultAudioEndpoint
(
eRender
,
eMultimedia
,
result
=
enumerator
->
GetDefaultAudioEndpoint
(
eRender
,
eMultimedia
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment