void LinphoneMainLoop()
        {
            LOG.Debug("Main loop started");
            bool isRunning = true;
            bool dequeCommand;
            while (isRunning)
            {
                regulator.WaitOne(30); // fire each 30 msec
                dequeCommand = true;
                try
                {
                    if (commandQueue.Count > 0)
                    {
                        LinphoneCommand command;
                        lock(commandQueue)
                        {
                            command = commandQueue.Peek();
                        }

                        if (command != null)
                        {
                            switch (command.Command)
                            {
                                case LinphoneCommandType.TerminateCall:
                                    var terminateCmd = command as TerminateCallCommand;

                                    if (terminateCmd != null)
                                        LinphoneAPI.linphone_core_terminate_call(LinphoneCore, terminateCmd.CallPtr);
                                    break;
                                case LinphoneCommandType.TerminateAllCalls:
                                    LinphoneAPI.linphone_core_terminate_all_calls(linphoneCore);
                                    break;
                                case LinphoneCommandType.AcceptCall:
                                    var acceptCmd = command as AcceptCallCommand;
                                    if (acceptCmd != null)
                                        LinphoneAPI.linphone_core_accept_call_with_params(linphoneCore,
                                            acceptCmd.CallPtr, acceptCmd.CallParamsPtr);
                                    break;
                                case LinphoneCommandType.DeclineCall:
                                    var declineCmd = command as DeclineCallCommand;
                                    if (declineCmd != null)
                                        LinphoneAPI.linphone_core_decline_call(linphoneCore, declineCmd.CallPtr,
                                            declineCmd.Reason);
                                    break;
                                case LinphoneCommandType.CreateCall:
                                {
                                    var createCmd = command as CreateCallCommand;
                                    if (createCmd != null)
                                    {
                                        // enable rtt
                                        LinphoneAPI.linphone_call_params_enable_realtime_text(createCmd.CallParamsPtr,
                                            createCmd.EnableRtt);
                                        MuteCall(createCmd.MuteMicrophone);
                                        MuteSpeaker(createCmd.MuteSpeaker);

                                        IntPtr callPtr = LinphoneAPI.linphone_core_invite_with_params(linphoneCore,
                                            createCmd.Callee, createCmd.CallParamsPtr);

                                        if (callPtr == IntPtr.Zero)
                                        {
                                            if (ErrorEvent != null)
                                                ErrorEvent(null, "Cannot create call to " + createCmd.Callee);
                                        }

                                        if (createCmd.CallParamsPtr != IntPtr.Zero)
                                            LinphoneAPI.linphone_call_params_destroy(createCmd.CallParamsPtr);
                                    }
                                }
                                    break;
                                case LinphoneCommandType.StopLinphone:
                                    isRunning = false;
                                    break;
                                case LinphoneCommandType.PauseCall:
                                    var pauseCmd = command as PauseCallCommand;
                                    if (pauseCmd != null)
                                    {
                                        dequeCommand = LinphoneAPI.linphone_call_media_in_progress(pauseCmd.CallPtr) == 0;
                                        if (dequeCommand)
                                            LinphoneAPI.linphone_core_pause_call(linphoneCore, pauseCmd.CallPtr);
                                    }
                                    break;
                                case LinphoneCommandType.ResumeCall:
                                    var resumeCmd = command as ResumeCallCommand;
                                    if (resumeCmd != null)
                                    {
                                        dequeCommand = LinphoneAPI.linphone_call_media_in_progress(resumeCmd.CallPtr) == 0;
                                        if (dequeCommand)
                                            LinphoneAPI.linphone_core_resume_call(linphoneCore, resumeCmd.CallPtr);
                                    }
                                    break;
                                case LinphoneCommandType.MuteCall:
                                    var muteCmd = command as MuteCallCommand;
                                    if (muteCmd != null)
                                    {
                                        IntPtr callPtr = LinphoneAPI.linphone_core_get_current_call(linphoneCore);
                                        dequeCommand = callPtr == IntPtr.Zero;
                                        if (callPtr != IntPtr.Zero)
                                            dequeCommand = LinphoneAPI.linphone_call_media_in_progress(callPtr) == 0;
                                        if (dequeCommand)
                                            LinphoneAPI.linphone_core_enable_mic(linphoneCore, muteCmd.MuteOn);
                                    }
                                    break;
                                case LinphoneCommandType.SendChatMessage:
                                {
                                    var msgCmd = command as SendChatMessageCommand;
                                    if (msgCmd != null)
                                    {
                                        IntPtr callbacks =
                                            LinphoneAPI.linphone_chat_message_get_callbacks(msgCmd.CallPtr);

                                        LinphoneAPI.linphone_chat_message_cbs_set_msg_state_changed(callbacks,
                                            Marshal.GetFunctionPointerForDelegate(message_status_changed));
                                        LinphoneAPI.linphone_chat_room_send_chat_message(msgCmd.ChatPtr, msgCmd.CallPtr);
                                        LinphoneAPI.linphone_chat_message_unref(msgCmd.CallPtr);
                                    }
                                    break;
                                }
                                case LinphoneCommandType.ToggleCamera:
                                {
                                    var msgCmd = command as ToggleCameraCommand;
                                    if (msgCmd != null)
                                    {
                                        IntPtr callPtr = LinphoneAPI.linphone_core_get_current_call(linphoneCore);

                                        if (msgCmd.CallPtr == callPtr)
                                        {
                                            LinphoneAPI.linphone_call_enable_camera(callPtr, msgCmd.EnableCamera);
                                        }
                                    }
                                    break;
                                }
                            }
                        }

                        if (dequeCommand)
                        {
                            lock (commandQueue)
                            {
                                commandQueue.Dequeue();
                            }
                        }
                    }
                    
                    LinphoneAPI.linphone_core_iterate(linphoneCore); // roll
                }
                catch (Exception ex)
                {
                    LOG.Debug("************ Linphone Main loop exception: " + ex.Message);
                }
            }

            LinphoneAPI.linphone_core_iterate(linphoneCore); // roll

            if (vtablePtr != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(vtablePtr);
                vtablePtr = IntPtr.Zero;
            }

            if (t_configPtr != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(t_configPtr);
                t_configPtr = IntPtr.Zero;
            }

            if (_cardDavStatsPtr != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(_cardDavStatsPtr);
               _cardDavStatsPtr = IntPtr.Zero;
            }

            //Marshal.FreeHGlobal(linphoneCore);
            LinphoneAPI.linphone_core_destroy(linphoneCore);
            registration_state_changed = null;
            call_state_changed = null;
            notify_received = null;
            info_received = null;
            network_reachable = null;
            message_received = null;
            message_status_changed = null;
            call_log_updated = null;
            carddav_new_contact = null;
            carddav_removed_contact = null;
            carddav_updated_contact = null;
            carddav_sync_done = null;
            carddav_auth = IntPtr.Zero;
            linphoneCore = proxy_cfg = auth_info = t_configPtr = IntPtr.Zero;
            call_stats_updated = null;
            coreLoop = null;
            _isStarting = false;
            _isStarted = false;
            _isStopping = false;
            _isStopped = true;
            LOG.Debug("Main loop exited");
            if (ServiceStopped != null)
                ServiceStopped(this, EventArgs.Empty);
         
        }
        public bool Start(bool enableLogs)
		{
			if (IsStarting)
				return false;

			if (IsStarted)
				return true;
            linphone_log_received = new LinphoneLogFuncCB(OnLinphoneLog);
		    try
		    {
		        if (enableLogs)
		        {
#if !USE_LINPHONE_LOGGING
		            LinphoneAPI.linphone_core_enable_logs(IntPtr.Zero);
                    LinphoneAPI.linphone_core_set_log_level_mask(OrtpLogLevel.ORTP_DEBUG);
#else
                    LinphoneAPI.linphone_core_enable_logs_with_cb(Marshal.GetFunctionPointerForDelegate(linphone_log_received));
#endif
		        }
		        else
		            LinphoneAPI.linphone_core_disable_logs();
		    }
		    catch (Exception ex)
		    {
		        LOG.Debug(ex.ToString());
		    }

            registration_state_changed = new LinphoneCoreRegistrationStateChangedCb(OnRegistrationChanged);
			call_state_changed = new LinphoneCoreCallStateChangedCb(OnCallStateChanged);
			global_state_changed = new LinphoneCoreGlobalStateChangedCb(OnGlobalStateChanged);
			notify_received = new LinphoneCoreNotifyReceivedCb(OnNotifyEventReceived);
            call_stats_updated = new LinphoneCoreCallStatsUpdatedCb(OnCallStatsUpdated);
		    is_composing_received = new LinphoneCoreIsComposingReceivedCb(OnIsComposingReceived);
            message_received = new LinphoneCoreMessageReceivedCb(OnMessageReceived);
            message_status_changed = new LinphoneChatMessageCbsMsgStateChangedCb(OnMessageStatusChanged);
            call_log_updated = new LinphoneCoreCallLogUpdatedCb(OnCallLogUpdated);
            info_received = new LinphoneCoreInfoReceivedCb(OnInfoEventReceived);
            network_reachable = new LinphoneCoreNetworkReachableCb(OnNetworkReachable);
            // cardDAV stuff
            carddav_new_contact = new LinphoneFriendListContactCreatedCb(OnCardDAVContactCreated);
            carddav_removed_contact = new LinphoneFriendListContactDeletedCb(OnCardDAVContactRemoved);
            carddav_updated_contact = new LinphoneFriendListContactUpdatedCb(OnCardDAVContactUpdated);
            carddav_sync_done = new LinphoneFriendListSyncStateChangedCb(OnCardDAVSyncChanged);
			vtable = new LinphoneCoreVTable()
			{
				global_state_changed = Marshal.GetFunctionPointerForDelegate(global_state_changed),
				registration_state_changed = Marshal.GetFunctionPointerForDelegate(registration_state_changed),
				call_state_changed = Marshal.GetFunctionPointerForDelegate(call_state_changed),
				notify_presence_received = IntPtr.Zero,
				new_subscription_requested = IntPtr.Zero,
				auth_info_requested = IntPtr.Zero,
                call_log_updated = Marshal.GetFunctionPointerForDelegate(call_log_updated),
                message_received = Marshal.GetFunctionPointerForDelegate(message_received),
				is_composing_received = Marshal.GetFunctionPointerForDelegate(is_composing_received),
				dtmf_received = IntPtr.Zero,
				refer_received = IntPtr.Zero,
				call_encryption_changed = IntPtr.Zero,
				transfer_state_changed = IntPtr.Zero,
				buddy_info_updated = IntPtr.Zero,
                call_stats_updated = Marshal.GetFunctionPointerForDelegate(call_stats_updated),
				info_received = Marshal.GetFunctionPointerForDelegate(info_received),
                network_reachable = Marshal.GetFunctionPointerForDelegate(network_reachable),
				subscription_state_changed = IntPtr.Zero,
				notify_received = Marshal.GetFunctionPointerForDelegate(notify_received),
				publish_state_changed = IntPtr.Zero,
				configuring_status = IntPtr.Zero,
				display_status = IntPtr.Zero,
				display_message = IntPtr.Zero,
				display_warning = IntPtr.Zero,
				display_url = IntPtr.Zero,
				show = IntPtr.Zero,
                text_received = IntPtr.Zero,
			};
			vtablePtr = Marshal.AllocHGlobal(Marshal.SizeOf(vtable));
			Marshal.StructureToPtr(vtable, vtablePtr, false);

            string configPath = manager.BuildStoragePath("linphonerc.cfg");

            linphoneCore = LinphoneAPI.linphone_core_new(vtablePtr, configPath, null, IntPtr.Zero);
			if (linphoneCore != IntPtr.Zero)
			{
                LinphoneAPI.libmsopenh264_init(LinphoneAPI.linphone_core_get_ms_factory(linphoneCore));
                // Liz E. - this is set in the account settings now
                //LinphoneAPI.linphone_core_set_video_preset(linphoneCore, "high-fps");
				LinphoneAPI.linphone_core_enable_video_capture(linphoneCore, true);
				LinphoneAPI.linphone_core_enable_video_display(linphoneCore, true);
				LinphoneAPI.linphone_core_enable_video_preview(linphoneCore, false);
				LinphoneAPI.linphone_core_set_native_preview_window_id(linphoneCore, -1);

			    LinphoneAPI.linphone_core_set_upload_bandwidth(linphoneCore, 1500);
                LinphoneAPI.linphone_core_set_download_bandwidth(linphoneCore, 1500);

                string codeBase = Assembly.GetExecutingAssembly().CodeBase;
                UriBuilder uri = new UriBuilder(codeBase);
                string path = Uri.UnescapeDataString(uri.Path);
			    string directory = String.Empty;
			    if (!string.IsNullOrEmpty(path))
			    {
			        directory = Path.GetDirectoryName(path);
			    }

			    if (!string.IsNullOrEmpty(directory ))
                {
                    var rootCAPath = Path.Combine(directory, "rootca.pem");
                    LinphoneAPI.linphone_core_set_root_ca(linphoneCore, rootCAPath);

                    var noVideoImagePath = Path.Combine(directory, "images");
                    noVideoImagePath = Path.Combine(noVideoImagePath, "camera_disabled.jpg");
                    LinphoneAPI.ms_static_image_set_default_image(noVideoImagePath);
                }

			    LinphoneAPI.linphone_core_verify_server_cn(linphoneCore, true);
                LinphoneAPI.linphone_core_verify_server_certificates(linphoneCore, true);

			    // load installed codecs
			    LoadAudioCodecs();
                LoadVideoCodecs();

			    IntPtr defProxyCfg = LinphoneAPI.linphone_core_get_default_proxy_config(linphoneCore);
			    if (defProxyCfg != IntPtr.Zero)
			    {
			        proxy_cfg = defProxyCfg;
                    LinphoneAPI.linphone_proxy_config_edit(proxy_cfg);
                    LinphoneAPI.linphone_proxy_config_enable_register(proxy_cfg, false);
                    LinphoneAPI.linphone_proxy_config_done(proxy_cfg);
			    }

                ClearProxyInformation();

                IntPtr coreConfig = LinphoneAPI.linphone_core_get_config(linphoneCore);
                if (coreConfig != IntPtr.Zero)
                {
                    LinphoneAPI.lp_config_set_int(coreConfig, "sip", "tcp_tls_keepalive", 1);
                    LinphoneAPI.lp_config_set_int(coreConfig, "sip", "keepalive_period", 90000);
                    LinphoneAPI.lp_config_set_int(coreConfig, "sip", "auto_net_state_mon", 1); // enable linphone network monitoring

                    // store contacts as vcard
                    LinphoneAPI.lp_config_set_int(coreConfig, "misc", "store_friends", 1);

                    // VATRP-2130, prevent SIP spam
                    LinphoneAPI.lp_config_set_int(coreConfig, "sip", "sip_random_port", 1); // force to set random ports
                }

			    LinphoneAPI.linphone_core_enable_keep_alive(linphoneCore, false);

				coreLoop = new Thread(LinphoneMainLoop) {IsBackground = true};
				coreLoop.Start();
			    _isStarting = false;
                _isStopping = false;
                _isStopped = false;
				_isStarted = true;
			}
            if (ServiceStarted != null)
                ServiceStarted(this, EventArgs.Empty);
			return _isStarted;
		}