windows-nt/Source/XPSP1/NT/drivers/wdm/capture/mini/testcap/capvbi.c
2020-09-26 16:20:57 +08:00

810 lines
23 KiB
C

//==========================================================================;
//
// 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)
}