mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
MF Discovery extended
This commit is contained in:
parent
8d8ee8b3cd
commit
5f0ad931ba
@ -31,13 +31,12 @@ body{
|
||||
}
|
||||
.navbar-brand{
|
||||
position:fixed;
|
||||
padding: 0px;
|
||||
height: 80px;
|
||||
}
|
||||
#main-nav{
|
||||
position:absolute !important;
|
||||
}
|
||||
.navbar-top-links li a {
|
||||
.navbar-top-links li a > .fa{
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
@ -104,7 +103,7 @@ table.first_cell_borderless td:first-child,table.first_cell_borderless th:first-
|
||||
table.first_cell_borderless td:first-child{width: 25px !important;}
|
||||
|
||||
/*Header*/
|
||||
.navbar-brand{padding-top:0px;padding-left:2;}
|
||||
.navbar-brand{top:0px;left:17px;padding-top:0;}
|
||||
.sidebar{margin-top:85px;}
|
||||
.dropdown{font-size:18px;}
|
||||
@media (max-width: 767px) {.sidebar{margin-top:0px;padding-top:0px !important;}}
|
||||
@ -239,6 +238,7 @@ table.first_cell_borderless td:first-child{width: 25px !important;}
|
||||
.btn-transparent {
|
||||
background-color: transparent;
|
||||
border-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/*general instance management button*/
|
||||
|
@ -170,7 +170,7 @@
|
||||
<li id="btn_setlang">
|
||||
<a>
|
||||
<div>
|
||||
<i class="fa fa-globe"></i>
|
||||
<i class="fa fa-globe fa-fw"></i>
|
||||
<select id="language-select" class="selectpicker" data-width="fit" data-style="btn-transparent"> </select>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -49,6 +49,16 @@ public:
|
||||
GUID guid = GUID_NULL;
|
||||
};
|
||||
|
||||
struct DeviceControls
|
||||
{
|
||||
QString property = QString();
|
||||
int minValue = 0;
|
||||
int maxValue = 0;
|
||||
int step = 0;
|
||||
int default = 0;
|
||||
int currentValue = 0;
|
||||
};
|
||||
|
||||
MFGrabber();
|
||||
~MFGrabber() override;
|
||||
|
||||
@ -91,6 +101,7 @@ private:
|
||||
QString _currentDeviceName,
|
||||
_newDeviceName;
|
||||
QMap<QString, QList<DeviceProperties>> _deviceProperties;
|
||||
QMap<QString, QList<DeviceControls>> _deviceControls;
|
||||
HRESULT _hr;
|
||||
IMFSourceReader* _sourceReader;
|
||||
SourceReaderCB* _sourceReaderCB;
|
||||
|
@ -2,7 +2,24 @@
|
||||
#include "grabber/MFGrabber.h"
|
||||
|
||||
// Constants
|
||||
namespace { const bool verbose = false; }
|
||||
namespace { const bool verbose = true; }
|
||||
|
||||
// Need more video properties? Visit https://docs.microsoft.com/en-us/windows/win32/api/strmif/ne-strmif-videoprocampproperty
|
||||
using VideoProcAmpPropertyMap = QMap<VideoProcAmpProperty, QString>;
|
||||
inline QMap<VideoProcAmpProperty, QString> initVideoProcAmpPropertyMap()
|
||||
{
|
||||
QMap<VideoProcAmpProperty, QString> _videoProcAmpPropertyMap
|
||||
{
|
||||
{VideoProcAmp_Brightness, "brightness" },
|
||||
{VideoProcAmp_Contrast , "contrast" },
|
||||
{VideoProcAmp_Saturation, "saturation" },
|
||||
{VideoProcAmp_Hue , "hue" }
|
||||
};
|
||||
|
||||
return _videoProcAmpPropertyMap;
|
||||
};
|
||||
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(VideoProcAmpPropertyMap, _videoProcAmpPropertyMap, (initVideoProcAmpPropertyMap()));
|
||||
|
||||
MFGrabber::MFGrabber()
|
||||
: Grabber("V4L2:MEDIA_FOUNDATION")
|
||||
@ -102,9 +119,10 @@ void MFGrabber::stop()
|
||||
_threadManager->stop();
|
||||
disconnect(_threadManager, nullptr, nullptr, nullptr);
|
||||
_sourceReader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM);
|
||||
while (_sourceReaderCB->isBusy()) {}
|
||||
_sourceReaderCB->Wait();
|
||||
SAFE_RELEASE(_sourceReader);
|
||||
_deviceProperties.clear();
|
||||
_deviceControls.clear();
|
||||
Info(_log, "Stopped");
|
||||
}
|
||||
}
|
||||
@ -171,7 +189,6 @@ HRESULT MFGrabber::init_device(QString deviceName, DeviceProperties props)
|
||||
QString error;
|
||||
IMFMediaSource* device = nullptr;
|
||||
IMFAttributes* deviceAttributes = nullptr, *sourceReaderAttributes = nullptr;
|
||||
IAMVideoProcAmp *pProcAmp = nullptr;
|
||||
IMFMediaType* type = nullptr;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
@ -213,86 +230,43 @@ HRESULT MFGrabber::init_device(QString deviceName, DeviceProperties props)
|
||||
else
|
||||
Debug(_log, "Device opened");
|
||||
|
||||
// Set Brightness/Contrast/Saturation/Hue
|
||||
if (_brightness != 0 || _contrast != 0 || _saturation != 0 || _hue != 0)
|
||||
{
|
||||
IAMVideoProcAmp *pProcAmp = nullptr;
|
||||
if (SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&pProcAmp))))
|
||||
{
|
||||
long lMin, lMax, lStep, lDefault, lCaps, Val;
|
||||
if (_brightness != 0)
|
||||
for (auto control : _deviceControls[deviceName])
|
||||
{
|
||||
if (SUCCEEDED(pProcAmp->GetRange(VideoProcAmp_Brightness, &lMin, &lMax, &lStep, &lDefault, &lCaps)))
|
||||
switch (_videoProcAmpPropertyMap->key(control.property))
|
||||
{
|
||||
Debug(_log, "Brightness: min=%i, max=%i, default=%i", lMin, lMax, lDefault);
|
||||
|
||||
if (SUCCEEDED(pProcAmp->Get(VideoProcAmp_Brightness, &Val, &lCaps)))
|
||||
Debug(_log, "Current brightness set to: %i",Val);
|
||||
|
||||
if (SUCCEEDED(pProcAmp->Set(VideoProcAmp_Brightness, _brightness, VideoProcAmp_Flags_Manual)))
|
||||
Debug(_log, "Brightness set to: %i",_brightness);
|
||||
else
|
||||
Error(_log, "Could not set brightness");
|
||||
case VideoProcAmpProperty::VideoProcAmp_Brightness:
|
||||
if (_brightness >= control.minValue && _brightness <= control.maxValue && _brightness != control.currentValue)
|
||||
{
|
||||
Debug(_log,"Set brightness to %i", _brightness);
|
||||
pProcAmp->Set(VideoProcAmp_Brightness, _brightness, VideoProcAmp_Flags_Manual);
|
||||
}
|
||||
else
|
||||
Error(_log, "Brightness is not supported by the grabber");
|
||||
}
|
||||
|
||||
if (_contrast != 0)
|
||||
break;
|
||||
case VideoProcAmpProperty::VideoProcAmp_Contrast:
|
||||
if (_contrast >= control.minValue && _contrast <= control.maxValue && _contrast != control.currentValue)
|
||||
{
|
||||
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");
|
||||
Debug(_log,"Set contrast to %i", _contrast);
|
||||
pProcAmp->Set(VideoProcAmp_Contrast, _contrast, VideoProcAmp_Flags_Manual);
|
||||
}
|
||||
else
|
||||
Error(_log, "Contrast is not supported by the grabber");
|
||||
}
|
||||
|
||||
if (_saturation != 0)
|
||||
break;
|
||||
case VideoProcAmpProperty::VideoProcAmp_Saturation:
|
||||
if (_saturation >= control.minValue && _saturation <= control.maxValue && _saturation != control.currentValue)
|
||||
{
|
||||
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");
|
||||
Debug(_log,"Set saturation to %i", _saturation);
|
||||
pProcAmp->Set(VideoProcAmp_Saturation, _saturation, VideoProcAmp_Flags_Manual);
|
||||
}
|
||||
else
|
||||
Error(_log, "Saturation is not supported by the grabber");
|
||||
}
|
||||
|
||||
if (_hue != 0)
|
||||
break;
|
||||
case VideoProcAmpProperty::VideoProcAmp_Hue:
|
||||
if (_hue >= control.minValue && _hue <= control.maxValue && _hue != control.currentValue)
|
||||
{
|
||||
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");
|
||||
Debug(_log,"Set hue to %i", _hue);
|
||||
pProcAmp->Set(VideoProcAmp_Hue, _hue, VideoProcAmp_Flags_Manual);
|
||||
}
|
||||
else
|
||||
Error(_log, "Hue is not supported by the grabber");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -402,6 +376,7 @@ done:
|
||||
void MFGrabber::enumVideoCaptureDevices()
|
||||
{
|
||||
_deviceProperties.clear();
|
||||
_deviceControls.clear();
|
||||
|
||||
IMFAttributes* attr;
|
||||
if (SUCCEEDED(MFCreateAttributes(&attr, 1)))
|
||||
@ -465,11 +440,51 @@ void MFGrabber::enumVideoCaptureDevices()
|
||||
}
|
||||
}
|
||||
|
||||
pType->Release();
|
||||
SAFE_RELEASE(pType);
|
||||
}
|
||||
reader->Release();
|
||||
|
||||
IAMVideoProcAmp *videoProcAmp = nullptr;
|
||||
if (SUCCEEDED(pSource->QueryInterface(IID_PPV_ARGS(&videoProcAmp))))
|
||||
{
|
||||
QList<DeviceControls> deviceControlList;
|
||||
for (auto it = _videoProcAmpPropertyMap->begin(); it != _videoProcAmpPropertyMap->end(); it++)
|
||||
{
|
||||
long minVal, maxVal, stepVal, defaultVal, flag;
|
||||
if (SUCCEEDED(videoProcAmp->GetRange(it.key(), &minVal, &maxVal, &stepVal, &defaultVal, &flag)))
|
||||
{
|
||||
if (flag & VideoProcAmp_Flags_Manual)
|
||||
{
|
||||
DeviceControls control;
|
||||
control.property = it.value();
|
||||
control.minValue = minVal;
|
||||
control.maxValue = maxVal;
|
||||
control.step = stepVal;
|
||||
control.default = defaultVal;
|
||||
|
||||
long currentVal;
|
||||
if (SUCCEEDED(videoProcAmp->Get(it.key(), ¤tVal, &flag)))
|
||||
{
|
||||
control.currentValue = currentVal;
|
||||
DebugIf(verbose, _log, "%s: min=%i, max=%i, step=%i, default=%i, current=%i", QSTRING_CSTR(it.value()), minVal, maxVal, stepVal, defaultVal, currentVal);
|
||||
}
|
||||
pSource->Release();
|
||||
else
|
||||
break;
|
||||
|
||||
deviceControlList.append(control);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!deviceControlList.isEmpty())
|
||||
_deviceControls.insert(dev, deviceControlList);
|
||||
}
|
||||
|
||||
SAFE_RELEASE(videoProcAmp);
|
||||
SAFE_RELEASE(reader);
|
||||
}
|
||||
|
||||
SAFE_RELEASE(pSource);
|
||||
}
|
||||
|
||||
if (!devicePropertyList.isEmpty())
|
||||
@ -480,13 +495,13 @@ void MFGrabber::enumVideoCaptureDevices()
|
||||
}
|
||||
|
||||
CoTaskMemFree(name);
|
||||
devices[i]->Release();
|
||||
SAFE_RELEASE(devices[i]);
|
||||
}
|
||||
|
||||
CoTaskMemFree(devices);
|
||||
}
|
||||
|
||||
attr->Release();
|
||||
SAFE_RELEASE(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -628,9 +643,6 @@ void MFGrabber::setBrightnessContrastSaturationHue(int brightness, int contrast,
|
||||
{
|
||||
if (_brightness != brightness || _contrast != contrast || _saturation != saturation || _hue != hue)
|
||||
{
|
||||
if (_initialized)
|
||||
Debug(_log,"Set brightness to %i, contrast to %i, saturation to %i, hue to %i", _brightness, _contrast, _saturation, _hue);
|
||||
|
||||
_brightness = brightness;
|
||||
_contrast = contrast;
|
||||
_saturation = saturation;
|
||||
@ -677,14 +689,18 @@ void MFGrabber::setSignalDetectionEnable(bool enable)
|
||||
|
||||
bool MFGrabber::reload(bool force)
|
||||
{
|
||||
if (_sourceReader && (_reload || force))
|
||||
if (_reload || force)
|
||||
{
|
||||
if (_sourceReader)
|
||||
{
|
||||
Info(_log,"Reloading Media Foundation Grabber");
|
||||
uninit();
|
||||
_pixelFormat = _pixelFormatConfig;
|
||||
_newDeviceName = _currentDeviceName;
|
||||
}
|
||||
|
||||
_reload = false;
|
||||
return start();
|
||||
return prepare() && start();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -754,12 +770,39 @@ QJsonArray MFGrabber::discover(const QJsonObject& params)
|
||||
}
|
||||
in["formats"] = formats;
|
||||
video_inputs.append(in);
|
||||
|
||||
device["video_inputs"] = video_inputs;
|
||||
|
||||
QJsonObject controls, controls_default;
|
||||
for (auto control : _deviceControls[it.key()])
|
||||
{
|
||||
QJsonObject property;
|
||||
property["minValue"] = control.minValue;
|
||||
property["maxValue"] = control.maxValue;
|
||||
property["step"] = control.step;
|
||||
controls[control.property] = property;
|
||||
controls_default[control.property] = control.default;
|
||||
}
|
||||
device["properties"] = controls;
|
||||
|
||||
QJsonObject default, video_inputs_default, format_default, resolution_default;
|
||||
resolution_default["width"] = 640;
|
||||
resolution_default["height"] = 480;
|
||||
resolution_default["fps"] = 25;
|
||||
format_default["format"] = "bgr24";
|
||||
format_default["resolution"] = resolution_default;
|
||||
video_inputs_default["inputIdx"] = 0;
|
||||
video_inputs_default["standards"] = "PAL";
|
||||
video_inputs_default["formats"] = format_default;
|
||||
|
||||
default["video_input"] = video_inputs_default;
|
||||
default["properties"] = controls_default;
|
||||
device["default"] = default;
|
||||
|
||||
inputsDiscovered.append(device);
|
||||
}
|
||||
|
||||
_deviceProperties.clear();
|
||||
_deviceControls.clear();
|
||||
DebugIf (verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return inputsDiscovered;
|
||||
|
@ -42,14 +42,15 @@ public:
|
||||
SourceReaderCB(MFGrabber* grabber)
|
||||
: _nRefCount(1)
|
||||
, _grabber(grabber)
|
||||
, _bEOS(FALSE)
|
||||
, _hrStatus(S_OK)
|
||||
, _isBusy(false)
|
||||
, _transform(nullptr)
|
||||
, _pixelformat(PixelFormat::NO_CHANGE)
|
||||
{
|
||||
// Initialize critical section.
|
||||
InitializeCriticalSection(&_critsec);
|
||||
|
||||
// Create event handler
|
||||
_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
}
|
||||
|
||||
// IUnknown methods
|
||||
@ -83,12 +84,11 @@ public:
|
||||
DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample* pSample)
|
||||
{
|
||||
EnterCriticalSection(&_critsec);
|
||||
_isBusy = true;
|
||||
|
||||
if (_grabber->_sourceReader == nullptr)
|
||||
{
|
||||
_isBusy = false;
|
||||
LeaveCriticalSection(&_critsec);
|
||||
SetEvent(_event);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -172,14 +172,11 @@ public:
|
||||
done:
|
||||
SAFE_RELEASE(buffer);
|
||||
|
||||
if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags)
|
||||
_bEOS = TRUE; // Reached the end of the stream.
|
||||
|
||||
if (_pixelformat != PixelFormat::MJPEG && _pixelformat != PixelFormat::BGR24 && _pixelformat != PixelFormat::NO_CHANGE)
|
||||
SAFE_RELEASE(pSample);
|
||||
|
||||
_isBusy = false;
|
||||
LeaveCriticalSection(&_critsec);
|
||||
SetEvent(_event);
|
||||
return _hrStatus;
|
||||
}
|
||||
|
||||
@ -283,15 +280,7 @@ public:
|
||||
return _hrStatus;
|
||||
}
|
||||
|
||||
BOOL SourceReaderCB::isBusy()
|
||||
{
|
||||
EnterCriticalSection(&_critsec);
|
||||
BOOL result = _isBusy;
|
||||
LeaveCriticalSection(&_critsec);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Wait() { WaitForSingleObject(_event, INFINITE); }
|
||||
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent*) { return S_OK; }
|
||||
STDMETHODIMP OnFlush(DWORD) { return S_OK; }
|
||||
|
||||
@ -397,5 +386,5 @@ private:
|
||||
HRESULT _hrStatus;
|
||||
IMFTransform* _transform;
|
||||
PixelFormat _pixelformat;
|
||||
std::atomic<bool> _isBusy;
|
||||
HANDLE _event;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user