test_video_io.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010
  1. /*M///////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
  4. //
  5. // By downloading, copying, installing or using the software you agree to this license.
  6. // If you do not agree to this license, do not download, install,
  7. // copy or use the software.
  8. //
  9. //
  10. // License Agreement
  11. // For Open Source Computer Vision Library
  12. //
  13. // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
  14. // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
  15. // Third party copyrights are property of their respective owners.
  16. //
  17. // Redistribution and use in source and binary forms, with or without modification,
  18. // are permitted provided that the following conditions are met:
  19. //
  20. // * Redistribution's of source code must retain the above copyright notice,
  21. // this list of conditions and the following disclaimer.
  22. //
  23. // * Redistribution's in binary form must reproduce the above copyright notice,
  24. // this list of conditions and the following disclaimer in the documentation
  25. // and/or other materials provided with the distribution.
  26. //
  27. // * The name of the copyright holders may not be used to endorse or promote products
  28. // derived from this software without specific prior written permission.
  29. //
  30. // This software is provided by the copyright holders and contributors "as is" and
  31. // any express or implied warranties, including, but not limited to, the implied
  32. // warranties of merchantability and fitness for a particular purpose are disclaimed.
  33. // In no event shall the Intel Corporation or contributors be liable for any direct,
  34. // indirect, incidental, special, exemplary, or consequential damages
  35. // (including, but not limited to, procurement of substitute goods or services;
  36. // loss of use, data, or profits; or business interruption) however caused
  37. // and on any theory of liability, whether in contract, strict liability,
  38. // or tort (including negligence or otherwise) arising in any way out of
  39. // the use of this software, even if advised of the possibility of such damage.
  40. //
  41. //M*/
  42. #include "test_precomp.hpp"
  43. #include "opencv2/videoio/videoio_c.h"
  44. namespace opencv_test
  45. {
  46. class Videoio_Test_Base
  47. {
  48. protected:
  49. string ext;
  50. string video_file;
  51. VideoCaptureAPIs apiPref;
  52. protected:
  53. Videoio_Test_Base() {}
  54. virtual ~Videoio_Test_Base() {}
  55. virtual void writeVideo() {}
  56. virtual void checkFrameContent(Mat &, int) {}
  57. virtual void checkFrameCount(int &) {}
  58. void checkFrameRead(int idx, VideoCapture & cap)
  59. {
  60. //int frameID = (int)cap.get(CAP_PROP_POS_FRAMES);
  61. Mat img;
  62. ASSERT_NO_THROW(cap >> img);
  63. //std::cout << "idx=" << idx << " img=" << img.size() << " frameID=" << frameID << std::endl;
  64. ASSERT_FALSE(img.empty()) << "idx=" << idx;
  65. checkFrameContent(img, idx);
  66. }
  67. void checkFrameSeek(int idx, VideoCapture & cap)
  68. {
  69. bool canSeek = false;
  70. ASSERT_NO_THROW(canSeek = cap.set(CAP_PROP_POS_FRAMES, idx));
  71. if (!canSeek)
  72. {
  73. std::cout << "Seek to frame '" << idx << "' is not supported. SKIP." << std::endl;
  74. return;
  75. }
  76. EXPECT_EQ(idx, (int)cap.get(CAP_PROP_POS_FRAMES));
  77. checkFrameRead(idx, cap);
  78. }
  79. public:
  80. void doTest()
  81. {
  82. if (!videoio_registry::hasBackend(apiPref))
  83. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  84. if (cvtest::skipUnstableTests && apiPref == CAP_MSMF && (ext == "h264" || ext == "h265" || ext == "mpg"))
  85. throw SkipTestException("Unstable MSMF test");
  86. writeVideo();
  87. VideoCapture cap;
  88. ASSERT_NO_THROW(cap.open(video_file, apiPref));
  89. if (!cap.isOpened())
  90. {
  91. std::cout << "SKIP test: backend " << apiPref << " can't open the video: " << video_file << std::endl;
  92. return;
  93. }
  94. int n_frames = -1;
  95. EXPECT_NO_THROW(n_frames = (int)cap.get(CAP_PROP_FRAME_COUNT));
  96. if (n_frames > 0)
  97. {
  98. ASSERT_GT(n_frames, 0);
  99. checkFrameCount(n_frames);
  100. }
  101. else
  102. {
  103. std::cout << "CAP_PROP_FRAME_COUNT is not supported by backend. Assume 50 frames." << std::endl;
  104. n_frames = 50;
  105. }
  106. {
  107. SCOPED_TRACE("consecutive read");
  108. if (apiPref == CAP_GSTREAMER)
  109. {
  110. // This workaround is for GStreamer 1.3.1.1 and older.
  111. // Old Gstreamer has a bug which handles the total duration 1 frame shorter
  112. // Old Gstreamer are used in Ubuntu 14.04, so the following code could be removed after it's EOL
  113. n_frames--;
  114. }
  115. for (int k = 0; k < n_frames; ++k)
  116. {
  117. checkFrameRead(k, cap);
  118. if (::testing::Test::HasFailure() && k % 10 == 0)
  119. break;
  120. }
  121. }
  122. bool canSeek = false;
  123. EXPECT_NO_THROW(canSeek = cap.set(CAP_PROP_POS_FRAMES, 0));
  124. if (!canSeek)
  125. {
  126. std::cout << "Seek to frame '0' is not supported. SKIP all 'seek' tests." << std::endl;
  127. return;
  128. }
  129. if (ext != "wmv" && ext != "h264" && ext != "h265")
  130. {
  131. SCOPED_TRACE("progressive seek");
  132. bool res = false;
  133. EXPECT_NO_THROW(res = cap.set(CAP_PROP_POS_FRAMES, 0));
  134. ASSERT_TRUE(res);
  135. for (int k = 0; k < n_frames; k += 20)
  136. {
  137. checkFrameSeek(k, cap);
  138. if (::testing::Test::HasFailure() && k % 10 == 0)
  139. break;
  140. }
  141. }
  142. if (ext != "mpg" && ext != "wmv" && ext != "h264" && ext != "h265")
  143. {
  144. SCOPED_TRACE("random seek");
  145. bool res = false;
  146. EXPECT_NO_THROW(res = cap.set(CAP_PROP_POS_FRAMES, 0));
  147. ASSERT_TRUE(res);
  148. for (int k = 0; k < 10; ++k)
  149. {
  150. checkFrameSeek(cvtest::TS::ptr()->get_rng().uniform(0, n_frames), cap);
  151. if (::testing::Test::HasFailure() && k % 10 == 0)
  152. break;
  153. }
  154. }
  155. }
  156. };
  157. //==================================================================================================
  158. typedef tuple<string, VideoCaptureAPIs> Backend_Type_Params;
  159. class videoio_bunny : public Videoio_Test_Base, public testing::TestWithParam<Backend_Type_Params>
  160. {
  161. BunnyParameters bunny_param;
  162. public:
  163. videoio_bunny()
  164. {
  165. ext = get<0>(GetParam());
  166. apiPref = get<1>(GetParam());
  167. video_file = BunnyParameters::getFilename(String(".") + ext);
  168. }
  169. void doFrameCountTest()
  170. {
  171. if (!videoio_registry::hasBackend(apiPref))
  172. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  173. if (cvtest::skipUnstableTests && apiPref == CAP_MSMF && (ext == "h264" || ext == "h265" || ext == "mpg"))
  174. throw SkipTestException("Unstable MSMF test");
  175. VideoCapture cap;
  176. EXPECT_NO_THROW(cap.open(video_file, apiPref));
  177. if (!cap.isOpened())
  178. {
  179. std::cout << "SKIP test: backend " << apiPref << " can't open the video: " << video_file << std::endl;
  180. return;
  181. }
  182. Size actual;
  183. EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH),
  184. (int)cap.get(CAP_PROP_FRAME_HEIGHT)));
  185. EXPECT_EQ(bunny_param.getWidth(), actual.width);
  186. EXPECT_EQ(bunny_param.getHeight(), actual.height);
  187. double fps_prop = 0;
  188. EXPECT_NO_THROW(fps_prop = cap.get(CAP_PROP_FPS));
  189. if (fps_prop > 0)
  190. EXPECT_NEAR(fps_prop, bunny_param.getFps(), 1);
  191. else
  192. std::cout << "FPS is not available. SKIP check." << std::endl;
  193. int count_prop = 0;
  194. EXPECT_NO_THROW(count_prop = (int)cap.get(CAP_PROP_FRAME_COUNT));
  195. // mpg file reports 5.08 sec * 24 fps => property returns 122 frames
  196. // but actual number of frames returned is 125
  197. if (ext != "mpg")
  198. {
  199. if (count_prop > 0)
  200. {
  201. EXPECT_EQ(bunny_param.getCount(), count_prop);
  202. }
  203. }
  204. int count_actual = 0;
  205. while (cap.isOpened())
  206. {
  207. Mat frame;
  208. EXPECT_NO_THROW(cap >> frame);
  209. if (frame.empty())
  210. break;
  211. EXPECT_EQ(bunny_param.getWidth(), frame.cols);
  212. EXPECT_EQ(bunny_param.getHeight(), frame.rows);
  213. count_actual += 1;
  214. if (::testing::Test::HasFailure() && count_actual % 10 == 0)
  215. break;
  216. }
  217. if (count_prop > 0)
  218. {
  219. EXPECT_NEAR(bunny_param.getCount(), count_actual, 1);
  220. }
  221. else
  222. std::cout << "Frames counter is not available. Actual frames: " << count_actual << ". SKIP check." << std::endl;
  223. }
  224. void doTimestampTest()
  225. {
  226. if (!isBackendAvailable(apiPref, cv::videoio_registry::getStreamBackends()))
  227. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  228. // GStreamer: https://github.com/opencv/opencv/issues/19025
  229. if (apiPref == CAP_GSTREAMER)
  230. throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) +
  231. cv::String(" does not return reliable values for CAP_PROP_POS_MSEC property"));
  232. if (((apiPref == CAP_FFMPEG) && ((ext == "h264") || (ext == "h265"))))
  233. throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) +
  234. cv::String(" does not support CAP_PROP_POS_MSEC option"));
  235. VideoCapture cap;
  236. EXPECT_NO_THROW(cap.open(video_file, apiPref));
  237. if (!cap.isOpened())
  238. throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) +
  239. cv::String(" can't open the video: ") + video_file);
  240. Mat img;
  241. for(int i = 0; i < 10; i++)
  242. {
  243. double timestamp = 0;
  244. ASSERT_NO_THROW(cap >> img);
  245. EXPECT_NO_THROW(timestamp = cap.get(CAP_PROP_POS_MSEC));
  246. if (cvtest::debugLevel > 0)
  247. std::cout << "i = " << i << ": timestamp = " << timestamp << std::endl;
  248. const double frame_period = 1000.f/bunny_param.getFps();
  249. // NOTE: eps == frame_period, because videoCapture returns frame begining timestamp or frame end
  250. // timestamp depending on codec and back-end. So the first frame has timestamp 0 or frame_period.
  251. EXPECT_NEAR(timestamp, i*frame_period, frame_period) << "i=" << i;
  252. }
  253. }
  254. };
  255. //==================================================================================================
  256. struct Ext_Fourcc_PSNR
  257. {
  258. const char* ext;
  259. const char* fourcc;
  260. float PSNR;
  261. VideoCaptureAPIs api;
  262. };
  263. typedef tuple<Size, Ext_Fourcc_PSNR> Size_Ext_Fourcc_PSNR;
  264. class videoio_synthetic : public Videoio_Test_Base, public testing::TestWithParam<Size_Ext_Fourcc_PSNR>
  265. {
  266. Size frame_size;
  267. int fourcc;
  268. float PSNR_GT;
  269. int frame_count;
  270. double fps;
  271. public:
  272. videoio_synthetic()
  273. {
  274. frame_size = get<0>(GetParam());
  275. const Ext_Fourcc_PSNR p = get<1>(GetParam());
  276. ext = p.ext;
  277. fourcc = fourccFromString(p.fourcc);
  278. PSNR_GT = p.PSNR;
  279. video_file = cv::tempfile((fourccToString(fourcc) + "." + ext).c_str());
  280. frame_count = 100;
  281. fps = 25.;
  282. apiPref = p.api;
  283. }
  284. void TearDown()
  285. {
  286. remove(video_file.c_str());
  287. }
  288. virtual void writeVideo()
  289. {
  290. Mat img(frame_size, CV_8UC3);
  291. VideoWriter writer;
  292. EXPECT_NO_THROW(writer.open(video_file, apiPref, fourcc, fps, frame_size, true));
  293. ASSERT_TRUE(writer.isOpened());
  294. for(int i = 0; i < frame_count; ++i )
  295. {
  296. generateFrame(i, frame_count, img);
  297. EXPECT_NO_THROW(writer << img);
  298. if (::testing::Test::HasFailure() && i % 10 == 0)
  299. break;
  300. }
  301. EXPECT_NO_THROW(writer.release());
  302. }
  303. virtual void checkFrameContent(Mat & img, int idx)
  304. {
  305. Mat imgGT(frame_size, CV_8UC3);
  306. generateFrame(idx, frame_count, imgGT);
  307. double psnr = cvtest::PSNR(img, imgGT);
  308. ASSERT_GT(psnr, PSNR_GT) << "frame " << idx;
  309. }
  310. virtual void checkFrameCount(int &actual)
  311. {
  312. Range expected_frame_count = Range(frame_count, frame_count);
  313. // Hack! Newer FFmpeg versions in this combination produce a file
  314. // whose reported duration is one frame longer than needed, and so
  315. // the calculated frame count is also off by one. Ideally, we'd want
  316. // to fix both writing (to produce the correct duration) and reading
  317. // (to correctly report frame count for such files), but I don't know
  318. // how to do either, so this is a workaround for now.
  319. if (fourcc == VideoWriter::fourcc('M', 'P', 'E', 'G') && ext == "mkv")
  320. expected_frame_count.end += 1;
  321. // Workaround for some gstreamer pipelines
  322. if (apiPref == CAP_GSTREAMER)
  323. expected_frame_count.start -= 1;
  324. ASSERT_LE(expected_frame_count.start, actual);
  325. ASSERT_GE(expected_frame_count.end, actual);
  326. actual = expected_frame_count.start; // adjust actual frame boundary to possible minimum
  327. }
  328. };
  329. //==================================================================================================
  330. static const VideoCaptureAPIs backend_params[] = {
  331. #ifdef HAVE_AVFOUNDATION
  332. CAP_AVFOUNDATION,
  333. #endif
  334. #ifdef _WIN32
  335. CAP_MSMF,
  336. #endif
  337. CAP_GSTREAMER,
  338. CAP_FFMPEG,
  339. #ifdef HAVE_XINE
  340. CAP_XINE,
  341. #endif
  342. CAP_OPENCV_MJPEG
  343. // CAP_INTEL_MFX
  344. };
  345. static const string bunny_params[] = {
  346. string("wmv"),
  347. string("mov"),
  348. string("mp4"),
  349. string("mpg"),
  350. string("avi"),
  351. string("h264"),
  352. string("h265"),
  353. string("mjpg.avi")
  354. };
  355. TEST_P(videoio_bunny, read_position) { doTest(); }
  356. TEST_P(videoio_bunny, frame_count) { doFrameCountTest(); }
  357. TEST_P(videoio_bunny, frame_timestamp) { doTimestampTest(); }
  358. INSTANTIATE_TEST_CASE_P(videoio, videoio_bunny,
  359. testing::Combine(
  360. testing::ValuesIn(bunny_params),
  361. testing::ValuesIn(backend_params)));
  362. inline static std::ostream &operator<<(std::ostream &out, const Ext_Fourcc_PSNR &p)
  363. {
  364. out << "FOURCC(" << p.fourcc << "), ." << p.ext << ", " << p.api << ", " << p.PSNR << "dB"; return out;
  365. }
  366. static Ext_Fourcc_PSNR synthetic_params[] = {
  367. #ifdef _WIN32
  368. #if !defined(_M_ARM)
  369. {"wmv", "WMV1", 30.f, CAP_MSMF},
  370. {"wmv", "WMV2", 30.f, CAP_MSMF},
  371. #endif
  372. {"wmv", "WMV3", 30.f, CAP_MSMF},
  373. {"wmv", "WVC1", 30.f, CAP_MSMF},
  374. {"mov", "H264", 30.f, CAP_MSMF},
  375. #endif
  376. #ifdef HAVE_AVFOUNDATION
  377. {"mov", "H264", 30.f, CAP_AVFOUNDATION},
  378. {"mov", "MJPG", 30.f, CAP_AVFOUNDATION},
  379. {"mp4", "H264", 30.f, CAP_AVFOUNDATION},
  380. {"mp4", "MJPG", 30.f, CAP_AVFOUNDATION},
  381. {"m4v", "H264", 30.f, CAP_AVFOUNDATION},
  382. {"m4v", "MJPG", 30.f, CAP_AVFOUNDATION},
  383. #endif
  384. {"avi", "XVID", 30.f, CAP_FFMPEG},
  385. {"avi", "MPEG", 30.f, CAP_FFMPEG},
  386. {"avi", "IYUV", 30.f, CAP_FFMPEG},
  387. {"avi", "MJPG", 30.f, CAP_FFMPEG},
  388. {"mkv", "XVID", 30.f, CAP_FFMPEG},
  389. {"mkv", "MPEG", 30.f, CAP_FFMPEG},
  390. {"mkv", "MJPG", 30.f, CAP_FFMPEG},
  391. {"avi", "MPEG", 28.f, CAP_GSTREAMER},
  392. {"avi", "MJPG", 30.f, CAP_GSTREAMER},
  393. {"avi", "H264", 30.f, CAP_GSTREAMER},
  394. {"mkv", "MPEG", 28.f, CAP_GSTREAMER},
  395. {"mkv", "MJPG", 30.f, CAP_GSTREAMER},
  396. {"mkv", "H264", 30.f, CAP_GSTREAMER},
  397. {"avi", "MJPG", 30.f, CAP_OPENCV_MJPEG},
  398. };
  399. Size all_sizes[] = {
  400. Size(640, 480),
  401. Size(976, 768)
  402. };
  403. TEST_P(videoio_synthetic, write_read_position) { doTest(); }
  404. INSTANTIATE_TEST_CASE_P(videoio, videoio_synthetic,
  405. testing::Combine(
  406. testing::ValuesIn(all_sizes),
  407. testing::ValuesIn(synthetic_params)));
  408. struct Ext_Fourcc_API
  409. {
  410. const char* ext;
  411. const char* fourcc;
  412. VideoCaptureAPIs api;
  413. };
  414. inline static std::ostream &operator<<(std::ostream &out, const Ext_Fourcc_API &p)
  415. {
  416. out << "(FOURCC(" << p.fourcc << "), \"" << p.ext << "\", " << p.api << ")"; return out;
  417. }
  418. class Videoio_Writer : public Videoio_Test_Base, public testing::TestWithParam<Ext_Fourcc_API>
  419. {
  420. protected:
  421. Size frame_size;
  422. int fourcc;
  423. double fps;
  424. public:
  425. Videoio_Writer()
  426. {
  427. frame_size = Size(640, 480);
  428. const Ext_Fourcc_API p = GetParam();
  429. ext = p.ext;
  430. fourcc = fourccFromString(p.fourcc);
  431. if (ext.size() == 3)
  432. video_file = cv::tempfile((fourccToString(fourcc) + "." + ext).c_str());
  433. else
  434. video_file = ext;
  435. fps = 25.;
  436. apiPref = p.api;
  437. }
  438. void SetUp()
  439. {
  440. }
  441. void TearDown()
  442. {
  443. if (ext.size() == 3)
  444. (void)remove(video_file.c_str());
  445. }
  446. };
  447. TEST_P(Videoio_Writer, write_nothing)
  448. {
  449. if (!cv::videoio_registry::hasBackend(apiPref))
  450. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  451. VideoWriter writer;
  452. EXPECT_NO_THROW(writer.open(video_file, apiPref, fourcc, fps, frame_size, true));
  453. ASSERT_TRUE(writer.isOpened());
  454. #if 0 // no frames
  455. cv::Mat m(frame_size, CV_8UC3, Scalar::all(127));
  456. writer << m;
  457. #endif
  458. EXPECT_NO_THROW(writer.release());
  459. }
  460. static vector<Ext_Fourcc_API> generate_Ext_Fourcc_API()
  461. {
  462. const size_t N = sizeof(synthetic_params)/sizeof(synthetic_params[0]);
  463. vector<Ext_Fourcc_API> result; result.reserve(N);
  464. for (size_t i = 0; i < N; i++)
  465. {
  466. const Ext_Fourcc_PSNR& src = synthetic_params[i];
  467. Ext_Fourcc_API e = { src.ext, src.fourcc, src.api };
  468. result.push_back(e);
  469. }
  470. {
  471. Ext_Fourcc_API e = { "appsrc ! videoconvert ! video/x-raw, format=(string)NV12 ! filesink location=test.nv12", "\0\0\0\0", CAP_GSTREAMER };
  472. result.push_back(e);
  473. }
  474. {
  475. Ext_Fourcc_API e = { "appsrc ! videoconvert ! video/x-raw, format=(string)I420 ! matroskamux ! filesink location=test.mkv", "\0\0\0\0", CAP_GSTREAMER };
  476. result.push_back(e);
  477. }
  478. return result;
  479. }
  480. INSTANTIATE_TEST_CASE_P(videoio, Videoio_Writer, testing::ValuesIn(generate_Ext_Fourcc_API()));
  481. TEST(Videoio, exceptions)
  482. {
  483. VideoCapture cap;
  484. Mat mat;
  485. EXPECT_FALSE(cap.grab());
  486. EXPECT_FALSE(cap.retrieve(mat));
  487. EXPECT_FALSE(cap.set(CAP_PROP_POS_FRAMES, 1));
  488. EXPECT_FALSE(cap.open("this_does_not_exist.avi", CAP_OPENCV_MJPEG));
  489. cap.setExceptionMode(true);
  490. EXPECT_THROW(cap.grab(), Exception);
  491. EXPECT_THROW(cap.retrieve(mat), Exception);
  492. EXPECT_THROW(cap.set(CAP_PROP_POS_FRAMES, 1), Exception);
  493. EXPECT_THROW(cap.open("this_does_not_exist.avi", CAP_OPENCV_MJPEG), Exception);
  494. }
  495. typedef Videoio_Writer Videoio_Writer_bad_fourcc;
  496. TEST_P(Videoio_Writer_bad_fourcc, nocrash)
  497. {
  498. if (!isBackendAvailable(apiPref, cv::videoio_registry::getStreamBackends()))
  499. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  500. VideoWriter writer;
  501. EXPECT_NO_THROW(writer.open(video_file, apiPref, fourcc, fps, frame_size, true));
  502. ASSERT_FALSE(writer.isOpened());
  503. EXPECT_NO_THROW(writer.release());
  504. }
  505. static vector<Ext_Fourcc_API> generate_Ext_Fourcc_API_nocrash()
  506. {
  507. static const Ext_Fourcc_API params[] = {
  508. #ifdef HAVE_MSMF_DISABLED // MSMF opens writer stream
  509. {"wmv", "aaaa", CAP_MSMF},
  510. {"mov", "aaaa", CAP_MSMF},
  511. #endif
  512. #ifdef HAVE_QUICKTIME
  513. {"mov", "aaaa", CAP_QT},
  514. {"avi", "aaaa", CAP_QT},
  515. {"mkv", "aaaa", CAP_QT},
  516. #endif
  517. #ifdef HAVE_AVFOUNDATION
  518. {"mov", "aaaa", CAP_AVFOUNDATION},
  519. {"mp4", "aaaa", CAP_AVFOUNDATION},
  520. {"m4v", "aaaa", CAP_AVFOUNDATION},
  521. #endif
  522. #ifdef HAVE_FFMPEG
  523. {"avi", "aaaa", CAP_FFMPEG},
  524. {"mkv", "aaaa", CAP_FFMPEG},
  525. #endif
  526. #ifdef HAVE_GSTREAMER
  527. {"avi", "aaaa", CAP_GSTREAMER},
  528. {"mkv", "aaaa", CAP_GSTREAMER},
  529. #endif
  530. {"avi", "aaaa", CAP_OPENCV_MJPEG},
  531. };
  532. const size_t N = sizeof(params)/sizeof(params[0]);
  533. vector<Ext_Fourcc_API> result; result.reserve(N);
  534. for (size_t i = 0; i < N; i++)
  535. {
  536. const Ext_Fourcc_API& src = params[i];
  537. Ext_Fourcc_API e = { src.ext, src.fourcc, src.api };
  538. result.push_back(e);
  539. }
  540. return result;
  541. }
  542. INSTANTIATE_TEST_CASE_P(videoio, Videoio_Writer_bad_fourcc, testing::ValuesIn(generate_Ext_Fourcc_API_nocrash()));
  543. typedef testing::TestWithParam<VideoCaptureAPIs> safe_capture;
  544. TEST_P(safe_capture, frames_independency)
  545. {
  546. VideoCaptureAPIs apiPref = GetParam();
  547. if (!videoio_registry::hasBackend(apiPref))
  548. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  549. VideoCapture cap;
  550. String video_file = BunnyParameters::getFilename(String(".avi"));
  551. EXPECT_NO_THROW(cap.open(video_file, apiPref));
  552. if (!cap.isOpened())
  553. {
  554. std::cout << "SKIP test: backend " << apiPref << " can't open the video: " << video_file << std::endl;
  555. return;
  556. }
  557. Mat frames[10];
  558. Mat hardCopies[10];
  559. for(int i = 0; i < 10; i++)
  560. {
  561. ASSERT_NO_THROW(cap >> frames[i]);
  562. EXPECT_FALSE(frames[i].empty());
  563. hardCopies[i] = frames[i].clone();
  564. }
  565. for(int i = 0; i < 10; i++)
  566. EXPECT_EQ(0, cv::norm(frames[i], hardCopies[i], NORM_INF)) << i;
  567. }
  568. static VideoCaptureAPIs safe_apis[] = {CAP_FFMPEG, CAP_GSTREAMER, CAP_MSMF,CAP_AVFOUNDATION};
  569. INSTANTIATE_TEST_CASE_P(videoio, safe_capture, testing::ValuesIn(safe_apis));
  570. //==================================================================================================
  571. // TEST_P(videocapture_acceleration, ...)
  572. struct VideoCaptureAccelerationInput
  573. {
  574. const char* filename;
  575. double psnr_threshold;
  576. };
  577. static inline
  578. std::ostream& operator<<(std::ostream& out, const VideoCaptureAccelerationInput& p)
  579. {
  580. out << p.filename;
  581. return out;
  582. }
  583. typedef testing::TestWithParam<tuple<VideoCaptureAccelerationInput, VideoCaptureAPIs, VideoAccelerationType, bool>> videocapture_acceleration;
  584. TEST_P(videocapture_acceleration, read)
  585. {
  586. auto param = GetParam();
  587. std::string filename = get<0>(param).filename;
  588. double psnr_threshold = get<0>(param).psnr_threshold;
  589. VideoCaptureAPIs backend = get<1>(param);
  590. VideoAccelerationType va_type = get<2>(param);
  591. bool use_umat = get<3>(param);
  592. const int frameNum = 15;
  593. std::string filepath = cvtest::findDataFile("video/" + filename);
  594. if (backend == CAP_MSMF && (
  595. filename == "sample_322x242_15frames.yuv420p.mjpeg.mp4" ||
  596. filename == "sample_322x242_15frames.yuv420p.libx265.mp4" ||
  597. filename == "sample_322x242_15frames.yuv420p.libaom-av1.mp4" ||
  598. filename == "sample_322x242_15frames.yuv420p.mpeg2video.mp4"
  599. ))
  600. throw SkipTestException("Format/codec is not supported");
  601. std::string backend_name = cv::videoio_registry::getBackendName(backend);
  602. if (!videoio_registry::hasBackend(backend))
  603. throw SkipTestException(cv::String("Backend is not available/disabled: ") + backend_name);
  604. // HW reader
  605. std::vector<int> params = { CAP_PROP_HW_ACCELERATION, static_cast<int>(va_type) };
  606. if (use_umat)
  607. {
  608. if (backend != CAP_FFMPEG)
  609. throw SkipTestException(cv::String("UMat/OpenCL mapping is not supported by current backend: ") + backend_name);
  610. if (!cv::videoio_registry::isBackendBuiltIn(backend))
  611. throw SkipTestException(cv::String("UMat/OpenCL mapping is not supported through plugins yet: ") + backend_name);
  612. params.push_back(CAP_PROP_HW_ACCELERATION_USE_OPENCL);
  613. params.push_back(1);
  614. }
  615. VideoCapture hw_reader(filepath, backend, params);
  616. if (!hw_reader.isOpened())
  617. {
  618. if (use_umat)
  619. {
  620. throw SkipTestException(backend_name + " VideoCapture on " + filename + " not supported with HW acceleration + OpenCL/Umat mapping, skipping");
  621. }
  622. else if (va_type == VIDEO_ACCELERATION_ANY || va_type == VIDEO_ACCELERATION_NONE)
  623. {
  624. // ANY HW acceleration should have fallback to SW codecs
  625. VideoCapture sw_reader(filepath, backend, {
  626. CAP_PROP_HW_ACCELERATION, VIDEO_ACCELERATION_NONE
  627. });
  628. if (!sw_reader.isOpened())
  629. throw SkipTestException(backend_name + " VideoCapture on " + filename + " not supported, skipping");
  630. ASSERT_TRUE(hw_reader.isOpened()) << "ANY HW acceleration should have fallback to SW codecs";
  631. }
  632. else
  633. {
  634. throw SkipTestException(backend_name + " VideoCapture on " + filename + " not supported with HW acceleration, skipping");
  635. }
  636. }
  637. VideoAccelerationType actual_va = static_cast<VideoAccelerationType>(static_cast<int>(hw_reader.get(CAP_PROP_HW_ACCELERATION)));
  638. if (va_type != VIDEO_ACCELERATION_ANY && va_type != VIDEO_ACCELERATION_NONE)
  639. {
  640. ASSERT_EQ((int)actual_va, (int)va_type) << "actual_va=" << actual_va << ", va_type=" << va_type;
  641. }
  642. std::cout << "VideoCapture " << backend_name << ":" << actual_va << std::endl << std::flush;
  643. double min_psnr_original = 1000;
  644. for (int i = 0; i < frameNum; i++)
  645. {
  646. SCOPED_TRACE(cv::format("frame=%d", i));
  647. Mat frame;
  648. if (use_umat)
  649. {
  650. UMat umat;
  651. bool read_umat_result = hw_reader.read(umat);
  652. if (!read_umat_result && i == 0)
  653. {
  654. if (filename == "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4")
  655. throw SkipTestException("Unable to read the first frame with VP9 codec (media stack misconfiguration / bug)");
  656. // FFMPEG: [av1 @ 0000027ac07d1340] Your platform doesn't suppport hardware accelerated AV1 decoding.
  657. if (filename == "sample_322x242_15frames.yuv420p.libaom-av1.mp4")
  658. throw SkipTestException("Unable to read the first frame with AV1 codec (missing support)");
  659. }
  660. EXPECT_TRUE(read_umat_result);
  661. ASSERT_FALSE(umat.empty());
  662. umat.copyTo(frame);
  663. }
  664. else
  665. {
  666. bool read_result = hw_reader.read(frame);
  667. if (!read_result && i == 0)
  668. {
  669. if (filename == "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4")
  670. throw SkipTestException("Unable to read the first frame with VP9 codec (media stack misconfiguration / bug)");
  671. // FFMPEG: [av1 @ 0000027ac07d1340] Your platform doesn't suppport hardware accelerated AV1 decoding.
  672. if (filename == "sample_322x242_15frames.yuv420p.libaom-av1.mp4")
  673. throw SkipTestException("Unable to read the first frame with AV1 codec (missing support)");
  674. }
  675. EXPECT_TRUE(read_result);
  676. }
  677. ASSERT_FALSE(frame.empty());
  678. if (cvtest::debugLevel > 0)
  679. {
  680. imwrite(cv::format("test_frame%03d.png", i), frame);
  681. }
  682. Mat original(frame.size(), CV_8UC3, Scalar::all(0));
  683. generateFrame(i, frameNum, original);
  684. double psnr = cvtest::PSNR(frame, original);
  685. if (psnr < min_psnr_original)
  686. min_psnr_original = psnr;
  687. }
  688. std::ostringstream ss; ss << actual_va;
  689. std::string actual_va_str = ss.str();
  690. std::cout << "VideoCapture with acceleration = " << cv::format("%-6s @ %-10s", actual_va_str.c_str(), backend_name.c_str())
  691. << " on " << filename
  692. << " with PSNR-original = " << min_psnr_original
  693. << std::endl << std::flush;
  694. EXPECT_GE(min_psnr_original, psnr_threshold);
  695. }
  696. static const VideoCaptureAccelerationInput hw_filename[] = {
  697. { "sample_322x242_15frames.yuv420p.libxvid.mp4", 28.0 },
  698. { "sample_322x242_15frames.yuv420p.mjpeg.mp4", 20.0 },
  699. { "sample_322x242_15frames.yuv420p.mpeg2video.mp4", 24.0 }, // GSTREAMER on Ubuntu 18.04
  700. { "sample_322x242_15frames.yuv420p.libx264.mp4", 24.0 }, // GSTREAMER on Ubuntu 18.04
  701. { "sample_322x242_15frames.yuv420p.libx265.mp4", 30.0 },
  702. { "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4", 30.0 },
  703. { "sample_322x242_15frames.yuv420p.libaom-av1.mp4", 30.0 }
  704. };
  705. static const VideoCaptureAPIs hw_backends[] = {
  706. CAP_FFMPEG,
  707. CAP_GSTREAMER,
  708. #ifdef _WIN32
  709. CAP_MSMF,
  710. #endif
  711. };
  712. static const VideoAccelerationType hw_types[] = {
  713. VIDEO_ACCELERATION_NONE,
  714. VIDEO_ACCELERATION_ANY,
  715. VIDEO_ACCELERATION_MFX,
  716. #ifdef _WIN32
  717. VIDEO_ACCELERATION_D3D11,
  718. #else
  719. VIDEO_ACCELERATION_VAAPI,
  720. #endif
  721. };
  722. static bool hw_use_umat[] = {
  723. false,
  724. true
  725. };
  726. INSTANTIATE_TEST_CASE_P(videoio, videocapture_acceleration, testing::Combine(
  727. testing::ValuesIn(hw_filename),
  728. testing::ValuesIn(hw_backends),
  729. testing::ValuesIn(hw_types),
  730. testing::ValuesIn(hw_use_umat)
  731. ));
  732. ////////////////////////////////////////// TEST_P(video_acceleration, write_read)
  733. typedef tuple<Ext_Fourcc_PSNR, VideoAccelerationType, bool> VATestParams;
  734. typedef testing::TestWithParam<VATestParams> videowriter_acceleration;
  735. TEST_P(videowriter_acceleration, write)
  736. {
  737. auto param = GetParam();
  738. VideoCaptureAPIs backend = get<0>(param).api;
  739. std::string codecid = get<0>(param).fourcc;
  740. std::string extension = get<0>(param).ext;
  741. double psnr_threshold = get<0>(param).PSNR;
  742. VideoAccelerationType va_type = get<1>(param);
  743. bool use_umat = get<2>(param);
  744. std::string backend_name = cv::videoio_registry::getBackendName(backend);
  745. if (!videoio_registry::hasBackend(backend))
  746. throw SkipTestException(cv::String("Backend is not available/disabled: ") + backend_name);
  747. const Size sz(640, 480);
  748. const int frameNum = 15;
  749. const double fps = 25;
  750. std::string filename = tempfile("videowriter_acceleration.") + extension;
  751. // Write video
  752. VideoAccelerationType actual_va;
  753. {
  754. std::vector<int> params = { VIDEOWRITER_PROP_HW_ACCELERATION, static_cast<int>(va_type) };
  755. if (use_umat) {
  756. if (backend != CAP_FFMPEG)
  757. throw SkipTestException(cv::String("UMat/OpenCL mapping is not supported by current backend: ") + backend_name);
  758. if (!cv::videoio_registry::isBackendBuiltIn(backend))
  759. throw SkipTestException(cv::String("UMat/OpenCL mapping is not supported through plugins yet: ") + backend_name);
  760. params.push_back(VIDEOWRITER_PROP_HW_ACCELERATION_USE_OPENCL);
  761. params.push_back(1);
  762. }
  763. VideoWriter hw_writer(
  764. filename,
  765. backend,
  766. VideoWriter::fourcc(codecid[0], codecid[1], codecid[2], codecid[3]),
  767. fps,
  768. sz,
  769. params
  770. );
  771. if (!hw_writer.isOpened())
  772. {
  773. if (use_umat)
  774. {
  775. throw SkipTestException(backend_name + " VideoWriter on " + filename + " not supported with HW acceleration + OpenCL/Umat mapping, skipping");
  776. }
  777. else if (va_type == VIDEO_ACCELERATION_ANY || va_type == VIDEO_ACCELERATION_NONE)
  778. {
  779. // ANY HW acceleration should have fallback to SW codecs
  780. {
  781. VideoWriter sw_writer(
  782. filename,
  783. backend,
  784. VideoWriter::fourcc(codecid[0], codecid[1], codecid[2], codecid[3]),
  785. fps,
  786. sz,
  787. {
  788. VIDEOWRITER_PROP_HW_ACCELERATION, VIDEO_ACCELERATION_NONE,
  789. }
  790. );
  791. if (!sw_writer.isOpened()) {
  792. remove(filename.c_str());
  793. throw SkipTestException(backend_name + " VideoWriter on codec " + codecid + " not supported, skipping");
  794. }
  795. }
  796. remove(filename.c_str());
  797. ASSERT_TRUE(hw_writer.isOpened()) << "ANY HW acceleration should have fallback to SW codecs";
  798. } else {
  799. throw SkipTestException(backend_name + " VideoWriter on " + filename + " not supported with HW acceleration, skipping");
  800. }
  801. }
  802. actual_va = static_cast<VideoAccelerationType>(static_cast<int>(hw_writer.get(VIDEOWRITER_PROP_HW_ACCELERATION)));
  803. if (va_type != VIDEO_ACCELERATION_ANY && va_type != VIDEO_ACCELERATION_NONE)
  804. {
  805. ASSERT_EQ((int)actual_va, (int)va_type) << "actual_va=" << actual_va << ", va_type=" << va_type;
  806. }
  807. std::cout << "VideoWriter " << backend_name << ":" << actual_va << std::endl << std::flush;
  808. Mat frame(sz, CV_8UC3);
  809. for (int i = 0; i < frameNum; ++i) {
  810. generateFrame(i, frameNum, frame);
  811. if (use_umat) {
  812. UMat umat;
  813. frame.copyTo(umat);
  814. hw_writer.write(umat);
  815. }
  816. else {
  817. hw_writer.write(frame);
  818. }
  819. }
  820. }
  821. std::ifstream ofile(filename, std::ios::binary);
  822. ofile.seekg(0, std::ios::end);
  823. int64 fileSize = (int64)ofile.tellg();
  824. ASSERT_GT(fileSize, 0);
  825. std::cout << "File size: " << fileSize << std::endl;
  826. // Read video and check PSNR on every frame
  827. {
  828. VideoCapture reader(
  829. filename,
  830. CAP_ANY /*backend*/,
  831. { CAP_PROP_HW_ACCELERATION, VIDEO_ACCELERATION_NONE }
  832. );
  833. ASSERT_TRUE(reader.isOpened());
  834. double min_psnr = 1000;
  835. Mat reference(sz, CV_8UC3);
  836. for (int i = 0; i < frameNum; ++i) {
  837. Mat actual;
  838. if (use_umat) {
  839. UMat umat;
  840. EXPECT_TRUE(reader.read(umat));
  841. umat.copyTo(actual);
  842. }
  843. else {
  844. EXPECT_TRUE(reader.read(actual));
  845. }
  846. EXPECT_FALSE(actual.empty());
  847. generateFrame(i, frameNum, reference);
  848. EXPECT_EQ(reference.size(), actual.size());
  849. EXPECT_EQ(reference.depth(), actual.depth());
  850. EXPECT_EQ(reference.channels(), actual.channels());
  851. double psnr = cvtest::PSNR(actual, reference);
  852. EXPECT_GE(psnr, psnr_threshold) << " frame " << i;
  853. if (psnr < min_psnr)
  854. min_psnr = psnr;
  855. }
  856. Mat actual;
  857. EXPECT_FALSE(reader.read(actual));
  858. {
  859. std::ostringstream ss; ss << actual_va;
  860. std::string actual_va_str = ss.str();
  861. std::cout << "VideoWriter with acceleration = " << cv::format("%-6s @ %-10s", actual_va_str.c_str(), backend_name.c_str())
  862. << " on codec=" << codecid << " (." << extension << ")"
  863. << ", bitrate = " << fileSize / (frameNum / fps)
  864. << ", with PSNR-original = " << min_psnr
  865. << std::endl << std::flush;
  866. }
  867. remove(filename.c_str());
  868. }
  869. }
  870. static Ext_Fourcc_PSNR hw_codecs[] = {
  871. {"mp4", "MPEG", 29.f, CAP_FFMPEG},
  872. {"mp4", "H264", 29.f, CAP_FFMPEG},
  873. {"mp4", "HEVC", 29.f, CAP_FFMPEG},
  874. {"avi", "MJPG", 29.f, CAP_FFMPEG},
  875. {"avi", "XVID", 29.f, CAP_FFMPEG},
  876. //{"webm", "VP8", 29.f, CAP_FFMPEG},
  877. //{"webm", "VP9", 29.f, CAP_FFMPEG},
  878. {"mkv", "MPEG", 29.f, CAP_GSTREAMER},
  879. {"mkv", "H264", 29.f, CAP_GSTREAMER},
  880. #ifdef _WIN32
  881. {"mp4", "MPEG", 29.f, CAP_MSMF},
  882. {"mp4", "H264", 29.f, CAP_MSMF},
  883. #endif
  884. };
  885. INSTANTIATE_TEST_CASE_P(videoio, videowriter_acceleration, testing::Combine(
  886. testing::ValuesIn(hw_codecs),
  887. testing::ValuesIn(hw_types),
  888. testing::ValuesIn(hw_use_umat)
  889. ));
  890. } // namespace