//==========================================================================; // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) 1992 - 1999 Microsoft Corporation. All Rights Reserved. // //==========================================================================; #include "strmini.h" #include "ksmedia.h" #include "capmain.h" #include "capdebug.h" #include "vbixfer.h" #include "ntstatus.h" /* ** VBICaptureRoutine() ** ** Routine to generate video frames based on a timer. ** ** Note: Devices capable of using interrupts should always ** trigger capture on a VSYNC interrupt, and not use a timer. ** ** Arguments: ** ** Returns: nothing ** ** Side Effects: none */ VOID STREAMAPI VBICaptureRoutine( IN PSTREAMEX pStrmEx ) { PHW_DEVICE_EXTENSION pHwDevExt = pStrmEx->pHwDevExt; int StreamNumber = pStrmEx->pStreamObject->StreamNumber; PKSSTREAM_HEADER pDataPacket; PKS_VBI_FRAME_INFO pVBIFrameInfo; // If we're stopped and the timer is still running, just return. // This will stop the timer. if (pStrmEx->KSState == KSSTATE_STOP) { return; } // Find out what time it is, if we're using a clock if (pStrmEx->hMasterClock) { HW_TIME_CONTEXT TimeContext; TimeContext.HwDeviceExtension = pHwDevExt; TimeContext.HwStreamObject = pStrmEx->pStreamObject; TimeContext.Function = TIME_GET_STREAM_TIME; StreamClassQueryMasterClockSync ( pStrmEx->hMasterClock, &TimeContext); pStrmEx->QST_StreamTime = TimeContext.Time; pStrmEx->QST_Now = TimeContext.SystemTime; if (pStrmEx->QST_NextFrame == 0) { pStrmEx->QST_NextFrame = pStrmEx->QST_StreamTime + pStrmEx->pVBIStreamFormat->ConfigCaps.MinFrameInterval; } #ifdef CREATE_A_FLURRY_OF_TIMING_SPEW DbgLogTrace(("TestCap: Time=%16lx\n", TimeContext.Time)); DbgLogTrace(("TestCap: SysTime=%16lx\n", TimeContext.SystemTime)); #endif } // Only capture in the RUN state if (pStrmEx->KSState == KSSTATE_RUN) { // // Determine if it is time to capture a frame based on // how much time has elapsed since capture started. // If there isn't a clock available, then capture immediately. // if ((!pStrmEx->hMasterClock) || (pStrmEx->QST_StreamTime >= pStrmEx->QST_NextFrame)) { PHW_STREAM_REQUEST_BLOCK pSrb; // Increment the picture count (usually this is VSYNC count) pStrmEx->VBIFrameInfo.PictureNumber++; // // Get the next queue SRB (if any) // pSrb = VideoQueueRemoveSRB (pHwDevExt, StreamNumber); if (pSrb) { pDataPacket = pSrb->CommandData.DataBufferArray; pVBIFrameInfo = (PKS_VBI_FRAME_INFO)(pDataPacket + 1); pStrmEx->VBIFrameInfo.dwFrameFlags = 0; // // If needed, send out VBIInfoHeader // if (!(pStrmEx->SentVBIInfoHeader)) { pStrmEx->SentVBIInfoHeader = 1; pStrmEx->VBIFrameInfo.dwFrameFlags |= KS_VBI_FLAG_VBIINFOHEADER_CHANGE; pStrmEx->VBIFrameInfo.VBIInfoHeader = StreamFormatVBI.VBIInfoHeader; } // Set additional info fields about the data captured such as: // Frames Captured // Frames Dropped // Field Polarity // Protection status // pStrmEx->VBIFrameInfo.ExtendedHeaderSize = pVBIFrameInfo->ExtendedHeaderSize; if (pStrmEx->VBIFrameInfo.PictureNumber & 1) pStrmEx->VBIFrameInfo.dwFrameFlags |= KS_VBI_FLAG_FIELD1; else pStrmEx->VBIFrameInfo.dwFrameFlags |= KS_VBI_FLAG_FIELD2; pStrmEx->VBIFrameInfo.dwFrameFlags |= pHwDevExt->ProtectionStatus & (KS_VBI_FLAG_MV_PRESENT |KS_VBI_FLAG_MV_HARDWARE |KS_VBI_FLAG_MV_DETECTED); *pVBIFrameInfo = pStrmEx->VBIFrameInfo; // Copy this into stream header so ring 3 filters can see it pDataPacket->TypeSpecificFlags = pVBIFrameInfo->dwFrameFlags; // Init the flags to zero pDataPacket->OptionsFlags = 0; // Set the discontinuity flag if frames have been previously // dropped, and then reset our internal flag if (pStrmEx->fDiscontinuity) { pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY; pStrmEx->fDiscontinuity = FALSE; } // // Return the timestamp for the frame // pDataPacket->PresentationTime.Numerator = 1; pDataPacket->PresentationTime.Denominator = 1; pDataPacket->Duration = pStrmEx->pVBIStreamFormat->ConfigCaps.MinFrameInterval; // // if we have a master clock AND this is a capture stream // if (pStrmEx->hMasterClock && (StreamNumber == STREAM_Capture || StreamNumber == STREAM_VBI)) { pDataPacket->PresentationTime.Time = pStrmEx->QST_StreamTime; pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID | KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; } else { // // No clock or not a capture stream, // so just mark the time as unknown // pDataPacket->PresentationTime.Time = 0; // clear the timestamp valid flags pDataPacket->OptionsFlags &= ~(KSSTREAM_HEADER_OPTIONSF_TIMEVALID | KSSTREAM_HEADER_OPTIONSF_DURATIONVALID); } // Every frame we generate is a key frame (aka SplicePoint) // Delta frames (B or P) should not set this flag pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_SPLICEPOINT; // // Call the routine which synthesizes images // VBI_ImageSynth(pSrb); // Output a frame count every 300th frame (~5 sec) in Debug mode if (pStrmEx->VBIFrameInfo.PictureNumber % 300 == 0) { DbgLogInfo(("TestCap: Picture %u, Stream=%d\n", (unsigned int)pStrmEx->VBIFrameInfo.PictureNumber, StreamNumber)); } CompleteStreamSRB(pSrb); } // if we have an SRB else { // // No buffer was available when we should have captured one // Increment the counter which keeps track of // dropped frames pStrmEx->VBIFrameInfo.DropCount++; // Set the (local) discontinuity flag // This will cause the next packet processed to have the // KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY flag set. pStrmEx->fDiscontinuity = TRUE; } // Figure out when to capture the next frame pStrmEx->QST_NextFrame += pStrmEx->pVBIStreamFormat->ConfigCaps.MinFrameInterval; } // endif time to capture a frame } // endif we're running } /* ** VBIhwCaptureRoutine() ** ** Routine to capture video frames based on a timer. ** ** Notes: * Devices capable of using interrupts should always trigger ** capture on a VSYNC interrupt, and not use a timer. ** * This routine is used by VBI streams which do NOT have extended ** headers, such as CC and NABTS. ** ** Arguments: ** ** Returns: nothing ** ** Side Effects: none */ VOID STREAMAPI VBIhwCaptureRoutine( IN PSTREAMEX pStrmEx ) { PHW_DEVICE_EXTENSION pHwDevExt = pStrmEx->pHwDevExt; int StreamNumber = pStrmEx->pStreamObject->StreamNumber; PKSSTREAM_HEADER pDataPacket; // If we're stopped and the timer is still running, just return. // This will stop the timer. if (pStrmEx->KSState == KSSTATE_STOP) { return; } // Find out what time it is, if we're using a clock if (pStrmEx->hMasterClock ) { HW_TIME_CONTEXT TimeContext; TimeContext.HwDeviceExtension = pHwDevExt; TimeContext.HwStreamObject = pStrmEx->pStreamObject; TimeContext.Function = TIME_GET_STREAM_TIME; StreamClassQueryMasterClockSync ( pStrmEx->hMasterClock, &TimeContext); pStrmEx->QST_StreamTime = TimeContext.Time; pStrmEx->QST_Now = TimeContext.SystemTime; if (pStrmEx->QST_NextFrame == 0) { pStrmEx->QST_NextFrame = pStrmEx->QST_StreamTime + pStrmEx->pVBIStreamFormat->ConfigCaps.MinFrameInterval; } #ifdef CREATE_A_FLURRY_OF_TIMING_SPEW DbgLogTrace(("TestCap: Time=%16lx\n", TimeContext.Time)); DbgLogTrace(("TestCap: SysTime=%16lx\n", TimeContext.SystemTime)); #endif } // Only capture in the RUN state if (pStrmEx->KSState == KSSTATE_RUN) { // // Determine if it is time to capture a frame based on // how much time has elapsed since capture started. // If there isn't a clock available, then capture immediately. // if ((!pStrmEx->hMasterClock) || (pStrmEx->QST_StreamTime >= pStrmEx->QST_NextFrame)) { PHW_STREAM_REQUEST_BLOCK pSrb; // Increment the picture count (usually this is VSYNC count) pStrmEx->VBIFrameInfo.PictureNumber++; // // Get the next queue SRB (if any) // pSrb = VideoQueueRemoveSRB (pHwDevExt, StreamNumber); if (pSrb) { pDataPacket = pSrb->CommandData.DataBufferArray; // Init the flags to zero pDataPacket->OptionsFlags = 0; // Set the discontinuity flag if frames have been previously // dropped, and then reset our internal flag if (pStrmEx->fDiscontinuity) { pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY; pStrmEx->fDiscontinuity = FALSE; } // // Return the timestamp for the frame // pDataPacket->PresentationTime.Numerator = 1; pDataPacket->PresentationTime.Denominator = 1; pDataPacket->Duration = pStrmEx->pVBIStreamFormat->ConfigCaps.MinFrameInterval; // // if we have a master clock AND this is the capture stream // if (pStrmEx->hMasterClock && (StreamNumber == 0)) { pDataPacket->PresentationTime.Time = pStrmEx->QST_StreamTime; pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID | KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; } else { // // No clock or the preview stream, // so just mark the time as unknown // pDataPacket->PresentationTime.Time = 0; // clear the timestamp valid flags pDataPacket->OptionsFlags &= ~(KSSTREAM_HEADER_OPTIONSF_TIMEVALID | KSSTREAM_HEADER_OPTIONSF_DURATIONVALID); } // Every frame we generate is a key frame (aka SplicePoint) // Delta frames (B or P) should not set this flag pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_SPLICEPOINT; // // Call the routine which synthesizes images // switch (StreamNumber) { case STREAM_NABTS: NABTS_ImageSynth(pSrb); break; case STREAM_CC: CC_ImageSynth(pSrb); break; default: case STREAM_VBI: DbgLogError(("TestCap::VBIhwCaptureRoutine: Bad stream %d\n", StreamNumber)); break; } CompleteStreamSRB (pSrb); } // if we have an SRB else { // // No buffer was available when we should have captured one // Increment the counter which keeps track of // dropped frames pStrmEx->VBIFrameInfo.DropCount++; // Set the (local) discontinuity flag // This will cause the next packet processed to have the // KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY flag set. pStrmEx->fDiscontinuity = TRUE; } // Figure out when to capture the next frame pStrmEx->QST_NextFrame += pStrmEx->pVBIStreamFormat->ConfigCaps.MinFrameInterval; } // endif time to capture a frame } // endif we're running } /* ** VBITimerRoutine() ** ** A timer has been created based on the requested capture interval. ** This is the callback routine for this timer event. ** ** Note: Devices capable of using interrupts should always ** trigger capture on a VSYNC interrupt, and not use a timer. ** ** Arguments: ** ** Context - pointer to the stream extension ** ** Returns: nothing ** ** Side Effects: none */ VOID STREAMAPI VBITimerRoutine( PVOID Context ) { PSTREAMEX pStrmEx = ((PSTREAMEX)Context); PHW_DEVICE_EXTENSION pHwDevExt = pStrmEx->pHwDevExt; int StreamNumber = pStrmEx->pStreamObject->StreamNumber; ULONG interval; // If we're stopped and the timer is still running, just return. // This will stop the timer. if (pStrmEx->KSState == KSSTATE_STOP) return; // Calculate next interval interval = (ULONG)(pStrmEx->pVBIStreamFormat->ConfigCaps.MinFrameInterval / 10); interval /= 2; // Run at 2x noted rate for accuracy // Capture a frame if it's time and we have a buffer switch (StreamNumber) { case STREAM_NABTS: VBIhwCaptureRoutine(pStrmEx); break; case STREAM_CC: VBIhwCaptureRoutine(pStrmEx); break; default: case STREAM_VBI: VBICaptureRoutine(pStrmEx); break; } // Schedule the next timer event StreamClassScheduleTimer ( pStrmEx->pStreamObject, // StreamObject pHwDevExt, // HwDeviceExtension interval, // Microseconds VBITimerRoutine, // TimerRoutine pStrmEx); // Context } /* ** VBISetState() ** ** Sets the current state for a given stream ** ** Arguments: ** ** pSrb - pointer to the stream request block for properties ** ** Returns: ** ** Side Effects: none */ VOID STREAMAPI VBISetState(PHW_STREAM_REQUEST_BLOCK pSrb) { PHW_DEVICE_EXTENSION pHwDevExt = pSrb->HwDeviceExtension; PSTREAMEX pStrmEx = pSrb->StreamObject->HwStreamExtension; int StreamNumber = pStrmEx->pStreamObject->StreamNumber; KSSTATE PreviousState; // // Remember the state we're transitioning away from // PreviousState = pStrmEx->KSState; // // Set the new state // pStrmEx->KSState = pSrb->CommandData.StreamState; switch (pSrb->CommandData.StreamState) { case KSSTATE_STOP: // // The stream class will cancel all outstanding IRPs for us // (but only if it is maintaining the queue ie. using Stream Class synchronization) // Since Testcap is not using Stream Class synchronization, we must clear the queue here VideoQueueCancelAllSRBs (pStrmEx); pStrmEx->SentVBIInfoHeader = 0; // Send out a fresh one next RUN DbgLogInfo(("TestCap: STATE Stopped, Stream=%d\n", StreamNumber)); break; case KSSTATE_ACQUIRE: // // This is a KS only state, that has no correspondence in DirectShow // DbgLogInfo(("TestCap: STATE Acquire, Stream=%d\n", StreamNumber)); break; case KSSTATE_PAUSE: // // On a transition to pause from acquire or stop, start our timer running. // if (PreviousState == KSSTATE_ACQUIRE || PreviousState == KSSTATE_STOP) { // Zero the frame counters pStrmEx->VBIFrameInfo.PictureNumber = 0; pStrmEx->VBIFrameInfo.DropCount = 0; pStrmEx->VBIFrameInfo.dwFrameFlags = 0; // Setup the next timer callback VBITimerRoutine(pStrmEx); } DbgLogInfo(("TestCap: STATE Pause, Stream=%d\n", StreamNumber)); break; case KSSTATE_RUN: // // Begin Streaming. // // Reset the discontinuity flag pStrmEx->fDiscontinuity = FALSE; // Setting the NextFrame time to zero will cause the value to be // reset from the stream time pStrmEx->QST_NextFrame = 0; DbgLogInfo(("TestCap: STATE Run, Stream=%d\n", StreamNumber)); break; } // end switch (pSrb->CommandData.StreamState) } /* ** VBIReceiveCtrlPacket() ** ** Receives packet commands that control all the VBI (VBI/NABTS/CC) streams ** ** Arguments: ** ** pSrb - The stream request block for the VBI stream ** ** Returns: nothing ** ** Side Effects: none */ VOID STREAMAPI VBIReceiveCtrlPacket( IN PHW_STREAM_REQUEST_BLOCK pSrb ) { PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension); PSTREAMEX pStrmEx = (PSTREAMEX)pSrb->StreamObject->HwStreamExtension; int StreamNumber = pStrmEx->pStreamObject->StreamNumber; BOOL Busy; // // make sure we have a device extension and are at passive level // DEBUG_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); DEBUG_ASSERT(pHwDevExt != 0); DbgLogInfo(("TestCap: Receiving %s Stream Control SRB %p, %x\n", (StreamNumber == STREAM_VBI)? "VBI" : (StreamNumber == STREAM_NABTS)? "NABTS" : (StreamNumber == STREAM_CC)? "CC" : "???", pSrb, pSrb->Command)); // // If we're already processing an SRB, add it to the queue // Busy = AddToListIfBusy ( pSrb, &pHwDevExt->AdapterSpinLock, &pHwDevExt->ProcessingControlSRB [StreamNumber], &pHwDevExt->StreamControlSRBList[StreamNumber]); if (Busy) { return; } do { // // Default to success // pSrb->Status = STATUS_SUCCESS; // // determine the type of packet. // switch (pSrb->Command) { case SRB_PROPOSE_DATA_FORMAT: DbgLogInfo(("TestCap: Receiving SRB_PROPOSE_DATA_FORMAT SRB %p, StreamNumber= %d\n", pSrb, StreamNumber)); if (!(AdapterVerifyFormat ( pSrb->CommandData.OpenFormat, pSrb->StreamObject->StreamNumber))) { pSrb->Status = STATUS_NO_MATCH; } break; case SRB_SET_DATA_FORMAT: DbgLogInfo(("TestCap: SRB_SET_DATA_FORMAT")); pSrb->Status = STATUS_NOT_IMPLEMENTED; break; case SRB_GET_DATA_FORMAT: DbgLogInfo(("TestCap: SRB_GET_DATA_FORMAT")); pSrb->Status = STATUS_NOT_IMPLEMENTED; break; case SRB_SET_STREAM_STATE: VBISetState(pSrb); break; case SRB_GET_STREAM_STATE: VideoGetState(pSrb); break; case SRB_GET_STREAM_PROPERTY: VideoGetProperty(pSrb); break; case SRB_INDICATE_MASTER_CLOCK: VideoIndicateMasterClock(pSrb); break; default: // // invalid / unsupported command. Fail it as such // TRAP; pSrb->Status = STATUS_NOT_IMPLEMENTED; } CompleteStreamSRB (pSrb); // // See if there's anything else on the queue // Busy = RemoveFromListIfAvailable ( &pSrb, &pHwDevExt->AdapterSpinLock, &pHwDevExt->ProcessingControlSRB [StreamNumber], &pHwDevExt->StreamControlSRBList[StreamNumber]); } while (Busy); } /* ** VBIReceiveDataPacket() ** ** Receives VBI data packet commands on the output streams ** ** Arguments: ** ** pSrb - Stream request block for the VBI stream ** ** Returns: nothing ** ** Side Effects: none */ VOID STREAMAPI VBIReceiveDataPacket( IN PHW_STREAM_REQUEST_BLOCK pSrb ) { PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension); PSTREAMEX pStrmEx = (PSTREAMEX)pSrb->StreamObject->HwStreamExtension; int StreamNumber = pSrb->StreamObject->StreamNumber; // // make sure we have a device extension and are at passive level // DEBUG_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); DEBUG_ASSERT(pHwDevExt != 0); DbgLogTrace(("'TestCap: Receiving VBI Stream Data SRB %p, %x\n", pSrb, pSrb->Command)); // // Default to success // pSrb->Status = STATUS_SUCCESS; // // determine the type of packet. // switch (pSrb->Command){ case SRB_READ_DATA: // Rule: // Only accept read requests when in either the Pause or Run // States. If Stopped, immediately return the SRB. if (pStrmEx->KSState == KSSTATE_STOP) { CompleteStreamSRB (pSrb); break; } // // Put this read request on the pending queue // VideoQueueAddSRB (pSrb); // Since another thread COULD HAVE MODIFIED THE STREAM STATE // in the midst of adding it to the queue, check the stream // state again, and cancel the SRB if necessary. Note that // this race condition was NOT handled in the original DDK // release of testcap! if (pStrmEx->KSState == KSSTATE_STOP) { VideoQueueCancelOneSRB ( pStrmEx, pSrb); } break; default: // // invalid / unsupported command. Fail it as such // TRAP; pSrb->Status = STATUS_NOT_IMPLEMENTED; CompleteStreamSRB (pSrb); } // switch (pSrb->Command) }