









This tutorial continues previous tutorial, Multimedia Player with DirectShow Part 1. This time, we are going to discuss how to display video on our own window.
If you already try multimedia player application in first part of the tutorial, you will get video displayed on their own window when playing a video file. Then how if we want to display the video, for example, on our own form?
Filter Graph Manager implements IVideoWindow interface. This interface is interface we use to manipulate how video get displayed. We can zoom video in 50%, 100%, 200% or any scales on particular window. Video can also be displayed in fullscreen.
To get pointer to this interface, we call QueryInterface of Filter graph manager
var FVideoWindow:IVideoWindow;
FFilterGraph.QueryInterface(IID_IVideoWindow,FVideoWindow);
If succeed FVideoWindow will be filled with pointer to IVideoWindow instance. To be able to display video on a window, the steps are as follows:
var FWindowHandle:HWND;
FVideoWindow.put_Owner(OAHWND(FWindowHandle));
FVideoWindow.put_visible(true);
Note: window referred by FWindowHandle must be visible too to let video
visible. 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);
Changing width and height cause zoom to happen if new width and new height
is not same with width and height of the original video. Zoom doesn't count
aspect ratio. If we need to zoom and maintain same aspect ratio, we must
get width and height of the original video. How we retrieve these data will
be discussed after this.IVideoWindow has put_FullscreenMode() method that we can use to change mode to fullscreen or windowed. If it is true, video is shown in fullscreen mode, otherwise it is shown in windowed mode.
Fullscreen mode:
FVideoWindow.put_FullscreenMode(true);
Windowed mode:
FVideoWindow.put_FullscreenMode(false);
To get status of status of fullscreen mode,
var statusFull:longBool;
FVideoWindow.get_FullscreenMode(statusFull);
When we're in fullscreen mode, our application doesn't receive focus, so all messages will be sent to filter graph manager. To let our application receive messages even in fullscreen mode (so we can, for example, toggle fullscreen on/off), we need to intercept messages to our application with put_MesageDrain() of IVideoWindow.
put_MessageDrain() expects window handle that will handle messages.
FVideoWindow.put_MessageDrain(FHandleWindow);
To get window handle intercepting messages we use IVideoWindow.get_MessageDrain().
FVideoWindow.Get_MessageDrain(FHandleWindow);
After we finish with IVideoWindow, we must change visibility status to false. If we failed to do that, video will keep showing on the screen and user will not be able to get rid of it. After that, owner window must be set to null with put_Owner().
FVideoWindow.put_Visible(false);
FvideoWindow.put_Owner(0);
To be able to zoom and maintain aspect ratio, we need to know width and height of the original video. IBasicVideo interface, can be queried from Filter graph manager, is here to do that job.
var orgVideoWidth,orgVideoHeight:integer;
FBasicVideo.get_VideoWidth(orgVideoWidth);
FBasicVideo.get_VideoHeight(orgVideoHeight);
Sometime, we need to check whether a media we played contains video and audio data or only audio. For example, we want to display video window only when media contains video and audio. To do that, we must query filter graph manager for IBasicVideo interface. If it returns nil then media is not video or codec for particular format has not yet been installed. If it's not nil value, we must check IVideoWindow visibilility status. If get_visible() call fails, media is not video. For example:
var basicVideo:IBasicVideo;
vis:longBool;
FFilterGraph.QueryInterface(IID_IBasicVideo,basicVideo);
audioOnly:=(basicVideo=nil) or (failed(FVideoWindow.get_Visible(vis)));
Code below is new implementation of TBasicPlayer class.
{---------------------------------
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.
Following code is sample application built with new TMMPlayer class.
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 200%
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 for form above is as follow:
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 where video is shown:
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.
Its corresponding DFM is as follow:
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
Source code is available for download here.
We have discussed required steps to display video in our own window. We also have discussed how to display video in fullscreen mode, how to intercept messages and how to zoom video display as we like.
Do you like this article? Help this website improve by donating. Any amounts is appreciated.
Or you can help by bookmarking this page.
Bookmark this on Delicious