HRESULT CPushPinDesktop::FillBuffer(IMediaSample *pSample) { LocalOutput("video frame requested"); __int64 startThisRound = StartCounter(); BYTE *pData; CheckPointer(pSample, E_POINTER); if(m_bReReadRegistry) { reReadCurrentStartXY(1); } if(!ever_started) { // allow it to startup until Run is called...so StreamTime can work see http://stackoverflow.com/questions/2469855/how-to-get-imediacontrol-run-to-start-a-file-playing-with-no-delay/2470548#2470548 // since StreamTime anticipates that the graph's start time has already been set FILTER_STATE myState; CSourceStream::m_pFilter->GetState(INFINITE, &myState); while(myState != State_Running) { // TODO accomodate for pausing better, we're single run only currently [does VLC do pausing even?] Sleep(1); LocalOutput("sleeping till graph running for audio..."); m_pParent->GetState(INFINITE, &myState); } ever_started = true; } // Access the sample's data buffer pSample->GetPointer(&pData); // Make sure that we're still using video format ASSERT_RETURN(m_mt.formattype == FORMAT_VideoInfo); VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*) m_mt.pbFormat; boolean gotNew = false; // dedupe stuff while(!gotNew) { CopyScreenToDataBlock(hScrDc, pData, (BITMAPINFO *) &(pVih->bmiHeader), pSample); if(m_bDeDupe) { if(memcmp(pData, pOldData, pSample->GetSize())==0) { // took desktop: 10ms for 640x1152, still 100 fps uh guess... Sleep(m_millisToSleepBeforePollForChanges); } else { gotNew = true; memcpy( /* dest */ pOldData, pData, pSample->GetSize()); // took 4ms for 640x1152, but it's worth it LOL. // LODO memcmp and memcpy in the same loop LOL. } } else { // it's always new for everyone else (the typical case) gotNew = true; } } // capture some debug stats (how long it took) before we add in our own arbitrary delay to enforce fps... long double millisThisRoundTook = GetCounterSinceStartMillis(startThisRound); fastestRoundMillis = min(millisThisRoundTook, fastestRoundMillis); // keep stats :) sumMillisTook += millisThisRoundTook; CRefTime now; CRefTime endFrame; now = 0; CSourceStream::m_pFilter->StreamTime(now); if((now > 0) && (now < previousFrameEndTime)) { // now > 0 to accomodate for if there is no reference graph clock at all...also at boot strap time to ignore it XXXX can negatives even ever happen anymore though? while(now < previousFrameEndTime) { // guarantees monotonicity too :P LocalOutput("sleeping because %llu < %llu", now, previousFrameEndTime); Sleep(1); CSourceStream::m_pFilter->StreamTime(now); } // avoid a tidge of creep since we sleep until [typically] just past the previous end. endFrame = previousFrameEndTime + m_rtFrameLength; previousFrameEndTime = endFrame; } else { // if there's no reference clock, it will "always" think it missed a frame if(show_performance) { if(now == 0) LocalOutput("probable none reference clock, streaming fastly"); else LocalOutput("it missed a frame--can't keep up %d %llu %llu", countMissed++, now, previousFrameEndTime); // we don't miss time typically I don't think, unless de-dupe is turned on, or aero, or slow computer, buffering problems downstream, etc. } // have to add a bit here, or it will always be "it missed a frame" for the next round...forever! endFrame = now + m_rtFrameLength; // most of this stuff I just made up because it "sounded right" //LocalOutput("checking to see if I can catch up again now: %llu previous end: %llu subtr: %llu %i", now, previousFrameEndTime, previousFrameEndTime - m_rtFrameLength, previousFrameEndTime - m_rtFrameLength); if(now > (previousFrameEndTime - (long long) m_rtFrameLength)) { // do I even need a long long cast? // let it pretend and try to catch up, it's not quite a frame behind previousFrameEndTime = previousFrameEndTime + m_rtFrameLength; } else { endFrame = now + m_rtFrameLength/2; // ?? seems to not hurt, at least...I guess previousFrameEndTime = endFrame; } } // accomodate for 0 to avoid startup negatives, which would kill our math on the next loop... previousFrameEndTime = max(0, previousFrameEndTime); pSample->SetTime((REFERENCE_TIME *) &now, (REFERENCE_TIME *) &endFrame); //pSample->SetMediaTime((REFERENCE_TIME *)&now, (REFERENCE_TIME *) &endFrame); LocalOutput("timestamping video packet as %lld -> %lld", now, endFrame); m_iFrameNumber++; // Set TRUE on every sample for uncompressed frames http://msdn.microsoft.com/en-us/library/windows/desktop/dd407021%28v=vs.85%29.aspx pSample->SetSyncPoint(TRUE); // only set discontinuous for the first...I think... pSample->SetDiscontinuity(m_iFrameNumber <= 1); #ifdef _DEBUG // the swprintf costs like 0.04ms (25000 fps LOL) double m_fFpsSinceBeginningOfTime = ((double) m_iFrameNumber)/(GetTickCount() - globalStart)*1000; swprintf(out, L"done video frame! total frames: %d this one %dx%d -> (%dx%d) took: %.02Lfms, %.02f ave fps (%.02f is the theoretical max fps based on this round, ave. possible fps %.02f, fastest round fps %.02f, negotiated fps %.06f), frame missed %d", m_iFrameNumber, m_iCaptureConfigHeight, m_iCaptureConfigWidth, getNegotiatedFinalWidth(), getNegotiatedFinalHeight(), millisThisRoundTook, m_fFpsSinceBeginningOfTime, 1.0*1000/millisThisRoundTook, /* average */ 1.0*1000*m_iFrameNumber/sumMillisTook, 1.0*1000/fastestRoundMillis, GetFps(), countMissed); LocalOutput(out); set_config_string_setting(L"frame_stats", out); #endif return S_OK; }
HRESULT CPushPinDesktop::FillBuffer(IMediaSample *pSample) { __int64 startThisRound = StartCounter(); BYTE *pData; CheckPointer(pSample, E_POINTER); if(m_bReReadRegistry) { reReadCurrentPosition(1); } // Access the sample's data buffer pSample->GetPointer(&pData); // Make sure that we're still using video format ASSERT(m_mt.formattype == FORMAT_VideoInfo); VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*) m_mt.pbFormat; // for some reason the timings are messed up initially, as there's no start time at all for the first frame (?) we don't start in State_Running ? // race condition? // so don't do some calculations unless we're in State_Running FILTER_STATE myState; CSourceStream::m_pFilter->GetState(INFINITE, &myState); bool fullyStarted = myState == State_Running; boolean gotNew = false; while(!gotNew) { CopyScreenToDataBlock(hScrDc, pData, (BITMAPINFO *) &(pVih->bmiHeader), pSample); if(m_bDeDupe) { if(memcmp(pData, pOldData, pSample->GetSize())==0) { // took desktop: 10ms for 640x1152, still 100 fps uh guess... Sleep(m_millisToSleepBeforePollForChanges); } else { gotNew = true; memcpy( /* dest */ pOldData, pData, pSample->GetSize()); // took 4ms for 640x1152, but it's worth it LOL. // LODO memcmp and memcpy in the same loop LOL. } } else { // it's always new for everyone else! gotNew = true; } } // capture how long it took before we add in our own arbitrary delay to enforce fps... long double millisThisRoundTook = GetCounterSinceStartMillis(startThisRound); fastestRoundMillis = min(millisThisRoundTook, fastestRoundMillis); // keep stats :) sumMillisTook += millisThisRoundTook; CRefTime now; CRefTime endFrame; CSourceStream::m_pFilter->StreamTime(now); // wait until we "should" send this frame out... if((now > 0) && (now < previousFrameEndTime)) { // now > 0 to accomodate for if there is no reference graph clock at all...also boot strap time ignore it :P while(now < previousFrameEndTime) { // guarantees monotonicity too :P Sleep(1); CSourceStream::m_pFilter->StreamTime(now); } // avoid a tidge of creep since we sleep until [typically] just past the previous end. endFrame = previousFrameEndTime + m_rtFrameLength; previousFrameEndTime = endFrame; } else { if(show_performance) LocalOutput("it missed a frame--can't keep up %d", countMissed++); // we don't miss time typically I don't think, unless de-dupe is turned on, or aero, or slow computer, buffering problems downstream, etc. // have to add a bit here, or it will always be "it missed some time" for the next round...forever! endFrame = now + m_rtFrameLength; // most of this stuff I just made up because it "sounded right" //LocalOutput("checking to see if I can catch up again now: %llu previous end: %llu subtr: %llu %i", now, previousFrameEndTime, previousFrameEndTime - m_rtFrameLength, previousFrameEndTime - m_rtFrameLength); if(now > (previousFrameEndTime - (long long) m_rtFrameLength)) { // do I need a long long cast? // let it pretend and try to catch up, it's not quite a frame behind previousFrameEndTime = previousFrameEndTime + m_rtFrameLength; } else { endFrame = now + m_rtFrameLength/2; // ?? seems to work...I guess... previousFrameEndTime = endFrame; } } previousFrameEndTime = max(0, previousFrameEndTime);// avoid startup negatives, which would kill our math on the next loop... // LocalOutput("marking frame with timestamps: %llu %llu", now, endFrame); pSample->SetTime((REFERENCE_TIME *) &now, (REFERENCE_TIME *) &endFrame); //pSample->SetMediaTime((REFERENCE_TIME *)&now, (REFERENCE_TIME *) &endFrame); //useless seemingly if(fullyStarted) { m_iFrameNumber++; } // Set TRUE on every sample for uncompressed frames http://msdn.microsoft.com/en-us/library/windows/desktop/dd407021%28v=vs.85%29.aspx pSample->SetSyncPoint(TRUE); // only set discontinuous for the first...I think... pSample->SetDiscontinuity(m_iFrameNumber <= 1); // the swprintf costs like 0.04ms (25000 fps LOL) m_fFpsSinceBeginningOfTime = ((double) m_iFrameNumber)/(GetTickCount() - globalStart)*1000; swprintf(out, L"done frame! total frames: %d this one %dx%d -> (%dx%d) took: %.02Lfms, %.02f ave fps (%.02f is the theoretical max fps based on this round, ave. possible fps %.02f, fastest round fps %.02f, negotiated fps %.06f), frame missed %d", m_iFrameNumber, m_iCaptureConfigHeight, m_iCaptureConfigWidth, getNegotiatedFinalWidth(), getNegotiatedFinalHeight(), millisThisRoundTook, m_fFpsSinceBeginningOfTime, 1.0*1000/millisThisRoundTook, /* average */ 1.0*1000*m_iFrameNumber/sumMillisTook, 1.0*1000/fastestRoundMillis, GetFps(), countMissed); //#ifdef _DEBUG // probably not worth it but we do hit this a lot...hmm... LocalOutput(out); set_config_string_setting(L"frame_stats", out); //#endif return S_OK; }