2020-12-18 17:38:21 +01:00
# include "MFSourceReaderCB.h"
# include "grabber/MFGrabber.h"
2021-01-24 12:16:16 +01:00
// Constants
namespace { const bool verbose = false ; }
2021-04-04 12:43:29 +02:00
MFGrabber : : MFGrabber ( )
2021-01-29 21:08:47 +01:00
: Grabber ( " V4L2:MEDIA_FOUNDATION " )
2021-04-04 12:43:29 +02:00
, _currentDeviceName ( " none " )
, _newDeviceName ( " none " )
2020-12-18 17:38:21 +01:00
, _hr ( S_FALSE )
, _sourceReader ( nullptr )
2021-04-04 12:43:29 +02:00
, _sourceReaderCB ( nullptr )
, _threadManager ( nullptr )
, _pixelFormat ( PixelFormat : : NO_CHANGE )
, _pixelFormatConfig ( PixelFormat : : NO_CHANGE )
2020-12-18 17:38:21 +01:00
, _lineLength ( - 1 )
, _frameByteSize ( - 1 )
, _noSignalCounterThreshold ( 40 )
, _noSignalCounter ( 0 )
, _brightness ( 0 )
, _contrast ( 0 )
, _saturation ( 0 )
, _hue ( 0 )
, _currentFrame ( 0 )
, _noSignalThresholdColor ( ColorRgb { 0 , 0 , 0 } )
, _signalDetectionEnabled ( true )
, _noSignalDetected ( false )
, _initialized ( false )
2021-04-04 12:43:29 +02:00
, _reload ( false )
2020-12-18 17:38:21 +01:00
, _x_frac_min ( 0.25 )
, _y_frac_min ( 0.25 )
, _x_frac_max ( 0.75 )
, _y_frac_max ( 0.75 )
{
CoInitializeEx ( 0 , COINIT_MULTITHREADED ) ;
_hr = MFStartup ( MF_VERSION , MFSTARTUP_NOSOCKET ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( _hr ) )
2020-12-18 17:38:21 +01:00
CoUninitialize ( ) ;
}
MFGrabber : : ~ MFGrabber ( )
{
uninit ( ) ;
SAFE_RELEASE ( _sourceReader ) ;
SAFE_RELEASE ( _sourceReaderCB ) ;
2021-04-04 12:43:29 +02:00
if ( _threadManager )
delete _threadManager ;
_threadManager = nullptr ;
if ( SUCCEEDED ( _hr ) & & SUCCEEDED ( MFShutdown ( ) ) )
2020-12-18 17:38:21 +01:00
CoUninitialize ( ) ;
}
2021-04-04 12:43:29 +02:00
bool MFGrabber : : prepare ( )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( SUCCEEDED ( _hr ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( ! _sourceReaderCB )
_sourceReaderCB = new SourceReaderCB ( this ) ;
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
if ( ! _threadManager )
_threadManager = new MFThreadManager ( this ) ;
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
return ( _sourceReaderCB ! = nullptr & & _threadManager ! = nullptr ) ;
}
return false ;
}
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
bool MFGrabber : : start ( )
{
if ( ! _initialized )
{
if ( init ( ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
connect ( _threadManager , & MFThreadManager : : newFrame , this , & MFGrabber : : newThreadFrame ) ;
_threadManager - > start ( ) ;
DebugIf ( verbose , _log , " Decoding threads: %d " , _threadManager - > _threadCount ) ;
start_capturing ( ) ;
Info ( _log , " Started " ) ;
return true ;
2020-12-18 17:38:21 +01:00
}
else
{
2021-04-04 12:43:29 +02:00
Error ( _log , " The Media Foundation Grabber could not be started " ) ;
2020-12-18 17:38:21 +01:00
return false ;
}
2021-04-04 12:43:29 +02:00
}
else
return true ;
}
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
void MFGrabber : : stop ( )
{
if ( _initialized )
{
_initialized = false ;
_threadManager - > stop ( ) ;
disconnect ( _threadManager , nullptr , nullptr , nullptr ) ;
_sourceReader - > Flush ( MF_SOURCE_READER_FIRST_VIDEO_STREAM ) ;
while ( _sourceReaderCB - > isBusy ( ) ) { }
SAFE_RELEASE ( _sourceReader ) ;
_deviceProperties . clear ( ) ;
Info ( _log , " Stopped " ) ;
}
}
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
bool MFGrabber : : init ( )
{
// enumerate the video capture devices on the user's system
enumVideoCaptureDevices ( ) ;
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
if ( ! _initialized & & SUCCEEDED ( _hr ) )
{
int deviceIndex = - 1 ;
bool noDeviceName = _currentDeviceName . compare ( " none " , Qt : : CaseInsensitive ) = = 0 | | _currentDeviceName . compare ( " auto " , Qt : : CaseInsensitive ) = = 0 ;
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
if ( noDeviceName )
return false ;
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
if ( ! _deviceProperties . contains ( _currentDeviceName ) )
{
Debug ( _log , " Configured device '%s' is not available. " , QSTRING_CSTR ( _currentDeviceName ) ) ;
return false ;
2020-12-18 17:38:21 +01:00
}
2021-04-04 12:43:29 +02:00
Debug ( _log , " Searching for %s %d x %d @ %d fps (%s) " , QSTRING_CSTR ( _currentDeviceName ) , _width , _height , _fps , QSTRING_CSTR ( pixelFormatToString ( _pixelFormat ) ) ) ;
QList < DeviceProperties > dev = _deviceProperties [ _currentDeviceName ] ;
for ( int i = 0 ; i < dev . count ( ) & & deviceIndex < 0 ; + + i )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( dev [ i ] . width ! = _width | | dev [ i ] . height ! = _height | | dev [ i ] . fps ! = _fps | | dev [ i ] . pf ! = _pixelFormat )
continue ;
2020-12-18 17:38:21 +01:00
else
2021-04-04 12:43:29 +02:00
deviceIndex = i ;
2020-12-18 17:38:21 +01:00
}
2021-04-04 12:43:29 +02:00
if ( deviceIndex > = 0 & & SUCCEEDED ( init_device ( _currentDeviceName , dev [ deviceIndex ] ) ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
_initialized = true ;
_newDeviceName = _currentDeviceName ;
2020-12-18 17:38:21 +01:00
}
else
2021-04-04 12:43:29 +02:00
{
Debug ( _log , " Configured device '%s' is not available. " , QSTRING_CSTR ( _currentDeviceName ) ) ;
return false ;
}
2020-12-18 17:38:21 +01:00
}
return _initialized ;
}
void MFGrabber : : uninit ( )
{
// stop if the grabber was not stopped
2021-04-04 12:43:29 +02:00
if ( _initialized )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
Debug ( _log , " Uninit grabber: %s " , QSTRING_CSTR ( _newDeviceName ) ) ;
2020-12-18 17:38:21 +01:00
stop ( ) ;
}
}
2021-01-31 13:49:31 +01:00
HRESULT MFGrabber : : init_device ( QString deviceName , DeviceProperties props )
2020-12-18 17:38:21 +01:00
{
PixelFormat pixelformat = GetPixelFormatForGuid ( props . guid ) ;
2021-01-31 13:49:31 +01:00
QString error ;
2021-01-22 21:29:47 +01:00
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
2021-01-31 13:49:31 +01:00
Debug ( _log , " Init %s, %d x %d @ %d fps (%s) " , QSTRING_CSTR ( deviceName ) , props . width , props . height , props . fps , QSTRING_CSTR ( pixelFormatToString ( pixelformat ) ) ) ;
2021-04-04 12:43:29 +02:00
DebugIf ( verbose , _log , " Symbolic link: %s " , QSTRING_CSTR ( props . symlink ) ) ;
2020-12-18 17:38:21 +01:00
2021-01-22 21:29:47 +01:00
hr = MFCreateAttributes ( & deviceAttributes , 2 ) ;
2021-04-04 12:43:29 +02:00
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 ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
error = QString ( " SetGUID_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE (%1) " ) . arg ( hr ) ;
goto done ;
}
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
if ( FAILED ( deviceAttributes - > SetString ( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK , ( LPCWSTR ) props . symlink . utf16 ( ) ) ) & & _sourceReaderCB )
2021-01-22 21:29:47 +01:00
{
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 ) ;
2021-04-04 12:43:29 +02:00
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-04-04 12:43:29 +02: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 ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
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 ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
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 ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
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 ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
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 ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
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 ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
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-31 13:49:31 +01:00
hr = MFSetAttributeSize ( type , MF_MT_FRAME_SIZE , props . width , props . height ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
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-31 13:49:31 +01:00
hr = MFSetAttributeSize ( type , MF_MT_FRAME_RATE , props . numerator , props . denominator ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
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 ) ;
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
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 :
2021-04-04 12:43:29 +02:00
if ( FAILED ( hr ) )
2021-01-22 21:29:47 +01:00
{
2021-04-04 12:43:29 +02:00
emit readError ( QSTRING_CSTR ( error ) ) ;
2020-12-18 17:38:21 +01:00
SAFE_RELEASE ( _sourceReader ) ;
}
else
{
_pixelFormat = props . pf ;
2021-01-31 13:49:31 +01:00
_width = props . width ;
_height = props . height ;
_frameByteSize = _width * _height * 3 ;
_lineLength = _width * 3 ;
2020-12-18 17:38:21 +01:00
}
2021-01-22 21:29:47 +01:00
// Cleanup
SAFE_RELEASE ( deviceAttributes ) ;
SAFE_RELEASE ( device ) ;
SAFE_RELEASE ( pProcAmp ) ;
SAFE_RELEASE ( type ) ;
SAFE_RELEASE ( sourceReaderAttributes ) ;
return hr ;
2020-12-18 17:38:21 +01:00
}
void MFGrabber : : enumVideoCaptureDevices ( )
{
_deviceProperties . clear ( ) ;
IMFAttributes * attr ;
2021-04-04 12:43:29 +02:00
if ( SUCCEEDED ( MFCreateAttributes ( & attr , 1 ) ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( SUCCEEDED ( attr - > SetGUID ( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE , MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID ) ) )
2020-12-18 17:38:21 +01:00
{
UINT32 count ;
IMFActivate * * devices ;
2021-04-04 12:43:29 +02:00
if ( SUCCEEDED ( MFEnumDeviceSources ( attr , & devices , & count ) ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
DebugIf ( verbose , _log , " Detected devices: %u " , count ) ;
for ( UINT32 i = 0 ; i < count ; i + + )
2020-12-18 17:38:21 +01:00
{
UINT32 length ;
LPWSTR name ;
LPWSTR symlink ;
2021-04-04 12:43:29 +02:00
if ( SUCCEEDED ( devices [ i ] - > GetAllocatedString ( MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME , & name , & length ) ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( SUCCEEDED ( devices [ i ] - > GetAllocatedString ( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK , & symlink , & length ) ) )
2020-12-18 17:38:21 +01:00
{
2021-01-31 13:49:31 +01:00
QList < DeviceProperties > devicePropertyList ;
2020-12-18 17:38:21 +01:00
QString dev = QString : : fromUtf16 ( ( const ushort * ) name ) ;
2021-01-31 13:49:31 +01:00
2020-12-18 17:38:21 +01:00
IMFMediaSource * pSource = nullptr ;
2021-04-04 12:43:29 +02:00
if ( SUCCEEDED ( devices [ i ] - > ActivateObject ( IID_PPV_ARGS ( & pSource ) ) ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
DebugIf ( verbose , _log , " Found capture device: %s " , QSTRING_CSTR ( dev ) ) ;
2021-02-14 16:36:50 +01:00
2020-12-18 17:38:21 +01:00
IMFMediaType * pType = nullptr ;
IMFSourceReader * reader ;
2021-04-04 12:43:29 +02:00
if ( SUCCEEDED ( MFCreateSourceReaderFromMediaSource ( pSource , NULL , & reader ) ) )
2020-12-18 17:38:21 +01:00
{
for ( DWORD i = 0 ; ; i + + )
{
if ( FAILED ( reader - > GetNativeMediaType ( ( DWORD ) MF_SOURCE_READER_FIRST_VIDEO_STREAM , i , & pType ) ) )
break ;
GUID format ;
2021-01-31 13:49:31 +01:00
UINT32 width = 0 , height = 0 , numerator = 0 , denominator = 0 ;
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
if ( SUCCEEDED ( pType - > GetGUID ( MF_MT_SUBTYPE , & format ) ) & &
2021-01-31 13:49:31 +01:00
SUCCEEDED ( MFGetAttributeSize ( pType , MF_MT_FRAME_SIZE , & width , & height ) ) & &
SUCCEEDED ( MFGetAttributeRatio ( pType , MF_MT_FRAME_RATE , & numerator , & denominator ) ) )
2020-12-18 17:38:21 +01:00
{
PixelFormat pixelformat = GetPixelFormatForGuid ( format ) ;
if ( pixelformat ! = PixelFormat : : NO_CHANGE )
{
2021-01-31 13:49:31 +01:00
DeviceProperties properties ;
properties . symlink = QString : : fromUtf16 ( ( const ushort * ) symlink ) ;
properties . width = width ;
properties . height = height ;
properties . fps = numerator / denominator ;
properties . numerator = numerator ;
properties . denominator = denominator ;
properties . pf = pixelformat ;
properties . guid = format ;
devicePropertyList . append ( properties ) ;
2021-04-04 12:43:29 +02:00
DebugIf ( verbose , _log , " %s %d x %d @ %d fps (%s) " , QSTRING_CSTR ( dev ) , properties . width , properties . height , properties . fps , QSTRING_CSTR ( pixelFormatToString ( properties . pf ) ) ) ;
2020-12-18 17:38:21 +01:00
}
}
pType - > Release ( ) ;
}
reader - > Release ( ) ;
}
pSource - > Release ( ) ;
}
2021-01-22 21:29:47 +01:00
2021-02-14 16:36:50 +01:00
if ( ! devicePropertyList . isEmpty ( ) )
_deviceProperties . insert ( dev , devicePropertyList ) ;
2020-12-18 17:38:21 +01:00
}
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 ( )
{
2021-04-04 12:43:29 +02:00
if ( _initialized & & _sourceReader & & _threadManager )
2020-12-18 17:38:21 +01:00
{
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
{
2021-04-04 12:43:29 +02:00
int processFrameIndex = _currentFrame + + ;
2020-12-18 17:38:21 +01:00
// frame skipping
2021-04-04 12:43:29 +02:00
if ( ( processFrameIndex % ( _fpsSoftwareDecimation + 1 ) ! = 0 ) & & ( _fpsSoftwareDecimation > 0 ) )
2021-01-22 21:29:47 +01:00
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 ) ;
2021-04-04 12:43:29 +02:00
else if ( _threadManager ! = nullptr )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
for ( int i = 0 ; i < _threadManager - > _threadCount ; i + + )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( ! _threadManager - > _threads [ i ] - > isBusy ( ) )
2021-01-27 18:55:21 +01:00
{
2021-04-04 12:43:29 +02:00
_threadManager - > _threads [ i ] - > setup ( _pixelFormat , ( uint8_t * ) frameImageBuffer , size , _width , _height , _lineLength , _subsamp , _cropLeft , _cropTop , _cropBottom , _cropRight , _videoMode , _flipMode , _pixelDecimation ) ;
_threadManager - > _threads [ i ] - > process ( ) ;
break ;
2021-01-27 18:55:21 +01:00
}
2020-12-18 17:38:21 +01:00
}
}
}
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 ( ) ;
}
2021-04-04 12:43:29 +02:00
void MFGrabber : : newThreadFrame ( Image < ColorRgb > image )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02: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-04-04 12:43:29 +02: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-04-04 12:43:29 +02:00
if ( noSignal )
2020-12-18 17:38:21 +01:00
+ + _noSignalCounter ;
else
{
2021-04-04 12:43:29 +02:00
if ( _noSignalCounter > = _noSignalCounterThreshold )
2020-12-18 17:38:21 +01:00
{
_noSignalDetected = true ;
Info ( _log , " Signal detected " ) ;
}
_noSignalCounter = 0 ;
}
2021-04-04 12:43:29 +02:00
if ( _noSignalCounter < _noSignalCounterThreshold )
2020-12-18 17:38:21 +01:00
{
emit newFrame ( image ) ;
}
2021-04-04 12:43:29 +02:00
else if ( _noSignalCounter = = _noSignalCounterThreshold )
2020-12-18 17:38:21 +01:00
{
_noSignalDetected = false ;
Info ( _log , " Signal lost " ) ;
}
}
else
emit newFrame ( image ) ;
}
2021-04-04 12:43:29 +02:00
void MFGrabber : : setDevice ( const QString & device )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( _currentDeviceName ! = device )
{
_currentDeviceName = device ;
_reload = true ;
}
2021-01-31 13:49:31 +01:00
}
2021-04-04 12:43:29 +02:00
bool MFGrabber : : setInput ( int input )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( Grabber : : setInput ( input ) )
2021-01-31 13:49:31 +01:00
{
2021-04-04 12:43:29 +02:00
_reload = true ;
return true ;
2021-01-31 13:49:31 +01:00
}
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
return false ;
2020-12-18 17:38:21 +01:00
}
2021-04-04 12:43:29 +02:00
bool MFGrabber : : setWidthHeight ( int width , int height )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( Grabber : : setWidthHeight ( width , height ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
_reload = true ;
return true ;
2020-12-18 17:38:21 +01:00
}
2021-04-04 12:43:29 +02:00
return false ;
2020-12-18 17:38:21 +01:00
}
2021-04-04 12:43:29 +02:00
void MFGrabber : : setEncoding ( QString enc )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( _pixelFormatConfig ! = parsePixelFormat ( enc ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
_pixelFormatConfig = parsePixelFormat ( enc ) ;
if ( _initialized )
{
Debug ( _log , " Set hardware encoding to: %s " , QSTRING_CSTR ( enc . toUpper ( ) ) ) ;
_reload = true ;
}
else
_pixelFormat = _pixelFormatConfig ;
2020-12-18 17:38:21 +01:00
}
}
2021-04-04 12:43:29 +02:00
void MFGrabber : : setBrightnessContrastSaturationHue ( int brightness , int contrast , int saturation , int hue )
2021-01-26 20:01:23 +01:00
{
2021-04-04 12:43:29 +02:00
if ( _brightness ! = brightness | | _contrast ! = contrast | | _saturation ! = saturation | | _hue ! = hue )
2021-01-26 20:01:23 +01:00
{
2021-04-04 12:43:29 +02:00
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 ;
_hue = hue ;
_reload = true ;
2021-01-26 20:01:23 +01:00
}
}
2021-04-04 12:43:29 +02:00
void MFGrabber : : setSignalThreshold ( double redSignalThreshold , double greenSignalThreshold , double blueSignalThreshold , int noSignalCounterThreshold )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
_noSignalThresholdColor . red = uint8_t ( 255 * redSignalThreshold ) ;
_noSignalThresholdColor . green = uint8_t ( 255 * greenSignalThreshold ) ;
_noSignalThresholdColor . blue = uint8_t ( 255 * blueSignalThreshold ) ;
_noSignalCounterThreshold = qMax ( 1 , noSignalCounterThreshold ) ;
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
if ( _signalDetectionEnabled )
Info ( _log , " Signal threshold set to: {%d, %d, %d} and frames: %d " , _noSignalThresholdColor . red , _noSignalThresholdColor . green , _noSignalThresholdColor . blue , _noSignalCounterThreshold ) ;
2021-01-24 12:16:16 +01:00
}
2021-04-04 12:43:29 +02:00
void MFGrabber : : setSignalDetectionOffset ( double horizontalMin , double verticalMin , double horizontalMax , double verticalMax )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
// rainbow 16 stripes 0.47 0.2 0.49 0.8
// unicolor: 0.25 0.25 0.75 0.75
2021-01-27 18:55:21 +01:00
2021-04-04 12:43:29 +02:00
_x_frac_min = horizontalMin ;
_y_frac_min = verticalMin ;
_x_frac_max = horizontalMax ;
_y_frac_max = verticalMax ;
if ( _signalDetectionEnabled )
Info ( _log , " Signal detection area set to: %f,%f x %f,%f " , _x_frac_min , _y_frac_min , _x_frac_max , _y_frac_max ) ;
2020-12-18 17:38:21 +01:00
}
2021-04-04 12:43:29 +02:00
void MFGrabber : : setSignalDetectionEnable ( bool enable )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( _signalDetectionEnabled ! = enable )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
_signalDetectionEnabled = enable ;
if ( _initialized )
Info ( _log , " Signal detection is now %s " , enable ? " enabled " : " disabled " ) ;
2020-12-18 17:38:21 +01:00
}
}
2021-04-04 12:43:29 +02:00
bool MFGrabber : : reload ( bool force )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
if ( _sourceReader & & ( _reload | | force ) )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
Info ( _log , " Reloading Media Foundation Grabber " ) ;
uninit ( ) ;
_pixelFormat = _pixelFormatConfig ;
_newDeviceName = _currentDeviceName ;
_reload = false ;
return start ( ) ;
2020-12-18 17:38:21 +01:00
}
2021-04-04 12:43:29 +02:00
2021-01-26 20:01:23 +01:00
return false ;
2020-12-18 17:38:21 +01:00
}
2021-04-04 12:43:29 +02:00
QJsonArray MFGrabber : : discover ( const QJsonObject & params )
2020-12-18 17:38:21 +01:00
{
2021-04-04 12:43:29 +02:00
DebugIf ( verbose , _log , " params: [%s] " , QString ( QJsonDocument ( params ) . toJson ( QJsonDocument : : Compact ) ) . toUtf8 ( ) . constData ( ) ) ;
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
enumVideoCaptureDevices ( ) ;
2020-12-18 17:38:21 +01:00
2021-04-04 12:43:29 +02:00
QJsonArray inputsDiscovered ;
for ( auto it = _deviceProperties . begin ( ) ; it ! = _deviceProperties . end ( ) ; + + it )
2021-01-26 20:01:23 +01:00
{
2021-04-04 12:43:29 +02:00
QJsonObject device , in ;
QJsonArray video_inputs , formats ;
device [ " device " ] = it . key ( ) ;
device [ " device_name " ] = it . key ( ) ;
device [ " type " ] = " v4l2 " ;
in [ " name " ] = " " ;
in [ " inputIdx " ] = 0 ;
QStringList encodingFormats = QStringList ( ) ;
for ( int i = 0 ; i < _deviceProperties [ it . key ( ) ] . count ( ) ; + + i )
if ( ! encodingFormats . contains ( pixelFormatToString ( _deviceProperties [ it . key ( ) ] [ i ] . pf ) , Qt : : CaseInsensitive ) )
encodingFormats < < pixelFormatToString ( _deviceProperties [ it . key ( ) ] [ i ] . pf ) . toLower ( ) ;
for ( auto encodingFormat : encodingFormats )
{
QJsonObject format ;
QJsonArray resolutionArray ;
format [ " format " ] = encodingFormat ;
QMultiMap < int , int > deviceResolutions = QMultiMap < int , int > ( ) ;
for ( int i = 0 ; i < _deviceProperties [ it . key ( ) ] . count ( ) ; + + i )
if ( ! deviceResolutions . contains ( _deviceProperties [ it . key ( ) ] [ i ] . width , _deviceProperties [ it . key ( ) ] [ i ] . height ) & & _deviceProperties [ it . key ( ) ] [ i ] . pf = = parsePixelFormat ( encodingFormat ) )
deviceResolutions . insert ( _deviceProperties [ it . key ( ) ] [ i ] . width , _deviceProperties [ it . key ( ) ] [ i ] . height ) ;
for ( auto width_height = deviceResolutions . begin ( ) ; width_height ! = deviceResolutions . end ( ) ; width_height + + )
{
QJsonObject resolution ;
QJsonArray fps ;
resolution [ " width " ] = width_height . key ( ) ;
resolution [ " height " ] = width_height . value ( ) ;
QIntList framerates = QIntList ( ) ;
for ( int i = 0 ; i < _deviceProperties [ it . key ( ) ] . count ( ) ; + + i )
{
int fps = _deviceProperties [ it . key ( ) ] [ i ] . numerator / _deviceProperties [ it . key ( ) ] [ i ] . denominator ;
if ( ! framerates . contains ( fps ) & & _deviceProperties [ it . key ( ) ] [ i ] . pf = = parsePixelFormat ( encodingFormat ) & & _deviceProperties [ it . key ( ) ] [ i ] . width = = width_height . key ( ) & & _deviceProperties [ it . key ( ) ] [ i ] . height = = width_height . value ( ) )
framerates < < fps ;
}
for ( auto framerate : framerates )
fps . append ( framerate ) ;
resolution [ " fps " ] = fps ;
resolutionArray . append ( resolution ) ;
}
format [ " resolutions " ] = resolutionArray ;
formats . append ( format ) ;
}
in [ " formats " ] = formats ;
video_inputs . append ( in ) ;
device [ " video_inputs " ] = video_inputs ;
inputsDiscovered . append ( device ) ;
2020-12-18 17:38:21 +01:00
}
2021-04-04 12:43:29 +02:00
_deviceProperties . clear ( ) ;
DebugIf ( verbose , _log , " device: [%s] " , QString ( QJsonDocument ( inputsDiscovered ) . toJson ( QJsonDocument : : Compact ) ) . toUtf8 ( ) . constData ( ) ) ;
return inputsDiscovered ;
2020-12-18 17:38:21 +01:00
}