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

Delphi控件源码

开发平台:

Delphi

  1. unit AsyncReader;
  2.    (*********************************************************************
  3.     * The contents of this file are used with permission, subject to    *
  4.     * the Mozilla Public License Version 1.1 (the "License"); you may   *
  5.     * not use this file except in compliance with the License. You may  *
  6.     * obtain a copy of the License at                                   *
  7.     * http://www.mozilla.org/MPL/MPL-1.1.html                           *
  8.     *                                                                   *
  9.     * Software distributed under the License is distributed on an       *
  10.     * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or    *
  11.     * implied. See the License for the specific language governing      *
  12.     * rights and limitations under the License.                         *
  13.     *                                                                   *
  14.     * (C) 2004 Martin Offenwanger: coder@dsplayer.de                    *
  15.     *********************************************************************)
  16. {
  17. @author(Martin Offenwanger: coder@dsplayer.de)
  18. @created(Apr 22, 2004)
  19. @lastmod(Sep 09, 2004)
  20. }
  21. interface
  22. uses
  23.   ActiveX, Classes, DirectShow9, BaseClass, Windows, Queue, Config, Forms,
  24.   ShoutCastStream, SysUtils, Dialogs, ExtCtrls;
  25. type
  26.   TAsyncIO = class(TInterfacedObject, IAsyncReader)
  27.   private
  28.     FStream: IStream;
  29.     FStop,
  30.       FWaiting,
  31.       FFlushing,
  32.       FFwdStream: boolean;
  33.     FReaderLock,
  34.       FListsLock: TBCCritSec;
  35.     FWorkList,
  36.       FDoneList: TQueue;
  37.     FWorkEvent,
  38.       FDoneEvent,
  39.       FAllDoneEv: TBCAMEvent;
  40.     FOutCount: Longint;
  41.     FStrmSize: Int64;
  42.     FThread: TThread;
  43.     FURLMode: boolean;
  44.     FMediaControl: IMediaControl;
  45.     { the pause and run commands called with FMediaControl in Syncread
  46.       must called via a timer, otherwise ondestroy in unit filter won't called }
  47.     FTimerPlay: TTimer;
  48.     FTimerPause: TTimer;
  49.     procedure OnTimerPlay(Sender: TObject);
  50.     procedure OnTimerPause(Sender: TObject);
  51.     procedure PutDoneItem(AItem: PAsyncRequest);
  52.     function GetDoneItem: PAsyncRequest;
  53.     function PutWorkItem(AItem: PAsyncRequest): HRESULT;
  54.     function GetWorkItem: PAsyncRequest;
  55.     function SetPosition(const APos: Int64): HResult;
  56.     procedure InitStreamLen;
  57.     function SetStreamPos(const APos: Int64): HResult;
  58.     function GetStreamPos: Int64;
  59.     function CreateRequest(llPos: LONGLONG; lLength: Integer;
  60.       bAligned: BOOL; pBuffer: Pointer; pContext: Pointer;
  61.       dwUser: DWORD): PAsyncRequest;
  62.     procedure CompleteRequest(Req: PAsyncRequest);
  63.     function InitAllocator(out Alloc: IMemAllocator): HRESULT; virtual;
  64.     function DoRequest(llPos: LONGLONG; lLength: Longint;
  65.       bAligned: BOOL; pBuffer: Pointer; pContext: Pointer;
  66.       dwUser: DWORD): HResult;
  67.     function DoWaitForNext(dwTimeout: DWORD; var ppContext: Pointer;
  68.       var pdwUser: DWORD; var pcbActual: Longint): HRESULT;
  69.   protected
  70.     // IAsyncReader methods
  71.     function RequestAllocator(pPreferred: IMemAllocator;
  72.       pProps: PAllocatorProperties;
  73.       out ppActual: IMemAllocator): HResult; stdcall;
  74.     function Request(pSample: IMediaSample; dwUser: DWORD): HResult; stdcall;
  75.     function WaitForNext(dwTimeout: DWORD; out ppSample: IMediaSample;
  76.       out pdwUser: DWORD): HResult; stdcall;
  77.     function SyncReadAligned(pSample: IMediaSample): HResult; stdcall;
  78.     function SyncRead(llPosition: int64; lLength: Longint;
  79.       pBuffer: Pbyte): HResult; stdcall;
  80.     function Length(out pTotal, pAvailable: int64): HResult; stdcall;
  81.   public
  82.     constructor Create(AStream: IStream; FwdOnly: boolean = false;
  83.       const StreamSize: Int64 = 0; URLMode: boolean = false);
  84.     // calling the destructor causes crashes
  85.     destructor Destroy; override;
  86.     // we use this function to detroy memeber objects
  87.     procedure FreeAllObjects;
  88.     // the graph object for full control during buffering URL stream
  89.     procedure SetActiveGraph(var FilterGraph: IFilterGraph);
  90.     procedure Addref;
  91.     procedure Release;
  92.     procedure Process;
  93.     // IAsyncReader methods
  94.     function BeginFlush: HRESULT; stdcall;
  95.     function EndFlush: HRESULT; stdcall;
  96.     // FURLMode methods
  97.     procedure Connect(Adress: string; Port: string;
  98.       Location: string; MetaData: boolean);
  99.   end;
  100. implementation
  101. uses WorkerThread, filter;
  102. procedure TAsyncIO.setActiveGraph(var FilterGraph: IFilterGraph);
  103. begin
  104.   // In URlmode we need to control the Graph during buffering
  105.   if (FURLMode) and (FMediaControl = nil) then
  106.   begin
  107.     FilterGraph.QueryInterface(IID_IMediaControl, FMediaControl);
  108.     FTimerPlay := TTimer.Create(nil);
  109.     FTimerPlay.Enabled := false;
  110.     FTimerPlay.Interval := 1;
  111.     // makes shure that run is always called after pause
  112.     FTimerPlay.OnTimer := OnTimerPlay;
  113.     FTimerPause := TTimer.Create(nil);
  114.     FTimerPause.Enabled := false;
  115.     FTimerPause.Interval := 1;
  116.     FTimerPause.OnTimer := OnTimerPause;
  117.   end;
  118. end;
  119. procedure TAsyncIO.Connect(Adress: string; Port: string; Location: string;
  120.   MetaData: boolean);
  121. begin
  122.   GFExit := false;
  123.   g_threadedShoutCastStream := TThreadedShoutcastStream.Create(Adress, Port,
  124.     Location, MetaData);
  125. end;
  126. procedure TAsyncIO.Release;
  127. begin
  128.   _Release;
  129. end;
  130. procedure TAsyncIO.Addref;
  131. begin
  132.   _AddRef;
  133. end;
  134. constructor TAsyncIO.Create(AStream: IStream; FwdOnly: boolean = false;
  135.   const StreamSize: Int64 = 0; URLMode: boolean = false);
  136. begin
  137.   inherited Create;
  138.   FTimerPlay := nil;
  139.   if g_threadedShoutCastStream <> nil then
  140.   begin
  141.     g_threadedShoutCastStream.Destroy;
  142.     g_threadedShoutCastStream := nil;
  143.   end;
  144.   FURLMode := URLMode;
  145.   FStream := AStream;
  146.   FListsLock := TBCCritSec.Create;
  147.   FReaderLock := TBCCritSec.Create;
  148.   FWorkList := TQueue.Create;
  149.   FDoneList := TQueue.Create;
  150.   FWorkEvent := TBCAMEvent.Create(true);
  151.   FDoneEvent := TBCAMEvent.Create(true);
  152.   FAllDoneEv := TBCAMEvent.Create(true);
  153.   FFwdStream := FwdOnly;
  154.   FStrmSize := StreamSize;
  155.   FWorkEvent.Reset;
  156.   FThread := TWorkThread.Create(Self);
  157.   FThread.Resume;
  158. end;
  159. procedure TAsyncIO.FreeAllObjects;
  160. var
  161.   Req: PAsyncRequest;
  162. begin
  163.   FStop := true;
  164.   FThread.Terminate;
  165.   FWorkEvent.SetEv;
  166.   FThread.WaitFor;
  167.   FThread.Free;
  168.   Req := GetDoneItem;
  169.   while Req <> nil do
  170.   begin
  171.     Dispose(Req);
  172.     Req := GetDoneItem;
  173.   end;
  174.   // FStream._Release;
  175.   FReaderLock.Free;
  176.   FListsLock.Free;
  177.   FWorkList.Free;
  178.   FDoneList.Free;
  179.   FWorkEvent.Free;
  180.   FDoneEvent.Free;
  181.   FAllDoneEv.Free;
  182.   FTimerPlay.Free;
  183.   FTimerPause.Free;
  184. end;
  185. destructor TAsyncIO.Destroy;
  186. var
  187.   Req: PAsyncRequest;
  188. begin
  189.   GFExit := true;
  190.   FStop := true;
  191.   FThread.Terminate;
  192.   FWorkEvent.SetEv;
  193.   FThread.WaitFor;
  194.   FThread.Free;
  195.   Req := GetDoneItem;
  196.   while Req <> nil do
  197.   begin
  198.     Dispose(Req);
  199.     Req := GetDoneItem;
  200.   end;
  201.   FStream := nil;
  202.   FReaderLock.Free;
  203.   FListsLock.Free;
  204.   FWorkList.Free;
  205.   FDoneList.Free;
  206.   FWorkEvent.Free;
  207.   FDoneEvent.Free;
  208.   FAllDoneEv.Free;
  209.   inherited destroy;
  210. end;
  211. function TAsyncIO.BeginFlush: HRESULT;
  212. var
  213.   Req: PAsyncRequest;
  214. begin
  215.   GFExit := true;
  216.   { need to nil here IMediaControl,
  217.     if not, the destructor in TFilter will not executed }
  218.   FMediaControl := nil;
  219.   FListsLock.Lock;
  220.   Result := S_OK;
  221.   // we nil here and in the filter destructor
  222.   if g_threadedShoutCastStream <> nil then
  223.   begin
  224.     g_threadedShoutCastStream.Destroy;
  225.     g_threadedShoutCastStream := nil;
  226.   end;
  227.   if GFStringQueue <> nil then
  228.   begin
  229.     GFStringQueue.destroy;
  230.     GFStringQueue := nil;
  231.   end;
  232.   try
  233.     FFlushing := true;
  234.     Req := GetWorkItem;
  235.     while Req <> nil do
  236.     begin
  237.       PutDoneItem(Req);
  238.       Req := GetWorkItem;
  239.     end;
  240.     if FOutCount > 0 then
  241.     begin
  242.       Assert(not FWaiting);
  243.       FAllDoneEv.Reset;
  244.       FWaiting := true;
  245.     end
  246.     else
  247.     begin
  248.       FDoneEvent.SetEv;
  249.       FWorkEvent.SetEv;
  250.     end;
  251.   finally
  252.     FListsLock.UnLock;
  253.   end;
  254.   //Assert(FWaiting);
  255.   while FWaiting do
  256.   begin
  257.     FAllDoneEv.Wait();
  258.     FListsLock.Lock;
  259.     try
  260.       if FOutCount = 0 then
  261.       begin
  262.         FWaiting := false;
  263.         FDoneEvent.SetEv;
  264.       end;
  265.     finally
  266.       FListsLock.UnLock;
  267.     end;
  268.   end;
  269. end;
  270. function TAsyncIO.EndFlush: HRESULT;
  271. begin
  272.   GFExit := true;
  273.   FListsLock.Lock;
  274.   FFlushing := false;
  275.   Assert(not FWaiting);
  276.   if FDoneList.Count > 0 then
  277.     FDoneEvent.SetEv
  278.   else
  279.     FDoneEvent.Reset;
  280.   Result := S_OK;
  281.   FListsLock.UnLock;
  282. end;
  283. procedure TAsyncIO.Process;
  284. var
  285.   Req: PAsyncRequest;
  286. begin
  287.   while true do
  288.   begin
  289.     FWorkEvent.Wait;
  290.     FListsLock.Lock;
  291.     Req := GetWorkItem;
  292.     if Req <> nil then
  293.       Inc(FOutCount);
  294.     FListsLock.UnLock;
  295.     if Req <> nil then
  296.     begin
  297.       CompleteRequest(Req);
  298.       FListsLock.Lock;
  299.       PutDoneItem(Req);
  300.       Dec(FOutCount);
  301.       if (FOutCount = 0) and FWaiting then
  302.         FAllDoneEv.SetEv;
  303.       FListsLock.UnLock;
  304.     end;
  305.     if FStop then
  306.       break;
  307.   end;
  308. end;
  309. function TAsyncIO.DoRequest(
  310.   llPos: LONGLONG; lLength: Integer; bAligned: BOOL; pBuffer,
  311.   pContext: Pointer; dwUser: DWORD): HResult;
  312. var
  313.   Req: PAsyncRequest;
  314. begin
  315.   Req := CreateRequest(llPos, lLength, bAligned, pBuffer, pContext, dwUser);
  316.   Result := PutWorkItem(Req);
  317.   if not Succeeded(Result) then
  318.     Dispose(Req);
  319. end;
  320. function TAsyncIO.DoWaitForNext(dwTimeout: DWORD; var ppContext: Pointer;
  321.   var pdwUser: DWORD; var pcbActual: Integer): HRESULT;
  322. var
  323.   Req: PAsyncRequest;
  324. begin
  325.   Result := S_OK;
  326.   ppContext := nil;
  327.   pdwUser := 0;
  328.   pcbActual := 0;
  329.   while true do
  330.   begin
  331.     if (not FDoneEvent.Wait(dwTimeout)) then
  332.     begin
  333.       Result := VFW_E_TIMEOUT;
  334.       Break;
  335.     end;
  336.     Req := GetDoneItem;
  337.     if Req <> nil then
  338.     begin
  339.       ppContext := Req.FContext;
  340.       pdwUser := Req.FUser;
  341.       pcbActual := Req.FLength;
  342.       Result := Req.Fhr;
  343.       Dispose(Req);
  344.       Break;
  345.     end
  346.     else
  347.     begin
  348.       FListsLock.Lock;
  349.       try
  350.         if FFlushing {and not FWaiting} then
  351.         begin
  352.           Result := VFW_E_WRONG_STATE;
  353.           Break;
  354.         end;
  355.       finally
  356.         FListsLock.UnLock;
  357.       end;
  358.     end;
  359.   end;
  360. end;
  361. procedure TAsyncIO.OnTimerPlay(Sender: TObject);
  362. begin
  363.   if FMediaControl <> nil then
  364.     FMediaControl.Run;
  365.   FTimerPlay.Enabled := false;
  366. end;
  367. procedure TAsyncIO.OnTimerPause(Sender: TObject);
  368. begin
  369.   if FMediaControl <> nil then
  370.     FMediaControl.Pause;
  371.   FTimerPause.Enabled := false;
  372. end;
  373. function TAsyncIO.SyncRead(llPosition: int64; lLength: Longint;
  374.   pBuffer: Pbyte): HResult;
  375. var
  376.   Req: PAsyncRequest;
  377.   DataWritten: boolean;
  378.   i: integer;
  379.   StringStream: TStringStream;
  380.   Buffer: string;
  381.   Tempbuffer: string;
  382.   Avdata: int64;
  383.   Application: TApplication;
  384.   Buffering: boolean;
  385.   Count: integer;
  386. begin
  387.   // we do not accept a Nil buffer
  388.   if pBuffer = nil then
  389.   begin
  390.     result := E_FAIL;
  391.     exit;
  392.   end;
  393.   Result := S_OK;
  394.   // the URL buffer control for Dirctshow is added here
  395.   // buffering during the playback
  396.   if FURLMode then
  397.   begin
  398.     // the min. buffersize must be equal to the requested length
  399.     if GFBufferSize < lLength then
  400.       GFBufferSize := lLength;
  401.     // Mpeg1 splitter requests same samples during connection process and
  402.     // after starting the graph.
  403.     StringStream := nil;
  404.     GFStreamPos := llPosition;
  405.     DataWritten := false;
  406.     Buffer := '';
  407.     Tempbuffer := '';
  408.     Avdata := 0;
  409.     Buffering := false;
  410.     Count := 0;
  411.     Application := TApplication.Create(nil);
  412.     if not GFConnected then
  413.     begin
  414.       if assigned(GFFilterCallBack) then
  415.         GFFilterCallBack.AsyncExFilterState(false, false, true, false, 0);
  416.       // since XP ServicePack2 rc2 the mpeg splitter requests a end sample
  417.       // of the stream during pin connection process,
  418.       // we skip this sample because we can't send it
  419.       if (llPosition > (GCFInt64max - lLength - 2)) then
  420.       begin
  421.         result := E_FAIL;
  422.         exit;
  423.       end;
  424.       i := 0;
  425.       if GFStringQueue = nil then
  426.       begin
  427.         result := E_FAIL;
  428.         exit;
  429.       end;
  430.       while not Datawritten do
  431.       begin
  432.         if GFStringQueue <> nil then
  433.           Count := GFStringQueue.getcount;
  434.         if ((GFExit) or (GFStringQueue = nil) or (Count <= i)) then
  435.         begin
  436.           Application.Destroy;
  437.           if g_threadedShoutCastStream <> nil then
  438.           begin
  439.             g_threadedShoutCastStream.Destroy;
  440.             g_threadedShoutCastStream := nil;
  441.           end;
  442.           if GFStringQueue <> nil then
  443.           begin
  444.             if assigned(GFFilterCallBack) then
  445.               GFFilterCallBack.AsyncExSockError('Your prebuffer is too small for the pin connection process. Raise the pebuffer!')
  446.             else
  447.               ShowMessage('TAsyncIO.SyncRead: Your prebuffer is too small for the pin connection process. Raise the prebuffer!');
  448.             GFStringQueue.Destroy;
  449.             GFStringQueue := nil;
  450.           end;
  451.           result := E_FAIL;
  452.           exit;
  453.         end;
  454.         Buffer := Buffer + GFStringQueue.getitem(i);
  455.         inc(i);
  456.         if (llPosition + lLength <= Avdata) then
  457.         begin
  458.           StringStream := TStringStream.Create(Buffer);
  459.           StringStream.Position := llPosition;
  460.           Result := StringStream.Read(pBuffer^, lLength);
  461.           freeandnil(StringStream);
  462.           break;
  463.         end
  464.         else
  465.           Avdata := system.length(Buffer);
  466.       end;
  467.     end
  468.     else
  469.     begin
  470.       if assigned(GFFilterCallBack) then
  471.         GFFilterCallBack.AsyncExFilterState(false, false, false, true, 0);
  472.       while not Datawritten do
  473.       begin
  474.         // we need to free some cpu time for other processes -> sleep(1)
  475.         Sleep(1);
  476.         if GFExit then
  477.         begin
  478.           result := E_FAIL;
  479.           Application.destroy;
  480.           if GFStringQueue <> nil then
  481.           begin
  482.             GFStringQueue.Destroy;
  483.             GFStringQueue := nil;
  484.           end;
  485.           exit;
  486.         end;
  487.         while not Buffering do
  488.         begin
  489.           // we need to free some cpu time for other processes -> sleep(1)
  490.           Sleep(1);
  491.           if GFExit then
  492.           begin
  493.             result := E_FAIL;
  494.             Application.destroy;
  495.             if GFStringQueue <> nil then
  496.             begin
  497.               GFStringQueue.Destroy;
  498.               GFStringQueue := nil;
  499.             end;
  500.             exit;
  501.           end;
  502.           Application.ProcessMessages;
  503.           // we needed to process the onsock read events
  504.         // during waiting for the data
  505.           while (llength > Avdata) do
  506.           begin
  507.             // we need to free some cpu time for other processes -> sleep(1)
  508.             Sleep(1);
  509.             if GFExit then
  510.             begin
  511.               result := E_FAIL;
  512.               Application.destroy;
  513.               if GFStringQueue <> nil then
  514.               begin
  515.                 GFStringQueue.Destroy;
  516.                 GFStringQueue := nil;
  517.               end;
  518.               exit;
  519.             end;
  520.             Application.ProcessMessages;
  521.             // we needed to process the onsock read events
  522.           // during waiting for the data
  523.             if GFStringQueue.getcount > 0 then
  524.             begin
  525.               Buffer := Buffer + GFStringQueue.pop;
  526.               Avdata := system.length(Buffer);
  527.             end
  528.             else
  529.             begin
  530.               Buffering := true;
  531.               if (FTimerPause <> nil) then
  532.                 FTimerPause.Enabled := true;
  533.               break;
  534.             end;
  535.           end;
  536.           if (llength <= Avdata) then
  537.           begin
  538.             StringStream := TStringStream.Create(Buffer);
  539.             StringStream.Position := 0;
  540.             Result := StringStream.Read(pBuffer^, llength);
  541.             freeandnil(StringStream);
  542.             if (Avdata - llength > 0) then
  543.             begin
  544.               Tempbuffer := copy(Buffer, llength + 1, system.length(Buffer));
  545.               GFStringQueue.InsertItem(Tempbuffer, 0);
  546.             end;
  547.             Application.Destroy;
  548.             if assigned(GFFilterCallBack) then
  549.               GFFilterCallBack.AsyncExFilterState(false, false, false, true, 0);
  550.             // we can not call Fmediacontrol.play directly at this point,
  551.             // because destroy in uniot Filter won't called if we do,
  552.             // so we call the Fmediacontrol.play via a timer control
  553.             if (FTimerPlay <> nil) then
  554.               FTimerPlay.Enabled := true;
  555.             exit;
  556.           end;
  557.         end;
  558.         if assigned(GFFilterCallBack) then
  559.           GFFilterCallBack.AsyncExFilterState(true, false, false, false,
  560.             trunc((Avdata * 100) / (GFBufferSize)));
  561.         if GFStringQueue.getcount > 0 then
  562.           Buffer := Buffer + GFStringQueue.pop;
  563.         Avdata := system.length(Buffer);
  564.         if ((GFBufferSize) <= Avdata) then
  565.         begin
  566.           if assigned(GFFilterCallBack) then
  567.             GFFilterCallBack.AsyncExFilterState(true, false, false, false, 100);
  568.           StringStream := TStringStream.Create(Buffer);
  569.           StringStream.Position := 0;
  570.           Result := StringStream.Read(pBuffer^, llength);
  571.           freeandnil(StringStream);
  572.           if (Avdata - llength > 0) then
  573.           begin
  574.             Tempbuffer := copy(Buffer, llength + 1, system.length(Buffer));
  575.             GFStringQueue.InsertItem(Tempbuffer, 0);
  576.           end;
  577.           if assigned(GFFilterCallBack) then
  578.             GFFilterCallBack.AsyncExFilterState(false, false, false, true, 0);
  579.           if (FTimerPlay <> nil) then
  580.             FTimerPlay.Enabled := true;
  581.           break;
  582.         end;
  583.       end;
  584.     end;
  585.     Application.Destroy;
  586.   end
  587.   else
  588.   begin
  589.     FListsLock.Lock;
  590.     try
  591.       if FFlushing then
  592.         Result := VFW_E_WRONG_STATE
  593.       else
  594.       begin
  595.         Req := CreateRequest(llPosition, lLength, false, pBuffer, nil, 0);
  596.         CompleteRequest(Req);
  597.         Result := Req.Fhr;
  598.         Dispose(Req);
  599.       end;
  600.     finally
  601.       FListsLock.UnLock;
  602.     end;
  603.   end;
  604. end;
  605. function TAsyncIO.PutWorkItem(AItem: PAsyncRequest): HRESULT;
  606. begin
  607.   FListsLock.Lock;
  608.   try
  609.     if FFlushing then
  610.       Result := VFW_E_WRONG_STATE
  611.     else
  612.     begin
  613.       FWorkList.Push(AItem);
  614.       FWorkEvent.SetEv;
  615.       Result := S_OK;
  616.     end;
  617.   finally
  618.     FListsLock.UnLock;
  619.   end;
  620. end;
  621. function TAsyncIO.GetWorkItem: PAsyncRequest;
  622. begin
  623.   FListsLock.Lock;
  624.   Result := FWorkList.Pop;
  625.   if FWorkList.Count = 0 then
  626.     FWorkEvent.Reset;
  627.   FListsLock.UnLock;
  628. end;
  629. function TAsyncIO.GetDoneItem: PAsyncRequest;
  630. begin
  631.   FListsLock.Lock;
  632.   Result := FDoneList.Pop;
  633.   if (FDoneList.Count = 0) and (not FFlushing or FWaiting) then
  634.     FDoneEvent.Reset;
  635.   FListsLock.UnLock;
  636. end;
  637. procedure TAsyncIO.PutDoneItem(AItem: PAsyncRequest);
  638. begin
  639.   Assert(FListsLock.CritCheckIn);
  640.   FDoneList.Push(AItem);
  641.   FDoneEvent.SetEv;
  642. end;
  643. function TAsyncIO.Length(out pTotal, pAvailable: int64): HResult;
  644. begin
  645.   FReaderLock.Lock;
  646.   try
  647.     if FURLMode then
  648.     begin
  649.       // we return the max int64 value
  650.       pTotal := GCFInt64max;
  651.       GFStreamLength := pTotal;
  652.       FStrmSize := pTotal;
  653.       Result := S_OK; //VFW_S_ESTIMATED;
  654.     end
  655.     else
  656.     begin
  657.       if FStrmSize = 0 then
  658.         InitStreamLen;
  659.       pTotal := FStrmSize;
  660.       GFStreamLength := FStrmSize;
  661.       pAvailable := pTotal;
  662.       Result := S_OK;
  663.       exit;
  664.     end;
  665.   finally
  666.     FReaderLock.UnLock;
  667.   end;
  668. end;
  669. function TAsyncIO.SetPosition(const APos: Int64): HResult;
  670. var
  671.   CPos: Int64;
  672. begin
  673.   FReaderLock.Lock;
  674.   Result := S_OK;
  675.   try
  676.     if FStrmSize = 0 then
  677.       InitStreamLen;
  678.     CPos := GetStreamPos;
  679.     if not FFwdStream then
  680.     try
  681.       if CPos <> APos then
  682.         Result := SetStreamPos(APos);
  683.     except
  684.       //sometimes it's not working
  685.       //try from the begining
  686.       Result := S_FALSE;
  687.     end
  688.     else
  689.     begin
  690.       try
  691.         if Apos <> CPos then
  692.         begin
  693.           if APos < CPos then
  694.             SetStreamPos(0);
  695.           Result := SetStreamPos(APos);
  696.         end;
  697.       except
  698.         Result := S_FALSE;
  699.       end;
  700.     end;
  701.   finally
  702.     FReaderLock.UnLock;
  703.   end;
  704. end;
  705. procedure TAsyncIO.InitStreamLen;
  706. begin
  707.   if not FFwdStream then
  708.   try
  709.     FFwdStream := FStream.Seek(0, STREAM_SEEK_END, FStrmSize) <> S_OK;
  710.   except
  711.     FStrmSize := 0;
  712.     FFwdStream := true;
  713.   end;
  714.   if FFwdStream then
  715.   try
  716.     SetStreamPos(0);
  717.     FStrmSize := 32768;
  718.     try
  719.       while SetStreamPos(FStrmSize) = S_OK do
  720.         FStrmSize := 2 * FStrmSize;
  721.     except
  722.     end;
  723.     FStrmSize := GetStreamPos;
  724.     SetStreamPos(0);
  725.   except
  726.     FStrmSize := 10000; //fake
  727.   end;
  728. end;
  729. function TAsyncIO.GetStreamPos: Int64;
  730. begin
  731.   FStream.Seek(0, STREAM_SEEK_CUR, Result);
  732.   GFStreamPos := Result;
  733. end;
  734. function TAsyncIO.SetStreamPos(const APos: Int64): HResult;
  735. var
  736.   NewPos: Int64;
  737. begin
  738.   Result := FStream.Seek(APos, STREAM_SEEK_SET, NewPos);
  739. end;
  740. procedure TAsyncIO.CompleteRequest(Req: PAsyncRequest);
  741. var
  742.   R: integer;
  743. begin
  744.   FReaderLock.Lock;
  745.   with Req^ do
  746.   try
  747.     Fhr := SetPosition(FPos);
  748.     R := 0;
  749.     if Fhr = S_OK then
  750.     begin
  751.       Fhr := FStream.Read(FBuffer, FLength, @R);
  752.       if FLength <> R then
  753.       begin
  754.         Fhr := S_FALSE;
  755.         FLength := R;
  756.       end;
  757.     end;
  758.   finally
  759.     FReaderLock.UnLock;
  760.   end;
  761. end;
  762. function TAsyncIO.CreateRequest(
  763.   llPos: LONGLONG; lLength: Integer; bAligned: BOOL; pBuffer,
  764.   pContext: Pointer; dwUser: DWORD): PAsyncRequest;
  765. begin
  766.   New(Result);
  767.   with Result^ do
  768.   begin
  769.     FPos := llPos;
  770.     FAligned := bAligned;
  771.     FLength := lLength;
  772.     FBuffer := pBuffer;
  773.     FContext := pContext;
  774.     FUser := dwUser;
  775.     Fhr := VFW_E_TIMEOUT;
  776.   end;
  777. end;
  778. function TAsyncIO.InitAllocator(out Alloc: IMemAllocator): HRESULT;
  779. begin
  780.   Result := CoCreateInstance(CLSID_MemoryAllocator, nil, CLSCTX_INPROC_SERVER,
  781.     IID_IMemAllocator, Alloc);
  782. end;
  783. function TAsyncIO.WaitForNext(dwTimeout: DWORD; out ppSample: IMediaSample;
  784.   out pdwUser: DWORD): HResult;
  785. var
  786.   cbActual: Longint;
  787. begin
  788.   result := DoWaitForNext(dwTimeout, Pointer(ppSample), pdwUser, cbActual);
  789. end;
  790. function TAsyncIO.RequestAllocator(pPreferred: IMemAllocator;
  791.   pProps: PAllocatorProperties; out ppActual: IMemAllocator): HResult; stdcall;
  792. var
  793.   P, PA: TAllocatorProperties;
  794. begin
  795.   P := pProps^;
  796.   P.cbAlign := 1;
  797.   if pPreferred <> nil then
  798.   begin
  799.     Result := pPreferred.SetProperties(P, PA);
  800.     if Succeeded(Result) and (P.cbAlign = PA.cbAlign) then
  801.     begin
  802.       ppActual := pPreferred;
  803.       exit;
  804.     end;
  805.   end;
  806.   InitAllocator(ppActual);
  807.   Result := ppActual.SetProperties(P, PA);
  808.   if Succeeded(Result) and (P.cbAlign = PA.cbAlign) then
  809.   begin
  810.     Result := S_OK;
  811.     exit;
  812.   end;
  813.   if Succeeded(Result) then
  814.     Result := VFW_E_BADALIGN;
  815.   ppActual := nil;
  816. end;
  817. function TAsyncIO.SyncReadAligned(pSample: IMediaSample): HResult;
  818. var
  819.   T1, T2: TReferenceTime;
  820.   Start, Total: LONGLONG;
  821.   Length: Longint;
  822.   Buffer: PByte;
  823. begin
  824.   pSample.GetTime(T1, T2);
  825.   if not FURLMode then
  826.     Self.Length(Total, Start)
  827.   else
  828.     Buffer := nil;
  829.   Start := T1 div NANOSECONDS;
  830.   Length := (T2 - T1) div NANOSECONDS;
  831.   if not FURLMode then
  832.     if Start + Length > Total then
  833.     begin
  834.       Length := Total - Start;
  835.       T2 := Total * NANOSECONDS;
  836.       pSample.SetTime(@T1, @T2);
  837.     end;
  838.   Result := pSample.GetPointer(Buffer);
  839.   if (FAILED(Result)) then
  840.     exit;
  841.   Result := SyncRead(Start, Length, Buffer);
  842. end;
  843. function TAsyncIO.Request(pSample: IMediaSample; dwUser: DWORD): HResult;
  844. var
  845.   T1, T2: TReferenceTime;
  846.   Start, Total: LONGLONG;
  847.   Length: Longint;
  848.   Buffer: PByte;
  849. begin
  850.   pSample.GetTime(T1, T2);
  851.   self.Length(Total, Start);
  852.   Start := T1 div NANOSECONDS;
  853.   Length := (T2 - T1) div NANOSECONDS;
  854.   if Start + Length > Total then
  855.   begin
  856.     Length := Total - Start;
  857.     T2 := Total * NANOSECONDS;
  858.     pSample.SetTime(@T1, @T2);
  859.   end;
  860.   Result := pSample.GetPointer(Buffer);
  861.   if (FAILED(Result)) then
  862.     exit;
  863.   Result := DoRequest(Start, Length,
  864.     false, Buffer, Pointer(pSample), dwUser);
  865. end;
  866. end.