You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1295 lines
33 KiB
1295 lines
33 KiB
//@@@@AUTOBLOCK+============================================================;
|
|
//
|
|
// 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.
|
|
//
|
|
// File: black.cpp
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//@@@@AUTOBLOCK-============================================================;
|
|
|
|
|
|
// !!! use 2 RO buffers, fill with black by calling GetBuffer twice
|
|
|
|
#include <streams.h>
|
|
#include <qeditint.h>
|
|
#include <qedit.h>
|
|
#include "black.h"
|
|
#include "..\util\conv.cxx"
|
|
#include "..\util\filfuncs.h"
|
|
#include "..\util\dexmisc.h"
|
|
|
|
#define MAXBUFFERCNT 2
|
|
|
|
// Setup data
|
|
|
|
const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
|
|
{
|
|
&MEDIATYPE_Video, // Major type
|
|
&MEDIASUBTYPE_NULL // Minor type
|
|
};
|
|
|
|
const AMOVIESETUP_PIN sudOpPin =
|
|
{
|
|
L"Output", // Pin string name
|
|
FALSE, // Is it rendered
|
|
TRUE, // Is it an output
|
|
FALSE, // Can we have none
|
|
FALSE, // Can we have many
|
|
&CLSID_NULL, // Connects to filter
|
|
NULL, // Connects to pin
|
|
1, // Number of types
|
|
&sudOpPinTypes }; // Pin details
|
|
|
|
const AMOVIESETUP_FILTER sudBlkVid =
|
|
{
|
|
&CLSID_GenBlkVid, // Filter CLSID
|
|
L"Generate Solid Colour", // String name
|
|
MERIT_DO_NOT_USE, // Filter merit
|
|
1, // Number pins
|
|
&sudOpPin // Pin details
|
|
};
|
|
|
|
//
|
|
// CreateInstance
|
|
//
|
|
// Create GenBlkVid filter
|
|
//
|
|
CUnknown * WINAPI CGenBlkVid::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr)
|
|
{
|
|
CUnknown *punk = new CGenBlkVid(lpunk, phr);
|
|
if (punk == NULL) {
|
|
*phr = E_OUTOFMEMORY;
|
|
}
|
|
return punk;
|
|
|
|
} // CreateInstance
|
|
|
|
|
|
//
|
|
// Constructor
|
|
//
|
|
// Initialise a CBlkVidStream object so that we have a pin.
|
|
//
|
|
CGenBlkVid::CGenBlkVid(LPUNKNOWN lpunk, HRESULT *phr) :
|
|
CSource(NAME("Generate Solid Colour") ,lpunk,CLSID_GenBlkVid)
|
|
,CPersistStream(lpunk, phr)
|
|
|
|
{
|
|
DbgLog((LOG_TRACE,3,TEXT("BLACK::")));
|
|
|
|
CAutoLock cAutoLock(pStateLock());
|
|
|
|
m_paStreams = (CSourceStream **) new CBlkVidStream*[1];
|
|
if (m_paStreams == NULL) {
|
|
*phr = E_OUTOFMEMORY;
|
|
return;
|
|
}
|
|
|
|
m_paStreams[0] = new CBlkVidStream(phr, this, L"Generate Solid Colour");
|
|
if (m_paStreams[0] == NULL) {
|
|
*phr = E_OUTOFMEMORY;
|
|
return;
|
|
}
|
|
|
|
} // (Constructor)
|
|
|
|
|
|
CGenBlkVid::~CGenBlkVid()
|
|
{
|
|
DbgLog((LOG_TRACE,3,TEXT("~BLACK::")));
|
|
}
|
|
|
|
|
|
STDMETHODIMP CGenBlkVid::NonDelegatingQueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
CheckPointer(ppv,E_POINTER);
|
|
|
|
if (riid == IID_IPersistStream) {
|
|
return GetInterface((IPersistStream *) this, ppv);
|
|
} else if (riid == IID_IDispatch) {
|
|
return GetInterface((IDispatch *)this, ppv);
|
|
} else {
|
|
return CSource::NonDelegatingQueryInterface(riid, ppv);
|
|
}
|
|
} // NonDelegatingQueryInterface
|
|
|
|
|
|
|
|
// IDispatch
|
|
//
|
|
STDMETHODIMP CGenBlkVid::GetTypeInfoCount(unsigned int *)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CGenBlkVid::GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CGenBlkVid::GetIDsOfNames(const struct _GUID &guid,unsigned short **pName ,unsigned int num,unsigned long loc,long *pOut)
|
|
{
|
|
WCHAR *pw = *pName;
|
|
if(
|
|
(!DexCompareW(L"colour", pw))
|
|
||
|
|
(!DexCompareW(L"color", pw))
|
|
)
|
|
{
|
|
*pOut = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CGenBlkVid::Invoke(long dispid,const struct _GUID &,unsigned long,unsigned short,struct tagDISPPARAMS *pDisp,struct tagVARIANT *,struct tagEXCEPINFO *,unsigned int *)
|
|
{
|
|
if (dispid != 1)
|
|
return E_FAIL;
|
|
|
|
CBlkVidStream *pOutpin=( CBlkVidStream *)m_paStreams[0];
|
|
|
|
#define US_LCID MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
|
|
VARIANT v;
|
|
VariantInit(&v);
|
|
HRESULT hr = VariantChangeTypeEx(&v, pDisp->rgvarg, US_LCID, 0, VT_R8);
|
|
ASSERT(hr == S_OK);
|
|
|
|
double f = V_R8(&v);
|
|
pOutpin->m_dwRGBA = (DWORD)f;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// IPersistStream
|
|
|
|
// tell our clsid
|
|
//
|
|
STDMETHODIMP CGenBlkVid::GetClassID(CLSID *pClsid)
|
|
{
|
|
CheckPointer(pClsid, E_POINTER);
|
|
*pClsid = CLSID_GenBlkVid;
|
|
return S_OK;
|
|
}
|
|
|
|
typedef struct _BLKSave {
|
|
double dOutputFrmRate; // Output frm rate frames/second
|
|
LONG dwRGBA;
|
|
AM_MEDIA_TYPE mt; // format is hidden after the array
|
|
} BLKSav;
|
|
|
|
// persist ourself
|
|
//
|
|
HRESULT CGenBlkVid::WriteToStream(IStream *pStream)
|
|
{
|
|
DbgLog((LOG_TRACE,1,TEXT("CGenBlkVid::WriteToStream")));
|
|
|
|
CheckPointer(pStream, E_POINTER);
|
|
CheckPointer(m_paStreams[0], E_POINTER);
|
|
|
|
BLKSav *px;
|
|
CBlkVidStream *pOutpin=( CBlkVidStream *)m_paStreams[0];
|
|
|
|
//get current media type
|
|
CMediaType MyMt;
|
|
pOutpin->get_MediaType( &MyMt );
|
|
|
|
int savesize = sizeof(BLKSav) + MyMt.cbFormat;
|
|
|
|
DbgLog((LOG_TRACE,1,TEXT("Persisted data is %d bytes"), savesize));
|
|
|
|
px = (BLKSav *)QzTaskMemAlloc(savesize);
|
|
if (px == NULL) {
|
|
DbgLog((LOG_ERROR,1,TEXT("*** Out of memory")));
|
|
SaferFreeMediaType(MyMt);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
px->dOutputFrmRate = pOutpin->m_dOutputFrmRate;
|
|
px->dwRGBA = pOutpin->m_dwRGBA;
|
|
|
|
px->mt = MyMt;
|
|
// Can't persist pointers
|
|
px->mt.pbFormat = NULL;
|
|
px->mt.pUnk = NULL; // !!!
|
|
|
|
BYTE *pb;
|
|
pb=(BYTE *)(px)+sizeof(BLKSav);
|
|
|
|
// the format goes after the array
|
|
CopyMemory(pb, MyMt.pbFormat, MyMt.cbFormat);
|
|
|
|
HRESULT hr = pStream->Write(px, savesize, 0);
|
|
SaferFreeMediaType(MyMt);
|
|
QzTaskMemFree(px);
|
|
if(FAILED(hr)) {
|
|
DbgLog((LOG_ERROR,1,TEXT("*** WriteToStream FAILED")));
|
|
return hr;
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
// load ourself
|
|
//
|
|
HRESULT CGenBlkVid::ReadFromStream(IStream *pStream)
|
|
{
|
|
DbgLog((LOG_TRACE,1,TEXT("CenBlkVid::ReadFromStream")));
|
|
|
|
CheckPointer(pStream, E_POINTER);
|
|
CheckPointer(m_paStreams[0], E_POINTER);
|
|
|
|
int savesize=sizeof(BLKSav);
|
|
|
|
// we don't yet know how many saved connections there are
|
|
// all we know we have for sure is the beginning of the struct
|
|
BLKSav *px = (BLKSav *)QzTaskMemAlloc(savesize);
|
|
if (px == NULL) {
|
|
DbgLog((LOG_ERROR,1,TEXT("*** Out of memory")));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT hr = pStream->Read(px, savesize, 0);
|
|
if(FAILED(hr)) {
|
|
DbgLog((LOG_ERROR,1,TEXT("*** ReadFromStream FAILED")));
|
|
QzTaskMemFree(px);
|
|
return hr;
|
|
}
|
|
|
|
if(px->mt.cbFormat)
|
|
{
|
|
// how much saved data was there, really? Get the rest
|
|
savesize += px->mt.cbFormat;
|
|
px = (BLKSav *)QzTaskMemRealloc(px, savesize);
|
|
if (px == NULL) {
|
|
DbgLog((LOG_ERROR,1,TEXT("*** Out of memory")));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
}
|
|
DbgLog((LOG_TRACE,1,TEXT("Persisted data is %d bytes"), savesize));
|
|
|
|
BYTE *pb;
|
|
pb=(BYTE *)(px)+sizeof(BLKSav) ;
|
|
hr = pStream->Read(pb, (savesize-sizeof(BLKSav)), 0);
|
|
if(FAILED(hr)) {
|
|
DbgLog((LOG_ERROR,1,TEXT("*** ReadFromStream FAILED")));
|
|
QzTaskMemFree(px);
|
|
return hr;
|
|
}
|
|
|
|
CBlkVidStream *pOutpin=( CBlkVidStream *)m_paStreams[0];
|
|
|
|
pOutpin->put_OutputFrmRate(px->dOutputFrmRate);
|
|
pOutpin->put_RGBAValue(px->dwRGBA);
|
|
|
|
AM_MEDIA_TYPE MyMt = px->mt;
|
|
MyMt.pbFormat = (BYTE *)QzTaskMemAlloc(MyMt.cbFormat);
|
|
|
|
// remember, the format is after the array
|
|
CopyMemory(MyMt.pbFormat, pb, MyMt.cbFormat);
|
|
|
|
pOutpin->put_MediaType (&MyMt);
|
|
SaferFreeMediaType(MyMt);
|
|
QzTaskMemFree(px);
|
|
SetDirty(FALSE);
|
|
return S_OK;
|
|
}
|
|
|
|
// how big is our save data?
|
|
int CGenBlkVid::SizeMax()
|
|
{
|
|
if (!m_paStreams || !m_paStreams[0]) {
|
|
return 0;
|
|
|
|
}
|
|
CBlkVidStream *pOutpin=( CBlkVidStream *)m_paStreams[0];
|
|
int savesize = sizeof(BLKSav) + pOutpin->m_mtAccept.cbFormat;
|
|
return savesize;
|
|
}
|
|
|
|
|
|
//
|
|
// output pin Constructor
|
|
//
|
|
CBlkVidStream::CBlkVidStream(HRESULT *phr,
|
|
CGenBlkVid *pParent,
|
|
LPCWSTR pPinName) :
|
|
CSourceStream(NAME("Generate Solid Colour"),phr, pParent, pPinName),
|
|
m_iBufferCnt(0), //How many buffers we get
|
|
m_lDataLen (0), //output buffer data length
|
|
m_dOutputFrmRate(15.0),
|
|
m_dwRGBA( 0xFF000000 ),
|
|
m_ppbDstBuf(NULL),
|
|
m_fMediaTypeIsSet(FALSE),
|
|
m_pImportBuffer(NULL),
|
|
m_rtNewSeg(0), // last NewSeg given
|
|
// includes the NewSeg value
|
|
m_rtStartTime(0), // start at the beginning
|
|
m_rtDuration(MAX_TIME/1000),// MUST DEFAULT TO INFINITE LENGTH because
|
|
// dexter never sets a stop time! (don't be
|
|
// too big that math on it will overflow)
|
|
m_llSamplesSent(0)
|
|
{
|
|
pParent->m_pStream = this;
|
|
|
|
//build a default media type
|
|
ZeroMemory(&m_mtAccept, sizeof(AM_MEDIA_TYPE));
|
|
m_mtAccept.majortype = MEDIATYPE_Video;
|
|
m_mtAccept.subtype = MEDIASUBTYPE_ARGB32;
|
|
m_mtAccept.formattype = FORMAT_VideoInfo;
|
|
|
|
m_mtAccept.bFixedSizeSamples = TRUE;
|
|
m_mtAccept.bTemporalCompression = FALSE;
|
|
|
|
m_mtAccept.pbFormat = (BYTE *)QzTaskMemAlloc(sizeof(VIDEOINFOHEADER));
|
|
m_mtAccept.cbFormat = sizeof(VIDEOINFOHEADER);
|
|
ZeroMemory(m_mtAccept.pbFormat, m_mtAccept.cbFormat);
|
|
|
|
LPBITMAPINFOHEADER lpbi = HEADER(m_mtAccept.pbFormat);
|
|
lpbi->biSize = sizeof(BITMAPINFOHEADER);
|
|
lpbi->biCompression = BI_RGB;
|
|
lpbi->biBitCount = 32;
|
|
lpbi->biWidth = 320;
|
|
lpbi->biHeight = 240;
|
|
lpbi->biPlanes = 1;
|
|
lpbi->biSizeImage = DIBSIZE(*lpbi);
|
|
m_mtAccept.lSampleSize = DIBSIZE(*lpbi);
|
|
((VIDEOINFOHEADER *)(m_mtAccept.pbFormat))->AvgTimePerFrame = Frame2Time( 1, m_dOutputFrmRate );
|
|
((VIDEOINFOHEADER *)(m_mtAccept.pbFormat))->dwBitRate =
|
|
(DWORD)(DIBSIZE(*lpbi) * m_dOutputFrmRate);
|
|
|
|
} // (Constructor)
|
|
|
|
//X
|
|
// destructor
|
|
CBlkVidStream::~CBlkVidStream()
|
|
{
|
|
/* BUFFER POINTER */
|
|
if (m_ppbDstBuf) delete [] m_ppbDstBuf;
|
|
|
|
if (m_pImportBuffer){ delete [] m_pImportBuffer; m_pImportBuffer=NULL;};
|
|
|
|
SaferFreeMediaType(m_mtAccept); // in destructor
|
|
}
|
|
|
|
//
|
|
// IGenVideo, IDexterSequencer
|
|
// ISpecifyPropertyPages
|
|
//
|
|
STDMETHODIMP CBlkVidStream::NonDelegatingQueryInterface(
|
|
REFIID riid,
|
|
void ** ppv
|
|
)
|
|
{
|
|
if (IsEqualIID(IID_IGenVideo, riid))
|
|
return GetInterface((IGenVideo *) this, ppv);
|
|
|
|
if (IsEqualIID(IID_IDexterSequencer, riid))
|
|
return GetInterface((IDexterSequencer *) this, ppv);
|
|
|
|
if (IsEqualIID(IID_ISpecifyPropertyPages, riid))
|
|
return GetInterface((ISpecifyPropertyPages *) this, ppv);
|
|
|
|
if (IsEqualIID(IID_IMediaSeeking, riid))
|
|
return GetInterface((IMediaSeeking *) this, ppv);
|
|
|
|
return CSourceStream::NonDelegatingQueryInterface(riid, ppv);
|
|
}
|
|
|
|
|
|
//
|
|
// DoBufferProcessingLoop - overridden to put time stamps on GetBuffer
|
|
//
|
|
// Grabs a buffer and calls the users processing function.
|
|
// Overridable, so that different delivery styles can be catered for.
|
|
HRESULT CBlkVidStream::DoBufferProcessingLoop(void) {
|
|
|
|
Command com;
|
|
|
|
DbgLog((LOG_TRACE, 3, TEXT("Entering DoBufferProcessing Loop")));
|
|
|
|
OnThreadStartPlay();
|
|
|
|
do {
|
|
while (!CheckRequest(&com)) {
|
|
IMediaSample *pSample;
|
|
|
|
// What time stamps will this buffer get? Use that in GetBuffer
|
|
// because the switch needs to know. MAKE SURE to use the same
|
|
// algorithm as FillBuffer!
|
|
LONGLONG llOffset = Time2Frame( m_rtStartTime, m_dOutputFrmRate );
|
|
REFERENCE_TIME rtStart = Frame2Time( llOffset + m_llSamplesSent,
|
|
m_dOutputFrmRate );
|
|
REFERENCE_TIME rtStop = Frame2Time( llOffset + m_llSamplesSent + 1,
|
|
m_dOutputFrmRate );
|
|
|
|
if ( rtStart > m_rtStartTime + m_rtDuration ||
|
|
(rtStart == m_rtStartTime + m_rtDuration && m_rtDuration > 0)) {
|
|
DbgLog((LOG_TRACE,2,TEXT("Black: Finished")));
|
|
//m_llSamplesSent = 0;
|
|
DeliverEndOfStream();
|
|
return S_OK;
|
|
}
|
|
|
|
rtStart -= m_rtNewSeg;
|
|
rtStop -= m_rtNewSeg;
|
|
|
|
DbgLog((LOG_TRACE,2,TEXT("Black: GetBuffer %d"),
|
|
(int)(rtStart / 10000)));
|
|
HRESULT hr = GetDeliveryBuffer(&pSample,&rtStart,&rtStop,0);
|
|
if (FAILED(hr)) {
|
|
DbgLog((LOG_TRACE,2,TEXT("Black: FAILED %x"), hr));
|
|
return S_OK;
|
|
}
|
|
|
|
// Virtual function user will override.
|
|
hr = FillBuffer(pSample);
|
|
|
|
if (hr == S_OK) {
|
|
hr = Deliver(pSample);
|
|
pSample->Release();
|
|
|
|
// downstream filter returns S_FALSE if it wants us to
|
|
// stop or an error if it's reporting an error.
|
|
if(hr != S_OK)
|
|
{
|
|
DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
|
|
// NO NO! DeliverEndOfStream();
|
|
// !!! EC_ERRORABORT if FAILED?
|
|
return hr;
|
|
}
|
|
|
|
} else if (hr == S_FALSE) {
|
|
// derived class wants us to stop pushing data
|
|
pSample->Release();
|
|
DeliverEndOfStream();
|
|
return S_OK;
|
|
} else {
|
|
// derived class encountered an error
|
|
pSample->Release();
|
|
DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
|
|
DeliverEndOfStream();
|
|
m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
|
|
return hr;
|
|
}
|
|
|
|
// all paths release the sample
|
|
}
|
|
|
|
// For all commands sent to us there must be a Reply call!
|
|
|
|
if (com == CMD_RUN || com == CMD_PAUSE) {
|
|
Reply(NOERROR);
|
|
} else if (com != CMD_STOP) {
|
|
Reply((DWORD) E_UNEXPECTED);
|
|
DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
|
|
}
|
|
} while (com != CMD_STOP);
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FillBuffer called by HRESULT CSourceStream::DoBufferProcessingLoop(void) {
|
|
//
|
|
// Plots a Blk video into the supplied video buffer
|
|
//
|
|
// Give a start time, a duration, and a frame rate,
|
|
// it sends a certain size (ARGB32, RGB555,RGB24 ) black frames out time stamped appropriately starting
|
|
// at the start time.
|
|
//
|
|
HRESULT CBlkVidStream::FillBuffer(IMediaSample *pms)
|
|
{
|
|
CAutoLock foo(&m_csFilling);
|
|
|
|
ASSERT( m_ppbDstBuf != NULL );
|
|
ASSERT( m_iBufferCnt );
|
|
ASSERT( m_dOutputFrmRate != 0.0);
|
|
|
|
// !!! If NewSeg > 0, this is broken!
|
|
|
|
// calc the output sample times the SAME WAY FRC DOES, so the FRC will
|
|
// not need to modify anything
|
|
LONGLONG llOffset = Time2Frame( m_rtStartTime, m_dOutputFrmRate );
|
|
REFERENCE_TIME rtStart = Frame2Time( llOffset + m_llSamplesSent,
|
|
m_dOutputFrmRate );
|
|
REFERENCE_TIME rtStop = Frame2Time( llOffset + m_llSamplesSent + 1,
|
|
m_dOutputFrmRate );
|
|
if ( rtStart > m_rtStartTime + m_rtDuration ||
|
|
(rtStart == m_rtStartTime + m_rtDuration && m_rtDuration > 0)) {
|
|
DbgLog((LOG_TRACE,2,TEXT("Black: Finished")));
|
|
//m_llSamplesSent = 0;
|
|
DeliverEndOfStream();
|
|
return S_FALSE;
|
|
}
|
|
|
|
rtStart -= m_rtNewSeg;
|
|
rtStop -= m_rtNewSeg;
|
|
|
|
BYTE *pData;
|
|
|
|
//pms: output media sample pointer
|
|
pms->GetPointer(&pData); //get pointer to output buffer
|
|
|
|
if( m_bZeroBufCnt < m_iBufferCnt )
|
|
{
|
|
//
|
|
// there is no guarantee the buffer we just get is not initilized before
|
|
//
|
|
int i = 0;
|
|
BOOL bInit = FALSE;
|
|
while ( i < m_bZeroBufCnt )
|
|
{
|
|
if( m_ppbDstBuf[ i++ ] == pData)
|
|
{
|
|
bInit = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bInit == FALSE )
|
|
{
|
|
long lDataLen = pms->GetSize(); //get output buffer size
|
|
if(!m_dwRGBA)
|
|
{
|
|
//import buffer
|
|
if(m_pImportBuffer)
|
|
CopyMemory(pData,m_pImportBuffer,lDataLen);
|
|
else
|
|
//TRANSPARENT black
|
|
ZeroMemory( pData, lDataLen ); //clear memory
|
|
}
|
|
else
|
|
{
|
|
long *pl = (long *)pData;
|
|
BYTE *p=(BYTE *)pl;
|
|
int iCnt= lDataLen/12;
|
|
|
|
switch(HEADER(m_mtAccept.pbFormat)->biBitCount)
|
|
{
|
|
case 32:
|
|
while(lDataLen)
|
|
{
|
|
*pl++=m_dwRGBA;
|
|
lDataLen-=4;
|
|
}
|
|
break;
|
|
case 24:
|
|
long dwVal[3];
|
|
dwVal[0]= ( m_dwRGBA & 0xffffff ) | ( (m_dwRGBA & 0xff) << 24);
|
|
dwVal[1]= ( (m_dwRGBA & 0xffff00) >>8 ) | ( (m_dwRGBA & 0xffff) << 16);
|
|
dwVal[2]= ( (m_dwRGBA & 0xff0000) >>16 )| ( (m_dwRGBA & 0xffffff) << 8);
|
|
while(iCnt)
|
|
{
|
|
*pl++=dwVal[0];
|
|
*pl++=dwVal[1];
|
|
*pl++=dwVal[2];
|
|
iCnt--;
|
|
}
|
|
while(iCnt)
|
|
{
|
|
*p++=(BYTE)( m_dwRGBA & 0xff );
|
|
*p++=(BYTE)( ( m_dwRGBA & 0xff00 ) >> 8) ;
|
|
*p++=(BYTE)( ( m_dwRGBA & 0xff0000 ) >> 16) ;
|
|
iCnt--;
|
|
}
|
|
break;
|
|
case 16:
|
|
WORD wTmp=(WORD)( ((m_dwRGBA & 0xf8) >>3 ) //R
|
|
| ((m_dwRGBA & 0xf800) >>6 ) //G
|
|
| ((m_dwRGBA & 0xf80000)>>9 ) ); //B
|
|
WORD *pw=(WORD *)pData;
|
|
while(lDataLen)
|
|
{
|
|
*pw++=wTmp;
|
|
lDataLen-=2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
m_ppbDstBuf[ i ] = pData; //save this data pointer
|
|
m_bZeroBufCnt++;
|
|
}
|
|
}
|
|
|
|
DbgLog((LOG_TRACE,2,TEXT("Black: Deliver %d"), (int)(rtStart / 10000)));
|
|
pms->SetTime( &rtStart,&rtStop);
|
|
|
|
m_llSamplesSent++;
|
|
pms->SetActualDataLength(m_lDataLen);
|
|
pms->SetSyncPoint(TRUE);
|
|
return NOERROR;
|
|
|
|
} // FillBuffer
|
|
|
|
|
|
//
|
|
// GetMediaType
|
|
//
|
|
HRESULT CBlkVidStream::GetMediaType(int iPosition, CMediaType *pmt)
|
|
{
|
|
CAutoLock cAutoLock(m_pFilter->pStateLock());
|
|
|
|
if (iPosition < 0) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Have we run off the end of types
|
|
if (iPosition > 0) {
|
|
return VFW_S_NO_MORE_ITEMS;
|
|
}
|
|
|
|
return get_MediaType( pmt );
|
|
|
|
} // GetMediaType
|
|
|
|
|
|
// set media type
|
|
//
|
|
HRESULT CBlkVidStream::SetMediaType(const CMediaType* pmt)
|
|
{
|
|
DbgLog((LOG_TRACE,2,TEXT("SetMediaType %x %dbit %dx%d"),
|
|
HEADER(pmt->Format())->biCompression,
|
|
HEADER(pmt->Format())->biBitCount,
|
|
HEADER(pmt->Format())->biWidth,
|
|
HEADER(pmt->Format())->biHeight));
|
|
|
|
// !!! check for the frame rate given, and use it?
|
|
|
|
return CSourceStream::SetMediaType(pmt);
|
|
}
|
|
|
|
//
|
|
// CheckMediaType
|
|
//
|
|
// We accept mediatype =vids, subtype =MEDIASUBTYPE_ARGB32, RGB24, RGB555
|
|
// Returns E_INVALIDARG if the mediatype is not acceptable
|
|
//
|
|
HRESULT CBlkVidStream::CheckMediaType(const CMediaType *pMediaType)
|
|
{
|
|
CAutoLock cAutoLock(m_pFilter->pStateLock());
|
|
|
|
if ( ( (*pMediaType->Type()) != MEDIATYPE_Video ) // we only output video!
|
|
|| ( (*pMediaType->Subtype())!= MEDIASUBTYPE_ARGB32
|
|
&& (*pMediaType->Subtype())!= MEDIASUBTYPE_RGB24
|
|
&& (*pMediaType->Subtype())!= MEDIASUBTYPE_RGB555
|
|
)
|
|
)
|
|
return E_INVALIDARG;
|
|
|
|
// Get the format area of the media type
|
|
VIDEOINFO *pvi = (VIDEOINFO *) pMediaType->Format();
|
|
|
|
if (pvi == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
// Check the image size.
|
|
if ( (pvi->bmiHeader.biWidth != HEADER(m_mtAccept.pbFormat)->biWidth ) ||
|
|
(pvi->bmiHeader.biHeight != HEADER(m_mtAccept.pbFormat)->biHeight ) ||
|
|
(pvi->bmiHeader.biBitCount != HEADER(m_mtAccept.pbFormat)->biBitCount ))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if( !IsRectEmpty( &pvi->rcTarget) ) {
|
|
if (pvi->rcTarget.top != 0 || pvi->rcTarget.left !=0 ||
|
|
pvi->rcTarget.right != pvi->bmiHeader.biWidth ||
|
|
pvi->rcTarget.bottom != pvi->bmiHeader.biHeight) {
|
|
return VFW_E_TYPE_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
if( !IsRectEmpty( &pvi->rcSource) ) {
|
|
if (pvi->rcSource.top != 0 || pvi->rcSource.left !=0 ||
|
|
pvi->rcSource.right != pvi->bmiHeader.biWidth ||
|
|
pvi->rcSource.bottom != pvi->bmiHeader.biHeight) {
|
|
return VFW_E_TYPE_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
|
|
return S_OK; // This format is acceptable.
|
|
|
|
} // CheckMediaType
|
|
|
|
|
|
HRESULT CBlkVidStream::DecideAllocator(IMemInputPin *pPin, IMemAllocator **ppAlloc)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
*ppAlloc = NULL;
|
|
|
|
// get downstream prop request
|
|
// the derived class may modify this in DecideBufferSize, but
|
|
// we assume that he will consistently modify it the same way,
|
|
// so we only get it once
|
|
ALLOCATOR_PROPERTIES prop;
|
|
ZeroMemory(&prop, sizeof(prop));
|
|
|
|
// whatever he returns, we assume prop is either all zeros
|
|
// or he has filled it out.
|
|
pPin->GetAllocatorRequirements(&prop);
|
|
|
|
// if he doesn't care about alignment, then set it to 1
|
|
if (prop.cbAlign == 0) {
|
|
prop.cbAlign = 1;
|
|
}
|
|
|
|
/* Try the allocator provided by the input pin */
|
|
|
|
hr = pPin->GetAllocator(ppAlloc);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
hr = DecideBufferSize(*ppAlloc, &prop);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pPin->NotifyAllocator(*ppAlloc, TRUE); //read only buffer
|
|
if (SUCCEEDED(hr)) {
|
|
return NOERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If the GetAllocator failed we may not have an interface */
|
|
|
|
if (*ppAlloc) {
|
|
(*ppAlloc)->Release();
|
|
*ppAlloc = NULL;
|
|
}
|
|
|
|
/* Try the output pin's allocator by the same method */
|
|
|
|
hr = InitAllocator(ppAlloc);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
// note - the properties passed here are in the same
|
|
// structure as above and may have been modified by
|
|
// the previous call to DecideBufferSize
|
|
hr = DecideBufferSize(*ppAlloc, &prop);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pPin->NotifyAllocator(*ppAlloc, TRUE); //READ-only buffer
|
|
if (SUCCEEDED(hr)) {
|
|
return NOERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Likewise we may not have an interface to release */
|
|
|
|
if (*ppAlloc) {
|
|
(*ppAlloc)->Release();
|
|
*ppAlloc = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// DecideBufferSize
|
|
//
|
|
// This will always be called after the format has been sucessfully
|
|
// negotiated. So we have a look at m_mt to see what size image we agreed.
|
|
// Then we can ask for buffers of the correct size to contain them.
|
|
//
|
|
HRESULT CBlkVidStream::DecideBufferSize(IMemAllocator *pAlloc,ALLOCATOR_PROPERTIES *pProperties)
|
|
{
|
|
CAutoLock cAutoLock(m_pFilter->pStateLock());
|
|
|
|
ASSERT(pAlloc);
|
|
ASSERT(pProperties);
|
|
HRESULT hr = NOERROR;
|
|
|
|
VIDEOINFO *pvi = (VIDEOINFO *) m_mt.Format();
|
|
|
|
pProperties->cBuffers = MAXBUFFERCNT; //only one read-only buffer
|
|
pProperties->cbBuffer = pvi->bmiHeader.biSizeImage;
|
|
|
|
|
|
// Ask the allocator to reserve us some sample memory, NOTE the function
|
|
// can succeed (that is return NOERROR) but still not have allocated the
|
|
// memory that we requested, so we must check we got whatever we wanted
|
|
|
|
ALLOCATOR_PROPERTIES Actual;
|
|
hr = pAlloc->SetProperties(pProperties,&Actual);
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
// Is this allocator unsuitable
|
|
|
|
if (Actual.cbBuffer < pProperties->cbBuffer) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
//because I am not insisting my own buffer, I may get more than MAXBUFFERCNT buffers.
|
|
m_iBufferCnt =Actual.cBuffers; //how many buffer need to be set to 0
|
|
|
|
return NOERROR;
|
|
|
|
} // DecideBufferSize
|
|
|
|
|
|
|
|
//
|
|
// OnThreadCreate
|
|
//
|
|
//
|
|
HRESULT CBlkVidStream::OnThreadCreate()
|
|
{
|
|
// we have to have at least MAXBUFFERCNT buffer
|
|
ASSERT(m_iBufferCnt >= MAXBUFFERCNT);
|
|
|
|
//output frame cnt
|
|
m_llSamplesSent =0;
|
|
|
|
//how many buffer is already set to 0.
|
|
m_bZeroBufCnt =0;
|
|
|
|
// actual output buffer's data size
|
|
m_lDataLen= HEADER(m_mtAccept.pbFormat)->biHeight * (DWORD)WIDTHBYTES((DWORD)HEADER(m_mtAccept.pbFormat)->biWidth * HEADER(m_mtAccept.pbFormat)->biBitCount);
|
|
|
|
// will be used to zero the Dst buffers
|
|
delete [] m_ppbDstBuf;
|
|
m_ppbDstBuf = new BYTE *[ m_iBufferCnt ]; //NULL;
|
|
if( !m_ppbDstBuf )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// don't reset m_rtNewSeg! We might have seeked while stopped
|
|
|
|
for (int i=0; i<m_iBufferCnt; i++)
|
|
m_ppbDstBuf[i]=NULL;
|
|
|
|
return NOERROR;
|
|
|
|
} // OnThreadCreate
|
|
|
|
|
|
//
|
|
// Notify
|
|
//
|
|
//
|
|
STDMETHODIMP CBlkVidStream::Notify(IBaseFilter * pSender, Quality q)
|
|
{
|
|
//Even I am later, I do not care. I still send my time frame as nothing happened.
|
|
return NOERROR;
|
|
|
|
} // Notify
|
|
|
|
//
|
|
// GetPages
|
|
//
|
|
// Returns the clsid's of the property pages we support
|
|
//
|
|
STDMETHODIMP CBlkVidStream::GetPages(CAUUID *pPages)
|
|
{
|
|
pPages->cElems = 1;
|
|
pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID));
|
|
if (pPages->pElems == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
*(pPages->pElems) = CLSID_GenVidPropertiesPage;
|
|
return NOERROR;
|
|
|
|
} // GetPages
|
|
|
|
//
|
|
// IDexterSequencer
|
|
//
|
|
|
|
STDMETHODIMP CBlkVidStream::get_OutputFrmRate( double *dpFrmRate )
|
|
{
|
|
CAutoLock cAutolock(m_pFilter->pStateLock());
|
|
|
|
CheckPointer(dpFrmRate,E_POINTER);
|
|
|
|
*dpFrmRate = m_dOutputFrmRate;
|
|
|
|
return NOERROR;
|
|
|
|
} // get_OutputFrmRate
|
|
|
|
//
|
|
// Frame rate can be changed as long as the filter is stopped.
|
|
//
|
|
STDMETHODIMP CBlkVidStream::put_OutputFrmRate( double dFrmRate )
|
|
{
|
|
CAutoLock cAutolock(m_pFilter->pStateLock());
|
|
|
|
//can not change property if our filter is not currently stopped
|
|
if(!IsStopped() )
|
|
return VFW_E_WRONG_STATE;
|
|
|
|
// don't blow up
|
|
if (dFrmRate == 0.0)
|
|
dFrmRate = 0.1;
|
|
m_dOutputFrmRate = dFrmRate;
|
|
|
|
return NOERROR;
|
|
|
|
} // put_OutputFrmRate
|
|
|
|
STDMETHODIMP CBlkVidStream::get_MediaType(AM_MEDIA_TYPE *pmt)
|
|
{
|
|
CAutoLock cAutolock(m_pFilter->pStateLock());
|
|
CheckPointer(pmt,E_POINTER);
|
|
|
|
return CopyMediaType(pmt, &m_mtAccept);
|
|
}
|
|
|
|
//
|
|
// size can be changed only the output pin is not connected yet.
|
|
//
|
|
STDMETHODIMP CBlkVidStream::put_MediaType(const AM_MEDIA_TYPE *pmt)
|
|
{
|
|
CAutoLock cAutolock(m_pFilter->pStateLock());
|
|
CheckPointer(pmt,E_POINTER);
|
|
|
|
if ( IsConnected() )
|
|
return VFW_E_ALREADY_CONNECTED;
|
|
|
|
if ( HEADER(pmt->pbFormat)->biBitCount != 32 &&
|
|
HEADER(pmt->pbFormat)->biBitCount != 24 &&
|
|
HEADER(pmt->pbFormat)->biBitCount != 16)
|
|
return E_INVALIDARG;
|
|
|
|
if (HEADER(pmt->pbFormat)->biWidth == 16 &&
|
|
pmt->subtype != MEDIASUBTYPE_RGB555)
|
|
return E_INVALIDARG;
|
|
|
|
SaferFreeMediaType(m_mtAccept);
|
|
HRESULT hr = CopyMediaType(&m_mtAccept, pmt);
|
|
|
|
m_fMediaTypeIsSet =TRUE;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// We don't support this, the frame rate converter does
|
|
|
|
STDMETHODIMP CBlkVidStream::GetStartStopSkewCount(int *piCount)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBlkVidStream::GetStartStopSkew( REFERENCE_TIME *prtStart, REFERENCE_TIME *prtStop, REFERENCE_TIME *prtSkew, double *pdRate )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CBlkVidStream::AddStartStopSkew( REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, REFERENCE_TIME rtSkew , double dRate)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
//
|
|
// Duration can be changed as long as the filter is stopped.
|
|
//
|
|
STDMETHODIMP CBlkVidStream::ClearStartStopSkew()
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
//
|
|
// IGenVideo
|
|
//
|
|
|
|
|
|
STDMETHODIMP CBlkVidStream::get_RGBAValue( long *pdwRGBA )
|
|
{
|
|
CAutoLock cAutolock(m_pFilter->pStateLock());
|
|
CheckPointer(pdwRGBA,E_POINTER);
|
|
|
|
*pdwRGBA=m_dwRGBA ;
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
STDMETHODIMP CBlkVidStream::put_RGBAValue( long dwRGBA )
|
|
{
|
|
|
|
CAutoLock cAutolock(m_pFilter->pStateLock());
|
|
|
|
//can not change duration if our filter is not currently stopped
|
|
if(!IsStopped() )
|
|
return VFW_E_WRONG_STATE;
|
|
|
|
m_dwRGBA = dwRGBA;
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
// --- IMediaSeeking methods ----------
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::GetCapabilities(DWORD * pCaps)
|
|
{
|
|
CheckPointer(pCaps,E_POINTER);
|
|
// we always know the current position
|
|
*pCaps = AM_SEEKING_CanSeekAbsolute
|
|
| AM_SEEKING_CanSeekForwards
|
|
| AM_SEEKING_CanSeekBackwards
|
|
| AM_SEEKING_CanGetCurrentPos
|
|
| AM_SEEKING_CanGetStopPos
|
|
| AM_SEEKING_CanGetDuration;
|
|
//| AM_SEEKING_CanDoSegments
|
|
//| AM_SEEKING_Source;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::CheckCapabilities(DWORD * pCaps)
|
|
{
|
|
CheckPointer(pCaps,E_POINTER);
|
|
|
|
DWORD dwMask = 0;
|
|
GetCapabilities(&dwMask);
|
|
*pCaps &= dwMask;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::IsFormatSupported(const GUID * pFormat)
|
|
{
|
|
CheckPointer(pFormat,E_POINTER);
|
|
return (*pFormat == TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::QueryPreferredFormat(GUID *pFormat)
|
|
{
|
|
CheckPointer(pFormat,E_POINTER);
|
|
*pFormat = TIME_FORMAT_MEDIA_TIME;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::SetTimeFormat(const GUID * pFormat)
|
|
{
|
|
CheckPointer(pFormat,E_POINTER);
|
|
|
|
if(*pFormat == TIME_FORMAT_MEDIA_TIME)
|
|
return S_OK;
|
|
else
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::GetTimeFormat(GUID *pFormat)
|
|
{
|
|
CheckPointer(pFormat,E_POINTER);
|
|
*pFormat = TIME_FORMAT_MEDIA_TIME ;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::IsUsingTimeFormat(const GUID * pFormat)
|
|
{
|
|
CheckPointer(pFormat,E_POINTER);
|
|
if (*pFormat == TIME_FORMAT_MEDIA_TIME)
|
|
return S_OK;
|
|
else
|
|
return S_FALSE;
|
|
}
|
|
|
|
// The biggie!
|
|
//
|
|
STDMETHODIMP
|
|
CBlkVidStream::SetPositions( LONGLONG * pCurrent, DWORD CurrentFlags
|
|
, LONGLONG * pStop, DWORD StopFlags )
|
|
{
|
|
// make sure we're not filling a buffer right now
|
|
m_csFilling.Lock();
|
|
|
|
HRESULT hr;
|
|
REFERENCE_TIME rtStart, rtStop;
|
|
|
|
// we don't do segments
|
|
if ((CurrentFlags & AM_SEEKING_Segment) ||
|
|
(StopFlags & AM_SEEKING_Segment)) {
|
|
DbgLog((LOG_TRACE,1,TEXT("FRC: ERROR-Seek used EC_ENDOFSEGMENT!")));
|
|
m_csFilling.Unlock();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// default to current values unless this seek changes them
|
|
// these numbers already include new segment times
|
|
GetCurrentPosition(&rtStart);
|
|
GetStopPosition(&rtStop);
|
|
|
|
// figure out where we're seeking to
|
|
DWORD dwFlags = (CurrentFlags & AM_SEEKING_PositioningBitsMask);
|
|
if (dwFlags == AM_SEEKING_AbsolutePositioning) {
|
|
CheckPointer(pCurrent, E_POINTER);
|
|
rtStart = *pCurrent;
|
|
} else if (dwFlags == AM_SEEKING_RelativePositioning) {
|
|
CheckPointer(pCurrent, E_POINTER);
|
|
hr = GetCurrentPosition(&rtStart);
|
|
rtStart += *pCurrent;
|
|
} else if (dwFlags) {
|
|
DbgLog((LOG_TRACE,1,TEXT("Switch::Invalid Current Seek flags")));
|
|
m_csFilling.Unlock();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
dwFlags = (StopFlags & AM_SEEKING_PositioningBitsMask);
|
|
if (dwFlags == AM_SEEKING_AbsolutePositioning) {
|
|
CheckPointer(pStop, E_POINTER);
|
|
rtStop = *pStop;
|
|
} else if (dwFlags == AM_SEEKING_RelativePositioning) {
|
|
CheckPointer(pStop, E_POINTER);
|
|
hr = GetStopPosition(&rtStop);
|
|
rtStop += *pStop;
|
|
} else if (dwFlags == AM_SEEKING_IncrementalPositioning) {
|
|
CheckPointer(pStop, E_POINTER);
|
|
hr = GetCurrentPosition(&rtStop);
|
|
rtStop += *pStop;
|
|
}
|
|
|
|
// !!! We ignore the seek stop time!
|
|
|
|
DbgLog((LOG_TRACE,2,TEXT("Seek BLACK: Start=%d Stop=%d"),
|
|
(int)(rtStart / 10000), (int)(rtStop / 10000)));
|
|
|
|
// flush first, so that our thread won't be blocked delivering
|
|
DeliverBeginFlush();
|
|
|
|
// Unlock/Stop so that our thread can wake up and stop without hanging
|
|
m_csFilling.Unlock();
|
|
Stop();
|
|
|
|
m_rtStartTime = rtStart;
|
|
m_rtDuration = rtStop - rtStart;
|
|
m_llSamplesSent = 0;
|
|
|
|
// now finish flushing
|
|
DeliverEndFlush();
|
|
|
|
DeliverNewSegment(rtStart, rtStop, 1.0);
|
|
m_rtNewSeg = rtStart;
|
|
|
|
// reset same stuff we reset when we start streaming
|
|
m_bZeroBufCnt = 0;
|
|
|
|
// now start the thread up again
|
|
Pause();
|
|
|
|
DbgLog((LOG_TRACE,2,TEXT("Completed BLACK seek")));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::GetPositions(LONGLONG *pCurrent, LONGLONG * pStop)
|
|
{
|
|
CheckPointer(pCurrent, E_POINTER);
|
|
CheckPointer(pStop, E_POINTER);
|
|
GetCurrentPosition(pCurrent);
|
|
GetStopPosition(pStop);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::GetCurrentPosition(LONGLONG *pCurrent)
|
|
{
|
|
CheckPointer(pCurrent, E_POINTER);
|
|
*pCurrent = m_rtStartTime + Frame2Time( m_llSamplesSent, m_dOutputFrmRate );
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::GetStopPosition(LONGLONG *pStop)
|
|
{
|
|
CheckPointer(pStop, E_POINTER);
|
|
*pStop = m_rtStartTime + m_rtDuration;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::GetAvailable( LONGLONG *pEarliest, LONGLONG *pLatest )
|
|
{
|
|
CheckPointer(pEarliest, E_POINTER);
|
|
CheckPointer(pLatest, E_POINTER);
|
|
*pEarliest = 0;
|
|
*pLatest = MAX_TIME;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::GetDuration( LONGLONG *pDuration )
|
|
{
|
|
CheckPointer(pDuration, E_POINTER);
|
|
*pDuration = m_rtDuration;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::GetRate( double *pdRate )
|
|
{
|
|
CheckPointer(pdRate, E_POINTER);
|
|
*pdRate = 1.0;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CBlkVidStream::SetRate( double dRate )
|
|
{
|
|
if (dRate == 1.0)
|
|
return S_OK;
|
|
ASSERT(FALSE);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
/*X* When you make the filter, it always asks for a filename
|
|
because it's based on a file source filter.
|
|
So, solid colour filter support ImportSrcBuffer(), Stillvid filter does not) *X*/
|
|
STDMETHODIMP CBlkVidStream::ImportSrcBuffer(const AM_MEDIA_TYPE *pmt, const BYTE *pBuf)
|
|
{
|
|
|
|
CAutoLock cAutolock(m_pFilter->pStateLock());
|
|
CheckPointer(pBuf, E_POINTER);
|
|
|
|
if ( IsConnected() )
|
|
return VFW_E_ALREADY_CONNECTED;
|
|
|
|
HRESULT hr = put_MediaType(pmt);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if( m_pImportBuffer!=NULL )
|
|
delete [] m_pImportBuffer;
|
|
|
|
VIDEOINFO *pvi = (VIDEOINFO *) m_mt.Format();
|
|
LONG lSize = pvi->bmiHeader.biSizeImage;
|
|
|
|
m_pImportBuffer = new BYTE [lSize ]; //NULL;
|
|
if( !m_pImportBuffer )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
CopyMemory(m_pImportBuffer, (PBYTE) pBuf, sizeof(BYTE)*lSize);
|
|
//m_fMediaTypeIsSet = FALSE;
|
|
return NOERROR;
|
|
}
|
|
|