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 /* 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
);
246 filter
->stream_start
= start
;
247 SetEvent(filter
->state_event
);
248 SetEvent(filter
->run_event
);
249 if (filter
->sink
.pin
.peer
)
252 if (filter
->sink
.pin
.peer
&& filter
->ops
->renderer_start_stream
)
253 filter
->ops
->renderer_start_stream(filter
);
258 static HRESULT
renderer_stop_stream(struct strmbase_filter
*iface
)
260 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
262 ResetEvent(filter
->run_event
);
264 if (filter
->sink
.pin
.peer
&& filter
->ops
->renderer_stop_stream
)
265 filter
->ops
->renderer_stop_stream(filter
);
270 static HRESULT
renderer_cleanup_stream(struct strmbase_filter
*iface
)
272 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
274 strmbase_passthrough_invalidate_time(&filter
->passthrough
);
275 SetEvent(filter
->state_event
);
276 SetEvent(filter
->flush_event
);
281 static HRESULT
renderer_wait_state(struct strmbase_filter
*iface
, DWORD timeout
)
283 struct strmbase_renderer
*filter
= impl_from_strmbase_filter(iface
);
285 if (WaitForSingleObject(filter
->state_event
, timeout
) == WAIT_TIMEOUT
)
286 return VFW_S_STATE_INTERMEDIATE
;
290 static const struct strmbase_filter_ops filter_ops
=
292 .filter_get_pin
= renderer_get_pin
,
293 .filter_destroy
= renderer_destroy
,
294 .filter_query_interface
= renderer_query_interface
,
295 .filter_init_stream
= renderer_init_stream
,
296 .filter_start_stream
= renderer_start_stream
,
297 .filter_stop_stream
= renderer_stop_stream
,
298 .filter_cleanup_stream
= renderer_cleanup_stream
,
299 .filter_wait_state
= renderer_wait_state
,
302 static HRESULT
sink_query_accept(struct strmbase_pin
*pin
, const AM_MEDIA_TYPE
*mt
)
304 struct strmbase_renderer
*filter
= impl_from_IPin(&pin
->IPin_iface
);
305 return filter
->ops
->renderer_query_accept(filter
, mt
);
308 static HRESULT
sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
310 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->IPin_iface
);
313 if (filter
->ops
->renderer_pin_query_interface
314 && SUCCEEDED(hr
= filter
->ops
->renderer_pin_query_interface(filter
, iid
, out
)))
317 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
318 *out
= &filter
->sink
.IMemInputPin_iface
;
320 return E_NOINTERFACE
;
322 IUnknown_AddRef((IUnknown
*)*out
);
326 static HRESULT WINAPI
BaseRenderer_Receive(struct strmbase_sink
*pin
, IMediaSample
*sample
)
328 struct strmbase_renderer
*filter
= impl_from_IPin(&pin
->pin
.IPin_iface
);
329 REFERENCE_TIME start
, stop
;
330 BOOL need_wait
= FALSE
;
335 if (filter
->eos
|| filter
->sink
.flushing
)
338 state
= filter
->filter
.state
;
339 if (state
== State_Stopped
)
340 return VFW_E_WRONG_STATE
;
342 if (IMediaSample_GetMediaType(sample
, &mt
) == S_OK
)
344 TRACE("Format change.\n");
345 strmbase_dump_media_type(mt
);
347 if (FAILED(filter
->ops
->renderer_query_accept(filter
, mt
)))
348 return VFW_E_TYPE_NOT_ACCEPTED
;
352 if (filter
->filter
.clock
&& SUCCEEDED(IMediaSample_GetTime(sample
, &start
, &stop
)))
354 strmbase_passthrough_update_time(&filter
->passthrough
, start
);
358 if (state
== State_Paused
)
360 HANDLE events
[2] = {filter
->run_event
, filter
->flush_event
};
362 filter
->current_sample
= sample
;
364 hr
= filter
->ops
->renderer_render(filter
, sample
);
366 SetEvent(filter
->state_event
);
367 LeaveCriticalSection(&filter
->filter
.stream_cs
);
368 WaitForMultipleObjects(2, events
, FALSE
, INFINITE
);
369 EnterCriticalSection(&filter
->filter
.stream_cs
);
371 filter
->current_sample
= NULL
;
376 REFERENCE_TIME now
, jitter
;
379 IReferenceClock_GetTime(filter
->filter
.clock
, &now
);
381 /* "jitter" describes how early (negative) or late (positive) this
382 * buffer arrived, relative to its PTS. */
383 jitter
= (now
- filter
->stream_start
) - start
;
385 if (jitter
<= -10000) /* if we are early by at least 1 ms */
387 HANDLE handles
[2] = {filter
->advise_event
, filter
->flush_event
};
390 IReferenceClock_AdviseTime(filter
->filter
.clock
, filter
->stream_start
,
391 start
, (HEVENT
)filter
->advise_event
, &cookie
);
393 ret
= WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
394 IReferenceClock_Unadvise(filter
->filter
.clock
, cookie
);
398 TRACE("Flush signaled; discarding current sample.\n");
403 if (state
== State_Running
)
404 hr
= filter
->ops
->renderer_render(filter
, sample
);
406 perform_qos(filter
, start
, stop
, jitter
);
410 if (state
== State_Running
)
411 hr
= filter
->ops
->renderer_render(filter
, sample
);
417 static HRESULT
sink_connect(struct strmbase_sink
*iface
, IPin
*peer
, const AM_MEDIA_TYPE
*mt
)
419 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
421 if (filter
->ops
->renderer_connect
)
422 return filter
->ops
->renderer_connect(filter
, mt
);
426 static void sink_disconnect(struct strmbase_sink
*iface
)
428 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
430 if (filter
->ops
->renderer_disconnect
)
431 filter
->ops
->renderer_disconnect(filter
);
434 static HRESULT
sink_eos(struct strmbase_sink
*iface
)
436 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
437 IFilterGraph
*graph
= filter
->filter
.graph
;
438 IMediaEventSink
*event_sink
;
442 if (graph
&& SUCCEEDED(IFilterGraph_QueryInterface(graph
,
443 &IID_IMediaEventSink
, (void **)&event_sink
)))
445 IMediaEventSink_Notify(event_sink
, EC_COMPLETE
, S_OK
,
446 (LONG_PTR
)&filter
->filter
.IBaseFilter_iface
);
447 IMediaEventSink_Release(event_sink
);
449 strmbase_passthrough_eos(&filter
->passthrough
);
450 SetEvent(filter
->state_event
);
455 static HRESULT
sink_begin_flush(struct strmbase_sink
*iface
)
457 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
459 SetEvent(filter
->flush_event
);
464 static HRESULT
sink_end_flush(struct strmbase_sink
*iface
)
466 struct strmbase_renderer
*filter
= impl_from_IPin(&iface
->pin
.IPin_iface
);
468 EnterCriticalSection(&filter
->filter
.stream_cs
);
472 strmbase_passthrough_invalidate_time(&filter
->passthrough
);
473 ResetEvent(filter
->flush_event
);
475 LeaveCriticalSection(&filter
->filter
.stream_cs
);
479 static const struct strmbase_sink_ops sink_ops
=
481 .base
.pin_query_accept
= sink_query_accept
,
482 .base
.pin_query_interface
= sink_query_interface
,
483 .pfnReceive
= BaseRenderer_Receive
,
484 .sink_connect
= sink_connect
,
485 .sink_disconnect
= sink_disconnect
,
486 .sink_eos
= sink_eos
,
487 .sink_begin_flush
= sink_begin_flush
,
488 .sink_end_flush
= sink_end_flush
,
491 static struct strmbase_renderer
*impl_from_IQualityControl(IQualityControl
*iface
)
493 return CONTAINING_RECORD(iface
, struct strmbase_renderer
, IQualityControl_iface
);
496 static HRESULT WINAPI
quality_control_QueryInterface(IQualityControl
*iface
, REFIID iid
, void **out
)
498 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
499 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
502 static ULONG WINAPI
quality_control_AddRef(IQualityControl
*iface
)
504 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
505 return IUnknown_AddRef(filter
->filter
.outer_unk
);
508 static ULONG WINAPI
quality_control_Release(IQualityControl
*iface
)
510 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
511 return IUnknown_Release(filter
->filter
.outer_unk
);
514 static HRESULT WINAPI
quality_control_Notify(IQualityControl
*iface
, IBaseFilter
*sender
, Quality q
)
516 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
517 HRESULT hr
= S_FALSE
;
519 TRACE("filter %p, sender %p, type %#x, proportion %u, late %s, timestamp %s.\n",
520 filter
, sender
, q
.Type
, q
.Proportion
, debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
523 return IQualityControl_Notify(filter
->qc_sink
, &filter
->filter
.IBaseFilter_iface
, q
);
525 if (filter
->sink
.pin
.peer
)
527 IQualityControl
*peer_qc
= NULL
;
528 IPin_QueryInterface(filter
->sink
.pin
.peer
, &IID_IQualityControl
, (void **)&peer_qc
);
531 hr
= IQualityControl_Notify(peer_qc
, &filter
->filter
.IBaseFilter_iface
, q
);
532 IQualityControl_Release(peer_qc
);
539 static HRESULT WINAPI
quality_control_SetSink(IQualityControl
*iface
, IQualityControl
*sink
)
541 struct strmbase_renderer
*filter
= impl_from_IQualityControl(iface
);
543 TRACE("filter %p, sink %p.\n", filter
, sink
);
545 filter
->qc_sink
= sink
;
549 static const IQualityControlVtbl quality_control_vtbl
=
551 quality_control_QueryInterface
,
552 quality_control_AddRef
,
553 quality_control_Release
,
554 quality_control_Notify
,
555 quality_control_SetSink
,
558 void strmbase_renderer_cleanup(struct strmbase_renderer
*filter
)
560 if (filter
->sink
.pin
.peer
)
561 IPin_Disconnect(filter
->sink
.pin
.peer
);
562 IPin_Disconnect(&filter
->sink
.pin
.IPin_iface
);
563 strmbase_sink_cleanup(&filter
->sink
);
565 strmbase_passthrough_cleanup(&filter
->passthrough
);
567 CloseHandle(filter
->state_event
);
568 CloseHandle(filter
->advise_event
);
569 CloseHandle(filter
->run_event
);
570 CloseHandle(filter
->flush_event
);
571 strmbase_filter_cleanup(&filter
->filter
);
574 void strmbase_renderer_init(struct strmbase_renderer
*filter
, IUnknown
*outer
,
575 const CLSID
*clsid
, const WCHAR
*sink_name
, const struct strmbase_renderer_ops
*ops
)
577 memset(filter
, 0, sizeof(*filter
));
578 strmbase_filter_init(&filter
->filter
, outer
, clsid
, &filter_ops
);
579 strmbase_passthrough_init(&filter
->passthrough
, (IUnknown
*)&filter
->filter
.IBaseFilter_iface
);
580 ISeekingPassThru_Init(&filter
->passthrough
.ISeekingPassThru_iface
, TRUE
, &filter
->sink
.pin
.IPin_iface
);
581 filter
->IQualityControl_iface
.lpVtbl
= &quality_control_vtbl
;
585 strmbase_sink_init(&filter
->sink
, &filter
->filter
, sink_name
, &sink_ops
, NULL
);
587 filter
->state_event
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);
588 filter
->advise_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
589 filter
->run_event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
590 filter
->flush_event
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);