Multimedia Player dengan DirectShow (2)

March 8, 2006 at 12:25 pm | Posted in Code Samples, Tutorials | Leave a comment

by: Zamrony P Juhara

Tutorial ini adalah lanjutan tutorial sebelumnya yang berjudul sama. Kali ini kita akan mendiskusikan bagaimana menampilkan video pada suatu window.

Menampilkan Video pada suatu Window.

Jika anda sudah mencoba aplikasi multimedia player pada tutorial bagian 1, ketika memainkan file video, anda akan mendapatkan bahwa video ditampilkan pada window tersendiri. Lalu bagaimana bila kita ingin menampilkan window tersebut, misal di form milik kita sendiri?

Filter Graph Manager mengimplementasikan interface IVideoWindow. Interface ini adalah interface yang kita pergunakan untuk manipulasi bagaimana video ditampilkan. Kita bisa menampilkan video pada skala zoom 50%, 100%, 200% atau skala sembarang pada window yang kita inginkan. Video bisa juga ditampilkan fullscreen.

Untuk mendapatkan pointer ke interface ini kita panggil QueryInterface milik Filter graph manager

Var FVideoWindow:IVideoWindow;

FFilterGraph.QueryInterface(IID_IVideoWindow,FVideoWindow);

Jika sukses FVideoWindow akan berisi pointer ke instance IVideoWindow.

Untuk dapat menampilkan video pada suatu window, langkah-langkahnya adalah:

  • Set video ke handle window yang menjadi target.
    Menggunakan metode put_Owner(). Parameter fungsi ini adalah handle window. DirectShow menggunakan tipe OAHWND untuk mengacu pada handle window. So handle window harus dicast ke OAHWND.

    var FWindowHandle:HWND;

    FVideoWindow.put_Owner(OAHWND(FWindowHandle));

  • Mengubah status visibility. Agar video terlihat di window, kita perlu mengubah visibility video dengan metode put_visible() milik IVideoWindow. Parameternya adalah visibility bertipe longBool.

    FvideoWindow.put_visible(true);

    Catatan: window yang direferensi oleh FWindowHandle harus juga visible agar video terlihat dilayar.

  • Mengubah Style Window

    Style window diset menggunakan SetWindowStyle milik IVideoWindow.
    Window perlu diset menjadi child window mengunakan WS_CHILD dan agar video tidak digambar diluar area window, kita perlu menggunakan style WS_CLIPSIBLINGS

    FVideoWindow.SetWindowStyle(WS_CLIPSIBLINGS or WS_CHILD);

  • Mengubah Posisi video pada Window

    Video bisa kita letakkan dimana saja pada client area window FWindowHandle menggunakan metode SetPosition() milik IVideoWindow.


    Var arect:TRect;

    
    aRect.Left:=0;
    aRect.Top:=0;
    aRect.Right:=ClientWidth;
    aRect.Bottom:=ClientHeight;
    FVideoWindow.SetPosition(aRect.left,aRect.Top,aRect.Right,aRect.Bottom);
    

    Mengubah ukuran tinggi dan lebar menyebabkan terjadi zoom bila lebar dan tinggi tidak sama dengan lebar tinggi original video. Proses zoom tidak peduli aspek ratio. Jika kita butuh zoom dimana aspek rasio tidak berubah, kita perlu mendapatkan lebar tinggi original video. Cara mendapatkan data ini akan kita bahas setelah ini.

Menampilkan Video Fullscreen

IVideoWindow memiliki fungsi put_FullscreenMode(), yang kita gunakan mengubah tampilan fullscreen atau window. Bila true, video ditampilkan fullscreen, bila sebaliknya dalam windowed mode.

Fullscreen mode:

FVideoWindow.put_FullscreenMode(true);

Windowed mode:

FVideoWindow.put_FullscreenMode(false);

Untuk mendapatkan status mode fullcreen,

Var statusFull:longBool;

FVideoWindow.get_FullscreenMode(statusFull);

