appsink.rs 4.83 KB
Newer Older
1
#[macro_use]
Sebastian Dröge's avatar
Sebastian Dröge committed
2
extern crate gstreamer as gst;
3
use gst::prelude::*;
Sebastian Dröge's avatar
Sebastian Dröge committed
4
extern crate gstreamer_app as gst_app;
5
extern crate gstreamer_audio as gst_audio;
Sebastian Dröge's avatar
Sebastian Dröge committed
6

7
8
extern crate glib;

9
10
11
extern crate byte_slice_cast;
use byte_slice_cast::*;

Sebastian Dröge's avatar
Sebastian Dröge committed
12
13
use std::i16;
use std::i32;
14
use std::error::Error as StdError;
Sebastian Dröge's avatar
Sebastian Dröge committed
15

16
extern crate failure;
17
use failure::Error;
18

19
20
21
#[macro_use]
extern crate failure_derive;

22
23
24
#[path = "../examples-common.rs"]
mod examples_common;

25
26
27
28
29
30
31
32
33
34
#[derive(Debug, Fail)]
#[fail(display = "Missing element {}", _0)]
struct MissingElement(&'static str);

#[derive(Debug, Fail)]
#[fail(display = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {
    src: String,
    error: String,
    debug: Option<String>,
35
36
    #[cause]
    cause: glib::Error,
37
}
Sebastian Dröge's avatar
Sebastian Dröge committed
38

39
40
41
42
43
44
45
46
47
48
fn create_pipeline() -> Result<gst::Pipeline, Error> {
    gst::init()?;

    let pipeline = gst::Pipeline::new(None);
    let src =
        gst::ElementFactory::make("audiotestsrc", None).ok_or(MissingElement("audiotestsrc"))?;
    let sink = gst::ElementFactory::make("appsink", None).ok_or(MissingElement("appsink"))?;

    pipeline.add_many(&[&src, &sink])?;
    src.link(&sink)?;
49
50

    let appsink = sink.clone()
51
        .dynamic_cast::<gst_app::AppSink>()
52
        .expect("Sink element is expected to be an appsink!");
Sebastian Dröge's avatar
Sebastian Dröge committed
53

54
    appsink.set_caps(&gst::Caps::new_simple(
Sebastian Dröge's avatar
Sebastian Dröge committed
55
56
        "audio/x-raw",
        &[
57
            ("format", &gst_audio::AUDIO_FORMAT_S16.to_string()),
58
59
            ("layout", &"interleaved"),
            ("channels", &(1i32)),
60
            ("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
Sebastian Dröge's avatar
Sebastian Dröge committed
61
62
63
        ],
    ));

64
    appsink.set_callbacks(
65
        gst_app::AppSinkCallbacks::new()
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
            .new_sample(|appsink| {
                let sample = match appsink.pull_sample() {
                    None => return gst::FlowReturn::Eos,
                    Some(sample) => sample,
                };

                let buffer = if let Some(buffer) = sample.get_buffer() {
                    buffer
                } else {
                    gst_element_error!(
                        appsink,
                        gst::ResourceError::Failed,
                        ("Failed to get buffer from appsink")
                    );

                    return gst::FlowReturn::Error;
                };

                let map = if let Some(map) = buffer.map_readable() {
                    map
                } else {
                    gst_element_error!(
                        appsink,
                        gst::ResourceError::Failed,
                        ("Failed to map buffer readable")
                    );

                    return gst::FlowReturn::Error;
                };

                let samples = if let Ok(samples) = map.as_slice().as_slice_of::<i16>() {
                    samples
                } else {
                    gst_element_error!(
                        appsink,
                        gst::ResourceError::Failed,
                        ("Failed to interprete buffer as S16 PCM")
                    );

                    return gst::FlowReturn::Error;
                };

                let sum: f64 = samples
                    .iter()
                    .map(|sample| {
                        let f = f64::from(*sample) / f64::from(i16::MAX);
                        f * f
                    })
                    .sum();
                let rms = (sum / (samples.len() as f64)).sqrt();
                println!("rms: {}", rms);

                gst::FlowReturn::Ok
            })
            .build(),
    );
Sebastian Dröge's avatar
Sebastian Dröge committed
122

123
124
125
    Ok(pipeline)
}

126
fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
127
    pipeline.set_state(gst::State::Playing).into_result()?;
128
129
130
131

    let bus = pipeline
        .get_bus()
        .expect("Pipeline without bus. Shouldn't happen!");
Sebastian Dröge's avatar
Sebastian Dröge committed
132

133
    while let Some(msg) = bus.timed_pop(gst::CLOCK_TIME_NONE) {
134
135
        use gst::MessageView;

Sebastian Dröge's avatar
Sebastian Dröge committed
136
137
138
        match msg.view() {
            MessageView::Eos(..) => break,
            MessageView::Error(err) => {
139
140
                pipeline.set_state(gst::State::Null).into_result()?;
                Err(ErrorMessage {
141
                    src: err.get_src()
142
                        .map(|s| s.get_path_string())
143
                        .unwrap_or_else(|| String::from("None")),
144
145
146
147
                    error: err.get_error().description().into(),
                    debug: err.get_debug(),
                    cause: err.get_error(),
                })?;
Sebastian Dröge's avatar
Sebastian Dröge committed
148
149
150
151
152
            }
            _ => (),
        }
    }

153
    pipeline.set_state(gst::State::Null).into_result()?;
154
155
156
157

    Ok(())
}

158
fn example_main() {
159
    match create_pipeline().and_then(main_loop) {
160
161
162
        Ok(r) => r,
        Err(e) => eprintln!("Error! {}", e),
    }
Sebastian Dröge's avatar
Sebastian Dröge committed
163
}
164
165
166
167
168
169

fn main() {
    // tutorials_common::run is only required to set up the application environent on macOS
    // (but not necessary in normal Cocoa applications where this is set up autmatically)
    examples_common::run(example_main);
}