Leaked source code of windows server 2003
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.

2626 lines
87 KiB

  1. //------------------------------------------------------------------------------
  2. // File: DMOBase.h
  3. //
  4. // Desc: A collection of DMO base classes.
  5. //
  6. // Copyright (c) 1999-2001 Microsoft Corporation. All rights reserved.
  7. //------------------------------------------------------------------------------
  8. // Current hierarchy:
  9. //
  10. // IMediaObject
  11. // |
  12. // +-- C1in1outDMO - generic base class for DMOs with 1 in and 1 out
  13. // | |
  14. // | +-- FBRDMO - base class for fixed sample size, fixed bitrate DMOs
  15. // | | |
  16. // | | +-- CPCMDMO - base class for PCM audio DMOs
  17. // | |
  18. // | +-- C1for1DMO - base class for single sample per buffer 1-in/1-out DMOs
  19. // | |
  20. // | +-- C1for1QCDMO - adds IDMOQualityControl to C1for1DMO
  21. // |
  22. // +-- CGenericDMO - resonably generic base class for multi-input/output DMOs
  23. //
  24. #ifndef __DMOBASE_H_
  25. #define __DMOBASE_H_
  26. #include "dmo.h"
  27. #include "assert.h"
  28. #include "math.h"
  29. //
  30. // locking helper class
  31. //
  32. #ifdef DMO_NOATL
  33. class CDMOAutoLock {
  34. public:
  35. CDMOAutoLock(CRITICAL_SECTION* pcs)
  36. : m_pcs(pcs)
  37. {
  38. EnterCriticalSection(m_pcs);
  39. }
  40. ~CDMOAutoLock() {
  41. LeaveCriticalSection(m_pcs);
  42. }
  43. private:
  44. CRITICAL_SECTION* m_pcs;
  45. };
  46. #else
  47. class CDMOAutoLock {
  48. public:
  49. CDMOAutoLock(CComAutoCriticalSection* pcs)
  50. : m_pcs(pcs)
  51. {
  52. m_pcs->Lock();
  53. }
  54. ~CDMOAutoLock() {
  55. m_pcs->Unlock();
  56. }
  57. private:
  58. CComAutoCriticalSection* m_pcs;
  59. };
  60. #endif
  61. //
  62. // C1in1outDMO - generic base class for 1-input/1-output DMOs.
  63. //
  64. //
  65. //
  66. // C1in1outDMO implements all IMediaObject methods. The derived class
  67. // customizes the DMO's behavior by overriding some or all of the following
  68. // virtual functions:
  69. //
  70. // Main Streaming:
  71. // AcceptInput // accept one new input buffer
  72. // ProduceOutput // fill up one output buffer with new data
  73. // AcceptingInput // check if DMO is ready for new input
  74. // Other streaming:
  75. // PrepareForStreaming // hook called after both types have been set
  76. // Discontinuity // notify DMO of a discontinuity
  77. // DoFlush // discard all data and start anew
  78. // Mediatype negotiation:
  79. // GetInputType // input type enumerator
  80. // GetOutputType // output type enumerator
  81. // CheckInputType // verifies proposed input type is acceptable
  82. // CheckOutputType // verifies proposed output type is acceptable
  83. // Buffer size negotiation:
  84. // GetInputFlags // input data flow flags
  85. // GetOutputFlags // output fata flow flags
  86. // GetInputSizeInfo // input buffer size requirements
  87. // GetOutputSizeInfo // output buffer size requirements
  88. //
  89. // This base class assumes that the derived class will not override any
  90. // IMediaObject methods directly - the derived class should override the
  91. // methods listed above instead.
  92. //
  93. //
  94. //
  95. // The base class provides a default implementation for each of the
  96. // overridables listed above. However, to make a useful DMO the derived class
  97. // probably needs to override at least the following two methods:
  98. //
  99. // HRESULT AcceptingInput();
  100. // HRESULT AcceptInput(BYTE* pData,
  101. // ULONG ulSize,
  102. // DWORD dwFlags,
  103. // REFERENCE_TIME rtTimestamp,
  104. // REFERENCE_TIME rtTimelength,
  105. // IMediaBuffer* pMediaBuffer);
  106. // HRESULT ProduceOutput(BYTE *pData,
  107. // ULONG ulAvail,
  108. // ULONG* pulUsed,
  109. // DWORD* pdwStatus,
  110. // REFERENCE_TIME *prtTimestamp,
  111. // REFERENCE_TIME *prtTimelength);
  112. //
  113. // All good DMOs should also override these (the default implementation
  114. // simply accepts any mediatype, which in general is not good DMO behavior):
  115. //
  116. // HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  117. // HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  118. // HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt);
  119. // HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt);
  120. //
  121. // DMOs that store data and/or state information may need to implement
  122. //
  123. // HRESULT PrepareForStreaming();
  124. // HRESULT Discontinuity();
  125. // HRESULT Flush();
  126. //
  127. // Finally, DMOs that make any buffer size assumptions will need to override
  128. // these:
  129. //
  130. // HRESULT GetInputFlags(DWORD* pdwFlags);
  131. // HRESULT GetOutputFlags(DWORD* pdwFlags);
  132. // HRESULT GetInputSizeInfo(ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment);
  133. // HRESULT GetOutputSizeInfo(ULONG *pulSize, ULONG *pulAlignment);
  134. //
  135. //
  136. //
  137. // The following functions are provided by this base class exclusively for use
  138. // by the derived class. The derived class should call these to find out the
  139. // currently set mediatype(s) whenever it needs to make a decision that
  140. // depends on the mediatype used. Each of these returns NULL if the mediatype
  141. // has not been set yet.
  142. //
  143. // DMO_MEDIA_TYPE *InputType();
  144. // DMO_MEDIA_TYPE *OutputType().
  145. //
  146. #define PROLOGUE \
  147. CDMOAutoLock l(&m_cs); \
  148. if (ulStreamIndex >= 1) \
  149. return DMO_E_INVALIDSTREAMINDEX
  150. class C1in1outDMO : public IMediaObject
  151. {
  152. public:
  153. C1in1outDMO() :
  154. m_bInputTypeSet(FALSE),
  155. m_bOutputTypeSet(FALSE),
  156. m_bIncomplete(FALSE)
  157. {
  158. #ifdef DMO_NOATL
  159. InitializeCriticalSection(&m_cs);
  160. #endif
  161. }
  162. ~C1in1outDMO() {
  163. FreeInputType();
  164. FreeOutputType();
  165. #ifdef DMO_NOATL
  166. DeleteCriticalSection(&m_cs);
  167. #endif
  168. }
  169. public:
  170. //
  171. // IMediaObject methods
  172. //
  173. STDMETHODIMP GetStreamCount(unsigned long *pulNumberOfInputStreams, unsigned long *pulNumberOfOutputStreams)
  174. {
  175. CDMOAutoLock l(&m_cs);
  176. if (pulNumberOfInputStreams == NULL ||
  177. pulNumberOfOutputStreams == NULL) {
  178. return E_POINTER;
  179. }
  180. *pulNumberOfInputStreams = 1;
  181. *pulNumberOfOutputStreams = 1;
  182. return S_OK;
  183. }
  184. STDMETHODIMP GetInputStreamInfo(ULONG ulStreamIndex, DWORD *pdwFlags)
  185. {
  186. if( NULL == pdwFlags ) {
  187. return E_POINTER;
  188. }
  189. PROLOGUE;
  190. return GetInputFlags(pdwFlags);
  191. }
  192. STDMETHODIMP GetOutputStreamInfo(ULONG ulStreamIndex, DWORD *pdwFlags)
  193. {
  194. if( NULL == pdwFlags ) {
  195. return E_POINTER;
  196. }
  197. PROLOGUE;
  198. return GetOutputFlags(pdwFlags);
  199. }
  200. STDMETHODIMP GetInputType(ULONG ulStreamIndex, ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  201. PROLOGUE;
  202. return GetInputType(ulTypeIndex, pmt);
  203. }
  204. STDMETHODIMP GetOutputType(ULONG ulStreamIndex, ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  205. PROLOGUE;
  206. return GetOutputType(ulTypeIndex, pmt);
  207. }
  208. STDMETHODIMP GetInputCurrentType(ULONG ulStreamIndex, DMO_MEDIA_TYPE *pmt) {
  209. PROLOGUE;
  210. if (m_bInputTypeSet)
  211. return MoCopyMediaType(pmt, &m_InputType);
  212. else
  213. return DMO_E_TYPE_NOT_SET;
  214. }
  215. STDMETHODIMP GetOutputCurrentType(ULONG ulStreamIndex, DMO_MEDIA_TYPE *pmt) {
  216. PROLOGUE;
  217. if (m_bOutputTypeSet)
  218. return MoCopyMediaType(pmt, &m_OutputType);
  219. else
  220. return DMO_E_TYPE_NOT_SET;
  221. }
  222. STDMETHODIMP GetInputSizeInfo(ULONG ulStreamIndex, ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  223. if( (NULL == pulSize) || (NULL == pcbMaxLookahead) || (NULL == pulAlignment) ) {
  224. return E_POINTER;
  225. }
  226. PROLOGUE;
  227. if (!m_bInputTypeSet)
  228. return DMO_E_TYPE_NOT_SET;
  229. return GetInputSizeInfo(pulSize, pcbMaxLookahead, pulAlignment);
  230. }
  231. STDMETHODIMP GetOutputSizeInfo(ULONG ulStreamIndex, ULONG *pulSize, ULONG *pulAlignment) {
  232. if( (NULL == pulSize) || (NULL == pulAlignment) ) {
  233. return E_POINTER;
  234. }
  235. PROLOGUE;
  236. if (!m_bOutputTypeSet)
  237. return DMO_E_TYPE_NOT_SET;
  238. return GetOutputSizeInfo(pulSize, pulAlignment);
  239. }
  240. STDMETHODIMP SetInputType(ULONG ulStreamIndex, const DMO_MEDIA_TYPE *pmt, DWORD dwFlags) {
  241. PROLOGUE;
  242. HRESULT hr = ValidateSetTypeParameters(pmt, dwFlags);
  243. if (FAILED(hr)) {
  244. return hr;
  245. }
  246. if (DMO_SET_TYPEF_CLEAR & dwFlags) {
  247. FreeInputType();
  248. return NOERROR;
  249. } else {
  250. hr = CheckInputType(pmt);
  251. if (FAILED(hr))
  252. return hr;
  253. if (dwFlags & DMO_SET_TYPEF_TEST_ONLY)
  254. return NOERROR;
  255. hr = AtomicCopyMediaType(pmt, &m_InputType, m_bInputTypeSet);
  256. if (FAILED(hr)) {
  257. return hr;
  258. }
  259. m_bInputTypeSet = TRUE;
  260. if (m_bOutputTypeSet) {
  261. hr = PrepareForStreaming();
  262. if (FAILED(hr)) {
  263. FreeInputType();
  264. return hr;
  265. }
  266. }
  267. return NOERROR;
  268. }
  269. }
  270. STDMETHODIMP SetOutputType(ULONG ulStreamIndex, const DMO_MEDIA_TYPE *pmt, DWORD dwFlags) {
  271. PROLOGUE;
  272. HRESULT hr = ValidateSetTypeParameters(pmt, dwFlags);
  273. if (FAILED(hr)) {
  274. return hr;
  275. }
  276. if (DMO_SET_TYPEF_CLEAR & dwFlags) {
  277. FreeOutputType();
  278. return NOERROR;
  279. } else {
  280. hr = CheckOutputType(pmt);
  281. if (FAILED(hr))
  282. return hr;
  283. if (dwFlags & DMO_SET_TYPEF_TEST_ONLY)
  284. return NOERROR;
  285. hr = AtomicCopyMediaType(pmt, &m_OutputType, m_bOutputTypeSet);
  286. if (FAILED(hr)) {
  287. return hr;
  288. }
  289. m_bOutputTypeSet = TRUE;
  290. if (m_bInputTypeSet) {
  291. hr = PrepareForStreaming();
  292. if (FAILED(hr)) {
  293. FreeOutputType();
  294. return hr;
  295. }
  296. }
  297. return NOERROR;
  298. }
  299. }
  300. STDMETHODIMP GetInputStatus(
  301. ULONG ulStreamIndex,
  302. DWORD *pdwStatus
  303. ) {
  304. if( NULL == pdwStatus ) {
  305. return E_POINTER;
  306. }
  307. PROLOGUE;
  308. *pdwStatus = 0;
  309. if (AcceptingInput() == S_OK)
  310. *pdwStatus |= DMO_INPUT_STATUSF_ACCEPT_DATA;
  311. return NOERROR;
  312. }
  313. STDMETHODIMP GetInputMaxLatency(unsigned long ulStreamIndex, REFERENCE_TIME *prtLatency) {
  314. return E_NOTIMPL;
  315. }
  316. STDMETHODIMP SetInputMaxLatency(unsigned long ulStreamIndex, REFERENCE_TIME rtLatency) {
  317. return E_NOTIMPL;
  318. }
  319. STDMETHODIMP Discontinuity(ULONG ulStreamIndex) {
  320. PROLOGUE;
  321. return Discontinuity();
  322. }
  323. STDMETHODIMP Flush()
  324. {
  325. CDMOAutoLock l(&m_cs);
  326. DoFlush();
  327. return NOERROR;
  328. }
  329. STDMETHODIMP AllocateStreamingResources() {return S_OK;}
  330. STDMETHODIMP FreeStreamingResources() {return S_OK;}
  331. //
  332. // Processing methods - public entry points
  333. //
  334. STDMETHODIMP ProcessInput(
  335. DWORD ulStreamIndex,
  336. IMediaBuffer *pBuffer, // [in], must not be NULL
  337. DWORD dwFlags, // [in] - discontinuity, timestamp, etc.
  338. REFERENCE_TIME rtTimestamp, // [in], valid if flag set
  339. REFERENCE_TIME rtTimelength // [in], valid if flag set
  340. ) {
  341. PROLOGUE;
  342. if (!TypesSet()) {
  343. return DMO_E_TYPE_NOT_SET;
  344. }
  345. if (AcceptingInput() != S_OK)
  346. return DMO_E_NOTACCEPTING;
  347. if (!pBuffer)
  348. return E_POINTER;
  349. // deal with the IMediaBuffer so the derived class doesn't have to
  350. BYTE *pData;
  351. ULONG ulSize;
  352. HRESULT hr = pBuffer->GetBufferAndLength(&pData, &ulSize);
  353. if (FAILED(hr))
  354. return hr;
  355. if (pData == NULL)
  356. ulSize = 0;
  357. m_bIncomplete = TRUE; // new input means we may be able to produce output
  358. return AcceptInput(pData, ulSize, dwFlags, rtTimestamp, rtTimelength, pBuffer);
  359. }
  360. STDMETHODIMP ProcessOutput(
  361. DWORD dwReserved,
  362. DWORD ulOutputBufferCount,
  363. DMO_OUTPUT_DATA_BUFFER *pOutputBuffers,
  364. DWORD *pdwStatus)
  365. {
  366. HRESULT hr;
  367. CDMOAutoLock l(&m_cs);
  368. if (pdwStatus == NULL) {
  369. return E_POINTER;
  370. }
  371. *pdwStatus = 0;
  372. if (ulOutputBufferCount != 1)
  373. return E_INVALIDARG;
  374. if (!TypesSet()) {
  375. return DMO_E_TYPE_NOT_SET;
  376. }
  377. pOutputBuffers[0].dwStatus = 0;
  378. // deal with the IMediaBuffer so the derived class doesn't have to
  379. BYTE *pOut;
  380. ULONG ulSize;
  381. ULONG ulAvail;
  382. if (pOutputBuffers[0].pBuffer) {
  383. hr = pOutputBuffers[0].pBuffer->GetBufferAndLength(&pOut, &ulSize);
  384. if (FAILED(hr)) return hr;
  385. hr = pOutputBuffers[0].pBuffer->GetMaxLength(&ulAvail);
  386. if (FAILED(hr)) return hr;
  387. if (ulSize) { // skip any already used portion of the buffer
  388. if (ulSize > ulAvail)
  389. return E_INVALIDARG;
  390. ulAvail -= ulSize;
  391. pOut += ulSize;
  392. }
  393. }
  394. else { // no IMediaBuffer
  395. //
  396. // If (a) the output stream says it can operate without buffers, AND
  397. // (b) the DISCARD flag was set in dwReserved,
  398. // then call ProduceOutput with a NULL output buffer pointer.
  399. //
  400. // Otherwise just return the INCOMPLETE flag without any processing.
  401. //
  402. DWORD dwFlags;
  403. if (SUCCEEDED(GetOutputFlags(&dwFlags)) &&
  404. ((dwFlags & DMO_OUTPUT_STREAMF_DISCARDABLE) ||
  405. (dwFlags & DMO_OUTPUT_STREAMF_OPTIONAL)
  406. ) &&
  407. (dwReserved & DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER))
  408. { // process, but discard the output
  409. pOut = NULL;
  410. ulAvail = 0;
  411. }
  412. else { // just report the incomplete status without altering our state
  413. if (m_bIncomplete)
  414. pOutputBuffers[0].dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
  415. return NOERROR;
  416. }
  417. }
  418. ULONG ulProduced = 0;
  419. hr = ProduceOutput(pOut,
  420. ulAvail,
  421. &ulProduced,
  422. &(pOutputBuffers[0].dwStatus),
  423. &(pOutputBuffers[0].rtTimestamp),
  424. &(pOutputBuffers[0].rtTimelength));
  425. if (FAILED(hr))
  426. return hr;
  427. HRESULT hrProcess = hr; // remember this in case it's S_FALSE
  428. // remember the DMO's incomplete status
  429. if (pOutputBuffers[0].dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE)
  430. m_bIncomplete = TRUE;
  431. else
  432. m_bIncomplete = FALSE;
  433. if (pOut) { // if using an output buffer, set the amount we used
  434. if (ulProduced > ulAvail)
  435. return E_FAIL;
  436. hr = pOutputBuffers[0].pBuffer->SetLength(ulSize + ulProduced);
  437. if (FAILED(hr))
  438. return hr;
  439. }
  440. return hrProcess;
  441. }
  442. #ifdef FIX_LOCK_NAME
  443. STDMETHODIMP DMOLock(LONG lLock)
  444. #else
  445. STDMETHODIMP Lock(LONG lLock)
  446. #endif
  447. {
  448. if (lLock) {
  449. #ifdef DMO_NOATL
  450. EnterCriticalSection(&m_cs);
  451. #else
  452. m_cs.Lock();
  453. #endif
  454. }
  455. else
  456. {
  457. #ifdef DMO_NOATL
  458. LeaveCriticalSection(&m_cs);
  459. #else
  460. m_cs.Unlock();
  461. #endif
  462. }
  463. return S_OK;
  464. }
  465. protected:
  466. HRESULT AtomicCopyMediaType(const DMO_MEDIA_TYPE *pmtSource, DMO_MEDIA_TYPE *pmtDestination, BOOL bDestinationInitialized) {
  467. // pmtDestination should always point to a valid DMO_MEDIA_TYPE structure.
  468. assert(NULL != pmtDestination);
  469. DMO_MEDIA_TYPE mtTempDestination;
  470. // actually set the type
  471. HRESULT hr = MoCopyMediaType(&mtTempDestination, pmtSource);
  472. if (FAILED(hr)) {
  473. return hr;
  474. }
  475. // Free any previous mediatype
  476. if (bDestinationInitialized) {
  477. MoFreeMediaType(pmtDestination);
  478. }
  479. *pmtDestination = mtTempDestination;
  480. return S_OK;
  481. }
  482. //
  483. // private methods for use by derived class
  484. //
  485. DMO_MEDIA_TYPE *InputType() {
  486. if (m_bInputTypeSet)
  487. return &m_InputType;
  488. else
  489. return NULL;
  490. }
  491. DMO_MEDIA_TYPE *OutputType() {
  492. if (m_bOutputTypeSet)
  493. return &m_OutputType;
  494. else
  495. return NULL;
  496. }
  497. protected:
  498. //
  499. // To be overriden by the derived class
  500. //
  501. virtual HRESULT GetInputFlags(DWORD* pdwFlags) {
  502. *pdwFlags = 0; // default implementation assumes no lookahead
  503. return NOERROR;
  504. }
  505. virtual HRESULT GetOutputFlags(DWORD* pdwFlags) {
  506. *pdwFlags = 0;
  507. return NOERROR;
  508. }
  509. virtual HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  510. return DMO_E_NO_MORE_ITEMS; // default implementation exposes no types
  511. }
  512. virtual HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  513. return DMO_E_NO_MORE_ITEMS; // default implementation exposes no types
  514. }
  515. virtual HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt) {
  516. if ((pmt == NULL) || ((pmt->cbFormat > 0) && (pmt->pbFormat == NULL)))
  517. return E_POINTER;
  518. return S_OK; // default implementation accepts anything
  519. }
  520. virtual HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt) {
  521. if ((pmt == NULL) || ((pmt->cbFormat > 0) && (pmt->pbFormat == NULL)))
  522. return E_POINTER;
  523. return S_OK; // default implementation accepts anything
  524. }
  525. virtual HRESULT GetInputSizeInfo(ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  526. *pulSize = 1; // default implementation imposes no size requirements
  527. *pcbMaxLookahead = 0; // default implementation assumes no lookahead
  528. *pulAlignment = 1; // default implementation assumes no alignment
  529. return NOERROR;
  530. }
  531. virtual HRESULT GetOutputSizeInfo(ULONG *pulSize, ULONG *pulAlignment) {
  532. *pulSize = 1; // default implementation imposes no size requirements
  533. *pulAlignment = 1; // default implementation assumes no alignment
  534. return NOERROR;
  535. }
  536. virtual HRESULT PrepareForStreaming() {
  537. return NOERROR;
  538. }
  539. virtual HRESULT AcceptingInput() {
  540. return S_FALSE;
  541. }
  542. virtual HRESULT Discontinuity() {
  543. return NOERROR;
  544. }
  545. virtual HRESULT DoFlush() {
  546. return NOERROR;
  547. }
  548. virtual HRESULT AcceptInput(BYTE* pData,
  549. ULONG ulSize,
  550. DWORD dwFlags,
  551. REFERENCE_TIME rtTimestamp,
  552. REFERENCE_TIME rtTimelength,
  553. IMediaBuffer* pMediaBuffer
  554. ) {
  555. m_bIncomplete = FALSE;
  556. return S_FALSE;
  557. }
  558. virtual HRESULT ProduceOutput(BYTE *pData,
  559. ULONG ulAvail,
  560. ULONG* pulUsed,
  561. DWORD* pdwStatus,
  562. REFERENCE_TIME *prtTimestamp,
  563. REFERENCE_TIME *prtTimelength
  564. ) {
  565. *pulUsed = 0;
  566. return S_FALSE;
  567. }
  568. HRESULT ValidateSetTypeParameters(const DMO_MEDIA_TYPE *pmt, DWORD dwFlags)
  569. {
  570. // Validate parameters.
  571. if (!(DMO_SET_TYPEF_CLEAR & dwFlags)) {
  572. // The DMO specification states that pmt CANNOT be NULL if
  573. // the DMO_SET_TYPEF_CLEAR flag is NOT set.
  574. if (NULL == pmt) {
  575. return E_POINTER;
  576. }
  577. }
  578. // The caller cannot set the DMO_SET_TYPEF_CLEAR flag and the
  579. // DMO_SET_TYPEF_TEST_ONLY flag. The DMO specification prohibits
  580. // this combination because the two flags are mutually exclusive.
  581. if ((DMO_SET_TYPEF_CLEAR & dwFlags) && (DMO_SET_TYPEF_TEST_ONLY & dwFlags)) {
  582. return E_INVALIDARG;
  583. }
  584. // Check for illegal flags.
  585. if (~(DMO_SET_TYPEF_CLEAR | DMO_SET_TYPEF_TEST_ONLY) & dwFlags) {
  586. return E_INVALIDARG;
  587. }
  588. return S_OK;
  589. }
  590. bool TypesSet() {
  591. return m_bInputTypeSet && m_bOutputTypeSet;
  592. }
  593. void FreeInputType() {
  594. if (m_bInputTypeSet) {
  595. MoFreeMediaType( &m_InputType );
  596. m_bInputTypeSet = FALSE;
  597. }
  598. }
  599. void FreeOutputType() {
  600. if (m_bOutputTypeSet) {
  601. MoFreeMediaType( &m_OutputType );
  602. m_bOutputTypeSet = FALSE;
  603. }
  604. }
  605. protected:
  606. // mediatype stuff
  607. BOOL m_bInputTypeSet;
  608. BOOL m_bOutputTypeSet;
  609. DMO_MEDIA_TYPE m_InputType;
  610. DMO_MEDIA_TYPE m_OutputType;
  611. BOOL m_bIncomplete;
  612. protected:
  613. #ifdef DMO_NOATL
  614. CRITICAL_SECTION m_cs;
  615. #else
  616. CComAutoCriticalSection m_cs;
  617. #endif
  618. };
  619. //
  620. // C1for1DMO - base class for 1-input/1-output DMOs which
  621. // - work on whole samples at a time, one sample per buffer
  622. // - produce exactly one output sample for every input sample
  623. // - don't need to accumulate more than 1 input sample before producing
  624. // - don't produce any additional stuff at the end
  625. // - the output sample corresponds in time to the input sample
  626. //
  627. // The derived class must implement:
  628. // HRESULT Process(BYTE* pIn,
  629. // ULONG ulBytesIn,
  630. // BYTE* pOut,
  631. // ULONG* pulProduced);
  632. // HRESULT GetSampleSizes(ULONG* pulMaxInputSampleSize,
  633. // ULONG* pulMaxOutputSampleSize);
  634. //
  635. //
  636. // The derived class should implement:
  637. // HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  638. // HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  639. // HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt);
  640. // HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt);
  641. //
  642. // The derived class may implement if it needs to:
  643. // HRESULT Init();
  644. //
  645. // The following methods are implemented by the base class. The derived class
  646. // should call these to find out if the input/output type has been set and if
  647. // so what it was set to.
  648. // DMO_MEDIA_TYPE *InputType();
  649. // DMO_MEDIA_TYPE *OutputType().
  650. //
  651. class C1for1DMO : public C1in1outDMO
  652. {
  653. public:
  654. C1for1DMO() :
  655. m_pBuffer(NULL)
  656. {
  657. }
  658. ~C1for1DMO() {
  659. if (m_pBuffer)
  660. m_pBuffer->Release();
  661. }
  662. protected:
  663. //
  664. // Implement C1in1outDMO overridables
  665. //
  666. virtual HRESULT GetInputFlags(DWORD* pdwFlags) {
  667. *pdwFlags = DMO_INPUT_STREAMF_WHOLE_SAMPLES |
  668. DMO_INPUT_STREAMF_SINGLE_SAMPLE_PER_BUFFER;
  669. return NOERROR;
  670. }
  671. virtual HRESULT GetOutputFlags(DWORD* pdwFlags) {
  672. *pdwFlags = DMO_OUTPUT_STREAMF_WHOLE_SAMPLES |
  673. DMO_OUTPUT_STREAMF_SINGLE_SAMPLE_PER_BUFFER;
  674. return NOERROR;
  675. }
  676. HRESULT GetInputSizeInfo(ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  677. HRESULT hr = GetSampleSizes(&m_ulMaxInputSize, &m_ulMaxOutputSize);
  678. if (FAILED(hr))
  679. return hr;
  680. *pulSize = m_ulMaxInputSize;
  681. *pcbMaxLookahead = 0;
  682. *pulAlignment = 1;
  683. return NOERROR;
  684. }
  685. HRESULT GetOutputSizeInfo(ULONG *pulSize, ULONG *pulAlignment) {
  686. HRESULT hr = GetSampleSizes(&m_ulMaxInputSize, &m_ulMaxOutputSize);
  687. if (FAILED(hr))
  688. return hr;
  689. *pulSize = m_ulMaxOutputSize;
  690. *pulAlignment = 1;
  691. return NOERROR;
  692. }
  693. HRESULT PrepareForStreaming() {
  694. HRESULT hr = GetSampleSizes(&m_ulMaxInputSize, &m_ulMaxOutputSize);
  695. if (FAILED(hr))
  696. return hr;
  697. return Init();
  698. }
  699. HRESULT AcceptingInput() {
  700. return m_pBuffer ? S_FALSE : S_OK; // accept unless holding one already
  701. }
  702. HRESULT AcceptInput(BYTE* pData,
  703. ULONG ulSize,
  704. DWORD dwFlags,
  705. REFERENCE_TIME rtTimestamp,
  706. REFERENCE_TIME rtTimelength,
  707. IMediaBuffer* pMediaBuffer
  708. ) {
  709. if (AcceptingInput() != S_OK)
  710. return E_FAIL;
  711. m_pData = pData;
  712. m_ulSize = ulSize;
  713. m_dwFlags = dwFlags;
  714. m_rtTimestamp = rtTimestamp;
  715. m_rtTimelength = rtTimelength;
  716. m_pBuffer = pMediaBuffer;
  717. pMediaBuffer->AddRef();
  718. return NOERROR;
  719. }
  720. HRESULT DoFlush() {
  721. Discontinuity();
  722. if (m_pBuffer) {
  723. m_pBuffer->Release();
  724. m_pBuffer = NULL;
  725. }
  726. return NOERROR;
  727. }
  728. HRESULT ProduceOutput(BYTE *pOut,
  729. ULONG ulAvail,
  730. ULONG* pulUsed,
  731. DWORD* pdwStatus,
  732. REFERENCE_TIME *prtTimestamp,
  733. REFERENCE_TIME *prtTimelength
  734. ) {
  735. *pulUsed = 0;
  736. *pdwStatus = 0;
  737. if (!m_pBuffer)
  738. return S_FALSE;
  739. if (pOut) {
  740. if (ulAvail < m_ulMaxOutputSize)
  741. return E_INVALIDARG;
  742. }
  743. HRESULT hr = Process(m_pData, m_ulSize, pOut, pulUsed);
  744. m_pBuffer->Release();
  745. m_pBuffer = NULL;
  746. if (FAILED(hr))
  747. return hr;
  748. if (*pulUsed == 0)
  749. return S_FALSE;
  750. if (m_dwFlags & DMO_INPUT_DATA_BUFFERF_SYNCPOINT)
  751. *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT;
  752. if (m_dwFlags & DMO_INPUT_DATA_BUFFERF_TIME)
  753. *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME;
  754. if (m_dwFlags & DMO_INPUT_DATA_BUFFERF_TIMELENGTH)
  755. *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH;
  756. *prtTimestamp = m_rtTimestamp;
  757. *prtTimelength = m_rtTimelength;
  758. return hr;
  759. }
  760. protected:
  761. //
  762. // To be implemented by derived class
  763. //
  764. virtual HRESULT Process(BYTE* pIn,
  765. ULONG ulBytesIn,
  766. BYTE* pOut,
  767. ULONG* pulProduced) = 0;
  768. virtual HRESULT GetSampleSizes(ULONG* pulMaxInputSampleSize,
  769. ULONG* pulMaxOutputSampleSize) = 0;
  770. virtual HRESULT Init() {
  771. return NOERROR;
  772. }
  773. IMediaBuffer* m_pBuffer;
  774. BYTE* m_pData;
  775. ULONG m_ulSize;
  776. DWORD m_dwFlags;
  777. REFERENCE_TIME m_rtTimestamp;
  778. REFERENCE_TIME m_rtTimelength;
  779. ULONG m_ulMaxOutputSize;
  780. ULONG m_ulMaxInputSize;
  781. };
  782. //
  783. // C1for1QCDMO - adds an IDMOQualityControl implementation to C1for1DMO. Just like
  784. // C1for1DMO, this base class assumes that the DMO produces exactly one output sample
  785. // for each input sample, etc. etc.
  786. //
  787. // A class that derives from C1for1QCDMO has access to / ability to override all
  788. // the same methods as with C1for1DMO, except
  789. // (1) A class derived from C1for1QCDMO should override QCProcess instead of
  790. // Process because C1for1QCDMO::Process implements some code required for
  791. // quality control. QCProcess has the same prototype as C1for1DMO::Process.
  792. // (2) If a class derived from C1for1QCDMO overrides Init(), it should at some
  793. // point call C1for1QCDMO::Init() to make sure C1for1QCDMO's quality control
  794. // data members are properly initialized.
  795. //
  796. class C1for1QCDMO : public C1for1DMO, public IDMOQualityControl {
  797. public:
  798. //
  799. // IDMOQualityControl
  800. //
  801. STDMETHODIMP SetNow(REFERENCE_TIME rtNow) {
  802. // Remember SetNow values even if quality control is not currently enabled
  803. DWORD dwTicks = GetTickCount();
  804. CDMOAutoLock l(&m_cs);
  805. m_rtNow = rtNow;
  806. m_dwNow = dwTicks;
  807. return NOERROR;
  808. }
  809. STDMETHODIMP SetStatus(DWORD dwFlags) {
  810. // Any point in grabbing the object lock here ?
  811. if (dwFlags & DMO_QUALITY_STATUS_ENABLED)
  812. m_bQualityControlEnabled = TRUE;
  813. else
  814. m_bQualityControlEnabled = FALSE;
  815. return NOERROR;
  816. }
  817. STDMETHODIMP GetStatus(DWORD *pdwFlags) {
  818. // Any point in grabbing the object lock here ?
  819. if (m_bQualityControlEnabled)
  820. *pdwFlags = DMO_QUALITY_STATUS_ENABLED;
  821. else
  822. *pdwFlags = 0;
  823. return NOERROR;
  824. }
  825. protected:
  826. HRESULT Init() {
  827. m_bQualityControlEnabled = FALSE;
  828. m_rtProcess = 100000; // 10 ms - initial guess at processing time
  829. return NOERROR;
  830. }
  831. // Override Process to add quality control
  832. HRESULT Process(BYTE* pIn,ULONG ulBytesIn,BYTE* pOut,ULONG* pulProduced) {
  833. // Skip the sample if it is likely to be late.
  834. if (m_bQualityControlEnabled &&
  835. (m_dwFlags & DMO_INPUT_DATA_BUFFERF_TIME) && // timestamp present
  836. (m_rtNow + (GetTickCount() - m_dwNow) * 10000 + m_rtProcess > m_rtTimestamp + 0000000)) {
  837. *pulProduced = 0;
  838. return S_FALSE;
  839. }
  840. DWORD dwBefore = GetTickCount();
  841. HRESULT hr = QCProcess(m_pData, m_ulSize, pOut, pulProduced);
  842. DWORD dwAfter = GetTickCount();
  843. // Make the new m_rtProcess a weighted average of the old m_rtProcess
  844. // and the value we just got. 0.8 and 0.2 give a time constant of about 4,
  845. // and it takes about 10 iterations to reach 90% - seems reasonable, but
  846. // I don't know what the optimal value is.
  847. m_rtProcess = (REFERENCE_TIME)(0.8 * m_rtProcess + 0.2 * (((REFERENCE_TIME)(dwAfter - dwBefore)) * 10000));
  848. return hr;
  849. }
  850. // To be implemented by derived class
  851. virtual HRESULT QCProcess(BYTE* pIn,
  852. ULONG ulBytesIn,
  853. BYTE* pOut,
  854. ULONG* pulProduced) = 0;
  855. private:
  856. // variables used by quality control code
  857. BOOL m_bQualityControlEnabled;
  858. REFERENCE_TIME m_rtNow;
  859. DWORD m_dwNow;
  860. REFERENCE_TIME m_rtProcess; // average processing delay
  861. };
  862. //
  863. // CFBRDMO - DMO base class for 'fixed bitrate' DMOs. More specifically,
  864. // this base class assumes the following:
  865. // - 1 input, 1 output;
  866. // - both input and output consist of equally sized 'quanta';
  867. // - input/output quantum sizes can be determined from mediatypes;
  868. // - each output quantum can be generated independently (without looking at
  869. // previous output quanta);
  870. // - if multiple input quanta are needed to generate a particular output
  871. // quantum ('window overhead'), then the range of input required has an upper
  872. // bound derived from mediatypes on both sides (i.e., both 'lookahead'
  873. // and 'input memory' are bounded).
  874. //
  875. // The derived class must implement the following virtual functions:
  876. // HRESULT FBRProcess(DWORD cQuanta, BYTE *pIn, BYTE *pOut);
  877. // HRESULT GetStreamingParams(
  878. // DWORD *pdwInputQuantumSize, // in bytes
  879. // DWORD *pdwOutputQuantumSize, // in bytes
  880. // DWORD *pdwMaxLookahead, // in input quanta, 0 means no lookahead
  881. // DWORD *pdwLookBehind,
  882. // REFERENCE_TIME *prtQuantumDuration, // same for input and output quanta
  883. // REFERENCE_TIME *prtDurationDenominator // optional, normally 1
  884. // );
  885. // The derived class should also implement the following:
  886. // HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  887. // HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  888. // HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt);
  889. // HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt);
  890. // The derived class may need to implement the followng:
  891. // HRESULT Init();
  892. // HRESULT Discontinuity();
  893. //
  894. // The derived class may use these entry points into the base class to get
  895. // the currently set mediatypes:
  896. // DMO_MEDIA_TYPE *InputType();
  897. // DMO_MEDIA_TYPE *OutputType().
  898. //
  899. // The sum of *pdwMaxLookahead and *pdwLoookbehind is the 'window overhead' of
  900. // the algorithm (the window overhead is 0 if the algorithm only needs the
  901. // current input sample).
  902. //
  903. // Because the non-zero window overhead case is more complicated, it is handled by a
  904. // separate set of functions in this base class. The names of all non-zero
  905. // window overhead functions have the 'NZWO' prefix. The names of the
  906. // zero window overhead functions begin with 'ZWO'.
  907. //
  908. // A data copy on the input side is necessary in the non-zero window overhead case.
  909. //
  910. class CFBRDMO : public C1in1outDMO
  911. {
  912. public:
  913. CFBRDMO() :
  914. m_bParametersSet(FALSE),
  915. m_pMediaBuffer(NULL),
  916. m_pAllocAddr(NULL),
  917. m_bStreaming(FALSE)
  918. {
  919. }
  920. ~CFBRDMO() {
  921. /*
  922. if (m_bStreaming)
  923. StopStreaming();
  924. */
  925. if (m_pAllocAddr)
  926. delete[] m_pAllocAddr;
  927. if (m_pMediaBuffer)
  928. m_pMediaBuffer->Release();
  929. }
  930. protected:
  931. //
  932. // Implement C1in1outDMO overridables
  933. //
  934. HRESULT GetInputSizeInfo(ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  935. if (!(InputType() && OutputType()))
  936. return DMO_E_TYPE_NOT_SET;
  937. //
  938. // For efficiency reasons we might like to be fed fairly large amounts
  939. // of data at a time, but technically all we need is one quantum.
  940. //
  941. *pulSize = m_ulInputQuantumSize;
  942. *pcbMaxLookahead = 0; // this base class does not rely on HOLDS_BUFFERS
  943. *pulAlignment = 1;
  944. return NOERROR;
  945. }
  946. HRESULT GetOutputSizeInfo(ULONG *pulSize, ULONG *pulAlignment) {
  947. if (!(InputType() && OutputType()))
  948. return DMO_E_TYPE_NOT_SET;
  949. *pulSize = m_ulOutputQuantumSize;
  950. *pulAlignment = 1;
  951. return NOERROR;
  952. }
  953. virtual HRESULT Discontinuity() {
  954. m_bDiscontinuity = TRUE;
  955. return NOERROR;
  956. }
  957. virtual HRESULT AcceptInput(BYTE* pData,
  958. ULONG ulSize,
  959. DWORD dwFlags,
  960. REFERENCE_TIME rtTimestamp,
  961. REFERENCE_TIME rtTimelength,
  962. IMediaBuffer* pBuffer
  963. ) {
  964. BOOL bTimestamp = (dwFlags & DMO_INPUT_DATA_BUFFERF_TIME) ? TRUE : FALSE;
  965. if (m_ulWindowOverhead)
  966. return NZWOProcessInput(pBuffer, pData, ulSize, bTimestamp, rtTimestamp);
  967. else
  968. return ZWOProcessInput(pBuffer, pData, ulSize, bTimestamp, rtTimestamp);
  969. }
  970. virtual HRESULT ProduceOutput(BYTE *pOut,
  971. ULONG ulAvail,
  972. ULONG* pulUsed,
  973. DWORD* pdwStatus,
  974. REFERENCE_TIME *prtTimestamp,
  975. REFERENCE_TIME *prtTimelength
  976. ) {
  977. HRESULT hr;
  978. if (!m_bParametersSet)
  979. return DMO_E_TYPE_NOT_SET;
  980. // call Discontinuity() if this is the first ProcessOutput() call
  981. if (!m_bStreaming) {
  982. HRESULT hr = Discontinuity();
  983. if (FAILED(hr))
  984. return hr;
  985. m_bStreaming = TRUE;
  986. }
  987. *pdwStatus = 0;
  988. ULONG ulInputQuantaAvailable = InputQuantaAvailable();
  989. if (!ulInputQuantaAvailable)
  990. return S_FALSE; // did not produce anything
  991. ULONG ulOutputQuantaPossible = ulAvail / m_ulOutputQuantumSize;
  992. if (!ulOutputQuantaPossible)
  993. return E_INVALIDARG;
  994. ULONG ulQuantaToProcess = min(ulOutputQuantaPossible, ulInputQuantaAvailable);
  995. assert(ulQuantaToProcess > 0);
  996. BOOL bTimestamp;
  997. if (m_ulWindowOverhead)
  998. hr = NZWOProcessOutput(pOut, ulQuantaToProcess, &bTimestamp, prtTimestamp);
  999. else
  1000. hr = ZWOProcessOutput(pOut, ulQuantaToProcess, &bTimestamp, prtTimestamp);
  1001. if (FAILED(hr))
  1002. return hr;
  1003. *pulUsed = ulQuantaToProcess * m_ulOutputQuantumSize;
  1004. *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT;
  1005. if (bTimestamp)
  1006. *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME;
  1007. // any data left ?
  1008. if (InputQuantaAvailable()) // yes - set incomplete
  1009. *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
  1010. else if (m_bDiscontinuity) // no - process any discontinuity
  1011. DoFlush();
  1012. return NOERROR;
  1013. }
  1014. HRESULT DoFlush()
  1015. {
  1016. Discontinuity();
  1017. // reset flags
  1018. m_bDiscontinuity = FALSE;
  1019. m_bTimestamps = FALSE;
  1020. if (m_ulWindowOverhead)
  1021. NZWODiscardData();
  1022. else
  1023. ZWODiscardData();
  1024. return NOERROR;
  1025. }
  1026. HRESULT AcceptingInput() {
  1027. if (!m_bParametersSet) // uninitialized
  1028. return S_FALSE;
  1029. BOOL bResult;
  1030. if (m_ulWindowOverhead)
  1031. bResult = NZWOQueryAccept();
  1032. else
  1033. bResult = ZWOQueryAccept();
  1034. return bResult ? S_OK : S_FALSE;
  1035. }
  1036. // End C1in1out overridables implementation
  1037. private:
  1038. //
  1039. // Common private code (window overhead or no window overhead)
  1040. //
  1041. // returns the number of input quanta available minus any window overhead
  1042. ULONG InputQuantaAvailable() {
  1043. if (m_ulWindowOverhead)
  1044. return NZWOAvail();
  1045. else
  1046. return ZWOAvail();
  1047. }
  1048. // Private method to compute/allocate stuff once all types have been set.
  1049. HRESULT PrepareForStreaming () {
  1050. m_bParametersSet = FALSE;
  1051. // Now that both types are set, query the derived class for params
  1052. HRESULT hr;
  1053. if (FAILED(hr = GetStreamingParams(&m_ulInputQuantumSize,
  1054. &m_ulOutputQuantumSize,
  1055. &m_ulLookahead,
  1056. &m_ulLookbehind,
  1057. &m_rtDurationNumerator,
  1058. &m_rtDenominator)))
  1059. return hr;
  1060. // m_ulOutputQuantumSize and m_ulInputQuantumSize should never be 0.
  1061. assert( (0 != m_ulInputQuantumSize) && (0 != m_ulOutputQuantumSize) );
  1062. if (!m_rtDenominator) {
  1063. assert(!"bad object - duration denominator should not be 0 !");
  1064. return E_FAIL;
  1065. }
  1066. // Attempt to reduce the fraction. Probably the most complicated number
  1067. // we will ever see is 44100 = (3 * 7 * 2 * 5) ^ 2, so trying the first
  1068. // few numbers should suffice in most cases.
  1069. DWORD dwP[] = {2,3,5,7,11,13,17,19,23,29,31};
  1070. for (DWORD c = 0; c < sizeof(dwP) / sizeof(DWORD); c++) {
  1071. while ((m_rtDurationNumerator % dwP[c] == 0) &&
  1072. (m_rtDenominator % dwP[c] == 0)) {
  1073. m_rtDurationNumerator /= dwP[c];
  1074. m_rtDenominator /= dwP[c];
  1075. }
  1076. }
  1077. // We cannot afford to have huge denominators, unfortunately, because
  1078. // we store timestamp numerators using 64 bits, so a large denominator
  1079. // could result in timestamp overflows. So if the denominator is still
  1080. // too large, reduce it anyway with loss of precision.
  1081. ULONG ulMax = 0x10000; // largest acceptable denominator value
  1082. if (m_rtDenominator >= ulMax) {
  1083. double actual_ratio = (double)m_rtDurationNumerator * (double)m_rtDenominator;
  1084. ULONG ulDenominator = 1;
  1085. // Repeatedly increase the denominator until either the actual ratio
  1086. // can be represented precisely using the denominator, or the
  1087. // denominator gets too large.
  1088. do {
  1089. double fractional_part = actual_ratio * (double)ulDenominator
  1090. - floor(actual_ratio * (double)ulDenominator);
  1091. if (fractional_part == 0)
  1092. break;
  1093. ULONG ulNewDenominator = (ULONG)floor(ulDenominator / fractional_part);
  1094. if (ulNewDenominator >= ulMax)
  1095. break;
  1096. ulDenominator = ulNewDenominator;
  1097. } while(1);
  1098. m_rtDurationNumerator = (ULONG)floor(actual_ratio * ulDenominator);
  1099. m_rtDenominator = ulDenominator;
  1100. }
  1101. m_ulWindowOverhead = m_ulLookahead + m_ulLookbehind;
  1102. if (!m_ulWindowOverhead) // No window overhead - the simple case
  1103. m_bParametersSet = TRUE;
  1104. else // The complicated case with window overhead
  1105. AllocateCircularBuffer();
  1106. m_bTimestamps = FALSE;
  1107. m_bDiscontinuity = FALSE;
  1108. if (m_bStreaming) {
  1109. //StopStreaming();
  1110. m_bStreaming = FALSE;
  1111. }
  1112. hr = Init();
  1113. if( FAILED( hr ) ) {
  1114. m_bParametersSet = FALSE;
  1115. return hr;
  1116. }
  1117. return m_bParametersSet ? NOERROR : E_FAIL;
  1118. }
  1119. // end common code
  1120. //
  1121. // zero window overhead case code
  1122. //
  1123. HRESULT ZWOProcessInput(IMediaBuffer* pBuffer,
  1124. BYTE* pData,
  1125. ULONG ulSize,
  1126. BOOL bTimestamp,
  1127. REFERENCE_TIME rtTimestamp) {
  1128. assert(!m_pMediaBuffer);
  1129. m_bTimestamp = bTimestamp;
  1130. m_rtTimestamp = rtTimestamp;
  1131. m_pData = pData;
  1132. m_ulData = ulSize;
  1133. m_ulUsed = 0;
  1134. // make sure they gave us a meaningful amount of data
  1135. if (m_ulData < m_ulInputQuantumSize)
  1136. return S_FALSE;
  1137. // save the buffer we were given
  1138. m_pMediaBuffer = pBuffer;
  1139. pBuffer->AddRef();
  1140. return NOERROR;
  1141. }
  1142. HRESULT ZWOProcessOutput(BYTE* pOut,
  1143. ULONG ulQuantaToProcess,
  1144. BOOL* pbTimestamp,
  1145. REFERENCE_TIME* prtTimestamp) {
  1146. assert(m_ulUsed % m_ulInputQuantumSize == 0);
  1147. HRESULT hr = FBRProcess(ulQuantaToProcess, m_pData + m_ulUsed, pOut);
  1148. if (FAILED(hr)) return hr;
  1149. ZWOConsume(ulQuantaToProcess);
  1150. if (m_bTimestamp) { // there was a timestamp on this input buffer
  1151. // m_rtTimestamp refers to the beginning of the input buffer.
  1152. // Extrapolate to the beginning of the area we just processed.
  1153. *prtTimestamp = m_rtTimestamp +
  1154. (m_ulUsed % m_ulInputQuantumSize) * m_rtDurationNumerator /
  1155. m_rtDenominator;
  1156. *pbTimestamp = TRUE;
  1157. }
  1158. else if (m_bTimestamps) { // there was a timestamp earlier
  1159. // should we extrapolate from a previous timestamp ?
  1160. *pbTimestamp = FALSE;
  1161. }
  1162. else // no timestamps at all
  1163. *pbTimestamp = FALSE;
  1164. return NOERROR;
  1165. }
  1166. ULONG ZWOAvail() {
  1167. if (m_pMediaBuffer) {
  1168. assert(m_ulData - m_ulUsed >= m_ulInputQuantumSize);
  1169. return (m_ulData - m_ulUsed) / m_ulInputQuantumSize;
  1170. }
  1171. else
  1172. return 0;
  1173. }
  1174. void ZWOConsume(ULONG ulN) { // the zero window overhead version
  1175. assert(m_pMediaBuffer);
  1176. m_ulUsed += ulN * m_ulInputQuantumSize;
  1177. assert(m_ulData >= m_ulUsed);
  1178. if (m_ulData - m_ulUsed < m_ulInputQuantumSize) {
  1179. m_pMediaBuffer->Release();
  1180. m_pMediaBuffer = NULL;
  1181. }
  1182. }
  1183. BOOL ZWOQueryAccept() {
  1184. // Accept if and only if (IFF) the DMO is not already holding a buffer.
  1185. if (!m_pMediaBuffer)
  1186. return TRUE;
  1187. else
  1188. return FALSE;
  1189. }
  1190. void ZWODiscardData() {
  1191. if (m_pMediaBuffer) {
  1192. m_pMediaBuffer->Release();
  1193. m_pMediaBuffer = NULL;
  1194. }
  1195. }
  1196. // End zero window overhead case code
  1197. //
  1198. // Non zero window overhead case code.
  1199. //
  1200. HRESULT NZWOProcessInput(IMediaBuffer* pBuffer,
  1201. BYTE* pData,
  1202. ULONG ulSize,
  1203. BOOL bTimestamp,
  1204. REFERENCE_TIME rtTimestamp) {
  1205. if (bTimestamp) { // process the timestamp
  1206. if (!m_bTimestamps) { // this is the first timestamp we've seen
  1207. // Just getting started - initialize the timestamp to refer to
  1208. // the first input quantum for which we will actually generate
  1209. // output (the first m_ulLookbehind quanta are pure lookbehind and
  1210. // generate no output).
  1211. m_rtTimestampNumerator = rtTimestamp * m_rtDenominator
  1212. + m_ulLookbehind * m_rtDurationNumerator;
  1213. }
  1214. else {
  1215. // We are already streaming and just got a new timestamp. Use it
  1216. // to check if our stored timestamp has somehow drifted away from
  1217. // where it should be and adjust if it is far enough off.
  1218. ULONG ulInputQuantaAvailable = InputQuantaAvailable();
  1219. if (ulInputQuantaAvailable) {
  1220. // ulInputQuantaAvailable is how far back in time the next
  1221. // quantum we would process is located relative the beginning
  1222. // of the new buffer we just received.
  1223. // Compute what the timestamp back there ought to be now.
  1224. REFERENCE_TIME rtTimestampNumerator;
  1225. rtTimestampNumerator = m_rtDenominator * rtTimestamp
  1226. - ulInputQuantaAvailable * m_rtDurationNumerator;
  1227. // Adjust the stored timestamp if it is off by more than half
  1228. // the duration of a quantum. Should also have a DbgLog here.
  1229. if ((m_rtTimestampNumerator >= rtTimestampNumerator + m_rtDurationNumerator / 2) ||
  1230. (m_rtTimestampNumerator <= rtTimestampNumerator - m_rtDurationNumerator / 2)) {
  1231. m_rtTimestampNumerator = rtTimestampNumerator;
  1232. }
  1233. }
  1234. else {
  1235. // We must still be accumulating the initial window overhead.
  1236. // Too early to need an adjustment, one would hope.
  1237. }
  1238. }
  1239. m_bTimestamps = TRUE;
  1240. }
  1241. if (BufferUsed() + ulSize > m_ulBufferAllocated)
  1242. return E_FAIL; // need a max input size to prevent this
  1243. // append to our buffer
  1244. AppendData(pData, ulSize);
  1245. // are we ready to produce now ?
  1246. if (NZWOAvail())
  1247. return NOERROR;
  1248. else
  1249. return S_FALSE; // no output can be produced yet
  1250. }
  1251. HRESULT NZWOProcessOutput(BYTE* pOut,
  1252. ULONG ulQuantaToProcess,
  1253. BOOL* pbTimestamp,
  1254. REFERENCE_TIME* prtTimestamp) {
  1255. //
  1256. // Handle any timestamps
  1257. //
  1258. if (m_bTimestamps) {
  1259. // In window overhead mode the stored timestamp refers to the input
  1260. // data immediately after lookbehind, which corresponds to the
  1261. // begining of the output buffer by definition of FDRProcess.
  1262. *prtTimestamp = m_rtTimestampNumerator / m_rtDenominator;
  1263. *pbTimestamp = TRUE;
  1264. }
  1265. else
  1266. *pbTimestamp = FALSE;
  1267. //
  1268. // Handle the data
  1269. //
  1270. HRESULT hr;
  1271. ULONG ulInputNeeded = m_ulInputQuantumSize * (ulQuantaToProcess + m_ulWindowOverhead);
  1272. assert(ulInputNeeded < BufferUsed());
  1273. if (m_ulDataHead + ulInputNeeded <= m_ulBufferAllocated) {
  1274. // No wraparound, everything is easy
  1275. hr = FBRProcess(ulQuantaToProcess,
  1276. m_pCircularBuffer + m_ulDataHead + m_ulLookbehind * m_ulInputQuantumSize,
  1277. pOut);
  1278. if (FAILED(hr))
  1279. return hr;
  1280. NZWOConsume(ulQuantaToProcess);
  1281. }
  1282. else { // The data we want to send wraps around the end
  1283. // Q.: does it wrap around inside the window overhead area
  1284. // or inside the main data area ?
  1285. if (m_ulDataHead + m_ulWindowOverhead * m_ulInputQuantumSize < m_ulBufferAllocated) {
  1286. // The wraparound occurs inside the main data area. Advance the
  1287. // window overhead up to the wraparound point by processing some data.
  1288. ULONG ulAdvance = m_ulBufferAllocated - (m_ulDataHead + m_ulWindowOverhead * m_ulInputQuantumSize);
  1289. assert(ulAdvance % m_ulInputQuantumSize == 0);
  1290. ulAdvance /= m_ulInputQuantumSize; // convert to quanta
  1291. assert(ulAdvance > 0);
  1292. assert(ulAdvance < ulQuantaToProcess);
  1293. hr = FBRProcess(ulAdvance,
  1294. m_pCircularBuffer + m_ulDataHead + m_ulLookbehind * m_ulInputQuantumSize,
  1295. pOut);
  1296. if (FAILED(hr))
  1297. return hr;
  1298. NZWOConsume(ulAdvance);
  1299. // Adjust stuff so that the code below can act
  1300. // as if this extra process call never happened.
  1301. pOut += m_ulOutputQuantumSize * ulAdvance;
  1302. ulQuantaToProcess -= ulAdvance;
  1303. assert(ulQuantaToProcess > 0);
  1304. // Now the wraparound point should be exactly on the boundary
  1305. // between window overhead and main data.
  1306. assert(m_ulDataHead + m_ulWindowOverhead * m_ulInputQuantumSize == m_ulBufferAllocated);
  1307. } // wraparound in main data
  1308. // When we get here, the wraparound point occurs somewhere inside
  1309. // the window overhead area or right on the border between window overhead and
  1310. // main data.
  1311. assert(m_ulDataHead + m_ulWindowOverhead * m_ulInputQuantumSize >= m_ulBufferAllocated);
  1312. ULONG ulLookaheadToCopy = m_ulBufferAllocated - m_ulDataHead;
  1313. // copy to the special area we reserved at the front
  1314. memcpy(m_pCircularBuffer - ulLookaheadToCopy,
  1315. m_pCircularBuffer + m_ulDataHead,
  1316. ulLookaheadToCopy);
  1317. // Now the block we are interested in is all in one piece
  1318. hr = FBRProcess(ulQuantaToProcess,
  1319. m_pCircularBuffer - ulLookaheadToCopy + m_ulLookbehind * m_ulInputQuantumSize,
  1320. pOut);
  1321. if (FAILED(hr))
  1322. return hr;
  1323. NZWOConsume(ulQuantaToProcess);
  1324. } // data handling - wraparound case
  1325. return NOERROR;
  1326. }
  1327. void AllocateCircularBuffer() {
  1328. // free any previously allocated input buffer
  1329. if (m_pAllocAddr)
  1330. delete[] m_pAllocAddr;
  1331. // need a better way to decide this number
  1332. m_ulBufferAllocated = max(m_ulInputQuantumSize * 16, 65536L);
  1333. m_ulDataHead = m_ulDataTail = 0;
  1334. // reserve room at the front for copying window overhead
  1335. ULONG ulPrefix = m_ulWindowOverhead * m_ulInputQuantumSize;
  1336. m_pAllocAddr = new BYTE[m_ulBufferAllocated + ulPrefix];
  1337. if (!m_pAllocAddr)
  1338. return;
  1339. m_pCircularBuffer = m_pAllocAddr + ulPrefix;
  1340. m_bParametersSet = TRUE;
  1341. }
  1342. BOOL NZWOQueryAccept() {
  1343. // We are using a temp input buffer. Is there room to append more ?
  1344. // The answer really depends on how much data they will try to feed
  1345. // us. Without knowing the maximum input buffer size, we will accept
  1346. // more if the input buffer is less than half full.
  1347. if (2 * BufferUsed() < m_ulBufferAllocated)
  1348. return TRUE;
  1349. else
  1350. return FALSE;
  1351. }
  1352. ULONG NZWOAvail() {
  1353. ULONG ulInputQuantaAvailable = BufferUsed() / m_ulInputQuantumSize;
  1354. if (ulInputQuantaAvailable > m_ulWindowOverhead)
  1355. return ulInputQuantaAvailable - m_ulWindowOverhead;
  1356. else
  1357. return 0;
  1358. }
  1359. void NZWOConsume(ULONG ulN) { // the window overhead version
  1360. assert(ulN * m_ulInputQuantumSize <= BufferUsed());
  1361. m_ulDataHead += ulN * m_ulInputQuantumSize;
  1362. if (m_ulDataHead > m_ulBufferAllocated) //wraparound
  1363. m_ulDataHead -= m_ulBufferAllocated;
  1364. // Advance the timestamp.
  1365. // The same denominator is used for both timestamp and duration.
  1366. m_rtTimestampNumerator += ulN * m_rtDurationNumerator;
  1367. }
  1368. ULONG BufferUsed() {
  1369. if (m_ulDataTail >= m_ulDataHead)
  1370. return m_ulDataTail - m_ulDataHead;
  1371. else
  1372. return m_ulBufferAllocated - (m_ulDataHead - m_ulDataTail);
  1373. }
  1374. void AppendData(BYTE *pData, ULONG ulSize) {
  1375. if (m_ulDataTail + ulSize <= m_ulBufferAllocated) { // no wraparound
  1376. memcpy(m_pCircularBuffer + m_ulDataTail, pData, ulSize);
  1377. m_ulDataTail += ulSize;
  1378. }
  1379. else { // wraparound
  1380. memcpy(m_pCircularBuffer + m_ulDataTail, pData, m_ulBufferAllocated - m_ulDataTail);
  1381. memcpy(m_pCircularBuffer, pData + m_ulBufferAllocated - m_ulDataTail, ulSize - (m_ulBufferAllocated - m_ulDataTail));
  1382. m_ulDataTail += ulSize;
  1383. m_ulDataTail -= m_ulBufferAllocated;
  1384. }
  1385. }
  1386. void NZWODiscardData() {
  1387. m_ulDataHead = m_ulDataTail = 0;
  1388. }
  1389. // End window overhead case code
  1390. protected:
  1391. //
  1392. // To be implemebted by the derived class
  1393. //
  1394. virtual HRESULT FBRProcess(DWORD cQuanta, BYTE *pIn, BYTE *pOut) = 0;
  1395. virtual HRESULT GetStreamingParams(
  1396. DWORD *pdwInputQuantumSize, // in bytes
  1397. DWORD *pdwOutputQuantumSize, // in bytes
  1398. DWORD *pdwMaxLookahead, // in input quanta, 0 means no lookahead
  1399. DWORD *pdwLookbehind,
  1400. REFERENCE_TIME *prtQuantumDuration, // same for input and output quanta
  1401. REFERENCE_TIME *prtDurationDenominator // optional, normally 1
  1402. ) = 0;
  1403. virtual HRESULT Init() {
  1404. return NOERROR;
  1405. }
  1406. private:
  1407. BOOL m_bNewInput;
  1408. // streaming parameters
  1409. BOOL m_bParametersSet;
  1410. ULONG m_ulInputQuantumSize;
  1411. ULONG m_ulOutputQuantumSize;
  1412. ULONG m_ulLookahead;
  1413. ULONG m_ulLookbehind;
  1414. ULONG m_ulWindowOverhead;
  1415. REFERENCE_TIME m_rtDurationNumerator;
  1416. REFERENCE_TIME m_rtDenominator;
  1417. // streaming state
  1418. BOOL m_bTimestamps; // we have seen at least one timestamp
  1419. BOOL m_bDiscontinuity;
  1420. BOOL m_bStreaming;
  1421. // zero window overhead case input data
  1422. IMediaBuffer *m_pMediaBuffer;
  1423. BYTE *m_pData;
  1424. ULONG m_ulData;
  1425. ULONG m_ulUsed;
  1426. BOOL m_bTimestamp; // timestamp on current buffer
  1427. REFERENCE_TIME m_rtTimestamp;
  1428. // window overhead case input data
  1429. BYTE *m_pCircularBuffer;
  1430. BYTE *m_pAllocAddr;
  1431. ULONG m_ulBufferAllocated;
  1432. ULONG m_ulDataHead;
  1433. ULONG m_ulDataTail;
  1434. REFERENCE_TIME m_rtTimestampNumerator; // uses the same denominator as duration
  1435. };
  1436. // CPCMDMO - base class for PCM audio transform filters.
  1437. // Helps non-converting PCM audio transforms with mediatype negotiation.
  1438. // Based on CFBRDMO - study that first.
  1439. //
  1440. // Derived class must implement:
  1441. // FBRProcess()
  1442. // Deriver class may implement:
  1443. // Discontinuity() // default implementaion does nothing
  1444. // Init() // default implementaion does nothing
  1445. // GetPCMParams() // default implementation proposes 44100/2/16
  1446. // CheckPCMParams() // default implementation accepts any 8/16 bit format
  1447. // GetWindowParams() // default implementation assumes no lookahead/lookbehind
  1448. //
  1449. // This class conveniently provides the following data members accessible
  1450. // by the derived class:
  1451. // ULONG m_ulSamplingRate
  1452. // ULONG m_cChannels
  1453. // BOOL m_b8bit
  1454. //
  1455. #include <mmreg.h>
  1456. #include <uuids.h>
  1457. class CPCMDMO : public CFBRDMO
  1458. {
  1459. protected:
  1460. //
  1461. // implement pure virtual CFBRDMO methods
  1462. //
  1463. HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  1464. if (ulTypeIndex > 0)
  1465. return DMO_E_NO_MORE_ITEMS;
  1466. if (pmt != NULL) {
  1467. HRESULT hr = GetType(pmt, OutputType());
  1468. if (FAILED(hr)) {
  1469. return hr;
  1470. }
  1471. }
  1472. return S_OK;
  1473. }
  1474. HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  1475. if (ulTypeIndex > 0)
  1476. return DMO_E_NO_MORE_ITEMS;
  1477. if (pmt != NULL) {
  1478. HRESULT hr = GetType(pmt, InputType());
  1479. if (FAILED(hr)) {
  1480. return hr;
  1481. }
  1482. }
  1483. return S_OK;
  1484. }
  1485. HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt) {
  1486. return CheckType(pmt, OutputType());
  1487. }
  1488. HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt) {
  1489. return CheckType(pmt, InputType());
  1490. }
  1491. HRESULT Init() {
  1492. return NOERROR;
  1493. }
  1494. HRESULT Discontinuity() {
  1495. return NOERROR;
  1496. }
  1497. HRESULT GetStreamingParams(
  1498. DWORD *pdwInputQuantumSize, // in bytes
  1499. DWORD *pdwOutputQuantumSize, // in bytes
  1500. DWORD *pdwMaxLookahead, // in input quanta, 0 means no lookahead
  1501. DWORD *pdwMaxLookbehind,
  1502. REFERENCE_TIME *prtQuantumDuration, // same for input and output quanta
  1503. REFERENCE_TIME *prtDurationDenominator // optional, normally 1
  1504. ) {
  1505. // Sanity check: all of this should have been taken care of by base class
  1506. DMO_MEDIA_TYPE* pmtIn = InputType();
  1507. DMO_MEDIA_TYPE* pmtOut = OutputType();
  1508. if (!pmtIn || !pmtOut)
  1509. return DMO_E_TYPE_NOT_SET;
  1510. if (CheckType(pmtIn, NULL) || CheckType(pmtOut, pmtIn))
  1511. return DMO_E_TYPE_NOT_ACCEPTED;
  1512. WAVEFORMATEX *pWave = (WAVEFORMATEX*)pmtIn->pbFormat;
  1513. m_b8bit = (pWave->wBitsPerSample == 8);
  1514. m_cChannels = pWave->nChannels;
  1515. m_ulSamplingRate = pWave->nSamplesPerSec;
  1516. *pdwInputQuantumSize = pWave->nBlockAlign;
  1517. *pdwOutputQuantumSize = pWave->nBlockAlign;
  1518. *prtQuantumDuration = 10000000; // rt units per sec
  1519. *prtDurationDenominator = pWave->nSamplesPerSec;
  1520. GetWindowParams(pdwMaxLookahead, pdwMaxLookbehind);
  1521. return NOERROR;
  1522. }
  1523. protected:
  1524. //
  1525. // Methods to be overridden by derived class
  1526. //
  1527. // We use this to get lookahead/lookbehind from the derived class
  1528. virtual void GetWindowParams(DWORD *pdwMaxLookahead,
  1529. DWORD *pdwMaxLookbehind) {
  1530. *pdwMaxLookahead = 0;
  1531. *pdwMaxLookbehind = 0;
  1532. }
  1533. // derived class can override these if it has specific requirements
  1534. virtual void GetPCMParams(BOOL* pb8bit, DWORD* pcChannels, DWORD* pdwSamplesPerSec) {
  1535. // These values are what the DMO will advertise in its media type.
  1536. // Specifying them here does not mean that this is the only acceptable
  1537. // combination - CheckPCMParams() is the ultimate authority on what we will
  1538. // accept.
  1539. *pb8bit = FALSE;
  1540. *pcChannels = 2;
  1541. *pdwSamplesPerSec = 44100;
  1542. }
  1543. virtual BOOL CheckPCMParams(BOOL b8bit, DWORD cChannels, DWORD dwSamplesPerSec) {
  1544. // Default implementation accepts anything. Override if you have specific
  1545. // requirements WRT sampling rate, number of channels, or bit depth.
  1546. return TRUE;
  1547. }
  1548. private:
  1549. //
  1550. // private helpers
  1551. //
  1552. HRESULT GetType(DMO_MEDIA_TYPE* pmt, const DMO_MEDIA_TYPE *pmtOther) {
  1553. HRESULT hr;
  1554. // If the other type is set, enumerate that. Otherwise propose 44100/2/16.
  1555. if (pmtOther) {
  1556. hr = MoCopyMediaType(pmt, pmtOther);
  1557. if (FAILED(hr)) {
  1558. return hr;
  1559. }
  1560. return NOERROR;
  1561. }
  1562. hr = MoInitMediaType(pmt, sizeof(WAVEFORMATEX));
  1563. if (FAILED(hr))
  1564. return hr;
  1565. pmt->majortype = MEDIATYPE_Audio;
  1566. pmt->subtype = MEDIASUBTYPE_PCM;
  1567. pmt->formattype = FORMAT_WaveFormatEx;
  1568. WAVEFORMATEX* pWave = (WAVEFORMATEX*) pmt->pbFormat;
  1569. pWave->wFormatTag = WAVE_FORMAT_PCM;
  1570. BOOL b8bit;
  1571. DWORD cChannels;
  1572. GetPCMParams(&b8bit, &cChannels, &(pWave->nSamplesPerSec));
  1573. (pWave->nChannels) = (unsigned short)cChannels;
  1574. pWave->wBitsPerSample = b8bit ? 8 : 16;
  1575. pWave->nBlockAlign = pWave->nChannels * pWave->wBitsPerSample / 8;
  1576. pWave->nAvgBytesPerSec = pWave->nSamplesPerSec * pWave->nBlockAlign;
  1577. pWave->cbSize = 0;
  1578. return NOERROR;
  1579. }
  1580. HRESULT CheckType(const DMO_MEDIA_TYPE *pmt, DMO_MEDIA_TYPE *pmtOther) {
  1581. if (NULL == pmt) {
  1582. return E_POINTER;
  1583. }
  1584. // verify that this is PCM with a WAVEFORMATEX format specifier
  1585. if ((pmt->majortype != MEDIATYPE_Audio) ||
  1586. (pmt->subtype != MEDIASUBTYPE_PCM) ||
  1587. (pmt->formattype != FORMAT_WaveFormatEx) ||
  1588. (pmt->cbFormat < sizeof(WAVEFORMATEX)) ||
  1589. (pmt->pbFormat == NULL))
  1590. return DMO_E_TYPE_NOT_ACCEPTED;
  1591. // If other type set, accept only if identical to that. Otherwise accept
  1592. // any standard PCM audio.
  1593. if (pmtOther) {
  1594. if (memcmp(pmt->pbFormat, pmtOther->pbFormat, sizeof(WAVEFORMATEX)))
  1595. return DMO_E_TYPE_NOT_ACCEPTED;
  1596. }
  1597. else {
  1598. WAVEFORMATEX* pWave = (WAVEFORMATEX*)pmt->pbFormat;
  1599. if ((pWave->wFormatTag != WAVE_FORMAT_PCM) ||
  1600. ((pWave->wBitsPerSample != 8) && (pWave->wBitsPerSample != 16)) ||
  1601. (pWave->nBlockAlign != pWave->nChannels * pWave->wBitsPerSample / 8) ||
  1602. (pWave->nAvgBytesPerSec != pWave->nSamplesPerSec * pWave->nBlockAlign) ||
  1603. !CheckPCMParams((pWave->wBitsPerSample == 8), pWave->nChannels, pWave->nSamplesPerSec))
  1604. return DMO_E_TYPE_NOT_ACCEPTED;
  1605. }
  1606. return NOERROR;
  1607. }
  1608. protected:
  1609. // format info - the derived class may look at these (but no modify)
  1610. ULONG m_ulSamplingRate;
  1611. ULONG m_cChannels;
  1612. BOOL m_b8bit;
  1613. };
  1614. //
  1615. // CGenericDMO - generic DMO base class. This is currently the only base
  1616. // class for DMOs that have multiple inputs or multiple outputs.
  1617. //
  1618. // This base class tries to be reasonably generic. The derived class reports
  1619. // how many streams it supports and describes each stream by calling
  1620. // CreateInputStreams() and CreateOutputStreams(). Each of these functions
  1621. // takes an array of STREAMDESCRIPTOR structures, each of which poits to an
  1622. // array of FORMATENTRY structures.
  1623. //
  1624. // This base class uses CInputStream and COutputStream classes (both derived
  1625. // from CStream) to keep track of input and output stream. However, these
  1626. // objects are not visible to the derived class - the derived class only sees
  1627. // stream IDs.
  1628. //
  1629. // One limitation of the scheme use here is that the derived class cannot
  1630. // override the GetType/SetType methods individually for each stream. It must
  1631. // either (a) live with a static, finite set of types communicated via the
  1632. // STREAMDESCRIPTOR structure, or (b) override all IMediaObject type methods
  1633. // and handle type negotiation for all streams itself.
  1634. //
  1635. // Processing occurs when the base class calles DoProcess (overridden by the
  1636. // derived class). DoProcess receives an array of input buffer structs and
  1637. // an array of output buffer structs. The base class takes care of talking
  1638. // to IMediaBuffers, so the derived class only sees actual data pointers.
  1639. //
  1640. // flags used to communicate with the derived class
  1641. enum _INPUT_STATUS_FLAGS {
  1642. INPUT_STATUSF_RESIDUAL // cannot be further processed w/o additional input
  1643. };
  1644. // These are used to pass buffers between this class and the derived class.
  1645. typedef struct _INPUTBUFFER {
  1646. BYTE *pData; // [in] - if NULL, the rest are garbage
  1647. DWORD cbSize; // [in]
  1648. DWORD cbUsed; // [out]
  1649. DWORD dwFlags; // [in] - DMO_INPUT_DATA_BUFFERF_XXX
  1650. DWORD dwStatus; // [out] - INPUT_STATUSF_XXX from above
  1651. REFERENCE_TIME rtTimestamp; // [in]
  1652. REFERENCE_TIME rtTimelength; // [in]
  1653. } INPUTBUFFER, *PINPUTBUFFER;
  1654. typedef struct _OUTPUTBUFFER {
  1655. BYTE *pData; // [in]
  1656. DWORD cbSize; // [in]
  1657. DWORD cbUsed; // [out]
  1658. DWORD dwFlags; // [out] - DMO_OUTPUT_DATA_BUFFERF_XXX
  1659. REFERENCE_TIME rtTimestamp; // [out]
  1660. REFERENCE_TIME rtTimelength; // [out]
  1661. } OUTPUTBUFFER, *POUTPUTBUFFER;
  1662. // Used by derived class to describe the format supported by each stream
  1663. typedef struct _FORMATENTRY
  1664. {
  1665. const GUID *majortype;
  1666. const GUID *subtype;
  1667. const GUID *formattype;
  1668. DWORD cbFormat;
  1669. BYTE* pbFormat;
  1670. } FORMATENTRY;
  1671. // These are used by the derived class to described its streams
  1672. typedef struct _INPUTSTREAMDESCRIPTOR {
  1673. DWORD cFormats;
  1674. FORMATENTRY *pFormats;
  1675. DWORD dwMinBufferSize;
  1676. BOOL bHoldsBuffers;
  1677. DWORD dwMaxLookahead; // used if HOLDS_BUFFERS set
  1678. } INPUTSTREAMDESCRIPTOR;
  1679. typedef struct _OUTPUTSTREAMDESCRIPTOR {
  1680. DWORD cFormats;
  1681. FORMATENTRY *pFormats;
  1682. DWORD dwMinBufferSize;
  1683. } OUTPUTSTREAMDESCRIPTOR;
  1684. // Common input/output stream stuff
  1685. class CStream {
  1686. public:
  1687. DMO_MEDIA_TYPE m_MediaType;
  1688. BOOL m_bEOS;
  1689. BOOL m_bTypeSet;
  1690. DWORD m_cFormats;
  1691. FORMATENTRY *m_pFormats;
  1692. DWORD m_dwMinBufferSize;
  1693. // Should really pass in a format type list
  1694. CStream()
  1695. {
  1696. MoInitMediaType(&m_MediaType, 0);
  1697. m_bTypeSet = FALSE;
  1698. Flush();
  1699. }
  1700. ~CStream()
  1701. {
  1702. MoFreeMediaType(&m_MediaType);
  1703. }
  1704. HRESULT Flush() {
  1705. m_bEOS = FALSE;
  1706. return NOERROR;
  1707. }
  1708. HRESULT StreamInfo(unsigned long *pdwFlags)
  1709. {
  1710. if (pdwFlags == NULL) {
  1711. return E_POINTER;
  1712. }
  1713. *pdwFlags = 0;
  1714. return S_OK;
  1715. }
  1716. HRESULT GetType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt)
  1717. {
  1718. if (ulTypeIndex >= m_cFormats) {
  1719. return E_INVALIDARG;
  1720. }
  1721. // Just return our types
  1722. MoInitMediaType(pmt, m_pFormats[ulTypeIndex].cbFormat);
  1723. pmt->majortype = *m_pFormats[ulTypeIndex].majortype;
  1724. pmt->subtype = *m_pFormats[ulTypeIndex].subtype;
  1725. pmt->formattype = *m_pFormats[ulTypeIndex].formattype;
  1726. memcpy(pmt->pbFormat, m_pFormats[ulTypeIndex].pbFormat, m_pFormats[ulTypeIndex].cbFormat);
  1727. return S_OK;
  1728. }
  1729. HRESULT GetCurrentType(DMO_MEDIA_TYPE *pmt)
  1730. {
  1731. if (NULL == pmt) {
  1732. return E_POINTER;
  1733. }
  1734. if (m_bTypeSet) {
  1735. // check success
  1736. MoCopyMediaType(pmt, &(m_MediaType));
  1737. return S_OK;
  1738. }
  1739. else
  1740. return DMO_E_TYPE_NOT_SET;
  1741. }
  1742. HRESULT SetType(const DMO_MEDIA_TYPE *pmt, DWORD dwFlags)
  1743. {
  1744. // Need to check this
  1745. HRESULT hr = CheckType(pmt, 0);
  1746. if (FAILED(hr)) {
  1747. return hr;
  1748. }
  1749. if (dwFlags & DMO_SET_TYPEF_TEST_ONLY) {
  1750. return NOERROR; // check konly
  1751. }
  1752. // check success
  1753. MoCopyMediaType(&m_MediaType, pmt);
  1754. m_bTypeSet = TRUE;;
  1755. return S_OK;
  1756. }
  1757. HRESULT CheckType(const DMO_MEDIA_TYPE *pmt, DWORD dwFlags)
  1758. {
  1759. if (pmt == NULL) {
  1760. return E_POINTER;
  1761. }
  1762. //if (dwFlags & ~DMO_SET_TYPEF_NOT_PARTIAL)
  1763. // return E_INVALIDARG;
  1764. // Default - check GUIDs
  1765. bool bMatched = false;
  1766. for (DWORD i = 0; i < m_cFormats; i++) {
  1767. const FORMATENTRY *pFormat = &(m_pFormats[i]);
  1768. if (pmt->majortype == *(pFormat->majortype) &&
  1769. pmt->subtype == *(pFormat->subtype) &&
  1770. pmt->formattype == *(pFormat->formattype)) {
  1771. bMatched = true;
  1772. break;
  1773. }
  1774. }
  1775. if (bMatched) {
  1776. return S_OK;
  1777. } else {
  1778. return DMO_E_INVALIDTYPE;
  1779. }
  1780. }
  1781. HRESULT SizeInfo(ULONG *plSize, ULONG *plAlignment)
  1782. {
  1783. if (plSize == NULL || plAlignment == NULL) {
  1784. return E_POINTER;
  1785. }
  1786. *plAlignment = 1;
  1787. *plSize = m_dwMinBufferSize;
  1788. return S_OK;
  1789. }
  1790. };
  1791. // Input stream specific stuff
  1792. class CInputStream : public CStream {
  1793. public:
  1794. BOOL m_bHoldsBuffers;
  1795. DWORD m_dwMaxLookahead; // used if HOLDS_BUFFERS set
  1796. // Current input sample
  1797. IMediaBuffer *m_pMediaBuffer;
  1798. DWORD m_dwFlags; // discontinuity, etc.
  1799. REFERENCE_TIME m_rtTimestamp;
  1800. REFERENCE_TIME m_rtTimelength;
  1801. BYTE *m_pData;
  1802. DWORD m_cbSize;
  1803. DWORD m_cbUsed;
  1804. // residual
  1805. BYTE *m_pbResidual;
  1806. DWORD m_cbResidual;
  1807. DWORD m_cbResidualBuffer;
  1808. // temporary buffer for handling the residual
  1809. BYTE *m_pbTemp;
  1810. HRESULT Flush() {
  1811. if (m_pMediaBuffer) {
  1812. m_pMediaBuffer->Release();
  1813. m_pMediaBuffer = NULL;
  1814. }
  1815. return CStream::Flush();
  1816. }
  1817. CInputStream() {
  1818. m_pMediaBuffer = NULL;
  1819. m_pbResidual = NULL;
  1820. m_pbTemp = NULL;
  1821. }
  1822. ~CInputStream() {
  1823. if (m_pMediaBuffer)
  1824. m_pMediaBuffer->Release();
  1825. if (m_pbResidual)
  1826. delete[] m_pbResidual;
  1827. }
  1828. HRESULT StreamInfo(DWORD *pdwFlags) {
  1829. HRESULT hr = CStream::StreamInfo(pdwFlags);
  1830. if (FAILED(hr))
  1831. return hr;
  1832. if (m_bHoldsBuffers)
  1833. *pdwFlags |= DMO_INPUT_STREAMF_HOLDS_BUFFERS;
  1834. return NOERROR;
  1835. }
  1836. HRESULT Init(INPUTSTREAMDESCRIPTOR *pDescriptor) {
  1837. m_cFormats = pDescriptor->cFormats;
  1838. m_pFormats = pDescriptor->pFormats;
  1839. m_dwMinBufferSize = pDescriptor->dwMinBufferSize;
  1840. m_bHoldsBuffers = pDescriptor->bHoldsBuffers;
  1841. m_dwMaxLookahead = pDescriptor->dwMaxLookahead;
  1842. // Just in case Init is called multiple times:
  1843. // delete any preexisting stuff.
  1844. if (m_pMediaBuffer) {
  1845. m_pMediaBuffer->Release();
  1846. m_pMediaBuffer = NULL;
  1847. }
  1848. if (m_pbResidual) {
  1849. delete[] m_pbResidual;
  1850. m_pbResidual = NULL;
  1851. }
  1852. m_cbResidual = 0;
  1853. m_cbResidualBuffer = m_dwMinBufferSize * 2; // enough ?
  1854. m_pbResidual = new BYTE[m_cbResidualBuffer];
  1855. return NOERROR;
  1856. }
  1857. HRESULT InputStatus(DWORD *pdwStatus) {
  1858. // objects that hold buffers must implement InputStatus themselves
  1859. assert(!m_bHoldsBuffers);
  1860. *pdwStatus = 0;
  1861. if (!m_pMediaBuffer)
  1862. *pdwStatus |= DMO_INPUT_STATUSF_ACCEPT_DATA;
  1863. return NOERROR;
  1864. }
  1865. HRESULT Deliver(
  1866. IMediaBuffer *pBuffer, // [in], must not be NULL
  1867. DWORD dwFlags, // [in] - discontinuity, timestamp, etc.
  1868. REFERENCE_TIME rtTimestamp, // [in], valid if flag set
  1869. REFERENCE_TIME rtTimelength // [in], valid if flag set
  1870. ) {
  1871. if (!pBuffer)
  1872. return E_POINTER;
  1873. // objects that hold buffers must implement Deliver themselves
  1874. assert(!m_bHoldsBuffers);
  1875. DWORD dwStatus = 0;
  1876. InputStatus(&dwStatus);
  1877. if (!(dwStatus & DMO_INPUT_STATUSF_ACCEPT_DATA))
  1878. return DMO_E_NOTACCEPTING;
  1879. assert(!m_pMediaBuffer); // can't hold multiple buffers
  1880. //Deal with the IMediaBuffer
  1881. HRESULT hr;
  1882. hr = pBuffer->GetBufferAndLength(&m_pData, &m_cbSize);
  1883. if (FAILED(hr))
  1884. return hr;
  1885. if (!m_cbSize) // empty buffer
  1886. return S_FALSE; // no data
  1887. pBuffer->AddRef();
  1888. m_pMediaBuffer = pBuffer;
  1889. m_dwFlags = dwFlags;
  1890. m_rtTimestamp = rtTimestamp;
  1891. m_rtTimelength = rtTimelength;
  1892. m_cbUsed = 0;
  1893. return NOERROR;
  1894. }
  1895. //
  1896. // Fetch data from the currently held IMediaBuffer plus any residual
  1897. //
  1898. HRESULT PrepareInputBuffer(INPUTBUFFER *pBuffer)
  1899. {
  1900. // Q.: do we even have any data to give it ?
  1901. if (m_pMediaBuffer) {
  1902. // Is there a residual we need to feed first ?
  1903. if (m_cbResidual) {
  1904. // Yes, prepend the residual to the new input
  1905. // If we have used some of the input buffer by now, we
  1906. // should have also used up any residual with that.
  1907. assert(m_cbUsed == 0);
  1908. // compute how many bytes total we are going to send
  1909. pBuffer->cbSize = m_cbResidual
  1910. + m_cbSize;
  1911. // Make sure we have at least dwMinBufferSize bytes of data.
  1912. // We really should - the input buffer alone ought to be at
  1913. // least that big.
  1914. assert(pBuffer->cbSize > m_dwMinBufferSize);
  1915. // Is the residual buffer big enough to hold the residual plus
  1916. // all of the new buffer ?
  1917. if (pBuffer->cbSize <= m_cbResidualBuffer) {
  1918. // Yes - wonderful, we can use the residual buffer
  1919. memcpy(m_pbResidual + m_cbResidual,
  1920. m_pData,
  1921. m_cbSize);
  1922. pBuffer->pData = m_pbResidual;
  1923. }
  1924. else {
  1925. // No - allocate a sufficiently large temporary buffer.
  1926. // This is supposed to be a rare case.
  1927. m_pbTemp = new BYTE[pBuffer->cbSize];
  1928. if (m_pbTemp == NULL)
  1929. return E_OUTOFMEMORY;
  1930. // copy the residual
  1931. memcpy(m_pbTemp,
  1932. m_pbResidual,
  1933. m_cbResidual);
  1934. // append the new buffer
  1935. memcpy(m_pbTemp + m_cbResidual,
  1936. m_pData,
  1937. m_cbSize);
  1938. // set the buffer pointer to our temp buffer
  1939. pBuffer->pData = m_pbTemp;
  1940. }
  1941. // is this the correct way to handle timestamps &
  1942. // discontinuities when handling a residual ?
  1943. pBuffer->dwFlags = 0;
  1944. }
  1945. else { // no residual
  1946. pBuffer->pData = m_pData + m_cbUsed;
  1947. pBuffer->cbSize = m_cbSize - m_cbUsed;
  1948. pBuffer->dwFlags = m_dwFlags;
  1949. pBuffer->rtTimestamp = m_rtTimestamp;
  1950. pBuffer->rtTimelength= m_rtTimelength;
  1951. }
  1952. pBuffer->cbUsed = 0; // derived class should set this
  1953. pBuffer->dwStatus = 0; // derived class should set this
  1954. }
  1955. else {
  1956. pBuffer->pData = NULL;
  1957. pBuffer->cbSize = 0;
  1958. }
  1959. return NOERROR;
  1960. }
  1961. //
  1962. // Save any residual and release the IMediaBuffer as appropriate.
  1963. // Returns TRUE if there is enough data left to call ProcesInput again.
  1964. //
  1965. BOOL PostProcessInputBuffer(INPUTBUFFER *pBuffer)
  1966. {
  1967. BOOL bRet = FALSE;
  1968. // did we even give this stream anything ?
  1969. if (m_pMediaBuffer) {
  1970. // Yes, but did it eat any of it ?
  1971. if (pBuffer->cbUsed) {
  1972. // Did we even get past the residual
  1973. if (pBuffer->cbUsed > m_cbResidual) {
  1974. // Yes - reflect this in the current buffer's cbUsed.
  1975. m_cbUsed += (pBuffer->cbUsed - m_cbResidual);
  1976. m_cbResidual = 0;
  1977. }
  1978. else {
  1979. // No - just subtract from the residual.
  1980. // This is a rather silly case.
  1981. m_cbResidual -= pBuffer->cbUsed;
  1982. memmove(m_pbResidual,
  1983. m_pbResidual + pBuffer->cbUsed,
  1984. m_cbResidual);
  1985. }
  1986. }
  1987. // Is there enough left to feed again the next time ?
  1988. if ((m_cbSize - m_cbUsed <
  1989. m_dwMinBufferSize)
  1990. || (pBuffer->dwStatus & INPUT_STATUSF_RESIDUAL)) {
  1991. // No - copy the residual and release the buffer
  1992. memcpy(m_pbResidual,
  1993. m_pData + m_cbUsed,
  1994. m_cbSize - m_cbUsed);
  1995. m_cbResidual
  1996. = pBuffer->cbSize - pBuffer->cbUsed;
  1997. m_pMediaBuffer->Release();
  1998. m_pMediaBuffer = NULL;
  1999. }
  2000. else { // Yes - need another Process call to eat remaining input
  2001. bRet = TRUE;
  2002. }
  2003. // Free any temporary buffer we may have used - rare case
  2004. if (m_pbTemp) {
  2005. delete[] m_pbTemp;
  2006. m_pbTemp = NULL;
  2007. }
  2008. }
  2009. return bRet;
  2010. }
  2011. HRESULT Discontinuity() {
  2012. // implement
  2013. // m_bDiscontinuity = TRUE;
  2014. return NOERROR;
  2015. }
  2016. HRESULT SizeInfo(ULONG *pulSize,
  2017. ULONG *pulMaxLookahead,
  2018. ULONG *pulAlignment) {
  2019. HRESULT hr = CStream::SizeInfo(pulSize, pulAlignment);
  2020. if (FAILED(hr))
  2021. return hr;
  2022. if (m_bHoldsBuffers)
  2023. *pulMaxLookahead = m_dwMaxLookahead;
  2024. else
  2025. *pulMaxLookahead = *pulSize;
  2026. return NOERROR;
  2027. }
  2028. };
  2029. // Output stream specific stuff
  2030. class COutputStream : public CStream {
  2031. public:
  2032. BOOL m_bIncomplete;
  2033. DWORD m_cbAlreadyUsed; // temp per-stream variable used during Process
  2034. HRESULT Init(OUTPUTSTREAMDESCRIPTOR *pDescriptor) {
  2035. m_cFormats = pDescriptor->cFormats;
  2036. m_pFormats = pDescriptor->pFormats;
  2037. m_dwMinBufferSize = pDescriptor->dwMinBufferSize;
  2038. return NOERROR;
  2039. }
  2040. //
  2041. // Initialize the OUTPUTBUFFER struct with info from the IMediaBuffer
  2042. //
  2043. HRESULT PrepareOutputBuffer(OUTPUTBUFFER *pBuffer, IMediaBuffer *pMediaBuffer, BOOL bNewInput)
  2044. {
  2045. //
  2046. // See if the caller supplied an output buffer
  2047. //
  2048. if (pMediaBuffer == NULL) {
  2049. // This is allowed to be NULL only if (1) the object did not set
  2050. // the INCOMPLETE flag for this stream during the last Process
  2051. // call, and (2) no new input data has been supplied to the object
  2052. // since the last Process call.
  2053. if (bNewInput)
  2054. return E_POINTER;
  2055. if (m_bIncomplete)
  2056. return E_POINTER;
  2057. // ok - initialize assuming no buffer
  2058. pBuffer->cbSize = 0;
  2059. pBuffer->pData = NULL;
  2060. }
  2061. else { // the IMediaBuffer is not NULL - deal with it
  2062. HRESULT hr;
  2063. hr = pMediaBuffer->GetMaxLength(&pBuffer->cbSize);
  2064. if (FAILED(hr))
  2065. return hr;
  2066. hr = pMediaBuffer->GetBufferAndLength(
  2067. &(pBuffer->pData),
  2068. &(m_cbAlreadyUsed));
  2069. if (FAILED(hr))
  2070. return hr;
  2071. // Check current size - should we even bother with this ?
  2072. if (m_cbAlreadyUsed) {
  2073. if (m_cbAlreadyUsed >= pBuffer->cbSize)
  2074. return E_INVALIDARG; // buffer already full ?!?
  2075. pBuffer->cbSize -= m_cbAlreadyUsed;
  2076. pBuffer->pData += m_cbAlreadyUsed;
  2077. }
  2078. }
  2079. // It is really the derived class's job to set these, but we
  2080. // will be nice to it and initialize them anyway just in case.
  2081. pBuffer->cbUsed = 0;
  2082. pBuffer->dwFlags = 0;
  2083. return NOERROR;
  2084. }
  2085. //
  2086. // Copy the OUTPUTBUFFER back into the DMO_OUTPUT_DATA_BUFFER (yawn)
  2087. //
  2088. void PostProcessOutputBuffer(OUTPUTBUFFER *pBuffer, DMO_OUTPUT_DATA_BUFFER *pDMOBuffer, BOOL bForceIncomplete) {
  2089. assert(pBuffer->cbUsed <= pBuffer->cbSize);
  2090. if (pDMOBuffer->pBuffer)
  2091. pDMOBuffer->pBuffer->SetLength(pBuffer->cbUsed + m_cbAlreadyUsed);
  2092. pDMOBuffer->dwStatus = pBuffer->dwFlags;
  2093. pDMOBuffer->rtTimestamp = pBuffer->rtTimestamp;
  2094. pDMOBuffer->rtTimelength = pBuffer->rtTimelength;
  2095. // Even if the derived class did not set INCOMPLETE, we may need to
  2096. // set it anyway if some input buffer we are holding still has
  2097. // enough data to call Process() again.
  2098. if (bForceIncomplete)
  2099. pDMOBuffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
  2100. // remember this output stream's INCOMPLETE state
  2101. if (pDMOBuffer->dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE)
  2102. m_bIncomplete = TRUE;
  2103. else
  2104. m_bIncomplete = FALSE;
  2105. }
  2106. };
  2107. // Code that goes at the beginning of every IMediaObject method
  2108. #define INPUT_STREAM_PROLOGUE \
  2109. CDMOAutoLock l(&m_cs); \
  2110. if (ulInputStreamIndex >= m_nInputStreams) \
  2111. return DMO_E_INVALIDSTREAMINDEX; \
  2112. CInputStream *pStream = &m_pInputStreams[ulInputStreamIndex]
  2113. #define OUTPUT_STREAM_PROLOGUE \
  2114. CDMOAutoLock l(&m_cs); \
  2115. if (ulOutputStreamIndex >= m_nOutputStreams) \
  2116. return DMO_E_INVALIDSTREAMINDEX; \
  2117. COutputStream *pStream = &m_pOutputStreams[ulOutputStreamIndex]
  2118. class CGenericDMO : public IMediaObject
  2119. {
  2120. public:
  2121. CGenericDMO() {
  2122. #ifdef DMO_NOATL
  2123. InitializeCriticalSection(&m_cs);
  2124. #endif
  2125. m_nInputStreams = 0;
  2126. m_nOutputStreams = 0;
  2127. }
  2128. #ifdef DMO_NOATL
  2129. ~CGenericDMO() {
  2130. DeleteCriticalSection(&m_cs);
  2131. }
  2132. #endif
  2133. public:
  2134. //
  2135. // Implement IMediaObject methods
  2136. //
  2137. STDMETHODIMP GetInputStreamInfo(ULONG ulInputStreamIndex, DWORD *pdwFlags)
  2138. {
  2139. INPUT_STREAM_PROLOGUE;
  2140. return pStream->StreamInfo(pdwFlags);
  2141. }
  2142. STDMETHODIMP GetOutputStreamInfo(ULONG ulOutputStreamIndex, DWORD *pdwFlags)
  2143. {
  2144. OUTPUT_STREAM_PROLOGUE;
  2145. return pStream->StreamInfo(pdwFlags);
  2146. }
  2147. STDMETHODIMP GetInputType(ULONG ulInputStreamIndex, ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  2148. INPUT_STREAM_PROLOGUE;
  2149. return pStream->GetType(ulTypeIndex, pmt);
  2150. }
  2151. STDMETHODIMP GetOutputType(ULONG ulOutputStreamIndex, ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  2152. OUTPUT_STREAM_PROLOGUE;
  2153. return pStream->GetType(ulTypeIndex, pmt);
  2154. }
  2155. STDMETHODIMP GetInputCurrentType(ULONG ulInputStreamIndex, DMO_MEDIA_TYPE *pmt) {
  2156. INPUT_STREAM_PROLOGUE;
  2157. return pStream->GetCurrentType(pmt);
  2158. }
  2159. STDMETHODIMP GetOutputCurrentType(ULONG ulOutputStreamIndex, DMO_MEDIA_TYPE *pmt) {
  2160. OUTPUT_STREAM_PROLOGUE;
  2161. return pStream->GetCurrentType(pmt);
  2162. }
  2163. STDMETHODIMP GetInputSizeInfo(ULONG ulInputStreamIndex, ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  2164. INPUT_STREAM_PROLOGUE;
  2165. return pStream->SizeInfo(pulSize, pcbMaxLookahead, pulAlignment);
  2166. }
  2167. STDMETHODIMP GetOutputSizeInfo(ULONG ulOutputStreamIndex, ULONG *pulSize, ULONG *pulAlignment) {
  2168. OUTPUT_STREAM_PROLOGUE;
  2169. return pStream->SizeInfo(pulSize, pulAlignment);
  2170. }
  2171. STDMETHODIMP SetInputType(ULONG ulInputStreamIndex, const DMO_MEDIA_TYPE *pmt, DWORD dwFlags) {
  2172. INPUT_STREAM_PROLOGUE;
  2173. return pStream->SetType(pmt, dwFlags);
  2174. }
  2175. STDMETHODIMP SetOutputType(ULONG ulOutputStreamIndex, const DMO_MEDIA_TYPE *pmt, DWORD dwFlags) {
  2176. OUTPUT_STREAM_PROLOGUE;
  2177. return pStream->SetType(pmt, dwFlags);
  2178. }
  2179. STDMETHODIMP GetInputStatus(
  2180. ULONG ulInputStreamIndex,
  2181. DWORD *pdwStatus
  2182. ) {
  2183. INPUT_STREAM_PROLOGUE;
  2184. return pStream->InputStatus(pdwStatus);
  2185. }
  2186. STDMETHODIMP GetInputMaxLatency(unsigned long ulInputStreamIndex, REFERENCE_TIME *prtLatency) {
  2187. // I don't know what to do with this right now.
  2188. // Punt to the derived class ?
  2189. return E_NOTIMPL;
  2190. }
  2191. STDMETHODIMP SetInputMaxLatency(unsigned long ulInputStreamIndex, REFERENCE_TIME rtLatency) {
  2192. return E_NOTIMPL;
  2193. }
  2194. STDMETHODIMP ProcessInput(
  2195. DWORD ulInputStreamIndex,
  2196. IMediaBuffer *pBuffer, // [in], must not be NULL
  2197. DWORD dwFlags, // [in] - discontinuity, timestamp, etc.
  2198. REFERENCE_TIME rtTimestamp, // [in], valid if flag set
  2199. REFERENCE_TIME rtTimelength // [in], valid if flag set
  2200. ) {
  2201. INPUT_STREAM_PROLOGUE;
  2202. return pStream->Deliver(pBuffer, dwFlags, rtTimestamp, rtTimelength);
  2203. }
  2204. STDMETHODIMP Discontinuity(ULONG ulInputStreamIndex) {
  2205. INPUT_STREAM_PROLOGUE;
  2206. return pStream->Discontinuity();
  2207. }
  2208. STDMETHODIMP Flush()
  2209. {
  2210. CDMOAutoLock l(&m_cs);
  2211. // Flush all the streams
  2212. ULONG i;
  2213. for (i = 0; i < m_nInputStreams; i++) {
  2214. m_pInputStreams[i].Flush();
  2215. }
  2216. for (i = 0; i < m_nOutputStreams; i++) {
  2217. m_pOutputStreams[i].Flush();
  2218. }
  2219. return S_OK;
  2220. }
  2221. STDMETHODIMP AllocateStreamingResources() {return S_OK;}
  2222. STDMETHODIMP FreeStreamingResources() {return S_OK;}
  2223. STDMETHODIMP GetStreamCount(unsigned long *pulNumberOfInputStreams, unsigned long *pulNumberOfOutputStreams)
  2224. {
  2225. CDMOAutoLock l(&m_cs);
  2226. if (pulNumberOfInputStreams == NULL ||
  2227. pulNumberOfOutputStreams == NULL) {
  2228. return E_POINTER;
  2229. }
  2230. *pulNumberOfInputStreams = m_nInputStreams;
  2231. *pulNumberOfOutputStreams = m_nOutputStreams;
  2232. return S_OK;
  2233. }
  2234. STDMETHODIMP ProcessOutput(
  2235. DWORD dwReserved,
  2236. DWORD ulOutputBufferCount,
  2237. DMO_OUTPUT_DATA_BUFFER *pOutputBuffers,
  2238. DWORD *pdwStatus)
  2239. {
  2240. CDMOAutoLock l(&m_cs);
  2241. if (ulOutputBufferCount != m_nOutputStreams)
  2242. return E_INVALIDARG;
  2243. HRESULT hr;
  2244. DWORD c;
  2245. // Prepare the input buffers
  2246. for (c = 0; c < m_nInputStreams; c++) {
  2247. // objects that hold buffers must implement Process themselves
  2248. assert(!m_pInputStreams[c].m_bHoldsBuffers);
  2249. hr = m_pInputStreams[c].PrepareInputBuffer(&m_pInputBuffers[c]);
  2250. if (FAILED(hr))
  2251. return hr;
  2252. }
  2253. //
  2254. // Prepare the output buffers
  2255. //
  2256. for (c = 0; c < m_nOutputStreams; c++) {
  2257. hr = m_pOutputStreams[c].PrepareOutputBuffer(&m_pOutputBuffers[c], pOutputBuffers[c].pBuffer, m_bNewInput);
  2258. if (FAILED(hr))
  2259. return hr;
  2260. }
  2261. hr = DoProcess(m_pInputBuffers,m_pOutputBuffers);
  2262. if (FAILED(hr))
  2263. return hr; // don't just "return hr", do something !
  2264. // post-process input buffers
  2265. BOOL bSomeInputStillHasData = FALSE;
  2266. for (c = 0; c < m_nInputStreams; c++) {
  2267. if (m_pInputStreams[c].PostProcessInputBuffer(&m_pInputBuffers[c]))
  2268. bSomeInputStillHasData = TRUE;
  2269. }
  2270. // post-process output buffers
  2271. for (c = 0; c < m_nOutputStreams; c++) {
  2272. m_pOutputStreams[c].PostProcessOutputBuffer(&m_pOutputBuffers[c],
  2273. &pOutputBuffers[c],
  2274. bSomeInputStillHasData);
  2275. }
  2276. m_bNewInput = FALSE;
  2277. return NOERROR;
  2278. }
  2279. protected:
  2280. //
  2281. // These are called by the derived class at initialization time
  2282. //
  2283. HRESULT CreateInputStreams(INPUTSTREAMDESCRIPTOR *pStreams, DWORD cStreams) {
  2284. CDMOAutoLock l(&m_cs);
  2285. if (pStreams == NULL) {
  2286. return E_POINTER;
  2287. }
  2288. m_pInputStreams = new CInputStream[cStreams];
  2289. if (m_pInputStreams == NULL) {
  2290. return E_OUTOFMEMORY;
  2291. }
  2292. DWORD c;
  2293. for (c = 0; c < cStreams; c++) {
  2294. HRESULT hr = m_pInputStreams[c].Init(&(pStreams[c]));
  2295. if (FAILED(hr)) {
  2296. delete[] m_pInputStreams;
  2297. return hr;
  2298. }
  2299. }
  2300. m_pInputBuffers = new INPUTBUFFER[cStreams];
  2301. if (!m_pInputBuffers) {
  2302. delete[] m_pInputStreams;
  2303. return E_OUTOFMEMORY;
  2304. }
  2305. m_nInputStreams = cStreams;
  2306. return NOERROR;
  2307. }
  2308. HRESULT CreateOutputStreams(OUTPUTSTREAMDESCRIPTOR *pStreams, DWORD cStreams) {
  2309. CDMOAutoLock l(&m_cs);
  2310. if (pStreams == NULL) {
  2311. return E_POINTER;
  2312. }
  2313. m_pOutputStreams = new COutputStream[cStreams];
  2314. if (m_pOutputStreams == NULL) {
  2315. return E_OUTOFMEMORY;
  2316. }
  2317. DWORD c;
  2318. for (c = 0; c < cStreams; c++) {
  2319. HRESULT hr = m_pOutputStreams[c].Init(&(pStreams[c]));
  2320. if (FAILED(hr)) {
  2321. delete[] m_pOutputStreams;
  2322. return hr;
  2323. }
  2324. }
  2325. m_pOutputBuffers = new OUTPUTBUFFER[cStreams];
  2326. if (!m_pOutputBuffers) {
  2327. delete[] m_pOutputStreams;
  2328. return E_OUTOFMEMORY;
  2329. }
  2330. m_nOutputStreams = cStreams;
  2331. return NOERROR;
  2332. }
  2333. virtual HRESULT DoProcess(INPUTBUFFER*, OUTPUTBUFFER *) = 0;
  2334. private:
  2335. ULONG m_nInputStreams;
  2336. CInputStream* m_pInputStreams;
  2337. ULONG m_nOutputStreams;
  2338. COutputStream* m_pOutputStreams;
  2339. INPUTBUFFER* m_pInputBuffers;
  2340. OUTPUTBUFFER* m_pOutputBuffers;
  2341. BOOL m_bNewInput;
  2342. #ifdef DMO_NOATL
  2343. CRITICAL_SECTION m_cs;
  2344. #else
  2345. CComAutoCriticalSection m_cs;
  2346. #endif
  2347. };
  2348. #endif // __DMOBASE_H__