//=========================================================================== // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved. // //=========================================================================== // // filename: fvcrctrl.cpp // // Sample VCR Control Filter // This filter is CBaseFilter derived. // // Responds to run/pause/stop. An app would probably want to override the // default operation of this filter and "disconnect" the PLAY, STOP and // PAUSE mode commands from the filter's ::Run(), ::Stop() and ::Pause() // methods using the "link" property. // // #include #include #include #include #include #include #include #include #include "vcruids.h" #include "ctimecod.h" #include "cdevcom.h" #include "cvcrutil.h" #include "fvcrctrl.h" #include "vcrprop.h" #include "trprop.h" #define DbgFunc(a) DbgLog(( LOG_TRACE, \ 2, \ TEXT("CVcrFilter::%s"), \ TEXT(a) \ )); // IPersistStream macros #define WRITEOUT(var) hr = pStream->Write(&var, sizeof(var), NULL); \ if (FAILED(hr)) return hr; #define READIN(var) hr = pStream->Read(&var, sizeof(var), NULL); \ if (FAILED(hr)) return hr; // some local storage LONGLONG CVcrTCOut::m_llLastTime = 0L; const int textbufsize = 12*sizeof(TCHAR); static TIMECODE_SAMPLE m_tcsTimecode = { 0L,{0,0,0L},0L,0L }; // we will ask for timecode samples this often const REFERENCE_TIME rtPeriodTime = 3333 * (UNITS / (100*MILLISECONDS)); // Physical Pin data const TCHAR *strSVO = {"SVO-5800"}; const TCHAR *strPhysConn_Video_Composite = {"Composite"}; const TCHAR *strPhysConn_Video_SVideo = {"S-Video"}; const TCHAR *strPhysConn_Video_Black = {"Black"}; const TCHAR *strPhysConn_Audio_Mic = {"Microphone"}; const TCHAR *strPhysConn_Audio_Line = {"Line"}; // Setup data const AMOVIESETUP_MEDIATYPE sudTcOpPinTypes[] = { {&MEDIATYPE_Text, // Major type &MEDIASUBTYPE_NULL}, // Minor type {&MEDIATYPE_Timecode, // Major type &MEDIASUBTYPE_NULL} // Minor type }; const AMOVIESETUP_MEDIATYPE sudAudOpPinType[] = { &MEDIATYPE_AnalogAudio, // Major type &MEDIASUBTYPE_NULL // Minor type }; const AMOVIESETUP_MEDIATYPE sudVidIpPinType[] = { &MEDIATYPE_AnalogVideo, // Major type &MEDIASUBTYPE_AnalogVideo_NTSC_M, // Minor type &MEDIATYPE_AnalogVideo, // Major type &MEDIASUBTYPE_AnalogVideo_NTSC_M // Minor type }; const AMOVIESETUP_MEDIATYPE sudAudIpPinType[] = { &MEDIATYPE_AnalogAudio, // Major type &MEDIASUBTYPE_NULL, // Minor type &MEDIATYPE_AnalogAudio, // Major type &MEDIASUBTYPE_NULL // Minor type }; const AMOVIESETUP_PIN sudPins[] = { {L"Timecode Out", // 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 2, // Number of types sudTcOpPinTypes}, // Pin details {L"Analog Composite Video In", // Pin string name FALSE, // Is it rendered FALSE, // 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 sudVidIpPinType} // Pin details }; const AMOVIESETUP_FILTER sudVcrctrlax = { &CLSID_VCRControlFilter, // Filter CLSID L"VCR Control Filter", // String name MERIT_DO_NOT_USE, // Filter merit 1, // Number of pins (that we know about) sudPins // Pin details }; CFactoryTemplate g_Templates[]= { {L"VCR Control Filter", &CLSID_VCRControlFilter, CVcrFilter::CreateInstance, NULL, &sudVcrctrlax}, {L"VCR Control General Property Page", &CLSID_VCRControlPropertyPage, CVcrProperties::CreateInstance, NULL, NULL}, {L"VCR Control Transport Property Page", &CLSID_VCRTransPropertyPage, CExtTransProperties::CreateInstance, NULL, NULL} }; int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]); //------------------------------------------ // // exported entry points for registration and // unregistration (in this case they only call // through to default implmentations). // //------------------------------------------ //------------------------------------------ // // DllRegisterServer // // Exported entry points for registration and unregistration // STDAPI DllRegisterServer() { HRESULT hr = AMovieDllRegisterServer2(TRUE); if(SUCCEEDED(hr)) { IFilterMapper2 *pFm2; HRESULT hr = CoCreateInstance( CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, IID_IFilterMapper2, (void **)&pFm2); if(SUCCEEDED(hr)) { WCHAR wszUniq[200]; wsprintfW(wszUniq, L"VCR Control Sample"); // RegisterFilter returns a moniker here. IMoniker *pMoniker = 0; REGFILTER2 rf2; rf2.dwVersion = 1; rf2.dwMerit = MERIT_DO_NOT_USE; rf2.cPins = 0; rf2.rgPins = 0; hr = pFm2->RegisterFilter( CLSID_VCRControlFilter, wszUniq, &pMoniker, &CLSID_DeviceControlCategory, wszUniq, &rf2); pFm2->Release(); } // CoCreateInstance } // AMovieDllRegisterServer2 return hr; } // DllRegisterServer //------------------------------------------ // // DllUnregisterServer // //------------------------------------------ STDAPI DllUnregisterServer() { HRESULT hr = AMovieDllRegisterServer2( FALSE ); if(SUCCEEDED(hr)) { // remove each entry IFilterMapper2 *pFm2; HRESULT hr = CoCreateInstance( CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, IID_IFilterMapper2, (void **)&pFm2); if(SUCCEEDED(hr)) { WCHAR wszUniq[200]; wsprintfW(wszUniq, L"VCR Control Sample"); hr = pFm2->UnregisterFilter( &CLSID_DeviceControlCategory, wszUniq, CLSID_VCRControlFilter); pFm2->Release(); } // CoCreateInstance } // AMovieDllRegisterServer2 return hr; } // DllUnregisterServer //------------------------------------------ //------------------------------------------ // // CVcrFilter::Constructor // //------------------------------------------ CVcrFilter::CVcrFilter(TCHAR *tszName, LPUNKNOWN punk, CLSID clsid, HRESULT *phr) : CBaseFilter(tszName, punk, &m_StateLock, clsid, phr), CAMExtDevice(phr, tszName), CAMExtTransport(phr, tszName), CAMTcr(phr, tszName), CPersistStream(punk, phr), m_iPins(0), m_paStreams(NULL) { DbgFunc("CVcrFilter"); CAutoLock cObjectLock(&m_StateLock); LPOLESTR pName = NULL; // we use a single CDevCom object to make the filter thread-safe CAMTcr::SetCommunicationObject(m_devcom); CAMExtTransport::SetCommunicationObject(m_devcom); // create the pins - reserve space for 6, but we might only make 5 m_paStreams = (CBasePin **) new COutStream*[6]; if (m_paStreams == NULL) { *phr = E_OUTOFMEMORY; return; } // our only output pin m_paStreams[0] = new CVcrTCOut(phr, this, L"Timecode Out"); if (m_paStreams[0] == NULL) { *phr = E_OUTOFMEMORY; return; } // Create the input pins m_paStreams[1] = new CVcrAudioLineInputPin(NAME("VCR Audio Line"),this, phr, L"Audio Line In"); if (m_paStreams[1] == NULL) { *phr = E_OUTOFMEMORY; return; } m_paStreams[2] = new CVcrAudioMicInputPin(NAME("VCR Audio Line"),this, phr, L"Audio Mic In"); if (m_paStreams[2] == NULL) { *phr = E_OUTOFMEMORY; return; } m_paStreams[3] = new CVcrVideoCompInputPin(NAME("VCR Video Input"),this, phr, L"Composite Video In"); if (m_paStreams[3] == NULL) { *phr = E_OUTOFMEMORY; return; } m_paStreams[4] = new CVcrVideoSInputPin(NAME("VCR Video Input"),this, phr, L"S-Video In"); if (m_paStreams[4] == NULL) { *phr = E_OUTOFMEMORY; return; } // if this is an SVO-5800, then it has an additional input pin get_ExternalDeviceID( &pName ); if ( strcmp(strSVO, (const char *)(LPSTR)pName) == 0 ) { m_paStreams[5] = new CVcrVideoBlackInputPin(NAME("VCR Video Input"),this, phr, L"Video Black"); if (m_paStreams[5] == NULL) { *phr = E_OUTOFMEMORY; return; } } QzTaskMemFree(pName); // must release the memory pName = NULL; put_Mode( ED_MODE_STOP ); // configure tc reader SetTCRMode(ED_TCR_SOURCE, ED_TCR_LTC); SetTCRMode(ED_MODE_NOTIFY_ENABLE, OAFALSE); CPersistStream::SetDirty(TRUE); // always write out the state } //------------------------------------------ // // CreateInstance // // Provide the way for COM to create a CVcrFilter object. Also // initialize a CVcrTCOut object so that we have a pin. //------------------------------------------ CUnknown * CVcrFilter::CreateInstance(LPUNKNOWN punk, HRESULT *phr) { CVcrFilter *pNewObject = new CVcrFilter(NAME("VCR Control Filter"), punk, CLSID_VCRControlFilter, phr); if (pNewObject == NULL) { *phr = E_OUTOFMEMORY; } return (CSource *)pNewObject; } //------------------------------------------ // // The Destructor // //------------------------------------------ CVcrFilter::~CVcrFilter( void ) { // Free our pins and pin array while (m_iPins != 0) { // deleting the pins causes them to be removed from the array... delete m_paStreams[m_iPins - 1]; } ASSERT(m_paStreams == NULL); } //------------------------------------------ // // NonDelegatingQueryInterface // // Reveal our property pages and IAMExtDev/ // IAMExtTransport/etc. interfaces //------------------------------------------ STDMETHODIMP CVcrFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv) { CAutoLock cObjectLock(&m_StateLock); if (riid == IID_ISpecifyPropertyPages) { return GetInterface((ISpecifyPropertyPages *) this, ppv); } else if (riid == IID_IAMExtDevice) { return GetInterface((IAMExtDevice *) this, ppv); } else if (riid == IID_IAMExtTransport) { return GetInterface((IAMExtTransport *) this, ppv); } else if (riid == IID_IAMTimecodeReader) { return GetInterface((IAMTimecodeReader *) this, ppv); } else if (riid == IID_IPersistStream) { return GetInterface((IPersistStream *) this, ppv); }else { return CBaseFilter::NonDelegatingQueryInterface(riid, ppv); } } //------------------------------------------ // // GetClassID // // Override CBaseMediaFilter method for interface IPersist // Part of the persistent file support. We must supply our class id // which can be saved in a graph file and used on loading a graph with // an external device in it to instantiate this filter via CoCreateInstance. // //------------------------------------------ STDMETHODIMP CVcrFilter::GetClassID(CLSID *pClsid) { if (pClsid==NULL) { return E_POINTER; } *pClsid = CLSID_VCRControlFilter; return NOERROR; } //------------------------------------------ // // SizeMax // // Override CPersistStream method. // State the maximum number of bytes we would ever write in a file // to save our properties. // //------------------------------------------ int CVcrFilter::SizeMax() { // When an int is expanded as characters it takes at most 12 characters // including a trailing delimiter. // Wide chars doubles this and we want 16 ints. // return 384; } //------------------------------------------ // // WriteToStream // // Override CPersistStream method. // Write our properties to the stream. // // We only write a few of the important properties // to demonstrate the process. // //------------------------------------------ HRESULT CVcrFilter::WriteToStream(IStream *pStream) { long temp; HRESULT hr; GetTransportVideoParameters(ED_TRANSVIDEO_SET_SOURCE, &temp); hr = WRITEOUT(temp); if (FAILED(hr)) return hr; GetTransportAudioParameters(ED_TRANSAUDIO_SET_SOURCE, &temp); hr = WRITEOUT(temp); if (FAILED(hr)) return hr; GetStatus(ED_LINK_MODE, &temp); hr = WRITEOUT(temp); if (FAILED(hr)) return hr; get_DevicePort(&temp); hr = WRITEOUT(temp); if (FAILED(hr)) return hr; return NOERROR; } //------------------------------------------ // // ReadFromStream // // Override CPersistStream method. // Read our properties from the stream. // //------------------------------------------ HRESULT CVcrFilter::ReadFromStream(IStream *pStream) { long temp; HRESULT hr; hr = READIN(temp); SetTransportVideoParameters(ED_TRANSVIDEO_SET_SOURCE, temp); if (FAILED(hr)) return hr; hr = READIN(temp); SetTransportAudioParameters(ED_TRANSAUDIO_SET_SOURCE, temp); if (FAILED(hr)) return hr; hr = READIN(temp); put_Mode(temp); // this will set the link mode if (FAILED(hr)) return hr; hr = READIN(temp); put_DevicePort(temp); // this will set the active communications if (FAILED(hr)) return hr; // port return NOERROR; } //------------------------------------------ // // Add a new pin // //------------------------------------------ HRESULT CVcrFilter::AddPin(CBasePin *pPin) { CAutoLock lock(&m_StateLock); /* Allocate space for this pin and the old ones */ CBasePin **paStreams = new CBasePin *[m_iPins + 1]; if (paStreams == NULL) { return E_OUTOFMEMORY; } if (m_paStreams != NULL) { CopyMemory((PVOID)paStreams, (PVOID)m_paStreams, m_iPins * sizeof(m_paStreams[0])); paStreams[m_iPins] = pPin; delete [] m_paStreams; } m_paStreams = (CBasePin **)paStreams; m_paStreams[m_iPins] = pPin; m_iPins++; return S_OK; } //------------------------------------------ // // Remove a pin - pStream is NOT deleted // //------------------------------------------ HRESULT CVcrFilter::RemovePin(CBasePin *pStream) { int i; for (i = 0; i < m_iPins; i++) { if (m_paStreams[i] == pStream) { if (m_iPins == 1) { delete [] m_paStreams; m_paStreams = NULL; } else { /* no need to reallocate */ while (++i < m_iPins) m_paStreams[i - 1] = m_paStreams[i]; } m_iPins--; return S_OK; } } return S_FALSE; } //------------------------------------------ // // GetPinCount // // Returns the number of pins this filter has // //------------------------------------------ int CVcrFilter::GetPinCount(void) { CAutoLock lock(&m_StateLock); return m_iPins; } //------------------------------------------ // // GetPin // // Return a non-addref'd pointer to pin n // needed by CBaseFilter // //------------------------------------------ CBasePin * CVcrFilter::GetPin(int n) { CAutoLock lock(&m_StateLock); if ((m_iPins > 0) && (n < m_iPins)) { ASSERT(m_paStreams[n]); return m_paStreams[n]; } return NULL; } //------------------------------------------ // // IMediaControl Overrides // //------------------------------------------ HRESULT CVcrFilter::Run(REFERENCE_TIME tStart) { long Mode; long LinkMode; DbgFunc("Run"); GetStatus(ED_LINK_MODE, &LinkMode); if (LinkMode == OATRUE) { get_Mode( &Mode ); if (Mode == ED_MODE_FREEZE) put_Mode(ED_MODE_THAW); else put_Mode(ED_MODE_PLAY); } // use the base Run method for the state transition CBaseFilter::Run(tStart); return NOERROR; } HRESULT CVcrFilter::Pause() { long LinkMode; DbgFunc("Pause"); GetStatus(ED_LINK_MODE, &LinkMode); if (LinkMode == OATRUE) put_Mode(ED_MODE_FREEZE); // use the base Run method for the state transition CBaseFilter::Pause(); return NOERROR; } HRESULT CVcrFilter::Stop() { long LinkMode; DbgFunc("Stop"); GetStatus(ED_LINK_MODE, &LinkMode); if (LinkMode == OATRUE) put_Mode(ED_MODE_STOP); // use the base Run method for the state transition CBaseFilter::Stop(); return NOERROR; } // ==============Implementation of the IPropertypages Interface =========== //------------------------------------------ // // GetPages - return the CLSIDs for the property pages we support // //------------------------------------------ STDMETHODIMP CVcrFilter::GetPages(CAUUID * pPages) { CAutoLock l(&m_StateLock); CheckPointer(pPages,E_POINTER); pPages->cElems = 2; pPages->pElems = (GUID *) QzTaskMemAlloc(2 * sizeof(GUID)); if (pPages->pElems == NULL) { return E_OUTOFMEMORY; } pPages->pElems[0] = CLSID_VCRControlPropertyPage; pPages->pElems[1] = CLSID_VCRTransPropertyPage; return NOERROR; } //------------------------------------------ //------------------------------------------ //------------------------------------------ // * // * --- COutStream ---- // * //------------------------------------------ //------------------------------------------ //------------------------------------------ // //------------------------------------------ // // COutStream::Constructor // // increments the number of pins present on the filter // //------------------------------------------ COutStream::COutStream( TCHAR *pObjectName, HRESULT *phr, CVcrFilter *pParent, LPCWSTR pPinName) : CBaseOutputPin(pObjectName, pParent, pParent->pLock(), phr, pPinName), m_pFilter(pParent), m_pRefClock(NULL), m_hAdviseEvent(NULL) { *phr = m_pFilter->AddPin(this); } //------------------------------------------ // // COutStream::Destructor // // Decrements the number of pins on this filter //------------------------------------------ COutStream::~COutStream(void) { m_pFilter->RemovePin(this); } //------------------------------------------ // // Active // // The pin is active - start up the worker thread // //------------------------------------------ HRESULT COutStream::Active(void) { CAutoLock lock(m_pFilter->pLock()); HRESULT hr; if (m_pFilter->IsActive()) { return S_FALSE; // succeeded, but did not allocate resources // (they already exist...) } // do nothing if not connected - its ok not to connect to // all pins of an external device filter if (!IsConnected()) { return NOERROR; } hr = CBaseOutputPin::Active(); if (FAILED(hr)) { return hr; } ASSERT(!ThreadExists()); // start the thread if (!Create()) { return E_FAIL; } // Tell thread to initialize. If OnThreadCreate Fails, so does this. hr = Init(); if (FAILED(hr)) return hr; return Pause(); } //------------------------------------------ // // Inactive // // Pin is inactive - shut down the worker thread // Waits for the worker to exit before returning. // //------------------------------------------ HRESULT COutStream::Inactive(void) { CAutoLock lock(m_pFilter->pLock()); HRESULT hr; // do nothing if not connected - its ok not to connect to // all pins of an external device filter if (!IsConnected()) return NOERROR; // !!! need to do this before trying to stop the thread, because // we may be stuck waiting for our own allocator!!! hr = CBaseOutputPin::Inactive(); // call this first to decommit // the allocator if (FAILED(hr)) return hr; if (ThreadExists()) { hr = Stop(); if (FAILED(hr)) { return hr; } hr = Exit(); if (FAILED(hr)) { return hr; } Close(); // Wait for the thread to exit, then tidy up. } return NOERROR; } //------------------------------------------ // // ThreadProc // // When this returns the thread exits // Return codes > 0 indicate an error occured // //------------------------------------------ DWORD COutStream::ThreadProc(void) { HRESULT hr; // the return code from calls Command com; REFERENCE_TIME rtNow; DWORD dwAdviseToken; do { com = GetRequest(); if (com != CMD_INIT) { DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command"))); Reply(E_UNEXPECTED); } } while (com != CMD_INIT); DbgLog((LOG_TRACE, 1, TEXT("COutStream worker thread initializing"))); hr = OnThreadCreate(); // perform set up tasks if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("COutStream::OnThreadCreate failed. Aborting thread."))); OnThreadDestroy(); Reply(hr); // send failed return code from OnThreadCreate return 1; } // Initialisation suceeded Reply(NOERROR); Command cmd; do { cmd = GetRequest(); switch (cmd) { case CMD_EXIT: Reply(NOERROR); break; case CMD_RUN: DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???"))); // !!! fall through??? case CMD_PAUSE: Reply(NOERROR); // we pick up the reference clock here if ( m_pFilter->GetSyncSource(&m_pRefClock) ) return (DWORD) VFW_E_NO_CLOCK; ASSERT(m_pRefClock); // and set the tick to 33.33ms (an NTSC video frame). We use a // "one-shot" event instead of a periodic notification because // we don't want events to stack up due to system latencies. m_hAdviseEvent = CreateEvent(NULL, FALSE, FALSE, NULL); m_pRefClock->GetTime(&rtNow); m_pRefClock->AdviseTime(m_tStart, rtNow+rtPeriodTime, (unsigned long)m_hAdviseEvent, &dwAdviseToken); DoBufferProcessingLoop(); break; case CMD_STOP: // release our reference clock m_pRefClock->Release(); m_pRefClock = NULL; m_hAdviseEvent = NULL; Reply(NOERROR); break; default: DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd)); Reply(E_NOTIMPL); break; } } while (cmd != CMD_EXIT); hr = OnThreadDestroy(); // tidy up. if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("COutStream::OnThreadDestroy failed. Exiting thread."))); return 1; } DbgLog((LOG_TRACE, 1, TEXT("COutStream worker thread exiting"))); return 0; } //------------------------------------------ // // DoBufferProcessingLoop // // Grabs a buffer and waits for a frame time to pass // //------------------------------------------ HRESULT COutStream::DoBufferProcessingLoop(void) { Command com; REFERENCE_TIME rtNow = 0L; REFERENCE_TIME rtAdvise = 0L; DWORD dwAdviseToken; OnThreadStartPlay(); do { while (!CheckRequest(&com)) { IMediaSample *pSample; HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,FALSE); if (FAILED(hr)) { continue; // go round again. Perhaps the error will go away // or the allocator is decommited & we will be asked to // exit soon. } // Virtual function user will override. hr = FillBuffer(pSample); // !!! shouldn't we check for errors here??? if (hr == S_OK) { Deliver(pSample); } else if (hr == S_FALSE) { pSample->Release(); DeliverEndOfStream(); return S_OK; } else { DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr)); } pSample->Release(); // and block here for a frame - // but only do this if an event has been set if (m_hAdviseEvent) { WaitForSingleObject(m_hAdviseEvent, 1000L); // woke up - show the time m_pRefClock->GetTime(&rtNow); rtAdvise = rtNow + rtPeriodTime; // reset the clock m_pRefClock->AdviseTime(m_tStart, rtAdvise, (unsigned long)m_hAdviseEvent, &dwAdviseToken); } } if (com == CMD_RUN || com == CMD_PAUSE) com = GetRequest(); // throw command away else if (com != CMD_STOP) { DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!"))); } } while (com != CMD_STOP); return S_FALSE; } //------------------------------------------ //------------------------------------------ //------------------------------------------ // * // * CVcrTCOut // * //------------------------------------------ //------------------------------------------ //------------------------------------------ //------------------------------------------ // // CVcrTCOut::Constructor // // Create a default stream // //------------------------------------------ CVcrTCOut::CVcrTCOut(HRESULT *phr, CVcrFilter *pParent, LPCWSTR pName) : COutStream(NAME("VCR Timecode output pin"),phr, pParent, pName), m_pFilter(pParent), m_ctc(NULL), m_mtype(0), m_DeviceID(0L), m_bNotify(0L) { CAutoLock l(m_pFilter->pLock()); m_ctc = new CTimecode(); } //------------------------------------------ // // CVcrTCOut::Destructor // //------------------------------------------ CVcrTCOut::~CVcrTCOut(void) { CAutoLock l(m_pFilter->pLock()); delete m_ctc; } //------------------------------------------ // // FillBuffer // // Stuffs the buffer with data // //------------------------------------------ HRESULT CVcrTCOut::FillBuffer(IMediaSample *pms) { BYTE *pData; long mode; DWORD dwresult; HRESULT hr; TIMECODE_SAMPLE tcsTimecode ; DbgFunc("FillBuffer"); pms->GetPointer(&pData); //what time is it? CRefTime rtStart = m_rtSampleTime; // the current time is // the sample's start m_rtSampleTime += (LONG)(1000L*1001L)/30000L; // 29.97 fps for // now in 100 ns units pms->SetTime((REFERENCE_TIME*)&rtStart, (REFERENCE_TIME*)&m_rtSampleTime); if ( *(m_mt.Type()) == MEDIATYPE_Text) pms->SetActualDataLength(textbufsize); else pms->SetActualDataLength(sizeof(TIMECODE_SAMPLE)); // should we update the display (only when in PLAY)? m_pFilter->get_Mode(&mode); if ( mode == ED_MODE_PLAY ) { // get a new timecode if ( (dwresult = m_pFilter->GetTimecode(&tcsTimecode)) ) tcsTimecode.timecode.dwFrames = -1; if ( m_mtype == 0) { // we need to output text - make the timecode displayable m_ctc->ConvertTimecodeToString(&tcsTimecode, (TCHAR *)pData); } else { // dummy out unused members tcsTimecode.qwTick = m_rtSampleTime; tcsTimecode.timecode.wFrameFract = 0; tcsTimecode.dwUser = 0L; tcsTimecode.dwFlags = 0x0000; // make it non-drop for now memcpy((TCHAR *)pData, &tcsTimecode, sizeof(TIMECODE_SAMPLE)); } // notify the world that timecode is available if (m_bNotify) { DbgFunc("TCNotify"); // make sure the app is plucking these buggers from the graph's event queue hr = m_pFilter->NotifyEvent(EC_TIMECODE_AVAILABLE, (long)this, (DWORD)m_DeviceID); } } return NOERROR; } //------------------------------------------ // // GetMediaType // //------------------------------------------ HRESULT CVcrTCOut::GetMediaType(int iPosition, CMediaType *pmt) { CAutoLock l(m_pFilter->pLock()); if (iPosition<0) { return E_INVALIDARG; } // we support 2 media types now if (iPosition == 0) { pmt->SetType(&MEDIATYPE_Text); pmt->SetSubtype(&GUID_NULL); } else if (iPosition == 1) { pmt->SetType(&MEDIATYPE_Timecode); pmt->SetSubtype(&MEDIASUBTYPE_NULL); } else if (iPosition>1) { return VFW_S_NO_MORE_ITEMS; } // no temporal compression in both cases pmt->SetTemporalCompression(FALSE); return NOERROR; } //------------------------------------------ // // CheckMediaType // // Returns E_INVALIDARG if the mediatype is not // acceptable, S_OK if it is // //------------------------------------------ HRESULT CVcrTCOut::CheckMediaType(const CMediaType *pMediaType) { CAutoLock l(m_pFilter->pLock()); if (*(pMediaType->Type()) != MEDIATYPE_Text && *(pMediaType->Type()) != MEDIATYPE_Timecode) //Text or timecode only return E_INVALIDARG; return S_OK; // This format is acceptable. } //------------------------------------------ // // 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 media sample // we agreed. Then we can ask for buffers of the correct size to contain them. // //------------------------------------------ HRESULT CVcrTCOut::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pRequest) { CAutoLock l(m_pFilter->pLock()); if ( *(m_mt.Type()) == MEDIATYPE_Text) pRequest->cbBuffer = textbufsize; //the size we will request else pRequest->cbBuffer = sizeof(TIMECODE_SAMPLE);//the size we will request pRequest->cBuffers = 5; //the number of buffers to use. ALLOCATOR_PROPERTIES Actual; pAlloc->SetProperties(pRequest, &Actual); if ((Actual.cbBuffer < pRequest->cbBuffer) ) // this allocator is unsuitable return E_FAIL; return NOERROR; } //------------------------------------------ // // OnThreadCreate // // as we go active reset the stream time to zero // //------------------------------------------ HRESULT CVcrTCOut::OnThreadCreate(void) { // save a local copy of the currently selected media type // for this pin: 0 for text, 1 for timecode if (*(m_mt.Type()) == MEDIATYPE_Text) m_mtype = 0; else m_mtype = 1; m_rtSampleTime = 0; m_tcsTimecode.timecode.dwFrames = 0L; // Find out if notification should be used for timecode availability m_pFilter->GetTCRMode(ED_MODE_NOTIFY_ENABLE, &m_bNotify); // and pick up the DeviceID that will be used for the notifications. m_pFilter->get_DevicePort(&m_DeviceID); return NOERROR; } //------------------------------------------ // // OnThreadDestroy // // cleanup // //------------------------------------------ HRESULT CVcrTCOut::OnThreadDestroy(void) { return NOERROR; } //------------------------------------------ //------------------------------------------ //------------------------------------------ // * // * CVcrInputPin // * // // Base class for hardware input pins //------------------------------------------ //------------------------------------------ //------------------------------------------ //------------------------------------------ // // CVcrInputPin Constructor // //------------------------------------------ CVcrInputPin::CVcrInputPin(TCHAR *pObjectName, CVcrFilter *pFilter, HRESULT * phr, LPCWSTR pName) : CBaseInputPin(pObjectName, pFilter, pFilter->pLock(), phr, pName), CAMPhysicalPinInfo(phr, (TCHAR *)pName), m_pFilter(pFilter) { DbgLog((LOG_TRACE,1,TEXT("CVcrInputPin constructor"))); ASSERT(pFilter); *phr = m_pFilter->AddPin(this); } //------------------------------------------ // // CVcrInputPin Destructor // //------------------------------------------ CVcrInputPin::~CVcrInputPin(void) { DbgLog((LOG_TRACE,1,TEXT("*Destroying an input pin"))); m_pFilter->RemovePin(this); } //------------------------------------------ // // NonDelegatingQueryInterface // // Reveal our Interfaces //------------------------------------------ STDMETHODIMP CVcrInputPin::NonDelegatingQueryInterface(REFIID riid, void **ppv) { CAutoLock cObjectLock(&m_StateLock); if (riid == IID_IAMPhysicalPinInfo) return GetInterface((IAMPhysicalPinInfo *) this, ppv); else return CBaseInputPin::NonDelegatingQueryInterface(riid, ppv); } //------------------------------------------ //------------------------------------------ //------------------------------------------ // * // * CVcrVideoCompInputPin // * //------------------------------------------ //------------------------------------------ //------------------------------------------ //------------------------------------------ // // CVcrVideoCompInputPin Constructor // //------------------------------------------ CVcrVideoCompInputPin::CVcrVideoCompInputPin(TCHAR *pObjectName, CVcrFilter *pFilter, HRESULT * phr, LPCWSTR pName) : CVcrInputPin(pObjectName, pFilter, phr, pName) { } //------------------------------------------ // // CVcrVideoCompInputPin Destructor // //------------------------------------------ CVcrVideoCompInputPin::~CVcrVideoCompInputPin(void) { } //------------------------------------------ // // GetPhysicalType // // Returns physical connection info // //------------------------------------------ HRESULT CVcrVideoCompInputPin::GetPhysicalType( long *pType, LPOLESTR * ppszType) { *pType = PhysConn_Video_Composite; CheckPointer(ppszType, E_POINTER); *ppszType = NULL; // query the device *ppszType = (LPOLESTR) QzTaskMemAlloc(sizeof(WCHAR) * \ (1+lstrlenW((LPOLESTR)strPhysConn_Video_Composite))); if (*ppszType !=NULL) lstrcpyW(*ppszType, (LPOLESTR)strPhysConn_Video_Composite); return S_OK; } //------------------------------------------ //------------------------------------------ //------------------------------------------ // * // * CVcrVideoSInputPin // * //------------------------------------------ //------------------------------------------ //------------------------------------------ //------------------------------------------ // // CVcrVideoSInputPin Constructor // //------------------------------------------ CVcrVideoSInputPin::CVcrVideoSInputPin(TCHAR *pObjectName, CVcrFilter *pFilter, HRESULT * phr, LPCWSTR pName) : CVcrInputPin(pObjectName, pFilter, phr, pName) { } //------------------------------------------ // // CVcrVideoSInputPin Destructor // //------------------------------------------ CVcrVideoSInputPin::~CVcrVideoSInputPin(void) { } //------------------------------------------ // // GetPhysicalType // // Returns physical connection info // //------------------------------------------ HRESULT CVcrVideoSInputPin::GetPhysicalType( long *pType, LPOLESTR * ppszType) { *pType = PhysConn_Video_SVideo; CheckPointer(ppszType, E_POINTER); *ppszType = NULL; // query the device *ppszType = (LPOLESTR) QzTaskMemAlloc(sizeof(WCHAR) * \ (1+lstrlenW((LPOLESTR)strPhysConn_Video_SVideo))); if (*ppszType !=NULL) lstrcpyW(*ppszType, (LPOLESTR)strPhysConn_Video_SVideo); return S_OK; } //------------------------------------------ //------------------------------------------ //------------------------------------------ // * // * CVcrVideoBlackInputPin // * //------------------------------------------ //------------------------------------------ //------------------------------------------ //------------------------------------------ // // CVcrVideoBlackInputPin Constructor // //------------------------------------------ CVcrVideoBlackInputPin::CVcrVideoBlackInputPin(TCHAR *pObjectName, CVcrFilter *pFilter, HRESULT * phr, LPCWSTR pName) : CVcrInputPin(pObjectName, pFilter, phr, pName) { } //------------------------------------------ // // CVcrVideoBlackInputPin Destructor // //------------------------------------------ CVcrVideoBlackInputPin::~CVcrVideoBlackInputPin(void) { } //------------------------------------------ // // GetPhysicalType // // Returns physical connection info // //------------------------------------------ HRESULT CVcrVideoBlackInputPin::GetPhysicalType( long *pType, LPOLESTR * ppszType) { *pType = PhysConn_Video_Black; CheckPointer(ppszType, E_POINTER); *ppszType = NULL; // query the device *ppszType = (LPOLESTR) QzTaskMemAlloc(sizeof(WCHAR) * \ (1+lstrlenW((LPOLESTR)strPhysConn_Video_Black))); if (*ppszType !=NULL) lstrcpyW(*ppszType, (LPOLESTR)strPhysConn_Video_Black); return S_OK; } //------------------------------------------ //------------------------------------------ //------------------------------------------ // * // * CVcrAudioLineInputPin // * //------------------------------------------ //------------------------------------------ //------------------------------------------ //------------------------------------------ // // CVcrAudioLineInputPin Constructor // //------------------------------------------ CVcrAudioLineInputPin::CVcrAudioLineInputPin(TCHAR *pObjectName, CVcrFilter *pFilter, HRESULT * phr, LPCWSTR pName) : CVcrInputPin(pObjectName, pFilter, phr, pName) { } //------------------------------------------ // // CVcrAudioLineInputPin Destructor // //------------------------------------------ CVcrAudioLineInputPin::~CVcrAudioLineInputPin(void) { } //------------------------------------------ // // GetPhysicalType // // Returns physical connection info // //------------------------------------------ HRESULT CVcrAudioLineInputPin::GetPhysicalType( long *pType, LPOLESTR * ppszType) { *pType = PhysConn_Audio_Line; CheckPointer(ppszType, E_POINTER); *ppszType = NULL; // query the device *ppszType = (LPOLESTR) QzTaskMemAlloc(sizeof(WCHAR) * (1+lstrlenW((LPOLESTR)strPhysConn_Audio_Line))); if (*ppszType !=NULL) lstrcpyW(*ppszType, (LPOLESTR)strPhysConn_Audio_Line); return S_OK; } //------------------------------------------ //------------------------------------------ //------------------------------------------ // * // * CVcrAudioMicInputPin // * //------------------------------------------ //------------------------------------------ //------------------------------------------ //------------------------------------------ // // CVcrAudioMicInputPin Constructor // //------------------------------------------ CVcrAudioMicInputPin::CVcrAudioMicInputPin(TCHAR *pObjectName, CVcrFilter *pFilter, HRESULT * phr, LPCWSTR pName) : CVcrInputPin(pObjectName, pFilter, phr, pName) { } //------------------------------------------ // // CVcrAudioMicInputPin Destructor // //------------------------------------------ CVcrAudioMicInputPin::~CVcrAudioMicInputPin(void) { } //------------------------------------------ // // GetPhysicalType // // Returns physical connection info // //------------------------------------------ HRESULT CVcrAudioMicInputPin::GetPhysicalType( long *pType, LPOLESTR * ppszType) { *pType = PhysConn_Audio_Mic; CheckPointer(ppszType, E_POINTER); *ppszType = NULL; // query the device *ppszType = (LPOLESTR) QzTaskMemAlloc(sizeof(WCHAR) * \ (1+lstrlenW((LPOLESTR)strPhysConn_Audio_Mic))); if (*ppszType !=NULL) lstrcpyW(*ppszType, (LPOLESTR)strPhysConn_Audio_Mic); return S_OK; } //eof fvcrctrl.cpp