From 1367a58b54ec284047a22c5591b1b6ba978bfef4 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 9 Apr 2018 01:27:55 +0300 Subject: [PATCH] Added encode-pipeline to sample --- samples/cpp/gstreamer_pipeline.cpp | 514 ++++++++++++++++++++++------- 1 file changed, 400 insertions(+), 114 deletions(-) diff --git a/samples/cpp/gstreamer_pipeline.cpp b/samples/cpp/gstreamer_pipeline.cpp index 4e3f108b48..19b6187ca1 100644 --- a/samples/cpp/gstreamer_pipeline.cpp +++ b/samples/cpp/gstreamer_pipeline.cpp @@ -8,144 +8,430 @@ using namespace std; using namespace cv; -string getGstDemuxPlugin(string container); -string getGstAvDecodePlugin(string codec); - -int main(int argc, char *argv[]) +class GStreamerPipeline { - const string keys = - "{h help usage ? | | print help messages }" - "{p pipeline |gst-default| pipeline name (supported: 'gst-default', 'gst-vaapi', 'gst-libav', 'ffmpeg') }" - "{ct container |mp4 | container name (supported: 'mp4', 'mov', 'avi', 'mkv') }" - "{cd codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }" - "{f file path | | path to file }" - "{fm fast | | fast measure fps }"; - - CommandLineParser parser(argc, argv, keys); - - parser.about("This program shows how to read a video file with GStreamer pipeline with OpenCV."); - - if (parser.has("help")) + public: + // Preprocessing arguments command line + GStreamerPipeline(int argc, char *argv[]) { - parser.printMessage(); - return 0; + const string keys = + "{h help usage ? | | print help messages }" + "{m mode | | coding mode (supported: encode, decode) }" + "{p pipeline |default | pipeline name (supported: 'default', 'gst-basic', 'gst-vaapi', 'gst-libav', 'ffmpeg') }" + "{ct container |mp4 | container name (supported: 'mp4', 'mov', 'avi', 'mkv') }" + "{cd codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }" + "{f file path | | path to file }" + "{vr resolution |720p | video resolution for encoding (supported: '720p', '1080p', '4k') }" + "{fps |30 | fix frame per second for encoding (supported: fps > 0) }" + "{fm fast | | fast measure fps }"; + cmd_parser = new CommandLineParser(argc, argv, keys); + cmd_parser->about("This program shows how to read a video file with GStreamer pipeline with OpenCV."); + + if (cmd_parser->has("help")) + { + cmd_parser->printMessage(); + exit_code = -1; + } + + fast_measure = cmd_parser->has("fast"); // fast measure fps + fix_fps = cmd_parser->get("fps"); // fixed frame per second + pipeline = cmd_parser->get("pipeline"), // gstreamer pipeline type + container = cmd_parser->get("container"), // container type + mode = cmd_parser->get("mode"), // coding mode + codec = cmd_parser->get("codec"), // codec type + file_name = cmd_parser->get("file"), // path to videofile + resolution = cmd_parser->get("resolution"); // video resolution + + if (!cmd_parser->check()) + { + cmd_parser->printErrors(); + exit_code = -1; + } + exit_code = 0; } - bool arg_fast_measure = parser.has("fast"); // fast measure fps - string arg_pipeline = parser.get("pipeline"), // GStreamer pipeline type - arg_container = parser.get("container"), // container type - arg_codec = parser.get("codec"), // codec type - arg_file_name = parser.get("file"); // path to videofile - VideoCapture cap; + ~GStreamerPipeline() { delete cmd_parser; } - if (!parser.check()) + // Start pipeline + int run() { - parser.printErrors(); - return 0; - } - - // Choose the constructed GStreamer pipeline - if (arg_pipeline.find("gst") == 0) - { - ostringstream pipeline; - pipeline << "filesrc location=\"" << arg_file_name << "\""; - pipeline << " ! " << getGstDemuxPlugin(arg_container); - - if (arg_pipeline.find("default") == 4) { - pipeline << " ! decodebin"; - } - else if (arg_pipeline.find("vaapi1710") == 4) + if (exit_code < 0) { return exit_code; } + if (mode == "decode") { if (createDecodePipeline() < 0) return -1; } + else if (mode == "encode") { if (createEncodePipeline() < 0) return -1; } + else { - pipeline << " ! vaapidecodebin"; - if (arg_container == "mkv") - { - pipeline << " ! autovideoconvert"; - } - else - { - pipeline << " ! video/x-raw, format=YV12"; - } + cout << "Unsupported mode: " << mode << endl; + cmd_parser->printErrors(); + return -1; } - else if (arg_pipeline.find("libav") == 4) + cout << "_____________________________________" << endl; + cout << "Pipeline " << mode << ":" << endl; + cout << stream_pipeline.str() << endl; + // Choose a show video or only measure fps + cout << "_____________________________________" << endl; + cout << "Start measure frame per seconds (fps)" << endl; + cout << "Loading ..." << endl; + + vector tick_counts; + + cout << "Start " << mode << ": " << file_name; + cout << " (" << pipeline << ")" << endl; + + while(true) { - pipeline << " ! " << getGstAvDecodePlugin(arg_codec); + int64 temp_count_tick = 0; + if (mode == "decode") + { + Mat frame; + temp_count_tick = getTickCount(); + cap >> frame; + temp_count_tick = getTickCount() - temp_count_tick; + if (frame.empty()) { break; } + } + else if (mode == "encode") + { + Mat element; + while(!cap.grab()); + cap.retrieve(element); + temp_count_tick = getTickCount(); + wrt << element; + temp_count_tick = getTickCount() - temp_count_tick; + } + + tick_counts.push_back(static_cast(temp_count_tick)); + if (((mode == "decode") && fast_measure && (tick_counts.size() > 1e3)) || + ((mode == "encode") && (tick_counts.size() > 3e3)) || + ((mode == "encode") && fast_measure && (tick_counts.size() > 1e2))) + { break; } + + } + double time_fps = sum(tick_counts)[0] / getTickFrequency(); + + if (tick_counts.size() != 0) + { + cout << "Finished: " << tick_counts.size() << " in " << time_fps <<" sec ~ " ; + cout << tick_counts.size() / time_fps <<" fps " << endl; } else { - parser.printMessage(); - cout << "Unsupported pipeline: " << arg_pipeline << endl; - return -4; + cout << "Failed " << mode << ": " << file_name; + cout << " (" << pipeline << ")" << endl; + return -1; + } + return 0; + } + + // Free video resource + void close() + { + cap.release(); + wrt.release(); + } + + private: + // Choose the constructed GStreamer pipeline for decode + int createDecodePipeline() + { + if (pipeline == "default") { + cap = VideoCapture(file_name, CAP_GSTREAMER); + } + else if (pipeline.find("gst") == 0) + { + stream_pipeline << "filesrc location=\"" << file_name << "\""; + stream_pipeline << " ! " << getGstMuxPlugin(); + + if (pipeline.find("basic") == 4) + { + stream_pipeline << getGstDefaultCodePlugin(); + } + else if (pipeline.find("vaapi1710") == 4) + { + stream_pipeline << getGstVaapiCodePlugin(); + } + else if (pipeline.find("libav") == 4) + { + stream_pipeline << getGstAvCodePlugin(); + } + else + { + cout << "Unsupported pipeline: " << pipeline << endl; + cmd_parser->printErrors(); + return -1; + } + + stream_pipeline << " ! videoconvert n-threads=" << getNumThreads(); + stream_pipeline << " ! appsink sync=false"; + cap = VideoCapture(stream_pipeline.str(), CAP_GSTREAMER); + } + else if (pipeline == "ffmpeg") + { + cap = VideoCapture(file_name, CAP_FFMPEG); + stream_pipeline << "default pipeline for ffmpeg" << endl; + } + else + { + cout << "Unsupported pipeline: " << pipeline << endl; + cmd_parser->printErrors(); + return -1; + } + return 0; + } + + // Choose the constructed GStreamer pipeline for encode + int createEncodePipeline() + { + if (checkConfiguration() < 0) return -1; + ostringstream test_pipeline; + test_pipeline << "videotestsrc pattern=smpte"; + test_pipeline << " ! video/x-raw, " << getVideoSettings(); + test_pipeline << " ! appsink sync=false"; + cap = VideoCapture(test_pipeline.str(), CAP_GSTREAMER); + + if (pipeline == "default") { + wrt = VideoWriter(file_name, CAP_GSTREAMER, getFourccCode(), fix_fps, fix_size, true); + } + else if (pipeline.find("gst") == 0) + { + stream_pipeline << "appsrc ! videoconvert n-threads=" << getNumThreads() << " ! "; + + if (pipeline.find("basic") == 4) + { + stream_pipeline << getGstDefaultCodePlugin(); + } + else if (pipeline.find("vaapi1710") == 4) + { + stream_pipeline << getGstVaapiCodePlugin(); + } + else if (pipeline.find("libav") == 4) + { + stream_pipeline << getGstAvCodePlugin(); + } + else + { + cout << "Unsupported pipeline: " << pipeline << endl; + cmd_parser->printErrors(); + return -1; + } + + stream_pipeline << " ! " << getGstMuxPlugin(); + stream_pipeline << " ! filesink location=\"" << file_name << "\""; + wrt = VideoWriter(stream_pipeline.str(), CAP_GSTREAMER, 0, fix_fps, fix_size, true); + } + else if (pipeline == "ffmpeg") + { + wrt = VideoWriter(file_name, CAP_FFMPEG, getFourccCode(), fix_fps, fix_size, true); + stream_pipeline << "default pipeline for ffmpeg" << endl; + } + else + { + cout << "Unsupported pipeline: " << pipeline << endl; + cmd_parser->printErrors(); + return -1; + } + return 0; + } + + // Choose video resolution for encoding + string getVideoSettings() + { + ostringstream video_size; + if (fix_fps > 0) { video_size << "framerate=" << fix_fps << "/1, "; } + else + { + cout << "Unsupported fps (< 0): " << fix_fps << endl; + cmd_parser->printErrors(); + return string(); } - pipeline << " ! videoconvert"; - pipeline << " n-threads=" << getNumThreads(); - pipeline << " ! appsink sync=false"; - cap = VideoCapture(pipeline.str(), CAP_GSTREAMER); + if (resolution == "720p") { fix_size = Size(1280, 720); } + else if (resolution == "1080p") { fix_size = Size(1920, 1080); } + else if (resolution == "4k") { fix_size = Size(3840, 2160); } + else + { + cout << "Unsupported video resolution: " << resolution << endl; + cmd_parser->printErrors(); + return string(); + } + + video_size << "width=" << fix_size.width << ", height=" << fix_size.height; + return video_size.str(); } - else if (arg_pipeline == "ffmpeg") + + // Choose a video container + string getGstMuxPlugin() { - cap = VideoCapture(arg_file_name, CAP_FFMPEG); + ostringstream plugin; + if (container == "avi") { plugin << "avi"; } + else if (container == "mp4") { plugin << "qt"; } + else if (container == "mov") { plugin << "qt"; } + else if (container == "mkv") { plugin << "matroska"; } + else + { + cout << "Unsupported container: " << container << endl; + cmd_parser->printErrors(); + return string(); + } + + if (mode == "decode") { plugin << "demux"; } + else if (mode == "encode") { plugin << "mux"; } + else + { + cout << "Unsupported mode: " << mode << endl; + cmd_parser->printErrors(); + return string(); + } + + return plugin.str(); } - else + + // Choose a libav codec + string getGstAvCodePlugin() { - parser.printMessage(); - cout << "Unsupported pipeline: " << arg_pipeline << endl; - return -4; + ostringstream plugin; + if (mode == "decode") + { + if (codec == "h264") { plugin << "h264parse ! "; } + else if (codec == "h265") { plugin << "h265parse ! "; } + plugin << "avdec_"; + } + else if (mode == "encode") { plugin << "avenc_"; } + else + { + cout << "Unsupported mode: " << mode << endl; + cmd_parser->printErrors(); + return string(); + } + + if (codec == "h264") { plugin << "h264"; } + else if (codec == "h265") { plugin << "h265"; } + else if (codec == "mpeg2") { plugin << "mpeg2video"; } + else if (codec == "mpeg4") { plugin << "mpeg4"; } + else if (codec == "mjpeg") { plugin << "mjpeg"; } + else if (codec == "vp8") { plugin << "vp8"; } + else + { + cout << "Unsupported libav codec: " << codec << endl; + cmd_parser->printErrors(); + return string(); + } + + return plugin.str(); } - // Choose a show video or only measure fps - cout << "_____________________________________" << '\n'; - cout << "Start measure frame per seconds (fps)" << '\n'; - cout << "Loading ..." << '\n'; - - Mat frame; - vector tick_counts; - - cout << "Start decoding: " << arg_file_name; - cout << " (" << arg_pipeline << ")" << endl; - - while(true) + // Choose a vaapi codec + string getGstVaapiCodePlugin() { - int64 temp_count_tick = getTickCount(); - cap >> frame; - temp_count_tick = getTickCount() - temp_count_tick; - if (frame.empty()) { break; } - tick_counts.push_back(static_cast(temp_count_tick)); - if (arg_fast_measure && (tick_counts.size() > 1000)) { break; } + ostringstream plugin; + if (mode == "decode") + { + plugin << "vaapidecodebin"; + if (container == "mkv") { plugin << " ! autovideoconvert"; } + else { plugin << " ! video/x-raw, format=YV12"; } + } + else if (mode == "encode") + { + if (codec == "h264") { plugin << "vaapih264enc"; } + else if (codec == "h265") { plugin << "vaapih265enc"; } + else if (codec == "mpeg2") { plugin << "vaapimpeg2enc"; } + else if (codec == "mjpeg") { plugin << "vaapijpegenc"; } + else if (codec == "vp8") { plugin << "vaapivp8enc"; } + else + { + cout << "Unsupported vaapi codec: " << codec << endl; + cmd_parser->printErrors(); + return string(); + } + } + else + { + cout << "Unsupported mode: " << resolution << endl; + cmd_parser->printErrors(); + return string(); + } + return plugin.str(); + } + // Choose a default codec + string getGstDefaultCodePlugin() + { + ostringstream plugin; + if (mode == "decode") + { + plugin << " ! decodebin"; + } + else if (mode == "encode") + { + if (codec == "h264") { plugin << "x264enc"; } + else if (codec == "h265") { plugin << "x265enc"; } + else if (codec == "mpeg2") { plugin << "mpeg2enc"; } + else if (codec == "mjpeg") { plugin << "jpegenc"; } + else if (codec == "vp8") { plugin << "vp8enc"; } + else + { + cout << "Unsupported default codec: " << codec << endl; + cmd_parser->printErrors(); + return string(); + } + } + else + { + cout << "Unsupported mode: " << resolution << endl; + cmd_parser->printErrors(); + return string(); + } + return plugin.str(); + } + // Get fourcc for codec + int getFourccCode() + { + if (codec == "h264") { return VideoWriter::fourcc('H','2','6','4'); } + else if (codec == "h265") { return VideoWriter::fourcc('H','E','V','C'); } + else if (codec == "mpeg2") { return VideoWriter::fourcc('M','P','E','G'); } + else if (codec == "mpeg4") { return VideoWriter::fourcc('M','P','4','2'); } + else if (codec == "mjpeg") { return VideoWriter::fourcc('M','J','P','G'); } + else if (codec == "vp8") { return VideoWriter::fourcc('V','P','8','0'); } + else + { + cout << "Unsupported ffmpeg codec: " << codec << endl; + cmd_parser->printErrors(); + return 0; + } } - double time_fps = sum(tick_counts)[0] / getTickFrequency(); - if (tick_counts.size() != 0) + // Check bad configuration + int checkConfiguration() { - cout << "Finished: " << tick_counts.size() << " in " << time_fps <<" sec ~ " ; - cout << tick_counts.size() / time_fps <<" fps " << endl; + if ((codec == "mpeg2" && getGstMuxPlugin() == "qtmux") || + (codec == "h265" && getGstMuxPlugin() == "avimux") || + (pipeline == "gst-libav" && (codec == "h264" || codec == "h265")) || + (pipeline == "gst-vaapi1710" && codec=="mpeg2" && resolution=="4k") || + (pipeline == "gst-vaapi1710" && codec=="mpeg2" && resolution=="1080p" && fix_fps > 30)) + { + cout << "Unsupported configuration" << endl; + cmd_parser->printErrors(); + return -1; + } + return 0; } - else - { - cout << "Failed decoding: " << arg_file_name; - cout << " (" << arg_pipeline << ")" << endl; - return -5; - } - return 0; -} - -// Choose a video container -string getGstDemuxPlugin(string container) { - if (container == "avi") { return "avidemux"; } - else if (container == "mp4") { return "qtdemux"; } - else if (container == "mov") { return "qtdemux"; } - else if (container == "mkv") { return "matroskademux"; } - return string(); -} - -// Choose a codec -string getGstAvDecodePlugin(string codec) { - if (codec == "h264") { return "h264parse ! avdec_h264"; } - else if (codec == "h265") { return "h265parse ! avdec_h265"; } - else if (codec == "mpeg2") { return "avdec_mpeg2video"; } - else if (codec == "mpeg4") { return "avdec_mpeg4"; } - else if (codec == "mjpeg") { return "avdec_mjpeg"; } - else if (codec == "vp8") { return "avdec_vp8"; } - return string(); + + bool fast_measure; // fast measure fps + string pipeline, // gstreamer pipeline type + container, // container type + mode, // coding mode + codec, // codec type + file_name, // path to videofile + resolution; // video resolution + int fix_fps; // fixed frame per second + Size fix_size; // fixed frame size + int exit_code; + VideoWriter wrt; + VideoCapture cap; + ostringstream stream_pipeline; + CommandLineParser* cmd_parser; +}; + +int main(int argc, char *argv[]) +{ + GStreamerPipeline pipe(argc, argv); + return pipe.run(); }