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
use crate::utils::common::{gst_from_otc_format, MissingElement};

use crate::audio_device::{AudioDeviceSettings, AudioSampleData};
use crate::video_capturer::VideoCapturerSettings;
use anyhow::Error;
use byte_slice_cast::*;
use gst::prelude::*;

pub struct CapturerBuffer(gst::buffer::MappedBuffer<gst::buffer::Readable>);

impl AsRef<[u8]> for CapturerBuffer {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref().as_slice_of::<u8>().unwrap()
    }
}

impl AsRef<[i16]> for CapturerBuffer {
    fn as_ref(&self) -> &[i16] {
        self.0.as_ref().as_slice_of::<i16>().unwrap()
    }
}

pub struct Capturer {
    pipeline: gst::Pipeline,
    sink: gst::Element,
}

impl Capturer {
    pub fn new(settings: &VideoCapturerSettings) -> Result<Self, Error> {
        gst::init()?;

        let format = gst_from_otc_format(settings.format);
        let caps = gst::Caps::new_simple(
            "video/x-raw",
            &[
                ("format", &format.to_string()),
                ("width", &settings.width),
                ("height", &settings.height),
                ("framerate", &gst::Fraction::new(settings.fps, 1)),
            ],
        );

        let pipeline = gst::Pipeline::new(None);
        let src = gst::ElementFactory::make("videotestsrc", None)
            .map_err(|_| MissingElement("videotestsrc"))?;
        let capsfilter = gst::ElementFactory::make("capsfilter", None)
            .map_err(|_| MissingElement("capsfilter"))?;
        capsfilter.set_property("caps", &caps);
        let sink =
            gst::ElementFactory::make("appsink", None).map_err(|_| MissingElement("appsink"))?;

        pipeline.add_many(&[&src, &capsfilter, &sink])?;
        gst::Element::link_many(&[&src, &capsfilter, &sink])?;

        pipeline.set_state(gst::State::Playing)?;

        let bin_ref = pipeline.upcast_ref::<gst::Bin>();
        gst::debug_bin_to_dot_file_with_ts(
            bin_ref,
            gst::DebugGraphDetails::all(),
            "CapturerPipeline",
        );

        Ok(Self { pipeline, sink })
    }

    pub fn pull_buffer(&self) -> Result<Box<dyn AsRef<[u8]>>, Error> {
        let appsink = self.sink.downcast_ref::<gst_app::AppSink>().unwrap();
        let sample = appsink.pull_sample().unwrap();
        let buffer = sample.buffer_owned().unwrap();
        let map = buffer.into_mapped_buffer_readable().unwrap();
        Ok(Box::new(CapturerBuffer(map)))
    }
}

impl Drop for Capturer {
    fn drop(&mut self) {
        let _ = self.pipeline.set_state(gst::State::Null);
    }
}

pub struct AudioCapturer {
    pipeline: gst::Pipeline,
    sink: gst::Element,
    samples_per_buffer: usize,
}

impl AudioCapturer {
    pub fn new(settings: &AudioDeviceSettings) -> Result<Self, Error> {
        gst::init()?;

        let caps = gst::Caps::new_simple(
            "audio/x-raw",
            &[
                ("format", &"S16LE"),
                ("layout", &"interleaved"),
                ("rate", &settings.sampling_rate),
                ("channels", &settings.number_of_channels),
            ],
        );

        let pipeline = gst::Pipeline::new(None);
        let src = gst::ElementFactory::make("audiotestsrc", None)
            .map_err(|_| MissingElement("audiotestsrc"))?;
        let capsfilter = gst::ElementFactory::make("capsfilter", None)
            .map_err(|_| MissingElement("capsfilter"))?;
        capsfilter.set_property("caps", &caps);
        let sink =
            gst::ElementFactory::make("appsink", None).map_err(|_| MissingElement("appsink"))?;

        let samples_per_buffer = (settings.sampling_rate / 100) * settings.number_of_channels;
        src.set_property("samplesperbuffer", samples_per_buffer);
        pipeline.add_many(&[&src, &capsfilter, &sink])?;
        gst::Element::link_many(&[&src, &capsfilter, &sink])?;

        pipeline.set_state(gst::State::Playing)?;

        let bin_ref = pipeline.upcast_ref::<gst::Bin>();
        gst::debug_bin_to_dot_file_with_ts(
            bin_ref,
            gst::DebugGraphDetails::all(),
            "AudioCapturerPipeline",
        );

        Ok(Self {
            pipeline,
            sink,
            samples_per_buffer: samples_per_buffer as usize,
        })
    }

    pub fn pull_buffer(&self) -> Option<AudioSampleData> {
        // NOTE: Ideally we should use size here, with an adapter. For testing
        // purpose it was simpler to set the samplesperbuffer property to match
        // our settings.
        let size = 441;
        let appsink = self.sink.downcast_ref::<gst_app::AppSink>().unwrap();
        if let Ok(sample) = appsink.pull_sample() {
            let buffer = sample.buffer_owned().unwrap();
            let map = buffer.into_mapped_buffer_readable().unwrap();
            let m = CapturerBuffer(map);
            let mut d: Vec<i16> = vec![0; self.samples_per_buffer];
            d[..size].clone_from_slice(m.as_ref());
            Some(AudioSampleData(d))
        } else {
            None
        }
    }
}

impl Drop for AudioCapturer {
    fn drop(&mut self) {
        let _ = self.pipeline.set_state(gst::State::Null);
    }
}