NativeLibVlc.cs

From VideoLAN Wiki
Revision as of 01:09, 1 January 2007 by Tappen (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
/*****************************************************************************
 * 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.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 */
		}

        #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 playlist_t_begin
		{
			public Int32 i_index;
			public Int32 i_enabled;
			public Int32 i_size;
		}

		#endregion

		#region vlc api interop
        [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);

		const int AOUT_VOLUME_MAX = 1024;
		const int VOLUME_MAX = 200;

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

		const int DEFAULT_CHAN = 1;

		[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
		static extern void __vout_OSDMessage(IntPtr p_input, int i_channel, String message);

		#endregion

		public NativeLibVlc()
        {
            vlcInstallDirectory = QueryVlcInstallPath();
        }

        #region IDisposable
        public void Dispose()
        {
            if(vlcHandle != -1)
            {
                try
                {
					VideoOutput = null;
					VLC_CleanUp(vlcHandle);
                    VLC_Destroy(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 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 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
				{
					using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
					{
						if(vobj.SubObject != IntPtr.Zero)
						{
							playlist_t_begin playlist = (playlist_t_begin)Marshal.PtrToStructure(vobj.SubObject, typeof(playlist_t_begin));
							return playlist.i_index;
						}
					}
				}
				catch(Exception ex)
				{
					this.lastErrorMessage = ex.Message;
					return -1;
				}
				return -1;
			}
		}

		public int PlaylistCount
		{
			get
			{
				try
				{
					using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
					{
						if(vobj.SubObject != IntPtr.Zero)
						{
							playlist_t_begin playlist = (playlist_t_begin)Marshal.PtrToStructure(vobj.SubObject, typeof(playlist_t_begin));
							return playlist.i_size;
						}
					}
				}
				catch(Exception ex)
				{
					this.lastErrorMessage = ex.Message;
					return -1;
				}
				return -1;
			}
		}

        #endregion

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

            // try init
            try
            {
                // create instance
                vlcHandle = VLC_Create();

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

                string[] initOptions = { "vlc",
                                            ":no-one-instance",
                                            ":no-loop",
                                            ":no-drop-late-frames",
                                            ":disable-screensaver",
											":vout=vout_directx" };
                if(vlcInstallDirectory.Length > 0)
                    initOptions[0] = vlcInstallDirectory + @"\vlc";

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

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

            return true;
        }

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

		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,
		}

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

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

		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;
				}
				else
				{
					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.NoVar;
						}
					}
					catch(Exception e)
					{
						this.lastErrorMessage = e.Message;
						return VlcError.Exception;
					}
				}
			}
			return VlcError.Success;
		}

        #endregion

		#region private members, properties and methods

		private int vlcHandle = -1;
		private Control outputWindow = null;
		private string vlcInstallDirectory = "";
		private string lastErrorMessage = "";
		private int artificialLength;
		private double timeScaling;

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

	/* future features
    add_float_with_range( "contrast", 1.0, 0.0, 2.0, NULL,
                          CONT_TEXT, CONT_LONGTEXT, VLC_FALSE );
    add_float_with_range( "brightness", 1.0, 0.0, 2.0, NULL,
                           LUM_TEXT, LUM_LONGTEXT, VLC_FALSE );
    add_integer_with_range( "hue", 0, 0, 360, NULL,
                            HUE_TEXT, HUE_LONGTEXT, VLC_FALSE );
    add_float_with_range( "saturation", 1.0, 0.0, 3.0, NULL,
                          SAT_TEXT, SAT_LONGTEXT, VLC_FALSE );
    add_float_with_range( "gamma", 1.0, 0.01, 10.0, NULL,
                          GAMMA_TEXT, GAMMA_LONGTEXT, VLC_FALSE );

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