HRESULT QualityControlRender_WaitFor(QualityControlImpl *This, IMediaSample *sample, HANDLE ev) { REFERENCE_TIME start = -1, stop = -1, jitter = 0; This->current_rstart = This->current_rstop = -1; This->current_jitter = 0; if (!This->clock || FAILED(IMediaSample_GetTime(sample, &start, &stop))) return S_OK; if (start >= 0) { REFERENCE_TIME now; IReferenceClock_GetTime(This->clock, &now); now -= This->clockstart; jitter = now - start; if (jitter <= -10000) { DWORD_PTR cookie; IReferenceClock_AdviseTime(This->clock, This->clockstart, start, (HEVENT)ev, &cookie); WaitForSingleObject(ev, INFINITE); IReferenceClock_Unadvise(This->clock, cookie); } } else start = stop = -1; This->current_rstart = start; This->current_rstop = stop > start ? stop : start; This->current_jitter = jitter; This->is_dropped = QualityControlRender_IsLate(This, jitter, start, stop); TRACE("Dropped: %i %i %i %i\n", This->is_dropped, (int)(start/10000), (int)(stop/10000), (int)(jitter / 10000)); if (This->is_dropped) { This->dropped++; if (!This->qos_handled) return S_FALSE; } else This->rendered++; return S_OK; }
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; }
void QualityControlRender_BeginRender(QualityControlImpl *This) { TRACE("%p\n", This); This->start = -1; if (!This->clock) return; IReferenceClock_GetTime(This->clock, &This->start); TRACE("at: " XTIME_FMT "\n", XTIME(This->start)); }
/* The following method expects a reference clock that will keep ticking for * at least 5 seconds since its creation. This method assumes no other methods * were called on the IReferenceClock interface since its creation. */ static void test_IReferenceClock_methods(const char * clockdesc, IReferenceClock * pClock) { HRESULT hr; REFERENCE_TIME time1; REFERENCE_TIME time2; LONG diff; /* Test response from invalid (NULL) argument */ hr = IReferenceClock_GetTime(pClock, NULL); ok (hr == E_POINTER, "%s - Expected E_POINTER (0x%08x), got 0x%08x\n", clockdesc, E_POINTER, hr); /* Test response for valid value - try 1 */ /* TODO: test whether Windows actually returns S_FALSE in its first invocation */ time1 = (REFERENCE_TIME)0xdeadbeef; hr = IReferenceClock_GetTime(pClock, &time1); ok (hr == S_FALSE || hr == S_OK, "%s - Expected S_OK or S_FALSE, got 0x%08x\n", clockdesc, hr); ok (time1 != 0xdeadbeef, "%s - value was NOT changed on return!\n", clockdesc); /* Test response for valid value - try 2 */ time2 = (REFERENCE_TIME)0xdeadbeef; hr = IReferenceClock_GetTime(pClock, &time2); ok (hr == S_FALSE || hr == S_OK, "%s - Expected S_OK or S_FALSE, got 0x%08x\n", clockdesc, hr); ok (time2 != 0xdeadbeef, "%s - value was NOT changed on return!\n", clockdesc); /* In case the second invocation managed to return S_FALSE, MSDN says the returned time is the same as the previous one. */ ok ((hr != S_FALSE || time1 == time2), "%s - returned S_FALSE, but values not equal!\n", clockdesc); time1 = time2; Sleep(1000); /* Sleep for at least 1 second */ hr = IReferenceClock_GetTime(pClock, &time2); /* After a 1-second sleep, there is no excuse to get S_FALSE (see TODO above) */ ok (hr == S_OK, "%s - Expected S_OK, got 0x%08x\n", clockdesc, hr); /* FIXME: How much deviation should be allowed after a sleep? */ /* 0.3% is common, and 0.4% is sometimes observed. */ diff = time2 - time1; ok (9940000 <= diff && diff <= 10240000, "%s - Expected difference around 10000000, got %u\n", clockdesc, diff); }
void QualityControlRender_EndRender(QualityControlImpl *This) { REFERENCE_TIME elapsed; if (!This->clock || This->start < 0 || FAILED(IReferenceClock_GetTime(This->clock, &This->stop))) return; elapsed = This->start - This->stop; if (elapsed < 0) return; if (This->avg_render < 0) This->avg_render = elapsed; else This->avg_render = UPDATE_RUNNING_AVG (This->avg_render, elapsed); }
void QualityControlRender_BeginRender(QualityControlImpl *This) { This->start = -1; if (!This->clock) return; IReferenceClock_GetTime(This->clock, &This->start); }
static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { SystemClockImpl* This = lpParam; DWORD timeOut = INFINITE; DWORD tmpTimeOut; MSG msg; HRESULT hr; REFERENCE_TIME curTime; SystemClockAdviseEntry* it = NULL; TRACE("(%p): Main Loop\n", This); while (TRUE) { if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER); EnterCriticalSection(&This->safe); /*timeOut = IReferenceClock_OnTimerUpdated(This); */ hr = IReferenceClock_GetTime(&This->IReferenceClock_iface, &curTime); if (FAILED(hr)) { timeOut = INFINITE; goto outrefresh; } /** First SingleShots Advice: sorted list */ it = This->pSingleShotAdvise; while ((NULL != it) && (it->rtBaseTime + it->rtIntervalTime) <= curTime) { SystemClockAdviseEntry* nextit = it->next; /** send event ... */ SetEvent(it->hEvent); /** ... and Release it */ QUARTZ_RemoveAviseEntryFromQueue(This, it); CoTaskMemFree(it); it = nextit; } if (NULL != it) timeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000; else timeOut = INFINITE; /** Now Periodics Advice: semi sorted list (sort cannot be used) */ for (it = This->pPeriodicAdvise; NULL != it; it = it->next) { if (it->rtBaseTime <= curTime) { DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime); /** Release the semaphore ... */ ReleaseSemaphore(it->hEvent, nPeriods, NULL); /** ... and refresh time */ it->rtBaseTime += nPeriods * it->rtIntervalTime; /*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/ } tmpTimeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000; if (timeOut > tmpTimeOut) timeOut = tmpTimeOut; } outrefresh: LeaveCriticalSection(&This->safe); while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { /** if hwnd we suppose that is a windows event ... */ if (NULL != msg.hwnd) { TranslateMessage(&msg); DispatchMessageW(&msg); } else { switch (msg.message) { case WM_QUIT: case ADVISE_EXIT: goto outofthread; case ADVISE_ADD_SINGLESHOT: case ADVISE_ADD_PERIODIC: /** set timeout to 0 to do a rescan now */ timeOut = 0; break; case ADVISE_REMOVE: /** hmmmm what we can do here ... */ timeOut = INFINITE; break; default: ERR("Unhandled message %u. Critical Path\n", msg.message); break; } } } } outofthread: TRACE("(%p): Exiting\n", This); return 0; }
static DWORD WINAPI DSoundAdviseThread(LPVOID lpParam) { DSoundRenderImpl *This = lpParam; struct dsoundrender_timer head = {NULL}; MSG msg; TRACE("(%p): Main Loop\n", This); PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); SetEvent(This->thread_wait); while (1) { HRESULT hr; REFERENCE_TIME curtime = 0; BOOL ret; struct dsoundrender_timer *prev = &head, *cur; hr = IReferenceClock_GetTime(&This->IReferenceClock_iface, &curtime); if (SUCCEEDED(hr)) { TRACE("Time: %s\n", wine_dbgstr_longlong(curtime)); while (prev->next) { cur = prev->next; if (cur->start > curtime) { TRACE("Skipping %p\n", cur); prev = cur; } else if (cur->periodicity) { while (cur->start <= curtime) { cur->start += cur->periodicity; ReleaseSemaphore(cur->handle, 1, NULL); } prev = cur; } else { struct dsoundrender_timer *next = cur->next; TRACE("Firing %p %s < %s\n", cur, wine_dbgstr_longlong(cur->start), wine_dbgstr_longlong(curtime)); SetEvent(cur->handle); HeapFree(GetProcessHeap(), 0, cur); prev->next = next; } } } if (!head.next) ret = GetMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4); else ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE); while (ret) { switch (LOWORD(msg.message) - WM_APP) { case 0: TRACE("Exiting\n"); return 0; case 1: case 2: { struct dsoundrender_timer *t = (struct dsoundrender_timer *)msg.wParam; if (LOWORD(msg.message) - WM_APP == 1) TRACE("Adding one-shot timer %p\n", t); else TRACE("Adding periodic timer %p\n", t); t->next = head.next; head.next = t; break; } case 3: prev = &head; while (prev->next) { cur = prev->next; if (cur->cookie == msg.wParam) { struct dsoundrender_timer *next = cur->next; HeapFree(GetProcessHeap(), 0, cur); prev->next = next; break; } prev = cur; } break; } ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE); } MsgWaitForMultipleObjects(0, NULL, 5, QS_POSTMESSAGE, 0); } return 0; }
static HRESULT DSoundRender_GetWritePos(DSoundRenderImpl *This, DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip) { WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat; DWORD writepos, min_writepos, playpos; REFERENCE_TIME max_lag = 50 * 10000; REFERENCE_TIME min_lag = 25 * 10000; REFERENCE_TIME cur, writepos_t, delta_t; DSoundRender_UpdatePositions(This, &writepos, &min_writepos); playpos = This->last_playpos; if (This->renderer.filter.pClock == &This->IReferenceClock_iface) { max_lag = min_lag; cur = This->play_time + time_from_pos(This, playpos); cur -= This->renderer.filter.rtStreamStart; } else if (This->renderer.filter.pClock) { IReferenceClock_GetTime(This->renderer.filter.pClock, &cur); cur -= This->renderer.filter.rtStreamStart; } else write_at = -1; if (writepos == min_writepos) max_lag = 0; *skip = 0; if (write_at < 0) { *ret_writepos = writepos; goto end; } if (writepos >= playpos) writepos_t = cur + time_from_pos(This, writepos - playpos); else writepos_t = cur + time_from_pos(This, This->buf_size + writepos - playpos); /* write_at: Starting time of sample */ /* cur: current time of play position */ /* writepos_t: current time of our pointer play position */ delta_t = write_at - writepos_t; if (delta_t >= -max_lag && delta_t <= max_lag) { TRACE("Continuing from old position\n"); *ret_writepos = writepos; } else if (delta_t < 0) { REFERENCE_TIME past, min_writepos_t; WARN("Delta too big %i/%i, overwriting old data or even skipping\n", (int)delta_t / 10000, (int)max_lag / 10000); if (min_writepos >= playpos) min_writepos_t = cur + time_from_pos(This, min_writepos - playpos); else min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos); past = min_writepos_t - write_at; if (past >= 0) { DWORD skipbytes = pos_from_time(This, past); WARN("Skipping %u bytes\n", skipbytes); *skip = skipbytes; *ret_writepos = min_writepos; } else { DWORD aheadbytes = pos_from_time(This, -past); WARN("Advancing %u bytes\n", aheadbytes); *ret_writepos = (min_writepos + aheadbytes) % This->buf_size; } } else /* delta_t > 0 */ { DWORD aheadbytes; WARN("Delta too big %i/%i, too far ahead\n", (int)delta_t / 10000, (int)max_lag / 10000); aheadbytes = pos_from_time(This, delta_t); WARN("Advancing %u bytes\n", aheadbytes); if (delta_t >= DSoundRenderer_Max_Fill) return S_FALSE; *ret_writepos = (min_writepos + aheadbytes) % This->buf_size; } end: if (playpos > *ret_writepos) *pfree = playpos - *ret_writepos; else if (playpos == *ret_writepos) *pfree = This->buf_size - wfx->nBlockAlign; else *pfree = This->buf_size + playpos - *ret_writepos; if (time_from_pos(This, This->buf_size - *pfree) >= DSoundRenderer_Max_Fill) { TRACE("Blocked: too full %i / %i\n", (int)(time_from_pos(This, This->buf_size - *pfree)/10000), (int)(DSoundRenderer_Max_Fill / 10000)); return S_FALSE; } return S_OK; }
static HRESULT FFMVWrapper_ProcessReceive( CTransformBaseImpl* pImpl, IMediaSample* pSampIn ) { CFFMVWrapperImpl* This = pImpl->m_pUserData; BYTE* pDataIn = NULL; LONG lDataInLen; IMediaSample* pSampOut = NULL; BYTE* pOutBuf; HRESULT hr; AVFrame tmp_pic; AVPicture dst_pic; int nOut, got_pic; LONG width, height; REFERENCE_TIME rtStart, rtStop, rtNow; BOOL skip; TRACE("(%p)\n",This); if ( This == NULL || !This->ctx.codec || This->m_pbiIn == NULL || This->m_pbiOut == NULL ) return E_UNEXPECTED; hr = IMediaSample_GetPointer( pSampIn, &pDataIn ); if ( FAILED(hr) ) return hr; lDataInLen = IMediaSample_GetActualDataLength( pSampIn ); if ( lDataInLen < 0 ) return E_FAIL; EnterCriticalSection( &This->m_cs ); if ( !This->ctx.codec ) { hr = E_UNEXPECTED; goto failed; } if ( IMediaSample_IsDiscontinuity( pSampIn ) == S_OK ) avcodec_flush_buffers( &This->ctx ); width = This->m_pbiIn->bmiHeader.biWidth; height = (This->m_pbiIn->bmiHeader.biHeight < 0) ? -This->m_pbiIn->bmiHeader.biHeight : This->m_pbiIn->bmiHeader.biHeight; while ( TRUE ) { nOut = avcodec_decode_video( &This->ctx, &tmp_pic, &got_pic, (void*)pDataIn, lDataInLen ); if ( nOut < 0 ) { TRACE("decoding error\n"); goto fail; } TRACE("used %d of %d bytes\n", nOut, lDataInLen); if ( nOut > lDataInLen ) { WARN("arrgh - FFmpeg read too much\n"); nOut = lDataInLen; } pDataIn += nOut; lDataInLen -= nOut; if (!got_pic) { TRACE("no frame decoded\n"); if (lDataInLen) continue; LeaveCriticalSection( &This->m_cs ); return S_OK; } TRACE("frame decoded\n"); This->rtInternal ++; hr = IMediaSample_GetTime( pSampIn, &rtStart, &rtStop ); if ( hr == S_OK ) { /* if the parser gives us a timestamp, the data * we got from it should be a single frame */ if ( lDataInLen ) { ERR("excessive data in compressed frame\n"); lDataInLen = 0; } } else { /* compute our own timestamp */ rtStart = This->rtCur; This->rtCur = This->rtInternal * (REFERENCE_TIME)QUARTZ_TIMEUNITS * This->ctx.frame_rate_base / This->ctx.frame_rate; rtStop = This->rtCur; } TRACE("frame start=%lld, stop=%lld\n", rtStart, rtStop); skip = FALSE; hr = IReferenceClock_GetTime(pImpl->basefilter.pClock, &rtNow); if (SUCCEEDED(hr)) { rtNow -= pImpl->basefilter.rtStart; TRACE("time=%lld\n", rtNow); if (rtStart < rtNow + SKIP_TIME) { skip = TRUE; if ( ++This->skipFrames >= MAX_SKIP ) { This->skipFrames = 0; TRACE("frame late, but max skip exceeded\n"); skip = FALSE; } } } if (skip) { TRACE("skipping late frame\n"); if ( lDataInLen == 0 ) { LeaveCriticalSection( &This->m_cs ); return S_OK; } } else { /* process frame */ hr = IMemAllocator_GetBuffer( pImpl->m_pOutPinAllocator, &pSampOut, &rtStart, &rtStop, 0 ); if ( FAILED(hr) ) goto failed; hr = IMediaSample_GetPointer( pSampOut, &pOutBuf ); if ( FAILED(hr) ) goto failed; dst_pic.data[0] = ( This->m_pOutBuf != NULL ) ? This->m_pOutBuf : pOutBuf; dst_pic.linesize[0] = DIBWIDTHBYTES(This->m_pbiOut->bmiHeader); /* convert to RGB (or BGR) */ switch (This->m_pbiOut->bmiHeader.biBitCount) { case 24: img_convert( &dst_pic, PIX_FMT_BGR24, (AVPicture*)&tmp_pic, This->ctx.pix_fmt, width, height ); break; case 32: /* RGBA32 is misnamed (is actually cpu-endian ARGB, which means BGRA on x86), * might get renamed in future ffmpeg snapshots */ img_convert( &dst_pic, PIX_FMT_RGBA32, (AVPicture*)&tmp_pic, This->ctx.pix_fmt, width, height ); break; default: TRACE("bad bpp\n"); goto fail; } if ( This->m_pOutBuf != NULL ) memcpy( pOutBuf, This->m_pOutBuf, This->m_pbiOut->bmiHeader.biSizeImage ); IMediaSample_SetActualDataLength( pSampOut, This->m_pbiOut->bmiHeader.biSizeImage ); /* FIXME: discontinuity and sync point */ LeaveCriticalSection( &This->m_cs ); hr = CPinBaseImpl_SendSample( &pImpl->pOutPin->pin, pSampOut ); if ( FAILED(hr) ) return hr; IMediaSample_Release( pSampOut ); pSampOut = NULL; if ( lDataInLen == 0 ) return S_OK; EnterCriticalSection( &This->m_cs ); if ( !This->ctx.codec ) { hr = E_UNEXPECTED; goto failed; } } } fail: hr = E_FAIL; failed: LeaveCriticalSection( &This->m_cs ); return hr; }