/************************************************************************** A/V Stream Camera Sample Copyright (c) 2013, Microsoft Corporation. File: Roi.cpp Abstract: This file contains the implemention of CRoiConfig, CRoiProperty and related classes used in the filter to implement ROI handling. History: created 6/27/2013 **************************************************************************/ #include "Common.h" /************************************************************************** PAGED CODE **************************************************************************/ #ifdef ALLOC_PRAGMA #pragma code_seg("PAGE") #endif // ALLOC_PRAGMA // Helper function to diff ptrs. inline UINT_PTR ByteDiffPtrs( PVOID pBase, PVOID pCurrent ) { PAGED_CODE(); return reinterpret_cast(pCurrent) - reinterpret_cast(pBase) ; } // Helper function to get the size of an ROI control based just on the ID. static ULONG GetSizeOfRoiInfo( ULONG ControlId ) { PAGED_CODE(); switch( ControlId ) { case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: return sizeof(KSCAMERA_EXTENDEDPROP_ROI_WHITEBALANCE); break; case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: return sizeof(KSCAMERA_EXTENDEDPROP_ROI_EXPOSURE); break; case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: return sizeof(KSCAMERA_EXTENDEDPROP_ROI_FOCUS); break; default: NT_ASSERT(FALSE); return 0; } } // Helper function to get the size of an ROI control. static ULONG GetSize( _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl ) { PAGED_CODE(); // This actually needs to return the type based on the ROI_INFO size ULONG RoiInfoSize = GetSizeOfRoiInfo( pCtrl->ControlId ); if( RoiInfoSize ) { return sizeof(KSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL) + (pCtrl->ROICount * RoiInfoSize); } else { NT_ASSERTMSG("GetSizeOfRoiInfo( pCtrl->ControlId ) returned 0! Should never happen!", FALSE); return 0; } } // Helper function to walk to the next ROI control. static PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL NextCtrl( _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl ) { PAGED_CODE(); ULONG Size = GetSize( pCtrl ); if( Size ) { return reinterpret_cast (((PBYTE) pCtrl) + Size); } else { NT_ASSERTMSG("GetSize( pCtrl ) returned 0! Should never happen!", FALSE); return nullptr; } } PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL CRoiProperty:: Find( _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property ) /*++ Routine Description: Search this ROI property for a property ID. Arguments: Property - Which control to find. Return Value: A pointer to the ROI control structure. --*/ { PAGED_CODE(); // We assume the controls have been validated first. PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pIspCtrl = reinterpret_cast(reinterpret_cast(this) + sizeof(CRoiProperty)); // Loop thru the controls. for( ULONG i=0; iControlId == (ULONG) Property ) { return pIspCtrl; } // Advance to the next control. pIspCtrl = NextCtrl( pIspCtrl ); if(pIspCtrl == nullptr) { NT_ASSERTMSG("NextCtrl( pCtrl ) returned 0! Should never happen!", FALSE); return nullptr; } } return nullptr; } PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL CRoiProperty:: Add( _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl ) /*++ Routine Description: Add a control to the end of this header. Note: This function does not reallocate memory. Be careful to ensure there is space available. Arguments: pCtrl - An ROI control to append. Return Value: A pointer to the appended ROI control structure. --*/ { PAGED_CODE(); // Only bother to add if there are ROIs. if( pCtrl->ROICount > 0 ) { // We assume the controls have been validated first. PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pIspCtrl = reinterpret_cast(reinterpret_cast(this) + sizeof(CRoiProperty)); // Loop thru all the controls. for( ULONG i=0; i(reinterpret_cast(this) + sizeof(*this)); // Loop thru the controls. for( ULONG i=0; i(pIspCtrl) + sizeof(*pIspCtrl) ) || m_Hdr.Size < ByteDiffPtrs( &m_Hdr, reinterpret_cast(pIspCtrl) + sizeof(*pIspCtrl) ) ) { //NT_ASSERT(FALSE); DBG_TRACE( "Failed(1): Size=%d, should be at least %Iu", Size, ByteDiffPtrs( this, pIspCtrl+1 ) ); DBG_TRACE( " Or: m_Hdr.Size=%d, should be at least %Iu", m_Hdr.Size, ByteDiffPtrs( &m_Hdr, pIspCtrl+1 ) ); DBG_LEAVE( "()=false" ); return false; } // Make sure the control is of a known type switch( pIspCtrl->ControlId ) { case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: break; default: //NT_ASSERT(FALSE); DBG_LEAVE( "()=false : Invalid ControlId = 0x%08X", pIspCtrl->ControlId ); return false; } // Make sure there is room for the # of ROIs reported if( Size < ByteDiffPtrs( this, NextCtrl(pIspCtrl) ) || m_Hdr.Size < ByteDiffPtrs( &m_Hdr, NextCtrl(pIspCtrl) ) ) { //NT_ASSERT(FALSE); DBG_TRACE( "Failed(2): Size=%d, should be at least %Iu", Size, ByteDiffPtrs( this, NextCtrl(pIspCtrl) ) ); DBG_TRACE( " Or: m_Hdr.Size=%d, should be at least %Iu", m_Hdr.Size, ByteDiffPtrs( &m_Hdr, NextCtrl(pIspCtrl) ) ); DBG_LEAVE( "()=false" ); return false; } // Validate the ROIs for( ULONG j=0; jROICount; j++ ) { // Index into to the control's ROI list. Get the equivilent of "pIspCtrl->RoiInfo[j]" PKSCAMERA_EXTENDEDPROP_ROI_INFO pRoiInfo = reinterpret_cast ((reinterpret_cast(pIspCtrl) + sizeof(*pIspCtrl)) + (j * GetSizeOfRoiInfo(pIspCtrl->ControlId) )); // Validate the cooridinates if( pRoiInfo->Region.top < (LONG) TO_Q31(0) || pRoiInfo->Region.bottom < (LONG) TO_Q31(0) || pRoiInfo->Region.left < (LONG) TO_Q31(0) || pRoiInfo->Region.right < (LONG) TO_Q31(0) ) { //NT_ASSERT(FALSE); DBG_TRACE( "Failed: Region out of range." ); DBG_TRACE( " (top=0x%08X, left=0x%08X, bottom=0x%08X, right=0x%08X)" , pRoiInfo->Region.top , pRoiInfo->Region.bottom , pRoiInfo->Region.left , pRoiInfo->Region.right ); DBG_LEAVE( "()=false" ); return false; } // Validate the RegionOfInterestType switch( pRoiInfo->RegionOfInterestType ) { case KSCAMERA_EXTENDEDPROP_ROITYPE_UNKNOWN: case KSCAMERA_EXTENDEDPROP_ROITYPE_FACE: break; default: //NT_ASSERT(FALSE); DBG_LEAVE( "()=false Failed: RegionOfInterestType=%d", pRoiInfo->RegionOfInterestType ); return false; } // Validate the Weight if( pRoiInfo->Weight > 100 ) { //NT_ASSERT(FALSE); DBG_LEAVE( "()=false Failed: Weight=%d", pRoiInfo->Weight ); return false; } // Validate the Flags. // Note: the driver doesn't allow mis-matched ROI Flags. ULONGLONG RoiFlags = pRoiInfo->Flags & ~KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION; switch( pIspCtrl->ControlId ) { // Exposure & WhiteBalance only support Auto, Manual & Lock. case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: if( j!=0 ) { if( RoiFlags != ExposureFlags ) { DBG_LEAVE( "()=false : Mismatched Exposure Flags" ); return false; } } else { switch( RoiFlags & (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO| KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ) { case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: case 0: break; default: DBG_LEAVE( "()=false : Invalid Exposure Flags" ); return false; } ExposureFlags = RoiFlags; } break; case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: if( j!=0 ) { if( RoiFlags != WhiteBalanceFlags ) { DBG_LEAVE( "()=false : Mismatched WhiteBalance Flags" ); return false; } } else { switch( RoiFlags & (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO| KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ) { case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: case 0: break; default: DBG_LEAVE( "()=false : Invalid WhiteBalance Flags" ); return false; } WhiteBalanceFlags = RoiFlags; } break; // Focus supports additional flags... case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: if( j!=0 ) { if( RoiFlags != FocusFlags ) { DBG_LEAVE( "()=false : Mismatched Focus Flags" ); return false; } } else { switch( RoiFlags & (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK) ) { case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS: case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK: case 0: break; default: DBG_LEAVE( "()=false : Invalid Focus Flags" ); return false; } switch( RoiFlags & KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MASK ) { case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MACRO: case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_NORMAL: case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE: // Depreciated cases are not supported: //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_INFINITY: //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_HYPERFOCAL: case 0: break; default: DBG_LEAVE( "()=false : Invalid Focus Range" ); return false; } FocusFlags = RoiFlags; } break; } } // Advance to the next control. pIspCtrl = NextCtrl( pIspCtrl ) ; } DBG_LEAVE( "()=true" ); return true; } // Log to the debug output. void CRoiProperty:: Log() { PAGED_CODE(); DBG_TRACE("Version=%d, PinId=%d, Capability=0x%016llX, Flags=0x%016llX, Size=%d", Version, PinId, Capability, Flags, Size ); CRoiIspControl *pIspCtrl = reinterpret_cast (this+1); // Loop thru the controls. for( ULONG i=0; iLog(); // Advance to the next control. pIspCtrl = reinterpret_cast( NextCtrl( pIspCtrl ) ) ; if (pIspCtrl == nullptr) { NT_ASSERTMSG("NextCtrl( pCtrl ) returned 0! Should never happen!", FALSE); return; } } } // // Initialize a block of memory from another ROI control // // Note: This function assumes enough ROI_INFO structures follow // such that this copy will not overrun. // BOOL CRoiIspControl:: Init( _In_ CRoiProperty *pRoiProperty, _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property ) /*++ Routine Description: Initialize a block of memory from another ROI control Note: This function assumes enough ROI_INFO structures follow such that this copy will not overrun. Arguments: pRoiProperty - The extended ROI control payload. Property - Which ISP control to copy from the payload. Return Value: TRUE - The ROI was set. FALSE - The ROI was cleared. --*/ { PAGED_CODE(); CRoiIspControl * pCtrl = reinterpret_cast (pRoiProperty->Find( Property )); if( pCtrl ) { memcpy( this, pCtrl, pCtrl->GetSize() ); return TRUE; } ControlId = Property; ROICount = 0; return FALSE; } // Log to the debug output. void CRoiIspControl:: Log() { PAGED_CODE(); switch( ControlId ) { case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: DBG_TRACE("WhiteBalance"); break; case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: DBG_TRACE("Exposure"); break; case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: DBG_TRACE("Focus"); break; default: DBG_TRACE("[Unknown]"); NT_ASSERT(FALSE); break; } DBG_TRACE("Result=0x%08X", Result); for( ULONG i=0; iRoiInfo[j]" PKSCAMERA_EXTENDEDPROP_ROI_INFO pRoiInfo = reinterpret_cast (((PBYTE) (this+1)) + (i * GetSizeOfRoiInfo(ControlId) )); DBG_TRACE(" | Flags=0x%016llX [Top=0x%08X, Left=0x%08X, Bottom=0x%08X, Right=0x%08X]", pRoiInfo->Flags, pRoiInfo->Region.top, pRoiInfo->Region.left, pRoiInfo->Region.bottom, pRoiInfo->Region.right ); } } // // Initialize a block of memory with an empty control list. // CRoiIspControl:: CRoiIspControl( _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property ) { PAGED_CODE(); ControlId = Property; ROICount = 0; Result = 0; Reserved = 0; } ULONG CRoiIspControl:: GetSize() { PAGED_CODE(); return (ROICount>0) ? ::GetSize( this ) : 0; }