// 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.Diagnostics; using System.Runtime.InteropServices; public class FrameSet : Frame, ICompositeDisposable, IEnumerable<Frame> { private readonly List<IDisposable> disposables = new List<IDisposable>(); [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Enumerator enumerator; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private int count; /// <summary> /// Create a new <see cref="FrameSet"/> from <see cref="Frame"/> /// </summary> /// <param name="composite">a composite frame</param> /// <returns>a new <see cref="FrameSet"/> to be disposed</returns> /// <exception cref="ArgumentException">Thrown when frame is not a composite frame</exception> public static FrameSet FromFrame(Frame composite) { if (composite.IsComposite) { object error; NativeMethods.rs2_frame_add_ref(composite.Handle, out error); return Create(composite.Handle); } throw new ArgumentException("The frame is a not composite frame", nameof(composite)); } [Obsolete("This method is obsolete. Use DisposeWith method instead")] public static FrameSet FromFrame(Frame composite, FramesReleaser releaser) { return FromFrame(composite).DisposeWith(releaser); } internal override void Initialize() { object error; count = NativeMethods.rs2_embedded_frames_count(Handle, out error); enumerator.Reset(); disposables.Clear(); } internal FrameSet(IntPtr ptr) : base(ptr) { object error; count = NativeMethods.rs2_embedded_frames_count(Handle, out error); enumerator = new Enumerator(this); } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public new bool IsComposite => true; /// <summary> /// Cast this to a <see cref="Frame"/> /// </summary> /// <returns>a frame to be disposed</returns> public Frame AsFrame() { object error; NativeMethods.rs2_frame_add_ref(Handle, out error); return Frame.Create(Handle); } /// <summary> /// Invoke the <paramref name="action"/> delegate on each frame in the set /// </summary> /// <param name="action">Delegate to invoke</param> public void ForEach(Action<Frame> action) { for (int i = 0; i < count; i++) { using (var frame = this[i]) { action(frame); } } } public T FirstOrDefault<T>(Stream stream, Format format = Format.Any) where T : Frame { return FirstOrDefault(stream, format)?.Cast<T>(); } public Frame FirstOrDefault(Stream stream, Format format = Format.Any) { for (int i = 0; i < count; i++) { var frame = this[i]; using (var fp = frame.Profile) if (fp.Stream == stream && (format == Format.Any || fp.Format == format)) { return frame; } frame.Dispose(); } return null; } public T FirstOrDefault<T>(Predicate<T> predicate) where T : Frame { object error; for (int i = 0; i < count; i++) { var ptr = NativeMethods.rs2_extract_frame(Handle, i, out error); var frame = Frame.Create<T>(ptr); if (predicate(frame)) { return frame; } frame.Dispose(); } return null; } /// <summary> /// Retrieve back the first frame of specific stream type, if no frame found, error will be thrown /// </summary> /// <typeparam name="T"><see cref="Frame"/> type or subclass</typeparam> /// <param name="stream">stream type of frame to be retrieved</param> /// <param name="format">format type of frame to be retrieved, defaults to <see cref="Format.Any"/></param> /// <returns>first found frame with <paramref name="stream"/> type and <paramref name="format"/> type</returns> /// <exception cref="ArgumentException">Thrown when requested type not found</exception> public T First<T>(Stream stream, Format format = Format.Any) where T : Frame { var f = FirstOrDefault<T>(stream, format); if (f == null) { throw new ArgumentException("Frame of requested stream type was not found!"); } return f; } /// <summary> /// Retrieve back the first frame of specific stream type, if no frame found, error will be thrown /// </summary> /// <param name="stream">stream type of frame to be retrieved</param> /// <param name="format">format type of frame to be retrieved, defaults to <see cref="Format.Any"/></param> /// <returns>first found frame with <paramref name="stream"/> type and <paramref name="format"/> type</returns> /// <exception cref="ArgumentException">Thrown when requested type not found</exception> /// <seealso cref="First{T}(Stream, Format)"/> public Frame First(Stream stream, Format format = Format.Any) => First<Frame>(stream, format); /// <summary>Gets the first depth frame</summary> public DepthFrame DepthFrame => FirstOrDefault<DepthFrame>(Stream.Depth, Format.Z16); /// <summary>Gets the first color frame</summary> public VideoFrame ColorFrame => FirstOrDefault<VideoFrame>(Stream.Color); /// <summary>Gets the first infrared frame</summary> public VideoFrame InfraredFrame => FirstOrDefault<VideoFrame>(Stream.Infrared); /// <summary>Gets the first fisheye frame</summary> public VideoFrame FishEyeFrame => FirstOrDefault<VideoFrame>(Stream.Fisheye); /// <summary>Gets the first pose frame</summary> public PoseFrame PoseFrame => FirstOrDefault<PoseFrame>(Stream.Pose); /// <inheritdoc/> public IEnumerator<Frame> GetEnumerator() { enumerator.Reset(); return enumerator; } /// <inheritdoc/> IEnumerator IEnumerable.GetEnumerator() { enumerator.Reset(); return enumerator; } /// <summary>Gets the number of frames embedded within a composite frame</summary> /// <value>Number of embedded frames</value> public int Count => count; /// <summary>Extract frame from within a composite frame</summary> /// <param name="index">Index of the frame to extract within the composite frame</param> /// <returns>returns reference to a frame existing within the composite frame</returns> /// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="index"/> is out of range</exception> public Frame this[int index] { get { if (index < count) { object error; var ptr = NativeMethods.rs2_extract_frame(Handle, index, out error); return Frame.Create(ptr); } throw new ArgumentOutOfRangeException(nameof(index)); } } public Frame this[Stream stream, int index = 0] { get { return FirstOrDefault<Frame>(f => { using (var p = f.Profile) { return p.Stream == stream && p.Index == index; } }); } } public Frame this[Stream stream, Format format, int index = 0] { get { return FirstOrDefault<Frame>(f => { using (var p = f.Profile) { return p.Stream == stream && p.Format == format && p.Index == index; } }); } } public static new FrameSet Create(IntPtr ptr) { return ObjectPool.Get<FrameSet>(ptr); } protected override void Dispose(bool disposing) { disposables.ForEach(d => d?.Dispose()); disposables.Clear(); base.Dispose(disposing); } /// <inheritdoc/> public void AddDisposable(IDisposable disposable) { if (disposable == this) { return; } disposables.Add(disposable); } internal sealed class Enumerator : IEnumerator<Frame> { private readonly FrameSet fs; private Frame current; private int index; public Enumerator(FrameSet fs) { this.fs = fs; index = 0; current = default(Frame); } public Frame Current { get { return current; } } object IEnumerator.Current { get { if (index == 0 || index == fs.count + 1) { throw new InvalidOperationException(); } return Current; } } public void Dispose() { // Method intentionally left empty. } public bool MoveNext() { if ((uint)index < (uint)fs.count) { object error; var ptr = NativeMethods.rs2_extract_frame(fs.Handle, index, out error); current = Frame.Create(ptr); index++; return true; } index = fs.count + 1; current = null; return false; } public void Reset() { index = 0; current = null; } } } public static class FrameSetExtensions { public static FrameSet AsFrameSet(this Frame frame) { return FrameSet.FromFrame(frame); } } }