// 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.Generic; using System.Linq; using System.Runtime.InteropServices; /// /// Custom processing block based on a delegate. This lets the users pass frames between module boundaries for processing /// // TODO: extends Base.Object, ctor is trickly... public class CustomProcessingBlock : IOptions, IDisposable { internal Base.DeleterHandle m_instance; private static readonly frame_processor_callback fpc = new frame_processor_callback(ProcessingBlockCallback); private static readonly frame_callback frameCallback = new frame_callback(ProcessingBlockFrameCallback); private readonly FrameQueue queue = new FrameQueue(1); private readonly GCHandle frameProcessorCallbackHandle; private GCHandle frameCallbackHandle; /// /// Initializes a new instance of the class. /// /// Processing function to be applied to every frame entering the block public CustomProcessingBlock(FrameProcessorCallback cb) { frameProcessorCallbackHandle = GCHandle.Alloc(cb, GCHandleType.Normal); IntPtr cbPtr = GCHandle.ToIntPtr(frameProcessorCallbackHandle); object error; var pb = NativeMethods.rs2_create_processing_block_fptr(fpc, cbPtr, out error); m_instance = new Base.DeleterHandle(pb, NativeMethods.rs2_delete_processing_block); Options = new OptionsList(m_instance.Handle); } ~CustomProcessingBlock() { Dispose(false); } private static void ProcessingBlockCallback(IntPtr f, IntPtr src, IntPtr u) { try { var callback = GCHandle.FromIntPtr(u).Target as FrameProcessorCallback; using (var frame = Frame.Create(f)) { callback(frame, new FrameSource(new HandleRef(frame, src))); } } catch (ArgumentException) { // ArgumentException: GCHandle value belongs to a different domain // Happens when Unity Editor stop the scene with multiple devices streaming with multiple post-processing filters. } } /// public IOptionsContainer Options { get; private set; } public FrameQueue Queue { get { return queue; } } /// /// This method is used to pass frames into a processing block /// /// type or subclass /// Frame to process public void Process(T f) where T : Frame { object error; NativeMethods.rs2_frame_add_ref(f.Handle, out error); NativeMethods.rs2_process_frame(m_instance.Handle, f.Handle, out error); } /// /// Start the processing block, delivering frames to external queue /// /// to place the processed frames in public void Start(FrameQueue queue) { object error; NativeMethods.rs2_start_processing_queue(m_instance.Handle, queue.Handle, out error); } /// /// Start the processing block /// public void Start() { object error; NativeMethods.rs2_start_processing_queue(m_instance.Handle, Queue.Handle, out error); } /// /// Start the processing block, delivering frames to a callback /// /// callback to receive frames // TODO: overload with state object and Action callback to avoid allocations public void Start(FrameCallback cb) { frameCallbackHandle = GCHandle.Alloc(cb, GCHandleType.Normal); IntPtr cbPtr = GCHandle.ToIntPtr(frameCallbackHandle); object error; NativeMethods.rs2_start_processing_fptr(m_instance.Handle, frameCallback, cbPtr, out error); } /// /// This method adds a custom option to a custom processing block. /// /// ID for referencing the option /// the minimum value which will be accepted for this option /// the maximum value which will be accepted for this option /// the granularity of options which accept discrete values, or zero if the option accepts continuous values /// the default value of the option.This will be the initial value. /// true if adding the option succeeds. false if it fails e.g. an option with this id is already registered public bool RegisterOption(Option option_id, float min, float max, float step, float def) { object error; return NativeMethods.rs2_processing_block_register_simple_option(m_instance.Handle, option_id, min, max, step, def, out error) > 0; } private static void ProcessingBlockFrameCallback(IntPtr f, IntPtr u) { try { var callback = GCHandle.FromIntPtr(u).Target as FrameCallback; using (var frame = Frame.Create(f)) { callback(frame); } } catch (ArgumentException) { // ArgumentException: GCHandle value belongs to a different domain // Happens when Unity Editor stop the scene with multiple devices streaming with multiple post-processing filters. } } protected virtual void Dispose(bool disposing) { if (frameCallbackHandle.IsAllocated) { frameCallbackHandle.Free(); } if (frameProcessorCallbackHandle.IsAllocated) { frameProcessorCallbackHandle.Free(); } m_instance.Dispose(); } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Processing delegate to be applied to every frame entering the block /// /// frame entering the processing block /// frame source used to notify frame ready public delegate void FrameProcessorCallback(Frame frame, FrameSource source); } }