Refactor MediaFoundation (Part 1)

This commit is contained in:
Paulchen Panther 2021-01-22 21:29:47 +01:00
parent 4a79d3f143
commit d5717af2df
7 changed files with 566 additions and 379 deletions

View File

@ -1,5 +1,8 @@
#pragma once #pragma once
// Windows include
#include <Windows.h>
// COM includes // COM includes
#include <Guiddef.h> #include <Guiddef.h>
@ -56,7 +59,7 @@ public:
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,24 +91,17 @@ 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;

View File

@ -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()

View File

@ -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,63 +153,67 @@ 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;
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;
IMFAttributes* attr;
hr = MFCreateAttributes(&attr, 2);
if (SUCCEEDED(hr))
{
hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if (SUCCEEDED(hr))
{
int size = guid.length() + 1024; int size = guid.length() + 1024;
wchar_t *name = new wchar_t[size]; wchar_t *name = new wchar_t[size];
memset(name, 0, size); memset(name, 0, size);
guid.toWCharArray(name); guid.toWCharArray(name);
IMFMediaSource* device = nullptr;
IMFAttributes* deviceAttributes = nullptr, *sourceReaderAttributes = nullptr;
IAMVideoProcAmp *pProcAmp = nullptr;
IMFMediaType* type = nullptr;
HRESULT hr = S_OK;
if (SUCCEEDED(attr->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, (LPCWSTR)name)) && _sourceReaderCB) 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));
hr = MFCreateAttributes(&deviceAttributes, 2);
if(FAILED(hr))
{ {
hr = MFCreateDeviceSource(attr, &device); error = QString("Could not create device attributes (%1)").arg(hr);
if (FAILED(hr)) goto done;
{
SAFE_RELEASE(device);;
error = QString("MFCreateDeviceSource %1").arg(hr);
} }
hr = deviceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if(FAILED(hr))
{
error = QString("SetGUID_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE (%1)").arg(hr);
goto done;
}
if(FAILED(deviceAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, (LPCWSTR)name)) && _sourceReaderCB)
{
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
error = QString("IMFAttributes_SetString_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK %1").arg(hr);
delete[] name;
}
SAFE_RELEASE(attr);
}
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");
// Set Brightness/Contrast/Saturation/Hue
if (_brightness != 0 || _contrast != 0 || _saturation != 0 || _hue != 0) if (_brightness != 0 || _contrast != 0 || _saturation != 0 || _hue != 0)
{ {
IAMVideoProcAmp *pProcAmp = NULL;
if (SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&pProcAmp)))) if (SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&pProcAmp))))
{ {
long lMin, lMax, lStep, lDefault, lCaps, Val; long lMin, lMax, lStep, lDefault, lCaps, Val;
@ -301,84 +292,90 @@ bool MFGrabber::init_device(QString deviceName, DevicePropertiesItem props)
else else
Error(_log, "Hue is not supported by the grabber"); Error(_log, "Hue is not supported by the grabber");
} }
pProcAmp->Release();
} }
} }
IMFAttributes* pAttributes; hr = MFCreateAttributes(&sourceReaderAttributes, 1);
hr1 = MFCreateAttributes(&pAttributes, 1); if(FAILED(hr))
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; 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); hr = MFCreateMediaType(&type);
if (SUCCEEDED(hr)) 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); hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
if (SUCCEEDED(hr)) 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); hr = type->SetGUID(MF_MT_SUBTYPE, props.guid);
if (SUCCEEDED(hr)) 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); hr = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, props.x, props.y);
if (SUCCEEDED(hr)) 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); hr = MFSetAttributeSize(type, MF_MT_FRAME_RATE, props.fps_a, props.fps_b);
if (SUCCEEDED(hr)) if(FAILED(hr))
{ {
MFSetAttributeRatio(type, MF_MT_PIXEL_ASPECT_RATIO, 1, 1); error = QString("Could not set stream parameter: MFSetAttributeSize_MF_MT_FRAME_RATE (%1)").arg(hr);
goto done;
}
hr = _sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, type); hr = MFSetAttributeRatio(type, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
if (SUCCEEDED(hr)) if(FAILED(hr))
{ {
setStreamParamOK = true; error = QString("Could not set stream parameter: MFSetAttributeRatio_MF_MT_PIXEL_ASPECT_RATIO (%1)").arg(hr);
goto done;
} }
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
error = QString("SetGUID_MF_MT_MAJOR_TYPE %1").arg(hr);
type->Release(); hr = _sourceReaderCB->InitializeVideoEncoder(type, pixelformat);
} if (FAILED(hr))
else
error = QString("IMFAttributes_SetString %1").arg(hr);
if (!setStreamParamOK)
Error(_log, "Could not stream set params (%s)", QSTRING_CSTR(error));
}
else
Error(_log, "MFCreateSourceReaderFromMediaSource (%i)", hr);
}
else
Error(_log, "Could not open device (%s)", QSTRING_CSTR(error));
if (!setStreamParamOK)
{ {
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;
switch (_pixelFormat)
{
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; _frameByteSize = props.x * props.y * 3;
_lineLength = props.x * 3; _lineLength = props.x * 3;
} }
break;
case PixelFormat::RGB32: // Cleanup
{ SAFE_RELEASE(deviceAttributes);
_frameByteSize = props.x * props.y * 4; delete[] name;
_lineLength = props.x * 4; SAFE_RELEASE(device);
} SAFE_RELEASE(pProcAmp);
break; SAFE_RELEASE(type);
SAFE_RELEASE(sourceReaderAttributes);
case PixelFormat::NV12: return hr;
{
_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;
} }
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)
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); 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();

