// License: Apache 2.0. See LICENSE file in root directory. // Copyright(c) 2017 Intel Corporation. All Rights Reserved. namespace Intel.RealSense { using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; // TODO: subclasses - DepthSensor, DepthStereoSensor, PoseSensor... public class Sensor : Base.RefCountedPooledObject, IOptions { protected static Hashtable refCountTable = new Hashtable(); protected static readonly object tableLock = new object(); internal override void Initialize() { lock (tableLock) { if (refCountTable.Contains(Handle)) refCount = refCountTable[Handle] as Base.RefCount; else { refCount = new Base.RefCount(); refCountTable[Handle] = refCount; } Retain(); } Info = new InfoCollection(NativeMethods.rs2_supports_sensor_info, NativeMethods.rs2_get_sensor_info, Handle); Options = new OptionsList(Handle); } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private frame_callback m_callback; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private FrameQueue m_queue; internal static T Create(IntPtr ptr) where T : Sensor { return ObjectPool.Get(ptr); } /// Returns a strongly-typed clone /// type or subclass /// to clone /// an instance of public static T Create(Sensor other) where T : Sensor { return ObjectPool.Get(other.Handle); } internal Sensor(IntPtr sensor) : base(sensor, NativeMethods.rs2_delete_sensor) { Initialize(); } protected override void Dispose(bool disposing) { if (m_instance.IsInvalid) { return; } //m_queue.Dispose(); (Options as OptionsList).Dispose(); lock (tableLock) { IntPtr localHandle = Handle; System.Diagnostics.Debug.Assert(refCountTable.Contains(localHandle)); base.Dispose(disposing); if (refCount.count == 0) { refCountTable.Remove(localHandle); } } } public class CameraInfos : IEnumerable> { private readonly IntPtr m_sensor; public CameraInfos(IntPtr sensor) { m_sensor = sensor; } /// retrieve sensor specific information, like versions of various internal components /// camera info type to retrieve /// the requested camera info string, in a format specific to the device model public string this[CameraInfo info] { get { object err; if (NativeMethods.rs2_supports_sensor_info(m_sensor, info, out err) > 0) { return Marshal.PtrToStringAnsi(NativeMethods.rs2_get_sensor_info(m_sensor, info, out err)); } return null; } } private static readonly CameraInfo[] CameraInfoValues = Enum.GetValues(typeof(CameraInfo)) as CameraInfo[]; /// public IEnumerator> GetEnumerator() { object error; foreach (var key in CameraInfoValues) { if (NativeMethods.rs2_supports_sensor_info(m_sensor, key, out error) > 0) { var value = Marshal.PtrToStringAnsi(NativeMethods.rs2_get_sensor_info(m_sensor, key, out error)); yield return new KeyValuePair(key, value); } } } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public InfoCollection Info { get; private set; } /// public IOptionsContainer Options { get; private set; } /// open subdevice for exclusive access, by committing to a configuration /// stream profile that defines single stream configuration public void Open(StreamProfile profile) { object error; NativeMethods.rs2_open(Handle, profile.Handle, out error); } /// open subdevice for exclusive access, by committing to composite configuration, specifying one or more stream profiles /// /// this method should be used for interdependent streams, such as depth and infrared, that have to be configured together /// /// list of stream profiles public void Open(params StreamProfile[] profiles) { object error; IntPtr[] handles = new IntPtr[profiles.Length]; for (int i = 0; i < profiles.Length; i++) { handles[i] = profiles[i].Handle; } NativeMethods.rs2_open_multiple(Handle, handles, profiles.Length, out error); } /// /// start streaming from specified configured sensor of specific stream to frame queue /// /// frame-queue to store new frames into // TODO: overload with state object and Action callback to avoid allocations public void Start(FrameQueue queue) { object error; NativeMethods.rs2_start_queue(Handle, queue.Handle, out error); m_queue = queue; m_callback = null; } /// start streaming from specified configured sensor /// delegate to register as per-frame callback // TODO: overload with state object and Action callback to avoid allocations public void Start(FrameCallback cb) { object error; frame_callback cb2 = (IntPtr f, IntPtr u) => { using (var frame = Frame.Create(f)) { cb(frame); } }; m_callback = cb2; m_queue = null; NativeMethods.rs2_start(Handle, cb2, IntPtr.Zero, out error); } /// /// stops streaming from specified configured device /// public void Stop() { object error; NativeMethods.rs2_stop(Handle, out error); m_callback = null; m_queue = null; } /// /// close subdevice for exclusive access this method should be used for releasing device resource /// public void Close() { object error; NativeMethods.rs2_close(Handle, out error); } public bool Is(Extension ext) { object error; return NativeMethods.rs2_is_sensor_extendable_to(Handle, ext, out error) != 0; } /// Returns a strongly-typed clone /// type or subclass /// an instance of public T As() where T : Sensor { return Create(this); } /// /// Gets the mapping between the units of the depth image and meters /// /// depth in meters corresponding to a depth value of 1 public float DepthScale { get { object error; return NativeMethods.rs2_get_depth_scale(Handle, out error); } } /// /// Gets the active region of interest to be used by auto-exposure algorithm /// public AutoExposureROI AutoExposureSettings { get { object error; if (NativeMethods.rs2_is_sensor_extendable_to(Handle, Extension.Roi, out error) != 0) { return new AutoExposureROI { m_instance = Handle }; } // TODO: consider making AutoExposureROI a struct and throwing instead return null; } } /// Gets the list of supported stream profiles /// list of stream profiles that given subdevice can provide public ReadOnlyCollection StreamProfiles { get { object error; using (var pl = new StreamProfileList(NativeMethods.rs2_get_stream_profiles(Handle, out error))) { var profiles = new StreamProfile[pl.Count]; pl.CopyTo(profiles, 0); return Array.AsReadOnly(profiles); } } } /// Gets the list of recommended processing blocks for a specific sensor. /// /// Order and configuration of the blocks are decided by the sensor /// /// list of supported sensor recommended processing blocks public ReadOnlyCollection ProcessingBlocks { get { object error; using (var pl = new ProcessingBlockList(NativeMethods.rs2_get_recommended_processing_blocks(Handle, out error))) { var blocks = new ProcessingBlock[pl.Count]; pl.CopyTo(blocks, 0); return Array.AsReadOnly(blocks); } } } } }