HRESULT WINAPI QualityControlImpl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm) { QualityControlImpl *This = impl_from_IQualityControl(iface); HRESULT hr = S_FALSE; TRACE("%p %p { 0x%x %u " XTIME_FMT " " XTIME_FMT " }\n", This, sender, qm.Type, qm.Proportion, XTIME(qm.Late), XTIME(qm.TimeStamp)); if (This->tonotify) return IQualityControl_Notify(This->tonotify, This->self, qm); if (This->input) { IPin *to = NULL; IPin_ConnectedTo(This->input, &to); if (to) { IQualityControl *qc = NULL; IPin_QueryInterface(to, &IID_IQualityControl, (void**)&qc); if (qc) { hr = IQualityControl_Notify(qc, This->self, qm); IQualityControl_Release(qc); } IPin_Release(to); } } return hr; }
static HRESULT WINAPI DSoundRender_DoRenderSample(BaseRenderer *iface, IMediaSample * pSample) { DSoundRenderImpl *This = impl_from_BaseRenderer(iface); LPBYTE pbSrcStream = NULL; LONG cbSrcStream = 0; REFERENCE_TIME tStart, tStop; HRESULT hr; TRACE("%p %p\n", iface, pSample); /* Slightly incorrect, Pause completes when a frame is received so we should signal * pause completion here, but for sound playing a single frame doesn't make sense */ hr = IMediaSample_GetPointer(pSample, &pbSrcStream); if (FAILED(hr)) { ERR("Cannot get pointer to sample data (%x)\n", hr); return hr; } hr = IMediaSample_GetTime(pSample, &tStart, &tStop); if (FAILED(hr)) { ERR("Cannot get sample time (%x)\n", hr); tStart = tStop = -1; } IMediaSample_IsDiscontinuity(pSample); if (IMediaSample_IsPreroll(pSample) == S_OK) { TRACE("Preroll!\n"); return S_OK; } cbSrcStream = IMediaSample_GetActualDataLength(pSample); TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream); hr = DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream); if (This->renderer.filter.state == State_Running && This->renderer.filter.pClock && tStart >= 0) { REFERENCE_TIME jitter, now = 0; Quality q; IReferenceClock_GetTime(This->renderer.filter.pClock, &now); jitter = now - This->renderer.filter.rtStreamStart - tStart; if (jitter <= -DSoundRenderer_Max_Fill) jitter += DSoundRenderer_Max_Fill; else if (jitter < 0) jitter = 0; q.Type = (jitter > 0 ? Famine : Flood); q.Proportion = 1000; q.Late = jitter; q.TimeStamp = tStart; IQualityControl_Notify((IQualityControl *)This->renderer.qcimpl, (IBaseFilter*)This, q); } return hr; }
HRESULT WINAPI QualityControlImpl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm) { HRESULT hr = S_FALSE; QualityControlImpl *This = (QualityControlImpl*)iface; if (This->tonotify) return IQualityControl_Notify(This->tonotify, This->self, qm); if (This->input) { IPin *to = NULL; IPin_ConnectedTo(This->input, &to); if (to) { IQualityControl *qc = NULL; IPin_QueryInterface(to, &IID_IQualityControl, (void**)&qc); if (qc) { hr = IQualityControl_Notify(qc, This->self, qm); IQualityControl_Release(qc); } IPin_Release(to); } } return hr; }
void QualityControlRender_DoQOS(QualityControlImpl *priv) { REFERENCE_TIME start, stop, jitter, pt, entered, left, duration; double rate; if (!priv->clock || priv->current_rstart < 0) return; start = priv->current_rstart; stop = priv->current_rstop; jitter = priv->current_jitter; if (jitter < 0) { /* this is the time the buffer entered the sink */ if (start < -jitter) entered = 0; else entered = start + jitter; left = start; } else { /* this is the time the buffer entered the sink */ entered = start + jitter; /* this is the time the buffer left the sink */ left = start + jitter; } /* calculate duration of the buffer */ if (stop >= start) duration = stop - start; else duration = 0; /* if we have the time when the last buffer left us, calculate * processing time */ if (priv->last_left >= 0) { if (entered > priv->last_left) { pt = entered - priv->last_left; } else { pt = 0; } } else { pt = priv->avg_pt; } #define XTIME(u) (int)(u/10000000), (int)((u / 10000)%1000) TRACE("start: %u.%03u, entered %u.%03u, left %u.%03u, pt: %u.%03u, " "duration %u.%03u, jitter %u.%03u\n", XTIME(start), XTIME(entered), XTIME(left), XTIME(pt), XTIME(duration), XTIME(jitter)); TRACE("avg_duration: %u.%03u, avg_pt: %u.%03u, avg_rate: %g\n", XTIME(priv->avg_duration), XTIME(priv->avg_pt), priv->avg_rate); #undef XTIME /* collect running averages. for first observations, we copy the * values */ if (priv->avg_duration < 0) priv->avg_duration = duration; else priv->avg_duration = UPDATE_RUNNING_AVG (priv->avg_duration, duration); if (priv->avg_pt < 0) priv->avg_pt = pt; else priv->avg_pt = UPDATE_RUNNING_AVG (priv->avg_pt, pt); if (priv->avg_duration != 0) rate = (double)priv->avg_pt / (double)priv->avg_duration; else rate = 0.0; if (priv->last_left >= 0) { if (priv->is_dropped || priv->avg_rate < 0.0) { priv->avg_rate = rate; } else { if (rate > 1.0) priv->avg_rate = UPDATE_RUNNING_AVG_N (priv->avg_rate, rate); else priv->avg_rate = UPDATE_RUNNING_AVG_P (priv->avg_rate, rate); } } if (priv->avg_rate >= 0.0) { HRESULT hr; Quality q; /* if we have a valid rate, start sending QoS messages */ if (priv->current_jitter < 0) { /* make sure we never go below 0 when adding the jitter to the * timestamp. */ if (priv->current_rstart < -priv->current_jitter) priv->current_jitter = -priv->current_rstart; } else priv->current_jitter += (priv->current_rstop - priv->current_rstart); q.Type = (jitter > 0 ? Famine : Flood); q.Proportion = (LONG)(1000. / priv->avg_rate); if (q.Proportion < 200) q.Proportion = 200; else if (q.Proportion > 5000) q.Proportion = 5000; q.Late = priv->current_jitter; q.TimeStamp = priv->current_rstart; TRACE("Late: %i from %i, rate: %g\n", (int)(q.Late/10000), (int)(q.TimeStamp/10000), 1./priv->avg_rate); hr = IQualityControl_Notify((IQualityControl *)priv, priv->self, q); priv->qos_handled = hr == S_OK; } /* record when this buffer will leave us */ priv->last_left = left; }