之前都是用的delphi下的dspack进行的视频开发,这个组件其实很好用,就是找解码器麻烦点,而且还得在客户的计算机上使用RegSvr32.exe也注册解码器,要不有可能播放不了。
结果在查找合适的解码器过程中,无意搜索到了迅雷的APlayer组件。
迅雷APlayer这个组件提供了一个完整的解码器合集(核心的流媒体播放技术也是DirectShow和dspack一样一样的),下载APlayer的解码器合集并注册到系统后,确实在dspack也用的挺好,不过看了APlayer的介绍后发现人家做的更好,虽然是个ActiveX,但是给出的c++示例表示无需显式注册即可使用(就是不需要用Regsvr32.exe预先注册APlayer组件到目标计算机上),而且也无需预先注册解码器(也是Regsvr32)到操作系统,只要指定解码器路径,APlayer可以自行搜索此路径查找合适的解码器,简直太好了,本来就怕发布到客户计算机上后由于解码器问题导致播放不正常(其实开发测试阶段已经出现过了),这么个好东西赶快试试。
第一次使用先按照Delphi下的传统方式来,在开发环境中引入APlayer组件,这个就是个ActiveX控件,添加到组件面板上,建个工程拖到窗体上,响应几个事件,轻轻松松视频就开始播放了,呵呵,也不用关心解码器文件缺不缺了,APlayer组件会查找并指示出来缺少的文件,真是太智能了,省心,好用。
接下来晋级操作,怎么不注册APlayer.dll就能直接创建ActiveX组件在自己的程序里面呢?看APlayer的示例工程定义了两个函数(BOOL CreateAPlayerFromFile(void)、HRESULT CreateInstanceFromFile(const TCHAR * pcszPath, REFCLSID rclsid, REFIID riid, IUnknown * pUnkOuter, LPVOID * ppv)),直接通过APlayer.dll就创建了ActiveX组件,不过那个示例工程是C++的,咱们不熟,对照着改了下,没搞定,于是求助万能的网络搜索引擎,目标:Delphi不注册COM直接使用ActiveX控件并绑定事件,呵呵,感谢前辈们,果然有啊,原文章链接:
照着来吧,按照这位前辈的话,文笔不好直接上代码吧:
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.OleCtnrs, System.Win.ComObj, EventSink, Winapi.ActiveX, Vcl.ExtCtrls, Vcl.StdCtrls; const CLASS_Player: TGUID = ; type PIUnknown = ^IUnknown; TAtlAxAttachControl = function(Control: IUnknown; hwind: hwnd; ppUnkContainer: PIUnknown): HRESULT; stdcall; _IPlayerEvents = dispinterface [] { function OnMessage(nMessage: Integer; wParam: Integer; lParam: Integer): HResult; dispid 1; function OnStateChanged(nOldState: Integer; nNewState: Integer): HResult; dispid 2; function OnOpenSucceeded: HResult; dispid 3; function OnSeekCompleted(nPosition: Integer): HResult; dispid 4; function OnBuffer(nPercent: Integer): HResult; dispid 5; function OnVideoSizeChanged: HResult; dispid 6; function OnDownloadCodec(const strCodecPath: WideString): HResult; dispid 7; function OnEvent(nEventCode: Integer; nEventParam: Integer): HResult; dispid 8; } end; TfrmMain = class(TForm) pnlCom: TPanel; btnOpen: TButton; dlgOpen1: TOpenDialog; btnPath: TButton; procedure FormCreate(Sender: TObject); procedure btnOpenClick(Sender: TObject); procedure btnPathClick(Sender: TObject); APlayer: Variant; APlayerCreateSuccess: Boolean; EventSink: TEventSink; function InitAPlayer: Boolean; function CreateComObjectFromDll(CLSID: TGUID; DllHandle: THandle): IUnknown; procedure EventSinkInvoke(Sender: TObject; DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; Params: tagDISPPARAMS; VarResult, ExcepInfo, ArgErr: Pointer); ; var frmMain: TfrmMain; TfrmMain.btnOpenClick(Sender: TObject); APlayerCreateSuccess then Exit; if dlgOpen1.Execute(Handle) then begin APlayer.Open(dlgOpen1.FileName); end; end; procedure TfrmMain.btnPathClick(Sender: TObject); APlayerCreateSuccess then Exit; ShowMessage(APlayer.GetConfig(2)); end; function TfrmMain.CreateComObjectFromDll(CLSID: TGUID; DllHandle: THandle): IUnknown; var Factory: IClassFactory; DllGetClassObject: function(const CLSID, IID: TGUID; var Obj): HResult; stdcall; hr: HRESULT; begin DllGetClassObject := GetProcAddress(DllHandle, ); if Assigned(DllGetClassObject) then begin hr := DllGetClassObject(CLSID, IClassFactory, Factory); if hr = S_OK then try hr := Factory.CreateInstance(nil, IUnknown, Result); if hr <> S_OK then begin MessageBox(Handle, , , MB_OK + MB_ICONERROR); end; except MessageBox(Handle, PChar(+ IntToStr(GetLastError)), , MB_OK + MB_ICONERROR); end; end; end; procedure TfrmMain.EventSinkInvoke(Sender: TObject; DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; Params: tagDISPPARAMS; VarResult, ExcepInfo, ArgErr: Pointer); var ov: OleVariant; begin { 这里需要注明Params这个参数, 包含了事件的参数 如: Params.rgvarg[0] 代表第一个参数 Params.rgvarg[1] 代表第二个参数 ...... Params.rgvarg[65535] 代表第65535个参数 最多65535个参数 具体可以参考 tagDISPPARAMS 的定义 $00000001: begin end; // function OnStateChanged(nOldState: Integer; nNewState: Integer): HResult; dispid 2; $00000002: begin end; // function OnOpenSucceeded: HResult; dispid 3; $00000003: begin end; // function OnSeekCompleted(nPosition: Integer): HResult; dispid 4; $00000004: begin end; // function OnBuffer(nPercent: Integer): HResult; dispid 5; $00000005: begin end; // function OnVideoSizeChanged: HResult; dispid 6; $00000006: begin end; // function OnDownloadCodec(const strCodecPath: WideString): HResult; dispid 7; $00000007: begin ov := OleVariant(Params.rgvarg[0]); MessageBox(Handle, PChar(+ VarToStr(ov)), , MB_OK + MB_ICONERROR); end; // function OnEvent(nEventCode: Integer; nEventParam: Integer): HResult; dispid 8; $00000008: begin end; end end; procedure TfrmMain.FormCreate(Sender: TObject); begin ReportMemoryLeaksOnShutdown := DebugHook <> 0; APlayerCreateSuccess := InitAPlayer; end; function TfrmMain.InitAPlayer: Boolean; var hModule, hDll: THandle; AtlAxAttachControl: TAtlAxAttachControl; begin hModule := LoadLibrary(); Exit(False); end; AtlAxAttachControl := TAtlAxAttachControl(GetProcAddress(hModule, )); EventSink := TEventSink.Create(Self); EventSink.OnInvoke := EventSinkInvoke; if not Assigned(AtlAxAttachControl) then Exit(False); try hDll := LoadLibrary(); APlayer := CreateComObjectFromDll(CLASS_Player, hDll) as IDispatch; if VarIsNull(APlayer) then begin Exit(False); end; EventSink.Connect(APlayer, _IPlayerEvents); AtlAxAttachControl(APlayer, pnlCom.Handle, nil); Result := True; except Result := False; end; end; end.
接下来EventSink单元代码(绑定ActiveX控件事件用的):