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

  1. //------------------------------------------------------------------------------
  2. // File: Vtrans.cpp
  3. //
  4. // Desc: DirectShow base classes.
  5. //
  6. // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9.  
  10. #include <streams.h>
  11. #include <measure.h>
  12. // #include <vtransfr.h>         // now in precomp file streams.h
  13.  
  14. CVideoTransformFilter::CVideoTransformFilter
  15.     ( TCHAR *pName, LPUNKNOWN pUnk, REFCLSID clsid)
  16.     : CTransformFilter(pName, pUnk, clsid)
  17.     , m_itrLate(0)
  18.     , m_nKeyFramePeriod(0)      // No QM until we see at least 2 key frames
  19.     , m_nFramesSinceKeyFrame(0)
  20.     , m_bSkipping(FALSE)
  21.     , m_tDecodeStart(0)
  22.     , m_itrAvgDecode(300000)    // 30mSec - probably allows skipping
  23.     , m_bQualityChanged(FALSE) {
  24. #ifdef PERF
  25.     RegisterPerfId();
  26. #endif //  PERF
  27. }
  28.  
  29.  
  30. CVideoTransformFilter::~CVideoTransformFilter() {
  31.     // nothing to do
  32. }
  33.  
  34.  
  35. // Reset our quality management state
  36.  
  37. HRESULT CVideoTransformFilter::StartStreaming() {
  38.     m_itrLate = 0;
  39.     m_nKeyFramePeriod = 0;       // No QM until we see at least 2 key frames
  40.     m_nFramesSinceKeyFrame = 0;
  41.     m_bSkipping = FALSE;
  42.     m_tDecodeStart = 0;
  43.     m_itrAvgDecode = 300000;     // 30mSec - probably allows skipping
  44.     m_bQualityChanged = FALSE;
  45.     m_bSampleSkipped = FALSE;
  46.     return NOERROR;
  47. }
  48.  
  49.  
  50. // Overriden to reset quality management information
  51.  
  52. HRESULT CVideoTransformFilter::EndFlush() { {
  53.         //  Synchronize
  54.         CAutoLock lck(&m_csReceive);
  55.  
  56.         // Reset our stats
  57.         //
  58.         // Note - we don't want to call derived classes here,
  59.         // we only want to reset our internal variables and this
  60.         // is a convenient way to do it
  61.         CVideoTransformFilter::StartStreaming();
  62.     }
  63.     return CTransformFilter::EndFlush();
  64. }
  65.  
  66.  
  67. HRESULT CVideoTransformFilter::AbortPlayback(HRESULT hr) {
  68.     NotifyEvent(EC_ERRORABORT, hr, 0);
  69.     m_pOutput->DeliverEndOfStream();
  70.     return hr;
  71. }
  72.  
  73.  
  74. // Receive()
  75. //
  76. // Accept a sample from upstream, decide whether to process it
  77. // or drop it.  If we process it then get a buffer from the
  78. // allocator of the downstream connection, transform it into the
  79. // new buffer and deliver it to the downstream filter.
  80. // If we decide not to process it then we do not get a buffer.
  81.  
  82. // Remember that although this code will notice format changes coming into
  83. // the input pin, it will NOT change its output format if that results
  84. // in the filter needing to make a corresponding output format change.  Your
  85. // derived filter will have to take care of that.  (eg. a palette change if
  86. // the input and output is an 8 bit format).  If the input sample is discarded
  87. // and nothing is sent out for this Receive, please remember to put the format
  88. // change on the first output sample that you actually do send.
  89. // If your filter will produce the same output type even when the input type
  90. // changes, then this base class code will do everything you need.
  91.  
  92. HRESULT CVideoTransformFilter::Receive(IMediaSample *pSample) {
  93.     // If the next filter downstream is the video renderer, then it may
  94.     // be able to operate in DirectDraw mode which saves copying the data
  95.     // and gives higher performance.  In that case the buffer which we
  96.     // get from GetDeliveryBuffer will be a DirectDraw buffer, and
  97.     // drawing into this buffer draws directly onto the display surface.
  98.     // This means that any waiting for the correct time to draw occurs
  99.     // during GetDeliveryBuffer, and that once the buffer is given to us
  100.     // the video renderer will count it in its statistics as a frame drawn.
  101.     // This means that any decision to drop the frame must be taken before
  102.     // calling GetDeliveryBuffer.
  103.  
  104.     ASSERT(CritCheckIn(&m_csReceive));
  105.     AM_MEDIA_TYPE *pmtOut, *pmt;
  106. #ifdef DEBUG
  107.     FOURCCMap fccOut;
  108. #endif
  109.     HRESULT hr;
  110.     ASSERT(pSample);
  111.     IMediaSample * pOutSample;
  112.  
  113.     // If no output pin to deliver to then no point sending us data
  114.     ASSERT(m_pOutput != NULL) ;
  115.  
  116.     // The source filter may dynamically ask us to start transforming from a
  117.     // different media type than the one we're using now.  If we don't, we'll
  118.     // draw garbage. (typically, this is a palette change in the movie,
  119.     // but could be something more sinister like the compression type changing,
  120.     // or even the video size changing)
  121.  
  122. #define rcS1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcSource
  123. #define rcT1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcTarget
  124.  
  125.     pSample->GetMediaType(&pmt);
  126.     if(pmt != NULL && pmt->pbFormat != NULL) {
  127.  
  128.         // spew some debug output
  129.         ASSERT(!IsEqualGUID(pmt->majortype, GUID_NULL));
  130. #ifdef DEBUG
  131.         fccOut.SetFOURCC(&pmt->subtype);
  132.         LONG lCompression = HEADER(pmt->pbFormat)->biCompression;
  133.         LONG lBitCount = HEADER(pmt->pbFormat)->biBitCount;
  134.         LONG lStride = (HEADER(pmt->pbFormat)->biWidth * lBitCount + 7) / 8;
  135.         lStride = (lStride + 3) & ~3;
  136.         DbgLog((LOG_TRACE,3,TEXT("*Changing input type on the fly to")));
  137.         DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),
  138.             fccOut.GetFOURCC(), lCompression, lBitCount));
  139.         DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),
  140.             HEADER(pmt->pbFormat)->biHeight,
  141.             rcT1.left, rcT1.top, rcT1.right, rcT1.bottom));
  142.         DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),
  143.             rcS1.left, rcS1.top, rcS1.right, rcS1.bottom,
  144.             lStride));
  145. #endif
  146.  
  147.         // now switch to using the new format.  I am assuming that the
  148.         // derived filter will do the right thing when its media type is
  149.         // switched and streaming is restarted.
  150.  
  151.         StopStreaming();
  152.         m_pInput->CurrentMediaType() = *pmt;
  153.         DeleteMediaType(pmt);
  154.         // if this fails, playback will stop, so signal an error
  155.         hr = StartStreaming();
  156.         if(FAILED(hr)) {
  157.             return AbortPlayback(hr);
  158.         }
  159.     }
  160.  
  161.     // Now that we have noticed any format changes on the input sample, it's
  162.     // OK to discard it.
  163.  
  164.     if(ShouldSkipFrame(pSample)) {
  165.         MSR_NOTE(m_idSkip);
  166.         m_bSampleSkipped = TRUE;
  167.         return NOERROR;
  168.     }
  169.  
  170.     // Set up the output sample
  171.     hr = InitializeOutputSample(pSample, &pOutSample);
  172.  
  173.     if(FAILED(hr)) {
  174.         return hr;
  175.     }
  176.  
  177.     m_bSampleSkipped = FALSE;
  178.  
  179.     // The renderer may ask us to on-the-fly to start transforming to a
  180.     // different format.  If we don't obey it, we'll draw garbage
  181.  
  182. #define rcS ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcSource
  183. #define rcT ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcTarget
  184.  
  185.     pOutSample->GetMediaType(&pmtOut);
  186.     if(pmtOut != NULL && pmtOut->pbFormat != NULL) {
  187.  
  188.         // spew some debug output
  189.         ASSERT(!IsEqualGUID(pmtOut->majortype, GUID_NULL));
  190. #ifdef DEBUG
  191.         fccOut.SetFOURCC(&pmtOut->subtype);
  192.         LONG lCompression = HEADER(pmtOut->pbFormat)->biCompression;
  193.         LONG lBitCount = HEADER(pmtOut->pbFormat)->biBitCount;
  194.         LONG lStride = (HEADER(pmtOut->pbFormat)->biWidth * lBitCount + 7) / 8;
  195.         lStride = (lStride + 3) & ~3;
  196.         DbgLog((LOG_TRACE,3,TEXT("*Changing output type on the fly to")));
  197.         DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),
  198.             fccOut.GetFOURCC(), lCompression, lBitCount));
  199.         DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),
  200.             HEADER(pmtOut->pbFormat)->biHeight,
  201.             rcT.left, rcT.top, rcT.right, rcT.bottom));
  202.         DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),
  203.             rcS.left, rcS.top, rcS.right, rcS.bottom,
  204.             lStride));
  205. #endif
  206.  
  207.         // now switch to using the new format.  I am assuming that the
  208.         // derived filter will do the right thing when its media type is
  209.         // switched and streaming is restarted.
  210.  
  211.         StopStreaming();
  212.         m_pOutput->CurrentMediaType() = *pmtOut;
  213.         DeleteMediaType(pmtOut);
  214.         hr = StartStreaming();
  215.  
  216.         if(SUCCEEDED(hr)) {
  217.             // a new format, means a new empty buffer, so wait for a keyframe
  218.             // before passing anything on to the renderer.
  219.             // !!! a keyframe may never come, so give up after 30 frames
  220.             DbgLog((LOG_TRACE,3,TEXT("Output format change means we must wait for a keyframe")));
  221.             m_nWaitForKey = 30;
  222.  
  223.             // if this fails, playback will stop, so signal an error
  224.         }
  225.         else {
  226.  
  227.             //  Must release the sample before calling AbortPlayback
  228.             //  because we might be holding the win16 lock or
  229.             //  ddraw lock
  230.             pOutSample->Release();
  231.             AbortPlayback(hr);
  232.             return hr;
  233.         }
  234.     }
  235.  
  236.     // After a discontinuity, we need to wait for the next key frame
  237.     if(pSample->IsDiscontinuity() == S_OK) {
  238.         DbgLog((LOG_TRACE,3,TEXT("Non-key discontinuity - wait for keyframe")));
  239.         m_nWaitForKey = 30;
  240.     }
  241.  
  242.     // Start timing the transform (and log it if PERF is defined)
  243.  
  244.     if(SUCCEEDED(hr)) {
  245.         m_tDecodeStart = timeGetTime();
  246.         MSR_START(m_idTransform);
  247.  
  248.         // have the derived class transform the data
  249.         hr = Transform(pSample, pOutSample);
  250.  
  251.         // Stop the clock (and log it if PERF is defined)
  252.         MSR_STOP(m_idTransform);
  253.         m_tDecodeStart = timeGetTime()-m_tDecodeStart;
  254.         m_itrAvgDecode = m_tDecodeStart*(10000/16) + 15*(m_itrAvgDecode/16);
  255.  
  256.         // Maybe we're waiting for a keyframe still?
  257.         if(m_nWaitForKey)
  258.             m_nWaitForKey--;
  259.         if(m_nWaitForKey && pSample->IsSyncPoint() == S_OK)
  260.             m_nWaitForKey = FALSE;
  261.  
  262.         // if so, then we don't want to pass this on to the renderer
  263.         if(m_nWaitForKey && hr == NOERROR) {
  264.             DbgLog((LOG_TRACE,3,TEXT("still waiting for a keyframe")));
  265.             hr = S_FALSE;
  266.         }
  267.     }
  268.  
  269.     if(FAILED(hr)) {
  270.         DbgLog((LOG_TRACE,1,TEXT("Error from video transform")));
  271.     }
  272.     else {
  273.         // the Transform() function can return S_FALSE to indicate that the
  274.         // sample should not be delivered; we only deliver the sample if it's
  275.         // really S_OK (same as NOERROR, of course.)
  276.         // Try not to return S_FALSE to a direct draw buffer (it's wasteful)
  277.         // Try to take the decision earlier - before you get it.
  278.  
  279.         if(hr == NOERROR) {
  280.             hr = m_pOutput->Deliver(pOutSample);
  281.         }
  282.         else {
  283.             // S_FALSE returned from Transform is a PRIVATE agreement
  284.             // We should return NOERROR from Receive() in this case because returning S_FALSE
  285.             // from Receive() means that this is the end of the stream and no more data should
  286.             // be sent.
  287.             if(S_FALSE == hr) {
  288.  
  289.                 //  We must Release() the sample before doing anything
  290.                 //  like calling the filter graph because having the
  291.                 //  sample means we may have the DirectDraw lock
  292.                 //  (== win16 lock on some versions)
  293.                 pOutSample->Release();
  294.                 m_bSampleSkipped = TRUE;
  295.                 if(!m_bQualityChanged) {
  296.                     m_bQualityChanged = TRUE;
  297.                     NotifyEvent(EC_QUALITY_CHANGE,0,0);
  298.                 }
  299.                 return NOERROR;
  300.             }
  301.         }
  302.     }
  303.  
  304.     // release the output buffer. If the connected pin still needs it,
  305.     // it will have addrefed it itself.
  306.     pOutSample->Release();
  307.     ASSERT(CritCheckIn(&m_csReceive));
  308.  
  309.     return hr;
  310. }
  311.  
  312.  
  313.  
  314. BOOL CVideoTransformFilter::ShouldSkipFrame( IMediaSample * pIn) {
  315.     REFERENCE_TIME trStart, trStopAt;
  316.     HRESULT hr = pIn->GetTime(&trStart, &trStopAt);
  317.  
  318.     // Don't skip frames with no timestamps
  319.     if(hr != S_OK)
  320.         return FALSE;
  321.  
  322.     int itrFrame = (int)(trStopAt - trStart);  // frame duration
  323.  
  324.     if(S_OK==pIn->IsSyncPoint()) {
  325.         MSR_INTEGER(m_idFrameType, 1);
  326.         if(m_nKeyFramePeriod < m_nFramesSinceKeyFrame) {
  327.             // record the max
  328.             m_nKeyFramePeriod = m_nFramesSinceKeyFrame;
  329.         }
  330.         m_nFramesSinceKeyFrame = 0;
  331.         m_bSkipping = FALSE;
  332.     }
  333.     else {
  334.         MSR_INTEGER(m_idFrameType, 2);
  335.         if(m_nFramesSinceKeyFrame>m_nKeyFramePeriod
  336.             && m_nKeyFramePeriod>0) {
  337.             // We haven't seen the key frame yet, but we were clearly being
  338.             // overoptimistic about how frequent they are.
  339.             m_nKeyFramePeriod = m_nFramesSinceKeyFrame;
  340.         }
  341.     }
  342.  
  343.  
  344.     // Whatever we might otherwise decide,
  345.     // if we are taking only a small fraction of the required frame time to decode
  346.     // then any quality problems are actually coming from somewhere else.
  347.     // Could be a net problem at the source for instance.  In this case there's
  348.     // no point in us skipping frames here.
  349.     if(m_itrAvgDecode*4>itrFrame) {
  350.  
  351.         // Don't skip unless we are at least a whole frame late.
  352.         // (We would skip B frames if more than 1/2 frame late, but they're safe).
  353.         if(m_itrLate > itrFrame) {
  354.  
  355.             // Don't skip unless the anticipated key frame would be no more than
  356.             // 1 frame early.  If the renderer has not been waiting (we *guess*
  357.             // it hasn't because we're late) then it will allow frames to be
  358.             // played early by up to a frame.
  359.  
  360.             // Let T = Stream time from now to anticipated next key frame
  361.             // = (frame duration) * (KeyFramePeriod - FramesSinceKeyFrame)
  362.             // So we skip if T - Late < one frame  i.e.
  363.             //   (duration) * (freq - FramesSince) - Late < duration
  364.             // or (duration) * (freq - FramesSince - 1) < Late
  365.  
  366.             // We don't dare skip until we have seen some key frames and have
  367.             // some idea how often they occur and they are reasonably frequent.
  368.             if(m_nKeyFramePeriod>0) {
  369.                 // It would be crazy - but we could have a stream with key frames
  370.                 // a very long way apart - and if they are further than about
  371.                 // 3.5 minutes apart then we could get arithmetic overflow in
  372.                 // reference time units.  Therefore we switch to mSec at this point
  373.                 int it = (itrFrame/10000)
  374.                     * (m_nKeyFramePeriod-m_nFramesSinceKeyFrame -  1);
  375.                 MSR_INTEGER(m_idTimeTillKey, it);
  376.  
  377.                 // For debug - might want to see the details - dump them as scratch pad
  378. #ifdef VTRANSPERF
  379.                 MSR_INTEGER(0, itrFrame);
  380.                 MSR_INTEGER(0, m_nFramesSinceKeyFrame);
  381.                 MSR_INTEGER(0, m_nKeyFramePeriod);
  382. #endif
  383.                 if(m_itrLate/10000 > it) {
  384.                     m_bSkipping = TRUE;
  385.                     // Now we are committed.  Once we start skipping, we
  386.                     // cannot stop until we hit a key frame.
  387.                 }
  388.                 else {
  389. #ifdef VTRANSPERF
  390.                     MSR_INTEGER(0, 777770);  // not near enough to next key
  391. #endif
  392.                 }
  393.             }
  394.             else {
  395. #ifdef VTRANSPERF
  396.                 MSR_INTEGER(0, 777771);  // Next key not predictable
  397. #endif
  398.             }
  399.         }
  400.         else {
  401. #ifdef VTRANSPERF
  402.             MSR_INTEGER(0, 777772);  // Less than one frame late
  403.             MSR_INTEGER(0, m_itrLate);
  404.             MSR_INTEGER(0, itrFrame);
  405. #endif
  406.         }
  407.     }
  408.     else {
  409. #ifdef VTRANSPERF
  410.         MSR_INTEGER(0, 777773);  // Decode time short - not not worth skipping
  411.         MSR_INTEGER(0, m_itrAvgDecode);
  412.         MSR_INTEGER(0, itrFrame);
  413. #endif
  414.     }
  415.  
  416.     ++m_nFramesSinceKeyFrame;
  417.  
  418.     if(m_bSkipping) {
  419.         // We will count down the lateness as we skip each frame.
  420.         // We re-assess each frame.  The key frame might not arrive when expected.
  421.         // We reset m_itrLate if we get a new Quality message, but actually that's
  422.         // not likely because we're not sending frames on to the Renderer.  In
  423.         // fact if we DID get another one it would mean that there's a long
  424.         // pipe between us and the renderer and we might need an altogether
  425.         // better strategy to avoid hunting!
  426.         m_itrLate = m_itrLate - itrFrame;
  427.     }
  428.  
  429.     MSR_INTEGER(m_idLate, (int)m_itrLate/10000); // Note how late we think we are
  430.     if(m_bSkipping) {
  431.         if(!m_bQualityChanged) {
  432.             m_bQualityChanged = TRUE;
  433.             NotifyEvent(EC_QUALITY_CHANGE,0,0);
  434.         }
  435.     }
  436.     return m_bSkipping;
  437. }
  438.  
  439.  
  440. HRESULT CVideoTransformFilter::AlterQuality(Quality q) {
  441.     // to reduce the amount of 64 bit arithmetic, m_itrLate is an int.
  442.     // +, -, >, == etc  are not too bad, but * and / are painful.
  443.     if(m_itrLate>300000000) {
  444.         // Avoid overflow and silliness - more than 30 secs late is already silly
  445.         m_itrLate = 300000000;
  446.     }
  447.     else {
  448.         m_itrLate = (int)q.Late;
  449.     }
  450.     // We ignore the other fields
  451.  
  452.     // We're actually not very good at handling this.  In non-direct draw mode
  453.     // most of the time can be spent in the renderer which can skip any frame.
  454.     // In that case we'd rather the renderer handled things.
  455.     // Nevertheless we will keep an eye on it and if we really start getting
  456.     // a very long way behind then we will actually skip - but we'll still tell
  457.     // the renderer (or whoever is downstream) that they should handle quality.
  458.  
  459.     return E_FAIL;     // Tell the renderer to do his thing.
  460.  
  461. }
  462.  
  463.  
  464.  
  465. // This will avoid several hundred useless warnings if compiled -W4 by MS VC++ v4
  466. #pragma warning(disable:4514)
  467.  
  468.  
  469.