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(quartz
);
25 /* The following quality-of-service code is based on GstBaseSink QoS code, which
26 * is covered by the following copyright information:
29 * Copyright (C) 2005-2007 Wim Taymans <wim.taymans@gmail.com>
31 * gstbasesink.c: Base class for sink elements
33 * This library is free software; you can redistribute it and/or
34 * modify it under the terms of the GNU Library General Public
35 * License as published by the Free Software Foundation; either
36 * version 2 of the License, or (at your option) any later version.
38 * This library is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41 * Library General Public License for more details.
43 * You should have received a copy of the GNU Library General Public
44 * License along with this library; if not, write to the
45 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
46 * Boston, MA 02110-1301, USA.
49 #define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
51 /* Generic running average; this has a neutral window size. */
52 #define UPDATE_RUNNING_AVG(avg,val) DO_RUNNING_AVG(avg,val,8)
54 /* The windows for these running averages are experimentally obtained.
55 * Positive values get averaged more, while negative values use a small
56 * window so we can react faster to badness. */
57 #define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16)
58 #define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4)
60 static void reset_qos(struct strmbase_renderer
*filter
)
62 filter
->last_left
= filter
->avg_duration
= filter
->avg_pt
= -1;
63 filter
->avg_rate
= -1.0;
66 static void perform_qos(struct strmbase_renderer
*filter
,
67 const REFERENCE_TIME start
, const REFERENCE_TIME stop
, REFERENCE_TIME jitter
)
69 REFERENCE_TIME pt
, entered
, left
, duration
;
74 /* This is the time the buffer entered the sink. */
78 entered
= start
+ jitter
;
83 /* This is the time the buffer entered the sink. */
84 entered
= start
+ jitter
;
85 /* This is the time the buffer left the sink. */
86 left
= start
+ jitter
;
89 /* Calculate the duration of the buffer. */
91 duration
= stop
- start
;
95 /* If we have the time when the last buffer left us, calculate processing
97 if (filter
->last_left
>= 0)
99 if (entered
> filter
->last_left
)
100 pt
= entered
- filter
->last_left
;
109 TRACE("start %s, entered %s, left %s, pt %s, duration %s, jitter %s.\n",
110 debugstr_time(start
), debugstr_time(entered
), debugstr_time(left
),
111 debugstr_time(pt
), debugstr_time(duration
), debugstr_time(jitter
));
113 TRACE("average duration %s, average pt %s, average rate %.16e.\n",
114 debugstr_time(filter
->avg_duration
), debugstr_time(filter
->avg_pt
), filter
->avg_rate
);
116 /* Collect running averages. For first observations, we copy the values. */
117 if (filter
->avg_duration
< 0)
118 filter
->avg_duration
= duration
;
120 filter
->avg_duration
= UPDATE_RUNNING_AVG(filter
->avg_duration
, duration
);
122 if (filter
->avg_pt
< 0)
125 filter
->avg_pt
= UPDATE_RUNNING_AVG(filter
->avg_pt
, pt
);
127 if (filter
->avg_duration
!= 0)
128 rate
= (double)filter
->avg_pt
/ (double)filter
->avg_duration
;
132 if (filter
->last_left
>= 0)
134 if (filter
->avg_rate
< 0.0)
136 filter
->avg_rate
= rate
;
141 filter
->avg_rate
= UPDATE_RUNNING_AVG_N(filter
->avg_rate
, rate
);
143 filter
->avg_rate
= UPDATE_RUNNING_AVG_P(filter
->avg_rate
, rate
);
147 if (filter
->avg_rate
>= 0.0)
151 /* If we have a valid rate, start sending QoS messages. */
154 /* Make sure we never go below 0 when adding the jitter to the
161 jitter
+= stop
- start
;
164 q
.Type
= (jitter
> 0 ? Famine
: Flood
);
165 q
.Proportion
= 1000.0 / filter
->avg_rate
;
166 if (q
.Proportion
< 200)
168 else if (q
.Proportion
> 5000)
172 IQualityControl_Notify(&filter
->IQualityControl_iface
, &filter
->filter
.IBaseFilter_iface
, q
);
175 /* Record when this buffer will leave us. */
176 filter
->last_left
= left
;
179 static inline struct strmbase_renderer
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
181 return CONTAINING_RECORD(iface
, struct strmbase_renderer
, filter
);
184 static inline struct strmbase_renderer
*impl_from_IPin(IPin
*iface
)
186 return CONTAINING_RECORD(iface
, struct strmbase_renderer
, sink
.pin
.IPin_iface
);
189 static struct strmbase_pin
*renderer_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
191 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
194 return &filter
->sink
.pin
;
198 static void renderer_destroy(struct strmbase_filter
*iface
)
200 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
201 filter
->ops
->renderer_destroy(filter
);
204 static HRESULT
renderer_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
206 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
209 if (filter
->ops
->renderer_query_interface
210 && SUCCEEDED(hr
= filter
->ops
->renderer_query_interface(filter
, iid
, out
)))
215 if (IsEqualGUID(iid
, &IID_IMediaPosition
))
216 *out
= &filter
->passthrough
.IMediaPosition_iface
;
217 else if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
218 *out
= &filter
->passthrough
.IMediaSeeking_iface
;
219 else if (IsEqualGUID(iid
, &IID_IQualityControl
))
220 *out
= &filter
->IQualityControl_iface
;
222 return E_NOINTERFACE
;
224 IUnknown_AddRef((IUnknown
*)*out
);
228 static HRESULT
renderer_init_stream(struct strmbase_filter
*iface
)
230 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
232 if (filter
->sink
.pin
.peer
)
233 ResetEvent(filter
->state_event
);
235 ResetEvent(filter
->flush_event
);
236 if (filter
->ops
->renderer_init_stream
)
237 filter
->ops
->renderer_init_stream(filter
);
239 return filter
->sink
.pin
.peer
? S_FALSE
: S_OK
;
242 static HRESULT
renderer_start_stream(struct strmbase_filter
*iface
, REFERENCE_TIME start
)
244 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
245 IFilterGraph
*graph
= filter
->filter
.graph
;
246 IMediaEventSink
*event_sink
;
248 filter
->stream_start
= start
;
249 SetEvent(filter
->state_event
);
250 SetEvent(filter
->run_event
);
252 if (filter
->sink
.pin
.peer
&& filter
->ops
->renderer_start_stream
)
253 filter
->ops
->renderer_start_stream(filter
);
255 if ((filter
->eos
|| !filter
->sink
.pin
.peer
) && graph
256 && SUCCEEDED(IFilterGraph_QueryInterface(graph
,
257 &IID_IMediaEventSink
, (void **)&event_sink
)))
259 IMediaEventSink_Notify(event_sink
, EC_COMPLETE
, S_OK
,
260 (LONG_PTR
)&filter
->filter
.IBaseFilter_iface
);
261 IMediaEventSink_Release(event_sink
);
267 static HRESULT
renderer_stop_stream(struct strmbase_filter
*iface
)
269 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
271 ResetEvent(filter
->run_event
);
273 if (filter
->sink
.pin
.peer
&& filter
->ops
->renderer_stop_stream
)
274 filter
->ops
->renderer_stop_stream(filter
);
279 static HRESULT
renderer_cleanup_stream(struct strmbase_filter
*iface
)
281 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
283 strmbase_passthrough_invalidate_time(&filter
->passthrough
);
284 SetEvent(filter
->state_event
);
285 SetEvent(filter
->flush_event
);
290 static HRESULT
renderer_wait_state(struct strmbase_filter
*iface
, DWORD timeout
)
292 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
294 if (WaitForSingleObject(filter
->state_event
, timeout
) == WAIT_TIMEOUT
)
295 return VFW_S_STATE_INTERMEDIATE
;
299 static const struct strmbase_filter_ops filter_ops
=
301 .filter_get_pin
= renderer_get_pin
,
302 .filter_destroy
= renderer_destroy
,
303 .filter_query_interface
= renderer_query_interface
,
304 .filter_init_stream
= renderer_init_stream
,
305 .filter_start_stream
= renderer_start_stream
,
306 .filter_stop_stream
= renderer_stop_stream
,
307 .filter_cleanup_stream
= renderer_cleanup_stream
,
308 .filter_wait_state
= renderer_wait_state
,
311 static HRESULT
sink_query_accept(struct strmbase_pin
*pin
, const AM_MEDIA_TYPE
*mt
)
313 struct strmbase_renderer
*filter
= impl_from_IPin(&pin
->IPin_iface
);
314 return filter
->ops
->renderer_query_accept(filter
, mt
);
317 static HRESULT
sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
319 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->IPin_iface
);
322 if (filter
->ops
->renderer_pin_query_interface
323 && SUCCEEDED(hr
= filter
->ops
->renderer_pin_query_interface(filter
, iid
, out
)))
326 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
327 *out
= &filter
->sink
.IMemInputPin_iface
;
329 return E_NOINTERFACE
;
331 IUnknown_AddRef((IUnknown
*)*out
);
335 static HRESULT WINAPI
BaseRenderer_Receive(struct strmbase_sink
*pin
, IMediaSample
*sample
)
337 struct strmbase_renderer
*filter
= impl_from_IPin(&pin
->pin
.IPin_iface
);
338 REFERENCE_TIME start
, stop
;
339 BOOL need_wait
= FALSE
;
344 if (filter
->eos
|| filter
->sink
.flushing
)
347 state
= filter
->filter
.state
;
348 if (state
== State_Stopped
)
349 return VFW_E_WRONG_STATE
;
351 if (IMediaSample_GetMediaType(sample
, &mt
) == S_OK
)
353 TRACE("Format change.\n");
354 strmbase_dump_media_type(mt
);
356 if (FAILED(filter
->ops
->renderer_query_accept(filter
, mt
)))
357 return VFW_E_TYPE_NOT_ACCEPTED
;
361 if (filter
->filter
.clock
&& SUCCEEDED(IMediaSample_GetTime(sample
, &start
, &stop
)))
363 strmbase_passthrough_update_time(&filter
->passthrough
, start
);
367 if (state
== State_Paused
)
369 HANDLE events
[2] = {filter
->run_event
, filter
->flush_event
};
371 filter
->current_sample
= sample
;
373 hr
= filter
->ops
->renderer_render(filter
, sample
);
375 SetEvent(filter
->state_event
);
376 LeaveCriticalSection(&filter
->filter
.stream_cs
);
377 WaitForMultipleObjects(2, events
, FALSE
, INFINITE
);
378 EnterCriticalSection(&filter
->filter
.stream_cs
);
380 filter
->current_sample
= NULL
;
385 REFERENCE_TIME now
, jitter
;
388 IReferenceClock_GetTime(filter
->filter
.clock
, &now
);
390 /* "jitter" describes how early (negative) or late (positive) this
391 * buffer arrived, relative to its PTS. */
392 jitter
= (now
- filter
->stream_start
) - start
;
394 if (jitter
<= -10000) /* if we are early by at least 1 ms */
396 HANDLE handles
[2] = {filter
->advise_event
, filter
->flush_event
};
399 IReferenceClock_AdviseTime(filter
->filter
.clock
, filter
->stream_start
,
400 start
, (HEVENT
)filter
->advise_event
, &cookie
);
402 ret
= WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
403 IReferenceClock_Unadvise(filter
->filter
.clock
, cookie
);
407 TRACE("Flush signaled; discarding current sample.\n");
412 if (state
== State_Running
)
413 hr
= filter
->ops
->renderer_render(filter
, sample
);
415 perform_qos(filter
, start
, stop
, jitter
);
419 if (state
== State_Running
)
420 hr
= filter
->ops
->renderer_render(filter
, sample
);
426 static HRESULT
sink_connect(struct strmbase_sink
*iface
, IPin
*peer
, const AM_MEDIA_TYPE
*mt
)
428 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
430 if (filter
->ops
->renderer_connect
)
431 return filter
->ops
->renderer_connect(filter
, mt
);
435 static void sink_disconnect(struct strmbase_sink
*iface
)
437 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
439 if (filter
->ops
->renderer_disconnect
)
440 filter
->ops
->renderer_disconnect(filter
);
443 static HRESULT
sink_eos(struct strmbase_sink
*iface
)
445 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
446 IFilterGraph
*graph
= filter
->filter
.graph
;
447 IMediaEventSink
*event_sink
;
451 if (filter
->filter
.state
== State_Running
&& graph
452 && SUCCEEDED(IFilterGraph_QueryInterface(graph
,
453 &IID_IMediaEventSink
, (void **)&event_sink
)))
455 IMediaEventSink_Notify(event_sink
, EC_COMPLETE
, S_OK
,
456 (LONG_PTR
)&filter
->filter
.IBaseFilter_iface
);
457 IMediaEventSink_Release(event_sink
);
459 strmbase_passthrough_eos(&filter
->passthrough
);
460 SetEvent(filter
->state_event
);
465 static HRESULT
sink_begin_flush(struct strmbase_sink
*iface
)
467 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
469 SetEvent(filter
->flush_event
);
474 static HRESULT
sink_end_flush(struct strmbase_sink
*iface
)
476 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
478 EnterCriticalSection(&filter
->filter
.stream_cs
);
482 strmbase_passthrough_invalidate_time(&filter
->passthrough
);
483 ResetEvent(filter
->flush_event
);
485 LeaveCriticalSection(&filter
->filter
.stream_cs
);
489 static const struct strmbase_sink_ops sink_ops
=
491 .base
.pin_query_accept
= sink_query_accept
,
492 .base
.pin_query_interface
= sink_query_interface
,
493 .pfnReceive
= BaseRenderer_Receive
,
494 .sink_connect
= sink_connect
,
495 .sink_disconnect
= sink_disconnect
,
496 .sink_eos
= sink_eos
,
497 .sink_begin_flush
= sink_begin_flush
,
498 .sink_end_flush
= sink_end_flush
,
501 static struct strmbase_renderer
*impl_from_IQualityControl(IQualityControl
*iface
)
503 return CONTAINING_RECORD(iface
, struct strmbase_renderer
, IQualityControl_iface
);
506 static HRESULT WINAPI
quality_control_QueryInterface(IQualityControl
*iface
, REFIID iid
, void **out
)
508 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
509 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
512 static ULONG WINAPI
quality_control_AddRef(IQualityControl
*iface
)
514 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
515 return IUnknown_AddRef(filter
->filter
.outer_unk
);
518 static ULONG WINAPI
quality_control_Release(IQualityControl
*iface
)
520 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
521 return IUnknown_Release(filter
->filter
.outer_unk
);
524 static HRESULT WINAPI
quality_control_Notify(IQualityControl
*iface
, IBaseFilter
*sender
, Quality q
)
526 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
527 HRESULT hr
= S_FALSE
;
529 TRACE("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s.\n",
530 filter
, sender
, q
.Type
, q
.Proportion
, debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
533 return IQualityControl_Notify(filter
->qc_sink
, &filter
->filter
.IBaseFilter_iface
, q
);
535 if (filter
->sink
.pin
.peer
)
537 IQualityControl
*peer_qc
= NULL
;
538 IPin_QueryInterface(filter
->sink
.pin
.peer
, &IID_IQualityControl
, (void **)&peer_qc
);
541 hr
= IQualityControl_Notify(peer_qc
, &filter
->filter
.IBaseFilter_iface
, q
);
542 IQualityControl_Release(peer_qc
);
549 static HRESULT WINAPI
quality_control_SetSink(IQualityControl
*iface
, IQualityControl
*sink
)
551 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
553 TRACE("filter %p, sink %p.\n", filter
, sink
);
555 filter
->qc_sink
= sink
;
559 static const IQualityControlVtbl quality_control_vtbl
=
561 quality_control_QueryInterface
,
562 quality_control_AddRef
,
563 quality_control_Release
,
564 quality_control_Notify
,
565 quality_control_SetSink
,
568 void strmbase_renderer_cleanup(struct strmbase_renderer
*filter
)
570 if (filter
->sink
.pin
.peer
)
571 IPin_Disconnect(filter
->sink
.pin
.peer
);
572 IPin_Disconnect(&filter
->sink
.pin
.IPin_iface
);
573 strmbase_sink_cleanup(&filter
->sink
);
575 strmbase_passthrough_cleanup(&filter
->passthrough
);
577 CloseHandle(filter
->state_event
);
578 CloseHandle(filter
->advise_event
);
579 CloseHandle(filter
->run_event
);
580 CloseHandle(filter
->flush_event
);
581 strmbase_filter_cleanup(&filter
->filter
);
584 void strmbase_renderer_init(struct strmbase_renderer
*filter
, IUnknown
*outer
,
585 const CLSID
*clsid
, const WCHAR
*sink_name
, const struct strmbase_renderer_ops
*ops
)
587 memset(filter
, 0, sizeof(*filter
));
588 strmbase_filter_init(&filter
->filter
, outer
, clsid
, &filter_ops
);
589 strmbase_passthrough_init(&filter
->passthrough
, (IUnknown
*)&filter
->filter
.IBaseFilter_iface
);
590 ISeekingPassThru_Init(&filter
->passthrough
.ISeekingPassThru_iface
, TRUE
, &filter
->sink
.pin
.IPin_iface
);
591 filter
->IQualityControl_iface
.lpVtbl
= &quality_control_vtbl
;
595 strmbase_sink_init(&filter
->sink
, &filter
->filter
, sink_name
, &sink_ops
, NULL
);
597 filter
->state_event
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);
598 filter
->advise_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
599 filter
->run_event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
600 filter
->flush_event
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);