/composants/tmed_tvideocapture/VCap.pas

http://toutenvrac.googlecode.com/ · Pascal · 1899 lines · 1390 code · 216 blank · 293 comment · 0 complexity · 4f67d29b1f44a61c0250b4a46ecf6a8b MD5 · raw file

  1. // ******************************************************************************
  2. // TVideoCapture - wrapper for DirectShow video capture functions
  3. // Based on
  4. // Microsoft's AMCap & StillCap
  5. // and
  6. // DScapture by orthkon * www.mp3.com/orthkon * orthkon@mail.com
  7. //
  8. // written by Egor Averchenkov, 2001-2002,
  9. // e_g_o_r@mail.ru
  10. //
  11. // version 1.15
  12. // ******************************************************************************
  13. // License Agreement
  14. //
  15. // Permission to use, copy, modify, and distribute this software and its
  16. // documentation for any purpose and without fee is hereby granted,
  17. // provided that the above copyright notice appears in all copies and
  18. // that both the above copyright notice and this permission notice appear
  19. // in supporting documentation, and that the name the author
  20. // not be used in advertising or publicity pertaining to distribution of
  21. // the software without specific, written prior permission. This
  22. // software is made available "as is", and AUTHOR DISCLAIM
  23. // ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
  24. // INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  25. // AND FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL AUTHORS BE
  26. // LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  27. // DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  28. // WHETHER IN AN ACTION OF CONTRACT, TORT (INCLUDING NEGLIGENCE) OR
  29. // STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  30. // PERFORMANCE OF THIS SOFTWARE.
  31. //
  32. // In other words - feel free to use this code)))
  33. // I hope someone find it usefull. If you do - send me a message, pls.
  34. // (Beer messages would be appreciated a lot!!! :o) )
  35. // ******************************************************************************
  36. // History log:
  37. // 01-04-24 - v1.00 (initial release)
  38. // 01-09-18 - v1.02 - I had to continue this work after a long delay
  39. // some stupid bugs corrected
  40. // interface section was slightly ordered
  41. // new property CapturePixelFormat added to select color depth of captured bitmaps
  42. // 01-09-20 - v1.03 - orthkon mailed me TCapture based on this component - good work!
  43. // added properties and functions to work with available capture modes
  44. // 01-09-22 - v1.04
  45. // working with filters property pages
  46. // 01-10-18 - v1.05
  47. // working with compressors if ParCam[1].Vignette.Visible then if ParCam[1].Vignette.Visible then
  48. // 01-10-27 - v1.06 - thanks to Lee_Nover (Lee.Nover@email.si) - he did all dirty work))))
  49. // one graph for preview/capture
  50. // compression dialogs
  51. // 01-10-28 - v1.07
  52. // bugs in building graph with compressors were corrected
  53. // 01-11-23 - v1.08
  54. // completely rewriten graph building/controlling functions
  55. // new properties:
  56. // IsDV - Indicates DV Capture source is used
  57. // WantDVAudio - Force using DV Audio stream to capture audio
  58. // when DV device is used for capturing
  59. // Sorry but I've changed some names(((
  60. // 01-11-25 - v1.09
  61. // saving/restoring capture graph configuration
  62. // 02-05-25 - v1.10
  63. // some bugs corrected
  64. // graph restoring/building algorithms slightly ordered
  65. // new properties:
  66. // VCompDevice - name of video compressor used
  67. // ACompDevice - name of audio compressor used
  68. // VCapDevice - name of video capture device used
  69. // ACapDevice - name of audio capture device used
  70. // VCapIndex - index of video capture device used
  71. // ACapIndex - index of audio capture device used
  72. // VCompIndex - index of video compressor device used
  73. // ACompIndex - index of audio compressor device used
  74. // ACapModeIdx - index of audio capture mode used
  75. // VCapModeIdx - index of video capture mode used
  76. // 02-10-10 - v1.11
  77. // 1. added SampleGrabber buffer locking functions to avoid
  78. // simultaneous access from different threads to buffer data
  79. // 2. added OnFramePassed event firing every time frame comes
  80. // through SampleGrabber
  81. // 02-10-13 - v1.12
  82. // only BCB support added to sources
  83. // 02-10-16 - v1.13
  84. // added WantAudioPreview property to disable audio preview rendering
  85. // added OnAborted event to indicate graph run aborted for some reason
  86. // added OnDeviceLost event to indicate capture device lost during preview/capture
  87. // some optimization done
  88. // 02-10-20 - v1.14
  89. // some bugs in working with the capture and compressor lists corrected
  90. // 02-11-03 - v1.15
  91. // bugs when working with DV capture devices corrected
  92. // bug in rendering graph with WantAudioPreview property corrected
  93. // bug in working with the capture and compressor lists corrected
  94. // ******************************************************************************
  95. // Known problems.
  96. // Some audio compressor filters do not expose IAMStreamControl
  97. // (it's pitty but ACM Wrapper does not((( ) so component can't correctly
  98. // start/stop capture part of graph using such filters.
  99. // It's recomended to use native DShow audio compressors to capture avi.
  100. // ******************************************************************************
  101. Unit VCap;
  102. Interface
  103. // -----------------------------------------------------------------------------
  104. // BCB support
  105. {$NOINCLUDE ActiveX}
  106. {$NOINCLUDE DirectShowVC}
  107. {$HPPEMIT '/* don''t use DirectShow.pas in C++ sources - use native C++ headers!!! */' }
  108. {$HPPEMIT '#define Directshow ' }
  109. {$HPPEMIT '#include <strmif.h>' }
  110. {$HPPEMIT '#include <control.h>' }
  111. {$HPPEMIT 'typedef System::DelphiInterface<IGraphBuilder> _di_IGraphBuilder;'}
  112. {$HPPEMIT 'typedef System::DelphiInterface<ICaptureGraphBuilder2> _di_ICaptureGraphBuilder2;' }
  113. {$HPPEMIT 'typedef System::DelphiInterface<IVideoWindow> _di_IVideoWindow;' }
  114. {$HPPEMIT 'typedef System::DelphiInterface<IMediaEventEx> _di_IMediaEventEx;' }
  115. {$HPPEMIT 'typedef System::DelphiInterface<IAMDroppedFrames> _di_IAMDroppedFrames;' }
  116. {$HPPEMIT 'typedef System::DelphiInterface<IAMVfwCaptureDialogs> _di_IAMVfwCaptureDialogs;' }
  117. {$HPPEMIT 'typedef System::DelphiInterface<IAMVfwCompressDialogs> _di_IAMVfwCompressDialogs;' }
  118. {$HPPEMIT 'typedef System::DelphiInterface<IAMStreamConfig> _di_IAMStreamConfig;' }
  119. {$HPPEMIT 'typedef System::DelphiInterface<IBaseFilter> _di_IBaseFilter;' }
  120. {$HPPEMIT 'typedef System::DelphiInterface<IFileSinkFilter> _di_IFileSinkFilter;' }
  121. {$HPPEMIT 'typedef System::DelphiInterface<IConfigAviMux> _di_IConfigAviMux;' }
  122. {$HPPEMIT 'typedef System::DelphiInterface<IUnknown> _di_ISampleGrabber; // to use ISampleGrabber include <qedit.h>' }
  123. Uses
  124. Windows, Messages, SysUtils, Classes, Graphics, Controls, extctrls,
  125. ActiveX, DirectShowVC;
  126. // -----------------------------------------------------------------------------
  127. // Windows messages
  128. Const
  129. WM_FGNOTIFY = WM_USER + 1;
  130. WM_SAMPLECAPTURED = WM_USER + 2;
  131. WM_SAMPLEPASSED = WM_USER + 3;
  132. // message WM_SAMPLECAPTURED
  133. Type
  134. TWMSampleCaptured = Packed Record
  135. Msg: Cardinal;
  136. GrabberCB: TObject;
  137. LParam: Longint;
  138. Result: Longint;
  139. End;
  140. // -----------------------------------------------------------------------------
  141. Type
  142. EVideoCaptureError = Class(Exception);
  143. Type
  144. TCapturedBitmap = Class(TBitmap); // to eliminate some problems with BCB
  145. Type
  146. TBitmapCapturedEvent = Procedure(CapturedImage: TCapturedBitmap) Of Object;
  147. Type
  148. TDVResolution = (dvrDontWorry, dvrFull, dvrHalf, dvrQuater, dvrDC);
  149. // -----------------------------------------------------------------------------
  150. // Working with capture modes
  151. Type
  152. TVCapMode = Record
  153. MediaType: TGUID;
  154. MediaSubType: TGUID;
  155. Width: integer;
  156. Height: integer;
  157. BitCount: integer;
  158. FrameRate, MinFrameRate, MaxFrameRate: double;
  159. End;
  160. Type
  161. TACapMode = Record
  162. MediaType: TGUID;
  163. MediaSubType: TGUID;
  164. SampleRate: integer;
  165. SampleSize: integer;
  166. Channels: integer;
  167. AvgSampleRate: integer;
  168. End;
  169. Function IsEqualModes(Const mode1, mode2: TVCapMode): boolean; Overload;
  170. Function IsEqualModes(Const mode1, mode2: TACapMode): boolean; Overload;
  171. Function GetModeString(Const mode: TVCapMode): String; Overload;
  172. Function GetModeString(Const mode: TACapMode): String; Overload;
  173. // -----------------------------------------------------------------------------
  174. // Various Capture Dialog Types
  175. Type
  176. TCaptureDialog = (cdVFORMAT, cdVSOURCE, cdVDISPLAY, cdVCAPTURE, cdVCROSSBAR, cdTVTUNER, cdACAPTURE, cdACROSSBAR,
  177. cdTVAUDIO, cdVCAPTURE_PIN, cdVPREVIEW_PIN, cdACAPTURE_PIN, cdVCOMPRESSION, cdACOMPRESSION);
  178. TCaptureDialogs = Set Of TCaptureDialog;
  179. // -----------------------------------------------------------------------------
  180. // TVideoCapture component
  181. Type
  182. TGraphConfig = Class;
  183. TVideoCapture = Class(TCustomControl)
  184. Private
  185. Graph: IGraphBuilder;
  186. Builder: ICaptureGraphBuilder2;
  187. VideoWindow: IVideoWindow;
  188. MediaEvent: IMediaEventEx;
  189. DroppedFrames: IAMDroppedFrames;
  190. CaptureDialogs: IAMVfwCaptureDialogs;
  191. CompressDialogs: IAMVfwCompressDialogs;
  192. AStreamConf: IAMStreamConfig; // for audio capture
  193. VStreamConf: IAMStreamConfig; // for video capture
  194. Render: IBaseFilter; // file writer
  195. VCap, ACap, // capture filters
  196. VComp, AComp, // compressor filters
  197. DVSplitter, DVDec: IBaseFilter; // filters to use with DV devices
  198. Sink: IFileSinkFilter;
  199. NullRenderer: IBaseFilter;
  200. ConfigAviMux: IConfigAviMux;
  201. Grabber: ISampleGrabber;
  202. GrabberCB: TObject;
  203. FCCAvail: boolean;
  204. FCapCC: boolean;
  205. FGraphBuilt: boolean;
  206. FACapFriendlyName, FVCapFriendlyName: String;
  207. FCapturing: boolean;
  208. FPreviewing: boolean;
  209. FUseFrameRate: boolean;
  210. FUseTimeLimit: boolean;
  211. FFrameRate: double;
  212. FCapStartTime: DWORD;
  213. FCapStopTime: DWORD;
  214. FMasterStream: integer;
  215. FNotDropped: integer;
  216. FDroppedFrames: integer;
  217. FNotDroppedBase: integer;
  218. FDroppedBase: integer;
  219. FCapTime: DWORD;
  220. mVideo, mAudio, mVComp, mAComp: IMoniker;
  221. FCaptureTimer: TTimer;
  222. FDialogs: TCaptureDialogs;
  223. FDoPreallocFile: boolean;
  224. FTempCaptureFileName: String;
  225. FCaptureFileName: String;
  226. FTimeLimit: integer;
  227. FUseTempFile: boolean;
  228. FPreallocFileSize: Cardinal;
  229. FCaptureFile: WideString;
  230. FDVResolution: TDVResolution;
  231. FOnStopPreview: TNotifyEvent;
  232. FOnStartPreview: TNotifyEvent;
  233. FOnStopCapture: TNotifyEvent;
  234. FOnStartCapture: TNotifyEvent;
  235. FOnChangeDevice: TNotifyEvent;
  236. FOnCaptureProgress: TNotifyEvent;
  237. FOnBitmapGrabbed: TBitmapCapturedEvent;
  238. FOnChangeACapMode: TNotifyEvent;
  239. FOnChangeVCapMode: TNotifyEvent;
  240. FOnChangeCompressor: TNotifyEvent;
  241. FOnFrameRendered: TNotifyEvent;
  242. FOnDeviceLost: TNotifyEvent;
  243. FOnAborted: TNotifyEvent;
  244. FPixelFormat: TPixelFormat;
  245. FWantPreview: boolean;
  246. FWantCapture: boolean;
  247. FWantAudio: boolean;
  248. FWantDVAudio: boolean;
  249. FWantBitmaps: boolean;
  250. FWantAudioPreview: boolean;
  251. Procedure SetMasterStream(Const Value: integer);
  252. Property MasterStream: integer Read FMasterStream Write SetMasterStream;
  253. Procedure SetFrameRate(Const Value: double);
  254. Procedure SetTempCaptureFileName(Const Value: String);
  255. Procedure SetCaptureFileName(Const Value: String);
  256. Function GetACapMode: TACapMode;
  257. Function GetACapModeCount: integer;
  258. Function GetACapModes(i: integer): TACapMode;
  259. Function GetVCapMode: TVCapMode;
  260. Function GetVCapModeCount: integer;
  261. Function GetVCapModes(i: integer): TVCapMode;
  262. Function GetIsDVSource: boolean;
  263. Procedure SetDVResolution(Const Value: TDVResolution);
  264. Function GetACapSource: String;
  265. Function GetVCapSource: String;
  266. Function GetACompDevice: String;
  267. Function GetVCompDevice: String;
  268. Function GetACapSourceIdx: integer;
  269. Function GetACompDeviceIdx: integer;
  270. Function GetVCapSourceIdx: integer;
  271. Function GetVCompDeviceIdx: integer;
  272. Function GetACapModeIdx: integer;
  273. Function GetVCapModeIdx: integer;
  274. Private
  275. Procedure SetSize(Var Msg: TMessage); Message WM_SIZE; // Changing size of cap window
  276. Procedure GraphEvent(Var Msg: TMessage); Message WM_FGNOTIFY;
  277. Procedure BitmapGrabbed(Var Msg: TWMSampleCaptured); Message WM_SAMPLECAPTURED;
  278. Procedure FramePassed(Var Msg: TMessage); Message WM_SAMPLEPASSED; // sample passed through SampleGrabber
  279. Procedure CaptureProgress(Sender: TObject);
  280. Procedure ResizeWindow;
  281. Procedure UpdateStatus;
  282. Procedure ChooseDevices(nmVideo, nmAudio: IMoniker; ForceGraphRebuild: boolean = false); Overload;
  283. Procedure ChooseCompressors(nmVComp, nmAComp: IMoniker; ForceGraphRebuild: boolean = false); Overload;
  284. Procedure DoAborted(hr: HRESULT);
  285. Procedure DoDeviceLost;
  286. Procedure CleanUp;
  287. Procedure FreeCapFilters;
  288. Function InitCapFilters: boolean;
  289. Function MakeBuilder: boolean;
  290. Function MakeGraph: boolean;
  291. Procedure NukeDownstream(pf: IBaseFilter);
  292. Procedure TearDownGraph;
  293. Function FindVideoWindow: boolean;
  294. Function RenderVideoPreview(Const VSource: IBaseFilter): boolean;
  295. Function BuildGraph: boolean;
  296. Function ControlCaptureStream(start: boolean): boolean;
  297. Function AllocCaptureFile(Const SizeMb: integer): boolean;
  298. Function SaveCaptureFile(Const FileName: String): boolean;
  299. Procedure GetAvailableDialogs;
  300. Public
  301. // if someone wants to work with DShow objects
  302. Property CaptureGraph: IGraphBuilder Read Graph;
  303. Property VCapFilter: IBaseFilter Read VCap;
  304. Property ACapFilter: IBaseFilter Read ACap;
  305. Property VCompFilter: IBaseFilter Read VComp;
  306. Property ACompFilter: IBaseFilter Read AComp;
  307. // graph state properties
  308. Property Capturing: boolean Read FCapturing; // is capturing now
  309. Property Previewing: boolean Read FPreviewing; // is previewing now
  310. // video capture properties
  311. Property VCapFriendlyName: String Read FVCapFriendlyName;
  312. Property VCapName: String Read GetVCapSource; // used video capture device name
  313. Property VCapIndex: integer Read GetVCapSourceIdx;
  314. Property VCapMode: TVCapMode Read GetVCapMode; // current video capture mode
  315. Property VCapModeIdx: integer Read GetVCapModeIdx; // current video capture mode index
  316. Property VCapModeCount: integer Read GetVCapModeCount; // available video capture mode count
  317. Property VCapModes[i: integer]: TVCapMode Read GetVCapModes;
  318. Function SetVCapMode(i: integer): boolean; Overload;
  319. Function SetVCapMode(Const mode: TVCapMode): boolean; Overload;
  320. // audio capture properties
  321. Property ACapFriendlyName: String Read FACapFriendlyName;
  322. Property ACapName: String Read GetACapSource; // used audio capture device name
  323. Property ACapIndex: integer Read GetACapSourceIdx;
  324. Property ACapMode: TACapMode Read GetACapMode; // current audio capture mode
  325. Property ACapModeIdx: integer Read GetACapModeIdx; // current audio capture mode index
  326. Property ACapModeCount: integer Read GetACapModeCount; // available audio capture mode count
  327. Property ACapModes[i: integer]: TACapMode Read GetACapModes;
  328. Function SetACapMode(i: integer): boolean; Overload;
  329. Function SetACapMode(Const mode: TACapMode): boolean; Overload;
  330. // compressors properties
  331. Property VCompName: String Read GetVCompDevice; // used video compressor name
  332. Property VCompIndex: integer Read GetVCompDeviceIdx; // used video compressor index
  333. Property ACompName: String Read GetACompDevice; // used audio compressor name
  334. Property ACompIndex: integer Read GetACompDeviceIdx; // used audio compressor index
  335. // other properties
  336. Property IsDVSource: boolean Read GetIsDVSource; // indicates DV capture source is used
  337. Property Dialogs: TCaptureDialogs Read FDialogs; // available graph dialogs
  338. Property FramesDropped: integer Read FDroppedFrames;
  339. Property NotDropped: integer Read FNotDropped;
  340. Property CapStartTime: DWORD Read FCapStartTime;
  341. Property CapStopTime: DWORD Read FCapStopTime;
  342. Property CapTime: DWORD Read FCapTime;
  343. Procedure ChooseDevices(Const szVideo, szAudio: String; ForceGraphRebuild: boolean = false); Overload;
  344. Procedure ChooseDevices(Const numVideo, numAudio: integer; ForceGraphRebuild: boolean = false); Overload;
  345. Procedure ChooseCompressors(Const szVComp, szAComp: String; ForceGraphRebuild: boolean = false); Overload;
  346. Procedure ChooseCompressors(Const numVComp, numAComp: integer; ForceGraphRebuild: boolean = false); Overload;
  347. Function Init: boolean; // you have to call this routine first!!!
  348. Function StartPreview: boolean;
  349. Function StopPreview: boolean;
  350. Function StartCapture(Const Dialog: boolean = false): boolean;
  351. Function StopCapture: boolean;
  352. Function CaptureFrame: boolean;
  353. Function ShowDialog(Const DialogType: TCaptureDialog): boolean;
  354. Procedure SaveGraph(config: TGraphConfig);
  355. Procedure RestoreGraph(config: TGraphConfig);
  356. Constructor Create(AOwner: TComponent); Override;
  357. Destructor Destroy; Override;
  358. Published
  359. Property DVResolution: TDVResolution Read FDVResolution Write SetDVResolution;
  360. // set to true if you want to capture single frames during preview
  361. Property WantBitmaps: boolean Read FWantBitmaps Write FWantBitmaps;
  362. Property BitmapPixelFormat: TPixelFormat Read FPixelFormat Write FPixelFormat;
  363. Property WantAudio: boolean Read FWantAudio Write FWantAudio; // set true if you want audio in captured file
  364. Property WantDVAudio: boolean Read FWantDVAudio Write FWantDVAudio;
  365. // set true if you want DV audio in captured file
  366. Property WantAudioPreview: boolean Read FWantAudioPreview Write FWantAudioPreview;
  367. // set true if you want render audio preview
  368. Property WantPreview: boolean Read FWantPreview Write FWantPreview;
  369. Property WantCapture: boolean Read FWantCapture Write FWantCapture;
  370. // set to false if dont want capture file is being created
  371. Property UseFrameRate: boolean Read FUseFrameRate Write FUseFrameRate;
  372. Property FrameRate: double Read FFrameRate Write SetFrameRate;
  373. Property CaptureFileName: String Read FCaptureFileName Write SetCaptureFileName;
  374. Property UseTempFile: boolean Read FUseTempFile Write FUseTempFile;
  375. Property TempCaptureFileName: String Read FTempCaptureFileName Write SetTempCaptureFileName;
  376. Property UseTimeLimit: boolean Read FUseTimeLimit Write FUseTimeLimit;
  377. Property TimeLimit: integer Read FTimeLimit Write FTimeLimit;
  378. Property DoPreallocFile: boolean Read FDoPreallocFile Write FDoPreallocFile;
  379. Property PreallocFileSize: Cardinal Read FPreallocFileSize Write FPreallocFileSize Default 10;
  380. Property OnAborted: TNotifyEvent Read FOnAborted Write FOnAborted; // if normal graph run has been aborted
  381. Property OnDeviceLost: TNotifyEvent Read FOnDeviceLost Write FOnDeviceLost; //
  382. Property OnChangeDevice: TNotifyEvent Read FOnChangeDevice Write FOnChangeDevice;
  383. Property OnChangeCompressor: TNotifyEvent Read FOnChangeCompressor Write FOnChangeCompressor;
  384. Property OnChangeVCapMode: TNotifyEvent Read FOnChangeVCapMode Write FOnChangeVCapMode;
  385. Property OnChangeACapMode: TNotifyEvent Read FOnChangeACapMode Write FOnChangeACapMode;
  386. Property OnCaptureProgress: TNotifyEvent Read FOnCaptureProgress Write FOnCaptureProgress;
  387. Property OnStopCapture: TNotifyEvent Read FOnStopCapture Write FOnStopCapture;
  388. Property OnStopPreview: TNotifyEvent Read FOnStopPreview Write FOnStopPreview;
  389. Property OnStartCapture: TNotifyEvent Read FOnStartCapture Write FOnStartCapture;
  390. Property OnStartPreview: TNotifyEvent Read FOnStartPreview Write FOnStartPreview;
  391. Property OnBitmapGrabbed: TBitmapCapturedEvent Read FOnBitmapGrabbed Write FOnBitmapGrabbed;
  392. Property OnFrameRendered: TNotifyEvent Read FOnFrameRendered Write FOnFrameRendered;
  393. Published
  394. Property Align;
  395. Property Color;
  396. Property Visible;
  397. Property OnMouseMove;
  398. Property OnMouseUp;
  399. Property OnMouseDown;
  400. Property OnClick;
  401. Property OnDblClick;
  402. End;
  403. TGraphConfig = Class
  404. Public
  405. VCapSource: String;
  406. ACapSource: String;
  407. VComp: String;
  408. AComp: String;
  409. ACapMode: TACapMode;
  410. VCapMode: TVCapMode;
  411. VCompState: String;
  412. WantCapture: boolean;
  413. WantPreview: boolean;
  414. WantBitmaps: boolean;
  415. WantAudio: boolean;
  416. WantDVAudio: boolean;
  417. WantAudioPreview: boolean;
  418. PixelFormat: TPixelFormat;
  419. DVResolution: TDVResolution;
  420. CaptureFileName: String;
  421. UseTempFile: boolean;
  422. TempCaptureFileName: String;
  423. DoPreallocFile: boolean;
  424. PreallocFileSize: Cardinal;
  425. Procedure Clear;
  426. Procedure RestoreGraph(layout: String);
  427. Function RestoreGraphFromStream(Stream: TStream): boolean;
  428. Function SaveGraph: String;
  429. Function SaveGraphToStream(Stream: TStream): boolean;
  430. Constructor Create;
  431. End;
  432. // device enum functions
  433. // callers have to free aquired lists!!!
  434. Function GetVideoDevicesList(Const Refresh: boolean = false): TStringList;
  435. Function GetAudioDevicesList(Const Refresh: boolean = false): TStringList;
  436. Function GetVideoCompressorsList(Const Refresh: boolean = false): TStringList;
  437. Function GetAudioCompressorsList(Const Refresh: boolean = false): TStringList;
  438. Implementation
  439. Uses VCapStrings, Utils, Dialogs, MMSystem, contnrs, syncobjs;
  440. // -----------------------------------------------------------------
  441. // const
  442. // MAX_TIME: TReference_Time = $7FFFFFFFFFFFFFFF;
  443. // my filter names to find these filters in the graph
  444. Const
  445. VCompFilterName = 'MyVideoCompressor';
  446. ACompFilterName = 'MyAudioCompressor';
  447. DVSplitterFilterName = 'MyDVSplitter';
  448. DVDecoderFilterName = 'MyDVDecoder';
  449. SmartTeeFilterName = 'MySmartTee';
  450. InfTeeFilterName = 'MyInfTee';
  451. SampleGrabberFilterName = 'MyBitmapGrabber';
  452. NullRendererFilterName = 'MyNullRenderer';
  453. Const
  454. IID_IPropertyBag: TGUID = '{55272A00-42CB-11CE-8135-00AA004BB851}';
  455. IID_ISpecifyPropertyPages: TGUID = '{B196B28B-BAB4-101A-B69C-00AA00341D07}';
  456. Const
  457. VfwCompressDialog_Config = 1;
  458. VfwCompressDialog_About = 2;
  459. // returns S_OK if the dialog exists and can be shown, else S_FALSE
  460. VfwCompressDialog_QueryConfig = 4;
  461. VfwCompressDialog_QueryAbout = 8;
  462. Const
  463. AM_FILE_OVERWRITE = 1;
  464. // DV Modes
  465. Const
  466. DVResolutions: Array [TDVResolution] Of integer = (0, DVRESOLUTION_FULL, DVRESOLUTION_HALF, DVRESOLUTION_QUARTER,
  467. DVRESOLUTION_DC);
  468. Type
  469. TDVRes = Record
  470. DVWidth, DVHeight, DVBits: integer;
  471. End;
  472. Const
  473. DVRes: Array [DVRESOLUTION_FULL .. DVRESOLUTION_DC] Of TDVRes = ((DVWidth: 720; DVHeight: 576; DVBits: 24), // Full
  474. (DVWidth: 360; DVHeight: 288; DVBits: 24), // Half
  475. (DVWidth: 180; DVHeight: 144; DVBits: 24), // quater
  476. (DVWidth: 88; DVHeight: 72; DVBits: 24) // DC
  477. );
  478. Const
  479. DVRes1: Array [DVRESOLUTION_FULL .. DVRESOLUTION_DC] Of TDVResolution = (dvrFull, dvrHalf, dvrQuater, dvrDC);
  480. Const
  481. DVCapModesCount = High(DVRes) - Low(DVRes) + 1;
  482. // used to install SampleGrabber
  483. Const
  484. PixelFormatGUIDs: Array [TPixelFormat] Of PGUID = (@MEDIASUBTYPE_RGB24, @MEDIASUBTYPE_RGB1, @MEDIASUBTYPE_RGB4,
  485. @MEDIASUBTYPE_RGB8, @MEDIASUBTYPE_RGB555, @MEDIASUBTYPE_RGB565, @MEDIASUBTYPE_RGB24, @MEDIASUBTYPE_RGB32,
  486. @MEDIASUBTYPE_RGB24);
  487. // -----------------------------------------------------------------
  488. Function MyMsg(szMsg: String; hr: HRESULT): String;
  489. Begin
  490. Result := Format(szMsg, [hr]);
  491. MessageBox(GetForegroundWindow, PChar(Result), PChar(rsDShowCapture), MB_OK Or MB_ICONSTOP);
  492. End;
  493. Procedure ErrMsg(szMsg: String; hr: HRESULT = 0);
  494. Begin
  495. MyMsg(szMsg, hr);
  496. End;
  497. Procedure ErrMsgException(szMsg: String; hr: HRESULT = 0);
  498. Begin
  499. Raise EVideoCaptureError.Create(MyMsg(szMsg, hr));
  500. End;
  501. // -----------------------------------------------------------------
  502. Function CheckGUID(Const p1, p2: TGUID): boolean;
  503. Var
  504. i: byte;
  505. Begin
  506. Result := false;
  507. For i := 0 To 7 Do
  508. If p1.D4[i] <> p2.D4[i] Then
  509. Exit;
  510. Result := (p1.D1 = p2.D1) And (p1.D2 = p2.D2) And (p1.D3 = p2.D3);
  511. End;
  512. // Free an existing media type (ie free resources it holds)
  513. Procedure FreeMediaType(mt: TAM_MEDIA_TYPE);
  514. Begin
  515. If mt.cbFormat <> 0 Then
  516. Begin
  517. CoTaskMemFree(mt.pbFormat);
  518. // Strictly unnecessary but tidier
  519. mt.cbFormat := 0;
  520. mt.pbFormat := Nil;
  521. End;
  522. mt.pUnk := Nil;
  523. End;
  524. Procedure DeleteMediaType(pmt: PAM_MEDIA_TYPE);
  525. Begin
  526. // allow NULL pointers for coding simplicity
  527. If pmt <> Nil Then
  528. Begin
  529. FreeMediaType(pmt^);
  530. CoTaskMemFree(pmt);
  531. End;
  532. End;
  533. // -----------------------------------------------------------------
  534. Function IsEqualModes(Const mode1, mode2: TVCapMode): boolean;
  535. Begin
  536. Result := CheckGUID(mode1.MediaSubType, mode2.MediaSubType) And (mode1.Height = mode2.Height) And
  537. (mode1.Width = mode2.Width) And (mode1.BitCount = mode2.BitCount);
  538. End;
  539. Function IsEqualModes(Const mode1, mode2: TACapMode): boolean;
  540. Begin
  541. Result := CheckGUID(mode1.MediaSubType, mode2.MediaSubType);
  542. If Result Then
  543. Begin
  544. If CheckGUID(mode1.MediaSubType, MEDIASUBTYPE_PCM) Then
  545. Result := (mode1.SampleRate = mode2.SampleRate) And (mode1.SampleSize = mode2.SampleSize) And
  546. (mode1.Channels = mode2.Channels)
  547. Else
  548. Result := (mode1.AvgSampleRate = mode2.AvgSampleRate) And (mode1.SampleRate = mode2.SampleRate) And
  549. (mode1.Channels = mode2.Channels);
  550. End;
  551. End;
  552. Function GetModeString(Const mode: TVCapMode): String; Overload;
  553. Type
  554. TSubTypeName = Record
  555. SubType: PGUID;
  556. Name: String;
  557. End;
  558. Const
  559. MediaSubTypeNames: Array [1 .. 13] Of TSubTypeName = ((SubType: @MEDIASUBTYPE_YVU9; Name: '(YVU9)'),
  560. (SubType: @MEDIASUBTYPE_Y411; Name: '(Y411)'), (SubType: @MEDIASUBTYPE_Y41P; Name: '(Y41P)'),
  561. (SubType: @MEDIASUBTYPE_YUY2; Name: '(YUY2)'), (SubType: @MEDIASUBTYPE_YVYU; Name: '(YVYU)'),
  562. (SubType: @MEDIASUBTYPE_UYVY; Name: '(UYVY)'), (SubType: @MEDIASUBTYPE_Y211; Name: '(Y211)'),
  563. (SubType: @MEDIASUBTYPE_RGB24; Name: '(RGB24)'), (SubType: @MEDIASUBTYPE_RGB32; Name: '(RGB32)'),
  564. (SubType: @MEDIASUBTYPE_RGB565; Name: '(RGB565)'), (SubType: @MEDIASUBTYPE_RGB555; Name: '(RGB555)'),
  565. (SubType: @MEDIASUBTYPE_ARGB32; Name: '(ARGB32)'), (SubType: @MEDIASUBTYPE_RGB565; Name: '(RGB565)'));
  566. Var
  567. i: integer;
  568. Begin
  569. Result := Format('%dx%dx%db', [mode.Width, mode.Height, mode.BitCount]);
  570. For i := Low(MediaSubTypeNames) To High(MediaSubTypeNames) Do
  571. If CheckGUID(mode.MediaSubType, MediaSubTypeNames[i].SubType^) Then
  572. Begin
  573. Result := Result + ' ' + MediaSubTypeNames[i].Name;
  574. break;
  575. End;
  576. End;
  577. Function GetModeString(Const mode: TACapMode): String; Overload;
  578. Begin
  579. If CheckGUID(mode.MediaSubType, MEDIASUBTYPE_PCM) Then
  580. Begin
  581. Result := Format('%dHzx%dbx%d', [mode.SampleRate, mode.SampleSize, mode.Channels]);
  582. End
  583. Else
  584. Begin
  585. Result := Format('%dkBits/s, %dHz, %d', [mode.AvgSampleRate * 8 Div 1000, mode.SampleRate, mode.Channels]);
  586. End;
  587. End;
  588. // -----------------------------------------------------------------
  589. { TCapDeviceInfo }
  590. Type
  591. TCapDeviceInfo = Class
  592. Public
  593. DeviceName: String;
  594. DeviceNameInList: String;
  595. Moniker: IMoniker;
  596. Constructor Create(Const aDeviceName, aDeviceNameInList: String; Const aMoniker: IMoniker);
  597. Destructor Destroy; Override;
  598. End;
  599. Constructor TCapDeviceInfo.Create(Const aDeviceName, aDeviceNameInList: String; Const aMoniker: IMoniker);
  600. Begin
  601. DeviceName := aDeviceName;
  602. DeviceNameInList := aDeviceNameInList;
  603. Moniker := aMoniker;
  604. End;
  605. Destructor TCapDeviceInfo.Destroy;
  606. Begin
  607. Moniker := Nil; // I guess we have to release moniker - tnx to Gabriel Corneanu
  608. Inherited;
  609. End;
  610. // -----------------------------------------------------------------
  611. Function GetIntByMoniker(list: TObjectList; aMoniker: IMoniker): integer;
  612. Var
  613. i: integer;
  614. Begin
  615. Result := -1;
  616. For i := 0 To list.Count - 1 Do
  617. With list[i] As TCapDeviceInfo Do
  618. If aMoniker = Moniker Then
  619. Begin
  620. Result := i;
  621. break;
  622. End;
  623. End;
  624. Function GetStringByMoniker(list: TObjectList; aMoniker: IMoniker): String;
  625. Var
  626. i: integer;
  627. Begin
  628. Result := '';
  629. i := GetIntByMoniker(list, aMoniker);
  630. If (i >= 0) Then
  631. With list[i] As TCapDeviceInfo Do
  632. Result := DeviceNameInList;
  633. End;
  634. Function GetMonikerByString(list: TObjectList; aString: String): IMoniker;
  635. Var
  636. i: integer;
  637. Begin
  638. Result := Nil;
  639. For i := 0 To list.Count - 1 Do
  640. With list[i] As TCapDeviceInfo Do
  641. If aString = DeviceNameInList Then
  642. Begin
  643. Result := Moniker;
  644. break;
  645. End;
  646. End;
  647. // -----------------------------------------------------------------
  648. Function FindDeviceNum(Const DeviceName: String; Devices: TObjectList): integer;
  649. Var
  650. i: integer;
  651. d: TObject;
  652. Begin
  653. Result := 1;
  654. For i := 0 To Devices.Count - 1 Do
  655. Begin
  656. d := Devices[i];
  657. If (d Is TCapDeviceInfo) And (TCapDeviceInfo(d).DeviceName = DeviceName) Then
  658. Result := Result + 1;
  659. End;
  660. End;
  661. Procedure EnumFilters(Const clsidDeviceClass: TGUID; Const FiltersList: TObjectList);
  662. Var
  663. SysDevEnum: ICreateDevEnum;
  664. EnumCat: IEnumMoniker;
  665. Moniker: IMoniker;
  666. PropBag: IPropertyBag;
  667. varName: OleVariant;
  668. n: integer;
  669. s: String;
  670. Begin
  671. FiltersList.Clear;
  672. If (CoCreateInstance(CLSID_SystemDeviceEnum, Nil, CLSCTX_INPROC, IID_ICreateDevEnum, SysDevEnum) = S_OK) Then
  673. Begin
  674. // enum available video capture devices
  675. If (SysDevEnum.CreateClassEnumerator(clsidDeviceClass, EnumCat, 0) = S_OK) Then
  676. While EnumCat.Next(1, Moniker, Nil) = S_OK Do
  677. Begin
  678. If (Moniker.BindToStorage(Nil, Nil, IID_IPropertyBag, PropBag) = S_OK) And (PropBag <> Nil) Then
  679. Begin
  680. PropBag.Read('FriendlyName', varName, Nil);
  681. s := varName;
  682. If Trim(s) <> '' Then
  683. Begin
  684. n := FindDeviceNum(s, FiltersList);
  685. If n > 1 Then
  686. s := s + Format(' (%d)', [n]);
  687. FiltersList.Add(TCapDeviceInfo.Create(varName, s, Moniker));
  688. End;
  689. End;
  690. End;
  691. End;
  692. End;
  693. // -----------------------------------------------------------------
  694. Var
  695. VideoDevicesList, AudioDevicesList, VideoCompressorsList, AudioCompressorsList: TObjectList;
  696. Procedure BuildVideoDevicesList;
  697. Begin
  698. EnumFilters(CLSID_VideoInputDeviceCategory, VideoDevicesList);
  699. End;
  700. Procedure BuildAudioDevicesList;
  701. Begin
  702. EnumFilters(CLSID_AudioInputDeviceCategory, AudioDevicesList);
  703. End;
  704. Procedure BuildVideoCompressorsList;
  705. Begin
  706. EnumFilters(CLSID_VideoCompressorCategory, VideoCompressorsList);
  707. End;
  708. Procedure BuildAudioCompressorsList;
  709. Begin
  710. EnumFilters(CLSID_AudioCompressorCategory, AudioCompressorsList);
  711. End;
  712. Procedure BuildDevicesList;
  713. Begin
  714. BuildVideoDevicesList;
  715. BuildAudioDevicesList;
  716. BuildVideoCompressorsList;
  717. BuildAudioCompressorsList;
  718. End;
  719. Procedure ClearDevicesList;
  720. Begin
  721. VideoDevicesList.Clear;
  722. AudioDevicesList.Clear;
  723. VideoCompressorsList.Clear;
  724. AudioCompressorsList.Clear;
  725. End;
  726. // -----------------------------------------------------------------
  727. // devices Enum functions
  728. Function GetVideoDevicesList(Const Refresh: boolean): TStringList;
  729. Var
  730. i: integer;
  731. Begin
  732. If Refresh Or (VideoDevicesList.Count = 0) Then
  733. BuildVideoDevicesList;
  734. Result := TStringList.Create;
  735. Try
  736. For i := 0 To VideoDevicesList.Count - 1 Do
  737. Result.Add(TCapDeviceInfo(VideoDevicesList[i]).DeviceNameInList);
  738. Except
  739. Result.Free;
  740. Raise;
  741. End;
  742. End;
  743. Function GetAudioDevicesList(Const Refresh: boolean): TStringList;
  744. Var
  745. i: integer;
  746. Begin
  747. If Refresh Or (AudioDevicesList.Count = 0) Then
  748. BuildAudioDevicesList;
  749. Result := TStringList.Create;
  750. Try
  751. For i := 0 To AudioDevicesList.Count - 1 Do
  752. Result.Add(TCapDeviceInfo(AudioDevicesList[i]).DeviceNameInList);
  753. Except
  754. Result.Free;
  755. Raise;
  756. End;
  757. End;
  758. Function GetVideoCompressorsList(Const Refresh: boolean = false): TStringList;
  759. Var
  760. i: integer;
  761. Begin
  762. If Refresh Or (VideoCompressorsList.Count = 0) Then
  763. BuildVideoCompressorsList;
  764. Result := TStringList.Create;
  765. Try
  766. For i := 0 To VideoCompressorsList.Count - 1 Do
  767. Result.Add(TCapDeviceInfo(VideoCompressorsList[i]).DeviceNameInList);
  768. Except
  769. Result.Free;
  770. Raise;
  771. End;
  772. End;
  773. Function GetAudioCompressorsList(Const Refresh: boolean = false): TStringList;
  774. Var
  775. i: integer;
  776. Begin
  777. If Refresh Or (AudioCompressorsList.Count = 0) Then
  778. BuildAudioCompressorsList;
  779. Result := TStringList.Create;
  780. Try
  781. For i := 0 To AudioCompressorsList.Count - 1 Do
  782. Result.Add(TCapDeviceInfo(AudioCompressorsList[i]).DeviceNameInList);
  783. Except
  784. Result.Free;
  785. Raise;
  786. End;
  787. End;
  788. // -----------------------------------------------------------------
  789. { TSampleGrabberCB - ISampleGrabberCB realization }
  790. Type
  791. TSampleGrabberCB = Class(TObject, ISampleGrabberCB)
  792. Private
  793. FBuffer: PByte;
  794. FBuffLen: Cardinal;
  795. FOwner: HWND;
  796. FEnable: boolean;
  797. FLock: TCriticalSection;
  798. Public
  799. { IUnknown }
  800. Function QueryInterface(Const IID: TGUID; Out Obj): HRESULT; Stdcall;
  801. Function _AddRef: integer; Stdcall;
  802. Function _Release: integer; Stdcall;
  803. { ISampleGrabberCB }
  804. Function SampleCB(SampleTime: double; pSample: IMediaSample): HRESULT; Stdcall;
  805. Function BufferCB(SampleTime: double; pBuffer: PByte; BufferLen: Longint): HRESULT; Stdcall;
  806. Public
  807. Property Owner: HWND Read FOwner Write FOwner;
  808. Property Buffer: PByte Read FBuffer;
  809. Property BufferLength: Cardinal Read FBuffLen;
  810. Property GrabbingEnabled: boolean Read FEnable Write FEnable;
  811. Procedure ClearBuffer;
  812. // to avoid access to buffer from other thread
  813. // before accessing to buffer need to disable access to it
  814. // don't forget to enable access after using buffer data!
  815. Procedure DisableBufferAccess;
  816. Procedure EnableBufferAccess;
  817. Constructor Create;
  818. Destructor Destroy; Override;
  819. End;
  820. { TSampleGrabberCB }
  821. Constructor TSampleGrabberCB.Create;
  822. Begin
  823. FBuffer := Nil;
  824. FBuffLen := 0;
  825. FLock := TCriticalSection.Create;
  826. End;
  827. Destructor TSampleGrabberCB.Destroy;
  828. Begin
  829. ClearBuffer;
  830. FLock.Free;
  831. Inherited;
  832. End;
  833. Procedure TSampleGrabberCB.DisableBufferAccess;
  834. Begin
  835. FLock.Enter;
  836. End;
  837. Procedure TSampleGrabberCB.EnableBufferAccess;
  838. Begin
  839. FLock.Leave;
  840. End;
  841. Procedure TSampleGrabberCB.ClearBuffer;
  842. Begin
  843. DisableBufferAccess;
  844. Try
  845. FBuffLen := 0;
  846. ReallocMem(FBuffer, FBuffLen);
  847. Finally
  848. EnableBufferAccess;
  849. End;
  850. End;
  851. { TSampleGrabberCB - IUnknown }
  852. Function TSampleGrabberCB._AddRef: integer;
  853. Begin
  854. Result := 2;
  855. End;
  856. Function TSampleGrabberCB._Release: integer;
  857. Begin
  858. Result := 1;
  859. End;
  860. Function TSampleGrabberCB.QueryInterface(Const IID: TGUID; Out Obj): HRESULT;
  861. Begin
  862. // We need to return the two event interfaces when they're asked for
  863. Result := E_NOINTERFACE;
  864. If CheckGUID(IID, ISampleGrabberCB) Or CheckGUID(IID, IUnknown) Then
  865. Begin
  866. If GetInterface(IID, Obj) Then
  867. Result := S_OK;
  868. End;
  869. End;
  870. { TSampleGrabberCB - ISampleGrabberCB }
  871. Function TSampleGrabberCB.SampleCB(SampleTime: double; pSample: IMediaSample): HRESULT;
  872. Begin // not implemented
  873. Result := S_OK;
  874. End;
  875. Function TSampleGrabberCB.BufferCB(SampleTime: double; pBuffer: PByte; BufferLen: integer): HRESULT;
  876. Begin
  877. If GrabbingEnabled Then
  878. Begin
  879. // Comment from microsoft programmer below
  880. // The sample grabber is calling us back on its deliver thread.
  881. // This is NOT the main app thread!
  882. //
  883. // !!!!! WARNING WARNING WARNING !!!!!
  884. //
  885. // On Windows 9x systems, you are not allowed to call most of the
  886. // Windows API functions in this callback. Why not? Because the
  887. // video renderer might hold the global Win16 lock so that the video
  888. // surface can be locked while you copy its data. This is not an
  889. // issue on Windows 2000, but is a limitation on Win95,98,98SE and ME.
  890. // Calling a 16-bit legacy function could lock the system, because
  891. // it would wait forever for the Win16 lock, which would be forever
  892. // held by the video renderer.
  893. // As a workaround, we will copy the bitmap data during the callback,
  894. // post a message to our app, and write the data later.
  895. GrabbingEnabled := false;
  896. DisableBufferAccess;
  897. Try
  898. ReallocMem(FBuffer, BufferLen);
  899. FBuffLen := BufferLen;
  900. CopyMemory(FBuffer, pBuffer, BufferLen);
  901. Finally
  902. EnableBufferAccess;
  903. End;
  904. // notify application frame buffer filled
  905. PostMessage(FOwner, WM_SAMPLECAPTURED, integer(Self), 0);
  906. End;
  907. // notify application that next frame arrived
  908. PostMessage(FOwner, WM_SAMPLEPASSED, integer(Self), 0);
  909. Result := S_OK;
  910. End;
  911. // -----------------------------------------------------------------
  912. { TVideoCapture }
  913. Constructor TVideoCapture.Create(AOwner: TComponent);
  914. Begin
  915. Inherited;
  916. CleanUp;
  917. FUseFrameRate := false;
  918. FWantAudio := true;
  919. FWantAudioPreview := true;
  920. FWantDVAudio := true;
  921. FWantPreview := true;
  922. FWantCapture := true;
  923. FWantBitmaps := true;
  924. Color := clBlue;
  925. Width := 100;
  926. Height := 100;
  927. FPreallocFileSize := 1;
  928. FPixelFormat := pf24bit;
  929. GrabberCB := TSampleGrabberCB.Create;
  930. FCaptureTimer := TTimer.Create(Self);
  931. FCaptureTimer.Interval := 100;
  932. FCaptureTimer.OnTimer := CaptureProgress;
  933. End;
  934. Destructor TVideoCapture.Destroy;
  935. Begin
  936. StopPreview;
  937. StopCapture;
  938. TearDownGraph;
  939. CleanUp;
  940. FCaptureTimer.Enabled := false;
  941. FCaptureTimer.Free;
  942. GrabberCB.Free;
  943. // free resources occupied by lists
  944. ClearDevicesList;
  945. Inherited;
  946. End;
  947. // -----------------------------------------------------------------
  948. Procedure TVideoCapture.ChooseDevices(nmVideo, nmAudio: IMoniker; ForceGraphRebuild: boolean = false);
  949. Begin
  950. If (ForceGraphRebuild) Or ((mVideo <> nmVideo) Or (mAudio <> nmAudio)) Then
  951. Begin
  952. mVideo := nmVideo;
  953. mAudio := nmAudio;
  954. TearDownGraph;
  955. asm // this was added due to corrupted registers after func call (D5 compiler error?)
  956. push ebx; push edi; push esi
  957. end;
  958. InitCapFilters;
  959. asm
  960. pop esi; pop edi; pop ebx
  961. end;
  962. If BuildGraph And WantPreview Then
  963. StartPreview;
  964. Try
  965. If Assigned(FOnChangeDevice) Then
  966. FOnChangeDevice(Self);
  967. Except
  968. End;
  969. End;
  970. End;
  971. Procedure TVideoCapture.ChooseCompressors(nmVComp, nmAComp: IMoniker; ForceGraphRebuild: boolean);
  972. Begin
  973. If (ForceGraphRebuild) Or ((mVComp <> nmVComp) Or (mAComp <> nmAComp)) Then
  974. Begin
  975. mVComp := Nil;
  976. mAComp := Nil;
  977. mVComp := nmVComp;
  978. mAComp := nmAComp;
  979. TearDownGraph;
  980. asm // this was added due to corrupted registers after func call (D5 compiler error?)
  981. push ebx; push edi; push esi
  982. end;
  983. InitCapFilters;
  984. asm
  985. pop esi; pop edi; pop ebx
  986. end;
  987. If BuildGraph And WantPreview Then
  988. StartPreview;
  989. Try
  990. If Assigned(FOnChangeCompressor) Then
  991. FOnChangeCompressor(Self);
  992. Except
  993. End;
  994. End;
  995. End;
  996. // -----------------------------------------------------------------
  997. Procedure TVideoCapture.ChooseDevices(Const szVideo, szAudio: String; ForceGraphRebuild: boolean = false);
  998. Var
  999. nmVideo, nmAudio: IMoniker;
  1000. Begin
  1001. nmVideo := Nil;
  1002. nmAudio := Nil;
  1003. If (VideoDevicesList.Count = 0) Then
  1004. BuildVideoDevicesList;
  1005. If (AudioDevicesList.Count = 0) Then
  1006. BuildAudioDevicesList;
  1007. nmVideo := GetMonikerByString(VideoDevicesList, szVideo);
  1008. nmAudio := GetMonikerByString(AudioDevicesList, szAudio);
  1009. ChooseDevices(nmVideo, nmAudio, ForceGraphRebuild);
  1010. nmVideo := Nil;
  1011. nmAudio := Nil;
  1012. End;
  1013. Procedure TVideoCapture.ChooseDevices(Const numVideo, numAudio: integer; ForceGraphRebuild: boolean = false);
  1014. Var
  1015. nmVideo, nmAudio: IMoniker;
  1016. Begin
  1017. nmVideo := Nil;
  1018. nmAudio := Nil;
  1019. If (numVideo >= 0) And (numVideo < VideoDevicesList.Count) Then
  1020. nmVideo := TCapDeviceInfo(VideoDevicesList[numVideo]).Moniker;
  1021. If (numAudio >= 0) And (numAudio < AudioDevicesList.Count) Then
  1022. nmAudio := TCapDeviceInfo(AudioDevicesList[numAudio]).Moniker;
  1023. ChooseDevices(nmVideo, nmAudio, ForceGraphRebuild);
  1024. nmVideo := Nil;
  1025. nmAudio := Nil;
  1026. End;
  1027. Procedure TVideoCapture.ChooseCompressors(Const szVComp, szAComp: String; ForceGraphRebuild: boolean);
  1028. Var
  1029. nmVComp, nmAComp: IMoniker;
  1030. Begin
  1031. nmVComp := Nil;
  1032. nmAComp := Nil;
  1033. If (VideoCompressorsList.Count = 0) Then
  1034. BuildVideoCompressorsList;
  1035. If (AudioCompressorsList.Count = 0) Then
  1036. BuildAudioCompressorsList;
  1037. nmVComp := GetMonikerByString(VideoCompressorsList, szVComp);
  1038. nmAComp := GetMonikerByString(AudioCompressorsList, szAComp);
  1039. ChooseCompressors(nmVComp, nmAComp, ForceGraphRebuild);
  1040. nmVComp := Nil;
  1041. nmAComp := Nil;
  1042. End;
  1043. Procedure TVideoCapture.ChooseCompressors(Const numVComp, numAComp: integer; ForceGraphRebuild: boolean);
  1044. Var
  1045. nmVComp, nmAComp: IMoniker;
  1046. Begin
  1047. nmVComp := Nil;
  1048. nmAComp := Nil;
  1049. If (numVComp >= 0) And (numVComp < VideoCompressorsList.Count) Then
  1050. nmVComp := TCapDeviceInfo(VideoCompressorsList[numVComp]).Moniker;
  1051. If (numAComp >= 0) And (numAComp < AudioCompressorsList.Count) Then
  1052. nmAComp := TCapDeviceInfo(AudioCompressorsList[numAComp]).Moniker;
  1053. ChooseCompressors(nmVComp, nmAComp, ForceGraphRebuild);
  1054. nmVComp := Nil;
  1055. nmAComp := Nil;
  1056. End;
  1057. // -----------------------------------------------------------------
  1058. Procedure TVideoCapture.CleanUp;
  1059. Begin
  1060. FreeCapFilters;
  1061. VideoWindow := Nil;
  1062. MediaEvent := Nil;
  1063. DroppedFrames := Nil;
  1064. Render := Nil;
  1065. Sink := Nil;
  1066. ConfigAviMux := Nil;
  1067. NullRenderer := Nil;
  1068. FCCAvail := false;
  1069. FCapCC := false;
  1070. FGraphBuilt := false;
  1071. FCapturing := false;
  1072. FPreviewing := false;
  1073. FMasterStream := -1;
  1074. End;
  1075. Procedure TVideoCapture.FreeCapFilters;
  1076. Begin
  1077. Graph := Nil;
  1078. Builder := Nil;
  1079. VCap := Nil;
  1080. ACap := Nil;
  1081. DVSplitter := Nil;
  1082. DVDec := Nil;
  1083. VComp := Nil;
  1084. AComp := Nil;
  1085. AStreamConf := Nil;
  1086. VStreamConf := Nil;
  1087. CaptureDialogs := Nil;
  1088. CompressDialogs := Nil;
  1089. Grabber := Nil;
  1090. FDialogs := [];
  1091. End;
  1092. Function TVideoCapture.MakeBuilder: boolean;
  1093. Begin
  1094. Result := (Builder <> Nil) Or (CoCreateInstance(CLSID_CaptureGraphBuilder2, Nil, CLSCTX_INPROC,
  1095. IID_ICaptureGraphBuilder2, Builder) = NOERROR);
  1096. End;
  1097. Function TVideoCapture.MakeGraph: boolean;
  1098. Begin
  1099. Result := (Graph <> Nil) Or (CoCreateInstance(CLSID_FilterGraph, Nil, CLSCTX_INPROC, IID_IGraphBuilder, Graph)
  1100. = NOERROR);
  1101. End;
  1102. Function TVideoCapture.Init: boolean;
  1103. Begin
  1104. // Create the filter graph and create the capture graph builder.
  1105. Result := MakeGraph And MakeBuilder;
  1106. If Not Result Then
  1107. Exit;
  1108. Builder.SetFiltergraph(Graph);
  1109. BuildDevicesList;
  1110. Result := (VideoDevicesList.Count > 0); // or (AudioDevicesList.Count>0);
  1111. End;
  1112. Function TVideoCapture.InitCapFilters: boolean;
  1113. Var
  1114. PropBag: IPropertyBag;
  1115. hr: HRESULT;
  1116. varOle: OleVariant;
  1117. Pin: IPin;
  1118. AStream: IBaseFilter;
  1119. Begin
  1120. FreeCapFilters;
  1121. Result := MakeBuilder;
  1122. If Not Result Then
  1123. Begin
  1124. ErrMsg(rsCantMakeGraphBuilder);
  1125. Exit;
  1126. End;
  1127. DVSplitter := Nil;
  1128. DVDec := Nil;
  1129. Try
  1130. VCap := Nil;
  1131. hr := 0;
  1132. If mVideo <> Nil Then
  1133. Begin
  1134. hr := mVideo.BindToStorage(Nil, Nil, IID_IPropertyBag, PropBag);
  1135. If Succeeded(hr) Then
  1136. Begin
  1137. If PropBag.Read('FriendlyName', varOle, Nil) = NOERROR Then
  1138. FVCapFriendlyName := varOle;
  1139. PropBag := Nil;
  1140. End;
  1141. hr := mVideo.BindToObject(Nil, Nil, IID_IBaseFilter, VCap);
  1142. End;
  1143. If VCap = Nil Then
  1144. ErrMsgException(rsCantCreateVCaptureFilter, hr);
  1145. // make a filtergraph, give it to the graph builder and put the video
  1146. // capture filter in the graph
  1147. If Not MakeGraph Then
  1148. ErrMsgException(rsCantMakeGraph);
  1149. If Builder.SetFiltergraph(Graph) <> NOERROR Then
  1150. ErrMsgException(rsCantSetFilterGraph);
  1151. If Graph.AddFilter(VCap, Nil) <> NOERROR Then
  1152. ErrMsgException(rsCantAddVFilterToGraph, hr);
  1153. // !!! What if this interface isn't supported?
  1154. // we use this interface to set the frame rate and get the capture size
  1155. hr := Builder.FindInterface(@PIN_CATEGORY_CAPTURE, @MEDIATYPE_Interleaved, VCap, IID_IAMStreamConfig, VStreamConf);
  1156. If hr <> NOERROR Then
  1157. Begin
  1158. hr := Builder.FindInterface(@PIN_CATEGORY_CAPTURE, @MEDIATYPE_Video, VCap, IID_IAMStreamConfig, VStreamConf);
  1159. If hr <> NOERROR Then // this means we can't set frame rate (non-DV only)
  1160. ErrMsg(rsCantFindVStreamConfig, hr);
  1161. End;
  1162. If IsDVSource Then
  1163. Begin
  1164. // insert DVSplitter & DVDecoder if they are not exist
  1165. If Failed(Graph.FindFilterByName(DVDecoderFilterName, DVDec)) Then
  1166. Begin
  1167. If Failed(CoCreateInstance(CLSID_DVVideoCodec, Nil, CLSCTX_INPROC, IID_IBaseFilter, DVDec)) Then
  1168. Begin
  1169. ErrMsgException(rsCantAddDVDecoder);
  1170. End;
  1171. End;
  1172. If WantAudio And WantDVAudio Then
  1173. Begin
  1174. If Failed(Graph.FindFilterByName(DVSplitterFilterName, DVSplitter)) Then
  1175. Begin
  1176. If Failed(CoCreateInstance(CLSID_DVSplitter, Nil, CLSCTX_INPROC, IID_IBaseFilter, DVSplitter)) Then
  1177. Begin
  1178. ErrMsgException(rsCantAddDVSplitter);
  1179. End;
  1180. End;
  1181. End;
  1182. End;
  1183. // we use this interface to bring up the 3 dialogs
  1184. // NOTE: Only the VfW capture filter supports this. This app only brings
  1185. // up dialogs for legacy VfW capture drivers, since only those have dialogs
  1186. Builder.FindInterface(@PIN_CATEGORY_CAPTURE, @MEDIATYPE_Video, VCap, IID_IAMVfwCaptureDialogs, CaptureDialogs);
  1187. // add video compression filter
  1188. If WantCapture And (VCap <> Nil) And (mVComp <> Nil) Then
  1189. Begin
  1190. hr := mVComp.BindToObject(Nil, Nil, IID_IBaseFilter, VComp);
  1191. If (hr = S_OK) Then
  1192. Begin
  1193. Builder.FindInterface(@PIN_CATEGORY_CAPTURE, @MEDIATYPE_Video, VComp, IID_IAMVfwCompressDialogs,
  1194. CompressDialogs)
  1195. End;
  1196. End;
  1197. // there's no point making an audio capture filter
  1198. If WantAudio Then
  1199. Begin
  1200. // create the audio capture filter, even if we are not capturing audio right
  1201. // now, so we have all the filters around all the time.
  1202. // We want an audio capture filter and some interfaces
  1203. If (DVSplitter = Nil) Then
  1204. Begin // is not DV source
  1205. If (mAudio <> Nil) Then
  1206. Begin
  1207. hr := mAudio.BindToStorage(Nil, Nil, IID_IPropertyBag, PropBag);
  1208. If Succeeded(hr) Then
  1209. Begin
  1210. If PropBag.Read('FriendlyName', varOle, Nil) = NOERROR Then
  1211. FACapFriendlyName := varOle;
  1212. PropBag := Nil;
  1213. End;
  1214. ACap := Nil;
  1215. mAudio.BindToObject(Nil, Nil, IID_IBaseFilter, ACap);
  1216. If ACap = Nil Then
  1217. // there are no audio capture devices. We'll only allow video capture
  1218. ErrMsg(rsCantMakeACapFilter)
  1219. Else
  1220. Begin
  1221. // put the audio capture filter in the graph
  1222. // We'll need this in the graph to get audio property pages
  1223. If Graph.AddFilter(ACap, Nil) <> NOERROR Then
  1224. ErrMsgException(rsCantAddAFilterToGraph, hr);
  1225. End;
  1226. End;
  1227. AStream := ACap;
  1228. End
  1229. Else
  1230. Begin
  1231. AStream := DVSplitter;
  1232. End;
  1233. If (ACap <> Nil) Or (WantAudio And WantDVAudio) Then
  1234. Begin
  1235. // add audio compression filter
  1236. If WantCapture And (mAComp <> Nil) And Succeeded(mAComp.BindToObject(Nil, Nil, IID_IBaseFilter, AComp)) Then
  1237. Begin
  1238. AStream := AComp // to control audio stream of AudioCompressor filter
  1239. End;
  1240. // !!! What if this interface isn't supported?
  1241. // we use this interface to set the captured wave format
  1242. hr := Builder.FindInterface(Nil, @MEDIATYPE_Audio, AStream, IID_IAMStreamConfig, AStreamConf);
  1243. If (hr <> NOERROR) And Not(IsDVSource And (AStream = DVSplitter)) Then
  1244. ErrMsg(rsCantFindAStreamConfig);
  1245. End;
  1246. End;
  1247. // Can this filter do closed captioning?
  1248. hr := Builder.FindPin(VCap, PINDIR_OUTPUT, PIN_CATEGORY_VBI, TGUID(Nil^), false, 0, Pin);
  1249. If hr <> S_OK Then
  1250. hr := Builder.FindPin(VCap, PINDIR_OUTPUT, PIN_CATEGORY_CC, TGUID(Nil^), false, 0, Pin);
  1251. FCCAvail := (hr = S_OK); // can't capture it, then
  1252. If FCapCC Then
  1253. Pin := Nil;
  1254. Result := true;
  1255. Except
  1256. FreeCapFilters;
  1257. Result := false;
  1258. End;
  1259. End;
  1260. Procedure TVideoCapture.ResizeWindow;
  1261. Begin
  1262. If Assigned(VideoWindow) Then
  1263. VideoWindow.SetWindowPosition(0, 0, ClientWidth, ClientHeight);
  1264. End;
  1265. Procedure TVideoCapture.SetSize(Var Msg: TMessage);
  1266. Begin
  1267. Inherited;
  1268. ResizeWindow;
  1269. End;
  1270. Function TVideoCapture.FindVideoWindow: boolean;
  1271. Begin
  1272. // Get the preview window to be a child of our app's window
  1273. // This will find the IVideoWindow interface on the renderer. It is
  1274. // important to ask the filtergraph for this interface... do NOT use
  1275. // ICaptureGraphBuilder2::FindInterface, because the filtergraph needs to
  1276. // know we own the window so it can give us display changed messages, etc.
  1277. Result := (Graph.QueryInterface(IID_IVideoWindow, VideoWindow) = S_OK);
  1278. If Result Then
  1279. Begin
  1280. VideoWindow.put_Owner(Handle); // We own the window now
  1281. VideoWindow.put_WindowStyle(WS_CHILD Or WS_CLIPSIBLINGS); // you are now a child
  1282. // give the preview window all our space
  1283. ResizeWindow;
  1284. VideoWindow.put_Visible(true);
  1285. End;
  1286. End;
  1287. Procedure TVideoCapture.DoAborted(hr: HRESULT);
  1288. Begin
  1289. StopCapture;
  1290. If Assigned(FOnAborted) Then
  1291. FOnAborted(Self);
  1292. End;
  1293. Procedure TVideoCapture.DoDeviceLost;
  1294. Begin
  1295. If Assigned(FOnDeviceLost) Then
  1296. FOnDeviceLost(Self);
  1297. StopCapture;
  1298. StopPreview;
  1299. End;
  1300. // graph event occured - get & process events
  1301. Procedure TVideoCapture.GraphEvent(Var Msg: TMessage);
  1302. Var
  1303. Event, param1, param2: integer;
  1304. wasCapturing, wasPreviewing: boolean;
  1305. Begin
  1306. wasCapturing := Capturing;
  1307. wasPreviewing := Previewing;
  1308. If (MediaEvent <> Nil) Then
  1309. Begin
  1310. While MediaEvent.GetEvent(Event, param1, param2, 0) = S_OK Do
  1311. Begin
  1312. Try
  1313. // if you need parameters supplied by GetEvent function - process it here
  1314. // case Event of
  1315. // end;
  1316. Finally
  1317. MediaEvent.FreeEventParams(Event, param1, param2);
  1318. End;
  1319. Case Event Of
  1320. EC_ERRORABORT:
  1321. DoAborted(param1);
  1322. EC_DEVICE_LOST:
  1323. DoDeviceLost;
  1324. End;
  1325. End;
  1326. // we have stopped capture now need to restore preview
  1327. If ((Capturing <> wasCapturing) Or (wasPreviewing <> Previewing)) And (WantPreview) Then
  1328. Begin
  1329. StartPreview; // it will rebuild graph
  1330. End;
  1331. End;
  1332. End;
  1333. // we have to build video preview part of graph
  1334. // if we want to capture bitmaps need to render stream through grabber filter
  1335. Function TVideoCapture.RenderVideoPreview(Const VSource: IBaseFilter): boolean;
  1336. Var
  1337. FGrabber: IBaseFilter;
  1338. mt: TAM_MEDIA_TYPE;
  1339. flt: IBaseFilter;
  1340. Begin
  1341. flt := VSource;
  1342. If WantBitmaps Then
  1343. Begin
  1344. Result := Succeeded(Graph.FindFilterByName(SampleGrabberFilterName, FGrabber)) Or // already exists
  1345. ( // have to make new one
  1346. (CoCreateInstance(CLSID_SampleGrabber, Nil, CLSCTX_INPROC, IID_IBaseFilter, FGrabber) = NOERROR) And
  1347. (Graph.AddFilter(FGrabber, SampleGrabberFilterName) = S_OK));
  1348. Result := Result And (FGrabber.QueryInterface(IID_ISampleGrabber, Grabber) = S_OK);
  1349. If Result Then
  1350. Begin
  1351. If (FPixelFormat <> pfDevice) Then
  1352. Begin
  1353. // set prefered bitmap pixel depth
  1354. ZeroMemory(@mt, sizeof(mt));
  1355. mt.formattype := FORMAT_VideoInfo;
  1356. mt.majortype := MEDIATYPE_Video;
  1357. mt.SubType := PixelFormatGUIDs[FPixelFormat]^;
  1358. Grabber.SetMediaType(mt);
  1359. End;
  1360. If Succeeded(Builder.RenderStream(Nil, @MEDIATYPE_Video, flt, Nil, FGrabber)) Then
  1361. Begin
  1362. Grabber.SetBufferSamples(false);
  1363. Grabber.SetOneShot(false);
  1364. TSampleGrabberCB(GrabberCB).Owner := Handle;
  1365. Grabber.SetCallback(TSampleGrabberCB(GrabberCB), 1);
  1366. flt := FGrabber;
  1367. End
  1368. Else
  1369. Begin
  1370. // we cant connect grabber - probably wrong mediatype?
  1371. // TO DO: try to request DecoderOut available mediatypes and
  1372. // set one of them
  1373. ErrMsg(rsCantInstallSampleGrabber);
  1374. End;
  1375. End;
  1376. End;
  1377. Result := Succeeded(Builder.RenderStream(Nil, @MEDIATYPE_Video, flt, Nil, Nil));
  1378. End;
  1379. // Tear down everything downstream of a given filter
  1380. Procedure TVideoCapture.NukeDownstream(pf: IBaseFilter);
  1381. Var
  1382. pP, pTo: IPin;
  1383. pins: IEnumPins;
  1384. pininfo: TPIN_INFO;
  1385. hr: HRESULT;
  1386. Begin
  1387. pins := Nil;
  1388. hr := pf.EnumPins(pins);
  1389. pins.Reset;
  1390. While hr = NOERROR Do
  1391. Begin
  1392. hr := pins.Next(1, pP, Nil);
  1393. If (hr = S_OK) And (pP <> Nil) Then
  1394. Begin
  1395. pP.ConnectedTo(pTo);
  1396. If pTo <> Nil Then
  1397. Begin
  1398. hr := pTo.QueryPinInfo(pininfo);
  1399. If hr = NOERROR Then
  1400. Begin
  1401. If pininfo.dir = PINDIR_INPUT Then
  1402. Begin
  1403. NukeDownstream(pininfo.pFilter);
  1404. Graph.Disconnect(pTo);
  1405. Graph.Disconnect(pP);
  1406. Graph.RemoveFilter(pininfo.pFilter);
  1407. End;
  1408. pininfo.pFilter := Nil;
  1409. End;
  1410. pTo := Nil;
  1411. End;
  1412. pP := Nil;
  1413. End;
  1414. End;
  1415. pins := Nil;
  1416. End;
  1417. // Tear down everything downstream of the capture filters, so we can build
  1418. // a different capture graph. Notice that we never destroy the capture filters
  1419. // and WDM filters upstream of them, because then all the capture settings
  1420. // we've set would be lost.
  1421. Procedure TVideoCapture.TearDownGraph;
  1422. Begin
  1423. If Not FGraphBuilt Then
  1424. Exit;
  1425. If Capturing Then
  1426. StopCapture;
  1427. If Previewing Then
  1428. StopPreview;
  1429. If VideoWindow <> Nil Then
  1430. Begin
  1431. // stop drawing in our window, or we may get wierd repaint effects
  1432. VideoWindow.put_Visible(false);
  1433. VideoWindow.put_Owner(0);
  1434. End;
  1435. // free compressors and other filters we used
  1436. VideoWindow := Nil;
  1437. Sink := Nil;
  1438. NullRenderer := Nil;
  1439. ConfigAviMux := Nil;
  1440. Render := Nil;
  1441. Grabber := Nil;
  1442. MediaEvent := Nil;
  1443. DroppedFrames := Nil;
  1444. // destroy the graph downstream of our capture filters
  1445. If VCap <> Nil Then
  1446. NukeDownstream(VCap);
  1447. If ACap <> Nil Then
  1448. NukeDownstream(ACap);
  1449. FGraphBuilt := false;
  1450. If (WantCapture) Then
  1451. DeleteFile(TempCaptureFileName);
  1452. End;
  1453. // build the preview graph!
  1454. // !!! PLEASE NOTE !!! Some new WDM devices have totally separate capture
  1455. // and preview settings. An application that wishes to preview and then
  1456. // capture may have to set the preview pin format using IAMStreamConfig on the
  1457. // preview pin, and then again on the capture pin to capture with that format.
  1458. // In this sample app, there is a separate page to set the settings on the
  1459. // capture pin and one for the preview pin. To avoid the user
  1460. // having to enter the same settings in 2 dialog boxes, an app can have its own
  1461. // UI for choosing a format (the possible formats can be enumerated using
  1462. // IAMStreamConfig) and then the app can programmatically call IAMStreamConfig
  1463. // to set the format on both pins.
  1464. // build the capture graph!
  1465. Function TVideoCapture.BuildGraph: boolean;
  1466. Var
  1467. hr: HRESULT;
  1468. pmt: PAM_MEDIA_TYPE;
  1469. SmartTee: IBaseFilter;
  1470. InfiniteTee: IBaseFilter;
  1471. VSource, ASource: IBaseFilter; // aliases to video/audio sources
  1472. Sink2: IFileSinkFilter2;
  1473. Begin
  1474. // we have no GraphBuilder((
  1475. Result := Assigned(Graph);
  1476. If Not Result Then
  1477. Exit;
  1478. // we have one already
  1479. Result := FGraphBuilt;
  1480. If Result Then
  1481. Exit;
  1482. // Result = false
  1483. // We don't have necessary capture filters
  1484. If (VCap = Nil) Then
  1485. Exit;
  1486. If (WantAudio) And ((ACap = Nil) And (Not(IsDVSource And WantDVAudio))) Then
  1487. Exit;
  1488. If (WantCapture) Then
  1489. Begin
  1490. // no capture file name yet... we need one first
  1491. If UseTempFile Then
  1492. FCaptureFile := TempCaptureFileName
  1493. Else
  1494. FCaptureFile := CaptureFileName;
  1495. If (FCaptureFile = '') Then
  1496. Begin
  1497. ErrMsg(rsEmptyFileName);
  1498. Exit;
  1499. End;
  1500. DeleteFile(FCaptureFile);
  1501. If FDoPreallocFile And Not AllocCaptureFile(PreallocFileSize) Then
  1502. Begin
  1503. ErrMsg(rsFailedToAllocFileSize);
  1504. Exit;
  1505. End;
  1506. End;
  1507. Try
  1508. VSource := VCap;
  1509. ASource := ACap;
  1510. If IsDVSource Then
  1511. Begin
  1512. If (DVDec <> Nil) Then
  1513. Graph.AddFilter(DVDec, DVDecoderFilterName);
  1514. If (DVSplitter <> Nil) Then
  1515. Graph.AddFilter(DVSplitter, DVSplitterFilterName);
  1516. If (DVDec <> Nil) And (Builder.RenderStream(Nil, @MEDIATYPE_Interleaved, VCap, DVSplitter, DVDec) = S_OK) Then
  1517. Begin
  1518. SetDVResolution(FDVResolution);
  1519. VSource := DVDec;
  1520. If WantAudio And WantDVAudio Then
  1521. ASource := DVSplitter;
  1522. End
  1523. Else
  1524. ErrMsgException(rsCantRenderVCaptureStream);
  1525. End;
  1526. // insert Smart Tee Filter
  1527. If Failed(CoCreateInstance(CLSID_SmartTee, Nil, CLSCTX_INPROC, IID_IBaseFilter, SmartTee)) Or
  1528. Failed(Graph.AddFilter(SmartTee, SmartTeeFilterName)) Or
  1529. Failed(Builder.RenderStream(Nil, @MEDIATYPE_Video, VSource, Nil, SmartTee)) Then
  1530. Begin
  1531. ErrMsgException(rsCantAddSmartTee);
  1532. End;
  1533. VSource := SmartTee;
  1534. // Infinite Tee Filter
  1535. If (ASource <> Nil) And (WantAudioPreview) Then
  1536. Begin
  1537. If Failed(CoCreateInstance(CLSID_InfTee, Nil, CLSCTX_INPROC, IID_IBaseFilter, InfiniteTee)) Or
  1538. Failed(Graph.AddFilter(InfiniteTee, InfTeeFilterName)) Or
  1539. Failed(Builder.RenderStream(Nil, @MEDIATYPE_Audio, ASource, Nil, InfiniteTee)) Then
  1540. Begin
  1541. ErrMsgException(rsCantAddInfTee);
  1542. End;
  1543. ASource := InfiniteTee;
  1544. End;
  1545. // Rendering audio preview stream
  1546. If WantPreview And WantAudioPreview And (ASource <> Nil) Then
  1547. Begin
  1548. If Failed(Builder.RenderStream(Nil, @MEDIATYPE_Audio, ASource, Nil, Nil)) Then
  1549. ErrMsgException(rsCantRenderAPreviewStream);
  1550. End;
  1551. If WantCapture Then
  1552. Begin
  1553. // if Succeeded(CoCreateInstance(CLSID_NullRenderer, nil, CLSCTX_INPROC, IID_IBaseFilter, NullRenderer)) then begin
  1554. // Graph.AddFilter(NullRenderer, NullRendererFilterName);
  1555. // end;
  1556. // We need a rendering section that will write the capture file out in AVI file format
  1557. hr := Builder.SetOutputFileName(MEDIASUBTYPE_Avi, PWCHAR(FCaptureFile), Render, Sink);
  1558. If (hr <> NOERROR) Then
  1559. ErrMsgException(rsCantSetCaptureFile);
  1560. If Succeeded(Sink.QueryInterface(IID_IFileSinkFilter2, Sink2)) Then
  1561. Begin
  1562. // set overwrite mode for file sinker
  1563. Sink2.SetMode(AM_FILE_OVERWRITE);
  1564. Sink2 := Nil;
  1565. End;
  1566. // Now tell the AVIMUX to write out AVI files that old apps can read properly.
  1567. // If we don't, most apps won't be able to tell where the keyframes are, slowing down editing considerably
  1568. // Doing this will cause one seek (over the area the index will go) when
  1569. // you capture past 1 Gig, but that's no big deal.
  1570. // NOTE: This is on by default, so it's not necessary to turn it on
  1571. hr := Render.QueryInterface(IID_IConfigAviMux, ConfigAviMux);
  1572. If (hr = NOERROR) And (ConfigAviMux <> Nil) Then
  1573. Begin
  1574. ConfigAviMux.SetOutputCompatibilityIndex(true);
  1575. If (ASource <> Nil) Then // Also, set the proper MASTER STREAM
  1576. MasterStream := FMasterStream;
  1577. End;
  1578. If (VComp <> Nil) Then
  1579. Graph.AddFilter(VComp, VCompFilterName);
  1580. hr := Builder.RenderStream(Nil, @MEDIATYPE_Video, VSource, VComp, Render);
  1581. If (hr <> NOERROR) Then
  1582. ErrMsgException(rsCantRenderVCaptureStream);
  1583. If ASource <> Nil Then
  1584. Begin
  1585. // render audio capture stream
  1586. If (AComp <> Nil) Then
  1587. Graph.AddFilter(AComp, ACompFilterName);
  1588. hr := Builder.RenderStream(Nil, @MEDIATYPE_Audio, ASource, AComp, Render);
  1589. If Failed(hr) Then
  1590. ErrMsgException(rsCantRenderACaptureStream);
  1591. End;
  1592. End;
  1593. // Rendering video preview stream
  1594. If WantPreview And Not RenderVideoPreview(VSource) Then
  1595. ErrMsgException(rsCantRenderPreviewStream);
  1596. // NOTE: We do this even if we didn't ask for a preview, because rendering
  1597. // the capture pin may have rendered the preview pin too (WDM overlay
  1598. // devices) because they must have a preview going. So we better always
  1599. // put the preview window in our app, or we may get a top level window
  1600. // appearing out of nowhere!
  1601. If Not FindVideoWindow And (FWantPreview) Then
  1602. ErrMsg(rsThisGraphCantPreview);
  1603. // Render the closed captioning pin? It could be a CC or a VBI category pin,
  1604. // depending on the capture driver
  1605. If (FCCAvail And FCapCC) Then
  1606. Begin
  1607. hr := Builder.RenderStream(@PIN_CATEGORY_CC, Nil, VCap, Nil, Render);
  1608. If (hr <> NOERROR) Then
  1609. Begin
  1610. hr := Builder.RenderStream(@PIN_CATEGORY_VBI, Nil, VCap, Nil, Render);
  1611. If (hr <> NOERROR) Then
  1612. ErrMsg(rsCantRenderCC);
  1613. End;
  1614. // To preview and capture VBI at the same time, we can call this twice
  1615. If (WantPreview) Then
  1616. Builder.RenderStream(@PIN_CATEGORY_VBI, Nil, VCap, Nil, Nil);
  1617. End;
  1618. // now tell it what frame rate to capture at. Just find the format it
  1619. // is capturing with, and leave everything alone but change the frame rate
  1620. If UseFrameRate Then
  1621. hr := E_FAIL
  1622. Else
  1623. hr := NOERROR;
  1624. If (VStreamConf <> Nil) And UseFrameRate Then
  1625. Begin
  1626. // DV capture does not use a VIDEOINFOHEADER
  1627. If VStreamConf.GetFormat(pmt) = NOERROR Then
  1628. Begin
  1629. Try
  1630. If CheckGUID(pmt^.formattype, FORMAT_VideoInfo) Then
  1631. Begin
  1632. PVIDEOINFOHEADER(pmt^.pbFormat)^.AvgTimePerFrame := round(10000000 / FrameRate);
  1633. hr := VStreamConf.SetFormat(pmt^);
  1634. If hr <> NOERROR Then
  1635. ErrMsg(rsCantSetPreviewFrameRate, hr);
  1636. End;
  1637. Finally
  1638. DeleteMediaType(pmt);
  1639. End;
  1640. End;
  1641. End;
  1642. If (hr <> NOERROR) Then
  1643. ErrMsg(rsCantSetCaptureFrameRate);
  1644. // now ask the filtergraph to tell us when something is completed or aborted
  1645. // (EC_COMPLETE, EC_USERABORT, EC_ERRORABORT). This is how we will find out
  1646. // if the disk gets full while capturing
  1647. If (Graph.QueryInterface(IID_IMediaEventEx, MediaEvent) = NOERROR) Then
  1648. MediaEvent.SetNotifyWindow(Handle, WM_FGNOTIFY, 0);
  1649. // All done.
  1650. GetAvailableDialogs;
  1651. FGraphBuilt := true;
  1652. Result := true;
  1653. Except
  1654. TearDownGraph;
  1655. Result := false;
  1656. End;
  1657. End;
  1658. Function TVideoCapture.GetIsDVSource: boolean;
  1659. Var
  1660. pmt: PAM_MEDIA_TYPE;
  1661. Begin
  1662. Result := false;
  1663. If (VStreamConf <> Nil) And (VStreamConf.GetFormat(pmt) = S_OK) Then
  1664. Try
  1665. Result := CheckGUID(pmt^.majortype, MEDIATYPE_Interleaved);
  1666. Finally
  1667. DeleteMediaType(pmt);
  1668. End;
  1669. End;
  1670. // --------------------------------------------------------------------
  1671. // current capture and compressors devices
  1672. Function TVideoCapture.GetACapSourceIdx: integer;
  1673. Begin
  1674. Result := GetIntByMoniker(AudioDevicesList, mAudio);
  1675. If Result < 0 Then
  1676. Begin
  1677. BuildAudioDevicesList;
  1678. Result := GetIntByMoniker(AudioDevicesList, mAudio);
  1679. End;
  1680. End;
  1681. Function TVideoCapture.GetACompDeviceIdx: integer;
  1682. Begin
  1683. Result := GetIntByMoni