在Delphi下使用迅雷APlayer组件进行免注册开发

之前都是用的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控件并绑定事件,呵呵,感谢前辈们,果然有啊,原文章链接:http://blog.csdn.net/love3s/article/details/7411757

 

照着来吧,按照这位前辈的话,文笔不好直接上代码吧:

 

复制代码
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 = '{A9332148-C691-4B9D-91FC-B9C461DBE9DD}';

type
  PIUnknown = ^IUnknown;
  TAtlAxAttachControl = function(Control: IUnknown; hwind: hwnd; ppUnkContainer: PIUnknown): HRESULT; stdcall;

  _IPlayerEvents = dispinterface
    ['{31D6469C-1DA7-47C0-91F9-38F0C39F9B89}']
    {
    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);
  private
    { Private declarations }
    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);
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

{ TForm1 }

procedure TfrmMain.btnOpenClick(Sender: TObject);
begin
  if not APlayerCreateSuccess then Exit;

  if dlgOpen1.Execute(Handle) then
  begin
    APlayer.Open(dlgOpen1.FileName);
  end;
end;

procedure TfrmMain.btnPathClick(Sender: TObject);
begin
  if not 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, 'DllGetClassObject');
  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, '创建APlayer实例失败!', '错误', MB_OK + MB_ICONERROR);
      end;
    except
      MessageBox(Handle, PChar('创建APlayer实例失败!错误代码:' + 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 的定义
  }
  case dispid of
    // function OnMessage(nMessage: Integer; wParam: Integer; lParam: Integer): HResult; dispid 1;
    $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('atl.dll');
  if hModule < 32 then
  begin
    Exit(False);
  end;
  AtlAxAttachControl := TAtlAxAttachControl(GetProcAddress(hModule, 'AtlAxAttachControl'));
  EventSink := TEventSink.Create(Self);
  EventSink.OnInvoke := EventSinkInvoke;
  if not Assigned(AtlAxAttachControl) then
    Exit(False);
  try
    hDll := LoadLibrary('APlayer.dll');
    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控件事件用的):

 

复制代码
unit EventSink;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Winapi.ActiveX;

type
  TInvokeEvent = procedure(Sender: TObject; DispID: Integer; const IID: TGUID;
    LocaleID: Integer; Flags: Word; Params: TDispParams;
    VarResult, ExcepInfo, ArgErr: Pointer) of object;

  TAbstractEventSink = class(TObject, IUnknown, IDispatch)
  private
    FDispatch: IDispatch;
    FDispIntfIID: TGUID;
    FConnection: LongInt;
    FOwner: TComponent;
  protected
    { IUnknown }
    function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    { IDispatch }
    function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
    function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo)
      : HRESULT; stdcall;
    function GetIDsOfNames(const IID: TGUID; Names: Pointer;
      NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT; stdcall;
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer)
      : HRESULT; stdcall;
  public
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
    procedure Connect(AnAppDispatch: IDispatch; const AnAppDispIntfIID: TGUID);
    procedure Disconnect;
  end;

  TEventSink = class(TComponent)
  private
    { Private declarations }
    FSink: TAbstractEventSink;
    FOnInvoke: TInvokeEvent;
  protected
    { Protected declarations }
    procedure DoInvoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer); virtual;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Connect(AnAppDispatch: IDispatch; const AnAppDispIntfIID: TGUID);
  published
    { Published declarations }
    property OnInvoke: TInvokeEvent read FOnInvoke write FOnInvoke;
  end;

implementation

uses
  ComObj;

procedure InterfaceConnect(const Source: IUnknown; const IID: TIID;
  const Sink: IUnknown; var Connection: LongInt);
var
  CPC: IConnectionPointContainer;
  CP: IConnectionPoint;
  i: HRESULT;
begin
  Connection := 0;
  if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then
    if Succeeded(CPC.FindConnectionPoint(IID, CP)) then
      i := CP.Advise(Sink, Connection);
end;

procedure InterfaceDisconnect(const Source: IUnknown; const IID: TIID;
  var Connection: LongInt);
var
  CPC: IConnectionPointContainer;
  CP: IConnectionPoint;
begin
  if Connection <> 0 then
    if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then
      if Succeeded(CPC.FindConnectionPoint(IID, CP)) then
        if Succeeded(CP.Unadvise(Connection)) then
          Connection := 0;
