//==========================================================================
-//////////////////////////////// Parallel VideoWriters and VideoCaptures ////////////////////////////////////
-
-class CreateVideoWriterInvoker :
- public ParallelLoopBody
+static void generateFrame(Mat &frame, unsigned int i, const Point ¢er, const Scalar &color)
{
-public:
- const static Size FrameSize;
- static std::string TmpDirectory;
+ frame = Scalar::all(i % 255);
+ stringstream buf(ios::out);
+ buf << "frame #" << i;
+ putText(frame, buf.str(), Point(50, center.y), FONT_HERSHEY_SIMPLEX, 5.0, color, 5, CV_AA);
+ circle(frame, center, i + 2, color, 2, CV_AA);
+}
- CreateVideoWriterInvoker(std::vector< cv::Ptr<VideoWriter> >& _writers, std::vector<std::string>& _files) :
- writers(_writers), files(_files)
+TEST(videoio_ffmpeg, parallel)
+{
+ if (!videoio_registry::hasBackend(CAP_FFMPEG))
+ throw SkipTestException("FFmpeg backend was not found");
+
+ const int NUM = 4;
+ const int GRAN = 4;
+ const Range R(0, NUM);
+ const Size sz(1020, 900);
+ const int frameNum = 300;
+ const Scalar color(Scalar::all(0));
+ const Point center(sz.height / 2, sz.width / 2);
+
+ // Generate filenames
+ vector<string> files;
+ for (int i = 0; i < NUM; ++i)
{
+ ostringstream stream;
+ stream << i << ".avi";
+ files.push_back(tempfile(stream.str().c_str()));
}
-
- virtual void operator() (const Range& range) const CV_OVERRIDE
+ // Write videos
{
- for (int i = range.start; i != range.end; ++i)
+ vector< Ptr<VideoWriter> > writers(NUM);
+ auto makeWriters = [&](const Range &r)
{
- std::ostringstream stream;
- stream << i << ".avi";
- std::string fileName = tempfile(stream.str().c_str());
-
- files[i] = fileName;
- writers[i] = makePtr<VideoWriter>(fileName, CAP_FFMPEG, VideoWriter::fourcc('X','V','I','D'), 25.0f, FrameSize);
-
- CV_Assert(writers[i]->isOpened());
+ for (int i = r.start; i != r.end; ++i)
+ writers[i] = makePtr<VideoWriter>(files[i],
+ CAP_FFMPEG,
+ VideoWriter::fourcc('X','V','I','D'),
+ 25.0f,
+ sz);
+ };
+ parallel_for_(R, makeWriters, GRAN);
+ for(int i = 0; i < NUM; ++i)
+ {
+ ASSERT_TRUE(writers[i]);
+ ASSERT_TRUE(writers[i]->isOpened());
}
- }
-
-private:
- std::vector< cv::Ptr<VideoWriter> >& writers;
- std::vector<std::string>& files;
-};
-
-std::string CreateVideoWriterInvoker::TmpDirectory;
-const Size CreateVideoWriterInvoker::FrameSize(1020, 900);
-
-class WriteVideo_Invoker :
- public ParallelLoopBody
-{
-public:
- enum { FrameCount = 300 };
-
- static const Scalar ObjectColor;
- static const Point Center;
-
- WriteVideo_Invoker(const std::vector< cv::Ptr<VideoWriter> >& _writers) :
- ParallelLoopBody(), writers(&_writers)
- {
- }
-
- static void GenerateFrame(Mat& frame, unsigned int i)
- {
- frame = Scalar::all(i % 255);
-
- std::string text = to_string(i);
- putText(frame, text, Point(50, Center.y), FONT_HERSHEY_SIMPLEX, 5.0, ObjectColor, 5, CV_AA);
- circle(frame, Center, i + 2, ObjectColor, 2, CV_AA);
- }
-
- virtual void operator() (const Range& range) const CV_OVERRIDE
- {
- for (int j = range.start; j < range.end; ++j)
+ auto writeFrames = [&](const Range &r)
{
- VideoWriter* writer = writers->operator[](j);
- CV_Assert(writer != NULL);
- CV_Assert(writer->isOpened());
-
- Mat frame(CreateVideoWriterInvoker::FrameSize, CV_8UC3);
- for (unsigned int i = 0; i < FrameCount; ++i)
+ for (int j = r.start; j < r.end; ++j)
{
- GenerateFrame(frame, i);
- writer->operator<< (frame);
+ Mat frame(sz, CV_8UC3);
+ for (int i = 0; i < frameNum; ++i)
+ {
+ generateFrame(frame, i, center, color);
+ writers[j]->write(frame);
+ }
}
- }
- }
-
-protected:
- static std::string to_string(unsigned int i)
- {
- std::stringstream stream(std::ios::out);
- stream << "frame #" << i;
- return stream.str();
- }
-
-private:
- const std::vector< cv::Ptr<VideoWriter> >* writers;
-};
-
-const Scalar WriteVideo_Invoker::ObjectColor(Scalar::all(0));
-const Point WriteVideo_Invoker::Center(CreateVideoWriterInvoker::FrameSize.height / 2,
- CreateVideoWriterInvoker::FrameSize.width / 2);
-
-class CreateVideoCaptureInvoker :
- public ParallelLoopBody
-{
-public:
- CreateVideoCaptureInvoker(std::vector< cv::Ptr<VideoCapture> >& _readers, const std::vector<std::string>& _files) :
- ParallelLoopBody(), readers(&_readers), files(&_files)
- {
+ };
+ parallel_for_(R, writeFrames, GRAN);
}
-
- virtual void operator() (const Range& range) const CV_OVERRIDE
+ // Read videos
{
- for (int i = range.start; i != range.end; ++i)
+ vector< Ptr<VideoCapture> > readers(NUM);
+ auto makeCaptures = [&](const Range &r)
{
- readers->operator[](i) = makePtr<VideoCapture>(files->operator[](i), CAP_FFMPEG);
- CV_Assert(readers->operator[](i)->isOpened());
+ for (int i = r.start; i != r.end; ++i)
+ readers[i] = makePtr<VideoCapture>(files[i], CAP_FFMPEG);
+ };
+ parallel_for_(R, makeCaptures, GRAN);
+ for(int i = 0; i < NUM; ++i)
+ {
+ ASSERT_TRUE(readers[i]);
+ ASSERT_TRUE(readers[i]->isOpened());
}
- }
-private:
- std::vector< cv::Ptr<VideoCapture> >* readers;
- const std::vector<std::string>* files;
-};
-
-class ReadImageAndTest :
- public ParallelLoopBody
-{
-public:
- ReadImageAndTest(const std::vector< cv::Ptr<VideoCapture> >& _readers, cvtest::TS* _ts) :
- ParallelLoopBody(), readers(&_readers), ts(_ts)
- {
- }
-
- virtual void operator() (const Range& range) const CV_OVERRIDE
- {
- for (int j = range.start; j < range.end; ++j)
+ auto readFrames = [&](const Range &r)
{
- VideoCapture* capture = readers->operator[](j).get();
- CV_Assert(capture != NULL);
- CV_Assert(capture->isOpened());
-
- const static double eps = 23.0;
- unsigned int frameCount = static_cast<unsigned int>(capture->get(CAP_PROP_FRAME_COUNT));
- CV_Assert(frameCount == WriteVideo_Invoker::FrameCount);
- Mat reference(CreateVideoWriterInvoker::FrameSize, CV_8UC3);
-
- for (unsigned int i = 0; i < frameCount && next; ++i)
+ for (int j = r.start; j < r.end; ++j)
{
- SCOPED_TRACE(cv::format("frame=%d/%d", (int)i, (int)frameCount));
-
- Mat actual;
- (*capture) >> actual;
-
- WriteVideo_Invoker::GenerateFrame(reference, i);
-
- EXPECT_EQ(reference.cols, actual.cols);
- EXPECT_EQ(reference.rows, actual.rows);
- EXPECT_EQ(reference.depth(), actual.depth());
- EXPECT_EQ(reference.channels(), actual.channels());
-
- double psnr = cvtest::PSNR(actual, reference);
- if (psnr < eps)
+ Mat reference(sz, CV_8UC3);
+ for (int i = 0; i < frameNum; ++i)
{
- #define SUM cvtest::TS::SUMMARY
- ts->printf(SUM, "\nPSNR: %lf\n", psnr);
- ts->printf(SUM, "Video #: %d\n", range.start);
- ts->printf(SUM, "Frame #: %d\n", i);
- #undef SUM
- ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
- ts->set_gtest_status();
-
- Mat diff;
- absdiff(actual, reference, diff);
-
- EXPECT_EQ(countNonZero(diff.reshape(1) > 1), 0);
-
- next = false;
+ Mat actual;
+ EXPECT_TRUE(readers[j]->read(actual));
+ EXPECT_FALSE(actual.empty());
+ generateFrame(reference, i, center, color);
+ EXPECT_EQ(reference.size(), actual.size());
+ EXPECT_EQ(reference.depth(), actual.depth());
+ EXPECT_EQ(reference.channels(), actual.channels());
+ EXPECT_GE(cvtest::PSNR(actual, reference), 35.0) << "cap" << j << ", frame " << i;
}
}
- }
+ };
+ parallel_for_(R, readFrames, GRAN);
}
-
- static bool next;
-
-private:
- const std::vector< cv::Ptr<VideoCapture> >* readers;
- cvtest::TS* ts;
-};
-
-bool ReadImageAndTest::next;
-
-TEST(Videoio_Video_parallel_writers_and_readers, accuracy)
-{
- const unsigned int threadsCount = 4;
- cvtest::TS* ts = cvtest::TS::ptr();
-
- // creating VideoWriters
- std::vector< cv::Ptr<VideoWriter> > writers(threadsCount);
- Range range(0, threadsCount);
- std::vector<std::string> files(threadsCount);
- CreateVideoWriterInvoker invoker1(writers, files);
- parallel_for_(range, invoker1);
-
- // write a video
- parallel_for_(range, WriteVideo_Invoker(writers));
-
- // deleting the writers
- writers.clear();
-
- std::vector<cv::Ptr<VideoCapture> > readers(threadsCount);
- CreateVideoCaptureInvoker invoker2(readers, files);
- parallel_for_(range, invoker2);
-
- ReadImageAndTest::next = true;
-
- parallel_for_(range, ReadImageAndTest(readers, ts));
-
- // deleting tmp video files
- for (std::vector<std::string>::const_iterator i = files.begin(), end = files.end(); i != end; ++i)
+ // Remove files
+ for(int i = 0; i < NUM; ++i)
{
- int code = remove(i->c_str());
- if (code == 1)
- std::cerr << "Couldn't delete " << *i << std::endl;
+ remove(files[i].c_str());
}
-
- // delete the readers
- readers.clear();
}
-#endif
+ typedef std::pair<VideoCaptureProperties, double> cap_property_t;
+ typedef std::vector<cap_property_t> cap_properties_t;
+ typedef std::pair<std::string, cap_properties_t> ffmpeg_cap_properties_param_t;
+ typedef testing::TestWithParam<ffmpeg_cap_properties_param_t> ffmpeg_cap_properties;
+
+ #ifdef _WIN32
+ namespace {
+ ::testing::AssertionResult IsOneOf(double value, double expected1, double expected2)
+ {
+ // internal floating point class is used to perform accurate floating point types comparison
+ typedef ::testing::internal::FloatingPoint<double> FloatingPoint;
+
+ FloatingPoint val(value);
+ if (val.AlmostEquals(FloatingPoint(expected1)) || val.AlmostEquals(FloatingPoint(expected2)))
+ {
+ return ::testing::AssertionSuccess();
+ }
+ else
+ {
+ return ::testing::AssertionFailure()
+ << value << " is neither equal to " << expected1 << " nor " << expected2;
+ }
+ }
+ }
+ #endif
+
+ TEST_P(ffmpeg_cap_properties, can_read_property)
+ {
++ if (!videoio_registry::hasBackend(CAP_FFMPEG))
++ throw SkipTestException("FFmpeg backend was not found");
++
+ ffmpeg_cap_properties_param_t parameters = GetParam();
+ const std::string path = parameters.first;
+ const cap_properties_t properties = parameters.second;
+
+ VideoCapture cap(findDataFile(path), CAP_FFMPEG);
+ ASSERT_TRUE(cap.isOpened()) << "Can not open " << findDataFile(path);
+
+ for (std::size_t i = 0; i < properties.size(); ++i)
+ {
+ const cap_property_t& prop = properties[i];
+ const double actualValue = cap.get(static_cast<int>(prop.first));
+ #ifndef _WIN32
+ EXPECT_DOUBLE_EQ(actualValue, prop.second)
+ << "Property " << static_cast<int>(prop.first) << " has wrong value";
+ #else
+ EXPECT_TRUE(IsOneOf(actualValue, prop.second, 0.0))
+ << "Property " << static_cast<int>(prop.first) << " has wrong value";
+ #endif
+ }
+ }
+
+ cap_properties_t loadBigBuckBunnyFFProbeResults() {
+ cap_property_t properties[] = { cap_property_t(CAP_PROP_BITRATE, 5851.),
+ cap_property_t(CAP_PROP_FPS, 24.),
+ cap_property_t(CAP_PROP_FRAME_HEIGHT, 384.),
+ cap_property_t(CAP_PROP_FRAME_WIDTH, 672.) };
+ return cap_properties_t(properties, properties + sizeof(properties) / sizeof(cap_property_t));
+ }
+
+ const ffmpeg_cap_properties_param_t videoio_ffmpeg_properties[] = {
+ ffmpeg_cap_properties_param_t("video/big_buck_bunny.avi", loadBigBuckBunnyFFProbeResults())
+ };
+
+ INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_cap_properties, testing::ValuesIn(videoio_ffmpeg_properties));
+
}} // namespace