123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- #include "opencv2/core/utility.hpp"
- #include "opencv2/imgproc.hpp"
- #include "opencv2/imgcodecs.hpp"
- #include "opencv2/highgui.hpp"
- #include <string>
- #include <iostream>
- #include <map>
- using namespace std;
- using namespace cv;
- //================================================================================
- template<typename M>
- inline typename M::mapped_type getValue(const M &dict, const typename M::key_type &key, const string & errorMessage)
- {
- typename M::const_iterator it = dict.find(key);
- if (it == dict.end())
- {
- CV_Error(Error::StsBadArg, errorMessage);
- }
- return it->second;
- }
- inline map<string, Size> sizeByResolution()
- {
- map<string, Size> res;
- res["720p"] = Size(1280, 720);
- res["1080p"] = Size(1920, 1080);
- res["4k"] = Size(3840, 2160);
- return res;
- }
- inline map<string, int> fourccByCodec()
- {
- map<string, int> res;
- res["h264"] = VideoWriter::fourcc('H','2','6','4');
- res["h265"] = VideoWriter::fourcc('H','E','V','C');
- res["mpeg2"] = VideoWriter::fourcc('M','P','E','G');
- res["mpeg4"] = VideoWriter::fourcc('M','P','4','2');
- res["mjpeg"] = VideoWriter::fourcc('M','J','P','G');
- res["vp8"] = VideoWriter::fourcc('V','P','8','0');
- return res;
- }
- inline map<string, string> defaultEncodeElementByCodec()
- {
- map<string, string> res;
- res["h264"] = "x264enc";
- res["h265"] = "x265enc";
- res["mpeg2"] = "mpeg2enc";
- res["mjpeg"] = "jpegenc";
- res["vp8"] = "vp8enc";
- return res;
- }
- inline map<string, string> VAAPIEncodeElementByCodec()
- {
- map<string, string> res;
- res["h264"] = "parsebin ! vaapih264enc";
- res["h265"] = "parsebin ! vaapih265enc";
- res["mpeg2"] = "parsebin ! vaapimpeg2enc";
- res["mjpeg"] = "parsebin ! vaapijpegenc";
- res["vp8"] = "parsebin ! vaapivp8enc";
- return res;
- }
- inline map<string, string> mfxDecodeElementByCodec()
- {
- map<string, string> res;
- res["h264"] = "parsebin ! mfxh264dec";
- res["h265"] = "parsebin ! mfxhevcdec";
- res["mpeg2"] = "parsebin ! mfxmpeg2dec";
- res["mjpeg"] = "parsebin ! mfxjpegdec";
- return res;
- }
- inline map<string, string> mfxEncodeElementByCodec()
- {
- map<string, string> res;
- res["h264"] = "mfxh264enc";
- res["h265"] = "mfxhevcenc";
- res["mpeg2"] = "mfxmpeg2enc";
- res["mjpeg"] = "mfxjpegenc";
- return res;
- }
- inline map<string, string> libavDecodeElementByCodec()
- {
- map<string, string> res;
- res["h264"] = "parsebin ! avdec_h264";
- res["h265"] = "parsebin ! avdec_h265";
- res["mpeg2"] = "parsebin ! avdec_mpeg2video";
- res["mpeg4"] = "parsebin ! avdec_mpeg4";
- res["mjpeg"] = "parsebin ! avdec_mjpeg";
- res["vp8"] = "parsebin ! avdec_vp8";
- return res;
- }
- inline map<string, string> libavEncodeElementByCodec()
- {
- map<string, string> res;
- res["h264"] = "avenc_h264";
- res["h265"] = "avenc_h265";
- res["mpeg2"] = "avenc_mpeg2video";
- res["mpeg4"] = "avenc_mpeg4";
- res["mjpeg"] = "avenc_mjpeg";
- res["vp8"] = "avenc_vp8";
- return res;
- }
- inline map<string, string> demuxPluginByContainer()
- {
- map<string, string> res;
- res["avi"] = "avidemux";
- res["mp4"] = "qtdemux";
- res["mov"] = "qtdemux";
- res["mkv"] = "matroskademux";
- return res;
- }
- inline map<string, string> muxPluginByContainer()
- {
- map<string, string> res;
- res["avi"] = "avimux";
- res["mp4"] = "qtmux";
- res["mov"] = "qtmux";
- res["mkv"] = "matroskamux";
- return res;
- }
- //================================================================================
- inline string containerByName(const string &name)
- {
- size_t found = name.rfind(".");
- if (found != string::npos)
- {
- return name.substr(found + 1); // container type
- }
- return string();
- }
- //================================================================================
- inline Ptr<VideoCapture> createCapture(const string &backend, const string &file_name, const string &codec)
- {
- if (backend == "gst-default")
- {
- cout << "Created GStreamer capture ( " << file_name << " )" << endl;
- return makePtr<VideoCapture>(file_name, CAP_GSTREAMER);
- }
- else if (backend.find("gst") == 0)
- {
- ostringstream line;
- line << "filesrc location=\"" << file_name << "\"";
- line << " ! ";
- line << getValue(demuxPluginByContainer(), containerByName(file_name), "Invalid container");
- line << " ! ";
- if (backend.find("basic") == 4)
- line << "decodebin";
- else if (backend.find("vaapi") == 4)
- line << "vaapidecodebin";
- else if (backend.find("libav") == 4)
- line << getValue(libavDecodeElementByCodec(), codec, "Invalid codec");
- else if (backend.find("mfx") == 4)
- line << getValue(mfxDecodeElementByCodec(), codec, "Invalid or unsupported codec");
- else
- return Ptr<VideoCapture>();
- line << " ! videoconvert n-threads=" << getNumThreads();
- line << " ! appsink sync=false";
- cout << "Created GStreamer capture ( " << line.str() << " )" << endl;
- return makePtr<VideoCapture>(line.str(), CAP_GSTREAMER);
- }
- else if (backend == "ffmpeg")
- {
- cout << "Created FFmpeg capture ( " << file_name << " )" << endl;
- return makePtr<VideoCapture>(file_name, CAP_FFMPEG);
- }
- return Ptr<VideoCapture>();
- }
- inline Ptr<VideoCapture> createSynthSource(Size sz, unsigned fps)
- {
- ostringstream line;
- line << "videotestsrc pattern=smpte";
- line << " ! video/x-raw";
- line << ",width=" << sz.width << ",height=" << sz.height;
- if (fps > 0)
- line << ",framerate=" << fps << "/1";
- line << " ! appsink sync=false";
- cout << "Created synthetic video source ( " << line.str() << " )" << endl;
- return makePtr<VideoCapture>(line.str(), CAP_GSTREAMER);
- }
- inline Ptr<VideoWriter> createWriter(const string &backend, const string &file_name, const string &codec, Size sz, unsigned fps)
- {
- if (backend == "gst-default")
- {
- cout << "Created GStreamer writer ( " << file_name << ", FPS=" << fps << ", Size=" << sz << ")" << endl;
- return makePtr<VideoWriter>(file_name, CAP_GSTREAMER, getValue(fourccByCodec(), codec, "Invalid codec"), fps, sz, true);
- }
- else if (backend.find("gst") == 0)
- {
- ostringstream line;
- line << "appsrc ! videoconvert n-threads=" << getNumThreads() << " ! ";
- if (backend.find("basic") == 4)
- line << getValue(defaultEncodeElementByCodec(), codec, "Invalid codec");
- else if (backend.find("vaapi") == 4)
- line << getValue(VAAPIEncodeElementByCodec(), codec, "Invalid codec");
- else if (backend.find("libav") == 4)
- line << getValue(libavEncodeElementByCodec(), codec, "Invalid codec");
- else if (backend.find("mfx") == 4)
- line << getValue(mfxEncodeElementByCodec(), codec, "Invalid codec");
- else
- return Ptr<VideoWriter>();
- line << " ! ";
- line << getValue(muxPluginByContainer(), containerByName(file_name), "Invalid container");
- line << " ! ";
- line << "filesink location=\"" << file_name << "\"";
- cout << "Created GStreamer writer ( " << line.str() << " )" << endl;
- return makePtr<VideoWriter>(line.str(), CAP_GSTREAMER, 0, fps, sz, true);
- }
- else if (backend == "ffmpeg")
- {
- cout << "Created FFmpeg writer ( " << file_name << ", FPS=" << fps << ", Size=" << sz << " )" << endl;
- return makePtr<VideoWriter>(file_name, CAP_FFMPEG, getValue(fourccByCodec(), codec, "Invalid codec"), fps, sz, true);
- }
- return Ptr<VideoWriter>();
- }
- //================================================================================
- int main(int argc, char *argv[])
- {
- const string keys =
- "{h help usage ? | | print help messages }"
- "{m mode |decode | coding mode (supported: encode, decode) }"
- "{b backend |default | video backend (supported: 'gst-default', 'gst-basic', 'gst-vaapi', 'gst-libav', 'gst-mfx', 'ffmpeg') }"
- "{c codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }"
- "{f file path | | path to file }"
- "{r resolution |720p | video resolution for encoding (supported: '720p', '1080p', '4k') }"
- "{fps |30 | fix frame per second for encoding (supported: fps > 0) }"
- "{fast | | fast measure fps }";
- CommandLineParser cmd_parser(argc, argv, keys);
- cmd_parser.about("This program measures performance of video encoding and decoding using different backends OpenCV.");
- if (cmd_parser.has("help"))
- {
- cmd_parser.printMessage();
- return 0;
- }
- bool fast_measure = cmd_parser.has("fast"); // fast measure fps
- unsigned fix_fps = cmd_parser.get<unsigned>("fps"); // fixed frame per second
- string backend = cmd_parser.get<string>("backend"); // video backend
- string mode = cmd_parser.get<string>("mode"); // coding mode
- string codec = cmd_parser.get<string>("codec"); // codec type
- string file_name = cmd_parser.get<string>("file"); // path to videofile
- string resolution = cmd_parser.get<string>("resolution"); // video resolution
- if (!cmd_parser.check())
- {
- cmd_parser.printErrors();
- return -1;
- }
- if (mode != "encode" && mode != "decode")
- {
- cout << "Unsupported mode: " << mode << endl;
- return -1;
- }
- if (mode == "decode")
- {
- file_name = samples::findFile(file_name);
- }
- cout << "Mode: " << mode << ", Backend: " << backend << ", File: " << file_name << ", Codec: " << codec << endl;
- TickMeter total;
- Ptr<VideoCapture> cap;
- Ptr<VideoWriter> wrt;
- try
- {
- if (mode == "decode")
- {
- cap = createCapture(backend, file_name, codec);
- if (!cap)
- {
- cout << "Failed to create video capture" << endl;
- return -3;
- }
- if (!cap->isOpened())
- {
- cout << "Capture is not opened" << endl;
- return -4;
- }
- }
- else if (mode == "encode")
- {
- Size sz = getValue(sizeByResolution(), resolution, "Invalid resolution");
- cout << "FPS: " << fix_fps << ", Frame size: " << sz << endl;
- cap = createSynthSource(sz, fix_fps);
- wrt = createWriter(backend, file_name, codec, sz, fix_fps);
- if (!cap || !wrt)
- {
- cout << "Failed to create synthetic video source or video writer" << endl;
- return -3;
- }
- if (!cap->isOpened() || !wrt->isOpened())
- {
- cout << "Synthetic video source or video writer is not opened" << endl;
- return -4;
- }
- }
- }
- catch (...)
- {
- cout << "Unsupported parameters" << endl;
- return -2;
- }
- TickMeter tick;
- Mat frame;
- Mat element;
- total.start();
- while(true)
- {
- if (mode == "decode")
- {
- tick.start();
- if (!cap->grab())
- {
- cout << "No more frames - break" << endl;
- break;
- }
- if (!cap->retrieve(frame))
- {
- cout << "Failed to retrieve frame - break" << endl;
- break;
- }
- if (frame.empty())
- {
- cout << "Empty frame received - break" << endl;
- break;
- }
- tick.stop();
- }
- else if (mode == "encode")
- {
- int limit = 100;
- while (!cap->grab() && --limit != 0)
- {
- cout << "Skipping empty input frame - " << limit << endl;
- }
- cap->retrieve(element);
- tick.start();
- *wrt << element;
- tick.stop();
- }
- if (fast_measure && tick.getCounter() >= 1000)
- {
- cout << "Fast mode frame limit reached - break" << endl;
- break;
- }
- if (mode == "encode" && tick.getCounter() >= 1000)
- {
- cout << "Encode frame limit reached - break" << endl;
- break;
- }
- }
- total.stop();
- if (tick.getCounter() == 0)
- {
- cout << "No frames have been processed" << endl;
- return -10;
- }
- else
- {
- double res_fps = tick.getCounter() / tick.getTimeSec();
- cout << tick.getCounter() << " frames in " << tick.getTimeSec() << " sec ~ " << res_fps << " FPS" << " (total time: " << total.getTimeSec() << " sec)" << endl;
- }
- return 0;
- }
|