WavSink.cpp
Upload User: geng8029
Upload Date: 2021-01-30
Package Size: 187k
Code Size: 58k
Category:

Audio program

Development Platform:

Visual C++

  1. //#include "StdAfx.h"
  2. //-----------------------------------------------------------------------------
  3. // File: WaveSink.cpp
  4. // Description: Archive sink for creating .wav files.
  5. //
  6. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  7. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  8. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  9. // PARTICULAR PURPOSE.
  10. //
  11. //  Copyright (C) Microsoft Corporation. All rights reserved.
  12. //-----------------------------------------------------------------------------
  13. #include "WavSink.h"
  14. #include <aviriff.h>
  15. #pragma warning( push )
  16. #pragma warning( disable : 4355 )  // 'this' used in base member initializer list
  17. //-------------------------------------------------------------------
  18. // Name: CreateWavSink 
  19. // Description: Creates an instance of the WavSink object. 
  20. //
  21. // To use the WavSink, include this header file in an application
  22. // and link to the library file created by this project.
  23. //
  24. // pStream: Pointer to a bytestream where the .wav file will be
  25. //          written. The bystream must support writing and seeking.
  26. //
  27. // ppSink:  Receives a pointer to the IMFMediaSink interface. The
  28. //          caller must release the interface.
  29. //-------------------------------------------------------------------
  30. HRESULT CreateWavSink(IMFByteStream *pStream, IMFMediaSink **ppSink, CALLBACK_FOR_CAPTUREDATA cb)
  31. {
  32.     return CWavSink::CreateInstance(pStream, IID_IMFMediaSink, (void**)ppSink, cb);
  33. }
  34. // The stream ID of the one stream on the sink.
  35. const DWORD WAV_SINK_STREAM_ID = 1;
  36. // WAV_FILE_HEADER
  37. // This structure contains the first part of the .wav file, up to the
  38. // data portion. (Wave files are so simple there is no reason to write
  39. // a general-purpose RIFF authoring object.)
  40. struct WAV_FILE_HEADER
  41. {
  42.     RIFFCHUNK       FileHeader;
  43.     DWORD           fccWaveType;    // must be 'WAVE'
  44.     RIFFCHUNK       WaveHeader;
  45.     WAVEFORMATEX    WaveFormat;
  46.     RIFFCHUNK       DataHeader;
  47. };
  48. // PCM_Audio_Format_Params
  49. // Defines parameters for uncompressed PCM audio formats.
  50. // The remaining fields can be derived from these.
  51. struct PCM_Audio_Format_Params
  52. {
  53.     DWORD   nSamplesPerSec; // Samples per second.
  54.     WORD    wBitsPerSample; // Bits per sample.
  55.     WORD    nChannels;      // Number of channels.
  56. };
  57. // g_AudioFormats: Static list of our preferred formats.
  58. // This is an ordered list that we use to hand out formats in the 
  59. // stream's IMFMediaTypeHandler::GetMediaTypeByIndex method. The 
  60. // stream will accept other bit rates not listed here.
  61. //PCM_Audio_Format_Params g_AudioFormats[] =
  62. //{
  63. //    { 48000, 16, 2 },
  64. //    { 48000, 8, 2 },
  65. //    { 44100, 16, 2 },
  66. //    { 44100, 8, 2 },
  67. //    { 22050, 16, 2 },
  68. //    { 22050, 8, 2 },
  69. //
  70. //    { 48000, 16, 1 },
  71. //    { 48000, 8, 1 },
  72. //    { 44100, 16, 1 },
  73. //    { 44100, 8, 1 },
  74. //    { 22050, 16, 1 },
  75. //    { 22050, 8, 1 },
  76. //};
  77. PCM_Audio_Format_Params g_AudioFormats[] =
  78. {
  79.     { 8000, 16, 1 }
  80. };
  81. DWORD g_NumAudioFormats = ARRAY_SIZE(g_AudioFormats);
  82. // Forward declares
  83. HRESULT ValidateWaveFormat(const WAVEFORMATEX *pWav, DWORD cbSize);
  84. HRESULT CreatePCMAudioType(
  85.     UINT32 sampleRate,        // Samples per second
  86.     UINT32 bitsPerSample,     // Bits per sample
  87.     UINT32 cChannels,         // Number of channels
  88.     IMFMediaType **ppType     // Receives a pointer to the media type.
  89.     );
  90. /////////////////////////////////////////////////////////////////////////////////////////////
  91. //
  92. // CWavSink class. - Implements the media sink.
  93. //
  94. // Notes:
  95. // - Most public methods calls CheckShutdown. This method fails if the sink was shut down.
  96. //
  97. /////////////////////////////////////////////////////////////////////////////////////////////
  98. //-------------------------------------------------------------------
  99. // Name: CreateInstance 
  100. // Description: Creates an instance of the WavSink object. 
  101. // [See CreateWavSink]
  102. //-------------------------------------------------------------------
  103. /* static */ HRESULT CWavSink::CreateInstance(IMFByteStream *pStream, REFIID iid, void **ppSink, CALLBACK_FOR_CAPTUREDATA cb)
  104. {
  105.     if (pStream == NULL || ppSink == NULL)
  106.     {
  107.         return E_INVALIDARG;
  108.     }
  109.     HRESULT hr = S_OK;
  110.     CWavSink *pSink = new CWavSink();   // Created with ref count = 1.
  111.     if (pSink == NULL)
  112.     {
  113.         hr = E_OUTOFMEMORY;
  114.     }
  115.     if (SUCCEEDED(hr))
  116.     {
  117.         hr = pSink->Initialize(pStream);
  118.     }
  119.     if (SUCCEEDED(hr))
  120.     {
  121.         hr = pSink->QueryInterface(iid, ppSink);
  122.     }
  123. //Add by zyw
  124.     if (SUCCEEDED(hr))
  125.     {
  126.         hr = pSink->SetCallBackObject(cb);
  127.     }
  128.     SAFE_RELEASE(pSink);
  129.     return hr;
  130. }
  131. //-------------------------------------------------------------------
  132. // CWavSink constructor.
  133. //-------------------------------------------------------------------
  134. CWavSink::CWavSink() :
  135.     m_nRefCount(1), m_IsShutdown(FALSE), m_pStream(NULL), m_pClock(NULL)
  136. {
  137. }
  138. //-------------------------------------------------------------------
  139. // CWavSink destructor.
  140. //-------------------------------------------------------------------
  141. CWavSink::~CWavSink()
  142. {
  143.     TRACE((L"~CWavSinkn"));
  144.     assert(m_IsShutdown);
  145. }
  146. // IUnknown methods
  147. ULONG CWavSink::AddRef()
  148. {
  149.     return InterlockedIncrement(&m_nRefCount);
  150. }
  151. ULONG  CWavSink::Release()
  152. {
  153.     ULONG uCount = InterlockedDecrement(&m_nRefCount);
  154.     if (uCount == 0)
  155.     {
  156.         delete this;
  157.     }
  158.     // For thread safety, return a temporary variable.
  159.     return uCount;
  160. }
  161. HRESULT CWavSink::QueryInterface(REFIID iid, void** ppv)
  162. {
  163.     if (!ppv)
  164.     {
  165.         return E_POINTER;
  166.     }
  167.     if (iid == IID_IUnknown)
  168.     {
  169.         *ppv = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this));
  170.     }
  171.     else if (iid == __uuidof(IMFMediaSink))
  172.     {
  173.         *ppv = static_cast<IMFMediaSink*>(this);
  174.     }
  175.     else if (iid == __uuidof(IMFFinalizableMediaSink))
  176.     {
  177.         *ppv = static_cast<IMFFinalizableMediaSink*>(this);
  178.     }
  179.     else if (iid == __uuidof(IMFClockStateSink))
  180.     {
  181.         *ppv = static_cast<IMFClockStateSink*>(this);
  182.     }
  183.     else
  184.     {
  185.         *ppv = NULL;
  186.         return E_NOINTERFACE;
  187.     }
  188.     AddRef();
  189.     return S_OK;
  190. }
  191. ///  IMFMediaSink methods.
  192. //-------------------------------------------------------------------
  193. // Name: GetCharacteristics 
  194. // Description: Returns the characteristics flags. 
  195. //
  196. // Note: This sink has a fixed number of streams and is rateless.
  197. //-------------------------------------------------------------------
  198. HRESULT CWavSink::GetCharacteristics(DWORD *pdwCharacteristics)
  199. {
  200.     AutoLock lock(m_critSec);
  201.     if (pdwCharacteristics == NULL)
  202.     {
  203.         return E_INVALIDARG;
  204.     }
  205.     HRESULT hr = CheckShutdown();
  206.     if (SUCCEEDED(hr))
  207.     {
  208.         *pdwCharacteristics = MEDIASINK_FIXED_STREAMS | MEDIASINK_RATELESS;
  209.     }
  210.     return hr;
  211. }
  212. //-------------------------------------------------------------------
  213. // Name: AddStreamSink 
  214. // Description: Adds a new stream to the sink. 
  215. //
  216. // Note: This sink has a fixed number of streams, so this method
  217. //       always returns MF_E_STREAMSINKS_FIXED.
  218. //-------------------------------------------------------------------
  219. HRESULT CWavSink::AddStreamSink( 
  220.     DWORD dwStreamSinkIdentifier,
  221.     IMFMediaType *pMediaType,
  222.     IMFStreamSink **ppStreamSink)
  223. {
  224.     return MF_E_STREAMSINKS_FIXED;
  225. }
  226. //-------------------------------------------------------------------
  227. // Name: RemoveStreamSink 
  228. // Description: Removes a stream from the sink. 
  229. //
  230. // Note: This sink has a fixed number of streams, so this method
  231. //       always returns MF_E_STREAMSINKS_FIXED.
  232. //-------------------------------------------------------------------
  233. HRESULT CWavSink::RemoveStreamSink(DWORD dwStreamSinkIdentifier)
  234. {
  235.     return MF_E_STREAMSINKS_FIXED;
  236. }
  237. //-------------------------------------------------------------------
  238. // Name: GetStreamSinkCount 
  239. // Description: Returns the number of streams. 
  240. //-------------------------------------------------------------------
  241. HRESULT CWavSink::GetStreamSinkCount(DWORD *pcStreamSinkCount)
  242. {
  243.     AutoLock lock(m_critSec);
  244.     if (pcStreamSinkCount == NULL)
  245.     {
  246.         return E_INVALIDARG;
  247.     }
  248.     HRESULT hr = CheckShutdown();
  249.     if (SUCCEEDED(hr))
  250.     {
  251.         *pcStreamSinkCount = 1;  // Fixed number of streams.
  252.     }
  253.     return hr;
  254. }
  255. //-------------------------------------------------------------------
  256. // Name: GetStreamSinkByIndex 
  257. // Description: Retrieves a stream by index. 
  258. //-------------------------------------------------------------------
  259. HRESULT CWavSink::GetStreamSinkByIndex( 
  260.     DWORD dwIndex,
  261.     IMFStreamSink **ppStreamSink)
  262. {
  263.     AutoLock lock(m_critSec);
  264.     if (ppStreamSink == NULL)
  265.     {
  266.         return E_INVALIDARG;
  267.     }
  268.     // Fixed stream: Index 0. 
  269.     if (dwIndex > 0)
  270.     {
  271.         return MF_E_INVALIDINDEX;
  272.     }
  273.     HRESULT hr = CheckShutdown();
  274.     if (SUCCEEDED(hr))
  275.     {
  276.         *ppStreamSink = m_pStream;
  277.         (*ppStreamSink)->AddRef();
  278.     }
  279.     return hr;
  280. }
  281. //-------------------------------------------------------------------
  282. // Name: GetStreamSinkById 
  283. // Description: Retrieves a stream by ID. 
  284. //-------------------------------------------------------------------
  285. HRESULT CWavSink::GetStreamSinkById( 
  286.     DWORD dwStreamSinkIdentifier,
  287.     IMFStreamSink **ppStreamSink)
  288. {
  289.     AutoLock lock(m_critSec);
  290.     if (ppStreamSink == NULL)
  291.     {
  292.         return E_INVALIDARG;
  293.     }
  294.     // Fixed stream ID.
  295.     if (dwStreamSinkIdentifier != WAV_SINK_STREAM_ID)
  296.     {
  297.         return MF_E_INVALIDSTREAMNUMBER;
  298.     }
  299.     HRESULT hr = CheckShutdown();
  300.     if (SUCCEEDED(hr))
  301.     {
  302.         *ppStreamSink = m_pStream;
  303.         (*ppStreamSink)->AddRef();
  304.     }
  305.     return hr;
  306. }
  307. //-------------------------------------------------------------------
  308. // Name: SetPresentationClock 
  309. // Description: Sets the presentation clock. 
  310. //
  311. // pPresentationClock: Pointer to the clock. Can be NULL.
  312. //-------------------------------------------------------------------
  313. HRESULT CWavSink::SetPresentationClock(IMFPresentationClock *pPresentationClock)
  314. {
  315.     AutoLock lock(m_critSec);
  316.     HRESULT hr = CheckShutdown();
  317.     // If we already have a clock, remove ourselves from that clock's
  318.     // state notifications.
  319.     if (SUCCEEDED(hr))
  320.     {
  321.         if (m_pClock)
  322.         {
  323.             hr = m_pClock->RemoveClockStateSink(this);
  324.         }
  325.     }
  326.     // Register ourselves to get state notifications from the new clock.
  327.     if (SUCCEEDED(hr))
  328.     {
  329.         if (pPresentationClock)
  330.         {
  331.             hr = pPresentationClock->AddClockStateSink(this);
  332.         }
  333.     }
  334.     if (SUCCEEDED(hr))
  335.     {
  336.         // Release the pointer to the old clock.
  337.         // Store the pointer to the new clock.
  338.         SAFE_RELEASE(m_pClock);
  339.         m_pClock = pPresentationClock;
  340.         if (m_pClock)
  341.         {
  342.             m_pClock->AddRef();
  343.         }
  344.     }
  345.     return hr;
  346. }
  347. //-------------------------------------------------------------------
  348. // Name: GetPresentationClock 
  349. // Description: Returns a pointer to the presentation clock. 
  350. //-------------------------------------------------------------------
  351. HRESULT CWavSink::GetPresentationClock(IMFPresentationClock **ppPresentationClock)
  352. {
  353.     AutoLock lock(m_critSec);
  354.     if (ppPresentationClock == NULL)
  355.     {
  356.         return E_INVALIDARG;
  357.     }
  358.     HRESULT hr = CheckShutdown();
  359.     if (SUCCEEDED(hr))
  360.     {
  361.         if (m_pClock == NULL)
  362.         {
  363.             hr = MF_E_NO_CLOCK; // There is no presentation clock.
  364.         }
  365.         else
  366.         {
  367.             // Return the pointer to the caller.
  368.             *ppPresentationClock = m_pClock;
  369.             (*ppPresentationClock)->AddRef();
  370.         }
  371.     }
  372.     return hr;
  373. }
  374. //-------------------------------------------------------------------
  375. // Name: Shutdown 
  376. // Description: Releases resources held by the media sink. 
  377. //-------------------------------------------------------------------
  378. HRESULT CWavSink::Shutdown()
  379. {
  380.     TRACE((L"CWavSink::Shutdownn"));
  381.     AutoLock lock(m_critSec);
  382.     HRESULT hr = CheckShutdown();
  383.     if (SUCCEEDED(hr))
  384.     {
  385.         hr = m_pStream->Shutdown();
  386.         SAFE_RELEASE(m_pClock);
  387.         SAFE_RELEASE(m_pStream);
  388.  
  389.         m_IsShutdown = true;
  390.     }
  391.     return hr;
  392. }
  393. /// IMFFinalizableMediaSink methods
  394. //-------------------------------------------------------------------
  395. // Name: BeginFinalize 
  396. // Description: Starts the asynchronous finalize operation.
  397. //
  398. // Note: We use the Finalize operation to write the RIFF headers.
  399. //-------------------------------------------------------------------
  400. HRESULT CWavSink::BeginFinalize( 
  401.     IMFAsyncCallback *pCallback,
  402.     IUnknown *punkState)
  403. {
  404.     TRACE((L"CWavSink::BeginFinalizen"));
  405.     AutoLock lock(m_critSec);
  406.     HRESULT hr = CheckShutdown();
  407.     // Tell the stream to finalize.
  408.     if (SUCCEEDED(hr))
  409.     {
  410.         hr = m_pStream->Finalize(pCallback, punkState);
  411.     }
  412.     return hr;
  413. }
  414. //-------------------------------------------------------------------
  415. // Name: EndFinalize 
  416. // Description: Completes the asynchronous finalize operation.
  417. //-------------------------------------------------------------------
  418. HRESULT CWavSink::EndFinalize(IMFAsyncResult *pResult)
  419. {
  420.     TRACE((L"CWavSink::EndFinalizen"));
  421.     HRESULT hr = S_OK;
  422.     // Return the status code from the async result.
  423.     if (pResult == NULL)
  424.     {
  425.         hr = E_INVALIDARG;
  426.     }
  427.     else
  428.     {
  429.         hr = pResult->GetStatus();
  430.     }
  431.     return hr;
  432. }
  433. //-------------------------------------------------------------------
  434. // Name: OnClockStart 
  435. // Description: Called when the presentation clock starts.
  436. //
  437. // hnsSystemTime: System time when the clock started.
  438. // llClockStartOffset: Starting presentatation time.
  439. //
  440. // Note: For an archive sink, we don't care about the system time.
  441. //       But we need to cache the value of llClockStartOffset. This 
  442. //       gives us the earliest time stamp that we archive. If any 
  443. //       input samples have an earlier time stamp, we discard them.
  444. //-------------------------------------------------------------------
  445. HRESULT CWavSink::OnClockStart( 
  446.         /* [in] */ MFTIME hnsSystemTime,
  447.         /* [in] */ LONGLONG llClockStartOffset)
  448. {
  449.     AutoLock lock(m_critSec);
  450.     HRESULT hr = CheckShutdown();
  451.     if (SUCCEEDED(hr))
  452.     {
  453.         hr = m_pStream->Start(llClockStartOffset);
  454.     }
  455.     return hr;
  456. }    
  457. //-------------------------------------------------------------------
  458. // Name: OnClockStop 
  459. // Description: Called when the presentation clock stops.
  460. //
  461. // Note: After this method is called, we stop accepting new data.
  462. //-------------------------------------------------------------------
  463. HRESULT CWavSink::OnClockStop( 
  464.         /* [in] */ MFTIME hnsSystemTime)
  465. {
  466.     TRACE((L"CWavSink::OnClockStopn"));
  467.     AutoLock lock(m_critSec);
  468.     HRESULT hr = CheckShutdown();
  469.     if (SUCCEEDED(hr))
  470.     {
  471.         hr = m_pStream->Stop();
  472.     }
  473.     return hr;
  474. }    
  475. //-------------------------------------------------------------------
  476. // Name: OnClockPause 
  477. // Description: Called when the presentation clock paused.
  478. //
  479. // Note: For an archive sink, the paused state is equivalent to the
  480. //       running (started) state. We still accept data and archive it.
  481. //-------------------------------------------------------------------
  482. HRESULT CWavSink::OnClockPause( 
  483.         /* [in] */ MFTIME hnsSystemTime)
  484. {
  485.     AutoLock lock(m_critSec);
  486.     HRESULT hr = CheckShutdown();
  487.     if (SUCCEEDED(hr))
  488.     {
  489.         hr = m_pStream->Pause();
  490.     }
  491.     return hr;
  492. }
  493. //-------------------------------------------------------------------
  494. // Name: OnClockRestart 
  495. // Description: Called when the presentation clock restarts.
  496. //-------------------------------------------------------------------
  497. HRESULT CWavSink::OnClockRestart( 
  498.         /* [in] */ MFTIME hnsSystemTime)
  499. {
  500.     AutoLock lock(m_critSec);
  501.     HRESULT hr = CheckShutdown();
  502.     if (SUCCEEDED(hr))
  503.     {
  504.         hr = m_pStream->Restart();
  505.     }
  506.     return hr;
  507. }
  508. //-------------------------------------------------------------------
  509. // Name: OnClockSetRate 
  510. // Description: Called when the presentation clock's rate changes.
  511. //
  512. // Note: For a rateless sink, the clock rate is not important.
  513. //-------------------------------------------------------------------
  514. HRESULT CWavSink::OnClockSetRate( 
  515.         /* [in] */ MFTIME hnsSystemTime,
  516.         /* [in] */ float flRate)
  517. {
  518.     return S_OK;
  519. }
  520. /// Private methods
  521. //-------------------------------------------------------------------
  522. // Name: Initialize 
  523. // Description: Initializes the media sink.
  524. //
  525. // Note: This method is called once when the media sink is first
  526. //       initialized.
  527. //-------------------------------------------------------------------
  528. HRESULT CWavSink::Initialize(IMFByteStream *pByteStream)
  529. {
  530.     HRESULT hr = S_OK;
  531.     m_pStream = new CWavStream();
  532.     if (m_pStream == NULL)
  533.     {
  534.         hr = E_OUTOFMEMORY;
  535.     }
  536.     // Initialize the stream.
  537.     if (SUCCEEDED(hr))
  538.     {
  539.         hr = m_pStream->Initialize(this, pByteStream);
  540.     }
  541.     return hr;
  542. }
  543. //-------------------------------------------------------------------
  544. // Name: SetCallBackObject 
  545. // Description: set the call back object.
  546. //
  547. // Note: This method is called once when the media sink is first
  548. //       initialized.
  549. //-------------------------------------------------------------------
  550. HRESULT CWavSink::SetCallBackObject(CALLBACK_FOR_CAPTUREDATA cb)
  551. {
  552.     HRESULT hr = S_OK;
  553. if (cb)
  554. {
  555. m_cb = cb;
  556. }
  557. else
  558. {
  559. hr = E_FAIL;
  560. }
  561.     return hr;
  562. }
  563. //-------------------------------------------------------------------
  564. // Name: CallBackFromAudioSample 
  565. // Description: run the call back object.
  566. //
  567. // Note: This method is called once when the media sink is first
  568. //       initialized.
  569. //-------------------------------------------------------------------
  570. HRESULT CWavSink::CallBackFromAudioSample(const byte *bpData, ULONG ulDataLength)
  571. {
  572.     HRESULT hr = S_OK;
  573. if (m_cb)
  574. {
  575. (*m_cb)(bpData, ulDataLength);
  576. }
  577. else
  578. {
  579. hr = E_FAIL;
  580. }
  581.     return hr;
  582. }
  583. /////////////////////////////////////////////////////////////////////////////////////////////
  584. //
  585. // CAsyncOperation class. - Private class used by CWavStream class.
  586. //
  587. /////////////////////////////////////////////////////////////////////////////////////////////
  588. CWavStream::CAsyncOperation::CAsyncOperation(StreamOperation op) 
  589.     : m_nRefCount(1), m_op(op)
  590. {
  591. }
  592. CWavStream::CAsyncOperation::~CAsyncOperation()
  593. {
  594.     assert(m_nRefCount == 0);
  595. }
  596. ULONG CWavStream::CAsyncOperation::AddRef()
  597. {
  598.     return InterlockedIncrement(&m_nRefCount);
  599. }
  600. ULONG CWavStream::CAsyncOperation::Release()
  601. {
  602.     ULONG uCount = InterlockedDecrement(&m_nRefCount);
  603.     if (uCount == 0)
  604.     {
  605.         delete this;
  606.     }
  607.     // For thread safety, return a temporary variable.
  608.     return uCount;
  609. }
  610. HRESULT CWavStream::CAsyncOperation::QueryInterface(REFIID iid, void** ppv)
  611. {
  612.     if (!ppv)
  613.     {
  614.         return E_POINTER;
  615.     }
  616.     if (iid == IID_IUnknown)
  617.     {
  618.         *ppv = static_cast<IUnknown*>(this);
  619.     }
  620.     else
  621.     {
  622.         *ppv = NULL;
  623.         return E_NOINTERFACE;
  624.     }
  625.     AddRef();
  626.     return S_OK;
  627. }
  628. /////////////////////////////////////////////////////////////////////////////////////////////
  629. //
  630. // CWavStream class. - Implements the stream sink.
  631. //
  632. // Notes: 
  633. // - Most of the real work gets done in this class. 
  634. // - The sink has one stream. If it had multiple streams, it would need to coordinate them.
  635. // - Most operations are done asynchronously on a work queue.
  636. // - Async methods are handled like this:
  637. //      1. Call ValidateOperation to check if the operation is permitted at this time
  638. //      2. Create an CAsyncOperation object for the operation.
  639. //      3. Call QueueAsyncOperation. This puts the operation on the work queue.
  640. //      4. The workqueue calls OnDispatchWorkItem.
  641. // - Locking:
  642. //      To avoid deadlocks, do not hold the CWavStream lock followed by the CWavSink lock.
  643. //      The other order is OK (CWavSink, then CWavStream).
  644. // 
  645. /////////////////////////////////////////////////////////////////////////////////////////////
  646. //-------------------------------------------------------------------
  647. // CWavStream constructor
  648. //-------------------------------------------------------------------
  649. CWavStream::CWavStream() 
  650.     : m_nRefCount(1), m_state(State_TypeNotSet), m_IsShutdown(FALSE), 
  651.     m_pSink(NULL), m_pEventQueue(NULL), m_pByteStream(NULL), 
  652.     m_pCurrentType(NULL), m_pFinalizeResult(NULL),
  653.     m_StartTime(0), m_cbDataWritten(0), m_WorkQueueId(0), 
  654.     m_WorkQueueCB(this, &CWavStream::OnDispatchWorkItem)
  655. {
  656. }
  657. //-------------------------------------------------------------------
  658. // CWavStream destructor
  659. //-------------------------------------------------------------------
  660. CWavStream::~CWavStream()
  661. {
  662.     TRACE((L"~CWavStreamn"));
  663.     assert(m_IsShutdown);
  664. }
  665. // IUnknown methods
  666. ULONG CWavStream::AddRef()
  667. {
  668.     return InterlockedIncrement(&m_nRefCount);
  669. }
  670. ULONG  CWavStream::Release()
  671. {
  672.     ULONG uCount = InterlockedDecrement(&m_nRefCount);
  673.     if (uCount == 0)
  674.     {
  675.         delete this;
  676.     }
  677.     // For thread safety, return a temporary variable.
  678.     return uCount;
  679. }
  680. HRESULT CWavStream::QueryInterface(REFIID iid, void** ppv)
  681. {
  682.     if (!ppv)
  683.     {
  684.         return E_POINTER;
  685.     }
  686.     if (iid == IID_IUnknown)
  687.     {
  688.         *ppv = static_cast<IUnknown*>(static_cast<IMFStreamSink*>(this));
  689.     }
  690.     else if (iid == __uuidof(IMFStreamSink ))
  691.     {
  692.         *ppv = static_cast<IMFStreamSink *>(this);
  693.     }
  694.     else if (iid == __uuidof(IMFMediaEventGenerator))
  695.     {
  696.         *ppv = static_cast<IMFMediaEventGenerator*>(this);
  697.     }
  698.     else if (iid == __uuidof(IMFMediaTypeHandler))
  699.     {
  700.         *ppv = static_cast<IMFMediaTypeHandler*>(this);
  701.     }
  702.     else
  703.     {
  704.         *ppv = NULL;
  705.         return E_NOINTERFACE;
  706.     }
  707.     AddRef();
  708.     return S_OK;
  709. }
  710. // IMFMediaEventGenerator methods.
  711. // Note: These methods call through to the event queue helper object.
  712. HRESULT CWavStream::BeginGetEvent(IMFAsyncCallback* pCallback, IUnknown* punkState)
  713. {
  714.     HRESULT hr = S_OK;
  715.     AutoLock lock(m_critSec);
  716.     hr = CheckShutdown();
  717.     if (SUCCEEDED(hr))
  718.     {
  719.         hr = m_pEventQueue->BeginGetEvent(pCallback, punkState);
  720.     }
  721.     return hr;
  722. }
  723. HRESULT CWavStream::EndGetEvent(IMFAsyncResult* pResult, IMFMediaEvent** ppEvent)
  724. {
  725.     HRESULT hr = S_OK;
  726.     AutoLock lock(m_critSec);
  727.     hr = CheckShutdown();
  728.     if (SUCCEEDED(hr))
  729.     {
  730.         hr = m_pEventQueue->EndGetEvent(pResult, ppEvent);
  731.     }
  732.     return hr;
  733. }
  734. HRESULT CWavStream::GetEvent(DWORD dwFlags, IMFMediaEvent** ppEvent)
  735. {
  736.     // NOTE: 
  737.     // GetEvent can block indefinitely, so we don't hold the lock.
  738.     // This requires some juggling with the event queue pointer.
  739.     HRESULT hr = S_OK;
  740.     IMFMediaEventQueue *pQueue = NULL;
  741.     { // scope for lock
  742.       
  743.         AutoLock lock(m_critSec);
  744.         // Check shutdown
  745.         hr = CheckShutdown();
  746.         // Get the pointer to the event queue.
  747.         if (SUCCEEDED(hr))
  748.         {
  749.             pQueue = m_pEventQueue;
  750.             pQueue->AddRef();
  751.         }
  752.     }   // release lock
  753.     // Now get the event.
  754.     if (SUCCEEDED(hr))
  755.     {
  756.         hr = pQueue->GetEvent(dwFlags, ppEvent);
  757.     }
  758.     SAFE_RELEASE(pQueue);
  759.     return hr;
  760. }
  761. HRESULT CWavStream::QueueEvent(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT* pvValue)
  762. {
  763.     HRESULT hr = S_OK;
  764.     AutoLock lock(m_critSec);
  765.     hr = CheckShutdown();
  766.     if (SUCCEEDED(hr))
  767.     {
  768.         hr = m_pEventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue);
  769.     }
  770.     return hr;
  771. }
  772. /// IMFStreamSink methods
  773. //-------------------------------------------------------------------
  774. // Name: GetMediaSink 
  775. // Description: Returns the parent media sink.
  776. //-------------------------------------------------------------------
  777. HRESULT CWavStream::GetMediaSink(IMFMediaSink **ppMediaSink)
  778. {
  779.     AutoLock lock(m_critSec);
  780.     if (ppMediaSink == NULL)
  781.     {
  782.         return E_INVALIDARG;
  783.     }
  784.     HRESULT hr = CheckShutdown();
  785.     if (SUCCEEDED(hr))
  786.     {
  787.         *ppMediaSink = (IMFMediaSink*)m_pSink;
  788.         (*ppMediaSink)->AddRef();
  789.     }
  790.     return hr;
  791. }
  792. //-------------------------------------------------------------------
  793. // Name: GetIdentifier 
  794. // Description: Returns the stream identifier.
  795. //-------------------------------------------------------------------
  796. HRESULT CWavStream::GetIdentifier(DWORD *pdwIdentifier)
  797. {
  798.     AutoLock lock(m_critSec);
  799.     if (pdwIdentifier == NULL)
  800.     {
  801.         return E_INVALIDARG;
  802.     }
  803.     HRESULT hr = CheckShutdown();
  804.     if (SUCCEEDED(hr))
  805.     {
  806.         *pdwIdentifier = WAV_SINK_STREAM_ID;
  807.     }
  808.     return hr;
  809. }
  810. //-------------------------------------------------------------------
  811. // Name: GetMediaTypeHandler 
  812. // Description: Returns a media type handler for this stream.
  813. //-------------------------------------------------------------------
  814. HRESULT CWavStream::GetMediaTypeHandler(IMFMediaTypeHandler **ppHandler)
  815. {
  816.     AutoLock lock(m_critSec);
  817.     if (ppHandler == NULL)
  818.     {
  819.         return E_INVALIDARG;
  820.     }
  821.     HRESULT hr = CheckShutdown();
  822.     // This stream object acts as its own type handler, so we QI ourselves.
  823.     if (SUCCEEDED(hr))
  824.     {
  825.         hr = this->QueryInterface(IID_IMFMediaTypeHandler, (void**)ppHandler);
  826.     }
  827.     return hr;
  828. }
  829. //-------------------------------------------------------------------
  830. // Name: ProcessSample 
  831. // Description: Receives an input sample. [Asynchronous]
  832. //
  833. // Note: The client should only give us a sample after we send an
  834. //       MEStreamSinkRequestSample event.
  835. //-------------------------------------------------------------------
  836. HRESULT CWavStream::ProcessSample(IMFSample *pSample)
  837. {
  838.     AutoLock lock(m_critSec);
  839.     if (pSample == NULL)
  840.     {
  841.         return E_INVALIDARG;
  842.     }
  843.     HRESULT hr = S_OK;
  844.     
  845.     hr = CheckShutdown();
  846.     // Validate the operation.
  847.     if (SUCCEEDED(hr))
  848.     {
  849.         hr = ValidateOperation(OpProcessSample);
  850.     }
  851.     // Add the sample to the sample queue.
  852.     if (SUCCEEDED(hr))
  853.     {
  854.         hr = m_SampleQueue.InsertBack(pSample);
  855.     }
  856.     // Unless we are paused, start an async operation to dispatch the next sample.
  857.     if (SUCCEEDED(hr))
  858.     {
  859.         if (m_state != State_Paused)
  860.         {
  861.             // Queue the operation.
  862.             hr = QueueAsyncOperation(OpProcessSample); 
  863.         }
  864.     }
  865.     return hr;
  866. }
  867. //-------------------------------------------------------------------
  868. // Name: PlaceMarker 
  869. // Description: Receives a marker. [Asynchronous]
  870. //
  871. // Note: The client can call PlaceMarker at any time. In response,
  872. //       we need to queue an MEStreamSinkMarer event, but not until
  873. //       *after* we have processed all samples that we have received
  874. //       up to this point. 
  875. //
  876. //       Also, in general you might need to handle specific marker
  877. //       types, although this sink does not.
  878. //-------------------------------------------------------------------
  879. HRESULT CWavStream::PlaceMarker( 
  880.     MFSTREAMSINK_MARKER_TYPE eMarkerType,
  881.     const PROPVARIANT *pvarMarkerValue,
  882.     const PROPVARIANT *pvarContextValue)
  883. {
  884.     AutoLock lock(m_critSec);
  885.     HRESULT hr = S_OK;
  886.     
  887.     IMarker *pMarker = NULL;
  888.     
  889.     hr = CheckShutdown();
  890.     if (SUCCEEDED(hr))
  891.     {
  892.         hr = ValidateOperation(OpPlaceMarker);
  893.     }
  894.     // Create a marker object and put it on the sample queue.
  895.     if (SUCCEEDED(hr))
  896.     {
  897.         hr = CMarker::Create(
  898.             eMarkerType,
  899.             pvarMarkerValue,
  900.             pvarContextValue,
  901.             &pMarker);
  902.     }
  903.     if (SUCCEEDED(hr))
  904.     {
  905.         hr = m_SampleQueue.InsertBack(pMarker);
  906.     }
  907.     // Unless we are paused, start an async operation to dispatch the next sample/marker.
  908.     if (SUCCEEDED(hr))
  909.     {
  910.         if (m_state != State_Paused)
  911.         {
  912.             // Queue the operation.
  913.             hr = QueueAsyncOperation(OpPlaceMarker); // Increments ref count on pOp.
  914.         }
  915.     }
  916.     SAFE_RELEASE(pMarker);
  917.     return hr;   
  918. }
  919. //-------------------------------------------------------------------
  920. // Name: Flush 
  921. // Description: Discards all samples that were not processed yet.
  922. //-------------------------------------------------------------------
  923. HRESULT CWavStream::Flush()
  924. {
  925.     AutoLock lock(m_critSec);
  926.     HRESULT hr = CheckShutdown();
  927.     if (SUCCEEDED(hr))
  928.     {
  929.         // Note: Even though we are flushing data, we still need to send 
  930.         // any marker events that were queued.
  931.         hr = ProcessSamplesFromQueue(DropSamples);
  932.     }
  933.     return hr;
  934. }
  935. /// IMFMediaTypeHandler methods
  936. //-------------------------------------------------------------------
  937. // Name: IsMediaTypeSupported 
  938. // Description: Check if a media type is supported.
  939. //
  940. // pMediaType: The media type to check.
  941. // ppMediaType: Optionally, receives a "close match" media type.
  942. //-------------------------------------------------------------------
  943. HRESULT CWavStream::IsMediaTypeSupported( 
  944.     /* [in] */ IMFMediaType *pMediaType,
  945.     /* [out] */ IMFMediaType **ppMediaType)
  946. {
  947.     if (pMediaType == NULL)
  948.     {
  949.         return E_INVALIDARG;
  950.     }
  951.     AutoLock lock(m_critSec);
  952.     GUID majorType = GUID_NULL;
  953.     WAVEFORMATEX *pWav = NULL;
  954.     UINT cbSize = 0;
  955.     HRESULT hr = CheckShutdown();
  956.     if (SUCCEEDED(hr))
  957.     {
  958.         hr = pMediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType);
  959.     }
  960.     // First make sure it's audio. 
  961.     if (SUCCEEDED(hr))
  962.     {
  963.         if (majorType != MFMediaType_Audio)
  964.         {
  965.             hr = MF_E_INVALIDTYPE;
  966.         }
  967.     }
  968.     // Get a WAVEFORMATEX structure to validate against.
  969.     if (SUCCEEDED(hr))
  970.     {
  971.         hr = MFCreateWaveFormatExFromMFMediaType(pMediaType, &pWav, &cbSize);
  972.     }
  973.     // Validate the WAVEFORMATEX structure.
  974.     if (SUCCEEDED(hr))
  975.     {
  976.         hr = ValidateWaveFormat(pWav, cbSize);
  977.     }
  978.     // We don't return any "close match" types.
  979.     if (ppMediaType)
  980.     {
  981.         *ppMediaType = NULL;
  982.     }
  983.     CoTaskMemFree(pWav);
  984.     return hr;
  985. }
  986. //-------------------------------------------------------------------
  987. // Name: GetMediaTypeCount 
  988. // Description: Return the number of preferred media types.
  989. //-------------------------------------------------------------------
  990. HRESULT CWavStream::GetMediaTypeCount(DWORD *pdwTypeCount)
  991. {
  992.     if (pdwTypeCount == NULL)
  993.     {
  994.         return E_INVALIDARG;
  995.     }
  996.     AutoLock lock(m_critSec);
  997.     HRESULT hr = CheckShutdown();
  998.     if (SUCCEEDED(hr))
  999.     {
  1000.         *pdwTypeCount = g_NumAudioFormats;
  1001.     }
  1002.     return hr;
  1003. }
  1004. //-------------------------------------------------------------------
  1005. // Name: GetMediaTypeByIndex 
  1006. // Description: Return a preferred media type by index.
  1007. //-------------------------------------------------------------------
  1008. HRESULT CWavStream::GetMediaTypeByIndex( 
  1009.     /* [in] */ DWORD dwIndex,
  1010.     /* [out] */ IMFMediaType **ppType)
  1011. {
  1012.     if (ppType == NULL)
  1013.     {
  1014.         return E_INVALIDARG;
  1015.     }
  1016.     AutoLock lock(m_critSec);
  1017.     HRESULT hr = CheckShutdown();
  1018.     if (SUCCEEDED(hr))
  1019.     {
  1020.         if (dwIndex >= g_NumAudioFormats)
  1021.         {
  1022.             hr = MF_E_NO_MORE_TYPES;
  1023.         }
  1024.     }
  1025.     if (SUCCEEDED(hr))
  1026.     {
  1027.         const DWORD   nSamplesPerSec = g_AudioFormats[dwIndex].nSamplesPerSec;
  1028.         const WORD    wBitsPerSample = g_AudioFormats[dwIndex].wBitsPerSample;
  1029.         const WORD    nChannels = g_AudioFormats[dwIndex].nChannels;
  1030.         hr = CreatePCMAudioType(nSamplesPerSec, wBitsPerSample, nChannels, ppType);
  1031.     }
  1032.     return hr;
  1033. }
  1034. //-------------------------------------------------------------------
  1035. // Name: SetCurrentMediaType 
  1036. // Description: Set the current media type.
  1037. //-------------------------------------------------------------------
  1038. HRESULT CWavStream::SetCurrentMediaType(IMFMediaType *pMediaType)
  1039. {
  1040.     if (pMediaType == NULL)
  1041.     {
  1042.         return E_INVALIDARG;
  1043.     }
  1044.     AutoLock lock(m_critSec);
  1045.     HRESULT hr = CheckShutdown();
  1046.     // We don't allow format changes after streaming starts,
  1047.     // because this would invalidate the .wav file.
  1048.     if (SUCCEEDED(hr))
  1049.     {
  1050.         hr = ValidateOperation(OpSetMediaType);
  1051.     }
  1052.     if (SUCCEEDED(hr))
  1053.     {
  1054.         hr = IsMediaTypeSupported(pMediaType, NULL);
  1055.     }
  1056.     if (SUCCEEDED(hr))
  1057.     {
  1058.         SAFE_RELEASE(m_pCurrentType);
  1059.         m_pCurrentType = pMediaType;
  1060.         m_pCurrentType->AddRef();
  1061.         m_state = State_Ready;
  1062.     }
  1063.     return hr;
  1064. }
  1065. //-------------------------------------------------------------------
  1066. // Name: GetCurrentMediaType 
  1067. // Description: Return the current media type, if any.
  1068. //-------------------------------------------------------------------
  1069. HRESULT CWavStream::GetCurrentMediaType(IMFMediaType **ppMediaType)
  1070. {
  1071.     AutoLock lock(m_critSec);
  1072.     if (ppMediaType == NULL)
  1073.     {
  1074.         return E_INVALIDARG;
  1075.     }
  1076.     HRESULT hr = CheckShutdown();
  1077.     if (SUCCEEDED(hr))
  1078.     {
  1079.         if (m_pCurrentType == NULL)
  1080.         {
  1081.             hr = MF_E_NOT_INITIALIZED;
  1082.         }
  1083.     }
  1084.     if (SUCCEEDED(hr))
  1085.     {
  1086.         *ppMediaType = m_pCurrentType;
  1087.         (*ppMediaType)->AddRef();
  1088.     }
  1089.     return hr;
  1090. }
  1091. //-------------------------------------------------------------------
  1092. // Name: GetMajorType 
  1093. // Description: Return the major type GUID.
  1094. //-------------------------------------------------------------------
  1095. HRESULT CWavStream::GetMajorType(GUID *pguidMajorType)
  1096. {
  1097.     if (pguidMajorType == NULL)
  1098.     {
  1099.         return E_INVALIDARG;
  1100.     }
  1101.     *pguidMajorType = MFMediaType_Audio;
  1102.     return S_OK;
  1103. }
  1104. // private methods
  1105. //-------------------------------------------------------------------
  1106. // Name: Initialize 
  1107. // Description: Initializes the stream sink.
  1108. //
  1109. // Note: This method is called once when the media sink is first
  1110. //       initialized.
  1111. //-------------------------------------------------------------------
  1112. HRESULT CWavStream::Initialize(CWavSink *pParent, IMFByteStream *pByteStream)
  1113. {
  1114.     assert(pParent != NULL);
  1115.     assert(pByteStream != NULL);
  1116.     HRESULT hr = S_OK;
  1117.     
  1118.     DWORD dwCaps  = 0;
  1119.     const DWORD dwRequiredCaps = (MFBYTESTREAM_IS_WRITABLE | MFBYTESTREAM_IS_SEEKABLE);
  1120.     // Make sure the byte stream has the necessary caps bits.
  1121.     hr = pByteStream->GetCapabilities(&dwCaps);
  1122.     if (SUCCEEDED(hr))
  1123.     {
  1124.         if ((dwCaps & dwRequiredCaps) != dwRequiredCaps)
  1125.         {
  1126.             hr = E_FAIL;
  1127.         }
  1128.     }
  1129.     // Move the file pointer to leave room for the RIFF headers.
  1130.     if (SUCCEEDED(hr))
  1131.     {
  1132.         hr = pByteStream->SetCurrentPosition(sizeof(WAV_FILE_HEADER));
  1133.     }
  1134.     // Create the event queue helper.
  1135.     if (SUCCEEDED(hr))
  1136.     {
  1137.         hr = MFCreateEventQueue(&m_pEventQueue);
  1138.     }
  1139.     // Allocate a new work queue for async operations.
  1140.     if (SUCCEEDED(hr))
  1141.     {
  1142.         hr = MFAllocateWorkQueue(&m_WorkQueueId);
  1143.     }
  1144.     if (SUCCEEDED(hr))
  1145.     {
  1146.         m_pByteStream = pByteStream;
  1147.         m_pByteStream->AddRef();
  1148.         m_pSink = pParent;
  1149.         m_pSink->AddRef();
  1150.     }
  1151.     return hr;
  1152. }
  1153. //-------------------------------------------------------------------
  1154. // Name: Start 
  1155. // Description: Called when the presentation clock starts.
  1156. //
  1157. // Note: Start time can be PRESENTATION_CURRENT_POSITION meaning
  1158. //       resume from the last current position.
  1159. //-------------------------------------------------------------------
  1160. HRESULT CWavStream::Start(MFTIME start)
  1161. {
  1162.     AutoLock lock(m_critSec);
  1163.     HRESULT hr = S_OK;
  1164.     hr = ValidateOperation(OpStart);
  1165.     if (SUCCEEDED(hr))
  1166.     {
  1167.         if (start != PRESENTATION_CURRENT_POSITION)
  1168.         {
  1169.             m_StartTime = start;        // Cache the start time.
  1170.         }
  1171.         m_state = State_Started;
  1172.         hr = QueueAsyncOperation(OpStart);
  1173.     }
  1174.     return hr;
  1175. }
  1176. //-------------------------------------------------------------------
  1177. // Name: Stop
  1178. // Description: Called when the presentation clock stops.
  1179. //-------------------------------------------------------------------
  1180. HRESULT CWavStream::Stop()
  1181. {
  1182.     AutoLock lock(m_critSec);
  1183.     HRESULT hr = S_OK;
  1184.     hr = ValidateOperation(OpStop);
  1185.     if (SUCCEEDED(hr))
  1186.     {
  1187.         m_state = State_Stopped;
  1188.         hr = QueueAsyncOperation(OpStop);
  1189.     }
  1190.     return hr;
  1191. }
  1192. //-------------------------------------------------------------------
  1193. // Name: Pause
  1194. // Description: Called when the presentation clock pauses.
  1195. //-------------------------------------------------------------------
  1196. HRESULT CWavStream::Pause()
  1197. {
  1198.     AutoLock lock(m_critSec);
  1199.     HRESULT hr = S_OK;
  1200.     hr = ValidateOperation(OpPause);
  1201.     if (SUCCEEDED(hr))
  1202.     {
  1203.         m_state = State_Paused;
  1204.         hr = QueueAsyncOperation(OpPause);
  1205.     }
  1206.     return hr;
  1207. }
  1208. //-------------------------------------------------------------------
  1209. // Name: Restart
  1210. // Description: Called when the presentation clock restarts.
  1211. //-------------------------------------------------------------------
  1212. HRESULT CWavStream::Restart()
  1213. {
  1214.     AutoLock lock(m_critSec);
  1215.     HRESULT hr = S_OK;
  1216.     hr = ValidateOperation(OpRestart);
  1217.     if (SUCCEEDED(hr))
  1218.     {
  1219.         m_state = State_Started;
  1220.         hr = QueueAsyncOperation(OpRestart);
  1221.     }
  1222.     return hr;
  1223. }
  1224. //-------------------------------------------------------------------
  1225. // Name: Finalize
  1226. // Description: Starts the async finalize operation.
  1227. //-------------------------------------------------------------------
  1228. HRESULT CWavStream::Finalize(IMFAsyncCallback *pCallback, IUnknown *punkState)
  1229. {
  1230.     AutoLock lock(m_critSec);
  1231.     HRESULT hr = S_OK;
  1232.     hr = ValidateOperation(OpFinalize);
  1233.     if (SUCCEEDED(hr))
  1234.     {
  1235.         if (m_pFinalizeResult != NULL)
  1236.         {
  1237.             hr = MF_E_INVALIDREQUEST;  // The operation is already pending.
  1238.         }
  1239.     }
  1240.     // Create and store the async result object.
  1241.     if (SUCCEEDED(hr))
  1242.     {
  1243.         hr = MFCreateAsyncResult(NULL, pCallback, punkState, &m_pFinalizeResult);
  1244.     }
  1245.     if (SUCCEEDED(hr))
  1246.     {
  1247.         m_state = State_Finalized;
  1248.         hr = QueueAsyncOperation(OpFinalize); 
  1249.     }
  1250.     return hr;
  1251. }
  1252. //-------------------------------------------------------------------
  1253. // Name: ValidStateMatrix
  1254. // Description: Class-static matrix of operations vs states.
  1255. //
  1256. // If an entry is TRUE, the operation is valid from that state.
  1257. //-------------------------------------------------------------------
  1258. BOOL CWavStream::ValidStateMatrix[CWavStream::State_Count][CWavStream::Op_Count] = 
  1259. {
  1260. // States:    Operations:
  1261. //            SetType   Start     Restart   Pause     Stop      Sample    Marker    Finalize
  1262. /* NotSet */  TRUE,     FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  1263. /* Ready */   TRUE,     TRUE,     FALSE,    TRUE,     TRUE,     FALSE,    TRUE,     TRUE,
  1264. /* Start */   FALSE,    TRUE,     FALSE,    TRUE,     TRUE,     TRUE,     TRUE,     TRUE,
  1265. /* Pause */   FALSE,    TRUE,     TRUE,     TRUE,     TRUE,     TRUE,     TRUE,     TRUE,
  1266. /* Stop */    FALSE,    TRUE,     FALSE,    FALSE,    TRUE,     FALSE,    TRUE,     TRUE,
  1267. /* Final */   FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE
  1268. // Note about states:
  1269. // 1. OnClockRestart should only be called from paused state.
  1270. // 2. While paused, the sink accepts samples but does not process them.
  1271. };
  1272. //-------------------------------------------------------------------
  1273. // Name: ValidateOperation
  1274. // Description: Checks if an operation is valid in the current state.
  1275. //-------------------------------------------------------------------
  1276. HRESULT CWavStream::ValidateOperation(StreamOperation op)
  1277. {
  1278.     assert(!m_IsShutdown);
  1279.     HRESULT hr = S_OK;
  1280.     
  1281.     BOOL bTransitionAllowed = ValidStateMatrix[m_state][op];
  1282.     if (bTransitionAllowed)
  1283.     {
  1284.         return S_OK;
  1285.     }
  1286.     else
  1287.     {
  1288.         return MF_E_INVALIDREQUEST;
  1289.     }
  1290. }
  1291. //-------------------------------------------------------------------
  1292. // Name: Shutdown
  1293. // Description: Shuts down the stream sink.
  1294. //-------------------------------------------------------------------
  1295. HRESULT CWavStream::Shutdown()
  1296. {
  1297.     assert(!m_IsShutdown);
  1298.     if (m_pEventQueue)
  1299.     {
  1300.         m_pEventQueue->Shutdown();
  1301.     }
  1302.     MFUnlockWorkQueue(m_WorkQueueId);
  1303.     m_SampleQueue.Clear();
  1304.     SAFE_RELEASE(m_pSink);
  1305.     SAFE_RELEASE(m_pEventQueue);
  1306.     SAFE_RELEASE(m_pByteStream);
  1307.     SAFE_RELEASE(m_pCurrentType);
  1308.     SAFE_RELEASE(m_pFinalizeResult);
  1309.     m_IsShutdown = TRUE;
  1310.     return S_OK;
  1311. }
  1312. //-------------------------------------------------------------------
  1313. // Name: QueueAsyncOperation
  1314. // Description: Puts an async operation on the work queue.
  1315. //-------------------------------------------------------------------
  1316. HRESULT CWavStream::QueueAsyncOperation(StreamOperation op)
  1317. {
  1318.     HRESULT hr = S_OK;
  1319.     CAsyncOperation *pOp = new CAsyncOperation(op); // Created with ref count = 1
  1320.     if (pOp == NULL)
  1321.     {
  1322.         hr = E_OUTOFMEMORY;
  1323.     }
  1324.     if (SUCCEEDED(hr))
  1325.     {
  1326.         hr = MFPutWorkItem(m_WorkQueueId, &m_WorkQueueCB, pOp); 
  1327.     }
  1328.     SAFE_RELEASE(pOp);
  1329.     return hr;
  1330. }
  1331. //-------------------------------------------------------------------
  1332. // Name: OnDispatchWorkItem
  1333. // Description: Callback for MFPutWorkItem.
  1334. //-------------------------------------------------------------------
  1335. HRESULT CWavStream::OnDispatchWorkItem(IMFAsyncResult* pAsyncResult)
  1336. {
  1337.     // Called by work queue thread. Need to hold the critical section.
  1338.     AutoLock lock(m_critSec);
  1339.     HRESULT hr = S_OK;
  1340.     IUnknown *pState = NULL;
  1341.     hr = pAsyncResult->GetState(&pState);
  1342.     if (SUCCEEDED(hr))
  1343.     {
  1344.         // The state object is a CAsncOperation object.
  1345.         CAsyncOperation *pOp = (CAsyncOperation*)pState;
  1346.         StreamOperation op = pOp->m_op;
  1347.         switch (op)
  1348.         {
  1349.         case OpStart:
  1350.         case OpRestart:
  1351.             // Send MEStreamSinkStarted.
  1352.             hr = QueueEvent(MEStreamSinkStarted, GUID_NULL, hr, NULL);
  1353.             
  1354.             // Kick things off by requesting two samples...
  1355.             if (SUCCEEDED(hr))
  1356.             {
  1357.                 hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, hr, NULL);
  1358.             }
  1359.             if (SUCCEEDED(hr))
  1360.             {
  1361.                 hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, hr, NULL);
  1362.             }
  1363.             // There might be samples queue from earlier (ie, while paused).
  1364.             if (SUCCEEDED(hr))
  1365.             {
  1366.                 hr = ProcessSamplesFromQueue(WriteSamples);
  1367.             }
  1368.             break;
  1369.         case OpStop:
  1370.             // Drop samples from queue.
  1371.             hr = ProcessSamplesFromQueue(DropSamples);
  1372.             // Send the event even if the previous call failed.
  1373.             hr = QueueEvent(MEStreamSinkStopped, GUID_NULL, hr, NULL);
  1374.             break;
  1375.         case OpPause:
  1376.             hr = QueueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL);
  1377.             break;
  1378.         case OpProcessSample:
  1379.         case OpPlaceMarker:
  1380.             hr = DispatchProcessSample(pOp);
  1381.             break;
  1382.         case OpFinalize:
  1383.             hr = DispatchFinalize(pOp);
  1384.             break;
  1385.         }
  1386.     }
  1387.     SAFE_RELEASE(pState);
  1388.     return hr;
  1389. }
  1390. //-------------------------------------------------------------------
  1391. // Name: DispatchProcessSample
  1392. // Description: Complete a ProcessSample or PlaceMarker request.
  1393. //-------------------------------------------------------------------
  1394. HRESULT CWavStream::DispatchProcessSample(CAsyncOperation* pOp)
  1395. {
  1396.     HRESULT hr = S_OK;
  1397.     assert(pOp != NULL);
  1398.     hr = ProcessSamplesFromQueue(WriteSamples);
  1399.     // Ask for another sample
  1400.     if (SUCCEEDED(hr))
  1401.     {
  1402.         if (pOp->m_op == OpProcessSample)
  1403.         {
  1404.             hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
  1405.         }
  1406.     }
  1407.     // We are in the middle of an asynchronous operation, so if something failed, send an error.
  1408.     if (FAILED(hr))
  1409.     {
  1410.         hr = QueueEvent(MEError, GUID_NULL, hr, NULL);
  1411.     }
  1412.     return hr;
  1413. }
  1414. //-------------------------------------------------------------------
  1415. // Name: ProcessSamplesFromQueue
  1416. // Description: 
  1417. //
  1418. // Removes all of the samples and markers that are currently in the
  1419. // queue and processes them.
  1420. //
  1421. // If bFlushData = DropSamples:
  1422. //     For each marker, send an MEStreamSinkMarker event, with hr = E_ABORT.
  1423. //     For each sample, drop the sample.
  1424. //
  1425. // If bFlushData = WriteSamples
  1426. //     For each marker, send an MEStreamSinkMarker event, with hr = S_OK.
  1427. //     For each sample, write the sample to the file.
  1428. //
  1429. // This method is called when we flush, stop, restart, receive a new
  1430. // sample, or receive a marker.
  1431. //-------------------------------------------------------------------
  1432. HRESULT CWavStream::ProcessSamplesFromQueue(FlushState bFlushData)
  1433. {
  1434.     HRESULT hr = S_OK;
  1435.     ComPtrList<IUnknown>::POSITION pos = m_SampleQueue.FrontPosition();
  1436.     // Enumerate all of the samples/markers in the queue.
  1437.     while (pos != m_SampleQueue.EndPosition())
  1438.     {
  1439.         IUnknown *pUnk = NULL;
  1440.         IMarker  *pMarker = NULL;
  1441.         IMFSample *pSample = NULL;
  1442.         hr = m_SampleQueue.GetItemPos(pos, &pUnk);
  1443.         assert(pUnk != NULL); // GetItemPos should not fail unless we reached the end of the list.
  1444.         // Figure out if this is a marker or a sample.
  1445.         if (SUCCEEDED(hr))
  1446.         {
  1447.             hr = pUnk->QueryInterface(__uuidof(IMarker), (void**)&pMarker);
  1448.             if (hr == E_NOINTERFACE)
  1449.             {
  1450.                 // If this is a sample, write it to the file.
  1451.                 hr = pUnk->QueryInterface(IID_IMFSample, (void**)&pSample);
  1452.             }
  1453.         }
  1454.         // Now handle the sample/marker appropriately.
  1455.         if (SUCCEEDED(hr))
  1456.         {
  1457.             if (pMarker)
  1458.             {
  1459.                 hr = SendMarkerEvent(pMarker, bFlushData);
  1460.             }
  1461.             else 
  1462.             {
  1463.                 assert(pSample != NULL);    // Not a marker, must be a sample
  1464.                 if (bFlushData == WriteSamples)
  1465.                 {
  1466.                     hr = WriteSampleToFile(pSample);
  1467.                 }
  1468.             }
  1469.         }
  1470.         SAFE_RELEASE(pUnk);
  1471.         SAFE_RELEASE(pMarker);
  1472.         SAFE_RELEASE(pSample);
  1473.         if (FAILED(hr))
  1474.         {
  1475.             break;
  1476.         }
  1477.         pos = m_SampleQueue.Next(pos);
  1478.     }       // while loop
  1479.     // Now clear the list.
  1480.     m_SampleQueue.Clear();
  1481.     return hr;
  1482. }
  1483. //-------------------------------------------------------------------
  1484. // Name: WriteSampleToFile
  1485. // Description: Output one media sample to the file.
  1486. //-------------------------------------------------------------------
  1487. HRESULT CWavStream::WriteSampleToFile(IMFSample *pSample)
  1488. {
  1489.     HRESULT hr = S_OK;
  1490.     LONGLONG time = 0;
  1491.     DWORD cBufferCount = 0; // Number of buffers in the sample.
  1492.     BYTE *pData = NULL;
  1493.     DWORD cbData = 0;
  1494.     DWORD cbWritten = 0;
  1495.     // Get the time stamp
  1496.     hr = pSample->GetSampleTime(&time);
  1497.     if (SUCCEEDED(hr))
  1498.     {
  1499.         // If the time stamp is too early, just discard this sample.
  1500.         if (time < m_StartTime)
  1501.         {
  1502.             return S_OK;
  1503.         }
  1504.     }
  1505.     // Note: If there is no time stamp on the sample, proceed anyway.
  1506.     // Find how many buffers are in this sample.
  1507.     hr = pSample->GetBufferCount(&cBufferCount);
  1508.     if (SUCCEEDED(hr))
  1509.     {
  1510.         // Loop through all the buffers in the sample.
  1511.         for (DWORD iBuffer = 0; iBuffer < cBufferCount; iBuffer++)
  1512.         {
  1513.             IMFMediaBuffer *pBuffer = NULL;
  1514.             hr = pSample->GetBufferByIndex(iBuffer, &pBuffer);
  1515.             // Lock the buffer and write the data to the file.
  1516.             if (SUCCEEDED(hr))
  1517.             {
  1518.                 hr = pBuffer->Lock(&pData, NULL, &cbData);
  1519.             }
  1520.             if (SUCCEEDED(hr))
  1521.             {
  1522.                 hr = m_pByteStream->Write(pData, cbData, &cbWritten);
  1523.                 pBuffer->Unlock();
  1524. m_pSink->CallBackFromAudioSample(pData, (ULONG)cbWritten);
  1525.             }
  1526.             // Update the running tally of bytes written.
  1527.             if (SUCCEEDED(hr))
  1528.             {
  1529.                 m_cbDataWritten += cbData;
  1530.             }
  1531.             SAFE_RELEASE(pBuffer);
  1532.             if (FAILED(hr))
  1533.             {
  1534.                 break;
  1535.             }
  1536.         }   // for loop
  1537.     }
  1538.     return hr;
  1539. }
  1540. //-------------------------------------------------------------------
  1541. // Name: SendMarkerEvent
  1542. // Description: Saned a marker event.
  1543. // 
  1544. // pMarker: Pointer to our custom IMarker interface, which holds
  1545. //          the marker information.
  1546. //-------------------------------------------------------------------
  1547. HRESULT CWavStream::SendMarkerEvent(IMarker *pMarker, FlushState FlushState)
  1548. {
  1549.     HRESULT hr = S_OK;
  1550.     HRESULT hrStatus = S_OK;  // Status code for marker event.
  1551.     
  1552.     if (FlushState == DropSamples)
  1553.     {
  1554.         hrStatus = E_ABORT; 
  1555.     }
  1556.     PROPVARIANT var;
  1557.     PropVariantInit(&var);
  1558.     // Get the context data.
  1559.     hr = pMarker->GetContext(&var);
  1560.     if (SUCCEEDED(hr))
  1561.     {
  1562.         hr = QueueEvent(MEStreamSinkMarker, GUID_NULL, hrStatus, &var);
  1563.     }
  1564.     PropVariantClear(&var);
  1565.     return hr;
  1566. }
  1567. //-------------------------------------------------------------------
  1568. // Name: DispatchFinalize
  1569. // Description: Complete a BeginFinalize request.
  1570. //-------------------------------------------------------------------
  1571. HRESULT CWavStream::DispatchFinalize(CAsyncOperation* pOp)
  1572. {
  1573.     HRESULT hr = S_OK; 
  1574.     WAVEFORMATEX *pWav = NULL;
  1575.     UINT cbSize = 0;
  1576.     DWORD cbWritten = 0;
  1577.     WAV_FILE_HEADER header;
  1578.     ZeroMemory(&header, sizeof(header));
  1579.     DWORD cbFileSize = m_cbDataWritten + sizeof(WAV_FILE_HEADER) - sizeof(RIFFCHUNK);
  1580.     // Write any samples left in the queue...
  1581.     hr = ProcessSamplesFromQueue(WriteSamples);
  1582.     // Now we're done writing all of the audio data. 
  1583.     // Fill in the RIFF headers...
  1584.     if (SUCCEEDED(hr))
  1585.     {
  1586.         hr = MFCreateWaveFormatExFromMFMediaType(m_pCurrentType, &pWav, &cbSize);
  1587.     }
  1588.     if (SUCCEEDED(hr))
  1589.     {
  1590.         header.FileHeader.fcc = MAKEFOURCC('R', 'I', 'F', 'F');
  1591.         header.FileHeader.cb = cbFileSize;
  1592.         header.fccWaveType = MAKEFOURCC('W', 'A', 'V', 'E');
  1593.         header.WaveHeader.fcc = MAKEFOURCC('f', 'm', 't', ' ');
  1594.         header.WaveHeader.cb = RIFFROUND(sizeof(WAVEFORMATEX));
  1595.    
  1596.         CopyMemory(&header.WaveFormat, pWav, sizeof(WAVEFORMATEX));
  1597.         header.DataHeader.fcc = MAKEFOURCC('d', 'a', 't', 'a');
  1598.         header.DataHeader.cb = m_cbDataWritten;
  1599.     }
  1600.     
  1601.     // Move the file pointer back to the start of the file and write the
  1602.     // RIFF headers.
  1603.     if (SUCCEEDED(hr))
  1604.     {
  1605.         hr = m_pByteStream->SetCurrentPosition(0);
  1606.     }
  1607.     if (SUCCEEDED(hr))
  1608.     {
  1609.         hr = m_pByteStream->Write((BYTE*)&header, sizeof(WAV_FILE_HEADER), &cbWritten);
  1610.     }
  1611.     // Close the byte stream.
  1612.     if (SUCCEEDED(hr))
  1613.     {
  1614.         hr = m_pByteStream->Close();
  1615.     }
  1616.     // Set the async status and invoke the callback.
  1617.     m_pFinalizeResult->SetStatus(hr);
  1618.     hr = MFInvokeCallback(m_pFinalizeResult);
  1619.     CoTaskMemFree(pWav);
  1620.     return hr;
  1621. }
  1622. //////////////////////
  1623. // CMarker class
  1624. // Holds information from IMFStreamSink::PlaceMarker
  1625. // 
  1626. CMarker::CMarker(MFSTREAMSINK_MARKER_TYPE eMarkerType) : m_nRefCount(1), m_eMarkerType(eMarkerType)
  1627. {
  1628.     PropVariantInit(&m_varMarkerValue);
  1629.     PropVariantInit(&m_varContextValue);
  1630. }
  1631. CMarker::~CMarker()
  1632. {
  1633.     assert(m_nRefCount == 0);
  1634.     PropVariantClear(&m_varMarkerValue);
  1635.     PropVariantClear(&m_varContextValue);
  1636. }
  1637. /* static */ 
  1638. HRESULT CMarker::Create(
  1639.     MFSTREAMSINK_MARKER_TYPE eMarkerType,
  1640.     const PROPVARIANT* pvarMarkerValue,     // Can be NULL.
  1641.     const PROPVARIANT* pvarContextValue,    // Can be NULL.
  1642.     IMarker **ppMarker
  1643.     )
  1644. {
  1645.     if (ppMarker == NULL)
  1646.     {
  1647.         return E_POINTER;
  1648.     }
  1649.     HRESULT hr = S_OK;
  1650.     CMarker *pMarker = new CMarker(eMarkerType);
  1651.     if (pMarker == NULL)
  1652.     {
  1653.         hr = E_OUTOFMEMORY;
  1654.     }
  1655.     // Copy the marker data.
  1656.     if (SUCCEEDED(hr))
  1657.     {
  1658.         if (pvarMarkerValue)
  1659.         {
  1660.             hr = PropVariantCopy(&pMarker->m_varMarkerValue, pvarMarkerValue);
  1661.         }
  1662.     }
  1663.     if (SUCCEEDED(hr))
  1664.     {
  1665.         if (pvarContextValue)
  1666.         {
  1667.             hr = PropVariantCopy(&pMarker->m_varContextValue, pvarContextValue);
  1668.         }
  1669.     }
  1670.     if (SUCCEEDED(hr))
  1671.     {
  1672.         *ppMarker = pMarker;
  1673.         (*ppMarker)->AddRef();
  1674.     }
  1675.     SAFE_RELEASE(pMarker);
  1676.     return hr;
  1677. }
  1678. // IUnknown methods.
  1679. ULONG CMarker::AddRef()
  1680. {
  1681.     return InterlockedIncrement(&m_nRefCount);
  1682. }
  1683. ULONG CMarker::Release()
  1684. {
  1685.     ULONG uCount = InterlockedDecrement(&m_nRefCount);
  1686.     if (uCount == 0)
  1687.     {
  1688.         delete this;
  1689.     }
  1690.     // For thread safety, return a temporary variable.
  1691.     return uCount;
  1692. }
  1693. HRESULT CMarker::QueryInterface(REFIID iid, void** ppv)
  1694. {
  1695.     if (!ppv)
  1696.     {
  1697.         return E_POINTER;
  1698.     }
  1699.     if (iid == IID_IUnknown)
  1700.     {
  1701.         *ppv = static_cast<IUnknown*>(this);
  1702.     }
  1703.     else if (iid == __uuidof(IMarker))
  1704.     {
  1705.         *ppv = static_cast<IMarker*>(this);
  1706.     }
  1707.     else
  1708.     {
  1709.         *ppv = NULL;
  1710.         return E_NOINTERFACE;
  1711.     }
  1712.     AddRef();
  1713.     return S_OK;
  1714. }
  1715. // IMarker methods
  1716. HRESULT CMarker::GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType)
  1717. {
  1718.     if (pType == NULL)
  1719.     {
  1720.         return E_POINTER;
  1721.     }
  1722.     *pType = m_eMarkerType;
  1723.     return S_OK;
  1724. }
  1725. HRESULT CMarker::GetMarkerValue(PROPVARIANT *pvar)
  1726. {
  1727.     if (pvar == NULL)
  1728.     {
  1729.         return E_POINTER;
  1730.     }
  1731.     return PropVariantCopy(pvar, &m_varMarkerValue);
  1732. }
  1733. HRESULT CMarker::GetContext(PROPVARIANT *pvar)
  1734. {
  1735.     if (pvar == NULL)
  1736.     {
  1737.         return E_POINTER;
  1738.     }
  1739.     return PropVariantCopy(pvar, &m_varContextValue);
  1740. }
  1741. //-------------------------------------------------------------------
  1742. // CreatePCMAudioType
  1743. //
  1744. // Creates a media type that describes an uncompressed PCM audio
  1745. // format.
  1746. //-------------------------------------------------------------------
  1747. HRESULT CreatePCMAudioType(
  1748.     UINT32 sampleRate,        // Samples per second
  1749.     UINT32 bitsPerSample,     // Bits per sample
  1750.     UINT32 cChannels,         // Number of channels
  1751.     IMFMediaType **ppType     // Receives a pointer to the media type.
  1752.     )
  1753. {
  1754.     HRESULT hr = S_OK;
  1755.     IMFMediaType *pType = NULL;
  1756.     // Calculate derived values.
  1757.     UINT32 blockAlign = cChannels * (bitsPerSample / 8);
  1758.     UINT32 bytesPerSecond = blockAlign * sampleRate;
  1759.     // Create the empty media type.
  1760.     hr = MFCreateMediaType(&pType);
  1761.     // Set attributes on the type.
  1762.     if (SUCCEEDED(hr))
  1763.     {
  1764.         hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
  1765.     }
  1766.     if (SUCCEEDED(hr))
  1767.     {
  1768.         hr = pType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
  1769.     }
  1770.     if (SUCCEEDED(hr))
  1771.     {
  1772.         hr = pType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, cChannels);
  1773.     }
  1774.     if (SUCCEEDED(hr))
  1775.     {
  1776.         hr = pType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate);
  1777.     }
  1778.     if (SUCCEEDED(hr))
  1779.     {
  1780.         hr = pType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, blockAlign);
  1781.     }
  1782.     if (SUCCEEDED(hr))
  1783.     {
  1784.         hr = pType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerSecond);
  1785.     }
  1786.     if (SUCCEEDED(hr))
  1787.     {
  1788.         hr = pType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample);
  1789.     }
  1790.     if (SUCCEEDED(hr))
  1791.     {
  1792.         hr = pType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
  1793.     }
  1794.     if (SUCCEEDED(hr))
  1795.     {
  1796.         // Return the type to the caller.
  1797.         *ppType = pType;
  1798.         (*ppType)->AddRef();
  1799.     }
  1800.     SAFE_RELEASE(pType);
  1801.     return hr;
  1802. }
  1803. //-------------------------------------------------------------------
  1804. // Name: ValidateWaveFormat
  1805. // Description: Validates a WAVEFORMATEX structure. 
  1806. //
  1807. // Just to keep the sample as simple as possible, we only accept 
  1808. // uncompressed PCM formats.
  1809. //-------------------------------------------------------------------
  1810. HRESULT ValidateWaveFormat(const WAVEFORMATEX *pWav, DWORD cbSize)
  1811. {
  1812.     if (pWav->wFormatTag != WAVE_FORMAT_PCM)
  1813.     {
  1814.         return MF_E_INVALIDMEDIATYPE;
  1815.     }
  1816.     if (pWav->nChannels != 1 && pWav->nChannels != 2)
  1817.     {
  1818.         return MF_E_INVALIDMEDIATYPE;
  1819.     }
  1820.     if (pWav->wBitsPerSample != 8 && pWav->wBitsPerSample != 16)
  1821.     {
  1822.         return MF_E_INVALIDMEDIATYPE;
  1823.     }
  1824.     if (pWav->cbSize != 0)
  1825.     {
  1826.         return MF_E_INVALIDMEDIATYPE;
  1827.     }
  1828.     // Make sure block alignment was calculated correctly.
  1829.     if (pWav->nBlockAlign != pWav->nChannels * (pWav->wBitsPerSample / 8))
  1830.     {   
  1831.         return MF_E_INVALIDMEDIATYPE;
  1832.     }
  1833.     // Check possible overflow...
  1834.     if (pWav->nSamplesPerSec  > (DWORD)(MAXDWORD / pWav->nBlockAlign))        // Is (nSamplesPerSec * nBlockAlign > MAXDWORD) ?
  1835.     {
  1836.         return MF_E_INVALIDMEDIATYPE;
  1837.     }
  1838.     // Make sure average bytes per second was calculated correctly.
  1839.     if (pWav->nAvgBytesPerSec != pWav->nSamplesPerSec * pWav->nBlockAlign)
  1840.     {
  1841.         return MF_E_INVALIDMEDIATYPE;
  1842.     }
  1843.     // Everything checked out.
  1844.     return S_OK;
  1845. }
  1846. #pragma warning( pop )