juhara.com
Language : English Indonesia

Multimedia Player with DirectShow Part 2

Zamrony P. Juhara
15 September 2006 14:30:00
 (7581 views)
This article continues previous article with same title. Here we discuss how to display video in our own window and fullscreen

Introduction

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.

Displaying video on particular 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:

  1. Set video to target window handle. You can do that with put_Owner() method. Parameter of this function is window handle. DirectShow uses OAHWND type to refere to window handle. So window handle must be typecasted to OAHWND.
    var FWindowHandle:HWND;
    FVideoWindow.put_Owner(OAHWND(FWindowHandle));
  2. Change visibility status. To make video visible on window, we need to change visibility of video with put_visible() method of IVideoWindow. Its parameter is visibility in longbool type.
    FVideoWindow.put_visible(true);
    Note: window referred by FWindowHandle must be visible too to let video visible.
  3. Change Style of Window. Style of window is set with SetWindowStyle of IVideoWindow. Window need to be set to child window with WS_CHILD and to ensure video is not drawn outside window area, we need to useWS_CLIPSIBLINGS style.
    FVideoWindow.SetWindowStyle(WS_CLIPSIBLINGS or WS_CHILD);
  4. Change Position of video on Window. Video can be placed anywhere on window's clientarea with SetPosition() of IVideoWindow.
    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.

Displaying Video in Fullscreen

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);

Clean Up

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);

Getting Width and Height of Original Video.

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);

Checking whether a media has video data.

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.

Conclusion

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.

Related Article

Do you like this article? Help this website improve by donating. Any amounts is appreciated.

Or you can help by bookmarking this page. Delicious Bookmark this on Delicious