Initial version of MediaSDK integration:
- cmake dependencies search (WITH_MFX option) - raw H264, H265, MPEG2 encoding and decoding - tests for supported formats
This commit is contained in:
parent
68d01972fe
commit
c06f3d63e8
@ -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_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 "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_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_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_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) )
|
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)
|
status(" Intel PerC:" HAVE_INTELPERC THEN "YES" ELSE NO)
|
||||||
endif(DEFINED WITH_INTELPERC)
|
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)
|
if(DEFINED WITH_GPHOTO2)
|
||||||
status(" gPhoto2:" HAVE_GPHOTO2 THEN "YES" ELSE NO)
|
status(" gPhoto2:" HAVE_GPHOTO2 THEN "YES" ELSE NO)
|
||||||
endif(DEFINED WITH_GPHOTO2)
|
endif(DEFINED WITH_GPHOTO2)
|
||||||
|
|||||||
38
cmake/OpenCVDetectMediaSDK.cmake
Normal file
38
cmake/OpenCVDetectMediaSDK.cmake
Normal file
@ -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()
|
||||||
@ -294,6 +294,10 @@ if(WITH_INTELPERC)
|
|||||||
include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindIntelPerCSDK.cmake")
|
include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindIntelPerCSDK.cmake")
|
||||||
endif(WITH_INTELPERC)
|
endif(WITH_INTELPERC)
|
||||||
|
|
||||||
|
if(WITH_MFX)
|
||||||
|
include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectMediaSDK.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
# --- gPhoto2 ---
|
# --- gPhoto2 ---
|
||||||
ocv_clear_vars(HAVE_GPHOTO2)
|
ocv_clear_vars(HAVE_GPHOTO2)
|
||||||
if(WITH_GPHOTO2)
|
if(WITH_GPHOTO2)
|
||||||
|
|||||||
@ -205,6 +205,9 @@
|
|||||||
/* Intel VA-API/OpenCL */
|
/* Intel VA-API/OpenCL */
|
||||||
#cmakedefine HAVE_VA_INTEL
|
#cmakedefine HAVE_VA_INTEL
|
||||||
|
|
||||||
|
/* Intel Media SDK */
|
||||||
|
#cmakedefine HAVE_MFX
|
||||||
|
|
||||||
/* Lapack */
|
/* Lapack */
|
||||||
#cmakedefine HAVE_LAPACK
|
#cmakedefine HAVE_LAPACK
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt/MediaStreamSink.hpp)
|
||||||
endif()
|
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)
|
if(WIN32 AND NOT ARM)
|
||||||
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_cmu.cpp)
|
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_cmu.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@ -115,7 +115,8 @@ enum VideoCaptureAPIs {
|
|||||||
CAP_FFMPEG = 1900, //!< Open and record video file or stream using the FFMPEG library
|
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_IMAGES = 2000, //!< OpenCV Image Sequence (e.g. img_%02d.jpg)
|
||||||
CAP_ARAVIS = 2100, //!< Aravis SDK
|
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.
|
/** @brief %VideoCapture generic properties identifier.
|
||||||
|
|||||||
@ -43,6 +43,11 @@
|
|||||||
#include "cap_intelperc.hpp"
|
#include "cap_intelperc.hpp"
|
||||||
#include "cap_dshow.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
|
// All WinRT versions older than 8.0 should provide classes used for video support
|
||||||
#if defined(WINRT) && !defined(WINRT_8_0) && defined(__cplusplus_winrt)
|
#if defined(WINRT) && !defined(WINRT_8_0) && defined(__cplusplus_winrt)
|
||||||
# include "cap_winrt_capture.hpp"
|
# include "cap_winrt_capture.hpp"
|
||||||
@ -525,6 +530,9 @@ static Ptr<IVideoCapture> IVideoCapture_create(const String& filename)
|
|||||||
CV_CAP_ANY,
|
CV_CAP_ANY,
|
||||||
#ifdef HAVE_GPHOTO2
|
#ifdef HAVE_GPHOTO2
|
||||||
CV_CAP_GPHOTO2,
|
CV_CAP_GPHOTO2,
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_MFX
|
||||||
|
CAP_INTEL_MFX,
|
||||||
#endif
|
#endif
|
||||||
-1, -1
|
-1, -1
|
||||||
};
|
};
|
||||||
@ -543,6 +551,11 @@ static Ptr<IVideoCapture> IVideoCapture_create(const String& filename)
|
|||||||
case CV_CAP_GPHOTO2:
|
case CV_CAP_GPHOTO2:
|
||||||
capture = createGPhoto2Capture(filename);
|
capture = createGPhoto2Capture(filename);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_MFX
|
||||||
|
case CAP_INTEL_MFX:
|
||||||
|
capture = makePtr<VideoCapture_IntelMFX>(filename);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,9 +571,19 @@ static Ptr<IVideoCapture> IVideoCapture_create(const String& filename)
|
|||||||
static Ptr<IVideoWriter> IVideoWriter_create(int apiPreference, const String& filename, int _fourcc, double fps, Size frameSize, bool isColor)
|
static Ptr<IVideoWriter> IVideoWriter_create(int apiPreference, const String& filename, int _fourcc, double fps, Size frameSize, bool isColor)
|
||||||
{
|
{
|
||||||
Ptr<IVideoWriter> iwriter;
|
Ptr<IVideoWriter> 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)
|
if( (apiPreference == CAP_OCV_MJPEG || apiPreference == CAP_ANY)
|
||||||
&& _fourcc == CV_FOURCC('M', 'J', 'P', 'G') )
|
&& _fourcc == CV_FOURCC('M', 'J', 'P', 'G') )
|
||||||
iwriter = createMotionJpegWriter(filename, fps, frameSize, isColor);
|
iwriter = createMotionJpegWriter(filename, fps, frameSize, isColor);
|
||||||
|
|
||||||
return iwriter;
|
return iwriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
183
modules/videoio/src/cap_mfx_common.cpp
Normal file
183
modules/videoio/src/cap_mfx_common.cpp
Normal file
@ -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 <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
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<mfxHandleType>(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<mfxFrameSurface1>::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();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
318
modules/videoio/src/cap_mfx_common.hpp
Normal file
318
modules/videoio/src/cap_mfx_common.hpp
Normal file
@ -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 <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <mfxcommon.h>
|
||||||
|
#include <mfxstructures.h>
|
||||||
|
#include <mfxvideo++.h>
|
||||||
|
#include <mfxvp8.h>
|
||||||
|
#include <mfxjpeg.h>
|
||||||
|
#include <mfxplugin++.h>
|
||||||
|
|
||||||
|
// //
|
||||||
|
// 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 <typename T>
|
||||||
|
struct HexWrap {
|
||||||
|
HexWrap(T val_) : val(val_) {}
|
||||||
|
T val;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::ostream & operator<<(std::ostream &out, const HexWrap<T> &wrap) {
|
||||||
|
std::ios_base::fmtflags flags = out.flags(std::ios::hex | std::ios::showbase);
|
||||||
|
out << wrap.val;
|
||||||
|
out.flags(flags);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline ::HexWrap<T> asHex(const T & val) {
|
||||||
|
return ::HexWrap<T>(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 "<Invalid mfxStatus>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 <typename T>
|
||||||
|
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 <typename T>
|
||||||
|
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<uchar, 0> buffers;
|
||||||
|
std::vector<mfxFrameSurface1> surfaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
class DeviceHandler {
|
||||||
|
public:
|
||||||
|
virtual ~DeviceHandler() {}
|
||||||
|
bool init(MFXVideoSession &session);
|
||||||
|
protected:
|
||||||
|
virtual bool initDeviceSession(MFXVideoSession &session) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Linux specific
|
||||||
|
|
||||||
|
#include <va/va_drm.h>
|
||||||
|
|
||||||
|
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
|
||||||
273
modules/videoio/src/cap_mfx_reader.cpp
Normal file
273
modules/videoio/src/cap_mfx_reader.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
41
modules/videoio/src/cap_mfx_reader.hpp
Normal file
41
modules/videoio/src/cap_mfx_reader.hpp
Normal file
@ -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
|
||||||
272
modules/videoio/src/cap_mfx_writer.cpp
Normal file
272
modules/videoio/src/cap_mfx_writer.cpp
Normal file
@ -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> VideoWriter_IntelMFX::create(const String &filename, int _fourcc, double fps, Size frameSize, bool isColor)
|
||||||
|
{
|
||||||
|
if (codecIdByFourCC(_fourcc) > 0)
|
||||||
|
{
|
||||||
|
Ptr<VideoWriter_IntelMFX> a = makePtr<VideoWriter_IntelMFX>(filename, _fourcc, fps, frameSize, isColor);
|
||||||
|
if (a->isOpened())
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return Ptr<VideoWriter_IntelMFX>();
|
||||||
|
}
|
||||||
48
modules/videoio/src/cap_mfx_writer.hpp
Normal file
48
modules/videoio/src/cap_mfx_writer.hpp
Normal file
@ -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<VideoWriter_IntelMFX> 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
|
||||||
161
modules/videoio/test/test_mfx.cpp
Normal file
161
modules/videoio/test/test_mfx.cpp
Normal file
@ -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 <sstream>
|
||||||
|
#include <queue>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#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, double, const char *> 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<Mat> 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
|
||||||
Loading…
Reference in New Issue
Block a user