1 /*****************************************************************************
2 * mmdevice.c : Windows Multimedia Device API audio output plugin for VLC
3 *****************************************************************************
4 * Copyright (C) 2012 RĂ©mi Denis-Courmont
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
30 #include <audiopolicy.h>
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
35 #include <vlc_modules.h>
36 #include "audio_output/mmdevice.h"
38 DEFINE_GUID (GUID_VLC_AUD_OUT
, 0x4533f59d, 0x59ee, 0x00c6,
39 0xad, 0xb2, 0xc6, 0x8b, 0x50, 0x1a, 0x66, 0x55);
41 static void EnterMTA(void)
43 HRESULT hr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
44 if (unlikely(FAILED(hr
)))
48 static void LeaveMTA(void)
55 aout_stream_t
*stream
; /**< Underlying audio output stream */
60 static int vlc_FromHR(audio_output_t
*aout
, HRESULT hr
)
62 aout_sys_t
* sys
= aout
->sys
;
63 /* Select the default device (and restart) on unplug */
64 if (unlikely(hr
== AUDCLNT_E_DEVICE_INVALIDATED
||
65 hr
== AUDCLNT_E_RESOURCES_INVALIDATED
))
69 return SUCCEEDED(hr
) ? 0 : -1;
72 static int VolumeSet(audio_output_t
*aout
, float vol
)
74 aout_sys_t
*sys
= aout
->sys
;
75 if( unlikely( sys
->client
== NULL
) )
78 ISimpleAudioVolume
*pc_AudioVolume
= NULL
;
81 vol
= vol
* vol
* vol
; /* ISimpleAudioVolume is tapered linearly. */
89 aout_GainRequest(aout
, gain
);
91 hr
= IAudioClient_GetService(sys
->client
, &IID_ISimpleAudioVolume
, &pc_AudioVolume
);
94 msg_Err(aout
, "cannot get volume service (error 0x%lx)", hr
);
98 hr
= ISimpleAudioVolume_SetMasterVolume(pc_AudioVolume
, vol
, NULL
);
101 msg_Err(aout
, "cannot set volume (error 0x%lx)", hr
);
106 ISimpleAudioVolume_Release(pc_AudioVolume
);
108 return SUCCEEDED(hr
) ? 0 : -1;
111 static int MuteSet(audio_output_t
*aout
, bool mute
)
113 aout_sys_t
*sys
= aout
->sys
;
114 if( unlikely( sys
->client
== NULL
) )
117 ISimpleAudioVolume
*pc_AudioVolume
= NULL
;
119 hr
= IAudioClient_GetService(sys
->client
, &IID_ISimpleAudioVolume
, &pc_AudioVolume
);
122 msg_Err(aout
, "cannot get volume service (error 0x%lx)", hr
);
126 hr
= ISimpleAudioVolume_SetMute(pc_AudioVolume
, mute
, NULL
);
129 msg_Err(aout
, "cannot set mute (error 0x%lx)", hr
);
134 ISimpleAudioVolume_Release(pc_AudioVolume
);
136 return SUCCEEDED(hr
) ? 0 : -1;
139 static int TimeGet(audio_output_t
*aout
, mtime_t
*restrict delay
)
141 aout_sys_t
*sys
= aout
->sys
;
142 if( unlikely( sys
->client
== NULL
) )
147 hr
= aout_stream_TimeGet(sys
->stream
, delay
);
150 return SUCCEEDED(hr
) ? 0 : -1;
153 static void Play(audio_output_t
*aout
, block_t
*block
)
155 aout_sys_t
*sys
= aout
->sys
;
156 if( unlikely( sys
->client
== NULL
) )
160 HRESULT hr
= aout_stream_Play(sys
->stream
, block
);
163 vlc_FromHR(aout
, hr
);
166 static void Pause(audio_output_t
*aout
, bool paused
, mtime_t date
)
168 aout_sys_t
*sys
= aout
->sys
;
169 if( unlikely( sys
->client
== NULL
) )
173 HRESULT hr
= aout_stream_Pause(sys
->stream
, paused
);
177 vlc_FromHR(aout
, hr
);
180 static void Flush(audio_output_t
*aout
, bool wait
)
182 aout_sys_t
*sys
= aout
->sys
;
183 if( unlikely( sys
->client
== NULL
) )
187 HRESULT hr
= aout_stream_Flush(sys
->stream
, wait
);
190 vlc_FromHR(aout
, hr
);
193 static HRESULT
ActivateDevice(void *opaque
, REFIID iid
, PROPVARIANT
*actparms
,
196 IAudioClient
*client
= opaque
;
198 if (!IsEqualIID(iid
, &IID_IAudioClient
))
199 return E_NOINTERFACE
;
200 if (actparms
!= NULL
|| client
== NULL
)
203 IAudioClient_AddRef(client
);
209 static int aout_stream_Start(void *func
, va_list ap
)
211 aout_stream_start_t start
= func
;
212 aout_stream_t
*s
= va_arg(ap
, aout_stream_t
*);
213 audio_sample_format_t
*fmt
= va_arg(ap
, audio_sample_format_t
*);
214 HRESULT
*hr
= va_arg(ap
, HRESULT
*);
216 *hr
= start(s
, fmt
, &GUID_VLC_AUD_OUT
);
217 return SUCCEEDED(*hr
) ? VLC_SUCCESS
: VLC_EGENERIC
;
220 static void aout_stream_Stop(void *func
, va_list ap
)
222 aout_stream_stop_t stop
= func
;
223 aout_stream_t
*s
= va_arg(ap
, aout_stream_t
*);
228 static int Start(audio_output_t
*aout
, audio_sample_format_t
*restrict fmt
)
230 aout_sys_t
*sys
= aout
->sys
;
233 aout_stream_t
*s
= vlc_object_create(aout
, sizeof (*s
));
234 if (unlikely(s
== NULL
))
237 s
->owner
.device
= sys
->client
;
238 s
->owner
.activate
= ActivateDevice
;
241 sys
->module
= vlc_module_load(s
, "aout stream", NULL
, false,
242 aout_stream_Start
, s
, fmt
, &hr
);
245 if (sys
->module
== NULL
)
247 vlc_object_release(s
);
251 assert (sys
->stream
== NULL
);
256 static void Stop(audio_output_t
*aout
)
258 aout_sys_t
*sys
= aout
->sys
;
260 assert (sys
->stream
!= NULL
);
263 vlc_module_unload(sys
->stream
, sys
->module
, aout_stream_Stop
, sys
->stream
);
266 vlc_object_release(sys
->stream
);
270 static int DeviceSelect(audio_output_t
*aout
, const char* psz_device
)
272 if( psz_device
== NULL
)
275 intptr_t ptr
= strtoll( psz_device
, &psz_end
, 16 );
278 if (aout
->sys
->client
== (IAudioClient
*)ptr
)
280 aout
->sys
->client
= (IAudioClient
*)ptr
;
281 var_SetAddress( aout
->obj
.parent
, "winstore-client", aout
->sys
->client
);
282 aout_RestartRequest( aout
, AOUT_RESTART_OUTPUT
);
286 static int Open(vlc_object_t
*obj
)
288 audio_output_t
*aout
= (audio_output_t
*)obj
;
290 aout_sys_t
*sys
= malloc(sizeof (*sys
));
291 if (unlikely(sys
== NULL
))
296 aout
->sys
->client
= var_CreateGetAddress( aout
->obj
.parent
, "winstore-client" );
297 if (aout
->sys
->client
!= NULL
)
298 msg_Dbg( aout
, "Reusing previous client: %p", aout
->sys
->client
);
301 aout
->time_get
= TimeGet
;
302 aout
->volume_set
= VolumeSet
;
303 aout
->mute_set
= MuteSet
;
307 aout
->device_select
= DeviceSelect
;
311 static void Close(vlc_object_t
*obj
)
313 audio_output_t
*aout
= (audio_output_t
*)obj
;
314 aout_sys_t
*sys
= aout
->sys
;
320 set_shortname("winstore")
321 set_description("Windows Store audio output")
322 set_capability("audio output", 0)
323 set_category(CAT_AUDIO
)
324 set_subcategory(SUBCAT_AUDIO_AOUT
)
325 add_shortcut("wasapi")
326 set_callbacks(Open
, Close
)