diff --git a/CMakeLists.txt b/CMakeLists.txt index 16fe3eca9b487b486db06bc31dd8612445ab3c04..08bed826ba27a940a744013d0dae626294d4e991 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,6 +252,7 @@ OCV_OPTION(WITH_IPP_A "Include Intel IPP_A support" OFF OCV_OPTION(WITH_MATLAB "Include Matlab support" ON IF (NOT ANDROID AND NOT IOS AND NOT WINRT)) OCV_OPTION(WITH_VA "Include VA support" OFF IF (UNIX AND NOT ANDROID) ) OCV_OPTION(WITH_VA_INTEL "Include Intel VA-API/OpenCL support" OFF IF (UNIX AND NOT ANDROID) ) +OCV_OPTION(WITH_MFX "Include Intel Media SDK support" OFF IF (UNIX AND NOT ANDROID) ) OCV_OPTION(WITH_GDAL "Include GDAL Support" OFF IF (NOT ANDROID AND NOT IOS AND NOT WINRT) ) OCV_OPTION(WITH_GPHOTO2 "Include gPhoto2 library support" ON IF (UNIX AND NOT ANDROID) ) OCV_OPTION(WITH_LAPACK "Include Lapack library support" ON IF (NOT ANDROID AND NOT IOS) ) @@ -1228,6 +1229,10 @@ if(DEFINED WITH_INTELPERC) status(" Intel PerC:" HAVE_INTELPERC THEN "YES" ELSE NO) endif(DEFINED WITH_INTELPERC) +if(DEFINED WITH_MFX) + status(" Intel Media SDK:" HAVE_MFX THEN "YES (${MFX_LIBRARY})" ELSE NO) +endif() + if(DEFINED WITH_GPHOTO2) status(" gPhoto2:" HAVE_GPHOTO2 THEN "YES" ELSE NO) endif(DEFINED WITH_GPHOTO2) diff --git a/cmake/OpenCVDetectMediaSDK.cmake b/cmake/OpenCVDetectMediaSDK.cmake new file mode 100644 index 0000000000000000000000000000000000000000..bc0b2ed3ea581fbb01c5a07bfe97f49ab27715ae --- /dev/null +++ b/cmake/OpenCVDetectMediaSDK.cmake @@ -0,0 +1,38 @@ +set(root "$ENV{MFX_HOME}") + +find_path(MFX_INCLUDE mfxdefs.h PATHS "${root}/include" NO_DEFAULT_PATH) + +# TODO: ICC? MINGW? ARM? IOS? +if(WIN32) + if(X86_64) + set(arch "x64") + else() + set(arch "win32") + endif() +elseif(UNIX) + set(arch "lin_x64") +else() + # ??? +endif() + +find_library(MFX_LIBRARY mfx PATHS "${root}/lib/${arch}" NO_DEFAULT_PATH) +find_library(MFX_VA_LIBRARY va) +find_library(MFX_VA_DRM_LIBRARY va-drm) + +if(MFX_INCLUDE AND MFX_LIBRARY AND MFX_VA_LIBRARY AND MFX_VA_DRM_LIBRARY) + add_library(mfx-va UNKNOWN IMPORTED) + set_target_properties(mfx-va PROPERTIES IMPORTED_LOCATION "${MFX_VA_LIBRARY}") + + add_library(mfx-va-drm UNKNOWN IMPORTED) + set_target_properties(mfx-va-drm PROPERTIES IMPORTED_LOCATION "${MFX_VA_DRM_LIBRARY}") + + add_library(mfx UNKNOWN IMPORTED) + set_target_properties(mfx PROPERTIES + IMPORTED_LOCATION "${MFX_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${MFX_INCLUDE}" + INTERFACE_LINK_LIBRARIES "mfx-va;mfx-va-drm;-Wl,--exclude-libs=libmfx" + ) + set(HAVE_MFX 1) +else() + set(HAVE_MFX 0) +endif() diff --git a/cmake/OpenCVFindLibsVideo.cmake b/cmake/OpenCVFindLibsVideo.cmake index 13b62ac4dd5b79f0b5ddcaa0f609a8a742e7ef20..ca52a05de68c40fdc0f83f68dae55e1f1534d78a 100644 --- a/cmake/OpenCVFindLibsVideo.cmake +++ b/cmake/OpenCVFindLibsVideo.cmake @@ -294,6 +294,10 @@ if(WITH_INTELPERC) include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindIntelPerCSDK.cmake") endif(WITH_INTELPERC) +if(WITH_MFX) + include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectMediaSDK.cmake") +endif() + # --- gPhoto2 --- ocv_clear_vars(HAVE_GPHOTO2) if(WITH_GPHOTO2) diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index 267aebf513d2ebf5f4c709cfe92c3bf3313bac65..754409a6b287eff5973eae47f230888952c658a7 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -205,6 +205,9 @@ /* Intel VA-API/OpenCL */ #cmakedefine HAVE_VA_INTEL +/* Intel Media SDK */ +#cmakedefine HAVE_MFX + /* Lapack */ #cmakedefine HAVE_LAPACK diff --git a/modules/videoio/CMakeLists.txt b/modules/videoio/CMakeLists.txt index 06279a08cf656c584c6c48cb45a882f758d83169..89a375781e6d94bec83178a3b83dc4945793a3f7 100644 --- a/modules/videoio/CMakeLists.txt +++ b/modules/videoio/CMakeLists.txt @@ -61,6 +61,16 @@ if(DEFINED WINRT AND NOT DEFINED WINRT_8_0 AND NOT DEFINED ENABLE_WINRT_MODE_NAT ${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt/MediaStreamSink.hpp) endif() +if(HAVE_MFX) + list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_common.cpp) + list(APPEND videoio_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_common.hpp) + list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_reader.cpp) + list(APPEND videoio_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_reader.hpp) + list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_writer.cpp) + list(APPEND videoio_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_writer.hpp) + list(APPEND VIDEOIO_LIBRARIES mfx) +endif() + if(WIN32 AND NOT ARM) list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_cmu.cpp) endif() diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index d480624b98d66c30ac109ebaa955ad3a276c48ec..149e7a6e643ccdc31d692032e6d40a0b662369bd 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -115,7 +115,8 @@ enum VideoCaptureAPIs { CAP_FFMPEG = 1900, //!< Open and record video file or stream using the FFMPEG library CAP_IMAGES = 2000, //!< OpenCV Image Sequence (e.g. img_%02d.jpg) CAP_ARAVIS = 2100, //!< Aravis SDK - CAP_OCV_MJPEG = 2200 //!< Built-in MotionJPEG codec + CAP_OCV_MJPEG = 2200, //!< Built-in MotionJPEG codec + CAP_INTEL_MFX = 2300 //!< Intel MediaSDK }; /** @brief %VideoCapture generic properties identifier. diff --git a/modules/videoio/src/cap.cpp b/modules/videoio/src/cap.cpp index 95a23e730d2503713610c46d4894c16d4aedde84..6de3994a250a8fe059b0a8c4b3d8caf72ffe8ef5 100644 --- a/modules/videoio/src/cap.cpp +++ b/modules/videoio/src/cap.cpp @@ -43,6 +43,11 @@ #include "cap_intelperc.hpp" #include "cap_dshow.hpp" +#ifdef HAVE_MFX +#include "cap_mfx_reader.hpp" +#include "cap_mfx_writer.hpp" +#endif + // All WinRT versions older than 8.0 should provide classes used for video support #if defined(WINRT) && !defined(WINRT_8_0) && defined(__cplusplus_winrt) # include "cap_winrt_capture.hpp" @@ -525,6 +530,9 @@ static Ptr IVideoCapture_create(const String& filename) CV_CAP_ANY, #ifdef HAVE_GPHOTO2 CV_CAP_GPHOTO2, +#endif +#ifdef HAVE_MFX + CAP_INTEL_MFX, #endif -1, -1 }; @@ -543,6 +551,11 @@ static Ptr IVideoCapture_create(const String& filename) case CV_CAP_GPHOTO2: capture = createGPhoto2Capture(filename); break; +#endif +#ifdef HAVE_MFX + case CAP_INTEL_MFX: + capture = makePtr(filename); + break; #endif } @@ -558,9 +571,19 @@ static Ptr IVideoCapture_create(const String& filename) static Ptr IVideoWriter_create(int apiPreference, const String& filename, int _fourcc, double fps, Size frameSize, bool isColor) { Ptr iwriter; +#ifdef HAVE_MFX + if (apiPreference == CAP_INTEL_MFX || apiPreference == CAP_ANY) + { + iwriter = VideoWriter_IntelMFX::create(filename, _fourcc, fps, frameSize, isColor); + if (!iwriter.empty()) + return iwriter; + } +#endif + if( (apiPreference == CAP_OCV_MJPEG || apiPreference == CAP_ANY) && _fourcc == CV_FOURCC('M', 'J', 'P', 'G') ) iwriter = createMotionJpegWriter(filename, fps, frameSize, isColor); + return iwriter; } diff --git a/modules/videoio/src/cap_mfx_common.cpp b/modules/videoio/src/cap_mfx_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dda0cc226c8e8eb621cccc323c8abdf5e375a951 --- /dev/null +++ b/modules/videoio/src/cap_mfx_common.cpp @@ -0,0 +1,183 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#include "cap_mfx_common.hpp" + +// Linux specific +#include +#include +#include +#include + +using namespace std; +using namespace cv; + +bool DeviceHandler::init(MFXVideoSession &session) +{ + mfxStatus res = MFX_ERR_NONE; + mfxIMPL impl = MFX_IMPL_AUTO; + mfxVersion ver = { {19, 1} }; + + res = session.Init(impl, &ver); + DBG(cout << "MFX SessionInit: " << res << endl); + + res = session.QueryIMPL(&impl); + DBG(cout << "MFX QueryIMPL: " << res << " => " << asHex(impl) << endl); + + res = session.QueryVersion(&ver); + DBG(cout << "MFX QueryVersion: " << res << " => " << ver.Major << "." << ver.Minor << endl); + + if (res != MFX_ERR_NONE) + return false; + + return initDeviceSession(session); +} + +//================================================================================================== + +VAHandle::VAHandle() { + // TODO: provide a way of modifying this path + const string filename = "/dev/dri/card0"; + file = open(filename.c_str(), O_RDWR); + if (file < 0) + CV_Error(Error::StsError, "Can't open file: " + filename); + display = vaGetDisplayDRM(file); +} + +VAHandle::~VAHandle() { + if (display) { + vaTerminate(display); + } + if (file >= 0) { + close(file); + } +} + +bool VAHandle::initDeviceSession(MFXVideoSession &session) { + int majorVer = 0, minorVer = 0; + VAStatus va_res = vaInitialize(display, &majorVer, &minorVer); + DBG(cout << "vaInitialize: " << va_res << endl << majorVer << '.' << minorVer << endl); + if (va_res == VA_STATUS_SUCCESS) { + mfxStatus mfx_res = session.SetHandle(static_cast(MFX_HANDLE_VA_DISPLAY), display); + DBG(cout << "MFX SetHandle: " << mfx_res << endl); + if (mfx_res == MFX_ERR_NONE) { + return true; + } + } + return false; +} + +//================================================================================================== + +SurfacePool::SurfacePool(ushort width_, ushort height_, ushort count, const mfxFrameInfo &frameInfo, uchar bpp) + : width(alignSize(width_, 32)), + height(alignSize(height_, 32)), + oneSize(width * height * bpp / 8), + buffers(count * oneSize), + surfaces(count) +{ + for(int i = 0; i < count; ++i) + { + mfxFrameSurface1 &surface = surfaces[i]; + uint8_t * dataPtr = buffers + oneSize * i; + memset(&surface, 0, sizeof(mfxFrameSurface1)); + surface.Info = frameInfo; + surface.Data.Y = dataPtr; + surface.Data.UV = dataPtr + width * height; + surface.Data.Pitch = width; + DBG(cout << "allocate surface " << (void*)&surface << ", Y = " << (void*)dataPtr << " (" << width << "x" << height << ")" << endl); + } + DBG(cout << "Allocated: " << endl + << "- surface data: " << buffers.size() << " bytes" << endl + << "- surface headers: " << surfaces.size() * sizeof(mfxFrameSurface1) << " bytes" << endl); +} + +SurfacePool::~SurfacePool() +{ +} + +mfxFrameSurface1 *SurfacePool::getFreeSurface() +{ + for(std::vector::iterator i = surfaces.begin(); i != surfaces.end(); ++i) + if (!i->Data.Locked) + return &(*i); + return 0; +} + +//================================================================================================== + +ReadBitstream::ReadBitstream(const char *filename, size_t maxSize) : drain(false) +{ + input.open(filename, std::ios::in | std::ios::binary); + DBG(cout << "Open " << filename << " -> " << input.is_open() << std::endl); + memset(&stream, 0, sizeof(stream)); + stream.MaxLength = maxSize; + stream.Data = new mfxU8[stream.MaxLength]; + CV_Assert(stream.Data); +} + +ReadBitstream::~ReadBitstream() +{ + delete[] stream.Data; +} + +bool ReadBitstream::isOpened() const +{ + return input.is_open(); +} + +bool ReadBitstream::isDone() const +{ + return input.eof(); +} + +bool ReadBitstream::read() +{ + memmove(stream.Data, stream.Data + stream.DataOffset, stream.DataLength); + stream.DataOffset = 0; + input.read((char*)(stream.Data + stream.DataLength), stream.MaxLength - stream.DataLength); + if (input.eof() || input.good()) + { + mfxU32 bytesRead = input.gcount(); + if (bytesRead > 0) + { + stream.DataLength += bytesRead; + DBG(cout << "read " << bytesRead << " bytes" << endl); + return true; + } + } + return false; +} + +//================================================================================================== + +WriteBitstream::WriteBitstream(const char * filename, size_t maxSize) +{ + output.open(filename, std::ios::out | std::ios::binary); + DBG(cout << "BS Open " << filename << " -> " << output.is_open() << std::endl); + memset(&stream, 0, sizeof(stream)); + stream.MaxLength = maxSize; + stream.Data = new mfxU8[stream.MaxLength]; + DBG(cout << "BS Allocate " << maxSize << " bytes (" << ((float)maxSize / (1 << 20)) << " Mb)" << endl); + CV_Assert(stream.Data); +} + +WriteBitstream::~WriteBitstream() +{ + delete[] stream.Data; +} + +bool WriteBitstream::write() +{ + output.write((char*)(stream.Data + stream.DataOffset), stream.DataLength); + stream.DataLength = 0; + return output.good(); +} + +bool WriteBitstream::isOpened() const +{ + return output.is_open(); +} + +//================================================================================================== diff --git a/modules/videoio/src/cap_mfx_common.hpp b/modules/videoio/src/cap_mfx_common.hpp new file mode 100644 index 0000000000000000000000000000000000000000..aea0164f7e2ba1c0cca758f5da979bdf197aed48 --- /dev/null +++ b/modules/videoio/src/cap_mfx_common.hpp @@ -0,0 +1,318 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef MFXHELPER_H +#define MFXHELPER_H + +#include "opencv2/core.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// // +// Debug helpers // +// // + +#if 0 +# define DBG(i) i +#else +# define DBG(i) +#endif + +#if 1 +# define MSG(i) i +#else +# define MSG(i) +#endif + +template +struct HexWrap { + HexWrap(T val_) : val(val_) {} + T val; +}; + +template +inline std::ostream & operator<<(std::ostream &out, const HexWrap &wrap) { + std::ios_base::fmtflags flags = out.flags(std::ios::hex | std::ios::showbase); + out << wrap.val; + out.flags(flags); + return out; +} + +template +inline ::HexWrap asHex(const T & val) { + return ::HexWrap(val); +} + +struct FourCC +{ + FourCC(uint val) : val32(val) {} + FourCC(char a, char b, char c, char d) { val8[0] = a; val8[1] = b; val8[2] = c; val8[3] = d; } + union { + uint val32; + int vali32; + uchar val8[4]; + }; +}; + +inline std::ostream & operator<<(std::ostream &out, FourCC cc) { + for (size_t i = 0; i < 4; out << cc.val8[i++]) {} + out << " (" << asHex(cc.val32) << ")"; + return out; +} + +inline std::string mfxStatusToString(mfxStatus s) { + switch (s) + { + case MFX_ERR_NONE: return "MFX_ERR_NONE"; + case MFX_ERR_UNKNOWN: return "MFX_ERR_UNKNOWN"; + case MFX_ERR_NULL_PTR: return "MFX_ERR_NULL_PTR"; + case MFX_ERR_UNSUPPORTED: return "MFX_ERR_UNSUPPORTED"; + case MFX_ERR_MEMORY_ALLOC: return "MFX_ERR_MEMORY_ALLOC"; + case MFX_ERR_NOT_ENOUGH_BUFFER: return "MFX_ERR_NOT_ENOUGH_BUFFER"; + case MFX_ERR_INVALID_HANDLE: return "MFX_ERR_INVALID_HANDLE"; + case MFX_ERR_LOCK_MEMORY: return "MFX_ERR_LOCK_MEMORY"; + case MFX_ERR_NOT_INITIALIZED: return "MFX_ERR_NOT_INITIALIZED"; + case MFX_ERR_NOT_FOUND: return "MFX_ERR_NOT_FOUND"; + case MFX_ERR_MORE_DATA: return "MFX_ERR_MORE_DATA"; + case MFX_ERR_MORE_SURFACE: return "MFX_ERR_MORE_SURFACE"; + case MFX_ERR_ABORTED: return "MFX_ERR_ABORTED"; + case MFX_ERR_DEVICE_LOST: return "MFX_ERR_DEVICE_LOST"; + case MFX_ERR_INCOMPATIBLE_VIDEO_PARAM: return "MFX_ERR_INCOMPATIBLE_VIDEO_PARAM"; + case MFX_ERR_INVALID_VIDEO_PARAM: return "MFX_ERR_INVALID_VIDEO_PARAM"; + case MFX_ERR_UNDEFINED_BEHAVIOR: return "MFX_ERR_UNDEFINED_BEHAVIOR"; + case MFX_ERR_DEVICE_FAILED: return "MFX_ERR_DEVICE_FAILED"; + case MFX_ERR_MORE_BITSTREAM: return "MFX_ERR_MORE_BITSTREAM"; + case MFX_ERR_INCOMPATIBLE_AUDIO_PARAM: return "MFX_ERR_INCOMPATIBLE_AUDIO_PARAM"; + case MFX_ERR_INVALID_AUDIO_PARAM: return "MFX_ERR_INVALID_AUDIO_PARAM"; + case MFX_ERR_GPU_HANG: return "MFX_ERR_GPU_HANG"; + case MFX_ERR_REALLOC_SURFACE: return "MFX_ERR_REALLOC_SURFACE"; + case MFX_WRN_IN_EXECUTION: return "MFX_WRN_IN_EXECUTION"; + case MFX_WRN_DEVICE_BUSY: return "MFX_WRN_DEVICE_BUSY"; + case MFX_WRN_VIDEO_PARAM_CHANGED: return "MFX_WRN_VIDEO_PARAM_CHANGED"; + case MFX_WRN_PARTIAL_ACCELERATION: return "MFX_WRN_PARTIAL_ACCELERATION"; + case MFX_WRN_INCOMPATIBLE_VIDEO_PARAM: return "MFX_WRN_INCOMPATIBLE_VIDEO_PARAM"; + case MFX_WRN_VALUE_NOT_CHANGED: return "MFX_WRN_VALUE_NOT_CHANGED"; + case MFX_WRN_OUT_OF_RANGE: return "MFX_WRN_OUT_OF_RANGE"; + case MFX_WRN_FILTER_SKIPPED: return "MFX_WRN_FILTER_SKIPPED"; + case MFX_WRN_INCOMPATIBLE_AUDIO_PARAM: return "MFX_WRN_INCOMPATIBLE_AUDIO_PARAM"; + default: return ""; + } +} + +inline std::ostream & operator<<(std::ostream &out, mfxStatus s) { + out << mfxStatusToString(s) << " (" << (int)s << ")"; return out; +} + +inline std::ostream & operator<<(std::ostream &out, const mfxInfoMFX &info) { + out << "InfoMFX:" << std::endl + << "| Codec: " << FourCC(info.CodecId) << " / " << info.CodecProfile << " / " << info.CodecLevel << std::endl + << "| DecodedOrder: " << info.DecodedOrder << std::endl + << "| TimeStampCalc: " << info.TimeStampCalc << std::endl + ; + return out; +} + +inline std::ostream & operator<<(std::ostream & out, const mfxFrameInfo & info) { + out << "FrameInfo: " << std::endl + << "| FourCC: " << FourCC(info.FourCC) << std::endl + << "| Size: " << info.Width << "x" << info.Height << std::endl + << "| ROI: " << "(" << info.CropX << ";" << info.CropY << ") " << info.CropW << "x" << info.CropH << std::endl + << "| BitDepth(L/C): " << info.BitDepthLuma << " / " << info.BitDepthChroma << std::endl + << "| Shift: " << info.Shift << std::endl + << "| TemporalID: " << info.FrameId.TemporalId << std::endl + << "| FrameRate: " << info.FrameRateExtN << "/" << info.FrameRateExtD << std::endl + << "| AspectRatio: " << info.AspectRatioW << "x" << info.AspectRatioH << std::endl + << "| PicStruct: " << info.PicStruct << std::endl + << "| ChromaFormat: " << info.ChromaFormat << std::endl + ; + return out; +} + +inline std::ostream & operator<<(std::ostream &out, const mfxFrameData &data) { + out << "FrameData:" << std::endl + << "| NumExtParam: " << data.NumExtParam << std::endl + << "| MemType: " << data.MemType << std::endl + << "| PitchHigh: " << data.PitchHigh << std::endl + << "| TimeStamp: " << data.TimeStamp << std::endl + << "| FrameOrder: " << data.FrameOrder << std::endl + << "| Locked: " << data.Locked << std::endl + << "| Pitch: " << data.PitchHigh << ", " << data.PitchLow << std::endl + << "| Y: " << (void*)data.Y << std::endl + << "| U: " << (void*)data.U << std::endl + << "| V: " << (void*)data.V << std::endl + ; + return out; +} + +//================================================================================================== + +static const int CC_MPG2 = FourCC('M', 'P', 'G', '2').vali32; +static const int CC_H264 = FourCC('H', '2', '6', '4').vali32; +static const int CC_X264 = FourCC('X', '2', '6', '4').vali32; +static const int CC_AVC = FourCC('A', 'V', 'C', ' ').vali32; +static const int CC_H265 = FourCC('H', '2', '6', '5').vali32; +static const int CC_HEVC = FourCC('H', 'E', 'V', 'C').vali32; +static const int CC_VC1 = FourCC('V', 'C', '1', ' ').vali32; + +//================================================================================================== + +template +inline void cleanup(T * &ptr) +{ + if (ptr) + { + delete ptr; + ptr = 0; + } +} + +//================================================================================================== + +struct Plugin +{ +public: + static Plugin * loadEncoderPlugin(MFXVideoSession &session, mfxU32 codecId) + { + static const mfxPluginUID hevc_enc_uid = { 0x6f, 0xad, 0xc7, 0x91, 0xa0, 0xc2, 0xeb, 0x47, 0x9a, 0xb6, 0xdc, 0xd5, 0xea, 0x9d, 0xa3, 0x47 }; + if (codecId == MFX_CODEC_HEVC) + return new Plugin(session, hevc_enc_uid); + return 0; + } + static Plugin * loadDecoderPlugin(MFXVideoSession &session, mfxU32 codecId) + { + static const mfxPluginUID hevc_dec_uid = { 0x33, 0xa6, 0x1c, 0x0b, 0x4c, 0x27, 0x45, 0x4c, 0xa8, 0xd8, 0x5d, 0xde, 0x75, 0x7c, 0x6f, 0x8e }; + if (codecId == MFX_CODEC_HEVC) + return new Plugin(session, hevc_dec_uid); + return 0; + } + ~Plugin() + { + if (isGood()) + MFXVideoUSER_UnLoad(session, &uid); + } + bool isGood() const { return res >= MFX_ERR_NONE; } +private: + MFXVideoSession &session; + mfxPluginUID uid; + mfxStatus res; +private: + Plugin(MFXVideoSession &_session, mfxPluginUID _uid) : session(_session), uid(_uid) + { + res = MFXVideoUSER_Load(session, &uid, 1); + } + Plugin(const Plugin &); + Plugin &operator=(const Plugin &); +}; + +//================================================================================================== + +struct ReadBitstream +{ +public: + ReadBitstream(const char * filename, size_t maxSize = 10 * 1024 * 1024); + ~ReadBitstream(); + bool isOpened() const; + bool isDone() const; + bool read(); +private: + ReadBitstream(const ReadBitstream &); + ReadBitstream &operator=(const ReadBitstream &); +public: + std::fstream input; + mfxBitstream stream; + bool drain; +}; + +//================================================================================================== + +struct WriteBitstream +{ +public: + WriteBitstream(const char * filename, size_t maxSize); + ~WriteBitstream(); + bool write(); + bool isOpened() const; +private: + WriteBitstream(const WriteBitstream &); + WriteBitstream &operator=(const WriteBitstream &); +public: + std::fstream output; + mfxBitstream stream; +}; + +//================================================================================================== + +class SurfacePool +{ +public: + SurfacePool(ushort width_, ushort height_, ushort count, const mfxFrameInfo & frameInfo, uchar bpp = 12); + ~SurfacePool(); + mfxFrameSurface1 *getFreeSurface(); + + template + static SurfacePool * create(T * instance, mfxVideoParam ¶ms) + { + CV_Assert(instance); + mfxFrameAllocRequest request; + memset(&request, 0, sizeof(request)); + mfxStatus res = instance->QueryIOSurf(¶ms, &request); + DBG(std::cout << "MFX QueryIOSurf: " << res << std::endl); + if (res < MFX_ERR_NONE) + return 0; + return new SurfacePool(request.Info.Width, + request.Info.Height, + request.NumFrameSuggested, + params.mfx.FrameInfo); + } +private: + SurfacePool(const SurfacePool &); + SurfacePool &operator=(const SurfacePool &); +public: + ushort width, height; + size_t oneSize; + cv::AutoBuffer buffers; + std::vector surfaces; +}; + +//================================================================================================== + +class DeviceHandler { +public: + virtual ~DeviceHandler() {} + bool init(MFXVideoSession &session); +protected: + virtual bool initDeviceSession(MFXVideoSession &session) = 0; +}; + + +// Linux specific + +#include + +class VAHandle : public DeviceHandler { +public: + VAHandle(); + ~VAHandle(); +private: + VAHandle(const VAHandle &); + VAHandle &operator=(const VAHandle &); + virtual bool initDeviceSession(MFXVideoSession &session); +private: + VADisplay display; + int file; +}; + +// TODO: Windows specific + + +#endif // MFXHELPER_H diff --git a/modules/videoio/src/cap_mfx_reader.cpp b/modules/videoio/src/cap_mfx_reader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b5c39cad50b6a825bfa33f8d817722533a5261cf --- /dev/null +++ b/modules/videoio/src/cap_mfx_reader.cpp @@ -0,0 +1,273 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#include "cap_mfx_reader.hpp" +#include "opencv2/core/base.hpp" +#include "cap_mfx_common.hpp" + +using namespace cv; +using namespace std; + +inline bool hasExtension(const String &filename, const String &ext) +{ + if (filename.size() <= ext.size()) + return false; + const size_t diff = filename.size() - ext.size(); + const size_t found_at = filename.rfind(ext); + return found_at == diff; +} + +inline mfxU32 determineCodecId(const String &filename) +{ + if (hasExtension(filename, ".h264") || hasExtension(filename, ".264")) + return MFX_CODEC_AVC; + else if (hasExtension(filename, ".mp2") || hasExtension(filename, ".mpeg2")) + return MFX_CODEC_MPEG2; + else if (hasExtension(filename, ".265") || hasExtension(filename, ".hevc")) + return MFX_CODEC_HEVC; + else + return (mfxU32)-1; +} + +//========================================================================== + +VideoCapture_IntelMFX::VideoCapture_IntelMFX(const cv::String &filename) + : session(0), plugin(0), deviceHandler(0), bs(0), decoder(0), pool(0), outSurface(0), good(false) +{ + mfxStatus res = MFX_ERR_NONE; + + // Init device and session + + deviceHandler = new VAHandle(); + session = new MFXVideoSession(); + if (!deviceHandler->init(*session)) + { + MSG(cerr << "MFX: Can't initialize session" << endl); + return; + } + + // Load appropriate plugin + + mfxU32 codecId = determineCodecId(filename); + if (codecId == (mfxU32)-1) + { + MSG(cerr << "MFX: Unsupported extension: " << filename << endl); + return; + } + plugin = Plugin::loadDecoderPlugin(*session, codecId); + if (plugin && !plugin->isGood()) + { + MSG(cerr << "MFX: LoadPlugin failed for codec: " << codecId << " (" << filename << ")" << endl); + return; + } + + // Read some content from file + + bs = new ReadBitstream(filename.c_str()); + if (!bs->read()) + { + MSG(cerr << "MFX: Failed to read bitstream" << endl); + return; + } + + // Create decoder and decode stream header + + decoder = new MFXVideoDECODE(*session); + mfxVideoParam params; + memset(¶ms, 0, sizeof(params)); + params.mfx.CodecId = codecId; + params.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; + res = decoder->DecodeHeader(&bs->stream, ¶ms); + DBG(cout << "DecodeHeader: " << res << endl << params.mfx << params.mfx.FrameInfo << endl); + if (res < MFX_ERR_NONE) + { + MSG(cerr << "MFX: Failed to decode stream header: " << res << endl); + return; + } + + // Adjust parameters + + res = decoder->Query(¶ms, ¶ms); + DBG(cout << "MFX Query: " << res << endl << params.mfx << params.mfx.FrameInfo); + CV_Assert(res >= MFX_ERR_NONE); + + // Init surface pool + + pool = SurfacePool::create(decoder, params); + if (!pool) + { + MSG(cerr << "MFX: Failed to create surface pool" << endl); + return; + } + + // Init decoder + + res = decoder->Init(¶ms); + DBG(cout << "MFX Init: " << res << endl << params.mfx.FrameInfo); + if (res < MFX_ERR_NONE) + { + MSG(cerr << "MFX: Failed to init decoder: " << res << endl); + return; + } + + good = true; +} + + +VideoCapture_IntelMFX::~VideoCapture_IntelMFX() +{ + cleanup(plugin); + cleanup(bs); + cleanup(decoder); + cleanup(pool); + session->Close(); + cleanup(session); + cleanup(deviceHandler); +} + +double VideoCapture_IntelMFX::getProperty(int) const +{ + MSG(cerr << "MFX: getProperty() is not implemented" << endl); + return 0; +} + +bool VideoCapture_IntelMFX::setProperty(int, double) +{ + MSG(cerr << "MFX: setProperty() is not implemented" << endl); + return false; +} + +bool VideoCapture_IntelMFX::grabFrame() +{ + mfxStatus res; + mfxFrameSurface1 *workSurface = 0; + mfxSyncPoint sync; + + workSurface = pool->getFreeSurface(); + + while (true) + { + if (!workSurface) + { + // not enough surfaces + MSG(cerr << "MFX: Failed to get free surface" << endl); + return false; + } + + outSurface = 0; + res = decoder->DecodeFrameAsync(bs->drain ? 0 : &bs->stream, workSurface, (mfxFrameSurface1**)&outSurface, &sync); + if (res == MFX_ERR_NONE) + { + res = session->SyncOperation(sync, 1000); // 1 sec, TODO: provide interface to modify timeout + if (res == MFX_ERR_NONE) + { + // ready to retrieve + DBG(cout << "Frame ready to retrieve" << endl); + return true; + } + else + { + MSG(cerr << "MFX: Sync error: " << res << endl); + return false; + } + } + else if (res == MFX_ERR_MORE_DATA) + { + if (bs->isDone()) + { + if (bs->drain) + { + // finish + DBG(cout << "Drain finished" << endl); + return false; + } + else + { + DBG(cout << "Bitstream finished - Drain started" << endl); + bs->drain = true; + continue; + } + } + else + { + bool read_res = bs->read(); + if (!read_res) + { + // failed to read + MSG(cerr << "MFX: Bitstream read failure" << endl); + return false; + } + else + { + DBG(cout << "Bitstream read success" << endl); + continue; + } + } + } + else if (res == MFX_ERR_MORE_SURFACE) + { + DBG(cout << "Getting another surface" << endl); + workSurface = pool->getFreeSurface(); + continue; + } + else if (res == MFX_WRN_DEVICE_BUSY) + { + DBG(cout << "Waiting for device" << endl); + sleep(1); + continue; + } + else if (res == MFX_WRN_VIDEO_PARAM_CHANGED) + { + DBG(cout << "Video param changed" << endl); + continue; + } + else + { + MSG(cerr << "MFX: Bad status: " << res << endl); + return false; + } + return false; + } +} + + +bool VideoCapture_IntelMFX::retrieveFrame(int, OutputArray out) +{ + if (!outSurface) + { + MSG(cerr << "MFX: No frame ready to retrieve" << endl); + return false; + } + mfxFrameSurface1 * s = (mfxFrameSurface1*)outSurface; + mfxFrameInfo &info = s->Info; + mfxFrameData &data = s->Data; + + const int cols = info.CropW; + const int rows = info.CropH; + Mat nv12(rows * 3 / 2, cols, CV_8UC1); + + Mat Y(rows, cols, CV_8UC1, data.Y, data.Pitch); + Mat UV(rows / 2, cols, CV_8UC1, data.UV, data.Pitch); + + Y.copyTo(Mat(nv12, Rect(0, 0, cols, rows))); + UV.copyTo(Mat(nv12, Rect(0, rows, cols, rows / 2))); + + Mat u_and_v[2]; + split(UV.reshape(2), u_and_v); + cvtColor(nv12, out, COLOR_YUV2BGR_NV12); + + return true; +} + +bool VideoCapture_IntelMFX::isOpened() const +{ + return good; +} + +int VideoCapture_IntelMFX::getCaptureDomain() +{ + return CAP_INTEL_MFX; +} + +//================================================================================================== diff --git a/modules/videoio/src/cap_mfx_reader.hpp b/modules/videoio/src/cap_mfx_reader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1d2fa8b009597c6860b17a028d82339a4f912715 --- /dev/null +++ b/modules/videoio/src/cap_mfx_reader.hpp @@ -0,0 +1,41 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef CAP_MFX_HPP +#define CAP_MFX_HPP + +#include "precomp.hpp" + + +class MFXVideoSession; +class Plugin; +class DeviceHandler; +class ReadBitstream; +class SurfacePool; +class MFXVideoDECODE; + +class VideoCapture_IntelMFX : public cv::IVideoCapture +{ +public: + VideoCapture_IntelMFX(const cv::String &filename); + virtual ~VideoCapture_IntelMFX(); + virtual double getProperty(int) const; + virtual bool setProperty(int, double); + virtual bool grabFrame(); + virtual bool retrieveFrame(int, cv::OutputArray out); + virtual bool isOpened() const; + virtual int getCaptureDomain(); +private: + MFXVideoSession *session; + Plugin *plugin; + DeviceHandler *deviceHandler; + ReadBitstream *bs; + MFXVideoDECODE *decoder; + SurfacePool *pool; + void *outSurface; + bool good; +}; + + +#endif diff --git a/modules/videoio/src/cap_mfx_writer.cpp b/modules/videoio/src/cap_mfx_writer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9fc0063c09b0497f7906d668d6e583533f6f99bd --- /dev/null +++ b/modules/videoio/src/cap_mfx_writer.cpp @@ -0,0 +1,272 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#include "cap_mfx_writer.hpp" +#include "opencv2/core/base.hpp" +#include "cap_mfx_common.hpp" + +using namespace std; +using namespace cv; + +inline mfxU32 codecIdByFourCC(int fourcc) +{ + if (fourcc == CC_X264 || fourcc == CC_H264 || fourcc == CC_AVC) + return MFX_CODEC_AVC; + else if (fourcc == CC_H265 || fourcc == CC_HEVC) + return MFX_CODEC_HEVC; + else if (fourcc == CC_MPG2) + return MFX_CODEC_MPEG2; + else + return (mfxU32)-1; +} + +VideoWriter_IntelMFX::VideoWriter_IntelMFX(const String &filename, int _fourcc, double fps, Size frameSize_, bool) + : session(0), plugin(0), deviceHandler(0), bs(0), encoder(0), pool(0), frameSize(frameSize_), good(false) +{ + mfxStatus res = MFX_ERR_NONE; + + if (frameSize.width % 2 || frameSize.height % 2) + { + MSG(cerr << "MFX: Invalid frame size passed to encoder" << endl); + return; + } + + // Init device and session + + deviceHandler = new VAHandle(); + session = new MFXVideoSession(); + if (!deviceHandler->init(*session)) + { + MSG(cerr << "MFX: Can't initialize session" << endl); + return; + } + + // Load appropriate plugin + + mfxU32 codecId = codecIdByFourCC(_fourcc); + if (codecId == (mfxU32)-1) + { + MSG(cerr << "MFX: Unsupported FourCC: " << FourCC(_fourcc) << endl); + return; + } + plugin = Plugin::loadEncoderPlugin(*session, codecId); + if (plugin && !plugin->isGood()) + { + MSG(cerr << "MFX: LoadPlugin failed for codec: " << codecId << " (" << FourCC(_fourcc) << ")" << endl); + return; + } + + // Init encoder + + encoder = new MFXVideoENCODE(*session); + mfxVideoParam params; + memset(¶ms, 0, sizeof(params)); + params.mfx.CodecId = codecId; + params.mfx.TargetUsage = MFX_TARGETUSAGE_BALANCED; + params.mfx.TargetKbps = frameSize.area() * fps / 500; // TODO: set in options + params.mfx.RateControlMethod = MFX_RATECONTROL_VBR; + params.mfx.FrameInfo.FrameRateExtN = cvRound(fps * 1000); + params.mfx.FrameInfo.FrameRateExtD = 1000; + params.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; + params.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + params.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + params.mfx.FrameInfo.CropX = 0; + params.mfx.FrameInfo.CropY = 0; + params.mfx.FrameInfo.CropW = frameSize.width; + params.mfx.FrameInfo.CropH = frameSize.height; + params.mfx.FrameInfo.Width = alignSize(frameSize.width, 32); + params.mfx.FrameInfo.Height = alignSize(frameSize.height, 32); + params.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; + res = encoder->Query(¶ms, ¶ms); + DBG(cout << "MFX Query: " << res << endl << params.mfx << params.mfx.FrameInfo); + if (res < MFX_ERR_NONE) + { + MSG(cerr << "MFX: Query failed: " << res << endl); + return; + } + + // Init surface pool + pool = SurfacePool::create(encoder, params); + if (!pool) + { + MSG(cerr << "MFX: Failed to create surface pool" << endl); + return; + } + + // Init encoder + res = encoder->Init(¶ms); + DBG(cout << "MFX Init: " << res << endl << params.mfx.FrameInfo); + if (res < MFX_ERR_NONE) + { + MSG(cerr << "MFX: Failed to init encoder: " << res << endl); + return; + } + + // Open output bitstream + { + mfxVideoParam par; + memset(&par, 0, sizeof(par)); + res = encoder->GetVideoParam(&par); + DBG(cout << "MFX GetVideoParam: " << res << endl << "requested " << par.mfx.BufferSizeInKB << " kB" << endl); + CV_Assert(res >= MFX_ERR_NONE); + bs = new WriteBitstream(filename.c_str(), par.mfx.BufferSizeInKB * 1024 * 2); + if (!bs->isOpened()) + { + MSG(cerr << "MFX: Failed to open output file: " << filename << endl); + return; + } + } + + good = true; +} + +VideoWriter_IntelMFX::~VideoWriter_IntelMFX() +{ + if (isOpened()) + { + DBG(cout << "====== Drain bitstream..." << endl); + Mat dummy; + while (write_one(dummy)) {} + DBG(cout << "====== Drain Finished" << endl); + } + cleanup(bs); + cleanup(pool); + cleanup(encoder); + cleanup(plugin); + cleanup(session); + cleanup(deviceHandler); +} + +double VideoWriter_IntelMFX::getProperty(int) const +{ + MSG(cerr << "MFX: getProperty() is not implemented" << endl); + return 0; +} + +bool VideoWriter_IntelMFX::setProperty(int, double) +{ + MSG(cerr << "MFX: setProperty() is not implemented" << endl); + return false; +} + +bool VideoWriter_IntelMFX::isOpened() const +{ + return good; +} + +void VideoWriter_IntelMFX::write(cv::InputArray input) +{ + write_one(input); +} + +inline static void to_nv12(cv::InputArray bgr, cv::Mat & Y, cv::Mat & UV) +{ + const int height = bgr.rows(); + const int width = bgr.cols(); + Mat yuv; + cvtColor(bgr, yuv, CV_BGR2YUV_I420); + CV_Assert(yuv.isContinuous()); + Mat Y_(Y, Rect(0, 0, width, height)); + yuv.rowRange(0, height).copyTo(Y_); + Mat UV_planar(height, width / 2, CV_8UC1, yuv.ptr(height)); + Mat u_and_v[2] = { + UV_planar.rowRange(0, height / 2), + UV_planar.rowRange(height / 2, height), + }; + Mat uv; + cv::merge(u_and_v, 2, uv); + Mat UV_(UV, Rect(0, 0, width, height / 2)); + uv.reshape(1).copyTo(UV_); +} + +bool VideoWriter_IntelMFX::write_one(cv::InputArray bgr) +{ + mfxStatus res; + mfxFrameSurface1 *workSurface = 0; + mfxSyncPoint sync; + + if (!bgr.empty() && (bgr.dims() != 2 || bgr.type() != CV_8UC3 || bgr.size() != frameSize)) + { + MSG(cerr << "MFX: invalid frame passed to encoder: " + << "dims/depth/cn=" << bgr.dims() << "/" << bgr.depth() << "/" << bgr.channels() + << ", size=" << bgr.size() << endl); + return false; + + } + if (!bgr.empty()) + { + workSurface = pool->getFreeSurface(); + if (!workSurface) + { + // not enough surfaces + MSG(cerr << "MFX: Failed to get free surface" << endl); + return false; + } + const int rows = workSurface->Info.Height; + const int cols = workSurface->Info.Width; + Mat Y(rows, cols, CV_8UC1, workSurface->Data.Y, workSurface->Data.Pitch); + Mat UV(rows / 2, cols, CV_8UC1, workSurface->Data.UV, workSurface->Data.Pitch); + to_nv12(bgr, Y, UV); + CV_Assert(Y.ptr() == workSurface->Data.Y); + CV_Assert(UV.ptr() == workSurface->Data.UV); + } + + while (true) + { + outSurface = 0; + DBG(cout << "Calling with surface: " << workSurface << endl); + res = encoder->EncodeFrameAsync(NULL, workSurface, &bs->stream, &sync); + if (res == MFX_ERR_NONE) + { + res = session->SyncOperation(sync, 1000); // 1 sec, TODO: provide interface to modify timeout + if (res == MFX_ERR_NONE) + { + // ready to write + if (!bs->write()) + { + MSG(cerr << "MFX: Failed to write bitstream" << endl); + return false; + } + else + { + DBG(cout << "Write bitstream" << endl); + return true; + } + } + else + { + MSG(cerr << "MFX: Sync error: " << res << endl); + return false; + } + } + else if (res == MFX_ERR_MORE_DATA) + { + DBG(cout << "ERR_MORE_DATA" << endl); + return false; + } + else if (res == MFX_WRN_DEVICE_BUSY) + { + DBG(cout << "Waiting for device" << endl); + sleep(1); + continue; + } + else + { + MSG(cerr << "MFX: Bad status: " << res << endl); + return false; + } + return true; + } +} + +Ptr VideoWriter_IntelMFX::create(const String &filename, int _fourcc, double fps, Size frameSize, bool isColor) +{ + if (codecIdByFourCC(_fourcc) > 0) + { + Ptr a = makePtr(filename, _fourcc, fps, frameSize, isColor); + if (a->isOpened()) + return a; + } + return Ptr(); +} diff --git a/modules/videoio/src/cap_mfx_writer.hpp b/modules/videoio/src/cap_mfx_writer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6a60960bb7754d0c150192b5e1c81baf18ac558f --- /dev/null +++ b/modules/videoio/src/cap_mfx_writer.hpp @@ -0,0 +1,48 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef CAP_MFX_WRITER_HPP +#define CAP_MFX_WRITER_HPP + +#include "precomp.hpp" + +class MFXVideoSession; +class Plugin; +class DeviceHandler; +class WriteBitstream; +class SurfacePool; +class MFXVideoDECODE; +class MFXVideoENCODE; + +class VideoWriter_IntelMFX : public cv::IVideoWriter +{ +public: + VideoWriter_IntelMFX(const cv::String &filename, int _fourcc, double fps, cv::Size frameSize, bool isColor); + virtual ~VideoWriter_IntelMFX(); + virtual double getProperty(int) const; + virtual bool setProperty(int, double); + virtual bool isOpened() const; + virtual void write(cv::InputArray input); + static cv::Ptr create(const cv::String& filename, int _fourcc, double fps, cv::Size frameSize, bool isColor); + +protected: + bool write_one(cv::InputArray bgr); + +private: + VideoWriter_IntelMFX(const VideoWriter_IntelMFX &); + VideoWriter_IntelMFX & operator=(const VideoWriter_IntelMFX &); + +private: + MFXVideoSession *session; + Plugin *plugin; + DeviceHandler *deviceHandler; + WriteBitstream *bs; + MFXVideoENCODE *encoder; + SurfacePool *pool; + void *outSurface; + cv::Size frameSize; + bool good; +}; + +#endif // CAP_MFX_WRITER_HPP diff --git a/modules/videoio/test/test_mfx.cpp b/modules/videoio/test/test_mfx.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1149bb257bfc8b0ae40446e5941052e879304e22 --- /dev/null +++ b/modules/videoio/test/test_mfx.cpp @@ -0,0 +1,161 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#include "test_precomp.hpp" +#include "opencv2/videoio.hpp" +#include "opencv2/highgui.hpp" +#include +#include +#include + +#ifdef HAVE_MFX + +using namespace cv; +using namespace std; +using namespace std::tr1; + + +TEST(Videoio_MFX, read_invalid) +{ + VideoCapture cap; + ASSERT_NO_THROW(cap.open("nonexistent-file", CAP_INTEL_MFX)); + ASSERT_FALSE(cap.isOpened()); + Mat img; + ASSERT_NO_THROW(cap >> img); + ASSERT_TRUE(img.empty()); +} + +TEST(Videoio_MFX, write_invalid) +{ + const string filename = cv::tempfile(".264"); + VideoWriter writer; + bool res; + ASSERT_NO_THROW(res = writer.open(CAP_INTEL_MFX, filename, VideoWriter::fourcc('H', '2', '6', '4'), 1, Size(641, 480), true)); + EXPECT_FALSE(res); + EXPECT_FALSE(writer.isOpened()); + ASSERT_NO_THROW(res = writer.open(CAP_INTEL_MFX,filename, VideoWriter::fourcc('H', '2', '6', '4'), 1, Size(640, 481), true)); + EXPECT_FALSE(res); + EXPECT_FALSE(writer.isOpened()); + ASSERT_NO_THROW(res = writer.open(CAP_INTEL_MFX,filename, VideoWriter::fourcc('A', 'B', 'C', 'D'), 1, Size(640, 480), true)); + EXPECT_FALSE(res); + EXPECT_FALSE(writer.isOpened()); + ASSERT_NO_THROW(res = writer.open(CAP_INTEL_MFX,String(), VideoWriter::fourcc('H', '2', '6', '4'), 1, Size(640, 480), true)); + EXPECT_FALSE(res); + EXPECT_FALSE(writer.isOpened()); + ASSERT_NO_THROW(res = writer.open(CAP_INTEL_MFX,filename, VideoWriter::fourcc('H', '2', '6', '4'), 0, Size(640, 480), true)); + EXPECT_FALSE(res); + EXPECT_FALSE(writer.isOpened()); + + ASSERT_NO_THROW(res = writer.open(CAP_INTEL_MFX,filename, VideoWriter::fourcc('H', '2', '6', '4'), 30, Size(640, 480), true)); + ASSERT_TRUE(res); + ASSERT_TRUE(writer.isOpened()); + Mat t; + // write some bad frames + t = Mat(Size(1024, 768), CV_8UC3); + EXPECT_NO_THROW(writer << t); + t = Mat(Size(320, 240), CV_8UC3); + EXPECT_NO_THROW(writer << t); + t = Mat(Size(640, 480), CV_8UC2); + EXPECT_NO_THROW(writer << t); + + // cleanup + ASSERT_NO_THROW(writer.release()); + remove(filename.c_str()); +} + + +//================================================================================================== + +const int FRAME_COUNT = 20; + +inline void generateFrame(int i, Mat & frame) +{ + frame = 0; + ostringstream buf; buf << "Frame " << setw(2) << setfill('0') << i + 1; + int baseLine = 0; + Size box = getTextSize(buf.str(), FONT_HERSHEY_COMPLEX, 2, 5, &baseLine); + putText(frame, buf.str(), Point((frame.cols - box.width) / 2, (frame.rows - box.height) / 2 + baseLine), + FONT_HERSHEY_COMPLEX, 2, Scalar(255, 255, 255), 5, LINE_AA); + Point p(i * frame.cols / (FRAME_COUNT - 1), i * frame.rows / (FRAME_COUNT - 1)); + circle(frame, p, 20, Scalar(200, 25, 55), 5, LINE_AA); +} + +inline int fourccByExt(const String &ext) +{ + if (ext == ".mpeg2") + return VideoWriter::fourcc('M', 'P', 'G', '2'); + else if (ext == ".264") + return VideoWriter::fourcc('H', '2', '6', '4'); + else if (ext == ".265") + return VideoWriter::fourcc('H', '2', '6', '5'); + return -1; +} + +//================================================================================================== + +typedef tuple Size_FPS_Ext; +typedef testing::TestWithParam< Size_FPS_Ext > Videoio_MFX; + +TEST_P(Videoio_MFX, read_write_raw) +{ + const Size FRAME_SIZE = get<0>(GetParam()); + const double FPS = get<1>(GetParam()); + const char *ext = get<2>(GetParam()); + const String filename = cv::tempfile(ext); + const int fourcc = fourccByExt(ext); + + bool isColor = true; + queue goodFrames; + + // Write video + VideoWriter writer; + writer.open(CAP_INTEL_MFX, filename, fourcc, FPS, FRAME_SIZE, isColor); + ASSERT_TRUE(writer.isOpened()); + Mat frame(FRAME_SIZE, CV_8UC3); + for (int i = 0; i < FRAME_COUNT; ++i) + { + generateFrame(i, frame); + goodFrames.push(frame.clone()); + writer << frame; + } + writer.release(); + EXPECT_FALSE(writer.isOpened()); + + // Read video + VideoCapture cap; + cap.open(filename, CAP_INTEL_MFX); + ASSERT_TRUE(cap.isOpened()); + for (int i = 0; i < FRAME_COUNT; ++i) + { + ASSERT_TRUE(cap.read(frame)); + ASSERT_FALSE(frame.empty()); + ASSERT_EQ(FRAME_SIZE.width, frame.cols); + ASSERT_EQ(FRAME_SIZE.height, frame.rows); + // verify + ASSERT_NE(goodFrames.size(), 0u); + const Mat &goodFrame = goodFrames.front(); + EXPECT_EQ(goodFrame.depth(), frame.depth()); + EXPECT_EQ(goodFrame.channels(), frame.channels()); + EXPECT_EQ(goodFrame.type(), frame.type()); + double psnr = cvtest::PSNR(goodFrame, frame); + if (fourcc == VideoWriter::fourcc('M', 'P', 'G', '2')) + EXPECT_GT(psnr, 37); // experimentally chosen value + else + EXPECT_GT(psnr, 43); // experimentally chosen value + goodFrames.pop(); + } + EXPECT_FALSE(cap.read(frame)); + EXPECT_TRUE(frame.empty()); + cap.release(); + EXPECT_FALSE(cap.isOpened()); + remove(filename.c_str()); +} + +INSTANTIATE_TEST_CASE_P(videoio, Videoio_MFX, + testing::Combine( + testing::Values(Size(640, 480), Size(638, 478), Size(636, 476), Size(1920, 1080)), + testing::Values(1, 30, 100), + testing::Values(".mpeg2", ".264", ".265"))); + +#endif