Ketika berada pada mode fullscreen, aplikasi kita tidak menerima focus, sehingga semua message akan dikirim ke filter graph manager. Agar pada mode fullscreen aplikasi kita tetap bisa menerima message input keyboard dan input mouse (sehingga kita melakukan toggle fullscreen on/off misalnya), kita perlu membelokkan message tersebut ke aplikasi kita dgn fungsi put_MesageDrain()() milik IVideoWindow.
Put_MessageDrain() mengharapkan handle window yang akan menghandle message-message.

FVideoWindow.Put_MessageDrain(FHandleWindow);

Untuk mendapatkan handle window yang membelokkan message-message kita menggunakan IVideoWindow.get_MessageDrain().

FVideoWindow.Get_MessageDrain(FHandleWindow);

Bersih-bersih

Setelah finish dengan IVideoWindow, kita perlu mengubah status visiblity menjadi false, jika tidak, maka video tetap akan tampil di layar dan tidak bisa dihilangkan oleh user. Setelah itu owner window harus kita nol kan menggunakan put_Owner().


FvideoWindow.put_Visible(false);
FvideoWindow.put_Owner(0);

Mendapatkan lebar tinggi asli video

Agar kita bisa melakukan zoom dengan mempertahankan aspek rasio, kita perlu mengetahui lebar tinggi asli video. Interface IBasicVideo yang bisa di-QueryInterface dari Filter graph manager disediakan salah satunya adalah untuk keperluan ini.

Var orgVideoWidth,orgVideoHeight:integer;


FbasicVideo.get_VideoWidth(orgVideoWidth);
FbasicVideo.get_VideoHeight(orgVideoHeight);

Mengecek Apakah Suatu Media Memiliki data video

Ada kalanya kita perlu mengecek apakah media yang akan kita mainkan mengandung data video dan audio ataukah hanya audio saja. Misal kita akan menampilkan window video hanya bila media tersebut mengandung data video dan audio. Caranya dengan meng-queryInterface FilterGraph untuk interface IBasicVideo. Jika nil berarti media bukan video atau codec untuk format tersebut belum terinstall. Jika
Tidak nil, kita perlu mengecek status visible IVideoWindow. Jika pemanggilan get_visible() gagal berarti media bukan video. Contoh:


Var basicVideo:IBasicVideo;
     Vis:longBool;

FFilterGraph.QueryInterface(IID_IBasicVideo,basicVideo);
audioOnly:=(basicVideo=nil) or (failed(FVideoWindow.get_Visible(vis)));

Berikut ini adalah implementasi kelas TBasicPlayer yang baru. Catatan: di sini hanya bagian yang berubah yang ditampilkan.

unit uDirectShowPlayer;

interface

uses classes,windows,messages,directShow,controls;

const WM_MMNOTIFY=WM_APP+$1234;

type
     TPlayPosition=record
       Current:int64;
       Stop:int64;
     end;
     TBasicPlayer=class(TObject)
     private
       ....
       FVideoRect:TRect;
       ...
       procedure SetControl(const Value: TWinControl);
       procedure SetDrainHandle(const Value: HWND);
       function GetDrainHandle: HWND;
       ...
       procedure SetFullScreen(const Value: boolean);
       function GetFullScreen:boolean;
       ...
       function GetOrgHeight: integer;
       function GetOrgWidth: integer;
       procedure SetVideoRect(const Value: TRect);

       procedure UpdateVideoWindowPos;

       function GetAudioOnly:boolean;
     public
       ...
       procedure GetVideoOrgSize(out width,height:integer);
     published
       ...
       property DrainHandle:HWND read GetDrainHandle write SetDrainHandle;
       ...
       property FullScreen:boolean read GetFullScreen write SetFullScreen;

       property VideoRect:TRect read FVideoRect write SetVideoRect;

       property OrgWidth:integer read GetOrgWidth;
       property OrgHeight:integer read GetOrgHeight;

       property AudioOnly:boolean read GetAudioOnly;
     end;
....
implementation

