BaseClass.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:487k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1.   FObjectCreationLock.Lock;
  2.   try
  3.     // Should only ever be called with zero
  4.     Assert(n = 0);
  5.     if (n <> 0) then
  6.     begin
  7.       Result := nil;
  8.       Exit;
  9.     end;
  10.     // Create the input pin if not already done so
  11.     if (FInputPin = nil) then
  12.     begin
  13.       // hr must be initialized to NOERROR because
  14.       // CRendererInputPin's constructor only changes
  15.       // hr's value if an error occurs.
  16.       hr := NOERROR;
  17.       FInputPin := TBCRendererInputPin.Create(Self, hr, 'In');
  18.       if (FInputPin = nil) then
  19.       begin
  20.         Result := nil;
  21.         Exit;
  22.       end;
  23.       if Failed(hr) then
  24.       begin
  25.         FreeAndNil(FInputPin);
  26.         Result := nil;
  27.         Exit;
  28.       end;
  29.     end;
  30.     Result := FInputPin;
  31.   finally
  32.     FObjectCreationLock.UnLock;
  33.   end;
  34. end;
  35. function DumbItDownFor95(const S1, S2: WideString; CmpFlags: Integer): Integer;
  36. var
  37.   a1, a2: AnsiString;
  38. begin
  39.   a1 := s1;
  40.   a2 := s2;
  41.   Result := CompareStringA(LOCALE_USER_DEFAULT, CmpFlags, PChar(a1), Length(a1),
  42.     PChar(a2), Length(a2)) - 2;
  43. end;
  44. function WideCompareText(const S1, S2: WideString): Integer;
  45. begin
  46.   SetLastError(0);
  47.   Result := CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PWideChar(S1),
  48.     Length(S1), PWideChar(S2), Length(S2)) - 2;
  49.   case GetLastError of
  50.     0: ;
  51.     ERROR_CALL_NOT_IMPLEMENTED: Result := DumbItDownFor95(S1, S2, NORM_IGNORECASE);
  52.   end;
  53. end;
  54. // If "In" then return the IPin for our input pin, otherwise NULL and error
  55. function TBCBaseRenderer.FindPin(id: PWideChar; out Pin: IPin): HResult;
  56. begin
  57. // Milenko start
  58.   if (@Pin = nil) then
  59.   begin
  60.     Result := E_POINTER;
  61.     Exit;
  62.   end;
  63. // Milenko end
  64. // milenko start (delphi 5 doesn't know WideCompareText)
  65.   if WideCompareText(id, 'In') = 0 then
  66. // milenko end
  67.   begin
  68.     Pin := GetPin(0);
  69.     Assert(Pin <> nil);
  70.     // ??? Pin.AddRef;
  71.     Result := NOERROR;
  72.   end
  73.   else
  74.   begin
  75.     Pin := nil;
  76.     Result := VFW_E_NOT_FOUND;
  77.   end;
  78. end;
  79. // Called when the input pin receives an EndOfStream notification. If we have
  80. // not got a sample, then notify EC_COMPLETE now. If we have samples, then set
  81. // m_bEOS and check for this on completing samples. If we're waiting to pause
  82. // then complete the transition to paused state by setting the state event
  83. function TBCBaseRenderer.EndOfStream: HResult;
  84. begin
  85.   // Ignore these calls if we are stopped
  86.   if (FState = State_Stopped) then
  87.   begin
  88.     Result := NOERROR;
  89.     Exit;
  90.   end;
  91.   // If we have a sample then wait for it to be rendered
  92.   FIsEOS := True;
  93.   if Assigned(FMediaSample) then
  94.   begin
  95.     Result := NOERROR;
  96.     Exit;
  97.   end;
  98.   // If we are waiting for pause then we are now ready since we cannot now
  99.   // carry on waiting for a sample to arrive since we are being told there
  100.   // won't be any. This sets an event that the GetState function picks up
  101.   Ready;
  102.   // Only signal completion now if we are running otherwise queue it until
  103.   // we do run in StartStreaming. This is used when we seek because a seek
  104.   // causes a pause where early notification of completion is misleading
  105.   if FIsStreaming then
  106.     SendEndOfStream;
  107.   Result := NOERROR;
  108. end;
  109. // When we are told to flush we should release the source thread
  110. function TBCBaseRenderer.BeginFlush: HResult;
  111. begin
  112.   // If paused then report state intermediate until we get some data
  113.   if (FState = State_Paused) then
  114.     NotReady;
  115.   SourceThreadCanWait(False);
  116.   CancelNotification;
  117.   ClearPendingSample;
  118.   //  Wait for Receive to complete
  119.   WaitForReceiveToComplete;
  120.   Result := NOERROR;
  121. end;
  122. // After flushing the source thread can wait in Receive again
  123. function TBCBaseRenderer.EndFlush: HResult;
  124. begin
  125.   // Reset the current sample media time
  126.   if Assigned(FPosition) then
  127.     FPosition.ResetMediaTime;
  128.   // There should be no outstanding advise
  129.   Assert(CancelNotification = S_FALSE);
  130.   SourceThreadCanWait(True);
  131.   Result := NOERROR;
  132. end;
  133. // We can now send EC_REPAINTs if so required
  134. function TBCBaseRenderer.CompleteConnect(ReceivePin: IPin): HResult;
  135. begin
  136.   // The caller should always hold the interface lock because
  137.   // the function uses CBaseFilter::m_State.
  138.   {$IFDEF DEBUG}
  139.   Assert(FInterfaceLock.CritCheckIn);
  140.   {$ENDIF}
  141.   FAbort := False;
  142.   if (State_Running = GetRealState) then
  143.   begin
  144.     Result := StartStreaming;
  145.     if Failed(Result) then
  146.       Exit;
  147.     SetRepaintStatus(False);
  148.   end
  149.   else
  150.     SetRepaintStatus(True);
  151.   Result := NOERROR;
  152. end;
  153. // Called when we go paused or running
  154. function TBCBaseRenderer.Active: HResult;
  155. begin
  156.   Result := NOERROR;
  157. end;
  158. // Called when we go into a stopped state
  159. function TBCBaseRenderer.Inactive: HResult;
  160. begin
  161.   if Assigned(FPosition) then
  162.     FPosition.ResetMediaTime;
  163.   //  People who derive from this may want to override this behaviour
  164.   //  to keep hold of the sample in some circumstances
  165.   ClearPendingSample;
  166.   Result := NOERROR;
  167. end;
  168. // Tell derived classes about the media type agreed
  169. function TBCBaseRenderer.SetMediaType(MediaType: PAMMediaType): HResult;
  170. begin
  171.   Result := NOERROR;
  172. end;
  173. // When we break the input pin connection we should reset the EOS flags. When
  174. // we are asked for either IMediaPosition or IMediaSeeking we will create a
  175. // CPosPassThru object to handles media time pass through. When we're handed
  176. // samples we store (by calling CPosPassThru::RegisterMediaTime) their media
  177. // times so we can then return a real current position of data being rendered
  178. function TBCBaseRenderer.BreakConnect: HResult;
  179. begin
  180.   // Do we have a quality management sink
  181.   if Assigned(FQSink) then
  182.     FQSink := nil;
  183.   // Check we have a valid connection
  184.   if not FInputPin.IsConnected then
  185.   begin
  186.     Result := S_FALSE;
  187.     Exit;
  188.   end;
  189.   // Check we are stopped before disconnecting
  190.   if (FState <> State_Stopped) and (not FInputPin.CanReconnectWhenActive) then
  191.   begin
  192.     Result := VFW_E_NOT_STOPPED;
  193.     Exit;
  194.   end;
  195.   SetRepaintStatus(False);
  196.   ResetEndOfStream;
  197.   ClearPendingSample;
  198.   FAbort := False;
  199.   if (State_Running = FState) then
  200.     StopStreaming;
  201.   Result := NOERROR;
  202. end;
  203. // Retrieves the sample times for this samples (note the sample times are
  204. // passed in by reference not value). We return S_FALSE to say schedule this
  205. // sample according to the times on the sample. We also return S_OK in
  206. // which case the object should simply render the sample data immediately
  207. function TBCBaseRenderer.GetSampleTimes(MediaSample: IMediaSample;
  208.   out StartTime: TReferenceTime; out EndTime: TReferenceTime): HResult;
  209. begin
  210.   Assert(FAdvisedCookie = 0);
  211.   Assert(Assigned(MediaSample));
  212.   // If the stop time for this sample is before or the same as start time,
  213.   // then just ignore it (release it) and schedule the next one in line
  214.   // Source filters should always fill in the start and end times properly!
  215.   if Succeeded(MediaSample.GetTime(StartTime, EndTime)) then
  216.   begin
  217.     if (EndTime < StartTime) then
  218.     begin
  219.       Result := VFW_E_START_TIME_AFTER_END;
  220.       Exit;
  221.     end;
  222.   end
  223.   else
  224.   begin
  225.     // no time set in the sample... draw it now?
  226.     Result := S_OK;
  227.     Exit;
  228.   end;
  229.   // Can't synchronise without a clock so we return S_OK which tells the
  230.   // caller that the sample should be rendered immediately without going
  231.   // through the overhead of setting a timer advise link with the clock
  232.   if (FClock = nil) then
  233.     Result := S_OK
  234.   else
  235.     Result := ShouldDrawSampleNow(MediaSample, StartTime, EndTime);
  236. end;
  237. // By default all samples are drawn according to their time stamps so we
  238. // return S_FALSE. Returning S_OK means draw immediately, this is used
  239. // by the derived video renderer class in its quality management.
  240. function TBCBaseRenderer.ShouldDrawSampleNow(MediaSample: IMediaSample;
  241.   StartTime: TReferenceTime; out EndTime: TReferenceTime): HResult;
  242. begin
  243.   Result := S_FALSE;
  244. end;
  245. // We must always reset the current advise time to zero after a timer fires
  246. // because there are several possible ways which lead us not to do any more
  247. // scheduling such as the pending image being cleared after state changes
  248. procedure TBCBaseRenderer.SignalTimerFired;
  249. begin
  250.   FAdvisedCookie := 0;
  251. end;
  252. // Cancel any notification currently scheduled. This is called by the owning
  253. // window object when it is told to stop streaming. If there is no timer link
  254. // outstanding then calling this is benign otherwise we go ahead and cancel
  255. // We must always reset the render event as the quality management code can
  256. // signal immediate rendering by setting the event without setting an advise
  257. // link. If we're subsequently stopped and run the first attempt to setup an
  258. // advise link with the reference clock will find the event still signalled
  259. function TBCBaseRenderer.CancelNotification: HResult;
  260. var
  261.   dwAdvisedCookie: DWord;
  262. begin
  263.   Assert((FAdvisedCookie = 0) or Assigned(FClock));
  264.   dwAdvisedCookie := FAdvisedCookie;
  265.   // Have we a live advise link
  266.   if (FAdvisedCookie <> 0) then
  267.   begin
  268.     FClock.Unadvise(FAdvisedCookie);
  269.     SignalTimerFired;
  270.     Assert(FAdvisedCookie = 0);
  271.   end;
  272.   // Clear the event and return our status
  273.   FRenderEvent.Reset;
  274.   if (dwAdvisedCookie <> 0) then
  275.     Result := S_OK
  276.   else
  277.     Result := S_FALSE;
  278. end;
  279. // Responsible for setting up one shot advise links with the clock
  280. // Return FALSE if the sample is to be dropped (not drawn at all)
  281. // Return TRUE if the sample is to be drawn and in this case also
  282. // arrange for m_RenderEvent to be set at the appropriate time
  283. function TBCBaseRenderer.ScheduleSample(MediaSample: IMediaSample): Boolean;
  284. var
  285.   StartSample, EndSample: TReferenceTime;
  286.   hr: HResult;
  287. begin
  288.   // Is someone pulling our leg
  289.   if (MediaSample = nil) then
  290.   begin
  291.     Result := False;
  292.     Exit;
  293.   end;
  294.   // Get the next sample due up for rendering.  If there aren't any ready
  295.   // then GetNextSampleTimes returns an error.  If there is one to be done
  296.   // then it succeeds and yields the sample times. If it is due now then
  297.   // it returns S_OK other if it's to be done when due it returns S_FALSE
  298.   hr := GetSampleTimes(MediaSample, StartSample, EndSample);
  299.   if Failed(hr) then
  300.   begin
  301.     Result := False;
  302.     Exit;
  303.   end;
  304.   // If we don't have a reference clock then we cannot set up the advise
  305.   // time so we simply set the event indicating an image to render. This
  306.   // will cause us to run flat out without any timing or synchronisation
  307.   if (hr = S_OK) then
  308.   begin
  309.     // ???Assert(SetEvent(FRenderEvent.Handle));
  310.     FRenderEvent.SetEv;
  311.     Result := True;
  312.     Exit;
  313.   end;
  314.   Assert(FAdvisedCookie = 0);
  315.   Assert(Assigned(FClock));
  316.   Assert(Wait_Timeout = WaitForSingleObject(FRenderEvent.Handle, 0));
  317.   // We do have a valid reference clock interface so we can ask it to
  318.   // set an event when the image comes due for rendering. We pass in
  319.   // the reference time we were told to start at and also the current
  320.   // stream time which is the offset from the start reference time
  321.   hr := FClock.AdviseTime(
  322.     FStart,               // Start run time
  323.     StartSample,          // Stream time
  324.     FRenderEvent.Handle,  // Render notification
  325.     FAdvisedCookie);      // Advise cookie
  326.   if Succeeded(hr) then
  327.   begin
  328.     Result := True;
  329.     Exit;
  330.   end;
  331.   // We could not schedule the next sample for rendering despite the fact
  332.   // we have a valid sample here. This is a fair indication that either
  333.   // the system clock is wrong or the time stamp for the sample is duff
  334.   Assert(FAdvisedCookie = 0);
  335.   Result := False;
  336. end;
  337. // This is called when a sample comes due for rendering. We pass the sample
  338. // on to the derived class. After rendering we will initialise the timer for
  339. // the next sample, NOTE signal that the last one fired first, if we don't
  340. // do this it thinks there is still one outstanding that hasn't completed
  341. function TBCBaseRenderer.Render(MediaSample: IMediaSample): HResult;
  342. begin
  343.   // If the media sample is NULL then we will have been notified by the
  344.   // clock that another sample is ready but in the mean time someone has
  345.   // stopped us streaming which causes the next sample to be released
  346.   if (MediaSample = nil) then
  347.   begin
  348.     Result := S_FALSE;
  349.     Exit;
  350.   end;
  351.   // If we have stopped streaming then don't render any more samples, the
  352.   // thread that got in and locked us and then reset this flag does not
  353.   // clear the pending sample as we can use it to refresh any output device
  354.   if Not FIsStreaming then
  355.   begin
  356.     Result := S_FALSE;
  357.     Exit;
  358.   end;
  359.   // Time how long the rendering takes
  360.   OnRenderStart(MediaSample);
  361.   DoRenderSample(MediaSample);
  362.   OnRenderEnd(MediaSample);
  363.   Result := NOERROR;
  364. end;
  365. // Checks if there is a sample waiting at the renderer
  366. function TBCBaseRenderer.HaveCurrentSample: Boolean;
  367. begin
  368.   FRendererLock.Lock;
  369.   try
  370.     Result := (FMediaSample <> nil);
  371.   finally
  372.     FRendererLock.UnLock;
  373.   end;
  374. end;
  375. // Returns the current sample waiting at the video renderer. We AddRef the
  376. // sample before returning so that should it come due for rendering the
  377. // person who called this method will hold the remaining reference count
  378. // that will stop the sample being added back onto the allocator free list
  379. function TBCBaseRenderer.GetCurrentSample: IMediaSample;
  380. begin
  381.   FRendererLock.Lock;
  382.   try
  383.     (* ???
  384.         if (m_pMediaSample) {
  385.             m_pMediaSample->AddRef();
  386.     *)
  387.     Result := FMediaSample;
  388.   finally
  389.     FRendererLock.Unlock;
  390.   end;
  391. end;
  392. // Called when the source delivers us a sample. We go through a few checks to
  393. // make sure the sample can be rendered. If we are running (streaming) then we
  394. // have the sample scheduled with the reference clock, if we are not streaming
  395. // then we have received an sample in paused mode so we can complete any state
  396. // transition. On leaving this function everything will be unlocked so an app
  397. // thread may get in and change our state to stopped (for example) in which
  398. // case it will also signal the thread event so that our wait call is stopped
  399. function TBCBaseRenderer.PrepareReceive(MediaSample: IMediaSample): HResult;
  400. var
  401.   hr: HResult;
  402. begin
  403.   FInterfaceLock.Lock;
  404.   try
  405.     FInReceive := True;
  406.     // Check our flushing and filter state
  407.     // This function must hold the interface lock because it calls
  408.     // CBaseInputPin::Receive() and CBaseInputPin::Receive() uses
  409.     // CBasePin::m_bRunTimeError.
  410. // ???     HRESULT hr = m_pInputPin->CBaseInputPin::Receive(MediaSample);
  411.     hr := FInputPin.InheritedReceive(MediaSample);
  412.     if (hr <> NOERROR) then
  413.     begin
  414.       FInReceive := False;
  415.       Result := E_FAIL;
  416.       Exit;
  417.     end;
  418.     // Has the type changed on a media sample. We do all rendering
  419.     // synchronously on the source thread, which has a side effect
  420.     // that only one buffer is ever outstanding. Therefore when we
  421.     // have Receive called we can go ahead and change the format
  422.     // Since the format change can cause a SendMessage we just don't
  423.     // lock
  424.     if Assigned(FInputPin.SampleProps.pMediaType) then
  425.     begin
  426.       hr := FInputPin.SetMediaType(FInputPin.FSampleProps.pMediaType);
  427.       if Failed(hr) then
  428.       begin
  429.         Result := hr;
  430.         FInReceive := False;
  431.         Exit;
  432.       end;
  433.     end;
  434.     FRendererLock.Lock;
  435.     try
  436.       Assert(IsActive);
  437.       Assert(not FInputPin.IsFlushing);
  438.       Assert(FInputPin.IsConnected);
  439.       Assert(FMediaSample = nil);
  440.       // Return an error if we already have a sample waiting for rendering
  441.       // source pins must serialise the Receive calls - we also check that
  442.       // no data is being sent after the source signalled an end of stream
  443.       if (Assigned(FMediaSample) or FIsEOS or FAbort) then
  444.       begin
  445.         Ready;
  446.         FInReceive := False;
  447.         Result := E_UNEXPECTED;
  448.         Exit;
  449.       end;
  450.       // Store the media times from this sample
  451.       if Assigned(FPosition) then
  452.         FPosition.RegisterMediaTime(MediaSample);
  453.       // Schedule the next sample if we are streaming
  454.       if (FIsStreaming and (not ScheduleSample(MediaSample))) then
  455.       begin
  456.         Assert(WAIT_TIMEOUT = WaitForSingleObject(FRenderEvent.Handle, 0));
  457.         Assert(CancelNotification = S_FALSE);
  458.         FInReceive := False;
  459.         Result := VFW_E_SAMPLE_REJECTED;
  460.         Exit;
  461.       end;
  462.       // Store the sample end time for EC_COMPLETE handling
  463.       FSignalTime := FInputPin.FSampleProps.tStop;
  464.       // BEWARE we sometimes keep the sample even after returning the thread to
  465.       // the source filter such as when we go into a stopped state (we keep it
  466.       // to refresh the device with) so we must AddRef it to keep it safely. If
  467.       // we start flushing the source thread is released and any sample waiting
  468.       // will be released otherwise GetBuffer may never return (see BeginFlush)
  469.       FMediaSample := MediaSample;
  470.       //???      m_pMediaSample->AddRef();
  471.       if not FIsStreaming then
  472.         SetRepaintStatus(True);
  473.       Result := NOERROR;
  474.     finally
  475.       FRendererLock.Unlock;
  476.     end;
  477.   finally
  478.     FInterfaceLock.UnLock;
  479.   end;
  480. end;
  481. // Called by the source filter when we have a sample to render. Under normal
  482. // circumstances we set an advise link with the clock, wait for the time to
  483. // arrive and then render the data using the PURE virtual DoRenderSample that
  484. // the derived class will have overriden. After rendering the sample we may
  485. // also signal EOS if it was the last one sent before EndOfStream was called
  486. function TBCBaseRenderer.Receive(MediaSample: IMediaSample): HResult;
  487. begin
  488.   Assert(Assigned(MediaSample));
  489.   // It may return VFW_E_SAMPLE_REJECTED code to say don't bother
  490.   Result := PrepareReceive(MediaSample);
  491.   Assert(FInReceive = Succeeded(Result));
  492.   if Failed(Result) then
  493.   begin
  494.     if (Result = VFW_E_SAMPLE_REJECTED) then
  495.       Result := NOERROR;
  496.     Exit;
  497.   end;
  498.   // We realize the palette in "PrepareRender()" so we have to give away the
  499.   // filter lock here.
  500.   if (FState = State_Paused) then
  501.   begin
  502.     PrepareRender;
  503.     // no need to use InterlockedExchange
  504.     FInReceive := False;
  505.     // We must hold both these locks
  506.     FInterfaceLock.Lock;
  507.     try
  508.       if (FState = State_Stopped) then
  509.       begin
  510.         Result := NOERROR;
  511.         Exit;
  512.       end;
  513.       FInReceive := True;
  514.       FRendererLock.Lock;
  515.       try
  516.         OnReceiveFirstSample(MediaSample);
  517.       finally
  518.         FRendererLock.UnLock;
  519.       end;
  520.     finally
  521.       FInterfaceLock.UnLock;
  522.     end;
  523.     Ready;
  524.   end;
  525.   // Having set an advise link with the clock we sit and wait. We may be
  526.   // awoken by the clock firing or by a state change. The rendering call
  527.   // will lock the critical section and check we can still render the data
  528.   Result := WaitForRenderTime;
  529.   if Failed(Result) then
  530.   begin
  531.     FInReceive := False;
  532.     Result := NOERROR;
  533.     Exit;
  534.   end;
  535.   PrepareRender;
  536.   //  Set this here and poll it until we work out the locking correctly
  537.   //  It can't be right that the streaming stuff grabs the interface
  538.   //  lock - after all we want to be able to wait for this stuff
  539.   //  to complete
  540.   FInReceive := False;
  541.   // We must hold both these locks
  542.   FInterfaceLock.Lock;
  543.   try
  544.     // since we gave away the filter wide lock, the sate of the filter could
  545.     // have chnaged to Stopped
  546.     if (FState = State_Stopped) then
  547.     begin
  548.       Result := NOERROR;
  549.       Exit;
  550.     end;
  551.     FRendererLock.Lock;
  552.     try
  553.       // Deal with this sample
  554.       Render(FMediaSample);
  555.       ClearPendingSample;
  556. // milenko start (why commented before?)
  557.       SendEndOfStream;
  558. // milenko end
  559.       CancelNotification;
  560.       Result := NOERROR;
  561.     finally
  562.       FRendererLock.UnLock;
  563.     end;
  564.   finally
  565.     FInterfaceLock.UnLock;
  566.   end;
  567. end;
  568. // This is called when we stop or are inactivated to clear the pending sample
  569. // We release the media sample interface so that they can be allocated to the
  570. // source filter again, unless of course we are changing state to inactive in
  571. // which case GetBuffer will return an error. We must also reset the current
  572. // media sample to NULL so that we know we do not currently have an image
  573. function TBCBaseRenderer.ClearPendingSample: HResult;
  574. begin
  575.   FRendererLock.Lock;
  576.   try
  577.     if Assigned(FMediaSample) then
  578.       FMediaSample := nil;
  579.     Result := NOERROR;
  580.   finally
  581.     FRendererLock.Unlock;
  582.   end;
  583. end;
  584. // Used to signal end of stream according to the sample end time
  585. // Milenko start (use this callback outside of the class and with stdcall;)
  586. procedure EndOfStreamTimer(uID, uMsg: UINT;
  587.   dwUser, dw1, dw2: DWord); stdcall;
  588. var
  589.   Renderer: TBCBaseRenderer;
  590. begin
  591.   Renderer := TBCBaseRenderer(dwUser);
  592.   {$IFDEF DEBUG}
  593.   //NOTE1("EndOfStreamTimer called (%d)",uID);
  594.   DbgLog(Format('EndOfStreamTimer called (%d)', [uID]));
  595.   {$ENDIF}
  596.   Renderer.TimerCallback;
  597. {
  598. ???
  599.     CBaseRenderer *pRenderer = (CBaseRenderer * ) dwUser;
  600.     pRenderer->TimerCallback();
  601. }
  602. end;
  603. // Milenko end
  604. //  Do the timer callback work
  605. procedure TBCBaseRenderer.TimerCallback;
  606. begin
  607.   //  Lock for synchronization (but don't hold this lock when calling
  608.   //  timeKillEvent)
  609.   FRendererLock.Lock;
  610.   try
  611.     // See if we should signal end of stream now
  612.     if (FEndOfStreamTimer <> 0) then
  613.     begin
  614.       FEndOfStreamTimer := 0;
  615. // milenko start (why commented before?)
  616.       SendEndOfStream;
  617. // milenko end      
  618.     end;
  619.   finally
  620.     FRendererLock.Unlock;
  621.   end;
  622. end;
  623. // If we are at the end of the stream signal the filter graph but do not set
  624. // the state flag back to FALSE. Once we drop off the end of the stream we
  625. // leave the flag set (until a subsequent ResetEndOfStream). Each sample we
  626. // get delivered will update m_SignalTime to be the last sample's end time.
  627. // We must wait this long before signalling end of stream to the filtergraph
  628. const
  629.   TIMEOUT_DELIVERYWAIT = 50;
  630.   TIMEOUT_RESOLUTION = 10;
  631. function TBCBaseRenderer.SendEndOfStream: HResult;
  632. var
  633.   Signal, CurrentTime: TReferenceTime;
  634.   Delay: Longint;
  635. begin
  636.   {$IFDEF DEBUG}
  637.   Assert(FRendererLock.CritCheckIn);
  638.   {$ENDIF}
  639.   if ((not FIsEOS) or FIsEOSDelivered or (FEndOfStreamTimer <> 0)) then
  640.   begin
  641.     Result := NOERROR;
  642.     Exit;
  643.   end;
  644.   // If there is no clock then signal immediately
  645.   if (FClock = nil) then
  646.   begin
  647.     Result := NotifyEndOfStream;
  648.     Exit;
  649.   end;
  650.   // How long into the future is the delivery time
  651.   Signal := FStart + FSignalTime;
  652.   FClock.GetTime(int64(CurrentTime));
  653. // Milenko Start (important!)
  654. //  Delay := (Longint(Signal) - CurrentTime) div 10000;
  655.   Delay := LongInt((Signal - CurrentTime) div 10000);
  656. // Milenko end
  657.   // Dump the timing information to the debugger
  658. {$IFDEF DEBUG}
  659.   DbgLog(Self, Format('Delay until end of stream delivery %d', [Delay]));
  660.   // ???    NOTE1("Current %s",(LPCTSTR)CDisp((LONGLONG)CurrentTime));
  661.   // ???    NOTE1("Signal %s",(LPCTSTR)CDisp((LONGLONG)Signal));
  662.   DbgLog(Self, Format('Current %d', [CurrentTime]));
  663.   DbgLog(Self, Format('Signal %d', [Signal]));
  664. {$ENDIF}
  665.   // Wait for the delivery time to arrive
  666.   if (Delay < TIMEOUT_DELIVERYWAIT) then
  667.   begin
  668.     Result := NotifyEndOfStream;
  669.     Exit;
  670.   end;
  671.   // Signal a timer callback on another worker thread
  672.   FEndOfStreamTimer := CompatibleTimeSetEvent(
  673.     Delay,                            // Period of timer
  674.     TIMEOUT_RESOLUTION,               // Timer resolution
  675.     // ???
  676. // Milenko start (callback is now outside of the class)
  677.     @EndOfStreamTimer,// Callback function
  678. // Milenko end
  679.     Cardinal(Self),                   // Used information
  680.     TIME_ONESHOT);                    // Type of callback
  681.   if (FEndOfStreamTimer = 0) then
  682.   begin
  683.     Result := NotifyEndOfStream;
  684.     Exit;
  685.   end;
  686.   Result := NOERROR;
  687. end;
  688. // Signals EC_COMPLETE to the filtergraph manager
  689. function TBCBaseRenderer.NotifyEndOfStream: HResult;
  690. var
  691.   Filter: IBaseFilter;
  692. begin
  693.   FRendererLock.Lock;
  694.   try
  695.     Assert(not FIsEOSDelivered);
  696.     Assert(FEndOfStreamTimer = 0);
  697.     // Has the filter changed state
  698.     if not FIsStreaming then
  699.     begin
  700.       Assert(FEndOfStreamTimer = 0);
  701.       Result := NOERROR;
  702.       Exit;
  703.     end;
  704.     // Reset the end of stream timer
  705.     FEndOfStreamTimer := 0;
  706.     // If we've been using the IMediaPosition interface, set it's start
  707.     // and end media "times" to the stop position by hand.  This ensures
  708.     // that we actually get to the end, even if the MPEG guestimate has
  709.     // been bad or if the quality management dropped the last few frames
  710.     if Assigned(FPosition) then
  711.       FPosition.EOS;
  712.     FIsEOSDelivered := True;
  713. {$IFDEF DEBUG}
  714.     DbgLog('Sending EC_COMPLETE...');
  715. {$ENDIF}
  716.     // ??? return NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);
  717. // milenko start (Delphi 5 compatibility)
  718.     QueryInterface(IID_IBaseFilter,Filter);
  719.     Result := NotifyEvent(EC_COMPLETE, S_OK, Integer(Filter));
  720.     Filter := nil;
  721. // milenko end
  722.   finally
  723.     FRendererLock.UnLock;
  724.   end;
  725. end;
  726. // Reset the end of stream flag, this is typically called when we transfer to
  727. // stopped states since that resets the current position back to the start so
  728. // we will receive more samples or another EndOfStream if there aren't any. We
  729. // keep two separate flags one to say we have run off the end of the stream
  730. // (this is the m_bEOS flag) and another to say we have delivered EC_COMPLETE
  731. // to the filter graph. We need the latter otherwise we can end up sending an
  732. // EC_COMPLETE every time the source changes state and calls our EndOfStream
  733. function TBCBaseRenderer.ResetEndOfStream: HResult;
  734. begin
  735.   ResetEndOfStreamTimer;
  736.   FRendererLock.Lock;
  737.   try
  738.     FIsEOS          := False;
  739.     FIsEOSDelivered := False;
  740.     FSignalTime     := 0;
  741.     Result := NOERROR;
  742.   finally
  743.     FRendererLock.UnLock;
  744.   end;
  745. end;
  746. // Kills any outstanding end of stream timer
  747. procedure TBCBaseRenderer.ResetEndOfStreamTimer;
  748. begin
  749.   {$IFDEF DEBUG}
  750.   Assert(FRendererLock.CritCheckOut);
  751.   {$ENDIF}
  752.   if (FEndOfStreamTimer <> 0) then
  753.   begin
  754.     timeKillEvent(FEndOfStreamTimer);
  755.     FEndOfStreamTimer := 0;
  756.   end;
  757. end;
  758. // This is called when we start running so that we can schedule any pending
  759. // image we have with the clock and display any timing information. If we
  760. // don't have any sample but we have queued an EOS flag then we send it. If
  761. // we do have a sample then we wait until that has been rendered before we
  762. // signal the filter graph otherwise we may change state before it's done
  763. function TBCBaseRenderer.StartStreaming: HResult;
  764. begin
  765.   FRendererLock.Lock;
  766.   try
  767.     if FIsStreaming then
  768.     begin
  769.       Result := NOERROR;
  770.       Exit;
  771.     end;
  772.     // Reset the streaming times ready for running
  773.     FIsStreaming := True;
  774.     timeBeginPeriod(1);
  775.     OnStartStreaming;
  776.     // There should be no outstanding advise
  777.     Assert(WAIT_TIMEOUT = WaitForSingleObject(FRenderEvent.Handle, 0));
  778.     Assert(CancelNotification = S_FALSE);
  779.     // If we have an EOS and no data then deliver it now
  780.     if (FMediaSample = nil) then
  781.     begin
  782.       Result := SendEndOfStream;
  783.       Exit;
  784.     end;
  785.     // Have the data rendered
  786.     Assert(Assigned(FMediaSample));
  787.     if not ScheduleSample(FMediaSample) then
  788.       FRenderEvent.SetEv;
  789.     Result := NOERROR;
  790.   finally
  791.     FRendererLock.UnLock;
  792.   end;
  793. end;
  794. // This is called when we stop streaming so that we can set our internal flag
  795. // indicating we are not now to schedule any more samples arriving. The state
  796. // change methods in the filter implementation take care of cancelling any
  797. // clock advise link we have set up and clearing any pending sample we have
  798. function TBCBaseRenderer.StopStreaming: HResult;
  799. begin
  800.   FRendererLock.Lock;
  801.   try
  802.     FIsEOSDelivered := False;
  803.     if FIsStreaming then
  804.     begin
  805.       FIsStreaming := False;
  806.       OnStopStreaming;
  807.       timeEndPeriod(1);
  808.     end;
  809.     Result := NOERROR;
  810.   finally
  811.     FRendererLock.Unlock;
  812.   end;
  813. end;
  814. // We have a boolean flag that is reset when we have signalled EC_REPAINT to
  815. // the filter graph. We set this when we receive an image so that should any
  816. // conditions arise again we can send another one. By having a flag we ensure
  817. // we don't flood the filter graph with redundant calls. We do not set the
  818. // event when we receive an EndOfStream call since there is no point in us
  819. // sending further EC_REPAINTs. In particular the AutoShowWindow method and
  820. // the DirectDraw object use this method to control the window repainting
  821. procedure TBCBaseRenderer.SetRepaintStatus(Repaint: Boolean);
  822. begin
  823.   FRendererLock.Lock;
  824.   try
  825.     FRepaintStatus := Repaint;
  826.   finally
  827.     FRendererLock.Unlock;
  828.   end;
  829. end;
  830. // Pass the window handle to the upstream filter
  831. procedure TBCBaseRenderer.SendNotifyWindow(Pin: IPin; Handle: HWND);
  832. var
  833.   Sink: IMediaEventSink;
  834.   hr: HResult;
  835. begin
  836.   // Does the pin support IMediaEventSink
  837.   hr := Pin.QueryInterface(IID_IMediaEventSink, Sink);
  838.   if Succeeded(hr) then
  839.   begin
  840.     Sink.Notify(EC_NOTIFY_WINDOW, Handle, 0);
  841.     Sink := nil;
  842.   end;
  843.   NotifyEvent(EC_NOTIFY_WINDOW, Handle, 0);
  844. end;
  845. // Signal an EC_REPAINT to the filter graph. This can be used to have data
  846. // sent to us. For example when a video window is first displayed it may
  847. // not have an image to display, at which point it signals EC_REPAINT. The
  848. // filtergraph will either pause the graph if stopped or if already paused
  849. // it will call put_CurrentPosition of the current position. Setting the
  850. // current position to itself has the stream flushed and the image resent
  851. // ??? #define RLOG(_x_) DbgLog((LOG_TRACE,1,TEXT(_x_)));
  852. procedure TBCBaseRenderer.SendRepaint;
  853. var
  854.   Pin: IPin;
  855. begin
  856.   FRendererLock.Lock;
  857.   try
  858.     Assert(Assigned(FInputPin));
  859.     // We should not send repaint notifications when...
  860.     //    - An end of stream has been notified
  861.     //    - Our input pin is being flushed
  862.     //    - The input pin is not connected
  863.     //    - We have aborted a video playback
  864.     //    - There is a repaint already sent
  865.     if (not FAbort) and
  866.       (FInputPin.IsConnected) and
  867.       (not FInputPin.IsFlushing) and
  868.       (not IsEndOfStream) and
  869.       FRepaintStatus then
  870.     begin
  871. // milenko start (delphi 5 compatibility)
  872. //      Pin := FInputPin as IPin;
  873.       FInputPin.QueryInterface(IID_IPin,Pin);
  874.       NotifyEvent(EC_REPAINT, Integer(Pin), 0);
  875.       Pin := nil;
  876. // milenko end
  877.       SetRepaintStatus(False);
  878. {$IFDEF DEBUG}
  879.       DbgLog('Sending repaint');
  880. {$ENDIF}
  881.     end;
  882.   finally
  883.     FRendererLock.Unlock;
  884.   end;
  885. end;
  886. // When a video window detects a display change (WM_DISPLAYCHANGE message) it
  887. // can send an EC_DISPLAY_CHANGED event code along with the renderer pin. The
  888. // filtergraph will stop everyone and reconnect our input pin. As we're then
  889. // reconnected we can accept the media type that matches the new display mode
  890. // since we may no longer be able to draw the current image type efficiently
  891. function TBCBaseRenderer.OnDisplayChange: Boolean;
  892. var
  893.   Pin: IPin;
  894. begin
  895.   // Ignore if we are not connected yet
  896.   FRendererLock.Lock;
  897.   try
  898.     if not FInputPin.IsConnected then
  899.     begin
  900.       Result := False;
  901.       Exit;
  902.     end;
  903. {$IFDEF DEBUG}
  904.     DbgLog('Notification of EC_DISPLAY_CHANGE');
  905. {$ENDIF}
  906.     // Pass our input pin as parameter on the event
  907. // milenko start (Delphi 5 compatibility)
  908. //  Pin := FInputPin as IPin;
  909.     FInputPin.QueryInterface(IID_IPin,Pin);
  910.     // ??? m_pInputPin->AddRef();
  911.     NotifyEvent(EC_DISPLAY_CHANGED, Integer(Pin), 0);
  912.     SetAbortSignal(True);
  913.     ClearPendingSample;
  914. //    FreeAndNil(FInputPin);
  915.     Pin := nil;
  916. // milenko end
  917.     Result := True;
  918.   finally
  919.     FRendererLock.Unlock;
  920.   end;
  921. end;
  922. // Called just before we start drawing.
  923. // Store the current time in m_trRenderStart to allow the rendering time to be
  924. // logged.  Log the time stamp of the sample and how late it is (neg is early)
  925. procedure TBCBaseRenderer.OnRenderStart(MediaSample: IMediaSample);
  926. {$IFDEF PERF}
  927. var
  928.   StartTime, EndTime, StreamTime: TReferenceTime;
  929. {$ENDIF}
  930. begin
  931. {$IFDEF PERF}
  932.   MediaSample.GetTime(StartTime, EndTime);
  933.   MSR_INTEGER(FBaseStamp, Integer(StartTime)); // dump low order 32 bits
  934.   FClock.GetTime(pint64(@FRenderStart)^);
  935.   MSR_INTEGER(0, Integer(FRenderStart));
  936.   StreamTime := FRenderStart - FStart;      // convert reftime to stream time
  937.   MSR_INTEGER(0, Integer(StreamTime));
  938.   MSR_INTEGER(FBaseAccuracy, RefTimeToMiliSec(StreamTime - StartTime)); // dump in mSec
  939. {$ENDIF}
  940. end;
  941. // Called directly after drawing an image.
  942. // calculate the time spent drawing and log it.
  943. procedure TBCBaseRenderer.OnRenderEnd(MediaSample: IMediaSample);
  944. {$IFDEF PERF}
  945. var
  946.   NowTime: TReferenceTime;
  947.   t: Integer;
  948. {$ENDIF}
  949. begin
  950. {$IFDEF PERF}
  951.   FClock.GetTime(int64(NowTime));
  952.   MSR_INTEGER(0, Integer(NowTime));
  953.   t := RefTimeToMiliSec(NowTime - FRenderStart); // convert UNITS->msec
  954.   MSR_INTEGER(FBaseRenderTime, t);
  955. {$ENDIF}
  956. end;
  957. function TBCBaseRenderer.OnStartStreaming: HResult;
  958. begin
  959.   Result := NOERROR;
  960. end;
  961. function TBCBaseRenderer.OnStopStreaming: HResult;
  962. begin
  963.   Result := NOERROR;
  964. end;
  965. procedure TBCBaseRenderer.OnWaitStart;
  966. begin
  967. end;
  968. procedure TBCBaseRenderer.OnWaitEnd;
  969. begin
  970. end;
  971. procedure TBCBaseRenderer.PrepareRender;
  972. begin
  973. end;
  974. // Constructor must be passed the base renderer object
  975. constructor TBCRendererInputPin.Create(Renderer: TBCBaseRenderer;
  976.   out hr: HResult; Name: PWideChar);
  977. begin
  978.   inherited Create('Renderer pin', Renderer, Renderer.FInterfaceLock,
  979.     hr, Name);
  980.   FRenderer := Renderer;
  981.   Assert(Assigned(FRenderer));
  982. end;
  983. // Signals end of data stream on the input pin
  984. function TBCRendererInputPin.EndOfStream: HResult;
  985. begin
  986.   FRenderer.FInterfaceLock.Lock;
  987.   FRenderer.FRendererLock.Lock;
  988.   try
  989.     // Make sure we're streaming ok
  990.     Result := CheckStreaming;
  991.     if (Result <> NOERROR) then
  992.       Exit;
  993.     // Pass it onto the renderer
  994.     Result := FRenderer.EndOfStream;
  995.     if Succeeded(Result) then
  996.       Result := inherited EndOfStream;
  997.   finally
  998.     FRenderer.FRendererLock.UnLock;
  999.     FRenderer.FInterfaceLock.UnLock;
  1000.   end;
  1001. end;
  1002. // Signals start of flushing on the input pin - we do the final reset end of
  1003. // stream with the renderer lock unlocked but with the interface lock locked
  1004. // We must do this because we call timeKillEvent, our timer callback method
  1005. // has to take the renderer lock to serialise our state. Therefore holding a
  1006. // renderer lock when calling timeKillEvent could cause a deadlock condition
  1007. function TBCRendererInputPin.BeginFlush: HResult;
  1008. begin
  1009.   FRenderer.FInterfaceLock.Lock;
  1010.   try
  1011.     FRenderer.FRendererLock.Lock;
  1012.     try
  1013.       inherited BeginFlush;
  1014.       FRenderer.BeginFlush;
  1015.     finally
  1016.       FRenderer.FRendererLock.UnLock;
  1017.     end;
  1018.     Result := FRenderer.ResetEndOfStream;
  1019.   finally
  1020.     FRenderer.FInterfaceLock.UnLock;
  1021.   end;
  1022. end;
  1023. // Signals end of flushing on the input pin
  1024. function TBCRendererInputPin.EndFlush: HResult;
  1025. begin
  1026.   FRenderer.FInterfaceLock.Lock;
  1027.   FRenderer.FRendererLock.Lock;
  1028.   try
  1029.     Result := FRenderer.EndFlush;
  1030.     if Succeeded(Result) then
  1031.       Result := inherited EndFlush;
  1032.   finally
  1033.     FRenderer.FRendererLock.UnLock;
  1034.     FRenderer.FInterfaceLock.UnLock;
  1035.   end;
  1036. end;
  1037. // Pass the sample straight through to the renderer object
  1038. function TBCRendererInputPin.Receive(MediaSample: IMediaSample): HResult;
  1039. var
  1040.   hr: HResult;
  1041. begin
  1042.   hr := FRenderer.Receive(MediaSample);
  1043.   if Failed(hr) then
  1044.   begin
  1045.     // A deadlock could occur if the caller holds the renderer lock and
  1046.     // attempts to acquire the interface lock.
  1047.     {$IFDEF DEBUG}
  1048.     Assert(FRenderer.FRendererLock.CritCheckOut);
  1049.     {$ENDIF}
  1050.     // The interface lock must be held when the filter is calling
  1051.     // IsStopped or IsFlushing.  The interface lock must also
  1052.     // be held because the function uses m_bRunTimeError.
  1053.     FRenderer.FInterfaceLock.Lock;
  1054.     try
  1055.       // We do not report errors which occur while the filter is stopping,
  1056.       // flushing or if the FAborting flag is set .  Errors are expected to
  1057.       // occur during these operations and the streaming thread correctly
  1058.       // handles the errors.
  1059.       if (not IsStopped) and (not IsFlushing) and
  1060.         (not FRenderer.FAbort) and
  1061.         (not FRunTimeError) then
  1062.       begin
  1063.         // EC_ERRORABORT's first parameter is the error which caused
  1064.         // the event and its' last parameter is 0.  See the Direct
  1065.         // Show SDK documentation for more information.
  1066.         FRenderer.NotifyEvent(EC_ERRORABORT, hr, 0);
  1067.         FRenderer.FRendererLock.Lock;
  1068.         try
  1069.           if (FRenderer.IsStreaming and
  1070.             (not FRenderer.IsEndOfStreamDelivered)) then
  1071.             FRenderer.NotifyEndOfStream;
  1072.         finally
  1073.           FRenderer.FRendererLock.UnLock;
  1074.         end;
  1075.         FRunTimeError := True;
  1076.       end;
  1077.     finally
  1078.       FRenderer.FInterfaceLock.UnLock;
  1079.     end;
  1080.   end;
  1081.   Result := hr;
  1082. end;
  1083. function TBCRendererInputPin.InheritedReceive(MediaSample: IMediaSample): HResult;
  1084. begin
  1085.   Result := Inherited Receive(MediaSample);
  1086. end;
  1087. // Called when the input pin is disconnected
  1088. function TBCRendererInputPin.BreakConnect: HResult;
  1089. begin
  1090.   Result := FRenderer.BreakConnect;
  1091.   if Succeeded(Result) then
  1092.     Result := inherited BreakConnect;
  1093. end;
  1094. // Called when the input pin is connected
  1095. function TBCRendererInputPin.CompleteConnect(ReceivePin: IPin): HResult;
  1096. begin
  1097.   Result := FRenderer.CompleteConnect(ReceivePin);
  1098.   if Succeeded(Result) then
  1099.     Result := inherited CompleteConnect(ReceivePin);
  1100. end;
  1101. // Give the pin id of our one and only pin
  1102. function TBCRendererInputPin.QueryId(out Id: PWideChar): HRESULT;
  1103. begin
  1104. // milenko start (AMGetWideString bugged before, so this call only will do fine now) 
  1105.   Result := AMGetWideString('In', Id);
  1106. // milenko end
  1107. end;
  1108. // Will the filter accept this media type
  1109. function TBCRendererInputPin.CheckMediaType(MediaType: PAMMediaType): HResult;
  1110. begin
  1111.   Result := FRenderer.CheckMediaType(MediaType);
  1112. end;
  1113. // Called when we go paused or running
  1114. function TBCRendererInputPin.Active: HResult;
  1115. begin
  1116.   Result := FRenderer.Active;
  1117. end;
  1118. // Called when we go into a stopped state
  1119. function TBCRendererInputPin.Inactive: HResult;
  1120. begin
  1121.   // The caller must hold the interface lock because
  1122.   // this function uses FRunTimeError.
  1123.   {$IFDEF DEBUG}
  1124.   Assert(FRenderer.FInterfaceLock.CritCheckIn);
  1125.   {$ENDIF}
  1126.   FRunTimeError := False;
  1127.   Result := FRenderer.Inactive;
  1128. end;
  1129. // Tell derived classes about the media type agreed
  1130. function TBCRendererInputPin.SetMediaType(MediaType: PAMMediaType): HResult;
  1131. begin
  1132.   Result := inherited SetMediaType(MediaType);
  1133.   if Succeeded(Result) then
  1134.     Result := FRenderer.SetMediaType(MediaType);
  1135. end;
  1136. // We do not keep an event object to use when setting up a timer link with
  1137. // the clock but are given a pointer to one by the owning object through the
  1138. // SetNotificationObject method - this must be initialised before starting
  1139. // We can override the default quality management process to have it always
  1140. // draw late frames, this is currently done by having the following registry
  1141. // key (actually an INI key) called DrawLateFrames set to 1 (default is 0)
  1142. (* ???
  1143. const TCHAR AMQUALITY[] = TEXT("ActiveMovie");
  1144. const TCHAR DRAWLATEFRAMES[] = TEXT("DrawLateFrames");
  1145. *)
  1146. resourcestring
  1147.   AMQUALITY       = 'ActiveMovie';
  1148.   DRAWLATEFRAMES  = 'DrawLateFrames';
  1149. constructor TBCBaseVideoRenderer.Create(RenderClass: TGUID; Name: PChar;
  1150.   Unk: IUnknown; hr: HResult);
  1151. begin
  1152. // milenko start (not sure if this is really needed, but looks better)
  1153. //  inherited;
  1154.   inherited Create(RenderClass,Name,Unk,hr);
  1155. // milenko end
  1156.   FFramesDropped          := 0;
  1157.   FFramesDrawn            := 0;
  1158.   FSupplierHandlingQuality:= False;
  1159.   ResetStreamingTimes;
  1160. {$IFDEF PERF}
  1161.   FTimeStamp      := MSR_REGISTER('Frame time stamp');
  1162.   FEarliness      := MSR_REGISTER('Earliness fudge');
  1163.   FTarget         := MSR_REGISTER('Target(mSec)');
  1164.   FSchLateTime    := MSR_REGISTER('mSec late when scheduled');
  1165.   FDecision       := MSR_REGISTER('Scheduler decision code');
  1166.   FQualityRate    := MSR_REGISTER('Quality rate sent');
  1167.   FQualityTime    := MSR_REGISTER('Quality time sent');
  1168.   FWaitReal       := MSR_REGISTER('Render wait');
  1169.   FWait           := MSR_REGISTER('wait time recorded (msec)');
  1170.   FFrameAccuracy  := MSR_REGISTER('Frame accuracy(msecs)');
  1171.   FDrawLateFrames := Boolean(GetProfileInt(PChar(AMQUALITY),
  1172.     PChar(DRAWLATEFRAMES), Integer(False)));
  1173.   FSendQuality    := MSR_REGISTER('Processing Quality message');
  1174.   FRenderAvg      := MSR_REGISTER('Render draw time Avg');
  1175.   FFrameAvg       := MSR_REGISTER('FrameAvg');
  1176.   FWaitAvg        := MSR_REGISTER('WaitAvg');
  1177.   FDuration       := MSR_REGISTER('Duration');
  1178.   FThrottle       := MSR_REGISTER('Audio - video throttle wait');
  1179.   FDebug          := MSR_REGISTER('Debug stuff');
  1180. {$ENDIF}
  1181. end;
  1182. // Destructor is just a placeholder
  1183. destructor TBCBaseVideoRenderer.Destroy;
  1184. begin
  1185.   Assert(FAdvisedCookie = 0);
  1186.   // ??? seems should leave it, but...
  1187. // milenko start (not really needed...)
  1188. //  inherited;
  1189.   inherited Destroy;
  1190. // milenko end
  1191. end;
  1192. // The timing functions in this class are called by the window object and by
  1193. // the renderer's allocator.
  1194. // The windows object calls timing functions as it receives media sample
  1195. // images for drawing using GDI.
  1196. // The allocator calls timing functions when it starts passing DCI/DirectDraw
  1197. // surfaces which are not rendered in the same way; The decompressor writes
  1198. // directly to the surface with no separate rendering, so those code paths
  1199. // call direct into us.  Since we only ever hand out DCI/DirectDraw surfaces
  1200. // when we have allocated one and only one image we know there cannot be any
  1201. // conflict between the two.
  1202. //
  1203. // We use timeGetTime to return the timing counts we use (since it's relative
  1204. // performance we are interested in rather than absolute compared to a clock)
  1205. // The window object sets the accuracy of the system clock (normally 1ms) by
  1206. // calling timeBeginPeriod/timeEndPeriod when it changes streaming states
  1207. // Reset all times controlling streaming.
  1208. // Set them so that
  1209. // 1. Frames will not initially be dropped
  1210. // 2. The first frame will definitely be drawn (achieved by saying that there
  1211. //    has not ben a frame drawn for a long time).
  1212. function TBCBaseVideoRenderer.ResetStreamingTimes: HResult;
  1213. begin
  1214.   FLastDraw := -1000; // set up as first frame since ages (1 sec) ago
  1215.   FStreamingStart := timeGetTime;
  1216.   FRenderAvg := 0;
  1217.   FFrameAvg := -1; // -1000 fps :=:= "unset"
  1218.   FDuration := 0; // 0 - strange value
  1219.   FRenderLast := 0;
  1220.   FWaitAvg := 0;
  1221.   FRenderStart := 0;
  1222.   FFramesDrawn := 0;
  1223.   FFramesDropped := 0;
  1224.   FTotAcc := 0;
  1225.   FSumSqAcc := 0;
  1226.   FSumSqFrameTime := 0;
  1227.   FFrame := 0; // hygiene - not really needed
  1228.   FLate := 0; // hygiene - not really needed
  1229.   FSumFrameTime := 0;
  1230.   FNormal := 0;
  1231.   FEarliness := 0;
  1232.   FTarget := -300000; // 30mSec early
  1233.   FThrottle := 0;
  1234.   FRememberStampForPerf := 0;
  1235. {$IFDEF PERF}
  1236.   FRememberFrameForPerf := 0;
  1237. {$ENDIF}
  1238.   Result := NOERROR;
  1239. end;
  1240. // Reset all times controlling streaming. Note that we're now streaming. We
  1241. // don't need to set the rendering event to have the source filter released
  1242. // as it is done during the Run processing. When we are run we immediately
  1243. // release the source filter thread and draw any image waiting (that image
  1244. // may already have been drawn once as a poster frame while we were paused)
  1245. function TBCBaseVideoRenderer.OnStartStreaming: HResult;
  1246. begin
  1247.   ResetStreamingTimes;
  1248.   Result := NOERROR;
  1249. end;
  1250. // Called at end of streaming.  Fixes times for property page report
  1251. function TBCBaseVideoRenderer.OnStopStreaming: HResult;
  1252. begin
  1253. // milenko start (better to use int64 instead of integer)
  1254. //  FStreamingStart := Integer(timeGetTime) - FStreamingStart;
  1255.   FStreamingStart := Int64(timeGetTime) - FStreamingStart;
  1256. // milenko end
  1257.   Result := NOERROR;
  1258. end;
  1259. // Called when we start waiting for a rendering event.
  1260. // Used to update times spent waiting and not waiting.
  1261. procedure TBCBaseVideoRenderer.OnWaitStart;
  1262. begin
  1263. {$IFDEF PERF}
  1264.   MSR_START(FWaitReal);
  1265. {$ENDIF}
  1266. end;
  1267. // Called when we are awoken from the wait in the window OR by our allocator
  1268. // when it is hanging around until the next sample is due for rendering on a
  1269. // DCI/DirectDraw surface. We add the wait time into our rolling average.
  1270. // We grab the interface lock so that we're serialised with the application
  1271. // thread going through the run code - which in due course ends up calling
  1272. // ResetStreaming times - possibly as we run through this section of code
  1273. procedure TBCBaseVideoRenderer.OnWaitEnd;
  1274. {$IFDEF PERF}
  1275. var
  1276.   RealStream, RefTime: TReferenceTime;
  1277.   // the real time now expressed as stream time.
  1278.   Late, Frame: Integer;
  1279. {$ENDIF}
  1280. begin
  1281. {$IFDEF PERF}
  1282.   MSR_STOP(FWaitReal);
  1283.   // for a perf build we want to know just exactly how late we REALLY are.
  1284.   // even if this means that we have to look at the clock again.
  1285. {$IFDEF 0}
  1286.   FClock.GetTime(RealStream); // Calling clock here causes W95 deadlock!
  1287. {$ELSE}
  1288.   // We will be discarding overflows like mad here!
  1289.   // This is wrong really because timeGetTime() can wrap but it's
  1290.   // only for PERF
  1291.   RefTime := timeGetTime * 10000;
  1292.   RealStream := RefTime + FTimeOffset;
  1293. {$ENDIF}
  1294.   Dec(RealStream, FStart); // convert to stream time (this is a reftime)
  1295.   if (FRememberStampForPerf = 0) then
  1296.     // This is probably the poster frame at the start, and it is not scheduled
  1297.     // in the usual way at all.  Just count it.  The rememberstamp gets set
  1298.     // in ShouldDrawSampleNow, so this does invalid frame recording until we
  1299.     // actually start playing.
  1300.     PreparePerformanceData(0, 0)
  1301.   else
  1302.   begin
  1303.     Late := RealStream - FRememberStampForPerf;
  1304.     Frame := RefTime - FRememberFrameForPerf;
  1305.     PreparePerformanceData(Late, Frame);
  1306.   end;
  1307.   FRememberFrameForPerf := RefTime;
  1308. {$ENDIF}
  1309. end;
  1310. // Put data on one side that describes the lateness of the current frame.
  1311. // We don't yet know whether it will actually be drawn.  In direct draw mode,
  1312. // this decision is up to the filter upstream, and it could change its mind.
  1313. // The rules say that if it did draw it must call Receive().  One way or
  1314. // another we eventually get into either OnRenderStart or OnDirectRender and
  1315. // these both call RecordFrameLateness to update the statistics.
  1316. procedure TBCBaseVideoRenderer.PreparePerformanceData(Late, Frame: Integer);
  1317. begin
  1318.   FLate := Late;
  1319.   FFrame := Frame;
  1320. end;
  1321. // update the statistics:
  1322. // m_iTotAcc, m_iSumSqAcc, m_iSumSqFrameTime, m_iSumFrameTime, m_cFramesDrawn
  1323. // Note that because the properties page reports using these variables,
  1324. // 1. We need to be inside a critical section
  1325. // 2. They must all be updated together.  Updating the sums here and the count
  1326. // elsewhere can result in imaginary jitter (i.e. attempts to find square roots
  1327. // of negative numbers) in the property page code.
  1328. procedure TBCBaseVideoRenderer.RecordFrameLateness(Late, Frame: Integer);
  1329. var
  1330.   _Late, _Frame: Integer;
  1331. begin
  1332.   // Record how timely we are.
  1333.   _Late := Late div 10000;
  1334.   // Best estimate of moment of appearing on the screen is average of
  1335.   // start and end draw times.  Here we have only the end time.  This may
  1336.   // tend to show us as spuriously late by up to 1/2 frame rate achieved.
  1337.   // Decoder probably monitors draw time.  We don't bother.
  1338. {$IFDEF PERF}
  1339.   MSR_INTEGER(FFrameAccuracy, _Late);
  1340. {$ENDIF}
  1341.   // This is a kludge - we can get frames that are very late
  1342.   // especially (at start-up) and they invalidate the statistics.
  1343.   // So ignore things that are more than 1 sec off.
  1344.   if (_Late > 1000) or (_Late < -1000) then
  1345.     if (FFramesDrawn <= 1) then
  1346.       _Late := 0
  1347.     else if (_Late > 0) then
  1348.       _Late := 1000
  1349.     else
  1350.       _Late := -1000;
  1351.   // The very first frame often has a invalid time, so don't
  1352.   // count it into the statistics.   (???)
  1353.   if (FFramesDrawn > 1) then
  1354.   begin
  1355.     Inc(FTotAcc, _Late);
  1356.     Inc(FSumSqAcc, _Late * _Late);
  1357.   end;
  1358.   // calculate inter-frame time.  Doesn't make sense for first frame
  1359.   // second frame suffers from invalid first frame stamp.
  1360.   if (FFramesDrawn > 2) then
  1361.   begin
  1362.     _Frame := Frame div 10000; // convert to mSec else it overflows
  1363.     // This is a kludge.  It can overflow anyway (a pause can cause
  1364.     // a very long inter-frame time) and it overflows at 2**31/10**7
  1365.     // or about 215 seconds i.e. 3min 35sec
  1366.     if (_Frame > 1000) or (_Frame < 0) then
  1367.       _Frame := 1000;
  1368.     Inc(FSumSqFrameTime, _Frame * _Frame);
  1369.     Assert(FSumSqFrameTime >= 0);
  1370.     Inc(FSumFrameTime, _Frame);
  1371.   end;
  1372.   Inc(FFramesDrawn);
  1373. end;
  1374. procedure TBCBaseVideoRenderer.ThrottleWait;
  1375. var
  1376.   Throttle: Integer;
  1377. begin
  1378.   if (FThrottle > 0) then
  1379.   begin
  1380.     Throttle := FThrottle div 10000; // convert to mSec
  1381.     MSR_INTEGER(FThrottle, Throttle);
  1382.     {$IFDEF DEBUG}
  1383.     DbgLog(Self, Format('Throttle %d ms', [Throttle]));
  1384.     {$ENDIF}
  1385.     Sleep(Throttle);
  1386.   end
  1387.   else
  1388.     Sleep(0);
  1389. end;
  1390. // Whenever a frame is rendered it goes though either OnRenderStart
  1391. // or OnDirectRender.  Data that are generated during ShouldDrawSample
  1392. // are added to the statistics by calling RecordFrameLateness from both
  1393. // these two places.
  1394. // Called in place of OnRenderStart..OnRenderEnd
  1395. // When a DirectDraw image is drawn
  1396. procedure TBCBaseVideoRenderer.OnDirectRender(MediaSample: IMediaSample);
  1397. begin
  1398.   FRenderAvg := 0;
  1399.   FRenderLast := 5000000; // If we mode switch, we do NOT want this
  1400.   // to inhibit the new average getting going!
  1401.   // so we set it to half a second
  1402. // MSR_INTEGER(m_idRenderAvg, m_trRenderAvg div 10000);
  1403.   RecordFrameLateness(FLate, FFrame);
  1404.   ThrottleWait;
  1405. end;
  1406. // Called just before we start drawing.  All we do is to get the current clock
  1407. // time (from the system) and return.  We have to store the start render time
  1408. // in a member variable because it isn't used until we complete the drawing
  1409. // The rest is just performance logging.
  1410. procedure TBCBaseVideoRenderer.OnRenderStart(MediaSample: IMediaSample);
  1411. begin
  1412.   RecordFrameLateness(FLate, FFrame);
  1413.   FRenderStart := timeGetTime;
  1414. end;
  1415. // Called directly after drawing an image.  We calculate the time spent in the
  1416. // drawing code and if this doesn't appear to have any odd looking spikes in
  1417. // it then we add it to the current average draw time.  Measurement spikes may
  1418. // occur if the drawing thread is interrupted and switched to somewhere else.
  1419. procedure TBCBaseVideoRenderer.OnRenderEnd(MediaSample: IMediaSample);
  1420. var
  1421.   RefTime: Integer;
  1422. begin
  1423.   // The renderer time can vary erratically if we are interrupted so we do
  1424.   // some smoothing to help get more sensible figures out but even that is
  1425.   // not enough as figures can go 9,10,9,9,83,9 and we must disregard 83
  1426. // milenko start
  1427. //  RefTime := (Integer(timeGetTime) - FRenderStart) * 10000;
  1428.   RefTime := (Int64(timeGetTime) - FRenderStart) * 10000;
  1429. // milenko end
  1430.   // convert mSec->UNITS
  1431.   if (RefTime < FRenderAvg * 2) or (RefTime < 2 * FRenderLast) then
  1432.     // DO_MOVING_AVG(m_trRenderAvg, tr);
  1433.     FRenderAvg := (RefTime + (AVGPERIOD - 1) * FRenderAvg) div AVGPERIOD;
  1434.   FRenderLast := RefTime;
  1435.   ThrottleWait;
  1436. end;
  1437. function TBCBaseVideoRenderer.SetSink(QualityControl: IQualityControl): HResult;
  1438. begin
  1439.   FQSink := QualityControl;
  1440.   Result := NOERROR;
  1441. end;
  1442. function TBCBaseVideoRenderer.Notify(Filter: IBaseFilter;
  1443.   Q: TQuality): HResult;
  1444. begin
  1445.   // NOTE:  We are NOT getting any locks here.  We could be called
  1446.   // asynchronously and possibly even on a time critical thread of
  1447.   // someone else's - so we do the minumum.  We only set one state
  1448.   // variable (an integer) and if that happens to be in the middle
  1449.   // of another thread reading it they will just get either the new
  1450.   // or the old value.  Locking would achieve no more than this.
  1451.   // It might be nice to check that we are being called from m_pGraph, but
  1452.   // it turns out to be a millisecond or so per throw!
  1453.   // This is heuristics, these numbers are aimed at being "what works"
  1454.   // rather than anything based on some theory.
  1455.   // We use a hyperbola because it's easy to calculate and it includes
  1456.   // a panic button asymptote (which we push off just to the left)
  1457.   // The throttling fits the following table (roughly)
  1458.   // Proportion   Throttle (msec)
  1459.   //     >=1000         0
  1460.   //        900         3
  1461.   //        800         7
  1462.   //        700        11
  1463.   //        600        17
  1464.   //        500        25
  1465.   //        400        35
  1466.   //        300        50
  1467.   //        200        72
  1468.   //        125       100
  1469.   //        100       112
  1470.   //         50       146
  1471.   //          0       200
  1472.   // (some evidence that we could go for a sharper kink - e.g. no throttling
  1473.   // until below the 750 mark - might give fractionally more frames on a
  1474.   // P60-ish machine).  The easy way to get these coefficients is to use
  1475.   // Renbase.xls follow the instructions therein using excel solver.
  1476.   if (q.Proportion >= 1000) then
  1477.     FThrottle := 0
  1478.   else
  1479.     // The DWORD is to make quite sure I get unsigned arithmetic
  1480.     // as the constant is between 2**31 and 2**32
  1481.     FThrottle := -330000 + (388880000 div (q.Proportion + 167));
  1482.   Result := NOERROR;
  1483. end;
  1484. // Send a message to indicate what our supplier should do about quality.
  1485. // Theory:
  1486. // What a supplier wants to know is "is the frame I'm working on NOW
  1487. // going to be late?".
  1488. // F1 is the frame at the supplier (as above)
  1489. // Tf1 is the due time for F1
  1490. // T1 is the time at that point (NOW!)
  1491. // Tr1 is the time that f1 WILL actually be rendered
  1492. // L1 is the latency of the graph for frame F1 = Tr1-T1
  1493. // D1 (for delay) is how late F1 will be beyond its due time i.e.
  1494. // D1 = (Tr1-Tf1) which is what the supplier really wants to know.
  1495. // Unfortunately Tr1 is in the future and is unknown, so is L1
  1496. //
  1497. // We could estimate L1 by its value for a previous frame,
  1498. // L0 = Tr0-T0 and work off
  1499. // D1' = ((T1+L0)-Tf1) = (T1 + (Tr0-T0) -Tf1)
  1500. // Rearranging terms:
  1501. // D1' = (T1-T0) + (Tr0-Tf1)
  1502. //       adding (Tf0-Tf0) and rearranging again:
  1503. //     = (T1-T0) + (Tr0-Tf0) + (Tf0-Tf1)
  1504. //     = (T1-T0) - (Tf1-Tf0) + (Tr0-Tf0)
  1505. // But (Tr0-Tf0) is just D0 - how late frame zero was, and this is the
  1506. // Late field in the quality message that we send.
  1507. // The other two terms just state what correction should be applied before
  1508. // using the lateness of F0 to predict the lateness of F1.
  1509. // (T1-T0) says how much time has actually passed (we have lost this much)
  1510. // (Tf1-Tf0) says how much time should have passed if we were keeping pace
  1511. // (we have gained this much).
  1512. //
  1513. // Suppliers should therefore work off:
  1514. //    Quality.Late + (T1-T0)  - (Tf1-Tf0)
  1515. // and see if this is "acceptably late" or even early (i.e. negative).
  1516. // They get T1 and T0 by polling the clock, they get Tf1 and Tf0 from
  1517. // the time stamps in the frames.  They get Quality.Late from us.
  1518. //
  1519. function TBCBaseVideoRenderer.SendQuality(Late,
  1520.   RealStream: TReferenceTime): HResult;
  1521. var
  1522.   q: TQuality;
  1523.   hr: HResult;
  1524.   QC: IQualityControl;
  1525.   OutputPin: IPin;
  1526. begin
  1527.   // If we are the main user of time, then report this as Flood/Dry.
  1528.   // If our suppliers are, then report it as Famine/Glut.
  1529.   //
  1530.   // We need to take action, but avoid hunting.  Hunting is caused by
  1531.   // 1. Taking too much action too soon and overshooting
  1532.   // 2. Taking too long to react (so averaging can CAUSE hunting).
  1533.   //
  1534.   // The reason why we use trLate as well as Wait is to reduce hunting;
  1535.   // if the wait time is coming down and about to go into the red, we do
  1536.   // NOT want to rely on some average which is only telling is that it used
  1537.   // to be OK once.
  1538.   q.TimeStamp := RealStream;
  1539.   if (FFrameAvg < 0) then
  1540.     q.Typ := Famine // guess
  1541.     // Is the greater part of the time taken bltting or something else
  1542.   else if (FFrameAvg > 2 * FRenderAvg) then
  1543.     q.Typ := Famine // mainly other
  1544.   else
  1545.     q.Typ := Flood; // mainly bltting
  1546.   q.Proportion := 1000; // default
  1547.   if (FFrameAvg < 0) then
  1548.     // leave it alone - we don't know enough
  1549.   else if (Late > 0) then
  1550.   begin
  1551.     // try to catch up over the next second
  1552.     // We could be Really, REALLY late, but rendering all the frames
  1553.     // anyway, just because it's so cheap.
  1554.     q.Proportion := 1000 - (Late div (UNITS div 1000));
  1555.     if (q.Proportion < 500) then
  1556.       q.Proportion := 500; // don't go daft. (could've been negative!)
  1557.   end
  1558. // milenko start
  1559.   else if (FWaitAvg > 20000) and (Late < -20000) then
  1560.   begin
  1561. //    if (FWaitAvg > 20000) and (Late < -20000) then
  1562.       // Go cautiously faster - aim at 2mSec wait.
  1563.     if (FWaitAvg >= FFrameAvg) then
  1564.     begin
  1565.       // This can happen because of some fudges.
  1566.       // The waitAvg is how long we originally planned to wait
  1567.       // The frameAvg is more honest.
  1568.       // It means that we are spending a LOT of time waiting
  1569.       q.Proportion := 2000 // double.
  1570.     end else
  1571.     begin
  1572.       if (FFrameAvg + 20000 > FWaitAvg) then
  1573.         q.Proportion := 1000 * (FFrameAvg div (FFrameAvg + 20000 - FWaitAvg))
  1574.       else
  1575.       // We're apparently spending more than the whole frame time waiting.
  1576.       // Assume that the averages are slightly out of kilter, but that we
  1577.       // are indeed doing a lot of waiting.  (This leg probably never
  1578.       // happens, but the code avoids any potential divide by zero).
  1579.       q.Proportion := 2000;
  1580.     end;
  1581.     if (q.Proportion > 2000) then
  1582.       q.Proportion := 2000; // don't go crazy.
  1583.   end;
  1584. // milenko end
  1585.   // Tell the supplier how late frames are when they get rendered
  1586.   // That's how late we are now.
  1587.   // If we are in directdraw mode then the guy upstream can see the drawing
  1588.   // times and we'll just report on the start time.  He can figure out any
  1589.   // offset to apply.  If we are in DIB Section mode then we will apply an
  1590.   // extra offset which is half of our drawing time.  This is usually small
  1591.   // but can sometimes be the dominant effect.  For this we will use the
  1592.   // average drawing time rather than the last frame.  If the last frame took
  1593.   // a long time to draw and made us late, that's already in the lateness
  1594.   // figure.  We should not add it in again unless we expect the next frame
  1595.   // to be the same.  We don't, we expect the average to be a better shot.
  1596.   // In direct draw mode the RenderAvg will be zero.
  1597.   q.Late := Late + FRenderAvg div 2;
  1598. {$IFDEF PERF}
  1599.   // log what we're doing
  1600.   MSR_INTEGER(FQualityRate, q.Proportion);
  1601.   MSR_INTEGER(FQualityTime, refTimeToMiliSec(q.Late));
  1602. {$ENDIF}
  1603.   // A specific sink interface may be set through IPin
  1604.   if (FQSink = nil) then
  1605.   begin
  1606.     // Get our input pin's peer.  We send quality management messages
  1607.     // to any nominated receiver of these things (set in the IPin
  1608.     // interface), or else to our source filter.
  1609.     QC := nil;
  1610.     OutputPin := FInputPin.GetConnected;
  1611.     Assert(Assigned(OutputPin));
  1612.     // And get an AddRef'd quality control interface
  1613.     hr := OutputPin.QueryInterface(IID_IQualityControl, QC);
  1614.     if Succeeded(hr) then
  1615.       FQSink := QC;
  1616.   end;
  1617.   if Assigned(FQSink) then
  1618.     Result := FQSink.Notify(Self, q)
  1619.   else
  1620.     Result := S_FALSE;
  1621. end;
  1622. // We are called with a valid IMediaSample image to decide whether this is to
  1623. // be drawn or not.  There must be a reference clock in operation.
  1624. // Return S_OK if it is to be drawn Now (as soon as possible)
  1625. // Return S_FALSE if it is to be drawn when it's due
  1626. // Return an error if we want to drop it
  1627. // m_nNormal=-1 indicates that we dropped the previous frame and so this
  1628. // one should be drawn early.  Respect it and update it.
  1629. // Use current stream time plus a number of heuristics (detailed below)
  1630. // to make the decision
  1631. (* ??? StartTime is changing inside routine:
  1632. Inc(StartTime, E); // N.B. earliness is negative
  1633. So, maybe it should be declared as var or out?
  1634. *)
  1635. function TBCBaseVideoRenderer.ShouldDrawSampleNow(MediaSample: IMediaSample;
  1636.   StartTime: TReferenceTime; out EndTime: TReferenceTime): HResult;
  1637. var
  1638.   RealStream: TReferenceTime; // the real time now expressed as stream time.
  1639.   RefTime: TReferenceTime;
  1640.   TrueLate, Late, Duration, t, WaitAvg, L, Frame, E, Delay
  1641.   {$IFNDEF PERF} , Accuracy{$ENDIF}: Integer;
  1642.   hr: HResult;
  1643.   JustDroppedFrame, Res, PlayASAP: Boolean;
  1644. begin
  1645.   // Don't call us unless there's a clock interface to synchronise with
  1646.   Assert(Assigned(FClock));
  1647. {$IFDEF PERF}
  1648.   MSR_INTEGER(FTimeStamp, Integer(StartTime shr 32));   // high order 32 bits
  1649.   MSR_INTEGER(FTimeStamp, Integer(StartTime));          // low order 32 bits
  1650. {$ENDIF}
  1651.   // We lose a bit of time depending on the monitor type waiting for the next
  1652.   // screen refresh.  On average this might be about 8mSec - so it will be
  1653.   // later than we think when the picture appears.  To compensate a bit
  1654.   // we bias the media samples by -8mSec i.e. 80000 UNITs.
  1655.   // We don't ever make a stream time negative (call it paranoia)
  1656.   if (StartTime >= 80000) then
  1657.   begin
  1658.     Dec(StartTime, 80000);
  1659.     Dec(EndTime, 80000); // bias stop to to retain valid frame duration
  1660.   end;
  1661.   // Cache the time stamp now.  We will want to compare what we did with what
  1662.   // we started with (after making the monitor allowance).
  1663.   FRememberStampForPerf := StartTime;
  1664.   // Get reference times (current and late)
  1665.   FClock.GetTime(int64(RealStream));
  1666. {$IFDEF PERF}
  1667.   // While the reference clock is expensive:
  1668.   // Remember the offset from timeGetTime and use that.
  1669.   // This overflows all over the place, but when we subtract to get
  1670.   // differences the overflows all cancel out.
  1671.   FTimeOffset := RealStream - timeGetTime * 10000;
  1672. {$ENDIF}
  1673.   Dec(RealStream, FStart); // convert to stream time (this is a reftime)
  1674.   // We have to wory about two versions of "lateness".  The truth, which we
  1675.   // try to work out here and the one measured against m_trTarget which
  1676.   // includes long term feedback.  We report statistics against the truth
  1677.   // but for operational decisions we work to the target.
  1678.   // We use TimeDiff to make sure we get an integer because we
  1679.   // may actually be late (or more likely early if there is a big time
  1680.   // gap) by a very long time.
  1681.   TrueLate := TimeDiff(RealStream - StartTime);
  1682.   Late := TrueLate;
  1683. {$IFDEF PERF}
  1684.   MSR_INTEGER(FSchLateTime, refTimeToMiliSec(TrueLate));
  1685. {$ENDIF}
  1686.   // Send quality control messages upstream, measured against target
  1687.   hr := SendQuality(Late, RealStream);
  1688.   // Note: the filter upstream is allowed to this FAIL meaning "you do it".
  1689.   FSupplierHandlingQuality := (hr = S_OK);
  1690.   // Decision time!  Do we drop, draw when ready or draw immediately?
  1691.   Duration := EndTime - StartTime;
  1692.   // We need to see if the frame rate of the file has just changed.
  1693.   // This would make comparing our previous frame rate with the current
  1694.   // frame rate inefficent.  Hang on a moment though.  I've seen files
  1695.   // where the frames vary between 33 and 34 mSec so as to average
  1696.   // 30fps.  A minor variation like that won't hurt us.
  1697.   t := FDuration div 32;
  1698.   if (Duration > FDuration + t) or (Duration < FDuration - t) then
  1699.   begin
  1700.     // There's a major variation.  Reset the average frame rate to
  1701.     // exactly the current rate to disable decision 9002 for this frame,
  1702.     // and remember the new rate.
  1703.     FFrameAvg := Duration;
  1704.     FDuration := Duration;
  1705.   end;
  1706. {$IFDEF PERF}
  1707.   MSR_INTEGER(FEarliness, refTimeToMiliSec(FEarliness));
  1708.   MSR_INTEGER(FRenderAvg, refTimeToMiliSec(FRenderAvg));
  1709.   MSR_INTEGER(FFrameAvg, refTimeToMiliSec(FFrameAvg));
  1710.   MSR_INTEGER(FWaitAvg, refTimeToMiliSec(FWaitAvg));
  1711.   MSR_INTEGER(FDuration, refTimeToMiliSec(FDuration));
  1712.   if (S_OK = MediaSample.IsDiscontinuity) then
  1713.     MSR_INTEGER(FDecision, 9000);
  1714. {$ENDIF}
  1715.   // Control the graceful slide back from slow to fast machine mode.
  1716.   // After a frame drop accept an early frame and set the earliness to here
  1717.   // If this frame is already later than the earliness then slide it to here
  1718.   // otherwise do the standard slide (reduce by about 12% per frame).
  1719.   // Note: earliness is normally NEGATIVE
  1720.   JustDroppedFrame :=
  1721.     (FSupplierHandlingQuality and
  1722.     //  Can't use the pin sample properties because we might
  1723.     //  not be in Receive when we call this
  1724.     (S_OK = MediaSample.IsDiscontinuity) // he just dropped one
  1725.     ) or
  1726.     (FNormal = -1); // we just dropped one
  1727.   // Set m_trEarliness (slide back from slow to fast machine mode)
  1728.   if (Late > 0) then
  1729.     FEarliness := 0 // we are no longer in fast machine mode at all!
  1730.   else if ((Late >= FEarliness) or JustDroppedFrame) then
  1731.     FEarliness := Late // Things have slipped of their own accord
  1732.   else
  1733.     FEarliness := FEarliness - FEarliness div 8; // graceful slide
  1734.   // prepare the new wait average - but don't pollute the old one until
  1735.   // we have finished with it.
  1736.   // We never mix in a negative wait.  This causes us to believe in fast machines
  1737.   // slightly more.
  1738.   if (Late < 0) then
  1739.     L := -Late
  1740.   else
  1741.     L := 0;
  1742.   WaitAvg := (L + FWaitAvg * (AVGPERIOD - 1)) div AVGPERIOD;
  1743.   RefTime := RealStream - FLastDraw; // Cd be large - 4 min pause!
  1744.   if (RefTime > 10000000) then
  1745.     RefTime := 10000000; // 1 second - arbitrarily.
  1746.   Frame := RefTime;
  1747.   if FSupplierHandlingQuality then
  1748.     Res := (Late <= Duration * 4)
  1749.   else
  1750.     Res := (Late + Late < Duration);
  1751.   // We will DRAW this frame IF...
  1752.   if (
  1753.     // ...the time we are spending drawing is a small fraction of the total
  1754.     // observed inter-frame time so that dropping it won't help much.
  1755.     (3 * FRenderAvg <= FFrameAvg)
  1756.     // ...or our supplier is NOT handling things and the next frame would
  1757.     // be less timely than this one or our supplier CLAIMS to be handling
  1758.     // things, and is now less than a full FOUR frames late.
  1759.     or Res
  1760.     // ...or we are on average waiting for over eight milliseconds then
  1761.     // this may be just a glitch.  Draw it and we'll hope to catch up.
  1762.     or (FWaitAvg > 80000)
  1763.     // ...or we haven't drawn an image for over a second.  We will update
  1764.     // the display, which stops the video looking hung.
  1765.     // Do this regardless of how late this media sample is.
  1766.     or ((RealStream - FLastDraw) > UNITS)
  1767.     ) then
  1768.   begin
  1769.     // We are going to play this frame.  We may want to play it early.
  1770.     // We will play it early if we think we are in slow machine mode.
  1771.     // If we think we are NOT in slow machine mode, we will still play
  1772.     // it early by m_trEarliness as this controls the graceful slide back.
  1773.     // and in addition we aim at being m_trTarget late rather than "on time".
  1774.     PlayASAP := False;
  1775.     // we will play it AT ONCE (slow machine mode) if...
  1776.     // ...we are playing catch-up
  1777.     if (JustDroppedFrame) then
  1778.     begin
  1779.       PlayASAP := True;
  1780. {$IFDEF PERF}
  1781.       MSR_INTEGER(FDecision, 9001);
  1782. {$ENDIF}
  1783.     end
  1784.       // ...or if we are running below the true frame rate
  1785.       // exact comparisons are glitchy, for these measurements,
  1786.       // so add an extra 5% or so
  1787.     else if (FFrameAvg > Duration + Duration div 16)
  1788.     // It's possible to get into a state where we are losing ground, but
  1789.     // are a very long way ahead.  To avoid this or recover from it
  1790.     // we refuse to play early by more than 10 frames.
  1791.     and (Late > -Duration * 10) then
  1792.     begin
  1793.       PlayASAP := True;
  1794. {$IFDEF PERF}
  1795.       MSR_INTEGER(FDecision, 9002);
  1796. {$ENDIF}
  1797.     end
  1798. {$IFDEF 0}
  1799.       // ...or if we have been late and are less than one frame early
  1800.     else if ((Late + Duration > 0) and
  1801.       (FWaitAvg <= 20000) then
  1802.       begin
  1803.         PlayASAP := True;
  1804. {$IFDEF PERF}
  1805.         MSR_INTEGER(m_idDecision, 9003);
  1806. {$ENDIF}
  1807.       end
  1808. {$ENDIF}
  1809.       ;
  1810.       // We will NOT play it at once if we are grossly early.  On very slow frame
  1811.       // rate movies - e.g. clock.avi - it is not a good idea to leap ahead just
  1812.       // because we got starved (for instance by the net) and dropped one frame
  1813.       // some time or other.  If we are more than 900mSec early, then wait.
  1814.       if (Late < -9000000) then
  1815.         PlayASAP := False;
  1816.       if PlayASAP then
  1817.       begin
  1818.         FNormal := 0;
  1819. {$IFDEF PERF}
  1820.         MSR_INTEGER(FDecision, 0);
  1821. {$ENDIF}
  1822.         // When we are here, we are in slow-machine mode.  trLate may well
  1823.         // oscillate between negative and positive when the supplier is
  1824.         // dropping frames to keep sync.  We should not let that mislead
  1825.         // us into thinking that we have as much as zero spare time!
  1826.         // We just update with a zero wait.
  1827.         FWaitAvg := (FWaitAvg * (AVGPERIOD - 1)) div AVGPERIOD;
  1828.         // Assume that we draw it immediately.  Update inter-frame stats
  1829.         FFrameAvg := (Frame + FFrameAvg * (AVGPERIOD - 1)) div AVGPERIOD;
  1830. {$IFNDEF PERF}
  1831.         // If this is NOT a perf build, then report what we know so far
  1832.         // without looking at the clock any more.  This assumes that we
  1833.         // actually wait for exactly the time we hope to.  It also reports
  1834.         // how close we get to the manipulated time stamps that we now have
  1835.         // rather than the ones we originally started with.  It will
  1836.         // therefore be a little optimistic.  However it's fast.
  1837.         PreparePerformanceData(TrueLate, Frame);
  1838. {$ENDIF}
  1839.         FLastDraw := RealStream;
  1840.         if (FEarliness > Late) then
  1841.           FEarliness := Late; // if we are actually early, this is neg
  1842.         Result := S_OK; // Draw it now
  1843.       end
  1844.       else
  1845.       begin
  1846.         Inc(FNormal);
  1847.         // Set the average frame rate to EXACTLY the ideal rate.
  1848.         // If we are exiting slow-machine mode then we will have caught up
  1849.         // and be running ahead, so as we slide back to exact timing we will
  1850.         // have a longer than usual gap at this point.  If we record this
  1851.         // real gap then we'll think that we're running slow and go back
  1852.         // into slow-machine mode and vever get it straight.
  1853.         FFrameAvg := Duration;
  1854. {$IFDEF PERF}
  1855.         MSR_INTEGER(FDecision, 1);
  1856. {$ENDIF}
  1857.         // Play it early by m_trEarliness and by m_trTarget
  1858.         E := FEarliness;
  1859.         if (E < -FFrameAvg) then
  1860.           E := -FFrameAvg;
  1861.         Inc(StartTime, E); // N.B. earliness is negative
  1862.         Delay := -TrueLate;
  1863.         if (Delay <= 0) then
  1864.           Result := S_OK
  1865.         else
  1866.           Result := S_FALSE; // OK = draw now, FALSE = wait
  1867.         FWaitAvg := WaitAvg;
  1868.         // Predict when it will actually be drawn and update frame stats
  1869.         if (Result = S_FALSE) then // We are going to wait
  1870.         begin
  1871.           {$IFNDEF PERF}
  1872.           Frame := TimeDiff(StartTime - FLastDraw);
  1873.           {$ENDIF}
  1874.           FLastDraw := StartTime;
  1875.         end
  1876.         else
  1877.           // trFrame is already = trRealStream-m_trLastDraw;
  1878.           FLastDraw := RealStream;
  1879. {$IFNDEF PERF}
  1880.         if (Delay > 0) then
  1881.           // Report lateness based on when we intend to play it
  1882.           Accuracy := TimeDiff(StartTime - FRememberStampForPerf)
  1883.         else
  1884.           // Report lateness based on playing it *now*.
  1885.           Accuracy := TrueLate; // trRealStream-RememberStampForPerf;
  1886.         PreparePerformanceData(Accuracy, Frame);
  1887. {$ENDIF}
  1888.       end;
  1889.       Exit;
  1890.   end;
  1891.   // We are going to drop this frame!
  1892.   // Of course in DirectDraw mode the guy upstream may draw it anyway.
  1893.   // This will probably give a large negative wack to the wait avg.
  1894.   FWaitAvg := WaitAvg;
  1895. {$IFDEF PERF}
  1896.   // Respect registry setting - debug only!
  1897.   if (FDrawLateFrames) then
  1898.   begin
  1899.     Result := S_OK; // draw it when it's ready
  1900.     // even though it's late.
  1901.     Exit;
  1902.   end;
  1903. {$ENDIF}
  1904.   // We are going to drop this frame so draw the next one early
  1905.   // n.b. if the supplier is doing direct draw then he may draw it anyway
  1906.   // but he's doing something funny to arrive here in that case.
  1907. {$IFDEF PERF}
  1908.   MSR_INTEGER(FDecision, 2);
  1909. {$ENDIF}
  1910.   FNormal := -1;
  1911.   Result := E_FAIL; // drop it
  1912. end;
  1913. // NOTE we're called by both the window thread and the source filter thread
  1914. // so we have to be protected by a critical section (locked before called)
  1915. // Also, when the window thread gets signalled to render an image, it always
  1916. // does so regardless of how late it is. All the degradation is done when we
  1917. // are scheduling the next sample to be drawn. Hence when we start an advise
  1918. // link to draw a sample, that sample's time will always become the last one
  1919. // drawn - unless of course we stop streaming in which case we cancel links
  1920. function TBCBaseVideoRenderer.ScheduleSample(MediaSample: IMediaSample):
  1921.   Boolean;
  1922. begin
  1923.   // We override ShouldDrawSampleNow to add quality management
  1924.   Result := inherited ScheduleSample(MediaSample);
  1925.   if not Result then
  1926.     Inc(FFramesDropped);
  1927.   // m_cFramesDrawn must NOT be updated here.  It has to be updated
  1928.   // in RecordFrameLateness at the same time as the other statistics.
  1929. end;
  1930. // Implementation of IQualProp interface needed to support the property page
  1931. // This is how the property page gets the data out of the scheduler. We are
  1932. // passed into the constructor the owning object in the COM sense, this will
  1933. // either be the video renderer or an external IUnknown if we're aggregated.
  1934. // We initialise our CUnknown base class with this interface pointer. Then
  1935. // all we have to do is to override NonDelegatingQueryInterface to expose
  1936. // our IQualProp interface. The AddRef and Release are handled automatically
  1937. // by the base class and will be passed on to the appropriate outer object
  1938. function TBCBaseVideoRenderer.get_FramesDroppedInRenderer(var FramesDropped:
  1939.   Integer): HResult;
  1940. begin
  1941. // milenko start
  1942.   if not Assigned(@FramesDropped) then
  1943.   begin
  1944.     Result := E_POINTER;
  1945.     Exit;
  1946.   end;
  1947. // milenko end
  1948.   FInterfaceLock.Lock;
  1949.   try
  1950.     FramesDropped := FFramesDropped;
  1951.     Result := NOERROR;
  1952.   finally
  1953.     FInterfaceLock.UnLock;
  1954.   end;
  1955. end;
  1956. // Set *pcFramesDrawn to the number of frames drawn since
  1957. // streaming started.
  1958. function TBCBaseVideoRenderer.get_FramesDrawn(out FramesDrawn: Integer):
  1959.   HResult;
  1960. begin
  1961. // milenko start
  1962.   if not Assigned(@FramesDrawn) then
  1963.   begin
  1964.     Result := E_POINTER;
  1965.     Exit;
  1966.   end;
  1967. // milenko end
  1968.   FInterfaceLock.Lock;
  1969.   try
  1970.     FramesDrawn := FFramesDrawn;
  1971.     Result := NOERROR;
  1972.   finally
  1973.     FInterfaceLock.UnLock;
  1974.   end;
  1975. end;
  1976. // Set iAvgFrameRate to the frames per hundred secs since
  1977. // streaming started.  0 otherwise.
  1978. function TBCBaseVideoRenderer.get_AvgFrameRate(out AvgFrameRate: Integer):
  1979.   HResult;
  1980. var
  1981.   t: Integer;
  1982. begin
  1983. // milenko start
  1984.   if not Assigned(@AvgFrameRate) then
  1985.   begin
  1986.     Result := E_POINTER;
  1987.     Exit;
  1988.   end;
  1989. // milenko end
  1990.   FInterfaceLock.Lock;
  1991.   try
  1992.     if (FIsStreaming) then
  1993. // milenko start
  1994. //    t := Integer(timeGetTime) - FStreamingStart
  1995.       t := Int64(timeGetTime) - FStreamingStart
  1996. // milenko end
  1997.     else
  1998.       t := FStreamingStart;
  1999.     if (t <= 0) then
  2000.     begin
  2001.       AvgFrameRate := 0;
  2002.       Assert(FFramesDrawn = 0);
  2003.     end
  2004.     else
  2005.       // i is frames per hundred seconds
  2006.       AvgFrameRate := MulDiv(100000, FFramesDrawn, t);
  2007.     Result := NOERROR;
  2008.   finally
  2009.     FInterfaceLock.UnLock;
  2010.   end;
  2011. end;
  2012. // Set *piAvg to the average sync offset since streaming started
  2013. // in mSec.  The sync offset is the time in mSec between when the frame
  2014. // should have been drawn and when the frame was actually drawn.
  2015. function TBCBaseVideoRenderer.get_AvgSyncOffset(out Avg: Integer): HResult;
  2016. begin
  2017. // milenko start
  2018.   if not Assigned(@Avg) then
  2019.   begin
  2020.     Result := E_POINTER;
  2021.     Exit;
  2022.   end;
  2023. // milenko end
  2024.   FInterfaceLock.Lock;
  2025.   try
  2026.     if (nil = FClock) then
  2027.     begin
  2028.       Avg := 0;
  2029.       Result := NOERROR;
  2030.       Exit;
  2031.     end;
  2032.     // Note that we didn't gather the stats on the first frame
  2033.     // so we use m_cFramesDrawn-1 here
  2034.     if (FFramesDrawn <= 1) then
  2035.       Avg := 0
  2036.     else
  2037.       Avg := (FTotAcc div (FFramesDrawn - 1));
  2038.     Result := NOERROR;
  2039.   finally
  2040.     FInterfaceLock.UnLock;
  2041.   end;
  2042. end;
  2043. // To avoid dragging in the maths library - a cheap
  2044. // approximate integer square root.
  2045. // We do this by getting a starting guess which is between 1
  2046. // and 2 times too large, followed by THREE iterations of
  2047. // Newton Raphson.  (That will give accuracy to the nearest mSec
  2048. // for the range in question - roughly 0..1000)
  2049. //
  2050. // It would be faster to use a linear interpolation and ONE NR, but
  2051. // who cares.  If anyone does - the best linear interpolation is
  2052. // to approximates sqrt(x) by
  2053. // y = x * (sqrt(2)-1) + 1 - 1/sqrt(2) + 1/(8*(sqrt(2)-1))
  2054. // 0r y = x*0.41421 + 0.59467
  2055. // This minimises the maximal error in the range in question.
  2056. // (error is about +0.008883 and then one NR will give error .0000something
  2057. // (Of course these are integers, so you can't just multiply by 0.41421
  2058. // you'd have to do some sort of MulDiv).
  2059. // Anyone wanna check my maths?  (This is only for a property display!)
  2060. function isqrt(x: Integer): Integer;
  2061. var
  2062.   s: Integer;
  2063. begin
  2064.   s := 1;
  2065.   // Make s an initial guess for sqrt(x)
  2066.   if (x > $40000000) then
  2067.     s := $8000 // prevent any conceivable closed loop
  2068.   else
  2069.   begin
  2070.     while (s * s < x) do // loop cannot possible go more than 31 times
  2071.       s := 2 * s; // normally it goes about 6 times
  2072.     // Three NR iterations.
  2073.     if (x = 0) then
  2074.       s := 0 // Wouldn't it be tragic to divide by zero whenever our
  2075.       // accuracy was perfect!
  2076.     else
  2077.     begin
  2078.       s := (s * s + x) div (2 * s);
  2079.       if (s >= 0) then
  2080.         s := (s * s + x) div (2 * s);
  2081.       if (s >= 0) then
  2082.         s := (s * s + x) div (2 * s);
  2083.     end;
  2084.   end;
  2085.   Result := s;
  2086. end;
  2087. //
  2088. //  Do estimates for standard deviations for per-frame
  2089. //  statistics
  2090. //
  2091. function TBCBaseVideoRenderer.GetStdDev(Samples: Integer; out Res: Integer;
  2092.   SumSq, Tot: Int64): HResult;
  2093. var
  2094.   x: Int64;
  2095. begin
  2096. // milenko start
  2097.   if not Assigned(@Res) then
  2098.   begin
  2099.     Result := E_POINTER;
  2100.     Exit;
  2101.   end;
  2102. // milenko end
  2103.   FInterfaceLock.Lock;
  2104.   try
  2105.     if (nil = FClock) then
  2106.     begin
  2107.       Res := 0;
  2108.       Result := NOERROR;
  2109.       Exit;
  2110.     end;
  2111.     // If S is the Sum of the Squares of observations and
  2112.     //    T the Total (i.e. sum) of the observations and there were
  2113.     //    N observations, then an estimate of the standard deviation is
  2114.     //      sqrt( (S - T**2/N) / (N-1) )
  2115.     if (Samples <= 1) then
  2116.       Res := 0
  2117.     else
  2118.     begin
  2119.       // First frames have invalid stamps, so we get no stats for them
  2120.       // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
  2121.       // so we use m_cFramesDrawn-1 here
  2122.       // ??? llMilDiv ???
  2123. // milenko start (removed the 2 outputdebugstring messages...i added them and
  2124. //                they are not needed anymore)
  2125.       x := SumSq - llMulDiv(Tot, Tot, Samples, 0);
  2126.       x := x div (Samples - 1);
  2127. // milenko end
  2128.       Assert(x >= 0);
  2129.       Res := isqrt(Longint(x));
  2130.     end;
  2131.     Result := NOERROR;
  2132.   finally
  2133.     FInterfaceLock.UnLock;
  2134.   end;
  2135. end;
  2136. // Set *piDev to the standard deviation in mSec of the sync offset
  2137. // of each frame since streaming started.
  2138. function TBCBaseVideoRenderer.get_DevSyncOffset(out Dev: Integer): HResult;
  2139. begin
  2140.   // First frames have invalid stamps, so we get no stats for them
  2141.   // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
  2142.   Result := GetStdDev(FFramesDrawn - 1, Dev, FSumSqAcc, FTotAcc);
  2143. end;
  2144. // Set *piJitter to the standard deviation in mSec of the inter-frame time
  2145. // of frames since streaming started.
  2146. function TBCBaseVideoRenderer.get_Jitter(out Jitter: Integer): HResult;
  2147. begin
  2148.   // First frames have invalid stamps, so we get no stats for them
  2149.   // So second frame gives invalid inter-frame time
  2150.   // So we need 3 frames to get 1 datum, so N is cFramesDrawn-2
  2151.   Result := GetStdDev(FFramesDrawn - 2, Jitter, FSumSqFrameTime, FSumFrameTime);
  2152. end;
  2153. // Overidden to return our IQualProp interface
  2154. function TBCBaseVideoRenderer.NonDelegatingQueryInterface(const IID: TGUID;
  2155.   out Obj): HResult;
  2156. begin
  2157.   // We return IQualProp and delegate everything else
  2158.   if IsEqualGUID(IID, IID_IQualProp) then
  2159.     if GetInterface(IID_IQualProp, Obj) then
  2160.       Result := S_OK
  2161.     else
  2162.       Result := E_FAIL
  2163.   else if IsEqualGUID(IID, IID_IQualityControl) then
  2164.     if GetInterface(IID_IQualityControl, Obj) then
  2165.       Result := S_OK
  2166.     else
  2167.       Result := E_FAIL
  2168.   else
  2169.     Result := inherited NonDelegatingQueryInterface(IID, Obj);
  2170. end;
  2171. // Override JoinFilterGraph so that, just before leaving
  2172. // the graph we can send an EC_WINDOW_DESTROYED event
  2173. function TBCBaseVideoRenderer.JoinFilterGraph(Graph: IFilterGraph;
  2174.   Name: PWideChar): HResult;
  2175. var
  2176.   Filter: IBaseFilter;
  2177. begin
  2178.   // Since we send EC_ACTIVATE, we also need to ensure
  2179.   // we send EC_WINDOW_DESTROYED or the resource manager may be
  2180.   // holding us as a focus object
  2181.   if (Graph = nil) and Assigned(FGraph) then
  2182.   begin
  2183.     // We were in a graph and now we're not
  2184.     // Do this properly in case we are aggregated
  2185.     QueryInterface(IID_IBaseFilter, Filter);
  2186.     NotifyEvent(EC_WINDOW_DESTROYED, Integer(Filter), 0);
  2187.     Filter := nil;
  2188.   end;
  2189.   Result := inherited JoinFilterGraph(Graph, Name);
  2190. end;
  2191. // milenko start (added TBCPullPin)
  2192. constructor TBCPullPin.Create;
  2193. begin
  2194.   inherited Create;
  2195.   FReader := nil;
  2196.   FAlloc := nil;
  2197.   FState := TM_Exit;
  2198. end;
  2199. destructor TBCPullPin.Destroy;
  2200. begin
  2201.   Disconnect;
  2202. end;
  2203. procedure TBCPullPin.Process;
  2204. var
  2205.   Discontinuity: Boolean;
  2206.   Actual: TAllocatorProperties;
  2207.   hr: HRESULT;
  2208.   Start, Stop, Current, AlignStop: TReferenceTime;
  2209.   Request: DWORD;
  2210.   Sample: IMediaSample;
  2211.   StopThis: Int64;
  2212. begin
  2213.   // is there anything to do?
  2214.   if (FStop <= FStart) then
  2215.   begin
  2216.     EndOfStream;
  2217.     Exit;
  2218.   end;
  2219.   Discontinuity := True;
  2220.   // if there is more than one sample at the allocator,
  2221.   // then try to queue 2 at once in order to overlap.
  2222.   // -- get buffer count and required alignment
  2223.   FAlloc.GetProperties(Actual);
  2224.   // align the start position downwards
  2225.   Start := AlignDown(FStart div UNITS, Actual.cbAlign) * UNITS;
  2226.   Current := Start;
  2227.   Stop := FStop;
  2228.   if (Stop > FDuration) then Stop := FDuration;
  2229.   // align the stop position - may be past stop, but that
  2230.   // doesn't matter
  2231.   AlignStop := AlignUp(Stop div UNITS, Actual.cbAlign) * UNITS;
  2232.   if not FSync then
  2233.   begin
  2234.     //  Break out of the loop either if we get to the end or we're asked
  2235.     //  to do something else
  2236.     while (Current < AlignStop) do
  2237.     begin
  2238.       // Break out without calling EndOfStream if we're asked to
  2239.       // do something different
  2240.     if CheckRequest(@Request) then Exit;
  2241.     // queue a first sample
  2242.     if (Actual.cBuffers > 1) then
  2243.       begin
  2244.         hr := QueueSample(Current, AlignStop, True);
  2245.         Discontinuity := False;
  2246.         if FAILED(hr) then Exit;
  2247.     end;
  2248.     // loop queueing second and waiting for first..
  2249.     while (Current < AlignStop) do
  2250.       begin
  2251.         hr := QueueSample(Current, AlignStop, Discontinuity);
  2252.         Discontinuity := False;
  2253.         if FAILED(hr) then Exit;
  2254.         hr := CollectAndDeliver(Start, Stop);
  2255.         if (S_OK <> hr) then
  2256.         begin
  2257.           // stop if error, or if downstream filter said
  2258.           // to stop.
  2259.       Exit;
  2260.         end;
  2261.     end;
  2262.     if (Actual.cBuffers > 1) then
  2263.       begin
  2264.         hr := CollectAndDeliver(Start, Stop);
  2265.         if FAILED(hr) then Exit;
  2266.     end;
  2267.   end;
  2268.   end else
  2269.   begin
  2270.     // sync version of above loop
  2271.     while (Current < AlignStop) do
  2272.     begin
  2273.     // Break out without calling EndOfStream if we're asked to
  2274.     // do something different
  2275.     if CheckRequest(@Request) then Exit;
  2276.     hr := FAlloc.GetBuffer(Sample, nil, nil, 0);
  2277.     if FAILED(hr) then
  2278.       begin
  2279.         OnError(hr);
  2280.         Exit;
  2281.     end;
  2282.       StopThis := Current + (Sample.GetSize * UNITS);
  2283.     if (StopThis > AlignStop) then StopThis := AlignStop;
  2284.     Sample.SetTime(@Current, @StopThis);
  2285.     Current := StopThis;
  2286.     if Discontinuity then
  2287.       begin
  2288.         Sample.SetDiscontinuity(True);
  2289.         Discontinuity := False;
  2290.     end;
  2291.     hr := FReader.SyncReadAligned(Sample);
  2292.     if FAILED(hr) then
  2293.       begin
  2294.         Sample := nil;
  2295.         OnError(hr);
  2296.         Exit;
  2297.     end;
  2298.     hr := DeliverSample(Sample, Start, Stop);
  2299.     if (hr <> S_OK) then
  2300.       begin
  2301.         if FAILED(hr) then OnError(hr);
  2302.         Exit;
  2303.     end;
  2304.     end;
  2305.   end;
  2306.   EndOfStream;
  2307. end;
  2308. procedure TBCPullPin.CleanupCancelled;
  2309. var
  2310.   Sample: IMediaSample;
  2311.   Unused: DWORD;
  2312. begin
  2313.   while True do
  2314.   begin
  2315.     FReader.WaitForNext(
  2316.     0,          // no wait
  2317.     Sample,
  2318.     Unused);
  2319.     if Assigned(Sample) then Sample := nil
  2320.                         else Exit;
  2321.   end;
  2322. end;
  2323. function TBCPullPin.PauseThread: HRESULT;
  2324. begin
  2325.   FAccessLock.Lock;
  2326.   try
  2327.     if not ThreadExists then
  2328.     begin
  2329.       Result := E_UNEXPECTED;
  2330.       Exit;
  2331.     end;
  2332.     // need to flush to ensure the thread is not blocked
  2333.     // in WaitForNext
  2334.     Result := FReader.BeginFlush;
  2335.     if FAILED(Result) then Exit;
  2336.     FState := TM_Pause;
  2337.     Result := CallWorker(Cardinal(TM_Pause));
  2338.     FReader.EndFlush;
  2339.   finally
  2340.     FAccessLock.UnLock;
  2341.   end;
  2342. end;
  2343. function TBCPullPin.StartThread: HRESULT;
  2344. begin
  2345.   FAccessLock.Lock;
  2346.   try
  2347.     if not Assigned(FAlloc) or not Assigned(FReader) then
  2348.     begin
  2349.       Result := E_UNEXPECTED;
  2350.       Exit;
  2351.     end;
  2352.     if not ThreadExists then
  2353.     begin
  2354.       // commit allocator
  2355.       Result := FAlloc.Commit;
  2356.       if FAILED(Result) then Exit;
  2357.       // start thread
  2358.       if not Create_ then
  2359.       begin
  2360.         Result := E_FAIL;
  2361.         Exit;
  2362.       end;
  2363.   end;
  2364.     FState := TM_Start;
  2365.     Result := HRESULT(CallWorker(DWORD(FState)));
  2366.   finally
  2367.     FAccessLock.UnLock;
  2368.   end;
  2369. end;
  2370. function TBCPullPin.StopThread: HRESULT;
  2371. begin
  2372.   FAccessLock.Lock;
  2373.   try
  2374.     if not ThreadExists then
  2375.     begin
  2376.       Result := S_FALSE;
  2377.       Exit;
  2378.     end;
  2379.     // need to flush to ensure the thread is not blocked
  2380.     // in WaitForNext
  2381.     Result := FReader.BeginFlush;
  2382.     if FAILED(Result) then Exit;
  2383.     FState := TM_Exit;
  2384.     Result := CallWorker(Cardinal(TM_Exit));
  2385.     FReader.EndFlush;
  2386.     // wait for thread to completely exit
  2387.     Close;
  2388.     // decommit allocator
  2389.     if Assigned(FAlloc) then FAlloc.Decommit;
  2390.     Result := S_OK;
  2391.   finally
  2392.     FAccessLock.UnLock;
  2393.   end;
  2394. end;
  2395. function TBCPullPin.QueueSample(var tCurrent: TReferenceTime; tAlignStop: TReferenceTime; bDiscontinuity: Boolean): HRESULT;
  2396. var
  2397.   Sample: IMediaSample;
  2398.   StopThis: Int64;
  2399. begin
  2400.   Result := FAlloc.GetBuffer(Sample, nil, nil, 0);
  2401.   if FAILED(Result) then Exit;
  2402.   StopThis := tCurrent + (Sample.GetSize * UNITS);
  2403.   if (StopThis > tAlignStop) then StopThis := tAlignStop;
  2404.   Sample.SetTime(@tCurrent, @StopThis);
  2405.   tCurrent := StopThis;
  2406.   Sample.SetDiscontinuity(bDiscontinuity);
  2407.   Result := FReader.Request(Sample,0);
  2408.   if FAILED(Result) then
  2409.   begin
  2410.     Sample := nil;
  2411.     CleanupCancelled;
  2412.     OnError(Result);
  2413.   end;
  2414. end;
  2415. function TBCPullPin.CollectAndDeliver(tStart,tStop: TReferenceTime): HRESULT;
  2416. var
  2417.   Sample: IMediaSample;
  2418.   Unused: DWORD;
  2419. begin
  2420.   Result := FReader.WaitForNext(INFINITE,Sample,Unused);
  2421.   if FAILED(Result) then
  2422.   begin
  2423.     if Assigned(Sample) then Sample := nil;
  2424. end else
  2425.   begin
  2426.     Result := DeliverSample(Sample, tStart, tStop);
  2427.   end;
  2428.   if FAILED(Result) then
  2429.   begin
  2430.     CleanupCancelled;
  2431.     OnError(Result);
  2432.   end;
  2433. end;
  2434. function TBCPullPin.DeliverSample(pSample: IMediaSample; tStart,tStop: TReferenceTime): HRESULT;
  2435. var
  2436.   t1, t2: TReferenceTime;
  2437. begin
  2438.   // fix up sample if past actual stop (for sector alignment)
  2439.   pSample.GetTime(t1, t2);
  2440.   if (t2 > tStop) then t2 := tStop;
  2441.   // adjust times to be relative to (aligned) start time
  2442.   dec(t1,tStart);
  2443.   dec(t2,tStart);
  2444.   pSample.SetTime(@t1, @t2);
  2445.   Result := Receive(pSample);
  2446.   pSample := nil;
  2447. end;
  2448. function TBCPullPin.ThreadProc: DWord;
  2449. var
  2450.   cmd: DWORD;
  2451. begin
  2452.   Result := 1; // ???
  2453.   while True do
  2454.   begin
  2455.     cmd := GetRequest;
  2456.   case TThreadMsg(cmd) of
  2457.       TM_Exit:
  2458.       begin
  2459.         Reply(S_OK);
  2460.         Result := 0;
  2461.         Exit;
  2462.     end;
  2463.       TM_Pause:
  2464.       begin
  2465.         // we are paused already
  2466.         Reply(S_OK);
  2467.         break;
  2468.       end;
  2469.       TM_Start:
  2470.       begin
  2471.         Reply(S_OK);
  2472.         Process;
  2473.         break;
  2474.       end;
  2475.     end;
  2476.     // at this point, there should be no outstanding requests on the
  2477.     // upstream filter.
  2478.     // We should force begin/endflush to ensure that this is true.
  2479.     // !!!Note that we may currently be inside a BeginFlush/EndFlush pair
  2480.     // on another thread, but the premature EndFlush will do no harm now
  2481.     // that we are idle.
  2482.     FReader.BeginFlush;
  2483.     CleanupCancelled;
  2484.     FReader.EndFlush;
  2485.   end;
  2486. end;
  2487. // returns S_OK if successfully connected to an IAsyncReader interface
  2488. // from this object
  2489. // Optional allocator should be proposed as a preferred allocator if
  2490. // necessary
  2491. function TBCPullPin.Connect(pUnk: IUnknown; pAlloc: IMemAllocator; bSync: Boolean): HRESULT;
  2492. var
  2493.   Total, Avail: Int64;
  2494. begin
  2495.   FAccessLock.Lock;
  2496.   try
  2497.     if Assigned(FReader) then
  2498.     begin
  2499.       Result := VFW_E_ALREADY_CONNECTED;
  2500.       Exit;
  2501.     end;
  2502.     Result := pUnk.QueryInterface(IID_IAsyncReader, FReader);
  2503.     if FAILED(Result) then Exit;
  2504.     Result := DecideAllocator(pAlloc, nil);
  2505.     if FAILED(Result) then
  2506.     begin
  2507.       Disconnect;
  2508.       Exit;
  2509.     end;
  2510.     Result := FReader.Length(Total, Avail);
  2511.     if FAILED(Result) then
  2512.     begin
  2513.       Disconnect;
  2514.       Exit;
  2515.     end;
  2516.     // convert from file position to reference time
  2517.     FDuration := Total * UNITS;
  2518.     FStop := FDuration;
  2519.     FStart := 0;
  2520.     FSync := bSync;
  2521.     Result := S_OK;
  2522.   finally
  2523.     FAccessLock.UnLock;
  2524.   end;
  2525. end;
  2526. // disconnect any connection made in Connect
  2527. function TBCPullPin.Disconnect: HRESULT;
  2528. begin
  2529.   FAccessLock.Lock;
  2530.   try
  2531.     StopThread;
  2532.     if Assigned(FReader) then FReader := nil;
  2533.     if Assigned(FAlloc) then FAlloc := nil;
  2534.     Result := S_OK;
  2535.   finally
  2536.     FAccessLock.UnLock;
  2537.   end;
  2538. end;
  2539. // agree an allocator using RequestAllocator - optional
  2540. // props param specifies your requirements (non-zero fields).
  2541. // returns an error code if fail to match requirements.
  2542. // optional IMemAllocator interface is offered as a preferred allocator
  2543. // but no error occurs if it can't be met.
  2544. function TBCPullPin.DecideAllocator(pAlloc: IMemAllocator; pProps: PAllocatorProperties): HRESULT;
  2545. var
  2546.   pRequest: PAllocatorProperties;
  2547.   Request: TAllocatorProperties;
  2548. begin
  2549.   if (pProps = nil) then
  2550.   begin
  2551.     Request.cBuffers := 3;
  2552.     Request.cbBuffer := 64*1024;
  2553.     Request.cbAlign := 0;
  2554.     Request.cbPrefix := 0;
  2555.     pRequest := @Request;
  2556.   end else
  2557.   begin
  2558.     pRequest := pProps;
  2559.   end;
  2560.   Result := FReader.RequestAllocator(pAlloc,pRequest,FAlloc);
  2561. end;
  2562. function TBCPullPin.Seek(tStart, tStop: TReferenceTime): HRESULT;
  2563. var
  2564.   AtStart: TThreadMsg;
  2565. begin
  2566.   FAccessLock.Lock;
  2567.   try
  2568.     AtStart := FState;
  2569.     if (AtStart = TM_Start) then
  2570.     begin
  2571.       BeginFlush;
  2572.       PauseThread;
  2573.       EndFlush;
  2574.     end;
  2575.     FStart := tStart;
  2576.     FStop := tStop;
  2577.     Result := S_OK;
  2578.     if (AtStart = TM_Start) then Result := StartThread;
  2579.   finally
  2580.     FAccessLock.UnLock;
  2581.   end;
  2582. end;
  2583. function TBCPullPin.Duration(out ptDuration: TReferenceTime): HRESULT;
  2584. begin
  2585.   ptDuration := FDuration;
  2586.   Result := S_OK;
  2587. end;
  2588. // start pulling data
  2589. function TBCPullPin.Active: HRESULT;
  2590. begin
  2591.   ASSERT(not ThreadExists);
  2592.   Result := StartThread;
  2593. end;
  2594. // stop pulling data
  2595. function TBCPullPin.Inactive: HRESULT;
  2596. begin
  2597.   StopThread;
  2598.   Result := S_OK;
  2599. end;
  2600. function TBCPullPin.AlignDown(ll: Int64; lAlign: LongInt): Int64;
  2601. begin
  2602. Result := ll and not (lAlign-1);
  2603. end;
  2604. function TBCPullPin.AlignUp(ll: Int64; lAlign: LongInt): Int64;
  2605. begin
  2606. Result := (ll + (lAlign -1)) and not (lAlign -1);
  2607. end;
  2608. function TBCPullPin.GetReader: IAsyncReader;
  2609. begin
  2610. Result := FReader;
  2611. end;
  2612. // milenko end
  2613. // milenko start reftime implementation
  2614. procedure TBCRefTime.Create_;
  2615. begin
  2616.   FTime := 0;
  2617. end;
  2618. procedure TBCRefTime.Create_(msecs: Longint);
  2619. begin
  2620.   FTime := MILLISECONDS_TO_100NS_UNITS(msecs);
  2621. end;
  2622. function TBCRefTime.SetTime(var rt: TBCRefTime): TBCRefTime;
  2623. begin
  2624.   FTime := rt.FTime;
  2625.   Result := Self;
  2626. end;
  2627. function TBCRefTime.SetTime(var ll: LONGLONG): TBCRefTime;
  2628. begin
  2629.   FTime := ll;
  2630. end;
  2631. function TBCRefTime.AddTime(var rt: TBCRefTime): TBCRefTime;
  2632. begin
  2633.   TReferenceTime(Self) := TReferenceTime(Self) + TReferenceTime(rt);
  2634.   Result := Self;
  2635. end;
  2636. function TBCRefTime.SubstractTime(var rt: TBCRefTime): TBCRefTime;
  2637. begin
  2638.   TReferenceTime(Self) := TReferenceTime(Self) - TReferenceTime(rt);
  2639.   Result := Self;
  2640. end;
  2641. function TBCRefTime.Millisecs: Longint;
  2642. begin
  2643.   Result := fTime div (UNITS div MILLISECONDS);
  2644. end;
  2645. function TBCRefTime.GetUnits: LONGLONG;
  2646. begin
  2647.   Result := fTime;
  2648. end;
  2649. // milenko end
  2650. // milenko start schedule implementation
  2651. constructor TBCAdvisePacket.Create;
  2652. begin
  2653.   inherited Create;
  2654. end;
  2655. constructor TBCAdvisePacket.Create(Next: TBCAdvisePacket; Time: LONGLONG);
  2656. begin
  2657.   inherited Create;
  2658.   FNext := Next;
  2659.   FEventTime := Time;
  2660. end;
  2661. procedure TBCAdvisePacket.InsertAfter(Packet: TBCAdvisePacket);
  2662. begin
  2663.   Packet.FNext := FNext;
  2664.   FNext := Packet;
  2665. end;
  2666. function TBCAdvisePacket.IsZ: Boolean;
  2667. begin
  2668.   Result := FNext = nil;
  2669. end;
  2670. function TBCAdvisePacket.RemoveNext: TBCAdvisePacket;
  2671. var
  2672.   Next,
  2673.   NewNext : TBCAdvisePacket;
  2674. begin
  2675.   Next := FNext;
  2676.   NewNext := Next.FNext;
  2677.   FNext := NewNext;
  2678.   Result := Next;
  2679. end;
  2680. procedure TBCAdvisePacket.DeleteNext;
  2681. begin
  2682.   RemoveNext.Free;
  2683. end;
  2684. function TBCAdvisePacket.Next: TBCAdvisePacket;
  2685. begin
  2686.   Result := FNext;
  2687.   if Result.IsZ then Result := nil;
  2688. end;
  2689. function TBCAdvisePacket.Cookie: DWORD;
  2690. begin
  2691.   Result := FAdviseCookie;
  2692. end;
  2693. constructor TBCAMSchedule.Create(Event: THandle);
  2694. begin
  2695.   inherited Create('TBCAMSchedule');
  2696.   FZ := TBCAdvisePacket.Create(nil,MAX_TIME);
  2697.   FHead := TBCAdvisePacket.Create(FZ,0);
  2698.   FNextCookie := 0;
  2699.   FAdviseCount := 0;
  2700.   FAdviseCache := nil;
  2701.   FCacheCount := 0;
  2702.   FEvent := Event;
  2703.   FSerialize := TBCCritSec.Create;
  2704.   FZ.FAdviseCookie := 0;
  2705.   FHead.FAdviseCookie := FZ.FAdviseCookie;
  2706. end;
  2707. destructor TBCAMSchedule.Destroy;
  2708. var
  2709.   p, p_next : TBCAdvisePacket;
  2710. begin
  2711.   FSerialize.Lock;
  2712.   try
  2713.     // Delete cache
  2714.     p := FAdviseCache;
  2715.     while (p <> nil) do
  2716.     begin
  2717.       p_next := p.FNext;
  2718.       FreeAndNil(p);
  2719.       p := p_next;
  2720.     end;
  2721.     ASSERT(FAdviseCount = 0);
  2722.     // Better to be safe than sorry
  2723.     if (FAdviseCount > 0) then
  2724.     begin
  2725.       DumpLinkedList;
  2726.       while not FHead.FNext.IsZ do
  2727.       begin
  2728.         FHead.DeleteNext;
  2729.         dec(FAdviseCount);
  2730.       end;
  2731.     end;
  2732.     // If, in the debug version, we assert twice, it means, not only
  2733.     // did we have left over advises, but we have also let m_dwAdviseCount
  2734.     // get out of sync. with the number of advises actually on the list.
  2735.     ASSERT(FAdviseCount = 0);
  2736.   finally
  2737.     FSerialize.Unlock;
  2738.   end;
  2739.   FreeAndNil(FSerialize);
  2740.   inherited Destroy;
  2741. end;
  2742. function TBCAMSchedule.GetAdviseCount: DWORD;
  2743. begin
  2744.   // No need to lock, m_dwAdviseCount is 32bits & declared volatile
  2745.   // DCODER: No volatile in Delphi -> needs a lock ?
  2746.   FSerialize.Lock;
  2747.   try
  2748.     Result := FAdviseCount;
  2749.   finally
  2750.     FSerialize.UnLock;
  2751.   end;
  2752. end;
  2753. function TBCAMSchedule.GetNextAdviseTime: TReferenceTime;
  2754. begin
  2755.   FSerialize.Lock;  // Need to stop the linked list from changing
  2756.   try
  2757.     Result := FHead.FNext.FEventTime;
  2758.   finally
  2759.     FSerialize.UnLock;
  2760.   end;
  2761. end;
  2762. function TBCAMSchedule.AddAdvisePacket(const time1, time2: TReferenceTime;
  2763.   h: THandle; periodic: Boolean): DWORD;
  2764. var
  2765.   p : TBCAdvisePacket;
  2766. begin
  2767.   // Since we use MAX_TIME as a sentry, we can't afford to
  2768.   // schedule a notification at MAX_TIME
  2769.   ASSERT(time1 < MAX_TIME);
  2770.   FSerialize.Lock;
  2771.   try
  2772.     if Assigned(FAdviseCache) then
  2773.     begin
  2774.       p := FAdviseCache;
  2775.       FAdviseCache := p.FNext;
  2776.       dec(FCacheCount);
  2777.     end else
  2778.     begin
  2779.       p := TBCAdvisePacket.Create;
  2780.     end;
  2781.     if Assigned(p) then
  2782.     begin
  2783.       p.FEventTime := time1;
  2784.       p.FPeriod := time2;
  2785.       p.FNotify := h;
  2786.       p.FPeriodic := periodic;
  2787.       Result := AddAdvisePacket(p);
  2788.     end else
  2789.     begin
  2790.       Result := 0;
  2791.     end;
  2792.   finally
  2793.     FSerialize.UnLock;
  2794.   end;
  2795. end;
  2796. function TBCAMSchedule.Unadvise(AdviseCookie: DWORD): HRESULT;
  2797. var
  2798.   p_prev, p_n : TBCAdvisePacket;
  2799. begin
  2800.   Result := S_FALSE;
  2801.   p_prev := FHead;
  2802.   FSerialize.Lock;
  2803.   try
  2804.     p_n := p_prev.Next;
  2805.     while Assigned(p_n) do // The Next() method returns NULL when it hits z
  2806.     begin
  2807.       if (p_n.FAdviseCookie = AdviseCookie) then
  2808.       begin
  2809.         Delete(p_prev.RemoveNext);
  2810.         dec(FAdviseCount);
  2811.         Result := S_OK;
  2812.         // Having found one cookie that matches, there should be no more
  2813.         {$IFDEF DEBUG}
  2814.         p_n := p_prev.Next;
  2815.         while Assigned(p_n) do
  2816.         begin
  2817.           ASSERT(p_n.FAdviseCookie <> AdviseCookie);
  2818.           p_prev := p_n;
  2819.           p_n := p_prev.Next;
  2820.         end;
  2821.         {$ENDIF}
  2822.         break;
  2823.       end;
  2824.       p_prev := p_n;
  2825.       p_n := p_prev.Next;
  2826.     end;
  2827.   finally
  2828.     FSerialize.UnLock;
  2829.   end;
  2830. end;
  2831. function TBCAMSchedule.Advise(const Time_: TReferenceTime): TReferenceTime;
  2832. var
  2833.   NextTime : TReferenceTime;
  2834.   Advise : TBCAdvisePacket;
  2835. begin
  2836.   {$IFDEF DEBUG}
  2837.     DbgLog(
  2838.       Self, 'TBCAMSchedule.Advise( ' +
  2839.       inttostr((Time_ div (UNITS div MILLISECONDS))) + ' ms '
  2840.     );
  2841.   {$ENDIF}
  2842.   FSerialize.Lock;
  2843.   try
  2844.     {$IFDEF DEBUG}
  2845.       DumpLinkedList;
  2846.     {$ENDIF}
  2847.     //  Note - DON'T cache the difference, it might overflow
  2848.     Advise := FHead.FNext;
  2849.     NextTime := Advise.FEventTime;
  2850.     while ((Time_ >= NextTime) and not Advise.IsZ) do
  2851.     begin
  2852.       // DCODER: assert raised here
  2853.       ASSERT(Advise.FAdviseCookie > 0); // If this is zero, its the head or the tail!!
  2854.       ASSERT(Advise.FNotify <> INVALID_HANDLE_VALUE);
  2855.       if (Advise.FPeriodic = True) then
  2856.       begin
  2857.         ReleaseSemaphore(Advise.FNotify,1,nil);
  2858.         Advise.FEventTime := Advise.FEventTime + Advise.FPeriod;
  2859.         ShuntHead;
  2860.       end else
  2861.       begin
  2862.         ASSERT(Advise.FPeriodic = False);
  2863.         SetEvent(Advise.FNotify);
  2864.         dec(FAdviseCount);
  2865.         Delete(FHead.RemoveNext);
  2866.       end;
  2867.       Advise := FHead.FNext;
  2868.       NextTime := Advise.FEventTime;
  2869.     end;
  2870.   finally
  2871.     FSerialize.UnLock;
  2872.   end;
  2873.   {$IFDEF DEBUG}
  2874.     DbgLog(
  2875.       Self, 'TBCAMSchedule.Advise(Next time stamp: ' +
  2876.       inttostr((NextTime div (UNITS div MILLISECONDS))) +
  2877.       ' ms, for advise ' + inttostr(Advise.FAdviseCookie)
  2878.     );
  2879.   {$ENDIF}
  2880.   Result := NextTime;
  2881. end;
  2882. function TBCAMSchedule.GetEvent: THandle;
  2883. begin
  2884.   Result := FEvent;
  2885. end;
  2886. procedure TBCAMSchedule.DumpLinkedList;
  2887. {$IFDEF DEBUG}
  2888. var
  2889.   i : integer;
  2890.   p : TBCAdvisePacket;
  2891. {$ENDIF}
  2892. begin
  2893.   {$IFDEF DEBUG}
  2894.   FSerialize.Lock;
  2895.   try
  2896.     DbgLog(Self,'TBCAMSchedule.DumpLinkedList');
  2897.     i := 0;
  2898.     p := FHead;
  2899.     while True do
  2900.     begin
  2901.       if p = nil then break;
  2902.       DbgLog(
  2903.         Self, 'Advise List # ' + inttostr(i) + ', Cookie ' +
  2904.         inttostr(p.FAdviseCookie) + ',  RefTime ' +
  2905.         inttostr(p.FEventTime div (UNITS div MILLISECONDS))
  2906.       );
  2907.       inc(i);
  2908.       p := p.Next;
  2909.     end;
  2910.   finally
  2911.     FSerialize.Unlock;
  2912.   end;
  2913.   {$ENDIF}
  2914. end;
  2915. function TBCAMSchedule.AddAdvisePacket(Packet: TBCAdvisePacket): DWORD;
  2916. var
  2917.   p_prev, p_n : TBCAdvisePacket;
  2918. begin
  2919.   ASSERT((Packet.FEventTime >= 0) and (Packet.FEventTime < MAX_TIME));
  2920.   {$IFDEF DEBUG}
  2921.   ASSERT(FSerialize.CritCheckIn);
  2922.   {$ENDIF}
  2923.   p_prev := FHead;
  2924.   inc(FNextCookie);
  2925.   Packet.FAdviseCookie := FNextCookie;
  2926.   Result := Packet.FAdviseCookie;
  2927.   // This relies on the fact that z is a sentry with a maximal m_rtEventTime
  2928.   while True do
  2929.   begin
  2930.     p_n := p_prev.FNext;
  2931.     if (p_n.FEventTime >= Packet.FEventTime) then break;
  2932.     p_prev := p_n;
  2933.   end;
  2934.   p_prev.InsertAfter(Packet);
  2935.   inc(FAdviseCount);
  2936.   {$IFDEF DEBUG}
  2937.   DbgLog(
  2938.     Self, 'Added advise ' + inttostr(Packet.FAdviseCookie) + ', for thread ' +
  2939.     inttostr(GetCurrentThreadId) + ', scheduled at ' +
  2940.     inttostr(Packet.FEventTime div (UNITS div MILLISECONDS))
  2941.   );
  2942.   {$ENDIF}
  2943.   // If packet added at the head, then clock needs to re-evaluate wait time.
  2944.   if (p_prev = FHead) then SetEvent(FEvent);
  2945. end;
  2946. procedure TBCAMSchedule.ShuntHead;
  2947. var
  2948.   p_prev, p_n : TBCAdvisePacket;
  2949.   Packet : TBCAdvisePacket;
  2950. begin
  2951.   p_prev := FHead;
  2952.   p_n := nil;
  2953.   FSerialize.Lock;
  2954.   try
  2955.     Packet := FHead.FNext;
  2956.     // This will catch both an empty list,
  2957.     // and if somehow a MAX_TIME time gets into the list
  2958.     // (which would also break this method).
  2959.     ASSERT(Packet.FEventTime < MAX_TIME);
  2960.     // This relies on the fact that z is a sentry with a maximal m_rtEventTime
  2961.     while True do
  2962.     begin
  2963.       p_n := p_prev.FNext;
  2964.       if (p_n.FEventTime >= Packet.FEventTime) then break;
  2965.       p_prev := p_n;
  2966.     end;
  2967.     // If p_prev == pPacket then we're already in the right place
  2968.     if (p_prev <> Packet) then
  2969.     begin
  2970.       FHead.FNext := Packet.FNext;
  2971.       p_prev.FNext := Packet;
  2972.       p_prev.FNext.FNext := p_n;
  2973.     end;
  2974.   {$IFDEF DEBUG}
  2975.   DbgLog(
  2976.     Self, 'Periodic advise ' + inttostr(Packet.FAdviseCookie) + ', shunted to ' +
  2977.     inttostr(Packet.FEventTime div (UNITS div MILLISECONDS))
  2978.   );
  2979.   {$ENDIF}
  2980.   finally
  2981.     FSerialize.Unlock;
  2982.   end;
  2983. end;
  2984. procedure TBCAMSchedule.Delete(Packet: TBCAdvisePacket);
  2985. const
  2986.   CacheMax = 5; // Don't bother caching more than five
  2987. begin
  2988.   if (FCacheCount >= CacheMax) then FreeAndNil(Packet)
  2989.   else
  2990.   begin
  2991.     FSerialize.Lock;
  2992.     try
  2993.       Packet.FNext := FAdviseCache;
  2994.       FAdviseCache := Packet;
  2995.       inc(FCacheCount);
  2996.     finally
  2997.       FSerialize.Unlock;
  2998.     end;
  2999.   end;
  3000. end;
  3001. // milenko end
  3002. // milenko start refclock implementation
  3003. function AdviseThreadFunction(p: Pointer): DWORD; stdcall;
  3004. begin
  3005.   Result := TBCBaseReferenceClock(p).AdviseThread;
  3006. end;
  3007. constructor TBCBaseReferenceClock.Create(Name: String; Unk: IUnknown; out hr: HRESULT;
  3008.   Sched: TBCAMSchedule);
  3009. var
  3010.   tc : TIMECAPS;
  3011.   ThreadID : DWORD;
  3012. begin
  3013.   inherited Create(Name,Unk);
  3014.   FLastGotTime := 0;
  3015.   FTimerResolution := 0;
  3016.   FAbort := False;
  3017.   if not Assigned(Sched)
  3018.     then FSchedule := TBCAMSchedule.Create(CreateEvent(nil,False,False,nil))
  3019.     else FSchedule := Sched;
  3020.   ASSERT(fSchedule <> nil);
  3021.   if not Assigned(FSchedule) then
  3022.   begin
  3023.     hr := E_OUTOFMEMORY;
  3024.   end else
  3025.   begin
  3026.     FLock := TBCCritSec.Create;
  3027.     // Set up the highest resolution timer we can manage
  3028.     if (timeGetDevCaps(@tc, sizeof(tc)) = TIMERR_NOERROR)
  3029.       then FTimerResolution := tc.wPeriodMin
  3030.       else FTimerResolution := 1;
  3031.     timeBeginPeriod(FTimerResolution);
  3032.     // Initialise our system times - the derived clock should set the right values 
  3033.     FPrevSystemTime := timeGetTime;
  3034.     FPrivateTime := (UNITS div MILLISECONDS) * FPrevSystemTime;
  3035.     {$IFDEF PERF}
  3036.       FGetSystemTime := MSR_REGISTER('TBCBaseReferenceClock.GetTime');
  3037.     {$ENDIF}
  3038.     if not Assigned(Sched) then
  3039.   begin
  3040.     FThread := CreateThread(nil,                      // Security attributes
  3041.                         0,                      // Initial stack size
  3042.                         @AdviseThreadFunction,  // Thread start address
  3043.                                 Self,                   // Thread parameter
  3044.                                 0,                      // Creation flags
  3045.                                 ThreadID);              // Thread identifier
  3046.     if (FThread > 0) then
  3047.       begin
  3048.         SetThreadPriority(FThread, THREAD_PRIORITY_TIME_CRITICAL);
  3049.     end else
  3050.     begin
  3051.         hr := E_FAIL;
  3052.         CloseHandle(FSchedule.GetEvent);
  3053.         FreeAndNil(FSchedule);
  3054.     end;
  3055.     end;
  3056. end;
  3057. end;
  3058. destructor TBCBaseReferenceClock.Destroy;
  3059. begin
  3060.   if (FTimerResolution > 0) then
  3061.   begin
  3062.     timeEndPeriod(FTimerResolution);
  3063.     FTimerResolution := 0;
  3064.   end;
  3065.   FSchedule.DumpLinkedList;
  3066.   if (FThread > 0) then
  3067.   begin
  3068.     FAbort := True;
  3069.     TriggerThread;
  3070.     WaitForSingleObject(FThread, INFINITE);
  3071.     CloseHandle(FSchedule.GetEvent);
  3072.     FreeAndNil(FSchedule);
  3073.   end;
  3074.   if Assigned(FLock) then FreeAndNil(FLock);
  3075.   inherited Destroy;
  3076. end;
  3077. function TBCBaseReferenceClock.AdviseThread: HRESULT;
  3078. var
  3079.   dwWait : DWORD;
  3080.   rtNow  : TReferenceTime;
  3081.   llWait : LONGLONG;
  3082. begin
  3083.   dwWait := INFINITE;
  3084.   // The first thing we do is wait until something interesting happens
  3085.   // (meaning a first advise or shutdown).  This prevents us calling
  3086.   // GetPrivateTime immediately which is goodness as that is a virtual
  3087.   // routine and the derived class may not yet be constructed.  (This
  3088.   // thread is created in the base class constructor.)
  3089.   while not FAbort do
  3090.   begin
  3091.     // Wait for an interesting event to happen
  3092.     {$IFDEF DEBUG}
  3093.     DbgLog(Self,'AdviseThread Delay: ' + inttostr(dwWait) + ' ms');
  3094.     {$ENDIF}
  3095.     WaitForSingleObject(FSchedule.GetEvent, dwWait);
  3096.     if FAbort then break;
  3097.     // There are several reasons why we need to work from the internal
  3098.     // time, mainly to do with what happens when time goes backwards.
  3099.     // Mainly, it stop us looping madly if an event is just about to
  3100.     // expire when the clock goes backward (i.e. GetTime stop for a
  3101.     // while).
  3102.     rtNow := GetPrivateTime;
  3103.     {$IFDEF DEBUG}
  3104.     DbgLog(
  3105.       Self,'AdviseThread Woke at = ' + inttostr(RefTimeToMiliSec(rtNow)) + ' ms'
  3106.     );
  3107.     {$ENDIF}
  3108.     // We must add in a millisecond, since this is the resolution of our
  3109.     // WaitForSingleObject timer.  Failure to do so will cause us to loop
  3110.     // franticly for (approx) 1 a millisecond.
  3111.     FNextAdvise := FSchedule.Advise(10000 + rtNow);
  3112.     llWait := FNextAdvise - rtNow;
  3113.     ASSERT(llWait > 0);
  3114.     llWait := RefTimeToMiliSec(llWait);
  3115.     // DON'T replace this with a max!! (The type's of these things is VERY important)
  3116.     if (llWait > REFERENCE_TIME(HIGH(DWORD))) then dwWait := HIGH(DWORD)
  3117.                                               else dwWait := DWORD(llWait)
  3118.   end;
  3119.   Result := NOERROR;
  3120. end;
  3121. function TBCBaseReferenceClock.NonDelegatingQueryInterface(const IID: TGUID;
  3122.   out Obj): HResult; stdcall;
  3123. begin
  3124.   if (IsEqualGUID(IID,IID_IReferenceClock)) then
  3125.   begin
  3126.     if GetInterface(IID,Obj) then Result := S_OK
  3127.                              else Result := E_NOINTERFACE;
  3128.   end
  3129.   else
  3130.     Result := inherited NonDelegatingQueryInterface(IID, Obj);
  3131. end;
  3132. function TBCBaseReferenceClock.GetTime(out Time: int64): HResult; stdcall;
  3133. var
  3134.   Now_ : TReferenceTime;
  3135. begin
  3136.   if Assigned(@Time) then
  3137.   begin
  3138.     FLock.Lock;
  3139.     try
  3140.       Now_ := GetPrivateTime;
  3141.       if (Now_ > FLastGotTime) then
  3142.       begin
  3143.         FLastGotTime := Now_;
  3144.         Result := S_OK;
  3145.       end else
  3146.       begin
  3147.         Result := S_FALSE;
  3148.       end;
  3149.       Time := FLastGotTime;
  3150.     finally
  3151.       FLock.UnLock;
  3152.     end;
  3153.     {$IFDEF PERF}
  3154.     MSR_INTEGER(FGetSystemTime, Time div (UNITS div MILLISECONDS));
  3155.     {$ENDIF}
  3156.   end else Result := E_POINTER;
  3157. end;
  3158. function TBCBaseReferenceClock.AdviseTime(BaseTime, StreamTime: int64;
  3159.   Event: THandle; out AdviseCookie: DWORD): HResult; stdcall;
  3160. var
  3161.   RefTime : TReferenceTime;
  3162. begin
  3163.   if @AdviseCookie = nil then
  3164.   begin
  3165.     Result := E_POINTER;
  3166.     Exit;
  3167.   end;
  3168.   AdviseCookie := 0;
  3169.   // Check that the event is not already set
  3170.   ASSERT(WAIT_TIMEOUT = WaitForSingleObject(Event,0));
  3171.   RefTime := BaseTime + StreamTime;
  3172.   if ((RefTime <= 0) or (RefTime = MAX_TIME)) then
  3173.   begin
  3174.     Result := E_INVALIDARG;
  3175.   end else
  3176.   begin
  3177.     AdviseCookie := FSchedule.AddAdvisePacket(RefTime, 0, Event, False);
  3178.     if AdviseCookie > 0 then Result := NOERROR
  3179.                         else Result := E_OUTOFMEMORY;
  3180.   end;
  3181. end;
  3182. function TBCBaseReferenceClock.AdvisePeriodic(const StartTime, PeriodTime: int64;
  3183.   Semaphore: THandle; out AdviseCookie: DWORD): HResult; stdcall;
  3184. begin
  3185.   if @AdviseCookie = nil then
  3186.   begin
  3187.     Result := E_POINTER;
  3188.     Exit;
  3189.   end;
  3190.   AdviseCookie := 0;
  3191.   if ((StartTime > 0) and (PeriodTime > 0) and (StartTime <> MAX_TIME)) then
  3192.   begin
  3193.     AdviseCookie := FSchedule.AddAdvisePacket(StartTime,PeriodTime,Semaphore,True);
  3194.     if AdviseCookie > 0 then Result := NOERROR
  3195.                         else Result := E_OUTOFMEMORY;
  3196.   end
  3197.     else Result := E_INVALIDARG;
  3198. end;
  3199. function TBCBaseReferenceClock.Unadvise(AdviseCookie: DWORD): HResult; stdcall;
  3200. begin
  3201.   Result := FSchedule.Unadvise(AdviseCookie);
  3202. end;
  3203. function TBCBaseReferenceClock.GetPrivateTime: TReferenceTime;
  3204. var
  3205.   Time_ : DWORD;
  3206. begin
  3207.   FLock.Lock;
  3208.   try
  3209.     (* If the clock has wrapped then the current time will be less than
  3210.      * the last time we were notified so add on the extra milliseconds
  3211.      *
  3212.      * The time period is long enough so that the likelihood of
  3213.      * successive calls spanning the clock cycle is not considered.
  3214.      *)
  3215.     Time_ := timeGetTime;
  3216.     FPrivateTime := FPrivateTime + Int32x32To64(UNITS div MILLISECONDS, DWORD(Time_ - FPrevSystemTime));
  3217.     FPrevSystemTime := Time_;
  3218.   finally
  3219.     FLock.UnLock;
  3220.   end;
  3221.   Result := FPrivateTime;
  3222. end;
  3223. function TBCBaseReferenceClock.SetTimeDelta(const TimeDelta: TReferenceTime): HRESULT; stdcall;
  3224. {$IFDEF DEBUG}
  3225. var
  3226.   llDelta : LONGLONG;
  3227.   usDelta : Longint;
  3228.   delta : DWORD;
  3229.   Severity : integer;
  3230. {$ENDIF}
  3231. begin
  3232. {$IFDEF DEBUG}
  3233.   // Just break if passed an improper time delta value
  3234.   if TimeDelta > 0 then llDelta := TimeDelta
  3235.                    else llDelta := -TimeDelta;
  3236.   if (llDelta > UNITS * 1000) then
  3237.   begin
  3238.     DbgLog(Self,'Bad Time Delta');
  3239.     // DebugBreak;
  3240.   end;
  3241.   // We're going to calculate a "severity" for the time change. Max -1
  3242.   // min 8.  We'll then use this as the debug logging level for a
  3243.   // debug log message.
  3244.   usDelta := Longint(TimeDelta div 10);      // Delta in micro-secs
  3245.   delta := abs(usDelta);            // varying delta
  3246.   // Severity == 8 - ceil(log<base 8>(abs( micro-secs delta)))
  3247.   Severity := 8;
  3248.   while (delta > 0) do
  3249.   begin
  3250.     delta := delta shr 3;  // div 8
  3251.     dec(Severity);
  3252.   end;
  3253.   // Sev == 0 => > 2 second delta!
  3254.   DbgLog(
  3255.     Self, 'Sev ' + inttostr(Severity) + ': CSystemClock::SetTimeDelta(' +
  3256.     inttostr(usDelta) + ' us) ' + inttostr(RefTimeToMiliSec(FPrivateTime)) +
  3257.     ' -> ' + inttostr(RefTimeToMiliSec(TimeDelta + FPrivateTime)) + ' ms'
  3258.   );
  3259. {$ENDIF}
  3260.   FLock.Lock;
  3261.   try
  3262.     FPrivateTime := FPrivateTime + TimeDelta;
  3263.     // If time goes forwards, and we have advises, then we need to
  3264.     // trigger the thread so that it can re-evaluate its wait time.
  3265.     // Since we don't want the cost of the thread switches if the change
  3266.     // is really small, only do it if clock goes forward by more than
  3267.     // 0.5 millisecond.  If the time goes backwards, the thread will
  3268.     // wake up "early" (relativly speaking) and will re-evaluate at
  3269.     // that time.
  3270.     if ((TimeDelta > 5000) and (FSchedule.GetAdviseCount > 0)) then TriggerThread;
  3271.   finally
  3272.     FLock.UnLock;
  3273.   end;
  3274.   Result := NOERROR;
  3275. end;
  3276. function TBCBaseReferenceClock.GetSchedule : TBCAMSchedule;
  3277. begin
  3278.   Result := FSchedule;
  3279. end;
  3280. procedure TBCBaseReferenceClock.TriggerThread;
  3281. begin
  3282. {$IFDEF DEBUG}
  3283.   DbgLog(Self,'TriggerThread : ' + inttostr(FSchedule.GetEvent));
  3284. {$ENDIF}
  3285.   SetEvent(FSchedule.GetEvent);
  3286. end;
  3287. // milenko end
  3288. // milenko start sysclock implementation
  3289. constructor TBCSystemClock.Create(Name: WideString; Unk : IUnknown; out hr : HRESULT);
  3290. begin
  3291.   inherited Create(Name,Unk,hr);
  3292. end;
  3293. function TBCSystemClock.NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult;
  3294. begin
  3295.   if IsEqualGUID(IID,IID_IPersist) then
  3296.   begin
  3297.     if GetInterface(IID,Obj) then Result := S_OK
  3298.                              else Result := E_NOINTERFACE;
  3299.   end else
  3300.   if IsEqualGUID(IID,IID_IAMClockAdjust) then
  3301.   begin
  3302.     if GetInterface(IID,Obj) then Result := S_OK
  3303.                              else Result := E_NOINTERFACE;
  3304.   end
  3305.   else Result := inherited NonDelegatingQueryInterface(IID,Obj);
  3306. end;
  3307. function TBCSystemClock.GetClassID(out classID: TCLSID): HResult; stdcall;
  3308. begin
  3309.   if not Assigned(@ClassID) then
  3310.   begin
  3311.     Result := E_POINTER;
  3312.     Exit;
  3313.   end;
  3314.   classID := CLSID_SystemClock;
  3315.   Result := NOERROR;
  3316. end;
  3317. function TBCSystemClock.SetClockDelta(rtDelta: TReferenceTime): HResult; stdcall;
  3318. begin
  3319.   Result := SetTimeDelta(rtDelta);
  3320. end;
  3321. // milenko end
  3322. initialization
  3323. {$IFDEF DEBUG}
  3324.   {$IFDEF VER130}
  3325.     AssertErrorProc := @DbgAssert;
  3326.   {$ELSE}
  3327.     AssertErrorProc := DbgAssert;
  3328.  {$ENDIF}
  3329.  {$IFNDEF MESSAGE}
  3330.   AssignFile(DebugFile, ParamStr(0) + '.log');
  3331.   if FileExists(ParamStr(0) + '.log') then
  3332.     Append(DebugFile) else
  3333.     Rewrite(DebugFile);
  3334.  {$ENDIF}
  3335. {$ENDIF}
  3336. finalization
  3337. begin
  3338.   if TemplatesVar <> nil then TemplatesVar.Free;
  3339.   TemplatesVar := nil;
  3340. {$IFDEF DEBUG}
  3341.  {$IFNDEF MESSAGE}
  3342.   Writeln(DebugFile, format('FactoryCount: %d, ObjectCount: %d.',[FactoryCount, ObjectCount]));
  3343.   CloseFile(DebugFile);
  3344.  {$ELSE}
  3345.   OutputDebugString(PChar(format('FactoryCount: %d, ObjectCount: %d.',[FactoryCount, ObjectCount])));
  3346.  {$ENDIF}
  3347. {$ENDIF}
  3348. // milenko start (only needed with PERF)
  3349. {$IFDEF PERF}
  3350.   SetLength(Incidents, 0);
  3351.   SetLength(IncidentsLog, 0);
  3352. {$ENDIF}
  3353. // milenko end
  3354. end;
  3355. end.