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