diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs
index b1c46717..dc2fe6c7 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs
@@ -562,6 +562,59 @@ namespace PepperDash_Essentials_Core.Bridges.JoinMaps
JoinType = eJoinType.Digital
});
+ [JoinName("SourceShareStart")] public JoinDataComplete SourceShareStart =
+ new JoinDataComplete(new JoinData {JoinNumber = 201, JoinSpan = 1},
+ new JoinMetadata
+ {
+ Description = "Start Sharing & Feedback",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("SourceShareEnd")]
+ public JoinDataComplete SourceShareEnd =
+ new JoinDataComplete(new JoinData { JoinNumber = 202, JoinSpan = 1 },
+ new JoinMetadata
+ {
+ Description = "Stop Sharing & Feedback",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("AutoShareWhileInCall")] public JoinDataComplete SourceShareAutoStart =
+ new JoinDataComplete(new JoinData {JoinNumber = 203, JoinSpan = 1},
+ new JoinMetadata
+ {
+ Description = "When high, will autostart sharing when a call is joined",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ [JoinName("CurrentSource")] public JoinDataComplete CurrentSource = new JoinDataComplete(new JoinData{JoinNumber = 201, JoinSpan = 1},
+ new JoinMetadata
+ {
+ Description = "Current Source",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("CurrentParticipants")] public JoinDataComplete CurrentParticipants =
+ new JoinDataComplete(new JoinData{JoinNumber = 151, JoinSpan = 1},
+ new JoinMetadata()
+ {
+ Description = "Current Participants XSig",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ [JoinName("ParticipantCount")] public JoinDataComplete ParticipantCount = new JoinDataComplete(new JoinData{JoinNumber = 151, JoinSpan = 1},
+ new JoinMetadata
+ {
+ Description = "Current Participant Count",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
public VideoCodecControllerJoinMap(uint joinStart) : base(joinStart, typeof (VideoCodecControllerJoinMap))
{
}
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
index d3d468fc..94988b7b 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
@@ -120,6 +120,7 @@
+
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs
new file mode 100644
index 00000000..e0f1d1a3
--- /dev/null
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces
+{
+ public interface IHasParticipants
+ {
+ CodecParticipants Participants { get; }
+ }
+
+ public interface IHasParticipantVideoMute:IHasParticipants
+ {
+ void MuteVideoForParticipant(int userId);
+ void UnmuteVideoForParticipant(int userId);
+ void ToggleVideoForParticipant(int userId);
+ }
+
+ public interface IHasParticipantAudioMute:IHasParticipantVideoMute
+ {
+ void MuteAudioForParticipant(int userId);
+ void UnmuteAudioForParticipant(int userId);
+ void ToggleAudioForParticipant(int userId);
+ }
+
+ public class CodecParticipants
+ {
+ private List _currentParticipants;
+
+ public List CurrentParticipants {
+ get { return _currentParticipants; }
+ set
+ {
+ _currentParticipants = value;
+ var handler = ParticipantsListHasChanged;
+
+ if(handler == null) return;
+
+ handler(this, new EventArgs());
+ }
+ }
+
+ public event EventHandler ParticipantsListHasChanged;
+
+ public CodecParticipants()
+ {
+ _currentParticipants = new List();
+ }
+ }
+
+ public class Participant
+ {
+ public bool IsHost { get; set; }
+ public string Name { get; set; }
+ public bool CanMuteVideo { get; set; }
+ public bool CanUnmuteVideo { get; set; }
+ public bool VideoMuteFb { get; set; }
+ public bool AudioMuteFb { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs
index fb330049..87ac8581 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs
@@ -16,6 +16,7 @@ using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Essentials.Devices.Common.Cameras;
using PepperDash.Essentials.Devices.Common.Codec;
+using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces;
using PepperDash_Essentials_Core.Bridges.JoinMaps;
using Feedback = PepperDash.Essentials.Core.Feedback;
@@ -25,6 +26,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced
{
private const int XSigEncoding = 28591;
+ private readonly byte[] _clearBytes = XSigHelpers.ClearOutputs();
protected VideoCodecBase(DeviceConfig config)
: base(config)
{
@@ -287,6 +289,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
LinkVideoCodecCallControlsToApi(trilist, joinMap);
+ LinkVideoCodecContentSharingToApi(trilist, joinMap);
+
if (codec is IHasCodecCameras)
{
LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap);
@@ -317,8 +321,85 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
{
LinkVideoCodecScheduleToApi(codec as IHasScheduleAwareness, trilist, joinMap);
}
+
+ if (codec is IHasParticipants)
+ {
+ LinkVideoCodecParticipantsToApi(codec as IHasParticipants, trilist, joinMap);
+ }
}
+ private void LinkVideoCodecParticipantsToApi(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
+ {
+ codec.Participants.ParticipantsListHasChanged += (sender, args) =>
+ {
+ string participantsXSig;
+ if (codec.Participants.CurrentParticipants.Count == 0)
+ {
+ participantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length);
+ trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig);
+ trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count);
+ return;
+ }
+
+ participantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length);
+
+ trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig);
+
+ participantsXSig = UpdateParticipantsXSig(codec.Participants.CurrentParticipants);
+
+ trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig);
+
+ trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort) codec.Participants.CurrentParticipants.Count);
+ };
+ }
+
+ private string UpdateParticipantsXSig(List currentParticipants)
+ {
+ const int maxParticipants = 255;
+ const int maxDigitals = 5;
+ const int maxStrings = 1;
+ const int offset = maxDigitals + maxStrings;
+ var digitalIndex = maxStrings * maxParticipants; //15
+ var stringIndex = 0;
+ var meetingIndex = 0;
+
+ var tokenArray = new XSigToken[maxParticipants * offset];
+
+ foreach (var participant in currentParticipants)
+ {
+ if (meetingIndex > maxParticipants * offset) break;
+
+ //digitals
+ tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, participant.AudioMuteFb);
+ tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, participant.VideoMuteFb);
+ tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, participant.CanMuteVideo);
+ tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, participant.CanUnmuteVideo);
+ tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, participant.IsHost);
+
+ //serials
+ tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, participant.Name);
+
+ digitalIndex += maxDigitals;
+ meetingIndex += offset;
+ stringIndex += maxStrings;
+ }
+
+ return GetXSigString(tokenArray);
+ }
+
+ private void LinkVideoCodecContentSharingToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
+ {
+ SharingContentIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SourceShareStart.JoinNumber]);
+ SharingContentIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.SourceShareEnd.JoinNumber]);
+
+ SharingSourceFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentSource.JoinNumber]);
+
+ trilist.SetSigFalseAction(joinMap.SourceShareStart.JoinNumber, StartSharing);
+ trilist.SetSigFalseAction(joinMap.SourceShareEnd.JoinNumber, StopSharing);
+
+ trilist.SetBoolSigAction(joinMap.SourceShareAutoStart.JoinNumber, (b) => AutoShareContentWhileInCall = b);
+ }
+
private void LinkVideoCodecScheduleToApi(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap)
{
trilist.SetSigFalseAction(joinMap.UpdateMeetings.JoinNumber, codec.GetSchedule);
@@ -341,6 +422,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
Dial(codec.CodecSchedule.Meetings[0]);
}
});
+
+ trilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort) codec.CodecSchedule.Meetings.Count);
};
}
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs
index c81a47cb..3425e45b 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs
@@ -11,6 +11,7 @@ using PepperDash.Essentials.Devices.Common.Codec;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces;
namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
{
@@ -909,7 +910,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
///
public class zCommand
{
- public partial class BookingsListResult
+ public class BookingsListResult
{
[JsonProperty("accessRole")]
public string AccessRole { get; set; }
@@ -1072,6 +1073,23 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
{
HandStatus = new HandStatus();
}
+
+ public static List GetGenericParticipantListFromParticipantsResult(
+ List participants)
+ {
+ return
+ participants.Select(
+ p =>
+ new Participant
+ {
+ Name = p.UserName,
+ IsHost = p.IsHost,
+ CanMuteVideo = p.IsVideoCanMuteByHost,
+ CanUnmuteVideo = p.IsVideoCanUnmuteByHost,
+ AudioMuteFb = p.AudioStatusState == "AUDIO_MUTED",
+ VideoMuteFb = p.VideoStatusIsSending
+ }).ToList();
+ }
}
public class CallinCountryList
diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs
index 045d6482..14ef2d55 100644
--- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs
+++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs
@@ -14,12 +14,13 @@ using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Essentials.Devices.Common.Cameras;
using PepperDash.Essentials.Devices.Common.Codec;
+using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces;
namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
{
public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectoryHistoryStack, ICommunicationMonitor,
IRouting,
- IHasScheduleAwareness, IHasCodecCameras
+ IHasScheduleAwareness, IHasCodecCameras, IHasParticipants
{
private const uint DefaultMeetingDurationMin = 30;
private const string Delimiter = "\x0D\x0A";
@@ -94,6 +95,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
Cameras = new List();
SetUpDirectory();
+
+ Participants = new CodecParticipants();
}
public CommunicationGather PortGather { get; private set; }
@@ -827,51 +830,61 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
{
Debug.Console(1, this, "JTokenType: {0}", responseObj.Type);
- if (responseObj.Type == JTokenType.Array)
+ switch (responseObj.Type)
{
- // if the type is array this must be the complete list
- Status.Call.Participants =
- JsonConvert.DeserializeObject>(
- responseObj.ToString());
- }
- else if (responseObj.Type == JTokenType.Object)
- {
- // this is a single participant event notification
-
- var participant =
- JsonConvert.DeserializeObject(responseObj.ToString());
-
- if (participant != null)
+ case JTokenType.Array:
+ Status.Call.Participants =
+ JsonConvert.DeserializeObject>(
+ responseObj.ToString());
+ break;
+ case JTokenType.Object:
{
- if (participant.Event == "ZRCUserChangedEventLeftMeeting" ||
- participant.Event == "ZRCUserChangedEventUserInfoUpdated")
- {
- var existingParticipant =
- Status.Call.Participants.FirstOrDefault(
- p => p.UserId.Equals(participant.UserId));
+ // this is a single participant event notification
- if (existingParticipant != null)
+ var participant =
+ JsonConvert.DeserializeObject(responseObj.ToString());
+
+ if (participant != null)
+ {
+ switch (participant.Event)
{
- if (participant.Event == "ZRCUserChangedEventLeftMeeting")
+ case "ZRCUserChangedEventUserInfoUpdated":
+ case "ZRCUserChangedEventLeftMeeting":
{
- // Remove participant
- Status.Call.Participants.Remove(existingParticipant);
- }
- else if (participant.Event == "ZRCUserChangedEventUserInfoUpdated")
- {
- // Update participant
- JsonConvert.PopulateObject(responseObj.ToString(),
- existingParticipant);
+ var existingParticipant =
+ Status.Call.Participants.FirstOrDefault(
+ p => p.UserId.Equals(participant.UserId));
+
+ if (existingParticipant != null)
+ {
+ switch (participant.Event)
+ {
+ case "ZRCUserChangedEventLeftMeeting":
+ Status.Call.Participants.Remove(existingParticipant);
+ break;
+ case "ZRCUserChangedEventUserInfoUpdated":
+ JsonConvert.PopulateObject(responseObj.ToString(),
+ existingParticipant);
+ break;
+ }
+ }
}
+ break;
+ case "ZRCUserChangedEventJoinedMeeting":
+ Status.Call.Participants.Add(participant);
+ break;
}
}
- else if (participant.Event == "ZRCUserChangedEventJoinedMeeting")
- {
- Status.Call.Participants.Add(participant);
- }
}
+ break;
}
+ var participants =
+ zCommand.ListParticipant.GetGenericParticipantListFromParticipantsResult(
+ Status.Call.Participants);
+
+ Participants.CurrentParticipants = participants;
+
PrintCurrentCallParticipants();
break;
@@ -1019,6 +1032,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
OnCallStatusChange(activeCall);
}
}
+ var emptyList = new List();
+ Participants.CurrentParticipants = emptyList;
}
UpdateCallStatus();
@@ -1175,16 +1190,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
public void PrintCurrentCallParticipants()
{
- if (Debug.Level > 0)
+ if (Debug.Level <= 0)
{
- Debug.Console(1, this, "****************************Call Participants***************************");
- foreach (var participant in Status.Call.Participants)
- {
- Debug.Console(1, this, "Name: {0} Audio: {1} IsHost: {2}", participant.UserName,
- participant.AudioStatusState, participant.IsHost);
- }
- Debug.Console(1, this, "************************************************************************");
+ return;
}
+
+ Debug.Console(1, this, "****************************Call Participants***************************");
+ foreach (var participant in Participants.CurrentParticipants)
+ {
+ Debug.Console(1, this, "Name: {0} Audio: {1} IsHost: {2}", participant.Name,
+ participant.AudioMuteFb, participant.IsHost);
+ }
+ Debug.Console(1, this, "************************************************************************");
}
///
@@ -1226,13 +1243,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
{
var existingCall = ActiveCalls.FirstOrDefault(c => !c.Status.Equals(eCodecCallStatus.Ringing));
- if (callStatus == zStatus.eCallStatus.IN_MEETING)
+ switch (callStatus)
{
- existingCall.Status = eCodecCallStatus.Connected;
- }
- else if (callStatus == zStatus.eCallStatus.NOT_IN_MEETING)
- {
- existingCall.Status = eCodecCallStatus.Disconnected;
+ case zStatus.eCallStatus.IN_MEETING:
+ existingCall.Status = eCodecCallStatus.Connected;
+ break;
+ case zStatus.eCallStatus.NOT_IN_MEETING:
+ existingCall.Status = eCodecCallStatus.Disconnected;
+ break;
}
OnCallStatusChange(existingCall);
@@ -1525,6 +1543,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom
{
// TODO: set up far end cameras for the current call
}
+
+ #region Implementation of IHasParticipants
+
+ public CodecParticipants Participants { get; private set; }
+
+ #endregion
}
///