uses sysutils,activeX;
...
{ TBasicPlayer }
...
procedure TBasicPlayer.SetControl(const Value: TWinControl);
begin
  FControl:= Value;
  if FControlnil then
  begin
    FVideoWindow.put_Owner(OAHWND(FControl.handle));
    FVideoWindow.put_WindowStyle(WS_CHILD or WS_CLIPSIBLINGS);

    FVideoRect:=Rect(0,0,FControl.ClientWidth,FControl.ClientHeight);
    UpdateVideoWindowPos;

    FVideoWindow.put_Visible(true);
    FVideoWindow.SetWindowForeground(1);
  end;
end;

procedure TBasicPlayer.SetFullScreen(const Value: boolean);
begin
  FVideoWindow.put_FullScreenMode(value);
end;

function TBasicPlayer.GetFullScreen: boolean;
var full:longBool;
begin
  FVideoWindow.get_FullScreenMode(full);
  result:=full;
end;

procedure TBasicPlayer.SetVideoRect(const Value: TRect);
begin
  FVideoRect := Value;
  UpdateVideoWindowPos;
end;

function TBasicPlayer.GetOrgHeight: integer;
var awidth,aheight:integer;
begin
  GetVideoOrgSize(aWidth,aHeight);
  result:=aHeight;
end;

function TBasicPlayer.GetOrgWidth: integer;
var awidth,aheight:integer;
begin
  GetVideoOrgSize(aWidth,aHeight);
  result:=awidth;
end;

procedure TBasicPlayer.GetVideoOrgSize(out width, height: integer);
var basicVideo:IBasicVideo;
begin
  FFilterGraph.QueryInterface(IID_IBasicVideo,basicVideo);
  if basicVideo<>nil then
    basicVideo.GetVideoSize(width,height)
  else
  begin
    width:=0;
    height:=0;
  end;
end;

procedure TBasicPlayer.UpdateVideoWindowPos;
begin
  FVideoWindow.SetWindowPosition(FVideoRect.Left,
                           FVideoRect.Top,
                           FVideoRect.Right,
                           FVideoRect.Bottom);
end;


procedure TBasicPlayer.SetDrainHandle(const Value: HWND);
begin
  FVideoWindow.put_MessageDrain(Value);
end;

function TBasicPlayer.GetDrainHandle: HWND;
var hdrain:integer;
begin
  FVideoWindow.get_MessageDrain(hdrain);
  result:=hdrain;
end;

function TBasicPlayer.GetAudioOnly: boolean;
var basicVideo:IBasicVideo;
    vis:longBool;
begin
  FFilterGraph.QueryInterface(IID_IBasicVideo,basicVideo);
  result:=(basicVideo=nil) or (failed(FVideoWindow.get_Visible(vis)));
end;
...

initialization
 CoInitialize(nil);

finalization
 CoUnInitialize;
end.


Berikut ini contoh aplikasi yang memanfaatkan kelas TMMPlayer yang baru. Hanya bagian yang berubah yang ditampilkan disini.

unit ufrmMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,DirectShow, StdCtrls, ExtCtrls,
  uDirectShowPlayer, ComCtrls, ActnList;

type
  TfrmMediaPlayer = class(TForm)
    ...
    chkbxFullscreen: TCheckBox;
    ActionList1: TActionList;
    actToggleFullscreen: TAction;
    rdgrpZoom: TRadioGroup;
    procedure btnOpenClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure chkbxFullscreenClick(Sender: TObject);
    procedure actToggleFullscreenExecute(Sender: TObject);
    procedure rdgrpZoomClick(Sender: TObject);
  private
    FDuration:int64;
    FMMPlayer:TMMPlayer;
    OldDrain:HWND;

    procedure WM_MMNotify(var msg:TMessage);message WM_MMNOTIFY;

    { Private declarations }
  public
    constructor Create(AOwner:TComponent);override;
    { Public declarations }
  end;

var
  frmMediaPlayer: TfrmMediaPlayer;

implementation

uses ufrmVideo;

{$R *.dfm}

{ TForm1 }

constructor TfrmMediaPlayer.Create(AOwner: TComponent);
begin
  inherited;
  FMMPlayer:=TMMPlayer.Create;
  FMMPlayer.Handle:=Handle;
  OldDrain:=0;
