home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / mac / SiteBldr / AMOVIE / SDK / _SETUP / COMMON.Z / pullpin.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-18  |  11.1 KB  |  522 lines

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