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.

2320 lines
79 KiB

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