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

Delphi控件源码

开发平台:

Delphi

  1.     Pointer(FAllocator) := Pointer(Allocator);
  2. end;
  3. { TBCTransInPlaceFilter }
  4. function TBCTransInPlaceFilter.CheckTransform(mtIn,
  5.   mtOut: PAMMediaType): HRESULT;
  6. begin
  7.   result := S_OK;
  8. end;
  9. // dir is the direction of our pin.
  10. // pReceivePin is the pin we are connecting to.
  11. function TBCTransInPlaceFilter.CompleteConnect(dir: TPinDirection;
  12.   ReceivePin: IPin): HRESULT;
  13. var
  14.   pmt: PAMMediaType;
  15. begin
  16.   ASSERT(FInput <> nil);
  17.   ASSERT(FOutput <> nil);
  18.   // if we are not part of a graph, then don't indirect the pointer
  19.   // this probably prevents use of the filter without a filtergraph
  20.   if(FGraph = nil) then
  21.   begin
  22.     result := VFW_E_NOT_IN_GRAPH;
  23.     exit;
  24.   end;
  25.   // Always reconnect the input to account for buffering changes
  26.   //
  27.   // Because we don't get to suggest a type on ReceiveConnection
  28.   // we need another way of making sure the right type gets used.
  29.   //
  30.   // One way would be to have our EnumMediaTypes return our output
  31.   // connection type first but more deterministic and simple is to
  32.   // call ReconnectEx passing the type we want to reconnect with
  33.   // via the base class ReconeectPin method.
  34.   if(dir = PINDIR_OUTPUT) then
  35.   begin
  36.     if FInput.IsConnected then
  37.     begin
  38.       result := ReconnectPin(FInput, FOutput.AMMediaType);
  39.       exit;
  40.     end;
  41.     result := NOERROR;
  42.     exit;
  43.   end;
  44.   ASSERT(dir = PINDIR_INPUT);
  45.   // Reconnect output if necessary
  46.   if FOutput.IsConnected then
  47.   begin
  48.     pmt := FInput.CurrentMediaType.MediaType;
  49.     if (not TBCMediaType(pmt).Equal(FOutput.CurrentMediaType.MediaType)) then
  50.     begin
  51.       result := ReconnectPin(FOutput, FInput.CurrentMediaType.MediaType);
  52.       exit;
  53.     end;
  54.   end;
  55.   result := NOERROR;
  56. end;
  57. function TBCTransInPlaceFilter.Copy(Source: IMediaSample): IMediaSample;
  58. var
  59.   Start, Stop: TReferenceTime;
  60.   Time: boolean;
  61.   pStartTime, pEndTime: PReferenceTime;
  62.   TimeStart, TimeEnd: Int64;
  63.   Flags: DWORD;
  64.   Sample2: IMediaSample2;
  65.   props: PAMSample2Properties;
  66.   MediaType: PAMMediaType;
  67.   DataLength: LongInt;
  68.   SourceBuffer, DestBuffer: PByte;
  69.   SourceSize, DestSize: LongInt;
  70.   hr: hresult;
  71. begin
  72.     Time := (Source.GetTime(Start, Stop) = S_OK);
  73.     // this may block for an indeterminate amount of time
  74.     if Time then
  75.       begin
  76.         pStartTime := @Start;
  77.         pEndTime   := @Stop;
  78.       end
  79.     else
  80.       begin
  81.         pStartTime := nil;
  82.         pEndTime   := nil;
  83.       end;
  84.     if FSampleSkipped then Flags := AM_GBF_PREVFRAMESKIPPED else Flags := 0;
  85.     hr := OutputPin.PeekAllocator.GetBuffer(result, pStartTime, pEndTime, Flags);
  86.     if FAILED(hr) then exit;
  87.     ASSERT(result <> nil);
  88.     if(SUCCEEDED(result.QueryInterface(IID_IMediaSample2, Sample2))) then
  89.       begin
  90.         props :=  FInput.SampleProps;
  91.         hr := Sample2.SetProperties(SizeOf(TAMSample2Properties) - (4*2), props^);
  92.         Sample2 := nil;
  93.         if FAILED(hr) then
  94.         begin
  95.           result := nil;
  96.           exit;
  97.         end;
  98.       end
  99.     else
  100.       begin
  101.         if Time then result.SetTime(@Start, @Stop);
  102.         if (Source.IsSyncPoint = S_OK) then result.SetSyncPoint(True);
  103.         if ((Source.IsDiscontinuity = S_OK) or FSampleSkipped) then result.SetDiscontinuity(True);
  104.         if (Source.IsPreroll = S_OK) then result.SetPreroll(True);
  105.         // Copy the media type
  106.         if (Source.GetMediaType(MediaType) = S_OK) then
  107.           begin
  108.             result.SetMediaType(MediaType^);
  109.             DeleteMediaType(MediaType);
  110.           end;
  111.       end;
  112.     FSampleSkipped := FALSE;
  113.     // Copy the sample media times
  114.     if (Source.GetMediaTime(TimeStart, TimeEnd) = NOERROR) then
  115.       result.SetMediaTime(@TimeStart,@TimeEnd);
  116.     // Copy the actual data length and the actual data.
  117.     DataLength := Source.GetActualDataLength;
  118.     result.SetActualDataLength(DataLength);
  119.     // Copy the sample data
  120.     SourceSize := Source.GetSize;
  121.     DestSize   := result.GetSize;
  122.     // milenko start get rid of compiler warnings
  123.     if (DestSize < SourceSize) then
  124.     begin
  125.     end;
  126.     // milenko end
  127.     ASSERT(DestSize >= SourceSize, format('DestSize (%d) < SourceSize (%d)',[DestSize, SourceSize]));
  128.     ASSERT(DestSize >= DataLength);
  129.     Source.GetPointer(SourceBuffer);
  130.     result.GetPointer(DestBuffer);
  131.     ASSERT((DestSize = 0) or (SourceBuffer <> nil) and (DestBuffer <> nil));
  132.     CopyMemory(DestBuffer, SourceBuffer, DataLength);
  133. end;
  134. constructor TBCTransInPlaceFilter.Create(ObjectName: string;
  135.   unk: IUnKnown; clsid: TGUID; out hr: HRESULT; ModifiesData: boolean);
  136. begin
  137.   inherited create(ObjectName, Unk, clsid);
  138.   FModifiesData := ModifiesData;
  139. end;
  140. constructor TBCTransInPlaceFilter.CreateFromFactory(Factory: TBCClassFactory;
  141.   const Controller: IUnknown);
  142. begin
  143.   inherited create(FacTory.FName, Controller, FacTory.FClassID);
  144.   FModifiesData := True;
  145. end;
  146. // Tell the output pin's allocator what size buffers we require.
  147. // *pAlloc will be the allocator our output pin is using.
  148. function TBCTransInPlaceFilter.DecideBufferSize(Alloc: IMemAllocator;
  149.   propInputRequest: PAllocatorProperties): HRESULT;
  150. var Request, Actual: TAllocatorProperties;
  151. begin
  152.   // If we are connected upstream, get his views
  153.   if FInput.IsConnected then
  154.     begin
  155.       // Get the input pin allocator, and get its size and count.
  156.       // we don't care about his alignment and prefix.
  157.       result := InputPin.FAllocator.GetProperties(Request);
  158.       //Request.cbBuffer := 230400;
  159.       if FAILED(result) then exit; // Input connected but with a secretive allocator - enough!
  160.     end
  161.   else
  162.     begin
  163.       // We're reduced to blind guessing.  Let's guess one byte and if
  164.       // this isn't enough then when the other pin does get connected
  165.       // we can revise it.
  166.       ZeroMemory(@Request, sizeof(Request));
  167.       Request.cBuffers := 1;
  168.       Request.cbBuffer := 1;
  169.     end;
  170. {$IFDEF DEBUG}
  171.   DbgLog(self, 'Setting Allocator Requirements');
  172.   DbgLog(self, format('Count %d, Size %d',[Request.cBuffers, Request.cbBuffer]));
  173. {$ENDIF}
  174.   // Pass the allocator requirements to our output side
  175.   // but do a little sanity checking first or we'll just hit
  176.   // asserts in the allocator.
  177.   propInputRequest.cBuffers := Request.cBuffers;
  178.   propInputRequest.cbBuffer := Request.cbBuffer;
  179.   if (propInputRequest.cBuffers <= 0) then propInputRequest.cBuffers := 1;
  180.   if (propInputRequest.cbBuffer <= 0) then propInputRequest.cbBuffer := 1;
  181.   result := Alloc.SetProperties(propInputRequest^, Actual);
  182.   if FAILED(result) then exit;
  183. {$IFDEF DEBUG}
  184.   DbgLog(self, 'Obtained Allocator Requirements');
  185.   DbgLog(self, format('Count %d, Size %d, Alignment %d', [Actual.cBuffers, Actual.cbBuffer, Actual.cbAlign]));
  186. {$ENDIF}
  187.   // Make sure we got the right alignment and at least the minimum required
  188.   if ((Request.cBuffers > Actual.cBuffers)
  189.       or (Request.cbBuffer > Actual.cbBuffer)
  190.       or (Request.cbAlign  > Actual.cbAlign)) then
  191.     result := E_FAIL
  192.   else
  193.     result := NOERROR;
  194. end;
  195. function TBCTransInPlaceFilter.GetMediaType(Position: integer;
  196.   out MediaType: PAMMediaType): HRESULT;
  197. begin
  198. {$IFDEF DEBUG}
  199.   DbgLog(self, 'TBCTransInPlaceFilter.GetMediaType should never be called');
  200. {$ENDIF}
  201.   result := E_UNEXPECTED;
  202. end;
  203. // return a non-addrefed CBasePin * for the user to addref if he holds onto it
  204. // for longer than his pointer to us. We create the pins dynamically when they
  205. // are asked for rather than in the constructor. This is because we want to
  206. // give the derived class an oppportunity to return different pin objects
  207. // As soon as any pin is needed we create both (this is different from the
  208. // usual transform filter) because enumerators, allocators etc are passed
  209. // through from one pin to another and it becomes very painful if the other
  210. // pin isn't there.  If we fail to create either pin we ensure we fail both.
  211. function TBCTransInPlaceFilter.GetPin(n: integer): TBCBasePin;
  212. var hr: HRESULT;
  213. begin
  214.   hr := S_OK;
  215.   // Create an input pin if not already done
  216.   if(FInput = nil) then
  217.   begin
  218.     FInput := TBCTransInPlaceInputPin.Create('TransInPlace input pin',
  219.       self,      // Owner filter
  220.       hr,        // Result code
  221.       'Input');  // Pin name
  222.     // Constructor for CTransInPlaceInputPin can't fail
  223.     ASSERT(SUCCEEDED(hr));
  224.   end;
  225.   // Create an output pin if not already done
  226.   if((FInput <> nil) and (FOutput = nil)) then
  227.   begin
  228.     FOutput := TBCTransInPlaceOutputPin.Create('TransInPlace output pin',
  229.       self,      // Owner filter
  230.       hr,        // Result code
  231.       'Output'); // Pin name
  232.     // a failed return code should delete the object
  233.     ASSERT(SUCCEEDED(hr));
  234.       if(FOutput = nil) then
  235.       begin
  236.         FInput.Free;
  237.         FInput := nil;
  238.       end;
  239.   end;
  240.   // Return the appropriate pin
  241.   ASSERT(n in [0,1]);
  242.   case n of
  243.     0: result := FInput;
  244.     1: result := FOutput;
  245.   else
  246.     result := nil;
  247.   end;
  248. end;
  249. function TBCTransInPlaceFilter.InputPin: TBCTransInPlaceInputPin;
  250. begin
  251.   result := TBCTransInPlaceInputPin(FInput);
  252. end;
  253. function TBCTransInPlaceFilter.OutputPin: TBCTransInPlaceOutputPin;
  254. begin
  255.   result := TBCTransInPlaceOutputPin(FOutput);
  256. end;
  257. function TBCTransInPlaceFilter.Receive(Sample: IMediaSample): HRESULT;
  258. var Props: PAMSample2Properties;
  259. begin
  260.   //  Check for other streams and pass them on */
  261.   Props := FInput.SampleProps;
  262.   if (Props.dwStreamId <> AM_STREAM_MEDIA) then
  263.     begin
  264.       result := FOutput.Deliver(Sample);
  265.       exit;
  266.     end;
  267.   if UsingDifferentAllocators then
  268.   begin
  269.     // We have to copy the data.
  270.     Sample := Copy(Sample);
  271.     if (Sample = nil) then
  272.     begin
  273.       result := E_UNEXPECTED;
  274.       exit;
  275.     end;
  276.   end;
  277.   // have the derived class transform the data
  278.   result := Transform(Sample);
  279.   if FAILED(result) then
  280.   begin
  281.   {$IFDEF DEBUG}
  282.     DbgLog(self, 'Error from TransInPlace');
  283.   {$ENDIF}
  284.     if UsingDifferentAllocators then Sample := nil;
  285.     exit;
  286.   end;
  287.   // the Transform() function can return S_FALSE to indicate that the
  288.   // sample should not be delivered; we only deliver the sample if it's
  289.   // really S_OK (same as NOERROR, of course.)
  290.   if (result = NOERROR) then
  291.     result := FOutput.Deliver(Sample)
  292.   else
  293.     begin
  294.       //  But it would be an error to return this private workaround
  295.       //  to the caller ...
  296.       if (result = S_FALSE) then
  297.       begin
  298.         // S_FALSE returned from Transform is a PRIVATE agreement
  299.         // We should return NOERROR from Receive() in this cause because
  300.         // returning S_FALSE from Receive() means that this is the end
  301.         // of the stream and no more data should be sent.
  302.         FSampleSkipped := True;
  303.         if (not FQualityChanged) then
  304.         begin
  305.           NotifyEvent(EC_QUALITY_CHANGE,0,0);
  306.           FQualityChanged := True;
  307.         end;
  308.         result := NOERROR;
  309.       end;
  310.     end;
  311.   // release the output buffer. If the connected pin still needs it,
  312.   // it will have addrefed it itself.
  313.   if UsingDifferentAllocators then Sample := nil;
  314. end;
  315. function TBCTransInPlaceFilter.TypesMatch: boolean;
  316. var
  317.   pmt: PAMMediaType;
  318. begin
  319.   pmt := InputPin.CurrentMediaType.MediaType;
  320.   result := TBCMediaType(pmt).Equal(OutputPin.CurrentMediaType.MediaType);
  321. end;
  322. function TBCTransInPlaceFilter.UsingDifferentAllocators: boolean;
  323. begin
  324.   result := Pointer(InputPin.FAllocator) <> Pointer(OutputPin.FAllocator);
  325. end;
  326. { TBCBasePropertyPage }
  327. function TBCBasePropertyPage.Activate(hwndParent: HWnd; const rc: TRect;
  328.   bModal: BOOL): HResult;
  329. begin
  330.   // Return failure if SetObject has not been called.
  331.   if (FObjectSet = FALSE) or (hwndParent = 0) then
  332.     begin
  333.       result := E_UNEXPECTED;
  334.       exit;
  335.     end;
  336.    // FForm := TCustomFormClass(FFormClass).Create(nil);
  337.     if (FForm = nil) then
  338.       begin
  339.         result := E_OUTOFMEMORY;
  340.         exit;
  341.       end;
  342.     FForm.ParentWindow := hwndParent;
  343.     if assigned(FForm.OnActivate) then FForm.OnActivate(FForm);
  344.     Move(rc);
  345.     result := Show(SW_SHOWNORMAL);
  346. end;
  347. function TBCBasePropertyPage.Apply: HResult;
  348. begin
  349.   // In ActiveMovie 1.0 we used to check whether we had been activated or
  350.   // not. This is too constrictive. Apply should be allowed as long as
  351.   // SetObject was called to set an object. So we will no longer check to
  352.   // see if we have been activated (ie., m_hWnd != NULL), but instead
  353.   // make sure that m_bObjectSet is True (ie., SetObject has been called).
  354.   if (FObjectSet = FALSE) or (FPageSite = nil) then
  355.   begin
  356.     result := E_UNEXPECTED;
  357.     exit;
  358.   end;
  359.   if (FDirty = FALSE) then
  360.   begin
  361.     result := NOERROR;
  362.     exit;
  363.   end;
  364.   // Commit derived class changes
  365.   result := FForm.OnApplyChanges;
  366.   if SUCCEEDED(result) then FDirty := FALSE;
  367. end;
  368. function TBCBasePropertyPage.Deactivate: HResult;
  369. var Style: DWORD;
  370. begin
  371.     if (FForm = nil) then
  372.     begin
  373.       result := E_UNEXPECTED;
  374.       exit;
  375.     end;
  376.     // Remove WS_EX_CONTROLPARENT before DestroyWindow call
  377.     Style := GetWindowLong(FForm.Handle, GWL_EXSTYLE);
  378.     Style := Style and (not WS_EX_CONTROLPARENT);
  379.     //  Set m_hwnd to be NULL temporarily so the message handler
  380.     //  for WM_STYLECHANGING doesn't add the WS_EX_CONTROLPARENT
  381.     //  style back in
  382.     SetWindowLong(FForm.Handle, GWL_EXSTYLE, Style);
  383.     if assigned(FForm.OnDeactivate) then FForm.OnDeactivate(FForm);
  384.     // Destroy the dialog window
  385.     //FForm.Free;
  386.     //FForm := nil;
  387.     result := NOERROR;
  388. end;
  389. function TBCBasePropertyPage.GetPageInfo(out pageInfo: TPropPageInfo): HResult;
  390. begin
  391.   pageInfo.cb := sizeof(TPropPageInfo);
  392.   AMGetWideString(FForm.Caption, pageInfo.pszTitle);
  393.   PageInfo.pszDocString := nil;
  394.   PageInfo.pszHelpFile  := nil;
  395.   PageInfo.dwHelpContext:= 0;
  396.   PageInfo.size.cx := FForm.width;
  397.   PageInfo.size.cy := FForm.Height;
  398.   Result := NoError;
  399. end;
  400. function TBCBasePropertyPage.Help(pszHelpDir: POleStr): HResult;
  401. begin
  402.   result := E_NOTIMPL;
  403. end;
  404. function TBCBasePropertyPage.IsPageDirty: HResult;
  405. begin
  406.   if FDirty then result := S_OK else result := S_FALSE; 
  407. end;
  408. function TBCBasePropertyPage.Move(const rect: TRect): HResult;
  409. begin
  410.   if (FForm = nil) then
  411.   begin
  412.     result := E_UNEXPECTED;
  413.     exit;
  414.   end;
  415.   MoveWindow(FForm.Handle,             // Property page handle
  416.                Rect.left,              // x coordinate
  417.                Rect.top,               // y coordinate
  418.                Rect.Right - Rect.Left, // Overall window width
  419.                Rect.Bottom - Rect.Top, // And likewise height
  420.                True);                  // Should we repaint it
  421.   result := NOERROR;
  422. end;
  423. function TBCBasePropertyPage.SetObjects(cObjects: Integer;
  424.   pUnkList: PUnknownList): HResult;
  425. begin
  426.   if (cObjects = 1) then
  427.     begin
  428.       if (pUnkList = nil) then
  429.       begin
  430.         result := E_POINTER;
  431.         exit;
  432.       end;
  433.       // Set a flag to say that we have set the Object
  434.       FObjectSet := True ;
  435.       result := FForm.OnConnect(pUnkList^[0]);
  436.       exit;
  437.      end
  438.    else
  439.      if (cObjects = 0) then
  440.      begin
  441.        // Set a flag to say that we have not set the Object for the page
  442.        FObjectSet := FALSE;
  443.        result := FForm.OnDisconnect;
  444.        exit;
  445.      end;
  446.   {$IFDEF DEBUG}
  447.     DbgLog(self, 'No support for more than one object');
  448.   {$ENDIF}
  449.     result := E_UNEXPECTED;
  450. end;
  451. function TBCBasePropertyPage.SetPageSite(
  452.   const pageSite: IPropertyPageSite): HResult;
  453. begin
  454.   if (pageSite <> nil) then
  455.     begin
  456.       if (FPageSite <> nil) then
  457.       begin
  458.         result := E_UNEXPECTED;
  459.         exit;
  460.       end;
  461.       FPageSite := pageSite;
  462.     end
  463.   else
  464.     begin
  465.       if (FPageSite = nil) then
  466.       begin
  467.         result := E_UNEXPECTED;
  468.         exit;
  469.       end;
  470.       FPageSite := nil;
  471.     end;
  472.   result := NOERROR;
  473. end;
  474. function TBCBasePropertyPage.Show(nCmdShow: Integer): HResult;
  475. begin
  476.   if (FForm = nil) then
  477.   begin
  478.     result := E_UNEXPECTED;
  479.     exit;
  480.   end;
  481.   if ((nCmdShow <> SW_SHOW) and (nCmdShow <> SW_SHOWNORMAL) and (nCmdShow <> SW_HIDE)) then
  482.     begin
  483.       result := E_INVALIDARG;
  484.       exit;
  485.     end;
  486.     if nCmdShow in [SW_SHOW,SW_SHOWNORMAL] then FForm.Show else FForm.Hide;
  487.     InvalidateRect(FForm.Handle, nil, True);
  488.     result := NOERROR;
  489. end;
  490. function TBCBasePropertyPage.TranslateAccelerator(msg: PMsg): HResult;
  491. begin
  492.   result := E_NOTIMPL;
  493. end;
  494. constructor TBCBasePropertyPage.Create(Name: String; Unk: IUnKnown; Form: TFormPropertyPage);
  495. begin
  496.   inherited Create(Name, Unk);
  497.   FForm := Form;
  498.   FForm.BorderStyle := bsNone;
  499.   FPageSite  := nil;
  500.   FObjectSet := false;
  501.   FDirty     := false;
  502. end;
  503. destructor TBCBasePropertyPage.Destroy;
  504. begin
  505.   if FForm <> nil then
  506.     begin
  507.       FForm.Free;
  508.       FForm := nil;
  509.     end;
  510.   inherited;
  511. end;
  512. constructor TFormPropertyPage.Create(AOwner: TComponent);
  513. begin
  514.   inherited Create(AOwner);
  515.   WindowProc := MyWndProc;
  516. end;
  517. procedure TFormPropertyPage.MyWndProc(var aMsg: TMessage);
  518. var
  519.   lpss : PStyleStruct;
  520. begin
  521.   // we would like the TAB key to move around the tab stops in our property
  522.   // page, but for some reason OleCreatePropertyFrame clears the CONTROLPARENT
  523.   // style behind our back, so we need to switch it back on now behind its
  524.   // back.  Otherwise the tab key will be useless in every page.
  525.   // DCoder: removing CONTROLPARENT is also the reason for non responding
  526.   // PropertyPages when using ShowMessage and TComboBox.
  527.   if (aMsg.Msg = WM_STYLECHANGING) and (aMsg.WParam = GWL_EXSTYLE) then
  528.   begin
  529.     lpss := PStyleStruct(aMsg.LParam);
  530.     lpss.styleNew := lpss.styleNew or WS_EX_CONTROLPARENT;
  531.     aMsg.Result := 0;
  532.     Exit;
  533.   end;
  534.   WndProc(aMsg);
  535. end;
  536. function TFormPropertyPage.OnApplyChanges: HRESULT;
  537. begin
  538.   result := NOERROR;
  539. end;
  540. function TFormPropertyPage.OnConnect(Unknown: IUnKnown): HRESULT;
  541. begin
  542.   result := NOERROR;
  543. end;
  544. function TFormPropertyPage.OnDisconnect: HRESULT;
  545. begin
  546.   result := NOERROR;
  547. end;
  548. procedure TBCBasePropertyPage.SetPageDirty;
  549. begin
  550.   FDirty := True;
  551.   if Assigned(FPageSite) then FPageSite.OnStatusChange(PROPPAGESTATUS_DIRTY);
  552. end;
  553. { TBCBaseDispatch }
  554. function TBCBaseDispatch.GetIDsOfNames(const IID: TGUID; Names: Pointer;
  555.   NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
  556. var ti: ITypeInfo;
  557. begin
  558.   // although the IDispatch riid is dead, we use this to pass from
  559.   // the interface implementation class to us the iid we are talking about.
  560.   result := GetTypeInfo(iid, 0, LocaleID, ti);
  561.   if SUCCEEDED(result) then
  562.     result := ti.GetIDsOfNames(Names, NameCount, DispIDs);
  563. end;
  564. function TBCBaseDispatch.GetTypeInfo(const iid: TGUID; info: Cardinal; lcid: LCID;
  565.   out tinfo): HRESULT; stdcall;
  566. var
  567.   tlib : ITypeLib;
  568. begin
  569.   // we only support one type element
  570.   if (info <> 0) then
  571.     begin
  572.       result := TYPE_E_ELEMENTNOTFOUND;
  573.       exit;
  574.     end;
  575.   // always look for neutral
  576.   if (FTI = nil) then
  577.   begin
  578.     result := LoadRegTypeLib(LIBID_QuartzTypeLib, 1, 0, lcid, tlib);
  579.     if FAILED(result) then
  580.     begin
  581.       result := LoadTypeLib('control.tlb', tlib);
  582.       if FAILED(result) then exit;
  583.     end;
  584.     result := tlib.GetTypeInfoOfGuid(iid, Fti);
  585.     tlib := nil;
  586.     if FAILED(result) then exit;
  587.   end;
  588.   ITypeInfo(tinfo) := Fti;
  589.   result := S_OK;
  590. end;
  591. function TBCBaseDispatch.GetTypeInfoCount(out Count: Integer): HResult;
  592. begin
  593.   count := 1;
  594.   result := S_OK;
  595. end;
  596. { TBCMediaControl }
  597. constructor TBCMediaControl.Create(name: string; unk: IUnknown);
  598. begin
  599.   FBaseDisp := TBCBaseDispatch.Create;
  600. end;
  601. destructor TBCMediaControl.Destroy;
  602. begin
  603.   FBaseDisp.Free;
  604.   inherited;
  605. end;
  606. function TBCMediaControl.GetIDsOfNames(const IID: TGUID; Names: Pointer;
  607.   NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
  608. begin
  609.   result := FBasedisp.GetIDsOfNames(IID_IMediaControl, Names, NameCount, LocaleID, DispIDs);
  610. end;
  611. function TBCMediaControl.GetTypeInfo(Index, LocaleID: Integer;
  612.   out TypeInfo): HResult;
  613. begin
  614.   result := Fbasedisp.GetTypeInfo(IID_IMediaControl, index, LocaleID, TypeInfo);
  615. end;
  616. function TBCMediaControl.GetTypeInfoCount(out Count: Integer): HResult;
  617. begin
  618.   result := FBaseDisp.GetTypeInfoCount(Count);
  619. end;
  620. function TBCMediaControl.Invoke(DispID: Integer; const IID: TGUID;
  621.   LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  622.   ArgErr: Pointer): HResult;
  623. var ti: ITypeInfo;
  624. begin
  625.   // this parameter is a dead leftover from an earlier interface
  626.   if not IsEqualGUID(GUID_NULL, IID) then
  627.     begin
  628.       result := DISP_E_UNKNOWNINTERFACE;
  629.       exit;
  630.     end;
  631.   result := GetTypeInfo(0, LocaleID, ti);
  632.   if FAILED(result) then exit;
  633.   result := ti.Invoke(Pointer(Integer(Self)), DISPID, Flags, TDispParams(Params),
  634.     VarResult, ExcepInfo, ArgErr);
  635. end;
  636. { TBCMediaEvent }
  637. constructor TBCMediaEvent.Create(Name: string; Unk: IUnknown);
  638. begin
  639.   inherited Create(name, Unk);
  640.   FBasedisp := TBCBaseDispatch.Create;
  641. end;
  642. destructor TBCMediaEvent.destroy;
  643. begin
  644.   FBasedisp.Free;
  645.   inherited;
  646. end;
  647. function TBCMediaEvent.GetIDsOfNames(const IID: TGUID; Names: Pointer;
  648.   NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
  649. begin
  650.   result := FBasedisp.GetIDsOfNames(IID_IMediaEvent, Names, NameCount, LocaleID, DispIDs);
  651. end;
  652. function TBCMediaEvent.GetTypeInfo(Index, LocaleID: Integer;
  653.   out TypeInfo): HResult;
  654. begin
  655.   result := Fbasedisp.GetTypeInfo(IID_IMediaEvent, index, LocaleID, TypeInfo);
  656. end;
  657. function TBCMediaEvent.GetTypeInfoCount(out Count: Integer): HResult;
  658. begin
  659.   result := FBaseDisp.GetTypeInfoCount(Count);
  660. end;
  661. function TBCMediaEvent.Invoke(DispID: Integer; const IID: TGUID;
  662.   LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  663.   ArgErr: Pointer): HResult;
  664. var ti: ITypeInfo;
  665. begin
  666.   // this parameter is a dead leftover from an earlier interface
  667.   if not IsEqualGUID(GUID_NULL, IID) then
  668.     begin
  669.       result := DISP_E_UNKNOWNINTERFACE;
  670.       exit;
  671.     end;
  672.   result := GetTypeInfo(0, LocaleID, ti);
  673.   if FAILED(result) then exit;
  674.   result := ti.Invoke(Pointer(Integer(Self)), DISPID, Flags, TDispParams(Params), VarResult, ExcepInfo, ArgErr);
  675. end;
  676. { TBCMediaPosition }
  677. constructor TBCMediaPosition.Create(Name: String; Unk: IUnknown);
  678. begin
  679.   inherited Create(Name, Unk);
  680.   FBaseDisp := TBCBaseDispatch.Create;
  681. end;
  682. constructor TBCMediaPosition.Create(Name: String; Unk: IUnknown;
  683.   out hr: HRESULT);
  684. begin
  685.   inherited Create(Name, Unk);
  686.   FBaseDisp := TBCBaseDispatch.Create;
  687. end;
  688. destructor TBCMediaPosition.Destroy;
  689. begin
  690.   FBaseDisp.Free;
  691.   inherited;
  692. end;
  693. function TBCMediaPosition.GetIDsOfNames(const IID: TGUID; Names: Pointer;
  694.   NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
  695. begin
  696.   result := FBasedisp.GetIDsOfNames(IID_IMediaPosition, Names, NameCount, LocaleID, DispIDs);
  697. end;
  698. function TBCMediaPosition.GetTypeInfo(Index, LocaleID: Integer;
  699.   out TypeInfo): HResult;
  700. begin
  701.   result := Fbasedisp.GetTypeInfo(IID_IMediaPosition, index, LocaleID, TypeInfo);
  702. end;
  703. function TBCMediaPosition.GetTypeInfoCount(out Count: Integer): HResult;
  704. begin
  705.   result := Fbasedisp.GetTypeInfoCount(Count);
  706. end;
  707. function TBCMediaPosition.Invoke(DispID: Integer; const IID: TGUID;
  708.   LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  709.   ArgErr: Pointer): HResult;
  710. var ti: ITypeInfo;
  711. begin
  712.   // this parameter is a dead leftover from an earlier interface
  713.   if not IsEqualGUID(GUID_NULL, IID) then
  714.     begin
  715.       result := DISP_E_UNKNOWNINTERFACE;
  716.       exit;
  717.     end;
  718.   result := GetTypeInfo(0, LocaleID, ti);
  719.   if FAILED(result) then exit;
  720.   result := ti.Invoke(Pointer(Integer(Self)), DISPID, Flags, TDispParams(Params), VarResult, ExcepInfo, ArgErr);
  721. end;
  722. { TBCPosPassThru }
  723. function TBCPosPassThru.CanSeekBackward(
  724.   out pCanSeekBackward: Integer): HResult;
  725. var MP: IMediaPosition;
  726. begin
  727.   result := GetPeer(MP);
  728.   if FAILED(result) then exit;
  729.   result := MP.CanSeekBackward(pCanSeekBackward);
  730. end;
  731. function TBCPosPassThru.CanSeekForward(
  732.   out pCanSeekForward: Integer): HResult;
  733. var MP: IMediaPosition;
  734. begin
  735.   result := GetPeer(MP);
  736.   if FAILED(result) then exit;
  737.   result := MP.CanSeekForward(pCanSeekForward);
  738. end;
  739. function TBCPosPassThru.CheckCapabilities(
  740.   var pCapabilities: DWORD): HRESULT;
  741. var
  742.   MS: IMediaSeeking;
  743. begin
  744.   result := GetPeerSeeking(MS);
  745.   if FAILED(result) then exit;
  746.   result := MS.CheckCapabilities(pCapabilities);
  747. end;
  748. function TBCPosPassThru.ConvertTimeFormat(out pTarget: int64;
  749.   pTargetFormat: PGUID; Source: int64; pSourceFormat: PGUID): HRESULT;
  750. var MS: IMediaSeeking;
  751. begin
  752.   result := GetPeerSeeking(MS);
  753.   if FAILED(result) then exit;
  754.   result := MS.ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat);
  755. end;
  756. constructor TBCPosPassThru.Create(name: String; Unk: IUnknown;
  757.   out hr: HRESULT; Pin: IPin);
  758. begin
  759.   assert(Pin <> nil);
  760.   inherited Create(Name,Unk);
  761.   FPin := Pin;
  762. end;
  763. function TBCPosPassThru.ForceRefresh: HRESULT;
  764. begin
  765.   result := S_OK;
  766. end;
  767. function TBCPosPassThru.get_CurrentPosition(
  768.   out pllTime: TRefTime): HResult;
  769. var MP: IMediaPosition;
  770. begin
  771.   result := GetPeer(MP);
  772.   if FAILED(result) then exit;
  773.   result := MP.get_CurrentPosition(pllTime);
  774. end;
  775. function TBCPosPassThru.get_Duration(out plength: TRefTime): HResult;
  776. var MP: IMediaPosition;
  777. begin
  778.   result := GetPeer(MP);
  779.   if FAILED(result) then exit;
  780.   result := MP.get_Duration(plength);
  781. end;
  782. function TBCPosPassThru.get_PrerollTime(out pllTime: TRefTime): HResult;
  783. var MP: IMediaPosition;
  784. begin
  785.   result := GetPeer(MP);
  786.   if FAILED(result) then exit;
  787.   result := MP.get_PrerollTime(pllTime);
  788. end;
  789. function TBCPosPassThru.get_Rate(out pdRate: double): HResult;
  790. var MP: IMediaPosition;
  791. begin
  792.   result := GetPeer(MP);
  793.   if FAILED(result) then exit;
  794.   result := MP.get_Rate(pdRate);
  795. end;
  796. function TBCPosPassThru.get_StopTime(out pllTime: TRefTime): HResult;
  797. var MP: IMediaPosition;
  798. begin
  799.   result := GetPeer(MP);
  800.   if FAILED(result) then exit;
  801.   result := MP.get_StopTime(pllTime);
  802. end;
  803. function TBCPosPassThru.GetAvailable(out pEarliest,
  804.   pLatest: int64): HRESULT;
  805. var MS: IMediaSeeking;
  806. begin
  807.   result := GetPeerSeeking(MS);
  808.   if FAILED(result) then exit;
  809.   result := MS.GetAvailable(pEarliest, pLatest);
  810. end;
  811. function TBCPosPassThru.GetCapabilities(out pCapabilities: DWORD): HRESULT;
  812. var MS: IMediaSeeking;
  813. begin
  814.   result := GetPeerSeeking(MS);
  815.   if FAILED(result) then exit;
  816.   result := MS.GetCapabilities(pCapabilities);
  817. end;
  818. function TBCPosPassThru.GetCurrentPosition(out pCurrent: int64): HRESULT;
  819. var
  820.   MS: IMediaSeeking;
  821.   Stop: int64;
  822. begin
  823.   result := GetMediaTime(pCurrent, Stop);
  824.   if SUCCEEDED(result) then
  825.     result := NOERROR
  826.   else
  827.     begin
  828.       result := GetPeerSeeking(MS);
  829.       if FAILED(result) then exit;
  830.       result := MS.GetCurrentPosition(pCurrent)
  831.     end;
  832. end;
  833. function TBCPosPassThru.GetDuration(out pDuration: int64): HRESULT;
  834. var MS: IMediaSeeking;
  835. begin
  836.   result := GetPeerSeeking(MS);
  837.   if FAILED(result) then exit;
  838.   result := MS.GetDuration(pDuration);
  839. end;
  840. function TBCPosPassThru.GetMediaTime(out StartTime,
  841.   EndTime: Int64): HRESULT;
  842. begin
  843.   result := E_FAIL;
  844. end;
  845. // Return the IMediaPosition interface from our peer
  846. function TBCPosPassThru.GetPeer(out MP: IMediaPosition): HRESULT;
  847. var
  848.   Connected: IPin;
  849. begin
  850.   result := FPin.ConnectedTo(Connected);
  851.   if FAILED(result) then
  852.     begin
  853.       result := E_NOTIMPL;
  854.       exit;
  855.     end;
  856.   result := Connected.QueryInterface(IID_IMediaPosition, MP);
  857.   Connected := nil;
  858.   if FAILED(result) then
  859.     begin
  860.       result := E_NOTIMPL;
  861.       exit;
  862.     end;
  863.   result := S_OK;
  864. end;
  865. function TBCPosPassThru.GetPeerSeeking(out MS: IMediaSeeking): HRESULT;
  866. var
  867.   Connected: IPin;
  868. begin
  869.   MS := nil;
  870.   result := FPin.ConnectedTo(Connected);
  871.   if FAILED(result) then
  872.     begin
  873.       result := E_NOTIMPL;
  874.       exit;
  875.     end;
  876.   result := Connected.QueryInterface(IID_IMediaSeeking, MS);
  877.   Connected := nil;
  878.   if FAILED(result) then
  879.     begin
  880.       result := E_NOTIMPL;
  881.       exit;
  882.     end;
  883.   result := S_OK;
  884. end;
  885. function TBCPosPassThru.GetPositions(out pCurrent, pStop: int64): HRESULT;
  886. var MS: IMediaSeeking;
  887. begin
  888.   result := GetPeerSeeking(MS);
  889.   if FAILED(result) then exit;
  890.   result := MS.GetPositions(pCurrent, pStop);
  891. end;
  892. function TBCPosPassThru.GetPreroll(out pllPreroll: int64): HRESULT;
  893. var MS: IMediaSeeking;
  894. begin
  895.   result := GetPeerSeeking(MS);
  896.   if FAILED(result) then exit;
  897.   result := MS.GetPreroll(pllPreroll);
  898. end;
  899. function TBCPosPassThru.GetRate(out pdRate: double): HRESULT;
  900. var MS: IMediaSeeking;
  901. begin
  902.   result := GetPeerSeeking(MS);
  903.   if FAILED(result) then exit;
  904.   result := MS.GetRate(pdRate);
  905. end;
  906. function TBCPosPassThru.GetStopPosition(out pStop: int64): HRESULT;
  907. var MS: IMediaSeeking;
  908. begin
  909.   result := GetPeerSeeking(MS);
  910.   if FAILED(result) then exit;
  911.   result := MS.GetStopPosition(pStop);
  912. end;
  913. function TBCPosPassThru.GetTimeFormat(out pFormat: TGUID): HRESULT;
  914. var MS: IMediaSeeking;
  915. begin
  916.   result := GetPeerSeeking(MS);
  917.   if FAILED(result) then exit;
  918.   result := MS.GetTimeFormat(pFormat);
  919. end;
  920. function TBCPosPassThru.IsFormatSupported(const pFormat: TGUID): HRESULT;
  921. var MS: IMediaSeeking;
  922. begin
  923.   result := GetPeerSeeking(MS);
  924.   if FAILED(result) then exit;
  925.   result := MS.IsFormatSupported(pFormat);
  926. end;
  927. function TBCPosPassThru.IsUsingTimeFormat(const pFormat: TGUID): HRESULT;
  928. var MS: IMediaSeeking;
  929. begin
  930.   result := GetPeerSeeking(MS);
  931.   if FAILED(result) then exit;
  932.   result := MS.IsUsingTimeFormat(pFormat);
  933. end;
  934. function TBCPosPassThru.put_CurrentPosition(llTime: TRefTime): HResult;
  935. var MP: IMediaPosition;
  936. begin
  937.   result := GetPeer(MP);
  938.   if FAILED(result) then exit;
  939.   result := MP.put_CurrentPosition(llTime);
  940. end;
  941. function TBCPosPassThru.put_PrerollTime(llTime: TRefTime): HResult;
  942. var MP: IMediaPosition;
  943. begin
  944.   result := GetPeer(MP);
  945.   if FAILED(result) then exit;
  946.   result := MP.put_PrerollTime(llTime);
  947. end;
  948. function TBCPosPassThru.put_Rate(dRate: double): HResult;
  949. var MP: IMediaPosition;
  950. begin
  951.   if (dRate = 0.0) then
  952.     begin
  953.       result := E_INVALIDARG;
  954.       exit;
  955.     end;
  956.   result := GetPeer(MP);
  957.   if FAILED(result) then exit;
  958.   result := MP.put_Rate(dRate);
  959. end;
  960. function TBCPosPassThru.put_StopTime(llTime: TRefTime): HResult;
  961. var MP: IMediaPosition;
  962. begin
  963.   result := GetPeer(MP);
  964.   if FAILED(result) then exit;
  965.   result := MP.put_StopTime(llTime);
  966. end;
  967. function TBCPosPassThru.QueryPreferredFormat(out pFormat: TGUID): HRESULT;
  968. var MS: IMediaSeeking;
  969. begin
  970.   result := GetPeerSeeking(MS);
  971.   if FAILED(result) then exit;
  972.   result := MS.QueryPreferredFormat(pFormat);
  973. end;
  974. function TBCPosPassThru.SetPositions(var pCurrent: int64;
  975.   dwCurrentFlags: DWORD; var pStop: int64; dwStopFlags: DWORD): HRESULT;
  976. var MS: IMediaSeeking;
  977. begin
  978.   result := GetPeerSeeking(MS);
  979.   if FAILED(result) then exit;
  980.   result := MS.SetPositions(pCurrent, dwCurrentFlags, pStop, dwStopFlags);
  981. end;
  982. function TBCPosPassThru.SetRate(dRate: double): HRESULT;
  983. var MS: IMediaSeeking;
  984. begin
  985.   if (dRate = 0.0) then
  986.     begin
  987.       result := E_INVALIDARG;
  988.       exit;
  989.     end;
  990.   result := GetPeerSeeking(MS);
  991.   if FAILED(result) then exit;
  992.   result := MS.SetRate(dRate);
  993. end;
  994. function TBCPosPassThru.SetTimeFormat(const pFormat: TGUID): HRESULT;
  995. var MS: IMediaSeeking;
  996. begin
  997.   result := GetPeerSeeking(MS);
  998.   if FAILED(result) then exit;
  999.   result := MS.SetTimeFormat(pFormat);
  1000. end;
  1001. { TBCRendererPosPassThru }
  1002. // Media times (eg current frame, field, sample etc) are passed through the
  1003. // filtergraph in media samples. When a renderer gets a sample with media
  1004. // times in it, it will call one of the RegisterMediaTime methods we expose
  1005. // (one takes an IMediaSample, the other takes the media times direct). We
  1006. // store the media times internally and return them in GetCurrentPosition.
  1007. constructor TBCRendererPosPassThru.Create(name: String; Unk: IUnknown;
  1008.   out hr: HRESULT; Pin: IPin);
  1009. begin
  1010.     inherited Create(Name,Unk,hr,Pin);
  1011.     FStartMedia:= 0;
  1012.     FEndMedia  := 0;
  1013.     FReset     := True;
  1014.     FPositionLock := TBCCritSec.Create;
  1015. end;
  1016. destructor TBCRendererPosPassThru.destroy;
  1017. begin
  1018.   FPositionLock.Free;
  1019.   inherited;
  1020. end;
  1021. // Intended to be called by the owing filter during EOS processing so
  1022. // that the media times can be adjusted to the stop time.  This ensures
  1023. // that the GetCurrentPosition will actully get to the stop position.
  1024. function TBCRendererPosPassThru.EOS: HRESULT;
  1025. var Stop: int64;
  1026. begin
  1027.   if FReset then result := E_FAIL
  1028.   else
  1029.     begin
  1030.       result := GetStopPosition(Stop);
  1031.       if SUCCEEDED(result) then
  1032.         begin
  1033.           FPositionLock.Lock;
  1034.           try
  1035.             FStartMedia := Stop;
  1036.             FEndMedia   := Stop;
  1037.           finally
  1038.             FPositionLock.UnLock;
  1039.           end;
  1040.         end;
  1041.     end;
  1042. end;
  1043. function TBCRendererPosPassThru.GetMediaTime(out StartTime,
  1044.   EndTime: int64): HRESULT;
  1045. begin
  1046.   FPositionLock.Lock;
  1047.   try
  1048.     if FReset then
  1049.       begin
  1050.         result := E_FAIL;
  1051.         exit;
  1052.       end;
  1053.     // We don't have to return the end time
  1054.     result := ConvertTimeFormat(StartTime, nil, FStartMedia, @TIME_FORMAT_MEDIA_TIME);
  1055.     if SUCCEEDED(result) then
  1056.       result := ConvertTimeFormat(EndTime, nil, FEndMedia, @TIME_FORMAT_MEDIA_TIME);
  1057.   finally
  1058.     FPositionLock.UnLock;
  1059.   end;
  1060. end;
  1061. // Sets the media times the object should report
  1062. function TBCRendererPosPassThru.RegisterMediaTime(
  1063.   MediaSample: IMediaSample): HRESULT;
  1064. var  StartMedia, EndMedia: TReferenceTime;
  1065. begin
  1066.   ASSERT(assigned(MediaSample));
  1067.   FPositionLock.Lock;
  1068.   try
  1069.     // Get the media times from the sample
  1070.     result := MediaSample.GetTime(StartMedia, EndMedia);
  1071.     if FAILED(result) then
  1072.       begin
  1073.         ASSERT(result = VFW_E_SAMPLE_TIME_NOT_SET);
  1074.         exit;
  1075.       end;
  1076.     FStartMedia := StartMedia;
  1077.     FEndMedia   := EndMedia;
  1078.     FReset      := FALSE;
  1079.     result := NOERROR;
  1080.   finally
  1081.     FPositionLock.Unlock;
  1082.   end;
  1083. end;
  1084. // Sets the media times the object should report
  1085. function TBCRendererPosPassThru.RegisterMediaTime(StartTime,
  1086.   EndTime: int64): HRESULT;
  1087. begin
  1088.   FPositionLock.Lock;
  1089.   try
  1090.     FStartMedia := StartTime;
  1091.     FEndMedia   := EndTime;
  1092.     FReset      := FALSE;
  1093.     result      := NOERROR;
  1094.   finally
  1095.     FPositionLock.UnLock;
  1096.   end;
  1097. end;
  1098. // Resets the media times we hold
  1099. function TBCRendererPosPassThru.ResetMediaTime: HRESULT;
  1100. begin
  1101.   FPositionLock.Lock;
  1102.   try
  1103.     FStartMedia := 0;
  1104.     FEndMedia   := 0;
  1105.     FReset      := True;
  1106.     result      := NOERROR;
  1107.   finally
  1108.     FPositionLock.UnLock;
  1109.   end;
  1110. end;
  1111. { TBCAMEvent }
  1112. function TBCAMEvent.Check: boolean;
  1113. begin
  1114.   result := Wait(0); 
  1115. end;
  1116. constructor TBCAMEvent.Create(ManualReset: boolean);
  1117. begin
  1118.   FEvent := CreateEvent(nil, ManualReset, FALSE, nil);
  1119. end;
  1120. destructor TBCAMEvent.destroy;
  1121. begin
  1122.   if FEvent <> 0 then
  1123.     Assert(CloseHandle(FEvent));
  1124.   inherited;
  1125. end;
  1126. procedure TBCAMEvent.Reset;
  1127. begin
  1128.   ResetEvent(FEvent);
  1129. end;
  1130. procedure TBCAMEvent.SetEv;
  1131. begin
  1132.   SetEvent(FEvent);
  1133. end;
  1134. function TBCAMEvent.Wait(Timeout: Cardinal): boolean;
  1135. begin
  1136.   result := (WaitForSingleObject(FEvent, Timeout) = WAIT_OBJECT_0);
  1137. end;
  1138. { TBCRenderedInputPin }
  1139. function TBCRenderedInputPin.Active: HRESULT;
  1140. begin
  1141.   FAtEndOfStream := FALSE;
  1142.   FCompleteNotified := FALSE;
  1143.   result := inherited Active;
  1144. end;
  1145. constructor TBCRenderedInputPin.Create(ObjectName: string;
  1146.   Filter: TBCBaseFilter; Lock: TBCCritSec; out hr: HRESULT;
  1147.   Name: WideString);
  1148. begin
  1149.    inherited Create(ObjectName, Filter, Lock, hr, Name);
  1150.    FAtEndOfStream := FALSE;
  1151.    FCompleteNotified := FALSE;
  1152. end;
  1153. procedure TBCRenderedInputPin.DoCompleteHandling;
  1154. begin
  1155.   ASSERT(FAtEndOfStream);
  1156.   if (not FCompleteNotified) then
  1157.   begin
  1158.     FCompleteNotified := True;
  1159.     FFilter.NotifyEvent(EC_COMPLETE, S_OK, Integer(FFilter));
  1160.   end;
  1161. end;
  1162. function TBCRenderedInputPin.EndFlush: HRESULT;
  1163. begin
  1164.   FLock.Lock;
  1165.   try
  1166.     // Clean up renderer state
  1167.     FAtEndOfStream := FALSE;
  1168.     FCompleteNotified := FALSE;
  1169.     result := inherited EndFlush;
  1170.   finally
  1171.     FLock.UnLock;
  1172.   end;
  1173. end;
  1174. function TBCRenderedInputPin.EndOfStream: HRESULT;
  1175. var
  1176.   fs: TFilterState;
  1177. begin
  1178.   result := CheckStreaming;
  1179.   //  Do EC_COMPLETE handling for rendered pins
  1180.   if ((result = S_OK) and (not FAtEndOfStream)) then
  1181.   begin
  1182.     FAtEndOfStream := True;
  1183.     ASSERT(SUCCEEDED(FFilter.GetState(0, fs)));
  1184.     if (fs = State_Running) then
  1185.       DoCompleteHandling;
  1186.   end;
  1187. end;
  1188. function TBCRenderedInputPin.Run(Start: TReferenceTime): HRESULT;
  1189. begin
  1190.   FCompleteNotified := FALSE;
  1191.   if FAtEndOfStream then DoCompleteHandling;
  1192.   result := S_OK;
  1193. end;
  1194. { TBCAMMsgEvent }
  1195. function TBCAMMsgEvent.WaitMsg(Timeout: DWord): boolean;
  1196. var
  1197.   // wait for the event to be signalled, or for the
  1198.   // timeout (in MS) to expire.  allow SENT messages
  1199.   // to be processed while we wait
  1200.   Wait, StartTime: DWord;
  1201.   // set the waiting period.
  1202.   WaitTime: Dword;
  1203.   Msg: TMsg;
  1204.   Elapsed: DWord;
  1205. begin
  1206.   WaitTime := Timeout;
  1207.   // the timeout will eventually run down as we iterate
  1208.   // processing messages.  grab the start time so that
  1209.   // we can calculate elapsed times.
  1210.   if (WaitTime <> INFINITE) then
  1211.     StartTime := timeGetTime else
  1212.     StartTime := 0; // don't generate compiler hint 
  1213.   repeat
  1214.     Wait := MsgWaitForMultipleObjects(1, FEvent, FALSE, WaitTime, QS_SENDMESSAGE);
  1215.     if (Wait = WAIT_OBJECT_0 + 1) then
  1216.     begin
  1217.       PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
  1218.     // If we have an explicit length of time to wait calculate
  1219.     // the next wake up point - which might be now.
  1220.     // If dwTimeout is INFINITE, it stays INFINITE
  1221.     if (WaitTime <> INFINITE) then
  1222.       begin
  1223.      Elapsed := timeGetTime - StartTime;
  1224.         if (Elapsed >= Timeout) then
  1225.           WaitTime := 0 else // wake up with WAIT_TIMEOUT
  1226.      WaitTime := Timeout - Elapsed;
  1227.     end;
  1228.     end
  1229.   until (Wait <> WAIT_OBJECT_0 + 1);
  1230.   // return True if we woke on the event handle,
  1231.   //        FALSE if we timed out.
  1232.   result := (Wait = WAIT_OBJECT_0);
  1233. end;
  1234. { TBCAMThread }
  1235. function TBCAMThread.CallWorker(Param: DWORD): DWORD;
  1236. begin
  1237.   // lock access to the worker thread for scope of this object
  1238.   FAccessLock.Lock;
  1239.   try
  1240.     if not ThreadExists then
  1241.     begin
  1242.       Result := DWORD(E_FAIL);
  1243.       Exit;
  1244.     end;
  1245.     // set the parameter
  1246.     FParam := Param;
  1247.     // signal the worker thread
  1248.     FEventSend.SetEv;
  1249.     // wait for the completion to be signalled
  1250.     FEventComplete.Wait;
  1251.     // done - this is the thread's return value
  1252.     Result := FReturnVal;
  1253.   finally
  1254.     FAccessLock.unlock;
  1255.   end;
  1256. end;
  1257. function TBCAMThread.CheckRequest(Param: PDWORD): boolean;
  1258. begin
  1259.   if not FEventSend.Check then
  1260.   begin
  1261.     Result := FALSE;
  1262.     Exit;
  1263.   end else
  1264.   begin
  1265.     if (Param <> nil) then
  1266.       Param^ := FParam;
  1267.     Result := True;
  1268.   end;
  1269. end;
  1270. procedure TBCAMThread.Close;
  1271. var
  1272.   Thread: THandle;
  1273. begin
  1274.   Thread := InterlockedExchange(Integer(FThread), 0);
  1275.   if BOOL(Thread) then
  1276.   begin
  1277.     WaitForSingleObject(Thread, INFINITE);
  1278.     CloseHandle(Thread);
  1279.   end;
  1280. end;
  1281. class function TBCAMThread.CoInitializeHelper: HRESULT;
  1282. var
  1283.   hr: HRESULT;
  1284.   hOle: LongWord;
  1285.   CoInitializeEx: function(pvReserved: Pointer; coInit: Longint): HResult; stdcall;
  1286. begin
  1287.     // call CoInitializeEx and tell OLE not to create a window (this
  1288.     // thread probably won't dispatch messages and will hang on
  1289.     // broadcast msgs o/w).
  1290.     //
  1291.     // If CoInitEx is not available, threads that don't call CoCreate
  1292.     // aren't affected. Threads that do will have to handle the
  1293.     // failure. Perhaps we should fall back to CoInitialize and risk
  1294.     // hanging?
  1295.     //
  1296.     // older versions of ole32.dll don't have CoInitializeEx
  1297.     hr := E_FAIL;
  1298.     hOle := GetModuleHandle(PChar('ole32.dll'));
  1299.     if (hOle <> 0) then
  1300.     begin
  1301.       CoInitializeEx := GetProcAddress(hOle, 'CoInitializeEx');
  1302.       if (@CoInitializeEx <> nil) then
  1303.         hr := CoInitializeEx(nil, COINIT_DISABLE_OLE1DDE);
  1304.     end else
  1305.     begin
  1306.     {$IFDEF DEBUG}
  1307.       // caller must load ole32.dll
  1308.        DbgLog('couldn''t locate ole32.dll');
  1309.     {$ENDIF}
  1310.     end;
  1311.     result := hr;
  1312. end;
  1313. constructor TBCAMThread.Create;
  1314. begin
  1315.   // must be manual-reset for CheckRequest()
  1316.   FAccessLock := TBCCritSec.Create;
  1317.   FWorkerLock := TBCCritSec.Create;
  1318.   FEventSend := TBCAMEvent.Create(True);
  1319.   FEventComplete := TBCAMEvent.Create;
  1320.   FThread := 0;
  1321.   FThreadProc := nil;
  1322. end;
  1323. function TBCAMThread.Create_: boolean;
  1324. var
  1325.   threadid: DWORD;
  1326. begin
  1327.   FAccessLock.Lock;
  1328.   try
  1329.     if ThreadExists then
  1330.     begin
  1331.       Result := False;
  1332.       Exit;
  1333.     end;
  1334.     FThread := CreateThread(nil, 0, @TBCAMThread.InitialThreadProc,
  1335.       Self, 0, threadid);
  1336.     if not BOOL(FThread) then
  1337.       Result := FALSE else
  1338.       Result := True;
  1339.   finally
  1340.     FAccessLock.Unlock;
  1341.   end;
  1342. end;
  1343. destructor TBCAMThread.Destroy;
  1344. begin
  1345.   Close;
  1346.   FAccessLock.Free;
  1347.   FWorkerLock.Free;
  1348.   FEventSend.Free;
  1349.   FEventComplete.Free;
  1350.   inherited;
  1351. end;
  1352. function TBCAMThread.GetRequest: DWORD;
  1353. begin
  1354.   FEventSend.Wait;
  1355.   Result := FParam;
  1356. end;
  1357. function TBCAMThread.GetRequestHandle: THANDLE;
  1358. begin
  1359.   Result := FEventSend.FEvent
  1360. end;
  1361. function TBCAMThread.GetRequestParam: DWORD;
  1362. begin
  1363.   Result := FParam;
  1364. end;
  1365. function TBCAMThread.InitialThreadProc(p: Pointer): DWORD;
  1366. var
  1367.   hrCoInit: HRESULT;
  1368. begin
  1369.   hrCoInit := TBCAMThread.CoInitializeHelper;
  1370. {$IFDEF DEBUG}
  1371.   if(FAILED(hrCoInit)) then
  1372.     DbgLog('CoInitializeEx failed.');
  1373. {$ENDIF}
  1374.   Result := ThreadProc;
  1375.   if(SUCCEEDED(hrCoInit)) then
  1376.     CoUninitialize;
  1377. end;
  1378. procedure TBCAMThread.Reply(v: DWORD);
  1379. begin
  1380.     FReturnVal := v;
  1381.     // The request is now complete so CheckRequest should fail from
  1382.     // now on
  1383.     //
  1384.     // This event should be reset BEFORE we signal the client or
  1385.     // the client may Set it before we reset it and we'll then
  1386.     // reset it (!)
  1387.     FEventSend.Reset;
  1388.     // Tell the client we're finished
  1389.     FEventComplete.SetEv;
  1390. end;
  1391. function TBCAMThread.ThreadExists: boolean;
  1392. begin
  1393.   Result := FThread <> 0;
  1394. end;
  1395. function TBCAMThread.ThreadProc: DWord;
  1396. begin
  1397.   if @FThreadProc <> nil then
  1398.     Result := FThreadProc else
  1399.     Result := 0
  1400. end;
  1401. { TBCNode }
  1402. {$ifdef DEBUG}
  1403. constructor TBCNode.Create;
  1404. begin
  1405.   inherited Create('List node');
  1406. end;
  1407. {$ENDIF}
  1408. { TBCNodeCache }
  1409. procedure TBCNodeCache.AddToCache(Node: TBCNode);
  1410. begin
  1411.   if (FUsed < FCacheSize) then
  1412.   begin
  1413.     Node.Next := FHead;
  1414.     FHead := Node;
  1415.     inc(FUsed);
  1416.   end else
  1417.     Node.Free;
  1418. end;
  1419. constructor TBCNodeCache.Create(CacheSize: Integer);
  1420. begin
  1421.   FCacheSize := CacheSize;
  1422.   FHead := nil;
  1423.   FUsed := 0;
  1424. end;
  1425. destructor TBCNodeCache.Destroy;
  1426. var Node, Current: TBCNode;
  1427. begin
  1428.   Node := FHead;
  1429.   while (Node <> nil) do
  1430.   begin
  1431.     Current := Node;
  1432.     Node := Node.Next;
  1433.     Current.Free;
  1434.   end;
  1435.   inherited;
  1436. end;
  1437. function TBCNodeCache.RemoveFromCache: TBCNode;
  1438. var Node: TBCNode;
  1439. begin
  1440.   Node := FHead;
  1441.   if (Node <> nil) then
  1442.   begin
  1443.     FHead := Node.Next;
  1444.     Dec(FUsed);
  1445.     ASSERT(FUsed >= 0);
  1446.   end else
  1447.     ASSERT(FUsed = 0);
  1448.   Result := Node;
  1449. end;
  1450. { TBCBaseList }
  1451. function TBCBaseList.AddAfter(p: Position; List: TBCBaseList): BOOL;
  1452. var pos: Position;
  1453. begin
  1454.   pos := list.GetHeadPositionI;
  1455.   while(pos <> nil) do
  1456.   begin
  1457.     // p follows along the elements being added
  1458.     p := AddAfterI(p, List.GetI(pos));
  1459.     if (p = nil) then
  1460.     begin
  1461.       Result := FALSE;
  1462.       Exit;
  1463.     end;
  1464.     pos := list.Next(pos);
  1465.   end;
  1466.   Result := True;
  1467. end;
  1468. (* Add the object after position p
  1469.    p is still valid after the operation.
  1470.    AddAfter(NULL,x) adds x to the start - same as AddHead
  1471.    Return the position of the new object, NULL if it failed
  1472. *)
  1473. function TBCBaseList.AddAfterI(pos: Position; Obj: Pointer): Position;
  1474. var After, Node, Before: TBCNode;
  1475. begin
  1476.   if (pos = nil) then
  1477.     Result := AddHeadI(Obj) else
  1478.   begin
  1479.     (* As someone else might be furkling with the list -
  1480.        Lock the critical section before continuing
  1481.     *)
  1482.     After := pos;
  1483.     ASSERT(After <> nil);
  1484.     if (After = FLast) then
  1485.       Result := AddTailI(Obj) else
  1486.       begin
  1487.         // set pnode to point to a new node, preferably from the cache
  1488.         Node := FCache.RemoveFromCache;
  1489.         if (Node = nil) then
  1490.           Node := TBCNode.Create;
  1491.         // Check we have a valid object
  1492.         if (Node = nil) then
  1493.           Result := nil else
  1494.           begin
  1495.           (* Initialise all the CNode object
  1496.              just in case it came from the cache
  1497.           *)
  1498.           Node.Data := Obj;
  1499.           (* It is to be added to the middle of the list - there is a before
  1500.              and after node.  Chain it after pAfter, before pBefore.
  1501.           *)
  1502.           Before := After.Next;
  1503.           ASSERT(Before <> nil);
  1504.           // chain it in (set four pointers)
  1505.           Node.Prev := After;
  1506.           Node.Next := Before;
  1507.           Before.Prev := Node;
  1508.           After.Next := Node;
  1509.           inc(FCount);
  1510.           Result := Node;
  1511.         end;
  1512.       end;
  1513.     end;
  1514. end;
  1515. function TBCBaseList.AddBefore(p: Position; List: TBCBaseList): BOOL;
  1516. var pos: Position;
  1517. begin
  1518.   pos := List.GetTailPositionI;
  1519.   while (pos <> nil) do
  1520.   begin
  1521.       // p follows along the elements being added
  1522.       p := AddBeforeI(p, List.GetI(pos));
  1523.       if (p = nil) then
  1524.       begin
  1525.         Result := FALSE;
  1526.         Exit;
  1527.       end;
  1528.     pos := list.Prev(pos);
  1529.   end;
  1530.   Result := True;
  1531. end;
  1532. (* Mirror images:
  1533.    Add the element or list after position p.
  1534.    p is still valid after the operation.
  1535.    AddBefore(NULL,x) adds x to the end - same as AddTail
  1536. *)
  1537. function TBCBaseList.AddBeforeI(pos: Position; Obj: Pointer): Position;
  1538. var
  1539.   Before, Node, After: TBCNode;
  1540. begin
  1541.   if (pos = nil) then
  1542.     Result := AddTailI(Obj) else
  1543.     begin
  1544.       // set pnode to point to a new node, preferably from the cache
  1545.       Before := pos;
  1546.       ASSERT(Before <> nil);
  1547.       if (Before = FFirst) then
  1548.         Result := AddHeadI(Obj) else
  1549.         begin
  1550.           Node := FCache.RemoveFromCache;
  1551.           if (Node = nil) then
  1552.             Node := TBCNode.Create;
  1553.           // Check we have a valid object */
  1554.           if (Node = nil) then
  1555.             Result := nil else
  1556.             begin
  1557.               (* Initialise all the CNode object
  1558.                  just in case it came from the cache
  1559.               *)
  1560.               Node.Data := Obj;
  1561.               (* It is to be added to the middle of the list - there is a before
  1562.                  and after node.  Chain it after pAfter, before pBefore.
  1563.               *)
  1564.               After := Before.Prev;
  1565.               ASSERT(After <> nil);
  1566.               // chain it in (set four pointers)
  1567.               Node.Prev := After;
  1568.               Node.Next := Before;
  1569.               Before.Prev := Node;
  1570.               After.Next := Node;
  1571.               inc(FCount);
  1572.               Result := Node;
  1573.             end;
  1574.         end;
  1575.     end;
  1576. end;
  1577. (* Add all the elements in *pList to the head of this list.
  1578.    Return True if it all worked, FALSE if it didn't.
  1579.    If it fails some elements may have been added.
  1580. *)
  1581. function TBCBaseList.AddHead(List: TBCBaseList): BOOL;
  1582. var
  1583.   pos: Position;
  1584. begin
  1585.   (* lock the object before starting then enumerate
  1586.      each entry in the source list and add them one by one to
  1587.      our list (while still holding the object lock)
  1588.      Lock the other list too.
  1589.      To avoid reversing the list, traverse it backwards.
  1590.   *)
  1591.   pos := list.GetTailPositionI;
  1592.   while (pos <> nil) do
  1593.   begin
  1594.     if (nil = AddHeadI(List.GetI(pos))) then
  1595.     begin
  1596.       Result := FALSE;
  1597.       Exit;
  1598.     end;
  1599.     pos := list.Prev(pos)
  1600.   end;
  1601.   Result := True;
  1602. end;
  1603. (* Add this object to the head end of our list
  1604.    Return the new head position.
  1605. *)
  1606. function TBCBaseList.AddHeadI(Obj: Pointer): Position;
  1607. var Node: TBCNode;
  1608. begin
  1609.   (* If there is a node objects in the cache then use
  1610.      that otherwise we will have to create a new one *)
  1611.   Node := FCache.RemoveFromCache;
  1612.   if (Node = nil) then
  1613.     Node := TBCNode.Create;
  1614.   // Check we have a valid object
  1615.   if (Node = nil) then
  1616.   begin
  1617.     Result := nil;
  1618.     Exit;
  1619.   end;
  1620.   (* Initialise all the CNode object
  1621.      just in case it came from the cache
  1622.   *)
  1623.   Node.Data := Obj;
  1624.   // chain it in (set four pointers)
  1625.   Node.Prev := nil;
  1626.   Node.Next := FFirst;
  1627.   if (FFirst = nil) then
  1628.     FLast := Node;
  1629.     FFirst.Prev := Node;
  1630.   FFirst := Node;
  1631.   inc(FCount);
  1632.   Result := Node;
  1633. end;
  1634. (* Add all the elements in *pList to the tail of this list.
  1635.    Return True if it all worked, FALSE if it didn't.
  1636.    If it fails some elements may have been added.
  1637. *)
  1638. function TBCBaseList.AddTail(List: TBCBaseList): boolean;
  1639. var pos: Position;
  1640. begin
  1641.   (* lock the object before starting then enumerate
  1642.      each entry in the source list and add them one by one to
  1643.      our list (while still holding the object lock)
  1644.      Lock the other list too.
  1645.   *)
  1646.   Result := false;
  1647.   pos := List.GetHeadPositionI;
  1648.   while (pos <> nil) do
  1649.     if (nil = AddTailI(List.GetNextI(pos))) then
  1650.       Exit;
  1651.   Result := True;
  1652. end;
  1653. (* Add this object to the tail end of our list
  1654.    Return the new tail position.
  1655. *)
  1656. function TBCBaseList.AddTailI(Obj: Pointer): Position;
  1657. var
  1658.   Node: TBCNode;
  1659. begin
  1660.   // Lock the critical section before continuing
  1661.   // ASSERT(pObject);   // NULL pointers in the list are allowed.
  1662.   (* If there is a node objects in the cache then use
  1663.      that otherwise we will have to create a new one *)
  1664.   Node := FCache.RemoveFromCache;
  1665.   if (Node = nil) then
  1666.     Node := TBCNode.Create;
  1667.   // Check we have a valid object
  1668.   if Node = nil then // HG: out of memory ???
  1669.   begin
  1670.     Result := nil;
  1671.     Exit;
  1672.   end;
  1673.   (* Initialise all the CNode object
  1674.      just in case it came from the cache
  1675.   *)
  1676.   Node.Data := Obj;
  1677.   Node.Next := nil;
  1678.   Node.Prev := FLast;
  1679.   if (FLast = nil) then
  1680.     FFirst := Node;
  1681.     FLast.Next := Node;
  1682.   (* Set the new last node pointer and also increment the number
  1683.      of list entries, the critical section is unlocked when we
  1684.      exit the function
  1685.   *)
  1686.   FLast := Node;
  1687.   inc(FCount);
  1688.   Result := Node;
  1689. end;
  1690. (* Constructor calls a separate initialisation function that
  1691.    creates a node cache, optionally creates a lock object
  1692.    and optionally creates a signaling object.
  1693.    By default we create a locking object, a DEFAULTCACHE sized
  1694.    cache but no event object so the list cannot be used in calls
  1695.    to WaitForSingleObject
  1696. *)
  1697. constructor TBCBaseList.Create(Name: string; Items: Integer = DEFAULTCACHE);
  1698. begin
  1699. {$ifdef DEBUG}
  1700.   inherited Create(Name);
  1701. {$endif}
  1702.   FFirst := nil;
  1703.   FLast  := nil;
  1704.   FCount := 0;
  1705.   FCache := TBCNodeCache.Create(Items);
  1706. end;
  1707. (* The destructor enumerates all the node objects in the list and
  1708.    in the cache deleting each in turn. We do not do any processing
  1709.    on the objects that the list holds (i.e. points to) so if they
  1710.    represent interfaces for example the creator of the list should
  1711.    ensure that each of them is released before deleting us
  1712. *)
  1713. destructor TBCBaseList.Destroy;
  1714. begin
  1715.   RemoveAll;
  1716.   FCache.Free;
  1717.   inherited;
  1718. end;
  1719. (* Return the first position in the list which holds the given pointer.
  1720.    Return NULL if it's not found.
  1721. *)
  1722. function TBCBaseList.FindI(Obj: Pointer): Position;
  1723. begin
  1724.   Result := GetHeadPositionI;
  1725.   while (Result <> nil) do
  1726.   begin
  1727.     if (GetI(Result) = Obj) then Exit;
  1728.     Result := Next(Result);
  1729.   end;
  1730. end;
  1731. (* Get the number of objects in the list,
  1732.    Get the lock before accessing the count.
  1733.    Locking may not be entirely necessary but it has the side effect
  1734.    of making sure that all operations are complete before we get it.
  1735.    So for example if a list is being added to this list then that
  1736.    will have completed in full before we continue rather than seeing
  1737.    an intermediate albeit valid state
  1738. *)
  1739. function TBCBaseList.GetCountI: Integer;
  1740. begin
  1741.   Result := FCount;
  1742. end;
  1743. (* Return a position enumerator for the entire list.
  1744.    A position enumerator is a pointer to a node object cast to a
  1745.    transparent type so all we do is return the head/tail node
  1746.    pointer in the list.
  1747.    WARNING because the position is a pointer to a node there is
  1748.    an implicit assumption for users a the list class that after
  1749.    deleting an object from the list that any other position
  1750.    enumerators that you have may be invalid (since the node
  1751.    may be gone).
  1752. *)
  1753. function TBCBaseList.GetHeadPositionI: Position;
  1754. begin
  1755.   result := Position(FFirst);
  1756. end;
  1757. (* Return the object at p.
  1758.    Asking for the object at NULL ASSERTs then returns NULL
  1759.    The object is NOT locked.  The list is not being changed
  1760.    in any way.  If another thread is busy deleting the object
  1761.    then locking would only result in a change from one bad
  1762.    behaviour to another.
  1763. *)
  1764. function TBCBaseList.GetI(p: Position): Pointer;
  1765. begin
  1766.   if (p = nil) then
  1767.     Result := nil else
  1768.     Result := TBCNode(p).Data;
  1769. end;
  1770. (* Return the object at rp, update rp to the next object from
  1771.    the list or NULL if you have moved over the last object.
  1772.    You may still call this function once we return NULL but
  1773.    we will continue to return a NULL position value
  1774. *)
  1775. function TBCBaseList.GetNextI(var rp: Position): Pointer;
  1776. var
  1777.   pn: TBCNode;
  1778. begin
  1779.   // have we reached the end of the list
  1780.   if (rp = nil) then
  1781.     Result := nil else
  1782.   begin
  1783.     // Lock the object before continuing
  1784.     // Copy the original position then step on
  1785.     pn := rp;
  1786.     ASSERT(pn <> nil);
  1787.     rp := Position(pn.Next);
  1788.     // Get the object at the original position from the list
  1789.     Result := pn.Data;
  1790.   end;
  1791. end;
  1792. function TBCBaseList.GetTailPositionI: Position;
  1793. begin
  1794.   Result := Position(FLast);
  1795. end;
  1796. (* Mirror image of MoveToTail:
  1797.    Split self before position p in self.
  1798.    Retain in self the head portion of the original self
  1799.    Add the tail portion to the start (i.e. head) of *pList
  1800.    Return True if it all worked, FALSE if it didn't.
  1801.    e.g.
  1802.       foo->MoveToHead(foo->GetTailPosition(), bar);
  1803.           moves one element from the tail of foo to the head of bar
  1804.       foo->MoveToHead(NULL, bar);
  1805.           is a no-op
  1806.       foo->MoveToHead(foo->GetHeadPosition, bar);
  1807.           concatenates foo onto the start of bar and empties foo.
  1808. *)
  1809. function TBCBaseList.MoveToHead(pos: Position; List: TBCBaseList): boolean;
  1810. var
  1811.   p: TBCNode;
  1812.   m: Integer;
  1813. begin
  1814.   // See the comments on the algorithm in MoveToTail
  1815.   if (pos = nil) then
  1816.     Result := True else  // no-op.  Eliminates special cases later.
  1817.     begin
  1818.       // Make cMove the number of nodes to move
  1819.       p := pos;
  1820.       m := 0;            // number of nodes to move
  1821.       while(p <> nil) do
  1822.       begin
  1823.         p := p.Next;
  1824.         inc(m);
  1825.       end;
  1826.       // Join the two chains together
  1827.       if (List.FFirst <> nil) then
  1828.         List.FFirst.Prev := FLast;
  1829.       if (FLast <> nil) then
  1830.         FLast.Next := List.FFirst;
  1831.       // set first and last pointers
  1832.       p := pos;
  1833.       if (List.FLast = nil) then
  1834.         List.FLast := FLast;
  1835.       FLast := p.Prev;
  1836.       if (FLast = nil) then
  1837.         FFirst := nil;
  1838.       List.FFirst := p;
  1839.       // Break the chain after p to create the new pieces
  1840.       if (FLast <> nil) then
  1841.         FLast.Next := nil;
  1842.       p.Prev := nil;
  1843.       // Adjust the counts
  1844.       dec(FCount, m);
  1845.       inc(List.FCount, m);
  1846.       Result := True;
  1847.     end;
  1848. end;
  1849. (* Split self after position p in self
  1850.    Retain as self the tail portion of the original self
  1851.    Add the head portion to the tail end of *pList
  1852.    Return True if it all worked, FALSE if it didn't.
  1853.    e.g.
  1854.       foo->MoveToTail(foo->GetHeadPosition(), bar);
  1855.           moves one element from the head of foo to the tail of bar
  1856.       foo->MoveToTail(NULL, bar);
  1857.           is a no-op
  1858.       foo->MoveToTail(foo->GetTailPosition, bar);
  1859.           concatenates foo onto the end of bar and empties foo.
  1860.    A better, except excessively long name might be
  1861.        MoveElementsFromHeadThroughPositionToOtherTail
  1862. *)
  1863. function TBCBaseList.MoveToTail(pos: Position; List: TBCBaseList): boolean;
  1864. var
  1865.   p: TBCNode;
  1866.   m: Integer;
  1867. begin
  1868.   (* Algorithm:
  1869.      Note that the elements (including their order) in the concatenation
  1870.      of *pList to the head of self is invariant.
  1871.      1. Count elements to be moved
  1872.      2. Join *pList onto the head of this to make one long chain
  1873.      3. Set first/Last pointers in self and *pList
  1874.      4. Break the chain at the new place
  1875.      5. Adjust counts
  1876.      6. Set/Reset any events
  1877.   *)
  1878.   if (pos = nil) then
  1879.     Result := True else  // no-op.  Eliminates special cases later.
  1880.     begin
  1881.       // Make m the number of nodes to move
  1882.       p := pos;
  1883.       m := 0;            // number of nodes to move
  1884.       while(p <> nil) do
  1885.       begin
  1886.         p := p.Prev;
  1887.         inc(m);
  1888.       end;
  1889.       // Join the two chains together
  1890.       if (List.FLast <> nil) then
  1891.         List.FLast.Next := FFirst;
  1892.       if (FFirst <> nil) then
  1893.         FFirst.Prev := List.FLast;
  1894.       // set first and last pointers 
  1895.       p := pos;
  1896.       if (List.FFirst = nil) then
  1897.         List.FFirst := FFirst;
  1898.       FFirst := p.Next;
  1899.       if (FFirst = nil) then
  1900.         FLast := nil;
  1901.       List.FLast := p;
  1902.       // Break the chain after p to create the new pieces
  1903.       if (FFirst <> nil) then
  1904.         FFirst.Prev := nil;
  1905.       p.Next := nil;
  1906.       // Adjust the counts 
  1907.       dec(FCount, m);
  1908.       inc(List.FCount, m);
  1909.       Result := True;
  1910.     end;
  1911. end;
  1912. function TBCBaseList.Next(pos: Position): Position;
  1913. begin
  1914.   if (pos = nil) then
  1915.     Result := Position(FFirst) else
  1916.     Result := Position(TBCNode(pos).Next);
  1917. end;
  1918. function TBCBaseList.Prev(pos: Position): Position;
  1919. begin
  1920.   if (pos = nil) then
  1921.     Result := Position(FLast) else
  1922.     Result := Position(TBCNode(pos).Prev);
  1923. end;
  1924. (* Remove all the nodes from the list but don't do anything
  1925.    with the objects that each node looks after (this is the
  1926.    responsibility of the creator).
  1927.    Aa a last act we reset the signalling event
  1928.    (if available) to indicate to clients that the list
  1929.    does not have any entries in it.
  1930. *)
  1931. procedure TBCBaseList.RemoveAll;
  1932. var pn, op: TBCNode;
  1933. begin
  1934.   (* Free up all the CNode objects NOTE we don't bother putting the
  1935.      deleted nodes into the cache as this method is only really called
  1936.      in serious times of change such as when we are being deleted at
  1937.      which point the cache will be deleted anyway *)
  1938.   pn := FFirst;
  1939.   while (pn <> nil) do
  1940.   begin
  1941.     op := pn;
  1942.     pn := pn.Next;
  1943.     op.Free;
  1944.   end;
  1945.   (* Reset the object count and the list pointers *)
  1946.   FCount := 0;
  1947.   FFirst := nil;
  1948.   FLast  := nil;
  1949. end;
  1950. (* Remove the first node in the list (deletes the pointer to its object
  1951.    from the list, does not free the object itself).
  1952.    Return the pointer to its object or NULL if empty
  1953. *)
  1954. function TBCBaseList.RemoveHeadI: Pointer;
  1955. begin
  1956.   (* All we do is get the head position and ask for that to be deleted.
  1957.      We could special case this since some of the code path checking
  1958.      in Remove() is redundant as we know there is no previous
  1959.      node for example but it seems to gain little over the
  1960.      added complexity
  1961.   *)
  1962.   Result := RemoveI(FFirst);
  1963. end;
  1964. (* Remove the pointer to the object in this position from the list.
  1965.    Deal with all the chain pointers
  1966.    Return a pointer to the object removed from the list.
  1967.    The node object that is freed as a result
  1968.    of this operation is added to the node cache where
  1969.    it can be used again.
  1970.    Remove(NULL) is a harmless no-op - but probably is a wart.
  1971. *)
  1972. function TBCBaseList.RemoveI(pos: Position): Pointer;
  1973. var
  1974.   Current, Node: TBCNode;
  1975. begin
  1976.   (* Lock the critical section before continuing *)
  1977.   if (pos = nil) then
  1978.     Result := nil else
  1979.     begin
  1980.       Current := pos;
  1981.       ASSERT(Current <> nil);
  1982.       // Update the previous node
  1983.       Node := Current.Prev;
  1984.       if (Node = nil) then
  1985.         FFirst := Current.Next else
  1986.         Node.Next := Current.Next;
  1987.       // Update the following node
  1988.       Node := Current.Next;
  1989.       if (Node = nil) then
  1990.         FLast := Current.Prev else
  1991.         Node.Prev := Current.Prev;
  1992.       // Get the object this node was looking after */
  1993.       Result := Current.Data;
  1994.       // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.
  1995.       (* Try and add the node object to the cache -
  1996.          a NULL return code from the cache means we ran out of room.
  1997.          The cache size is fixed by a constructor argument when the
  1998.          list is created and defaults to DEFAULTCACHE.
  1999.          This means that the cache will have room for this many
  2000.          node objects. So if you have a list of media samples
  2001.          and you know there will never be more than five active at
  2002.          any given time of them for example then override the default
  2003.          constructor
  2004.       *)
  2005.       FCache.AddToCache(Current);
  2006.       // If the list is empty then reset the list event
  2007.       Dec(FCount);
  2008.       ASSERT(FCount >= 0);
  2009.     end;
  2010. end;
  2011. (* Remove the last node in the list (deletes the pointer to its object
  2012.    from the list, does not free the object itself).
  2013.    Return the pointer to its object or NULL if empty
  2014. *)
  2015. function TBCBaseList.RemoveTailI: Pointer;
  2016. begin
  2017.   (* All we do is get the tail position and ask for that to be deleted.
  2018.      We could special case this since some of the code path checking
  2019.      in Remove() is redundant as we know there is no previous
  2020.      node for example but it seems to gain little over the
  2021.      added complexity
  2022.   *)
  2023.   Result := RemoveI(FLast);
  2024. end;
  2025. (* Reverse the order of the [pointers to] objects in slef *)
  2026. procedure TBCBaseList.Reverse;
  2027. var p, q: TBCNode;
  2028. begin
  2029.   (* algorithm:
  2030.      The obvious booby trap is that you flip pointers around and lose
  2031.      addressability to the node that you are going to process next.
  2032.      The easy way to avoid this is do do one chain at a time.
  2033.      Run along the forward chain,
  2034.      For each node, set the reverse pointer to the one ahead of us.
  2035.      The reverse chain is now a copy of the old forward chain, including
  2036.      the NULL termination.
  2037.      Run along the reverse chain (i.e. old forward chain again)
  2038.      For each node set the forward pointer of the node ahead to point back
  2039.      to the one we're standing on.
  2040.      The first node needs special treatment,
  2041.      it's new forward pointer is NULL.
  2042.      Finally set the First/Last pointers
  2043.   *)
  2044.   // Yes we COULD use a traverse, but it would look funny!
  2045.   p := FFirst;
  2046.   while (p <> nil) do
  2047.   begin
  2048.     q := p.Next;
  2049.     p.Next := p.Prev;
  2050.     p.Prev := q;
  2051.     p := q;
  2052.   end;
  2053.   p := FFirst;
  2054.   FFirst := FLast;
  2055.   FLast := p;
  2056. end;
  2057. { TBCSource }
  2058. function TBCSource.AddPin(Stream: TBCSourceStream): HRESULT;
  2059. begin
  2060.   FStateLock.Lock;
  2061.   try
  2062.     inc(FPins);
  2063.     ReallocMem(FStreams, FPins * SizeOf(TBCSourceStream));
  2064.     TStreamArray(FStreams)[FPins-1] := Stream;
  2065.     Result := S_OK;
  2066.   finally
  2067.     FStateLock.UnLock;
  2068.   end;
  2069. end;
  2070. // milenko start (delphi 5 doesn't IInterface - changed IInterface to IUnknown)
  2071. constructor TBCSource.Create(const Name: string; unk: IUnknown;
  2072. // milenko end
  2073.   const clsid: TGUID; out hr: HRESULT);
  2074. begin
  2075.   FStateLock := TBCCritSec.Create;
  2076.   // nev: changed 02/17/04
  2077.   inherited Create(Name, unk, FStateLock, clsid, hr);
  2078.   FPins := 0;
  2079.   FStreams := nil;
  2080. end;
  2081. // milenko start (delphi 5 doesn't IInterface - changed IInterface to IUnknown)
  2082. constructor TBCSource.Create(const Name: string; unk: IUnknown;
  2083. // milenko end
  2084.   const clsid: TGUID);
  2085. begin
  2086.   FStateLock := TBCCritSec.Create;
  2087.   inherited Create(Name, unk, FStateLock, clsid);
  2088.   FPins := 0;
  2089.   FStreams := nil;
  2090. end;
  2091. destructor TBCSource.Destroy;
  2092. begin
  2093.   //  Free our pins and pin array
  2094.   while (FPins <> 0) do
  2095.     // deleting the pins causes them to be removed from the array...
  2096.   TStreamArray(FStreams)[FPins - 1].Free;
  2097.   if Assigned(FStreams) then FreeMem(FStreams);
  2098.   ASSERT(FPins = 0);
  2099.   inherited;
  2100. end;
  2101. // Set Pin to the IPin that has the id Id.
  2102. // or to nil if the Id cannot be matched.
  2103. function TBCSource.FindPin(Id: PWideChar; out Pin: IPin): HRESULT;
  2104. var
  2105.   i : integer;
  2106.   Code : integer;
  2107. begin
  2108.   // The -1 undoes the +1 in QueryId and ensures that totally invalid
  2109.   // strings (for which WstrToInt delivers 0) give a deliver a NULL pin.
  2110.   // DCoder (1. Nov 2003)
  2111.   // StrToInt throws EConvertError Exceptions if
  2112.   // a Filter calls FindPin with a String instead of a Number in ID.
  2113.   // To be sure, capture the Error Handling by using Val and call
  2114.   // the inherited function if Val fails.
  2115.   
  2116.   Val(Id,i,Code);
  2117.   if Code = 0 then
  2118.   begin
  2119.     i := i - 1;
  2120.     Pin := GetPin(i);
  2121.     if (Pin <> nil) then
  2122.       Result := NOERROR else
  2123.       Result := VFW_E_NOT_FOUND;
  2124.   end else Result := inherited FindPin(Id,Pin);
  2125. end;
  2126. // return the number of the pin with this IPin or -1 if none
  2127. function TBCSource.FindPinNumber(Pin: IPin): Integer;
  2128. begin
  2129.   for Result := 0 to FPins - 1 do
  2130.     if (IPin(TStreamArray(FStreams)[Result]) = Pin) then
  2131.       Exit;
  2132.   Result := -1;
  2133. end;
  2134. // Return a non-addref'd pointer to pin n
  2135. // needed by CBaseFilter
  2136. function TBCSource.GetPin(n: Integer): TBCBasePin;
  2137. begin
  2138.   FStateLock.Lock;
  2139.   try
  2140.     // n must be in the range 0..m_iPins-1
  2141.     // if m_iPins>n  && n>=0 it follows that m_iPins>0
  2142.     // which is what used to be checked (i.e. checking that we have a pin)
  2143.     if ((n >= 0) and (n < FPins)) then
  2144.     begin
  2145.       ASSERT(TStreamArray(FStreams)[n] <> nil);
  2146.      Result := TStreamArray(FStreams)[n];
  2147.     end else
  2148.       Result := nil;
  2149.   finally
  2150.     FStateLock.UnLock;
  2151.   end;
  2152. end;
  2153. // Returns the number of pins this filter has
  2154. function TBCSource.GetPinCount: Integer;
  2155. begin
  2156.   FStateLock.Lock;
  2157.   try
  2158.     Result := FPins;
  2159.   finally
  2160.     FStateLock.UnLock;
  2161.   end;
  2162. end;
  2163. function TBCSource.RemovePin(Stream: TBCSourceStream): HRESULT;
  2164. var i, j: Integer;
  2165. begin
  2166.   for i := 0 to FPins - 1 do
  2167.   begin
  2168.     if (TStreamArray(FStreams)[i] = Stream) then
  2169.     begin
  2170.       if (FPins = 1) then
  2171.       begin
  2172.         FreeMem(FStreams);
  2173.         FStreams := nil;
  2174.       end else
  2175.       begin
  2176.         //  no need to reallocate
  2177.         j := i + 1;
  2178.         while (j < FPins) do
  2179.         begin
  2180.           TStreamArray(FStreams)[j-1] := TStreamArray(FStreams)[j];
  2181.           inc(j);
  2182.         end;
  2183.       end;
  2184.       dec(FPins);
  2185.       Result := S_OK;
  2186.       Exit;
  2187.     end;
  2188.   end;
  2189.   Result := S_FALSE;
  2190. end;
  2191. { TBCSourceStream }
  2192. // The pin is active - start up the worker thread
  2193. function TBCSourceStream.Active: HRESULT;
  2194. begin
  2195.   FFilter.FStateLock.Lock;
  2196.   try
  2197.     if (FFilter.IsActive) then
  2198.     begin
  2199.       Result := S_FALSE; // succeeded, but did not allocate resources (they already exist...)
  2200.       Exit;
  2201.     end;
  2202.     // do nothing if not connected - its ok not to connect to
  2203.     // all pins of a source filter
  2204.     if not IsConnected then
  2205.     begin
  2206.       Result := NOERROR;
  2207.       Exit;
  2208.     end;
  2209.     Result := inherited Active;
  2210.     if FAILED(Result) then
  2211.       Exit;
  2212.     ASSERT(not FThread.ThreadExists);
  2213.     // start the thread
  2214.     if not FThread.Create_ then
  2215.     begin
  2216.       Result := E_FAIL;
  2217.       Exit;
  2218.     end;
  2219.     // Tell thread to initialize. If OnThreadCreate Fails, so does this.
  2220.     Result := Init;
  2221.     if FAILED(Result) then
  2222.       Exit;
  2223.     Result := Pause;
  2224.   finally
  2225.     FFilter.FStateLock.UnLock;
  2226.   end;
  2227. end;
  2228. // Do we support this type? Provides the default support for 1 type.
  2229. function TBCSourceStream.CheckMediaType(MediaType: PAMMediaType): HRESULT;
  2230. var mt: TAMMediaType;
  2231.     pmt: PAMMediaType;
  2232. begin
  2233.   FFilter.FStateLock.Lock;
  2234.   try
  2235.     pmt := @mt;
  2236.     GetMediaType(pmt);
  2237.     if TBCMediaType(pmt).Equal(MediaType) then
  2238.       Result := NOERROR else
  2239.       Result := E_FAIL;
  2240.   finally
  2241.     FFilter.FStateLock.UnLock;
  2242.   end;
  2243. end;
  2244. function TBCSourceStream.CheckRequest(var com: TThreadCommand): boolean;
  2245. begin
  2246.   Result := FThread.CheckRequest(@Com);
  2247. end;
  2248. // increments the number of pins present on the filter
  2249. constructor TBCSourceStream.Create(const ObjectName: string;
  2250.   out hr: HRESULT; Filter: TBCSource; const Name: WideString);
  2251. begin
  2252.   FThread := TBCAMThread.Create;
  2253.   FThread.FThreadProc := ThreadProc;
  2254.   inherited Create(ObjectName, Filter, Filter.FStateLock,  hr, Name);
  2255.   FFilter := Filter;
  2256.   hr := FFilter.AddPin(Self);
  2257. end;
  2258. // Decrements the number of pins on this filter
  2259. destructor TBCSourceStream.Destroy;
  2260. begin
  2261.   FFilter.RemovePin(Self);
  2262.   inherited;
  2263.   FThread.Free;
  2264. end;
  2265. // Grabs a buffer and calls the users processing function.
  2266. // Overridable, so that different delivery styles can be catered for.
  2267. function TBCSourceStream.DoBufferProcessingLoop: HRESULT;
  2268. var
  2269.   com: TThreadCommand;
  2270.   Sample: IMediaSample;
  2271. begin
  2272.   OnThreadStartPlay;
  2273.   repeat
  2274.   begin
  2275.   while not CheckRequest(com) do
  2276.     begin
  2277.     Result := GetDeliveryBuffer(Sample, nil, nil, 0);
  2278.     if FAILED(result) then
  2279.       begin
  2280.         Sleep(1);
  2281.     continue; // go round again. Perhaps the error will go away
  2282.             // or the allocator is decommited & we will be asked to
  2283.             // exit soon.
  2284.     end;
  2285.     // Virtual function user will override.
  2286.     Result := FillBuffer(Sample);
  2287.     if (Result = S_OK) then
  2288.       begin
  2289.       Result := Deliver(Sample);
  2290.         Sample := nil;
  2291.         // downstream filter returns S_FALSE if it wants us to
  2292.         // stop or an error if it's reporting an error.
  2293.         if (Result <> S_OK) then
  2294.         begin
  2295.         {$IFDEF DEBUG}
  2296.           DbgLog(format('Deliver() returned %08x; stopping', [Result]));
  2297.         {$ENDIF}
  2298.           Result := S_OK;
  2299.           Exit;
  2300.         end;
  2301.     end else
  2302.         if (Result = S_FALSE) then
  2303.         begin
  2304.           // derived class wants us to stop pushing data
  2305.           Sample := nil;
  2306.       DeliverEndOfStream;
  2307.       Result := S_OK;
  2308.           Exit;
  2309.       end else
  2310.         begin
  2311.           // derived class encountered an error
  2312.           Sample := nil;
  2313.           {$IFDEF DEBUG}
  2314.             DbgLog(format('Error %08lX from FillBuffer!!!', [Result]));
  2315.           {$ENDIF}
  2316.           DeliverEndOfStream;
  2317.           FFilter.NotifyEvent(EC_ERRORABORT, Result, 0);
  2318.           Exit;
  2319.       end;
  2320.         // all paths release the sample
  2321.   end;
  2322.     // For all commands sent to us there must be a Reply call!
  2323.   if ((com = CMD_RUN) or (com = CMD_PAUSE)) then
  2324.     FThread.Reply(NOERROR) else
  2325.       if (com <> CMD_STOP) then
  2326.       begin
  2327.       Fthread.Reply(DWORD(E_UNEXPECTED));
  2328.       {$IFDEF DEBUG}
  2329.       DbgLog('Unexpected command!!!');
  2330.       {$ENDIF}
  2331.       end
  2332.   end until (com = CMD_STOP);
  2333.   Result := S_FALSE;
  2334. end;
  2335. function TBCSourceStream.Exit_: HRESULT;
  2336. begin
  2337.   Result := FThread.CallWorker(Ord(CMD_EXIT));
  2338. end;
  2339. function TBCSourceStream.GetMediaType(MediaType: PAMMediaType): HRESULT;
  2340. begin
  2341.   Result := E_UNEXPECTED;
  2342. end;
  2343. function TBCSourceStream.GetMediaType(Position: integer;
  2344.   out MediaType: PAMMediaType): HRESULT;
  2345. begin
  2346.   // By default we support only one type
  2347.   // Position indexes are 0-n
  2348.   FFilter.FStateLock.Lock;
  2349.   try
  2350.     if (Position = 0) then
  2351.       Result := GetMediaType(MediaType)
  2352.     else
  2353.       if (Position > 0) then
  2354.         Result := VFW_S_NO_MORE_ITEMS else
  2355.         Result := E_INVALIDARG;
  2356.   finally
  2357.     FFilter.FStateLock.UnLock;
  2358.   end;
  2359. end;
  2360. function TBCSourceStream.GetRequest: TThreadCommand;
  2361. begin
  2362.   Result := TThreadCommand(FThread.GetRequest);
  2363. end;
  2364. // Pin is inactive - shut down the worker thread
  2365. // Waits for the worker to exit before returning.
  2366. function TBCSourceStream.Inactive: HRESULT;
  2367. begin
  2368.   FFilter.FStateLock.Lock;
  2369.   try
  2370.     // do nothing if not connected - its ok not to connect to
  2371.     // all pins of a source filter
  2372.     if not IsConnected then
  2373.     begin
  2374.       Result := NOERROR;
  2375.       Exit;
  2376.     end;
  2377.     // !!! need to do this before trying to stop the thread, because
  2378.     // we may be stuck waiting for our own allocator!!!
  2379.     Result := inherited Inactive;  // call this first to Decommit the allocator
  2380.     if FAILED(Result) then
  2381.       Exit;
  2382.     if FThread.ThreadExists then
  2383.     begin
  2384.     Result := Stop;
  2385.      if FAILED(Result) then
  2386.         Exit;
  2387.     Result := Exit_;
  2388.       if FAILED(Result) then
  2389.         Exit;
  2390.     FThread.Close; // Wait for the thread to exit, then tidy up.
  2391.     end;
  2392.     Result := NOERROR;
  2393.   finally
  2394.     FFilter.FStateLock.UnLock;
  2395.   end;
  2396. end;
  2397. function TBCSourceStream.Init: HRESULT;
  2398. begin
  2399.   Result := FThread.CallWorker(Ord(CMD_INIT));
  2400. end;
  2401. function TBCSourceStream.OnThreadCreate: HRESULT;
  2402. begin
  2403.   Result := NOERROR;
  2404. end;
  2405. function TBCSourceStream.OnThreadDestroy: HRESULT;
  2406. begin
  2407.   Result := NOERROR;
  2408. end;
  2409. function TBCSourceStream.OnThreadStartPlay: HRESULT;
  2410. begin
  2411.   Result := NOERROR;
  2412. end;
  2413. function TBCSourceStream.Pause: HRESULT;
  2414. begin
  2415.   Result := FThread.CallWorker(Ord(CMD_PAUSE));
  2416. end;
  2417. // Set Id to point to a CoTaskMemAlloc'd
  2418. function TBCSourceStream.QueryId(out id: PWideChar): HRESULT;
  2419. var
  2420.   i: Integer;
  2421. begin
  2422.   // We give the pins id's which are 1,2,...
  2423.   // FindPinNumber returns -1 for an invalid pin
  2424.   i := 1 + FFilter.FindPinNumber(Self);
  2425.   if (i < 1) then
  2426.     Result := VFW_E_NOT_FOUND else
  2427.     Result := AMGetWideString(IntToStr(i), id);
  2428. end;
  2429. function TBCSourceStream.Run: HRESULT;
  2430. begin
  2431.   Result := FThread.CallWorker(Ord(CMD_RUN));
  2432. end;
  2433. function TBCSourceStream.Stop: HRESULT;
  2434. begin
  2435.   Result := FThread.CallWorker(Ord(CMD_STOP));
  2436. end;
  2437. // When this returns the thread exits
  2438. // Return codes > 0 indicate an error occured
  2439. function TBCSourceStream.ThreadProc: DWORD;
  2440. var
  2441.   com, cmd: TThreadCommand;
  2442. begin
  2443.   repeat
  2444.   com := GetRequest;
  2445.    if (com <> CMD_INIT) then
  2446.     begin
  2447.     {$IFDEF DEBUG}
  2448.     DbgLog(self, 'Thread expected init command');
  2449.     {$ENDIF}
  2450.     FThread.Reply(DWORD(E_UNEXPECTED));
  2451.   end;
  2452.   until (com = CMD_INIT);
  2453.   {$IFDEF DEBUG}
  2454.     DbgLog(self, 'Worker thread initializing');
  2455.   {$ENDIF}
  2456.   Result := OnThreadCreate; // perform set up tasks
  2457.   if FAILED(Result) then
  2458.   begin
  2459.   {$IFDEF DEBUG}
  2460.     DbgLog(Self, 'OnThreadCreate failed. Aborting thread.');
  2461.   {$ENDIF}
  2462.     OnThreadDestroy();
  2463.     FThread.Reply(Result); // send failed return code from OnThreadCreate
  2464.     Result := 1;
  2465.     Exit;
  2466.   end;
  2467.   // Initialisation suceeded
  2468.   FThread.Reply(NOERROR);
  2469.   repeat
  2470.     cmd := GetRequest;
  2471.     // nev: changed 02/17/04
  2472.     // "repeat..until false" ensures, that if cmd = CMD_RUN
  2473.     // the next executing block will be CMD_PAUSE handler block.
  2474.     // This corresponds to the original C "switch" functionality
  2475.     repeat
  2476.       case cmd of
  2477.         CMD_EXIT, CMD_STOP:
  2478.           begin
  2479.             FThread.Reply(NOERROR);
  2480.             Break;
  2481.           end;
  2482.         CMD_RUN:
  2483.           begin
  2484.           {$IFDEF DEBUG}
  2485.             DbgLog(Self, 'CMD_RUN received before a CMD_PAUSE???');
  2486.           {$ENDIF}
  2487.             // !!! fall through???
  2488.             cmd := CMD_PAUSE;
  2489.           end;
  2490.         CMD_PAUSE:
  2491.           begin
  2492.             FThread.Reply(NOERROR);
  2493.             DoBufferProcessingLoop;
  2494.             Break;
  2495.           end;
  2496.       else
  2497.       {$IFDEF DEBUG}
  2498.         DbgLog(self, format('Unknown command %d received!', [Integer(cmd)]));
  2499.       {$ENDIF}
  2500.         FThread.Reply(DWORD(E_NOTIMPL));
  2501.         Break;
  2502.       end;
  2503.     until False;
  2504.   until (cmd = CMD_EXIT);
  2505.   Result := OnThreadDestroy; // tidy up.
  2506.   if FAILED(Result) then
  2507.   begin
  2508.   {$IFDEF DEBUG}
  2509.     DbgLog(self, 'OnThreadDestroy failed. Exiting thread.');
  2510.   {$ENDIF}
  2511.     Result := 1;
  2512.     Exit;
  2513.   end;
  2514. {$IFDEF DEBUG}
  2515.   DbgLog(Self, 'worker thread exiting');
  2516. {$ENDIF}
  2517.   Result := 0;
  2518. end;
  2519. function TimeKillSynchronousFlagAvailable: Boolean;
  2520. var
  2521.   osverinfo: TOSVERSIONINFO;
  2522. begin
  2523.   osverinfo.dwOSVersionInfoSize := sizeof(osverinfo);
  2524.   if GetVersionEx(osverinfo) then
  2525.     // Windows XP's major version is 5 and its' minor version is 1.
  2526.     // timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag
  2527.     // in Windows XP.
  2528.     Result := (osverinfo.dwMajorVersion > 5) or
  2529.       ((osverinfo.dwMajorVersion = 5) and (osverinfo.dwMinorVersion >= 1))
  2530.   else
  2531.     Result := False;
  2532. end;
  2533. function CompatibleTimeSetEvent(Delay, Resolution: UINT;
  2534.   TimeProc: TFNTimeCallBack; User: DWORD; Event: UINT): MMResult;
  2535. // milenko start (replaced with global variables)
  2536. //const
  2537. //{$IFOPT J-}
  2538. //{$DEFINE ResetJ}
  2539. //{$J+}
  2540. //{$ENDIF}
  2541. //  IsCheckedVersion: Bool = False;
  2542. //  IsTimeKillSynchronousFlagAvailable: Bool = False;
  2543. //{$IFDEF ResetJ}
  2544. //{$J-}
  2545. //{$UNDEF ResetJ}
  2546. //{$ENDIF}
  2547. const
  2548.   TIME_KILL_SYNCHRONOUS = $100;
  2549. // Milenko end
  2550. var
  2551.   Event_: UINT;
  2552. begin
  2553.   Event_ := Event;
  2554.   // ??? TIME_KILL_SYNCHRONOUS flag is defined in MMSystem for XP:
  2555.   // need to check that D7 unit for proper compilation flag
  2556. // Milenko start (no need for "ifdef xp" in delphi)
  2557. // {$IFDEF XP}
  2558.   if not IsCheckedVersion then
  2559.   begin
  2560.     IsTimeKillSynchronousFlagAvailable := TimeKillSynchronousFlagAvailable;
  2561.     IsCheckedVersion := true;
  2562.   end;
  2563.   if IsTimeKillSynchronousFlagAvailable then
  2564.     Event_ := Event_ or TIME_KILL_SYNCHRONOUS;
  2565. // {$ENDIF}
  2566. // Milenko end
  2567.   Result := timeSetEvent(Delay, Resolution, TimeProc, User, Event_);
  2568. end;
  2569. // ??? See Measure.h for Msr_??? definition
  2570. // milenko start (only needed with PERF)
  2571. {$IFDEF PERF}
  2572. type
  2573.   TIncidentRec = packed record
  2574.     Name: String[255];
  2575.   end;
  2576.   TIncidentLog = packed record
  2577.     Id: Integer;
  2578.     Time: TReferenceTime;
  2579.     Data: Integer;
  2580.     Note: String[10];
  2581.   end;
  2582. var
  2583.   Incidents: array of TIncidentRec;
  2584.   IncidentsLog: array of TIncidentLog;
  2585. {$ENDIF}
  2586. // milenko end
  2587. function MSR_REGISTER(s: String): Integer;
  2588. // milenko start (only needed with PERF)
  2589. {$IFDEF PERF}
  2590. var
  2591.   k: Integer;
  2592. {$ENDIF}
  2593. // milenko end
  2594. begin
  2595. // milenko start (only needed with PERF)
  2596. {$IFDEF PERF}
  2597.   k := Length(Incidents) + 1;
  2598.   SetLength(Incidents, k);
  2599.   Incidents[k-1].Name := Copy(s, 0, 255);
  2600.   Result := k-1;
  2601. {$ELSE}
  2602.   Result := 0;
  2603. {$ENDIF}
  2604. // milenko end
  2605. end;
  2606. procedure MSR_START(Id_: Integer);
  2607. {$IFDEF PERF}
  2608. var
  2609.   k: Integer;
  2610. {$ENDIF}
  2611. begin
  2612. {$IFDEF PERF}
  2613.   Assert((Id_>=0) and (Id_<Length(Incidents)));
  2614.   k := Length(IncidentsLog) + 1;
  2615.   SetLength(IncidentsLog, k);
  2616.   with IncidentsLog[k-1] do
  2617.   begin
  2618.     Id    := Id_;
  2619.     Time  := timeGetTime;
  2620.     Data  := 0;
  2621.     Note  := Copy('START', 0, 10);
  2622.   end;
  2623. {$ENDIF}
  2624. end;
  2625. procedure MSR_STOP(Id_: Integer);
  2626. {$IFDEF PERF}
  2627. var
  2628.   k: Integer;
  2629. {$ENDIF}
  2630. begin
  2631. {$IFDEF PERF}
  2632.   Assert((Id_>=0) and (Id_<Length(Incidents)));
  2633.   k := Length(IncidentsLog) + 1;
  2634.   SetLength(IncidentsLog, k);
  2635.   with IncidentsLog[k-1] do
  2636.   begin
  2637.     Id    := Id_;
  2638.     Time  := timeGetTime;
  2639.     Data  := 0;
  2640.     Note  := Copy('STOP', 0, 10);
  2641.   end;
  2642. {$ENDIF}
  2643. end;
  2644. procedure MSR_INTEGER(Id_, i: Integer);
  2645. {$IFDEF PERF}
  2646. var
  2647.   k: Integer;
  2648. {$ENDIF}
  2649. begin
  2650. {$IFDEF PERF}
  2651.   Assert((Id_>=0) and (Id_<Length(Incidents)));
  2652.   k := Length(IncidentsLog) + 1;
  2653.   SetLength(IncidentsLog, k);
  2654.   with IncidentsLog[k-1] do
  2655.   begin
  2656.     Id    := Id_;
  2657.     Time  := timeGetTime;
  2658.     Data  := i;
  2659.     Note  := Copy('START', 0, 10);
  2660.   end;
  2661. {$ENDIF}
  2662. end;
  2663. // #define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD)
  2664. procedure DO_MOVING_AVG(var avg, obs: Integer);
  2665. begin
  2666.   avg := (1024 * obs + (AVGPERIOD - 1) * avg) div AVGPERIOD;
  2667. end;
  2668. //  Helper function for clamping time differences
  2669. function TimeDiff(rt: TReferenceTime): Integer;
  2670. begin
  2671.   if (rt < -(50 * UNITS)) then
  2672.     Result := -(50 * UNITS)
  2673.   else
  2674.     if (rt > 50 * UNITS) then
  2675.       Result := 50 * UNITS
  2676.     else
  2677.       Result := Integer(rt);
  2678. end;
  2679. // Implements the CBaseRenderer class
  2680. constructor TBCBaseRenderer.Create(RendererClass: TGUID; Name: PChar;
  2681.   Unk: IUnknown; hr: HResult);
  2682. begin
  2683.   FInterfaceLock      := TBCCritSec.Create;
  2684.   FRendererLock       := TBCCritSec.Create;
  2685.   FObjectCreationLock := TBCCritSec.Create;
  2686.   inherited Create(Name, Unk, FInterfaceLock, RendererClass);
  2687.   FCompleteEvent    := TBCAMEvent.Create(True);
  2688.   FRenderEvent      := TBCAMEvent.Create(True);
  2689.   FAbort            := False;
  2690.   FPosition         := nil;
  2691.   FThreadSignal     := TBCAMEvent.Create(True);
  2692.   FIsStreaming      := False;
  2693.   FIsEOS            := False;
  2694.   FIsEOSDelivered   := False;
  2695.   FMediaSample      := nil;
  2696.   FAdvisedCookie    := 0;
  2697.   FQSink            := nil;
  2698.   FInputPin         := nil;
  2699.   FRepaintStatus    := True;
  2700.   FSignalTime       := 0;
  2701.   FInReceive        := False;
  2702.   FEndOfStreamTimer := 0;
  2703.   Ready;
  2704. {$IFDEF PERF}
  2705.   FBaseStamp      := MSR_REGISTER('BaseRenderer: sample time stamp');
  2706.   FBaseRenderTime := MSR_REGISTER('BaseRenderer: draw time(msec)');
  2707.   FBaseAccuracy   := MSR_REGISTER('BaseRenderer: Accuracy(msec)');
  2708. {$ENDIF}
  2709. end;
  2710. // Delete the dynamically allocated IMediaPosition and IMediaSeeking helper
  2711. // object. The object is created when somebody queries us. These are standard
  2712. // control interfaces for seeking and setting start/stop positions and rates.
  2713. // We will probably also have made an input pin based on CRendererInputPin
  2714. // that has to be deleted, it's created when an enumerator calls our GetPin
  2715. destructor TBCBaseRenderer.Destroy;
  2716. begin
  2717.   Assert(not FIsStreaming);
  2718.   Assert(FEndOfStreamTimer = 0);
  2719.   StopStreaming;
  2720.   ClearPendingSample;
  2721.   // Delete any IMediaPosition implementation
  2722.   if Assigned(FPosition) then
  2723.     FreeAndNil(FPosition);
  2724.   // Delete any input pin created
  2725.   if Assigned(FInputPin) then
  2726.     FreeAndNil(FInputPin);
  2727.   // Release any Quality sink
  2728.   Assert(FQSink = nil);
  2729.   // Release critical sections objects
  2730.   // ??? will be deleted by the parent class destroy FreeAndNil(FInterfaceLock);
  2731.   FreeAndNil(FRendererLock);
  2732.   FreeAndNil(FObjectCreationLock);
  2733.   FreeAndNil(FCompleteEvent);
  2734.   FreeAndNil(FRenderEvent);
  2735.   FreeAndNil(FThreadSignal);
  2736.   inherited Destroy;
  2737. end;
  2738. // This returns the IMediaPosition and IMediaSeeking interfaces
  2739. function TBCBaseRenderer.GetMediaPositionInterface(IID: TGUID;
  2740.   out Obj): HResult;
  2741. var                                       
  2742.   hr: HResult;
  2743. begin
  2744.   FObjectCreationLock.Lock;
  2745.   try
  2746.     if Assigned(FPosition) then
  2747.     begin
  2748. // Milenko start
  2749. //      Result := FPosition.QueryInterface(IID, Obj);
  2750.       Result := FPosition.NonDelegatingQueryInterface(IID, Obj);
  2751. // Milenko end
  2752.       Exit;
  2753.     end;
  2754.     hr := NOERROR;
  2755.     // Create implementation of this dynamically since sometimes we may
  2756.     // never try and do a seek. The helper object implements a position
  2757.     // control interface (IMediaPosition) which in fact simply takes the
  2758.     // calls normally from the filter graph and passes them upstream
  2759.     //hr := CreatePosPassThru(GetOwner, False, GetPin(0), FPosition);
  2760.     FPosition := TBCRendererPosPassThru.Create('Renderer TBCPosPassThru',
  2761.       Inherited GetOwner, hr, GetPin(0));
  2762.     if (FPosition = nil) then
  2763.     begin
  2764.       Result := E_OUTOFMEMORY;
  2765.       Exit;
  2766.     end;
  2767.     if (Failed(hr)) then
  2768.     begin
  2769.       FreeAndNil(FPosition);
  2770.       Result := E_NOINTERFACE;
  2771.       Exit;
  2772.     end;
  2773. // milenko start (needed or the class will destroy itself. Disadvantage=Destructor is not called)
  2774. // Solution is to keep FPosition alive without adding a Reference Count to it. But how???
  2775.     FPosition._AddRef;
  2776. // milenko end
  2777.     Result := GetMediaPositionInterface(IID, Obj);
  2778.   finally
  2779.     FObjectCreationLock.UnLock;
  2780.   end;
  2781. end;
  2782. // milenko start (workaround for destructor issue with FPosition)
  2783. function TBCBaseRenderer.JoinFilterGraph(pGraph: IFilterGraph;
  2784.   pName: PWideChar): HRESULT;
  2785. begin
  2786.   if (pGraph = nil) and (FPosition <> nil) then
  2787.   begin
  2788.     FPosition._Release;
  2789.     Pointer(FPosition) := nil;
  2790.   end;
  2791.   Result := inherited JoinFilterGraph(pGraph,pName);
  2792. end;
  2793. // milenko end
  2794. // Overriden to say what interfaces we support and where
  2795. function TBCBaseRenderer.NonDelegatingQueryInterface(const IID: TGUID;
  2796.   out Obj): HResult;
  2797. begin
  2798. // Milenko start (removed unnessacery code)
  2799.   // Do we have this interface
  2800.   if IsEqualGUID(IID, IID_IMediaPosition) or IsEqualGUID(IID, IID_IMediaSeeking)
  2801.     then Result := GetMediaPositionInterface(IID,Obj)
  2802.     else Result := inherited NonDelegatingQueryInterface(IID, Obj);
  2803. // Milenko end
  2804. end;
  2805. // This is called whenever we change states, we have a manual reset event that
  2806. // is signalled whenever we don't won't the source filter thread to wait in us
  2807. // (such as in a stopped state) and likewise is not signalled whenever it can
  2808. // wait (during paused and running) this function sets or resets the thread
  2809. // event. The event is used to stop source filter threads waiting in Receive
  2810. function TBCBaseRenderer.SourceThreadCanWait(CanWait: Boolean): HResult;
  2811. begin
  2812.   if CanWait then
  2813.     FThreadSignal.Reset
  2814.   else
  2815.     FThreadSignal.SetEv;
  2816.   Result := NOERROR;
  2817. end;
  2818. {$IFDEF DEBUG}
  2819. // Dump the current renderer state to the debug terminal. The hardest part of
  2820. // the renderer is the window where we unlock everything to wait for a clock
  2821. // to signal it is time to draw or for the application to cancel everything
  2822. // by stopping the filter. If we get things wrong we can leave the thread in
  2823. // WaitForRenderTime with no way for it to ever get out and we will deadlock
  2824. procedure TBCBaseRenderer.DisplayRendererState;
  2825. var
  2826.   bSignalled, bFlushing: Boolean;
  2827.   CurrentTime, StartTime, EndTime, Offset, Wait: TReferenceTime;
  2828.   function RT_in_Millisecs(rt: TReferenceTime): Int64;
  2829.   begin
  2830.     Result := rt div 10000;
  2831.   end;
  2832. begin
  2833.   DbgLog(Self, 'Timed out in WaitForRenderTime');
  2834.   // No way should this be signalled at this point
  2835.   bSignalled := FThreadSignal.Check;
  2836.   DbgLog(Self, Format('Signal sanity check %d', [Byte(bSignalled)]));
  2837.   // Now output the current renderer state variables
  2838.   DbgLog(Self, Format('Filter state %d', [Ord(FState)]));
  2839.   DbgLog(Self, Format('Abort flag %d', [Byte(FAbort)]));
  2840.   DbgLog(Self, Format('Streaming flag %d', [Byte(FIsStreaming)]));
  2841.   DbgLog(Self, Format('Clock advise link %d', [FAdvisedCookie]));
  2842. //  DbgLog(Self, Format('Current media sample %x', [FMediaSample]));
  2843.   DbgLog(Self, Format('EOS signalled %d', [Byte(FIsEOS)]));
  2844.   DbgLog(Self, Format('EOS delivered %d', [Byte(FIsEOSDelivered)]));
  2845.   DbgLog(Self, Format('Repaint status %d', [Byte(FRepaintStatus)]));
  2846.   // Output the delayed end of stream timer information
  2847.   DbgLog(Self, Format('End of stream timer %x', [FEndOfStreamTimer]));
  2848.   // ??? convert reftime to str
  2849.   //    DbgLog((LOG_TIMING, 1, TEXT("Deliver time %s"),CDisp((LONGLONG)FSignalTime)));
  2850.   DbgLog(Self, Format('Deliver time %d', [FSignalTime]));
  2851.   // Should never timeout during a flushing state
  2852.   bFlushing := FInputPin.IsFlushing;
  2853.   DbgLog(Self, Format('Flushing sanity check %d', [Byte(bFlushing)]));
  2854.   // Display the time we were told to start at
  2855. // ???  DbgLog((LOG_TIMING, 1, TEXT("Last run time %s"),CDisp((LONGLONG)m_tStart.m_time)));
  2856.   DbgLog(Self, Format('Last run time %d', [FStart]));
  2857.   // Have we got a reference clock
  2858.   if (FClock = nil) then
  2859.     Exit;
  2860.   // Get the current time from the wall clock
  2861.   FClock.GetTime(int64(CurrentTime));
  2862.   Offset := CurrentTime - FStart;
  2863.   // Display the current time from the clock
  2864.   DbgLog(Self, Format('Clock time %d', [CurrentTime]));
  2865.   DbgLog(Self, Format('Time difference %d ms', [RT_in_Millisecs(Offset)]));
  2866.   // Do we have a sample ready to render
  2867.   if (FMediaSample = nil) then
  2868.     Exit;
  2869.   FMediaSample.GetTime(StartTime, EndTime);
  2870.   DbgLog(Self, Format('Next sample stream times (Start %d End %d ms)',
  2871.     [RT_in_Millisecs(StartTime), RT_in_Millisecs(EndTime)]));
  2872.   // Calculate how long it is until it is due for rendering
  2873.   Wait := (FStart + StartTime) - CurrentTime;
  2874.   DbgLog(Self, Format('Wait required %d ms', [RT_in_Millisecs(Wait)]));
  2875. end;
  2876. {$ENDIF}
  2877. // Wait until the clock sets the timer event or we're otherwise signalled. We
  2878. // set an arbitrary timeout for this wait and if it fires then we display the
  2879. // current renderer state on the debugger. It will often fire if the filter's
  2880. // left paused in an application however it may also fire during stress tests
  2881. // if the synchronisation with application seeks and state changes is faulty
  2882. const
  2883.   RENDER_TIMEOUT = 10000;
  2884. function TBCBaseRenderer.WaitForRenderTime: HResult;
  2885. var
  2886.   WaitObjects: array[0..1] of THandle;
  2887. begin
  2888.   WaitObjects[0] := FThreadSignal.Handle;
  2889.   WaitObjects[1] := FRenderEvent.Handle;
  2890.   DWord(Result) := WAIT_TIMEOUT;
  2891.   // Wait for either the time to arrive or for us to be stopped
  2892.   OnWaitStart;
  2893.   while (Result = WAIT_TIMEOUT) do
  2894.   begin
  2895.     Result := WaitForMultipleObjects(2, @WaitObjects, False, RENDER_TIMEOUT);
  2896. {$IFDEF DEBUG}
  2897.   if (Result = WAIT_TIMEOUT) then
  2898.     DisplayRendererState;
  2899. {$ENDIF}
  2900.   end;
  2901.   OnWaitEnd;
  2902.   // We may have been awoken without the timer firing
  2903.   if (Result = WAIT_OBJECT_0) then
  2904.   begin
  2905.     Result := VFW_E_STATE_CHANGED;
  2906.     Exit;
  2907.   end;
  2908.   SignalTimerFired;
  2909.   Result := NOERROR;
  2910. end;
  2911. // Poll waiting for Receive to complete.  This really matters when
  2912. // Receive may set the palette and cause window messages
  2913. // The problem is that if we don't really wait for a renderer to
  2914. // stop processing we can deadlock waiting for a transform which
  2915. // is calling the renderer's Receive() method because the transform's
  2916. // Stop method doesn't know to process window messages to unblock
  2917. // the renderer's Receive processing
  2918. procedure TBCBaseRenderer.WaitForReceiveToComplete;
  2919. var
  2920.   msg: TMsg;
  2921. begin
  2922.   repeat
  2923.     if Not FInReceive then
  2924.       Break;
  2925.     //  Receive all interthread sendmessages
  2926.     PeekMessage(msg, 0, WM_NULL, WM_NULL, PM_NOREMOVE);
  2927.     Sleep(1);
  2928.   until False;
  2929.   // If the wakebit for QS_POSTMESSAGE is set, the PeekMessage call
  2930.   // above just cleared the changebit which will cause some messaging
  2931.   // calls to block (waitMessage, MsgWaitFor...) now.
  2932.   // Post a dummy message to set the QS_POSTMESSAGE bit again
  2933.   
  2934.   if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) and QS_POSTMESSAGE) <> 0 then
  2935.     //  Send dummy message
  2936.     PostThreadMessage(GetCurrentThreadId, WM_NULL, 0, 0);
  2937. end;
  2938. // A filter can have four discrete states, namely Stopped, Running, Paused,
  2939. // Intermediate. We are in an intermediate state if we are currently trying
  2940. // to pause but haven't yet got the first sample (or if we have been flushed
  2941. // in paused state and therefore still have to wait for a sample to arrive)
  2942. // This class contains an event called FCompleteEvent which is signalled when
  2943. // the current state is completed and is not signalled when we are waiting to
  2944. // complete the last state transition. As mentioned above the only time we
  2945. // use this at the moment is when we wait for a media sample in paused state
  2946. // If while we are waiting we receive an end of stream notification from the
  2947. // source filter then we know no data is imminent so we can reset the event
  2948. // This means that when we transition to paused the source filter must call
  2949. // end of stream on us or send us an image otherwise we'll hang indefinately
  2950. // Simple internal way of getting the real state
  2951. // !!! make property here
  2952. function TBCBaseRenderer.GetRealState: TFilterState;
  2953. begin
  2954.   Result := FState;
  2955. end;
  2956. // Waits for the HANDLE hObject.  While waiting messages sent
  2957. // to windows on our thread by SendMessage will be processed.
  2958. // Using this function to do waits and mutual exclusion
  2959. // avoids some deadlocks in objects with windows.
  2960. // Return codes are the same as for WaitForSingleObject
  2961. function WaitDispatchingMessages(Object_: THandle; Wait: DWord;
  2962.   Wnd: HWnd = 0; Msg: Cardinal = 0; Event: THandle = 0): DWord;
  2963. // milenko start (replaced with global variables)
  2964. //const
  2965. //{$IFOPT J-}
  2966. //{$DEFINE ResetJ}
  2967. //{$J+}
  2968. //{$ENDIF}
  2969. //  MsgId: Cardinal = 0;
  2970. //{$IFDEF ResetJ}
  2971. //{$J-}
  2972. //{$UNDEF ResetJ}
  2973. //{$ENDIF}
  2974. // milenko end
  2975. var
  2976.   Peeked: Boolean;
  2977.   Res, Start, ThreadPriority: DWord;
  2978.   Objects: array[0..1] of THandle;
  2979.   Count, TimeOut, WakeMask, Now_, Diff: DWord;
  2980.   Msg_: TMsg;
  2981. begin
  2982.   Peeked := False;
  2983.   MsgId := 0;
  2984.   Start := 0;
  2985.   ThreadPriority := THREAD_PRIORITY_NORMAL;
  2986.   Objects[0] := Object_;
  2987.   Objects[1] := Event;
  2988.   if (Wait <> INFINITE) and (Wait <> 0) then
  2989.     Start := GetTickCount;
  2990.   repeat
  2991.     if (Event <> 0) then
  2992.       Count := 2
  2993.     else
  2994.       Count := 1;
  2995.     //  Minimize the chance of actually dispatching any messages
  2996.     //  by seeing if we can lock immediately.
  2997.     Res := WaitForMultipleObjects(Count, @Objects, False, 0);
  2998.     if (Res < WAIT_OBJECT_0 + Count) then
  2999.       Break;
  3000.     TimeOut := Wait;
  3001.     if (TimeOut > 10) then
  3002.       TimeOut := 10;
  3003.     if (Wnd = 0) then
  3004.       WakeMask := QS_SENDMESSAGE
  3005.     else
  3006.       WakeMask := QS_SENDMESSAGE + QS_POSTMESSAGE;
  3007.     Res := MsgWaitForMultipleObjects(Count, Objects, False,
  3008.       TimeOut, WakeMask);
  3009.     if (Res = WAIT_OBJECT_0 + Count) or
  3010.       ((Res = WAIT_TIMEOUT) and (TimeOut <> Wait)) then
  3011.     begin
  3012.       if (Wnd <> 0) then
  3013.         while PeekMessage(Msg_, Wnd, Msg, Msg, PM_REMOVE) do
  3014.           DispatchMessage(Msg_);
  3015.       // Do this anyway - the previous peek doesn't flush out the
  3016.       // messages
  3017.       PeekMessage(Msg_, 0, 0, 0, PM_NOREMOVE);
  3018.       if (Wait <> INFINITE) and (Wait <> 0) then
  3019.       begin
  3020.         Now_ := GetTickCount();
  3021.         // Working with differences handles wrap-around
  3022.         Diff := Now_ - Start;
  3023.         if (Diff > Wait) then
  3024.           Wait := 0
  3025.         else
  3026.           Dec(Wait, Diff);
  3027.         Start := Now_;
  3028.       end;
  3029.       if not (Peeked) then
  3030.       begin
  3031.         //  Raise our priority to prevent our message queue
  3032.         //  building up
  3033.         ThreadPriority := GetThreadPriority(GetCurrentThread);
  3034.         if (ThreadPriority < THREAD_PRIORITY_HIGHEST) then
  3035.         begin
  3036.           // ??? raising priority requires one more routine....
  3037.           SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_HIGHEST);
  3038.         end;
  3039.         Peeked := True;
  3040.       end;
  3041.     end
  3042.     else
  3043.       Break;
  3044.   until False;
  3045.   if (Peeked) then
  3046.   begin
  3047.     // ??? setting priority requires one more routine....
  3048.     SetThreadPriority(GetCurrentThread, ThreadPriority);
  3049. // milenko start (important!)
  3050. //    if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) and QS_POSTMESSAGE) = 0 then
  3051.     if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) and QS_POSTMESSAGE) > 0 then
  3052. // milenko end
  3053.     begin
  3054.       if (MsgId = 0) then
  3055.         MsgId := RegisterWindowMessage('AMUnblock')
  3056.       else
  3057.         //  Remove old ones
  3058.         while (PeekMessage(Msg_, (Wnd) - 1, MsgId, MsgId, PM_REMOVE)) do
  3059. // milenko start (this is a loop without any further function.
  3060. //                it does not call PostThreadMEssage while looping!)
  3061.         begin
  3062.         end;
  3063. // milenko end
  3064.       PostThreadMessage(GetCurrentThreadId, MsgId, 0, 0);
  3065.     end;
  3066.   end;
  3067.   Result := Res;
  3068. end;
  3069. // The renderer doesn't complete the full transition to paused states until
  3070. // it has got one media sample to render. If you ask it for its state while
  3071. // it's waiting it will return the state along with VFW_S_STATE_INTERMEDIATE
  3072. function TBCBaseRenderer.GetState(MSecs: DWord; out State: TFilterState):
  3073.   HResult;
  3074. begin
  3075.   if (WaitDispatchingMessages(FCompleteEvent.Handle, MSecs) = WAIT_TIMEOUT) then
  3076.     Result := VFW_S_STATE_INTERMEDIATE
  3077.   else
  3078.     Result := NOERROR;
  3079.   State := FState;
  3080. end;
  3081. // If we're pausing and we have no samples we don't complete the transition
  3082. // to State_Paused and we return S_FALSE. However if the FAborting flag has
  3083. // been set then all samples are rejected so there is no point waiting for
  3084. // one. If we do have a sample then return NOERROR. We will only ever return
  3085. // VFW_S_STATE_INTERMEDIATE from GetState after being paused with no sample
  3086. // (calling GetState after either being stopped or Run will NOT return this)
  3087. function TBCBaseRenderer.CompleteStateChange(OldState: TFilterState): HResult;
  3088. begin
  3089.   // Allow us to be paused when disconnected
  3090.   if not (FInputPin.IsConnected) or
  3091.     // Have we run off the end of stream
  3092.   IsEndOfStream or
  3093.     // Make sure we get fresh data after being stopped
  3094.   (HaveCurrentSample and (OldState <> State_Stopped)) then
  3095.   begin
  3096.     Ready;
  3097.     Result := S_OK;
  3098.     Exit;
  3099.   end;
  3100.   NotReady;
  3101.   Result := S_False;
  3102. end;
  3103. procedure TBCBaseRenderer.SetAbortSignal(Abort_: Boolean);
  3104. begin
  3105.   FAbort := Abort_;
  3106. end;
  3107. procedure TBCBaseRenderer.OnReceiveFirstSample(MediaSample: IMediaSample);
  3108. begin
  3109. end;
  3110. procedure TBCBaseRenderer.Ready;
  3111. begin
  3112.   FCompleteEvent.SetEv
  3113. end;
  3114. procedure TBCBaseRenderer.NotReady;
  3115. begin
  3116.   FCompleteEvent.Reset
  3117. end;
  3118. function TBCBaseRenderer.CheckReady: Boolean;
  3119. begin
  3120.   Result := FCompleteEvent.Check
  3121. end;
  3122. // When we stop the filter the things we do are:-
  3123. //      Decommit the allocator being used in the connection
  3124. //      Release the source filter if it's waiting in Receive
  3125. //      Cancel any advise link we set up with the clock
  3126. //      Any end of stream signalled is now obsolete so reset
  3127. //      Allow us to be stopped when we are not connected
  3128. function TBCBaseRenderer.Stop: HResult;
  3129. begin
  3130.   FInterfaceLock.Lock;
  3131.   try
  3132.     // Make sure there really is a state change
  3133.     if (FState = State_Stopped) then
  3134.     begin
  3135.       Result := NOERROR;
  3136.       Exit;
  3137.     end;
  3138.     // Is our input pin connected
  3139.     if not (FInputPin.IsConnected) then
  3140.     begin
  3141. {$IFDEF DEBUG}
  3142.       DbgLog(Self, 'Input pin is not connected');
  3143. {$ENDIF}
  3144.       FState := State_Stopped;
  3145.       Result := NOERROR;
  3146.       Exit;
  3147.     end;
  3148.     inherited Stop;
  3149.     // If we are going into a stopped state then we must decommit whatever
  3150.     // allocator we are using it so that any source filter waiting in the
  3151.     // GetBuffer can be released and unlock themselves for a state change
  3152.     if Assigned(FInputPin.FAllocator) then
  3153.       FInputPin.FAllocator.Decommit;
  3154.     // Cancel any scheduled rendering
  3155.     SetRepaintStatus(True);
  3156.     StopStreaming;
  3157.     SourceThreadCanWait(False);
  3158.     ResetEndOfStream;
  3159.     CancelNotification;
  3160.     // There should be no outstanding clock advise
  3161.     Assert(CancelNotification = S_FALSE);
  3162.     Assert(WAIT_TIMEOUT = WaitForSingleObject(FRenderEvent.Handle, 0));
  3163.     Assert(FEndOfStreamTimer = 0);
  3164.     Ready;
  3165.     WaitForReceiveToComplete;
  3166.     FAbort := False;
  3167.     Result := NOERROR;
  3168.   finally
  3169.     FInterfaceLock.UnLock;
  3170.   end;
  3171. end;
  3172. // When we pause the filter the things we do are:-
  3173. //      Commit the allocator being used in the connection
  3174. //      Allow a source filter thread to wait in Receive
  3175. //      Cancel any clock advise link (we may be running)
  3176. //      Possibly complete the state change if we have data
  3177. //      Allow us to be paused when we are not connected
  3178. function TBCBaseRenderer.Pause: HResult;
  3179. var
  3180.   OldState: TFilterState;
  3181.   hr: HResult;
  3182. begin
  3183.   FInterfaceLock.Lock;
  3184.   try
  3185.     OldState := FState;
  3186.     Assert(not FInputPin.IsFlushing);
  3187.     // Make sure there really is a state change
  3188.     if (FState = State_Paused) then
  3189.     begin
  3190.       Result := CompleteStateChange(State_Paused);
  3191.       Exit;
  3192.     end;
  3193.     // Has our input pin been connected
  3194.     if Not FInputPin.IsConnected then
  3195.     begin
  3196. {$IFDEF DEBUG}
  3197.       DbgLog(Self, 'Input pin is not connected');
  3198. {$ENDIF}
  3199.       FState := State_Paused;
  3200.       Result := CompleteStateChange(State_Paused);
  3201.       Exit;
  3202.     end;
  3203.     // Pause the base filter class
  3204.     hr := inherited Pause;
  3205.     if Failed(hr) then
  3206.     begin
  3207. {$IFDEF DEBUG}
  3208.       DbgLog(Self, 'Pause failed');
  3209. {$ENDIF}
  3210.       Result := hr;
  3211.       Exit;
  3212.     end;
  3213.     // Enable EC_REPAINT events again
  3214.     SetRepaintStatus(True);
  3215.     StopStreaming;
  3216.     SourceThreadCanWait(True);
  3217.     CancelNotification;
  3218.     ResetEndOfStreamTimer;
  3219.     // If we are going into a paused state then we must commit whatever
  3220.     // allocator we are using it so that any source filter can call the
  3221.     // GetBuffer and expect to get a buffer without returning an error
  3222.     if Assigned(FInputPin.FAllocator) then
  3223.       FInputPin.FAllocator.Commit;
  3224.     // There should be no outstanding advise
  3225.     Assert(CancelNotification = S_FALSE);
  3226.     Assert(WAIT_TIMEOUT = WaitForSingleObject(FRenderEvent.Handle, 0));
  3227.     Assert(FEndOfStreamTimer = 0);
  3228.     Assert(not FInputPin.IsFlushing);
  3229.     // When we come out of a stopped state we must clear any image we were
  3230.     // holding onto for frame refreshing. Since renderers see state changes
  3231.     // first we can reset ourselves ready to accept the source thread data
  3232.     // Paused or running after being stopped causes the current position to
  3233.     // be reset so we're not interested in passing end of stream signals
  3234.     if (OldState = State_Stopped) then
  3235.     begin
  3236.       FAbort := False;
  3237.       ClearPendingSample;
  3238.     end;
  3239.     Result := CompleteStateChange(OldState);
  3240.   finally
  3241.     FInterfaceLock.Unlock;
  3242.   end;
  3243. end;
  3244. // When we run the filter the things we do are:-
  3245. //      Commit the allocator being used in the connection
  3246. //      Allow a source filter thread to wait in Receive
  3247. //      Signal the render event just to get us going
  3248. //      Start the base class by calling StartStreaming
  3249. //      Allow us to be run when we are not connected
  3250. //      Signal EC_COMPLETE if we are not connected
  3251. function TBCBaseRenderer.Run(StartTime: TReferenceTime): HResult;
  3252. var
  3253.   OldState: TFilterState;
  3254.   hr: HResult;
  3255. // milenko start
  3256.   Filter: IBaseFilter;
  3257. // milenko end
  3258. begin
  3259.   FInterfaceLock.Lock;
  3260.   try
  3261.     OldState := FState;
  3262.     // Make sure there really is a state change
  3263.     if (FState = State_Running) then
  3264.     begin
  3265.       Result := NOERROR;
  3266.       Exit;
  3267.     end;
  3268.     // Send EC_COMPLETE if we're not connected
  3269.     if not FInputPin.IsConnected then
  3270.     begin
  3271. // milenko start (Delphi 5 compatibility)
  3272.       QueryInterface(IID_IBaseFilter,Filter);
  3273.       NotifyEvent(EC_COMPLETE, S_OK, Integer(Filter));
  3274.       Filter := nil;
  3275. // milenko end      
  3276.       FState := State_Running;
  3277.       Result := NOERROR;
  3278.       Exit;
  3279.     end;
  3280.     Ready;
  3281.     // Pause the base filter class
  3282.     hr := inherited Run(StartTime);
  3283.     if Failed(hr) then
  3284.     begin
  3285. {$IFDEF DEBUG}
  3286.       DbgLog(Self, 'Run failed');
  3287. {$ENDIF}
  3288.       Result := hr;
  3289.       Exit;
  3290.     end;
  3291.     // Allow the source thread to wait
  3292.     Assert(not FInputPin.IsFlushing);
  3293.     SourceThreadCanWait(True);
  3294.     SetRepaintStatus(False);
  3295.     // There should be no outstanding advise
  3296.     Assert(CancelNotification = S_FALSE);
  3297.     Assert(WAIT_TIMEOUT = WaitForSingleObject(FRenderEvent.Handle, 0));
  3298.     Assert(FEndOfStreamTimer = 0);
  3299.     Assert(not FInputPin.IsFlushing);
  3300.     // If we are going into a running state then we must commit whatever
  3301.     // allocator we are using it so that any source filter can call the
  3302.     // GetBuffer and expect to get a buffer without returning an error
  3303.     if Assigned(FInputPin.FAllocator) then
  3304.       FInputPin.FAllocator.Commit;
  3305.     // When we come out of a stopped state we must clear any image we were
  3306.     // holding onto for frame refreshing. Since renderers see state changes
  3307.     // first we can reset ourselves ready to accept the source thread data
  3308.     // Paused or running after being stopped causes the current position to
  3309.     // be reset so we're not interested in passing end of stream signals
  3310.     if (OldState = State_Stopped) then
  3311.     begin
  3312.       FAbort := False;
  3313.       ClearPendingSample;
  3314.     end;
  3315.     Result := StartStreaming;
  3316.   finally
  3317.     FInterfaceLock.Unlock;
  3318.   end;
  3319. end;
  3320. // Return the number of input pins we support
  3321. function TBCBaseRenderer.GetPinCount: Integer;
  3322. begin
  3323.   Result := 1;
  3324. end;
  3325. // We only support one input pin and it is numbered zero
  3326. function TBCBaseRenderer.GetPin(n: integer): TBCBasePin;
  3327. var
  3328.   hr: HResult;
  3329. begin