//==========================================================================; // // CWDMCaptureStream - Capture Stream base class implementation // // $Date: 22 Feb 1999 15:13:58 $ // $Revision: 1.1 $ // $Author: KLEBANOV $ // // $Copyright: (c) 1997 - 1998 ATI Technologies Inc. All Rights Reserved. $ // //==========================================================================; extern "C" { #include "strmini.h" #include "ksmedia.h" #include "ddkmapi.h" } #include "wdmvdec.h" #include "wdmdrv.h" #include "aticonfg.h" #include "capdebug.h" #include "defaults.h" #include "winerror.h" void CWDMCaptureStream::TimeoutPacket(IN OUT PHW_STREAM_REQUEST_BLOCK pSrb) { if (m_KSState == KSSTATE_STOP || !m_pVideoDecoder->PreEventOccurred()) { DBGTRACE(("Attempting to complete Srbs.\n")); EmptyIncomingDataSrbQueue(); } } void CWDMCaptureStream::Startup(PUINT puiErrorCode) { KIRQL Irql; DBGTRACE(("CWDMCaptureStream::Startup()\n")); KeInitializeEvent(&m_specialEvent, SynchronizationEvent, FALSE); KeInitializeEvent(&m_stateTransitionEvent, SynchronizationEvent, FALSE); KeInitializeEvent(&m_SrbAvailableEvent, SynchronizationEvent, FALSE); KeInitializeSpinLock(&m_streamDataLock); KeAcquireSpinLock(&m_streamDataLock, &Irql); InitializeListHead(&m_incomingDataSrbQueue); InitializeListHead(&m_waitQueue); InitializeListHead(&m_reversalQueue); KeReleaseSpinLock(&m_streamDataLock, Irql); ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); ASSERT(m_stateChange == Initializing); m_stateChange = Starting; HANDLE threadHandle; NTSTATUS status = PsCreateSystemThread(&threadHandle, (ACCESS_MASK) 0L, NULL, NULL, NULL, (PKSTART_ROUTINE) ThreadStart, (PVOID) this); if (status != STATUS_SUCCESS) { DBGERROR(("CreateStreamThread failed\n")); *puiErrorCode = WDMMINI_ERROR_MEMORYALLOCATION; return; } // Don't need this for anything, so might as well close it now. // The thread will call PsTerminateThread on itself when it // is done. ZwClose(threadHandle); KeWaitForSingleObject(&m_specialEvent, Suspended, KernelMode, FALSE, NULL); ASSERT(m_stateChange == ChangeComplete); DBGTRACE(("SrbOpenStream got notification that thread started\n")); *puiErrorCode = WDMMINI_NOERROR; } void CWDMCaptureStream::Shutdown() { KIRQL Irql; DBGTRACE(("CWDMCaptureStream::Shutdown()\n")); if ( m_stateChange != Initializing ) { ASSERT(m_stateChange == ChangeComplete); m_stateChange = Closing; KeResetEvent(&m_specialEvent); KeSetEvent(&m_stateTransitionEvent, 0, TRUE); KeWaitForSingleObject(&m_specialEvent, Suspended, KernelMode, FALSE, NULL); ASSERT(m_stateChange == ChangeComplete); KeAcquireSpinLock(&m_streamDataLock, &Irql); if (!IsListEmpty(&m_incomingDataSrbQueue)) { TRAP(); } if (!IsListEmpty(&m_waitQueue)) { TRAP(); } KeReleaseSpinLock(&m_streamDataLock, Irql); } ReleaseCaptureHandle(); } void CWDMCaptureStream::ThreadProc() { PHW_STREAM_REQUEST_BLOCK pCurrentSrb = NULL; PSRB_DATA_EXTENSION pSrbExt = NULL; KEVENT DummyEvent; const int numEvents = 3; NTSTATUS status; // Wo unto you if you overrun this array PVOID eventArray[numEvents]; KeInitializeEvent(&DummyEvent, SynchronizationEvent, FALSE); ASSERT(m_stateChange == Starting); // Indicates to SrbOpenStream() to continue m_stateChange = ChangeComplete; KeSetEvent(&m_specialEvent, 0, FALSE); // These should remain constant the whole time eventArray[0] = &m_stateTransitionEvent; eventArray[1] = &m_SrbAvailableEvent; // eventArray[2] changes, so it is set below // This runs until the thread terminates itself // inside of HandleStateTransition while (1) { // May not be necessary #define ENABLE_TIMEOUT #ifdef ENABLE_TIMEOUT LARGE_INTEGER i; #endif if (pCurrentSrb == NULL) { pSrbExt = (PSRB_DATA_EXTENSION)ExInterlockedRemoveHeadList(&m_waitQueue, &m_streamDataLock); if (pSrbExt) { pCurrentSrb = pSrbExt->pSrb; eventArray[2] = &pSrbExt->bufferDoneEvent; } else { #ifdef DEBUG if (m_KSState == KSSTATE_RUN && m_stateChange == ChangeComplete && m_pVideoDecoder->PreEventOccurred() == FALSE) { static int j; // Indicates that we are starved for buffers. Probably // a higher level is not handing them to us in a timely // fashion for some reason DBGPRINTF((" S ")); if ((++j % 10) == 0) { DBGPRINTF(("\n")); } } #endif pCurrentSrb = NULL; eventArray[2] = &DummyEvent; } } #ifdef ENABLE_TIMEOUT // This is meant mainly as a failsafe measure. i.QuadPart = -2000000; // 200 ms #endif status = KeWaitForMultipleObjects( numEvents, // count eventArray, // DispatcherObjectArray WaitAny, // WaitType Executive, // WaitReason KernelMode, // WaitMode FALSE, // Alertable #ifdef ENABLE_TIMEOUT &i, // Timeout (Optional) #else NULL, #endif NULL); // WaitBlockArray (Optional) switch (status) { // State transition. May including killing this very thread case 0: if ( pCurrentSrb ) { ExInterlockedInsertHeadList( &m_waitQueue, &pSrbExt->srbListEntry, &m_streamDataLock ); pCurrentSrb = NULL; } HandleStateTransition(); break; // New Srb available case 1: if ( pCurrentSrb ) { ExInterlockedInsertHeadList( &m_waitQueue, &pSrbExt->srbListEntry, &m_streamDataLock ); pCurrentSrb = NULL; } if (m_KSState == KSSTATE_RUN && m_stateChange == ChangeComplete) { AddBuffersToDirectDraw(); } break; // Busmaster complete case 2: if ( pCurrentSrb ) { HandleBusmasterCompletion(pCurrentSrb); pCurrentSrb = NULL; } break; #ifdef ENABLE_TIMEOUT // If we timeout in the RUN state, this is our chance to try again // to add buffers. May not be necessary, since currently, we go // through a state transition for DOS boxes, etc. case STATUS_TIMEOUT: if ( pCurrentSrb ) { ExInterlockedInsertHeadList( &m_waitQueue, &pSrbExt->srbListEntry, &m_streamDataLock ); pCurrentSrb = NULL; } if (m_KSState == KSSTATE_RUN && m_stateChange == ChangeComplete && m_pVideoDecoder->PreEventOccurred() == FALSE) { AddBuffersToDirectDraw(); } break; #endif default: TRAP(); break; } } } VOID STREAMAPI CWDMCaptureStream::VideoReceiveDataPacket(IN PHW_STREAM_REQUEST_BLOCK pSrb) { KIRQL Irql; PSRB_DATA_EXTENSION pSrbExt; ASSERT(pSrb->Irp->MdlAddress); DBGINFO(("Receiving SD---- SRB=%x\n", pSrb)); pSrb->Status = STATUS_SUCCESS; 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 ( (m_KSState == KSSTATE_STOP) || ( m_stateChange == Initializing ) ) { StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb); break; } pSrbExt = (PSRB_DATA_EXTENSION)pSrb->SRBExtension; RtlZeroMemory (pSrbExt, sizeof (SRB_DATA_EXTENSION)); pSrbExt->pSrb = pSrb; KeInitializeEvent(&pSrbExt->bufferDoneEvent, SynchronizationEvent, FALSE); DBGINFO(("Adding 0x%x to data queue\n", pSrb)); KeAcquireSpinLock(&m_streamDataLock, &Irql); InsertTailList(&m_incomingDataSrbQueue, &pSrbExt->srbListEntry); KeReleaseSpinLock(&m_streamDataLock, Irql); KeSetEvent(&m_SrbAvailableEvent, 0, FALSE); break; default: // // invalid / unsupported command. Fail it as such // TRAP(); pSrb->Status = STATUS_NOT_IMPLEMENTED; StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb); break; } } /* ** VideoGetProperty() ** ** Routine to process video property requests ** ** Arguments: ** ** pSrb - pointer to the stream request block for properties ** ** Returns: ** ** Side Effects: none */ VOID CWDMCaptureStream::VideoGetProperty(PHW_STREAM_REQUEST_BLOCK pSrb) { PSTREAM_PROPERTY_DESCRIPTOR pSPD = pSrb->CommandData.PropertyInfo; if (IsEqualGUID (KSPROPSETID_Connection, pSPD->Property->Set)) { VideoStreamGetConnectionProperty (pSrb); } else if (IsEqualGUID (PROPSETID_VIDCAP_DROPPEDFRAMES, pSPD->Property->Set)) { VideoStreamGetDroppedFramesProperty (pSrb); } else { pSrb->Status = STATUS_NOT_IMPLEMENTED; } } /* ** VideoSetState() ** ** Sets the current state of the requested stream ** ** Arguments: ** ** pSrb - pointer to the stream request block for properties ** BOOL bVPVBIConnected ** BOOL bVPConnected ** ** Returns: ** ** Side Effects: none */ VOID CWDMCaptureStream::VideoSetState(PHW_STREAM_REQUEST_BLOCK pSrb, BOOL bVPConnected, BOOL bVPVBIConnected) { // // For each stream, the following states are used: // // Stop: Absolute minimum resources are used. No outstanding IRPs. // Pause: Getting ready to run. Allocate needed resources so that // the eventual transition to Run is as fast as possible. // SRBs will be queued at either the Stream class or in your // driver. // Run: Streaming. // // Moving to Stop or Run ALWAYS transitions through Pause, so that ONLY // the following transitions are possible: // // Stop -> Pause // Pause -> Run // Run -> Pause // Pause -> Stop // // Note that it is quite possible to transition repeatedly between states: // Stop -> Pause -> Stop -> Pause -> Run -> Pause -> Run -> Pause -> Stop // BOOL bStreamCondition; DBGINFO(("CWDMCaptureStream::VideoSetState for stream %d\n", pSrb->StreamObject->StreamNumber)); pSrb->Status = STATUS_SUCCESS; switch (pSrb->CommandData.StreamState) { case KSSTATE_STOP: DBGINFO((" state KSSTATE_STOP")); ASSERT(m_stateChange == ChangeComplete); m_stateChange = Stopping; FlushBuffers(); KeResetEvent(&m_specialEvent); KeSetEvent(&m_stateTransitionEvent, 0, TRUE); KeWaitForSingleObject(&m_specialEvent, Suspended, KernelMode, FALSE, NULL); ASSERT(m_stateChange == ChangeComplete); break; case KSSTATE_ACQUIRE: DBGINFO((" state KSSTATE_ACQUIRE")); ASSERT(m_KSState == KSSTATE_STOP); break; case KSSTATE_PAUSE: DBGINFO((" state KSSTATE_PAUSE")); switch( pSrb->StreamObject->StreamNumber) { case STREAM_VideoCapture: bStreamCondition = bVPConnected; break; case STREAM_VBICapture: bStreamCondition = bVPVBIConnected; break; default: bStreamCondition = FALSE; break; } if( !bStreamCondition) { pSrb->Status = STATUS_UNSUCCESSFUL; } else if (m_pVideoDecoder->PreEventOccurred() && (m_KSState == KSSTATE_STOP || m_KSState == KSSTATE_ACQUIRE)) { pSrb->Status = STATUS_UNSUCCESSFUL; } else if (m_KSState == KSSTATE_STOP || m_KSState == KSSTATE_ACQUIRE) { ResetFrameCounters(); ResetFieldNumber(); if (!GetCaptureHandle()) pSrb->Status = STATUS_UNSUCCESSFUL; } else if (m_KSState == KSSTATE_RUN) { // Transitioning from run to pause ASSERT(m_stateChange == ChangeComplete); m_stateChange = Pausing; FlushBuffers(); KeResetEvent(&m_specialEvent); KeSetEvent(&m_stateTransitionEvent, 0, TRUE); KeWaitForSingleObject(&m_specialEvent, Suspended, KernelMode, FALSE, NULL); ASSERT(m_stateChange == ChangeComplete); } break; case KSSTATE_RUN: DBGINFO((" state KSSTATE_RUN")); ASSERT(m_KSState == KSSTATE_ACQUIRE || m_KSState == KSSTATE_PAUSE); if (m_pVideoDecoder->PreEventOccurred()) { pSrb->Status = STATUS_UNSUCCESSFUL; } else { ResetFieldNumber(); // Transitioning from pause to run ASSERT(m_stateChange == ChangeComplete); m_stateChange = Running; KeResetEvent(&m_specialEvent); KeSetEvent(&m_stateTransitionEvent, 0, TRUE); KeWaitForSingleObject(&m_specialEvent, Suspended, KernelMode, FALSE, NULL); ASSERT(m_stateChange == ChangeComplete); } break; } if (pSrb->Status == STATUS_SUCCESS) { m_KSState = pSrb->CommandData.StreamState; DBGINFO((" entered\n")); } else DBGINFO((" NOT entered ***\n")); } VOID CWDMCaptureStream::VideoStreamGetConnectionProperty (PHW_STREAM_REQUEST_BLOCK pSrb) { PSTREAM_PROPERTY_DESCRIPTOR pSPD = pSrb->CommandData.PropertyInfo; PKSALLOCATOR_FRAMING Framing = (PKSALLOCATOR_FRAMING) pSPD->PropertyInfo; ASSERT(pSPD->Property->Id == KSPROPERTY_CONNECTION_ALLOCATORFRAMING); if (pSPD->Property->Id == KSPROPERTY_CONNECTION_ALLOCATORFRAMING) { RtlZeroMemory(Framing, sizeof(KSALLOCATOR_FRAMING)); Framing->RequirementsFlags = KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY | KSALLOCATOR_REQUIREMENTF_INPLACE_MODIFIER | KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY; Framing->PoolType = NonPagedPool; Framing->Frames = NumBuffers; Framing->FrameSize = GetFrameSize(); Framing->FileAlignment = 0;//FILE_QUAD_ALIGNMENT;// PAGE_SIZE - 1; pSrb->ActualBytesTransferred = sizeof(KSALLOCATOR_FRAMING); } else { pSrb->Status = STATUS_NOT_IMPLEMENTED; } } /* ** VideoStreamGetDroppedFramesProperty ** ** Gets dropped frame information ** ** Arguments: ** ** pSrb - pointer to the stream request block for properties ** ** Returns: ** ** Side Effects: none */ VOID CWDMCaptureStream::VideoStreamGetDroppedFramesProperty(PHW_STREAM_REQUEST_BLOCK pSrb) { PSTREAM_PROPERTY_DESCRIPTOR pSPD = pSrb->CommandData.PropertyInfo; PKSPROPERTY_DROPPEDFRAMES_CURRENT_S pDroppedFrames = (PKSPROPERTY_DROPPEDFRAMES_CURRENT_S) pSPD->PropertyInfo; ASSERT(pSPD->Property->Id == KSPROPERTY_DROPPEDFRAMES_CURRENT); if (pSPD->Property->Id == KSPROPERTY_DROPPEDFRAMES_CURRENT) { RtlCopyMemory(pDroppedFrames, pSPD->Property, sizeof(KSPROPERTY)); // initialize the unused portion GetDroppedFrames(pDroppedFrames); DBGINFO(("PictNumber: 0x%x; DropCount: 0x%x; BufSize: 0x%x\n", (ULONG) pDroppedFrames->PictureNumber, (ULONG) pDroppedFrames->DropCount, (ULONG) pDroppedFrames->AverageFrameSize)); pSrb->ActualBytesTransferred = sizeof (KSPROPERTY_DROPPEDFRAMES_CURRENT_S); } else { pSrb->Status = STATUS_NOT_IMPLEMENTED; } } VOID CWDMCaptureStream::CloseCapture() { DBGTRACE(("DDNOTIFY_CLOSECAPTURE; stream = %d\n", m_pStreamObject->StreamNumber)); m_hCapture = 0; } VOID CWDMCaptureStream::EmptyIncomingDataSrbQueue() { KIRQL Irql; PKSSTREAM_HEADER pDataPacket; if ( m_stateChange == Initializing ) { return; // queue not setup yet, so we can return knowing that nothing is in the queue } // Think about replacing with ExInterlockedRemoveHeadList. KeAcquireSpinLock(&m_streamDataLock, &Irql); while (!IsListEmpty(&m_incomingDataSrbQueue)) { PSRB_DATA_EXTENSION pSrbExt = (PSRB_DATA_EXTENSION)RemoveHeadList(&m_incomingDataSrbQueue); PHW_STREAM_REQUEST_BLOCK pSrb = pSrbExt->pSrb; pSrb->Status = STATUS_SUCCESS; pDataPacket = pSrb->CommandData.DataBufferArray; pDataPacket->DataUsed = 0; KeReleaseSpinLock(&m_streamDataLock, Irql); DBGINFO(("Completing Srb 0x%x in STATE_STOP\n", pSrb)); StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb); KeAcquireSpinLock(&m_streamDataLock, &Irql); } KeReleaseSpinLock(&m_streamDataLock, Irql); } BOOL CWDMCaptureStream::ReleaseCaptureHandle() { int streamNumber = m_pStreamObject->StreamNumber; DWORD ddOut = DD_OK; DDCLOSEHANDLE ddClose; if (m_hCapture != 0) { DBGTRACE(("Stream %d releasing capture handle\n", streamNumber)); ddClose.hHandle = m_hCapture; DxApi(DD_DXAPI_CLOSEHANDLE, &ddClose, sizeof(ddClose), &ddOut, sizeof(ddOut)); if (ddOut != DD_OK) { DBGERROR(("DD_DXAPI_CLOSEHANDLE failed.\n")); TRAP(); return FALSE; } m_hCapture = 0; } return TRUE; } VOID CWDMCaptureStream::HandleBusmasterCompletion(PHW_STREAM_REQUEST_BLOCK pCurrentSrb) { int streamNumber = m_pStreamObject->StreamNumber; PSRB_DATA_EXTENSION pSrbExt = (PSRB_DATA_EXTENSION)pCurrentSrb->SRBExtension; KIRQL Irql; // This function is called as a result of DD completing a BM. That means // m_stateChange will not be in the Initializing state for sure // First handle case where we get a Busmaster completion // indication while we are trying to pause or stop if (m_stateChange == Pausing || m_stateChange == Stopping) { PUCHAR ptr; KeAcquireSpinLock(&m_streamDataLock, &Irql); // Put it at the head of the temporary 'reversal' queue. InsertHeadList(&m_reversalQueue, &pSrbExt->srbListEntry); if (IsListEmpty(&m_waitQueue)) { // if there is nothing left in the wait queue we can now // proceed to move everything back to the incoming queue. // This whole ugly ordeal is to // make sure that they end up in the original order while (!IsListEmpty(&m_reversalQueue)) { ptr = (PUCHAR)RemoveHeadList(&m_reversalQueue); InsertHeadList(&m_incomingDataSrbQueue, (PLIST_ENTRY) ptr); } KeReleaseSpinLock(&m_streamDataLock, Irql); if (m_stateChange == Stopping) { EmptyIncomingDataSrbQueue(); } // Indicate that we have successfully completed this part // of the transition to the pause state. m_stateChange = ChangeComplete; KeSetEvent(&m_specialEvent, 0, FALSE); return; } KeReleaseSpinLock(&m_streamDataLock, Irql); return; } // else it is a regular busmaster completion while in the run state else { ASSERT (pCurrentSrb); PKSSTREAM_HEADER pDataPacket = pCurrentSrb->CommandData.DataBufferArray; pDataPacket->OptionsFlags = 0; pSrbExt = (PSRB_DATA_EXTENSION)pCurrentSrb->SRBExtension; DBGINFO(("FieldNum: %d; ddRVal: 0x%x; polarity: 0x%x\n", pSrbExt->ddCapBuffInfo.dwFieldNumber, pSrbExt->ddCapBuffInfo.ddRVal, pSrbExt->ddCapBuffInfo.bPolarity)); // It's possible that the srb got cancelled while we were waiting. // Currently, this status is reset below if (pCurrentSrb->Status == STATUS_CANCELLED) { DBGINFO(("pCurrentSrb 0x%x was cancelled while we were waiting\n", pCurrentSrb)); pDataPacket->DataUsed = 0; } // It's also possible that there was a problem in DD-land else if (pSrbExt->ddCapBuffInfo.ddRVal != DD_OK) { // Two cases of which I am aware. // 1) flushed buffers if (pSrbExt->ddCapBuffInfo.ddRVal == E_FAIL) { DBGINFO(("ddRVal = 0x%x. Assuming we flushed\n", pSrbExt->ddCapBuffInfo.ddRVal)); pDataPacket->DataUsed = 0; } // 2) something else else { DBGERROR(("= 0x%x. Problem in Busmastering?\n", pSrbExt->ddCapBuffInfo.ddRVal)); pDataPacket->DataUsed = 0; } } // There is also the remote possibility that everything is OK else { SetFrameInfo(pCurrentSrb); TimeStampSrb(pCurrentSrb); pDataPacket->DataUsed = pDataPacket->FrameExtent; } DBGINFO(("StreamRequestComplete for SRB 0x%x\n", pCurrentSrb)); // Always return success. Failure // is indicated by setting DataUsed to 0. pCurrentSrb->Status = STATUS_SUCCESS; ASSERT(pCurrentSrb->Irp->MdlAddress); StreamClassStreamNotification( StreamRequestComplete, pCurrentSrb->StreamObject, pCurrentSrb); } } void CWDMCaptureStream::AddBuffersToDirectDraw() { KIRQL Irql; BOOL fAdded; KeAcquireSpinLock(&m_streamDataLock, &Irql); while (!IsListEmpty(&m_incomingDataSrbQueue)) { // So if we've reached this point, we are in the run state, and // we have an SRB on our incoming queue, and we are holding the // the stream lock PSRB_DATA_EXTENSION pSrbExt = (PSRB_DATA_EXTENSION)RemoveHeadList(&m_incomingDataSrbQueue); PHW_STREAM_REQUEST_BLOCK pSrb = pSrbExt->pSrb; // Calls to DXAPI must be at Passive level, so release the spinlock temporarily KeReleaseSpinLock(&m_streamDataLock, Irql); DBGINFO(("Removed 0x%x from data queue\n", pSrb)); fAdded = AddBuffer(pSrb); KeAcquireSpinLock(&m_streamDataLock, &Irql); if (fAdded) { DBGINFO(("Adding 0x%x to wait queue\n", pSrb)); InsertTailList(&m_waitQueue, &pSrbExt->srbListEntry); } else { DBGINFO(("Adding 0x%x back to dataqueue\n", pSrb)); // put it back where it was InsertHeadList(&m_incomingDataSrbQueue, &pSrbExt->srbListEntry); break; } } KeReleaseSpinLock(&m_streamDataLock, Irql); } BOOL CWDMCaptureStream::AddBuffer(PHW_STREAM_REQUEST_BLOCK pSrb) { DDADDVPCAPTUREBUFF ddAddVPCaptureBuffIn; DWORD ddOut = DD_OK; PIRP irp = pSrb->Irp; PSRB_DATA_EXTENSION pSrbExt = (PSRB_DATA_EXTENSION)pSrb->SRBExtension; DBGINFO(("In AddBuffer. pSrb: 0x%x.\n", pSrb)); // For handling full-screen DOS, res changes, etc. if (m_hCapture == 0) { if (!GetCaptureHandle()) { return FALSE; } } ddAddVPCaptureBuffIn.hCapture = m_hCapture; ddAddVPCaptureBuffIn.dwFlags = DDADDBUFF_SYSTEMMEMORY; ddAddVPCaptureBuffIn.pMDL = irp->MdlAddress; ddAddVPCaptureBuffIn.lpBuffInfo = &pSrbExt->ddCapBuffInfo; ddAddVPCaptureBuffIn.pKEvent = &pSrbExt->bufferDoneEvent; DxApi(DD_DXAPI_ADDVPCAPTUREBUFFER, &ddAddVPCaptureBuffIn, sizeof(ddAddVPCaptureBuffIn), &ddOut, sizeof(ddOut)); if (ddOut != DD_OK) { // Not necessarily an error. DBGINFO(("DD_DXAPI_ADDVPCAPTUREBUFFER failed.\n")); // TRAP(); return FALSE; } return TRUE; } VOID CWDMCaptureStream::HandleStateTransition() { KIRQL Irql; switch (m_stateChange) { case Running: AddBuffersToDirectDraw(); m_stateChange = ChangeComplete; KeSetEvent(&m_specialEvent, 0, FALSE); break; case Pausing: KeAcquireSpinLock(&m_streamDataLock, &Irql); if (IsListEmpty(&m_waitQueue)) { KeReleaseSpinLock(&m_streamDataLock, Irql); m_stateChange = ChangeComplete; KeSetEvent(&m_specialEvent, 0, FALSE); } else { KeReleaseSpinLock(&m_streamDataLock, Irql); } break; case Stopping: KeAcquireSpinLock(&m_streamDataLock, &Irql); if (IsListEmpty(&m_waitQueue)) { KeReleaseSpinLock(&m_streamDataLock, Irql); EmptyIncomingDataSrbQueue(); m_stateChange = ChangeComplete; KeSetEvent(&m_specialEvent, 0, FALSE); } else { KeReleaseSpinLock(&m_streamDataLock, Irql); } break; case Closing: m_stateChange = ChangeComplete; KeSetEvent(&m_specialEvent, 0, FALSE); DBGTRACE(("StreamThread exiting\n")); PsTerminateSystemThread(STATUS_SUCCESS); DBGERROR(("Shouldn't get here\n")); TRAP(); break; case ChangeComplete: DBGTRACE(("Must have completed transition in HandleBusMasterCompletion\n")); break; default: TRAP(); break; } } BOOL CWDMCaptureStream::ResetFieldNumber() { int streamNumber = m_pStreamObject->StreamNumber; DDSETFIELDNUM ddSetFieldNum; DWORD ddOut; ASSERT(streamNumber == STREAM_VideoCapture || streamNumber == STREAM_VBICapture); if (m_pVideoPort->GetDirectDrawHandle() == 0) { DBGERROR(("Didn't expect ring0DirectDrawHandle to be zero.\n")); TRAP(); return FALSE; } if (m_pVideoPort->GetVideoPortHandle() == 0) { DBGERROR(("Didn't expect ring0VideoPortHandle to be zero.\n")); TRAP(); return FALSE; } RtlZeroMemory(&ddSetFieldNum, sizeof(ddSetFieldNum)); RtlZeroMemory(&ddOut, sizeof(ddOut)); KSPROPERTY_DROPPEDFRAMES_CURRENT_S DroppedFrames; GetDroppedFrames(&DroppedFrames); ddSetFieldNum.hDirectDraw = m_pVideoPort->GetDirectDrawHandle(); ddSetFieldNum.hVideoPort = m_pVideoPort->GetVideoPortHandle(); ddSetFieldNum.dwFieldNum = ((ULONG)DroppedFrames.PictureNumber + 1) * GetFieldInterval(); DxApi(DD_DXAPI_SET_VP_FIELD_NUMBER, &ddSetFieldNum, sizeof(ddSetFieldNum), &ddOut, sizeof(ddOut)); if (ddOut != DD_OK) { DBGERROR(("DD_DXAPI_SET_VP_FIELD_NUMBER failed.\n")); TRAP(); return FALSE; } else { #ifdef DEBUG DBGINFO(("PictureNumber: %d; ", DroppedFrames.PictureNumber)); DBGINFO(("DropCount: %d\n", DroppedFrames.DropCount)); DBGINFO(("AverageFrameSize: %d\n", DroppedFrames.AverageFrameSize)); #endif return TRUE; } } BOOL CWDMCaptureStream::FlushBuffers() { DWORD ddOut = DD_OK; // commented out the trap because it is possible that capture handle is closed in DD before flushbuffer is called during mode switch if (m_hCapture == NULL) { //DBGERROR(("m_hCapture === NULL in FlushBuffers.\n")); //TRAP(); return FALSE; } DxApi(DD_DXAPI_FLUSHVPCAPTUREBUFFERS, &m_hCapture, sizeof(HANDLE), &ddOut, sizeof(ddOut)); if (ddOut != DD_OK) { DBGERROR(("DD_DXAPI_FLUSHVPCAPTUREBUFFERS failed.\n")); TRAP(); return FALSE; } else { return TRUE; } } VOID CWDMCaptureStream::TimeStampSrb(PHW_STREAM_REQUEST_BLOCK pSrb) { PKSSTREAM_HEADER pDataPacket = pSrb->CommandData.DataBufferArray; PSRB_DATA_EXTENSION pSrbExt = (PSRB_DATA_EXTENSION)pSrb->SRBExtension; pDataPacket->Duration = GetFieldInterval() * NTSCFieldDuration; pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DURATIONVALID | KSSTREAM_HEADER_OPTIONSF_SPLICEPOINT; // Find out what time it is, if we're using a clock if (m_hMasterClock) { LARGE_INTEGER Delta; HW_TIME_CONTEXT TimeContext; // TimeContext.HwDeviceExtension = pHwDevExt; TimeContext.HwDeviceExtension = (struct _HW_DEVICE_EXTENSION *)m_pVideoDecoder; TimeContext.HwStreamObject = m_pStreamObject; TimeContext.Function = TIME_GET_STREAM_TIME; StreamClassQueryMasterClockSync ( m_hMasterClock, &TimeContext); // This calculation should result in the stream time WHEN the buffer // was filled. Delta.QuadPart = TimeContext.SystemTime - pSrbExt->ddCapBuffInfo.liTimeStamp.QuadPart; // Be safe, just use the current stream time, without the correction for when // DDraw actually returned the buffer to us. pDataPacket->PresentationTime.Time = TimeContext.Time; #ifdef THIS_SHOULD_WORK_BUT_IT_DOESNT if (TimeContext.Time > (ULONGLONG) Delta.QuadPart) { pDataPacket->PresentationTime.Time = TimeContext.Time - Delta.QuadPart; } else { // There's a bug in Ks or Stream after running for 2 hours // that makes this hack necessary. Will be fixed soon... pDataPacket->PresentationTime.Time = TimeContext.Time; } #endif #ifdef DEBUG ULONG *tmp1, *tmp2; tmp1 = (ULONG *)&pDataPacket->PresentationTime.Time; tmp2 = (ULONG *)&TimeContext.Time; DBGINFO(("PT: 0x%x%x; ST: 0x%x%x\n", tmp1[1], tmp1[0], tmp2[1], tmp2[0])); #endif pDataPacket->PresentationTime.Numerator = 1; pDataPacket->PresentationTime.Denominator = 1; pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID; } else { pDataPacket->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; } } void CWDMCaptureStream::CancelPacket( PHW_STREAM_REQUEST_BLOCK pSrbToCancel) { PHW_STREAM_REQUEST_BLOCK pCurrentSrb; KIRQL Irql; PLIST_ENTRY Entry; BOOL bFound = FALSE; if ( m_stateChange == Initializing ) // Stream not completely setup, so nothing in the queue { DBGINFO(( "Bt829: Didn't find Srb 0x%x\n", pSrbToCancel)); return; } KeAcquireSpinLock( &m_streamDataLock, &Irql); Entry = m_incomingDataSrbQueue.Flink; // // Loop through the linked list from the beginning to end, // trying to find the SRB to cancel // while( Entry != &m_incomingDataSrbQueue) { PSRB_DATA_EXTENSION pSrbExt; pSrbExt = ( PSRB_DATA_EXTENSION)Entry; pCurrentSrb = pSrbExt->pSrb; if( pCurrentSrb == pSrbToCancel) { RemoveEntryList( Entry); bFound = TRUE; break; } Entry = Entry->Flink; } KeReleaseSpinLock( &m_streamDataLock, Irql); if( bFound) { pCurrentSrb->Status = STATUS_CANCELLED; pCurrentSrb->CommandData.DataBufferArray->DataUsed = 0; DBGINFO(( "Bt829: Cancelled Srb 0x%x\n", pCurrentSrb)); StreamClassStreamNotification( StreamRequestComplete, pCurrentSrb->StreamObject, pCurrentSrb); } else { // If this is a DATA_TRANSFER and a STREAM_REQUEST SRB, // then it must be in the waitQueue, being filled by DDraw. // If so, mark it cancelled, and it will // be returned when DDraw is finished with it. if(( pSrbToCancel->Flags & (SRB_HW_FLAGS_DATA_TRANSFER | SRB_HW_FLAGS_STREAM_REQUEST)) == (SRB_HW_FLAGS_DATA_TRANSFER | SRB_HW_FLAGS_STREAM_REQUEST)) { pSrbToCancel->Status = STATUS_CANCELLED; DBGINFO(( "Bt829: Cancelled Srb on waitQueue 0x%x\n", pSrbToCancel)); } else { DBGINFO(( "Bt829: Didn't find Srb 0x%x\n", pSrbToCancel)); } } }