diff --git a/modules/videoio/src/cap_msmf.cpp b/modules/videoio/src/cap_msmf.cpp index 15b1d2ade7..7caa7c1ea0 100644 --- a/modules/videoio/src/cap_msmf.cpp +++ b/modules/videoio/src/cap_msmf.cpp @@ -346,8 +346,6 @@ public: STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) CV_OVERRIDE { - CV_UNUSED(llTimestamp); - HRESULT hr = 0; cv::AutoLock lock(m_mutex); @@ -360,6 +358,7 @@ public: { CV_LOG_DEBUG(NULL, "videoio(MSMF): drop frame (not processed)"); } + m_lastSampleTimestamp = llTimestamp; m_lastSample = pSample; } } @@ -439,6 +438,7 @@ public: IMFSourceReader *m_reader; DWORD m_dwStreamIndex; + LONGLONG m_lastSampleTimestamp; _ComPtr m_lastSample; }; @@ -912,6 +912,7 @@ bool CvCapture_MSMF::grabFrame() CV_LOG_WARNING(NULL, "videoio(MSMF): EOS signal. Capture stream is lost"); return false; } + sampleTime = reader->m_lastSampleTimestamp; return true; } else if (isOpen) diff --git a/modules/videoio/test/test_camera.cpp b/modules/videoio/test/test_camera.cpp index d816f637a7..e82285ad5e 100644 --- a/modules/videoio/test/test_camera.cpp +++ b/modules/videoio/test/test_camera.cpp @@ -11,21 +11,51 @@ namespace opencv_test { namespace { -static void test_readFrames(/*const*/ VideoCapture& capture, const int N = 100, Mat* lastFrame = NULL) +static void test_readFrames(/*const*/ VideoCapture& capture, const int N = 100, Mat* lastFrame = NULL, bool testTimestamps = true) { Mat frame; int64 time0 = cv::getTickCount(); + int64 sysTimePrev = time0; + const double cvTickFreq = cv::getTickFrequency(); + + double camTimePrev = 0.0; + const double fps = capture.get(cv::CAP_PROP_FPS); + const double framePeriod = fps == 0.0 ? 1. : 1.0 / fps; + + const bool validTickAndFps = cvTickFreq != 0 && fps != 0.; + testTimestamps &= validTickAndFps; + for (int i = 0; i < N; i++) { SCOPED_TRACE(cv::format("frame=%d", i)); capture >> frame; + const int64 sysTimeCurr = cv::getTickCount(); + const double camTimeCurr = capture.get(cv::CAP_PROP_POS_MSEC); ASSERT_FALSE(frame.empty()); + // Do we have a previous frame? + if (i > 0 && testTimestamps) + { + const double sysTimeElapsedSecs = (sysTimeCurr - sysTimePrev) / cvTickFreq; + const double camTimeElapsedSecs = (camTimeCurr - camTimePrev) / 1000.; + + // Check that the time between two camera frames and two system time calls + // are within 1.5 frame periods of one another. + // + // 1.5x is chosen to accomodate for a dropped frame, and an additional 50% + // to account for drift in the scale of the camera and system time domains. + EXPECT_NEAR(sysTimeElapsedSecs, camTimeElapsedSecs, framePeriod * 1.5); + } + EXPECT_GT(cvtest::norm(frame, NORM_INF), 0) << "Complete black image has been received"; + + sysTimePrev = sysTimeCurr; + camTimePrev = camTimeCurr; } + int64 time1 = cv::getTickCount(); - printf("Processed %d frames on %.2f FPS\n", N, (N * cv::getTickFrequency()) / (time1 - time0 + 1)); + printf("Processed %d frames on %.2f FPS\n", N, (N * cvTickFreq) / (time1 - time0 + 1)); if (lastFrame) *lastFrame = frame.clone(); } diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index 3f5617d8ce..19fc32b53e 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -237,7 +237,7 @@ public: if (!isBackendAvailable(apiPref, cv::videoio_registry::getStreamBackends())) throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref)); - if ((apiPref == CAP_MSMF) || ((apiPref == CAP_FFMPEG) && ((ext == "h264") || (ext == "h265")))) + if (((apiPref == CAP_FFMPEG) && ((ext == "h264") || (ext == "h265")))) throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) + cv::String(" does not support CAP_PROP_POS_MSEC option"));