VOID STREAMAPI CompleteDeviceSRB ( IN PHW_STREAM_REQUEST_BLOCK pSrb ) { DbgLogTrace(("TestCap: Completing Adapter SRB %8x\n", pSrb)); StreamClassDeviceNotification( DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb); }
VOID STREAMAPI CompleteStreamSRB ( IN PHW_STREAM_REQUEST_BLOCK pSrb ) { DbgLogTrace(("TestCap: Completing Stream SRB %p\n", pSrb)); StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb); }
VOID STREAMAPI AnalogVideoReceiveCtrlPacket( 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 we are at passive level // DEBUG_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); DEBUG_ASSERT(pHwDevExt!=NULL); DbgLogTrace(("TestCap: Receiving Analog Stream Control SRB %p, %x\n", 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_STREAM_STATE: // // Don't use VideoSetState, since we don't want to start another // timer running // pStrmEx->KSState = pSrb->CommandData.StreamState; DbgLogInfo(("TestCap: STATE=%d, Stream=%d\n", pStrmEx->KSState, StreamNumber)); break; case SRB_GET_STREAM_STATE: VideoGetState(pSrb); break; case SRB_GET_STREAM_PROPERTY: VideoGetProperty(pSrb); break; case SRB_INDICATE_MASTER_CLOCK: // // Assigns a clock to a stream // 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 ); }
VOID STREAMAPI VideoReceiveCtrlPacket( 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!=NULL); DbgLogTrace(("TestCap: Receiving Stream Control SRB %p, %x\n", 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; } while (TRUE) { // // 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; DbgLogInfo(("TestCap: SRB_PROPOSE_DATA_FORMAT FAILED\n")); } // KS support for dynamic format changes is BROKEN right now, // so we prevent these from happening by saying they ALL fail. // If this is ever fixed, the next line must be removed. pSrb->Status = STATUS_NO_MATCH; // prevent dynamic format changes break; case SRB_SET_DATA_FORMAT: DbgLogInfo(("TestCap: SRB_SET_DATA_FORMAT\n")); if (!(AdapterVerifyFormat ( pSrb->CommandData.OpenFormat, pSrb->StreamObject->StreamNumber))) { pSrb->Status = STATUS_NO_MATCH; DbgLogInfo(("TestCap: SRB_SET_DATA_FORMAT FAILED\n")); } else { VideoSetFormat (pSrb); DbgLogInfo(("TestCap: SRB_SET_DATA_FORMAT SUCCEEDED\n")); } break; case SRB_GET_DATA_FORMAT: DbgLogInfo(("TestCap: SRB_GET_DATA_FORMAT\n")); pSrb->Status = STATUS_NOT_IMPLEMENTED; break; case SRB_SET_STREAM_STATE: VideoSetState(pSrb); break; case SRB_GET_STREAM_STATE: VideoGetState(pSrb); break; case SRB_GET_STREAM_PROPERTY: VideoGetProperty(pSrb); break; case SRB_SET_STREAM_PROPERTY: VideoSetProperty(pSrb); break; case SRB_INDICATE_MASTER_CLOCK: // // Assigns a clock to a stream // 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]); if (!Busy) { break; } } }
VOID STREAMAPI VideoReceiveDataPacket( 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!=NULL); DbgLogTrace(("TestCap: Receiving 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) }
VOID STREAMAPI VideoCaptureRoutine( IN PSTREAMEX pStrmEx ) { PHW_DEVICE_EXTENSION pHwDevExt = pStrmEx->pHwDevExt; int StreamNumber = pStrmEx->pStreamObject->StreamNumber; PKSSTREAM_HEADER pDataPacket; PKS_FRAME_INFO pFrameInfo; // 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->AvgTimePerFrame; } #ifdef CREATE_A_FLURRY_OF_TIMING_SPEW DbgLogTrace(("TestCap: Time=%6d mS at SystemTime=%I64d\n", (LONG) ((LONGLONG) TimeContext.Time / 10000), 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->FrameInfo.PictureNumber++; // // Get the next queue SRB (if any) // pSrb = VideoQueueRemoveSRB ( pHwDevExt, StreamNumber); if (pSrb) { pDataPacket = pSrb->CommandData.DataBufferArray; pFrameInfo = (PKS_FRAME_INFO) (pDataPacket + 1); // // Call the routine which synthesizes images // ImageSynth (pSrb, IMAGE_XFER_GRAY_INCREASING, pStrmEx->VideoControlMode & KS_VideoControlFlag_FlipHorizontal); // Set additional info fields about the data captured such as: // Frames Captured // Frames Dropped // Field Polarity pStrmEx->FrameInfo.ExtendedHeaderSize = pFrameInfo->ExtendedHeaderSize; *pFrameInfo = pStrmEx->FrameInfo; // 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->AvgTimePerFrame; // // 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; // Output a frame count every 100th frame in Debug mode if (pStrmEx->FrameInfo.PictureNumber % 100 == 0) { DbgLogInfo(("TestCap: Picture %u, Stream=%d\n", (unsigned int)pStrmEx->FrameInfo.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->FrameInfo.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->AvgTimePerFrame; } // endif time to capture a frame } // endif we're running }
VOID STREAMAPI AdapterReceivePacket( IN PHW_STREAM_REQUEST_BLOCK pSrb ) { PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension); BOOL Busy; DEBUG_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); DbgLogTrace(("TestCap: Receiving Adapter SRB %8x, %x\n", pSrb, pSrb->Command)); // The very first time through, we need to initialize the adapter spinlock // and queue if (!pHwDevExt->AdapterQueueInitialized) { InitializeListHead (&pHwDevExt->AdapterSRBList); KeInitializeSpinLock (&pHwDevExt->AdapterSpinLock); pHwDevExt->AdapterQueueInitialized = TRUE; pHwDevExt->ProcessingAdapterSRB = FALSE; } // // If we're already processing an SRB, add it to the queue // Busy = AddToListIfBusy ( pSrb, &pHwDevExt->AdapterSpinLock, &pHwDevExt->ProcessingAdapterSRB, &pHwDevExt->AdapterSRBList); if (Busy) { return; } // // This will run until the queue is empty // while (TRUE) { // // Assume success // pSrb->Status = STATUS_SUCCESS; // // determine the type of packet. // switch (pSrb->Command) { case SRB_INITIALIZE_DEVICE: // open the device HwInitialize(pSrb); break; case SRB_UNINITIALIZE_DEVICE: // close the device. HwUnInitialize(pSrb); break; case SRB_OPEN_STREAM: // open a stream AdapterOpenStream(pSrb); break; case SRB_CLOSE_STREAM: // close a stream AdapterCloseStream(pSrb); break; case SRB_GET_STREAM_INFO: // // return a block describing all the streams // AdapterStreamInfo(pSrb); break; case SRB_GET_DATA_INTERSECTION: // // Return a format, given a range // AdapterFormatFromRange(pSrb); break; case SRB_OPEN_DEVICE_INSTANCE: case SRB_CLOSE_DEVICE_INSTANCE: // // We should never get these since this is a single instance device // TRAP; pSrb->Status = STATUS_NOT_IMPLEMENTED; break; case SRB_GET_DEVICE_PROPERTY: // // Get adapter wide properties // AdapterGetProperty (pSrb); break; case SRB_SET_DEVICE_PROPERTY: // // Set adapter wide properties // AdapterSetProperty (pSrb); break; case SRB_PAGING_OUT_DRIVER: // // The driver is being paged out // Disable Interrupts if you have them! // DbgLogInfo(("'Testcap: Receiving SRB_PAGING_OUT_DRIVER -- SRB=%x\n", pSrb)); break; case SRB_CHANGE_POWER_STATE: // // Changing the device power state, D0 ... D3 // DbgLogInfo(("'Testcap: Receiving SRB_CHANGE_POWER_STATE ------ SRB=%x\n", pSrb)); AdapterPowerState(pSrb); break; case SRB_INITIALIZATION_COMPLETE: // // Stream class has finished initialization. // Now create DShow Medium interface BLOBs. // This needs to be done at low priority since it uses the registry // DbgLogInfo(("'Testcap: Receiving SRB_INITIALIZATION_COMPLETE-- SRB=%x\n", pSrb)); break; case SRB_UNKNOWN_DEVICE_COMMAND: default: // // this is a request that we do not understand. Indicate invalid // command and complete the request // pSrb->Status = STATUS_NOT_IMPLEMENTED; } // // Indicate back to the Stream Class that we're done with this SRB // CompleteDeviceSRB (pSrb); // // See if there's anything else on the queue // Busy = RemoveFromListIfAvailable ( &pSrb, &pHwDevExt->AdapterSpinLock, &pHwDevExt->ProcessingAdapterSRB, &pHwDevExt->AdapterSRBList); if (!Busy) { break; } } // end of while there's anything in the queue }
void ImageSynth ( IN OUT PHW_STREAM_REQUEST_BLOCK pSrb, IN ImageXferCommands Command, IN BOOL FlipHorizontal ) { PSTREAMEX pStrmEx = (PSTREAMEX)pSrb->StreamObject->HwStreamExtension; PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension); int StreamNumber = pSrb->StreamObject->StreamNumber; KS_VIDEOINFOHEADER *pVideoInfoHdr = pStrmEx->pVideoInfoHeader; UINT biWidth = pVideoInfoHdr->bmiHeader.biWidth; UINT biHeight = pVideoInfoHdr->bmiHeader.biHeight; UINT biSizeImage = pVideoInfoHdr->bmiHeader.biSizeImage; UINT biWidthBytes = KS_DIBWIDTHBYTES (pVideoInfoHdr->bmiHeader); UINT biBitCount = pVideoInfoHdr->bmiHeader.biBitCount; UINT LinesToCopy = abs (biHeight); DWORD biCompression = pVideoInfoHdr->bmiHeader.biCompression; UINT Line; PUCHAR pLineBuffer; PKSSTREAM_HEADER pDataPacket = pSrb->CommandData.DataBufferArray; PUCHAR pImage = pDataPacket->Data; DEBUG_ASSERT (pSrb->NumberOfBuffers == 1); #if 0 // Note: set "ulInDebug = 1" in a debugger to view this output with .ntkern DbgLogTrace(("\'TestCap: ImageSynthBegin\n")); DbgLogTrace(("\'TestCap: biSizeImage=%d, DataPacketLength=%d\n", biSizeImage, pDataPacket->DataPacketLength)); DbgLogTrace(("\'TestCap: biWidth=%d biHeight=%d WidthBytes=%d bpp=%d\n", biWidth, biHeight, biWidthBytes, biBitCount)); DbgLogTrace(("\'TestCap: pImage=%x\n", pImage)); #endif // // Synthesize a single line of image data, which will then be replicated // pLineBuffer = &pStrmEx->LineBuffer[0]; if ((biBitCount == 24) && (biCompression == KS_BI_RGB)) { switch (Command) { case IMAGE_XFER_NTSC_EIA_100AMP_100SAT: // 100% saturation { UINT x, col; PUCHAR pT = pLineBuffer; for (x = 0; x < biWidth; x++) { col = (x * 8) / biWidth; col = FlipHorizontal ? (7 - col) : col; *pT++ = NTSCColorBars100Amp100SatRGB24[0][col]; // Red *pT++ = NTSCColorBars100Amp100SatRGB24[1][col]; // Green *pT++ = NTSCColorBars100Amp100SatRGB24[2][col]; // Blue } } break; case IMAGE_XFER_NTSC_EIA_75AMP_100SAT: // 75% Saturation { UINT x, col; PUCHAR pT = pLineBuffer; for (x = 0; x < biWidth; x++) { col = (x * 8) / biWidth; col = FlipHorizontal ? (7 - col) : col; *pT++ = NTSCColorBars75Amp100SatRGB24[0][col]; // Red *pT++ = NTSCColorBars75Amp100SatRGB24[1][col]; // Green *pT++ = NTSCColorBars75Amp100SatRGB24[2][col]; // Blue } } break; case IMAGE_XFER_BLACK: // Camma corrected Grayscale ramp { UINT x, col; PUCHAR pT = pLineBuffer; for (x = 0; x < biWidth; x++) { col = (255 * (x * 10) / biWidth) / 10; col = FlipHorizontal ? (255 - col) : col; *pT++ = (BYTE) col; // Red *pT++ = (BYTE) col; // Green *pT++ = (BYTE) col; // Blue } } break; case IMAGE_XFER_WHITE: // All white RtlFillMemory( pLineBuffer, biWidthBytes, (UCHAR) 255); break; case IMAGE_XFER_GRAY_INCREASING: // grayscale increasing with each image captured RtlFillMemory( pLineBuffer, biWidthBytes, (UCHAR) (pStrmEx->FrameInfo.PictureNumber * 8)); break; default: break; } } // endif RGB24 else if ((biBitCount == 16) && (biCompression == FOURCC_YUV422)) { switch (Command) { case IMAGE_XFER_NTSC_EIA_100AMP_100SAT: default: { UINT x, col; PUCHAR pT = pLineBuffer; for (x = 0; x < (biWidth / 2); x++) { col = (x * 8) / (biWidth / 2); col = FlipHorizontal ? (7 - col) : col; *pT++ = NTSCColorBars100Amp100SatYUV[0][col]; // U *pT++ = NTSCColorBars100Amp100SatYUV[1][col]; // Y *pT++ = NTSCColorBars100Amp100SatYUV[2][col]; // V *pT++ = NTSCColorBars100Amp100SatYUV[3][col]; // Y } } break; } } else { DbgLogError(("\'TestCap: Unknown format in ImageSynth!!!\n")); TRAP; } // // Copy the single line synthesized to all rows of the image // for (Line = 0; Line < LinesToCopy; Line++, pImage += biWidthBytes) { // Show some action on an otherwise static image // This will be a changing grayscale horizontal band // at the bottom of an RGB image and a changing color band at the // top of a YUV image if (Line >= 3 && Line <= 6) { UINT j; for (j = 0; j < biWidthBytes; j++) { *(pImage + j) = (UCHAR) pStrmEx->FrameInfo.PictureNumber; } continue; } // Copy the synthesized line RtlCopyMemory( pImage, pLineBuffer, biWidthBytes); } // // Report back the actual number of bytes copied to the destination buffer // (This can be smaller than the allocated buffer for compressed images) // pDataPacket->DataUsed = biSizeImage; }