// $Header: G:/SwDev/WDM/Video/bt848/rcs/Vidch.cpp 1.22 1998/05/12 20:39:19 tomz Exp $ #include "vidch.h" #include "defaults.h" #include "fourcc.h" #include "capmain.h" #ifdef HAUPPAUGE #include "HCWDebug.h" #endif void CheckSrbStatus( PHW_STREAM_REQUEST_BLOCK pSrb ); BOOL VideoChannel::bIsVBI() { PSTREAMEX pStrmEx = (PSTREAMEX)GetStrmEx( ); if ( pStrmEx->StreamNumber == STREAM_IDX_VBI ) { return TRUE; } else { return FALSE; } } BOOL VideoChannel::bIsVideo() { PSTREAMEX pStrmEx = (PSTREAMEX)GetStrmEx( ); if (( pStrmEx->StreamNumber == STREAM_IDX_PREVIEW ) || ( pStrmEx->StreamNumber == STREAM_IDX_CAPTURE )) { return TRUE; } else { return FALSE; } } /* Method: VideoChannel::SetDigitalWindow * Purpose: Sets the output image size * Input: r: MRect & * Output: */ ErrorCode VideoChannel::SetDigitalWindow( MRect &r ) { Trace t("VideoChannel::SetDigitalWindow()"); return Digitizer_->SetDigitalWindow( r, *OurField_ ); } /* Method: VideoChannel::SetAnalogWindow * Purpose: Sets the analog dimention for this stream * Input: r: MRect & * Output: */ ErrorCode VideoChannel::SetAnalogWindow( MRect &r ) { Trace t("VideoChannel::SetAnalogWindow()"); return Digitizer_->SetAnalogWindow( r, *OurField_ ); } /* Method: VideoChannel::OpenChannel * Purpose: Allocates a stream from a capture chip * Input: * Output: * Note: It is possible that the current implementation does not require an * elaborate stream allocation scheme. Nonetheless it is used as number of * streams can increase in the future and their dynamics can change */ ErrorCode VideoChannel::OpenChannel() { Trace t("VideoChannel::OpenChannel()"); // can not open twice if ( IsOpen() == true ) return Fail; if ( Digitizer_->AllocateStream( OurField_, Stream_ ) == Success ) { // store information for all subsequent calls SetPaired( false ); OurField_->SetCallback( &Caller_ ); SetInterrupt( true ); // flag the state SetOpen(); SetDefaultQue(); return Success; } return Fail; } /* Method: VideoChannel::CloseChannel * Purpose: Closes the channel. Makes sure everything is freed * Input: * Output: */ ErrorCode VideoChannel::CloseChannel() { Trace t("VideoChannel::CloseChannel()"); if ( !IsOpen() ) return Fail; Stop( ); while( !BufQue_.IsEmpty( ) ) { DataBuf buf = BufQue_.Get(); } BufQue_.Flush(); while( !Requests_.IsEmpty( ) ) { PHW_STREAM_REQUEST_BLOCK pSrb = Requests_.Get(); if ( RemoveSRB( pSrb )) { DebugOut((0, " RemoveSRB failed\n")); DEBUG_BREAKPOINT(); } } Requests_.Flush(); SetClose(); return Success; } /* Method: VideoChannel::SetFormat * Purpose: * Input: * Output: */ ErrorCode VideoChannel::SetFormat( ColFmt aFormat ) { Trace t("VideoChannel::SetFormat()"); Digitizer_->SetPixelFormat( aFormat, *OurField_ ); return Success; } /* Method: VideoChannel::GetFormat * Purpose: * Input: * Output: */ ColFmt VideoChannel::GetFormat() { Trace t("VideoChannel::GetFormat()"); return Digitizer_->GetPixelFormat( *OurField_ ); } /* Method: VideoChannel::AddBuffer * Purpose: This function adds a buffer to a queue * Input: pNewBuffer: PVOID - pointer to a buffer to add * Output: None * Note: This function 'does not know' where the queue is located. It just uses * a pointer to it. */ void VideoChannel::AddBuffer( PVOID pPacket ) { Trace t("VideoChannel::AddBuffer()"); DataBuf buf( GetSRB(), pPacket ); BufQue_.Put( buf ); DebugOut((1, "AddBuf %x\n", pPacket ) ); LONGLONG *pB1 = (LONGLONG *)pPacket; LONGLONG *pB2 = pB1 + 1; #ifdef DEBUG for ( UINT i = 0; i < 640; i++ ) { #endif *pB1 = 0xAAAAAAAA33333333; *pB2 = 0xBBBBBBBB22222222; #ifdef DEBUG pB1 += 2; pB2 += 2; } #endif } /* Method: VideoChannel::ResetCounters * Purpose: Reset the frame info counters * Input: None * Output: None */ VOID VideoChannel::ResetCounters( ) { ULONG StreamNumber = Stream_; if ( StreamNumber == STREAM_IDX_VBI ) { PKS_VBI_FRAME_INFO pSavedFrameInfo = &((PSTREAMEX)GetStrmEx())->FrameInfo.VbiFrameInfo; pSavedFrameInfo->ExtendedHeaderSize = sizeof( KS_VBI_FRAME_INFO ); pSavedFrameInfo->PictureNumber = 0; pSavedFrameInfo->DropCount = 0; } else { PKS_FRAME_INFO pSavedFrameInfo = &((PSTREAMEX)GetStrmEx())->FrameInfo.VideoFrameInfo; pSavedFrameInfo->ExtendedHeaderSize = sizeof( KS_FRAME_INFO ); pSavedFrameInfo->PictureNumber = 0; pSavedFrameInfo->DropCount = 0; } } /* Method: VideoChannel::TimeStamp * Purpose: Performs the standard buffer massaging when it's done * Input: pSrb * Output: None */ void STREAMAPI VideoChannel::TimeStamp( PHW_STREAM_REQUEST_BLOCK pSrb ) { Trace t("VideoChannel::TimeStamp()"); PKSSTREAM_HEADER pDataPacket = pSrb->CommandData.DataBufferArray; VideoChannel *chan = (VideoChannel *)((PSTREAMEX)pSrb->StreamObject->HwStreamExtension)->videochannel; pDataPacket->PresentationTime.Numerator = 1; pDataPacket->PresentationTime.Denominator = 1; if( chan->IsVideoInfo2() ) { pDataPacket->DataUsed = chan->GetVidHdr2()->bmiHeader.biSizeImage; } else { pDataPacket->DataUsed = chan->GetVidHdr()->bmiHeader.biSizeImage; } pDataPacket->Duration = chan->GetTimePerFrame(); DebugOut((1, "DataUsed = %d\n", pDataPacket->DataUsed)); // [TMZ] [!!!] - hack, timestamping seems broken if( 0 ) { //if( hMasterClock ) { pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID; //pDataPacket->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; HW_TIME_CONTEXT TimeContext; TimeContext.HwDeviceExtension = (struct _HW_DEVICE_EXTENSION *)pSrb->HwDeviceExtension; TimeContext.HwStreamObject = pSrb->StreamObject; TimeContext.Function = TIME_GET_STREAM_TIME; StreamClassQueryMasterClockSync ( chan->hMasterClock, &TimeContext ); /* LARGE_INTEGER Delta; Delta.QuadPart = TimeContext.Time; if( TimeContext.Time > (ULONGLONG) Delta.QuadPart ) { pDataPacket->PresentationTime.Time = TimeContext.Time; } else { pDataPacket->PresentationTime.Time = 0; } */ pDataPacket->PresentationTime.Time = TimeContext.Time; } else { pDataPacket->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; pDataPacket->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; pDataPacket->PresentationTime.Time = 0; } // now gather the statistics PKS_FRAME_INFO pSavedFrameInfo = &((PSTREAMEX)chan->GetStrmEx())->FrameInfo.VideoFrameInfo; pSavedFrameInfo->ExtendedHeaderSize = sizeof( KS_FRAME_INFO ); pSavedFrameInfo->PictureNumber++; pSavedFrameInfo->DropCount = 0; PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO) ( pSrb->CommandData.DataBufferArray + 1 ); // copy the information to the outbound buffer pFrameInfo->ExtendedHeaderSize = pSavedFrameInfo->ExtendedHeaderSize; pFrameInfo->PictureNumber = pSavedFrameInfo->PictureNumber; pFrameInfo->DropCount = pSavedFrameInfo->DropCount; if ( pFrameInfo->DropCount ) { pSrb->CommandData.DataBufferArray->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY; } // 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; // make the stream class driver happy pSrb->Status = STATUS_SUCCESS; DebugOut((1, "*** 2 *** completing SRB %x\n", pSrb)); CheckSrbStatus( pSrb ); StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb ); DebugOut((1, "Signal SRB - %x\n", pSrb->CommandData.DataBufferArray->Data ) ); DebugOut((1, "********** NeedNotification_ = %d\n", chan->NeedNotification_ ) ); if ( chan->NeedNotification_ ) { // queue was full; now it has at least one entry StreamClassStreamNotification( ReadyForNextStreamDataRequest, pSrb->StreamObject ); } } /* Method: VideoChannel::Interrupt * Purpose: Called by the interface class on behalf of capture chip to let know * an interrupt happened. * Input: pTag: PVOID, to be passed to the Digitizer_ * Output: None */ void VideoChannel::Interrupt( PVOID pTag, bool skipped ) { Trace t("VideoChannel::Interrupt()"); Digitizer_->ProcessBufferAtInterrupt( pTag ); if ( skipped ) { DebugOut((1, "VidChan::Interrupt skipped\n" ) ); return; } // let the class driver know we are done with this buffer if ( !Requests_.IsEmpty() ) { PHW_STREAM_REQUEST_BLOCK pSrb = Requests_.Get(); TimeStamp( pSrb ); // [TMZ] [!!!] [HACK] } } /* Method: VideoChannel::Create * Purpose: Creates the stream * Input: None * Output: None */ ErrorCode VideoChannel::Create() { Trace t("VideoChannel::Create()"); KS_VIDEOINFOHEADER* pVideoInfoHdr = NULL; KS_VIDEOINFOHEADER2* pVideoInfoHdr2 = NULL; DWORD biCompression; WORD biBitCount; LONG biWidth; LONG biHeight; LONG biWidthBytes; if( IsVideoInfo2() ) { pVideoInfoHdr2 = GetVidHdr2(); biCompression = pVideoInfoHdr2->bmiHeader.biCompression; biBitCount = pVideoInfoHdr2->bmiHeader.biBitCount; biWidth = pVideoInfoHdr2->bmiHeader.biWidth; biHeight = abs(pVideoInfoHdr2->bmiHeader.biHeight); } else { pVideoInfoHdr = GetVidHdr(); biCompression = pVideoInfoHdr->bmiHeader.biCompression; biBitCount = pVideoInfoHdr->bmiHeader.biBitCount; biWidth = pVideoInfoHdr->bmiHeader.biWidth; biHeight = abs(pVideoInfoHdr->bmiHeader.biHeight); } MRect analog( 0, 0, biWidth, biHeight ); MRect ImageRect( 0, 0, biWidth, biHeight ); DebugOut((1, "**************************************************************************\n")); DebugOut((1, "biCompression = %d\n", biCompression)); DebugOut((1, "biBitCount = %d\n", biBitCount)); if ( pVideoInfoHdr->bmiHeader.biCompression == 3) { if( IsVideoInfo2() ) { pVideoInfoHdr2->bmiHeader.biCompression = FCC_YUY2; biCompression = FCC_YUY2; } else { pVideoInfoHdr->bmiHeader.biCompression = FCC_YUY2; biCompression = FCC_YUY2; } } ColorSpace tmp( biCompression, biBitCount ); DebugOut((1, "ColorFormat = %d\n", tmp.GetColorFormat())); DebugOut((1, "**************************************************************************\n")); OurField_->ResetCounters(); ResetCounters(); // verify that we are not asked to produce a smaller image #ifdef HACK_FUDGE_RECTANGLES if( IsVideoInfo2() ) { if( pVideoInfoHdr2->rcTarget.bottom == 0 ) { // [!!!] [TMZ] - hack pVideoInfoHdr2->rcTarget.left = 0; pVideoInfoHdr2->rcTarget.top = 0; pVideoInfoHdr2->rcTarget.right = biWidth; pVideoInfoHdr2->rcTarget.bottom = biHeight; } } else { if( pVideoInfoHdr->rcTarget.bottom == 0 ) { // [!!!] [TMZ] - hack pVideoInfoHdr->rcTarget.left = 0; pVideoInfoHdr->rcTarget.top = 0; pVideoInfoHdr->rcTarget.right = biWidth; pVideoInfoHdr->rcTarget.bottom = biHeight; } } #endif MRect dst; MRect src; if( IsVideoInfo2() ) { dst.Set( pVideoInfoHdr2->rcTarget.left, pVideoInfoHdr2->rcTarget.top, pVideoInfoHdr2->rcTarget.right, pVideoInfoHdr2->rcTarget.bottom ); src.Set( pVideoInfoHdr2->rcSource.left, pVideoInfoHdr2->rcSource.top, pVideoInfoHdr2->rcSource.right, pVideoInfoHdr2->rcSource.bottom ); } else { dst.Set( pVideoInfoHdr->rcTarget.left, pVideoInfoHdr->rcTarget.top, pVideoInfoHdr->rcTarget.right, pVideoInfoHdr->rcTarget.bottom ); src.Set( pVideoInfoHdr->rcSource.left, pVideoInfoHdr->rcSource.top, pVideoInfoHdr->rcSource.right, pVideoInfoHdr->rcSource.bottom ); } if ( !dst.IsEmpty() ) { // use the new size ImageRect = dst; if ( !src.IsEmpty() ) { analog = src; } else { analog = dst; } // calculate the offset for the new beginning of the data dwBufferOffset_ = dst.top * biWidth + dst.left * tmp.GetPitchBpp(); // when rcTarget is non-empty, biWidth is stride of the buffer biWidthBytes = biWidth; } else { biWidthBytes = biWidth * tmp.GetPitchBpp() / 8; } if( IsVideoInfo2() ) { DebugOut((1, "pVideoInfoHdr2->rcTarget(%d, %d, %d, %d)\n", pVideoInfoHdr2->rcTarget.left, pVideoInfoHdr2->rcTarget.top, pVideoInfoHdr2->rcTarget.right, pVideoInfoHdr2->rcTarget.bottom )); } else { DebugOut((1, "pVideoInfoHdr->rcTarget(%d, %d, %d, %d)\n", pVideoInfoHdr->rcTarget.left, pVideoInfoHdr->rcTarget.top, pVideoInfoHdr->rcTarget.right, pVideoInfoHdr->rcTarget.bottom )); } DebugOut((1, "dst(%d, %d, %d, %d)\n", dst.left, dst.top, dst.right, dst.bottom )); DebugOut((1, "Pitch =%d, width = %d\n", biWidthBytes, dst.Width() ) ); SetBufPitch( biWidthBytes ); if ( SetAnalogWindow ( analog ) == Success && //<-must be set first ! SetDigitalWindow( ImageRect ) == Success && SetFormat( tmp.GetColorFormat() ) == Success && Digitizer_->Create( *OurField_ ) == Success ) { State_ = Created; return Success; } return Fail; } /* Method: VideoChannel::Start * Purpose: Starts the stream * Input: None * Output: None */ void VideoChannel::Start() { Trace t("VideoChannel::Start()"); State_ = Started; Digitizer_->Start( *OurField_ ); } /* Method: VideoChannel::Stop * Purpose: Stops the stream * Input: None * Output: None */ ErrorCode VideoChannel::Stop() { Trace t("VideoChannel::Stop()"); if ( !IsOpen() ) return Fail; Digitizer_->Stop( *OurField_ ); State_ = Open; while( !BufQue_.IsEmpty( ) ) { DataBuf buf = BufQue_.Get(); } BufQue_.Flush(); return Success; } /* Method: VideoChannel::Pause * Purpose: Stops the stream * Input: None * Output: None */ ErrorCode VideoChannel::Pause() { Trace t("VideoChannel::Pause()"); Digitizer_->Pause( *OurField_ ); State_ = Paused; OurField_->ResetCounters(); // jaybo ResetCounters(); return Success; } /* Method: VideoChanIface::Notify * Purpose: Notifies the VideoChannel that an interrupt happened * Input: None * Output: None */ void VideoChanIface::Notify( PVOID pTag, bool skipped ) { Trace t("VideoChanIface::Notify()"); ToBeNotified_->Interrupt( pTag, skipped ); } /* Method: VideoChannel::AddSRB * Purpose: Adds SRB and buffer to the queues * Input: pSrb * Output: None */ void VideoChannel::AddSRB( PHW_STREAM_REQUEST_BLOCK pSrb ) { Trace t("VideoChannel::AddSRB()"); Requests_.Put( pSrb ); SetSRB( pSrb ); PUCHAR pBufAddr = (PUCHAR)pSrb->CommandData.DataBufferArray->Data; AddBuffer( pBufAddr + dwBufferOffset_ ); // don't forget to report our field type ! // this cast is valid for VBI FRAME as well ( see ksmedia.h ) PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO) ( pSrb->CommandData.DataBufferArray + 1 ); pFrameInfo->dwFrameFlags = FieldType_; // ask for more buffers CheckNotificationNeed(); } /* Method: VideoChannel::RemoveSRB * Purpose: Removes SRB from the queue and signals it * Input: pSrb * Output: None */ bool VideoChannel::RemoveSRB( PHW_STREAM_REQUEST_BLOCK pSrb ) { Trace t("VideoChannel::RemoveSRB()"); /* //FGR - TODO: i guess we should see if there really is a record of this SRB if(Requests_.IsEmpty()){ pSrb->Status = STATUS_CANCELLED; DebugOut((1, "*** 3 *** completing SRB %x\n", pSrb)); CheckSrbStatus( pSrb ); StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb ); //StreamClassStreamNotification( ReadyForNextStreamDataRequest, pSrb->StreamObject ); return( true ); } */ int n = 0; n = Requests_.GetNumOfItems(); DebugOut((1, "VideoChannel::RemoveSRB - Found %d SRBs in queue\n", n)); bool bFound = false; // cycle through the list // pull from the head, put to the tail // if we find our pSrb during one cycle, pull it out while ( n-- > 0 ) // yes it can go negative { PHW_STREAM_REQUEST_BLOCK pTempSrb = Requests_.Get(); if ( pTempSrb == pSrb ) { // Pull him out if ( bFound ) { DebugOut((0, "Found pSrb(%x) in the queue more than once\n", pSrb)); DEBUG_BREAKPOINT(); } else { bFound = true; pSrb->Status = STATUS_CANCELLED; DebugOut((1, "*** 4 *** completing SRB %x\n", pSrb)); CheckSrbStatus( pSrb ); StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb ); //StreamClassStreamNotification( ReadyForNextStreamDataRequest, pSrb->StreamObject ); } n--; // warning: if this is the last, it will go negative } else { Requests_.Put( pTempSrb ); } } n = Requests_.GetNumOfItems(); DebugOut((1, "VideoChannel::RemoveSRB - Left %d SRBs in queue, returning %d\n", n, bFound)); /* PHW_STREAM_REQUEST_BLOCK InQueSRB = Requests_.PeekLeft(); if ( InQueSRB == pSrb ) { InQueSRB = Requests_.Get(); InQueSRB->Status = STATUS_CANCELLED; DebugOut((1, "Cancel SRB -%x\n", pSrb ) ); CheckSrbStatus( pSrb ); StreamClassStreamNotification( StreamRequestComplete, InQueSRB->StreamObject, InQueSRB ); if ( Requests_.IsEmpty() ) DebugOut((1, " queue is empty\n" ) ); else DebugOut((1, "queue is not empty\n" ) ); return( true ); } else { // DebugOut((1, "Cancelling wrong SRB ! - %x, %x\n", pSrb, InQueSRB ) ); //#ifdef HAUPPAUGE // TRAP(); //#endif // } InQueSRB = Requests_.PeekRight(); if ( InQueSRB == pSrb ) { InQueSRB = Requests_.GetRight(); InQueSRB->Status = STATUS_CANCELLED; DebugOut((1, "Cancel SRB from right - %x\n", pSrb ) ); CheckSrbStatus( pSrb ); StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb ); return( true ); } else { DebugOut((0, "Cancelling wrong SRB from right too! - %x, %x\n", pSrb, InQueSRB ) ); return( false ); } } */ return( bFound ); } VideoChannel::~VideoChannel() { Trace t("VideoChannel::~VideoChannel()"); CloseChannel(); } /* Method: VideoChannel::CheckNotificationNeed * Purpose: Sees if there is room for more buffers * Input: None * Output: None */ void VideoChannel::CheckNotificationNeed() { Trace t("VideoChannel::CheckNotificationNeed()"); if ( !BufQue_.IsFull() ) { // always hungry for more StreamClassStreamNotification( ReadyForNextStreamDataRequest, pSRB_->StreamObject ); NeedNotification_ = false; } else NeedNotification_ = true; } /* Method: InterVideoChannel::Interrupt * Purpose: Processes the interrupt for the interleaved video streams * Input: pTag: PVOID - index in reality * skipped: bool - indicates if buffer was written to * Output: None */ void InterVideoChannel::Interrupt( PVOID pTag, bool skipped ) { Trace t("InterVideoChannel::Interrupt()"); int idx = (int)pTag; slave.IntNotify( PVOID( idx - ProgsWithinField ), skipped ); Parent::Interrupt( pTag, skipped ); } /* Method: InterVideoChannel::AddSRB * Purpose: Adds SRB to itself and dispatches 2 buffer pointers, one to each * channel * Input: pSRB * Output: None */ void InterVideoChannel::AddSRB( PHW_STREAM_REQUEST_BLOCK pSrb ) { Trace t("InterVideoChannel::AddSRB()"); PUCHAR pBufAddr = (PUCHAR)pSrb->CommandData.DataBufferArray->Data; // biWidth was set in Create() UINT biWidthBytes; if( IsVideoInfo2() ) { biWidthBytes = VidHeader2_.bmiHeader.biWidth / 2; } else { biWidthBytes = VidHeader_.bmiHeader.biWidth / 2; } // to be used when adding buffer SetSRB( pSrb ); slave.SetSRB( pSrb ); // need to swap addresses for even/odd fields for RGB formats due to up-side-down bitmaps ColorSpace tmp( GetFormat() ); if ( !( tmp.GetColorFormat() > CF_RGB8 && tmp.GetColorFormat() < CF_VBI ) ) { // put buffer in its place // and adjusted address into the other channel slave.AddBuffer( pBufAddr + biWidthBytes ); AddBuffer( pBufAddr ); } else { slave.AddBuffer( pBufAddr ); AddBuffer( pBufAddr + biWidthBytes ); } // don't forget to add the SRB ! Requests_.Put( pSrb ); // set field type to full frame. PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO)( pSrb->CommandData.DataBufferArray + 1 ); pFrameInfo->dwFrameFlags = KS_VIDEO_FLAG_FRAME; CheckNotificationNeed(); } /* Function: SplitFrame * Purpose: Halfs the size of the video image so 2 fields can be used to create * the original size * Input: VidHdr: KS_VIDEOINFOHEADER & * Output: None */ inline void SplitFrame( KS_VIDEOINFOHEADER &VidHdr ) { Trace t("SplitFrame()"); VidHdr.bmiHeader.biHeight /= 2; VidHdr.rcSource.top /= 2; VidHdr.rcTarget.top /= 2; VidHdr.rcSource.bottom /= 2; VidHdr.rcTarget.bottom /= 2; } inline void SplitFrame2( KS_VIDEOINFOHEADER2 &VidHdr2 ) { Trace t("SplitFrame()"); VidHdr2.bmiHeader.biHeight /= 2; VidHdr2.rcSource.top /= 2; VidHdr2.rcTarget.top /= 2; VidHdr2.rcSource.bottom /= 2; VidHdr2.rcTarget.bottom /= 2; } /* Method: InterVideoChannel::Create * Purpose: Sets the video parameters for the slave channel and * calls into parent to create both * Input: None * Output: None */ ErrorCode InterVideoChannel::Create() { Trace t("InterVideoChannel::Create()"); // slave.SetInterrupt( false ); slave.SetCallback( 0 ); // restore the original as SplitFrame mangles the parameters MRect dst; DWORD biCompression; WORD biBitCount; LONG biWidthBytes; if( IsVideoInfo2() ) { VidHeader2_ = OrigVidHeader2_; // split a frame into two fields SplitFrame2( VidHeader2_ ); // double up the pitch, so we can interleave the buffers dst.Set( VidHeader2_.rcTarget.left, VidHeader2_.rcTarget.top, VidHeader2_.rcTarget.right, VidHeader2_.rcTarget.bottom ); biCompression = VidHeader2_.bmiHeader.biCompression; biBitCount = VidHeader2_.bmiHeader.biBitCount; } else { VidHeader_ = OrigVidHeader_; // split a frame into two fields SplitFrame( VidHeader_ ); // double up the pitch, so we can interleave the buffers dst.Set( VidHeader_.rcTarget.left, VidHeader_.rcTarget.top, VidHeader_.rcTarget.right, VidHeader_.rcTarget.bottom ); biCompression = VidHeader_.bmiHeader.biCompression; biBitCount = VidHeader_.bmiHeader.biBitCount; } ColorSpace tmp( biCompression, biBitCount ); if ( !dst.IsEmpty() ) { // biWidth is the stride in bytes if( IsVideoInfo2() ) { VidHeader2_.bmiHeader.biWidth *= 2 * 2; biWidthBytes = VidHeader2_.bmiHeader.biWidth; } else { VidHeader_.bmiHeader.biWidth *= 2 * 2; biWidthBytes = VidHeader_.bmiHeader.biWidth; } } else { if( IsVideoInfo2() ) { // calculate the number of bytes per scan line biWidthBytes = tmp.GetPitchBpp() * VidHeader2_.bmiHeader.biWidth / 8; // can it be non-aligned ?? biWidthBytes += 3; biWidthBytes &= ~3; // must be increased two times to interleave the fields; biWidthBytes *= 2; // the rcTarget uses half the original height and full width VidHeader2_.rcTarget = MRect( 0, 0, VidHeader2_.bmiHeader.biWidth, abs(VidHeader2_.bmiHeader.biHeight) ); DebugOut((1, "VidHeader2_.rcTarget(%d, %d, %d, %d)\n", VidHeader2_.rcTarget.left, VidHeader2_.rcTarget.top, VidHeader2_.rcTarget.right, VidHeader2_.rcTarget.bottom )); // have to trick the slave into using correct ( doubled ) pitch VidHeader2_.bmiHeader.biWidth = biWidthBytes; // this is the pitch slave uses } else { // calculate the number of bytes per scan line biWidthBytes = tmp.GetPitchBpp() * VidHeader_.bmiHeader.biWidth / 8; // can it be non-aligned ?? biWidthBytes += 3; biWidthBytes &= ~3; // must be increased two times to interleave the fields; biWidthBytes *= 2; // the rcTarget uses half the original height and full width VidHeader_.rcTarget = MRect( 0, 0, VidHeader_.bmiHeader.biWidth, abs(VidHeader_.bmiHeader.biHeight) ); DebugOut((1, "VidHeader_.rcTarget(%d, %d, %d, %d)\n", VidHeader_.rcTarget.left, VidHeader_.rcTarget.top, VidHeader_.rcTarget.right, VidHeader_.rcTarget.bottom )); // have to trick the slave into using correct ( doubled ) pitch VidHeader_.bmiHeader.biWidth = biWidthBytes; // this is the pitch slave uses } } SetBufPitch( biWidthBytes ); // at this point slave will have all the members set up properly if( IsVideoInfo2() ) { slave.SetVidHdr2( VidHeader2_ ); } else { slave.SetVidHdr( VidHeader_ ); } slave.SetPaired( true ); // needed for full-size YUV9 and other planar modes Digitizer_->SetPlanarAdjust( biWidthBytes / 2 ); return Parent::Create(); } /* Method: VideoChannel::GetStreamType * Purpose: reports back type of the stream. Used when destroying channels */ StreamType VideoChannel::GetStreamType() { Trace t("VideoChannel::GetStreamType()"); return Single; } /* Method: VideoChannel::TimeStampVBI * Purpose: Performs the standard buffer massaging when it's done * Input: pSrb * Output: None */ void STREAMAPI VideoChannel::TimeStampVBI( PHW_STREAM_REQUEST_BLOCK pSrb ) { Trace t("VideoChannel::TimeStamp()"); PKSSTREAM_HEADER pDataPacket = pSrb->CommandData.DataBufferArray; VideoChannel *chan = (VideoChannel *)((PSTREAMEX)pSrb->StreamObject->HwStreamExtension)->videochannel; pDataPacket->PresentationTime.Numerator = 1; pDataPacket->PresentationTime.Denominator = 1; if( chan->IsVideoInfo2() ) { pDataPacket->DataUsed = chan->GetVidHdr2()->bmiHeader.biSizeImage; } else { pDataPacket->DataUsed = chan->GetVidHdr()->bmiHeader.biSizeImage; } pDataPacket->Duration = chan->GetTimePerFrame(); DebugOut((1, "DataUsed = %d\n", pDataPacket->DataUsed)); // [TMZ] [!!!] - hack, timestamping seems broken if( 0 ) { //if( hMasterClock ) { pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; pDataPacket->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID; //pDataPacket->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; HW_TIME_CONTEXT TimeContext; TimeContext.HwDeviceExtension = (struct _HW_DEVICE_EXTENSION *)pSrb->HwDeviceExtension; TimeContext.HwStreamObject = pSrb->StreamObject; TimeContext.Function = TIME_GET_STREAM_TIME; StreamClassQueryMasterClockSync ( chan->hMasterClock, &TimeContext ); /* LARGE_INTEGER Delta; Delta.QuadPart = TimeContext.Time; if( TimeContext.Time > (ULONGLONG) Delta.QuadPart ) { pDataPacket->PresentationTime.Time = TimeContext.Time; } else { pDataPacket->PresentationTime.Time = 0; } */ pDataPacket->PresentationTime.Time = TimeContext.Time; } else { pDataPacket->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; pDataPacket->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; pDataPacket->PresentationTime.Time = 0; } PKS_VBI_FRAME_INFO pSavedFrameInfo = &((PSTREAMEX)chan->GetStrmEx())->FrameInfo.VbiFrameInfo; pSavedFrameInfo->ExtendedHeaderSize = sizeof( PKS_VBI_FRAME_INFO ); pSavedFrameInfo->PictureNumber++; pSavedFrameInfo->DropCount = 0; // now gather the statistics PKS_VBI_FRAME_INFO pFrameInfo = (PKS_VBI_FRAME_INFO) ( pSrb->CommandData.DataBufferArray + 1 ); // copy the information to the outbound buffer pFrameInfo->ExtendedHeaderSize = pSavedFrameInfo->ExtendedHeaderSize; pFrameInfo->PictureNumber = pSavedFrameInfo->PictureNumber; pFrameInfo->DropCount = pSavedFrameInfo->DropCount; pFrameInfo->dwSamplingFrequency = VBISampFreq; // Bug - changes with video format if ( ((VBIChannel*)(chan))->Dirty_ ) { // propagate the tv tuner change notification ((VBIChannel*)(chan))->Dirty_ = false; pFrameInfo->TvTunerChangeInfo = ((VBIChannel*)(chan))->TVTunerChangeInfo_; pFrameInfo->dwFrameFlags |= KS_VBI_FLAG_TVTUNER_CHANGE; pFrameInfo->VBIInfoHeader = ((VBIChannel*)(chan))->VBIInfoHeader_; pFrameInfo->dwFrameFlags |= KS_VBI_FLAG_VBIINFOHEADER_CHANGE ; } else { pFrameInfo->dwFrameFlags &= ~KS_VBI_FLAG_TVTUNER_CHANGE; pFrameInfo->dwFrameFlags &= ~KS_VBI_FLAG_VBIINFOHEADER_CHANGE; } if ( pFrameInfo->DropCount ) { pSrb->CommandData.DataBufferArray->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY; } // 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; // make the stream class driver happy pSrb->Status = STATUS_SUCCESS; DebugOut((1, "*** 5 *** completing SRB %x\n", pSrb)); CheckSrbStatus( pSrb ); StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb ); DebugOut((1, "Signal SRB - %x\n", pSrb->CommandData.DataBufferArray->Data ) ); DebugOut((1, "********** NeedNotification_ = %d\n", chan->NeedNotification_ ) ); if ( chan->NeedNotification_ ) { // queue was full; now it has at least one entry StreamClassStreamNotification( ReadyForNextStreamDataRequest, pSrb->StreamObject ); } } /* Method: VBIAlterChannel::Interrupt * Purpose: Processes the interrupt for the VBI channel */ void VBIChannel::Interrupt( PVOID pTag, bool skipped ) { Trace t("VBIChannel::Interrupt()"); if ( Requests_.IsEmpty( ) ) { DebugOut((1, "VBI interrupt, but Requests_ is empty\n")); return; } // save the SRB for further processing ( it is gone from the qu in the Parent::Interrupt PHW_STREAM_REQUEST_BLOCK pSrb = Requests_.PeekLeft(); // Parent::Interrupt( pTag, skipped ); { Digitizer_->ProcessBufferAtInterrupt( pTag ); if ( skipped ) { DebugOut((1, "VidChan::Interrupt skipped\n" ) ); return; } // let the class driver know we are done with this buffer if ( !Requests_.IsEmpty() ) { PHW_STREAM_REQUEST_BLOCK pTimeSrb = Requests_.Get(); TimeStampVBI( pTimeSrb ); // [TMZ] [!!!] } } } /* Method: VBIChannel::ChangeNotification * Purpose: Called to save off the tv tuner change notification * Input: pSrb */ void VBIChannel::ChangeNotification( PHW_STREAM_REQUEST_BLOCK pSrb ) { Trace t("VBIChannel::ChangeNotification()"); const KSSTREAM_HEADER &DataPacket = *pSrb->CommandData.DataBufferArray; RtlCopyMemory( &TVTunerChangeInfo_, DataPacket.Data, sizeof( KS_TVTUNER_CHANGE_INFO ) ); Dirty_ = true; } /* Method: VideoChannel::ChangeNotification * Purpose: Noop for the base class. */ void VideoChannel::ChangeNotification( PHW_STREAM_REQUEST_BLOCK ) { Trace t("VideoChannel::ChangeNotification()"); } /* Method: VBIAlterChannel::SetVidHdr * Purpose: Transforms the VBI parameters ( size ) into regular video header * Input: */ void VBIAlterChannel::SetVidHdr( const KS_DATAFORMAT_VBIINFOHEADER &df ) { Trace t("VBIAlterChannel::SetVidHdr()"); // save for the history ( for the interrupt, actually ) SetVBIInfHdr( df.VBIInfoHeader ); (*(VBIChannel*)&slave).SetVBIInfHdr( df.VBIInfoHeader ); KS_VIDEOINFOHEADER VidInfHdr; RtlZeroMemory( &VidInfHdr, sizeof( VidInfHdr ) ); // create a regular video info header VidInfHdr.bmiHeader.biWidth = VBISamples; VidInfHdr.bmiHeader.biHeight = df.VBIInfoHeader.EndLine - df.VBIInfoHeader.StartLine + 1; // inclusive // taken from the VBI GUID VidInfHdr.bmiHeader.biCompression = FCC_VBI; VidInfHdr.bmiHeader.biBitCount = 8; // this is very important too VidInfHdr.bmiHeader.biSizeImage = VidInfHdr.bmiHeader.biWidth * VidInfHdr.bmiHeader.biHeight; // now handle the case when stride is larger than width ( have to set the // target rectangle ) if ( df.VBIInfoHeader.StrideInBytes > VBISamples ) { VidInfHdr.rcTarget.right = df.VBIInfoHeader.StrideInBytes; VidInfHdr.rcTarget.bottom = VidInfHdr.bmiHeader.biHeight; } // the Parent::Create will take care of setting vid header for the slave Parent::SetVidHdr( VidInfHdr ); } //??? TODO: -- is this needed? void VBIAlterChannel::SetVidHdr2( const KS_DATAFORMAT_VBIINFOHEADER &df ) { Trace t("VBIAlterChannel::SetVidHdr2()"); // save for the history ( for the interrupt, actually ) SetVBIInfHdr( df.VBIInfoHeader ); KS_VIDEOINFOHEADER2 VidInfHdr; RtlZeroMemory( &VidInfHdr, sizeof( VidInfHdr ) ); // create a regular video info header VidInfHdr.bmiHeader.biWidth = VBISamples; VidInfHdr.bmiHeader.biHeight = df.VBIInfoHeader.EndLine - df.VBIInfoHeader.StartLine + 1; // inclusive // taken from the VBI GUID VidInfHdr.bmiHeader.biCompression = FCC_VBI; VidInfHdr.bmiHeader.biBitCount = 8; // this is very important too VidInfHdr.bmiHeader.biSizeImage = VidInfHdr.bmiHeader.biWidth * VidInfHdr.bmiHeader.biHeight; // now handle the case when stride is larger than width ( have to set the // target rectangle ) if ( df.VBIInfoHeader.StrideInBytes > VBISamples ) { VidInfHdr.rcTarget.right = df.VBIInfoHeader.StrideInBytes; VidInfHdr.rcTarget.bottom = VidInfHdr.bmiHeader.biHeight; } // the Parent::Create will take care of setting vid header for the slave Parent::SetVidHdr2( VidInfHdr ); }