









Tutorial ini adalah lanjutan tutorial sebelumnya Multimedia Player dengan DirectShow Part 1. Kali ini kita akan mendiskusikan bagaimana 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 video 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:
var FWindowHandle:HWND; FVideoWindow.put_Owner(OAHWND(FWindowHandle));
FVideoWindow.put_visible(true);Catatan: window yang direferensi oleh FWindowHandle harus juga visible agar video terlihat di layar.
FVideoWindow.SetWindowStyle(WS_CLIPSIBLINGS or WS_CHILD);
var arect:TRect; aRect.Left:=0; aRect.Top:=0; aRect.Right:=ClientWidth; aRcet.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.
IVideoWindow memiliki metode 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 fullscreen,
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);
Setelah finish dengan IVideoWindow, kita perlu mengubah status visibility 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);
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 untuk keperluan ini.
var orgVideoWidth,orgVideoHeight:integer;
FBasicVideo.get_VideoWidth(orgVideoWidth);
FBasicVideo.get_VideoHeight(orgVideoHeight);
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.
{---------------------------------
DirectShow wrapper unit
----------------------------------
(c) 2006 juhara.com All rights
reserved.
----------------------------------
coder:zamrony p juhara
----------------------------------
history:
-bug in RemoveAllFilter(). Lack of
enum.Reset()
----------------------------------}
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
FFilterGraph:IGraphBuilder;
FMediaControl:IMediaControl;
FMediaEvent:IMediaEventEx;
FMediaSeek:IMediaSeeking;
FVideoWindow:IVideoWindow;
FHandle: HWND;
FControl: TWinControl;
FVideoRect:TRect;
procedure SetHandle(const Value: HWND);
procedure SetDrainHandle(const Value: HWND);
function GetDrainHandle: HWND;
function GetDuration: int64;
function GetPosition: TPlayPosition;
procedure SetPosition(const Value: TPlayPosition);
procedure SetControl(const Value: TWinControl);
procedure SetFullScreen(const Value: boolean);
function GetFullScreen:boolean;
function GetOrgHeight: integer;
function GetOrgWidth: integer;
procedure SetVideoRect(const Value: TRect);
procedure UpdateVideoWindowPos;
function GetAudioOnly:boolean;
protected
procedure SetNotifyWindow(const ahandle:HWND);
procedure SetWindow(const aHandle:HWND);virtual;
public
constructor Create;
destructor Destroy;override;
procedure BuildFilterGraph;virtual;abstract;
procedure RemoveAllFilters;
procedure Run;
procedure Stop;
procedure Pause;
procedure Rewind;
procedure GetVideoOrgSize(out width,height:integer);
published
property Control:TWinControl read FControl write SetControl;
property Handle:HWND read FHandle write SetHandle;
property DrainHandle:HWND read GetDrainHandle write SetDrainHandle;
property GraphObj:IGraphBuilder read FFilterGraph;
property ControlObj:IMediaControl read FMediaControl;
property EventObj:IMediaEventEx read FMediaEvent;
property SeekObj:IMediaSeeking read FMediaSeek;
property VideoWindow:IMediaSeeking read FMediaSeek;
property Position:TPlayPosition read GetPosition write SetPosition;
property Duration:int64 read GetDuration;
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;
TMMPlayer=class(TBasicPlayer)
private
FFilename: string;
procedure SetFilename(const Value: string);
public
procedure BuildFilterGraph;override;
published
property Filename:string read FFilename write SetFilename;
end;
TFilter=class(TCollectionItem)
private
FFilterInstance: IBaseFilter;
FFriendlyName: string;
procedure SetFilterInstance(const Value: IBaseFilter);
procedure SetFriendlyName(const Value: string);
published
property FilterInstance:IBaseFilter read FFilterInstance write SetFilterInstance;
property FriendlyName:string read FFriendlyName write SetFriendlyName;
end;
function SetPlayPosition(const curr,stop:int64):TPlayPosition;
implementation
uses sysutils,activeX;
function SetPlayPosition(const curr,stop:int64):TPlayPosition;
begin
result.Current:=curr;
result.Stop:=stop;
end;
{ TBasicPlayer }
constructor TBasicPlayer.Create;
var aEvent:IMediaEvent;
begin
CoCreateInstance(CLSID_FilterGraph,nil,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,FFilterGraph);
if FFilterGraph=nil then
raise Exception.Create('Inisialisasi filter graph manager gagal');
FFilterGraph.QueryInterface(IID_IMediaControl,FMediaControl);
FFilterGraph.QueryInterface(IID_IMediaEvent,aEvent);
aEvent.QueryInterface(IID_IMediaEventEx,FMediaEvent);
FFilterGraph.QueryInterface(IID_IMediaSeeking,FMediaSeek);
FFilterGraph.QueryInterface(IID_IVideoWindow,FVideoWindow);
end;
destructor TBasicPlayer.Destroy;
begin
RemoveAllFilters;
FVideoWindow.put_Visible(FALSE);
FVideoWindow.put_Owner(0);
FFilterGraph:=nil;
FMediaControl:=nil;
FMediaEvent:=nil;
FMediaSeek:=nil;
inherited;
end;
procedure TBasicPlayer.Run;
begin
FMediaControl.Run;
end;
procedure TBasicPlayer.Stop;
begin
FMediaControl.Stop;
end;
procedure TBasicPlayer.Pause;
begin
FMediaControl.Pause;
end;
procedure TBasicPlayer.SetHandle(const Value: HWND);
begin
if FHandle<>Value then
begin
FHandle := Value;
SetWindow(FHandle);
end;
end;
procedure TBasicPlayer.SetWindow(const aHAndle: HWND);
begin
SetNotifyWindow(AHandle);
end;
procedure TBasicPlayer.SetNotifyWindow(const ahandle: HWND);
begin
FMediaEvent.SetNotifyWindow(aHandle,WM_MMNOTIFY,integer(self));
end;
function TBasicPlayer.GetDuration: int64;
begin
FMediaSeek.GetDuration(result);
end;
function TBasicPlayer.GetPosition: TPlayPosition;
begin
FMediaSeek.GetPositions(result.current,
result.Stop);
end;
procedure TBasicPlayer.SetPosition(const Value: TPlayPosition);
var apos:TPlayPosition;
begin
apos:=value;
FMediaSeek.SetPositions(aPos.Current,
AM_SEEKING_AbsolutePositioning,
aPos.Stop,
AM_SEEKING_AbsolutePositioning);
end;
procedure TBasicPlayer.Rewind;
begin
SetPosition(SetPlayPosition(0,GetDuration));
end;
procedure TBasicPlayer.RemoveAllFilters;
var enum:IEnumFilters;
aFilter:IBaseFilter;
totread:cardinal;
begin
totRead:=0;
Stop;
FFilterGraph.EnumFilters(enum);
while (enum.Next(1,afilter,@totRead)=S_OK) do
begin
FFilterGraph.RemoveFilter(aFilter);
enum.Reset;
end;
enum:=nil;
end;
procedure TBasicPlayer.SetControl(const Value: TWinControl);
begin
FControl:= Value;
if FControl<>nil 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;
{TMMPlayer}
procedure TMMPlayer.BuildFilterGraph;
var wFilename:widestring;
begin
wFilename:=WideString(FFilename);
FFilterGraph.RenderFile(PWideChar(wFilename),nil);
end;
procedure TMMPlayer.SetFilename(const Value: string);
begin
FFilename := Value;
end;
initialization
CoInitialize(nil);
finalization
CoUnInitialize;
end.
Berikut ini contoh aplikasi yang memanfaatkan kelas TMMPlayer yang baru.
unit ufrmMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,DirectShow, StdCtrls, ExtCtrls,
uDirectShowPlayer, ComCtrls, ActnList;
type
TfrmMediaPlayer = class(TForm)
btnOpen: TButton;
OpenDialog1: TOpenDialog;
btnPlay: TButton;
btnStop: TButton;
Timer1: TTimer;
ProgressBar1: TProgressBar;
lblProgress: TLabel;
btnPause: TButton;
chkbxFullscreen: TCheckBox;
ActionList1: TActionList;
actToggleFullscreen: TAction;
rdgrpZoom: TRadioGroup;
procedure btnOpenClick(Sender: TObject);
procedure btnPlayClick(Sender: TObject);
procedure btnStopClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure btnPauseClick(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;
destructor Destroy;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;
destructor TfrmMediaPlayer.Destroy;
begin
FMMPlayer.Free;
inherited;
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.btnPlayClick(Sender: TObject);
begin
Timer1.Enabled:=true;
FMMPlayer.Run;
end;
procedure TfrmMediaPlayer.btnStopClick(Sender: TObject);
begin
Timer1.Enabled:=false;
FMMPlayer.Stop;
FMMPlayer.Rewind;
ProgressBar1.Position:=0;
lblProgress.Caption:='0%';
frmVideo.Hide;
end;
procedure TfrmMediaPlayer.Timer1Timer(Sender: TObject);
begin
ProgressBar1.Position:=round(FMMPlayer.Position.Current/FDuration*ProgressBar1.Max);
lblProgress.Caption:=inttostr(ProgressBar1.Position)+'%';
end;
procedure TfrmMediaPlayer.btnPauseClick(Sender: TObject);
begin
FMMPlayer.Pause;
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 (orgW<>0) and (orgH<>0) 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.
DFM untuk form di atas sebagai berikut:
object frmMediaPlayer: TfrmMediaPlayer
Left = 192
Top = 127
BorderStyle = bsDialog
Caption = 'Simple MediaPlayer DirectShow'
ClientHeight = 86
ClientWidth = 681
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnActivate = FormActivate
PixelsPerInch = 96
TextHeight = 13
object lblProgress: TLabel
Left = 647
Top = 16
Width = 22
Height = 13
end
object btnOpen: TButton
Left = 16
Top = 16
Width = 75
Height = 25
Caption = 'Open'
TabOrder = 0
OnClick = btnOpenClick
end
object btnPlay: TButton
Left = 96
Top = 16
Width = 75
Height = 25
Caption = 'Play'
TabOrder = 1
OnClick = btnPlayClick
end
object btnStop: TButton
Left = 16
Top = 48
Width = 75
Height = 25
Caption = 'Stop'
TabOrder = 2
OnClick = btnStopClick
end
object ProgressBar1: TProgressBar
Left = 200
Top = 16
Width = 441
Height = 19
Smooth = True
TabOrder = 3
end
object btnPause: TButton
Left = 96
Top = 48
Width = 75
Height = 25
Caption = 'Pause'
TabOrder = 4
OnClick = btnPauseClick
end
object chkbxFullscreen: TCheckBox
Left = 192
Top = 56
Width = 97
Height = 17
Caption = 'Fullscreen'
TabOrder = 5
OnClick = chkbxFullscreenClick
end
object rdgrpZoom: TRadioGroup
Left = 288
Top = 40
Width = 353
Height = 33
Caption = 'Zoom'
Columns = 3
ItemIndex = 1
Items.Strings = (
'50%'
'100%'
'200%')
TabOrder = 6
OnClick = rdgrpZoomClick
end
object OpenDialog1: TOpenDialog
Left = 560
Top = 40
end
object Timer1: TTimer
Enabled = False
Interval = 1
OnTimer = Timer1Timer
Left = 592
Top = 40
end
object ActionList1: TActionList
Left = 632
Top = 40
object actToggleFullscreen: TAction
Caption = 'actToggleFullscreen'
ShortCut = 16454
OnExecute = actToggleFullscreenExecute
end
end
end
Form di mana video ditampilkan
unit ufrmVideo;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, ActnList,uDirectShowPlayer;
type
TfrmVideo = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
published
end;
var
frmVideo: TfrmVideo;
implementation
{$R *.dfm}
end.
DFM nya sebagai berikut:
object frmVideo: TfrmVideo
Left = 208
Top = 194
BorderStyle = bsDialog
Caption = 'Video'
ClientHeight = 442
ClientWidth = 688
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
KeyPreview = True
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
end
Download source code tersedia di sini.
Kita telah mendiskusikan langkah-langkah yang diperlukan untuk menampilkan video di window kita sendiri. Kita juga telah mendiskusikan cara menampilkan video dalam mode fullscreen, membelokkan message-message dan juga bagaimana melakukan zooming ukuran display video sesuai keinginan kita.
Anda suka artikel ini? Bantu website ini berkembang dengan menyumbang. Berapapun jumlahnya akan sangat dihargai.
Atau Anda dapat membantu dengan membuat bookmark.
Bookmark this on Delicious