From 88263ccc77ef8a80381d4d6a9b19bea033d42220 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 23 Sep 2020 15:54:11 -0600 Subject: [PATCH] finished up with some things --- .../JoinMaps/VideoCodecControllerJoinMap.cs | 53 ++++++++ .../Essentials Devices Common.csproj | 1 + .../VideoCodec/Interfaces/IHasParticipants.cs | 59 +++++++++ .../VideoCodec/VideoCodecBase.cs | 83 ++++++++++++ .../VideoCodec/ZoomRoom/ResponseObjects.cs | 20 ++- .../VideoCodec/ZoomRoom/ZoomRoom.cs | 122 +++++++++++------- 6 files changed, 288 insertions(+), 50 deletions(-) create mode 100644 essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs 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 } ///