home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / BaseClasses / pullpin.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  11.5 KB  |  511 lines

  1. //------------------------------------------------------------------------------
  2. // File: PullPin.cpp
  3. //
  4. // Desc: DirectShow base classes - implements CPullPin class that pulls data
  5. //       from IAsyncReader.
  6. //
  7. // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
  8. //------------------------------------------------------------------------------
  9.  
  10.  
  11. #include <streams.h>
  12. #include "pullpin.h"
  13.  
  14.  
  15.  
  16. CPullPin::CPullPin()
  17.   : m_pReader(NULL),
  18.     m_pAlloc(NULL),
  19.     m_State(TM_Exit) {
  20.  
  21. }
  22.  
  23. CPullPin::~CPullPin() {
  24.     Disconnect();
  25. }
  26.  
  27. // returns S_OK if successfully connected to an IAsyncReader interface
  28. // from this object
  29. // Optional allocator should be proposed as a preferred allocator if
  30. // necessary
  31. HRESULT
  32. CPullPin::Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync) {
  33.     CAutoLock lock(&m_AccessLock);
  34.  
  35.     if(m_pReader) {
  36.         return VFW_E_ALREADY_CONNECTED;
  37.     }
  38.  
  39.     HRESULT hr = pUnk->QueryInterface(IID_IAsyncReader, (void**)&m_pReader);
  40.     if(FAILED(hr)) {
  41.         return(hr);
  42.     }
  43.  
  44.     hr = DecideAllocator(pAlloc, NULL);
  45.     if(FAILED(hr)) {
  46.         Disconnect();
  47.         return hr;
  48.     }
  49.  
  50.     LONGLONG llTotal, llAvail;
  51.     hr = m_pReader->Length(&llTotal, &llAvail);
  52.     if(FAILED(hr)) {
  53.         Disconnect();
  54.         return hr;
  55.     }
  56.  
  57.     // convert from file position to reference time
  58.     m_tDuration = llTotal * UNITS;
  59.     m_tStop = m_tDuration;
  60.     m_tStart = 0;
  61.     m_bSync = bSync;
  62.  
  63.     return S_OK;
  64. }
  65.  
  66. // disconnect any connection made in Connect
  67. HRESULT
  68. CPullPin::Disconnect() {
  69.     CAutoLock lock(&m_AccessLock);
  70.  
  71.     StopThread();
  72.  
  73.     if(m_pReader) {
  74.         m_pReader->Release();
  75.         m_pReader = NULL;
  76.     }
  77.  
  78.     if(m_pAlloc) {
  79.         m_pAlloc->Release();
  80.         m_pAlloc = NULL;
  81.     }
  82.  
  83.     return S_OK;
  84. }
  85.  
  86. // agree an allocator using RequestAllocator - optional
  87. // props param specifies your requirements (non-zero fields).
  88. // returns an error code if fail to match requirements.
  89. // optional IMemAllocator interface is offered as a preferred allocator
  90. // but no error occurs if it can't be met.
  91. HRESULT
  92. CPullPin::DecideAllocator(
  93.     IMemAllocator * pAlloc,
  94.     ALLOCATOR_PROPERTIES * pProps) {
  95.     ALLOCATOR_PROPERTIES *pRequest;
  96.     ALLOCATOR_PROPERTIES Request;
  97.     if(pProps == NULL) {
  98.         Request.cBuffers = 3;
  99.         Request.cbBuffer = 64*1024;
  100.         Request.cbAlign = 0;
  101.         Request.cbPrefix = 0;
  102.         pRequest = &Request;
  103.     }
  104.     else {
  105.         pRequest = pProps;
  106.     }
  107.     HRESULT hr = m_pReader->RequestAllocator(pAlloc,
  108.         pRequest,
  109.         &m_pAlloc);
  110.     return hr;
  111. }
  112.  
  113. // start pulling data
  114. HRESULT
  115. CPullPin::Active(void) {
  116.     ASSERT(!ThreadExists());
  117.     return StartThread();
  118. }
  119.  
  120. // stop pulling data
  121. HRESULT
  122. CPullPin::Inactive(void) {
  123.     StopThread();
  124.  
  125.     return S_OK;
  126. }
  127.  
  128. HRESULT
  129. CPullPin::Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop) {
  130.     CAutoLock lock(&m_AccessLock);
  131.  
  132.     ThreadMsg AtStart = m_State;
  133.  
  134.     if(AtStart == TM_Start) {
  135.         BeginFlush();
  136.         PauseThread();
  137.         EndFlush();
  138.     }
  139.  
  140.     m_tStart = tStart;
  141.     m_tStop = tStop;
  142.  
  143.     HRESULT hr = S_OK;
  144.     if(AtStart == TM_Start) {
  145.         hr = StartThread();
  146.     }
  147.  
  148.     return hr;
  149. }
  150.  
  151. HRESULT
  152. CPullPin::Duration(REFERENCE_TIME* ptDuration) {
  153.     *ptDuration = m_tDuration;
  154.     return S_OK;
  155. }
  156.  
  157.  
  158. HRESULT
  159. CPullPin::StartThread() {
  160.     CAutoLock lock(&m_AccessLock);
  161.  
  162.     if(!m_pAlloc || !m_pReader) {
  163.         return E_UNEXPECTED;
  164.     }
  165.  
  166.     HRESULT hr;
  167.     if(!ThreadExists()) {
  168.  
  169.         // commit allocator
  170.         hr = m_pAlloc->Commit();
  171.         if(FAILED(hr)) {
  172.             return hr;
  173.         }
  174.  
  175.         // start thread
  176.         if(!Create()) {
  177.             return E_FAIL;
  178.         }
  179.     }
  180.  
  181.     m_State = TM_Start;
  182.     hr = (HRESULT) CallWorker(m_State);
  183.     return hr;
  184. }
  185.  
  186. HRESULT
  187. CPullPin::PauseThread() {
  188.     CAutoLock lock(&m_AccessLock);
  189.  
  190.     if(!ThreadExists()) {
  191.         return E_UNEXPECTED;
  192.     }
  193.  
  194.     // need to flush to ensure the thread is not blocked
  195.     // in WaitForNext
  196.     HRESULT hr = m_pReader->BeginFlush();
  197.     if(FAILED(hr)) {
  198.         return hr;
  199.     }
  200.  
  201.     m_State = TM_Pause;
  202.     hr = CallWorker(TM_Pause);
  203.  
  204.     m_pReader->EndFlush();
  205.     return hr;
  206. }
  207.  
  208. HRESULT
  209. CPullPin::StopThread() {
  210.     CAutoLock lock(&m_AccessLock);
  211.  
  212.     if(!ThreadExists()) {
  213.         return S_FALSE;
  214.     }
  215.  
  216.     // need to flush to ensure the thread is not blocked
  217.     // in WaitForNext
  218.     HRESULT hr = m_pReader->BeginFlush();
  219.     if(FAILED(hr)) {
  220.         return hr;
  221.     }
  222.  
  223.     m_State = TM_Exit;
  224.     hr = CallWorker(TM_Exit);
  225.  
  226.     m_pReader->EndFlush();
  227.  
  228.     // wait for thread to completely exit
  229.     Close();
  230.  
  231.     // decommit allocator
  232.     if(m_pAlloc) {
  233.         m_pAlloc->Decommit();
  234.     }
  235.  
  236.     return S_OK;
  237. }
  238.  
  239.  
  240. DWORD
  241. CPullPin::ThreadProc(void) {
  242.     while(1) {
  243.         DWORD cmd = GetRequest();
  244.         switch(cmd) {
  245.             case TM_Exit:
  246.                 Reply(S_OK);
  247.                 return 0;
  248.  
  249.             case TM_Pause:
  250.                 // we are paused already
  251.                 Reply(S_OK);
  252.                 break;
  253.  
  254.             case TM_Start:
  255.                 Reply(S_OK);
  256.                 Process();
  257.                 break;
  258.         }
  259.  
  260.         // at this point, there should be no outstanding requests on the
  261.         // upstream filter.
  262.         // We should force begin/endflush to ensure that this is true.
  263.         // !!!Note that we may currently be inside a BeginFlush/EndFlush pair
  264.         // on another thread, but the premature EndFlush will do no harm now
  265.         // that we are idle.
  266.         m_pReader->BeginFlush();
  267.         CleanupCancelled();
  268.         m_pReader->EndFlush();
  269.     }
  270. }
  271.  
  272. HRESULT
  273. CPullPin::QueueSample(
  274.     REFERENCE_TIME& tCurrent,
  275.     REFERENCE_TIME tAlignStop,
  276.     BOOL bDiscontinuity
  277.     ) {
  278.     IMediaSample* pSample;
  279.  
  280.     HRESULT hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);
  281.     if(FAILED(hr)) {
  282.         return hr;
  283.     }
  284.  
  285.     LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);
  286.     if(tStopThis > tAlignStop) {
  287.         tStopThis = tAlignStop;
  288.     }
  289.     pSample->SetTime(&tCurrent, &tStopThis);
  290.     tCurrent = tStopThis;
  291.  
  292.     pSample->SetDiscontinuity(bDiscontinuity);
  293.  
  294.     hr = m_pReader->Request(pSample,
  295.         0);
  296.     if(FAILED(hr)) {
  297.         pSample->Release();
  298.  
  299.         CleanupCancelled();
  300.         OnError(hr);
  301.     }
  302.     return hr;
  303. }
  304.  
  305. HRESULT
  306. CPullPin::CollectAndDeliver(
  307.     REFERENCE_TIME tStart,
  308.     REFERENCE_TIME tStop) {
  309.     IMediaSample* pSample = NULL;   // better be sure pSample is set
  310.     DWORD_PTR dwUnused;
  311.     HRESULT hr = m_pReader->WaitForNext(INFINITE,
  312.         &pSample,
  313.         &dwUnused);
  314.     if(FAILED(hr)) {
  315.         if(pSample) {
  316.             pSample->Release();
  317.         }
  318.     }
  319.     else {
  320.         hr = DeliverSample(pSample, tStart, tStop);
  321.     }
  322.     if(FAILED(hr)) {
  323.         CleanupCancelled();
  324.         OnError(hr);
  325.     }
  326.     return hr;
  327.  
  328. }
  329.  
  330. HRESULT
  331. CPullPin::DeliverSample(
  332.     IMediaSample* pSample,
  333.     REFERENCE_TIME tStart,
  334.     REFERENCE_TIME tStop
  335.     ) {
  336.     // fix up sample if past actual stop (for sector alignment)
  337.     REFERENCE_TIME t1, t2;
  338.     pSample->GetTime(&t1, &t2);
  339.     if(t2 > tStop) {
  340.         t2 = tStop;
  341.     }
  342.  
  343.     // adjust times to be relative to (aligned) start time
  344.     t1 -= tStart;
  345.     t2 -= tStart;
  346.     pSample->SetTime(&t1, &t2);
  347.  
  348.  
  349.     HRESULT hr = Receive(pSample);
  350.     pSample->Release();
  351.     return hr;
  352. }
  353.  
  354. void
  355. CPullPin::Process(void) {
  356.     // is there anything to do?
  357.     if(m_tStop <= m_tStart) {
  358.         EndOfStream();
  359.         return;
  360.     }
  361.  
  362.     BOOL bDiscontinuity = TRUE;
  363.  
  364.     // if there is more than one sample at the allocator,
  365.     // then try to queue 2 at once in order to overlap.
  366.     // -- get buffer count and required alignment
  367.     ALLOCATOR_PROPERTIES Actual;
  368.     HRESULT hr = m_pAlloc->GetProperties(&Actual);
  369.  
  370.     // align the start position downwards
  371.     REFERENCE_TIME tStart = AlignDown(m_tStart / UNITS, Actual.cbAlign) * UNITS;
  372.     REFERENCE_TIME tCurrent = tStart;
  373.  
  374.     REFERENCE_TIME tStop = m_tStop;
  375.     if(tStop > m_tDuration) {
  376.         tStop = m_tDuration;
  377.     }
  378.  
  379.     // align the stop position - may be past stop, but that
  380.     // doesn't matter
  381.     REFERENCE_TIME tAlignStop = AlignUp(tStop / UNITS, Actual.cbAlign) * UNITS;
  382.  
  383.  
  384.     DWORD dwRequest;
  385.  
  386.     if(!m_bSync) {
  387.  
  388.         //  Break out of the loop either if we get to the end or we're asked
  389.         //  to do something else
  390.         while(tCurrent < tAlignStop) {
  391.  
  392.             // Break out without calling EndOfStream if we're asked to
  393.             // do something different
  394.             if(CheckRequest(&dwRequest)) {
  395.                 return;
  396.             }
  397.  
  398.             // queue a first sample
  399.             if(Actual.cBuffers > 1) {
  400.  
  401.                 hr = QueueSample(tCurrent, tAlignStop, TRUE);
  402.                 bDiscontinuity = FALSE;
  403.  
  404.                 if(FAILED(hr)) {
  405.                     return;
  406.                 }
  407.             }
  408.  
  409.  
  410.  
  411.             // loop queueing second and waiting for first..
  412.             while(tCurrent < tAlignStop) {
  413.  
  414.                 hr = QueueSample(tCurrent, tAlignStop, bDiscontinuity);
  415.                 bDiscontinuity = FALSE;
  416.  
  417.                 if(FAILED(hr)) {
  418.                     return;
  419.                 }
  420.  
  421.                 hr = CollectAndDeliver(tStart, tStop);
  422.                 if(S_OK != hr) {
  423.  
  424.                     // stop if error, or if downstream filter said
  425.                     // to stop.
  426.                     return;
  427.                 }
  428.             }
  429.  
  430.             if(Actual.cBuffers > 1) {
  431.                 hr = CollectAndDeliver(tStart, tStop);
  432.                 if(FAILED(hr)) {
  433.                     return;
  434.                 }
  435.             }
  436.         }
  437.     }
  438.     else {
  439.  
  440.         // sync version of above loop
  441.         while(tCurrent < tAlignStop) {
  442.  
  443.             // Break out without calling EndOfStream if we're asked to
  444.             // do something different
  445.             if(CheckRequest(&dwRequest)) {
  446.                 return;
  447.             }
  448.  
  449.             IMediaSample* pSample;
  450.  
  451.             hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);
  452.             if(FAILED(hr)) {
  453.                 OnError(hr);
  454.                 return;
  455.             }
  456.  
  457.             LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);
  458.             if(tStopThis > tAlignStop) {
  459.                 tStopThis = tAlignStop;
  460.             }
  461.             pSample->SetTime(&tCurrent, &tStopThis);
  462.             tCurrent = tStopThis;
  463.  
  464.             if(bDiscontinuity) {
  465.                 pSample->SetDiscontinuity(TRUE);
  466.                 bDiscontinuity = FALSE;
  467.             }
  468.  
  469.             hr = m_pReader->SyncReadAligned(pSample);
  470.  
  471.             if(FAILED(hr)) {
  472.                 pSample->Release();
  473.                 OnError(hr);
  474.                 return;
  475.             }
  476.  
  477.             hr = DeliverSample(pSample, tStart, tStop);
  478.             if(hr != S_OK) {
  479.                 if(FAILED(hr)) {
  480.                     OnError(hr);
  481.                 }
  482.                 return;
  483.             }
  484.         }
  485.     }
  486.  
  487.     EndOfStream();
  488. }
  489.  
  490. // after a flush, cancelled i/o will be waiting for collection
  491. // and release
  492. void
  493. CPullPin::CleanupCancelled(void) {
  494.     while(1) {
  495.         IMediaSample * pSample;
  496.         DWORD_PTR dwUnused;
  497.  
  498.         HRESULT hr = m_pReader->WaitForNext(0,          // no wait
  499.             &pSample,
  500.             &dwUnused);
  501.         if(pSample) {
  502.             pSample->Release();
  503.         }
  504.         else {
  505.             // no more samples
  506.             return;
  507.         }
  508.     }
  509. }
  510.  
  511.