Source code of Windows XP (NT5)
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.

477 lines
12 KiB

  1. // Copyright (c) 1994 - 1998 Microsoft Corporation. All Rights Reserved.
  2. // Video Timecode decoder, David Maymudes, January 1997
  3. //
  4. //
  5. #include <streams.h>
  6. #include <mmsystem.h>
  7. // XXX using backslashes since makedepend is broken!
  8. #include "..\timecode.h"
  9. #include "..\vtcdcode.h"
  10. #include "..\tchelper.h"
  11. #ifdef FILTER_DLL
  12. // define the GUIDs for streams and my CLSID in this file
  13. #include <initguid.h>
  14. #endif
  15. extern const AMOVIESETUP_FILTER sudVITCDecode;
  16. // Class ID for CVITCDecode objects
  17. //
  18. // 6a08cfa0-0e18-11cf-a24d-0020afd79767
  19. DEFINE_GUID(CLSID_VITCDecoder,
  20. 0x6a08cfa0, 0x0e18, 0x11cf, 0xa2, 0x4d, 0x0, 0x20, 0xaf, 0xd7, 0x97, 0x67);
  21. const int TIMECODE_SIZE = 50; // max size of a timecode string
  22. class CVITCDecode : public CTransformFilter
  23. {
  24. public:
  25. CVITCDecode(TCHAR *, LPUNKNOWN, HRESULT *);
  26. ~CVITCDecode();
  27. DECLARE_IUNKNOWN
  28. HRESULT Transform(IMediaSample * pIn, IMediaSample * pOut);
  29. HRESULT Receive( IMediaSample *pInSample );
  30. // check if you can support mtIn
  31. HRESULT CheckInputType(const CMediaType* mtIn);
  32. // check if you can support the transform from this input to
  33. // this output
  34. HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut);
  35. // called from CBaseOutputPin to prepare the allocator's count
  36. // of buffers and sizes
  37. HRESULT DecideBufferSize(IMemAllocator * pAllocator,
  38. ALLOCATOR_PROPERTIES *pProperties);
  39. // optional overrides - we want to know when streaming starts and stops
  40. HRESULT StartStreaming();
  41. HRESULT StopStreaming();
  42. // overriden to suggest OUTPUT pin media types
  43. HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
  44. // this goes in the factory template table to create new instances
  45. static CUnknown * CreateInstance(LPUNKNOWN, HRESULT *);
  46. STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv);
  47. private:
  48. VITCdecoder decoder;
  49. DWORD m_nWidth;
  50. DWORD m_nHeight;
  51. int m_nLine;
  52. BOOL m_bTimecodeSeen;
  53. // !!! statistics....
  54. LONG m_lStartTime;
  55. int m_iFrames, m_iErrors;
  56. };
  57. //*****************************************************************************
  58. //*****************************************************************************
  59. // setup data
  60. const AMOVIESETUP_MEDIATYPE
  61. sudInTypes = { &MEDIATYPE_Video // clsMajorType
  62. , &MEDIASUBTYPE_NULL }; // clsMinorType
  63. const AMOVIESETUP_MEDIATYPE
  64. sudOutTypes = { &MEDIATYPE_Text // clsMajorType
  65. , &MEDIASUBTYPE_NULL }; // clsMinorType
  66. const AMOVIESETUP_PIN sudpPins [] =
  67. {
  68. { L"Input" // strName
  69. , FALSE // bRendered
  70. , FALSE // bOutput
  71. , FALSE // bZero
  72. , FALSE // bMany
  73. , &CLSID_NULL // clsConnectsToFilter
  74. , L"Output" // strConnectsToPin
  75. , 1 // nTypes
  76. , &sudInTypes // lpTypes
  77. },
  78. { L"Output" // strName
  79. , FALSE // bRendered
  80. , TRUE // bOutput
  81. , FALSE // bZero
  82. , FALSE // bMany
  83. , &CLSID_NULL // clsConnectsToFilter
  84. , L"Input" // strConnectsToPin
  85. , 1 // nTypes
  86. , &sudOutTypes // lpTypes
  87. }
  88. };
  89. const AMOVIESETUP_FILTER sudVITCDecode =
  90. { &CLSID_VITCDecoder // clsID
  91. , L"Video Timecode decoder" // strName
  92. , MERIT_NORMAL // dwMerit
  93. , 2 // nPins
  94. , sudpPins }; // lpPin
  95. //*****************************************************************************
  96. //*****************************************************************************
  97. //*****************************************************************************
  98. #ifdef FILTER_DLL
  99. // List of class IDs and creator functions for class factory
  100. CFactoryTemplate g_Templates[] =
  101. {
  102. { L"Video Timecode Decoder"
  103. , &CLSID_VITCDecoder
  104. , CVITCDecode::CreateInstance
  105. , NULL
  106. , &sudVITCDecode
  107. }
  108. };
  109. int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
  110. //*****************************************************************************
  111. //*****************************************************************************
  112. //*****************************************************************************
  113. // exported entry points for registration and unregistration (in this case they
  114. // only call through to default implmentations).
  115. //
  116. STDAPI DllRegisterServer()
  117. {
  118. return AMovieDllRegisterServer2( TRUE );
  119. }
  120. STDAPI DllUnregisterServer()
  121. {
  122. return AMovieDllRegisterServer2( FALSE );
  123. }
  124. #endif
  125. //*****************************************************************************
  126. //*****************************************************************************
  127. //*****************************************************************************
  128. //
  129. // CreateInstance()
  130. //
  131. //
  132. CUnknown *CVITCDecode::CreateInstance(LPUNKNOWN pUnk, HRESULT * phr)
  133. {
  134. DbgLog((LOG_TRACE, 2, TEXT("CVITCDecode::CreateInstance")));
  135. return new CVITCDecode(TEXT("VITC decoder"), pUnk, phr);
  136. }
  137. //*****************************************************************************
  138. //
  139. // NonDelegatingQueryInterface()
  140. //
  141. //
  142. STDMETHODIMP CVITCDecode::NonDelegatingQueryInterface( REFIID riid, void **ppv )
  143. {
  144. return CTransformFilter::NonDelegatingQueryInterface(riid, ppv);
  145. }
  146. //*****************************************************************************
  147. //
  148. // CVITCDecode()
  149. //
  150. //
  151. CVITCDecode::CVITCDecode( TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr )
  152. : CTransformFilter( pName,
  153. pUnk,
  154. CLSID_VITCDecoder)
  155. {
  156. DbgLog((LOG_TRACE,3,TEXT("CVITCDecode")));
  157. }
  158. //*****************************************************************************
  159. //
  160. // ~CVITCDecode()
  161. //
  162. //
  163. CVITCDecode::~CVITCDecode()
  164. {
  165. DbgLog((LOG_TRACE,3,TEXT("~CVITCDecode")));
  166. \
  167. }
  168. //*****************************************************************************
  169. //
  170. // CheckInputType()
  171. //
  172. //
  173. HRESULT CVITCDecode::CheckInputType(const CMediaType* pmtIn)
  174. {
  175. DbgLog((LOG_TRACE, 3, TEXT("CVITCDecode::CheckInputType")));
  176. DisplayType("pmtIn details:", pmtIn);
  177. if (pmtIn->majortype != MEDIATYPE_Video) {
  178. DbgLog((LOG_ERROR, 1, TEXT("*** CheckInputType only takes video")));
  179. return E_INVALIDARG;
  180. }
  181. if( *pmtIn->FormatType() != FORMAT_VideoInfo ) {
  182. DbgLog((LOG_ERROR, 1, TEXT(" pmtOut->FormatType != FORMAT_VideoInfo!")));
  183. return E_INVALIDARG;
  184. }
  185. VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)pmtIn->Format();
  186. if( pvi->bmiHeader.biWidth < 400 || abs(pvi->bmiHeader.biHeight) > 20 ) {
  187. DbgLog((LOG_ERROR, 1, TEXT(" video not right shape for VBI data")));
  188. return E_INVALIDARG;
  189. }
  190. if (pvi->bmiHeader.biCompression != BI_RGB && pvi->bmiHeader.biCompression != BI_BITFIELDS) {
  191. DbgLog((LOG_ERROR, 1, TEXT(" video not uncompressed")));
  192. return E_INVALIDARG;
  193. }
  194. return S_OK;
  195. }
  196. //*****************************************************************************
  197. //
  198. // GetMediaType()
  199. //
  200. HRESULT CVITCDecode::GetMediaType( int iPosition, CMediaType *pmt )
  201. {
  202. DbgLog((LOG_TRACE, 3, TEXT("CVITCDecode::GetMediaType")));
  203. DbgLog((LOG_TRACE, 3, TEXT(" iPosition = %d"),iPosition));
  204. if( iPosition != 0 ) {
  205. return E_INVALIDARG;
  206. }
  207. pmt->SetType( &MEDIATYPE_Text );
  208. pmt->SetSubtype( &GUID_NULL );
  209. pmt->SetFormatType( &GUID_NULL );
  210. return S_OK;
  211. }
  212. //*****************************************************************************
  213. //
  214. // CheckTransform()
  215. //
  216. //
  217. HRESULT CVITCDecode::CheckTransform(const CMediaType* pmtIn,
  218. const CMediaType* pmtOut)
  219. {
  220. HRESULT hr = CheckInputType(pmtIn);
  221. if (FAILED(hr))
  222. return hr;
  223. DisplayType("pmtOut:", pmtOut);
  224. if (pmtOut->majortype != MEDIATYPE_Text) {
  225. DbgLog((LOG_ERROR, 1, TEXT("*** output type must be text")));
  226. return E_INVALIDARG;
  227. }
  228. return S_OK;
  229. }
  230. //*****************************************************************************
  231. //
  232. // DecideBufferSize()
  233. //
  234. HRESULT CVITCDecode::DecideBufferSize( IMemAllocator * pAllocator,
  235. ALLOCATOR_PROPERTIES *pProperties )
  236. {
  237. DbgLog((LOG_TRACE, 3, TEXT("CVITCDecode::DecideBufferSize")));
  238. if (pProperties->cBuffers < 4)
  239. pProperties->cBuffers = 4;
  240. if (pProperties->cbBuffer < TIMECODE_SIZE)
  241. pProperties->cbBuffer = TIMECODE_SIZE;
  242. if (pProperties->cbAlign < 1)
  243. pProperties->cbAlign = 1;
  244. ALLOCATOR_PROPERTIES Actual;
  245. HRESULT hr = pAllocator->SetProperties(pProperties,&Actual);
  246. if (FAILED(hr)) {
  247. return hr;
  248. }
  249. return S_OK;
  250. }
  251. //*****************************************************************************
  252. //
  253. // StartStreaming()
  254. //
  255. //
  256. HRESULT CVITCDecode::StartStreaming()
  257. {
  258. CAutoLock lock(&m_csFilter);
  259. DbgLog((LOG_TRACE, 3, TEXT("CVITCDecode::StartStreaming")));
  260. VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)m_pInput->CurrentMediaType().Format();
  261. m_nWidth = pvi->bmiHeader.biWidth * pvi->bmiHeader.biBitCount / 8;
  262. m_nHeight = pvi->bmiHeader.biHeight;
  263. m_nLine = -1;
  264. m_bTimecodeSeen = FALSE;
  265. m_lStartTime = timeGetTime();
  266. m_iFrames = m_iErrors = 0;
  267. return S_OK;
  268. }
  269. //*****************************************************************************
  270. //
  271. // StopStreaming()
  272. //
  273. //
  274. HRESULT CVITCDecode::StopStreaming()
  275. {
  276. CAutoLock lock(&m_csFilter);
  277. DbgLog((LOG_TRACE, 3, TEXT("CVITCDecode::StopStreaming")));
  278. DbgLog((LOG_TRACE, 1, TEXT("Total time: %d: %d frames, %d errors"),
  279. timeGetTime() - m_lStartTime, m_iFrames, m_iErrors));
  280. return NOERROR;
  281. }
  282. HRESULT CVITCDecode::Transform( IMediaSample *pIn, IMediaSample *pOut )
  283. {
  284. DbgLog((LOG_ERROR, 1, TEXT("*** CVITCDecode->Transform() called!")));
  285. ASSERT(0); // !!! shouldn't be called!
  286. return E_FAIL;
  287. }
  288. HRESULT CVITCDecode::Receive( IMediaSample *pInSample )
  289. {
  290. HRESULT hr = NOERROR;
  291. REFERENCE_TIME StartTime; // start/stop time of input buffer
  292. REFERENCE_TIME StopTime;
  293. BYTE *pbSample;
  294. DbgLog((LOG_TRACE, 4, TEXT("CVITCDecode::Receive")));
  295. // get input start and stop times
  296. pInSample->GetTime(&StartTime, &StopTime);
  297. pInSample->GetPointer( &pbSample );
  298. // !!! if we haven't decided what line the timecode is on, search for it.
  299. if (m_nLine == -1) {
  300. for (int iLine = 0; iLine < (int) m_nHeight; iLine++) {
  301. if (decoder.decodeBuffer(pbSample + iLine * m_nWidth, m_nWidth)) {
  302. m_nLine = iLine;
  303. DbgLog((LOG_TRACE, 1, TEXT("Found VITC on line %d"), m_nLine));
  304. break;
  305. }
  306. }
  307. }
  308. // !!! should we output something like "error" if no VITC?
  309. BOOL bHaveTimecode = (m_nLine >= 0 &&
  310. decoder.decodeBuffer(pbSample + m_nLine * m_nWidth, m_nWidth));
  311. if (bHaveTimecode) {
  312. m_bTimecodeSeen = TRUE;
  313. } else {
  314. // potentially search for new line if enough errors?
  315. ++m_iErrors;
  316. }
  317. ++m_iFrames;
  318. // !!! perhaps if we get enough errors in a row we should re-start the
  319. // search for the right line?
  320. if (TRUE /* bHaveTimecode */) {
  321. IMediaSample *pOutSample;
  322. TimeCode tc;
  323. decoder.getTimeCode(&tc);
  324. hr = m_pOutput->GetDeliveryBuffer( &pOutSample, NULL, NULL, 0 );
  325. if( FAILED(hr) )
  326. {
  327. DbgLog((LOG_ERROR, 1, TEXT("GetDeliveryBuffer(pOutSample) failed, hr = %u"),hr));
  328. return hr;
  329. }
  330. pOutSample->SetSyncPoint( TRUE );
  331. // not really right....
  332. pOutSample->SetDiscontinuity( pInSample->IsDiscontinuity() == S_OK );
  333. pOutSample->SetTime(&StartTime, &StopTime);
  334. BYTE *pbOut;
  335. pOutSample->GetPointer( &pbOut );
  336. *pbOut = '\0';
  337. if (m_bTimecodeSeen) {
  338. tc.GetString((char *) pbOut);
  339. }
  340. if (!bHaveTimecode) {
  341. lstrcat((char *) pbOut, " <error>");
  342. }
  343. pOutSample->SetActualDataLength(lstrlen((char *) pbOut) + 1);
  344. hr = m_pOutput->Deliver(pOutSample);
  345. // release the output buffer. If the connected pin still needs it,
  346. // it will have addrefed it itself.
  347. pOutSample->Release();
  348. if (FAILED(hr))
  349. return hr;
  350. }
  351. return S_OK;
  352. }