mirror of
https://github.com/opensim/opensim.git
synced 2026-05-13 01:46:07 +08:00
webrtc change ID and desc of rooms, add pin to multiuser
This commit is contained in:
@@ -84,12 +84,12 @@ namespace osWebRtcVoice
|
||||
/// <param name="pSpatial">boolean on whether room will be spatial or non-spatial</param>
|
||||
/// <param name="pRoomDesc">added as "description" to the created room</param>
|
||||
/// <returns></returns>
|
||||
public async Task<JanusRoom> CreateRoom(int pRoomId, bool pSpatial, string pRoomDesc)
|
||||
public async Task<JanusRoom> CreateRoom(int pRoomId, bool pSpatial, string pRoomDesc, string credentials)
|
||||
{
|
||||
JanusRoom ret = null;
|
||||
try
|
||||
{
|
||||
JanusMessageResp resp = await SendPluginMsg(new AudioBridgeCreateRoomReq(pRoomId, pSpatial, pRoomDesc)).ConfigureAwait(false);
|
||||
JanusMessageResp resp = await SendPluginMsg(new AudioBridgeCreateRoomReq(pRoomId, pSpatial, pRoomDesc, credentials)).ConfigureAwait(false);
|
||||
AudioBridgeResp abResp = new(resp);
|
||||
|
||||
m_log.Debug($"{LogHeader} CreateRoom. ReturnCode: '{abResp.AudioBridgeReturnCode}'");
|
||||
@@ -156,7 +156,7 @@ namespace osWebRtcVoice
|
||||
// The attempt is to deterministicly create a room number so all regions will generate the
|
||||
// same room number across sessions and across the grid.
|
||||
// getHashCode() is not deterministic across sessions.
|
||||
public static int CalcRoomNumber(string pRegionId, string pChannelType, int pParcelLocalID, string pChannelID)
|
||||
public static int CalcRoomNumber(string regionID, string gridhash, string pChannelType, int pParcelLocalID, string pChannelID)
|
||||
{
|
||||
BHasherMdjb2 hasher = new();
|
||||
// If there is a channel specified it must be group
|
||||
@@ -164,13 +164,12 @@ namespace osWebRtcVoice
|
||||
{
|
||||
case "local":
|
||||
// A "local" channel is unique to the region and parcel
|
||||
hasher.Add(pRegionId);
|
||||
hasher.Add(regionID);
|
||||
hasher.Add(pChannelType);
|
||||
hasher.Add(pParcelLocalID);
|
||||
break;
|
||||
case "multiagent":
|
||||
// A "multiagent" channel is unique to the grid
|
||||
// should add a GridId here
|
||||
hasher.Add(gridhash);
|
||||
hasher.Add(pChannelID);
|
||||
hasher.Add(pChannelType);
|
||||
break;
|
||||
@@ -184,61 +183,61 @@ namespace osWebRtcVoice
|
||||
return roomNumber;
|
||||
}
|
||||
|
||||
public async Task<JanusRoom> SelectRoom(string pRegionId, string pChannelType, bool pSpatial, int pParcelLocalID, string pChannelID)
|
||||
public async Task<JanusRoom> SelectRoom(string pRegionId, string pgridhash, string pChannelType, bool pSpatial, int pParcelLocalID, string pChannelID, string credentials)
|
||||
{
|
||||
int roomNumber = CalcRoomNumber(pRegionId, pChannelType, pParcelLocalID, pChannelID);
|
||||
int roomNumber = CalcRoomNumber(pRegionId, pgridhash, pChannelType, pParcelLocalID, pChannelID);
|
||||
|
||||
// Should be unique for the given use and channel type
|
||||
m_log.DebugFormat("{0} SelectRoom: roomNumber={1}", LogHeader, roomNumber);
|
||||
m_log.Debug($"{LogHeader} SelectRoom: roomNumber={roomNumber}");
|
||||
|
||||
// Check to see if the room has already been created
|
||||
JanusRoom existingRoom;
|
||||
lock (_rooms)
|
||||
{
|
||||
if (_rooms.ContainsKey(roomNumber))
|
||||
if (_rooms.TryGetValue(roomNumber, out existingRoom))
|
||||
{
|
||||
return _rooms[roomNumber];
|
||||
return existingRoom;
|
||||
}
|
||||
}
|
||||
|
||||
// The room doesn't exist. Create it.
|
||||
string roomDesc = pRegionId + "/" + pChannelType + "/" + pParcelLocalID + "/" + pChannelID;
|
||||
JanusRoom ret = await CreateRoom(roomNumber, pSpatial, roomDesc).ConfigureAwait(false);
|
||||
string roomDesc;
|
||||
if(pChannelType == "local")
|
||||
roomDesc = $"{pRegionId}/{pChannelType}/{pParcelLocalID}";
|
||||
else
|
||||
roomDesc = $"{pgridhash}/{pChannelType}/{pChannelID}";
|
||||
|
||||
JanusRoom ret = await CreateRoom(roomNumber, pSpatial, roomDesc, credentials).ConfigureAwait(false);
|
||||
|
||||
JanusRoom existingRoom = null;
|
||||
if (ret is not null)
|
||||
{
|
||||
lock (_rooms)
|
||||
{
|
||||
if (_rooms.ContainsKey(roomNumber))
|
||||
if(!_rooms.TryGetValue(roomNumber, out existingRoom))
|
||||
{
|
||||
// If the room was created while we were waiting,
|
||||
existingRoom = _rooms[roomNumber];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Our room is the first one created. Save it.
|
||||
_rooms[roomNumber] = ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (existingRoom is not null)
|
||||
{
|
||||
// The room we created was already created by someone else. Delete ours and use the existing one
|
||||
await DestroyRoom(ret);
|
||||
ret = existingRoom;
|
||||
await DestroyRoom(ret).ConfigureAwait(false);
|
||||
return existingRoom;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Return the room with the given room ID or 'null' if no such room
|
||||
public JanusRoom GetRoom(int pRoomId)
|
||||
public bool GetRoom(int pRoomId, out JanusRoom room)
|
||||
{
|
||||
JanusRoom ret = null;
|
||||
lock (_rooms)
|
||||
{
|
||||
_rooms.TryGetValue(pRoomId, out ret);
|
||||
return _rooms.TryGetValue(pRoomId, out room);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override void Handle_Event(JanusMessageResp pResp)
|
||||
@@ -250,8 +249,8 @@ namespace osWebRtcVoice
|
||||
// An audio bridge event!
|
||||
m_log.DebugFormat("{0} Handle_Event. {1}", LogHeader, abResp.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void Handle_Message(JanusMessageResp pResp)
|
||||
{
|
||||
base.Handle_Message(pResp);
|
||||
@@ -261,7 +260,6 @@ namespace osWebRtcVoice
|
||||
// An audio bridge event!
|
||||
m_log.DebugFormat("{0} Handle_Event. {1}", LogHeader, abResp.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,11 +514,11 @@ namespace osWebRtcVoice
|
||||
// ==============================================================
|
||||
public class AudioBridgeCreateRoomReq : PluginMsgReq
|
||||
{
|
||||
public AudioBridgeCreateRoomReq(int pRoomId) : this(pRoomId, false, null)
|
||||
public AudioBridgeCreateRoomReq(int pRoomId) : this(pRoomId, false, null, null)
|
||||
{
|
||||
}
|
||||
|
||||
public AudioBridgeCreateRoomReq(int pRoomId, bool pSpatial, string pDesc) : base(new OSDMap() {
|
||||
public AudioBridgeCreateRoomReq(int pRoomId, bool pSpatial, string pDesc, string credentials) : base(new OSDMap() {
|
||||
{ "room", pRoomId },
|
||||
{ "request", "create" },
|
||||
{ "is_private", false },
|
||||
@@ -531,6 +531,8 @@ namespace osWebRtcVoice
|
||||
{
|
||||
if (!string.IsNullOrEmpty(pDesc))
|
||||
AddStringToBody("description", pDesc);
|
||||
if (!string.IsNullOrEmpty(credentials))
|
||||
AddStringToBody("pin", credentials);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ namespace osWebRtcVoice
|
||||
|
||||
public async Task<bool> JoinRoom(JanusViewerSession pVSession)
|
||||
{
|
||||
bool ret = false;
|
||||
try
|
||||
{
|
||||
// m_log.DebugFormat("{0} JoinRoom. New joinReq for room {1}", LogHeader, RoomId);
|
||||
@@ -79,10 +78,11 @@ namespace osWebRtcVoice
|
||||
{
|
||||
pVSession.ParticipantId = joinResp.ParticipantId;
|
||||
pVSession.Answer = joinResp.Jsep;
|
||||
ret = true;
|
||||
m_log.Debug($"{LogHeader} JoinRoom. Joined room {RoomId}. Participant={pVSession.ParticipantId}");
|
||||
return true;
|
||||
}
|
||||
else if (joinResp is not null && joinResp.AudioBridgeErrorCode == 491)
|
||||
|
||||
if (joinResp is not null && (joinResp.AudioBridgeErrorCode == 490 || joinResp.AudioBridgeErrorCode == 491))
|
||||
{
|
||||
m_log.Warn($"{LogHeader} JoinRoom. Already in a room for agent {pVSession.AgentId}. Attempting recovery.");
|
||||
|
||||
@@ -98,13 +98,11 @@ namespace osWebRtcVoice
|
||||
{
|
||||
pVSession.ParticipantId = retryJoinResp.ParticipantId;
|
||||
pVSession.Answer = retryJoinResp.Jsep;
|
||||
ret = true;
|
||||
m_log.Info($"{LogHeader} JoinRoom. Recovery succeeded for room {RoomId}. Participant={pVSession.ParticipantId}");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.Error($"{LogHeader} JoinRoom. Recovery retry failed for room {RoomId}. Resp={retryJoinResp?.ToString() ?? "null"}");
|
||||
}
|
||||
|
||||
m_log.Error($"{LogHeader} JoinRoom. Recovery retry failed for room {RoomId}. Resp={retryJoinResp?.ToString() ?? "null"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -120,18 +118,19 @@ namespace osWebRtcVoice
|
||||
if (m_log.IsDebugEnabled)
|
||||
m_log.DebugFormat("{0} JoinRoom. Invalid participant detail: {1}", LogHeader, joinResp.ToString());
|
||||
}
|
||||
|
||||
m_log.ErrorFormat("{0} JoinRoom. Failed to join room {1}", LogHeader, RoomId);
|
||||
if (m_log.IsDebugEnabled)
|
||||
m_log.DebugFormat("{0} JoinRoom. Failure detail: {1}", LogHeader, joinResp?.ToString() ?? "null");
|
||||
|
||||
else
|
||||
{
|
||||
m_log.ErrorFormat("{0} JoinRoom. Failed to join room {1}", LogHeader, RoomId);
|
||||
if (m_log.IsDebugEnabled)
|
||||
m_log.DebugFormat("{0} JoinRoom. Failure detail: {1}", LogHeader, joinResp?.ToString() ?? "null");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error($"{LogHeader} JoinRoom. Exception ", e);
|
||||
}
|
||||
return ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> RecoverAlreadyInRoomAndLeave(string pDisplay)
|
||||
@@ -222,7 +221,6 @@ namespace osWebRtcVoice
|
||||
|
||||
public async Task<bool> LeaveRoom(JanusViewerSession pAttendeeSession)
|
||||
{
|
||||
bool ret = false;
|
||||
try
|
||||
{
|
||||
JanusMessageResp resp = await _AudioBridge.SendPluginMsg(
|
||||
@@ -238,33 +236,31 @@ namespace osWebRtcVoice
|
||||
string returnCode = abResp.AudioBridgeReturnCode;
|
||||
string janusReturnCode = resp.ReturnCode;
|
||||
int errorCode = abResp.AudioBridgeErrorCode;
|
||||
bool isBenignAlreadyLeft = errorCode == 487 &&
|
||||
(returnCode == "event" || janusReturnCode == "event" || janusReturnCode == "ack");
|
||||
|
||||
if (errorCode == 0 &&
|
||||
(abResp.isSuccess || returnCode == "left" || returnCode == "event" || returnCode == "success" || janusReturnCode == "ack"))
|
||||
{
|
||||
ret = true;
|
||||
if (janusReturnCode == "ack" && string.IsNullOrEmpty(returnCode))
|
||||
{
|
||||
m_log.Debug($"{LogHeader} LeaveRoom. Ack accepted for room {RoomId}, participant={pAttendeeSession.ParticipantId}");
|
||||
m_log.Debug($"{LogHeader} LeaveRoom. Ack for room {RoomId}, participant={pAttendeeSession.ParticipantId}");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (isBenignAlreadyLeft)
|
||||
|
||||
if (errorCode == 487 &&
|
||||
(returnCode == "event" || janusReturnCode == "event" || janusReturnCode == "ack"))
|
||||
{
|
||||
ret = true;
|
||||
m_log.Info($"{LogHeader} LeaveRoom. Participant already left room {RoomId}, participant={pAttendeeSession.ParticipantId} (errorCode=487)");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.Error($"{LogHeader} LeaveRoom. Failed room {RoomId}, participant={pAttendeeSession.ParticipantId}, janus={janusReturnCode}, audiobridge={returnCode}, errorCode={errorCode}");
|
||||
}
|
||||
|
||||
m_log.Error($"{LogHeader} LeaveRoom. Failed room {RoomId}, participant={pAttendeeSession.ParticipantId}, janus={janusReturnCode}, audiobridge={returnCode}, errorCode={errorCode}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error($"{LogHeader} LeaveRoom. Exception ", e);
|
||||
}
|
||||
return ret;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +119,11 @@ namespace osWebRtcVoice
|
||||
{
|
||||
JanusSession s = Session;
|
||||
Session = null;
|
||||
_ = await s.DestroySession().ConfigureAwait(false);
|
||||
s.Dispose();
|
||||
if(s != null)
|
||||
{
|
||||
_ = await s.DestroySession().ConfigureAwait(false);
|
||||
s.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,12 +164,12 @@ namespace osWebRtcVoice
|
||||
// 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;
|
||||
}
|
||||
_log.Error($"{LogHeader} JanusPluginHandle not created");
|
||||
@@ -245,9 +245,9 @@ namespace osWebRtcVoice
|
||||
_log.InfoFormat("{0} ProvisionVoiceAccountRequest: disconnected by {1}. agent={2}, scene={3}, room={4}, participant={5}, viewer_session={6}",
|
||||
LogHeader, pReason, pViewerSession.AgentId, pViewerSession.RegionId, roomId, pViewerSession.ParticipantId, pViewerSession.ViewerSessionID);
|
||||
|
||||
VoiceViewerSession.RemoveViewerSession(pViewerSession.ViewerSessionID);
|
||||
Task.Run(() =>
|
||||
{
|
||||
VoiceViewerSession.RemoveViewerSession(pViewerSession.ViewerSessionID);
|
||||
// No need to wait for the session to be shutdown
|
||||
_ = pViewerSession.Shutdown();
|
||||
});
|
||||
@@ -265,23 +265,31 @@ namespace osWebRtcVoice
|
||||
|
||||
public async Task<OSDMap> ProvisionVoiceAccountRequestBAD(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
|
||||
{
|
||||
long flowId = Interlocked.Increment(ref _VoiceFlowCounter);
|
||||
if(pRequest.TryGetString("voice_server_type", out string voice_server_type))
|
||||
{
|
||||
if(!"webrtc".Equals(voice_server_type,StringComparison. CurrentCultureIgnoreCase))
|
||||
{
|
||||
_log.Error($"{LogHeader} ProvisionVoiceAccountRequest: invalid server type {voice_server_type ?? "null"}");
|
||||
return new OSDMap
|
||||
{
|
||||
{ "response", "failed" },
|
||||
{ "error", "Invalid server type" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
OSDMap ret = null;
|
||||
string errorMsg = null;
|
||||
JanusViewerSession viewerSession = pSession as JanusViewerSession;
|
||||
long flowId = Interlocked.Increment(ref _VoiceFlowCounter);
|
||||
|
||||
if (viewerSession is not null)
|
||||
{
|
||||
if (viewerSession.Session is null)
|
||||
{
|
||||
// This is a new session so we must create a new session and handle to the audio bridge
|
||||
await ConnectToSessionAndAudioBridge(viewerSession).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// TODO: need to keep count of users in a room to know when to close a room
|
||||
bool isLogout = pRequest.TryGetBool("logout", out bool lgout) && lgout;
|
||||
if (isLogout)
|
||||
{
|
||||
// The client is logging out. Exit the room.
|
||||
// Exit the room.
|
||||
if (viewerSession.Room is not null)
|
||||
{
|
||||
_ = await viewerSession.Room.LeaveRoom(viewerSession).ConfigureAwait(false);
|
||||
@@ -296,14 +304,20 @@ namespace osWebRtcVoice
|
||||
};
|
||||
}
|
||||
|
||||
if (viewerSession.Session is null)
|
||||
{
|
||||
// This is a new session so we must create a new session and handle to the audio bridge
|
||||
await ConnectToSessionAndAudioBridge(viewerSession).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Get the parameters that select the room
|
||||
// To get here, voice_server_type has already been checked to be 'webrtc' and channel_type='local'
|
||||
int parcel_local_id = pRequest.TryGetInt("parcel_local_id", out int pli) ? pli : JanusAudioBridge.REGION_ROOM_ID;
|
||||
string channel_id = pRequest.TryGetString("channel_id", out string cli) ? cli : string.Empty;
|
||||
string channel_credentials = pRequest.TryGetString("credentials", out string cred) ? cred : string.Empty;
|
||||
string channel_type = pRequest["channel_type"].AsString();
|
||||
string gridHash = pRequest.TryGetString("gridhash", out string ghash) ? ghash : string.Empty;
|
||||
bool isSpatial = channel_type == "local";
|
||||
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);
|
||||
|
||||
@@ -320,14 +334,15 @@ namespace osWebRtcVoice
|
||||
{
|
||||
// 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);
|
||||
viewerSession.Room = await viewerSession.AudioBridge.SelectRoom(pSceneID.ToString(),
|
||||
channel_type, isSpatial, parcel_local_id, channel_id).ConfigureAwait(false);
|
||||
viewerSession.Room = await viewerSession.AudioBridge.SelectRoom(pSceneID.ToString(), gridHash,
|
||||
channel_type, isSpatial, parcel_local_id, channel_id, channel_credentials).ConfigureAwait(false);
|
||||
if (viewerSession.Room is null)
|
||||
{
|
||||
errorMsg = "room selection failed";
|
||||
_log.Error($"{LogHeader} ProvisionVoiceAccountRequest: room selection failed");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
viewerSession.Offer = jsepSdp;
|
||||
viewerSession.OfferOrig = jsepSdp;
|
||||
viewerSession.AgentId = pUserID;
|
||||
@@ -399,7 +414,7 @@ namespace osWebRtcVoice
|
||||
JanusViewerSession viewerSession = pSession as JanusViewerSession;
|
||||
JanusMessageResp resp = null;
|
||||
long flowId = Interlocked.Increment(ref _VoiceFlowCounter);
|
||||
if (viewerSession is not null)
|
||||
if (viewerSession is not null && viewerSession.Session is not null)
|
||||
{
|
||||
// The request should be an array of candidates
|
||||
if (pRequest.TryGetOSDMap("candidate", out OSDMap candidate))
|
||||
|
||||
@@ -79,6 +79,8 @@ namespace osWebRtcVoice
|
||||
private IWebRtcVoiceService m_spatialVoiceService;
|
||||
private IWebRtcVoiceService m_nonSpatialVoiceService;
|
||||
|
||||
private UUID gridHash = UUID.Zero;
|
||||
|
||||
// ISharedRegionModule.Initialize
|
||||
public void Initialise(IConfigSource config)
|
||||
{
|
||||
@@ -175,6 +177,14 @@ namespace osWebRtcVoice
|
||||
|
||||
scene.EventManager.OnNewClient += OnNewClient;
|
||||
|
||||
if(gridHash.IsZero())
|
||||
{
|
||||
if(!string.IsNullOrEmpty(scene.SceneGridInfo.GridUrl))
|
||||
gridHash = Util.ComputeShake128UUID(scene.SceneGridInfo.GridUrl + scene.SceneGridInfo.GridName);
|
||||
else if (!string.IsNullOrEmpty(scene.SceneGridInfo.HomeURL + scene.SceneGridInfo.GridName))
|
||||
gridHash = Util.ComputeShake128UUID(scene.SceneGridInfo.HomeURLNoEndSlash);
|
||||
}
|
||||
|
||||
ISimulatorFeaturesModule simFeatures = scene.RequestModuleInterface<ISimulatorFeaturesModule>();
|
||||
simFeatures?.AddFeature("VoiceServerType", OSD.FromString("webrtc"));
|
||||
}
|
||||
@@ -552,7 +562,7 @@ namespace osWebRtcVoice
|
||||
}
|
||||
else
|
||||
{
|
||||
flags = IVoiceViewerSession.VFlags.IsEstate;
|
||||
flags = IVoiceViewerSession.VFlags.IsEstate;
|
||||
}
|
||||
|
||||
// TODO: check if this userId is making a new session (case that user is reconnecting)
|
||||
@@ -583,6 +593,7 @@ namespace osWebRtcVoice
|
||||
{
|
||||
vSession.Flags = IVoiceViewerSession.VFlags.IsAdmin;
|
||||
VoiceViewerSession.AddViewerSession(vSession);
|
||||
map["gridhash"] = gridHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user