test_video_io.cpp 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. #include "test_precomp.hpp"
  5. #include "opencv2/core/utils/filesystem.hpp"
  6. namespace opencv_test
  7. {
  8. class Videoio_Test_Base
  9. {
  10. protected:
  11. string ext;
  12. string video_file;
  13. VideoCaptureAPIs apiPref;
  14. protected:
  15. Videoio_Test_Base() {}
  16. virtual ~Videoio_Test_Base() {}
  17. virtual void writeVideo() {}
  18. virtual void checkFrameContent(Mat &, int) {}
  19. virtual void checkFrameCount(int &) {}
  20. void checkFrameRead(int idx, VideoCapture & cap)
  21. {
  22. //int frameID = (int)cap.get(CAP_PROP_POS_FRAMES);
  23. Mat img;
  24. ASSERT_NO_THROW(cap >> img);
  25. //std::cout << "idx=" << idx << " img=" << img.size() << " frameID=" << frameID << std::endl;
  26. ASSERT_FALSE(img.empty()) << "idx=" << idx;
  27. checkFrameContent(img, idx);
  28. }
  29. void checkFrameSeek(int idx, VideoCapture & cap)
  30. {
  31. bool canSeek = false;
  32. ASSERT_NO_THROW(canSeek = cap.set(CAP_PROP_POS_FRAMES, idx));
  33. if (!canSeek)
  34. {
  35. std::cout << "Seek to frame '" << idx << "' is not supported. SKIP." << std::endl;
  36. return;
  37. }
  38. EXPECT_EQ(idx, (int)cap.get(CAP_PROP_POS_FRAMES));
  39. checkFrameRead(idx, cap);
  40. }
  41. public:
  42. void doTest()
  43. {
  44. if (!videoio_registry::hasBackend(apiPref))
  45. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  46. if (cvtest::skipUnstableTests && apiPref == CAP_MSMF && (ext == "h264" || ext == "h265" || ext == "mpg"))
  47. throw SkipTestException("Unstable MSMF test");
  48. #ifdef __linux__
  49. if (cvtest::skipUnstableTests && apiPref == CAP_GSTREAMER &&
  50. (ext == "avi" || ext == "mkv") &&
  51. (video_file.find("MPEG") != std::string::npos))
  52. {
  53. throw SkipTestException("Unstable GSTREAMER test");
  54. }
  55. #endif
  56. writeVideo();
  57. VideoCapture cap;
  58. ASSERT_NO_THROW(cap.open(video_file, apiPref));
  59. if (!cap.isOpened())
  60. {
  61. std::cout << "SKIP test: backend " << apiPref << " can't open the video: " << video_file << std::endl;
  62. return;
  63. }
  64. int n_frames = -1;
  65. EXPECT_NO_THROW(n_frames = (int)cap.get(CAP_PROP_FRAME_COUNT));
  66. if (n_frames > 0)
  67. {
  68. ASSERT_GT(n_frames, 0);
  69. checkFrameCount(n_frames);
  70. }
  71. else
  72. {
  73. std::cout << "CAP_PROP_FRAME_COUNT is not supported by backend. Assume 50 frames." << std::endl;
  74. n_frames = 50;
  75. }
  76. // GStreamer can't read frame count of big_buck_bunny.wmv
  77. if (apiPref == CAP_GSTREAMER && ext == "wmv")
  78. {
  79. n_frames = 125;
  80. }
  81. {
  82. SCOPED_TRACE("consecutive read");
  83. if (apiPref == CAP_GSTREAMER)
  84. {
  85. // This workaround is for GStreamer 1.3.1.1 and older.
  86. // Old Gstreamer has a bug which handles the total duration 1 frame shorter
  87. // Old Gstreamer are used in Ubuntu 14.04, so the following code could be removed after it's EOL
  88. n_frames--;
  89. }
  90. for (int k = 0; k < n_frames; ++k)
  91. {
  92. checkFrameRead(k, cap);
  93. if (::testing::Test::HasFailure() && k % 10 == 0)
  94. break;
  95. }
  96. }
  97. bool canSeek = false;
  98. EXPECT_NO_THROW(canSeek = cap.set(CAP_PROP_POS_FRAMES, 0));
  99. if (!canSeek)
  100. {
  101. std::cout << "Seek to frame '0' is not supported. SKIP all 'seek' tests." << std::endl;
  102. return;
  103. }
  104. if (ext != "wmv" && ext != "h264" && ext != "h265")
  105. {
  106. SCOPED_TRACE("progressive seek");
  107. bool res = false;
  108. EXPECT_NO_THROW(res = cap.set(CAP_PROP_POS_FRAMES, 0));
  109. ASSERT_TRUE(res);
  110. for (int k = 0; k < n_frames; k += 20)
  111. {
  112. checkFrameSeek(k, cap);
  113. if (::testing::Test::HasFailure() && k % 10 == 0)
  114. break;
  115. }
  116. }
  117. if (ext != "mpg" && ext != "wmv" && ext != "h264" && ext != "h265")
  118. {
  119. SCOPED_TRACE("random seek");
  120. bool res = false;
  121. EXPECT_NO_THROW(res = cap.set(CAP_PROP_POS_FRAMES, 0));
  122. ASSERT_TRUE(res);
  123. for (int k = 0; k < 10; ++k)
  124. {
  125. checkFrameSeek(cvtest::TS::ptr()->get_rng().uniform(0, n_frames), cap);
  126. if (::testing::Test::HasFailure() && k % 10 == 0)
  127. break;
  128. }
  129. }
  130. }
  131. };
  132. //==================================================================================================
  133. typedef tuple<string, VideoCaptureAPIs> Backend_Type_Params;
  134. class videoio_bunny : public Videoio_Test_Base, public testing::TestWithParam<Backend_Type_Params>
  135. {
  136. BunnyParameters bunny_param;
  137. public:
  138. videoio_bunny()
  139. {
  140. ext = get<0>(GetParam());
  141. apiPref = get<1>(GetParam());
  142. video_file = BunnyParameters::getFilename(String(".") + ext);
  143. }
  144. void doFrameCountTest()
  145. {
  146. if (!videoio_registry::hasBackend(apiPref))
  147. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  148. if (cvtest::skipUnstableTests && apiPref == CAP_MSMF && (ext == "h264" || ext == "h265" || ext == "mpg"))
  149. throw SkipTestException("Unstable MSMF test");
  150. VideoCapture cap;
  151. EXPECT_NO_THROW(cap.open(video_file, apiPref));
  152. if (!cap.isOpened())
  153. {
  154. std::cout << "SKIP test: backend " << apiPref << " can't open the video: " << video_file << std::endl;
  155. return;
  156. }
  157. Size actual;
  158. EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH),
  159. (int)cap.get(CAP_PROP_FRAME_HEIGHT)));
  160. EXPECT_EQ(bunny_param.getWidth(), actual.width);
  161. EXPECT_EQ(bunny_param.getHeight(), actual.height);
  162. double fps_prop = 0;
  163. EXPECT_NO_THROW(fps_prop = cap.get(CAP_PROP_FPS));
  164. if (fps_prop > 0)
  165. EXPECT_NEAR(fps_prop, bunny_param.getFps(), 1);
  166. else
  167. std::cout << "FPS is not available. SKIP check." << std::endl;
  168. int count_prop = 0;
  169. EXPECT_NO_THROW(count_prop = (int)cap.get(CAP_PROP_FRAME_COUNT));
  170. // mpg file reports 5.08 sec * 24 fps => property returns 122 frames
  171. // but actual number of frames returned is 125
  172. if (ext != "mpg" && !(apiPref == CAP_GSTREAMER && ext == "wmv"))
  173. {
  174. if (count_prop > 0)
  175. {
  176. EXPECT_EQ(bunny_param.getCount(), count_prop);
  177. }
  178. }
  179. int count_actual = 0;
  180. while (cap.isOpened())
  181. {
  182. Mat frame;
  183. EXPECT_NO_THROW(cap >> frame);
  184. if (frame.empty())
  185. break;
  186. EXPECT_EQ(bunny_param.getWidth(), frame.cols);
  187. EXPECT_EQ(bunny_param.getHeight(), frame.rows);
  188. count_actual += 1;
  189. if (::testing::Test::HasFailure() && count_actual % 10 == 0)
  190. break;
  191. }
  192. if (count_prop > 0)
  193. {
  194. EXPECT_NEAR(bunny_param.getCount(), count_actual, 1);
  195. }
  196. else
  197. std::cout << "Frames counter is not available. Actual frames: " << count_actual << ". SKIP check." << std::endl;
  198. }
  199. void doTimestampTest()
  200. {
  201. if (!isBackendAvailable(apiPref, cv::videoio_registry::getStreamBackends()))
  202. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  203. if (((apiPref == CAP_GSTREAMER) && (ext == "avi")))
  204. throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) +
  205. cv::String(" does not support CAP_PROP_POS_MSEC option"));
  206. if (((apiPref == CAP_FFMPEG || apiPref == CAP_GSTREAMER) && ((ext == "h264") || (ext == "h265"))))
  207. throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) +
  208. cv::String(" does not support CAP_PROP_POS_MSEC option"));
  209. VideoCapture cap;
  210. EXPECT_NO_THROW(cap.open(video_file, apiPref));
  211. if (!cap.isOpened())
  212. throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) +
  213. cv::String(" can't open the video: ") + video_file);
  214. int frame_count = (int)cap.get(CAP_PROP_FRAME_COUNT);
  215. // HACK: Video consists of 125 frames, but cv::VideoCapture with FFmpeg reports only 122 frames for mpg video.
  216. // mpg file reports 5.08 sec * 24 fps => property returns 122 frames,but actual number of frames returned is 125
  217. // HACK: CAP_PROP_FRAME_COUNT is not supported for vmw + MSMF. Just force check for all 125 frames
  218. if (ext == "mpg")
  219. EXPECT_GE(frame_count, 114);
  220. else if ((ext == "wmv") && (apiPref == CAP_MSMF || apiPref == CAP_GSTREAMER))
  221. frame_count = 125;
  222. else
  223. EXPECT_EQ(frame_count, 125);
  224. Mat img;
  225. // HACK: FFmpeg reports picture_pts = AV_NOPTS_VALUE_ for the last frame for AVI container by some reason
  226. if ((ext == "avi") && (apiPref == CAP_FFMPEG))
  227. frame_count--;
  228. for (int i = 0; i < frame_count; i++)
  229. {
  230. double timestamp = 0;
  231. ASSERT_NO_THROW(cap >> img);
  232. EXPECT_NO_THROW(timestamp = cap.get(CAP_PROP_POS_MSEC));
  233. if (cvtest::debugLevel > 0)
  234. std::cout << "i = " << i << ": timestamp = " << timestamp << std::endl;
  235. const double frame_period = 1000.f/bunny_param.getFps();
  236. // big_buck_bunny.mpg starts at 0.500 msec
  237. if ((ext == "mpg") && (apiPref == CAP_GSTREAMER))
  238. timestamp -= 500.0;
  239. // NOTE: eps == frame_period, because videoCapture returns frame beginning timestamp or frame end
  240. // timestamp depending on codec and back-end. So the first frame has timestamp 0 or frame_period.
  241. EXPECT_NEAR(timestamp, i*frame_period, frame_period) << "i=" << i;
  242. }
  243. }
  244. };
  245. //==================================================================================================
  246. struct Ext_Fourcc_PSNR
  247. {
  248. const char* ext;
  249. const char* fourcc;
  250. float PSNR;
  251. VideoCaptureAPIs api;
  252. };
  253. typedef tuple<Size, Ext_Fourcc_PSNR> Size_Ext_Fourcc_PSNR;
  254. class videoio_synthetic : public Videoio_Test_Base, public testing::TestWithParam<Size_Ext_Fourcc_PSNR>
  255. {
  256. Size frame_size;
  257. int fourcc;
  258. float PSNR_GT;
  259. int frame_count;
  260. double fps;
  261. public:
  262. videoio_synthetic()
  263. {
  264. frame_size = get<0>(GetParam());
  265. const Ext_Fourcc_PSNR p = get<1>(GetParam());
  266. ext = p.ext;
  267. fourcc = fourccFromString(p.fourcc);
  268. PSNR_GT = p.PSNR;
  269. video_file = cv::tempfile((fourccToString(fourcc) + "." + ext).c_str());
  270. frame_count = 100;
  271. fps = 25.;
  272. apiPref = p.api;
  273. }
  274. void TearDown()
  275. {
  276. remove(video_file.c_str());
  277. }
  278. virtual void writeVideo()
  279. {
  280. Mat img(frame_size, CV_8UC3);
  281. VideoWriter writer;
  282. EXPECT_NO_THROW(writer.open(video_file, apiPref, fourcc, fps, frame_size, true));
  283. ASSERT_TRUE(writer.isOpened());
  284. for(int i = 0; i < frame_count; ++i )
  285. {
  286. generateFrame(i, frame_count, img);
  287. EXPECT_NO_THROW(writer << img);
  288. if (::testing::Test::HasFailure() && i % 10 == 0)
  289. break;
  290. }
  291. EXPECT_NO_THROW(writer.release());
  292. }
  293. virtual void checkFrameContent(Mat & img, int idx)
  294. {
  295. Mat imgGT(frame_size, CV_8UC3);
  296. generateFrame(idx, frame_count, imgGT);
  297. double psnr = cvtest::PSNR(img, imgGT);
  298. ASSERT_GT(psnr, PSNR_GT) << "frame " << idx;
  299. }
  300. virtual void checkFrameCount(int &actual)
  301. {
  302. Range expected_frame_count = Range(frame_count, frame_count);
  303. // Hack! Newer FFmpeg versions in this combination produce a file
  304. // whose reported duration is one frame longer than needed, and so
  305. // the calculated frame count is also off by one. Ideally, we'd want
  306. // to fix both writing (to produce the correct duration) and reading
  307. // (to correctly report frame count for such files), but I don't know
  308. // how to do either, so this is a workaround for now.
  309. if (fourcc == VideoWriter::fourcc('M', 'P', 'E', 'G') && ext == "mkv")
  310. expected_frame_count.end += 1;
  311. // Workaround for some gstreamer pipelines
  312. if (apiPref == CAP_GSTREAMER)
  313. expected_frame_count.start -= 1;
  314. ASSERT_LE(expected_frame_count.start, actual);
  315. ASSERT_GE(expected_frame_count.end, actual);
  316. actual = expected_frame_count.start; // adjust actual frame boundary to possible minimum
  317. }
  318. };
  319. //==================================================================================================
  320. static const VideoCaptureAPIs backend_params[] = {
  321. #ifdef HAVE_AVFOUNDATION
  322. CAP_AVFOUNDATION,
  323. #endif
  324. #ifdef _WIN32
  325. CAP_MSMF,
  326. #endif
  327. CAP_GSTREAMER,
  328. CAP_FFMPEG,
  329. #ifdef HAVE_XINE
  330. CAP_XINE,
  331. #endif
  332. CAP_OPENCV_MJPEG
  333. // CAP_INTEL_MFX
  334. };
  335. static const string bunny_params[] = {
  336. string("wmv"),
  337. string("mov"),
  338. string("mp4"),
  339. string("mpg"),
  340. string("avi"),
  341. string("h264"),
  342. string("h265"),
  343. string("mjpg.avi")
  344. };
  345. TEST_P(videoio_bunny, read_position) { doTest(); }
  346. TEST_P(videoio_bunny, frame_count) { doFrameCountTest(); }
  347. TEST_P(videoio_bunny, frame_timestamp) { doTimestampTest(); }
  348. inline static std::string videoio_bunny_name_printer(const testing::TestParamInfo<videoio_bunny::ParamType>& info)
  349. {
  350. std::ostringstream os;
  351. os << extToStringSafe(get<0>(info.param)) << "_"
  352. << getBackendNameSafe(get<1>(info.param));
  353. return os.str();
  354. }
  355. INSTANTIATE_TEST_CASE_P(videoio, videoio_bunny,
  356. testing::Combine(
  357. testing::ValuesIn(bunny_params),
  358. testing::ValuesIn(backend_params)),
  359. videoio_bunny_name_printer);
  360. inline static std::ostream &operator<<(std::ostream &out, const Ext_Fourcc_PSNR &p)
  361. {
  362. out << "FOURCC(" << p.fourcc << "), ." << p.ext << ", " << p.api << ", " << p.PSNR << "dB"; return out;
  363. }
  364. static Ext_Fourcc_PSNR synthetic_params[] = {
  365. #ifdef _WIN32
  366. #if !defined(_M_ARM)
  367. {"wmv", "WMV1", 30.f, CAP_MSMF},
  368. {"wmv", "WMV2", 30.f, CAP_MSMF},
  369. #endif
  370. {"wmv", "WMV3", 30.f, CAP_MSMF},
  371. {"wmv", "WVC1", 30.f, CAP_MSMF},
  372. {"mov", "H264", 30.f, CAP_MSMF},
  373. // {"mov", "HEVC", 30.f, CAP_MSMF}, // excluded due to CI issue: https://github.com/opencv/opencv/pull/23172
  374. #endif
  375. #ifdef HAVE_AVFOUNDATION
  376. {"mov", "H264", 30.f, CAP_AVFOUNDATION},
  377. {"mov", "MJPG", 30.f, CAP_AVFOUNDATION},
  378. {"mp4", "H264", 30.f, CAP_AVFOUNDATION},
  379. {"mp4", "MJPG", 30.f, CAP_AVFOUNDATION},
  380. {"m4v", "H264", 30.f, CAP_AVFOUNDATION},
  381. {"m4v", "MJPG", 30.f, CAP_AVFOUNDATION},
  382. #endif
  383. {"avi", "XVID", 30.f, CAP_FFMPEG},
  384. {"avi", "MPEG", 30.f, CAP_FFMPEG},
  385. {"avi", "IYUV", 30.f, CAP_FFMPEG},
  386. {"avi", "MJPG", 30.f, CAP_FFMPEG},
  387. {"mkv", "XVID", 30.f, CAP_FFMPEG},
  388. {"mkv", "MPEG", 30.f, CAP_FFMPEG},
  389. {"mkv", "MJPG", 30.f, CAP_FFMPEG},
  390. {"avi", "FFV1", 30.f, CAP_FFMPEG},
  391. {"mkv", "FFV1", 30.f, CAP_FFMPEG},
  392. {"avi", "MPEG", 28.f, CAP_GSTREAMER},
  393. {"avi", "MJPG", 30.f, CAP_GSTREAMER},
  394. {"avi", "H264", 30.f, CAP_GSTREAMER},
  395. {"mkv", "MPEG", 28.f, CAP_GSTREAMER},
  396. {"mkv", "MJPG", 30.f, CAP_GSTREAMER},
  397. {"mkv", "H264", 30.f, CAP_GSTREAMER},
  398. {"avi", "MJPG", 30.f, CAP_OPENCV_MJPEG},
  399. };
  400. Size all_sizes[] = {
  401. Size(640, 480),
  402. Size(976, 768)
  403. };
  404. TEST_P(videoio_synthetic, write_read_position) { doTest(); }
  405. inline static std::string videoio_synthetic_name_printer(const testing::TestParamInfo<videoio_synthetic::ParamType>& info)
  406. {
  407. std::ostringstream os;
  408. const Size sz = get<0>(info.param);
  409. const Ext_Fourcc_PSNR & param = get<1>(info.param);
  410. os << sz.height << "p" << "_"
  411. << param.ext << "_"
  412. << param.fourcc << "_"
  413. << getBackendNameSafe(param.api);
  414. return os.str();
  415. }
  416. INSTANTIATE_TEST_CASE_P(videoio, videoio_synthetic,
  417. testing::Combine(
  418. testing::ValuesIn(all_sizes),
  419. testing::ValuesIn(synthetic_params)),
  420. videoio_synthetic_name_printer);
  421. struct Ext_Fourcc_API
  422. {
  423. const char* ext;
  424. const char* fourcc;
  425. VideoCaptureAPIs api;
  426. };
  427. inline static std::ostream &operator<<(std::ostream &out, const Ext_Fourcc_API &p)
  428. {
  429. out << "(FOURCC(" << p.fourcc << "), \"" << p.ext << "\", " << p.api << ")"; return out;
  430. }
  431. class Videoio_Writer : public Videoio_Test_Base, public testing::TestWithParam<Ext_Fourcc_API>
  432. {
  433. protected:
  434. Size frame_size;
  435. int fourcc;
  436. double fps;
  437. public:
  438. Videoio_Writer()
  439. {
  440. frame_size = Size(640, 480);
  441. const Ext_Fourcc_API p = GetParam();
  442. ext = p.ext;
  443. fourcc = fourccFromString(p.fourcc);
  444. if (ext.size() == 3)
  445. video_file = cv::tempfile((fourccToString(fourcc) + "." + ext).c_str());
  446. else
  447. video_file = ext;
  448. fps = 25.;
  449. apiPref = p.api;
  450. }
  451. void SetUp()
  452. {
  453. }
  454. void TearDown()
  455. {
  456. if (ext.size() == 3)
  457. (void)remove(video_file.c_str());
  458. }
  459. };
  460. TEST_P(Videoio_Writer, write_nothing)
  461. {
  462. if (!cv::videoio_registry::hasBackend(apiPref))
  463. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  464. VideoWriter writer;
  465. EXPECT_NO_THROW(writer.open(video_file, apiPref, fourcc, fps, frame_size, true));
  466. ASSERT_TRUE(writer.isOpened());
  467. #if 0 // no frames
  468. cv::Mat m(frame_size, CV_8UC3, Scalar::all(127));
  469. writer << m;
  470. #endif
  471. EXPECT_NO_THROW(writer.release());
  472. }
  473. static vector<Ext_Fourcc_API> generate_Ext_Fourcc_API()
  474. {
  475. const size_t N = sizeof(synthetic_params)/sizeof(synthetic_params[0]);
  476. vector<Ext_Fourcc_API> result; result.reserve(N);
  477. for (size_t i = 0; i < N; i++)
  478. {
  479. const Ext_Fourcc_PSNR& src = synthetic_params[i];
  480. Ext_Fourcc_API e = { src.ext, src.fourcc, src.api };
  481. result.push_back(e);
  482. }
  483. {
  484. Ext_Fourcc_API e = { "appsrc ! videoconvert ! video/x-raw, format=(string)NV12 ! filesink location=test.nv12", "\0\0\0\0", CAP_GSTREAMER };
  485. result.push_back(e);
  486. }
  487. {
  488. Ext_Fourcc_API e = { "appsrc ! videoconvert ! video/x-raw, format=(string)I420 ! matroskamux ! filesink location=test.mkv", "\0\0\0\0", CAP_GSTREAMER };
  489. result.push_back(e);
  490. }
  491. return result;
  492. }
  493. inline static std::string Videoio_Writer_name_printer(const testing::TestParamInfo<Videoio_Writer::ParamType>& info)
  494. {
  495. std::ostringstream os;
  496. std::string ext(info.param.ext);
  497. if (info.param.api == CAP_GSTREAMER && info.param.fourcc[0] == '\0') // gstreamer pipeline instead of extension
  498. os << getExtensionSafe(info.param.ext) << "_" << "NONE";
  499. else
  500. os << info.param.ext << "_" << info.param.fourcc;
  501. os << "_" << getBackendNameSafe(info.param.api);
  502. return os.str();
  503. }
  504. INSTANTIATE_TEST_CASE_P(videoio, Videoio_Writer, testing::ValuesIn(generate_Ext_Fourcc_API()), Videoio_Writer_name_printer);
  505. TEST(Videoio, exceptions)
  506. {
  507. VideoCapture cap;
  508. Mat mat;
  509. EXPECT_FALSE(cap.grab());
  510. EXPECT_FALSE(cap.retrieve(mat));
  511. EXPECT_FALSE(cap.set(CAP_PROP_POS_FRAMES, 1));
  512. EXPECT_FALSE(cap.open("this_does_not_exist.avi", CAP_OPENCV_MJPEG));
  513. cap.setExceptionMode(true);
  514. EXPECT_THROW(cap.grab(), Exception);
  515. EXPECT_THROW(cap.retrieve(mat), Exception);
  516. EXPECT_THROW(cap.set(CAP_PROP_POS_FRAMES, 1), Exception);
  517. EXPECT_THROW(cap.open("this_does_not_exist.avi", CAP_OPENCV_MJPEG), Exception);
  518. }
  519. typedef Videoio_Writer Videoio_Writer_bad_fourcc;
  520. TEST_P(Videoio_Writer_bad_fourcc, nocrash)
  521. {
  522. if (!isBackendAvailable(apiPref, cv::videoio_registry::getStreamBackends()))
  523. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  524. VideoWriter writer;
  525. EXPECT_NO_THROW(writer.open(video_file, apiPref, fourcc, fps, frame_size, true));
  526. ASSERT_FALSE(writer.isOpened());
  527. EXPECT_NO_THROW(writer.release());
  528. }
  529. static vector<Ext_Fourcc_API> generate_Ext_Fourcc_API_nocrash()
  530. {
  531. static const Ext_Fourcc_API params[] = {
  532. #ifdef HAVE_MSMF_DISABLED // MSMF opens writer stream
  533. {"wmv", "aaaa", CAP_MSMF},
  534. {"mov", "aaaa", CAP_MSMF},
  535. #endif
  536. #ifdef HAVE_QUICKTIME
  537. {"mov", "aaaa", CAP_QT},
  538. {"avi", "aaaa", CAP_QT},
  539. {"mkv", "aaaa", CAP_QT},
  540. #endif
  541. #ifdef HAVE_AVFOUNDATION
  542. {"mov", "aaaa", CAP_AVFOUNDATION},
  543. {"mp4", "aaaa", CAP_AVFOUNDATION},
  544. {"m4v", "aaaa", CAP_AVFOUNDATION},
  545. #endif
  546. #ifdef HAVE_FFMPEG
  547. {"avi", "aaaa", CAP_FFMPEG},
  548. {"mkv", "aaaa", CAP_FFMPEG},
  549. #endif
  550. #ifdef HAVE_GSTREAMER
  551. {"avi", "aaaa", CAP_GSTREAMER},
  552. {"mkv", "aaaa", CAP_GSTREAMER},
  553. #endif
  554. {"avi", "aaaa", CAP_OPENCV_MJPEG},
  555. };
  556. const size_t N = sizeof(params)/sizeof(params[0]);
  557. vector<Ext_Fourcc_API> result; result.reserve(N);
  558. for (size_t i = 0; i < N; i++)
  559. {
  560. const Ext_Fourcc_API& src = params[i];
  561. Ext_Fourcc_API e = { src.ext, src.fourcc, src.api };
  562. result.push_back(e);
  563. }
  564. return result;
  565. }
  566. INSTANTIATE_TEST_CASE_P(videoio, Videoio_Writer_bad_fourcc, testing::ValuesIn(generate_Ext_Fourcc_API_nocrash()), Videoio_Writer_name_printer);
  567. typedef testing::TestWithParam<VideoCaptureAPIs> safe_capture;
  568. TEST_P(safe_capture, frames_independency)
  569. {
  570. VideoCaptureAPIs apiPref = GetParam();
  571. if (!videoio_registry::hasBackend(apiPref))
  572. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  573. VideoCapture cap;
  574. String video_file = BunnyParameters::getFilename(String(".avi"));
  575. EXPECT_NO_THROW(cap.open(video_file, apiPref));
  576. if (!cap.isOpened())
  577. {
  578. std::cout << "SKIP test: backend " << apiPref << " can't open the video: " << video_file << std::endl;
  579. return;
  580. }
  581. Mat frames[10];
  582. Mat hardCopies[10];
  583. for(int i = 0; i < 10; i++)
  584. {
  585. ASSERT_NO_THROW(cap >> frames[i]);
  586. EXPECT_FALSE(frames[i].empty());
  587. hardCopies[i] = frames[i].clone();
  588. }
  589. for(int i = 0; i < 10; i++)
  590. EXPECT_EQ(0, cv::norm(frames[i], hardCopies[i], NORM_INF)) << i;
  591. }
  592. static VideoCaptureAPIs safe_apis[] = {CAP_FFMPEG, CAP_GSTREAMER, CAP_MSMF,CAP_AVFOUNDATION};
  593. inline static std::string safe_capture_name_printer(const testing::TestParamInfo<safe_capture::ParamType>& info)
  594. {
  595. std::ostringstream os;
  596. os << getBackendNameSafe(info.param);
  597. return os.str();
  598. }
  599. INSTANTIATE_TEST_CASE_P(videoio, safe_capture, testing::ValuesIn(safe_apis), safe_capture_name_printer);
  600. //==================================================================================================
  601. // TEST_P(videocapture_acceleration, ...)
  602. struct VideoCaptureAccelerationInput
  603. {
  604. const char* filename;
  605. double psnr_threshold;
  606. };
  607. static inline
  608. std::ostream& operator<<(std::ostream& out, const VideoCaptureAccelerationInput& p)
  609. {
  610. out << p.filename;
  611. return out;
  612. }
  613. typedef testing::TestWithParam<tuple<VideoCaptureAccelerationInput, VideoCaptureAPIs, VideoAccelerationType, bool>> videocapture_acceleration;
  614. TEST_P(videocapture_acceleration, read)
  615. {
  616. auto param = GetParam();
  617. std::string filename = get<0>(param).filename;
  618. double psnr_threshold = get<0>(param).psnr_threshold;
  619. VideoCaptureAPIs backend = get<1>(param);
  620. VideoAccelerationType va_type = get<2>(param);
  621. bool use_umat = get<3>(param);
  622. const int frameNum = 15;
  623. std::string filepath = cvtest::findDataFile("video/" + filename);
  624. if (backend == CAP_MSMF && (
  625. filename == "sample_322x242_15frames.yuv420p.mjpeg.mp4" ||
  626. filename == "sample_322x242_15frames.yuv420p.libx265.mp4" ||
  627. filename == "sample_322x242_15frames.yuv420p.libaom-av1.mp4" ||
  628. filename == "sample_322x242_15frames.yuv420p.mpeg2video.mp4"
  629. ))
  630. throw SkipTestException("Format/codec is not supported");
  631. std::string backend_name = cv::videoio_registry::getBackendName(backend);
  632. if (!videoio_registry::hasBackend(backend))
  633. throw SkipTestException(cv::String("Backend is not available/disabled: ") + backend_name);
  634. // HW reader
  635. std::vector<int> params = { CAP_PROP_HW_ACCELERATION, static_cast<int>(va_type) };
  636. if (use_umat)
  637. {
  638. if (backend != CAP_FFMPEG)
  639. throw SkipTestException(cv::String("UMat/OpenCL mapping is not supported by current backend: ") + backend_name);
  640. if (!cv::videoio_registry::isBackendBuiltIn(backend))
  641. throw SkipTestException(cv::String("UMat/OpenCL mapping is not supported through plugins yet: ") + backend_name);
  642. params.push_back(CAP_PROP_HW_ACCELERATION_USE_OPENCL);
  643. params.push_back(1);
  644. }
  645. VideoCapture hw_reader(filepath, backend, params);
  646. if (!hw_reader.isOpened())
  647. {
  648. if (use_umat)
  649. {
  650. throw SkipTestException(backend_name + " VideoCapture on " + filename + " not supported with HW acceleration + OpenCL/Umat mapping, skipping");
  651. }
  652. else if (va_type == VIDEO_ACCELERATION_ANY || va_type == VIDEO_ACCELERATION_NONE)
  653. {
  654. // ANY HW acceleration should have fallback to SW codecs
  655. VideoCapture sw_reader(filepath, backend, {
  656. CAP_PROP_HW_ACCELERATION, VIDEO_ACCELERATION_NONE
  657. });
  658. if (!sw_reader.isOpened())
  659. throw SkipTestException(backend_name + " VideoCapture on " + filename + " not supported, skipping");
  660. ASSERT_TRUE(hw_reader.isOpened()) << "ANY HW acceleration should have fallback to SW codecs";
  661. }
  662. else
  663. {
  664. throw SkipTestException(backend_name + " VideoCapture on " + filename + " not supported with HW acceleration, skipping");
  665. }
  666. }
  667. VideoAccelerationType actual_va = static_cast<VideoAccelerationType>(static_cast<int>(hw_reader.get(CAP_PROP_HW_ACCELERATION)));
  668. if (va_type != VIDEO_ACCELERATION_ANY && va_type != VIDEO_ACCELERATION_NONE)
  669. {
  670. ASSERT_EQ((int)actual_va, (int)va_type) << "actual_va=" << actual_va << ", va_type=" << va_type;
  671. }
  672. std::cout << "VideoCapture " << backend_name << ":" << actual_va << std::endl << std::flush;
  673. double min_psnr_original = 1000;
  674. for (int i = 0; i < frameNum; i++)
  675. {
  676. SCOPED_TRACE(cv::format("frame=%d", i));
  677. Mat frame;
  678. if (use_umat)
  679. {
  680. UMat umat;
  681. bool read_umat_result = hw_reader.read(umat);
  682. if (!read_umat_result && i == 0)
  683. {
  684. if (filename == "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4")
  685. throw SkipTestException("Unable to read the first frame with VP9 codec (media stack misconfiguration / bug)");
  686. // FFMPEG: [av1 @ 0000027ac07d1340] Your platform doesn't support hardware accelerated AV1 decoding.
  687. if (filename == "sample_322x242_15frames.yuv420p.libaom-av1.mp4")
  688. throw SkipTestException("Unable to read the first frame with AV1 codec (missing support)");
  689. }
  690. #ifdef _WIN32
  691. if (!read_umat_result && i == 1)
  692. {
  693. if (filename == "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4")
  694. throw SkipTestException("Unable to read the second frame with VP9 codec (media stack misconfiguration / outdated MSMF version)");
  695. }
  696. #endif
  697. EXPECT_TRUE(read_umat_result);
  698. ASSERT_FALSE(umat.empty());
  699. umat.copyTo(frame);
  700. }
  701. else
  702. {
  703. bool read_result = hw_reader.read(frame);
  704. if (!read_result && i == 0)
  705. {
  706. if (filename == "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4")
  707. throw SkipTestException("Unable to read the first frame with VP9 codec (media stack misconfiguration / bug)");
  708. // FFMPEG: [av1 @ 0000027ac07d1340] Your platform doesn't support hardware accelerated AV1 decoding.
  709. if (filename == "sample_322x242_15frames.yuv420p.libaom-av1.mp4")
  710. throw SkipTestException("Unable to read the first frame with AV1 codec (missing support)");
  711. }
  712. #ifdef _WIN32
  713. if (!read_result && i == 1)
  714. {
  715. if (filename == "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4")
  716. throw SkipTestException("Unable to read the second frame with VP9 codec (media stack misconfiguration / outdated MSMF version)");
  717. }
  718. #endif
  719. EXPECT_TRUE(read_result);
  720. }
  721. ASSERT_FALSE(frame.empty());
  722. if (cvtest::debugLevel > 0)
  723. {
  724. imwrite(cv::format("test_frame%03d.png", i), frame);
  725. }
  726. Mat original(frame.size(), CV_8UC3, Scalar::all(0));
  727. generateFrame(i, frameNum, original);
  728. double psnr = cvtest::PSNR(frame, original);
  729. if (psnr < min_psnr_original)
  730. min_psnr_original = psnr;
  731. }
  732. std::ostringstream ss; ss << actual_va;
  733. std::string actual_va_str = ss.str();
  734. std::cout << "VideoCapture with acceleration = " << cv::format("%-6s @ %-10s", actual_va_str.c_str(), backend_name.c_str())
  735. << " on " << filename
  736. << " with PSNR-original = " << min_psnr_original
  737. << std::endl << std::flush;
  738. EXPECT_GE(min_psnr_original, psnr_threshold);
  739. }
  740. static const VideoCaptureAccelerationInput hw_filename[] = {
  741. { "sample_322x242_15frames.yuv420p.libxvid.mp4", 28.0 },
  742. { "sample_322x242_15frames.yuv420p.mjpeg.mp4", 20.0 },
  743. { "sample_322x242_15frames.yuv420p.mpeg2video.mp4", 24.0 }, // GSTREAMER on Ubuntu 18.04
  744. { "sample_322x242_15frames.yuv420p.libx264.mp4", 20.0 }, // 20 - D3D11 (i7-11800H), 23 - D3D11 on GHA/Windows, GSTREAMER on Ubuntu 18.04
  745. { "sample_322x242_15frames.yuv420p.libx265.mp4", 20.0 }, // 20 - D3D11 (i7-11800H), 23 - D3D11 on GHA/Windows
  746. { "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4", 30.0 },
  747. { "sample_322x242_15frames.yuv420p.libaom-av1.mp4", 30.0 }
  748. };
  749. static const VideoCaptureAPIs hw_backends[] = {
  750. CAP_FFMPEG,
  751. CAP_GSTREAMER,
  752. #ifdef _WIN32
  753. CAP_MSMF,
  754. #endif
  755. };
  756. static const VideoAccelerationType hw_types[] = {
  757. VIDEO_ACCELERATION_NONE,
  758. VIDEO_ACCELERATION_ANY,
  759. VIDEO_ACCELERATION_MFX,
  760. #ifdef _WIN32
  761. VIDEO_ACCELERATION_D3D11,
  762. #else
  763. VIDEO_ACCELERATION_VAAPI,
  764. VIDEO_ACCELERATION_DRM,
  765. #endif
  766. };
  767. static bool hw_use_umat[] = {
  768. false,
  769. true
  770. };
  771. inline static std::string videocapture_acceleration_name_printer(const testing::TestParamInfo<videocapture_acceleration::ParamType>& info)
  772. {
  773. std::ostringstream os;
  774. os << getExtensionSafe(get<0>(info.param).filename) << "_"
  775. << getBackendNameSafe(get<1>(info.param)) << "_"
  776. << get<2>(info.param) << "_"
  777. << (get<3>(info.param) ? "UMAT" : "MAT");
  778. return os.str();
  779. }
  780. INSTANTIATE_TEST_CASE_P(videoio, videocapture_acceleration, testing::Combine(
  781. testing::ValuesIn(hw_filename),
  782. testing::ValuesIn(hw_backends),
  783. testing::ValuesIn(hw_types),
  784. testing::ValuesIn(hw_use_umat)
  785. ), videocapture_acceleration_name_printer);
  786. ////////////////////////////////////////// TEST_P(video_acceleration, write_read)
  787. typedef tuple<Ext_Fourcc_PSNR, VideoAccelerationType, bool> VATestParams;
  788. typedef testing::TestWithParam<VATestParams> videowriter_acceleration;
  789. TEST_P(videowriter_acceleration, write)
  790. {
  791. auto param = GetParam();
  792. VideoCaptureAPIs backend = get<0>(param).api;
  793. std::string codecid = get<0>(param).fourcc;
  794. std::string extension = get<0>(param).ext;
  795. double psnr_threshold = get<0>(param).PSNR;
  796. VideoAccelerationType va_type = get<1>(param);
  797. bool use_umat = get<2>(param);
  798. std::string backend_name = cv::videoio_registry::getBackendName(backend);
  799. if (!videoio_registry::hasBackend(backend))
  800. throw SkipTestException(cv::String("Backend is not available/disabled: ") + backend_name);
  801. #ifdef __linux__
  802. if (cvtest::skipUnstableTests && backend == CAP_GSTREAMER &&
  803. (extension == "mkv") && (codecid == "MPEG"))
  804. {
  805. throw SkipTestException("Unstable GSTREAMER test");
  806. }
  807. #endif
  808. const Size sz(640, 480);
  809. const int frameNum = 15;
  810. const double fps = 25;
  811. std::string filename = tempfile("videowriter_acceleration.") + extension;
  812. // Write video
  813. VideoAccelerationType actual_va;
  814. {
  815. std::vector<int> params = { VIDEOWRITER_PROP_HW_ACCELERATION, static_cast<int>(va_type) };
  816. if (use_umat) {
  817. if (backend != CAP_FFMPEG)
  818. throw SkipTestException(cv::String("UMat/OpenCL mapping is not supported by current backend: ") + backend_name);
  819. if (!cv::videoio_registry::isBackendBuiltIn(backend))
  820. throw SkipTestException(cv::String("UMat/OpenCL mapping is not supported through plugins yet: ") + backend_name);
  821. params.push_back(VIDEOWRITER_PROP_HW_ACCELERATION_USE_OPENCL);
  822. params.push_back(1);
  823. }
  824. VideoWriter hw_writer(
  825. filename,
  826. backend,
  827. VideoWriter::fourcc(codecid[0], codecid[1], codecid[2], codecid[3]),
  828. fps,
  829. sz,
  830. params
  831. );
  832. if (!hw_writer.isOpened())
  833. {
  834. if (use_umat)
  835. {
  836. throw SkipTestException(backend_name + " VideoWriter on " + filename + " not supported with HW acceleration + OpenCL/Umat mapping, skipping");
  837. }
  838. else if (va_type == VIDEO_ACCELERATION_ANY || va_type == VIDEO_ACCELERATION_NONE)
  839. {
  840. // ANY HW acceleration should have fallback to SW codecs
  841. {
  842. VideoWriter sw_writer(
  843. filename,
  844. backend,
  845. VideoWriter::fourcc(codecid[0], codecid[1], codecid[2], codecid[3]),
  846. fps,
  847. sz,
  848. {
  849. VIDEOWRITER_PROP_HW_ACCELERATION, VIDEO_ACCELERATION_NONE,
  850. }
  851. );
  852. if (!sw_writer.isOpened()) {
  853. remove(filename.c_str());
  854. throw SkipTestException(backend_name + " VideoWriter on codec " + codecid + " not supported, skipping");
  855. }
  856. }
  857. remove(filename.c_str());
  858. ASSERT_TRUE(hw_writer.isOpened()) << "ANY HW acceleration should have fallback to SW codecs";
  859. } else {
  860. throw SkipTestException(backend_name + " VideoWriter on " + filename + " not supported with HW acceleration, skipping");
  861. }
  862. }
  863. actual_va = static_cast<VideoAccelerationType>(static_cast<int>(hw_writer.get(VIDEOWRITER_PROP_HW_ACCELERATION)));
  864. if (va_type != VIDEO_ACCELERATION_ANY && va_type != VIDEO_ACCELERATION_NONE)
  865. {
  866. ASSERT_EQ((int)actual_va, (int)va_type) << "actual_va=" << actual_va << ", va_type=" << va_type;
  867. }
  868. std::cout << "VideoWriter " << backend_name << ":" << actual_va << std::endl << std::flush;
  869. Mat frame(sz, CV_8UC3);
  870. for (int i = 0; i < frameNum; ++i) {
  871. generateFrame(i, frameNum, frame);
  872. if (use_umat) {
  873. UMat umat;
  874. frame.copyTo(umat);
  875. hw_writer.write(umat);
  876. }
  877. else {
  878. hw_writer.write(frame);
  879. }
  880. }
  881. }
  882. std::ifstream ofile(filename, std::ios::binary);
  883. ofile.seekg(0, std::ios::end);
  884. int64 fileSize = (int64)ofile.tellg();
  885. ASSERT_GT(fileSize, 0);
  886. std::cout << "File size: " << fileSize << std::endl;
  887. // Read video and check PSNR on every frame
  888. {
  889. VideoCapture reader(
  890. filename,
  891. CAP_ANY /*backend*/,
  892. { CAP_PROP_HW_ACCELERATION, VIDEO_ACCELERATION_NONE }
  893. );
  894. ASSERT_TRUE(reader.isOpened());
  895. double min_psnr = 1000;
  896. Mat reference(sz, CV_8UC3);
  897. for (int i = 0; i < frameNum; ++i) {
  898. Mat actual;
  899. if (use_umat) {
  900. UMat umat;
  901. EXPECT_TRUE(reader.read(umat));
  902. umat.copyTo(actual);
  903. }
  904. else {
  905. EXPECT_TRUE(reader.read(actual));
  906. }
  907. EXPECT_FALSE(actual.empty());
  908. generateFrame(i, frameNum, reference);
  909. EXPECT_EQ(reference.size(), actual.size());
  910. EXPECT_EQ(reference.depth(), actual.depth());
  911. EXPECT_EQ(reference.channels(), actual.channels());
  912. double psnr = cvtest::PSNR(actual, reference);
  913. EXPECT_GE(psnr, psnr_threshold) << " frame " << i;
  914. if (psnr < min_psnr)
  915. min_psnr = psnr;
  916. }
  917. Mat actual;
  918. EXPECT_FALSE(reader.read(actual));
  919. {
  920. std::ostringstream ss; ss << actual_va;
  921. std::string actual_va_str = ss.str();
  922. std::cout << "VideoWriter with acceleration = " << cv::format("%-6s @ %-10s", actual_va_str.c_str(), backend_name.c_str())
  923. << " on codec=" << codecid << " (." << extension << ")"
  924. << ", bitrate = " << fileSize / (frameNum / fps)
  925. << ", with PSNR-original = " << min_psnr
  926. << std::endl << std::flush;
  927. }
  928. remove(filename.c_str());
  929. }
  930. }
  931. static Ext_Fourcc_PSNR hw_codecs[] = {
  932. {"mp4", "MPEG", 29.f, CAP_FFMPEG},
  933. {"mp4", "H264", 29.f, CAP_FFMPEG},
  934. {"mp4", "HEVC", 29.f, CAP_FFMPEG},
  935. {"avi", "MJPG", 29.f, CAP_FFMPEG},
  936. {"avi", "XVID", 29.f, CAP_FFMPEG},
  937. //{"webm", "VP8", 29.f, CAP_FFMPEG},
  938. //{"webm", "VP9", 29.f, CAP_FFMPEG},
  939. {"mkv", "MPEG", 29.f, CAP_GSTREAMER},
  940. {"mkv", "H264", 29.f, CAP_GSTREAMER},
  941. #ifdef _WIN32
  942. {"mp4", "MPEG", 29.f, CAP_MSMF},
  943. {"mp4", "H264", 29.f, CAP_MSMF},
  944. {"mp4", "HEVC", 29.f, CAP_MSMF},
  945. #endif
  946. };
  947. inline static std::string videowriter_acceleration_name_printer(const testing::TestParamInfo<videowriter_acceleration::ParamType>& info)
  948. {
  949. std::ostringstream os;
  950. const Ext_Fourcc_PSNR & param = get<0>(info.param);
  951. os << extToStringSafe(param.ext) << "_"
  952. << param.fourcc << "_"
  953. << getBackendNameSafe(param.api) << "_"
  954. << get<1>(info.param) << "_"
  955. << (get<2>(info.param) ? "UMAT" : "MAT");
  956. return os.str();
  957. }
  958. INSTANTIATE_TEST_CASE_P(videoio, videowriter_acceleration, testing::Combine(
  959. testing::ValuesIn(hw_codecs),
  960. testing::ValuesIn(hw_types),
  961. testing::ValuesIn(hw_use_umat)
  962. ), videowriter_acceleration_name_printer);
  963. class BufferStream : public cv::IStreamReader
  964. {
  965. public:
  966. BufferStream(const std::string& filename)
  967. {
  968. Ptr<std::filebuf> file = makePtr<std::filebuf>();
  969. file->open(filename.c_str(), std::ios::in | std::ios::binary);
  970. stream = file;
  971. }
  972. BufferStream(const Ptr<std::stringbuf>& _stream) : stream(_stream) {}
  973. long long read(char* buffer, long long size) CV_OVERRIDE
  974. {
  975. auto result = stream->sgetn(buffer, size);
  976. return result;
  977. }
  978. long long seek(long long offset, int way) CV_OVERRIDE
  979. {
  980. auto result = stream->pubseekoff(offset, way == SEEK_SET ? std::ios_base::beg : (way == SEEK_END ? std::ios_base::end : std::ios_base::cur));
  981. return result;
  982. }
  983. private:
  984. Ptr<std::streambuf> stream;
  985. };
  986. typedef testing::TestWithParam<tuple<std::string, VideoCaptureAPIs>> stream_capture;
  987. TEST_P(stream_capture, read)
  988. {
  989. std::string ext = get<0>(GetParam());
  990. VideoCaptureAPIs apiPref = get<1>(GetParam());
  991. std::vector<VideoCaptureAPIs> supportedAPIs = videoio_registry::getStreamBufferedBackends();
  992. if (!videoio_registry::hasBackend(apiPref))
  993. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  994. if (std::find(supportedAPIs.begin(), supportedAPIs.end(), apiPref) == supportedAPIs.end())
  995. throw SkipTestException(cv::String("Backend is not supported: ") + cv::videoio_registry::getBackendName(apiPref));
  996. if (cvtest::skipUnstableTests && apiPref == CAP_MSMF && (ext == "h264" || ext == "h265" || ext == "mpg"))
  997. throw SkipTestException("Unstable MSMF test");
  998. if (!videoio_registry::isBackendBuiltIn(apiPref))
  999. {
  1000. int pluginABI, pluginAPI;
  1001. videoio_registry::getStreamBufferedBackendPluginVersion(apiPref, pluginABI, pluginAPI);
  1002. if (pluginABI < 1 || (pluginABI == 1 && pluginAPI < 2))
  1003. throw SkipTestException(format("Buffer capture supported since ABI/API = 1/2. %s plugin is %d/%d",
  1004. cv::videoio_registry::getBackendName(apiPref).c_str(), pluginABI, pluginAPI));
  1005. }
  1006. VideoCapture cap;
  1007. String video_file = BunnyParameters::getFilename(String(".") + ext);
  1008. ASSERT_TRUE(utils::fs::exists(video_file));
  1009. EXPECT_NO_THROW(cap.open(makePtr<BufferStream>(video_file), apiPref, {}));
  1010. ASSERT_TRUE(cap.isOpened());
  1011. const int numFrames = 10;
  1012. Mat frames[numFrames];
  1013. Mat hardCopies[numFrames];
  1014. for(int i = 0; i < numFrames; i++)
  1015. {
  1016. ASSERT_NO_THROW(cap >> frames[i]);
  1017. EXPECT_FALSE(frames[i].empty());
  1018. hardCopies[i] = frames[i].clone();
  1019. }
  1020. for(int i = 0; i < numFrames; i++)
  1021. EXPECT_EQ(0, cv::norm(frames[i], hardCopies[i], NORM_INF)) << i;
  1022. }
  1023. inline static std::string stream_capture_name_printer(const testing::TestParamInfo<stream_capture::ParamType>& info)
  1024. {
  1025. std::ostringstream os;
  1026. os << extToStringSafe(get<0>(info.param)) << "_"
  1027. << getBackendNameSafe(get<1>(info.param));
  1028. return os.str();
  1029. }
  1030. INSTANTIATE_TEST_CASE_P(videoio, stream_capture,
  1031. testing::Combine(
  1032. testing::ValuesIn(bunny_params),
  1033. testing::ValuesIn(backend_params)),
  1034. stream_capture_name_printer);
  1035. // This test for stream input for container format (See test_ffmpeg/videoio_container.read test)
  1036. typedef testing::TestWithParam<std::string> stream_capture_ffmpeg;
  1037. TEST_P(stream_capture_ffmpeg, raw)
  1038. {
  1039. std::string ext = GetParam();
  1040. VideoCaptureAPIs apiPref = CAP_FFMPEG;
  1041. std::vector<VideoCaptureAPIs> supportedAPIs = videoio_registry::getStreamBufferedBackends();
  1042. if (!videoio_registry::hasBackend(apiPref))
  1043. throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
  1044. if (std::find(supportedAPIs.begin(), supportedAPIs.end(), apiPref) == supportedAPIs.end())
  1045. throw SkipTestException(cv::String("Backend is not supported: ") + cv::videoio_registry::getBackendName(apiPref));
  1046. if (!videoio_registry::isBackendBuiltIn(apiPref))
  1047. {
  1048. int pluginABI, pluginAPI;
  1049. videoio_registry::getStreamBufferedBackendPluginVersion(apiPref, pluginABI, pluginAPI);
  1050. if (pluginABI < 1 || (pluginABI == 1 && pluginAPI < 2))
  1051. throw SkipTestException(format("Buffer capture supported since ABI/API = 1/2. %s plugin is %d/%d",
  1052. cv::videoio_registry::getBackendName(apiPref).c_str(), pluginABI, pluginAPI));
  1053. }
  1054. VideoCapture container;
  1055. String video_file = BunnyParameters::getFilename(String(".") + ext);
  1056. ASSERT_TRUE(utils::fs::exists(video_file));
  1057. EXPECT_NO_THROW(container.open(video_file, apiPref, {CAP_PROP_FORMAT, -1}));
  1058. ASSERT_TRUE(container.isOpened());
  1059. ASSERT_EQ(-1.f, container.get(CAP_PROP_FORMAT));
  1060. auto stream = std::make_shared<std::stringbuf>();
  1061. Mat keyFrame;
  1062. while (true)
  1063. {
  1064. container >> keyFrame;
  1065. if (keyFrame.empty())
  1066. break;
  1067. stream->sputn(keyFrame.ptr<char>(), keyFrame.total());
  1068. }
  1069. VideoCapture capRef(video_file);
  1070. VideoCapture capStream;
  1071. EXPECT_NO_THROW(capStream.open(makePtr<BufferStream>(stream), apiPref, {}));
  1072. ASSERT_TRUE(capStream.isOpened());
  1073. const int numFrames = 10;
  1074. Mat frameRef, frame;
  1075. for (int i = 0; i < numFrames; ++i)
  1076. {
  1077. capRef >> frameRef;
  1078. ASSERT_NO_THROW(capStream >> frame);
  1079. EXPECT_FALSE(frame.empty());
  1080. EXPECT_EQ(0, cv::norm(frame, frameRef, NORM_INF)) << i;
  1081. }
  1082. }
  1083. inline static std::string stream_capture_ffmpeg_name_printer(const testing::TestParamInfo<stream_capture_ffmpeg::ParamType>& info)
  1084. {
  1085. std::ostringstream os;
  1086. os << extToStringSafe(info.param);
  1087. return os.str();
  1088. }
  1089. INSTANTIATE_TEST_CASE_P(videoio, stream_capture_ffmpeg, testing::Values("h264", "h265", "mjpg.avi"), stream_capture_ffmpeg_name_printer);
  1090. } // namespace