NativeLibVlc.cs

From VideoLAN Wiki
Revision as of 01:44, 21 April 2007 by Tappen (talk | contribs) (Fixed a bug in Stop(). It was pausing instead of stopping.)
Jump to navigation Jump to search
This page contains example code for .Net Interface to VLC.
This code is licensed under the GNU Lesser General public license
/*****************************************************************************
 * NativeLibVlc.cs: NativeLibVlc class definition
 *****************************************************************************
 * Copyright (C) 2006 Chris Meadowcroft
 *
 * Authors: Chris Meadowcroft
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;

namespace VLanControl
{
	internal enum ObjectType : int
	{
		VLC_OBJECT_ROOT = (-1),
		VLC_OBJECT_VLC = (-2),
		VLC_OBJECT_MODULE = (-3),
		VLC_OBJECT_INTF = (-4),
		VLC_OBJECT_PLAYLIST = (-5),
		VLC_OBJECT_ITEM = (-6),
		VLC_OBJECT_INPUT = (-7),
		VLC_OBJECT_DECODER = (-8),
		VLC_OBJECT_VOUT = (-9),
		VLC_OBJECT_AOUT = (-10),
		VLC_OBJECT_SOUT = (-11),
		VLC_OBJECT_HTTPD = (-12),
		VLC_OBJECT_PACKETIZER = (-13),
		VLC_OBJECT_ENCODER = (-14),
		VLC_OBJECT_DIALOGS = (-15),
		VLC_OBJECT_VLM = (-16),
		VLC_OBJECT_ANNOUNCE = (-17),
		VLC_OBJECT_DEMUX = (-18),
		VLC_OBJECT_ACCESS = (-19),
		VLC_OBJECT_STREAM = (-20),
		VLC_OBJECT_OPENGL = (-21),
		VLC_OBJECT_FILTER = (-22),
		VLC_OBJECT_VOD = (-23),
		VLC_OBJECT_SPU = (-24),
		VLC_OBJECT_TLS = (-25),
		VLC_OBJECT_SD = (-26),
		VLC_OBJECT_XML = (-27),
		VLC_OBJECT_OSDMENU = (-28),
		VLC_OBJECT_STATS = (-29),
		VLC_OBJECT_HTTPD_HOST = (-30),

		VLC_OBJECT_GENERIC = (-666),
	}

	internal enum VlcError : int
	{
		Success = -0,
		NoMem = -1,
		Thread = -2,
		Timeout = -3,

		NoMod = -10,

		NoObj = -20,
		BadObj = -21,

		NoVar = -30,
		BadVar = -31,

		Exit = -255,
		Generic = -666,

		Exception = -998,
		NoInit = -999,
	};

	internal enum InputState : int
	{
		INIT_S = 0,
		OPENING_S = 1,
		BUFFERING_S = 2,
		PLAYING_S = 3,
		PAUSE_S = 4,
		END_S = 5,
		ERROR_S = 6,
	}

	internal class NativeLibVlc : IDisposable
	{
        #region private vlc enums

        enum Mode : int
        {
            Insert      = 0x01,
            Replace     = 0x02,
            Append      = 0x04,
            Go          = 0x08,
            CheckInsert = 0x10
        };

		public const Int32 EndOfPlaylist = -666;

		[Flags]
		enum ObjectSearchMode : int
		{
			FIND_PARENT         = 0x0001,
			FIND_CHILD          = 0x0002,
			FIND_ANYWHERE       = 0x0003,
			FIND_STRICT         = 0x0010,
		}

		[Flags]
		enum VarFlags : int
		{
			/** \defgroup var_flags Additive flags
			 * These flags are added to the type field of the variable. Most as a result of
			 * a __var_Change() call, but some may be added at creation time
			 * @{
			 */
			VLC_VAR_HASCHOICE     = 0x0100,
			VLC_VAR_HASMIN        = 0x0200,
			VLC_VAR_HASMAX        = 0x0400,
			VLC_VAR_HASSTEP       = 0x0800,

			VLC_VAR_ISLIST        = 0x1000,
			VLC_VAR_ISCOMMAND     = 0x2000,
			VLC_VAR_ISCONFIG      = 0x2000,

			/** Creation flag */
			VLC_VAR_DOINHERIT     = 0x8000,

			/**
			 * \defgroup var_action Variable actions
			 * These are the different actions that can be used with __var_Change().
			 * The parameters given are the meaning of the two last parameters of
			 * __var_Change() when this action is being used.
			 * @{
			 */

			/**
			 * Set the minimum value of this variable
			 * \param p_val The new minimum value
			 * \param p_val2 Unused
			 */
			VLC_VAR_SETMIN              = 0x0010,
			/**
			 * Set the maximum value of this variable
			 * \param p_val The new maximum value
			 * \param p_val2 Unused
			 */
			VLC_VAR_SETMAX              = 0x0011,
			VLC_VAR_SETSTEP             = 0x0012,
			/**
			 * Set the value of this variable without triggering any callbacks
			 * \param p_val The new value
			 * \param p_val2 Unused
			 */
			VLC_VAR_SETVALUE            = 0x0013,

			VLC_VAR_SETTEXT             = 0x0014,
			VLC_VAR_GETTEXT             = 0x0015,

			VLC_VAR_ADDCHOICE           = 0x0020,
			VLC_VAR_DELCHOICE           = 0x0021,
			VLC_VAR_CLEARCHOICES        = 0x0022,
			VLC_VAR_SETDEFAULT          = 0x0023,
			VLC_VAR_GETCHOICES          = 0x0024,
			VLC_VAR_FREECHOICES         = 0x0025,
			VLC_VAR_GETLIST             = 0x0026,
			VLC_VAR_FREELIST            = 0x0027,
			VLC_VAR_CHOICESCOUNT        = 0x0028,

			VLC_VAR_INHERITVALUE        = 0x0030,
			VLC_VAR_TRIGGER_CALLBACKS   = 0x0035,
		}

		enum playlist_command : int
		{
			PLAYLIST_PLAY = 0,      /**< No arg.                            res=can fail*/
			PLAYLIST_AUTOPLAY = 1,  /**< No arg.                            res=cant fail*/
			PLAYLIST_VIEWPLAY = 2,  /**< arg1= int, arg2= playlist_item_t*,*/
								/**  arg3 = playlist_item_t*          , res=can fail */
			PLAYLIST_ITEMPLAY = 3,  /** <arg1 = playlist_item_t *         , res=can fail */
			PLAYLIST_PAUSE = 4,     /**< No arg                             res=can fail*/
			PLAYLIST_STOP = 5,      /**< No arg                             res=can fail*/
			PLAYLIST_SKIP = 6,      /**< arg1=int,                          res=can fail*/
			PLAYLIST_GOTO = 7,      /**< arg1=int                           res=can fail */
			PLAYLIST_VIEWGOTO = 8   /**< arg1=int                           res=can fail */
		}

		enum CONFIG_ITEM : int
		{
			CONFIG_ITEM_STRING = 0x0010,
			CONFIG_ITEM_FILE = 0x0020,
			CONFIG_ITEM_MODULE = 0x0030,
			CONFIG_ITEM_INTEGER = 0x0040,
			CONFIG_ITEM_BOOL = 0x0050,
			CONFIG_ITEM_FLOAT = 0x0060,
		}

		enum input_query_e : int
		{
			/* input variable "position" */
			INPUT_GET_POSITION = 0,         /* arg1= double *       res=    */
			INPUT_SET_POSITION,         /* arg1= double         res=can fail    */

			/* input variable "length" */
			INPUT_GET_LENGTH,           /* arg1= int64_t *      res=can fail    */

			/* input variable "time" */
			INPUT_GET_TIME,             /* arg1= int64_t *      res=    */
			INPUT_SET_TIME,             /* arg1= int64_t        res=can fail    */

			/* input variable "rate" (1 is DEFAULT_RATE) */
			INPUT_GET_RATE,             /* arg1= int *          res=    */
			INPUT_SET_RATE,             /* arg1= int            res=can fail    */

			/* input variable "state" */
			INPUT_GET_STATE,            /* arg1= int *          res=    */
			INPUT_SET_STATE,            /* arg1= int            res=can fail    */

			/* input variable "audio-delay" and "sub-delay" */
			INPUT_GET_AUDIO_DELAY,      /* arg1 = int* res=can fail */
			INPUT_SET_AUDIO_DELAY,      /* arg1 = int  res=can fail */
			INPUT_GET_SPU_DELAY,        /* arg1 = int* res=can fail */
			INPUT_SET_SPU_DELAY,        /* arg1 = int  res=can fail */

			/* Meta datas */
			INPUT_ADD_INFO,   /* arg1= char* arg2= char* arg3=...     res=can fail */
			INPUT_GET_INFO,   /* arg1= char* arg2= char* arg3= char** res=can fail */
			INPUT_DEL_INFO,   /* arg1= char* arg2= char*              res=can fail */
			INPUT_SET_NAME,   /* arg1= char* res=can fail    */

			/* Input config options */
			INPUT_ADD_OPTION,      /* arg1= char * arg2= char *  res=can fail*/

			/* Input properties */
			INPUT_GET_BYTE_POSITION,     /* arg1= int64_t *       res=    */
			INPUT_SET_BYTE_SIZE,         /* arg1= int64_t *       res=    */

			/* bookmarks */
			INPUT_GET_BOOKMARKS,   /* arg1= seekpoint_t *** arg2= int * res=can fail */
			INPUT_CLEAR_BOOKMARKS, /* res=can fail */
			INPUT_ADD_BOOKMARK,    /* arg1= seekpoint_t *  res=can fail   */
			INPUT_CHANGE_BOOKMARK, /* arg1= seekpoint_t * arg2= int * res=can fail   */
			INPUT_DEL_BOOKMARK,    /* arg1= seekpoint_t *  res=can fail   */
			INPUT_SET_BOOKMARK,    /* arg1= int  res=can fail    */

			/* On the fly input slave */
			INPUT_ADD_SLAVE        /* arg1= char * */
		}

        #endregion

        #region private vlc structs
		[StructLayout(LayoutKind.Sequential)]
		struct libvlc_exception_t
		{
			public Int32 b_raised;
			public IntPtr psz_message;

			public void Init()
			{
				libvlc_exception_init(out this);
			}

			public bool WasExceptionRaised()
			{
				if(0 != libvlc_exception_raised(ref this))
				{
					libvlc_exception_clear(ref this);
					return true;
				}
				return false;
			}
		}

		[StructLayout(LayoutKind.Sequential)]
		struct libvlc_instance_t
		{
			public IntPtr p_vlc;
			public IntPtr p_playlist;
			public IntPtr p_vlm;
			public Int32 i_vlc_id;

			public libvlc_instance_t(IntPtr vlc, IntPtr playlist, int vlcHandle)
			{
				this.p_vlc = vlc;
				this.p_playlist = playlist;
				this.p_vlm = IntPtr.Zero;
				this.i_vlc_id = vlcHandle;
			}
		}

		[StructLayout(LayoutKind.Sequential)]
		struct vlc_list_t
		{
			public Int32 i_count;
			public IntPtr p_values;
			public IntPtr pi_types;
		}

        [StructLayout( LayoutKind.Explicit )]
        struct vlc_value_t
        {
            [FieldOffset( 0 )]  public Int32   i_int;
            [FieldOffset( 0 )]  public Int32   b_bool;
            [FieldOffset( 0 )][MarshalAs(UnmanagedType.R4)]  public float   f_float;
            [FieldOffset( 0 )]  public IntPtr  psz_string;
            [FieldOffset( 0 )]  public IntPtr  p_address;
            [FieldOffset( 0 )]  public IntPtr  p_object;
            [FieldOffset( 0 )]  public IntPtr  p_list;
            [FieldOffset( 0 )][MarshalAs(UnmanagedType.I8)]  public Int64   i_time;

            [FieldOffset( 0 )]  public IntPtr  psz_name;
            [FieldOffset( 4 )]  public Int32   i_object_id;
        }

		[StructLayout(LayoutKind.Sequential)]
		struct module_config_t
		{
			public CONFIG_ITEM i_type;
		}

		#endregion

		#region vlc api interop
		const int AOUT_VOLUME_MAX = 1024;
		const int VOLUME_MAX = 200;
		const int DEFAULT_CHAN = 1;
		const String Playlist_Current = "item-change";
		const String Now_Playing = "Now Playing";
		const String Meta_information = "Meta-information";
		const String Meta_title = "meta-title";
		const String Meta_author = "meta-author";
		const String Meta_artist = "meta-artist";
		const String Meta_genre = "meta-genre";
		const String Meta_description = "meta-description";
		const String Meta_url = "meta-url";
		
		[DllImport("libvlc")]
        static extern int VLC_Create();
        [DllImport("libvlc")]
		static extern VlcError VLC_Init(int iVLC, int Argc, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)]string[] Argv);
        [DllImport("libvlc")]
        static extern string VLC_Version();
        [DllImport("libvlc")]
        static extern VlcError VLC_CleanUp(int iVLC);
        [DllImport("libvlc")]
        static extern VlcError VLC_Destroy(int iVLC);
        [DllImport("libvlc")]
        static extern string VLC_Error(int i_err);

		[DllImport("libvlc")]
		static extern IntPtr vlc_current_object(int i_object);
		[DllImport("libvlc")]
		static extern IntPtr __vlc_object_find(IntPtr p_vlc, ObjectType objectType, ObjectSearchMode mode);
		[DllImport("libvlc")]
		static extern void __vlc_object_release(IntPtr p_vlc);
		[DllImport("libvlc")]
		static extern VlcError __var_Set(IntPtr p_vlc, String name, vlc_value_t value);
		[DllImport("libvlc")]
		static extern VlcError __var_Get(IntPtr p_this, String name, ref vlc_value_t value);
		[DllImport("libvlc")]
		static extern VlcError __var_Change(IntPtr p_this, String name, VarFlags varFlags,
			ref vlc_value_t value, ref vlc_value_t value2);

		[DllImport("libvlc")]
		static extern VlcError __aout_VolumeGet(IntPtr p_vlc, ref Int16 volume);
		[DllImport("libvlc")]
		static extern VlcError __aout_VolumeSet(IntPtr p_vlc, Int16 volume);
		[DllImport("libvlc")]
		static extern VlcError __aout_VolumeMute(IntPtr p_vlc, IntPtr alwaysNull);

		[DllImport("libvlc")]
		static extern void libvlc_exception_init(out libvlc_exception_t p_exception);
		[DllImport("libvlc")]
		static extern Int32 libvlc_exception_raised(ref libvlc_exception_t p_exception);
		[DllImport("libvlc")]
		static extern void libvlc_exception_clear(ref libvlc_exception_t p_exception);

		[DllImport("libvlc")]
		static extern void libvlc_playlist_play(ref libvlc_instance_t libvlc, Int32 id, 
			Int32 optionsCount, IntPtr optionsAlwaysNull, ref libvlc_exception_t p_exception);
		[DllImport("libvlc")]
		static extern IntPtr libvlc_playlist_get_input(ref libvlc_instance_t libvlc, ref libvlc_exception_t p_exception);

		[DllImport("libvlc")]
		static extern int VLC_PlaylistIndex(int vlcObject);
		[DllImport("libvlc")]
		static extern int VLC_PlaylistNumberOfItems(int vlcObject);

		[DllImport("libvlc")]
		static extern void libvlc_input_free(IntPtr p_input);

		[DllImport("libvlc")]
		static extern int libvlc_video_get_width(IntPtr p_input, ref libvlc_exception_t p_exception);
		[DllImport("libvlc")]
		static extern int libvlc_video_get_height(IntPtr p_input, ref libvlc_exception_t p_exception);

		[DllImport("libvlc", CallingConvention=CallingConvention.Cdecl)]
		static extern VlcError playlist_LockControl(IntPtr p_playlist, playlist_command i_query);
		[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
		static extern VlcError playlist_LockControl(IntPtr p_playlist, playlist_command i_query, Int32 arg1);
		[DllImport("libvlc")]
		static extern VlcError playlist_Clear(IntPtr p_playlist);
		[DllImport("libvlc")]
		static extern VlcError playlist_AddExt(IntPtr p_playlist, String mrl, String mrlDuplicate, 
			Mode mode, Int32 pos, Int64 mtime_t, 
			[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)]string[] Options,
			int OptionsCount);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		delegate int VarChangedCallback(IntPtr vlc, String variable, vlc_value_t old_val, 
			vlc_value_t new_val, IntPtr param);

		[DllImport("libvlc")]
		static extern int __var_AddCallback(IntPtr vlc, String variable, VarChangedCallback cb, 
			IntPtr param);
		[DllImport("libvlc")]
		static extern int __var_DelCallback(IntPtr vlc, String variable, VarChangedCallback cb, 
			IntPtr param);
		[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, CharSet=CharSet.Ansi)]
		static extern VlcError input_Control(IntPtr input_thread_t, input_query_e i_query, String category, String name,
			ref IntPtr result);

		[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
		static extern void __vout_OSDMessage(IntPtr p_input, int i_channel, String message);
		[DllImport("libvlc")]
		static extern IntPtr config_FindConfig(IntPtr vlc, String name);
		[DllImport("libvlc")]
		static extern void __config_PutInt(IntPtr vlc, String name, int value);
		[DllImport("libvlc")]
		static extern void __config_PutFloat(IntPtr vlc, String name, float value);
		[DllImport("libvlc")]
		static extern void __config_PutPsz(IntPtr vlc, String name, String value);

		[DllImport("libvlc")]
		static extern int __config_GetInt(IntPtr vlc, String name);
		[DllImport("libvlc")]
		static extern float __config_GetFloat(IntPtr vlc, String name);
		[DllImport("libvlc")]
		static extern String __config_GetPsz(IntPtr vlc, String name);

		#endregion

		static NativeLibVlc()
		{
			NativeLibVlc.vlcInstallDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase).Substring(6);
			//NativeLibVlc.vlcInstallDirectory = Environment.CurrentDirectory;
			//NativeLibVlc.vlcInstallDirectory = QueryVlcInstallPath();
		}

		public NativeLibVlc()
        {
        }

        #region IDisposable
        public void Dispose()
        {
            if(vlcHandle != -1)
            {
                try
                {
					if(this.gch.IsAllocated)
					{
						UnhookPlaylistChanges();
					}
					VideoOutput = null;
					VLC_CleanUp(this.vlcHandle);
                    VLC_Destroy(this.vlcHandle);
                }
                catch { }
            }
            vlcHandle = -1;
        }
        #endregion

		#region internal Vlc interop helper classes
		internal class VlcObject : IDisposable
		{
			IntPtr vlc = IntPtr.Zero;
			IntPtr subObject = IntPtr.Zero;
			bool isDisposed;

			public VlcObject(int vlcHandle, ObjectType objectType)
			{
				vlc = vlc_current_object(vlcHandle);
				if(IntPtr.Zero != vlc)
				{
					if(objectType == ObjectType.VLC_OBJECT_VLC)
					{
						subObject = vlc;
					}
					else
					{
						subObject = __vlc_object_find(vlc, objectType, ObjectSearchMode.FIND_CHILD);
					}
				}
			}

			public IntPtr Vlc { get { return this.vlc; } }
			public IntPtr SubObject { get { return this.subObject; } }

			public void Dispose()
			{
				Dispose(true);
				GC.SuppressFinalize(this);
			}

			protected virtual void Dispose(bool disposing)
			{
				if(!this.isDisposed)
				{
					this.isDisposed = true;
					if((IntPtr.Zero != subObject) && (subObject != vlc))
					{
						__vlc_object_release(subObject);
					}
					if(IntPtr.Zero != vlc)
					{
						__vlc_object_release(vlc);
					}
				}
			}

			protected bool IsDisposed { get { return this.isDisposed; } }

			~VlcObject()
			{
				Dispose(false);
			}
		}

		private class VlcPlaylistObject : VlcObject
		{
			public libvlc_instance_t libvlc;
			public libvlc_exception_t exception;

			public VlcPlaylistObject(int vlcHandle)
				: base(vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST)
			{
				if(this.SubObject != IntPtr.Zero)
				{
					this.libvlc = new libvlc_instance_t(this.Vlc, this.SubObject, vlcHandle);
					this.exception.Init();
				}
			}
		}
		#endregion

		#region public Vlc interop helper functions
		public VlcObject OpenVlcObject(ObjectType objectType)
		{
			return new VlcObject(this.vlcHandle, objectType);
		}

		public int GetVlcObjectInt(ObjectType objectType, String propertyName, int errorReturn)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					vlc_value_t intValue = new vlc_value_t();
					if((vobj.SubObject != IntPtr.Zero) &&
						(VlcError.Success == __var_Get(vobj.SubObject, propertyName, ref intValue)))
					{
						return intValue.i_int;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return errorReturn;
		}

		public VlcError SetVlcObjectInt(ObjectType objectType, String propertyName, int value)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t intValue = new vlc_value_t();
						intValue.i_int = value;
						return __var_Set(vobj.SubObject, propertyName, intValue);
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return VlcError.NoObj;
		}

		public long GetVlcObjectLong(ObjectType objectType, String propertyName, long errorReturn)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					vlc_value_t longValue = new vlc_value_t();
					if((vobj.SubObject != IntPtr.Zero) &&
						(VlcError.Success == __var_Get(vobj.SubObject, propertyName, ref longValue)))
					{
						return longValue.i_time;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return errorReturn;
		}

		public VlcError SetVlcObjectLong(ObjectType objectType, String propertyName, long value)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t longValue = new vlc_value_t();
						longValue.i_time = value;
						return __var_Set(vobj.SubObject, propertyName, longValue);
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return VlcError.NoObj;
		}

		public float GetVlcObjectFloat(ObjectType objectType, String propertyName, float errorReturn)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					vlc_value_t floatValue = new vlc_value_t();
					if((vobj.SubObject != IntPtr.Zero) &&
						(VlcError.Success == __var_Get(vobj.SubObject, propertyName, ref floatValue)))
					{
						return floatValue.f_float;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return errorReturn;
		}

		public VlcError SetVlcObjectFloat(ObjectType objectType, String propertyName, float value)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t floatValue = new vlc_value_t();
						floatValue.f_float = value;
						return __var_Set(vobj.SubObject, propertyName, floatValue);
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return VlcError.NoObj;
		}

		public String GetVlcObjectString(ObjectType objectType, String propertyName, String errorReturn)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					vlc_value_t stringValue = new vlc_value_t();
					if((vobj.SubObject != IntPtr.Zero) &&
						(VlcError.Success == __var_Get(vobj.SubObject, propertyName, ref stringValue)))
					{
						return Marshal.PtrToStringAnsi(stringValue.psz_string);
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return errorReturn;
		}

		public VlcError SetVlcObjectString(ObjectType objectType, String propertyName, String value)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t stringValue = new vlc_value_t();
						IntPtr valuePtr = Marshal.StringToCoTaskMemAnsi(value);
						stringValue.psz_string = valuePtr;
						VlcError ret = __var_Set(vobj.SubObject, propertyName, stringValue);
						Marshal.ZeroFreeCoTaskMemAnsi(valuePtr);
						return ret;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return VlcError.NoObj;
		}

		public VlcError GetVlcVariableChoiceList(ObjectType objectType, String propertyName,
			out int[] choiceIds, out String[] choiceText)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t idValues = new vlc_value_t();
						vlc_value_t textValues = new vlc_value_t();
						if(VlcError.Success == __var_Change(vobj.SubObject, propertyName,
							VarFlags.VLC_VAR_GETLIST, ref idValues, ref textValues))
						{
							try
							{
								vlc_list_t idList = (vlc_list_t)Marshal.PtrToStructure(
									idValues.p_list, typeof(vlc_list_t));
								vlc_list_t textList = (vlc_list_t)Marshal.PtrToStructure(
									textValues.p_list, typeof(vlc_list_t));

								int choiceCount = idList.i_count;
								choiceIds = new Int32[choiceCount];
								choiceText = new String[choiceCount];

								for(int index = 0; index < choiceCount; index++)
								{
									IntPtr idPtr = new IntPtr(idList.p_values.ToInt32() +
										index * Marshal.SizeOf(typeof(vlc_value_t)));
									vlc_value_t idValue = (vlc_value_t)Marshal.PtrToStructure(
										idPtr, typeof(vlc_value_t));
									choiceIds[index] = idValue.i_int;

									IntPtr textPtr = new IntPtr(textList.p_values.ToInt32() +
										index * Marshal.SizeOf(typeof(vlc_value_t)));
									vlc_value_t textValue = (vlc_value_t)Marshal.PtrToStructure(
										textPtr, typeof(vlc_value_t));
									choiceText[index] = Marshal.PtrToStringAnsi(textValue.psz_string);
								}
								return VlcError.Success;
							}
							finally
							{
								__var_Change(vobj.SubObject, propertyName, VarFlags.VLC_VAR_FREELIST,
									ref idValues, ref textValues);
							}
						}
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}

			choiceIds = new int[0];
			choiceText = new string[0];
			return VlcError.NoObj;
		}

		public VlcError GetVlcVariableChoiceList(ObjectType objectType, String propertyName,
			out String[] choices, out String[] choiceText)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t idValues = new vlc_value_t();
						vlc_value_t textValues = new vlc_value_t();
						if(VlcError.Success == __var_Change(vobj.SubObject, propertyName,
							VarFlags.VLC_VAR_GETLIST, ref idValues, ref textValues))
						{
							try
							{
								vlc_list_t idList = (vlc_list_t)Marshal.PtrToStructure(
									idValues.p_list, typeof(vlc_list_t));
								vlc_list_t textList = (vlc_list_t)Marshal.PtrToStructure(
									textValues.p_list, typeof(vlc_list_t));

								int choiceCount = idList.i_count;
								List<String> choiceList = new List<string>(choiceCount);
								List<String> choiceTextList = new List<string>(choiceCount);
								Dictionary<String, int> choiceDict = new Dictionary<string, int>(choiceCount);
								for(int index = 0; index < choiceCount; index++)
								{
									IntPtr idPtr = new IntPtr(idList.p_values.ToInt32() +
										index * Marshal.SizeOf(typeof(vlc_value_t)));
									vlc_value_t idValue = (vlc_value_t)Marshal.PtrToStructure(
										idPtr, typeof(vlc_value_t));
									String choice = Marshal.PtrToStringAnsi(idValue.psz_name);
									choiceList.Add(choice);
									if(choiceDict.ContainsKey(choice))
									{
										choiceDict[choice] = choiceDict[choice] + 1;
									}
									else
									{
										choiceDict[choice] = 1;
									}

									IntPtr textPtr = new IntPtr(textList.p_values.ToInt32() +
										index * Marshal.SizeOf(typeof(vlc_value_t)));
									vlc_value_t textValue = (vlc_value_t)Marshal.PtrToStructure(
										textPtr, typeof(vlc_value_t));
									choiceTextList.Add(Marshal.PtrToStringAnsi(textValue.psz_string));
								}

								int listIndex = 0;
								for(int index = 0; index < choiceCount; index++)
								{
									String choice = choiceList[listIndex];
									if((choiceDict[choice] > 1) && (choiceTextList[listIndex] == null))
									{
										choiceList.RemoveAt(listIndex);
										choiceTextList.RemoveAt(listIndex);
										choiceDict[choice] = choiceDict[choice] - 1;
									}
									else
									{
										listIndex++;
									}
								}
								for(int index = 0; index < choiceList.Count; index++)
								{
									if(choiceTextList[index] == null)
									{
										choiceTextList[index] = choiceList[index];
									}
								}

								choices = choiceList.ToArray();
								choiceText = choiceTextList.ToArray();
								return VlcError.Success;
							}
							finally
							{
								__var_Change(vobj.SubObject, propertyName, VarFlags.VLC_VAR_FREELIST,
									ref idValues, ref textValues);
							}
						}
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}

			choices = new string[0];
			choiceText = new string[0];
			return VlcError.NoObj;
		}
		#endregion

        #region public Properties
        public static string VlcInstallDir
        {
            get{ return vlcInstallDirectory; }
            set{ vlcInstallDirectory = value; }
        }

        public bool IsInitialized
        {
            get{return (vlcHandle != -1);}
        }

        public Control VideoOutput
        {
            get { return outputWindow; }
            set
            {
				if(value == null)
				{
					if(outputWindow != null)
					{
						outputWindow = null;
						if(vlcHandle != -1)
						{
							SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "drawable", 0);
						}
					}
				}
				else
				{
					outputWindow = value;
					if(vlcHandle != -1)
					{
						SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "drawable", outputWindow.Handle.ToInt32());
					}
				}
            }
        }

        public string LastError
        {
            get{return lastErrorMessage;}
        }

		public event MetaDataEventHandler NowPlaying;

		protected virtual void OnNowPlaying(MetaDataUpdateEventArgs args)
		{
			if(this.NowPlaying != null)
			{
				this.NowPlaying(this, args);
			}
		}

		protected delegate void HandleNowPlaying(MetaDataUpdateEventArgs args);

		protected virtual void VlcNowPlayingChanged(String newText)
		{
			// switch out of the Vlc thread to our User Interface thread if possible
			if(this.VideoOutput != null)
			{
				this.VideoOutput.BeginInvoke(new HandleNowPlaying(OnNowPlaying),
					new MetaDataUpdateEventArgs(MetaData.NowPlaying, newText));
			}
			else
			{
				OnNowPlaying(new MetaDataUpdateEventArgs(MetaData.NowPlaying, newText));
			}
		}

        public int Length
        {
            get
            {
				if(this.artificialLength != 0)
				{
					return this.artificialLength;
				}
				else
				{
					return Convert.ToInt32(GetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "length", 0) / 1000000L);
				}
            }
        }

		public void SetArtificialLength(int newLength)
		{
			this.artificialLength = newLength;
		}

        public int Time
        {
            get
            {
				int time = Convert.ToInt32(GetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "time", 0) / 1000000L);
				if((time == 0) && (this.artificialLength != 0))
				{
					time = Convert.ToInt32(Position * this.artificialLength + .5d);
				}
				if(this.timeScaling != 0.0d)
				{
					time = Convert.ToInt32(time / this.timeScaling);
				}
				return time;
            }
			set
			{
				if(this.artificialLength != 0)
				{
					float position = Convert.ToSingle(value) / Convert.ToSingle(this.artificialLength);
					if(this.timeScaling != 0.0d)
					{
						position = Convert.ToSingle(position * this.timeScaling);
					}
					Debug.WriteLine(String.Format("Set Position {0}", position));
					SetVlcObjectFloat(ObjectType.VLC_OBJECT_INPUT, "position", position);
				}
				else
				{
					long time = Convert.ToInt64(value) * 1000000L;
					if(this.timeScaling != 0.0d)
					{
						time = Convert.ToInt64(time * this.timeScaling);
					}
					Debug.WriteLine(String.Format("Set Time {0}", time));
					SetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "time", time);
				}
			}
        }

		public double TimeScaling
		{
			get { return this.timeScaling; }
			set { this.timeScaling = value; }
		}

        public double Position
        {
            get
            {
				double position = GetVlcObjectFloat(ObjectType.VLC_OBJECT_INPUT, "position", 0.0f);
				if(this.timeScaling != 0.0d)
				{
					position = position / this.timeScaling;
				}
				return position;
            }
			set
			{
				double position = value;
				if(this.timeScaling != 0.0d)
				{
					position = position * this.timeScaling;
				}
				Debug.WriteLine(String.Format("Set Position {0}", position));
				SetVlcObjectFloat(ObjectType.VLC_OBJECT_INPUT, "position", Convert.ToSingle(position));
			}
        }

        public int Volume
        {
            get
            {
				IntPtr vlc = vlc_current_object(vlcHandle);
				if(IntPtr.Zero != vlc)
				{
					try
					{
						Int16 aoutVol = 0;
						if(__aout_VolumeGet(vlc, ref aoutVol) == VlcError.Success)
						{
							return (aoutVol * VOLUME_MAX + AOUT_VOLUME_MAX / 2) / AOUT_VOLUME_MAX;
						}
					}
					catch(Exception)
					{
					}
					finally
					{
						__vlc_object_release(vlc);
					}
				}
				return 0;
            }
            set
            {
				IntPtr vlc = vlc_current_object(vlcHandle);
				if(IntPtr.Zero != vlc)
				{
					try
					{
						Int16 aoutVol = Convert.ToInt16((value * AOUT_VOLUME_MAX + VOLUME_MAX / 2) / VOLUME_MAX);
						__aout_VolumeSet(vlc, aoutVol);
					}
					catch(Exception)
					{
					}
					finally
					{
						__vlc_object_release(vlc);
					}
				}
            }
        }

        public bool Fullscreen
        {
            get
            {
				int isFullScreen = GetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "fullscreen", -666);
				if((isFullScreen != -666) && (isFullScreen != 0))
				{
					return true;
				}
                return false;
               
            }
            set
            {
				SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "fullscreen", value ? 1 : 0);
            }
        }

		public int PlaylistIndex
		{
			get
			{
				try
				{
					return VLC_PlaylistIndex(this.vlcHandle);
				}
				catch(Exception ex)
				{
					this.lastErrorMessage = ex.Message;
					return -1;
				}
			}
		}

		public int PlaylistCount
		{
			get
			{
				try
				{
					return VLC_PlaylistNumberOfItems(this.vlcHandle);
				}
				catch(Exception ex)
				{
					this.lastErrorMessage = ex.Message;
					return -1;
				}
			}
		}

        #endregion

        #region public Methods
        public bool Initialize()
        {
            // check if already initializes
            if(vlcHandle != -1)
                return true;

			string oldDir = Environment.CurrentDirectory;
			// try init
			try
			{
				// create instance
				Environment.CurrentDirectory = NativeLibVlc.vlcInstallDirectory;
				this.vlcHandle = VLC_Create();

				if(this.vlcHandle < 0)
				{
					lastErrorMessage = "Failed to create VLC instance";
					return false;
				}

				string[] initOptions = {	NativeLibVlc.vlcInstallDirectory,
											":no-one-instance",
											":no-loop",
											":no-drop-late-frames",
											":disable-screensaver",
											":vout=vout_directx",
											"--plugin-path=" + NativeLibVlc.vlcInstallDirectory + @"\plugins",
					};

				// init libvlc
				VlcError errVlcLib = VLC_Init(vlcHandle, initOptions.Length, initOptions);
				if(errVlcLib != VlcError.Success)
				{
					VLC_Destroy(vlcHandle);
					lastErrorMessage = "Failed to initialise VLC";
					this.vlcHandle = -1;
					return false;
				}
			}
			catch
			{
				lastErrorMessage = "Could not find libvlc";
				return false;
			}
			finally
			{
				Environment.CurrentDirectory = oldDir;
			}

            // check output window
            if(outputWindow != null)
            {
				SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "drawable", outputWindow.Handle.ToInt32());
			}

            return true;
        }

		// there's an overhead to supporting Vlc events, a memory leak, so we need to have them off by default
		public bool ProducingEvents
		{
			get { return this.gch.IsAllocated; }
			set
			{
				if(value)
				{
					if(!this.gch.IsAllocated)
					{
						HookPlaylistChanges();
					}
				}
				else
				{
					if(this.gch.IsAllocated)
					{
						UnhookPlaylistChanges();
					}
				}
			}
		}

		private static String AdjustFilterString(String current, String filter, bool add)
		{
			String newFilter = null;
			int findFilter = current.IndexOf(filter);
			if(findFilter == -1)
			{
				if(add)
				{
					if(current.Length == 0)
					{
						newFilter = filter;
					}
					else
					{
						newFilter = String.Format("{0}:{1}", current, filter);
					}
				}
			}
			else
			{
				if(!add)
				{
					int colonAfterAdjust = current.IndexOf(':', findFilter + 1);
					if(findFilter == 0)
					{
						if(colonAfterAdjust == -1)
						{
							newFilter = String.Empty;
						}
						else
						{
							newFilter = current.Substring(colonAfterAdjust + 1);
						}
					}
					else
					{
						if(colonAfterAdjust == -1)
						{
							newFilter = current.Substring(0, findFilter - 1);
						}
						else
						{
							newFilter = current.Substring(0, findFilter - 1) +
								current.Substring(colonAfterAdjust);
						}
					}
				}
			}
			return newFilter;
		}

		const String VideoFilterList = "vout-filter";
		const String AdjustFilter = "adjust";

		public bool AllowVideoAdjustments
		{
			get
			{
				String voutFilter = GetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, VideoFilterList, null);
				if(voutFilter == null)
				{
					GetConfigVariable(VideoFilterList, out voutFilter);
				}
				if(voutFilter != null)
				{
					return voutFilter.IndexOf(AdjustFilter) != -1;
				}
				else
				{
					return false;
				}
			}
			set
			{
				bool useConfig = false;
				String voutFilter = GetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, VideoFilterList, null);
				if(voutFilter == null)
				{
					using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_VLC))
					{
						GetConfigVariable(VideoFilterList, out voutFilter);
						useConfig = true;
					}
				}
				if(voutFilter != null)
				{
					String newVoutFilter = AdjustFilterString(voutFilter, AdjustFilter, value);
					if(newVoutFilter != null)
					{
						if(useConfig)
						{
							SetConfigVariable(VideoFilterList, newVoutFilter);
						}
						else
						{
							SetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, VideoFilterList, newVoutFilter);
						}
					}
				}
			}
		}

		public VlcError AddTarget(string target, ref int itemId)
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						VlcError enmErr = playlist_AddExt(vobj.SubObject, target, target, Mode.Append, 
							EndOfPlaylist, -1L, null, 0);
						if(enmErr >= VlcError.Success)
						{
							itemId = (int)enmErr;
						}
						return enmErr;
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
		}

		public VlcError AddTarget(string target, string[] options, ref int itemId)
        {
            int optionsCount = 0;
			if(options != null)
			{
				optionsCount = options.Length;
			}

			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						VlcError enmErr = playlist_AddExt(vobj.SubObject, target, target, Mode.Append,
							EndOfPlaylist, -1L, options, optionsCount);
						if(enmErr >= VlcError.Success)
						{
							itemId = (int)enmErr;
						}
						return enmErr;
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
        }

		public Size VideoSize
		{
			get
			{
				try
				{
					using(VlcPlaylistObject vpobj = new VlcPlaylistObject(this.vlcHandle))
					{
						if(vpobj.SubObject != IntPtr.Zero)
						{
							IntPtr p_input = libvlc_playlist_get_input(ref vpobj.libvlc, ref vpobj.exception);
							if(vpobj.exception.WasExceptionRaised())
							{
								this.lastErrorMessage = Marshal.PtrToStringAnsi(vpobj.exception.psz_message);
							}
							else
							{
								try
								{
									int width = libvlc_video_get_width(p_input, ref vpobj.exception);
									if(!vpobj.exception.WasExceptionRaised())
									{
										int height = libvlc_video_get_height(p_input, ref vpobj.exception);
										if(!vpobj.exception.WasExceptionRaised())
										{
											return new Size(width, height);
										}
									}
								}
								finally
								{
									libvlc_input_free(p_input);
								}
							}
						}
					}
				}
				catch(Exception ex)
				{
					this.lastErrorMessage = ex.Message;
				}
				return new Size();
			}
		}

		public VlcError Play(int itemId)
		{
			try
			{
				this.artificialLength = 0;
				this.timeScaling = 0.0d;
				using(VlcPlaylistObject vpobj = new VlcPlaylistObject(this.vlcHandle))
				{
					if(vpobj.SubObject != IntPtr.Zero)
					{
						libvlc_playlist_play(ref vpobj.libvlc, itemId, 0, IntPtr.Zero, 
							ref vpobj.exception);
						if(vpobj.exception.WasExceptionRaised())
						{
							return VlcError.Generic;
						}
						return VlcError.Success;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
			return VlcError.NoObj;
		}

        public VlcError Play()
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						return playlist_LockControl(vobj.SubObject, playlist_command.PLAYLIST_PLAY);
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
        }

        public VlcError Pause()
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						return playlist_LockControl(vobj.SubObject, playlist_command.PLAYLIST_PAUSE);
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
        }

        public VlcError Stop()
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						return playlist_LockControl(vobj.SubObject, playlist_command.PLAYLIST_STOP);
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
		}

        public VlcError PlaylistClear()
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						return playlist_Clear(vobj.SubObject);
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
        }

        public VlcError ToggleVolumeMute()
        {
			IntPtr vlc = vlc_current_object(vlcHandle);
			if(IntPtr.Zero != vlc)
			{
				try
				{
					return __aout_VolumeMute(vlc, IntPtr.Zero);
				}
				catch(Exception)
				{
				}
				finally
				{
					__vlc_object_release(vlc);
				}
			}
            return VlcError.NoObj;
        }

        public VlcError PressKey(string strKey)
        {
			int key = GetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, strKey, -666);
			if(key != -666)
			{
				return SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "key-pressed", key);
			}
			return VlcError.NoVar;
		}

		public VlcError ShowMessage(String message)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_INPUT))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						__vout_OSDMessage(vobj.SubObject, DEFAULT_CHAN, message);
						return VlcError.Success;
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
		}

		public VlcError GetConfigVariable(String name, out String value)
		{
			value = null;
			using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_VLC))
			{
				if(IntPtr.Zero == vlc.SubObject)
				{
					return VlcError.NoObj;
				}

				IntPtr p_item = config_FindConfig(vlc.SubObject, name);
				if(IntPtr.Zero == p_item)
				{
					return VlcError.NoVar;
				}

				try
				{
					module_config_t mod = (module_config_t)Marshal.PtrToStructure(p_item, typeof(module_config_t));
					switch(mod.i_type)
					{
					case CONFIG_ITEM.CONFIG_ITEM_BOOL:
						{
							bool result = (__config_GetInt(vlc.SubObject, name) == 0);
							value = result.ToString();
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_INTEGER:
						{
							int intResult = __config_GetInt(vlc.SubObject, name);
							value = intResult.ToString();
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_FLOAT:
						{
							float floatResult = __config_GetFloat(vlc.SubObject, name);
							value = floatResult.ToString();
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_STRING:
						value = __config_GetPsz(vlc.SubObject, name);
						break;
					default:
						return VlcError.BadVar;
					}
				}
				catch(Exception e)
				{
					this.lastErrorMessage = e.Message;
					return VlcError.Exception;
				}
			}
			return VlcError.Success;
		}

		public VlcError SetConfigVariable(String name, String value)
		{
			using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_VLC))
			{
				if(IntPtr.Zero == vlc.SubObject)
				{
					return VlcError.NoObj;
				}

				IntPtr p_item = config_FindConfig(vlc.SubObject, name);
				if(IntPtr.Zero == p_item)
				{
					return VlcError.NoVar;
				}
				try
				{
					module_config_t mod = (module_config_t)Marshal.PtrToStructure(p_item, typeof(module_config_t));
					switch(mod.i_type)
					{
					case CONFIG_ITEM.CONFIG_ITEM_BOOL:
						{
							bool boolResult;
							if(Boolean.TryParse(value, out boolResult))
							{
								__config_PutInt(vlc.SubObject, name, boolResult ? 1 : 0);
							}
							else
							{
								return VlcError.BadVar;
							}
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_INTEGER:
						{
							int intResult;
							if(Int32.TryParse(value, out intResult))
							{
								__config_PutInt(vlc.SubObject, name, intResult);
							}
							else
							{
								return VlcError.BadVar;
							}
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_FLOAT:
						{
							float floatResult;
							if(Single.TryParse(value, out floatResult))
							{
								__config_PutFloat(vlc.SubObject, name, floatResult);
							}
							else
							{
								return VlcError.BadVar;
							}
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_STRING:
						__config_PutPsz(vlc.SubObject, name, value);
						break;
					default:
						return VlcError.BadVar;
					}
				}
				catch(Exception e)
				{
					this.lastErrorMessage = e.Message;
					return VlcError.Exception;
				}
			}
			return VlcError.Success;
		}

        #endregion

		#region private members, properties and methods

		private static string vlcInstallDirectory = "";
		private int vlcHandle = -1;
		private Control outputWindow = null;
		private string lastErrorMessage = "";
		private int artificialLength;
		private double timeScaling;
		private GCHandle gch;
		private VarChangedCallback currentTrackCallback;
		private string previousNowPlaying = String.Empty;

        /// -------------------------------------------------------------------
        /// <summary>
        /// Method name      :   QueryVlcInstallPath
        /// Author         :   Odysee
        ///   Date         :   10.11.2006
        /// </summary>
        /// -------------------------------------------------------------------
        private static string QueryVlcInstallPath()
        {
            // open registry
            RegistryKey regkeyVlcInstallPathKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\VideoLAN\VLC");
            if(regkeyVlcInstallPathKey == null)
                return "";
            return (string)regkeyVlcInstallPathKey.GetValue("InstallDir","");
        }

		private void HookPlaylistChanges()
		{
			using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
			{
				if(vlc.SubObject != IntPtr.Zero)
				{
					this.gch = GCHandle.Alloc(this);
					this.currentTrackCallback = new VarChangedCallback(CurrentTrackChanged);

					int isSet = __var_AddCallback(vlc.SubObject, Playlist_Current,
						this.currentTrackCallback, (IntPtr)this.gch);
					//Debug.WriteLine("__var_AddCallback playlistObject = " + isSet.ToString());
				}
			}
		}

		private void UnhookPlaylistChanges()
		{
			using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
			{
				if(vlc.SubObject != IntPtr.Zero)
				{
					__var_DelCallback(vlc.SubObject, Playlist_Current,
						this.currentTrackCallback, (IntPtr)this.gch);
				}
			}
			this.gch.Free();
		}

		private static int CurrentTrackChanged(IntPtr vlc, String variable, vlc_value_t old_val,
			vlc_value_t new_val, IntPtr param)
		{
			//Debug.WriteLine("CurrentTrackChanged: " + new_val.i_int.ToString());
			GCHandle gch = (GCHandle)param;
			NativeLibVlc thisVlc = (NativeLibVlc)gch.Target;
			try
			{
				using(VlcObject vobj = new VlcObject(thisVlc.vlcHandle, ObjectType.VLC_OBJECT_INPUT))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						IntPtr resultString = IntPtr.Zero;
						input_Control(vobj.SubObject, input_query_e.INPUT_GET_INFO,
							Meta_information, Now_Playing, ref resultString);
						String nowPlaying = Marshal.PtrToStringAnsi(resultString);
						if(nowPlaying != thisVlc.previousNowPlaying)
						{
							thisVlc.previousNowPlaying = nowPlaying;
							Debug.WriteLine(String.Format("nowPlaying: {0}", nowPlaying));
							thisVlc.VlcNowPlayingChanged(nowPlaying);
						}
					}
				}
			}
			catch(Exception ex)
			{
				thisVlc.lastErrorMessage = ex.Message;
			}
			return (int)VlcError.Success;
		}

        #endregion
	}

	/* future features

    add_bool( "brightness-threshold", 0, NULL,
              THRES_TEXT, THRES_LONGTEXT, VLC_FALSE );
	 */
}