filesink.rs 4.52 KB
Newer Older
1
2
// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
//               2016 Luis de Bethencourt <luisbg@osg.samsung.com>
3
//
4
5
6
7
8
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
9

10
11
12
13
use std::fs::File;
use url::Url;

use std::io::Write;
14
use std::convert::From;
15

16
use gst_plugin::error::*;
17
18
use gst_plugin_simple::sink::*;
use gst_plugin_simple::UriValidator;
19

20
21
use gst;
use gst::prelude::*;
22

Sebastian Dröge's avatar
Sebastian Dröge committed
23
24
25
26
27
28
#[derive(Debug)]
enum StreamingState {
    Stopped,
    Started { file: File, position: u64 },
}

29
30
#[derive(Debug)]
pub struct FileSink {
31
    streaming_state: StreamingState,
32
    cat: gst::DebugCategory,
33
34
35
}

impl FileSink {
36
    pub fn new(_sink: &BaseSink) -> FileSink {
37
38
        FileSink {
            streaming_state: StreamingState::Stopped,
39
40
41
42
            cat: gst::DebugCategory::new(
                "rsfilesink",
                gst::DebugColorFlags::empty(),
                "Rust file source",
43
            ),
44
        }
45
46
    }

47
    pub fn new_boxed(sink: &BaseSink) -> Box<SinkImpl> {
48
        Box::new(FileSink::new(sink))
49
50
51
    }
}

52
fn validate_uri(uri: &Url) -> Result<(), UriError> {
53
54
55
56
    let _ = try!(uri.to_file_path().or_else(|_| Err(UriError::new(
        gst::URIError::UnsupportedProtocol,
        format!("Unsupported file URI '{}'", uri.as_str()),
    ))));
57
58
    Ok(())
}
59

60
impl SinkImpl for FileSink {
61
62
    fn uri_validator(&self) -> Box<UriValidator> {
        Box::new(validate_uri)
63
64
    }

65
    fn start(&mut self, sink: &BaseSink, uri: Url) -> Result<(), gst::ErrorMessage> {
66
        if let StreamingState::Started { .. } = self.streaming_state {
67
            return Err(gst_error_msg!(
68
69
70
                gst::LibraryError::Failed,
                ["Sink already started"]
            ));
Sebastian Dröge's avatar
Sebastian Dröge committed
71
        }
72

73
        let location = try!(uri.to_file_path().or_else(|_| {
74
75
76
77
78
79
            gst_error!(
                self.cat,
                obj: sink,
                "Unsupported file URI '{}'",
                uri.as_str()
            );
80
            Err(gst_error_msg!(
81
                gst::LibraryError::Failed,
82
83
84
                ["Unsupported file URI '{}'", uri.as_str()]
            ))
        }));
85

86
        let file = try!(File::create(location.as_path()).or_else(|err| {
87
88
89
            gst_error!(
                self.cat,
                obj: sink,
90
91
92
                "Could not open file for writing: {}",
                err.to_string()
            );
93
            Err(gst_error_msg!(
94
                gst::ResourceError::OpenWrite,
95
96
97
98
99
100
                [
                    "Could not open file for writing '{}': {}",
                    location.to_str().unwrap_or("Non-UTF8 path"),
                    err.to_string()
                ]
            ))
101
102
        }));

103
        gst_debug!(self.cat, obj: sink, "Opened file {:?}", file);
104

105
        self.streaming_state = StreamingState::Started {
106
107
108
109
110
            file: file,
            position: 0,
        };

        Ok(())
111
112
    }

113
    fn stop(&mut self, _sink: &BaseSink) -> Result<(), gst::ErrorMessage> {
114
        self.streaming_state = StreamingState::Stopped;
115

116
        Ok(())
117
118
    }

119
    fn render(&mut self, sink: &BaseSink, buffer: &gst::BufferRef) -> Result<(), FlowError> {
120
        let cat = self.cat;
121
        let streaming_state = &mut self.streaming_state;
122

123
        gst_trace!(cat, obj: sink, "Rendering {:?}", buffer);
124

125
        let (file, position) = match *streaming_state {
126
127
128
129
            StreamingState::Started {
                ref mut file,
                ref mut position,
            } => (file, position),
130
            StreamingState::Stopped => {
131
                return Err(FlowError::Error(gst_error_msg!(
132
133
134
                    gst::LibraryError::Failed,
                    ["Not started yet"]
                )));
135
            }
136
137
        };

138
        let map = match buffer.map_readable() {
139
            None => {
140
                return Err(FlowError::Error(gst_error_msg!(
141
142
143
                    gst::LibraryError::Failed,
                    ["Failed to map buffer"]
                )));
144
145
146
147
148
            }
            Some(map) => map,
        };
        let data = map.as_slice();

149
        try!(file.write_all(data).or_else(|err| {
150
            gst_error!(cat, obj: sink, "Failed to write: {}", err);
151
            Err(FlowError::Error(gst_error_msg!(
152
                gst::ResourceError::Write,
153
154
155
                ["Failed to write: {}", err]
            )))
        }));
156
157

        *position += data.len() as u64;
158

159
        Ok(())
160
    }
161
}