private void Receive(object sender, ICommand cmd0)
 {
     if (cmd0 is CameraControlGetCommand cmd)
     {
         LastCommand = cmd;
     }
 }
        private static void FillRandomData(CameraControlGetCommand cmd, params CameraControlDataType[] omitTypes)
        {
            cmd.Input     = (VideoSource)(Randomiser.RangeInt(20) + 50);
            cmd.Category  = Randomiser.RangeInt(10) + 20;
            cmd.Parameter = Randomiser.RangeInt(10) + 15;

            cmd.Type = Randomiser.EnumValue <CameraControlDataType>(omitTypes);
            switch (cmd.Type)
            {
            case CameraControlDataType.Bool:
                cmd.BoolData = Enumerable.Range(0, Randomiser.RangeInt(8, 8))     // TODO - higher has issues
                               .Select(i => Randomiser.Range(0, 10) >= 5)
                               .ToArray();
                break;

            case CameraControlDataType.SInt8:
                cmd.IntData = Enumerable.Range(0, Randomiser.RangeInt(1, 8))     // TODO - higher has issues
                              .Select(i => Randomiser.RangeInt(sbyte.MinValue, sbyte.MaxValue))
                              .ToArray();
                break;

            case CameraControlDataType.SInt16:
                cmd.IntData = Enumerable.Range(0, Randomiser.RangeInt(1, 4))
                              .Select(i => Randomiser.RangeInt(short.MinValue, short.MaxValue))
                              .ToArray();
                break;

            case CameraControlDataType.SInt32:
                cmd.IntData = Enumerable.Range(0, Randomiser.RangeInt(1, 2))
                              .Select(i => Randomiser.RangeInt(-500000, 500000))
                              .ToArray();
                break;

            case CameraControlDataType.SInt64:
                cmd.LongData = Enumerable.Range(0, 1)
                               .Select(i => (long)Randomiser.RangeInt(-5000000, 5000000))
                               .ToArray();
                break;

            case CameraControlDataType.String:
                cmd.StringData = Randomiser.String(32);
                break;

            case CameraControlDataType.Float:
                cmd.FloatData = Enumerable.Range(0, Randomiser.RangeInt(1, 4))
                                .Select(i => Randomiser.Range(0, 1))
                                .ToArray();
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        private static void ApplyOffsets(CameraControlGetCommand getCmd, CameraControlCommandBase deltaCmd)
        {
            Assert.False(deltaCmd.IntData == null && deltaCmd.FloatData == null && deltaCmd.LongData == null && deltaCmd.BoolData == null);
            if (deltaCmd.IntData != null)
            {
                Assert.NotNull(getCmd.IntData);
                Assert.Equal(getCmd.IntData.Length, deltaCmd.IntData.Length);

                var newVals = new int[getCmd.IntData.Length];
                for (int i = 0; i < newVals.Length; i++)
                {
                    newVals[i] = getCmd.IntData[i] + deltaCmd.IntData[i];
                }
                getCmd.IntData = newVals;
            }
            if (deltaCmd.LongData != null)
            {
                Assert.NotNull(getCmd.LongData);
                Assert.Equal(getCmd.LongData.Length, deltaCmd.LongData.Length);

                var newVals = new long[getCmd.LongData.Length];
                for (int i = 0; i < newVals.Length; i++)
                {
                    newVals[i] = getCmd.LongData[i] + deltaCmd.LongData[i];
                }
                getCmd.LongData = newVals;
            }
            if (deltaCmd.FloatData != null)
            {
                Assert.NotNull(getCmd.FloatData);
                Assert.Equal(getCmd.FloatData.Length, deltaCmd.FloatData.Length);

                var newVals = new double[getCmd.FloatData.Length];
                for (int i = 0; i < getCmd.FloatData.Length; i++)
                {
                    newVals[i] = getCmd.FloatData[i] + deltaCmd.FloatData[i];
                }
                getCmd.FloatData = newVals;
            }

            if (deltaCmd.BoolData != null)
            {
                Assert.NotNull(getCmd.BoolData);
                Assert.Equal(getCmd.BoolData.Length, deltaCmd.BoolData.Length);

                var newVals = new bool[getCmd.BoolData.Length];
                for (int i = 0; i < getCmd.BoolData.Length; i++)
                {
                    newVals[i] = deltaCmd.BoolData[i] ? !getCmd.BoolData[i] : getCmd.BoolData[i];
                }
                getCmd.BoolData = newVals;
            }
        }
        private IEnumerable <ICommand> CameraCommandHandler(Lazy <ImmutableList <ICommand> > previousCommands, ICommand cmd)
        {
            if (cmd is CameraControlSetCommand setCmd)
            {
                CameraControlGetCommand getCmd = CopyCommand(setCmd);

                // If relative, we need to be more clever
                if (setCmd.Relative)
                {
                    Assert.Equal(setCmd.Input, _prevCmd.Input);
                    Assert.Equal(setCmd.Category, _prevCmd.Category);
                    Assert.Equal(setCmd.Parameter, _prevCmd.Parameter);
                    Assert.Equal(setCmd.Type, _prevCmd.Type);

                    getCmd.IntData    = _prevCmd.IntData;
                    getCmd.LongData   = _prevCmd.LongData;
                    getCmd.FloatData  = _prevCmd.FloatData;
                    getCmd.StringData = _prevCmd.StringData;
                    getCmd.BoolData   = _prevCmd.BoolData;

                    ApplyOffsets(getCmd, setCmd);
                }

                yield return(getCmd);
            }
            else if (cmd is CameraControlDeviceOptionsSetCommand optCmd)
            {
                Assert.Equal(CameraControlDeviceOptionsSetCommand.MaskFlags.PeriodicFlushEnabled, optCmd.Mask);

                CameraControlGetCommand getCmd = CopyCommand(_optRefCmd);
                getCmd.PeriodicFlushEnabled = optCmd.PeriodicFlushEnabled;

                /*new CameraControlGetCommand
                 * {
                 *  Input = optCmd.Input,
                 *  Category = optCmd.Category,
                 *  Parameter = optCmd.Parameter,
                 *  Type = CameraControlDataType.String,
                 *  StringData = "abc",
                 *  PeriodicFlushEnabled = optCmd.PeriodicFlushEnabled,
                 * };*/

                yield return(getCmd);
            }
        }
        private static void FillRandomOffsetData(CameraControlGetCommand cmd)
        {
            switch (cmd.Type)
            {
            case CameraControlDataType.Bool:
                cmd.BoolData = cmd.BoolData
                               .Select(i => Randomiser.Range(0, 10) >= 5)
                               .ToArray();
                break;

            case CameraControlDataType.SInt8:
                cmd.IntData = cmd.IntData
                              .Select(i => Randomiser.RangeInt(sbyte.MinValue - i, sbyte.MaxValue - i))
                              .ToArray();
                break;

            case CameraControlDataType.SInt16:
                cmd.IntData = cmd.IntData
                              .Select(i => Randomiser.RangeInt(short.MinValue - i, short.MaxValue - i))
                              .ToArray();
                break;

            case CameraControlDataType.SInt32:
                cmd.IntData = cmd.IntData
                              .Select(i => Randomiser.RangeInt(-50000, 50000))
                              .ToArray();
                break;

            case CameraControlDataType.SInt64:
                cmd.LongData = cmd.LongData
                               .Select(i => (long)Randomiser.RangeInt(-50000, 50000))
                               .ToArray();
                break;

            case CameraControlDataType.Float:
                cmd.FloatData = cmd.FloatData
                                .Select(i => Randomiser.Range(0 - i, 1 - i))
                                .ToArray();
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        public void TestPeriodicFlushEnabled()
        {
            AtemMockServerWrapper.Each(_output, _pool, CameraCommandHandler, DeviceTestCases.CameraControl, helper =>
            {
                helper.Helper.StateSettings.IgnoreUnknownCameraControlProperties = true;
                IBMDSwitcherCameraControl camera = helper.SdkClient.SdkSwitcher as IBMDSwitcherCameraControl;
                Assert.NotNull(camera);

                using var watcher = new CameraControlReceiver(helper);

                for (int i = 0; i < 10; i++)
                {
                    AtemState stateBefore = helper.Helper.BuildLibState();

                    // Generate and send some data
                    CameraControlGetCommand cmd = new CameraControlGetCommand();
                    FillRandomData(cmd);
                    _optRefCmd         = cmd;
                    bool targetEnabled = Randomiser.Range(0, 10) >= 5;

                    if (!stateBefore.CameraControl.Cameras.ContainsKey((long)cmd.Input))
                    {
                        stateBefore.CameraControl.Cameras[(long)cmd.Input] = new CameraControlState.CameraState();
                    }

                    helper.SendAndWaitForChange(stateBefore, () =>
                    {
                        camera.SetParameterPeriodicFlushEnabled((uint)cmd.Input, cmd.Category, cmd.Parameter,
                                                                targetEnabled ? 1 : 0);
                    });

                    // Check libatem encoding
                    CameraControlGetCommand libCmd = watcher.LastCommand;
                    Assert.NotNull(libCmd);
                    AreEqual(cmd, libCmd);

                    // Pull the value out of the sdk, and ensure it is the same
                    camera.GetParameterPeriodicFlushEnabled((uint)cmd.Input, cmd.Category, cmd.Parameter,
                                                            out int resultEnabled);
                    Assert.Equal(targetEnabled, resultEnabled != 0);
                }
            });
        }
        private static void AreEqual(CameraControlGetCommand a, CameraControlGetCommand b)
        {
            Assert.Equal(a.Input, b.Input);
            Assert.Equal(a.Category, b.Category);
            Assert.Equal(a.Parameter, b.Parameter);
            Assert.Equal(a.Type, b.Type);

            Assert.Equal(a.IntData != null, b.IntData != null);
            Assert.Equal(a.LongData != null, b.LongData != null);
            Assert.Equal(a.FloatData != null, b.FloatData != null);
            Assert.Equal(a.StringData, b.StringData);
            Assert.Equal(a.BoolData != null, b.BoolData != null);
            if (a.IntData != null)
            {
                for (int i = 0; i < a.IntData.Length; i++)
                {
                    Assert.Equal(a.IntData[i], b.IntData[i]);
                }
            }
            if (a.LongData != null)
            {
                for (int i = 0; i < a.LongData.Length; i++)
                {
                    Assert.Equal(a.LongData[i], b.LongData[i]);
                }
            }
            if (a.FloatData != null)
            {
                for (int i = 0; i < a.FloatData.Length; i++)
                {
                    Assert.True(Math.Abs(a.FloatData[i] - b.FloatData[i]) < 0.01);
                }
            }
            if (a.BoolData != null)
            {
                for (int i = 0; i < a.BoolData.Length; i++)
                {
                    Assert.Equal(a.BoolData[i], b.BoolData[i]);
                }
            }
        }
        public static void Build(AtemState state, IBMDSwitcherCameraControl camera, AtemStateBuilderSettings updateSettings)
        {
            camera.GetPeriodicFlushInterval(out uint interval);
            state.CameraControl.PeriodicFlushInterval = interval;

            IBMDSwitcherCameraControlParameterIterator iterator =
                AtemSDKConverter.CastSdk <IBMDSwitcherCameraControlParameterIterator>(camera.CreateIterator);
            uint lastDevice    = uint.MaxValue;
            uint lastCategory  = 0u;
            uint lastParameter = 0u;

            for (iterator.Next(out var device, out var category, out var parameter);
                 ;
                 iterator.Next(out device, out category, out parameter))
            {
                if (device == lastDevice && category == lastCategory && parameter == lastParameter)
                {
                    break;
                }

                lastDevice    = device;
                lastCategory  = category;
                lastParameter = parameter;

                if (device == 0)
                {
                    continue;
                }

                if (!state.CameraControl.Cameras.TryGetValue(device, out CameraControlState.CameraState cState))
                {
                    cState = state.CameraControl.Cameras[device] = new CameraControlState.CameraState();
                }

                CameraControlGetCommand cmd = BuildCommand(camera, device, category, parameter);
                CameraControlUtil.ApplyToState(cState, cmd, updateSettings.IgnoreUnknownCameraControlProperties);
            }
        }
        public void TestSdkDecoding()
        {
            AtemMockServerWrapper.Each(_output, _pool, null, DeviceTestCases.CameraControl, helper =>
            {
                helper.Helper.StateSettings.IgnoreUnknownCameraControlProperties = true;
                IBMDSwitcherCameraControl camera = helper.SdkClient.SdkSwitcher as IBMDSwitcherCameraControl;
                Assert.NotNull(camera);

                using var watcher = new CameraControlReceiver(helper);

                for (int i = 0; i < 20; i++)
                {
                    AtemState stateBefore = helper.Helper.BuildLibState();

                    // Generate and send some data
                    CameraControlGetCommand cmd = new CameraControlGetCommand();
                    FillRandomData(cmd);

                    if (!stateBefore.CameraControl.Cameras.ContainsKey((long)cmd.Input))
                    {
                        stateBefore.CameraControl.Cameras[(long)cmd.Input] = new CameraControlState.CameraState();
                    }
                    helper.SendFromServerAndWaitForChange(stateBefore, cmd);

                    // Check libatem encoding
                    CameraControlGetCommand libCmd = watcher.LastCommand;
                    Assert.NotNull(libCmd);
                    AreEqual(cmd, libCmd);

                    // Pull the value out of the sdk, and ensure it is the same
                    CameraControlGetCommand sdkCmd =
                        CameraControlBuilder.BuildCommand(camera, (uint)cmd.Input, cmd.Category, cmd.Parameter);

                    AreEqual(cmd, sdkCmd);
                }
            });
        }
        public void TestSdkRelativeEncoding()
        {
            AtemMockServerWrapper.Each(_output, _pool, CameraCommandHandler, DeviceTestCases.CameraControl, helper =>
            {
                helper.Helper.StateSettings.IgnoreUnknownCameraControlProperties = true;
                IBMDSwitcherCameraControl camera = helper.SdkClient.SdkSwitcher as IBMDSwitcherCameraControl;
                Assert.NotNull(camera);

                using var watcher = new CameraControlReceiver(helper);

                for (int i = 0; i < 20; i++)
                {
                    AtemState stateBefore = helper.Helper.BuildLibState();

                    // Generate and send some data
                    CameraControlGetCommand refCmd = new CameraControlGetCommand();
                    FillRandomData(refCmd, CameraControlDataType.String);

                    if (!stateBefore.CameraControl.Cameras.ContainsKey((long)refCmd.Input))
                    {
                        stateBefore.CameraControl.Cameras[(long)refCmd.Input] = new CameraControlState.CameraState();
                    }

                    helper.SendFromServerAndWaitForChange(stateBefore, refCmd);
                    _prevCmd = refCmd;

                    {
                        // Pull the value out of the sdk, and ensure it is the same
                        CameraControlGetCommand sdkCmd =
                            CameraControlBuilder.BuildCommand(camera, (uint)refCmd.Input, refCmd.Category, refCmd.Parameter);

                        AreEqual(refCmd, sdkCmd);
                    }

                    CameraControlGetCommand cmd = CopyCommand(refCmd);
                    FillRandomOffsetData(cmd);

                    // Now do the sdk offset
                    helper.SendAndWaitForChange(stateBefore, () =>
                    {
                        switch (cmd.Type)
                        {
                        case CameraControlDataType.Bool:
                            {
                                IntPtr data = Randomiser.BuildSdkArray(sizeof(sbyte),
                                                                       cmd.BoolData.Select(v => v ? 1 : 0).ToArray());
                                unsafe
                                {
                                    var ptr = (int *)data.ToPointer();
                                    camera.ToggleFlags((uint)cmd.Input, cmd.Category, cmd.Parameter,
                                                       (uint)cmd.BoolData.Length, ref *ptr);
                                }
                                break;
                            }

                        case CameraControlDataType.SInt8:
                            {
                                IntPtr data = Randomiser.BuildSdkArray(sizeof(sbyte), cmd.IntData);
                                unsafe
                                {
                                    var ptr = (sbyte *)data.ToPointer();
                                    camera.OffsetInt8s((uint)cmd.Input, cmd.Category, cmd.Parameter,
                                                       (uint)cmd.IntData.Length, ref *ptr);
                                }
                                break;
                            }

                        case CameraControlDataType.SInt16:
                            {
                                IntPtr data = Randomiser.BuildSdkArray(sizeof(short), cmd.IntData);
                                unsafe
                                {
                                    var ptr = (short *)data.ToPointer();
                                    camera.OffsetInt16s((uint)cmd.Input, cmd.Category, cmd.Parameter,
                                                        (uint)cmd.IntData.Length, ref *ptr);
                                }
                                break;
                            }

                        case CameraControlDataType.SInt32:
                            {
                                IntPtr data = Randomiser.BuildSdkArray(sizeof(int), cmd.IntData);
                                unsafe
                                {
                                    var ptr = (int *)data.ToPointer();
                                    camera.OffsetInt32s((uint)cmd.Input, cmd.Category, cmd.Parameter,
                                                        (uint)cmd.IntData.Length, ref *ptr);
                                }
                                break;
                            }

                        case CameraControlDataType.SInt64:
                            {
                                IntPtr data = Randomiser.BuildSdkArray(cmd.LongData);
                                unsafe
                                {
                                    var ptr = (long *)data.ToPointer();
                                    camera.OffsetInt64s((uint)cmd.Input, cmd.Category, cmd.Parameter,
                                                        (uint)cmd.LongData.Length, ref *ptr);
                                }
                                break;
                            }

                        case CameraControlDataType.Float:
                            {
                                IntPtr data = Randomiser.BuildSdkArray(cmd.FloatData);
                                unsafe
                                {
                                    var ptr = (double *)data.ToPointer();
                                    camera.OffsetFloats((uint)cmd.Input, cmd.Category, cmd.Parameter,
                                                        (uint)cmd.FloatData.Length, ref *ptr);
                                }
                                break;
                            }

                        default:
                            throw new ArgumentOutOfRangeException();
                        }
                    });

                    var completeCmd = CopyCommand(refCmd);
                    ApplyOffsets(completeCmd, cmd);

                    // Check libatem encoding
                    CameraControlGetCommand libCmd = watcher.LastCommand;
                    Assert.NotNull(libCmd);
                    AreEqual(completeCmd, libCmd);

                    {
                        // Pull the value out of the sdk, and ensure it is the same
                        CameraControlGetCommand sdkCmd =
                            CameraControlBuilder.BuildCommand(camera, (uint)refCmd.Input, refCmd.Category, refCmd.Parameter);

                        AreEqual(completeCmd, sdkCmd);
                    }
                }
            });
        }
Пример #11
0
        private static void EnsureDataIsValid(CameraControlGetCommand cmd, CameraControlDataType expectedType,
                                              int expectedLength)
        {
            if (cmd.Type != expectedType)
            {
                throw new Exception($"Incorrect type (target: {expectedType}, got: {cmd.Type})");
            }

            bool fail = false;

            uint notNull = 0;

            if (cmd.FloatData != null)
            {
                notNull++;
            }
            if (cmd.IntData != null)
            {
                notNull++;
            }
            if (cmd.BoolData != null)
            {
                notNull++;
            }
            if (cmd.LongData != null)
            {
                notNull++;
            }
            if (cmd.StringData != null)
            {
                notNull++;
            }

            if (notNull != 1)
            {
                fail = true;
            }

            if (expectedLength != 0)
            {
                if (cmd.FloatData != null && cmd.FloatData.Length != expectedLength)
                {
                    fail = true;
                }
                if (cmd.IntData != null && cmd.IntData.Length != expectedLength)
                {
                    fail = true;
                }
                if (cmd.BoolData != null && cmd.BoolData.Length != expectedLength)
                {
                    fail = true;
                }
                if (cmd.LongData != null && cmd.LongData.Length != expectedLength)
                {
                    fail = true;
                }
                if (cmd.StringData != null && cmd.StringData.Length != expectedLength)
                {
                    fail = true;
                }
            }

            if (fail)
            {
                throw new Exception($"Not enough values (target: {expectedLength})");
            }
        }
Пример #12
0
        public static string[] ApplyToState(CameraControlState.CameraState input, CameraControlGetCommand cmd, bool ignoreUnknown)
        {
            AdjustmentDomain category = (AdjustmentDomain)cmd.Category;

            if (category == AdjustmentDomain.Camera)
            {
                switch ((CameraFeature)cmd.Parameter)
                {
                case CameraFeature.Detail:
                    EnsureDataIsValid(cmd, CameraControlDataType.SInt8, 1);
                    input.Camera.Detail = (CameraDetail)cmd.IntData[0];
                    input.Camera.DetailPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case CameraFeature.Gain:
                    EnsureDataIsValid(cmd, CameraControlDataType.SInt8, 1);
                    input.Camera.Gain = cmd.IntData[0];
                    input.Camera.GainPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case CameraFeature.PositiveGain:
                    EnsureDataIsValid(cmd, CameraControlDataType.SInt8, 1);
                    input.Camera.PositiveGain = (uint)cmd.IntData[0];
                    input.Camera.PositiveGainPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case CameraFeature.Shutter:
                    EnsureDataIsValid(cmd, CameraControlDataType.SInt32, 1);
                    input.Camera.Shutter = (uint)cmd.IntData[0];
                    input.Camera.ShutterPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case CameraFeature.WhiteBalance:
                    EnsureDataIsValid(cmd, CameraControlDataType.SInt16, 1);
                    input.Camera.WhiteBalance = (uint)cmd.IntData[0];
                    input.Camera.WhiteBalancePeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                default:
                    if (ignoreUnknown)
                    {
                        return(Array.Empty <string>());
                    }
                    throw new ArgumentOutOfRangeException();
                }
                return(new[] { "Camera" });
            }
            else if (category == AdjustmentDomain.Lens)
            {
                switch ((LensFeature)cmd.Parameter)
                {
                case LensFeature.Zoom:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 1);
                    input.Lens.ZoomSpeed = cmd.FloatData[0];
                    input.Lens.ZoomSpeedPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case LensFeature.Focus:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 1);
                    input.Lens.Focus = cmd.FloatData[0];
                    input.Lens.FocusPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case LensFeature.Iris:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 1);
                    input.Lens.Iris = cmd.FloatData[0];
                    input.Lens.IrisPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case LensFeature.AutoFocus:
                    EnsureDataIsValid(cmd, CameraControlDataType.Bool, 0);
                    break;

                default:
                    if (ignoreUnknown)
                    {
                        return(Array.Empty <string>());
                    }
                    throw new ArgumentOutOfRangeException();
                }
                return(new[] { "Lens" });
            }
            else if (category == AdjustmentDomain.Chip)
            {
                switch ((ChipFeature)cmd.Parameter)
                {
                case ChipFeature.Lift:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 4);
                    input.Chip.Lift.R = cmd.FloatData[0];
                    input.Chip.Lift.G = cmd.FloatData[1];
                    input.Chip.Lift.B = cmd.FloatData[2];
                    input.Chip.Lift.Y = cmd.FloatData[3];
                    input.Chip.Lift.PeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case ChipFeature.Gamma:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 4);
                    input.Chip.Gamma.R = cmd.FloatData[0];
                    input.Chip.Gamma.G = cmd.FloatData[1];
                    input.Chip.Gamma.B = cmd.FloatData[2];
                    input.Chip.Gamma.Y = cmd.FloatData[3];
                    input.Chip.Gamma.PeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case ChipFeature.Gain:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 4);
                    input.Chip.Gain.R = cmd.FloatData[0];
                    input.Chip.Gain.G = cmd.FloatData[1];
                    input.Chip.Gain.B = cmd.FloatData[2];
                    input.Chip.Gain.Y = cmd.FloatData[3];
                    input.Chip.Gain.PeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case ChipFeature.Contrast:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 2);
                    // TODO - byte 0
                    input.Chip.Contrast = cmd.FloatData[1];
                    input.Chip.ContrastPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case ChipFeature.HueSaturation:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 2);
                    input.Chip.Hue        = cmd.FloatData[0];
                    input.Chip.Saturation = cmd.FloatData[1];
                    input.Chip.HueSaturationPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case ChipFeature.Lum:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 1);
                    input.Chip.LumMix = cmd.FloatData[0];
                    input.Chip.LumMixPeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                case ChipFeature.Aperture:
                    EnsureDataIsValid(cmd, CameraControlDataType.Float, 4);
                    // TODO - byte 1, 2, 3
                    input.Chip.Aperture = cmd.FloatData[0];
                    input.Chip.AperturePeriodicFlushEnabled = cmd.PeriodicFlushEnabled;
                    break;

                default:
                    if (ignoreUnknown)
                    {
                        return(Array.Empty <string>());
                    }
                    throw new ArgumentOutOfRangeException();
                }
                return(new[] { "Chip" });
            }
            else if (category == AdjustmentDomain.ColourBars)
            {
                switch (cmd.Parameter)
                {
                case 4:
                {
                    input.ColorBars = cmd.IntData[0] == 1;
                    return(new[] { "ColorBars" });
                }

                default:
                    if (ignoreUnknown)
                    {
                        return(Array.Empty <string>());
                    }
                    throw new ArgumentOutOfRangeException();
                }
            }
            else
            {
                if (ignoreUnknown)
                {
                    return(Array.Empty <string>());
                }
                //throw new ArgumentOutOfRangeException();
                // TODO - not everything is parsed yet
                return(Array.Empty <string>());
            }
        }
        public static CameraControlGetCommand BuildCommand(IBMDSwitcherCameraControl camera, uint device, uint category, uint parameter)
        {
            camera.GetParameterInfo(device, category, parameter,
                                    out _BMDSwitcherCameraControlParameterType type, out uint count);

            CameraControlDataType newType;

            switch (type)
            {
            case _BMDSwitcherCameraControlParameterType.bmdSwitcherCameraControlParameterTypeVoidBool:
                newType = CameraControlDataType.Bool;
                break;

            case _BMDSwitcherCameraControlParameterType.bmdSwitcherCameraControlParameterTypeSigned8Bit:
                newType = CameraControlDataType.SInt8;
                break;

            case _BMDSwitcherCameraControlParameterType.bmdSwitcherCameraControlParameterTypeSigned16Bit:
                newType = CameraControlDataType.SInt16;
                break;

            case _BMDSwitcherCameraControlParameterType.bmdSwitcherCameraControlParameterTypeSigned32Bit:
                newType = CameraControlDataType.SInt32;
                break;

            case _BMDSwitcherCameraControlParameterType.bmdSwitcherCameraControlParameterTypeSigned64Bit:
                newType = CameraControlDataType.SInt64;
                break;

            case _BMDSwitcherCameraControlParameterType.bmdSwitcherCameraControlParameterTypeUTF8:
                newType = CameraControlDataType.String;
                break;

            case _BMDSwitcherCameraControlParameterType.bmdSwitcherCameraControlParameterTypeFixedPoint16Bit:
                newType = CameraControlDataType.Float;
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            camera.GetParameterPeriodicFlushEnabled(device, category, parameter, out int flushEnabled);

            var cmd = new CameraControlGetCommand
            {
                Input                = (VideoSource)device,
                Category             = category,
                Parameter            = parameter,
                Type                 = newType,
                PeriodicFlushEnabled = flushEnabled != 0,
            };

            switch (cmd.Type)
            {
            case CameraControlDataType.Bool:
            {
                uint count2 = count;
                camera.GetFlags(device, category, parameter, ref count2, out int values);
                int[] intVals   = Randomiser.ConvertSdkArray(count2, ref values);
                var   sbyteVals = new sbyte[count2];
                Buffer.BlockCopy(intVals, 0, sbyteVals, 0, (int)count2);
                cmd.BoolData = sbyteVals.Select(v => v != 0).ToArray();
                break;
            }

            case CameraControlDataType.SInt8:
            {
                uint count2 = count;
                camera.GetInt8s(device, category, parameter, ref count2, out sbyte values);
                cmd.IntData = Randomiser.ConvertSdkArray(count2, ref values).Select(v => (int)v).ToArray();
                break;
            }

            case CameraControlDataType.SInt16:
            {
                uint count2 = count;
                camera.GetInt16s(device, category, parameter, ref count2, out short values);
                cmd.IntData = Randomiser.ConvertSdkArray(count2, ref values).Select(v => (int)v).ToArray();
                break;
            }

            case CameraControlDataType.SInt32:
            {
                uint count2 = count;
                camera.GetInt32s(device, category, parameter, ref count2, out int values);
                cmd.IntData = Randomiser.ConvertSdkArray(count2, ref values);
                break;
            }

            case CameraControlDataType.SInt64:
            {
                uint count2 = count;
                camera.GetInt64s(device, category, parameter, ref count2, out long values);
                cmd.LongData = Randomiser.ConvertSdkArray(count2, ref values);
                break;
            }

            case CameraControlDataType.String:
            {
                camera.GetString(device, category, parameter, out string value);
                cmd.StringData = value;
                break;
            }

            case CameraControlDataType.Float:
            {
                uint count2 = count;
                camera.GetFloats(device, category, parameter, ref count2, out double values);
                cmd.FloatData = Randomiser.ConvertSdkArray(count2, ref values);
                break;
            }

            default:
                throw new ArgumentOutOfRangeException();
            }

            return(cmd);
        }