test_ffmpeg.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  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. using namespace std;
  6. namespace opencv_test { namespace {
  7. static inline long long getFileSize(const string &filename)
  8. {
  9. ifstream f(filename, ios_base::in | ios_base::binary);
  10. f.seekg(0, ios_base::end);
  11. return f.tellg();
  12. }
  13. typedef tuple<string, string, Size> FourCC_Ext_Size;
  14. typedef testing::TestWithParam< FourCC_Ext_Size > videoio_ffmpeg;
  15. TEST_P(videoio_ffmpeg, write_big)
  16. {
  17. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  18. throw SkipTestException("FFmpeg backend was not found");
  19. const string fourcc = get<0>(GetParam());
  20. const string ext = get<1>(GetParam());
  21. const Size sz = get<2>(GetParam());
  22. const double time_sec = 1;
  23. const double fps = 25;
  24. ostringstream buf;
  25. buf << "write_big_" << fourcc << "." << ext;
  26. const string filename = tempfile(buf.str().c_str());
  27. VideoWriter writer(filename, CAP_FFMPEG, fourccFromString(fourcc), fps, sz);
  28. if (ext == "mp4" && fourcc == "H264" && !writer.isOpened())
  29. {
  30. throw cvtest::SkipTestException("H264/mp4 codec is not supported - SKIP");
  31. }
  32. ASSERT_TRUE(writer.isOpened());
  33. Mat img(sz, CV_8UC3, Scalar::all(0));
  34. const int coeff = cvRound(min(sz.width, sz.height)/(fps * time_sec));
  35. for (int i = 0 ; i < static_cast<int>(fps * time_sec); i++ )
  36. {
  37. rectangle(img,
  38. Point2i(coeff * i, coeff * i),
  39. Point2i(coeff * (i + 1), coeff * (i + 1)),
  40. Scalar::all(255 * (1.0 - static_cast<double>(i) / (fps * time_sec * 2))),
  41. -1);
  42. writer << img;
  43. }
  44. writer.release();
  45. EXPECT_GT(getFileSize(filename), 8192);
  46. remove(filename.c_str());
  47. }
  48. #if defined(OPENCV_32BIT_CONFIGURATION)
  49. static const Size bigSize(1920, 1080);
  50. #else
  51. static const Size bigSize(4096, 4096);
  52. #endif
  53. const FourCC_Ext_Size entries[] =
  54. {
  55. make_tuple("", "avi", bigSize),
  56. make_tuple("DX50", "avi", bigSize),
  57. make_tuple("FLV1", "avi", bigSize),
  58. make_tuple("H261", "avi", Size(352, 288)),
  59. make_tuple("H263", "avi", Size(704, 576)),
  60. make_tuple("I420", "avi", bigSize),
  61. make_tuple("MJPG", "avi", bigSize),
  62. make_tuple("mp4v", "avi", bigSize),
  63. make_tuple("MPEG", "avi", Size(720, 576)),
  64. make_tuple("XVID", "avi", bigSize),
  65. make_tuple("H264", "mp4", Size(4096, 2160)),
  66. make_tuple("FFV1", "avi", bigSize),
  67. make_tuple("FFV1", "mkv", bigSize)
  68. };
  69. inline static std::string videoio_ffmpeg_name_printer(const testing::TestParamInfo<videoio_ffmpeg::ParamType>& info)
  70. {
  71. std::ostringstream os;
  72. const string & fourcc = get<0>(info.param);
  73. const Size sz = get<2>(info.param);
  74. os << (fourcc.size() == 0 ? "NONE" : fourcc) << "_" << get<1>(info.param) << "_" << sz.height << "p";
  75. return os.str();
  76. }
  77. INSTANTIATE_TEST_CASE_P(videoio, videoio_ffmpeg, testing::ValuesIn(entries), videoio_ffmpeg_name_printer);
  78. //==========================================================================
  79. TEST(videoio_ffmpeg, image)
  80. {
  81. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  82. throw SkipTestException("FFmpeg backend was not found");
  83. const string filename = findDataFile("readwrite/ordinary.bmp");
  84. Mat image = imread(filename, IMREAD_COLOR);
  85. ASSERT_FALSE(image.empty());
  86. VideoCapture cap(filename, CAP_FFMPEG);
  87. ASSERT_TRUE(cap.isOpened());
  88. Mat frame1, frame2;
  89. cap >> frame1 >> frame2;
  90. ASSERT_FALSE(frame1.empty());
  91. ASSERT_TRUE(frame2.empty());
  92. ASSERT_EQ(0, cvtest::norm(image, frame1, NORM_INF));
  93. }
  94. //==========================================================================
  95. typedef tuple<string, int, bool> videoio_read_params_t;
  96. typedef testing::TestWithParam< testing::tuple<videoio_read_params_t, int, bool>> videoio_read;
  97. TEST_P(videoio_read, threads)
  98. {
  99. const VideoCaptureAPIs api = CAP_FFMPEG;
  100. if (!videoio_registry::hasBackend(api))
  101. throw SkipTestException("Backend was not found");
  102. const string fileName = get<0>(get<0>(GetParam()));
  103. const int nFrames = get<1>(get<0>(GetParam()));
  104. const bool fixedThreadCount = get<2>(get<0>(GetParam()));
  105. const int nThreads = get<1>(GetParam());
  106. const bool rawRead = get<2>(GetParam());
  107. VideoCapture cap(findDataFile(fileName), api, { CAP_PROP_N_THREADS, nThreads });
  108. if (!cap.isOpened())
  109. throw SkipTestException("Video stream is not supported");
  110. if (nThreads == 0 || fixedThreadCount)
  111. EXPECT_EQ(cap.get(CAP_PROP_N_THREADS), VideoCapture(findDataFile(fileName), api).get(CAP_PROP_N_THREADS));
  112. else
  113. EXPECT_EQ(cap.get(CAP_PROP_N_THREADS), nThreads);
  114. if (rawRead && !cap.set(CAP_PROP_FORMAT, -1)) // turn off video decoder (extract stream)
  115. throw SkipTestException("Fetching of RAW video streams is not supported");
  116. Mat frame;
  117. int n = 0;
  118. while (cap.read(frame)) {
  119. ASSERT_FALSE(frame.empty());
  120. n++;
  121. }
  122. ASSERT_EQ(n, nFrames);
  123. }
  124. const videoio_read_params_t videoio_read_params[] =
  125. {
  126. videoio_read_params_t("video/big_buck_bunny.h264", 125, false),
  127. //videoio_read_params_t("video/big_buck_bunny.h265", 125, false),
  128. videoio_read_params_t("video/big_buck_bunny.mjpg.avi", 125, true),
  129. //videoio_read_params_t("video/big_buck_bunny.mov", 125, false),
  130. //videoio_read_params_t("video/big_buck_bunny.mp4", 125, false),
  131. //videoio_read_params_t("video/big_buck_bunny.mpg", 125, false),
  132. //videoio_read_params_t("video/big_buck_bunny.wmv", 125, true),
  133. };
  134. inline static std::string videoio_read_name_printer(const testing::TestParamInfo<videoio_read::ParamType>& info)
  135. {
  136. std::ostringstream out;
  137. out << getExtensionSafe(get<0>(get<0>(info.param))) << "_"
  138. << get<1>(info.param) << "_"
  139. << (get<2>(info.param) ? "RAW" : "ENC");
  140. return out.str();
  141. }
  142. INSTANTIATE_TEST_CASE_P(/**/, videoio_read, testing::Combine(testing::ValuesIn(videoio_read_params),
  143. testing::Values(0, 1, 2, 50),
  144. testing::Values(true, false)),
  145. videoio_read_name_printer);
  146. //==========================================================================
  147. typedef tuple<VideoCaptureAPIs, string, string, string, string, string> videoio_container_params_t;
  148. typedef testing::TestWithParam< videoio_container_params_t > videoio_container;
  149. TEST_P(videoio_container, read)
  150. {
  151. const VideoCaptureAPIs api = get<0>(GetParam());
  152. if (!videoio_registry::hasBackend(api))
  153. throw SkipTestException("Backend was not found");
  154. const string path = get<1>(GetParam());
  155. const string ext = get<2>(GetParam());
  156. const string ext_raw = get<3>(GetParam());
  157. const string codec = get<4>(GetParam());
  158. const string pixelFormat = get<5>(GetParam());
  159. const string fileName = path + "." + ext;
  160. const string fileNameOut = tempfile(cv::format("test_container_stream.%s", ext_raw.c_str()).c_str());
  161. // Write encoded video read using VideoContainer to tmp file
  162. size_t totalBytes = 0;
  163. {
  164. VideoCapture container(findDataFile(fileName), api);
  165. if (!container.isOpened())
  166. throw SkipTestException("Video stream is not supported");
  167. if (!container.set(CAP_PROP_FORMAT, -1)) // turn off video decoder (extract stream)
  168. throw SkipTestException("Fetching of RAW video streams is not supported");
  169. ASSERT_EQ(-1.f, container.get(CAP_PROP_FORMAT)); // check
  170. EXPECT_EQ(codec, fourccToString((int)container.get(CAP_PROP_FOURCC)));
  171. EXPECT_EQ(pixelFormat, fourccToString((int)container.get(CAP_PROP_CODEC_PIXEL_FORMAT)));
  172. std::ofstream file(fileNameOut.c_str(), ios::out | ios::trunc | std::ios::binary);
  173. Mat raw_data;
  174. while (true)
  175. {
  176. container >> raw_data;
  177. size_t size = raw_data.total();
  178. if (raw_data.empty())
  179. break;
  180. ASSERT_EQ(CV_8UC1, raw_data.type());
  181. ASSERT_LE(raw_data.dims, 2);
  182. ASSERT_EQ(raw_data.rows, 1);
  183. ASSERT_EQ((size_t)raw_data.cols, raw_data.total());
  184. ASSERT_TRUE(raw_data.isContinuous());
  185. totalBytes += size;
  186. file.write(reinterpret_cast<char*>(raw_data.data), size);
  187. ASSERT_FALSE(file.fail());
  188. }
  189. ASSERT_GE(totalBytes, (size_t)39775) << "Encoded stream is too small";
  190. }
  191. std::cout << "Checking extracted video stream: " << fileNameOut << " (size: " << totalBytes << " bytes)" << std::endl;
  192. // Check decoded frames read from original media are equal to frames decoded from tmp file
  193. {
  194. VideoCapture capReference(findDataFile(fileName), api);
  195. ASSERT_TRUE(capReference.isOpened());
  196. VideoCapture capActual(fileNameOut.c_str(), api);
  197. ASSERT_TRUE(capActual.isOpened());
  198. Mat reference, actual;
  199. int nframes = 0, n_err = 0;
  200. while (capReference.read(reference) && n_err < 3)
  201. {
  202. nframes++;
  203. ASSERT_TRUE(capActual.read(actual)) << nframes;
  204. EXPECT_EQ(0, cvtest::norm(actual, reference, NORM_INF)) << "frame=" << nframes << " err=" << ++n_err;
  205. }
  206. ASSERT_GT(nframes, 0);
  207. }
  208. ASSERT_EQ(0, remove(fileNameOut.c_str()));
  209. }
  210. const videoio_container_params_t videoio_container_params[] =
  211. {
  212. videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h264", "h264", "h264", "I420"),
  213. videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h265", "h265", "hevc", "I420"),
  214. videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "mjpg.avi", "mjpg", "MJPG", "I420"),
  215. videoio_container_params_t(CAP_FFMPEG, "video/sample_322x242_15frames.yuv420p.libx264", "mp4", "h264", "h264", "I420")
  216. //videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h264.mkv", "mkv.h264", "h264", "I420"),
  217. //videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h265.mkv", "mkv.h265", "hevc", "I420"),
  218. //videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h264.mp4", "mp4.avc1", "avc1", "I420"),
  219. //videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h265.mp4", "mp4.hev1", "hev1", "I420"),
  220. };
  221. INSTANTIATE_TEST_CASE_P(/**/, videoio_container, testing::ValuesIn(videoio_container_params));
  222. typedef tuple<VideoCaptureAPIs, string, int, int, int, int, int> videoio_container_get_params_t;
  223. typedef testing::TestWithParam<videoio_container_get_params_t > videoio_container_get;
  224. TEST_P(videoio_container_get, read)
  225. {
  226. const VideoCaptureAPIs api = get<0>(GetParam());
  227. if (!videoio_registry::hasBackend(api))
  228. throw SkipTestException("Backend was not found");
  229. const string fileName = get<1>(GetParam());
  230. const int height = get<2>(GetParam());
  231. const int width = get<3>(GetParam());
  232. const int nFrames = get<4>(GetParam());
  233. const int bitrate = get<5>(GetParam());
  234. const int fps = get<6>(GetParam());
  235. VideoCapture container(findDataFile(fileName), api, { CAP_PROP_FORMAT, -1 });
  236. if (!container.isOpened())
  237. throw SkipTestException("Video stream is not supported");
  238. const int heightProp = static_cast<int>(container.get(CAP_PROP_FRAME_HEIGHT));
  239. ASSERT_EQ(height, heightProp);
  240. const int widthProp = static_cast<int>(container.get(CAP_PROP_FRAME_WIDTH));
  241. ASSERT_EQ(width, widthProp);
  242. const int nFramesProp = static_cast<int>(container.get(CAP_PROP_FRAME_COUNT));
  243. ASSERT_EQ(nFrames, nFramesProp);
  244. const int bitrateProp = static_cast<int>(container.get(CAP_PROP_BITRATE));
  245. ASSERT_EQ(bitrate, bitrateProp);
  246. const double fpsProp = container.get(CAP_PROP_FPS);
  247. ASSERT_EQ(fps, fpsProp);
  248. vector<int> displayTimeMs;
  249. int iFrame = 1;
  250. while (container.grab()) {
  251. displayTimeMs.push_back(static_cast<int>(container.get(CAP_PROP_POS_MSEC)));
  252. const int iFrameProp = static_cast<int>(container.get(CAP_PROP_POS_FRAMES));
  253. ASSERT_EQ(iFrame++, iFrameProp);
  254. }
  255. sort(displayTimeMs.begin(), displayTimeMs.end());
  256. vector<int> displayTimeDiffMs(displayTimeMs.size());
  257. std::adjacent_difference(displayTimeMs.begin(), displayTimeMs.end(), displayTimeDiffMs.begin());
  258. auto minTimeMsIt = min_element(displayTimeDiffMs.begin() + 1, displayTimeDiffMs.end());
  259. auto maxTimeMsIt = max_element(displayTimeDiffMs.begin() + 1, displayTimeDiffMs.end());
  260. const int frameTimeMs = static_cast<int>(1000.0 / fps);
  261. ASSERT_NEAR(frameTimeMs, *minTimeMsIt, 1);
  262. ASSERT_NEAR(frameTimeMs, *maxTimeMsIt, 1);
  263. }
  264. const videoio_container_get_params_t videoio_container_get_params[] =
  265. {
  266. videoio_container_get_params_t(CAP_FFMPEG, "video/big_buck_bunny.mp4", 384, 672, 125, 483, 24),
  267. videoio_container_get_params_t(CAP_FFMPEG, "video/big_buck_bunny.mjpg.avi", 384, 672, 125, 2713, 24),
  268. videoio_container_get_params_t(CAP_FFMPEG, "video/sample_322x242_15frames.yuv420p.libx264.mp4", 242, 322, 15, 542, 25)
  269. };
  270. INSTANTIATE_TEST_CASE_P(/**/, videoio_container_get, testing::ValuesIn(videoio_container_get_params));
  271. typedef tuple<string, string, int, int, bool, bool> videoio_encapsulate_params_t;
  272. typedef testing::TestWithParam< videoio_encapsulate_params_t > videoio_encapsulate;
  273. TEST_P(videoio_encapsulate, write)
  274. {
  275. const VideoCaptureAPIs api = CAP_FFMPEG;
  276. if (!videoio_registry::hasBackend(api))
  277. throw SkipTestException("FFmpeg backend was not found");
  278. const string fileName = findDataFile(get<0>(GetParam()));
  279. const string ext = get<1>(GetParam());
  280. const int idrPeriod = get<2>(GetParam());
  281. const int nFrames = get<3>(GetParam());
  282. const string fileNameOut = tempfile(cv::format("test_encapsulated_stream.%s", ext.c_str()).c_str());
  283. const bool setPts = get<4>(GetParam());
  284. const bool tsWorking = get<5>(GetParam());
  285. // Use VideoWriter to encapsulate encoded video read with VideoReader
  286. {
  287. VideoCapture capRaw(fileName, api, { CAP_PROP_FORMAT, -1 });
  288. ASSERT_TRUE(capRaw.isOpened());
  289. const int width = static_cast<int>(capRaw.get(CAP_PROP_FRAME_WIDTH));
  290. const int height = static_cast<int>(capRaw.get(CAP_PROP_FRAME_HEIGHT));
  291. const double fps = capRaw.get(CAP_PROP_FPS);
  292. const int codecExtradataIndex = static_cast<int>(capRaw.get(CAP_PROP_CODEC_EXTRADATA_INDEX));
  293. Mat extraData;
  294. capRaw.retrieve(extraData, codecExtradataIndex);
  295. const int fourcc = static_cast<int>(capRaw.get(CAP_PROP_FOURCC));
  296. const bool mpeg4 = (fourcc == fourccFromString("FMP4"));
  297. VideoWriter container(fileNameOut, api, fourcc, fps, { width, height }, { VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO, 1, VideoWriterProperties::VIDEOWRITER_PROP_KEY_INTERVAL, idrPeriod });
  298. ASSERT_TRUE(container.isOpened());
  299. Mat rawFrame;
  300. for (int i = 0; i < nFrames; i++) {
  301. ASSERT_TRUE(capRaw.read(rawFrame));
  302. if (setPts && i == 0) {
  303. double dts = capRaw.get(CAP_PROP_DTS_DELAY);
  304. ASSERT_TRUE(container.set(VIDEOWRITER_PROP_DTS_DELAY, dts)) << "dts=" << dts;
  305. }
  306. ASSERT_FALSE(rawFrame.empty());
  307. if (i == 0 && mpeg4) {
  308. Mat tmp = rawFrame.clone();
  309. const size_t newSzt = tmp.total() + extraData.total();
  310. const int newSz = static_cast<int>(newSzt);
  311. ASSERT_TRUE(newSzt == static_cast<size_t>(newSz));
  312. rawFrame = Mat(1, newSz, CV_8UC1);
  313. memcpy(rawFrame.data, extraData.data, extraData.total());
  314. memcpy(rawFrame.data + extraData.total(), tmp.data, tmp.total());
  315. }
  316. if (setPts) {
  317. double pts = capRaw.get(CAP_PROP_PTS);
  318. ASSERT_TRUE(container.set(VIDEOWRITER_PROP_PTS, pts)) << "pts=" << pts;
  319. }
  320. container.write(rawFrame);
  321. }
  322. container.release();
  323. }
  324. std::cout << "Checking encapsulated video container: " << fileNameOut << std::endl;
  325. // Check encapsulated video container is "identical" to the original
  326. {
  327. VideoCapture capReference(fileName), capActual(fileNameOut), capActualRaw(fileNameOut, api, { CAP_PROP_FORMAT, -1 });
  328. ASSERT_TRUE(capReference.isOpened());
  329. ASSERT_TRUE(capActual.isOpened());
  330. ASSERT_TRUE(capActualRaw.isOpened());
  331. const double fpsReference = capReference.get(CAP_PROP_FPS);
  332. const double fpsActual = capActual.get(CAP_PROP_FPS);
  333. ASSERT_NEAR(fpsReference, fpsActual, 1e-2);
  334. const int nFramesActual = static_cast<int>(capActual.get(CAP_PROP_FRAME_COUNT));
  335. ASSERT_EQ(nFrames, nFramesActual);
  336. Mat reference, actual;
  337. for (int i = 0; i < nFrames; i++) {
  338. ASSERT_TRUE(capReference.read(reference));
  339. ASSERT_FALSE(reference.empty());
  340. ASSERT_TRUE(capActual.read(actual));
  341. ASSERT_FALSE(actual.empty());
  342. ASSERT_EQ(0, cvtest::norm(reference, actual, NORM_INF));
  343. ASSERT_TRUE(capActualRaw.grab());
  344. const bool keyFrameActual = capActualRaw.get(CAP_PROP_LRF_HAS_KEY_FRAME) == 1.;
  345. const bool keyFrameReference = idrPeriod ? i % idrPeriod == 0 : 1;
  346. ASSERT_EQ(keyFrameReference, keyFrameActual);
  347. if (tsWorking) {
  348. ASSERT_EQ(round(capReference.get(CAP_PROP_POS_MSEC)), round(capActual.get(CAP_PROP_POS_MSEC)));
  349. }
  350. }
  351. }
  352. ASSERT_EQ(0, remove(fileNameOut.c_str()));
  353. }
  354. const videoio_encapsulate_params_t videoio_encapsulate_params[] =
  355. {
  356. videoio_encapsulate_params_t("video/big_buck_bunny.h264", "avi", 125, 125, false, false), // tsWorking = false: no timestamp information
  357. videoio_encapsulate_params_t("video/big_buck_bunny.h265", "mp4", 125, 125, false, false), // tsWorking = false: no timestamp information
  358. videoio_encapsulate_params_t("video/big_buck_bunny.wmv", "wmv", 12, 13, false, true),
  359. videoio_encapsulate_params_t("video/big_buck_bunny.mp4", "mp4", 12, 13, false, true),
  360. videoio_encapsulate_params_t("video/big_buck_bunny.mjpg.avi", "mp4", 0, 4, false, true),
  361. videoio_encapsulate_params_t("video/big_buck_bunny.mov", "mp4", 12, 13, false, true),
  362. videoio_encapsulate_params_t("video/big_buck_bunny.avi", "mp4", 125, 125, false, false), // tsWorking = false: PTS not available for all frames
  363. videoio_encapsulate_params_t("video/big_buck_bunny.mpg", "mp4", 12, 13, true, true),
  364. videoio_encapsulate_params_t("video/VID00003-20100701-2204.wmv", "wmv", 12, 13, false, true),
  365. videoio_encapsulate_params_t("video/VID00003-20100701-2204.mpg", "mp4", 12, 13, false, false), // tsWorking = false: PTS not available for all frames
  366. videoio_encapsulate_params_t("video/VID00003-20100701-2204.avi", "mp4", 12, 13, false, false), // tsWorking = false: Unable to correctly set PTS when writing
  367. videoio_encapsulate_params_t("video/VID00003-20100701-2204.3GP", "mp4", 51, 52, false, false), // tsWorking = false: Source with variable fps
  368. videoio_encapsulate_params_t("video/sample_sorenson.avi", "mp4", 12, 13, false, true),
  369. videoio_encapsulate_params_t("video/sample_322x242_15frames.yuv420p.libxvid.mp4", "mp4", 3, 4, false, true),
  370. videoio_encapsulate_params_t("video/sample_322x242_15frames.yuv420p.mpeg2video.mp4", "mpg", 12, 13, false, true),
  371. videoio_encapsulate_params_t("video/sample_322x242_15frames.yuv420p.mjpeg.mp4", "mp4", 0, 5, false, true),
  372. videoio_encapsulate_params_t("video/sample_322x242_15frames.yuv420p.libx264.mp4", "ts", 15, 15, true, true),
  373. videoio_encapsulate_params_t("../cv/tracking/faceocc2/data/faceocc2.webm", "webm", 128, 129, false, true),
  374. videoio_encapsulate_params_t("../cv/video/1920x1080.avi", "mp4", 12, 13, false, true),
  375. videoio_encapsulate_params_t("../cv/video/768x576.avi", "avi", 15, 16, false, true),
  376. // Not supported by with FFmpeg:
  377. //videoio_encapsulate_params_t("video/sample_322x242_15frames.yuv420p.libx265.mp4", "mp4", 15, 15, true, true),
  378. //videoio_encapsulate_params_t("video/sample_322x242_15frames.yuv420p.libvpx-vp9.mp4", "mp4", 15, 15, false, true),
  379. };
  380. INSTANTIATE_TEST_CASE_P(/**/, videoio_encapsulate, testing::ValuesIn(videoio_encapsulate_params));
  381. TEST(videoio_encapsulate_set_idr, write)
  382. {
  383. const VideoCaptureAPIs api = CAP_FFMPEG;
  384. if (!videoio_registry::hasBackend(api))
  385. throw SkipTestException("FFmpeg backend was not found");
  386. const string fileName = findDataFile("video/big_buck_bunny.mp4");
  387. const string ext = "mp4";
  388. const string fileNameOut = tempfile(cv::format("test_encapsulated_stream_set_idr.%s", ext.c_str()).c_str());
  389. // Use VideoWriter to encapsulate encoded video read with VideoReader
  390. {
  391. VideoCapture capRaw(fileName, api, { CAP_PROP_FORMAT, -1 });
  392. ASSERT_TRUE(capRaw.isOpened());
  393. const int width = static_cast<int>(capRaw.get(CAP_PROP_FRAME_WIDTH));
  394. const int height = static_cast<int>(capRaw.get(CAP_PROP_FRAME_HEIGHT));
  395. const double fps = capRaw.get(CAP_PROP_FPS);
  396. const int codecExtradataIndex = static_cast<int>(capRaw.get(CAP_PROP_CODEC_EXTRADATA_INDEX));
  397. Mat extraData;
  398. capRaw.retrieve(extraData, codecExtradataIndex);
  399. const int fourcc = static_cast<int>(capRaw.get(CAP_PROP_FOURCC));
  400. const bool mpeg4 = (fourcc == fourccFromString("FMP4"));
  401. VideoWriter container(fileNameOut, api, fourcc, fps, { width, height }, { VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO, 1 });
  402. ASSERT_TRUE(container.isOpened());
  403. Mat rawFrame;
  404. int i = 0;
  405. while (capRaw.read(rawFrame)) {
  406. ASSERT_FALSE(rawFrame.empty());
  407. if (i == 0 && mpeg4) {
  408. Mat tmp = rawFrame.clone();
  409. const size_t newSzt = tmp.total() + extraData.total();
  410. const int newSz = static_cast<int>(newSzt);
  411. ASSERT_TRUE(newSzt == static_cast<size_t>(newSz));
  412. rawFrame = Mat(1, newSz, CV_8UC1);
  413. memcpy(rawFrame.data, extraData.data, extraData.total());
  414. memcpy(rawFrame.data + extraData.total(), tmp.data, tmp.total());
  415. }
  416. if (capRaw.get(CAP_PROP_LRF_HAS_KEY_FRAME) != 0)
  417. container.set(VideoWriterProperties::VIDEOWRITER_PROP_KEY_FLAG, 1);
  418. else
  419. container.set(VideoWriterProperties::VIDEOWRITER_PROP_KEY_FLAG, 0);
  420. container.write(rawFrame);
  421. i++;
  422. }
  423. container.release();
  424. }
  425. std::cout << "Checking encapsulated video container: " << fileNameOut << std::endl;
  426. // Check encapsulated video container is "identical" to the original
  427. {
  428. VideoCapture capReference(fileName), capReferenceRaw(fileName, api, { CAP_PROP_FORMAT, -1 }), capActual(fileNameOut), capActualRaw(fileNameOut, api, { CAP_PROP_FORMAT, -1 });
  429. ASSERT_TRUE(capReference.isOpened());
  430. ASSERT_TRUE(capActual.isOpened());
  431. ASSERT_TRUE(capReferenceRaw.isOpened());
  432. ASSERT_TRUE(capActualRaw.isOpened());
  433. const double fpsReference = capReference.get(CAP_PROP_FPS);
  434. const double fpsActual = capActual.get(CAP_PROP_FPS);
  435. ASSERT_EQ(fpsReference, fpsActual);
  436. const int nFramesReference = static_cast<int>(capReference.get(CAP_PROP_FRAME_COUNT));
  437. const int nFramesActual = static_cast<int>(capActual.get(CAP_PROP_FRAME_COUNT));
  438. ASSERT_EQ(nFramesReference, nFramesActual);
  439. Mat reference, actual;
  440. for (int i = 0; i < nFramesReference; i++) {
  441. ASSERT_TRUE(capReference.read(reference));
  442. ASSERT_FALSE(reference.empty());
  443. ASSERT_TRUE(capActual.read(actual));
  444. ASSERT_FALSE(actual.empty());
  445. ASSERT_EQ(0, cvtest::norm(reference, actual, NORM_INF));
  446. ASSERT_TRUE(capReferenceRaw.grab());
  447. ASSERT_TRUE(capActualRaw.grab());
  448. const bool keyFrameReference = capActualRaw.get(CAP_PROP_LRF_HAS_KEY_FRAME) == 1.;
  449. const bool keyFrameActual = capActualRaw.get(CAP_PROP_LRF_HAS_KEY_FRAME) == 1.;
  450. ASSERT_EQ(keyFrameReference, keyFrameActual);
  451. }
  452. }
  453. ASSERT_EQ(0, remove(fileNameOut.c_str()));
  454. }
  455. typedef tuple<string, string, int> videoio_skip_params_t;
  456. typedef testing::TestWithParam< videoio_skip_params_t > videoio_skip;
  457. TEST_P(videoio_skip, DISABLED_read) // optional test, may fail in some configurations
  458. {
  459. #if CV_VERSION_MAJOR >= 4
  460. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  461. throw SkipTestException("Backend was not found");
  462. #endif
  463. const string path = get<0>(GetParam());
  464. const string env = get<1>(GetParam());
  465. const int expectedFrameNumber = get<2>(GetParam());
  466. #ifdef _WIN32
  467. _putenv_s("OPENCV_FFMPEG_CAPTURE_OPTIONS", env.c_str());
  468. #else
  469. setenv("OPENCV_FFMPEG_CAPTURE_OPTIONS", env.c_str(), 1);
  470. #endif
  471. VideoCapture container(findDataFile(path), CAP_FFMPEG);
  472. #ifdef _WIN32
  473. _putenv_s("OPENCV_FFMPEG_CAPTURE_OPTIONS", "");
  474. #else
  475. setenv("OPENCV_FFMPEG_CAPTURE_OPTIONS", "", 1);
  476. #endif
  477. ASSERT_TRUE(container.isOpened());
  478. Mat reference;
  479. int nframes = 0, n_err = 0;
  480. while (container.isOpened())
  481. {
  482. if (container.read(reference))
  483. nframes++;
  484. else if (++n_err > 3)
  485. break;
  486. }
  487. EXPECT_EQ(expectedFrameNumber, nframes);
  488. }
  489. const videoio_skip_params_t videoio_skip_params[] =
  490. {
  491. videoio_skip_params_t("video/big_buck_bunny.mp4", "", 125),
  492. videoio_skip_params_t("video/big_buck_bunny.mp4", "avdiscard;nonkey", 11)
  493. };
  494. INSTANTIATE_TEST_CASE_P(/**/, videoio_skip, testing::ValuesIn(videoio_skip_params));
  495. //==========================================================================
  496. static void generateFrame(Mat &frame, unsigned int i, const Point &center, const Scalar &color)
  497. {
  498. frame = Scalar::all(i % 255);
  499. stringstream buf(ios::out);
  500. buf << "frame #" << i;
  501. putText(frame, buf.str(), Point(50, center.y), FONT_HERSHEY_SIMPLEX, 5.0, color, 5, LINE_AA);
  502. circle(frame, center, i + 2, color, 2, LINE_AA);
  503. }
  504. TEST(videoio_ffmpeg, parallel)
  505. {
  506. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  507. throw SkipTestException("FFmpeg backend was not found");
  508. const int NUM = 4;
  509. const int GRAN = 4;
  510. const Range R(0, NUM);
  511. const Size sz(1020, 900);
  512. const int frameNum = 300;
  513. const Scalar color(Scalar::all(0));
  514. const Point center(sz.height / 2, sz.width / 2);
  515. // Generate filenames
  516. vector<string> files;
  517. for (int i = 0; i < NUM; ++i)
  518. {
  519. ostringstream stream;
  520. stream << i << ".avi";
  521. files.push_back(tempfile(stream.str().c_str()));
  522. }
  523. // Write videos
  524. {
  525. vector< Ptr<VideoWriter> > writers(NUM);
  526. auto makeWriters = [&](const Range &r)
  527. {
  528. for (int i = r.start; i != r.end; ++i)
  529. writers[i] = makePtr<VideoWriter>(files[i],
  530. CAP_FFMPEG,
  531. VideoWriter::fourcc('X','V','I','D'),
  532. 25.0f,
  533. sz);
  534. };
  535. parallel_for_(R, makeWriters, GRAN);
  536. for(int i = 0; i < NUM; ++i)
  537. {
  538. ASSERT_TRUE(writers[i]);
  539. ASSERT_TRUE(writers[i]->isOpened());
  540. }
  541. auto writeFrames = [&](const Range &r)
  542. {
  543. for (int j = r.start; j < r.end; ++j)
  544. {
  545. Mat frame(sz, CV_8UC3);
  546. for (int i = 0; i < frameNum; ++i)
  547. {
  548. generateFrame(frame, i, center, color);
  549. writers[j]->write(frame);
  550. }
  551. }
  552. };
  553. parallel_for_(R, writeFrames, GRAN);
  554. }
  555. // Read videos
  556. {
  557. vector< Ptr<VideoCapture> > readers(NUM);
  558. auto makeCaptures = [&](const Range &r)
  559. {
  560. for (int i = r.start; i != r.end; ++i)
  561. readers[i] = makePtr<VideoCapture>(files[i], CAP_FFMPEG);
  562. };
  563. parallel_for_(R, makeCaptures, GRAN);
  564. for(int i = 0; i < NUM; ++i)
  565. {
  566. ASSERT_TRUE(readers[i]);
  567. ASSERT_TRUE(readers[i]->isOpened());
  568. }
  569. auto readFrames = [&](const Range &r)
  570. {
  571. for (int j = r.start; j < r.end; ++j)
  572. {
  573. Mat reference(sz, CV_8UC3);
  574. for (int i = 0; i < frameNum; ++i)
  575. {
  576. Mat actual;
  577. EXPECT_TRUE(readers[j]->read(actual));
  578. EXPECT_FALSE(actual.empty());
  579. generateFrame(reference, i, center, color);
  580. EXPECT_EQ(reference.size(), actual.size());
  581. EXPECT_EQ(reference.depth(), actual.depth());
  582. EXPECT_EQ(reference.channels(), actual.channels());
  583. EXPECT_GE(cvtest::PSNR(actual, reference), 35.0) << "cap" << j << ", frame " << i;
  584. }
  585. }
  586. };
  587. parallel_for_(R, readFrames, GRAN);
  588. }
  589. // Remove files
  590. for(int i = 0; i < NUM; ++i)
  591. {
  592. remove(files[i].c_str());
  593. }
  594. }
  595. typedef std::pair<VideoCaptureProperties, double> cap_property_t;
  596. typedef std::vector<cap_property_t> cap_properties_t;
  597. typedef std::pair<std::string, cap_properties_t> ffmpeg_cap_properties_param_t;
  598. typedef testing::TestWithParam<ffmpeg_cap_properties_param_t> ffmpeg_cap_properties;
  599. TEST_P(ffmpeg_cap_properties, can_read_property)
  600. {
  601. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  602. throw SkipTestException("FFmpeg backend was not found");
  603. ffmpeg_cap_properties_param_t parameters = GetParam();
  604. const std::string path = parameters.first;
  605. const cap_properties_t properties = parameters.second;
  606. VideoCapture cap(findDataFile(path), CAP_FFMPEG);
  607. ASSERT_TRUE(cap.isOpened()) << "Can not open " << findDataFile(path);
  608. for (std::size_t i = 0; i < properties.size(); ++i)
  609. {
  610. const cap_property_t& prop = properties[i];
  611. const double actualValue = cap.get(static_cast<int>(prop.first));
  612. EXPECT_DOUBLE_EQ(actualValue, prop.second)
  613. << "Property " << static_cast<int>(prop.first) << " has wrong value";
  614. }
  615. }
  616. cap_properties_t loadBigBuckBunnyFFProbeResults() {
  617. cap_property_t properties[] = { cap_property_t(CAP_PROP_BITRATE, 5851.),
  618. cap_property_t(CAP_PROP_FPS, 24.),
  619. cap_property_t(CAP_PROP_FRAME_HEIGHT, 384.),
  620. cap_property_t(CAP_PROP_FRAME_WIDTH, 672.) };
  621. return cap_properties_t(properties, properties + sizeof(properties) / sizeof(cap_property_t));
  622. }
  623. const ffmpeg_cap_properties_param_t videoio_ffmpeg_properties[] = {
  624. ffmpeg_cap_properties_param_t("video/big_buck_bunny.avi", loadBigBuckBunnyFFProbeResults())
  625. };
  626. INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_cap_properties, testing::ValuesIn(videoio_ffmpeg_properties));
  627. typedef tuple<string, string> ffmpeg_get_fourcc_param_t;
  628. typedef testing::TestWithParam<ffmpeg_get_fourcc_param_t> ffmpeg_get_fourcc;
  629. TEST_P(ffmpeg_get_fourcc, check_short_codecs)
  630. {
  631. const VideoCaptureAPIs api = CAP_FFMPEG;
  632. if (!videoio_registry::hasBackend(api))
  633. throw SkipTestException("Backend was not found");
  634. const string fileName = get<0>(GetParam());
  635. const string fourcc_string = get<1>(GetParam());
  636. VideoCapture cap(findDataFile(fileName), api);
  637. if (!cap.isOpened())
  638. throw SkipTestException("Video stream is not supported");
  639. const double fourcc = cap.get(CAP_PROP_FOURCC);
  640. ASSERT_EQ(fourcc_string, fourccToString((int)fourcc));
  641. }
  642. const ffmpeg_get_fourcc_param_t ffmpeg_get_fourcc_param[] =
  643. {
  644. ffmpeg_get_fourcc_param_t("../cv/tracking/faceocc2/data/faceocc2.webm", "VP80"),
  645. ffmpeg_get_fourcc_param_t("video/big_buck_bunny.h265", "hevc"),
  646. ffmpeg_get_fourcc_param_t("video/big_buck_bunny.h264", "h264"),
  647. ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.libvpx-vp9.mp4", "VP90"),
  648. ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.libaom-av1.mp4", "AV01"),
  649. ffmpeg_get_fourcc_param_t("video/big_buck_bunny.mp4", "FMP4"),
  650. ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.mpeg2video.mp4", "mpg2"),
  651. ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.mjpeg.mp4", "MJPG"),
  652. ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.libxvid.mp4", "FMP4"),
  653. ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.libx265.mp4", "hevc"),
  654. ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.libx264.mp4", "h264")
  655. };
  656. inline static std::string ffmpeg_get_fourcc_name_printer(const testing::TestParamInfo<ffmpeg_get_fourcc::ParamType>& info)
  657. {
  658. std::ostringstream os;
  659. const string & fourcc = get<1>(info.param);
  660. os << info.index << "_" << (fourcc.size() == 0 ? "NONE" : fourcc);
  661. return os.str();
  662. }
  663. INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_get_fourcc, testing::ValuesIn(ffmpeg_get_fourcc_param), ffmpeg_get_fourcc_name_printer);
  664. static void ffmpeg_check_read_raw(VideoCapture& cap)
  665. {
  666. ASSERT_TRUE(cap.isOpened()) << "Can't open the video";
  667. Mat data;
  668. cap >> data;
  669. EXPECT_EQ(CV_8UC1, data.type()) << "CV_8UC1 != " << typeToString(data.type());
  670. EXPECT_TRUE(data.rows == 1 || data.cols == 1) << data.size;
  671. EXPECT_EQ((size_t)29729, data.total());
  672. cap >> data;
  673. EXPECT_EQ(CV_8UC1, data.type()) << "CV_8UC1 != " << typeToString(data.type());
  674. EXPECT_TRUE(data.rows == 1 || data.cols == 1) << data.size;
  675. EXPECT_EQ((size_t)37118, data.total());
  676. // 12 is the nearset key frame to frame 18
  677. EXPECT_TRUE(cap.set(CAP_PROP_POS_FRAMES, 18.));
  678. EXPECT_EQ(cap.get(CAP_PROP_POS_FRAMES), 12.);
  679. cap >> data;
  680. EXPECT_EQ(CV_8UC1, data.type()) << "CV_8UC1 != " << typeToString(data.type());
  681. EXPECT_TRUE(data.rows == 1 || data.cols == 1) << data.size;
  682. EXPECT_EQ((size_t)8726, data.total());
  683. }
  684. TEST(videoio_ffmpeg, ffmpeg_check_extra_data)
  685. {
  686. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  687. throw SkipTestException("FFmpeg backend was not found");
  688. string video_file = findDataFile("video/big_buck_bunny.mp4");
  689. VideoCapture cap;
  690. EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG));
  691. ASSERT_TRUE(cap.isOpened()) << "Can't open the video";
  692. const int codecExtradataIdx = (int)cap.get(CAP_PROP_CODEC_EXTRADATA_INDEX);
  693. Mat data;
  694. ASSERT_TRUE(cap.retrieve(data, codecExtradataIdx));
  695. EXPECT_EQ(CV_8UC1, data.type()) << "CV_8UC1 != " << typeToString(data.type());
  696. EXPECT_TRUE(data.rows == 1 || data.cols == 1) << data.size;
  697. EXPECT_EQ((size_t)45, data.total());
  698. }
  699. TEST(videoio_ffmpeg, open_with_property)
  700. {
  701. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  702. throw SkipTestException("FFmpeg backend was not found");
  703. string video_file = findDataFile("video/big_buck_bunny.mp4");
  704. VideoCapture cap;
  705. EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG, {
  706. CAP_PROP_FORMAT, -1 // demux only
  707. }));
  708. // confirm properties are returned without initializing AVCodecContext
  709. EXPECT_EQ(cap.get(CAP_PROP_FORMAT), -1);
  710. EXPECT_EQ(static_cast<int>(cap.get(CAP_PROP_FOURCC)), fourccFromString("FMP4"));
  711. EXPECT_EQ(cap.get(CAP_PROP_N_THREADS), 0.0);
  712. EXPECT_EQ(cap.get(CAP_PROP_FRAME_HEIGHT), 384.0);
  713. EXPECT_EQ(cap.get(CAP_PROP_FRAME_WIDTH), 672.0);
  714. EXPECT_EQ(cap.get(CAP_PROP_FRAME_COUNT), 125);
  715. EXPECT_EQ(cap.get(CAP_PROP_FPS), 24.0);
  716. ffmpeg_check_read_raw(cap);
  717. }
  718. TEST(videoio_ffmpeg, create_with_property)
  719. {
  720. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  721. throw SkipTestException("FFmpeg backend was not found");
  722. string video_file = findDataFile("video/big_buck_bunny.mp4");
  723. VideoCapture cap(video_file, CAP_FFMPEG, {
  724. CAP_PROP_FORMAT, -1 // demux only
  725. });
  726. // confirm properties are returned without initializing AVCodecContext
  727. EXPECT_TRUE(cap.get(CAP_PROP_FORMAT) == -1);
  728. EXPECT_EQ(static_cast<int>(cap.get(CAP_PROP_FOURCC)), fourccFromString("FMP4"));
  729. EXPECT_EQ(cap.get(CAP_PROP_N_THREADS), 0.0);
  730. EXPECT_EQ(cap.get(CAP_PROP_FRAME_HEIGHT), 384.0);
  731. EXPECT_EQ(cap.get(CAP_PROP_FRAME_WIDTH), 672.0);
  732. EXPECT_EQ(cap.get(CAP_PROP_FRAME_COUNT), 125);
  733. EXPECT_EQ(cap.get(CAP_PROP_FPS), 24.0);
  734. ffmpeg_check_read_raw(cap);
  735. }
  736. TEST(videoio_ffmpeg, create_with_property_badarg)
  737. {
  738. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  739. throw SkipTestException("FFmpeg backend was not found");
  740. string video_file = findDataFile("video/big_buck_bunny.mp4");
  741. VideoCapture cap(video_file, CAP_FFMPEG, {
  742. CAP_PROP_FORMAT, -2 // invalid
  743. });
  744. EXPECT_FALSE(cap.isOpened());
  745. }
  746. // related issue: https://github.com/opencv/opencv/issues/16821
  747. TEST(videoio_ffmpeg, DISABLED_open_from_web)
  748. {
  749. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  750. throw SkipTestException("FFmpeg backend was not found");
  751. string video_file = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
  752. VideoCapture cap(video_file, CAP_FFMPEG);
  753. int n_frames = -1;
  754. EXPECT_NO_THROW(n_frames = (int)cap.get(CAP_PROP_FRAME_COUNT));
  755. EXPECT_EQ((int)14315, n_frames);
  756. }
  757. typedef tuple<string, string, bool, bool> FourCC_Ext_Color_Support;
  758. typedef testing::TestWithParam< FourCC_Ext_Color_Support > videoio_ffmpeg_16bit;
  759. TEST_P(videoio_ffmpeg_16bit, basic)
  760. {
  761. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  762. throw SkipTestException("FFmpeg backend was not found");
  763. const int fourcc = fourccFromString(get<0>(GetParam()));
  764. const string ext = string(".") + get<1>(GetParam());
  765. const bool isColor = get<2>(GetParam());
  766. const bool isSupported = get<3>(GetParam());
  767. const int cn = isColor ? 3 : 1;
  768. const int dataType = CV_16UC(cn);
  769. const string filename = tempfile(ext.c_str());
  770. const Size sz(640, 480);
  771. const double fps = 30.0;
  772. const double time_sec = 1;
  773. const int numFrames = static_cast<int>(fps * time_sec);
  774. {
  775. VideoWriter writer;
  776. writer.open(filename, CAP_FFMPEG, fourcc, fps, sz,
  777. {
  778. VIDEOWRITER_PROP_DEPTH, CV_16U,
  779. VIDEOWRITER_PROP_IS_COLOR, isColor
  780. });
  781. ASSERT_EQ(isSupported, writer.isOpened());
  782. if (isSupported)
  783. {
  784. Mat img(sz, dataType, Scalar::all(0));
  785. const int coeff = cvRound(min(sz.width, sz.height)/(fps * time_sec));
  786. for (int i = 0 ; i < numFrames; i++ )
  787. {
  788. rectangle(img,
  789. Point2i(coeff * i, coeff * i),
  790. Point2i(coeff * (i + 1), coeff * (i + 1)),
  791. Scalar::all(255 * (1.0 - static_cast<double>(i) / (fps * time_sec * 2))),
  792. -1);
  793. writer << img;
  794. }
  795. writer.release();
  796. EXPECT_GT(getFileSize(filename), 8192);
  797. }
  798. }
  799. if (isSupported)
  800. {
  801. VideoCapture cap;
  802. ASSERT_TRUE(cap.open(filename, CAP_FFMPEG, {CAP_PROP_CONVERT_RGB, false}));
  803. ASSERT_TRUE(cap.isOpened());
  804. Mat img;
  805. bool res = true;
  806. int numRead = 0;
  807. while(res)
  808. {
  809. res = cap.read(img);
  810. if (res)
  811. {
  812. ++numRead;
  813. ASSERT_EQ(img.type(), dataType);
  814. ASSERT_EQ(img.size(), sz);
  815. }
  816. }
  817. ASSERT_EQ(numRead, numFrames);
  818. remove(filename.c_str());
  819. }
  820. }
  821. const FourCC_Ext_Color_Support sixteen_bit_modes[] =
  822. {
  823. // 16-bit grayscale is supported
  824. make_tuple("FFV1", "avi", false, true),
  825. make_tuple("FFV1", "mkv", false, true),
  826. // 16-bit color formats are NOT supported
  827. make_tuple("FFV1", "avi", true, false),
  828. make_tuple("FFV1", "mkv", true, false),
  829. };
  830. inline static std::string videoio_ffmpeg_16bit_name_printer(const testing::TestParamInfo<videoio_ffmpeg_16bit::ParamType>& info)
  831. {
  832. std::ostringstream os;
  833. os << get<0>(info.param) << "_"
  834. << get<1>(info.param) << "_"
  835. << (get<2>(info.param) ? "COLOR" : "GRAY") << "_"
  836. << (get<3>(info.param) ? "SUP" : "NOT");
  837. return os.str();
  838. }
  839. INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_16bit, testing::ValuesIn(sixteen_bit_modes), videoio_ffmpeg_16bit_name_printer);
  840. typedef tuple<int /*inputType*/, int /*Depth*/, bool /*isColor*/, bool /*isValid*/, string /*description*/> ChannelMismatchTestParams;
  841. typedef testing::TestWithParam< ChannelMismatchTestParams > videoio_ffmpeg_channel_mismatch;
  842. TEST_P(videoio_ffmpeg_channel_mismatch, basic)
  843. {
  844. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  845. throw SkipTestException("FFmpeg backend was not found");
  846. const string filename = "mismatch_video.mp4";
  847. int input_type = get<0>(GetParam());
  848. int depth = get<1>(GetParam());
  849. bool is_Color = get<2>(GetParam());
  850. bool is_valid = get<3>(GetParam());
  851. const string description = get<4>(GetParam());
  852. const double fps = 15.0;
  853. const int fourcc = VideoWriter::fourcc('m', 'p', '4', 'v');
  854. const Mat frame(480, 640, input_type, Scalar::all(0));
  855. VideoWriter writer(filename, fourcc, fps, frame.size(),
  856. {
  857. cv::VIDEOWRITER_PROP_DEPTH, depth,
  858. VIDEOWRITER_PROP_IS_COLOR, is_Color
  859. });
  860. if (!writer.isOpened())
  861. throw SkipTestException("Failed to open video writer");
  862. for (int i = 1; i <= 15; i++)
  863. {
  864. // In case of mismatch between input frame channels and
  865. // expected depth/isColor configuration a warning should be printed communicating it
  866. writer.write(frame);
  867. }
  868. writer.release();
  869. VideoCapture cap(filename, CAP_FFMPEG);
  870. if (is_valid) {
  871. ASSERT_TRUE(cap.isOpened()) << "Can't open video for " << description;
  872. EXPECT_EQ(cap.get(CAP_PROP_FRAME_COUNT), 15) << "All frames should be written for: " << description;
  873. } else {
  874. ASSERT_FALSE(cap.isOpened()) << "Video capture should fail to open for: " << description;
  875. }
  876. std::remove(filename.c_str());
  877. }
  878. const ChannelMismatchTestParams mismatch_cases[] =
  879. {
  880. // Testing input frame channels and expected depth/isColor combinations
  881. // Open VideoWriter depth/isColor combination: CV_8U/true, everything with 3 channels should be valid
  882. make_tuple(CV_16UC1, CV_8U, true, false, "input_CV_16UC1_expected_CV_8U_isColor_true"),
  883. make_tuple(CV_8UC1, CV_8U, true, false, "input_CV_8UC1_expected_CV_8U_isColor_true"),
  884. make_tuple(CV_8UC3, CV_8U, true, true, "input_CV_8UC3_expected_CV_8U_isColor_true_valid"),
  885. make_tuple(CV_16UC3, CV_8U, true, true, "input_CV_16UC3_expected_CV_8U_isColor_true_valid"),
  886. // Open VideoWriter depth/isColor combination: 16U,8U/false, everything with 1 channel should be valid
  887. make_tuple(CV_8UC3, CV_8U, false, false, "input_CV_8UC3_expected_CV_8U_isColor_false"),
  888. make_tuple(CV_16UC3, CV_8U, false, false, "input_CV_16UC3_expected_CV_8U_isColor_false"),
  889. make_tuple(CV_8UC3, CV_16U, false, false, "input_CV_8UC3_expected_CV_16U_isColor_false"),
  890. make_tuple(CV_16UC3, CV_16U, false, false, "input_CV_16UC3_expected_CV_16U_isColor_false"),
  891. make_tuple(CV_8UC1, CV_16U, false, true, "input_CV_8UC1_expected_CV_16U_isColor_false_valid"),
  892. make_tuple(CV_16UC1, CV_8U, false, true, "input_CV_16UC1_expected_CV_8U_isColor_false_valid"),
  893. };
  894. inline static std::string videoio_ffmpeg_mismatch_name_printer(const testing::TestParamInfo<videoio_ffmpeg_channel_mismatch::ParamType>& info)
  895. {
  896. std::ostringstream os;
  897. os << get<4>(info.param);
  898. return os.str();
  899. }
  900. INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_channel_mismatch, testing::ValuesIn(mismatch_cases), videoio_ffmpeg_mismatch_name_printer);
  901. // related issue: https://github.com/opencv/opencv/issues/23088
  902. TEST(ffmpeg_cap_properties, set_pos_get_msec)
  903. {
  904. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  905. throw SkipTestException("FFmpeg backend was not found");
  906. string video_file = findDataFile("video/big_buck_bunny.mp4");
  907. VideoCapture cap;
  908. EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG));
  909. ASSERT_TRUE(cap.isOpened()) << "Can't open the video";
  910. cap.set(CAP_PROP_POS_FRAMES, 25);
  911. EXPECT_EQ(cap.get(CAP_PROP_POS_MSEC), 1000.0);
  912. cap.set(CAP_PROP_POS_MSEC, 525);
  913. EXPECT_EQ(cap.get(CAP_PROP_POS_MSEC), 500.0);
  914. cap.set(CAP_PROP_POS_AVI_RATIO, 0);
  915. EXPECT_EQ(cap.get(CAP_PROP_POS_MSEC), 0.0);
  916. }
  917. // Test that seeking twice to the same frame in videos with negative DTS
  918. // does not result in negative position or timestamp values
  919. // related issue: https://github.com/opencv/opencv/issues/27819
  920. TEST(videoio_ffmpeg, seek_with_negative_dts)
  921. {
  922. if (!videoio_registry::hasBackend(CAP_FFMPEG))
  923. throw SkipTestException("FFmpeg backend was not found");
  924. const std::string filename = findDataFile("video/negdts_h264.mp4");
  925. VideoCapture cap(filename, CAP_FFMPEG);
  926. if (!cap.isOpened())
  927. throw SkipTestException("Video stream is not supported");
  928. // after open, a single grab() should not yield negative POS_MSEC.
  929. ASSERT_TRUE(cap.grab());
  930. EXPECT_GE(cap.get(CAP_PROP_POS_MSEC), 0.0) << "Negative ts immediately after open+grab()";
  931. ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, 0));
  932. (void)cap.get(CAP_PROP_POS_FRAMES);
  933. const int framesToProbe[] = {2, 3, 4, 5};
  934. for (int f : framesToProbe)
  935. {
  936. // Reset to frame 0
  937. ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, 0));
  938. cap.get(CAP_PROP_POS_FRAMES);
  939. // Seek to target frame
  940. ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, f));
  941. const double posAfterFirstSeek = cap.get(CAP_PROP_POS_FRAMES);
  942. // Seek to the same frame again
  943. ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, f));
  944. const double posAfterSecondSeek = cap.get(CAP_PROP_POS_FRAMES);
  945. const double tsAfterSecondSeek = cap.get(CAP_PROP_POS_MSEC);
  946. EXPECT_GE(posAfterSecondSeek, 0)
  947. << "Frame index became negative after second seek to frame " << f
  948. << " (first seek gave " << posAfterFirstSeek << ")";
  949. EXPECT_GE(tsAfterSecondSeek, 0.0)
  950. << "Timestamp became negative after second seek to frame " << f;
  951. // Per-iteration decode check: grab() + ts non-negative
  952. ASSERT_TRUE(cap.grab());
  953. EXPECT_GE(cap.get(CAP_PROP_POS_MSEC), 0.0) << "Negative timestamp after grab() at frame " << f;
  954. // Verify that reading a frame works and position advances
  955. Mat frame;
  956. ASSERT_TRUE(cap.read(frame));
  957. ASSERT_FALSE(frame.empty());
  958. EXPECT_GE(cap.get(CAP_PROP_POS_FRAMES), f);
  959. }
  960. }
  961. }} // namespace