end;

{ TAbstractEventSink }

function TAbstractEventSink._AddRef: Integer; stdcall;
begin
  Result := 2;
end;

function TAbstractEventSink._Release: Integer; stdcall;
begin
  Result := 1;
end;

constructor TAbstractEventSink.Create(AOwner: TComponent);
begin
  inherited Create;
  FOwner := AOwner;
end;

destructor TAbstractEventSink.Destroy;
var
  p: Pointer;
begin
  Disconnect;

  inherited Destroy;
end;

function TAbstractEventSink.GetIDsOfNames(const IID: TGUID; Names: Pointer;
  NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAbstractEventSink.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo)
  : HRESULT; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAbstractEventSink.GetTypeInfoCount(out Count: Integer)
  : HRESULT; stdcall;
begin
  Count := 0;
  Result := S_OK;
end;

function TAbstractEventSink.Invoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params;
  VarResult, ExcepInfo, ArgErr: Pointer): HRESULT; stdcall;
begin
  (FOwner as TEventSink).DoInvoke(DispID, IID, LocaleID, Flags, Params,
    VarResult, ExcepInfo, ArgErr);
  Result := S_OK;
end;

function TAbstractEventSink.QueryInterface(const IID: TGUID; out Obj)
  : HRESULT; stdcall;
begin
  // We need to return the event interface when it's asked for
  Result := E_NOINTERFACE;
  if GetInterface(IID, Obj) then
    Result := S_OK;
  if IsEqualGUID(IID, FDispIntfIID) and GetInterface(IDispatch, Obj) then
    Result := S_OK;
end;

procedure TAbstractEventSink.Connect(AnAppDispatch: IDispatch;
  const AnAppDispIntfIID: TGUID);
begin
  FDispIntfIID := AnAppDispIntfIID;
  FDispatch := AnAppDispatch;
  // Hook the sink up to the automation server
  InterfaceConnect(FDispatch, FDispIntfIID, Self, FConnection);
end;

procedure TAbstractEventSink.Disconnect;
begin
  if Assigned(FDispatch) then
  begin
    // Unhook the sink from the automation server
    InterfaceDisconnect(FDispatch, FDispIntfIID, FConnection);
    FDispatch := nil;
    FConnection := 0;
  end;
end;

{ TEventSink }

procedure TEventSink.Connect(AnAppDispatch: IDispatch;
  const AnAppDispIntfIID: TGUID);
begin
  FSink.Connect(AnAppDispatch, AnAppDispIntfIID);
end;

constructor TEventSink.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FSink := TAbstractEventSink.Create(Self);
end;

destructor TEventSink.Destroy;
begin
  FSink.Free;

  inherited Destroy;
end;

procedure TEventSink.DoInvoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params;
  VarResult, ExcepInfo, ArgErr: Pointer);
begin
  if Assigned(FOnInvoke) then
    FOnInvoke(Self, DispID, IID, LocaleID, Flags, TDispParams(Params),
      VarResult, ExcepInfo, ArgErr);
end;

end.
复制代码


循着前辈的脚步果然很容易并顺利的解决了问题,我在APlayer论坛看有人问怎么在Delphi下也可以免注册使用APlayer组件呢,呵呵,现在有答案了!而且我们掌握了一个重要的Delphi技能“Delphi不注册COM直接使用ActiveX控件并绑定事件”,开心!特此记录。

 

后附程序执行的截图:

1、程序设计界面,只是放置了两个按钮、一个OpenDialog、一个Panel(作为APlayer组件的容器)。

2、程序运行后,可以看到APlayer组件成功创建到了Panel上,读取APlayer的解码器路径,和APlayer.dll在同一目录下,如果用的注册ActiveX的方式并拖拽到窗体上进行开发的,自己试试就会发现解码器路径固定在“C:\Users\Public\Thunder Network\APlayer”且无法修改。如果解码器路径固定了会导致在客户端计算机部署时更复杂些,不如在本地目录方便,况且还得在客户计算机上注册APlayer组件,忒麻烦了。呵呵,免注册真好!

