2020-12-18 17:38:21 +01:00
# include "MFSourceReaderCB.h"
# include "grabber/MFGrabber.h"
MFGrabber : : MFGrabber ( const QString & device , unsigned width , unsigned height , unsigned fps , unsigned input , int pixelDecimation )
: Grabber ( " V4L2: " + device )
, _deviceName ( device )
, _hr ( S_FALSE )
, _sourceReader ( nullptr )
, _pixelDecimation ( pixelDecimation )
, _lineLength ( - 1 )
, _frameByteSize ( - 1 )
, _noSignalCounterThreshold ( 40 )
, _noSignalCounter ( 0 )
, _fpsSoftwareDecimation ( 1 )
, _brightness ( 0 )
, _contrast ( 0 )
, _saturation ( 0 )
, _hue ( 0 )
, _currentFrame ( 0 )
, _noSignalThresholdColor ( ColorRgb { 0 , 0 , 0 } )
, _signalDetectionEnabled ( true )
, _cecDetectionEnabled ( true )
, _noSignalDetected ( false )
, _initialized ( false )
, _x_frac_min ( 0.25 )
, _y_frac_min ( 0.25 )
, _x_frac_max ( 0.75 )
, _y_frac_max ( 0.75 )
{
setInput ( input ) ;
setWidthHeight ( width , height ) ;
setFramerate ( fps ) ;
2021-01-03 13:30:37 +01:00
// setDeviceVideoStandard(device, videoStandard); // TODO
2020-12-18 17:38:21 +01:00
CoInitializeEx ( 0 , COINIT_MULTITHREADED ) ;
_hr = MFStartup ( MF_VERSION , MFSTARTUP_NOSOCKET ) ;
2021-01-22 21:29:47 +01:00
if ( FAILED ( _hr ) )
2020-12-18 17:38:21 +01:00
CoUninitialize ( ) ;
else
_sourceReaderCB = new SourceReaderCB ( this ) ;
}
MFGrabber : : ~ MFGrabber ( )
{
uninit ( ) ;
SAFE_RELEASE ( _sourceReader ) ;
SAFE_RELEASE ( _sourceReaderCB ) ;
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( _hr ) & & SUCCEEDED ( MFShutdown ( ) ) )
2020-12-18 17:38:21 +01:00
CoUninitialize ( ) ;
}
bool MFGrabber : : init ( )
{
2021-01-22 21:29:47 +01:00
if ( ! _initialized & & SUCCEEDED ( _hr ) )
2020-12-18 17:38:21 +01:00
{
QString foundDevice = " " ;
int foundIndex = - 1 , bestGuess = - 1 , bestGuessMinX = INT_MAX , bestGuessMinFPS = INT_MAX ;
bool autoDiscovery = ( QString : : compare ( _deviceName , " auto " , Qt : : CaseInsensitive ) = = 0 ) ;
// enumerate the video capture devices on the user's system
enumVideoCaptureDevices ( ) ;
2021-01-22 21:29:47 +01:00
if ( ! autoDiscovery & & ! _deviceProperties . contains ( _deviceName ) )
2020-12-18 17:38:21 +01:00
{
Debug ( _log , " Device '%s' is not available. Changing to auto. " , QSTRING_CSTR ( _deviceName ) ) ;
autoDiscovery = true ;
}
2021-01-22 21:29:47 +01:00
if ( autoDiscovery )
2020-12-18 17:38:21 +01:00
{
Debug ( _log , " Forcing auto discovery device " ) ;
2021-01-22 21:29:47 +01:00
if ( _deviceProperties . count ( ) > 0 )
2020-12-18 17:38:21 +01:00
{
foundDevice = _deviceProperties . firstKey ( ) ;
_deviceName = foundDevice ;
Debug ( _log , " Auto discovery set to %s " , QSTRING_CSTR ( _deviceName ) ) ;
}
}
else
foundDevice = _deviceName ;
2021-01-22 21:29:47 +01:00
if ( foundDevice . isNull ( ) | | foundDevice . isEmpty ( ) | | ! _deviceProperties . contains ( foundDevice ) )
2020-12-18 17:38:21 +01:00
{
Error ( _log , " Could not find any capture device " ) ;
return false ;
}
MFGrabber : : DeviceProperties dev = _deviceProperties [ foundDevice ] ;
Debug ( _log , " Searching for %s %d x %d @ %d fps (%s) " , QSTRING_CSTR ( foundDevice ) , _width , _height , _fps , QSTRING_CSTR ( pixelFormatToString ( _pixelFormat ) ) ) ;
for ( int i = 0 ; i < dev . valid . count ( ) & & foundIndex < 0 ; + + i )
{
bool strict = false ;
const auto & val = dev . valid [ i ] ;
2021-01-22 21:29:47 +01:00
if ( bestGuess = = - 1 | | ( val . x < = bestGuessMinX & & val . x > = 640 & & val . fps < = bestGuessMinFPS & & val . fps > = 10 ) )
2020-12-18 17:38:21 +01:00
{
bestGuess = i ;
bestGuessMinFPS = val . fps ;
bestGuessMinX = val . x ;
}
if ( _width & & _height )
{
strict = true ;
2021-01-22 21:29:47 +01:00
if ( val . x ! = _width | | val . y ! = _height )
2020-12-18 17:38:21 +01:00
continue ;
}
if ( _fps & & _fps ! = 15 )
{
strict = true ;
2021-01-22 21:29:47 +01:00
if ( val . fps ! = _fps )
2020-12-18 17:38:21 +01:00
continue ;
}
if ( _pixelFormat ! = PixelFormat : : NO_CHANGE )
{
strict = true ;
2021-01-22 21:29:47 +01:00
if ( val . pf ! = _pixelFormat )
2020-12-18 17:38:21 +01:00
continue ;
}
2021-01-22 21:29:47 +01:00
if ( strict & & ( val . fps < = 60 | | _fps ! = 15 ) )
2020-12-18 17:38:21 +01:00
foundIndex = i ;
}
2021-01-22 21:29:47 +01:00
if ( foundIndex < 0 & & bestGuess > = 0 )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
if ( ! autoDiscovery )
2020-12-18 17:38:21 +01:00
Warning ( _log , " Selected resolution not found in supported modes. Forcing best resolution " ) ;
else
Debug ( _log , " Forcing best resolution " ) ;
foundIndex = bestGuess ;
}
2021-01-22 21:29:47 +01:00
if ( foundIndex > = 0 )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( init_device ( foundDevice , dev . valid [ foundIndex ] ) ) )
2020-12-18 17:38:21 +01:00
_initialized = true ;
}
else
Error ( _log , " Could not find any capture device settings " ) ;
}
return _initialized ;
}
void MFGrabber : : uninit ( )
{
// stop if the grabber was not stopped
2021-01-22 21:29:47 +01:00
if ( _initialized )
2020-12-18 17:38:21 +01:00
{
Debug ( _log , " uninit grabber: %s " , QSTRING_CSTR ( _deviceName ) ) ;
stop ( ) ;
}
}
2021-01-22 21:29:47 +01:00
HRESULT MFGrabber : : init_device ( QString deviceName , DevicePropertiesItem props )
2020-12-18 17:38:21 +01:00
{
PixelFormat pixelformat = GetPixelFormatForGuid ( props . guid ) ;
QString error , guid = _deviceProperties [ deviceName ] . name ;
2021-01-22 21:29:47 +01:00
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 ;
2020-12-18 17:38:21 +01:00
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 ) ) ;
2021-01-22 21:29:47 +01:00
hr = MFCreateAttributes ( & deviceAttributes , 2 ) ;
if ( FAILED ( hr ) )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
error = QString ( " Could not create device attributes (%1) " ) . arg ( hr ) ;
goto done ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
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 ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
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 ;
2020-12-18 17:38:21 +01:00
}
2021-01-22 21:29:47 +01:00
hr = MFCreateDeviceSource ( deviceAttributes , & device ) ;
if ( FAILED ( hr ) )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
error = QString ( " MFCreateDeviceSource (%1) " ) . arg ( hr ) ;
goto done ;
2020-12-18 17:38:21 +01:00
}
2021-01-22 21:29:47 +01:00
if ( ! device )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
error = QString ( " Could not open device (%1) " ) . arg ( hr ) ;
goto done ;
}
else
2020-12-18 17:38:21 +01:00
Debug ( _log , " Device opened " ) ;
2021-01-22 21:29:47 +01:00
// Set Brightness/Contrast/Saturation/Hue
if ( _brightness ! = 0 | | _contrast ! = 0 | | _saturation ! = 0 | | _hue ! = 0 )
{
if ( SUCCEEDED ( device - > QueryInterface ( IID_PPV_ARGS ( & pProcAmp ) ) ) )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
long lMin , lMax , lStep , lDefault , lCaps , Val ;
if ( _brightness ! = 0 )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( pProcAmp - > GetRange ( VideoProcAmp_Brightness , & lMin , & lMax , & lStep , & lDefault , & lCaps ) ) )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
Debug ( _log , " Brightness: min=%i, max=%i, default=%i " , lMin , lMax , lDefault ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( pProcAmp - > Get ( VideoProcAmp_Brightness , & Val , & lCaps ) ) )
Debug ( _log , " Current brightness set to: %i " , Val ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( pProcAmp - > Set ( VideoProcAmp_Brightness , _brightness , VideoProcAmp_Flags_Manual ) ) )
Debug ( _log , " Brightness set to: %i " , _brightness ) ;
2020-12-18 17:38:21 +01:00
else
2021-01-22 21:29:47 +01:00
Error ( _log , " Could not set brightness " ) ;
2020-12-18 17:38:21 +01:00
}
2021-01-22 21:29:47 +01:00
else
Error ( _log , " Brightness is not supported by the grabber " ) ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( _contrast ! = 0 )
{
if ( SUCCEEDED ( pProcAmp - > GetRange ( VideoProcAmp_Contrast , & lMin , & lMax , & lStep , & lDefault , & lCaps ) ) )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
Debug ( _log , " Contrast: min=%i, max=%i, default=%i " , lMin , lMax , lDefault ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( pProcAmp - > Get ( VideoProcAmp_Contrast , & Val , & lCaps ) ) )
Debug ( _log , " Current contrast set to: %i " , Val ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( pProcAmp - > Set ( VideoProcAmp_Contrast , _contrast , VideoProcAmp_Flags_Manual ) ) )
Debug ( _log , " Contrast set to: %i " , _contrast ) ;
2020-12-18 17:38:21 +01:00
else
2021-01-22 21:29:47 +01:00
Error ( _log , " Could not set contrast " ) ;
2020-12-18 17:38:21 +01:00
}
2021-01-22 21:29:47 +01:00
else
Error ( _log , " Contrast is not supported by the grabber " ) ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( _saturation ! = 0 )
{
if ( SUCCEEDED ( pProcAmp - > GetRange ( VideoProcAmp_Saturation , & lMin , & lMax , & lStep , & lDefault , & lCaps ) ) )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
Debug ( _log , " Saturation: min=%i, max=%i, default=%i " , lMin , lMax , lDefault ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( pProcAmp - > Get ( VideoProcAmp_Saturation , & Val , & lCaps ) ) )
Debug ( _log , " Current saturation set to: %i " , Val ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( pProcAmp - > Set ( VideoProcAmp_Saturation , _saturation , VideoProcAmp_Flags_Manual ) ) )
Debug ( _log , " Saturation set to: %i " , _saturation ) ;
2020-12-18 17:38:21 +01:00
else
2021-01-22 21:29:47 +01:00
Error ( _log , " Could not set saturation " ) ;
2020-12-18 17:38:21 +01:00
}
2021-01-22 21:29:47 +01:00
else
Error ( _log , " Saturation is not supported by the grabber " ) ;
}
if ( _hue ! = 0 )
{
hr = pProcAmp - > GetRange ( VideoProcAmp_Hue , & lMin , & lMax , & lStep , & lDefault , & lCaps ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( SUCCEEDED ( hr ) )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
Debug ( _log , " Hue: min=%i, max=%i, default=%i " , lMin , lMax , lDefault ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
hr = pProcAmp - > Get ( VideoProcAmp_Hue , & Val , & lCaps ) ;
2020-12-18 17:38:21 +01:00
if ( SUCCEEDED ( hr ) )
2021-01-22 21:29:47 +01:00
Debug ( _log , " Current hue set to: %i " , Val ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
hr = pProcAmp - > Set ( VideoProcAmp_Hue , _hue , VideoProcAmp_Flags_Manual ) ;
if ( SUCCEEDED ( hr ) )
Debug ( _log , " Hue set to: %i " , _hue ) ;
2020-12-18 17:38:21 +01:00
else
2021-01-22 21:29:47 +01:00
Error ( _log , " Could not set hue " ) ;
2020-12-18 17:38:21 +01:00
}
2021-01-22 21:29:47 +01:00
else
Error ( _log , " Hue is not supported by the grabber " ) ;
2020-12-18 17:38:21 +01:00
}
}
2021-01-22 21:29:47 +01:00
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
hr = MFCreateAttributes ( & sourceReaderAttributes , 1 ) ;
if ( FAILED ( hr ) )
{
error = QString ( " Could not create Source Reader attributes (%1) " ) . arg ( hr ) ;
goto done ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
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 ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
hr = MFCreateSourceReaderFromMediaSource ( device , sourceReaderAttributes , & _sourceReader ) ;
if ( FAILED ( hr ) )
{
error = QString ( " Could not create the Source Reader (%1) " ) . arg ( hr ) ;
goto done ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
hr = MFCreateMediaType ( & type ) ;
if ( FAILED ( hr ) )
{
error = QString ( " Could not create an empty media type (%1) " ) . arg ( hr ) ;
goto done ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
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 ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
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 ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
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 ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
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 ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
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 ;
}
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
hr = _sourceReaderCB - > InitializeVideoEncoder ( type , pixelformat ) ;
if ( FAILED ( hr ) )
{
error = QString ( " Failed to initialize the Video Encoder (%1) " ) . arg ( hr ) ;
goto done ;
2020-12-18 17:38:21 +01:00
}
2021-01-22 21:29:47 +01:00
hr = _sourceReader - > SetCurrentMediaType ( MF_SOURCE_READER_FIRST_VIDEO_STREAM , nullptr , type ) ;
if ( FAILED ( hr ) )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
error = QString ( " Failed to set media type on Source Reader (%1) " ) . arg ( hr ) ;
}
done :
if ( FAILED ( hr ) )
{
Error ( _log , " %s " , QSTRING_CSTR ( error ) ) ;
2020-12-18 17:38:21 +01:00
SAFE_RELEASE ( _sourceReader ) ;
}
else
{
_pixelFormat = props . pf ;
_width = props . x ;
_height = props . y ;
2021-01-22 21:29:47 +01:00
_frameByteSize = props . x * props . y * 3 ;
_lineLength = props . x * 3 ;
2020-12-18 17:38:21 +01:00
}
2021-01-22 21:29:47 +01:00
// Cleanup
SAFE_RELEASE ( deviceAttributes ) ;
delete [ ] name ;
SAFE_RELEASE ( device ) ;
SAFE_RELEASE ( pProcAmp ) ;
SAFE_RELEASE ( type ) ;
SAFE_RELEASE ( sourceReaderAttributes ) ;
return hr ;
2020-12-18 17:38:21 +01:00
}
void MFGrabber : : uninit_device ( )
{
SAFE_RELEASE ( _sourceReader ) ;
}
void MFGrabber : : enumVideoCaptureDevices ( )
{
2021-01-22 21:29:47 +01:00
if ( FAILED ( _hr ) )
2020-12-18 17:38:21 +01:00
{
Error ( _log , " enumVideoCaptureDevices(): Media Foundation not initialized " ) ;
return ;
}
_deviceProperties . clear ( ) ;
IMFAttributes * attr ;
if ( SUCCEEDED ( MFCreateAttributes ( & attr , 1 ) ) )
{
if ( SUCCEEDED ( attr - > SetGUID ( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE , MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID ) ) )
{
UINT32 count ;
IMFActivate * * devices ;
if ( SUCCEEDED ( MFEnumDeviceSources ( attr , & devices , & count ) ) )
{
Debug ( _log , " Detected devices: %u " , count ) ;
2021-01-22 21:29:47 +01:00
for ( UINT32 i = 0 ; i < count ; i + + )
2020-12-18 17:38:21 +01:00
{
UINT32 length ;
LPWSTR name ;
LPWSTR symlink ;
if ( SUCCEEDED ( devices [ i ] - > GetAllocatedString ( MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME , & name , & length ) ) )
{
if ( SUCCEEDED ( devices [ i ] - > GetAllocatedString ( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK , & symlink , & length ) ) )
{
QString dev = QString : : fromUtf16 ( ( const ushort * ) name ) ;
MFGrabber : : DeviceProperties properties ;
properties . name = QString : : fromUtf16 ( ( const ushort * ) symlink ) ;
Info ( _log , " Found capture device: %s " , QSTRING_CSTR ( dev ) ) ;
IMFMediaSource * pSource = nullptr ;
if ( SUCCEEDED ( devices [ i ] - > ActivateObject ( IID_PPV_ARGS ( & pSource ) ) ) )
{
IMFMediaType * pType = nullptr ;
IMFSourceReader * reader ;
if ( SUCCEEDED ( MFCreateSourceReaderFromMediaSource ( pSource , NULL , & reader ) ) )
{
for ( DWORD i = 0 ; ; i + + )
{
if ( FAILED ( reader - > GetNativeMediaType ( ( DWORD ) MF_SOURCE_READER_FIRST_VIDEO_STREAM , i , & pType ) ) )
break ;
GUID format ;
UINT64 frame_size ;
UINT64 frame_rate ;
if ( SUCCEEDED ( pType - > GetGUID ( MF_MT_SUBTYPE , & format ) ) & &
SUCCEEDED ( pType - > GetUINT64 ( MF_MT_FRAME_SIZE , & frame_size ) ) & &
SUCCEEDED ( pType - > GetUINT64 ( MF_MT_FRAME_RATE , & frame_rate ) ) & &
frame_rate > 0 )
{
PixelFormat pixelformat = GetPixelFormatForGuid ( format ) ;
DWORD w = frame_size > > 32 ;
DWORD h = ( DWORD ) frame_size ;
DWORD fr1 = frame_rate > > 32 ;
DWORD fr2 = ( DWORD ) frame_rate ;
if ( pixelformat ! = PixelFormat : : NO_CHANGE )
{
int framerate = fr1 / fr2 ;
QString sFrame = QString : : number ( framerate ) . rightJustified ( 2 , ' ' ) ;
QString displayResolutions = QString : : number ( w ) . rightJustified ( 4 , ' ' ) + " x " + QString : : number ( h ) . rightJustified ( 4 , ' ' ) ;
if ( ! properties . displayResolutions . contains ( displayResolutions ) )
properties . displayResolutions < < displayResolutions ;
if ( ! properties . framerates . contains ( sFrame ) )
properties . framerates < < sFrame ;
DevicePropertiesItem di ;
di . x = w ;
di . y = h ;
di . fps = framerate ;
di . fps_a = fr1 ;
di . fps_b = fr2 ;
di . pf = pixelformat ;
di . guid = format ;
properties . valid . append ( di ) ;
2021-01-03 13:30:37 +01:00
Debug ( _log , " %s %d x %d @ %d fps (%s) " , QSTRING_CSTR ( dev ) , di . x , di . y , di . fps , QSTRING_CSTR ( pixelFormatToString ( di . pf ) ) ) ;
2020-12-18 17:38:21 +01:00
}
}
pType - > Release ( ) ;
}
reader - > Release ( ) ;
}
pSource - > Release ( ) ;
}
2021-01-22 21:29:47 +01:00
2020-12-18 17:38:21 +01:00
properties . displayResolutions . sort ( ) ;
properties . framerates . sort ( ) ;
_deviceProperties . insert ( dev , properties ) ;
}
2021-01-22 21:29:47 +01:00
2020-12-18 17:38:21 +01:00
CoTaskMemFree ( symlink ) ;
}
2021-01-22 21:29:47 +01:00
2020-12-18 17:38:21 +01:00
CoTaskMemFree ( name ) ;
devices [ i ] - > Release ( ) ;
}
CoTaskMemFree ( devices ) ;
}
2021-01-22 21:29:47 +01:00
2020-12-18 17:38:21 +01:00
attr - > Release ( ) ;
}
}
}
void MFGrabber : : start_capturing ( )
{
if ( _sourceReader )
{
2021-01-22 21:29:47 +01:00
HRESULT hr = _sourceReader - > ReadSample ( MF_SOURCE_READER_FIRST_VIDEO_STREAM , 0 , NULL , NULL , NULL , NULL ) ;
2020-12-18 17:38:21 +01:00
if ( ! SUCCEEDED ( hr ) )
Error ( _log , " ReadSample (%i) " , hr ) ;
}
}
2021-01-22 21:29:47 +01:00
void MFGrabber : : process_image ( const void * frameImageBuffer , int size )
2020-12-18 17:38:21 +01:00
{
unsigned int processFrameIndex = _currentFrame + + ;
// frame skipping
2021-01-22 21:29:47 +01:00
if ( ( processFrameIndex % _fpsSoftwareDecimation ! = 0 ) & & ( _fpsSoftwareDecimation > 1 ) )
return ;
2020-12-18 17:38:21 +01:00
// CEC detection
2021-01-22 21:29:47 +01:00
if ( _cecDetectionEnabled )
return ;
2020-12-18 17:38:21 +01:00
// We do want a new frame...
if ( size < _frameByteSize & & _pixelFormat ! = PixelFormat : : MJPEG )
Error ( _log , " Frame too small: %d != %d " , size , _frameByteSize ) ;
else
{
if ( _threadManager . isActive ( ) )
{
if ( _threadManager . _threads = = nullptr )
{
_threadManager . initThreads ( ) ;
Debug ( _log , " Max thread count = %d " , _threadManager . _maxThreads ) ;
for ( unsigned int i = 0 ; i < _threadManager . _maxThreads & & _threadManager . _threads ! = nullptr ; i + + )
{
MFThread * _thread = _threadManager . _threads [ i ] ;
connect ( _thread , SIGNAL ( newFrame ( unsigned int , const Image < ColorRgb > & , unsigned int ) ) , this , SLOT ( newThreadFrame ( unsigned int , const Image < ColorRgb > & , unsigned int ) ) ) ;
}
}
for ( unsigned int i = 0 ; _threadManager . isActive ( ) & & i < _threadManager . _maxThreads & & _threadManager . _threads ! = nullptr ; i + + )
{
if ( ( _threadManager . _threads [ i ] - > isFinished ( ) | | ! _threadManager . _threads [ i ] - > isRunning ( ) ) )
2021-01-22 21:29:47 +01:00
{
2020-12-18 17:38:21 +01:00
// aquire lock
if ( _threadManager . _threads [ i ] - > isBusy ( ) = = false )
{
MFThread * _thread = _threadManager . _threads [ i ] ;
_thread - > setup ( i , _pixelFormat , ( uint8_t * ) frameImageBuffer , size , _width , _height , _lineLength , _subsamp , _cropLeft , _cropTop , _cropBottom , _cropRight , _videoMode , processFrameIndex , _pixelDecimation ) ;
if ( _threadManager . _maxThreads > 1 )
_threadManager . _threads [ i ] - > start ( ) ;
break ;
}
2021-01-22 21:29:47 +01:00
}
2020-12-18 17:38:21 +01:00
}
}
}
}
void MFGrabber : : setSignalThreshold ( double redSignalThreshold , double greenSignalThreshold , double blueSignalThreshold , int noSignalCounterThreshold )
{
_noSignalThresholdColor . red = uint8_t ( 255 * redSignalThreshold ) ;
_noSignalThresholdColor . green = uint8_t ( 255 * greenSignalThreshold ) ;
_noSignalThresholdColor . blue = uint8_t ( 255 * blueSignalThreshold ) ;
_noSignalCounterThreshold = qMax ( 1 , noSignalCounterThreshold ) ;
Info ( _log , " Signal threshold set to: {%d, %d, %d} and frames: %d " , _noSignalThresholdColor . red , _noSignalThresholdColor . green , _noSignalThresholdColor . blue , _noSignalCounterThreshold ) ;
}
void MFGrabber : : setSignalDetectionOffset ( double horizontalMin , double verticalMin , double horizontalMax , double verticalMax )
{
// rainbow 16 stripes 0.47 0.2 0.49 0.8
// unicolor: 0.25 0.25 0.75 0.75
_x_frac_min = horizontalMin ;
_y_frac_min = verticalMin ;
_x_frac_max = horizontalMax ;
_y_frac_max = verticalMax ;
Info ( _log , " Signal detection area set to: %f,%f x %f,%f " , _x_frac_min , _y_frac_min , _x_frac_max , _y_frac_max ) ;
}
bool MFGrabber : : start ( )
{
try
{
_threadManager . start ( ) ;
2021-01-22 21:29:47 +01:00
Info ( _log , " Decoding threads: %d " , _threadManager . _maxThreads ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
if ( init ( ) )
2020-12-18 17:38:21 +01:00
{
start_capturing ( ) ;
Info ( _log , " Started " ) ;
return true ;
}
}
catch ( std : : exception & e )
{
Error ( _log , " Start failed (%s) " , e . what ( ) ) ;
}
return false ;
}
void MFGrabber : : stop ( )
{
2021-01-22 21:29:47 +01:00
if ( _initialized )
2020-12-18 17:38:21 +01:00
{
_threadManager . stop ( ) ;
uninit_device ( ) ;
_deviceProperties . clear ( ) ;
_initialized = false ;
Info ( _log , " Stopped " ) ;
}
}
2021-01-22 21:29:47 +01:00
void MFGrabber : : receive_image ( const void * frameImageBuffer , int size )
2020-12-18 17:38:21 +01:00
{
2021-01-22 21:29:47 +01:00
process_image ( frameImageBuffer , size ) ;
2020-12-18 17:38:21 +01:00
start_capturing ( ) ;
}
void MFGrabber : : newThreadFrame ( unsigned int threadIndex , const Image < ColorRgb > & image , unsigned int sourceCount )
{
checkSignalDetectionEnabled ( image ) ;
// get next frame
if ( threadIndex > _threadManager . _maxThreads )
Error ( _log , " Frame index %d out of range " , sourceCount ) ;
if ( threadIndex < = _threadManager . _maxThreads )
_threadManager . _threads [ threadIndex ] - > noBusy ( ) ;
}
void MFGrabber : : checkSignalDetectionEnabled ( Image < ColorRgb > image )
{
2021-01-22 21:29:47 +01:00
if ( _signalDetectionEnabled )
2020-12-18 17:38:21 +01:00
{
// check signal (only in center of the resulting image, because some grabbers have noise values along the borders)
bool noSignal = true ;
// top left
unsigned xOffset = image . width ( ) * _x_frac_min ;
unsigned yOffset = image . height ( ) * _y_frac_min ;
// bottom right
unsigned xMax = image . width ( ) * _x_frac_max ;
unsigned yMax = image . height ( ) * _y_frac_max ;
2021-01-22 21:29:47 +01:00
for ( unsigned x = xOffset ; noSignal & & x < xMax ; + + x )
for ( unsigned y = yOffset ; noSignal & & y < yMax ; + + y )
2020-12-18 17:38:21 +01:00
noSignal & = ( ColorRgb & ) image ( x , y ) < = _noSignalThresholdColor ;
2021-01-22 21:29:47 +01:00
if ( noSignal )
2020-12-18 17:38:21 +01:00
+ + _noSignalCounter ;
else
{
2021-01-22 21:29:47 +01:00
if ( _noSignalCounter > = _noSignalCounterThreshold )
2020-12-18 17:38:21 +01:00
{
_noSignalDetected = true ;
Info ( _log , " Signal detected " ) ;
}
_noSignalCounter = 0 ;
}
2021-01-22 21:29:47 +01:00
if ( _noSignalCounter < _noSignalCounterThreshold )
2020-12-18 17:38:21 +01:00
{
emit newFrame ( image ) ;
}
2021-01-22 21:29:47 +01:00
else if ( _noSignalCounter = = _noSignalCounterThreshold )
2020-12-18 17:38:21 +01:00
{
_noSignalDetected = false ;
Info ( _log , " Signal lost " ) ;
}
}
else
emit newFrame ( image ) ;
}
QStringList MFGrabber : : getV4L2devices ( ) const
{
QStringList result = QStringList ( ) ;
2021-01-22 21:29:47 +01:00
for ( auto it = _deviceProperties . begin ( ) ; it ! = _deviceProperties . end ( ) ; + + it )
2020-12-18 17:38:21 +01:00
result < < it . key ( ) ;
return result ;
}
QStringList MFGrabber : : getV4L2EncodingFormats ( const QString & devicePath ) const
{
QStringList result = QStringList ( ) ;
for ( int i = 0 ; i < _deviceProperties [ devicePath ] . valid . count ( ) ; + + i )
2021-01-22 21:29:47 +01:00
if ( ! result . contains ( pixelFormatToString ( _deviceProperties [ devicePath ] . valid [ i ] . pf ) , Qt : : CaseInsensitive ) )
2020-12-18 17:38:21 +01:00
result < < pixelFormatToString ( _deviceProperties [ devicePath ] . valid [ i ] . pf ) . toLower ( ) ;
return result ;
}
void MFGrabber : : setSignalDetectionEnable ( bool enable )
{
2021-01-22 21:29:47 +01:00
if ( _signalDetectionEnabled ! = enable )
2020-12-18 17:38:21 +01:00
{
_signalDetectionEnabled = enable ;
Info ( _log , " Signal detection is now %s " , enable ? " enabled " : " disabled " ) ;
}
}
void MFGrabber : : setCecDetectionEnable ( bool enable )
{
2021-01-22 21:29:47 +01:00
if ( _cecDetectionEnabled ! = enable )
2020-12-18 17:38:21 +01:00
{
_cecDetectionEnabled = enable ;
Info ( _log , QString ( " CEC detection is now %1 " ) . arg ( enable ? " enabled " : " disabled " ) . toLocal8Bit ( ) ) ;
}
}
void MFGrabber : : setPixelDecimation ( int pixelDecimation )
{
2021-01-22 21:29:47 +01:00
if ( _pixelDecimation ! = pixelDecimation )
2020-12-18 17:38:21 +01:00
_pixelDecimation = pixelDecimation ;
}
void MFGrabber : : setDeviceVideoStandard ( QString device , VideoStandard videoStandard )
{
2021-01-22 21:29:47 +01:00
if ( _deviceName ! = device )
2020-12-18 17:38:21 +01:00
{
_deviceName = device ;
2021-01-22 21:29:47 +01:00
if ( _initialized & & ! device . isEmpty ( ) )
2020-12-18 17:38:21 +01:00
{
Debug ( _log , " Restarting Media Foundation grabber " ) ;
uninit ( ) ;
start ( ) ;
}
}
}
bool MFGrabber : : setInput ( int input )
{
if ( Grabber : : setInput ( input ) )
{
bool started = _initialized ;
uninit ( ) ;
if ( started )
start ( ) ;
return true ;
}
return false ;
}
bool MFGrabber : : setWidthHeight ( int width , int height )
{
if ( Grabber : : setWidthHeight ( width , height ) )
{
Debug ( _log , " Set width:height to: %i:&i " , width , height ) ;
2021-01-22 21:29:47 +01:00
if ( _initialized )
2020-12-18 17:38:21 +01:00
{
Debug ( _log , " Restarting Media Foundation grabber " ) ;
uninit ( ) ;
start ( ) ;
}
return true ;
}
return false ;
}
bool MFGrabber : : setFramerate ( int fps )
{
if ( Grabber : : setFramerate ( fps ) )
{
Debug ( _log , " Set fps to: %i " , fps ) ;
2021-01-22 21:29:47 +01:00
if ( _initialized )
2020-12-18 17:38:21 +01:00
{
Debug ( _log , " Restarting Media Foundation grabber " ) ;
uninit ( ) ;
start ( ) ;
}
return true ;
}
return false ;
}
void MFGrabber : : setFpsSoftwareDecimation ( int decimation )
{
_fpsSoftwareDecimation = decimation ;
2021-01-22 21:29:47 +01:00
if ( decimation > 1 )
2020-12-18 17:38:21 +01:00
Debug ( _log , " Every %ith image per second are processed " , decimation ) ;
}
void MFGrabber : : setEncoding ( QString enc )
{
2021-01-22 21:29:47 +01:00
if ( _pixelFormat ! = parsePixelFormat ( enc ) )
2020-12-18 17:38:21 +01:00
{
Debug ( _log , " Set encoding to: %s " , QSTRING_CSTR ( enc ) ) ;
_pixelFormat = parsePixelFormat ( enc ) ;
2021-01-22 21:29:47 +01:00
if ( _initialized )
2020-12-18 17:38:21 +01:00
{
Debug ( _log , " Restarting Media Foundation Grabber " ) ;
uninit ( ) ;
start ( ) ;
}
}
}
void MFGrabber : : setBrightnessContrastSaturationHue ( int brightness , int contrast , int saturation , int hue )
{
2021-01-22 21:29:47 +01:00
if ( _brightness ! = brightness | | _contrast ! = contrast | | _saturation ! = saturation | | _hue ! = hue )
2020-12-18 17:38:21 +01:00
{
_brightness = brightness ;
_contrast = contrast ;
_saturation = saturation ;
_hue = hue ;
Debug ( _log , " Set brightness to %i, contrast to %i, saturation to %i, hue to %i " , _brightness , _contrast , _saturation , _hue ) ;
2021-01-22 21:29:47 +01:00
if ( _initialized )
2020-12-18 17:38:21 +01:00
{
Debug ( _log , " Restarting Media Foundation Grabber " ) ;
uninit ( ) ;
start ( ) ;
}
}
}