diff options
Diffstat (limited to 'AVStream/avscamera/sys/hwsim.cpp')
| -rw-r--r-- | AVStream/avscamera/sys/hwsim.cpp | 1409 |
1 files changed, 1409 insertions, 0 deletions
diff --git a/AVStream/avscamera/sys/hwsim.cpp b/AVStream/avscamera/sys/hwsim.cpp new file mode 100644 index 00000000..f00a4684 --- /dev/null +++ b/AVStream/avscamera/sys/hwsim.cpp @@ -0,0 +1,1409 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + hwsim.cpp + + Abstract: + + This file contains the implementation of CHardwareSimulation used to + simulate a camera's capture stream. + + Our simulation cannot mimic a real camera with any precision. A real + camera would likely have dedicated hardware to provide interrupts, do + DMA transfers of frames from an internal buffer as well as do scaling + and on-the-fly colorspace and planar format conversions so as to supply + frames for preview, video and photo in varying resolutions and + colorspaces. + + This simulation does not have such hardware, so instead it synthesizes + a frame on the fly for each local pin as required. The frame is + synthesized from common state information, so the images produced + should be comparable from pin to pin. CHardwareSimulation is used as + a base class for each simulated pin / hardware. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +inline +CHwSimTimer:: +CHwSimTimer( + _In_ CHardwareSimulation *Parent +) + : KPassiveTimer( Parent->m_Sensor->GetDeviceObject() ) + , m_Parent( Parent ) +/*++ + +Routine Description: + + CHwSimTimer object construction. + +Arguments: + + Parent - + The parent CHardwareSimulation. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); +} + +void +CHwSimTimer::Handler( + _In_opt_ PVOID Context +) +/*++ + +Routine Description: + + Punts to the HardwareSim... + +Arguments: + + Context - + Our parent CHardwareSimulation. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + ((CHardwareSimulation *) Context)->FakeHardware(); +} + +/*************************************************/ + +CHardwareSimulation:: +CHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : m_Sensor (Sensor) + , m_IsrTimer(this) + , m_ScatterGatherMappingsMax (SCATTER_GATHER_MAPPINGS_MAX) + , m_TimePerFrame(ONESECOND/30) + , m_PinID(PinID) + , m_Synthesizer(nullptr) + , m_Width(0) + , m_Height(0) + , m_ImageSize(0) + , m_PinState(PinStopped) + , m_NumMappingsCompleted(0) + , m_ScatterGatherMappingsQueued(0) + , m_ScatterGatherBytesQueued(0) + , m_NumFramesSkipped(0) + , m_InterruptTime(0) + , m_LastReportedExposureTime(DEF_EXPOSURE_TIME) // Assume the default exposure time for now. + , m_LastReportedWhiteBalance(0) + , m_FaceDetectionDelay(1) // Start out reporting immediately. + +/*++ + +Routine Description: + + CHardwareSimulation Construction. + +Arguments: + + Sensor - + The parent CSensor object that contains state for the controls. + PinID - + The pin index. + +Return Value: + + None + +--*/ +{ + + PAGED_CODE(); + + // + // Initialize the timer's, and locks necessary to simulate this hardware. + // + InitializeListHead (&m_ScatterGatherMappings); + + // + // Initialize the entry lookaside. + // + ExInitializeNPagedLookasideList ( + &m_ScatterGatherLookaside, + NULL, + NULL, + POOL_NX_ALLOCATION, + sizeof (SCATTER_GATHER_ENTRY), + 'nEGS', + 0 + ); + + m_StartTime.QuadPart = 0; +} + +CHardwareSimulation:: +~CHardwareSimulation() +{ + PAGED_CODE(); + + // Cancel the timer, just in case its running. + m_IsrTimer.Cancel(); + + // + // Delete the scatter / gather lookaside list. + // + ExDeleteNPagedLookasideList (&m_ScatterGatherLookaside); +} + +/*************************************************/ + +BOOLEAN +CHardwareSimulation:: +Initialize() + +/*++ + +Routine Description: + + Initialize the hardware simulation + +Arguments: + + [none] + +Return Value: + + TRUE - CHardwareSimulation is fully initialized. + +--*/ + +{ + PAGED_CODE(); + + return m_IsrTimer.IsValid() ; +} + +/*************************************************/ + +NTSTATUS +CHardwareSimulation:: +Start ( + IN CSynthesizer *ImageSynth, + IN LONGLONG TimePerFrame, + IN ULONG Width, + IN ULONG Height, + IN ULONG ImageSize +) + +/*++ + +Routine Description: + + Start the hardware simulation. This will kick the interrupts on, + begin issuing DPC's, filling in capture information, etc... + 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. + + TimePerFrame - + The time per frame... we issue interrupts this often. + + 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. + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + + PAGED_CODE(); + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + NTSTATUS Status = STATUS_SUCCESS; + + m_Synthesizer = ImageSynth; + m_TimePerFrame = TimePerFrame; + m_ImageSize = ImageSize; + m_Height = Height; + m_Width = Width; + + m_NumMappingsCompleted = 0; + m_ScatterGatherMappingsQueued = 0; + m_NumFramesSkipped = 0; + m_InterruptTime = 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 ); + } + + return Status; +} + +/*************************************************/ + +NTSTATUS +CHardwareSimulation:: +Pause ( + BOOLEAN Pausing +) + +/*++ + +Routine Description: + + Pause the hardware simulation... When the hardware simulation is told + to pause, it stops stops the timer, but it does not reset the counters. + +Arguments: + + Pausing - + Indicates whether the hardware is pausing or not. + + TRUE - + Pause the hardware + + FALSE - + Unpause the hardware from a previous pause + + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + { + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + if (Pausing && m_PinState == PinRunning) + { + // + // If we were running, stop completing mappings, etc... + // + m_PinState = PinPaused; + } + else if (!Pausing && m_PinState == PinPaused) + { + // + // For unpausing the hardware, we need to compute the relative time + // and restart interrupts. + // + LARGE_INTEGER UnpauseTime; + + KeQuerySystemTime (&UnpauseTime); + m_InterruptTime = (ULONG) ( + (UnpauseTime.QuadPart - m_StartTime.QuadPart) / + m_TimePerFrame + ); + + UnpauseTime.QuadPart = m_StartTime.QuadPart + + (m_InterruptTime + 1) * m_TimePerFrame; + + m_PinState = PinRunning; + m_IsrTimer.Set( UnpauseTime ); + } + } + + // If we are pausing, it's best to stop the timer and not wait for another frame. + if( Pausing ) + { + m_IsrTimer.Cancel(); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CHardwareSimulation:: +Stop() + +/*++ + +Routine Description: + + Stop the hardware simulation... + + For us, stop the timer, free the synthesizer and flush the queue. + +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); + + BOOLEAN bCancel = FALSE; + + { + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + bCancel = (m_PinState == PinRunning); + m_PinState = PinStopped; + + // Free the synthesis buffer. + m_Synthesizer->Destroy(); + + // + // Free S/G buffer + // + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Stop SG List" ); + + m_ScatterGatherMappingsQueued=0; + m_NumMappingsCompleted = 0; + m_ScatterGatherBytesQueued = 0; + } + + // If running, stop the timer to make sure we don't try to deliver one last frame. + if( bCancel ) + { + m_IsrTimer.Cancel(); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CHardwareSimulation:: +Reset() +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + DBG_ENTER("(): m_PinID=%d", m_PinID); + + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Reset SG List" ); + + m_ScatterGatherMappingsQueued=0; + m_NumMappingsCompleted = 0; + m_ScatterGatherBytesQueued = 0; + + DBG_LEAVE("(): m_PinID=%d", m_PinID); + return STATUS_SUCCESS; +} + +NTSTATUS +CHardwareSimulation:: +Reset (IN PKSPIN Pin) +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + if(Pin->ResetState == KSRESET_END) + { + KsPinAttemptProcessing(Pin, TRUE); + } + else + { + status = Reset(); + } + + return status; +} + +ULONG +CHardwareSimulation:: +ReadNumberOfMappingsCompleted() + +/*++ + +Routine Description: + + Read the number of scatter / gather mappings which have been + completed since the last reset of the simulated hardware + +Arguments: + + None + +Return Value: + + Total number of completed mappings. + +--*/ + +{ + PAGED_CODE(); + + return m_NumMappingsCompleted; +} + +/*************************************************/ + + +ULONG +CHardwareSimulation:: +ProgramScatterGatherMappings ( + IN PKSSTREAM_POINTER *Clone, + IN PUCHAR *Buffer, + IN PKSMAPPING Mappings, + IN ULONG MappingsCount, + IN ULONG MappingStride +) + +/*++ + +Routine Description: + + Add a single entry to our queue. In practice our simulation can only + accept one frame at a time, but we still base this code off of the + avshws sample. + +Arguments: + + Buffer - + The virtual address of the buffer mapped by the mapping list + + ClonePointer + The KSStreamClone Pointer to be associated with the entry item + + Mappings - + The KSMAPPINGS array corresponding to the buffer + + MappingsCount - + The number of mappings in the mappings array + + MappingStride - + The mapping stride used in initialization of AVStream DMA + +Return Value: + + Number of mappings actually inserted. + +--*/ + +{ + PAGED_CODE(); + + ULONG MappingsInserted = 0; + + DBG_ENTER("(Clone=%p, Buffer=%p, MappingsCount=%d)", + Clone, Buffer, MappingsCount); + + // + // Protect our S/G list with a spinlock. + // + KScopedMutex Lock( m_ListLock ); + + // + // Loop through the scatter / gather list and break the buffer up into + // chunks equal to the scatter / gather mappings. Stuff the virtual + // addresses of these chunks on a list somewhere. We update the buffer + // pointer the caller passes as a more convenient way of doing this. + // + // If I could just remap physical in the list to virtual easily here, + // I wouldn't need to do it. + // + do + { + PSCATTER_GATHER_ENTRY Entry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + ExAllocateFromNPagedLookasideList ( + &m_ScatterGatherLookaside + ) + ); + + if (!Entry) + { + break; + } + + Entry ->CloneEntry = nullptr; + Entry -> Virtual = nullptr; + Entry -> ByteCount = 0; + + + Entry ->CloneEntry = *Clone; + Entry -> CloneEntry -> StreamHeader -> PresentationTime.Time = 0; + Entry -> CloneEntry -> StreamHeader -> OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + + Entry -> Virtual = *Buffer; + Entry -> ByteCount = MappingsCount; + Entry->PhotoConfirmationInfo = PHOTOCONFIRMATION_INFO(); + + // + // Move forward a specific number of bytes in chunking this into + // mapping sized va buffers. + // + *Buffer += MappingsCount; + Mappings = reinterpret_cast <PKSMAPPING> ( + (reinterpret_cast <PUCHAR> (Mappings) + MappingStride) + ); + + InsertTailList (&m_ScatterGatherMappings, &(Entry -> ListEntry)); + m_ScatterGatherMappingsQueued++; + MappingsInserted = MappingsCount; + DBG_TRACE("m_ScatterGatherMappingsQueued=%d", m_ScatterGatherMappingsQueued); + m_ScatterGatherBytesQueued += MappingsCount; + + } + while(FALSE); + + DBG_LEAVE(" MappingsInserted=%d", MappingsInserted ); + + return MappingsInserted; + +} + +/************************************************************************** + + The following are helper functions used in the production of metadata. + Since this is just a simulation, most "auto" modes get converted into + a random value that is in range. + + While simple, this still requires writing a helper function for each + piece of data. + +**************************************************************************/ + +// +// Translate Exposure settings into a "current value". +// +LONGLONG +CHardwareSimulation:: +GetCurrentExposureTime() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + LONGLONG Value = 0; + LPCSTR Mode = "[UNKNOWN]"; + + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + // Get random value in global setting's bound (LONG) + // I'm abandoning the reported min/max and using something more reasonable. + Value = GetRandom( MIN_EXPOSURE_TIME*5, DEF_EXPOSURE_TIME*5 ); + Mode = "KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO"; + } + + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Value = pSettings->ExposureSetting.VideoProc.Value.ll; + Mode = "KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL"; + } + + // Locked just reports the last value set... + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + Value = m_LastReportedExposureTime; + Mode = "KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK"; + } + + DBG_TRACE("ExposureMode=0x%016llX (%s), Time=%llu00ns, LastReported=%llu00ns", + pSettings->ExposureMode, Mode, Value, m_LastReportedExposureTime ); + + m_LastReportedExposureTime = Value; + return Value; +} + +// +// Translate White Balance into a "current value". +// +ULONG +CHardwareSimulation:: +GetCurrentWhiteBalance() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + ULONG Value = 0; + + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Value = (ULONG) GetRandom( pSettings->WhiteBalanceSetting.Min, pSettings->WhiteBalanceSetting.Max ); + } + + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Value = pSettings->WhiteBalanceSetting.VideoProc.Value.ul; + } + + // Locked just reports the last value set... + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + Value = m_LastReportedWhiteBalance; + } + + m_LastReportedWhiteBalance= Value; + return Value; +} + +// +// Translate ISO settings into a "current value". +// +ULONG +CHardwareSimulation:: +GetCurrentISOSpeed() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + ULONG Value = 0; + + if( pSettings->ISOMode & KSCAMERA_EXTENDEDPROP_ISO_AUTO ) + { + Value = GetRandom( (ULONG) 50, (ULONG) 3200 ); + } + else if( pSettings->ISOMode & KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + Value = pSettings->ISOValue; + } + else + { + Value = // Try converting any legacy presets to a manual value. + IsoPreset2Value( pSettings->ISOMode ); + } + return Value; +} + +// +// Translate Exposure Mode to an Exif Exposure Program value +// +USHORT +CHardwareSimulation:: +GetExposureProgram() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + USHORT Value = 0; // Undefined. + + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Value = 1; // Manual + } + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Value = 2; // Normal + } + + // TODO: Extend SceneMode to ISP settings so I can use it here to capture Portrait mode, etc. + return Value; +} + +/*************************************************/ + +// +// Emit metadata here that is common to all pins. +// +// All derived classes' EmitMetadata() function must +// call this function first. +// +void +CHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +{ + PAGED_CODE(); + + NT_ASSERT(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); + //PBYTE pData = (PBYTE) pMetadata->SystemVa; + //ULONG BytesLeft = pMetadata->BufferSize; + + // A real driver might write focus state or other info here. We've got nothing. + pMetadata->UsedSize = 0; + } + else + { + DBG_TRACE("Metadata not present..."); + } +} + +void +CHardwareSimulation:: +EmitFaceMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader, + _In_ ULONG Count, + _In_ ULONGLONG Flags, + _In_ ULONG DelayLimit +) +/*++ + +Routine Description: + + Helper function that emits FaceDetection metadata. + + Note: This function assumes a metadata buffer is present. + +Arguments: + + pStreamHeader - + The frame's stream header. + + Count - + The maximum number of faces to detect. + + Flags - + A combination of the KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK and + KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK flags passed to the face + detection control. Used to decide what characterization data to + present in the metadata. + + DelayLimit - + The maximum number of frames to delay before generating the next + face detection. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + NT_ASSERT(Count <= SIZEOF_ARRAY(m_LastFaceDetect.Data)); + + // Do nothing if we're turned on, but they want no faces. + if( Count==0 ) + { + return; + } + + if( --m_FaceDetectionDelay != 0 || // Count down our random delay for new face data. + Count > m_LastFaceDetect.Count ) // Make sure we never emit more faces than requested. + { + ULONG CountReported = GetRandom( 0, Count ); + + DBG_TRACE("Generating new faces (Count=%d, Flags=0x%016llX, Delay=%d)...", + Count, Flags, DelayLimit); + + // Synthesize a new set of "detected" faces... + m_LastFaceDetect.Header.MetadataId = (ULONG) MetadataId_Custom_FaceDetection; + m_LastFaceDetect.Header.Size = + sizeof(CAMERA_METADATA_FACEHEADER) + (sizeof(METADATA_FACEDATA) * CountReported); + + m_LastFaceDetect.Count = CountReported; + m_LastFaceDetect.Flags = Flags; + m_LastFaceDetect.Timestamp = pStreamHeader->PresentationTime.Time ; + + DBG_TRACE("Count=%d, Flags=0x%016llX, Time=0x%016llX", + m_LastFaceDetect.Count, + m_LastFaceDetect.Flags, + m_LastFaceDetect.Timestamp ); + + // Report face data... + for( ULONG i=0; i<CountReported; i++ ) + { + RtlZeroMemory( &m_LastFaceDetect.Data[i], sizeof(m_LastFaceDetect.Data[i]) ); + + RECT &rect = m_LastFaceDetect.Data[i].Region; + rect.top = GetRandom((LONG)0, TO_Q31(1)-2); + rect.left = GetRandom((LONG)0, TO_Q31(1)-2); + rect.bottom = GetRandom(rect.top+1, TO_Q31(1)-1); + rect.right = GetRandom(rect.left+1, TO_Q31(1)-1); + + m_LastFaceDetect.Data[i].confidenceLevel = GetRandom((ULONG)25, 100); // Don't bother reporting really low confidence faces. + + DBG_TRACE(" #%d: [(0x%08X,0x%08X), (0x%08X,0x%08X)](Conf=%d%%)", + i, + rect.top, rect.left, rect.bottom, rect.right, + m_LastFaceDetect.Data[i].confidenceLevel); + + if( Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK ) + { + m_LastFaceDetect.Data[i].BlinkScoreLeft = GetRandom((ULONG)0, 100); // Should really skew these numbers more in favor of no blink. + m_LastFaceDetect.Data[i].BlinkScoreRight = GetRandom((ULONG)0, 100); + + DBG_TRACE(" --- Blink(left=%d%%, right=%d%%)", + m_LastFaceDetect.Data[i].BlinkScoreLeft, + m_LastFaceDetect.Data[i].BlinkScoreRight); + } + + if( Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_SMILE ) + { + m_LastFaceDetect.Data[i].FacialExpression = GetRandom((ULONG)0, 1) ? EXPRESSION_SMILE : 0; + m_LastFaceDetect.Data[i].FacialExpressionScore = GetRandom((ULONG)0, 100); + + DBG_TRACE(" --- Smile(exp=%s, score=%d%%)", + (m_LastFaceDetect.Data[i].FacialExpression==EXPRESSION_SMILE ? "Smile":"[unknown]"), + m_LastFaceDetect.Data[i].FacialExpressionScore); + } + } + + // Randomly choose when to generate new facedetection data. + m_FaceDetectionDelay = GetRandom( 1, DelayLimit ); + DBG_TRACE("Delay to next is %d", m_FaceDetectionDelay); + } + + // The flags can change between face detection updates. + // Drop the additional characterization data immediately, if requested. + // We can hardly add it that fast since it might not be generated straight away. + ULONGLONG LostFlags = m_LastFaceDetect.Flags & ~Flags; + for( ULONG i=0; i<m_LastFaceDetect.Count; i++ ) + { + if( LostFlags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK ) + { + m_LastFaceDetect.Data[i].BlinkScoreLeft = 0; + m_LastFaceDetect.Data[i].BlinkScoreRight = 0; + } + if( LostFlags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_SMILE ) + { + m_LastFaceDetect.Data[i].FacialExpression = 0; + m_LastFaceDetect.Data[i].FacialExpressionScore = 0; + } + } + m_LastFaceDetect.Flags &= Flags; + + PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO)(pStreamHeader + 1); + PKSSTREAM_METADATA_INFO pMetadata = (PKSSTREAM_METADATA_INFO) (pFrameInfo + 1); + ULONG BytesLeft = pMetadata->BufferSize - pMetadata->UsedSize; + + // Write Face Detection Info here + // Note: If we derived Video and Preview from a common base class, we + // might use that to emit this information for both types. + if( BytesLeft >= m_LastFaceDetect.Header.Size ) + { + // Always write the most recently generated face detection info. + RtlCopyMemory( + ((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize, + &m_LastFaceDetect, + m_LastFaceDetect.Header.Size ); + + DBG_TRACE("Emitting Face metadata..."); + + BytesLeft -= m_LastFaceDetect.Header.Size; + pMetadata->UsedSize += m_LastFaceDetect.Header.Size; + } + else + { + DBG_TRACE("WARNING: Out of metadata buffer..."); + } +} + +// +// PIN_STATE text for debug output. +// +static +const char * +GetPinStateTxt( PIN_STATE state ) +{ + PAGED_CODE(); + + switch( state ) + { + case PinStopped: + return "STOPPED"; + case PinPaused: + return "PAUSED"; + case PinRunning: + return "RUNNING"; + } + return "[UNKNOWN]"; +} + +NTSTATUS +CHardwareSimulation:: +FillScatterGatherBuffers() + +/*++ + +Routine Description: + + The hardware has synthesized a buffer and we're to fill a frame buffer. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("() m_PinState=%s, m_PinID=0x%08X, m_ImageSize=0x%08X, m_ScatterGatherMappingsQueued=%u, " + "m_ScatterGatherBytesQueue=0x%08X", + GetPinStateTxt(m_PinState), + m_PinID, m_ImageSize, m_ScatterGatherMappingsQueued, m_ScatterGatherBytesQueued); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ULONG BufferRemaining = m_ImageSize; + + // + // If there isn't a frame buffer queued, we justskip the frame and consider + // it starvation. + // + while (BufferRemaining && + !IsListEmpty(&m_ScatterGatherMappings) && + m_ScatterGatherBytesQueued >= BufferRemaining) + { + 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 Stride = 0; + if ( SGEntry->CloneEntry -> StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) + + sizeof (KS_FRAME_INFO)) + { + PKS_FRAME_INFO FrameInfo = reinterpret_cast <PKS_FRAME_INFO> (SGEntry->CloneEntry->StreamHeader+1); + Stride = (ULONG) ABS(FrameInfo->lSurfacePitch); + } + + // Have the synthesizer output a frame to the buffer. + ULONG BytesCopied = m_Synthesizer->DoCommit( SGEntry->Virtual, SGEntry->ByteCount, Stride ); + NT_ASSERT( BytesCopied ); + DBG_TRACE( "BytesCopied = %d", BytesCopied ); + + //Adding time stamp + if(m_PhotoConfirmationEntry.isRequired()) + { + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = + m_PhotoConfirmationEntry.getTime(); + DBG_TRACE("Using Photo Confirmation time = 0x%016llX", m_PhotoConfirmationEntry.getTime()); + } + else + { + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = ConvertQPCtoTimeStamp(NULL); + DBG_TRACE("PresentationTime = 0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + } + + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Numerator = + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Denominator = 1; + + SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID ; + + // Add metadata to the sample. + EmitMetadata( SGEntry -> CloneEntry -> StreamHeader ); + + BufferRemaining = 0; //-= BytesCopied; + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + // + // Release the scatter / gather entry back to our lookaside. + // + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast <PVOID> (SGEntry) + ); + + } + + // Report an error if we used the last buffer. + if (BufferRemaining) + { + //DBG_TRACE("BufferRemaining=%u", BufferRemaining); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + DBG_LEAVE("() = 0x%08X", ntStatus); + return ntStatus; +} + +/************************************************************************** + + Debug helpers + +**************************************************************************/ + +PCCHAR +DVS_Text( ULONGLONG Flags ) +{ + PAGED_CODE(); + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_OFF: return "Off"; + case KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_ON: return "On"; + case KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_AUTO: return "Auto"; + default: + { + static + CHAR buffer[32]; + + RtlStringCbPrintfA(buffer, sizeof(buffer), "Unknown [0x%016llX]", Flags); + return (PCCHAR) buffer; + } + } +} + +PCCHAR +OIS_Text( ULONGLONG Flags ) +{ + PAGED_CODE(); + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_OIS_OFF: return "Off"; + case KSCAMERA_EXTENDEDPROP_OIS_ON: return "On"; + case KSCAMERA_EXTENDEDPROP_OIS_AUTO: return "Auto"; + default: + { + static + CHAR buffer[32]; + + RtlStringCbPrintfA(buffer, sizeof(buffer), "Unknown [0x%016llX]", Flags); + return (PCCHAR) buffer; + } + } +} + +/**************************************************************************/ + +void +CHardwareSimulation:: +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(); + + 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) + { + // + // Generate a "time stamp" just to overlay it onto the capture image. + // It makes it more exciting than bars that do nothing. + // + ULONGLONG time = ConvertQPCtoTimeStamp(NULL); + DBG_TRACE("QPC=0x%016llX", time); + + m_Synthesizer->SetFrameNumber( m_InterruptTime ); + m_Synthesizer->SetRelativePts( (m_InterruptTime + 1) * m_TimePerFrame ); + m_Synthesizer->SetQpcTime( time ); + + m_Synthesizer->DoSynthesize(); + + CHAR Text[64]; + + CExtendedProperty Control; + m_Sensor->GetVideoStabilization( &Control ); + RtlStringCbPrintfA(Text, sizeof(Text), "DVS: %s", DVS_Text(Control.Flags)); + m_Synthesizer->OverlayText( 0, m_Height-38, 1, Text, TRANSPARENT, TEXT_COLOR ); + + m_Sensor->GetOpticalImageStabilization( &Control ); + RtlStringCbPrintfA(Text, sizeof(Text), "OIS: %s", OIS_Text(Control.Flags)); + m_Synthesizer->OverlayText( 0, m_Height-48, 1, Text, TRANSPARENT, TEXT_COLOR ); + + // + // 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 ); + } +} + +ULONGLONG +CHardwareSimulation:: +ConvertQPCtoTimeStamp( + _In_opt_ PLARGE_INTEGER pQpcTime +) +/*++ + +Routine Description: + + Get the QPC measured in 100ns units. + +Arguments: + + pQpcTime - + An optional time value to convert. If NULL, use the current QPC. + +Return Value: + + ULONLONG - + The time in 100ns increments. + +--*/ +{ + PAGED_CODE(); + + LARGE_INTEGER qpcTime = {0}; + LARGE_INTEGER qpcFrequency; + + qpcTime = KeQueryPerformanceCounter(&qpcFrequency); + + if( pQpcTime ) + { + qpcTime = *pQpcTime; + } + + return KSCONVERT_PERFORMANCE_TIME( qpcFrequency.QuadPart, qpcTime ); +} + +NTSTATUS +CHardwareSimulation:: +GeneratePhotoConfirmation( + _In_ ULONG PfsFrameNumber, + _In_ LONGLONG time +) +/*++ + +Routine Description: + + So a photo sim can ask a preview sim for a confirmation frame. + +Arguments: + + PfsFrameNumber - + The VPS shot number. + time - + The precise time of the associated photo. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + + if( PinRunning != m_PinState ) + { + return STATUS_DEVICE_NOT_READY; + } + + DBG_ENTER("( Index=%d, Time=0x%016llX )", PfsFrameNumber, time ); + + m_PhotoConfirmationEntry = PHOTOCONFIRMATION_INFO( PfsFrameNumber, time ); + + // We basically used the previously synthesized preview image + NTSTATUS status = FillScatterGatherBuffers(); + + //Clean up the photo confiramtion flag + m_PhotoConfirmationEntry = PHOTOCONFIRMATION_INFO(); + + if(NT_SUCCESS(status)) + { + m_Sensor->Interrupt (m_PinID); + } + + DBG_LEAVE("() = 0x%08X", status); + + return status; +} + +void +CHardwareSimulation:: +FreeSGEntry( + _In_ PLIST_ENTRY Entry, + _In_ PCWSTR EventName, + _In_opt_ LPCGUID Activity, + _In_ ULONG DataUsed +) +{ + PAGED_CODE(); + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast <PSCATTER_GATHER_ENTRY> ( + CONTAINING_RECORD ( + Entry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + NT_ASSERT(SGEntry->CloneEntry); + NT_ASSERT(SGEntry->CloneEntry->StreamHeader); + + SGEntry->CloneEntry->StreamHeader->DataUsed = DataUsed; + + KsStreamPointerDelete(SGEntry->CloneEntry); + + // + // Release the scatter / gather entry back to our lookaside. + // + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + SGEntry + ); +} + +void +CHardwareSimulation:: +FreeSGList( + _In_ PLIST_ENTRY List, + _In_ PCWSTR EventName, + _In_opt_ LPCGUID Activity +) +{ + PAGED_CODE(); + + while(!IsListEmpty(List)) + { + PLIST_ENTRY head = RemoveHeadList(List); + m_ScatterGatherMappingsQueued--; + FreeSGEntry( head, EventName, Activity ); + } +} + +// +// Get the current frame settings. +// +ISP_FRAME_SETTINGS * +CHardwareSimulation:: +GetIspSettings(void) +{ + PAGED_CODE(); + + return m_Sensor->GetGlobalIspSettings(); +} |
