2 * Generic Implementation of strmbase Base Renderer classes
4 * Copyright 2012 Aric Stewart, CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "strmbase_private.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(strmbase
);
25 static inline struct strmbase_renderer
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
27 return CONTAINING_RECORD(iface
, struct strmbase_renderer
, filter
);
30 static const IQualityControlVtbl Renderer_QualityControl_Vtbl
= {
31 QualityControlImpl_QueryInterface
,
32 QualityControlImpl_AddRef
,
33 QualityControlImpl_Release
,
34 QualityControlImpl_Notify
,
35 QualityControlImpl_SetSink
38 static inline struct strmbase_renderer
*impl_from_IPin(IPin
*iface
)
40 return CONTAINING_RECORD(iface
, struct strmbase_renderer
, sink
.pin
.IPin_iface
);
43 static struct strmbase_pin
*renderer_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
45 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
48 return &filter
->sink
.pin
;
52 static void renderer_destroy(struct strmbase_filter
*iface
)
54 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
55 filter
->pFuncsTable
->renderer_destroy(filter
);
58 static HRESULT
renderer_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
60 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
63 if (filter
->pFuncsTable
->renderer_query_interface
64 && SUCCEEDED(hr
= filter
->pFuncsTable
->renderer_query_interface(filter
, iid
, out
)))
69 if (IsEqualGUID(iid
, &IID_IMediaPosition
))
70 *out
= &filter
->passthrough
.IMediaPosition_iface
;
71 else if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
72 *out
= &filter
->passthrough
.IMediaSeeking_iface
;
73 else if (IsEqualGUID(iid
, &IID_IQualityControl
))
74 *out
= &filter
->qcimpl
->IQualityControl_iface
;
78 IUnknown_AddRef((IUnknown
*)*out
);
82 static HRESULT
renderer_init_stream(struct strmbase_filter
*iface
)
84 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
86 if (filter
->sink
.pin
.peer
)
87 ResetEvent(filter
->state_event
);
89 ResetEvent(filter
->flush_event
);
90 if (filter
->pFuncsTable
->renderer_init_stream
)
91 filter
->pFuncsTable
->renderer_init_stream(filter
);
93 return filter
->sink
.pin
.peer
? S_FALSE
: S_OK
;
96 static HRESULT
renderer_start_stream(struct strmbase_filter
*iface
, REFERENCE_TIME start
)
98 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
100 filter
->stream_start
= start
;
101 SetEvent(filter
->state_event
);
102 if (filter
->sink
.pin
.peer
)
104 QualityControlRender_Start(filter
->qcimpl
, filter
->stream_start
);
105 if (filter
->sink
.pin
.peer
&& filter
->pFuncsTable
->renderer_start_stream
)
106 filter
->pFuncsTable
->renderer_start_stream(filter
);
111 static HRESULT
renderer_stop_stream(struct strmbase_filter
*iface
)
113 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
115 if (filter
->sink
.pin
.peer
&& filter
->pFuncsTable
->renderer_stop_stream
)
116 filter
->pFuncsTable
->renderer_stop_stream(filter
);
121 static HRESULT
renderer_cleanup_stream(struct strmbase_filter
*iface
)
123 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
125 strmbase_passthrough_invalidate_time(&filter
->passthrough
);
126 SetEvent(filter
->state_event
);
127 SetEvent(filter
->flush_event
);
132 static HRESULT
renderer_wait_state(struct strmbase_filter
*iface
, DWORD timeout
)
134 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
136 if (WaitForSingleObject(filter
->state_event
, timeout
) == WAIT_TIMEOUT
)
137 return VFW_S_STATE_INTERMEDIATE
;
141 static const struct strmbase_filter_ops filter_ops
=
143 .filter_get_pin
= renderer_get_pin
,
144 .filter_destroy
= renderer_destroy
,
145 .filter_query_interface
= renderer_query_interface
,
146 .filter_init_stream
= renderer_init_stream
,
147 .filter_start_stream
= renderer_start_stream
,
148 .filter_stop_stream
= renderer_stop_stream
,
149 .filter_cleanup_stream
= renderer_cleanup_stream
,
150 .filter_wait_state
= renderer_wait_state
,
153 static HRESULT
sink_query_accept(struct strmbase_pin
*pin
, const AM_MEDIA_TYPE
*mt
)
155 struct strmbase_renderer
*filter
= impl_from_IPin(&pin
->IPin_iface
);
156 return filter
->pFuncsTable
->pfnCheckMediaType(filter
, mt
);
159 static HRESULT
sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
161 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->IPin_iface
);
164 if (filter
->pFuncsTable
->renderer_pin_query_interface
165 && SUCCEEDED(hr
= filter
->pFuncsTable
->renderer_pin_query_interface(filter
, iid
, out
)))
168 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
169 *out
= &filter
->sink
.IMemInputPin_iface
;
171 return E_NOINTERFACE
;
173 IUnknown_AddRef((IUnknown
*)*out
);
177 static HRESULT WINAPI
BaseRenderer_Receive(struct strmbase_sink
*pin
, IMediaSample
*sample
)
179 struct strmbase_renderer
*filter
= impl_from_IPin(&pin
->pin
.IPin_iface
);
180 return BaseRendererImpl_Receive(filter
, sample
);
183 static HRESULT
sink_connect(struct strmbase_sink
*iface
, IPin
*peer
, const AM_MEDIA_TYPE
*mt
)
185 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
187 if (filter
->pFuncsTable
->renderer_connect
)
188 return filter
->pFuncsTable
->renderer_connect(filter
, mt
);
192 static void sink_disconnect(struct strmbase_sink
*iface
)
194 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
196 if (filter
->pFuncsTable
->pfnBreakConnect
)
197 filter
->pFuncsTable
->pfnBreakConnect(filter
);
200 static HRESULT
sink_eos(struct strmbase_sink
*iface
)
202 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
203 IFilterGraph
*graph
= filter
->filter
.graph
;
204 IMediaEventSink
*event_sink
;
207 EnterCriticalSection(&filter
->csRenderLock
);
211 if (graph
&& SUCCEEDED(IFilterGraph_QueryInterface(graph
,
212 &IID_IMediaEventSink
, (void **)&event_sink
)))
214 IMediaEventSink_Notify(event_sink
, EC_COMPLETE
, S_OK
,
215 (LONG_PTR
)&filter
->filter
.IBaseFilter_iface
);
216 IMediaEventSink_Release(event_sink
);
218 strmbase_passthrough_eos(&filter
->passthrough
);
219 SetEvent(filter
->state_event
);
221 if (filter
->pFuncsTable
->pfnEndOfStream
)
222 hr
= filter
->pFuncsTable
->pfnEndOfStream(filter
);
224 LeaveCriticalSection(&filter
->csRenderLock
);
228 static HRESULT
sink_begin_flush(struct strmbase_sink
*iface
)
230 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
232 SetEvent(filter
->flush_event
);
237 static HRESULT
sink_end_flush(struct strmbase_sink
*iface
)
239 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
242 EnterCriticalSection(&filter
->csRenderLock
);
245 QualityControlRender_Start(filter
->qcimpl
, filter
->stream_start
);
246 strmbase_passthrough_invalidate_time(&filter
->passthrough
);
247 ResetEvent(filter
->flush_event
);
249 if (filter
->pFuncsTable
->pfnEndFlush
)
250 hr
= filter
->pFuncsTable
->pfnEndFlush(filter
);
252 LeaveCriticalSection(&filter
->csRenderLock
);
256 static const struct strmbase_sink_ops sink_ops
=
258 .base
.pin_query_accept
= sink_query_accept
,
259 .base
.pin_query_interface
= sink_query_interface
,
260 .pfnReceive
= BaseRenderer_Receive
,
261 .sink_connect
= sink_connect
,
262 .sink_disconnect
= sink_disconnect
,
263 .sink_eos
= sink_eos
,
264 .sink_begin_flush
= sink_begin_flush
,
265 .sink_end_flush
= sink_end_flush
,
268 void strmbase_renderer_cleanup(struct strmbase_renderer
*filter
)
270 if (filter
->sink
.pin
.peer
)
271 IPin_Disconnect(filter
->sink
.pin
.peer
);
272 IPin_Disconnect(&filter
->sink
.pin
.IPin_iface
);
273 strmbase_sink_cleanup(&filter
->sink
);
275 strmbase_passthrough_cleanup(&filter
->passthrough
);
277 filter
->csRenderLock
.DebugInfo
->Spare
[0] = 0;
278 DeleteCriticalSection(&filter
->csRenderLock
);
280 CloseHandle(filter
->state_event
);
281 CloseHandle(filter
->advise_event
);
282 CloseHandle(filter
->flush_event
);
283 QualityControlImpl_Destroy(filter
->qcimpl
);
284 strmbase_filter_cleanup(&filter
->filter
);
287 HRESULT WINAPI
BaseRendererImpl_Receive(struct strmbase_renderer
*This
, IMediaSample
*pSample
)
290 REFERENCE_TIME start
, stop
;
293 TRACE("(%p)->%p\n", This
, pSample
);
295 if (This
->eos
|| This
->sink
.flushing
)
298 if (This
->filter
.state
== State_Stopped
)
299 return VFW_E_WRONG_STATE
;
301 if (IMediaSample_GetMediaType(pSample
, &pmt
) == S_OK
)
303 TRACE("Format change.\n");
304 strmbase_dump_media_type(pmt
);
306 if (FAILED(This
->pFuncsTable
->pfnCheckMediaType(This
, pmt
)))
308 return VFW_E_TYPE_NOT_ACCEPTED
;
310 DeleteMediaType(pmt
);
313 if (This
->pFuncsTable
->pfnPrepareReceive
)
314 hr
= This
->pFuncsTable
->pfnPrepareReceive(This
, pSample
);
317 if (hr
== VFW_E_SAMPLE_REJECTED
)
323 EnterCriticalSection(&This
->csRenderLock
);
324 if (This
->filter
.state
== State_Paused
)
325 SetEvent(This
->state_event
);
327 /* Wait for render Time */
328 if (This
->filter
.clock
&& SUCCEEDED(IMediaSample_GetTime(pSample
, &start
, &stop
)))
331 strmbase_passthrough_update_time(&This
->passthrough
, start
);
332 if (This
->pFuncsTable
->pfnShouldDrawSampleNow
)
333 hr
= This
->pFuncsTable
->pfnShouldDrawSampleNow(This
, pSample
, &start
, &stop
);
336 ;/* Do not wait: drop through */
337 else if (hr
== S_FALSE
)
342 IReferenceClock_GetTime(This
->filter
.clock
, &now
);
344 if (now
- This
->stream_start
- start
<= -10000)
346 HANDLE handles
[2] = {This
->advise_event
, This
->flush_event
};
349 IReferenceClock_AdviseTime(This
->filter
.clock
, This
->stream_start
,
350 start
, (HEVENT
)This
->advise_event
, &cookie
);
352 LeaveCriticalSection(&This
->csRenderLock
);
354 ret
= WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
355 IReferenceClock_Unadvise(This
->filter
.clock
, cookie
);
359 TRACE("Flush signaled, discarding current sample.\n");
363 EnterCriticalSection(&This
->csRenderLock
);
368 LeaveCriticalSection(&This
->csRenderLock
);
378 QualityControlRender_BeginRender(This
->qcimpl
, start
, stop
);
379 hr
= This
->pFuncsTable
->pfnDoRenderSample(This
, pSample
);
380 QualityControlRender_EndRender(This
->qcimpl
);
383 QualityControlRender_DoQOS(This
->qcimpl
);
385 LeaveCriticalSection(&This
->csRenderLock
);
390 void strmbase_renderer_init(struct strmbase_renderer
*filter
, IUnknown
*outer
,
391 const CLSID
*clsid
, const WCHAR
*sink_name
, const struct strmbase_renderer_ops
*ops
)
393 memset(filter
, 0, sizeof(*filter
));
394 strmbase_filter_init(&filter
->filter
, outer
, clsid
, &filter_ops
);
395 strmbase_passthrough_init(&filter
->passthrough
, (IUnknown
*)&filter
->filter
.IBaseFilter_iface
);
396 ISeekingPassThru_Init(&filter
->passthrough
.ISeekingPassThru_iface
, TRUE
, &filter
->sink
.pin
.IPin_iface
);
398 filter
->pFuncsTable
= ops
;
400 strmbase_sink_init(&filter
->sink
, &filter
->filter
, sink_name
, &sink_ops
, NULL
);
402 InitializeCriticalSection(&filter
->csRenderLock
);
403 filter
->csRenderLock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": strmbase_renderer.csRenderLock");
404 filter
->state_event
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);
405 filter
->advise_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
406 filter
->flush_event
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);
408 QualityControlImpl_Create(&filter
->sink
.pin
, &filter
->qcimpl
);
409 filter
->qcimpl
->IQualityControl_iface
.lpVtbl
= &Renderer_QualityControl_Vtbl
;