videocapture_gstreamer_pipeline.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. #include "opencv2/core/utility.hpp"
  2. #include "opencv2/imgproc.hpp"
  3. #include "opencv2/imgcodecs.hpp"
  4. #include "opencv2/highgui.hpp"
  5. #include <string>
  6. #include <iostream>
  7. #include <map>
  8. using namespace std;
  9. using namespace cv;
  10. //================================================================================
  11. template<typename M>
  12. inline typename M::mapped_type getValue(const M &dict, const typename M::key_type &key, const string & errorMessage)
  13. {
  14. typename M::const_iterator it = dict.find(key);
  15. if (it == dict.end())
  16. {
  17. CV_Error(Error::StsBadArg, errorMessage);
  18. }
  19. return it->second;
  20. }
  21. inline map<string, Size> sizeByResolution()
  22. {
  23. map<string, Size> res;
  24. res["720p"] = Size(1280, 720);
  25. res["1080p"] = Size(1920, 1080);
  26. res["4k"] = Size(3840, 2160);
  27. return res;
  28. }
  29. inline map<string, int> fourccByCodec()
  30. {
  31. map<string, int> res;
  32. res["h264"] = VideoWriter::fourcc('H','2','6','4');
  33. res["h265"] = VideoWriter::fourcc('H','E','V','C');
  34. res["mpeg2"] = VideoWriter::fourcc('M','P','E','G');
  35. res["mpeg4"] = VideoWriter::fourcc('M','P','4','2');
  36. res["mjpeg"] = VideoWriter::fourcc('M','J','P','G');
  37. res["vp8"] = VideoWriter::fourcc('V','P','8','0');
  38. return res;
  39. }
  40. inline map<string, string> defaultEncodeElementByCodec()
  41. {
  42. map<string, string> res;
  43. res["h264"] = "x264enc";
  44. res["h265"] = "x265enc";
  45. res["mpeg2"] = "mpeg2enc";
  46. res["mjpeg"] = "jpegenc";
  47. res["vp8"] = "vp8enc";
  48. return res;
  49. }
  50. inline map<string, string> VAAPIEncodeElementByCodec()
  51. {
  52. map<string, string> res;
  53. res["h264"] = "parsebin ! vaapih264enc";
  54. res["h265"] = "parsebin ! vaapih265enc";
  55. res["mpeg2"] = "parsebin ! vaapimpeg2enc";
  56. res["mjpeg"] = "parsebin ! vaapijpegenc";
  57. res["vp8"] = "parsebin ! vaapivp8enc";
  58. return res;
  59. }
  60. inline map<string, string> mfxDecodeElementByCodec()
  61. {
  62. map<string, string> res;
  63. res["h264"] = "parsebin ! mfxh264dec";
  64. res["h265"] = "parsebin ! mfxhevcdec";
  65. res["mpeg2"] = "parsebin ! mfxmpeg2dec";
  66. res["mjpeg"] = "parsebin ! mfxjpegdec";
  67. return res;
  68. }
  69. inline map<string, string> mfxEncodeElementByCodec()
  70. {
  71. map<string, string> res;
  72. res["h264"] = "mfxh264enc";
  73. res["h265"] = "mfxhevcenc";
  74. res["mpeg2"] = "mfxmpeg2enc";
  75. res["mjpeg"] = "mfxjpegenc";
  76. return res;
  77. }
  78. inline map<string, string> libavDecodeElementByCodec()
  79. {
  80. map<string, string> res;
  81. res["h264"] = "parsebin ! avdec_h264";
  82. res["h265"] = "parsebin ! avdec_h265";
  83. res["mpeg2"] = "parsebin ! avdec_mpeg2video";
  84. res["mpeg4"] = "parsebin ! avdec_mpeg4";
  85. res["mjpeg"] = "parsebin ! avdec_mjpeg";
  86. res["vp8"] = "parsebin ! avdec_vp8";
  87. return res;
  88. }
  89. inline map<string, string> libavEncodeElementByCodec()
  90. {
  91. map<string, string> res;
  92. res["h264"] = "avenc_h264";
  93. res["h265"] = "avenc_h265";
  94. res["mpeg2"] = "avenc_mpeg2video";
  95. res["mpeg4"] = "avenc_mpeg4";
  96. res["mjpeg"] = "avenc_mjpeg";
  97. res["vp8"] = "avenc_vp8";
  98. return res;
  99. }
  100. inline map<string, string> demuxPluginByContainer()
  101. {
  102. map<string, string> res;
  103. res["avi"] = "avidemux";
  104. res["mp4"] = "qtdemux";
  105. res["mov"] = "qtdemux";
  106. res["mkv"] = "matroskademux";
  107. return res;
  108. }
  109. inline map<string, string> muxPluginByContainer()
  110. {
  111. map<string, string> res;
  112. res["avi"] = "avimux";
  113. res["mp4"] = "qtmux";
  114. res["mov"] = "qtmux";
  115. res["mkv"] = "matroskamux";
  116. return res;
  117. }
  118. //================================================================================
  119. inline string containerByName(const string &name)
  120. {
  121. size_t found = name.rfind(".");
  122. if (found != string::npos)
  123. {
  124. return name.substr(found + 1); // container type
  125. }
  126. return string();
  127. }
  128. //================================================================================
  129. inline Ptr<VideoCapture> createCapture(const string &backend, const string &file_name, const string &codec)
  130. {
  131. if (backend == "gst-default")
  132. {
  133. cout << "Created GStreamer capture ( " << file_name << " )" << endl;
  134. return makePtr<VideoCapture>(file_name, CAP_GSTREAMER);
  135. }
  136. else if (backend.find("gst") == 0)
  137. {
  138. ostringstream line;
  139. line << "filesrc location=\"" << file_name << "\"";
  140. line << " ! ";
  141. line << getValue(demuxPluginByContainer(), containerByName(file_name), "Invalid container");
  142. line << " ! ";
  143. if (backend.find("basic") == 4)
  144. line << "decodebin";
  145. else if (backend.find("vaapi") == 4)
  146. line << "vaapidecodebin";
  147. else if (backend.find("libav") == 4)
  148. line << getValue(libavDecodeElementByCodec(), codec, "Invalid codec");
  149. else if (backend.find("mfx") == 4)
  150. line << getValue(mfxDecodeElementByCodec(), codec, "Invalid or unsupported codec");
  151. else
  152. return Ptr<VideoCapture>();
  153. line << " ! videoconvert n-threads=" << getNumThreads();
  154. line << " ! appsink sync=false";
  155. cout << "Created GStreamer capture ( " << line.str() << " )" << endl;
  156. return makePtr<VideoCapture>(line.str(), CAP_GSTREAMER);
  157. }
  158. else if (backend == "ffmpeg")
  159. {
  160. cout << "Created FFmpeg capture ( " << file_name << " )" << endl;
  161. return makePtr<VideoCapture>(file_name, CAP_FFMPEG);
  162. }
  163. return Ptr<VideoCapture>();
  164. }
  165. inline Ptr<VideoCapture> createSynthSource(Size sz, unsigned fps)
  166. {
  167. ostringstream line;
  168. line << "videotestsrc pattern=smpte";
  169. line << " ! video/x-raw";
  170. line << ",width=" << sz.width << ",height=" << sz.height;
  171. if (fps > 0)
  172. line << ",framerate=" << fps << "/1";
  173. line << " ! appsink sync=false";
  174. cout << "Created synthetic video source ( " << line.str() << " )" << endl;
  175. return makePtr<VideoCapture>(line.str(), CAP_GSTREAMER);
  176. }
  177. inline Ptr<VideoWriter> createWriter(const string &backend, const string &file_name, const string &codec, Size sz, unsigned fps)
  178. {
  179. if (backend == "gst-default")
  180. {
  181. cout << "Created GStreamer writer ( " << file_name << ", FPS=" << fps << ", Size=" << sz << ")" << endl;
  182. return makePtr<VideoWriter>(file_name, CAP_GSTREAMER, getValue(fourccByCodec(), codec, "Invalid codec"), fps, sz, true);
  183. }
  184. else if (backend.find("gst") == 0)
  185. {
  186. ostringstream line;
  187. line << "appsrc ! videoconvert n-threads=" << getNumThreads() << " ! ";
  188. if (backend.find("basic") == 4)
  189. line << getValue(defaultEncodeElementByCodec(), codec, "Invalid codec");
  190. else if (backend.find("vaapi") == 4)
  191. line << getValue(VAAPIEncodeElementByCodec(), codec, "Invalid codec");
  192. else if (backend.find("libav") == 4)
  193. line << getValue(libavEncodeElementByCodec(), codec, "Invalid codec");
  194. else if (backend.find("mfx") == 4)
  195. line << getValue(mfxEncodeElementByCodec(), codec, "Invalid codec");
  196. else
  197. return Ptr<VideoWriter>();
  198. line << " ! ";
  199. line << getValue(muxPluginByContainer(), containerByName(file_name), "Invalid container");
  200. line << " ! ";
  201. line << "filesink location=\"" << file_name << "\"";
  202. cout << "Created GStreamer writer ( " << line.str() << " )" << endl;
  203. return makePtr<VideoWriter>(line.str(), CAP_GSTREAMER, 0, fps, sz, true);
  204. }
  205. else if (backend == "ffmpeg")
  206. {
  207. cout << "Created FFmpeg writer ( " << file_name << ", FPS=" << fps << ", Size=" << sz << " )" << endl;
  208. return makePtr<VideoWriter>(file_name, CAP_FFMPEG, getValue(fourccByCodec(), codec, "Invalid codec"), fps, sz, true);
  209. }
  210. return Ptr<VideoWriter>();
  211. }
  212. //================================================================================
  213. int main(int argc, char *argv[])
  214. {
  215. const string keys =
  216. "{h help usage ? | | print help messages }"
  217. "{m mode |decode | coding mode (supported: encode, decode) }"
  218. "{b backend |default | video backend (supported: 'gst-default', 'gst-basic', 'gst-vaapi', 'gst-libav', 'gst-mfx', 'ffmpeg') }"
  219. "{c codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }"
  220. "{f file path | | path to file }"
  221. "{r resolution |720p | video resolution for encoding (supported: '720p', '1080p', '4k') }"
  222. "{fps |30 | fix frame per second for encoding (supported: fps > 0) }"
  223. "{fast | | fast measure fps }";
  224. CommandLineParser cmd_parser(argc, argv, keys);
  225. cmd_parser.about("This program measures performance of video encoding and decoding using different backends OpenCV.");
  226. if (cmd_parser.has("help"))
  227. {
  228. cmd_parser.printMessage();
  229. return 0;
  230. }
  231. bool fast_measure = cmd_parser.has("fast"); // fast measure fps
  232. unsigned fix_fps = cmd_parser.get<unsigned>("fps"); // fixed frame per second
  233. string backend = cmd_parser.get<string>("backend"); // video backend
  234. string mode = cmd_parser.get<string>("mode"); // coding mode
  235. string codec = cmd_parser.get<string>("codec"); // codec type
  236. string file_name = cmd_parser.get<string>("file"); // path to videofile
  237. string resolution = cmd_parser.get<string>("resolution"); // video resolution
  238. if (!cmd_parser.check())
  239. {
  240. cmd_parser.printErrors();
  241. return -1;
  242. }
  243. if (mode != "encode" && mode != "decode")
  244. {
  245. cout << "Unsupported mode: " << mode << endl;
  246. return -1;
  247. }
  248. if (mode == "decode")
  249. {
  250. file_name = samples::findFile(file_name);
  251. }
  252. cout << "Mode: " << mode << ", Backend: " << backend << ", File: " << file_name << ", Codec: " << codec << endl;
  253. TickMeter total;
  254. Ptr<VideoCapture> cap;
  255. Ptr<VideoWriter> wrt;
  256. try
  257. {
  258. if (mode == "decode")
  259. {
  260. cap = createCapture(backend, file_name, codec);
  261. if (!cap)
  262. {
  263. cout << "Failed to create video capture" << endl;
  264. return -3;
  265. }
  266. if (!cap->isOpened())
  267. {
  268. cout << "Capture is not opened" << endl;
  269. return -4;
  270. }
  271. }
  272. else if (mode == "encode")
  273. {
  274. Size sz = getValue(sizeByResolution(), resolution, "Invalid resolution");
  275. cout << "FPS: " << fix_fps << ", Frame size: " << sz << endl;
  276. cap = createSynthSource(sz, fix_fps);
  277. wrt = createWriter(backend, file_name, codec, sz, fix_fps);
  278. if (!cap || !wrt)
  279. {
  280. cout << "Failed to create synthetic video source or video writer" << endl;
  281. return -3;
  282. }
  283. if (!cap->isOpened() || !wrt->isOpened())
  284. {
  285. cout << "Synthetic video source or video writer is not opened" << endl;
  286. return -4;
  287. }
  288. }
  289. }
  290. catch (...)
  291. {
  292. cout << "Unsupported parameters" << endl;
  293. return -2;
  294. }
  295. TickMeter tick;
  296. Mat frame;
  297. Mat element;
  298. total.start();
  299. while(true)
  300. {
  301. if (mode == "decode")
  302. {
  303. tick.start();
  304. if (!cap->grab())
  305. {
  306. cout << "No more frames - break" << endl;
  307. break;
  308. }
  309. if (!cap->retrieve(frame))
  310. {
  311. cout << "Failed to retrieve frame - break" << endl;
  312. break;
  313. }
  314. if (frame.empty())
  315. {
  316. cout << "Empty frame received - break" << endl;
  317. break;
  318. }
  319. tick.stop();
  320. }
  321. else if (mode == "encode")
  322. {
  323. int limit = 100;
  324. while (!cap->grab() && --limit != 0)
  325. {
  326. cout << "Skipping empty input frame - " << limit << endl;
  327. }
  328. cap->retrieve(element);
  329. tick.start();
  330. *wrt << element;
  331. tick.stop();
  332. }
  333. if (fast_measure && tick.getCounter() >= 1000)
  334. {
  335. cout << "Fast mode frame limit reached - break" << endl;
  336. break;
  337. }
  338. if (mode == "encode" && tick.getCounter() >= 1000)
  339. {
  340. cout << "Encode frame limit reached - break" << endl;
  341. break;
  342. }
  343. }
  344. total.stop();
  345. if (tick.getCounter() == 0)
  346. {
  347. cout << "No frames have been processed" << endl;
  348. return -10;
  349. }
  350. else
  351. {
  352. double res_fps = tick.getCounter() / tick.getTimeSec();
  353. cout << tick.getCounter() << " frames in " << tick.getTimeSec() << " sec ~ " << res_fps << " FPS" << " (total time: " << total.getTimeSec() << " sec)" << endl;
  354. }
  355. return 0;
  356. }