public MediaServerCore(string friendlyName)
        {
            if (serverCore != null) throw new Exception("Only a single MediaServerCode instance is allowed");
            serverCore = this;

            OpenSource.Utilities.EventLogger.SetLog("Media Server", "MediaServerCode", System.Windows.Forms.Application.ProductVersion.ToString());

            DeviceInfo info = new DeviceInfo();
            info.AllowRemoteContentManagement = true;
            info.FriendlyName = friendlyName;
            info.Manufacturer = "OpenSource";
            info.ManufacturerURL = "";
            info.ModelName = "Media Server";
            info.ModelDescription = "Provides content through UPnP ContentDirectory service";
            info.ModelURL = "";
            info.ModelNumber = "0.765";
            info.LocalRootDirectory = "";
            Tags T = Tags.GetInstance();
            info.SearchCapabilities = "dc:title,dc:creator,upnp:class,upnp:album,res@protocolInfo,res@size,res@bitrate";
            info.SortCapabilities = "dc:title,dc:creator,upnp:class,upnp:album";
            info.EnableSearch = true;
            info.CacheTime = MediaServerCore.CacheTime;
            info.CustomUDN = MediaServerCore.CustomUDN;
            info.INMPR03 = MediaServerCore.INMPR;

            // encode in UTF16 instead of UTF8
            MediaObject.ENCODE_UTF8 = false;

            mediaServer = new MediaServerDevice(info, null, true, "http-get:*:*:*", "");
            mediaServer.OnStatsChanged += new MediaServerDevice.Delegate_MediaServerHandler(StatsChangedChangedSink);
            mediaServer.OnHttpTransfersChanged += new MediaServerDevice.Delegate_MediaServerHandler(HttpTransfersChangedSink);
            mediaServer.OnFileNotMapped = new MediaServerDevice.Delegate_FileNotMappedHandler(this.Handle_OnRequestUnmappedFile);
            mediaServer.OnRequestAddBranch = new MediaServerDevice.Delegate_AddBranch(this.Handle_OnRequestAddBranch);
            mediaServer.OnRequestRemoveBranch = new MediaServerDevice.Delegate_RemoveBranch(this.Handle_OnRequestRemoveBranch);
            mediaServer.OnRequestChangeMetadata = new MediaServerDevice.Delegate_ChangeMetadata(this.Handle_OnRequestChangeMetadata);
            mediaServer.OnRequestSaveBinary = new MediaServerDevice.Delegate_ModifyBinary (this.Handle_OnRequestSaveBinary);
            mediaServer.OnRequestDeleteBinary = new MediaServerDevice.Delegate_ModifyBinary(this.Handle_OnRequestDeleteBinary);

            this.ResetCoreRoot();

            this.m_DeviceWatcher = new UPnPDeviceWatcher(mediaServer._Device);
            this.m_DeviceWatcher.OnSniff += new OpenSource.UPnP.UPnPDeviceWatcher.SniffHandler(this.Sink_DeviceWatcherSniff);

            this.mediaServer.Start();
            m_Paused = false;
        }
        /// <summary>
        /// Initializes static members of the <see cref="UPnP"/> class.
        /// </summary>
        static UPnP()
        {
            _di = new DeviceInfo
                {
                    AllowRemoteContentManagement = true,
                    FriendlyName                 = Signature.Software + " on " + Environment.MachineName,
                    Manufacturer                 = Signature.Developer,
                    ManufacturerURL              = "http://lab.rolisoft.net/tvshowtracker.html",
                    ModelName                    = "RS TV Show Tracker",
                    ModelDescription             = "Provides access to TV shows tracked by " + Signature.Software + " on " + Environment.MachineName,
                    ModelURL                     = "http://lab.rolisoft.net/tvshowtracker.html",
                    ModelNumber                  = Signature.Version,
                    LocalRootDirectory           = "",
                    SearchCapabilities           = "dc:title,dc:creator,upnp:class,upnp:album,res@protocolInfo,res@size,res@bitrate",
                    SortCapabilities             = "dc:title,dc:creator,upnp:class,upnp:album",
                    EnableSearch                 = true,
                    CacheTime                    = 1800,
                    CustomUDN                    = "",
                    INMPR03                      = true
                };

            MediaObject.ENCODE_UTF8 = false;
        }
        /// <summary>
        /// Constructor instantiates a properly behaving UPnP-AV
        /// MediaServer device that has a root container.
        /// </summary>
        /// <param name="info">general information about the MediaServer like manufacturer info</param>
        /// <param name="isRootDevice">true if the MediaServer has no parent UPnP device</param>
        /// <param name="enableHttpContentServing">
        /// True, if the MediaServer should support the <see cref="MediaResource.AUTOMAPFILE"/>
        /// convention.
        /// </param>
        /// <param name="initialSourceProtocolInfoSet">
        /// Comma separated value list of protocolInfo strings for this MediaServer as a source.
        /// "HTTP-GET:*:*:*" is also allowed, indicating that any HTTP-GET resource is supported on the server.
        /// Generally, this value should be true unless the application logic for the
        /// MediaServer will always create resource objects with fully pathed URIs that are
        /// accessible from the UPNP network.
        /// </param>
        /// <param name="initialSinkProtocolInfoSet">
        /// Comma separated value list of protocolInfo strings for this MediaServer as a sink.
        /// This should generally be blank although it may be possible to eventually 
        /// migrate this implementation to behave both as a MediaRenderer and a MediaServer,
        /// although there may be subtleties that make such a device impossible.
        /// </param>
        public MediaServerDevice(
			DeviceInfo info,
			UPnPDevice parent,
			bool enableHttpContentServing,
			string initialSourceProtocolInfoSet,
			string initialSinkProtocolInfoSet
			)
        {
            // enable HTTP webserving of HTTP-GET resource/content?
            this.EnableHttp = enableHttpContentServing;

            // Wire up the delegate and weak event used when an HTTP transfer should be
            // removed from the server's list.
            // When an HTTP transfer completes it's progress info still needs to be
            // available for GetTransferProgress action for at least 30 seconds.
            // LifeTimeMonitor will delay the removal for such a time, and then
            // execute the delegate.
            this.LTMDelegate = new LifeTimeMonitor.LifeTimeHandler(this.Sink_OnExpired);
            this.m_LFT.OnExpired += LTMDelegate;

            // Create the UPnP device object - no servics are attached yet
            if (parent == null)
            {
                this.Device = UPnPDevice.CreateRootDevice(info.CacheTime, 1.0, info.LocalRootDirectory);
                if(info.CustomUDN!="")
                {
                    this.Device.UniqueDeviceName = info.CustomUDN;
                }
            }
            else
            {
                Guid udn = System.Guid.NewGuid();
                this.Device = UPnPDevice.CreateEmbeddedDevice(1.0, udn.ToString());
                parent.AddDevice(this.Device);
            }

            // transfer basic info about the device, like serial #, manufacturer, etc.
            this.Device.HasPresentation = false;
            this.Device.StandardDeviceType = "MediaServer";

            this.Device.FriendlyName		= info.FriendlyName;
            this.Device.Manufacturer		= info.Manufacturer;
            this.Device.ManufacturerURL	= info.ManufacturerURL;
            this.Device.ModelName			= info.ModelName;
            this.Device.ModelDescription	= info.ModelDescription;
            if (info.ModelURL != null)
            {
                try
                {
                    this.Device.ModelURL			= new Uri(info.ModelURL);
                }
                catch
                {
                    this.Device.ModelURL = null;
                }
            }
            this.Device.ModelNumber		= info.ModelNumber;

            if (info.INMPR03)
            {
                this.Device.AddCustomFieldInDescription("INMPR03", "1.0", "");
            }

            this.ConnectionManager = new DvConnectionManager();
            this.ContentDirectory = new DvContentDirectory();

            // Set periodic behavior for the moderated state variables.
            // Only state variables that do not overwrite a pending
            // value need an accumulator.
            //
            this.ContentDirectory.ModerationDuration_SystemUpdateID = 2;
            this.ContentDirectory.ModerationDuration_ContainerUpdateIDs = 2;
            this.ContentDirectory.Accumulator_ContainerUpdateIDs = new Accumulator_ContainerUpdateIDs();

            // Determine whether the application logic actually
            // wants control points to have access to content management
            // related methods. If not, then remove those actions.
            if (info.AllowRemoteContentManagement==false)
            {
                this.ContentDirectory.RemoveAction_CreateObject();
                this.ContentDirectory.RemoveAction_CreateReference();
                this.ContentDirectory.RemoveAction_DeleteResource();
                this.ContentDirectory.RemoveAction_DestroyObject();
                this.ContentDirectory.RemoveAction_ImportResource();
                this.ContentDirectory.RemoveAction_UpdateObject();
                this.ContentDirectory.RemoveAction_ExportResource();
                this.ContentDirectory.RemoveAction_GetTransferProgress();
                this.ContentDirectory.RemoveAction_StopTransferResource();
            }

            if (info.EnablePrepareForConnection==false)
            {
                this.ConnectionManager.RemoveAction_PrepareForConnection();
            }

            if (info.EnableConnectionComplete==false)
            {
                this.ConnectionManager.RemoveAction_ConnectionComplete();
            }

            if (
                (info.EnablePrepareForConnection==false) &&
                (info.EnableConnectionComplete==false)
                )
            {
                ProtocolInfoString protInfo = new ProtocolInfoString("http-get:*:*:*");
                DvConnectionManager.Enum_A_ARG_TYPE_ConnectionStatus status = DvConnectionManager.Enum_A_ARG_TYPE_ConnectionStatus.UNKNOWN;

                Connection newConnection = new Connection(GetConnectionID(), -1, -1, -1, protInfo, "/", OpenSource.UPnP.AV.DvConnectionManager.Enum_A_ARG_TYPE_Direction.OUTPUT, OpenSource.UPnP.AV.DvConnectionManager.Enum_A_ARG_TYPE_ConnectionStatus.UNKNOWN);
                this.AddConnection(newConnection);
            }

            if (info.EnableSearch == false)
            {
                this.ContentDirectory.RemoveAction_Search();
            }

            this.m_SearchCapabilities = info.SearchCapabilities;
            this.m_SortCapabilities = info.SortCapabilities;

            //AVTransport and RendererControl not used...yet.
            //this.m_AVTransports = new ArrayList();
            //this.m_RenderingControls = new ArrayList();

            // Set values for each state variable
            //
            if (this.ConnectionManager.Evented_CurrentConnectionIDs == null)
            {
                this.ConnectionManager.Evented_CurrentConnectionIDs = "";
            }

            // this state variable does not exist until UPNP-AV 1.1
            //this.ConnectionManager.Evented_PhysicalConnections = "";

            // have the device advertise the protocolInfo strings for
            // this media server
            this.UpdateProtocolInfoSet(false, initialSinkProtocolInfoSet);
            this.UpdateProtocolInfoSet(true, initialSourceProtocolInfoSet);

            // initialize state varaibles
            this.ContentDirectory.Evented_ContainerUpdateIDs = "";
            this.ContentDirectory.Evented_SystemUpdateID = 0;
            this.ContentDirectory.Evented_TransferIDs = "";

            // Wire up the ContentDirectory and ConnectionManager actions to actual methods
            // in this class that will actually do the work.
            this.ConnectionManager.External_ConnectionComplete			= new DvConnectionManager.Delegate_ConnectionComplete(this.SinkCm_ConnectionComplete);
            this.ConnectionManager.External_GetCurrentConnectionIDs		= new DvConnectionManager.Delegate_GetCurrentConnectionIDs(this.SinkCm_GetCurrentConnectionIDs);
            this.ConnectionManager.External_GetCurrentConnectionInfo	= new DvConnectionManager.Delegate_GetCurrentConnectionInfo(this.SinkCm_GetCurrentConnectionInfo);
            this.ConnectionManager.External_GetProtocolInfo				= new DvConnectionManager.Delegate_GetProtocolInfo(this.SinkCm_GetProtocolInfo);
            this.ConnectionManager.External_PrepareForConnection		= new DvConnectionManager.Delegate_PrepareForConnection(this.SinkCm_PrepareForConnection);

            this.ContentDirectory.External_Browse					= new DvContentDirectory.Delegate_Browse(this.SinkCd_Browse);
            this.ContentDirectory.External_CreateObject				= new DvContentDirectory.Delegate_CreateObject(this.SinkCd_CreateObject);
            this.ContentDirectory.External_CreateReference			= new DvContentDirectory.Delegate_CreateReference(this.SinkCd_CreateReference);
            this.ContentDirectory.External_DeleteResource			= new DvContentDirectory.Delegate_DeleteResource(this.SinkCd_DeleteResource);
            this.ContentDirectory.External_DestroyObject			= new DvContentDirectory.Delegate_DestroyObject(this.SinkCd_DestroyObject);
            this.ContentDirectory.External_ExportResource			= new DvContentDirectory.Delegate_ExportResource(this.SinkCd_ExportResource);
            this.ContentDirectory.External_GetSearchCapabilities	= new DvContentDirectory.Delegate_GetSearchCapabilities(this.SinkCd_GetSearchCapabilities);
            this.ContentDirectory.External_GetSortCapabilities		= new DvContentDirectory.Delegate_GetSortCapabilities(this.SinkCd_GetSortCapabilities);
            this.ContentDirectory.External_GetSystemUpdateID		= new DvContentDirectory.Delegate_GetSystemUpdateID(this.SinkCd_GetSystemUpdateID);
            this.ContentDirectory.External_GetTransferProgress		= new DvContentDirectory.Delegate_GetTransferProgress(this.SinkCd_GetTransferProgress);
            this.ContentDirectory.External_ImportResource			= new DvContentDirectory.Delegate_ImportResource(this.SinkCd_ImportResource);
            this.ContentDirectory.External_Search					= new DvContentDirectory.Delegate_Search(this.SinkCd_Search);
            this.ContentDirectory.External_StopTransferResource		= new DvContentDirectory.Delegate_StopTransferResource(this.SinkCd_StopTransferResource);
            this.ContentDirectory.External_UpdateObject				= new DvContentDirectory.Delegate_UpdateObject(this.SinkCd_UpdateObject);

            // add the services to the device - voila, it's now a useful device
            this.Device.AddService(this.ConnectionManager);
            this.Device.AddService(this.ContentDirectory);

            // set up a virtual directory for local webserving - we'll always
            // have this here, even if HTTP webserving is not enabled because
            // it really doesn't hurt to have the virtual directory.
            Interlocked.Increment(ref VirtualDirCounter);
            m_VirtualDirName = "MediaServerContent_" + VirtualDirCounter.ToString();
            this.Device.AddVirtualDirectory(m_VirtualDirName, new UPnPDevice.VirtualDirectoryHandler(this.WebServer_OnHeaderReceiveSink), new UPnPDevice.VirtualDirectoryHandler(this.WebServer_OnPacketReceiveSink));

            // create the root container for this media server's content hierarchy.
            // Prevent control points from modifying this root container or
            // creating new objects in the root.
            // Also need to wire up internally visible events so that the MediaServer
            // will properly event changes in the content hierarchy.
            MediaBuilder.container rootInfo = new MediaBuilder.container("Root");
            rootInfo.Searchable = true;
            rootInfo.IsRestricted = true;
            this.m_Root = (DvRootContainer) DvMediaBuilder.CreateRoot(rootInfo);
            this.m_Root.OnContainerChanged += new DvRootContainer.Delegate_OnContainerChanged(this.Sink_ContainerChanged);

            // At this point the device is ready to go... simply call the Start()
            // method to have the device advertise itself.
        }