2 * Quality Control Interfaces
4 * Copyright 2010 Maarten Lankhorst for CodeWeavers
6 * rendering qos functions based on, the original can be found at
7 * gstreamer/libs/gst/base/gstbasesink.c which has copyright notice:
9 * Copyright (C) 2005-2007 Wim Taymans <wim.taymans@gmail.com>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "strmbase_private.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(strmbase_qc
);
30 static inline struct strmbase_qc
*impl_from_IQualityControl(IQualityControl
*iface
)
32 return CONTAINING_RECORD(iface
, struct strmbase_qc
, IQualityControl_iface
);
35 static HRESULT WINAPI
quality_control_QueryInterface(IQualityControl
*iface
, REFIID riid
, void **ppv
)
37 struct strmbase_qc
*This
= impl_from_IQualityControl(iface
);
38 return IBaseFilter_QueryInterface(&This
->pin
->filter
->IBaseFilter_iface
, riid
, ppv
);
41 static ULONG WINAPI
quality_control_AddRef(IQualityControl
*iface
)
43 struct strmbase_qc
*This
= impl_from_IQualityControl(iface
);
44 return IBaseFilter_AddRef(&This
->pin
->filter
->IBaseFilter_iface
);
47 static ULONG WINAPI
quality_control_Release(IQualityControl
*iface
)
49 struct strmbase_qc
*This
= impl_from_IQualityControl(iface
);
50 return IBaseFilter_Release(&This
->pin
->filter
->IBaseFilter_iface
);
53 static HRESULT WINAPI
quality_control_Notify(IQualityControl
*iface
, IBaseFilter
*sender
, Quality qm
)
55 struct strmbase_qc
*This
= impl_from_IQualityControl(iface
);
58 TRACE("iface %p, sender %p, type %#x, proportion %u, late %s, timestamp %s.\n",
59 iface
, sender
, qm
.Type
, qm
.Proportion
, debugstr_time(qm
.Late
), debugstr_time(qm
.TimeStamp
));
62 return IQualityControl_Notify(This
->tonotify
, &This
->pin
->filter
->IBaseFilter_iface
, qm
);
66 IQualityControl
*qc
= NULL
;
67 IPin_QueryInterface(This
->pin
->peer
, &IID_IQualityControl
, (void **)&qc
);
70 hr
= IQualityControl_Notify(qc
, &This
->pin
->filter
->IBaseFilter_iface
, qm
);
71 IQualityControl_Release(qc
);
78 static HRESULT WINAPI
quality_control_SetSink(IQualityControl
*iface
, IQualityControl
*tonotify
)
80 struct strmbase_qc
*This
= impl_from_IQualityControl(iface
);
81 TRACE("%p %p\n", This
, tonotify
);
82 This
->tonotify
= tonotify
;
86 static const IQualityControlVtbl quality_control_vtbl
=
88 quality_control_QueryInterface
,
89 quality_control_AddRef
,
90 quality_control_Release
,
91 quality_control_Notify
,
92 quality_control_SetSink
,
95 /* Macros copied from gstreamer, weighted average between old average and new ones */
96 #define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
98 /* generic running average, this has a neutral window size */
99 #define UPDATE_RUNNING_AVG(avg,val) DO_RUNNING_AVG(avg,val,8)
101 /* the windows for these running averages are experimentally obtained.
102 * positive values get averaged more while negative values use a small
103 * window so we can react faster to badness. */
104 #define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16)
105 #define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4)
107 void QualityControlRender_Start(struct strmbase_qc
*This
, REFERENCE_TIME tStart
)
109 This
->avg_render
= This
->last_in_time
= This
->last_left
= This
->avg_duration
= This
->avg_pt
= -1;
110 This
->clockstart
= tStart
;
111 This
->avg_rate
= -1.0;
112 This
->rendered
= This
->dropped
= 0;
113 This
->is_dropped
= FALSE
;
114 This
->qos_handled
= TRUE
; /* Lie that will be corrected on first adjustment */
117 static BOOL
QualityControlRender_IsLate(struct strmbase_qc
*This
, REFERENCE_TIME jitter
,
118 REFERENCE_TIME start
, REFERENCE_TIME stop
)
120 REFERENCE_TIME max_lateness
= 200000;
122 TRACE("jitter %s, start %s, stop %s.\n", debugstr_time(jitter
),
123 debugstr_time(start
), debugstr_time(stop
));
125 /* we can add a valid stop time */
127 max_lateness
+= stop
;
129 max_lateness
+= start
;
131 /* if the jitter bigger than duration and lateness we are too late */
132 if (start
+ jitter
> max_lateness
) {
133 WARN("buffer is too late %i > %i\n", (int)((start
+ jitter
)/10000), (int)(max_lateness
/10000));
134 /* !!emergency!!, if we did not receive anything valid for more than a
135 * second, render it anyway so the user sees something */
136 if (This
->last_in_time
< 0 ||
137 start
- This
->last_in_time
< 10000000)
139 FIXME("A lot of buffers are being dropped.\n");
140 FIXME("There may be a timestamping problem, or this computer is too slow.\n");
142 This
->last_in_time
= start
;
146 void QualityControlRender_DoQOS(struct strmbase_qc
*priv
)
148 REFERENCE_TIME start
, stop
, jitter
, pt
, entered
, left
, duration
;
153 if (!priv
->pin
->filter
->clock
|| priv
->current_rstart
< 0)
156 start
= priv
->current_rstart
;
157 stop
= priv
->current_rstop
;
158 jitter
= priv
->current_jitter
;
161 /* this is the time the buffer entered the sink */
165 entered
= start
+ jitter
;
168 /* this is the time the buffer entered the sink */
169 entered
= start
+ jitter
;
170 /* this is the time the buffer left the sink */
171 left
= start
+ jitter
;
174 /* calculate duration of the buffer */
176 duration
= stop
- start
;
180 /* if we have the time when the last buffer left us, calculate
182 if (priv
->last_left
>= 0) {
183 if (entered
> priv
->last_left
) {
184 pt
= entered
- priv
->last_left
;
192 TRACE("start %s, entered %s, left %s, pt %s, duration %s, jitter %s.\n",
193 debugstr_time(start
), debugstr_time(entered
), debugstr_time(left
),
194 debugstr_time(pt
), debugstr_time(duration
), debugstr_time(jitter
));
196 TRACE("average duration %s, average pt %s, average rate %.16e.\n",
197 debugstr_time(priv
->avg_duration
), debugstr_time(priv
->avg_pt
), priv
->avg_rate
);
199 /* collect running averages. for first observations, we copy the
201 if (priv
->avg_duration
< 0)
202 priv
->avg_duration
= duration
;
204 priv
->avg_duration
= UPDATE_RUNNING_AVG (priv
->avg_duration
, duration
);
206 if (priv
->avg_pt
< 0)
209 priv
->avg_pt
= UPDATE_RUNNING_AVG (priv
->avg_pt
, pt
);
211 if (priv
->avg_duration
!= 0)
213 (double)priv
->avg_pt
/
214 (double)priv
->avg_duration
;
218 if (priv
->last_left
>= 0) {
219 if (priv
->is_dropped
|| priv
->avg_rate
< 0.0) {
220 priv
->avg_rate
= rate
;
223 priv
->avg_rate
= UPDATE_RUNNING_AVG_N (priv
->avg_rate
, rate
);
225 priv
->avg_rate
= UPDATE_RUNNING_AVG_P (priv
->avg_rate
, rate
);
229 if (priv
->avg_rate
>= 0.0) {
232 /* if we have a valid rate, start sending QoS messages */
233 if (priv
->current_jitter
< 0) {
234 /* make sure we never go below 0 when adding the jitter to the
236 if (priv
->current_rstart
< -priv
->current_jitter
)
237 priv
->current_jitter
= -priv
->current_rstart
;
240 priv
->current_jitter
+= (priv
->current_rstop
- priv
->current_rstart
);
241 q
.Type
= (jitter
> 0 ? Famine
: Flood
);
242 q
.Proportion
= (LONG
)(1000. / priv
->avg_rate
);
243 if (q
.Proportion
< 200)
245 else if (q
.Proportion
> 5000)
247 q
.Late
= priv
->current_jitter
;
248 q
.TimeStamp
= priv
->current_rstart
;
249 TRACE("Late: %s from %s, rate: %g\n", debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
), 1./priv
->avg_rate
);
250 hr
= IQualityControl_Notify(&priv
->IQualityControl_iface
, &priv
->pin
->filter
->IBaseFilter_iface
, q
);
251 priv
->qos_handled
= hr
== S_OK
;
254 /* record when this buffer will leave us */
255 priv
->last_left
= left
;
259 void QualityControlRender_BeginRender(struct strmbase_qc
*This
, REFERENCE_TIME start
, REFERENCE_TIME stop
)
263 This
->current_rstart
= start
;
264 This
->current_rstop
= max(stop
, start
);
269 IReferenceClock_GetTime(This
->pin
->filter
->clock
, &now
);
270 This
->current_jitter
= (now
- This
->clockstart
) - start
;
273 This
->current_jitter
= 0;
275 /* FIXME: This isn't correct; we don't drop samples, nor should. */
276 This
->is_dropped
= QualityControlRender_IsLate(This
, This
->current_jitter
, start
, stop
);
277 TRACE("dropped %d, start %s, stop %s, jitter %s.\n", This
->is_dropped
,
278 debugstr_time(start
), debugstr_time(stop
), debugstr_time(This
->current_jitter
));
279 if (This
->is_dropped
)
284 if (!This
->pin
->filter
->clock
)
287 IReferenceClock_GetTime(This
->pin
->filter
->clock
, &This
->start
);
289 TRACE("Starting at %s.\n", debugstr_time(This
->start
));
292 void QualityControlRender_EndRender(struct strmbase_qc
*This
)
294 REFERENCE_TIME elapsed
;
298 if (!This
->pin
->filter
->clock
|| This
->start
< 0
299 || FAILED(IReferenceClock_GetTime(This
->pin
->filter
->clock
, &This
->stop
)))
302 elapsed
= This
->start
- This
->stop
;
305 if (This
->avg_render
< 0)
306 This
->avg_render
= elapsed
;
308 This
->avg_render
= UPDATE_RUNNING_AVG (This
->avg_render
, elapsed
);
311 void strmbase_qc_init(struct strmbase_qc
*qc
, struct strmbase_pin
*pin
)
313 memset(qc
, 0, sizeof(*qc
));
315 qc
->current_rstart
= qc
->current_rstop
= -1;
316 qc
->IQualityControl_iface
.lpVtbl
= &quality_control_vtbl
;