View File

@ -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,20 +74,53 @@ 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;
if(_pixelformat == PixelFormat::MJPEG || _pixelformat == PixelFormat::NO_CHANGE)
{
hrStatus = pSample->ConvertToContiguousBuffer(&buffer); hrStatus = pSample->ConvertToContiguousBuffer(&buffer);
if (SUCCEEDED(hrStatus)) if (SUCCEEDED(hrStatus))
{ {
@ -80,25 +130,89 @@ public:
hrStatus = buffer->Lock(&data, &maxLength, &currentLength); hrStatus = buffer->Lock(&data, &maxLength, &currentLength);
if(SUCCEEDED(hrStatus)) if(SUCCEEDED(hrStatus))
{ {
frameSend = true; _grabber->receive_image(data,currentLength);
_grabber->receive_image(data,currentLength,error);
buffer->Unlock(); buffer->Unlock();
} }
else else
error = QString("buffer->Lock failed => %1").arg(hrStatus); error = QString("buffer->Lock failed => %1").arg(hrStatus);
} }
else else
error = QString("pSample->ConvertToContiguousBuffer failed => %1").arg(hrStatus); error = QString("pSample->ConvertToContiguousBuffer failed => %1").arg(hrStatus);
}
else
{
// 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, &currentLength);
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; }
@ -130,4 +340,6 @@ private:
MFGrabber* _grabber; MFGrabber* _grabber;
BOOL _bEOS; BOOL _bEOS;
HRESULT _hrStatus; HRESULT _hrStatus;
IMFTransform* _transform;
PixelFormat _pixelformat;
}; };

View File

@ -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);
} }
} }

View File

@ -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;
} }

View File

@ -87,12 +87,33 @@ 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)
{
if (efx.file.mid(0, 1) != ":")
{
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); QAction *efxAction = new QAction(efx.name, this);
connect(efxAction, SIGNAL(triggered()), this, SLOT(setEffect())); connect(efxAction, SIGNAL(triggered()), this, SLOT(setEffect()));
_trayIconEfxMenu->addAction(efxAction); _trayIconEfxMenu->addAction(efxAction);
} }
}
#ifdef _WIN32 #ifdef _WIN32
autorunAction = new QAction(tr("&Disable autostart"), this); autorunAction = new QAction(tr("&Disable autostart"), this);