Merge branch 'master' into lickx

This commit is contained in:
2026-02-26 19:22:59 +01:00
8 changed files with 154 additions and 159 deletions

View File

@@ -71,20 +71,20 @@ namespace osWebRtcVoice
{ {
ViewerSessionID = OMV.UUID.Random().ToString(); ViewerSessionID = OMV.UUID.Random().ToString();
VoiceService = pVoiceService; VoiceService = pVoiceService;
m_log.DebugFormat("{0} JanusViewerSession created {1}", LogHeader, ViewerSessionID); m_log.Debug($"{LogHeader} JanusViewerSession created {ViewerSessionID}");
} }
public JanusViewerSession(string pViewerSessionID, IWebRtcVoiceService pVoiceService) public JanusViewerSession(string pViewerSessionID, IWebRtcVoiceService pVoiceService)
{ {
ViewerSessionID = pViewerSessionID; ViewerSessionID = pViewerSessionID;
VoiceService = pVoiceService; VoiceService = pVoiceService;
m_log.DebugFormat("{0} JanusViewerSession created {1}", LogHeader, ViewerSessionID); m_log.Debug($"{LogHeader} JanusViewerSession created {ViewerSessionID}");
} }
// Send the messages to the voice service to try and get rid of the session // Send the messages to the voice service to try and get rid of the session
// IVoiceViewerSession.Shutdown // IVoiceViewerSession.Shutdown
public async Task Shutdown() public async Task Shutdown()
{ {
m_log.DebugFormat("{0} JanusViewerSession shutdown {1}", LogHeader, ViewerSessionID); m_log.DebugFormat($"{LogHeader} JanusViewerSession shutdown {ViewerSessionID}");
if (Room is not null) if (Room is not null)
{ {
var rm = Room; var rm = Room;
@@ -101,7 +101,7 @@ namespace osWebRtcVoice
{ {
var s = Session; var s = Session;
Session = null; Session = null;
await s.DestroySession(); _ = await s.DestroySession().ConfigureAwait(false);
s.Dispose(); s.Dispose();
} }
} }

View File

@@ -48,10 +48,10 @@ namespace osWebRtcVoice
private readonly IConfigSource _Config; private readonly IConfigSource _Config;
private bool _Enabled = false; private bool _Enabled = false;
private string _JanusServerURI = String.Empty; private string _JanusServerURI = string.Empty;
private string _JanusAPIToken = String.Empty; private string _JanusAPIToken = string.Empty;
private string _JanusAdminURI = String.Empty; private string _JanusAdminURI = string.Empty;
private string _JanusAdminToken = String.Empty; private string _JanusAdminToken = string.Empty;
private bool _MessageDetails = false; private bool _MessageDetails = false;
@@ -74,86 +74,84 @@ namespace osWebRtcVoice
IConfig janusConfig = _Config.Configs["JanusWebRtcVoice"]; IConfig janusConfig = _Config.Configs["JanusWebRtcVoice"];
if (_Enabled && janusConfig is not null) if (_Enabled && janusConfig is not null)
{ {
_JanusServerURI = janusConfig.GetString("JanusGatewayURI", String.Empty); _JanusServerURI = janusConfig.GetString("JanusGatewayURI", string.Empty);
_JanusAPIToken = janusConfig.GetString("APIToken", String.Empty); _JanusAPIToken = janusConfig.GetString("APIToken", string.Empty);
_JanusAdminURI = janusConfig.GetString("JanusGatewayAdminURI", String.Empty); _JanusAdminURI = janusConfig.GetString("JanusGatewayAdminURI", string.Empty);
_JanusAdminToken = janusConfig.GetString("AdminAPIToken", String.Empty); _JanusAdminToken = janusConfig.GetString("AdminAPIToken", string.Empty);
// Debugging options // Debugging options
_MessageDetails = janusConfig.GetBoolean("MessageDetails", false); _MessageDetails = janusConfig.GetBoolean("MessageDetails", false);
if (String.IsNullOrEmpty(_JanusServerURI) || String.IsNullOrEmpty(_JanusAPIToken) || if (string.IsNullOrEmpty(_JanusServerURI) || string.IsNullOrEmpty(_JanusAPIToken) ||
String.IsNullOrEmpty(_JanusAdminURI) || String.IsNullOrEmpty(_JanusAdminToken)) string.IsNullOrEmpty(_JanusAdminURI) || string.IsNullOrEmpty(_JanusAdminToken))
{ {
_log.ErrorFormat("{0} JanusWebRtcVoice configuration section missing required fields", LogHeader); _log.Error($"{LogHeader} JanusWebRtcVoice configuration section missing required fields");
_Enabled = false; _Enabled = false;
} }
if (_Enabled) if (_Enabled)
{ {
_log.DebugFormat("{0} Enabled", LogHeader); if(!StartConnectionToJanus())
StartConnectionToJanus(); {
_log.Error($"{LogHeader} failed connection to Janus Gateway. Disabled");
_Enabled=false;
return;
}
RegisterConsoleCommands(); RegisterConsoleCommands();
_log.Info($"{LogHeader} Enabled");
} }
} }
else else
{ {
_log.ErrorFormat("{0} No JanusWebRtcVoice configuration section", LogHeader); _log.Error($"{LogHeader} No JanusWebRtcVoice configuration section");
_Enabled = false; _Enabled = false;
} }
} }
else else
{ {
_log.ErrorFormat("{0} No WebRtcVoice configuration section", LogHeader); _log.Error($"{LogHeader} No WebRtcVoice configuration section");
_Enabled = false; _Enabled = false;
} }
} }
// Start a thread to do the connection to the Janus server.
// Here an initial session is created and then a handle to the audio bridge plugin // Here an initial session is created and then a handle to the audio bridge plugin
// is created for the console commands. Since webrtc PeerConnections that are created // is created for the console commands. Since webrtc PeerConnections that are created
// my Janus are per-session, the other sessions will be created by the viewer requests. // my Janus are per-session, the other sessions will be created by the viewer requests.
private void StartConnectionToJanus() private bool StartConnectionToJanus()
{ {
_log.DebugFormat("{0} StartConnectionToJanus", LogHeader); _log.DebugFormat("{0} StartConnectionToJanus", LogHeader);
Task.Run(async () =>
{
_ViewerSession = new JanusViewerSession(this); _ViewerSession = new JanusViewerSession(this);
await ConnectToSessionAndAudioBridge(_ViewerSession); //bad
}); return ConnectToSessionAndAudioBridge(_ViewerSession).Result;
} }
private async Task ConnectToSessionAndAudioBridge(JanusViewerSession pViewerSession) private async Task<bool> ConnectToSessionAndAudioBridge(JanusViewerSession pViewerSession)
{ {
JanusSession janusSession = new JanusSession(_JanusServerURI, _JanusAPIToken, _JanusAdminURI, _JanusAdminToken, _MessageDetails); JanusSession janusSession = new JanusSession(_JanusServerURI, _JanusAPIToken, _JanusAdminURI, _JanusAdminToken, _MessageDetails);
if (await janusSession.CreateSession()) if (await janusSession.CreateSession().ConfigureAwait(false))
{ {
_log.DebugFormat("{0} JanusSession created", LogHeader); _log.DebugFormat("{0} JanusSession created", LogHeader);
janusSession.OnDisconnect += Handle_Hangup;
// Once the session is created, create a handle to the plugin for rooms // Once the session is created, create a handle to the plugin for rooms
JanusAudioBridge audioBridge = new JanusAudioBridge(janusSession); JanusAudioBridge audioBridge = new JanusAudioBridge(janusSession);
janusSession.AddPlugin(audioBridge);
pViewerSession.VoiceServiceSessionId = janusSession.SessionId; if (await audioBridge.Activate(_Config).ConfigureAwait(false))
pViewerSession.Session = janusSession;
pViewerSession.AudioBridge = audioBridge;
janusSession.OnHangup += Handle_Hangup;
if (await audioBridge.Activate(_Config))
{ {
_log.DebugFormat("{0} AudioBridgePluginHandle created", LogHeader); _log.Debug($"{LogHeader} AudioBridgePluginHandle created");
// Requests through the capabilities will create rooms // Requests through the capabilities will create rooms
janusSession.AddPlugin(audioBridge);
pViewerSession.VoiceServiceSessionId = janusSession.SessionId;
pViewerSession.Session = janusSession;
pViewerSession.AudioBridge = audioBridge;
janusSession.OnDisconnect += Handle_Hangup;
janusSession.OnHangup += Handle_Hangup;
return true;
} }
else _log.Error($"{LogHeader} JanusPluginHandle not created");
{
_log.ErrorFormat("{0} JanusPluginHandle not created", LogHeader);
}
} }
else _log.Error($"{LogHeader} JanusSession not created");
{ return false;
_log.ErrorFormat("{0} JanusSession not created", LogHeader);
}
} }
private void Handle_Hangup(EventResp pResp) private void Handle_Hangup(EventResp pResp)
@@ -161,7 +159,7 @@ namespace osWebRtcVoice
if (pResp is not null) if (pResp is not null)
{ {
var sessionId = pResp.sessionId; var sessionId = pResp.sessionId;
_log.DebugFormat("{0} Handle_Hangup: {1}, sessionId={2}", LogHeader, pResp.RawBody.ToString(), sessionId); _log.Debug($"{LogHeader} Handle_Hangup: {pResp.RawBody}, sessionId={sessionId}");
if (VoiceViewerSession.TryGetViewerSessionByVSSessionId(sessionId, out IVoiceViewerSession viewerSession)) if (VoiceViewerSession.TryGetViewerSessionByVSSessionId(sessionId, out IVoiceViewerSession viewerSession))
{ {
// There is a viewer session associated with this session // There is a viewer session associated with this session
@@ -169,7 +167,7 @@ namespace osWebRtcVoice
} }
else else
{ {
_log.DebugFormat("{0} Handle_Hangup: no session found. SessionId={1}", LogHeader, sessionId); _log.Debug($"{LogHeader} Handle_Hangup: no session found. SessionId={sessionId}");
} }
} }
} }
@@ -192,7 +190,12 @@ namespace osWebRtcVoice
// This is the logic that takes the client's request and converts it into // This is the logic that takes the client's request and converts it into
// operations on rooms in the audio bridge. // operations on rooms in the audio bridge.
// IWebRtcVoiceService.ProvisionVoiceAccountRequest // IWebRtcVoiceService.ProvisionVoiceAccountRequest
public async Task<OSDMap> ProvisionVoiceAccountRequest(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap ProvisionVoiceAccountRequest(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
return ProvisionVoiceAccountRequestBAD(pSession, pRequest, pUserID, pSceneID).Result;
}
public async Task<OSDMap> ProvisionVoiceAccountRequestBAD(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
OSDMap ret = null; OSDMap ret = null;
string errorMsg = null; string errorMsg = null;
@@ -202,11 +205,11 @@ namespace osWebRtcVoice
if (viewerSession.Session is null) if (viewerSession.Session is null)
{ {
// This is a new session so we must create a new session and handle to the audio bridge // This is a new session so we must create a new session and handle to the audio bridge
await ConnectToSessionAndAudioBridge(viewerSession); await ConnectToSessionAndAudioBridge(viewerSession).ConfigureAwait(false);
} }
// TODO: need to keep count of users in a room to know when to close a room // TODO: need to keep count of users in a room to know when to close a room
bool isLogout = pRequest.ContainsKey("logout") && pRequest["logout"].AsBoolean(); bool isLogout = pRequest.TryGetBool("logout", out bool lgout) && lgout;
if (isLogout) if (isLogout)
{ {
// The client is logging out. Exit the room. // The client is logging out. Exit the room.
@@ -223,16 +226,16 @@ namespace osWebRtcVoice
// Get the parameters that select the room // Get the parameters that select the room
// To get here, voice_server_type has already been checked to be 'webrtc' and channel_type='local' // To get here, voice_server_type has already been checked to be 'webrtc' and channel_type='local'
int parcel_local_id = pRequest.ContainsKey("parcel_local_id") ? pRequest["parcel_local_id"].AsInteger() : JanusAudioBridge.REGION_ROOM_ID; int parcel_local_id = pRequest.TryGetInt("parcel_local_id", out int pli) ? pli : JanusAudioBridge.REGION_ROOM_ID;
string channel_id = pRequest.ContainsKey("channel_id") ? pRequest["channel_id"].AsString() : String.Empty; string channel_id = pRequest.TryGetString("channel_id", out string cli) ? cli : string.Empty;
string channel_credentials = pRequest.ContainsKey("credentials") ? pRequest["credentials"].AsString() : String.Empty; string channel_credentials = pRequest.TryGetString("credentials", out string cred) ? cred : string.Empty;
string channel_type = pRequest["channel_type"].AsString(); string channel_type = pRequest["channel_type"].AsString();
bool isSpatial = channel_type == "local"; bool isSpatial = channel_type == "local";
string voice_server_type = pRequest["voice_server_type"].AsString(); string voice_server_type = pRequest["voice_server_type"].AsString();
_log.DebugFormat("{0} ProvisionVoiceAccountRequest: parcel_id={1} channel_id={2} channel_type={3} voice_server_type={4}", LogHeader, parcel_local_id, channel_id, channel_type, voice_server_type); _log.DebugFormat("{0} ProvisionVoiceAccountRequest: parcel_id={1} channel_id={2} channel_type={3} voice_server_type={4}", LogHeader, parcel_local_id, channel_id, channel_type, voice_server_type);
if (pRequest.ContainsKey("jsep") && pRequest["jsep"] is OSDMap jsep) if (pRequest.TryGetOSDMap("jsep", out OSDMap jsep))
{ {
// The jsep is the SDP from the client. This is the client's request to connect to the audio bridge. // The jsep is the SDP from the client. This is the client's request to connect to the audio bridge.
string jsepType = jsep["type"].AsString(); string jsepType = jsep["type"].AsString();
@@ -242,17 +245,17 @@ namespace osWebRtcVoice
// The client is sending an offer. Find the right room and join it. // The client is sending an offer. Find the right room and join it.
// _log.DebugFormat("{0} ProvisionVoiceAccountRequest: jsep type={1} sdp={2}", LogHeader, jsepType, jsepSdp); // _log.DebugFormat("{0} ProvisionVoiceAccountRequest: jsep type={1} sdp={2}", LogHeader, jsepType, jsepSdp);
viewerSession.Room = await viewerSession.AudioBridge.SelectRoom(pSceneID.ToString(), viewerSession.Room = await viewerSession.AudioBridge.SelectRoom(pSceneID.ToString(),
channel_type, isSpatial, parcel_local_id, channel_id); channel_type, isSpatial, parcel_local_id, channel_id).ConfigureAwait(false);
if (viewerSession.Room is null) if (viewerSession.Room is null)
{ {
errorMsg = "room selection failed"; errorMsg = "room selection failed";
_log.ErrorFormat("{0} ProvisionVoiceAccountRequest: room selection failed", LogHeader); _log.Error($"{LogHeader} ProvisionVoiceAccountRequest: room selection failed");
} }
else { else {
viewerSession.Offer = jsepSdp; viewerSession.Offer = jsepSdp;
viewerSession.OfferOrig = jsepSdp; viewerSession.OfferOrig = jsepSdp;
viewerSession.AgentId = pUserID; viewerSession.AgentId = pUserID;
if (await viewerSession.Room.JoinRoom(viewerSession)) if (await viewerSession.Room.JoinRoom(viewerSession).ConfigureAwait(false))
{ {
ret = new OSDMap ret = new OSDMap
{ {
@@ -263,29 +266,29 @@ namespace osWebRtcVoice
else else
{ {
errorMsg = "JoinRoom failed"; errorMsg = "JoinRoom failed";
_log.ErrorFormat("{0} ProvisionVoiceAccountRequest: JoinRoom failed", LogHeader); _log.Error($"{LogHeader} ProvisionVoiceAccountRequest: JoinRoom failed");
} }
} }
} }
else else
{ {
errorMsg = "jsep type not offer"; errorMsg = "jsep type not offer";
_log.ErrorFormat("{0} ProvisionVoiceAccountRequest: jsep type={1} not offer", LogHeader, jsepType); _log.Error($"{LogHeader} ProvisionVoiceAccountRequest: jsep type={jsepType} not offer");
} }
} }
else else
{ {
errorMsg = "no jsep"; errorMsg = "no jsep";
_log.DebugFormat("{0} ProvisionVoiceAccountRequest: no jsep. req={1}", LogHeader, pRequest.ToString()); _log.Debug($"{LogHeader} ProvisionVoiceAccountRequest: no jsep. req={pRequest}");
} }
} }
else else
{ {
errorMsg = "viewersession not JanusViewerSession"; errorMsg = "viewersession not JanusViewerSession";
_log.ErrorFormat("{0} ProvisionVoiceAccountRequest: viewersession not JanusViewerSession", LogHeader); _log.Error("{LogHeader} ProvisionVoiceAccountRequest: viewersession not JanusViewerSession");
} }
if (!String.IsNullOrEmpty(errorMsg) && ret is null) if (!string.IsNullOrEmpty(errorMsg) && ret is null)
{ {
// The provision failed so build an error messgage to return // The provision failed so build an error messgage to return
ret = new OSDMap ret = new OSDMap
@@ -299,7 +302,12 @@ namespace osWebRtcVoice
} }
// IWebRtcVoiceService.VoiceAccountBalanceRequest // IWebRtcVoiceService.VoiceAccountBalanceRequest
public async Task<OSDMap> VoiceSignalingRequest(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap VoiceSignalingRequest(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
return VoiceSignalingRequestBAD(pSession, pRequest, pUserID, pSceneID).Result;
}
public async Task<OSDMap> VoiceSignalingRequestBAD(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
OSDMap ret = null; OSDMap ret = null;
JanusViewerSession viewerSession = pSession as JanusViewerSession; JanusViewerSession viewerSession = pSession as JanusViewerSession;
@@ -307,19 +315,19 @@ namespace osWebRtcVoice
if (viewerSession is not null) if (viewerSession is not null)
{ {
// The request should be an array of candidates // The request should be an array of candidates
if (pRequest.ContainsKey("candidate") && pRequest["candidate"] is OSDMap candidate) if (pRequest.TryGetOSDMap("candidate", out OSDMap candidate))
{ {
if (candidate.ContainsKey("completed") && candidate["completed"].AsBoolean()) if (candidate.TryGetBool("completed", out bool iscompleted) && iscompleted)
{ {
// The client has finished sending candidates // The client has finished sending candidates
resp = await viewerSession.Session.TrickleCompleted(viewerSession); resp = await viewerSession.Session.TrickleCompleted(viewerSession).ConfigureAwait(false);
_log.DebugFormat("{0} VoiceSignalingRequest: candidate completed", LogHeader); _log.DebugFormat($"{LogHeader} VoiceSignalingRequest: candidate completed");
} }
else else
{ {
} }
} }
else if (pRequest.ContainsKey("candidates") && pRequest["candidates"] is OSDArray candidates) else if (pRequest.TryGetOSDArray("candidates", out OSDArray candidates))
{ {
OSDArray candidatesArray = new OSDArray(); OSDArray candidatesArray = new OSDArray();
foreach (OSDMap cand in candidates) foreach (OSDMap cand in candidates)
@@ -330,17 +338,17 @@ namespace osWebRtcVoice
{ "sdpMLineIndex", cand["sdpMLineIndex"].AsLong() } { "sdpMLineIndex", cand["sdpMLineIndex"].AsLong() }
}); });
} }
resp = await viewerSession.Session.TrickleCandidates(viewerSession, candidatesArray); resp = await viewerSession.Session.TrickleCandidates(viewerSession, candidatesArray).ConfigureAwait(false);
_log.DebugFormat("{0} VoiceSignalingRequest: {1} candidates", LogHeader, candidatesArray.Count); _log.Debug($"{LogHeader} VoiceSignalingRequest: {candidatesArray.Count} candidates");
} }
else else
{ {
_log.ErrorFormat("{0} VoiceSignalingRequest: no 'candidate' or 'candidates'", LogHeader); _log.Error($"{LogHeader} VoiceSignalingRequest: no 'candidate' or 'candidates'");
} }
} }
if (resp is null) if (resp is null)
{ {
_log.ErrorFormat("{0} VoiceSignalingRequest: no response so returning error", LogHeader); _log.ErrorFormat($"{LogHeader} VoiceSignalingRequest: no response so returning error");
ret = new OSDMap ret = new OSDMap
{ {
{ "response", "error" } { "response", "error" }
@@ -355,14 +363,14 @@ namespace osWebRtcVoice
// This module should not be invoked with this signature // This module should not be invoked with this signature
// IWebRtcVoiceService.ProvisionVoiceAccountRequest // IWebRtcVoiceService.ProvisionVoiceAccountRequest
public Task<OSDMap> ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
// This module should not be invoked with this signature // This module should not be invoked with this signature
// IWebRtcVoiceService.VoiceSignalingRequest // IWebRtcVoiceService.VoiceSignalingRequest
public Task<OSDMap> VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@@ -395,24 +403,26 @@ namespace osWebRtcVoice
} }
} }
private async void HandleJanusInfo(string module, string[] cmdparms) private void HandleJanusInfo(string module, string[] cmdparms)
{ {
if (_ViewerSession is not null && _ViewerSession.Session is not null) if (_ViewerSession is not null && _ViewerSession.Session is not null)
{ {
WriteOut("{0} Janus session: {1}", LogHeader, _ViewerSession.Session.SessionId); WriteOut("{0} Janus session: {1}", LogHeader, _ViewerSession.Session.SessionId);
string infoURI = _ViewerSession.Session.JanusServerURI + "/info"; string infoURI = _ViewerSession.Session.JanusServerURI + "/info";
var resp = await _ViewerSession.Session.GetFromJanus(infoURI);
var resp = _ViewerSession.Session.GetFromJanus(infoURI).Result;
if (resp is not null) if (resp is not null)
MainConsole.Instance.Output(resp.ToJson()); MainConsole.Instance.Output(resp.ToJson());
} }
} }
private async void HandleJanusListRooms(string module, string[] cmdparms) private void HandleJanusListRooms(string module, string[] cmdparms)
{ {
if (_ViewerSession is not null && _ViewerSession.Session is not null && _ViewerSession.AudioBridge is not null) if (_ViewerSession is not null && _ViewerSession.Session is not null && _ViewerSession.AudioBridge is not null)
{ {
var ab = _ViewerSession.AudioBridge; var ab = _ViewerSession.AudioBridge;
var resp = await ab.SendAudioBridgeMsg(new AudioBridgeListRoomsReq()); var resp = ab.SendAudioBridgeMsg(new AudioBridgeListRoomsReq()).Result;
if (resp is not null && resp.isSuccess) if (resp is not null && resp.isSuccess)
{ {
if (resp.PluginRespData.TryGetValue("list", out OSD list)) if (resp.PluginRespData.TryGetValue("list", out OSD list))
@@ -423,11 +433,14 @@ namespace osWebRtcVoice
"Room", "Description", "Num", "SampleRate", "Spatial", "Recording"); "Room", "Description", "Num", "SampleRate", "Spatial", "Recording");
foreach (OSDMap room in list as OSDArray) foreach (OSDMap room in list as OSDArray)
{ {
int roomid = room["room"].AsInteger();
MainConsole.Instance.Output( MainConsole.Instance.Output(
" {0,10} {1,15} {2,5} {3,10} {4,7} {5,7}", " {0,10} {1,15} {2,5} {3,10} {4,7} {5,7}",
room["room"], room["description"], room["num_participants"], roomid, room["description"], room["num_participants"],
room["sampling_rate"], room["spatial_audio"], room["record"]); room["sampling_rate"], room["spatial_audio"], room["record"]);
var participantResp = await ab.SendAudioBridgeMsg(new AudioBridgeListParticipantsReq(room["room"].AsInteger()));
var participantResp = ab.SendAudioBridgeMsg(new AudioBridgeListParticipantsReq(roomid)).Result;
if (participantResp is not null && participantResp.AudioBridgeReturnCode == "participants") if (participantResp is not null && participantResp.AudioBridgeReturnCode == "participants")
{ {
if (participantResp.PluginRespData.TryGetValue("participants", out OSD participants)) if (participantResp.PluginRespData.TryGetValue("participants", out OSD participants))

View File

@@ -25,11 +25,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
using OpenSim.Framework;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.StructuredData; using OpenMetaverse.StructuredData;
using System.Threading.Tasks;
namespace osWebRtcVoice namespace osWebRtcVoice
{ {
@@ -45,12 +42,12 @@ namespace osWebRtcVoice
// If there are problems, the returned map will contain an error message. // If there are problems, the returned map will contain an error message.
// Initial calls to the voice server to get the user connected // Initial calls to the voice server to get the user connected
public Task<OSDMap> ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pScene); public OSDMap ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pScene);
public Task<OSDMap> VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pScene); public OSDMap VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pScene);
// Once connection state is looked up, the viewer session is passed in // Once connection state is looked up, the viewer session is passed in
public Task<OSDMap> ProvisionVoiceAccountRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pScene); public OSDMap ProvisionVoiceAccountRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pScene);
public Task<OSDMap> VoiceSignalingRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pScene); public OSDMap VoiceSignalingRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pScene);
// Create a viewer session with all the variables needed for the underlying implementation // Create a viewer session with all the variables needed for the underlying implementation
public IVoiceViewerSession CreateViewerSession(OSDMap pRequest, UUID pUserID, UUID pScene); public IVoiceViewerSession CreateViewerSession(OSDMap pRequest, UUID pUserID, UUID pScene);

View File

@@ -114,7 +114,7 @@ namespace osWebRtcVoice
m_log.ErrorFormat("{0} PVAR: no local service", LogHeader); m_log.ErrorFormat("{0} PVAR: no local service", LogHeader);
return false; return false;
} }
OSDMap resp = m_WebRtcVoiceService.ProvisionVoiceAccountRequest(request, userID, sceneID).Result; OSDMap resp = m_WebRtcVoiceService.ProvisionVoiceAccountRequest(request, userID, sceneID);
pResponse = new JsonRpcResponse(); pResponse = new JsonRpcResponse();
pResponse.Result = resp; pResponse.Result = resp;
@@ -147,7 +147,7 @@ namespace osWebRtcVoice
try try
{ {
OSDMap resp = m_WebRtcVoiceService.VoiceSignalingRequest(request, userID, sceneID).Result; OSDMap resp = m_WebRtcVoiceService.VoiceSignalingRequest(request, userID, sceneID);
pResponse = new JsonRpcResponse(); pResponse = new JsonRpcResponse();
pResponse.Result = resp; pResponse.Result = resp;

View File

@@ -27,7 +27,6 @@
using System; using System;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using OpenSim.Framework; using OpenSim.Framework;
@@ -36,7 +35,6 @@ using OpenMetaverse.StructuredData;
using log4net; using log4net;
using Nini.Config; using Nini.Config;
using OSHttpServer;
namespace osWebRtcVoice namespace osWebRtcVoice
{ {
@@ -65,12 +63,12 @@ namespace osWebRtcVoice
m_serverURI = moduleConfig.GetString("WebRtcVoiceServerURI", string.Empty); m_serverURI = moduleConfig.GetString("WebRtcVoiceServerURI", string.Empty);
if (string.IsNullOrWhiteSpace(m_serverURI)) if (string.IsNullOrWhiteSpace(m_serverURI))
{ {
m_log.ErrorFormat("{0} WebRtcVoiceServiceConnector enabled but no WebRtcVoiceServerURI specified", LogHeader); m_log.Error($"{LogHeader} WebRtcVoiceServiceConnector enabled but no WebRtcVoiceServerURI specified");
m_Enabled = false; m_Enabled = false;
} }
else else
{ {
m_log.InfoFormat("{0} WebRtcVoiceServiceConnector enabled", LogHeader); m_log.Info($"{LogHeader} WebRtcVoiceServiceConnector enabled");
} }
m_MessageDetails = moduleConfig.GetBoolean("MessageDetails", false); m_MessageDetails = moduleConfig.GetBoolean("MessageDetails", false);
@@ -83,50 +81,49 @@ namespace osWebRtcVoice
// so that the viewer session ID is the same here as from the WebRTC service. // so that the viewer session ID is the same here as from the WebRTC service.
public IVoiceViewerSession CreateViewerSession(OSDMap pRequest, UUID pUserID, UUID pSceneID) public IVoiceViewerSession CreateViewerSession(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
m_log.DebugFormat("{0} CreateViewerSession", LogHeader); m_log.Debug($"{LogHeader} CreateViewerSession");
return new VoiceViewerSession(this, pUserID, pSceneID); return new VoiceViewerSession(this, pUserID, pSceneID);
} }
public Task<OSDMap> ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
m_log.DebugFormat("{0} ProvisionVoiceAccountRequest without ViewerSession. uID={1}, sID={2}", LogHeader, pUserID, pSceneID); m_log.Debug($"{LogHeader} ProvisionVoiceAccountRequest without ViewerSession. uID={pUserID}, sID={pSceneID}");
return null; return null;
} }
// Received a ProvisionVoiceAccountRequest from a viewer. Forward it to the WebRTC service. // Received a ProvisionVoiceAccountRequest from a viewer. Forward it to the WebRTC service.
public async Task<OSDMap> ProvisionVoiceAccountRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap ProvisionVoiceAccountRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
m_log.DebugFormat("{0} VoiceSignalingRequest. uID={1}, sID={2}", LogHeader, pUserID, pSceneID); m_log.Debug($"{LogHeader} VoiceSignalingRequest. uID={pUserID}, sID={pSceneID}");
OSDMap req = new OSDMap() OSDMap req = new()
{ {
{ "request", pRequest }, { "request", pRequest },
{ "userID", pUserID.ToString() }, { "userID", pUserID.ToString() },
{ "scene", pSceneID.ToString() } { "scene", pSceneID.ToString() }
}; };
var resp = await JsonRpcRequest("provision_voice_account_request", m_serverURI, req); var resp = JsonRpcRequest("provision_voice_account_request", m_serverURI, req);
// Kludge to sync the viewer session number in our IVoiceViewerSession with the one from the WebRTC service. // Kludge to sync the viewer session number in our IVoiceViewerSession with the one from the WebRTC service.
if (resp.ContainsKey("viewer_session")) if (resp.TryGetString("viewer_session", out string otherViewerSessionId))
{ {
string otherViewerSessionId = resp["viewer_session"].AsString(); m_log.Debug(
m_log.DebugFormat("{0} ProvisionVoiceAccountRequest: syncing viewSessionID. old={1}, new={2}", $"{LogHeader} ProvisionVoiceAccountRequest: syncing viewSessionID. old={pVSession.ViewerSessionID}, new={otherViewerSessionId}");
LogHeader, pVSession.ViewerSessionID, otherViewerSessionId);
VoiceViewerSession.UpdateViewerSessionId(pVSession, otherViewerSessionId); VoiceViewerSession.UpdateViewerSessionId(pVSession, otherViewerSessionId);
} }
return resp; return resp;
} }
public Task<OSDMap> VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
m_log.DebugFormat("{0} VoiceSignalingRequest without ViewerSession. uID={1}, sID={2}", LogHeader, pUserID, pSceneID); m_log.Debug($"{LogHeader} VoiceSignalingRequest without ViewerSession. uID={pUserID}, sID={pSceneID}");
return null; return null;
} }
public Task<OSDMap> VoiceSignalingRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap VoiceSignalingRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
m_log.DebugFormat("{0} VoiceSignalingRequest. uID={1}, sID={2}", LogHeader, pUserID, pSceneID); m_log.DebugFormat("{0} VoiceSignalingRequest. uID={1}, sID={2}", LogHeader, pUserID, pSceneID);
OSDMap req = new OSDMap() OSDMap req = new()
{ {
{ "request", pRequest }, { "request", pRequest },
{ "userID", pUserID.ToString() }, { "userID", pUserID.ToString() },
@@ -135,16 +132,13 @@ namespace osWebRtcVoice
return JsonRpcRequest("voice_signaling_request", m_serverURI, req); return JsonRpcRequest("voice_signaling_request", m_serverURI, req);
} }
public Task<OSDMap> JsonRpcRequest(string method, string uri, OSDMap pParams) public OSDMap JsonRpcRequest(string method, string uri, OSDMap pParams)
{ {
string jsonId = UUID.Random().ToString(); string jsonId = UUID.Random().ToString();
if(string.IsNullOrWhiteSpace(uri)) if(string.IsNullOrWhiteSpace(uri))
return null; return null;
TaskCompletionSource<OSDMap> tcs = new TaskCompletionSource<OSDMap>();
_ = Task.Run(() =>
{
OSDMap request = new() OSDMap request = new()
{ {
{ "jsonrpc", OSD.FromString("2.0") }, { "jsonrpc", OSD.FromString("2.0") },
@@ -156,61 +150,54 @@ namespace osWebRtcVoice
OSDMap outerResponse = null; OSDMap outerResponse = null;
try try
{ {
if (m_MessageDetails) m_log.DebugFormat("{0}: request: {1}", LogHeader, request); if (m_MessageDetails) m_log.Debug($"{LogHeader}: request: {request}");
outerResponse = WebUtil.PostToService(uri, request, 10000, true); outerResponse = WebUtil.PostToService(uri, request, 10000, true);
if (m_MessageDetails) m_log.DebugFormat("{0}: response: {1}", LogHeader, outerResponse);
if (m_MessageDetails) m_log.Debug($"{LogHeader}: response: {outerResponse}");
} }
catch (Exception e) catch (Exception e)
{ {
m_log.ErrorFormat("{0}: JsonRpc request '{1}' to {2} failed: {3}", LogHeader, method, uri, e); m_log.Error($"{LogHeader}: JsonRpc request '{method}' to {uri} failed: {e.Message}");
m_log.DebugFormat("{0}: request: {1}", LogHeader, request); m_log.Debug($"{LogHeader}: request: {request}");
tcs.SetResult(new OSDMap() return new OSDMap()
{ {
{ "error", OSD.FromString(e.Message) } { "error", OSD.FromString(e.Message) }
}); };
}
if (!outerResponse.TryGetOSDMap("_Result", out OSDMap response))
{
string errm = $"JsonRpc request '{method}' to {1} returned an invalid response: {OSDParser.SerializeJsonString(outerResponse)}";
m_log.Error(errm);
return new OSDMap()
{
{ "error", errm }
};
} }
OSD osdtmp; OSD osdtmp;
if (!outerResponse.TryGetValue("_Result", out osdtmp) || (osdtmp is not OSDMap))
{
string errm = String.Format("JsonRpc request '{0}' to {1} returned an invalid response: {2}",
method, uri, OSDParser.SerializeJsonString(outerResponse));
m_log.ErrorFormat(errm);
tcs.SetResult(new OSDMap()
{
{ "error", errm }
});
}
OSDMap response = osdtmp as OSDMap;
if (response.TryGetValue("error", out osdtmp)) if (response.TryGetValue("error", out osdtmp))
{ {
string errm = String.Format("JsonRpc request '{0}' to {1} returned an error: {2}", string errm = $"JsonRpc request '{method}' to {uri} returned an error: {OSDParser.SerializeJsonString(osdtmp)}";
method, uri, OSDParser.SerializeJsonString(osdtmp)); m_log.Error(errm);
m_log.ErrorFormat(errm); return new OSDMap()
tcs.SetResult(new OSDMap()
{ {
{ "error", errm } { "error", errm }
}); };
} }
OSDMap resultmap = null; if (!response.TryGetOSDMap("result", out OSDMap resultmap ))
if (!response.TryGetValue("result", out osdtmp) || (osdtmp is not OSDMap))
{ {
string errm = String.Format("JsonRpc request '{0}' to {1} returned result as non-OSDMap: {2}", string errm = $"JsonRpc request '{method}' to {uri} returned result as non-OSDMap: {OSDParser.SerializeJsonString(outerResponse)}";
method, uri, OSDParser.SerializeJsonString(outerResponse)); m_log.Error(errm);
m_log.ErrorFormat(errm); return new OSDMap()
tcs.SetResult(new OSDMap()
{ {
{ "error", errm } { "error", errm }
}); };
} }
resultmap = osdtmp as OSDMap;
tcs.SetResult(resultmap); return resultmap;
});
return tcs.Task;
} }
} }

