Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
vlc
Commits
a199a57b
Commit
a199a57b
authored
Mar 02, 2014
by
Rémi Denis-Courmont
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
wasapi: audio capture client module (fixes #7205)
parent
5c2fc028
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
483 additions
and
0 deletions
+483
-0
NEWS
NEWS
+1
-0
modules/MODULES_LIST
modules/MODULES_LIST
+1
-0
modules/access/Makefile.am
modules/access/Makefile.am
+8
-0
modules/access/wasapi.c
modules/access/wasapi.c
+472
-0
po/POTFILES.in
po/POTFILES.in
+1
-0
No files found.
NEWS
View file @
a199a57b
...
...
@@ -18,6 +18,7 @@ Access:
* Support depayloading Opus from RTP
* Support sftp username and passwords options and URL
* New UPnP access module, to list directories without infinite recursions
* New WASAPI audio capture module on Windows
Decoder:
* OMX GPU-zerocopy support for decoding and display on Android using OpenMax IL
...
...
modules/MODULES_LIST
View file @
a199a57b
...
...
@@ -19,6 +19,7 @@ $Id$
* access_output_shout: Shoutcast access output
* access_output_udp: UDP Network access_output module
* access_realrtsp: Real RTSP access
* access_wasapi: WASAPI audio input
* addonsfsstorage: Local storage extensions repository
* addonsvorepository: Videolan extensions repository
* adjust: Contrast/Hue/saturation/Brightness adjust module
...
...
modules/access/Makefile.am
View file @
a199a57b
...
...
@@ -105,6 +105,14 @@ if HAVE_QTKIT
access_LTLIBRARIES
+=
libqtsound_plugin.la
endif
libaccess_wasapi_plugin_la_SOURCES
=
access/wasapi.c
libaccess_wasapi_plugin_la_LIBADD
=
-lole32
-lksuser
if
HAVE_WASAPI
if
!HAVE_WINSTORE
access_LTLIBRARIES
+=
libaccess_wasapi_plugin.la
endif
endif
### Video capture ###
...
...
modules/access/wasapi.c
0 → 100644
View file @
a199a57b
/**
* \file wasapi.c
* \brief Windows Audio Session API capture plugin for VLC
*/
/*****************************************************************************
* Copyright (C) 2014-2015 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#define INITGUID
#define COBJMACROS
#define CONST_VTABLE
#include <assert.h>
#include <stdlib.h>
#include <vlc_common.h>
#include <vlc_aout.h>
#include <vlc_demux.h>
#include <vlc_plugin.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
static
LARGE_INTEGER
freq
;
/* performance counters frequency */
BOOL
WINAPI
DllMain
(
HINSTANCE
,
DWORD
,
LPVOID
);
/* avoid warning */
BOOL
WINAPI
DllMain
(
HINSTANCE
dll
,
DWORD
reason
,
LPVOID
reserved
)
{
(
void
)
dll
;
(
void
)
reserved
;
switch
(
reason
)
{
case
DLL_PROCESS_ATTACH
:
if
(
!
QueryPerformanceFrequency
(
&
freq
))
return
FALSE
;
break
;
}
return
TRUE
;
}
static
UINT64
GetQPC
(
void
)
{
LARGE_INTEGER
counter
;
if
(
!
QueryPerformanceCounter
(
&
counter
))
abort
();
lldiv_t
d
=
lldiv
(
counter
.
QuadPart
,
freq
.
QuadPart
);
return
(
d
.
quot
*
10000000
)
+
((
d
.
rem
*
10000000
)
/
freq
.
QuadPart
);
}
static_assert
(
CLOCK_FREQ
*
10
==
10000000
,
"REFERENCE_TIME conversion broken"
);
static
IAudioClient
*
GetClient
(
demux_t
*
demux
)
{
IMMDeviceEnumerator
*
e
;
IMMDevice
*
dev
;
void
*
pv
;
HRESULT
hr
;
hr
=
CoCreateInstance
(
&
CLSID_MMDeviceEnumerator
,
NULL
,
CLSCTX_ALL
,
&
IID_IMMDeviceEnumerator
,
&
pv
);
if
(
FAILED
(
hr
))
{
msg_Err
(
demux
,
"cannot create device enumerator (error 0x%lx)"
,
hr
);
return
NULL
;
}
e
=
pv
;
hr
=
IMMDeviceEnumerator_GetDefaultAudioEndpoint
(
e
,
eCapture
,
eCommunications
,
&
dev
);
IMMDeviceEnumerator_Release
(
e
);
if
(
FAILED
(
hr
))
{
msg_Err
(
demux
,
"cannot get default device (error 0x%lx)"
,
hr
);
return
NULL
;
}
hr
=
IMMDevice_Activate
(
dev
,
&
IID_IAudioClient
,
CLSCTX_ALL
,
NULL
,
&
pv
);
IMMDevice_Release
(
dev
);
if
(
FAILED
(
hr
))
msg_Err
(
demux
,
"cannot activate device (error 0x%lx)"
,
hr
);
return
pv
;
}
static
int
vlc_FromWave
(
const
WAVEFORMATEX
*
restrict
wf
,
audio_sample_format_t
*
restrict
fmt
)
{
fmt
->
i_rate
=
wf
->
nSamplesPerSec
;
/* As per MSDN, IAudioClient::GetMixFormat() always uses this format. */
assert
(
wf
->
wFormatTag
==
WAVE_FORMAT_EXTENSIBLE
);
const
WAVEFORMATEXTENSIBLE
*
wfe
=
(
void
*
)
wf
;
fmt
->
i_physical_channels
=
0
;
if
(
wfe
->
dwChannelMask
&
SPEAKER_FRONT_LEFT
)
fmt
->
i_physical_channels
|=
AOUT_CHAN_LEFT
;
if
(
wfe
->
dwChannelMask
&
SPEAKER_FRONT_RIGHT
)
fmt
->
i_physical_channels
|=
AOUT_CHAN_RIGHT
;
if
(
wfe
->
dwChannelMask
&
SPEAKER_FRONT_CENTER
)
fmt
->
i_physical_channels
|=
AOUT_CHAN_CENTER
;
if
(
wfe
->
dwChannelMask
&
SPEAKER_LOW_FREQUENCY
)
fmt
->
i_physical_channels
|=
AOUT_CHAN_LFE
;
fmt
->
i_original_channels
=
fmt
->
i_physical_channels
;
assert
(
popcount
(
wfe
->
dwChannelMask
)
==
wf
->
nChannels
);
if
(
IsEqualIID
(
&
wfe
->
SubFormat
,
&
KSDATAFORMAT_SUBTYPE_PCM
))
{
switch
(
wf
->
wBitsPerSample
)
{
case
32
:
switch
(
wfe
->
Samples
.
wValidBitsPerSample
)
{
case
32
:
fmt
->
i_format
=
VLC_CODEC_S32N
;
break
;
case
24
:
#ifdef WORDS_BIGENDIAN
fmt
->
i_format
=
VLC_CODEC_S24B32
;
#else
fmt
->
i_format
=
VLC_CODEC_S24L32
;
#endif
break
;
default:
return
-
1
;
}
break
;
case
24
:
if
(
wfe
->
Samples
.
wValidBitsPerSample
==
24
)
fmt
->
i_format
=
VLC_CODEC_S24N
;
else
return
-
1
;
break
;
case
16
:
if
(
wfe
->
Samples
.
wValidBitsPerSample
==
16
)
fmt
->
i_format
=
VLC_CODEC_S16N
;
else
return
-
1
;
break
;
case
8
:
if
(
wfe
->
Samples
.
wValidBitsPerSample
==
8
)
fmt
->
i_format
=
VLC_CODEC_S8
;
else
return
-
1
;
break
;
default:
return
-
1
;
}
}
else
if
(
IsEqualIID
(
&
wfe
->
SubFormat
,
&
KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
{
if
(
wf
->
wBitsPerSample
!=
wfe
->
Samples
.
wValidBitsPerSample
)
return
-
1
;
switch
(
wf
->
wBitsPerSample
)
{
case
64
:
fmt
->
i_format
=
VLC_CODEC_FL64
;
break
;
case
32
:
fmt
->
i_format
=
VLC_CODEC_FL32
;
break
;
default:
return
-
1
;
}
}
/*else if (IsEqualIID(&wfe->Subformat, &KSDATAFORMAT_SUBTYPE_DRM)) {} */
else
if
(
IsEqualIID
(
&
wfe
->
SubFormat
,
&
KSDATAFORMAT_SUBTYPE_ALAW
))
fmt
->
i_format
=
VLC_CODEC_ALAW
;
else
if
(
IsEqualIID
(
&
wfe
->
SubFormat
,
&
KSDATAFORMAT_SUBTYPE_MULAW
))
fmt
->
i_format
=
VLC_CODEC_MULAW
;
else
if
(
IsEqualIID
(
&
wfe
->
SubFormat
,
&
KSDATAFORMAT_SUBTYPE_ADPCM
))
fmt
->
i_format
=
VLC_CODEC_ADPCM_MS
;
else
return
-
1
;
aout_FormatPrepare
(
fmt
);
if
(
wf
->
nChannels
!=
fmt
->
i_channels
)
return
-
1
;
return
0
;
}
static
es_out_id_t
*
CreateES
(
demux_t
*
demux
,
IAudioClient
*
client
,
mtime_t
caching
,
size_t
*
restrict
frame_size
)
{
es_format_t
fmt
;
WAVEFORMATEX
*
pwf
;
HRESULT
hr
;
hr
=
IAudioClient_GetMixFormat
(
client
,
&
pwf
);
if
(
FAILED
(
hr
))
{
msg_Err
(
demux
,
"cannot get mix format (error 0x%lx)"
,
hr
);
return
NULL
;
}
es_format_Init
(
&
fmt
,
AUDIO_ES
,
0
);
if
(
vlc_FromWave
(
pwf
,
&
fmt
.
audio
))
{
msg_Err
(
demux
,
"unsupported mix format"
);
CoTaskMemFree
(
pwf
);
return
NULL
;
}
fmt
.
i_codec
=
fmt
.
audio
.
i_format
;
fmt
.
i_bitrate
=
fmt
.
audio
.
i_bitspersample
*
fmt
.
audio
.
i_channels
*
fmt
.
audio
.
i_rate
;
*
frame_size
=
fmt
.
audio
.
i_bitspersample
*
fmt
.
audio
.
i_channels
/
8
;
DWORD
flags
=
AUDCLNT_STREAMFLAGS_EVENTCALLBACK
;
/* TODO: loopback */
/* Request at least thrice the PTS delay */
REFERENCE_TIME
bufsize
=
caching
*
INT64_C
(
10
)
*
3
;
hr
=
IAudioClient_Initialize
(
client
,
AUDCLNT_SHAREMODE_SHARED
,
flags
,
bufsize
,
0
,
pwf
,
NULL
);
CoTaskMemFree
(
pwf
);
if
(
FAILED
(
hr
))
{
msg_Err
(
demux
,
"cannot initialize audio client (error 0x%lx)"
,
hr
);
return
NULL
;
}
return
es_out_Add
(
demux
->
out
,
&
fmt
);
}
struct
demux_sys_t
{
IAudioClient
*
client
;
es_out_id_t
*
es
;
size_t
frame_size
;
mtime_t
caching
;
mtime_t
start_time
;
HANDLE
events
[
2
];
union
{
HANDLE
thread
;
HANDLE
ready
;
};
};
static
unsigned
__stdcall
Thread
(
void
*
data
)
{
demux_t
*
demux
=
data
;
demux_sys_t
*
sys
=
demux
->
p_sys
;
IAudioCaptureClient
*
capture
=
NULL
;
void
*
pv
;
HRESULT
hr
;
hr
=
CoInitializeEx
(
NULL
,
COINIT_MULTITHREADED
);
assert
(
SUCCEEDED
(
hr
));
/* COM already allocated by parent thread */
SetEvent
(
sys
->
ready
);
hr
=
IAudioClient_GetService
(
sys
->
client
,
&
IID_IAudioCaptureClient
,
&
pv
);
if
(
FAILED
(
hr
))
{
msg_Err
(
demux
,
"cannot get capture client (error 0x%lx)"
,
hr
);
goto
out
;
}
capture
=
pv
;
hr
=
IAudioClient_Start
(
sys
->
client
);
if
(
FAILED
(
hr
))
{
msg_Err
(
demux
,
"cannot start client (error 0x%lx)"
,
hr
);
IAudioCaptureClient_Release
(
capture
);
goto
out
;
}
while
(
WaitForMultipleObjects
(
2
,
sys
->
events
,
FALSE
,
INFINITE
)
!=
WAIT_OBJECT_0
)
{
BYTE
*
data
;
UINT32
frames
;
DWORD
flags
;
UINT64
qpc
;
mtime_t
pts
;
hr
=
IAudioCaptureClient_GetBuffer
(
capture
,
&
data
,
&
frames
,
&
flags
,
NULL
,
&
qpc
);
if
(
hr
!=
S_OK
)
continue
;
pts
=
mdate
()
-
((
GetQPC
()
-
qpc
)
/
10
);
es_out_Control
(
demux
->
out
,
ES_OUT_SET_PCR
,
pts
);
size_t
bytes
=
frames
*
sys
->
frame_size
;
block_t
*
block
=
block_Alloc
(
bytes
);
if
(
likely
(
block
!=
NULL
))
{
memcpy
(
block
->
p_buffer
,
data
,
bytes
);
block
->
i_nb_samples
=
frames
;
block
->
i_pts
=
block
->
i_dts
=
pts
;
es_out_Send
(
demux
->
out
,
sys
->
es
,
block
);
}
IAudioCaptureClient_ReleaseBuffer
(
capture
,
frames
);
}
IAudioClient_Stop
(
sys
->
client
);
IAudioCaptureClient_Release
(
capture
);
out:
CoUninitialize
();
return
0
;
}
static
int
Control
(
demux_t
*
demux
,
int
query
,
va_list
ap
)
{
demux_sys_t
*
sys
=
demux
->
p_sys
;
switch
(
query
)
{
case
DEMUX_GET_TIME
:
*
(
va_arg
(
ap
,
int64_t
*
))
=
mdate
()
-
sys
->
start_time
;
break
;
case
DEMUX_GET_PTS_DELAY
:
*
(
va_arg
(
ap
,
int64_t
*
))
=
sys
->
caching
;
break
;
case
DEMUX_HAS_UNSUPPORTED_META
:
case
DEMUX_CAN_RECORD
:
case
DEMUX_CAN_PAUSE
:
case
DEMUX_CAN_CONTROL_PACE
:
case
DEMUX_CAN_CONTROL_RATE
:
case
DEMUX_CAN_SEEK
:
*
(
va_arg
(
ap
,
bool
*
))
=
false
;
break
;
default:
return
VLC_EGENERIC
;
}
return
VLC_SUCCESS
;
}
static
int
Open
(
vlc_object_t
*
obj
)
{
demux_t
*
demux
=
(
demux_t
*
)
obj
;
HRESULT
hr
;
if
(
demux
->
psz_location
!=
NULL
&&
demux
->
psz_location
!=
'\0'
)
return
VLC_EGENERIC
;
/* TODO non-default device */
demux_sys_t
*
sys
=
malloc
(
sizeof
(
*
sys
));
if
(
unlikely
(
sys
==
NULL
))
return
VLC_ENOMEM
;
sys
->
client
=
NULL
;
sys
->
es
=
NULL
;
sys
->
caching
=
INT64_C
(
1000
)
*
var_InheritInteger
(
obj
,
"live-caching"
);
sys
->
start_time
=
mdate
();
for
(
unsigned
i
=
0
;
i
<
2
;
i
++
)
sys
->
events
[
i
]
=
NULL
;
for
(
unsigned
i
=
0
;
i
<
2
;
i
++
)
{
sys
->
events
[
i
]
=
CreateEvent
(
NULL
,
FALSE
,
FALSE
,
NULL
);
if
(
sys
->
events
[
i
]
==
NULL
)
goto
error
;
}
hr
=
CoInitializeEx
(
NULL
,
COINIT_MULTITHREADED
);
if
(
unlikely
(
FAILED
(
hr
)))
{
msg_Err
(
demux
,
"cannot initialize COM (error 0x%lx)"
,
hr
);
goto
error
;
}
sys
->
client
=
GetClient
(
demux
);
if
(
sys
->
client
==
NULL
)
{
CoUninitialize
();
goto
error
;
}
sys
->
es
=
CreateES
(
demux
,
sys
->
client
,
sys
->
caching
,
&
sys
->
frame_size
);
if
(
sys
->
es
==
NULL
)
goto
error
;
hr
=
IAudioClient_SetEventHandle
(
sys
->
client
,
sys
->
events
[
1
]);
if
(
FAILED
(
hr
))
{
msg_Err
(
demux
,
"cannot set event handle (error 0x%lx)"
,
hr
);
goto
error
;
}
demux
->
p_sys
=
sys
;
sys
->
ready
=
CreateEvent
(
NULL
,
FALSE
,
FALSE
,
NULL
);
if
(
sys
->
ready
==
NULL
)
goto
error
;
uintptr_t
h
=
_beginthreadex
(
NULL
,
0
,
Thread
,
demux
,
0
,
NULL
);
if
(
h
!=
0
)
WaitForSingleObject
(
sys
->
ready
,
INFINITE
);
CloseHandle
(
sys
->
ready
);
sys
->
thread
=
(
HANDLE
)
h
;
if
(
sys
->
thread
==
NULL
)
goto
error
;
CoUninitialize
();
demux
->
pf_demux
=
NULL
;
demux
->
pf_control
=
Control
;
return
VLC_SUCCESS
;
error:
if
(
sys
->
es
!=
NULL
)
es_out_Del
(
demux
->
out
,
sys
->
es
);
if
(
sys
->
client
!=
NULL
)
{
IAudioClient_Release
(
sys
->
client
);
CoUninitialize
();
}
for
(
unsigned
i
=
0
;
i
<
2
;
i
++
)
if
(
sys
->
events
[
i
]
!=
NULL
)
CloseHandle
(
sys
->
events
[
i
]);
free
(
sys
);
return
VLC_ENOMEM
;
}
static
void
Close
(
vlc_object_t
*
obj
)
{
demux_t
*
demux
=
(
demux_t
*
)
obj
;
demux_sys_t
*
sys
=
demux
->
p_sys
;
HRESULT
hr
;
hr
=
CoInitializeEx
(
NULL
,
COINIT_MULTITHREADED
);
assert
(
SUCCEEDED
(
hr
));
SetEvent
(
sys
->
events
[
0
]);
WaitForSingleObject
(
sys
->
thread
,
INFINITE
);
CloseHandle
(
sys
->
thread
);
es_out_Del
(
demux
->
out
,
sys
->
es
);
IAudioClient_Release
(
sys
->
client
);
CoUninitialize
();
for
(
unsigned
i
=
0
;
i
<
2
;
i
++
)
CloseHandle
(
sys
->
events
[
i
]);
free
(
sys
);
}
vlc_module_begin
()
set_shortname
(
N_
(
"WASAPI"
))
set_description
(
N_
(
"Windows Audio Session API input"
))
set_capability
(
"access_demux"
,
0
)
set_category
(
CAT_INPUT
)
set_subcategory
(
SUBCAT_INPUT_ACCESS
)
add_shortcut
(
"wasapi"
)
set_callbacks
(
Open
,
Close
)
vlc_module_end
()
po/POTFILES.in
View file @
a199a57b
...
...
@@ -278,6 +278,7 @@ modules/access/vcdx/vcdplayer.c
modules/access/vcdx/vcdplayer.h
modules/access/vdr.c
modules/access/vnc.c
modules/access/wasapi.c
modules/access/zip/zipstream.c
modules/arm_neon/chroma_yuv.c
modules/arm_neon/simple_channel_mixer.c
...
...
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