// Copyright (c) Microsoft Corporation 1994-1996. All Rights Reserved // Timecode decoder, David Maymudes, November 1996 // // #include #include // XXX using backslashes since makedepend is broken! #include "..\timecode.h" #include "..\ltcdcode.h" #include "..\tchelper.h" #ifdef FILTER_DLL // define the GUIDs for streams and my CLSID in this file #include #endif extern const AMOVIESETUP_FILTER sudTCDecode; // Class ID for CTCDecode objects // // 6a08cf9f-0e18-11cf-a24d-0020afd79767 DEFINE_GUID(CLSID_TCDecoder, 0x6a08cf9f, 0x0e18, 0x11cf, 0xa2, 0x4d, 0x0, 0x20, 0xaf, 0xd7, 0x97, 0x67); const int TIMECODE_SIZE = 50; // max size of a timecode string class CTCDecode : public CTransformFilter { public: CTCDecode(TCHAR *, LPUNKNOWN, HRESULT *); ~CTCDecode(); DECLARE_IUNKNOWN HRESULT Transform(IMediaSample * pIn, IMediaSample * pOut); HRESULT Receive( IMediaSample *pInSample ); // check if you can support mtIn HRESULT CheckInputType(const CMediaType* mtIn); // check if you can support the transform from this input to // this output HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut); // called from CBaseOutputPin to prepare the allocator's count // of buffers and sizes HRESULT DecideBufferSize(IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pProperties); // optional overrides - we want to know when streaming starts and stops HRESULT StartStreaming(); HRESULT StopStreaming(); // overriden to suggest OUTPUT pin media types HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); // this goes in the factory template table to create new instances static CUnknown * CreateInstance(LPUNKNOWN, HRESULT *); STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv); private: LTCdecoder decoder; DWORD m_nSamplesPerSec; DWORD m_nBlockAlign; LONGLONG _sampleNumber; }; //***************************************************************************** //***************************************************************************** // setup data const AMOVIESETUP_MEDIATYPE sudInTypes = { &MEDIATYPE_Audio // clsMajorType , &MEDIASUBTYPE_NULL }; // clsMinorType const AMOVIESETUP_MEDIATYPE sudOutTypes = { &MEDIATYPE_Text // clsMajorType , &MEDIASUBTYPE_NULL }; // clsMinorType const AMOVIESETUP_PIN sudpPins [] = { { L"Input" // strName , FALSE // bRendered , FALSE // bOutput , FALSE // bZero , FALSE // bMany , &CLSID_NULL // clsConnectsToFilter , L"Output" // strConnectsToPin , 1 // nTypes , &sudInTypes // lpTypes }, { L"Output" // strName , FALSE // bRendered , TRUE // bOutput , FALSE // bZero , FALSE // bMany , &CLSID_NULL // clsConnectsToFilter , L"Input" // strConnectsToPin , 1 // nTypes , &sudOutTypes // lpTypes } }; const AMOVIESETUP_FILTER sudTCDecode = { &CLSID_TCDecoder // clsID , L"Timecode decoder" // strName , MERIT_NORMAL // dwMerit , 2 // nPins , sudpPins }; // lpPin //***************************************************************************** //***************************************************************************** //***************************************************************************** #ifdef FILTER_DLL // List of class IDs and creator functions for class factory CFactoryTemplate g_Templates[] = { { L"Timecode Decoder" , &CLSID_TCDecoder , CTCDecode::CreateInstance , NULL , &sudTCDecode } }; 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). // STDAPI DllRegisterServer() { return AMovieDllRegisterServer2( TRUE ); } STDAPI DllUnregisterServer() { return AMovieDllRegisterServer2( FALSE ); } #endif //***************************************************************************** //***************************************************************************** //***************************************************************************** // // CreateInstance() // // CUnknown *CTCDecode::CreateInstance(LPUNKNOWN pUnk, HRESULT * phr) { DbgLog((LOG_TRACE, 2, TEXT("CTCDecode::CreateInstance"))); return new CTCDecode(TEXT("LTC decoder"), pUnk, phr); } //***************************************************************************** // // NonDelegatingQueryInterface() // // STDMETHODIMP CTCDecode::NonDelegatingQueryInterface( REFIID riid, void **ppv ) { return CTransformFilter::NonDelegatingQueryInterface(riid, ppv); } //***************************************************************************** // // CTCDecode() // // CTCDecode::CTCDecode( TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr ) : CTransformFilter( pName, pUnk, CLSID_TCDecoder) { _sampleNumber = 0; DbgLog((LOG_TRACE,3,TEXT("CTCDecode"))); } //***************************************************************************** // // ~CTCDecode() // // CTCDecode::~CTCDecode() { DbgLog((LOG_TRACE,3,TEXT("~CTCDecode"))); } //***************************************************************************** // // CheckInputType() // // HRESULT CTCDecode::CheckInputType(const CMediaType* pmtIn) { DbgLog((LOG_TRACE, 3, TEXT("CTCDecode::CheckInputType"))); DisplayType("pmtIn details:", pmtIn); WAVEFORMATEX * pwfx = (WAVEFORMATEX *)pmtIn->Format(); if (pmtIn->majortype != MEDIATYPE_Audio) { DbgLog((LOG_ERROR, 1, TEXT("*** CheckInputType only takes audio"))); return E_INVALIDARG; } if( *pmtIn->FormatType() != FORMAT_WaveFormatEx ) { DbgLog((LOG_ERROR, 1, TEXT(" pmtOut->FormatType != FORMAT_WaveFormatEx!"))); return E_INVALIDARG; } if (pmtIn->FormatLength() < sizeof(PCMWAVEFORMAT)) { DbgLog((LOG_ERROR, 1, TEXT("*** pmtIn->FormatLength < PCMWAVEFORMAT"))); return E_INVALIDARG; } // only accept mono 16 bit data if (pwfx->wFormatTag != WAVE_FORMAT_PCM || pwfx->wBitsPerSample != 16 || pwfx->nChannels != 1) return E_INVALIDARG; return S_OK; } //***************************************************************************** // // GetMediaType() // HRESULT CTCDecode::GetMediaType( int iPosition, CMediaType *pmt ) { DbgLog((LOG_TRACE, 3, TEXT("CTCDecode::GetMediaType"))); DbgLog((LOG_TRACE, 3, TEXT(" iPosition = %d"),iPosition)); if( iPosition != 0 ) { return E_INVALIDARG; } pmt->SetType( &MEDIATYPE_Text ); pmt->SetSubtype( &GUID_NULL ); pmt->SetFormatType( &GUID_NULL ); return S_OK; } //***************************************************************************** // // CheckTransform() // // HRESULT CTCDecode::CheckTransform(const CMediaType* pmtIn, const CMediaType* pmtOut) { HRESULT hr = CheckInputType(pmtIn); if (FAILED(hr)) return hr; DisplayType("pmtOut:", pmtOut); if (pmtOut->majortype != MEDIATYPE_Text) { DbgLog((LOG_ERROR, 1, TEXT("*** output type must be text"))); return E_INVALIDARG; } return S_OK; } //***************************************************************************** // // DecideBufferSize() // HRESULT CTCDecode::DecideBufferSize( IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pProperties ) { DbgLog((LOG_TRACE, 3, TEXT("CTCDecode::DecideBufferSize"))); if (pProperties->cBuffers < 4) pProperties->cBuffers = 4; if (pProperties->cbBuffer < TIMECODE_SIZE) pProperties->cbBuffer = TIMECODE_SIZE; if (pProperties->cbAlign < 1) pProperties->cbAlign = 1; ALLOCATOR_PROPERTIES Actual; HRESULT hr = pAllocator->SetProperties(pProperties,&Actual); if (FAILED(hr)) { return hr; } return S_OK; } //***************************************************************************** // // StartStreaming() // // HRESULT CTCDecode::StartStreaming() { CAutoLock lock(&m_csFilter); DbgLog((LOG_TRACE, 3, TEXT("CTCDecode::StartStreaming"))); WAVEFORMATEX *pwfx = (WAVEFORMATEX *)m_pInput->CurrentMediaType().Format(); m_nSamplesPerSec = pwfx->nSamplesPerSec ; m_nBlockAlign = pwfx->nBlockAlign ; // !!! decoder.Reset(); return S_OK; } //***************************************************************************** // // StopStreaming() // // HRESULT CTCDecode::StopStreaming() { CAutoLock lock(&m_csFilter); DbgLog((LOG_TRACE, 3, TEXT("CTCDecode::StopStreaming"))); return NOERROR; } HRESULT CTCDecode::Transform( IMediaSample *pIn, IMediaSample *pOut ) { DbgLog((LOG_ERROR, 1, TEXT("*** CTCDecode->Transform() called!"))); ASSERT(0); // !!! shouldn't be called! return E_FAIL; } HRESULT CTCDecode::Receive( IMediaSample *pInSample ) { HRESULT hr = NOERROR; REFERENCE_TIME inBufStartTime; // start/stop time of input buffer REFERENCE_TIME inBufStopTime; REFERENCE_TIME tcStartTime; // start/stop time of the timecode REFERENCE_TIME tcStopTime; LONGLONG startTCsample, stopTCsample; // 1st, last sample of timecode BYTE *pbSample; LONG cbSampleLength, cbLeft; DbgLog((LOG_TRACE, 4, TEXT("CTCDecode::Receive"))); // get input start and stop times pInSample->GetTime(&inBufStartTime, &inBufStopTime); pInSample->GetPointer( &pbSample ); DbgLog((LOG_TRACE, 3, TEXT("Total Sample: Start = %s End = %s"), (LPCTSTR)CDisp((LONGLONG)(inBufStartTime),CDISP_HEX), (LPCTSTR)CDisp((LONGLONG)(inBufStopTime),CDISP_HEX))); cbSampleLength = pInSample->GetActualDataLength() / m_nBlockAlign; cbLeft = cbSampleLength; // sample number of the last sample in buf LONGLONG lastSampleNumber= _sampleNumber + cbSampleLength; DbgLog((LOG_TRACE, 4, TEXT(" cbSampleLength = %d"),cbSampleLength)); while (cbLeft > 0) { TimeCode tc; LONGLONG syncSample; // the audio sample that this tc corresponds to if (decoder.decodeBuffer((short **) &pbSample, (int *) &cbLeft)) { IMediaSample *pOutSample; decoder.getTimeCode(&tc); decoder.getStartStopSample(&startTCsample, &stopTCsample); hr = m_pOutput->GetDeliveryBuffer( &pOutSample, NULL, NULL, 0 ); if( FAILED(hr) ) { DbgLog((LOG_ERROR, 1, TEXT("GetDeliveryBuffer(pOutSample) failed, hr = %u"),hr)); return hr; } pOutSample->SetSyncPoint( pInSample->IsSyncPoint() == S_OK ); // not really right.... pOutSample->SetDiscontinuity( pInSample->IsDiscontinuity() == S_OK ); tcStartTime = extrapolate(_sampleNumber, inBufStartTime, // begin bf lastSampleNumber, inBufStopTime, // end bf startTCsample); // samnumber of desired time tcStopTime = extrapolate(_sampleNumber, inBufStartTime, // begin bf lastSampleNumber, inBufStopTime, // end bf stopTCsample); // samnumber of desired time // !!! really should be last tStop of the last timecode sent.... // XXX realy? I am not so sure... pOutSample->SetTime(&tcStartTime, &tcStopTime); DbgLog((LOG_TRACE, 4, TEXT(" Breaking up: Start = %s End = %s"), (LPCTSTR)CDisp((LONGLONG)(tcStartTime),CDISP_HEX), (LPCTSTR)CDisp((LONGLONG)(tcStopTime),CDISP_HEX))); BYTE *pbOut; pOutSample->GetPointer( &pbOut ); tc.GetString((char *) pbOut); pOutSample->SetActualDataLength(lstrlen((char *) pbOut) + 1); hr = m_pOutput->Deliver(pOutSample); // release the output buffer. If the connected pin still needs it, // it will have addrefed it itself. pOutSample->Release(); if (FAILED(hr)) return hr; } } // sample count of 1st sample in decode buffer // needed because the synch will occur in a PREVIOUS buffer (or 1st samp) _sampleNumber += cbSampleLength; return S_OK; } // !!! do we need special EOS handling?