end;
...
procedure TfrmMediaPlayer.WM_MMNotify(var msg: TMessage);
var aplayer:TBasicPlayer;
    evCode,param1,param2:integer;
begin
  aplayer:=TBasicPlayer(msg.LParam);
  aplayer.EventObj.GetEvent(evCode,param1,param2,0);
  case evCode of
    EC_COMPLETE:begin
                  Timer1.Enabled:=false;
                  aplayer.Stop;
                  aplayer.Rewind;
                  ProgressBar1.Position:=0;
                  lblProgress.Caption:='0%';
                end;
   EC_FULLSCREEN_LOST:chkbxFullscreen.Checked:=false;
  end;
  aplayer.EventObj.FreeEventParams(evCode,param1,param2);
end;

...

procedure TfrmMediaPlayer.btnOpenClick(Sender: TObject);
var orgW,orgH:integer;
begin
  if opendialog1.Execute then
  begin
    FMMPlayer.Stop;
    FMMPlayer.Rewind;
    FMMPlayer.RemoveAllFilters;

    ProgressBar1.Position:=0;
    lblProgress.Caption:='0%';

    FMMPlayer.Filename:=opendialog1.FileName;
    FMMPlayer.BuildFilterGraph;
    FDuration:=FMMPlayer.Duration;

    if not FMMPlayer.AudioOnly then
    begin
      //zoom 100%
      FMMPlayer.GetVideoOrgSize(orgW,orgH);
      frmVideo.ClientWidth:=OrgW;
      frmVideo.ClientHeight:=OrgH;
      rdgrpZoom.ItemIndex:=1;
      frmVideo.Show;
    end;
  end;
end;

procedure TfrmMediaPlayer.FormActivate(Sender: TObject);
begin
  FMMPlayer.Control:=frmVideo;
end;

procedure TfrmMediaPlayer.chkbxFullscreenClick(Sender: TObject);
begin
  if chkbxFullscreen.Checked then
  begin
    OldDrain:=FMMPlayer.DrainHandle;
    FMMPlayer.DrainHandle:=Handle;
  end else
    FMMPlayer.DrainHandle:=OldDrain;

  FMMPlayer.FullScreen:=chkbxFullscreen.Checked;
end;

procedure TfrmMediaPlayer.actToggleFullscreenExecute(Sender: TObject);
begin
  chkbxFullscreen.Checked:=not chkbxFullscreen.Checked;
end;

procedure TfrmMediaPlayer.rdgrpZoomClick(Sender: TObject);
var orgW,orgH:integer;
    aspect:single;
begin
  FMMPlayer.GetVideoOrgSize(orgW,orgH);
  if (orgW0) and (orgH0) then
  begin
    case rdgrpZoom.ItemIndex of
      0:begin
          //zoom 50%
          aspect:=orgW/orgH;
          frmVideo.ClientWidth:=trunc(0.5*OrgW);
          frmVideo.ClientHeight:=trunc(frmVideo.ClientWidth/aspect);
          FMMPlayer.VideoRect:=Rect(0,0,frmVideo.ClientWidth,frmVideo.ClientHeight);
        end;
      1:begin
          //zoom 100%
          frmVideo.ClientWidth:=OrgW;
          frmVideo.ClientHeight:=OrgH;
          FMMPlayer.VideoRect:=Rect(0,0,frmVideo.ClientWidth,frmVideo.ClientHeight);
        end;
      2:begin
          //zoom 50%
          aspect:=orgW/orgH;
          frmVideo.ClientWidth:=trunc(2*OrgW);
          frmVideo.ClientHeight:=trunc(frmVideo.ClientWidth/aspect);
          FMMPlayer.VideoRect:=Rect(0,0,frmVideo.ClientWidth,frmVideo.ClientHeight);
        end;
    end;
  end;
end;

end.


Source kode lengkap dapat di download di milis Delphindo pada Files section di kategori
Technical Article dengan nama directshow_dengan_delphi2.zip

Ok happy coding

Leave a Comment »

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.
Entries and comments feeds.