diff options
| author | karlf <[email protected]> | 2016-08-11 13:28:13 -0700 |
|---|---|---|
| committer | karlf <[email protected]> | 2016-08-11 13:28:13 -0700 |
| commit | 96eb96dfb613e4c745db6bd1f53a92fe7e2290fc (patch) | |
| tree | ad5f3ede5cbcd6b598677ce41bcf8318471bdd92 /AVStream/avscamera/sys/imagehwsim.cpp | |
| parent | 687b274aa38fd05c8c26e3068932121876d7f745 (diff) | |
Updated for "Windows 10 Anniversary Update" (Version 1607)
Diffstat (limited to 'AVStream/avscamera/sys/imagehwsim.cpp')
| -rw-r--r-- | AVStream/avscamera/sys/imagehwsim.cpp | 1509 |
1 files changed, 1509 insertions, 0 deletions
diff --git a/AVStream/avscamera/sys/imagehwsim.cpp b/AVStream/avscamera/sys/imagehwsim.cpp new file mode 100644 index 00000000..e6256d45 --- /dev/null +++ b/AVStream/avscamera/sys/imagehwsim.cpp @@ -0,0 +1,1509 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + ImageHwSim.cpp + + Abstract: + + This file contains the implementation of the CImageHardwareSimulation + class. + + This is a specialization of CHardwareSimulation that provides photo- + specific metadata and implements specialized functionality for photo, + photo sequence and variable photo sequence. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CImageHardwareSimulation:: +CImageHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : CHardwareSimulation( Sensor, PinID ) + , m_Clock(NULL) + , m_PfsLoopLimit(0) + , m_PfsFrameLimit(0) + , m_pIspSettings(nullptr) + , m_PfsLoopNumber(0) + , m_PfsFrameNumber(0) + , m_GlobalFrameNumber(0) + , m_bEndOfSequence(FALSE) + , m_PastBufferCount(0) // Zero only when the simulation inits. + +/*++ + +Routine Description: + + Construct a hardware simulation + +Arguments: + + Sensor - + The hardware sink interface. This is used to trigger + fake interrupt service routines from. + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + InitializeListHead (&m_BurstList); +} + +CImageHardwareSimulation:: +~CImageHardwareSimulation() +{ + + PAGED_CODE(); + + SAFE_DELETE( m_pIspSettings ); + + if (m_Clock) + { + m_Clock -> Release (); + m_Clock = NULL; + } +} + +/*************************************************/ + +NTSTATUS +CImageHardwareSimulation:: +Start ( + _In_ CSynthesizer *ImageSynth, + _In_ ULONG Width, + _In_ ULONG Height, + _In_ ULONG ImageSize, + _In_ PIN_MODE pinMode +) + +/*++ + +Routine Description: + + Start capturing frames. This turns on the timer and begins frame capture, + but it we do not deliver frames until we receive a Trigger. We keep track + of starvation starting at this point. + +Arguments: + + ImageSynth - + The image synthesizer to use to generate pictures to display + on the capture buffer. + + Width - + The image width + + Height - + The image height + + ImageSize - + The size of the image. We allocate a temporary scratch buffer + based on this size to fake hardware. + + PinMode - + Normal or photosequence. + + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + + PAGED_CODE(); + + DBG_ENTER("(Width=%d, Height=%d, ImageSize=%d, pinMode=%s): m_PinID=%d", + Width, Height, ImageSize, pinMode?"PinBurstMode":"PinNormalMode", m_PinID ); + + NTSTATUS Status = STATUS_SUCCESS; + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + m_Synthesizer = ImageSynth; + + NT_ASSERT(ImageSize); + m_ImageSize = ImageSize; + m_Height = Height; + m_Width = Width; + + m_NumMappingsCompleted = 0; + m_ScatterGatherMappingsQueued = 0; + m_NumFramesSkipped = 0; + m_InterruptTime = 0; + m_bTriggered = FALSE; + m_bEndOfSequence = FALSE; + m_pClone = NULL; + m_bPastBufferTrigger = FALSE; + m_PinMode = pinMode; + m_TriggerTime = 0; + m_bFlashed = FALSE; + + DBG_TRACE("m_bTriggered=FALSE, m_bPastBufferTrigger=FALSE"); + + m_FlashStatus = 0; + + // Initialize VPS counters. + m_PfsLoopNumber = 0 ; + m_PfsFrameNumber= 0 ; + m_GlobalFrameNumber = 0; + + KeQuerySystemTime (&m_StartTime); + + if( !m_Synthesizer->Initialize() ) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + // + // If everything is ok, start issuing interrupts. + // + if (NT_SUCCESS (Status)) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + m_TimePerFrame; + + m_PinState = PinRunning; + m_IsrTimer.Set( NextTime ); + + } + + DBG_LEAVE("(Width=%d, Height=%d, ImageSize=%d, pinMode=%s): m_PinID=%d, Status=0x%08X", + Width, Height, ImageSize, pinMode?"PinBurstMode":"PinNormalMode", m_PinID, Status ); + + return Status; + +} + +NTSTATUS +CImageHardwareSimulation:: +Trigger( + _In_ LONG mode +) +/*++ + +Routine Description: + + Take a picture / Begin delivering frames. + +Arguments: + + mode - + Normal trigger, start or stop photo sequence. + + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + + DBG_ENTER( "(mode=0x%08X), m_PinMode=%d", mode, m_PinMode ); + + KScopedMutex Lock( m_ListLock ); + + //Start Trigger for Burst Mode + if(mode & KS_VideoControlFlag_StartPhotoSequenceCapture) + { + if(m_bTriggered == FALSE && m_PinMode == PinBurstMode) + { + m_bPastBufferTrigger = TRUE; + m_bTriggered = TRUE; + DBG_TRACE("m_bTriggered=TRUE, m_bPastBufferTrigger=TRUE"); + + status = STATUS_SUCCESS; + } + else + { + status = STATUS_INVALID_PARAMETER; + } + } + //Stop Trigger for Burst Mode + else if(mode & KS_VideoControlFlag_StopPhotoSequenceCapture) + { + if(m_bTriggered == TRUE && m_PinMode == PinBurstMode) + { + m_bTriggered = FALSE; + DBG_TRACE("m_bTriggered=FALSE"); + + // reset the PFS EOS, frame number and loop count. + m_bEndOfSequence = FALSE; + m_PfsFrameNumber = 0; + m_PfsLoopNumber = 0; + + m_bFlashed = FALSE; + status = STATUS_SUCCESS; + } + else + { + status = STATUS_INVALID_PARAMETER; + } + } + //Normal Trigger + else if(mode & KS_VideoControlFlag_Trigger ) + { + if(m_PinMode == PinBurstMode) + { + status = STATUS_INVALID_PARAMETER; + } + else + { + m_bTriggered = TRUE; + m_bEndOfSequence = FALSE; + DBG_TRACE("m_bTriggered=TRUE"); + } + } + + DBG_LEAVE("()"); + + return STATUS_SUCCESS; +} + + +LONG +CImageHardwareSimulation:: +GetTriggerMode() +/*++ + +Returns the pin's current trigger mode and state: + + Return Value: + + mode - + Normal trigger, start or stop photo sequence. + +--*/ + +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + LONG mode = 0; + + DBG_ENTER( "(), m_PinMode=%d", m_PinMode ); + + KScopedMutex Lock( m_ListLock ); + + if( m_bTriggered ) + { + mode = (m_PinMode == PinBurstMode) ? + KS_VideoControlFlag_StartPhotoSequenceCapture : + KS_VideoControlFlag_Trigger; + } + + DBG_LEAVE("()=0x%08X", mode); + + return mode; +} + + +NTSTATUS +CImageHardwareSimulation:: +SetMode( + _In_ ULONGLONG Flags, + _In_ ULONG PastBuffers +) +/*++ + +Routine Description: + + Set the photo sequence mode. + +Arguments: + + Flags - + Normal or photo sequence. + + PastBuffers - + Number of history frames to gather. + + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER( "(Flags=0x%016llX, PastBuffers=%d)", Flags, PastBuffers ); + + if(m_bTriggered == TRUE) + { + return STATUS_INVALID_TRANSACTION; + } + + m_PastBufferCount = PastBuffers; + + if(Flags & KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE) + { + m_PinMode = PinBurstMode; + } + else + { + m_PinMode = PinNormalMode; + } + + DBG_LEAVE("()"); + + return STATUS_SUCCESS; +} + +// +// Set the interval between frames here. +// +NTSTATUS +CImageHardwareSimulation:: +SetPhotoFrameRate( + _In_ ULONGLONG TimePerFrame +) +{ + PAGED_CODE(); + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + m_TimePerFrame = TimePerFrame; + + // + // Reschedule the timer if the hardware isn't being stopped. + // + if( m_PinState == PinRunning ) // && !m_StopHardware ) + { + // First restart our start time. We can't use the old time. + KeQuerySystemTime( &m_StartTime ); + + // + // Reschedule the timer for the next interrupt time. + // + m_StartTime.QuadPart += m_TimePerFrame; + m_InterruptTime = 0; + + m_IsrTimer.Set( m_StartTime ); + } + return STATUS_SUCCESS; +} + +NTSTATUS +CImageHardwareSimulation:: +Stop() + +/*++ + +Routine Description: + + Stop the hardware simulation... + + Wait until the timer has stopped, flush the queue, dereference the clock + and reset our state before returning. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + // If the hardware is told to stop while it's running, we need to + // halt the interrupts first. If we're already paused, this has + // already been done. + // + + DBG_ENTER("(): m_PinID=%d", m_PinID); + + // + // Protect the S/G list + // + KScopedMutex Lock( m_ListLock ); + + CHardwareSimulation::Stop(); + + // + // Free S/G buffer + // + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Stop Burst List" ); + + if (m_Clock) + { + m_Clock -> Release (); + m_Clock = NULL; + } + + m_bTriggered = FALSE; + m_bEndOfSequence = FALSE; + m_pClone = NULL; + m_bPastBufferTrigger = FALSE; + m_PinMode = PinNormalMode; + m_TriggerTime = 0; + + DBG_TRACE("m_bTriggered=FALSE, m_bPastBufferTrigger=FALSE"); + + DBG_LEAVE("(): m_PinID=%d", m_PinID); + + return STATUS_SUCCESS; +} + + +/*************************************************/ + + +// +// Helper function that collects current settings into our metadata structure. +// +METADATA_IMAGEAGGREGATION +CImageHardwareSimulation:: +GetMetadata() +{ + PAGED_CODE(); + + METADATA_IMAGEAGGREGATION Metadata; + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + + // Wipe the metadata so all settings will default to "Not Set". + RtlZeroMemory( &Metadata, sizeof(Metadata) ); + + // Identify the current PFS frame number. + // If PFS not active, then this item is not present. + Metadata.FrameId.Set = IsPfsActive(); + Metadata.FrameId.Value = (ULONG) m_PfsFrameNumber; + DBG_TRACE("Metadata.FrameId.Set=%s, Metadata.FrameId.Value=%d", + (Metadata.FrameId.Set?"Yes":"No"), Metadata.FrameId.Value); + + // Just reflect the exposure time from the setting. + //Metadata.ExposureTime.Set = TRUE; + //Metadata.ExposureTime.Value = GetCurrentExposureTime(); + + // Just reflect the ISO Speed from the setting. + Metadata.ISOSpeed = CMetadataLong(GetCurrentISOSpeed()); + DBG_TRACE("ISO=%d, ISO Flags=0x%016llX", Metadata.ISOSpeed.Value, pSettings->ISOMode); + + // TODO: Do we need to bracket this by whether or not a flash has been taken? + // Report the current flash mode. + Metadata.FlashOn = CMetadataLong((ULONG) pSettings->FlashMode); + + // Report the current flash power. + Metadata.FlashPower = CMetadataLong(pSettings->FlashValue); + + // Set the White Balance lock state. + Metadata.WhiteBalanceLocked = CMetadataLong( + ( (pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) + == KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ); + + // Set the Exposure lock state. + Metadata.ExposureLocked = CMetadataLong( + ( (pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) + == KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ); + + Metadata.ExposureTime = + //CMetadataRational(GetCurrentExposureTime(), 10000000); + CMetadataLongLong( GetCurrentExposureTime() ); + + Metadata.LensPosition = CMetadataLong( pSettings->FocusSetting.VideoProc.Value.ul ); + + Metadata.SceneMode = CMetadataULongLong(KSCAMERA_EXTENDEDPROP_SCENEMODE_AUTO); //TODO: Need to fill in real value from CCaptureFilter::m_SceneMode + + Metadata.WhiteBalanceMode = CMetadataLong((ULONG) pSettings->WhiteBalanceMode); + + CExtendedVidProcSetting Zoom; + m_Sensor->GetZoom( &Zoom ); + Metadata.ZoomFactor = CMetadataLong(Zoom.GetLONG()); //TODO: Fill in a real value from zoom simulation. + + Metadata.FocusLocked = CMetadataLong(FALSE); //TODO: Fill in a real value when we complete the focus changes. + + // Add EVCompensation metadata... + Metadata.EVCompensation = CMetadataEVCompensation(pSettings->EVCompensation.Mode, pSettings->EVCompensation.Value); + + Metadata.Orientation = CMetadataShort(Metadata_Orientation_TopBottomLeftRight); //TODO: Randomize? + + { + LARGE_INTEGER SystemTime; + LARGE_INTEGER LocalTime; + + KeQuerySystemTimePrecise( &SystemTime ); + ExSystemTimeToLocalTime( &SystemTime, &LocalTime ); + RtlTimeToTimeFields( &LocalTime, &Metadata.LocalTime.Time ); + Metadata.LocalTime.Set = TRUE; + } + + Metadata.Make = CMetadataShortString("Make: Microsoft SOC Camera"); + Metadata.Model = CMetadataShortString( "Model: AvsCam" ); + Metadata.Software = CMetadataShortString( "Software: Microsoft Camera Sim" ); + + Metadata.ColorSpace.Set = TRUE; + Metadata.ColorSpace.Value = 0xFFFF; // 0xFFFF Means "uncalibrated". Use this value for all non-RGB formats. + + Metadata.Gamma = CMetadataRational(); + + Metadata.MakerNote = CMetadataShortString( "Maker's Note..." ); + + // Just reflect the exposure time from the setting. + //Metadata.ExposureTime = + // CMetadataRational( GetCurrentExposureTime(), 1000 ); // report exposure time as milliseconds. + + Metadata.FNumber = CMetadataRational(4); // Fake an FNumber of 4. // TODO: It looks like we might be able to calculate this. + + Metadata.ExposureProgram.Set = TRUE; + Metadata.ExposureProgram.Value = GetExposureProgram(); + + Metadata.ShutterSpeedValue = CMetadataSRational(); // TODO: Calculate this from the ExposureTime. *** + Metadata.Aperture = CMetadataRational(4); // TODO: Calculate this from the F-Number. (We're currently faking the FNumber.) + Metadata.Brightness = CMetadataSRational(0); // TODO: Find a more reasonable brightness value. + Metadata.ExposureBias = CMetadataSRational(0); // TODO: More reasonable? + Metadata.SubjectDistance = CMetadataRational(0xFFFFFFFF); // Distance in meters. Infinity. (Anything better?) + + Metadata.MeteringMode.Set = TRUE; + Metadata.MeteringMode.Value = (USHORT) GetRandom( (ULONG) 1, (ULONG) 6); // Pick a number ... any number. + + Metadata.LightSource.Set = TRUE; + Metadata.LightSource.Value = 1; // TODO: Pick a random value; but override when a flash occurs. + + Metadata.Flash.Set = TRUE; + Metadata.Flash.Value = (UINT16) pSettings->FlashMode; // We assume that the flash fires when requested! + DBG_TRACE("FlashMode=0x%016llX, FlashPower=%d", pSettings->FlashMode, pSettings->FlashValue); + + Metadata.FocalLength = CMetadataRational(); // TODO: Calculate? + Metadata.FocalPlaneXResolution = CMetadataRational(); // TODO: Calculate? + Metadata.FocalPlaneYResolution = CMetadataRational(); // TODO: Calculate? + Metadata.ExposureIndex = CMetadataRational(); // TODO: Calculate? + + Metadata.ExposureMode.Set = TRUE; + Metadata.ExposureMode.Value = 0 ; // Assume Auto exposure. + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Metadata.ExposureMode.Value = 0 ; // Manual exposure. + } + + Metadata.WhiteBalance.Set = TRUE; + Metadata.WhiteBalance.Value = 0 ; // Assume Auto white balance. + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Metadata.WhiteBalance.Value = 0 ; // Manual while balance. + } + + Metadata.DigitalZoomRatio = CMetadataRational(1); + + Metadata.FocalLengthIn35mmFilm = CMetadataShort(0); + Metadata.SceneCaptureType = CMetadataShort(0); + Metadata.GainControl = CMetadataRational(); + Metadata.Contrast = CMetadataShort(0); + Metadata.Saturation = CMetadataShort(0); + Metadata.Sharpness = CMetadataShort(0); + Metadata.SubjectDistanceRange = CMetadataShort(0); + + // Report (optional) focus state. + KSCAMERA_EXTENDEDPROP_FOCUSSTATE State = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_UNINITIALIZED; + if( NT_SUCCESS(m_Sensor->GetFocusState( &State )) ) + { + Metadata.FocusState = CMetadataLong((UINT32)State); + } + + return Metadata; +} + +// +// Emit metadata here for still pin. +// +void +CImageHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +/*++ + +Routine Description: + + Emit metadata for a photo. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + + // Add the normal frame info to the metadata + CHardwareSimulation::EmitMetadata( pStreamHeader ); + + if (0 != (pStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_METADATA)) + { + PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO)(pStreamHeader + 1); + PKSSTREAM_METADATA_INFO pMetadata = (PKSSTREAM_METADATA_INFO) (pFrameInfo + 1); + PCAMERA_METADATA_IMAGEAGGREGATION pAggregation = + (PCAMERA_METADATA_IMAGEAGGREGATION) (((PBYTE) pMetadata->SystemVa) + pMetadata->UsedSize); + ULONG BytesLeft = pMetadata->BufferSize - pMetadata->UsedSize; + + if( BytesLeft >= sizeof(*pAggregation) ) + { + pAggregation->Header.MetadataId = (ULONG) MetadataId_Custom_ImageAggregation; + pAggregation->Header.Size = sizeof(*pAggregation); + + // Just copy over the current frame's ISP settings for now. + // We still need to develop a contract between the driver and the MFT0 for these settings. + pAggregation->Data = GetMetadata(); + + pMetadata->UsedSize += sizeof(*pAggregation); + BytesLeft -= sizeof(*pAggregation); + } + + CExtendedVidProcSetting FaceDetect; + m_Sensor->GetFaceDetection(&FaceDetect); + + if( FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_PHOTO ) + { + DBG_TRACE("IMAGE"); + EmitFaceMetadata( + pStreamHeader, + FaceDetect.GetULONG(), + FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK, + 1); + } + } +} + +NTSTATUS +CImageHardwareSimulation:: +FillScatterGatherBuffers() + +/*++ + +Routine Description: + + The hardware has synthesized a buffer in scratch space and we're to + fill scatter / gather buffers. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("() m_PinID=0x%08X, m_ImageSize=0x%08X, m_ScatterGatherMappingsQueued=%d, " + "m_ScatterGatherBytesQueue=0x%08X", + m_PinID, m_ImageSize, m_ScatterGatherMappingsQueued, m_ScatterGatherBytesQueued); + + // + // We're using this list lock to protect our scatter / gather lists instead + // of some hardware mechanism / KeSynchronizeExecution / whatever. + // + //KeAcquireSpinLockAtDpcLevel (&m_ListLock); + + ULONG BufferRemaining = m_ImageSize; + + // + // If there aren't enough scatter / gather buffers queued, consider it starvation. + // + while( BufferRemaining && + !IsListEmpty(&m_ScatterGatherMappings) && + m_ScatterGatherBytesQueued >= BufferRemaining) + { + DBG_TRACE( "BufferRemaining=0x%08X, m_ScatterGatherBytesQueued=0x%08X, m_ScatterGatherMappingsQueued=%d", + BufferRemaining, m_ScatterGatherBytesQueued, m_ScatterGatherMappingsQueued ); + + LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + // Deal with cancellation. + PIRP pIrp = KsStreamPointerGetIrp(SGEntry->CloneEntry, FALSE, FALSE); + if (pIrp) + { + if (pIrp->Cancel) + { + DBG_TRACE( "Cancelling..." ); + FreeSGEntry( listEntry, L"StreamPointer Cancel SG List" ); + continue; + } + } + + // + // Since we're software, we'll be accessing this by virtual address... + // + ULONG BytesToCopy = min( BufferRemaining, SGEntry->ByteCount ); + + // Have the synthesizer output a frame to the buffer. + DBG_TRACE( "DataUsed before Commit() = %d", SGEntry->CloneEntry->StreamHeader->DataUsed ); + ULONG BytesCopied = + m_Synthesizer->DoCommit( SGEntry->Virtual, BytesToCopy ); + NT_ASSERT(BytesCopied); + DBG_TRACE( "BytesCopied = %d", BytesCopied ); + + BufferRemaining = 0; //-= BytesCopied; + + // Add metadata to the sample. + EmitMetadata( SGEntry -> CloneEntry -> StreamHeader ); + + ULONGLONG time = ConvertQPCtoTimeStamp(NULL); + + if (IsPhotoConfirmationNeeded()) + { + DBG_TRACE( "PhotoConfirmation is needed. Frame=%d, Time=0x%016llX", m_PfsFrameNumber, (LONGLONG) time ); + SGEntry->PhotoConfirmationInfo = PHOTOCONFIRMATION_INFO( m_PfsFrameNumber, (LONGLONG) time ); + } + + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = time; + DBG_TRACE("PresentationTime = 0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + + SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + + DBG_TRACE("m_FlashStatus=0x%016llX", m_FlashStatus); + + if(m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_ON || m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER || + m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_AUTO || m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER) + { + if(m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_SINGLEFLASH && time >= m_TriggerTime && m_TriggerTime != 0 && !m_bFlashed) + { + m_bFlashed = TRUE; + DBG_TRACE("(Single) FLASHED!!!"); + } + } + + // + // Release the scatter / gather entry back to our lookaside. + // + if( m_bTriggered && !IsPfsEOS() ) + { + DBG_TRACE("m_PinMode=%d", m_PinMode); + m_pClone = SGEntry->CloneEntry; + m_PhotoConfirmationInfo = SGEntry->PhotoConfirmationInfo; + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + DBG_TRACE( "m_NumMappingsCompleted=%d, m_PhotoConfirmationInfo.isRequired()=%s", m_NumMappingsCompleted, m_PhotoConfirmationInfo.isRequired()?"TRUE":"FALSE" ); + + if(m_PinMode != PinBurstMode) + { + m_bTriggered = FALSE; + DBG_TRACE("m_bTriggered=FALSE"); + } + + // Update the VPS frame and loop numbers here. Mark the frame as the EOS + // if we've completed the sequence. + // + // Note: It's actually up to DevProxy to stop feeding us frames! + if( AdvanceFrameCounter() ) + { + // We've reached the end of a VPS sequence! Mark the frame as EOS. + SGEntry->CloneEntry->StreamHeader->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_ENDOFPHOTOSEQUENCE; + m_bEndOfSequence = TRUE; + } + + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast <PVOID> (SGEntry) + ); + } + else + { + InsertTailList( &m_ScatterGatherMappings, listEntry ); + m_ScatterGatherMappingsQueued++; + m_pClone = NULL; + } + } + + DBG_LEAVE("()"); + + if (BufferRemaining) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + else + { + return STATUS_SUCCESS; + } +} + +/************************************************************************** + + Debug helpers + +**************************************************************************/ + +PCCHAR +AdvancedPhoto_Text( ULONGLONG Flags ) +{ + PAGED_CODE(); + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_OFF: return "Off"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO: return "Auto"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR: return "HDR"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF: return "FNF"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT: return "UltraLL"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR: return "Auto|HDR"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF: return "Auto|FNF"; + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT: return "Auto|UltraLL"; + default: + { + static + CHAR buffer[32]; + + RtlStringCbPrintfA(buffer, sizeof(buffer), "Unknown [0x%016llX]", Flags); + return (PCCHAR) buffer; + } + } +} + +/*************************************************/ + +void +CImageHardwareSimulation:: +FakeHardware() + +/*++ + +Routine Description: + + Simulate an interrupt and what the hardware would have done in the + time since the previous interrupt. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + m_InterruptTime++; + + // + // The hardware can be in a pause state in which case, it issues interrupts + // but does not complete mappings. In this case, don't bother synthesizing + // a frame and doing the work of looking through the mappings table. + // + if( m_PinState == PinRunning ) + { + if(m_PinMode == PinBurstMode && m_bTriggered && m_bPastBufferTrigger) + { + CompletePastBuffers(); + } + + m_Synthesizer->DoSynthesize(); + + CHAR Text[64]; + + CExtendedProperty Control; + m_Sensor->GetAdvancedPhoto(&Control); + RtlStringCbPrintfA(Text, sizeof(Text), "Adv: %s", AdvancedPhoto_Text(Control.Flags)); + m_Synthesizer->OverlayText( 0, m_Height-38, 1, Text, TRANSPARENT, WHITE ); + + // + // Fill scatter gather buffers + // + if (!NT_SUCCESS (FillScatterGatherBuffers ())) + { + InterlockedIncrement (PLONG (&m_NumFramesSkipped)); + } + } + + // + // Issue an interrupt to our hardware sink. This is a "fake" interrupt. + // It will occur at DISPATCH_LEVEL. + // + m_Sensor -> Interrupt (m_PinID); + + // + // Schedule the timer for the next interrupt time, if the pin is still running. + // + if( m_PinState == PinRunning ) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + + (m_TimePerFrame * (m_InterruptTime + 1)); + +#ifdef ENABLE_TRACING // To keep us from a tight spin when trying to debug this code... + LARGE_INTEGER Now; + KeQuerySystemTime(&Now); + + if( Now.QuadPart >= NextTime.QuadPart ) + { + NextTime.QuadPart = 0LL - m_TimePerFrame ; + } + +#endif + m_IsrTimer.Set( NextTime ); + } +} + +NTSTATUS +CImageHardwareSimulation:: +CompletePastBuffers() + +/*++ + +Routine Description: + + Find and complete any history frames. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + ULONG ulNumBuffers = m_PastBufferCount; + BOOLEAN bContinue = TRUE; + LIST_ENTRY *listEntry = NULL; + + DBG_ENTER("()"); + DBG_TRACE("m_PastBufferCount=%d, m_TriggerTime=0x%016llX", m_PastBufferCount, m_TriggerTime ); + + // If we're in burst mode and have ISP settings, we can't + // really support changing the ISP settings in the past... + // ... so we'll just treat them all past frames. + // Note: The upper layer will determine past frames from the + // metadata FrameId. + if( m_PinMode == PinBurstMode && + !m_pIspSettings ) + { + // Walk through the entire list of buffers, find the most + // recent frame presentation time and put all past buffers + // into another list. + while( !IsListEmpty(&m_ScatterGatherMappings) ) + { + listEntry = RemoveTailList(&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + NT_ASSERT(SGEntry); + NT_ASSERT(SGEntry->CloneEntry); + NT_ASSERT(SGEntry->CloneEntry->StreamHeader); + + // We've found one that's not stamped. We must be at the end. + // Push it back and exit the loop. + if(SGEntry->CloneEntry->StreamHeader->PresentationTime.Time == 0) + { + InsertTailList(&m_ScatterGatherMappings, listEntry); + m_ScatterGatherMappingsQueued++; + DBG_TRACE( "No past frames found." ); + bContinue = FALSE; + break; + } + + // Since we're walking from the most recent to least recent frame, + // This one is a valid "future" frame. + DBG_TRACE( "Adding 'future frame' to the list" ); + PushCloneList(SGEntry); + + // If the presentation time is less than the trigger time, stop here + // and use this as the first triggered frame. + if((ULONGLONG)(SGEntry->CloneEntry->StreamHeader->PresentationTime.Time) < m_TriggerTime) + { + DBG_TRACE( "First matching frame time=0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + break; + } + + // Watch out! We might actually need to pick up multiple frames since + // in theory we could have generated several since the trigger time. + } + } + + // Grab N past frames from the queue, if we have them. + while( bContinue && + !IsListEmpty(&m_ScatterGatherMappings) && + ulNumBuffers) + { + listEntry = RemoveTailList(&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + if(!(SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID) ) + { + InsertTailList(&m_ScatterGatherMappings, listEntry); + m_ScatterGatherMappingsQueued++; + DBG_TRACE( "No more past frames found." ); + bContinue = FALSE; + } + else + { + PushCloneList(SGEntry); + ulNumBuffers--; + DBG_TRACE( "Past frame #%d found (%p)", ulNumBuffers, SGEntry->CloneEntry->StreamHeader ); + } + } + + // If we got all of the past frames we needed and didn't consume the entire queue... + if(bContinue && (m_ScatterGatherMappingsQueued > 0)) + { + //Mark the next tail as Time = 0 so that we don't output any super old frames. + listEntry = RemoveTailList(&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + // Mark the PTS as invalid. + SGEntry->CloneEntry->StreamHeader->PresentationTime.Time = 0; + SGEntry->CloneEntry->StreamHeader->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + + InsertTailList(&m_ScatterGatherMappings, listEntry); + m_ScatterGatherMappingsQueued++; + } + + CompleteCloneList(); + + m_bPastBufferTrigger = FALSE; + + DBG_LEAVE("()"); + + return STATUS_SUCCESS; + +} + +// +// Put an item onto the history list. +// +void +CImageHardwareSimulation:: +PushCloneList( + _Inout_ PSCATTER_GATHER_ENTRY SGEntry +) +{ + PAGED_CODE(); + + DBG_TRACE( "Frame %p, PresentationTime=0x%016llX", + SGEntry->CloneEntry->StreamHeader, + SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + + InsertHeadList( &m_BurstList, &SGEntry->ListEntry ); +} + +// +// Complete the history list. +// +NTSTATUS +CImageHardwareSimulation:: +CompleteCloneList() +{ + PAGED_CODE(); + + int i = 0; + while(!IsListEmpty(&m_BurstList)) + { + LIST_ENTRY *listEntry = RemoveHeadList(&m_BurstList); + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + m_pClone = SGEntry->CloneEntry; + m_PhotoConfirmationInfo = SGEntry->PhotoConfirmationInfo; + + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + DBG_TRACE( "m_NumMappingsCompleted=%d, m_PhotoConfirmationInfo.isRequired()=%s", m_NumMappingsCompleted, m_PhotoConfirmationInfo.isRequired( )?"TRUE":"FALSE" ); + DBG_TRACE( "Frame %p, PresentationTime=0x%016llX", + SGEntry->CloneEntry->StreamHeader, + SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + + m_Sensor -> Interrupt (m_PinID); + + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast <PVOID> (SGEntry) + ); + i++; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CImageHardwareSimulation:: +SetClock(PKSPIN pin) +{ + PAGED_CODE(); + + if(!NT_SUCCESS(KsPinGetReferenceClockInterface(pin, &m_Clock))) + { + m_Clock = NULL; + } + + return STATUS_SUCCESS; +} + +void +CImageHardwareSimulation:: +SetTriggerTime( + _In_ ULONGLONG TriggerTime +) +/*++ + +Routine Description: + + Identify exactly when the user pressed that button. + +Arguments: + + TriggerTime - + The QPC time in 100ns when the user asked for the photo. + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + m_TriggerTime = TriggerTime; + DBG_TRACE( "Setting Trigger Time = 0x%016llX", TriggerTime ); +} + +NTSTATUS +CImageHardwareSimulation:: +Reset() +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + DBG_ENTER("(): m_PinID=%d", m_PinID); + + // Parent class reset first... + CHardwareSimulation::Reset(); + + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Reset Burst List" ); + + m_bTriggered = FALSE; + m_bEndOfSequence = FALSE; + m_pClone = NULL; + m_bPastBufferTrigger = FALSE; + m_TriggerTime = 0; + + DBG_LEAVE("(): m_PinID=%d", m_PinID); + + return STATUS_SUCCESS; +} + +NTSTATUS +CImageHardwareSimulation:: +SetFlashStatus( + _In_ ULONGLONG ullFlashStatus +) +{ + PAGED_CODE(); + + m_FlashStatus = ullFlashStatus; + + DBG_TRACE("FlashStatus=0x%016llX", ullFlashStatus); + + return STATUS_SUCCESS; +} + +// +// Program the Per Frame Settings for simulation +// We do it before calling start so we can be ready +// to program our simulation's hardware. +// +// Note: We make a local copy. +// +NTSTATUS +CImageHardwareSimulation:: +SetPFS( + _In_ ISP_FRAME_SETTINGS *pIspSettings, + _In_ ULONG FrameLimit, + _In_ ULONG LoopLimit +) +{ + PAGED_CODE(); + + // Set the current ISP settings and free any prior. + // This has to be done at simulation start or stop or we have + // a synchronization problem. + SAFE_DELETE_ARRAY( m_pIspSettings ); + + if( pIspSettings ) + { + m_pIspSettings = new (NonPagedPoolNx) ISP_FRAME_SETTINGS[FrameLimit]; + if( !m_pIspSettings ) + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory( m_pIspSettings, pIspSettings, FrameLimit*sizeof(ISP_FRAME_SETTINGS) ); + m_PfsLoopLimit = LoopLimit; + m_PfsFrameLimit = FrameLimit; + } + else + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + } + + return STATUS_SUCCESS; +} + +// Function: +// bool CImageHardwareSimulation::AdvanceFrameCounter(void) +// +// Description: +// Advance our frame and loop pointers to the next PFS settings. +// +// Parameters: +// [None] +// +// Returns: +// bool - true if we've reached the end of our Per Frame Settings. +// +bool +CImageHardwareSimulation:: +AdvanceFrameCounter(void) +{ + PAGED_CODE(); + + bool bEOS = false; + + DBG_ENTER( "()" ); + + m_GlobalFrameNumber ++; + DBG_TRACE( "m_GlobalFrameNumber=%lld", m_GlobalFrameNumber ); + + // + // Calculate the PFS frame & loop numbers, but only if we've + // gotten ISP settings. + // + if( m_bTriggered && + m_PinMode == PinBurstMode && + m_pIspSettings ) + { + m_PfsFrameNumber ++; + + if( m_PfsFrameNumber >= m_PfsFrameLimit ) + { + m_PfsFrameNumber = 0; + m_PfsLoopNumber ++; + } + + // Only mark EOS if we're not in an infinite loop. + if( m_PfsLoopLimit != 0 ) + { + NT_ASSERT( !(m_PfsLoopNumber > m_PfsLoopLimit) ); + // Check to see if we've hit our limit. + if( m_PfsLoopNumber >= m_PfsLoopLimit ) + { + DBG_TRACE( "Marking EOS" ); + bEOS = true; + } + } + } + + DBG_TRACE( "m_PfsFrameNumber=%d, m_PfsLoopNumber=%d", m_PfsFrameNumber, m_PfsLoopNumber ); + DBG_TRACE( "m_PfsFrameLimit=%d, m_PfsLoopLimit=%d", m_PfsFrameLimit, m_PfsLoopLimit ); + DBG_LEAVE( "() = %s", bEOS ? "true" : "false" ); + return bEOS; +} + + +// Function: +// bool CImageHardwareSimulation::IsPfsEOS(void) +// +// Description: +// Determine if we're at the EOS. +// +// Parameters: +// [None] +// +// Returns: +// bool - true if we've reached the end of our Per Frame Settings. +// +bool +CImageHardwareSimulation:: +IsPfsEOS(void) +{ + PAGED_CODE(); + + DBG_ENTER( "()" ); + DBG_TRACE( "m_bTriggered=%s, m_bEndOfSequence=%s, m_PinMode=%d, m_pIspSetting=0x%p", + ( m_bTriggered ? "true" : "false" ), + ( m_bEndOfSequence ? "true" : "false" ), + m_PinMode, + m_pIspSettings ); + + // + // Make sure we're in a Variable Photo Sequence. + // + bool result = + bool( m_bTriggered == TRUE && + m_bEndOfSequence && + m_PinMode == PinBurstMode && + m_pIspSettings ); // Must have ISP settings set to be PFS EOS. + + DBG_LEAVE( "() = %s", (result ? "true" : "false") ); + return result; +} + +// Function: +// BOOL CImageHardwareSimulation::IsPfsActive(void) +// +// Description: +// Determine if we're in a Variable Photo Sequence. +// +// Parameters: +// [None] +// +// Returns: +// BOOL - TRUE if we are actively processing Per Frame Settings. +// +BOOL +CImageHardwareSimulation:: +IsPfsActive(void) +{ + PAGED_CODE(); + + return + BOOL( m_bTriggered && !m_bEndOfSequence && + m_PinMode == PinBurstMode && + m_pIspSettings ) ; +} + +// Get the current frame settings. +// +// Note: +// Call this function to acquire ISP settings for the current frame's +// simulation. Initially we'll just use it to report back the ISP +// settings originally requested in the PFS by the user. +// +ISP_FRAME_SETTINGS * +CImageHardwareSimulation:: +GetIspSettings(void) +{ + PAGED_CODE(); + + return + IsPfsActive() + ? &m_pIspSettings[m_PfsFrameNumber] + : CHardwareSimulation::GetIspSettings() ; +} + +BOOLEAN +CImageHardwareSimulation:: +IsPhotoConfirmationNeeded() +/*++ + +Routine Description: + + Check flags for photo confirmation and return + whether driver should issue confirmation. + +--*/ +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + + return pSettings ? pSettings->bPhotoConfirmation : FALSE; +} |
