Click here to Skip to main content
13,801,290 members
Click here to Skip to main content

Stats

723.1K views
29.8K downloads
281 bookmarked
Posted 14 Sep 2010
Licenced CPOL

PVS.AVPlayer - MCI Audio and Video Library

, 7 Aug 2018
Windows Media Control Interface (MCI) library with many added features
PVS.AVPlayer
PVS.AVPlayer .NET 2.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 3.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 3.5
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.5
PVS.AVPlayer .NET 4.5.1
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.5.2
PVS.AVPlayer.XML
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6.1
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6.2
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.7
PVS.AVPlayer .NET 4.7.1
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.7.2
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer All Source Code
AVPlayerExample
AVPlayerExample
AVPlayerExample.csproj.user
bin
Debug
PVS.AVPlayer.XML
Release
Dialogs
Display Overlays
obj
Debug
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
WingDings3a.ttf
Voice Recorder
FolderView
FolderView
bin
Debug
PVS.AVPlayer.XML
Release
FolderView.csproj.user
obj
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
PVS.AVPlayer
AVPlayerExample.csproj.user
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Custom Items
Native Methods
Bob.png
Crystal Italic1.ttf
Dial Green 2.png
Dial Green 4.png
Dial Green.png
Dial Red 2.png
Dial Red.png
media7.ico
media7a.ico
Media8.ico
Media8a.ico
VU Meter.png
WingDings3a.ttf
Sound Recorder
Various
About Dialog
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Custom Items
FolderView.csproj.user
Debug
Bob.png
Crystal Italic1.ttf
media7a.ico
media7b.ico
Media8a.ico
Media8b.ico
Subtitles Overlay
Various
How To (C#)
PVSAVPlayerHowTo
bin
Debug
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Release
obj
Debug
Release
Properties
How To (VB.NET)
PVSAVPlayerHowToVB
bin
Debug
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Release
My Project
Application.myapp
obj
Debug
Release
PVSAVPlayerHowTo.vbproj.user
PVS.AVPlayer Examples
AVPlayerExample.ex_
FolderView.ex_
AVPlayerExample.exe
FolderView.exe
PVS.AVPlayer.dll
/****************************************************************

    PVS.AVPlayer - Version 0.91
    August 2018, The Netherlands
    © Copyright 2018 PVS The Netherlands - Free to Use

    ****************

    For use with Microsoft .NET Framework version 2.0 or higher and any CPU.
    Created with Microsoft Visual Studio.

    Articles on CodeProject with information on the use of the PVS.AVPlayer library:
    About the Player: http://www.codeproject.com/Articles/109714/PVS-AVPlayer-MCI-Audio-and-Video-Library
    About the Sound Recorder: http://www.codeproject.com/Articles/1116698/PVS-AVPlayer-MCI-Sound-Recorder

    ****************

    The PVS.AVPlayer library source code is divided into 10 files:

     1. Player.cs            - Player source code
     2. MouseEvents.cs       - extension source code of Player.cs, provides player display mouse events
     3. PeakMeter.cs         - extension source code of Player.cs, provides player audio peak level values
     4. DisplayClones.cs     - extension source code of Player.cs, provides player multiple video displays 
     5. Subtitles.cs         - extension source code of Player.cs, provides player SubRip (.srt) subtitles
     6. Signals.cs           - extension source code of Player.cs, provides player media position signaling
     7. CursorHide.cs        - extension source code of Player.cs, hides the mouse cursor during inactivity
     8. Recorder.cs          - Sound Recorder source code
     9. PlayerRecorder.cs    - code used by both Player.cs (and its extension files) and Recorder.cs
    10. Infolabel.cs         - Info Label (custom ToolTip) source code

    Required references:
    System
    System.Drawing
    System.Windows.Forms

    ****************

    This file: Player.cs

    Player Class
    Uses files 'PlayerRecorder.cs', 'MouseEvents.cs', 'OutputLevel.cs', 'DisplayClones.cs', 'Subtitles.cs',
    'Signals.cs' and 'CursorHide.cs'.

    ****************

    About Media Control Interface (MCI)
    - for information about MCI, please see https://msdn.microsoft.com/en-us/library/vs/alm/dd757151(v=vs.85).aspx
    - you can find many articles about mci on the internet, search examples: 'c# mci', 'vb mci', 'mcisendstring'
      or the subject of your question.
    - the PVS.AVPlayer library also provides 'direct access' to MCI (e.g. 'Mci.MciSendString').

    ****************

    The PVS.AVPlayer.Player 'operating steps' for every media file to play:
    1. open player (mci device; e.g. 'open "C:\MyMovies\MyMovie.mpg" type mpegvideo alias myPlayer')
    2. set player properties (e.g. 'set myPlayer time format ms' / 'setaudio myPlayer left volume to 500')
    3. start playing (e.g. 'play myPlayer')
    4. stop playing (e.g. 'stop myPlayer' or when media has finished playing (mci notify / Media Ended event))
    5. close player (mci device; e.g. 'close myPlayer')

    The 'open' and 'close' device commands are 'hidden' from the user:
    The user only has to use the 'play' and 'stop' commands.

    ****************

    Thanks!

    Many thanks to Microsoft (Windows, .NET Framework, Visual Studio Express, etc.), all the people
    writing about programming on the internet (a great source for ideas and solving problems),
    the websites publishing those or other writings about programming, the people responding
    to the PVS.AVPlayer articles with comments and suggestions and, of course, CodeProject:
    thank you Deeksha, Smitha and Sean Ewington for the beautiful article and all!

    TaskbarProgress class based on code by WhoIsRich at:
    stackoverflow.com/questions/1295890/windows-7-progress-bar-in-taskbar-in-c

    Menu Monitor based on code by nicholas at:
    stackoverflow.com/questions/35613926/detect-if-a-contextmenustrip-is-displayed-or-intercepting-key-events

    Special thanks to Sean Ewington of CodeProject who also took care of publishing the many code
    updates and changes in the PVS.AVPlayer articles in a friendly, fast, and highly competent manner.
    Thank you very much, Sean!

    Peter Vegter
    August 2018, The Netherlands

    ****************************************************************/

#region Usings

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Text;
using System.Windows.Forms;

#endregion

namespace PVS.AVPlayer
{
    // ******************************** PVS.AVPlayer (Player) - Enumerations

    #region PVS.AVPlayer (Player) - Enumerations

    /// <summary>
    /// Specifies the size and location of the video on the display of the player.
    /// </summary>
    public enum DisplayMode
    {
        /// <summary>
        /// Size: original size.
        /// Location: topleft of the display of the player.
        /// Display resize: shrink: no, grow: no.
        /// </summary>
        Normal,
        /// <summary>
        /// Size: original size.
        /// Location: center of the display of the player.
        /// Display resize: shrink: no, grow: no.
        /// </summary>
        Center,
        /// <summary>
        /// Size: same size as the display of the player.
        /// Location: topleft of the display of the player.
        /// Display resize: shrink: yes, grow: yes.
        /// </summary>
        Stretch,
        /// <summary>
        /// Size: the largest possible size within the display of the player while maintaining the aspect ratio.
        /// Location: topleft of the display of the player.
        /// Display resize: shrink: yes, grow: yes.
        /// </summary>
        Zoom,
        /// <summary>
        /// Size: the largest possible size within the display of the player while maintaining the aspect ratio.
        /// Location: center of the display of the player.
        /// Display resize: shrink: yes, grow: yes.
        /// </summary>
        ZoomCenter,
        /// <summary>
        /// Size: same size as the display of the player while maintaining the aspect ratio, but possibly with horizontal or vertical image cropping.
        /// Location: center of the display of the player.
        /// Display resize: shrink: yes, grow: yes.
        /// </summary>
        CoverCenter,
        /// <summary>
        /// Size: original size or the largest possible size within the display of the player while maintaining the aspect ratio.
        /// Location: topleft of the display of the player.
        /// Display resize: shrink: yes, grow: if smaller than original size.
        /// </summary>
        SizeToFit,
        /// <summary>
        /// Size: original size or the largest possible size within the display of the player while maintaining the aspect ratio.
        /// Location: center of the display of the player.
        /// Display resize: shrink: yes, grow: if smaller than original size.
        /// </summary>
        SizeToFitCenter,
        /// <summary>
        /// Size, Location and Display resize: set from Player.Display.SetCustomMode callback method.
        /// </summary>
        Custom,
        /// <summary>
        /// Size: set manually.
        /// Location: set manually.
        /// Display resize: shrink: no, grow: no.
        /// </summary>
        Manual
    }

    /// <summary>
    /// Specifies the fullscreen mode of the player.
    /// </summary>
    public enum FullScreenMode
    {
        /// <summary>
        /// The display of the player is shown fullscreen.
        /// </summary>
        Display,
        /// <summary>
        /// The (parent) control that contains the display of the player is shown fullscreen.
        /// </summary>
        Parent,
        /// <summary>
        /// The form that contains the display of the player is shown fullscreen.
        /// </summary>
        Form
    }

    /// <summary>
    /// Specifies the size mode of the display overlay of the player.
    /// </summary>
    public enum OverlayMode
    {
        /// <summary>
        /// The overlay has the same size and position as the display of the player.
        /// </summary>
        Display,
        /// <summary>
        /// The overlay has the same size and position as the visible part of the video image on the display of the player.
        /// </summary>
        Video
    }

    /// <summary>
    /// Specifies the part of the playing media whose length (duration) is to be obtained.
    /// </summary>
    public enum MediaLength
    {
        /// <summary>
        /// The total length (duration) of the playing media.
        /// </summary>
        BeginToEnd,
        /// <summary>
        /// The length (duration) of the playing media from the natural beginning of the media to the current position.
        /// </summary>
        FromBegin,
        /// <summary>
        /// The length (duration) of the playing media from Player.Media.StartPosition to the current position.
        /// </summary>
        FromStart,
        /// <summary>
        /// The length (duration) of the playing media from the current position to the natural end of the media.
        /// </summary>
        ToEnd,
        /// <summary>
        /// The length (duration) of the playing media from the current position to Player.Media.StopPosition.
        /// </summary>
        ToStop,
        /// <summary>
        /// The length (duration) of the playing media from Player.Media.StartPosition to Player.Media.StopPosition.
        /// </summary>
        StartToStop,
    }

    /// <summary>
    /// Specifies the part of the file name of the playing media to be obtained. 
    /// </summary>
    public enum MediaName
    {
        /// <summary>
        /// The file name without path and extension of the playing media.
        /// </summary>
        FileNameWithoutExtension,
        /// <summary>
        /// The file name and extension without path of the playing media.
        /// </summary>
        FileName,
        /// <summary>
        /// The file name with path and extension of the playing media.
        /// </summary>
        FullPath,
        /// <summary>
        /// The extension of the file name of the playing media.
        /// </summary>
        Extension,
        /// <summary>
        /// The path (directory) of the file name of the playing media.
        /// </summary>
        DirectoryName,
        /// <summary>
        /// The root path (root directory) of the file name of the playing media.
        /// </summary>
        PathRoot
    }

    /// <summary>
    /// Specifies the area of the screen to copy.
    /// </summary>
    public enum ScreenCopyMode
    {
        /// <summary>
        /// The (visible part of the) video on the display of the player.
        /// </summary>
        Video,
        /// <summary>
        /// The display of the player.
        /// </summary>
        Display,
        /// <summary>
        /// The (parent) control that contains the display of the player.
        /// </summary>
        Parent,
        /// <summary>
        /// The display area of the form that contains the display of the player.
        /// </summary>
        Form,
        /// <summary>
        /// The (entire) screen that contains the display of the player.
        /// </summary>
        Screen
    }

    /// <summary>
    /// Specifies the display mode of the positionslider controlled by the player.
    /// </summary>
    public enum PositionSliderMode
    {
        /// <summary>
        /// The positionslider shows the playback position of the playing media from Player.Media.StartPosition to Player.Media.StopPosition.
        /// </summary>
        Progress,
        /// <summary>
        /// The positionslider shows the playback position from the natural beginning of the media to the natural end of the media.
        /// </summary>
        Track
    }

    /// <summary>
    /// Specifies the display mode of the taskbar progress indicator.
    /// </summary>
    public enum TaskbarProgressMode
    {
        /// <summary>
        /// The taskbar progress indicator shows the playback position of the playing media from Player.Media.StartPosition to Player.Media.StopPosition.
        /// </summary>
        Progress,
        /// <summary>
        /// The taskbar progress indicator shows the playback position from the natural beginning of the media to the natural end of the media.
        /// </summary>
        Track
    }

    /// <summary>
    /// Specifies where to retrieve an album art image.
    /// </summary>
    public enum ImageSource
    {
        /// <summary>
        /// No album art image is retrieved.
        /// </summary>
        None,
        /// <summary>
        /// The album art image is retrieved from the media file.
        /// </summary>
        MediaOnly,
        /// <summary>
        /// The album art image is retrieved from the media file or, if not found, from the folder of the media file.
        /// </summary>
        MediaOrFolder,
        /// <summary>
        /// The album art image is retrieved from the folder of the media file or, if not found, from the media file.
        /// </summary>
        FolderOrMedia,
        /// <summary>
        /// The album art image is retrieved from the folder of the media file.
        /// </summary>
        FolderOnly
    }

    /// <summary>
    /// Specifies the reason that media has stopped playing.
    /// </summary>
    public enum StopReason
    {
        /// <summary>
        /// The media has reached its natural end or stop position.
        /// </summary>
        Finished,
        /// <summary>
        /// The media is stopped by the player to play other media.
        /// </summary>
        AutoStop,
        /// <summary>
        /// The media is stopped by using the stop method of the player.
        /// </summary>
        UserStop,
    }

    /// <summary>
    /// Specifies the shape of the display window of the player.
    /// </summary>
    public enum DisplayShape
    {
        /// <summary>
        /// The display window of the player has the shape of an arrow pointing down.
        /// </summary>
        ArrowDown,
        /// <summary>
        /// The display window of the player has the shape of an arrow pointing left.
        /// </summary>
        ArrowLeft,
        /// <summary>
        /// The display window of the player has the shape of an arrow pointing right.
        /// </summary>
        ArrowRight,
        /// <summary>
        /// The display window of the player has the shape of an arrow pointing up.
        /// </summary>
        ArrowUp,
        /// <summary>
        /// The display window of the player has the shape of 5 vertical bars.
        /// </summary>
        Bars,
        /// <summary>
        /// The display window of the player has the shape of 5 horizontal bars.
        /// </summary>
        Beams,
        /// <summary>
        /// The display window of the player has the shape of a cross.
        /// </summary>
        Cross,
        /// <summary>
        /// The display window of the player has a user-defined shape.
        /// </summary>
        Custom,
        /// <summary>
        /// The display window of the player has a diamond shape.
        /// </summary>
        Diamond,
        /// <summary>
        /// The display window of the player has a heart shape.
        /// </summary>
        Heart,
        /// <summary>
        /// The display window of the player has a hexagonal shape.
        /// </summary>
        Hexagon,
        /// <summary>
        /// The display window of the player has its normal shape.
        /// </summary>
        Normal,
        /// <summary>
        /// The display window of the player has an oval shape.
        /// </summary>
        Oval,
        /// <summary>
        /// The display window of the player has a rounded rectangular shape.
        /// </summary>
        Rounded,
        /// <summary>
        /// The display window of the player has the shape of a 8-pointed star.
        /// </summary>
        Star,
        /// <summary>
        /// The display window of the player has the shape of 3 by 3 tiles.
        /// </summary>
        Tiles,
        /// <summary>
        /// The display window of the player has a triangular shape.
        /// </summary>
        Triangle
    }

    /// <summary>
    /// Specifies the reason that the mouse cursor is hidden or shown.
    /// </summary>
    public enum CursorChangedReason
    {
        /// <summary>
        /// The mouse cursor is hidden because it was inactive for a certain time.
        /// </summary>
        MouseIdle,
        /// <summary>
        /// The mouse cursor is shown because the mouse has been moved.
        /// </summary>
        MouseMoved,
        /// <summary>
        /// The mouse cursor is shown because a mouse button was used.
        /// </summary>
        MouseAction,
        /// <summary>
        /// The mouse cursor is shown because a menu has been opened.
        /// </summary>
        MenuOpened,
        /// <summary>
        /// The mouse cursor is shown because a modal dialog has been opened.
        /// </summary>
        ModalDialog,
        /// <summary>
        /// The mouse cursor is hidden or shown because the Enabled, ShowCursor or HideCursor command was used.
        /// </summary>
        UserCommand
    }

    // Used with TaskbarProgress
    internal enum TaskbarStates
    {
        NoProgress      = 0,
        Indeterminate   = 0x1,
        Normal          = 0x2,
        Error           = 0x4,
        Paused          = 0x8
    }

    #endregion


    // ******************************** PVS.AVPlayer (Player) - EventArgs

    #region PVS.AVPlayer (Player) - PositionEventArgs

    /// <summary>
    /// Provides data for the Player.Events.MediaPositionChanged event.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public class PositionEventArgs : HideObjectEventArgs
    {
        // ******************************** Fields (PositionEventArgs)

        #region Fields (PositionEventArgs)

        internal TimeSpan _fromBegin;
        internal TimeSpan _toEnd;
        internal TimeSpan _fromStart;
        internal TimeSpan _toStop;

        #endregion

        /// <summary>
        /// Gets the length (duration) of the playing media from the natural beginning of the media to the current playback position.
        /// </summary>
        public TimeSpan FromBegin
        {
            get { return _fromBegin; }
        }

        /// <summary>
        /// Gets the length (duration) of the playing media from the current playback position to the natural end of the media.
        /// </summary>
        public TimeSpan ToEnd
        {
            get { return _toEnd; }
        }

        /// <summary>
        /// Gets the length (duration) of the playing media from Player.Media.StartPosition to the current playback position.
        /// </summary>
        public TimeSpan FromStart
        {
            get { return _fromStart; }
        }

        /// <summary>
        /// Gets the length (duration) of the playing media from the current playback position to Player.Media.StopPosition.
        /// </summary>
        public TimeSpan ToStop
        {
            get { return _toStop; }
        }
    }

    #endregion

    #region PVS.AVPlayer (Player) - EndedEventArgs

    /// <summary>
    /// Provides data for the Player.Events.MediaEnded event.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public class EndedEventArgs : HideObjectEventArgs
    {
        internal StopReason _reason;

        /// <summary>
        /// Specifies the reason that media has stopped playing.
        /// </summary>
        public StopReason StopReason
        {
            get { return _reason; }
        }
    }

    #endregion

    #region PVS.AVPlayer (Player) - PeakLevelEventArgs

    /// <summary>
    /// Provides data for the Player.Events.MediaPeakLevelChanged event.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public class PeakLevelEventArgs : HideObjectEventArgs
    {
        internal int        _channelCount;
        internal float      _masterPeakValue;
        internal float[]    _channelsValues;

        /// <summary>
        /// Gets the number of audio output channels and the number of peak values returned by the ChannelsValues property (usually 2 for stereo devices).
        /// </summary>
        public int ChannelCount
        {
            get { return _channelCount; }
        }

        /// <summary>
        /// Gets the highest peak value of the audio output channels (ChannelsValues). Values are from 0.0 to 1.0 (inclusive) or -1 if media playback is paused, stopped or ended.
        /// </summary>
        public float MasterPeakValue
        {
            get { return _masterPeakValue; }
        }

        /// <summary>
        /// Gets the peak values of the audio output channels: Levels[0] contains the value of the left audio output channel and Levels[1] of the right channel. More channels can be present with, for example, surround sound systems. Values are from 0.0 to 1.0 (inclusive) or -1 if media playback is paused, stopped or ended.
        /// </summary>
        public float[] ChannelsValues
        {
            get { return _channelsValues; }
        }
    }

    #endregion

    #region PVS.AVPlayer (Player) - SubtitleEventArgs

    /// <summary>
    /// Provides data for the Player.Events.MediaSubtitleChanged event.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public class SubtitleEventArgs : HideObjectEventArgs
    {
        internal int    _index;
        internal string _subtitle;

        /// <summary>
        /// Gets the index of the current subtitle of the player.
        /// </summary>
        public int Index
        {
            get { return _index; }
        }

        /// <summary>
        /// Gets the text of the current subtitle (or string.Empty) of the player.
        /// </summary>
        public string Subtitle
        {
            get { return _subtitle; }
        }
    }

    #endregion

    #region PVS.AVPlayer (Player) - SignalEventArgs

    /// <summary>
    /// Provides data for the Player.Events.MediaSignal event.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public class SignalEventArgs : HideObjectEventArgs
    {
        internal int    _index;
        internal string _message;

        /// <summary>
        /// Gets the index of the current media signal of the player.
        /// </summary>
        public int Index
        {
            get { return _index; }
        }

        /// <summary>
        /// Gets the message of the current media signal (or string.Empty) of the player.
        /// </summary>
        public string Message
        {
            get { return _message; }
        }
    }

    #endregion

    #region PVS.AVPlayer (Player) - CursorHideEventArgs

    /// <summary>
    /// Provides data for the Player.Events.MediaCursorHideChanged event.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public class CursorHideEventArgs : HideObjectEventArgs
    {
        internal bool                   _visible;
        internal Form                   _form;
        internal CursorChangedReason    _reason;

        /// <summary>
        /// Gets a value indicating whether the mouse cursor was hidden or shown.
        /// </summary>
        public bool Visible
        {
            get { return _visible; }
        }

        /// <summary>
        /// Gets the form that has hidden the mouse cursor or the active form (can be null/Nothing) when the mouse cursor was shown.
        /// </summary>
        public Form Form
        {
            get { return _form; }
        }

        /// <summary>
        /// Specifies the reason that the mouse cursor is hidden or shown.
        /// </summary>
        public CursorChangedReason Reason
        {
            get { return _reason; }
        }
    }

    #endregion

    #region PVS.AVPlayer (Player) - EventArgs - MediaMouseEventArgs

    /// <summary>
    /// Provides data for the Player.Mouse events.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class MediaMouseEventArgs : HideObjectEventArgs
    {
        internal MouseButtons _button;
        internal int _clicks;
        internal Point _location;
        internal int _delta;

        /// <summary>
        /// Gets which mouse button was pressed.
        /// </summary>
        public MouseButtons Button
        {
            get { return _button; }
        }

        /// <summary>
        /// Gets the number of times the mouse button was pressed and released.
        /// </summary>
        public int Clicks
        {
            get { return _clicks; }
        }

        /// <summary>
        /// Gets the location (in screen coordinates) of the mouse during the generating mouse event.
        /// </summary>
        public Point Location
        {
            get { return _location; }
        }

        /// <summary>
        /// Gets the x-coordinate (in screen coordinates) of the mouse during the generating mouse event.
        /// </summary>
        public int X
        {
            get { return _location.X; }
        }

        /// <summary>
        /// Gets the y-coordinate (in screen coordinates) of the mouse during the generating mouse event.
        /// </summary>
        public int Y
        {
            get { return _location.Y; }
        }

        /// <summary>
        /// Gets a signed count of the number of detents the mouse wheel has rotated. A detent is one notch of the mouse wheel.
        /// </summary>
        public int Delta
        {
            get { return _delta; }
        }
    }

    #endregion


    // ******************************** PVS.AVPlayer (Player) - Delegates

    #region PVS.AVPlayer (Player) - Delegates

    /// <summary>
    /// References a method to be called when the shape of the display window of a player needs to be updated.
    /// </summary>
    /// <param name="player">The player whose display window shape needs to be updated.</param>
    /// <param name="videoShape">A value indicating whether the shape should be related to the size of the video image (videoShape = true) or to the size of the display window (videoShape = false).</param>
    /// <param name="shapeBounds">The size and location of the video image (videoShape = true) or the display window (videoShape = false) to be used for the shape.</param>
    public delegate Region DisplayShapeCallback(Player player, bool videoShape, Rectangle shapeBounds);

    /// <summary>
    /// References a method to be called when the display of a player with a custom display mode needs to be updated.
    /// </summary>
    /// <param name="player">The player whose display needs to be updated.</param>
    public delegate Rectangle DisplayModeCallback(Player player);

    #endregion



    /// <summary>
    /// Represents a media player that can be used to play media files using Microsoft Windows built-in Media Control Interface (MCI).
    /// </summary>
    [CLSCompliant(true)]
    public sealed partial class Player : HideObjectMembers, IDisposable
    {

        // ******************************** Event Declarations (Player Class)

        #region Event Declarations (Player Class)

        internal event EventHandler<EndedEventArgs> MediaEnded;
        internal event EventHandler<EndedEventArgs> MediaEndedNotice;
        internal event EventHandler MediaNextRequested;
        internal event EventHandler MediaPreviousRequested;
        internal event EventHandler MediaRepeatChanged;
        internal event EventHandler MediaRepeating;
        internal event EventHandler MediaRepeated;
        internal event EventHandler MediaOpened;
        internal event EventHandler MediaStarted;
        internal event EventHandler MediaPaused;
        internal event EventHandler MediaResumed;
        internal event EventHandler<PositionEventArgs> MediaPositionChanged;
        //internal event EventHandler MediaClosing;
        //internal event EventHandler MediaClosed;
        internal event EventHandler MediaStartEndNextChanged;
        internal event EventHandler MediaStartEndChanged;
        internal event EventHandler MediaDisplayChanged;
        internal event EventHandler MediaDisplayModeChanged;
        internal event EventHandler MediaDisplayShapeChanged;
        internal event EventHandler MediaFullScreenChanged;
        internal event EventHandler MediaFullScreenModeChanged;
        internal event EventHandler MediaAudioVolumeChanged;
        internal event EventHandler MediaAudioBalanceChanged;
        internal event EventHandler MediaAudioEnabledChanged;
        internal event EventHandler MediaVideoEnabledChanged;
        internal event EventHandler MediaVideoBoundsChanged;
        internal event EventHandler MediaSpeedChanged;
        internal event EventHandler MediaOverlayChanged;
        internal event EventHandler MediaOverlayModeChanged;
        internal event EventHandler MediaOverlayHoldChanged;
        internal event EventHandler MediaOverlayActiveChanged;
        internal event EventHandler MediaLevelDeviceChanged;
        internal EventHandler<PeakLevelEventArgs> _mediaPeakLevelChanged;
        internal event EventHandler<PeakLevelEventArgs> MediaPeakLevelChanged
        {
            add
            {
                if (Environment.OSVersion.Version.Major >= 6)
                {
                    if (PeakMeter_Open())
                    {
                        if (_peakLevelArgs == null) _peakLevelArgs = new PeakLevelEventArgs();
                        _mediaPeakLevelChanged += value;
                        _hasPeakLevelEvents = true;
                        _lastError = Global.MCIERR_NO_ERROR;
                        StartMainTimerCheck();
                    }
                    else _lastError = Global.MCIERR_DEVICE_NOT_READY;
                }
                else _lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
            }

            remove
            {
                if (_hasPeakLevelEvents)
                {
                    _peakLevelArgs._channelCount    = pm_PeakMeterChannelCount;
                    _peakLevelArgs._masterPeakValue = -1;
                    _peakLevelArgs._channelsValues  = pm_PeakMeterValuesStop;
                    value(this, _peakLevelArgs);

                    _mediaPeakLevelChanged -= value;
                    if (_mediaPeakLevelChanged == null)
                    {
                        _hasPeakLevelEvents = false;
                        StopMainTimerCheck();
                    }
                }
            }
        }
        internal event EventHandler MediaDisplayClonesChanged;
        internal event EventHandler MediaDisplayClonesStarted;
        internal event EventHandler MediaDisplayClonesStopped;
        private EventHandler<SubtitleEventArgs> _mediaSubtitleChanged;
        internal event EventHandler<SubtitleEventArgs> MediaSubtitleChanged
        {
            add
            {
                if (st_SubtitleChangedArgs == null) st_SubtitleChangedArgs = new SubtitleEventArgs();
                _mediaSubtitleChanged += value;

                if (!st_SubtitlesEnabled)
                {
                    st_SubtitlesEnabled = true;
                    if (!st_HasSubtitles && _playing)
                    {
                        Subtitles_Start(true);
                        StartMainTimerCheck();
                    }
                }
            }

            remove
            {
                _mediaSubtitleChanged -= value;
                if (_mediaSubtitleChanged == null)
                {
                    if (st_HasSubtitles)
                    {
                        st_SubtitleOn = false; // prevent 'no title' event firing
                        Subtitles_Stop();
                    }
                    st_SubtitlesEnabled = false;
                    StopMainTimerCheck();
                }
            }
        }
        private EventHandler<SignalEventArgs> _mediaSignal;
        internal event EventHandler<SignalEventArgs> MediaSignal
        {
            add
            {
                if (ms_SignalEventArgs == null) ms_SignalEventArgs = new SignalEventArgs();
                _mediaSignal += value;

                if (!ms_SignalsEnabled)
                {
                    ms_SignalsEnabled = true;
                    if (!ms_HasSignals && _playing)
                    {
                        MS_Start(true);
                        StartMainTimerCheck();
                    }
                }
            }

            remove
            {
                _mediaSignal -= value;
                if (_mediaSignal == null)
                {
                    if (ms_HasSignals)
                    {
                        MS_Stop();
                    }
                    ms_SignalsEnabled = false;
                    StopMainTimerCheck();
                }
            }
        }
        internal event EventHandler MediaVideoTrackChanged;
        internal event EventHandler MediaAudioTrackChanged;

        internal event EventHandler MediaDragDisplayBegin;
        internal event EventHandler MediaDragDisplayMove;
        internal event EventHandler MediaDragDisplayEnd;

        internal static event EventHandler<CursorHideEventArgs> MediaCursorHideChanged;

        #endregion


        // ******************************** On Event Methods (Player Class)

        #region On Event Methods (Player Class)

        internal void OnMediaPositionChanged()
        {
            //if (MediaPositionChanged != null) // test done at call
            {
                if (_hasDevice)
                {
                    int pos = PositionX;
                    _positionArgs._fromBegin = TimeSpan.FromMilliseconds(pos);
                    _positionArgs._toEnd = TimeSpan.FromMilliseconds(_mediaLength - pos);

                    _positionArgs._fromStart = TimeSpan.FromMilliseconds(pos - _startPositionCurrent);
                    _positionArgs._toStop = _endPositionCurrent == 0 ? TimeSpan.FromMilliseconds(_mediaLength - pos) : TimeSpan.FromMilliseconds(_endPositionCurrent - pos);
                }
                else
                {
                    _positionArgs._fromBegin =
                    _positionArgs._toEnd =
                    _positionArgs._fromStart =
                    _positionArgs._toStop = TimeSpan.Zero;
                }
                MediaPositionChanged(this, _positionArgs);
            }
        }

        internal void OnMediaDisplayModeChanged()
        {
            if (MediaDisplayModeChanged != null) MediaDisplayModeChanged(this, EventArgs.Empty);
        }

        internal void OnMediaDisplayShapeChanged()
        {
            if (MediaDisplayShapeChanged != null) MediaDisplayShapeChanged(this, EventArgs.Empty);
        }

        internal void OnMediaOverlayModeChanged()
        {
            if (MediaOverlayModeChanged != null) MediaOverlayModeChanged(this, EventArgs.Empty);
        }

        internal void OnMediaOverlayHoldChanged()
        {
            if (MediaOverlayHoldChanged != null) MediaOverlayHoldChanged(this, EventArgs.Empty);
        }

        internal void OnMediaVideoBoundsChanged()
        {
            if (MediaVideoBoundsChanged != null) MediaVideoBoundsChanged(this, EventArgs.Empty);
        }

        internal void OnMediaStartEndChanged()
        {
            if (MediaStartEndChanged != null) MediaStartEndChanged(this, EventArgs.Empty);
        }

        internal void OnMediaStartEndNextChanged()
        {
            if (MediaStartEndNextChanged != null) MediaStartEndNextChanged(this, EventArgs.Empty);
        }

        internal void OnMediaLevelDeviceChanged()
        {
            if (MediaLevelDeviceChanged != null) MediaLevelDeviceChanged(this, EventArgs.Empty);
        }

        internal void OnMediaDragFormBegin()
        {
            if (MediaDragDisplayBegin != null) MediaDragDisplayBegin(this, EventArgs.Empty);
        }

        internal void OnMediaDragFormMove()
        {
            if (MediaDragDisplayMove != null) MediaDragDisplayMove(this, EventArgs.Empty);
        }

        internal void OnMediaDragFormEnd()
        {
            if (MediaDragDisplayEnd != null) MediaDragDisplayEnd(this, EventArgs.Empty);
        }

        #endregion


        // ******************************** Fields (Player Class)

        #region Fields (Player Class)

        #region Constants

        // MCI Default Values
        private const bool              DEFAULT_VIDEO_ENABLED       = true;
        private const bool              DEFAULT_AUDIO_ENABLED       = true;
        private const int               DEFAULT_AUDIO_VOLUME        = 1000;
        private const int               DEFAULT_AUDIO_BALANCE       = 500;
        private const int               DEFAULT_SPEED               = 1000;

        // Player Default Values
        private const DisplayMode       DEFAULT_DISPLAYMODE         = DisplayMode.ZoomCenter;
        private const FullScreenMode    DEFAULT_FULLSCREENMODE      = FullScreenMode.Display;
        private const OverlayMode       DEFAULT_OVERLAYMODE         = OverlayMode.Video;
        private const ScreenCopyMode    DEFAULT_SCREENCOPYMODE      = ScreenCopyMode.Video;

        // Maximum number of fullscreen players (forms)
        private const int               MAX_FULLSCREEN_PLAYERS      = 20;

        // Timers default values
        private const int               DEFAULT_TIMERINTERVAL       = 100;
        private const int               DEFAULT_MINIMIZEDINTERVAL   = 300;

        // Used with MciNotifyClass
        private const int               MM_MCINOTIFY                = 0x03B9;
        //private const int               MM_MCISIGNAL                = 0x03CB;

        private const int               MCI_NOTIFY_SUCCESS          = 0x01;
        //private const int             MCI_NOTIFY_SUPERSEDED       = 0x02;
        //private const int             MCI_NOTIFY_ABORTED          = 0x04;
        //private const int             MCI_NOTIFY_FAILURE          = 0x08;

        private const int               WM_ACTIVATEAPP              = 0x1C; // used with WndProc to prevent 'flashing' window

        #endregion

        // This class is used only to receive the MCI notification 'End Of Media'
        #region MCI Notify Class

        private sealed class MciNotifyClass : Control
        {
            internal Player Player;

            protected override void WndProc(ref Message m)
            {
                if (m.Msg == MM_MCINOTIFY)
                {
                    if (m.WParam.ToInt32() == MCI_NOTIFY_SUCCESS)
                    {
                        Player.AV_EndOfMedia(true);
                    }
                }
                else base.WndProc(ref m);
            }
        }
        private MciNotifyClass _mciNotify;

        #endregion

        // Used with all players fullscreen management
        private static Form[]       _fullScreenForms        = new Form[MAX_FULLSCREEN_PLAYERS];

        // MCI communication buffers
        internal StringBuilder      _mciBuffer;
        internal StringBuilder      _mciSmallBuffer1;
        internal StringBuilder      _mciSmallBuffer2;
        private StringBuilder       _paintCommand;

        // MCI device
        internal string             _deviceId;
        internal bool               _hasDevice;
        private CloseDeviceHandler  _closeDeviceHandler;
        internal bool               _closeHandlerEnabled    = true;

        // Last error
        internal int                _lastError;

        // Display
        internal Control            _display;
        internal bool               _hasDisplay;
        private bool                _hasDisplayEvents;
        internal bool               _resizeFormRefresh;
        internal DisplayMode        _displayMode            = DEFAULT_DISPLAYMODE;
        private bool                _hasOwnWindow;

        internal bool               _hasDisplayShape;
        internal bool               _hasVideoShape;
        internal DisplayShape       _currentDisplayShape    = DisplayShape.Normal;
        internal DisplayShapeCallback _displayShapeCallback;
        internal DisplayShapeCallback _customShapeCallback;

        internal bool               _hasDisplayCustomMode;
        internal DisplayModeCallback _displayModeCallback;

        // CursorHide
        internal bool               _hasCursorHide;

        // Display Overlay
        internal Form               _overlay;
        internal bool               _hasOverlay;
        internal OverlayMode        _overlayMode            = DEFAULT_OVERLAYMODE;
        internal bool               _overlayHold;
        internal bool               _overlayCanFocus;
        private bool                _hasOverlayMenu;
        internal bool               _hasOverlayShown;
        private bool                _hasOverlayEvents;
        private bool                _hasOverlayFocusEvents;
        internal bool               _hasOverlayClipping;
        private bool                _hasOverlayClippingEvents;

        // Full Screen
        internal bool               _fullScreen;
        internal FullScreenMode     _fullScreenMode         = DEFAULT_FULLSCREENMODE;
        internal Rectangle          _fsFormBounds;
        private FormBorderStyle     _fsFormBorder;
        private Rectangle           _fsParentBounds;
        private BorderStyle         _fsParentBorder;
        private int                 _fsParentIndex;
        private Rectangle           _fsDisplayBounds;
        private BorderStyle         _fsDisplayBorder;
        private int                 _fsDisplayIndex;

        // Player / Media
        private string              _playerName;
        internal string             _fileName;
        private bool                _hasTempFile;
        private string              _tempFileName;
        internal int                _startPositionNext;
        internal int                _startPositionCurrent;
        internal int                _endPositionNext;
        internal int                _endPositionCurrent;
        private bool                _seeking;
        private bool                _seekSkipped;
        private int                 _seekSkippedPosition;
        internal bool               _repeat;
        internal bool               _playing;
        internal bool               _paused;
        private bool                _resumeIsPlay;
        internal int                _speed                  = DEFAULT_SPEED;
        private int                 _mciSpeed;
        private bool                _speedSkipped;
        private bool                _hasMediaLength;
        internal int                _mediaLength;
        private bool                _busyStarting;
        private EndedEventArgs      _endedEventArgs;

        // PlayerStartInfo
        private string              _siFileName;
        private Control             _siDisplay;
        private TimeSpan            _siStartPosition;
        private TimeSpan            _siEndPosition;
        private bool                _siRepeat;

        // Video
        internal bool               _hasVideo;
        internal bool               _videoEnabled           = DEFAULT_VIDEO_ENABLED;
        private bool                _mciVideoEnabled        = true;
        internal bool               _hasVideoBounds;
        internal Rectangle          _videoBounds;
        internal Rectangle          _videoBoundsClip;
        internal bool               _hasVideoSourceSize;
        internal Size               _videoSourceSize;

        // Audio
        internal bool               _hasAudio;
        internal bool               _mciAudioEnabled        = true;
        internal bool               _audioEnabled           = DEFAULT_AUDIO_ENABLED;
        internal int                _mciAudioVolume;
        internal int                _audioVolume            = DEFAULT_AUDIO_VOLUME;
        internal int                _audioBalance           = DEFAULT_AUDIO_BALANCE;

        // ScreenCopy
        internal ScreenCopyMode     _screenCopyMode         = DEFAULT_SCREENCOPYMODE;

        // Timer used with position changed events, output level meter
        internal Timer              _timer;
        internal bool               _hasPositionEvents;
        private bool                _skipPositionEvents;
        private PositionEventArgs   _positionArgs;
        // Output Level
        internal bool               _hasPeakLevelEvents;
        internal PeakLevelEventArgs _peakLevelArgs;
        private bool                _peakLevelMuted;

        // Minimized
        internal bool               _minimizeEnabled        = true;
        private bool                _minimizeHasEvent;
        private bool                _minimized;
        private Timer               _minimizeTimer;
        internal int                _minimizedInterval      = DEFAULT_MINIMIZEDINTERVAL;
        private double              _minimizedOpacity;

        // Computer sleep disable
        private bool                _sleepOff;

        // Taskbar progress
        internal static TaskbarIndicator.ITaskbarList3 TaskbarInstance;
        internal static bool        _taskbarProgressEnabled;
        internal bool               _hasTaskbarProgress;

        // Miscellaneous
        //private Point               _movePoint;
        private bool                _disposed;

        // Member grouping classes (with lazy initialization)
        private Audio               _audioClass;
        private DisplayClones       _clonesClass;
        private Display             _displayClass;
        private CursorHide          _cursorHideClass;
        private Events              _eventsClass;
        private Media               _mediaClass;
        private TaskbarProgress     _taskbarProgress;
        private MciDevice           _mciClass;
        private Overlay             _overlayClass;
        private SystemPanels        _panelsClass;
        private PointTo             _pointToClass;
        private Position            _positionClass;
        private ScreenCopy          _screenCopyClass;
        private Sliders             _slidersClass;
        private Subtitles           _subtitlesClass;
        private Signals             _signalsClass;
        private Video               _videoClass;

        #endregion


        // ******************************** Constructor / Dispose / Destructor / Version

        #region Public - Player Constructor

        /// <summary>
        /// Initializes a new instance of the PVS.AVPlayer.Player class (creates a new media player).
        /// </summary>
        public Player()
        {
            _deviceId           = (Global.RandomNumber.Next(10, 100000000)) + "AVP";
            _closeDeviceHandler = new CloseDeviceHandler();

            _mciBuffer          = new StringBuilder(Global.BUFFER_SIZE);
            _mciSmallBuffer1    = new StringBuilder(Global.SMALL_BUFFER_SIZE);
            _mciSmallBuffer2    = new StringBuilder(Global.SMALL_BUFFER_SIZE);
            _paintCommand       = new StringBuilder(Global.SMALL_BUFFER_SIZE);

            _mciNotify          = new MciNotifyClass { Player = this };
            _positionArgs       = new PositionEventArgs();
            _endedEventArgs     = new EndedEventArgs();

            _timer              = new Timer { Interval = DEFAULT_TIMERINTERVAL };
            _timer.Tick         += AV_TimerTick;

            _minimizeTimer      = new Timer { Interval = DEFAULT_MINIMIZEDINTERVAL };
            _minimizeTimer.Tick += AV_MinimizeTimer_Tick;
        }

        /// <summary>
        /// Initializes a new instance of the PVS.AVPlayer.Player class (creates a new media player).
        /// </summary>
        /// <param name="display">The form or control that is used to display video and overlays.</param>
        public Player(Control display) : this(display, null) { }

        /// <summary>
        /// Initializes a new instance of the PVS.AVPlayer.Player class (creates a new media player).
        /// </summary>
        /// <param name="display">The form or control that is used to display video and overlays.</param>
        /// <param name="overlay">The form that is used as display overlay.</param>
        public Player(Control display, Form overlay) : this()
        {
            if (display != null)
            {
                if (AV_SetDisplay(display, true) == Global.MCIERR_NO_ERROR && overlay != null)
                {
                    AV_SetOverlay(overlay);
                }
            }
        }

        #endregion

        #region Public - Player Dispose / Finalizer

        /// <summary>
        /// Remove the player and clean up any resources being used.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Remove the player and clean up any resources being used.
        /// </summary>
        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                _disposed = true;
                if (disposing)
                {
                    if (_hasDevice) AV_CloseDevice(true, true, false);
                    if (dc_hasDisplayClones) DisplayClones_Clear();
                    if (_hasTaskbarProgress) TaskbarProgress.Clear();

                    if (_fullScreen) AV_ResetFullScreen();
                    if (_sleepOff) SafeNativeMethods.SleepStatus = false;

                    if (_hasPositionSlider) Sliders.Position = null;
                    if (_speedSlider != null) Sliders.Speed = null;
                    if (_shuttleSlider != null) Sliders.Shuttle = null;
                    if (_volumeSlider != null) Sliders.AudioVolume = null;
                    if (_balanceSlider != null) Sliders.AudioBalance = null;

                    if (_mciNotify != null)
                    {
                        _mciNotify.Dispose();
                        _mciNotify = null;
                    }

                    if (_timer != null)
                    {
                        _timer.Dispose();
                        _timer = null;
                    }

                    if (_minimizeTimer != null)
                    {
                        _minimizeTimer.Dispose();
                        _minimizeTimer = null;
                    }

                    if (_hasOverlay) AV_SetOverlay(null);
                    _deviceId = string.Empty;

                    if (_hasCursorHide) CH_RemovePlayerItems(this);

                    if (_closeDeviceHandler != null)
                    {
                        _closeDeviceHandler.Dispose();
                        _closeDeviceHandler = null;
                    }
                }
                else
                {
                    if (_hasDevice) SafeNativeMethods.mciSendString("close " + _deviceId, null, 0, IntPtr.Zero);
                    if (_fullScreen)
                    {
                        try { AV_ResetFullScreen(); }
                        catch { /* ignored */ }
                    }
                    if (_sleepOff) SafeNativeMethods.SleepStatus = false;

                    if (_hasCursorHide)
                    {
                        try { CH_RemovePlayerItems(this); }
                        catch { /* ignored */ }
                    }

                    if (_closeDeviceHandler != null)
                    {
                        _closeDeviceHandler.Dispose();
                        _closeDeviceHandler = null;
                    }
                }
            }
        }

        /// <summary>
        /// Remove the player and clean up any resources being used.
        /// </summary>
        ~Player()
        {
            Dispose(false);
        }

        # endregion

        #region Public - PVS.AVPlayer Version

        /// <summary>
        /// Gets the version number of the PVS.AVPlayer library.
        /// </summary>
        public static float Version
        {
            get { return Global.VERSION; }
        }

        /// <summary>
        /// Gets the version string of the PVS.AVPlayer library.
        /// </summary>
        public static string VersionString
        {
            get { return Global.VERSION_STRING; }
        }

        #endregion


        // ************************ Play

        #region Public - Play

        /// <summary>
        /// Plays media.
        /// </summary>
        /// <param name="fileName">The path and file name of the media to play.</param>
        public int Play(string fileName)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            //AV_GetStartInfo();
            _siDisplay          = _display;
            _siStartPosition    = TimeSpan.FromMilliseconds(_startPositionNext);
            _siEndPosition      = TimeSpan.FromMilliseconds(_endPositionNext);
            _siRepeat           = _repeat;

            _siFileName         = fileName;

            return AV_Play();
        }

        /// <summary>
        /// Plays media.
        /// </summary>
        /// <param name="fileName">The path and file name of the media to play.</param>
        /// <param name="repeat">A value indicating whether to repeat playback when the media has finished playing.</param>
        public int Play(string fileName, bool repeat)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            AV_GetStartInfo();
            _siFileName = fileName;
            _siRepeat   = repeat;

            return AV_Play();
        }

        /// <summary>
        /// Plays media.
        /// </summary>
        /// <param name="fileName">The path and file name of the media to play.</param>
        /// <param name="display">The form or control to use for displaying the video of the media.</param>
        public int Play(string fileName, Control display)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            AV_GetStartInfo();
            _siFileName = fileName;
            _siDisplay  = display;

            return AV_Play();
        }

        /// <summary>
        /// Plays media.
        /// </summary>
        /// <param name="fileName">The path and file name of the media to play.</param>
        /// <param name="display">The form or control to use for displaying the video of the media.</param>
        /// <param name="repeat">A value indicating whether to repeat playback when the media has finished playing.</param>
        public int Play(string fileName, Control display, bool repeat)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            AV_GetStartInfo();
            _siFileName = fileName;
            _siDisplay  = display;
            _siRepeat   = repeat;

            return AV_Play();
        }

        /// <summary>
        /// Plays media.
        /// </summary>
        /// <param name="fileName">The path and file name of the media to play.</param>
        /// <param name="startPosition">The playback start position of the media.</param>
        /// <param name="stopPosition">The playback stop position of the media (TimeSpan.Zero or 00:00:00 = natural end of the media).</param>
        public int Play(string fileName, TimeSpan startPosition, TimeSpan stopPosition)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            _siFileName         = fileName;
            _siDisplay          = _display;
            _siStartPosition    = startPosition;
            _siEndPosition      = stopPosition;
            _siRepeat           = _repeat;

            return AV_Play();
        }

        /// <summary>
        /// Plays media.
        /// </summary>
        /// <param name="fileName">The path and file name of the media to play.</param>
        /// <param name="startPosition">The playback start position of the media.</param>
        /// <param name="stopPosition">The playback stop position of the media (TimeSpan.Zero or 00:00:00 = natural end of the media).</param>
        /// <param name="repeat">A value indicating whether to repeat playback when the media has finished playing.</param>
        public int Play(string fileName, TimeSpan startPosition, TimeSpan stopPosition, bool repeat)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            _siFileName         = fileName;
            _siDisplay          = _display;
            _siStartPosition    = startPosition;
            _siEndPosition      = stopPosition;
            _siRepeat           = repeat;

            return AV_Play();
        }

        /// <summary>
        /// Plays media.
        /// </summary>
        /// <param name="fileName">The path and file name of the media to play.</param>
        /// <param name="display">The form or control to use for displaying the video of the media.</param>
        /// <param name="startPosition">The playback start position of the media.</param>
        /// <param name="stopPosition">The playback stop position of the media (TimeSpan.Zero or 00:00:00 = natural end of the media).</param>
        public int Play(string fileName, Control display, TimeSpan startPosition, TimeSpan stopPosition)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            _siFileName         = fileName;
            _siDisplay          = display;
            _siStartPosition    = startPosition;
            _siEndPosition      = stopPosition;
            _siRepeat           = _repeat;

            return AV_Play();
        }

        /// <summary>
        /// Plays media.
        /// </summary>
        /// <param name="fileName">The path and file name of the media to play.</param>
        /// <param name="display">The form or control to use for displaying the video of the media.</param>
        /// <param name="startPosition">The playback start position of the media.</param>
        /// <param name="stopPosition">The playback stop position of the media (TimeSpan.Zero or 00:00:00 = natural end of the media).</param>
        /// <param name="repeat">A value indicating whether to repeat playback when the media has finished playing.</param>
        public int Play(string fileName, Control display, TimeSpan startPosition, TimeSpan stopPosition, bool repeat)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            _siFileName         = fileName;
            _siDisplay          = display;
            _siStartPosition    = startPosition;
            _siEndPosition      = stopPosition;
            _siRepeat           = repeat;

            return AV_Play();
        }

        #endregion

        #region Public - PlayResource

        /// <summary>
        /// Plays an embedded media resource by saving the resource as a file in the system's temporary folder. The file is deleted afterwards.
        /// </summary>
        /// <param name="fileName">The file name for the temporary file of the specified embedded media resource to play.</param>
        /// <param name="resource">The embedded media resource (e.g. Properties.Resources.MyMedia) to save as a file in the system's temporary folder.</param>
        public int PlayResource(string fileName, byte[] resource)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            //AV_GetStartInfo();
            _siDisplay          = _display;
            _siStartPosition    = TimeSpan.FromMilliseconds(_startPositionNext);
            _siEndPosition      = TimeSpan.FromMilliseconds(_endPositionNext);
            _siRepeat           = _repeat;

            return AV_PlayResource(fileName, resource);
        }

        /// <summary>
        /// Plays an embedded media resource by saving the resource as a file in the system's temporary folder. The file is deleted afterwards.
        /// </summary>
        /// <param name="fileName">The file name for the temporary file of the specified embedded media resource to play.</param>
        /// <param name="resource">The embedded media resource (e.g. Properties.Resources.MyMedia) to save as a file in the system's temporary folder.</param>
        /// <param name="repeat">A value indicating whether to repeat playback when the media has finished playing.</param>
        public int PlayResource(string fileName, byte[] resource, bool repeat)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            AV_GetStartInfo();
            _siRepeat = repeat;

            return AV_PlayResource(fileName, resource);
        }

        /// <summary>
        /// Plays an embedded media resource by saving the resource as a file in the system's temporary folder. The file is deleted afterwards.
        /// </summary>
        /// <param name="fileName">The file name for the temporary file of the specified embedded media resource to play.</param>
        /// <param name="resource">The embedded media resource (e.g. Properties.Resources.MyMedia) to save as a file in the system's temporary folder.</param>
        /// <param name="display">The form or control to use for displaying the video of the media.</param>
        public int PlayResource(string fileName, byte[] resource,   Control display)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            AV_GetStartInfo();
            _siDisplay = display;

            return AV_PlayResource(fileName, resource);
        }

        /// <summary>
        /// Plays an embedded media resource by saving the resource as a file in the system's temporary folder. The file is deleted afterwards.
        /// </summary>
        /// <param name="fileName">The file name for the temporary file of the specified embedded media resource to play.</param>
        /// <param name="resource">The embedded media resource (e.g. Properties.Resources.MyMedia) to save as a file in the system's temporary folder.</param>
        /// <param name="display">The form or control to use for displaying the video of the media.</param>
        /// <param name="repeat">A value indicating whether to repeat playback when the media has finished playing.</param>
        public int PlayResource(string fileName, byte[] resource, Control display, bool repeat)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            AV_GetStartInfo();
            _siDisplay  = display;
            _siRepeat   = repeat;

            return AV_PlayResource(fileName, resource);
        }

        /// <summary>
        /// Plays an embedded media resource by saving the resource as a file in the system's temporary folder. The file is deleted afterwards.
        /// </summary>
        /// <param name="fileName">The file name for the temporary file of the specified embedded media resource to play.</param>
        /// <param name="resource">The embedded media resource (e.g. Properties.Resources.MyMedia) to save as a file in the system's temporary folder.</param>
        /// <param name="startPosition">The playback start position of the media.</param>
        /// <param name="stopPosition">The playback stop position of the media (TimeSpan.Zero or 00:00:00 = natural end of the media).</param>
        public int PlayResource(string fileName, byte[] resource, TimeSpan startPosition, TimeSpan stopPosition)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            _siDisplay          = _display;
            _siStartPosition    = startPosition;
            _siEndPosition      = stopPosition;
            _siRepeat           = _repeat;

            return AV_PlayResource(fileName, resource);
        }

        /// <summary>
        /// Plays an embedded media resource by saving the resource as a file in the system's temporary folder. The file is deleted afterwards.
        /// </summary>
        /// <param name="fileName">The file name for the temporary file of the specified embedded media resource to play.</param>
        /// <param name="resource">The embedded media resource (e.g. Properties.Resources.MyMedia) to save as a file in the system's temporary folder.</param>
        /// <param name="startPosition">The playback start position of the media.</param>
        /// <param name="stopPosition">The playback stop position of the media (TimeSpan.Zero or 00:00:00 = natural end of the media).</param>
        /// <param name="repeat">A value indicating whether to repeat playback when the media has finished playing.</param>
        public int PlayResource(string fileName, byte[] resource, TimeSpan startPosition, TimeSpan stopPosition, bool repeat)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            AV_GetStartInfo();
            _siDisplay          = _display;
            _siStartPosition    = startPosition;
            _siEndPosition      = stopPosition;
            _siRepeat           = repeat;

            return AV_PlayResource(fileName, resource);
        }

        /// <summary>
        /// Plays an embedded media resource by saving the resource as a file in the system's temporary folder. The file is deleted afterwards.
        /// </summary>
        /// <param name="fileName">The file name for the temporary file of the specified embedded media resource to play.</param>
        /// <param name="resource">The embedded media resource (e.g. Properties.Resources.MyMedia) to save as a file in the system's temporary folder.</param>
        /// <param name="display">The form or control to use for displaying the video of the media.</param>
        /// <param name="startPosition">The playback start position of the media.</param>
        /// <param name="stopPosition">The playback stop position of the media (TimeSpan.Zero or 00:00:00 = natural end of the media).</param>
        public int PlayResource(string fileName, byte[] resource, Control display, TimeSpan startPosition, TimeSpan stopPosition)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            _siDisplay          = display;
            _siStartPosition    = startPosition;
            _siEndPosition      = stopPosition;
            _siRepeat           = _repeat;

            return AV_PlayResource(fileName, resource);
        }

        /// <summary>
        /// Plays an embedded media resource by saving the resource as a file in the system's temporary folder. The file is deleted afterwards.
        /// </summary>
        /// <param name="fileName">The file name for the temporary file of the specified embedded media resource to play.</param>
        /// <param name="resource">The embedded media resource (e.g. Properties.Resources.MyMedia) to save as a file in the system's temporary folder.</param>
        /// <param name="display">The form or control to use for displaying the video of the media.</param>
        /// <param name="startPosition">The playback start position of the media.</param>
        /// <param name="stopPosition">The playback stop position of the media (TimeSpan.Zero or 00:00:00 = natural end of the media).</param>
        /// <param name="repeat">A value indicating whether to repeat playback when the media has finished playing.</param>
        public int PlayResource(string fileName, byte[] resource, Control display, TimeSpan startPosition, TimeSpan stopPosition, bool repeat)
        {
            if (_busyStarting) return Global.MCIERR_NO_ERROR;

            _siDisplay          = display;
            _siStartPosition    = startPosition;
            _siEndPosition      = stopPosition;
            _siRepeat           = repeat;

            return AV_PlayResource(fileName, resource);
        }

        private int AV_PlayResource(string fileName, byte[] resource)
        {
            _siFileName = AV_ResourceToFile(resource, fileName);
            if (_lastError == Global.MCIERR_NO_ERROR)
            {
                _hasTempFile = true;
                _tempFileName = _siFileName;

                return AV_Play();
            }
            else return _lastError;
        }

        #endregion

        #region Public - Pause / Resume / PlayNext / PlayPrevious / Stop / Reset

        /// <summary>
        /// Activates the pause mode of the player (pauses media playback).
        /// </summary>
        public int Pause()
        {
            _lastError = Global.MCIERR_NO_ERROR;
            if (!_paused)
            {
                if (_hasDevice)
                {
                    _lastError = AV_MciSendString("pause");
                }
                if (_lastError == Global.MCIERR_NO_ERROR)
                {
                    _paused = true;
                    if (_hasPeakLevelEvents)
                    {
                        _peakLevelArgs._channelCount = pm_PeakMeterChannelCount;
                        _peakLevelArgs._masterPeakValue = -1;
                        _peakLevelArgs._channelsValues = pm_PeakMeterValuesStop;
                        _mediaPeakLevelChanged(this, _peakLevelArgs);
                    }
                    if (_hasTaskbarProgress && _playing) _taskbarProgress.SetState(TaskbarStates.Paused);
                    if (MediaPaused != null) MediaPaused(this, EventArgs.Empty);
                    _timer.Stop();
                }
            }
            return _lastError;
        }

        /// <summary>
        /// Deactivates the pause mode of the player (resumes paused media).
        /// </summary>
        public int Resume()
        {
            int retVal = Global.MCIERR_NO_ERROR;

            if (_paused)
            {
                if (_hasDevice)
                {
                    _paused = false;
                    if (_resumeIsPlay)
                    {
                        _skipPositionEvents = true;
                        _resumeIsPlay = false;
                        int savePlayFrom = _startPositionCurrent;
                        _startPositionCurrent = PositionX;
                        AV_PlayMedia(false);
                        _startPositionCurrent = savePlayFrom;
                        _skipPositionEvents = false;
                    }
                    else
                    {
                        retVal = AV_MciSendString("resume");
                    }
                    if (retVal == Global.MCIERR_NO_ERROR)
                    {
                        if (!_videoEnabled)
                        {
                            _mciVideoEnabled = true;
                            AV_SetVideoEnabled(false);
                        }
                        if (MediaResumed != null) MediaResumed(this, EventArgs.Empty);
                        StartMainTimerCheck();
                    }
                }
                else
                {
                    _resumeIsPlay = false;
                    _paused = false;
                    if (MediaResumed != null) MediaResumed(this, EventArgs.Empty);
                }
                if (_hasTaskbarProgress && _playing) _taskbarProgress.SetState(TaskbarStates.Normal);
            }
            _lastError = retVal;
            return retVal;
        }

        /// <summary>
        /// Requests to play previous media (raises the Player.Events.MediaPreviousRequested event).
        /// </summary>
        public int PlayPrevious()
        {
            if (MediaPreviousRequested != null) MediaPreviousRequested(this, EventArgs.Empty);
            _lastError = Global.MCIERR_NO_ERROR;
            return _lastError;
        }

        /// <summary>
        /// Requests to play next media (raises the Player.Events.MediaNextRequested event).
        /// </summary>
        public int PlayNext()
        {
            if (MediaNextRequested != null) MediaNextRequested(this, EventArgs.Empty);
            _lastError = Global.MCIERR_NO_ERROR;
            return _lastError;
        }

        /// <summary>
        /// Stops media playback.
        /// </summary>
        public int Stop()
        {
            if (_hasDevice) AV_CloseDevice(false, true, false);
            _lastError = Global.MCIERR_NO_ERROR;
            return _lastError;
        }

        /// <summary>
        /// Stops media playback and resets the display mode, audio and start/end positions settings of the player to their default values.
        /// </summary>
        public int Reset()
        {
            Stop();

            _displayMode = DEFAULT_DISPLAYMODE;
            _hasVideoBounds = false;
            if (_hasVideo) AV_SetDisplayMode(DEFAULT_DISPLAYMODE);
            OnMediaDisplayModeChanged();

            AV_SetAudioVolume(DEFAULT_AUDIO_VOLUME);
            AV_SetAudioBalance(DEFAULT_AUDIO_BALANCE, false);
            AV_SetAudioEnabled(DEFAULT_AUDIO_ENABLED);
            AV_SetVideoEnabled(DEFAULT_VIDEO_ENABLED);

            Paused = false;
            Repeat = false;
            Speed = DEFAULT_SPEED;

            if (_startPositionNext != 0)
            {
                _startPositionNext = 0;
                OnMediaStartEndNextChanged();
            }
            if (_endPositionNext != 0)
            {
                _endPositionNext = 0;
                OnMediaStartEndNextChanged();
            }

            _lastError = Global.MCIERR_NO_ERROR;
            return _lastError;
        }

        #endregion

        #region Public - Playing / Paused

        /// <summary>
        /// Gets a value indicating whether media is playing (includes paused media).
        /// </summary>
        public bool Playing
        {
            get { return _playing; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the pause mode of the player is activated.
        /// </summary>
        public bool Paused
        {
            get { return _paused; }
            set
            {
                if (value) Pause();
                else Resume();
            }
        }

        #endregion

        #region Public - Position / Step

        /// <summary>
        /// Provides access to the playback position settings of the player (e.g. Player.Position.FromStart).
        /// </summary>
        public Position Position
        {
            get
            {
                if (_positionClass == null) _positionClass = new Position(this);
                return _positionClass;
            }
        }

        // Position helper functions
        internal int PositionX
        {
            get
            {
                if (_hasDevice)
                {
                    if (_psTracking) return _positionSlider.Value;

                    _lastError = SafeNativeMethods.mciSendString("status " + _deviceId + " position", _mciSmallBuffer2, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_lastError == Global.MCIERR_NO_ERROR)
                    {
                        int number = 0;
                        for (int i = 0; i < _mciSmallBuffer2.Length; ++i)
                        {
                            number = 10 * number + (_mciSmallBuffer2[i] - 48);
                        }
                        return number;
                    }
                }
                else
                {
                    _lastError = Global.MCIERR_NO_ERROR;
                }
                return 0;
            }

            set
            {
                if (_hasDevice)
                {
                    if (_seeking)
                    {
                        _seekSkipped = true;
                        _seekSkippedPosition = value;
                    }
                    else
                    {
                        _seeking = true;
                        _timer.Stop();

                        do
                        {
                            _seekSkipped = false;

                            if (value < 0) value = 0;
                            else if (value >= _mediaLength) value = _mediaLength - 100;

                            if (value == 0) AV_MciSendString("seek", "to start");

                            _mciBuffer.Length = 0;
                            _mciBuffer.Append("play ").Append(_deviceId);
                            if (value > 0)
                            {
                                _mciBuffer.Append(" from ").Append(value);
                                if (_endPositionCurrent > value) _mciBuffer.Append(" to ").Append(_endPositionCurrent);
                            }
                            else if (_endPositionCurrent > 0)
                            {
                                _mciBuffer.Append(" to ").Append(_endPositionCurrent);
                            }
                            _mciBuffer.Append(" notify");

                            _resumeIsPlay = false;

                            SafeNativeMethods.mciSendString(_mciBuffer.ToString(), null, 0, _mciNotify.Handle);

                            if (_paused) AV_MciSendString("pause");
                            Application.DoEvents();

                            if (_lastError == Global.MCIERR_NO_ERROR)
                            {
                                if (_hasTaskbarProgress) _taskbarProgress.SetValue(value);
                                if (MediaPositionChanged != null) OnMediaPositionChanged();
                            }

                            value = _seekSkippedPosition;

                        } while (_seekSkipped);

                        if (!_paused)
                        {
                            StartMainTimerCheck();
                        }
                        _seeking = false;
                    }
                }
                else
                {
                    _lastError = Global.MCIERR_NO_ERROR;
                }
            }
        }

        internal void SetPosition(int position)
        {
            if (_playing)
            {
                PositionX = position;
                if (_hasPositionSlider)
                {
                    if (position < _positionSlider.Minimum) _positionSlider.Value = _positionSlider.Minimum;
                    else if (position > _positionSlider.Maximum) _positionSlider.Value = _positionSlider.Maximum;
                    else _positionSlider.Value = position;

                }
            }
        }

        // called from the Position class
        internal int Step(int frames)
        {
            if (!_playing)
            {
                _lastError = Global.MCIERR_DEVICE_NOT_READY;
                return _lastError;
            }

            int retVal = Global.MCIERR_NO_ERROR;
            if (frames != 0)
            {
                if (_hasVideo)
                {
                    retVal = AV_MciSendString("step", "by " + frames.ToString());

                    if (retVal == Global.MCIERR_NO_ERROR)
                    {
                        PositionX = PositionX;
                    }
                    else if (frames < 0)
                    {
                        if (_paused) _resumeIsPlay = true;
                        else PositionX = 0;
                    }

                    if (_hasPositionSlider) AV_TimerTick(this, EventArgs.Empty);
                    else if (MediaPositionChanged != null) OnMediaPositionChanged();
                }
                else
                {
                    PositionX += (frames * 50);
                    if (_hasPositionSlider) AV_TimerTick(this, EventArgs.Empty);
                    else if (MediaPositionChanged != null) OnMediaPositionChanged();
                }
                Application.DoEvents();
            }
            _lastError = retVal;
            return _lastError;
        }

        #endregion

        #region  Public - Speed / Repeat

        /// <summary>
        /// Gets or sets a value indicating the speed at which media is played by the player (normal speed = 1000).
        /// </summary>
        public int Speed
        {
            get { return _speed; }
            set
            {
                if (value < 0) value = 0;
                AV_SetSpeed(value);
                if (_speedSlider != null) SpeedSlider_ValueToSlider(value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to repeat media playback when finished.
        /// </summary>
        public bool Repeat
        {
            get { return _repeat; }
            set
            {
                if (_repeat == value) return;

                _repeat = value;
                if (_hasDevice && _repeat)
                {
                    double pos = PositionX;
                    if ((pos < _startPositionCurrent || (_endPositionCurrent != 0 && pos > _endPositionCurrent)))
                    {
                        AV_EndOfMedia(true);
                        if (_paused)
                        {
                            AV_MciSendString("pause");
                            AV_TimerTick(this, EventArgs.Empty);
                        }
                    }
                }
                if (MediaRepeatChanged != null) MediaRepeatChanged(this, EventArgs.Empty);
            }
        }

        #endregion


        // ************************ Video

        #region Public - Video

        /// <summary>
        /// Provides access to the video settings of the player (e.g. Player.Video.Present).
        /// </summary>
        public Video Video
        {
            get
            {
                if (_videoClass == null) _videoClass = new Video(this);
                return _videoClass;
            }
        }

        #endregion


        // ************************ Audio

        #region Public - Audio

        /// <summary>
        /// Provides access to the audio settings of the player (e.g. Player.Audio.Volume).
        /// </summary>
        public Audio Audio
        {
            get
            {
                if (_audioClass == null) _audioClass = new Audio(this);
                return _audioClass;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the audio output of the player is muted (default: false).
        /// </summary>
        public bool Mute
        {
            get { return !_audioEnabled; }
            set { AV_SetAudioEnabled(!value); }
        }

        #endregion


        // ************************ Display

        #region Public - Display

        /// <summary>
        /// Provides access to the display settings of the player (e.g. Player.Display.Window).
        /// </summary>
        public Display Display
        {
            get
            {
                if (_displayClass == null) _displayClass = new Display(this);
                return _displayClass;
            }
        }

        #endregion


        // ************************ FullScreen

        #region Public - FullScreen

        /// <summary>
        /// Gets or sets a value indicating whether the fullscreen mode of the player is activated (default: false).
        /// </summary>
        public bool FullScreen
        {
            get { return _fullScreen; }
            set { AV_SetFullScreen(value); }
        }

        /// <summary>
        /// Gets or sets the fullscreen display mode of the player (default: FullScreenMode.Display).
        /// </summary>
        public FullScreenMode FullScreenMode
        {
            get { return _fullScreenMode; }
            set
            {
                if (_fullScreen) AV_SetFullScreenMode(value, true);
                else
                {
                    _lastError = Global.MCIERR_NO_ERROR;
                    _fullScreenMode = value;
                    if (MediaFullScreenModeChanged != null) MediaFullScreenModeChanged(this, EventArgs.Empty);
                }
            }
        }

        #endregion


        // ************************ Overlay

        #region Public - Overlay

        /// <summary>
        /// Provides access to the display overlay settings of the player (e.g. Player.Overlay.Window).
        /// </summary>
        public Overlay Overlay
        {
            get
            {
                if (_overlayClass == null) _overlayClass = new Overlay(this);
                return _overlayClass;
            }
        }

        // Overlay Delay helper functions

        private void AV_MinimizeTimer_Tick(object sender, EventArgs e)
        {
            _minimizeTimer.Stop();
            if (_hasOverlay)
            {
                if (_minimized)
                {
                    _minimizedOpacity = _overlay.Opacity;
                    _overlay.Opacity = 0;
                }
                else
                {
                    _overlay.Opacity = _minimizedOpacity;
                }
            }
        }

        private void AV_Minimize_SizeChanged(object sender, EventArgs e)
        {
            if (_minimized && _overlay.Owner.WindowState != FormWindowState.Minimized)
            {
                _minimized = false;
                _minimizeTimer.Interval = _minimizedInterval;
                _minimizeTimer.Start();
            }
            else if (_hasOverlay && _overlay.Owner.WindowState == FormWindowState.Minimized)
            {
                _minimized = true;
                _minimizeTimer.Interval = 300;
                _minimizeTimer.Start();
            }
        }

        internal void AV_MinimizeActivate(bool active)
        {
            if (active)
            {
                if (!_minimizeEnabled || !_hasOverlay || _overlay.Owner == null) return;

                if (!_minimizeHasEvent)
                {
                    _overlay.Owner.SizeChanged += AV_Minimize_SizeChanged;
                    _minimizeHasEvent = true;
                }
                if (!_minimized && _overlay.Owner.WindowState == FormWindowState.Minimized)
                {
                    _minimizedOpacity = _overlay.Opacity;
                    _overlay.Opacity = 0;
                    _minimized = true;
                }
            }
            else
            {
                if (!_minimizeHasEvent) return;

                if (_overlay.Owner != null) _overlay.Owner.SizeChanged -= AV_Minimize_SizeChanged;
                _minimizeHasEvent = false;
                if (_minimized)
                {
                    _overlay.Opacity = _minimizedOpacity;
                    _minimized = false;
                }
            }
        }

        #endregion


        // ************************ ScreenCopy

        #region Public - ScreenCopy

        /// <summary>
        /// Provides access to the screen copy settings of the player (e.g. Player.ScreenCopy.ToImage).
        /// </summary>
        public ScreenCopy ScreenCopy
        {
            get
            {
                if (_screenCopyClass == null) _screenCopyClass = new ScreenCopy(this);
                return _screenCopyClass;
            }
        }

        #endregion


        // ************************ Various Settings / Information

        #region Public - 'Shortcut' properties 'Has...'

        /// <summary>
        /// Gets a value indicating whether the playing media contains audio.
        /// </summary>
        public bool HasAudio
        {
            get
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return _hasAudio;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the player has audio output peak level information.
        /// </summary>
        public bool HasAudioPeakLevels
        {
            get
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return _hasPeakLevelEvents;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the playing media contains video.
        /// </summary>
        public bool HasVideo
        {
            get
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return _hasVideo;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the player has a display overlay.
        /// </summary>
        public bool HasOverlay
        {
            get
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return _hasOverlay;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the player has one or more display clones.
        /// </summary>
        public bool HasDisplayClones
        {
            get
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return dc_hasDisplayClones;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the playing media has active subtitles.
        /// </summary>
        public bool HasSubtitles
        {
            get
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return st_HasSubtitles;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the playing media has active media signals.
        /// </summary>
        public bool HasSignals
        {
            get
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return ms_HasSignals;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the player has one or more taskbar progress indicators.
        /// </summary>
        public bool HasTaskbarProgress
        {
            get
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return _hasTaskbarProgress;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the player has a custom shaped display window.
        /// </summary>
        public bool HasDisplayShape
        {
            get
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return _hasDisplayShape;
            }
        }

        #endregion

        #region Public - Error Information

        /// <summary>
        /// Gets a value indicating whether an error has occurred with the last player instruction.
        /// </summary>
        public bool LastError
        {
            get { return _lastError != Global.MCIERR_NO_ERROR; }
        }

        /// <summary>
        /// Gets the code of the last error of the player that has occurred (0 = no error).
        /// </summary>
        public int LastErrorCode
        {
            get { return _lastError; }
        }

        /// <summary>
        /// Gets a description of the last error of the player that has occurred.
        /// </summary>
        public string LastErrorString
        {
            get { return GetErrorString(_lastError); }
        }

        /// <summary>
        /// Returns a description of the specified error code.
        /// </summary>
        /// <param name="errorCode">The error code whose description needs to be obtained.</param>
        public string GetErrorString(int errorCode)
        {
            SafeNativeMethods.mciGetErrorString(errorCode, _mciBuffer, Global.BUFFER_SIZE);
            return _mciBuffer.ToString();
        }

        #endregion

        #region Public - Player Name

        /// <summary>
        /// Gets or sets the name of the player.
        /// </summary>
        public string Name
        {
            get { return string.IsNullOrEmpty(_playerName) ? _deviceId : _playerName; }
            set { _playerName = value; }
        }

        #endregion

        #region Public - Media

        /// <summary>
        /// Provides access to the media settings of the player (e.g. Player.Media.GetName).
        /// </summary>
        public Media Media
        {
            get
            {
                if (_mediaClass == null) _mediaClass = new Media(this);
                return _mediaClass;
            }
        }

        // GetLength helper function
        private int Length
        {
            get
            {
                if (_hasMediaLength) return _mediaLength;

                if (_hasDevice)
                {
                    _lastError = SafeNativeMethods.mciSendString("status " + _deviceId + " length", _mciSmallBuffer1, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_lastError == Global.MCIERR_NO_ERROR)
                    {
                        _mediaLength = 0;
                        for (int i = 0; i < _mciSmallBuffer1.Length; ++i)
                        {
                            _mediaLength = 10 * _mediaLength + (_mciSmallBuffer1[i] - 48);
                        }
                        _hasMediaLength = true;
                        return _mediaLength;
                    }
                }
                return 0;
            }
        }

        #endregion

        #region Public - TaskbarProgress

        /// <summary>
        /// Provides access to the taskbar progress indicator settings of the player (e.g. Player.TaskbarProgress.Add).
        /// </summary>
        public TaskbarProgress TaskbarProgress
        {
            get
            {
                if (_taskbarProgress == null) _taskbarProgress = new TaskbarProgress(this);
                return _taskbarProgress;
            }
        }

        #endregion

        #region Public - PointTo

        /// <summary>
        /// Provides access to the point conversion methods of the player (e.g. Player.PointTo.Display).
        /// </summary>
        public PointTo PointTo
        {
            get
            {
                if (_pointToClass == null) _pointToClass = new PointTo(this);
                return _pointToClass;
            }
        }

        #endregion

        #region Public - Timer Interval

        /// <summary>
        /// Gets or sets the interval of the events timer of the player in milliseconds (default: 100 ms).
        /// </summary>
        public int TimerInterval
        {
            get { return _timer.Interval; }
            set
            {
                _timer.Interval = value <= 10 ? 10 : value;
                _lastError = Global.MCIERR_NO_ERROR;
            }
        }

        #endregion


        // ************************ MCI Device

        #region Public - MCI Device

        /// <summary>
        /// Provides access to the MCI device associated with the playing media (e.g. Player.MciDevice.Command).
        /// </summary>
        public MciDevice MciDevice
        {
            get
            {
                if (_mciClass == null) _mciClass = new MciDevice(this);
                return _mciClass;
            }
        }

        #endregion


        // ************************ System Control Panels

        #region Public - System Control Panels

        /// <summary>
        /// Provides access to the system audio and display control panels (e.g. Player.SystemPanels.ShowAudioMixer).
        /// </summary>
        public SystemPanels SystemPanels
        {
            get
            {
                if (_panelsClass == null) _panelsClass = new SystemPanels(this);
                return _panelsClass;
            }
        }

        #endregion


        // ************************ Sleep Mode

        #region Public - Sleep Mode

        /// <summary>
        /// Gets or sets a value indicating whether the System Energy Saving setting (sleep mode) is disabled. The original system setting is restored when all players that disabled (set to true) this setting, have re-enabled (set to false) this setting. 
        /// </summary>
        public bool SleepDisabled
        {
            get { return _sleepOff; }
            set
            {
                if (value != _sleepOff)
                {
                    SafeNativeMethods.SleepStatus = _sleepOff = value;
                }
                _lastError = Global.MCIERR_NO_ERROR;
            }
        }

        #endregion


        // ************************ Events

        #region Public - Events

        /// <summary>
        /// Provides access to the events of the player (e.g. Player.Events.MediaEnded).
        /// </summary>
        public Events Events
        {
            get
            {
                if (_eventsClass == null) _eventsClass = new Events(this);
                return _eventsClass;
            }
        }

        #endregion


        /* Player - Private Class Members: */


        // ************************ Play

        #region Private - PlayerStartInfo

        private void AV_GetStartInfo()
        {
            _siDisplay = _display;
            _siStartPosition = TimeSpan.FromMilliseconds(_startPositionNext);
            _siEndPosition = TimeSpan.FromMilliseconds(_endPositionNext);
            _siRepeat = _repeat;
        }

        #endregion

        #region Private - ResourceToFile

        internal string AV_ResourceToFile(byte[] resource, string fileName)
        {
            string path = string.Empty;

            if (resource == null || resource.Length <= 0) _lastError = Global.MCIERR_NULL_PARAMETER_BLOCK;
            else if (string.IsNullOrEmpty(fileName)) _lastError = Global.MCIERR_FILENAME_REQUIRED;
            else
            {
                _lastError = Global.MCIERR_NO_ERROR;
                try
                {
                    if (Path.IsPathRooted(fileName)) path = fileName;
                    else path = Path.Combine(Path.GetTempPath(), fileName);
                    File.WriteAllBytes(path, resource);
                }
                catch
                {
                    path = string.Empty;
                    _lastError = Global.MCIERR_FILE_NOT_SAVED;
                }
            }
            return path;
        }
        #endregion

        #region Private - Play / PlayMedia

        private int AV_Play()
        {
            if (_busyStarting) return 0;
            if (_siFileName.Length < 4)
            {
                _lastError = Global.MCIERR_FILENAME_REQUIRED;
                return _lastError;
            }
            _busyStarting = true;

            if (_hasDevice) AV_CloseDevice(false, true, true);

            _fileName = _siFileName;
            if (_siDisplay == null && _display == null)
            {
                _siDisplay = _mciNotify;
                _hasOwnWindow = true;
            }
            else
            {
                _hasOwnWindow = false;
            }

            if (_siDisplay != _display)
            {
                AV_SetDisplay(_siDisplay, false);
                Application.DoEvents();
            }

            _startPositionNext = (int)_siStartPosition.TotalMilliseconds;
            _endPositionNext = (int)_siEndPosition.TotalMilliseconds;
            _repeat = _siRepeat;

            int retVal = AV_OpenDevice();
            Application.DoEvents();

            if (_hasOwnWindow) AV_SetDisplay(null, true);

            if (retVal == Global.MCIERR_NO_ERROR)
            {
                if (!_hasOwnWindow && _fullScreen)
                {
                    AV_SetFullScreenMode(_fullScreenMode, true);
                }

                retVal = AV_PlayMedia(true);
                if (retVal == Global.MCIERR_NO_ERROR)
                {
                    if (_hasDisplayShape) AV_UpdateDisplayShape();

                    if (_overlay != null)
                    {
                        if (!_hasOverlayShown)
                        {
                            AV_SetOverlay(_overlay);
                            Application.DoEvents();
                        }
                        else _display.Invalidate();
                    }

                    if (_hasPositionSlider)
                    {
                        _positionSlider.Enabled = true;
                        if (_psHandlesProgress)
                        {
                            _positionSlider.Minimum = _startPositionCurrent;
                            _positionSlider.Maximum = _endPositionCurrent == 0 ? _mediaLength : _endPositionCurrent;
                        }
                        else
                        {
                            _positionSlider.Minimum = 0;
                            _positionSlider.Maximum = _mediaLength;
                        }
                    }
                    //if (_hasShuttleSlider) _shuttleSlider.Enabled = true;

                    if (_paused && _hasTaskbarProgress && _taskbarProgress._progressMode == TaskbarProgressMode.Track && _startPositionCurrent != 0)
                    {
                        _taskbarProgress.SetValue(_startPositionCurrent);
                    }

                    if (dc_hasDisplayClones) DisplayClones_Start();
                    if (st_SubtitlesEnabled) Subtitles_Start(false);
                    if (ms_SignalsEnabled) MS_Start(false);

                    //if (_fileName.EndsWith(".avi", StringComparison.OrdinalIgnoreCase)) _aviMode = true;

                    if (MediaStarted != null) MediaStarted(this, EventArgs.Empty);

                    if (MediaPositionChanged != null)
                    {
                        _hasPositionEvents = true;
                        OnMediaPositionChanged();
                    }
                    else _hasPositionEvents = false;

                    if (_hasPositionSlider) _positionSlider.Value = _startPositionCurrent;
                    StartMainTimerCheck();
                }
            }
            _busyStarting = false;
            _lastError = retVal;
            return retVal;
        }

        private int AV_PlayMedia(bool newPlay)
        {
            if (newPlay)
            {
                if (_startPositionNext != 0 || _endPositionNext != 0)
                {
                    if (_startPositionNext > 0 && _startPositionNext < 100)
                    {
                        _startPositionCurrent = (int)(_mediaLength * (_startPositionNext / 100.0));
                    }
                    else _startPositionCurrent = _startPositionNext;

                    if (_endPositionNext > 0 && _endPositionNext < 100)
                    {
                        _endPositionCurrent = (int)(_mediaLength * (_endPositionNext / 100.0));
                        if (_endPositionCurrent <= _startPositionCurrent) _endPositionCurrent = 0;
                    }
                    else _endPositionCurrent = _endPositionNext <= _startPositionCurrent ? 0 : _endPositionNext;
                    if (MediaStartEndChanged != null) MediaStartEndChanged(this, EventArgs.Empty);

                    _startPositionNext = 0;
                    _endPositionNext = 0;
                    if (MediaStartEndNextChanged != null) MediaStartEndNextChanged(this, EventArgs.Empty);
                }
                else if (_startPositionCurrent != 0 || _endPositionCurrent != 0)
                {
                    _startPositionCurrent = 0;
                    _endPositionCurrent = 0;
                    if (MediaStartEndChanged != null) MediaStartEndChanged(this, EventArgs.Empty);
                }
            }

            _mciBuffer.Length = 0;
            _mciBuffer.Append("play ").Append(_deviceId);
            if (_startPositionCurrent > 0)
            {
                _mciBuffer.Append(" from ").Append(_startPositionCurrent);
                if (_endPositionCurrent > _startPositionCurrent)
                {
                    _mciBuffer.Append(" to ").Append(_endPositionCurrent);
                }
            }
            else if (_endPositionCurrent > 0)
            {
                _mciBuffer.Append(" from 0 to ").Append(_endPositionCurrent);
            }
            _mciBuffer.Append(" notify");

            _resumeIsPlay = false;

            Application.DoEvents();
            int retVal = SafeNativeMethods.mciSendString(_mciBuffer.ToString(), null, 0, _mciNotify.Handle);
            Application.DoEvents();

            dc_paintBusy = false;

            if (newPlay)
            {
                if (retVal == Global.MCIERR_NO_ERROR)
                {
                    _playing = true;
                    if (_hasVideo && !_videoEnabled) AV_SetVideoEnabled(_videoEnabled);
                    if (_paused)
                    {
                        AV_MciSendString("pause");
                        if (_hasTaskbarProgress) _taskbarProgress.SetState(TaskbarStates.Paused);
                    }
                    Application.DoEvents();
                }
                else
                {
                    AV_CloseDevice(false, false, false);
                }
            }

            _lastError = retVal;
            return retVal;
        }

        #endregion


        // ************************ Device Open / Device Close / CloseDeviceHandler (WindowFix)

        #region Private - Device Open / Device Close / CloseDeviceHandler (WindowFix)

        private int AV_OpenDevice()
        {
            int retVal = Global.MCIERR_NO_ERROR;
            int aliasRepeat = Global.ALIAS_REPEAT;

            _mciVideoEnabled = DEFAULT_VIDEO_ENABLED;
            _mciAudioEnabled = DEFAULT_AUDIO_ENABLED;
            _mciAudioVolume = DEFAULT_AUDIO_VOLUME;
            _mciSpeed = DEFAULT_SPEED;

            do // repeat in (very rare) case the device alias (= deviceID) already exists
            {
                _mciBuffer.Length = 0;
                _mciBuffer.Append("open \"")
                .Append(_fileName)
                .Append("\" type mpegvideo alias ")
                .Append(_deviceId)
                .Append(" parent ")
                .Append(_display.Handle)
                .Append(" style child");

                Application.DoEvents();
                retVal = SafeNativeMethods.mciSendString(_mciBuffer.ToString(), null, 0, IntPtr.Zero);
                Application.DoEvents();

                if (retVal == Global.MCIERR_DUPLICATE_ALIAS) _deviceId = (Global.RandomNumber.Next(10, 100000000)) + "AVPD";
            }
            while (retVal == Global.MCIERR_DUPLICATE_ALIAS && aliasRepeat-- > 0);

            if (retVal == Global.MCIERR_INTERNAL)
            {
                _mciBuffer.Length = 0;
                _mciBuffer.Append("open \"")
                .Append(_fileName)
                .Append("\" alias ")
                .Append(_deviceId)
                .Append(" parent ")
                .Append(_display.Handle)
                .Append(" style child");

                Application.DoEvents();
                retVal = SafeNativeMethods.mciSendString(_mciBuffer.ToString(), null, 0, IntPtr.Zero);
                Application.DoEvents();

                if (retVal != Global.MCIERR_NO_ERROR) retVal = Global.MCIERR_INTERNAL; // if any error, restore the 'old' errorcode
            }

            if (retVal == Global.MCIERR_NO_ERROR)
            {
                _hasDevice = true;
                AV_SetDisplayMode(_displayMode);

                if (_hasOwnWindow && _hasVideo)
                {
                    SafeNativeMethods.mciSendString("close " + _deviceId, null, 0, IntPtr.Zero);

                    _hasDevice = false;

                    _hasAudio = false;
                    _hasVideo = false;
                    _hasMediaLength = false;

                    _hasVideoBounds = false;
                    _hasVideoSourceSize = false;

                    retVal = Global.MCIERR_NO_WINDOW;
                }
                else
                {
                    AV_MciSendString("set", "time format ms");
                    AV_MciSendString("set", "seek exactly on");

                    //AV_MciSendString("window", "state no action");

                    _mediaLength = Length;

                    AV_SetAudioEnabled(_audioEnabled);
                    AV_SetAudioVolume(_audioVolume);

                    AV_SetSpeed(_speed);

                    if (MediaOpened != null) MediaOpened(this, EventArgs.Empty);
                }
            }

            _lastError = retVal;
            return retVal;
        }

        private void AV_CloseDevice(bool purge, bool stopped, bool autoStop)
        {
            if (_hasDevice)
            {
                _timer.Stop();

                if (_hasPeakLevelEvents)
                {
                    _peakLevelArgs._channelCount    = pm_PeakMeterChannelCount;
                    _peakLevelArgs._masterPeakValue = -1;
                    _peakLevelArgs._channelsValues  = pm_PeakMeterValuesStop;

                    _mediaPeakLevelChanged(this, _peakLevelArgs);
                }
                _hasPositionEvents = false;

                if (st_HasSubtitles) Subtitles_Stop();
                if (ms_HasSignals) MS_Stop();
                if (dc_hasDisplayClones) DisplayClones_Stop(false);
                if (_hasTaskbarProgress) _taskbarProgress.SetState(TaskbarStates.NoProgress);

                //if (MediaClosing != null) MediaClosing(this, EventArgs.Empty);

                if (_closeHandlerEnabled && _hasVideo && _display.FindForm() == Form.ActiveForm) _closeDeviceHandler.SetForm(_display.FindForm());
                SafeNativeMethods.mciSendString("close " + _deviceId, null, 0, IntPtr.Zero);
                Application.DoEvents();

                AV_RemoveDisplay(purge);
                Application.DoEvents();

                _hasDevice = false;
                _playing = false;
                _resumeIsPlay = false;

                _startPositionCurrent = 0;
                _endPositionCurrent = 0;
                if (MediaStartEndChanged != null) MediaStartEndChanged(this, EventArgs.Empty);

                _hasAudio = false;
                _hasVideo = false;
                //_aviMode = false;
                _hasMediaLength = false;

                _hasVideoBounds = false;
                _hasVideoSourceSize = false;

                if (_hasPositionSlider)
                {
                    _positionSlider.Value = _positionSlider.Minimum;
                    _positionSlider.Enabled = false;
                }
                //if (_hasShuttleSlider) _shuttleSlider.Enabled = false;

                if (_hasTempFile)
                {
                    try
                    {
                        File.Delete(_tempFileName);
                        _hasTempFile = false;
                    }
                    catch { /* ignore */ }
                }

                if (stopped)
                {
                    if (MediaEndedNotice != null)
                    {
                        _endedEventArgs._reason = autoStop ? StopReason.AutoStop : StopReason.UserStop;
                        MediaEndedNotice(this, _endedEventArgs);
                    }
                    if (MediaEnded != null)
                    {
                        _endedEventArgs._reason = autoStop ? StopReason.AutoStop : StopReason.UserStop;
                        MediaEnded(this, _endedEventArgs);
                    }
                }
                if (MediaPositionChanged != null) OnMediaPositionChanged();

                //if (MediaClosed != null) MediaClosed(this, EventArgs.Empty);
            }
            _lastError = Global.MCIERR_NO_ERROR;
        }

        // prevents 'flashing' form when an MCI device is closed
        private sealed class CloseDeviceHandler : NativeWindow
        {
            Form window;

            internal void SetForm(Form form)
            {
                if (form != null)
                {
                    if (window != null) this.ReleaseHandle();
                    window = form;
                    this.AssignHandle(window.Handle);
                }
            }

            internal void Dispose()
            {
                if (window != null)
                {
                    this.ReleaseHandle();
                    window = null;
                }
            }

            protected override void WndProc(ref Message m)
            {
                if (m.Msg == WM_ACTIVATEAPP && m.WParam == IntPtr.Zero)
                {
                    try
                    {
                        window.Activate();
                        m.Result = IntPtr.Zero;
                    }
                    catch { /* ignore */ }
                    this.ReleaseHandle();
                    window = null;
                    return;
                }
                base.WndProc(ref m);
            }
        }

        #endregion


        // ************************ Speed

        #region Private - Speed

        private void AV_SetSpeed(int speed)
        {
            _lastError = Global.MCIERR_NO_ERROR;

            _speed = speed <= 1 ? 1 : speed;
            if (speed != _mciSpeed)
            {
                if (_hasDevice)
                {
                    _lastError = AV_MciSendString("set", "speed " + speed);
                    if (_lastError == Global.MCIERR_NO_ERROR)
                    {
                        _mciSpeed = speed;
                        //if (MediaSpeedChanged != null) MediaSpeedChanged(this, EventArgs.Empty);
                    }
                }
                if (MediaSpeedChanged != null) MediaSpeedChanged(this, EventArgs.Empty);
            }
        }

        #endregion


        // ************************ Video / Audio

        #region Private - Video / Audio

        internal void AV_SetVideoEnabled(bool enabled)
        {
            int retVal = Global.MCIERR_NO_ERROR;

            _videoEnabled = enabled;

            if (_videoEnabled != _mciVideoEnabled)
            {
                if (_hasDevice)
                {
                    if (enabled)
                    {
                        if (_hasDisplay)
                        {
                            retVal = AV_MciSendString("window", "state show");
                            if (_playing && _hasVideo)
                            {
                                AV_MciSendString("pause");
                                AV_MciSendString("resume");
                                if (_paused) AV_MciSendString("pause");
                            }
                        }
                    }
                    else
                    {
                        retVal = AV_MciSendString("window", "state hide");
                    }
                    if (retVal == Global.MCIERR_NO_ERROR)
                    {
                        _mciVideoEnabled = enabled;
                        if (MediaVideoEnabledChanged != null) MediaVideoEnabledChanged(this, EventArgs.Empty);
                    }
                }
            }
            _lastError = retVal;
        }

        internal void AV_SetVideoTrack(int track)
        {
            if (_playing)
            {
                _lastError = SafeNativeMethods.mciSendString("setvideo " + _deviceId + " stream to " + track, null, 0, IntPtr.Zero);
                if (MediaVideoTrackChanged != null && _lastError == Global.MCIERR_NO_ERROR)
                {
                    MediaVideoTrackChanged(this, EventArgs.Empty);
                }
            }
            else _lastError = Global.MCIERR_DEVICE_NOT_READY;
        }

        internal void AV_SetAudioEnabled(bool enabled)
        {
            int retVal = Global.MCIERR_NO_ERROR;

            _audioEnabled = enabled;
            if (_audioEnabled != _mciAudioEnabled)
            {
                if (_hasDevice)
                {
                    retVal = AV_MciSendString("setaudio", enabled ? "on" : "off");
                    if (retVal == Global.MCIERR_NO_ERROR)
                    {
                        _mciAudioEnabled = enabled;
                        if (MediaAudioEnabledChanged != null) MediaAudioEnabledChanged(this, EventArgs.Empty);
                    }
                    else
                    {
                        _audioEnabled = !_audioEnabled;
                    }
                }
            }
            _lastError = retVal;
        }

        internal void AV_SetAudioVolume(int volume)
        {
            if (volume < 1) volume = 0;
            else if (volume > 1000) volume = 1000;
            _audioVolume = volume;

            if (volume == _mciAudioVolume && _hasAudio)
            {
                _lastError = Global.MCIERR_NO_ERROR;
                return;
            }
            AV_SetAudioBalance(_audioBalance, true);
        }

        internal void AV_SetAudioBalance(int balance, bool volumeOnly)
        {
            int retVal = Global.MCIERR_NO_ERROR;
            int leftVolume, rightVolume;

            if (balance < 1) balance = 0;
            else if (balance > 1000) balance = 1000;
            _audioBalance = balance;

            if (balance <= 500)
            {
                leftVolume = _audioVolume;
                rightVolume = (int)((balance / 500f) * _audioVolume);
            }
            else
            {
                leftVolume = (int)(((1000 - balance) / 500f) * _audioVolume);
                rightVolume = _audioVolume;
            }

            if (_hasDevice)
            {
                retVal = AV_MciSendString("setaudio", "left volume to " + leftVolume.ToString());
                if (retVal == Global.MCIERR_NO_ERROR)
                {
                    _hasAudio = true;
                    retVal = AV_MciSendString("setaudio", "right volume to " + rightVolume.ToString());
                    _mciAudioVolume = _audioVolume;
                }
                else
                {
                    _hasAudio = false;
                }
            }

            if (volumeOnly)
            {
                if (_volumeSlider != null) _volumeSlider.Value = _audioVolume;
                if (MediaAudioVolumeChanged != null) MediaAudioVolumeChanged(this, EventArgs.Empty);
            }
            else
            {
                if (_balanceSlider != null) _balanceSlider.Value = _audioBalance;
                if (MediaAudioBalanceChanged != null) MediaAudioBalanceChanged(this, EventArgs.Empty);
            }
            _lastError = retVal;
        }

        internal void AV_SetAudioTrack(int track)
        {
            if (_playing)
            {
                _lastError = SafeNativeMethods.mciSendString("setaudio " + _deviceId + " stream to " + track, null, 0, IntPtr.Zero);
                if (MediaAudioTrackChanged != null && _lastError == Global.MCIERR_NO_ERROR)
                {
                    MediaAudioTrackChanged(this, EventArgs.Empty);
                }
            }
            else _lastError = Global.MCIERR_DEVICE_NOT_READY;
        }

        #endregion


        // ************************ Display

        #region Private - Display

        internal int AV_SetDisplay(Control newDisplay, bool setAll)
        {
            bool changeDisplay = false;
            bool oldFullScreen = false;

            int retVal = Global.MCIERR_NO_ERROR;

            if (newDisplay != _display)
            {
                if ((newDisplay != null) && _hasDevice)
                {
                    if (_hasVideo)
                    {
                        _siFileName = _fileName;
                        _siDisplay = newDisplay;
                        int oldStartPosition = _startPositionCurrent;
                        _siEndPosition = TimeSpan.FromMilliseconds(_endPositionCurrent);
                        _siRepeat = _repeat;
                        //_siStartPosition = Position.FromStart;
                        _siStartPosition = TimeSpan.FromMilliseconds(PositionX);

                        retVal = AV_Play();
                        _startPositionCurrent = oldStartPosition;
                    }
                    if (_hasDisplay) MF_ChangeDisplay(_display, newDisplay);
                }
                else
                {
                    if (_hasDisplay)
                    {
                        if (dc_hasDisplayClones && newDisplay == null) DisplayClones_Clear();
                        if (_hasDisplayShape && newDisplay == null) AV_RemoveDisplayShape(true);
                        if (newDisplay != null) MF_ChangeDisplay(_display, newDisplay);

                        changeDisplay = true;
                        oldFullScreen = _fullScreen;
                        Form oldOverlay = _overlay;
                        bool oldHasOverlay = _hasOverlay;

                        AV_RemoveDisplay(true);

                        _overlay = oldOverlay;
                        _hasOverlay = oldHasOverlay;
                    }
                    if (newDisplay != null)
                    {
                        _display = newDisplay;
                        _hasDisplay = true;

                        AV_SetDisplayEvents();
                        Application.DoEvents();
                        if (setAll)
                        {
                            AV_SetFullScreen(changeDisplay ? oldFullScreen : _fullScreen);
                            AV_SetOverlay(_overlay);
                        }
                    }
                    if (dc_hasDisplayClones && _hasDisplay) DisplayClones_DisplayCheck();
                }
                if (MediaDisplayChanged != null) MediaDisplayChanged(this, EventArgs.Empty);
            }
            _lastError = retVal;
            return retVal;
        }

        internal void AV_RemoveDisplay(bool purge)
        {
            if (_hasDisplay)
            {
                if (_hasOverlay)
                {
                    if (purge)
                    {
                        AV_RemoveOverlay(true);
                    }
                    else
                    {
                        if (!_overlayHold) AV_RemoveOverlay(false);
                    }
                }
                if (purge)
                {
                    if (_playing && _hasVideo) AV_CloseDevice(false, true, true);
                    if (_fullScreen) AV_ResetFullScreen();
                    if (_hasDisplayEvents) AV_RemoveDisplayEvents();
                    _display = null;
                    _hasDisplay = false;
                }
            }
        }

        internal void AV_SetDisplayMode(DisplayMode displayMode)
        {
            int videoTop = 0;
            int videoLeft = 0;
            int videoWidth = 0;
            int videoHeight = 0;

            int retVal = AV_GetDisplayModeSize(displayMode, ref videoTop, ref videoLeft, ref videoWidth, ref videoHeight);
            Application.DoEvents();
            if (retVal == Global.MCIERR_NO_ERROR)
            {
                _hasVideo = true;
                if (_display != null) _display.Invalidate();
            }
            else
            {
                _hasVideo = false;
            }
            _lastError = retVal;
        }

        private int AV_GetDisplayModeSize(DisplayMode mode, ref int top, ref int left, ref int width, ref int height)
        {
            if (_hasDevice)
            {
                if (_hasVideoSourceSize)
                {
                    left = 0;
                    top = 0;
                    width = _videoSourceSize.Width;
                    height = _videoSourceSize.Height;
                }
                else
                {
                    _mciSmallBuffer1.Length = 0;
                    int retVal = SafeNativeMethods.mciSendString("where " + _deviceId + " source", _mciSmallBuffer1, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if ((retVal != Global.MCIERR_NO_ERROR) || (_mciSmallBuffer1.Length == 0))
                    {
                        top = 0;
                        left = 0;
                        width = 0;
                        height = 0;
                        _lastError = retVal;
                        return retVal;
                    }

                    string[] aSize = _mciSmallBuffer1.ToString().Split(' ');
                    int i;

                    left = 0;
                    for (i = 0; i < aSize[0].Length; ++i)
                    {
                        left = 10 * left + (aSize[0][i] - 48);
                    }

                    top = 0;
                    for (i = 0; i < aSize[1].Length; ++i)
                    {
                        top = 10 * top + (aSize[1][i] - 48);
                    }

                    width = 0;
                    for (i = 0; i < aSize[2].Length; ++i)
                    {
                        width = 10 * width + (aSize[2][i] - 48);
                    }

                    height = 0;
                    for (i = 0; i < aSize[3].Length; ++i)
                    {
                        height = 10 * height + (aSize[3][i] - 48);
                    }

                    width -= left;
                    left = 0;
                    height -= top;
                    top = 0;

                    _videoSourceSize.Height = height;
                    _videoSourceSize.Width = width;
                    _hasVideoSourceSize = true;
                }
            }
            else if (_hasDisplay)
            {
                width = _display.DisplayRectangle.Width;
                left = 0;
                height = _display.DisplayRectangle.Height;
                top = 0;
            }

            if (_hasDisplay)
            {
                double difX;
                double difY;
                switch (mode)
                {
                    case DisplayMode.Manual: // manual
                        left = _videoBounds.Left;
                        top = _videoBounds.Top;
                        height = _videoBounds.Height;
                        width = _videoBounds.Width;
                        break;

                    case DisplayMode.Custom: // custom
                        bool modeSet = false;
                        if (_hasDisplayCustomMode)
                        {
                            Rectangle bounds = _displayModeCallback(this);
                            if ((bounds.Right - bounds.Left >= 8) && (bounds.Bottom - bounds.Top >= 8))
                            {
                                left = bounds.Left;
                                top = bounds.Top;
                                height = bounds.Height;
                                width = bounds.Width;
                                modeSet = true;
                            }
                        }
                        if (!modeSet)
                        {
                            left = _videoBounds.Left;
                            top = _videoBounds.Top;
                            height = _videoBounds.Height;
                            width = _videoBounds.Width;
                        }
                        break;

                    case DisplayMode.Center: // center
                        left = (_display.DisplayRectangle.Width - width) / 2;
                        top = (_display.DisplayRectangle.Height - height) / 2;
                        break;

                    case DisplayMode.Zoom:  // zoom
                        difX = (double)_display.DisplayRectangle.Width / width;
                        difY = (double)_display.DisplayRectangle.Height / height;
                        if (difX < difY)
                        {
                            height = (int)(height * difX);
                            width = (int)(width * difX);
                        }
                        else
                        {
                            height = (int)(height * difY);
                            width = (int)(width * difY);
                        }
                        break;

                    case DisplayMode.ZoomCenter: // zoom and center
                        difX = (double)_display.DisplayRectangle.Width / width;
                        difY = (double)_display.DisplayRectangle.Height / height;
                        if (difX < difY)
                        {
                            height = (int)(height * difX);
                            width = (int)(width * difX);
                            top = (_display.DisplayRectangle.Height - height) / 2;
                        }
                        else
                        {
                            height = (int)(height * difY);
                            width = (int)(width * difY);
                            left = (_display.DisplayRectangle.Width - width) / 2;
                        }
                        break;

                    case DisplayMode.Stretch: // stretch
                        width = _display.DisplayRectangle.Width;
                        height = _display.DisplayRectangle.Height;
                        break;

                    case DisplayMode.CoverCenter: // cover and center
                        difX = (double)_display.DisplayRectangle.Width / width;
                        difY = (double)_display.DisplayRectangle.Height / height;
                        if (difX > difY)
                        {
                            height = (int)(height * difX);
                            width = (int)(width * difX);
                            top = (_display.DisplayRectangle.Height - height) / 2;
                        }
                        else
                        {
                            height = (int)(height * difY);
                            width = (int)(width * difY);
                            left = (_display.DisplayRectangle.Width - width) / 2;
                        }
                        break;

                    case DisplayMode.SizeToFit: // size to fit
                        if ((width - _display.DisplayRectangle.Width > 0) || (height - _display.DisplayRectangle.Height > 0))
                        {
                            difX = (double)_display.DisplayRectangle.Width / width;
                            difY = (double)_display.DisplayRectangle.Height / height;
                            if (difX < difY)
                            {
                                height = (int)(height * difX);
                                width = (int)(width * difX);
                            }
                            else
                            {
                                height = (int)(height * difY);
                                width = (int)(width * difY);
                            }
                        }
                        break;

                    case DisplayMode.SizeToFitCenter: // size to fit and center
                        if ((width - _display.DisplayRectangle.Width > 0) || (height - _display.DisplayRectangle.Height > 0))
                        {
                            difX = (double)_display.DisplayRectangle.Width / width;
                            difY = (double)_display.DisplayRectangle.Height / height;
                            if (difX < difY)
                            {
                                height = (int)(height * difX);
                                width = (int)(width * difX);
                                top = (_display.DisplayRectangle.Height - height) / 2;
                            }
                            else
                            {
                                height = (int)(height * difY);
                                width = (int)(width * difY);
                                left = (_display.DisplayRectangle.Width - width) / 2;
                            }
                        }
                        else
                        {
                            left = (_display.DisplayRectangle.Width - width) / 2;
                            top = (_display.DisplayRectangle.Height - height) / 2;
                        }
                        break;
                }
                if (mode != DisplayMode.Manual) _videoBounds = new Rectangle(left, top, width, height);
                _videoBoundsClip = Rectangle.Intersect(_display.DisplayRectangle, _videoBounds);
                _hasVideoBounds = true;
            }
            else
            {
                _hasVideoBounds = false;
            }
            _lastError = Global.MCIERR_NO_ERROR;
            return Global.MCIERR_NO_ERROR;
        }

        internal void AV_UpdateDisplayShape()
        {
            try
            {
                Region update;
                if (_hasVideoShape) update = _displayShapeCallback(this, true, _videoBounds);
                else update = _displayShapeCallback(this, false, _display.ClientRectangle);

                if (update != null)
                {
                    if (_display.Region != null) _display.Region.Dispose();
                    _display.Region = update;
                    if (_hasOverlay && _hasOverlayClipping) AV_ClipOverlay();
                }
            }
            catch { /* ignore */ }
        }

        internal void AV_RemoveDisplayShape(bool overlayClipping)
        {
            DisplayShape oldShape = _currentDisplayShape;

            _displayShapeCallback = null;
            _currentDisplayShape = DisplayShape.Normal;

            if (_hasDisplay && _display.Region != null)
            {
                _display.Region.Dispose();
                _display.Region = null;
            }
            _hasDisplayShape = false;

            if (_hasVideoShape)
            {
                MediaVideoBoundsChanged -= AV_DisplayShapeChanged;
                _hasVideoShape = false;
            }

            if (overlayClipping) AV_SetOverlayClipping(false);

            if (oldShape != DisplayShape.Normal && MediaDisplayShapeChanged != null) MediaDisplayShapeChanged(this, EventArgs.Empty);
        }

        // for display window shape video mode
        internal void AV_DisplayShapeChanged(object sender, EventArgs e)
        {
            if (_hasDisplayShape) AV_UpdateDisplayShape();
        }

        #endregion

        #region Private - Display Overlay

        internal void AV_SetOverlay(Form theOverlay)
        {
            if (theOverlay == null)
            {
                if (_hasOverlay)
                {
                    AV_RemoveOverlay(true);
                    if (MediaOverlayChanged != null) MediaOverlayChanged(this, EventArgs.Empty);
                }
                _lastError = Global.MCIERR_NO_ERROR;
                return;
            }

            if (_display == null)
            {
                _lastError = Global.MCIERR_NO_WINDOW;
                return;
            }

            if (theOverlay.GetType() == _display.FindForm().GetType())
            {
                _lastError = Global.MCIERR_CREATEWINDOW;
                return;
            }
            if (theOverlay == _overlay)
            {
                if (_hasDevice || _overlayHold)
                {
                    _overlay.Owner = _display.FindForm();
                    _overlay.SendToBack();
                    if (_overlay.ContextMenuStrip == null && _display.ContextMenuStrip != null)
                    {
                        _overlay.ContextMenuStrip = _display.ContextMenuStrip;
                        _hasOverlayMenu = true;
                    }

                    if (!_hasOverlayShown) AV_ShowOverlay();
                    else if (!_hasVideo && _overlayMode == OverlayMode.Video && (_overlay.Size != _display.DisplayRectangle.Size))
                    {
                        _overlay.Location = _display.PointToScreen(_display.DisplayRectangle.Location);
                        _overlay.Size = _display.DisplayRectangle.Size;
                    }
                }
                _lastError = Global.MCIERR_NO_ERROR;
                return;
            }

            if (_hasOverlay) AV_RemoveOverlay(true);

            Form myOverlay = theOverlay;
            myOverlay.Owner = _display.FindForm();

            myOverlay.ControlBox = false;
            myOverlay.FormBorderStyle = FormBorderStyle.None;
            myOverlay.MaximizeBox = false;
            myOverlay.MinimizeBox = false;
            myOverlay.ShowIcon = false;
            myOverlay.HelpButton = false;
            myOverlay.ShowInTaskbar = false;
            myOverlay.SizeGripStyle = SizeGripStyle.Hide;
            myOverlay.StartPosition = FormStartPosition.Manual;

            myOverlay.MinimumSize = new Size(0, 0);
            myOverlay.MaximumSize = myOverlay.MinimumSize;
            myOverlay.AutoSize = false;
            myOverlay.IsMdiContainer = false;
            myOverlay.TopMost = false;
            myOverlay.UseWaitCursor = false;
            myOverlay.WindowState = FormWindowState.Normal;

            if (myOverlay.TransparencyKey.IsEmpty) myOverlay.TransparencyKey = myOverlay.BackColor;
            if (myOverlay.ContextMenuStrip == null && _display.ContextMenuStrip != null)
            {
                myOverlay.ContextMenuStrip = _display.ContextMenuStrip;
                _hasOverlayMenu = true;
            }
            _overlay = myOverlay;
            _hasOverlay = true;

            if (_hasDevice || _overlayHold) AV_ShowOverlay();

            if (MediaOverlayChanged != null) MediaOverlayChanged(this, EventArgs.Empty);

            _lastError = Global.MCIERR_NO_ERROR;
        }

        internal void AV_ShowOverlay()
        {
            if (_hasOverlay)
            {
                bool oldFocus = (Form.ActiveForm == _overlay.Owner || Form.ActiveForm == _overlay); //.ContainsFocus || av_Overlay.Owner.Focused;
                double myOpacity = _overlay.Opacity;

                _overlay.Opacity = 0;
                _overlay.Location = _display.PointToScreen(_display.DisplayRectangle.Location);

                _hasOverlayShown = true;
                AV_SetOverlayEvents();

                _display.Invalidate();
                Application.DoEvents();

                if (_hasOverlayClipping) AV_ClipOverlay();

                if (_display.Visible && _overlay.Owner.WindowState != FormWindowState.Minimized)
                {
                    SafeNativeMethods.ShowWindow(_overlay.Handle, 4);
                    //_overlay.SendToBack(); triggers mousedown event ???
                    if (oldFocus) _overlay.Owner.Activate();
                    Application.DoEvents();
                }
                else if (_minimizeEnabled)
                {
                    _minimizedOpacity = myOpacity;
                    myOpacity = 0;
                }

                _overlay.Opacity = myOpacity;
                Application.DoEvents();

                if (dc_hasDisplayClones) DisplayClones_Start();
                if (MediaOverlayActiveChanged != null) MediaOverlayActiveChanged(this, EventArgs.Empty);
            }
        }

        internal void AV_HideOverlay()
        {
            if (_hasOverlay)
            {
                AV_RemoveOverlayEvents();
                _overlay.Hide();
                _hasOverlayShown = false;

                if (dc_displayClonesRunning && !_playing && _overlayHold) DisplayClones_Stop(false);
                if (MediaOverlayActiveChanged != null) MediaOverlayActiveChanged(this, EventArgs.Empty);
            }
        }

        private void AV_RemoveOverlay(bool purge)
        {
            if (_hasOverlay)
            {
                _display.FindForm().Focus();
                if (purge)
                {
                    if (_hasOverlayEvents) AV_RemoveOverlayEvents();
                    if (_hasOverlayMenu)
                    {
                        _overlay.ContextMenuStrip = null;
                        _hasOverlayMenu = false;
                    }
                    _overlay.Hide();
                    _overlay.Owner = null;
                    _hasOverlayShown = false;
                    _overlay = null;
                    _hasOverlay = false;

                    if (dc_displayClonesRunning)
                    {
                        if (!_playing && _overlayHold)
                        {
                            DisplayClones_Stop(false);
                        }
                        else if (!_hasVideo) // invalidate clone display
                        {
                            for (int i = 0; i < dc_displayClones.Length; i++)
                            {
                                if (dc_displayClones[i] != null && dc_displayClones[i].Control != null)
                                {
                                    dc_displayClones[i].Control.Invalidate();
                                }
                            }
                        }
                    }
                }
                else
                {
                    AV_HideOverlay();
                }
            }
            _lastError = Global.MCIERR_NO_ERROR;
        }

        internal void AV_SetOverlayCanFocus(bool value)
        {
            if (_hasOverlay)
            {
                if (value)
                {
                    AV_RemoveOverlayFocusEvents();
                }
                else
                {
                    AV_SetOverlayFocusEvents();
                    _overlay.SendToBack();
                    if (_overlay == Form.ActiveForm) _display.FindForm().Activate();
                }
            }
            _overlayCanFocus = value;
            _lastError = Global.MCIERR_NO_ERROR;
        }

        private void AV_ClipOverlay()
        {
            if (!_hasOverlay || _overlay == null) return;

            bool        setClip = false;
            Rectangle   clip = new Rectangle(0, 0, 10000, 10000);
            Rectangle   r = _display.FindForm().RectangleToScreen(_display.FindForm().ClientRectangle);

            if (r.X > _overlay.Left)
            {
                clip.X = r.X - _overlay.Left;
                setClip = true;
            }

            if (r.Y > _overlay.Top)
            {
                clip.Y = r.Y - _overlay.Top;
                setClip = true;
            }

            if (_overlay.Right > r.Right)
            {
                clip.Width = _overlay.Width - (_overlay.Right - r.Right) - clip.X;
                setClip = true;
            }

            if (_overlay.Bottom > r.Bottom)
            {
                clip.Height = _overlay.Height - (_overlay.Bottom - r.Bottom) - clip.Y;
                setClip = true;
            }

            if (_overlay.Region != null) _overlay.Region.Dispose();
            if (_hasDisplayShape)
            {
                _overlay.Region = null;
                if (dc_hasDisplayClones)
                {
                    DisplayClones_Pause();
                    Application.DoEvents();
                    foreach (Control c in _overlay.Controls) c.Visible = false;
                }

                Region temp = _display.Region.Clone();
                if (_hasVideo) temp.Translate(-(_videoBounds.X + 1), -(_videoBounds.Y + 1));
                if (setClip) temp.Intersect(new Region(clip));
                _overlay.Region = temp;
                //temp.Dispose(); don't do that

                if (dc_hasDisplayClones)
                {
                    foreach (Control c in _overlay.Controls) c.Visible = true;
                    Application.DoEvents();
                    DisplayClones_Refresh();
                    Application.DoEvents();
                    DisplayClones_Resume();
                }
            }
            else if (setClip)
            {
                _overlay.Region = new Region(clip);
            }
            else _overlay.Region = null;
        }

        internal void AV_SetOverlayClipping(bool enable)
        {
            //if (!_hasOverlay || _overlay == null) return;

            if (enable)
            {
                if (!_hasOverlayClipping)
                {
                    _hasOverlayClipping = true;
                    if (!(_display is Form))
                    {
                        _display.FindForm().Resize += AV_DoResize;
                        _hasOverlayClippingEvents = true;
                        if (_hasOverlay && _overlay != null)
                        {
                            _display.FindForm().Invalidate();
                            AV_ClipOverlay();
                        }
                    }
                }
            }
            else
            {
                if (_hasOverlayClipping)
                {
                    _hasOverlayClipping = false;
                    if (_hasOverlayClippingEvents)
                    {
                        _display.FindForm().Resize -= AV_DoResize;
                        _hasOverlayClippingEvents = false;
                        if (_hasOverlay && _overlay != null)
                        {
                            if (_overlay.Region != null)
                            {
                                _overlay.Region.Dispose();
                                _overlay.Region = null;
                                _overlay.Invalidate();
                                _display.FindForm().Invalidate();
                            }
                        }
                    }
                }
            }
        }

        #endregion

        #region Private - FullScreen

        internal void AV_SetFullScreen(bool value)
        {
            int retVal = Global.MCIERR_NO_ERROR;

            if (_display == null)
            {
                retVal = Global.MCIERR_CREATEWINDOW;
            }
            else
            {
                if (value)
                {
                    if (!_fullScreen)
                    {
                        retVal = !AV_AddFullScreen(_display.FindForm()) ? Global.MCIERR_CREATEWINDOW : AV_SetFullScreenMode(_fullScreenMode, true);
                    }
                }
                else
                {
                    if (_fullScreen)
                    {
                        retVal = AV_ResetFullScreen();
                    }
                }
            }
            _lastError = retVal;
        }

        private static bool AV_AddFullScreen(Form theForm)
        {
            int i = 0;

            for (; i < MAX_FULLSCREEN_PLAYERS; i++)
            {
                if (_fullScreenForms[i] == theForm) return false;
            }

            for (i = 0; i < MAX_FULLSCREEN_PLAYERS; i++)
            {
                if (_fullScreenForms[i] == null)
                {
                    _fullScreenForms[i] = theForm;
                    return true;
                }
            }

            return false;
        }

        private static void AV_RemoveFullScreen(Form theForm)
        {
            for (int i = 0; i < MAX_FULLSCREEN_PLAYERS; i++)
            {
                if (_fullScreenForms[i] == theForm)
                {
                    _fullScreenForms[i] = null;
                    break;
                }
            }
        }

        private int AV_ResetFullScreen()
        {
            FullScreenMode saveMode = _fullScreenMode;

            if (_fullScreen)
            {
                if (FullScreenMode != FullScreenMode.Form) AV_SetFullScreenMode(FullScreenMode.Form, false);

                Form theForm = (Form)_display.TopLevelControl;
                theForm.FormBorderStyle = _fsFormBorder;
                theForm.Bounds = _fsFormBounds;
                _fullScreenMode = saveMode;
                _fullScreen = false;

                AV_RemoveFullScreen(theForm);

                if (MediaFullScreenChanged != null) MediaFullScreenChanged(this, EventArgs.Empty);
            }
            return Global.MCIERR_NO_ERROR;
        }

        private int AV_SetFullScreenMode(FullScreenMode mode, bool doEvent)
        {
            Form theForm = null;
            bool fChanged = false;

            if (!_fullScreen)
            {
                if (_display.GetType().BaseType == typeof(Form))
                    theForm = (Form)_display;
                else
                    theForm = (Form)_display.TopLevelControl;

                _fsFormBounds = theForm.WindowState == FormWindowState.Maximized ? theForm.RestoreBounds : theForm.Bounds;
                _fsFormBorder = theForm.FormBorderStyle;

                Rectangle r = Screen.GetBounds(_display);

                theForm.FormBorderStyle = FormBorderStyle.None;

                SafeNativeMethods.SetWindowPos(theForm.Handle, IntPtr.Zero, r.Left, r.Top, r.Width, r.Height, SafeNativeMethods.SWP_SHOWWINDOW);

                _fullScreenMode = FullScreenMode.Form;
                _fullScreen = true;

                if (theForm == _display || mode == FullScreenMode.Form)
                {
                    if (MediaFullScreenChanged != null) MediaFullScreenChanged(this, EventArgs.Empty);
                    return Global.MCIERR_NO_ERROR;
                }
                fChanged = true;
            }

            if (mode != _fullScreenMode)
            {
                switch (mode)
                {
                    case FullScreenMode.Display:
                        if (_fullScreenMode == FullScreenMode.Form) //  else its mode parent
                        {
                            if (_display.Parent.GetType().BaseType != typeof(Form)) // upsize parent if any
                            {
                                if (_display.Parent.Parent.GetType().BaseType == typeof(Form))
                                {
                                    FS_SetDisplayParent(); // upsize parent
                                }
                                else
                                {
                                    theForm.ResumeLayout();
                                    return Global.MCIERR_CREATEWINDOW; // nested too deep
                                }
                            }
                        }
                        FS_SetDisplay(); // upsize display
                        _fullScreenMode = FullScreenMode.Display;
                        break;

                    case FullScreenMode.Parent:
                        if (_fullScreenMode == FullScreenMode.Display)
                        {
                            FS_ResetDisplay(); // downsize display
                            _fullScreenMode = _display.Parent.GetType().BaseType == typeof(Form) ? FullScreenMode.Form : FullScreenMode.Parent;
                        }
                        else // its mode form
                        {
                            if (_display.Parent.GetType().BaseType != typeof(Form)) // upsize parent if any
                            {
                                if (_display.Parent.Parent.GetType().BaseType == typeof(Form))
                                {
                                    FS_SetDisplayParent(); // upsize parent
                                }
                                else
                                {
                                    theForm.ResumeLayout();
                                    return Global.MCIERR_CREATEWINDOW; // nested to deep
                                }
                                _fullScreenMode = FullScreenMode.Parent;
                            }
                            else
                            {
                                _fullScreenMode = FullScreenMode.Form;
                            }
                        }
                        break;

                    case FullScreenMode.Form:
                        if (_fullScreenMode == FullScreenMode.Parent) // downsize parent
                        {
                            FS_ResetDisplayParent();
                        }
                        else // downsize display
                        {
                            FS_ResetDisplay();
                            if (_display.Parent.GetType().BaseType != typeof(Form)) FS_ResetDisplayParent();
                        }
                        _fullScreenMode = FullScreenMode.Form;
                        break;
                }

                if (doEvent && MediaFullScreenModeChanged != null) MediaFullScreenModeChanged(this, EventArgs.Empty);
            }

            if (fChanged && MediaFullScreenChanged != null) MediaFullScreenChanged(this, EventArgs.Empty);

            if (theForm != null)
            {
                theForm.ResumeLayout();
                Application.DoEvents();
            }

            return Global.MCIERR_NO_ERROR;
        }

        private void FS_SetDisplay()
        {
            _fsDisplayIndex = _display.Parent.Controls.GetChildIndex(_display);
            _fsDisplayBounds = _display.Bounds;
            try
            {
                _fsDisplayBorder = ((Panel)_display).BorderStyle;
                ((Panel)_display).BorderStyle = BorderStyle.None;
            }
            catch { /* ignored */ }

            Rectangle r = _display.Parent.Bounds;
            r.X = r.Y = 0;
            _display.Bounds = r;
            _display.BringToFront();
        }

        private void FS_ResetDisplay()
        {
            try
            {
                ((Panel)_display).BorderStyle = _fsDisplayBorder;
            }
            catch { /* ignored */ }
            _display.Bounds = _fsDisplayBounds;
            _display.Parent.Controls.SetChildIndex(_display, _fsDisplayIndex);
        }

        private void FS_SetDisplayParent()
        {
            _fsParentIndex = _display.Parent.Parent.Controls.GetChildIndex(_display.Parent);
            _fsParentBounds = _display.Parent.Bounds;
            try
            {
                _fsParentBorder = ((Panel)_display.Parent).BorderStyle;
                ((Panel)_display.Parent).BorderStyle = BorderStyle.None;
            }
            catch { /* ignored */ }

            Rectangle r = _display.Parent.Parent.Bounds;
            r.X = r.Y = 0;
            _display.Parent.Bounds = r;
            _display.Parent.BringToFront();
        }

        private void FS_ResetDisplayParent()
        {
            try
            {
                ((Panel)_display.Parent).BorderStyle = _fsParentBorder;
            }
            catch { /* ignored */ }
            _display.Parent.Bounds = _fsParentBounds;
            _display.Parent.Parent.Controls.SetChildIndex(_display.Parent, _fsParentIndex);
        }

        #endregion


        // ************************ MCI MciSendString

        #region Private - MciSendString

        internal int AV_MciSendString(string mciString1, string mciString2 = null)
        {
            _mciBuffer.Length = 0;
            _mciBuffer.Append(mciString1).Append(" ").Append(_deviceId);
            if (mciString2 != null)
            {
                _mciBuffer.Append(" ").Append(mciString2);
            }
            _lastError = SafeNativeMethods.mciSendString(_mciBuffer.ToString(), null, 0, IntPtr.Zero);
            Application.DoEvents();

            return _lastError;
        }

        #endregion


        // ************************ Player Events / Display Shape Callbacks

        #region Private - Player Events / Display Shape Callbacks

        private void AV_SetDisplayEvents()
        {
            if (!_hasDisplayEvents)
            {
                try
                {
                    _display.Paint += AV_DoPaint;
                    _display.Layout += AV_DoLayout;
                    _hasDisplayEvents = true;
                }
                catch { /* ignored */ }
            }
        }

        private void AV_RemoveDisplayEvents()
        {
            if (_hasDisplayEvents)
            {
                MF_UnsubscribePlayer(this); // ToDo: put this somewhere else?
                try
                {
                    _display.Paint -= AV_DoPaint;
                    _display.Layout -= AV_DoLayout;
                }
                catch { /* ignored */ }
                _hasDisplayEvents = false;
                AV_RemoveOverlayEvents();
            }
        }

        private void AV_SetOverlayEvents()
        {
            if (!_hasOverlayEvents)
            {
                try
                {
                    Form parent = _display.FindForm();
                    parent.Move += AV_DoMove;
                    if (parent.IsMdiChild) parent.MdiParent.Move += AV_DoMove;

                    if (!(_display is Form))
                    {
                        _display.Move += AV__DoDisplayMove;
                        if (_hasOverlayClipping)
                        {
                            parent.Resize += AV_DoResize;
                            _hasOverlayClippingEvents = true;
                        }
                    }

                    _overlay.FormClosing += AV_Overlay_FormClosing;

                    _hasOverlayEvents = true;
                    if (!_overlayCanFocus) AV_SetOverlayFocusEvents();
                }
                catch { /* ignored */ }
                AV_MinimizeActivate(true);
            }
        }

        private void AV_RemoveOverlayEvents()
        {
            if (_hasOverlayEvents)
            {
                try
                {
                    Form parent = _display.FindForm();
                    parent.Move -= AV_DoMove;
                    if (parent.IsMdiChild) parent.MdiParent.Move -= AV_DoMove;

                    if (!(_display is Form)) _display.Move -= AV__DoDisplayMove;
                    if (_hasOverlayClippingEvents)
                    {
                        parent.Resize -= AV_DoResize;
                        _hasOverlayClippingEvents = false;
                        if (_overlay.Region != null)
                        {
                            _overlay.Region.Dispose();
                            _overlay.Region = null;
                        }
                    }

                    _overlay.FormClosing -= AV_Overlay_FormClosing;
                    AV_RemoveOverlayFocusEvents();
                }
                catch { /* ignored */ }
                _hasOverlayEvents = false;
                AV_MinimizeActivate(false);
            }
        }

        // Prevent overlay being closed by user (with OverlayCanFocus)
        private static void AV_Overlay_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing) e.Cancel = true;
        }

        private void AV_SetOverlayFocusEvents()
        {
            if (!_hasOverlayFocusEvents)
            {
                try
                {
                    _overlay.Activated += AV_DoActivated;
                    _hasOverlayFocusEvents = true;
                }
                catch { /* ignored */ }
            }
        }

        private void AV_RemoveOverlayFocusEvents()
        {
            if (_hasOverlayFocusEvents)
            {
                try
                {
                    _overlay.Activated -= AV_DoActivated;
                }
                catch { /* ignored */ }
                _hasOverlayFocusEvents = false;
            }
        }

        private void AV_DoLayout(object sender, LayoutEventArgs e)
        {
            _display.Invalidate();
            //if (_hasVideo && (_displayMode == DisplayMode.Normal || _displayMode == DisplayMode.Center || _displayMode == DisplayMode.Manual || _displayMode == DisplayMode.CoverCenter || _displayMode == DisplayMode.Custom))
            if (_hasVideo && (_displayMode <= DisplayMode.Center || _displayMode == DisplayMode.CoverCenter || _displayMode >= DisplayMode.Custom))
            {
                if (_resizeFormRefresh) _display.FindForm().Invalidate(true);
                else if (_paused) _display.Refresh();
            }
            //if (_hasDisplayClones) DisplayClones_Refresh();
            if (_hasDisplayShape)
            {
                _display.Refresh();
                AV_UpdateDisplayShape();
                if (_resizeFormRefresh) _display.FindForm().Invalidate(true);
            }
        }

        private void AV_DoPaint(object sender, PaintEventArgs e)
        {
            if ((_display.Width < 4) || (_display.Height < 4)) return;

            int videoTop = 0;
            int videoLeft = 0;
            int videoWidth = 0;
            int videoHeight = 0;

            AV_GetDisplayModeSize(_displayMode, ref videoTop, ref videoLeft, ref videoWidth, ref videoHeight);

            if (_hasOverlayEvents && _hasOverlayShown)
            {
                // Set Overlay location and size
                if (_overlayMode == OverlayMode.Video && _hasVideo && _hasVideoBounds)
                {
                    _overlay.Location = _display.PointToScreen(_videoBoundsClip.Location);
                    _overlay.Size = _videoBoundsClip.Size;
                }
                else
                {
                    _overlay.Location = _display.PointToScreen(Point.Empty);
                    _overlay.Size = _display.DisplayRectangle.Size;
                }
            }

            _paintCommand.Length = 0;
            _paintCommand.Append("put ")
                .Append(_deviceId)
                .Append(" window at ")
                .Append(videoLeft.ToString())
                .Append(" ")
                .Append(videoTop.ToString())
                .Append(" ")
                .Append(videoWidth.ToString())
                .Append(" ")
                .Append(videoHeight.ToString());
            SafeNativeMethods.mciSendString(_paintCommand.ToString(), null, 0, IntPtr.Zero);
        }

        private void AV_DoMove(object sender, EventArgs e)
        {
            if (_overlayMode == OverlayMode.Display || !_hasVideoBounds)
            {
                _overlay.Location = _display.PointToScreen(_display.DisplayRectangle.Location);
            }
            else
            {
                if (_overlayMode == OverlayMode.Video)
                {
                    if (_videoBounds.Left < 0)
                    {
                        if (_videoBounds.Top < 0) _overlay.Location = _display.PointToScreen(_display.DisplayRectangle.Location);
                        else
                        {
                            //_movePoint.X = _display.DisplayRectangle.Location.X;
                            //_movePoint.Y = _videoBounds.Y;
                            //_overlay.Location = _display.PointToScreen(_movePoint);

                            _overlay.Location = _display.PointToScreen(new Point(_display.DisplayRectangle.Location.X, _videoBounds.Y));
                        }
                    }
                    else if (_videoBounds.Top < 0)
                    {
                        //_movePoint.X = _videoBounds.X;
                        //_movePoint.Y = _display.DisplayRectangle.Location.Y;
                        //_overlay.Location = _display.PointToScreen(_movePoint);

                        _overlay.Location = _display.PointToScreen(new Point(_videoBounds.X, _display.DisplayRectangle.Location.Y));
                    }
                    else _overlay.Location = _display.PointToScreen(_videoBounds.Location);
                }
                else // if (_overlayMode == OverlayMode.Clipped)
                {
                    _overlay.Location = _display.PointToScreen(_videoBounds.Location);
                }
            }
        }

        private void AV__DoDisplayMove(object sender, EventArgs e)
        {
            _display.Invalidate();
            if (_hasOverlayClipping)
            {
                _display.Refresh();
                AV_ClipOverlay();
            }
        }

        private void AV_DoResize(object sender, EventArgs e)
        {
            AV_ClipOverlay();
        }

        private void AV_DoActivated(object sender, EventArgs e)
        {
            if (_hasOverlayEvents)
            {
                // About the "DoEvents", here and elsewhere: without them many "things" would not work well or not at all.
                // Even changing the order in which they are used can make a big difference.

                // Application.DoEvents(); // this is causing problems with overlay events
                // replaced with version 0.77:

                if (_overlay.Owner.IsMdiChild)
                {
                    _overlay.Owner.MdiParent.Activate();
                    Application.DoEvents();
                }
                else
                {
                    Application.DoEvents();
                    _overlay.Owner.Activate();
                }

                if (mf_Active && mf_HasMouseDown)
                {
                    // check if to raise mousedown event
                    //MF_OverlayActivateMouseDown(this);
                    for (int i = 0; i < mf_Clients.Count; i++)
                    {
                        if (mf_Clients[i].Player == this && mf_Clients[i].EventType == MediaMouseEventFlags.MouseDown && mf_Clients[i].Player._hasOverlay && !mf_Clients[i].Player._overlayCanFocus)
                        {
                            mf_MessageFilter.mf_MouseEventArgs._location = Control.MousePosition;
                            mf_MessageFilter.mf_MouseEventArgs._button = Control.MouseButtons;
                            mf_MessageFilter.mf_MouseEventArgs._clicks = 1;
                            mf_MessageFilter.mf_MouseEventArgs._delta = 0;
                            mf_Clients[i].CallBack(mf_Clients[i].Display, mf_MessageFilter.mf_MouseEventArgs);

                            mf_MessageFilter.mf_MouseUpPendingPlayer = mf_Clients[i].Player;
                            mf_MessageFilter.mf_MouseUpPendingButton = mf_MessageFilter.mf_MouseEventArgs._button;
                            mf_MessageFilter.mf_MouseUpPending = true;

                            break;
                        }
                    }
                }
            }
        }

        internal void AV_EndOfMedia(bool seek)
        {
            if (_repeat)
            {
                if (!_busyStarting)
                {
                    _busyStarting = true;

                    if (MediaRepeating != null) MediaRepeating(this, EventArgs.Empty);

                    _skipPositionEvents = true;
                    if (_hasVideo && _startPositionCurrent == 0) _startPositionCurrent = 1;
                    if (seek) AV_MciSendString("seek", "to " + _startPositionCurrent);
                    AV_PlayMedia(false);
                    if (_startPositionCurrent == 1) _startPositionCurrent = 0;
                    _skipPositionEvents = false;

                    Application.DoEvents();

                    _busyStarting = false;
                    if (MediaRepeated != null) MediaRepeated(this, EventArgs.Empty);
                }
            }
            else
            {
                AV_CloseDevice(false, false, false);
                if (MediaEndedNotice != null)
                {
                    _endedEventArgs._reason = StopReason.Finished;
                    MediaEndedNotice(this, _endedEventArgs);
                }
                if (MediaEnded != null)
                {
                    _endedEventArgs._reason = StopReason.Finished;
                    MediaEnded(this, _endedEventArgs);
                }
            }
        }

        // Player preset display window shape callbacks:

        internal Region AV_PresetArrowDownShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;

            // x and y units
            float x = r.Width / 6f;
            float y = r.Height / 6f;

            PointF[] points = new PointF[8];

            points[0].X = r.X + x;
            //points[1].X = r.X + x;
            points[1].X = points[0].X;
            points[2].X = r.X;
            points[3].X = r.X + (3 * x);
            points[4].X = r.X + (6 * x);
            points[5].X = r.X + (5 * x);
            //points[6].X = r.X + (5 * x);
            points[6].X = points[5].X;
            //points[7].X = r.X + x;
            points[7].X = points[0].X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + (3 * y);
            //points[2].Y = r.Y + (3 * y);
            points[2].Y = points[1].Y;
            points[3].Y = r.Y + (6 * y);
            //points[4].Y = r.Y + (3 * y);
            points[4].Y = points[1].Y;
            //points[5].Y = r.Y + (3 * y);
            points[5].Y = points[1].Y;
            points[6].Y = r.Y;
            points[7].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetArrowLeftShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;

            // x and y units
            float x = r.Width / 6f;
            float y = r.Height / 6f;

            PointF[] points = new PointF[8];

            points[0].X = r.X + (3 * x);
            points[1].X = r.X;
            //points[2].X = r.X + (3 * x);
            points[2].X = points[0].X;
            //points[3].X = r.X + (3 * x);
            points[3].X = points[0].X;
            points[4].X = r.X + (6 * x);
            //points[5].X = r.X + (6 * x);
            points[5].X = points[4].X;
            //points[6].X = r.X + (3 * x);
            points[6].X = points[0].X;
            //points[7].X = r.X + (3 * x);
            points[7].X = points[0].X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + (3 * y);
            points[2].Y = r.Y + (6 * y);
            points[3].Y = r.Y + (5 * y);
            //points[4].Y = r.Y + (5 * y);
            points[4].Y = points[3].Y;
            points[5].Y = r.Y + y;
            //points[6].Y = r.Y + y;
            points[6].Y = points[5].Y;
            points[7].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetArrowRightShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;

            // x and y units
            float x = r.Width / 6f;
            float y = r.Height / 6f;

            PointF[] points = new PointF[8];

            points[0].X = r.X + (3 * x);
            //points[1].X = r.X + (3 * x);
            points[1].X = points[0].X;
            points[2].X = r.X;
            points[3].X = r.X;
            //points[4].X = r.X + (3 * x);
            points[4].X = points[0].X;
            //points[5].X = r.X + (3 * x);
            points[5].X = points[0].X;
            points[6].X = r.X + (6 * x);
            //points[7].X = r.X + (3 * x);
            points[7].X = points[0].X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + y;
            //points[2].Y = r.Y + y;
            points[2].Y = points[1].Y;
            points[3].Y = r.Y + (5 * y);
            //points[4].Y = r.Y + (5 * y);
            points[4].Y = points[3].Y;
            points[5].Y = r.Y + (6 * y);
            points[6].Y = r.Y + (3 * y);
            points[7].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetArrowUpShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;

            // x and y units
            float x = r.Width / 6f;
            float y = r.Height / 6f;

            PointF[] points = new PointF[8];

            points[0].X = r.X + (3 * x);
            points[1].X = r.X;
            points[2].X = r.X + x;
            //points[3].X = r.X + x;
            points[3].X = points[2].X;
            points[4].X = r.X + (5 * x);
            //points[5].X = r.X + (5 * x);
            points[5].X = points[4].X;
            points[6].X = r.X + (6 * x);
            //points[7].X = r.X + (3 * x);
            points[7].X = points[0].X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + (3 * y);
            //points[2].Y = r.Y + (3 * y);
            points[2].Y = points[1].Y;
            points[3].Y = r.Y + (6 * y);
            //points[4].Y = r.Y + (6 * y);
            points[4].Y = points[3].Y;
            //points[5].Y = r.Y + (3 * y);
            points[5].Y = points[1].Y;
            //points[6].Y = r.Y + (3 * y);
            points[6].Y = points[1].Y;
            points[7].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetBarsShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;

            // x and y units
            float x = r.Width / 5f;
            float y = r.Height;

            PointF[] points = new PointF[13];

            points[0].X = r.X + (5 * x);
            points[1].X = r.X;
            points[2].X = r.X;
            points[3].X = r.X + x;
            points[4].X = r.X + x;
            points[5].X = r.X + (2 * x);
            //points[6].X = r.X + (2 * x);
            points[6].X = points[5].X;
            points[7].X = r.X + (3 * x);
            //points[8].X = r.X + (3 * x);
            points[8].X = points[7].X;
            points[9].X = r.X + (4 * x);
            //points[10].X = r.X + (4 * x);
            points[10].X = points[9].X;
            //points[11].X = r.X + (5 * x);
            points[11].X = points[0].X;
            //points[12].X = r.X + (5 * x);
            points[12].X = points[0].X;

            points[0].Y = r.Y;
            points[1].Y = r.Y;
            points[2].Y = r.Y + y;
            //points[3].Y = r.Y + y;
            points[3].Y = points[2].Y;
            points[4].Y = r.Y;
            points[5].Y = r.Y;
            //points[6].Y = r.Y + y;
            points[6].Y = points[2].Y;
            //points[7].Y = r.Y + y;
            points[7].Y = points[2].Y;
            points[8].Y = r.Y;
            points[9].Y = r.Y;
            //points[10].Y = r.Y + y;
            points[10].Y = points[2].Y;
            //points[11].Y = r.Y + y;
            points[11].Y = points[2].Y;
            points[12].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetBeamsShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;
            
            // x and y units
            float x = r.Width;
            float y = r.Height / 5;

            PointF[] points = new PointF[13];

            points[0].X = r.X;
            points[1].X = r.X;
            points[2].X = r.X + x;
            //points[3].X = r.X + x;
            points[3].X = points[2].X;
            points[4].X = r.X;
            points[5].X = r.X;
            //points[6].X = r.X + x;
            points[6].X = points[2].X;
            //points[7].X = r.X + x;
            points[7].X = points[2].X;
            points[8].X = r.X;
            points[9].X = r.X;
            //points[10].X = r.X + x;
            points[10].X = points[2].X;
            //points[11].X = r.X + x;
            points[11].X = points[2].X;
            points[12].X = r.X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + (5 * y);
            //points[2].Y = r.Y + (5 * y);
            points[2].Y = points[1].Y;
            points[3].Y = r.Y + (4 * y);
            //points[4].Y = r.Y + (4 * y);
            points[4].Y = points[3].Y;
            points[5].Y = r.Y + (3 * y);
            //points[6].Y = r.Y + (3 * y);
            points[6].Y = points[5].Y;
            points[7].Y = r.Y + (2 * y);
            //points[8].Y = r.Y + (2 * y);
            points[8].Y = points[7].Y;
            points[9].Y = r.Y + y;
            //points[10].Y = r.Y + y;
            points[10].Y = points[9].Y;
            points[11].Y = r.Y;
            points[11].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetCrossShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;
            
            // x and y units
            float x = r.Width / 6f;
            float y = r.Height / 6f;

            PointF[] points = new PointF[13];

            points[0].X = r.X + (2 * x);
            //points[1].X = r.X + (2 * x);
            points[1].X = points[0].X;
            points[2].X = r.X;
            points[3].X = r.X;
            //points[4].X = r.X + (2 * x);
            points[4].X = points[0].X;
            //points[5].X = r.X + (2 * x);
            points[5].X = points[0].X;
            points[6].X = r.X + (4 * x);
            //points[7].X = r.X + (4 * x);
            points[7].X = points[6].X;
            points[8].X = r.X + (6 * x);
            //points[9].X = r.X + (6 * x);
            points[9].X = points[8].X;
            //points[10].X = r.X + (4 * x);
            points[10].X = points[6].X;
            //points[11].X = r.X + (4 * x);
            points[11].X = points[6].X;
            //points[12].X = r.X + (2 * x);
            points[12].X = points[0].X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + (2 * y);
            //points[2].Y = r.Y + (2 * y);
            points[2].Y = points[1].Y;
            points[3].Y = r.Y + (4 * y);
            //points[4].Y = r.Y + (4 * y);
            points[4].Y = points[3].Y;
            points[5].Y = r.Y + (6 * y);
            //points[6].Y = r.Y + (6 * y);
            points[6].Y = points[5].Y;
            //points[7].Y = r.Y + (4 * y);
            points[7].Y = points[3].Y;
            //points[8].Y = r.Y + (4 * y);
            points[8].Y = points[3].Y;
            //points[9].Y = r.Y + (2 * y);
            points[9].Y = points[1].Y;
            //points[10].Y = r.Y + (2 * y);
            points[10].Y = points[1].Y;
            points[11].Y = r.Y;
            points[12].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetDiamondShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;
            
            // x and y units
            float x = r.Width / 6f;
            float y = r.Height / 6f;

            PointF[] points = new PointF[5];

            points[0].X = r.X + (3 * x);
            points[1].X = r.X;
            //points[2].X = r.X + (3 * x);
            points[2].X = points[0].X;
            points[3].X = r.X + (6 * x);
            //points[4].X = r.X + (3 * x);
            points[4].X = points[0].X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + (3 * y);
            points[2].Y = r.Y + (6 * y);
            //points[3].Y = r.Y + (3 * y);
            points[3].Y = points[1].Y;
            points[4].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetHeartShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;

            // x and y units
            float x = r.Width / 6f;
            float y = r.Height / 6f;

            GraphicsPath path = new GraphicsPath();
            path.FillMode = FillMode.Winding;

            path.AddEllipse(new RectangleF(r.X, r.Y, 3.1f * x, 3 * y));
            path.AddEllipse(new RectangleF(r.X + (2.9f * x), r.Y, 3.1f * x, 3 * y));

            PointF[] points = new PointF[4];

            points[0].X = r.X + (0.085f * x);
            points[0].Y = r.Y + (2f * y);

            points[1].X = r.X + (5.78f * x);
            //points[1].Y = r.Y + (2f * y);
            points[1].Y = points[0].Y;

            points[2].X = r.X + (3 * x);
            points[2].Y = r.Y + (6 * y);

            //points[3].X = r.X + (0.085f * x);
            points[3].X = points[0].X;
            //points[3].Y = r.Y + (2f * y);
            points[3].Y = points[0].Y;

            path.AddCurve(points, 0.15f);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetHexagonalShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;
           
            // x and y units
            float x = r.Width / 4f;
            float y = r.Height / 4f;

            PointF[] points = new PointF[7];

            points[0].X = r.X + x;
            points[1].X = r.X;
            //points[2].X = r.X + x;
            points[2].X = points[0].X;
            points[3].X = r.X + (3 * x);
            points[4].X = r.X + (4 * x);
            //points[5].X = r.X + (3 * x);
            points[5].X = points[3].X;
            //points[6].X = r.X + x;
            points[6].X = points[0].X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + (2 * y);
            points[2].Y = r.Y + (4 * y);
            //points[3].Y = r.Y + (4 * y);
            points[3].Y = points[2].Y;
            //points[4].Y = r.Y + (2 * y);
            points[4].Y = points[1].Y;
            points[5].Y = r.Y;
            points[6].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetOvalShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            GraphicsPath path = new GraphicsPath();
            path.AddEllipse(shapeBounds);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetRoundedShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            IntPtr handle = SafeNativeMethods.CreateRoundRectRgn(
                shapeBounds.Left + 1,
                shapeBounds.Top + 1,
                shapeBounds.Left + shapeBounds.Width + 2,
                shapeBounds.Top + shapeBounds.Height + 2,
                48,
                48);

            Region region = Region.FromHrgn(handle);
            SafeNativeMethods.DeleteObject(handle);

            return region;
        }

        internal Region AV_PresetStarShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;
            
            // x and y units
            float x = r.Width / 6f;
            float y = r.Height / 6f;

            PointF[] points = new PointF[17];

            points[0].X = r.X;
            points[1].X = r.X + (2.5f * x);
            points[2].X = r.X + (3 * x);
            points[3].X = r.X + (3.5f * x);
            points[4].X = r.X + (6 * x);
            points[5].X = r.X + (4.5f * x);
            // points[6].X = r.X + (6 * x);
            points[6].X = points[4].X;
            //points[7].X = r.X + (4.5f * x);
            points[7].X = points[5].X;
            //points[8].X = r.X + (6 * x);
            points[8].X = points[4].X;
            //points[9].X = r.X + (3.5f * x);
            points[9].X = points[3].X;
            //points[10].X = r.X + (3 * x);
            points[10].X = points[2].X;
            //points[11].X = r.X + (2.5f * x);
            points[11].X = points[1].X;
            points[12].X = r.X;
            points[13].X = r.X + (1.5f * x);
            points[14].X = r.X;
            //points[15].X = r.X + (1.5f * x);
            points[15].X = points[13].X;
            points[16].X = r.X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + (1.5f * y);
            points[2].Y = r.Y;
            //points[3].Y = r.Y + (1.5f * y);
            points[3].Y = points[1].Y;
            points[4].Y = r.Y;
            points[5].Y = r.Y + (2.5f * y);
            points[6].Y = r.Y + (3 * y);
            points[7].Y = r.Y + (3.5f * y);
            points[8].Y = r.Y + (6 * y);
            points[9].Y = r.Y + (4.5f * y);
            //points[10].Y = r.Y + (6 * y);
            points[10].Y = points[8].Y;
            //points[11].Y = r.Y + (4.5f * y);
            points[11].Y = points[9].Y;
            //points[12].Y = r.Y + (6 * y);
            points[12].Y = points[8].Y;
            //points[13].Y = r.Y + (3.5f * y);
            points[13].Y = points[7].Y;
            //points[14].Y = r.Y + (3 * y);
            points[14].Y = points[6].Y;
            //points[15].Y = r.Y + (2.5f * y);
            points[15].Y = points[5].Y;
            points[16].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        //internal Region AV_PresetStarShape(Player player, bool videoShape, Rectangle shapeBounds)
        //{
        //    Rectangle r = shapeBounds;
        //    if (!videoShape)
        //    {
        //        if (_display == _display.FindForm())
        //        {
        //            r.X--;
        //            r.Y--;
        //            r.Width += 1;
        //            r.Height += 1;
        //        }
        //        else
        //        {
        //            r.Width += 1;
        //            r.Height += 1;
        //        }
        //    }

        //    // x and y units
        //    float x = r.Width / 6f;
        //    float y = r.Height / 6f;

        //    PointF[] points = new PointF[11];

        //    points[0].X = r.X + (3 * x);
        //    points[1].X = r.X + (2.25f * x);
        //    points[2].X = r.X;
        //    points[3].X = r.X + (1.7f * x);
        //    points[4].X = r.X + x;
        //    points[5].X = r.X + (3 * x);
        //    points[6].X = r.X + (5 * x);
        //    points[7].X = r.X + (4.3f * x);
        //    points[8].X = r.X + (6 * x);
        //    points[9].X = r.X + (3.75f * x);
        //    points[10].X = r.X + (3 * x);

        //    points[0].Y = r.Y;
        //    points[1].Y = r.Y + (2 * y);
        //    points[2].Y = r.Y + (2 * y);
        //    points[3].Y = r.Y + (3.5f * y);
        //    points[4].Y = r.Y + (6 * y);
        //    points[5].Y = r.Y + (5 * y);
        //    points[6].Y = r.Y + (6 * y);
        //    points[7].Y = r.Y + (3.5f * y);
        //    points[8].Y = r.Y + (2 * y);
        //    points[9].Y = r.Y + (2 * y);
        //    points[10].Y = r.Y;

        //    System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
        //    path.AddPolygon(points);

        //    Region region = new Region(path);
        //    path.Dispose();

        //    return region;
        //}

        internal Region AV_PresetTilesShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;

            // x and y units
            float x = r.Width / 3f;
            float y = r.Height / 3f;

            PointF[] points = new PointF[21];

            points[0].X = r.X;
            points[1].X = r.X;
            points[2].X = r.X + x;
            points[3].X = r.X + x;
            points[4].X = r.X;
            points[5].X = r.X;
            points[6].X = r.X + x;
            points[7].X = r.X + x;
            points[8].X = r.X + (2 * x);
            points[9].X = r.X + (2 * x);
            points[10].X = r.X + (3 * x);
            points[11].X = r.X + (3 * x);
            points[12].X = r.X + (2 * x);
            points[13].X = r.X + (2 * x);
            points[14].X = r.X + (3 * x);
            points[15].X = r.X + (3 * x);
            points[16].X = r.X + (2 * x);
            points[17].X = r.X + (2 * x);
            points[18].X = r.X + x;
            points[19].X = r.X + x;
            points[20].X = r.X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + y;
            points[2].Y = r.Y + y;
            points[3].Y = r.Y + (2 * y);
            points[4].Y = r.Y + (2 * y);
            points[5].Y = r.Y + (3 * y);
            points[6].Y = r.Y + (3 * y);
            points[7].Y = r.Y + (2 * y);
            points[8].Y = r.Y + (2 * y);
            points[9].Y = r.Y + (3 * y);
            points[10].Y = r.Y + (3 * y);
            points[11].Y = r.Y + (2 * y);
            points[12].Y = r.Y + (2 * y);
            points[13].Y = r.Y + y;
            points[14].Y = r.Y + y;
            points[15].Y = r.Y;
            points[16].Y = r.Y;
            points[17].Y = r.Y + y;
            points[18].Y = r.Y + y;
            points[19].Y = r.Y;
            points[20].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        internal Region AV_PresetTriangleShape(Player player, bool videoShape, Rectangle shapeBounds)
        {
            Rectangle r = shapeBounds;
            
            // x and y units
            float x = r.Width / 4f;
            float y = r.Height / 4f;

            PointF[] points = new PointF[4];

            points[0].X = r.X + (2 * x);
            points[1].X = r.X;
            points[2].X = r.X + (4 * x);
            //points[3].X = r.X + (2 * x);
            points[3].X = points[0].X;

            points[0].Y = r.Y;
            points[1].Y = r.Y + (4 * y);
            //points[2].Y = r.Y + (4 * y);
            points[2].Y = points[1].Y;
            points[3].Y = r.Y;

            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(points);

            Region region = new Region(path);
            path.Dispose();

            return region;
        }

        #endregion


        // ************************ Main Timer Start / Stop / Event

        #region Private - Main Timer Start / Stop / Event

        internal void StartMainTimerCheck()
    {
        if (!_timer.Enabled && (_playing && !_paused && (_hasPositionEvents || _hasPositionSlider || _hasPeakLevelEvents || _hasTaskbarProgress))) _timer.Start();
    }

        internal void StopMainTimerCheck()
    {
        if (_timer.Enabled && (!_playing || (!_hasPositionEvents && !_hasPositionSlider && !_hasPeakLevelEvents && !_hasTaskbarProgress))) _timer.Stop();
    }

        internal void AV_TimerTick(object sender, EventArgs e)
    {
        if (!_skipPositionEvents)
        {
            if (_hasPositionSlider)
            {
                int pos = PositionX;
                if (pos <= _positionSlider.Minimum) _positionSlider.Value = _positionSlider.Minimum;
                else if (pos >= _positionSlider.Maximum) _positionSlider.Value = _positionSlider.Maximum;
                else _positionSlider.Value = pos;

                if (_hasTaskbarProgress)
                {
                    _taskbarProgress.SetValue(pos);
                }
            }
            else if (_hasTaskbarProgress)
            {
                _taskbarProgress.SetValue(PositionX);
            }

            if (MediaPositionChanged != null) OnMediaPositionChanged();
        }

        if (_hasPeakLevelEvents)
        {
            if (_audioVolume == 0 || !_audioEnabled || _paused)
            {
                if (!_peakLevelMuted)
                {
                    _peakLevelArgs._channelCount = pm_PeakMeterChannelCount;
                    _peakLevelArgs._masterPeakValue = -1;
                    _peakLevelArgs._channelsValues = pm_PeakMeterValuesStop;
                    _peakLevelMuted = true;
                    _mediaPeakLevelChanged(this, _peakLevelArgs);
                }
            }
            else
            {
                PeakMeter_GetValues();
                _peakLevelArgs._channelCount    = pm_PeakMeterChannelCount;
                _peakLevelArgs._masterPeakValue = pm_PeakMeterMasterValue;
                _peakLevelArgs._channelsValues  = pm_PeakMeterValues;
                _peakLevelMuted = false;
                _mediaPeakLevelChanged(this, _peakLevelArgs);
            }
        }
    }

        #endregion


        // ******************************** Slider (TrackBar) Managers

        #region Slider (TrackBar) Managers

        /// <summary>
        /// Provides access to the sliders (trackbars) settings of the player (e.g. Player.Sliders.Position).
        /// </summary>
        public Sliders Sliders
        {
            get
            {
                if (_slidersClass == null) _slidersClass = new Sliders(this);
                return _slidersClass;
            }
        }

        #region Public - PositionSlider Manager

        #region PositionSlider Fields

        internal int        _psMouseWheel       = 0;    // 0 = disabled
        internal int        _psMouseWheelShift  = 5000;

        internal TrackBar   _positionSlider;
        internal bool       _hasPositionSlider;

        internal bool       _psHorizontal;
        internal bool       _psLiveSeek         = true;
        internal bool       _psSkipSignals;     // media signals
        internal bool       _psTracking;

        internal bool       _psHandlesProgress  = false;
        internal int        _psValue;
        internal bool       _psBusy;
        internal bool       _psSkipped;

        private Point       _psOverlayMouse;

        #endregion

        internal void PositionSlider_MouseDown(object sender, MouseEventArgs e)
        {
            if (_hasDevice)
            {
                if (e.Button == MouseButtons.Left)
                {
                    _psTracking = true;
                    _psSkipped = false;

                    _timer.Stop();
                    _positionSlider.Scroll -= PositionSlider_Scroll;

                    float pos;
                    if (_psHorizontal)
                    {
                        if (e.X <= 13) pos = 0;
                        else if (e.X >= _positionSlider.Width - 13) pos = 1;
                        else pos = (float)(e.X - 13) / (_positionSlider.Width - 27);
                    }
                    else
                    {
                        if (e.Y <= 13) pos = 1;
                        else if (e.Y >= _positionSlider.Height - 13) pos = 0;
                        else pos = 1 - (float)(e.Y - 13) / (_positionSlider.Height - 27);
                    }

                    if (_psHandlesProgress)
                    {
                        if (Math.Abs(_positionSlider.Value - (int)(pos * (_positionSlider.Maximum - _positionSlider.Minimum)) + _positionSlider.Minimum) > 5) _positionSlider.Value = (int)(pos * (_positionSlider.Maximum - _positionSlider.Minimum)) + _positionSlider.Minimum;
                    }
                    else if (Math.Abs(_positionSlider.Value - (int)(pos * _positionSlider.Maximum)) > 5) _positionSlider.Value = (int)(pos * _positionSlider.Maximum);
                    _psValue = _positionSlider.Value;

                    if (_psLiveSeek)
                    {
                        if (ms_HasSignals && _psSkipSignals) ms_SignalsBusy = true;
                        PositionX = _positionSlider.Value;
                    }
                    else
                    {
                        if (st_HasSubtitles)
                        {
                            st_SubtitleChangedArgs._index = -1;
                            st_SubtitleChangedArgs._subtitle = string.Empty;
                            _mediaSubtitleChanged(this, st_SubtitleChangedArgs);
                            st_SubtitlesBusy = true;
                        }

                        if (ms_HasSignals) ms_SignalsBusy = true;
                    }
                }
            }
            else
            {
                _positionSlider.Value = 0;
                _psValue = 0;
            }
        }

        internal void PositionSlider_MouseMove(object sender, MouseEventArgs e)
        {
            if (_psTracking)
            {
                if (e.Location == _psOverlayMouse) return; // for overlays with timers
                _psOverlayMouse = e.Location;

                if (_psBusy) _psSkipped = true;
                else
                {
                    if (_positionSlider.Value != _psValue)
                    {
                        _psBusy = true;
                        do
                        {
                            _psSkipped = false;
                            if (_psLiveSeek) PositionX = _positionSlider.Value;
                            else if (MediaPositionChanged != null) OnMediaPositionChanged();

                        } while (_psSkipped);
                        _psBusy = false;
                    }
                }
            }
        }

        internal void PositionSlider_MouseUp(object sender, MouseEventArgs e)
        {
            if (_psTracking)
            {
                if (_psLiveSeek)
                {
                    if (ms_HasSignals && _psSkipSignals) ms_SignalsBusy = false;
                }
                else
                {
                    st_SubtitlesBusy = false;
                    ms_SignalsBusy = false;
                    PositionX = _positionSlider.Value;
                }

                _positionSlider.Scroll += PositionSlider_Scroll;
                if (!_paused) _timer.Start();

                _psTracking = false;
            }
        }

        internal void PositionSlider_Scroll(object sender, EventArgs e)
        {
            if (_hasDevice)
            {
                if (!_psTracking)
                {
                    if (_psBusy) _psSkipped = true;
                    else
                    {
                        _psBusy = true;

                        do
                        {
                            _psSkipped = false;
                            PositionX = _positionSlider.Value;

                        } while (_psSkipped);

                        _psBusy = false;
                    }
                }
            }
            else
            {
                _positionSlider.Value = 0;
                _psValue = 0;
            }
        }

        internal void PositionSlider_MouseWheel(object sender, MouseEventArgs e)
        {
            ((HandledMouseEventArgs)e).Handled = true;

            if (!_psBusy && _playing && _psMouseWheel > 0)
            {
                _psBusy = true;

                int pos = _positionSlider.Value;
                if (e.Delta > 0)
                {
                    if (Control.ModifierKeys == Keys.Shift) pos += _psMouseWheelShift;
                    else pos += _psMouseWheel;
                    if (pos > _positionSlider.Maximum) pos = _positionSlider.Maximum;
                }
                else
                {
                    if (Control.ModifierKeys == Keys.Shift) pos -= _psMouseWheelShift;
                    else pos -= _psMouseWheel;
                    if (pos < _positionSlider.Minimum) pos = _positionSlider.Minimum;
                }

                _positionSlider.Value = pos;
                PositionX = pos;

                _psBusy = false;
            }
        }

        #endregion

        #region Public - ShuttleSlider Manager

        internal TrackBar    _shuttleSlider;
        internal bool        _hasShuttleSlider;
        private bool        _shuttleSliderBusy;

        internal void ShuttleSlider_MouseDown(object sender, MouseEventArgs e)
        {
            //if (_hasDisplay) _display.Focus();

            if (_playing && Control.MouseButtons == MouseButtons.Left)
            {
                if (_hasVideo) AV_MciSendString("set", "speed 4");
                while (Control.MouseButtons == MouseButtons.Left)
                {
                    if (_shuttleSlider.Value != 0)
                    {
                        if (_shuttleSlider.Value < 0 && !_paused) Step(_shuttleSlider.Value - 1);
                        else Step(_shuttleSlider.Value);
                        System.Threading.Thread.Sleep(25);
                    }
                    Application.DoEvents();
                }
                if (_hasVideo) AV_MciSendString("set", "speed " + _speed);
            }
        }

        internal void ShuttleSlider_MouseUp(object sender, MouseEventArgs e)
        {
            _shuttleSlider.Value = 0;
        }

        internal void ShuttleSlider_MouseWheel(object sender, MouseEventArgs e)
        {
            const int VIDEO_SMALL_STEP = 1;
            const int VIDEO_LARGE_STEP = 5;

            ((HandledMouseEventArgs)e).Handled = true;

            if (_hasVideo && !_shuttleSliderBusy)
            {
                _shuttleSliderBusy = true;
                bool highSpeed = Control.ModifierKeys == Keys.Shift;

                AV_MciSendString("set", "speed 4");

                if (e.Delta < 0)
                {
                    if (highSpeed)
                    {
                        _shuttleSlider.Value = _shuttleSlider.Minimum;
                        Step(-VIDEO_LARGE_STEP);
                    }
                    else
                    {
                        _shuttleSlider.Value = -1;
                        if (_paused) Step(-VIDEO_SMALL_STEP);
                        else Step(-VIDEO_LARGE_STEP);
                    }
                }
                else
                {
                    if (highSpeed)
                    {
                        _shuttleSlider.Value = _shuttleSlider.Maximum;
                        Step(VIDEO_LARGE_STEP);
                    }
                    else
                    {
                        _shuttleSlider.Value = 1;
                        if (_paused) Step(VIDEO_SMALL_STEP);
                        else Step(VIDEO_LARGE_STEP);
                    }
                }
                Application.DoEvents();
                System.Threading.Thread.Sleep(150);
                _shuttleSlider.Value = 0;

                AV_MciSendString("set", "speed " + _speed);

                _shuttleSliderBusy = false;
            }
        }

        #endregion

        #region Public - VolumeSlider Manager

        internal TrackBar _volumeSlider;

        internal void VolumeSlider_Scroll(object sender, EventArgs e)
        {
            AV_SetAudioVolume(_volumeSlider.Value);
        }

        #endregion

        #region Public - BalanceSlider Manager

        internal TrackBar _balanceSlider;

        internal void BalanceSlider_Scroll(object sender, EventArgs e)
        {
            AV_SetAudioBalance(_balanceSlider.Value, false);
        }

        #endregion

        #region Public - SpeedSlider Manager

        internal TrackBar    _speedSlider;
        internal bool        _speedSliderBusy;

        internal void SpeedSlider_MouseDown(object sender, MouseEventArgs e)
        {
            _speedSkipped = false;
        }

        internal void SpeedSlider_Scroll(object sender, EventArgs e)
        {
            if (_speedSliderBusy)
            {
                _speedSkipped = true;
            }
            else
            {
                _speedSliderBusy = true;
                do
                {
                    _speedSkipped = false;

                    int scrollSpeed;
                    switch (_speedSlider.Value)
                    {
                        case 0: scrollSpeed = 100;
                            break;
                        case 1: scrollSpeed = 250;
                            break;
                        case 2: scrollSpeed = 333;
                            break;
                        case 3: scrollSpeed = 500;
                            break;
                        case 4: scrollSpeed = 666;
                            break;
                        case 5: scrollSpeed = 750;
                            break;
                        case 6: scrollSpeed = 1000;
                            break;
                        case 7: scrollSpeed = 1500;
                            break;
                        case 8: scrollSpeed = 2000;
                            break;
                        case 9: scrollSpeed = 2500;
                            break;
                        case 10: scrollSpeed = 3000;
                            break;
                        case 11: scrollSpeed = 3500;
                            break;
                        default: scrollSpeed = 4000;
                            break;
                    }
                    AV_SetSpeed(scrollSpeed);
                } while (_speedSkipped);

                _speedSliderBusy = false;
            }
        }

        internal void SpeedSlider_ValueToSlider(int speed)
        {
            if (speed < 875)
            {
                if (speed < 175) speed = 0;
                else if (speed < 292) speed = 1;
                else if (speed < 416) speed = 2;
                else if (speed < 583) speed = 3;
                else if (speed < 708) speed = 4;
                else speed = 5;
            }
            else
            {
                if (speed < 1250) speed = 6;
                else if (speed < 1750) speed = 7;
                else if (speed < 2250) speed = 8;
                else if (speed < 2750) speed = 9;
                else if (speed < 3250) speed = 10;
                else if (speed < 3750) speed = 11;
                else speed = 12;
            }

            _speedSlider.Value = speed;
        }

        internal void SpeedSlider_MouseWheel(object sender, MouseEventArgs e)
        {
            ((HandledMouseEventArgs)e).Handled = true;

            int newSpeed = _speed;
            bool highSpeed = Control.ModifierKeys == Keys.Shift;

            if (e.Delta < 0)
            {
                if (highSpeed) newSpeed -= 100;
                else newSpeed -= 10;
            }
            else
            {
                if (highSpeed) newSpeed += 100;
                else newSpeed += 10;
            }
            AV_SetSpeed(newSpeed);
            if (newSpeed == _speed) SpeedSlider_ValueToSlider(newSpeed);
        }

        #endregion

        #endregion

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Peter Vegter
United States United States
No Biography provided

You may also be interested in...

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01 | 2.8.181215.1 | Last Updated 7 Aug 2018
Article Copyright 2010 by Peter Vegter
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid