// 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);
}
}