1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Type for representing an "inactive" pipeline which is unconfigured and cannot acquire frames.

use super::{active::ActivePipeline, profile::PipelineProfile};
use crate::{check_rs2_error, config::Config, context::Context, kind::Rs2Exception};
use anyhow::Result;
use realsense_sys as sys;
use std::{convert::TryFrom, ptr::NonNull};
use thiserror::Error;

/// Enumeration of possible errors that can occur during pipeline construction.
#[derive(Error, Debug)]
pub enum PipelineConstructionError {
    /// The pipeline could not be created from the context.
    #[error("Could not create the pipeline from the provided context. Type: {0}; Reason {1}")]
    CouldNotCreatePipelineFromContext(Rs2Exception, String),
}

/// Enumeration of possible errors that can occur when trying to start the pipeline.
#[derive(Error, Debug)]
pub enum PipelineActivationError {
    /// The pipeline could not be started due to an internal exception.
    #[error("Could not successfully start the pipeline. Type: {0}; Reason: {1}")]
    CouldNotStartPipelineError(Rs2Exception, String),
    /// The configuration cannot be resolved.
    ///
    /// See [`InactivePipeline::can_resolve`] for more information.
    #[error("Config cannot be resolved by any active devices / stream combinations.")]
    ConfigCannotBeResolved,
}

/// A type describing an "inactive" pipeline which is unconfigured and cannot acquire frames.
#[derive(Debug)]
pub struct InactivePipeline {
    /// A (non-null) pointer to the pipeline.
    pipeline_ptr: NonNull<sys::rs2_pipeline>,
}

impl Drop for InactivePipeline {
    fn drop(&mut self) {
        unsafe {
            sys::rs2_delete_pipeline(self.pipeline_ptr.as_ptr());
        }
    }
}

unsafe impl Send for InactivePipeline {}

impl TryFrom<&Context> for InactivePipeline {
    type Error = anyhow::Error;

    fn try_from(context: &Context) -> Result<Self, Self::Error> {
        unsafe {
            let mut err = std::ptr::null_mut::<sys::rs2_error>();
            let context_ptr = context.get_raw();

            let pipeline_ptr = sys::rs2_create_pipeline(context_ptr.as_ptr(), &mut err);
            check_rs2_error!(
                err,
                PipelineConstructionError::CouldNotCreatePipelineFromContext
            )?;

            Ok(Self::new(NonNull::new(pipeline_ptr).unwrap()))
        }
    }
}

impl InactivePipeline {
    /// Constructs a new inactive pipeline from the constituent components
    ///
    /// This is only to be used / called from the [`ActivePipeline`] type.
    pub(crate) fn new(pipeline_ptr: NonNull<sys::rs2_pipeline>) -> Self {
        Self { pipeline_ptr }
    }

    /// Start the pipeline with an optional config.
    ///
    /// The method consumes inactive pipeline itself, and returns the started pipeine.
    pub fn start(self, config: Option<Config>) -> Result<ActivePipeline> {
        unsafe {
            let mut err = std::ptr::null_mut::<sys::rs2_error>();
            let profile_ptr = if let Some(conf) = config {
                if !self.can_resolve(&conf) {
                    return Err(anyhow::anyhow!(
                        PipelineActivationError::ConfigCannotBeResolved
                    ));
                }

                sys::rs2_pipeline_start_with_config(
                    self.pipeline_ptr.as_ptr(),
                    conf.get_raw().as_ptr(),
                    &mut err,
                )
            } else {
                sys::rs2_pipeline_start(self.pipeline_ptr.as_ptr(), &mut err)
            };
            check_rs2_error!(err, PipelineActivationError::CouldNotStartPipelineError)?;

            let profile = PipelineProfile::try_from(NonNull::new(profile_ptr).unwrap())?;
            let active = ActivePipeline::new(self.pipeline_ptr, profile);

            std::mem::forget(self);
            Ok(active)
        }
    }

    /// Resolve a configuration and get the corresponding pipeline profile.
    ///
    /// This function checks the pipeline to see if this config can be used to start the pipeline,
    /// and if this configuration can be used it returns the pipeline profile (device and streams)
    /// that will be used as the active profile when the pipeline is started. Otherwise, if this
    /// configuration cannot resolve, this will return `None`.
    pub fn resolve(&self, config: &Config) -> Option<PipelineProfile> {
        if !self.can_resolve(config) {
            return None;
        }

        unsafe {
            let mut err = std::ptr::null_mut::<sys::rs2_error>();
            let profile_ptr = sys::rs2_config_resolve(
                config.get_raw().as_ptr(),
                self.pipeline_ptr.as_ptr(),
                &mut err,
            );

            if err.as_ref().is_none() {
                PipelineProfile::try_from(NonNull::new(profile_ptr).unwrap()).ok()
            } else {
                sys::rs2_free_error(err);
                None
            }
        }
    }

    /// Predicate to check if a pipeline profile exists for a given configuration.
    ///
    /// This predicate evaluates whether or not a configuration can be resolved to a device and set
    /// of streams (which constitute a pipeline profile) that can be used by the pipeline to start
    /// streaming.
    ///
    /// Returns true iff the configuration can be satisfied and a pipeline profile can be
    /// constructed.
    pub fn can_resolve(&self, config: &Config) -> bool {
        unsafe {
            let mut err = std::ptr::null_mut::<sys::rs2_error>();
            let can_resolve = sys::rs2_config_can_resolve(
                config.get_raw().as_ptr(),
                self.pipeline_ptr.as_ptr(),
                &mut err,
            );

            if err.as_ref().is_none() {
                can_resolve != 0
            } else {
                sys::rs2_free_error(err);
                false
            }
        }
    }
}