









DirectShow adalah salah satu komponen DirectX yang berguna untuk streaming multimedia. Tutorial ini sebenarnya adalah bagian pertama dari tutorial menciptakan aplikasi TV Tuner (terinspirasi pertanyaan yang diposting mikroplus di milis Delphindo tentang bagaimana membuat aplikasi TV Tuner). Karena untuk membuat aplikasi TV Tuner memanfaatkan DirectShow butuh pengetahuan tentang Directshow, maka untuk bagian pertama, tutorial ini membahas langkah-langkah dasar membuat aplikasi DirectShow. Untuk membuat aplikasi sederhana dengan DirectShow tidak terlalu susah.
Anda membutuhkan perangkat lunak berikut ini:
Untuk menginisialisasi COM, kita panggil CoInitialize(). Contoh:
CoInitialize(nil);
CoInitialize() dideklarasikan di unit Activex.pas.
DirectShow menggunakan istilah filter graph (biasa disebut filter) untuk mengacu pada komponen software yang memproses data multimedia. Filter graph manager tentunya adalah yang mengatur filter-filter tesebut.
Untuk menciptakan Filter Graph Manager kita bisa menggunakan fungsi CoCreateInstance(), juga dideklarasi di activex.pas
Contohnya:
var FFilterGraph:IGraphBuilder;
CoCreateInstance(CLSID_FilterGraph,nil,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
FFilterGraph);
CLSID_FilterGraph adalah classID untuk filter graph manager (dideklarasikan di unit Directshow.pas), CLSCTX_INPROC_SERVER mengindikasikan kita akan menggunakan in-process COM server (DirectX adalah in-process COM server). IID_IGraphBuilder adalah interface ID untuk IGraphBuilder. Interface IGraphiBuilder adalah interface filter graph manager. Jika sukses FFilterGraph akan diisi pointer ke interface IGraphBuilder, jika gagal isinya nil.
Filter DirectShow cukup banyak, salah satu filter adalah filter reader untuk membaca dan menciptakan filter untuk memainkan suatu file multimedia. Untuk membaca file multimedia dan memainkannya, IGraphBuilder dilengkapi fungsi RenderFile() yang berfungsi untuk mengkonstruksi filter-filter dan menambahkan filter graph ke filter graph manager. Contoh
var FFilename:string;
wFilename:widestring;
begin
wFilename:=WideString(FFilename);
FFilterGraph.RenderFile(PWideChar(wFilename),nil);
end;
RenderFile() mengharapkan nama file yang akan di render. Namanya bertipe PWideChar (2 byte per 1 karakter) oleh karena itu, jika inputnya bertipe string harus di typecast ke widestring. Parameter kedua adalah PlayList. Parameter ini reserved dan harus diisi nil.
RenderFile tidak menghapus filter graph yang sebelumnya sudah ada. Jika sebelumnya kita memanggil RenderFile dua kali maka ketika dimainkan kedua filter dimainkan bersama-sama.
IGraphBuilder memiliki metode RemoveFilter digunakan untuk menghapus sebuah filter dari filter graph. Contoh:
FFilterGraph.RemoveFilter(aFilter);
Di mana aFilter adalah filter graph yang akan dihapus bertipe IBaseFilter. Untuk mendapatkan filter apa saja yang ada di filter graph manager, kita menggunakan enumFilters().
FFilterGraph.EnumFilters(enum);
enum bertipe IEnumFilters. Interface ini memiliki metode bernama Next yang digunakan untuk mengakses filter berikut. Contoh
while (enum.Next(1,afilter,@totRead)=S_OK) do
begin
//lakukan sesuatu dengan filter
end;
Parameter pertama adalah jumlah total filter yang ingin diambil. Dicontoh di atas, kita mengambil filter satu-satu. afilter adalah filter bertipe IBaseFilter, totRead adalah pointer ke variable yang akan menampung jumlah aktual fiter yang dibaca. Menurut Micosoft, afilter adalah array IBaseFilter sebanyak total filter yang direquest, tapi dari deklarasinya di DirectShow.pas, tipenya adalah IBaseFilter.
Untuk meremove semua filter contohnya:
while (enum.Next(1,afilter,@totRead)=S_OK) do
begin
FFilterGraph.RemoveFilter(aFilter);
enum.Reset();
end;
Setelah dihapus, enum menjadi tidak sinkron lagi (out of sync). Agar enum isinya sinkron, perlu kita reset.
Interface-interface lain yang kita perlukan adalah IMediaControl berguna untuk mengontrol proses streaming multimedia data. IMediaSeeking untuk mengubah-ubah/mencari posisi dalam stream multimedia. IMediaEvent dan IMediaEventEx.digunakan untuk proses notifikasi event. Untuk mendapatkan interface-interface tersebut (kecuali IMediaEventEx) kita meng-query dari interface IGraphBuilder. Contoh:
var FMediaControl:IMediaControl;
aEvent:IMediaEvent;
FMediaEvent:IMediaEventEx;
FMediaSeek:IMediaSeek;
FFilterGraph.QueryInterface(IID_IMediaControl,FMediaControl);
FFilterGraph.QueryInterface(IID_IMediaEvent,aEvent);
aEvent.QueryInterface(IID_IMediaEventEx,FMediaEvent);
FFilterGraph.QueryInterface(IID_IMediaSeeking,FMediaSeek);
Khusus untuk IMediaEventEx, tidak langsung diquery dari IGraphBuilder. IMediaEventEx adalah ekstensi dari IMediaEvent dan harus diquery dari IMediaEvent.
Agar kita dapat mengetahui event-event yang terjadi kita perlu memberitahu DirectShow bahwa kita ingin diberi notifikasi. Caranya adalah dengan dengan menggunakan fungsi SetNotifyWindow() milik IMediaEventEx. Contoh
FMediaEvent.SetNotifyWindow(aHandle,WM_MMNOTIFY,integer(self));
Parameter aHandle adalah handle window yang akan menerima pesan WM_MMNOTIFY. Di mana WM_MMNOTIFY adalah message yang kita definisikan sendiri. Syaratnya message ini harus berada antara WM_APP-WM_APP+$BFFF, contoh:
const WM_MMNOTIFY=WM_APP+$1234;
Jangan lupa tambahkan unit messages.pas. Parameter ketiga adalah data milik kita sendiri, isinya bebas. Nantinya data ini dipass ke message handler melalui parameter lParam pesan WM_MMNOTIFY. Pada contoh di atas kita mengirimkan address instance kelas.
Buat sebuah event handler untuk WM_MMNOTIFY, contohnya:
procedure WM_MMNotify(var msg:TMessage);message WM_MMNOTIFY;
dan pada implementasinya
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
//lakukan sesuatu
end;
end;
aplayer.EventObj.FreeEventParams(evCode,param1,param2);
end;
msg.lParam akan berisi alamat instance kelas (lihat "Mengirim permintaan notifikasi DirectShow" di atas).
IMediaEventEx memiliki metode GetEvent yang berguna untuk mendapatkan kode event yang sedang terjadi beserta parameter-parameternya. Berhubung fungsi ini mengalokasikan memori, untuk parameter-parameter event, kita wajib memanggil FreeEventParams() untuk mencegah memory leak. Mengenai daftar lengkap kode-kode event, bisa dilihat di dokumentasi DirectX. Yang paling sering saya pakai adalah EC_COMPLETE, yang berarti proses streaming data telah selesai.
IMediaControl digunakan untuk kontrol aliran data multimedia.
Untuk memulai streaming kita menggunakan Run. Contoh
FMediaControl.Run;
Untuk menghentikan streaming kita menggunakan Stop, contoh
FMediaControl.Stop;
Untuk pause, contoh
FMediaControl.Pause;
IMediaSeeking kita pergunakan untuk mencari posisi dalam stream multimedia. Dengan IMediaSeeking, kita bisa memainkan stream dari mana saja. Dari awal stream, dari tengah atau lainnya. Directshow menggunakan dua istilah posisi untuk seeking, yakni current position dan stop position. Current position adalah posisi streaming saat ini, sedangkan stop position adalah posisi di mana streaming akan dihentikan. Untuk mendapatkan current position dan stop position, kita menggunakan GetPositions().
FMediaSeek.GetPositions(currentPos,
StopPos);
Untuk mengeset current position dan stop position menggunakan SetPositions().
FMediaSeek.SetPositions(CurrentPos,
AM_SEEKING_AbsolutePositioning,
StopPos,
AM_SEEKING_AbsolutePositioning);
Untuk mendapatkan total durasi stream, kita menggunakan GetDuration().
FMediaSeek.GetDuration(durasi);
Current pos, stop pos dan durasi semuanya bertipe int64.
//membuang semua filter graph dari filter graph manager
RemoveAllFilters;
FMediaControl:=nil;
FMediaEvent:=nil;
FMediaSeeking:=nil;
FFilterGraph:=nil;
CoUninitialize;
Berikut ini contoh implementasi kelas player multimedia, implementasinya belum lengkap dan belum dicoba pada semua tipe file. Format file yang sudah dites adalah WAV, MP3, MIDI, MPEG, AVI, WMV,WMA, ASF.
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;
FHandle: HWND;
procedure SetHandle(const Value: HWND);
function GetDuration: int64;
function GetPosition: TPlayPosition;
procedure SetPosition(const Value: TPlayPosition);
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;
published
property Handle:HWND read FHandle write SetHandle;
property GraphObj:IGraphBuilder read FFilterGraph;
property ControlObj:IMediaControl read FMediaControl;
property EventObj:IMediaEventEx read FMediaEvent;
property SeekObj:IMediaSeeking read FMediaSeek;
property Position:TPlayPosition read GetPosition write SetPosition;
property Duration:int64 read GetDuration;
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;
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);
end;
destructor TBasicPlayer.Destroy;
begin
RemoveAllFilters;
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;
{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.
Kode berikut ini adalah contoh aplikasi yang memanfaatkan kelas TMMPlayer.
unit ufrmMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,DirectShow, StdCtrls, ExtCtrls,
uDirectShowPlayer, ComCtrls;
type
TfrmMediaPlayer = class(TForm)
btnOpen: TButton;
OpenDialog1: TOpenDialog;
btnPlay: TButton;
btnStop: TButton;
Timer1: TTimer;
ProgressBar1: TProgressBar;
lblProgress: TLabel;
btnPause: TButton;
procedure btnOpenClick(Sender: TObject);
procedure btnPlayClick(Sender: TObject);
procedure btnStopClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure btnPauseClick(Sender: TObject);
private
FDuration:int64;
FMMPlayer:TMMPlayer;
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
{$R *.dfm}
{ TForm1 }
constructor TfrmMediaPlayer.Create(AOwner: TComponent);
begin
inherited;
FMMPlayer:=TMMPlayer.Create;
FMMPlayer.Handle:=Handle;
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;
end;
aplayer.EventObj.FreeEventParams(evCode,param1,param2);
end;
procedure TfrmMediaPlayer.btnOpenClick(Sender: TObject);
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;
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%';
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;
end.
Berikut ini adalah file DFM form diatas
object frmMediaPlayer: TfrmMediaPlayer
Left = 192
Top = 127
BorderStyle = bsDialog
Caption = 'Simple MediaPlayer DirectShow'
ClientHeight = 84
ClientWidth = 681
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object lblProgress: TLabel
Left = 408
Top = 48
Width = 51
Height = 13
Caption = 'lblProgress'
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 OpenDialog1: TOpenDialog
Left = 496
Top = 48
end
object Timer1: TTimer
Enabled = False
Interval = 1
OnTimer = Timer1Timer
Left = 544
Top = 48
end
end
Download source code di sini.
Kita telah mendiskusikan langkah-langkah dasar menggunakan DirectShow, meliputi menciptakan filter graph manager, mendapatkan interface kontrol media, mendapatkan interface media seeking dan bagaimana memainkan file multimedia seperti file audio dan movie menggunakan filter graph manager.
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