2 * Copyright 2019 Nikolay Sivov for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "mf_private.h"
25 #include "mmdeviceapi.h"
26 #include "audioclient.h"
28 #include "wine/debug.h"
29 #include "wine/heap.h"
30 #include "wine/list.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(mfplat
);
36 STREAM_STATE_STOPPED
= 0,
41 enum audio_renderer_flags
45 SAR_SAMPLE_REQUESTED
= 0x4,
48 enum queued_object_type
57 enum queued_object_type type
;
63 unsigned int frame_offset
;
67 MFSTREAMSINK_MARKER_TYPE type
;
75 IMFMediaSink IMFMediaSink_iface
;
76 IMFMediaSinkPreroll IMFMediaSinkPreroll_iface
;
77 IMFStreamSink IMFStreamSink_iface
;
78 IMFMediaTypeHandler IMFMediaTypeHandler_iface
;
79 IMFClockStateSink IMFClockStateSink_iface
;
80 IMFMediaEventGenerator IMFMediaEventGenerator_iface
;
81 IMFGetService IMFGetService_iface
;
82 IMFSimpleAudioVolume IMFSimpleAudioVolume_iface
;
83 IMFAudioStreamVolume IMFAudioStreamVolume_iface
;
84 IMFAudioPolicy IMFAudioPolicy_iface
;
85 IMFAsyncCallback render_callback
;
87 IMFMediaEventQueue
*event_queue
;
88 IMFMediaEventQueue
*stream_event_queue
;
89 IMFPresentationClock
*clock
;
90 IMFMediaType
*media_type
;
91 IMFMediaType
*current_media_type
;
93 IAudioClient
*audio_client
;
94 IAudioRenderClient
*audio_render_client
;
95 IAudioStreamVolume
*stream_volume
;
96 ISimpleAudioVolume
*audio_volume
;
102 HANDLE buffer_ready_event
;
103 MFWORKITEM_KEY buffer_ready_key
;
104 unsigned int frame_size
;
106 enum stream_state state
;
111 static void release_pending_object(struct queued_object
*object
)
113 list_remove(&object
->entry
);
114 switch (object
->type
)
116 case OBJECT_TYPE_SAMPLE
:
117 if (object
->u
.sample
.sample
)
118 IMFSample_Release(object
->u
.sample
.sample
);
120 case OBJECT_TYPE_MARKER
:
121 PropVariantClear(&object
->u
.marker
.context
);
127 static struct audio_renderer
*impl_from_IMFMediaSink(IMFMediaSink
*iface
)
129 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFMediaSink_iface
);
132 static struct audio_renderer
*impl_from_IMFMediaSinkPreroll(IMFMediaSinkPreroll
*iface
)
134 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFMediaSinkPreroll_iface
);
137 static struct audio_renderer
*impl_from_IMFClockStateSink(IMFClockStateSink
*iface
)
139 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFClockStateSink_iface
);
142 static struct audio_renderer
*impl_from_IMFMediaEventGenerator(IMFMediaEventGenerator
*iface
)
144 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFMediaEventGenerator_iface
);
147 static struct audio_renderer
*impl_from_IMFGetService(IMFGetService
*iface
)
149 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFGetService_iface
);
152 static struct audio_renderer
*impl_from_IMFSimpleAudioVolume(IMFSimpleAudioVolume
*iface
)
154 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFSimpleAudioVolume_iface
);
157 static struct audio_renderer
*impl_from_IMFAudioStreamVolume(IMFAudioStreamVolume
*iface
)
159 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFAudioStreamVolume_iface
);
162 static struct audio_renderer
*impl_from_IMFAudioPolicy(IMFAudioPolicy
*iface
)
164 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFAudioPolicy_iface
);
167 static struct audio_renderer
*impl_from_IMFStreamSink(IMFStreamSink
*iface
)
169 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFStreamSink_iface
);
172 static struct audio_renderer
*impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler
*iface
)
174 return CONTAINING_RECORD(iface
, struct audio_renderer
, IMFMediaTypeHandler_iface
);
177 static struct audio_renderer
*impl_from_render_callback_IMFAsyncCallback(IMFAsyncCallback
*iface
)
179 return CONTAINING_RECORD(iface
, struct audio_renderer
, render_callback
);
182 static HRESULT WINAPI
audio_renderer_sink_QueryInterface(IMFMediaSink
*iface
, REFIID riid
, void **obj
)
184 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
186 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
188 if (IsEqualIID(riid
, &IID_IMFMediaSink
) ||
189 IsEqualIID(riid
, &IID_IUnknown
))
193 else if (IsEqualIID(riid
, &IID_IMFMediaSinkPreroll
))
195 *obj
= &renderer
->IMFMediaSinkPreroll_iface
;
197 else if (IsEqualIID(riid
, &IID_IMFClockStateSink
))
199 *obj
= &renderer
->IMFClockStateSink_iface
;
201 else if (IsEqualIID(riid
, &IID_IMFMediaEventGenerator
))
203 *obj
= &renderer
->IMFMediaEventGenerator_iface
;
205 else if (IsEqualIID(riid
, &IID_IMFGetService
))
207 *obj
= &renderer
->IMFGetService_iface
;
211 WARN("Unsupported %s.\n", debugstr_guid(riid
));
213 return E_NOINTERFACE
;
216 IUnknown_AddRef((IUnknown
*)*obj
);
221 static ULONG WINAPI
audio_renderer_sink_AddRef(IMFMediaSink
*iface
)
223 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
224 ULONG refcount
= InterlockedIncrement(&renderer
->refcount
);
225 TRACE("%p, refcount %u.\n", iface
, refcount
);
229 static void audio_renderer_release_audio_client(struct audio_renderer
*renderer
)
231 struct queued_object
*obj
, *obj2
;
233 MFCancelWorkItem(renderer
->buffer_ready_key
);
234 LIST_FOR_EACH_ENTRY_SAFE(obj
, obj2
, &renderer
->queue
, struct queued_object
, entry
)
236 release_pending_object(obj
);
238 renderer
->buffer_ready_key
= 0;
239 if (renderer
->audio_client
)
241 IAudioClient_Stop(renderer
->audio_client
);
242 IAudioClient_Reset(renderer
->audio_client
);
243 IAudioClient_Release(renderer
->audio_client
);
245 renderer
->audio_client
= NULL
;
246 if (renderer
->audio_render_client
)
247 IAudioRenderClient_Release(renderer
->audio_render_client
);
248 renderer
->audio_render_client
= NULL
;
249 if (renderer
->stream_volume
)
250 IAudioStreamVolume_Release(renderer
->stream_volume
);
251 renderer
->stream_volume
= NULL
;
252 if (renderer
->audio_volume
)
253 ISimpleAudioVolume_Release(renderer
->audio_volume
);
254 renderer
->audio_volume
= NULL
;
255 renderer
->flags
&= ~SAR_PREROLLED
;
258 static ULONG WINAPI
audio_renderer_sink_Release(IMFMediaSink
*iface
)
260 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
261 ULONG refcount
= InterlockedDecrement(&renderer
->refcount
);
263 TRACE("%p, refcount %u.\n", iface
, refcount
);
267 if (renderer
->event_queue
)
268 IMFMediaEventQueue_Release(renderer
->event_queue
);
269 if (renderer
->stream_event_queue
)
270 IMFMediaEventQueue_Release(renderer
->stream_event_queue
);
272 IMFPresentationClock_Release(renderer
->clock
);
273 if (renderer
->device
)
274 IMMDevice_Release(renderer
->device
);
275 if (renderer
->media_type
)
276 IMFMediaType_Release(renderer
->media_type
);
277 if (renderer
->current_media_type
)
278 IMFMediaType_Release(renderer
->current_media_type
);
279 audio_renderer_release_audio_client(renderer
);
280 CloseHandle(renderer
->buffer_ready_event
);
281 DeleteCriticalSection(&renderer
->cs
);
288 static HRESULT WINAPI
audio_renderer_sink_GetCharacteristics(IMFMediaSink
*iface
, DWORD
*flags
)
290 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
292 TRACE("%p, %p.\n", iface
, flags
);
294 if (renderer
->flags
& SAR_SHUT_DOWN
)
295 return MF_E_SHUTDOWN
;
297 *flags
= MEDIASINK_FIXED_STREAMS
| MEDIASINK_CAN_PREROLL
;
302 static HRESULT WINAPI
audio_renderer_sink_AddStreamSink(IMFMediaSink
*iface
, DWORD stream_sink_id
,
303 IMFMediaType
*media_type
, IMFStreamSink
**stream_sink
)
305 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
307 TRACE("%p, %#x, %p, %p.\n", iface
, stream_sink_id
, media_type
, stream_sink
);
309 return renderer
->flags
& SAR_SHUT_DOWN
? MF_E_SHUTDOWN
: MF_E_STREAMSINKS_FIXED
;
312 static HRESULT WINAPI
audio_renderer_sink_RemoveStreamSink(IMFMediaSink
*iface
, DWORD stream_sink_id
)
314 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
316 TRACE("%p, %#x.\n", iface
, stream_sink_id
);
318 return renderer
->flags
& SAR_SHUT_DOWN
? MF_E_SHUTDOWN
: MF_E_STREAMSINKS_FIXED
;
321 static HRESULT WINAPI
audio_renderer_sink_GetStreamSinkCount(IMFMediaSink
*iface
, DWORD
*count
)
323 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
325 TRACE("%p, %p.\n", iface
, count
);
330 if (renderer
->flags
& SAR_SHUT_DOWN
)
331 return MF_E_SHUTDOWN
;
338 static HRESULT WINAPI
audio_renderer_sink_GetStreamSinkByIndex(IMFMediaSink
*iface
, DWORD index
,
339 IMFStreamSink
**stream
)
341 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
344 TRACE("%p, %u, %p.\n", iface
, index
, stream
);
346 EnterCriticalSection(&renderer
->cs
);
348 if (renderer
->flags
& SAR_SHUT_DOWN
)
351 hr
= MF_E_INVALIDINDEX
;
354 *stream
= &renderer
->IMFStreamSink_iface
;
355 IMFStreamSink_AddRef(*stream
);
358 LeaveCriticalSection(&renderer
->cs
);
363 static HRESULT WINAPI
audio_renderer_sink_GetStreamSinkById(IMFMediaSink
*iface
, DWORD stream_sink_id
,
364 IMFStreamSink
**stream
)
366 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
369 TRACE("%p, %#x, %p.\n", iface
, stream_sink_id
, stream
);
371 EnterCriticalSection(&renderer
->cs
);
373 if (renderer
->flags
& SAR_SHUT_DOWN
)
375 else if (stream_sink_id
> 0)
376 hr
= MF_E_INVALIDSTREAMNUMBER
;
379 *stream
= &renderer
->IMFStreamSink_iface
;
380 IMFStreamSink_AddRef(*stream
);
383 LeaveCriticalSection(&renderer
->cs
);
388 static void audio_renderer_set_presentation_clock(struct audio_renderer
*renderer
, IMFPresentationClock
*clock
)
392 IMFPresentationClock_RemoveClockStateSink(renderer
->clock
, &renderer
->IMFClockStateSink_iface
);
393 IMFPresentationClock_Release(renderer
->clock
);
395 renderer
->clock
= clock
;
398 IMFPresentationClock_AddRef(renderer
->clock
);
399 IMFPresentationClock_AddClockStateSink(renderer
->clock
, &renderer
->IMFClockStateSink_iface
);
403 static HRESULT WINAPI
audio_renderer_sink_SetPresentationClock(IMFMediaSink
*iface
, IMFPresentationClock
*clock
)
405 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
408 TRACE("%p, %p.\n", iface
, clock
);
410 EnterCriticalSection(&renderer
->cs
);
412 if (renderer
->flags
& SAR_SHUT_DOWN
)
415 audio_renderer_set_presentation_clock(renderer
, clock
);
417 LeaveCriticalSection(&renderer
->cs
);
422 static HRESULT WINAPI
audio_renderer_sink_GetPresentationClock(IMFMediaSink
*iface
, IMFPresentationClock
**clock
)
424 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
427 TRACE("%p, %p.\n", iface
, clock
);
432 EnterCriticalSection(&renderer
->cs
);
434 if (renderer
->flags
& SAR_SHUT_DOWN
)
436 else if (renderer
->clock
)
438 *clock
= renderer
->clock
;
439 IMFPresentationClock_AddRef(*clock
);
444 LeaveCriticalSection(&renderer
->cs
);
449 static HRESULT WINAPI
audio_renderer_sink_Shutdown(IMFMediaSink
*iface
)
451 struct audio_renderer
*renderer
= impl_from_IMFMediaSink(iface
);
453 TRACE("%p.\n", iface
);
455 if (renderer
->flags
& SAR_SHUT_DOWN
)
456 return MF_E_SHUTDOWN
;
458 EnterCriticalSection(&renderer
->cs
);
459 renderer
->flags
|= SAR_SHUT_DOWN
;
460 IMFMediaEventQueue_Shutdown(renderer
->event_queue
);
461 IMFMediaEventQueue_Shutdown(renderer
->stream_event_queue
);
462 audio_renderer_set_presentation_clock(renderer
, NULL
);
463 audio_renderer_release_audio_client(renderer
);
464 LeaveCriticalSection(&renderer
->cs
);
469 static const IMFMediaSinkVtbl audio_renderer_sink_vtbl
=
471 audio_renderer_sink_QueryInterface
,
472 audio_renderer_sink_AddRef
,
473 audio_renderer_sink_Release
,
474 audio_renderer_sink_GetCharacteristics
,
475 audio_renderer_sink_AddStreamSink
,
476 audio_renderer_sink_RemoveStreamSink
,
477 audio_renderer_sink_GetStreamSinkCount
,
478 audio_renderer_sink_GetStreamSinkByIndex
,
479 audio_renderer_sink_GetStreamSinkById
,
480 audio_renderer_sink_SetPresentationClock
,
481 audio_renderer_sink_GetPresentationClock
,
482 audio_renderer_sink_Shutdown
,
485 static void audio_renderer_preroll(struct audio_renderer
*renderer
)
489 if (renderer
->flags
& SAR_PREROLLED
)
492 for (i
= 0; i
< 2; ++i
)
493 IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkRequestSample
, &GUID_NULL
, S_OK
, NULL
);
494 renderer
->flags
|= SAR_PREROLLED
;
497 static HRESULT WINAPI
audio_renderer_preroll_QueryInterface(IMFMediaSinkPreroll
*iface
, REFIID riid
, void **obj
)
499 struct audio_renderer
*renderer
= impl_from_IMFMediaSinkPreroll(iface
);
500 return IMFMediaSink_QueryInterface(&renderer
->IMFMediaSink_iface
, riid
, obj
);
503 static ULONG WINAPI
audio_renderer_preroll_AddRef(IMFMediaSinkPreroll
*iface
)
505 struct audio_renderer
*renderer
= impl_from_IMFMediaSinkPreroll(iface
);
506 return IMFMediaSink_AddRef(&renderer
->IMFMediaSink_iface
);
509 static ULONG WINAPI
audio_renderer_preroll_Release(IMFMediaSinkPreroll
*iface
)
511 struct audio_renderer
*renderer
= impl_from_IMFMediaSinkPreroll(iface
);
512 return IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
515 static HRESULT WINAPI
audio_renderer_preroll_NotifyPreroll(IMFMediaSinkPreroll
*iface
, MFTIME start_time
)
517 struct audio_renderer
*renderer
= impl_from_IMFMediaSinkPreroll(iface
);
519 TRACE("%p, %s.\n", iface
, debugstr_time(start_time
));
521 if (renderer
->flags
& SAR_SHUT_DOWN
)
522 return MF_E_SHUTDOWN
;
524 audio_renderer_preroll(renderer
);
525 return IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkPrerolled
, &GUID_NULL
, S_OK
, NULL
);
528 static const IMFMediaSinkPrerollVtbl audio_renderer_preroll_vtbl
=
530 audio_renderer_preroll_QueryInterface
,
531 audio_renderer_preroll_AddRef
,
532 audio_renderer_preroll_Release
,
533 audio_renderer_preroll_NotifyPreroll
,
536 static HRESULT WINAPI
audio_renderer_events_QueryInterface(IMFMediaEventGenerator
*iface
, REFIID riid
, void **obj
)
538 struct audio_renderer
*renderer
= impl_from_IMFMediaEventGenerator(iface
);
539 return IMFMediaSink_QueryInterface(&renderer
->IMFMediaSink_iface
, riid
, obj
);
542 static ULONG WINAPI
audio_renderer_events_AddRef(IMFMediaEventGenerator
*iface
)
544 struct audio_renderer
*renderer
= impl_from_IMFMediaEventGenerator(iface
);
545 return IMFMediaSink_AddRef(&renderer
->IMFMediaSink_iface
);
548 static ULONG WINAPI
audio_renderer_events_Release(IMFMediaEventGenerator
*iface
)
550 struct audio_renderer
*renderer
= impl_from_IMFMediaEventGenerator(iface
);
551 return IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
554 static HRESULT WINAPI
audio_renderer_events_GetEvent(IMFMediaEventGenerator
*iface
, DWORD flags
, IMFMediaEvent
**event
)
556 struct audio_renderer
*renderer
= impl_from_IMFMediaEventGenerator(iface
);
558 TRACE("%p, %#x, %p.\n", iface
, flags
, event
);
560 return IMFMediaEventQueue_GetEvent(renderer
->event_queue
, flags
, event
);
563 static HRESULT WINAPI
audio_renderer_events_BeginGetEvent(IMFMediaEventGenerator
*iface
, IMFAsyncCallback
*callback
,
566 struct audio_renderer
*renderer
= impl_from_IMFMediaEventGenerator(iface
);
568 TRACE("%p, %p, %p.\n", iface
, callback
, state
);
570 return IMFMediaEventQueue_BeginGetEvent(renderer
->event_queue
, callback
, state
);
573 static HRESULT WINAPI
audio_renderer_events_EndGetEvent(IMFMediaEventGenerator
*iface
, IMFAsyncResult
*result
,
574 IMFMediaEvent
**event
)
576 struct audio_renderer
*renderer
= impl_from_IMFMediaEventGenerator(iface
);
578 TRACE("%p, %p, %p.\n", iface
, result
, event
);
580 return IMFMediaEventQueue_EndGetEvent(renderer
->event_queue
, result
, event
);
583 static HRESULT WINAPI
audio_renderer_events_QueueEvent(IMFMediaEventGenerator
*iface
, MediaEventType event_type
,
584 REFGUID ext_type
, HRESULT hr
, const PROPVARIANT
*value
)
586 struct audio_renderer
*renderer
= impl_from_IMFMediaEventGenerator(iface
);
588 TRACE("%p, %u, %s, %#x, %p.\n", iface
, event_type
, debugstr_guid(ext_type
), hr
, value
);
590 return IMFMediaEventQueue_QueueEventParamVar(renderer
->event_queue
, event_type
, ext_type
, hr
, value
);
593 static const IMFMediaEventGeneratorVtbl audio_renderer_events_vtbl
=
595 audio_renderer_events_QueryInterface
,
596 audio_renderer_events_AddRef
,
597 audio_renderer_events_Release
,
598 audio_renderer_events_GetEvent
,
599 audio_renderer_events_BeginGetEvent
,
600 audio_renderer_events_EndGetEvent
,
601 audio_renderer_events_QueueEvent
,
604 static HRESULT WINAPI
audio_renderer_clock_sink_QueryInterface(IMFClockStateSink
*iface
, REFIID riid
, void **obj
)
606 struct audio_renderer
*renderer
= impl_from_IMFClockStateSink(iface
);
607 return IMFMediaSink_QueryInterface(&renderer
->IMFMediaSink_iface
, riid
, obj
);
610 static ULONG WINAPI
audio_renderer_clock_sink_AddRef(IMFClockStateSink
*iface
)
612 struct audio_renderer
*renderer
= impl_from_IMFClockStateSink(iface
);
613 return IMFMediaSink_AddRef(&renderer
->IMFMediaSink_iface
);
616 static ULONG WINAPI
audio_renderer_clock_sink_Release(IMFClockStateSink
*iface
)
618 struct audio_renderer
*renderer
= impl_from_IMFClockStateSink(iface
);
619 return IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
622 static HRESULT WINAPI
audio_renderer_clock_sink_OnClockStart(IMFClockStateSink
*iface
, MFTIME systime
, LONGLONG offset
)
624 struct audio_renderer
*renderer
= impl_from_IMFClockStateSink(iface
);
627 TRACE("%p, %s, %s.\n", iface
, debugstr_time(systime
), debugstr_time(offset
));
629 EnterCriticalSection(&renderer
->cs
);
630 if (renderer
->audio_client
)
632 if (renderer
->state
== STREAM_STATE_STOPPED
)
634 if (FAILED(hr
= IAudioClient_Start(renderer
->audio_client
)))
635 WARN("Failed to start audio client, hr %#x.\n", hr
);
636 renderer
->state
= STREAM_STATE_RUNNING
;
640 hr
= MF_E_NOT_INITIALIZED
;
642 IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkStarted
, &GUID_NULL
, hr
, NULL
);
644 audio_renderer_preroll(renderer
);
645 LeaveCriticalSection(&renderer
->cs
);
650 static HRESULT WINAPI
audio_renderer_clock_sink_OnClockStop(IMFClockStateSink
*iface
, MFTIME systime
)
652 struct audio_renderer
*renderer
= impl_from_IMFClockStateSink(iface
);
655 TRACE("%p, %s.\n", iface
, debugstr_time(systime
));
657 EnterCriticalSection(&renderer
->cs
);
658 if (renderer
->audio_client
)
660 if (renderer
->state
!= STREAM_STATE_STOPPED
)
662 if (SUCCEEDED(hr
= IAudioClient_Stop(renderer
->audio_client
)))
664 if (FAILED(hr
= IAudioClient_Reset(renderer
->audio_client
)))
665 WARN("Failed to reset audio client, hr %#x.\n", hr
);
668 WARN("Failed to stop audio client, hr %#x.\n", hr
);
669 renderer
->state
= STREAM_STATE_STOPPED
;
670 renderer
->flags
&= ~SAR_PREROLLED
;
674 hr
= MF_E_NOT_INITIALIZED
;
676 IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkStopped
, &GUID_NULL
, hr
, NULL
);
677 LeaveCriticalSection(&renderer
->cs
);
682 static HRESULT WINAPI
audio_renderer_clock_sink_OnClockPause(IMFClockStateSink
*iface
, MFTIME systime
)
684 struct audio_renderer
*renderer
= impl_from_IMFClockStateSink(iface
);
687 TRACE("%p, %s.\n", iface
, debugstr_time(systime
));
689 EnterCriticalSection(&renderer
->cs
);
690 if (renderer
->state
== STREAM_STATE_RUNNING
)
692 if (renderer
->audio_client
)
694 if (FAILED(hr
= IAudioClient_Stop(renderer
->audio_client
)))
695 WARN("Failed to stop audio client, hr %#x.\n", hr
);
696 renderer
->state
= STREAM_STATE_PAUSED
;
699 hr
= MF_E_NOT_INITIALIZED
;
701 IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkPaused
, &GUID_NULL
, hr
, NULL
);
704 hr
= MF_E_INVALID_STATE_TRANSITION
;
705 LeaveCriticalSection(&renderer
->cs
);
710 static HRESULT WINAPI
audio_renderer_clock_sink_OnClockRestart(IMFClockStateSink
*iface
, MFTIME systime
)
712 struct audio_renderer
*renderer
= impl_from_IMFClockStateSink(iface
);
713 BOOL preroll
= FALSE
;
716 TRACE("%p, %s.\n", iface
, debugstr_time(systime
));
718 EnterCriticalSection(&renderer
->cs
);
719 if (renderer
->audio_client
)
721 if ((preroll
= (renderer
->state
!= STREAM_STATE_RUNNING
)))
723 if (FAILED(hr
= IAudioClient_Start(renderer
->audio_client
)))
724 WARN("Failed to start audio client, hr %#x.\n", hr
);
725 renderer
->state
= STREAM_STATE_RUNNING
;
729 hr
= MF_E_NOT_INITIALIZED
;
731 IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkStarted
, &GUID_NULL
, hr
, NULL
);
733 audio_renderer_preroll(renderer
);
735 LeaveCriticalSection(&renderer
->cs
);
740 static HRESULT WINAPI
audio_renderer_clock_sink_OnClockSetRate(IMFClockStateSink
*iface
, MFTIME systime
, float rate
)
742 FIXME("%p, %s, %f.\n", iface
, debugstr_time(systime
), rate
);
747 static const IMFClockStateSinkVtbl audio_renderer_clock_sink_vtbl
=
749 audio_renderer_clock_sink_QueryInterface
,
750 audio_renderer_clock_sink_AddRef
,
751 audio_renderer_clock_sink_Release
,
752 audio_renderer_clock_sink_OnClockStart
,
753 audio_renderer_clock_sink_OnClockStop
,
754 audio_renderer_clock_sink_OnClockPause
,
755 audio_renderer_clock_sink_OnClockRestart
,
756 audio_renderer_clock_sink_OnClockSetRate
,
759 static HRESULT WINAPI
audio_renderer_get_service_QueryInterface(IMFGetService
*iface
, REFIID riid
, void **obj
)
761 struct audio_renderer
*renderer
= impl_from_IMFGetService(iface
);
762 return IMFMediaSink_QueryInterface(&renderer
->IMFMediaSink_iface
, riid
, obj
);
765 static ULONG WINAPI
audio_renderer_get_service_AddRef(IMFGetService
*iface
)
767 struct audio_renderer
*renderer
= impl_from_IMFGetService(iface
);
768 return IMFMediaSink_AddRef(&renderer
->IMFMediaSink_iface
);
771 static ULONG WINAPI
audio_renderer_get_service_Release(IMFGetService
*iface
)
773 struct audio_renderer
*renderer
= impl_from_IMFGetService(iface
);
774 return IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
777 static HRESULT WINAPI
audio_renderer_get_service_GetService(IMFGetService
*iface
, REFGUID service
, REFIID riid
, void **obj
)
779 struct audio_renderer
*renderer
= impl_from_IMFGetService(iface
);
781 TRACE("%p, %s, %s, %p.\n", iface
, debugstr_guid(service
), debugstr_guid(riid
), obj
);
785 if (IsEqualGUID(service
, &MR_POLICY_VOLUME_SERVICE
) && IsEqualIID(riid
, &IID_IMFSimpleAudioVolume
))
787 *obj
= &renderer
->IMFSimpleAudioVolume_iface
;
789 else if (IsEqualGUID(service
, &MR_STREAM_VOLUME_SERVICE
) && IsEqualIID(riid
, &IID_IMFAudioStreamVolume
))
791 *obj
= &renderer
->IMFAudioStreamVolume_iface
;
793 else if (IsEqualGUID(service
, &MR_AUDIO_POLICY_SERVICE
) && IsEqualIID(riid
, &IID_IMFAudioPolicy
))
795 *obj
= &renderer
->IMFAudioPolicy_iface
;
798 FIXME("Unsupported service %s, interface %s.\n", debugstr_guid(service
), debugstr_guid(riid
));
801 IUnknown_AddRef((IUnknown
*)*obj
);
803 return *obj
? S_OK
: E_NOINTERFACE
;
806 static const IMFGetServiceVtbl audio_renderer_get_service_vtbl
=
808 audio_renderer_get_service_QueryInterface
,
809 audio_renderer_get_service_AddRef
,
810 audio_renderer_get_service_Release
,
811 audio_renderer_get_service_GetService
,
814 static HRESULT WINAPI
audio_renderer_simple_volume_QueryInterface(IMFSimpleAudioVolume
*iface
, REFIID riid
, void **obj
)
816 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
818 if (IsEqualIID(riid
, &IID_IMFSimpleAudioVolume
) ||
819 IsEqualIID(riid
, &IID_IUnknown
))
822 IMFSimpleAudioVolume_AddRef(iface
);
826 WARN("Unsupported interface %s.\n", debugstr_guid(riid
));
828 return E_NOINTERFACE
;
831 static ULONG WINAPI
audio_renderer_simple_volume_AddRef(IMFSimpleAudioVolume
*iface
)
833 struct audio_renderer
*renderer
= impl_from_IMFSimpleAudioVolume(iface
);
834 return IMFMediaSink_AddRef(&renderer
->IMFMediaSink_iface
);
837 static ULONG WINAPI
audio_renderer_simple_volume_Release(IMFSimpleAudioVolume
*iface
)
839 struct audio_renderer
*renderer
= impl_from_IMFSimpleAudioVolume(iface
);
840 return IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
843 static HRESULT WINAPI
audio_renderer_simple_volume_SetMasterVolume(IMFSimpleAudioVolume
*iface
, float level
)
845 struct audio_renderer
*renderer
= impl_from_IMFSimpleAudioVolume(iface
);
848 TRACE("%p, %f.\n", iface
, level
);
850 EnterCriticalSection(&renderer
->cs
);
851 if (renderer
->audio_volume
)
852 hr
= ISimpleAudioVolume_SetMasterVolume(renderer
->audio_volume
, level
, NULL
);
853 LeaveCriticalSection(&renderer
->cs
);
858 static HRESULT WINAPI
audio_renderer_simple_volume_GetMasterVolume(IMFSimpleAudioVolume
*iface
, float *level
)
860 struct audio_renderer
*renderer
= impl_from_IMFSimpleAudioVolume(iface
);
863 TRACE("%p, %p.\n", iface
, level
);
870 EnterCriticalSection(&renderer
->cs
);
871 if (renderer
->audio_volume
)
872 hr
= ISimpleAudioVolume_GetMasterVolume(renderer
->audio_volume
, level
);
873 LeaveCriticalSection(&renderer
->cs
);
878 static HRESULT WINAPI
audio_renderer_simple_volume_SetMute(IMFSimpleAudioVolume
*iface
, BOOL mute
)
880 struct audio_renderer
*renderer
= impl_from_IMFSimpleAudioVolume(iface
);
883 TRACE("%p, %d.\n", iface
, mute
);
885 EnterCriticalSection(&renderer
->cs
);
886 if (renderer
->audio_volume
)
887 hr
= ISimpleAudioVolume_SetMute(renderer
->audio_volume
, mute
, NULL
);
888 LeaveCriticalSection(&renderer
->cs
);
893 static HRESULT WINAPI
audio_renderer_simple_volume_GetMute(IMFSimpleAudioVolume
*iface
, BOOL
*mute
)
895 struct audio_renderer
*renderer
= impl_from_IMFSimpleAudioVolume(iface
);
898 TRACE("%p, %p.\n", iface
, mute
);
905 EnterCriticalSection(&renderer
->cs
);
906 if (renderer
->audio_volume
)
907 hr
= ISimpleAudioVolume_GetMute(renderer
->audio_volume
, mute
);
908 LeaveCriticalSection(&renderer
->cs
);
913 static const IMFSimpleAudioVolumeVtbl audio_renderer_simple_volume_vtbl
=
915 audio_renderer_simple_volume_QueryInterface
,
916 audio_renderer_simple_volume_AddRef
,
917 audio_renderer_simple_volume_Release
,
918 audio_renderer_simple_volume_SetMasterVolume
,
919 audio_renderer_simple_volume_GetMasterVolume
,
920 audio_renderer_simple_volume_SetMute
,
921 audio_renderer_simple_volume_GetMute
,
924 static HRESULT WINAPI
audio_renderer_stream_volume_QueryInterface(IMFAudioStreamVolume
*iface
, REFIID riid
, void **obj
)
926 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
928 if (IsEqualIID(riid
, &IID_IMFAudioStreamVolume
) ||
929 IsEqualIID(riid
, &IID_IUnknown
))
932 IMFAudioStreamVolume_AddRef(iface
);
936 WARN("Unsupported interface %s.\n", debugstr_guid(riid
));
938 return E_NOINTERFACE
;
941 static ULONG WINAPI
audio_renderer_stream_volume_AddRef(IMFAudioStreamVolume
*iface
)
943 struct audio_renderer
*renderer
= impl_from_IMFAudioStreamVolume(iface
);
944 return IMFMediaSink_AddRef(&renderer
->IMFMediaSink_iface
);
947 static ULONG WINAPI
audio_renderer_stream_volume_Release(IMFAudioStreamVolume
*iface
)
949 struct audio_renderer
*renderer
= impl_from_IMFAudioStreamVolume(iface
);
950 return IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
953 static HRESULT WINAPI
audio_renderer_stream_volume_GetChannelCount(IMFAudioStreamVolume
*iface
, UINT32
*count
)
955 struct audio_renderer
*renderer
= impl_from_IMFAudioStreamVolume(iface
);
958 TRACE("%p, %p.\n", iface
, count
);
965 EnterCriticalSection(&renderer
->cs
);
966 if (renderer
->stream_volume
)
967 hr
= IAudioStreamVolume_GetChannelCount(renderer
->stream_volume
, count
);
968 LeaveCriticalSection(&renderer
->cs
);
973 static HRESULT WINAPI
audio_renderer_stream_volume_SetChannelVolume(IMFAudioStreamVolume
*iface
, UINT32 index
, float level
)
975 struct audio_renderer
*renderer
= impl_from_IMFAudioStreamVolume(iface
);
978 TRACE("%p, %u, %f.\n", iface
, index
, level
);
980 EnterCriticalSection(&renderer
->cs
);
981 if (renderer
->stream_volume
)
982 hr
= IAudioStreamVolume_SetChannelVolume(renderer
->stream_volume
, index
, level
);
983 LeaveCriticalSection(&renderer
->cs
);
988 static HRESULT WINAPI
audio_renderer_stream_volume_GetChannelVolume(IMFAudioStreamVolume
*iface
, UINT32 index
, float *level
)
990 struct audio_renderer
*renderer
= impl_from_IMFAudioStreamVolume(iface
);
993 TRACE("%p, %u, %p.\n", iface
, index
, level
);
1000 EnterCriticalSection(&renderer
->cs
);
1001 if (renderer
->stream_volume
)
1002 hr
= IAudioStreamVolume_GetChannelVolume(renderer
->stream_volume
, index
, level
);
1003 LeaveCriticalSection(&renderer
->cs
);
1008 static HRESULT WINAPI
audio_renderer_stream_volume_SetAllVolumes(IMFAudioStreamVolume
*iface
, UINT32 count
,
1009 const float *volumes
)
1011 struct audio_renderer
*renderer
= impl_from_IMFAudioStreamVolume(iface
);
1014 TRACE("%p, %u, %p.\n", iface
, count
, volumes
);
1016 EnterCriticalSection(&renderer
->cs
);
1017 if (renderer
->stream_volume
)
1018 hr
= IAudioStreamVolume_SetAllVolumes(renderer
->stream_volume
, count
, volumes
);
1019 LeaveCriticalSection(&renderer
->cs
);
1024 static HRESULT WINAPI
audio_renderer_stream_volume_GetAllVolumes(IMFAudioStreamVolume
*iface
, UINT32 count
, float *volumes
)
1026 struct audio_renderer
*renderer
= impl_from_IMFAudioStreamVolume(iface
);
1029 TRACE("%p, %u, %p.\n", iface
, count
, volumes
);
1035 memset(volumes
, 0, sizeof(*volumes
) * count
);
1037 EnterCriticalSection(&renderer
->cs
);
1038 if (renderer
->stream_volume
)
1039 hr
= IAudioStreamVolume_GetAllVolumes(renderer
->stream_volume
, count
, volumes
);
1040 LeaveCriticalSection(&renderer
->cs
);
1045 static const IMFAudioStreamVolumeVtbl audio_renderer_stream_volume_vtbl
=
1047 audio_renderer_stream_volume_QueryInterface
,
1048 audio_renderer_stream_volume_AddRef
,
1049 audio_renderer_stream_volume_Release
,
1050 audio_renderer_stream_volume_GetChannelCount
,
1051 audio_renderer_stream_volume_SetChannelVolume
,
1052 audio_renderer_stream_volume_GetChannelVolume
,
1053 audio_renderer_stream_volume_SetAllVolumes
,
1054 audio_renderer_stream_volume_GetAllVolumes
,
1057 static HRESULT WINAPI
audio_renderer_policy_QueryInterface(IMFAudioPolicy
*iface
, REFIID riid
, void **obj
)
1059 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
1061 if (IsEqualIID(riid
, &IID_IMFAudioPolicy
) ||
1062 IsEqualIID(riid
, &IID_IUnknown
))
1065 IMFAudioPolicy_AddRef(iface
);
1069 WARN("Unsupported interface %s.\n", debugstr_guid(riid
));
1071 return E_NOINTERFACE
;
1074 static ULONG WINAPI
audio_renderer_policy_AddRef(IMFAudioPolicy
*iface
)
1076 struct audio_renderer
*renderer
= impl_from_IMFAudioPolicy(iface
);
1077 return IMFMediaSink_AddRef(&renderer
->IMFMediaSink_iface
);
1080 static ULONG WINAPI
audio_renderer_policy_Release(IMFAudioPolicy
*iface
)
1082 struct audio_renderer
*renderer
= impl_from_IMFAudioPolicy(iface
);
1083 return IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
1086 static HRESULT WINAPI
audio_renderer_policy_SetGroupingParam(IMFAudioPolicy
*iface
, REFGUID param
)
1088 FIXME("%p, %s.\n", iface
, debugstr_guid(param
));
1093 static HRESULT WINAPI
audio_renderer_policy_GetGroupingParam(IMFAudioPolicy
*iface
, GUID
*param
)
1095 FIXME("%p, %p.\n", iface
, param
);
1100 static HRESULT WINAPI
audio_renderer_policy_SetDisplayName(IMFAudioPolicy
*iface
, const WCHAR
*name
)
1102 FIXME("%p, %s.\n", iface
, debugstr_w(name
));
1107 static HRESULT WINAPI
audio_renderer_policy_GetDisplayName(IMFAudioPolicy
*iface
, WCHAR
**name
)
1109 FIXME("%p, %p.\n", iface
, name
);
1114 static HRESULT WINAPI
audio_renderer_policy_SetIconPath(IMFAudioPolicy
*iface
, const WCHAR
*path
)
1116 FIXME("%p, %s.\n", iface
, debugstr_w(path
));
1121 static HRESULT WINAPI
audio_renderer_policy_GetIconPath(IMFAudioPolicy
*iface
, WCHAR
**path
)
1123 FIXME("%p, %p.\n", iface
, path
);
1128 static const IMFAudioPolicyVtbl audio_renderer_policy_vtbl
=
1130 audio_renderer_policy_QueryInterface
,
1131 audio_renderer_policy_AddRef
,
1132 audio_renderer_policy_Release
,
1133 audio_renderer_policy_SetGroupingParam
,
1134 audio_renderer_policy_GetGroupingParam
,
1135 audio_renderer_policy_SetDisplayName
,
1136 audio_renderer_policy_GetDisplayName
,
1137 audio_renderer_policy_SetIconPath
,
1138 audio_renderer_policy_GetIconPath
,
1141 static HRESULT
sar_create_mmdevice(IMFAttributes
*attributes
, struct audio_renderer
*renderer
)
1144 unsigned int length
, role
= eMultimedia
;
1145 IMMDeviceEnumerator
*devenum
;
1150 /* Mutually exclusive attributes. */
1151 if (SUCCEEDED(IMFAttributes_GetItem(attributes
, &MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ROLE
, NULL
)) &&
1152 SUCCEEDED(IMFAttributes_GetItem(attributes
, &MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID
, NULL
)))
1154 return E_INVALIDARG
;
1158 if (FAILED(hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
,
1159 (void **)&devenum
)))
1165 if (attributes
&& SUCCEEDED(IMFAttributes_GetUINT32(attributes
, &MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ROLE
, &role
)))
1166 TRACE("Specified role %d.\n", role
);
1168 if (attributes
&& SUCCEEDED(IMFAttributes_GetAllocatedString(attributes
, &MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID
,
1169 &endpoint
, &length
)))
1171 TRACE("Specified end point %s.\n", debugstr_w(endpoint
));
1172 hr
= IMMDeviceEnumerator_GetDevice(devenum
, endpoint
, &renderer
->device
);
1173 CoTaskMemFree(endpoint
);
1176 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, eRender
, role
, &renderer
->device
);
1178 /* Configuration attributes to be used later for audio client initialization. */
1181 IMFAttributes_GetUINT32(attributes
, &MF_AUDIO_RENDERER_ATTRIBUTE_FLAGS
, &renderer
->stream_config
.flags
);
1182 IMFAttributes_GetGUID(attributes
, &MF_AUDIO_RENDERER_ATTRIBUTE_SESSION_ID
, &renderer
->stream_config
.session_id
);
1186 hr
= MF_E_NO_AUDIO_PLAYBACK_DEVICE
;
1188 IMMDeviceEnumerator_Release(devenum
);
1193 static HRESULT WINAPI
audio_renderer_stream_QueryInterface(IMFStreamSink
*iface
, REFIID riid
, void **obj
)
1195 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1197 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
1199 if (IsEqualIID(riid
, &IID_IMFStreamSink
) ||
1200 IsEqualIID(riid
, &IID_IMFMediaEventGenerator
) ||
1201 IsEqualIID(riid
, &IID_IUnknown
))
1203 *obj
= &renderer
->IMFStreamSink_iface
;
1205 else if (IsEqualIID(riid
, &IID_IMFMediaTypeHandler
))
1207 *obj
= &renderer
->IMFMediaTypeHandler_iface
;
1211 WARN("Unsupported %s.\n", debugstr_guid(riid
));
1213 return E_NOINTERFACE
;
1216 IUnknown_AddRef((IUnknown
*)*obj
);
1221 static ULONG WINAPI
audio_renderer_stream_AddRef(IMFStreamSink
*iface
)
1223 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1224 return IMFMediaSink_AddRef(&renderer
->IMFMediaSink_iface
);
1227 static ULONG WINAPI
audio_renderer_stream_Release(IMFStreamSink
*iface
)
1229 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1230 return IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
1233 static HRESULT WINAPI
audio_renderer_stream_GetEvent(IMFStreamSink
*iface
, DWORD flags
, IMFMediaEvent
**event
)
1235 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1237 TRACE("%p, %#x, %p.\n", iface
, flags
, event
);
1239 if (renderer
->flags
& SAR_SHUT_DOWN
)
1240 return MF_E_STREAMSINK_REMOVED
;
1242 return IMFMediaEventQueue_GetEvent(renderer
->stream_event_queue
, flags
, event
);
1245 static HRESULT WINAPI
audio_renderer_stream_BeginGetEvent(IMFStreamSink
*iface
, IMFAsyncCallback
*callback
,
1248 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1250 TRACE("%p, %p, %p.\n", iface
, callback
, state
);
1252 if (renderer
->flags
& SAR_SHUT_DOWN
)
1253 return MF_E_STREAMSINK_REMOVED
;
1255 return IMFMediaEventQueue_BeginGetEvent(renderer
->stream_event_queue
, callback
, state
);
1258 static HRESULT WINAPI
audio_renderer_stream_EndGetEvent(IMFStreamSink
*iface
, IMFAsyncResult
*result
,
1259 IMFMediaEvent
**event
)
1261 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1263 TRACE("%p, %p, %p.\n", iface
, result
, event
);
1265 if (renderer
->flags
& SAR_SHUT_DOWN
)
1266 return MF_E_STREAMSINK_REMOVED
;
1268 return IMFMediaEventQueue_EndGetEvent(renderer
->stream_event_queue
, result
, event
);
1271 static HRESULT WINAPI
audio_renderer_stream_QueueEvent(IMFStreamSink
*iface
, MediaEventType event_type
,
1272 REFGUID ext_type
, HRESULT hr
, const PROPVARIANT
*value
)
1274 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1276 TRACE("%p, %u, %s, %#x, %p.\n", iface
, event_type
, debugstr_guid(ext_type
), hr
, value
);
1278 if (renderer
->flags
& SAR_SHUT_DOWN
)
1279 return MF_E_STREAMSINK_REMOVED
;
1281 return IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, event_type
, ext_type
, hr
, value
);
1284 static HRESULT WINAPI
audio_renderer_stream_GetMediaSink(IMFStreamSink
*iface
, IMFMediaSink
**sink
)
1286 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1288 TRACE("%p, %p.\n", iface
, sink
);
1290 if (renderer
->flags
& SAR_SHUT_DOWN
)
1291 return MF_E_STREAMSINK_REMOVED
;
1293 *sink
= &renderer
->IMFMediaSink_iface
;
1294 IMFMediaSink_AddRef(*sink
);
1299 static HRESULT WINAPI
audio_renderer_stream_GetIdentifier(IMFStreamSink
*iface
, DWORD
*identifier
)
1301 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1303 TRACE("%p, %p.\n", iface
, identifier
);
1305 if (renderer
->flags
& SAR_SHUT_DOWN
)
1306 return MF_E_STREAMSINK_REMOVED
;
1313 static HRESULT WINAPI
audio_renderer_stream_GetMediaTypeHandler(IMFStreamSink
*iface
, IMFMediaTypeHandler
**handler
)
1315 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1317 TRACE("%p, %p.\n", iface
, handler
);
1322 if (renderer
->flags
& SAR_SHUT_DOWN
)
1323 return MF_E_STREAMSINK_REMOVED
;
1325 *handler
= &renderer
->IMFMediaTypeHandler_iface
;
1326 IMFMediaTypeHandler_AddRef(*handler
);
1331 static HRESULT
stream_queue_sample(struct audio_renderer
*renderer
, IMFSample
*sample
)
1333 struct queued_object
*object
;
1335 if (!(object
= heap_alloc_zero(sizeof(*object
))))
1336 return E_OUTOFMEMORY
;
1338 object
->type
= OBJECT_TYPE_SAMPLE
;
1339 object
->u
.sample
.sample
= sample
;
1340 IMFSample_AddRef(object
->u
.sample
.sample
);
1342 list_add_tail(&renderer
->queue
, &object
->entry
);
1347 static HRESULT WINAPI
audio_renderer_stream_ProcessSample(IMFStreamSink
*iface
, IMFSample
*sample
)
1349 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1352 TRACE("%p, %p.\n", iface
, sample
);
1357 if (renderer
->flags
& SAR_SHUT_DOWN
)
1358 return MF_E_STREAMSINK_REMOVED
;
1360 EnterCriticalSection(&renderer
->cs
);
1361 if (renderer
->state
== STREAM_STATE_RUNNING
)
1362 hr
= stream_queue_sample(renderer
, sample
);
1363 renderer
->flags
&= ~SAR_SAMPLE_REQUESTED
;
1364 LeaveCriticalSection(&renderer
->cs
);
1369 static HRESULT
stream_place_marker(struct audio_renderer
*renderer
, MFSTREAMSINK_MARKER_TYPE marker_type
,
1370 const PROPVARIANT
*context_value
)
1372 struct queued_object
*marker
;
1375 if (!(marker
= heap_alloc_zero(sizeof(*marker
))))
1376 return E_OUTOFMEMORY
;
1378 marker
->type
= OBJECT_TYPE_MARKER
;
1379 marker
->u
.marker
.type
= marker_type
;
1380 PropVariantInit(&marker
->u
.marker
.context
);
1382 hr
= PropVariantCopy(&marker
->u
.marker
.context
, context_value
);
1384 list_add_tail(&renderer
->queue
, &marker
->entry
);
1386 release_pending_object(marker
);
1391 static HRESULT WINAPI
audio_renderer_stream_PlaceMarker(IMFStreamSink
*iface
, MFSTREAMSINK_MARKER_TYPE marker_type
,
1392 const PROPVARIANT
*marker_value
, const PROPVARIANT
*context_value
)
1394 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1397 TRACE("%p, %d, %p, %p.\n", iface
, marker_type
, marker_value
, context_value
);
1399 if (renderer
->flags
& SAR_SHUT_DOWN
)
1400 return MF_E_STREAMSINK_REMOVED
;
1402 EnterCriticalSection(&renderer
->cs
);
1403 hr
= stream_place_marker(renderer
, marker_type
, context_value
);
1404 LeaveCriticalSection(&renderer
->cs
);
1409 static HRESULT WINAPI
audio_renderer_stream_Flush(IMFStreamSink
*iface
)
1411 struct audio_renderer
*renderer
= impl_from_IMFStreamSink(iface
);
1412 struct queued_object
*obj
, *obj2
;
1415 TRACE("%p.\n", iface
);
1417 EnterCriticalSection(&renderer
->cs
);
1418 if (renderer
->flags
& SAR_SHUT_DOWN
)
1419 hr
= MF_E_STREAMSINK_REMOVED
;
1422 LIST_FOR_EACH_ENTRY_SAFE(obj
, obj2
, &renderer
->queue
, struct queued_object
, entry
)
1424 if (obj
->type
== OBJECT_TYPE_MARKER
)
1426 IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkMarker
,
1427 &GUID_NULL
, S_OK
, &obj
->u
.marker
.context
);
1429 release_pending_object(obj
);
1432 LeaveCriticalSection(&renderer
->cs
);
1437 static const IMFStreamSinkVtbl audio_renderer_stream_vtbl
=
1439 audio_renderer_stream_QueryInterface
,
1440 audio_renderer_stream_AddRef
,
1441 audio_renderer_stream_Release
,
1442 audio_renderer_stream_GetEvent
,
1443 audio_renderer_stream_BeginGetEvent
,
1444 audio_renderer_stream_EndGetEvent
,
1445 audio_renderer_stream_QueueEvent
,
1446 audio_renderer_stream_GetMediaSink
,
1447 audio_renderer_stream_GetIdentifier
,
1448 audio_renderer_stream_GetMediaTypeHandler
,
1449 audio_renderer_stream_ProcessSample
,
1450 audio_renderer_stream_PlaceMarker
,
1451 audio_renderer_stream_Flush
,
1454 static HRESULT WINAPI
audio_renderer_stream_type_handler_QueryInterface(IMFMediaTypeHandler
*iface
, REFIID riid
,
1457 struct audio_renderer
*renderer
= impl_from_IMFMediaTypeHandler(iface
);
1458 return IMFStreamSink_QueryInterface(&renderer
->IMFStreamSink_iface
, riid
, obj
);
1461 static ULONG WINAPI
audio_renderer_stream_type_handler_AddRef(IMFMediaTypeHandler
*iface
)
1463 struct audio_renderer
*renderer
= impl_from_IMFMediaTypeHandler(iface
);
1464 return IMFStreamSink_AddRef(&renderer
->IMFStreamSink_iface
);
1467 static ULONG WINAPI
audio_renderer_stream_type_handler_Release(IMFMediaTypeHandler
*iface
)
1469 struct audio_renderer
*renderer
= impl_from_IMFMediaTypeHandler(iface
);
1470 return IMFStreamSink_Release(&renderer
->IMFStreamSink_iface
);
1473 static HRESULT WINAPI
audio_renderer_stream_type_handler_IsMediaTypeSupported(IMFMediaTypeHandler
*iface
,
1474 IMFMediaType
*in_type
, IMFMediaType
**out_type
)
1476 struct audio_renderer
*renderer
= impl_from_IMFMediaTypeHandler(iface
);
1480 TRACE("%p, %p, %p.\n", iface
, in_type
, out_type
);
1482 EnterCriticalSection(&renderer
->cs
);
1483 hr
= IMFMediaType_IsEqual(renderer
->media_type
, in_type
, &flags
);
1484 LeaveCriticalSection(&renderer
->cs
);
1486 return hr
!= S_OK
? MF_E_INVALIDMEDIATYPE
: hr
;
1489 static HRESULT WINAPI
audio_renderer_stream_type_handler_GetMediaTypeCount(IMFMediaTypeHandler
*iface
, DWORD
*count
)
1491 TRACE("%p, %p.\n", iface
, count
);
1498 static HRESULT WINAPI
audio_renderer_stream_type_handler_GetMediaTypeByIndex(IMFMediaTypeHandler
*iface
, DWORD index
,
1499 IMFMediaType
**media_type
)
1501 struct audio_renderer
*renderer
= impl_from_IMFMediaTypeHandler(iface
);
1503 TRACE("%p, %u, %p.\n", iface
, index
, media_type
);
1507 *media_type
= renderer
->media_type
;
1508 IMFMediaType_AddRef(*media_type
);
1514 static HRESULT
audio_renderer_create_audio_client(struct audio_renderer
*renderer
)
1516 IMFAsyncResult
*result
;
1521 audio_renderer_release_audio_client(renderer
);
1523 hr
= IMMDevice_Activate(renderer
->device
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
,
1524 (void **)&renderer
->audio_client
);
1527 WARN("Failed to create audio client, hr %#x.\n", hr
);
1531 /* FIXME: for now always use default format. */
1532 if (FAILED(hr
= IAudioClient_GetMixFormat(renderer
->audio_client
, &wfx
)))
1534 WARN("Failed to get audio format, hr %#x.\n", hr
);
1538 renderer
->frame_size
= wfx
->wBitsPerSample
* wfx
->nChannels
/ 8;
1540 flags
= AUDCLNT_STREAMFLAGS_EVENTCALLBACK
;
1541 if (renderer
->stream_config
.flags
& MF_AUDIO_RENDERER_ATTRIBUTE_FLAGS_CROSSPROCESS
)
1542 flags
|= AUDCLNT_STREAMFLAGS_CROSSPROCESS
;
1543 if (renderer
->stream_config
.flags
& MF_AUDIO_RENDERER_ATTRIBUTE_FLAGS_NOPERSIST
)
1544 flags
|= AUDCLNT_STREAMFLAGS_NOPERSIST
;
1545 hr
= IAudioClient_Initialize(renderer
->audio_client
, AUDCLNT_SHAREMODE_SHARED
, flags
, 1000000, 0, wfx
,
1546 &renderer
->stream_config
.session_id
);
1550 WARN("Failed to initialize audio client, hr %#x.\n", hr
);
1554 if (FAILED(hr
= IAudioClient_GetService(renderer
->audio_client
, &IID_IAudioStreamVolume
,
1555 (void **)&renderer
->stream_volume
)))
1557 WARN("Failed to get stream volume control, hr %#x.\n", hr
);
1561 if (FAILED(hr
= IAudioClient_GetService(renderer
->audio_client
, &IID_ISimpleAudioVolume
, (void **)&renderer
->audio_volume
)))
1563 WARN("Failed to get audio volume control, hr %#x.\n", hr
);
1567 if (FAILED(hr
= IAudioClient_GetService(renderer
->audio_client
, &IID_IAudioRenderClient
,
1568 (void **)&renderer
->audio_render_client
)))
1570 WARN("Failed to get audio render client, hr %#x.\n", hr
);
1574 if (FAILED(hr
= IAudioClient_SetEventHandle(renderer
->audio_client
, renderer
->buffer_ready_event
)))
1576 WARN("Failed to set event handle, hr %#x.\n", hr
);
1580 if (SUCCEEDED(hr
= MFCreateAsyncResult(NULL
, &renderer
->render_callback
, NULL
, &result
)))
1582 if (FAILED(hr
= MFPutWaitingWorkItem(renderer
->buffer_ready_event
, 0, result
, &renderer
->buffer_ready_key
)))
1583 WARN("Failed to submit wait item, hr %#x.\n", hr
);
1584 IMFAsyncResult_Release(result
);
1590 static HRESULT WINAPI
audio_renderer_stream_type_handler_SetCurrentMediaType(IMFMediaTypeHandler
*iface
,
1591 IMFMediaType
*media_type
)
1593 struct audio_renderer
*renderer
= impl_from_IMFMediaTypeHandler(iface
);
1594 const unsigned int test_flags
= MF_MEDIATYPE_EQUAL_MAJOR_TYPES
| MF_MEDIATYPE_EQUAL_FORMAT_TYPES
;
1595 BOOL compare_result
;
1599 TRACE("%p, %p.\n", iface
, media_type
);
1604 EnterCriticalSection(&renderer
->cs
);
1605 if (SUCCEEDED(IMFMediaType_IsEqual(renderer
->media_type
, media_type
, &flags
)) && ((flags
& test_flags
) == test_flags
))
1607 if (renderer
->current_media_type
)
1608 IMFMediaType_Release(renderer
->current_media_type
);
1609 renderer
->current_media_type
= media_type
;
1610 IMFMediaType_AddRef(renderer
->current_media_type
);
1612 if (SUCCEEDED(hr
= audio_renderer_create_audio_client(renderer
)))
1614 if (SUCCEEDED(IMFMediaType_Compare(renderer
->media_type
, (IMFAttributes
*)media_type
, MF_ATTRIBUTES_MATCH_OUR_ITEMS
,
1615 &compare_result
)) && !compare_result
)
1617 IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkFormatInvalidated
, &GUID_NULL
,
1619 audio_renderer_preroll(renderer
);
1624 hr
= MF_E_INVALIDMEDIATYPE
;
1625 LeaveCriticalSection(&renderer
->cs
);
1630 static HRESULT WINAPI
audio_renderer_stream_type_handler_GetCurrentMediaType(IMFMediaTypeHandler
*iface
,
1631 IMFMediaType
**media_type
)
1633 struct audio_renderer
*renderer
= impl_from_IMFMediaTypeHandler(iface
);
1636 TRACE("%p, %p.\n", iface
, media_type
);
1638 EnterCriticalSection(&renderer
->cs
);
1639 if (renderer
->current_media_type
)
1641 *media_type
= renderer
->current_media_type
;
1642 IMFMediaType_AddRef(*media_type
);
1645 hr
= MF_E_NOT_INITIALIZED
;
1646 LeaveCriticalSection(&renderer
->cs
);
1651 static HRESULT WINAPI
audio_renderer_stream_type_handler_GetMajorType(IMFMediaTypeHandler
*iface
, GUID
*type
)
1653 struct audio_renderer
*renderer
= impl_from_IMFMediaTypeHandler(iface
);
1655 TRACE("%p, %p.\n", iface
, type
);
1660 if (renderer
->flags
& SAR_SHUT_DOWN
)
1661 return MF_E_STREAMSINK_REMOVED
;
1663 memcpy(type
, &MFMediaType_Audio
, sizeof(*type
));
1667 static const IMFMediaTypeHandlerVtbl audio_renderer_stream_type_handler_vtbl
=
1669 audio_renderer_stream_type_handler_QueryInterface
,
1670 audio_renderer_stream_type_handler_AddRef
,
1671 audio_renderer_stream_type_handler_Release
,
1672 audio_renderer_stream_type_handler_IsMediaTypeSupported
,
1673 audio_renderer_stream_type_handler_GetMediaTypeCount
,
1674 audio_renderer_stream_type_handler_GetMediaTypeByIndex
,
1675 audio_renderer_stream_type_handler_SetCurrentMediaType
,
1676 audio_renderer_stream_type_handler_GetCurrentMediaType
,
1677 audio_renderer_stream_type_handler_GetMajorType
,
1680 static HRESULT
audio_renderer_collect_supported_types(struct audio_renderer
*renderer
)
1682 IAudioClient
*client
;
1683 WAVEFORMATEX
*format
;
1686 if (FAILED(hr
= MFCreateMediaType(&renderer
->media_type
)))
1689 hr
= IMMDevice_Activate(renderer
->device
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, (void **)&client
);
1692 WARN("Failed to create audio client, hr %#x.\n", hr
);
1698 hr
= IAudioClient_GetMixFormat(client
, &format
);
1699 IAudioClient_Release(client
);
1702 WARN("Failed to get device audio format, hr %#x.\n", hr
);
1706 hr
= MFInitMediaTypeFromWaveFormatEx(renderer
->media_type
, format
, format
->cbSize
+ sizeof(*format
));
1707 CoTaskMemFree(format
);
1710 WARN("Failed to initialize media type, hr %#x.\n", hr
);
1714 IMFMediaType_DeleteItem(renderer
->media_type
, &MF_MT_AUDIO_PREFER_WAVEFORMATEX
);
1719 static HRESULT WINAPI
audio_renderer_render_callback_QueryInterface(IMFAsyncCallback
*iface
, REFIID riid
, void **obj
)
1721 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
1723 if (IsEqualIID(riid
, &IID_IMFAsyncCallback
) ||
1724 IsEqualIID(riid
, &IID_IUnknown
))
1727 IMFAsyncCallback_AddRef(iface
);
1731 WARN("Unsupported interface %s.\n", debugstr_guid(riid
));
1733 return E_NOINTERFACE
;
1736 static ULONG WINAPI
audio_renderer_render_callback_AddRef(IMFAsyncCallback
*iface
)
1738 struct audio_renderer
*renderer
= impl_from_render_callback_IMFAsyncCallback(iface
);
1739 return IMFMediaSink_AddRef(&renderer
->IMFMediaSink_iface
);
1742 static ULONG WINAPI
audio_renderer_render_callback_Release(IMFAsyncCallback
*iface
)
1744 struct audio_renderer
*renderer
= impl_from_render_callback_IMFAsyncCallback(iface
);
1745 return IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
1748 static HRESULT WINAPI
audio_renderer_render_callback_GetParameters(IMFAsyncCallback
*iface
, DWORD
*flags
, DWORD
*queue
)
1753 static void audio_renderer_render(struct audio_renderer
*renderer
, IMFAsyncResult
*result
)
1755 unsigned int src_frames
, dst_frames
, max_frames
, src_len
;
1756 struct queued_object
*obj
, *obj2
;
1757 BOOL keep_sample
= FALSE
;
1758 IMFMediaBuffer
*buffer
;
1762 LIST_FOR_EACH_ENTRY_SAFE(obj
, obj2
, &renderer
->queue
, struct queued_object
, entry
)
1764 if (obj
->type
== OBJECT_TYPE_MARKER
)
1766 IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkMarker
,
1767 &GUID_NULL
, S_OK
, &obj
->u
.marker
.context
);
1769 else if (obj
->type
== OBJECT_TYPE_SAMPLE
)
1771 if (SUCCEEDED(IMFSample_ConvertToContiguousBuffer(obj
->u
.sample
.sample
, &buffer
)))
1773 if (SUCCEEDED(IMFMediaBuffer_Lock(buffer
, &src
, NULL
, &src_len
)))
1775 if ((src_frames
= src_len
/ renderer
->frame_size
))
1777 if (SUCCEEDED(IAudioClient_GetBufferSize(renderer
->audio_client
, &max_frames
)))
1779 src_frames
-= obj
->u
.sample
.frame_offset
;
1780 dst_frames
= min(src_frames
, max_frames
);
1782 if (SUCCEEDED(hr
= IAudioRenderClient_GetBuffer(renderer
->audio_render_client
, dst_frames
, &dst
)))
1784 memcpy(dst
, src
+ obj
->u
.sample
.frame_offset
* renderer
->frame_size
,
1785 dst_frames
* renderer
->frame_size
);
1787 IAudioRenderClient_ReleaseBuffer(renderer
->audio_render_client
, dst_frames
, 0);
1789 obj
->u
.sample
.frame_offset
+= dst_frames
;
1792 keep_sample
= FAILED(hr
) || src_frames
> max_frames
;
1795 IMFMediaBuffer_Unlock(buffer
);
1797 IMFMediaBuffer_Release(buffer
);
1804 list_remove(&obj
->entry
);
1805 release_pending_object(obj
);
1808 if (list_empty(&renderer
->queue
) && !(renderer
->flags
& SAR_SAMPLE_REQUESTED
))
1810 IMFMediaEventQueue_QueueEventParamVar(renderer
->stream_event_queue
, MEStreamSinkRequestSample
, &GUID_NULL
, S_OK
, NULL
);
1811 renderer
->flags
|= SAR_SAMPLE_REQUESTED
;
1814 if (FAILED(hr
= MFPutWaitingWorkItem(renderer
->buffer_ready_event
, 0, result
, &renderer
->buffer_ready_key
)))
1815 WARN("Failed to submit wait item, hr %#x.\n", hr
);
1818 static HRESULT WINAPI
audio_renderer_render_callback_Invoke(IMFAsyncCallback
*iface
, IMFAsyncResult
*result
)
1820 struct audio_renderer
*renderer
= impl_from_render_callback_IMFAsyncCallback(iface
);
1822 EnterCriticalSection(&renderer
->cs
);
1823 if (!(renderer
->flags
& SAR_SHUT_DOWN
))
1824 audio_renderer_render(renderer
, result
);
1825 LeaveCriticalSection(&renderer
->cs
);
1830 static const IMFAsyncCallbackVtbl audio_renderer_render_callback_vtbl
=
1832 audio_renderer_render_callback_QueryInterface
,
1833 audio_renderer_render_callback_AddRef
,
1834 audio_renderer_render_callback_Release
,
1835 audio_renderer_render_callback_GetParameters
,
1836 audio_renderer_render_callback_Invoke
,
1839 static HRESULT
sar_create_object(IMFAttributes
*attributes
, void *user_context
, IUnknown
**obj
)
1841 struct audio_renderer
*renderer
;
1844 TRACE("%p, %p, %p.\n", attributes
, user_context
, obj
);
1846 if (!(renderer
= heap_alloc_zero(sizeof(*renderer
))))
1847 return E_OUTOFMEMORY
;
1849 renderer
->IMFMediaSink_iface
.lpVtbl
= &audio_renderer_sink_vtbl
;
1850 renderer
->IMFMediaSinkPreroll_iface
.lpVtbl
= &audio_renderer_preroll_vtbl
;
1851 renderer
->IMFStreamSink_iface
.lpVtbl
= &audio_renderer_stream_vtbl
;
1852 renderer
->IMFMediaTypeHandler_iface
.lpVtbl
= &audio_renderer_stream_type_handler_vtbl
;
1853 renderer
->IMFClockStateSink_iface
.lpVtbl
= &audio_renderer_clock_sink_vtbl
;
1854 renderer
->IMFMediaEventGenerator_iface
.lpVtbl
= &audio_renderer_events_vtbl
;
1855 renderer
->IMFGetService_iface
.lpVtbl
= &audio_renderer_get_service_vtbl
;
1856 renderer
->IMFSimpleAudioVolume_iface
.lpVtbl
= &audio_renderer_simple_volume_vtbl
;
1857 renderer
->IMFAudioStreamVolume_iface
.lpVtbl
= &audio_renderer_stream_volume_vtbl
;
1858 renderer
->IMFAudioPolicy_iface
.lpVtbl
= &audio_renderer_policy_vtbl
;
1859 renderer
->render_callback
.lpVtbl
= &audio_renderer_render_callback_vtbl
;
1860 renderer
->refcount
= 1;
1861 InitializeCriticalSection(&renderer
->cs
);
1862 renderer
->buffer_ready_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1863 list_init(&renderer
->queue
);
1865 if (FAILED(hr
= MFCreateEventQueue(&renderer
->event_queue
)))
1868 if (FAILED(hr
= MFCreateEventQueue(&renderer
->stream_event_queue
)))
1871 if (FAILED(hr
= sar_create_mmdevice(attributes
, renderer
)))
1874 if (FAILED(hr
= audio_renderer_collect_supported_types(renderer
)))
1877 *obj
= (IUnknown
*)&renderer
->IMFMediaSink_iface
;
1883 IMFMediaSink_Release(&renderer
->IMFMediaSink_iface
);
1888 static void sar_shutdown_object(void *user_context
, IUnknown
*obj
)
1892 if (SUCCEEDED(IUnknown_QueryInterface(obj
, &IID_IMFMediaSink
, (void **)&sink
)))
1894 IMFMediaSink_Shutdown(sink
);
1895 IMFMediaSink_Release(sink
);
1899 static const struct activate_funcs sar_activate_funcs
=
1901 .create_object
= sar_create_object
,
1902 .shutdown_object
= sar_shutdown_object
,
1905 /***********************************************************************
1906 * MFCreateAudioRendererActivate (mf.@)
1908 HRESULT WINAPI
MFCreateAudioRendererActivate(IMFActivate
**activate
)
1910 TRACE("%p.\n", activate
);
1915 return create_activation_object(NULL
, &sar_activate_funcs
, activate
);
1918 /***********************************************************************
1919 * MFCreateAudioRenderer (mf.@)
1921 HRESULT WINAPI
MFCreateAudioRenderer(IMFAttributes
*attributes
, IMFMediaSink
**sink
)
1926 TRACE("%p, %p.\n", attributes
, sink
);
1928 if (SUCCEEDED(hr
= sar_create_object(attributes
, NULL
, &object
)))
1930 hr
= IUnknown_QueryInterface(object
, &IID_IMFMediaSink
, (void **)sink
);
1931 IUnknown_Release(object
);