View File

@@ -28,8 +28,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text;
using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using Mono.Addins; using Mono.Addins;
@@ -306,7 +304,7 @@ namespace osWebRtcVoice
} }
// The checks passed. Send the request to the voice service. // The checks passed. Send the request to the voice service.
OSDMap resp = voiceService.ProvisionVoiceAccountRequest(map, agentID, scene.RegionInfo.RegionID).Result; OSDMap resp = voiceService.ProvisionVoiceAccountRequest(map, agentID, scene.RegionInfo.RegionID);
if(resp is not null) if(resp is not null)
{ {
@@ -364,7 +362,7 @@ namespace osWebRtcVoice
} }
} }
OSDMap resp = voiceService.VoiceSignalingRequest(map, agentID, scene.RegionInfo.RegionID).Result; OSDMap resp = voiceService.VoiceSignalingRequest(map, agentID, scene.RegionInfo.RegionID);
if (_MessageDetails) m_log.Debug($"{logHeader}[VoiceSignalingRequest]: Response: {resp}"); if (_MessageDetails) m_log.Debug($"{logHeader}[VoiceSignalingRequest]: Response: {resp}");

View File

@@ -209,7 +209,7 @@ namespace osWebRtcVoice
// IWebRtcVoiceService // IWebRtcVoiceService
// IWebRtcVoiceService.ProvisionVoiceAccountRequest // IWebRtcVoiceService.ProvisionVoiceAccountRequest
public async Task<OSDMap> ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
OSDMap response = null; OSDMap response = null;
IVoiceViewerSession vSession = null; IVoiceViewerSession vSession = null;
@@ -246,13 +246,13 @@ namespace osWebRtcVoice
} }
if (vSession is not null) if (vSession is not null)
{ {
response = await vSession.VoiceService.ProvisionVoiceAccountRequest(vSession, pRequest, pUserID, pSceneID); response = vSession.VoiceService.ProvisionVoiceAccountRequest(vSession, pRequest, pUserID, pSceneID);
} }
return response; return response;
} }
// IWebRtcVoiceService.VoiceSignalingRequest // IWebRtcVoiceService.VoiceSignalingRequest
public async Task<OSDMap> VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
OSDMap response = null; OSDMap response = null;
IVoiceViewerSession vSession = null; IVoiceViewerSession vSession = null;
@@ -261,7 +261,7 @@ namespace osWebRtcVoice
// request has a viewer session. Use that to find the voice service // request has a viewer session. Use that to find the voice service
if (VoiceViewerSession.TryGetViewerSession(viewerSessionId, out vSession)) if (VoiceViewerSession.TryGetViewerSession(viewerSessionId, out vSession))
{ {
response = await vSession.VoiceService.VoiceSignalingRequest(vSession, pRequest, pUserID, pSceneID); response = vSession.VoiceService.VoiceSignalingRequest(vSession, pRequest, pUserID, pSceneID);
} }
else else
{ {
@@ -276,13 +276,13 @@ namespace osWebRtcVoice
} }
// This module should never be called with this signature // This module should never be called with this signature
public Task<OSDMap> ProvisionVoiceAccountRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap ProvisionVoiceAccountRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
// This module should never be called with this signature // This module should never be called with this signature
public Task<OSDMap> VoiceSignalingRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID) public OSDMap VoiceSignalingRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@@ -526,7 +526,7 @@ namespace OpenSim.Framework
{ {
OSHHTPHost tmp = new OSHHTPHost(stuns[i].Trim(), false); OSHHTPHost tmp = new OSHHTPHost(stuns[i].Trim(), false);
if (tmp.IsValidHost) if (tmp.IsValidHost)
stunsarr.Add(tmp.URI); stunsarr.Add("stun:" + tmp.HostAndPort);
} }
m_StunServers = stunsarr.Count > 0 ? stunsarr.ToArray() : null; m_StunServers = stunsarr.Count > 0 ? stunsarr.ToArray() : null;
} }
@@ -785,7 +785,7 @@ namespace OpenSim.Framework
{ {
OSHHTPHost tmp = new OSHHTPHost(value[i].Trim(), false); OSHHTPHost tmp = new OSHHTPHost(value[i].Trim(), false);
if (tmp.IsValidHost) if (tmp.IsValidHost)
values.Add(tmp.URI); values.Add("stun:" + tmp.HostAndPort);
} }
m_StunServers = values.Count > 0 ? values.ToArray() : null; m_StunServers = values.Count > 0 ? values.ToArray() : null;
} }