3、播放

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
APEvents OnMessage说明:3事件发生在用户在 APlayer 视频区域引发鼠标操作或者当 APlayer 视频区域拥有焦点时引发键盘操作时。 OnStateChanged说明:4事件发生在 APlayer 引擎状态改变后。 OnOpenSucceeded说明:5发生在 APlayer 引擎成功打开一个媒体文件时。 OnSeekCompleted说明:6事件发生在用户进行一个 SetPosition 的异步调用完成后。 OnBuffer说明:7事件发生在 APlayer 从网络缓冲媒体数据的过程中。 OnVideoSizeChanged说明:8发生在所播放的视频的分辨率改变时。 OnDownloadCodec说明:9发生在 APlayer 引擎播放某个媒体文件缺少对应的解码器时。。 OnEvent说明:10事件在 APlayer 的特定扩展事件通知时发出。 APlayer 创建说明:创建播放器视口 句柄说明:返回播放器句柄 打开说明:方法用来打开需要播放的媒体文件, 要播放一个媒体文件, 首先就需要打开它 停止说明:停止 播放说明:播放 暂停说明:暂停 版本说明:版本 图标说明:视频区域在未播放视频时显示的图片 状态说明:引擎的当前状态 #PS_READY:准备就绪 // #PS_OPENING:正在打开 // #PS_PAUSING:正在暂停 // #PS_PAUSED:暂停中 // #PS_PLAYING:正在开始播放 // #PS_PLAY:播放中 // #PS_CLOSING:正在开始关闭 时长说明:接收获取到的媒体时长,单位毫秒(ms) 进度说明:取到的播放进度,单位毫秒(ms) 位置说明:设置的播放位置值 宽度说明:取当前播放媒体文件的视频宽度 高度说明:取到的视频高度 音量说明:设置(获取)播放音量 IsSeeking说明:引擎当前是否处于设置播放进度(Seek)过程中 缓冲说明:取到的缓冲进度值:-1不在缓冲过程中 0-99缓冲进度 快进说明:单位秒 快退说明:单位秒 AI类 激活说明:2501 是否激活AI功能,需要在打开媒体文件之前设置,AI功能需要依赖AI库文件,可以到官方论坛下载。 人脸加载说明:2502   获取当前已加载了多少个人脸标签,标签可以是人名什么的,也可以是别的文字;标签的用途:如果视频中出现了该标签的人脸,APlayer就会在返回的人脸信息中给出来。 人脸列表说明:2503   获取当前已加载的人脸标签列表,格式:"李某某;刘某某;张某某"。 人脸文件说明:2505   添加人脸照片文件标签,标签需要在 EVENTCODE_AILOADCOMPLETED 后添加才会成功,格式 "李某某;C:\test.jpg"。 画面人脸说明:2507   添加当前播放视频的当前画面中的人脸作为标签(在程序退出后会自动保存),格式:"C:\李某某.jpg" 或者 "C:\李某某.bmp",文件名(不包含后缀)即为标签。 添加进度说明:2508   因为添加目录标签是一个耗时的过程,该配置获取当前添加目录标签的进度,返回格式:"current;total" 重新装载说明:2510   重新装载 Faces.txt 文件,这对于多个应用程序共享 Faces.txt 很有效,对于这种情况下,某个应用产生标签,其他应用使用这些标签,这些应用可是是在不同机器上,通过共享目录访问 Faces.txt。 添加人脸说明:2511   通过标签添加一个人脸饰物,当APlayer识别到视频中这个标签的人出现的时候,自动绘制饰物,格式:"TheMode;FileName;DstX;DstY;DstZ;SrcX;SrcY;SrcZ;Extra;Label(VideoX;VideoY)",格式解释如下: 人脸信息说明:2512   获取当前视频中人脸信息,返回格式:"lablel1,error,left,top,right,bottom,angleX,angleY,angleZ;lablel2,error,left,top,right,bottom,angleX,angleY,angleZ;...",其中 error 为匹配误差,angle开头的为角度 人脸关键点说明:2513   获取当前视频中人脸信息的关键点,每行一个人脸信息,每个人脸68个关键点,这些关键点数值上都基于视频像素坐标,格式 "lablel1;x1,y1;x2,y2;x3,y3;...x68,y68\r\nlablel2;x1,y1;x2,y2;x3,y3;...x68,y68" 人脸动态检测说明:2514 动态开启或者禁止AI检测功能,1开启,0禁止,默认为1。 人脸调试信息说明:2515 获取或设置是否显示人脸调试信息,方便调试程序,1显示,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值