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

Delphi控件源码

开发平台:

Delphi

  1.         ASSERT(Fetched = 0);
  2.         result := VFW_E_ENUM_OUT_OF_SYNC;
  3.         exit;
  4.       end;
  5.       // We only want to return this pin, if it is not in our cache
  6.       if FPinCache.IndexOf(Pin) = -1 then
  7.       begin
  8.         // From the object get an IPin interface
  9.         TPointerDynArray(@ppPins)[Fetched] := nil;
  10.         TIPinDynArray(@ppPins)[Fetched] := Pin;
  11.         inc(Fetched);
  12.         FPinCache.Add(Pin);
  13.         dec(RealPins);
  14.       end;
  15.     end;
  16.     if (pcFetched <> nil) then pcFetched^ := Fetched;
  17.     if (cPins = Fetched) then result := NOERROR else result := S_FALSE;
  18. end;
  19. function TBCEnumPins.Skip(cPins: ULONG): HRESULT;
  20. var PinsLeft: Cardinal;
  21. begin
  22.   // Check we are still in sync with the filter
  23.   if AreWeOutOfSync then
  24.   begin
  25.     result := VFW_E_ENUM_OUT_OF_SYNC;
  26.     exit;
  27.   end;
  28.   // Work out how many pins are left to skip over
  29.   // We could position at the end if we are asked to skip too many...
  30.   // ..which would match the base implementation for CEnumMediaTypes::Skip
  31.   PinsLeft := FPinCount - FPosition;
  32.   if (cPins > PinsLeft) then
  33.   begin
  34.     result := S_FALSE;
  35.     exit;
  36.   end;
  37.   inc(FPosition, cPins);
  38.   result := NOERROR;
  39. end;
  40. function TBCEnumPins.Reset: HRESULT;
  41. begin
  42.   FVersion  := FFilter.GetPinVersion;
  43.   FPinCount := FFilter.GetPinCount;
  44.   FPosition := 0;
  45.   FPinCache.Clear;
  46.   result := S_OK;
  47. end;
  48. function TBCEnumPins.Refresh: HRESULT;
  49. begin
  50.   FVersion  := FFilter.GetPinVersion;
  51.   FPinCount := FFilter.GetPinCount;
  52.   Fposition := 0;
  53.   result    := S_OK;
  54. end;
  55. function TBCEnumPins.AreWeOutOfSync: boolean;
  56. begin
  57.   if FFilter.GetPinVersion = FVersion then result:= FALSE else result := True;
  58. end;
  59. { TBCBasePin }
  60. { Called by IMediaFilter implementation when the state changes from Stopped
  61.   to either paused or running and in derived classes could do things like
  62.   commit memory and grab hardware resource (the default is to do nothing) }
  63. function TBCBasePin.Active: HRESULT;
  64. begin
  65.   result := NOERROR;
  66. end;
  67. { This is called to make the connection, including the task of finding
  68.   a media type for the pin connection. pmt is the proposed media type
  69.   from the Connect call: if this is fully specified, we will try that.
  70.   Otherwise we enumerate and try all the input pin's types first and
  71.   if that fails we then enumerate and try all our preferred media types.
  72.   For each media type we check it against pmt (if non-null and partially
  73.   specified) as well as checking that both pins will accept it. }
  74. function TBCBasePin.AgreeMediaType(ReceivePin: IPin; pmt: PAMMediaType): HRESULT;
  75. var
  76.   EnumMT: IEnumMediaTypes;
  77.   hrFailure: HResult;
  78.   i: integer;
  79. begin
  80.   ASSERT(ReceivePin <> nil);
  81.   // if the media type is fully specified then use that
  82.   if ((pmt <> nil) and (not TBCMediaType(pmt).IsPartiallySpecified)) then
  83.   begin
  84.     // if this media type fails, then we must fail the connection
  85.     // since if pmt is nonnull we are only allowed to connect
  86.     // using a type that matches it.
  87.     result := AttemptConnection(ReceivePin, pmt);
  88.     exit;
  89.   end;
  90.   // Try the other pin's enumerator
  91.   hrFailure := VFW_E_NO_ACCEPTABLE_TYPES;
  92.   for i := 0 to 1 do
  93.   begin
  94.     if (i = byte(FTryMyTypesFirst)) then
  95.          result := ReceivePin.EnumMediaTypes(EnumMT)
  96.     else result := EnumMediaTypes(EnumMT);
  97.     if Succeeded(Result) then
  98.     begin
  99.       Assert(EnumMT <> nil);
  100.       result := TryMediaTypes(ReceivePin,pmt,EnumMT);
  101.       EnumMT := nil;
  102.       if Succeeded(result) then
  103.         begin
  104.           result := NOERROR;
  105.           exit;
  106.         end
  107.       else
  108.         begin
  109.           // try to remember specific error codes if there are any
  110.           if ((result <> E_FAIL) and
  111.               (result <> E_INVALIDARG) and
  112.               (result <> VFW_E_TYPE_NOT_ACCEPTED)) then hrFailure := result;
  113.         end;
  114.     end;
  115.   end;
  116.   result := hrFailure;
  117. end;
  118. function TBCBasePin.AttemptConnection(ReceivePin: IPin; pmt: PAMMediaType): HRESULT;
  119. begin
  120.   // The caller should hold the filter lock becasue this function
  121.   // uses m_Connected.  The caller should also hold the filter lock
  122.   // because this function calls SetMediaType(), IsStopped() and
  123.   // CompleteConnect().
  124.   ASSERT(FLock.CritCheckIn);
  125.   // Check that the connection is valid  -- need to do this for every
  126.   // connect attempt since BreakConnect will undo it.
  127.   result := CheckConnect(ReceivePin);
  128.   if FAILED(result) then
  129.   begin
  130.   {$IFDEF DEBUG}
  131.     DbgLog(self, 'CheckConnect failed');
  132.   {$ENDIF}
  133.     // Since the procedure is already returning an error code, there
  134.     // is nothing else this function can do to report the error.
  135.     Assert(SUCCEEDED(BreakConnect));
  136.     exit;
  137.   end;
  138.   DisplayTypeInfo(ReceivePin, pmt);
  139.   // Check we will accept this media type
  140.   result := CheckMediaType(pmt);
  141.   if (result = NOERROR) then
  142.     begin
  143.       // Make ourselves look connected otherwise ReceiveConnection
  144.       // may not be able to complete the connection
  145.       FConnected := ReceivePin;
  146.       result := SetMediaType(pmt);
  147.       if Succeeded(result) then
  148.       begin
  149.         // See if the other pin will accept this type */
  150.         result := ReceivePin.ReceiveConnection(self, pmt^);
  151.         if Succeeded(result) then
  152.         begin
  153.           // Complete the connection
  154.           result := CompleteConnect(ReceivePin);
  155.           if Succeeded(result) then exit
  156.           else
  157.             begin
  158.             {$IFDEF DEBUG}
  159.               DbgLog(self, 'Failed to complete connection');
  160.             {$ENDIF}
  161.               ReceivePin.Disconnect;
  162.             end;
  163.         end;
  164.       end;
  165.     end
  166.   else
  167.     begin
  168.       // we cannot use this media type
  169.       // return a specific media type error if there is one
  170.       // or map a general failure code to something more helpful
  171.       // (in particular S_FALSE gets changed to an error code)
  172.       if (SUCCEEDED(result) or (result = E_FAIL) or (result = E_INVALIDARG)) then
  173.         result := VFW_E_TYPE_NOT_ACCEPTED;
  174.     end;
  175.   // BreakConnect and release any connection here in case CheckMediaType
  176.   // failed, or if we set anything up during a call back during
  177.   // ReceiveConnection.
  178.   // Since the procedure is already returning an error code, there
  179.   // is nothing else this function can do to report the error.
  180.   Assert(Succeeded(BreakConnect));
  181.   //  If failed then undo our state
  182.   FConnected := nil;
  183. end;
  184. { This is called when we realise we can't make a connection to the pin and
  185.   must undo anything we did in CheckConnect - override to release QIs done }
  186. function TBCBasePin.BreakConnect: HRESULT;
  187. begin
  188.   result := NOERROR;
  189. end;
  190. { This is called during Connect() to provide a virtual method that can do
  191.   any specific check needed for connection such as QueryInterface. This
  192.   base class method just checks that the pin directions don't match }
  193. function TBCBasePin.CheckConnect(Pin: IPin): HRESULT;
  194. var pd: TPinDirection;
  195. begin
  196.   // Check that pin directions DONT match
  197.   Pin.QueryDirection(pd);
  198.   ASSERT((pd = PINDIR_OUTPUT) or (pd = PINDIR_INPUT));
  199.   ASSERT((Fdir = PINDIR_OUTPUT) or (Fdir = PINDIR_INPUT));
  200.   // we should allow for non-input and non-output connections?
  201.   if (pd = Fdir) then result := VFW_E_INVALID_DIRECTION
  202.                  else result := NOERROR;
  203. end;
  204. { Called when we want to complete a connection to another filter. Failing
  205.   this will also fail the connection and disconnect the other pin as well }
  206. function TBCBasePin.CompleteConnect(ReceivePin: IPin): HRESULT;
  207. begin
  208.   result := NOERROR;
  209. end;
  210.  { Asked to connect to a pin. A pin is always attached to an owning filter
  211.    object so we always delegate our locking to that object. We first of all
  212.    retrieve a media type enumerator for the input pin and see if we accept
  213.    any of the formats that it would ideally like, failing that we retrieve
  214.    our enumerator and see if it will accept any of our preferred types }
  215. function TBCBasePin.Connect(pReceivePin: IPin; const pmt: PAMMediaType): HRESULT;
  216. var HR: HResult;
  217. begin
  218.   FLock.Lock;
  219.   try
  220.     DisplayPinInfo(pReceivePin);
  221.     // See if we are already connected
  222.     if FConnected <> nil then
  223.     begin
  224.     {$IFDEF DEBUG}
  225.       DbgLog(self, 'Already connected');
  226.     {$ENDIF}
  227.       result := VFW_E_ALREADY_CONNECTED;
  228. // milenko start
  229.       Exit;
  230. // milenko end
  231.     end;
  232.     // See if the filter is active
  233.     if (not IsStopped) and (not FCanReconnectWhenActive) then
  234.     begin
  235.       result := VFW_E_NOT_STOPPED;
  236.       exit;
  237.     end;
  238.     // Find a mutually agreeable media type -
  239.     // Pass in the template media type. If this is partially specified,
  240.     // each of the enumerated media types will need to be checked against
  241.     // it. If it is non-null and fully specified, we will just try to connect
  242.     // with this.
  243.     Hr := AgreeMediaType(pReceivePin, pmt);
  244.     if Failed(hr) then
  245.     begin
  246.     {$IFDEF DEBUG}
  247.       DbgLog(self, 'Failed to agree type');
  248.     {$ENDIF}
  249.       // Since the procedure is already returning an error code, there
  250.       // is nothing else this function can do to report the error.
  251.       ASSERT(SUCCEEDED(BreakConnect));
  252.       result := HR;
  253.       exit;
  254.     end;
  255.   {$IFDEF DEBUG}
  256.     DbgLog(self, 'Connection succeeded');
  257.   {$ENDIF}
  258.     result := NOERROR;
  259.   finally
  260.     FLock.UnLock;
  261.   end;
  262. end;
  263. // Return an AddRef()'d pointer to the connected pin if there is one
  264. function TBCBasePin.ConnectedTo(out pPin: IPin): HRESULT;
  265. begin
  266.     //  It's pointless to lock here.
  267.     //  The caller should ensure integrity.
  268.     pPin := FConnected;
  269.     if (pPin <> nil) then
  270.          result := S_OK
  271.     else result := VFW_E_NOT_CONNECTED;
  272. end;
  273. function TBCBasePin.ConnectionMediaType(out pmt: TAMMediaType): HRESULT;
  274. begin
  275.   FLock.Lock;
  276.   try
  277.     //  Copy constructor of m_mt allocates the memory
  278.     if IsConnected then
  279.       begin
  280.         CopyMediaType(@pmt,@Fmt);
  281.         result := S_OK;
  282.       end
  283.     else
  284.       begin
  285.         zeromemory(@pmt, SizeOf(TAMMediaType));
  286.         pmt.lSampleSize := 1;
  287.         pmt.bFixedSizeSamples := True;
  288.         result := VFW_E_NOT_CONNECTED;
  289.       end;
  290.   finally
  291.     FLock.UnLock;
  292.   end;
  293. end;
  294. constructor TBCBasePin.Create(ObjectName: string; Filter: TBCBaseFilter;
  295.   Lock: TBCCritSec; out hr: HRESULT; Name: WideString;
  296.   dir: TPinDirection);
  297. begin
  298.   inherited Create(ObjectName, nil);
  299.   FFilter                 := Filter;
  300.   FLock                   := Lock;
  301.   FPinName                := Name;
  302.   FConnected              := nil;
  303.   Fdir                    := dir;
  304.   FRunTimeError           := FALSE;
  305.   FQSink                  := nil;
  306.   FTypeVersion            := 1;
  307.   FStart                  := 0;
  308.   FStop                   := MAX_TIME;
  309.   FCanReconnectWhenActive := false;
  310.   FTryMyTypesFirst        := false;
  311.   FRate                   := 1.0;
  312.   { WARNING - Filter is often not a properly constituted object at
  313.     this state (in particular QueryInterface may not work) - this
  314.     is because its owner is often its containing object and we
  315.     have been called from the containing object's constructor so
  316.     the filter's owner has not yet had its CUnknown constructor
  317.     called.}
  318.   FRef := 0; // debug
  319.   ZeroMemory(@fmt, SizeOf(TAMMediaType));
  320.   ASSERT(Filter <> nil);
  321.   ASSERT(Lock <> nil);
  322. end;
  323. destructor TBCBasePin.destroy;
  324. begin
  325.   //  We don't call disconnect because if the filter is going away
  326.   //  all the pins must have a reference count of zero so they must
  327.   //  have been disconnected anyway - (but check the assumption)
  328.   ASSERT(FConnected = nil);
  329.   FPinName := '';
  330.   Assert(FRef = 0);
  331.   FreeMediaType(@fmt);
  332.   inherited Destroy;
  333. end;
  334. // Called when we want to terminate a pin connection
  335. function TBCBasePin.Disconnect: HRESULT;
  336. begin
  337.   FLock.Lock;
  338.   try
  339.     // See if the filter is active
  340.     if not IsStopped then
  341.          result := VFW_E_NOT_STOPPED
  342.     else result := DisconnectInternal;
  343.   finally
  344.     FLock.UnLock;
  345.   end;
  346. end;
  347. function TBCBasePin.DisconnectInternal: HRESULT;
  348. begin
  349.   ASSERT(FLock.CritCheckIn);
  350.   if (FConnected <> nil) then
  351.     begin
  352.       result := BreakConnect;
  353.       if FAILED(result) then
  354.       begin
  355.         // There is usually a bug in the program if BreakConnect() fails.
  356.       {$IFDEF DEBUG}
  357.         DbgLog(self, 'WARNING: BreakConnect() failed in CBasePin::Disconnect().');
  358.       {$ENDIF}
  359.         exit;
  360.       end;
  361.       FConnected := nil;
  362.       result := S_OK;
  363.       exit;
  364.     end
  365.   else
  366.     // no connection - not an error
  367.     result := S_FALSE;
  368. end;
  369. procedure TBCBasePin.DisplayPinInfo(ReceivePin: IPin);
  370. {$IFDEF DEBUG}
  371. const
  372.   BadPin : WideString = 'Bad Pin';
  373. var
  374.   ConnectPinInfo, ReceivePinInfo: TPinInfo;
  375. begin
  376.   if FAILED(QueryPinInfo(ConnectPinInfo)) then
  377.        move(Pointer(BadPin)^, ConnectPinInfo.achName, length(BadPin) * 2 +2)
  378.   else ConnectPinInfo.pFilter := nil;
  379.   if FAILED(ReceivePin.QueryPinInfo(ReceivePinInfo)) then
  380.         move(Pointer(BadPin)^, ReceivePinInfo.achName, length(BadPin) * 2 +2)
  381.    else ReceivePinInfo.pFilter := nil;
  382.   DbgLog(self, 'Trying to connect Pins :');
  383.   DbgLog(self, format('    <%s>', [ConnectPinInfo.achName]));
  384.   DbgLog(self, format('    <%s>', [ReceivePinInfo.achName]));
  385. {$ELSE}
  386. begin
  387. {$ENDIF}
  388. end;
  389. procedure TBCBasePin.DisplayTypeInfo(Pin: IPin; pmt: PAMMediaType);
  390. begin
  391. {$IFDEF DEBUG}
  392.   DbgLog(self, 'Trying media type:');
  393.   DbgLog(self, '    major type:  '+ GuidToString(pmt.majortype));
  394.   DbgLog(self, '    sub type  :  '+ GuidToString(pmt.subtype));
  395.   DbgLog(self, GetMediaTypeDescription(pmt));  
  396. {$ENDIF}
  397. end;
  398. // Called when no more data will arrive
  399. function TBCBasePin.EndOfStream: HRESULT;
  400. begin
  401.   result := S_OK;
  402. end;
  403. { This can be called to return an enumerator for the pin's list of preferred
  404.   media types. An input pin is not obliged to have any preferred formats
  405.   although it can do. For example, the window renderer has a preferred type
  406.   which describes a video image that matches the current window size. All
  407.   output pins should expose at least one preferred format otherwise it is
  408.   possible that neither pin has any types and so no connection is possible }
  409. function TBCBasePin.EnumMediaTypes(out ppEnum: IEnumMediaTypes): HRESULT;
  410. begin
  411.   // Create a new ref counted enumerator
  412.   ppEnum := TBCEnumMediaTypes.Create(self, nil);
  413.   if (ppEnum = nil) then result := E_OUTOFMEMORY
  414.                     else result := NOERROR;
  415. end;
  416. { This is a virtual function that returns a media type corresponding with
  417.   place iPosition in the list. This base class simply returns an error as
  418.   we support no media types by default but derived classes should override }
  419. function TBCBasePin.GetMediaType(Position: integer;
  420.   out MediaType: PAMMediaType): HRESULT;
  421. begin
  422.   result := E_UNEXPECTED;;
  423. end;
  424. { This is a virtual function that returns the current media type version.
  425.   The base class initialises the media type enumerators with the value 1
  426.   By default we always returns that same value. A Derived class may change
  427.   the list of media types available and after doing so it should increment
  428.   the version either in a method derived from this, or more simply by just
  429.   incrementing the m_TypeVersion base pin variable. The type enumerators
  430.   call this when they want to see if their enumerations are out of date }
  431. function TBCBasePin.GetMediaTypeVersion: longint;
  432. begin
  433.   result := FTypeVersion;
  434. end;
  435. { Also called by the IMediaFilter implementation when the state changes to
  436.   Stopped at which point you should decommit allocators and free hardware
  437.   resources you grabbed in the Active call (default is also to do nothing) }
  438. function TBCBasePin.Inactive: HRESULT;
  439. begin
  440.   FRunTimeError := FALSE;
  441.   result := NOERROR;
  442. end;
  443. // Increment the cookie representing the current media type version
  444. procedure TBCBasePin.IncrementTypeVersion;
  445. begin
  446.   InterlockedIncrement(FTypeVersion);
  447. end;
  448. function TBCBasePin.IsConnected: boolean;
  449. begin
  450.   result := FConnected <> nil;
  451. end;
  452. function TBCBasePin.IsStopped: boolean;
  453. begin
  454.   result := FFilter.FState = State_Stopped;
  455. end;
  456. // NewSegment notifies of the start/stop/rate applying to the data
  457. // about to be received. Default implementation records data and
  458. // returns S_OK.
  459. // Override this to pass downstream.
  460. function TBCBasePin.NewSegment(tStart, tStop: TReferenceTime;
  461.   dRate: double): HRESULT;
  462. begin
  463.   FStart := tStart;
  464.   FStop  := tStop;
  465.   FRate  := dRate;
  466.   result := S_OK;
  467. end;
  468. function TBCBasePin.NonDelegatingAddRef: Integer;
  469. begin
  470.   ASSERT(InterlockedIncrement(FRef) > 0);
  471.   result := FFilter._AddRef;
  472. end;
  473. function TBCBasePin.NonDelegatingRelease: Integer;
  474. begin
  475.   ASSERT(InterlockedDecrement(FRef) >= 0);
  476.   result := FFilter._Release
  477. end;
  478. function TBCBasePin.Notify(pSelf: IBaseFilter; q: TQuality): HRESULT;
  479. begin
  480.   {$IFDEF DEBUG}
  481.     DbgLog(self, 'IQualityControl::Notify not over-ridden from CBasePin.  (IGNORE is OK)');
  482.   {$ENDIF}
  483.     result := E_NOTIMPL;
  484. end;
  485. { Does this pin support this media type WARNING this interface function does
  486.   not lock the main object as it is meant to be asynchronous by nature - if
  487.   the media types you support depend on some internal state that is updated
  488.   dynamically then you will need to implement locking in a derived class }
  489. function TBCBasePin.QueryAccept(const pmt: TAMMediaType): HRESULT;
  490. begin
  491.   { The CheckMediaType method is valid to return error codes if the media
  492.     type is horrible, an example might be E_INVALIDARG. What we do here
  493.     is map all the error codes into either S_OK or S_FALSE regardless }
  494.   result := CheckMediaType(@pmt);
  495.   if FAILED(result) then result := S_FALSE;
  496. end;
  497. function TBCBasePin.QueryDirection(out pPinDir: TPinDirection): HRESULT;
  498. begin
  499.   pPinDir := Fdir;
  500.   result  := NOERROR;
  501. end;
  502. function TBCBasePin.QueryId(out Id: PWideChar): HRESULT;
  503. begin
  504.   result := AMGetWideString(FPinName, id);
  505. end;
  506. function TBCBasePin.QueryInternalConnections(out apPin: IPin;
  507.   var nPin: ULONG): HRESULT;
  508. begin
  509.   result := E_NOTIMPL;
  510. end;
  511. // Return information about the filter we are connect to
  512. function TBCBasePin.QueryPinInfo(out pInfo: TPinInfo): HRESULT;
  513. begin
  514.   pInfo.pFilter := FFilter;
  515.   if (FPinName <> '') then
  516.     begin
  517.        move(Pointer(FPinName)^, pInfo.achName, length(FPinName)*2);
  518.        pInfo.achName[length(FPinName)] := #0;
  519.     end
  520.   else pInfo.achName[0] := #0;
  521.   pInfo.dir := Fdir;
  522.   result := NOERROR;
  523. end;
  524. { Called normally by an output pin on an input pin to try and establish a
  525.   connection. }
  526. function TBCBasePin.ReceiveConnection(pConnector: IPin;
  527.   const pmt: TAMMediaType): HRESULT;
  528. begin
  529.   FLock.Lock;
  530.   try
  531.     // Are we already connected
  532.     if (FConnected <> nil) then
  533.     begin
  534.       result := VFW_E_ALREADY_CONNECTED;
  535.       exit;
  536.     end;
  537.     // See if the filter is active
  538.     if (not IsStopped) and (not FCanReconnectWhenActive) then
  539.     begin
  540.       result := VFW_E_NOT_STOPPED;
  541.       exit;
  542.     end;
  543.     result := CheckConnect(pConnector);
  544.     if FAILED(result) then
  545.     begin
  546.       // Since the procedure is already returning an error code, there
  547.       // is nothing else this function can do to report the error.
  548.       ASSERT(SUCCEEDED(BreakConnect));
  549.       exit;
  550.     end;
  551.     // Ask derived class if this media type is ok
  552.     //CMediaType * pcmt = (CMediaType*) pmt;
  553.     result := CheckMediaType(@pmt);
  554.     if (result <> NOERROR) then
  555.     begin
  556.       // no -we don't support this media type
  557.       // Since the procedure is already returning an error code, there
  558.       // is nothing else this function can do to report the error.
  559.       ASSERT(SUCCEEDED(BreakConnect));
  560.       // return a specific media type error if there is one
  561.       // or map a general failure code to something more helpful
  562.       // (in particular S_FALSE gets changed to an error code)
  563.       if (SUCCEEDED(result) or
  564.           (result = E_FAIL) or
  565.           (result = E_INVALIDARG)) then
  566.         result := VFW_E_TYPE_NOT_ACCEPTED;
  567.       exit;
  568.     end;
  569.     // Complete the connection
  570.     FConnected := pConnector;
  571.     result := SetMediaType(@pmt);
  572.     if SUCCEEDED(result) then
  573.     begin
  574.       result := CompleteConnect(pConnector);
  575.       if SUCCEEDED(result) then
  576.       begin
  577.         result := S_OK;
  578.         exit;
  579.       end;
  580.     end;
  581.   {$IFDEF DEBUG}
  582.     DbgLog(self, 'Failed to set the media type or failed to complete the connection.');
  583.   {$ENDIF}
  584.     FConnected := nil;
  585.     // Since the procedure is already returning an error code, there
  586.     // is nothing else this function can do to report the error.
  587.     ASSERT(SUCCEEDED(BreakConnect));
  588.   finally
  589.     FLock.UnLock;
  590.   end;
  591. end;
  592. { Called by IMediaFilter implementation when the state changes from
  593.   to either paused to running and in derived classes could do things like
  594.   commit memory and grab hardware resource (the default is to do nothing) }
  595. function TBCBasePin.Run(Start: TReferenceTime): HRESULT;
  596. begin
  597.   result := NOERROR;
  598. end;
  599. function TBCBasePin.GetCurrentMediaType: TBCMediaType;
  600. begin
  601.   result := TBCMediaType(@FMT);
  602. end;
  603. function TBCBasePin.GetAMMediaType: PAMMediaType;
  604. begin
  605.   result := @FMT;
  606. end;
  607. { This is called to set the format for a pin connection - CheckMediaType
  608.   will have been called to check the connection format and if it didn't
  609.   return an error code then this (virtual) function will be invoked }
  610. function TBCBasePin.SetMediaType(mt: PAMMediaType): HRESULT;
  611. begin
  612.   FreeMediaType(@Fmt);
  613.   CopyMediaType(@Fmt, mt);
  614.   result := NOERROR;
  615. end;
  616. function TBCBasePin.SetSink(piqc: IQualityControl): HRESULT;
  617. begin
  618.   FLock.Lock;
  619.   try
  620.     FQSink := piqc;
  621.     result := NOERROR;
  622.   finally
  623.     FLock.UnLock;
  624.   end;
  625. end;
  626. { Given an enumerator we cycle through all the media types it proposes and
  627.   firstly suggest them to our derived pin class and if that succeeds try
  628.   them with the pin in a ReceiveConnection call. This means that if our pin
  629.   proposes a media type we still check in here that we can support it. This
  630.   is deliberate so that in simple cases the enumerator can hold all of the
  631.   media types even if some of them are not really currently available }
  632. function TBCBasePin.TryMediaTypes(ReceivePin: IPin; pmt: PAMMediaType;
  633.   Enum: IEnumMediaTypes): HRESULT;
  634. var
  635.   MediaCount: Cardinal;
  636.   hrFailure : HResult;
  637.   MediaType : PAMMediaType;
  638. begin
  639.   // Reset the current enumerator position
  640.   result := Enum.Reset;
  641.   if Failed(result) then exit;
  642.   MediaCount := 0;
  643.   // attempt to remember a specific error code if there is one
  644.   hrFailure := S_OK;
  645.   while True do
  646.   begin
  647.     { Retrieve the next media type NOTE each time round the loop the
  648.       enumerator interface will allocate another AM_MEDIA_TYPE structure
  649.       If we are successful then we copy it into our output object, if
  650.       not then we must delete the memory allocated before returning }
  651.     result := Enum.Next(1, MediaType, @MediaCount);
  652.     if (result <> S_OK) then
  653.     begin
  654.       if (S_OK = hrFailure) then
  655.         hrFailure := VFW_E_NO_ACCEPTABLE_TYPES;
  656.       result := hrFailure;
  657.       exit;
  658.     end;
  659.     ASSERT(MediaCount = 1);
  660.     ASSERT(MediaType <> nil);
  661.     // check that this matches the partial type (if any)
  662.     if (pmt = nil) or TBCMediaType(MediaType).MatchesPartial(pmt) then
  663.     begin
  664.       result := AttemptConnection(ReceivePin, MediaType);
  665.       // attempt to remember a specific error code
  666.       if FAILED(result)           and
  667.          SUCCEEDED(hrFailure)     and
  668.          (result <> E_FAIL)       and
  669.          (result <> E_INVALIDARG) and
  670.          (result <> VFW_E_TYPE_NOT_ACCEPTED) then hrFailure := result;
  671.     end
  672.     else result := VFW_E_NO_ACCEPTABLE_TYPES;
  673.     DeleteMediaType(MediaType);
  674.     if result = S_OK then exit;
  675.   end;
  676. end;
  677. { TBCEnumMediaTypes }
  678. { The media types a filter supports can be quite dynamic so we add to
  679.   the general IEnumXXXX interface the ability to be signaled when they
  680.   change via an event handle the connected filter supplies. Until the
  681.   Reset method is called after the state changes all further calls to
  682.   the enumerator (except Reset) will return E_UNEXPECTED error code. }
  683. function TBCEnumMediaTypes.AreWeOutOfSync: boolean;
  684. begin
  685.   if FPin.GetMediaTypeVersion = FVersion then result := FALSE else result := True;
  686. end;
  687. { One of an enumerator's basic member functions allows us to create a cloned
  688.   interface that initially has the same state. Since we are taking a snapshot
  689.   of an object (current position and all) we must lock access at the start }
  690. function TBCEnumMediaTypes.Clone(out ppEnum: IEnumMediaTypes): HRESULT;
  691. begin
  692.   result := NOERROR;
  693.   // Check we are still in sync with the pin
  694.   if AreWeOutOfSync then
  695.     begin
  696.       ppEnum := nil;
  697.       result := VFW_E_ENUM_OUT_OF_SYNC;
  698.       exit;
  699.     end
  700.   else
  701.     begin
  702.       ppEnum := TBCEnumMediaTypes.Create(FPin, self);
  703.       if (ppEnum = nil) then result := E_OUTOFMEMORY;
  704.     end;
  705. end;
  706. constructor TBCEnumMediaTypes.Create(Pin: TBCBasePin;
  707.   EnumMediaTypes: TBCEnumMediaTypes);
  708. begin
  709.   FPosition := 0;
  710.   FPin      := Pin;
  711.   {$IFDEF DEBUG}
  712.     DbgLog('TBCEnumMediaTypes.Create');
  713.   {$ENDIF}
  714.   // We must be owned by a pin derived from CBasePin */
  715.   ASSERT(Pin <> nil);
  716.   // Hold a reference count on our pin
  717.   FPin._AddRef;
  718.   // Are we creating a new enumerator
  719.   if (EnumMediaTypes = nil) then
  720.   begin
  721.     FVersion := FPin.GetMediaTypeVersion;
  722.     exit;
  723.   end;
  724.   FPosition := EnumMediaTypes.FPosition;
  725.   FVersion  := EnumMediaTypes.FVersion;
  726. end;
  727. { Destructor releases the reference count on our base pin. NOTE since we hold
  728.   a reference count on the pin who created us we know it is safe to release
  729.   it, no access can be made to it afterwards though as we might have just
  730.   caused the last reference count to go and the object to be deleted }
  731. destructor TBCEnumMediaTypes.Destroy;
  732. begin
  733.   {$IFDEF DEBUG}
  734.     DbgLog('TBCEnumMediaTypes.Destroy');
  735.   {$ENDIF}
  736.   FPin._Release;
  737.   inherited;
  738. end;
  739. { Enumerate the next pin(s) after the current position. The client using this
  740.    interface passes in a pointer to an array of pointers each of which will
  741.    be filled in with a pointer to a fully initialised media type format
  742.    Return NOERROR if it all works,
  743.           S_FALSE if fewer than cMediaTypes were enumerated.
  744.           VFW_E_ENUM_OUT_OF_SYNC if the enumerator has been broken by
  745.                                  state changes in the filter
  746.    The actual count always correctly reflects the number of types in the array.}
  747. function TBCEnumMediaTypes.Next(cMediaTypes: ULONG;
  748.   out ppMediaTypes: PAMMediaType; pcFetched: PULONG): HRESULT;
  749. type TMTDynArray = array of PAMMediaType;
  750. var
  751.   Fetched: Cardinal;
  752.   cmt: PAMMediaType;
  753. begin
  754.     // Check we are still in sync with the pin
  755.     if AreWeOutOfSync then
  756.       begin
  757.         result := VFW_E_ENUM_OUT_OF_SYNC;
  758.         exit;
  759.       end;
  760.     if (pcFetched <> nil) then
  761.       pcFetched^ := 0           // default unless we succeed
  762.     // now check that the parameter is valid
  763.     else
  764.       if (cMediaTypes > 1) then
  765.         begin     // pcFetched == NULL
  766.           result := E_INVALIDARG;
  767.           exit;
  768.         end;
  769.     Fetched := 0;           // increment as we get each one.
  770.     {  Return each media type by asking the filter for them in turn - If we
  771.        have an error code retured to us while we are retrieving a media type
  772.        we assume that our internal state is stale with respect to the filter
  773.        (for example the window size changing) so we return
  774.        VFW_E_ENUM_OUT_OF_SYNC }
  775.     new(cmt);
  776.     while (cMediaTypes > 0) do
  777.     begin
  778.         TBCMediaType(cmt).InitMediaType;
  779.         inc(FPosition);
  780.         result := FPin.GetMediaType(FPosition-1, cmt);
  781.         if (S_OK <> result) then Break;
  782.         {  We now have a CMediaType object that contains the next media type
  783.            but when we assign it to the array position we CANNOT just assign
  784.            the AM_MEDIA_TYPE structure because as soon as the object goes out of
  785.            scope it will delete the memory we have just copied. The function
  786.            we use is CreateMediaType which allocates a task memory block }
  787.         {   Transfer across the format block manually to save an allocate
  788.             and free on the format block and generally go faster }
  789.         TMTDynArray(@ppMediaTypes)[Fetched] := CoTaskMemAlloc(sizeof(TAMMediaType));
  790.         if TMTDynArray(@ppMediaTypes)[Fetched] = nil then Break;
  791.         {  Do a regular copy }
  792.         //CopyMediaType(TMTDynArray(@ppMediaTypes)[Fetched], cmt);
  793.         Move(cmt^,TMTDynArray(@ppMediaTypes)[Fetched]^,SizeOf(TAMMediaType));
  794.         // Make sure the destructor doesn't free these
  795.         cmt.pbFormat      := nil;
  796.         cmt.cbFormat      := 0;
  797.         Pointer(cmt.pUnk) := nil;
  798.         inc(Fetched);
  799.         dec(cMediaTypes);
  800.     end;
  801.     dispose(cmt);
  802.     if (pcFetched <> nil) then pcFetched^ := Fetched;
  803.     if cMediaTypes = 0 then result := NOERROR else result := S_FALSE;
  804. end;
  805. { Set the current position back to the start
  806.   Reset has 3 simple steps:
  807.   set position to head of list
  808.   sync enumerator with object being enumerated
  809.   return S_OK }
  810. function TBCEnumMediaTypes.Reset: HRESULT;
  811. begin
  812.   FPosition := 0;
  813.   // Bring the enumerator back into step with the current state.  This
  814.   // may be a noop but ensures that the enumerator will be valid on the
  815.   // next call.
  816.   FVersion := FPin.GetMediaTypeVersion;
  817.   result := NOERROR;
  818. end;
  819. // Skip over one or more entries in the enumerator
  820. function TBCEnumMediaTypes.Skip(cMediaTypes: ULONG): HRESULT;
  821. var cmt: PAMMediaType;
  822. begin
  823.   cmt := nil;
  824.   //  If we're skipping 0 elements we're guaranteed to skip the
  825.   //  correct number of elements
  826.   if (cMediaTypes = 0) then
  827.   begin
  828.     result := S_OK;
  829.     exit;
  830.   end;
  831.   // Check we are still in sync with the pin
  832.   if AreWeOutOfSync then
  833.   begin
  834.     result := VFW_E_ENUM_OUT_OF_SYNC;
  835.     exit;
  836.   end;
  837.   FPosition := FPosition + cMediaTypes;
  838.   // See if we're over the end
  839.   if (S_OK = FPin.GetMediaType(FPosition - 1, cmt)) then result := S_OK else result := S_FALSE;
  840. end;
  841. { TBCBaseOutputPin }
  842. // Commit the allocator's memory, this is called through IMediaFilter
  843. // which is responsible for locking the object before calling us
  844. function TBCBaseOutputPin.Active: HRESULT;
  845. begin
  846.   if (FAllocator = nil) then
  847.        result := VFW_E_NO_ALLOCATOR
  848.   else result := FAllocator.Commit;
  849. end;
  850. function TBCBaseOutputPin.BeginFlush: HRESULT;
  851. begin
  852.   result := E_UNEXPECTED;
  853. end;
  854. // Overriden from CBasePin
  855. function TBCBaseOutputPin.BreakConnect: HRESULT;
  856. begin
  857.   // Release any allocator we hold
  858.   if (FAllocator <> nil) then
  859.   begin
  860.     // Always decommit the allocator because a downstream filter may or
  861.     // may not decommit the connection's allocator.  A memory leak could
  862.     // occur if the allocator is not decommited when a connection is broken.
  863.     result := FAllocator.Decommit;
  864.     if FAILED(result) then exit;
  865.     FAllocator := nil;
  866.   end;
  867.   // Release any input pin interface we hold
  868.   if (FInputPin <> nil) then FInputPin := nil;
  869.   result := NOERROR;
  870. end;
  871. { This method is called when the output pin is about to try and connect to
  872.   an input pin. It is at this point that you should try and grab any extra
  873.   interfaces that you need, in this case IMemInputPin. Because this is
  874.   only called if we are not currently connected we do NOT need to call
  875.   BreakConnect. This also makes it easier to derive classes from us as
  876.   BreakConnect is only called when we actually have to break a connection
  877.   (or a partly made connection) and not when we are checking a connection }
  878. function TBCBaseOutputPin.CheckConnect(Pin: IPin): HRESULT;
  879. begin
  880.   result := inherited CheckConnect(Pin);
  881.   if FAILED(result) then exit;
  882.   // get an input pin and an allocator interface
  883.   result := Pin.QueryInterface(IID_IMemInputPin, FInputPin);
  884.   if FAILED(result) then exit;
  885.   result := NOERROR;
  886. end;
  887. // This is called after a media type has been proposed
  888. // Try to complete the connection by agreeing the allocator
  889. function TBCBaseOutputPin.CompleteConnect(ReceivePin: IPin): HRESULT;
  890. begin
  891.   result := DecideAllocator(FInputPin, FAllocator);
  892. end;
  893. constructor TBCBaseOutputPin.Create(ObjectName: string;
  894.   Filter: TBCBaseFilter; Lock: TBCCritSec; out hr: HRESULT;
  895.   const Name: WideString);
  896. begin
  897.   inherited Create(ObjectName, Filter, Lock, hr, Name, PINDIR_OUTPUT);
  898.   FAllocator := nil;
  899.   FInputPin  := nil;
  900.   ASSERT(FFilter <> nil);
  901. end;
  902. { Decide on an allocator, override this if you want to use your own allocator
  903.   Override DecideBufferSize to call SetProperties. If the input pin fails
  904.   the GetAllocator call then this will construct a CMemAllocator and call
  905.   DecideBufferSize on that, and if that fails then we are completely hosed.
  906.   If the you succeed the DecideBufferSize call, we will notify the input
  907.   pin of the selected allocator. NOTE this is called during Connect() which
  908.   therefore looks after grabbing and locking the object's critical section }
  909. // We query the input pin for its requested properties and pass this to
  910. // DecideBufferSize to allow it to fulfill requests that it is happy
  911. // with (eg most people don't care about alignment and are thus happy to
  912. // use the downstream pin's alignment request).
  913. function TBCBaseOutputPin.DecideAllocator(Pin: IMemInputPin;
  914.   out Alloc: IMemAllocator): HRESULT;
  915. var
  916.   prop: TAllocatorProperties;
  917. begin
  918.   Alloc := nil;
  919.   // get downstream prop request
  920.   // the derived class may modify this in DecideBufferSize, but
  921.   // we assume that he will consistently modify it the same way,
  922.   // so we only get it once
  923.   ZeroMemory(@prop, sizeof(TAllocatorProperties));
  924.   // whatever he returns, we assume prop is either all zeros
  925.   // or he has filled it out.
  926.   Pin.GetAllocatorRequirements(prop);
  927.   // if he doesn't care about alignment, then set it to 1
  928.   if (prop.cbAlign = 0) then prop.cbAlign := 1;
  929.   // Try the allocator provided by the input pin
  930.   result := Pin.GetAllocator(Alloc);
  931.   if SUCCEEDED(result) then
  932.   begin
  933.     result := DecideBufferSize(Alloc, @prop);
  934.     if SUCCEEDED(result) then
  935.     begin
  936.       result := Pin.NotifyAllocator(Alloc, FALSE);
  937.       if SUCCEEDED(result) then
  938.       begin
  939.         result := NOERROR;
  940.         exit;
  941.       end;
  942.     end;
  943.   end;
  944.   // If the GetAllocator failed we may not have an interface
  945.   if (Alloc <> nil) then Alloc := nil;
  946.   // Try the output pin's allocator by the same method
  947.   result := InitAllocator(Alloc);
  948.   if SUCCEEDED(result) then
  949.   begin
  950.     // note - the properties passed here are in the same
  951.     // structure as above and may have been modified by
  952.     // the previous call to DecideBufferSize
  953.     result := DecideBufferSize(Alloc, @prop);
  954.     if SUCCEEDED(result) then
  955.     begin
  956.       result := Pin.NotifyAllocator(Alloc, FALSE);
  957.       if SUCCEEDED(result) then
  958.       begin
  959.         result := NOERROR;
  960.         exit;
  961.       end;
  962.     end;
  963.   end;
  964.   // Likewise we may not have an interface to release
  965.   if (Alloc <> nil) then Alloc := nil;
  966. end;
  967. function TBCBaseOutputPin.DecideBufferSize(Alloc: IMemAllocator;
  968.   propInputRequest: PAllocatorProperties): HRESULT;
  969. begin
  970.   result := S_OK; // ???
  971. end;
  972. { Deliver a filled-in sample to the connected input pin. NOTE the object must
  973.   have locked itself before calling us otherwise we may get halfway through
  974.   executing this method only to find the filter graph has got in and
  975.   disconnected us from the input pin. If the filter has no worker threads
  976.   then the lock is best applied on Receive(), otherwise it should be done
  977.   when the worker thread is ready to deliver. There is a wee snag to worker
  978.   threads that this shows up. The worker thread must lock the object when
  979.   it is ready to deliver a sample, but it may have to wait until a state
  980.   change has completed, but that may never complete because the state change
  981.   is waiting for the worker thread to complete. The way to handle this is for
  982.   the state change code to grab the critical section, then set an abort event
  983.   for the worker thread, then release the critical section and wait for the
  984.   worker thread to see the event we set and then signal that it has finished
  985.   (with another event). At which point the state change code can complete }
  986. // note (if you've still got any breath left after reading that) that you
  987. // need to release the sample yourself after this call. if the connected
  988. // input pin needs to hold onto the sample beyond the call, it will addref
  989. // the sample itself.
  990. // of course you must release this one and call GetDeliveryBuffer for the
  991. // next. You cannot reuse it directly.
  992. function TBCBaseOutputPin.Deliver(Sample: IMediaSample): HRESULT;
  993. begin
  994.   if (FInputPin = nil) then result := VFW_E_NOT_CONNECTED
  995.                        else result := FInputPin.Receive(Sample);
  996. end;
  997. // call BeginFlush on the connected input pin
  998. function TBCBaseOutputPin.DeliverBeginFlush: HRESULT;
  999. begin
  1000.   // remember this is on IPin not IMemInputPin
  1001.   if (FConnected = nil) then
  1002.        result := VFW_E_NOT_CONNECTED
  1003.   else result := FConnected.BeginFlush;
  1004. end;
  1005. // call EndFlush on the connected input pin
  1006. function TBCBaseOutputPin.DeliverEndFlush: HRESULT;
  1007. begin
  1008.   // remember this is on IPin not IMemInputPin
  1009.   if (FConnected = nil) then
  1010.        result := VFW_E_NOT_CONNECTED
  1011.   else result := FConnected.EndFlush;
  1012. end;
  1013. // called from elsewhere in our filter to pass EOS downstream to
  1014. // our connected input pin
  1015. function TBCBaseOutputPin.DeliverEndOfStream: HRESULT;
  1016. begin
  1017.   // remember this is on IPin not IMemInputPin
  1018.   if (FConnected = nil) then
  1019.        result := VFW_E_NOT_CONNECTED
  1020.   else result := FConnected.EndOfStream;
  1021. end;
  1022. // deliver NewSegment to connected pin
  1023. function TBCBaseOutputPin.DeliverNewSegment(Start, Stop: TReferenceTime;
  1024.   Rate: double): HRESULT;
  1025. begin
  1026.   if (FConnected = nil) then
  1027.        result := VFW_E_NOT_CONNECTED
  1028.   else result := FConnected.NewSegment(Start, Stop, Rate);
  1029. end;
  1030. function TBCBaseOutputPin.EndFlush: HRESULT;
  1031. begin
  1032.   result := E_UNEXPECTED;
  1033. end;
  1034. // we have a default handling of EndOfStream which is to return
  1035. // an error, since this should be called on input pins only
  1036. function TBCBaseOutputPin.EndOfStream: HRESULT;
  1037. begin
  1038.   result := E_UNEXPECTED;
  1039. end;
  1040. // This returns an empty sample buffer from the allocator WARNING the same
  1041. // dangers and restrictions apply here as described below for Deliver()
  1042. function TBCBaseOutputPin.GetDeliveryBuffer(out Sample: IMediaSample;
  1043.   StartTime, EndTime: PReferenceTime; Flags: Longword): HRESULT;
  1044. begin
  1045.   if (FAllocator <> nil) then
  1046.        result := FAllocator.GetBuffer(Sample, StartTime, EndTime, Flags)
  1047.   else result := E_NOINTERFACE;
  1048. end;
  1049. { Free up or unprepare allocator's memory, this is called through
  1050.   IMediaFilter which is responsible for locking the object first }
  1051. function TBCBaseOutputPin.Inactive: HRESULT;
  1052. begin
  1053.   FRunTimeError := FALSE;
  1054.   if (FAllocator = nil) then
  1055.        result := VFW_E_NO_ALLOCATOR
  1056.   else result := FAllocator.Decommit;
  1057. end;
  1058. // This is called when the input pin didn't give us a valid allocator
  1059. function TBCBaseOutputPin.InitAllocator(out Alloc: IMemAllocator): HRESULT;
  1060. begin
  1061.   result := CoCreateInstance(CLSID_MemoryAllocator, nil, CLSCTX_INPROC_SERVER,
  1062.     IID_IMemAllocator, Alloc);
  1063. end;
  1064. { TBCBaseInputPin }
  1065. // Default handling for BeginFlush - call at the beginning
  1066. // of your implementation (makes sure that all Receive calls
  1067. // fail). After calling this, you need to free any queued data
  1068. // and then call downstream.
  1069. function TBCBaseInputPin.BeginFlush: HRESULT;
  1070. begin
  1071.     //  BeginFlush is NOT synchronized with streaming but is part of
  1072.     //  a control action - hence we synchronize with the filter
  1073.   FLock.Lock;
  1074.   try
  1075.     // if we are already in mid-flush, this is probably a mistake
  1076.     // though not harmful - try to pick it up for now so I can think about it
  1077.     ASSERT(not FFlushing);
  1078.     // first thing to do is ensure that no further Receive calls succeed
  1079.     FFlushing := True;
  1080.     // now discard any data and call downstream - must do that
  1081.     // in derived classes
  1082.     result := S_OK;
  1083.   finally
  1084.     FLock.UnLock;
  1085.   end;
  1086. end;
  1087. function TBCBaseInputPin.BreakConnect: HRESULT;
  1088. begin
  1089.   // We don't need our allocator any more
  1090.   if (FAllocator <> nil) then
  1091.   begin
  1092.     // Always decommit the allocator because a downstream filter may or
  1093.     // may not decommit the connection's allocator.  A memory leak could
  1094.     // occur if the allocator is not decommited when a pin is disconnected.
  1095.     result := FAllocator.Decommit;
  1096.     if FAILED(result) then exit;
  1097.     FAllocator := nil;
  1098.   end;
  1099.   result := S_OK;
  1100. end;
  1101. //  Check if it's OK to process data
  1102. function TBCBaseInputPin.CheckStreaming: HRESULT;
  1103. begin
  1104.   //  Shouldn't be able to get any data if we're not connected!
  1105.   ASSERT(IsConnected);
  1106.   //  Don't process stuff in Stopped state
  1107.   if IsStopped then begin result := VFW_E_WRONG_STATE; exit end;
  1108.   if FFlushing then begin result := S_FALSE; exit end;
  1109.   if FRunTimeError then begin result := VFW_E_RUNTIME_ERROR; exit end;
  1110.   result := S_OK;
  1111. end;
  1112. // Constructor creates a default allocator object
  1113. constructor TBCBaseInputPin.Create(ObjectName: string;
  1114.   Filter: TBCBaseFilter; Lock: TBCCritSec; out hr: HRESULT;
  1115.   Name: WideString);
  1116. begin
  1117.     inherited create(ObjectName, Filter, Lock, hr, Name, PINDIR_INPUT);
  1118.     FAllocator := nil;
  1119.     FReadOnly  := false;
  1120.     FFlushing  := false;
  1121.     ZeroMemory(@FSampleProps, sizeof(FSampleProps));
  1122. end;
  1123. destructor TBCBaseInputPin.Destroy;
  1124. begin
  1125.   if FAllocator <> nil then FAllocator := nil;
  1126.   inherited;
  1127. end;
  1128. // default handling for EndFlush - call at end of your implementation
  1129. // - before calling this, ensure that there is no queued data and no thread
  1130. // pushing any more without a further receive, then call downstream,
  1131. // then call this method to clear the m_bFlushing flag and re-enable
  1132. // receives
  1133. function TBCBaseInputPin.EndFlush: HRESULT;
  1134. begin
  1135.     //  Endlush is NOT synchronized with streaming but is part of
  1136.     //  a control action - hence we synchronize with the filter
  1137.   FLock.Lock;
  1138.   try
  1139.     // almost certainly a mistake if we are not in mid-flush
  1140.     ASSERT(FFlushing);
  1141.     // before calling, sync with pushing thread and ensure
  1142.     // no more data is going downstream, then call EndFlush on
  1143.     // downstream pins.
  1144.     // now re-enable Receives
  1145.     FFlushing := FALSE;
  1146.     // No more errors
  1147.     FRunTimeError := FALSE;
  1148.     result := S_OK;
  1149.   finally
  1150.     FLock.UnLock;
  1151.   end;
  1152. end;
  1153. { Return the allocator interface that this input pin would like the output
  1154.    pin to use. NOTE subsequent calls to GetAllocator should all return an
  1155.    interface onto the SAME object so we create one object at the start
  1156.    Note:
  1157.        The allocator is Release()'d on disconnect and replaced on
  1158.        NotifyAllocator().
  1159.    Override this to provide your own allocator.}
  1160. function TBCBaseInputPin.GetAllocator(
  1161.   out ppAllocator: IMemAllocator): HRESULT;
  1162. begin
  1163.   FLock.Lock;
  1164.   try
  1165.     if (FAllocator = nil) then
  1166.     begin
  1167.       result := CoCreateInstance(CLSID_MemoryAllocator, nil, CLSCTX_INPROC_SERVER,
  1168.         IID_IMemAllocator, FAllocator);
  1169.       if FAILED(result) then exit;
  1170.     end;
  1171.     ASSERT(FAllocator <> nil);
  1172.     ppAllocator := FAllocator;
  1173.     result := NOERROR;
  1174.   finally
  1175.     FLock.UnLock;
  1176.   end;
  1177. end;
  1178. // what requirements do we have of the allocator - override if you want
  1179. // to support other people's allocators but need a specific alignment
  1180. // or prefix.
  1181. function TBCBaseInputPin.GetAllocatorRequirements(
  1182.   out pProps: TAllocatorProperties): HRESULT;
  1183. begin
  1184.   result := E_NOTIMPL;
  1185. end;
  1186. { Free up or unprepare allocator's memory, this is called through
  1187.   IMediaFilter which is responsible for locking the object first. }
  1188. function TBCBaseInputPin.Inactive: HRESULT;
  1189. begin
  1190.   FRunTimeError := FALSE;
  1191.   if (FAllocator = nil) then
  1192.   begin
  1193.     result := VFW_E_NO_ALLOCATOR;
  1194.     exit;
  1195.   end;
  1196.   FFlushing := FALSE;
  1197.   result := FAllocator.Decommit;
  1198. end;
  1199. function TBCBaseInputPin.Notify(pSelf: IBaseFilter; q: TQuality): HRESULT;
  1200. begin
  1201. {$IFDEF DEBUG}
  1202.   DbgLog(self, 'IQuality.Notify called on an input pin');
  1203. {$ENDIF}
  1204.   result := NOERROR;
  1205. end;
  1206. { Tell the input pin which allocator the output pin is actually going to use
  1207.   Override this if you care - NOTE the locking we do both here and also in
  1208.   GetAllocator is unnecessary but derived classes that do something useful
  1209.   will undoubtedly have to lock the object so this might help remind people }
  1210. function TBCBaseInputPin.NotifyAllocator(pAllocator: IMemAllocator;
  1211.   bReadOnly: BOOL): HRESULT;
  1212. begin
  1213.   FLock.Lock;
  1214.   try
  1215.     FAllocator := pAllocator;
  1216.     // the readonly flag indicates whether samples from this allocator should
  1217.     // be regarded as readonly - if True, then inplace transforms will not be
  1218.     // allowed.
  1219.     FReadOnly := bReadOnly;
  1220.     result    := NOERROR;
  1221.   finally
  1222.     FLock.UnLock;
  1223.   end;
  1224. end;
  1225. // Pass on the Quality notification q to
  1226. // a. Our QualityControl sink (if we have one) or else
  1227. // b. to our upstream filter
  1228. // and if that doesn't work, throw it away with a bad return code
  1229. function TBCBaseInputPin.PassNotify(const q: TQuality): HRESULT;
  1230. var IQC: IQualityControl;
  1231. begin
  1232.   // We pass the message on, which means that we find the quality sink
  1233.   // for our input pin and send it there
  1234. {$IFDEF DEBUG}
  1235.   DbgLog(self, 'Passing Quality notification through transform');
  1236. {$ENDIF}
  1237.   if (FQSink <> nil) then
  1238.     begin
  1239.       result := FQSink.Notify(FFilter, q);
  1240.       exit;
  1241.     end
  1242.   else
  1243.     begin
  1244.       // no sink set, so pass it upstream
  1245.       result := VFW_E_NOT_FOUND;                   // default
  1246.       if (FConnected <> nil) then
  1247.       begin
  1248.         FConnected.QueryInterface(IID_IQualityControl, IQC);
  1249.         if (IQC <> nil) then
  1250.         begin
  1251.           result := IQC.Notify(FFilter, q);
  1252.           IQC := nil;
  1253.         end;
  1254.       end;
  1255.     end;
  1256. end;
  1257. { Do something with this media sample - this base class checks to see if the
  1258.   format has changed with this media sample and if so checks that the filter
  1259.   will accept it, generating a run time error if not. Once we have raised a
  1260.   run time error we set a flag so that no more samples will be accepted
  1261.   It is important that any filter should override this method and implement
  1262.   synchronization so that samples are not processed when the pin is
  1263.   disconnected etc. }
  1264. function TBCBaseInputPin.Receive(pSample: IMediaSample): HRESULT;
  1265. var Sample2: IMediaSample2;
  1266. begin
  1267.   ASSERT(pSample <> nil);
  1268.   result := CheckStreaming;
  1269.   if (S_OK <> result) then exit;
  1270.   // Check for IMediaSample2
  1271.   if SUCCEEDED(pSample.QueryInterface(IID_IMediaSample2, Sample2)) then
  1272.     begin
  1273.       result := Sample2.GetProperties(sizeof(FSampleProps), FSampleProps);
  1274.       Sample2 := nil;
  1275.       if FAILED(result) then exit;
  1276.     end
  1277.   else
  1278.     begin
  1279.       //  Get the properties the hard way
  1280.       FSampleProps.cbData := sizeof(FSampleProps);
  1281.       FSampleProps.dwTypeSpecificFlags := 0;
  1282.       FSampleProps.dwStreamId          := AM_STREAM_MEDIA;
  1283.       FSampleProps.dwSampleFlags       := 0;
  1284.       if (S_OK = pSample.IsDiscontinuity) then
  1285.           FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_DATADISCONTINUITY;
  1286.       if (S_OK = pSample.IsPreroll) then
  1287.           FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_PREROLL;
  1288.       if (S_OK = pSample.IsSyncPoint) then
  1289.           FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_SPLICEPOINT;
  1290.       if SUCCEEDED(pSample.GetTime(FSampleProps.tStart, FSampleProps.tStop)) then
  1291.           FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_TIMEVALID or AM_SAMPLE_STOPVALID;
  1292.       if (S_OK = pSample.GetMediaType(FSampleProps.pMediaType)) then
  1293.           FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_TYPECHANGED;
  1294.       pSample.GetPointer(PByte(FSampleProps.pbBuffer));
  1295.       FSampleProps.lActual := pSample.GetActualDataLength;
  1296.       FSampleProps.cbBuffer := pSample.GetSize;
  1297.     end;
  1298.   // Has the format changed in this sample
  1299.   if (not BOOL(FSampleProps.dwSampleFlags and AM_SAMPLE_TYPECHANGED)) then
  1300.   begin
  1301.     result := NOERROR;
  1302.     exit;
  1303.   end;
  1304.   // Check the derived class accepts this format */
  1305.   // This shouldn't fail as the source must call QueryAccept first */
  1306.   result := CheckMediaType(FSampleProps.pMediaType);
  1307.   if (result = NOERROR) then exit;
  1308.   // Raise a runtime error if we fail the media type
  1309.   FRunTimeError := True;
  1310.   EndOfStream;
  1311.   FFilter.NotifyEvent(EC_ERRORABORT,VFW_E_TYPE_NOT_ACCEPTED,0);
  1312.   result := VFW_E_INVALIDMEDIATYPE;
  1313. end;
  1314. // See if Receive() might block
  1315. function TBCBaseInputPin.ReceiveCanBlock: HRESULT;
  1316. var
  1317.   c, Pins, OutputPins: Integer;
  1318.   Pin: TBCBasePin;
  1319.   pd: TPinDirection;
  1320.   Connected: IPin;
  1321.   InputPin: IMemInputPin;
  1322. begin
  1323.   { Ask all the output pins if they block
  1324.     If there are no output pin assume we do block. }
  1325.   Pins := FFilter.GetPinCount;
  1326.   OutputPins := 0;
  1327.   for c := 0 to Pins - 1 do
  1328.   begin
  1329.     Pin := FFilter.GetPin(c);
  1330.     result := Pin.QueryDirection(pd);
  1331.     if FAILED(result) then exit;
  1332.     if (pd = PINDIR_OUTPUT) then
  1333.     begin
  1334.       result := Pin.ConnectedTo(Connected);
  1335.       if SUCCEEDED(result) then
  1336.       begin
  1337.         assert(Connected <> nil);
  1338.         inc(OutputPins);
  1339.         result := Connected.QueryInterface(IID_IMemInputPin, InputPin);
  1340.         Connected := nil;
  1341.         if SUCCEEDED(result) then
  1342.           begin
  1343.             result := InputPin.ReceiveCanBlock;
  1344.             InputPin := nil;
  1345.             if (result <> S_FALSE) then
  1346.               begin
  1347.                 result := S_OK;
  1348.                 exit;
  1349.               end;
  1350.           end
  1351.         else
  1352.           begin
  1353.             // There's a transport we don't understand here
  1354.             result := S_OK;
  1355.             exit;
  1356.           end;
  1357.       end;
  1358.     end;
  1359.   end;
  1360.   if OutputPins = 0 then result := S_OK else result := S_FALSE;
  1361. end;
  1362. //  Receive multiple samples
  1363. function TBCBaseInputPin.ReceiveMultiple(var pSamples: IMediaSample;
  1364.   nSamples: Integer; out nSamplesProcessed: Integer): HRESULT;
  1365. type
  1366.   TMediaSampleDynArray = array of IMediaSample;
  1367. begin
  1368.   result := S_OK;
  1369.   nSamplesProcessed := 0;
  1370.   dec(nSamples);
  1371.   while (nSamples >= 0) do
  1372.   begin
  1373.     result := Receive(TMediaSampleDynArray(@pSamples)[nSamplesProcessed]);
  1374.     //  S_FALSE means don't send any more
  1375.     if (result <> S_OK) then break;
  1376.     inc(nSamplesProcessed);
  1377.     dec(nSamples)
  1378.   end;
  1379. end;
  1380. function TBCBaseInputPin.SampleProps: PAMSample2Properties;
  1381. begin
  1382.   ASSERT(FSampleProps.cbData <> 0);
  1383.   result := @FSampleProps;
  1384. end;
  1385. // milenko start (added TBCDynamicOutputPin conversion)
  1386. { TBCDynamicOutputPin }
  1387. //
  1388. // The streaming thread calls IPin::NewSegment(), IPin::EndOfStream(),
  1389. // IMemInputPin::Receive() and IMemInputPin::ReceiveMultiple() on the
  1390. // connected input pin.  The application thread calls Block().  The
  1391. // following class members can only be called by the streaming thread.
  1392. //
  1393. //    Deliver()
  1394. //    DeliverNewSegment()
  1395. //    StartUsingOutputPin()
  1396. //    StopUsingOutputPin()
  1397. //    ChangeOutputFormat()
  1398. //    ChangeMediaType()
  1399. //    DynamicReconnect()
  1400. //
  1401. // The following class members can only be called by the application thread.
  1402. //
  1403. //    Block()
  1404. //    SynchronousBlockOutputPin()
  1405. //    AsynchronousBlockOutputPin()
  1406. //
  1407. constructor TBCDynamicOutputPin.Create(ObjectName: WideString; Filter: TBCBaseFilter;
  1408.                    Lock: TBCCritSec; out hr: HRESULT; Name: WideString);
  1409. begin
  1410.   inherited Create(ObjectName,Filter,Lock,hr,Name);
  1411.   FStopEvent := 0;
  1412.   FGraphConfig := nil;
  1413.   FPinUsesReadOnlyAllocator := False;
  1414.   FBlockState := NOT_BLOCKED;
  1415.   FUnblockOutputPinEvent := 0;
  1416.   FNotifyCallerPinBlockedEvent := 0;
  1417.   FBlockCallerThreadID := 0;
  1418.   FNumOutstandingOutputPinUsers := 0;
  1419.   FBlockStateLock := TBCCritSec.Create;
  1420.   hr := Initialize;
  1421. end;
  1422. destructor TBCDynamicOutputPin.Destroy;
  1423. begin
  1424.   if(FUnblockOutputPinEvent <> 0) then
  1425.   begin
  1426.     // This call should not fail because we have access to m_hUnblockOutputPinEvent
  1427.     // and m_hUnblockOutputPinEvent is a valid event.
  1428.     ASSERT(CloseHandle(FUnblockOutputPinEvent));
  1429.   end;
  1430.   if(FNotifyCallerPinBlockedEvent <> 0) then
  1431.   begin
  1432.     // This call should not fail because we have access to m_hNotifyCallerPinBlockedEvent
  1433.     // and m_hNotifyCallerPinBlockedEvent is a valid event.
  1434.     ASSERT(CloseHandle(FNotifyCallerPinBlockedEvent));
  1435.   end;
  1436.   if Assigned(FBlockStateLock) then FreeAndNil(FBlockStateLock);
  1437.   inherited Destroy;
  1438. end;
  1439. function TBCDynamicOutputPin.NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult;
  1440. begin
  1441.   if IsEqualGUID(IID,IID_IPinFlowControl) then
  1442.   begin
  1443.     if GetInterface(IID_IPinFlowControl, Obj) then Result := S_OK
  1444.                                               else Result := E_NOINTERFACE;
  1445.   end else
  1446.   begin
  1447.     Result := inherited NonDelegatingQueryInterface(IID,Obj);
  1448.   end;
  1449. end;
  1450. function TBCDynamicOutputPin.Disconnect: HRESULT;
  1451. begin
  1452.   FLock.Lock;
  1453.   try
  1454.     Result := DisconnectInternal;
  1455.   finally
  1456.     FLock.Unlock;
  1457.   end;
  1458. end;
  1459. function TBCDynamicOutputPin.Block(dwBlockFlags: DWORD; hEvent: THandle): HResult;
  1460. begin
  1461.   // Check for illegal flags.
  1462.   if BOOL(dwBlockFlags and not AM_PIN_FLOW_CONTROL_BLOCK) then
  1463.   begin
  1464.     Result := E_INVALIDARG;
  1465.     Exit;
  1466.   end;
  1467.   // Make sure the event is unsignaled.
  1468.   if(BOOL(dwBlockFlags and AM_PIN_FLOW_CONTROL_BLOCK) and (hEvent <> 0)) then
  1469.   begin
  1470.     if not ResetEvent(hEvent) then
  1471.     begin
  1472.       Result := AmGetLastErrorToHResult;
  1473.       Exit
  1474.     end;
  1475.   end;
  1476.   // No flags are set if we are unblocking the output pin.
  1477.   if(dwBlockFlags = 0) then
  1478.   begin
  1479.     // This parameter should be NULL because unblock operations are always synchronous.
  1480.     // There is no need to notify the caller when the event is done.
  1481.     if(hEvent <> 0) then
  1482.     begin
  1483.       Result := E_INVALIDARG;
  1484.       Exit;
  1485.     end;
  1486.   end;
  1487.   {$IFDEF DEBUG}
  1488.   AssertValid;
  1489.   {$ENDIF} // DEBUG
  1490.   if BOOL(dwBlockFlags and AM_PIN_FLOW_CONTROL_BLOCK) then
  1491.   begin
  1492.     // IPinFlowControl::Block()'s hEvent parameter is NULL if the block is synchronous.
  1493.     // If hEvent is not NULL, the block is asynchronous.
  1494.     if(hEvent = 0) then Result := SynchronousBlockOutputPin
  1495.                    else Result := AsynchronousBlockOutputPin(hEvent);
  1496.   end else
  1497.   begin
  1498.     Result := UnblockOutputPin;
  1499.   end;
  1500.   {$IFDEF DEBUG}
  1501.   AssertValid;
  1502.   {$ENDIF} // DEBUG
  1503.   if(FAILED(Result)) then Exit;
  1504.   Result := S_OK;
  1505. end;
  1506. procedure TBCDynamicOutputPin.SetConfigInfo(GraphConfig: IGraphConfig; StopEvent: THandle);
  1507. begin
  1508.   // This pointer is not addrefed because filters are not allowed to
  1509.   // hold references to the filter graph manager.  See the documentation for
  1510.   // IBaseFilter::JoinFilterGraph() in the Direct Show SDK for more information.
  1511.   Pointer(FGraphConfig) := Pointer(GraphConfig);
  1512.   FStopEvent := StopEvent;
  1513. end;
  1514. {$IFDEF DEBUG}
  1515. function TBCDynamicOutputPin.Deliver(Sample: IMediaSample): HRESULT;
  1516. begin
  1517.   // The caller should call StartUsingOutputPin() before calling this
  1518.   // method.
  1519.   ASSERT(StreamingThreadUsingOutputPin);
  1520.   Result := inherited Deliver(Sample);
  1521. end;
  1522. function TBCDynamicOutputPin.DeliverEndOfStream: HRESULT;
  1523. begin
  1524.   // The caller should call StartUsingOutputPin() before calling this
  1525.   // method.
  1526.   ASSERT(StreamingThreadUsingOutputPin);
  1527.   Result := inherited DeliverEndOfStream;
  1528. end;
  1529. function TBCDynamicOutputPin.DeliverNewSegment(Start, Stop: TReferenceTime; Rate: Double): HRESULT;
  1530. begin
  1531.   // The caller should call StartUsingOutputPin() before calling this
  1532.   // method.
  1533.   ASSERT(StreamingThreadUsingOutputPin);
  1534.   Result := inherited DeliverNewSegment(Start, Stop, Rate);
  1535. end;
  1536. {$ENDIF}
  1537. function TBCDynamicOutputPin.DeliverBeginFlush: HRESULT;
  1538. begin
  1539.   // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
  1540.   // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
  1541.   // An event handle is invalid if 1) the event does not exist or the user does not have the security
  1542.   // permissions to use the event.
  1543.   ASSERT(SetEvent(FStopEvent));
  1544.   Result := inherited DeliverBeginFlush;
  1545. end;
  1546. function TBCDynamicOutputPin.DeliverEndFlush: HRESULT;
  1547. begin
  1548.   // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
  1549.   // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
  1550.   // An event handle is invalid if 1) the event does not exist or the user does not have the security
  1551.   // permissions to use the event.
  1552.   ASSERT(ResetEvent(FStopEvent));
  1553.   Result := inherited DeliverEndFlush;
  1554. end;
  1555. function TBCDynamicOutputPin.Active: HRESULT;
  1556. begin
  1557.   // Make sure the user initialized the object by calling SetConfigInfo().
  1558.   if(FStopEvent = 0) or (FGraphConfig = nil) then
  1559.   begin
  1560.     {$IFDEF DEBUG}
  1561.      DbgLog('ERROR: TBCDynamicOutputPin.Active() failed because m_pGraphConfig' +
  1562.             ' and m_hStopEvent were not initialized.  Call SetConfigInfo() to initialize them.');
  1563.     {$ENDIF} // DEBUG
  1564.     Result := E_FAIL;
  1565.     Exit;
  1566.   end;
  1567.   // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
  1568.   // The ASSERT can also fire if the event if destroyed and then Active() is called.  An event
  1569.   // handle is invalid if 1) the event does not exist or the user does not have the security
  1570.   // permissions to use the event.
  1571.   ASSERT(ResetEvent(FStopEvent));
  1572.   Result := inherited Active;
  1573. end;
  1574. function TBCDynamicOutputPin.Inactive: HRESULT;
  1575. begin
  1576.   // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
  1577.   // The ASSERT can also fire if the event if destroyed and then Active() is called.  An event
  1578.   // handle is invalid if 1) the event does not exist or the user does not have the security
  1579.   // permissions to use the event.
  1580.   ASSERT(SetEvent(FStopEvent));
  1581.   Result := inherited Inactive;
  1582. end;
  1583. function TBCDynamicOutputPin.CompleteConnect(ReceivePin: IPin): HRESULT;
  1584. begin
  1585.   Result := inherited CompleteConnect(ReceivePin);
  1586.   if(SUCCEEDED(Result)) then
  1587.   begin
  1588.     if (not IsStopped) and (FAllocator <> nil) then
  1589.     begin
  1590.       Result := FAllocator.Commit;
  1591.       ASSERT(Result <> VFW_E_ALREADY_COMMITTED);
  1592.     end;
  1593.   end;
  1594. end;
  1595. function TBCDynamicOutputPin.StartUsingOutputPin: HRESULT;
  1596. var
  1597.   WaitEvents: array[0..1] of THandle;
  1598.   NumWaitEvents: DWORD;
  1599.   ReturnValue: DWORD;
  1600. begin
  1601.   // The caller should not hold m_BlockStateLock.  If the caller does,
  1602.   // a deadlock could occur.
  1603.   ASSERT(FBlockStateLock.CritCheckIn);
  1604.   FBlockStateLock.Lock;
  1605.   try
  1606.     {$IFDEF DEBUG}
  1607.     AssertValid;
  1608.     {$ENDIF} // DEBUG
  1609.     // Are we in the middle of a block operation?
  1610.     while(BLOCKED = FBlockState) do
  1611.     begin
  1612.       FBlockStateLock.Unlock;
  1613.       // If this ASSERT fires, a deadlock could occur.  The caller should make sure
  1614.       // that this thread never acquires the Block State lock more than once.
  1615.       ASSERT(FBlockStateLock.CritCheckIn);
  1616.       // WaitForMultipleObjects() returns WAIT_OBJECT_0 if the unblock event
  1617.       // is fired.  It returns WAIT_OBJECT_0 + 1 if the stop event if fired.
  1618.       // See the Windows SDK documentation for more information on
  1619.       // WaitForMultipleObjects().
  1620.       WaitEvents[0] := FUnblockOutputPinEvent;
  1621.       WaitEvents[0] := FStopEvent;
  1622.       NumWaitEvents := sizeof(WaitEvents) div sizeof(THANDLE);
  1623.       ReturnValue := WaitForMultipleObjects(NumWaitEvents, @WaitEvents, False, INFINITE);
  1624.       FBlockStateLock.Lock;
  1625.       {$IFDEF DEBUG}
  1626.       AssertValid;
  1627.       {$ENDIF} // DEBUG
  1628.       case ReturnValue of
  1629.         WAIT_OBJECT_0: break;
  1630.         WAIT_OBJECT_0 + 1:
  1631.         begin
  1632.           Result := VFW_E_STATE_CHANGED;
  1633.           Exit;
  1634.         end;
  1635.         WAIT_FAILED:
  1636.         begin
  1637.           Result := AmGetLastErrorToHResult;
  1638.           Exit;
  1639.         end;
  1640.         else
  1641.         begin
  1642.           {$IFDEF DEBUG}
  1643.           DbgLog('An Unexpected case occured in TBCDynamicOutputPin.StartUsingOutputPin().');
  1644.           {$ENDIF} // DEBUG
  1645.           Result := E_UNEXPECTED;
  1646.           Exit;
  1647.         end;
  1648.       end;
  1649.     end;
  1650.     inc(FNumOutstandingOutputPinUsers);
  1651.     {$IFDEF DEBUG}
  1652.     AssertValid;
  1653.     {$ENDIF} // DEBUG
  1654.     Result := S_OK;
  1655.   finally
  1656.     FBlockStateLock.Unlock;
  1657.   end;
  1658. end;
  1659. procedure TBCDynamicOutputPin.StopUsingOutputPin;
  1660. begin
  1661.   FBlockStateLock.Lock;
  1662.   try
  1663.     {$IFDEF DEBUG}
  1664.     AssertValid;
  1665.     {$ENDIF} // DEBUG
  1666.     dec(FNumOutstandingOutputPinUsers);
  1667.     if(FNumOutstandingOutputPinUsers = 0) and (NOT_BLOCKED <> FBlockState)
  1668.       then BlockOutputPin;
  1669.     {$IFDEF DEBUG}
  1670.     AssertValid;
  1671.     {$ENDIF} // DEBUG
  1672.   finally
  1673.     FBlockStateLock.Unlock;
  1674.   end;
  1675. end;
  1676. function TBCDynamicOutputPin.StreamingThreadUsingOutputPin: Boolean;
  1677. begin
  1678.   FBlockStateLock.Lock;
  1679.   try
  1680.     Result := (FNumOutstandingOutputPinUsers > 0);
  1681.   finally
  1682.     FBlockStateLock.UnLock;
  1683.   end;
  1684. end;
  1685. function TBCDynamicOutputPin.ChangeOutputFormat(const pmt: PAMMEdiaType; tSegmentStart, tSegmentStop:
  1686.                             TreferenceTime; dSegmentRate: Double): HRESULT;
  1687. begin
  1688.   // The caller should call StartUsingOutputPin() before calling this
  1689.   // method.
  1690.   ASSERT(StreamingThreadUsingOutputPin);
  1691.   // Callers should always pass a valid media type to ChangeOutputFormat() .
  1692.   ASSERT(pmt <> nil);
  1693.   Result := ChangeMediaType(pmt);
  1694.   if (FAILED(Result)) then Exit;
  1695.   Result :=DeliverNewSegment(tSegmentStart, tSegmentStop, dSegmentRate);
  1696.   if(FAILED(Result)) then Exit;
  1697.   Result := S_OK;
  1698. end;
  1699. function TBCDynamicOutputPin.ChangeMediaType(const pmt: PAMMediaType): HRESULT;
  1700. var
  1701.   pConnection: IPinConnection;
  1702. begin
  1703.   // The caller should call StartUsingOutputPin() before calling this
  1704.   // method.
  1705.   ASSERT(StreamingThreadUsingOutputPin);
  1706.   // This function assumes the filter graph is running.
  1707.   ASSERT(not IsStopped);
  1708.   if (not IsConnected) then
  1709.   begin
  1710.     Result := VFW_E_NOT_CONNECTED;
  1711.     Exit;
  1712.   end;
  1713.   //  First check if the downstream pin will accept a dynamic
  1714.   //  format change
  1715.   FConnected.QueryInterface(IID_IPinConnection, pConnection);
  1716.   if(pConnection <> nil) then
  1717.   begin
  1718.     if(S_OK = pConnection.DynamicQueryAccept(pmt^)) then
  1719.     begin
  1720.       Result := ChangeMediaTypeHelper(pmt);
  1721.       if(FAILED(Result)) then Exit;
  1722.       Result := S_OK;
  1723.       Exit;
  1724.     end;
  1725.   end;
  1726.   // Can't do the dynamic connection
  1727.   Result := DynamicReconnect(pmt);
  1728. end;
  1729. // this method has to be called from the thread that is pushing data,
  1730. // and it's the caller's responsibility to make sure that the thread
  1731. // has no outstand samples because they cannot be delivered after a
  1732. // reconnect
  1733. //
  1734. function TBCDynamicOutputPin.DynamicReconnect(const pmt: PAMMediaType): HRESULT;
  1735. begin
  1736.   // The caller should call StartUsingOutputPin() before calling this
  1737.   // method.
  1738.   ASSERT(StreamingThreadUsingOutputPin);
  1739.   if(FGraphConfig = nil) or (FStopEvent = 0) then
  1740.   begin
  1741.     Result := E_FAIL;
  1742.     Exit;
  1743.   end;
  1744.   Result := FGraphConfig.Reconnect(Self,nil,pmt,nil,FStopEvent,
  1745.             AM_GRAPH_CONFIG_RECONNECT_CACHE_REMOVED_FILTERS);
  1746. end;
  1747. function TBCDynamicOutputPin.SynchronousBlockOutputPin: HRESULT;
  1748. var
  1749.   NotifyCallerPinBlockedEvent: THandle;
  1750. begin
  1751.   NotifyCallerPinBlockedEvent := CreateEvent(nil,   // The event will have the default security attributes.
  1752.                                              False, // This is an automatic reset event.
  1753.                                              False, // The event is initially unsignaled.
  1754.                                              nil);  // The event is not named.
  1755.   // CreateEvent() returns NULL if an error occurs.
  1756.   if(NotifyCallerPinBlockedEvent = 0) then
  1757.   begin
  1758.     Result := AmGetLastErrorToHResult;
  1759.     Exit;
  1760.   end;
  1761.   Result := AsynchronousBlockOutputPin(NotifyCallerPinBlockedEvent);
  1762.   if(FAILED(Result)) then
  1763.   begin
  1764.     // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
  1765.     // and hNotifyCallerPinBlockedEvent is a valid event.
  1766.     ASSERT(CloseHandle(NotifyCallerPinBlockedEvent));
  1767.     Exit;
  1768.   end;
  1769.   Result := WaitEvent(NotifyCallerPinBlockedEvent);
  1770.   // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
  1771.   // and hNotifyCallerPinBlockedEvent is a valid event.
  1772.   ASSERT(CloseHandle(NotifyCallerPinBlockedEvent));
  1773.   if(FAILED(Result)) then Exit;
  1774.   Result := S_OK;
  1775. end;
  1776. function TBCDynamicOutputPin.AsynchronousBlockOutputPin(NotifyCallerPinBlockedEvent: THandle): HRESULT;
  1777. var
  1778.   Success : Boolean;
  1779. begin
  1780.   // This function holds the m_BlockStateLock because it uses
  1781.   // m_dwBlockCallerThreadID, m_BlockState and
  1782.   // m_hNotifyCallerPinBlockedEvent.
  1783.   FBlockStateLock.Lock;
  1784.   try
  1785.     if (NOT_BLOCKED <> FBlockState) then
  1786.     begin
  1787.       if(FBlockCallerThreadID = GetCurrentThreadId)
  1788.         then Result := VFW_E_PIN_ALREADY_BLOCKED_ON_THIS_THREAD
  1789.         else Result := VFW_E_PIN_ALREADY_BLOCKED;
  1790.       Exit;
  1791.     end;
  1792.     Success := DuplicateHandle(GetCurrentProcess,
  1793.                                NotifyCallerPinBlockedEvent,
  1794.                                GetCurrentProcess,
  1795.                                @FNotifyCallerPinBlockedEvent,
  1796.                                EVENT_MODIFY_STATE,
  1797.                                False,
  1798.                                0);
  1799.     if not Success then
  1800.     begin
  1801.       Result := AmGetLastErrorToHResult;
  1802.       Exit;
  1803.     end;
  1804.     FBlockState := PENDING;
  1805.     FBlockCallerThreadID := GetCurrentThreadId;
  1806.     // The output pin cannot be blocked if the streaming thread is
  1807.     // calling IPin::NewSegment(), IPin::EndOfStream(), IMemInputPin::Receive()
  1808.     // or IMemInputPin::ReceiveMultiple() on the connected input pin.  Also, it
  1809.     // cannot be blocked if the streaming thread is calling DynamicReconnect(),
  1810.     // ChangeMediaType() or ChangeOutputFormat().
  1811.     // The output pin can be immediately blocked.
  1812.     if not StreamingThreadUsingOutputPin then BlockOutputPin();
  1813.     
  1814.     Result := S_OK;
  1815.   finally
  1816.     FBlockStateLock.Unlock;
  1817.   end;
  1818. end;
  1819. function TBCDynamicOutputPin.UnblockOutputPin: HRESULT;
  1820. begin
  1821.   // UnblockOutputPin() holds the m_BlockStateLock because it
  1822.   // uses m_BlockState, m_dwBlockCallerThreadID and
  1823.   // m_hNotifyCallerPinBlockedEvent.
  1824.   FBlockStateLock.Lock;
  1825.   try
  1826.     if (NOT_BLOCKED = FBlockState) then
  1827.     begin
  1828.       Result := S_FALSE;
  1829.       Exit;
  1830.     end;
  1831.     // This should not fail because we successfully created the event
  1832.     // and we have the security permissions to change it's state.
  1833.     ASSERT(SetEvent(FUnblockOutputPinEvent));
  1834.     // Cancel the block operation if it's still pending.
  1835.     if (FNotifyCallerPinBlockedEvent <> 0) then
  1836.     begin
  1837.       // This event should not fail because AsynchronousBlockOutputPin() successfully
  1838.       // duplicated this handle and we have the appropriate security permissions.
  1839.       ASSERT(SetEvent(FNotifyCallerPinBlockedEvent));
  1840.       ASSERT(CloseHandle(FNotifyCallerPinBlockedEvent));
  1841.     end;
  1842.     FBlockState := NOT_BLOCKED;
  1843.     FBlockCallerThreadID := 0;
  1844.     FNotifyCallerPinBlockedEvent := 0;
  1845.     Result := S_OK;
  1846.   finally
  1847.     FBlockStateLock.Unlock;
  1848.   end;
  1849. end;
  1850. procedure TBCDynamicOutputPin.BlockOutputPin;
  1851. begin
  1852.   // The caller should always hold the m_BlockStateLock because this function
  1853.   // uses m_BlockState and m_hNotifyCallerPinBlockedEvent.
  1854.   ASSERT(FBlockStateLock.CritCheckIn);
  1855.   // This function should not be called if the streaming thread is modifying
  1856.   // the connection state or it's passing data downstream.
  1857.   ASSERT(not StreamingThreadUsingOutputPin);
  1858.   // This should not fail because we successfully created the event
  1859.   // and we have the security permissions to change it's state.
  1860.   ASSERT(ResetEvent(FUnblockOutputPinEvent));
  1861.   // This event should not fail because AsynchronousBlockOutputPin() successfully
  1862.   // duplicated this handle and we have the appropriate security permissions.
  1863.   ASSERT(SetEvent(FNotifyCallerPinBlockedEvent));
  1864.   ASSERT(CloseHandle(FNotifyCallerPinBlockedEvent));
  1865.   FBlockState := BLOCKED;
  1866.   FNotifyCallerPinBlockedEvent := 0;
  1867. end;
  1868. procedure TBCDynamicOutputPin.ResetBlockState;
  1869. begin
  1870. end;
  1871. class function TBCDynamicOutputPin.WaitEvent(Event: THandle): HRESULT;
  1872. var
  1873.   ReturnValue: DWORD;
  1874. begin
  1875.   ReturnValue := WaitForSingleObject(Event, INFINITE);
  1876.   case ReturnValue of
  1877.     WAIT_OBJECT_0: Result := S_OK;
  1878.     WAIT_FAILED  : Result := AmGetLastErrorToHResult;
  1879.     else
  1880.     begin
  1881.       {$IFDEF DEBUG}
  1882.       DbgLog('An Unexpected case occured in TBCDynamicOutputPin::WaitEvent.');
  1883.       {$ENDIF}
  1884.       Result := E_UNEXPECTED;
  1885.     end;
  1886.   end;
  1887. end;
  1888. function TBCDynamicOutputPin.Initialize: HRESULT;
  1889. begin
  1890.   FUnblockOutputPinEvent := CreateEvent(nil,   // The event will have the default security descriptor.
  1891.                                         True,  // This is a manual reset event.
  1892.                                         True,  // The event is initially signaled.
  1893.                                         nil);  // The event is not named.
  1894.   // CreateEvent() returns NULL if an error occurs.
  1895.   if (FUnblockOutputPinEvent = 0) then
  1896.   begin
  1897.     Result := AmGetLastErrorToHResult;
  1898.     Exit;
  1899.   end;
  1900.   //  Set flag to say we can reconnect while streaming.
  1901.   CanReconnectWhenActive := True;
  1902.   Result := S_OK;
  1903. end;
  1904. function TBCDynamicOutputPin.ChangeMediaTypeHelper(const pmt: PAMMediaType): HRESULT;
  1905. var
  1906.   InputPinRequirements: ALLOCATOR_PROPERTIES;
  1907. begin
  1908.   // The caller should call StartUsingOutputPin() before calling this
  1909.   // method.
  1910.   ASSERT(StreamingThreadUsingOutputPin);
  1911.   Result := FConnected.ReceiveConnection(Self,pmt^);
  1912.   if(FAILED(Result)) then Exit;
  1913.   Result := SetMediaType(pmt);
  1914.   if(FAILED(Result)) then Exit;
  1915.   // Does this pin use the local memory transport?
  1916.   if(FInputPin <> nil) then
  1917.   begin
  1918.     // This function assumes that m_pInputPin and m_Connected are
  1919.     // two different interfaces to the same object.
  1920.     ASSERT(IsEqualObject(FConnected, FInputPin));
  1921.     InputPinRequirements.cbAlign := 0;
  1922.     InputPinRequirements.cbBuffer := 0;
  1923.     InputPinRequirements.cbPrefix := 0;
  1924.     InputPinRequirements.cBuffers := 0;
  1925.     FInputPin.GetAllocatorRequirements(InputPinRequirements);
  1926.     // A zero allignment does not make any sense.
  1927.     if(0 = InputPinRequirements.cbAlign)
  1928.       then InputPinRequirements.cbAlign := 1;
  1929.     Result := FAllocator.Decommit;
  1930.     if(FAILED(Result)) then Exit;
  1931.     Result := DecideBufferSize(FAllocator, @InputPinRequirements);
  1932.     if(FAILED(Result)) then Exit;
  1933.     Result := FAllocator.Commit;
  1934.     if(FAILED(Result)) then Exit;
  1935.     Result := FInputPin.NotifyAllocator(FAllocator, FPinUsesReadOnlyAllocator);
  1936.     if(FAILED(Result)) then Exit;
  1937.   end;
  1938.   Result := S_OK;
  1939. end;
  1940. {$IFDEF DEBUG}
  1941. procedure TBCDynamicOutputPin.AssertValid;
  1942. begin
  1943.   // Make sure the object was correctly initialized.
  1944.   // This ASSERT only fires if the object failed to initialize
  1945.   // and the user ignored the constructor's return code (phr).
  1946.   ASSERT(FUnblockOutputPinEvent <> 0);
  1947.   // If either of these ASSERTs fire, the user did not correctly call
  1948.   // SetConfigInfo().
  1949.   ASSERT(FStopEvent <> 0);
  1950.   ASSERT(FGraphConfig <> nil);
  1951.   // Make sure the block state is consistent.
  1952.   FBlockStateLock.Lock;
  1953.   try
  1954.     // BLOCK_STATE variables only have three legal values: PENDING, BLOCKED and NOT_BLOCKED.
  1955.     ASSERT((NOT_BLOCKED = FBlockState) or (PENDING = FBlockState) or (BLOCKED = FBlockState));
  1956.     // m_hNotifyCallerPinBlockedEvent is only needed when a block operation cannot complete
  1957.     // immediately.
  1958.     ASSERT(((FNotifyCallerPinBlockedEvent = 0) and (PENDING <> FBlockState)) or
  1959.            ((FNotifyCallerPinBlockedEvent <> 0) and (PENDING = FBlockState)) );
  1960.     // m_dwBlockCallerThreadID should always be 0 if the pin is not blocked and
  1961.     // the user is not trying to block the pin.
  1962.     ASSERT((0 = FBlockCallerThreadID) or (NOT_BLOCKED <> FBlockState));
  1963.     // If this ASSERT fires, the streaming thread is using the output pin and the
  1964.     // output pin is blocked.
  1965.     ASSERT(((0 <> FNumOutstandingOutputPinUsers) and (BLOCKED <> FBlockState)) or
  1966.            ((0 = FNumOutstandingOutputPinUsers) and (NOT_BLOCKED <> FBlockState)) or
  1967.            ((0 = FNumOutstandingOutputPinUsers) and (NOT_BLOCKED = FBlockState)) );
  1968.   finally
  1969.     FBlockStateLock.UnLock;
  1970.   end;
  1971. end;
  1972. {$ENDIF}
  1973. // milenko end
  1974. { TBCTransformInputPin }
  1975. // enter flushing state. Call default handler to block Receives, then
  1976. // pass to overridable method in filter
  1977. function TBCTransformInputPin.BeginFlush: HRESULT;
  1978. begin
  1979.   FTransformFilter.FcsFilter.Lock;
  1980.   try
  1981.     //  Are we actually doing anything?
  1982.     ASSERT(FTransformFilter.FOutput <> nil);
  1983.     if ((not IsConnected) or (not FTransformFilter.FOutput.IsConnected)) then
  1984.       begin
  1985.         result := VFW_E_NOT_CONNECTED;
  1986.         exit;
  1987.       end;
  1988.     result := inherited BeginFlush;
  1989.     if FAILED(result) then exit;
  1990.     result := FTransformFilter.BeginFlush;
  1991.   finally
  1992.     FTransformFilter.FcsFilter.UnLock;
  1993.   end;
  1994. end;
  1995. // provides derived filter a chance to release it's extra interfaces
  1996. function TBCTransformInputPin.BreakConnect: HRESULT;
  1997. begin
  1998.   ASSERT(IsStopped);
  1999.   FTransformFilter.BreakConnect(PINDIR_INPUT);
  2000.   result := inherited BreakConnect;
  2001. end;
  2002. function TBCTransformInputPin.CheckConnect(Pin: IPin): HRESULT;
  2003. begin
  2004.   result := FTransformFilter.CheckConnect(PINDIR_INPUT, Pin);
  2005.   if FAILED(result) then exit;
  2006.   result := inherited CheckConnect(Pin);
  2007. end;
  2008. // check that we can support a given media type
  2009. function TBCTransformInputPin.CheckMediaType(
  2010.   mtIn: PAMMediaType): HRESULT;
  2011. begin
  2012.   // Check the input type
  2013.   result := FTransformFilter.CheckInputType(mtIn);
  2014.   if (S_OK <> result) then exit;
  2015.   // if the output pin is still connected, then we have
  2016.   // to check the transform not just the input format
  2017.   if ((FTransformFilter.FOutput <> nil) and
  2018.       (FTransformFilter.FOutput.IsConnected)) then
  2019.     begin
  2020.       result := FTransformFilter.CheckTransform(mtIn,
  2021.           FTransformFilter.FOutput.AMMediaType);
  2022.     end;
  2023. end;
  2024. function TBCTransformInputPin.CheckStreaming: HRESULT;
  2025. begin
  2026.   ASSERT(FTransformFilter.FOutput <> nil);
  2027.   if(not FTransformFilter.FOutput.IsConnected) then
  2028.     begin
  2029.       result := VFW_E_NOT_CONNECTED;
  2030.       exit;
  2031.     end
  2032.   else
  2033.     begin
  2034.       //  Shouldn't be able to get any data if we're not connected!
  2035.       ASSERT(IsConnected);
  2036.       //  we're flushing
  2037.       if FFlushing then
  2038.         begin
  2039.           result := S_FALSE;
  2040.           exit;
  2041.         end;
  2042.       //  Don't process stuff in Stopped state
  2043.       if IsStopped then
  2044.         begin
  2045.           result := VFW_E_WRONG_STATE;
  2046.           exit;
  2047.         end;
  2048.       if FRunTimeError then
  2049.         begin
  2050.           result := VFW_E_RUNTIME_ERROR;
  2051.           exit;
  2052.         end;
  2053.       result := S_OK;
  2054.     end;
  2055. end;
  2056. function TBCTransformInputPin.CompleteConnect(ReceivePin: IPin): HRESULT;
  2057. begin
  2058.   result := FTransformFilter.CompleteConnect(PINDIR_INPUT, ReceivePin);
  2059.   if FAILED(result) then exit;
  2060.   result := inherited CompleteConnect(ReceivePin);
  2061. end;
  2062. constructor TBCTransformInputPin.Create(ObjectName: string;
  2063.   TransformFilter: TBCTransformFilter; out hr: HRESULT; Name: WideString);
  2064. begin
  2065.   inherited  Create(ObjectName, TransformFilter, TransformFilter.FcsFilter, hr, Name);
  2066. {$IFDEF DEBUG}
  2067.   DbgLog(self, 'TBCTransformInputPin.Create');
  2068. {$ENDIF}
  2069.   FTransformFilter := TransformFilter;
  2070. end;
  2071. // leave flushing state.
  2072. // Pass to overridable method in filter, then call base class
  2073. // to unblock receives (finally)
  2074. destructor TBCTransformInputPin.destroy;
  2075. begin
  2076. {$IFDEF DEBUG}
  2077.   DbgLog(self, 'TBCTransformInputPin.destroy');
  2078. {$ENDIF}
  2079.   inherited;
  2080. end;
  2081. function TBCTransformInputPin.EndFlush: HRESULT;
  2082. begin
  2083.   FTransformFilter.FcsFilter.Lock;
  2084.   try
  2085.     //  Are we actually doing anything?
  2086.     ASSERT(FTransformFilter.FOutput <> nil);
  2087.     if((not IsConnected) or (not FTransformFilter.FOutput.IsConnected)) then
  2088.       begin
  2089.         result := VFW_E_NOT_CONNECTED;
  2090.         exit;
  2091.       end;
  2092.     result := FTransformFilter.EndFlush;
  2093.     if FAILED(result) then exit;
  2094.     result := inherited EndFlush;
  2095.   finally
  2096.     FTransformFilter.FcsFilter.UnLock;
  2097.   end;
  2098. end;
  2099. // provide EndOfStream that passes straight downstream
  2100. // (there is no queued data)
  2101. function TBCTransformInputPin.EndOfStream: HRESULT;
  2102. begin
  2103.   FTransformFilter.FcsReceive.Lock;
  2104.   try
  2105.     result := CheckStreaming;
  2106.     if (S_OK = result) then
  2107.       result := FTransformFilter.EndOfStream;
  2108.   finally
  2109.     FTransformFilter.FcsReceive.UnLock;
  2110.   end;
  2111. end;
  2112. function TBCTransformInputPin.NewSegment(Start, Stop: TReferenceTime;
  2113.   Rate: double): HRESULT;
  2114. begin
  2115.   //  Save the values in the pin
  2116.   inherited NewSegment(Start, Stop, Rate);
  2117.   result := FTransformFilter.NewSegment(Start, Stop, Rate);
  2118. end;
  2119. function TBCTransformInputPin.QueryId(out id: PWideChar): HRESULT;
  2120. begin
  2121. // milenko start (AMGetWideString was bugged, now the second line is not needed)
  2122.   Result := AMGetWideString('In', Id);
  2123. //  if id <> nil then result := S_OK else result := S_FALSE;
  2124. // milenko end
  2125. end;
  2126. // here's the next block of data from the stream.
  2127. // AddRef it yourself if you need to hold it beyond the end
  2128. // of this call.
  2129. function TBCTransformInputPin.Receive(pSample: IMediaSample): HRESULT;
  2130. begin
  2131.   FTransformFilter.FcsReceive.Lock;
  2132.   try
  2133.     ASSERT(pSample <> nil);
  2134.     // check all is well with the base class
  2135.     result := inherited Receive(pSample);
  2136.     if (result = S_OK) then
  2137.       result := FTransformFilter.Receive(pSample);
  2138.   finally
  2139.     FTransformFilter.FcsReceive.Unlock;
  2140.   end;
  2141. end;
  2142. // set the media type for this connection
  2143. function TBCTransformInputPin.SetMediaType(mt: PAMMediaType): HRESULT;
  2144. begin
  2145.   // Set the base class media type (should always succeed)
  2146.   result := inherited SetMediaType(mt);
  2147.   if FAILED(result) then exit;
  2148.   // check the transform can be done (should always succeed)
  2149.   ASSERT(SUCCEEDED(FTransformFilter.CheckInputType(mt)));
  2150.   result := FTransformFilter.SetMediaType(PINDIR_INPUT,mt);
  2151. end;
  2152. { TBCCritSec }
  2153. constructor TBCCritSec.Create;
  2154. begin
  2155.   InitializeCriticalSection(FCritSec);
  2156.   {$IFDEF DEBUG}
  2157.      FcurrentOwner := 0;
  2158.      FlockCount    := 0;
  2159. //     {$IFDEF TRACE}
  2160. //     FTrace        := True;
  2161. //     {$ELSE}
  2162. //     FTrace        := FALSE;
  2163. //     {$ENDIF}
  2164.   {$ENDIF}
  2165. end;
  2166. function TBCCritSec.CritCheckIn: boolean;
  2167. begin
  2168. {$IFDEF DEBUG}
  2169.   result := (GetCurrentThreadId = Self.FcurrentOwner);
  2170. {$ELSE}
  2171.   result := True;
  2172. {$ENDIF}
  2173. end;
  2174. function TBCCritSec.CritCheckOut: boolean;
  2175. begin
  2176. {$IFDEF DEBUG}
  2177.   result := (GetCurrentThreadId <> Self.FcurrentOwner);
  2178. {$ELSE}
  2179.   result := false;
  2180. {$ENDIF}
  2181. end;
  2182. destructor TBCCritSec.Destroy;
  2183. begin
  2184.   DeleteCriticalSection(FCritSec)
  2185. end;
  2186. procedure TBCCritSec.Lock;
  2187. begin
  2188.   {$IFDEF DEBUG}
  2189.     if ((FCurrentOwner <> 0)  and (FCurrentOwner <> GetCurrentThreadId)) then
  2190.     begin
  2191.       // already owned, but not by us
  2192.     {$IFDEF TRACE}
  2193.       DbgLog(format('Thread %d about to wait for lock %x owned by %d',
  2194.         [GetCurrentThreadId, longint(self), FCurrentOwner]));
  2195.     {$ENDIF}
  2196.     end;
  2197.   {$ENDIF}
  2198.     EnterCriticalSection(FCritSec);
  2199.   {$IFDEF DEBUG}
  2200.     inc(FLockCount);
  2201.     if (FLockCount > 0) then
  2202.     begin
  2203.       // we now own it for the first time.  Set owner information
  2204.       FcurrentOwner := GetCurrentThreadId;
  2205.     {$IFDEF TRACE}
  2206.       DbgLog(format('Thread %d now owns lock %x', [FcurrentOwner, LongInt(self)]));
  2207.     {$ENDIF}
  2208.     end;
  2209.   {$ENDIF}
  2210. end;
  2211. procedure TBCCritSec.UnLock;
  2212. begin
  2213.   {$IFDEF DEBUG}
  2214.      dec(FlockCount);
  2215.      if(FlockCount = 0) then
  2216.      begin
  2217.        // about to be unowned
  2218.      {$IFDEF TRACE}
  2219.        DbgLog(format('Thread %d releasing lock %x', [FcurrentOwner, LongInt(Self)]));
  2220.      {$ENDIF}
  2221.        FcurrentOwner := 0;
  2222.     end;
  2223.   {$ENDIF}
  2224.   LeaveCriticalSection(FCritSec)
  2225. end;
  2226. { TBCTransformFilter }
  2227. // Return S_FALSE to mean "pass the note on upstream"
  2228. // Return NOERROR (Same as S_OK)
  2229. // to mean "I've done something about it, don't pass it on"
  2230. function TBCTransformFilter.AlterQuality(const q: TQuality): HRESULT;
  2231. begin
  2232.   result := S_FALSE;
  2233. end;
  2234. // enter flush state. Receives already blocked
  2235. // must override this if you have queued data or a worker thread
  2236. function TBCTransformFilter.BeginFlush: HRESULT;
  2237. begin
  2238.   result := NOERROR;
  2239.   if (FOutput <> nil) then
  2240.     // block receives -- done by caller (CBaseInputPin::BeginFlush)
  2241.     // discard queued data -- we have no queued data
  2242.     // free anyone blocked on receive - not possible in this filter
  2243.     // call downstream
  2244.     result := FOutput.DeliverBeginFlush;
  2245. end;
  2246. function TBCTransformFilter.BreakConnect(dir: TPinDirection): HRESULT;
  2247. begin
  2248.   result := NOERROR;
  2249. end;
  2250. function TBCTransformFilter.CheckConnect(dir: TPinDirection;
  2251.   Pin: IPin): HRESULT;
  2252. begin
  2253.   result := NOERROR;
  2254. end;
  2255. function TBCTransformFilter.CompleteConnect(direction: TPinDirection;
  2256.   ReceivePin: IPin): HRESULT;
  2257. begin
  2258.   result := NOERROR;
  2259. end;
  2260. constructor TBCTransformFilter.Create(ObjectName: string; unk: IUnKnown;
  2261.   const clsid: TGUID);
  2262. begin
  2263.   FcsFilter := TBCCritSec.Create;
  2264.   FcsReceive := TBCCritSec.Create;
  2265.   inherited Create(ObjectName,Unk,FcsFilter, clsid);
  2266.   FInput         := nil;
  2267.   FOutput        := nil;
  2268.   FEOSDelivered  := FALSE;
  2269.   FQualityChanged:= FALSE;
  2270.   FSampleSkipped := FALSE;
  2271. {$ifdef PERF}
  2272. //  RegisterPerfId;
  2273. {$endif}
  2274. end;
  2275. constructor TBCTransformFilter.CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown);
  2276. begin
  2277.   Create(Factory.FName, Controller, Factory.FClassID);
  2278. end;
  2279. destructor TBCTransformFilter.destroy;
  2280. begin
  2281.   if FInput <> nil then FInput.Free;
  2282.   if FOutput <> nil then FOutput.Free;
  2283. {$IFDEF DEBUG}
  2284.   DbgLog(self, 'TBCTransformFilter.destroy');
  2285. {$ENDIF}
  2286.   FcsReceive.Free;
  2287.   inherited;
  2288. end;
  2289. // leave flush state. must override this if you have queued data
  2290. // or a worker thread
  2291. function TBCTransformFilter.EndFlush: HRESULT;
  2292. begin
  2293.   // sync with pushing thread -- we have no worker thread
  2294.   // ensure no more data to go downstream -- we have no queued data
  2295.   // call EndFlush on downstream pins
  2296.   ASSERT(FOutput <> nil);
  2297.   result := FOutput.DeliverEndFlush;
  2298.   // caller (the input pin's method) will unblock Receives
  2299. end;
  2300. // EndOfStream received. Default behaviour is to deliver straight
  2301. // downstream, since we have no queued data. If you overrode Receive
  2302. // and have queue data, then you need to handle this and deliver EOS after
  2303. // all queued data is sent
  2304. function TBCTransformFilter.EndOfStream: HRESULT;
  2305. begin
  2306.   result := NOERROR;
  2307.   if (FOutput <> nil) then
  2308.     result := FOutput.DeliverEndOfStream;
  2309. end;
  2310. // If Id is In or Out then return the IPin* for that pin
  2311. // creating the pin if need be.  Otherwise return NULL with an error.
  2312. function TBCTransformFilter.FindPin(Id: PWideChar; out ppPin: IPin): HRESULT;
  2313. begin
  2314.     if(WideString(Id) = 'In')  then ppPin := GetPin(0) else
  2315.     if(WideString(Id) = 'Out') then ppPin := GetPin(1) else
  2316.       begin
  2317.         ppPin := nil;
  2318.         result := VFW_E_NOT_FOUND;
  2319.         exit;
  2320.       end;
  2321.    result := NOERROR;
  2322.    if(ppPin = nil) then result := E_OUTOFMEMORY;
  2323. end;
  2324. // return a non-addrefed CBasePin * for the user to addref if he holds onto it
  2325. // for longer than his pointer to us. We create the pins dynamically when they
  2326. // are asked for rather than in the constructor. This is because we want to
  2327. // give the derived class an oppportunity to return different pin objects
  2328. // We return the objects as and when they are needed. If either of these fails
  2329. // then we return NULL, the assumption being that the caller will realise the
  2330. // whole deal is off and destroy us - which in turn will delete everything.
  2331. function TBCTransformFilter.GetPin(n: integer): TBCBasePin;
  2332. var hr: HRESULT;
  2333. begin
  2334.   hr := S_OK;
  2335.   // Create an input pin if necessary
  2336.   if(FInput = nil) then
  2337.   begin
  2338.     FInput := TBCTransformInputPin.Create('Transform input pin',
  2339.         self,        // Owner filter
  2340.         hr,          // Result code
  2341.         'XForm In'); // Pin name
  2342.     //  Can't fail
  2343.     ASSERT(SUCCEEDED(hr));
  2344.     if(FInput = nil) then
  2345.     begin
  2346.       result := nil;
  2347.       exit;
  2348.     end;
  2349.     FOutput := TBCTransformOutputPin.Create('Transform output pin',
  2350.         self,           // Owner filter
  2351.         hr,             // Result code
  2352.         'XForm Out');   // Pin name
  2353.     // Can't fail
  2354.     ASSERT(SUCCEEDED(hr));
  2355.     if(FOutput = nil) then FreeAndNil(FInput);
  2356.   end;
  2357.   // Return the appropriate pin
  2358.   case n of
  2359.     0 : result := FInput;
  2360.     1 : result := FOutput;
  2361.     else
  2362.       result := nil;
  2363.   end;
  2364. end;
  2365. function TBCTransformFilter.GetPinCount: integer;
  2366. begin
  2367.   result := 2;
  2368. end;
  2369. // Set up our output sample
  2370. function TBCTransformFilter.InitializeOutputSample(Sample: IMediaSample;
  2371.   out OutSample: IMediaSample): HRESULT;
  2372. var
  2373.   Props: PAMSample2Properties;
  2374.   Flags: DWORD;
  2375.   Start, Stop: PReferenceTime;
  2376.   OutSample2: IMediaSample2;
  2377.   OutProps: TAMSample2Properties;
  2378.   MediaStart, MediaEnd: Int64;
  2379. begin
  2380.   // default - times are the same
  2381.   Props := FInput.SampleProps;
  2382.   if FSampleSkipped then Flags := AM_GBF_PREVFRAMESKIPPED else Flags := 0;
  2383.   // This will prevent the image renderer from switching us to DirectDraw
  2384.   // when we can't do it without skipping frames because we're not on a
  2385.   // keyframe.  If it really has to switch us, it still will, but then we
  2386.   // will have to wait for the next keyframe
  2387.   if(not BOOL(Props.dwSampleFlags and AM_SAMPLE_SPLICEPOINT)) then Flags := Flags or AM_GBF_NOTASYNCPOINT;
  2388.   ASSERT(FOutput.FAllocator <> nil);
  2389.   if  BOOL(Props.dwSampleFlags and AM_SAMPLE_TIMEVALID) then Start := @Props.tStart else Start := nil;
  2390.   if  BOOL(Props.dwSampleFlags and AM_SAMPLE_STOPVALID) then Stop := @Props.tStop else Stop := nil;
  2391.   result := FOutput.FAllocator.GetBuffer(OutSample, Start, Stop, Flags);
  2392.   if FAILED(result) then exit;
  2393.   ASSERT(OutSample <> nil);
  2394.   if SUCCEEDED(OutSample.QueryInterface(IID_IMediaSample2, OutSample2)) then
  2395.     begin
  2396.       ASSERT(SUCCEEDED(OutSample2.GetProperties(4*4, OutProps)));
  2397.       OutProps.dwTypeSpecificFlags := Props.dwTypeSpecificFlags;
  2398.       OutProps.dwSampleFlags := (OutProps.dwSampleFlags and AM_SAMPLE_TYPECHANGED) or
  2399.           (Props.dwSampleFlags and (not AM_SAMPLE_TYPECHANGED));
  2400.       OutProps.tStart := Props.tStart;
  2401.       OutProps.tStop  := Props.tStop;
  2402.       OutProps.cbData := (4*4) + (2*8);
  2403.       OutSample2.SetProperties((4*4)+(2*8), OutProps);
  2404.       if BOOL(Props.dwSampleFlags and AM_SAMPLE_DATADISCONTINUITY) then FSampleSkipped := FALSE;
  2405.       OutSample2 := nil;
  2406.     end
  2407.   else
  2408.     begin
  2409.       if BOOL(Props.dwSampleFlags and AM_SAMPLE_TIMEVALID) then
  2410.         OutSample.SetTime(@Props.tStart, @Props.tStop);
  2411.       if BOOL(Props.dwSampleFlags and AM_SAMPLE_SPLICEPOINT) then
  2412.         OutSample.SetSyncPoint(True);
  2413.       if BOOL(Props.dwSampleFlags and AM_SAMPLE_DATADISCONTINUITY) then
  2414.         begin
  2415.           OutSample.SetDiscontinuity(True);
  2416.           FSampleSkipped := FALSE;
  2417.         end;
  2418.       // Copy the media times
  2419.       if (Sample.GetMediaTime(MediaStart,MediaEnd) = NOERROR) then
  2420.         OutSample.SetMediaTime(@MediaStart, @MediaEnd);
  2421.     end;
  2422.   result := S_OK;
  2423. end;
  2424. function TBCTransformFilter.NewSegment(Start, Stop: TReferenceTime;
  2425.   Rate: double): HRESULT;
  2426. begin
  2427.   result := S_OK;
  2428.   if (FOutput <> nil) then
  2429.     result := FOutput.DeliverNewSegment(Start, Stop, Rate);
  2430. end;
  2431. function TBCTransformFilter.Pause: HRESULT;
  2432. begin
  2433.   FcsFilter.Lock;
  2434.   try
  2435.     result := NOERROR;
  2436.     if (FState = State_Paused) then
  2437.       begin
  2438.         // (This space left deliberately blank)
  2439.       end
  2440.     // If we have no input pin or it isn't yet connected then when we are
  2441.     // asked to pause we deliver an end of stream to the downstream filter.
  2442.     // This makes sure that it doesn't sit there forever waiting for
  2443.     // samples which we cannot ever deliver without an input connection.
  2444.     else
  2445.       if ((FInput = nil) or (FInput.IsConnected = FALSE)) then
  2446.         begin
  2447.           if ((FOutput <> nil) and (FEOSDelivered = FALSE)) then
  2448.           begin
  2449.             FOutput.DeliverEndOfStream;
  2450.             FEOSDelivered := True;
  2451.           end;
  2452.           FState := State_Paused;
  2453.         end
  2454.     // We may have an input connection but no output connection
  2455.     // However, if we have an input pin we do have an output pin
  2456.     else
  2457.       if (FOutput.IsConnected = FALSE) then
  2458.         FState := State_Paused
  2459.       else
  2460.         begin
  2461.           if(FState = State_Stopped) then
  2462.           begin
  2463.               // allow a class derived from CTransformFilter
  2464.               // to know about starting and stopping streaming
  2465.               FcsReceive.Lock;
  2466.             try
  2467.               result := StartStreaming;
  2468.             finally
  2469.               FcsReceive.UnLock;
  2470.             end;
  2471.           end;
  2472.           if SUCCEEDED(result) then result := inherited Pause;
  2473.         end;
  2474.     FSampleSkipped := FALSE;
  2475.     FQualityChanged := FALSE;
  2476.   finally
  2477.     FcsFilter.UnLock;
  2478.   end;
  2479. end;
  2480. // override this to customize the transform process
  2481. function TBCTransformFilter.Receive(Sample: IMediaSample): HRESULT;
  2482. var
  2483.   Props: PAMSample2Properties;
  2484.   OutSample: IMediaSample;
  2485. begin
  2486.   //  Check for other streams and pass them on
  2487.   Props := FInput.SampleProps;
  2488.   if(Props.dwStreamId <> AM_STREAM_MEDIA) then
  2489.   begin
  2490.     result := FOutput.FInputPin.Receive(Sample);
  2491.     exit;
  2492.   end;
  2493.   // If no output to deliver to then no point sending us data
  2494.   ASSERT(FOutput <> nil) ;
  2495.   // Set up the output sample
  2496.   result := InitializeOutputSample(Sample, OutSample);
  2497.   if FAILED(result) then exit;
  2498.   result := Transform(Sample, OutSample);
  2499.   if FAILED(result) then
  2500.     begin
  2501.     {$IFDEF DEBUG}
  2502.       DbgLog(self, 'Error from transform');
  2503.     {$ENDIF}
  2504.       exit;
  2505.     end
  2506.   else
  2507.     begin
  2508.       // the Transform() function can return S_FALSE to indicate that the
  2509.       // sample should not be delivered; we only deliver the sample if it's
  2510.       // really S_OK (same as NOERROR, of course.)
  2511.       if (result = NOERROR) then
  2512.         begin
  2513.           result := FOutput.FInputPin.Receive(OutSample);
  2514.           FSampleSkipped := FALSE;   // last thing no longer dropped
  2515.         end
  2516.       else
  2517.         begin
  2518.           // S_FALSE returned from Transform is a PRIVATE agreement
  2519.           // We should return NOERROR from Receive() in this cause because returning S_FALSE
  2520.           // from Receive() means that this is the end of the stream and no more data should
  2521.           // be sent.
  2522.           if (result = S_FALSE) then
  2523.           begin
  2524.             //  Release the sample before calling notify to avoid
  2525.             //  deadlocks if the sample holds a lock on the system
  2526.             //  such as DirectDraw buffers do
  2527.             OutSample := nil;
  2528.             FSampleSkipped := True;
  2529.             if not FQualityChanged then
  2530.             begin
  2531.               NotifyEvent(EC_QUALITY_CHANGE,0,0);
  2532.               FQualityChanged := True;
  2533.             end;
  2534.             result := NOERROR;
  2535.             exit;
  2536.           end;
  2537.         end;
  2538.     end;
  2539.   // release the output buffer. If the connected pin still needs it,
  2540.   // it will have addrefed it itself.
  2541.   OutSample := nil;
  2542. end;
  2543. function TBCTransformFilter.SetMediaType(direction: TPinDirection;
  2544.   pmt: PAMMediaType): HRESULT;
  2545. begin
  2546.   result := NOERROR;
  2547. end;
  2548. // override these two functions if you want to inform something
  2549. // about entry to or exit from streaming state.
  2550. function TBCTransformFilter.StartStreaming: HRESULT;
  2551. begin
  2552.   result := NOERROR;
  2553. end;
  2554. // override these so that the derived filter can catch them
  2555. function TBCTransformFilter.Stop: HRESULT;
  2556. begin
  2557.   FcsFilter.Lock;
  2558.   try
  2559.     if(FState = State_Stopped) then
  2560.     begin
  2561.       result := NOERROR;
  2562.       exit;
  2563.     end;
  2564.     // Succeed the Stop if we are not completely connected
  2565.     ASSERT((FInput = nil) or (FOutput <> nil));
  2566.     if((FInput = nil) or (FInput.IsConnected = FALSE) or (FOutput.IsConnected = FALSE)) then
  2567.     begin
  2568.       FState := State_Stopped;
  2569.       FEOSDelivered := FALSE;
  2570.       result := NOERROR;
  2571.       exit;
  2572.     end;
  2573.     ASSERT(FInput <> nil);
  2574.     ASSERT(FOutput <> nil);
  2575.     // decommit the input pin before locking or we can deadlock
  2576.     FInput.Inactive;
  2577.     // synchronize with Receive calls
  2578.     FcsReceive.Lock;
  2579.     try
  2580.       FOutput.Inactive;
  2581.       // allow a class derived from CTransformFilter
  2582.       // to know about starting and stopping streaming
  2583.       result := StopStreaming;
  2584.       if SUCCEEDED(result) then
  2585.       begin
  2586.         // complete the state transition
  2587.         FState := State_Stopped;
  2588.         FEOSDelivered := FALSE;
  2589.       end;
  2590.     finally
  2591.       FcsReceive.UnLock;
  2592.     end;
  2593.   finally
  2594.     FcsFilter.UnLock;
  2595.   end;
  2596. end;
  2597. function TBCTransformFilter.StopStreaming: HRESULT;
  2598. begin
  2599.   result := NOERROR;
  2600. end;
  2601. function TBCTransformFilter.Transform(msIn, msout: IMediaSample): HRESULT;
  2602. begin
  2603. {$IFDEF DEBUG}
  2604.   DbgLog(self, 'TBCTransformFilter.Transform should never be called');
  2605. {$ENDIF}
  2606.   result := E_UNEXPECTED;
  2607. end;
  2608. { TBCTransformOutputPin }
  2609. // provides derived filter a chance to release it's extra interfaces
  2610. function TBCTransformOutputPin.BreakConnect: HRESULT;
  2611. begin
  2612.   //  Can't disconnect unless stopped
  2613.   ASSERT(IsStopped);
  2614.   FTransformFilter.BreakConnect(PINDIR_OUTPUT);
  2615.   result := inherited BreakConnect;
  2616. end;
  2617. // provides derived filter a chance to grab extra interfaces
  2618. function TBCTransformOutputPin.CheckConnect(Pin: IPin): HRESULT;
  2619. begin
  2620.   // we should have an input connection first
  2621.   ASSERT(FTransformFilter.FInput <> nil);
  2622.   if(FTransformFilter.FInput.IsConnected = FALSE) then
  2623.     begin
  2624.       result := E_UNEXPECTED;
  2625.       exit;
  2626.     end;
  2627.   result := FTransformFilter.CheckConnect(PINDIR_OUTPUT, Pin);
  2628.   if FAILED(result) then exit;
  2629.   result := inherited CheckConnect(Pin);
  2630. end;
  2631. // check a given transform - must have selected input type first
  2632. function TBCTransformOutputPin.CheckMediaType(
  2633.   mtOut: PAMMediaType): HRESULT;
  2634. begin
  2635.   // must have selected input first
  2636.   ASSERT(FTransformFilter.FInput <> nil);
  2637.   if(FTransformFilter.FInput.IsConnected = FALSE) then
  2638.     begin
  2639.       result := E_INVALIDARG;
  2640.       exit;
  2641.     end;
  2642.   result := FTransformFilter.CheckTransform(FTransformFilter.FInput.AMMediaType, mtOut);
  2643. end;
  2644. // Let derived class know when the output pin is connected
  2645. function TBCTransformOutputPin.CompleteConnect(ReceivePin: IPin): HRESULT;
  2646. begin
  2647.   result := FTransformFilter.CompleteConnect(PINDIR_OUTPUT, ReceivePin);
  2648.   if FAILED(result) then exit;
  2649.   result := inherited CompleteConnect(ReceivePin);
  2650. end;
  2651. constructor TBCTransformOutputPin.Create(ObjectName: string;
  2652.   TransformFilter: TBCTransformFilter; out hr: HRESULT; Name: WideString);
  2653. begin
  2654.   inherited create(ObjectName, TransformFilter, TransformFilter.FcsFilter, hr, Name);
  2655.   FPosition := nil;
  2656. {$IFDEF DEBUG}
  2657.   DbgLog(self, 'TBCTransformOutputPin.Create');
  2658. {$ENDIF}
  2659.   FTransformFilter := TransformFilter;
  2660. end;
  2661. function TBCTransformOutputPin.DecideBufferSize(Alloc: IMemAllocator;
  2662.   Prop: PAllocatorProperties): HRESULT;
  2663. begin
  2664.   result := FTransformFilter.DecideBufferSize(Alloc, Prop);
  2665. end;
  2666. destructor TBCTransformOutputPin.destroy;
  2667. begin
  2668. {$IFDEF DEBUG}
  2669.   DbgLog(self, 'TBCTransformOutputPin.Destroy');
  2670. {$ENDIF}
  2671.   FPosition := nil;
  2672.   inherited;
  2673. end;
  2674. function TBCTransformOutputPin.GetMediaType(Position: integer;
  2675.   out MediaType: PAMMediaType): HRESULT;
  2676. begin
  2677.   ASSERT(FTransformFilter.FInput <> nil);
  2678.   //  We don't have any media types if our input is not connected
  2679.   if(FTransformFilter.FInput.IsConnected) then
  2680.     begin
  2681.       result := FTransformFilter.GetMediaType(Position, MediaType);
  2682.       exit;
  2683.     end
  2684.   else
  2685.     result := VFW_S_NO_MORE_ITEMS;
  2686. end;
  2687. function TBCTransformOutputPin.NonDelegatingQueryInterface(
  2688.   const IID: TGUID; out Obj): HResult;
  2689. begin
  2690.   if IsEqualGUID(iid, IID_IMediaPosition) or IsEqualGUID(iid, IID_IMediaSeeking) then
  2691.     begin
  2692.       // we should have an input pin by now
  2693.       ASSERT(FTransformFilter.FInput <> nil);
  2694.       if (FPosition = nil) then
  2695.         begin
  2696.           result := CreatePosPassThru(GetOwner, FALSE, FTransformFilter.FInput, FPosition);
  2697.           if FAILED(result) then exit;
  2698.         end;
  2699.       result := FPosition.QueryInterface(iid, obj);
  2700.     end
  2701.   else
  2702.     result := inherited NonDelegatingQueryInterface(iid, obj);
  2703. end;
  2704. // Override this if you can do something constructive to act on the
  2705. // quality message.  Consider passing it upstream as well
  2706. // Pass the quality mesage on upstream.
  2707. function TBCTransformOutputPin.Notify(Sendr: IBaseFilter; q: TQuality): HRESULT;
  2708. begin
  2709.   // First see if we want to handle this ourselves
  2710.   result := FTransformFilter.AlterQuality(q);
  2711.   if (result <> S_FALSE) then exit;
  2712.   // S_FALSE means we pass the message on.
  2713.   // Find the quality sink for our input pin and send it there
  2714.   ASSERT(FTransformFilter.FInput <> nil);
  2715.   result := FTransformFilter.FInput.PassNotify(q);
  2716. end;
  2717. function TBCTransformOutputPin.QueryId(out Id: PWideChar): HRESULT;
  2718. begin
  2719.   result := AMGetWideString('Out', Id);
  2720. end;
  2721. // called after we have agreed a media type to actually set it in which case
  2722. // we run the CheckTransform function to get the output format type again
  2723. function TBCTransformOutputPin.SetMediaType(pmt: PAMMediaType): HRESULT;
  2724. begin
  2725.   ASSERT(FTransformFilter.FInput <> nil);
  2726.   ASSERT(not IsEqualGUID(FTransformFilter.FInput.AMMediaType.majortype,GUID_NULL));
  2727.   // Set the base class media type (should always succeed)
  2728.   result := inherited SetMediaType(pmt);
  2729.   if FAILED(result) then exit;
  2730. {$ifdef DEBUG}
  2731.   if(FAILED(FTransformFilter.CheckTransform(FTransformFilter.FInput.AMMediaType, pmt))) then
  2732.     begin
  2733.       DbgLog(self, '*** This filter is accepting an output media type');
  2734.       DbgLog(self, '    that it can''t currently transform to.  I hope');
  2735.       DbgLog(self, '    it''s smart enough to reconnect its input.');
  2736.     end;
  2737. {$endif}
  2738.   result := FTransformFilter.SetMediaType(PINDIR_OUTPUT,pmt);
  2739. end;
  2740. // milenko start (added TBCVideoTransformFilter conversion)
  2741. { TBCVideoTransformFilter }
  2742. // This class is derived from CTransformFilter, but is specialised to handle
  2743. // the requirements of video quality control by frame dropping.
  2744. // This is a non-in-place transform, (i.e. it copies the data) such as a decoder.
  2745. constructor TBCVideoTransformFilter.Create(Name: WideString; Unk: IUnknown; clsid: TGUID);
  2746. begin
  2747.   inherited Create(name, Unk, clsid);
  2748.   FitrLate := 0;
  2749.   FKeyFramePeriod := 0;      // No QM until we see at least 2 key frames
  2750.   FFramesSinceKeyFrame := 0;
  2751.   FSkipping := False;
  2752.   FtDecodeStart := 0;
  2753.   FitrAvgDecode := 300000;    // 30mSec - probably allows skipping
  2754.   FQualityChanged := False;
  2755. {$IFDEF PERF}
  2756.   RegisterPerfId();
  2757. {$ENDIF} // PERF
  2758. end;
  2759. destructor TBCVideoTransformFilter.Destroy;
  2760. begin
  2761.   inherited Destroy;
  2762. end;
  2763. // Overriden to reset quality management information
  2764. function TBCVideoTransformFilter.EndFlush: HRESULT;
  2765. begin
  2766.   FcsReceive.Lock;
  2767.   try
  2768.     // Reset our stats
  2769.     //
  2770.     // Note - we don't want to call derived classes here,
  2771.     // we only want to reset our internal variables and this
  2772.     // is a convenient way to do it
  2773.     StartStreaming;
  2774.     Result := inherited EndFlush;
  2775.   finally
  2776.     FcsReceive.UnLock;
  2777.   end;
  2778. end;
  2779. {$IFDEF PERF}
  2780. procedure TBCVideoTransformFilter.RegisterPerfId;
  2781. begin
  2782.   FidSkip        := MSR_REGISTER('Video Transform Skip frame');
  2783.   FidFrameType   := MSR_REGISTER('Video transform frame type');
  2784.   FidLate        := MSR_REGISTER('Video Transform Lateness');
  2785.   FidTimeTillKey := MSR_REGISTER('Video Transform Estd. time to next key');
  2786. //  inherited RegisterPerfId;
  2787. end;
  2788. {$ENDIF}
  2789. function TBCVideoTransformFilter.StartStreaming: HRESULT;
  2790. begin
  2791.   FitrLate := 0;
  2792.   FKeyFramePeriod := 0;       // No QM until we see at least 2 key frames
  2793.   FFramesSinceKeyFrame := 0;
  2794.   FSkipping := False;
  2795.   FtDecodeStart := 0;
  2796.   FitrAvgDecode := 300000;     // 30mSec - probably allows skipping
  2797.   FQualityChanged := False;
  2798.   FSampleSkipped := False;
  2799.   Result := NOERROR;
  2800. end;
  2801. // Reset our quality management state
  2802. function TBCVideoTransformFilter.AbortPlayback(hr: HRESULT): HRESULT;
  2803. begin
  2804.   NotifyEvent(EC_ERRORABORT, hr, 0);
  2805.   FOutput.DeliverEndOfStream;
  2806.   Result := hr;
  2807. end;
  2808. // Receive()
  2809. //
  2810. // Accept a sample from upstream, decide whether to process it
  2811. // or drop it.  If we process it then get a buffer from the
  2812. // allocator of the downstream connection, transform it into the
  2813. // new buffer and deliver it to the downstream filter.
  2814. // If we decide not to process it then we do not get a buffer.
  2815. // Remember that although this code will notice format changes coming into
  2816. // the input pin, it will NOT change its output format if that results
  2817. // in the filter needing to make a corresponding output format change.  Your
  2818. // derived filter will have to take care of that.  (eg. a palette change if
  2819. // the input and output is an 8 bit format).  If the input sample is discarded
  2820. // and nothing is sent out for this Receive, please remember to put the format
  2821. // change on the first output sample that you actually do send.
  2822. // If your filter will produce the same output type even when the input type
  2823. // changes, then this base class code will do everything you need.
  2824. function TBCVideoTransformFilter.Receive(Sample: IMediaSample): HRESULT;
  2825. var
  2826.   pmtOut, pmt: PAMMediaType;
  2827.   pOutSample: IMediaSample;
  2828. {$IFDEF DEBUG}
  2829.   fccOut: TGUID;
  2830. lCompression: LongInt;
  2831. lBitCount: LongInt;
  2832. lStride: LongInt;
  2833.   rcS: TRect;
  2834.   rcT: TRect;
  2835.   rcS1: TRect;
  2836.   rcT1: TRect;
  2837. {$ENDIF}
  2838. begin
  2839.   // If the next filter downstream is the video renderer, then it may
  2840.   // be able to operate in DirectDraw mode which saves copying the data
  2841.   // and gives higher performance.  In that case the buffer which we
  2842.   // get from GetDeliveryBuffer will be a DirectDraw buffer, and
  2843.   // drawing into this buffer draws directly onto the display surface.
  2844.   // This means that any waiting for the correct time to draw occurs
  2845.   // during GetDeliveryBuffer, and that once the buffer is given to us
  2846.   // the video renderer will count it in its statistics as a frame drawn.
  2847.   // This means that any decision to drop the frame must be taken before
  2848.   // calling GetDeliveryBuffer.
  2849.   ASSERT(FcsReceive.CritCheckIn);
  2850.   ASSERT(Sample <> nil);
  2851.   // If no output pin to deliver to then no point sending us data
  2852.   ASSERT (FOutput <> nil) ;
  2853.   // The source filter may dynamically ask us to start transforming from a
  2854.   // different media type than the one we're using now.  If we don't, we'll
  2855.   // draw garbage. (typically, this is a palette change in the movie,
  2856.   // but could be something more sinister like the compression type changing,
  2857.   // or even the video size changing)
  2858.   Sample.GetMediaType(pmt);
  2859.   if (pmt <> nil) and  (pmt.pbFormat <> nil) then
  2860.   begin
  2861.     // spew some debug output
  2862.     ASSERT(not IsEqualGUID(pmt.majortype, GUID_NULL));
  2863.   {$IFDEF DEBUG}
  2864.     fccOut := pmt.subtype;
  2865.     lCompression := PVideoInfoHeader(pmt.pbFormat).bmiHeader.biCompression;
  2866.     lBitCount := PVideoInfoHeader(pmt.pbFormat).bmiHeader.biBitCount;
  2867.     lStride := (PVideoInfoHeader(pmt.pbFormat).bmiHeader.biWidth * lBitCount + 7) div 8;
  2868.     lStride := (lStride + 3) and not 3;
  2869.     rcS1 := PVideoInfoHeader(pmt.pbFormat).rcSource;
  2870.     rcT1 := PVideoInfoHeader(pmt.pbFormat).rcTarget;
  2871.     DbgLog(Self,'Changing input type on the fly to');
  2872.     DbgLog(Self,'FourCC: ' + inttohex(fccOut.D1,8) + ' Compression: ' + inttostr(lCompression) +
  2873.            ' BitCount: ' + inttostr(lBitCount));
  2874.     DbgLog(Self,'biHeight: ' + inttostr(PVideoInfoHeader(pmt.pbFormat).bmiHeader.biHeight) +
  2875.            ' rcDst: (' + inttostr(rcT1.left) + ', ' + inttostr(rcT1.top) + ', ' +
  2876.            inttostr(rcT1.right) + ', ' + inttostr(rcT1.bottom) + ')');
  2877.     DbgLog(Self,'rcSrc: (' + inttostr(rcS1.left) + ', ' + inttostr(rcS1.top) + ', ' +
  2878.            inttostr(rcS1.right) + ', ' + inttostr(rcS1.bottom) + ') Stride' + inttostr(lStride));
  2879.   {$ENDIF}
  2880.     // now switch to using the new format.  I am assuming that the
  2881.     // derived filter will do the right thing when its media type is
  2882.     // switched and streaming is restarted.
  2883.     StopStreaming();
  2884.     CopyMediaType(FInput.AMMediaType,pmt);
  2885.     DeleteMediaType(pmt);
  2886.     // if this fails, playback will stop, so signal an error
  2887.     Result := StartStreaming;
  2888.     if (FAILED(Result)) then
  2889.     begin
  2890.       Result := AbortPlayback(Result);
  2891.       Exit;
  2892.     end;
  2893.   end;
  2894.     // Now that we have noticed any format changes on the input sample, it's
  2895.     // OK to discard it.
  2896.   if ShouldSkipFrame(Sample) then
  2897.   begin
  2898.   {$IFDEF PERF}
  2899. //    MSR_NOTE(m_idSkip);
  2900.   {$ENDIF}
  2901.     FSampleSkipped := True;
  2902.     Result := NOERROR;
  2903.     Exit;
  2904.   end;
  2905.     // Set up the output sample
  2906.   Result := InitializeOutputSample(Sample, pOutSample);
  2907.   if (FAILED(Result)) then Exit;
  2908.   FSampleSkipped := False;
  2909.   // The renderer may ask us to on-the-fly to start transforming to a
  2910.   // different format.  If we don't obey it, we'll draw garbage
  2911.   pOutSample.GetMediaType(pmtOut);
  2912.   if (pmtOut <> nil) and (pmtOut.pbFormat <> nil) then
  2913.   begin
  2914.     // spew some debug output
  2915.     ASSERT(not IsEqualGUID(pmtOut.majortype, GUID_NULL));
  2916.   {$IFDEF DEBUG}
  2917.     fccOut := pmtOut.subtype;
  2918.     lCompression := PVideoInfoHeader(pmtOut.pbFormat).bmiHeader.biCompression;
  2919.     lBitCount := PVideoInfoHeader(pmtOut.pbFormat).bmiHeader.biBitCount;
  2920.     lStride := (PVideoInfoHeader(pmtOut.pbFormat).bmiHeader.biWidth * lBitCount + 7) div 8;
  2921.     lStride := (lStride + 3) and not 3;
  2922.     rcS := PVideoInfoHeader(pmtOut.pbFormat).rcSource;
  2923.     rcT := PVideoInfoHeader(pmtOut.pbFormat).rcTarget;
  2924.     DbgLog(Self,'Changing input type on the fly to');
  2925.     DbgLog(Self,'FourCC: ' + inttohex(fccOut.D1,8) + ' Compression: ' + inttostr(lCompression) +
  2926.            ' BitCount: ' + inttostr(lBitCount));
  2927.     DbgLog(Self,'biHeight: ' + inttostr(PVideoInfoHeader(pmtOut.pbFormat).bmiHeader.biHeight) +
  2928.            ' rcDst: (' + inttostr(rcT1.left) + ', ' + inttostr(rcT1.top) + ', ' +
  2929.            inttostr(rcT1.right) + ', ' + inttostr(rcT1.bottom) + ')');
  2930.     DbgLog(Self,'rcSrc: (' + inttostr(rcS1.left) + ', ' + inttostr(rcS1.top) + ', ' +
  2931.            inttostr(rcS1.right) + ', ' + inttostr(rcS1.bottom) + ') Stride' + inttostr(lStride));
  2932.   {$ENDIF}
  2933.     // now switch to using the new format.  I am assuming that the
  2934.     // derived filter will do the right thing when its media type is
  2935.     // switched and streaming is restarted.
  2936.     StopStreaming();
  2937.     CopyMediaType(FOutput.AMMediaType,pmtOut);
  2938.     DeleteMediaType(pmtOut);
  2939.     Result := StartStreaming;
  2940.     if (SUCCEEDED(Result)) then
  2941.     begin
  2942.       // a new format, means a new empty buffer, so wait for a keyframe
  2943.       // before passing anything on to the renderer.
  2944.       // !!! a keyframe may never come, so give up after 30 frames
  2945.     {$IFDEF DEBUG}
  2946.       DbgLog(Self,'Output format change means we must wait for a keyframe');
  2947.     {$ENDIF}
  2948.       FWaitForKey := 30;
  2949.       // if this fails, playback will stop, so signal an error
  2950.     end else
  2951.     begin
  2952.       //  Must release the sample before calling AbortPlayback
  2953.       //  because we might be holding the win16 lock or
  2954.       //  ddraw lock
  2955.       pOutSample := nil;
  2956.       AbortPlayback(Result);
  2957.       Exit;
  2958.     end;
  2959.   end;
  2960.   // After a discontinuity, we need to wait for the next key frame
  2961.   if (Sample.IsDiscontinuity = S_OK) then
  2962.   begin
  2963.   {$IFDEF DEBUG}
  2964.     DbgLog(Self,'Non-key discontinuity - wait for keyframe');
  2965.   {$ENDIF}
  2966.     FWaitForKey := 30;
  2967.   end;
  2968.   // Start timing the transform (and log it if PERF is defined)
  2969.   if (SUCCEEDED(Result)) then
  2970.   begin
  2971.     FtDecodeStart := timeGetTime;
  2972.   {$IFDEF PERF}
  2973. //    MSR_START(FidTransform); // not added in conversion
  2974.   {$ENDIF}
  2975.     // have the derived class transform the data
  2976.     Result := Transform(Sample, pOutSample);
  2977.     // Stop the clock (and log it if PERF is defined)
  2978.   {$IFDEF PERF}
  2979. //    MSR_STOP(m_idTransform); // not added in conversion
  2980.   {$ENDIF}
  2981.     FtDecodeStart := timeGetTime - int64(FtDecodeStart);
  2982.     FitrAvgDecode := Round(FtDecodeStart * (10000 / 16) + 15 * (FitrAvgDecode / 16));
  2983.     // Maybe we're waiting for a keyframe still?
  2984.     if (FWaitForKey > 0) then dec(FWaitForKey);
  2985.     if (FWaitForKey > 0) and (Sample.IsSyncPoint = S_OK) then BOOL(FWaitForKey) := False;
  2986.     // if so, then we don't want to pass this on to the renderer
  2987.     if (FWaitForKey > 0) and (Result = NOERROR) then
  2988.     begin
  2989.     {$IFDEF DEBUG}
  2990.       DbgLog(Self,'still waiting for a keyframe');
  2991.     Result := S_FALSE;
  2992.     {$ENDIF}
  2993.     end;
  2994.   end;
  2995.   if (FAILED(Result)) then
  2996.   begin
  2997.   {$IFDEF DEBUG}
  2998.     DbgLog(Self,'Error from video transform');
  2999.   {$ENDIF}
  3000.   end else
  3001.   begin
  3002.     // the Transform() function can return S_FALSE to indicate that the
  3003.     // sample should not be delivered; we only deliver the sample if it's
  3004.     // really S_OK (same as NOERROR, of course.)
  3005.     // Try not to return S_FALSE to a direct draw buffer (it's wasteful)
  3006.     // Try to take the decision earlier - before you get it.
  3007.     if (Result = NOERROR) then
  3008.     begin
  3009.      Result := FOutput.Deliver(pOutSample);
  3010.     end else
  3011.     begin
  3012.       // S_FALSE returned from Transform is a PRIVATE agreement
  3013.       // We should return NOERROR from Receive() in this case because returning S_FALSE
  3014.       // from Receive() means that this is the end of the stream and no more data should
  3015.       // be sent.
  3016.       if (S_FALSE = Result) then
  3017.       begin
  3018.         //  We must Release() the sample before doing anything
  3019.         //  like calling the filter graph because having the
  3020.         //  sample means we may have the DirectDraw lock
  3021.         //  (== win16 lock on some versions)
  3022.         pOutSample := nil;
  3023.         FSampleSkipped := True;
  3024.         if not FQualityChanged then
  3025.         begin
  3026.           FQualityChanged := True;
  3027.           NotifyEvent(EC_QUALITY_CHANGE,0,0);
  3028.         end;
  3029.         Result := NOERROR;
  3030.         Exit;
  3031.       end;
  3032.     end;
  3033.   end;
  3034.   // release the output buffer. If the connected pin still needs it,
  3035.   // it will have addrefed it itself.
  3036.   pOutSample := nil;
  3037.   ASSERT(FcsReceive.CritCheckIn);
  3038. end;
  3039. function TBCVideoTransformFilter.AlterQuality(const q: TQuality): HRESULT;
  3040. begin
  3041.   // to reduce the amount of 64 bit arithmetic, m_itrLate is an int.
  3042.   // +, -, >, == etc  are not too bad, but * and / are painful.
  3043.   if (FitrLate > 300000000) then
  3044.   begin
  3045.     // Avoid overflow and silliness - more than 30 secs late is already silly
  3046.     FitrLate := 300000000;
  3047.    end else
  3048.    begin
  3049.      FitrLate := integer(q.Late);
  3050.    end;
  3051.   // We ignore the other fields
  3052.   // We're actually not very good at handling this.  In non-direct draw mode
  3053.   // most of the time can be spent in the renderer which can skip any frame.
  3054.   // In that case we'd rather the renderer handled things.
  3055.   // Nevertheless we will keep an eye on it and if we really start getting
  3056.   // a very long way behind then we will actually skip - but we'll still tell
  3057.   // the renderer (or whoever is downstream) that they should handle quality.
  3058.   Result := E_FAIL;     // Tell the renderer to do his thing.
  3059. end;
  3060. function TBCVideoTransformFilter.ShouldSkipFrame(pIn: IMediaSample): Boolean;
  3061. var
  3062.   Start, StopAt: TReferenceTime;
  3063.   itrFrame: integer;
  3064.   it: integer;
  3065. begin
  3066.   Result := pIn.GetTime(Start, StopAt) = S_OK;
  3067.   // Don't skip frames with no timestamps
  3068.   if not Result then Exit;
  3069.   itrFrame := integer(StopAt - Start);  // frame duration
  3070.   if(S_OK = pIn.IsSyncPoint) then
  3071.   begin
  3072.     {$IFDEF PERF}
  3073.     MSR_INTEGER(FidFrameType, 1);
  3074.     {$ENDIF}
  3075.     if (FKeyFramePeriod < FFramesSinceKeyFrame) then
  3076.     begin
  3077.       // record the max
  3078.       FKeyFramePeriod := FFramesSinceKeyFrame;
  3079.     end;
  3080.     FFramesSinceKeyFrame := 0;
  3081.     FSkipping := False;
  3082.   end else
  3083.   begin
  3084.     {$IFDEF PERF}
  3085.     MSR_INTEGER(FidFrameType, 2);
  3086.     {$ENDIF}
  3087.     if (FFramesSinceKeyFrame > FKeyFramePeriod) and (FKeyFramePeriod > 0) then
  3088.     begin
  3089.       // We haven't seen the key frame yet, but we were clearly being
  3090.       // overoptimistic about how frequent they are.
  3091.       FKeyFramePeriod := FFramesSinceKeyFrame;
  3092.     end;
  3093.   end;
  3094.   // Whatever we might otherwise decide,
  3095.   // if we are taking only a small fraction of the required frame time to decode
  3096.   // then any quality problems are actually coming from somewhere else.
  3097.   // Could be a net problem at the source for instance.  In this case there's
  3098.   // no point in us skipping frames here.
  3099.   if (FitrAvgDecode * 4 > itrFrame) then
  3100.   begin
  3101.     // Don't skip unless we are at least a whole frame late.
  3102.     // (We would skip B frames if more than 1/2 frame late, but they're safe).
  3103.     if (FitrLate > itrFrame) then
  3104.     begin
  3105.       // Don't skip unless the anticipated key frame would be no more than
  3106.       // 1 frame early.  If the renderer has not been waiting (we *guess*
  3107.       // it hasn't because we're late) then it will allow frames to be
  3108.       // played early by up to a frame.
  3109.       // Let T = Stream time from now to anticipated next key frame
  3110.       // = (frame duration) * (KeyFramePeriod - FramesSinceKeyFrame)
  3111.       // So we skip if T - Late < one frame  i.e.
  3112.       //   (duration) * (freq - FramesSince) - Late < duration
  3113.       // or (duration) * (freq - FramesSince - 1) < Late
  3114.       // We don't dare skip until we have seen some key frames and have
  3115.       // some idea how often they occur and they are reasonably frequent.
  3116.       if (FKeyFramePeriod > 0) then
  3117.       begin
  3118.         // It would be crazy - but we could have a stream with key frames
  3119.         // a very long way apart - and if they are further than about
  3120.         // 3.5 minutes apart then we could get arithmetic overflow in
  3121.         // reference time units.  Therefore we switch to mSec at this point
  3122.         it := (itrFrame div 10000) * (FKeyFramePeriod - FFramesSinceKeyFrame - 1);
  3123.         {$IFDEF PERF}
  3124.         MSR_INTEGER(FidTimeTillKey, it);
  3125.         {$ENDIF}
  3126.         // For debug - might want to see the details - dump them as scratch pad
  3127.         {$IFDEF VTRANSPERF}
  3128.         MSR_INTEGER(0, itrFrame);
  3129.         MSR_INTEGER(0, FFramesSinceKeyFrame);
  3130.         MSR_INTEGER(0, FKeyFramePeriod);
  3131.         {$ENDIF}
  3132.         if (FitrLate div 10000 > it) then
  3133.         begin
  3134.           FSkipping := True;
  3135.           // Now we are committed.  Once we start skipping, we
  3136.           // cannot stop until we hit a key frame.
  3137.         end else
  3138.         begin
  3139.         {$IFDEF VTRANSPERF}
  3140.           MSR_INTEGER(0, 777770);  // not near enough to next key
  3141.         {$ENDIF}
  3142.         end;
  3143.       end else
  3144.       begin
  3145.       {$IFDEF VTRANSPERF}
  3146.         MSR_INTEGER(0, 777771);  // Next key not predictable
  3147.       {$ENDIF}
  3148.       end;
  3149.     end else
  3150.     begin
  3151.     {$IFDEF VTRANSPERF}
  3152.       MSR_INTEGER(0, 777772);  // Less than one frame late
  3153.       MSR_INTEGER(0, FitrLate);
  3154.       MSR_INTEGER(0, itrFrame);
  3155.     {$ENDIF}
  3156.     end;
  3157.   end else
  3158.   begin
  3159.   {$IFDEF VTRANSPERF}
  3160.     MSR_INTEGER(0, 777773);  // Decode time short - not not worth skipping
  3161.     MSR_INTEGER(0, FitrAvgDecode);
  3162.     MSR_INTEGER(0, itrFrame);
  3163.     {$ENDIF}
  3164.   end;
  3165.   inc(FFramesSinceKeyFrame);
  3166.   if FSkipping then
  3167.   begin
  3168.     // We will count down the lateness as we skip each frame.
  3169.     // We re-assess each frame.  The key frame might not arrive when expected.
  3170.     // We reset m_itrLate if we get a new Quality message, but actually that's
  3171.     // not likely because we're not sending frames on to the Renderer.  In
  3172.     // fact if we DID get another one it would mean that there's a long
  3173.     // pipe between us and the renderer and we might need an altogether
  3174.     // better strategy to avoid hunting!
  3175.     FitrLate := FitrLate - itrFrame;
  3176.   end;
  3177. {$IFDEF PERF}
  3178.   MSR_INTEGER(FidLate, integer(FitrLate div 10000)); // Note how late we think we are
  3179. {$ENDIF}
  3180.   if FSkipping then
  3181.   begin
  3182.     if not FQualityChanged then
  3183.     begin
  3184.       FQualityChanged := True;
  3185.       NotifyEvent(EC_QUALITY_CHANGE,0,0);
  3186.     end;
  3187.   end;
  3188.   Result := FSkipping;
  3189. end;
  3190. // milenko end
  3191. { TCTransInPlaceInputPin }
  3192. function TBCTransInPlaceInputPin.CheckMediaType(
  3193.   pmt: PAMMediaType): HRESULT;
  3194. begin
  3195.   result := FTIPFilter.CheckInputType(pmt);
  3196.   if (result <> S_OK) then exit;
  3197.   if FTIPFilter.FOutput.IsConnected then
  3198.     result := FTIPFilter.FOutput.GetConnected.QueryAccept(pmt^)
  3199.   else
  3200.     result := S_OK;
  3201. end;
  3202. function TBCTransInPlaceInputPin.EnumMediaTypes(
  3203.   out ppEnum: IEnumMediaTypes): HRESULT;
  3204. begin
  3205.   // Can only pass through if connected
  3206.   if (not FTIPFilter.FOutput.IsConnected) then
  3207.     begin
  3208.       result := VFW_E_NOT_CONNECTED;
  3209.       exit;
  3210.     end;
  3211.   result := FTIPFilter.FOutput.GetConnected.EnumMediaTypes(ppEnum);
  3212. end;
  3213. function TBCTransInPlaceInputPin.GetAllocator(
  3214.   out Allocator: IMemAllocator): HRESULT;
  3215. begin
  3216.   FLock.Lock;
  3217.   try
  3218.     if FTIPFilter.FOutput.IsConnected then
  3219.       begin
  3220.         //  Store the allocator we got
  3221.         result := FTIPFilter.OutputPin.ConnectedIMemInputPin.GetAllocator(Allocator);
  3222.         if SUCCEEDED(result) then
  3223.           FTIPFilter.OutputPin.SetAllocator(Allocator);
  3224.       end
  3225.     else
  3226.       begin
  3227.         //  Help upstream filter (eg TIP filter which is having to do a copy)
  3228.         //  by providing a temp allocator here - we'll never use
  3229.         //  this allocator because when our output is connected we'll
  3230.         //  reconnect this pin
  3231.         result := inherited GetAllocator(Allocator);
  3232.       end;
  3233.   finally
  3234.     FLock.UnLock;
  3235.   end;
  3236. end;
  3237. function TBCTransInPlaceInputPin.GetAllocatorRequirements(
  3238.   props: PAllocatorProperties): HRESULT;
  3239. begin
  3240.   if FTIPFilter.FOutput.IsConnected then
  3241.     result := FTIPFilter.OutputPin.ConnectedIMemInputPin.GetAllocatorRequirements(Props^)
  3242.   else
  3243.     result := E_NOTIMPL;
  3244. end;
  3245. function TBCTransInPlaceInputPin.NotifyAllocator(Allocator: IMemAllocator;
  3246.   ReadOnly: BOOL): HRESULT;
  3247. var
  3248.   OutputAllocator: IMemAllocator;
  3249.   Props, Actual: TAllocatorProperties;
  3250. begin
  3251.   result := S_OK;
  3252.   FLock.Lock;
  3253.   try
  3254.     FReadOnly := ReadOnly;
  3255.     //  If we modify data then don't accept the allocator if it's
  3256.     //  the same as the output pin's allocator
  3257.     //  If our output is not connected just accept the allocator
  3258.     //  We're never going to use this allocator because when our
  3259.     //  output pin is connected we'll reconnect this pin
  3260.     if not FTIPFilter.OutputPin.IsConnected then
  3261.       begin
  3262.         result := inherited NotifyAllocator(Allocator, ReadOnly);
  3263.         exit;
  3264.       end;
  3265.     //  If the allocator is read-only and we're modifying data
  3266.     //  and the allocator is the same as the output pin's
  3267.     //  then reject
  3268.     if (FReadOnly and FTIPFilter.FModifiesData) then
  3269.       begin
  3270.         OutputAllocator := FTIPFilter.OutputPin.PeekAllocator;
  3271.         //  Make sure we have an output allocator
  3272.         if (OutputAllocator = nil) then
  3273.         begin
  3274.           result := FTIPFilter.OutputPin.ConnectedIMemInputPin.GetAllocator(OutputAllocator);
  3275.           if FAILED(result) then result := CreateMemoryAllocator(OutputAllocator);
  3276.           if SUCCEEDED(result) then
  3277.             begin
  3278.               FTIPFilter.OutputPin.SetAllocator(OutputAllocator);
  3279.               OutputAllocator := nil;
  3280.             end;
  3281.         end;
  3282.         if (Allocator = OutputAllocator) then
  3283.           begin
  3284.             result := E_FAIL;
  3285.             exit;
  3286.           end
  3287.         else
  3288.           if SUCCEEDED(result) then
  3289.           begin
  3290.             //  Must copy so set the allocator properties on the output
  3291.             result := Allocator.GetProperties(Props);
  3292.             if SUCCEEDED(result) then
  3293.                result := OutputAllocator.SetProperties(Props, Actual);
  3294.             if SUCCEEDED(result) then
  3295.             begin
  3296.               if ((Props.cBuffers > Actual.cBuffers)
  3297.                   or (Props.cbBuffer > Actual.cbBuffer)
  3298.                   or (Props.cbAlign  > Actual.cbAlign)) then
  3299.                 result :=  E_FAIL;
  3300.             end;
  3301.             //  Set the allocator on the output pin
  3302.             if SUCCEEDED(result) then
  3303.               result := FTIPFilter.OutputPin.ConnectedIMemInputPin.NotifyAllocator(OutputAllocator, FALSE);
  3304.           end;
  3305.       end
  3306.     else
  3307.       begin
  3308.         result := FTIPFilter.OutputPin.ConnectedIMemInputPin.NotifyAllocator(Allocator, ReadOnly);
  3309.         if SUCCEEDED(result) then  FTIPFilter.OutputPin.SetAllocator(Allocator);
  3310.       end;
  3311.     if SUCCEEDED(result) then
  3312.     begin
  3313.       // It's possible that the old and the new are the same thing.
  3314.       // AddRef before release ensures that we don't unload it.
  3315.       Allocator._AddRef;
  3316.       if (FAllocator <> nil) then FAllocator := nil;
  3317.       Pointer(FAllocator) := Pointer(Allocator);    // We have an allocator for the input pin
  3318.     end;
  3319.   finally
  3320.     FLock.UnLock;
  3321.   end;
  3322. end;
  3323. function TBCTransInPlaceInputPin.PeekAllocator: IMemAllocator;
  3324. begin
  3325.  result := FAllocator;
  3326. end;
  3327. constructor TBCTransInPlaceInputPin.Create(ObjectName: string;
  3328.   Filter: TBCTransInPlaceFilter; out hr: HRESULT; Name: WideString);
  3329. begin
  3330.   inherited Create(ObjectName, Filter, hr, Name);
  3331.   FReadOnly := FALSE;
  3332.   FTIPFilter := Filter;
  3333. {$IFDEF DEBUG}
  3334.   DbgLog(self, 'TBCTransInPlaceInputPin.Create');
  3335. {$ENDIF}
  3336. end;
  3337. { TBCTransInPlaceOutputPin }
  3338. function TBCTransInPlaceOutputPin.CheckMediaType(
  3339.   pmt: PAMMediaType): HRESULT;
  3340. begin
  3341.   // Don't accept any output pin type changes if we're copying
  3342.   // between allocators - it's too late to change the input
  3343.   // allocator size.
  3344.   if (FTIPFilter.UsingDifferentAllocators and (not FFilter.IsStopped)) then
  3345.   begin
  3346.     if TBCMediaType(pmt).Equal(@Fmt) then result := S_OK else result := VFW_E_TYPE_NOT_ACCEPTED;
  3347.     exit;
  3348.   end;
  3349.   // Assumes the type does not change.  That's why we're calling
  3350.   // CheckINPUTType here on the OUTPUT pin.
  3351.   result := FTIPFilter.CheckInputType(pmt);
  3352.   if (result <> S_OK) then exit;
  3353.   if (FTIPFilter.FInput.IsConnected) then
  3354.     result := FTIPFilter.FInput.GetConnected.QueryAccept(pmt^)
  3355.   else
  3356.     result := S_OK;
  3357. end;
  3358. function TBCTransInPlaceOutputPin.ConnectedIMemInputPin: IMemInputPin;
  3359. begin
  3360.   pointer(result) := pointer(FInputPin);
  3361. end;
  3362. constructor TBCTransInPlaceOutputPin.Create(ObjectName: string;
  3363.   Filter: TBCTransInPlaceFilter; out hr: HRESULT; Name: WideString);
  3364. begin
  3365.   inherited Create(ObjectName, Filter, hr, Name);
  3366.   FTIPFilter := Filter;
  3367. {$IFDEF DEBUG}
  3368.   DbgLog(self, 'TBCTransInPlaceOutputPin.Create');
  3369. {$ENDIF}
  3370. end;
  3371. function TBCTransInPlaceOutputPin.EnumMediaTypes(
  3372.   out ppEnum: IEnumMediaTypes): HRESULT;
  3373. begin
  3374.   // Can only pass through if connected.
  3375.   if not FTIPFilter.FInput.IsConnected then
  3376.     result := VFW_E_NOT_CONNECTED
  3377.   else
  3378.     result := FTIPFilter.FInput.GetConnected.EnumMediaTypes(ppEnum);
  3379. end;
  3380. function TBCTransInPlaceOutputPin.PeekAllocator: IMemAllocator;
  3381. begin
  3382.   result := FAllocator;
  3383. end;
  3384. procedure TBCTransInPlaceOutputPin.SetAllocator(Allocator: IMemAllocator);
  3385. begin
  3386.     Allocator._AddRef;
  3387.     if(FAllocator <> nil) then  FAllocator._Release;