From 9a4f3aec859f89562b33eed861a407a84d381780 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 24 Mar 2026 20:42:51 +0000 Subject: [PATCH] WebRtcVoiceServiceModule should not be a region module. Move the region logic on it to proper region module (early dirty code) --- .../WebRtcVoice/WebRtcVoiceServerConnector.cs | 66 ++-- .../WebRtcVoiceRegionModule.cs | 330 +++++++++++++++--- .../WebRtcVoiceServiceModule.cs | 110 +----- 3 files changed, 316 insertions(+), 190 deletions(-) diff --git a/OpenSim/Addons/os-webrtc-janus/WebRtcVoice/WebRtcVoiceServerConnector.cs b/OpenSim/Addons/os-webrtc-janus/WebRtcVoice/WebRtcVoiceServerConnector.cs index 4af87d07da..517872409c 100644 --- a/OpenSim/Addons/os-webrtc-janus/WebRtcVoice/WebRtcVoiceServerConnector.cs +++ b/OpenSim/Addons/os-webrtc-janus/WebRtcVoice/WebRtcVoiceServerConnector.cs @@ -74,17 +74,16 @@ namespace osWebRtcVoice string localServiceModule = moduleConfig.GetString("LocalServiceModule", "WebRtcVoiceServiceModule.dll:WebRtcVoiceServiceModule"); m_log.Debug($"{LogHeader} loading {localServiceModule}"); - m_WebRtcVoiceService = ServerUtils.LoadPlugin(localServiceModule, []); + m_WebRtcVoiceService = ServerUtils.LoadPlugin(localServiceModule, [pConfig]); // The WebRtcVoiceServiceModule is both an IWebRtcVoiceService and a ISharedRegionModule // so we can initialize it as if it was the region module. - if (m_WebRtcVoiceService is not ISharedRegionModule sharedModule) + if (m_WebRtcVoiceService is not IWebRtcVoiceService voiceservice) { m_log.ErrorFormat("{0} local service module does not implement ISharedRegionModule", LogHeader); m_Enabled = false; return; } - sharedModule.Initialise(pConfig); // Now that we have someone to handle the requests, we can set up the handlers pServer.AddJsonRPCHandler("provision_voice_account_request", Handle_ProvisionVoiceAccountRequest); @@ -99,11 +98,17 @@ namespace osWebRtcVoice m_log.Debug($"{LogHeader} Handle_ProvisionVoiceAccountRequest"); if (m_MessageDetails) m_log.DebugFormat("{0} PVAR: req={1}", LogHeader, pJson.ToString()); - if (pJson.ContainsKey("params") && pJson["params"] is OSDMap paramsMap) + if (pJson.TryGetOSDMap("params", out OSDMap paramsMap)) { - OSDMap request = paramsMap.ContainsKey("request") ? paramsMap["request"] as OSDMap : null; - UUID userID = paramsMap.ContainsKey("userID") ? paramsMap["userID"].AsUUID() : UUID.Zero; - UUID sceneID = paramsMap.ContainsKey("scene") ? paramsMap["scene"].AsUUID() : UUID.Zero; + OSDMap request = paramsMap.TryGetOSDMap("request", out OSDMap treq) ? treq : null; + if(request is null) + { + m_log.Error($"{LogHeader} PVAR: invalid parameter 'request'"); + return false; + } + + UUID userID = paramsMap.TryGetUUID("userID", out UUID tuserid) ? tuserid : UUID.Zero; + UUID sceneID = paramsMap.TryGetUUID("scene", out UUID tsceneid) ? tsceneid : UUID.Zero; try { @@ -112,58 +117,69 @@ namespace osWebRtcVoice m_log.ErrorFormat("{0} PVAR: no local service", LogHeader); return false; } + OSDMap resp = m_WebRtcVoiceService.ProvisionVoiceAccountRequest(request, userID, sceneID); - pResponse = new JsonRpcResponse(); - pResponse.Result = resp; + pResponse = new JsonRpcResponse + { + Result = resp + }; + if (m_MessageDetails) m_log.DebugFormat("{0} PVAR: resp={1}", LogHeader, resp.ToString()); ret = true; } catch (Exception e) { - m_log.ErrorFormat("{0} PVAR: exception {1}", LogHeader, e); + m_log.Error($"{LogHeader} PVAR: exception ", e); } } else { - m_log.ErrorFormat("{0} PVAR: missing parameters", LogHeader); + m_log.Error($"{LogHeader} PVAR: missing parameters"); } return ret; } private bool Handle_VoiceSignalingRequest(OSDMap pJson, ref JsonRpcResponse pResponse) { - bool ret = false; - if (pJson.ContainsKey("params") && pJson["params"] is OSDMap paramsMap) + if (pJson.TryGetOSDMap("params", out OSDMap paramsMap)) { - m_log.DebugFormat("{0} Handle_VoiceSignalingRequest", LogHeader); - if (m_MessageDetails) m_log.DebugFormat("{0} VSR: req={1}", LogHeader, paramsMap.ToString()); + m_log.Debug($"{LogHeader} Handle_VoiceSignalingRequest"); + if (m_MessageDetails) m_log.Debug($"{LogHeader} VSR: req={paramsMap}"); - OSDMap request = paramsMap.ContainsKey("request") ? paramsMap["request"] as OSDMap : null; - UUID userID = paramsMap.ContainsKey("userID") ? paramsMap["userID"].AsUUID() : UUID.Zero; - UUID sceneID = paramsMap.ContainsKey("scene") ? paramsMap["scene"].AsUUID() : UUID.Zero; + OSDMap request = paramsMap.TryGetOSDMap("request", out OSDMap treq) ? treq : null; + if(request is null) + { + m_log.Error($"{LogHeader} VSR: null parameter 'request'"); + return false; + } + + UUID userID = paramsMap.TryGetUUID("userID", out UUID tuserid) ? tuserid : UUID.Zero; + UUID sceneID = paramsMap.TryGetUUID("scene", out UUID tsceneid) ? tsceneid : UUID.Zero; try { OSDMap resp = m_WebRtcVoiceService.VoiceSignalingRequest(request, userID, sceneID); - pResponse = new JsonRpcResponse(); - pResponse.Result = resp; - if (m_MessageDetails) m_log.DebugFormat("{0} VSR: resp={1}", LogHeader, resp.ToString()); + pResponse = new JsonRpcResponse + { + Result = resp + }; + if (m_MessageDetails) m_log.Debug($"{LogHeader} VSR: resp={resp}"); - ret = true; + return true; } catch (Exception e) { - m_log.ErrorFormat("{0} VSR: exception {1}", LogHeader, e); + m_log.Error($"{LogHeader} VSR: exception ", e); } } else { - m_log.ErrorFormat("{0} VSR: missing parameters", LogHeader); + m_log.Error($"{LogHeader} VSR: missing parameters"); } - return ret; + return false; } } } diff --git a/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceRegionModule/WebRtcVoiceRegionModule.cs b/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceRegionModule/WebRtcVoiceRegionModule.cs index 68507dc790..2269c678ea 100644 --- a/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceRegionModule/WebRtcVoiceRegionModule.cs +++ b/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceRegionModule/WebRtcVoiceRegionModule.cs @@ -25,26 +25,26 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -using System; -using System.IO; -using System.Net; -using System.Reflection; - +using log4net; using Mono.Addins; - +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using OpenSim.Server.Base; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Threading.Tasks; using Caps = OpenSim.Framework.Capabilities.Caps; - -using OpenMetaverse; -using OpenMetaverse.StructuredData; using OSDMap = OpenMetaverse.StructuredData.OSDMap; -using log4net; -using Nini.Config; - [assembly: Addin("WebRtcVoiceRegionModule", "1.0")] [assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] @@ -66,7 +66,7 @@ namespace osWebRtcVoice public class WebRtcVoiceRegionModule : ISharedRegionModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private static readonly string logHeader = "[REGION WEBRTC VOICE]"; + private static readonly string LogHeader = "[REGION WEBRTC VOICE]"; private static byte[] llsdUndefAnswerBytes = Util.UTF8.GetBytes(""); private bool _MessageDetails = false; @@ -76,6 +76,9 @@ namespace osWebRtcVoice private IConfig m_Config; + private IWebRtcVoiceService m_spatialVoiceService; + private IWebRtcVoiceService m_nonSpatialVoiceService; + // ISharedRegionModule.Initialize public void Initialise(IConfigSource config) { @@ -85,9 +88,53 @@ namespace osWebRtcVoice m_Enabled = m_Config.GetBoolean("Enabled", false); if (m_Enabled) { - _MessageDetails = m_Config.GetBoolean("MessageDetails", false); + // Get the DLLs for the two voice services + // TODO: spacial/nonspacial names are wrong + // spacial here means service for region parcels, that can be spacial or not + // non spacial means for other uses like IMs, that just happen to be non spacial + // in fact this needs more consideration than just this 2 options - m_log.Info($"{logHeader}: enabled"); + string spatialDllName = m_Config.GetString("SpatialVoiceService", string.Empty); + string nonSpatialDllName = m_Config.GetString("NonSpatialVoiceService", string.Empty); + if (string.IsNullOrEmpty(spatialDllName) && string.IsNullOrEmpty(nonSpatialDllName)) + { + m_log.Error($"{LogHeader} No VoiceService specified in configuration"); + m_Enabled = false; + return; + } + + // Default non-spatial to spatial if not specified + if (string.IsNullOrEmpty(nonSpatialDllName)) + { + m_log.Debug($"{LogHeader} nonSpatialDllName not specified. Defaulting to spatialDllName"); + nonSpatialDllName = spatialDllName; + } + + // Load the two voice services + m_log.Debug($"{LogHeader} Loading SpatialVoiceService from {spatialDllName}"); + m_spatialVoiceService = ServerUtils.LoadPlugin(spatialDllName, [config]); + if (m_spatialVoiceService is null) + { + m_log.Error($"{LogHeader} Could not load SpatialVoiceService from {spatialDllName}, module disabled"); + m_Enabled = false; + return; + } + + m_log.Debug($"{LogHeader} Loading NonSpatialVoiceService from {nonSpatialDllName}"); + if (spatialDllName != nonSpatialDllName) + { + m_nonSpatialVoiceService = ServerUtils.LoadPlugin(nonSpatialDllName, [ m_Config ]); + if (m_nonSpatialVoiceService is null) + { + m_log.Error($"{LogHeader} Could not load NonSpatialVoiceService from {nonSpatialDllName}"); + m_Enabled = false; + } + } + + if (m_Enabled) + { + m_log.Info($"{LogHeader} WebRtcVoiceService enabled"); + } } } } @@ -140,6 +187,68 @@ namespace osWebRtcVoice get { return null; } } + // ===================================================================== + // Thought about doing this but currently relying on the voice service + // event ("hangup") to remove the viewer session. +/* + private void Event_OnRemovePresence(UUID pAgentID) + { + // When a presence is removed, remove the viewer sessions for that agent + IEnumerable> vSessions; + if (VoiceViewerSession.TryGetViewerSessionByAgentId(pAgentID, out vSessions)) + { + foreach(KeyValuePair v in vSessions) + { + m_log.DebugFormat("{0} Event_OnRemovePresence: removing viewer session {1}", LogHeader, v.Key); + VoiceViewerSession.RemoveViewerSession(v.Key); + v.Value.Shutdown(); + } + } + } +*/ + + private static List> GetViewerSessionsByAgentAndScene(UUID pAgentID, UUID pSceneID) + { + List> matches = []; + if (VoiceViewerSession.TryGetViewerSessionsByAgentId(pAgentID, out IEnumerable> vSessions)) + { + foreach (KeyValuePair v in vSessions) + matches.Add(v); + } + return matches; + } + + private static void CleanupDuplicateSessions(UUID pAgentID, UUID pSceneID, string pKeepViewerSessionId) + { + if(VoiceViewerSession.TryGetViewerSessionsByAgentAndRegion(pAgentID, pSceneID, out IEnumerable> candidates)) + { + bool noskip = string.IsNullOrEmpty(pKeepViewerSessionId); + foreach (KeyValuePair candidate in candidates) + { + if (noskip && candidate.Key == pKeepViewerSessionId) + continue; + + m_log.Warn( + $"{LogHeader} CleanupDuplicateSessions: removing stale viewer_session {candidate.Key} for agent {pAgentID}, scene {pSceneID}"); + + VoiceViewerSession.RemoveViewerSession(candidate.Key); + _ = Task.Run(async () => + { + try + { + await candidate.Value.Shutdown(); + } + catch (Exception ex) + { + m_log.Debug( + $"{LogHeader} CleanupDuplicateSessions: shutdown failed for viewer_session {candidate.Key}: {ex.Message}"); + } + }); + } + } + } + + // // OnRegisterCaps is invoked via the scene.EventManager // everytime OpenSim hands out capabilities to a client @@ -161,7 +270,7 @@ namespace osWebRtcVoice public void OnRegisterCaps(Scene scene, UUID agentID, Caps caps) { m_log.Debug( - $"{logHeader}: OnRegisterCaps called with agentID {agentID} in scene {scene.Name}"); + $"{LogHeader}: OnRegisterCaps called with agentID {agentID} in scene {scene.Name}"); caps.RegisterSimpleHandler("ProvisionVoiceAccountRequest", new SimpleStreamHandler("/" + UUID.Random(), (IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) => @@ -194,27 +303,18 @@ namespace osWebRtcVoice /// public void ProvisionVoiceAccountRequest(IOSHttpRequest request, IOSHttpResponse response, UUID agentID, Scene scene) { - // Get the voice service. If it doesn't exist, return an error. - IWebRtcVoiceService voiceService = scene.RequestModuleInterface(); - if (voiceService is null) - { - m_log.Error($"{logHeader}[ProvisionVoice]: voice service not loaded"); - response.StatusCode = (int)HttpStatusCode.NotFound; - return; - } - if(request.HttpMethod != "POST") { - m_log.Debug($"{logHeader}[ProvisionVoice]: Not a POST request. Agent={agentID}"); + m_log.Debug($"{LogHeader}[ProvisionVoice]: Not a POST request. Agent={agentID}"); response.StatusCode = (int)HttpStatusCode.NotFound; return; } // Deserialize the request. Convert the LLSDXml to OSD for our use - OSDMap map = BodyToMap(request, $"{logHeader}[ProvisionVoice]"); + OSDMap map = BodyToMap(request, $"{LogHeader}[ProvisionVoice]"); if (map is null) { - m_log.Error($"{logHeader}[ProvisionVoice]: No request data found. Agent={agentID}"); + m_log.Error($"{LogHeader}[ProvisionVoice]: No request data found. Agent={agentID}"); response.StatusCode = (int)HttpStatusCode.NoContent; return; } @@ -224,9 +324,9 @@ namespace osWebRtcVoice { if (vstosd is OSDString vst && !((string)vst).Equals("webrtc", StringComparison.OrdinalIgnoreCase)) { - m_log.Warn($"{logHeader}[ProvisionVoice]: voice_server_type is not 'webrtc'"); + m_log.Warn($"{LogHeader}[ProvisionVoice]: voice_server_type is not 'webrtc'"); if (m_log.IsDebugEnabled) - m_log.Warn($"{logHeader}[ProvisionVoice]: Request detail: {map}"); + m_log.Warn($"{LogHeader}[ProvisionVoice]: Request detail: {map}"); response.RawBuffer = llsdUndefAnswerBytes; response.StatusCode = (int)HttpStatusCode.OK; @@ -234,7 +334,7 @@ namespace osWebRtcVoice } } - if (_MessageDetails) m_log.Debug($"{logHeader}[ProvisionVoice]: request: {map}"); + if (_MessageDetails) m_log.Debug($"{LogHeader}[ProvisionVoice]: request: {map}"); if (map.TryGetString("channel_type", out string channelType)) { @@ -243,14 +343,14 @@ namespace osWebRtcVoice { if (!scene.RegionInfo.EstateSettings.AllowVoice) { - m_log.Debug($"{logHeader}[ProvisionVoice]:region \"{scene.Name}\": voice not enabled in estate settings"); + m_log.Debug($"{LogHeader}[ProvisionVoice]:region \"{scene.Name}\": voice not enabled in estate settings"); response.RawBuffer = llsdUndefAnswerBytes; response.StatusCode = (int)HttpStatusCode.NotImplemented; return; } if (scene.LandChannel == null) { - m_log.Error($"{logHeader}[ProvisionVoice] region \"{scene.Name}\" land data not yet available"); + m_log.Error($"{LogHeader}[ProvisionVoice] region \"{scene.Name}\" land data not yet available"); response.RawBuffer = llsdUndefAnswerBytes; response.StatusCode = (int)HttpStatusCode.NotImplemented; return; @@ -258,7 +358,7 @@ namespace osWebRtcVoice if(!scene.TryGetScenePresence(agentID, out ScenePresence sp)) { - m_log.Debug($"{logHeader}[ProvisionVoice]:avatar not found"); + m_log.Debug($"{LogHeader}[ProvisionVoice]:avatar not found"); response.RawBuffer = llsdUndefAnswerBytes; response.StatusCode = (int)HttpStatusCode.NotFound; return; @@ -284,7 +384,7 @@ namespace osWebRtcVoice if (!scene.RegionInfo.EstateSettings.TaxFree && (land.Flags & (uint)ParcelFlags.AllowVoiceChat) == 0) { - m_log.Debug($"{logHeader}[ProvisionVoice]:parcel voice not allowed"); + m_log.Debug($"{LogHeader}[ProvisionVoice]:parcel voice not allowed"); response.RawBuffer = llsdUndefAnswerBytes; response.StatusCode = (int)HttpStatusCode.Forbidden; return; @@ -300,7 +400,7 @@ namespace osWebRtcVoice else if(parcel.IsRestrictedFromLand(agentID) || parcel.IsBannedFromLand(agentID)) { // check Z distance? - m_log.Debug($"{logHeader}[ProvisionVoice]:agent not allowed on parcel"); + m_log.Debug($"{LogHeader}[ProvisionVoice]:agent not allowed on parcel"); response.RawBuffer = llsdUndefAnswerBytes; response.StatusCode = (int)HttpStatusCode.Forbidden; return; @@ -310,11 +410,11 @@ namespace osWebRtcVoice } // The checks passed. Send the request to the voice service. - OSDMap resp = voiceService.ProvisionVoiceAccountRequest(map, agentID, scene.RegionInfo.RegionID); + OSDMap resp = ProvisionVoiceAccountRequestCont(map, agentID, scene.RegionInfo.RegionID); if(resp is not null) { - if (_MessageDetails) m_log.Debug($"{logHeader}[ProvisionVoice]: response: {resp}"); + if (_MessageDetails) m_log.Debug($"{LogHeader}[ProvisionVoice]: response: {resp}"); // TODO: check for errors and package the response @@ -325,34 +425,123 @@ namespace osWebRtcVoice } else { - m_log.Debug($"{logHeader}[ProvisionVoice]: got null response"); + m_log.Debug($"{LogHeader}[ProvisionVoice]: got null response"); response.StatusCode = (int)HttpStatusCode.OK; } return; } - public void VoiceSignalingRequest(IOSHttpRequest request, IOSHttpResponse response, UUID agentID, Scene scene) + public OSDMap ProvisionVoiceAccountRequestCont(OSDMap pRequest, UUID pUserID, UUID pSceneID) { - IWebRtcVoiceService voiceService = scene.RequestModuleInterface(); - if (voiceService is null) + IVoiceViewerSession vSession = null; + if (pRequest.TryGetString("viewer_session", out string viewerSessionId)) { - m_log.Error($"{logHeader}[VoiceSignalingRequest]: No voice service, Agent={agentID}"); - response.StatusCode = (int)HttpStatusCode.NotFound; - return; + if(pRequest.TryGetBool("logout", out bool islog) && islog) + { + if(UUID.ZeroString.Equals(viewerSessionId, StringComparison.OrdinalIgnoreCase)) + { + if (VoiceViewerSession.TryGetViewerSessionsByAgentId(pUserID, out IEnumerable> vSessions)) + { + m_log.Info( + $"{LogHeader} ProvisionVoiceAccountRequest: doing logout for {vSessions.Count()} stall sessions"); + + OSDMap vreq = new() {{ "logout" , true} }; + + foreach(KeyValuePair kvp in vSessions) + { + IVoiceViewerSession v = kvp.Value; + if(v is null) + continue; + vreq["viewer_session"] = v.VoiceServiceSessionId; + v.VoiceService.ProvisionVoiceAccountRequest(v, vreq , pUserID, pSceneID); + } + //return new OSDMap {{ "response", "closed" }}; + } + /* + else + { + return new OSDMap + { + { "response", "error" }, + { "message", "Unable to provision voice session not found)" } + }; + } + */ + return new OSDMap {{ "response", "closed" }}; + } + + if (VoiceViewerSession.TryGetViewerSession(viewerSessionId, out vSession)) + { + VoiceViewerSession.RemoveViewerSession(viewerSessionId); + OSDMap resp = vSession.VoiceService.ProvisionVoiceAccountRequest(vSession, pRequest, pUserID, pSceneID); + return resp ?? new OSDMap() { + { "response", "error" }, + { "message", "Logout session not found" } }; + } + } + + // request has a viewer session. Use that to find the voice service + if (VoiceViewerSession.TryGetViewerSession(viewerSessionId, out vSession)) + { + CleanupDuplicateSessions(pUserID, pSceneID, viewerSessionId); + } + } + else + { + // the request does not have a viewer session. See if it's an initial request + if (pRequest.TryGetString("channel_type", out string channelType)) + { + // Ensure stale sessions are cleared before creating a new one. + CleanupDuplicateSessions(pUserID, pSceneID, null); + if (channelType == "local") + { + // TODO: check if this userId is making a new session (case that user is reconnecting) + vSession = m_spatialVoiceService.CreateViewerSession(pRequest, pUserID, pSceneID); + if(vSession != null) VoiceViewerSession.AddViewerSession(vSession); + } + else + { + // TODO: check if this userId is making a new session (case that user is reconnecting) + vSession = m_nonSpatialVoiceService.CreateViewerSession(pRequest, pUserID, pSceneID); + if(vSession != null) VoiceViewerSession.AddViewerSession(vSession); + } + } } + OSDMap response = null; + + if (vSession is not null) + { + response = vSession.VoiceService.ProvisionVoiceAccountRequest(vSession, pRequest, pUserID, pSceneID); + } + + if (response is null) + { + return new OSDMap + { + { "response", "error" }, + { "message", "Unable to provision voice session (missing viewer_session/channel_type or session not found)" } + }; + } + + return response; + } + + + public void VoiceSignalingRequest(IOSHttpRequest request, IOSHttpResponse response, UUID agentID, Scene scene) + { if(request.HttpMethod != "POST") { - m_log.Error($"{logHeader}[VoiceSignaling]: Not a POST request. Agent={agentID}"); + m_log.Error($"{LogHeader}[VoiceSignaling]: Not a POST request. Agent={agentID}"); response.StatusCode = (int)HttpStatusCode.NotFound; return; } // Deserialize the request. Convert the LLSDXml to OSD for our use - OSDMap map = BodyToMap(request, $"{logHeader}[VoiceSignaling]"); + OSDMap map = BodyToMap(request, $"{LogHeader}[VoiceSignaling]"); if (map is null) { - m_log.Error($"{logHeader}[VoiceSignalingRequest]: No request data found. Agent={agentID}"); + m_log.Error($"{LogHeader}[VoiceSignalingRequest]: No request data found. Agent={agentID}"); response.StatusCode = (int)HttpStatusCode.NoContent; return; } @@ -368,9 +557,9 @@ namespace osWebRtcVoice } } - OSDMap resp = voiceService.VoiceSignalingRequest(map, agentID, scene.RegionInfo.RegionID); + OSDMap resp = VoiceSignalingRequestCont(map, agentID, scene.RegionInfo.RegionID); - if (_MessageDetails) m_log.Debug($"{logHeader}[VoiceSignalingRequest]: Response: {resp}"); + if (_MessageDetails) m_log.Debug($"{LogHeader}[VoiceSignalingRequest]: Response: {resp}"); // TODO: check for errors // viewers ignore response @@ -379,6 +568,31 @@ namespace osWebRtcVoice return; } + + public OSDMap VoiceSignalingRequestCont(OSDMap pRequest, UUID pUserID, UUID pSceneID) + { + OSDMap response = null; + IVoiceViewerSession vSession = null; + if (pRequest.TryGetString("viewer_session", out string viewerSessionId)) + { + // request has a viewer session. Use that to find the voice service + if (VoiceViewerSession.TryGetViewerSession(viewerSessionId, out vSession)) + { + response = vSession.VoiceService.VoiceSignalingRequest(vSession, pRequest, pUserID, pSceneID); + } + else + { + m_log.Error($"{LogHeader} VoiceSignalingRequest: viewer session {viewerSessionId} not found"); + } + } + else + { + m_log.Error($"{LogHeader} VoiceSignalingRequest: no viewer_session in request"); + } + return response; + } + + /// /// Callback for a client request for ChatSessionRequest. /// The viewer sends this request when the user tries to start a P2P text or voice session @@ -390,7 +604,7 @@ namespace osWebRtcVoice /// public void ChatSessionRequest(IOSHttpRequest request, IOSHttpResponse response, UUID agentID, Scene scene) { - m_log.Debug($"{logHeader}: ChatSessionRequest received for agent {agentID} in scene {scene.Name}"); + m_log.Debug($"{LogHeader}: ChatSessionRequest received for agent {agentID} in scene {scene.Name}"); if (request.HttpMethod != "POST") { response.StatusCode = (int)HttpStatusCode.NotFound; @@ -399,31 +613,31 @@ namespace osWebRtcVoice if (!scene.TryGetScenePresence(agentID, out ScenePresence sp) || sp.IsDeleted) { - m_log.Warn($"{logHeader} ChatSessionRequest: scene presence not found or deleted for agent {agentID}"); + m_log.Warn($"{LogHeader} ChatSessionRequest: scene presence not found or deleted for agent {agentID}"); response.StatusCode = (int)HttpStatusCode.NotFound; return; } - OSDMap reqmap = BodyToMap(request, $"{logHeader}[ChatSessionRequest]"); + OSDMap reqmap = BodyToMap(request, $"{LogHeader}[ChatSessionRequest]"); if (reqmap is null) { - m_log.Warn($"{logHeader} ChatSessionRequest: message body not parsable in request for agent {agentID}"); + m_log.Warn($"{LogHeader} ChatSessionRequest: message body not parsable in request for agent {agentID}"); response.StatusCode = (int)HttpStatusCode.NoContent; return; } - m_log.Debug($"{logHeader} ChatSessionRequest"); + m_log.Debug($"{LogHeader} ChatSessionRequest"); if (!reqmap.TryGetString("method", out string method)) { - m_log.Warn($"{logHeader} ChatSessionRequest: missing required 'method' field in request for agent {agentID}"); + m_log.Warn($"{LogHeader} ChatSessionRequest: missing required 'method' field in request for agent {agentID}"); response.StatusCode = (int)HttpStatusCode.NotFound; return; } if (!reqmap.TryGetUUID("session-id", out UUID sessionID)) { - m_log.Warn($"{logHeader} ChatSessionRequest: missing required 'session-id' field in request for agent {agentID}"); + m_log.Warn($"{LogHeader} ChatSessionRequest: missing required 'session-id' field in request for agent {agentID}"); response.StatusCode = (int)HttpStatusCode.NotFound; return; } @@ -450,7 +664,7 @@ namespace osWebRtcVoice IEventQueue queue = scene.RequestModuleInterface(); if (queue is null) { - m_log.Error($"{logHeader}: no event queue for scene {scene.Name}"); + m_log.Error($"{LogHeader}: no event queue for scene {scene.Name}"); response.StatusCode = (int)HttpStatusCode.InternalServerError; } else diff --git a/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceServiceModule/WebRtcVoiceServiceModule.cs b/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceServiceModule/WebRtcVoiceServiceModule.cs index d52a6e7a7d..1fe410d9cd 100644 --- a/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceServiceModule/WebRtcVoiceServiceModule.cs +++ b/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceServiceModule/WebRtcVoiceServiceModule.cs @@ -58,7 +58,7 @@ namespace osWebRtcVoice /// Fancier configurations are possible. /// [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebRtcVoiceServiceModule")] - public class WebRtcVoiceServiceModule : ISharedRegionModule, IWebRtcVoiceService + public class WebRtcVoiceServiceModule : IWebRtcVoiceService { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static string LogHeader = "[WEBRTC VOICE SERVICE MODULE]"; @@ -71,9 +71,7 @@ namespace osWebRtcVoice // ===================================================================== - // ISharedRegionModule.Initialize - // Get configuration and load the modules that will handle spatial and non-spatial voice. - public void Initialise(IConfigSource pConfig) + public WebRtcVoiceServiceModule(IConfigSource pConfig) { m_Config = pConfig; IConfig moduleConfig = m_Config.Configs["WebRtcVoice"]; @@ -121,7 +119,7 @@ namespace osWebRtcVoice m_nonSpatialVoiceService = ServerUtils.LoadPlugin(nonSpatialDllName, [ m_Config ]); if (m_nonSpatialVoiceService is null) { - m_log.Error("{LogHeader} Could not load NonSpatialVoiceService from {nonSpatialDllName}"); + m_log.Error($"{LogHeader} Could not load NonSpatialVoiceService from {nonSpatialDllName}"); m_Enabled = false; } } @@ -134,85 +132,6 @@ namespace osWebRtcVoice } } - // ISharedRegionModule.PostInitialize - public void PostInitialise() - { - } - - // ISharedRegionModule.Close - public void Close() - { - } - - // ISharedRegionModule.ReplaceableInterface - public Type ReplaceableInterface - { - get { return null; } - } - - // ISharedRegionModule.Name - public string Name - { - get { return "WebRtcVoiceServiceModule"; } - } - - // ISharedRegionModule.AddRegion - public void AddRegion(Scene scene) - { - if (m_Enabled) - { - m_log.Debug($"{LogHeader} Adding WebRtcVoiceService to region {scene.Name}"); - scene.RegisterModuleInterface(this); - - // TODO: figure out what events we care about - // When new client (child or root) is added to scene, before OnClientLogin - // scene.EventManager.OnNewClient += Event_OnNewClient; - // When client is added on login. - // scene.EventManager.OnClientLogin += Event_OnClientLogin; - // New presence is added to scene. Child, root, and NPC. See Scene.AddNewAgent() - // scene.EventManager.OnNewPresence += Event_OnNewPresence; - // scene.EventManager.OnRemovePresence += Event_OnRemovePresence; - // update to client position (either this or 'significant') - // scene.EventManager.OnClientMovement += Event_OnClientMovement; - // "significant" update to client position - // scene.EventManager.OnSignificantClientMovement += Event_OnSignificantClientMovement; - } - - } - - // ISharedRegionModule.RemoveRegion - public void RemoveRegion(Scene scene) - { - if (m_Enabled) - { - scene.UnregisterModuleInterface(this); - } - } - - // ISharedRegionModule.RegionLoaded - public void RegionLoaded(Scene scene) - { - } - - // ===================================================================== - // Thought about doing this but currently relying on the voice service - // event ("hangup") to remove the viewer session. -/* - private void Event_OnRemovePresence(UUID pAgentID) - { - // When a presence is removed, remove the viewer sessions for that agent - IEnumerable> vSessions; - if (VoiceViewerSession.TryGetViewerSessionByAgentId(pAgentID, out vSessions)) - { - foreach(KeyValuePair v in vSessions) - { - m_log.DebugFormat("{0} Event_OnRemovePresence: removing viewer session {1}", LogHeader, v.Key); - VoiceViewerSession.RemoveViewerSession(v.Key); - v.Value.Shutdown(); - } - } - } -*/ private static List> GetViewerSessionsByAgentAndScene(UUID pAgentID, UUID pSceneID) { @@ -237,29 +156,6 @@ namespace osWebRtcVoice return propertyInfo.GetValue(pSource); } - private static bool IsViewerSessionReusable(IVoiceViewerSession pViewerSession) - { - if (pViewerSession is null) - return false; - - if (string.IsNullOrEmpty(pViewerSession.ViewerSessionID) || string.IsNullOrEmpty(pViewerSession.VoiceServiceSessionId)) - return false; - - object disconnectReason = TryGetPropertyValue(pViewerSession, "DisconnectReason"); - if (disconnectReason is string reason && !string.IsNullOrEmpty(reason)) - return false; - - object sessionObj = TryGetPropertyValue(pViewerSession, "Session"); - if (sessionObj is not null) - { - object isConnectedObj = TryGetPropertyValue(sessionObj, "IsConnected"); - if (isConnectedObj is bool isConnected && !isConnected) - return false; - } - - return true; - } - private static void CleanupDuplicateSessions(UUID pAgentID, UUID pSceneID, string pKeepViewerSessionId) { if(VoiceViewerSession.TryGetViewerSessionsByAgentAndRegion(pAgentID, pSceneID, out IEnumerable> candidates))