mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Refactor MediaFoundation (Part 1)
This commit is contained in:
parent
4a79d3f143
commit
d5717af2df
@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// Windows include
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
// COM includes
|
// COM includes
|
||||||
#include <Guiddef.h>
|
#include <Guiddef.h>
|
||||||
|
|
||||||
@ -46,17 +49,17 @@ public:
|
|||||||
|
|
||||||
struct DeviceProperties
|
struct DeviceProperties
|
||||||
{
|
{
|
||||||
QString name = QString();
|
QString name = QString();
|
||||||
QMultiMap<QString, int> inputs = QMultiMap<QString, int>();
|
QMultiMap<QString, int> inputs = QMultiMap<QString, int>();
|
||||||
QStringList displayResolutions = QStringList();
|
QStringList displayResolutions = QStringList();
|
||||||
QStringList framerates = QStringList();
|
QStringList framerates = QStringList();
|
||||||
QList<DevicePropertiesItem> valid = QList<DevicePropertiesItem>();
|
QList<DevicePropertiesItem> valid = QList<DevicePropertiesItem>();
|
||||||
};
|
};
|
||||||
|
|
||||||
MFGrabber(const QString & device, const unsigned width, const unsigned height, const unsigned fps, const unsigned input, int pixelDecimation);
|
MFGrabber(const QString & device, const unsigned width, const unsigned height, const unsigned fps, const unsigned input, int pixelDecimation);
|
||||||
~MFGrabber() override;
|
~MFGrabber() override;
|
||||||
|
|
||||||
void receive_image(const void *frameImageBuffer, int size, QString message);
|
void receive_image(const void *frameImageBuffer, int size);
|
||||||
QRectF getSignalDetectionOffset() const { return QRectF(_x_frac_min, _y_frac_min, _x_frac_max, _y_frac_max); }
|
QRectF getSignalDetectionOffset() const { return QRectF(_x_frac_min, _y_frac_min, _x_frac_max, _y_frac_max); }
|
||||||
bool getSignalDetectionEnabled() const { return _signalDetectionEnabled; }
|
bool getSignalDetectionEnabled() const { return _signalDetectionEnabled; }
|
||||||
bool getCecDetectionEnabled() const { return _cecDetectionEnabled; }
|
bool getCecDetectionEnabled() const { return _cecDetectionEnabled; }
|
||||||
@ -88,51 +91,44 @@ signals:
|
|||||||
void newFrame(const Image<ColorRgb> & image);
|
void newFrame(const Image<ColorRgb> & image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct buffer
|
|
||||||
{
|
|
||||||
void *start;
|
|
||||||
size_t length;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool init();
|
bool init();
|
||||||
void uninit();
|
void uninit();
|
||||||
bool init_device(QString device, DevicePropertiesItem props);
|
HRESULT init_device(QString device, DevicePropertiesItem props);
|
||||||
void uninit_device();
|
void uninit_device();
|
||||||
void enumVideoCaptureDevices();
|
void enumVideoCaptureDevices();
|
||||||
void start_capturing();
|
void start_capturing();
|
||||||
bool process_image(const void *frameImageBuffer, int size);
|
void process_image(const void *frameImageBuffer, int size);
|
||||||
void checkSignalDetectionEnabled(Image<ColorRgb> image);
|
void checkSignalDetectionEnabled(Image<ColorRgb> image);
|
||||||
|
|
||||||
QString _deviceName;
|
QString _deviceName;
|
||||||
QMap<QString, MFGrabber::DeviceProperties> _deviceProperties;
|
QMap<QString, MFGrabber::DeviceProperties> _deviceProperties;
|
||||||
std::vector<buffer> _buffers;
|
HRESULT _hr;
|
||||||
HRESULT _hr;
|
SourceReaderCB* _sourceReaderCB;
|
||||||
SourceReaderCB* _sourceReaderCB;
|
PixelFormat _pixelFormat;
|
||||||
PixelFormat _pixelFormat;
|
int _pixelDecimation,
|
||||||
int _pixelDecimation,
|
_lineLength,
|
||||||
_lineLength,
|
_frameByteSize,
|
||||||
_frameByteSize,
|
_noSignalCounterThreshold,
|
||||||
_noSignalCounterThreshold,
|
_noSignalCounter,
|
||||||
_noSignalCounter,
|
_fpsSoftwareDecimation,
|
||||||
_fpsSoftwareDecimation,
|
_brightness,
|
||||||
_brightness,
|
_contrast,
|
||||||
_contrast,
|
_saturation,
|
||||||
_saturation,
|
_hue;
|
||||||
_hue;
|
volatile unsigned int _currentFrame;
|
||||||
volatile unsigned int _currentFrame;
|
ColorRgb _noSignalThresholdColor;
|
||||||
ColorRgb _noSignalThresholdColor;
|
bool _signalDetectionEnabled,
|
||||||
bool _signalDetectionEnabled,
|
_cecDetectionEnabled,
|
||||||
_cecDetectionEnabled,
|
_noSignalDetected,
|
||||||
_noSignalDetected,
|
_initialized;
|
||||||
_initialized;
|
double _x_frac_min,
|
||||||
double _x_frac_min,
|
_y_frac_min,
|
||||||
_y_frac_min,
|
_x_frac_max,
|
||||||
_x_frac_max,
|
_y_frac_max;
|
||||||
_y_frac_max;
|
MFThreadManager _threadManager;
|
||||||
MFThreadManager _threadManager;
|
IMFSourceReader* _sourceReader;
|
||||||
IMFSourceReader* _sourceReader;
|
|
||||||
|
|
||||||
#ifdef HAVE_TURBO_JPEG
|
#ifdef HAVE_TURBO_JPEG
|
||||||
int _subsamp;
|
int _subsamp;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -78,7 +78,7 @@ class MFThreadManager : public QObject
|
|||||||
public:
|
public:
|
||||||
MFThreadManager() : _threads(nullptr)
|
MFThreadManager() : _threads(nullptr)
|
||||||
{
|
{
|
||||||
_maxThreads = qBound(1, ((QThread::idealThreadCount() * 3) / 2), 12);
|
_maxThreads = qBound(1, (QThread::idealThreadCount() > 4 ? (QThread::idealThreadCount() - 1) : QThread::idealThreadCount()), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
~MFThreadManager()
|
~MFThreadManager()
|
||||||
|
@ -1,22 +1,9 @@
|
|||||||
#include "MFSourceReaderCB.h"
|
#include "MFSourceReaderCB.h"
|
||||||
#include "grabber/MFGrabber.h"
|
#include "grabber/MFGrabber.h"
|
||||||
|
|
||||||
static PixelFormat GetPixelFormatForGuid(const GUID guid)
|
|
||||||
{
|
|
||||||
if (IsEqualGUID(guid, MFVideoFormat_RGB32)) return PixelFormat::RGB32;
|
|
||||||
if (IsEqualGUID(guid, MFVideoFormat_RGB24)) return PixelFormat::BGR24;
|
|
||||||
if (IsEqualGUID(guid, MFVideoFormat_YUY2)) return PixelFormat::YUYV;
|
|
||||||
if (IsEqualGUID(guid, MFVideoFormat_UYVY)) return PixelFormat::UYVY;
|
|
||||||
if (IsEqualGUID(guid, MFVideoFormat_MJPG)) return PixelFormat::MJPEG;
|
|
||||||
if (IsEqualGUID(guid, MFVideoFormat_NV12)) return PixelFormat::NV12;
|
|
||||||
if (IsEqualGUID(guid, MFVideoFormat_I420)) return PixelFormat::I420;
|
|
||||||
return PixelFormat::NO_CHANGE;
|
|
||||||
};
|
|
||||||
|
|
||||||
MFGrabber::MFGrabber(const QString & device, unsigned width, unsigned height, unsigned fps, unsigned input, int pixelDecimation)
|
MFGrabber::MFGrabber(const QString & device, unsigned width, unsigned height, unsigned fps, unsigned input, int pixelDecimation)
|
||||||
: Grabber("V4L2:"+device)
|
: Grabber("V4L2:"+device)
|
||||||
, _deviceName(device)
|
, _deviceName(device)
|
||||||
, _buffers()
|
|
||||||
, _hr(S_FALSE)
|
, _hr(S_FALSE)
|
||||||
, _sourceReader(nullptr)
|
, _sourceReader(nullptr)
|
||||||
, _pixelDecimation(pixelDecimation)
|
, _pixelDecimation(pixelDecimation)
|
||||||
@ -47,7 +34,7 @@ MFGrabber::MFGrabber(const QString & device, unsigned width, unsigned height, un
|
|||||||
|
|
||||||
CoInitializeEx(0, COINIT_MULTITHREADED);
|
CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||||
_hr = MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET);
|
_hr = MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET);
|
||||||
if (FAILED(_hr))
|
if(FAILED(_hr))
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
else
|
else
|
||||||
_sourceReaderCB = new SourceReaderCB(this);
|
_sourceReaderCB = new SourceReaderCB(this);
|
||||||
@ -60,13 +47,13 @@ MFGrabber::~MFGrabber()
|
|||||||
SAFE_RELEASE(_sourceReader);
|
SAFE_RELEASE(_sourceReader);
|
||||||
SAFE_RELEASE(_sourceReaderCB);
|
SAFE_RELEASE(_sourceReaderCB);
|
||||||
|
|
||||||
if (SUCCEEDED(_hr) && SUCCEEDED(MFShutdown()))
|
if(SUCCEEDED(_hr) && SUCCEEDED(MFShutdown()))
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MFGrabber::init()
|
bool MFGrabber::init()
|
||||||
{
|
{
|
||||||
if (!_initialized && SUCCEEDED(_hr))
|
if(!_initialized && SUCCEEDED(_hr))
|
||||||
{
|
{
|
||||||
QString foundDevice = "";
|
QString foundDevice = "";
|
||||||
int foundIndex = -1, bestGuess = -1, bestGuessMinX = INT_MAX, bestGuessMinFPS = INT_MAX;
|
int foundIndex = -1, bestGuess = -1, bestGuessMinX = INT_MAX, bestGuessMinFPS = INT_MAX;
|
||||||
@ -75,16 +62,16 @@ bool MFGrabber::init()
|
|||||||
// enumerate the video capture devices on the user's system
|
// enumerate the video capture devices on the user's system
|
||||||
enumVideoCaptureDevices();
|
enumVideoCaptureDevices();
|
||||||
|
|
||||||
if (!autoDiscovery && !_deviceProperties.contains(_deviceName))
|
if(!autoDiscovery && !_deviceProperties.contains(_deviceName))
|
||||||
{
|
{
|
||||||
Debug(_log, "Device '%s' is not available. Changing to auto.", QSTRING_CSTR(_deviceName));
|
Debug(_log, "Device '%s' is not available. Changing to auto.", QSTRING_CSTR(_deviceName));
|
||||||
autoDiscovery = true;
|
autoDiscovery = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoDiscovery)
|
if(autoDiscovery)
|
||||||
{
|
{
|
||||||
Debug(_log, "Forcing auto discovery device");
|
Debug(_log, "Forcing auto discovery device");
|
||||||
if (_deviceProperties.count()>0)
|
if(_deviceProperties.count()>0)
|
||||||
{
|
{
|
||||||
foundDevice = _deviceProperties.firstKey();
|
foundDevice = _deviceProperties.firstKey();
|
||||||
_deviceName = foundDevice;
|
_deviceName = foundDevice;
|
||||||
@ -94,7 +81,7 @@ bool MFGrabber::init()
|
|||||||
else
|
else
|
||||||
foundDevice = _deviceName;
|
foundDevice = _deviceName;
|
||||||
|
|
||||||
if (foundDevice.isNull() || foundDevice.isEmpty() || !_deviceProperties.contains(foundDevice))
|
if(foundDevice.isNull() || foundDevice.isEmpty() || !_deviceProperties.contains(foundDevice))
|
||||||
{
|
{
|
||||||
Error(_log, "Could not find any capture device");
|
Error(_log, "Could not find any capture device");
|
||||||
return false;
|
return false;
|
||||||
@ -109,7 +96,7 @@ bool MFGrabber::init()
|
|||||||
bool strict = false;
|
bool strict = false;
|
||||||
const auto& val = dev.valid[i];
|
const auto& val = dev.valid[i];
|
||||||
|
|
||||||
if (bestGuess == -1 || (val.x <= bestGuessMinX && val.x >= 640 && val.fps <= bestGuessMinFPS && val.fps >= 10))
|
if(bestGuess == -1 || (val.x <= bestGuessMinX && val.x >= 640 && val.fps <= bestGuessMinFPS && val.fps >= 10))
|
||||||
{
|
{
|
||||||
bestGuess = i;
|
bestGuess = i;
|
||||||
bestGuessMinFPS = val.fps;
|
bestGuessMinFPS = val.fps;
|
||||||
@ -119,31 +106,31 @@ bool MFGrabber::init()
|
|||||||
if(_width && _height)
|
if(_width && _height)
|
||||||
{
|
{
|
||||||
strict = true;
|
strict = true;
|
||||||
if (val.x != _width || val.y != _height)
|
if(val.x != _width || val.y != _height)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_fps && _fps!=15)
|
if(_fps && _fps!=15)
|
||||||
{
|
{
|
||||||
strict = true;
|
strict = true;
|
||||||
if (val.fps != _fps)
|
if(val.fps != _fps)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_pixelFormat != PixelFormat::NO_CHANGE)
|
if(_pixelFormat != PixelFormat::NO_CHANGE)
|
||||||
{
|
{
|
||||||
strict = true;
|
strict = true;
|
||||||
if (val.pf != _pixelFormat)
|
if(val.pf != _pixelFormat)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strict && (val.fps <= 60 || _fps != 15))
|
if(strict && (val.fps <= 60 || _fps != 15))
|
||||||
foundIndex = i;
|
foundIndex = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundIndex < 0 && bestGuess >= 0)
|
if(foundIndex < 0 && bestGuess >= 0)
|
||||||
{
|
{
|
||||||
if (!autoDiscovery)
|
if(!autoDiscovery)
|
||||||
Warning(_log, "Selected resolution not found in supported modes. Forcing best resolution");
|
Warning(_log, "Selected resolution not found in supported modes. Forcing best resolution");
|
||||||
else
|
else
|
||||||
Debug(_log, "Forcing best resolution");
|
Debug(_log, "Forcing best resolution");
|
||||||
@ -151,9 +138,9 @@ bool MFGrabber::init()
|
|||||||
foundIndex = bestGuess;
|
foundIndex = bestGuess;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundIndex>=0)
|
if(foundIndex>=0)
|
||||||
{
|
{
|
||||||
if (init_device(foundDevice, dev.valid[foundIndex]))
|
if(SUCCEEDED(init_device(foundDevice, dev.valid[foundIndex])))
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -166,219 +153,229 @@ bool MFGrabber::init()
|
|||||||
void MFGrabber::uninit()
|
void MFGrabber::uninit()
|
||||||
{
|
{
|
||||||
// stop if the grabber was not stopped
|
// stop if the grabber was not stopped
|
||||||
if (_initialized)
|
if(_initialized)
|
||||||
{
|
{
|
||||||
Debug(_log,"uninit grabber: %s", QSTRING_CSTR(_deviceName));
|
Debug(_log,"uninit grabber: %s", QSTRING_CSTR(_deviceName));
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MFGrabber::init_device(QString deviceName, DevicePropertiesItem props)
|
HRESULT MFGrabber::init_device(QString deviceName, DevicePropertiesItem props)
|
||||||
{
|
{
|
||||||
bool setStreamParamOK = false;
|
|
||||||
PixelFormat pixelformat = GetPixelFormatForGuid(props.guid);
|
PixelFormat pixelformat = GetPixelFormatForGuid(props.guid);
|
||||||
QString error, guid = _deviceProperties[deviceName].name;
|
QString error, guid = _deviceProperties[deviceName].name;
|
||||||
HRESULT hr,hr1,hr2;
|
int size = guid.length() + 1024;
|
||||||
|
wchar_t *name = new wchar_t[size];
|
||||||
|
memset(name, 0, size);
|
||||||
|
guid.toWCharArray(name);
|
||||||
|
IMFMediaSource* device = nullptr;
|
||||||
|
IMFAttributes* deviceAttributes = nullptr, *sourceReaderAttributes = nullptr;
|
||||||
|
IAMVideoProcAmp *pProcAmp = nullptr;
|
||||||
|
IMFMediaType* type = nullptr;
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
Debug(_log, "Init %s, %d x %d @ %d fps (%s) => %s", QSTRING_CSTR(deviceName), props.x, props.y, props.fps, QSTRING_CSTR(pixelFormatToString(pixelformat)), QSTRING_CSTR(guid));
|
Debug(_log, "Init %s, %d x %d @ %d fps (%s) => %s", QSTRING_CSTR(deviceName), props.x, props.y, props.fps, QSTRING_CSTR(pixelFormatToString(pixelformat)), QSTRING_CSTR(guid));
|
||||||
|
|
||||||
IMFMediaSource* device = nullptr;
|
hr = MFCreateAttributes(&deviceAttributes, 2);
|
||||||
IMFAttributes* attr;
|
if(FAILED(hr))
|
||||||
hr = MFCreateAttributes(&attr, 2);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
{
|
||||||
hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
|
error = QString("Could not create device attributes (%1)").arg(hr);
|
||||||
if (SUCCEEDED(hr))
|
goto done;
|
||||||
{
|
}
|
||||||
int size = guid.length() + 1024;
|
|
||||||
wchar_t *name = new wchar_t[size];
|
|
||||||
memset(name, 0, size);
|
|
||||||
guid.toWCharArray(name);
|
|
||||||
|
|
||||||
if (SUCCEEDED(attr->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, (LPCWSTR)name)) && _sourceReaderCB)
|
hr = deviceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
|
||||||
{
|
if(FAILED(hr))
|
||||||
hr = MFCreateDeviceSource(attr, &device);
|
{
|
||||||
if (FAILED(hr))
|
error = QString("SetGUID_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE (%1)").arg(hr);
|
||||||
{
|
goto done;
|
||||||
SAFE_RELEASE(device);;
|
}
|
||||||
error = QString("MFCreateDeviceSource %1").arg(hr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error = QString("IMFAttributes_SetString_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK %1").arg(hr);
|
|
||||||
|
|
||||||
delete[] name;
|
if(FAILED(deviceAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, (LPCWSTR)name)) && _sourceReaderCB)
|
||||||
}
|
{
|
||||||
SAFE_RELEASE(attr);
|
error = QString("IMFAttributes_SetString_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = MFCreateDeviceSource(deviceAttributes, &device);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("MFCreateDeviceSource (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!device)
|
||||||
|
{
|
||||||
|
error = QString("Could not open device (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
SAFE_RELEASE(attr);
|
|
||||||
error = QString("MFCreateAttributes_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE %1").arg(hr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device)
|
|
||||||
{
|
|
||||||
Debug(_log, "Device opened");
|
Debug(_log, "Device opened");
|
||||||
if (_brightness != 0 || _contrast != 0 || _saturation != 0 || _hue != 0)
|
|
||||||
|
// Set Brightness/Contrast/Saturation/Hue
|
||||||
|
if (_brightness != 0 || _contrast != 0 || _saturation != 0 || _hue != 0)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&pProcAmp))))
|
||||||
{
|
{
|
||||||
IAMVideoProcAmp *pProcAmp = NULL;
|
long lMin, lMax, lStep, lDefault, lCaps, Val;
|
||||||
if (SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&pProcAmp))))
|
if (_brightness != 0)
|
||||||
{
|
{
|
||||||
long lMin, lMax, lStep, lDefault, lCaps, Val;
|
if (SUCCEEDED(pProcAmp->GetRange(VideoProcAmp_Brightness, &lMin, &lMax, &lStep, &lDefault, &lCaps)))
|
||||||
if (_brightness != 0)
|
|
||||||
{
|
{
|
||||||
if (SUCCEEDED(pProcAmp->GetRange(VideoProcAmp_Brightness, &lMin, &lMax, &lStep, &lDefault, &lCaps)))
|
Debug(_log, "Brightness: min=%i, max=%i, default=%i", lMin, lMax, lDefault);
|
||||||
{
|
|
||||||
Debug(_log, "Brightness: min=%i, max=%i, default=%i", lMin, lMax, lDefault);
|
|
||||||
|
|
||||||
if (SUCCEEDED(pProcAmp->Get(VideoProcAmp_Brightness, &Val, &lCaps)))
|
if (SUCCEEDED(pProcAmp->Get(VideoProcAmp_Brightness, &Val, &lCaps)))
|
||||||
Debug(_log, "Current brightness set to: %i",Val);
|
Debug(_log, "Current brightness set to: %i",Val);
|
||||||
|
|
||||||
if (SUCCEEDED(pProcAmp->Set(VideoProcAmp_Brightness, _brightness, VideoProcAmp_Flags_Manual)))
|
if (SUCCEEDED(pProcAmp->Set(VideoProcAmp_Brightness, _brightness, VideoProcAmp_Flags_Manual)))
|
||||||
Debug(_log, "Brightness set to: %i",_brightness);
|
Debug(_log, "Brightness set to: %i",_brightness);
|
||||||
else
|
|
||||||
Error(_log, "Could not set brightness");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
Error(_log, "Brightness is not supported by the grabber");
|
Error(_log, "Could not set brightness");
|
||||||
}
|
|
||||||
|
|
||||||
if (_contrast != 0)
|
|
||||||
{
|
|
||||||
if (SUCCEEDED(pProcAmp->GetRange(VideoProcAmp_Contrast, &lMin, &lMax, &lStep, &lDefault, &lCaps)))
|
|
||||||
{
|
|
||||||
Debug(_log, "Contrast: min=%i, max=%i, default=%i", lMin, lMax, lDefault);
|
|
||||||
|
|
||||||
if (SUCCEEDED(pProcAmp->Get(VideoProcAmp_Contrast, &Val, &lCaps)))
|
|
||||||
Debug(_log, "Current contrast set to: %i",Val);
|
|
||||||
|
|
||||||
if (SUCCEEDED(pProcAmp->Set(VideoProcAmp_Contrast, _contrast, VideoProcAmp_Flags_Manual)))
|
|
||||||
Debug(_log, "Contrast set to: %i",_contrast);
|
|
||||||
else
|
|
||||||
Error(_log, "Could not set contrast");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Error(_log, "Contrast is not supported by the grabber");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_saturation != 0)
|
|
||||||
{
|
|
||||||
if (SUCCEEDED(pProcAmp->GetRange(VideoProcAmp_Saturation, &lMin, &lMax, &lStep, &lDefault, &lCaps)))
|
|
||||||
{
|
|
||||||
Debug(_log, "Saturation: min=%i, max=%i, default=%i", lMin, lMax, lDefault);
|
|
||||||
|
|
||||||
if (SUCCEEDED(pProcAmp->Get(VideoProcAmp_Saturation, &Val, &lCaps)))
|
|
||||||
Debug(_log, "Current saturation set to: %i",Val);
|
|
||||||
|
|
||||||
if (SUCCEEDED(pProcAmp->Set(VideoProcAmp_Saturation, _saturation, VideoProcAmp_Flags_Manual)))
|
|
||||||
Debug(_log, "Saturation set to: %i",_saturation);
|
|
||||||
else
|
|
||||||
Error(_log, "Could not set saturation");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Error(_log, "Saturation is not supported by the grabber");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_hue != 0)
|
|
||||||
{
|
|
||||||
hr = pProcAmp->GetRange(VideoProcAmp_Hue, &lMin, &lMax, &lStep, &lDefault, &lCaps);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
Debug(_log, "Hue: min=%i, max=%i, default=%i", lMin, lMax, lDefault);
|
|
||||||
|
|
||||||
hr = pProcAmp->Get(VideoProcAmp_Hue, &Val, &lCaps);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
Debug(_log, "Current hue set to: %i",Val);
|
|
||||||
|
|
||||||
hr = pProcAmp->Set(VideoProcAmp_Hue, _hue, VideoProcAmp_Flags_Manual);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
Debug(_log, "Hue set to: %i",_hue);
|
|
||||||
else
|
|
||||||
Error(_log, "Could not set hue");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Error(_log, "Hue is not supported by the grabber");
|
|
||||||
}
|
|
||||||
|
|
||||||
pProcAmp->Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IMFAttributes* pAttributes;
|
|
||||||
hr1 = MFCreateAttributes(&pAttributes, 1);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr1))
|
|
||||||
hr2 = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, (IMFSourceReaderCallback *)_sourceReaderCB);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr1) && SUCCEEDED(hr2))
|
|
||||||
hr = MFCreateSourceReaderFromMediaSource(device, pAttributes, &_sourceReader);
|
|
||||||
else
|
|
||||||
hr = E_INVALIDARG;
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr1))
|
|
||||||
pAttributes->Release();
|
|
||||||
|
|
||||||
device->Release();
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
IMFMediaType* type;
|
|
||||||
|
|
||||||
hr = MFCreateMediaType(&type);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = type->SetGUID(MF_MT_SUBTYPE, props.guid);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, props.x, props.y);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = MFSetAttributeSize(type, MF_MT_FRAME_RATE, props.fps_a, props.fps_b);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
MFSetAttributeRatio(type, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
|
|
||||||
|
|
||||||
hr = _sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, type);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
setStreamParamOK = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error = QString("SetCurrentMediaType %1").arg(hr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error = QString("MFSetAttributeSize_MF_MT_FRAME_RATE %1").arg(hr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error = QString("SMFSetAttributeSize_MF_MT_FRAME_SIZE %1").arg(hr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error = QString("SetGUID_MF_MT_SUBTYPE %1").arg(hr);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
error = QString("SetGUID_MF_MT_MAJOR_TYPE %1").arg(hr);
|
Error(_log, "Brightness is not supported by the grabber");
|
||||||
|
|
||||||
type->Release();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
error = QString("IMFAttributes_SetString %1").arg(hr);
|
|
||||||
|
|
||||||
if (!setStreamParamOK)
|
if (_contrast != 0)
|
||||||
Error(_log, "Could not stream set params (%s)", QSTRING_CSTR(error));
|
{
|
||||||
|
if (SUCCEEDED(pProcAmp->GetRange(VideoProcAmp_Contrast, &lMin, &lMax, &lStep, &lDefault, &lCaps)))
|
||||||
|
{
|
||||||
|
Debug(_log, "Contrast: min=%i, max=%i, default=%i", lMin, lMax, lDefault);
|
||||||
|
|
||||||
|
if (SUCCEEDED(pProcAmp->Get(VideoProcAmp_Contrast, &Val, &lCaps)))
|
||||||
|
Debug(_log, "Current contrast set to: %i",Val);
|
||||||
|
|
||||||
|
if (SUCCEEDED(pProcAmp->Set(VideoProcAmp_Contrast, _contrast, VideoProcAmp_Flags_Manual)))
|
||||||
|
Debug(_log, "Contrast set to: %i",_contrast);
|
||||||
|
else
|
||||||
|
Error(_log, "Could not set contrast");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Error(_log, "Contrast is not supported by the grabber");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_saturation != 0)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(pProcAmp->GetRange(VideoProcAmp_Saturation, &lMin, &lMax, &lStep, &lDefault, &lCaps)))
|
||||||
|
{
|
||||||
|
Debug(_log, "Saturation: min=%i, max=%i, default=%i", lMin, lMax, lDefault);
|
||||||
|
|
||||||
|
if (SUCCEEDED(pProcAmp->Get(VideoProcAmp_Saturation, &Val, &lCaps)))
|
||||||
|
Debug(_log, "Current saturation set to: %i",Val);
|
||||||
|
|
||||||
|
if (SUCCEEDED(pProcAmp->Set(VideoProcAmp_Saturation, _saturation, VideoProcAmp_Flags_Manual)))
|
||||||
|
Debug(_log, "Saturation set to: %i",_saturation);
|
||||||
|
else
|
||||||
|
Error(_log, "Could not set saturation");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Error(_log, "Saturation is not supported by the grabber");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_hue != 0)
|
||||||
|
{
|
||||||
|
hr = pProcAmp->GetRange(VideoProcAmp_Hue, &lMin, &lMax, &lStep, &lDefault, &lCaps);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
Debug(_log, "Hue: min=%i, max=%i, default=%i", lMin, lMax, lDefault);
|
||||||
|
|
||||||
|
hr = pProcAmp->Get(VideoProcAmp_Hue, &Val, &lCaps);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
Debug(_log, "Current hue set to: %i",Val);
|
||||||
|
|
||||||
|
hr = pProcAmp->Set(VideoProcAmp_Hue, _hue, VideoProcAmp_Flags_Manual);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
Debug(_log, "Hue set to: %i",_hue);
|
||||||
|
else
|
||||||
|
Error(_log, "Could not set hue");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Error(_log, "Hue is not supported by the grabber");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
Error(_log, "MFCreateSourceReaderFromMediaSource (%i)", hr);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
Error(_log, "Could not open device (%s)", QSTRING_CSTR(error));
|
|
||||||
|
|
||||||
if (!setStreamParamOK)
|
hr = MFCreateAttributes(&sourceReaderAttributes, 1);
|
||||||
|
if(FAILED(hr))
|
||||||
{
|
{
|
||||||
|
error = QString("Could not create Source Reader attributes (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = sourceReaderAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, (IMFSourceReaderCallback *)_sourceReaderCB);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Could not set stream parameter: SetUnknown_MF_SOURCE_READER_ASYNC_CALLBACK (%1)").arg(hr);
|
||||||
|
hr = E_INVALIDARG;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = MFCreateSourceReaderFromMediaSource(device, sourceReaderAttributes, &_sourceReader);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Could not create the Source Reader (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = MFCreateMediaType(&type);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Could not create an empty media type (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Could not set stream parameter: SetGUID_MF_MT_MAJOR_TYPE (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = type->SetGUID(MF_MT_SUBTYPE, props.guid);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Could not set stream parameter: SetGUID_MF_MT_SUBTYPE (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, props.x, props.y);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Could not set stream parameter: SMFSetAttributeSize_MF_MT_FRAME_SIZE (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = MFSetAttributeSize(type, MF_MT_FRAME_RATE, props.fps_a, props.fps_b);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Could not set stream parameter: MFSetAttributeSize_MF_MT_FRAME_RATE (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = MFSetAttributeRatio(type, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Could not set stream parameter: MFSetAttributeRatio_MF_MT_PIXEL_ASPECT_RATIO (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = _sourceReaderCB->InitializeVideoEncoder(type, pixelformat);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Failed to initialize the Video Encoder (%1)").arg(hr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = _sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr, type);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
error = QString("Failed to set media type on Source Reader (%1)").arg(hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
Error(_log, "%s", QSTRING_CSTR(error));
|
||||||
SAFE_RELEASE(_sourceReader);
|
SAFE_RELEASE(_sourceReader);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -386,49 +383,19 @@ bool MFGrabber::init_device(QString deviceName, DevicePropertiesItem props)
|
|||||||
_pixelFormat = props.pf;
|
_pixelFormat = props.pf;
|
||||||
_width = props.x;
|
_width = props.x;
|
||||||
_height = props.y;
|
_height = props.y;
|
||||||
|
_frameByteSize = props.x * props.y * 3;
|
||||||
switch (_pixelFormat)
|
_lineLength = props.x * 3;
|
||||||
{
|
|
||||||
case PixelFormat::UYVY:
|
|
||||||
case PixelFormat::YUYV:
|
|
||||||
{
|
|
||||||
_frameByteSize = props.x * props.y * 2;
|
|
||||||
_lineLength = props.x * 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PixelFormat::BGR24:
|
|
||||||
case PixelFormat::MJPEG:
|
|
||||||
{
|
|
||||||
_frameByteSize = props.x * props.y * 3;
|
|
||||||
_lineLength = props.x * 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PixelFormat::RGB32:
|
|
||||||
{
|
|
||||||
_frameByteSize = props.x * props.y * 4;
|
|
||||||
_lineLength = props.x * 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PixelFormat::NV12:
|
|
||||||
{
|
|
||||||
_frameByteSize = (6 * props.x * props.y) / 4;
|
|
||||||
_lineLength = props.x;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PixelFormat::I420:
|
|
||||||
{
|
|
||||||
_frameByteSize = (6 * props.x * props.y) / 4;
|
|
||||||
_lineLength = props.x;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return setStreamParamOK;
|
// Cleanup
|
||||||
|
SAFE_RELEASE(deviceAttributes);
|
||||||
|
delete[] name;
|
||||||
|
SAFE_RELEASE(device);
|
||||||
|
SAFE_RELEASE(pProcAmp);
|
||||||
|
SAFE_RELEASE(type);
|
||||||
|
SAFE_RELEASE(sourceReaderAttributes);
|
||||||
|
|
||||||
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MFGrabber::uninit_device()
|
void MFGrabber::uninit_device()
|
||||||
@ -438,7 +405,7 @@ void MFGrabber::uninit_device()
|
|||||||
|
|
||||||
void MFGrabber::enumVideoCaptureDevices()
|
void MFGrabber::enumVideoCaptureDevices()
|
||||||
{
|
{
|
||||||
if (FAILED(_hr))
|
if(FAILED(_hr))
|
||||||
{
|
{
|
||||||
Error(_log, "enumVideoCaptureDevices(): Media Foundation not initialized");
|
Error(_log, "enumVideoCaptureDevices(): Media Foundation not initialized");
|
||||||
return;
|
return;
|
||||||
@ -456,7 +423,7 @@ void MFGrabber::enumVideoCaptureDevices()
|
|||||||
if(SUCCEEDED(MFEnumDeviceSources(attr, &devices, &count)))
|
if(SUCCEEDED(MFEnumDeviceSources(attr, &devices, &count)))
|
||||||
{
|
{
|
||||||
Debug(_log, "Detected devices: %u", count);
|
Debug(_log, "Detected devices: %u", count);
|
||||||
for (UINT32 i = 0; i < count; i++)
|
for(UINT32 i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
UINT32 length;
|
UINT32 length;
|
||||||
LPWSTR name;
|
LPWSTR name;
|
||||||
@ -529,18 +496,22 @@ void MFGrabber::enumVideoCaptureDevices()
|
|||||||
}
|
}
|
||||||
pSource->Release();
|
pSource->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
properties.displayResolutions.sort();
|
properties.displayResolutions.sort();
|
||||||
properties.framerates.sort();
|
properties.framerates.sort();
|
||||||
_deviceProperties.insert(dev, properties);
|
_deviceProperties.insert(dev, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
CoTaskMemFree(symlink);
|
CoTaskMemFree(symlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
CoTaskMemFree(name);
|
CoTaskMemFree(name);
|
||||||
devices[i]->Release();
|
devices[i]->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
CoTaskMemFree(devices);
|
CoTaskMemFree(devices);
|
||||||
}
|
}
|
||||||
|
|
||||||
attr->Release();
|
attr->Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -550,26 +521,23 @@ void MFGrabber::start_capturing()
|
|||||||
{
|
{
|
||||||
if (_sourceReader)
|
if (_sourceReader)
|
||||||
{
|
{
|
||||||
HRESULT hr = _sourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
|
HRESULT hr = _sourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, NULL, NULL, NULL);
|
||||||
0, NULL, NULL, NULL, NULL);
|
|
||||||
if (!SUCCEEDED(hr))
|
if (!SUCCEEDED(hr))
|
||||||
Error(_log, "ReadSample (%i)", hr);
|
Error(_log, "ReadSample (%i)", hr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MFGrabber::process_image(const void *frameImageBuffer, int size)
|
void MFGrabber::process_image(const void *frameImageBuffer, int size)
|
||||||
{
|
{
|
||||||
bool frameSend = false;
|
|
||||||
|
|
||||||
unsigned int processFrameIndex = _currentFrame++;
|
unsigned int processFrameIndex = _currentFrame++;
|
||||||
|
|
||||||
// frame skipping
|
// frame skipping
|
||||||
if ( (processFrameIndex % _fpsSoftwareDecimation != 0) && (_fpsSoftwareDecimation > 1))
|
if((processFrameIndex % _fpsSoftwareDecimation != 0) && (_fpsSoftwareDecimation > 1))
|
||||||
return frameSend;
|
return;
|
||||||
|
|
||||||
// CEC detection
|
// CEC detection
|
||||||
if (_cecDetectionEnabled)
|
if(_cecDetectionEnabled)
|
||||||
return frameSend;
|
return;
|
||||||
|
|
||||||
// We do want a new frame...
|
// We do want a new frame...
|
||||||
if (size < _frameByteSize && _pixelFormat != PixelFormat::MJPEG)
|
if (size < _frameByteSize && _pixelFormat != PixelFormat::MJPEG)
|
||||||
@ -593,6 +561,7 @@ bool MFGrabber::process_image(const void *frameImageBuffer, int size)
|
|||||||
for (unsigned int i=0;_threadManager.isActive() && i < _threadManager._maxThreads && _threadManager._threads != nullptr; i++)
|
for (unsigned int i=0;_threadManager.isActive() && i < _threadManager._maxThreads && _threadManager._threads != nullptr; i++)
|
||||||
{
|
{
|
||||||
if ((_threadManager._threads[i]->isFinished() || !_threadManager._threads[i]->isRunning()))
|
if ((_threadManager._threads[i]->isFinished() || !_threadManager._threads[i]->isRunning()))
|
||||||
|
{
|
||||||
// aquire lock
|
// aquire lock
|
||||||
if ( _threadManager._threads[i]->isBusy() == false)
|
if ( _threadManager._threads[i]->isBusy() == false)
|
||||||
{
|
{
|
||||||
@ -602,14 +571,12 @@ bool MFGrabber::process_image(const void *frameImageBuffer, int size)
|
|||||||
if (_threadManager._maxThreads > 1)
|
if (_threadManager._maxThreads > 1)
|
||||||
_threadManager._threads[i]->start();
|
_threadManager._threads[i]->start();
|
||||||
|
|
||||||
frameSend = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return frameSend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MFGrabber::setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold)
|
void MFGrabber::setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold)
|
||||||
@ -640,9 +607,9 @@ bool MFGrabber::start()
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_threadManager.start();
|
_threadManager.start();
|
||||||
Info(_log, "Decoding threads: %d",_threadManager._maxThreads );
|
Info(_log, "Decoding threads: %d",_threadManager._maxThreads);
|
||||||
|
|
||||||
if (init())
|
if(init())
|
||||||
{
|
{
|
||||||
start_capturing();
|
start_capturing();
|
||||||
Info(_log, "Started");
|
Info(_log, "Started");
|
||||||
@ -659,7 +626,7 @@ bool MFGrabber::start()
|
|||||||
|
|
||||||
void MFGrabber::stop()
|
void MFGrabber::stop()
|
||||||
{
|
{
|
||||||
if (_initialized)
|
if(_initialized)
|
||||||
{
|
{
|
||||||
_threadManager.stop();
|
_threadManager.stop();
|
||||||
uninit_device();
|
uninit_device();
|
||||||
@ -669,17 +636,9 @@ void MFGrabber::stop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MFGrabber::receive_image(const void *frameImageBuffer, int size, QString message)
|
void MFGrabber::receive_image(const void *frameImageBuffer, int size)
|
||||||
{
|
{
|
||||||
if (frameImageBuffer == NULL || size ==0)
|
process_image(frameImageBuffer, size);
|
||||||
Error(_log, "Received empty image frame: %s", QSTRING_CSTR(message));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!message.isEmpty())
|
|
||||||
Debug(_log, "Received image frame: %s", QSTRING_CSTR(message));
|
|
||||||
process_image(frameImageBuffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
start_capturing();
|
start_capturing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,7 +656,7 @@ void MFGrabber::newThreadFrame(unsigned int threadIndex, const Image<ColorRgb>&
|
|||||||
|
|
||||||
void MFGrabber::checkSignalDetectionEnabled(Image<ColorRgb> image)
|
void MFGrabber::checkSignalDetectionEnabled(Image<ColorRgb> image)
|
||||||
{
|
{
|
||||||
if (_signalDetectionEnabled)
|
if(_signalDetectionEnabled)
|
||||||
{
|
{
|
||||||
// check signal (only in center of the resulting image, because some grabbers have noise values along the borders)
|
// check signal (only in center of the resulting image, because some grabbers have noise values along the borders)
|
||||||
bool noSignal = true;
|
bool noSignal = true;
|
||||||
@ -710,15 +669,15 @@ void MFGrabber::checkSignalDetectionEnabled(Image<ColorRgb> image)
|
|||||||
unsigned xMax = image.width() * _x_frac_max;
|
unsigned xMax = image.width() * _x_frac_max;
|
||||||
unsigned yMax = image.height() * _y_frac_max;
|
unsigned yMax = image.height() * _y_frac_max;
|
||||||
|
|
||||||
for (unsigned x = xOffset; noSignal && x < xMax; ++x)
|
for(unsigned x = xOffset; noSignal && x < xMax; ++x)
|
||||||
for (unsigned y = yOffset; noSignal && y < yMax; ++y)
|
for(unsigned y = yOffset; noSignal && y < yMax; ++y)
|
||||||
noSignal &= (ColorRgb&)image(x, y) <= _noSignalThresholdColor;
|
noSignal &= (ColorRgb&)image(x, y) <= _noSignalThresholdColor;
|
||||||
|
|
||||||
if (noSignal)
|
if(noSignal)
|
||||||
++_noSignalCounter;
|
++_noSignalCounter;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_noSignalCounter >= _noSignalCounterThreshold)
|
if(_noSignalCounter >= _noSignalCounterThreshold)
|
||||||
{
|
{
|
||||||
_noSignalDetected = true;
|
_noSignalDetected = true;
|
||||||
Info(_log, "Signal detected");
|
Info(_log, "Signal detected");
|
||||||
@ -727,11 +686,11 @@ void MFGrabber::checkSignalDetectionEnabled(Image<ColorRgb> image)
|
|||||||
_noSignalCounter = 0;
|
_noSignalCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( _noSignalCounter < _noSignalCounterThreshold)
|
if( _noSignalCounter < _noSignalCounterThreshold)
|
||||||
{
|
{
|
||||||
emit newFrame(image);
|
emit newFrame(image);
|
||||||
}
|
}
|
||||||
else if (_noSignalCounter == _noSignalCounterThreshold)
|
else if(_noSignalCounter == _noSignalCounterThreshold)
|
||||||
{
|
{
|
||||||
_noSignalDetected = false;
|
_noSignalDetected = false;
|
||||||
Info(_log, "Signal lost");
|
Info(_log, "Signal lost");
|
||||||
@ -744,7 +703,7 @@ void MFGrabber::checkSignalDetectionEnabled(Image<ColorRgb> image)
|
|||||||
QStringList MFGrabber::getV4L2devices() const
|
QStringList MFGrabber::getV4L2devices() const
|
||||||
{
|
{
|
||||||
QStringList result = QStringList();
|
QStringList result = QStringList();
|
||||||
for (auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it)
|
for(auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it)
|
||||||
result << it.key();
|
result << it.key();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -755,7 +714,7 @@ QStringList MFGrabber::getV4L2EncodingFormats(const QString& devicePath) const
|
|||||||
QStringList result = QStringList();
|
QStringList result = QStringList();
|
||||||
|
|
||||||
for(int i = 0; i < _deviceProperties[devicePath].valid.count(); ++i )
|
for(int i = 0; i < _deviceProperties[devicePath].valid.count(); ++i )
|
||||||
if (!result.contains(pixelFormatToString(_deviceProperties[devicePath].valid[i].pf), Qt::CaseInsensitive))
|
if(!result.contains(pixelFormatToString(_deviceProperties[devicePath].valid[i].pf), Qt::CaseInsensitive))
|
||||||
result << pixelFormatToString(_deviceProperties[devicePath].valid[i].pf).toLower();
|
result << pixelFormatToString(_deviceProperties[devicePath].valid[i].pf).toLower();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -763,7 +722,7 @@ QStringList MFGrabber::getV4L2EncodingFormats(const QString& devicePath) const
|
|||||||
|
|
||||||
void MFGrabber::setSignalDetectionEnable(bool enable)
|
void MFGrabber::setSignalDetectionEnable(bool enable)
|
||||||
{
|
{
|
||||||
if (_signalDetectionEnabled != enable)
|
if(_signalDetectionEnabled != enable)
|
||||||
{
|
{
|
||||||
_signalDetectionEnabled = enable;
|
_signalDetectionEnabled = enable;
|
||||||
Info(_log, "Signal detection is now %s", enable ? "enabled" : "disabled");
|
Info(_log, "Signal detection is now %s", enable ? "enabled" : "disabled");
|
||||||
@ -772,7 +731,7 @@ void MFGrabber::setSignalDetectionEnable(bool enable)
|
|||||||
|
|
||||||
void MFGrabber::setCecDetectionEnable(bool enable)
|
void MFGrabber::setCecDetectionEnable(bool enable)
|
||||||
{
|
{
|
||||||
if (_cecDetectionEnabled != enable)
|
if(_cecDetectionEnabled != enable)
|
||||||
{
|
{
|
||||||
_cecDetectionEnabled = enable;
|
_cecDetectionEnabled = enable;
|
||||||
Info(_log, QString("CEC detection is now %1").arg(enable ? "enabled" : "disabled").toLocal8Bit());
|
Info(_log, QString("CEC detection is now %1").arg(enable ? "enabled" : "disabled").toLocal8Bit());
|
||||||
@ -781,16 +740,16 @@ void MFGrabber::setCecDetectionEnable(bool enable)
|
|||||||
|
|
||||||
void MFGrabber::setPixelDecimation(int pixelDecimation)
|
void MFGrabber::setPixelDecimation(int pixelDecimation)
|
||||||
{
|
{
|
||||||
if (_pixelDecimation != pixelDecimation)
|
if(_pixelDecimation != pixelDecimation)
|
||||||
_pixelDecimation = pixelDecimation;
|
_pixelDecimation = pixelDecimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MFGrabber::setDeviceVideoStandard(QString device, VideoStandard videoStandard)
|
void MFGrabber::setDeviceVideoStandard(QString device, VideoStandard videoStandard)
|
||||||
{
|
{
|
||||||
if (_deviceName != device)
|
if(_deviceName != device)
|
||||||
{
|
{
|
||||||
_deviceName = device;
|
_deviceName = device;
|
||||||
if (_initialized && !device.isEmpty())
|
if(_initialized && !device.isEmpty())
|
||||||
{
|
{
|
||||||
Debug(_log,"Restarting Media Foundation grabber");
|
Debug(_log,"Restarting Media Foundation grabber");
|
||||||
uninit();
|
uninit();
|
||||||
@ -819,7 +778,7 @@ bool MFGrabber::setWidthHeight(int width, int height)
|
|||||||
if(Grabber::setWidthHeight(width,height))
|
if(Grabber::setWidthHeight(width,height))
|
||||||
{
|
{
|
||||||
Debug(_log,"Set width:height to: %i:&i", width, height);
|
Debug(_log,"Set width:height to: %i:&i", width, height);
|
||||||
if (_initialized)
|
if(_initialized)
|
||||||
{
|
{
|
||||||
Debug(_log,"Restarting Media Foundation grabber");
|
Debug(_log,"Restarting Media Foundation grabber");
|
||||||
uninit();
|
uninit();
|
||||||
@ -835,7 +794,7 @@ bool MFGrabber::setFramerate(int fps)
|
|||||||
if(Grabber::setFramerate(fps))
|
if(Grabber::setFramerate(fps))
|
||||||
{
|
{
|
||||||
Debug(_log,"Set fps to: %i", fps);
|
Debug(_log,"Set fps to: %i", fps);
|
||||||
if (_initialized)
|
if(_initialized)
|
||||||
{
|
{
|
||||||
Debug(_log,"Restarting Media Foundation grabber");
|
Debug(_log,"Restarting Media Foundation grabber");
|
||||||
uninit();
|
uninit();
|
||||||
@ -849,17 +808,17 @@ bool MFGrabber::setFramerate(int fps)
|
|||||||
void MFGrabber::setFpsSoftwareDecimation(int decimation)
|
void MFGrabber::setFpsSoftwareDecimation(int decimation)
|
||||||
{
|
{
|
||||||
_fpsSoftwareDecimation = decimation;
|
_fpsSoftwareDecimation = decimation;
|
||||||
if (decimation > 1)
|
if(decimation > 1)
|
||||||
Debug(_log,"Every %ith image per second are processed", decimation);
|
Debug(_log,"Every %ith image per second are processed", decimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MFGrabber::setEncoding(QString enc)
|
void MFGrabber::setEncoding(QString enc)
|
||||||
{
|
{
|
||||||
if (_pixelFormat != parsePixelFormat(enc))
|
if(_pixelFormat != parsePixelFormat(enc))
|
||||||
{
|
{
|
||||||
Debug(_log,"Set encoding to: %s", QSTRING_CSTR(enc));
|
Debug(_log,"Set encoding to: %s", QSTRING_CSTR(enc));
|
||||||
_pixelFormat = parsePixelFormat(enc);
|
_pixelFormat = parsePixelFormat(enc);
|
||||||
if (_initialized)
|
if(_initialized)
|
||||||
{
|
{
|
||||||
Debug(_log,"Restarting Media Foundation Grabber");
|
Debug(_log,"Restarting Media Foundation Grabber");
|
||||||
uninit();
|
uninit();
|
||||||
@ -870,7 +829,7 @@ void MFGrabber::setEncoding(QString enc)
|
|||||||
|
|
||||||
void MFGrabber::setBrightnessContrastSaturationHue(int brightness, int contrast, int saturation, int hue)
|
void MFGrabber::setBrightnessContrastSaturationHue(int brightness, int contrast, int saturation, int hue)
|
||||||
{
|
{
|
||||||
if (_brightness != brightness || _contrast != contrast || _saturation != saturation || _hue != hue)
|
if(_brightness != brightness || _contrast != contrast || _saturation != saturation || _hue != hue)
|
||||||
{
|
{
|
||||||
_brightness = brightness;
|
_brightness = brightness;
|
||||||
_contrast = contrast;
|
_contrast = contrast;
|
||||||
@ -879,7 +838,7 @@ void MFGrabber::setBrightnessContrastSaturationHue(int brightness, int contrast,
|
|||||||
|
|
||||||
Debug(_log,"Set brightness to %i, contrast to %i, saturation to %i, hue to %i", _brightness, _contrast, _saturation, _hue);
|
Debug(_log,"Set brightness to %i, contrast to %i, saturation to %i, hue to %i", _brightness, _contrast, _saturation, _hue);
|
||||||
|
|
||||||
if (_initialized)
|
if(_initialized)
|
||||||
{
|
{
|
||||||
Debug(_log,"Restarting Media Foundation Grabber");
|
Debug(_log,"Restarting Media Foundation Grabber");
|
||||||
uninit();
|
uninit();
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <mfapi.h>
|
#include <mfapi.h>
|
||||||
|
#include <mftransform.h>
|
||||||
|
#include <dmo.h>
|
||||||
|
#include <wmcodecdsp.h>
|
||||||
#include <mfidl.h>
|
#include <mfidl.h>
|
||||||
#include <mfreadwrite.h>
|
#include <mfreadwrite.h>
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
@ -13,11 +15,26 @@
|
|||||||
#pragma comment (lib, "mfplat.lib")
|
#pragma comment (lib, "mfplat.lib")
|
||||||
#pragma comment (lib, "mfuuid.lib")
|
#pragma comment (lib, "mfuuid.lib")
|
||||||
#pragma comment (lib, "mfreadwrite.lib")
|
#pragma comment (lib, "mfreadwrite.lib")
|
||||||
|
#pragma comment (lib, "strmiids.lib")
|
||||||
|
#pragma comment (lib, "wmcodecdspuuid.lib")
|
||||||
|
|
||||||
#include <grabber/MFGrabber.h>
|
#include <grabber/MFGrabber.h>
|
||||||
|
|
||||||
#define SAFE_RELEASE(x) if(x) { x->Release(); x = nullptr; }
|
#define SAFE_RELEASE(x) if(x) { x->Release(); x = nullptr; }
|
||||||
|
|
||||||
|
// Need more supported formats? Visit https://docs.microsoft.com/en-us/windows/win32/medfound/colorconverter
|
||||||
|
static PixelFormat GetPixelFormatForGuid(const GUID guid)
|
||||||
|
{
|
||||||
|
if (IsEqualGUID(guid, MFVideoFormat_RGB32)) return PixelFormat::RGB32;
|
||||||
|
if (IsEqualGUID(guid, MFVideoFormat_RGB24)) return PixelFormat::BGR24;
|
||||||
|
if (IsEqualGUID(guid, MFVideoFormat_YUY2)) return PixelFormat::YUYV;
|
||||||
|
if (IsEqualGUID(guid, MFVideoFormat_UYVY)) return PixelFormat::UYVY;
|
||||||
|
if (IsEqualGUID(guid, MFVideoFormat_MJPG)) return PixelFormat::MJPEG;
|
||||||
|
if (IsEqualGUID(guid, MFVideoFormat_NV12)) return PixelFormat::NV12;
|
||||||
|
if (IsEqualGUID(guid, MFVideoFormat_I420)) return PixelFormat::I420;
|
||||||
|
return PixelFormat::NO_CHANGE;
|
||||||
|
};
|
||||||
|
|
||||||
class SourceReaderCB : public IMFSourceReaderCallback
|
class SourceReaderCB : public IMFSourceReaderCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -57,48 +74,145 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IMFSourceReaderCallback methods
|
// IMFSourceReaderCallback methods
|
||||||
STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
|
STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD /*dwStreamIndex*/,
|
||||||
DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample)
|
DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample)
|
||||||
{
|
{
|
||||||
EnterCriticalSection(&_critsec);
|
EnterCriticalSection(&_critsec);
|
||||||
|
|
||||||
|
if(dwStreamFlags & MF_SOURCE_READERF_STREAMTICK)
|
||||||
|
{
|
||||||
|
Debug(_grabber->_log, "Skipping stream gap");
|
||||||
|
LeaveCriticalSection(&_critsec);
|
||||||
|
_grabber->_sourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, NULL, NULL, NULL);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwStreamFlags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
|
||||||
|
{
|
||||||
|
IMFMediaType *type = nullptr;
|
||||||
|
GUID format;
|
||||||
|
_grabber->_sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, MF_SOURCE_READER_CURRENT_TYPE_INDEX, &type);
|
||||||
|
type->GetGUID(MF_MT_SUBTYPE, &format);
|
||||||
|
Debug(_grabber->_log, "Native media type changed");
|
||||||
|
InitializeVideoEncoder(type, GetPixelFormatForGuid(format));
|
||||||
|
SAFE_RELEASE(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwStreamFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
|
||||||
|
{
|
||||||
|
IMFMediaType *type = nullptr;
|
||||||
|
GUID format;
|
||||||
|
_grabber->_sourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, &type);
|
||||||
|
type->GetGUID(MF_MT_SUBTYPE, &format);
|
||||||
|
Debug(_grabber->_log, "Current media type changed");
|
||||||
|
InitializeVideoEncoder(type, GetPixelFormatForGuid(format));
|
||||||
|
SAFE_RELEASE(type);
|
||||||
|
}
|
||||||
|
|
||||||
if (SUCCEEDED(hrStatus))
|
if (SUCCEEDED(hrStatus))
|
||||||
{
|
{
|
||||||
QString error = "";
|
QString error = "";
|
||||||
bool frameSend = false;
|
|
||||||
|
|
||||||
if (pSample)
|
if (pSample)
|
||||||
{
|
{
|
||||||
IMFMediaBuffer* buffer;
|
//variables declaration
|
||||||
|
IMFMediaBuffer* buffer = nullptr, *mediaBuffer = nullptr;
|
||||||
|
IMFSample* sampleOut = nullptr;
|
||||||
|
|
||||||
hrStatus = pSample->ConvertToContiguousBuffer(&buffer);
|
if(_pixelformat == PixelFormat::MJPEG || _pixelformat == PixelFormat::NO_CHANGE)
|
||||||
if (SUCCEEDED(hrStatus))
|
|
||||||
{
|
{
|
||||||
BYTE* data = nullptr;
|
hrStatus = pSample->ConvertToContiguousBuffer(&buffer);
|
||||||
DWORD maxLength = 0, currentLength = 0;
|
if (SUCCEEDED(hrStatus))
|
||||||
|
|
||||||
hrStatus = buffer->Lock(&data, &maxLength, ¤tLength);
|
|
||||||
if(SUCCEEDED(hrStatus))
|
|
||||||
{
|
{
|
||||||
frameSend = true;
|
BYTE* data = nullptr;
|
||||||
_grabber->receive_image(data,currentLength,error);
|
DWORD maxLength = 0, currentLength = 0;
|
||||||
|
|
||||||
buffer->Unlock();
|
hrStatus = buffer->Lock(&data, &maxLength, ¤tLength);
|
||||||
|
if(SUCCEEDED(hrStatus))
|
||||||
|
{
|
||||||
|
_grabber->receive_image(data,currentLength);
|
||||||
|
buffer->Unlock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("buffer->Lock failed => %1").arg(hrStatus);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
error = QString("buffer->Lock failed => %1").arg(hrStatus);
|
error = QString("pSample->ConvertToContiguousBuffer failed => %1").arg(hrStatus);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
error = QString("pSample->ConvertToContiguousBuffer failed => %1").arg(hrStatus);
|
{
|
||||||
|
// Send our input sample to the transform
|
||||||
|
_transform->ProcessInput(0, pSample, 0);
|
||||||
|
|
||||||
|
MFT_OUTPUT_STREAM_INFO streamInfo;
|
||||||
|
hrStatus = _transform->GetOutputStreamInfo(0, &streamInfo);
|
||||||
|
if (SUCCEEDED(hrStatus))
|
||||||
|
{
|
||||||
|
hrStatus = MFCreateMemoryBuffer(streamInfo.cbSize, &buffer);
|
||||||
|
if (SUCCEEDED(hrStatus))
|
||||||
|
{
|
||||||
|
hrStatus = MFCreateSample(&sampleOut);
|
||||||
|
if (SUCCEEDED(hrStatus))
|
||||||
|
{
|
||||||
|
hrStatus = sampleOut->AddBuffer(buffer);
|
||||||
|
if (SUCCEEDED(hrStatus))
|
||||||
|
{
|
||||||
|
MFT_OUTPUT_DATA_BUFFER outputDataBuffer = {0};
|
||||||
|
memset(&outputDataBuffer, 0, sizeof outputDataBuffer);
|
||||||
|
outputDataBuffer.dwStreamID = 0;
|
||||||
|
outputDataBuffer.dwStatus = 0;
|
||||||
|
outputDataBuffer.pEvents = nullptr;
|
||||||
|
outputDataBuffer.pSample = sampleOut;
|
||||||
|
|
||||||
|
DWORD status = 0;
|
||||||
|
hrStatus = _transform->ProcessOutput(0, 1, &outputDataBuffer, &status);
|
||||||
|
if (hrStatus == MF_E_TRANSFORM_NEED_MORE_INPUT)
|
||||||
|
{
|
||||||
|
SAFE_RELEASE(sampleOut);
|
||||||
|
SAFE_RELEASE(buffer);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
hrStatus = sampleOut->ConvertToContiguousBuffer(&mediaBuffer);
|
||||||
|
if (SUCCEEDED(hrStatus))
|
||||||
|
{
|
||||||
|
BYTE* data = nullptr;
|
||||||
|
DWORD currentLength = 0;
|
||||||
|
|
||||||
|
hrStatus = mediaBuffer->Lock(&data, 0, ¤tLength);
|
||||||
|
if(SUCCEEDED(hrStatus))
|
||||||
|
{
|
||||||
|
_grabber->receive_image(data, currentLength);
|
||||||
|
mediaBuffer->Unlock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("mediaBuffer->Lock failed => %1").arg(hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("sampleOut->ConvertToContiguousBuffer failed => %1").arg(hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("AddBuffer failed %1").arg(hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("MFCreateSample failed %1").arg(hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("MFCreateMemoryBuffer failed %1").arg(hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("GetOutputStreamInfo failed %1").arg(hrStatus);
|
||||||
|
}
|
||||||
|
|
||||||
SAFE_RELEASE(buffer);
|
SAFE_RELEASE(buffer);
|
||||||
|
SAFE_RELEASE(mediaBuffer);
|
||||||
|
SAFE_RELEASE(sampleOut);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
error = "pSample is NULL";
|
error = "pSample is NULL";
|
||||||
|
|
||||||
if (!frameSend)
|
if (!error.isEmpty())
|
||||||
_grabber->receive_image(NULL,0,error);
|
Error(_grabber->_log, "%s", QSTRING_CSTR(error));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -111,12 +225,108 @@ public:
|
|||||||
// Reached the end of the stream.
|
// Reached the end of the stream.
|
||||||
_bEOS = TRUE;
|
_bEOS = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hrStatus = hrStatus;
|
_hrStatus = hrStatus;
|
||||||
|
|
||||||
LeaveCriticalSection(&_critsec);
|
LeaveCriticalSection(&_critsec);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT SourceReaderCB::InitializeVideoEncoder(IMFMediaType* type, PixelFormat format)
|
||||||
|
{
|
||||||
|
_pixelformat = format;
|
||||||
|
if (format == PixelFormat::MJPEG || format == PixelFormat::NO_CHANGE)
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
// Variable declaration
|
||||||
|
IMFMediaType *output = nullptr;
|
||||||
|
DWORD mftStatus = 0;
|
||||||
|
QString error = "";
|
||||||
|
|
||||||
|
// Create instance of IMFTransform interface pointer as CColorConvertDMO
|
||||||
|
if (SUCCEEDED(CoCreateInstance(CLSID_CColorConvertDMO, nullptr, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&_transform)))
|
||||||
|
{
|
||||||
|
// Set input type as media type of our input stream
|
||||||
|
_hrStatus = _transform->SetInputType(0, type, 0);
|
||||||
|
if (SUCCEEDED(_hrStatus))
|
||||||
|
{
|
||||||
|
// Create new media type
|
||||||
|
_hrStatus = MFCreateMediaType(&output);
|
||||||
|
if (SUCCEEDED(_hrStatus))
|
||||||
|
{
|
||||||
|
// Copy data from input type to output type
|
||||||
|
_hrStatus = type->CopyAllItems(output);
|
||||||
|
if (SUCCEEDED(_hrStatus))
|
||||||
|
{
|
||||||
|
UINT32 width, height;
|
||||||
|
UINT32 numerator, denominator;
|
||||||
|
|
||||||
|
// Fill the missing attributes
|
||||||
|
output->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
|
||||||
|
output->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB24);
|
||||||
|
output->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE);
|
||||||
|
output->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
|
||||||
|
output->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
|
||||||
|
MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height);
|
||||||
|
MFSetAttributeSize(output, MF_MT_FRAME_SIZE, width, height);
|
||||||
|
MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, &denominator);
|
||||||
|
MFSetAttributeRatio(output, MF_MT_FRAME_RATE, numerator, denominator);
|
||||||
|
MFSetAttributeRatio(output, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
|
||||||
|
|
||||||
|
if (SUCCEEDED(_hrStatus))
|
||||||
|
{
|
||||||
|
// Set transform output type
|
||||||
|
_hrStatus = _transform->SetOutputType(0, output, 0);
|
||||||
|
if (SUCCEEDED(_hrStatus))
|
||||||
|
{
|
||||||
|
// Check if encoder parameters set properly
|
||||||
|
_hrStatus = _transform->GetInputStatus(0, &mftStatus);
|
||||||
|
if (SUCCEEDED(_hrStatus))
|
||||||
|
{
|
||||||
|
if (MFT_INPUT_STATUS_ACCEPT_DATA == mftStatus)
|
||||||
|
{
|
||||||
|
// Notify the transform we are about to begin streaming data
|
||||||
|
if (FAILED(_transform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0)) ||
|
||||||
|
FAILED(_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0)) ||
|
||||||
|
FAILED(_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0)))
|
||||||
|
{
|
||||||
|
error = QString("ProcessMessage failed %1").arg(_hrStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_hrStatus = S_FALSE;
|
||||||
|
error = QString("GetInputStatus failed %1").arg(_hrStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("GetInputStatus failed %1").arg(_hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("SetOutputType failed %1").arg(_hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("Can not set output attributes %1").arg(_hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("CopyAllItems failed %1").arg(_hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("MFCreateMediaType failed %1").arg(_hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("SetInputType failed %1").arg(_hrStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = QString("CoCreateInstance failed %1").arg(_hrStatus);
|
||||||
|
|
||||||
|
if (!error.isEmpty())
|
||||||
|
Error(_grabber->_log, "%s", QSTRING_CSTR(error));
|
||||||
|
|
||||||
|
SAFE_RELEASE(output);
|
||||||
|
return _hrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) { return S_OK; }
|
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) { return S_OK; }
|
||||||
STDMETHODIMP OnFlush(DWORD) { return S_OK; }
|
STDMETHODIMP OnFlush(DWORD) { return S_OK; }
|
||||||
|
|
||||||
@ -125,9 +335,11 @@ private:
|
|||||||
void NotifyError(HRESULT hr) { Error(_grabber->_log, "Source Reader error"); }
|
void NotifyError(HRESULT hr) { Error(_grabber->_log, "Source Reader error"); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
long _nRefCount;
|
long _nRefCount;
|
||||||
CRITICAL_SECTION _critsec;
|
CRITICAL_SECTION _critsec;
|
||||||
MFGrabber* _grabber;
|
MFGrabber* _grabber;
|
||||||
BOOL _bEOS;
|
BOOL _bEOS;
|
||||||
HRESULT _hrStatus;
|
HRESULT _hrStatus;
|
||||||
|
IMFTransform* _transform;
|
||||||
|
PixelFormat _pixelformat;
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "grabber/MFThread.h"
|
#include "grabber/MFThread.h"
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
volatile bool MFThread::_isActive = false;
|
volatile bool MFThread::_isActive = false;
|
||||||
|
|
||||||
@ -52,7 +51,7 @@ void MFThread::setup(
|
|||||||
_imageResampler.setCropping(cropLeft, cropRight, cropTop, cropBottom);
|
_imageResampler.setCropping(cropLeft, cropRight, cropTop, cropBottom);
|
||||||
_imageResampler.setHorizontalPixelDecimation(_pixelDecimation);
|
_imageResampler.setHorizontalPixelDecimation(_pixelDecimation);
|
||||||
_imageResampler.setVerticalPixelDecimation(_pixelDecimation);
|
_imageResampler.setVerticalPixelDecimation(_pixelDecimation);
|
||||||
_imageResampler.setFlipMode(FlipMode::HORIZONTAL);
|
_imageResampler.setFlipMode(FlipMode::NO_CHANGE);
|
||||||
|
|
||||||
if (size > _localDataSize)
|
if (size > _localDataSize)
|
||||||
{
|
{
|
||||||
@ -79,7 +78,7 @@ void MFThread::run()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Image<ColorRgb> image = Image<ColorRgb>();
|
Image<ColorRgb> image = Image<ColorRgb>();
|
||||||
_imageResampler.processImage(_localData, _width, _height, _lineLength, _pixelFormat, image);
|
_imageResampler.processImage(_localData, _width, _height, _lineLength, PixelFormat::BGR24, image);
|
||||||
emit newFrame(_workerIndex, image, _currentFrame);
|
emit newFrame(_workerIndex, image, _currentFrame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,8 +155,8 @@ void MFThread::processImageMjpeg()
|
|||||||
if ( !(_cropLeft > 0 || _cropTop > 0 || _cropBottom > 0 || _cropRight > 0))
|
if ( !(_cropLeft > 0 || _cropTop > 0 || _cropBottom > 0 || _cropRight > 0))
|
||||||
emit newFrame(_workerIndex, srcImage, _currentFrame);
|
emit newFrame(_workerIndex, srcImage, _currentFrame);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// calculate the output size
|
// calculate the output size
|
||||||
int outputWidth = (_width - _cropLeft - _cropRight);
|
int outputWidth = (_width - _cropLeft - _cropRight);
|
||||||
int outputHeight = (_height - _cropTop - _cropBottom);
|
int outputHeight = (_height - _cropTop - _cropBottom);
|
||||||
|
|
||||||
|
@ -57,8 +57,8 @@ void ImageResampler::processImage(const uint8_t * data, int width, int height, i
|
|||||||
}
|
}
|
||||||
else if (pixelFormat == PixelFormat::I420)
|
else if (pixelFormat == PixelFormat::I420)
|
||||||
{
|
{
|
||||||
uOffset = height * lineLength + ((ySource * lineLength) / 4);
|
uOffset = (lineLength * (5 * height + ySource) / 4);
|
||||||
vOffset = ((5 * height * lineLength) * 4) + ((ySource * lineLength) / 4);
|
vOffset = (lineLength * (4 * height + ySource)) * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int xDest = 0, xSource = _cropLeft + (_horizontalDecimation >> 1); xDest < outputWidth; xSource += _horizontalDecimation, ++xDest)
|
for (int xDest = 0, xSource = _cropLeft + (_horizontalDecimation >> 1); xDest < outputWidth; xSource += _horizontalDecimation, ++xDest)
|
||||||
@ -150,8 +150,8 @@ void ImageResampler::processImage(const uint8_t * data, int width, int height, i
|
|||||||
{
|
{
|
||||||
int index = yOffset + xSource;
|
int index = yOffset + xSource;
|
||||||
uint8_t y = data[index];
|
uint8_t y = data[index];
|
||||||
uint8_t u = data[uOffset + (xSource >> 1)];
|
uint8_t u = data[uOffset + xSource];
|
||||||
uint8_t v = data[vOffset + (xSource >> 1)];
|
uint8_t v = data[vOffset + xSource];
|
||||||
ColorSys::yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
|
ColorSys::yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -87,11 +87,32 @@ void SysTray::createTrayIcon()
|
|||||||
_trayIconEfxMenu = new QMenu(_trayIconMenu);
|
_trayIconEfxMenu = new QMenu(_trayIconMenu);
|
||||||
_trayIconEfxMenu->setTitle(tr("Effects"));
|
_trayIconEfxMenu->setTitle(tr("Effects"));
|
||||||
_trayIconEfxMenu->setIcon(QPixmap(":/effects.svg"));
|
_trayIconEfxMenu->setIcon(QPixmap(":/effects.svg"));
|
||||||
|
|
||||||
|
// custom effects
|
||||||
for (auto efx : efxs)
|
for (auto efx : efxs)
|
||||||
{
|
{
|
||||||
QAction *efxAction = new QAction(efx.name, this);
|
if (efx.file.mid(0, 1) != ":")
|
||||||
connect(efxAction, SIGNAL(triggered()), this, SLOT(setEffect()));
|
{
|
||||||
_trayIconEfxMenu->addAction(efxAction);
|
qDebug() << efx.file;
|
||||||
|
QAction *efxAction = new QAction(efx.name, this);
|
||||||
|
connect(efxAction, SIGNAL(triggered()), this, SLOT(setEffect()));
|
||||||
|
_trayIconEfxMenu->addAction(efxAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add seperator if custom effects exists
|
||||||
|
if (!_trayIconEfxMenu->isEmpty())
|
||||||
|
_trayIconEfxMenu->addSeparator();
|
||||||
|
|
||||||
|
// build in effects
|
||||||
|
for (auto efx : efxs)
|
||||||
|
{
|
||||||
|
if (efx.file.mid(0, 1) == ":")
|
||||||
|
{
|
||||||
|
QAction *efxAction = new QAction(efx.name, this);
|
||||||
|
connect(efxAction, SIGNAL(triggered()), this, SLOT(setEffect()));
|
||||||
|
_trayIconEfxMenu->addAction(efxAction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
Loading…
Reference in New Issue
Block a user