- cmake dependencies search (WITH_MFX option) - raw H264, H265, MPEG2 encoding and decoding - tests for supported formatspull/8825/head
parent
68d01972fe
commit
c06f3d63e8
14 changed files with 1381 additions and 1 deletions
@ -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() |
@ -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(); |
||||
} |
||||
|
||||
//==================================================================================================
|
@ -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
|
@ -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; |
||||
} |
||||
|
||||
//==================================================================================================
|
@ -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 |
@ -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>(); |
||||
} |
@ -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
|
@ -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